mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 00:25:27 +00:00
271 lines
7.2 KiB
JavaScript
271 lines
7.2 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";
|
|
|
|
let 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 XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
const PREF_BRANCH = "browser.newtab.";
|
|
const TOPIC_DELAYED_STARTUP = "browser-delayed-startup-finished";
|
|
const PRELOADER_INIT_DELAY_MS = 5000;
|
|
|
|
let BrowserNewTabPreloader = {
|
|
init: function Preloader_init() {
|
|
Initializer.start();
|
|
},
|
|
|
|
uninit: function Preloader_uninit() {
|
|
Initializer.stop();
|
|
HostFrame.destroy();
|
|
Preferences.uninit();
|
|
HiddenBrowser.destroy();
|
|
},
|
|
|
|
newTab: function Preloader_newTab(aTab) {
|
|
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 = {
|
|
get isPreloaded() {
|
|
return this._browser &&
|
|
this._browser.contentDocument &&
|
|
this._browser.contentDocument.readyState == "complete" &&
|
|
this._browser.currentURI.spec == Preferences.url;
|
|
},
|
|
|
|
swapWithNewTab: function HiddenBrowser_swapWithNewTab(aTab) {
|
|
if (this.isPreloaded) {
|
|
let tabbrowser = aTab.ownerDocument.defaultView.gBrowser;
|
|
if (tabbrowser) {
|
|
tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
|
|
}
|
|
}
|
|
},
|
|
|
|
create: function HiddenBrowser_create() {
|
|
HostFrame.getFrame(function (aFrame) {
|
|
let doc = aFrame.document;
|
|
this._browser = doc.createElementNS(XUL_NS, "browser");
|
|
this._browser.setAttribute("type", "content");
|
|
this._browser.setAttribute("src", Preferences.url);
|
|
doc.documentElement.appendChild(this._browser);
|
|
}.bind(this));
|
|
},
|
|
|
|
update: function HiddenBrowser_update(aURL) {
|
|
this._browser.setAttribute("src", aURL);
|
|
},
|
|
|
|
destroy: function HiddenBrowser_destroy() {
|
|
if (this._browser) {
|
|
this._browser.parentNode.removeChild(this._browser);
|
|
this._browser = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
let HostFrame = {
|
|
_listener: null,
|
|
_privilegedFrame: null,
|
|
|
|
_privilegedContentTypes: {
|
|
"application/vnd.mozilla.xul+xml": true,
|
|
"application/xhtml+xml": true
|
|
},
|
|
|
|
get _frame() {
|
|
delete this._frame;
|
|
return this._frame = Services.appShell.hiddenDOMWindow;
|
|
},
|
|
|
|
get _isReady() {
|
|
let readyState = this._frame.document.readyState;
|
|
return (readyState == "complete" || readyState == "interactive");
|
|
},
|
|
|
|
get _isPrivileged() {
|
|
return (this._frame.location.protocol == "chrome:" &&
|
|
this._frame.document.contentType in this._privilegedContentTypes);
|
|
},
|
|
|
|
getFrame: function HostFrame_getFrame(aCallback) {
|
|
if (this._isReady && !this._isPrivileged) {
|
|
this._createPrivilegedFrame();
|
|
}
|
|
|
|
if (this._isReady) {
|
|
aCallback(this._frame);
|
|
} else {
|
|
this._waitUntilLoaded(aCallback);
|
|
}
|
|
},
|
|
|
|
destroy: function HostFrame_destroy() {
|
|
delete this._frame;
|
|
this._listener = null;
|
|
},
|
|
|
|
_createPrivilegedFrame: function HostFrame_createPrivilegedFrame() {
|
|
let doc = this._frame.document;
|
|
let iframe = doc.createElement("iframe");
|
|
iframe.setAttribute("src", "chrome://browser/content/newtab/preload.xhtml");
|
|
doc.documentElement.appendChild(iframe);
|
|
this._frame = iframe.contentWindow;
|
|
},
|
|
|
|
_waitUntilLoaded: function HostFrame_waitUntilLoaded(aCallback) {
|
|
this._listener = new HiddenWindowLoadListener(this._frame, function () {
|
|
HostFrame.getFrame(aCallback);
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
function HiddenWindowLoadListener(aWindow, aCallback) {
|
|
this._window = aWindow;
|
|
this._callback = aCallback;
|
|
|
|
let docShell = Services.appShell.hiddenWindow.docShell;
|
|
this._webProgress = docShell.QueryInterface(Ci.nsIWebProgress);
|
|
this._webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_ALL);
|
|
}
|
|
|
|
HiddenWindowLoadListener.prototype = {
|
|
_window: null,
|
|
_callback: null,
|
|
_webProgress: null,
|
|
|
|
_destroy: function HiddenWindowLoadListener_destroy() {
|
|
this._webProgress.removeProgressListener(this);
|
|
this._window = null;
|
|
this._callback = null;
|
|
this._webProgress = null;
|
|
},
|
|
|
|
onStateChange:
|
|
function HiddenWindowLoadListener_onStateChange(aWebProgress, aRequest,
|
|
aStateFlags, aStatus) {
|
|
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
|
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
|
|
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
|
|
this._window == aWebProgress.DOMWindow) {
|
|
this._callback();
|
|
this._destroy();
|
|
}
|
|
},
|
|
|
|
onStatusChange: function () {},
|
|
onLocationChange: function () {},
|
|
onProgressChange: function () {},
|
|
onSecurityChange: function () {},
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
|
Ci.nsISupportsWeakReference])
|
|
};
|