зеркало из https://github.com/mozilla/gecko-dev.git
Bug 841418: Uplift the stabilization branch of add-on sdk to Firefox.
This commit is contained in:
Родитель
ab470235f5
Коммит
fc29877825
|
@ -38,8 +38,8 @@ Bugs
|
|||
|
||||
* file a bug: https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK
|
||||
|
||||
|
||||
Style Guidelines
|
||||
--------------------
|
||||
|
||||
* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
|
||||
* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>18.0</em:minVersion>
|
||||
<em:maxVersion>21.0a1</em:maxVersion>
|
||||
<em:maxVersion>20.*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
|
@ -14,7 +13,7 @@ const { ns } = require("./core/namespace");
|
|||
const { validateOptions, getTypeOf } = require("./deprecated/api-utils");
|
||||
const { URL } = require("./url");
|
||||
const { WindowTracker, browserWindowIterator } = require("./deprecated/window-utils");
|
||||
const { isBrowser } = require("./window/utils");
|
||||
const { isBrowser, getInnerId } = require("./window/utils");
|
||||
const { Ci } = require("chrome");
|
||||
const { MatchPattern } = require("./page-mod/match-pattern");
|
||||
const { Worker } = require("./content/worker");
|
||||
|
@ -325,11 +324,11 @@ function hasMatchingContext(contexts, popupNode) {
|
|||
}
|
||||
|
||||
// Gets the matched context from any worker for this item. If there is no worker
|
||||
// or no matched context then returns null.
|
||||
// or no matched context then returns false.
|
||||
function getCurrentWorkerContext(item, popupNode) {
|
||||
let worker = getItemWorkerForWindow(item, popupNode.ownerDocument.defaultView);
|
||||
if (!worker)
|
||||
return null;
|
||||
if (!worker || !worker.anyContextListeners())
|
||||
return true;
|
||||
return worker.getMatchedContext(popupNode);
|
||||
}
|
||||
|
||||
|
@ -346,18 +345,10 @@ function isItemVisible(item, popupNode, defaultVisibility) {
|
|||
return false;
|
||||
|
||||
let context = getCurrentWorkerContext(item, popupNode);
|
||||
if (typeof(context) === "string")
|
||||
if (typeof(context) === "string" && context != "")
|
||||
item.label = context;
|
||||
|
||||
return context !== false;
|
||||
}
|
||||
|
||||
// Destroys any item's content scripts workers associated with the given window
|
||||
function destroyItemWorkerForWindow(item, window) {
|
||||
let worker = internal(item).workerMap.get(window);
|
||||
if (worker)
|
||||
worker.destroy();
|
||||
internal(item).workerMap.delete(window);
|
||||
return !!context;
|
||||
}
|
||||
|
||||
// Gets the item's content script worker for a window, creating one if necessary
|
||||
|
@ -367,7 +358,8 @@ function getItemWorkerForWindow(item, window) {
|
|||
if (!item.contentScript && !item.contentScriptFile)
|
||||
return null;
|
||||
|
||||
let worker = internal(item).workerMap.get(window);
|
||||
let id = getInnerId(window);
|
||||
let worker = internal(item).workerMap.get(id);
|
||||
|
||||
if (worker)
|
||||
return worker;
|
||||
|
@ -380,11 +372,11 @@ function getItemWorkerForWindow(item, window) {
|
|||
emit(item, "message", msg);
|
||||
},
|
||||
onDetach: function() {
|
||||
destroyItemWorkerForWindow(item, window);
|
||||
internal(item).workerMap.delete(id);
|
||||
}
|
||||
});
|
||||
|
||||
internal(item).workerMap.set(window, worker);
|
||||
internal(item).workerMap.set(id, worker);
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
@ -463,8 +455,8 @@ let LabelledItem = Class({
|
|||
},
|
||||
|
||||
destroy: function destroy() {
|
||||
for (let [window] of internal(this).workerMap)
|
||||
destroyItemWorkerForWindow(this, window);
|
||||
for (let [,worker] of internal(this).workerMap)
|
||||
worker.destroy();
|
||||
|
||||
BaseItem.prototype.destroy.call(this);
|
||||
},
|
||||
|
@ -645,6 +637,12 @@ when(function() {
|
|||
// App specific UI code lives here, it should handle populating the context
|
||||
// menu and passing clicks etc. through to the items.
|
||||
|
||||
function countVisibleItems(nodes) {
|
||||
return Array.reduce(nodes, function(sum, node) {
|
||||
return node.hidden ? sum : sum + 1;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
let MenuWrapper = Class({
|
||||
initialize: function initialize(winWrapper, items, contextMenu) {
|
||||
this.winWrapper = winWrapper;
|
||||
|
@ -654,11 +652,17 @@ let MenuWrapper = Class({
|
|||
this.populated = false;
|
||||
this.menuMap = new Map();
|
||||
|
||||
this.contextMenu.addEventListener("popupshowing", this, false);
|
||||
// updateItemVisibilities will run first, updateOverflowState will run after
|
||||
// all other instances of this module have run updateItemVisibilities
|
||||
this._updateItemVisibilities = this.updateItemVisibilities.bind(this);
|
||||
this.contextMenu.addEventListener("popupshowing", this._updateItemVisibilities, true);
|
||||
this._updateOverflowState = this.updateOverflowState.bind(this);
|
||||
this.contextMenu.addEventListener("popupshowing", this._updateOverflowState, false);
|
||||
},
|
||||
|
||||
destroy: function destroy() {
|
||||
this.contextMenu.removeEventListener("popupshowing", this, false);
|
||||
this.contextMenu.removeEventListener("popupshowing", this._updateOverflowState, false);
|
||||
this.contextMenu.removeEventListener("popupshowing", this._updateItemVisibilities, true);
|
||||
|
||||
if (!this.populated)
|
||||
return;
|
||||
|
@ -693,7 +697,7 @@ let MenuWrapper = Class({
|
|||
},
|
||||
|
||||
get overflowItems() {
|
||||
return this.contextMenu.querySelectorAll("." + OVERFLOW_ITEM_CLASS + " > ." + ITEM_CLASS);
|
||||
return this.contextMenu.querySelectorAll("." + OVERFLOW_ITEM_CLASS);
|
||||
},
|
||||
|
||||
getXULNodeForItem: function getXULNodeForItem(item) {
|
||||
|
@ -741,31 +745,11 @@ let MenuWrapper = Class({
|
|||
|
||||
let menu = item.parentMenu;
|
||||
if (menu === this.items) {
|
||||
// Insert into the overflow popup if it exists, otherwise the normal
|
||||
// context menu
|
||||
menupopup = this.overflowPopup;
|
||||
|
||||
// If there isn't already an overflow menu then check if we need to
|
||||
// create one, otherwise use the main context menu
|
||||
if (!menupopup) {
|
||||
if (!menupopup)
|
||||
menupopup = this.contextMenu;
|
||||
let toplevel = this.topLevelItems;
|
||||
|
||||
if (toplevel.length >= MenuManager.overflowThreshold) {
|
||||
// Create the overflow menu and move everything there
|
||||
let overflowMenu = this.window.document.createElement("menu");
|
||||
overflowMenu.setAttribute("class", OVERFLOW_MENU_CLASS);
|
||||
overflowMenu.setAttribute("label", OVERFLOW_MENU_LABEL);
|
||||
this.contextMenu.insertBefore(overflowMenu, this.separator.nextSibling);
|
||||
|
||||
menupopup = this.window.document.createElement("menupopup");
|
||||
menupopup.setAttribute("class", OVERFLOW_POPUP_CLASS);
|
||||
overflowMenu.appendChild(menupopup);
|
||||
|
||||
for (let xulNode of toplevel) {
|
||||
menupopup.appendChild(xulNode);
|
||||
this.updateXULClass(xulNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
let xulNode = this.getXULNodeForItem(menu);
|
||||
|
@ -839,7 +823,7 @@ let MenuWrapper = Class({
|
|||
xulNode.setAttribute("value", item.data);
|
||||
|
||||
let self = this;
|
||||
xulNode.addEventListener("click", function(event) {
|
||||
xulNode.addEventListener("command", function(event) {
|
||||
// Only care about clicks directly on this item
|
||||
if (event.target !== xulNode)
|
||||
return;
|
||||
|
@ -932,30 +916,18 @@ let MenuWrapper = Class({
|
|||
}
|
||||
}
|
||||
else if (parent == this.overflowPopup) {
|
||||
// If there are no more items then remove the overflow menu and separator
|
||||
if (parent.childNodes.length == 0) {
|
||||
// It's possible that this add-on had all the items in the overflow
|
||||
// menu and they're now all gone, so remove the separator and overflow
|
||||
// menu directly
|
||||
|
||||
let separator = this.separator;
|
||||
separator.parentNode.removeChild(separator);
|
||||
this.contextMenu.removeChild(parent.parentNode);
|
||||
}
|
||||
else if (parent.childNodes.length <= MenuManager.overflowThreshold) {
|
||||
// Otherwise if the overflow menu is empty enough move everything in
|
||||
// the overflow menu back to top level and remove the overflow menu
|
||||
|
||||
while (parent.firstChild) {
|
||||
let node = parent.firstChild;
|
||||
this.contextMenu.insertBefore(node, parent.parentNode);
|
||||
this.updateXULClass(node);
|
||||
}
|
||||
this.contextMenu.removeChild(parent.parentNode);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(event) {
|
||||
// Recurses through all the items owned by this module and sets their hidden
|
||||
// state
|
||||
updateItemVisibilities: function updateItemVisibilities(event) {
|
||||
try {
|
||||
if (event.type != "popupshowing")
|
||||
return;
|
||||
|
@ -970,28 +942,77 @@ let MenuWrapper = Class({
|
|||
this.populate(this.items);
|
||||
}
|
||||
|
||||
let separator = this.separator;
|
||||
let popup = this.overflowMenu;
|
||||
|
||||
let popupNode = event.target.triggerNode;
|
||||
if (this.setVisibility(this.items, popupNode, PageContext().isCurrent(popupNode))) {
|
||||
// Some of this instance's items are visible so make sure the separator
|
||||
// and if necessary the overflow popup are visible
|
||||
separator.hidden = false;
|
||||
if (popup)
|
||||
popup.hidden = false;
|
||||
this.setVisibility(this.items, popupNode, PageContext().isCurrent(popupNode));
|
||||
}
|
||||
catch (e) {
|
||||
console.exception(e);
|
||||
}
|
||||
},
|
||||
|
||||
// Counts the number of visible items across all modules and makes sure they
|
||||
// are in the right place between the top level context menu and the overflow
|
||||
// menu
|
||||
updateOverflowState: function updateOverflowState(event) {
|
||||
try {
|
||||
if (event.type != "popupshowing")
|
||||
return;
|
||||
if (event.target != this.contextMenu)
|
||||
return;
|
||||
|
||||
// The main items will be in either the top level context menu or the
|
||||
// overflow menu at this point. Count the visible ones and if they are in
|
||||
// the wrong place move them
|
||||
let toplevel = this.topLevelItems;
|
||||
let overflow = this.overflowItems;
|
||||
let visibleCount = countVisibleItems(toplevel) +
|
||||
countVisibleItems(overflow);
|
||||
|
||||
if (visibleCount == 0) {
|
||||
let separator = this.separator;
|
||||
if (separator)
|
||||
separator.hidden = true;
|
||||
let overflowMenu = this.overflowMenu;
|
||||
if (overflowMenu)
|
||||
overflowMenu.hidden = true;
|
||||
}
|
||||
else if (visibleCount > MenuManager.overflowThreshold) {
|
||||
this.separator.hidden = false;
|
||||
let overflowPopup = this.overflowPopup;
|
||||
if (overflowPopup)
|
||||
overflowPopup.parentNode.hidden = false;
|
||||
|
||||
if (toplevel.length > 0) {
|
||||
// The overflow menu shouldn't exist here but let's play it safe
|
||||
if (!overflowPopup) {
|
||||
let overflowMenu = this.window.document.createElement("menu");
|
||||
overflowMenu.setAttribute("class", OVERFLOW_MENU_CLASS);
|
||||
overflowMenu.setAttribute("label", OVERFLOW_MENU_LABEL);
|
||||
this.contextMenu.insertBefore(overflowMenu, this.separator.nextSibling);
|
||||
|
||||
overflowPopup = this.window.document.createElement("menupopup");
|
||||
overflowPopup.setAttribute("class", OVERFLOW_POPUP_CLASS);
|
||||
overflowMenu.appendChild(overflowPopup);
|
||||
}
|
||||
|
||||
for (let xulNode of toplevel) {
|
||||
overflowPopup.appendChild(xulNode);
|
||||
this.updateXULClass(xulNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We need to test whether any other instance has visible items.
|
||||
// Get all the highest level items and see if any are visible.
|
||||
let anyVisible = (Array.some(this.topLevelItems, function(item) !item.hidden)) ||
|
||||
(Array.some(this.overflowItems, function(item) !item.hidden));
|
||||
|
||||
// If any were visible make sure the separator and if necessary the
|
||||
// overflow popup are visible, otherwise hide them
|
||||
separator.hidden = !anyVisible;
|
||||
if (popup)
|
||||
popup.hidden = !anyVisible;
|
||||
this.separator.hidden = false;
|
||||
|
||||
if (overflow.length > 0) {
|
||||
// Move all the overflow nodes out of the overflow menu and position
|
||||
// them immediately before it
|
||||
for (let xulNode of overflow) {
|
||||
this.contextMenu.insertBefore(xulNode, xulNode.parentNode.parentNode);
|
||||
this.updateXULClass(xulNode);
|
||||
}
|
||||
this.contextMenu.removeChild(this.overflowMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
|
|
|
@ -57,7 +57,11 @@ const TabTrait = Trait.compose(EventEmitter, {
|
|||
},
|
||||
destroy: function destroy() {
|
||||
this._removeAllListeners();
|
||||
this._browser.removeEventListener(EVENTS.ready.dom, this._onReady, true);
|
||||
if (this._tab) {
|
||||
this._browser.removeEventListener(EVENTS.ready.dom, this._onReady, true);
|
||||
this._tab = null;
|
||||
TABS.splice(TABS.indexOf(this), 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -98,35 +102,35 @@ const TabTrait = Trait.compose(EventEmitter, {
|
|||
/**
|
||||
* Unique id for the tab, actually maps to tab.linkedPanel but with some munging.
|
||||
*/
|
||||
get id() getTabId(this._tab),
|
||||
get id() this._tab ? getTabId(this._tab) : undefined,
|
||||
|
||||
/**
|
||||
* The title of the page currently loaded in the tab.
|
||||
* Changing this property changes an actual title.
|
||||
* @type {String}
|
||||
*/
|
||||
get title() getTabTitle(this._tab),
|
||||
set title(title) setTabTitle(this._tab, title),
|
||||
get title() this._tab ? getTabTitle(this._tab) : undefined,
|
||||
set title(title) this._tab && setTabTitle(this._tab, title),
|
||||
|
||||
/**
|
||||
* Returns the MIME type that the document loaded in the tab is being
|
||||
* rendered as.
|
||||
* @type {String}
|
||||
*/
|
||||
get contentType() getTabContentType(this._tab),
|
||||
get contentType() this._tab ? getTabContentType(this._tab) : undefined,
|
||||
|
||||
/**
|
||||
* Location of the page currently loaded in this tab.
|
||||
* Changing this property will loads page under under the specified location.
|
||||
* @type {String}
|
||||
*/
|
||||
get url() getTabURL(this._tab),
|
||||
set url(url) setTabURL(this._tab, url),
|
||||
get url() this._tab ? getTabURL(this._tab) : undefined,
|
||||
set url(url) this._tab && setTabURL(this._tab, url),
|
||||
/**
|
||||
* URI of the favicon for the page currently loaded in this tab.
|
||||
* @type {String}
|
||||
*/
|
||||
get favicon() getFaviconURIForLocation(this.url),
|
||||
get favicon() this._tab ? getFaviconURIForLocation(this.url) : undefined,
|
||||
/**
|
||||
* The CSS style for the tab
|
||||
*/
|
||||
|
@ -137,23 +141,30 @@ const TabTrait = Trait.compose(EventEmitter, {
|
|||
* @type {Number}
|
||||
*/
|
||||
get index()
|
||||
this._window.gBrowser.getBrowserIndexForDocument(this._contentDocument),
|
||||
set index(value) this._window.gBrowser.moveTabTo(this._tab, value),
|
||||
this._tab ?
|
||||
this._window.gBrowser.getBrowserIndexForDocument(this._contentDocument) :
|
||||
undefined,
|
||||
set index(value)
|
||||
this._tab && this._window.gBrowser.moveTabTo(this._tab, value),
|
||||
/**
|
||||
* Thumbnail data URI of the page currently loaded in this tab.
|
||||
* @type {String}
|
||||
*/
|
||||
getThumbnail: function getThumbnail()
|
||||
getThumbnailURIForWindow(this._contentWindow),
|
||||
this._tab ? getThumbnailURIForWindow(this._contentWindow) : undefined,
|
||||
/**
|
||||
* Whether or not tab is pinned (Is an app-tab).
|
||||
* @type {Boolean}
|
||||
*/
|
||||
get isPinned() this._tab.pinned,
|
||||
get isPinned() this._tab ? this._tab.pinned : undefined,
|
||||
pin: function pin() {
|
||||
if (!this._tab)
|
||||
return;
|
||||
this._window.gBrowser.pinTab(this._tab);
|
||||
},
|
||||
unpin: function unpin() {
|
||||
if (!this._tab)
|
||||
return;
|
||||
this._window.gBrowser.unpinTab(this._tab);
|
||||
},
|
||||
|
||||
|
@ -162,6 +173,8 @@ const TabTrait = Trait.compose(EventEmitter, {
|
|||
* @type {Worker}
|
||||
*/
|
||||
attach: function attach(options) {
|
||||
if (!this._tab)
|
||||
return;
|
||||
// BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946
|
||||
// TODO: fix this circular dependency
|
||||
let { Worker } = require('./worker');
|
||||
|
@ -175,12 +188,16 @@ const TabTrait = Trait.compose(EventEmitter, {
|
|||
* we would like to return instance before firing a 'TabActivated' event.
|
||||
*/
|
||||
activate: defer(function activate() {
|
||||
if (!this._tab)
|
||||
return;
|
||||
activateTab(this._tab);
|
||||
}),
|
||||
/**
|
||||
* Close the tab
|
||||
*/
|
||||
close: function close(callback) {
|
||||
if (!this._tab)
|
||||
return;
|
||||
if (callback)
|
||||
this.once(EVENTS.close.name, callback);
|
||||
this._window.gBrowser.removeTab(this._tab);
|
||||
|
@ -189,6 +206,8 @@ const TabTrait = Trait.compose(EventEmitter, {
|
|||
* Reload the tab
|
||||
*/
|
||||
reload: function reload() {
|
||||
if (!this._tab)
|
||||
return;
|
||||
this._window.gBrowser.reloadTab(this._tab);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -86,6 +86,9 @@ const WindowLoader = Trait.compose({
|
|||
this._onLoad(window)
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.__window = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
__window: null,
|
||||
|
|
|
@ -196,3 +196,4 @@ def replace_file(env_root, dest_path, file_contents, must_rewrite_links):
|
|||
if must_rewrite_links and dest_path.endswith(".html"):
|
||||
file_contents = rewrite_links(env_root, get_sdk_docs_path(env_root), file_contents, dest_path)
|
||||
open(dest_path, "w").write(file_contents)
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ def gen_manifest(template_root_dir, target_cfg, jid,
|
|||
ta_desc.appendChild(elem)
|
||||
|
||||
elem = dom.createElement("em:maxVersion")
|
||||
elem.appendChild(dom.createTextNode("21.0a1"))
|
||||
elem.appendChild(dom.createTextNode("20.*"))
|
||||
ta_desc.appendChild(elem)
|
||||
|
||||
if target_cfg.get("homepage"):
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
let { Cc, Ci } = require("chrome");
|
||||
|
||||
let {Cc,Ci} = require("chrome");
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const timer = require("sdk/timers");
|
||||
|
||||
|
@ -480,6 +482,52 @@ exports.testURLContextRemove = function (test) {
|
|||
});
|
||||
};
|
||||
|
||||
// Loading a new page in the same tab should correctly start a new worker for
|
||||
// any content scripts
|
||||
exports.testPageReload = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = loader.cm.Item({
|
||||
label: "Item",
|
||||
contentScript: "var doc = document; self.on('context', function(node) doc.body.getAttribute('showItem') == 'true');"
|
||||
});
|
||||
|
||||
test.withTestDoc(function (window, doc) {
|
||||
// Set a flag on the document that the item uses
|
||||
doc.body.setAttribute("showItem", "true");
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// With the attribute true the item should be visible in the menu
|
||||
test.checkMenu([item], [], []);
|
||||
test.hideMenu(function() {
|
||||
let browser = this.tabBrowser.getBrowserForTab(this.tab)
|
||||
test.delayedEventListener(browser, "load", function() {
|
||||
test.delayedEventListener(browser, "load", function() {
|
||||
window = browser.contentWindow;
|
||||
doc = window.document;
|
||||
|
||||
// Set a flag on the document that the item uses
|
||||
doc.body.setAttribute("showItem", "false");
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// In the new document with the attribute false the item should be
|
||||
// hidden, but if the contentScript hasn't been reloaded it will
|
||||
// still see the old value
|
||||
test.checkMenu([item], [item], []);
|
||||
|
||||
test.done();
|
||||
});
|
||||
}, true);
|
||||
browser.loadURI(TEST_DOC_URL, null, null);
|
||||
}, true);
|
||||
// Required to make sure we load a new page in history rather than
|
||||
// just reloading the current page which would unload it
|
||||
browser.loadURI("about:blank", null, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Closing a page after it's been used with a worker should cause the worker
|
||||
// to be destroyed
|
||||
|
@ -555,6 +603,143 @@ exports.testContentContextNoMatch = function (test) {
|
|||
};
|
||||
|
||||
|
||||
// Content contexts that return undefined should cause their items to be absent
|
||||
// from the menu.
|
||||
exports.testContentContextUndefined = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "item",
|
||||
contentScript: 'self.on("context", function () {});'
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item], [item], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Content contexts that return an empty string should cause their items to be
|
||||
// absent from the menu and shouldn't wipe the label
|
||||
exports.testContentContextEmptyString = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "item",
|
||||
contentScript: 'self.on("context", function () "");'
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item], [item], []);
|
||||
test.assertEqual(item.label, "item", "Label should still be correct");
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// If any content contexts returns true then their items should be present in
|
||||
// the menu.
|
||||
exports.testMultipleContentContextMatch1 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "item",
|
||||
contentScript: 'self.on("context", function () true); ' +
|
||||
'self.on("context", function () false);',
|
||||
onMessage: function() {
|
||||
test.fail("Should not have called the second context listener");
|
||||
}
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item], [], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// If any content contexts returns true then their items should be present in
|
||||
// the menu.
|
||||
exports.testMultipleContentContextMatch2 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "item",
|
||||
contentScript: 'self.on("context", function () false); ' +
|
||||
'self.on("context", function () true);'
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item], [], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// If any content contexts returns a string then their items should be present
|
||||
// in the menu.
|
||||
exports.testMultipleContentContextString1 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "item",
|
||||
contentScript: 'self.on("context", function () "new label"); ' +
|
||||
'self.on("context", function () false);'
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item], [], []);
|
||||
test.assertEqual(item.label, "new label", "Label should have changed");
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// If any content contexts returns a string then their items should be present
|
||||
// in the menu.
|
||||
exports.testMultipleContentContextString2 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "item",
|
||||
contentScript: 'self.on("context", function () false); ' +
|
||||
'self.on("context", function () "new label");'
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item], [], []);
|
||||
test.assertEqual(item.label, "new label", "Label should have changed");
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// If many content contexts returns a string then the first should take effect
|
||||
exports.testMultipleContentContextString3 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "item",
|
||||
contentScript: 'self.on("context", function () "new label 1"); ' +
|
||||
'self.on("context", function () "new label 2");'
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item], [], []);
|
||||
test.assertEqual(item.label, "new label 1", "Label should have changed");
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Content contexts that return true should cause their items to be present
|
||||
// in the menu when context clicking an active element.
|
||||
exports.testContentContextMatchActiveElement = function (test) {
|
||||
|
@ -631,6 +816,44 @@ exports.testContentContextNoMatchActiveElement = function (test) {
|
|||
};
|
||||
|
||||
|
||||
// Content contexts that return undefined should cause their items to be absent
|
||||
// from the menu when context clicking an active element.
|
||||
exports.testContentContextNoMatchActiveElement = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let items = [
|
||||
new loader.cm.Item({
|
||||
label: "item 1",
|
||||
contentScript: 'self.on("context", function () {});'
|
||||
}),
|
||||
new loader.cm.Item({
|
||||
label: "item 2",
|
||||
context: undefined,
|
||||
contentScript: 'self.on("context", function () {});'
|
||||
}),
|
||||
// These items will always be hidden by the declarative usage of PageContext
|
||||
new loader.cm.Item({
|
||||
label: "item 3",
|
||||
context: loader.cm.PageContext(),
|
||||
contentScript: 'self.on("context", function () {});'
|
||||
}),
|
||||
new loader.cm.Item({
|
||||
label: "item 4",
|
||||
context: [loader.cm.PageContext()],
|
||||
contentScript: 'self.on("context", function () {});'
|
||||
})
|
||||
];
|
||||
|
||||
test.withTestDoc(function (window, doc) {
|
||||
test.showMenu(doc.getElementById("image"), function (popup) {
|
||||
test.checkMenu(items, items, []);
|
||||
test.done();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Content contexts that return a string should cause their items to be present
|
||||
// in the menu and the items' labels to be updated.
|
||||
exports.testContentContextMatchString = function (test) {
|
||||
|
@ -799,7 +1022,7 @@ exports.testUnload = function (test) {
|
|||
|
||||
|
||||
// Using multiple module instances to add items without causing overflow should
|
||||
// work OK. Assumes OVERFLOW_THRESH_DEFAULT <= 2.
|
||||
// work OK. Assumes OVERFLOW_THRESH_DEFAULT >= 2.
|
||||
exports.testMultipleModulesAdd = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader0 = test.newLoader();
|
||||
|
@ -1164,7 +1387,6 @@ exports.testMultipleModulesOrderOverflow = function (test) {
|
|||
|
||||
// Same again
|
||||
test.checkMenu([item0, item2, item1, item3], [], []);
|
||||
prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT);
|
||||
test.done();
|
||||
});
|
||||
});
|
||||
|
@ -1172,6 +1394,401 @@ exports.testMultipleModulesOrderOverflow = function (test) {
|
|||
};
|
||||
|
||||
|
||||
// Checks that if a module's items are all hidden then the overflow menu doesn't
|
||||
// get hidden
|
||||
exports.testMultipleModulesOverflowHidden = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader0 = test.newLoader();
|
||||
let loader1 = test.newLoader();
|
||||
|
||||
let prefs = loader0.loader.require("preferences-service");
|
||||
prefs.set(OVERFLOW_THRESH_PREF, 0);
|
||||
|
||||
// Use each module to add an item, then unload each module in turn.
|
||||
let item0 = new loader0.cm.Item({ label: "item 0" });
|
||||
let item1 = new loader1.cm.Item({
|
||||
label: "item 1",
|
||||
context: loader1.cm.SelectorContext("a")
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// One should be hidden
|
||||
test.checkMenu([item0, item1], [item1], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Checks that if a module's items are all hidden then the overflow menu doesn't
|
||||
// get hidden (reverse order to above)
|
||||
exports.testMultipleModulesOverflowHidden2 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader0 = test.newLoader();
|
||||
let loader1 = test.newLoader();
|
||||
|
||||
let prefs = loader0.loader.require("preferences-service");
|
||||
prefs.set(OVERFLOW_THRESH_PREF, 0);
|
||||
|
||||
// Use each module to add an item, then unload each module in turn.
|
||||
let item0 = new loader0.cm.Item({
|
||||
label: "item 0",
|
||||
context: loader0.cm.SelectorContext("a")
|
||||
});
|
||||
let item1 = new loader1.cm.Item({ label: "item 1" });
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// One should be hidden
|
||||
test.checkMenu([item0, item1], [item0], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Checks that we don't overflow if there are more items than the overflow
|
||||
// threshold but not all of them are visible
|
||||
exports.testOverflowIgnoresHidden = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let prefs = loader.loader.require("preferences-service");
|
||||
prefs.set(OVERFLOW_THRESH_PREF, 2);
|
||||
|
||||
let allItems = [
|
||||
new loader.cm.Item({
|
||||
label: "item 0"
|
||||
}),
|
||||
new loader.cm.Item({
|
||||
label: "item 1"
|
||||
}),
|
||||
new loader.cm.Item({
|
||||
label: "item 2",
|
||||
context: loader.cm.SelectorContext("a")
|
||||
})
|
||||
];
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// One should be hidden
|
||||
test.checkMenu(allItems, [allItems[2]], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Checks that we don't overflow if there are more items than the overflow
|
||||
// threshold but not all of them are visible
|
||||
exports.testOverflowIgnoresHiddenMultipleModules1 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader0 = test.newLoader();
|
||||
let loader1 = test.newLoader();
|
||||
|
||||
let prefs = loader0.loader.require("preferences-service");
|
||||
prefs.set(OVERFLOW_THRESH_PREF, 2);
|
||||
|
||||
let allItems = [
|
||||
new loader0.cm.Item({
|
||||
label: "item 0"
|
||||
}),
|
||||
new loader0.cm.Item({
|
||||
label: "item 1"
|
||||
}),
|
||||
new loader1.cm.Item({
|
||||
label: "item 2",
|
||||
context: loader1.cm.SelectorContext("a")
|
||||
}),
|
||||
new loader1.cm.Item({
|
||||
label: "item 3",
|
||||
context: loader1.cm.SelectorContext("a")
|
||||
})
|
||||
];
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// One should be hidden
|
||||
test.checkMenu(allItems, [allItems[2], allItems[3]], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Checks that we don't overflow if there are more items than the overflow
|
||||
// threshold but not all of them are visible
|
||||
exports.testOverflowIgnoresHiddenMultipleModules2 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader0 = test.newLoader();
|
||||
let loader1 = test.newLoader();
|
||||
|
||||
let prefs = loader0.loader.require("preferences-service");
|
||||
prefs.set(OVERFLOW_THRESH_PREF, 2);
|
||||
|
||||
let allItems = [
|
||||
new loader0.cm.Item({
|
||||
label: "item 0"
|
||||
}),
|
||||
new loader0.cm.Item({
|
||||
label: "item 1",
|
||||
context: loader0.cm.SelectorContext("a")
|
||||
}),
|
||||
new loader1.cm.Item({
|
||||
label: "item 2"
|
||||
}),
|
||||
new loader1.cm.Item({
|
||||
label: "item 3",
|
||||
context: loader1.cm.SelectorContext("a")
|
||||
})
|
||||
];
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// One should be hidden
|
||||
test.checkMenu(allItems, [allItems[1], allItems[3]], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Checks that we don't overflow if there are more items than the overflow
|
||||
// threshold but not all of them are visible
|
||||
exports.testOverflowIgnoresHiddenMultipleModules3 = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader0 = test.newLoader();
|
||||
let loader1 = test.newLoader();
|
||||
|
||||
let prefs = loader0.loader.require("preferences-service");
|
||||
prefs.set(OVERFLOW_THRESH_PREF, 2);
|
||||
|
||||
let allItems = [
|
||||
new loader0.cm.Item({
|
||||
label: "item 0",
|
||||
context: loader0.cm.SelectorContext("a")
|
||||
}),
|
||||
new loader0.cm.Item({
|
||||
label: "item 1",
|
||||
context: loader0.cm.SelectorContext("a")
|
||||
}),
|
||||
new loader1.cm.Item({
|
||||
label: "item 2"
|
||||
}),
|
||||
new loader1.cm.Item({
|
||||
label: "item 3"
|
||||
})
|
||||
];
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// One should be hidden
|
||||
test.checkMenu(allItems, [allItems[0], allItems[1]], []);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Tests that we transition between overflowing to non-overflowing to no items
|
||||
// and back again
|
||||
exports.testOverflowTransition = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let prefs = loader.loader.require("preferences-service");
|
||||
prefs.set(OVERFLOW_THRESH_PREF, 2);
|
||||
|
||||
let pItems = [
|
||||
new loader.cm.Item({
|
||||
label: "item 0",
|
||||
context: loader.cm.SelectorContext("p")
|
||||
}),
|
||||
new loader.cm.Item({
|
||||
label: "item 1",
|
||||
context: loader.cm.SelectorContext("p")
|
||||
})
|
||||
];
|
||||
|
||||
let aItems = [
|
||||
new loader.cm.Item({
|
||||
label: "item 2",
|
||||
context: loader.cm.SelectorContext("a")
|
||||
}),
|
||||
new loader.cm.Item({
|
||||
label: "item 3",
|
||||
context: loader.cm.SelectorContext("a")
|
||||
})
|
||||
];
|
||||
|
||||
let allItems = pItems.concat(aItems);
|
||||
|
||||
test.withTestDoc(function (window, doc) {
|
||||
test.showMenu(doc.getElementById("link"), function (popup) {
|
||||
// The menu should contain all items and will overflow
|
||||
test.checkMenu(allItems, [], []);
|
||||
popup.hidePopup();
|
||||
|
||||
test.showMenu(doc.getElementById("text"), function (popup) {
|
||||
// Only contains hald the items and will not overflow
|
||||
test.checkMenu(allItems, aItems, []);
|
||||
popup.hidePopup();
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// None of the items will be visible
|
||||
test.checkMenu(allItems, allItems, []);
|
||||
popup.hidePopup();
|
||||
|
||||
test.showMenu(doc.getElementById("text"), function (popup) {
|
||||
// Only contains hald the items and will not overflow
|
||||
test.checkMenu(allItems, aItems, []);
|
||||
popup.hidePopup();
|
||||
|
||||
test.showMenu(doc.getElementById("link"), function (popup) {
|
||||
// The menu should contain all items and will overflow
|
||||
test.checkMenu(allItems, [], []);
|
||||
popup.hidePopup();
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
// None of the items will be visible
|
||||
test.checkMenu(allItems, allItems, []);
|
||||
popup.hidePopup();
|
||||
|
||||
test.showMenu(doc.getElementById("link"), function (popup) {
|
||||
// The menu should contain all items and will overflow
|
||||
test.checkMenu(allItems, [], []);
|
||||
test.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// An item's command listener should work.
|
||||
exports.testItemCommand = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "item",
|
||||
data: "item data",
|
||||
contentScript: 'self.on("click", function (node, data) {' +
|
||||
' self.postMessage({' +
|
||||
' tagName: node.tagName,' +
|
||||
' data: data' +
|
||||
' });' +
|
||||
'});',
|
||||
onMessage: function (data) {
|
||||
test.assertEqual(this, item, "`this` inside onMessage should be item");
|
||||
test.assertEqual(data.tagName, "HTML", "node should be an HTML element");
|
||||
test.assertEqual(data.data, item.data, "data should be item data");
|
||||
test.done();
|
||||
}
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item], [], []);
|
||||
let elt = test.getItemElt(popup, item);
|
||||
|
||||
// create a command event
|
||||
let evt = elt.ownerDocument.createEvent('Event');
|
||||
evt.initEvent('command', true, true);
|
||||
elt.dispatchEvent(evt);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// A menu's click listener should work and receive bubbling 'command' events from
|
||||
// sub-items appropriately. This also tests menus and ensures that when a CSS
|
||||
// selector context matches the clicked node's ancestor, the matching ancestor
|
||||
// is passed to listeners as the clicked node.
|
||||
exports.testMenuCommand = function (test) {
|
||||
// Create a top-level menu, submenu, and item, like this:
|
||||
// topMenu -> submenu -> item
|
||||
// Click the item and make sure the click bubbles.
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let item = new loader.cm.Item({
|
||||
label: "submenu item",
|
||||
data: "submenu item data",
|
||||
context: loader.cm.SelectorContext("a"),
|
||||
});
|
||||
|
||||
let submenu = new loader.cm.Menu({
|
||||
label: "submenu",
|
||||
context: loader.cm.SelectorContext("a"),
|
||||
items: [item]
|
||||
});
|
||||
|
||||
let topMenu = new loader.cm.Menu({
|
||||
label: "top menu",
|
||||
contentScript: 'self.on("click", function (node, data) {' +
|
||||
' let Ci = Components["interfaces"];' +
|
||||
' self.postMessage({' +
|
||||
' tagName: node.tagName,' +
|
||||
' data: data' +
|
||||
' });' +
|
||||
'});',
|
||||
onMessage: function (data) {
|
||||
test.assertEqual(this, topMenu, "`this` inside top menu should be menu");
|
||||
test.assertEqual(data.tagName, "A", "Clicked node should be anchor");
|
||||
test.assertEqual(data.data, item.data,
|
||||
"Clicked item data should be correct");
|
||||
test.done();
|
||||
},
|
||||
items: [submenu],
|
||||
context: loader.cm.SelectorContext("a")
|
||||
});
|
||||
|
||||
test.withTestDoc(function (window, doc) {
|
||||
test.showMenu(doc.getElementById("span-link"), function (popup) {
|
||||
test.checkMenu([topMenu], [], []);
|
||||
let topMenuElt = test.getItemElt(popup, topMenu);
|
||||
let topMenuPopup = topMenuElt.firstChild;
|
||||
let submenuElt = test.getItemElt(topMenuPopup, submenu);
|
||||
let submenuPopup = submenuElt.firstChild;
|
||||
let itemElt = test.getItemElt(submenuPopup, item);
|
||||
|
||||
// create a command event
|
||||
let evt = itemElt.ownerDocument.createEvent('Event');
|
||||
evt.initEvent('command', true, true);
|
||||
itemElt.dispatchEvent(evt);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Click listeners should work when multiple modules are loaded.
|
||||
exports.testItemCommandMultipleModules = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader0 = test.newLoader();
|
||||
let loader1 = test.newLoader();
|
||||
|
||||
let item0 = loader0.cm.Item({
|
||||
label: "loader 0 item",
|
||||
contentScript: 'self.on("click", self.postMessage);',
|
||||
onMessage: function () {
|
||||
test.fail("loader 0 item should not emit click event");
|
||||
}
|
||||
});
|
||||
let item1 = loader1.cm.Item({
|
||||
label: "loader 1 item",
|
||||
contentScript: 'self.on("click", self.postMessage);',
|
||||
onMessage: function () {
|
||||
test.pass("loader 1 item clicked as expected");
|
||||
test.done();
|
||||
}
|
||||
});
|
||||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu([item0, item1], [], []);
|
||||
let item1Elt = test.getItemElt(popup, item1);
|
||||
|
||||
// create a command event
|
||||
let evt = item1Elt.ownerDocument.createEvent('Event');
|
||||
evt.initEvent('command', true, true);
|
||||
item1Elt.dispatchEvent(evt);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// An item's click listener should work.
|
||||
exports.testItemClick = function (test) {
|
||||
test = new TestHelper(test);
|
||||
|
@ -1526,6 +2143,7 @@ exports.testDrawImageOnClickNode = function (test) {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
// Setting an item's label before the menu is ever shown should correctly change
|
||||
// its label.
|
||||
exports.testSetLabelBeforeShow = function (test) {
|
||||
|
@ -1589,7 +2207,6 @@ exports.testSetLabelBeforeShowOverflow = function (test) {
|
|||
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu(items, [], []);
|
||||
prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT);
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
@ -1617,7 +2234,6 @@ exports.testSetLabelAfterShowOverflow = function (test) {
|
|||
test.assertEqual(items[0].label, "z");
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu(items, [], []);
|
||||
prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT);
|
||||
test.done();
|
||||
});
|
||||
});
|
||||
|
@ -2090,6 +2706,8 @@ exports.testSubItemDefaultVisible = function (test) {
|
|||
});
|
||||
};
|
||||
|
||||
// Tests that the click event on sub menuitem
|
||||
// tiggers the click event for the sub menuitem and the parent menu
|
||||
exports.testSubItemClick = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
@ -2145,6 +2763,68 @@ exports.testSubItemClick = function (test) {
|
|||
});
|
||||
};
|
||||
|
||||
// Tests that the command event on sub menuitem
|
||||
// tiggers the click event for the sub menuitem and the parent menu
|
||||
exports.testSubItemCommand = function (test) {
|
||||
test = new TestHelper(test);
|
||||
let loader = test.newLoader();
|
||||
|
||||
let state = 0;
|
||||
|
||||
let items = [
|
||||
loader.cm.Menu({
|
||||
label: "menu 1",
|
||||
items: [
|
||||
loader.cm.Item({
|
||||
label: "subitem 1",
|
||||
data: "foobar",
|
||||
contentScript: 'self.on("click", function (node, data) {' +
|
||||
' self.postMessage({' +
|
||||
' tagName: node.tagName,' +
|
||||
' data: data' +
|
||||
' });' +
|
||||
'});',
|
||||
onMessage: function(msg) {
|
||||
test.assertEqual(msg.tagName, "HTML", "should have seen the right node");
|
||||
test.assertEqual(msg.data, "foobar", "should have seen the right data");
|
||||
test.assertEqual(state, 0, "should have seen the event at the right time");
|
||||
state++;
|
||||
}
|
||||
})
|
||||
],
|
||||
contentScript: 'self.on("click", function (node, data) {' +
|
||||
' self.postMessage({' +
|
||||
' tagName: node.tagName,' +
|
||||
' data: data' +
|
||||
' });' +
|
||||
'});',
|
||||
onMessage: function(msg) {
|
||||
test.assertEqual(msg.tagName, "HTML", "should have seen the right node");
|
||||
test.assertEqual(msg.data, "foobar", "should have seen the right data");
|
||||
test.assertEqual(state, 1, "should have seen the event at the right time");
|
||||
state++
|
||||
|
||||
test.done();
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
test.withTestDoc(function (window, doc) {
|
||||
test.showMenu(null, function (popup) {
|
||||
test.checkMenu(items, [], []);
|
||||
|
||||
let topMenuElt = test.getItemElt(popup, items[0]);
|
||||
let topMenuPopup = topMenuElt.firstChild;
|
||||
let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]);
|
||||
|
||||
// create a command event
|
||||
let evt = itemElt.ownerDocument.createEvent('Event');
|
||||
evt.initEvent('command', true, true);
|
||||
itemElt.dispatchEvent(evt);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Tests that opening a context menu for an outer frame when an inner frame
|
||||
// has a selection doesn't activate the SelectionContext
|
||||
exports.testSelectionInInnerFrameNoMatch = function (test) {
|
||||
|
@ -2249,6 +2929,8 @@ function TestHelper(test) {
|
|||
this.browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Ci.nsIWindowMediator).
|
||||
getMostRecentWindow("navigator:browser");
|
||||
this.overflowThreshValue = require("sdk/preferences/service").
|
||||
get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT);
|
||||
}
|
||||
|
||||
TestHelper.prototype = {
|
||||
|
@ -2351,6 +3033,9 @@ TestHelper.prototype = {
|
|||
let mainNodes = this.browserWindow.document.querySelectorAll("#contentAreaContextMenu > ." + ITEM_CLASS);
|
||||
let overflowNodes = this.browserWindow.document.querySelectorAll("." + OVERFLOW_POPUP_CLASS + " > ." + ITEM_CLASS);
|
||||
|
||||
this.test.assert(mainNodes.length == 0 || overflowNodes.length == 0,
|
||||
"Should only see nodes at the top level or in overflow");
|
||||
|
||||
let overflow = this.overflowSubmenu;
|
||||
if (this.shouldOverflow(total)) {
|
||||
this.test.assert(overflow && !overflow.hidden,
|
||||
|
@ -2361,12 +3046,15 @@ TestHelper.prototype = {
|
|||
else {
|
||||
this.test.assert(!overflow || overflow.hidden,
|
||||
"overflow menu should not be present");
|
||||
this.test.assertEqual(overflowNodes.length, 0,
|
||||
"should be no items in the overflow context menu");
|
||||
// When visible nodes == 0 they could be in overflow or top level
|
||||
if (total > 0) {
|
||||
this.test.assertEqual(overflowNodes.length, 0,
|
||||
"should be no items in the overflow context menu");
|
||||
}
|
||||
}
|
||||
|
||||
let nodes = this.shouldOverflow(total) ? overflowNodes : mainNodes;
|
||||
|
||||
// Iterate over wherever the nodes have ended up
|
||||
let nodes = mainNodes.length ? mainNodes : overflowNodes;
|
||||
this.checkNodes(nodes, presentItems, absentItems, removedItems)
|
||||
let pos = 0;
|
||||
},
|
||||
|
@ -2382,6 +3070,11 @@ TestHelper.prototype = {
|
|||
if (removedItems.indexOf(item) >= 0)
|
||||
continue;
|
||||
|
||||
if (nodes.length <= pos) {
|
||||
this.test.assert(false, "Not enough nodes");
|
||||
return;
|
||||
}
|
||||
|
||||
let hidden = absentItems.indexOf(item) >= 0;
|
||||
|
||||
this.checkItemElt(nodes[pos], item);
|
||||
|
@ -2432,12 +3125,16 @@ TestHelper.prototype = {
|
|||
|
||||
// Call to finish the test.
|
||||
done: function () {
|
||||
const self = this;
|
||||
function commonDone() {
|
||||
this.closeTab();
|
||||
|
||||
while (this.loaders.length) {
|
||||
this.loaders[0].unload();
|
||||
}
|
||||
|
||||
require("sdk/preferences/service").set(OVERFLOW_THRESH_PREF, self.overflowThreshValue);
|
||||
|
||||
this.test.done();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,23 +5,19 @@
|
|||
const port = 8099;
|
||||
const file = require("sdk/io/file");
|
||||
const { pathFor } = require("sdk/system");
|
||||
const { Loader } = require("sdk/test/loader");
|
||||
const options = require("@test/options");
|
||||
|
||||
const loader = Loader(module);
|
||||
const httpd = loader.require("sdk/test/httpd");
|
||||
if (options.parseable || options.verbose)
|
||||
loader.sandbox("sdk/test/httpd").DEBUG = true;
|
||||
|
||||
exports.testBasicHTTPServer = function(test) {
|
||||
let basePath = pathFor("TmpD");
|
||||
// Use the profile directory for the temporary file as that will be deleted
|
||||
// when tests are complete
|
||||
let basePath = pathFor("ProfD");
|
||||
let filePath = file.join(basePath, 'test-httpd.txt');
|
||||
let content = "This is the HTTPD test file.\n";
|
||||
let fileStream = file.open(filePath, 'w');
|
||||
fileStream.write(content);
|
||||
fileStream.close();
|
||||
|
||||
let srv = httpd.startServerAsync(port, basePath);
|
||||
let { startServerAsync } = require("sdk/test/httpd");
|
||||
let srv = startServerAsync(port, basePath);
|
||||
|
||||
test.waitUntilDone();
|
||||
|
||||
|
@ -45,7 +41,8 @@ exports.testBasicHTTPServer = function(test) {
|
|||
exports.testDynamicServer = function (test) {
|
||||
let content = "This is the HTTPD test file.\n";
|
||||
|
||||
let srv = httpd.startServerAsync(port);
|
||||
let { startServerAsync } = require("sdk/test/httpd");
|
||||
let srv = startServerAsync(port);
|
||||
|
||||
// See documentation here:
|
||||
//http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a
|
||||
|
|
|
@ -1029,3 +1029,4 @@ if (require("sdk/system/xul-app").is("Fennec")) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,3 +28,4 @@ exports.testIsActiveDefault = function(test) {
|
|||
test.assertEqual(pb.isActive, false,
|
||||
'pb.isActive returns false when private browsing isn\'t supported');
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@ if (options.parseable || options.verbose)
|
|||
loader.sandbox("sdk/test/httpd").DEBUG = true;
|
||||
const { startServerAsync } = httpd;
|
||||
|
||||
const basePath = pathFor("TmpD")
|
||||
// Use the profile directory for the temporary files as that will be deleted
|
||||
// when tests are complete
|
||||
const basePath = pathFor("ProfD")
|
||||
const port = 8099;
|
||||
|
||||
|
||||
|
|
|
@ -109,6 +109,28 @@ function step3(assert, done) {
|
|||
});
|
||||
}
|
||||
|
||||
exports["test behavior on close"] = function(assert, done) {
|
||||
|
||||
tabs.open({
|
||||
url: "about:mozilla",
|
||||
onReady: function(tab) {
|
||||
assert.equal(tab.url, "about:mozilla", "Tab has the expected url");
|
||||
assert.equal(tab.index, 1, "Tab has the expected index");
|
||||
tab.close(function () {
|
||||
assert.equal(tab.url, undefined,
|
||||
"After being closed, tab attributes are undefined (url)");
|
||||
assert.equal(tab.index, undefined,
|
||||
"After being closed, tab attributes are undefined (index)");
|
||||
// Ensure that we can call destroy multiple times without throwing
|
||||
tab.destroy();
|
||||
tab.destroy();
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (require("sdk/system/xul-app").is("Fennec")) {
|
||||
module.exports = {
|
||||
"test Unsupported Test": function UnsupportedTest (assert) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче