Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/network/XMLView.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/network/XMLView.js b/third_party/WebKit/Source/devtools/front_end/network/XMLView.js |
| index 3e05de441cae18dca751d8de871817969bf71581..52e9a3300ebd2ebcc6e41e446f4e0582dd6fe50d 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/network/XMLView.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/network/XMLView.js |
| @@ -5,6 +5,7 @@ |
| /** |
| * @constructor |
| * @extends {WebInspector.Widget} |
| + * @implements {WebInspector.Searchable} |
| * @param {!Document} parsedXML |
| */ |
| WebInspector.XMLView = function(parsedXML) |
| @@ -12,9 +13,33 @@ WebInspector.XMLView = function(parsedXML) |
| WebInspector.Widget.call(this, true); |
| this.registerRequiredCSS("network/xmlView.css"); |
| this.contentElement.classList.add("shadow-xml-view", "source-code"); |
| - var treeOutline = new TreeOutline(); |
| - this.contentElement.appendChild(treeOutline.element); |
| - WebInspector.XMLView.Node.populate(treeOutline, parsedXML); |
| + this._treeOutline = new TreeOutline(); |
| + this.contentElement.appendChild(this._treeOutline.element); |
| + WebInspector.XMLView.Node.populate(this._treeOutline, parsedXML, this); |
| + |
| + /** @type {?WebInspector.SearchableView} */ |
| + this._searchableView; |
| + /** @type {number} */ |
| + this._currentSearchFocusIndex = 0; |
| + /** @type {!Array.<!TreeElement>} */ |
| + this._currentSearchTreeElements = []; |
| + /** @type {?WebInspector.SearchableView.SearchConfig} */ |
| + this._lastSearchConfig; |
| +} |
| + |
| +/** |
| + * @param {!Document} parsedXML |
| + * @return {!WebInspector.SearchableView} |
| + */ |
| +WebInspector.XMLView.createSearchableView = function(parsedXML) |
| +{ |
| + var xmlView = new WebInspector.XMLView(parsedXML); |
| + var searchableView = new WebInspector.SearchableView(xmlView); |
| + searchableView.setPlaceholder(WebInspector.UIString("Find")); |
| + xmlView.setSearchableView(searchableView); |
| + xmlView.show(searchableView.element); |
| + xmlView.contentElement.setAttribute("tabIndex", 0); |
| + return searchableView; |
| } |
| /** |
| @@ -36,6 +61,169 @@ WebInspector.XMLView.parseXML = function(text, mimeType) |
| } |
| WebInspector.XMLView.prototype = { |
| + /** |
| + * @param {?WebInspector.SearchableView} view |
| + */ |
| + setSearchableView: function(view) |
| + { |
| + this._searchableView = view; |
| + }, |
|
lushnikov
2016/05/04 17:00:47
style: new line
allada
2016/05/04 21:22:35
Done.
|
| + /** |
| + * @param {number} index |
| + * @param {boolean} shouldJump |
| + */ |
| + _goToMatch: function(index, shouldJump) |
|
lushnikov
2016/05/04 17:00:47
_jumpToMatch: function...
to correlate with Sear
allada
2016/05/04 21:22:35
Done.
|
| + { |
| + if (!this._lastSearchConfig) |
| + return; |
| + var regEx = this._lastSearchConfig.toSearchRegex(true); |
|
lushnikov
2016/05/04 17:00:47
regex
allada
2016/05/04 21:22:35
Done.
|
| + var newFocusElement = this._currentSearchTreeElements[index]; |
| + var previousFocusElement = this._currentSearchTreeElements[this._currentSearchFocusIndex]; |
| + |
| + if (!newFocusElement) |
| + index = 0; |
| + |
| + if (previousFocusElement) |
| + previousFocusElement.setSearchRegex(regEx); |
| + |
| + this._updateSearchIndex(index); |
| + if (newFocusElement) { |
| + if (shouldJump) |
| + newFocusElement.reveal(true); |
| + newFocusElement.setSearchRegex(regEx, WebInspector.highlightedCurrentSearchResultClassName); |
| + } |
| + }, |
| + |
| + /** |
| + * @param {number} count |
| + */ |
| + _updateSearchCount: function(count) |
| + { |
| + if (!this._searchableView) |
| + return; |
| + this._searchableView.updateSearchMatchesCount(count); |
| + }, |
| + |
| + /** |
| + * @param {number} index |
| + */ |
| + _updateSearchIndex: function(index) |
| + { |
| + this._currentSearchFocusIndex = index; |
| + if (!this._searchableView) |
| + return; |
| + this._searchableView.updateCurrentMatchIndex(index); |
| + }, |
| + |
| + _rerunSearch: function() |
|
lushnikov
2016/05/04 17:00:47
It's usually good to avoid calling public API from
allada
2016/05/04 21:22:35
Done.
|
| + { |
| + if (this._lastSearchConfig) |
| + this.performSearch(this._lastSearchConfig, false, false); |
| + }, |
| + |
| + /** |
| + * @override |
| + */ |
| + searchCanceled: function() |
| + { |
| + this._lastSearchConfig = null; |
| + this._currentSearchTreeElements = []; |
| + var currentElement = this._treeOutline.rootElement(); |
| + |
| + while (currentElement) { |
| + if (currentElement instanceof WebInspector.XMLView.Node) |
| + currentElement.revertHighlightChanges(); |
| + currentElement = currentElement.traverseNextTreeElement(false); |
| + } |
| + this._updateSearchCount(0); |
| + this._updateSearchIndex(0); |
| + }, |
| + |
| + /** |
| + * @override |
| + * @param {!WebInspector.SearchableView.SearchConfig} searchConfig |
| + * @param {boolean} shouldJump |
| + * @param {boolean=} jumpBackwards |
| + */ |
| + performSearch: function(searchConfig, shouldJump, jumpBackwards) |
| + { |
| + var newIndex = this._currentSearchFocusIndex; |
| + var previousSearchFocusElement = this._currentSearchTreeElements[newIndex]; |
| + this.searchCanceled(); |
| + var regEx = searchConfig.toSearchRegex(true); |
| + var currentElement = this._treeOutline.rootElement(); |
| + |
| + while (currentElement) { |
| + if (currentElement instanceof WebInspector.XMLView.Node) { |
|
lushnikov
2016/05/04 17:00:48
let's use fast-returns as much as possible; the le
allada
2016/05/04 21:22:35
Done.
|
| + var hasMatch = currentElement.setSearchRegex(regEx); |
| + if (hasMatch) |
| + this._currentSearchTreeElements.push(currentElement); |
| + var currentIndex = this._currentSearchTreeElements.length - 1; |
| + if (previousSearchFocusElement === currentElement) { |
| + if(hasMatch) |
|
lushnikov
2016/05/04 17:00:48
style: space after if
allada
2016/05/04 21:22:36
Done.
|
| + newIndex = currentIndex; |
| + else if (jumpBackwards) |
| + newIndex = currentIndex - 1; |
| + else |
| + newIndex = currentIndex + 1; |
| + } |
| + } |
| + currentElement = currentElement.traverseNextTreeElement(false); |
| + } |
| + this._updateSearchCount(this._currentSearchTreeElements.length); |
| + |
| + if (!this._currentSearchTreeElements.length) { |
| + this._goToMatch(0, shouldJump); |
|
lushnikov
2016/05/04 17:00:48
why do we call _goToMatch if there are no matches?
allada
2016/05/04 21:22:36
Done.
|
| + return; |
| + } |
| + newIndex = mod(newIndex, this._currentSearchTreeElements.length); |
| + |
| + this._lastSearchConfig = /** @type {?WebInspector.SearchableView.SearchConfig} */ (searchConfig); |
| + this._goToMatch(newIndex, shouldJump); |
| + }, |
| + |
| + /** |
| + * @override |
| + */ |
| + jumpToNextSearchResult: function() |
| + { |
| + if (!this._currentSearchTreeElements.length) |
| + return; |
| + |
| + var newIndex = mod(this._currentSearchFocusIndex + 1, this._currentSearchTreeElements.length); |
| + this._goToMatch(newIndex, true); |
| + }, |
| + |
| + /** |
| + * @override |
| + */ |
| + jumpToPreviousSearchResult: function() |
| + { |
| + if (!this._currentSearchTreeElements.length) |
| + return; |
| + |
| + var newIndex = mod(this._currentSearchFocusIndex - 1, this._currentSearchTreeElements.length); |
| + this._goToMatch(newIndex, true); |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + supportsCaseSensitiveSearch: function() |
| + { |
| + return true; |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + supportsRegexSearch: function() |
| + { |
| + return true; |
| + }, |
| + |
| __proto__: WebInspector.Widget.prototype |
| } |
| @@ -44,21 +232,26 @@ WebInspector.XMLView.prototype = { |
| * @extends {TreeElement} |
| * @param {!Node} node |
| * @param {boolean} closeTag |
| + * @param {!WebInspector.XMLView} xmlView |
| */ |
| -WebInspector.XMLView.Node = function(node, closeTag) |
| +WebInspector.XMLView.Node = function(node, closeTag, xmlView) |
| { |
| - TreeElement.call(this, "", !closeTag && !!node.childElementCount); |
| + TreeElement.call(this, "", !closeTag && !!node.childNodes.length); |
| this._node = node; |
| this._closeTag = closeTag; |
| this.selectable = false; |
| + /** @type {!Array.<!Object>} */ |
| + this._highlightChanges = []; |
| this._updateTitle(); |
| + this._xmlView = xmlView; |
| } |
| /** |
| * @param {!TreeOutline|!TreeElement} root |
| * @param {!Node} xmlNode |
| + * @param {!WebInspector.XMLView} xmlView |
| */ |
| -WebInspector.XMLView.Node.populate = function(root, xmlNode) |
| +WebInspector.XMLView.Node.populate = function(root, xmlNode, xmlView) |
| { |
| var node = xmlNode.firstChild; |
| while (node) { |
| @@ -71,11 +264,54 @@ WebInspector.XMLView.Node.populate = function(root, xmlNode) |
| // ignore ATTRIBUTE, ENTITY_REFERENCE, ENTITY, DOCUMENT, DOCUMENT_TYPE, DOCUMENT_FRAGMENT, NOTATION |
| if ((nodeType !== 1) && (nodeType !== 3) && (nodeType !== 4) && (nodeType !== 7) && (nodeType !== 8)) |
| continue; |
| - root.appendChild(new WebInspector.XMLView.Node(currentNode, false)); |
| + root.appendChild(new WebInspector.XMLView.Node(currentNode, false, xmlView)); |
| } |
| } |
| WebInspector.XMLView.Node.prototype = { |
| + /** |
| + * @param {?RegExp} regex |
| + * @param {string=} additionalCssClassName |
| + * @return {boolean} |
| + */ |
| + setSearchRegex: function(regex, additionalCssClassName) { |
| + var cssClasses = WebInspector.highlightedSearchResultClassName; |
|
lushnikov
2016/05/04 17:00:48
let's structure this with fast-returns in the begi
allada
2016/05/04 21:22:36
Done.
|
| + if (additionalCssClassName) |
| + cssClasses += " " + additionalCssClassName; |
| + this.revertHighlightChanges(); |
| + if (regex) |
| + this._applySearch(regex, this.listItemElement, cssClasses); |
| + if (this._closeTag && this.parent && !this.parent.expanded) { |
| + return false; |
| + } |
| + return !!this._highlightChanges.length; |
| + }, |
| + |
| + /** |
| + * @param {!RegExp} regex |
| + * @param {!Element} element |
| + * @param {string} cssClassName |
| + */ |
| + _applySearch: function(regex, element, cssClassName) |
|
lushnikov
2016/05/04 17:00:47
let's inline this method
allada
2016/05/04 21:22:35
Done.
|
| + { |
| + var ranges = []; |
| + var content = element.textContent.replace(/\xA0/g, " "); |
|
allada
2016/05/03 18:22:55
This is because is treated as character 160
allada
2016/05/04 21:22:35
Done.
|
| + regex.lastIndex = 0; |
| + var match = regex.exec(content); |
| + while (match) { |
| + ranges.push(new WebInspector.SourceRange(match.index, match[0].length)); |
| + match = regex.exec(content); |
| + } |
| + if (ranges.length) |
| + WebInspector.highlightRangesWithStyleClass(element, ranges, cssClassName, this._highlightChanges); |
| + }, |
| + |
| + revertHighlightChanges: function() |
| + { |
| + WebInspector.revertDomChanges(this._highlightChanges); |
| + this._highlightChanges = []; |
| + }, |
| + |
| _updateTitle: function() |
| { |
| var node = this._node; |
| @@ -98,7 +334,7 @@ WebInspector.XMLView.Node.prototype = { |
| "\"", "shadow-xml-view-tag") |
| } |
| if (!this.expanded) { |
| - if (node.childElementCount) { |
| + if (node.childNodes.length) { |
|
allada
2016/05/03 18:22:55
Found this bug in the tests I wrote. It was not co
lushnikov
2016/05/04 17:00:48
sg!
allada
2016/05/04 21:22:35
Reverted for now because of a few minor side affec
|
| titleItems.push( |
| ">", "shadow-xml-view-tag", |
| "\u2026", "shadow-xml-view-comment", |
| @@ -142,6 +378,8 @@ WebInspector.XMLView.Node.prototype = { |
| for (var i = 0; i < items.length; i += 2) |
| titleFragment.createChild("span", items[i + 1]).textContent = items[i]; |
| this.title = titleFragment; |
| + if (this._xmlView) |
|
lushnikov
2016/05/04 17:00:47
AFAIU this if always evaluates to "true"
allada
2016/05/04 21:22:35
Done.
|
| + this._xmlView._rerunSearch(); |
| }, |
| onattach: function() |
| @@ -161,8 +399,8 @@ WebInspector.XMLView.Node.prototype = { |
| onpopulate: function() |
| { |
| - WebInspector.XMLView.Node.populate(this, this._node); |
| - this.appendChild(new WebInspector.XMLView.Node(this._node, true)); |
| + WebInspector.XMLView.Node.populate(this, this._node, this._xmlView); |
| + this.appendChild(new WebInspector.XMLView.Node(this._node, true, this._xmlView)); |
| }, |
| __proto__: TreeElement.prototype |