[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-102.9.0esr-12.5-1] 4 commits: Bug 41631: Prevent weird initial window dimensions caused by subpixel computations

Pier Angelo Vendrame (@pierov) git at gitlab.torproject.org
Wed Mar 29 10:09:57 UTC 2023



Pier Angelo Vendrame pushed to branch tor-browser-102.9.0esr-12.5-1 at The Tor Project / Applications / Tor Browser


Commits:
16c5a240 by hackademix at 2023-03-28T22:53:50+02:00
Bug 41631: Prevent weird initial window dimensions caused by subpixel computations

- - - - -
1d0cc334 by hackademix at 2023-03-28T23:30:47+02:00
fixup! Bug 10760: Integrate TorButton to TorBrowser core

- - - - -
e32a6cd3 by hackademix at 2023-03-28T23:33:12+02:00
fixup! Bug 40562: Added Tor Browser preferences to 000-tor-browser.js

- - - - -
be88af1c by hackademix at 2023-03-29T09:52:37+02:00
Bug 41695: Warn on window maximization without letterboxing in RFPHelper module

- - - - -


5 changed files:

- browser/app/profile/000-tor-browser.js
- browser/locales/en-US/browser/languageNotification.ftl
- toolkit/components/resistfingerprinting/RFPHelper.jsm
- toolkit/torbutton/chrome/content/torbutton.js
- toolkit/torbutton/chrome/locale/en-US/torbutton.properties


Changes:

=====================================
browser/app/profile/000-tor-browser.js
=====================================
@@ -65,11 +65,6 @@ pref("extensions.torbutton.use_nontor_proxy", false);
 // State prefs:
 pref("extensions.torbutton.startup", false);
 
-// This is only used when letterboxing is disabled.
-// See #7255 for details. We display the warning three times to make sure the
-// user did not click on it by accident.
-pref("extensions.torbutton.maximize_warnings_remaining", 3);
-
 // Security prefs:
 pref("extensions.torbutton.resize_new_windows", false);
 pref("extensions.torbutton.launch_warning", true);


=====================================
browser/locales/en-US/browser/languageNotification.ftl
=====================================
@@ -8,3 +8,7 @@ language-notification-label-system = { -brand-short-name } has set your display
 # $language is the language Tor Browser is displayed in (already translated).
 language-notification-label = { -brand-short-name } has set your display language to { $language }.
 language-notification-button = Change Language…
+
+basebrowser-rfp-maximize-warning-message = Maximizing the browser window can allow websites to determine your monitor size, which can be used to track you. We recommend that you leave browser windows in their original default size.
+basebrowser-rfp-restore-window-size-button-label = Restore
+basebrowser-rfp-restore-window-size-button-ak = R


=====================================
toolkit/components/resistfingerprinting/RFPHelper.jsm
=====================================
@@ -22,16 +22,110 @@ const kPrefLetterboxingTesting =
   "privacy.resistFingerprinting.letterboxing.testing";
 const kTopicDOMWindowOpened = "domwindowopened";
 
-var logConsole;
-function log(msg) {
-  if (!logConsole) {
-    logConsole = console.createInstance({
-      prefix: "RFPHelper.jsm",
-      maxLogLevelPref: "privacy.resistFingerprinting.jsmloglevel",
-    });
+const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings";
+
+XPCOMUtils.defineLazyGetter(this, "logConsole", () =>
+  console.createInstance({
+    prefix: "RFPHelper.jsm",
+    maxLogLevelPref: "privacy.resistFingerprinting.jsmloglevel",
+  })
+);
+
+function log(...args) {
+  logConsole.log(...args);
+}
+
+function forEachWindow(callback) {
+  const windowList = Services.wm.getEnumerator("navigator:browser");
+  while (windowList.hasMoreElements()) {
+    const win = windowList.getNext();
+    if (win.gBrowser && !win.closed) {
+      try {
+        callback(win);
+      } catch (e) {
+        logConsole.error(e);
+      }
+    }
+  }
+}
+
+
+async function windowResizeHandler(aEvent) {
+  if (RFPHelper.letterboxingEnabled) {
+    return;
+  }
+  if (Services.prefs.getIntPref(kPrefResizeWarnings, 3) <= 0) {
+    return;
+  }
+
+  const window = aEvent.currentTarget;
+
+  // Wait for end of execution queue to ensure we have correct windowState.
+  await new Promise(resolve => window.setTimeout(resolve, 0));
+  switch (window.windowState) {
+    case window.STATE_MAXIMIZED:
+    case window.STATE_FULLSCREEN:
+      break;
+    default:
+      return;
+  }
+
+  // Do not add another notification if one is already showing.
+  const kNotificationName = "rfp-window-resize-notification";
+  let box = window.gNotificationBox;
+  if (box.getNotificationWithValue(kNotificationName)) {
+    return;
   }
 
-  logConsole.log(msg);
+  // Rate-limit showing our notification if needed.
+  if (Date.now() - (windowResizeHandler.timestamp || 0) < 1000) {
+    return;
+  }
+  windowResizeHandler.timestamp = Date.now();
+
+  const decreaseWarningsCount = () => {
+    const currentCount = Services.prefs.getIntPref(kPrefResizeWarnings);
+    if (currentCount > 0) {
+      Services.prefs.setIntPref(kPrefResizeWarnings, currentCount - 1);
+    }
+  };
+
+  const [label, accessKey] = await window.document.l10n.formatValues([
+    { id: "basebrowser-rfp-restore-window-size-button-label" },
+    { id: "basebrowser-rfp-restore-window-size-button-ak" },
+  ]);
+
+  const buttons = [
+    {
+      label,
+      accessKey,
+      popup: null,
+      callback() {
+        // reset notification timer to work-around resize race conditions
+        windowResizeHandler.timestamp = Date.now();
+        // restore the original (rounded) size we had stored on window startup
+        let { _rfpOriginalSize } = window;
+        window.setTimeout(() => {
+          window.resizeTo(_rfpOriginalSize.width, _rfpOriginalSize.height);
+        }, 0);
+      },
+    },
+  ];
+
+  box.appendNotification(
+    kNotificationName,
+    {
+      label: { "l10n-id": "basebrowser-rfp-maximize-warning-message" },
+      priority: box.PRIORITY_WARNING_LOW,
+      eventCallback(event) {
+        if (event === "dismissed") {
+          // user manually dismissed the notification
+          decreaseWarningsCount();
+        }
+      },
+    },
+    buttons
+  );
 }
 
 class _RFPHelper {
@@ -158,7 +252,11 @@ class _RFPHelper {
   _handleResistFingerprintingChanged() {
     if (Services.prefs.getBoolPref(kPrefResistFingerprinting)) {
       this._addRFPObservers();
+      Services.ww.registerNotification(this);
+      forEachWindow(win => this._attachWindow(win));
     } else {
+      forEachWindow(win => this._detachWindow(win));
+      Services.ww.unregisterNotification(this);
       this._removeRFPObservers();
     }
   }
@@ -295,13 +393,11 @@ class _RFPHelper {
   }
 
   _handleLetterboxingPrefChanged() {
-    if (Services.prefs.getBoolPref(kPrefLetterboxing, false)) {
-      Services.ww.registerNotification(this);
-      this._attachAllWindows();
-    } else {
-      this._detachAllWindows();
-      Services.ww.unregisterNotification(this);
-    }
+    this.letterboxingEnabled = Services.prefs.getBoolPref(
+      kPrefLetterboxing,
+      false
+    );
+    forEachWindow(win => this._updateSizeForTabsInWindow(win));
   }
 
   // The function to parse the dimension set from the pref value. The pref value
@@ -402,11 +498,13 @@ class _RFPHelper {
     let logPrefix = `_roundContentSize[${Math.random()}]`;
     log(logPrefix);
     let win = aBrowser.ownerGlobal;
+
     let browserContainer = aBrowser
       .getTabBrowser()
       .getBrowserContainer(aBrowser);
     let browserParent = aBrowser.parentElement;
     browserParent.classList.remove("exclude-letterboxing");
+
     let [
       [contentWidth, contentHeight],
       [parentWidth, parentHeight],
@@ -419,6 +517,27 @@ class _RFPHelper {
       ])
     );
 
+    if (
+      !win._rfpSizeOffset ||
+      (win._rfpOriginalSize &&
+        win.outerWidth === win._rfpOriginalSize.width &&
+        win.outerHeight === win._rfpOriginalSize.height)
+    ) {
+      const BASELINE_ROUNDING = 10;
+      const offset = s =>
+        s - Math.round(s / BASELINE_ROUNDING) * BASELINE_ROUNDING;
+
+      win._rfpSizeOffset = {
+        width: offset(parentWidth),
+        height: offset(parentHeight),
+      };
+      log(
+        `${logPrefix} Window size offsets %o (from %s, %s)`,
+        win._rfpSizeOffset,
+        parentWidth,
+        parentHeight
+      );
+    }
     log(
       `${logPrefix} contentWidth=${contentWidth} contentHeight=${contentHeight} parentWidth=${parentWidth} parentHeight=${parentHeight} containerWidth=${containerWidth} containerHeight=${containerHeight}${
         isNewTab ? " (new tab)." : "."
@@ -437,6 +556,16 @@ class _RFPHelper {
       });
 
       let result;
+
+      if (!this.letterboxingEnabled) {
+        const offset = win._rfpSizeOffset;
+        result = r(aWidth - offset.width, aHeight - offset.height);
+        log(
+          `${logPrefix} Letterboxing disabled, applying baseline rounding offsets: (${aWidth}, ${aHeight}) => ${result.width} x ${result.height})`
+        );
+        return result;
+      }
+
       log(`${logPrefix} roundDimensions(${aWidth}, ${aHeight})`);
       // If the set is empty, we will round the content with the default
       // stepping size.
@@ -554,7 +683,6 @@ class _RFPHelper {
 
   _updateSizeForTabsInWindow(aWindow) {
     let tabBrowser = aWindow.gBrowser;
-
     tabBrowser.tabpanels?.classList.add("letterboxing");
 
     for (let tab of tabBrowser.tabs) {
@@ -564,10 +692,18 @@ class _RFPHelper {
     // we need to add this class late because otherwise new windows get maximized
     aWindow.setTimeout(() => {
       tabBrowser.tabpanels?.classList.add("letterboxing-ready");
+      if (!aWindow._rfpOriginalSize) {
+        aWindow._rfpOriginalSize = {
+          width: aWindow.outerWidth,
+          height: aWindow.outerHeight,
+        };
+        log("Recording original window size", aWindow._rfpOriginalSize);
+      }
     });
   }
 
   _attachWindow(aWindow) {
+    aWindow.addEventListener("sizemodechange", windowResizeHandler);
     aWindow.gBrowser.addTabsProgressListener(this);
     aWindow.addEventListener("TabOpen", this);
     const resizeObserver = (aWindow._rfpResizeObserver = new aWindow.ResizeObserver(
@@ -585,19 +721,7 @@ class _RFPHelper {
     this._updateSizeForTabsInWindow(aWindow);
   }
 
-  _attachAllWindows() {
-    let windowList = Services.wm.getEnumerator("navigator:browser");
 
-    while (windowList.hasMoreElements()) {
-      let win = windowList.getNext();
-
-      if (win.closed || !win.gBrowser) {
-        continue;
-      }
-
-      this._attachWindow(win);
-    }
-  }
 
   _detachWindow(aWindow) {
     let tabBrowser = aWindow.gBrowser;
@@ -614,20 +738,7 @@ class _RFPHelper {
       let browser = tab.linkedBrowser;
       this._resetContentSize(browser);
     }
-  }
-
-  _detachAllWindows() {
-    let windowList = Services.wm.getEnumerator("navigator:browser");
-
-    while (windowList.hasMoreElements()) {
-      let win = windowList.getNext();
-
-      if (win.closed || !win.gBrowser) {
-        continue;
-      }
-
-      this._detachWindow(win);
-    }
+    aWindow.removeEventListener("sizemodechange", windowResizeHandler);
   }
 
   _handleDOMWindowOpened(win) {


=====================================
toolkit/torbutton/chrome/content/torbutton.js
=====================================
@@ -54,19 +54,12 @@ var torbutton_new_circuit;
       m_tb_prefs.addObserver("extensions.torbutton", this);
       m_tb_prefs.addObserver("browser.privatebrowsing.autostart", this);
       m_tb_prefs.addObserver("javascript", this);
-      m_tb_prefs.addObserver("privacy.resistFingerprinting", this);
-      m_tb_prefs.addObserver("privacy.resistFingerprinting.letterboxing", this);
     },
 
     unregister() {
       m_tb_prefs.removeObserver("extensions.torbutton", this);
       m_tb_prefs.removeObserver("browser.privatebrowsing.autostart", this);
       m_tb_prefs.removeObserver("javascript", this);
-      m_tb_prefs.removeObserver("privacy.resistFingerprinting", this);
-      m_tb_prefs.removeObserver(
-        "privacy.resistFingerprinting.letterboxing",
-        this
-      );
     },
 
     // topic:   what event occurred
@@ -83,10 +76,6 @@ var torbutton_new_circuit;
         case "extensions.torbutton.use_nontor_proxy":
           torbutton_use_nontor_proxy();
           break;
-        case "privacy.resistFingerprinting":
-        case "privacy.resistFingerprinting.letterboxing":
-          torbutton_update_fingerprinting_prefs();
-          break;
       }
     },
   };
@@ -666,21 +655,6 @@ var torbutton_new_circuit;
     Services.prefs.savePrefFile(null);
   }
 
-  function torbutton_update_fingerprinting_prefs() {
-    var mode = m_tb_prefs.getBoolPref("privacy.resistFingerprinting");
-    var letterboxing = m_tb_prefs.getBoolPref(
-      "privacy.resistFingerprinting.letterboxing",
-      false
-    );
-    m_tb_prefs.setBoolPref(
-      "extensions.torbutton.resize_new_windows",
-      mode && !letterboxing
-    );
-
-    // Force prefs to be synced to disk
-    Services.prefs.savePrefFile(null);
-  }
-
   // Bug 1506 P1: This function just cleans up prefs that got set badly in previous releases
   function torbutton_fixup_old_prefs() {
     if (m_tb_prefs.getIntPref("extensions.torbutton.pref_fixup_version") < 1) {
@@ -728,9 +702,6 @@ var torbutton_new_circuit;
       // Bug 1506: Should probably be moved to an XPCOM component
       torbutton_do_main_window_startup();
 
-      // For charsets
-      torbutton_update_fingerprinting_prefs();
-
       // Bug 30565: sync browser.privatebrowsing.autostart with security.nocertdb
       torbutton_update_disk_prefs();
 
@@ -741,46 +712,6 @@ var torbutton_new_circuit;
     }
   }
 
-  // Bug 1506 P3: Used to decide if we should resize the window.
-  //
-  // Returns true if the window wind is neither maximized, full screen,
-  // ratpoisioned/evilwmed, nor minimized.
-  function torbutton_is_windowed(wind) {
-    torbutton_log(
-      3,
-      "Window: (" +
-        wind.outerWidth +
-        "," +
-        wind.outerHeight +
-        ") ?= (" +
-        wind.screen.availWidth +
-        "," +
-        wind.screen.availHeight +
-        ")"
-    );
-    if (
-      wind.windowState == Ci.nsIDOMChromeWindow.STATE_MINIMIZED ||
-      wind.windowState == Ci.nsIDOMChromeWindow.STATE_MAXIMIZED
-    ) {
-      torbutton_log(2, "Window is minimized/maximized");
-      return false;
-    }
-    if ("fullScreen" in wind && wind.fullScreen) {
-      torbutton_log(2, "Window is fullScreen");
-      return false;
-    }
-    if (
-      wind.outerHeight == wind.screen.availHeight &&
-      wind.outerWidth == wind.screen.availWidth
-    ) {
-      torbutton_log(3, "Window is ratpoisoned/evilwm'ed");
-      return false;
-    }
-
-    torbutton_log(2, "Window is normal");
-    return true;
-  }
-
   // Bug 1506 P3: This is needed pretty much only for the window resizing.
   // See comments for individual functions for details
   function torbutton_new_window(event) {
@@ -797,17 +728,6 @@ var torbutton_new_circuit;
     }
 
     torbutton_do_startup();
-
-    let progress = Cc["@mozilla.org/docloaderservice;1"].getService(
-      Ci.nsIWebProgress
-    );
-
-    if (torbutton_is_windowed(window)) {
-      progress.addProgressListener(
-        torbutton_resizelistener,
-        Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT
-      );
-    }
   }
 
   // Bug 1506 P2: This is only needed because we have observers
@@ -815,8 +735,6 @@ var torbutton_new_circuit;
   function torbutton_close_window(event) {
     torbutton_tor_check_observer.unregister();
 
-    window.removeEventListener("sizemodechange", m_tb_resize_handler);
-
     // TODO: This is a real ghetto hack.. When the original window
     // closes, we need to find another window to handle observing
     // unique events... The right way to do this is to move the
@@ -856,158 +774,4 @@ var torbutton_new_circuit;
 
   window.addEventListener("load", torbutton_new_window);
   window.addEventListener("unload", torbutton_close_window);
-
-  var m_tb_resize_handler = null;
-  var m_tb_resize_date = null;
-
-  // Bug 1506 P1/P3: Setting a fixed window size is important, but
-  // probably not for android.
-  var torbutton_resizelistener = {
-    QueryInterface: ChromeUtils.generateQI([
-      "nsIWebProgressListener",
-      "nsISupportsWeakReference",
-    ]),
-
-    onLocationChange(aProgress, aRequest, aURI) {},
-    onStateChange(aProgress, aRequest, aFlag, aStatus) {
-      if (aFlag & Ci.nsIWebProgressListener.STATE_STOP) {
-        window.promiseDocumentFlushed(() => {
-          // Here we're guaranteed to read the "final" (!) initial size, rather than [1, 1].
-          torbutton_resizelistener.originalSize = {
-            width: window.outerWidth,
-            height: window.outerHeight,
-          };
-        });
-        m_tb_resize_handler = async function() {
-          // Wait for end of execution queue to ensure we have correct windowState.
-          await new Promise(resolve => setTimeout(resolve, 0));
-          if (
-            window.windowState === window.STATE_MAXIMIZED ||
-            window.windowState === window.STATE_FULLSCREEN
-          ) {
-            const kRemainingWarnings =
-              "extensions.torbutton.maximize_warnings_remaining";
-            if (
-              Services.prefs.getBoolPref(
-                "extensions.torbutton.resize_new_windows"
-              ) &&
-              Services.prefs.getIntPref(kRemainingWarnings) > 0
-            ) {
-              // Do not add another notification if one is already showing.
-              const kNotificationName = "torbutton-maximize-notification";
-
-              const box = gNotificationBox;
-              if (box.getNotificationWithValue(kNotificationName)) {
-                return;
-              }
-
-              // Rate-limit showing our notification if needed.
-              if (m_tb_resize_date === null) {
-                m_tb_resize_date = Date.now();
-              } else {
-                // We wait at least another second before we show a new
-                // notification. Should be enough to rule out OSes that call our
-                // handler rapidly due to internal workings.
-                if (Date.now() - m_tb_resize_date < 1000) {
-                  return;
-                }
-                // Resizing but we need to reset |m_tb_resize_date| now.
-                m_tb_resize_date = Date.now();
-              }
-
-              // No need to get "OK" translated again.
-              const bundle = Services.strings.createBundle(
-                "chrome://global/locale/commonDialogs.properties"
-              );
-
-              const decreaseWarningsCount = () => {
-                const currentCount = Services.prefs.getIntPref(
-                  kRemainingWarnings
-                );
-                if (currentCount > 0) {
-                  Services.prefs.setIntPref(
-                    kRemainingWarnings,
-                    currentCount - 1
-                  );
-                }
-              };
-
-              let buttons = [
-                {
-                  label: bundle.GetStringFromName("OK"),
-                  accessKey: "O",
-                  popup: null,
-                  callback() {
-                    // reset notification timer to work-around resize race conditions
-                    m_tb_resize_date = Date.now();
-                    // restore the original (rounded) size we had stored on window startup
-                    let { originalSize } = torbutton_resizelistener;
-                    window.resizeTo(originalSize.width, originalSize.height);
-                  },
-                },
-              ];
-
-              const label = torbutton_get_property_string(
-                "torbutton.maximize_warning"
-              );
-
-              box.appendNotification(
-                kNotificationName,
-                {
-                  label,
-                  priority: box.PRIORITY_WARNING_LOW,
-                  eventCallback(event) {
-                    if (event === "dismissed") {
-                      // user manually dismissed the notification
-                      decreaseWarningsCount();
-                    }
-                  },
-                },
-                buttons
-              );
-            }
-          }
-        }; // m_tb_resize_handler
-
-        // We need to handle OSes that auto-maximize windows depending on user
-        // settings and/or screen resolution in the start-up phase and users that
-        // try to shoot themselves in the foot by maximizing the window manually.
-        // We add a listener which is triggerred as soon as the window gets
-        // maximized (windowState = 1). We are resizing during start-up but not
-        // later as the user should see only a warning there as a stopgap before
-        // #14229 lands.
-        // Alas, the Firefox window code is handling the event not itself:
-        // "// Note the current implementation of SetSizeMode just stores
-        //  // the new state; it doesn't actually resize. So here we store
-        //  // the state and pass the event on to the OS."
-        // (See: https://mxr.mozilla.org/mozilla-esr31/source/xpfe/appshell/src/
-        // nsWebShellWindow.cpp#348)
-        // This means we have to cope with race conditions and resizing in the
-        // sizemodechange listener is likely to fail. Thus, we add a specific
-        // resize listener that is doing the work for us. It seems (at least on
-        // Ubuntu) to be the case that maximizing (and then again normalizing) of
-        // the window triggers more than one resize event the first being not the
-        // one we need. Thus we can't remove the listener after the first resize
-        // event got fired. Thus, we have the rather klunky setTimeout() call.
-        window.addEventListener("sizemodechange", m_tb_resize_handler);
-
-        let progress = Cc["@mozilla.org/docloaderservice;1"].getService(
-          Ci.nsIWebProgress
-        );
-        progress.removeProgressListener(this);
-      }
-    }, // onStateChange
-
-    onProgressChange(
-      aProgress,
-      aRequest,
-      curSelfProgress,
-      maxSelfProgress,
-      curTotalProgress,
-      maxTotalProgress
-    ) {},
-    onStatusChange(aProgress, aRequest, stat, message) {},
-    onSecurityChange() {},
-  };
 })();
-//vim:set ts=4


=====================================
toolkit/torbutton/chrome/locale/en-US/torbutton.properties
=====================================
@@ -115,6 +115,3 @@ torbutton.popup.no_newnym = Torbutton cannot safely give you a new identity. It
 # Preferences for mobile: these strings are still referenced, but we should
 # check whether this feature is still used
 torbutton.security_settings.menu.title = Security Settings
-
-# Other legacy stuff we might just remove since we are using letterbox
-torbutton.maximize_warning = Maximizing Tor Browser can allow websites to determine your monitor size, which can be used to track you. We recommend that you leave Tor Browser windows in their original default size.



View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c1b352e3a33dfbb16de07370408724837b3550aa...be88af1c72d3b9ab8352a0de77bfed763be51734

-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c1b352e3a33dfbb16de07370408724837b3550aa...be88af1c72d3b9ab8352a0de77bfed763be51734
You're receiving this email because of your account on gitlab.torproject.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tor-commits/attachments/20230329/34b1afe4/attachment-0001.htm>


More information about the tor-commits mailing list