зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1190688) for browser_ext_tabs_executeScript.js permatimeouts
Backed out changeset 1d5e9f3d094d (bug 1190688) Backed out changeset 4a10c564dfca (bug 1190688)
This commit is contained in:
Родитель
6092606eef
Коммит
fdc2d1bcff
|
@ -32,8 +32,6 @@ function BrowserAction(options, extension)
|
|||
this.id = makeWidgetId(extension.id) + "-browser-action";
|
||||
this.widget = null;
|
||||
|
||||
this.tabManager = TabManager.for(extension);
|
||||
|
||||
let title = extension.localize(options.default_title || "");
|
||||
let popup = extension.localize(options.default_popup || "");
|
||||
if (popup) {
|
||||
|
@ -76,7 +74,6 @@ BrowserAction.prototype = {
|
|||
node.addEventListener("command", event => {
|
||||
let tab = tabbrowser.selectedTab;
|
||||
let popup = this.getProperty(tab, "popup");
|
||||
this.tabManager.addActiveTabPermission(tab);
|
||||
if (popup) {
|
||||
this.togglePopup(node, popup);
|
||||
} else {
|
||||
|
|
|
@ -43,11 +43,8 @@ var menuBuilder = {
|
|||
for (let [ext, menuItemMap] of contextMenuMap) {
|
||||
let parentMap = new Map();
|
||||
let topLevelItems = new Set();
|
||||
for (let entry of menuItemMap) {
|
||||
// We need a closure over |item|, and we don't currently get a
|
||||
// fresh binding per loop if we declare it in the loop head.
|
||||
let [id, item] = entry;
|
||||
|
||||
for (let [id, item] of menuItemMap) {
|
||||
dump(id + " : " + item + "\n");
|
||||
if (item.enabledForContext(contextData)) {
|
||||
let element;
|
||||
if (item.isMenu) {
|
||||
|
@ -82,13 +79,15 @@ var menuBuilder = {
|
|||
topLevelItems.add(element);
|
||||
}
|
||||
|
||||
element.addEventListener("command", event => {
|
||||
item.tabManager.addActiveTabPermission();
|
||||
if (item.onclick) {
|
||||
let clickData = item.getClickData(contextData, event);
|
||||
runSafe(item.extContext, item.onclick, clickData);
|
||||
if (item.onclick) {
|
||||
function clickHandlerForItem(item) {
|
||||
return event => {
|
||||
let clickData = item.getClickData(contextData, event);
|
||||
runSafe(item.extContext, item.onclick, clickData);
|
||||
}
|
||||
}
|
||||
});
|
||||
element.addEventListener("command", clickHandlerForItem(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (topLevelItems.size > 1) {
|
||||
|
@ -167,8 +166,6 @@ function MenuItem(extension, extContext, createProperties)
|
|||
this.extension = extension;
|
||||
this.extContext = extContext;
|
||||
|
||||
this.tabManager = TabManager.for(extension);
|
||||
|
||||
this.init(createProperties);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ function PageAction(options, extension)
|
|||
this.extension = extension;
|
||||
this.id = makeWidgetId(extension.id) + "-page-action";
|
||||
|
||||
this.tabManager = TabManager.for(extension);
|
||||
|
||||
let title = extension.localize(options.default_title || "");
|
||||
let popup = extension.localize(options.default_popup || "");
|
||||
if (popup) {
|
||||
|
@ -141,8 +139,6 @@ PageAction.prototype = {
|
|||
let tab = window.gBrowser.selectedTab;
|
||||
let popup = this.tabContext.get(tab).popup;
|
||||
|
||||
this.tabManager.addActiveTabPermission(tab);
|
||||
|
||||
if (popup) {
|
||||
openPanel(this.getButton(window), popup, this.extension);
|
||||
} else {
|
||||
|
|
|
@ -457,8 +457,13 @@ extensions.registerAPI((extension, context) => {
|
|||
}
|
||||
|
||||
let result = [];
|
||||
for (let window of WindowListManager.browserWindows()) {
|
||||
let tabs = TabManager.for(extension).getTabs(window);
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
let window = e.getNext();
|
||||
if (window.document.readyState != "complete") {
|
||||
continue;
|
||||
}
|
||||
let tabs = TabManager.getTabs(extension, window);
|
||||
for (let tab of tabs) {
|
||||
if (matches(window, tab)) {
|
||||
result.push(tab);
|
||||
|
@ -484,15 +489,9 @@ extensions.registerAPI((extension, context) => {
|
|||
// window IDs and insufficient permissions need to result in a
|
||||
// callback with |lastError| set.
|
||||
innerWindowID: tab.linkedBrowser.innerWindowID,
|
||||
};
|
||||
|
||||
if (TabManager.for(extension).hasActiveTabPermission(tab)) {
|
||||
// If we have the "activeTab" permission for this tab, ignore
|
||||
// the host whitelist.
|
||||
options.matchesHost = ["<all_urls>"];
|
||||
} else {
|
||||
options.matchesHost = extension.whiteListedHosts.serialize();
|
||||
}
|
||||
matchesHost: extension.whiteListedHosts.serialize(),
|
||||
};
|
||||
|
||||
if (details.code) {
|
||||
options[kind + 'Code'] = details.code;
|
||||
|
|
|
@ -267,87 +267,7 @@ TabContext.prototype = {
|
|||
},
|
||||
};
|
||||
|
||||
// Manages tab mappings and permissions for a specific extension.
|
||||
function ExtensionTabManager(extension) {
|
||||
this.extension = extension;
|
||||
|
||||
// A mapping of tab objects to the inner window ID the extension currently has
|
||||
// the active tab permission for. The active permission for a given tab is
|
||||
// valid only for the inner window that was active when the permission was
|
||||
// granted. If the tab navigates, the inner window ID changes, and the
|
||||
// permission automatically becomes stale.
|
||||
//
|
||||
// WeakMap[tab => inner-window-id<int>]
|
||||
this.hasTabPermissionFor = new WeakMap();
|
||||
}
|
||||
|
||||
ExtensionTabManager.prototype = {
|
||||
addActiveTabPermission(tab = TabManager.activeTab) {
|
||||
if (this.extension.hasPermission("activeTab")) {
|
||||
// Note that, unlike Chrome, we don't currently clear this permission with
|
||||
// the tab navigates. If the inner window is revived from BFCache before
|
||||
// we've granted this permission to a new inner window, the extension
|
||||
// maintains its permissions for it.
|
||||
this.hasTabPermissionFor.set(tab, tab.linkedBrowser.innerWindowID);
|
||||
}
|
||||
},
|
||||
|
||||
// Returns true if the extension has the "activeTab" permission for this tab.
|
||||
// This is somewhat more permissive than the generic "tabs" permission, as
|
||||
// checked by |hasTabPermission|, in that it also allows programmatic script
|
||||
// injection without an explicit host permission.
|
||||
hasActiveTabPermission(tab) {
|
||||
// This check is redundant with addTabPermission, but cheap.
|
||||
if (this.extension.hasPermission("activeTab")) {
|
||||
return (this.hasTabPermissionFor.has(tab) &&
|
||||
this.hasTabPermissionFor.get(tab) === tab.linkedBrowser.innerWindowID);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
hasTabPermission(tab) {
|
||||
return this.extension.hasPermission("tabs") || this.hasActiveTabPermission(tab);
|
||||
},
|
||||
|
||||
convert(tab) {
|
||||
let window = tab.ownerDocument.defaultView;
|
||||
let windowActive = window == WindowManager.topWindow;
|
||||
|
||||
let result = {
|
||||
id: TabManager.getId(tab),
|
||||
index: tab._tPos,
|
||||
windowId: WindowManager.getId(window),
|
||||
selected: tab.selected,
|
||||
highlighted: tab.selected,
|
||||
active: tab.selected,
|
||||
pinned: tab.pinned,
|
||||
status: TabManager.getStatus(tab),
|
||||
incognito: PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser),
|
||||
width: tab.linkedBrowser.clientWidth,
|
||||
height: tab.linkedBrowser.clientHeight,
|
||||
};
|
||||
|
||||
if (this.hasTabPermission(tab)) {
|
||||
result.url = tab.linkedBrowser.currentURI.spec;
|
||||
if (tab.linkedBrowser.contentTitle) {
|
||||
result.title = tab.linkedBrowser.contentTitle;
|
||||
}
|
||||
let icon = window.gBrowser.getIcon(tab);
|
||||
if (icon) {
|
||||
result.favIconUrl = icon;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
getTabs(window) {
|
||||
return Array.from(window.gBrowser.tabs, tab => this.convert(tab));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// Manages global mappings between XUL tabs and extension tab IDs.
|
||||
// Manages mapping between XUL tabs and extension tab IDs.
|
||||
global.TabManager = {
|
||||
_tabs: new WeakMap(),
|
||||
_nextId: 1,
|
||||
|
@ -402,26 +322,44 @@ global.TabManager = {
|
|||
},
|
||||
|
||||
convert(extension, tab) {
|
||||
return TabManager.for(extension).convert(tab);
|
||||
let window = tab.ownerDocument.defaultView;
|
||||
let windowActive = window == WindowManager.topWindow;
|
||||
let result = {
|
||||
id: this.getId(tab),
|
||||
index: tab._tPos,
|
||||
windowId: WindowManager.getId(window),
|
||||
selected: tab.selected,
|
||||
highlighted: tab.selected,
|
||||
active: tab.selected,
|
||||
pinned: tab.pinned,
|
||||
status: this.getStatus(tab),
|
||||
incognito: PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser),
|
||||
width: tab.linkedBrowser.clientWidth,
|
||||
height: tab.linkedBrowser.clientHeight,
|
||||
};
|
||||
|
||||
if (extension.hasPermission("tabs")) {
|
||||
result.url = tab.linkedBrowser.currentURI.spec;
|
||||
if (tab.linkedBrowser.contentTitle) {
|
||||
result.title = tab.linkedBrowser.contentTitle;
|
||||
}
|
||||
let icon = window.gBrowser.getIcon(tab);
|
||||
if (icon) {
|
||||
result.favIconUrl = icon;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
getTabs(extension, window) {
|
||||
if (!window.gBrowser) {
|
||||
return [];
|
||||
}
|
||||
return Array.map(window.gBrowser.tabs, tab => this.convert(extension, tab));
|
||||
},
|
||||
};
|
||||
|
||||
// WeakMap[Extension -> ExtensionTabManager]
|
||||
let tabManagers = new WeakMap();
|
||||
|
||||
// Returns the extension-specific tab manager for the given extension, or
|
||||
// creates one if it doesn't already exist.
|
||||
TabManager.for = function (extension) {
|
||||
if (!tabManagers.has(extension)) {
|
||||
tabManagers.set(extension, new ExtensionTabManager(extension));
|
||||
}
|
||||
return tabManagers.get(extension);
|
||||
};
|
||||
|
||||
extensions.on("shutdown", (type, extension) => {
|
||||
tabManagers.delete(extension);
|
||||
});
|
||||
|
||||
// Manages mapping between XUL windows and extension window IDs.
|
||||
global.WindowManager = {
|
||||
_windows: new WeakMap(),
|
||||
|
@ -473,7 +411,7 @@ global.WindowManager = {
|
|||
};
|
||||
|
||||
if (getInfo && getInfo.populate) {
|
||||
results.tabs = TabManager.for(extension).getTabs(window);
|
||||
results.tabs = TabManager.getTabs(extension, window);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -1,31 +1,21 @@
|
|||
"use strict";
|
||||
|
||||
function* testHasPermission(params) {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true);
|
||||
|
||||
let contentSetup = params.contentSetup || (() => Promise.resolve());
|
||||
add_task(function* () {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: params.manifest,
|
||||
manifest: {
|
||||
"permissions": ["tabs"]
|
||||
},
|
||||
|
||||
background: `(${function(contentSetup) {
|
||||
background: function() {
|
||||
browser.runtime.onMessage.addListener((msg, sender) => {
|
||||
browser.test.assertEq(msg, "script ran", "script ran");
|
||||
browser.test.notifyPass("executeScript");
|
||||
});
|
||||
|
||||
browser.test.onMessage.addListener(msg => {
|
||||
browser.test.assertEq(msg, "execute-script");
|
||||
|
||||
browser.tabs.executeScript({
|
||||
file: "script.js"
|
||||
});
|
||||
browser.tabs.executeScript({
|
||||
file: "script.js"
|
||||
});
|
||||
|
||||
contentSetup().then(() => {
|
||||
browser.test.sendMessage("ready");
|
||||
});
|
||||
}})(${contentSetup})`,
|
||||
},
|
||||
|
||||
files: {
|
||||
"script.js": function() {
|
||||
|
@ -35,225 +25,8 @@ function* testHasPermission(params) {
|
|||
});
|
||||
|
||||
yield extension.startup();
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
if (params.setup) {
|
||||
yield params.setup(extension);
|
||||
}
|
||||
|
||||
extension.sendMessage("execute-script");
|
||||
|
||||
yield extension.awaitFinish("executeScript");
|
||||
yield extension.unload();
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
// This is a pretty terrible hack, but it's the best we can do until we
|
||||
// support |executeScript| callbacks and |lastError|.
|
||||
function* testHasNoPermission(params) {
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
|
||||
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
|
||||
|
||||
let contentSetup = params.contentSetup || (() => Promise.resolve());
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: params.manifest,
|
||||
|
||||
background: `(${function(contentSetup) {
|
||||
browser.runtime.onMessage.addListener((msg, sender) => {
|
||||
browser.test.assertEq(msg, "second script ran", "second script ran");
|
||||
browser.test.notifyPass("executeScript");
|
||||
});
|
||||
|
||||
browser.test.onMessage.addListener(msg => {
|
||||
browser.test.assertEq(msg, "execute-script");
|
||||
|
||||
browser.tabs.query({ activeWindow: true }, tabs => {
|
||||
browser.tabs.executeScript({
|
||||
file: "script.js"
|
||||
});
|
||||
|
||||
// Execute a script we know we have permissions for in the
|
||||
// second tab, in the hopes that it will execute after the
|
||||
// first one. This has intermittent failure written all over
|
||||
// it, but it's just about the best we can do until we
|
||||
// support callbacks for executeScript.
|
||||
browser.tabs.executeScript(tabs[1].id, {
|
||||
file: "second-script.js"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
contentSetup().then(() => {
|
||||
browser.test.sendMessage("ready");
|
||||
});
|
||||
}})(${contentSetup})`,
|
||||
|
||||
files: {
|
||||
"script.js": function() {
|
||||
browser.runtime.sendMessage("first script ran");
|
||||
},
|
||||
|
||||
"second-script.js": function() {
|
||||
browser.runtime.sendMessage("second script ran");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
if (params.setup) {
|
||||
yield params.setup(extension);
|
||||
}
|
||||
|
||||
extension.sendMessage("execute-script");
|
||||
|
||||
yield extension.awaitFinish("executeScript");
|
||||
yield extension.unload();
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab2);
|
||||
yield BrowserTestUtils.removeTab(tab1);
|
||||
}
|
||||
|
||||
add_task(function* testGoodPermissions() {
|
||||
info("Test explicit host permission");
|
||||
yield testHasPermission({
|
||||
manifest: { "permissions": ["http://mochi.test/"] }
|
||||
});
|
||||
|
||||
info("Test explicit host subdomain permission");
|
||||
yield testHasPermission({
|
||||
manifest: { "permissions": ["http://*.mochi.test/"] }
|
||||
});
|
||||
|
||||
info("Test explicit <all_urls> permission");
|
||||
yield testHasPermission({
|
||||
manifest: { "permissions": ["<all_urls>"] }
|
||||
});
|
||||
|
||||
info("Test activeTab permission with a browser action click");
|
||||
yield testHasPermission({
|
||||
manifest: {
|
||||
"permissions": ["activeTab"],
|
||||
"browser_action": {},
|
||||
},
|
||||
setup: clickBrowserAction,
|
||||
});
|
||||
|
||||
info("Test activeTab permission with a page action click");
|
||||
yield testHasPermission({
|
||||
manifest: {
|
||||
"permissions": ["activeTab"],
|
||||
"page_action": {},
|
||||
},
|
||||
contentSetup() {
|
||||
return new Promise(resolve => {
|
||||
browser.tabs.query({ active: true, currentWindow: true }, tabs => {
|
||||
browser.pageAction.show(tabs[0].id);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
setup: clickPageAction,
|
||||
});
|
||||
|
||||
info("Test activeTab permission with a browser action w/popup click");
|
||||
yield testHasPermission({
|
||||
manifest: {
|
||||
"permissions": ["activeTab"],
|
||||
"browser_action": { "default_popup": "_blank.html" },
|
||||
},
|
||||
setup: clickBrowserAction,
|
||||
});
|
||||
|
||||
info("Test activeTab permission with a page action w/popup click");
|
||||
yield testHasPermission({
|
||||
manifest: {
|
||||
"permissions": ["activeTab"],
|
||||
"page_action": { "default_popup": "_blank.html" },
|
||||
},
|
||||
contentSetup() {
|
||||
return new Promise(resolve => {
|
||||
browser.tabs.query({ active: true, currentWindow: true }, tabs => {
|
||||
browser.pageAction.show(tabs[0].id);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
setup: clickPageAction,
|
||||
});
|
||||
|
||||
info("Test activeTab permission with a context menu click");
|
||||
yield testHasPermission({
|
||||
manifest: {
|
||||
"permissions": ["activeTab", "contextMenus"],
|
||||
},
|
||||
contentSetup() {
|
||||
browser.contextMenus.create({ title: "activeTab", contexts: ["all"] });
|
||||
return Promise.resolve();
|
||||
},
|
||||
setup: function* (extension) {
|
||||
info("setup");
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
|
||||
let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
|
||||
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("a[href]", { type: "contextmenu", button: 2 },
|
||||
gBrowser.selectedBrowser);
|
||||
info("clicked");
|
||||
yield awaitPopupShown;
|
||||
info("shown");
|
||||
|
||||
let item = contextMenu.querySelector("[label=activeTab]");
|
||||
|
||||
yield EventUtils.synthesizeMouseAtCenter(item, {}, window);
|
||||
info("re-clicked");
|
||||
|
||||
yield awaitPopupHidden;
|
||||
info("hidden");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testBadPermissions() {
|
||||
info("Test no special permissions");
|
||||
yield testHasNoPermission({
|
||||
manifest: { "permissions": ["http://example.com/"] }
|
||||
});
|
||||
|
||||
info("Test tabs permissions");
|
||||
yield testHasNoPermission({
|
||||
manifest: { "permissions": ["http://example.com/", "tabs"] }
|
||||
});
|
||||
|
||||
info("Test active tab, browser action, no click");
|
||||
yield testHasNoPermission({
|
||||
manifest: {
|
||||
"permissions": ["http://example.com/", "activeTab"],
|
||||
"browser_action": {},
|
||||
},
|
||||
});
|
||||
|
||||
info("Test active tab, page action, no click");
|
||||
yield testHasNoPermission({
|
||||
manifest: {
|
||||
"permissions": ["http://example.com/", "activeTab"],
|
||||
"page_action": {},
|
||||
},
|
||||
contentSetup() {
|
||||
return new Promise(resolve => {
|
||||
browser.tabs.query({ active: true, currentWindow: true }, tabs => {
|
||||
browser.pageAction.show(tabs[0].id);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Test that |executeScript| fails if the tab has navigated to a
|
||||
// new page, and no longer matches our expected state. This involves
|
||||
// intentionally trying to trigger a race condition, and is probably not
|
||||
// even worth attempting until we have proper |executeScript| callbacks.
|
||||
|
|
|
@ -44,6 +44,6 @@ function clickPageAction(extension, win = window) {
|
|||
let pageActionId = makeWidgetId(extension.id) + "-page-action";
|
||||
let elem = win.document.getElementById(pageActionId);
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(elem, {}, win);
|
||||
EventUtils.synthesizeMouse(elem, 8, 8, {}, win);
|
||||
return new Promise(SimpleTest.executeSoon);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче