tor-browser

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

browser-thumbnails.js (6483B)


      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 /**
      6 * Keeps thumbnails of open web pages up-to-date.
      7 */
      8 var gBrowserThumbnails = {
      9  /**
     10   * Pref that controls whether we can store SSL content on disk
     11   */
     12  PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",
     13 
     14  _captureDelayMS: 1000,
     15 
     16  /**
     17   * Used to keep track of disk_cache_ssl preference
     18   */
     19  _sslDiskCacheEnabled: null,
     20 
     21  /**
     22   * Map of capture() timeouts assigned to their browsers.
     23   */
     24  _timeouts: null,
     25 
     26  /**
     27   * Top site URLs refresh timer.
     28   */
     29  _topSiteURLsRefreshTimer: null,
     30 
     31  /**
     32   * List of tab events we want to listen for.
     33   */
     34  _tabEvents: ["TabClose", "TabSelect"],
     35 
     36  init: function Thumbnails_init() {
     37    gBrowser.addTabsProgressListener(this);
     38    Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this);
     39 
     40    this._sslDiskCacheEnabled = Services.prefs.getBoolPref(
     41      this.PREF_DISK_CACHE_SSL
     42    );
     43 
     44    this._tabEvents.forEach(function (aEvent) {
     45      gBrowser.tabContainer.addEventListener(aEvent, this);
     46    }, this);
     47 
     48    this._timeouts = new WeakMap();
     49  },
     50 
     51  uninit: function Thumbnails_uninit() {
     52    gBrowser.removeTabsProgressListener(this);
     53    Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
     54 
     55    if (this._topSiteURLsRefreshTimer) {
     56      this._topSiteURLsRefreshTimer.cancel();
     57      this._topSiteURLsRefreshTimer = null;
     58    }
     59 
     60    this._tabEvents.forEach(function (aEvent) {
     61      gBrowser.tabContainer.removeEventListener(aEvent, this);
     62    }, this);
     63  },
     64 
     65  handleEvent: function Thumbnails_handleEvent(aEvent) {
     66    switch (aEvent.type) {
     67      case "scroll": {
     68        let browser = aEvent.currentTarget;
     69        if (this._timeouts.has(browser)) {
     70          this._delayedCapture(browser);
     71        }
     72        break;
     73      }
     74      case "TabSelect":
     75        this._delayedCapture(aEvent.target.linkedBrowser);
     76        break;
     77      case "TabClose": {
     78        this._cancelDelayedCapture(aEvent.target.linkedBrowser);
     79        break;
     80      }
     81    }
     82  },
     83 
     84  observe: function Thumbnails_observe(subject, topic, data) {
     85    switch (data) {
     86      case this.PREF_DISK_CACHE_SSL:
     87        this._sslDiskCacheEnabled = Services.prefs.getBoolPref(
     88          this.PREF_DISK_CACHE_SSL
     89        );
     90        break;
     91    }
     92  },
     93 
     94  clearTopSiteURLCache: function Thumbnails_clearTopSiteURLCache() {
     95    if (this._topSiteURLsRefreshTimer) {
     96      this._topSiteURLsRefreshTimer.cancel();
     97      this._topSiteURLsRefreshTimer = null;
     98    }
     99    // Delete the defined property
    100    delete this._topSiteURLs;
    101    ChromeUtils.defineLazyGetter(this, "_topSiteURLs", getTopSiteURLs);
    102  },
    103 
    104  notify: function Thumbnails_notify() {
    105    gBrowserThumbnails._topSiteURLsRefreshTimer = null;
    106    gBrowserThumbnails.clearTopSiteURLCache();
    107  },
    108 
    109  /**
    110   * State change progress listener for all tabs.
    111   */
    112  onStateChange: function Thumbnails_onStateChange(
    113    aBrowser,
    114    aWebProgress,
    115    aRequest,
    116    aStateFlags,
    117    _aStatus
    118  ) {
    119    if (
    120      aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
    121      aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK
    122    ) {
    123      this._delayedCapture(aBrowser);
    124    }
    125  },
    126 
    127  async _capture(aBrowser) {
    128    // Only capture about:newtab top sites.
    129    const topSites = await this._topSiteURLs;
    130    if (!aBrowser.currentURI || !topSites.includes(aBrowser.currentURI.spec)) {
    131      return;
    132    }
    133    if (await this._shouldCapture(aBrowser)) {
    134      await PageThumbs.captureAndStoreIfStale(aBrowser);
    135    }
    136  },
    137 
    138  _delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
    139    if (this._timeouts.has(aBrowser)) {
    140      this._cancelDelayedCallbacks(aBrowser);
    141    } else {
    142      aBrowser.addEventListener("scroll", this, true);
    143    }
    144 
    145    let idleCallback = () => {
    146      this._cancelDelayedCapture(aBrowser);
    147      this._capture(aBrowser);
    148    };
    149 
    150    // setTimeout to set a guarantee lower bound for the requestIdleCallback
    151    // (and therefore the delayed capture)
    152    let timeoutId = setTimeout(() => {
    153      let idleCallbackId = requestIdleCallback(idleCallback, {
    154        timeout: this._captureDelayMS * 30,
    155      });
    156      this._timeouts.set(aBrowser, { isTimeout: false, id: idleCallbackId });
    157    }, this._captureDelayMS);
    158 
    159    this._timeouts.set(aBrowser, { isTimeout: true, id: timeoutId });
    160  },
    161 
    162  _shouldCapture: async function Thumbnails_shouldCapture(aBrowser) {
    163    // Capture only if it's the currently selected tab and not an about: page.
    164    if (
    165      aBrowser != gBrowser.selectedBrowser ||
    166      gBrowser.currentURI.schemeIs("about")
    167    ) {
    168      return false;
    169    }
    170    return PageThumbs.shouldStoreThumbnail(aBrowser);
    171  },
    172 
    173  _cancelDelayedCapture: function Thumbnails_cancelDelayedCapture(aBrowser) {
    174    if (this._timeouts.has(aBrowser)) {
    175      aBrowser.removeEventListener("scroll", this);
    176      this._cancelDelayedCallbacks(aBrowser);
    177      this._timeouts.delete(aBrowser);
    178    }
    179  },
    180 
    181  _cancelDelayedCallbacks: function Thumbnails_cancelDelayedCallbacks(
    182    aBrowser
    183  ) {
    184    let timeoutData = this._timeouts.get(aBrowser);
    185 
    186    if (timeoutData.isTimeout) {
    187      clearTimeout(timeoutData.id);
    188    } else {
    189      // idle callback dispatched
    190      window.cancelIdleCallback(timeoutData.id);
    191    }
    192  },
    193 };
    194 
    195 async function getTopSiteURLs() {
    196  // The _topSiteURLs getter can be expensive to run, but its return value can
    197  // change frequently on new profiles, so as a compromise we cache its return
    198  // value as a lazy getter for 1 minute every time it's called.
    199  gBrowserThumbnails._topSiteURLsRefreshTimer = Cc[
    200    "@mozilla.org/timer;1"
    201  ].createInstance(Ci.nsITimer);
    202  gBrowserThumbnails._topSiteURLsRefreshTimer.initWithCallback(
    203    gBrowserThumbnails,
    204    60 * 1000,
    205    Ci.nsITimer.TYPE_ONE_SHOT
    206  );
    207  let sites = [];
    208  // Get both the top sites returned by the query, and also any pinned sites
    209  // that the user might have added manually that also need a screenshot.
    210  // Also include top sites that don't have rich icons
    211  let topSites = await NewTabUtils.activityStreamLinks.getTopSites();
    212  sites.push(...topSites.filter(link => !(link.faviconSize >= 96)));
    213  sites.push(...NewTabUtils.pinnedLinks.links);
    214  return sites.reduce((urls, link) => {
    215    if (link) {
    216      urls.push(link.url);
    217    }
    218    return urls;
    219  }, []);
    220 }
    221 
    222 ChromeUtils.defineLazyGetter(
    223  gBrowserThumbnails,
    224  "_topSiteURLs",
    225  getTopSiteURLs
    226 );