Bug 1522253: about:newinstall should get its configuration via RemotePageManager rather than query params. r=Gijs
☠☠ backed out by 8df956c34c94 ☠ ☠
authorDave Townsend <dtownsend@oxymoronical.com>
Wed, 13 Feb 2019 16:57:35 +0000
changeset 458918 5b2aeaf2da17
parent 458917 a091f6b58aba
child 458919 fa484096ffd3
push id35551
push usershindli@mozilla.com
push dateWed, 13 Feb 2019 21:34:09 +0000
treeherdermozilla-central@08f794a4928e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1522253
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1522253: about:newinstall should get its configuration via RemotePageManager rather than query params. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D19413
browser/app/profile/firefox.js
browser/base/content/newInstallPage.js
browser/components/nsBrowserContentHandler.js
toolkit/components/remotepagemanager/MessagePort.jsm
toolkit/components/remotepagemanager/RemotePageManagerChild.jsm
toolkit/components/remotepagemanager/RemotePageManagerParent.jsm
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -232,18 +232,16 @@ pref("browser.defaultbrowser.notificatio
 
 // 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
 // The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
 pref("browser.startup.page",                1);
 pref("browser.startup.homepage",            "about:home");
 // Whether we should skip the homepage when opening the first-run page
 pref("browser.startup.firstrunSkipsHomepage", true);
 
-pref("browser.dedicatedprofile.welcome.accounts.endpoint", "https://accounts.firefox.com/");
-
 // Show an about:blank window as early as possible for quick startup feedback.
 // Held to nightly on Linux due to bug 1450626.
 // Disabled on Mac because the bouncing dock icon already provides feedback.
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) && defined(NIGHTLY_BUILD)
 pref("browser.startup.blankWindow", true);
 #else
 pref("browser.startup.blankWindow", false);
 #endif
--- a/browser/base/content/newInstallPage.js
+++ b/browser/base/content/newInstallPage.js
@@ -1,35 +1,35 @@
 /* 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/. */
 
 const PARAMS = new URL(location).searchParams;
 const ENTRYPOINT = "new-install-page";
-const SOURCE = `new-install-page-${PARAMS.get("channel")}`;
+const SOURCE = `new-install-page-${RPMGetUpdateChannel()}`;
 const CAMPAIGN = "dedicated-profiles";
 const ENDPOINT = PARAMS.get("endpoint");
-const CONTEXT = "fx_desktop_v3";
 
 function appendAccountsParams(url) {
   url.searchParams.set("entrypoint", ENTRYPOINT);
   url.searchParams.set("utm_source", SOURCE);
   url.searchParams.set("utm_campaign", CAMPAIGN);
 }
 
 function appendParams(url, params) {
   appendAccountsParams(url);
 
   for (let [key, value] of Object.entries(params)) {
     url.searchParams.set(key, value);
   }
 }
 
 async function requestFlowMetrics() {
-  let requestURL = new URL(`${ENDPOINT}metrics-flow`);
+  let requestURL = new URL(await endpoint);
+  requestURL.pathname = "metrics-flow";
   appendParams(requestURL, {
     "form_type": "email",
   });
 
   let response = await fetch(requestURL, { credentials: "omit" });
   if (response.status === 200) {
     return response.json();
   }
@@ -42,28 +42,28 @@ async function submitForm(event) {
   event.preventDefault();
 
   let input = document.getElementById("sync-input");
   input.disabled = true;
   document.getElementById("sync-button").disabled = true;
 
   let { flowId, flowBeginTime } = await metrics;
 
-  let requestURL = new URL(ENDPOINT);
+  let requestURL = new URL(await endpoint);
   appendParams(requestURL, {
-    "service": "sync",
     "action": "email",
-    "context": CONTEXT,
     "utm_campaign": CAMPAIGN,
     "email": input.value,
     "flow_id": flowId,
     "flow_begin_time": flowBeginTime,
   });
 
   window.open(requestURL, "_blank", "noopener");
 }
 
+const endpoint = RPMGetFxAccountsEndpoint(ENTRYPOINT);
+
 // This must come before the CSP is set or it will be blocked.
 const metrics = requestFlowMetrics();
 
 document.addEventListener("DOMContentLoaded", () => {
   document.getElementById("sync").addEventListener("submit", submitForm);
 }, { once: true });
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -10,24 +10,27 @@ XPCOMUtils.defineLazyModuleGetters(this,
   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   HeadlessShell: "resource:///modules/HeadlessShell.jsm",
   HomePage: "resource:///modules/HomePage.jsm",
   LaterRun: "resource:///modules/LaterRun.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
   ShellService: "resource:///modules/ShellService.jsm",
   UpdatePing: "resource://gre/modules/UpdatePing.jsm",
+  RemotePages: "resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm",
 });
 XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
   "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
 
 XPCOMUtils.defineLazyGetter(this, "gSystemPrincipal",
   () => Services.scriptSecurityManager.getSystemPrincipal());
 XPCOMUtils.defineLazyGlobalGetters(this, [URL]);
 
+const NEWINSTALL_PAGE = "about:newinstall";
+
 function shouldLoadURI(aURI) {
   if (aURI && !aURI.schemeIs("chrome"))
     return true;
 
   dump("*** Preventing external load of chrome: URI into browser window\n");
   dump("    Use --chrome <uri> instead\n");
   return false;
 }
@@ -55,22 +58,24 @@ function resolveURIInternal(aCmdLine, aA
     uri = uriFixup.createFixupURI(aArgument, 0);
   } catch (e) {
     Cu.reportError(e);
   }
 
   return uri;
 }
 
+let gRemoteInstallPage = null;
+
 function getNewInstallPage() {
-  let url = new URL("about:newinstall");
-  let endpoint = Services.prefs.getCharPref("browser.dedicatedprofile.welcome.accounts.endpoint");
-  url.searchParams.set("endpoint", endpoint);
-  url.searchParams.set("channel", AppConstants.MOZ_UPDATE_CHANNEL);
-  return url.toString();
+  if (!gRemoteInstallPage) {
+    gRemoteInstallPage = new RemotePages(NEWINSTALL_PAGE);
+  }
+
+  return NEWINSTALL_PAGE;
 }
 
 var gFirstWindow = false;
 
 const OVERRIDE_NONE        = 0;
 const OVERRIDE_NEW_PROFILE = 1;
 const OVERRIDE_NEW_MSTONE  = 2;
 const OVERRIDE_NEW_BUILD_ID = 3;
--- a/toolkit/components/remotepagemanager/MessagePort.jsm
+++ b/toolkit/components/remotepagemanager/MessagePort.jsm
@@ -8,16 +8,18 @@ var EXPORTED_SYMBOLS = ["MessagePort", "
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "AsyncPrefs",
   "resource://gre/modules/AsyncPrefs.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PromiseUtils",
   "resource://gre/modules/PromiseUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "UpdateUtils",
+  "resource://gre/modules/UpdateUtils.jsm");
 
 /*
  * Used for all kinds of permissions checks which requires explicit
  * whitelisting of specific permissions granted through RPM.
  *
  * Please note that prefs that one wants to update need to be
  * whitelisted within AsyncPrefs.jsm.
  */
@@ -28,16 +30,20 @@ let RPMAccessManager = {
       // "setBoolPref": handled within AsyncPrefs.jsm and uses the pref
       //                ["privacy.trackingprotection.pbmode.enabled"],
       "getBoolPref": ["privacy.trackingprotection.pbmode.enabled",
                       "browser.privatebrowsing.searchUI"],
       "getFormatURLPref": ["privacy.trackingprotection.introURL",
                            "app.support.baseURL"],
       "isWindowPrivate": ["yes"],
     },
+    "about:newinstall": {
+      "getUpdateChannel": ["yes"],
+      "getFxAccountsEndpoint": ["yes"],
+    },
   },
 
   checkAllowAccess(aPrincipal, aFeature, aValue) {
     // if there is no content principal; deny access
     if (!aPrincipal || !aPrincipal.URI) {
       return false;
     }
     let uri = aPrincipal.URI.asciiSpec;
@@ -158,30 +164,30 @@ class MessagePort {
     this.messageManager = messageManager;
     this.addMessageListeners();
   }
 
   // Sends a request to the other process and returns a promise that completes
   // once the other process has responded to the request or some error occurs.
   sendRequest(name, data = null) {
     if (this.destroyed) {
-      return Promise.reject(new Error("Message port has been destroyed"));
+      return this.window.Promise.reject(new Error("Message port has been destroyed"));
     }
 
     let deferred = PromiseUtils.defer();
     this.requests.push(deferred);
 
     this.messageManager.sendAsyncMessage("RemotePage:Request", {
       portID: this.portID,
       requestID: this.requests.length - 1,
       name,
       data,
     });
 
-    return deferred.promise;
+    return this.wrapPromise(deferred.promise);
   }
 
   // Handles an IPC message to perform a request of some kind.
   async receiveRequest({ data: messagedata }) {
     if (this.destroyed || (messagedata.portID != this.portID)) {
       return;
     }
 
@@ -286,30 +292,30 @@ class MessagePort {
 
     this.messageManager = null;
     this.destroyed = true;
     this.portID = null;
     this.listener = null;
     this.requests = [];
   }
 
+  wrapPromise(promise) {
+    return new this.window.Promise((resolve, reject) => promise.then(resolve, reject));
+  }
+
   getBoolPref(aPref) {
     let principal = this.window.document.nodePrincipal;
     if (!RPMAccessManager.checkAllowAccess(principal, "getBoolPref", aPref)) {
       throw new Error("RPMAccessManager does not allow access to getBoolPref");
     }
     return Services.prefs.getBoolPref(aPref);
   }
 
   setBoolPref(aPref, aVal) {
-    return new this.window.Promise(function(resolve) {
-      AsyncPrefs.set(aPref, aVal).then(function() {
-        resolve();
-      });
-    });
+    return this.wrapPromise(AsyncPrefs.set(aPref, aVal));
   }
 
   getFormatURLPref(aFormatURL) {
     let principal = this.window.document.nodePrincipal;
     if (!RPMAccessManager.checkAllowAccess(principal, "getFormatURLPref", aFormatURL)) {
       throw new Error("RPMAccessManager does not allow access to getFormatURLPref");
     }
     return Services.urlFormatter.formatURLPref(aFormatURL);
@@ -317,9 +323,26 @@ class MessagePort {
 
   isWindowPrivate() {
     let principal = this.window.document.nodePrincipal;
     if (!RPMAccessManager.checkAllowAccess(principal, "isWindowPrivate", "yes")) {
       throw new Error("RPMAccessManager does not allow access to isWindowPrivate");
     }
     return PrivateBrowsingUtils.isContentWindowPrivate(this.window);
   }
+
+  getUpdateChannel() {
+    let principal = this.window.document.nodePrincipal;
+    if (!RPMAccessManager.checkAllowAccess(principal, "getUpdateChannel", "yes")) {
+      throw new Error("RPMAccessManager does not allow access to getUpdateChannel");
+    }
+    return UpdateUtils.UpdateChannel;
+  }
+
+  getFxAccountsEndpoint(aEntrypoint) {
+    let principal = this.window.document.nodePrincipal;
+    if (!RPMAccessManager.checkAllowAccess(principal, "getFxAccountsEndpoint", "yes")) {
+      throw new Error("RPMAccessManager does not allow access to getFxAccountsEndpoint");
+    }
+
+    return this.sendRequest("FxAccountsEndpoint", aEntrypoint);
+  }
 }
--- a/toolkit/components/remotepagemanager/RemotePageManagerChild.jsm
+++ b/toolkit/components/remotepagemanager/RemotePageManagerChild.jsm
@@ -36,16 +36,22 @@ class ChildMessagePort extends MessagePo
       defineAs: "RPMSetBoolPref",
     });
     Cu.exportFunction(this.getFormatURLPref.bind(this), window, {
       defineAs: "RPMGetFormatURLPref",
     });
     Cu.exportFunction(this.isWindowPrivate.bind(this), window, {
       defineAs: "RPMIsWindowPrivate",
     });
+    Cu.exportFunction(this.getUpdateChannel.bind(this), window, {
+      defineAs: "RPMGetUpdateChannel",
+    });
+    Cu.exportFunction(this.getFxAccountsEndpoint.bind(this), window, {
+      defineAs: "RPMGetFxAccountsEndpoint",
+    });
 
     // Send a message for load events
     let loadListener = () => {
       this.sendAsyncMessage("RemotePage:Load");
       window.removeEventListener("load", loadListener);
     };
     window.addEventListener("load", loadListener);
 
--- a/toolkit/components/remotepagemanager/RemotePageManagerParent.jsm
+++ b/toolkit/components/remotepagemanager/RemotePageManagerParent.jsm
@@ -16,16 +16,18 @@ var EXPORTED_SYMBOLS = ["RemotePages", "
  * * When adding a new consumer of RPM that relies on other functionality
  *   then simple message passing provided by the RPM, then one has to
  *   whitelist permissions for the new URI within the RPMAccessManager
  *   from MessagePort.jsm.
  */
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const {MessageListener, MessagePort} = ChromeUtils.import("resource://gre/modules/remotepagemanager/MessagePort.jsm");
+ChromeUtils.defineModuleGetter(this, "FxAccounts",
+  "resource://gre/modules/FxAccounts.jsm");
 
 /**
  * Creates a RemotePages object which listens for new remote pages of some
  * particular URLs. A "RemotePage:Init" message will be dispatched to this
  * object for every page loaded. Message listeners added to this object receive
  * messages from all loaded pages from the requested urls.
  */
 class RemotePages {
@@ -231,16 +233,20 @@ class ChromeMessagePort extends MessageP
       name: "RemotePage:Unload",
       data: null,
     });
     this.destroy();
   }
 
   // Called when the content process is requesting some data.
   async handleRequest(name, data) {
+    if (name == "FxAccountsEndpoint") {
+      return FxAccounts.config.promiseEmailFirstURI(data);
+    }
+
     throw new Error(`Unknown request ${name}.`);
   }
 
   // Called when a message is received from the message manager.
   handleMessage(messagedata) {
     let message = {
       target: this.publicPort,
       name: messagedata.name,