diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index 5073afbbc2b1..0662fe14875f 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -1279,123 +1279,10 @@ var PlacesToolbarHelper = { } }, - updateManagedBookmarksContextMenu(popup) { - let hiddenContainerItems = [ - "placesContextManaged_openSeparator", - "placesContextManaged_open:newtab", - "placesContextManaged_open:newwindow", - "placesContextManaged_copy", - ]; - - if ( - popup.triggerNode.id == "managed-bookmarks" && - !popup.triggerNode.menupopup.hasAttribute("hasbeenopened") - ) { - this.populateManagedBookmarks(popup.triggerNode.menupopup); - } - let isContainer = popup.triggerNode.getAttribute("container") == "true"; - document.getElementById( - "placesContextManaged_openContainer:tabs" - ).hidden = !isContainer; - let openContainerInTabs = false; - if (isContainer) { - let menuitems = popup.triggerNode.menupopup.children; - openContainerInTabs = Array.from(menuitems).some( - menuitem => menuitem.link - ); - } - document.getElementById( - "placesContextManaged_openContainer:tabs" - ).disabled = !openContainerInTabs; - document.getElementById( - "placesContextManaged_open:newprivatewindow" - ).hidden = isContainer || PrivateBrowsingUtils.isWindowPrivate(window); - - hiddenContainerItems.forEach( - id => (document.getElementById(id).hidden = isContainer) - ); - }, - - openManagedBookmark(event, where, private = false) { - event = getRootEvent(event); - if (where) { - openUILinkIn(event.target.parentNode.triggerNode.link, where, { - triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), - private, - }); - } else { - openUILink(event.target.link, event, { - triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), - }); - } - }, - - openManagedFolder(event) { - let menuitems = event.target.parentNode.triggerNode.menupopup.children; - let items = []; - for (let i = 0; i < menuitems.length; i++) { - if (menuitems[i].link) { - let item = {}; - item.uri = menuitems[i].link; - item.isBookmark = true; - items.push(item); - } - } - PlacesUIUtils.openTabset(items, event, window); - }, - - copyManagedBookmark(event) { - // This is a little hacky, but there is a lot of code in Places that handles - // clipboard stuff, so it's easier to reuse. - let node = {}; - node.type = 0; - node.title = event.target.parentNode.triggerNode.label; - node.uri = event.target.parentNode.triggerNode.link; - // Copied from _populateClipboard in controller.js - - // This order is _important_! It controls how this and other applications - // select data to be inserted based on type. - let contents = [ - { type: PlacesUtils.TYPE_X_MOZ_URL, entries: [] }, - { type: PlacesUtils.TYPE_HTML, entries: [] }, - { type: PlacesUtils.TYPE_UNICODE, entries: [] }, - ]; - - contents.forEach(function(content) { - content.entries.push(PlacesUtils.wrapNode(node, content.type)); + openManagedBookmark(event) { + openUILink(event.target.link, event, { + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }); - - function addData(type, data) { - xferable.addDataFlavor(type); - xferable.setTransferData( - type, - PlacesUtils.toISupportsString(data), - data.length * 2 - ); - } - - let xferable = Cc["@mozilla.org/widget/transferable;1"].createInstance( - Ci.nsITransferable - ); - xferable.init(null); - let hasData = false; - // This order matters here! It controls how this and other applications - // select data to be inserted based on type. - contents.forEach(function(content) { - if (content.entries.length) { - hasData = true; - let glue = PlacesUtils.endl; - addData(content.type, content.entries.join(glue)); - } - }); - - if (hasData) { - Services.clipboard.setData( - xferable, - null, - Ci.nsIClipboard.kGlobalClipboard - ); - } }, onDragStartManaged(event) { diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index e2723c51258e..c45991c6e390 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -2098,9 +2098,11 @@ var gBrowserInit = { child => !("toplevel_name" in child) ); if (children.length) { - let managedBookmarksButton = document.getElementById( - "managed-bookmarks" + let managedBookmarksButton = document.createXULElement( + "toolbarbutton" ); + managedBookmarksButton.setAttribute("id", "managed-bookmarks"); + managedBookmarksButton.setAttribute("class", "bookmark-item"); let toplevel = managedBookmarks.find( element => "toplevel_name" in element ); @@ -2109,9 +2111,48 @@ var gBrowserInit = { "label", toplevel.toplevel_name ); - managedBookmarksButton.removeAttribute("data-l10n-id"); + } else { + managedBookmarksButton.setAttribute( + "data-l10n-id", + "managed-bookmarks" + ); } - managedBookmarksButton.hidden = false; + managedBookmarksButton.setAttribute("context", "placesContext"); + managedBookmarksButton.setAttribute("container", "true"); + managedBookmarksButton.setAttribute("removable", "false"); + managedBookmarksButton.setAttribute("type", "menu"); + + let managedBookmarksPopup = document.createXULElement("menupopup"); + managedBookmarksPopup.setAttribute("id", "managed-bookmarks-popup"); + managedBookmarksPopup.setAttribute( + "oncommand", + "PlacesToolbarHelper.openManagedBookmark(event);" + ); + managedBookmarksPopup.setAttribute( + "onclick", + "checkForMiddleClick(this, event);" + ); + managedBookmarksPopup.setAttribute( + "ondragover", + "event.dataTransfer.effectAllowed='none';" + ); + managedBookmarksPopup.setAttribute( + "ondragstart", + "PlacesToolbarHelper.onDragStartManaged(event);" + ); + managedBookmarksPopup.setAttribute( + "onpopupshowing", + "PlacesToolbarHelper.populateManagedBookmarks(this);" + ); + managedBookmarksPopup.setAttribute("placespopup", "true"); + managedBookmarksPopup.setAttribute("is", "places-popup"); + managedBookmarksButton.appendChild(managedBookmarksPopup); + + gNavToolbox.palette.appendChild(managedBookmarksButton); + CustomizableUI.ensureWidgetPlacedInWindow( + "managed-bookmarks", + window + ); } } } diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml index 0206335f4c59..a5e936777e7c 100644 --- a/browser/base/content/browser.xhtml +++ b/browser/base/content/browser.xhtml @@ -2051,22 +2051,6 @@ customizable="true"> - - { SimpleTest.waitForClipboard( diff --git a/browser/components/places/PlacesUIUtils.jsm b/browser/components/places/PlacesUIUtils.jsm index 7ea995cb2f44..b1d52cc131d0 100644 --- a/browser/components/places/PlacesUIUtils.jsm +++ b/browser/components/places/PlacesUIUtils.jsm @@ -454,6 +454,10 @@ var PlacesUIUtils = { return null; } if (popupNode) { + let isManaged = !!popupNode.closest("#managed-bookmarks"); + if (isManaged) { + return this.managedBookmarksController; + } let view = this.getViewForNode(popupNode); if (view && view._contextMenuShown) { return view.controllers.getControllerForCommand(command); @@ -1213,6 +1217,196 @@ var PlacesUIUtils = { this.maybeToggleBookmarkToolbarVisibility(true); } }, + + async managedPlacesContextShowing(event) { + let menupopup = event.target; + let document = menupopup.ownerDocument; + let window = menupopup.ownerGlobal; + // We need to populate the submenus in order to have information + // to show the context menu. + if ( + menupopup.triggerNode.id == "managed-bookmarks" && + !menupopup.triggerNode.menupopup.hasAttribute("hasbeenopened") + ) { + await window.PlacesToolbarHelper.populateManagedBookmarks( + menupopup.triggerNode.menupopup + ); + } + let linkItems = [ + "placesContext_open:newtab", + "placesContext_open:newwindow", + "placesContext_open:newprivatewindow", + "placesContext_openSeparator", + "placesContext_copy", + ]; + Array.from(menupopup.children).forEach(function(child) { + if (!(child.id in linkItems)) { + child.hidden = true; + } + }); + // Store triggerNode in controller for checking if commands are enabled + this.managedBookmarksController.triggerNode = menupopup.triggerNode; + // Container in this context means a folder. + let isFolder = menupopup.triggerNode.hasAttribute("container"); + let openContainerInTabs_menuitem = document.getElementById( + "placesContext_openContainer:tabs" + ); + if (isFolder) { + // Disable the openContainerInTabs menuitem if there + // are no children of the menu that have links. + let menuitems = menupopup.triggerNode.menupopup.children; + let openContainerInTabs = Array.from(menuitems).some( + menuitem => menuitem.link + ); + openContainerInTabs_menuitem.disabled = !openContainerInTabs; + } else { + document.getElementById( + "placesContext_open:newprivatewindow" + ).hidden = PrivateBrowsingUtils.isWindowPrivate(window); + } + openContainerInTabs_menuitem.hidden = !isFolder; + linkItems.forEach(id => (document.getElementById(id).hidden = isFolder)); + + event.target.ownerGlobal.updateCommands("places"); + }, + + placesContextShowing(event) { + let menupopup = event.target; + let isManaged = !!menupopup.triggerNode.closest("#managed-bookmarks"); + if (isManaged) { + this.managedPlacesContextShowing(event); + return true; + } + let document = menupopup.ownerDocument; + menupopup._view = this.getViewForNode(document.popupNode); + if (!this.openInTabClosesMenu) { + document + .getElementById("placesContext_open:newtab") + .setAttribute("closemenu", "single"); + } + return menupopup._view.buildContextMenu(menupopup); + }, + + placesContextHiding(event) { + let menupopup = event.target; + if (menupopup._view) { + menupopup._view.destroyContextMenu(); + } + }, + + openSelectionInTabs(event) { + let isManaged = !!event.target.parentNode.triggerNode.closest( + "#managed-bookmarks" + ); + let controller; + if (isManaged) { + controller = this.managedBookmarksController; + } else { + let document = event.target.ownerDocument; + controller = PlacesUIUtils.getViewForNode(document.popupNode).controller; + } + controller.openSelectionInTabs(event); + }, + + managedBookmarksController: { + triggerNode: null, + + openSelectionInTabs(event) { + let window = event.target.ownerGlobal; + let menuitems = event.target.parentNode.triggerNode.menupopup.children; + let items = []; + for (let i = 0; i < menuitems.length; i++) { + if (menuitems[i].link) { + let item = {}; + item.uri = menuitems[i].link; + item.isBookmark = true; + items.push(item); + } + } + PlacesUIUtils.openTabset(items, event, window); + }, + + isCommandEnabled(command) { + switch (command) { + case "placesCmd_copy": + case "placesCmd_open:window": + case "placesCmd_open:privatewindow": + case "placesCmd_open:tab": { + return true; + } + } + return false; + }, + + doCommand(command) { + let window = this.triggerNode.ownerGlobal; + switch (command) { + case "placesCmd_copy": + // This is a little hacky, but there is a lot of code in Places that handles + // clipboard stuff, so it's easier to reuse. + let node = {}; + node.type = 0; + node.title = this.triggerNode.label; + node.uri = this.triggerNode.link; + + // Copied from _populateClipboard in controller.js + + // This order is _important_! It controls how this and other applications + // select data to be inserted based on type. + let contents = [ + { type: PlacesUtils.TYPE_X_MOZ_URL, entries: [] }, + { type: PlacesUtils.TYPE_HTML, entries: [] }, + { type: PlacesUtils.TYPE_UNICODE, entries: [] }, + ]; + + contents.forEach(function(content) { + content.entries.push(PlacesUtils.wrapNode(node, content.type)); + }); + + let xferable = Cc[ + "@mozilla.org/widget/transferable;1" + ].createInstance(Ci.nsITransferable); + xferable.init(null); + + function addData(type, data) { + xferable.addDataFlavor(type); + xferable.setTransferData( + type, + PlacesUtils.toISupportsString(data), + data.length * 2 + ); + } + + contents.forEach(function(content) { + addData(content.type, content.entries.join(PlacesUtils.endl)); + }); + + Services.clipboard.setData( + xferable, + null, + Ci.nsIClipboard.kGlobalClipboard + ); + break; + case "placesCmd_open:privatewindow": + window.openUILinkIn(this.triggerNode.link, "window", { + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + private: true, + }); + break; + case "placesCmd_open:window": + window.openUILinkIn(this.triggerNode.link, "window", { + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + private: false, + }); + break; + case "placesCmd_open:tab": { + window.openUILinkIn(this.triggerNode.link, "tab", { + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + }); + } + } + }, + }, }; // These are lazy getters to avoid importing PlacesUtils immediately. diff --git a/browser/components/places/content/placesContextMenu.inc.xhtml b/browser/components/places/content/placesContextMenu.inc.xhtml index 5ad549016fe0..d5d174e1fcbd 100644 --- a/browser/components/places/content/placesContextMenu.inc.xhtml +++ b/browser/components/places/content/placesContextMenu.inc.xhtml @@ -3,13 +3,8 @@ # You can obtain one at http://mozilla.org/MPL/2.0/. + onpopupshowing="return PlacesUIUtils.placesContextShowing(event);" + onpopuphiding="PlacesUIUtils.placesContextHiding(event);"> - - - - - - - - - diff --git a/browser/modules/test/browser/browser_UsageTelemetry_toolbars.js b/browser/modules/test/browser/browser_UsageTelemetry_toolbars.js index 2038afdf4159..ca47e0805ad9 100644 --- a/browser/modules/test/browser/browser_UsageTelemetry_toolbars.js +++ b/browser/modules/test/browser/browser_UsageTelemetry_toolbars.js @@ -178,8 +178,6 @@ add_task(async function widgetPositions() { "forward-button_pinned_nav-bar-start", "back-button_pinned_nav-bar-start", - - "managed-bookmarks_pinned_bookmarks-bar", ]); organizeToolbars({ @@ -230,8 +228,6 @@ add_task(async function widgetPositions() { "fxa-toolbar-menu-button_pinned_bookmarks-bar", "new-tab-button_pinned_bookmarks-bar", "developer-button_pinned_bookmarks-bar", - - "managed-bookmarks_pinned_bookmarks-bar", ]); CustomizableUI.reset(); @@ -273,8 +269,6 @@ add_task(async function customizeMode() { "library-button_pinned_nav-bar-end", "personal-bookmarks_pinned_bookmarks-bar", - - "managed-bookmarks_pinned_bookmarks-bar", ]); let win = await BrowserTestUtils.openNewBrowserWindow(); @@ -365,8 +359,6 @@ add_task(async function contextMenus() { "library-button_pinned_nav-bar-end", "personal-bookmarks_pinned_bookmarks-bar", - - "managed-bookmarks_pinned_bookmarks-bar", ]); let menu = document.getElementById("toolbar-context-menu"); @@ -450,8 +442,6 @@ add_task(async function pageActions() { "pinTab_pinned_pageaction-urlbar", "personal-bookmarks_pinned_bookmarks-bar", - - "managed-bookmarks_pinned_bookmarks-bar", ]); let panel = document.getElementById("pageActionPanel"); @@ -554,8 +544,6 @@ add_task(async function extensions() { "random-addon-example-com_pinned_nav-bar-end", "random-addon-example-com_pinned_pageaction-urlbar", - - "managed-bookmarks_pinned_bookmarks-bar", ]); let addon = await AddonManager.getAddonByID(extension.id); @@ -579,8 +567,6 @@ add_task(async function extensions() { "forward-button_pinned_nav-bar-start", "back-button_pinned_nav-bar-start", - - "managed-bookmarks_pinned_bookmarks-bar", ]); await addon.enable(); @@ -607,8 +593,6 @@ add_task(async function extensions() { "random-addon-example-com_pinned_nav-bar-end", "random-addon-example-com_pinned_pageaction-urlbar", - - "managed-bookmarks_pinned_bookmarks-bar", ]); await addon.reload(); @@ -653,8 +637,6 @@ add_task(async function extensions() { "forward-button_pinned_nav-bar-start", "back-button_pinned_nav-bar-start", - - "managed-bookmarks_pinned_bookmarks-bar", ]); }); });