Hide and disable open_all/cut/copy/delete/properties when opening

bookmarks/history context menu with no selection

This resolves #882 (by not offering options that can't be used)
This commit is contained in:
wolfbeast 2019-07-28 21:24:32 +02:00 • committed by Roy Tam
commit aa1fc0720e
6 changed files with 68 additions and 56 deletions

View file

@ -109,8 +109,15 @@ PlacesViewBase.prototype = {
get selectedNode() { get selectedNode() {
if (this._contextMenuShown) { if (this._contextMenuShown) {
let popup = document.popupNode; let anchor = this._contextMenuShown.triggerNode;
return popup._placesNode || popup.parentNode._placesNode || null; if (!anchor)
return null;
if (anchor._placesNode)
return this._rootElt == anchor ? null : anchor._placesNode;
anchor = anchor.parentNode;
return this._rootElt == anchor ? null : (anchor._placesNode || null);
} }
return null; return null;
}, },
@ -176,13 +183,13 @@ PlacesViewBase.prototype = {
}, },
buildContextMenu: function PVB_buildContextMenu(aPopup) { buildContextMenu: function PVB_buildContextMenu(aPopup) {
this._contextMenuShown = true; this._contextMenuShown = aPopup;
window.updateCommands("places"); window.updateCommands("places");
return this.controller.buildContextMenu(aPopup); return this.controller.buildContextMenu(aPopup);
}, },
destroyContextMenu: function PVB_destroyContextMenu(aPopup) { destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
this._contextMenuShown = false; this._contextMenuShown = null;
}, },
_cleanPopup: function PVB_cleanPopup(aPopup, aDelay) { _cleanPopup: function PVB_cleanPopup(aPopup, aDelay) {

View file

@ -333,20 +333,6 @@ PlacesController.prototype = {
return ip != null && (isPaste || ip.isTag != true); return ip != null && (isPaste || ip.isTag != true);
}, },
/**
* Determines whether or not the root node for the view is selected
*/
rootNodeIsSelected: function PC_rootNodeIsSelected() {
var nodes = this._view.selectedNodes;
var root = this._view.result.root;
for (var i = 0; i < nodes.length; ++i) {
if (nodes[i] == root)
return true;
}
return false;
},
/** /**
* Looks at the data on the clipboard to see if it is paste-able. * Looks at the data on the clipboard to see if it is paste-able.
* Paste-able data is: * Paste-able data is:
@ -400,7 +386,7 @@ PlacesController.prototype = {
* Gathers information about the selected nodes according to the following * Gathers information about the selected nodes according to the following
* rules: * rules:
* "link" node is a URI * "link" node is a URI
* "bookmark" node is a bookamrk * "bookmark" node is a bookmark
* "livemarkChild" node is a child of a livemark * "livemarkChild" node is a child of a livemark
* "tagChild" node is a child of a tag * "tagChild" node is a child of a tag
* "folder" node is a folder * "folder" node is a folder
@ -414,15 +400,10 @@ PlacesController.prototype = {
* node are set on its corresponding object as properties. * node are set on its corresponding object as properties.
* Notes: * Notes:
* 1) This can be slow, so don't call it anywhere performance critical! * 1) This can be slow, so don't call it anywhere performance critical!
* 2) A single-object array corresponding the root node is returned if
* there's no selection.
*/ */
_buildSelectionMetadata: function PC__buildSelectionMetadata() { _buildSelectionMetadata: function PC__buildSelectionMetadata() {
var metadata = []; var metadata = [];
var root = this._view.result.root;
var nodes = this._view.selectedNodes; var nodes = this._view.selectedNodes;
if (nodes.length == 0)
nodes.push(root); // See the second note above
for (var i = 0; i < nodes.length; i++) { for (var i = 0; i < nodes.length; i++) {
var nodeData = {}; var nodeData = {};
@ -501,10 +482,23 @@ PlacesController.prototype = {
*/ */
_shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) { _shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) {
var selectiontype = aMenuItem.getAttribute("selectiontype"); var selectiontype = aMenuItem.getAttribute("selectiontype");
if (selectiontype == "multiple" && aMetaData.length == 1) if (!selectiontype) {
selectiontype = "single|multiple";
}
var selectionTypes = selectiontype.split("|");
if (selectionTypes.indexOf("any") != -1) {
return true;
}
var count = aMetaData.length;
if (count > 1 && selectionTypes.indexOf("multiple") == -1)
return false; return false;
if (selectiontype == "single" && aMetaData.length != 1) if (count == 1 && selectionTypes.indexOf("single") == -1)
return false; return false;
// NB: if there is no selection, we show the item if (and only if)
// the selectiontype includes 'none' - the metadata list will be
// empty so none of the other criteria will apply anyway.
if (count == 0)
return selectionTypes.indexOf("none") != -1;
var forceHideAttr = aMenuItem.getAttribute("forcehideselection"); var forceHideAttr = aMenuItem.getAttribute("forcehideselection");
if (forceHideAttr) { if (forceHideAttr) {
@ -551,9 +545,11 @@ PlacesController.prototype = {
* 1) The "selectiontype" attribute may be set on a menu-item to "single" * 1) The "selectiontype" attribute may be set on a menu-item to "single"
* if the menu-item should be visible only if there is a single node * if the menu-item should be visible only if there is a single node
* selected, or to "multiple" if the menu-item should be visible only if * selected, or to "multiple" if the menu-item should be visible only if
* multiple nodes are selected. If the attribute is not set or if it is * multiple nodes are selected, or to "none" if the menuitems should be
* set to an invalid value, the menu-item may be visible for both types of * visible for if there are no selected nodes, or to a |-separated
* selection. * combination of these.
* If the attribute is not set or set to an invalid value, the menu-item
* may be visible irrespective of the selection.
* 2) The "selection" attribute may be set on a menu-item to the various * 2) The "selection" attribute may be set on a menu-item to the various
* meta-data rules for which it may be visible. The rules should be * meta-data rules for which it may be visible. The rules should be
* separated with the | character. * separated with the | character.
@ -584,7 +580,7 @@ PlacesController.prototype = {
var separator = null; var separator = null;
var visibleItemsBeforeSep = false; var visibleItemsBeforeSep = false;
var anyVisible = false; var usableItemCount = 0;
for (var i = 0; i < aPopup.childNodes.length; ++i) { for (var i = 0; i < aPopup.childNodes.length; ++i) {
var item = aPopup.childNodes[i]; var item = aPopup.childNodes[i];
if (item.localName != "menuseparator") { if (item.localName != "menuseparator") {
@ -598,12 +594,13 @@ PlacesController.prototype = {
(!/tree/i.test(this._view.localName) || ip); (!/tree/i.test(this._view.localName) || ip);
var hideIfPrivate = item.getAttribute("hideifprivatebrowsing") == "true" && var hideIfPrivate = item.getAttribute("hideifprivatebrowsing") == "true" &&
PrivateBrowsingUtils.isWindowPrivate(window); PrivateBrowsingUtils.isWindowPrivate(window);
item.hidden = hideIfNoIP || hideIfPrivate || hideParentFolderItem || var shouldHideItem = hideIfNoIP || hideIfPrivate || hideParentFolderItem ||
!this._shouldShowMenuItem(item, metadata); !this._shouldShowMenuItem(item, metadata);
item.hidden = item.disabled = shouldHideItem;
if (!item.hidden) { if (!item.hidden) {
visibleItemsBeforeSep = true; visibleItemsBeforeSep = true;
anyVisible = true; usableItemCount++;
// Show the separator above the menu-item if any // Show the separator above the menu-item if any
if (separator) { if (separator) {
@ -627,21 +624,21 @@ PlacesController.prototype = {
} }
// Set Open Folder/Links In Tabs items enabled state if they're visible // Set Open Folder/Links In Tabs items enabled state if they're visible
if (anyVisible) { if (usableItemCount > 0) {
var openContainerInTabsItem = document.getElementById("placesContext_openContainer:tabs"); var openContainerInTabsItem = document.getElementById("placesContext_openContainer:tabs");
if (!openContainerInTabsItem.hidden && this._view.selectedNode && if (!openContainerInTabsItem.hidden) {
PlacesUtils.nodeIsContainer(this._view.selectedNode)) { var containerToUse = this._view.selectedNode || this._view.result.root;
openContainerInTabsItem.disabled = if (PlacesUtils.nodeIsContainer(containerToUse)) {
!PlacesUtils.hasChildURIs(this._view.selectedNode); if (!PlacesUtils.hasChildURIs(containerToUse, true)) {
} openContainerInTabsItem.disabled = true;
else { // Ensure that we don't display the menu if nothing is enabled:
// see selectiontype rule in the overlay usableItemCount--;
var openLinksInTabsItem = document.getElementById("placesContext_openLinks:tabs"); }
openLinksInTabsItem.disabled = openLinksInTabsItem.hidden; }
} }
} }
return anyVisible; return usableItemCount > 0;
}, },
/** /**
@ -707,10 +704,15 @@ PlacesController.prototype = {
*/ */
openSelectionInTabs: function PC_openLinksInTabs(aEvent) { openSelectionInTabs: function PC_openLinksInTabs(aEvent) {
var node = this._view.selectedNode; var node = this._view.selectedNode;
var nodes = this._view.selectedNodes;
// In the case of no selection, open the root node:
if (!node && !nodes.length) {
node = this._view.result.root;
}
if (node && PlacesUtils.nodeIsContainer(node)) if (node && PlacesUtils.nodeIsContainer(node))
PlacesUIUtils.openContainerNodeInTabs(this._view.selectedNode, aEvent, this._view); PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this._view);
else else
PlacesUIUtils.openURINodesInTabs(this._view.selectedNodes, aEvent, this._view); PlacesUIUtils.openURINodesInTabs(nodes, aEvent, this._view);
}, },
/** /**

View file

@ -149,20 +149,20 @@
command="placesCmd_new:bookmark" command="placesCmd_new:bookmark"
label="&cmd.new_bookmark.label;" label="&cmd.new_bookmark.label;"
accesskey="&cmd.new_bookmark.accesskey;" accesskey="&cmd.new_bookmark.accesskey;"
selection="any" selectiontype="any"
hideifnoinsertionpoint="true"/> hideifnoinsertionpoint="true"/>
<menuitem id="placesContext_new:folder" <menuitem id="placesContext_new:folder"
command="placesCmd_new:folder" command="placesCmd_new:folder"
label="&cmd.new_folder.label;" label="&cmd.new_folder.label;"
accesskey="&cmd.context_new_folder.accesskey;" accesskey="&cmd.context_new_folder.accesskey;"
selection="any" selectiontype="any"
hideifnoinsertionpoint="true"/> hideifnoinsertionpoint="true"/>
<menuitem id="placesContext_new:separator" <menuitem id="placesContext_new:separator"
command="placesCmd_new:separator" command="placesCmd_new:separator"
label="&cmd.new_separator.label;" label="&cmd.new_separator.label;"
accesskey="&cmd.new_separator.accesskey;" accesskey="&cmd.new_separator.accesskey;"
closemenu="single" closemenu="single"
selection="any" selectiontype="any"
hideifnoinsertionpoint="true"/> hideifnoinsertionpoint="true"/>
<menuseparator id="placesContext_newSeparator"/> <menuseparator id="placesContext_newSeparator"/>
<menuitem id="placesContext_createBookmark" <menuitem id="placesContext_createBookmark"
@ -182,14 +182,13 @@
command="placesCmd_copy" command="placesCmd_copy"
label="&copyCmd.label;" label="&copyCmd.label;"
closemenu="single" closemenu="single"
accesskey="&copyCmd.accesskey;" accesskey="&copyCmd.accesskey;"/>
selection="any"/>
<menuitem id="placesContext_paste" <menuitem id="placesContext_paste"
command="placesCmd_paste" command="placesCmd_paste"
label="&pasteCmd.label;" label="&pasteCmd.label;"
closemenu="single" closemenu="single"
accesskey="&pasteCmd.accesskey;" accesskey="&pasteCmd.accesskey;"
selection="any" selectiontype="any"
hideifnoinsertionpoint="true"/> hideifnoinsertionpoint="true"/>
<menuseparator id="placesContext_editSeparator"/> <menuseparator id="placesContext_editSeparator"/>
<menuitem id="placesContext_delete" <menuitem id="placesContext_delete"

View file

@ -40,7 +40,7 @@ var SidebarUtils = {
var openInTabs = isContainer && var openInTabs = isContainer &&
(aEvent.button == 1 || (aEvent.button == 1 ||
(aEvent.button == 0 && modifKey)) && (aEvent.button == 0 && modifKey)) &&
PlacesUtils.hasChildURIs(tbo.view.nodeForTreeIndex(cell.row)); PlacesUtils.hasChildURIs(tbo.view.nodeForTreeIndex(cell.row), true);
if (aEvent.button == 0 && isContainer && !openInTabs) { if (aEvent.button == 0 && isContainer && !openInTabs) {
tbo.view.toggleOpenState(cell.row); tbo.view.toggleOpenState(cell.row);

View file

@ -1341,7 +1341,7 @@ this.PlacesUtils = {
* The container node to search through. * The container node to search through.
* @returns true if the node contains uri nodes, false otherwise. * @returns true if the node contains uri nodes, false otherwise.
*/ */
hasChildURIs: function PU_hasChildURIs(aNode) { hasChildURIs: function PU_hasChildURIs(aNode, aMultiple=false) {
if (!this.nodeIsContainer(aNode)) if (!this.nodeIsContainer(aNode))
return false; return false;
@ -1357,11 +1357,14 @@ this.PlacesUtils = {
root.containerOpen = true; root.containerOpen = true;
} }
let foundFirst = !aMultiple;
let found = false; let found = false;
for (let i = 0; i < root.childCount && !found; i++) { for (let i = 0; i < root.childCount && !found; i++) {
let child = root.getChild(i); let child = root.getChild(i);
if (this.nodeIsURI(child)) if (this.nodeIsURI(child)) {
found = true; found = foundFirst;
foundFirst = true;
}
} }
if (!wasOpen) { if (!wasOpen) {

View file

@ -165,6 +165,7 @@ function check_uri_nodes(aQuery, aOptions, aExpectedURINodes) {
root.containerOpen = true; root.containerOpen = true;
var node = root.getChild(0); var node = root.getChild(0);
do_check_eq(PU.hasChildURIs(node), aExpectedURINodes > 0); do_check_eq(PU.hasChildURIs(node), aExpectedURINodes > 0);
do_check_eq(PU.hasChildURIs(node, true), aExpectedURINodes > 1);
do_check_eq(PU.getURLsForContainerNode(node).length, aExpectedURINodes); do_check_eq(PU.getURLsForContainerNode(node).length, aExpectedURINodes);
root.containerOpen = false; root.containerOpen = false;
} }