Bug 1298950 - Add support for overriding about:home to chrome_url_overrides r?mixedpuppy,aswan draft
authorMatthew Wein <mwein@mozilla.com>
Mon, 13 Feb 2017 11:36:38 +0000
changeset 483481 7eff97b4ec37f2d5832e34c25a249a8eba25e6c8
parent 483291 195049fabb7ac5709e5f75614ba630ba3d1b5a9b
child 545653 a431ab9188045381337968bac5ec2edfe1384baf
push id45326
push usermwein@mozilla.com
push dateTue, 14 Feb 2017 11:13:12 +0000
reviewersmixedpuppy, aswan
bugs1298950
milestone54.0a1
Bug 1298950 - Add support for overriding about:home to chrome_url_overrides r?mixedpuppy,aswan MozReview-Commit-ID: 4Bc2F0W7WqV
browser/components/extensions/ext-url-overrides.js
browser/components/extensions/schemas/url_overrides.json
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_url_overrides.js
browser/components/extensions/test/browser/browser_ext_url_overrides_all.js
browser/components/extensions/test/browser/browser_ext_url_overrides_home.js
browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
--- a/browser/components/extensions/ext-url-overrides.js
+++ b/browser/components/extensions/ext-url-overrides.js
@@ -3,47 +3,102 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+                                  "resource://gre/modules/Preferences.jsm");
 
 // Bug 1320736 tracks creating a generic precedence manager for handling
 // multiple addons modifying the same properties, and bug 1330494 has been filed
 // to track utilizing this manager for chrome_url_overrides. Until those land,
 // the edge cases surrounding multiple addons using chrome_url_overrides will
 // be ignored and precedence will be first come, first serve.
 let overrides = {
   // A queue of extensions in line to override the newtab page (sorted oldest to newest).
   newtab: [],
+  // A queue of extensions in line to override the home page (sorted oldest to newest).
+  home: [],
 };
 
+/**
+ * Resets the specified page to its default value.
+ *
+ * @param {string} page The page to override. Accepted values are "newtab" and "home".
+ */
+function resetPage(page) {
+  switch (page) {
+    case "newtab":
+      aboutNewTabService.resetNewTabURL();
+      break;
+    case "home":
+      Preferences.reset("browser.startup.homepage");
+      break;
+    default:
+      throw new Error("Unrecognized override type");
+  }
+}
+
+/**
+ * Overrides the specified page to the specified URL.
+ *
+ * @param {string} page The page to override. Accepted values are "newtab" and "home".
+ * @param {string} url The resolved URL to use for the page override.
+ */
+function overridePage(page, url) {
+  switch (page) {
+    case "newtab":
+      aboutNewTabService.newTabURL = url;
+      break;
+    case "home":
+      Preferences.set("browser.startup.homepage", url);
+      break;
+    default:
+      throw new Error("Unrecognized override type");
+  }
+}
+
+/**
+ * Updates the page to the URL specified by the extension next in line. If no extensions
+ * are in line, the page is reset to its default value.
+ *
+ * @param {string} page The page to override.
+ */
+function updatePage(page) {
+  if (overrides[page].length) {
+    overridePage(page, overrides[page][0].url);
+  } else {
+    resetPage(page);
+  }
+}
+
 /* eslint-disable mozilla/balanced-listeners */
 extensions.on("manifest_chrome_url_overrides", (type, directive, extension, manifest) => {
-  if (manifest.chrome_url_overrides.newtab) {
-    let newtab = manifest.chrome_url_overrides.newtab;
-    let url = extension.baseURI.resolve(newtab);
+  if (Object.keys(overrides).length > 1) {
+    extension.manifestError("Extensions can override only one page.");
+  }
 
-    // Only set the newtab URL if no other extension is overriding it.
-    if (!overrides.newtab.length) {
-      aboutNewTabService.newTabURL = url;
+  for (let page of Object.keys(overrides)) {
+    if (manifest.chrome_url_overrides[page]) {
+      let relativeURL = manifest.chrome_url_overrides[page];
+      let url = extension.baseURI.resolve(relativeURL);
+      // Store the extension ID instead of a hard reference to the extension.
+      overrides[page].push({id: extension.id, url});
+      updatePage(page);
+      break;
     }
-
-    overrides.newtab.push({extension, url});
   }
 });
 
 extensions.on("shutdown", (type, extension) => {
-  let i = overrides.newtab.findIndex(o => o.extension === extension);
-  if (i !== -1) {
-    overrides.newtab.splice(i, 1);
-
-    if (overrides.newtab.length) {
-      aboutNewTabService.newTabURL = overrides.newtab[0].url;
-    } else {
-      aboutNewTabService.resetNewTabURL();
+  for (let page of Object.keys(overrides)) {
+    let i = overrides[page].findIndex(o => o.id === extension.id);
+    if (i !== -1) {
+      overrides[page].splice(i, 1);
+      updatePage(page);
     }
   }
 });
 /* eslint-enable mozilla/balanced-listeners */
--- a/browser/components/extensions/schemas/url_overrides.json
+++ b/browser/components/extensions/schemas/url_overrides.json
@@ -9,16 +9,21 @@
             "type": "object",
             "optional": true,
             "properties": {
               "newtab": {
                 "$ref": "ExtensionURL",
                 "optional": true,
                 "preprocess": "localize"
               },
+              "home": {
+                "$ref": "ExtensionURL",
+                "optional": true,
+                "preprocess": "localize"
+              },
               "bookmarks": {
                 "unsupported": true,
                 "$ref": "ExtensionURL",
                 "optional": true,
                 "preprocess": "localize"
               },
               "history": {
                 "unsupported": true,
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -109,17 +109,19 @@ support-files =
 [browser_ext_tabs_cookieStoreId.js]
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_zoom.js]
 [browser_ext_tabs_update_url.js]
 [browser_ext_themes_chromeparity.js]
 [browser_ext_themes_dynamic_updates.js]
 [browser_ext_themes_lwtsupport.js]
 [browser_ext_topwindowid.js]
-[browser_ext_url_overrides.js]
+[browser_ext_url_overrides_all.js]
+[browser_ext_url_overrides_home.js]
+[browser_ext_url_overrides_newtab.js]
 [browser_ext_webRequest.js]
 [browser_ext_webNavigation_frameId0.js]
 [browser_ext_webNavigation_getFrames.js]
 [browser_ext_webNavigation_urlbar_transitions.js]
 [browser_ext_windows.js]
 [browser_ext_windows_create.js]
 tags = fullscreen
 [browser_ext_windows_create_params.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_all.js
@@ -0,0 +1,97 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+
+"use strict";
+
+XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
+                                   "@mozilla.org/browser/aboutnewtab-service;1",
+                                   "nsIAboutNewTabService");
+
+const NEWTAB_URI = "webext-newtab.html";
+const HOME_URI = "webext-home.html";
+
+add_task(function* test_extensions_overriding_different_pages() {
+  let defaultHomePage = Preferences.get("browser.startup.homepage");
+  let defaultNewtabPage = aboutNewTabService.newTabURL;
+
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Default home url should be ${defaultHomePage}`);
+  is(aboutNewTabService.newTabURL, defaultNewtabPage,
+    `Default newtab url should be ${defaultNewtabPage}`);
+
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {}},
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {newtab: NEWTAB_URI}},
+  });
+
+  let ext3 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {home: HOME_URI}},
+  });
+
+  yield ext1.startup();
+
+  is(aboutNewTabService.newTabURL, defaultNewtabPage,
+    `Default newtab url should still be ${defaultNewtabPage}`);
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Default home url should be ${defaultHomePage}`);
+
+  yield ext2.startup();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI),
+    "Newtab url should be overriden by the second extension.");
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Default home url should be ${defaultHomePage}`);
+
+  yield ext1.unload();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI),
+    "Newtab url should still be overriden by the second extension.");
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Default home url should be ${defaultHomePage}`);
+
+  yield ext3.startup();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI),
+    "Newtab url should still be overriden by the second extension.");
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI),
+    "Home url should be overriden by the third extension.");
+
+  yield ext2.unload();
+
+  is(aboutNewTabService.newTabURL, defaultNewtabPage,
+    `Newtab url should be reset to ${defaultNewtabPage}`);
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI),
+    "Home url should still be overriden by the third extension.");
+
+  yield ext3.unload();
+
+  is(aboutNewTabService.newTabURL, defaultNewtabPage,
+    `Newtab url should be reset to ${defaultNewtabPage}`);
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Home url should be reset to ${defaultHomePage}`);
+});
+
+add_task(function* test_extensions_with_multiple_overrides() {
+  let ext = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {
+      newtab: NEWTAB_URI,
+      home: HOME_URI,
+    }},
+  });
+
+  SimpleTest.waitForExplicitFinish();
+  let waitForConsole = new Promise(resolve => {
+    SimpleTest.monitorConsole(resolve, [{
+      message: /Extensions can override only one page./,
+    }]);
+  });
+
+  yield ext.startup();
+  yield ext.unload();
+
+  SimpleTest.endMonitorConsole();
+  yield waitForConsole;
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_home.js
@@ -0,0 +1,74 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+                                  "resource://gre/modules/Preferences.jsm");
+
+const HOME_URI_1 = "webext-home-1.html";
+const HOME_URI_2 = "webext-home-2.html";
+const HOME_URI_3 = "webext-home-3.html";
+
+add_task(function* test_multiple_extensions_overriding_newtab_page() {
+  let defaultHomePage = Preferences.get("browser.startup.homepage");
+
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+     `Default home url should be ${defaultHomePage}`);
+
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {}},
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {home: HOME_URI_1}},
+  });
+
+  let ext3 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {home: HOME_URI_2}},
+  });
+
+  let ext4 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {home: HOME_URI_3}},
+  });
+
+  yield ext1.startup();
+
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+       `Default home url should still be ${defaultHomePage}`);
+
+  yield ext2.startup();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_1),
+     "Home url should be overriden by the second extension.");
+
+  yield ext1.unload();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_1),
+     "Home url should still be overriden by the second extension.");
+
+  yield ext3.startup();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_1),
+     "Home url should still be overriden by the second extension.");
+
+  yield ext2.unload();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_2),
+     "Home url should be overriden by the third extension.");
+
+  yield ext4.startup();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_2),
+     "Home url should be overriden by the third extension.");
+
+  yield ext4.unload();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_2),
+     "Home url should be overriden by the third extension.");
+
+  yield ext3.unload();
+
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+     `Home url should be reset to ${defaultHomePage}`);
+});
rename from browser/components/extensions/test/browser/browser_ext_url_overrides.js
rename to browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
--- a/browser/components/extensions/test/browser/browser_ext_url_overrides.js
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
@@ -71,21 +71,21 @@ add_task(function* test_multiple_extensi
   is(aboutNewTabService.newTabURL, "about:newtab",
      "Newtab url should be reset to about:newtab");
 });
 
 add_task(function* test_sending_message_from_newtab_page() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "chrome_url_overrides": {
-        newtab: NEWTAB_URI_2,
+        newtab: NEWTAB_URI_1,
       },
     },
     files: {
-      [NEWTAB_URI_2]: `
+      [NEWTAB_URI_1]: `
         <!DOCTYPE html>
         <head>
           <meta charset="utf-8"/></head>
         <html>
           <body>
             <script src="newtab.js"></script>
           </body>
         </html>