Bug 1234150 - Support overriding "about:newtab" using chrome_url_overrides. r?aswan,mixedpuppy
MozReview-Commit-ID: 4psqXfT1w2p
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();
+});