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:
Peter Simonyi 2019-01-15 14:05:44 +00:00
parent 7005fd08d0
commit beef78d6c8
2 changed files with 157 additions and 1 deletions

View File

@ -4,6 +4,9 @@
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "Bookmarks",
"resource://gre/modules/Bookmarks.jsm");
var {
DefaultMap,
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
// for menu events from both Tools and context menus, both content and chrome.
const menuTracker = {
@ -874,6 +943,7 @@ const menuTracker = {
this.onWindowOpen(window);
}
windowTracker.addOpenListener(this.onWindowOpen);
libraryTracker.init(this.onLibraryOpen);
},
unregister() {
@ -882,6 +952,7 @@ const menuTracker = {
this.cleanupWindow(window);
}
windowTracker.removeOpenListener(this.onWindowOpen);
libraryTracker.uninit(this.cleanupLibrary);
},
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) {
const menu = event.target;
@ -963,6 +1044,10 @@ const menuTracker = {
const cell = tree.boxObject.getCellAt(event.x, event.y);
const node = tree.view.nodeForTreeIndex(cell.row);
if (!node.bookmarkGuid || Bookmarks.isVirtualRootItem(node.bookmarkGuid)) {
return;
}
gMenuBuilder.build({
menu,
bookmarkId: node.bookmarkGuid,

View File

@ -5,7 +5,9 @@
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/browser/components/places/tests/browser/head.js",
this);
/* globals withSidebarTree, synthesizeClickOnSelectedTreeCell */
/* globals withSidebarTree, synthesizeClickOnSelectedTreeCell,
* promiseLibrary, promiseLibraryClosed
*/
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() {
const bookmarksToolbar = document.getElementById("PersonalToolbar");
setToolbarVisibility(bookmarksToolbar, true);