tor-browser

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

Screenshots.sys.mjs (5118B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // We use importESModule here instead of static import so that
      6 // the Karma test environment won't choke on this module. This
      7 // is because the Karma test environment already stubs out
      8 // XPCOMUtils, and overrides importESModule to be a no-op (which
      9 // can't be done for a static import statement).
     10 
     11 // eslint-disable-next-line mozilla/use-static-import
     12 const { XPCOMUtils } = ChromeUtils.importESModule(
     13  "resource://gre/modules/XPCOMUtils.sys.mjs"
     14 );
     15 
     16 const lazy = {};
     17 
     18 ChromeUtils.defineESModuleGetters(lazy, {
     19  BackgroundPageThumbs: "resource://gre/modules/BackgroundPageThumbs.sys.mjs",
     20  PageThumbs: "resource://gre/modules/PageThumbs.sys.mjs",
     21  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
     22 });
     23 
     24 const GREY_10 = "#F9F9FA";
     25 
     26 XPCOMUtils.defineLazyPreferenceGetter(
     27  lazy,
     28  "gPrivilegedAboutProcessEnabled",
     29  "browser.tabs.remote.separatePrivilegedContentProcess",
     30  false
     31 );
     32 
     33 export const Screenshots = {
     34  /**
     35   * Get a screenshot / thumbnail for a url. Either returns the disk cached
     36   * image or initiates a background request for the url.
     37   *
     38   * @param url {string} The url to get a thumbnail
     39   * @return {Promise} Resolves a custom object or null if failed
     40   */
     41  async getScreenshotForURL(url) {
     42    try {
     43      await lazy.BackgroundPageThumbs.captureIfMissing(url, {
     44        backgroundColor: GREY_10,
     45      });
     46 
     47      // The privileged about content process is able to use the moz-page-thumb
     48      // protocol, so if it's enabled, send that down.
     49      if (lazy.gPrivilegedAboutProcessEnabled) {
     50        return lazy.PageThumbs.getThumbnailURL(url);
     51      }
     52 
     53      // Otherwise, for normal content processes, we fallback to using
     54      // Blob URIs for the screenshots.
     55      const imgPath = lazy.PageThumbs.getThumbnailPath(url);
     56 
     57      const filePathResponse = await fetch(`file://${imgPath}`);
     58      const fileContents = await filePathResponse.blob();
     59 
     60      // Check if the file is empty, which indicates there isn't actually a
     61      // thumbnail, so callers can show a failure state.
     62      if (fileContents.size === 0) {
     63        return null;
     64      }
     65 
     66      return { path: imgPath, data: fileContents };
     67    } catch (err) {
     68      console.error(`getScreenshot(${url}) failed:`, err);
     69    }
     70 
     71    // We must have failed to get the screenshot, so persist the failure by
     72    // storing an empty file. Future calls will then skip requesting and return
     73    // failure, so do the same thing here. The empty file should not expire with
     74    // the usual filtering process to avoid repeated background requests, which
     75    // can cause unwanted high CPU, network and memory usage - Bug 1384094
     76    try {
     77      await lazy.PageThumbs._store(url, url, null, true);
     78    } catch (err) {
     79      // Probably failed to create the empty file, but not much more we can do.
     80    }
     81    return null;
     82  },
     83 
     84  /**
     85   * Checks if all the open windows are private browsing windows. If so, we do not
     86   * want to collect screenshots. If there exists at least 1 non-private window,
     87   * we are ok to collect screenshots.
     88   */
     89  _shouldGetScreenshots() {
     90    for (let win of Services.wm.getEnumerator("navigator:browser")) {
     91      if (!lazy.PrivateBrowsingUtils.isWindowPrivate(win)) {
     92        // As soon as we encounter 1 non-private window, screenshots are fair game.
     93        return true;
     94      }
     95    }
     96    return false;
     97  },
     98 
     99  /**
    100   * Conditionally get a screenshot for a link if there's no existing pending
    101   * screenshot. Updates the cached link's desired property with the result.
    102   *
    103   * @param link {object} Link object to update
    104   * @param url {string} Url to get a screenshot of
    105   * @param property {string} Name of property on object to set
    106   @ @param onScreenshot {function} Callback for when the screenshot loads
    107   */
    108  async maybeCacheScreenshot(link, url, property, onScreenshot) {
    109    // If there are only private windows open, do not collect screenshots
    110    if (!this._shouldGetScreenshots()) {
    111      return;
    112    }
    113    // __sharedCache may not exist yet for links from default top sites that
    114    // don't have a default tippy top icon.
    115    if (!link.__sharedCache) {
    116      link.__sharedCache = {
    117        updateLink(prop, val) {
    118          link[prop] = val;
    119        },
    120      };
    121    }
    122    const cache = link.__sharedCache;
    123    // Nothing to do if we already have a pending screenshot or
    124    // if a previous request failed and returned null.
    125    if (cache.fetchingScreenshot || link[property] !== undefined) {
    126      return;
    127    }
    128 
    129    // Save the promise to the cache so other links get it immediately
    130    cache.fetchingScreenshot = this.getScreenshotForURL(url);
    131 
    132    // Clean up now that we got the screenshot
    133    const screenshot = await cache.fetchingScreenshot;
    134    delete cache.fetchingScreenshot;
    135 
    136    // Update the cache for future links and call back for existing content
    137    cache.updateLink(property, screenshot);
    138    onScreenshot(screenshot);
    139  },
    140 };