summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/test/test-context-menu.js
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /addon-sdk/source/test/test-context-menu.js
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'addon-sdk/source/test/test-context-menu.js')
-rw-r--r--addon-sdk/source/test/test-context-menu.js3763
1 files changed, 3763 insertions, 0 deletions
diff --git a/addon-sdk/source/test/test-context-menu.js b/addon-sdk/source/test/test-context-menu.js
new file mode 100644
index 000000000..f1a955545
--- /dev/null
+++ b/addon-sdk/source/test/test-context-menu.js
@@ -0,0 +1,3763 @@
+/* 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';
+
+require("sdk/context-menu");
+
+const { defer } = require("sdk/core/promise");
+const { isTravisCI } = require("sdk/test/utils");
+const packaging = require('@loader/options');
+
+// These should match the same constants in the module.
+const OVERFLOW_THRESH_DEFAULT = 10;
+const OVERFLOW_THRESH_PREF =
+ "extensions.addon-sdk.context-menu.overflowThreshold";
+
+const TEST_DOC_URL = module.uri.replace(/\.js$/, ".html");
+const data = require("./fixtures");
+
+const { TestHelper } = require("./context-menu/test-helper.js")
+
+// Tests that when present the separator is placed before the separator from
+// the old context-menu module
+exports.testSeparatorPosition = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ // Create the old separator
+ let oldSeparator = test.contextMenuPopup.ownerDocument.createElement("menuseparator");
+ oldSeparator.id = "jetpack-context-menu-separator";
+ test.contextMenuPopup.appendChild(oldSeparator);
+
+ // Create an item.
+ let item = new loader.cm.Item({ label: "item" });
+
+ test.showMenu(null, function (popup) {
+ assert.equal(test.contextMenuSeparator.nextSibling.nextSibling, oldSeparator,
+ "New separator should appear before the old one");
+ test.contextMenuPopup.removeChild(oldSeparator);
+ test.done();
+ });
+};
+
+// Destroying items that were previously created should cause them to be absent
+// from the menu.
+exports.testConstructDestroy = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ // Create an item.
+ let item = new loader.cm.Item({ label: "item" });
+ assert.equal(item.parentMenu, loader.cm.contentContextMenu,
+ "item's parent menu should be correct");
+
+ test.showMenu(null, function (popup) {
+
+ // It should be present when the menu is shown.
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ // Destroy the item. Multiple destroys should be harmless.
+ item.destroy();
+ item.destroy();
+ test.showMenu(null, function (popup) {
+
+ // It should be removed from the menu.
+ test.checkMenu([item], [], [item]);
+ test.done();
+ });
+ });
+};
+
+
+// Destroying an item twice should not cause an error.
+exports.testDestroyTwice = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({ label: "item" });
+ item.destroy();
+ item.destroy();
+
+ test.pass("Destroying an item twice should not cause an error.");
+ test.done();
+};
+
+
+// CSS selector contexts should cause their items to be present in the menu
+// when the menu is invoked on nodes that match the selectors.
+exports.testSelectorContextMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ data: "item",
+ context: loader.cm.SelectorContext("img")
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+};
+
+
+// CSS selector contexts should cause their items to be present in the menu
+// when the menu is invoked on nodes that have ancestors that match the
+// selectors.
+exports.testSelectorAncestorContextMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ data: "item",
+ context: loader.cm.SelectorContext("a[href]")
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#span-link", function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+};
+
+
+// CSS selector contexts should cause their items to be absent from the menu
+// when the menu is not invoked on nodes that match or have ancestors that
+// match the selectors.
+exports.testSelectorContextNoMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ data: "item",
+ context: loader.cm.SelectorContext("img")
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+};
+
+
+// Page contexts should cause their items to be present in the menu when the
+// menu is not invoked on an active element.
+exports.testPageContextMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ new loader.cm.Item({
+ label: "item 0"
+ }),
+ new loader.cm.Item({
+ label: "item 1",
+ context: undefined
+ }),
+ new loader.cm.Item({
+ label: "item 2",
+ context: loader.cm.PageContext()
+ }),
+ new loader.cm.Item({
+ label: "item 3",
+ context: [loader.cm.PageContext()]
+ })
+ ];
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+};
+
+
+// Page contexts should cause their items to be absent from the menu when the
+// menu is invoked on an active element.
+exports.testPageContextNoMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ new loader.cm.Item({
+ label: "item 0"
+ }),
+ new loader.cm.Item({
+ label: "item 1",
+ context: undefined
+ }),
+ new loader.cm.Item({
+ label: "item 2",
+ context: loader.cm.PageContext()
+ }),
+ new loader.cm.Item({
+ label: "item 3",
+ context: [loader.cm.PageContext()]
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, items, []);
+ test.done();
+ });
+ });
+};
+
+
+// Selection contexts should cause items to appear when a selection exists.
+exports.testSelectionContextMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({
+ label: "item",
+ context: loader.cm.SelectionContext()
+ });
+
+ test.withTestDoc(function (window, doc) {
+ window.getSelection().selectAllChildren(doc.body);
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Selection contexts should cause items to appear when a selection exists in
+// a text field.
+exports.testSelectionContextMatchInTextField = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({
+ label: "item",
+ context: loader.cm.SelectionContext()
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.selectRange("#textfield", 0, null);
+ test.showMenu("#textfield", function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Selection contexts should not cause items to appear when a selection does
+// not exist in a text field.
+exports.testSelectionContextNoMatchInTextField = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({
+ label: "item",
+ context: loader.cm.SelectionContext()
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.selectRange("#textfield", 0, 0);
+ test.showMenu("#textfield", function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+ });
+};
+
+
+// Selection contexts should not cause items to appear when a selection does
+// not exist.
+exports.testSelectionContextNoMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({
+ label: "item",
+ context: loader.cm.SelectionContext()
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+};
+
+
+// Selection contexts should cause items to appear when a selection exists even
+// for newly opened pages
+exports.testSelectionContextInNewTab = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({
+ label: "item",
+ context: loader.cm.SelectionContext()
+ });
+
+ test.withTestDoc(function (window, doc) {
+ let link = doc.getElementById("targetlink");
+ link.click();
+
+ let tablistener = event => {
+ this.tabBrowser.tabContainer.removeEventListener("TabOpen", tablistener, false);
+ let tab = event.target;
+ let browser = tab.linkedBrowser;
+ this.loadFrameScript(browser);
+ this.delayedEventListener(browser, "load", () => {
+ let window = browser.contentWindow;
+ let doc = browser.contentDocument;
+ window.getSelection().selectAllChildren(doc.body);
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ test.tabBrowser.removeTab(test.tabBrowser.selectedTab);
+ test.tabBrowser.selectedTab = test.tab;
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+ });
+ }, true);
+ };
+ this.tabBrowser.tabContainer.addEventListener("TabOpen", tablistener, false);
+ });
+};
+
+
+// Selection contexts should work when right clicking a form button
+exports.testSelectionContextButtonMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({
+ label: "item",
+ context: loader.cm.SelectionContext()
+ });
+
+ test.withTestDoc(function (window, doc) {
+ window.getSelection().selectAllChildren(doc.body);
+ test.showMenu("#button", function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+};
+
+
+//Selection contexts should work when right clicking a form button
+exports.testSelectionContextButtonNoMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({
+ label: "item",
+ context: loader.cm.SelectionContext()
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#button", function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+ });
+};
+
+
+// URL contexts should cause items to appear on pages that match.
+exports.testURLContextMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ loader.cm.Item({
+ label: "item 0",
+ context: loader.cm.URLContext(TEST_DOC_URL)
+ }),
+ loader.cm.Item({
+ label: "item 1",
+ context: loader.cm.URLContext([TEST_DOC_URL, "*.bogus.com"])
+ }),
+ loader.cm.Item({
+ label: "item 2",
+ context: loader.cm.URLContext([new RegExp(".*\\.html")])
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// URL contexts should not cause items to appear on pages that do not match.
+exports.testURLContextNoMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ loader.cm.Item({
+ label: "item 0",
+ context: loader.cm.URLContext("*.bogus.com")
+ }),
+ loader.cm.Item({
+ label: "item 1",
+ context: loader.cm.URLContext(["*.bogus.com", "*.gnarly.com"])
+ }),
+ loader.cm.Item({
+ label: "item 2",
+ context: loader.cm.URLContext([new RegExp(".*\\.js")])
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, items, []);
+ test.done();
+ });
+ });
+};
+
+
+// Loading a new page in the same tab should correctly start a new worker for
+// any content scripts
+exports.testPageReload = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({
+ label: "Item",
+ contentScript: "var doc = document; self.on('context', node => doc.body.getAttribute('showItem') == 'true');"
+ });
+
+ test.withTestDoc(function (window, doc) {
+ // Set a flag on the document that the item uses
+ doc.body.setAttribute("showItem", "true");
+
+ test.showMenu(null, function (popup) {
+ // With the attribute true the item should be visible in the menu
+ test.checkMenu([item], [], []);
+ test.hideMenu(function() {
+ let browser = this.tabBrowser.getBrowserForTab(this.tab)
+ test.delayedEventListener(browser, "load", function() {
+ test.delayedEventListener(browser, "load", function() {
+ window = browser.contentWindow;
+ doc = window.document;
+
+ // Set a flag on the document that the item uses
+ doc.body.setAttribute("showItem", "false");
+
+ test.showMenu(null, function (popup) {
+ // In the new document with the attribute false the item should be
+ // hidden, but if the contentScript hasn't been reloaded it will
+ // still see the old value
+ test.checkMenu([item], [item], []);
+
+ test.done();
+ });
+ }, true);
+ browser.loadURI(TEST_DOC_URL, null, null);
+ }, true);
+ // Required to make sure we load a new page in history rather than
+ // just reloading the current page which would unload it
+ browser.loadURI("about:blank", null, null);
+ });
+ });
+ });
+};
+
+// Closing a page after it's been used with a worker should cause the worker
+// to be destroyed
+/*exports.testWorkerDestroy = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let loadExpected = false;
+
+ let item = loader.cm.Item({
+ label: "item",
+ contentScript: 'self.postMessage("loaded"); self.on("detach", function () { console.log("saw detach"); self.postMessage("detach") });',
+ onMessage: function (msg) {
+ switch (msg) {
+ case "loaded":
+ assert.ok(loadExpected, "Should have seen the load event at the right time");
+ loadExpected = false;
+ break;
+ case "detach":
+ test.done();
+ break;
+ }
+ }
+ });
+
+ test.withTestDoc(function (window, doc) {
+ loadExpected = true;
+ test.showMenu(null, function (popup) {
+ assert.ok(!loadExpected, "Should have seen a message");
+
+ test.checkMenu([item], [], []);
+
+ test.closeTab();
+ });
+ });
+};*/
+
+
+// Content contexts that return true should cause their items to be present
+// in the menu.
+exports.testContentContextMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", () => true);'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+};
+
+
+// Content contexts that return false should cause their items to be absent
+// from the menu.
+exports.testContentContextNoMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", () => false);'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+};
+
+
+// Content contexts that return undefined should cause their items to be absent
+// from the menu.
+exports.testContentContextUndefined = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", function () {});'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+};
+
+
+// Content contexts that return an empty string should cause their items to be
+// absent from the menu and shouldn't wipe the label
+exports.testContentContextEmptyString = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", () => "");'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [item], []);
+ assert.equal(item.label, "item", "Label should still be correct");
+ test.done();
+ });
+};
+
+
+// If any content contexts returns true then their items should be present in
+// the menu.
+exports.testMultipleContentContextMatch1 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", () => true); ' +
+ 'self.on("context", () => false);',
+ onMessage: function() {
+ test.fail("Should not have called the second context listener");
+ }
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+};
+
+
+// If any content contexts returns true then their items should be present in
+// the menu.
+exports.testMultipleContentContextMatch2 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", () => false); ' +
+ 'self.on("context", () => true);'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+};
+
+
+// If any content contexts returns a string then their items should be present
+// in the menu.
+exports.testMultipleContentContextString1 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", () => "new label"); ' +
+ 'self.on("context", () => false);'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ assert.equal(item.label, "new label", "Label should have changed");
+ test.done();
+ });
+};
+
+
+// If any content contexts returns a string then their items should be present
+// in the menu.
+exports.testMultipleContentContextString2 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", () => false); ' +
+ 'self.on("context", () => "new label");'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ assert.equal(item.label, "new label", "Label should have changed");
+ test.done();
+ });
+};
+
+
+// If many content contexts returns a string then the first should take effect
+exports.testMultipleContentContextString3 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", () => "new label 1"); ' +
+ 'self.on("context", () => "new label 2");'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ assert.equal(item.label, "new label 1", "Label should have changed");
+ test.done();
+ });
+};
+
+
+// Content contexts that return true should cause their items to be present
+// in the menu when context clicking an active element.
+exports.testContentContextMatchActiveElement = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ new loader.cm.Item({
+ label: "item 1",
+ contentScript: 'self.on("context", () => true);'
+ }),
+ new loader.cm.Item({
+ label: "item 2",
+ context: undefined,
+ contentScript: 'self.on("context", () => true);'
+ }),
+ // These items will always be hidden by the declarative usage of PageContext
+ new loader.cm.Item({
+ label: "item 3",
+ context: loader.cm.PageContext(),
+ contentScript: 'self.on("context", () => true);'
+ }),
+ new loader.cm.Item({
+ label: "item 4",
+ context: [loader.cm.PageContext()],
+ contentScript: 'self.on("context", () => true);'
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, [items[2], items[3]], []);
+ test.done();
+ });
+ });
+};
+
+
+// Content contexts that return false should cause their items to be absent
+// from the menu when context clicking an active element.
+exports.testContentContextNoMatchActiveElement = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ new loader.cm.Item({
+ label: "item 1",
+ contentScript: 'self.on("context", () => false);'
+ }),
+ new loader.cm.Item({
+ label: "item 2",
+ context: undefined,
+ contentScript: 'self.on("context", () => false);'
+ }),
+ // These items will always be hidden by the declarative usage of PageContext
+ new loader.cm.Item({
+ label: "item 3",
+ context: loader.cm.PageContext(),
+ contentScript: 'self.on("context", () => false);'
+ }),
+ new loader.cm.Item({
+ label: "item 4",
+ context: [loader.cm.PageContext()],
+ contentScript: 'self.on("context", () => false);'
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, items, []);
+ test.done();
+ });
+ });
+};
+
+
+// Content contexts that return undefined should cause their items to be absent
+// from the menu when context clicking an active element.
+exports.testContentContextNoMatchActiveElement = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ new loader.cm.Item({
+ label: "item 1",
+ contentScript: 'self.on("context", () => {});'
+ }),
+ new loader.cm.Item({
+ label: "item 2",
+ context: undefined,
+ contentScript: 'self.on("context", () => {});'
+ }),
+ // These items will always be hidden by the declarative usage of PageContext
+ new loader.cm.Item({
+ label: "item 3",
+ context: loader.cm.PageContext(),
+ contentScript: 'self.on("context", () => {});'
+ }),
+ new loader.cm.Item({
+ label: "item 4",
+ context: [loader.cm.PageContext()],
+ contentScript: 'self.on("context", () => {});'
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, items, []);
+ test.done();
+ });
+ });
+};
+
+
+// Content contexts that return a string should cause their items to be present
+// in the menu and the items' labels to be updated.
+exports.testContentContextMatchString = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "first label",
+ contentScript: 'self.on("context", () => "second label");'
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ assert.equal(item.label, "second label",
+ "item's label should be updated");
+ test.done();
+ });
+};
+
+
+// Ensure that contentScriptFile is working correctly
+exports.testContentScriptFile = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+ let { defer, all } = require("sdk/core/promise");
+ let itemScript = [defer(), defer()];
+ let menuShown = defer();
+ let menuPromises = itemScript.concat(menuShown).map(({promise}) => promise);
+
+ // Reject remote files
+ assert.throws(function() {
+ new loader.cm.Item({
+ label: "item",
+ contentScriptFile: "http://mozilla.com/context-menu.js"
+ });
+ },
+ /The `contentScriptFile` option must be a local URL or an array of URLs/,
+ "Item throws when contentScriptFile is a remote URL");
+
+ // But accept files from data folder
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScriptFile: data.url("test-contentScriptFile.js"),
+ onMessage: (message) => {
+ assert.equal(message, "msg from contentScriptFile",
+ "contentScriptFile loaded with absolute url");
+ itemScript[0].resolve();
+ }
+ });
+
+ let item2 = new loader.cm.Item({
+ label: "item2",
+ contentScriptFile: "./test-contentScriptFile.js",
+ onMessage: (message) => {
+ assert.equal(message, "msg from contentScriptFile",
+ "contentScriptFile loaded with relative url");
+ itemScript[1].resolve();
+ }
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item, item2], [], []);
+ menuShown.resolve();
+ });
+
+ all(menuPromises).then(() => test.done());
+};
+
+
+// The args passed to context listeners should be correct.
+exports.testContentContextArgs = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+ let callbacks = 0;
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'self.on("context", function (node) {' +
+ ' self.postMessage(node.tagName);' +
+ ' return false;' +
+ '});',
+ onMessage: function (tagName) {
+ assert.equal(tagName, "HTML", "node should be an HTML element");
+ if (++callbacks == 2) test.done();
+ }
+ });
+
+ test.showMenu(null, function () {
+ if (++callbacks == 2) test.done();
+ });
+};
+
+// Multiple contexts imply intersection, not union.
+exports.testMultipleContexts = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ context: [loader.cm.SelectorContext("a[href]"), loader.cm.PageContext()],
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#span-link", function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+ });
+};
+
+// Once a context is removed, it should no longer cause its item to appear.
+exports.testRemoveContext = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let ctxt = loader.cm.SelectorContext("img");
+ let item = new loader.cm.Item({
+ label: "item",
+ context: ctxt
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+
+ // The item should be present at first.
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ // Remove the img context and check again.
+ item.context.remove(ctxt);
+ test.showMenu("#image", function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+ });
+ });
+};
+
+// Once a context is removed, it should no longer cause its item to appear.
+exports.testSetContextRemove = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let ctxt = loader.cm.SelectorContext("img");
+ let item = new loader.cm.Item({
+ label: "item",
+ context: ctxt
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+
+ // The item should be present at first.
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ // Remove the img context and check again.
+ item.context = [];
+ test.showMenu("#image", function (popup) {
+ test.checkMenu([item], [item], []);
+ test.done();
+ });
+ });
+ });
+};
+
+// Once a context is added, it should affect whether the item appears.
+exports.testAddContext = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let ctxt = loader.cm.SelectorContext("img");
+ let item = new loader.cm.Item({
+ label: "item"
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+
+ // The item should not be present at first.
+ test.checkMenu([item], [item], []);
+ popup.hidePopup();
+
+ // Add the img context and check again.
+ item.context.add(ctxt);
+ test.showMenu("#image", function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+ });
+};
+
+// Once a context is added, it should affect whether the item appears.
+exports.testSetContextAdd = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let ctxt = loader.cm.SelectorContext("img");
+ let item = new loader.cm.Item({
+ label: "item"
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+
+ // The item should not be present at first.
+ test.checkMenu([item], [item], []);
+ popup.hidePopup();
+
+ // Add the img context and check again.
+ item.context = [ctxt];
+ test.showMenu("#image", function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+ });
+};
+
+// Lots of items should overflow into the overflow submenu.
+exports.testOverflow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [];
+ for (let i = 0; i < OVERFLOW_THRESH_DEFAULT + 1; i++) {
+ let item = new loader.cm.Item({ label: "item " + i });
+ items.push(item);
+ }
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+};
+
+
+// Module unload should cause all items to be removed.
+exports.testUnload = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({ label: "item" });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain the item.
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ // Unload the module.
+ loader.unload();
+ test.showMenu(null, function (popup) {
+
+ // The item should be removed from the menu.
+ test.checkMenu([item], [], [item]);
+ test.done();
+ });
+ });
+};
+
+
+// Using multiple module instances to add items without causing overflow should
+// work OK. Assumes OVERFLOW_THRESH_DEFAULT >= 2.
+exports.testMultipleModulesAdd = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ // Use each module to add an item, then unload each module in turn.
+ let item0 = new loader0.cm.Item({ label: "item 0" });
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain both items.
+ test.checkMenu([item0, item1], [], []);
+ popup.hidePopup();
+
+ // Unload the first module.
+ loader0.unload();
+ test.showMenu(null, function (popup) {
+
+ // The first item should be removed from the menu.
+ test.checkMenu([item0, item1], [], [item0]);
+ popup.hidePopup();
+
+ // Unload the second module.
+ loader1.unload();
+ test.showMenu(null, function (popup) {
+
+ // Both items should be removed from the menu.
+ test.checkMenu([item0, item1], [], [item0, item1]);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Using multiple module instances to add items causing overflow should work OK.
+exports.testMultipleModulesAddOverflow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ // Use module 0 to add OVERFLOW_THRESH_DEFAULT items.
+ let items0 = [];
+ for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) {
+ let item = new loader0.cm.Item({ label: "item 0 " + i });
+ items0.push(item);
+ }
+
+ // Use module 1 to add one item.
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ let allItems = items0.concat(item1);
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain all items in overflow.
+ test.checkMenu(allItems, [], []);
+ popup.hidePopup();
+
+ // Unload the first module.
+ loader0.unload();
+ test.showMenu(null, function (popup) {
+
+ // The first items should be removed from the menu, which should not
+ // overflow.
+ test.checkMenu(allItems, [], items0);
+ popup.hidePopup();
+
+ // Unload the second module.
+ loader1.unload();
+ test.showMenu(null, function (popup) {
+
+ // All items should be removed from the menu.
+ test.checkMenu(allItems, [], allItems);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Using multiple module instances to modify the menu without causing overflow
+// should work OK. This test creates two loaders and:
+// loader0 create item -> loader1 create item -> loader0.unload ->
+// loader1.unload
+exports.testMultipleModulesDiffContexts1 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let item0 = new loader0.cm.Item({
+ label: "item 0",
+ context: loader0.cm.SelectorContext("img")
+ });
+
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain item1.
+ test.checkMenu([item0, item1], [item0], []);
+ popup.hidePopup();
+
+ // Unload module 0.
+ loader0.unload();
+ test.showMenu(null, function (popup) {
+
+ // item0 should be removed from the menu.
+ test.checkMenu([item0, item1], [], [item0]);
+ popup.hidePopup();
+
+ // Unload module 1.
+ loader1.unload();
+ test.showMenu(null, function (popup) {
+
+ // Both items should be removed from the menu.
+ test.checkMenu([item0, item1], [], [item0, item1]);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Using multiple module instances to modify the menu without causing overflow
+// should work OK. This test creates two loaders and:
+// loader1 create item -> loader0 create item -> loader0.unload ->
+// loader1.unload
+exports.testMultipleModulesDiffContexts2 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ let item0 = new loader0.cm.Item({
+ label: "item 0",
+ context: loader0.cm.SelectorContext("img")
+ });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain item1.
+ test.checkMenu([item0, item1], [item0], []);
+ popup.hidePopup();
+
+ // Unload module 0.
+ loader0.unload();
+ test.showMenu(null, function (popup) {
+
+ // item0 should be removed from the menu.
+ test.checkMenu([item0, item1], [], [item0]);
+ popup.hidePopup();
+
+ // Unload module 1.
+ loader1.unload();
+ test.showMenu(null, function (popup) {
+
+ // Both items should be removed from the menu.
+ test.checkMenu([item0, item1], [], [item0, item1]);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Using multiple module instances to modify the menu without causing overflow
+// should work OK. This test creates two loaders and:
+// loader0 create item -> loader1 create item -> loader1.unload ->
+// loader0.unload
+exports.testMultipleModulesDiffContexts3 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let item0 = new loader0.cm.Item({
+ label: "item 0",
+ context: loader0.cm.SelectorContext("img")
+ });
+
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain item1.
+ test.checkMenu([item0, item1], [item0], []);
+ popup.hidePopup();
+
+ // Unload module 1.
+ loader1.unload();
+ test.showMenu(null, function (popup) {
+
+ // item1 should be removed from the menu.
+ test.checkMenu([item0, item1], [item0], [item1]);
+ popup.hidePopup();
+
+ // Unload module 0.
+ loader0.unload();
+ test.showMenu(null, function (popup) {
+
+ // Both items should be removed from the menu.
+ test.checkMenu([item0, item1], [], [item0, item1]);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Using multiple module instances to modify the menu without causing overflow
+// should work OK. This test creates two loaders and:
+// loader1 create item -> loader0 create item -> loader1.unload ->
+// loader0.unload
+exports.testMultipleModulesDiffContexts4 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ let item0 = new loader0.cm.Item({
+ label: "item 0",
+ context: loader0.cm.SelectorContext("img")
+ });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain item1.
+ test.checkMenu([item0, item1], [item0], []);
+ popup.hidePopup();
+
+ // Unload module 1.
+ loader1.unload();
+ test.showMenu(null, function (popup) {
+
+ // item1 should be removed from the menu.
+ test.checkMenu([item0, item1], [item0], [item1]);
+ popup.hidePopup();
+
+ // Unload module 0.
+ loader0.unload();
+ test.showMenu(null, function (popup) {
+
+ // Both items should be removed from the menu.
+ test.checkMenu([item0, item1], [], [item0, item1]);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Test interactions between a loaded module, unloading another module, and the
+// menu separator and overflow submenu.
+exports.testMultipleModulesAddRemove = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let item = new loader0.cm.Item({ label: "item" });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain the item.
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ // Remove the item.
+ item.destroy();
+ test.showMenu(null, function (popup) {
+
+ // The item should be removed from the menu.
+ test.checkMenu([item], [], [item]);
+ popup.hidePopup();
+
+ // Unload module 1.
+ loader1.unload();
+ test.showMenu(null, function (popup) {
+
+ // There shouldn't be any errors involving the menu separator or
+ // overflow submenu.
+ test.checkMenu([item], [], [item]);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Checks that the order of menu items is correct when adding/removing across
+// multiple modules. All items from a single module should remain in a group
+exports.testMultipleModulesOrder = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ // Use each module to add an item, then unload each module in turn.
+ let item0 = new loader0.cm.Item({ label: "item 0" });
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain both items.
+ test.checkMenu([item0, item1], [], []);
+ popup.hidePopup();
+
+ let item2 = new loader0.cm.Item({ label: "item 2" });
+
+ test.showMenu(null, function (popup) {
+
+ // The new item should be grouped with the same items from loader0.
+ test.checkMenu([item0, item2, item1], [], []);
+ popup.hidePopup();
+
+ let item3 = new loader1.cm.Item({ label: "item 3" });
+
+ test.showMenu(null, function (popup) {
+
+ // Same again
+ test.checkMenu([item0, item2, item1, item3], [], []);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Checks that the order of menu items is correct when adding/removing across
+// multiple modules when overflowing. All items from a single module should
+// remain in a group
+exports.testMultipleModulesOrderOverflow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let prefs = loader0.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 0);
+
+ // Use each module to add an item, then unload each module in turn.
+ let item0 = new loader0.cm.Item({ label: "item 0" });
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ test.showMenu(null, function (popup) {
+
+ // The menu should contain both items.
+ test.checkMenu([item0, item1], [], []);
+ popup.hidePopup();
+
+ let item2 = new loader0.cm.Item({ label: "item 2" });
+
+ test.showMenu(null, function (popup) {
+
+ // The new item should be grouped with the same items from loader0.
+ test.checkMenu([item0, item2, item1], [], []);
+ popup.hidePopup();
+
+ let item3 = new loader1.cm.Item({ label: "item 3" });
+
+ test.showMenu(null, function (popup) {
+
+ // Same again
+ test.checkMenu([item0, item2, item1, item3], [], []);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Checks that if a module's items are all hidden then the overflow menu doesn't
+// get hidden
+exports.testMultipleModulesOverflowHidden = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let prefs = loader0.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 0);
+
+ // Use each module to add an item, then unload each module in turn.
+ let item0 = new loader0.cm.Item({ label: "item 0" });
+ let item1 = new loader1.cm.Item({
+ label: "item 1",
+ context: loader1.cm.SelectorContext("a")
+ });
+
+ test.showMenu(null, function (popup) {
+ // One should be hidden
+ test.checkMenu([item0, item1], [item1], []);
+ test.done();
+ });
+};
+
+
+// Checks that if a module's items are all hidden then the overflow menu doesn't
+// get hidden (reverse order to above)
+exports.testMultipleModulesOverflowHidden2 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let prefs = loader0.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 0);
+
+ // Use each module to add an item, then unload each module in turn.
+ let item0 = new loader0.cm.Item({
+ label: "item 0",
+ context: loader0.cm.SelectorContext("a")
+ });
+ let item1 = new loader1.cm.Item({ label: "item 1" });
+
+ test.showMenu(null, function (popup) {
+ // One should be hidden
+ test.checkMenu([item0, item1], [item0], []);
+ test.done();
+ });
+};
+
+
+// Checks that we don't overflow if there are more items than the overflow
+// threshold but not all of them are visible
+exports.testOverflowIgnoresHidden = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let prefs = loader.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 2);
+
+ let allItems = [
+ new loader.cm.Item({
+ label: "item 0"
+ }),
+ new loader.cm.Item({
+ label: "item 1"
+ }),
+ new loader.cm.Item({
+ label: "item 2",
+ context: loader.cm.SelectorContext("a")
+ })
+ ];
+
+ test.showMenu(null, function (popup) {
+ // One should be hidden
+ test.checkMenu(allItems, [allItems[2]], []);
+ test.done();
+ });
+};
+
+
+// Checks that we don't overflow if there are more items than the overflow
+// threshold but not all of them are visible
+exports.testOverflowIgnoresHiddenMultipleModules1 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let prefs = loader0.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 2);
+
+ let allItems = [
+ new loader0.cm.Item({
+ label: "item 0"
+ }),
+ new loader0.cm.Item({
+ label: "item 1"
+ }),
+ new loader1.cm.Item({
+ label: "item 2",
+ context: loader1.cm.SelectorContext("a")
+ }),
+ new loader1.cm.Item({
+ label: "item 3",
+ context: loader1.cm.SelectorContext("a")
+ })
+ ];
+
+ test.showMenu(null, function (popup) {
+ // One should be hidden
+ test.checkMenu(allItems, [allItems[2], allItems[3]], []);
+ test.done();
+ });
+};
+
+
+// Checks that we don't overflow if there are more items than the overflow
+// threshold but not all of them are visible
+exports.testOverflowIgnoresHiddenMultipleModules2 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let prefs = loader0.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 2);
+
+ let allItems = [
+ new loader0.cm.Item({
+ label: "item 0"
+ }),
+ new loader0.cm.Item({
+ label: "item 1",
+ context: loader0.cm.SelectorContext("a")
+ }),
+ new loader1.cm.Item({
+ label: "item 2"
+ }),
+ new loader1.cm.Item({
+ label: "item 3",
+ context: loader1.cm.SelectorContext("a")
+ })
+ ];
+
+ test.showMenu(null, function (popup) {
+ // One should be hidden
+ test.checkMenu(allItems, [allItems[1], allItems[3]], []);
+ test.done();
+ });
+};
+
+
+// Checks that we don't overflow if there are more items than the overflow
+// threshold but not all of them are visible
+exports.testOverflowIgnoresHiddenMultipleModules3 = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let prefs = loader0.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 2);
+
+ let allItems = [
+ new loader0.cm.Item({
+ label: "item 0",
+ context: loader0.cm.SelectorContext("a")
+ }),
+ new loader0.cm.Item({
+ label: "item 1",
+ context: loader0.cm.SelectorContext("a")
+ }),
+ new loader1.cm.Item({
+ label: "item 2"
+ }),
+ new loader1.cm.Item({
+ label: "item 3"
+ })
+ ];
+
+ test.showMenu(null, function (popup) {
+ // One should be hidden
+ test.checkMenu(allItems, [allItems[0], allItems[1]], []);
+ test.done();
+ });
+};
+
+
+// Tests that we transition between overflowing to non-overflowing to no items
+// and back again
+exports.testOverflowTransition = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let prefs = loader.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 2);
+
+ let pItems = [
+ new loader.cm.Item({
+ label: "item 0",
+ context: loader.cm.SelectorContext("p")
+ }),
+ new loader.cm.Item({
+ label: "item 1",
+ context: loader.cm.SelectorContext("p")
+ })
+ ];
+
+ let aItems = [
+ new loader.cm.Item({
+ label: "item 2",
+ context: loader.cm.SelectorContext("a")
+ }),
+ new loader.cm.Item({
+ label: "item 3",
+ context: loader.cm.SelectorContext("a")
+ })
+ ];
+
+ let allItems = pItems.concat(aItems);
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#link", function (popup) {
+ // The menu should contain all items and will overflow
+ test.checkMenu(allItems, [], []);
+ popup.hidePopup();
+
+ test.showMenu("#text", function (popup) {
+ // Only contains hald the items and will not overflow
+ test.checkMenu(allItems, aItems, []);
+ popup.hidePopup();
+
+ test.showMenu(null, function (popup) {
+ // None of the items will be visible
+ test.checkMenu(allItems, allItems, []);
+ popup.hidePopup();
+
+ test.showMenu("#text", function (popup) {
+ // Only contains hald the items and will not overflow
+ test.checkMenu(allItems, aItems, []);
+ popup.hidePopup();
+
+ test.showMenu("#link", function (popup) {
+ // The menu should contain all items and will overflow
+ test.checkMenu(allItems, [], []);
+ popup.hidePopup();
+
+ test.showMenu(null, function (popup) {
+ // None of the items will be visible
+ test.checkMenu(allItems, allItems, []);
+ popup.hidePopup();
+
+ test.showMenu("#link", function (popup) {
+ // The menu should contain all items and will overflow
+ test.checkMenu(allItems, [], []);
+ test.done();
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+};
+
+
+// An item's command listener should work.
+exports.testItemCommand = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ data: "item data",
+ contentScript: 'self.on("click", function (node, data) {' +
+ ' self.postMessage({' +
+ ' tagName: node.tagName,' +
+ ' data: data' +
+ ' });' +
+ '});',
+ onMessage: function (data) {
+ assert.equal(this, item, "`this` inside onMessage should be item");
+ assert.equal(data.tagName, "HTML", "node should be an HTML element");
+ assert.equal(data.data, item.data, "data should be item data");
+ test.done();
+ }
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ let elt = test.getItemElt(popup, item);
+
+ // create a command event
+ let evt = elt.ownerDocument.createEvent('Event');
+ evt.initEvent('command', true, true);
+ elt.dispatchEvent(evt);
+ });
+};
+
+
+// A menu's click listener should work and receive bubbling 'command' events from
+// sub-items appropriately. This also tests menus and ensures that when a CSS
+// selector context matches the clicked node's ancestor, the matching ancestor
+// is passed to listeners as the clicked node.
+exports.testMenuCommand = function (assert, done) {
+ // Create a top-level menu, submenu, and item, like this:
+ // topMenu -> submenu -> item
+ // Click the item and make sure the click bubbles.
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "submenu item",
+ data: "submenu item data",
+ context: loader.cm.SelectorContext("a"),
+ });
+
+ let submenu = new loader.cm.Menu({
+ label: "submenu",
+ context: loader.cm.SelectorContext("a"),
+ items: [item]
+ });
+
+ let topMenu = new loader.cm.Menu({
+ label: "top menu",
+ contentScript: 'self.on("click", function (node, data) {' +
+ ' self.postMessage({' +
+ ' tagName: node.tagName,' +
+ ' data: data' +
+ ' });' +
+ '});',
+ onMessage: function (data) {
+ assert.equal(this, topMenu, "`this` inside top menu should be menu");
+ assert.equal(data.tagName, "A", "Clicked node should be anchor");
+ assert.equal(data.data, item.data,
+ "Clicked item data should be correct");
+ test.done();
+ },
+ items: [submenu],
+ context: loader.cm.SelectorContext("a")
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#span-link", function (popup) {
+ test.checkMenu([topMenu], [], []);
+ let topMenuElt = test.getItemElt(popup, topMenu);
+ let topMenuPopup = topMenuElt.firstChild;
+ let submenuElt = test.getItemElt(topMenuPopup, submenu);
+ let submenuPopup = submenuElt.firstChild;
+ let itemElt = test.getItemElt(submenuPopup, item);
+
+ // create a command event
+ let evt = itemElt.ownerDocument.createEvent('Event');
+ evt.initEvent('command', true, true);
+ itemElt.dispatchEvent(evt);
+ });
+ });
+};
+
+
+// Click listeners should work when multiple modules are loaded.
+exports.testItemCommandMultipleModules = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let item0 = loader0.cm.Item({
+ label: "loader 0 item",
+ contentScript: 'self.on("click", self.postMessage);',
+ onMessage: function () {
+ test.fail("loader 0 item should not emit click event");
+ }
+ });
+ let item1 = loader1.cm.Item({
+ label: "loader 1 item",
+ contentScript: 'self.on("click", self.postMessage);',
+ onMessage: function () {
+ test.pass("loader 1 item clicked as expected");
+ test.done();
+ }
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item0, item1], [], []);
+ let item1Elt = test.getItemElt(popup, item1);
+
+ // create a command event
+ let evt = item1Elt.ownerDocument.createEvent('Event');
+ evt.initEvent('command', true, true);
+ item1Elt.dispatchEvent(evt);
+ });
+};
+
+
+
+
+// An item's click listener should work.
+exports.testItemClick = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ data: "item data",
+ contentScript: 'self.on("click", function (node, data) {' +
+ ' self.postMessage({' +
+ ' tagName: node.tagName,' +
+ ' data: data' +
+ ' });' +
+ '});',
+ onMessage: function (data) {
+ assert.equal(this, item, "`this` inside onMessage should be item");
+ assert.equal(data.tagName, "HTML", "node should be an HTML element");
+ assert.equal(data.data, item.data, "data should be item data");
+ test.done();
+ }
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ let elt = test.getItemElt(popup, item);
+ elt.click();
+ });
+};
+
+
+// A menu's click listener should work and receive bubbling clicks from
+// sub-items appropriately. This also tests menus and ensures that when a CSS
+// selector context matches the clicked node's ancestor, the matching ancestor
+// is passed to listeners as the clicked node.
+exports.testMenuClick = function (assert, done) {
+ // Create a top-level menu, submenu, and item, like this:
+ // topMenu -> submenu -> item
+ // Click the item and make sure the click bubbles.
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "submenu item",
+ data: "submenu item data",
+ context: loader.cm.SelectorContext("a"),
+ });
+
+ let submenu = new loader.cm.Menu({
+ label: "submenu",
+ context: loader.cm.SelectorContext("a"),
+ items: [item]
+ });
+
+ let topMenu = new loader.cm.Menu({
+ label: "top menu",
+ contentScript: 'self.on("click", function (node, data) {' +
+ ' self.postMessage({' +
+ ' tagName: node.tagName,' +
+ ' data: data' +
+ ' });' +
+ '});',
+ onMessage: function (data) {
+ assert.equal(this, topMenu, "`this` inside top menu should be menu");
+ assert.equal(data.tagName, "A", "Clicked node should be anchor");
+ assert.equal(data.data, item.data,
+ "Clicked item data should be correct");
+ test.done();
+ },
+ items: [submenu],
+ context: loader.cm.SelectorContext("a")
+ });
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#span-link", function (popup) {
+ test.checkMenu([topMenu], [], []);
+ let topMenuElt = test.getItemElt(popup, topMenu);
+ let topMenuPopup = topMenuElt.firstChild;
+ let submenuElt = test.getItemElt(topMenuPopup, submenu);
+ let submenuPopup = submenuElt.firstChild;
+ let itemElt = test.getItemElt(submenuPopup, item);
+ itemElt.click();
+ });
+ });
+};
+
+// Click listeners should work when multiple modules are loaded.
+exports.testItemClickMultipleModules = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let item0 = loader0.cm.Item({
+ label: "loader 0 item",
+ contentScript: 'self.on("click", self.postMessage);',
+ onMessage: function () {
+ test.fail("loader 0 item should not emit click event");
+ }
+ });
+ let item1 = loader1.cm.Item({
+ label: "loader 1 item",
+ contentScript: 'self.on("click", self.postMessage);',
+ onMessage: function () {
+ test.pass("loader 1 item clicked as expected");
+ test.done();
+ }
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item0, item1], [], []);
+ let item1Elt = test.getItemElt(popup, item1);
+ item1Elt.click();
+ });
+};
+
+
+// Adding a separator to a submenu should work OK.
+exports.testSeparator = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let menu = new loader.cm.Menu({
+ label: "submenu",
+ items: [new loader.cm.Separator()]
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [], []);
+ test.done();
+ });
+};
+
+
+// The parentMenu option should work
+exports.testParentMenu = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let menu = new loader.cm.Menu({
+ label: "submenu",
+ items: [loader.cm.Item({ label: "item 1" })],
+ parentMenu: loader.cm.contentContextMenu
+ });
+
+ let item = loader.cm.Item({
+ label: "item 2",
+ parentMenu: menu,
+ });
+
+ assert.equal(menu.items[1], item, "Item should be in the sub menu");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [], []);
+ test.done();
+ });
+};
+
+
+// Existing context menu modifications should apply to new windows.
+exports.testNewWindow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({ label: "item" });
+
+ test.withNewWindow(function () {
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+};
+
+
+// When a new window is opened, items added by an unloaded module should not
+// be present in the menu.
+exports.testNewWindowMultipleModules = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+ let item = new loader.cm.Item({ label: "item" });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+ loader.unload();
+ test.withNewWindow(function () {
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], [item]);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Existing context menu modifications should not apply to new private windows.
+exports.testNewPrivateWindow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({ label: "item" });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ test.withNewPrivateWindow(function () {
+ test.showMenu(null, function (popup) {
+ test.checkMenu([], [], []);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Existing context menu modifications should apply to new private windows when
+// private browsing support is enabled.
+exports.testNewPrivateEnabledWindow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newPrivateLoader();
+
+ let item = new loader.cm.Item({ label: "item" });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ test.withNewPrivateWindow(function () {
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Existing context menu modifications should apply to new private windows when
+// private browsing support is enabled unless unloaded.
+exports.testNewPrivateEnabledWindowUnloaded = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newPrivateLoader();
+
+ let item = new loader.cm.Item({ label: "item" });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ popup.hidePopup();
+
+ loader.unload();
+
+ test.withNewPrivateWindow(function () {
+ test.showMenu(null, function (popup) {
+ test.checkMenu([], [], []);
+ test.done();
+ });
+ });
+ });
+};
+
+
+// Items in the context menu should be sorted according to locale.
+exports.testSorting = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ // Make an unsorted items list. It'll look like this:
+ // item 1, item 0, item 3, item 2, item 5, item 4, ...
+ let items = [];
+ for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i += 2) {
+ items.push(new loader.cm.Item({ label: "item " + (i + 1) }));
+ items.push(new loader.cm.Item({ label: "item " + i }));
+ }
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+};
+
+
+// Items in the overflow menu should be sorted according to locale.
+exports.testSortingOverflow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ // Make an unsorted items list. It'll look like this:
+ // item 1, item 0, item 3, item 2, item 5, item 4, ...
+ let items = [];
+ for (let i = 0; i < OVERFLOW_THRESH_DEFAULT * 2; i += 2) {
+ items.push(new loader.cm.Item({ label: "item " + (i + 1) }));
+ items.push(new loader.cm.Item({ label: "item " + i }));
+ }
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+};
+
+
+// Multiple modules shouldn't interfere with sorting.
+exports.testSortingMultipleModules = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader0 = test.newLoader();
+ let loader1 = test.newLoader();
+
+ let items0 = [];
+ let items1 = [];
+ for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) {
+ if (i % 2) {
+ let item = new loader0.cm.Item({ label: "item " + i });
+ items0.push(item);
+ }
+ else {
+ let item = new loader1.cm.Item({ label: "item " + i });
+ items1.push(item);
+ }
+ }
+ let allItems = items0.concat(items1);
+
+ test.showMenu(null, function (popup) {
+
+ // All items should be present and sorted.
+ test.checkMenu(allItems, [], []);
+ popup.hidePopup();
+ loader0.unload();
+ loader1.unload();
+ test.showMenu(null, function (popup) {
+
+ // All items should be removed.
+ test.checkMenu(allItems, [], allItems);
+ test.done();
+ });
+ });
+};
+
+
+// Content click handlers and context handlers should be able to communicate,
+// i.e., they're eval'ed in the same worker and sandbox.
+exports.testContentCommunication = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript: 'var potato;' +
+ 'self.on("context", function () {' +
+ ' potato = "potato";' +
+ ' return true;' +
+ '});' +
+ 'self.on("click", function () {' +
+ ' self.postMessage(potato);' +
+ '});',
+ });
+
+ item.on("message", function (data) {
+ assert.equal(data, "potato", "That's a lot of potatoes!");
+ test.done();
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ let elt = test.getItemElt(popup, item);
+ elt.click();
+ });
+};
+
+
+// When the context menu is invoked on a tab that was already open when the
+// module was loaded, it should contain the expected items and content workers
+// should function as expected.
+exports.testLoadWithOpenTab = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ test.withTestDoc(function (window, doc) {
+ let loader = test.newLoader();
+ let item = new loader.cm.Item({
+ label: "item",
+ contentScript:
+ 'self.on("click", () => self.postMessage("click"));',
+ onMessage: function (msg) {
+ if (msg === "click")
+ test.done();
+ }
+ });
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ test.getItemElt(popup, item).click();
+ });
+ });
+};
+
+// Bug 732716: Ensure that the node given in `click` event works fine
+// (i.e. is correctly wrapped)
+exports.testDrawImageOnClickNode = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ test.withTestDoc(function (window, doc) {
+ let loader = test.newLoader();
+ let item = new loader.cm.Item({
+ label: "item",
+ context: loader.cm.SelectorContext("img"),
+ contentScript: "new " + function() {
+ self.on("click", function (img, data) {
+ let ctx = document.createElement("canvas").getContext("2d");
+ ctx.drawImage(img, 1, 1, 1, 1);
+ self.postMessage("done");
+ });
+ },
+ onMessage: function (msg) {
+ if (msg === "done")
+ test.done();
+ }
+ });
+ test.showMenu("#image", function (popup) {
+ test.checkMenu([item], [], []);
+ test.getItemElt(popup, item).click();
+ });
+ });
+};
+
+
+// Setting an item's label before the menu is ever shown should correctly change
+// its label.
+exports.testSetLabelBeforeShow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ new loader.cm.Item({ label: "a" }),
+ new loader.cm.Item({ label: "b" })
+ ]
+ items[0].label = "z";
+ assert.equal(items[0].label, "z");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+};
+
+
+// Setting an item's label after the menu is shown should correctly change its
+// label.
+exports.testSetLabelAfterShow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ new loader.cm.Item({ label: "a" }),
+ new loader.cm.Item({ label: "b" })
+ ];
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ popup.hidePopup();
+
+ items[0].label = "z";
+ assert.equal(items[0].label, "z");
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Setting an item's label before the menu is ever shown should correctly change
+// its label.
+exports.testSetLabelBeforeShowOverflow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let prefs = loader.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 0);
+
+ let items = [
+ new loader.cm.Item({ label: "a" }),
+ new loader.cm.Item({ label: "b" })
+ ]
+ items[0].label = "z";
+ assert.equal(items[0].label, "z");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+};
+
+
+// Setting an item's label after the menu is shown should correctly change its
+// label.
+exports.testSetLabelAfterShowOverflow = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let prefs = loader.loader.require("sdk/preferences/service");
+ prefs.set(OVERFLOW_THRESH_PREF, 0);
+
+ let items = [
+ new loader.cm.Item({ label: "a" }),
+ new loader.cm.Item({ label: "b" })
+ ];
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ popup.hidePopup();
+
+ items[0].label = "z";
+ assert.equal(items[0].label, "z");
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Setting the label of an item in a Menu should work.
+exports.testSetLabelMenuItem = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let menu = loader.cm.Menu({
+ label: "menu",
+ items: [loader.cm.Item({ label: "a" })]
+ });
+ menu.items[0].label = "z";
+
+ assert.equal(menu.items[0].label, "z");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [], []);
+ test.done();
+ });
+};
+
+
+// Menu.addItem() should work.
+exports.testMenuAddItem = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let menu = loader.cm.Menu({
+ label: "menu",
+ items: [
+ loader.cm.Item({ label: "item 0" })
+ ]
+ });
+ menu.addItem(loader.cm.Item({ label: "item 1" }));
+ menu.addItem(loader.cm.Item({ label: "item 2" }));
+
+ assert.equal(menu.items.length, 3,
+ "menu should have correct number of items");
+ for (let i = 0; i < 3; i++) {
+ assert.equal(menu.items[i].label, "item " + i,
+ "item label should be correct");
+ assert.equal(menu.items[i].parentMenu, menu,
+ "item's parent menu should be correct");
+ }
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [], []);
+ test.done();
+ });
+};
+
+
+// Adding the same item twice to a menu should work as expected.
+exports.testMenuAddItemTwice = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let menu = loader.cm.Menu({
+ label: "menu",
+ items: []
+ });
+ let subitem = loader.cm.Item({ label: "item 1" })
+ menu.addItem(subitem);
+ menu.addItem(loader.cm.Item({ label: "item 0" }));
+ menu.addItem(subitem);
+
+ assert.equal(menu.items.length, 2,
+ "menu should have correct number of items");
+ for (let i = 0; i < 2; i++) {
+ assert.equal(menu.items[i].label, "item " + i,
+ "item label should be correct");
+ }
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [], []);
+ test.done();
+ });
+};
+
+
+// Menu.removeItem() should work.
+exports.testMenuRemoveItem = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let subitem = loader.cm.Item({ label: "item 1" });
+ let menu = loader.cm.Menu({
+ label: "menu",
+ items: [
+ loader.cm.Item({ label: "item 0" }),
+ subitem,
+ loader.cm.Item({ label: "item 2" })
+ ]
+ });
+
+ // Removing twice should be harmless.
+ menu.removeItem(subitem);
+ menu.removeItem(subitem);
+
+ assert.equal(subitem.parentMenu, null,
+ "item's parent menu should be correct");
+
+ assert.equal(menu.items.length, 2,
+ "menu should have correct number of items");
+ assert.equal(menu.items[0].label, "item 0",
+ "item label should be correct");
+ assert.equal(menu.items[1].label, "item 2",
+ "item label should be correct");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [], []);
+ test.done();
+ });
+};
+
+
+// Adding an item currently contained in one menu to another menu should work.
+exports.testMenuItemSwap = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let subitem = loader.cm.Item({ label: "item" });
+ let menu0 = loader.cm.Menu({
+ label: "menu 0",
+ items: [subitem]
+ });
+ let menu1 = loader.cm.Menu({
+ label: "menu 1",
+ items: []
+ });
+ menu1.addItem(subitem);
+
+ assert.equal(menu0.items.length, 0,
+ "menu should have correct number of items");
+
+ assert.equal(menu1.items.length, 1,
+ "menu should have correct number of items");
+ assert.equal(menu1.items[0].label, "item",
+ "item label should be correct");
+
+ assert.equal(subitem.parentMenu, menu1,
+ "item's parent menu should be correct");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu0, menu1], [menu0], []);
+ test.done();
+ });
+};
+
+
+// Destroying an item should remove it from its parent menu.
+exports.testMenuItemDestroy = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let subitem = loader.cm.Item({ label: "item" });
+ let menu = loader.cm.Menu({
+ label: "menu",
+ items: [subitem]
+ });
+ subitem.destroy();
+
+ assert.equal(menu.items.length, 0,
+ "menu should have correct number of items");
+ assert.equal(subitem.parentMenu, null,
+ "item's parent menu should be correct");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [menu], []);
+ test.done();
+ });
+};
+
+
+// Setting Menu.items should work.
+exports.testMenuItemsSetter = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let menu = loader.cm.Menu({
+ label: "menu",
+ items: [
+ loader.cm.Item({ label: "old item 0" }),
+ loader.cm.Item({ label: "old item 1" })
+ ]
+ });
+ menu.items = [
+ loader.cm.Item({ label: "new item 0" }),
+ loader.cm.Item({ label: "new item 1" }),
+ loader.cm.Item({ label: "new item 2" })
+ ];
+
+ assert.equal(menu.items.length, 3,
+ "menu should have correct number of items");
+ for (let i = 0; i < 3; i++) {
+ assert.equal(menu.items[i].label, "new item " + i,
+ "item label should be correct");
+ assert.equal(menu.items[i].parentMenu, menu,
+ "item's parent menu should be correct");
+ }
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [], []);
+ test.done();
+ });
+};
+
+
+// Setting Item.data should work.
+exports.testItemDataSetter = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = loader.cm.Item({ label: "old item 0", data: "old" });
+ item.data = "new";
+
+ assert.equal(item.data, "new", "item should have correct data");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+};
+
+
+// Open the test doc, load the module, make sure items appear when context-
+// clicking the iframe.
+exports.testAlreadyOpenIframe = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ test.withTestDoc(function (window, doc) {
+ let loader = test.newLoader();
+ let item = new loader.cm.Item({
+ label: "item"
+ });
+ test.showMenu("#iframe", function (popup) {
+ test.checkMenu([item], [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Tests that a missing label throws an exception
+exports.testItemNoLabel = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ try {
+ new loader.cm.Item({});
+ assert.ok(false, "Should have seen exception");
+ }
+ catch (e) {
+ assert.ok(true, "Should have seen exception");
+ }
+
+ try {
+ new loader.cm.Item({ label: null });
+ assert.ok(false, "Should have seen exception");
+ }
+ catch (e) {
+ assert.ok(true, "Should have seen exception");
+ }
+
+ try {
+ new loader.cm.Item({ label: undefined });
+ assert.ok(false, "Should have seen exception");
+ }
+ catch (e) {
+ assert.ok(true, "Should have seen exception");
+ }
+
+ try {
+ new loader.cm.Item({ label: "" });
+ assert.ok(false, "Should have seen exception");
+ }
+ catch (e) {
+ assert.ok(true, "Should have seen exception");
+ }
+
+ test.done();
+}
+
+/* bug 1302854 - disabled this subtest as it is intermittent
+// Tests that items can have an empty data property
+exports.testItemNoData = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ function checkData(data) {
+ assert.equal(data, undefined, "Data should be undefined");
+ }
+
+ let item1 = new loader.cm.Item({
+ label: "item 1",
+ contentScript: 'self.on("click", (node, data) => self.postMessage(data))',
+ onMessage: checkData
+ });
+ let item2 = new loader.cm.Item({
+ label: "item 2",
+ data: null,
+ contentScript: 'self.on("click", (node, data) => self.postMessage(data))',
+ onMessage: checkData
+ });
+ let item3 = new loader.cm.Item({
+ label: "item 3",
+ data: undefined,
+ contentScript: 'self.on("click", (node, data) => self.postMessage(data))',
+ onMessage: checkData
+ });
+
+ assert.equal(item1.data, undefined, "Should be no defined data");
+ assert.equal(item2.data, null, "Should be no defined data");
+ assert.equal(item3.data, undefined, "Should be no defined data");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item1, item2, item3], [], []);
+
+ let itemElt = test.getItemElt(popup, item1);
+ itemElt.click();
+
+ test.hideMenu(function() {
+ test.showMenu(null, function (popup) {
+ let itemElt = test.getItemElt(popup, item2);
+ itemElt.click();
+
+ test.hideMenu(function() {
+ test.showMenu(null, function (popup) {
+ let itemElt = test.getItemElt(popup, item3);
+ itemElt.click();
+
+ test.done();
+ });
+ });
+ });
+ });
+ });
+}
+*/
+
+
+exports.testItemNoAccessKey = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item1 = new loader.cm.Item({ label: "item 1" });
+ let item2 = new loader.cm.Item({ label: "item 2", accesskey: null });
+ let item3 = new loader.cm.Item({ label: "item 3", accesskey: undefined });
+
+ assert.equal(item1.accesskey, undefined, "Should be no defined image");
+ assert.equal(item2.accesskey, null, "Should be no defined image");
+ assert.equal(item3.accesskey, undefined, "Should be no defined image");
+
+ test.showMenu().
+ then((popup) => test.checkMenu([item1, item2, item3], [], [])).
+ then(test.done).
+ catch(assert.fail);
+}
+
+
+// Test accesskey support.
+exports.testItemAccessKey = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item = new loader.cm.Item({ label: "item", accesskey: "i" });
+ assert.equal(item.accesskey, "i", "Should have set the image to i");
+
+ let menu = new loader.cm.Menu({ label: "menu", accesskey: "m", items: [
+ loader.cm.Item({ label: "subitem" })
+ ]});
+ assert.equal(menu.accesskey, "m", "Should have set the accesskey to m");
+
+ test.showMenu().then((popup) => {
+ test.checkMenu([item, menu], [], []);
+
+ let accesskey = "e";
+ menu.accesskey = item.accesskey = accesskey;
+ assert.equal(item.accesskey, accesskey, "Should have set the accesskey to " + accesskey);
+ assert.equal(menu.accesskey, accesskey, "Should have set the accesskey to " + accesskey);
+ test.checkMenu([item, menu], [], []);
+
+ item.accesskey = null;
+ menu.accesskey = null;
+ assert.equal(item.accesskey, null, "Should have set the accesskey to " + accesskey);
+ assert.equal(menu.accesskey, null, "Should have set the accesskey to " + accesskey);
+ test.checkMenu([item, menu], [], []);
+ }).
+ then(test.done).
+ catch(assert.fail);
+};
+
+
+// Tests that items without an image don't attempt to show one
+exports.testItemNoImage = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let item1 = new loader.cm.Item({ label: "item 1" });
+ let item2 = new loader.cm.Item({ label: "item 2", image: null });
+ let item3 = new loader.cm.Item({ label: "item 3", image: undefined });
+
+ assert.equal(item1.image, undefined, "Should be no defined image");
+ assert.equal(item2.image, null, "Should be no defined image");
+ assert.equal(item3.image, undefined, "Should be no defined image");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item1, item2, item3], [], []);
+
+ test.done();
+ });
+}
+
+
+// Test image support.
+exports.testItemImage = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let imageURL = data.url("moz_favicon.ico");
+ let item = new loader.cm.Item({ label: "item", image: imageURL });
+ let menu = new loader.cm.Menu({ label: "menu", image: imageURL, items: [
+ loader.cm.Item({ label: "subitem" })
+ ]});
+ assert.equal(item.image, imageURL, "Should have set the image correctly");
+ assert.equal(menu.image, imageURL, "Should have set the image correctly");
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([item, menu], [], []);
+
+ let imageURL2 = data.url("dummy.ico");
+ item.image = imageURL2;
+ menu.image = imageURL2;
+ assert.equal(item.image, imageURL2, "Should have set the image correctly");
+ assert.equal(menu.image, imageURL2, "Should have set the image correctly");
+ test.checkMenu([item, menu], [], []);
+
+ item.image = null;
+ menu.image = null;
+ assert.equal(item.image, null, "Should have set the image correctly");
+ assert.equal(menu.image, null, "Should have set the image correctly");
+ test.checkMenu([item, menu], [], []);
+
+ test.done();
+ });
+};
+
+// Test image URL validation.
+exports.testItemImageValidURL = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ assert.throws(function(){
+ new loader.cm.Item({
+ label: "item 1",
+ image: "foo"
+ })
+ }, /Image URL validation failed/
+ );
+
+ assert.throws(function(){
+ new loader.cm.Item({
+ label: "item 2",
+ image: false
+ })
+ }, /Image URL validation failed/
+ );
+
+ assert.throws(function(){
+ new loader.cm.Item({
+ label: "item 3",
+ image: 0
+ })
+ }, /Image URL validation failed/
+ );
+
+ let imageURL = data.url("moz_favicon.ico");
+ let item4 = new loader.cm.Item({ label: "item 4", image: imageURL });
+ let item5 = new loader.cm.Item({ label: "item 5", image: null });
+ let item6 = new loader.cm.Item({ label: "item 6", image: undefined });
+
+ assert.equal(item4.image, imageURL, "Should be proper image URL");
+ assert.equal(item5.image, null, "Should be null image");
+ assert.equal(item6.image, undefined, "Should be undefined image");
+
+ test.done();
+};
+
+
+// Menu.destroy should destroy the item tree rooted at that menu.
+exports.testMenuDestroy = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let menu = loader.cm.Menu({
+ label: "menu",
+ items: [
+ loader.cm.Item({ label: "item 0" }),
+ loader.cm.Menu({
+ label: "item 1",
+ items: [
+ loader.cm.Item({ label: "subitem 0" }),
+ loader.cm.Item({ label: "subitem 1" }),
+ loader.cm.Item({ label: "subitem 2" })
+ ]
+ }),
+ loader.cm.Item({ label: "item 2" })
+ ]
+ });
+ menu.destroy();
+
+ /*let numRegistryEntries = 0;
+ loader.globalScope.browserManager.browserWins.forEach(function (bwin) {
+ for (let itemID in bwin.items)
+ numRegistryEntries++;
+ });
+ assert.equal(numRegistryEntries, 0, "All items should be unregistered.");*/
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([menu], [], [menu]);
+ test.done();
+ });
+};
+
+// Checks that if a menu contains sub items that are hidden then the menu is
+// hidden too. Also checks that content scripts and contexts work for sub items.
+exports.testSubItemContextNoMatchHideMenu = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ loader.cm.Menu({
+ label: "menu 1",
+ items: [
+ loader.cm.Item({
+ label: "subitem 1",
+ context: loader.cm.SelectorContext(".foo")
+ })
+ ]
+ }),
+ loader.cm.Menu({
+ label: "menu 2",
+ items: [
+ loader.cm.Item({
+ label: "subitem 2",
+ contentScript: 'self.on("context", () => false);'
+ })
+ ]
+ }),
+ loader.cm.Menu({
+ label: "menu 3",
+ items: [
+ loader.cm.Item({
+ label: "subitem 3",
+ context: loader.cm.SelectorContext(".foo")
+ }),
+ loader.cm.Item({
+ label: "subitem 4",
+ contentScript: 'self.on("context", () => false);'
+ })
+ ]
+ })
+ ];
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, items, []);
+ test.done();
+ });
+};
+
+
+// Checks that if a menu contains a combination of hidden and visible sub items
+// then the menu is still visible too.
+exports.testSubItemContextMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let hiddenItems = [
+ loader.cm.Item({
+ label: "subitem 3",
+ context: loader.cm.SelectorContext(".foo")
+ }),
+ loader.cm.Item({
+ label: "subitem 6",
+ contentScript: 'self.on("context", () => false);'
+ })
+ ];
+
+ let items = [
+ loader.cm.Menu({
+ label: "menu 1",
+ items: [
+ loader.cm.Item({
+ label: "subitem 1",
+ context: loader.cm.URLContext(TEST_DOC_URL)
+ })
+ ]
+ }),
+ loader.cm.Menu({
+ label: "menu 2",
+ items: [
+ loader.cm.Item({
+ label: "subitem 2",
+ contentScript: 'self.on("context", () => true);'
+ })
+ ]
+ }),
+ loader.cm.Menu({
+ label: "menu 3",
+ items: [
+ hiddenItems[0],
+ loader.cm.Item({
+ label: "subitem 4",
+ contentScript: 'self.on("context", () => true);'
+ })
+ ]
+ }),
+ loader.cm.Menu({
+ label: "menu 4",
+ items: [
+ loader.cm.Item({
+ label: "subitem 5",
+ context: loader.cm.URLContext(TEST_DOC_URL)
+ }),
+ hiddenItems[1]
+ ]
+ }),
+ loader.cm.Menu({
+ label: "menu 5",
+ items: [
+ loader.cm.Item({
+ label: "subitem 7",
+ context: loader.cm.URLContext(TEST_DOC_URL)
+ }),
+ loader.cm.Item({
+ label: "subitem 8",
+ contentScript: 'self.on("context", () => true);'
+ })
+ ]
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, hiddenItems, []);
+ test.done();
+ });
+ });
+};
+
+
+// Child items should default to visible, not to PageContext
+exports.testSubItemDefaultVisible = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [
+ loader.cm.Menu({
+ label: "menu 1",
+ context: loader.cm.SelectorContext("img"),
+ items: [
+ loader.cm.Item({
+ label: "subitem 1"
+ }),
+ loader.cm.Item({
+ label: "subitem 2",
+ context: loader.cm.SelectorContext("img")
+ }),
+ loader.cm.Item({
+ label: "subitem 3",
+ context: loader.cm.SelectorContext("a")
+ })
+ ]
+ })
+ ];
+
+ // subitem 3 will be hidden
+ let hiddenItems = [items[0].items[2]];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, hiddenItems, []);
+ test.done();
+ });
+ });
+};
+
+// Tests that the click event on sub menuitem
+// tiggers the click event for the sub menuitem and the parent menu
+exports.testSubItemClick = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let state = 0;
+
+ let items = [
+ loader.cm.Menu({
+ label: "menu 1",
+ items: [
+ loader.cm.Item({
+ label: "subitem 1",
+ data: "foobar",
+ contentScript: 'self.on("click", function (node, data) {' +
+ ' self.postMessage({' +
+ ' tagName: node.tagName,' +
+ ' data: data' +
+ ' });' +
+ '});',
+ onMessage: function(msg) {
+ assert.equal(msg.tagName, "HTML", "should have seen the right node");
+ assert.equal(msg.data, "foobar", "should have seen the right data");
+ assert.equal(state, 0, "should have seen the event at the right time");
+ state++;
+ }
+ })
+ ],
+ contentScript: 'self.on("click", function (node, data) {' +
+ ' self.postMessage({' +
+ ' tagName: node.tagName,' +
+ ' data: data' +
+ ' });' +
+ '});',
+ onMessage: function(msg) {
+ assert.equal(msg.tagName, "HTML", "should have seen the right node");
+ assert.equal(msg.data, "foobar", "should have seen the right data");
+ assert.equal(state, 1, "should have seen the event at the right time");
+
+ test.done();
+ }
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+
+ let topMenuElt = test.getItemElt(popup, items[0]);
+ let topMenuPopup = topMenuElt.firstChild;
+ let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]);
+ itemElt.click();
+ });
+ });
+};
+
+// Tests that the command event on sub menuitem
+// tiggers the click event for the sub menuitem and the parent menu
+exports.testSubItemCommand = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let state = 0;
+
+ let items = [
+ loader.cm.Menu({
+ label: "menu 1",
+ items: [
+ loader.cm.Item({
+ label: "subitem 1",
+ data: "foobar",
+ contentScript: 'self.on("click", function (node, data) {' +
+ ' self.postMessage({' +
+ ' tagName: node.tagName,' +
+ ' data: data' +
+ ' });' +
+ '});',
+ onMessage: function(msg) {
+ assert.equal(msg.tagName, "HTML", "should have seen the right node");
+ assert.equal(msg.data, "foobar", "should have seen the right data");
+ assert.equal(state, 0, "should have seen the event at the right time");
+ state++;
+ }
+ })
+ ],
+ contentScript: 'self.on("click", function (node, data) {' +
+ ' self.postMessage({' +
+ ' tagName: node.tagName,' +
+ ' data: data' +
+ ' });' +
+ '});',
+ onMessage: function(msg) {
+ assert.equal(msg.tagName, "HTML", "should have seen the right node");
+ assert.equal(msg.data, "foobar", "should have seen the right data");
+ assert.equal(state, 1, "should have seen the event at the right time");
+ state++
+
+ test.done();
+ }
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+
+ let topMenuElt = test.getItemElt(popup, items[0]);
+ let topMenuPopup = topMenuElt.firstChild;
+ let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]);
+
+ // create a command event
+ let evt = itemElt.ownerDocument.createEvent('Event');
+ evt.initEvent('command', true, true);
+ itemElt.dispatchEvent(evt);
+ });
+ });
+};
+
+// Tests that opening a context menu for an outer frame when an inner frame
+// has a selection doesn't activate the SelectionContext
+exports.testSelectionInInnerFrameNoMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let state = 0;
+
+ let items = [
+ loader.cm.Item({
+ label: "test item",
+ context: loader.cm.SelectionContext()
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ let frame = doc.getElementById("iframe");
+ frame.contentWindow.getSelection().selectAllChildren(frame.contentDocument.body);
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, items, []);
+ test.done();
+ });
+ });
+};
+
+// Tests that opening a context menu for an inner frame when the inner frame
+// has a selection does activate the SelectionContext
+exports.testSelectionInInnerFrameMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let state = 0;
+
+ let items = [
+ loader.cm.Item({
+ label: "test item",
+ context: loader.cm.SelectionContext()
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ let frame = doc.getElementById("iframe");
+ frame.contentWindow.getSelection().selectAllChildren(frame.contentDocument.body);
+
+ test.showMenu(["#iframe", "#text"], function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Tests that opening a context menu for an inner frame when the outer frame
+// has a selection doesn't activate the SelectionContext
+exports.testSelectionInOuterFrameNoMatch = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let state = 0;
+
+ let items = [
+ loader.cm.Item({
+ label: "test item",
+ context: loader.cm.SelectionContext()
+ })
+ ];
+
+ test.withTestDoc(function (window, doc) {
+ let frame = doc.getElementById("iframe");
+ window.getSelection().selectAllChildren(doc.body);
+
+ test.showMenu(["#iframe", "#text"], function (popup) {
+ test.checkMenu(items, items, []);
+ test.done();
+ });
+ });
+};
+
+
+// Test that the return value of the predicate function determines if
+// item is shown
+exports.testPredicateContextControl = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let itemTrue = loader.cm.Item({
+ label: "visible",
+ context: loader.cm.PredicateContext(function () { return true; })
+ });
+
+ let itemFalse = loader.cm.Item({
+ label: "hidden",
+ context: loader.cm.PredicateContext(function () { return false; })
+ });
+
+ test.showMenu(null, function (popup) {
+ test.checkMenu([itemTrue, itemFalse], [itemFalse], []);
+ test.done();
+ });
+};
+
+// Test that the data object has the correct document type
+exports.testPredicateContextDocumentType = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.equal(data.documentType, 'text/html');
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has the correct document URL
+exports.testPredicateContextDocumentURL = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.equal(data.documentURL, TEST_DOC_URL);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Test that the data object has the correct element name
+exports.testPredicateContextTargetName = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.targetName, "input");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#button", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Test that the data object has the correct ID
+exports.testPredicateContextTargetIDSet = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.targetID, "button");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#button", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has the correct ID
+exports.testPredicateContextTargetIDNotSet = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.targetID, null);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(".predicate-test-a", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object is showing editable correctly for regular text inputs
+exports.testPredicateContextTextBoxIsEditable = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.isEditable, true);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#textbox", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object is showing editable correctly for readonly text inputs
+exports.testPredicateContextReadonlyTextBoxIsNotEditable = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.isEditable, false);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#readonly-textbox", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object is showing editable correctly for disabled text inputs
+exports.testPredicateContextDisabledTextBoxIsNotEditable = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.isEditable, false);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#disabled-textbox", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object is showing editable correctly for text areas
+exports.testPredicateContextTextAreaIsEditable = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.isEditable, true);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#textfield", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that non-text inputs are not considered editable
+exports.testPredicateContextButtonIsNotEditable = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.isEditable, false);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#button", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Test that the data object is showing editable correctly
+exports.testPredicateContextNonInputIsNotEditable = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.isEditable, false);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Test that the data object is showing editable correctly for HTML contenteditable elements
+exports.testPredicateContextEditableElement = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.isEditable, true);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#editable", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Test that the data object does not have a selection when there is none
+exports.testPredicateContextNoSelectionInPage = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.selectionText, null);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object includes the selected page text
+exports.testPredicateContextSelectionInPage = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ // since we might get whitespace
+ assert.ok(data.selectionText && data.selectionText.search(/^\s*Some text.\s*$/) != -1,
+ 'Expected "Some text.", got "' + data.selectionText + '"');
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ window.getSelection().selectAllChildren(doc.getElementById("text"));
+ test.showMenu(null, function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object includes the selected input text
+exports.testPredicateContextSelectionInTextBox = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ // since we might get whitespace
+ assert.strictEqual(data.selectionText, "t v");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ let textbox = doc.getElementById("textbox");
+ test.selectRange("#textbox", 3, 6);
+ test.showMenu("#textbox", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has the correct src for an image
+exports.testPredicateContextTargetSrcSet = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+ let image;
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.srcURL, image.src);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ image = doc.getElementById("image");
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has no src for a link
+exports.testPredicateContextTargetSrcNotSet = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.srcURL, null);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#link", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+
+// Test that the data object has the correct link set
+exports.testPredicateContextTargetLinkSet = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+ let image;
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.linkURL, TEST_DOC_URL + "#test");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(".predicate-test-a", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has no link for an image
+exports.testPredicateContextTargetLinkNotSet = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.linkURL, null);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has the correct link for a nested image
+exports.testPredicateContextTargetLinkSetNestedImage = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.linkURL, TEST_DOC_URL + "#nested-image");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#predicate-test-nested-image", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has the correct link for a complex nested structure
+exports.testPredicateContextTargetLinkSetNestedStructure = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.linkURL, TEST_DOC_URL + "#nested-structure");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#predicate-test-nested-structure", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has the value for an input textbox
+exports.testPredicateContextTargetValueSet = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+ let image;
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.value, "test value");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#textbox", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has no value for an image
+exports.testPredicateContextTargetValueNotSet = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.value, null);
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu("#image", function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+if (isTravisCI) {
+ module.exports = {
+ "test skip on jpm": (assert) => assert.pass("skipping this file with jpm")
+ };
+}
+
+require('sdk/test').run(exports);