addon-sdk/source/test/test-context-menu.js

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 'use strict';
michael@0 5
michael@0 6 let { Cc, Ci } = require("chrome");
michael@0 7
michael@0 8 require("sdk/context-menu");
michael@0 9
michael@0 10 const { Loader } = require('sdk/test/loader');
michael@0 11 const timer = require("sdk/timers");
michael@0 12 const { merge } = require("sdk/util/object");
michael@0 13
michael@0 14 // These should match the same constants in the module.
michael@0 15 const ITEM_CLASS = "addon-context-menu-item";
michael@0 16 const SEPARATOR_CLASS = "addon-context-menu-separator";
michael@0 17 const OVERFLOW_THRESH_DEFAULT = 10;
michael@0 18 const OVERFLOW_THRESH_PREF =
michael@0 19 "extensions.addon-sdk.context-menu.overflowThreshold";
michael@0 20 const OVERFLOW_MENU_CLASS = "addon-content-menu-overflow-menu";
michael@0 21 const OVERFLOW_POPUP_CLASS = "addon-content-menu-overflow-popup";
michael@0 22
michael@0 23 const TEST_DOC_URL = module.uri.replace(/\.js$/, ".html");
michael@0 24 const data = require("./fixtures");
michael@0 25
michael@0 26 // Tests that when present the separator is placed before the separator from
michael@0 27 // the old context-menu module
michael@0 28 exports.testSeparatorPosition = function (assert, done) {
michael@0 29 let test = new TestHelper(assert, done);
michael@0 30 let loader = test.newLoader();
michael@0 31
michael@0 32 // Create the old separator
michael@0 33 let oldSeparator = test.contextMenuPopup.ownerDocument.createElement("menuseparator");
michael@0 34 oldSeparator.id = "jetpack-context-menu-separator";
michael@0 35 test.contextMenuPopup.appendChild(oldSeparator);
michael@0 36
michael@0 37 // Create an item.
michael@0 38 let item = new loader.cm.Item({ label: "item" });
michael@0 39
michael@0 40 test.showMenu(null, function (popup) {
michael@0 41 assert.equal(test.contextMenuSeparator.nextSibling.nextSibling, oldSeparator,
michael@0 42 "New separator should appear before the old one");
michael@0 43 test.contextMenuPopup.removeChild(oldSeparator);
michael@0 44 test.done();
michael@0 45 });
michael@0 46 };
michael@0 47
michael@0 48 // Destroying items that were previously created should cause them to be absent
michael@0 49 // from the menu.
michael@0 50 exports.testConstructDestroy = function (assert, done) {
michael@0 51 let test = new TestHelper(assert, done);
michael@0 52 let loader = test.newLoader();
michael@0 53
michael@0 54 // Create an item.
michael@0 55 let item = new loader.cm.Item({ label: "item" });
michael@0 56 assert.equal(item.parentMenu, loader.cm.contentContextMenu,
michael@0 57 "item's parent menu should be correct");
michael@0 58
michael@0 59 test.showMenu(null, function (popup) {
michael@0 60
michael@0 61 // It should be present when the menu is shown.
michael@0 62 test.checkMenu([item], [], []);
michael@0 63 popup.hidePopup();
michael@0 64
michael@0 65 // Destroy the item. Multiple destroys should be harmless.
michael@0 66 item.destroy();
michael@0 67 item.destroy();
michael@0 68 test.showMenu(null, function (popup) {
michael@0 69
michael@0 70 // It should be removed from the menu.
michael@0 71 test.checkMenu([item], [], [item]);
michael@0 72 test.done();
michael@0 73 });
michael@0 74 });
michael@0 75 };
michael@0 76
michael@0 77
michael@0 78 // Destroying an item twice should not cause an error.
michael@0 79 exports.testDestroyTwice = function (assert, done) {
michael@0 80 let test = new TestHelper(assert, done);
michael@0 81 let loader = test.newLoader();
michael@0 82
michael@0 83 let item = new loader.cm.Item({ label: "item" });
michael@0 84 item.destroy();
michael@0 85 item.destroy();
michael@0 86
michael@0 87 test.pass("Destroying an item twice should not cause an error.");
michael@0 88 test.done();
michael@0 89 };
michael@0 90
michael@0 91
michael@0 92 // CSS selector contexts should cause their items to be present in the menu
michael@0 93 // when the menu is invoked on nodes that match the selectors.
michael@0 94 exports.testSelectorContextMatch = function (assert, done) {
michael@0 95 let test = new TestHelper(assert, done);
michael@0 96 let loader = test.newLoader();
michael@0 97
michael@0 98 let item = new loader.cm.Item({
michael@0 99 label: "item",
michael@0 100 data: "item",
michael@0 101 context: loader.cm.SelectorContext("img")
michael@0 102 });
michael@0 103
michael@0 104 test.withTestDoc(function (window, doc) {
michael@0 105 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 106 test.checkMenu([item], [], []);
michael@0 107 test.done();
michael@0 108 });
michael@0 109 });
michael@0 110 };
michael@0 111
michael@0 112
michael@0 113 // CSS selector contexts should cause their items to be present in the menu
michael@0 114 // when the menu is invoked on nodes that have ancestors that match the
michael@0 115 // selectors.
michael@0 116 exports.testSelectorAncestorContextMatch = function (assert, done) {
michael@0 117 let test = new TestHelper(assert, done);
michael@0 118 let loader = test.newLoader();
michael@0 119
michael@0 120 let item = new loader.cm.Item({
michael@0 121 label: "item",
michael@0 122 data: "item",
michael@0 123 context: loader.cm.SelectorContext("a[href]")
michael@0 124 });
michael@0 125
michael@0 126 test.withTestDoc(function (window, doc) {
michael@0 127 test.showMenu(doc.getElementById("span-link"), function (popup) {
michael@0 128 test.checkMenu([item], [], []);
michael@0 129 test.done();
michael@0 130 });
michael@0 131 });
michael@0 132 };
michael@0 133
michael@0 134
michael@0 135 // CSS selector contexts should cause their items to be absent from the menu
michael@0 136 // when the menu is not invoked on nodes that match or have ancestors that
michael@0 137 // match the selectors.
michael@0 138 exports.testSelectorContextNoMatch = function (assert, done) {
michael@0 139 let test = new TestHelper(assert, done);
michael@0 140 let loader = test.newLoader();
michael@0 141
michael@0 142 let item = new loader.cm.Item({
michael@0 143 label: "item",
michael@0 144 data: "item",
michael@0 145 context: loader.cm.SelectorContext("img")
michael@0 146 });
michael@0 147
michael@0 148 test.showMenu(null, function (popup) {
michael@0 149 test.checkMenu([item], [item], []);
michael@0 150 test.done();
michael@0 151 });
michael@0 152 };
michael@0 153
michael@0 154
michael@0 155 // Page contexts should cause their items to be present in the menu when the
michael@0 156 // menu is not invoked on an active element.
michael@0 157 exports.testPageContextMatch = function (assert, done) {
michael@0 158 let test = new TestHelper(assert, done);
michael@0 159 let loader = test.newLoader();
michael@0 160
michael@0 161 let items = [
michael@0 162 new loader.cm.Item({
michael@0 163 label: "item 0"
michael@0 164 }),
michael@0 165 new loader.cm.Item({
michael@0 166 label: "item 1",
michael@0 167 context: undefined
michael@0 168 }),
michael@0 169 new loader.cm.Item({
michael@0 170 label: "item 2",
michael@0 171 context: loader.cm.PageContext()
michael@0 172 }),
michael@0 173 new loader.cm.Item({
michael@0 174 label: "item 3",
michael@0 175 context: [loader.cm.PageContext()]
michael@0 176 })
michael@0 177 ];
michael@0 178
michael@0 179 test.showMenu(null, function (popup) {
michael@0 180 test.checkMenu(items, [], []);
michael@0 181 test.done();
michael@0 182 });
michael@0 183 };
michael@0 184
michael@0 185
michael@0 186 // Page contexts should cause their items to be absent from the menu when the
michael@0 187 // menu is invoked on an active element.
michael@0 188 exports.testPageContextNoMatch = function (assert, done) {
michael@0 189 let test = new TestHelper(assert, done);
michael@0 190 let loader = test.newLoader();
michael@0 191
michael@0 192 let items = [
michael@0 193 new loader.cm.Item({
michael@0 194 label: "item 0"
michael@0 195 }),
michael@0 196 new loader.cm.Item({
michael@0 197 label: "item 1",
michael@0 198 context: undefined
michael@0 199 }),
michael@0 200 new loader.cm.Item({
michael@0 201 label: "item 2",
michael@0 202 context: loader.cm.PageContext()
michael@0 203 }),
michael@0 204 new loader.cm.Item({
michael@0 205 label: "item 3",
michael@0 206 context: [loader.cm.PageContext()]
michael@0 207 })
michael@0 208 ];
michael@0 209
michael@0 210 test.withTestDoc(function (window, doc) {
michael@0 211 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 212 test.checkMenu(items, items, []);
michael@0 213 test.done();
michael@0 214 });
michael@0 215 });
michael@0 216 };
michael@0 217
michael@0 218
michael@0 219 // Selection contexts should cause items to appear when a selection exists.
michael@0 220 exports.testSelectionContextMatch = function (assert, done) {
michael@0 221 let test = new TestHelper(assert, done);
michael@0 222 let loader = test.newLoader();
michael@0 223
michael@0 224 let item = loader.cm.Item({
michael@0 225 label: "item",
michael@0 226 context: loader.cm.SelectionContext()
michael@0 227 });
michael@0 228
michael@0 229 test.withTestDoc(function (window, doc) {
michael@0 230 window.getSelection().selectAllChildren(doc.body);
michael@0 231 test.showMenu(null, function (popup) {
michael@0 232 test.checkMenu([item], [], []);
michael@0 233 test.done();
michael@0 234 });
michael@0 235 });
michael@0 236 };
michael@0 237
michael@0 238
michael@0 239 // Selection contexts should cause items to appear when a selection exists in
michael@0 240 // a text field.
michael@0 241 exports.testSelectionContextMatchInTextField = function (assert, done) {
michael@0 242 let test = new TestHelper(assert, done);
michael@0 243 let loader = test.newLoader();
michael@0 244
michael@0 245 let item = loader.cm.Item({
michael@0 246 label: "item",
michael@0 247 context: loader.cm.SelectionContext()
michael@0 248 });
michael@0 249
michael@0 250 test.withTestDoc(function (window, doc) {
michael@0 251 let textfield = doc.getElementById("textfield");
michael@0 252 textfield.setSelectionRange(0, textfield.value.length);
michael@0 253 test.showMenu(textfield, function (popup) {
michael@0 254 test.checkMenu([item], [], []);
michael@0 255 test.done();
michael@0 256 });
michael@0 257 });
michael@0 258 };
michael@0 259
michael@0 260
michael@0 261 // Selection contexts should not cause items to appear when a selection does
michael@0 262 // not exist in a text field.
michael@0 263 exports.testSelectionContextNoMatchInTextField = function (assert, done) {
michael@0 264 let test = new TestHelper(assert, done);
michael@0 265 let loader = test.newLoader();
michael@0 266
michael@0 267 let item = loader.cm.Item({
michael@0 268 label: "item",
michael@0 269 context: loader.cm.SelectionContext()
michael@0 270 });
michael@0 271
michael@0 272 test.withTestDoc(function (window, doc) {
michael@0 273 let textfield = doc.getElementById("textfield");
michael@0 274 textfield.setSelectionRange(0, 0);
michael@0 275 test.showMenu(textfield, function (popup) {
michael@0 276 test.checkMenu([item], [item], []);
michael@0 277 test.done();
michael@0 278 });
michael@0 279 });
michael@0 280 };
michael@0 281
michael@0 282
michael@0 283 // Selection contexts should not cause items to appear when a selection does
michael@0 284 // not exist.
michael@0 285 exports.testSelectionContextNoMatch = function (assert, done) {
michael@0 286 let test = new TestHelper(assert, done);
michael@0 287 let loader = test.newLoader();
michael@0 288
michael@0 289 let item = loader.cm.Item({
michael@0 290 label: "item",
michael@0 291 context: loader.cm.SelectionContext()
michael@0 292 });
michael@0 293
michael@0 294 test.showMenu(null, function (popup) {
michael@0 295 test.checkMenu([item], [item], []);
michael@0 296 test.done();
michael@0 297 });
michael@0 298 };
michael@0 299
michael@0 300
michael@0 301 // Selection contexts should cause items to appear when a selection exists even
michael@0 302 // for newly opened pages
michael@0 303 exports.testSelectionContextInNewTab = function (assert, done) {
michael@0 304 let test = new TestHelper(assert, done);
michael@0 305 let loader = test.newLoader();
michael@0 306
michael@0 307 let item = loader.cm.Item({
michael@0 308 label: "item",
michael@0 309 context: loader.cm.SelectionContext()
michael@0 310 });
michael@0 311
michael@0 312 test.withTestDoc(function (window, doc) {
michael@0 313 let link = doc.getElementById("targetlink");
michael@0 314 link.click();
michael@0 315
michael@0 316 test.delayedEventListener(this.tabBrowser, "load", function () {
michael@0 317 let browser = test.tabBrowser.selectedBrowser;
michael@0 318 let window = browser.contentWindow;
michael@0 319 let doc = browser.contentDocument;
michael@0 320 window.getSelection().selectAllChildren(doc.body);
michael@0 321
michael@0 322 test.showMenu(null, function (popup) {
michael@0 323 test.checkMenu([item], [], []);
michael@0 324 popup.hidePopup();
michael@0 325
michael@0 326 test.tabBrowser.removeTab(test.tabBrowser.selectedTab);
michael@0 327 test.tabBrowser.selectedTab = test.tab;
michael@0 328
michael@0 329 test.showMenu(null, function (popup) {
michael@0 330 test.checkMenu([item], [item], []);
michael@0 331 test.done();
michael@0 332 });
michael@0 333 });
michael@0 334 }, true);
michael@0 335 });
michael@0 336 };
michael@0 337
michael@0 338
michael@0 339 // Selection contexts should work when right clicking a form button
michael@0 340 exports.testSelectionContextButtonMatch = function (assert, done) {
michael@0 341 let test = new TestHelper(assert, done);
michael@0 342 let loader = test.newLoader();
michael@0 343
michael@0 344 let item = loader.cm.Item({
michael@0 345 label: "item",
michael@0 346 context: loader.cm.SelectionContext()
michael@0 347 });
michael@0 348
michael@0 349 test.withTestDoc(function (window, doc) {
michael@0 350 window.getSelection().selectAllChildren(doc.body);
michael@0 351 let button = doc.getElementById("button");
michael@0 352 test.showMenu(button, function (popup) {
michael@0 353 test.checkMenu([item], [], []);
michael@0 354 test.done();
michael@0 355 });
michael@0 356 });
michael@0 357 };
michael@0 358
michael@0 359
michael@0 360 //Selection contexts should work when right clicking a form button
michael@0 361 exports.testSelectionContextButtonNoMatch = function (assert, done) {
michael@0 362 let test = new TestHelper(assert, done);
michael@0 363 let loader = test.newLoader();
michael@0 364
michael@0 365 let item = loader.cm.Item({
michael@0 366 label: "item",
michael@0 367 context: loader.cm.SelectionContext()
michael@0 368 });
michael@0 369
michael@0 370 test.withTestDoc(function (window, doc) {
michael@0 371 let button = doc.getElementById("button");
michael@0 372 test.showMenu(button, function (popup) {
michael@0 373 test.checkMenu([item], [item], []);
michael@0 374 test.done();
michael@0 375 });
michael@0 376 });
michael@0 377 };
michael@0 378
michael@0 379
michael@0 380 // URL contexts should cause items to appear on pages that match.
michael@0 381 exports.testURLContextMatch = function (assert, done) {
michael@0 382 let test = new TestHelper(assert, done);
michael@0 383 let loader = test.newLoader();
michael@0 384
michael@0 385 let items = [
michael@0 386 loader.cm.Item({
michael@0 387 label: "item 0",
michael@0 388 context: loader.cm.URLContext(TEST_DOC_URL)
michael@0 389 }),
michael@0 390 loader.cm.Item({
michael@0 391 label: "item 1",
michael@0 392 context: loader.cm.URLContext([TEST_DOC_URL, "*.bogus.com"])
michael@0 393 }),
michael@0 394 loader.cm.Item({
michael@0 395 label: "item 2",
michael@0 396 context: loader.cm.URLContext([new RegExp(".*\\.html")])
michael@0 397 })
michael@0 398 ];
michael@0 399
michael@0 400 test.withTestDoc(function (window, doc) {
michael@0 401 test.showMenu(null, function (popup) {
michael@0 402 test.checkMenu(items, [], []);
michael@0 403 test.done();
michael@0 404 });
michael@0 405 });
michael@0 406 };
michael@0 407
michael@0 408
michael@0 409 // URL contexts should not cause items to appear on pages that do not match.
michael@0 410 exports.testURLContextNoMatch = function (assert, done) {
michael@0 411 let test = new TestHelper(assert, done);
michael@0 412 let loader = test.newLoader();
michael@0 413
michael@0 414 let items = [
michael@0 415 loader.cm.Item({
michael@0 416 label: "item 0",
michael@0 417 context: loader.cm.URLContext("*.bogus.com")
michael@0 418 }),
michael@0 419 loader.cm.Item({
michael@0 420 label: "item 1",
michael@0 421 context: loader.cm.URLContext(["*.bogus.com", "*.gnarly.com"])
michael@0 422 }),
michael@0 423 loader.cm.Item({
michael@0 424 label: "item 2",
michael@0 425 context: loader.cm.URLContext([new RegExp(".*\\.js")])
michael@0 426 })
michael@0 427 ];
michael@0 428
michael@0 429 test.withTestDoc(function (window, doc) {
michael@0 430 test.showMenu(null, function (popup) {
michael@0 431 test.checkMenu(items, items, []);
michael@0 432 test.done();
michael@0 433 });
michael@0 434 });
michael@0 435 };
michael@0 436
michael@0 437
michael@0 438 // Removing a non-matching URL context after its item is created and the page is
michael@0 439 // loaded should cause the item's content script to be evaluated when the
michael@0 440 // context menu is next opened.
michael@0 441 exports.testURLContextRemove = function (assert, done) {
michael@0 442 let test = new TestHelper(assert, done);
michael@0 443 let loader = test.newLoader();
michael@0 444
michael@0 445 let shouldBeEvaled = false;
michael@0 446 let context = loader.cm.URLContext("*.bogus.com");
michael@0 447 let item = loader.cm.Item({
michael@0 448 label: "item",
michael@0 449 context: context,
michael@0 450 contentScript: 'self.postMessage("ok"); self.on("context", function () true);',
michael@0 451 onMessage: function (msg) {
michael@0 452 assert.ok(shouldBeEvaled,
michael@0 453 "content script should be evaluated when expected");
michael@0 454 assert.equal(msg, "ok", "Should have received the right message");
michael@0 455 shouldBeEvaled = false;
michael@0 456 }
michael@0 457 });
michael@0 458
michael@0 459 test.withTestDoc(function (window, doc) {
michael@0 460 test.showMenu(null, function (popup) {
michael@0 461 test.checkMenu([item], [item], []);
michael@0 462
michael@0 463 item.context.remove(context);
michael@0 464
michael@0 465 shouldBeEvaled = true;
michael@0 466
michael@0 467 test.hideMenu(function () {
michael@0 468 test.showMenu(null, function (popup) {
michael@0 469 test.checkMenu([item], [], []);
michael@0 470
michael@0 471 assert.ok(!shouldBeEvaled,
michael@0 472 "content script should have been evaluated");
michael@0 473
michael@0 474 test.hideMenu(function () {
michael@0 475 // Shouldn't get evaluated again
michael@0 476 test.showMenu(null, function (popup) {
michael@0 477 test.checkMenu([item], [], []);
michael@0 478 test.done();
michael@0 479 });
michael@0 480 });
michael@0 481 });
michael@0 482 });
michael@0 483 });
michael@0 484 });
michael@0 485 };
michael@0 486
michael@0 487 // Loading a new page in the same tab should correctly start a new worker for
michael@0 488 // any content scripts
michael@0 489 exports.testPageReload = function (assert, done) {
michael@0 490 let test = new TestHelper(assert, done);
michael@0 491 let loader = test.newLoader();
michael@0 492
michael@0 493 let item = loader.cm.Item({
michael@0 494 label: "Item",
michael@0 495 contentScript: "var doc = document; self.on('context', function(node) doc.body.getAttribute('showItem') == 'true');"
michael@0 496 });
michael@0 497
michael@0 498 test.withTestDoc(function (window, doc) {
michael@0 499 // Set a flag on the document that the item uses
michael@0 500 doc.body.setAttribute("showItem", "true");
michael@0 501
michael@0 502 test.showMenu(null, function (popup) {
michael@0 503 // With the attribute true the item should be visible in the menu
michael@0 504 test.checkMenu([item], [], []);
michael@0 505 test.hideMenu(function() {
michael@0 506 let browser = this.tabBrowser.getBrowserForTab(this.tab)
michael@0 507 test.delayedEventListener(browser, "load", function() {
michael@0 508 test.delayedEventListener(browser, "load", function() {
michael@0 509 window = browser.contentWindow;
michael@0 510 doc = window.document;
michael@0 511
michael@0 512 // Set a flag on the document that the item uses
michael@0 513 doc.body.setAttribute("showItem", "false");
michael@0 514
michael@0 515 test.showMenu(null, function (popup) {
michael@0 516 // In the new document with the attribute false the item should be
michael@0 517 // hidden, but if the contentScript hasn't been reloaded it will
michael@0 518 // still see the old value
michael@0 519 test.checkMenu([item], [item], []);
michael@0 520
michael@0 521 test.done();
michael@0 522 });
michael@0 523 }, true);
michael@0 524 browser.loadURI(TEST_DOC_URL, null, null);
michael@0 525 }, true);
michael@0 526 // Required to make sure we load a new page in history rather than
michael@0 527 // just reloading the current page which would unload it
michael@0 528 browser.loadURI("about:blank", null, null);
michael@0 529 });
michael@0 530 });
michael@0 531 });
michael@0 532 };
michael@0 533
michael@0 534 // Closing a page after it's been used with a worker should cause the worker
michael@0 535 // to be destroyed
michael@0 536 /*exports.testWorkerDestroy = function (assert, done) {
michael@0 537 let test = new TestHelper(assert, done);
michael@0 538 let loader = test.newLoader();
michael@0 539
michael@0 540 let loadExpected = false;
michael@0 541
michael@0 542 let item = loader.cm.Item({
michael@0 543 label: "item",
michael@0 544 contentScript: 'self.postMessage("loaded"); self.on("detach", function () { console.log("saw detach"); self.postMessage("detach") });',
michael@0 545 onMessage: function (msg) {
michael@0 546 switch (msg) {
michael@0 547 case "loaded":
michael@0 548 assert.ok(loadExpected, "Should have seen the load event at the right time");
michael@0 549 loadExpected = false;
michael@0 550 break;
michael@0 551 case "detach":
michael@0 552 test.done();
michael@0 553 break;
michael@0 554 }
michael@0 555 }
michael@0 556 });
michael@0 557
michael@0 558 test.withTestDoc(function (window, doc) {
michael@0 559 loadExpected = true;
michael@0 560 test.showMenu(null, function (popup) {
michael@0 561 assert.ok(!loadExpected, "Should have seen a message");
michael@0 562
michael@0 563 test.checkMenu([item], [], []);
michael@0 564
michael@0 565 test.closeTab();
michael@0 566 });
michael@0 567 });
michael@0 568 };*/
michael@0 569
michael@0 570
michael@0 571 // Content contexts that return true should cause their items to be present
michael@0 572 // in the menu.
michael@0 573 exports.testContentContextMatch = function (assert, done) {
michael@0 574 let test = new TestHelper(assert, done);
michael@0 575 let loader = test.newLoader();
michael@0 576
michael@0 577 let item = new loader.cm.Item({
michael@0 578 label: "item",
michael@0 579 contentScript: 'self.on("context", function () true);'
michael@0 580 });
michael@0 581
michael@0 582 test.showMenu(null, function (popup) {
michael@0 583 test.checkMenu([item], [], []);
michael@0 584 test.done();
michael@0 585 });
michael@0 586 };
michael@0 587
michael@0 588
michael@0 589 // Content contexts that return false should cause their items to be absent
michael@0 590 // from the menu.
michael@0 591 exports.testContentContextNoMatch = function (assert, done) {
michael@0 592 let test = new TestHelper(assert, done);
michael@0 593 let loader = test.newLoader();
michael@0 594
michael@0 595 let item = new loader.cm.Item({
michael@0 596 label: "item",
michael@0 597 contentScript: 'self.on("context", function () false);'
michael@0 598 });
michael@0 599
michael@0 600 test.showMenu(null, function (popup) {
michael@0 601 test.checkMenu([item], [item], []);
michael@0 602 test.done();
michael@0 603 });
michael@0 604 };
michael@0 605
michael@0 606
michael@0 607 // Content contexts that return undefined should cause their items to be absent
michael@0 608 // from the menu.
michael@0 609 exports.testContentContextUndefined = function (assert, done) {
michael@0 610 let test = new TestHelper(assert, done);
michael@0 611 let loader = test.newLoader();
michael@0 612
michael@0 613 let item = new loader.cm.Item({
michael@0 614 label: "item",
michael@0 615 contentScript: 'self.on("context", function () {});'
michael@0 616 });
michael@0 617
michael@0 618 test.showMenu(null, function (popup) {
michael@0 619 test.checkMenu([item], [item], []);
michael@0 620 test.done();
michael@0 621 });
michael@0 622 };
michael@0 623
michael@0 624
michael@0 625 // Content contexts that return an empty string should cause their items to be
michael@0 626 // absent from the menu and shouldn't wipe the label
michael@0 627 exports.testContentContextEmptyString = function (assert, done) {
michael@0 628 let test = new TestHelper(assert, done);
michael@0 629 let loader = test.newLoader();
michael@0 630
michael@0 631 let item = new loader.cm.Item({
michael@0 632 label: "item",
michael@0 633 contentScript: 'self.on("context", function () "");'
michael@0 634 });
michael@0 635
michael@0 636 test.showMenu(null, function (popup) {
michael@0 637 test.checkMenu([item], [item], []);
michael@0 638 assert.equal(item.label, "item", "Label should still be correct");
michael@0 639 test.done();
michael@0 640 });
michael@0 641 };
michael@0 642
michael@0 643
michael@0 644 // If any content contexts returns true then their items should be present in
michael@0 645 // the menu.
michael@0 646 exports.testMultipleContentContextMatch1 = function (assert, done) {
michael@0 647 let test = new TestHelper(assert, done);
michael@0 648 let loader = test.newLoader();
michael@0 649
michael@0 650 let item = new loader.cm.Item({
michael@0 651 label: "item",
michael@0 652 contentScript: 'self.on("context", function () true); ' +
michael@0 653 'self.on("context", function () false);',
michael@0 654 onMessage: function() {
michael@0 655 test.fail("Should not have called the second context listener");
michael@0 656 }
michael@0 657 });
michael@0 658
michael@0 659 test.showMenu(null, function (popup) {
michael@0 660 test.checkMenu([item], [], []);
michael@0 661 test.done();
michael@0 662 });
michael@0 663 };
michael@0 664
michael@0 665
michael@0 666 // If any content contexts returns true then their items should be present in
michael@0 667 // the menu.
michael@0 668 exports.testMultipleContentContextMatch2 = function (assert, done) {
michael@0 669 let test = new TestHelper(assert, done);
michael@0 670 let loader = test.newLoader();
michael@0 671
michael@0 672 let item = new loader.cm.Item({
michael@0 673 label: "item",
michael@0 674 contentScript: 'self.on("context", function () false); ' +
michael@0 675 'self.on("context", function () true);'
michael@0 676 });
michael@0 677
michael@0 678 test.showMenu(null, function (popup) {
michael@0 679 test.checkMenu([item], [], []);
michael@0 680 test.done();
michael@0 681 });
michael@0 682 };
michael@0 683
michael@0 684
michael@0 685 // If any content contexts returns a string then their items should be present
michael@0 686 // in the menu.
michael@0 687 exports.testMultipleContentContextString1 = function (assert, done) {
michael@0 688 let test = new TestHelper(assert, done);
michael@0 689 let loader = test.newLoader();
michael@0 690
michael@0 691 let item = new loader.cm.Item({
michael@0 692 label: "item",
michael@0 693 contentScript: 'self.on("context", function () "new label"); ' +
michael@0 694 'self.on("context", function () false);'
michael@0 695 });
michael@0 696
michael@0 697 test.showMenu(null, function (popup) {
michael@0 698 test.checkMenu([item], [], []);
michael@0 699 assert.equal(item.label, "new label", "Label should have changed");
michael@0 700 test.done();
michael@0 701 });
michael@0 702 };
michael@0 703
michael@0 704
michael@0 705 // If any content contexts returns a string then their items should be present
michael@0 706 // in the menu.
michael@0 707 exports.testMultipleContentContextString2 = function (assert, done) {
michael@0 708 let test = new TestHelper(assert, done);
michael@0 709 let loader = test.newLoader();
michael@0 710
michael@0 711 let item = new loader.cm.Item({
michael@0 712 label: "item",
michael@0 713 contentScript: 'self.on("context", function () false); ' +
michael@0 714 'self.on("context", function () "new label");'
michael@0 715 });
michael@0 716
michael@0 717 test.showMenu(null, function (popup) {
michael@0 718 test.checkMenu([item], [], []);
michael@0 719 assert.equal(item.label, "new label", "Label should have changed");
michael@0 720 test.done();
michael@0 721 });
michael@0 722 };
michael@0 723
michael@0 724
michael@0 725 // If many content contexts returns a string then the first should take effect
michael@0 726 exports.testMultipleContentContextString3 = function (assert, done) {
michael@0 727 let test = new TestHelper(assert, done);
michael@0 728 let loader = test.newLoader();
michael@0 729
michael@0 730 let item = new loader.cm.Item({
michael@0 731 label: "item",
michael@0 732 contentScript: 'self.on("context", function () "new label 1"); ' +
michael@0 733 'self.on("context", function () "new label 2");'
michael@0 734 });
michael@0 735
michael@0 736 test.showMenu(null, function (popup) {
michael@0 737 test.checkMenu([item], [], []);
michael@0 738 assert.equal(item.label, "new label 1", "Label should have changed");
michael@0 739 test.done();
michael@0 740 });
michael@0 741 };
michael@0 742
michael@0 743
michael@0 744 // Content contexts that return true should cause their items to be present
michael@0 745 // in the menu when context clicking an active element.
michael@0 746 exports.testContentContextMatchActiveElement = function (assert, done) {
michael@0 747 let test = new TestHelper(assert, done);
michael@0 748 let loader = test.newLoader();
michael@0 749
michael@0 750 let items = [
michael@0 751 new loader.cm.Item({
michael@0 752 label: "item 1",
michael@0 753 contentScript: 'self.on("context", function () true);'
michael@0 754 }),
michael@0 755 new loader.cm.Item({
michael@0 756 label: "item 2",
michael@0 757 context: undefined,
michael@0 758 contentScript: 'self.on("context", function () true);'
michael@0 759 }),
michael@0 760 // These items will always be hidden by the declarative usage of PageContext
michael@0 761 new loader.cm.Item({
michael@0 762 label: "item 3",
michael@0 763 context: loader.cm.PageContext(),
michael@0 764 contentScript: 'self.on("context", function () true);'
michael@0 765 }),
michael@0 766 new loader.cm.Item({
michael@0 767 label: "item 4",
michael@0 768 context: [loader.cm.PageContext()],
michael@0 769 contentScript: 'self.on("context", function () true);'
michael@0 770 })
michael@0 771 ];
michael@0 772
michael@0 773 test.withTestDoc(function (window, doc) {
michael@0 774 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 775 test.checkMenu(items, [items[2], items[3]], []);
michael@0 776 test.done();
michael@0 777 });
michael@0 778 });
michael@0 779 };
michael@0 780
michael@0 781
michael@0 782 // Content contexts that return false should cause their items to be absent
michael@0 783 // from the menu when context clicking an active element.
michael@0 784 exports.testContentContextNoMatchActiveElement = function (assert, done) {
michael@0 785 let test = new TestHelper(assert, done);
michael@0 786 let loader = test.newLoader();
michael@0 787
michael@0 788 let items = [
michael@0 789 new loader.cm.Item({
michael@0 790 label: "item 1",
michael@0 791 contentScript: 'self.on("context", function () false);'
michael@0 792 }),
michael@0 793 new loader.cm.Item({
michael@0 794 label: "item 2",
michael@0 795 context: undefined,
michael@0 796 contentScript: 'self.on("context", function () false);'
michael@0 797 }),
michael@0 798 // These items will always be hidden by the declarative usage of PageContext
michael@0 799 new loader.cm.Item({
michael@0 800 label: "item 3",
michael@0 801 context: loader.cm.PageContext(),
michael@0 802 contentScript: 'self.on("context", function () false);'
michael@0 803 }),
michael@0 804 new loader.cm.Item({
michael@0 805 label: "item 4",
michael@0 806 context: [loader.cm.PageContext()],
michael@0 807 contentScript: 'self.on("context", function () false);'
michael@0 808 })
michael@0 809 ];
michael@0 810
michael@0 811 test.withTestDoc(function (window, doc) {
michael@0 812 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 813 test.checkMenu(items, items, []);
michael@0 814 test.done();
michael@0 815 });
michael@0 816 });
michael@0 817 };
michael@0 818
michael@0 819
michael@0 820 // Content contexts that return undefined should cause their items to be absent
michael@0 821 // from the menu when context clicking an active element.
michael@0 822 exports.testContentContextNoMatchActiveElement = function (assert, done) {
michael@0 823 let test = new TestHelper(assert, done);
michael@0 824 let loader = test.newLoader();
michael@0 825
michael@0 826 let items = [
michael@0 827 new loader.cm.Item({
michael@0 828 label: "item 1",
michael@0 829 contentScript: 'self.on("context", function () {});'
michael@0 830 }),
michael@0 831 new loader.cm.Item({
michael@0 832 label: "item 2",
michael@0 833 context: undefined,
michael@0 834 contentScript: 'self.on("context", function () {});'
michael@0 835 }),
michael@0 836 // These items will always be hidden by the declarative usage of PageContext
michael@0 837 new loader.cm.Item({
michael@0 838 label: "item 3",
michael@0 839 context: loader.cm.PageContext(),
michael@0 840 contentScript: 'self.on("context", function () {});'
michael@0 841 }),
michael@0 842 new loader.cm.Item({
michael@0 843 label: "item 4",
michael@0 844 context: [loader.cm.PageContext()],
michael@0 845 contentScript: 'self.on("context", function () {});'
michael@0 846 })
michael@0 847 ];
michael@0 848
michael@0 849 test.withTestDoc(function (window, doc) {
michael@0 850 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 851 test.checkMenu(items, items, []);
michael@0 852 test.done();
michael@0 853 });
michael@0 854 });
michael@0 855 };
michael@0 856
michael@0 857
michael@0 858 // Content contexts that return a string should cause their items to be present
michael@0 859 // in the menu and the items' labels to be updated.
michael@0 860 exports.testContentContextMatchString = function (assert, done) {
michael@0 861 let test = new TestHelper(assert, done);
michael@0 862 let loader = test.newLoader();
michael@0 863
michael@0 864 let item = new loader.cm.Item({
michael@0 865 label: "first label",
michael@0 866 contentScript: 'self.on("context", function () "second label");'
michael@0 867 });
michael@0 868
michael@0 869 test.showMenu(null, function (popup) {
michael@0 870 test.checkMenu([item], [], []);
michael@0 871 assert.equal(item.label, "second label",
michael@0 872 "item's label should be updated");
michael@0 873 test.done();
michael@0 874 });
michael@0 875 };
michael@0 876
michael@0 877
michael@0 878 // Ensure that contentScriptFile is working correctly
michael@0 879 exports.testContentScriptFile = function (assert, done) {
michael@0 880 let test = new TestHelper(assert, done);
michael@0 881 let loader = test.newLoader();
michael@0 882
michael@0 883 // Reject remote files
michael@0 884 assert.throws(function() {
michael@0 885 new loader.cm.Item({
michael@0 886 label: "item",
michael@0 887 contentScriptFile: "http://mozilla.com/context-menu.js"
michael@0 888 });
michael@0 889 },
michael@0 890 new RegExp("The 'contentScriptFile' option must be a local file URL " +
michael@0 891 "or an array of local file URLs."),
michael@0 892 "Item throws when contentScriptFile is a remote URL");
michael@0 893
michael@0 894 // But accept files from data folder
michael@0 895 let item = new loader.cm.Item({
michael@0 896 label: "item",
michael@0 897 contentScriptFile: data.url("test-context-menu.js")
michael@0 898 });
michael@0 899
michael@0 900 test.showMenu(null, function (popup) {
michael@0 901 test.checkMenu([item], [], []);
michael@0 902 test.done();
michael@0 903 });
michael@0 904 };
michael@0 905
michael@0 906
michael@0 907 // The args passed to context listeners should be correct.
michael@0 908 exports.testContentContextArgs = function (assert, done) {
michael@0 909 let test = new TestHelper(assert, done);
michael@0 910 let loader = test.newLoader();
michael@0 911 let callbacks = 0;
michael@0 912
michael@0 913 let item = new loader.cm.Item({
michael@0 914 label: "item",
michael@0 915 contentScript: 'self.on("context", function (node) {' +
michael@0 916 ' self.postMessage(node.tagName);' +
michael@0 917 ' return false;' +
michael@0 918 '});',
michael@0 919 onMessage: function (tagName) {
michael@0 920 assert.equal(tagName, "HTML", "node should be an HTML element");
michael@0 921 if (++callbacks == 2) test.done();
michael@0 922 }
michael@0 923 });
michael@0 924
michael@0 925 test.showMenu(null, function () {
michael@0 926 if (++callbacks == 2) test.done();
michael@0 927 });
michael@0 928 };
michael@0 929
michael@0 930 // Multiple contexts imply intersection, not union, and content context
michael@0 931 // listeners should not be called if all declarative contexts are not current.
michael@0 932 exports.testMultipleContexts = function (assert, done) {
michael@0 933 let test = new TestHelper(assert, done);
michael@0 934 let loader = test.newLoader();
michael@0 935
michael@0 936 let item = new loader.cm.Item({
michael@0 937 label: "item",
michael@0 938 context: [loader.cm.SelectorContext("a[href]"), loader.cm.PageContext()],
michael@0 939 contentScript: 'self.on("context", function () self.postMessage());',
michael@0 940 onMessage: function () {
michael@0 941 test.fail("Context listener should not be called");
michael@0 942 }
michael@0 943 });
michael@0 944
michael@0 945 test.withTestDoc(function (window, doc) {
michael@0 946 test.showMenu(doc.getElementById("span-link"), function (popup) {
michael@0 947 test.checkMenu([item], [item], []);
michael@0 948 test.done();
michael@0 949 });
michael@0 950 });
michael@0 951 };
michael@0 952
michael@0 953 // Once a context is removed, it should no longer cause its item to appear.
michael@0 954 exports.testRemoveContext = function (assert, done) {
michael@0 955 let test = new TestHelper(assert, done);
michael@0 956 let loader = test.newLoader();
michael@0 957
michael@0 958 let ctxt = loader.cm.SelectorContext("img");
michael@0 959 let item = new loader.cm.Item({
michael@0 960 label: "item",
michael@0 961 context: ctxt
michael@0 962 });
michael@0 963
michael@0 964 test.withTestDoc(function (window, doc) {
michael@0 965 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 966
michael@0 967 // The item should be present at first.
michael@0 968 test.checkMenu([item], [], []);
michael@0 969 popup.hidePopup();
michael@0 970
michael@0 971 // Remove the img context and check again.
michael@0 972 item.context.remove(ctxt);
michael@0 973 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 974 test.checkMenu([item], [item], []);
michael@0 975 test.done();
michael@0 976 });
michael@0 977 });
michael@0 978 });
michael@0 979 };
michael@0 980
michael@0 981
michael@0 982 // Lots of items should overflow into the overflow submenu.
michael@0 983 exports.testOverflow = function (assert, done) {
michael@0 984 let test = new TestHelper(assert, done);
michael@0 985 let loader = test.newLoader();
michael@0 986
michael@0 987 let items = [];
michael@0 988 for (let i = 0; i < OVERFLOW_THRESH_DEFAULT + 1; i++) {
michael@0 989 let item = new loader.cm.Item({ label: "item " + i });
michael@0 990 items.push(item);
michael@0 991 }
michael@0 992
michael@0 993 test.showMenu(null, function (popup) {
michael@0 994 test.checkMenu(items, [], []);
michael@0 995 test.done();
michael@0 996 });
michael@0 997 };
michael@0 998
michael@0 999
michael@0 1000 // Module unload should cause all items to be removed.
michael@0 1001 exports.testUnload = function (assert, done) {
michael@0 1002 let test = new TestHelper(assert, done);
michael@0 1003 let loader = test.newLoader();
michael@0 1004
michael@0 1005 let item = new loader.cm.Item({ label: "item" });
michael@0 1006
michael@0 1007 test.showMenu(null, function (popup) {
michael@0 1008
michael@0 1009 // The menu should contain the item.
michael@0 1010 test.checkMenu([item], [], []);
michael@0 1011 popup.hidePopup();
michael@0 1012
michael@0 1013 // Unload the module.
michael@0 1014 loader.unload();
michael@0 1015 test.showMenu(null, function (popup) {
michael@0 1016
michael@0 1017 // The item should be removed from the menu.
michael@0 1018 test.checkMenu([item], [], [item]);
michael@0 1019 test.done();
michael@0 1020 });
michael@0 1021 });
michael@0 1022 };
michael@0 1023
michael@0 1024
michael@0 1025 // Using multiple module instances to add items without causing overflow should
michael@0 1026 // work OK. Assumes OVERFLOW_THRESH_DEFAULT >= 2.
michael@0 1027 exports.testMultipleModulesAdd = function (assert, done) {
michael@0 1028 let test = new TestHelper(assert, done);
michael@0 1029 let loader0 = test.newLoader();
michael@0 1030 let loader1 = test.newLoader();
michael@0 1031
michael@0 1032 // Use each module to add an item, then unload each module in turn.
michael@0 1033 let item0 = new loader0.cm.Item({ label: "item 0" });
michael@0 1034 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1035
michael@0 1036 test.showMenu(null, function (popup) {
michael@0 1037
michael@0 1038 // The menu should contain both items.
michael@0 1039 test.checkMenu([item0, item1], [], []);
michael@0 1040 popup.hidePopup();
michael@0 1041
michael@0 1042 // Unload the first module.
michael@0 1043 loader0.unload();
michael@0 1044 test.showMenu(null, function (popup) {
michael@0 1045
michael@0 1046 // The first item should be removed from the menu.
michael@0 1047 test.checkMenu([item0, item1], [], [item0]);
michael@0 1048 popup.hidePopup();
michael@0 1049
michael@0 1050 // Unload the second module.
michael@0 1051 loader1.unload();
michael@0 1052 test.showMenu(null, function (popup) {
michael@0 1053
michael@0 1054 // Both items should be removed from the menu.
michael@0 1055 test.checkMenu([item0, item1], [], [item0, item1]);
michael@0 1056 test.done();
michael@0 1057 });
michael@0 1058 });
michael@0 1059 });
michael@0 1060 };
michael@0 1061
michael@0 1062
michael@0 1063 // Using multiple module instances to add items causing overflow should work OK.
michael@0 1064 exports.testMultipleModulesAddOverflow = function (assert, done) {
michael@0 1065 let test = new TestHelper(assert, done);
michael@0 1066 let loader0 = test.newLoader();
michael@0 1067 let loader1 = test.newLoader();
michael@0 1068
michael@0 1069 // Use module 0 to add OVERFLOW_THRESH_DEFAULT items.
michael@0 1070 let items0 = [];
michael@0 1071 for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) {
michael@0 1072 let item = new loader0.cm.Item({ label: "item 0 " + i });
michael@0 1073 items0.push(item);
michael@0 1074 }
michael@0 1075
michael@0 1076 // Use module 1 to add one item.
michael@0 1077 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1078
michael@0 1079 let allItems = items0.concat(item1);
michael@0 1080
michael@0 1081 test.showMenu(null, function (popup) {
michael@0 1082
michael@0 1083 // The menu should contain all items in overflow.
michael@0 1084 test.checkMenu(allItems, [], []);
michael@0 1085 popup.hidePopup();
michael@0 1086
michael@0 1087 // Unload the first module.
michael@0 1088 loader0.unload();
michael@0 1089 test.showMenu(null, function (popup) {
michael@0 1090
michael@0 1091 // The first items should be removed from the menu, which should not
michael@0 1092 // overflow.
michael@0 1093 test.checkMenu(allItems, [], items0);
michael@0 1094 popup.hidePopup();
michael@0 1095
michael@0 1096 // Unload the second module.
michael@0 1097 loader1.unload();
michael@0 1098 test.showMenu(null, function (popup) {
michael@0 1099
michael@0 1100 // All items should be removed from the menu.
michael@0 1101 test.checkMenu(allItems, [], allItems);
michael@0 1102 test.done();
michael@0 1103 });
michael@0 1104 });
michael@0 1105 });
michael@0 1106 };
michael@0 1107
michael@0 1108
michael@0 1109 // Using multiple module instances to modify the menu without causing overflow
michael@0 1110 // should work OK. This test creates two loaders and:
michael@0 1111 // loader0 create item -> loader1 create item -> loader0.unload ->
michael@0 1112 // loader1.unload
michael@0 1113 exports.testMultipleModulesDiffContexts1 = function (assert, done) {
michael@0 1114 let test = new TestHelper(assert, done);
michael@0 1115 let loader0 = test.newLoader();
michael@0 1116 let loader1 = test.newLoader();
michael@0 1117
michael@0 1118 let item0 = new loader0.cm.Item({
michael@0 1119 label: "item 0",
michael@0 1120 context: loader0.cm.SelectorContext("img")
michael@0 1121 });
michael@0 1122
michael@0 1123 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1124
michael@0 1125 test.showMenu(null, function (popup) {
michael@0 1126
michael@0 1127 // The menu should contain item1.
michael@0 1128 test.checkMenu([item0, item1], [item0], []);
michael@0 1129 popup.hidePopup();
michael@0 1130
michael@0 1131 // Unload module 0.
michael@0 1132 loader0.unload();
michael@0 1133 test.showMenu(null, function (popup) {
michael@0 1134
michael@0 1135 // item0 should be removed from the menu.
michael@0 1136 test.checkMenu([item0, item1], [], [item0]);
michael@0 1137 popup.hidePopup();
michael@0 1138
michael@0 1139 // Unload module 1.
michael@0 1140 loader1.unload();
michael@0 1141 test.showMenu(null, function (popup) {
michael@0 1142
michael@0 1143 // Both items should be removed from the menu.
michael@0 1144 test.checkMenu([item0, item1], [], [item0, item1]);
michael@0 1145 test.done();
michael@0 1146 });
michael@0 1147 });
michael@0 1148 });
michael@0 1149 };
michael@0 1150
michael@0 1151
michael@0 1152 // Using multiple module instances to modify the menu without causing overflow
michael@0 1153 // should work OK. This test creates two loaders and:
michael@0 1154 // loader1 create item -> loader0 create item -> loader0.unload ->
michael@0 1155 // loader1.unload
michael@0 1156 exports.testMultipleModulesDiffContexts2 = function (assert, done) {
michael@0 1157 let test = new TestHelper(assert, done);
michael@0 1158 let loader0 = test.newLoader();
michael@0 1159 let loader1 = test.newLoader();
michael@0 1160
michael@0 1161 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1162
michael@0 1163 let item0 = new loader0.cm.Item({
michael@0 1164 label: "item 0",
michael@0 1165 context: loader0.cm.SelectorContext("img")
michael@0 1166 });
michael@0 1167
michael@0 1168 test.showMenu(null, function (popup) {
michael@0 1169
michael@0 1170 // The menu should contain item1.
michael@0 1171 test.checkMenu([item0, item1], [item0], []);
michael@0 1172 popup.hidePopup();
michael@0 1173
michael@0 1174 // Unload module 0.
michael@0 1175 loader0.unload();
michael@0 1176 test.showMenu(null, function (popup) {
michael@0 1177
michael@0 1178 // item0 should be removed from the menu.
michael@0 1179 test.checkMenu([item0, item1], [], [item0]);
michael@0 1180 popup.hidePopup();
michael@0 1181
michael@0 1182 // Unload module 1.
michael@0 1183 loader1.unload();
michael@0 1184 test.showMenu(null, function (popup) {
michael@0 1185
michael@0 1186 // Both items should be removed from the menu.
michael@0 1187 test.checkMenu([item0, item1], [], [item0, item1]);
michael@0 1188 test.done();
michael@0 1189 });
michael@0 1190 });
michael@0 1191 });
michael@0 1192 };
michael@0 1193
michael@0 1194
michael@0 1195 // Using multiple module instances to modify the menu without causing overflow
michael@0 1196 // should work OK. This test creates two loaders and:
michael@0 1197 // loader0 create item -> loader1 create item -> loader1.unload ->
michael@0 1198 // loader0.unload
michael@0 1199 exports.testMultipleModulesDiffContexts3 = function (assert, done) {
michael@0 1200 let test = new TestHelper(assert, done);
michael@0 1201 let loader0 = test.newLoader();
michael@0 1202 let loader1 = test.newLoader();
michael@0 1203
michael@0 1204 let item0 = new loader0.cm.Item({
michael@0 1205 label: "item 0",
michael@0 1206 context: loader0.cm.SelectorContext("img")
michael@0 1207 });
michael@0 1208
michael@0 1209 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1210
michael@0 1211 test.showMenu(null, function (popup) {
michael@0 1212
michael@0 1213 // The menu should contain item1.
michael@0 1214 test.checkMenu([item0, item1], [item0], []);
michael@0 1215 popup.hidePopup();
michael@0 1216
michael@0 1217 // Unload module 1.
michael@0 1218 loader1.unload();
michael@0 1219 test.showMenu(null, function (popup) {
michael@0 1220
michael@0 1221 // item1 should be removed from the menu.
michael@0 1222 test.checkMenu([item0, item1], [item0], [item1]);
michael@0 1223 popup.hidePopup();
michael@0 1224
michael@0 1225 // Unload module 0.
michael@0 1226 loader0.unload();
michael@0 1227 test.showMenu(null, function (popup) {
michael@0 1228
michael@0 1229 // Both items should be removed from the menu.
michael@0 1230 test.checkMenu([item0, item1], [], [item0, item1]);
michael@0 1231 test.done();
michael@0 1232 });
michael@0 1233 });
michael@0 1234 });
michael@0 1235 };
michael@0 1236
michael@0 1237
michael@0 1238 // Using multiple module instances to modify the menu without causing overflow
michael@0 1239 // should work OK. This test creates two loaders and:
michael@0 1240 // loader1 create item -> loader0 create item -> loader1.unload ->
michael@0 1241 // loader0.unload
michael@0 1242 exports.testMultipleModulesDiffContexts4 = function (assert, done) {
michael@0 1243 let test = new TestHelper(assert, done);
michael@0 1244 let loader0 = test.newLoader();
michael@0 1245 let loader1 = test.newLoader();
michael@0 1246
michael@0 1247 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1248
michael@0 1249 let item0 = new loader0.cm.Item({
michael@0 1250 label: "item 0",
michael@0 1251 context: loader0.cm.SelectorContext("img")
michael@0 1252 });
michael@0 1253
michael@0 1254 test.showMenu(null, function (popup) {
michael@0 1255
michael@0 1256 // The menu should contain item1.
michael@0 1257 test.checkMenu([item0, item1], [item0], []);
michael@0 1258 popup.hidePopup();
michael@0 1259
michael@0 1260 // Unload module 1.
michael@0 1261 loader1.unload();
michael@0 1262 test.showMenu(null, function (popup) {
michael@0 1263
michael@0 1264 // item1 should be removed from the menu.
michael@0 1265 test.checkMenu([item0, item1], [item0], [item1]);
michael@0 1266 popup.hidePopup();
michael@0 1267
michael@0 1268 // Unload module 0.
michael@0 1269 loader0.unload();
michael@0 1270 test.showMenu(null, function (popup) {
michael@0 1271
michael@0 1272 // Both items should be removed from the menu.
michael@0 1273 test.checkMenu([item0, item1], [], [item0, item1]);
michael@0 1274 test.done();
michael@0 1275 });
michael@0 1276 });
michael@0 1277 });
michael@0 1278 };
michael@0 1279
michael@0 1280
michael@0 1281 // Test interactions between a loaded module, unloading another module, and the
michael@0 1282 // menu separator and overflow submenu.
michael@0 1283 exports.testMultipleModulesAddRemove = function (assert, done) {
michael@0 1284 let test = new TestHelper(assert, done);
michael@0 1285 let loader0 = test.newLoader();
michael@0 1286 let loader1 = test.newLoader();
michael@0 1287
michael@0 1288 let item = new loader0.cm.Item({ label: "item" });
michael@0 1289
michael@0 1290 test.showMenu(null, function (popup) {
michael@0 1291
michael@0 1292 // The menu should contain the item.
michael@0 1293 test.checkMenu([item], [], []);
michael@0 1294 popup.hidePopup();
michael@0 1295
michael@0 1296 // Remove the item.
michael@0 1297 item.destroy();
michael@0 1298 test.showMenu(null, function (popup) {
michael@0 1299
michael@0 1300 // The item should be removed from the menu.
michael@0 1301 test.checkMenu([item], [], [item]);
michael@0 1302 popup.hidePopup();
michael@0 1303
michael@0 1304 // Unload module 1.
michael@0 1305 loader1.unload();
michael@0 1306 test.showMenu(null, function (popup) {
michael@0 1307
michael@0 1308 // There shouldn't be any errors involving the menu separator or
michael@0 1309 // overflow submenu.
michael@0 1310 test.checkMenu([item], [], [item]);
michael@0 1311 test.done();
michael@0 1312 });
michael@0 1313 });
michael@0 1314 });
michael@0 1315 };
michael@0 1316
michael@0 1317
michael@0 1318 // Checks that the order of menu items is correct when adding/removing across
michael@0 1319 // multiple modules. All items from a single module should remain in a group
michael@0 1320 exports.testMultipleModulesOrder = function (assert, done) {
michael@0 1321 let test = new TestHelper(assert, done);
michael@0 1322 let loader0 = test.newLoader();
michael@0 1323 let loader1 = test.newLoader();
michael@0 1324
michael@0 1325 // Use each module to add an item, then unload each module in turn.
michael@0 1326 let item0 = new loader0.cm.Item({ label: "item 0" });
michael@0 1327 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1328
michael@0 1329 test.showMenu(null, function (popup) {
michael@0 1330
michael@0 1331 // The menu should contain both items.
michael@0 1332 test.checkMenu([item0, item1], [], []);
michael@0 1333 popup.hidePopup();
michael@0 1334
michael@0 1335 let item2 = new loader0.cm.Item({ label: "item 2" });
michael@0 1336
michael@0 1337 test.showMenu(null, function (popup) {
michael@0 1338
michael@0 1339 // The new item should be grouped with the same items from loader0.
michael@0 1340 test.checkMenu([item0, item2, item1], [], []);
michael@0 1341 popup.hidePopup();
michael@0 1342
michael@0 1343 let item3 = new loader1.cm.Item({ label: "item 3" });
michael@0 1344
michael@0 1345 test.showMenu(null, function (popup) {
michael@0 1346
michael@0 1347 // Same again
michael@0 1348 test.checkMenu([item0, item2, item1, item3], [], []);
michael@0 1349 test.done();
michael@0 1350 });
michael@0 1351 });
michael@0 1352 });
michael@0 1353 };
michael@0 1354
michael@0 1355
michael@0 1356 // Checks that the order of menu items is correct when adding/removing across
michael@0 1357 // multiple modules when overflowing. All items from a single module should
michael@0 1358 // remain in a group
michael@0 1359 exports.testMultipleModulesOrderOverflow = function (assert, done) {
michael@0 1360 let test = new TestHelper(assert, done);
michael@0 1361 let loader0 = test.newLoader();
michael@0 1362 let loader1 = test.newLoader();
michael@0 1363
michael@0 1364 let prefs = loader0.loader.require("sdk/preferences/service");
michael@0 1365 prefs.set(OVERFLOW_THRESH_PREF, 0);
michael@0 1366
michael@0 1367 // Use each module to add an item, then unload each module in turn.
michael@0 1368 let item0 = new loader0.cm.Item({ label: "item 0" });
michael@0 1369 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1370
michael@0 1371 test.showMenu(null, function (popup) {
michael@0 1372
michael@0 1373 // The menu should contain both items.
michael@0 1374 test.checkMenu([item0, item1], [], []);
michael@0 1375 popup.hidePopup();
michael@0 1376
michael@0 1377 let item2 = new loader0.cm.Item({ label: "item 2" });
michael@0 1378
michael@0 1379 test.showMenu(null, function (popup) {
michael@0 1380
michael@0 1381 // The new item should be grouped with the same items from loader0.
michael@0 1382 test.checkMenu([item0, item2, item1], [], []);
michael@0 1383 popup.hidePopup();
michael@0 1384
michael@0 1385 let item3 = new loader1.cm.Item({ label: "item 3" });
michael@0 1386
michael@0 1387 test.showMenu(null, function (popup) {
michael@0 1388
michael@0 1389 // Same again
michael@0 1390 test.checkMenu([item0, item2, item1, item3], [], []);
michael@0 1391 test.done();
michael@0 1392 });
michael@0 1393 });
michael@0 1394 });
michael@0 1395 };
michael@0 1396
michael@0 1397
michael@0 1398 // Checks that if a module's items are all hidden then the overflow menu doesn't
michael@0 1399 // get hidden
michael@0 1400 exports.testMultipleModulesOverflowHidden = function (assert, done) {
michael@0 1401 let test = new TestHelper(assert, done);
michael@0 1402 let loader0 = test.newLoader();
michael@0 1403 let loader1 = test.newLoader();
michael@0 1404
michael@0 1405 let prefs = loader0.loader.require("sdk/preferences/service");
michael@0 1406 prefs.set(OVERFLOW_THRESH_PREF, 0);
michael@0 1407
michael@0 1408 // Use each module to add an item, then unload each module in turn.
michael@0 1409 let item0 = new loader0.cm.Item({ label: "item 0" });
michael@0 1410 let item1 = new loader1.cm.Item({
michael@0 1411 label: "item 1",
michael@0 1412 context: loader1.cm.SelectorContext("a")
michael@0 1413 });
michael@0 1414
michael@0 1415 test.showMenu(null, function (popup) {
michael@0 1416 // One should be hidden
michael@0 1417 test.checkMenu([item0, item1], [item1], []);
michael@0 1418 test.done();
michael@0 1419 });
michael@0 1420 };
michael@0 1421
michael@0 1422
michael@0 1423 // Checks that if a module's items are all hidden then the overflow menu doesn't
michael@0 1424 // get hidden (reverse order to above)
michael@0 1425 exports.testMultipleModulesOverflowHidden2 = function (assert, done) {
michael@0 1426 let test = new TestHelper(assert, done);
michael@0 1427 let loader0 = test.newLoader();
michael@0 1428 let loader1 = test.newLoader();
michael@0 1429
michael@0 1430 let prefs = loader0.loader.require("sdk/preferences/service");
michael@0 1431 prefs.set(OVERFLOW_THRESH_PREF, 0);
michael@0 1432
michael@0 1433 // Use each module to add an item, then unload each module in turn.
michael@0 1434 let item0 = new loader0.cm.Item({
michael@0 1435 label: "item 0",
michael@0 1436 context: loader0.cm.SelectorContext("a")
michael@0 1437 });
michael@0 1438 let item1 = new loader1.cm.Item({ label: "item 1" });
michael@0 1439
michael@0 1440 test.showMenu(null, function (popup) {
michael@0 1441 // One should be hidden
michael@0 1442 test.checkMenu([item0, item1], [item0], []);
michael@0 1443 test.done();
michael@0 1444 });
michael@0 1445 };
michael@0 1446
michael@0 1447
michael@0 1448 // Checks that we don't overflow if there are more items than the overflow
michael@0 1449 // threshold but not all of them are visible
michael@0 1450 exports.testOverflowIgnoresHidden = function (assert, done) {
michael@0 1451 let test = new TestHelper(assert, done);
michael@0 1452 let loader = test.newLoader();
michael@0 1453
michael@0 1454 let prefs = loader.loader.require("sdk/preferences/service");
michael@0 1455 prefs.set(OVERFLOW_THRESH_PREF, 2);
michael@0 1456
michael@0 1457 let allItems = [
michael@0 1458 new loader.cm.Item({
michael@0 1459 label: "item 0"
michael@0 1460 }),
michael@0 1461 new loader.cm.Item({
michael@0 1462 label: "item 1"
michael@0 1463 }),
michael@0 1464 new loader.cm.Item({
michael@0 1465 label: "item 2",
michael@0 1466 context: loader.cm.SelectorContext("a")
michael@0 1467 })
michael@0 1468 ];
michael@0 1469
michael@0 1470 test.showMenu(null, function (popup) {
michael@0 1471 // One should be hidden
michael@0 1472 test.checkMenu(allItems, [allItems[2]], []);
michael@0 1473 test.done();
michael@0 1474 });
michael@0 1475 };
michael@0 1476
michael@0 1477
michael@0 1478 // Checks that we don't overflow if there are more items than the overflow
michael@0 1479 // threshold but not all of them are visible
michael@0 1480 exports.testOverflowIgnoresHiddenMultipleModules1 = function (assert, done) {
michael@0 1481 let test = new TestHelper(assert, done);
michael@0 1482 let loader0 = test.newLoader();
michael@0 1483 let loader1 = test.newLoader();
michael@0 1484
michael@0 1485 let prefs = loader0.loader.require("sdk/preferences/service");
michael@0 1486 prefs.set(OVERFLOW_THRESH_PREF, 2);
michael@0 1487
michael@0 1488 let allItems = [
michael@0 1489 new loader0.cm.Item({
michael@0 1490 label: "item 0"
michael@0 1491 }),
michael@0 1492 new loader0.cm.Item({
michael@0 1493 label: "item 1"
michael@0 1494 }),
michael@0 1495 new loader1.cm.Item({
michael@0 1496 label: "item 2",
michael@0 1497 context: loader1.cm.SelectorContext("a")
michael@0 1498 }),
michael@0 1499 new loader1.cm.Item({
michael@0 1500 label: "item 3",
michael@0 1501 context: loader1.cm.SelectorContext("a")
michael@0 1502 })
michael@0 1503 ];
michael@0 1504
michael@0 1505 test.showMenu(null, function (popup) {
michael@0 1506 // One should be hidden
michael@0 1507 test.checkMenu(allItems, [allItems[2], allItems[3]], []);
michael@0 1508 test.done();
michael@0 1509 });
michael@0 1510 };
michael@0 1511
michael@0 1512
michael@0 1513 // Checks that we don't overflow if there are more items than the overflow
michael@0 1514 // threshold but not all of them are visible
michael@0 1515 exports.testOverflowIgnoresHiddenMultipleModules2 = function (assert, done) {
michael@0 1516 let test = new TestHelper(assert, done);
michael@0 1517 let loader0 = test.newLoader();
michael@0 1518 let loader1 = test.newLoader();
michael@0 1519
michael@0 1520 let prefs = loader0.loader.require("sdk/preferences/service");
michael@0 1521 prefs.set(OVERFLOW_THRESH_PREF, 2);
michael@0 1522
michael@0 1523 let allItems = [
michael@0 1524 new loader0.cm.Item({
michael@0 1525 label: "item 0"
michael@0 1526 }),
michael@0 1527 new loader0.cm.Item({
michael@0 1528 label: "item 1",
michael@0 1529 context: loader0.cm.SelectorContext("a")
michael@0 1530 }),
michael@0 1531 new loader1.cm.Item({
michael@0 1532 label: "item 2"
michael@0 1533 }),
michael@0 1534 new loader1.cm.Item({
michael@0 1535 label: "item 3",
michael@0 1536 context: loader1.cm.SelectorContext("a")
michael@0 1537 })
michael@0 1538 ];
michael@0 1539
michael@0 1540 test.showMenu(null, function (popup) {
michael@0 1541 // One should be hidden
michael@0 1542 test.checkMenu(allItems, [allItems[1], allItems[3]], []);
michael@0 1543 test.done();
michael@0 1544 });
michael@0 1545 };
michael@0 1546
michael@0 1547
michael@0 1548 // Checks that we don't overflow if there are more items than the overflow
michael@0 1549 // threshold but not all of them are visible
michael@0 1550 exports.testOverflowIgnoresHiddenMultipleModules3 = function (assert, done) {
michael@0 1551 let test = new TestHelper(assert, done);
michael@0 1552 let loader0 = test.newLoader();
michael@0 1553 let loader1 = test.newLoader();
michael@0 1554
michael@0 1555 let prefs = loader0.loader.require("sdk/preferences/service");
michael@0 1556 prefs.set(OVERFLOW_THRESH_PREF, 2);
michael@0 1557
michael@0 1558 let allItems = [
michael@0 1559 new loader0.cm.Item({
michael@0 1560 label: "item 0",
michael@0 1561 context: loader0.cm.SelectorContext("a")
michael@0 1562 }),
michael@0 1563 new loader0.cm.Item({
michael@0 1564 label: "item 1",
michael@0 1565 context: loader0.cm.SelectorContext("a")
michael@0 1566 }),
michael@0 1567 new loader1.cm.Item({
michael@0 1568 label: "item 2"
michael@0 1569 }),
michael@0 1570 new loader1.cm.Item({
michael@0 1571 label: "item 3"
michael@0 1572 })
michael@0 1573 ];
michael@0 1574
michael@0 1575 test.showMenu(null, function (popup) {
michael@0 1576 // One should be hidden
michael@0 1577 test.checkMenu(allItems, [allItems[0], allItems[1]], []);
michael@0 1578 test.done();
michael@0 1579 });
michael@0 1580 };
michael@0 1581
michael@0 1582
michael@0 1583 // Tests that we transition between overflowing to non-overflowing to no items
michael@0 1584 // and back again
michael@0 1585 exports.testOverflowTransition = function (assert, done) {
michael@0 1586 let test = new TestHelper(assert, done);
michael@0 1587 let loader = test.newLoader();
michael@0 1588
michael@0 1589 let prefs = loader.loader.require("sdk/preferences/service");
michael@0 1590 prefs.set(OVERFLOW_THRESH_PREF, 2);
michael@0 1591
michael@0 1592 let pItems = [
michael@0 1593 new loader.cm.Item({
michael@0 1594 label: "item 0",
michael@0 1595 context: loader.cm.SelectorContext("p")
michael@0 1596 }),
michael@0 1597 new loader.cm.Item({
michael@0 1598 label: "item 1",
michael@0 1599 context: loader.cm.SelectorContext("p")
michael@0 1600 })
michael@0 1601 ];
michael@0 1602
michael@0 1603 let aItems = [
michael@0 1604 new loader.cm.Item({
michael@0 1605 label: "item 2",
michael@0 1606 context: loader.cm.SelectorContext("a")
michael@0 1607 }),
michael@0 1608 new loader.cm.Item({
michael@0 1609 label: "item 3",
michael@0 1610 context: loader.cm.SelectorContext("a")
michael@0 1611 })
michael@0 1612 ];
michael@0 1613
michael@0 1614 let allItems = pItems.concat(aItems);
michael@0 1615
michael@0 1616 test.withTestDoc(function (window, doc) {
michael@0 1617 test.showMenu(doc.getElementById("link"), function (popup) {
michael@0 1618 // The menu should contain all items and will overflow
michael@0 1619 test.checkMenu(allItems, [], []);
michael@0 1620 popup.hidePopup();
michael@0 1621
michael@0 1622 test.showMenu(doc.getElementById("text"), function (popup) {
michael@0 1623 // Only contains hald the items and will not overflow
michael@0 1624 test.checkMenu(allItems, aItems, []);
michael@0 1625 popup.hidePopup();
michael@0 1626
michael@0 1627 test.showMenu(null, function (popup) {
michael@0 1628 // None of the items will be visible
michael@0 1629 test.checkMenu(allItems, allItems, []);
michael@0 1630 popup.hidePopup();
michael@0 1631
michael@0 1632 test.showMenu(doc.getElementById("text"), function (popup) {
michael@0 1633 // Only contains hald the items and will not overflow
michael@0 1634 test.checkMenu(allItems, aItems, []);
michael@0 1635 popup.hidePopup();
michael@0 1636
michael@0 1637 test.showMenu(doc.getElementById("link"), function (popup) {
michael@0 1638 // The menu should contain all items and will overflow
michael@0 1639 test.checkMenu(allItems, [], []);
michael@0 1640 popup.hidePopup();
michael@0 1641
michael@0 1642 test.showMenu(null, function (popup) {
michael@0 1643 // None of the items will be visible
michael@0 1644 test.checkMenu(allItems, allItems, []);
michael@0 1645 popup.hidePopup();
michael@0 1646
michael@0 1647 test.showMenu(doc.getElementById("link"), function (popup) {
michael@0 1648 // The menu should contain all items and will overflow
michael@0 1649 test.checkMenu(allItems, [], []);
michael@0 1650 test.done();
michael@0 1651 });
michael@0 1652 });
michael@0 1653 });
michael@0 1654 });
michael@0 1655 });
michael@0 1656 });
michael@0 1657 });
michael@0 1658 });
michael@0 1659 };
michael@0 1660
michael@0 1661
michael@0 1662 // An item's command listener should work.
michael@0 1663 exports.testItemCommand = function (assert, done) {
michael@0 1664 let test = new TestHelper(assert, done);
michael@0 1665 let loader = test.newLoader();
michael@0 1666
michael@0 1667 let item = new loader.cm.Item({
michael@0 1668 label: "item",
michael@0 1669 data: "item data",
michael@0 1670 contentScript: 'self.on("click", function (node, data) {' +
michael@0 1671 ' self.postMessage({' +
michael@0 1672 ' tagName: node.tagName,' +
michael@0 1673 ' data: data' +
michael@0 1674 ' });' +
michael@0 1675 '});',
michael@0 1676 onMessage: function (data) {
michael@0 1677 assert.equal(this, item, "`this` inside onMessage should be item");
michael@0 1678 assert.equal(data.tagName, "HTML", "node should be an HTML element");
michael@0 1679 assert.equal(data.data, item.data, "data should be item data");
michael@0 1680 test.done();
michael@0 1681 }
michael@0 1682 });
michael@0 1683
michael@0 1684 test.showMenu(null, function (popup) {
michael@0 1685 test.checkMenu([item], [], []);
michael@0 1686 let elt = test.getItemElt(popup, item);
michael@0 1687
michael@0 1688 // create a command event
michael@0 1689 let evt = elt.ownerDocument.createEvent('Event');
michael@0 1690 evt.initEvent('command', true, true);
michael@0 1691 elt.dispatchEvent(evt);
michael@0 1692 });
michael@0 1693 };
michael@0 1694
michael@0 1695
michael@0 1696 // A menu's click listener should work and receive bubbling 'command' events from
michael@0 1697 // sub-items appropriately. This also tests menus and ensures that when a CSS
michael@0 1698 // selector context matches the clicked node's ancestor, the matching ancestor
michael@0 1699 // is passed to listeners as the clicked node.
michael@0 1700 exports.testMenuCommand = function (assert, done) {
michael@0 1701 // Create a top-level menu, submenu, and item, like this:
michael@0 1702 // topMenu -> submenu -> item
michael@0 1703 // Click the item and make sure the click bubbles.
michael@0 1704 let test = new TestHelper(assert, done);
michael@0 1705 let loader = test.newLoader();
michael@0 1706
michael@0 1707 let item = new loader.cm.Item({
michael@0 1708 label: "submenu item",
michael@0 1709 data: "submenu item data",
michael@0 1710 context: loader.cm.SelectorContext("a"),
michael@0 1711 });
michael@0 1712
michael@0 1713 let submenu = new loader.cm.Menu({
michael@0 1714 label: "submenu",
michael@0 1715 context: loader.cm.SelectorContext("a"),
michael@0 1716 items: [item]
michael@0 1717 });
michael@0 1718
michael@0 1719 let topMenu = new loader.cm.Menu({
michael@0 1720 label: "top menu",
michael@0 1721 contentScript: 'self.on("click", function (node, data) {' +
michael@0 1722 ' self.postMessage({' +
michael@0 1723 ' tagName: node.tagName,' +
michael@0 1724 ' data: data' +
michael@0 1725 ' });' +
michael@0 1726 '});',
michael@0 1727 onMessage: function (data) {
michael@0 1728 assert.equal(this, topMenu, "`this` inside top menu should be menu");
michael@0 1729 assert.equal(data.tagName, "A", "Clicked node should be anchor");
michael@0 1730 assert.equal(data.data, item.data,
michael@0 1731 "Clicked item data should be correct");
michael@0 1732 test.done();
michael@0 1733 },
michael@0 1734 items: [submenu],
michael@0 1735 context: loader.cm.SelectorContext("a")
michael@0 1736 });
michael@0 1737
michael@0 1738 test.withTestDoc(function (window, doc) {
michael@0 1739 test.showMenu(doc.getElementById("span-link"), function (popup) {
michael@0 1740 test.checkMenu([topMenu], [], []);
michael@0 1741 let topMenuElt = test.getItemElt(popup, topMenu);
michael@0 1742 let topMenuPopup = topMenuElt.firstChild;
michael@0 1743 let submenuElt = test.getItemElt(topMenuPopup, submenu);
michael@0 1744 let submenuPopup = submenuElt.firstChild;
michael@0 1745 let itemElt = test.getItemElt(submenuPopup, item);
michael@0 1746
michael@0 1747 // create a command event
michael@0 1748 let evt = itemElt.ownerDocument.createEvent('Event');
michael@0 1749 evt.initEvent('command', true, true);
michael@0 1750 itemElt.dispatchEvent(evt);
michael@0 1751 });
michael@0 1752 });
michael@0 1753 };
michael@0 1754
michael@0 1755
michael@0 1756 // Click listeners should work when multiple modules are loaded.
michael@0 1757 exports.testItemCommandMultipleModules = function (assert, done) {
michael@0 1758 let test = new TestHelper(assert, done);
michael@0 1759 let loader0 = test.newLoader();
michael@0 1760 let loader1 = test.newLoader();
michael@0 1761
michael@0 1762 let item0 = loader0.cm.Item({
michael@0 1763 label: "loader 0 item",
michael@0 1764 contentScript: 'self.on("click", self.postMessage);',
michael@0 1765 onMessage: function () {
michael@0 1766 test.fail("loader 0 item should not emit click event");
michael@0 1767 }
michael@0 1768 });
michael@0 1769 let item1 = loader1.cm.Item({
michael@0 1770 label: "loader 1 item",
michael@0 1771 contentScript: 'self.on("click", self.postMessage);',
michael@0 1772 onMessage: function () {
michael@0 1773 test.pass("loader 1 item clicked as expected");
michael@0 1774 test.done();
michael@0 1775 }
michael@0 1776 });
michael@0 1777
michael@0 1778 test.showMenu(null, function (popup) {
michael@0 1779 test.checkMenu([item0, item1], [], []);
michael@0 1780 let item1Elt = test.getItemElt(popup, item1);
michael@0 1781
michael@0 1782 // create a command event
michael@0 1783 let evt = item1Elt.ownerDocument.createEvent('Event');
michael@0 1784 evt.initEvent('command', true, true);
michael@0 1785 item1Elt.dispatchEvent(evt);
michael@0 1786 });
michael@0 1787 };
michael@0 1788
michael@0 1789
michael@0 1790
michael@0 1791
michael@0 1792 // An item's click listener should work.
michael@0 1793 exports.testItemClick = function (assert, done) {
michael@0 1794 let test = new TestHelper(assert, done);
michael@0 1795 let loader = test.newLoader();
michael@0 1796
michael@0 1797 let item = new loader.cm.Item({
michael@0 1798 label: "item",
michael@0 1799 data: "item data",
michael@0 1800 contentScript: 'self.on("click", function (node, data) {' +
michael@0 1801 ' self.postMessage({' +
michael@0 1802 ' tagName: node.tagName,' +
michael@0 1803 ' data: data' +
michael@0 1804 ' });' +
michael@0 1805 '});',
michael@0 1806 onMessage: function (data) {
michael@0 1807 assert.equal(this, item, "`this` inside onMessage should be item");
michael@0 1808 assert.equal(data.tagName, "HTML", "node should be an HTML element");
michael@0 1809 assert.equal(data.data, item.data, "data should be item data");
michael@0 1810 test.done();
michael@0 1811 }
michael@0 1812 });
michael@0 1813
michael@0 1814 test.showMenu(null, function (popup) {
michael@0 1815 test.checkMenu([item], [], []);
michael@0 1816 let elt = test.getItemElt(popup, item);
michael@0 1817 elt.click();
michael@0 1818 });
michael@0 1819 };
michael@0 1820
michael@0 1821
michael@0 1822 // A menu's click listener should work and receive bubbling clicks from
michael@0 1823 // sub-items appropriately. This also tests menus and ensures that when a CSS
michael@0 1824 // selector context matches the clicked node's ancestor, the matching ancestor
michael@0 1825 // is passed to listeners as the clicked node.
michael@0 1826 exports.testMenuClick = function (assert, done) {
michael@0 1827 // Create a top-level menu, submenu, and item, like this:
michael@0 1828 // topMenu -> submenu -> item
michael@0 1829 // Click the item and make sure the click bubbles.
michael@0 1830 let test = new TestHelper(assert, done);
michael@0 1831 let loader = test.newLoader();
michael@0 1832
michael@0 1833 let item = new loader.cm.Item({
michael@0 1834 label: "submenu item",
michael@0 1835 data: "submenu item data",
michael@0 1836 context: loader.cm.SelectorContext("a"),
michael@0 1837 });
michael@0 1838
michael@0 1839 let submenu = new loader.cm.Menu({
michael@0 1840 label: "submenu",
michael@0 1841 context: loader.cm.SelectorContext("a"),
michael@0 1842 items: [item]
michael@0 1843 });
michael@0 1844
michael@0 1845 let topMenu = new loader.cm.Menu({
michael@0 1846 label: "top menu",
michael@0 1847 contentScript: 'self.on("click", function (node, data) {' +
michael@0 1848 ' self.postMessage({' +
michael@0 1849 ' tagName: node.tagName,' +
michael@0 1850 ' data: data' +
michael@0 1851 ' });' +
michael@0 1852 '});',
michael@0 1853 onMessage: function (data) {
michael@0 1854 assert.equal(this, topMenu, "`this` inside top menu should be menu");
michael@0 1855 assert.equal(data.tagName, "A", "Clicked node should be anchor");
michael@0 1856 assert.equal(data.data, item.data,
michael@0 1857 "Clicked item data should be correct");
michael@0 1858 test.done();
michael@0 1859 },
michael@0 1860 items: [submenu],
michael@0 1861 context: loader.cm.SelectorContext("a")
michael@0 1862 });
michael@0 1863
michael@0 1864 test.withTestDoc(function (window, doc) {
michael@0 1865 test.showMenu(doc.getElementById("span-link"), function (popup) {
michael@0 1866 test.checkMenu([topMenu], [], []);
michael@0 1867 let topMenuElt = test.getItemElt(popup, topMenu);
michael@0 1868 let topMenuPopup = topMenuElt.firstChild;
michael@0 1869 let submenuElt = test.getItemElt(topMenuPopup, submenu);
michael@0 1870 let submenuPopup = submenuElt.firstChild;
michael@0 1871 let itemElt = test.getItemElt(submenuPopup, item);
michael@0 1872 itemElt.click();
michael@0 1873 });
michael@0 1874 });
michael@0 1875 };
michael@0 1876
michael@0 1877 // Click listeners should work when multiple modules are loaded.
michael@0 1878 exports.testItemClickMultipleModules = function (assert, done) {
michael@0 1879 let test = new TestHelper(assert, done);
michael@0 1880 let loader0 = test.newLoader();
michael@0 1881 let loader1 = test.newLoader();
michael@0 1882
michael@0 1883 let item0 = loader0.cm.Item({
michael@0 1884 label: "loader 0 item",
michael@0 1885 contentScript: 'self.on("click", self.postMessage);',
michael@0 1886 onMessage: function () {
michael@0 1887 test.fail("loader 0 item should not emit click event");
michael@0 1888 }
michael@0 1889 });
michael@0 1890 let item1 = loader1.cm.Item({
michael@0 1891 label: "loader 1 item",
michael@0 1892 contentScript: 'self.on("click", self.postMessage);',
michael@0 1893 onMessage: function () {
michael@0 1894 test.pass("loader 1 item clicked as expected");
michael@0 1895 test.done();
michael@0 1896 }
michael@0 1897 });
michael@0 1898
michael@0 1899 test.showMenu(null, function (popup) {
michael@0 1900 test.checkMenu([item0, item1], [], []);
michael@0 1901 let item1Elt = test.getItemElt(popup, item1);
michael@0 1902 item1Elt.click();
michael@0 1903 });
michael@0 1904 };
michael@0 1905
michael@0 1906
michael@0 1907 // Adding a separator to a submenu should work OK.
michael@0 1908 exports.testSeparator = function (assert, done) {
michael@0 1909 let test = new TestHelper(assert, done);
michael@0 1910 let loader = test.newLoader();
michael@0 1911
michael@0 1912 let menu = new loader.cm.Menu({
michael@0 1913 label: "submenu",
michael@0 1914 items: [new loader.cm.Separator()]
michael@0 1915 });
michael@0 1916
michael@0 1917 test.showMenu(null, function (popup) {
michael@0 1918 test.checkMenu([menu], [], []);
michael@0 1919 test.done();
michael@0 1920 });
michael@0 1921 };
michael@0 1922
michael@0 1923
michael@0 1924 // The parentMenu option should work
michael@0 1925 exports.testParentMenu = function (assert, done) {
michael@0 1926 let test = new TestHelper(assert, done);
michael@0 1927 let loader = test.newLoader();
michael@0 1928
michael@0 1929 let menu = new loader.cm.Menu({
michael@0 1930 label: "submenu",
michael@0 1931 items: [loader.cm.Item({ label: "item 1" })],
michael@0 1932 parentMenu: loader.cm.contentContextMenu
michael@0 1933 });
michael@0 1934
michael@0 1935 let item = loader.cm.Item({
michael@0 1936 label: "item 2",
michael@0 1937 parentMenu: menu,
michael@0 1938 });
michael@0 1939
michael@0 1940 assert.equal(menu.items[1], item, "Item should be in the sub menu");
michael@0 1941
michael@0 1942 test.showMenu(null, function (popup) {
michael@0 1943 test.checkMenu([menu], [], []);
michael@0 1944 test.done();
michael@0 1945 });
michael@0 1946 };
michael@0 1947
michael@0 1948
michael@0 1949 // Existing context menu modifications should apply to new windows.
michael@0 1950 exports.testNewWindow = function (assert, done) {
michael@0 1951 let test = new TestHelper(assert, done);
michael@0 1952 let loader = test.newLoader();
michael@0 1953
michael@0 1954 let item = new loader.cm.Item({ label: "item" });
michael@0 1955
michael@0 1956 test.withNewWindow(function () {
michael@0 1957 test.showMenu(null, function (popup) {
michael@0 1958 test.checkMenu([item], [], []);
michael@0 1959 test.done();
michael@0 1960 });
michael@0 1961 });
michael@0 1962 };
michael@0 1963
michael@0 1964
michael@0 1965 // When a new window is opened, items added by an unloaded module should not
michael@0 1966 // be present in the menu.
michael@0 1967 exports.testNewWindowMultipleModules = function (assert, done) {
michael@0 1968 let test = new TestHelper(assert, done);
michael@0 1969 let loader = test.newLoader();
michael@0 1970 let item = new loader.cm.Item({ label: "item" });
michael@0 1971
michael@0 1972 test.showMenu(null, function (popup) {
michael@0 1973 test.checkMenu([item], [], []);
michael@0 1974 popup.hidePopup();
michael@0 1975 loader.unload();
michael@0 1976 test.withNewWindow(function () {
michael@0 1977 test.showMenu(null, function (popup) {
michael@0 1978 test.checkMenu([item], [], [item]);
michael@0 1979 test.done();
michael@0 1980 });
michael@0 1981 });
michael@0 1982 });
michael@0 1983 };
michael@0 1984
michael@0 1985
michael@0 1986 // Existing context menu modifications should not apply to new private windows.
michael@0 1987 exports.testNewPrivateWindow = function (assert, done) {
michael@0 1988 let test = new TestHelper(assert, done);
michael@0 1989 let loader = test.newLoader();
michael@0 1990
michael@0 1991 let item = new loader.cm.Item({ label: "item" });
michael@0 1992
michael@0 1993 test.showMenu(null, function (popup) {
michael@0 1994 test.checkMenu([item], [], []);
michael@0 1995 popup.hidePopup();
michael@0 1996
michael@0 1997 test.withNewPrivateWindow(function () {
michael@0 1998 test.showMenu(null, function (popup) {
michael@0 1999 test.checkMenu([], [], []);
michael@0 2000 test.done();
michael@0 2001 });
michael@0 2002 });
michael@0 2003 });
michael@0 2004 };
michael@0 2005
michael@0 2006
michael@0 2007 // Existing context menu modifications should apply to new private windows when
michael@0 2008 // private browsing support is enabled.
michael@0 2009 exports.testNewPrivateEnabledWindow = function (assert, done) {
michael@0 2010 let test = new TestHelper(assert, done);
michael@0 2011 let loader = test.newPrivateLoader();
michael@0 2012
michael@0 2013 let item = new loader.cm.Item({ label: "item" });
michael@0 2014
michael@0 2015 test.showMenu(null, function (popup) {
michael@0 2016 test.checkMenu([item], [], []);
michael@0 2017 popup.hidePopup();
michael@0 2018
michael@0 2019 test.withNewPrivateWindow(function () {
michael@0 2020 test.showMenu(null, function (popup) {
michael@0 2021 test.checkMenu([item], [], []);
michael@0 2022 test.done();
michael@0 2023 });
michael@0 2024 });
michael@0 2025 });
michael@0 2026 };
michael@0 2027
michael@0 2028
michael@0 2029 // Existing context menu modifications should apply to new private windows when
michael@0 2030 // private browsing support is enabled unless unloaded.
michael@0 2031 exports.testNewPrivateEnabledWindowUnloaded = function (assert, done) {
michael@0 2032 let test = new TestHelper(assert, done);
michael@0 2033 let loader = test.newPrivateLoader();
michael@0 2034
michael@0 2035 let item = new loader.cm.Item({ label: "item" });
michael@0 2036
michael@0 2037 test.showMenu(null, function (popup) {
michael@0 2038 test.checkMenu([item], [], []);
michael@0 2039 popup.hidePopup();
michael@0 2040
michael@0 2041 loader.unload();
michael@0 2042
michael@0 2043 test.withNewPrivateWindow(function () {
michael@0 2044 test.showMenu(null, function (popup) {
michael@0 2045 test.checkMenu([], [], []);
michael@0 2046 test.done();
michael@0 2047 });
michael@0 2048 });
michael@0 2049 });
michael@0 2050 };
michael@0 2051
michael@0 2052
michael@0 2053 // Items in the context menu should be sorted according to locale.
michael@0 2054 exports.testSorting = function (assert, done) {
michael@0 2055 let test = new TestHelper(assert, done);
michael@0 2056 let loader = test.newLoader();
michael@0 2057
michael@0 2058 // Make an unsorted items list. It'll look like this:
michael@0 2059 // item 1, item 0, item 3, item 2, item 5, item 4, ...
michael@0 2060 let items = [];
michael@0 2061 for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i += 2) {
michael@0 2062 items.push(new loader.cm.Item({ label: "item " + (i + 1) }));
michael@0 2063 items.push(new loader.cm.Item({ label: "item " + i }));
michael@0 2064 }
michael@0 2065
michael@0 2066 test.showMenu(null, function (popup) {
michael@0 2067 test.checkMenu(items, [], []);
michael@0 2068 test.done();
michael@0 2069 });
michael@0 2070 };
michael@0 2071
michael@0 2072
michael@0 2073 // Items in the overflow menu should be sorted according to locale.
michael@0 2074 exports.testSortingOverflow = function (assert, done) {
michael@0 2075 let test = new TestHelper(assert, done);
michael@0 2076 let loader = test.newLoader();
michael@0 2077
michael@0 2078 // Make an unsorted items list. It'll look like this:
michael@0 2079 // item 1, item 0, item 3, item 2, item 5, item 4, ...
michael@0 2080 let items = [];
michael@0 2081 for (let i = 0; i < OVERFLOW_THRESH_DEFAULT * 2; i += 2) {
michael@0 2082 items.push(new loader.cm.Item({ label: "item " + (i + 1) }));
michael@0 2083 items.push(new loader.cm.Item({ label: "item " + i }));
michael@0 2084 }
michael@0 2085
michael@0 2086 test.showMenu(null, function (popup) {
michael@0 2087 test.checkMenu(items, [], []);
michael@0 2088 test.done();
michael@0 2089 });
michael@0 2090 };
michael@0 2091
michael@0 2092
michael@0 2093 // Multiple modules shouldn't interfere with sorting.
michael@0 2094 exports.testSortingMultipleModules = function (assert, done) {
michael@0 2095 let test = new TestHelper(assert, done);
michael@0 2096 let loader0 = test.newLoader();
michael@0 2097 let loader1 = test.newLoader();
michael@0 2098
michael@0 2099 let items0 = [];
michael@0 2100 let items1 = [];
michael@0 2101 for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) {
michael@0 2102 if (i % 2) {
michael@0 2103 let item = new loader0.cm.Item({ label: "item " + i });
michael@0 2104 items0.push(item);
michael@0 2105 }
michael@0 2106 else {
michael@0 2107 let item = new loader1.cm.Item({ label: "item " + i });
michael@0 2108 items1.push(item);
michael@0 2109 }
michael@0 2110 }
michael@0 2111 let allItems = items0.concat(items1);
michael@0 2112
michael@0 2113 test.showMenu(null, function (popup) {
michael@0 2114
michael@0 2115 // All items should be present and sorted.
michael@0 2116 test.checkMenu(allItems, [], []);
michael@0 2117 popup.hidePopup();
michael@0 2118 loader0.unload();
michael@0 2119 loader1.unload();
michael@0 2120 test.showMenu(null, function (popup) {
michael@0 2121
michael@0 2122 // All items should be removed.
michael@0 2123 test.checkMenu(allItems, [], allItems);
michael@0 2124 test.done();
michael@0 2125 });
michael@0 2126 });
michael@0 2127 };
michael@0 2128
michael@0 2129
michael@0 2130 // Content click handlers and context handlers should be able to communicate,
michael@0 2131 // i.e., they're eval'ed in the same worker and sandbox.
michael@0 2132 exports.testContentCommunication = function (assert, done) {
michael@0 2133 let test = new TestHelper(assert, done);
michael@0 2134 let loader = test.newLoader();
michael@0 2135
michael@0 2136 let item = new loader.cm.Item({
michael@0 2137 label: "item",
michael@0 2138 contentScript: 'var potato;' +
michael@0 2139 'self.on("context", function () {' +
michael@0 2140 ' potato = "potato";' +
michael@0 2141 ' return true;' +
michael@0 2142 '});' +
michael@0 2143 'self.on("click", function () {' +
michael@0 2144 ' self.postMessage(potato);' +
michael@0 2145 '});',
michael@0 2146 });
michael@0 2147
michael@0 2148 item.on("message", function (data) {
michael@0 2149 assert.equal(data, "potato", "That's a lot of potatoes!");
michael@0 2150 test.done();
michael@0 2151 });
michael@0 2152
michael@0 2153 test.showMenu(null, function (popup) {
michael@0 2154 test.checkMenu([item], [], []);
michael@0 2155 let elt = test.getItemElt(popup, item);
michael@0 2156 elt.click();
michael@0 2157 });
michael@0 2158 };
michael@0 2159
michael@0 2160
michael@0 2161 // When the context menu is invoked on a tab that was already open when the
michael@0 2162 // module was loaded, it should contain the expected items and content workers
michael@0 2163 // should function as expected.
michael@0 2164 exports.testLoadWithOpenTab = function (assert, done) {
michael@0 2165 let test = new TestHelper(assert, done);
michael@0 2166 test.withTestDoc(function (window, doc) {
michael@0 2167 let loader = test.newLoader();
michael@0 2168 let item = new loader.cm.Item({
michael@0 2169 label: "item",
michael@0 2170 contentScript:
michael@0 2171 'self.on("click", function () self.postMessage("click"));',
michael@0 2172 onMessage: function (msg) {
michael@0 2173 if (msg === "click")
michael@0 2174 test.done();
michael@0 2175 }
michael@0 2176 });
michael@0 2177 test.showMenu(null, function (popup) {
michael@0 2178 test.checkMenu([item], [], []);
michael@0 2179 test.getItemElt(popup, item).click();
michael@0 2180 });
michael@0 2181 });
michael@0 2182 };
michael@0 2183
michael@0 2184 // Bug 732716: Ensure that the node given in `click` event works fine
michael@0 2185 // (i.e. is correctly wrapped)
michael@0 2186 exports.testDrawImageOnClickNode = function (assert, done) {
michael@0 2187 let test = new TestHelper(assert, done);
michael@0 2188 test.withTestDoc(function (window, doc) {
michael@0 2189 let loader = test.newLoader();
michael@0 2190 let item = new loader.cm.Item({
michael@0 2191 label: "item",
michael@0 2192 context: loader.cm.SelectorContext("img"),
michael@0 2193 contentScript: "new " + function() {
michael@0 2194 self.on("click", function (img, data) {
michael@0 2195 let ctx = document.createElement("canvas").getContext("2d");
michael@0 2196 ctx.drawImage(img, 1, 1, 1, 1);
michael@0 2197 self.postMessage("done");
michael@0 2198 });
michael@0 2199 },
michael@0 2200 onMessage: function (msg) {
michael@0 2201 if (msg === "done")
michael@0 2202 test.done();
michael@0 2203 }
michael@0 2204 });
michael@0 2205 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 2206 test.checkMenu([item], [], []);
michael@0 2207 test.getItemElt(popup, item).click();
michael@0 2208 });
michael@0 2209 });
michael@0 2210 };
michael@0 2211
michael@0 2212
michael@0 2213 // Setting an item's label before the menu is ever shown should correctly change
michael@0 2214 // its label.
michael@0 2215 exports.testSetLabelBeforeShow = function (assert, done) {
michael@0 2216 let test = new TestHelper(assert, done);
michael@0 2217 let loader = test.newLoader();
michael@0 2218
michael@0 2219 let items = [
michael@0 2220 new loader.cm.Item({ label: "a" }),
michael@0 2221 new loader.cm.Item({ label: "b" })
michael@0 2222 ]
michael@0 2223 items[0].label = "z";
michael@0 2224 assert.equal(items[0].label, "z");
michael@0 2225
michael@0 2226 test.showMenu(null, function (popup) {
michael@0 2227 test.checkMenu(items, [], []);
michael@0 2228 test.done();
michael@0 2229 });
michael@0 2230 };
michael@0 2231
michael@0 2232
michael@0 2233 // Setting an item's label after the menu is shown should correctly change its
michael@0 2234 // label.
michael@0 2235 exports.testSetLabelAfterShow = function (assert, done) {
michael@0 2236 let test = new TestHelper(assert, done);
michael@0 2237 let loader = test.newLoader();
michael@0 2238
michael@0 2239 let items = [
michael@0 2240 new loader.cm.Item({ label: "a" }),
michael@0 2241 new loader.cm.Item({ label: "b" })
michael@0 2242 ];
michael@0 2243
michael@0 2244 test.showMenu(null, function (popup) {
michael@0 2245 test.checkMenu(items, [], []);
michael@0 2246 popup.hidePopup();
michael@0 2247
michael@0 2248 items[0].label = "z";
michael@0 2249 assert.equal(items[0].label, "z");
michael@0 2250 test.showMenu(null, function (popup) {
michael@0 2251 test.checkMenu(items, [], []);
michael@0 2252 test.done();
michael@0 2253 });
michael@0 2254 });
michael@0 2255 };
michael@0 2256
michael@0 2257
michael@0 2258 // Setting an item's label before the menu is ever shown should correctly change
michael@0 2259 // its label.
michael@0 2260 exports.testSetLabelBeforeShowOverflow = function (assert, done) {
michael@0 2261 let test = new TestHelper(assert, done);
michael@0 2262 let loader = test.newLoader();
michael@0 2263
michael@0 2264 let prefs = loader.loader.require("sdk/preferences/service");
michael@0 2265 prefs.set(OVERFLOW_THRESH_PREF, 0);
michael@0 2266
michael@0 2267 let items = [
michael@0 2268 new loader.cm.Item({ label: "a" }),
michael@0 2269 new loader.cm.Item({ label: "b" })
michael@0 2270 ]
michael@0 2271 items[0].label = "z";
michael@0 2272 assert.equal(items[0].label, "z");
michael@0 2273
michael@0 2274 test.showMenu(null, function (popup) {
michael@0 2275 test.checkMenu(items, [], []);
michael@0 2276 test.done();
michael@0 2277 });
michael@0 2278 };
michael@0 2279
michael@0 2280
michael@0 2281 // Setting an item's label after the menu is shown should correctly change its
michael@0 2282 // label.
michael@0 2283 exports.testSetLabelAfterShowOverflow = function (assert, done) {
michael@0 2284 let test = new TestHelper(assert, done);
michael@0 2285 let loader = test.newLoader();
michael@0 2286
michael@0 2287 let prefs = loader.loader.require("sdk/preferences/service");
michael@0 2288 prefs.set(OVERFLOW_THRESH_PREF, 0);
michael@0 2289
michael@0 2290 let items = [
michael@0 2291 new loader.cm.Item({ label: "a" }),
michael@0 2292 new loader.cm.Item({ label: "b" })
michael@0 2293 ];
michael@0 2294
michael@0 2295 test.showMenu(null, function (popup) {
michael@0 2296 test.checkMenu(items, [], []);
michael@0 2297 popup.hidePopup();
michael@0 2298
michael@0 2299 items[0].label = "z";
michael@0 2300 assert.equal(items[0].label, "z");
michael@0 2301 test.showMenu(null, function (popup) {
michael@0 2302 test.checkMenu(items, [], []);
michael@0 2303 test.done();
michael@0 2304 });
michael@0 2305 });
michael@0 2306 };
michael@0 2307
michael@0 2308
michael@0 2309 // Setting the label of an item in a Menu should work.
michael@0 2310 exports.testSetLabelMenuItem = function (assert, done) {
michael@0 2311 let test = new TestHelper(assert, done);
michael@0 2312 let loader = test.newLoader();
michael@0 2313
michael@0 2314 let menu = loader.cm.Menu({
michael@0 2315 label: "menu",
michael@0 2316 items: [loader.cm.Item({ label: "a" })]
michael@0 2317 });
michael@0 2318 menu.items[0].label = "z";
michael@0 2319
michael@0 2320 assert.equal(menu.items[0].label, "z");
michael@0 2321
michael@0 2322 test.showMenu(null, function (popup) {
michael@0 2323 test.checkMenu([menu], [], []);
michael@0 2324 test.done();
michael@0 2325 });
michael@0 2326 };
michael@0 2327
michael@0 2328
michael@0 2329 // Menu.addItem() should work.
michael@0 2330 exports.testMenuAddItem = function (assert, done) {
michael@0 2331 let test = new TestHelper(assert, done);
michael@0 2332 let loader = test.newLoader();
michael@0 2333
michael@0 2334 let menu = loader.cm.Menu({
michael@0 2335 label: "menu",
michael@0 2336 items: [
michael@0 2337 loader.cm.Item({ label: "item 0" })
michael@0 2338 ]
michael@0 2339 });
michael@0 2340 menu.addItem(loader.cm.Item({ label: "item 1" }));
michael@0 2341 menu.addItem(loader.cm.Item({ label: "item 2" }));
michael@0 2342
michael@0 2343 assert.equal(menu.items.length, 3,
michael@0 2344 "menu should have correct number of items");
michael@0 2345 for (let i = 0; i < 3; i++) {
michael@0 2346 assert.equal(menu.items[i].label, "item " + i,
michael@0 2347 "item label should be correct");
michael@0 2348 assert.equal(menu.items[i].parentMenu, menu,
michael@0 2349 "item's parent menu should be correct");
michael@0 2350 }
michael@0 2351
michael@0 2352 test.showMenu(null, function (popup) {
michael@0 2353 test.checkMenu([menu], [], []);
michael@0 2354 test.done();
michael@0 2355 });
michael@0 2356 };
michael@0 2357
michael@0 2358
michael@0 2359 // Adding the same item twice to a menu should work as expected.
michael@0 2360 exports.testMenuAddItemTwice = function (assert, done) {
michael@0 2361 let test = new TestHelper(assert, done);
michael@0 2362 let loader = test.newLoader();
michael@0 2363
michael@0 2364 let menu = loader.cm.Menu({
michael@0 2365 label: "menu",
michael@0 2366 items: []
michael@0 2367 });
michael@0 2368 let subitem = loader.cm.Item({ label: "item 1" })
michael@0 2369 menu.addItem(subitem);
michael@0 2370 menu.addItem(loader.cm.Item({ label: "item 0" }));
michael@0 2371 menu.addItem(subitem);
michael@0 2372
michael@0 2373 assert.equal(menu.items.length, 2,
michael@0 2374 "menu should have correct number of items");
michael@0 2375 for (let i = 0; i < 2; i++) {
michael@0 2376 assert.equal(menu.items[i].label, "item " + i,
michael@0 2377 "item label should be correct");
michael@0 2378 }
michael@0 2379
michael@0 2380 test.showMenu(null, function (popup) {
michael@0 2381 test.checkMenu([menu], [], []);
michael@0 2382 test.done();
michael@0 2383 });
michael@0 2384 };
michael@0 2385
michael@0 2386
michael@0 2387 // Menu.removeItem() should work.
michael@0 2388 exports.testMenuRemoveItem = function (assert, done) {
michael@0 2389 let test = new TestHelper(assert, done);
michael@0 2390 let loader = test.newLoader();
michael@0 2391
michael@0 2392 let subitem = loader.cm.Item({ label: "item 1" });
michael@0 2393 let menu = loader.cm.Menu({
michael@0 2394 label: "menu",
michael@0 2395 items: [
michael@0 2396 loader.cm.Item({ label: "item 0" }),
michael@0 2397 subitem,
michael@0 2398 loader.cm.Item({ label: "item 2" })
michael@0 2399 ]
michael@0 2400 });
michael@0 2401
michael@0 2402 // Removing twice should be harmless.
michael@0 2403 menu.removeItem(subitem);
michael@0 2404 menu.removeItem(subitem);
michael@0 2405
michael@0 2406 assert.equal(subitem.parentMenu, null,
michael@0 2407 "item's parent menu should be correct");
michael@0 2408
michael@0 2409 assert.equal(menu.items.length, 2,
michael@0 2410 "menu should have correct number of items");
michael@0 2411 assert.equal(menu.items[0].label, "item 0",
michael@0 2412 "item label should be correct");
michael@0 2413 assert.equal(menu.items[1].label, "item 2",
michael@0 2414 "item label should be correct");
michael@0 2415
michael@0 2416 test.showMenu(null, function (popup) {
michael@0 2417 test.checkMenu([menu], [], []);
michael@0 2418 test.done();
michael@0 2419 });
michael@0 2420 };
michael@0 2421
michael@0 2422
michael@0 2423 // Adding an item currently contained in one menu to another menu should work.
michael@0 2424 exports.testMenuItemSwap = function (assert, done) {
michael@0 2425 let test = new TestHelper(assert, done);
michael@0 2426 let loader = test.newLoader();
michael@0 2427
michael@0 2428 let subitem = loader.cm.Item({ label: "item" });
michael@0 2429 let menu0 = loader.cm.Menu({
michael@0 2430 label: "menu 0",
michael@0 2431 items: [subitem]
michael@0 2432 });
michael@0 2433 let menu1 = loader.cm.Menu({
michael@0 2434 label: "menu 1",
michael@0 2435 items: []
michael@0 2436 });
michael@0 2437 menu1.addItem(subitem);
michael@0 2438
michael@0 2439 assert.equal(menu0.items.length, 0,
michael@0 2440 "menu should have correct number of items");
michael@0 2441
michael@0 2442 assert.equal(menu1.items.length, 1,
michael@0 2443 "menu should have correct number of items");
michael@0 2444 assert.equal(menu1.items[0].label, "item",
michael@0 2445 "item label should be correct");
michael@0 2446
michael@0 2447 assert.equal(subitem.parentMenu, menu1,
michael@0 2448 "item's parent menu should be correct");
michael@0 2449
michael@0 2450 test.showMenu(null, function (popup) {
michael@0 2451 test.checkMenu([menu0, menu1], [menu0], []);
michael@0 2452 test.done();
michael@0 2453 });
michael@0 2454 };
michael@0 2455
michael@0 2456
michael@0 2457 // Destroying an item should remove it from its parent menu.
michael@0 2458 exports.testMenuItemDestroy = function (assert, done) {
michael@0 2459 let test = new TestHelper(assert, done);
michael@0 2460 let loader = test.newLoader();
michael@0 2461
michael@0 2462 let subitem = loader.cm.Item({ label: "item" });
michael@0 2463 let menu = loader.cm.Menu({
michael@0 2464 label: "menu",
michael@0 2465 items: [subitem]
michael@0 2466 });
michael@0 2467 subitem.destroy();
michael@0 2468
michael@0 2469 assert.equal(menu.items.length, 0,
michael@0 2470 "menu should have correct number of items");
michael@0 2471 assert.equal(subitem.parentMenu, null,
michael@0 2472 "item's parent menu should be correct");
michael@0 2473
michael@0 2474 test.showMenu(null, function (popup) {
michael@0 2475 test.checkMenu([menu], [menu], []);
michael@0 2476 test.done();
michael@0 2477 });
michael@0 2478 };
michael@0 2479
michael@0 2480
michael@0 2481 // Setting Menu.items should work.
michael@0 2482 exports.testMenuItemsSetter = function (assert, done) {
michael@0 2483 let test = new TestHelper(assert, done);
michael@0 2484 let loader = test.newLoader();
michael@0 2485
michael@0 2486 let menu = loader.cm.Menu({
michael@0 2487 label: "menu",
michael@0 2488 items: [
michael@0 2489 loader.cm.Item({ label: "old item 0" }),
michael@0 2490 loader.cm.Item({ label: "old item 1" })
michael@0 2491 ]
michael@0 2492 });
michael@0 2493 menu.items = [
michael@0 2494 loader.cm.Item({ label: "new item 0" }),
michael@0 2495 loader.cm.Item({ label: "new item 1" }),
michael@0 2496 loader.cm.Item({ label: "new item 2" })
michael@0 2497 ];
michael@0 2498
michael@0 2499 assert.equal(menu.items.length, 3,
michael@0 2500 "menu should have correct number of items");
michael@0 2501 for (let i = 0; i < 3; i++) {
michael@0 2502 assert.equal(menu.items[i].label, "new item " + i,
michael@0 2503 "item label should be correct");
michael@0 2504 assert.equal(menu.items[i].parentMenu, menu,
michael@0 2505 "item's parent menu should be correct");
michael@0 2506 }
michael@0 2507
michael@0 2508 test.showMenu(null, function (popup) {
michael@0 2509 test.checkMenu([menu], [], []);
michael@0 2510 test.done();
michael@0 2511 });
michael@0 2512 };
michael@0 2513
michael@0 2514
michael@0 2515 // Setting Item.data should work.
michael@0 2516 exports.testItemDataSetter = function (assert, done) {
michael@0 2517 let test = new TestHelper(assert, done);
michael@0 2518 let loader = test.newLoader();
michael@0 2519
michael@0 2520 let item = loader.cm.Item({ label: "old item 0", data: "old" });
michael@0 2521 item.data = "new";
michael@0 2522
michael@0 2523 assert.equal(item.data, "new", "item should have correct data");
michael@0 2524
michael@0 2525 test.showMenu(null, function (popup) {
michael@0 2526 test.checkMenu([item], [], []);
michael@0 2527 test.done();
michael@0 2528 });
michael@0 2529 };
michael@0 2530
michael@0 2531
michael@0 2532 // Open the test doc, load the module, make sure items appear when context-
michael@0 2533 // clicking the iframe.
michael@0 2534 exports.testAlreadyOpenIframe = function (assert, done) {
michael@0 2535 let test = new TestHelper(assert, done);
michael@0 2536 test.withTestDoc(function (window, doc) {
michael@0 2537 let loader = test.newLoader();
michael@0 2538 let item = new loader.cm.Item({
michael@0 2539 label: "item"
michael@0 2540 });
michael@0 2541 test.showMenu(doc.getElementById("iframe"), function (popup) {
michael@0 2542 test.checkMenu([item], [], []);
michael@0 2543 test.done();
michael@0 2544 });
michael@0 2545 });
michael@0 2546 };
michael@0 2547
michael@0 2548
michael@0 2549 // Tests that a missing label throws an exception
michael@0 2550 exports.testItemNoLabel = function (assert, done) {
michael@0 2551 let test = new TestHelper(assert, done);
michael@0 2552 let loader = test.newLoader();
michael@0 2553
michael@0 2554 try {
michael@0 2555 new loader.cm.Item({});
michael@0 2556 assert.ok(false, "Should have seen exception");
michael@0 2557 }
michael@0 2558 catch (e) {
michael@0 2559 assert.ok(true, "Should have seen exception");
michael@0 2560 }
michael@0 2561
michael@0 2562 try {
michael@0 2563 new loader.cm.Item({ label: null });
michael@0 2564 assert.ok(false, "Should have seen exception");
michael@0 2565 }
michael@0 2566 catch (e) {
michael@0 2567 assert.ok(true, "Should have seen exception");
michael@0 2568 }
michael@0 2569
michael@0 2570 try {
michael@0 2571 new loader.cm.Item({ label: undefined });
michael@0 2572 assert.ok(false, "Should have seen exception");
michael@0 2573 }
michael@0 2574 catch (e) {
michael@0 2575 assert.ok(true, "Should have seen exception");
michael@0 2576 }
michael@0 2577
michael@0 2578 try {
michael@0 2579 new loader.cm.Item({ label: "" });
michael@0 2580 assert.ok(false, "Should have seen exception");
michael@0 2581 }
michael@0 2582 catch (e) {
michael@0 2583 assert.ok(true, "Should have seen exception");
michael@0 2584 }
michael@0 2585
michael@0 2586 test.done();
michael@0 2587 }
michael@0 2588
michael@0 2589
michael@0 2590 // Tests that items can have an empty data property
michael@0 2591 exports.testItemNoData = function (assert, done) {
michael@0 2592 let test = new TestHelper(assert, done);
michael@0 2593 let loader = test.newLoader();
michael@0 2594
michael@0 2595 function checkData(data) {
michael@0 2596 assert.equal(data, undefined, "Data should be undefined");
michael@0 2597 }
michael@0 2598
michael@0 2599 let item1 = new loader.cm.Item({
michael@0 2600 label: "item 1",
michael@0 2601 contentScript: 'self.on("click", function(node, data) self.postMessage(data))',
michael@0 2602 onMessage: checkData
michael@0 2603 });
michael@0 2604 let item2 = new loader.cm.Item({
michael@0 2605 label: "item 2",
michael@0 2606 data: null,
michael@0 2607 contentScript: 'self.on("click", function(node, data) self.postMessage(data))',
michael@0 2608 onMessage: checkData
michael@0 2609 });
michael@0 2610 let item3 = new loader.cm.Item({
michael@0 2611 label: "item 3",
michael@0 2612 data: undefined,
michael@0 2613 contentScript: 'self.on("click", function(node, data) self.postMessage(data))',
michael@0 2614 onMessage: checkData
michael@0 2615 });
michael@0 2616
michael@0 2617 assert.equal(item1.data, undefined, "Should be no defined data");
michael@0 2618 assert.equal(item2.data, null, "Should be no defined data");
michael@0 2619 assert.equal(item3.data, undefined, "Should be no defined data");
michael@0 2620
michael@0 2621 test.showMenu(null, function (popup) {
michael@0 2622 test.checkMenu([item1, item2, item3], [], []);
michael@0 2623
michael@0 2624 let itemElt = test.getItemElt(popup, item1);
michael@0 2625 itemElt.click();
michael@0 2626
michael@0 2627 test.hideMenu(function() {
michael@0 2628 test.showMenu(null, function (popup) {
michael@0 2629 let itemElt = test.getItemElt(popup, item2);
michael@0 2630 itemElt.click();
michael@0 2631
michael@0 2632 test.hideMenu(function() {
michael@0 2633 test.showMenu(null, function (popup) {
michael@0 2634 let itemElt = test.getItemElt(popup, item3);
michael@0 2635 itemElt.click();
michael@0 2636
michael@0 2637 test.done();
michael@0 2638 });
michael@0 2639 });
michael@0 2640 });
michael@0 2641 });
michael@0 2642 });
michael@0 2643 }
michael@0 2644
michael@0 2645
michael@0 2646 // Tests that items without an image don't attempt to show one
michael@0 2647 exports.testItemNoImage = function (assert, done) {
michael@0 2648 let test = new TestHelper(assert, done);
michael@0 2649 let loader = test.newLoader();
michael@0 2650
michael@0 2651 let item1 = new loader.cm.Item({ label: "item 1" });
michael@0 2652 let item2 = new loader.cm.Item({ label: "item 2", image: null });
michael@0 2653 let item3 = new loader.cm.Item({ label: "item 3", image: undefined });
michael@0 2654
michael@0 2655 assert.equal(item1.image, undefined, "Should be no defined image");
michael@0 2656 assert.equal(item2.image, null, "Should be no defined image");
michael@0 2657 assert.equal(item3.image, undefined, "Should be no defined image");
michael@0 2658
michael@0 2659 test.showMenu(null, function (popup) {
michael@0 2660 test.checkMenu([item1, item2, item3], [], []);
michael@0 2661
michael@0 2662 test.done();
michael@0 2663 });
michael@0 2664 }
michael@0 2665
michael@0 2666
michael@0 2667 // Test image support.
michael@0 2668 exports.testItemImage = function (assert, done) {
michael@0 2669 let test = new TestHelper(assert, done);
michael@0 2670 let loader = test.newLoader();
michael@0 2671
michael@0 2672 let imageURL = data.url("moz_favicon.ico");
michael@0 2673 let item = new loader.cm.Item({ label: "item", image: imageURL });
michael@0 2674 let menu = new loader.cm.Menu({ label: "menu", image: imageURL, items: [
michael@0 2675 loader.cm.Item({ label: "subitem" })
michael@0 2676 ]});
michael@0 2677 assert.equal(item.image, imageURL, "Should have set the image correctly");
michael@0 2678 assert.equal(menu.image, imageURL, "Should have set the image correctly");
michael@0 2679
michael@0 2680 test.showMenu(null, function (popup) {
michael@0 2681 test.checkMenu([item, menu], [], []);
michael@0 2682
michael@0 2683 let imageURL2 = data.url("dummy.ico");
michael@0 2684 item.image = imageURL2;
michael@0 2685 menu.image = imageURL2;
michael@0 2686 assert.equal(item.image, imageURL2, "Should have set the image correctly");
michael@0 2687 assert.equal(menu.image, imageURL2, "Should have set the image correctly");
michael@0 2688 test.checkMenu([item, menu], [], []);
michael@0 2689
michael@0 2690 item.image = null;
michael@0 2691 menu.image = null;
michael@0 2692 assert.equal(item.image, null, "Should have set the image correctly");
michael@0 2693 assert.equal(menu.image, null, "Should have set the image correctly");
michael@0 2694 test.checkMenu([item, menu], [], []);
michael@0 2695
michael@0 2696 test.done();
michael@0 2697 });
michael@0 2698 };
michael@0 2699
michael@0 2700 // Test image URL validation.
michael@0 2701 exports.testItemImageValidURL = function (assert, done) {
michael@0 2702 let test = new TestHelper(assert, done);
michael@0 2703 let loader = test.newLoader();
michael@0 2704
michael@0 2705 assert.throws(function(){
michael@0 2706 new loader.cm.Item({
michael@0 2707 label: "item 1",
michael@0 2708 image: "foo"
michael@0 2709 })
michael@0 2710 }, /Image URL validation failed/
michael@0 2711 );
michael@0 2712
michael@0 2713 assert.throws(function(){
michael@0 2714 new loader.cm.Item({
michael@0 2715 label: "item 2",
michael@0 2716 image: false
michael@0 2717 })
michael@0 2718 }, /Image URL validation failed/
michael@0 2719 );
michael@0 2720
michael@0 2721 assert.throws(function(){
michael@0 2722 new loader.cm.Item({
michael@0 2723 label: "item 3",
michael@0 2724 image: 0
michael@0 2725 })
michael@0 2726 }, /Image URL validation failed/
michael@0 2727 );
michael@0 2728
michael@0 2729 let imageURL = data.url("moz_favicon.ico");
michael@0 2730 let item4 = new loader.cm.Item({ label: "item 4", image: imageURL });
michael@0 2731 let item5 = new loader.cm.Item({ label: "item 5", image: null });
michael@0 2732 let item6 = new loader.cm.Item({ label: "item 6", image: undefined });
michael@0 2733
michael@0 2734 assert.equal(item4.image, imageURL, "Should be proper image URL");
michael@0 2735 assert.equal(item5.image, null, "Should be null image");
michael@0 2736 assert.equal(item6.image, undefined, "Should be undefined image");
michael@0 2737
michael@0 2738 test.done();
michael@0 2739 };
michael@0 2740
michael@0 2741
michael@0 2742 // Menu.destroy should destroy the item tree rooted at that menu.
michael@0 2743 exports.testMenuDestroy = function (assert, done) {
michael@0 2744 let test = new TestHelper(assert, done);
michael@0 2745 let loader = test.newLoader();
michael@0 2746
michael@0 2747 let menu = loader.cm.Menu({
michael@0 2748 label: "menu",
michael@0 2749 items: [
michael@0 2750 loader.cm.Item({ label: "item 0" }),
michael@0 2751 loader.cm.Menu({
michael@0 2752 label: "item 1",
michael@0 2753 items: [
michael@0 2754 loader.cm.Item({ label: "subitem 0" }),
michael@0 2755 loader.cm.Item({ label: "subitem 1" }),
michael@0 2756 loader.cm.Item({ label: "subitem 2" })
michael@0 2757 ]
michael@0 2758 }),
michael@0 2759 loader.cm.Item({ label: "item 2" })
michael@0 2760 ]
michael@0 2761 });
michael@0 2762 menu.destroy();
michael@0 2763
michael@0 2764 /*let numRegistryEntries = 0;
michael@0 2765 loader.globalScope.browserManager.browserWins.forEach(function (bwin) {
michael@0 2766 for (let itemID in bwin.items)
michael@0 2767 numRegistryEntries++;
michael@0 2768 });
michael@0 2769 assert.equal(numRegistryEntries, 0, "All items should be unregistered.");*/
michael@0 2770
michael@0 2771 test.showMenu(null, function (popup) {
michael@0 2772 test.checkMenu([menu], [], [menu]);
michael@0 2773 test.done();
michael@0 2774 });
michael@0 2775 };
michael@0 2776
michael@0 2777 // Checks that if a menu contains sub items that are hidden then the menu is
michael@0 2778 // hidden too. Also checks that content scripts and contexts work for sub items.
michael@0 2779 exports.testSubItemContextNoMatchHideMenu = function (assert, done) {
michael@0 2780 let test = new TestHelper(assert, done);
michael@0 2781 let loader = test.newLoader();
michael@0 2782
michael@0 2783 let items = [
michael@0 2784 loader.cm.Menu({
michael@0 2785 label: "menu 1",
michael@0 2786 items: [
michael@0 2787 loader.cm.Item({
michael@0 2788 label: "subitem 1",
michael@0 2789 context: loader.cm.SelectorContext(".foo")
michael@0 2790 })
michael@0 2791 ]
michael@0 2792 }),
michael@0 2793 loader.cm.Menu({
michael@0 2794 label: "menu 2",
michael@0 2795 items: [
michael@0 2796 loader.cm.Item({
michael@0 2797 label: "subitem 2",
michael@0 2798 contentScript: 'self.on("context", function () false);'
michael@0 2799 })
michael@0 2800 ]
michael@0 2801 }),
michael@0 2802 loader.cm.Menu({
michael@0 2803 label: "menu 3",
michael@0 2804 items: [
michael@0 2805 loader.cm.Item({
michael@0 2806 label: "subitem 3",
michael@0 2807 context: loader.cm.SelectorContext(".foo")
michael@0 2808 }),
michael@0 2809 loader.cm.Item({
michael@0 2810 label: "subitem 4",
michael@0 2811 contentScript: 'self.on("context", function () false);'
michael@0 2812 })
michael@0 2813 ]
michael@0 2814 })
michael@0 2815 ];
michael@0 2816
michael@0 2817 test.showMenu(null, function (popup) {
michael@0 2818 test.checkMenu(items, items, []);
michael@0 2819 test.done();
michael@0 2820 });
michael@0 2821 };
michael@0 2822
michael@0 2823
michael@0 2824 // Checks that if a menu contains a combination of hidden and visible sub items
michael@0 2825 // then the menu is still visible too.
michael@0 2826 exports.testSubItemContextMatch = function (assert, done) {
michael@0 2827 let test = new TestHelper(assert, done);
michael@0 2828 let loader = test.newLoader();
michael@0 2829
michael@0 2830 let hiddenItems = [
michael@0 2831 loader.cm.Item({
michael@0 2832 label: "subitem 3",
michael@0 2833 context: loader.cm.SelectorContext(".foo")
michael@0 2834 }),
michael@0 2835 loader.cm.Item({
michael@0 2836 label: "subitem 6",
michael@0 2837 contentScript: 'self.on("context", function () false);'
michael@0 2838 })
michael@0 2839 ];
michael@0 2840
michael@0 2841 let items = [
michael@0 2842 loader.cm.Menu({
michael@0 2843 label: "menu 1",
michael@0 2844 items: [
michael@0 2845 loader.cm.Item({
michael@0 2846 label: "subitem 1",
michael@0 2847 context: loader.cm.URLContext(TEST_DOC_URL)
michael@0 2848 })
michael@0 2849 ]
michael@0 2850 }),
michael@0 2851 loader.cm.Menu({
michael@0 2852 label: "menu 2",
michael@0 2853 items: [
michael@0 2854 loader.cm.Item({
michael@0 2855 label: "subitem 2",
michael@0 2856 contentScript: 'self.on("context", function () true);'
michael@0 2857 })
michael@0 2858 ]
michael@0 2859 }),
michael@0 2860 loader.cm.Menu({
michael@0 2861 label: "menu 3",
michael@0 2862 items: [
michael@0 2863 hiddenItems[0],
michael@0 2864 loader.cm.Item({
michael@0 2865 label: "subitem 4",
michael@0 2866 contentScript: 'self.on("context", function () true);'
michael@0 2867 })
michael@0 2868 ]
michael@0 2869 }),
michael@0 2870 loader.cm.Menu({
michael@0 2871 label: "menu 4",
michael@0 2872 items: [
michael@0 2873 loader.cm.Item({
michael@0 2874 label: "subitem 5",
michael@0 2875 context: loader.cm.URLContext(TEST_DOC_URL)
michael@0 2876 }),
michael@0 2877 hiddenItems[1]
michael@0 2878 ]
michael@0 2879 }),
michael@0 2880 loader.cm.Menu({
michael@0 2881 label: "menu 5",
michael@0 2882 items: [
michael@0 2883 loader.cm.Item({
michael@0 2884 label: "subitem 7",
michael@0 2885 context: loader.cm.URLContext(TEST_DOC_URL)
michael@0 2886 }),
michael@0 2887 loader.cm.Item({
michael@0 2888 label: "subitem 8",
michael@0 2889 contentScript: 'self.on("context", function () true);'
michael@0 2890 })
michael@0 2891 ]
michael@0 2892 })
michael@0 2893 ];
michael@0 2894
michael@0 2895 test.withTestDoc(function (window, doc) {
michael@0 2896 test.showMenu(null, function (popup) {
michael@0 2897 test.checkMenu(items, hiddenItems, []);
michael@0 2898 test.done();
michael@0 2899 });
michael@0 2900 });
michael@0 2901 };
michael@0 2902
michael@0 2903
michael@0 2904 // Child items should default to visible, not to PageContext
michael@0 2905 exports.testSubItemDefaultVisible = function (assert, done) {
michael@0 2906 let test = new TestHelper(assert, done);
michael@0 2907 let loader = test.newLoader();
michael@0 2908
michael@0 2909 let items = [
michael@0 2910 loader.cm.Menu({
michael@0 2911 label: "menu 1",
michael@0 2912 context: loader.cm.SelectorContext("img"),
michael@0 2913 items: [
michael@0 2914 loader.cm.Item({
michael@0 2915 label: "subitem 1"
michael@0 2916 }),
michael@0 2917 loader.cm.Item({
michael@0 2918 label: "subitem 2",
michael@0 2919 context: loader.cm.SelectorContext("img")
michael@0 2920 }),
michael@0 2921 loader.cm.Item({
michael@0 2922 label: "subitem 3",
michael@0 2923 context: loader.cm.SelectorContext("a")
michael@0 2924 })
michael@0 2925 ]
michael@0 2926 })
michael@0 2927 ];
michael@0 2928
michael@0 2929 // subitem 3 will be hidden
michael@0 2930 let hiddenItems = [items[0].items[2]];
michael@0 2931
michael@0 2932 test.withTestDoc(function (window, doc) {
michael@0 2933 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 2934 test.checkMenu(items, hiddenItems, []);
michael@0 2935 test.done();
michael@0 2936 });
michael@0 2937 });
michael@0 2938 };
michael@0 2939
michael@0 2940 // Tests that the click event on sub menuitem
michael@0 2941 // tiggers the click event for the sub menuitem and the parent menu
michael@0 2942 exports.testSubItemClick = function (assert, done) {
michael@0 2943 let test = new TestHelper(assert, done);
michael@0 2944 let loader = test.newLoader();
michael@0 2945
michael@0 2946 let state = 0;
michael@0 2947
michael@0 2948 let items = [
michael@0 2949 loader.cm.Menu({
michael@0 2950 label: "menu 1",
michael@0 2951 items: [
michael@0 2952 loader.cm.Item({
michael@0 2953 label: "subitem 1",
michael@0 2954 data: "foobar",
michael@0 2955 contentScript: 'self.on("click", function (node, data) {' +
michael@0 2956 ' self.postMessage({' +
michael@0 2957 ' tagName: node.tagName,' +
michael@0 2958 ' data: data' +
michael@0 2959 ' });' +
michael@0 2960 '});',
michael@0 2961 onMessage: function(msg) {
michael@0 2962 assert.equal(msg.tagName, "HTML", "should have seen the right node");
michael@0 2963 assert.equal(msg.data, "foobar", "should have seen the right data");
michael@0 2964 assert.equal(state, 0, "should have seen the event at the right time");
michael@0 2965 state++;
michael@0 2966 }
michael@0 2967 })
michael@0 2968 ],
michael@0 2969 contentScript: 'self.on("click", function (node, data) {' +
michael@0 2970 ' self.postMessage({' +
michael@0 2971 ' tagName: node.tagName,' +
michael@0 2972 ' data: data' +
michael@0 2973 ' });' +
michael@0 2974 '});',
michael@0 2975 onMessage: function(msg) {
michael@0 2976 assert.equal(msg.tagName, "HTML", "should have seen the right node");
michael@0 2977 assert.equal(msg.data, "foobar", "should have seen the right data");
michael@0 2978 assert.equal(state, 1, "should have seen the event at the right time");
michael@0 2979
michael@0 2980 test.done();
michael@0 2981 }
michael@0 2982 })
michael@0 2983 ];
michael@0 2984
michael@0 2985 test.withTestDoc(function (window, doc) {
michael@0 2986 test.showMenu(null, function (popup) {
michael@0 2987 test.checkMenu(items, [], []);
michael@0 2988
michael@0 2989 let topMenuElt = test.getItemElt(popup, items[0]);
michael@0 2990 let topMenuPopup = topMenuElt.firstChild;
michael@0 2991 let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]);
michael@0 2992 itemElt.click();
michael@0 2993 });
michael@0 2994 });
michael@0 2995 };
michael@0 2996
michael@0 2997 // Tests that the command event on sub menuitem
michael@0 2998 // tiggers the click event for the sub menuitem and the parent menu
michael@0 2999 exports.testSubItemCommand = function (assert, done) {
michael@0 3000 let test = new TestHelper(assert, done);
michael@0 3001 let loader = test.newLoader();
michael@0 3002
michael@0 3003 let state = 0;
michael@0 3004
michael@0 3005 let items = [
michael@0 3006 loader.cm.Menu({
michael@0 3007 label: "menu 1",
michael@0 3008 items: [
michael@0 3009 loader.cm.Item({
michael@0 3010 label: "subitem 1",
michael@0 3011 data: "foobar",
michael@0 3012 contentScript: 'self.on("click", function (node, data) {' +
michael@0 3013 ' self.postMessage({' +
michael@0 3014 ' tagName: node.tagName,' +
michael@0 3015 ' data: data' +
michael@0 3016 ' });' +
michael@0 3017 '});',
michael@0 3018 onMessage: function(msg) {
michael@0 3019 assert.equal(msg.tagName, "HTML", "should have seen the right node");
michael@0 3020 assert.equal(msg.data, "foobar", "should have seen the right data");
michael@0 3021 assert.equal(state, 0, "should have seen the event at the right time");
michael@0 3022 state++;
michael@0 3023 }
michael@0 3024 })
michael@0 3025 ],
michael@0 3026 contentScript: 'self.on("click", function (node, data) {' +
michael@0 3027 ' self.postMessage({' +
michael@0 3028 ' tagName: node.tagName,' +
michael@0 3029 ' data: data' +
michael@0 3030 ' });' +
michael@0 3031 '});',
michael@0 3032 onMessage: function(msg) {
michael@0 3033 assert.equal(msg.tagName, "HTML", "should have seen the right node");
michael@0 3034 assert.equal(msg.data, "foobar", "should have seen the right data");
michael@0 3035 assert.equal(state, 1, "should have seen the event at the right time");
michael@0 3036 state++
michael@0 3037
michael@0 3038 test.done();
michael@0 3039 }
michael@0 3040 })
michael@0 3041 ];
michael@0 3042
michael@0 3043 test.withTestDoc(function (window, doc) {
michael@0 3044 test.showMenu(null, function (popup) {
michael@0 3045 test.checkMenu(items, [], []);
michael@0 3046
michael@0 3047 let topMenuElt = test.getItemElt(popup, items[0]);
michael@0 3048 let topMenuPopup = topMenuElt.firstChild;
michael@0 3049 let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]);
michael@0 3050
michael@0 3051 // create a command event
michael@0 3052 let evt = itemElt.ownerDocument.createEvent('Event');
michael@0 3053 evt.initEvent('command', true, true);
michael@0 3054 itemElt.dispatchEvent(evt);
michael@0 3055 });
michael@0 3056 });
michael@0 3057 };
michael@0 3058
michael@0 3059 // Tests that opening a context menu for an outer frame when an inner frame
michael@0 3060 // has a selection doesn't activate the SelectionContext
michael@0 3061 exports.testSelectionInInnerFrameNoMatch = function (assert, done) {
michael@0 3062 let test = new TestHelper(assert, done);
michael@0 3063 let loader = test.newLoader();
michael@0 3064
michael@0 3065 let state = 0;
michael@0 3066
michael@0 3067 let items = [
michael@0 3068 loader.cm.Item({
michael@0 3069 label: "test item",
michael@0 3070 context: loader.cm.SelectionContext()
michael@0 3071 })
michael@0 3072 ];
michael@0 3073
michael@0 3074 test.withTestDoc(function (window, doc) {
michael@0 3075 let frame = doc.getElementById("iframe");
michael@0 3076 frame.contentWindow.getSelection().selectAllChildren(frame.contentDocument.body);
michael@0 3077
michael@0 3078 test.showMenu(null, function (popup) {
michael@0 3079 test.checkMenu(items, items, []);
michael@0 3080 test.done();
michael@0 3081 });
michael@0 3082 });
michael@0 3083 };
michael@0 3084
michael@0 3085 // Tests that opening a context menu for an inner frame when the inner frame
michael@0 3086 // has a selection does activate the SelectionContext
michael@0 3087 exports.testSelectionInInnerFrameMatch = function (assert, done) {
michael@0 3088 let test = new TestHelper(assert, done);
michael@0 3089 let loader = test.newLoader();
michael@0 3090
michael@0 3091 let state = 0;
michael@0 3092
michael@0 3093 let items = [
michael@0 3094 loader.cm.Item({
michael@0 3095 label: "test item",
michael@0 3096 context: loader.cm.SelectionContext()
michael@0 3097 })
michael@0 3098 ];
michael@0 3099
michael@0 3100 test.withTestDoc(function (window, doc) {
michael@0 3101 let frame = doc.getElementById("iframe");
michael@0 3102 frame.contentWindow.getSelection().selectAllChildren(frame.contentDocument.body);
michael@0 3103
michael@0 3104 test.showMenu(frame.contentDocument.getElementById("text"), function (popup) {
michael@0 3105 test.checkMenu(items, [], []);
michael@0 3106 test.done();
michael@0 3107 });
michael@0 3108 });
michael@0 3109 };
michael@0 3110
michael@0 3111 // Tests that opening a context menu for an inner frame when the outer frame
michael@0 3112 // has a selection doesn't activate the SelectionContext
michael@0 3113 exports.testSelectionInOuterFrameNoMatch = function (assert, done) {
michael@0 3114 let test = new TestHelper(assert, done);
michael@0 3115 let loader = test.newLoader();
michael@0 3116
michael@0 3117 let state = 0;
michael@0 3118
michael@0 3119 let items = [
michael@0 3120 loader.cm.Item({
michael@0 3121 label: "test item",
michael@0 3122 context: loader.cm.SelectionContext()
michael@0 3123 })
michael@0 3124 ];
michael@0 3125
michael@0 3126 test.withTestDoc(function (window, doc) {
michael@0 3127 let frame = doc.getElementById("iframe");
michael@0 3128 window.getSelection().selectAllChildren(doc.body);
michael@0 3129
michael@0 3130 test.showMenu(frame.contentDocument.getElementById("text"), function (popup) {
michael@0 3131 test.checkMenu(items, items, []);
michael@0 3132 test.done();
michael@0 3133 });
michael@0 3134 });
michael@0 3135 };
michael@0 3136
michael@0 3137
michael@0 3138 // Test that the return value of the predicate function determines if
michael@0 3139 // item is shown
michael@0 3140 exports.testPredicateContextControl = function (assert, done) {
michael@0 3141 let test = new TestHelper(assert, done);
michael@0 3142 let loader = test.newLoader();
michael@0 3143
michael@0 3144 let itemTrue = loader.cm.Item({
michael@0 3145 label: "visible",
michael@0 3146 context: loader.cm.PredicateContext(function () { return true; })
michael@0 3147 });
michael@0 3148
michael@0 3149 let itemFalse = loader.cm.Item({
michael@0 3150 label: "hidden",
michael@0 3151 context: loader.cm.PredicateContext(function () { return false; })
michael@0 3152 });
michael@0 3153
michael@0 3154 test.showMenu(null, function (popup) {
michael@0 3155 test.checkMenu([itemTrue, itemFalse], [itemFalse], []);
michael@0 3156 test.done();
michael@0 3157 });
michael@0 3158 };
michael@0 3159
michael@0 3160 // Test that the data object has the correct document type
michael@0 3161 exports.testPredicateContextDocumentType = function (assert, done) {
michael@0 3162 let test = new TestHelper(assert, done);
michael@0 3163 let loader = test.newLoader();
michael@0 3164
michael@0 3165 let items = [loader.cm.Item({
michael@0 3166 label: "item",
michael@0 3167 context: loader.cm.PredicateContext(function (data) {
michael@0 3168 assert.equal(data.documentType, 'text/html');
michael@0 3169 return true;
michael@0 3170 })
michael@0 3171 })];
michael@0 3172
michael@0 3173 test.withTestDoc(function (window, doc) {
michael@0 3174 test.showMenu(null, function (popup) {
michael@0 3175 test.checkMenu(items, [], []);
michael@0 3176 test.done();
michael@0 3177 });
michael@0 3178 });
michael@0 3179 };
michael@0 3180
michael@0 3181 // Test that the data object has the correct document URL
michael@0 3182 exports.testPredicateContextDocumentURL = function (assert, done) {
michael@0 3183 let test = new TestHelper(assert, done);
michael@0 3184 let loader = test.newLoader();
michael@0 3185
michael@0 3186 let items = [loader.cm.Item({
michael@0 3187 label: "item",
michael@0 3188 context: loader.cm.PredicateContext(function (data) {
michael@0 3189 assert.equal(data.documentURL, TEST_DOC_URL);
michael@0 3190 return true;
michael@0 3191 })
michael@0 3192 })];
michael@0 3193
michael@0 3194 test.withTestDoc(function (window, doc) {
michael@0 3195 test.showMenu(null, function (popup) {
michael@0 3196 test.checkMenu(items, [], []);
michael@0 3197 test.done();
michael@0 3198 });
michael@0 3199 });
michael@0 3200 };
michael@0 3201
michael@0 3202
michael@0 3203 // Test that the data object has the correct element name
michael@0 3204 exports.testPredicateContextTargetName = function (assert, done) {
michael@0 3205 let test = new TestHelper(assert, done);
michael@0 3206 let loader = test.newLoader();
michael@0 3207
michael@0 3208 let items = [loader.cm.Item({
michael@0 3209 label: "item",
michael@0 3210 context: loader.cm.PredicateContext(function (data) {
michael@0 3211 assert.strictEqual(data.targetName, "input");
michael@0 3212 return true;
michael@0 3213 })
michael@0 3214 })];
michael@0 3215
michael@0 3216 test.withTestDoc(function (window, doc) {
michael@0 3217 test.showMenu(doc.getElementById("button"), function (popup) {
michael@0 3218 test.checkMenu(items, [], []);
michael@0 3219 test.done();
michael@0 3220 });
michael@0 3221 });
michael@0 3222 };
michael@0 3223
michael@0 3224
michael@0 3225 // Test that the data object has the correct ID
michael@0 3226 exports.testPredicateContextTargetIDSet = function (assert, done) {
michael@0 3227 let test = new TestHelper(assert, done);
michael@0 3228 let loader = test.newLoader();
michael@0 3229
michael@0 3230 let items = [loader.cm.Item({
michael@0 3231 label: "item",
michael@0 3232 context: loader.cm.PredicateContext(function (data) {
michael@0 3233 assert.strictEqual(data.targetID, "button");
michael@0 3234 return true;
michael@0 3235 })
michael@0 3236 })];
michael@0 3237
michael@0 3238 test.withTestDoc(function (window, doc) {
michael@0 3239 test.showMenu(doc.getElementById("button"), function (popup) {
michael@0 3240 test.checkMenu(items, [], []);
michael@0 3241 test.done();
michael@0 3242 });
michael@0 3243 });
michael@0 3244 };
michael@0 3245
michael@0 3246 // Test that the data object has the correct ID
michael@0 3247 exports.testPredicateContextTargetIDNotSet = function (assert, done) {
michael@0 3248 let test = new TestHelper(assert, done);
michael@0 3249 let loader = test.newLoader();
michael@0 3250
michael@0 3251 let items = [loader.cm.Item({
michael@0 3252 label: "item",
michael@0 3253 context: loader.cm.PredicateContext(function (data) {
michael@0 3254 assert.strictEqual(data.targetID, null);
michael@0 3255 return true;
michael@0 3256 })
michael@0 3257 })];
michael@0 3258
michael@0 3259 test.withTestDoc(function (window, doc) {
michael@0 3260 test.showMenu(doc.getElementsByClassName("predicate-test-a")[0], function (popup) {
michael@0 3261 test.checkMenu(items, [], []);
michael@0 3262 test.done();
michael@0 3263 });
michael@0 3264 });
michael@0 3265 };
michael@0 3266
michael@0 3267 // Test that the data object is showing editable correctly for regular text inputs
michael@0 3268 exports.testPredicateContextTextBoxIsEditable = function (assert, done) {
michael@0 3269 let test = new TestHelper(assert, done);
michael@0 3270 let loader = test.newLoader();
michael@0 3271
michael@0 3272 let items = [loader.cm.Item({
michael@0 3273 label: "item",
michael@0 3274 context: loader.cm.PredicateContext(function (data) {
michael@0 3275 assert.strictEqual(data.isEditable, true);
michael@0 3276 return true;
michael@0 3277 })
michael@0 3278 })];
michael@0 3279
michael@0 3280 test.withTestDoc(function (window, doc) {
michael@0 3281 test.showMenu(doc.getElementById("textbox"), function (popup) {
michael@0 3282 test.checkMenu(items, [], []);
michael@0 3283 test.done();
michael@0 3284 });
michael@0 3285 });
michael@0 3286 };
michael@0 3287
michael@0 3288 // Test that the data object is showing editable correctly for readonly text inputs
michael@0 3289 exports.testPredicateContextReadonlyTextBoxIsNotEditable = function (assert, done) {
michael@0 3290 let test = new TestHelper(assert, done);
michael@0 3291 let loader = test.newLoader();
michael@0 3292
michael@0 3293 let items = [loader.cm.Item({
michael@0 3294 label: "item",
michael@0 3295 context: loader.cm.PredicateContext(function (data) {
michael@0 3296 assert.strictEqual(data.isEditable, false);
michael@0 3297 return true;
michael@0 3298 })
michael@0 3299 })];
michael@0 3300
michael@0 3301 test.withTestDoc(function (window, doc) {
michael@0 3302 test.showMenu(doc.getElementById("readonly-textbox"), function (popup) {
michael@0 3303 test.checkMenu(items, [], []);
michael@0 3304 test.done();
michael@0 3305 });
michael@0 3306 });
michael@0 3307 };
michael@0 3308
michael@0 3309 // Test that the data object is showing editable correctly for disabled text inputs
michael@0 3310 exports.testPredicateContextDisabledTextBoxIsNotEditable = function (assert, done) {
michael@0 3311 let test = new TestHelper(assert, done);
michael@0 3312 let loader = test.newLoader();
michael@0 3313
michael@0 3314 let items = [loader.cm.Item({
michael@0 3315 label: "item",
michael@0 3316 context: loader.cm.PredicateContext(function (data) {
michael@0 3317 assert.strictEqual(data.isEditable, false);
michael@0 3318 return true;
michael@0 3319 })
michael@0 3320 })];
michael@0 3321
michael@0 3322 test.withTestDoc(function (window, doc) {
michael@0 3323 test.showMenu(doc.getElementById("disabled-textbox"), function (popup) {
michael@0 3324 test.checkMenu(items, [], []);
michael@0 3325 test.done();
michael@0 3326 });
michael@0 3327 });
michael@0 3328 };
michael@0 3329
michael@0 3330 // Test that the data object is showing editable correctly for text areas
michael@0 3331 exports.testPredicateContextTextAreaIsEditable = function (assert, done) {
michael@0 3332 let test = new TestHelper(assert, done);
michael@0 3333 let loader = test.newLoader();
michael@0 3334
michael@0 3335 let items = [loader.cm.Item({
michael@0 3336 label: "item",
michael@0 3337 context: loader.cm.PredicateContext(function (data) {
michael@0 3338 assert.strictEqual(data.isEditable, true);
michael@0 3339 return true;
michael@0 3340 })
michael@0 3341 })];
michael@0 3342
michael@0 3343 test.withTestDoc(function (window, doc) {
michael@0 3344 test.showMenu(doc.getElementById("textfield"), function (popup) {
michael@0 3345 test.checkMenu(items, [], []);
michael@0 3346 test.done();
michael@0 3347 });
michael@0 3348 });
michael@0 3349 };
michael@0 3350
michael@0 3351 // Test that non-text inputs are not considered editable
michael@0 3352 exports.testPredicateContextButtonIsNotEditable = function (assert, done) {
michael@0 3353 let test = new TestHelper(assert, done);
michael@0 3354 let loader = test.newLoader();
michael@0 3355
michael@0 3356 let items = [loader.cm.Item({
michael@0 3357 label: "item",
michael@0 3358 context: loader.cm.PredicateContext(function (data) {
michael@0 3359 assert.strictEqual(data.isEditable, false);
michael@0 3360 return true;
michael@0 3361 })
michael@0 3362 })];
michael@0 3363
michael@0 3364 test.withTestDoc(function (window, doc) {
michael@0 3365 test.showMenu(doc.getElementById("button"), function (popup) {
michael@0 3366 test.checkMenu(items, [], []);
michael@0 3367 test.done();
michael@0 3368 });
michael@0 3369 });
michael@0 3370 };
michael@0 3371
michael@0 3372
michael@0 3373 // Test that the data object is showing editable correctly
michael@0 3374 exports.testPredicateContextNonInputIsNotEditable = function (assert, done) {
michael@0 3375 let test = new TestHelper(assert, done);
michael@0 3376 let loader = test.newLoader();
michael@0 3377
michael@0 3378 let items = [loader.cm.Item({
michael@0 3379 label: "item",
michael@0 3380 context: loader.cm.PredicateContext(function (data) {
michael@0 3381 assert.strictEqual(data.isEditable, false);
michael@0 3382 return true;
michael@0 3383 })
michael@0 3384 })];
michael@0 3385
michael@0 3386 test.withTestDoc(function (window, doc) {
michael@0 3387 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 3388 test.checkMenu(items, [], []);
michael@0 3389 test.done();
michael@0 3390 });
michael@0 3391 });
michael@0 3392 };
michael@0 3393
michael@0 3394
michael@0 3395 // Test that the data object is showing editable correctly for HTML contenteditable elements
michael@0 3396 exports.testPredicateContextEditableElement = function (assert, done) {
michael@0 3397 let test = new TestHelper(assert, done);
michael@0 3398 let loader = test.newLoader();
michael@0 3399
michael@0 3400 let items = [loader.cm.Item({
michael@0 3401 label: "item",
michael@0 3402 context: loader.cm.PredicateContext(function (data) {
michael@0 3403 assert.strictEqual(data.isEditable, true);
michael@0 3404 return true;
michael@0 3405 })
michael@0 3406 })];
michael@0 3407
michael@0 3408 test.withTestDoc(function (window, doc) {
michael@0 3409 test.showMenu(doc.getElementById("editable"), function (popup) {
michael@0 3410 test.checkMenu(items, [], []);
michael@0 3411 test.done();
michael@0 3412 });
michael@0 3413 });
michael@0 3414 };
michael@0 3415
michael@0 3416
michael@0 3417 // Test that the data object does not have a selection when there is none
michael@0 3418 exports.testPredicateContextNoSelectionInPage = function (assert, done) {
michael@0 3419 let test = new TestHelper(assert, done);
michael@0 3420 let loader = test.newLoader();
michael@0 3421
michael@0 3422 let items = [loader.cm.Item({
michael@0 3423 label: "item",
michael@0 3424 context: loader.cm.PredicateContext(function (data) {
michael@0 3425 assert.strictEqual(data.selectionText, null);
michael@0 3426 return true;
michael@0 3427 })
michael@0 3428 })];
michael@0 3429
michael@0 3430 test.withTestDoc(function (window, doc) {
michael@0 3431 test.showMenu(null, function (popup) {
michael@0 3432 test.checkMenu(items, [], []);
michael@0 3433 test.done();
michael@0 3434 });
michael@0 3435 });
michael@0 3436 };
michael@0 3437
michael@0 3438 // Test that the data object includes the selected page text
michael@0 3439 exports.testPredicateContextSelectionInPage = function (assert, done) {
michael@0 3440 let test = new TestHelper(assert, done);
michael@0 3441 let loader = test.newLoader();
michael@0 3442
michael@0 3443 let items = [loader.cm.Item({
michael@0 3444 label: "item",
michael@0 3445 context: loader.cm.PredicateContext(function (data) {
michael@0 3446 // since we might get whitespace
michael@0 3447 assert.ok(data.selectionText && data.selectionText.search(/^\s*Some text.\s*$/) != -1,
michael@0 3448 'Expected "Some text.", got "' + data.selectionText + '"');
michael@0 3449 return true;
michael@0 3450 })
michael@0 3451 })];
michael@0 3452
michael@0 3453 test.withTestDoc(function (window, doc) {
michael@0 3454 window.getSelection().selectAllChildren(doc.getElementById("text"));
michael@0 3455 test.showMenu(null, function (popup) {
michael@0 3456 test.checkMenu(items, [], []);
michael@0 3457 test.done();
michael@0 3458 });
michael@0 3459 });
michael@0 3460 };
michael@0 3461
michael@0 3462 // Test that the data object includes the selected input text
michael@0 3463 exports.testPredicateContextSelectionInTextBox = function (assert, done) {
michael@0 3464 let test = new TestHelper(assert, done);
michael@0 3465 let loader = test.newLoader();
michael@0 3466
michael@0 3467 let items = [loader.cm.Item({
michael@0 3468 label: "item",
michael@0 3469 context: loader.cm.PredicateContext(function (data) {
michael@0 3470 // since we might get whitespace
michael@0 3471 assert.strictEqual(data.selectionText, "t v");
michael@0 3472 return true;
michael@0 3473 })
michael@0 3474 })];
michael@0 3475
michael@0 3476 test.withTestDoc(function (window, doc) {
michael@0 3477 let textbox = doc.getElementById("textbox");
michael@0 3478 textbox.focus();
michael@0 3479 textbox.setSelectionRange(3, 6);
michael@0 3480 test.showMenu(textbox, function (popup) {
michael@0 3481 test.checkMenu(items, [], []);
michael@0 3482 test.done();
michael@0 3483 });
michael@0 3484 });
michael@0 3485 };
michael@0 3486
michael@0 3487 // Test that the data object has the correct src for an image
michael@0 3488 exports.testPredicateContextTargetSrcSet = function (assert, done) {
michael@0 3489 let test = new TestHelper(assert, done);
michael@0 3490 let loader = test.newLoader();
michael@0 3491 let image;
michael@0 3492
michael@0 3493 let items = [loader.cm.Item({
michael@0 3494 label: "item",
michael@0 3495 context: loader.cm.PredicateContext(function (data) {
michael@0 3496 assert.strictEqual(data.srcURL, image.src);
michael@0 3497 return true;
michael@0 3498 })
michael@0 3499 })];
michael@0 3500
michael@0 3501 test.withTestDoc(function (window, doc) {
michael@0 3502 image = doc.getElementById("image");
michael@0 3503 test.showMenu(image, function (popup) {
michael@0 3504 test.checkMenu(items, [], []);
michael@0 3505 test.done();
michael@0 3506 });
michael@0 3507 });
michael@0 3508 };
michael@0 3509
michael@0 3510 // Test that the data object has no src for a link
michael@0 3511 exports.testPredicateContextTargetSrcNotSet = function (assert, done) {
michael@0 3512 let test = new TestHelper(assert, done);
michael@0 3513 let loader = test.newLoader();
michael@0 3514
michael@0 3515 let items = [loader.cm.Item({
michael@0 3516 label: "item",
michael@0 3517 context: loader.cm.PredicateContext(function (data) {
michael@0 3518 assert.strictEqual(data.srcURL, null);
michael@0 3519 return true;
michael@0 3520 })
michael@0 3521 })];
michael@0 3522
michael@0 3523 test.withTestDoc(function (window, doc) {
michael@0 3524 test.showMenu(doc.getElementById("link"), function (popup) {
michael@0 3525 test.checkMenu(items, [], []);
michael@0 3526 test.done();
michael@0 3527 });
michael@0 3528 });
michael@0 3529 };
michael@0 3530
michael@0 3531
michael@0 3532 // Test that the data object has the correct link set
michael@0 3533 exports.testPredicateContextTargetLinkSet = function (assert, done) {
michael@0 3534 let test = new TestHelper(assert, done);
michael@0 3535 let loader = test.newLoader();
michael@0 3536 let image;
michael@0 3537
michael@0 3538 let items = [loader.cm.Item({
michael@0 3539 label: "item",
michael@0 3540 context: loader.cm.PredicateContext(function (data) {
michael@0 3541 assert.strictEqual(data.linkURL, TEST_DOC_URL + "#test");
michael@0 3542 return true;
michael@0 3543 })
michael@0 3544 })];
michael@0 3545
michael@0 3546 test.withTestDoc(function (window, doc) {
michael@0 3547 test.showMenu(doc.getElementsByClassName("predicate-test-a")[0], function (popup) {
michael@0 3548 test.checkMenu(items, [], []);
michael@0 3549 test.done();
michael@0 3550 });
michael@0 3551 });
michael@0 3552 };
michael@0 3553
michael@0 3554 // Test that the data object has no link for an image
michael@0 3555 exports.testPredicateContextTargetLinkNotSet = function (assert, done) {
michael@0 3556 let test = new TestHelper(assert, done);
michael@0 3557 let loader = test.newLoader();
michael@0 3558
michael@0 3559 let items = [loader.cm.Item({
michael@0 3560 label: "item",
michael@0 3561 context: loader.cm.PredicateContext(function (data) {
michael@0 3562 assert.strictEqual(data.linkURL, null);
michael@0 3563 return true;
michael@0 3564 })
michael@0 3565 })];
michael@0 3566
michael@0 3567 test.withTestDoc(function (window, doc) {
michael@0 3568 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 3569 test.checkMenu(items, [], []);
michael@0 3570 test.done();
michael@0 3571 });
michael@0 3572 });
michael@0 3573 };
michael@0 3574
michael@0 3575 // Test that the data object has the value for an input textbox
michael@0 3576 exports.testPredicateContextTargetValueSet = function (assert, done) {
michael@0 3577 let test = new TestHelper(assert, done);
michael@0 3578 let loader = test.newLoader();
michael@0 3579 let image;
michael@0 3580
michael@0 3581 let items = [loader.cm.Item({
michael@0 3582 label: "item",
michael@0 3583 context: loader.cm.PredicateContext(function (data) {
michael@0 3584 assert.strictEqual(data.value, "test value");
michael@0 3585 return true;
michael@0 3586 })
michael@0 3587 })];
michael@0 3588
michael@0 3589 test.withTestDoc(function (window, doc) {
michael@0 3590 test.showMenu(doc.getElementById("textbox"), function (popup) {
michael@0 3591 test.checkMenu(items, [], []);
michael@0 3592 test.done();
michael@0 3593 });
michael@0 3594 });
michael@0 3595 };
michael@0 3596
michael@0 3597 // Test that the data object has no value for an image
michael@0 3598 exports.testPredicateContextTargetValueNotSet = function (assert, done) {
michael@0 3599 let test = new TestHelper(assert, done);
michael@0 3600 let loader = test.newLoader();
michael@0 3601
michael@0 3602 let items = [loader.cm.Item({
michael@0 3603 label: "item",
michael@0 3604 context: loader.cm.PredicateContext(function (data) {
michael@0 3605 assert.strictEqual(data.value, null);
michael@0 3606 return true;
michael@0 3607 })
michael@0 3608 })];
michael@0 3609
michael@0 3610 test.withTestDoc(function (window, doc) {
michael@0 3611 test.showMenu(doc.getElementById("image"), function (popup) {
michael@0 3612 test.checkMenu(items, [], []);
michael@0 3613 test.done();
michael@0 3614 });
michael@0 3615 });
michael@0 3616 };
michael@0 3617
michael@0 3618
michael@0 3619 // NO TESTS BELOW THIS LINE! ///////////////////////////////////////////////////
michael@0 3620
michael@0 3621 // This makes it easier to run tests by handling things like opening the menu,
michael@0 3622 // opening new windows, making assertions, etc. Methods on |test| can be called
michael@0 3623 // on instances of this class. Don't forget to call done() to end the test!
michael@0 3624 // WARNING: This looks up items in popups by comparing labels, so don't give two
michael@0 3625 // items the same label.
michael@0 3626 function TestHelper(assert, done) {
michael@0 3627 this.assert = assert;
michael@0 3628 this.end = done;
michael@0 3629 this.loaders = [];
michael@0 3630 this.browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
michael@0 3631 getService(Ci.nsIWindowMediator).
michael@0 3632 getMostRecentWindow("navigator:browser");
michael@0 3633 this.overflowThreshValue = require("sdk/preferences/service").
michael@0 3634 get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT);
michael@0 3635 }
michael@0 3636
michael@0 3637 TestHelper.prototype = {
michael@0 3638 get contextMenuPopup() {
michael@0 3639 return this.browserWindow.document.getElementById("contentAreaContextMenu");
michael@0 3640 },
michael@0 3641
michael@0 3642 get contextMenuSeparator() {
michael@0 3643 return this.browserWindow.document.querySelector("." + SEPARATOR_CLASS);
michael@0 3644 },
michael@0 3645
michael@0 3646 get overflowPopup() {
michael@0 3647 return this.browserWindow.document.querySelector("." + OVERFLOW_POPUP_CLASS);
michael@0 3648 },
michael@0 3649
michael@0 3650 get overflowSubmenu() {
michael@0 3651 return this.browserWindow.document.querySelector("." + OVERFLOW_MENU_CLASS);
michael@0 3652 },
michael@0 3653
michael@0 3654 get tabBrowser() {
michael@0 3655 return this.browserWindow.gBrowser;
michael@0 3656 },
michael@0 3657
michael@0 3658 // Methods on the wrapped test can be called on this object.
michael@0 3659 __noSuchMethod__: function (methodName, args) {
michael@0 3660 this.assert[methodName].apply(this.assert, args);
michael@0 3661 },
michael@0 3662
michael@0 3663 // Asserts that elt, a DOM element representing item, looks OK.
michael@0 3664 checkItemElt: function (elt, item) {
michael@0 3665 let itemType = this.getItemType(item);
michael@0 3666
michael@0 3667 switch (itemType) {
michael@0 3668 case "Item":
michael@0 3669 this.assert.equal(elt.localName, "menuitem",
michael@0 3670 "Item DOM element should be a xul:menuitem");
michael@0 3671 if (typeof(item.data) === "string") {
michael@0 3672 this.assert.equal(elt.getAttribute("value"), item.data,
michael@0 3673 "Item should have correct data");
michael@0 3674 }
michael@0 3675 break
michael@0 3676 case "Menu":
michael@0 3677 this.assert.equal(elt.localName, "menu",
michael@0 3678 "Menu DOM element should be a xul:menu");
michael@0 3679 let subPopup = elt.firstChild;
michael@0 3680 this.assert.ok(subPopup, "xul:menu should have a child");
michael@0 3681 this.assert.equal(subPopup.localName, "menupopup",
michael@0 3682 "xul:menu's first child should be a menupopup");
michael@0 3683 break;
michael@0 3684 case "Separator":
michael@0 3685 this.assert.equal(elt.localName, "menuseparator",
michael@0 3686 "Separator DOM element should be a xul:menuseparator");
michael@0 3687 break;
michael@0 3688 }
michael@0 3689
michael@0 3690 if (itemType === "Item" || itemType === "Menu") {
michael@0 3691 this.assert.equal(elt.getAttribute("label"), item.label,
michael@0 3692 "Item should have correct title");
michael@0 3693 if (typeof(item.image) === "string") {
michael@0 3694 this.assert.equal(elt.getAttribute("image"), item.image,
michael@0 3695 "Item should have correct image");
michael@0 3696 if (itemType === "Menu")
michael@0 3697 this.assert.ok(elt.classList.contains("menu-iconic"),
michael@0 3698 "Menus with images should have the correct class")
michael@0 3699 else
michael@0 3700 this.assert.ok(elt.classList.contains("menuitem-iconic"),
michael@0 3701 "Items with images should have the correct class")
michael@0 3702 }
michael@0 3703 else {
michael@0 3704 this.assert.ok(!elt.getAttribute("image"),
michael@0 3705 "Item should not have image");
michael@0 3706 this.assert.ok(!elt.classList.contains("menu-iconic") && !elt.classList.contains("menuitem-iconic"),
michael@0 3707 "The iconic classes should not be present")
michael@0 3708 }
michael@0 3709 }
michael@0 3710 },
michael@0 3711
michael@0 3712 // Asserts that the context menu looks OK given the arguments. presentItems
michael@0 3713 // are items that have been added to the menu. absentItems are items that
michael@0 3714 // shouldn't match the current context. removedItems are items that have been
michael@0 3715 // removed from the menu.
michael@0 3716 checkMenu: function (presentItems, absentItems, removedItems) {
michael@0 3717 // Count up how many top-level items there are
michael@0 3718 let total = 0;
michael@0 3719 for (let item of presentItems) {
michael@0 3720 if (absentItems.indexOf(item) < 0 && removedItems.indexOf(item) < 0)
michael@0 3721 total++;
michael@0 3722 }
michael@0 3723
michael@0 3724 let separator = this.contextMenuSeparator;
michael@0 3725 if (total == 0) {
michael@0 3726 this.assert.ok(!separator || separator.hidden,
michael@0 3727 "separator should not be present");
michael@0 3728 }
michael@0 3729 else {
michael@0 3730 this.assert.ok(separator && !separator.hidden,
michael@0 3731 "separator should be present");
michael@0 3732 }
michael@0 3733
michael@0 3734 let mainNodes = this.browserWindow.document.querySelectorAll("#contentAreaContextMenu > ." + ITEM_CLASS);
michael@0 3735 let overflowNodes = this.browserWindow.document.querySelectorAll("." + OVERFLOW_POPUP_CLASS + " > ." + ITEM_CLASS);
michael@0 3736
michael@0 3737 this.assert.ok(mainNodes.length == 0 || overflowNodes.length == 0,
michael@0 3738 "Should only see nodes at the top level or in overflow");
michael@0 3739
michael@0 3740 let overflow = this.overflowSubmenu;
michael@0 3741 if (this.shouldOverflow(total)) {
michael@0 3742 this.assert.ok(overflow && !overflow.hidden,
michael@0 3743 "overflow menu should be present");
michael@0 3744 this.assert.equal(mainNodes.length, 0,
michael@0 3745 "should be no items in the main context menu");
michael@0 3746 }
michael@0 3747 else {
michael@0 3748 this.assert.ok(!overflow || overflow.hidden,
michael@0 3749 "overflow menu should not be present");
michael@0 3750 // When visible nodes == 0 they could be in overflow or top level
michael@0 3751 if (total > 0) {
michael@0 3752 this.assert.equal(overflowNodes.length, 0,
michael@0 3753 "should be no items in the overflow context menu");
michael@0 3754 }
michael@0 3755 }
michael@0 3756
michael@0 3757 // Iterate over wherever the nodes have ended up
michael@0 3758 let nodes = mainNodes.length ? mainNodes : overflowNodes;
michael@0 3759 this.checkNodes(nodes, presentItems, absentItems, removedItems)
michael@0 3760 let pos = 0;
michael@0 3761 },
michael@0 3762
michael@0 3763 // Recurses through the item hierarchy of presentItems comparing it to the
michael@0 3764 // node hierarchy of nodes. Any items in removedItems will be skipped (so
michael@0 3765 // should not exist in the XUL), any items in absentItems must exist and be
michael@0 3766 // hidden
michael@0 3767 checkNodes: function (nodes, presentItems, absentItems, removedItems) {
michael@0 3768 let pos = 0;
michael@0 3769 for (let item of presentItems) {
michael@0 3770 // Removed items shouldn't be in the list
michael@0 3771 if (removedItems.indexOf(item) >= 0)
michael@0 3772 continue;
michael@0 3773
michael@0 3774 if (nodes.length <= pos) {
michael@0 3775 this.assert.ok(false, "Not enough nodes");
michael@0 3776 return;
michael@0 3777 }
michael@0 3778
michael@0 3779 let hidden = absentItems.indexOf(item) >= 0;
michael@0 3780
michael@0 3781 this.checkItemElt(nodes[pos], item);
michael@0 3782 this.assert.equal(nodes[pos].hidden, hidden,
michael@0 3783 "hidden should be set correctly");
michael@0 3784
michael@0 3785 // The contents of hidden menus doesn't matter so much
michael@0 3786 if (!hidden && this.getItemType(item) == "Menu") {
michael@0 3787 this.assert.equal(nodes[pos].firstChild.localName, "menupopup",
michael@0 3788 "menu XUL should contain a menupopup");
michael@0 3789 this.checkNodes(nodes[pos].firstChild.childNodes, item.items, absentItems, removedItems);
michael@0 3790 }
michael@0 3791
michael@0 3792 if (pos > 0)
michael@0 3793 this.assert.equal(nodes[pos].previousSibling, nodes[pos - 1],
michael@0 3794 "nodes should all be in the same group");
michael@0 3795 pos++;
michael@0 3796 }
michael@0 3797
michael@0 3798 this.assert.equal(nodes.length, pos,
michael@0 3799 "should have checked all the XUL nodes");
michael@0 3800 },
michael@0 3801
michael@0 3802 // Attaches an event listener to node. The listener is automatically removed
michael@0 3803 // when it's fired (so it's assumed it will fire), and callback is called
michael@0 3804 // after a short delay. Since the module we're testing relies on the same
michael@0 3805 // event listeners to do its work, this is to give them a little breathing
michael@0 3806 // room before callback runs. Inside callback |this| is this object.
michael@0 3807 // Optionally you can pass a function to test if the event is the event you
michael@0 3808 // want.
michael@0 3809 delayedEventListener: function (node, event, callback, useCapture, isValid) {
michael@0 3810 const self = this;
michael@0 3811 node.addEventListener(event, function handler(evt) {
michael@0 3812 if (isValid && !isValid(evt))
michael@0 3813 return;
michael@0 3814 node.removeEventListener(event, handler, useCapture);
michael@0 3815 timer.setTimeout(function () {
michael@0 3816 try {
michael@0 3817 callback.call(self, evt);
michael@0 3818 }
michael@0 3819 catch (err) {
michael@0 3820 self.assert.fail(err);
michael@0 3821 self.end();
michael@0 3822 }
michael@0 3823 }, 20);
michael@0 3824 }, useCapture);
michael@0 3825 },
michael@0 3826
michael@0 3827 // Call to finish the test.
michael@0 3828 done: function () {
michael@0 3829 const self = this;
michael@0 3830 function commonDone() {
michael@0 3831 this.closeTab();
michael@0 3832
michael@0 3833 while (this.loaders.length) {
michael@0 3834 this.loaders[0].unload();
michael@0 3835 }
michael@0 3836
michael@0 3837 require("sdk/preferences/service").set(OVERFLOW_THRESH_PREF, self.overflowThreshValue);
michael@0 3838
michael@0 3839 this.end();
michael@0 3840 }
michael@0 3841
michael@0 3842 function closeBrowserWindow() {
michael@0 3843 if (this.oldBrowserWindow) {
michael@0 3844 this.delayedEventListener(this.browserWindow, "unload", commonDone,
michael@0 3845 false);
michael@0 3846 this.browserWindow.close();
michael@0 3847 this.browserWindow = this.oldBrowserWindow;
michael@0 3848 delete this.oldBrowserWindow;
michael@0 3849 }
michael@0 3850 else {
michael@0 3851 commonDone.call(this);
michael@0 3852 }
michael@0 3853 };
michael@0 3854
michael@0 3855 if (this.contextMenuPopup.state == "closed") {
michael@0 3856 closeBrowserWindow.call(this);
michael@0 3857 }
michael@0 3858 else {
michael@0 3859 this.delayedEventListener(this.contextMenuPopup, "popuphidden",
michael@0 3860 function () closeBrowserWindow.call(this),
michael@0 3861 false);
michael@0 3862 this.contextMenuPopup.hidePopup();
michael@0 3863 }
michael@0 3864 },
michael@0 3865
michael@0 3866 closeTab: function() {
michael@0 3867 if (this.tab) {
michael@0 3868 this.tabBrowser.removeTab(this.tab);
michael@0 3869 this.tabBrowser.selectedTab = this.oldSelectedTab;
michael@0 3870 this.tab = null;
michael@0 3871 }
michael@0 3872 },
michael@0 3873
michael@0 3874 // Returns the DOM element in popup corresponding to item.
michael@0 3875 // WARNING: The element is found by comparing labels, so don't give two items
michael@0 3876 // the same label.
michael@0 3877 getItemElt: function (popup, item) {
michael@0 3878 let nodes = popup.childNodes;
michael@0 3879 for (let i = nodes.length - 1; i >= 0; i--) {
michael@0 3880 if (this.getItemType(item) === "Separator") {
michael@0 3881 if (nodes[i].localName === "menuseparator")
michael@0 3882 return nodes[i];
michael@0 3883 }
michael@0 3884 else if (nodes[i].getAttribute("label") === item.label)
michael@0 3885 return nodes[i];
michael@0 3886 }
michael@0 3887 return null;
michael@0 3888 },
michael@0 3889
michael@0 3890 // Returns "Item", "Menu", or "Separator".
michael@0 3891 getItemType: function (item) {
michael@0 3892 // Could use instanceof here, but that would require accessing the loader
michael@0 3893 // that created the item, and I don't want to A) somehow search through the
michael@0 3894 // this.loaders list to find it, and B) assume there are any live loaders at
michael@0 3895 // all.
michael@0 3896 return /^\[object (Item|Menu|Separator)/.exec(item.toString())[1];
michael@0 3897 },
michael@0 3898
michael@0 3899 // Returns a wrapper around a new loader: { loader, cm, unload, globalScope }.
michael@0 3900 // loader is a Cuddlefish sandboxed loader, cm is the context menu module,
michael@0 3901 // globalScope is the context menu module's global scope, and unload is a
michael@0 3902 // function that unloads the loader and associated resources.
michael@0 3903 newLoader: function () {
michael@0 3904 const self = this;
michael@0 3905 let loader = Loader(module);
michael@0 3906 let wrapper = {
michael@0 3907 loader: loader,
michael@0 3908 cm: loader.require("sdk/context-menu"),
michael@0 3909 globalScope: loader.sandbox("sdk/context-menu"),
michael@0 3910 unload: function () {
michael@0 3911 loader.unload();
michael@0 3912 let idx = self.loaders.indexOf(wrapper);
michael@0 3913 if (idx < 0)
michael@0 3914 throw new Error("Test error: tried to unload nonexistent loader");
michael@0 3915 self.loaders.splice(idx, 1);
michael@0 3916 }
michael@0 3917 };
michael@0 3918 this.loaders.push(wrapper);
michael@0 3919 return wrapper;
michael@0 3920 },
michael@0 3921
michael@0 3922 // As above but the loader has private-browsing support enabled.
michael@0 3923 newPrivateLoader: function() {
michael@0 3924 let base = require("@loader/options");
michael@0 3925
michael@0 3926 // Clone current loader's options adding the private-browsing permission
michael@0 3927 let options = merge({}, base, {
michael@0 3928 metadata: merge({}, base.metadata || {}, {
michael@0 3929 permissions: merge({}, base.metadata.permissions || {}, {
michael@0 3930 'private-browsing': true
michael@0 3931 })
michael@0 3932 })
michael@0 3933 });
michael@0 3934
michael@0 3935 const self = this;
michael@0 3936 let loader = Loader(module, null, options);
michael@0 3937 let wrapper = {
michael@0 3938 loader: loader,
michael@0 3939 cm: loader.require("sdk/context-menu"),
michael@0 3940 globalScope: loader.sandbox("sdk/context-menu"),
michael@0 3941 unload: function () {
michael@0 3942 loader.unload();
michael@0 3943 let idx = self.loaders.indexOf(wrapper);
michael@0 3944 if (idx < 0)
michael@0 3945 throw new Error("Test error: tried to unload nonexistent loader");
michael@0 3946 self.loaders.splice(idx, 1);
michael@0 3947 }
michael@0 3948 };
michael@0 3949 this.loaders.push(wrapper);
michael@0 3950 return wrapper;
michael@0 3951 },
michael@0 3952
michael@0 3953 // Returns true if the count crosses the overflow threshold.
michael@0 3954 shouldOverflow: function (count) {
michael@0 3955 return count >
michael@0 3956 (this.loaders.length ?
michael@0 3957 this.loaders[0].loader.require("sdk/preferences/service").
michael@0 3958 get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT) :
michael@0 3959 OVERFLOW_THRESH_DEFAULT);
michael@0 3960 },
michael@0 3961
michael@0 3962 // Opens the context menu on the current page. If targetNode is null, the
michael@0 3963 // menu is opened in the top-left corner. onShowncallback is passed the
michael@0 3964 // popup.
michael@0 3965 showMenu: function(targetNode, onshownCallback) {
michael@0 3966 function sendEvent() {
michael@0 3967 this.delayedEventListener(this.browserWindow, "popupshowing",
michael@0 3968 function (e) {
michael@0 3969 let popup = e.target;
michael@0 3970 onshownCallback.call(this, popup);
michael@0 3971 }, false);
michael@0 3972
michael@0 3973 let rect = targetNode ?
michael@0 3974 targetNode.getBoundingClientRect() :
michael@0 3975 { left: 0, top: 0, width: 0, height: 0 };
michael@0 3976 let contentWin = targetNode ? targetNode.ownerDocument.defaultView
michael@0 3977 : this.browserWindow.content;
michael@0 3978 contentWin.
michael@0 3979 QueryInterface(Ci.nsIInterfaceRequestor).
michael@0 3980 getInterface(Ci.nsIDOMWindowUtils).
michael@0 3981 sendMouseEvent("contextmenu",
michael@0 3982 rect.left + (rect.width / 2),
michael@0 3983 rect.top + (rect.height / 2),
michael@0 3984 2, 1, 0);
michael@0 3985 }
michael@0 3986
michael@0 3987 // If a new tab or window has not yet been opened, open a new tab now. For
michael@0 3988 // some reason using the tab already opened when the test starts causes
michael@0 3989 // leaks. See bug 566351 for details.
michael@0 3990 if (!targetNode && !this.oldSelectedTab && !this.oldBrowserWindow) {
michael@0 3991 this.oldSelectedTab = this.tabBrowser.selectedTab;
michael@0 3992 this.tab = this.tabBrowser.addTab("about:blank");
michael@0 3993 let browser = this.tabBrowser.getBrowserForTab(this.tab);
michael@0 3994
michael@0 3995 this.delayedEventListener(browser, "load", function () {
michael@0 3996 this.tabBrowser.selectedTab = this.tab;
michael@0 3997 sendEvent.call(this);
michael@0 3998 }, true);
michael@0 3999 }
michael@0 4000 else
michael@0 4001 sendEvent.call(this);
michael@0 4002 },
michael@0 4003
michael@0 4004 hideMenu: function(onhiddenCallback) {
michael@0 4005 this.delayedEventListener(this.browserWindow, "popuphidden", onhiddenCallback);
michael@0 4006
michael@0 4007 this.contextMenuPopup.hidePopup();
michael@0 4008 },
michael@0 4009
michael@0 4010 // Opens a new browser window. The window will be closed automatically when
michael@0 4011 // done() is called.
michael@0 4012 withNewWindow: function (onloadCallback) {
michael@0 4013 let win = this.browserWindow.OpenBrowserWindow();
michael@0 4014 this.delayedEventListener(win, "load", onloadCallback, true);
michael@0 4015 this.oldBrowserWindow = this.browserWindow;
michael@0 4016 this.browserWindow = win;
michael@0 4017 },
michael@0 4018
michael@0 4019 // Opens a new private browser window. The window will be closed
michael@0 4020 // automatically when done() is called.
michael@0 4021 withNewPrivateWindow: function (onloadCallback) {
michael@0 4022 let win = this.browserWindow.OpenBrowserWindow({private: true});
michael@0 4023 this.delayedEventListener(win, "load", onloadCallback, true);
michael@0 4024 this.oldBrowserWindow = this.browserWindow;
michael@0 4025 this.browserWindow = win;
michael@0 4026 },
michael@0 4027
michael@0 4028 // Opens a new tab with our test page in the current window. The tab will
michael@0 4029 // be closed automatically when done() is called.
michael@0 4030 withTestDoc: function (onloadCallback) {
michael@0 4031 this.oldSelectedTab = this.tabBrowser.selectedTab;
michael@0 4032 this.tab = this.tabBrowser.addTab(TEST_DOC_URL);
michael@0 4033 let browser = this.tabBrowser.getBrowserForTab(this.tab);
michael@0 4034
michael@0 4035 this.delayedEventListener(browser, "load", function () {
michael@0 4036 this.tabBrowser.selectedTab = this.tab;
michael@0 4037 onloadCallback.call(this, browser.contentWindow, browser.contentDocument);
michael@0 4038 }, true, function(evt) {
michael@0 4039 return evt.target.location == TEST_DOC_URL;
michael@0 4040 });
michael@0 4041 }
michael@0 4042 };
michael@0 4043
michael@0 4044 require('sdk/test').run(exports);

mercurial