Pier Angelo Vendrame pushed to branch tor-browser-154.0a1-17.0-2 at The Tor Project / Applications / Tor Browser
Commits:
-
8154acd5
by soni at 2026-10-15T01:09:18+02:00
6 changed files:
- browser/components/BrowserComponents.manifest
- browser/components/DesktopActorRegistry.sys.mjs
- browser/components/onionservices/OnionLocationChild.sys.mjs
- browser/components/onionservices/OnionLocationParent.sys.mjs
- + browser/components/onionservices/OnionLocationWatch.sys.mjs
- browser/components/onionservices/moz.build
Changes:
| ... | ... | @@ -172,6 +172,7 @@ category browser-first-window-ready moz-src:///browser/modules/SecurityLevelNoti |
| 172 | 172 | category browser-first-window-ready moz-src:///toolkit/modules/DragDropFilter.sys.mjs DragDropFilter.init
|
| 173 | 173 | category browser-first-window-ready moz-src:///browser/modules/TorSettingsNotification.sys.mjs TorSettingsNotification.ready
|
| 174 | 174 | category browser-first-window-ready moz-src:///browser/components/onionservices/OnionAliasStore.sys.mjs OnionAliasStore.init
|
| 175 | +category browser-first-window-ready moz-src:///browser/components/onionservices/OnionLocationWatch.sys.mjs OnionLocationWatch.init
|
|
| 175 | 176 | |
| 176 | 177 | category browser-idle-startup moz-src:///browser/components/places/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars
|
| 177 | 178 | category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes
|
| ... | ... | @@ -220,5 +221,6 @@ category browser-quit-application-granted resource://gre/modules/UpdateListener. |
| 220 | 221 | #endif
|
| 221 | 222 | category browser-quit-application-granted moz-src:///browser/components/urlbar/UrlbarSearchTermsPersistence.sys.mjs UrlbarSearchTermsPersistence.uninit
|
| 222 | 223 | category browser-quit-application-granted moz-src:///browser/components/onionservices/OnionAliasStore.sys.mjs OnionAliasStore.uninit
|
| 224 | +category browser-quit-application-granted moz-src:///browser/components/onionservices/OnionLocationWatch.sys.mjs OnionLocationWatch.uninit
|
|
| 223 | 225 | |
| 224 | 226 | category browser-newtab-external-component moz-src:///browser/components/search/SearchUIUtils.sys.mjs SearchNewTabComponentsRegistrant |
| ... | ... | @@ -656,6 +656,7 @@ let JSWINDOWACTORS = { |
| 656 | 656 | },
|
| 657 | 657 | },
|
| 658 | 658 | messageManagerGroups: ["browsers"],
|
| 659 | + safeForUntrustedWebProcess: true,
|
|
| 659 | 660 | },
|
| 660 | 661 | |
| 661 | 662 | PageAssist: {
|
| ... | ... | @@ -14,32 +14,9 @@ export class OnionLocationChild extends JSWindowActorChild { |
| 14 | 14 | }
|
| 15 | 15 | const onionLocationURI = this.document.onionLocationURI;
|
| 16 | 16 | if (onionLocationURI) {
|
| 17 | - this.sendAsyncMessage("OnionLocation:Set");
|
|
| 18 | - }
|
|
| 19 | - }
|
|
| 20 | - |
|
| 21 | - receiveMessage(aMessage) {
|
|
| 22 | - if (aMessage.name == "OnionLocation:Refresh") {
|
|
| 23 | - const doc = this.document;
|
|
| 24 | - const docShell = this.docShell;
|
|
| 25 | - let onionLocationURI = doc.onionLocationURI;
|
|
| 26 | - const refreshURI = docShell.QueryInterface(Ci.nsIRefreshURI);
|
|
| 27 | - if (onionLocationURI && refreshURI) {
|
|
| 28 | - const docUrl = URL.parse(doc.URL);
|
|
| 29 | - let onionUrl = URL.parse(onionLocationURI.asciiSpec);
|
|
| 30 | - // Keep consistent with Location
|
|
| 31 | - if (!onionUrl?.hash && docUrl?.hash) {
|
|
| 32 | - onionUrl.hash = docUrl.hash;
|
|
| 33 | - onionLocationURI = Services.io.newURI(onionUrl?.toString() || "");
|
|
| 34 | - }
|
|
| 35 | - refreshURI.refreshURI(
|
|
| 36 | - onionLocationURI,
|
|
| 37 | - doc.nodePrincipal,
|
|
| 38 | - 0,
|
|
| 39 | - false,
|
|
| 40 | - true
|
|
| 41 | - );
|
|
| 42 | - }
|
|
| 17 | + this.sendAsyncMessage("OnionLocation:Set", {
|
|
| 18 | + uri: onionLocationURI.asciiSpec,
|
|
| 19 | + });
|
|
| 43 | 20 | }
|
| 44 | 21 | }
|
| 45 | 22 | } |
| ... | ... | @@ -2,6 +2,8 @@ |
| 2 | 2 | |
| 3 | 3 | import { TorStrings } from "moz-src:///toolkit/modules/TorStrings.sys.mjs";
|
| 4 | 4 | |
| 5 | +import { BrowserUtils } from "resource://gre/modules/BrowserUtils.sys.mjs";
|
|
| 6 | + |
|
| 5 | 7 | // Prefs
|
| 6 | 8 | |
| 7 | 9 | // We keep the "prioritizeonions" name, even if obsolete, in order to
|
| ... | ... | @@ -36,36 +38,71 @@ export class OnionLocationParent extends JSWindowActorParent { |
| 36 | 38 | switch (aMsg.name) {
|
| 37 | 39 | case "OnionLocation:Set": {
|
| 38 | 40 | let browser = this.browsingContext.embedderElement;
|
| 39 | - OnionLocationParent.setOnionLocation(browser);
|
|
| 41 | + OnionLocationParent.setOnionLocationFallback(browser, aMsg);
|
|
| 42 | + |
|
| 43 | + // tor-browser #40100
|
|
| 44 | + // Wait for the page to fully load before showing the notification.
|
|
| 45 | + // This prevents redirects that occur before onPageShow from causing it to disappear.
|
|
| 46 | + // This could be handled more elegantly by changing the notification's "appear once and never again behaviour".
|
|
| 47 | + // Sites that redirect after the onPageShow event are not affected and will therefore cause the notification to disappear.
|
|
| 48 | + // https://gitlab.torproject.org/tpo/applications/tor-browser/-/merge_requests/2176#note_3459271
|
|
| 49 | + OnionLocationParent.showNotification(browser);
|
|
| 40 | 50 | break;
|
| 41 | 51 | }
|
| 42 | 52 | }
|
| 43 | 53 | }
|
| 44 | 54 | |
| 55 | + // Maintains compatibility with <meta http-equiv="onion-location">
|
|
| 56 | + static setOnionLocationFallback(browser, aMsg) {
|
|
| 57 | + let onionLocation;
|
|
| 58 | + try {
|
|
| 59 | + onionLocation = new URL(aMsg.data.uri);
|
|
| 60 | + } catch (e) {
|
|
| 61 | + return;
|
|
| 62 | + }
|
|
| 63 | + |
|
| 64 | + if (
|
|
| 65 | + browser &&
|
|
| 66 | + !browser._onionLocation &&
|
|
| 67 | + OnionLocationParent.validateOnionV3(onionLocation)
|
|
| 68 | + ) {
|
|
| 69 | + OnionLocationParent.setOnionLocation(browser, onionLocation);
|
|
| 70 | + }
|
|
| 71 | + }
|
|
| 72 | + |
|
| 45 | 73 | static init(document) {
|
| 46 | 74 | document
|
| 47 | 75 | .getElementById(ONIONLOCATION_BOX_ID)
|
| 48 | - .addEventListener("click", event => this.buttonClick(event));
|
|
| 76 | + .addEventListener("click", event => this.openOnionLink(event));
|
|
| 49 | 77 | }
|
| 50 | 78 | |
| 51 | - static buttonClick(event) {
|
|
| 52 | - if (event.button !== 0) {
|
|
| 53 | - return;
|
|
| 54 | - }
|
|
| 79 | + static openOnionLink(event) {
|
|
| 55 | 80 | const win = event.target.documentGlobal;
|
| 56 | - if (win.gBrowser) {
|
|
| 57 | - const browser = win.gBrowser.selectedBrowser;
|
|
| 58 | - OnionLocationParent.redirect(browser);
|
|
| 81 | + const browser = win.gBrowser?.selectedBrowser;
|
|
| 82 | + |
|
| 83 | + if (!browser?._onionLocation || event.button > 1) {
|
|
| 84 | + return;
|
|
| 59 | 85 | }
|
| 60 | - }
|
|
| 61 | 86 | |
| 62 | - static redirect(browser) {
|
|
| 63 | - let windowGlobal = browser.browsingContext.currentWindowGlobal;
|
|
| 64 | - let actor = windowGlobal.getActor("OnionLocation");
|
|
| 65 | - if (actor) {
|
|
| 66 | - actor.sendAsyncMessage("OnionLocation:Refresh", {});
|
|
| 87 | + const where = BrowserUtils.whereToOpenLink(event);
|
|
| 88 | + const triggeringPrincipal =
|
|
| 89 | + Services.scriptSecurityManager.getSystemPrincipal();
|
|
| 90 | + |
|
| 91 | + if (where == "current") {
|
|
| 92 | + browser.webNavigation.stop(Ci.nsIWebNavigation.STOP_ALL);
|
|
| 67 | 93 | OnionLocationParent.setDisabled(browser);
|
| 68 | 94 | }
|
| 95 | + |
|
| 96 | + const onionUrl = browser._onionLocation;
|
|
| 97 | + const docUrl = new URL(browser.browsingContext.currentURI.asciiSpec);
|
|
| 98 | + |
|
| 99 | + if (docUrl.hash && docUrl.hash != onionUrl.hash) {
|
|
| 100 | + onionUrl.hash = docUrl.hash;
|
|
| 101 | + }
|
|
| 102 | + |
|
| 103 | + win.openLinkIn(onionUrl.href, where, {
|
|
| 104 | + triggeringPrincipal,
|
|
| 105 | + });
|
|
| 69 | 106 | }
|
| 70 | 107 | |
| 71 | 108 | static onStateChange(browser) {
|
| ... | ... | @@ -73,8 +110,8 @@ export class OnionLocationParent extends JSWindowActorParent { |
| 73 | 110 | OnionLocationParent.hideNotification(browser);
|
| 74 | 111 | }
|
| 75 | 112 | |
| 76 | - static setOnionLocation(browser) {
|
|
| 77 | - browser._onionLocation = true;
|
|
| 113 | + static setOnionLocation(browser, onionLocation) {
|
|
| 114 | + browser._onionLocation = onionLocation;
|
|
| 78 | 115 | let tabBrowser = browser.getTabBrowser();
|
| 79 | 116 | if (tabBrowser && browser === tabBrowser.selectedBrowser) {
|
| 80 | 117 | OnionLocationParent.updateOnionLocationBadge(browser);
|
| ... | ... | @@ -90,7 +127,7 @@ export class OnionLocationParent extends JSWindowActorParent { |
| 90 | 127 | |
| 91 | 128 | static showNotification(browser) {
|
| 92 | 129 | const mustShow = Services.prefs.getBoolPref(NOTIFICATION_PREF, true);
|
| 93 | - if (!mustShow) {
|
|
| 130 | + if (!mustShow || !browser._onionLocation) {
|
|
| 94 | 131 | return;
|
| 95 | 132 | }
|
| 96 | 133 | |
| ... | ... | @@ -100,8 +137,8 @@ export class OnionLocationParent extends JSWindowActorParent { |
| 100 | 137 | const mainAction = {
|
| 101 | 138 | label: NOTIFICATION_OK_LABEL,
|
| 102 | 139 | accessKey: NOTIFICATION_OK_ACCESSKEY,
|
| 103 | - callback() {
|
|
| 104 | - OnionLocationParent.redirect(browser);
|
|
| 140 | + callback(obj) {
|
|
| 141 | + OnionLocationParent.openOnionLink(obj.event);
|
|
| 105 | 142 | },
|
| 106 | 143 | };
|
| 107 | 144 | |
| ... | ... | @@ -164,9 +201,15 @@ export class OnionLocationParent extends JSWindowActorParent { |
| 164 | 201 | static updateOnionLocationBadge(browser) {
|
| 165 | 202 | if (browser._onionLocation) {
|
| 166 | 203 | OnionLocationParent.setEnabled(browser);
|
| 167 | - OnionLocationParent.showNotification(browser);
|
|
| 168 | 204 | } else {
|
| 169 | 205 | OnionLocationParent.setDisabled(browser);
|
| 170 | 206 | }
|
| 171 | 207 | }
|
| 208 | + |
|
| 209 | + static validateOnionV3(url) {
|
|
| 210 | + if (!["http:", "https:"].includes(url.protocol)) {
|
|
| 211 | + return false;
|
|
| 212 | + }
|
|
| 213 | + return /(^|\.)[a-z2-7]{56}\.onion$/i.test(url.hostname);
|
|
| 214 | + }
|
|
| 172 | 215 | } |
| 1 | +/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
| 2 | + * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
| 3 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
| 4 | + |
|
| 5 | +import { OnionLocationParent } from "moz-src:///browser/components/onionservices/OnionLocationParent.sys.mjs";
|
|
| 6 | + |
|
| 7 | +const log = console.createInstance({
|
|
| 8 | + prefix: "OnionLocationWatch",
|
|
| 9 | +});
|
|
| 10 | + |
|
| 11 | +/**
|
|
| 12 | + * This request observer listens to http-on-examine-responses to know early if a clearnet site has an onion alternative.
|
|
| 13 | + */
|
|
| 14 | +class _OnionLocationWatch {
|
|
| 15 | + init() {
|
|
| 16 | + Services.obs.addObserver(this, "http-on-examine-response");
|
|
| 17 | + Services.obs.addObserver(this, "http-on-examine-cached-response");
|
|
| 18 | + Services.obs.addObserver(this, "http-on-examine-merged-response");
|
|
| 19 | + log.debug("Started");
|
|
| 20 | + }
|
|
| 21 | + |
|
| 22 | + uninit() {
|
|
| 23 | + Services.obs.removeObserver(this, "http-on-examine-response");
|
|
| 24 | + Services.obs.removeObserver(this, "http-on-examine-cached-response");
|
|
| 25 | + Services.obs.removeObserver(this, "http-on-examine-merged-response");
|
|
| 26 | + log.debug("Stopped");
|
|
| 27 | + }
|
|
| 28 | + |
|
| 29 | + // nsIObserver implementation
|
|
| 30 | + observe(subject, topic) {
|
|
| 31 | + try {
|
|
| 32 | + let channel = ChannelWrapper.get(
|
|
| 33 | + subject.QueryInterface(Ci.nsIHttpChannel)
|
|
| 34 | + );
|
|
| 35 | + switch (topic) {
|
|
| 36 | + case "http-on-examine-cached-response":
|
|
| 37 | + case "http-on-examine-merged-response":
|
|
| 38 | + case "http-on-examine-response":
|
|
| 39 | + this.onResponse(channel);
|
|
| 40 | + break;
|
|
| 41 | + }
|
|
| 42 | + } catch (e) {
|
|
| 43 | + log.error(e);
|
|
| 44 | + }
|
|
| 45 | + }
|
|
| 46 | + |
|
| 47 | + onResponse(channel) {
|
|
| 48 | + this.checkResponseForOnionLocation(channel);
|
|
| 49 | + }
|
|
| 50 | + |
|
| 51 | + checkResponseForOnionLocation(channel) {
|
|
| 52 | + const browser = channel.browserElement;
|
|
| 53 | + const uri = channel.finalURI;
|
|
| 54 | + |
|
| 55 | + if (!browser || uri?.scheme != "https") {
|
|
| 56 | + return;
|
|
| 57 | + }
|
|
| 58 | + |
|
| 59 | + const isTopLevelDoc =
|
|
| 60 | + channel.channel.loadInfo.isTopLevelLoad && channel.channel.isDocument;
|
|
| 61 | + const isOnion = uri.host?.toLowerCase().endsWith(".onion");
|
|
| 62 | + |
|
| 63 | + if (isTopLevelDoc && !isOnion && !browser._onionLocation) {
|
|
| 64 | + const onionLocation = this.getOnionLocation(channel);
|
|
| 65 | + if (onionLocation) {
|
|
| 66 | + OnionLocationParent.setOnionLocation(browser, onionLocation);
|
|
| 67 | + }
|
|
| 68 | + }
|
|
| 69 | + }
|
|
| 70 | + |
|
| 71 | + getOnionLocation(channel) {
|
|
| 72 | + let onionLocation = channel
|
|
| 73 | + .getResponseHeaders()
|
|
| 74 | + .find(el => el.name.toLowerCase() == "onion-location")?.value;
|
|
| 75 | + |
|
| 76 | + if (onionLocation) {
|
|
| 77 | + try {
|
|
| 78 | + onionLocation = new URL(onionLocation);
|
|
| 79 | + } catch (e) {
|
|
| 80 | + return null;
|
|
| 81 | + }
|
|
| 82 | + |
|
| 83 | + if (OnionLocationParent.validateOnionV3(onionLocation)) {
|
|
| 84 | + return onionLocation;
|
|
| 85 | + }
|
|
| 86 | + }
|
|
| 87 | + return null;
|
|
| 88 | + }
|
|
| 89 | +}
|
|
| 90 | + |
|
| 91 | +export const OnionLocationWatch = new _OnionLocationWatch(); |
| ... | ... | @@ -4,5 +4,6 @@ MOZ_SRC_FILES += [ |
| 4 | 4 | "OnionAliasStore.sys.mjs",
|
| 5 | 5 | "OnionLocationChild.sys.mjs",
|
| 6 | 6 | "OnionLocationParent.sys.mjs",
|
| 7 | + "OnionLocationWatch.sys.mjs",
|
|
| 7 | 8 | "TorRequestWatch.sys.mjs",
|
| 8 | 9 | ] |