diff --git a/browser/components/newtab/RemoteNewTabLocation.jsm b/browser/components/newtab/RemoteNewTabLocation.jsm index 15f1330360ae..9da939247cd8 100644 --- a/browser/components/newtab/RemoteNewTabLocation.jsm +++ b/browser/components/newtab/RemoteNewTabLocation.jsm @@ -1,19 +1,40 @@ -/* globals Services */ +/* globals Services, UpdateUtils, XPCOMUtils, URL, NewTabPrefsProvider */ +/* exported RemoteNewTabLocation */ "use strict"; this.EXPORTED_SYMBOLS = ["RemoteNewTabLocation"]; -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.importGlobalProperties(["URL"]); +const {interfaces: Ci, utils: Cu} = Components; +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.importGlobalProperties(["URL"]); -// TODO: will get dynamically set in bug 1210478 -const DEFAULT_PAGE_LOCATION = "https://newtab.cdn.mozilla.net/v2/nightly/en-US/index.html"; +XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils", + "resource://gre/modules/UpdateUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", + "resource:///modules/NewTabPrefsProvider.jsm"); -this.RemoteNewTabLocation = { - _url: new URL(DEFAULT_PAGE_LOCATION), - _overridden: false, +// 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"; + +const DEFAULT_PAGE_LOCATION = "https://newtab.cdn.mozilla.net/%VERSION%/%CHANNEL%/%LOCALE%/index.html"; + +const VALID_CHANNELS = new Set(["esr", "release", "beta", "aurora", "nightly"]); + +const NEWTAB_VERSION = "0"; + +let RemoteLocationProvider = function() { + NewTabPrefsProvider.prefs.on(PREF_SELECTED_LOCALE, this.updateMaybe.bind(this)); + NewTabPrefsProvider.prefs.on(PREF_MATCH_OS_LOCALE, this.updateMaybe.bind(this)); + this._url = this.generateDefaultURL(); + this._overridden = false; +}; + +RemoteLocationProvider.prototype = { get href() { return this._url.href; }, @@ -26,17 +47,108 @@ this.RemoteNewTabLocation = { return this._overridden; }, - override: function(newURL) { + /** + * Gets the currently selected locale + * + * @return {String} the selected locale or "en-US" if none is selected + */ + get locale() { + let result = "en-US"; + let matchOS; + + try { + matchOS = Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE); + } + catch (e) {} + + if (matchOS) { + result = Services.locale.getLocaleComponentForUserAgent(); + } + + try { + let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE, + Ci.nsIPrefLocalizedString); + if (locale) { + result = locale.data; + } + } + catch (e) {} + + try { + result = Services.prefs.getCharPref(PREF_SELECTED_LOCALE); + } + catch (e) {} + + return result; + }, + + get version() { + return NEWTAB_VERSION; + }, + + get channels() { + return VALID_CHANNELS; + }, + + /** + * Returns the release name from an Update Channel name + * + * @return {String} a release name based on the update channel. Defaults to nightly + */ + releaseFromUpdateChannel(channel) { + let result = "nightly"; + if (VALID_CHANNELS.has(channel)) { + result = channel; + } + return result; + }, + + /* + * Updates the location when the page is not overriden. + * Useful when there is a pref change + */ + updateMaybe() { + if (!this.overridden) { + let url = this.generateDefaultURL(); + if (url !== this._url) { + this._url = url; + Services.obs.notifyObservers(null, "remote-new-tab-location-changed", + this._url.href); + } + } + }, + + /* + * Generate a default url based on locale and update channel + */ + generateDefaultURL() { + let releaseName = this.releaseFromUpdateChannel(UpdateUtils.UpdateChannel); + let uri = DEFAULT_PAGE_LOCATION + .replace("%VERSION%", NEWTAB_VERSION) + .replace("%LOCALE%", this.locale) + .replace("%CHANNEL%", releaseName); + return new URL(uri); + }, + + /* + * Override the Remote newtab page location. + */ + override(newURL) { this._url = new URL(newURL); this._overridden = true; Services.obs.notifyObservers(null, "remote-new-tab-location-changed", this._url.href); }, - reset: function() { - this._url = new URL(DEFAULT_PAGE_LOCATION); + /* + * Reset the newtab page location to the default value + */ + reset() { + this._url = this.generateDefaultURL(); this._overridden = false; Services.obs.notifyObservers(null, "remote-new-tab-location-changed", this._url.href); } }; + +let RemoteNewTabLocation = new RemoteLocationProvider(); diff --git a/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js b/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js index e280b03426a5..1134dcad90b5 100644 --- a/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js +++ b/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js @@ -1,19 +1,24 @@ -/* globals ok, equal, RemoteNewTabLocation, Services */ +/* globals ok, equal, RemoteNewTabLocation, NewTabPrefsProvider, Services */ "use strict"; Components.utils.import("resource:///modules/RemoteNewTabLocation.jsm"); +Components.utils.import("resource:///modules/NewTabPrefsProvider.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.importGlobalProperties(["URL"]); -add_task(function* () { - var notificationPromise; - let defaultHref = RemoteNewTabLocation.href; +const defaultHref = RemoteNewTabLocation.href; + +add_task(function *test_defaults() { ok(RemoteNewTabLocation.href, "Default location has an href"); ok(RemoteNewTabLocation.origin, "Default location has an origin"); ok(!RemoteNewTabLocation.overridden, "Default location is not overridden"); +}); + +add_task(function *test_overrides() { let testURL = new URL("https://example.com/"); + let notificationPromise; notificationPromise = changeNotificationPromise(testURL.href); RemoteNewTabLocation.override(testURL.href); @@ -29,6 +34,41 @@ add_task(function* () { equal(RemoteNewTabLocation.href, defaultHref, "Remote href should be reset"); }); +add_task(function *test_updates() { + let notificationPromise; + Services.prefs.setBoolPref("intl.locale.matchOS", false); + NewTabPrefsProvider.prefs.startTracking(); + + notificationPromise = changeNotificationPromise(defaultHref); + Services.prefs.setBoolPref("intl.locale.matchOS", true); + yield notificationPromise; + equal(RemoteNewTabLocation.href, defaultHref, "Remote href should be updated"); + + let testURL = new URL("https://example.com/"); + RemoteNewTabLocation.override(testURL.href); + notificationPromise = changeNotificationPromise(defaultHref); + Services.prefs.setBoolPref("intl.locale.matchOS", false); + equal(RemoteNewTabLocation.href, testURL.href, "Remote href should not update when overridden"); + RemoteNewTabLocation.reset(); + yield notificationPromise; + equal(RemoteNewTabLocation.href, defaultHref, "href updates on reset, not on override"); + + NewTabPrefsProvider.prefs.stopTracking(); +}); + +add_task(function *test_release_names() { + let valid_channels = RemoteNewTabLocation.channels; + let invalid_channels = new Set(["default", "invalid"]); + + for (let channel of valid_channels) { + equal(channel, RemoteNewTabLocation.releaseFromUpdateChannel(channel), "release == channel name when valid"); + } + + for (let channel of invalid_channels) { + equal("nightly", RemoteNewTabLocation.releaseFromUpdateChannel(channel), "release == nightly when invalid"); + } +}); + function changeNotificationPromise(aNewURL) { return new Promise(resolve => { Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint ignore:line