tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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);