@@ -3,28 +3,110 @@
// 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 ;
// __extraWindowWidth, extraWindowHeight__.
// Returns any extra width or height not included in window.outerWidth, window.outerHeight.
// (Sometimes linux ignore the size of its titlebar in window.outerHeight.)
let extraWindowWidth , extraWindowHeight = [ 0 , 0 ] ;
// ## 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 ) ;
} ;
// __isMac__.
// Constant, set to true if we are using a Mac (Darwin).
let isMac = Services . appinfo . OS === "Darwin" ;
// Utility function
let { bindPrefAndInit } = Cu . import ( "resource://torbutton/modules/utils.js" ) ;
// __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 +129,213 @@ 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 ) ;
} ) ;
// __getExtraOuterSize(window)__.
// Figures out any extra parts of a window not included in
// [window.outerWidth, window.outerHeight] by faking a mouse move event.
// This gets around a bug in window.outerHeight in linux which ignores
// the title bar. Use with Task.jsm only.
let getExtraOuterSize = function * ( window ) {
let mouseMovePromise = listen ( window , "mousemove" , true , 500 ) ;
windowUtils ( window ) . sendMouseEventToWindow ( "mousemove" , 0 , 0 , 0 , 0 , 0 , false ) ;
let mouseMoveEvent = yield mouseMovePromise ;
return [ mouseMoveEvent . screenX - window . screenX - mouseMoveEvent . clientX ,
mouseMoveEvent . screenY - window . screenY - mouseMoveEvent . clientY ] ;
} ;
// __isNumber(value)__.
// Returns true iff the value is a number.
let isNumber = x => typeof x === "number" ;
// __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 ) ;
[ extraWindowWidth , extraWindowHeight ] = yield getExtraOuterSize ( window ) ;
return event ;
} ;
// ## 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.
let isDocked = window => ( ( window . screenY + window . outerHeight +
extraWindowHeight ) >= 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,6 +348,9 @@ 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 ) ;
// 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 . outerWidth ) {
window . resizeTo ( w , h ) ;
}
@@ -87,198 +365,156 @@ 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 } ;
} ;
// __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 ( ) {
updateDimensions ( window , xStep , yStep ) ;
updateBackground ( window ) ;
} ,
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
} ;