Bug 1234150 - Support overriding "about:newtab" using chrome_url_overrides. r=aswan,mixedpuppy
authorMatthew Wein <mwein@mozilla.com>
Tue, 10 Jan 2017 14:24:50 -0500
changeset 330741 485014423bd9
parent 330740 0723b0ecb8b9
child 330742 96d0d062d978
push id36398
push userryanvm@gmail.com
push date2017-01-24 03:27 +0000
treeherderautoland@485014423bd9 [default view] [failures only]
reviewersaswan, mixedpuppy
bugs1234150
milestone54.0a1
Bug 1234150 - Support overriding "about:newtab" using chrome_url_overrides. r=aswan,mixedpuppy

MozReview-Commit-ID: 4psqXfT1w2p
browser/components/extensions/ext-url-overrides.js
browser/components/extensions/extensions-browser.manifest
browser/components/extensions/jar.mn
browser/components/extensions/schemas/jar.mn
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
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-url-overrides.js
@@ -0,0 +1,49 @@
+/* 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";
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
+                                   "@mozilla.org/browser/aboutnewtab-service;1",
+                                   "nsIAboutNewTabService");
+
+// 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: [],
+};
+
+/* 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);
+
+    // Only set the newtab URL if no other extension is overriding it.
+    if (!overrides.newtab.length) {
+      aboutNewTabService.newTabURL = url;
+    }
+
+    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();
+    }
+  }
+});
+/* eslint-enable mozilla/balanced-listeners */
--- a/browser/components/extensions/extensions-browser.manifest
+++ b/browser/components/extensions/extensions-browser.manifest
@@ -7,16 +7,17 @@ category webextension-scripts contextMen
 category webextension-scripts desktop-runtime chrome://browser/content/ext-desktop-runtime.js
 category webextension-scripts devtools chrome://browser/content/ext-devtools.js
 category webextension-scripts history chrome://browser/content/ext-history.js
 category webextension-scripts omnibox chrome://browser/content/ext-omnibox.js
 category webextension-scripts pageAction chrome://browser/content/ext-pageAction.js
 category webextension-scripts sessions chrome://browser/content/ext-sessions.js
 category webextension-scripts tabs chrome://browser/content/ext-tabs.js
 category webextension-scripts theme chrome://browser/content/ext-theme.js
+category webextension-scripts url-overrides chrome://browser/content/ext-url-overrides.js
 category webextension-scripts utils chrome://browser/content/ext-utils.js
 category webextension-scripts windows chrome://browser/content/ext-windows.js
 
 # scripts specific for devtools extension contexts.
 category webextension-scripts-devtools devtools-inspectedWindow chrome://browser/content/ext-c-devtools-inspectedWindow.js
 
 # scripts that must run in the same process as addon code.
 category webextension-scripts-addon contextMenus chrome://browser/content/ext-c-contextMenus.js
@@ -33,9 +34,10 @@ category webextension-schemas context_me
 category webextension-schemas devtools chrome://browser/content/schemas/devtools.json
 category webextension-schemas devtools_inspected_window chrome://browser/content/schemas/devtools_inspected_window.json
 category webextension-schemas history chrome://browser/content/schemas/history.json
 category webextension-schemas omnibox chrome://browser/content/schemas/omnibox.json
 category webextension-schemas page_action chrome://browser/content/schemas/page_action.json
 category webextension-schemas sessions chrome://browser/content/schemas/sessions.json
 category webextension-schemas tabs chrome://browser/content/schemas/tabs.json
 category webextension-schemas theme chrome://browser/content/schemas/theme.json
+category webextension-schemas url_overrides chrome://browser/content/schemas/url_overrides.json
 category webextension-schemas windows chrome://browser/content/schemas/windows.json
--- a/browser/components/extensions/jar.mn
+++ b/browser/components/extensions/jar.mn
@@ -20,14 +20,15 @@ browser.jar:
     content/browser/ext-desktop-runtime.js
     content/browser/ext-devtools.js
     content/browser/ext-history.js
     content/browser/ext-omnibox.js
     content/browser/ext-pageAction.js
     content/browser/ext-sessions.js
     content/browser/ext-tabs.js
     content/browser/ext-theme.js
+    content/browser/ext-url-overrides.js
     content/browser/ext-utils.js
     content/browser/ext-windows.js
     content/browser/ext-c-contextMenus.js
     content/browser/ext-c-devtools-inspectedWindow.js
     content/browser/ext-c-omnibox.js
     content/browser/ext-c-tabs.js
--- a/browser/components/extensions/schemas/jar.mn
+++ b/browser/components/extensions/schemas/jar.mn
@@ -12,9 +12,10 @@ browser.jar:
     content/browser/schemas/devtools.json
     content/browser/schemas/devtools_inspected_window.json
     content/browser/schemas/history.json
     content/browser/schemas/omnibox.json
     content/browser/schemas/page_action.json
     content/browser/schemas/sessions.json
     content/browser/schemas/tabs.json
     content/browser/schemas/theme.json
+    content/browser/schemas/url_overrides.json
     content/browser/schemas/windows.json
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/url_overrides.json
@@ -0,0 +1,35 @@
+[
+  {
+    "namespace": "manifest",
+    "types": [
+      {
+        "$extend": "WebExtensionManifest",
+        "properties": {
+          "chrome_url_overrides": {
+            "type": "object",
+            "optional": true,
+            "properties": {
+              "newtab": {
+                "$ref": "ExtensionURL",
+                "optional": true,
+                "preprocess": "localize"
+              },
+              "bookmarks": {
+                "unsupported": true,
+                "$ref": "ExtensionURL",
+                "optional": true,
+                "preprocess": "localize"
+              },
+              "history": {
+                "unsupported": true,
+                "$ref": "ExtensionURL",
+                "optional": true,
+                "preprocess": "localize"
+              }
+            }
+          }
+        }
+      }
+    ]
+  }
+]
\ No newline at end of file
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -102,23 +102,24 @@ support-files =
 [browser_ext_tabs_reload_bypass_cache.js]
 [browser_ext_tabs_sendMessage.js]
 [browser_ext_tabs_cookieStoreId.js]
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_zoom.js]
 [browser_ext_tabs_update_url.js]
 [browser_ext_themes.js]
 [browser_ext_topwindowid.js]
+[browser_ext_url_overrides.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]
 [browser_ext_windows_create_tabId.js]
 [browser_ext_windows_create_url.js]
 [browser_ext_windows_events.js]
 [browser_ext_windows_size.js]
 skip-if = os == 'mac' # Fails when windows are randomly opened in fullscreen mode
 [browser_ext_windows_update.js]
-tags = fullscreen
+tags = fullscreen
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides.js
@@ -0,0 +1,110 @@
+/* -*- 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_1 = "webext-newtab-1.html";
+const NEWTAB_URI_2 = "webext-newtab-2.html";
+const NEWTAB_URI_3 = "webext-newtab-3.html";
+
+add_task(function* test_multiple_extensions_overriding_newtab_page() {
+  is(aboutNewTabService.newTabURL, "about:newtab",
+     "Default newtab url should be about:newtab");
+
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {}},
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {newtab: NEWTAB_URI_1}},
+  });
+
+  let ext3 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {newtab: NEWTAB_URI_2}},
+  });
+
+  let ext4 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {newtab: NEWTAB_URI_3}},
+  });
+
+  yield ext1.startup();
+
+  is(aboutNewTabService.newTabURL, "about:newtab",
+       "Default newtab url should still be about:newtab");
+
+  yield ext2.startup();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_1),
+     "Newtab url should be overriden by the second extension.");
+
+  yield ext1.unload();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_1),
+     "Newtab url should still be overriden by the second extension.");
+
+  yield ext3.startup();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_1),
+     "Newtab url should still be overriden by the second extension.");
+
+  yield ext2.unload();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
+     "Newtab url should be overriden by the third extension.");
+
+  yield ext4.startup();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
+     "Newtab url should be overriden by the third extension.");
+
+  yield ext4.unload();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
+     "Newtab url should be overriden by the third extension.");
+
+  yield ext3.unload();
+
+  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,
+      },
+    },
+    files: {
+      [NEWTAB_URI_2]: `
+        <!DOCTYPE html>
+        <head>
+          <meta charset="utf-8"/></head>
+        <html>
+          <body>
+            <script src="newtab.js"></script>
+          </body>
+        </html>
+      `,
+
+      "newtab.js": function() {
+        window.onload = () => {
+          browser.test.sendMessage("from-newtab-page");
+        };
+      },
+    },
+  });
+
+  yield extension.startup();
+
+  // Simulate opening the newtab open as a user would.
+  BrowserOpenTab();
+
+  yield extension.awaitMessage("from-newtab-page");
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  yield extension.unload();
+});