commit 89a15ab99da7cc73074f4e5c53260a688f15ffe9 Author: Arthur Edelstein arthuredelstein@gmail.com Date: Wed Apr 1 17:57:29 2015 +0100
Bug #14429, Part 3. Improve behavior of content window resizer --- src/chrome/content/content-sizer.js | 503 +++++++++++++++++++++-------- src/chrome/content/torbutton.js | 35 +- src/chrome/locale/en/torbutton.properties | 1 + src/modules/utils.js | 15 +- 4 files changed, 395 insertions(+), 159 deletions(-)
diff --git a/src/chrome/content/content-sizer.js b/src/chrome/content/content-sizer.js index f3710f7..e627c3c 100644 --- a/src/chrome/content/content-sizer.js +++ b/src/chrome/content/content-sizer.js @@ -3,28 +3,105 @@
// This file is formatted for docco.js. Later functions call earlier ones.
+/* +TODO: +* Decide on quantization amount. 100x100? 200x100? Maybe gradually increase, like 50, 100, 150, 200, 300, 500, 600, 800, etc.? +* Understand gBrowser.contentWindow.document.body.getBoundingClientRect(). Does this leak some fingerprintable information? +* Modify Tor Browser C++ code to allow precise setting of zoom? (Would allow more precise fit of content height in window.) +*/ + /* jshint esnext: true */
// __quantizeBrowserSize(window, xStep, yStep)__. // Ensures that gBrowser width and height are multiples of // xStep and yStep. let quantizeBrowserSize = function (window, xStep, yStep) { +"use strict"; + +// __currentDefaultZoom__. +// The settings of gBrowser.fullZoom used to quantize the content window dimensions, +// except if the user has pressed zoom+ or zoom-. Stateful. +let currentDefaultZoom = 1; + +// ## Utilities + +// Mozilla abbreviations. +let {classes: Cc, interfaces: Ci, results: Cr, Constructor: CC, utils: Cu } = Components;
// Use Task.jsm to avoid callback hell. Cu.import("resource://gre/modules/Task.jsm");
// Make the TorButton logger available. let logger = Cc["@torproject.org/torbutton-logger;1"] - .getService(Components.interfaces.nsISupports).wrappedJSObject; + .getService(Ci.nsISupports).wrappedJSObject; + +// __torbuttonBundle__. +// Bundle of localized strings for torbutton UI. +let torbuttonBundle = Services.strings.createBundle( + "chrome://torbutton/locale/torbutton.properties"); + +// Import utility functions +let { bindPrefAndInit, getEnv } = Cu.import("resource://torbutton/modules/utils.js"); + +// __windowUtils(window)__. +// See nsIDOMWindowUtils on MDN. +let windowUtils = window => window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + +// __isNumber(value)__. +// Returns true iff the value is a number. +let isNumber = x => typeof x === "number"; + +// __sortBy(array, scoreFn)__. +// Returns a copy of the array, sorted from least to best +// according to scoreFn. +let sortBy = function (array, scoreFn) { + let compareFn = (a, b) => scoreFn(a) - scoreFn(b); + return array.slice().sort(compareFn); +};
-// Utility function -let { bindPrefAndInit } = Cu.import("resource://torbutton/modules/utils.js"); +// __isMac__. +// Constant, set to true if we are using a Mac (Darwin). +let isMac = Services.appinfo.OS === "Darwin"; + +// __isWindows__. +// Constant, set to true if we are using Windows. +let isWindows = Services.appinfo.OS === "WINNT"; + +// __isTilingWindowManager__. +// Constant, set to true if we are using a (known) tiling window +// manager in linux. +let isTilingWindowManager = (function () { + if (isMac || isWindows) return false; + let gdmSession = getEnv("GDMSESSION"); + if (!gdmSession) return false; + let gdmSessionLower = gdmSession.toLowerCase(); + return ["9wm", "alopex", "awesome", "bspwm", "catwm", "dswm", "dwm", + "echinus", "euclid-wm", "frankenwm", "herbstluftwm", "i3", + "i3wm", "ion", "larswm", "monsterwm", "musca", "notion", + "qtile", "ratpoison", "snapwm", "spectrwm", "stumpwm", + "subtle", "tinywm", "ttwm", "wingo", "wmfs", "wmii", "xmonad"] + .filter(x => x.startsWith(gdmSessionLower)).length > 0; +})();
// __largestMultipleLessThan(factor, max)__. // Returns the largest number that is a multiple of factor // and is less or equal to max. let largestMultipleLessThan = function (factor, max) { - return Math.max(1, Math.floor((1 + max) / factor, 1)) * factor; + return Math.max(1, Math.floor(max / factor, 1)) * factor; +}; + +// ## Task.jsm helper functions + +// __sleep(timeMs)__. +// Returns a Promise that sleeps for the specified time interval, +// and returns an Event object of type "wake". +let sleep = function (timeMs) { + return new Promise(function (resolve, reject) { + window.setTimeout(function () { + resolve(new Event("wake")); + }, timeMs); + }); };
// __listen(target, eventType, useCapture, timeoutMs)__. @@ -47,20 +124,201 @@ let listen = function (target, eventType, useCapture, timeoutMs) { }); };
-// __sleep(time_ms)__. -// Returns a Promise that sleeps for the specified time interval, -// and returns an Event object of type "wake". -let sleep = function (timeoutMs) { - return new Promise(function (resolve, reject) { - window.setTimeout(function () { - resolve(new Event("wake")); - }, timeoutMs); - }); +// __listenForTrueResize(window, timeoutMs)__. +// Task.jsm function. Call `yield listenForTrueResize(window)` to +// wait until the window changes its outer dimensions. Ignores +// resize events where window dimensions are unchanged. Returns +// the resize event object. +let listenForTrueResize = function* (window, timeoutMs) { + let [originalWidth, originalHeight] = [window.outerWidth, window.outerHeight], + event, + finishTime = timeoutMs ? Date.now() + timeoutMs : null; + do { + event = yield listen(window, "resize", true, + finishTime ? finishTime - Date.now() : undefined); + } while (event.type === "resize" && + originalWidth === window.outerWidth && + originalHeight === window.outerHeight); + return event; };
-// __isNumber(value)__. -// Returns true iff the value is a number. -let isNumber = x => typeof x === "number"; +// ## Window state queries + +// __trueZoom(window)__. +// Returns the true magnification of the content in the window +// object. (In contrast, the `gBrowser.fullZoom` value is only approximated +// by the display zoom.) +let trueZoom = window => windowUtils(window).screenPixelsPerCSSPixel; + +// __systemZoom__. +// On Windows, if the user sets the DPI to be 125% or 150% (instead of 100%), +// then we get an overall zoom that needs to be accounted for. +let systemZoom = trueZoom(window); + +// __canBeResized(window)__. +// Returns true iff the window is in a state that can +// be resized. Namely, not fullscreen, not maximized, +// and not running in a tiling window manager. +let canBeResized = function (window) { + // Note that window.fullScreen and (window.windowState === window.STATE_FULLSCREEN) + // sometimes disagree, so we only allow resizing when both are false. + return !isTilingWindowManager && + !window.fullScreen && + window.windowState === window.STATE_NORMAL; +}; + +// __isDocked(window)__. +// On Windows and some linux desktops, you can "dock" a window +// at the right or left, so that it is maximized only in height. +// Returns true in this case. (Note we use mozInnerScreenY instead +// of screenY to take into account title bar space sometimes left +// out of outerHeight in certain desktop environments.) +let isDocked = window => ((window.mozInnerScreenY + window.outerHeight) >= + (window.screen.availTop + window.screen.availHeight) && + (window.screenY <= window.screen.availTop)); + +// ## Window appearance + +// __marginToolTip__. +// A constant. The tooltip string shown in the margin. +let marginToolTip = torbuttonBundle.GetStringFromName("torbutton.content_sizer.margin_tooltip"); + +// __updateContainerAppearance(container, on)__. +// Get the color and position of margins correct. +let updateContainerAppearance = function (container, on) { + // Align the browser at top left, so any gray margin will be visible + // at right and bottom. Except in fullscreen, where we have black + // margins and gBrowser in top center, and when using a tiling + // window manager, when we have gray margins and gBrowser in top + // center. + container.align = on ? + (canBeResized(window) ? "start" : "center") + : ""; + container.pack = on ? "start" : ""; + container.tooltipText = on ? marginToolTip : ""; +}; + +// __updateBackground(window)__. +// Sets the margin background to black or dim gray, depending on +// whether the window is full screen. +let updateBackground = function (window) { + window.gBrowser.parentElement.style + .backgroundColor = window.fullScreen ? "Black" : "LightGray"; +}; + +// ## Window Zooming + +// __computeTargetZoom(parentWidth, parentHeight, xStep, yStep, fillHeight)__. +// Given a parent width and height for gBrowser's container, returns the +// desired zoom for the content window. +let computeTargetZoom = function (parentWidth, parentHeight, xStep, yStep, fillHeight) { + if (fillHeight) { + // Return the estimated zoom need to fill the height of the browser. + let h = largestMultipleLessThan(yStep, parentHeight); + return parentHeight / h; + } else { + // Here we attempt to find a zoom with the best fit for the window size that will + // provide a content window with appropriately quantized dimensions. + let w = largestMultipleLessThan(xStep, parentWidth), + h = largestMultipleLessThan(yStep, parentHeight), + parentAspectRatio = parentWidth / parentHeight, + possibilities = [[w, h], + [Math.min(w, w - xStep), h], + [w, Math.min(h - yStep)]], + // Find the [w, h] pair with the closest aspect ratio to the parent window. + score = ([w, h]) => Math.abs(Math.log(w / h / parentAspectRatio)), + [W, H] = sortBy(possibilities, score)[0]; + // Return the estimated zoom. + return Math.min(parentHeight / H, parentWidth / W); + } +}; + +// __updateDimensions(window, xStep, yStep)__. +// Changes the width and height of the gBrowser XUL element to be a multiple of x/yStep. +let updateDimensions = function (window, xStep, yStep) { + // Don't run if window is minimized. + if (window.windowState === window.STATE_MINIMIZED) return; + let gBrowser = window.gBrowser, + container = gBrowser.parentElement; + updateContainerAppearance(container, true); + let parentWidth = container.clientWidth, + parentHeight = container.clientHeight, + longPage = !gBrowser.contentWindow.fullScreen, + targetZoom = (canBeResized(window) && !isDocked(window)) ? + 1 : computeTargetZoom(parentWidth, + parentHeight, xStep, yStep, longPage), + zoomOffset = 1; + for (let i = 0; i < 8; ++i) { + // We set `gBrowser.fullZoom` to 99% of the needed zoom, unless + // it's `1`. That's because the "true zoom" is sometimes larger + // than fullZoom, and we need to ensure the gBrowser width and + // height do not exceed the container size. + gBrowser.fullZoom = (targetZoom === 1 ? 1 : 0.99) * targetZoom * zoomOffset; + currentDefaultZoom = gBrowser.fullZoom; + let zoom = trueZoom(gBrowser.contentWindow) / systemZoom, + targetContentWidth = largestMultipleLessThan(xStep, parentWidth / zoom), + targetContentHeight = largestMultipleLessThan(yStep, parentHeight / zoom), + targetBrowserWidth = Math.round(targetContentWidth * zoom), + targetBrowserHeight = Math.round(targetContentHeight * zoom); + // Because gBrowser is inside a vbox, width and height behave differently. It turns + // out we need to set `gBrowser.width` and `gBrowser.maxHeight`. + gBrowser.width = targetBrowserWidth; + gBrowser.maxHeight = targetBrowserHeight; + // When using Windows DPI != 100%, we can get rounding errors. We'll need + // to try again if we failed to get rounded content width x height. + // Unfortunately, this is not detectable if search bar or dev console is open. + if ((// Some weird sidebar is open, or + gBrowser.clientWidth !== gBrowser.selectedBrowser.clientWidth || + // content width is correct. + gBrowser.contentWindow.innerWidth === targetContentWidth) && + (// Search bar or dev console is open, or + gBrowser.clientHeight !== gBrowser.selectedBrowser.clientHeight || + // content height is correct. + gBrowser.contentWindow.innerHeight === targetContentHeight)) { + logger.eclog(3, + " chromeWin " + window.outerWidth + "x" + window.outerHeight + + " container " + parentWidth + "x" + parentHeight + + " gBrowser.fullZoom " + gBrowser.fullZoom + "X" + + " targetContent " + targetContentWidth + "x" + targetContentHeight + + " zoom " + zoom + "X" + + " targetBrowser " + targetBrowserWidth + "x" + targetBrowserHeight + + " gBrowser " + gBrowser.clientWidth + "x" + gBrowser.clientHeight + + " content " + gBrowser.contentWindow.innerWidth + "x" + gBrowser.contentWindow.innerHeight); + break; + } + zoomOffset *= 1.02; + } +}; + +// __resetZoomOnDomainChanges(gBrowser, on)__. +// If `on` is true, then every time a tab location changes +// to a new domain, the tab's zoom level is set back to the +// "default zoom" level. +let resetZoomOnDomainChanges = (function () { + let tabToDomainMap = new Map(), + onLocationChange = function (browser) { + let lastHost = tabToDomainMap.get(browser), + currentHost = browser && + browser.currentURI && + browser.currentURI.asciiHost; + if (lastHost !== currentHost) { + browser.fullZoom = currentDefaultZoom; + // Record the tab's current domain, so that we + // can see when it changes. + tabToDomainMap.set(browser, currentHost); + } + }, + listener = { onLocationChange : onLocationChange }; + return function (gBrowser, on) { + if (on) { + gBrowser.addTabsProgressListener(listener); + } else { + gBrowser.removeTabsProgressListener(listener); + } + }; +})(); + +// ## Window Resizing
// __reshape(window, {left, top, width, height}, timeoutMs)__. // Reshapes the window to rectangle {left, top, width, height} and yields @@ -73,7 +331,10 @@ let reshape = function* (window, {left, top, width, height}, timeoutMs) { h = isNumber(height) ? height : window.outerHeight; // Make sure we are in a new event. yield sleep(0); - if (w !== window.outerWidth || h !== window.outerWidth) { + // Sometimes we get a race condition in linux when maximizing, + // so check again at the last minute that resizing is allowed. + if (!canBeResized(window)) return; + if (w !== window.outerWidth || h !== window.outerHeight) { window.resizeTo(w, h); } if (x !== window.screenX || y !== window.screenY) { @@ -87,27 +348,17 @@ let reshape = function* (window, {left, top, width, height}, timeoutMs) { h !== window.outerHeight) { let timeLeft = finishTime - Date.now(); if (timeLeft <= 0) break; - yield listen(window, "resize", true, timeLeft); + yield listenForTrueResize(window, timeLeft); } };
-// __rebuild(window)__. -// Jog the size of the window slightly, to remind the window manager -// to redraw the window. -let rebuild = function* (window) { - let h = window.outerHeight; - yield reshape(window, {height : (h + 1)}, 300); - yield reshape(window, {height : h}, 300); -}; - // __gaps(window)__. // Deltas between gBrowser and its container. Returns null if there is no gap. let gaps = function (window) { let gBrowser = window.gBrowser, container = gBrowser.parentElement, - deltaWidth = Math.max(0, container.clientWidth - gBrowser.clientWidth - 1), - deltaHeight = Math.max(0, container.clientHeight - gBrowser.clientHeight - 1); - //logger.eclog(3, "gaps " + deltaWidth + "," + deltaHeight); + deltaWidth = Math.max(0, container.clientWidth - gBrowser.clientWidth), + deltaHeight = Math.max(0, container.clientHeight - gBrowser.clientHeight); return (deltaWidth === 0 && deltaHeight === 0) ? null : { deltaWidth : deltaWidth, deltaHeight : deltaHeight }; }; @@ -115,170 +366,142 @@ let gaps = function (window) { // __shrinkwrap(window)__. // Shrinks the window so that it encloses the gBrowser with no gaps. let shrinkwrap = function* (window) { - // Maximized windows in Linux and Windows need to be demaximized first. - if (gaps(window) && - window.windowState === 1 && /* maximized */ - Services.appinfo.OS !== "Darwin") { - if (Services.appinfo.OS !== "WINNT") { - // Linux windows need an extra jolt out of maximized mode. - window.moveBy(1,1); - } - // If window has been maximized, demaximize by shrinking it to - // fit within the available screen area. - yield reshape(window, - {left : window.screen.availLeft + 1, - top : window.screen.availTop + 1, - width : window.screen.availWidth - 2, - height : window.screen.availHeight - 2}, - 500); - } // Figure out what size change we need. - let currentGaps = gaps(window); + let currentGaps = gaps(window), + screenRightEdge = window.screen.availWidth + window.screen.availLeft, + windowRightEdge = window.screenX + window.outerWidth; if (currentGaps) { // Now resize to close the gaps. yield reshape(window, {width : (window.outerWidth - currentGaps.deltaWidth), - height : (window.outerHeight - currentGaps.deltaHeight)}, + // Shrink in height only if we are not docked. + height : !isDocked(window) ? + (window.outerHeight - + currentGaps.deltaHeight) : null, + left : (isDocked(window) && + (windowRightEdge >= screenRightEdge)) ? + (window.screenX + currentGaps.deltaWidth) + : null }, 500); } };
-// __updateContainerAppearance(container, on)__. -// Get the color and position of margins right. -let updateContainerAppearance = function (container, on) { - // Align the browser at top left, so any gray margin will be visible - // at right and bottom. Except in fullscreen, where we have black - // margins and gBrowser in top center. - container.align = on ? (window.fullScreen ? "center" : "start") - : ""; - container.pack = on ? "start" : ""; - container.style.backgroundColor = on ? (window.fullScreen ? "Black" - : "DimGray") - : ""; +// __rebuild(window)__. +// Jog the size of the window slightly, to remind the window manager +// to redraw the window. +let rebuild = function* (window) { + let h = window.outerHeight; + yield reshape(window, {height : (h + 1)}, 300); + yield reshape(window, {height : h}, 300); };
// __fixWindow(window)__. // An async function for Task.jsm. Makes sure the window looks okay // given the quantized browser element. let fixWindow = function* (window) { - updateContainerAppearance(window.gBrowser.parentElement, true); - if (!window.fullScreen) { + if (canBeResized(window)) { yield shrinkwrap(window); - if (Services.appinfo.OS !== "Darwin" && Services.appinfo.OS !== "WINNT") { - // Linux tends to require us to rebuild the window, or we might be - // left with a large useless white area on the screen. - yield rebuild(window); + if (!isMac && !isWindows) { + // Unfortunately, on some linux desktops, + // the window resize fails if the user is still holding on + // to the drag-resize handle. Even more unfortunately, the + // only way to know that the user if finished dragging + // if we detect the mouse cursor inside the window or the + // user presses a key. + // So, after the first mousemove, or keydown event occurs, we + // rebuild the window. + let event = yield Promise.race( + [listen(window, "mousemove", true), + listen(window, "keydown", true), + listen(window, "resize", true)]); + if (event !== "resize") { + yield rebuild(window); + } + return event; } } };
// __autoresize(window, stepMs)__. -// Do what it takes to eliminate the gray margin around the gBrowser inside -// window. Periodically (stepMs) attempt to shrink the window. Runs -// as a Task.jsm coroutine. -let autoresize = function (window, stepMs) { +// Automatically resize the gBrowser, and then shrink the window +// if the user has attempted to resize it. +let autoresize = function (window, stepMs, xStep, yStep) { let stop = false; Task.spawn(function* () { + // Fix the content dimensions once at startup, and + // keep updating the dimensions whenever the user resizes + // the window. while (!stop) { + updateDimensions(window, xStep, yStep); + let event = yield fixWindow(window); // Do nothing until the user starts to resize window. - let event = yield listen(window, "resize", true); - // Here we wrestle with the window size. If the user has released the - // mouse cursor on the window's drag/resize handle, then fixWindow - // will resize the window on its first call. Unfortunately, on some - // OSs, the window resize fails if the user is still holding on - // to the drag-resize handle. Even more unfortunately, the - // only way to know that the user no longer has the mouse down - // on the window's drag/resize handle is if we detect the mouse - // cursor inside the window. So until the window fires a mousemove - // event, we repeatedly call fixWindow every stepMs. - while (event.type !== "mousemove") { - event = yield Promise.race( - [listen(window, "resize", true, stepMs), - listen(window, "mousemove", true, stepMs)]); - // If the user has stopped resizing the window after `stepMs`, then we can resize - // the window so no gray margin is visible. - if (event.type === "timeout" || event.type === "mousemove") { - yield fixWindow(window); + if ((!event || event.type !== "resize") && !stop) { + event = yield listenForTrueResize(window); + } + if (!isTilingWindowManager) { + while (event.type !== "timeout" && !stop) { + if (!stop) { + updateDimensions(window, xStep, yStep); + event = yield listenForTrueResize(window, stepMs); + } } } + // The user has likely released the mouse cursor on the window's + // drag/resize handle, so loop and call fixWindow. } }); return () => { stop = true; }; };
-// __updateDimensions(gBrowser, xStep, yStep)__. -// Changes the width and height of the gBrowser XUL element to be a multiple of x/yStep. -let updateDimensions = function (gBrowser, xStep, yStep) { - // TODO: Get zooming to work such that it doesn't cause the window - // to continuously shrink. - // We'll use something like: - // let winUtils = gBrowser.contentWindow - // .QueryInterface(Components.interfaces.nsIInterfaceRequestor) - // .getInterface(Components.interfaces.nsIDOMWindowUtils), - // zoom = winUtils.screenPixelsPerCSSPixel, - let zoom = 1, - parentWidth = gBrowser.parentElement.clientWidth, - parentHeight = gBrowser.parentElement.clientHeight, - targetContentWidth = largestMultipleLessThan(xStep, parentWidth / zoom), - targetContentHeight = largestMultipleLessThan(yStep, parentHeight / zoom), - targetBrowserWidth = targetContentWidth * zoom, - targetBrowserHeight = targetContentHeight * zoom; - // Because gBrowser is inside a vbox, width and height behave differently. It turns - // out we need to set `gBrowser.width` and `gBrowser.maxHeight`. - gBrowser.width = targetBrowserWidth; - gBrowser.maxHeight = targetBrowserHeight; - // If the content window's innerWidth/innerHeight failed to updated correctly, - // then jog the gBrowser width/height. (With zoom there may also be a rounding - // error, but we can't do much about that.) - if (gBrowser.contentWindow.innerWidth !== targetContentWidth || - gBrowser.contentWindow.innerHeight !== targetContentHeight) { - gBrowser.width = targetBrowserWidth + 1; - gBrowser.maxHeight = gBrowser.targetBrowserHeight + 1; - gBrowser.width = targetBrowserWidth; - gBrowser.maxHeight = targetBrowserHeight; - } - logger.eclog(3, "zoom " + zoom + "X" + - " chromeWin " + window.outerWidth + "x" + window.outerHeight + - " container " + parentWidth + "x" + parentHeight + - " gBrowser " + gBrowser.clientWidth + "x" + gBrowser.clientHeight + - " content " + gBrowser.contentWindow.innerWidth + "x" + gBrowser.contentWindow.innerHeight); -}; +// ## Main Function
-// __quantizeBrowserSizeNow(window, xStep, yStep)__. +// __quantizeBrowserSizeMain(window, xStep, yStep)__. // Ensures that gBrowser width and height are multiples of xStep and yStep, and always as // large as possible inside the chrome window. let quantizeBrowserSizeMain = function (window, xStep, yStep) { let gBrowser = window.gBrowser, container = window.gBrowser.parentElement, - updater = event => updateDimensions(gBrowser, xStep, yStep), - originalMinWidth = gBrowser.minWidth, - originalMinHeight = gBrowser.minHeight, + fullscreenHandler = function () { + // Use setTimeout to make sure we only update dimensions after + // full screen mode is fully established. + window.setTimeout(function () { + updateDimensions(window, xStep, yStep); + updateBackground(window); + }, 0); + }, + originalMinWidth = container.minWidth, + originalMinHeight = container.minHeight, stopAutoresizing, activate = function (on) { + console.log("activate:", on); // Don't let the browser shrink below a single xStep x yStep size. - gBrowser.minWidth = on ? xStep : originalMinWidth; - gBrowser.minHeight = on ? yStep : originalMinHeight; + container.minWidth = on ? xStep : originalMinWidth; + container.minHeight = on ? yStep : originalMinHeight; updateContainerAppearance(container, on); + updateBackground(window); + resetZoomOnDomainChanges(gBrowser, on); if (on) { - // Quantize browser size on activation. - updateDimensions(gBrowser, xStep, yStep); shrinkwrap(window); - // Quantize browser size at subsequent resize events. - window.addEventListener("resize", updater, false); - stopAutoresizing = autoresize(window, 250); + window.addEventListener("sizemodechange", fullscreenHandler, false); + stopAutoresizing = autoresize(window, + (isMac || isWindows) ? 250 : 500, + xStep, yStep); + console.log("activated"); } else { if (stopAutoresizing) stopAutoresizing(); // Ignore future resize events. - window.removeEventListener("resize", updater, false); + window.removeEventListener("sizemodechange", fullscreenHandler, false); // Let gBrowser expand with its parent vbox. gBrowser.width = ""; gBrowser.maxHeight = ""; + console.log("deactivated"); } }; - bindPrefAndInit("extensions.torbutton.resize_windows", activate); + let unbind = bindPrefAndInit("extensions.torbutton.resize_windows", activate); + window.addEventListener("unload", unbind, true); };
quantizeBrowserSizeMain(window, xStep, yStep);
-// quantizeBrowserSize +// end of quantizeBrowserSize definition }; diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js index c580c59..3454613 100644 --- a/src/chrome/content/torbutton.js +++ b/src/chrome/content/torbutton.js @@ -644,8 +644,6 @@ function torbutton_init() { createTorCircuitDisplay(m_tb_control_host, m_tb_control_port, m_tb_control_pass, "extensions.torbutton.display_circuit");
- quantizeBrowserSize(window, 200, 100); - torbutton_log(3, 'init completed'); }
@@ -3000,8 +2998,8 @@ function torbutton_new_tab(event) // 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.outerHeight+","+wind.outerWidth+") ?= (" - +wind.screen.availHeight+","+wind.screen.availWidth+")"); + torbutton_log(3, "Window: (" + wind.outerWidth + "," + wind.outerHeight + ") ?= (" + + wind.screen.availWidth + "," + wind.screen.availHeight + ")"); if(wind.windowState == Components.interfaces.nsIDOMChromeWindow.STATE_MINIMIZED || wind.windowState == Components.interfaces.nsIDOMChromeWindow.STATE_MAXIMIZED) { torbutton_log(2, "Window is minimized/maximized"); @@ -3391,7 +3389,8 @@ var torbutton_resizelistener = var progress = Components.classes["@mozilla.org/docloaderservice;1"]. getService(Components.interfaces.nsIWebProgress); - var win = getBrowser().contentWindow; + var win = getBrowser().contentWindow, + container = getBrowser().parentElement; if (!win || typeof(win) == "undefined") { torbutton_log(5, "No initial browser content window?"); progress.removeProgressListener(this); @@ -3416,8 +3415,8 @@ var torbutton_resizelistener = " Available: " + availWidth.value + "x" + availHeight.value);
- var diff_height = window.outerHeight - win.innerHeight; var diff_width = window.outerWidth - win.innerWidth; + var diff_height = window.outerHeight - win.innerHeight; var delta_fix = 0;
// The following block tries to cope with funny corner cases where the @@ -3481,6 +3480,14 @@ var torbutton_resizelistener = height = Math.floor(maxHeight/100.0)*100; }
+ let resizeInnerWindowTo = function (width, height) { + window.resizeBy(width - win.innerWidth, + height - win.innerHeight); + torbutton_log(3, "Resized new window from: " + container.clientWidth + "x" + + container.clientHeight + " to " + width + "x" + height + + " in state " + window.windowState); + } + m_tb_resize_handler = function() { if (window.windowState === 1) { if (m_tb_prefs. @@ -3537,7 +3544,7 @@ var torbutton_resizelistener = getBoolPref(k_tb_tor_resize_warn_pref)) { window.addEventListener("resize", function() { - win.resizeBy(width - win.innerWidth, height - win.innerHeight); + resizeInnerWindowTo(width, height); var calling_function = arguments.callee; setTimeout(function() { torbutton_log(3, "Removing resize listener.."); @@ -3575,10 +3582,7 @@ var torbutton_resizelistener = // This is fun. any attempt to directly set the inner window actually // resizes the outer width to that value instead. Must use resizeBy() // instead of assignment or resizeTo() - win.resizeBy(width - win.innerWidth, height - win.innerHeight); - torbutton_log(3, "Resized new window from: " + win.innerWidth + "x" + - win.innerHeight + " to " + width + "x" + height + - " in state " + window.windowState); + resizeInnerWindowTo(width, height);
// Resizing within this progress listener does not always work as overlays // of other extensions might still influence the height/width of the @@ -3592,14 +3596,9 @@ var torbutton_resizelistener = function(mutations) { mutations.forEach( function(mutation) { - torbutton_log(3, "Mutation observer: Window dimensions are: " + - win.innerWidth + " x " + win.innerHeight); setTimeout(function() { - win.resizeBy(width - win.innerWidth, - height - win.innerHeight); - torbutton_log(3, "Mutation observer: Window " + - "dimensions are (after resizing again): " + win. - innerWidth + " x " + win.innerHeight); + resizeInnerWindowTo(width, height); + quantizeBrowserSize(window, 100, 100); }, 0); mut_observer.disconnect(); } diff --git a/src/chrome/locale/en/torbutton.properties b/src/chrome/locale/en/torbutton.properties index ca048df..76c0c0d 100644 --- a/src/chrome/locale/en/torbutton.properties +++ b/src/chrome/locale/en/torbutton.properties @@ -7,6 +7,7 @@ torbutton.circuit_display.this_browser = This browser torbutton.circuit_display.relay = relay torbutton.circuit_display.tor_bridge = Bridge torbutton.circuit_display.unknown_country = Unknown country +torbutton.content_sizer.margin_tooltip = Tor Browser adds this margin to make the width and height of your window less distinctive, and thus reduces the ability of people to track you online. torbutton.panel.tooltip.disabled = Click to enable Tor torbutton.panel.tooltip.enabled = Click to disable Tor torbutton.panel.plugins.disabled = Click to enable plugins diff --git a/src/modules/utils.js b/src/modules/utils.js index 7a27326..de3cca6 100644 --- a/src/modules/utils.js +++ b/src/modules/utils.js @@ -39,5 +39,18 @@ let bindPrefAndInit = function (prefName, prefHandler) { return () => { prefs.removeObserver(prefName, observer); }; };
+// ## Environment variables + +// __env__. +// Provides access to process environment variables. +let env = Components.classes["@mozilla.org/process/environment;1"] + .getService(Components.interfaces.nsIEnvironment); + +// __getEnv(name)__. +// Reads the environment variable of the given name. +let getEnv = function (name) { + return env.exists(name) ? env.get(name) : undefined; +}; + // Export utility functions for external use. -let EXPORTED_SYMBOLS = ["bindPrefAndInit", "getPrefValue"]; +let EXPORTED_SYMBOLS = ["bindPrefAndInit", "getPrefValue", "getEnv"];