2010-02-16 Alexander Pavlov <apavlov@chromium.org>
authorapavlov@chromium.org <apavlov@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 Feb 2010 13:11:41 +0000 (13:11 +0000)
committerapavlov@chromium.org <apavlov@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 Feb 2010 13:11:41 +0000 (13:11 +0000)
        Reviewed by Pavel Feldman.

        Web Inspector: Elements Panel: Limit the number of initially loaded element children
        https://bugs.webkit.org/show_bug.cgi?id=34421

        Test: inspector/elements-panel-limited-children.html

        WebCore:
        * English.lproj/localizedStrings.js:
        * inspector/front-end/DOMAgent.js:
        (WebInspector.DOMNode.prototype._insertChild):
        * inspector/front-end/ElementsPanel.js:
        (WebInspector.ElementsPanel.prototype.updateModifiedNodes):
        * inspector/front-end/ElementsTreeOutline.js:
        (WebInspector.ElementsTreeOutline.prototype.createTreeElementFor):
        (WebInspector.ElementsTreeOutline.prototype.revealAndSelectNode):
        (WebInspector.ElementsTreeElement):
        (WebInspector.ElementsTreeElement.prototype.get expandedChildrenLimit):
        (WebInspector.ElementsTreeElement.prototype.set expandedChildrenLimit):
        (WebInspector.ElementsTreeElement.prototype.get expandedChildCount):
        (WebInspector.ElementsTreeElement.prototype.showChild):
        (WebInspector.ElementsTreeElement.prototype.insertChildElement):
        (WebInspector.ElementsTreeElement.prototype.moveChild):
        (WebInspector.ElementsTreeElement.prototype._updateChildren.updateChildrenOfNode):
        (WebInspector.ElementsTreeElement.prototype._updateChildren):
        (WebInspector.ElementsTreeElement.prototype.adjustCollapsedRange):
        (WebInspector.ElementsTreeElement.prototype.handleLoadAllChildren):
        ():
        * inspector/front-end/inspector.css:

        LayoutTests:
        * inspector/elements-panel-limited-children-expected.txt: Added.
        * inspector/elements-panel-limited-children.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@54819 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/inspector/elements-panel-limited-children-expected.txt [new file with mode: 0644]
LayoutTests/inspector/elements-panel-limited-children.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/English.lproj/localizedStrings.js
WebCore/inspector/front-end/DOMAgent.js
WebCore/inspector/front-end/ElementsPanel.js
WebCore/inspector/front-end/ElementsTreeOutline.js
WebCore/inspector/front-end/inspector.css

index 2fff5e0..e13fedd 100644 (file)
@@ -1,3 +1,13 @@
+2010-02-16  Alexander Pavlov  <apavlov@chromium.org>
+
+        Reviewed by Pavel Feldman.
+
+        Web Inspector: Elements Panel: Limit the number of initially loaded element children
+        https://bugs.webkit.org/show_bug.cgi?id=34421
+
+        * inspector/elements-panel-limited-children-expected.txt: Added.
+        * inspector/elements-panel-limited-children.html: Added.
+
 2010-02-16  Ben Murdoch  <benm@google.com>
 
         Reviewed by Simon Hausmann.
diff --git a/LayoutTests/inspector/elements-panel-limited-children-expected.txt b/LayoutTests/inspector/elements-panel-limited-children-expected.txt
new file mode 100644 (file)
index 0000000..52c4a39
--- /dev/null
@@ -0,0 +1,46 @@
+1
+3
+4
+5
+6
+7
+8
+9
+10
+Tests that src and href element targets are rewritten properly.
+
+
+<div id="data">/
+<a>
+<div id="id1">1
+</div>
+<div id="id3">3
+</div>
+<div id="id4">4
+</div>
+<div id="id5">5
+</div>Show All Nodes (6 More)
+</div>
+<div id="data">/
+<a>
+<div id="id1">1
+</div>
+<div id="id3">3
+</div>
+<div id="id4">4
+</div>
+<div id="id5">5
+</div>
+<div id="id6">6
+</div>
+<div id="id7">7
+</div>
+<div id="id8">8
+</div>
+<div id="id9">9
+</div>
+<div id="id10">10
+</div>
+<a>
+</div>
+
diff --git a/LayoutTests/inspector/elements-panel-limited-children.html b/LayoutTests/inspector/elements-panel-limited-children.html
new file mode 100644 (file)
index 0000000..cb0f895
--- /dev/null
@@ -0,0 +1,180 @@
+<html>
+<head>
+<script src="../http/tests/inspector/inspector-test.js"></script>
+<script src="elements-tests.js"></script>
+<script>
+
+function doit()
+{
+    function preOutput(result)
+    {
+        var output = document.getElementById("outputPre");
+        output.textContent += result.replace(/\u200b/g, "").replace(/\n/g, "").replace(/</g, "\n<");
+    }
+
+    function nodeDumpAfterCallback(dump)
+    {
+        preOutput(dump);
+        notifyDone();
+    }
+
+    function showAllCallback(result)
+    {
+        if (result) {
+            preOutput(result);
+            notifyDone();
+        }
+        evaluateInWebInspector("frontend_dumpNode", nodeDumpAfterCallback);
+    }
+
+    function nodeDumpBeforeCallback(dump)
+    {
+        preOutput(dump);
+        evaluateInWebInspector("frontend_showAll", showAllCallback);
+    }
+
+    function nodeExpandedCallback(result)
+    {
+        if (result) {
+            preOutput(result);
+            notifyDone();
+        }
+        var dataElement = document.getElementById("data");
+        dataElement.appendChild(document.createElement("a"));
+        dataElement.removeChild(document.getElementById("id2"));
+        dataElement.insertBefore(document.createElement("a"), document.getElementById("id1"));
+        evaluateInWebInspector("frontend_dumpNode", nodeDumpBeforeCallback);
+    }
+
+    function domLoadedCallback(result)
+    {
+        if (result) {
+            preOutput(result);
+            notifyDone();
+        }
+        evaluateInWebInspector("frontend_expandNode", nodeExpandedCallback);
+    }
+
+    evaluateInWebInspector("frontend_loadDOM", domLoadedCallback);
+}
+
+
+// Frontend functions.
+
+function frontend_showAll(testController)
+{
+    try {
+        var dataDivTreeElement = frontend_getDataTreeElement();
+        if (!dataDivTreeElement) {
+            testController.notifyDone("show_All: No data div tree element found");
+            return;
+        }
+        window.__oldUpdateChildren = WebInspector.ElementsTreeElement.prototype._updateChildren;
+        window.__newUpdateChildren = function() {
+            window.__oldUpdateChildren.apply(this, arguments);
+            WebInspector.ElementsTreeElement.prototype._updateChildren = window.__oldUpdateChildren;
+            testController.notifyDone("");
+        }
+        WebInspector.ElementsTreeElement.prototype._updateChildren = window.__newUpdateChildren;
+
+        WebInspector.ElementsTreeElement.prototype.handleLoadAllChildren.call(dataDivTreeElement);
+        testController.waitUntilDone();
+    } catch(e) {
+        testController.notifyDone("showAll: " + e);
+    }
+}
+
+function frontend_dumpNode(testController)
+{
+    testController.waitUntilDone();
+    function dumpFunction()
+    {
+        var dataDivTreeElement = frontend_getDataTreeElement();
+        if (!dataDivTreeElement) {
+            testController.notifyDone("dumpNode: No data div tree element found");
+            return;
+        }
+
+        var liContent = dataDivTreeElement.listItemElement ? dataDivTreeElement.listItemElement.textContent : "{EMPTY}";
+        var childrenContent = dataDivTreeElement.childrenListElement ? dataDivTreeElement.childrenListElement.textContent : "{EMPTY}";
+        testController.notifyDone(liContent + "/" + childrenContent);
+    }
+    // Ensure all asynchronous updates to the Elements tree outline are processed before dumping.
+    setTimeout(dumpFunction, 0);
+}
+
+function frontend_expandNode(testController)
+{
+    var dataDivTreeElement = frontend_getDataTreeElement();
+    if (!dataDivTreeElement) {
+        testController.notifyDone("No data div tree element found");
+        return;
+    }
+
+    try {
+        dataDivTreeElement._expandedChildrenLimit = 5;
+        dataDivTreeElement.reveal();
+        dataDivTreeElement.expand();
+        testController.notifyDone("");
+    } catch(e) {
+        testController.notifyDone("expandNode: " + e);
+    }
+}
+
+function frontend_loadDOM(testController)
+{
+    testController.waitUntilDone();
+    // Need test to be async to expand whole the tree first.
+    try {
+        frontend_expandDOMSubtree(WebInspector.domAgent.document);
+        testController.runAfterPendingDispatches(function() {
+            testController.notifyDone("");
+        });
+    } catch(e) {
+        testController.notifyDone("loadDOM: " + e);
+    }
+}
+
+function frontend_getDataTreeElement()
+{
+    var node = frontend_getDataDOMElement();
+    if (!node)
+        return null;
+    return WebInspector.panels.elements.treeOutline.createTreeElementFor(node);
+}
+
+
+function frontend_getDataDOMElement()
+{
+    var innerMapping = WebInspector.domAgent._idToDOMNode;
+
+    for (var nodeId in innerMapping) {
+        var node = innerMapping[nodeId];
+        if (node.nodeName === "DIV" || node.getAttribute("id") === "data")
+            return node;
+    }
+    return null;
+}
+</script>
+</head>
+
+<body onload="onload()">
+<div id="data">
+<div id="id1">1</div>
+<div id="id2">2</div>
+<div id="id3">3</div>
+<div id="id4">4</div>
+<div id="id5">5</div>
+<div id="id6">6</div>
+<div id="id7">7</div>
+<div id="id8">8</div>
+<div id="id9">9</div>
+<div id="id10">10</div>
+</div>
+<p>
+Tests that src and href element targets are rewritten properly.
+</p>
+<pre id="outputPre">
+</pre>
+</body>
+</html>
index 37097f2..60e68c1 100644 (file)
@@ -1,3 +1,34 @@
+2010-02-16  Alexander Pavlov  <apavlov@chromium.org>
+
+        Reviewed by Pavel Feldman.
+
+        Web Inspector: Elements Panel: Limit the number of initially loaded element children
+        https://bugs.webkit.org/show_bug.cgi?id=34421
+
+        Test: inspector/elements-panel-limited-children.html
+
+        * English.lproj/localizedStrings.js:
+        * inspector/front-end/DOMAgent.js:
+        (WebInspector.DOMNode.prototype._insertChild):
+        * inspector/front-end/ElementsPanel.js:
+        (WebInspector.ElementsPanel.prototype.updateModifiedNodes):
+        * inspector/front-end/ElementsTreeOutline.js:
+        (WebInspector.ElementsTreeOutline.prototype.createTreeElementFor):
+        (WebInspector.ElementsTreeOutline.prototype.revealAndSelectNode):
+        (WebInspector.ElementsTreeElement):
+        (WebInspector.ElementsTreeElement.prototype.get expandedChildrenLimit):
+        (WebInspector.ElementsTreeElement.prototype.set expandedChildrenLimit):
+        (WebInspector.ElementsTreeElement.prototype.get expandedChildCount):
+        (WebInspector.ElementsTreeElement.prototype.showChild):
+        (WebInspector.ElementsTreeElement.prototype.insertChildElement):
+        (WebInspector.ElementsTreeElement.prototype.moveChild):
+        (WebInspector.ElementsTreeElement.prototype._updateChildren.updateChildrenOfNode):
+        (WebInspector.ElementsTreeElement.prototype._updateChildren):
+        (WebInspector.ElementsTreeElement.prototype.adjustCollapsedRange):
+        (WebInspector.ElementsTreeElement.prototype.handleLoadAllChildren):
+        ():
+        * inspector/front-end/inspector.css:
+
 2010-02-16  Ismail Donmez  <ismail@namtrac.org>
 
         Reviewed by Pavel Feldman.
index 9196692..65d9528 100644 (file)
Binary files a/WebCore/English.lproj/localizedStrings.js and b/WebCore/English.lproj/localizedStrings.js differ
index 6889408..834f527 100644 (file)
@@ -153,10 +153,13 @@ WebInspector.DOMNode.prototype = {
     _insertChild: function(prev, payload)
     {
         var node = new WebInspector.DOMNode(this.ownerDocument, payload);
-        if (!prev)
-            // First node
-            this.children = [ node ];
-        else
+        if (!prev) {
+            if (!this.children) {
+                // First node
+                this.children = [ node ];
+            } else
+                this.children.unshift(node);
+        } else
             this.children.splice(this.children.indexOf(prev) + 1, 0, node);
         this._renumber();
         return node;
index bdfe5f9..897fdd1 100644 (file)
@@ -527,7 +527,8 @@ WebInspector.ElementsPanel.prototype = {
 
             if (this.recentlyModifiedNodes[i].updated) {
                 var nodeItem = this.treeOutline.findTreeElement(node);
-                nodeItem.updateTitle();
+                if (nodeItem)
+                    nodeItem.updateTitle();
                 continue;
             }
             
index b5dcf94..fbce9c1 100644 (file)
@@ -155,12 +155,27 @@ WebInspector.ElementsTreeOutline.prototype = {
         return treeElement;
     },
 
+    createTreeElementFor: function(node)
+    {
+        var treeElement = this.findTreeElement(node);
+        if (treeElement)
+            return treeElement;
+        if (!node.parentNode)
+            return null;
+
+        var treeElement = this.createTreeElementFor(node.parentNode);
+        if (treeElement && treeElement.showChild(node.index))
+            return treeElement.children[node.index];
+
+        return null;
+    },
+
     revealAndSelectNode: function(node)
     {
         if (!node)
             return;
 
-        var treeElement = this.findTreeElement(node);
+        var treeElement = this.createTreeElementFor(node);
         if (!treeElement)
             return;
 
@@ -297,8 +312,11 @@ WebInspector.ElementsTreeElement = function(node)
     if (this.representedObject.nodeType == Node.ELEMENT_NODE)
         this._canAddAttributes = true;
     this._searchQuery = null;
+    this._expandedChildrenLimit = WebInspector.ElementsTreeElement.InitialChildrenLimit;
 }
 
+WebInspector.ElementsTreeElement.InitialChildrenLimit = 500;
+
 WebInspector.ElementsTreeElement.prototype = {
     highlightSearchResults: function(searchQuery)
     {
@@ -331,6 +349,42 @@ WebInspector.ElementsTreeElement.prototype = {
         }
     },
 
+    get expandedChildrenLimit()
+    {
+        return this._expandedChildrenLimit;
+    },
+
+    set expandedChildrenLimit(x)
+    {
+        if (this._expandedChildrenLimit === x)
+            return;
+
+        this._expandedChildrenLimit = x;
+        if (this.treeOutline && !this._updateChildrenInProgress)
+            this._updateChildren(true);
+    },
+
+    get expandedChildCount()
+    {
+        var count = this.children.length;
+        if (count && this.children[count - 1].elementCloseTag)
+            count--;
+        if (count && this.children[count - 1].expandAllButton)
+            count--;
+        return count;
+    },
+
+    showChild: function(index)
+    {
+        if (index >= this.expandedChildrenLimit) {
+            this._expandedChildrenLimit = index + 1;
+            this._updateChildren(true);
+        }
+
+        // Whether index-th child is visible in the children tree
+        return this.expandedChildCount > index;
+    },
+
     createTooltipForImageNode: function(node, callback)
     {
         function createTooltipThenCallback(properties)
@@ -409,9 +463,34 @@ WebInspector.ElementsTreeElement.prototype = {
         WebInspector.domAgent.getChildNodesAsync(this.representedObject, this._updateChildren.bind(this, fullRefresh));
     },
 
+    insertChildElement: function(child, index)
+    {
+        var newElement = new WebInspector.ElementsTreeElement(child);
+        newElement.selectable = this.treeOutline.selectEnabled;
+        this.insertChild(newElement, index);
+        return newElement;
+    },
+
+    moveChild: function(child, targetIndex)
+    {
+        var wasSelected = child.selected;
+        treeElement.removeChild(child);
+        treeElement.insertChild(child, targetIndex);
+        if (wasSelected)
+            existingTreeElement.select();
+    },
+
     _updateChildren: function(fullRefresh)
     {
+        if (this._updateChildrenInProgress)
+            return;
+
+        this._updateChildrenInProgress = true;
+        var focusedNode = this.treeOutline.focusedDOMNode;
+        var originalScrollTop;
         if (fullRefresh) {
+            var treeOutlineContainerElement = this.treeOutline.element.parentNode;
+            originalScrollTop = treeOutlineContainerElement.scrollTop;
             var selectedTreeElement = this.treeOutline.selectedTreeElement;
             if (selectedTreeElement && selectedTreeElement.hasAncestor(this))
                 this.select();
@@ -420,6 +499,7 @@ WebInspector.ElementsTreeElement.prototype = {
 
         var treeElement = this;
         var treeChildIndex = 0;
+        var elementToSelect;
 
         function updateChildrenOfNode(node)
         {
@@ -430,7 +510,7 @@ WebInspector.ElementsTreeElement.prototype = {
                 if (!currentTreeElement || currentTreeElement.representedObject !== child) {
                     // Find any existing element that is later in the children list.
                     var existingTreeElement = null;
-                    for (var i = (treeChildIndex + 1); i < treeElement.children.length; ++i) {
+                    for (var i = (treeChildIndex + 1), size = treeElement.expandedChildCount; i < size; ++i) {
                         if (treeElement.children[i].representedObject === child) {
                             existingTreeElement = treeElement.children[i];
                             break;
@@ -439,16 +519,16 @@ WebInspector.ElementsTreeElement.prototype = {
 
                     if (existingTreeElement && existingTreeElement.parent === treeElement) {
                         // If an existing element was found and it has the same parent, just move it.
-                        var wasSelected = existingTreeElement.selected;
-                        treeElement.removeChild(existingTreeElement);
-                        treeElement.insertChild(existingTreeElement, treeChildIndex);
-                        if (wasSelected)
-                            existingTreeElement.select();
+                        treeElement.moveChild(existingTreeElement, treeChildIndex);
                     } else {
                         // No existing element found, insert a new element.
-                        var newElement = new WebInspector.ElementsTreeElement(child);
-                        newElement.selectable = treeOutline.selectEnabled;
-                        treeElement.insertChild(newElement, treeChildIndex);
+                        if (treeChildIndex < treeElement.expandedChildrenLimit) {
+                            var newElement = treeElement.insertChildElement(child, treeChildIndex);
+                            if (child === focusedNode)
+                                elementToSelect = newElement;
+                            if (treeElement.expandedChildCount > treeElement.expandedChildrenLimit)
+                                treeElement.expandedChildrenLimit++;
+                        }
                     }
                 }
 
@@ -477,6 +557,7 @@ WebInspector.ElementsTreeElement.prototype = {
         }
 
         updateChildrenOfNode(this.representedObject);
+        this.adjustCollapsedRange(false);
 
         var lastChild = this.children[this.children.length - 1];
         if (this.representedObject.nodeType == Node.ELEMENT_NODE && (!lastChild || !lastChild.elementCloseTag)) {
@@ -486,6 +567,55 @@ WebInspector.ElementsTreeElement.prototype = {
             item.elementCloseTag = true;
             this.appendChild(item);
         }
+
+        // We want to restore the original selection and tree scroll position after a full refresh, if possible.
+        if (fullRefresh && elementToSelect) {
+            elementToSelect.select();
+            if (treeOutlineContainerElement && originalScrollTop <= treeOutlineContainerElement.scrollHeight)
+                treeOutlineContainerElement.scrollTop = originalScrollTop;
+        }
+
+        delete this._updateChildrenInProgress;
+    },
+
+    adjustCollapsedRange: function()
+    {
+        // Ensure precondition: only the tree elements for node children are found in the tree
+        // (not the Expand All button or the closing tag).
+        if (this.expandAllButtonElement && this.expandAllButtonElement.__treeElement.parent)
+            this.removeChild(this.expandAllButtonElement.__treeElement);
+
+        const node = this.representedObject;
+        if (!node.children)
+            return;
+        const childNodeCount = node.children.length;
+
+        // In case some nodes from the expanded range were removed, pull some nodes from the collapsed range into the expanded range at the bottom.
+        for (var i = this.expandedChildCount, limit = Math.min(this.expandedChildrenLimit, childNodeCount); i < limit; ++i)
+            this.insertChildElement(node.children[i], i);
+
+        const expandedChildCount = this.expandedChildCount;
+        if (childNodeCount > this.expandedChildCount) {
+            var targetButtonIndex = expandedChildCount;
+            if (!this.expandAllButtonElement) {
+                var title = "<button class=\"show-all-nodes\" value=\"\" />";
+                var item = new TreeElement(title, null, false);
+                item.selectable = false;
+                item.expandAllButton = true;
+                this.insertChild(item, targetButtonIndex);
+                this.expandAllButtonElement = item.listItemElement.firstChild;
+                this.expandAllButtonElement.__treeElement = item;
+                this.expandAllButtonElement.addEventListener("click", this.handleLoadAllChildren.bind(this), false);
+            } else if (!this.expandAllButtonElement.__treeElement.parent)
+                this.insertChild(this.expandAllButtonElement.__treeElement, targetButtonIndex);
+            this.expandAllButtonElement.textContent = WebInspector.UIString("Show All Nodes (%d More)", childNodeCount - expandedChildCount);
+        } else if (this.expandAllButtonElement)
+            delete this.expandAllButtonElement;
+    },
+
+    handleLoadAllChildren: function()
+    {
+        this.expandedChildrenLimit = Math.max(this.representedObject._childNodeCount, this.expandedChildrenLimit + WebInspector.ElementsTreeElement.InitialChildrenLimit);
     },
 
     onexpand: function()
@@ -1026,6 +1156,7 @@ WebInspector.ElementsTreeElement.prototype = {
                 return;
 
             parentElement.removeChild(self);
+            parentElement.adjustCollapsedRange(true);
         }
 
         var callId = WebInspector.Callback.wrap(removeNodeCallback);
index dc49d6f..174830b 100644 (file)
@@ -2249,7 +2249,7 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des
     margin: 0 0 5px 20px;
 }
 
-.panel-enabler-view button:not(.status-bar-item), .pane button {
+.panel-enabler-view button:not(.status-bar-item), .pane button, button.show-all-nodes {
     color: rgb(6, 6, 6);
     background-color: transparent;
     border: 1px solid rgb(165, 165, 165);
@@ -2266,6 +2266,13 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des
     height: 24px;
 }
 
+button.show-all-nodes {
+    font-size: 13px;
+    margin: 0;
+    padding: 0 20px;
+    height: 20px;
+}
+
 .panel-enabler-view.welcome {
     z-index: auto;
 }
@@ -2297,12 +2304,12 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des
     padding: 2px 9px;
 }
 
-.panel-enabler-view button:active:not(.status-bar-item), .pane button:active {
+.panel-enabler-view button:active:not(.status-bar-item), .pane button:active, button.show-all-nodes:active {
     background-color: rgb(215, 215, 215);
     background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239)));
 }
 
-body.inactive .panel-enabler-view button:not(.status-bar-item), .panel-enabler-view button:disabled:not(.status-bar-item), body.inactive .pane button, .pane button:disabled {
+body.inactive .panel-enabler-view button:not(.status-bar-item), .panel-enabler-view button:disabled:not(.status-bar-item), body.inactive .pane button, .pane button:disabled, body.inactive button.show-all-nodes {
     color: rgb(130, 130, 130);
     border-color: rgb(212, 212, 212);
     background-color: rgb(239, 239, 239);