Verified Commit 412813d6 authored by Richard Pospesel's avatar Richard Pospesel
Browse files

Bug 27476: Implement about:torconnect captive portal within Tor Browser

- implements new about:torconnect page as tor-launcher replacement
- adds tor connection status to url bar and tweaks UX when not online
- adds new torconnect component to browser
- tor process management functionality remains implemented in tor-launcher through the TorProtocolService module
- the onion pattern from about:tor migrated to an .inc.xhtml file now used by both about:tor and about:torconnect
- various design tweaks and resusability fixes to onion pattern
- adds warning/error box to about:preferences#tor when not connected to tor
- explicitly allows about:torconnect URIs to ignore Resist Fingerprinting (RFP)
- various tweaks to info-pages.inc.css for about:torconnect (also affects other firefox info pages)
parent 998ac31c
......@@ -21,6 +21,10 @@ const { TelemetryController } = ChromeUtils.import(
"resource://gre/modules/TelemetryController.jsm"
);
const { TorConnect } = ChromeUtils.import(
"resource:///modules/TorConnect.jsm"
);
const PREF_SSL_IMPACT_ROOTS = [
"security.tls.version.",
"security.ssl3.",
......@@ -350,6 +354,10 @@ class NetErrorParent extends JSWindowActorParent {
break;
}
}
break;
case "ShouldShowTorConnect":
return TorConnect.shouldShowTorConnect;
}
return undefined;
}
}
......@@ -57,7 +57,10 @@ var gIdentityHandler = {
* RegExp used to decide if an about url should be shown as being part of
* the browser UI.
*/
_secureInternalPages: (AppConstants.TOR_BROWSER_UPDATE ? /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|ion|tor|tbupdate)(?:[?#]|$)/i : /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|ion|tor)(?:[?#]|$)/i),
_secureInternalPages: (AppConstants.TOR_BROWSER_UPDATE ?
/^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|ion|tor|torconnect|tbupdate)(?:[?#]|$)/i :
/^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|ion|tor|torconnect)(?:[?#]|$)/i),
/**
* Whether the established HTTPS connection is considered "broken".
......
......@@ -80,6 +80,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
TabModalPrompt: "chrome://global/content/tabprompts.jsm",
TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
TorConnect: "resource:///modules/TorConnect.jsm",
Translation: "resource:///modules/translation/TranslationParent.jsm",
OnionAliasStore: "resource:///modules/OnionAliasStore.jsm",
UITour: "resource:///modules/UITour.jsm",
......@@ -645,6 +646,7 @@ var gPageIcons = {
var gInitialPages = [
"about:tor",
"about:torconnect",
"about:blank",
"about:newtab",
"about:home",
......@@ -1859,6 +1861,8 @@ var gBrowserInit = {
}
this._loadHandled = true;
TorBootstrapUrlbar.init();
},
_cancelDelayedStartup() {
......@@ -2409,32 +2413,48 @@ var gBrowserInit = {
let defaultArgs = BrowserHandler.defaultArgs;
// If the given URI is different from the homepage, we want to load it.
if (uri != defaultArgs) {
AboutNewTab.noteNonDefaultStartup();
// figure out which URI to actually load (or a Promise to get the uri)
uri = ((uri) => {
// If the given URI is different from the homepage, we want to load it.
if (uri != defaultArgs) {
AboutNewTab.noteNonDefaultStartup();
if (uri instanceof Ci.nsIArray) {
// Transform the nsIArray of nsISupportsString's into a JS Array of
// JS strings.
return Array.from(
uri.enumerate(Ci.nsISupportsString),
supportStr => supportStr.data
);
} else if (uri instanceof Ci.nsISupportsString) {
return uri.data;
}
return uri;
}
if (uri instanceof Ci.nsIArray) {
// Transform the nsIArray of nsISupportsString's into a JS Array of
// JS strings.
return Array.from(
uri.enumerate(Ci.nsISupportsString),
supportStr => supportStr.data
);
} else if (uri instanceof Ci.nsISupportsString) {
return uri.data;
// The URI appears to be the the homepage. We want to load it only if
// session restore isn't about to override the homepage.
let willOverride = SessionStartup.willOverrideHomepage;
if (typeof willOverride == "boolean") {
return willOverride ? null : uri;
}
return uri;
}
return willOverride.then(willOverrideHomepage =>
willOverrideHomepage ? null : uri
);
})(uri);
// if using TorConnect, convert these uris to redirects
if (TorConnect.shouldShowTorConnect) {
return Promise.resolve(uri).then((uri) => {
if (uri == null) {
uri = [];
}
// The URI appears to be the the homepage. We want to load it only if
// session restore isn't about to override the homepage.
let willOverride = SessionStartup.willOverrideHomepage;
if (typeof willOverride == "boolean") {
return willOverride ? null : uri;
uri = TorConnect.getURIsToLoad(uri);
return uri;
});
}
return willOverride.then(willOverrideHomepage =>
willOverrideHomepage ? null : uri
);
return uri;
})());
},
......@@ -2501,6 +2521,8 @@ var gBrowserInit = {
OnionAuthPrompt.uninit();
TorBootstrapUrlbar.uninit();
gAccessibilityServiceIndicator.uninit();
if (gToolbarKeyNavEnabled) {
......
......@@ -10,6 +10,7 @@
override rules using selectors with the same specificity. This applies to
both "content" and "skin" packages, which bug 1385444 will unify later. -->
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
<?xml-stylesheet href="chrome://branding/content/tor-styles.css" type="text/css"?>
<!-- While these stylesheets are defined in Toolkit, they are only used in the
main browser window, so we can load them here. Bug 1474241 is on file to
......@@ -113,6 +114,7 @@
Services.scriptloader.loadSubScript("chrome://browser/content/search/searchbar.js", this);
Services.scriptloader.loadSubScript("chrome://torbutton/content/tor-circuit-display.js", this);
Services.scriptloader.loadSubScript("chrome://torbutton/content/torbutton.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/torconnect/torBootstrapUrlbar.js", this);
window.onload = gBrowserInit.onLoad.bind(gBrowserInit);
window.onunload = gBrowserInit.onUnload.bind(gBrowserInit);
......
......@@ -240,7 +240,7 @@ function setErrorPageStrings(err) {
document.l10n.setAttributes(titleElement, title);
}
function initPage() {
async function initPage() {
// We show an offline support page in case of a system-wide error,
// when a user cannot connect to the internet and access the SUMO website.
// For example, clock error, which causes certerrors across the web or
......@@ -259,6 +259,16 @@ function initPage() {
}
var err = getErrorCode();
// proxyConnectFailure because no-tor running daemon would return this error
if (
(err === "proxyConnectFailure") &&
(await RPMSendQuery("ShouldShowTorConnect"))
) {
// pass orginal destination as redirect param
const encodedRedirect = encodeURIComponent(document.location.href);
document.location.replace(`about:torconnect?redirect=${encodedRedirect}`);
}
// List of error pages with an illustration.
let illustratedErrors = [
"malformedURI",
......
......@@ -331,6 +331,7 @@
data-l10n-id="urlbar-go-button"/>
<hbox id="page-action-buttons" context="pageActionContextMenu">
<toolbartabstop/>
#include ../../components/torconnect/content/torconnect-urlbar.inc.xhtml
<hbox id="contextual-feature-recommendation" role="button" hidden="true">
<hbox id="cfr-label-container">
<label id="cfr-label"/>
......
......@@ -21,6 +21,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
ExtensionSettingsStore: "resource://gre/modules/ExtensionSettingsStore.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
ShellService: "resource:///modules/ShellService.jsm",
TorConnect: "resource:///modules/TorConnect.jsm",
});
XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
......@@ -258,6 +259,13 @@ function openUILinkIn(
aPostData,
aReferrerInfo
) {
// make sure users are not faced with the scary red 'tor isn't working' screen
// if they navigate to about:tor before bootstrapped
if (url === "about:tor" && TorConnect.shouldShowTorConnect) {
url = `about:torconnect?redirect=${encodeURIComponent("about:tor")}`;
}
var params;
if (arguments.length == 3 && typeof arguments[2] == "object") {
......
......@@ -18,4 +18,6 @@ browser.jar:
content/branding/icon128.png (../default128.png)
content/branding/icon256.png (../default256.png)
content/branding/icon512.png (../default512.png)
content/branding/identity-icons-brand.svg
content/branding/aboutDialog.css
* content/branding/tor-styles.css
%include ../../tor-styles.inc.css
/* default theme*/
:root,
/* light theme*/
:root:-moz-lwtheme-darktext {
--tor-branding-color: var(--teal-70);
}
/* dark theme */
:root:-moz-lwtheme-brighttext {
--tor-branding-color: var(--teal-60);
}
\ No newline at end of file
......@@ -20,3 +20,4 @@ browser.jar:
content/branding/icon512.png (../default512.png)
content/branding/identity-icons-brand.svg
content/branding/aboutDialog.css
* content/branding/tor-styles.css
%include ../../tor-styles.inc.css
/* default theme*/
:root,
/* light theme*/
:root:-moz-lwtheme-darktext {
--tor-branding-color: var(--blue-60);
}
/* dark theme */
:root:-moz-lwtheme-brighttext {
--tor-branding-color: var(--blue-40);
}
\ No newline at end of file
......@@ -20,3 +20,4 @@ browser.jar:
content/branding/icon512.png (../default512.png)
content/branding/identity-icons-brand.svg
content/branding/aboutDialog.css
* content/branding/tor-styles.css
%include ../../tor-styles.inc.css
/* default theme*/
:root,
/* light theme*/
:root:-moz-lwtheme-darktext {
--tor-branding-color: var(--purple-60);
}
/* dark theme */
:root:-moz-lwtheme-brighttext {
--tor-branding-color: var(--purple-30);
}
:root {
/* photon colors, not all of them are available for whatever reason
in firefox, so here they are */
--magenta-50: #ff1ad9;
--magenta-60: #ed00b5;
--magenta-70: #b5007f;
--magenta-80: #7d004f;
--magenta-90: #440027;
--purple-30: #c069ff;
--purple-40: #ad3bff;
--purple-50: #9400ff;
--purple-60: #8000d7;
--purple-70: #6200a4;
--purple-80: #440071;
--purple-90: #25003e;
--blue-40: #45a1ff;
--blue-50: #0a84ff;
--blue-50-a30: rgba(10, 132, 255, 0.3);
--blue-60: #0060df;
--blue-70: #003eaa;
--blue-80: #002275;
--blue-90: #000f40;
--teal-50: #00feff;
--teal-60: #00c8d7;
--teal-70: #008ea4;
--teal-80: #005a71;
--teal-90: #002d3e;
--green-50: #30e60b;
--green-60: #12bc00;
--green-70: #058b00;
--green-80: #006504;
--green-90: #003706;
--yellow-50: #ffe900;
--yellow-60: #d7b600;
--yellow-70: #a47f00;
--yellow-80: #715100;
--yellow-90: #3e2800;
--red-50: #ff0039;
--red-60: #d70022;
--red-70: #a4000f;
--red-80: #5a0002;
--red-90: #3e0200;
--orange-50: #ff9400;
--orange-60: #d76e00;
--orange-70: #a44900;
--orange-80: #712b00;
--orange-90: #3e1300;
--grey-10: #f9f9fa;
--grey-10-a10: rgba(249, 249, 250, 0.1);
--grey-10-a20: rgba(249, 249, 250, 0.2);
--grey-10-a40: rgba(249, 249, 250, 0.4);
--grey-10-a60: rgba(249, 249, 250, 0.6);
--grey-10-a80: rgba(249, 249, 250, 0.8);
--grey-20: #ededf0;
--grey-30: #d7d7db;
--grey-40: #b1b1b3;
--grey-50: #737373;
--grey-60: #4a4a4f;
--grey-70: #38383d;
--grey-80: #2a2a2e;
--grey-90: #0c0c0d;
--grey-90-a05: rgba(12, 12, 13, 0.05);
--grey-90-a10: rgba(12, 12, 13, 0.1);
--grey-90-a20: rgba(12, 12, 13, 0.2);
--grey-90-a30: rgba(12, 12, 13, 0.3);
--grey-90-a40: rgba(12, 12, 13, 0.4);
--grey-90-a50: rgba(12, 12, 13, 0.5);
--grey-90-a60: rgba(12, 12, 13, 0.6);
--grey-90-a70: rgba(12, 12, 13, 0.7);
--grey-90-a80: rgba(12, 12, 13, 0.8);
--grey-90-a90: rgba(12, 12, 13, 0.9);
--ink-70: #363959;
--ink-80: #202340;
--ink-90: #0f1126;
--white-100: #ffffff;
}
\ No newline at end of file
......@@ -725,6 +725,20 @@ let JSWINDOWACTORS = {
allFrames: true,
},
TorConnect: {
parent: {
moduleURI: "resource:///modules/TorConnectParent.jsm",
},
child: {
moduleURI: "resource:///modules/TorConnectChild.jsm",
events: {
DOMWindowCreated: {},
},
},
matches: ["about:torconnect","about:torconnect?*"],
},
Translation: {
parent: {
moduleURI: "resource:///modules/translation/TranslationParent.jsm",
......@@ -2522,7 +2536,28 @@ BrowserGlue.prototype = {
{
task: () => {
OnionAliasStore.init();
const { TorConnect, TorConnectTopics } = ChromeUtils.import(
"resource:///modules/TorConnect.jsm"
);
if (!TorConnect.shouldShowTorConnect) {
// we will take this path when the user is using the legacy tor launcher or
// when Tor Browser didn't launch its own tor.
OnionAliasStore.init();
} else {
// this path is taken when using about:torconnect, we wait to init
// after we are bootstrapped and connected to tor
const topic = TorConnectTopics.BootstrapComplete;
let bootstrapObserver = {
observe(aSubject, aTopic, aData) {
if (aTopic === topic) {
OnionAliasStore.init();
// we only need to init once, so remove ourselves as an obvserver
Services.obs.removeObserver(this, topic);
}
}
};
Services.obs.addObserver(bootstrapObserver, topic);
}
},
},
......
......@@ -128,6 +128,10 @@ static const RedirEntry kRedirMap[] = {
nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT},
#endif
{"torconnect", "chrome://browser/content/torconnect/aboutTorConnect.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT},
};
static nsAutoCString GetAboutModuleName(nsIURI* aURI) {
......
......@@ -26,6 +26,7 @@ pages = [
'robots',
'sessionrestore',
'tabcrashed',
'torconnect',
'welcome',
'welcomeback',
]
......
......@@ -55,6 +55,7 @@ DIRS += [
"syncedtabs",
"uitour",
"urlbar",
"torconnect",
"torpreferences",
"translation",
]
......
......@@ -41,6 +41,7 @@ const SECUREDROP_TOR_ONION_CHANNEL = {
class HttpsEverywhereControl {
constructor() {
this._extensionMessaging = null;
this._init();
}
async _sendMessage(type, object) {
......@@ -61,7 +62,6 @@ class HttpsEverywhereControl {
* Installs the .tor.onion update channel in https-everywhere
*/
async installTorOnionUpdateChannel(retries = 5) {
this._init();
// TODO: https-everywhere store is initialized asynchronously, so sending a message
// immediately results in a `store.get is undefined` error.
......@@ -143,5 +143,20 @@ class HttpsEverywhereControl {
if (!this._extensionMessaging) {
this._extensionMessaging = new ExtensionMessaging();
}
// update all of the existing https-everywhere channels
setTimeout(async () => {
let pinnedChannels = await this._sendMessage("get_pinned_update_channels");
for(let channel of pinnedChannels.update_channels) {
this._sendMessage("update_update_channel", channel);
}
let storedChannels = await this._sendMessage("get_stored_update_channels");
for(let channel of storedChannels.update_channels) {
this._sendMessage("update_update_channel", channel);
}
}, 0);
}
}
......@@ -186,6 +186,10 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/sessionstore/SessionHistory.jsm"
);
const { TorProtocolService } = ChromeUtils.import(
"resource:///modules/TorProtocolService.jsm"
);
XPCOMUtils.defineLazyServiceGetters(this, {
gScreenManager: ["@mozilla.org/gfx/screenmanager;1", "nsIScreenManager"],
});
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment