--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1404,55 +1404,31 @@ pref("geo.provider.use_gpsd", true);
pref("network.disable.ipc.security", true);
// CustomizableUI debug logging.
pref("browser.uiCustomization.debug", false);
// CustomizableUI state of the browser's user interface
pref("browser.uiCustomization.state", "");
-// The remote content URL shown for FxA signup. Must use HTTPS.
-pref("identity.fxaccounts.remote.signup.uri", "https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v3");
-
-// The URL where remote content that forces re-authentication for Firefox Accounts
-// should be fetched. Must use HTTPS.
-pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v3");
-
-// The remote content URL shown for signin in. Must use HTTPS.
-pref("identity.fxaccounts.remote.signin.uri", "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v3");
-
-// The remote content URL shown for the email (FxA determines if we show sign-in or sign-up) endpoint. Must use HTTPS.
-pref("identity.fxaccounts.remote.email.uri", "https://accounts.firefox.com/?service=sync&context=fx_desktop_v3&action=email");
+// The remote FxA root content URL. Must use HTTPS.
+pref("identity.fxaccounts.remote.root", "https://accounts.firefox.com/");
-// The remote content URL where FxAccountsWebChannel messages originate.
-pref("identity.fxaccounts.remote.webchannel.uri", "https://accounts.firefox.com/");
-
-// The value of the context query parameter passed in some fxa requests when config
-// discovery is enabled.
+// The value of the context query parameter passed in fxa requests.
pref("identity.fxaccounts.contextParam", "fx_desktop_v3");
-// The URL we take the user to when they opt to "manage" their Firefox Account.
-// Note that this will always need to be in the same TLD as the
-// "identity.fxaccounts.remote.signup.uri" pref.
-pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings?service=sync&context=fx_desktop_v3");
-
-// The URL of the FxA device manager page
-pref("identity.fxaccounts.settings.devices.uri", "https://accounts.firefox.com/settings/clients?service=sync&context=fx_desktop_v3");
-
// The remote URL of the FxA Profile Server
pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
// The remote URL of the FxA OAuth Server
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
// Token server used by the FxA Sync identity.
pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5");
-// The URL to a page that explains how to connect another device to Sync.
-pref("identity.fxaccounts.remote.connectdevice.uri", "https://accounts.firefox.com/connect_another_device?service=sync&context=fx_desktop_v3");
// URLs for promo links to mobile browsers. Note that consumers are expected to
// append a value for utm_campaign.
pref("identity.mobilepromo.android", "https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
pref("identity.mobilepromo.ios", "https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
// Migrate any existing Firefox Account data from the default profile to the
// Developer Edition profile.
#ifdef MOZ_DEV_EDITION
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -2,16 +2,18 @@
* 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/. */
// This file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */
ChromeUtils.import("resource://services-sync/UIState.jsm");
+ChromeUtils.defineModuleGetter(this, "FxAccounts",
+ "resource://gre/modules/FxAccounts.jsm");
ChromeUtils.defineModuleGetter(this, "EnsureFxAccountsWebChannel",
"resource://gre/modules/FxAccountsWebChannel.jsm");
ChromeUtils.defineModuleGetter(this, "Weave",
"resource://services-sync/main.js");
const MIN_STATUS_ANIMATION_DURATION = 1600;
var gSync = {
@@ -86,18 +88,16 @@ var gSync = {
"services.sync.engine.tabs.filteredUrls", null, null, rx => {
try {
return new RegExp(rx, "i");
} catch (e) {
Cu.reportError(`Failed to build url filter regexp for send tab: ${e}`);
return null;
}
});
- XPCOMUtils.defineLazyPreferenceGetter(this, "FXA_CONNECT_DEVICE_URI",
- "identity.fxaccounts.remote.connectdevice.uri");
XPCOMUtils.defineLazyPreferenceGetter(this, "PRODUCT_INFO_BASE_URL",
"app.productInfo.baseURL");
},
_maybeUpdateUIState() {
// Update the UI.
if (UIState.isReady()) {
const state = UIState.get();
@@ -282,35 +282,34 @@ var gSync = {
this.openPrefs("menupanel", "fxa");
break;
}
PanelUI.hide();
},
async openSignInAgainPage(entryPoint) {
- const url = await fxAccounts.promiseAccountsForceSigninURI(entryPoint);
+ const url = await FxAccounts.config.promiseForceSigninURI(entryPoint);
switchToTabHavingURI(url, true, {
replaceQueryString: true,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
},
async openDevicesManagementPage(entryPoint) {
- let url = await fxAccounts.promiseAccountsManageDevicesURI(entryPoint);
+ let url = await FxAccounts.config.promiseManageDevicesURI(entryPoint);
switchToTabHavingURI(url, true, {
replaceQueryString: true,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
},
- openConnectAnotherDevice(entryPoint) {
- let url = new URL(this.FXA_CONNECT_DEVICE_URI);
- url.searchParams.append("entrypoint", entryPoint);
- openUILinkIn(url.href, "tab");
+ async openConnectAnotherDevice(entryPoint) {
+ const url = await FxAccounts.config.promiseConnectDeviceURI(entryPoint);
+ openUILinkIn(url, "tab");
},
openSendToDevicePromo() {
let url = this.PRODUCT_INFO_BASE_URL;
url += "send-tabs/?utm_source=" + Services.appinfo.name.toLowerCase();
switchToTabHavingURI(url, true, { replaceQueryString: true });
},
--- a/browser/components/customizableui/test/browser_synced_tabs_menu.js
+++ b/browser/components/customizableui/test/browser_synced_tabs_menu.js
@@ -1,16 +1,17 @@
/* 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";
requestLongerTimeout(2);
+ChromeUtils.import("resource://gre/modules/FxAccounts.jsm");
let {SyncedTabs} = ChromeUtils.import("resource://services-sync/SyncedTabs.jsm", {});
let {UIState} = ChromeUtils.import("resource://services-sync/UIState.jsm", {});
ChromeUtils.defineModuleGetter(this, "UITour", "resource:///modules/UITour.jsm");
// These are available on the widget implementation, but it seems impossible
// to grab that impl at runtime.
const DECKINDEX_TABS = 0;
@@ -35,36 +36,41 @@ let mockedInternal = {
get isConfiguredToSyncTabs() { return true; },
getTabClients() { return Promise.resolve([]); },
syncTabs() { return Promise.resolve(); },
hasSyncedThisSession: false,
};
add_task(async function setup() {
+ const getSignedInUser = FxAccounts.config.getSignedInUser;
+ FxAccounts.config.getSignedInUser = async () => Promise.resolve({uid: "uid", email: "foo@bar.com"});
+ Services.prefs.setCharPref("identity.fxaccounts.remote.root", "https://example.com/");
+
let oldInternal = SyncedTabs._internal;
SyncedTabs._internal = mockedInternal;
let origNotifyStateUpdated = UIState._internal.notifyStateUpdated;
// Sync start-up will interfere with our tests, don't let UIState send UI updates.
UIState._internal.notifyStateUpdated = () => {};
// Force gSync initialization
gSync.init();
registerCleanupFunction(() => {
+ FxAccounts.config.getSignedInUser = getSignedInUser;
+ Services.prefs.clearUserPref("identity.fxaccounts.remote.root");
UIState._internal.notifyStateUpdated = origNotifyStateUpdated;
SyncedTabs._internal = oldInternal;
});
});
// The test expects the about:preferences#sync page to open in the current tab
async function openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
info("Check Sync button functionality");
- Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "https://example.com/");
CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
await waitForOverflowButtonShown();
// check the button's functionality
await document.getElementById("nav-bar").overflowable.show();
if (entryPoint == "uitour") {
@@ -116,17 +122,16 @@ async function openPrefsFromMenuPanel(ex
function hideOverflow() {
let panelHidePromise = promiseOverflowHidden(window);
PanelUI.overflowPanel.hidePopup();
return panelHidePromise;
}
async function asyncCleanup() {
- Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
// reset the panel UI to the default state
await resetCustomization();
ok(CustomizableUI.inDefaultState, "The panel UI is in default state again.");
// restore the tabs
BrowserTestUtils.addTab(gBrowser, initialLocation);
gBrowser.removeTab(newTab);
UITour.tourBrowsersByWindow.delete(window);
@@ -149,44 +154,31 @@ add_task(asyncCleanup);
// When Sync is configured in a "needs reauthentication" state.
add_task(async function() {
gSync.updateAllUI({ status: UIState.STATUS_LOGIN_FAILED, email: "foo@bar.com" });
await openPrefsFromMenuPanel("PanelUI-remotetabs-reauthsync", "synced-tabs");
});
// Test the Connect Another Device button
add_task(async function() {
- Services.prefs.setCharPref("identity.fxaccounts.remote.connectdevice.uri", "http://example.com/connectdevice");
-
gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, email: "foo@bar.com" });
let button = document.getElementById("PanelUI-remotetabs-connect-device-button");
ok(button, "found the button");
await document.getElementById("nav-bar").overflowable.show();
+ let expectedUrl = "https://example.com/connect_another_device?service=sync&context=" +
+ "fx_desktop_v3&entrypoint=synced-tabs&uid=uid&email=foo%40bar.com";
+ let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, expectedUrl);
button.click();
// the panel should have been closed.
ok(!isOverflowOpen(), "click closed the panel");
- // should be a new tab - wait for the load.
- is(gBrowser.tabs.length, 2, "there's a new tab");
- await new Promise(resolve => {
- if (gBrowser.selectedBrowser.currentURI.spec == "about:blank") {
- BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(resolve);
- return;
- }
- // the new tab has already transitioned away from about:blank so we
- // are good to go.
- resolve();
- });
+ await promiseTabOpened;
- let expectedUrl = `http://example.com/connectdevice?entrypoint=synced-tabs`;
- is(gBrowser.selectedBrowser.currentURI.spec, expectedUrl, "correct URL");
gBrowser.removeTab(gBrowser.selectedTab);
-
- Services.prefs.clearUserPref("identity.fxaccounts.remote.connectdevice.uri");
});
// Test the "Sync Now" button
add_task(async function() {
gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, email: "foo@bar.com" });
await document.getElementById("nav-bar").overflowable.show();
let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -32,16 +32,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
CustomizableUI: "resource:///modules/CustomizableUI.jsm",
DateTimePickerHelper: "resource://gre/modules/DateTimePickerHelper.jsm",
DirectoryLinksProvider: "resource:///modules/DirectoryLinksProvider.jsm",
ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
Feeds: "resource:///modules/Feeds.jsm",
FileUtils: "resource://gre/modules/FileUtils.jsm",
FileSource: "resource://gre/modules/L10nRegistry.jsm",
FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
+ FxAccounts: "resource://gre/modules/FxAccounts.jsm",
HybridContentTelemetry: "resource://gre/modules/HybridContentTelemetry.jsm",
Integration: "resource://gre/modules/Integration.jsm",
L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
LoginHelper: "resource://gre/modules/LoginHelper.jsm",
LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
NetUtil: "resource://gre/modules/NetUtil.jsm",
@@ -231,17 +232,16 @@ function BrowserGlue() {
"@mozilla.org/widget/idleservice;1",
"nsIIdleService");
XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() {
ChromeUtils.import("resource:///modules/distribution.js");
return new DistributionCustomizer();
});
- ChromeUtils.defineModuleGetter(this, "fxAccounts", "resource://gre/modules/FxAccounts.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", "@mozilla.org/alerts-service;1", "nsIAlertsService");
this._init();
}
BrowserGlue.prototype = {
_saveSession: false,
_migrationImportsDefaultBookmarks: false,
@@ -400,20 +400,16 @@ BrowserGlue.prototype = {
this._distributionCustomizer.applyCustomizations();
// To apply distribution bookmarks use "places-init-complete".
} else if (data == "force-places-init") {
this._initPlaces(false);
} else if (data == "smart-bookmarks-init") {
this.ensurePlacesDefaultQueriesInitialized().then(() => {
Services.obs.notifyObservers(null, "test-smart-bookmarks-done");
});
- } else if (data == "mock-fxaccounts") {
- Object.defineProperty(this, "fxAccounts", {
- value: subject.wrappedJSObject
- });
} else if (data == "mock-alerts-service") {
Object.defineProperty(this, "AlertsService", {
value: subject.wrappedJSObject
});
} else if (data == "places-browser-init-complete") {
if (this._placesBrowserInitComplete) {
Services.obs.notifyObservers(null, "places-browser-init-complete");
}
@@ -2642,17 +2638,17 @@ BrowserGlue.prototype = {
let title = accountsBundle.GetStringFromName("deviceConnectedTitle");
let body = accountsBundle.formatStringFromName("deviceConnectedBody" +
(deviceName ? "" : ".noDeviceName"),
[deviceName], 1);
let clickCallback = async (subject, topic, data) => {
if (topic != "alertclickcallback")
return;
- let url = await this.fxAccounts.promiseAccountsManageDevicesURI("device-connected-notification");
+ let url = await FxAccounts.config.promiseManageDevicesURI("device-connected-notification");
let win = RecentWindow.getMostRecentBrowserWindow({private: false});
if (!win) {
this._openURLInNewWindow(url);
} else {
win.gBrowser.addTab(url);
}
};
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -94,16 +94,18 @@ const ICON_URL_APP = AppConstants.platfo
const APP_ICON_ATTR_NAME = "appHandlerIcon";
ChromeUtils.defineModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
if (AppConstants.MOZ_DEV_EDITION) {
ChromeUtils.defineModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
+ ChromeUtils.defineModuleGetter(this, "FxAccounts",
+ "resource://gre/modules/FxAccounts.jsm");
}
Preferences.addAll([
// Startup
{ id: "browser.startup.page", type: "int" },
{ id: "browser.startup.homepage", type: "wstring" },
{ id: "pref.browser.homepage.disable_button.current_page", type: "bool" },
@@ -710,17 +712,17 @@ var gMainPane = {
return;
}
const user = await fxAccounts.getSignedInUser();
if (user) {
// We have a user, open Sync preferences in the same tab
win.openUILinkIn("about:preferences#sync", "current");
return;
}
- let url = await fxAccounts.promiseAccountsSignInURI("dev-edition-setup");
+ let url = await FxAccounts.config.promiseSignInURI("dev-edition-setup");
let accountsTab = win.gBrowser.addTab(url);
win.gBrowser.selectedTab = accountsTab;
},
// HOME PAGE
/*
* Preferences:
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -1,24 +1,22 @@
/* 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/. */
/* import-globals-from preferences.js */
ChromeUtils.import("resource://services-sync/main.js");
+ChromeUtils.import("resource://gre/modules/FxAccounts.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function() {
return ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js", {});
});
-ChromeUtils.defineModuleGetter(this, "fxAccounts",
- "resource://gre/modules/FxAccounts.jsm");
-
ChromeUtils.defineModuleGetter(this, "UIState",
"resource://services-sync/UIState.jsm");
const FXA_PAGE_LOGGED_OUT = 0;
const FXA_PAGE_LOGGED_IN = 1;
// Indexes into the "login status" deck.
// We are in a successful verified state - everything should work!
@@ -141,28 +139,28 @@ var gSyncPane = {
// Links for mobile devices before the user is logged in.
let url = Services.prefs.getCharPref("identity.mobilepromo.android") + "sync-preferences";
document.getElementById("fxaMobilePromo-android").setAttribute("href", url);
url = Services.prefs.getCharPref("identity.mobilepromo.ios") + "sync-preferences";
document.getElementById("fxaMobilePromo-ios").setAttribute("href", url);
// Links for mobile devices shown after the user is logged in.
- fxAccounts.promiseAccountsConnectDeviceURI(this._getEntryPoint()).then(connectURI => {
+ FxAccounts.config.promiseConnectDeviceURI(this._getEntryPoint()).then(connectURI => {
document.getElementById("mobilePromo-singledevice").setAttribute("href", connectURI);
});
- fxAccounts.promiseAccountsManageDevicesURI(this._getEntryPoint()).then(manageURI => {
+ FxAccounts.config.promiseManageDevicesURI(this._getEntryPoint()).then(manageURI => {
document.getElementById("mobilePromo-multidevice").setAttribute("href", manageURI);
});
document.getElementById("tosPP-small-ToS").setAttribute("href", Weave.Svc.Prefs.get("fxa.termsURL"));
document.getElementById("tosPP-small-PP").setAttribute("href", Weave.Svc.Prefs.get("fxa.privacyURL"));
- fxAccounts.promiseAccountsSignUpURI(this._getEntryPoint()).then(signUpURI => {
+ FxAccounts.config.promiseSignUpURI(this._getEntryPoint()).then(signUpURI => {
document.getElementById("noFxaSignUp").setAttribute("href", signUpURI);
});
this.updateWeavePrefs();
// Notify observers that the UI is now ready
Services.obs.notifyObservers(window, "sync-pane-loaded");
},
@@ -321,17 +319,17 @@ var gSyncPane = {
if (profileImageElement.style.listStyleImage === bgImage) {
profileImageElement.style.removeProperty("list-style-image");
}
};
img.src = state.avatarURL;
}
// The "manage account" link embeds the uid, so we need to update this
// if the account state changes.
- fxAccounts.promiseAccountsManageURI(this._getEntryPoint()).then(accountsManageURI => {
+ FxAccounts.config.promiseManageURI(this._getEntryPoint()).then(accountsManageURI => {
document.getElementById("verifiedManage").setAttribute("href", accountsManageURI);
});
let isUnverified = state.status == UIState.STATUS_NOT_VERIFIED;
// The mobile promo links - which one is shown depends on the number of devices.
let isMultiDevice = Weave.Service.clientsEngine.stats.numClients > 1;
document.getElementById("mobilePromo-singledevice").hidden = isUnverified || isMultiDevice;
document.getElementById("mobilePromo-multidevice").hidden = isUnverified || !isMultiDevice;
},
@@ -357,43 +355,43 @@ var gSyncPane = {
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
// And tell it to load our URL.
browser.loadURI(url);
},
async signIn() {
- const url = await fxAccounts.promiseAccountsSignInURI(this._getEntryPoint());
+ const url = await FxAccounts.config.promiseSignInURI(this._getEntryPoint());
this.replaceTabWithUrl(url);
},
async reSignIn() {
// There's a bit of an edge-case here - we might be forcing reauth when we've
// lost the FxA account data - in which case we'll not get a URL as the re-auth
// URL embeds account info and the server endpoint complains if we don't
// supply it - So we just use the regular "sign in" URL in that case.
let entryPoint = this._getEntryPoint();
- const url = (await fxAccounts.promiseAccountsForceSigninURI(entryPoint)) ||
- (await fxAccounts.promiseAccountsSignInURI(entryPoint));
+ const url = (await FxAccounts.config.promiseForceSigninURI(entryPoint)) ||
+ (await FxAccounts.config.promiseSignInURI(entryPoint));
this.replaceTabWithUrl(url);
},
clickOrSpaceOrEnterPressed(event) {
// Note: charCode is deprecated, but 'char' not yet implemented.
// Replace charCode with char when implemented, see Bug 680830
return ((event.type == "click" && event.button == 0) ||
(event.type == "keypress" &&
(event.charCode == KeyEvent.DOM_VK_SPACE || event.keyCode == KeyEvent.DOM_VK_RETURN)));
},
openChangeProfileImage(event) {
if (this.clickOrSpaceOrEnterPressed(event)) {
- fxAccounts.promiseAccountsChangeProfileURI(this._getEntryPoint(), "avatar")
+ FxAccounts.config.promiseChangeAvatarURI(this._getEntryPoint())
.then(url => {
this.openContentInBrowser(url, {
replaceQueryString: true,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
});
// Prevent page from scrolling on the space key.
event.preventDefault();
@@ -404,17 +402,17 @@ var gSyncPane = {
if (this.clickOrSpaceOrEnterPressed(event)) {
this.manageFirefoxAccount();
// Prevent page from scrolling on the space key.
event.preventDefault();
}
},
manageFirefoxAccount() {
- fxAccounts.promiseAccountsManageURI(this._getEntryPoint())
+ FxAccounts.config.promiseManageURI(this._getEntryPoint())
.then(url => {
this.openContentInBrowser(url, {
replaceQueryString: true,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
});
},
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -13,17 +13,17 @@ ChromeUtils.import("resource://gre/modul
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.importGlobalProperties(["URL"]);
ChromeUtils.defineModuleGetter(this, "BrowserUITelemetry",
"resource:///modules/BrowserUITelemetry.jsm");
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
-ChromeUtils.defineModuleGetter(this, "fxAccounts",
+ChromeUtils.defineModuleGetter(this, "FxAccounts",
"resource://gre/modules/FxAccounts.jsm");
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
ChromeUtils.defineModuleGetter(this, "PageActions",
"resource:///modules/PageActions.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ProfileAge",
@@ -546,43 +546,44 @@ this.UITour = {
return false;
}
window.openPreferences(data.pane, { origin: "UITour" });
break;
}
case "showFirefoxAccounts": {
Promise.resolve().then(() => {
- return data.email ? fxAccounts.promiseAccountsEmailURI(data.email, "uitour") :
- fxAccounts.promiseAccountsSignUpURI("uitour");
+ return data.email ? FxAccounts.config.promiseEmailURI(data.email, "uitour") :
+ FxAccounts.config.promiseSignUpURI("uitour");
}).then(uri => {
const url = new URL(uri);
// Call our helper to validate extraURLCampaignParams and populate URLSearchParams
if (!this._populateCampaignParams(url, data.extraURLCampaignParams)) {
log.warn("showFirefoxAccounts: invalid campaign args specified");
return;
}
// We want to replace the current tab.
browser.loadURI(url.href);
});
break;
}
case "showConnectAnotherDevice": {
- const url = new URL(Services.prefs.getCharPref("identity.fxaccounts.remote.connectdevice.uri"));
- url.searchParams.append("entrypoint", "uitour");
- // Call our helper to validate extraURLCampaignParams and populate URLSearchParams
- if (!this._populateCampaignParams(url, data.extraURLCampaignParams)) {
- log.warn("showConnectAnotherDevice: invalid campaign args specified");
- return false;
- }
+ FxAccounts.config.promiseConnectDeviceURI("uitour").then(uri => {
+ const url = new URL(uri);
+ // Call our helper to validate extraURLCampaignParams and populate URLSearchParams
+ if (!this._populateCampaignParams(url, data.extraURLCampaignParams)) {
+ log.warn("showConnectAnotherDevice: invalid campaign args specified");
+ return;
+ }
- // We want to replace the current tab.
- browser.loadURI(url.href);
+ // We want to replace the current tab.
+ browser.loadURI(url.href);
+ });
break;
}
case "resetFirefox": {
// Open a reset profile dialog window.
if (ResetProfile.resetSupported()) {
ResetProfile.openConfirmationDialog(window);
}
--- a/browser/components/uitour/test/browser_UITour_sync.js
+++ b/browser/components/uitour/test/browser_UITour_sync.js
@@ -1,27 +1,24 @@
"use strict";
var gTestTab;
var gContentAPI;
var gContentWindow;
registerCleanupFunction(function() {
- Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
- Services.prefs.clearUserPref("identity.fxaccounts.remote.email.uri");
+ Services.prefs.clearUserPref("identity.fxaccounts.remote.root");
Services.prefs.clearUserPref("services.sync.username");
});
add_task(setup_UITourTest);
add_task(async function setup() {
- Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri",
- "https://example.com/signup");
- Services.prefs.setCharPref("identity.fxaccounts.remote.email.uri",
- "https://example.com/?action=email");
+ Services.prefs.setCharPref("identity.fxaccounts.remote.root",
+ "https://example.com");
});
add_UITour_task(async function test_checkSyncSetup_disabled() {
let result = await getConfigurationPromise("sync");
is(result.setup, false, "Sync shouldn't be setup by default");
});
add_UITour_task(async function test_checkSyncSetup_enabled() {
@@ -58,45 +55,45 @@ add_UITour_task(async function test_chec
is(result.totalDevices, 0, "totalDevices should be 0");
});
// The showFirefoxAccounts API is sync related, so we test that here too...
add_UITour_task(async function test_firefoxAccountsNoParams() {
info("Load https://accounts.firefox.com");
await gContentAPI.showFirefoxAccounts();
await BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
- "https://example.com/signup?entrypoint=uitour");
+ "https://example.com/signup?service=sync&context=fx_desktop_v3&entrypoint=uitour");
});
add_UITour_task(async function test_firefoxAccountsValidParams() {
info("Load https://accounts.firefox.com");
await gContentAPI.showFirefoxAccounts({ utm_foo: "foo", utm_bar: "bar" });
await BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
- "https://example.com/signup?entrypoint=uitour&utm_foo=foo&utm_bar=bar");
+ "https://example.com/signup?service=sync&context=fx_desktop_v3&entrypoint=uitour&utm_foo=foo&utm_bar=bar");
});
add_UITour_task(async function test_firefoxAccountsWithEmail() {
info("Load https://accounts.firefox.com");
await gContentAPI.showFirefoxAccounts(null, "foo@bar.com");
await BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
- "https://example.com/?action=email&email=foo%40bar.com&entrypoint=uitour");
+ "https://example.com/?service=sync&context=fx_desktop_v3&entrypoint=uitour&email=foo%40bar.com");
});
add_UITour_task(async function test_firefoxAccountsNonAlphaValue() {
// All characters in the value are allowed, but they must be automatically escaped.
// (we throw a unicode character in there too - it's not auto-utf8 encoded,
// but that's ok, so long as it is escaped correctly.)
let value = "foo& /=?:\\\xa9";
// encodeURIComponent encodes spaces to %20 but we want "+"
let expected = encodeURIComponent(value).replace(/%20/g, "+");
info("Load https://accounts.firefox.com");
await gContentAPI.showFirefoxAccounts({ utm_foo: value });
await BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
- "https://example.com/signup?entrypoint=uitour&utm_foo=" + expected);
+ "https://example.com/signup?service=sync&context=fx_desktop_v3&entrypoint=uitour&utm_foo=" + expected);
});
// A helper to check the request was ignored due to invalid params.
async function checkFxANotLoaded() {
try {
await waitForConditionPromise(() => {
return gBrowser.selectedBrowser.currentURI.spec.startsWith("https://example.com");
}, "Check if FxA opened");
--- a/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
+++ b/browser/extensions/activity-stream/lib/SnippetsFeed.jsm
@@ -6,17 +6,17 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
const {actionTypes: at, actionCreators: ac} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
ChromeUtils.defineModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
ChromeUtils.defineModuleGetter(this, "ProfileAge",
"resource://gre/modules/ProfileAge.jsm");
-ChromeUtils.defineModuleGetter(this, "fxAccounts",
+ChromeUtils.defineModuleGetter(this, "FxAccounts",
"resource://gre/modules/FxAccounts.jsm");
// Url to fetch snippets, in the urlFormatter service format.
const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
const TELEMETRY_PREF = "datareporting.healthreport.uploadEnabled";
const FXA_USERNAME_PREF = "services.sync.username";
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
// Prefix for any target matching a search engine.
@@ -120,17 +120,17 @@ this.SnippetsFeed = class SnippetsFeed {
Services.prefs.removeObserver(SNIPPETS_URL_PREF, this._refresh);
Services.prefs.removeObserver(TELEMETRY_PREF, this._refresh);
Services.prefs.removeObserver(FXA_USERNAME_PREF, this._refresh);
Services.obs.removeObserver(this, SEARCH_ENGINE_OBSERVER_TOPIC);
this.store.dispatch(ac.BroadcastToContent({type: at.SNIPPETS_RESET}));
}
async showFirefoxAccounts(browser) {
- const url = await fxAccounts.promiseAccountsSignUpURI("snippets");
+ const url = await FxAccounts.config.promiseSignUpURI("snippets");
// We want to replace the current tab.
browser.loadURI(url);
}
onAction(action) {
switch (action.type) {
case at.INIT:
this.init();
--- a/browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js
@@ -16,17 +16,17 @@ describe("SnippetsFeed", () => {
sandbox = sinon.sandbox.create();
overrider.set({
ProfileAge: class ProfileAge {
constructor() {
this.created = Promise.resolve(0);
this.reset = Promise.resolve(WEEK_IN_MS);
}
},
- fxAccounts: {promiseAccountsSignUpURI: sandbox.stub().returns(Promise.resolve(signUpUrl))}
+ FxAccounts: {config: {promiseSignUpURI: sandbox.stub().returns(Promise.resolve(signUpUrl))}}
});
});
afterEach(() => {
clock.restore();
overrider.restore();
sandbox.restore();
});
it("should dispatch a SNIPPETS_DATA action with the right data on INIT", async () => {
--- a/services/fxaccounts/FxAccounts.jsm
+++ b/services/fxaccounts/FxAccounts.jsm
@@ -54,26 +54,17 @@ var publicProperties = [
"handleDeviceDisconnection",
"handleEmailUpdated",
"hasLocalSession",
"invalidateCertificate",
"loadAndPoll",
"localtimeOffsetMsec",
"notifyDevices",
"now",
- "promiseAccountsChangeProfileURI",
- "promiseAccountsEmailURI",
- "promiseAccountsForceSigninURI",
- "promiseAccountsManageURI",
- "promiseAccountsManageDevicesURI",
- "promiseAccountsConnectDeviceURI",
- "promiseAccountsSignUpURI",
- "promiseAccountsSignInURI",
"removeCachedOAuthToken",
- "requiresHttps",
"resendVerificationEmail",
"resetCredentials",
"sessionStatus",
"setProfileCache",
"setSignedInUser",
"signOut",
"updateDeviceRegistration",
"deleteDeviceRegistration",
@@ -358,16 +349,17 @@ this.FxAccounts = function(mockInternal)
});
}
// wait until after the mocks are setup before initializing.
internal.initialize();
return Object.freeze(external);
};
+this.FxAccounts.config = FxAccountsConfig;
/**
* The internal API's constructor.
*/
function FxAccountsInternal() {
// Make a local copy of this constant so we can mock it in testing
this.POLL_SESSION = POLL_SESSION;
@@ -1386,115 +1378,16 @@ FxAccountsInternal.prototype = {
}
},
_rejectWhenVerified(currentState, error) {
currentState.whenVerifiedDeferred.reject(error);
delete currentState.whenVerifiedDeferred;
},
- requiresHttps() {
- // Also used in FxAccountsOAuthGrantClient.jsm.
- let allowHttp = Services.prefs.getBoolPref("identity.fxaccounts.allowHttp", false);
- return allowHttp !== true;
- },
-
- async promiseAccountsSignUpURI(entrypoint) {
- const url = new URL((await FxAccountsConfig.promiseAccountsSignUpURI()));
- if (entrypoint) {
- url.searchParams.append("entrypoint", entrypoint);
- }
- return url.href;
- },
-
- async promiseAccountsSignInURI(entrypoint) {
- const url = new URL((await FxAccountsConfig.promiseAccountsSignInURI()));
- if (entrypoint) {
- url.searchParams.append("entrypoint", entrypoint);
- }
- return url.href;
- },
-
- async promiseAccountsEmailURI(email, entrypoint) {
- const url = new URL((await FxAccountsConfig.promiseAccountsEmailURI()));
- url.searchParams.append("email", email);
- if (entrypoint) {
- url.searchParams.append("entrypoint", entrypoint);
- }
- return url.href;
- },
-
- /**
- * Pull an URL defined in the user preferences, add the current UID and email
- * to the query string, add entrypoint and extra params to the query string if
- * requested.
- * @param {string} prefName The preference name from where to pull the URL to format.
- * @param {string} [entrypoint] "entrypoint" searchParam value.
- * @param {Object.<string, string>} [extraParams] Additionnal searchParam key and values.
- * @returns {Promise.<string>} A promise that resolves to the formatted URL
- */
- async _formatPrefURL(prefName, entrypoint, extraParams) {
- let url = new URL(Services.urlFormatter.formatURLPref(prefName));
- if (this.requiresHttps() && url.protocol != "https:") {
- throw new Error("Firefox Accounts server must use HTTPS");
- }
- let accountData = await this.getSignedInUser();
- if (!accountData) {
- return Promise.resolve(null);
- }
- url.searchParams.append("uid", accountData.uid);
- url.searchParams.append("email", accountData.email);
- if (entrypoint) {
- url.searchParams.append("entrypoint", entrypoint);
- }
- if (extraParams) {
- for (let [k, v] of Object.entries(extraParams)) {
- url.searchParams.append(k, v);
- }
- }
- return this.currentAccountState.resolve(url.href);
- },
-
- // Returns a promise that resolves with the URL to use to force a re-signin
- // of the current account.
- async promiseAccountsForceSigninURI(entrypoint) {
- await FxAccountsConfig.ensureConfigured();
- return this._formatPrefURL("identity.fxaccounts.remote.force_auth.uri", entrypoint);
- },
-
- // Returns a promise that resolves with the URL to use to change
- // the current account's profile image.
- // if settingToEdit is set, the profile page should hightlight that setting
- // for the user to edit.
- async promiseAccountsChangeProfileURI(entrypoint, settingToEdit = null) {
- let extraParams;
- if (settingToEdit) {
- extraParams = { setting: settingToEdit };
- }
- return this._formatPrefURL("identity.fxaccounts.settings.uri", entrypoint, extraParams);
- },
-
- // Returns a promise that resolves with the URL to use to manage the current
- // user's FxA acct.
- async promiseAccountsManageURI(entrypoint) {
- return this._formatPrefURL("identity.fxaccounts.settings.uri", entrypoint);
- },
-
- // Returns a promise that resolves with the URL to use to manage the devices in
- // the current user's FxA acct.
- async promiseAccountsManageDevicesURI(entrypoint) {
- return this._formatPrefURL("identity.fxaccounts.settings.devices.uri", entrypoint);
- },
-
- // Returns a promise that resolves with the URL to use to connect a new
- // device to the current user's FxA acct.
- async promiseAccountsConnectDeviceURI(entrypoint) {
- return this._formatPrefURL("identity.fxaccounts.remote.connectdevice.uri", entrypoint);
- },
-
/**
* Get an OAuth token for the user
*
* @param options
* {
* scope: (string/array) the oauth scope(s) being requested. As a
* convenience, you may pass a string if only one scope is
* required, or an array of strings if multiple are needed.
@@ -1649,24 +1542,16 @@ FxAccountsInternal.prototype = {
/**
* Get the user's account and profile data if it is locally cached. If
* not cached it will return null, but cause the profile data to be fetched
* in the background, after which a ON_PROFILE_CHANGE_NOTIFICATION
* observer notification will be sent, at which time this can be called
* again to obtain the most recent profile info.
*
- * @param options
- * {
- * contentUrl: (string) Used by the FxAccountsWebChannel.
- * Defaults to pref identity.fxaccounts.settings.uri
- * profileServerUrl: (string) Used by the FxAccountsWebChannel.
- * Defaults to pref identity.fxaccounts.remote.profile.uri
- * }
- *
* @return Promise.<object | Error>
* The promise resolves to an accountData object with extra profile
* information such as profileImageUrl, or rejects with
* an error object ({error: ERROR, details: {}}) of the following:
* INVALID_PARAMETER
* NO_ACCOUNT
* UNVERIFIED_ACCOUNT
* NETWORK_ERROR
--- a/services/fxaccounts/FxAccountsConfig.jsm
+++ b/services/fxaccounts/FxAccountsConfig.jsm
@@ -10,54 +10,95 @@ ChromeUtils.import("resource://gre/modul
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
ChromeUtils.defineModuleGetter(this, "EnsureFxAccountsWebChannel",
"resource://gre/modules/FxAccountsWebChannel.jsm");
+XPCOMUtils.defineLazyPreferenceGetter(this, "ROOT_URL",
+ "identity.fxaccounts.remote.root");
+XPCOMUtils.defineLazyPreferenceGetter(this, "CONTEXT_PARAM",
+ "identity.fxaccounts.contextParam");
+XPCOMUtils.defineLazyPreferenceGetter(this, "REQUIRES_HTTPS",
+ // Also used in FxAccountsOAuthGrantClient.jsm.
+ "identity.fxaccounts.allowHttp", false,
+ null, val => !val);
+
const CONFIG_PREFS = [
+ "identity.fxaccounts.remote.root",
"identity.fxaccounts.auth.uri",
"identity.fxaccounts.remote.oauth.uri",
"identity.fxaccounts.remote.profile.uri",
"identity.sync.tokenserver.uri",
- "identity.fxaccounts.remote.webchannel.uri",
- "identity.fxaccounts.settings.uri",
- "identity.fxaccounts.settings.devices.uri",
- "identity.fxaccounts.remote.signup.uri",
- "identity.fxaccounts.remote.signin.uri",
- "identity.fxaccounts.remote.email.uri",
- "identity.fxaccounts.remote.connectdevice.uri",
- "identity.fxaccounts.remote.force_auth.uri",
];
this.FxAccountsConfig = {
+ async promiseSignUpURI(entrypoint) {
+ return this._buildURL("signup", {entrypoint});
+ },
- async _getPrefURL(prefName) {
- await this.ensureConfigured();
- let url = Services.urlFormatter.formatURLPref(prefName);
- if (fxAccounts.requiresHttps() && !/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
- throw new Error("Firefox Accounts server must use HTTPS");
- }
- return url;
+ async promiseSignInURI(entrypoint) {
+ return this._buildURL("signin", {entrypoint});
+ },
+
+ async promiseEmailURI(email, entrypoint) {
+ return this._buildURL("", {entrypoint, email});
+ },
+
+ async promiseForceSigninURI(entrypoint) {
+ return this._buildURL("force_auth", {entrypoint}, true);
+ },
+
+ async promiseManageURI(entrypoint) {
+ return this._buildURL("settings", {entrypoint}, true);
+ },
+
+ async promiseChangeAvatarURI(entrypoint) {
+ return this._buildURL("settings/avatar/change", {entrypoint}, true);
+ },
+
+ async promiseManageDevicesURI(entrypoint) {
+ return this._buildURL("settings/clients", {entrypoint}, true);
},
- // Returns a promise that resolves with the URI of the remote UI flows.
- promiseAccountsSignUpURI() {
- return this._getPrefURL("identity.fxaccounts.remote.signup.uri");
+ async promiseConnectDeviceURI(entrypoint) {
+ return this._buildURL("connect_another_device", {entrypoint}, true);
+ },
+
+ get defaultParams() {
+ return {service: "sync", context: CONTEXT_PARAM};
},
- // Returns a promise that resolves with the URI of the remote UI flows.
- promiseAccountsSignInURI() {
- return this._getPrefURL("identity.fxaccounts.remote.signin.uri");
- },
-
- promiseAccountsEmailURI() {
- return this._getPrefURL("identity.fxaccounts.remote.email.uri");
+ /**
+ * @param path should be parsable by the URL constructor first parameter.
+ * @param {Object.<string, string>} [extraParams] Additionnal search params.
+ * @param {bool} [addCredentials] if true we add the current logged-in user
+ * uid and email to the search params.
+ */
+ async _buildURL(path, extraParams, addCredentials = false) {
+ await this.ensureConfigured();
+ const url = new URL(path, ROOT_URL);
+ if (REQUIRES_HTTPS && url.protocol != "https:") {
+ throw new Error("Firefox Accounts server must use HTTPS");
+ }
+ const params = {...this.defaultParams, ...extraParams};
+ for (let [k, v] of Object.entries(params)) {
+ url.searchParams.append(k, v);
+ }
+ if (addCredentials) {
+ const accountData = await this.getSignedInUser();
+ if (!accountData) {
+ return null;
+ }
+ url.searchParams.append("uid", accountData.uid);
+ url.searchParams.append("email", accountData.email);
+ }
+ return url.href;
},
resetConfigURLs() {
let autoconfigURL = this.getAutoConfigURL();
if (!autoconfigURL) {
return;
}
// They have the autoconfig uri pref set, so we clear all the prefs that we
@@ -78,22 +119,58 @@ this.FxAccountsConfig = {
let rootURL = Services.urlFormatter.formatURL(pref);
if (rootURL.endsWith("/")) {
rootURL.slice(0, -1);
}
return rootURL;
},
async ensureConfigured() {
- let isSignedIn = !!(await fxAccounts.getSignedInUser());
+ await this.tryPrefsMigration();
+ let isSignedIn = !!(await this.getSignedInUser());
if (!isSignedIn) {
await this.fetchConfigURLs();
}
},
+ // In bug 1427674 we migrated a set of preferences with a shared origin
+ // to a single preference (identity.fxaccounts.remote.root).
+ // This whole function should be removed in version 65 or later once
+ // everyone had a chance to migrate.
+ async tryPrefsMigration() {
+ // If this pref is set, there is a very good chance the user is running
+ // a custom FxA content server.
+ if (!Services.prefs.prefHasUserValue("identity.fxaccounts.remote.signin.uri")) {
+ return;
+ }
+
+ if (Services.prefs.prefHasUserValue("identity.fxaccounts.autoconfig.uri")) {
+ await this.fetchConfigURLs();
+ } else {
+ // Best effort.
+ const signinURI = Services.prefs.getCharPref("identity.fxaccounts.remote.signin.uri");
+ Services.prefs.setCharPref("identity.fxaccounts.remote.root",
+ signinURI.slice(0, signinURI.lastIndexOf("/signin")) + "/");
+ }
+
+ const migratedPrefs = [
+ "identity.fxaccounts.remote.webchannel.uri",
+ "identity.fxaccounts.settings.uri",
+ "identity.fxaccounts.settings.devices.uri",
+ "identity.fxaccounts.remote.signup.uri",
+ "identity.fxaccounts.remote.signin.uri",
+ "identity.fxaccounts.remote.email.uri",
+ "identity.fxaccounts.remote.connectdevice.uri",
+ "identity.fxaccounts.remote.force_auth.uri",
+ ];
+ for (const pref of migratedPrefs) {
+ Services.prefs.clearUserPref(pref);
+ }
+ },
+
// Read expected client configuration from the fxa auth server
// (from `identity.fxaccounts.autoconfig.uri`/.well-known/fxa-client-configuration)
// and replace all the relevant our prefs with the information found there.
// This is only done before sign-in and sign-up, and even then only if the
// `identity.fxaccounts.autoconfig.uri` preference is set.
async fetchConfigURLs() {
let rootURL = this.getAutoConfigURL();
if (!rootURL) {
@@ -128,31 +205,24 @@ this.FxAccountsConfig = {
let authServerBase = config.auth_server_base_url;
if (!authServerBase.endsWith("/v1")) {
authServerBase += "/v1";
}
Services.prefs.setCharPref("identity.fxaccounts.auth.uri", authServerBase);
Services.prefs.setCharPref("identity.fxaccounts.remote.oauth.uri", config.oauth_server_base_url + "/v1");
Services.prefs.setCharPref("identity.fxaccounts.remote.profile.uri", config.profile_server_base_url + "/v1");
Services.prefs.setCharPref("identity.sync.tokenserver.uri", config.sync_tokenserver_base_url + "/1.0/sync/1.5");
- // Update the prefs that are based off of the autoconfig url
-
- let contextParam = encodeURIComponent(
- Services.prefs.getCharPref("identity.fxaccounts.contextParam"));
-
- Services.prefs.setCharPref("identity.fxaccounts.remote.webchannel.uri", rootURL);
- Services.prefs.setCharPref("identity.fxaccounts.settings.uri", rootURL + "/settings?service=sync&context=" + contextParam);
- Services.prefs.setCharPref("identity.fxaccounts.settings.devices.uri", rootURL + "/settings/clients?service=sync&context=" + contextParam);
- Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", rootURL + "/signup?service=sync&context=" + contextParam);
- Services.prefs.setCharPref("identity.fxaccounts.remote.signin.uri", rootURL + "/signin?service=sync&context=" + contextParam);
- Services.prefs.setCharPref("identity.fxaccounts.remote.email.uri", rootURL + "/?service=sync&context=" + contextParam + "&action=email");
- Services.prefs.setCharPref("identity.fxaccounts.remote.connectdevice.uri", rootURL + "/connect_another_device?service=sync&context=" + contextParam);
- Services.prefs.setCharPref("identity.fxaccounts.remote.force_auth.uri", rootURL + "/force_auth?service=sync&context=" + contextParam);
+ Services.prefs.setCharPref("identity.fxaccounts.remote.root", rootURL);
// Ensure the webchannel is pointed at the correct uri
EnsureFxAccountsWebChannel();
} catch (e) {
log.error("Failed to initialize configuration preferences from autoconfig object", e);
throw e;
}
},
+ // For test purposes, returns a Promise.
+ getSignedInUser() {
+ return fxAccounts.getSignedInUser();
+ }
+
};
--- a/services/fxaccounts/FxAccountsWebChannel.jsm
+++ b/services/fxaccounts/FxAccountsWebChannel.jsm
@@ -517,17 +517,17 @@ this.FxAccountsWebChannelHelpers.prototy
var singleton;
// The entry-point for this module, which ensures only one of our channels is
// ever created - we require this because the WebChannel is global in scope
// (eg, it uses the observer service to tell interested parties of interesting
// things) and allowing multiple channels would cause such notifications to be
// sent multiple times.
this.EnsureFxAccountsWebChannel = () => {
- let contentUri = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.webchannel.uri");
+ let contentUri = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.root");
if (singleton && singleton._contentUri !== contentUri) {
singleton.tearDown();
singleton = null;
}
if (!singleton) {
try {
if (contentUri) {
// The FxAccountsWebChannel listens for events and updates
--- a/services/fxaccounts/tests/browser/browser_device_connected.js
+++ b/services/fxaccounts/tests/browser/browser_device_connected.js
@@ -1,30 +1,32 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+ChromeUtils.import("resource://gre/modules/FxAccounts.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const gBrowserGlue = Cc["@mozilla.org/browser/browserglue;1"]
.getService(Ci.nsIObserver);
const accountsBundle = Services.strings.createBundle(
"chrome://browser/locale/accounts.properties"
);
const DEVICES_URL = "http://localhost/devices";
let expectedBody;
add_task(async function setup() {
- let fxAccounts = {
- promiseAccountsManageDevicesURI() {
- return Promise.resolve(DEVICES_URL);
- }
- };
- gBrowserGlue.observe({wrappedJSObject: fxAccounts}, "browser-glue-test", "mock-fxaccounts");
+ const origManageDevicesURI = FxAccounts.config.promiseManageDevicesURI;
+ FxAccounts.config.promiseManageDevicesURI = () => Promise.resolve(DEVICES_URL);
setupMockAlertsService();
+
+ registerCleanupFunction(function() {
+ FxAccounts.config.promiseManageDevicesURI = origManageDevicesURI;
+ delete window.FxAccounts;
+ });
});
async function testDeviceConnected(deviceName) {
info("testDeviceConnected with deviceName=" + deviceName);
gBrowser.selectedBrowser.loadURI("about:robots");
await waitForDocLoadComplete();
let waitForTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
--- a/services/fxaccounts/tests/xpcshell/test_accounts.js
+++ b/services/fxaccounts/tests/xpcshell/test_accounts.js
@@ -23,27 +23,16 @@ initTestLogging("Trace");
var log = Log.repository.getLogger("Services.FxAccounts.test");
log.level = Log.Level.Debug;
// See verbose logging from FxAccounts.jsm and jwcrypto.jsm.
Services.prefs.setCharPref("identity.fxaccounts.loglevel", "Trace");
Log.repository.getLogger("FirefoxAccounts").level = Log.Level.Trace;
Services.prefs.setCharPref("services.crypto.jwcrypto.log.level", "Debug");
-// The oauth server is mocked, but set these prefs to pass param checks
-Services.prefs.setCharPref("identity.fxaccounts.remote.oauth.uri", "https://example.com/v1");
-Services.prefs.setCharPref("identity.fxaccounts.oauth.client_id", "abc123");
-
-
-const PROFILE_SERVER_URL = "http://example.com/v1";
-const CONTENT_URL = "http://accounts.example.com/";
-
-Services.prefs.setCharPref("identity.fxaccounts.remote.profile.uri", PROFILE_SERVER_URL);
-Services.prefs.setCharPref("identity.fxaccounts.settings.uri", CONTENT_URL);
-
/*
* The FxAccountsClient communicates with the remote Firefox
* Accounts auth server. Mock the server calls, with a little
* lag time to simulate some latency.
*
* We add the _verified attribute to mock the change in verification
* state on the FXA server.
*/
@@ -183,38 +172,16 @@ function MakeFxAccounts(internal = {}) {
internal._signOutServer = () => Promise.resolve();
}
if (!internal._registerOrUpdateDevice) {
internal._registerOrUpdateDevice = () => Promise.resolve();
}
return new FxAccounts(internal);
}
-add_task(async function test_non_https_remote_server_uri_with_requireHttps_false() {
- Services.prefs.setBoolPref(
- "identity.fxaccounts.allowHttp",
- true);
- Services.prefs.setCharPref(
- "identity.fxaccounts.remote.signup.uri",
- "http://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
- Assert.equal(await fxAccounts.promiseAccountsSignUpURI(),
- "http://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
-
- Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
- Services.prefs.clearUserPref("identity.fxaccounts.allowHttp");
-});
-
-add_task(async function test_non_https_remote_server_uri() {
- Services.prefs.setCharPref(
- "identity.fxaccounts.remote.signup.uri",
- "http://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
- Assert.rejects(fxAccounts.promiseAccountsSignUpURI(), null, "Firefox Accounts server must use HTTPS");
- Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
-});
-
add_task(async function test_get_signed_in_user_initially_unset() {
_("Check getSignedInUser initially and after signout reports no user");
let account = MakeFxAccounts();
let credentials = {
email: "foo@example.com",
uid: "1234@lcip.org",
assertion: "foobar",
sessionToken: "dead",
new file mode 100644
--- /dev/null
+++ b/services/fxaccounts/tests/xpcshell/test_accounts_config.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/FxAccounts.jsm");
+
+add_task(async function test_non_https_remote_server_uri_with_requireHttps_false() {
+ Services.prefs.setBoolPref(
+ "identity.fxaccounts.allowHttp",
+ true);
+ Services.prefs.setCharPref(
+ "identity.fxaccounts.remote.root",
+ "http://example.com/");
+ Assert.equal(await FxAccounts.config.promiseSignUpURI("test"),
+ "http://example.com/signup?service=sync&context=null&entrypoint=test");
+
+ Services.prefs.clearUserPref("identity.fxaccounts.remote.root");
+ Services.prefs.clearUserPref("identity.fxaccounts.allowHttp");
+});
+
+add_task(async function test_non_https_remote_server_uri() {
+ Services.prefs.setCharPref(
+ "identity.fxaccounts.remote.root",
+ "http://example.com/");
+ Assert.rejects(FxAccounts.config.promiseSignUpURI(), null, "Firefox Accounts server must use HTTPS");
+ Services.prefs.clearUserPref("identity.fxaccounts.remote.root");
+});
+
+function createFakeOldPrefs() {
+ const baseURL = "https://example.com/myfxa/";
+ let createPref = (pref, extraPath) => {
+ Services.prefs.setCharPref(pref, `${baseURL}${extraPath}?service=sync&context=fx_desktop_v3`);
+ };
+ createPref("identity.fxaccounts.remote.signin.uri", "signin");
+ createPref("identity.fxaccounts.remote.signup.uri", "signup");
+ createPref("identity.fxaccounts.remote.email.uri", "email");
+ createPref("identity.fxaccounts.remote.connectdevice.uri", "connect_another_device");
+ createPref("identity.fxaccounts.remote.force_auth.uri", "force_auth");
+ createPref("identity.fxaccounts.settings.uri", "settings");
+ createPref("identity.fxaccounts.settings.devices.uri", "settings/clients");
+ Services.prefs.setCharPref("identity.fxaccounts.remote.webchannel.uri", baseURL);
+}
+
+function checkOldPrefsDeleted() {
+ const migratedPrefs = [
+ "identity.fxaccounts.remote.webchannel.uri",
+ "identity.fxaccounts.settings.uri",
+ "identity.fxaccounts.settings.devices.uri",
+ "identity.fxaccounts.remote.signup.uri",
+ "identity.fxaccounts.remote.signin.uri",
+ "identity.fxaccounts.remote.email.uri",
+ "identity.fxaccounts.remote.connectdevice.uri",
+ "identity.fxaccounts.remote.force_auth.uri",
+ ];
+ for (const pref of migratedPrefs) {
+ Assert.ok(!Services.prefs.prefHasUserValue(pref));
+ }
+}
+
+add_task(async function test_migration_autoconfig() {
+ createFakeOldPrefs();
+ Services.prefs.setCharPref("identity.fxaccounts.autoconfig.uri",
+ "https://example.com/.well-known/fxa-client-configuration");
+ sinon.stub(FxAccounts.config, "fetchConfigURLs");
+ await FxAccounts.config.tryPrefsMigration();
+ Assert.ok(FxAccounts.config.fetchConfigURLs.called);
+ checkOldPrefsDeleted();
+ FxAccounts.config.fetchConfigURLs.restore();
+ Services.prefs.clearUserPref("identity.fxaccounts.autoconfig.uri");
+ Services.prefs.clearUserPref("identity.fxaccounts.remote.root");
+});
+
+add_task(async function test_migration_manual() {
+ createFakeOldPrefs();
+ sinon.stub(FxAccounts.config, "fetchConfigURLs");
+ await FxAccounts.config.tryPrefsMigration();
+ Assert.equal(Services.prefs.getCharPref("identity.fxaccounts.remote.root"),
+ "https://example.com/myfxa/");
+ Assert.ok(!FxAccounts.config.fetchConfigURLs.called);
+ checkOldPrefsDeleted();
+ FxAccounts.config.fetchConfigURLs.restore();
+ Services.prefs.clearUserPref("identity.fxaccounts.remote.root");
+});
--- a/services/fxaccounts/tests/xpcshell/test_accounts_device_registration.js
+++ b/services/fxaccounts/tests/xpcshell/test_accounts_device_registration.js
@@ -15,21 +15,16 @@ var log = Log.repository.getLogger("Serv
log.level = Log.Level.Debug;
const BOGUS_PUBLICKEY = "BBXOKjUb84pzws1wionFpfCBjDuCh4-s_1b52WA46K5wYL2gCWEOmFKWn_NkS5nmJwTBuO8qxxdjAIDtNeklvQc";
const BOGUS_AUTHKEY = "GSsIiaD2Mr83iPqwFNK4rw";
Services.prefs.setCharPref("identity.fxaccounts.loglevel", "Trace");
Log.repository.getLogger("FirefoxAccounts").level = Log.Level.Trace;
-Services.prefs.setCharPref("identity.fxaccounts.remote.oauth.uri", "https://example.com/v1");
-Services.prefs.setCharPref("identity.fxaccounts.oauth.client_id", "abc123");
-Services.prefs.setCharPref("identity.fxaccounts.remote.profile.uri", "http://example.com/v1");
-Services.prefs.setCharPref("identity.fxaccounts.settings.uri", "http://accounts.example.com/");
-
const DEVICE_REGISTRATION_VERSION = 42;
function MockStorageManager() {
}
MockStorageManager.prototype = {
initialize(accountData) {
this.accountData = accountData;
--- a/services/fxaccounts/tests/xpcshell/test_profile.js
+++ b/services/fxaccounts/tests/xpcshell/test_profile.js
@@ -3,60 +3,16 @@
"use strict";
ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js");
ChromeUtils.import("resource://gre/modules/FxAccountsProfileClient.jsm");
ChromeUtils.import("resource://gre/modules/FxAccountsProfile.jsm");
ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
-const URL_STRING = "https://example.com";
-Services.prefs.setCharPref("identity.fxaccounts.settings.uri", "https://example.com/settings");
-
-const STATUS_SUCCESS = 200;
-
-/**
- * Mock request responder
- * @param {String} response
- * Mocked raw response from the server
- * @returns {Function}
- */
-let mockResponse = function(response) {
- let Request = function(requestUri) {
- // Store the request uri so tests can inspect it
- Request._requestUri = requestUri;
- return {
- setHeader() {},
- head() {
- this.response = response;
- this.onComplete();
- }
- };
- };
-
- return Request;
-};
-
-/**
- * Mock request error responder
- * @param {Error} error
- * Error object
- * @returns {Function}
- */
-let mockResponseError = function(error) {
- return function() {
- return {
- setHeader() {},
- head() {
- this.onComplete(error);
- }
- };
- };
-};
-
let mockClient = function(fxa) {
let options = {
serverURL: "http://127.0.0.1:1111/v1",
fxa,
};
return new FxAccountsProfileClient(options);
};
--- a/services/fxaccounts/tests/xpcshell/xpcshell.ini
+++ b/services/fxaccounts/tests/xpcshell/xpcshell.ini
@@ -1,16 +1,17 @@
[DEFAULT]
head = head.js ../../../common/tests/unit/head_helpers.js ../../../common/tests/unit/head_http.js
skip-if = (toolkit == 'android' || appname == 'thunderbird')
support-files =
!/services/common/tests/unit/head_helpers.js
!/services/common/tests/unit/head_http.js
[test_accounts.js]
+[test_accounts_config.js]
[test_accounts_device_registration.js]
[test_client.js]
[test_credentials.js]
[test_loginmgr_storage.js]
[test_oauth_grant_client.js]
[test_oauth_grant_client_server.js]
[test_oauth_tokens.js]
[test_oauth_token_storage.js]
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -263,22 +263,18 @@ user_pref("toolkit.telemetry.firstShutdo
user_pref("toolkit.telemetry.test.pref1", true);
user_pref("toolkit.telemetry.test.pref2", false);
// We don't want to hit the real Firefox Accounts server for tests. We don't
// actually need a functioning FxA server, so just set it to something that
// resolves and accepts requests, even if they all fail.
user_pref("identity.fxaccounts.auth.uri", "https://%(server)s/fxa-dummy/");
-// Ditto for all the other Firefox accounts URIs used for about:accounts et al.:
-user_pref("identity.fxaccounts.remote.signup.uri", "https://%(server)s/fxa-signup");
-user_pref("identity.fxaccounts.remote.force_auth.uri", "https://%(server)s/fxa-force-auth");
-user_pref("identity.fxaccounts.remote.signin.uri", "https://%(server)s/fxa-signin");
-user_pref("identity.fxaccounts.settings.uri", "https://%(server)s/fxa-settings");
-user_pref("identity.fxaccounts.remote.webchannel.uri", "https://%(server)s/");
+// Ditto for all the FxA content root URI.
+user_pref("identity.fxaccounts.remote.root", "https://%(server)s/");
// We don't want browser tests to perform FxA device registration.
user_pref("identity.fxaccounts.skipDeviceRegistration", true);
// Increase the APZ content response timeout in tests to 1 minute.
// This is to accommodate the fact that test environments tends to be slower
// than production environments (with the b2g emulator being the slowest of them
// all), resulting in the production timeout value sometimes being exceeded