mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 10:15:41 +00:00
250 lines
6.4 KiB
JavaScript
250 lines
6.4 KiB
JavaScript
/* 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");
|
|
|
|
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 id='win'/>";
|
|
const PREF_BRANCH = "browser.newtab.";
|
|
const TOPIC_DELAYED_STARTUP = "browser-delayed-startup-finished";
|
|
const PRELOADER_INTERVAL_MS = 3000;
|
|
const PRELOADER_INIT_DELAY_MS = 5000;
|
|
|
|
this.BrowserNewTabPreloader = {
|
|
init: function Preloader_init() {
|
|
Initializer.start();
|
|
},
|
|
|
|
uninit: function Preloader_uninit() {
|
|
Initializer.stop();
|
|
HostFrame.destroy();
|
|
Preferences.uninit();
|
|
HiddenBrowser.destroy();
|
|
},
|
|
|
|
newTab: function Preloader_newTab(aTab) {
|
|
return HiddenBrowser.swapWithNewTab(aTab);
|
|
}
|
|
};
|
|
|
|
Object.freeze(BrowserNewTabPreloader);
|
|
|
|
let Initializer = {
|
|
_timer: null,
|
|
_observing: false,
|
|
|
|
start: function Initializer_start() {
|
|
Services.obs.addObserver(this, TOPIC_DELAYED_STARTUP, false);
|
|
this._observing = true;
|
|
},
|
|
|
|
stop: function Initializer_stop() {
|
|
if (this._timer) {
|
|
this._timer.cancel();
|
|
this._timer = null;
|
|
}
|
|
|
|
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 == "timer-callback") {
|
|
this._timer = null;
|
|
this._startPreloader();
|
|
}
|
|
},
|
|
|
|
_startTimer: function Initializer_startTimer() {
|
|
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
this._timer.init(this, PRELOADER_INIT_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
},
|
|
|
|
_startPreloader: function Initializer_startPreloader() {
|
|
Preferences.init();
|
|
if (Preferences.enabled) {
|
|
HiddenBrowser.create();
|
|
}
|
|
}
|
|
};
|
|
|
|
let Preferences = {
|
|
_enabled: null,
|
|
_branch: null,
|
|
_url: null,
|
|
|
|
get enabled() {
|
|
if (this._enabled === null) {
|
|
this._enabled = this._branch.getBoolPref("preload") &&
|
|
!this._branch.prefHasUserValue("url") &&
|
|
this.url && this.url != "about:blank";
|
|
}
|
|
|
|
return this._enabled;
|
|
},
|
|
|
|
get url() {
|
|
if (this._url === null) {
|
|
this._url = this._branch.getCharPref("url");
|
|
}
|
|
|
|
return this._url;
|
|
},
|
|
|
|
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(aSubject, aTopic, aData) {
|
|
let {url, enabled} = this;
|
|
this._url = this._enabled = null;
|
|
|
|
if (enabled && !this.enabled) {
|
|
HiddenBrowser.destroy();
|
|
} else if (!enabled && this.enabled) {
|
|
HiddenBrowser.create();
|
|
} else if (this._browser && url != this.url) {
|
|
HiddenBrowser.update(this.url);
|
|
}
|
|
},
|
|
};
|
|
|
|
let HiddenBrowser = {
|
|
_timer: null,
|
|
|
|
get isPreloaded() {
|
|
return this._browser &&
|
|
this._browser.contentDocument &&
|
|
this._browser.contentDocument.readyState == "complete" &&
|
|
this._browser.currentURI.spec == Preferences.url;
|
|
},
|
|
|
|
swapWithNewTab: function (aTab) {
|
|
if (!this.isPreloaded || this._timer) {
|
|
return false;
|
|
}
|
|
|
|
let tabbrowser = aTab.ownerDocument.defaultView.gBrowser;
|
|
if (!tabbrowser) {
|
|
return false;
|
|
}
|
|
|
|
// Swap docShells.
|
|
tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
|
|
|
|
// Start a timer that will kick off preloading the next newtab page.
|
|
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
this._timer.init(this, PRELOADER_INTERVAL_MS, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
|
|
// Signal that we swapped docShells.
|
|
return true;
|
|
},
|
|
|
|
observe: function () {
|
|
this._timer = null;
|
|
|
|
if (this._browser) {
|
|
this._browser.loadURI(Preferences.url);
|
|
}
|
|
},
|
|
|
|
create: function () {
|
|
HostFrame.get(aFrame => {
|
|
let doc = aFrame.document;
|
|
this._browser = doc.createElementNS(XUL_NS, "browser");
|
|
this._browser.setAttribute("type", "content");
|
|
this._browser.setAttribute("src", Preferences.url);
|
|
doc.getElementById("win").appendChild(this._browser);
|
|
});
|
|
},
|
|
|
|
update: function (aURL) {
|
|
this._browser.setAttribute("src", aURL);
|
|
},
|
|
|
|
destroy: function () {
|
|
if (this._browser) {
|
|
this._browser.parentNode.removeChild(this._browser);
|
|
this._browser = null;
|
|
}
|
|
|
|
if (this._timer) {
|
|
this._timer.cancel();
|
|
this._timer = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
let HostFrame = {
|
|
_frame: null,
|
|
_loading: false,
|
|
|
|
get hiddenDOMDocument() {
|
|
return Services.appShell.hiddenDOMWindow.document;
|
|
},
|
|
|
|
get isReady() {
|
|
return this.hiddenDOMDocument.readyState === "complete";
|
|
},
|
|
|
|
get: function (callback) {
|
|
if (this._frame) {
|
|
callback(this._frame);
|
|
} else if (this.isReady && !this._loading) {
|
|
this._create(callback);
|
|
this._loading = true;
|
|
} else {
|
|
Services.tm.currentThread.dispatch(() => HostFrame.get(callback),
|
|
Ci.nsIThread.DISPATCH_NORMAL);
|
|
}
|
|
},
|
|
|
|
destroy: function () {
|
|
this._frame = null;
|
|
},
|
|
|
|
_create: function (callback) {
|
|
let doc = this.hiddenDOMDocument;
|
|
let iframe = doc.createElementNS(HTML_NS, "iframe");
|
|
doc.documentElement.appendChild(iframe);
|
|
|
|
let frame = iframe.contentWindow;
|
|
let docShell = frame.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDocShell);
|
|
|
|
docShell.createAboutBlankContentViewer(null);
|
|
frame.location = XUL_PAGE;
|
|
|
|
let eventHandler = docShell.chromeEventHandler;
|
|
eventHandler.addEventListener("DOMContentLoaded", function onLoad() {
|
|
eventHandler.removeEventListener("DOMContentLoaded", onLoad, false);
|
|
callback(HostFrame._frame = frame);
|
|
}, false);
|
|
}
|
|
};
|