Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js | 
| diff --git a/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js b/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js | 
| index 47c9b79fb1bab592d240aaedacbf23d13fe4f67e..aefa34b4c466619c8281fd176f51453b97828b28 100644 | 
| --- a/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js | 
| +++ b/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js | 
| @@ -43,9 +43,18 @@ function TreeOutline(nonFocusable) | 
| this._contentElement = this._rootElement._childrenListNode; | 
| this._contentElement.addEventListener("keydown", this._treeKeyDown.bind(this), true); | 
| + this.element = this._contentElement; | 
| + | 
| this.setFocusable(!nonFocusable); | 
| - this.element = this._contentElement; | 
| + this._contentElement.addEventListener("keypress", this._handleKeyPressForHighlighting.bind(this), true); | 
| 
 
lushnikov
2016/04/06 00:22:36
this.element.addEventListener...
 
allada
2016/04/09 00:42:47
Done.
 
 | 
| + this.element.addEventListener("blur", this._clearFilter.bind(this), true); | 
| 
 
lushnikov
2016/04/06 00:22:36
is capturing actually needed in these three events
 
 | 
| + this.element.addEventListener("click", this._clearFilter.bind(this), true); | 
| + | 
| + this._currentSelectionFilterString = ""; | 
| + this._interactiveFilterEnabled = false; | 
| + /** @type {!Array.<!TreeElement>} */ | 
| + this._highlightedNodes = [] | 
| } | 
| TreeOutline.Events = { | 
| @@ -75,6 +84,106 @@ TreeOutline.prototype = { | 
| }, | 
| /** | 
| + * @param {boolean} enable | 
| + */ | 
| + setInteractiveFilterable: function (enable) | 
| + { | 
| + if (!enable) | 
| + this._setCurrentSelectionFilterString(""); | 
| + | 
| + this._interactiveFilterEnabled = enable; | 
| + }, | 
| + | 
| + /** | 
| + * @param {string} filterString | 
| + */ | 
| + _setCurrentSelectionFilterString: function (filterString) | 
| + { | 
| + this._currentSelectionFilterString = filterString; | 
| + this._refreshHighlighting(); | 
| + }, | 
| + | 
| + /** | 
| + * @param {string} filterString | 
| + * @return {!RegExp} | 
| + */ | 
| + _makeFilterRegExpFromString: function (filterString) | 
| + { | 
| + return new RegExp(filterString.escapeForRegExp(), "gi") | 
| + }, | 
| + | 
| + _refreshHighlighting: function () | 
| 
 
lushnikov
2016/04/06 00:22:37
nit: _refreshHighlight:
 
allada
2016/04/09 00:42:48
Done.
 
 | 
| + { | 
| + if (!this._rootElement) | 
| + return; | 
| + | 
| + var filterRegExp = this._makeFilterRegExpFromString(this._currentSelectionFilterString); | 
| 
 
lushnikov
2016/04/06 00:22:36
nit: filterRegex =
 
allada
2016/04/09 00:42:48
Done.
 
 | 
| + | 
| + for (var changedNode of this._highlightedNodes) | 
| + changedNode._revertHighlightChanges(); | 
| + | 
| + this._highlightedNodes = []; | 
| + | 
| + if (!this._currentSelectionFilterString) | 
| + return; | 
| + | 
| + if (this.selectedTreeElement && !this.selectedTreeElement.selectable) { | 
| + if (!this.selectNext()) | 
| + this.selectPrevious(); | 
| + } | 
| + | 
| + var node = this._rootElement.firstChild(); | 
| + do { | 
| 
 
lushnikov
2016/04/06 00:22:36
nit: no need for do-while; simple while will work
 
allada
2016/04/09 00:42:48
Done.
 
 | 
| + if (node._applyHighlightFilter(filterRegExp)) | 
| + this._highlightedNodes.push(node); | 
| + node = node.traverseNextTreeElement(true, null, true); | 
| + } while(node); | 
| + }, | 
| + | 
| + /** | 
| + * @param {!TreeElement} treeElement | 
| + * @return {boolean} | 
| + */ | 
| + _checkFilter: function (treeElement) | 
| 
 
lushnikov
2016/04/06 00:22:36
Let's move this method into TreeElement; treeEleme
 
allada
2016/04/09 00:42:48
Done.
 
 | 
| + { | 
| + return this._currentSelectionFilterString ? this._makeFilterRegExpFromString(this._currentSelectionFilterString).test(treeElement._titleElement.textContent) : true; | 
| + }, | 
| + | 
| + _clearFilter: function () | 
| + { | 
| + if (this._interactiveFilterEnabled) | 
| + this._setCurrentSelectionFilterString(""); | 
| + }, | 
| + | 
| + /** | 
| + * @param {!Event} event | 
| + */ | 
| + _handleKeyPressForHighlighting: function (event) | 
| + { | 
| + if (!this._interactiveFilterEnabled) | 
| + return; | 
| + | 
| + if (event.target !== this._contentElement) | 
| + return; | 
| + | 
| + if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey) | 
| + return; | 
| + | 
| + var currentFilterString = this._currentSelectionFilterString; | 
| + | 
| + switch (event.data) { | 
| + case "\r": | 
| + case "\n": | 
| + break; | 
| + case " ": | 
| + if (!currentFilterString) | 
| + break; | 
| + default: | 
| + this._setCurrentSelectionFilterString(currentFilterString + event.data); | 
| + } | 
| + }, | 
| + | 
| + /** | 
| * @return {?TreeElement} | 
| */ | 
| firstChild: function() | 
| @@ -152,9 +261,9 @@ TreeOutline.prototype = { | 
| setFocusable: function(focusable) | 
| { | 
| if (focusable) | 
| - this._contentElement.setAttribute("tabIndex", 0); | 
| + this.element.setAttribute("tabIndex", 0); | 
| 
 
lushnikov
2016/04/06 00:22:36
this and below seems to be unnecessary
 
allada
2016/04/09 00:42:48
This is needed to be able to capture keyboard even
 
 | 
| else | 
| - this._contentElement.removeAttribute("tabIndex"); | 
| + this.element.removeAttribute("tabIndex"); | 
| }, | 
| focus: function() | 
| @@ -229,30 +338,38 @@ TreeOutline.prototype = { | 
| if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey) | 
| return; | 
| + var currentFilterString = this._currentSelectionFilterString; | 
| var handled = false; | 
| + var key = event.keyCode; | 
| var nextSelectedElement; | 
| - if (event.keyIdentifier === "Up" && !event.altKey) { | 
| - handled = this.selectPrevious(); | 
| - } else if (event.keyIdentifier === "Down" && !event.altKey) { | 
| - handled = this.selectNext(); | 
| - } else if (event.keyIdentifier === "Left") { | 
| - if (this.selectedTreeElement.expanded) { | 
| - if (event.altKey) | 
| - this.selectedTreeElement.collapseRecursively(); | 
| - else | 
| - this.selectedTreeElement.collapse(); | 
| + | 
| + switch (key) { | 
| + case WebInspector.KeyboardShortcut.Keys.Esc.code: | 
| + if (this._interactiveFilterEnabled) { | 
| + if (currentFilterString) | 
| + handled = true; | 
| + this._clearFilter(); | 
| + } | 
| + break; | 
| + case WebInspector.KeyboardShortcut.Keys.Delete.code: | 
| + if (this._interactiveFilterEnabled && currentFilterString) { | 
| handled = true; | 
| - } else if (this.selectedTreeElement.parent && !this.selectedTreeElement.parent.root) { | 
| + this._clearFilter(); | 
| + } else | 
| + handled = this.selectedTreeElement.ondelete(); | 
| + break; | 
| + case WebInspector.KeyboardShortcut.Keys.Backspace.code: | 
| + if (this._interactiveFilterEnabled && currentFilterString) { | 
| handled = true; | 
| - if (this.selectedTreeElement.parent.selectable) { | 
| - nextSelectedElement = this.selectedTreeElement.parent; | 
| - while (nextSelectedElement && !nextSelectedElement.selectable) | 
| - nextSelectedElement = nextSelectedElement.parent; | 
| - handled = nextSelectedElement ? true : false; | 
| - } else if (this.selectedTreeElement.parent) | 
| - this.selectedTreeElement.parent.collapse(); | 
| + this._setCurrentSelectionFilterString(currentFilterString.substr(0, currentFilterString.length - 1)); | 
| + } else { | 
| + handled = this.selectedTreeElement.ondelete(); | 
| } | 
| - } else if (event.keyIdentifier === "Right") { | 
| + break; | 
| + case WebInspector.KeyboardShortcut.Keys.Right.code: | 
| + if (this._interactiveFilterEnabled) | 
| + this._clearFilter(); | 
| + | 
| if (!this.selectedTreeElement.revealed()) { | 
| this.selectedTreeElement.reveal(); | 
| handled = true; | 
| @@ -262,6 +379,7 @@ TreeOutline.prototype = { | 
| nextSelectedElement = this.selectedTreeElement.firstChild(); | 
| while (nextSelectedElement && !nextSelectedElement.selectable) | 
| nextSelectedElement = nextSelectedElement.nextSibling; | 
| + | 
| 
 
lushnikov
2016/04/06 00:22:36
nit: stray line
 
allada
2016/04/09 00:42:47
Done.
 
 | 
| handled = nextSelectedElement ? true : false; | 
| } else { | 
| if (event.altKey) | 
| @@ -270,12 +388,51 @@ TreeOutline.prototype = { | 
| this.selectedTreeElement.expand(); | 
| } | 
| } | 
| - } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /* Delete */) | 
| - handled = this.selectedTreeElement.ondelete(); | 
| - else if (isEnterKey(event)) | 
| - handled = this.selectedTreeElement.onenter(); | 
| - else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code) | 
| - handled = this.selectedTreeElement.onspace(); | 
| + break; | 
| + case WebInspector.KeyboardShortcut.Keys.Left.code: | 
| + if (this._interactiveFilterEnabled) | 
| + this._clearFilter(); | 
| + | 
| + if (this.selectedTreeElement.expanded) { | 
| 
 
lushnikov
2016/04/06 00:22:36
Are there any changes in this if-else-if code?
ni
 
allada
2016/04/09 00:42:48
Done.
 
 | 
| + if (event.altKey) | 
| + this.selectedTreeElement.collapseRecursively(); | 
| + else | 
| + this.selectedTreeElement.collapse(); | 
| + | 
| + handled = true; | 
| + } else if (this.selectedTreeElement.parent && !this.selectedTreeElement.parent.root) { | 
| + handled = true; | 
| + if (this.selectedTreeElement.parent.selectable) { | 
| + nextSelectedElement = this.selectedTreeElement.parent; | 
| + while (nextSelectedElement && !nextSelectedElement.selectable) | 
| + nextSelectedElement = nextSelectedElement.parent; | 
| + | 
| + handled = nextSelectedElement ? true : false; | 
| + } else if (this.selectedTreeElement.parent) { | 
| + this.selectedTreeElement.parent.collapse(); | 
| + } | 
| + } | 
| + break; | 
| + case WebInspector.KeyboardShortcut.Keys.Down.code: | 
| + if (!event.altKey) | 
| + handled = this.selectNext(); | 
| + break; | 
| + case WebInspector.KeyboardShortcut.Keys.Up.code: | 
| + if (!event.altKey) | 
| + handled = this.selectPrevious(); | 
| + break; | 
| + case WebInspector.KeyboardShortcut.Keys.Space.code: | 
| + if (!currentFilterString) | 
| + handled = this.selectedTreeElement.onspace(); | 
| + break; | 
| + default: | 
| + if (isEnterKey(event)) { | 
| + if (this._interactiveFilterEnabled) | 
| + this._clearFilter(); | 
| + | 
| + handled = this.selectedTreeElement.onenter(); | 
| + } | 
| + } | 
| if (nextSelectedElement) { | 
| nextSelectedElement.reveal(); | 
| @@ -377,6 +534,9 @@ function TreeElement(title, expandable) | 
| this.selected = false; | 
| this.setExpandable(expandable || false); | 
| this._collapsible = true; | 
| + | 
| + /** @type {!Array.<!Object>} */ | 
| + this._highlightChanges = []; | 
| } | 
| /** @const */ | 
| @@ -384,6 +544,36 @@ TreeElement._ArrowToggleWidth = 10; | 
| TreeElement.prototype = { | 
| /** | 
| + * @param {!RegExp} regex | 
| + * @return {boolean} | 
| + */ | 
| + _applyHighlightFilter: function (regex) { | 
| + var textContent = this._listItemNode.textContent; | 
| + var ranges = []; | 
| + | 
| + this._revertHighlightChanges(); | 
| + | 
| + var match = regex.exec(textContent); | 
| + while (match) { | 
| + ranges.push(new WebInspector.SourceRange(match.index, match[0].length)); | 
| + match = regex.exec(textContent); | 
| + } | 
| + if (ranges.length) | 
| + WebInspector.highlightRangesWithStyleClass(this._listItemNode, ranges, "tree-text-interactive-highlight", this._highlightChanges); | 
| + | 
| + if (this._highlightChanges.length) | 
| 
 
lushnikov
2016/04/06 00:22:36
return !!this._highlightChanges.length;
 
allada
2016/04/09 00:42:47
Done.
 
 | 
| + return true; | 
| + | 
| + return false; | 
| + }, | 
| + | 
| + _revertHighlightChanges: function () | 
| + { | 
| + WebInspector.revertDomChanges(this._highlightChanges); | 
| + this._highlightChanges = []; | 
| + }, | 
| + | 
| + /** | 
| * @param {?TreeElement} ancestor | 
| * @return {boolean} | 
| */ | 
| @@ -603,7 +793,7 @@ TreeElement.prototype = { | 
| get selectable() | 
| { | 
| - if (this._hidden) | 
| + if (this._hidden || !this.treeOutline._checkFilter(this)) | 
| return false; | 
| return this._selectable; | 
| }, |