Web Inspector: Convert TreeElement classes to ES6
https://bugs.webkit.org/show_bug.cgi?id=143111

Reviewed by Joseph Pecoraro.

* UserInterface/Views/ApplicationCacheFrameTreeElement.js:
* UserInterface/Views/ApplicationCacheManifestTreeElement.js:
* UserInterface/Views/BreakpointTreeElement.js:
* UserInterface/Views/CallFrameTreeElement.js:
* UserInterface/Views/ContentFlowTreeElement.js:
* UserInterface/Views/CookieStorageTreeElement.js:
* UserInterface/Views/DOMStorageTreeElement.js:
* UserInterface/Views/DOMTreeElement.js:
* UserInterface/Views/DOMTreeOutline.js:
* UserInterface/Views/DatabaseHostTreeElement.js:
* UserInterface/Views/DatabaseTableTreeElement.js:
* UserInterface/Views/DatabaseTreeElement.js:
* UserInterface/Views/FolderTreeElement.js:
* UserInterface/Views/FolderizedTreeElement.js:
* UserInterface/Views/FrameTreeElement.js:
* UserInterface/Views/GeneralTreeElement.js:
* UserInterface/Views/IndexedDatabaseHostTreeElement.js:
* UserInterface/Views/IndexedDatabaseObjectStoreIndexTreeElement.js:
* UserInterface/Views/IndexedDatabaseObjectStoreTreeElement.js:
* UserInterface/Views/IndexedDatabaseTreeElement.js:
* UserInterface/Views/LegacyConsoleMessageImpl.js:
* UserInterface/Views/LogTreeElement.js:
* UserInterface/Views/NavigationSidebarPanel.js:
* UserInterface/Views/ObjectTreeArrayIndexTreeElement.js:
* UserInterface/Views/ObjectTreeBaseTreeElement.js:
* UserInterface/Views/ObjectTreeMapEntryTreeElement.js:
* UserInterface/Views/ObjectTreePropertyTreeElement.js:
* UserInterface/Views/ObjectTreeSetIndexTreeElement.js:
* UserInterface/Views/ObjectTreeView.js:
* UserInterface/Views/ProfileNodeTreeElement.js:
* UserInterface/Views/PropertiesSection.js:
* UserInterface/Views/ResourceTreeElement.js:
* UserInterface/Views/ScriptTreeElement.js:
* UserInterface/Views/SearchResultTreeElement.js:
* UserInterface/Views/SourceCodeTimelineTreeElement.js:
* UserInterface/Views/SourceCodeTreeElement.js:
* UserInterface/Views/SourceMapResourceTreeElement.js:
* UserInterface/Views/StorageTreeElement.js:
* UserInterface/Views/TimelineDataGrid.js:
* UserInterface/Views/TimelineRecordTreeElement.js:
* UserInterface/Views/TreeElementStatusButton.js:
* UserInterface/Views/TreeOutline.js:
* UserInterface/Views/TreeOutlineDataGridSynchronizer.js:
* UserInterface/Views/TypePropertiesSection.js:
Converted to ES6 classes where possible.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@182042 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js b/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js
index 27cb058..2c0f9de 100644
--- a/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js
+++ b/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, 2013 Apple Inc.  All rights reserved.
+ * Copyright (C) 2007, 2013, 2015 Apple Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,187 +26,147 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-function TreeOutline(listNode)
+WebInspector.TreeOutline = class TreeOutline extends WebInspector.Object
 {
-    // FIXME: Convert this to a WebInspector.Object subclass, and call super().
-    // WebInspector.Object.call(this);
+    constructor(listNode)
+    {
+        super();
 
-    this.element = listNode;
+        this.element = listNode;
 
-    this.children = [];
-    this.selectedTreeElement = null;
-    this._childrenListNode = listNode;
-    this._childrenListNode.removeChildren();
-    this._knownTreeElements = [];
-    this._treeElementsExpandedState = [];
-    this.expandTreeElementsWhenArrowing = false;
-    this.allowsRepeatSelection = false;
-    this.root = true;
-    this.hasChildren = false;
-    this.expanded = true;
-    this.selected = false;
-    this.treeOutline = this;
-
-    this._childrenListNode.tabIndex = 0;
-    this._childrenListNode.addEventListener("keydown", this._treeKeyDown.bind(this), true);
-}
-
-TreeOutline._knownTreeElementNextIdentifier = 1;
-TreeOutline.prototype.constructor = TreeOutline;
-
-TreeOutline.prototype.appendChild = function(child)
-{
-    if (!child)
-        throw "child can't be undefined or null";
-
-    var lastChild = this.children[this.children.length - 1];
-    if (lastChild) {
-        lastChild.nextSibling = child;
-        child.previousSibling = lastChild;
-    } else {
-        child.previousSibling = null;
-        child.nextSibling = null;
-    }
-
-    var isFirstChild = !this.children.length;
-
-    this.children.push(child);
-    this.hasChildren = true;
-    child.parent = this;
-    child.treeOutline = this.treeOutline;
-    child.treeOutline._rememberTreeElement(child);
-
-    var current = child.children[0];
-    while (current) {
-        current.treeOutline = this.treeOutline;
-        current.treeOutline._rememberTreeElement(current);
-        current = current.traverseNextTreeElement(false, child, true);
-    }
-
-    if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined)
-        child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier];
-
-    if (this._childrenListNode)
-        child._attach();
-
-    if (this.treeOutline.onadd)
-        this.treeOutline.onadd(child);
-
-    if (isFirstChild && this.expanded)
-        this.expand();
-};
-
-TreeOutline.prototype.insertChild = function(child, index)
-{
-    if (!child)
-        throw "child can't be undefined or null";
-
-    var previousChild = (index > 0 ? this.children[index - 1] : null);
-    if (previousChild) {
-        previousChild.nextSibling = child;
-        child.previousSibling = previousChild;
-    } else {
-        child.previousSibling = null;
-    }
-
-    var nextChild = this.children[index];
-    if (nextChild) {
-        nextChild.previousSibling = child;
-        child.nextSibling = nextChild;
-    } else {
-        child.nextSibling = null;
-    }
-
-    var isFirstChild = !this.children.length;
-
-    this.children.splice(index, 0, child);
-    this.hasChildren = true;
-    child.parent = this;
-    child.treeOutline = this.treeOutline;
-    child.treeOutline._rememberTreeElement(child);
-
-    var current = child.children[0];
-    while (current) {
-        current.treeOutline = this.treeOutline;
-        current.treeOutline._rememberTreeElement(current);
-        current = current.traverseNextTreeElement(false, child, true);
-    }
-
-    if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined)
-        child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier];
-
-    if (this._childrenListNode)
-        child._attach();
-
-    if (this.treeOutline.onadd)
-        this.treeOutline.onadd(child);
-
-    if (isFirstChild && this.expanded)
-        this.expand();
-};
-
-TreeOutline.prototype.removeChildAtIndex = function(childIndex, suppressOnDeselect, suppressSelectSibling)
-{
-    if (childIndex < 0 || childIndex >= this.children.length)
-        throw "childIndex out of range";
-
-    var child = this.children[childIndex];
-    this.children.splice(childIndex, 1);
-
-    var parent = child.parent;
-    if (child.deselect(suppressOnDeselect)) {
-        if (child.previousSibling && !suppressSelectSibling)
-            child.previousSibling.select(true, false);
-        else if (child.nextSibling && !suppressSelectSibling)
-            child.nextSibling.select(true, false);
-        else if (!suppressSelectSibling)
-            parent.select(true, false);
-    }
-
-    if (child.previousSibling)
-        child.previousSibling.nextSibling = child.nextSibling;
-    if (child.nextSibling)
-        child.nextSibling.previousSibling = child.previousSibling;
-
-    if (child.treeOutline) {
-        child.treeOutline._forgetTreeElement(child);
-        child.treeOutline._forgetChildrenRecursive(child);
-    }
-
-    child._detach();
-    child.treeOutline = null;
-    child.parent = null;
-    child.nextSibling = null;
-    child.previousSibling = null;
-
-    if (this.treeOutline && this.treeOutline.onremove)
-        this.treeOutline.onremove(child);
-};
-
-TreeOutline.prototype.removeChild = function(child, suppressOnDeselect, suppressSelectSibling)
-{
-    if (!child)
-        throw "child can't be undefined or null";
-
-    var childIndex = this.children.indexOf(child);
-    if (childIndex === -1)
-        throw "child not found in this node's children";
-
-    this.removeChildAtIndex(childIndex, suppressOnDeselect, suppressSelectSibling);
-
-    if (!this.children.length) {
-        this._listItemNode.classList.remove("parent");
+        this.children = [];
+        this.selectedTreeElement = null;
+        this._childrenListNode = listNode;
+        this._childrenListNode.removeChildren();
+        this._knownTreeElements = [];
+        this._treeElementsExpandedState = [];
+        this.expandTreeElementsWhenArrowing = false;
+        this.allowsRepeatSelection = false;
+        this.root = true;
         this.hasChildren = false;
+        this.expanded = true;
+        this.selected = false;
+        this.treeOutline = this;
+
+        this._childrenListNode.tabIndex = 0;
+        this._childrenListNode.addEventListener("keydown", this._treeKeyDown.bind(this), true);
     }
-};
 
-TreeOutline.prototype.removeChildren = function(suppressOnDeselect)
-{
-    var treeOutline = this.treeOutline;
+    // Methods
 
-    for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i];
-        child.deselect(suppressOnDeselect);
+    appendChild(child)
+    {
+        if (!child)
+            throw "child can't be undefined or null";
+
+        var lastChild = this.children[this.children.length - 1];
+        if (lastChild) {
+            lastChild.nextSibling = child;
+            child.previousSibling = lastChild;
+        } else {
+            child.previousSibling = null;
+            child.nextSibling = null;
+        }
+
+        var isFirstChild = !this.children.length;
+
+        this.children.push(child);
+        this.hasChildren = true;
+        child.parent = this;
+        child.treeOutline = this.treeOutline;
+        child.treeOutline._rememberTreeElement(child);
+
+        var current = child.children[0];
+        while (current) {
+            current.treeOutline = this.treeOutline;
+            current.treeOutline._rememberTreeElement(current);
+            current = current.traverseNextTreeElement(false, child, true);
+        }
+
+        if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined)
+            child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier];
+
+        if (this._childrenListNode)
+            child._attach();
+
+        if (this.treeOutline.onadd)
+            this.treeOutline.onadd(child);
+
+        if (isFirstChild && this.expanded)
+            this.expand();
+    }
+
+    insertChild(child, index)
+    {
+        if (!child)
+            throw "child can't be undefined or null";
+
+        var previousChild = (index > 0 ? this.children[index - 1] : null);
+        if (previousChild) {
+            previousChild.nextSibling = child;
+            child.previousSibling = previousChild;
+        } else {
+            child.previousSibling = null;
+        }
+
+        var nextChild = this.children[index];
+        if (nextChild) {
+            nextChild.previousSibling = child;
+            child.nextSibling = nextChild;
+        } else {
+            child.nextSibling = null;
+        }
+
+        var isFirstChild = !this.children.length;
+
+        this.children.splice(index, 0, child);
+        this.hasChildren = true;
+        child.parent = this;
+        child.treeOutline = this.treeOutline;
+        child.treeOutline._rememberTreeElement(child);
+
+        var current = child.children[0];
+        while (current) {
+            current.treeOutline = this.treeOutline;
+            current.treeOutline._rememberTreeElement(current);
+            current = current.traverseNextTreeElement(false, child, true);
+        }
+
+        if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined)
+            child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier];
+
+        if (this._childrenListNode)
+            child._attach();
+
+        if (this.treeOutline.onadd)
+            this.treeOutline.onadd(child);
+
+        if (isFirstChild && this.expanded)
+            this.expand();
+    }
+
+    removeChildAtIndex(childIndex, suppressOnDeselect, suppressSelectSibling)
+    {
+        if (childIndex < 0 || childIndex >= this.children.length)
+            throw "childIndex out of range";
+
+        var child = this.children[childIndex];
+        this.children.splice(childIndex, 1);
+
+        var parent = child.parent;
+        if (child.deselect(suppressOnDeselect)) {
+            if (child.previousSibling && !suppressSelectSibling)
+                child.previousSibling.select(true, false);
+            else if (child.nextSibling && !suppressSelectSibling)
+                child.nextSibling.select(true, false);
+            else if (!suppressSelectSibling)
+                parent.select(true, false);
+        }
+
+        if (child.previousSibling)
+            child.previousSibling.nextSibling = child.nextSibling;
+        if (child.nextSibling)
+            child.nextSibling.previousSibling = child.previousSibling;
 
         if (child.treeOutline) {
             child.treeOutline._forgetTreeElement(child);
@@ -219,376 +179,439 @@
         child.nextSibling = null;
         child.previousSibling = null;
 
-        if (treeOutline && treeOutline.onremove)
-            treeOutline.onremove(child);
+        if (this.treeOutline && this.treeOutline.onremove)
+            this.treeOutline.onremove(child);
     }
 
-    this.children = [];
-};
+    removeChild(child, suppressOnDeselect, suppressSelectSibling)
+    {
+        if (!child)
+            throw "child can't be undefined or null";
 
-TreeOutline.prototype.removeChildrenRecursive = function(suppressOnDeselect)
-{
-    var childrenToRemove = this.children;
+        var childIndex = this.children.indexOf(child);
+        if (childIndex === -1)
+            throw "child not found in this node's children";
 
-    var treeOutline = this.treeOutline;
+        this.removeChildAtIndex(childIndex, suppressOnDeselect, suppressSelectSibling);
 
-    var child = this.children[0];
-    while (child) {
-        if (child.children.length)
-            childrenToRemove = childrenToRemove.concat(child.children);
-        child = child.traverseNextTreeElement(false, this, true);
-    }
-
-    for (var i = 0; i < childrenToRemove.length; ++i) {
-        child = childrenToRemove[i];
-        child.deselect(suppressOnDeselect);
-
-        if (child.treeOutline)
-            child.treeOutline._forgetTreeElement(child);
-
-        child._detach();
-        child.children = [];
-        child.treeOutline = null;
-        child.parent = null;
-        child.nextSibling = null;
-        child.previousSibling = null;
-
-        if (treeOutline && treeOutline.onremove)
-            treeOutline.onremove(child);
-    }
-
-    this.children = [];
-};
-
-TreeOutline.prototype._rememberTreeElement = function(element)
-{
-    if (!this._knownTreeElements[element.identifier])
-        this._knownTreeElements[element.identifier] = [];
-
-    // check if the element is already known
-    var elements = this._knownTreeElements[element.identifier];
-    if (elements.indexOf(element) !== -1)
-        return;
-
-    // add the element
-    elements.push(element);
-};
-
-TreeOutline.prototype._forgetTreeElement = function(element)
-{
-    if (this.selectedTreeElement === element)
-        this.selectedTreeElement = null;
-    if (this._knownTreeElements[element.identifier])
-        this._knownTreeElements[element.identifier].remove(element, true);
-};
-
-TreeOutline.prototype._forgetChildrenRecursive = function(parentElement)
-{
-    var child = parentElement.children[0];
-    while (child) {
-        this._forgetTreeElement(child);
-        child = child.traverseNextTreeElement(false, parentElement, true);
-    }
-};
-
-TreeOutline.prototype.getCachedTreeElement = function(representedObject)
-{
-    if (!representedObject)
-        return null;
-
-    if (representedObject.__treeElementIdentifier) {
-        // If this representedObject has a tree element identifier, and it is a known TreeElement
-        // in our tree we can just return that tree element.
-        var elements = this._knownTreeElements[representedObject.__treeElementIdentifier];
-        if (elements) {
-            for (var i = 0; i < elements.length; ++i)
-                if (elements[i].representedObject === representedObject)
-                    return elements[i];
-        }
-    }
-    return null;
-};
-
-TreeOutline.prototype.findTreeElement = function(representedObject, isAncestor, getParent)
-{
-    if (!representedObject)
-        return null;
-
-    var cachedElement = this.getCachedTreeElement(representedObject);
-    if (cachedElement)
-        return cachedElement;
-
-    // The representedObject isn't known, so we start at the top of the tree and work down to find the first
-    // tree element that represents representedObject or one of its ancestors.
-    var item;
-    var found = false;
-    for (var i = 0; i < this.children.length; ++i) {
-        item = this.children[i];
-        if (item.representedObject === representedObject || (isAncestor && isAncestor(item.representedObject, representedObject))) {
-            found = true;
-            break;
+        if (!this.children.length) {
+            this._listItemNode.classList.remove("parent");
+            this.hasChildren = false;
         }
     }
 
-    if (!found)
-        return null;
+    removeChildren(suppressOnDeselect)
+    {
+        var treeOutline = this.treeOutline;
 
-    // Make sure the item that we found is connected to the root of the tree.
-    // Build up a list of representedObject's ancestors that aren't already in our tree.
-    var ancestors = [];
-    var currentObject = representedObject;
-    while (currentObject) {
-        ancestors.unshift(currentObject);
-        if (currentObject === item.representedObject)
-            break;
-        currentObject = getParent(currentObject);
-    }
+        for (var i = 0; i < this.children.length; ++i) {
+            var child = this.children[i];
+            child.deselect(suppressOnDeselect);
 
-    // For each of those ancestors we populate them to fill in the tree.
-    for (var i = 0; i < ancestors.length; ++i) {
-        // Make sure we don't call findTreeElement with the same representedObject
-        // again, to prevent infinite recursion.
-        if (ancestors[i] === representedObject)
-            continue;
+            if (child.treeOutline) {
+                child.treeOutline._forgetTreeElement(child);
+                child.treeOutline._forgetChildrenRecursive(child);
+            }
 
-        // FIXME: we could do something faster than findTreeElement since we will know the next
-        // ancestor exists in the tree.
-        item = this.findTreeElement(ancestors[i], isAncestor, getParent);
-        if (item)
-            item.onpopulate();
-    }
+            child._detach();
+            child.treeOutline = null;
+            child.parent = null;
+            child.nextSibling = null;
+            child.previousSibling = null;
 
-    return this.getCachedTreeElement(representedObject);
-};
-
-TreeOutline.prototype._treeElementDidChange = function(treeElement)
-{
-    if (treeElement.treeOutline !== this)
-        return;
-
-    if (this.onchange)
-        this.onchange(treeElement);
-};
-
-TreeOutline.prototype.treeElementFromNode = function(node)
-{
-    var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(["ol", "li"]);
-    if (listNode)
-        return listNode.parentTreeElement || listNode.treeElement;
-    return null;
-};
-
-TreeOutline.prototype.treeElementFromPoint = function(x, y)
-{
-    var node = this._childrenListNode.ownerDocument.elementFromPoint(x, y);
-    if (!node)
-        return null;
-
-    return this.treeElementFromNode(node);
-};
-
-TreeOutline.prototype._treeKeyDown = function(event)
-{
-    if (event.target !== this._childrenListNode)
-        return;
-
-    if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey)
-        return;
-
-    var handled = false;
-    var nextSelectedElement;
-    if (event.keyIdentifier === "Up" && !event.altKey) {
-        nextSelectedElement = this.selectedTreeElement.traversePreviousTreeElement(true);
-        while (nextSelectedElement && !nextSelectedElement.selectable)
-            nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!this.expandTreeElementsWhenArrowing);
-        handled = nextSelectedElement ? true : false;
-    } else if (event.keyIdentifier === "Down" && !event.altKey) {
-        nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(true);
-        while (nextSelectedElement && !nextSelectedElement.selectable)
-            nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.expandTreeElementsWhenArrowing);
-        handled = nextSelectedElement ? true : false;
-    } else if (event.keyIdentifier === "Left") {
-        if (this.selectedTreeElement.expanded) {
-            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();
+            if (treeOutline && treeOutline.onremove)
+                treeOutline.onremove(child);
         }
-    } else if (event.keyIdentifier === "Right") {
-        if (!this.selectedTreeElement.revealed()) {
-            this.selectedTreeElement.reveal();
-            handled = true;
-        } else if (this.selectedTreeElement.hasChildren) {
-            handled = true;
-            if (this.selectedTreeElement.expanded) {
-                nextSelectedElement = this.selectedTreeElement.children[0];
-                while (nextSelectedElement && !nextSelectedElement.selectable)
-                    nextSelectedElement = nextSelectedElement.nextSibling;
-                handled = nextSelectedElement ? true : false;
-            } else {
-                if (event.altKey)
-                    this.selectedTreeElement.expandRecursively();
-                else
-                    this.selectedTreeElement.expand();
+
+        this.children = [];
+    }
+
+    removeChildrenRecursive(suppressOnDeselect)
+    {
+        var childrenToRemove = this.children;
+
+        var treeOutline = this.treeOutline;
+
+        var child = this.children[0];
+        while (child) {
+            if (child.children.length)
+                childrenToRemove = childrenToRemove.concat(child.children);
+            child = child.traverseNextTreeElement(false, this, true);
+        }
+
+        for (var i = 0; i < childrenToRemove.length; ++i) {
+            child = childrenToRemove[i];
+            child.deselect(suppressOnDeselect);
+
+            if (child.treeOutline)
+                child.treeOutline._forgetTreeElement(child);
+
+            child._detach();
+            child.children = [];
+            child.treeOutline = null;
+            child.parent = null;
+            child.nextSibling = null;
+            child.previousSibling = null;
+
+            if (treeOutline && treeOutline.onremove)
+                treeOutline.onremove(child);
+        }
+
+        this.children = [];
+    }
+
+    _rememberTreeElement(element)
+    {
+        if (!this._knownTreeElements[element.identifier])
+            this._knownTreeElements[element.identifier] = [];
+
+        // check if the element is already known
+        var elements = this._knownTreeElements[element.identifier];
+        if (elements.indexOf(element) !== -1)
+            return;
+
+        // add the element
+        elements.push(element);
+    }
+
+    _forgetTreeElement(element)
+    {
+        if (this.selectedTreeElement === element)
+            this.selectedTreeElement = null;
+        if (this._knownTreeElements[element.identifier])
+            this._knownTreeElements[element.identifier].remove(element, true);
+    }
+
+    _forgetChildrenRecursive(parentElement)
+    {
+        var child = parentElement.children[0];
+        while (child) {
+            this._forgetTreeElement(child);
+            child = child.traverseNextTreeElement(false, parentElement, true);
+        }
+    }
+
+    getCachedTreeElement(representedObject)
+    {
+        if (!representedObject)
+            return null;
+
+        if (representedObject.__treeElementIdentifier) {
+            // If this representedObject has a tree element identifier, and it is a known TreeElement
+            // in our tree we can just return that tree element.
+            var elements = this._knownTreeElements[representedObject.__treeElementIdentifier];
+            if (elements) {
+                for (var i = 0; i < elements.length; ++i)
+                    if (elements[i].representedObject === representedObject)
+                        return elements[i];
             }
         }
-    } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /* Delete */) {
-        if (this.selectedTreeElement.ondelete)
-            handled = this.selectedTreeElement.ondelete();
-        if (!handled && this.treeOutline.ondelete)
-            handled = this.treeOutline.ondelete(this.selectedTreeElement);
-    } else if (isEnterKey(event)) {
-        if (this.selectedTreeElement.onenter)
-            handled = this.selectedTreeElement.onenter();
-        if (!handled && this.treeOutline.onenter)
-            handled = this.treeOutline.onenter(this.selectedTreeElement);
-    } else if (event.keyIdentifier === "U+0020" /* Space */) {
-        if (this.selectedTreeElement.onspace)
-            handled = this.selectedTreeElement.onspace();
-        if (!handled && this.treeOutline.onspace)
-            handled = this.treeOutline.onspace(this.selectedTreeElement);
+        return null;
     }
 
-    if (nextSelectedElement) {
-        nextSelectedElement.reveal();
-        nextSelectedElement.select(false, true);
+    findTreeElement(representedObject, isAncestor, getParent)
+    {
+        if (!representedObject)
+            return null;
+
+        var cachedElement = this.getCachedTreeElement(representedObject);
+        if (cachedElement)
+            return cachedElement;
+
+        // The representedObject isn't known, so we start at the top of the tree and work down to find the first
+        // tree element that represents representedObject or one of its ancestors.
+        var item;
+        var found = false;
+        for (var i = 0; i < this.children.length; ++i) {
+            item = this.children[i];
+            if (item.representedObject === representedObject || (isAncestor && isAncestor(item.representedObject, representedObject))) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found)
+            return null;
+
+        // Make sure the item that we found is connected to the root of the tree.
+        // Build up a list of representedObject's ancestors that aren't already in our tree.
+        var ancestors = [];
+        var currentObject = representedObject;
+        while (currentObject) {
+            ancestors.unshift(currentObject);
+            if (currentObject === item.representedObject)
+                break;
+            currentObject = getParent(currentObject);
+        }
+
+        // For each of those ancestors we populate them to fill in the tree.
+        for (var i = 0; i < ancestors.length; ++i) {
+            // Make sure we don't call findTreeElement with the same representedObject
+            // again, to prevent infinite recursion.
+            if (ancestors[i] === representedObject)
+                continue;
+
+            // FIXME: we could do something faster than findTreeElement since we will know the next
+            // ancestor exists in the tree.
+            item = this.findTreeElement(ancestors[i], isAncestor, getParent);
+            if (item)
+                item.onpopulate();
+        }
+
+        return this.getCachedTreeElement(representedObject);
     }
 
-    if (handled) {
-        event.preventDefault();
-        event.stopPropagation();
+    _treeElementDidChange(treeElement)
+    {
+        if (treeElement.treeOutline !== this)
+            return;
+
+        if (this.onchange)
+            this.onchange(treeElement);
+    }
+
+    treeElementFromNode(node)
+    {
+        var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(["ol", "li"]);
+        if (listNode)
+            return listNode.parentTreeElement || listNode.treeElement;
+        return null;
+    }
+
+    treeElementFromPoint(x, y)
+    {
+        var node = this._childrenListNode.ownerDocument.elementFromPoint(x, y);
+        if (!node)
+            return null;
+
+        return this.treeElementFromNode(node);
+    }
+
+    _treeKeyDown(event)
+    {
+        if (event.target !== this._childrenListNode)
+            return;
+
+        if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey)
+            return;
+
+        var handled = false;
+        var nextSelectedElement;
+        if (event.keyIdentifier === "Up" && !event.altKey) {
+            nextSelectedElement = this.selectedTreeElement.traversePreviousTreeElement(true);
+            while (nextSelectedElement && !nextSelectedElement.selectable)
+                nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!this.expandTreeElementsWhenArrowing);
+            handled = nextSelectedElement ? true : false;
+        } else if (event.keyIdentifier === "Down" && !event.altKey) {
+            nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(true);
+            while (nextSelectedElement && !nextSelectedElement.selectable)
+                nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.expandTreeElementsWhenArrowing);
+            handled = nextSelectedElement ? true : false;
+        } else if (event.keyIdentifier === "Left") {
+            if (this.selectedTreeElement.expanded) {
+                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();
+            }
+        } else if (event.keyIdentifier === "Right") {
+            if (!this.selectedTreeElement.revealed()) {
+                this.selectedTreeElement.reveal();
+                handled = true;
+            } else if (this.selectedTreeElement.hasChildren) {
+                handled = true;
+                if (this.selectedTreeElement.expanded) {
+                    nextSelectedElement = this.selectedTreeElement.children[0];
+                    while (nextSelectedElement && !nextSelectedElement.selectable)
+                        nextSelectedElement = nextSelectedElement.nextSibling;
+                    handled = nextSelectedElement ? true : false;
+                } else {
+                    if (event.altKey)
+                        this.selectedTreeElement.expandRecursively();
+                    else
+                        this.selectedTreeElement.expand();
+                }
+            }
+        } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /* Delete */) {
+            if (this.selectedTreeElement.ondelete)
+                handled = this.selectedTreeElement.ondelete();
+            if (!handled && this.treeOutline.ondelete)
+                handled = this.treeOutline.ondelete(this.selectedTreeElement);
+        } else if (isEnterKey(event)) {
+            if (this.selectedTreeElement.onenter)
+                handled = this.selectedTreeElement.onenter();
+            if (!handled && this.treeOutline.onenter)
+                handled = this.treeOutline.onenter(this.selectedTreeElement);
+        } else if (event.keyIdentifier === "U+0020" /* Space */) {
+            if (this.selectedTreeElement.onspace)
+                handled = this.selectedTreeElement.onspace();
+            if (!handled && this.treeOutline.onspace)
+                handled = this.treeOutline.onspace(this.selectedTreeElement);
+        }
+
+        if (nextSelectedElement) {
+            nextSelectedElement.reveal();
+            nextSelectedElement.select(false, true);
+        }
+
+        if (handled) {
+            event.preventDefault();
+            event.stopPropagation();
+        }
+    }
+
+    expand()
+    {
+        // this is the root, do nothing
+    }
+
+    collapse()
+    {
+        // this is the root, do nothing
+    }
+
+    revealed()
+    {
+        return true;
+    }
+
+    reveal()
+    {
+        // this is the root, do nothing
+    }
+
+    select()
+    {
+        // this is the root, do nothing
+    }
+
+    revealAndSelect(omitFocus)
+    {
+        // this is the root, do nothing
     }
 };
 
-TreeOutline.prototype.expand = function()
+WebInspector.TreeOutline._knownTreeElementNextIdentifier = 1;
+
+WebInspector.TreeElement = class TreeElement extends WebInspector.Object
 {
-    // this is the root, do nothing
-};
+    constructor(title, representedObject, hasChildren)
+    {
+        super();
 
-TreeOutline.prototype.collapse = function()
-{
-    // this is the root, do nothing
-};
+        this._title = title;
+        this.representedObject = (representedObject || {});
 
-TreeOutline.prototype.revealed = function()
-{
-    return true;
-};
+        if (this.representedObject.__treeElementIdentifier)
+            this.identifier = this.representedObject.__treeElementIdentifier;
+        else {
+            this.identifier = WebInspector.TreeOutline._knownTreeElementNextIdentifier++;
+            this.representedObject.__treeElementIdentifier = this.identifier;
+        }
 
-TreeOutline.prototype.reveal = function()
-{
-    // this is the root, do nothing
-};
-
-TreeOutline.prototype.select = function()
-{
-    // this is the root, do nothing
-};
-
-TreeOutline.prototype.revealAndSelect = function(omitFocus)
-{
-    // this is the root, do nothing
-};
-
-TreeOutline.prototype.__proto__ = WebInspector.Object.prototype;
-
-function TreeElement(title, representedObject, hasChildren)
-{
-    // FIXME: Convert this to a WebInspector.Object subclass, and call super().
-    // WebInspector.Object.call(this);
-
-    this._title = title;
-    this.representedObject = (representedObject || {});
-
-    if (this.representedObject.__treeElementIdentifier)
-        this.identifier = this.representedObject.__treeElementIdentifier;
-    else {
-        this.identifier = TreeOutline._knownTreeElementNextIdentifier++;
-        this.representedObject.__treeElementIdentifier = this.identifier;
+        this._hidden = false;
+        this._selectable = true;
+        this.expanded = false;
+        this.selected = false;
+        this.hasChildren = hasChildren;
+        this.children = [];
+        this.treeOutline = null;
+        this.parent = null;
+        this.previousSibling = null;
+        this.nextSibling = null;
+        this._listItemNode = null;
     }
 
-    this._hidden = false;
-    this._selectable = true;
-    this.expanded = false;
-    this.selected = false;
-    this.hasChildren = hasChildren;
-    this.children = [];
-    this.treeOutline = null;
-    this.parent = null;
-    this.previousSibling = null;
-    this.nextSibling = null;
-    this._listItemNode = null;
-}
+    // Methods
 
-TreeElement.prototype = {
-    constructor: TreeElement,
+    appendChild() { return WebInspector.TreeOutline.prototype.appendChild.apply(this, arguments); }
+    insertChild() { return WebInspector.TreeOutline.prototype.insertChild.apply(this, arguments); }
+    removeChild() { return WebInspector.TreeOutline.prototype.removeChild.apply(this, arguments); }
+    removeChildAtIndex() { return WebInspector.TreeOutline.prototype.removeChildAtIndex.apply(this, arguments); }
+    removeChildren() { return WebInspector.TreeOutline.prototype.removeChildren.apply(this, arguments); }
+    removeChildrenRecursive() { return WebInspector.TreeOutline.prototype.removeChildrenRecursive.apply(this, arguments); }
 
-    arrowToggleWidth: 10,
+    get arrowToggleWidth()
+    {
+        return 10;
+    }
 
-    get selectable() {
+    get selectable()
+    {
         if (this._hidden)
             return false;
         return this._selectable;
-    },
+    }
 
-    set selectable(x) {
+    set selectable(x)
+    {
         this._selectable = x;
-    },
+    }
 
-    get listItemElement() {
+    get listItemElement()
+    {
         return this._listItemNode;
-    },
+    }
 
-    get childrenListElement() {
+    get childrenListElement()
+    {
         return this._childrenListNode;
-    },
+    }
 
-    get title() {
+    get title()
+    {
         return this._title;
-    },
+    }
 
-    set title(x) {
+    set title(x)
+    {
         this._title = x;
         this._setListItemNodeContent();
         this.didChange();
-    },
+    }
 
-    get titleHTML() {
+    get titleHTML()
+    {
         return this._titleHTML;
-    },
+    }
 
-    set titleHTML(x) {
+    set titleHTML(x)
+    {
         this._titleHTML = x;
         this._setListItemNodeContent();
         this.didChange();
-    },
+    }
 
-    get tooltip() {
+    get tooltip()
+    {
         return this._tooltip;
-    },
+    }
 
-    set tooltip(x) {
+    set tooltip(x)
+    {
         this._tooltip = x;
         if (this._listItemNode)
             this._listItemNode.title = x ? x : "";
         this.didChange();
-    },
+    }
 
-    get hasChildren() {
+    get hasChildren()
+    {
         return this._hasChildren;
-    },
+    }
 
-    set hasChildren(x) {
+    set hasChildren(x)
+    {
         if (this._hasChildren === x)
             return;
 
@@ -605,13 +628,15 @@
         }
 
         this.didChange();
-    },
+    }
 
-    get hidden() {
+    get hidden()
+    {
         return this._hidden;
-    },
+    }
 
-    set hidden(x) {
+    set hidden(x)
+    {
         if (this._hidden === x)
             return;
 
@@ -631,27 +656,29 @@
 
         if (this.treeOutline && this.treeOutline.onhidden)
             this.treeOutline.onhidden(this, x);
-    },
+    }
 
-    get shouldRefreshChildren() {
+    get shouldRefreshChildren()
+    {
         return this._shouldRefreshChildren;
-    },
+    }
 
-    set shouldRefreshChildren(x) {
+    set shouldRefreshChildren(x)
+    {
         this._shouldRefreshChildren = x;
         if (x && this.expanded)
             this.expand();
-    },
+    }
 
-    _fireDidChange: function()
+    _fireDidChange()
     {
         delete this._didChangeTimeoutIdentifier;
 
         if (this.treeOutline)
             this.treeOutline._treeElementDidChange(this);
-    },
+    }
 
-    didChange: function()
+    didChange()
     {
         if (!this.treeOutline)
             return;
@@ -659,9 +686,9 @@
         // Prevent telling the TreeOutline multiple times in a row by delaying it with a timeout.
         if (!this._didChangeTimeoutIdentifier)
             this._didChangeTimeoutIdentifier = setTimeout(this._fireDidChange.bind(this), 0);
-    },
+    }
 
-    _setListItemNodeContent: function()
+    _setListItemNodeContent()
     {
         if (!this._listItemNode)
             return;
@@ -679,401 +706,393 @@
             this._listItemNode.appendChild(this._title);
         }
     }
-};
 
-TreeElement.prototype.appendChild = TreeOutline.prototype.appendChild;
-TreeElement.prototype.insertChild = TreeOutline.prototype.insertChild;
-TreeElement.prototype.removeChild = TreeOutline.prototype.removeChild;
-TreeElement.prototype.removeChildAtIndex = TreeOutline.prototype.removeChildAtIndex;
-TreeElement.prototype.removeChildren = TreeOutline.prototype.removeChildren;
-TreeElement.prototype.removeChildrenRecursive = TreeOutline.prototype.removeChildrenRecursive;
+    _attach()
+    {
+        if (!this._listItemNode || this.parent._shouldRefreshChildren) {
+            if (this._listItemNode && this._listItemNode.parentNode)
+                this._listItemNode.parentNode.removeChild(this._listItemNode);
 
-TreeElement.prototype._attach = function()
-{
-    if (!this._listItemNode || this.parent._shouldRefreshChildren) {
+            this._listItemNode = this.treeOutline._childrenListNode.ownerDocument.createElement("li");
+            this._listItemNode.treeElement = this;
+            this._setListItemNodeContent();
+            this._listItemNode.title = this._tooltip ? this._tooltip : "";
+
+            if (this.hidden)
+                this._listItemNode.classList.add("hidden");
+            if (this.hasChildren)
+                this._listItemNode.classList.add("parent");
+            if (this.expanded)
+                this._listItemNode.classList.add("expanded");
+            if (this.selected)
+                this._listItemNode.classList.add("selected");
+
+            this._listItemNode.addEventListener("mousedown", WebInspector.TreeElement.treeElementMouseDown);
+            this._listItemNode.addEventListener("click", WebInspector.TreeElement.treeElementToggled);
+            this._listItemNode.addEventListener("dblclick", WebInspector.TreeElement.treeElementDoubleClicked);
+
+            if (this.onattach)
+                this.onattach(this);
+        }
+
+        var nextSibling = null;
+        if (this.nextSibling && this.nextSibling._listItemNode && this.nextSibling._listItemNode.parentNode === this.parent._childrenListNode)
+            nextSibling = this.nextSibling._listItemNode;
+        this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
+        if (this._childrenListNode)
+            this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
+        if (this.selected)
+            this.select();
+        if (this.expanded)
+            this.expand();
+    }
+
+    _detach()
+    {
+        if (this.ondetach)
+            this.ondetach(this);
         if (this._listItemNode && this._listItemNode.parentNode)
             this._listItemNode.parentNode.removeChild(this._listItemNode);
-
-        this._listItemNode = this.treeOutline._childrenListNode.ownerDocument.createElement("li");
-        this._listItemNode.treeElement = this;
-        this._setListItemNodeContent();
-        this._listItemNode.title = this._tooltip ? this._tooltip : "";
-
-        if (this.hidden)
-            this._listItemNode.classList.add("hidden");
-        if (this.hasChildren)
-            this._listItemNode.classList.add("parent");
-        if (this.expanded)
-            this._listItemNode.classList.add("expanded");
-        if (this.selected)
-            this._listItemNode.classList.add("selected");
-
-        this._listItemNode.addEventListener("mousedown", TreeElement.treeElementMouseDown, false);
-        this._listItemNode.addEventListener("click", TreeElement.treeElementToggled, false);
-        this._listItemNode.addEventListener("dblclick", TreeElement.treeElementDoubleClicked, false);
-
-        if (this.onattach)
-            this.onattach(this);
-    }
-
-    var nextSibling = null;
-    if (this.nextSibling && this.nextSibling._listItemNode && this.nextSibling._listItemNode.parentNode === this.parent._childrenListNode)
-        nextSibling = this.nextSibling._listItemNode;
-    this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
-    if (this._childrenListNode)
-        this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
-    if (this.selected)
-        this.select();
-    if (this.expanded)
-        this.expand();
-};
-
-TreeElement.prototype._detach = function()
-{
-    if (this.ondetach)
-        this.ondetach(this);
-    if (this._listItemNode && this._listItemNode.parentNode)
-        this._listItemNode.parentNode.removeChild(this._listItemNode);
-    if (this._childrenListNode && this._childrenListNode.parentNode)
-        this._childrenListNode.parentNode.removeChild(this._childrenListNode);
-};
-
-TreeElement.treeElementMouseDown = function(event)
-{
-    var element = event.currentTarget;
-    if (!element || !element.treeElement || !element.treeElement.selectable)
-        return;
-
-    if (element.treeElement.isEventWithinDisclosureTriangle(event)) {
-        event.preventDefault();
-        return;
-    }
-
-    element.treeElement.selectOnMouseDown(event);
-};
-
-TreeElement.treeElementToggled = function(event)
-{
-    var element = event.currentTarget;
-    if (!element || !element.treeElement)
-        return;
-
-    var toggleOnClick = element.treeElement.toggleOnClick && !element.treeElement.selectable;
-    var isInTriangle = element.treeElement.isEventWithinDisclosureTriangle(event);
-    if (!toggleOnClick && !isInTriangle)
-        return;
-
-    if (element.treeElement.expanded) {
-        if (event.altKey)
-            element.treeElement.collapseRecursively();
-        else
-            element.treeElement.collapse();
-    } else {
-        if (event.altKey)
-            element.treeElement.expandRecursively();
-        else
-            element.treeElement.expand();
-    }
-    event.stopPropagation();
-};
-
-TreeElement.treeElementDoubleClicked = function(event)
-{
-    var element = event.currentTarget;
-    if (!element || !element.treeElement)
-        return;
-
-    if (element.treeElement.isEventWithinDisclosureTriangle(event))
-        return;
-
-    if (element.treeElement.ondblclick)
-        element.treeElement.ondblclick.call(element.treeElement, event);
-    else if (element.treeElement.hasChildren && !element.treeElement.expanded)
-        element.treeElement.expand();
-};
-
-TreeElement.prototype.collapse = function()
-{
-    if (this._listItemNode)
-        this._listItemNode.classList.remove("expanded");
-    if (this._childrenListNode)
-        this._childrenListNode.classList.remove("expanded");
-
-    this.expanded = false;
-    if (this.treeOutline)
-        this.treeOutline._treeElementsExpandedState[this.identifier] = false;
-
-    if (this.oncollapse)
-        this.oncollapse(this);
-
-    if (this.treeOutline && this.treeOutline.oncollapse)
-        this.treeOutline.oncollapse(this);
-};
-
-TreeElement.prototype.collapseRecursively = function()
-{
-    var item = this;
-    while (item) {
-        if (item.expanded)
-            item.collapse();
-        item = item.traverseNextTreeElement(false, this, true);
-    }
-};
-
-TreeElement.prototype.expand = function()
-{
-    if (this.expanded && !this._shouldRefreshChildren && this._childrenListNode)
-        return;
-
-    // Set this before onpopulate. Since onpopulate can add elements and call onadd, this makes
-    // sure the expanded flag is true before calling those functions. This prevents the possibility
-    // of an infinite loop if onpopulate or onadd were to call expand.
-
-    this.expanded = true;
-    if (this.treeOutline)
-        this.treeOutline._treeElementsExpandedState[this.identifier] = true;
-
-    // If there are no children, return. We will be expanded once we have children.
-    if (!this.hasChildren)
-        return;
-
-    if (this.treeOutline && (!this._childrenListNode || this._shouldRefreshChildren)) {
         if (this._childrenListNode && this._childrenListNode.parentNode)
             this._childrenListNode.parentNode.removeChild(this._childrenListNode);
+    }
 
-        this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
-        this._childrenListNode.parentTreeElement = this;
-        this._childrenListNode.classList.add("children");
+    static treeElementMouseDown(event)
+    {
+        var element = event.currentTarget;
+        if (!element || !element.treeElement || !element.treeElement.selectable)
+            return;
 
+        if (element.treeElement.isEventWithinDisclosureTriangle(event)) {
+            event.preventDefault();
+            return;
+        }
+
+        element.treeElement.selectOnMouseDown(event);
+    }
+
+    static treeElementToggled(event)
+    {
+        var element = event.currentTarget;
+        if (!element || !element.treeElement)
+            return;
+
+        var toggleOnClick = element.treeElement.toggleOnClick && !element.treeElement.selectable;
+        var isInTriangle = element.treeElement.isEventWithinDisclosureTriangle(event);
+        if (!toggleOnClick && !isInTriangle)
+            return;
+
+        if (element.treeElement.expanded) {
+            if (event.altKey)
+                element.treeElement.collapseRecursively();
+            else
+                element.treeElement.collapse();
+        } else {
+            if (event.altKey)
+                element.treeElement.expandRecursively();
+            else
+                element.treeElement.expand();
+        }
+        event.stopPropagation();
+    }
+
+    static treeElementDoubleClicked(event)
+    {
+        var element = event.currentTarget;
+        if (!element || !element.treeElement)
+            return;
+
+        if (element.treeElement.isEventWithinDisclosureTriangle(event))
+            return;
+
+        if (element.treeElement.ondblclick)
+            element.treeElement.ondblclick.call(element.treeElement, event);
+        else if (element.treeElement.hasChildren && !element.treeElement.expanded)
+            element.treeElement.expand();
+    }
+
+    collapse()
+    {
+        if (this._listItemNode)
+            this._listItemNode.classList.remove("expanded");
+        if (this._childrenListNode)
+            this._childrenListNode.classList.remove("expanded");
+
+        this.expanded = false;
+        if (this.treeOutline)
+            this.treeOutline._treeElementsExpandedState[this.identifier] = false;
+
+        if (this.oncollapse)
+            this.oncollapse(this);
+
+        if (this.treeOutline && this.treeOutline.oncollapse)
+            this.treeOutline.oncollapse(this);
+    }
+
+    collapseRecursively()
+    {
+        var item = this;
+        while (item) {
+            if (item.expanded)
+                item.collapse();
+            item = item.traverseNextTreeElement(false, this, true);
+        }
+    }
+
+    expand()
+    {
+        if (this.expanded && !this._shouldRefreshChildren && this._childrenListNode)
+            return;
+
+        // Set this before onpopulate. Since onpopulate can add elements and call onadd, this makes
+        // sure the expanded flag is true before calling those functions. This prevents the possibility
+        // of an infinite loop if onpopulate or onadd were to call expand.
+
+        this.expanded = true;
+        if (this.treeOutline)
+            this.treeOutline._treeElementsExpandedState[this.identifier] = true;
+
+        // If there are no children, return. We will be expanded once we have children.
+        if (!this.hasChildren)
+            return;
+
+        if (this.treeOutline && (!this._childrenListNode || this._shouldRefreshChildren)) {
+            if (this._childrenListNode && this._childrenListNode.parentNode)
+                this._childrenListNode.parentNode.removeChild(this._childrenListNode);
+
+            this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
+            this._childrenListNode.parentTreeElement = this;
+            this._childrenListNode.classList.add("children");
+
+            if (this.hidden)
+                this._childrenListNode.classList.add("hidden");
+
+            this.onpopulate();
+
+            for (var i = 0; i < this.children.length; ++i)
+                this.children[i]._attach();
+
+            delete this._shouldRefreshChildren;
+        }
+
+        if (this._listItemNode) {
+            this._listItemNode.classList.add("expanded");
+            if (this._childrenListNode && this._childrenListNode.parentNode !== this._listItemNode.parentNode)
+                this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
+        }
+
+        if (this._childrenListNode)
+            this._childrenListNode.classList.add("expanded");
+
+        if (this.onexpand)
+            this.onexpand(this);
+
+        if (this.treeOutline && this.treeOutline.onexpand)
+            this.treeOutline.onexpand(this);
+    }
+
+    expandRecursively(maxDepth)
+    {
+        var item = this;
+        var info = {};
+        var depth = 0;
+
+        // The Inspector uses TreeOutlines to represents object properties, so recursive expansion
+        // in some case can be infinite, since JavaScript objects can hold circular references.
+        // So default to a recursion cap of 3 levels, since that gives fairly good results.
+        if (maxDepth === undefined)
+            maxDepth = 3;
+
+        while (item) {
+            if (depth < maxDepth)
+                item.expand();
+            item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
+            depth += info.depthChange;
+        }
+    }
+
+    hasAncestor(ancestor)
+        {
+        if (!ancestor)
+            return false;
+
+        var currentNode = this.parent;
+        while (currentNode) {
+            if (ancestor === currentNode)
+                return true;
+            currentNode = currentNode.parent;
+        }
+
+        return false;
+    }
+
+    reveal()
+    {
+        var currentAncestor = this.parent;
+        while (currentAncestor && !currentAncestor.root) {
+            if (!currentAncestor.expanded)
+                currentAncestor.expand();
+            currentAncestor = currentAncestor.parent;
+        }
+
+        if (this.onreveal)
+            this.onreveal(this);
+    }
+
+    revealed()
+    {
         if (this.hidden)
-            this._childrenListNode.classList.add("hidden");
-
-        this.onpopulate();
-
-        for (var i = 0; i < this.children.length; ++i)
-            this.children[i]._attach();
-
-        delete this._shouldRefreshChildren;
-    }
-
-    if (this._listItemNode) {
-        this._listItemNode.classList.add("expanded");
-        if (this._childrenListNode && this._childrenListNode.parentNode !== this._listItemNode.parentNode)
-            this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
-    }
-
-    if (this._childrenListNode)
-        this._childrenListNode.classList.add("expanded");
-
-    if (this.onexpand)
-        this.onexpand(this);
-
-    if (this.treeOutline && this.treeOutline.onexpand)
-        this.treeOutline.onexpand(this);
-};
-
-TreeElement.prototype.expandRecursively = function(maxDepth)
-{
-    var item = this;
-    var info = {};
-    var depth = 0;
-
-    // The Inspector uses TreeOutlines to represents object properties, so recursive expansion
-    // in some case can be infinite, since JavaScript objects can hold circular references.
-    // So default to a recursion cap of 3 levels, since that gives fairly good results.
-    if (maxDepth === undefined)
-        maxDepth = 3;
-
-    while (item) {
-        if (depth < maxDepth)
-            item.expand();
-        item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
-        depth += info.depthChange;
-    }
-};
-
-TreeElement.prototype.hasAncestor = function(ancestor) {
-    if (!ancestor)
-        return false;
-
-    var currentNode = this.parent;
-    while (currentNode) {
-        if (ancestor === currentNode)
-            return true;
-        currentNode = currentNode.parent;
-    }
-
-    return false;
-};
-
-TreeElement.prototype.reveal = function()
-{
-    var currentAncestor = this.parent;
-    while (currentAncestor && !currentAncestor.root) {
-        if (!currentAncestor.expanded)
-            currentAncestor.expand();
-        currentAncestor = currentAncestor.parent;
-    }
-
-    if (this.onreveal)
-        this.onreveal(this);
-};
-
-TreeElement.prototype.revealed = function()
-{
-    if (this.hidden)
-        return false;
-
-    var currentAncestor = this.parent;
-    while (currentAncestor && !currentAncestor.root) {
-        if (!currentAncestor.expanded)
             return false;
-        if (currentAncestor.hidden)
-            return false;
-        currentAncestor = currentAncestor.parent;
+
+        var currentAncestor = this.parent;
+        while (currentAncestor && !currentAncestor.root) {
+            if (!currentAncestor.expanded)
+                return false;
+            if (currentAncestor.hidden)
+                return false;
+            currentAncestor = currentAncestor.parent;
+        }
+
+        return true;
     }
 
-    return true;
-};
+    selectOnMouseDown(event)
+    {
+        this.select(false, true);
+    }
 
-TreeElement.prototype.selectOnMouseDown = function(event)
-{
-    this.select(false, true);
-};
+    select(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect)
+    {
+        if (!this.treeOutline || !this.selectable)
+            return;
 
-TreeElement.prototype.select = function(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect)
-{
-    if (!this.treeOutline || !this.selectable)
-        return;
+        if (this.selected && !this.treeOutline.allowsRepeatSelection)
+            return;
 
-    if (this.selected && !this.treeOutline.allowsRepeatSelection)
-        return;
+        if (!omitFocus)
+            this.treeOutline._childrenListNode.focus();
 
-    if (!omitFocus)
-        this.treeOutline._childrenListNode.focus();
+        // Focusing on another node may detach "this" from tree.
+        if (!this.treeOutline)
+            return;
 
-    // Focusing on another node may detach "this" from tree.
-    if (!this.treeOutline)
-        return;
+        this.treeOutline.processingSelectionChange = true;
 
-    this.treeOutline.processingSelectionChange = true;
+        if (!this.selected) {
+            if (this.treeOutline.selectedTreeElement)
+                this.treeOutline.selectedTreeElement.deselect(suppressOnDeselect);
 
-    if (!this.selected) {
-        if (this.treeOutline.selectedTreeElement)
-            this.treeOutline.selectedTreeElement.deselect(suppressOnDeselect);
+            this.selected = true;
+            this.treeOutline.selectedTreeElement = this;
 
-        this.selected = true;
-        this.treeOutline.selectedTreeElement = this;
+            if (this._listItemNode)
+                this._listItemNode.classList.add("selected");
+        }
+
+        if (this.onselect && !suppressOnSelect)
+            this.onselect(this, selectedByUser);
+
+        if (this.treeOutline.onselect && !suppressOnSelect)
+            this.treeOutline.onselect(this, selectedByUser);
+
+        delete this.treeOutline.processingSelectionChange;
+    }
+
+    revealAndSelect(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect)
+    {
+        this.reveal();
+        this.select(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect);
+    }
+
+    deselect(suppressOnDeselect)
+    {
+        if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !this.selected)
+            return false;
+
+        this.selected = false;
+        this.treeOutline.selectedTreeElement = null;
 
         if (this._listItemNode)
-            this._listItemNode.classList.add("selected");
+            this._listItemNode.classList.remove("selected");
+
+        if (this.ondeselect && !suppressOnDeselect)
+            this.ondeselect(this);
+
+        if (this.treeOutline.ondeselect && !suppressOnDeselect)
+            this.treeOutline.ondeselect(this);
+
+        return true;
     }
 
-    if (this.onselect && !suppressOnSelect)
-        this.onselect(this, selectedByUser);
+    onpopulate()
+    {
+        // Overriden by subclasses.
+    }
 
-    if (this.treeOutline.onselect && !suppressOnSelect)
-        this.treeOutline.onselect(this, selectedByUser);
+    traverseNextTreeElement(skipUnrevealed, stayWithin, dontPopulate, info)
+    {
+        if (!dontPopulate && this.hasChildren)
+            this.onpopulate.call(this); // FIXME: This shouldn't need to use call, but this is working around a JSC bug. https://webkit.org/b/74811
 
-    delete this.treeOutline.processingSelectionChange;
-};
-
-TreeElement.prototype.revealAndSelect = function(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect)
-{
-    this.reveal();
-    this.select(omitFocus, selectedByUser, suppressOnSelect, suppressOnDeselect);
-};
-
-TreeElement.prototype.deselect = function(suppressOnDeselect)
-{
-    if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !this.selected)
-        return false;
-
-    this.selected = false;
-    this.treeOutline.selectedTreeElement = null;
-
-    if (this._listItemNode)
-        this._listItemNode.classList.remove("selected");
-
-    if (this.ondeselect && !suppressOnDeselect)
-        this.ondeselect(this);
-
-    if (this.treeOutline.ondeselect && !suppressOnDeselect)
-        this.treeOutline.ondeselect(this);
-
-    return true;
-};
-
-TreeElement.prototype.onpopulate = function()
-{
-    // Overriden by subclasses.
-};
-
-TreeElement.prototype.traverseNextTreeElement = function(skipUnrevealed, stayWithin, dontPopulate, info)
-{
-    if (!dontPopulate && this.hasChildren)
-        this.onpopulate.call(this); // FIXME: This shouldn't need to use call, but this is working around a JSC bug. https://webkit.org/b/74811
-
-    if (info)
-        info.depthChange = 0;
-
-    var element = skipUnrevealed ? (this.revealed() ? this.children[0] : null) : this.children[0];
-    if (element && (!skipUnrevealed || (skipUnrevealed && this.expanded))) {
         if (info)
-            info.depthChange = 1;
-        return element;
+            info.depthChange = 0;
+
+        var element = skipUnrevealed ? (this.revealed() ? this.children[0] : null) : this.children[0];
+        if (element && (!skipUnrevealed || (skipUnrevealed && this.expanded))) {
+            if (info)
+                info.depthChange = 1;
+            return element;
+        }
+
+        if (this === stayWithin)
+            return null;
+
+        element = skipUnrevealed ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
+        if (element)
+            return element;
+
+        element = this;
+        while (element && !element.root && !(skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) && element.parent !== stayWithin) {
+            if (info)
+                info.depthChange -= 1;
+            element = element.parent;
+        }
+
+        if (!element)
+            return null;
+
+        return (skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
     }
 
-    if (this === stayWithin)
-        return null;
-
-    element = skipUnrevealed ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
-    if (element)
-        return element;
-
-    element = this;
-    while (element && !element.root && !(skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) && element.parent !== stayWithin) {
-        if (info)
-            info.depthChange -= 1;
-        element = element.parent;
-    }
-
-    if (!element)
-        return null;
-
-    return (skipUnrevealed ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
-};
-
-TreeElement.prototype.traversePreviousTreeElement = function(skipUnrevealed, dontPopulate)
-{
-    var element = skipUnrevealed ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
-    if (!dontPopulate && element && element.hasChildren)
-        element.onpopulate();
-
-    while (element && (skipUnrevealed ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1])) {
-        if (!dontPopulate && element.hasChildren)
+    traversePreviousTreeElement(skipUnrevealed, dontPopulate)
+    {
+        var element = skipUnrevealed ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
+        if (!dontPopulate && element && element.hasChildren)
             element.onpopulate();
-        element = (skipUnrevealed ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1]);
+
+        while (element && (skipUnrevealed ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1])) {
+            if (!dontPopulate && element.hasChildren)
+                element.onpopulate();
+            element = (skipUnrevealed ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1]);
+        }
+
+        if (element)
+            return element;
+
+        if (!this.parent || this.parent.root)
+            return null;
+
+        return this.parent;
     }
 
-    if (element)
-        return element;
+    isEventWithinDisclosureTriangle(event)
+    {
+        if (!document.contains(this._listItemNode))
+            return false;
 
-    if (!this.parent || this.parent.root)
-        return null;
-
-    return this.parent;
+        // FIXME: We should not use getComputedStyle(). For that we need to get rid of using ::before for disclosure triangle. (http://webk.it/74446)
+        var computedLeftPadding = window.getComputedStyle(this._listItemNode).getPropertyCSSValue("padding-left").getFloatValue(CSSPrimitiveValue.CSS_PX);
+        var left = this._listItemNode.totalOffsetLeft + computedLeftPadding;
+        return event.pageX >= left && event.pageX <= left + this.arrowToggleWidth && this.hasChildren;
+    }
 };
-
-TreeElement.prototype.isEventWithinDisclosureTriangle = function(event)
-{
-    if (!document.contains(this._listItemNode))
-        return false;
-
-    // FIXME: We should not use getComputedStyle(). For that we need to get rid of using ::before for disclosure triangle. (http://webk.it/74446)
-    var computedLeftPadding = window.getComputedStyle(this._listItemNode).getPropertyCSSValue("padding-left").getFloatValue(CSSPrimitiveValue.CSS_PX);
-    var left = this._listItemNode.totalOffsetLeft + computedLeftPadding;
-    return event.pageX >= left && event.pageX <= left + this.arrowToggleWidth && this.hasChildren;
-};
-
-TreeElement.prototype.__proto__ = WebInspector.Object.prototype;