// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const gLoadContext = Cu.createLoadContext(); const gContentPrefs = Cc["@mozilla.org/content-pref/service;1"].getService( Ci.nsIContentPrefService2 ); const gZoomPropertyName = "browser.content.full-zoom"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs", }); export var ZoomUI = { init(aWindow) { aWindow.addEventListener("EndSwapDocShells", onEndSwapDocShells, true); aWindow.addEventListener("FullZoomChange", onZoomChange); aWindow.addEventListener("TextZoomChange", onZoomChange); aWindow.addEventListener( "unload", () => { aWindow.removeEventListener( "EndSwapDocShells", onEndSwapDocShells, true ); aWindow.removeEventListener("FullZoomChange", onZoomChange); aWindow.removeEventListener("TextZoomChange", onZoomChange); }, { once: true } ); }, /** * Gets the global browser.content.full-zoom content preference. * * @returns Promise * Resolves to the preference value (float) when done. */ getGlobalValue() { return new Promise(resolve => { let cachedVal = gContentPrefs.getCachedGlobal( gZoomPropertyName, gLoadContext ); if (cachedVal) { // We've got cached information, though it may be we've cached // an undefined value, or the cached info is invalid. To ensure // a valid return, we opt to return the default 1.0 in the // undefined and invalid cases. resolve(parseFloat(cachedVal.value) || 1.0); return; } // Otherwise, nothing is cached, so we must do a full lookup // with `gContentPrefs.getGlobal()`. let value = 1.0; gContentPrefs.getGlobal(gZoomPropertyName, gLoadContext, { handleResult(pref) { if (pref.value) { value = parseFloat(pref.value); } }, handleCompletion(reason) { resolve(value); }, handleError(error) { console.error(error); }, }); }); }, }; function fullZoomLocationChangeObserver(aSubject, aTopic) { // If the tab was the last one in its window and has been dragged to another // window, the original browser's window will be unavailable here. Since that // window is closing, we can just ignore this notification. if (!aSubject.ownerGlobal) { return; } updateZoomUI(aSubject, false); } Services.obs.addObserver( fullZoomLocationChangeObserver, "browser-fullZoom:location-change" ); function onEndSwapDocShells(event) { updateZoomUI(event.originalTarget); } function onZoomChange(event) { let browser; if (event.target.nodeType == event.target.DOCUMENT_NODE) { // In non-e10s, the event is dispatched on the contentDocument // so we need to jump through some hoops to get to the . let topDoc = event.target.defaultView.top.document; if (!topDoc.documentElement) { // In some events, such as loading synthetic documents, the // documentElement will be null and we won't be able to find // an associated browser. return; } browser = topDoc.ownerGlobal.docShell.chromeEventHandler; } else { browser = event.originalTarget; } updateZoomUI(browser, true); } /** * Updates zoom controls. * * @param {object} aBrowser The browser that the zoomed content resides in. * @param {boolean} aAnimate Should be True for all cases unless the zoom * change is related to tab switching. Optional */ async function updateZoomUI(aBrowser, aAnimate = false) { let win = aBrowser.ownerGlobal; if (!win.gBrowser || win.gBrowser.selectedBrowser != aBrowser) { return; } let appMenuZoomReset = lazy.PanelMultiView.getViewNode( win.document, "appMenu-zoomReset-button2" ); // Exit early if UI elements aren't present. if (!appMenuZoomReset) { return; } let customizableZoomControls = win.document.getElementById("zoom-controls"); let customizableZoomReset = win.document.getElementById("zoom-reset-button"); let urlbarZoomButton = win.document.getElementById("urlbar-zoom-button"); let zoomFactor = Math.round(win.ZoomManager.zoom * 100); let defaultZoom = Math.round((await ZoomUI.getGlobalValue()) * 100); if (!win.gBrowser || win.gBrowser.selectedBrowser != aBrowser) { // Because the CPS call is async, at this point the selected browser // may have changed. We should re-check whether the browser for which we've // been notified is still the selected browser and bail out if not. // If the selected browser changed (again), we will have been called again // with the "right" browser, and that'll update the zoom level. return; } // Hide urlbar zoom button if zoom is at the default zoom level, // if we're viewing an about:blank page with an empty/null // principal, if the PDF viewer is currently open, // or if the customizable control is in the toolbar. urlbarZoomButton.hidden = defaultZoom == zoomFactor || (aBrowser.currentURI.spec == "about:blank" && (!aBrowser.contentPrincipal || aBrowser.contentPrincipal.isNullPrincipal)) || (aBrowser.contentPrincipal && aBrowser.contentPrincipal.spec == "resource://pdf.js/web/viewer.html") || (customizableZoomControls && customizableZoomControls.getAttribute("cui-areatype") == "toolbar"); let label = win.gNavigatorBundle.getFormattedString("zoom-button.label", [ zoomFactor, ]); if (appMenuZoomReset) { appMenuZoomReset.setAttribute("label", label); } if (customizableZoomReset) { customizableZoomReset.setAttribute("label", label); } if (!urlbarZoomButton.hidden) { if (aAnimate && !win.gReduceMotion) { urlbarZoomButton.setAttribute("animate", "true"); } else { urlbarZoomButton.removeAttribute("animate"); } urlbarZoomButton.setAttribute("label", label); } win.FullZoom.updateCommands(); } import { CustomizableUI } from "resource:///modules/CustomizableUI.sys.mjs"; let customizationListener = {}; customizationListener.onWidgetAdded = customizationListener.onWidgetRemoved = customizationListener.onWidgetMoved = function (aWidgetId) { if (aWidgetId == "zoom-controls") { for (let window of CustomizableUI.windows) { updateZoomUI(window.gBrowser.selectedBrowser); } } }; customizationListener.onWidgetReset = customizationListener.onWidgetUndoMove = function (aWidgetNode) { if (aWidgetNode.id == "zoom-controls") { updateZoomUI(aWidgetNode.ownerGlobal.gBrowser.selectedBrowser); } }; CustomizableUI.addListener(customizationListener);