зеркало из https://github.com/mozilla/gecko-dev.git
285 строки
9.4 KiB
JavaScript
285 строки
9.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/.
|
|
*/
|
|
|
|
/* globals XPCOMUtils, NewTabPrefsProvider, Services,
|
|
Locale, UpdateUtils, MODE_CHANNEL_MAP
|
|
*/
|
|
"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, "MODE_CHANNEL_MAP",
|
|
"resource:///modules/NewTabRemoteResources.jsm");
|
|
|
|
const LOCAL_NEWTAB_URL = "chrome://browser/content/newtab/newTab.xhtml";
|
|
|
|
const REMOTE_NEWTAB_PATH = "/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";
|
|
|
|
const VALID_CHANNELS = new Set(["esr", "release", "beta", "aurora", "nightly"]);
|
|
|
|
const REMOTE_NEWTAB_VERSION = "0";
|
|
|
|
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);
|
|
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);
|
|
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%", REMOTE_NEWTAB_VERSION)
|
|
.replace("%LOCALE%", Locale.getLocale())
|
|
.replace("%CHANNEL%", releaseName);
|
|
let mode = Services.prefs.getCharPref(PREF_REMOTE_MODE, "production");
|
|
if (!(mode in MODE_CHANNEL_MAP)) {
|
|
mode = "production";
|
|
}
|
|
return 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 REMOTE_NEWTAB_VERSION;
|
|
},
|
|
|
|
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]);
|