summaryrefslogtreecommitdiffstats
path: root/WebCore/inspector/front-end/ElementsTreeOutline.js
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/inspector/front-end/ElementsTreeOutline.js')
-rw-r--r--WebCore/inspector/front-end/ElementsTreeOutline.js239
1 files changed, 200 insertions, 39 deletions
diff --git a/WebCore/inspector/front-end/ElementsTreeOutline.js b/WebCore/inspector/front-end/ElementsTreeOutline.js
index 4a8dae0..fe7ae53 100644
--- a/WebCore/inspector/front-end/ElementsTreeOutline.js
+++ b/WebCore/inspector/front-end/ElementsTreeOutline.js
@@ -59,9 +59,16 @@ WebInspector.ElementsTreeOutline.prototype = {
this._rootDOMNode = x;
+ this._isXMLMimeType = !!(WebInspector.mainResource && WebInspector.mainResource.mimeType && WebInspector.mainResource.mimeType.match(/x(?:ht)?ml/i));
+
this.update();
},
+ get isXMLMimeType()
+ {
+ return this._isXMLMimeType;
+ },
+
get focusedDOMNode()
{
return this._focusedDOMNode;
@@ -155,12 +162,27 @@ WebInspector.ElementsTreeOutline.prototype = {
return treeElement;
},
+ createTreeElementFor: function(node)
+ {
+ var treeElement = this.findTreeElement(node);
+ if (treeElement)
+ return treeElement;
+ if (!node.parentNode)
+ return null;
+
+ var treeElement = this.createTreeElementFor(node.parentNode);
+ if (treeElement && treeElement.showChild(node.index))
+ return treeElement.children[node.index];
+
+ return null;
+ },
+
revealAndSelectNode: function(node)
{
if (!node)
return;
- var treeElement = this.findTreeElement(node);
+ var treeElement = this.createTreeElementFor(node);
if (!treeElement)
return;
@@ -277,9 +299,9 @@ WebInspector.ElementsTreeOutline.prototype = {
var tag = event.target.enclosingNodeOrSelfWithClass("webkit-html-tag");
var textNode = event.target.enclosingNodeOrSelfWithClass("webkit-html-text-node");
- if (tag)
+ if (tag && listItem.treeElement._populateTagContextMenu)
listItem.treeElement._populateTagContextMenu(contextMenu, event);
- else if (textNode)
+ else if (textNode && listItem.treeElement._populateTextContextMenu)
listItem.treeElement._populateTextContextMenu(contextMenu, textNode);
contextMenu.show(event);
}
@@ -296,27 +318,28 @@ WebInspector.ElementsTreeElement = function(node)
if (this.representedObject.nodeType == Node.ELEMENT_NODE)
this._canAddAttributes = true;
+ this._searchQuery = null;
+ this._expandedChildrenLimit = WebInspector.ElementsTreeElement.InitialChildrenLimit;
}
-WebInspector.ElementsTreeElement.prototype = {
- get highlighted()
- {
- return this._highlighted;
- },
+WebInspector.ElementsTreeElement.InitialChildrenLimit = 500;
+
+// A union of HTML4 and HTML5-Draft elements that explicitly
+// or implicitly (for HTML5) forbid the closing tag.
+// FIXME: Revise once HTML5 Final is published.
+WebInspector.ElementsTreeElement.ForbiddenClosingTagElements = [
+ "area", "base", "basefont", "br", "canvas", "col", "command", "embed", "frame",
+ "hr", "img", "input", "isindex", "keygen", "link", "meta", "param", "source"
+].keySet();
- set highlighted(x)
+WebInspector.ElementsTreeElement.prototype = {
+ highlightSearchResults: function(searchQuery)
{
- if (this._highlighted === x)
+ if (this._searchQuery === searchQuery)
return;
- this._highlighted = x;
-
- if (this.listItemElement) {
- if (x)
- this.listItemElement.addStyleClass("highlighted");
- else
- this.listItemElement.removeStyleClass("highlighted");
- }
+ this._searchQuery = searchQuery;
+ this.updateTitle();
},
get hovered()
@@ -341,6 +364,42 @@ WebInspector.ElementsTreeElement.prototype = {
}
},
+ get expandedChildrenLimit()
+ {
+ return this._expandedChildrenLimit;
+ },
+
+ set expandedChildrenLimit(x)
+ {
+ if (this._expandedChildrenLimit === x)
+ return;
+
+ this._expandedChildrenLimit = x;
+ if (this.treeOutline && !this._updateChildrenInProgress)
+ this._updateChildren(true);
+ },
+
+ get expandedChildCount()
+ {
+ var count = this.children.length;
+ if (count && this.children[count - 1].elementCloseTag)
+ count--;
+ if (count && this.children[count - 1].expandAllButton)
+ count--;
+ return count;
+ },
+
+ showChild: function(index)
+ {
+ if (index >= this.expandedChildrenLimit) {
+ this._expandedChildrenLimit = index + 1;
+ this._updateChildren(true);
+ }
+
+ // Whether index-th child is visible in the children tree
+ return this.expandedChildCount > index;
+ },
+
createTooltipForImageNode: function(node, callback)
{
function createTooltipThenCallback(properties)
@@ -386,9 +445,6 @@ WebInspector.ElementsTreeElement.prototype = {
{
this.listItemElement.addEventListener("mousedown", this.onmousedown.bind(this), false);
- if (this._highlighted)
- this.listItemElement.addStyleClass("highlighted");
-
if (this._hovered) {
this.updateSelection();
this.listItemElement.addStyleClass("hovered");
@@ -422,9 +478,34 @@ WebInspector.ElementsTreeElement.prototype = {
WebInspector.domAgent.getChildNodesAsync(this.representedObject, this._updateChildren.bind(this, fullRefresh));
},
+ insertChildElement: function(child, index)
+ {
+ var newElement = new WebInspector.ElementsTreeElement(child);
+ newElement.selectable = this.treeOutline.selectEnabled;
+ this.insertChild(newElement, index);
+ return newElement;
+ },
+
+ moveChild: function(child, targetIndex)
+ {
+ var wasSelected = child.selected;
+ treeElement.removeChild(child);
+ treeElement.insertChild(child, targetIndex);
+ if (wasSelected)
+ existingTreeElement.select();
+ },
+
_updateChildren: function(fullRefresh)
{
+ if (this._updateChildrenInProgress)
+ return;
+
+ this._updateChildrenInProgress = true;
+ var focusedNode = this.treeOutline.focusedDOMNode;
+ var originalScrollTop;
if (fullRefresh) {
+ var treeOutlineContainerElement = this.treeOutline.element.parentNode;
+ originalScrollTop = treeOutlineContainerElement.scrollTop;
var selectedTreeElement = this.treeOutline.selectedTreeElement;
if (selectedTreeElement && selectedTreeElement.hasAncestor(this))
this.select();
@@ -433,6 +514,7 @@ WebInspector.ElementsTreeElement.prototype = {
var treeElement = this;
var treeChildIndex = 0;
+ var elementToSelect;
function updateChildrenOfNode(node)
{
@@ -443,7 +525,7 @@ WebInspector.ElementsTreeElement.prototype = {
if (!currentTreeElement || currentTreeElement.representedObject !== child) {
// Find any existing element that is later in the children list.
var existingTreeElement = null;
- for (var i = (treeChildIndex + 1); i < treeElement.children.length; ++i) {
+ for (var i = (treeChildIndex + 1), size = treeElement.expandedChildCount; i < size; ++i) {
if (treeElement.children[i].representedObject === child) {
existingTreeElement = treeElement.children[i];
break;
@@ -452,16 +534,16 @@ WebInspector.ElementsTreeElement.prototype = {
if (existingTreeElement && existingTreeElement.parent === treeElement) {
// If an existing element was found and it has the same parent, just move it.
- var wasSelected = existingTreeElement.selected;
- treeElement.removeChild(existingTreeElement);
- treeElement.insertChild(existingTreeElement, treeChildIndex);
- if (wasSelected)
- existingTreeElement.select();
+ treeElement.moveChild(existingTreeElement, treeChildIndex);
} else {
// No existing element found, insert a new element.
- var newElement = new WebInspector.ElementsTreeElement(child);
- newElement.selectable = treeOutline.selectEnabled;
- treeElement.insertChild(newElement, treeChildIndex);
+ if (treeChildIndex < treeElement.expandedChildrenLimit) {
+ var newElement = treeElement.insertChildElement(child, treeChildIndex);
+ if (child === focusedNode)
+ elementToSelect = newElement;
+ if (treeElement.expandedChildCount > treeElement.expandedChildrenLimit)
+ treeElement.expandedChildrenLimit++;
+ }
}
}
@@ -490,6 +572,7 @@ WebInspector.ElementsTreeElement.prototype = {
}
updateChildrenOfNode(this.representedObject);
+ this.adjustCollapsedRange(false);
var lastChild = this.children[this.children.length - 1];
if (this.representedObject.nodeType == Node.ELEMENT_NODE && (!lastChild || !lastChild.elementCloseTag)) {
@@ -499,15 +582,66 @@ WebInspector.ElementsTreeElement.prototype = {
item.elementCloseTag = true;
this.appendChild(item);
}
+
+ // We want to restore the original selection and tree scroll position after a full refresh, if possible.
+ if (fullRefresh && elementToSelect) {
+ elementToSelect.select();
+ if (treeOutlineContainerElement && originalScrollTop <= treeOutlineContainerElement.scrollHeight)
+ treeOutlineContainerElement.scrollTop = originalScrollTop;
+ }
+
+ delete this._updateChildrenInProgress;
+ },
+
+ adjustCollapsedRange: function()
+ {
+ // Ensure precondition: only the tree elements for node children are found in the tree
+ // (not the Expand All button or the closing tag).
+ if (this.expandAllButtonElement && this.expandAllButtonElement.__treeElement.parent)
+ this.removeChild(this.expandAllButtonElement.__treeElement);
+
+ const node = this.representedObject;
+ if (!node.children)
+ return;
+ const childNodeCount = node.children.length;
+
+ // In case some nodes from the expanded range were removed, pull some nodes from the collapsed range into the expanded range at the bottom.
+ for (var i = this.expandedChildCount, limit = Math.min(this.expandedChildrenLimit, childNodeCount); i < limit; ++i)
+ this.insertChildElement(node.children[i], i);
+
+ const expandedChildCount = this.expandedChildCount;
+ if (childNodeCount > this.expandedChildCount) {
+ var targetButtonIndex = expandedChildCount;
+ if (!this.expandAllButtonElement) {
+ var title = "<button class=\"show-all-nodes\" value=\"\" />";
+ var item = new TreeElement(title, null, false);
+ item.selectable = false;
+ item.expandAllButton = true;
+ this.insertChild(item, targetButtonIndex);
+ this.expandAllButtonElement = item.listItemElement.firstChild;
+ this.expandAllButtonElement.__treeElement = item;
+ this.expandAllButtonElement.addEventListener("click", this.handleLoadAllChildren.bind(this), false);
+ } else if (!this.expandAllButtonElement.__treeElement.parent)
+ this.insertChild(this.expandAllButtonElement.__treeElement, targetButtonIndex);
+ this.expandAllButtonElement.textContent = WebInspector.UIString("Show All Nodes (%d More)", childNodeCount - expandedChildCount);
+ } else if (this.expandAllButtonElement)
+ delete this.expandAllButtonElement;
+ },
+
+ handleLoadAllChildren: function()
+ {
+ this.expandedChildrenLimit = Math.max(this.representedObject._childNodeCount, this.expandedChildrenLimit + WebInspector.ElementsTreeElement.InitialChildrenLimit);
},
onexpand: function()
{
+ this.updateTitle();
this.treeOutline.updateSelection();
},
oncollapse: function()
{
+ this.updateTitle();
this.treeOutline.updateSelection();
},
@@ -879,11 +1013,12 @@ WebInspector.ElementsTreeElement.prototype = {
var self = this;
function callback(tooltipText)
{
- var title = self._nodeTitleInfo(self.representedObject, self.hasChildren, WebInspector.linkifyURL, tooltipText).title;
+ var title = self._nodeTitleInfo(WebInspector.linkifyURL, tooltipText).title;
self.title = "<span class=\"highlight\">" + title + "</span>";
delete self.selectionElement;
self.updateSelection();
self._preventFollowingLinksOnDoubleClick();
+ self._highlightSearchResults();
};
// TODO: Replace with InjectedScriptAccess.getBasicProperties(obj, [names]).
@@ -916,9 +1051,10 @@ WebInspector.ElementsTreeElement.prototype = {
return hrefValue;
},
- _nodeTitleInfo: function(node, hasChildren, linkify, tooltipText)
+ _nodeTitleInfo: function(linkify, tooltipText)
{
- var info = {title: "", hasChildren: hasChildren};
+ var node = this.representedObject;
+ var info = {title: "", hasChildren: this.hasChildren};
switch (node.nodeType) {
case Node.DOCUMENT_NODE:
@@ -930,7 +1066,8 @@ WebInspector.ElementsTreeElement.prototype = {
break;
case Node.ELEMENT_NODE:
- info.title = "<span class=\"webkit-html-tag\">&lt;" + node.nodeName.toLowerCase().escapeHTML();
+ var tagName = node.nodeName.toLowerCase().escapeHTML();
+ info.title = "<span class=\"webkit-html-tag\">&lt;" + tagName;
if (node.hasAttributes()) {
for (var i = 0; i < node.attributes.length; ++i) {
@@ -951,15 +1088,21 @@ WebInspector.ElementsTreeElement.prototype = {
}
info.title += "&gt;</span>&#8203;";
+ const closingTagHTML = "<span class=\"webkit-html-tag\">&lt;/" + tagName + "&gt;</span>&#8203;";
+ var textChild = onlyTextChild.call(node);
+ var showInlineText = textChild && textChild.textContent.length < Preferences.maxInlineTextChildLength;
+
+ if (!this.expanded && (!showInlineText && (this.treeOutline.isXMLMimeType || !WebInspector.ElementsTreeElement.ForbiddenClosingTagElements[tagName]))) {
+ if (this.hasChildren)
+ info.title += "<span class=\"webkit-html-text-node\">&#8230;</span>&#8203;";
+ info.title += closingTagHTML;
+ }
+
// If this element only has a single child that is a text node,
// just show that text and the closing tag inline rather than
// create a subtree for them
-
- var textChild = onlyTextChild.call(node);
- var showInlineText = textChild && textChild.textContent.length < Preferences.maxInlineTextChildLength;
-
if (showInlineText) {
- info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>&#8203;<span class=\"webkit-html-tag\">&lt;/" + node.nodeName.toLowerCase().escapeHTML() + "&gt;</span>";
+ info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>&#8203;" + closingTagHTML;
info.hasChildren = false;
}
break;
@@ -1038,6 +1181,7 @@ WebInspector.ElementsTreeElement.prototype = {
return;
parentElement.removeChild(self);
+ parentElement.adjustCollapsedRange(true);
}
var callId = WebInspector.Callback.wrap(removeNodeCallback);
@@ -1077,6 +1221,23 @@ WebInspector.ElementsTreeElement.prototype = {
_copyHTML: function()
{
InspectorBackend.copyNode(this.representedObject.id);
+ },
+
+ _highlightSearchResults: function()
+ {
+ if (!this._searchQuery)
+ return;
+ var text = this.listItemElement.textContent;
+ var regexObject = createSearchRegex(this._searchQuery);
+
+ var offset = 0;
+ var match = regexObject.exec(text);
+ while (match) {
+ highlightSearchResult(this.listItemElement, offset + match.index, match[0].length);
+ offset += match.index + 1;
+ text = text.substring(match.index + 1);
+ match = regexObject.exec(text);
+ }
}
}