diff options
Diffstat (limited to 'WebCore/inspector/front-end/ElementsTreeOutline.js')
-rw-r--r-- | WebCore/inspector/front-end/ElementsTreeOutline.js | 239 |
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\"><" + node.nodeName.toLowerCase().escapeHTML(); + var tagName = node.nodeName.toLowerCase().escapeHTML(); + info.title = "<span class=\"webkit-html-tag\"><" + tagName; if (node.hasAttributes()) { for (var i = 0; i < node.attributes.length; ++i) { @@ -951,15 +1088,21 @@ WebInspector.ElementsTreeElement.prototype = { } info.title += "></span>​"; + const closingTagHTML = "<span class=\"webkit-html-tag\"></" + tagName + "></span>​"; + 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\">…</span>​"; + 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>​<span class=\"webkit-html-tag\"></" + node.nodeName.toLowerCase().escapeHTML() + "></span>"; + info.title += "<span class=\"webkit-html-text-node\">" + textChild.nodeValue.escapeHTML() + "</span>​" + 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); + } } } |