Bug 1280370 - Allow any scheme in targetUrlPatterns draft
authorRob Wu <rob@robwu.nl>
Tue, 24 Jul 2018 17:26:23 +0200
changeset 827705 fdf039bc1cc469e958e74d85062c02582198822c
parent 827704 6546660bd7b6c4b23fdbb53a11c656d1aed0cbd3
child 827706 aef8a69d4d11efe927216837d02679e532ca6509
push id118573
push userbmo:rob@robwu.nl
push dateWed, 08 Aug 2018 21:39:11 +0000
bugs1280370
milestone63.0a1
Bug 1280370 - Allow any scheme in targetUrlPatterns MozReview-Commit-ID: KupQIiAkz0h
browser/components/extensions/parent/ext-menus.js
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js
--- a/browser/components/extensions/parent/ext-menus.js
+++ b/browser/components/extensions/parent/ext-menus.js
@@ -550,17 +550,17 @@ MenuItem.prototype = {
       this[propName] = createProperties[propName];
     }
 
     if (createProperties.documentUrlPatterns != null) {
       this.documentUrlMatchPattern = new MatchPatternSet(this.documentUrlPatterns);
     }
 
     if (createProperties.targetUrlPatterns != null) {
-      this.targetUrlMatchPattern = new MatchPatternSet(this.targetUrlPatterns);
+      this.targetUrlMatchPattern = new MatchPatternSet(this.targetUrlPatterns, {restrictSchemes: false});
     }
 
     // If a child MenuItem does not specify any contexts, then it should
     // inherit the contexts specified from its parent.
     if (createProperties.parentId && !createProperties.contexts) {
       this.contexts = this.parent.contexts;
     }
   },
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -78,16 +78,17 @@ skip-if = (verify && (os == 'linux' || o
 [browser_ext_connect_and_move_tabs.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_contextMenus.js]
 [browser_ext_contextMenus_checkboxes.js]
 [browser_ext_contextMenus_commands.js]
 [browser_ext_contextMenus_icons.js]
 [browser_ext_contextMenus_onclick.js]
 [browser_ext_contextMenus_radioGroups.js]
+[browser_ext_contextMenus_targetUrlPatterns.js]
 [browser_ext_contextMenus_uninstall.js]
 [browser_ext_contextMenus_urlPatterns.js]
 [browser_ext_currentWindow.js]
 [browser_ext_devtools_inspectedWindow.js]
 [browser_ext_devtools_inspectedWindow_eval_bindings.js]
 [browser_ext_devtools_inspectedWindow_reload.js]
 [browser_ext_devtools_network.js]
 [browser_ext_devtools_page.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js
@@ -0,0 +1,191 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(async function unsupportedSchemes() {
+  const testcases = [{
+    // Link to URL with query string parameters only.
+    testUrl: "magnet:?xt=urn:btih:somesha1hash&dn=displayname.txt",
+    matchingPatterns: [
+      "magnet:*",
+      "magnet:?xt=*",
+      "magnet:?xt=*txt",
+      "magnet:*?xt=*txt",
+    ],
+    nonmatchingPatterns: [
+      // Although <all_urls> matches unsupported schemes in Chromium,
+      // we have specified that <all_urls> only matches all supported
+      // schemes. To match any scheme, an extension should not set the
+      // targetUrlPatterns field - this is checked below in subtest
+      // unsupportedSchemeWithoutTargetUrlPatterns.
+      "<all_urls>",
+      "agnet:*",
+      "magne:*",
+    ],
+  }, {
+    // Link to bookmarklet.
+    testUrl: "javascript:-URL",
+    matchingPatterns: [
+      "javascript:*",
+      "javascript:*URL",
+      "javascript:-URL",
+    ],
+    nonmatchingPatterns: [
+      "<all_urls>",
+      "javascript://-URL",
+      "javascript:javascript:-URL",
+    ],
+  }, {
+    // Link to bookmarklet with comment.
+    testUrl: "javascript://-URL",
+    matchingPatterns: [
+      "javascript:*",
+      "javascript://-URL",
+      "javascript:*URL",
+    ],
+    nonmatchingPatterns: [
+      "<all_urls>",
+      "javascript:-URL",
+    ],
+  }, {
+    // Link to data-URI.
+    testUrl: "data:application/foo,bar",
+    matchingPatterns: [
+      "<all_urls>",
+      "data:application/foo,bar",
+      "data:*,*",
+      "data:*",
+    ],
+    nonmatchingPatterns: [
+      "data:,bar",
+      "data:application/foo,",
+    ],
+  }, {
+    // Extension page.
+    testUrl: "moz-extension://uuid/manifest.json",
+    matchingPatterns: [
+      "moz-extension://*/*",
+    ],
+    nonmatchingPatterns: [
+      "<all_urls>",
+      "moz-extension://uuid/not/manifest.json*",
+    ],
+  }];
+
+  async function testScript(testcases) {
+    let testcase;
+
+    browser.contextMenus.onShown.addListener(({menuIds, linkUrl}) => {
+      browser.test.assertEq(testcase.testUrl, linkUrl, "Expected linkUrl");
+      for (let pattern of testcase.matchingPatterns) {
+        browser.test.assertTrue(
+          menuIds.includes(pattern),
+          `Menu item with targetUrlPattern="${pattern}" should be shown at ${testcase.testUrl}`);
+      }
+      for (let pattern of testcase.nonmatchingPatterns) {
+        browser.test.assertFalse(
+          menuIds.includes(pattern),
+          `Menu item with targetUrlPattern="${pattern}" should not be shown at ${testcase.testUrl}`);
+      }
+      testcase = null;
+      browser.test.sendMessage("onShown_checked");
+    });
+
+    browser.test.onMessage.addListener(async (msg, params) => {
+      browser.test.assertEq("setupTest", msg, "Expected message");
+
+      // Save test case in global variable for use in the onShown event.
+      testcase = params;
+      browser.test.log(`Running test for link with URL: ${testcase.testUrl}`);
+      document.getElementById("test_link_element").href = testcase.testUrl;
+      await browser.contextMenus.removeAll();
+      for (let targetUrlPattern of [...testcase.matchingPatterns, ...testcase.nonmatchingPatterns]) {
+        await new Promise(resolve => {
+          browser.test.log(`Creating menu with "${targetUrlPattern}"`);
+          browser.contextMenus.create({
+            id: targetUrlPattern,
+            contexts: ["link"],
+            title: "Some menu item",
+            targetUrlPatterns: [targetUrlPattern],
+          }, resolve);
+        });
+      }
+      browser.test.sendMessage("setupTest_ready");
+    });
+    browser.test.sendMessage("ready");
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: ["contextMenus"],
+    },
+    background() {
+      browser.tabs.create({url: "testrunner.html"});
+    },
+    files: {
+      "testrunner.js": `(${testScript})()`,
+      "testrunner.html": `
+        <!DOCTYPE html><meta charset="utf-8">
+        <body>
+        <a id="test_link_element">Test link</a>
+        <script src="testrunner.js"></script>
+        </body>
+      `,
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("ready");
+  for (let testcase of testcases) {
+    extension.sendMessage("setupTest", testcase);
+    await extension.awaitMessage("setupTest_ready");
+
+    await openExtensionContextMenu("#test_link_element");
+    await extension.awaitMessage("onShown_checked");
+    await closeContextMenu();
+  }
+  await extension.unload();
+});
+
+// Tests that a menu item is shown on links with an unsupported scheme if
+// targetUrlPatterns is not set.
+add_task(async function unsupportedSchemeWithoutPattern() {
+  function background() {
+    let menuId;
+    browser.contextMenus.onShown.addListener(({menuIds, linkUrl}) => {
+      browser.test.assertEq(1, menuIds.length, "Expected number of menus");
+      browser.test.assertEq(menuId, menuIds[0], "Expected menu ID");
+      browser.test.assertEq("unsupported-scheme:data", linkUrl, "Expected linkUrl");
+      browser.test.sendMessage("done");
+    });
+    menuId = browser.contextMenus.create({
+      contexts: ["link"],
+      title: "Test menu item without targetUrlPattern",
+    }, () => {
+      browser.tabs.create({url: "testpage.html"});
+    });
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: ["contextMenus"],
+    },
+    background,
+    files: {
+      "testpage.js": `browser.test.sendMessage("ready")`,
+      "testpage.html": `
+        <!DOCTYPE html><meta charset="utf-8">
+        <a id="test_link_element" href="unsupported-scheme:data">Test link</a>
+        <script src="testpage.js"></script>
+      `,
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("ready");
+  await openExtensionContextMenu("#test_link_element");
+  await extension.awaitMessage("done");
+  await closeContextMenu();
+
+  await extension.unload();
+});