ZoomUI.sys.mjs (7033B)
1 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*- 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 const gLoadContext = Cu.createLoadContext(); 7 const gContentPrefs = Cc["@mozilla.org/content-pref/service;1"].getService( 8 Ci.nsIContentPrefService2 9 ); 10 const gZoomPropertyName = "browser.content.full-zoom"; 11 12 export var ZoomUI = { 13 init(aWindow) { 14 aWindow.addEventListener("EndSwapDocShells", onEndSwapDocShells, true); 15 aWindow.addEventListener("FullZoomChange", onZoomChange); 16 aWindow.addEventListener("TextZoomChange", onZoomChange); 17 aWindow.addEventListener( 18 "unload", 19 () => { 20 aWindow.removeEventListener( 21 "EndSwapDocShells", 22 onEndSwapDocShells, 23 true 24 ); 25 aWindow.removeEventListener("FullZoomChange", onZoomChange); 26 aWindow.removeEventListener("TextZoomChange", onZoomChange); 27 }, 28 { once: true } 29 ); 30 }, 31 32 /** 33 * Gets the global browser.content.full-zoom content preference. 34 * 35 * @returns Promise<prefValue> 36 * Resolves to the preference value (float) when done. 37 */ 38 getGlobalValue() { 39 return new Promise(resolve => { 40 let cachedVal = gContentPrefs.getCachedGlobal( 41 gZoomPropertyName, 42 gLoadContext 43 ); 44 if (cachedVal) { 45 // We've got cached information, though it may be we've cached 46 // an undefined value, or the cached info is invalid. To ensure 47 // a valid return, we opt to return the default 1.0 in the 48 // undefined and invalid cases. 49 resolve(parseFloat(cachedVal.value) || 1.0); 50 return; 51 } 52 // Otherwise, nothing is cached, so we must do a full lookup 53 // with `gContentPrefs.getGlobal()`. 54 let value = 1.0; 55 gContentPrefs.getGlobal(gZoomPropertyName, gLoadContext, { 56 handleResult(pref) { 57 if (pref.value) { 58 value = parseFloat(pref.value); 59 } 60 }, 61 handleCompletion() { 62 resolve(value); 63 }, 64 handleError(error) { 65 console.error(error); 66 }, 67 }); 68 }); 69 }, 70 }; 71 72 function fullZoomLocationChangeObserver(aSubject) { 73 // If the tab was the last one in its window and has been dragged to another 74 // window, the original browser's window will be unavailable here. Since that 75 // window is closing, we can just ignore this notification. 76 if (!aSubject.ownerGlobal) { 77 return; 78 } 79 updateZoomUI(aSubject, false); 80 } 81 Services.obs.addObserver( 82 fullZoomLocationChangeObserver, 83 "browser-fullZoom:location-change" 84 ); 85 86 function onEndSwapDocShells(event) { 87 updateZoomUI(event.originalTarget); 88 } 89 90 function onZoomChange(event) { 91 let browser; 92 if (event.target.nodeType == event.target.DOCUMENT_NODE) { 93 // In non-e10s, the event is dispatched on the contentDocument 94 // so we need to jump through some hoops to get to the <xul:browser>. 95 let topDoc = event.target.defaultView.top.document; 96 if (!topDoc.documentElement) { 97 // In some events, such as loading synthetic documents, the 98 // documentElement will be null and we won't be able to find 99 // an associated browser. 100 return; 101 } 102 browser = topDoc.ownerGlobal.docShell.chromeEventHandler; 103 } else { 104 browser = event.originalTarget; 105 } 106 updateZoomUI(browser, true); 107 } 108 109 /** 110 * Updates zoom controls. 111 * 112 * @param {object} aBrowser The browser that the zoomed content resides in. 113 * @param {boolean} aAnimate Should be True for all cases unless the zoom 114 * change is related to tab switching. Optional 115 */ 116 export async function updateZoomUI(aBrowser, aAnimate = false) { 117 let win = aBrowser.ownerGlobal; 118 if ( 119 !win.gBrowser || 120 win.gBrowser.selectedBrowser != aBrowser || 121 aBrowser.browsingContext.topChromeWindow != win 122 ) { 123 return; 124 } 125 126 let appMenuZoomReset = win.document.getElementById( 127 "appMenu-zoomReset-button2" 128 ); 129 let customizableZoomControls = win.document.getElementById("zoom-controls"); 130 let customizableZoomReset = win.document.getElementById("zoom-reset-button"); 131 let urlbarZoomButton = win.document.getElementById("urlbar-zoom-button"); 132 let zoomFactor = Math.round(win.ZoomManager.zoom * 100); 133 134 let defaultZoom = Math.round((await ZoomUI.getGlobalValue()) * 100); 135 136 if (!win.gBrowser || win.gBrowser.selectedBrowser != aBrowser) { 137 // Because the CPS call is async, at this point the selected browser 138 // may have changed. We should re-check whether the browser for which we've 139 // been notified is still the selected browser and bail out if not. 140 // If the selected browser changed (again), we will have been called again 141 // with the "right" browser, and that'll update the zoom level. 142 return; 143 } 144 145 // Hide urlbar zoom button if zoom is at the default zoom level, 146 // if we're viewing an about:blank page with an empty/null 147 // principal, if the PDF viewer is currently open, 148 // or if the customizable control is in the toolbar. 149 150 urlbarZoomButton.hidden = 151 defaultZoom == zoomFactor || 152 (aBrowser.currentURI.spec == "about:blank" && 153 (!aBrowser.contentPrincipal || 154 aBrowser.contentPrincipal.isNullPrincipal)) || 155 (aBrowser.contentPrincipal && 156 aBrowser.contentPrincipal.spec == "resource://pdf.js/web/viewer.html") || 157 (customizableZoomControls && 158 customizableZoomControls.getAttribute("cui-areatype") == "toolbar"); 159 160 let label = win.gNavigatorBundle.getFormattedString("zoom-button.label", [ 161 zoomFactor, 162 ]); 163 let accessibilityLabel = win.gNavigatorBundle.getFormattedString( 164 "zoom-button.aria-label", 165 [zoomFactor] 166 ); 167 168 if (appMenuZoomReset) { 169 appMenuZoomReset.setAttribute("label", label); 170 } 171 if (customizableZoomReset) { 172 customizableZoomReset.setAttribute("label", label); 173 } 174 if (!urlbarZoomButton.hidden) { 175 if (aAnimate && !win.gReduceMotion) { 176 urlbarZoomButton.setAttribute("animate", "true"); 177 } else { 178 urlbarZoomButton.removeAttribute("animate"); 179 } 180 urlbarZoomButton.setAttribute("label", label); 181 urlbarZoomButton.setAttribute("aria-label", accessibilityLabel); 182 } 183 184 win.FullZoom.updateCommands(); 185 } 186 187 import { CustomizableUI } from "moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs"; 188 189 let customizationListener = {}; 190 customizationListener.onWidgetAdded = 191 customizationListener.onWidgetRemoved = 192 customizationListener.onWidgetMoved = 193 function (aWidgetId) { 194 if (aWidgetId == "zoom-controls") { 195 for (let window of CustomizableUI.windows) { 196 updateZoomUI(window.gBrowser.selectedBrowser); 197 } 198 } 199 }; 200 customizationListener.onWidgetReset = customizationListener.onWidgetUndoMove = 201 function (aWidgetNode) { 202 if (aWidgetNode.id == "zoom-controls") { 203 updateZoomUI(aWidgetNode.ownerGlobal.gBrowser.selectedBrowser); 204 } 205 }; 206 CustomizableUI.addListener(customizationListener);