From 3d81b4bb55c73a3cb4fdbdc8701b700fedae62d0 Mon Sep 17 00:00:00 2001 From: Olivier Yiptong Date: Fri, 13 Nov 2015 05:36:21 -0500 Subject: [PATCH] Bug 1210478 - Add Meta URL resolution in RemoteNewTabLocation r=mconley --- .../components/newtab/RemoteAboutNewTab.jsm | 2 + .../newtab/RemoteNewTabLocation.jsm | 131 +++++++++++++++--- .../xpcshell/test_RemoteNewTabLocation.js | 123 ++++++++++++++-- 3 files changed, 229 insertions(+), 27 deletions(-) diff --git a/browser/components/newtab/RemoteAboutNewTab.jsm b/browser/components/newtab/RemoteAboutNewTab.jsm index 1f335511d2a..4ef509633ea 100644 --- a/browser/components/newtab/RemoteAboutNewTab.jsm +++ b/browser/components/newtab/RemoteAboutNewTab.jsm @@ -43,6 +43,7 @@ let RemoteAboutNewTab = { * Initialize the RemotePageManager and add all message listeners for this page */ init: function() { + RemoteNewTabLocation.init(); this.pageListener = new RemotePages("about:remote-newtab"); this.pageListener.addMessageListener("NewTab:InitializeGrid", this.initializeGrid.bind(this)); this.pageListener.addMessageListener("NewTab:UpdateGrid", this.updateGrid.bind(this)); @@ -292,6 +293,7 @@ let RemoteAboutNewTab = { Ci.nsISupportsWeakReference]), uninit: function() { + RemoteNewTabLocation.uninit(); this._removeObservers(); this.pageListener.destroy(); this.pageListener = null; diff --git a/browser/components/newtab/RemoteNewTabLocation.jsm b/browser/components/newtab/RemoteNewTabLocation.jsm index 9ec17198772..1c958a275f8 100644 --- a/browser/components/newtab/RemoteNewTabLocation.jsm +++ b/browser/components/newtab/RemoteNewTabLocation.jsm @@ -1,17 +1,49 @@ -/* globals Services */ +/* globals Services, UpdateUtils, XPCOMUtils, URL, NewTabPrefsProvider, Locale */ +/* exported RemoteNewTabLocation */ "use strict"; this.EXPORTED_SYMBOLS = ["RemoteNewTabLocation"]; -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.importGlobalProperties(["URL"]); +const {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/v0/nightly/en-US/index.html"; +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"); -this.RemoteNewTabLocation = { - _url: new URL(DEFAULT_PAGE_LOCATION), +// 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/" + + "v%VERSION%/%CHANNEL%/%LOCALE%/index.html"; + +const VALID_CHANNELS = new Set(["esr", "release", "beta", "aurora", "nightly"]); + +const NEWTAB_VERSION = "0"; + +let RemoteNewTabLocation = { + /* + * Generate a default url based on locale and update channel + */ + _generateDefaultURL() { + let releaseName = this._releaseFromUpdateChannel(UpdateUtils.UpdateChannel); + let uri = DEFAULT_PAGE_LOCATION + .replace("%VERSION%", this.version) + .replace("%LOCALE%", Locale.getLocale()) + .replace("%CHANNEL%", releaseName); + return new URL(uri); + }, + + _url: null, _overridden: false, get href() { @@ -26,17 +58,84 @@ this.RemoteNewTabLocation = { return this._overridden; }, - override: function(newURL) { - this._url = new URL(newURL); - this._overridden = true; - Services.obs.notifyObservers(null, "remote-new-tab-location-changed", - this._url.href); + get version() { + return NEWTAB_VERSION; }, - reset: function() { - this._url = new URL(DEFAULT_PAGE_LOCATION); + 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.href !== this._url.href) { + this._url = url; + Services.obs.notifyObservers(null, "remote-new-tab-location-changed", + this._url.href); + } + } + }, + + /* + * Override the Remote newtab page location. + */ + override(newURL) { + let url = new URL(newURL); + if (url.href !== this._url.href) { + this._overridden = true; + this._url = url; + Services.obs.notifyObservers(null, "remote-new-tab-location-changed", + this._url.href); + } + }, + + /* + * Reset the newtab page location to the default value + */ + reset() { + let url = this._generateDefaultURL(); + if (url.href !== this._url.href) { + this._url = url; + this._overridden = false; + Services.obs.notifyObservers(null, "remote-new-tab-location-changed", + this._url.href); + } + }, + + init() { + 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(); + }, + + uninit() { + this._url = null; this._overridden = false; - Services.obs.notifyObservers(null, "remote-new-tab-location-changed", - this._url.href); + NewTabPrefsProvider.prefs.off(PREF_SELECTED_LOCALE, this._updateMaybe); + NewTabPrefsProvider.prefs.off(PREF_MATCH_OS_LOCALE, this._updateMaybe); } }; diff --git a/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js b/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js index e280b03426a..ad1b01de2e7 100644 --- a/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js +++ b/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js @@ -1,39 +1,140 @@ -/* globals ok, equal, RemoteNewTabLocation, Services */ +/* globals ok, equal, RemoteNewTabLocation, NewTabPrefsProvider, Services, Preferences */ +/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ "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.import("resource://gre/modules/Preferences.jsm"); Components.utils.importGlobalProperties(["URL"]); -add_task(function* () { - var notificationPromise; - let defaultHref = RemoteNewTabLocation.href; +RemoteNewTabLocation.init(); +const DEFAULT_HREF = RemoteNewTabLocation.href; +RemoteNewTabLocation.uninit(); +add_task(function* test_defaults() { + RemoteNewTabLocation.init(); ok(RemoteNewTabLocation.href, "Default location has an href"); ok(RemoteNewTabLocation.origin, "Default location has an origin"); ok(!RemoteNewTabLocation.overridden, "Default location is not overridden"); + RemoteNewTabLocation.uninit(); +}); +/** + * Tests the overriding of the default URL + */ +add_task(function* test_overrides() { + RemoteNewTabLocation.init(); let testURL = new URL("https://example.com/"); + let notificationPromise; - notificationPromise = changeNotificationPromise(testURL.href); + notificationPromise = nextChangeNotificationPromise( + testURL.href, "Remote Location should change"); RemoteNewTabLocation.override(testURL.href); yield notificationPromise; ok(RemoteNewTabLocation.overridden, "Remote location should be overridden"); - equal(RemoteNewTabLocation.href, testURL.href, "Remote href should be the custom URL"); - equal(RemoteNewTabLocation.origin, testURL.origin, "Remote origin should be the custom URL"); + equal(RemoteNewTabLocation.href, testURL.href, + "Remote href should be the custom URL"); + equal(RemoteNewTabLocation.origin, testURL.origin, + "Remote origin should be the custom URL"); - notificationPromise = changeNotificationPromise(defaultHref); + notificationPromise = nextChangeNotificationPromise( + DEFAULT_HREF, "Remote href should be reset"); RemoteNewTabLocation.reset(); yield notificationPromise; ok(!RemoteNewTabLocation.overridden, "Newtab URL should not be overridden"); - equal(RemoteNewTabLocation.href, defaultHref, "Remote href should be reset"); + RemoteNewTabLocation.uninit(); }); -function changeNotificationPromise(aNewURL) { +/** + * Tests how RemoteNewTabLocation responds to updates to prefs + */ +add_task(function* test_updates() { + RemoteNewTabLocation.init(); + let notificationPromise; + let expectedHref = "https://newtab.cdn.mozilla.net" + + `/v${RemoteNewTabLocation.version}` + + "/nightly" + + "/en-GB" + + "/index.html"; + Preferences.set("intl.locale.matchOS", true); + Preferences.set("general.useragent.locale", "en-GB"); + NewTabPrefsProvider.prefs.init(); + + // test update checks for prefs + notificationPromise = nextChangeNotificationPromise( + expectedHref, "Remote href should be updated"); + Preferences.set("intl.locale.matchOS", false); + yield notificationPromise; + + notificationPromise = nextChangeNotificationPromise( + DEFAULT_HREF, "Remote href changes back to default"); + Preferences.set("general.useragent.locale", "en-US"); + + yield notificationPromise; + + // test update fires on override and reset + let testURL = new URL("https://example.com/"); + notificationPromise = nextChangeNotificationPromise( + testURL.href, "a notification occurs on override"); + RemoteNewTabLocation.override(testURL.href); + yield notificationPromise; + + // from overridden to default + notificationPromise = nextChangeNotificationPromise( + DEFAULT_HREF, "a notification occurs on reset"); + RemoteNewTabLocation.reset(); + yield notificationPromise; + + // override to default URL from default URL + notificationPromise = nextChangeNotificationPromise( + testURL.href, "a notification only occurs for a change in overridden urls"); + RemoteNewTabLocation.override(DEFAULT_HREF); + RemoteNewTabLocation.override(testURL.href); + yield notificationPromise; + + // reset twice, only one notification for default URL + notificationPromise = nextChangeNotificationPromise( + DEFAULT_HREF, "reset occurs"); + RemoteNewTabLocation.reset(); + yield notificationPromise; + + notificationPromise = nextChangeNotificationPromise( + testURL.href, "a notification only occurs for a change in reset urls"); + RemoteNewTabLocation.reset(); + RemoteNewTabLocation.override(testURL.href); + yield notificationPromise; + + NewTabPrefsProvider.prefs.uninit(); + RemoteNewTabLocation.uninit(); +}); + +/** + * Verifies that RemoteNewTabLocation's _releaseFromUpdateChannel + * Returns the correct release names + */ +add_task(function* test_release_names() { + RemoteNewTabLocation.init(); + 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"); + } + RemoteNewTabLocation.uninit(); +}); + +function nextChangeNotificationPromise(aNewURL, testMessage) { return new Promise(resolve => { Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint ignore:line Services.obs.removeObserver(observer, aTopic); - equal(aData, aNewURL, "remote-new-tab-location-changed data should be new URL."); + equal(aData, aNewURL, testMessage); resolve(); }, "remote-new-tab-location-changed", false); });