forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			270 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			270 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";
 | |
| 
 | |
| 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 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;
 | |
| 
 | |
| 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) {
 | |
|     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])
 | |
| };
 | 
