Web Inspector: tree elements with depth > 1 should have context menu "expand all"/"collapse all" commands
https://bugs.webkit.org/show_bug.cgi?id=135590
Patch by Devin Rousso <dcrousso+webkit@gmail.com> on 2017-01-23
Reviewed by Timothy Hatcher.
Rework the context menu event handlers for all TreeOutline and TreeElement instances such
that the TreeOutline handles the event listener and creates the context menu object and the
TreeElement populates the list with items. This is necessary due to the way in which
children are laid out, as there is padding on either side of the element that would not
trigger a context menu event.
* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Views/BreakpointTreeElement.js:
(WebInspector.BreakpointTreeElement.prototype.ondetach):
(WebInspector.BreakpointTreeElement.prototype.populateContextMenu):
(WebInspector.BreakpointTreeElement.prototype.oncontextmenu): Deleted.
* UserInterface/Views/ContextMenuUtilities.js:
(WebInspector.appendContextMenuItemsForSourceCode):
* UserInterface/Views/DOMTreeElement.js:
(WebInspector.DOMTreeElement.prototype._populateNodeContextMenu):
* UserInterface/Views/DOMTreeOutline.js:
(WebInspector.DOMTreeOutline):
(WebInspector.DOMTreeOutline.prototype.populateContextMenu):
(WebInspector.DOMTreeOutline.prototype._onmousedown):
(WebInspector.DOMTreeOutline.prototype._onmousemove):
(WebInspector.DOMTreeOutline.prototype._ondragstart):
(WebInspector.DOMTreeOutline.prototype._ondragover):
(WebInspector.DOMTreeOutline.prototype._ondrop):
(WebInspector.DOMTreeOutline.prototype._treeElementFromEvent): Deleted.
(WebInspector.DOMTreeOutline.prototype._contextMenuEventFired): Deleted.
* UserInterface/Views/DebuggerSidebarPanel.js:
(WebInspector.DebuggerSidebarPanel):
(WebInspector.DebuggerSidebarPanel.prototype._breakpointTreeOutlineContextMenuTreeElement):
* UserInterface/Views/FrameTreeElement.js:
(WebInspector.FrameTreeElement.prototype.onattach):
* UserInterface/Views/GeneralTreeElement.js:
(WebInspector.GeneralTreeElement.prototype.onattach):
(WebInspector.GeneralTreeElement.prototype.ondetach): Deleted.
* UserInterface/Views/ObjectTreeBaseTreeElement.js:
(WebInspector.ObjectTreeBaseTreeElement.prototype.populateContextMenu):
(WebInspector.ObjectTreeBaseTreeElement.prototype._logSymbolProperty):
(WebInspector.ObjectTreeBaseTreeElement.prototype._logValue):
(WebInspector.ObjectTreeBaseTreeElement.prototype.oncontextmenu): Deleted.
(WebInspector.ObjectTreeBaseTreeElement.prototype._contextMenuHandler): Deleted.
* UserInterface/Views/ResourceTreeElement.js:
(WebInspector.ResourceTreeElement.prototype.populateContextMenu):
(WebInspector.ResourceTreeElement.prototype.onattach): Deleted.
(WebInspector.ResourceTreeElement.prototype._handleContextMenuEvent): Deleted.
* UserInterface/Views/ThreadTreeElement.js:
(WebInspector.ThreadTreeElement.prototype.populateContextMenu):
(WebInspector.ThreadTreeElement.prototype.oncontextmenu): Deleted.
* UserInterface/Views/TreeElement.js:
(WebInspector.TreeElement.prototype.populateContextMenu):
(WebInspector.TreeElement):
* UserInterface/Views/TreeOutline.js:
(WebInspector.TreeOutline):
(WebInspector.TreeOutline.prototype.treeElementFromEvent):
(WebInspector.TreeOutline.prototype.populateContextMenu):
(WebInspector.TreeOutline._generateStyleRulesIfNeeded):
* UserInterface/Views/VisualStyleSelectorTreeItem.js:
(WebInspector.VisualStyleSelectorTreeItem.prototype.onattach):
(WebInspector.VisualStyleSelectorTreeItem.prototype.populateContextMenu):
(WebInspector.VisualStyleSelectorTreeItem.prototype._highlightNodesWithSelector):
(WebInspector.VisualStyleSelectorTreeItem.prototype._hideDOMNodeHighlight):
(WebInspector.VisualStyleSelectorTreeItem.prototype._handleContextMenuEvent): Deleted.
* UserInterface/Views/WorkerTreeElement.js:
(WebInspector.WorkerTreeElement.prototype.populateContextMenu):
(WebInspector.WorkerTreeElement.prototype.onattach):
(WebInspector.WorkerTreeElement.prototype._handleContextMenuEvent): Deleted.
* UserInterface/Views/DataGrid.js:
(WebInspector.DataGrid.prototype._contextMenuInDataTable):
Add "Expand All"/"Collapse All" context menu items.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@211061 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog
index 8fc28da..0e52718 100644
--- a/Source/WebInspectorUI/ChangeLog
+++ b/Source/WebInspectorUI/ChangeLog
@@ -1,5 +1,81 @@
2017-01-23 Devin Rousso <dcrousso+webkit@gmail.com>
+ Web Inspector: tree elements with depth > 1 should have context menu "expand all"/"collapse all" commands
+ https://bugs.webkit.org/show_bug.cgi?id=135590
+
+ Reviewed by Timothy Hatcher.
+
+ Rework the context menu event handlers for all TreeOutline and TreeElement instances such
+ that the TreeOutline handles the event listener and creates the context menu object and the
+ TreeElement populates the list with items. This is necessary due to the way in which
+ children are laid out, as there is padding on either side of the element that would not
+ trigger a context menu event.
+
+ * Localizations/en.lproj/localizedStrings.js:
+ * UserInterface/Views/BreakpointTreeElement.js:
+ (WebInspector.BreakpointTreeElement.prototype.ondetach):
+ (WebInspector.BreakpointTreeElement.prototype.populateContextMenu):
+ (WebInspector.BreakpointTreeElement.prototype.oncontextmenu): Deleted.
+ * UserInterface/Views/ContextMenuUtilities.js:
+ (WebInspector.appendContextMenuItemsForSourceCode):
+ * UserInterface/Views/DOMTreeElement.js:
+ (WebInspector.DOMTreeElement.prototype._populateNodeContextMenu):
+ * UserInterface/Views/DOMTreeOutline.js:
+ (WebInspector.DOMTreeOutline):
+ (WebInspector.DOMTreeOutline.prototype.populateContextMenu):
+ (WebInspector.DOMTreeOutline.prototype._onmousedown):
+ (WebInspector.DOMTreeOutline.prototype._onmousemove):
+ (WebInspector.DOMTreeOutline.prototype._ondragstart):
+ (WebInspector.DOMTreeOutline.prototype._ondragover):
+ (WebInspector.DOMTreeOutline.prototype._ondrop):
+ (WebInspector.DOMTreeOutline.prototype._treeElementFromEvent): Deleted.
+ (WebInspector.DOMTreeOutline.prototype._contextMenuEventFired): Deleted.
+ * UserInterface/Views/DebuggerSidebarPanel.js:
+ (WebInspector.DebuggerSidebarPanel):
+ (WebInspector.DebuggerSidebarPanel.prototype._breakpointTreeOutlineContextMenuTreeElement):
+ * UserInterface/Views/FrameTreeElement.js:
+ (WebInspector.FrameTreeElement.prototype.onattach):
+ * UserInterface/Views/GeneralTreeElement.js:
+ (WebInspector.GeneralTreeElement.prototype.onattach):
+ (WebInspector.GeneralTreeElement.prototype.ondetach): Deleted.
+ * UserInterface/Views/ObjectTreeBaseTreeElement.js:
+ (WebInspector.ObjectTreeBaseTreeElement.prototype.populateContextMenu):
+ (WebInspector.ObjectTreeBaseTreeElement.prototype._logSymbolProperty):
+ (WebInspector.ObjectTreeBaseTreeElement.prototype._logValue):
+ (WebInspector.ObjectTreeBaseTreeElement.prototype.oncontextmenu): Deleted.
+ (WebInspector.ObjectTreeBaseTreeElement.prototype._contextMenuHandler): Deleted.
+ * UserInterface/Views/ResourceTreeElement.js:
+ (WebInspector.ResourceTreeElement.prototype.populateContextMenu):
+ (WebInspector.ResourceTreeElement.prototype.onattach): Deleted.
+ (WebInspector.ResourceTreeElement.prototype._handleContextMenuEvent): Deleted.
+ * UserInterface/Views/ThreadTreeElement.js:
+ (WebInspector.ThreadTreeElement.prototype.populateContextMenu):
+ (WebInspector.ThreadTreeElement.prototype.oncontextmenu): Deleted.
+ * UserInterface/Views/TreeElement.js:
+ (WebInspector.TreeElement.prototype.populateContextMenu):
+ (WebInspector.TreeElement):
+ * UserInterface/Views/TreeOutline.js:
+ (WebInspector.TreeOutline):
+ (WebInspector.TreeOutline.prototype.treeElementFromEvent):
+ (WebInspector.TreeOutline.prototype.populateContextMenu):
+ (WebInspector.TreeOutline._generateStyleRulesIfNeeded):
+ * UserInterface/Views/VisualStyleSelectorTreeItem.js:
+ (WebInspector.VisualStyleSelectorTreeItem.prototype.onattach):
+ (WebInspector.VisualStyleSelectorTreeItem.prototype.populateContextMenu):
+ (WebInspector.VisualStyleSelectorTreeItem.prototype._highlightNodesWithSelector):
+ (WebInspector.VisualStyleSelectorTreeItem.prototype._hideDOMNodeHighlight):
+ (WebInspector.VisualStyleSelectorTreeItem.prototype._handleContextMenuEvent): Deleted.
+ * UserInterface/Views/WorkerTreeElement.js:
+ (WebInspector.WorkerTreeElement.prototype.populateContextMenu):
+ (WebInspector.WorkerTreeElement.prototype.onattach):
+ (WebInspector.WorkerTreeElement.prototype._handleContextMenuEvent): Deleted.
+
+ * UserInterface/Views/DataGrid.js:
+ (WebInspector.DataGrid.prototype._contextMenuInDataTable):
+ Add "Expand All"/"Collapse All" context menu items.
+
+2017-01-23 Devin Rousso <dcrousso+webkit@gmail.com>
+
Web Inspector: color picker should feature an editable CSS value
https://bugs.webkit.org/show_bug.cgi?id=124356
diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
index 1855436..cb62bf8 100644
--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
@@ -178,6 +178,7 @@
localizedStrings["Closure Variables"] = "Closure Variables";
localizedStrings["Closure Variables (%s)"] = "Closure Variables (%s)";
localizedStrings["Code"] = "Code";
+localizedStrings["Collapse All"] = "Collapse All";
localizedStrings["Collapse columns"] = "Collapse columns";
localizedStrings["Color"] = "Color";
localizedStrings["Comment"] = "Comment";
@@ -334,6 +335,7 @@
localizedStrings["Event Listeners"] = "Event Listeners";
localizedStrings["Events"] = "Events";
localizedStrings["Exception with thrown value: %s"] = "Exception with thrown value: %s";
+localizedStrings["Expand All"] = "Expand All";
localizedStrings["Expand columns"] = "Expand columns";
localizedStrings["Expanded"] = "Expanded";
localizedStrings["Expires"] = "Expires";
diff --git a/Source/WebInspectorUI/UserInterface/Views/BreakpointTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/BreakpointTreeElement.js
index 154e53a..b73ddd3 100644
--- a/Source/WebInspectorUI/UserInterface/Views/BreakpointTreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/BreakpointTreeElement.js
@@ -101,12 +101,6 @@
return true;
}
- oncontextmenu(event)
- {
- let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
- WebInspector.breakpointPopoverController.appendContextMenuItems(contextMenu, this._breakpoint, this._statusImageElement);
- }
-
onattach()
{
super.onattach();
@@ -120,14 +114,19 @@
ondetach()
{
- super.ondetach();
-
this._listenerSet.uninstall();
if (this._probeSet)
this._removeProbeSet(this._probeSet);
}
+ populateContextMenu(contextMenu, event)
+ {
+ WebInspector.breakpointPopoverController.appendContextMenuItems(contextMenu, this._breakpoint, this._statusImageElement);
+
+ super.populateContextMenu(contextMenu, event);
+ }
+
removeStatusImage()
{
this._statusImageElement.remove();
diff --git a/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js b/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js
index 4d2b51a..ef6100a 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js
@@ -33,6 +33,8 @@
if (!(sourceCode instanceof WebInspector.SourceCode))
return;
+ contextMenu.appendSeparator();
+
if (sourceCode.url) {
contextMenu.appendItem(WebInspector.UIString("Open in New Tab"), () => {
const frame = null;
diff --git a/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js b/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js
index 0677d66..33c3fbc 100644
--- a/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js
+++ b/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js
@@ -55,8 +55,6 @@
this._editing = false;
this._visible = false;
- this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this));
-
this._hideElementKeyboardShortcut = new WebInspector.KeyboardShortcut(null, "H", this._hideElement.bind(this), this.element);
this._hideElementKeyboardShortcut.implicitlyPreventsDefault = false;
@@ -245,25 +243,21 @@
let commentNode = event.target.enclosingNodeOrSelfWithClass("html-comment");
let pseudoElement = event.target.enclosingNodeOrSelfWithClass("html-pseudo-element");
- let populated = false;
if (tag && treeElement._populateTagContextMenu) {
- if (populated)
- contextMenu.appendSeparator();
+ contextMenu.appendSeparator();
+
treeElement._populateTagContextMenu(contextMenu, event);
- populated = true;
} else if (textNode && treeElement._populateTextContextMenu) {
- if (populated)
- contextMenu.appendSeparator();
+ contextMenu.appendSeparator();
+
treeElement._populateTextContextMenu(contextMenu, textNode);
- populated = true;
} else if ((commentNode || pseudoElement) && treeElement._populateNodeContextMenu) {
- if (populated)
- contextMenu.appendSeparator();
+ contextMenu.appendSeparator();
+
treeElement._populateNodeContextMenu(contextMenu);
- populated = true;
}
- return populated;
+ super.populateContextMenu(contextMenu, event, treeElement);
}
adjustCollapsedRange()
@@ -291,35 +285,9 @@
treeElement.revealAndSelect(omitFocus);
}
- _treeElementFromEvent(event)
- {
- var scrollContainer = this.element.parentElement;
-
- // We choose this X coordinate based on the knowledge that our list
- // items extend at least to the right edge of the outer <ol> container.
- // In the no-word-wrap mode the outer <ol> may be wider than the tree container
- // (and partially hidden), in which case we are left to use only its right boundary.
- var x = scrollContainer.totalOffsetLeft + scrollContainer.offsetWidth - 36;
-
- var y = event.pageY;
-
- // Our list items have 1-pixel cracks between them vertically. We avoid
- // the cracks by checking slightly above and slightly below the mouse
- // and seeing if we hit the same element each time.
- var elementUnderMouse = this.treeElementFromPoint(x, y);
- var elementAboveMouse = this.treeElementFromPoint(x, y - 2);
- var element;
- if (elementUnderMouse === elementAboveMouse)
- element = elementUnderMouse;
- else
- element = this.treeElementFromPoint(x, y + 2);
-
- return element;
- }
-
_onmousedown(event)
{
- var element = this._treeElementFromEvent(event);
+ let element = this.treeElementFromEvent(event);
if (!element || element.isEventWithinDisclosureTriangle(event)) {
event.preventDefault();
return;
@@ -330,7 +298,7 @@
_onmousemove(event)
{
- var element = this._treeElementFromEvent(event);
+ let element = this.treeElementFromEvent(event);
if (element && this._previousHoveredElement === element)
return;
@@ -367,7 +335,7 @@
_ondragstart(event)
{
- var treeElement = this._treeElementFromEvent(event);
+ let treeElement = this.treeElementFromEvent(event);
if (!treeElement)
return false;
@@ -397,7 +365,7 @@
if (!this._nodeBeingDragged)
return false;
- let treeElement = this._treeElementFromEvent(event);
+ let treeElement = this.treeElementFromEvent(event);
if (!this._isValidDragSourceOrTarget(treeElement))
return false;
@@ -454,7 +422,7 @@
this.selectDOMNode(newNode, true);
}
- let treeElement = this._treeElementFromEvent(event);
+ let treeElement = this.treeElementFromEvent(event);
if (this._nodeBeingDragged && treeElement) {
let parentNode = null;
let anchorNode = null;
@@ -496,16 +464,6 @@
}
}
- _contextMenuEventFired(event)
- {
- let treeElement = this._treeElementFromEvent(event);
- if (!treeElement)
- return;
-
- let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
- this.populateContextMenu(contextMenu, event, treeElement);
- }
-
_updateModifiedNodes()
{
if (this._elementsTreeUpdater)
diff --git a/Source/WebInspectorUI/UserInterface/Views/DataGrid.js b/Source/WebInspectorUI/UserInterface/Views/DataGrid.js
index 90fd869..4e8359f 100644
--- a/Source/WebInspectorUI/UserInterface/Views/DataGrid.js
+++ b/Source/WebInspectorUI/UserInterface/Views/DataGrid.js
@@ -1646,23 +1646,32 @@
if (this.dataGrid._refreshCallback && (!gridNode || gridNode !== this.placeholderNode))
contextMenu.appendItem(WebInspector.UIString("Refresh"), this._refreshCallback.bind(this));
- if (gridNode && gridNode.selectable && gridNode.copyable && !gridNode.isEventWithinDisclosureTriangle(event)) {
- contextMenu.appendItem(WebInspector.UIString("Copy Row"), this._copyRow.bind(this, event.target));
- contextMenu.appendItem(WebInspector.UIString("Copy Table"), this._copyTable.bind(this));
+ if (gridNode) {
+ if (gridNode.selectable && gridNode.copyable && !gridNode.isEventWithinDisclosureTriangle(event)) {
+ contextMenu.appendItem(WebInspector.UIString("Copy Row"), this._copyRow.bind(this, event.target));
+ contextMenu.appendItem(WebInspector.UIString("Copy Table"), this._copyTable.bind(this));
- if (this.dataGrid._editCallback) {
- if (gridNode === this.placeholderNode)
- contextMenu.appendItem(WebInspector.UIString("Add New"), this._startEditing.bind(this, event.target));
- else {
- let element = event.target.enclosingNodeOrSelfWithNodeName("td");
- let columnIdentifier = element.__columnIdentifier;
- let columnTitle = this.dataGrid.columns.get(columnIdentifier)["title"];
- contextMenu.appendItem(WebInspector.UIString("Edit “%s”").format(columnTitle), this._startEditing.bind(this, event.target));
+ if (this.dataGrid._editCallback) {
+ if (gridNode === this.placeholderNode)
+ contextMenu.appendItem(WebInspector.UIString("Add New"), this._startEditing.bind(this, event.target));
+ else {
+ let element = event.target.enclosingNodeOrSelfWithNodeName("td");
+ let columnIdentifier = element.__columnIdentifier;
+ let columnTitle = this.dataGrid.columns.get(columnIdentifier)["title"];
+ contextMenu.appendItem(WebInspector.UIString("Edit “%s”").format(columnTitle), this._startEditing.bind(this, event.target));
+ }
}
+
+ if (this.dataGrid._deleteCallback && gridNode !== this.placeholderNode)
+ contextMenu.appendItem(WebInspector.UIString("Delete"), this._deleteCallback.bind(this, gridNode));
}
- if (this.dataGrid._deleteCallback && gridNode !== this.placeholderNode)
- contextMenu.appendItem(WebInspector.UIString("Delete"), this._deleteCallback.bind(this, gridNode));
+ if (gridNode.children.some((child) => child.hasChildren) || (gridNode.hasChildren && !gridNode.children.length)) {
+ contextMenu.appendSeparator();
+
+ contextMenu.appendItem(WebInspector.UIString("Expand All"), gridNode.expandRecursively.bind(gridNode));
+ contextMenu.appendItem(WebInspector.UIString("Collapse All"), gridNode.collapseRecursively.bind(gridNode));
+ }
}
}
diff --git a/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
index fe8650c..2560753 100644
--- a/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
+++ b/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
@@ -153,7 +153,11 @@
this._breakpointsContentTreeOutline.addEventListener(WebInspector.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this);
- this._breakpointsContentTreeOutline.oncontextmenu = this._breakpointTreeOutlineContextMenuTreeElement.bind(this);
+ this._breakpointsContentTreeOutline.populateContextMenu = function(contextMenu, event, treeElement) {
+ this._breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement);
+
+ WebInspector.TreeOutline.prototype.populateContextMenu(contextMenu, event, treeElement);
+ }.bind(this);
this._breakpointsContentTreeOutline.appendChild(this._allExceptionsBreakpointTreeElement);
this._breakpointsContentTreeOutline.appendChild(this._allUncaughtExceptionsBreakpointTreeElement);
@@ -747,9 +751,10 @@
return true;
}
- _breakpointTreeOutlineContextMenuTreeElement(event, treeElement)
+ _breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement)
{
- console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement);
+ // This check is necessary since the context menu is created by the TreeOutline, meaning
+ // that any child could be the target of the context menu event.
if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement))
return;
@@ -764,7 +769,6 @@
this._toggleAllBreakpoints(breakpoints, shouldDisable);
};
- let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
if (shouldDisable)
contextMenu.appendItem(WebInspector.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints);
else
diff --git a/Source/WebInspectorUI/UserInterface/Views/FrameTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/FrameTreeElement.js
index 9b8f2ed..ceff846 100644
--- a/Source/WebInspectorUI/UserInterface/Views/FrameTreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/FrameTreeElement.js
@@ -108,8 +108,6 @@
{
// Immediate superclasses are skipped, since Frames handle their own SourceMapResources.
WebInspector.GeneralTreeElement.prototype.onattach.call(this);
-
- this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this));
}
// Overrides from FolderizedTreeElement (Protected).
diff --git a/Source/WebInspectorUI/UserInterface/Views/GeneralTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/GeneralTreeElement.js
index c30bd7e..dd65cbe 100644
--- a/Source/WebInspectorUI/UserInterface/Views/GeneralTreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/GeneralTreeElement.js
@@ -208,24 +208,6 @@
if (this._statusElement)
this._listItemNode.appendChild(this._statusElement);
this._listItemNode.appendChild(this._titlesElement);
-
- if (this.oncontextmenu && typeof this.oncontextmenu === "function") {
- this._boundContextMenuEventHandler = this.oncontextmenu.bind(this);
- this._listItemNode.addEventListener("contextmenu", this._boundContextMenuEventHandler);
- }
-
- if (!this._boundContextMenuEventHandler && this.treeOutline.oncontextmenu && typeof this.treeOutline.oncontextmenu === "function") {
- this._boundContextMenuEventHandler = (event) => { this.treeOutline.oncontextmenu(event, this); };
- this._listItemNode.addEventListener("contextmenu", this._boundContextMenuEventHandler);
- }
- }
-
- ondetach()
- {
- if (this._boundContextMenuEventHandler) {
- this._listItemNode.removeEventListener("contextmenu", this._boundContextMenuEventHandler);
- this._boundContextMenuEventHandler = null;
- }
}
onreveal()
diff --git a/Source/WebInspectorUI/UserInterface/Views/ObjectTreeBaseTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/ObjectTreeBaseTreeElement.js
index 06dfaab..53afa13 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ObjectTreeBaseTreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ObjectTreeBaseTreeElement.js
@@ -55,11 +55,6 @@
// Protected
- oncontextmenu(event)
- {
- this._contextMenuHandler(event);
- }
-
resolvedValue()
{
console.assert(this._property);
@@ -151,43 +146,13 @@
return setterElement;
}
- // Private
-
- _logSymbolProperty()
- {
- var symbol = this._property.symbol;
- if (!symbol)
- return;
-
- var text = WebInspector.UIString("Selected Symbol");
- WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, symbol, true);
- }
-
- _logValue(value)
- {
- var resolvedValue = value || this.resolvedValue();
- if (!resolvedValue)
- return;
-
- var propertyPath = this.resolvedValuePropertyPath();
- var isImpossible = propertyPath.isFullPathImpossible();
- var text = isImpossible ? WebInspector.UIString("Selected Value") : propertyPath.displayPath(this.propertyPathType());
-
- if (!isImpossible)
- WebInspector.quickConsole.prompt.pushHistoryItem(text);
-
- WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, resolvedValue, isImpossible);
- }
-
- _contextMenuHandler(event)
+ populateContextMenu(contextMenu, event)
{
if (event.__addedObjectPreviewContextMenuItems)
return;
if (event.__addedObjectTreeContextMenuItems)
return;
- let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
-
event.__addedObjectTreeContextMenuItems = true;
if (typeof this.treeOutline.objectTreeElementAddContextMenuItems === "function") {
@@ -215,6 +180,36 @@
contextMenu.appendSeparator();
this._appendMenusItemsForObject(contextMenu, resolvedValue);
+
+ super.populateContextMenu(contextMenu, event);
+ }
+
+ // Private
+
+ _logSymbolProperty()
+ {
+ var symbol = this._property.symbol;
+ if (!symbol)
+ return;
+
+ var text = WebInspector.UIString("Selected Symbol");
+ WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, symbol, true);
+ }
+
+ _logValue(value)
+ {
+ var resolvedValue = value || this.resolvedValue();
+ if (!resolvedValue)
+ return;
+
+ var propertyPath = this.resolvedValuePropertyPath();
+ var isImpossible = propertyPath.isFullPathImpossible();
+ var text = isImpossible ? WebInspector.UIString("Selected Value") : propertyPath.displayPath(this.propertyPathType());
+
+ if (!isImpossible)
+ WebInspector.quickConsole.prompt.pushHistoryItem(text);
+
+ WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, resolvedValue, isImpossible);
}
_appendMenusItemsForObject(contextMenu, resolvedValue)
diff --git a/Source/WebInspectorUI/UserInterface/Views/ResourceTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/ResourceTreeElement.js
index 60f7be2..a073471 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ResourceTreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ResourceTreeElement.js
@@ -89,13 +89,6 @@
return {text: [urlComponents.lastPathComponent, urlComponents.path, this._resource.url]};
}
- onattach()
- {
- super.onattach();
-
- this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this));
- }
-
ondblclick()
{
InspectorFrontendHost.openInNewTab(this._resource.url);
@@ -163,11 +156,11 @@
this.callFirstAncestorFunction("descendantResourceTreeElementMainTitleDidChange", [this, oldMainTitle]);
}
- _handleContextMenuEvent(event)
+ populateContextMenu(contextMenu, event)
{
- let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
-
WebInspector.appendContextMenuItemsForSourceCode(contextMenu, this._resource);
+
+ super.populateContextMenu(contextMenu, event);
}
// Private
diff --git a/Source/WebInspectorUI/UserInterface/Views/ThreadTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/ThreadTreeElement.js
index d4449cc..f21466b 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ThreadTreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ThreadTreeElement.js
@@ -108,14 +108,15 @@
this.expand();
}
- oncontextmenu(event)
+ populateContextMenu(contextMenu, event)
{
let targetData = WebInspector.debuggerManager.dataForTarget(this._target);
- let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
contextMenu.appendItem(WebInspector.UIString("Resume Thread"), () => {
WebInspector.debuggerManager.continueUntilNextRunLoop(this._target);
}, !targetData.paused);
+
+ super.populateContextMenu(contextMenu, event);
}
// Private
diff --git a/Source/WebInspectorUI/UserInterface/Views/TreeElement.js b/Source/WebInspectorUI/UserInterface/Views/TreeElement.js
index 15793d9..b83466d 100644
--- a/Source/WebInspectorUI/UserInterface/Views/TreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/TreeElement.js
@@ -609,4 +609,14 @@
var left = this._listItemNode.totalOffsetLeft + computedLeftPadding;
return event.pageX >= left && event.pageX <= left + this.arrowToggleWidth && this.hasChildren;
}
+
+ populateContextMenu(contextMenu, event)
+ {
+ if (this.children.some((child) => child.hasChildren) || (this.hasChildren && !this.children.length)) {
+ contextMenu.appendSeparator();
+
+ contextMenu.appendItem(WebInspector.UIString("Expand All"), this.expandRecursively.bind(this));
+ contextMenu.appendItem(WebInspector.UIString("Collapse All"), this.collapseRecursively.bind(this));
+ }
+ }
};
diff --git a/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js b/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js
index d6a52fd..34a45e0 100644
--- a/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js
+++ b/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js
@@ -34,6 +34,7 @@
this.element = element || document.createElement("ol");
this.element.classList.add(WebInspector.TreeOutline.ElementStyleClassName);
+ this.element.addEventListener("contextmenu", this._handleContextmenu.bind(this));
this.children = [];
this.selectedTreeElement = null;
@@ -599,6 +600,38 @@
return false;
}
+ // Protected
+
+ treeElementFromEvent(event)
+ {
+ let scrollContainer = this.element.parentElement;
+
+ // We choose this X coordinate based on the knowledge that our list
+ // items extend at least to the right edge of the outer <ol> container.
+ // In the no-word-wrap mode the outer <ol> may be wider than the tree container
+ // (and partially hidden), in which case we are left to use only its right boundary.
+ let x = scrollContainer.totalOffsetLeft + scrollContainer.offsetWidth - 36;
+ let y = event.pageY;
+
+ // Our list items have 1-pixel cracks between them vertically. We avoid
+ // the cracks by checking slightly above and slightly below the mouse
+ // and seeing if we hit the same element each time.
+ let elementUnderMouse = this.treeElementFromPoint(x, y);
+ let elementAboveMouse = this.treeElementFromPoint(x, y - 2);
+ let element = null;
+ if (elementUnderMouse === elementAboveMouse)
+ element = elementUnderMouse;
+ else
+ element = this.treeElementFromPoint(x, y + 2);
+
+ return element;
+ }
+
+ populateContextMenu(contextMenu, event, treeElement)
+ {
+ treeElement.populateContextMenu(contextMenu, event);
+ }
+
// Private
static _generateStyleRulesIfNeeded()
@@ -625,6 +658,16 @@
document.head.appendChild(WebInspector.TreeOutline._styleElement);
}
+
+ _handleContextmenu(event)
+ {
+ let treeElement = this.treeElementFromEvent(event);
+ if (!treeElement)
+ return;
+
+ let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
+ this.populateContextMenu(contextMenu, event, treeElement);
+ }
};
WebInspector.TreeOutline._styleElement = null;
diff --git a/Source/WebInspectorUI/UserInterface/Views/VisualStyleSelectorTreeItem.js b/Source/WebInspectorUI/UserInterface/Views/VisualStyleSelectorTreeItem.js
index 3250d46..5a8ae9f 100644
--- a/Source/WebInspectorUI/UserInterface/Views/VisualStyleSelectorTreeItem.js
+++ b/Source/WebInspectorUI/UserInterface/Views/VisualStyleSelectorTreeItem.js
@@ -93,7 +93,6 @@
this._listItemNode.addEventListener("mouseover", this._highlightNodesWithSelector.bind(this));
this._listItemNode.addEventListener("mouseout", this._hideDOMNodeHighlight.bind(this));
- this._listItemNode.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this));
this._checkboxElement = document.createElement("input");
this._checkboxElement.type = "checkbox";
@@ -122,27 +121,8 @@
this._listItemNode.classList.remove("editable");
}
- // Private
-
- _highlightNodesWithSelector()
+ populateContextMenu(contextMenu, event)
{
- if (!this.representedObject.ownerRule) {
- WebInspector.domTreeManager.highlightDOMNode(this.representedObject.node.id);
- return;
- }
-
- WebInspector.domTreeManager.highlightSelector(this.selectorText, this.representedObject.node.ownerDocument.frameIdentifier);
- }
-
- _hideDOMNodeHighlight()
- {
- WebInspector.domTreeManager.hideDOMNodeHighlight();
- }
-
- _handleContextMenuEvent(event)
- {
- let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
-
contextMenu.appendItem(WebInspector.UIString("Copy Rule"), () => {
InspectorFrontendHost.copyText(this.representedObject.generateCSSRuleString());
});
@@ -213,6 +193,25 @@
this.representedObject.nodeStyles.addRule(pseudoSelectors.join(", "), styleText);
});
}
+
+ super.populateContextMenu(contextMenu, event);
+ }
+
+ // Private
+
+ _highlightNodesWithSelector()
+ {
+ if (!this.representedObject.ownerRule) {
+ WebInspector.domTreeManager.highlightDOMNode(this.representedObject.node.id);
+ return;
+ }
+
+ WebInspector.domTreeManager.highlightSelector(this.selectorText, this.representedObject.node.ownerDocument.frameIdentifier);
+ }
+
+ _hideDOMNodeHighlight()
+ {
+ WebInspector.domTreeManager.hideDOMNodeHighlight();
}
_handleCheckboxChanged(event)
diff --git a/Source/WebInspectorUI/UserInterface/Views/WorkerTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/WorkerTreeElement.js
index 2cca419..d2d3de6 100644
--- a/Source/WebInspectorUI/UserInterface/Views/WorkerTreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/WorkerTreeElement.js
@@ -100,6 +100,14 @@
}
}
+ populateContextMenu(contextMenu, event)
+ {
+ // FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
+ WebInspector.appendContextMenuItemsForSourceCode(contextMenu, this.script.resource ? this.script.resource : this.script);
+
+ super.populateContextMenu(contextMenu, event);
+ }
+
// Overrides from SourceCodeTreeElement.
updateSourceMapResources()
@@ -122,8 +130,6 @@
// Handle our own SourceMapResources. Skip immediate superclasses.
WebInspector.GeneralTreeElement.prototype.onattach.call(this);
-
- this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this));
}
// Overrides from FolderizedTreeElement
@@ -144,14 +150,6 @@
// Private
- _handleContextMenuEvent(event)
- {
- let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
-
- // FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
- WebInspector.appendContextMenuItemsForSourceCode(contextMenu, this.script.resource ? this.script.resource : this.script);
- }
-
_scriptAdded(event)
{
let script = event.data.script;