Bug 1464990 - Allow easier testing Switchboard experiments; r?sebastian,jchen draft
authorPetru Lingurar <petru.lingurar@softvision.ro>
Thu, 21 Jun 2018 09:33:20 +0300
changeset 809085 0d03ca9c61ddecad2a84332688381aa598fa95b4
parent 808264 1e2c9151a09e43613a79daa8d4a94dc3e314020c
push id113542
push userplingurar@mozilla.com
push dateThu, 21 Jun 2018 06:34:26 +0000
reviewerssebastian, jchen
bugs1464990
milestone62.0a1
Bug 1464990 - Allow easier testing Switchboard experiments; r?sebastian,jchen Based on Sebastian's addon - https://github.com/pocmo/Addon-Switchboard-Experiments, this will allow to easily enable / disable Switchboard experiments, process that after Firefox 57 and the obsolescence of the addon was too cumbersome. MozReview-Commit-ID: 2EkYQ42Bd8B
mobile/android/chrome/content/aboutExperiments.js
mobile/android/chrome/content/aboutExperiments.xhtml
mobile/android/chrome/content/browser.js
mobile/android/chrome/jar.mn
mobile/android/components/AboutRedirector.js
mobile/android/components/MobileComponents.manifest
mobile/android/themes/core/aboutExperiments.css
mobile/android/themes/core/jar.mn
new file mode 100644
--- /dev/null
+++ b/mobile/android/chrome/content/aboutExperiments.js
@@ -0,0 +1,113 @@
+/* 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";
+
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AndroidLog: "resource://gre/modules/AndroidLog.jsm",
+  EventDispatcher: "resource://gre/modules/Messaging.jsm",
+});
+
+const LOGTAG = "Experiments";
+const EXPERIMENTS_CONFIGURATION = "https://firefox.settings.services.mozilla.com/v1/buckets/fennec/collections/experiments/records";
+const Experiments = Services.wm.getMostRecentWindow("navigator:browser").Experiments;
+
+document.addEventListener("DOMContentLoaded", initList);
+
+function log(msg) {
+  AndroidLog.d(LOGTAG, msg);
+}
+
+function initList() {
+  const list = document.getElementById("list");
+  list.addEventListener("click", toggleOverride);
+
+  Promise.all([promiseEnabledExperiments(), promiseExperimentsConfiguration()]).then(values => {
+    const enabledExperiments = values[0];
+    const serverConfiguration = values[1];
+
+    serverConfiguration.data.forEach(function(experiment) {
+      try {
+        let item = document.createElement("li");
+        item.textContent = experiment.name;
+        item.setAttribute("name", experiment.name);
+        item.setAttribute("isEnabled", enabledExperiments.includes(experiment.name));
+        list.appendChild(item);
+      } catch (e) {
+          log(`Error while setting experiments list: ${e.error}`);
+      }
+    });
+  });
+}
+
+function toggleOverride(experiment) {
+  const item = experiment.originalTarget;
+  const name = item.getAttribute("name");
+  const isEnabled = item.getAttribute("isEnabled") === "true";
+
+  log(`toggleOverride: ${name}`);
+
+  Experiments.setOverride(name, !isEnabled);
+  item.setAttribute("isEnabled", !isEnabled);
+}
+
+/**
+ * Get the list of locally enabled experiments.
+ */
+function promiseEnabledExperiments() {
+  log("Getting the locally enabled experiments");
+
+  return EventDispatcher.instance.sendRequestForResult({
+    type: "Experiments:GetActive"
+  }).then(experiments => {
+    log("List of locally enabled experiments ready");
+    return experiments;
+  });
+}
+
+/**
+ * Fetch the list of experiments from server configuration.
+ */
+function promiseExperimentsConfiguration() {
+  log("Fetching server experiments");
+
+  return new Promise((resolve, reject) => {
+    const xhr = new XMLHttpRequest();
+
+    try {
+      xhr.open("GET", EXPERIMENTS_CONFIGURATION, true);
+    } catch (e) {
+      reject(`Error opening request: ${e}`);
+      return;
+    }
+
+    xhr.onerror = function(e) {
+      reject(`Error making request: ${e.error}`);
+    };
+
+    xhr.onload = function(event) {
+      if (xhr.readyState === 4) {
+        if (xhr.status === 200) {
+          try {
+            resolve(JSON.parse(xhr.responseText));
+          } catch (e) {
+            const errorMessage = `Error while parsing request: ${e}`;
+            log(errorMessage);
+            reject(errorMessage);
+          }
+        } else {
+          const errorMessage = `Request to ${url} returned status ${xhr.status}`;
+          log(errorMessage);
+          reject(errorMessage);
+        }
+      }
+      log("Finished fetching server experiments");
+    };
+
+    xhr.send(null);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/chrome/content/aboutExperiments.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>Switchboard Experiments</title>
+    <meta name="viewport" content="width=device-width; user-scalable=0" />
+    <link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
+    <link rel="stylesheet" href="chrome://browser/skin/aboutExperiments.css" type="text/css"/>
+    <script type="application/javascript" src="chrome://browser/content/aboutExperiments.js"></script>
+  </head>
+
+  <body>
+    <ul id="list"/>
+  </body>
+</html>
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5622,17 +5622,17 @@ var IdentityHandler = {
     if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       return this.IDENTITY_MODE_VERIFIED;
     }
 
     if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE) {
       return this.IDENTITY_MODE_IDENTIFIED;
     }
 
-    let whitelist = /^about:(about|accounts|addons|buildconfig|cache|config|crashes|devices|downloads|fennec|firefox|feedback|home|license|logins|logo|memory|mozilla|networking|privatebrowsing|rights|serviceworkers|support|telemetry|webrtc)($|\?)/i;
+    let whitelist = /^about:(about|accounts|addons|buildconfig|cache|config|crashes|devices|downloads|experiments|fennec|firefox|feedback|home|license|logins|logo|memory|mozilla|networking|privatebrowsing|rights|serviceworkers|support|telemetry|webrtc)($|\?)/i;
     if (uri.schemeIs("about") && whitelist.test(uri.spec)) {
         return this.IDENTITY_MODE_CHROMEUI;
     }
 
     return this.IDENTITY_MODE_UNKNOWN;
   },
 
   getMixedDisplayMode: function getMixedDisplayMode(aState) {
--- a/mobile/android/chrome/jar.mn
+++ b/mobile/android/chrome/jar.mn
@@ -40,16 +40,18 @@ chrome.jar:
   content/PermissionsHelper.js         (content/PermissionsHelper.js)
   content/FeedHandler.js               (content/FeedHandler.js)
   content/Feedback.js                  (content/Feedback.js)
   content/Linkify.js                   (content/Linkify.js)
   content/CastingApps.js               (content/CastingApps.js)
   content/RemoteDebugger.js            (content/RemoteDebugger.js)
   content/aboutAccounts.xhtml          (content/aboutAccounts.xhtml)
   content/aboutAccounts.js             (content/aboutAccounts.js)
+  content/aboutExperiments.xhtml       (content/aboutExperiments.xhtml)
+  content/aboutExperiments.js          (content/aboutExperiments.js)
   content/aboutLogins.xhtml            (content/aboutLogins.xhtml)
   content/aboutLogins.js               (content/aboutLogins.js)
 #ifndef RELEASE_OR_BETA
   content/WebcompatReporter.js         (content/WebcompatReporter.js)
 #endif
   content/ExtensionPermissions.js      (content/ExtensionPermissions.js)
 
 % content branding %content/branding/
--- a/mobile/android/components/AboutRedirector.js
+++ b/mobile/android/components/AboutRedirector.js
@@ -62,16 +62,21 @@ var modules = {
   logins: {
     uri: "chrome://browser/content/aboutLogins.xhtml",
     privileged: true
   },
   accounts: {
     uri: "chrome://browser/content/aboutAccounts.xhtml",
     privileged: true
   },
+  experiments: {
+    uri: "chrome://browser/content/aboutExperiments.xhtml",
+    privileged: true,
+    hide: true
+  },
 };
 
 function AboutRedirector() {}
 AboutRedirector.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
   classID: Components.ID("{322ba47e-7047-4f71-aebf-cb7d69325cd9}"),
 
   _getModuleInfo: function(aURI) {
--- a/mobile/android/components/MobileComponents.manifest
+++ b/mobile/android/components/MobileComponents.manifest
@@ -9,16 +9,17 @@ contract @mozilla.org/network/protocol/a
 contract @mozilla.org/network/protocol/about;1?what=home {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=downloads {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=reader {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=feedback {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=privatebrowsing {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=blocked {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=accounts {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 contract @mozilla.org/network/protocol/about;1?what=logins {322ba47e-7047-4f71-aebf-cb7d69325cd9}
+contract @mozilla.org/network/protocol/about;1?what=experiments {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 
 # DirectoryProvider.js
 component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js
 contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}
 category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
 
 # stylesheets
 category agent-style-sheets browser-content-stylesheet chrome://geckoview/skin/content.css
new file mode 100644
--- /dev/null
+++ b/mobile/android/themes/core/aboutExperiments.css
@@ -0,0 +1,19 @@
+/* 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/. */
+
+ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+li {
+  padding: 25px;
+  margin-bottom: 5px;
+}
+li[isEnabled="true"] {
+  background-color: #c5e1a5;
+}
+li[isEnabled="false"] {
+  background-color: #ef9a9a;
+}
\ No newline at end of file
--- a/mobile/android/themes/core/jar.mn
+++ b/mobile/android/themes/core/jar.mn
@@ -7,16 +7,17 @@
 chrome.jar:
 % skin browser classic/1.0 %skin/
   skin/aboutPage.css                        (aboutPage.css)
   skin/about.css                            (about.css)
   skin/aboutAccounts.css                    (aboutAccounts.css)
   skin/aboutAddons.css                      (aboutAddons.css)
   skin/aboutBase.css                        (aboutBase.css)
   skin/aboutDownloads.css                   (aboutDownloads.css)
+  skin/aboutExperiments.css                 (aboutExperiments.css)
   skin/aboutMemory.css                      (aboutMemory.css)
   skin/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
   skin/aboutReader.css                      (aboutReader.css)
   skin/aboutSupport.css                     (aboutSupport.css)
   skin/config.css                           (config.css)
   skin/defines.css                          (defines.css)
   skin/netError.css                         (netError.css)
   skin/spinner.css                          (spinner.css)