diff options
Diffstat (limited to 'application/palemoon/modules/BrowserNewTabPreloader.jsm')
-rw-r--r-- | application/palemoon/modules/BrowserNewTabPreloader.jsm | 436 |
1 files changed, 0 insertions, 436 deletions
diff --git a/application/palemoon/modules/BrowserNewTabPreloader.jsm b/application/palemoon/modules/BrowserNewTabPreloader.jsm deleted file mode 100644 index 778698fba..000000000 --- a/application/palemoon/modules/BrowserNewTabPreloader.jsm +++ /dev/null @@ -1,436 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["BrowserNewTabPreloader"]; - -const Cu = Components.utils; -const Cc = Components.classes; -const Ci = Components.interfaces; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); - -const HTML_NS = "http://www.w3.org/1999/xhtml"; -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; -const XUL_PAGE = "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='win'/>"; -const NEWTAB_URL = "about:newtab"; -const PREF_BRANCH = "browser.newtab."; - -// The interval between swapping in a preload docShell and kicking off the -// next preload in the background. -const PRELOADER_INTERVAL_MS = 600; -// The initial delay before we start preloading our first new tab page. The -// timer is started after the first 'browser-delayed-startup' has been sent. -const PRELOADER_INIT_DELAY_MS = 5000; -// The number of miliseconds we'll wait after we received a notification that -// causes us to update our list of browsers and tabbrowser sizes. This acts as -// kind of a damper when too many events are occuring in quick succession. -const PRELOADER_UPDATE_DELAY_MS = 3000; - -const TOPIC_TIMER_CALLBACK = "timer-callback"; -const TOPIC_DELAYED_STARTUP = "browser-delayed-startup-finished"; -const TOPIC_XUL_WINDOW_CLOSED = "xul-window-destroyed"; - -function createTimer(obj, delay) { - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.init(obj, delay, Ci.nsITimer.TYPE_ONE_SHOT); - return timer; -} - -function clearTimer(timer) { - if (timer) { - timer.cancel(); - } - return null; -} - -this.BrowserNewTabPreloader = { - init: function Preloader_init() { - Initializer.start(); - }, - - uninit: function Preloader_uninit() { - Initializer.stop(); - HostFrame.destroy(); - Preferences.uninit(); - HiddenBrowsers.uninit(); - }, - - newTab: function Preloader_newTab(aTab) { - let win = aTab.ownerDocument.defaultView; - if (win.gBrowser) { - let utils = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - - let {width, height} = utils.getBoundsWithoutFlushing(win.gBrowser); - let hiddenBrowser = HiddenBrowsers.get(width, height) - if (hiddenBrowser) { - return hiddenBrowser.swapWithNewTab(aTab); - } - } - - return false; - } -}; - -Object.freeze(BrowserNewTabPreloader); - -var Initializer = { - _timer: null, - _observing: false, - - start: function Initializer_start() { - Services.obs.addObserver(this, TOPIC_DELAYED_STARTUP, false); - this._observing = true; - }, - - stop: function Initializer_stop() { - this._timer = clearTimer(this._timer); - - if (this._observing) { - Services.obs.removeObserver(this, TOPIC_DELAYED_STARTUP); - this._observing = false; - } - }, - - observe: function Initializer_observe(aSubject, aTopic, aData) { - if (aTopic == TOPIC_DELAYED_STARTUP) { - Services.obs.removeObserver(this, TOPIC_DELAYED_STARTUP); - this._observing = false; - this._startTimer(); - } else if (aTopic == TOPIC_TIMER_CALLBACK) { - this._timer = null; - this._startPreloader(); - } - }, - - _startTimer: function Initializer_startTimer() { - this._timer = createTimer(this, PRELOADER_INIT_DELAY_MS); - }, - - _startPreloader: function Initializer_startPreloader() { - Preferences.init(); - if (Preferences.enabled) { - HiddenBrowsers.init(); - } - } -}; - -var Preferences = { - _enabled: null, - _branch: null, - - get enabled() { - if (this._enabled === null) { - this._enabled = this._branch.getBoolPref("preload") && - !this._branch.prefHasUserValue("url"); - } - - return this._enabled; - }, - - init: function Preferences_init() { - this._branch = Services.prefs.getBranch(PREF_BRANCH); - this._branch.addObserver("", this, false); - }, - - uninit: function Preferences_uninit() { - if (this._branch) { - this._branch.removeObserver("", this); - this._branch = null; - } - }, - - observe: function Preferences_observe() { - let prevEnabled = this._enabled; - this._enabled = null; - - if (prevEnabled && !this.enabled) { - HiddenBrowsers.uninit(); - } else if (!prevEnabled && this.enabled) { - HiddenBrowsers.init(); - } - }, -}; - -var HiddenBrowsers = { - _browsers: null, - _updateTimer: null, - - _topics: [ - TOPIC_DELAYED_STARTUP, - TOPIC_XUL_WINDOW_CLOSED - ], - - init: function () { - this._browsers = new Map(); - this._updateBrowserSizes(); - this._topics.forEach(t => Services.obs.addObserver(this, t, false)); - }, - - uninit: function () { - if (this._browsers) { - this._topics.forEach(t => Services.obs.removeObserver(this, t, false)); - this._updateTimer = clearTimer(this._updateTimer); - - for (let [key, browser] of this._browsers) { - browser.destroy(); - } - this._browsers = null; - } - }, - - get: function (width, height) { - // We haven't been initialized, yet. - if (!this._browsers) { - return null; - } - - let key = width + "x" + height; - if (!this._browsers.has(key)) { - // Update all browsers' sizes if we can't find a matching one. - this._updateBrowserSizes(); - } - - // We should now have a matching browser. - if (this._browsers.has(key)) { - return this._browsers.get(key); - } - - // We should never be here. Return the first browser we find. - Cu.reportError("NewTabPreloader: no matching browser found after updating"); - for (let [size, browser] of this._browsers) { - return browser; - } - - // We should really never be here. - Cu.reportError("NewTabPreloader: not even a single browser was found?"); - return null; - }, - - observe: function (subject, topic, data) { - if (topic === TOPIC_TIMER_CALLBACK) { - this._updateTimer = null; - this._updateBrowserSizes(); - } else { - this._updateTimer = clearTimer(this._updateTimer); - this._updateTimer = createTimer(this, PRELOADER_UPDATE_DELAY_MS); - } - }, - - _updateBrowserSizes: function () { - let sizes = this._collectTabBrowserSizes(); - let toRemove = []; - - // Iterate all browsers and check that they - // each can be assigned to one of the sizes. - for (let [key, browser] of this._browsers) { - if (sizes.has(key)) { - // We already have a browser for that size, great! - sizes.delete(key); - } else { - // This browser is superfluous or needs to be resized. - toRemove.push(browser); - this._browsers.delete(key); - } - } - - // Iterate all sizes that we couldn't find a browser for. - for (let [key, {width, height}] of sizes) { - let browser; - if (toRemove.length) { - // Let's just resize one of the superfluous - // browsers and put it back into the map. - browser = toRemove.shift(); - browser.resize(width, height); - } else { - // No more browsers to reuse, create a new one. - browser = new HiddenBrowser(width, height); - } - - this._browsers.set(key, browser); - } - - // Finally, remove all browsers we don't need anymore. - toRemove.forEach(b => b.destroy()); - }, - - _collectTabBrowserSizes: function () { - let sizes = new Map(); - - function tabBrowserBounds() { - let wins = Services.ww.getWindowEnumerator("navigator:browser"); - while (wins.hasMoreElements()) { - let win = wins.getNext(); - if (win.gBrowser) { - let utils = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - yield utils.getBoundsWithoutFlushing(win.gBrowser); - } - } - } - - // Collect the sizes of all <tabbrowser>s out there. - for (let {width, height} of tabBrowserBounds()) { - if (width > 0 && height > 0) { - let key = width + "x" + height; - if (!sizes.has(key)) { - sizes.set(key, {width: width, height: height}); - } - } - } - - return sizes; - } -}; - -function HiddenBrowser(width, height) { - this.resize(width, height); - - HostFrame.get().then(aFrame => { - let doc = aFrame.document; - this._browser = doc.createElementNS(XUL_NS, "browser"); - this._browser.setAttribute("type", "content"); - this._browser.setAttribute("src", NEWTAB_URL); - this._applySize(); - doc.getElementById("win").appendChild(this._browser); - }); -} - -HiddenBrowser.prototype = { - _width: null, - _height: null, - _timer: null, - _needsFrameScripts: true, - - get isPreloaded() { - return this._browser && - this._browser.contentDocument && - this._browser.contentDocument.readyState === "complete" && - this._browser.currentURI.spec === NEWTAB_URL; - }, - - swapWithNewTab: function (aTab) { - if (!this.isPreloaded || this._timer) { - return false; - } - - let win = aTab.ownerDocument.defaultView; - let tabbrowser = win.gBrowser; - - if (!tabbrowser) { - return false; - } - - // Swap docShells. - tabbrowser.swapNewTabWithBrowser(aTab, this._browser); - - // Load all default frame scripts. - if (this._needsFrameScripts) { - this._needsFrameScripts = false; - - let mm = aTab.linkedBrowser.messageManager; - mm.loadFrameScript("chrome://browser/content/content.js", true); - mm.loadFrameScript("chrome://browser/content/content-sessionStore.js", true); - - if ("TabView" in win) { - mm.loadFrameScript("chrome://browser/content/tabview-content.js", true); - } - } - - // Start a timer that will kick off preloading the next newtab page. - this._timer = createTimer(this, PRELOADER_INTERVAL_MS); - - // Signal that we swapped docShells. - return true; - }, - - observe: function () { - this._timer = null; - - // Start pre-loading the new tab page. - this._browser.loadURI(NEWTAB_URL); - }, - - resize: function (width, height) { - this._width = width; - this._height = height; - this._applySize(); - }, - - _applySize: function () { - if (this._browser) { - this._browser.style.width = this._width + "px"; - this._browser.style.height = this._height + "px"; - } - }, - - destroy: function () { - if (this._browser) { - this._browser.remove(); - this._browser = null; - } - - this._timer = clearTimer(this._timer); - } -}; - -var HostFrame = { - _frame: null, - _deferred: null, - - get hiddenDOMDocument() { - return Services.appShell.hiddenDOMWindow.document; - }, - - get isReady() { - return this.hiddenDOMDocument.readyState === "complete"; - }, - - get: function () { - if (!this._deferred) { - this._deferred = Promise.defer(); - this._create(); - } - - return this._deferred.promise; - }, - - destroy: function () { - if (this._frame) { - if (!Cu.isDeadWrapper(this._frame)) { - this._frame.removeEventListener("load", this, true); - this._frame.remove(); - } - - this._frame = null; - this._deferred = null; - } - }, - - handleEvent: function () { - let contentWindow = this._frame.contentWindow; - if (contentWindow.location.href === XUL_PAGE) { - this._frame.removeEventListener("load", this, true); - this._deferred.resolve(contentWindow); - } else { - contentWindow.location = XUL_PAGE; - } - }, - - _create: function () { - if (this.isReady) { - let doc = this.hiddenDOMDocument; - this._frame = doc.createElementNS(HTML_NS, "iframe"); - this._frame.addEventListener("load", this, true); - doc.documentElement.appendChild(this._frame); - } else { - let flags = Ci.nsIThread.DISPATCH_NORMAL; - Services.tm.currentThread.dispatch(() => this._create(), flags); - } - } -}; |