Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @constructor | 6 * @constructor |
| 7 * @extends {WebInspector.Widget} | 7 * @extends {WebInspector.Widget} |
| 8 * @implements {WebInspector.Searchable} | |
| 8 * @param {!Document} parsedXML | 9 * @param {!Document} parsedXML |
| 9 */ | 10 */ |
| 10 WebInspector.XMLView = function(parsedXML) | 11 WebInspector.XMLView = function(parsedXML) |
| 11 { | 12 { |
| 12 WebInspector.Widget.call(this, true); | 13 WebInspector.Widget.call(this, true); |
| 13 this.registerRequiredCSS("network/xmlView.css"); | 14 this.registerRequiredCSS("network/xmlView.css"); |
| 14 this.contentElement.classList.add("shadow-xml-view", "source-code"); | 15 this.contentElement.classList.add("shadow-xml-view", "source-code"); |
| 15 var treeOutline = new TreeOutline(); | 16 this._treeOutline = new TreeOutline(); |
| 16 this.contentElement.appendChild(treeOutline.element); | 17 this.contentElement.appendChild(this._treeOutline.element); |
| 17 WebInspector.XMLView.Node.populate(treeOutline, parsedXML); | 18 WebInspector.XMLView.Node.populate(this._treeOutline, parsedXML, this); |
| 19 | |
| 20 /** @type {?WebInspector.SearchableView} */ | |
| 21 this._searchableView; | |
| 22 /** @type {number} */ | |
| 23 this._currentSearchFocusIndex = 0; | |
| 24 /** @type {!Array.<!TreeElement>} */ | |
| 25 this._currentSearchTreeElements = []; | |
| 26 /** @type {?WebInspector.SearchableView.SearchConfig} */ | |
| 27 this._lastSearchConfig; | |
| 18 } | 28 } |
| 19 | 29 |
| 20 /** | 30 /** |
| 31 * @param {!Document} parsedXML | |
| 32 * @return {!WebInspector.SearchableView} | |
| 33 */ | |
| 34 WebInspector.XMLView.createSearchableView = function(parsedXML) | |
| 35 { | |
| 36 var xmlView = new WebInspector.XMLView(parsedXML); | |
| 37 var searchableView = new WebInspector.SearchableView(xmlView); | |
| 38 searchableView.setPlaceholder(WebInspector.UIString("Find")); | |
| 39 xmlView.setSearchableView(searchableView); | |
| 40 xmlView.show(searchableView.element); | |
| 41 xmlView.contentElement.setAttribute("tabIndex", 0); | |
| 42 return searchableView; | |
| 43 } | |
| 44 | |
| 45 /** | |
| 21 * @param {string} text | 46 * @param {string} text |
| 22 * @param {string} mimeType | 47 * @param {string} mimeType |
| 23 * @return {?Document} | 48 * @return {?Document} |
| 24 */ | 49 */ |
| 25 WebInspector.XMLView.parseXML = function(text, mimeType) | 50 WebInspector.XMLView.parseXML = function(text, mimeType) |
| 26 { | 51 { |
| 27 var parsedXML; | 52 var parsedXML; |
| 28 try { | 53 try { |
| 29 parsedXML = (new DOMParser()).parseFromString(text, mimeType); | 54 parsedXML = (new DOMParser()).parseFromString(text, mimeType); |
| 30 } catch (e) { | 55 } catch (e) { |
| 31 return null; | 56 return null; |
| 32 } | 57 } |
| 33 if (parsedXML.body) | 58 if (parsedXML.body) |
| 34 return null; | 59 return null; |
| 35 return parsedXML; | 60 return parsedXML; |
| 36 } | 61 } |
| 37 | 62 |
| 38 WebInspector.XMLView.prototype = { | 63 WebInspector.XMLView.prototype = { |
| 64 /** | |
| 65 * @param {?WebInspector.SearchableView} view | |
| 66 */ | |
| 67 setSearchableView: function(view) | |
| 68 { | |
| 69 this._searchableView = view; | |
| 70 }, | |
|
lushnikov
2016/05/04 17:00:47
style: new line
allada
2016/05/04 21:22:35
Done.
| |
| 71 /** | |
| 72 * @param {number} index | |
| 73 * @param {boolean} shouldJump | |
| 74 */ | |
| 75 _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.
| |
| 76 { | |
| 77 if (!this._lastSearchConfig) | |
| 78 return; | |
| 79 var regEx = this._lastSearchConfig.toSearchRegex(true); | |
|
lushnikov
2016/05/04 17:00:47
regex
allada
2016/05/04 21:22:35
Done.
| |
| 80 var newFocusElement = this._currentSearchTreeElements[index]; | |
| 81 var previousFocusElement = this._currentSearchTreeElements[this._current SearchFocusIndex]; | |
| 82 | |
| 83 if (!newFocusElement) | |
| 84 index = 0; | |
| 85 | |
| 86 if (previousFocusElement) | |
| 87 previousFocusElement.setSearchRegex(regEx); | |
| 88 | |
| 89 this._updateSearchIndex(index); | |
| 90 if (newFocusElement) { | |
| 91 if (shouldJump) | |
| 92 newFocusElement.reveal(true); | |
| 93 newFocusElement.setSearchRegex(regEx, WebInspector.highlightedCurren tSearchResultClassName); | |
| 94 } | |
| 95 }, | |
| 96 | |
| 97 /** | |
| 98 * @param {number} count | |
| 99 */ | |
| 100 _updateSearchCount: function(count) | |
| 101 { | |
| 102 if (!this._searchableView) | |
| 103 return; | |
| 104 this._searchableView.updateSearchMatchesCount(count); | |
| 105 }, | |
| 106 | |
| 107 /** | |
| 108 * @param {number} index | |
| 109 */ | |
| 110 _updateSearchIndex: function(index) | |
| 111 { | |
| 112 this._currentSearchFocusIndex = index; | |
| 113 if (!this._searchableView) | |
| 114 return; | |
| 115 this._searchableView.updateCurrentMatchIndex(index); | |
| 116 }, | |
| 117 | |
| 118 _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.
| |
| 119 { | |
| 120 if (this._lastSearchConfig) | |
| 121 this.performSearch(this._lastSearchConfig, false, false); | |
| 122 }, | |
| 123 | |
| 124 /** | |
| 125 * @override | |
| 126 */ | |
| 127 searchCanceled: function() | |
| 128 { | |
| 129 this._lastSearchConfig = null; | |
| 130 this._currentSearchTreeElements = []; | |
| 131 var currentElement = this._treeOutline.rootElement(); | |
| 132 | |
| 133 while (currentElement) { | |
| 134 if (currentElement instanceof WebInspector.XMLView.Node) | |
| 135 currentElement.revertHighlightChanges(); | |
| 136 currentElement = currentElement.traverseNextTreeElement(false); | |
| 137 } | |
| 138 this._updateSearchCount(0); | |
| 139 this._updateSearchIndex(0); | |
| 140 }, | |
| 141 | |
| 142 /** | |
| 143 * @override | |
| 144 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig | |
| 145 * @param {boolean} shouldJump | |
| 146 * @param {boolean=} jumpBackwards | |
| 147 */ | |
| 148 performSearch: function(searchConfig, shouldJump, jumpBackwards) | |
| 149 { | |
| 150 var newIndex = this._currentSearchFocusIndex; | |
| 151 var previousSearchFocusElement = this._currentSearchTreeElements[newInde x]; | |
| 152 this.searchCanceled(); | |
| 153 var regEx = searchConfig.toSearchRegex(true); | |
| 154 var currentElement = this._treeOutline.rootElement(); | |
| 155 | |
| 156 while (currentElement) { | |
| 157 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.
| |
| 158 var hasMatch = currentElement.setSearchRegex(regEx); | |
| 159 if (hasMatch) | |
| 160 this._currentSearchTreeElements.push(currentElement); | |
| 161 var currentIndex = this._currentSearchTreeElements.length - 1; | |
| 162 if (previousSearchFocusElement === currentElement) { | |
| 163 if(hasMatch) | |
|
lushnikov
2016/05/04 17:00:48
style: space after if
allada
2016/05/04 21:22:36
Done.
| |
| 164 newIndex = currentIndex; | |
| 165 else if (jumpBackwards) | |
| 166 newIndex = currentIndex - 1; | |
| 167 else | |
| 168 newIndex = currentIndex + 1; | |
| 169 } | |
| 170 } | |
| 171 currentElement = currentElement.traverseNextTreeElement(false); | |
| 172 } | |
| 173 this._updateSearchCount(this._currentSearchTreeElements.length); | |
| 174 | |
| 175 if (!this._currentSearchTreeElements.length) { | |
| 176 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.
| |
| 177 return; | |
| 178 } | |
| 179 newIndex = mod(newIndex, this._currentSearchTreeElements.length); | |
| 180 | |
| 181 this._lastSearchConfig = /** @type {?WebInspector.SearchableView.SearchC onfig} */ (searchConfig); | |
| 182 this._goToMatch(newIndex, shouldJump); | |
| 183 }, | |
| 184 | |
| 185 /** | |
| 186 * @override | |
| 187 */ | |
| 188 jumpToNextSearchResult: function() | |
| 189 { | |
| 190 if (!this._currentSearchTreeElements.length) | |
| 191 return; | |
| 192 | |
| 193 var newIndex = mod(this._currentSearchFocusIndex + 1, this._currentSearc hTreeElements.length); | |
| 194 this._goToMatch(newIndex, true); | |
| 195 }, | |
| 196 | |
| 197 /** | |
| 198 * @override | |
| 199 */ | |
| 200 jumpToPreviousSearchResult: function() | |
| 201 { | |
| 202 if (!this._currentSearchTreeElements.length) | |
| 203 return; | |
| 204 | |
| 205 var newIndex = mod(this._currentSearchFocusIndex - 1, this._currentSearc hTreeElements.length); | |
| 206 this._goToMatch(newIndex, true); | |
| 207 }, | |
| 208 | |
| 209 /** | |
| 210 * @override | |
| 211 * @return {boolean} | |
| 212 */ | |
| 213 supportsCaseSensitiveSearch: function() | |
| 214 { | |
| 215 return true; | |
| 216 }, | |
| 217 | |
| 218 /** | |
| 219 * @override | |
| 220 * @return {boolean} | |
| 221 */ | |
| 222 supportsRegexSearch: function() | |
| 223 { | |
| 224 return true; | |
| 225 }, | |
| 226 | |
| 39 __proto__: WebInspector.Widget.prototype | 227 __proto__: WebInspector.Widget.prototype |
| 40 } | 228 } |
| 41 | 229 |
| 42 /** | 230 /** |
| 43 * @constructor | 231 * @constructor |
| 44 * @extends {TreeElement} | 232 * @extends {TreeElement} |
| 45 * @param {!Node} node | 233 * @param {!Node} node |
| 46 * @param {boolean} closeTag | 234 * @param {boolean} closeTag |
| 235 * @param {!WebInspector.XMLView} xmlView | |
| 47 */ | 236 */ |
| 48 WebInspector.XMLView.Node = function(node, closeTag) | 237 WebInspector.XMLView.Node = function(node, closeTag, xmlView) |
| 49 { | 238 { |
| 50 TreeElement.call(this, "", !closeTag && !!node.childElementCount); | 239 TreeElement.call(this, "", !closeTag && !!node.childNodes.length); |
| 51 this._node = node; | 240 this._node = node; |
| 52 this._closeTag = closeTag; | 241 this._closeTag = closeTag; |
| 53 this.selectable = false; | 242 this.selectable = false; |
| 243 /** @type {!Array.<!Object>} */ | |
| 244 this._highlightChanges = []; | |
| 54 this._updateTitle(); | 245 this._updateTitle(); |
| 246 this._xmlView = xmlView; | |
| 55 } | 247 } |
| 56 | 248 |
| 57 /** | 249 /** |
| 58 * @param {!TreeOutline|!TreeElement} root | 250 * @param {!TreeOutline|!TreeElement} root |
| 59 * @param {!Node} xmlNode | 251 * @param {!Node} xmlNode |
| 252 * @param {!WebInspector.XMLView} xmlView | |
| 60 */ | 253 */ |
| 61 WebInspector.XMLView.Node.populate = function(root, xmlNode) | 254 WebInspector.XMLView.Node.populate = function(root, xmlNode, xmlView) |
| 62 { | 255 { |
| 63 var node = xmlNode.firstChild; | 256 var node = xmlNode.firstChild; |
| 64 while (node) { | 257 while (node) { |
| 65 var currentNode = node; | 258 var currentNode = node; |
| 66 node = node.nextSibling; | 259 node = node.nextSibling; |
| 67 var nodeType = currentNode.nodeType; | 260 var nodeType = currentNode.nodeType; |
| 68 // ignore empty TEXT | 261 // ignore empty TEXT |
| 69 if (nodeType === 3 && currentNode.nodeValue.match(/\s+/)) | 262 if (nodeType === 3 && currentNode.nodeValue.match(/\s+/)) |
| 70 continue; | 263 continue; |
| 71 // ignore ATTRIBUTE, ENTITY_REFERENCE, ENTITY, DOCUMENT, DOCUMENT_TYPE, DOCUMENT_FRAGMENT, NOTATION | 264 // ignore ATTRIBUTE, ENTITY_REFERENCE, ENTITY, DOCUMENT, DOCUMENT_TYPE, DOCUMENT_FRAGMENT, NOTATION |
| 72 if ((nodeType !== 1) && (nodeType !== 3) && (nodeType !== 4) && (nodeTyp e !== 7) && (nodeType !== 8)) | 265 if ((nodeType !== 1) && (nodeType !== 3) && (nodeType !== 4) && (nodeTyp e !== 7) && (nodeType !== 8)) |
| 73 continue; | 266 continue; |
| 74 root.appendChild(new WebInspector.XMLView.Node(currentNode, false)); | 267 root.appendChild(new WebInspector.XMLView.Node(currentNode, false, xmlVi ew)); |
| 75 } | 268 } |
| 76 } | 269 } |
| 77 | 270 |
| 78 WebInspector.XMLView.Node.prototype = { | 271 WebInspector.XMLView.Node.prototype = { |
| 272 /** | |
| 273 * @param {?RegExp} regex | |
| 274 * @param {string=} additionalCssClassName | |
| 275 * @return {boolean} | |
| 276 */ | |
| 277 setSearchRegex: function(regex, additionalCssClassName) { | |
| 278 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.
| |
| 279 if (additionalCssClassName) | |
| 280 cssClasses += " " + additionalCssClassName; | |
| 281 this.revertHighlightChanges(); | |
| 282 if (regex) | |
| 283 this._applySearch(regex, this.listItemElement, cssClasses); | |
| 284 if (this._closeTag && this.parent && !this.parent.expanded) { | |
| 285 return false; | |
| 286 } | |
| 287 return !!this._highlightChanges.length; | |
| 288 }, | |
| 289 | |
| 290 /** | |
| 291 * @param {!RegExp} regex | |
| 292 * @param {!Element} element | |
| 293 * @param {string} cssClassName | |
| 294 */ | |
| 295 _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.
| |
| 296 { | |
| 297 var ranges = []; | |
| 298 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.
| |
| 299 regex.lastIndex = 0; | |
| 300 var match = regex.exec(content); | |
| 301 while (match) { | |
| 302 ranges.push(new WebInspector.SourceRange(match.index, match[0].lengt h)); | |
| 303 match = regex.exec(content); | |
| 304 } | |
| 305 if (ranges.length) | |
| 306 WebInspector.highlightRangesWithStyleClass(element, ranges, cssClass Name, this._highlightChanges); | |
| 307 }, | |
| 308 | |
| 309 revertHighlightChanges: function() | |
| 310 { | |
| 311 WebInspector.revertDomChanges(this._highlightChanges); | |
| 312 this._highlightChanges = []; | |
| 313 }, | |
| 314 | |
| 79 _updateTitle: function() | 315 _updateTitle: function() |
| 80 { | 316 { |
| 81 var node = this._node; | 317 var node = this._node; |
| 82 switch (node.nodeType) { | 318 switch (node.nodeType) { |
| 83 case 1: // ELEMENT | 319 case 1: // ELEMENT |
| 84 var tag = node.tagName; | 320 var tag = node.tagName; |
| 85 if (this._closeTag) { | 321 if (this._closeTag) { |
| 86 this._setTitle(["</" + tag + ">", "shadow-xml-view-tag"]); | 322 this._setTitle(["</" + tag + ">", "shadow-xml-view-tag"]); |
| 87 return; | 323 return; |
| 88 } | 324 } |
| 89 var titleItems = ["<" + tag, "shadow-xml-view-tag"]; | 325 var titleItems = ["<" + tag, "shadow-xml-view-tag"]; |
| 90 var attributes = node.attributes; | 326 var attributes = node.attributes; |
| 91 for (var i = 0; i < attributes.length; ++i) { | 327 for (var i = 0; i < attributes.length; ++i) { |
| 92 var attributeNode = attributes.item(i); | 328 var attributeNode = attributes.item(i); |
| 93 titleItems.push( | 329 titleItems.push( |
| 94 "\u00a0", "shadow-xml-view-tag", | 330 "\u00a0", "shadow-xml-view-tag", |
| 95 attributeNode.name, "shadow-xml-view-attribute-name", | 331 attributeNode.name, "shadow-xml-view-attribute-name", |
| 96 "=\"", "shadow-xml-view-tag", | 332 "=\"", "shadow-xml-view-tag", |
| 97 attributeNode.value, "shadow-xml-view-attribute-value", | 333 attributeNode.value, "shadow-xml-view-attribute-value", |
| 98 "\"", "shadow-xml-view-tag") | 334 "\"", "shadow-xml-view-tag") |
| 99 } | 335 } |
| 100 if (!this.expanded) { | 336 if (!this.expanded) { |
| 101 if (node.childElementCount) { | 337 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
| |
| 102 titleItems.push( | 338 titleItems.push( |
| 103 ">", "shadow-xml-view-tag", | 339 ">", "shadow-xml-view-tag", |
| 104 "\u2026", "shadow-xml-view-comment", | 340 "\u2026", "shadow-xml-view-comment", |
| 105 "</" + tag, "shadow-xml-view-tag"); | 341 "</" + tag, "shadow-xml-view-tag"); |
| 106 } else if (this._node.textContent) { | 342 } else if (this._node.textContent) { |
| 107 titleItems.push( | 343 titleItems.push( |
| 108 ">", "shadow-xml-view-tag", | 344 ">", "shadow-xml-view-tag", |
| 109 node.textContent, "shadow-xml-view-text", | 345 node.textContent, "shadow-xml-view-text", |
| 110 "</" + tag, "shadow-xml-view-tag"); | 346 "</" + tag, "shadow-xml-view-tag"); |
| 111 } else { | 347 } else { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 135 | 371 |
| 136 /** | 372 /** |
| 137 * @param {!Array.<string>} items | 373 * @param {!Array.<string>} items |
| 138 */ | 374 */ |
| 139 _setTitle: function(items) | 375 _setTitle: function(items) |
| 140 { | 376 { |
| 141 var titleFragment = createDocumentFragment(); | 377 var titleFragment = createDocumentFragment(); |
| 142 for (var i = 0; i < items.length; i += 2) | 378 for (var i = 0; i < items.length; i += 2) |
| 143 titleFragment.createChild("span", items[i + 1]).textContent = items[ i]; | 379 titleFragment.createChild("span", items[i + 1]).textContent = items[ i]; |
| 144 this.title = titleFragment; | 380 this.title = titleFragment; |
| 381 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.
| |
| 382 this._xmlView._rerunSearch(); | |
| 145 }, | 383 }, |
| 146 | 384 |
| 147 onattach: function() | 385 onattach: function() |
| 148 { | 386 { |
| 149 this.listItemElement.classList.toggle("shadow-xml-view-close-tag", this. _closeTag); | 387 this.listItemElement.classList.toggle("shadow-xml-view-close-tag", this. _closeTag); |
| 150 }, | 388 }, |
| 151 | 389 |
| 152 onexpand: function() | 390 onexpand: function() |
| 153 { | 391 { |
| 154 this._updateTitle(); | 392 this._updateTitle(); |
| 155 }, | 393 }, |
| 156 | 394 |
| 157 oncollapse: function() | 395 oncollapse: function() |
| 158 { | 396 { |
| 159 this._updateTitle(); | 397 this._updateTitle(); |
| 160 }, | 398 }, |
| 161 | 399 |
| 162 onpopulate: function() | 400 onpopulate: function() |
| 163 { | 401 { |
| 164 WebInspector.XMLView.Node.populate(this, this._node); | 402 WebInspector.XMLView.Node.populate(this, this._node, this._xmlView); |
| 165 this.appendChild(new WebInspector.XMLView.Node(this._node, true)); | 403 this.appendChild(new WebInspector.XMLView.Node(this._node, true, this._x mlView)); |
| 166 }, | 404 }, |
| 167 | 405 |
| 168 __proto__: TreeElement.prototype | 406 __proto__: TreeElement.prototype |
| 169 } | 407 } |
| OLD | NEW |