michael@0: #ifdef 0 michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #endif michael@0: michael@0: Cu.import("resource://gre/modules/NewTabUtils.jsm"); michael@0: michael@0: /** michael@0: * Keeps thumbnails of open web pages up-to-date. michael@0: */ michael@0: let gBrowserThumbnails = { michael@0: /** michael@0: * Pref that controls whether we can store SSL content on disk michael@0: */ michael@0: PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl", michael@0: michael@0: _captureDelayMS: 1000, michael@0: michael@0: /** michael@0: * Used to keep track of disk_cache_ssl preference michael@0: */ michael@0: _sslDiskCacheEnabled: null, michael@0: michael@0: /** michael@0: * Map of capture() timeouts assigned to their browsers. michael@0: */ michael@0: _timeouts: null, michael@0: michael@0: /** michael@0: * List of tab events we want to listen for. michael@0: */ michael@0: _tabEvents: ["TabClose", "TabSelect"], michael@0: michael@0: init: function Thumbnails_init() { michael@0: // Bug 863512 - Make page thumbnails work in electrolysis michael@0: if (gMultiProcessBrowser) michael@0: return; michael@0: michael@0: PageThumbs.addExpirationFilter(this); michael@0: gBrowser.addTabsProgressListener(this); michael@0: Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false); michael@0: michael@0: this._sslDiskCacheEnabled = michael@0: Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL); michael@0: michael@0: this._tabEvents.forEach(function (aEvent) { michael@0: gBrowser.tabContainer.addEventListener(aEvent, this, false); michael@0: }, this); michael@0: michael@0: this._timeouts = new WeakMap(); michael@0: }, michael@0: michael@0: uninit: function Thumbnails_uninit() { michael@0: // Bug 863512 - Make page thumbnails work in electrolysis michael@0: if (gMultiProcessBrowser) michael@0: return; michael@0: michael@0: PageThumbs.removeExpirationFilter(this); michael@0: gBrowser.removeTabsProgressListener(this); michael@0: Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this); michael@0: michael@0: this._tabEvents.forEach(function (aEvent) { michael@0: gBrowser.tabContainer.removeEventListener(aEvent, this, false); michael@0: }, this); michael@0: }, michael@0: michael@0: handleEvent: function Thumbnails_handleEvent(aEvent) { michael@0: switch (aEvent.type) { michael@0: case "scroll": michael@0: let browser = aEvent.currentTarget; michael@0: if (this._timeouts.has(browser)) michael@0: this._delayedCapture(browser); michael@0: break; michael@0: case "TabSelect": michael@0: this._delayedCapture(aEvent.target.linkedBrowser); michael@0: break; michael@0: case "TabClose": { michael@0: this._clearTimeout(aEvent.target.linkedBrowser); michael@0: break; michael@0: } michael@0: } michael@0: }, michael@0: michael@0: observe: function Thumbnails_observe() { michael@0: this._sslDiskCacheEnabled = michael@0: Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL); michael@0: }, michael@0: michael@0: filterForThumbnailExpiration: michael@0: function Thumbnails_filterForThumbnailExpiration(aCallback) { michael@0: aCallback(this._topSiteURLs); michael@0: }, michael@0: michael@0: /** michael@0: * State change progress listener for all tabs. michael@0: */ michael@0: onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress, michael@0: aRequest, aStateFlags, aStatus) { michael@0: if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && michael@0: aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) michael@0: this._delayedCapture(aBrowser); michael@0: }, michael@0: michael@0: _capture: function Thumbnails_capture(aBrowser) { michael@0: if (this._shouldCapture(aBrowser)) michael@0: PageThumbs.captureAndStoreIfStale(aBrowser); michael@0: }, michael@0: michael@0: _delayedCapture: function Thumbnails_delayedCapture(aBrowser) { michael@0: if (this._timeouts.has(aBrowser)) michael@0: clearTimeout(this._timeouts.get(aBrowser)); michael@0: else michael@0: aBrowser.addEventListener("scroll", this, true); michael@0: michael@0: let timeout = setTimeout(function () { michael@0: this._clearTimeout(aBrowser); michael@0: this._capture(aBrowser); michael@0: }.bind(this), this._captureDelayMS); michael@0: michael@0: this._timeouts.set(aBrowser, timeout); michael@0: }, michael@0: michael@0: _shouldCapture: function Thumbnails_shouldCapture(aBrowser) { michael@0: // Capture only if it's the currently selected tab. michael@0: if (aBrowser != gBrowser.selectedBrowser) michael@0: return false; michael@0: michael@0: // Only capture about:newtab top sites. michael@0: if (this._topSiteURLs.indexOf(aBrowser.currentURI.spec) < 0) michael@0: return false; michael@0: michael@0: // Don't capture in per-window private browsing mode. michael@0: if (PrivateBrowsingUtils.isWindowPrivate(window)) michael@0: return false; michael@0: michael@0: let doc = aBrowser.contentDocument; michael@0: michael@0: // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as michael@0: // that currently regresses Talos SVG tests. michael@0: if (doc instanceof SVGDocument || doc instanceof XMLDocument) michael@0: return false; michael@0: michael@0: // There's no point in taking screenshot of loading pages. michael@0: if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) michael@0: return false; michael@0: michael@0: // Don't take screenshots of about: pages. michael@0: if (aBrowser.currentURI.schemeIs("about")) michael@0: return false; michael@0: michael@0: let channel = aBrowser.docShell.currentDocumentChannel; michael@0: michael@0: // No valid document channel. We shouldn't take a screenshot. michael@0: if (!channel) michael@0: return false; michael@0: michael@0: // Don't take screenshots of internally redirecting about: pages. michael@0: // This includes error pages. michael@0: let uri = channel.originalURI; michael@0: if (uri.schemeIs("about")) michael@0: return false; michael@0: michael@0: let httpChannel; michael@0: try { michael@0: httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); michael@0: } catch (e) { /* Not an HTTP channel. */ } michael@0: michael@0: if (httpChannel) { michael@0: // Continue only if we have a 2xx status code. michael@0: try { michael@0: if (Math.floor(httpChannel.responseStatus / 100) != 2) michael@0: return false; michael@0: } catch (e) { michael@0: // Can't get response information from the httpChannel michael@0: // because mResponseHead is not available. michael@0: return false; michael@0: } michael@0: michael@0: // Cache-Control: no-store. michael@0: if (httpChannel.isNoStoreResponse()) michael@0: return false; michael@0: michael@0: // Don't capture HTTPS pages unless the user explicitly enabled it. michael@0: if (uri.schemeIs("https") && !this._sslDiskCacheEnabled) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: }, michael@0: michael@0: get _topSiteURLs() { michael@0: return NewTabUtils.links.getLinks().reduce((urls, link) => { michael@0: if (link) michael@0: urls.push(link.url); michael@0: return urls; michael@0: }, []); michael@0: }, michael@0: michael@0: _clearTimeout: function Thumbnails_clearTimeout(aBrowser) { michael@0: if (this._timeouts.has(aBrowser)) { michael@0: aBrowser.removeEventListener("scroll", this, false); michael@0: clearTimeout(this._timeouts.get(aBrowser)); michael@0: this._timeouts.delete(aBrowser); michael@0: } michael@0: } michael@0: };