commit 32f9cf56c89ccd2e24924975dc5515e4198d28c3 Author: Kathy Brade brade@pearlcrescent.com Date: Sat Jun 24 08:52:19 2017 -0400
Bug 18913: about:tor should not have chrome privileges
Rearchitect our implementation so that about:tor pages are always loaded in a content process. This also fixes: Bug 22535: Searching brings me to duckduckgo but my query is discarded. Bug 21948: Going back to about:tor page gives "Address isn't valid" error.
Most of the code that initializes and updates about:tor content has been moved to a content script. When necessary, IPC is used to pass data from the chrome process to the content script.
Removed old, no-longer-used m_tb_orig_BrowserOnAboutPageLoad variable from torbutton.js.
Also, update the about:tor newChannel() implementation to accept an nsILoadInfo parameter. --- src/chrome.manifest | 3 - src/chrome/content/aboutTor/aboutTor-content.js | 328 ++++++++++++++++++++++++ src/chrome/content/aboutTor/aboutTor.xhtml | 143 +---------- src/chrome/content/torbutton.js | 257 ++++++------------- src/chrome/skin/aboutTor.css | 11 +- src/components/aboutTor.js | 21 +- src/components/startup-observer.js | 7 +- 7 files changed, 441 insertions(+), 329 deletions(-)
diff --git a/src/chrome.manifest b/src/chrome.manifest index 75bef4e..272401b 100644 --- a/src/chrome.manifest +++ b/src/chrome.manifest @@ -152,9 +152,6 @@ contract @torproject.org/startup-observer;1 {06322def-6fde-4c06-aef6-47ae8e79962 component {e6204253-b690-4159-bfe8-d4eedab6b3be} components/cookie-jar-selector.js contract @torproject.org/cookie-jar-selector;1 {e6204253-b690-4159-bfe8-d4eedab6b3be}
-component {84d47da6-79c3-4661-aa9f-8049476f7bf5} components/aboutTor.js -contract @mozilla.org/network/protocol/about;1?what=tor {84d47da6-79c3-4661-aa9f-8049476f7bf5} - component {5d57312b-5d8c-4169-b4af-e80d6a28a72e} components/torCheckService.js contract @torproject.org/torbutton-torCheckService;1 {5d57312b-5d8c-4169-b4af-e80d6a28a72e}
diff --git a/src/chrome/content/aboutTor/aboutTor-content.js b/src/chrome/content/aboutTor/aboutTor-content.js new file mode 100644 index 0000000..ec515bb --- /dev/null +++ b/src/chrome/content/aboutTor/aboutTor-content.js @@ -0,0 +1,328 @@ +/************************************************************************* + * Copyright (c) 2017, The Tor Project, Inc. + * See LICENSE for licensing information. + * + * vim: set sw=2 sts=2 ts=8 et syntax=javascript: + * + * about:tor content script + *************************************************************************/ + +/* + * The following about:tor IPC messages are exchanged by this code and + * the code in torbutton.js: + * AboutTor:Loaded page loaded content -> chrome + * AboutTor:ChromeData privileged data chrome -> content + * AboutTor:GetToolbarData request toolbar info content -> chrome + * AboutTor:ToolbarData toolbar info chrome -> content + */ + +var {classes: Cc, interfaces: Ci, utils: Cu} = Components; + + +Cu.import("resource://gre/modules/Services.jsm"); +let { bindPrefAndInit } = Cu.import("resource://torbutton/modules/utils.js", {}); + + +var AboutTorListener = { + kAboutTorMessages: [ "AboutTor:ChromeData", "AboutTor:ToolbarData" ], + + get isAboutTor() { + return content.document.documentURI.toLowerCase() == "about:tor"; + }, + + init: function(aChromeGlobal) { + aChromeGlobal.addEventListener("AboutTorLoad", this, false, true); + }, + + handleEvent: function(aEvent) { + if (!this.isAboutTor) + return; + + switch (aEvent.type) { + case "AboutTorLoad": + this.onPageLoad(); + break; + case "pagehide": + this.onPageHide(); + break; + case "resize": + sendAsyncMessage("AboutTor:GetToolbarData"); + break; + } + }, + + receiveMessage: function(aMessage) { + if (!this.isAboutTor) + return; + + switch (aMessage.name) { + case "AboutTor:ChromeData": + this.onChromeDataUpdate(aMessage.data); + break; + case "AboutTor:ToolbarData": + this.onToolbarDataUpdate(aMessage.data); + break; + } + }, + + onPageLoad: function() { + // Arrange to update localized text and links. + bindPrefAndInit("general.useragent.locale", aNewVal => { + this.onLocaleChange(aNewVal); + }); + + // Add message and event listeners. + this.kAboutTorMessages.forEach(aMsg => addMessageListener(aMsg, this)); + addMessageListener("AboutTor:ChromeData", this); + addEventListener("pagehide", this, false); + addEventListener("resize", this, false); + + sendAsyncMessage("AboutTor:Loaded"); + }, + + onPageHide: function() { + removeEventListener("resize", this, false); + removeEventListener("pagehide", this, false); + this.kAboutTorMessages.forEach(aMsg => removeMessageListener(aMsg, this)); + }, + + onChromeDataUpdate: function(aData) { + let body = content.document.body; + + // Update status: tor on/off, update needed, Tor Browser manual shown. + if (aData.torOn) + body.setAttribute("toron", "yes"); + else + body.removeAttribute("toron"); + + if (aData.updateNeeded) + body.setAttribute("torNeedsUpdate", "yes"); + else + body.removeAttribute("torNeedsUpdate"); + + if (aData.showManual) + body.setAttribute("showmanual", "yes"); + else + body.removeAttribute("showmanual"); + + // Setting body.initialized="yes" displays the body, which must be done + // at this point because our remaining initialization depends on elements + // being visible so that their size and position are accurate. + body.setAttribute("initialized", "yes"); + + let containerName = "torstatus-" + (aData.torOn ? "on" : "off") + + "-container"; + this.adjustFontSizes(containerName); + + this.onToolbarDataUpdate(aData); + }, + + onToolbarDataUpdate: function(aData) { + this.adjustArrow(aData.toolbarButtonXPos); + }, + + onLocaleChange: function(aLocale) { + this.insertPropertyStrings(); + + // Set Tor Browser manual link. + content.document.getElementById("manualLink").href = + "https://tb-manual.torproject.org/" + aLocale; + + // Insert "Test Tor Network Settings" url. + let elem = content.document.getElementById("testTorSettings"); + if (elem) { + let url = Services.prefs.getCharPref( + "extensions.torbutton.test_url_interactive"); + elem.href = url.replace(/__LANG__/g, aLocale.replace(/-/g, '_')); + } + + // Display the Tor Browser product name and version. + try { + const kBrandBundle = "chrome://branding/locale/brand.properties"; + let brandBundle = Cc["@mozilla.org/intl/stringbundle;1"] + .getService(Ci.nsIStringBundleService) + .createBundle(kBrandBundle); + let productName = brandBundle.GetStringFromName("brandFullName"); + let tbbVersion = Services.prefs.getCharPref("torbrowser.version"); + elem = content.document.getElementById("torstatus-version"); + + while (elem.firstChild) + elem.removeChild(elem.firstChild); + elem.appendChild(content.document.createTextNode(productName + '\n' + + tbbVersion)); + } catch (e) {} + }, + + insertPropertyStrings: function() { + try { + let kPropertiesURL = "chrome://torbutton/locale/aboutTor.properties"; + + let stringBundle = Services.strings.createBundle(kPropertiesURL); + let s1 = stringBundle.GetStringFromName("aboutTor.searchDDG.privacy.link"); + let s2 = stringBundle.GetStringFromName("aboutTor.searchDDG.search.link"); + let result = stringBundle.formatStringFromName( + "aboutTor.searchDDG.privacy", [s1, s2], 2); + if (result) { + let elem = content.document.getElementById("searchProviderInfo"); + if (elem) + elem.innerHTML = result; + } + } catch(e) {} + }, + + // Ensure that text in top area does not overlap the tor on/off (onion) image. + // This is done by reducing the font sizes as necessary. + adjustFontSizes: function(aContainerName) + { + let imgElem = content.document.getElementById("torstatus-image"); + let containerElem = content.document.getElementById(aContainerName); + if (!imgElem || !containerElem) + return; + + try + { + let imgRect = imgElem.getBoundingClientRect(); + + for (let textElem = containerElem.firstChild; textElem; + textElem = textElem.nextSibling) + { + if ((textElem.nodeType != textElem.ELEMENT_NODE) || + (textElem.nodeName.toLowerCase() == "br")) + { + continue; + } + + let textRect = textElem.getBoundingClientRect(); + if (0 == textRect.width) + continue; + + // Reduce font to 90% of previous size, repeating the process up to 7 + // times. This allows for a maximum reduction to just less than 50% of + // the original size. + let maxTries = 7; + while ((textRect.left < imgRect.right) && (--maxTries >= 0)) + { + let style = content.document.defaultView + .getComputedStyle(textElem, null); + let fontSize = parseFloat(style.getPropertyValue("font-size")); + textElem.style.fontSize = (fontSize * 0.9) + "px"; + textRect = textElem.getBoundingClientRect(); + } + } + + } catch (e) {} + }, + + adjustArrow: function(aToolbarButtonXPos) + { + let win = content; + let doc = content.document; + let textElem = doc.getElementById("updatePrompt"); + let arrowHeadDiv = doc.getElementById("toolbarIconArrowHead"); + let vertExtDiv = doc.getElementById("toolbarIconArrowVertExtension"); + let bendDiv = doc.getElementById("toolbarIconArrowBend"); + let horzExtDiv = doc.getElementById("toolbarIconArrowHorzExtension"); + if (!textElem || !arrowHeadDiv || !vertExtDiv || !bendDiv || !horzExtDiv) + return; + + let arrowTailElems = [ vertExtDiv, bendDiv, horzExtDiv ]; + if (!aToolbarButtonXPos || isNaN(aToolbarButtonXPos) || + (aToolbarButtonXPos < 0)) + { + arrowHeadDiv.style.display = "none"; + for (let elem of arrowTailElems) + elem.style.display = "none"; + return; + } + + const kArrowMargin = 6; // Horizontal margin between line and text. + const kArrowHeadExtraWidth = 9; // Horizontal margin to the line. + const kArrowLineThickness = 11; + const kBendWidth = 22; + const kBendHeight = 22; + + try { + // Compensate for any content zoom that may be in effect on about:tor. + // Because window.devicePixelRatio always returns 1.0 for non-Chrome + // windows (see bug 13875), we use screenPixelsPerCSSPixel for the + // content window. + let pixRatio = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .screenPixelsPerCSSPixel; + let tbXpos = Math.round(aToolbarButtonXPos / pixRatio); + + arrowHeadDiv.style.display = "block"; // Must be visible to get offsetWidth. + let arrowHalfWidth = Math.round(arrowHeadDiv.offsetWidth / 2); + let leftAnchor = textElem.offsetLeft - kArrowMargin + - kBendWidth + Math.round(kArrowLineThickness / 2); + let rightAnchor = textElem.offsetLeft + textElem.offsetWidth + + kArrowMargin + arrowHalfWidth; + + let isArrowOnLeft = (tbXpos < leftAnchor); + let isArrowOnRight = (tbXpos > rightAnchor) && + (tbXpos < (win.innerWidth - arrowHalfWidth)); + let isArrowInMiddle = (tbXpos >= leftAnchor) && (tbXpos <= rightAnchor); + + if (isArrowOnLeft || isArrowOnRight || isArrowInMiddle) + { + // Position the arrow head. + let arrowHeadLeft = tbXpos - arrowHalfWidth; + arrowHeadDiv.style.left = arrowHeadLeft + "px"; + if (isArrowOnLeft || isArrowOnRight) + { + let horzExtBottom = textElem.offsetTop + + Math.round((textElem.offsetHeight + kArrowLineThickness) / 2); + + // Position the vertical (extended) line. + let arrowHeadBottom = arrowHeadDiv.offsetTop + + arrowHeadDiv.offsetHeight; + vertExtDiv.style.top = arrowHeadBottom + "px"; + vertExtDiv.style.left = (arrowHeadLeft + kArrowHeadExtraWidth) + "px"; + let ht = horzExtBottom - kBendHeight - arrowHeadBottom; + vertExtDiv.style.height = ht + "px"; + + // Position the bend (elbow). + bendDiv.style.top = (horzExtBottom - kBendHeight) + "px"; + let bendDivLeft; + if (isArrowOnLeft) + { + bendDiv.setAttribute("pos", "left"); + bendDivLeft = arrowHeadLeft + kArrowHeadExtraWidth; + } + else if (isArrowOnRight) + { + bendDiv.setAttribute("pos", "right"); + bendDivLeft = arrowHeadLeft + kArrowHeadExtraWidth + + kArrowLineThickness - kBendWidth; + } + bendDiv.style.left = bendDivLeft + "px"; + + // Position the horizontal (extended) line. + horzExtDiv.style.top = (horzExtBottom - kArrowLineThickness) + "px"; + let horzExtLeft, w; + if (isArrowOnLeft) + { + horzExtLeft = bendDivLeft + kBendWidth; + w = (textElem.offsetLeft - horzExtLeft - kArrowMargin); + } + else + { + horzExtLeft = rightAnchor - arrowHalfWidth; + w = tbXpos - arrowHalfWidth - horzExtLeft; + } + horzExtDiv.style.left = horzExtLeft + "px"; + horzExtDiv.style.width = w + "px"; + } + } + + let headDisplay = (isArrowOnLeft || isArrowInMiddle || isArrowOnRight) + ? "block" : "none"; + arrowHeadDiv.style.display = headDisplay; + let tailDisplay = (isArrowOnLeft || isArrowOnRight) ? "block" : "none"; + for (let elem of arrowTailElems) + elem.style.display = tailDisplay; + } catch (e) {} + } +}; + +AboutTorListener.init(this); diff --git a/src/chrome/content/aboutTor/aboutTor.xhtml b/src/chrome/content/aboutTor/aboutTor.xhtml index e4050fc..7ae4b8b 100644 --- a/src/chrome/content/aboutTor/aboutTor.xhtml +++ b/src/chrome/content/aboutTor/aboutTor.xhtml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - - Copyright (c) 2016, The Tor Project, Inc. + - Copyright (c) 2017, The Tor Project, Inc. - See LICENSE for licensing information. - vim: set sw=2 sts=2 ts=8 et syntax=xml: --> @@ -20,154 +20,17 @@ <head> <title>&aboutTor.title;</title> <link rel="stylesheet" type="text/css" media="all" - href="chrome://torbutton/skin/aboutTor.css"/> + href="resource://torbutton/chrome/skin/aboutTor.css"/> <script type="text/javascript;version=1.7"> <![CDATA[ -Components.utils.import("resource://gre/modules/Services.jsm"); - -function onLoad() -{ - insertPropertyStrings(); - - let locale = Services.prefs.getCharPref("general.useragent.locale"); - document.getElementById("manualLink").href = "https://tb-manual.torproject.org/" + locale; - - document.addEventListener("AboutTorAdjustArrow", function() { - adjustToolbarIconArrow(); - }, false); - - window.setTimeout( function() { - adjustToolbarIconArrow(); - }, 0); -} - -function adjustToolbarIconArrow() -{ - let textElem = document.getElementById("updatePrompt"); - let arrowHeadDiv = document.getElementById("toolbarIconArrowHead"); - let vertExtDiv = document.getElementById("toolbarIconArrowVertExtension"); - let bendDiv = document.getElementById("toolbarIconArrowBend"); - let horzExtDiv = document.getElementById("toolbarIconArrowHorzExtension"); - if (!textElem || !arrowHeadDiv || !vertExtDiv || !bendDiv || !horzExtDiv) - return; - - let arrowTailElems = [ vertExtDiv, bendDiv, horzExtDiv ]; - let tbXpos; - if (document.body.hasAttribute("torbutton-xpos")) - tbXpos = parseInt(document.body.getAttribute("torbutton-xpos"), 10); - - if (!tbXpos || isNaN(tbXpos) || (tbXpos < 0)) - { - arrowHeadDiv.style.display = "none"; - for (let elem of arrowTailElems) - elem.style.display = "none"; - return; - } - - const kArrowMargin = 6; // Horizontal margin between line and text. - const kArrowHeadExtraWidth = 9; // Horizontal margin to the line. - const kArrowLineThickness = 11; - const kBendWidth = 22; - const kBendHeight = 22; - - arrowHeadDiv.style.display = "block"; // Must be visible to get offsetWidth. - let arrowHalfWidth = Math.round(arrowHeadDiv.offsetWidth / 2); - let leftAnchor = textElem.offsetLeft - kArrowMargin - - kBendWidth + Math.round(kArrowLineThickness / 2); - let rightAnchor = textElem.offsetLeft + textElem.offsetWidth - + kArrowMargin + arrowHalfWidth; - - let isArrowOnLeft = (tbXpos < leftAnchor); - let isArrowOnRight = (tbXpos > rightAnchor) && - (tbXpos < (window.innerWidth - arrowHalfWidth)); - let isArrowInMiddle = (tbXpos >= leftAnchor) && (tbXpos <= rightAnchor); - - if (isArrowOnLeft || isArrowOnRight || isArrowInMiddle) - { - // Position the arrow head. - let arrowHeadLeft = tbXpos - arrowHalfWidth; - arrowHeadDiv.style.left = arrowHeadLeft + "px"; - if (isArrowOnLeft || isArrowOnRight) - { - let horzExtBottom = textElem.offsetTop + - Math.round((textElem.offsetHeight + kArrowLineThickness) / 2); - - // Position the vertical (extended) line. - let arrowHeadBottom = arrowHeadDiv.offsetTop + arrowHeadDiv.offsetHeight; - vertExtDiv.style.top = arrowHeadBottom + "px"; - vertExtDiv.style.left = (arrowHeadLeft + kArrowHeadExtraWidth) + "px"; - let ht = horzExtBottom - kBendHeight - arrowHeadBottom; - vertExtDiv.style.height = ht + "px"; - - // Position the bend (elbow). - bendDiv.style.top = (horzExtBottom - kBendHeight) + "px"; - let bendDivLeft; - if (isArrowOnLeft) - { - bendDiv.setAttribute("pos", "left"); - bendDivLeft = arrowHeadLeft + kArrowHeadExtraWidth; - } - else if (isArrowOnRight) - { - bendDiv.setAttribute("pos", "right"); - bendDivLeft = arrowHeadLeft + kArrowHeadExtraWidth - + kArrowLineThickness - kBendWidth; - } - bendDiv.style.left = bendDivLeft + "px"; - - // Position the horizontal (extended) line. - horzExtDiv.style.top = (horzExtBottom - kArrowLineThickness) + "px"; - let horzExtLeft, w; - if (isArrowOnLeft) - { - horzExtLeft = bendDivLeft + kBendWidth; - w = (textElem.offsetLeft - horzExtLeft - kArrowMargin); - } - else - { - horzExtLeft = rightAnchor - arrowHalfWidth; - w = tbXpos - arrowHalfWidth - horzExtLeft; - } - horzExtDiv.style.left = horzExtLeft + "px"; - horzExtDiv.style.width = w + "px"; - } - } - - let headDisplay = (isArrowOnLeft || isArrowInMiddle || isArrowOnRight) - ? "block" : "none"; - arrowHeadDiv.style.display = headDisplay; - let tailDisplay = (isArrowOnLeft || isArrowOnRight) ? "block" : "none"; - for (let elem of arrowTailElems) - elem.style.display = tailDisplay; -} - -function insertPropertyStrings() -{ - try { - let kPropertiesURL = "chrome://torbutton/locale/aboutTor.properties"; - - let gStringBundle = Services.strings.createBundle(kPropertiesURL); - let s1 = gStringBundle.GetStringFromName("aboutTor.searchDDG.privacy.link"); - let s2 = gStringBundle.GetStringFromName("aboutTor.searchDDG.search.link"); - let result = gStringBundle.formatStringFromName("aboutTor.searchDDG.privacy", - [s1, s2], 2); - if (result) { - let elem = document.getElementById("searchProviderInfo"); - if (elem) - elem.innerHTML = result; - } - } catch(e) {}; -} - window.addEventListener("pageshow", function() { let evt = new CustomEvent("AboutTorLoad", { bubbles: true }); document.dispatchEvent(evt); }); - ]]> </script> </head> -<body dir="&locale.dir;" onload="onLoad();"> +<body dir="&locale.dir;"> <div id="torstatus" class="top"> <div id="torstatus-version"/> <div id="torstatus-image"/> diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js index ae705a3..3999bd4 100644 --- a/src/chrome/content/torbutton.js +++ b/src/chrome/content/torbutton.js @@ -40,8 +40,6 @@ var m_tb_control_host = null; // Set if using TCP. var m_tb_control_pass = null; var m_tb_control_desc = null; // For logging.
-var m_tb_orig_BrowserOnAboutPageLoad = null; - var m_tb_domWindowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor). getInterface(Ci.nsIDOMWindowUtils);
@@ -203,15 +201,24 @@ var torbutton_tor_check_observer = { // Update toolbar icon and tooltip. torbutton_update_toolbutton();
- // Update all open about:tor pages. If the user does not have an - // about:tor page open in the front most window, open one. - if (torbutton_update_all_abouttor_pages(undefined, false) < 1) { - var wm = Cc["@mozilla.org/appshell/window-mediator;1"] + // Update all open about:tor pages. + torbutton_abouttor_message_handler.updateAllOpenPages(); + + // If the user does not have an about:tor tab open in the front most + // window, open one. + var wm = Cc["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); - var win = wm.getMostRecentWindow("navigator:browser"); - if (win == window) { - gBrowser.selectedTab = gBrowser.addTab("about:tor"); + var win = wm.getMostRecentWindow("navigator:browser"); + if (win == window) { + let foundTab = false; + let tabBrowser = top.getBrowser(); + for (let i = 0; !foundTab && (i < tabBrowser.browsers.length); ++i) { + let b = tabBrowser.getBrowserAtIndex(i); + foundTab = (b.currentURI.spec.toLowerCase() == "about:tor"); } + + if (!foundTab) + gBrowser.selectedTab = gBrowser.addTab("about:tor"); } } } @@ -332,10 +339,10 @@ function torbutton_init() { } }
- // Add event listener for about:tor page loads. - document.addEventListener("AboutTorLoad", function(aEvent) { - torbutton_on_abouttor_load(aEvent.target); - }, false, true); + // Add about:tor IPC message listeners. + let aboutTorMessages = [ "AboutTor:Loaded", "AboutTor:GetToolbarData" ]; + aboutTorMessages.forEach(aMsg => window.messageManager.addMessageListener( + aMsg, torbutton_abouttor_message_handler));
// XXX: Get rid of the cached asmjs (or IndexedDB) files on disk in case we // don't allow things saved to disk. This is an ad-hoc fix to work around @@ -365,7 +372,7 @@ function torbutton_init() {
// Detect toolbar customization and update arrow on about:tor pages. window.addEventListener("aftercustomization", function() { - torbutton_update_all_abouttor_pages(undefined, undefined); + torbutton_abouttor_message_handler.updateAllOpenPages(); }, false);
//setting up context menu @@ -404,9 +411,58 @@ function torbutton_init() {
torbutton_init_user_manual_links();
+ // Arrange for our about:tor content script to be loaded in each frame. + window.messageManager.loadFrameScript( + "chrome://torbutton/content/aboutTor/aboutTor-content.js", true); + torbutton_log(3, 'init completed'); }
+var torbutton_abouttor_message_handler = { + // Receive IPC messages from the about:tor content script. + receiveMessage: function(aMessage) { + switch(aMessage.name) { + case "AboutTor:Loaded": + torbutton_show_sec_slider_notification(); + aMessage.target.messageManager.sendAsyncMessage("AboutTor:ChromeData", + this.chromeData); + break; + case "AboutTor:GetToolbarData": + aMessage.target.messageManager.sendAsyncMessage("AboutTor:ToolbarData", + this.toolbarData); + break; + } + }, + + // Send privileged data to all of the about:tor content scripts. + updateAllOpenPages: function() { + window.messageManager.broadcastAsyncMessage("AboutTor:ChromeData", + this.chromeData); + }, + + // The chrome data contains all of the data needed by the about:tor + // content process that is only available here (in the chrome process). + // It is sent to the content process when an about:tor window is opened + // and in response to events such as the browser noticing that an update + // is available. + get chromeData() { + return { + torOn: torbutton_tor_check_ok(), + updateNeeded: torbutton_update_is_needed(), + showManual: torbutton_show_torbrowser_manual(), + toolbarButtonXPos: torbutton_get_toolbarbutton_xpos() + }; + }, + + // The toolbar data only contains the x coordinate of Torbutton's toolbar + // item; it is sent back to the content process as the about:tor window + // is resized. + get toolbarData() { + return { + toolbarButtonXPos: torbutton_get_toolbarbutton_xpos() + }; + } +};
function torbutton_should_prompt_for_language_preference() { return torbutton_get_general_useragent_locale().substring(0, 2) != "en" && @@ -570,7 +626,7 @@ function torbutton_notify_if_update_needed() { setOrClearAttribute(btn, "tbUpdateNeeded", updateNeeded);
// Update all open about:tor pages. - torbutton_update_all_abouttor_pages(updateNeeded, undefined); + torbutton_abouttor_message_handler.updateAllOpenPages();
// Make the "check for update" menu item bold if an update is needed. var item = document.getElementById("torbutton-checkForUpdate"); @@ -597,117 +653,11 @@ function torbutton_check_for_update() { prompter.checkForUpdates(); }
-// Pass undefined for a parameter to have this function determine it. -// Returns a count of open pages that were updated, -function torbutton_update_all_abouttor_pages(aUpdateNeeded, aTorIsOn) { - if (aUpdateNeeded === undefined) - aUpdateNeeded = torbutton_update_is_needed(); - if (aTorIsOn === undefined) - aTorIsOn = torbutton_tor_check_ok(); - - var count = 0; - var tabBrowser = top.getBrowser(); - var tabs = tabBrowser.mTabs; - if (tabs && (tabs.length > 0)) { - for (var tab = tabs[0]; tab != null; tab = tab.nextSibling) { - try { - let doc = tabBrowser.getBrowserForTab(tab).contentDocument; - if (torbutton_update_abouttor_doc(doc, aTorIsOn, aUpdateNeeded)) - ++count; - } catch(e) {} - } - } - - return count; -} - -// Returns true if aDoc is an about:tor page. -function torbutton_update_abouttor_doc(aDoc, aTorOn, aUpdateNeeded) { - var isAboutTor = torbutton_is_abouttor_doc(aDoc); - if (isAboutTor) { - if (aTorOn) - aDoc.body.setAttribute("toron", "yes"); - else - aDoc.body.removeAttribute("toron"); - - if (aUpdateNeeded) - aDoc.body.setAttribute("torNeedsUpdate", "yes"); - else - aDoc.body.removeAttribute("torNeedsUpdate"); - - if (torbutton_show_torbrowser_manual()) - aDoc.body.setAttribute("showmanual", "yes"); - else - aDoc.body.removeAttribute("showmanual"); - - // Display product name and TBB version. - try { - const kBrandBundle = "chrome://branding/locale/brand.properties"; - let brandBundle = Cc["@mozilla.org/intl/stringbundle;1"] - .getService(Ci.nsIStringBundleService) - .createBundle(kBrandBundle); - let productName = brandBundle.GetStringFromName("brandFullName"); - let tbbVersion = m_tb_prefs.getCharPref("torbrowser.version"); - let e = aDoc.getElementById("torstatus-version"); - - while (e.firstChild) - e.removeChild(e.firstChild); - e.appendChild(aDoc.createTextNode(productName + '\n' + tbbVersion)); - } catch (e) {} - - let containerName = "torstatus-" + (aTorOn ? "on" : "off") + "-container"; - torbutton_adjust_abouttor_fontsizes(aDoc, containerName); - torbutton_update_abouttor_arrow(aDoc); - } - - return isAboutTor; -} - -// Ensure that text in top area does not overlap the tor on/off (onion) image. -// This is done by reducing the font sizes as necessary. -function torbutton_adjust_abouttor_fontsizes(aDoc, aContainerName) -{ - let imgElem = aDoc.getElementById("torstatus-image"); - let containerElem = aDoc.getElementById(aContainerName); - if (!imgElem || !containerElem) - return; - - try - { - let imgRect = imgElem.getBoundingClientRect(); - - for (let textElem = containerElem.firstChild; textElem; - textElem = textElem.nextSibling) - { - if ((textElem.nodeType != textElem.ELEMENT_NODE) || - (textElem.nodeName.toLowerCase() == "br")) - { - continue; - } - - let textRect = textElem.getBoundingClientRect(); - if (0 == textRect.width) - continue; - - // Reduce font to 90% of previous size, repeating the process up to 7 - // times. This allows for a maximum reduction to just less than 50% of - // the original size. - let maxTries = 7; - while ((textRect.left < imgRect.right) && (--maxTries >= 0)) - { - let style = aDoc.defaultView.getComputedStyle(textElem, null); - let fontSize = parseFloat(style.getPropertyValue("font-size")); - textElem.style.fontSize = (fontSize * 0.9) + "px"; - textRect = textElem.getBoundingClientRect(); - } - } - } catch (e) {} -} - -// Determine X position of torbutton toolbar item and pass it through -// to the xhtml document by setting a torbutton-xpos attribute on the body. -// The value that is set takes retina displays and content zoom into account. -function torbutton_update_abouttor_arrow(aDoc) { +// Determine X position of Torbutton toolbar item. The value returned +// accounts for retina but not content zoom. +// undefined is returned if the value cannot be determined (e.g., if the +// toolbar item is not on the toolbar). +function torbutton_get_toolbarbutton_xpos() { try { let tbXpos = -1; let tbItem = torbutton_get_toolbutton(); @@ -721,55 +671,16 @@ function torbutton_update_abouttor_arrow(aDoc) { }
if (tbXpos >= 0) { - // Convert to device-independent units, compensating for retina display - // and content zoom that may be in effect on the about:tor page. - // Because window.devicePixelRatio always returns 1.0 for non-Chrome - // windows (see bug 13875), we use screenPixelsPerCSSPixel for the - // content window. + // Convert to device-independent units, compensating for retina display. tbXpos *= window.devicePixelRatio; - let pixRatio = gBrowser.contentWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .screenPixelsPerCSSPixel; - tbXpos = Math.round(tbXpos / pixRatio); - aDoc.body.setAttribute("torbutton-xpos", tbXpos); - } else { - aDoc.body.removeAttribute("torbutton-xpos"); + return tbXpos; } - - let evt = new Event("AboutTorAdjustArrow"); - aDoc.dispatchEvent(evt); } catch(e) {} -} - -function torbutton_on_abouttor_load(aDoc) { - if (torbutton_is_abouttor_doc(aDoc) && - !aDoc.documentElement.hasAttribute("aboutTorLoaded")) { - aDoc.documentElement.setAttribute("aboutTorLoaded", true); - - // Show correct Tor on/off and "update needed" status. - let torOn = torbutton_tor_check_ok(); - let needsUpdate = torbutton_update_is_needed(); - torbutton_update_abouttor_doc(aDoc, torOn, needsUpdate); - - aDoc.defaultView.addEventListener("resize", - function() { torbutton_update_abouttor_arrow(aDoc); }, - false); - - // Insert "Test Tor Network Settings" url. - let elem = aDoc.getElementById("testTorSettings"); - if (elem) { - let locale = m_tb_prefs.getCharPref("general.useragent.locale"); - locale = locale.replace(/-/g, '_'); - let url = m_tb_prefs.getCharPref( - "extensions.torbutton.test_url_interactive"); - elem.href = url.replace(/__LANG__/g, locale); - } - }
- if (m_tb_orig_BrowserOnAboutPageLoad) - m_tb_orig_BrowserOnAboutPageLoad(aDoc); + return undefined; +}
+function torbutton_show_sec_slider_notification() { // Show the notification about the new security slider. if (m_tb_prefs. getBoolPref("extensions.torbutton.show_slider_notification")) { @@ -797,10 +708,6 @@ function torbutton_on_abouttor_load(aDoc) { } }
-function torbutton_is_abouttor_doc(aDoc) { - return (aDoc && /^about:tor$/i.test(aDoc.documentURI.toLowerCase())); -} - // Bug 1506 P4: Checking for Tor Browser updates is pretty important, // probably even as a fallback if we ever do get a working updater. function torbutton_do_async_versioncheck() { @@ -2449,7 +2356,7 @@ function torbutton_init_user_manual_links() { let menuitem = document.getElementById("torBrowserUserManual"); bindPrefAndInit("general.useragent.locale", val => { menuitem.hidden = !torbutton_show_torbrowser_manual(); - torbutton_update_all_abouttor_pages(undefined, undefined); + torbutton_abouttor_message_handler.updateAllOpenPages(); }); }
diff --git a/src/chrome/skin/aboutTor.css b/src/chrome/skin/aboutTor.css index 3f747a9..c0d2ebc 100644 --- a/src/chrome/skin/aboutTor.css +++ b/src/chrome/skin/aboutTor.css @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Tor Project, Inc. + * Copyright (c) 2017, The Tor Project, Inc. * See LICENSE for licensing information. * * vim: set sw=2 sts=2 ts=8 et syntax=css: @@ -34,6 +34,15 @@ body[toron] { background-image: -moz-linear-gradient(top, #ffffff, #ffffff 10%, #d5ffd5 50%, #d5ffd5); }
+/* Hide the entire document by default to avoid showing the incorrect + * Tor on / off status (that info must be retrieved from the chrome + * process, which involves IPC when multiprocess mode is enabled). An + * initialized attribute will be added as soon as the status is known. + */ +body:not([initialized]) { + display: none; +} + #torstatus-version { position: fixed; top: 6px; diff --git a/src/components/aboutTor.js b/src/components/aboutTor.js index 2a3431f..e3ee03d 100644 --- a/src/components/aboutTor.js +++ b/src/components/aboutTor.js @@ -1,9 +1,9 @@ /************************************************************************* - * Copyright (c) 2013, The Tor Project, Inc. + * Copyright (c) 2017, The Tor Project, Inc. * See LICENSE for licensing information. * * vim: set sw=2 sts=2 ts=8 et syntax=javascript: - * + * * about:tor component *************************************************************************/
@@ -17,9 +17,9 @@ const kAboutTorURL = "chrome://torbutton/content/aboutTor/aboutTor.xhtml"; const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; - + Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - + function AboutTor() { } @@ -35,11 +35,12 @@ AboutTor.prototype = contractID: kMODULE_CONTRACTID,
// nsIAboutModule implementation: - newChannel: function(aURI) + newChannel: function(aURI, aLoadInfo) { let ioSvc = Cc["@mozilla.org/network/io-service;1"] .getService(Ci.nsIIOService); - let channel = ioSvc.newChannel(kAboutTorURL, null, null); + let uri = ioSvc.newURI(kAboutTorURL, null, null); + let channel = ioSvc.newChannelFromURIWithLoadInfo(uri, aLoadInfo); channel.originalURI = aURI;
return channel; @@ -47,9 +48,13 @@ AboutTor.prototype =
getURIFlags: function(aURI) { - return Ci.nsIAboutModule.ALLOW_SCRIPT; + return Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | + Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD | + Ci.nsIAboutModule.ALLOW_SCRIPT; } };
-const NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutTor]); +let factory = XPCOMUtils._getFactory(AboutTor); +let reg = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); +reg.registerFactory(kMODULE_CID, "", kMODULE_CONTRACTID, factory); diff --git a/src/components/startup-observer.js b/src/components/startup-observer.js index 65ca659..dac7aff 100644 --- a/src/components/startup-observer.js +++ b/src/components/startup-observer.js @@ -61,12 +61,15 @@ function StartupObserver() { this.logger.log(4, "Early proxy change failed. Will try again at profile load. Error: "+e); }
- // Arrange for our nsIContentPolicy filter to be loaded in the - // default (chrome) process as well as in each content process. + // Arrange for our nsIContentPolicy filter and about:tor handler to be + // loaded in the default (chrome) process as well as in each content + // process. let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"] .getService(Ci.nsIProcessScriptLoader); ppmm.loadProcessScript("resource://torbutton/components/content-policy.js", true); + ppmm.loadProcessScript("resource://torbutton/components/aboutTor.js", + true); }
StartupObserver.prototype = {
tor-commits@lists.torproject.org