mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 18:26:15 +00:00
Bug 1419195: Show items from WebExtensions in Places Library context menu r=mixedpuppy
Depends on D16413 Differential Revision: https://phabricator.services.mozilla.com/D16414 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
7005fd08d0
commit
beef78d6c8
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
ChromeUtils.defineModuleGetter(this, "Bookmarks",
|
||||||
|
"resource://gre/modules/Bookmarks.jsm");
|
||||||
|
|
||||||
var {
|
var {
|
||||||
DefaultMap,
|
DefaultMap,
|
||||||
ExtensionError,
|
ExtensionError,
|
||||||
@ -863,6 +866,72 @@ MenuItem.prototype = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// windowTracker only looks as browser windows, but we're also interested in
|
||||||
|
// the Library window. Helper for menuTracker below.
|
||||||
|
const libraryTracker = {
|
||||||
|
libraryWindowType: "Places:Organizer",
|
||||||
|
|
||||||
|
isLibraryWindow(window) {
|
||||||
|
let winType = window.document.documentElement.getAttribute("windowtype");
|
||||||
|
return winType === this.libraryWindowType;
|
||||||
|
},
|
||||||
|
|
||||||
|
init(listener) {
|
||||||
|
this._listener = listener;
|
||||||
|
Services.ww.registerNotification(this);
|
||||||
|
|
||||||
|
// See WindowTrackerBase#*browserWindows in ext-tabs-base.js for why we
|
||||||
|
// can't use the enumerator's windowtype filter.
|
||||||
|
for (let window of Services.wm.getEnumerator("")) {
|
||||||
|
if (window.document.readyState === "complete") {
|
||||||
|
if (this.isLibraryWindow(window)) {
|
||||||
|
this.notify(window);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.addEventListener("load", this, {once: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// cleanupWindow is called on any library window that's open.
|
||||||
|
uninit(cleanupWindow) {
|
||||||
|
Services.ww.unregisterNotification(this);
|
||||||
|
|
||||||
|
for (let window of Services.wm.getEnumerator(this.libraryWindowType)) {
|
||||||
|
try {
|
||||||
|
window.removeEventListener("load", this);
|
||||||
|
cleanupWindow(window);
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Gets notifications from Services.ww.registerNotification.
|
||||||
|
// Defer actually doing anything until the window's loaded, though.
|
||||||
|
observe(window, topic) {
|
||||||
|
if (topic === "domwindowopened") {
|
||||||
|
window.addEventListener("load", this, {once: true});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Gets the load event for new windows(registered in observe()).
|
||||||
|
handleEvent(event) {
|
||||||
|
let window = event.target.defaultView;
|
||||||
|
if (this.isLibraryWindow(window)) {
|
||||||
|
this.notify(window);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
notify(window) {
|
||||||
|
try {
|
||||||
|
this._listener.call(null, window);
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// While any extensions are active, this Tracker registers to observe/listen
|
// While any extensions are active, this Tracker registers to observe/listen
|
||||||
// for menu events from both Tools and context menus, both content and chrome.
|
// for menu events from both Tools and context menus, both content and chrome.
|
||||||
const menuTracker = {
|
const menuTracker = {
|
||||||
@ -874,6 +943,7 @@ const menuTracker = {
|
|||||||
this.onWindowOpen(window);
|
this.onWindowOpen(window);
|
||||||
}
|
}
|
||||||
windowTracker.addOpenListener(this.onWindowOpen);
|
windowTracker.addOpenListener(this.onWindowOpen);
|
||||||
|
libraryTracker.init(this.onLibraryOpen);
|
||||||
},
|
},
|
||||||
|
|
||||||
unregister() {
|
unregister() {
|
||||||
@ -882,6 +952,7 @@ const menuTracker = {
|
|||||||
this.cleanupWindow(window);
|
this.cleanupWindow(window);
|
||||||
}
|
}
|
||||||
windowTracker.removeOpenListener(this.onWindowOpen);
|
windowTracker.removeOpenListener(this.onWindowOpen);
|
||||||
|
libraryTracker.uninit(this.cleanupLibrary);
|
||||||
},
|
},
|
||||||
|
|
||||||
observe(subject, topic, data) {
|
observe(subject, topic, data) {
|
||||||
@ -929,6 +1000,16 @@ const menuTracker = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onLibraryOpen(window) {
|
||||||
|
const menu = window.document.getElementById("placesContext");
|
||||||
|
menu.addEventListener("popupshowing", menuTracker.onBookmarksContextMenu);
|
||||||
|
},
|
||||||
|
|
||||||
|
cleanupLibrary(window) {
|
||||||
|
const menu = window.document.getElementById("placesContext");
|
||||||
|
menu.removeEventListener("popupshowing", menuTracker.onBookmarksContextMenu);
|
||||||
|
},
|
||||||
|
|
||||||
handleEvent(event) {
|
handleEvent(event) {
|
||||||
const menu = event.target;
|
const menu = event.target;
|
||||||
|
|
||||||
@ -963,6 +1044,10 @@ const menuTracker = {
|
|||||||
const cell = tree.boxObject.getCellAt(event.x, event.y);
|
const cell = tree.boxObject.getCellAt(event.x, event.y);
|
||||||
const node = tree.view.nodeForTreeIndex(cell.row);
|
const node = tree.view.nodeForTreeIndex(cell.row);
|
||||||
|
|
||||||
|
if (!node.bookmarkGuid || Bookmarks.isVirtualRootItem(node.bookmarkGuid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
gMenuBuilder.build({
|
gMenuBuilder.build({
|
||||||
menu,
|
menu,
|
||||||
bookmarkId: node.bookmarkGuid,
|
bookmarkId: node.bookmarkGuid,
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
Services.scriptloader.loadSubScript(
|
Services.scriptloader.loadSubScript(
|
||||||
"chrome://mochitests/content/browser/browser/components/places/tests/browser/head.js",
|
"chrome://mochitests/content/browser/browser/components/places/tests/browser/head.js",
|
||||||
this);
|
this);
|
||||||
/* globals withSidebarTree, synthesizeClickOnSelectedTreeCell */
|
/* globals withSidebarTree, synthesizeClickOnSelectedTreeCell,
|
||||||
|
* promiseLibrary, promiseLibraryClosed
|
||||||
|
*/
|
||||||
|
|
||||||
const PAGE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html";
|
const PAGE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html";
|
||||||
|
|
||||||
@ -606,6 +608,75 @@ add_task(async function test_bookmark_sidebar_contextmenu() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function bookmarkFolderContextMenuExtension() {
|
||||||
|
return ExtensionTestUtils.loadExtension({
|
||||||
|
manifest: {
|
||||||
|
permissions: ["contextMenus", "bookmarks"],
|
||||||
|
},
|
||||||
|
async background() {
|
||||||
|
const title = "Example";
|
||||||
|
let newBookmark = await browser.bookmarks.create({
|
||||||
|
title,
|
||||||
|
parentId: "toolbar_____",
|
||||||
|
});
|
||||||
|
await new Promise(resolve =>
|
||||||
|
browser.contextMenus.create({
|
||||||
|
title: "Get bookmark",
|
||||||
|
contexts: ["bookmark"],
|
||||||
|
}, resolve));
|
||||||
|
browser.contextMenus.onClicked.addListener(async (info) => {
|
||||||
|
browser.test.assertEq(newBookmark.id, info.bookmarkId, "Bookmark ID matches");
|
||||||
|
|
||||||
|
let [bookmark] = await browser.bookmarks.get(info.bookmarkId);
|
||||||
|
browser.test.assertEq(title, bookmark.title, "Bookmark title matches");
|
||||||
|
browser.test.assertFalse(info.hasOwnProperty("pageUrl"), "Context menu does not expose pageUrl");
|
||||||
|
await browser.bookmarks.remove(info.bookmarkId);
|
||||||
|
browser.test.sendMessage("test-finish");
|
||||||
|
});
|
||||||
|
browser.test.sendMessage("bookmark-created", newBookmark.id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function test_organizer_contextmenu() {
|
||||||
|
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
|
||||||
|
let library = await promiseLibrary("BookmarksToolbar");
|
||||||
|
|
||||||
|
let menu = library.document.getElementById("placesContext");
|
||||||
|
let mainTree = library.document.getElementById("placeContent");
|
||||||
|
let leftTree = library.document.getElementById("placesList");
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
[mainTree, bookmarkContextMenuExtension],
|
||||||
|
[mainTree, bookmarkFolderContextMenuExtension],
|
||||||
|
[leftTree, bookmarkFolderContextMenuExtension],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (AppConstants.DEBUG) {
|
||||||
|
// Avoid intermittent leak - bug 1520047
|
||||||
|
tests.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let [tree, makeExtension] of tests) {
|
||||||
|
let extension = makeExtension();
|
||||||
|
await extension.startup();
|
||||||
|
let bookmarkGuid = await extension.awaitMessage("bookmark-created");
|
||||||
|
|
||||||
|
tree.selectItems([bookmarkGuid]);
|
||||||
|
let shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
|
||||||
|
synthesizeClickOnSelectedTreeCell(tree, {type: "contextmenu"});
|
||||||
|
await shown;
|
||||||
|
|
||||||
|
let menuItem = menu.getElementsByAttribute("label", "Get bookmark")[0];
|
||||||
|
closeChromeContextMenu("placesContext", menuItem, library);
|
||||||
|
await extension.awaitMessage("test-finish");
|
||||||
|
await extension.unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
await promiseLibraryClosed(library);
|
||||||
|
BrowserTestUtils.removeTab(tab);
|
||||||
|
});
|
||||||
|
|
||||||
add_task(async function test_bookmark_context_requires_permission() {
|
add_task(async function test_bookmark_context_requires_permission() {
|
||||||
const bookmarksToolbar = document.getElementById("PersonalToolbar");
|
const bookmarksToolbar = document.getElementById("PersonalToolbar");
|
||||||
setToolbarVisibility(bookmarksToolbar, true);
|
setToolbarVisibility(bookmarksToolbar, true);
|
||||||
|
Loading…
Reference in New Issue
Block a user