forked from mirrors/gecko-dev
		
	 8d9ceb72a4
			
		
	
	
		8d9ceb72a4
		
	
	
	
	
		
			
			MozReview-Commit-ID: GNk74kkxHcV --HG-- extra : rebase_source : 8cd6cb4ab9272a20c65f5840bdf4332bfd0ff972
		
			
				
	
	
		
			289 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			289 lines
		
	
	
	
		
			9.8 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/.
 | |
| */
 | |
| 
 | |
| /* globals XPCOMUtils, NewTabPrefsProvider, Services,
 | |
|   Locale, UpdateUtils, NewTabRemoteResources
 | |
| */
 | |
| "use strict";
 | |
| 
 | |
| const {utils: Cu, interfaces: Ci} = Components;
 | |
| 
 | |
| Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 | |
| Cu.import("resource://gre/modules/Services.jsm");
 | |
| 
 | |
| XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
 | |
|                                   "resource://gre/modules/UpdateUtils.jsm");
 | |
| XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider",
 | |
|                                   "resource:///modules/NewTabPrefsProvider.jsm");
 | |
| XPCOMUtils.defineLazyModuleGetter(this, "Locale",
 | |
|                                   "resource://gre/modules/Locale.jsm");
 | |
| XPCOMUtils.defineLazyModuleGetter(this, "NewTabRemoteResources",
 | |
|                                   "resource:///modules/NewTabRemoteResources.jsm");
 | |
| 
 | |
| const LOCAL_NEWTAB_URL = "chrome://browser/content/newtab/newTab.xhtml";
 | |
| 
 | |
| const REMOTE_NEWTAB_PATH = "/newtab/v%VERSION%/%CHANNEL%/%LOCALE%/index.html";
 | |
| 
 | |
| const ABOUT_URL = "about:newtab";
 | |
| 
 | |
| // Pref that tells if remote newtab is enabled
 | |
| const PREF_REMOTE_ENABLED = "browser.newtabpage.remote";
 | |
| 
 | |
| // Pref branch necesssary for testing
 | |
| const PREF_REMOTE_CS_TEST = "browser.newtabpage.remote.content-signing-test";
 | |
| 
 | |
| // The preference that tells whether to match the OS locale
 | |
| const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
 | |
| 
 | |
| // The preference that tells what locale the user selected
 | |
| const PREF_SELECTED_LOCALE = "general.useragent.locale";
 | |
| 
 | |
| // The preference that tells what remote mode is enabled.
 | |
| const PREF_REMOTE_MODE = "browser.newtabpage.remote.mode";
 | |
| 
 | |
| // The preference that tells which remote version is expected.
 | |
| const PREF_REMOTE_VERSION = "browser.newtabpage.remote.version";
 | |
| 
 | |
| const VALID_CHANNELS = new Set(["esr", "release", "beta", "aurora", "nightly"]);
 | |
| 
 | |
| function AboutNewTabService() {
 | |
|   NewTabPrefsProvider.prefs.on(PREF_REMOTE_ENABLED, this._handleToggleEvent.bind(this));
 | |
| 
 | |
|   this._updateRemoteMaybe = this._updateRemoteMaybe.bind(this);
 | |
| 
 | |
|   // trigger remote change if needed, according to pref
 | |
|   this.toggleRemote(Services.prefs.getBoolPref(PREF_REMOTE_ENABLED));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * A service that allows for the overriding, at runtime, of the newtab page's url.
 | |
|  * Additionally, the service manages pref state between a remote and local newtab page.
 | |
|  *
 | |
|  * There is tight coupling with browser/about/AboutRedirector.cpp.
 | |
|  *
 | |
|  * 1. Browser chrome access:
 | |
|  *
 | |
|  * When the user issues a command to open a new tab page, usually clicking a button
 | |
|  * in the browser chrome or using shortcut keys, the browser chrome code invokes the
 | |
|  * service to obtain the newtab URL. It then loads that URL in a new tab.
 | |
|  *
 | |
|  * When not overridden, the default URL emitted by the service is "about:newtab".
 | |
|  * When overridden, it returns the overriden URL.
 | |
|  *
 | |
|  * 2. Redirector Access:
 | |
|  *
 | |
|  * When the URL loaded is about:newtab, the default behavior, or when entered in the
 | |
|  * URL bar, the redirector is hit. The service is then called to return either of
 | |
|  * two URLs, a chrome or remote one, based on the browser.newtabpage.remote pref.
 | |
|  *
 | |
|  * NOTE: "about:newtab" will always result in a default newtab page, and never an overridden URL.
 | |
|  *
 | |
|  * Access patterns:
 | |
|  *
 | |
|  * The behavior is different when accessing the service via browser chrome or via redirector
 | |
|  * largely to maintain compatibility with expectations of add-on developers.
 | |
|  *
 | |
|  * Loading a chrome resource, or an about: URL in the redirector with either the
 | |
|  * LOAD_NORMAL or LOAD_REPLACE flags yield unexpected behaviors, so a roundtrip
 | |
|  * to the redirector from browser chrome is avoided.
 | |
|  */
 | |
| AboutNewTabService.prototype = {
 | |
| 
 | |
|   _newTabURL: ABOUT_URL,
 | |
|   _remoteEnabled: false,
 | |
|   _remoteURL: null,
 | |
|   _overridden: false,
 | |
| 
 | |
|   classID: Components.ID("{dfcd2adc-7867-4d3a-ba70-17501f208142}"),
 | |
|   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutNewTabService]),
 | |
|   _xpcom_categories: [{
 | |
|     service: true
 | |
|   }],
 | |
| 
 | |
|   _handleToggleEvent(prefName, stateEnabled, forceState) { //jshint unused:false
 | |
|     if (this.toggleRemote(stateEnabled, forceState)) {
 | |
|       Services.obs.notifyObservers(null, "newtab-url-changed", ABOUT_URL);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * React to changes to the remote newtab pref.
 | |
|    *
 | |
|    * If browser.newtabpage.remote is true, this will change the default URL to the
 | |
|    * remote newtab page URL. If browser.newtabpage.remote is false, the default URL
 | |
|    * will be a local chrome URL.
 | |
|    *
 | |
|    * This will only act if there is a change of state and if not overridden.
 | |
|    *
 | |
|    * @returns {Boolean} Returns if there has been a state change
 | |
|    *
 | |
|    * @param {Boolean}   stateEnabled    remote state to set to
 | |
|    * @param {Boolean}   forceState      force state change
 | |
|    */
 | |
|   toggleRemote(stateEnabled, forceState) {
 | |
| 
 | |
|     if (!forceState && (this._overriden || stateEnabled === this._remoteEnabled)) {
 | |
|       // exit there is no change of state
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST);
 | |
|     if (stateEnabled) {
 | |
|       if (!csTest) {
 | |
|         this._remoteURL = this.generateRemoteURL();
 | |
|       } else {
 | |
|         this._remoteURL = this._newTabURL;
 | |
|       }
 | |
|       NewTabPrefsProvider.prefs.on(
 | |
|         PREF_SELECTED_LOCALE,
 | |
|         this._updateRemoteMaybe);
 | |
|       NewTabPrefsProvider.prefs.on(
 | |
|         PREF_MATCH_OS_LOCALE,
 | |
|         this._updateRemoteMaybe);
 | |
|       NewTabPrefsProvider.prefs.on(
 | |
|         PREF_REMOTE_MODE,
 | |
|         this._updateRemoteMaybe);
 | |
|       NewTabPrefsProvider.prefs.on(
 | |
|         PREF_REMOTE_VERSION,
 | |
|         this._updateRemoteMaybe);
 | |
|       this._remoteEnabled = true;
 | |
|     } else {
 | |
|       NewTabPrefsProvider.prefs.off(PREF_SELECTED_LOCALE, this._updateRemoteMaybe);
 | |
|       NewTabPrefsProvider.prefs.off(PREF_MATCH_OS_LOCALE, this._updateRemoteMaybe);
 | |
|       NewTabPrefsProvider.prefs.off(PREF_REMOTE_MODE, this._updateRemoteMaybe);
 | |
|       NewTabPrefsProvider.prefs.off(PREF_REMOTE_VERSION, this._updateRemoteMaybe);
 | |
|       this._remoteEnabled = false;
 | |
|     }
 | |
|     if (!csTest) {
 | |
|       this._newTabURL = ABOUT_URL;
 | |
|     }
 | |
|     return true;
 | |
|   },
 | |
| 
 | |
|   /*
 | |
|    * Generate a default url based on remote mode, version, locale and update channel
 | |
|    */
 | |
|   generateRemoteURL() {
 | |
|     let releaseName = this.releaseFromUpdateChannel(UpdateUtils.UpdateChannel);
 | |
|     let path = REMOTE_NEWTAB_PATH
 | |
|       .replace("%VERSION%", this.remoteVersion)
 | |
|       .replace("%LOCALE%", Locale.getLocale())
 | |
|       .replace("%CHANNEL%", releaseName);
 | |
|     let mode = Services.prefs.getCharPref(PREF_REMOTE_MODE, "production");
 | |
|     if (!(mode in NewTabRemoteResources.MODE_CHANNEL_MAP)) {
 | |
|       mode = "production";
 | |
|     }
 | |
|     return NewTabRemoteResources.MODE_CHANNEL_MAP[mode].origin + path;
 | |
|   },
 | |
| 
 | |
|   /*
 | |
|    * Returns the default URL.
 | |
|    *
 | |
|    * This URL only depends on the browser.newtabpage.remote pref. Overriding
 | |
|    * the newtab page has no effect on the result of this function.
 | |
|    *
 | |
|    * The result is also the remote URL if this is in a test (PREF_REMOTE_CS_TEST)
 | |
|    *
 | |
|    * @returns {String} the default newtab URL, remote or local depending on browser.newtabpage.remote
 | |
|    */
 | |
|   get defaultURL() {
 | |
|     let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST);
 | |
|     if (this._remoteEnabled || csTest)  {
 | |
|       return this._remoteURL;
 | |
|     }
 | |
|     return LOCAL_NEWTAB_URL;
 | |
|   },
 | |
| 
 | |
|   /*
 | |
|    * Updates the remote location when the page is not overriden.
 | |
|    *
 | |
|    * Useful when there is a dependent pref change
 | |
|    */
 | |
|   _updateRemoteMaybe() {
 | |
|     if (!this._remoteEnabled || this._overridden) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let url = this.generateRemoteURL();
 | |
|     if (url !== this._remoteURL) {
 | |
|       this._remoteURL = url;
 | |
|       Services.obs.notifyObservers(null, "newtab-url-changed",
 | |
|         this._remoteURL);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Returns the release name from an Update Channel name
 | |
|    *
 | |
|    * @returns {String} a release name based on the update channel. Defaults to nightly
 | |
|    */
 | |
|   releaseFromUpdateChannel(channelName) {
 | |
|     return VALID_CHANNELS.has(channelName) ? channelName : "nightly";
 | |
|   },
 | |
| 
 | |
|   get newTabURL() {
 | |
|     return this._newTabURL;
 | |
|   },
 | |
| 
 | |
|   get remoteVersion() {
 | |
|     return Services.prefs.getCharPref(PREF_REMOTE_VERSION, "1");
 | |
|   },
 | |
| 
 | |
|   get remoteReleaseName() {
 | |
|     return this.releaseFromUpdateChannel(UpdateUtils.UpdateChannel);
 | |
|   },
 | |
| 
 | |
|   set newTabURL(aNewTabURL) {
 | |
|     let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST);
 | |
|     aNewTabURL = aNewTabURL.trim();
 | |
|     if (aNewTabURL === ABOUT_URL) {
 | |
|       // avoid infinite redirects in case one sets the URL to about:newtab
 | |
|       this.resetNewTabURL();
 | |
|       return;
 | |
|     } else if (aNewTabURL === "") {
 | |
|       aNewTabURL = "about:blank";
 | |
|     }
 | |
|     let remoteURL = this.generateRemoteURL();
 | |
|     let prefRemoteEnabled = Services.prefs.getBoolPref(PREF_REMOTE_ENABLED);
 | |
|     let isResetLocal = !prefRemoteEnabled && aNewTabURL === LOCAL_NEWTAB_URL;
 | |
|     let isResetRemote = prefRemoteEnabled && aNewTabURL === remoteURL;
 | |
| 
 | |
|     if (isResetLocal || isResetRemote) {
 | |
|       if (this._overriden && !csTest) {
 | |
|         // only trigger a reset if previously overridden and this is no test
 | |
|         this.resetNewTabURL();
 | |
|       }
 | |
|       return;
 | |
|     }
 | |
|     // turn off remote state if needed
 | |
|     if (!csTest) {
 | |
|       this.toggleRemote(false);
 | |
|     } else {
 | |
|       // if this is a test, we want the remoteURL to be set
 | |
|       this._remoteURL = aNewTabURL;
 | |
|     }
 | |
|     this._newTabURL = aNewTabURL;
 | |
|     this._overridden = true;
 | |
|     Services.obs.notifyObservers(null, "newtab-url-changed", this._newTabURL);
 | |
|   },
 | |
| 
 | |
|   get overridden() {
 | |
|     return this._overridden;
 | |
|   },
 | |
| 
 | |
|   get remoteEnabled() {
 | |
|     return this._remoteEnabled;
 | |
|   },
 | |
| 
 | |
|   resetNewTabURL() {
 | |
|     this._overridden = false;
 | |
|     this._newTabURL = ABOUT_URL;
 | |
|     this.toggleRemote(Services.prefs.getBoolPref(PREF_REMOTE_ENABLED), true);
 | |
|     Services.obs.notifyObservers(null, "newtab-url-changed", this._newTabURL);
 | |
|   }
 | |
| };
 | |
| 
 | |
| this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutNewTabService]);
 |