Bug 103911

Summary: Web Inspector: more robust treeoutline.findTreeElement
Product: WebKit Reporter: johnjbarton <johnjbarton>
Component: Web Inspector (Deprecated)Assignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Normal CC: apavlov, bweinstein, joepeck, keishi, loislo, pfeldman, pmuellr, rik, timothy, vsevik, web-inspector-bugs, webkit.review.bot, yurys
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Attachments:
Description Flags
Patch
none
Patch none

johnjbarton
Reported 2012-12-03 11:32:23 PST
The current implementation of findTreeElement() calls itself recursively. If the tree data is correct, the algorithm (evidently) terminates. If the tree data is incorrect, the algorithm goes in to a loop. I hit this when debugging, the result is 100% CPU and sometimes no slow-script dialog and sometimes memory increases without bound. With the current behavior I am unable to determine what input data is incorrect since I cannot get control or output from the CPU bound process. I believe the current algorithm fails when given incorrect data because of the isAncestor search: for (var i = 0; i < this.children.length; ++i) { item = this.children[i]; if (item.representedObject === representedObject || isAncestor(item.representedObject, representedObject)) { found = true; break; } } When we call findTreeElement() recursively we are expecting an immediate child -- represented by the first conditional expression -- to match. After all we are walking down the ancestor chain from a known treeElement so one step should not require the isAncestor() path. However, if the match we need is not the first child, we check isAncestor() anyway (needlessly in the correct-data case). If we have bad data, then this first child could incorrectly report true for isAncestor(). Then the rest of the code in findTreeElement will cause us to recurse again. This should never happen with correct data. Obviously the errant data need not be in the first child, just any child before the true child. Note that the current code has this comment immediately before the recursive call: // FIXME: we could do something faster than findTreeElement since we will know the next // ancestor exists in the tree A more robust implementation would fix this ;-).
Attachments
Patch (4.67 KB, patch)
2012-12-03 11:43 PST, johnjbarton
no flags
Patch (3.92 KB, patch)
2012-12-04 16:08 PST, johnjbarton
no flags
johnjbarton
Comment 1 2012-12-03 11:43:38 PST
Pavel Feldman
Comment 2 2012-12-03 12:01:52 PST
Comment on attachment 177296 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=177296&action=review > Source/WebCore/inspector/front-end/treeoutline.js:279 > + function oneLevel() { { should go on the next line I also don't think this is right: imagine you get here due to isAncestor(item.representedObject, representedObject) returning true above. And there are no tree elements created for the path from root to given representedObject yet. You get into recursive findTreeElement for the parent object as the next step, but oneLevel (isAncestor substitude) returns false and you never call onpopulate on the parent. > Source/WebCore/inspector/front-end/treeoutline.js:282 > + function noAncestors() { ditto
johnjbarton
Comment 3 2012-12-03 12:37:17 PST
(In reply to comment #2) > I also don't think this is right: imagine you get here due to isAncestor(item.representedObject, representedObject) returning true above. And there are no tree elements created for the path from root to given representedObject yet. You get into recursive findTreeElement for the parent object as the next step, but oneLevel (isAncestor substitude) returns false and you never call onpopulate on the parent. When we reach the first recursive findTreeElement call: childTreeElement = childTreeElement.treeOutline.findTreeElement(ancestors[i], oneLevel, noAncestors); we have i = 0. So the first call to findTreeElement() will use ancestors[0]. This value will be the representedObject for the 'found' childTreeElement, which is one of this.children[]. I was assuming that this.children[] are all in the cache, so the findTreeElement() will return the childTreeElement and we will call onpopulate() on it to begin the process of building the children-tree. Is this assumption incorrect? (all the tests pass FWIW). If my assumption is incorrect, then we need to prepend this.representedObject to the ancestors list. If my assumption is correct, then I will add comments to the code.
johnjbarton
Comment 4 2012-12-04 16:08:41 PST
Build Bot
Comment 5 2012-12-04 19:29:33 PST
johnjbarton
Comment 6 2012-12-05 09:07:43 PST
This patch is only JavaScript. As far as I can tell the win EWS never ran tests, it just failed in some way. Any hints?
WebKit Review Bot
Comment 7 2012-12-05 09:46:51 PST
Comment on attachment 177595 [details] Patch Clearing flags on attachment: 177595 Committed r136706: <http://trac.webkit.org/changeset/136706>
WebKit Review Bot
Comment 8 2012-12-05 09:46:55 PST
All reviewed patches have been landed. Closing bug.
johnjbarton
Comment 9 2012-12-05 10:33:13 PST
In my original report I cited a 100% CPU problem tracked down to the code patched here. I claimed that incorrect remote data could cause the algorithm to recurse. With the patched WebInspector I attempted to reproduce the remote data problem. However I think my understanding of the issue was not quite correct. Here are some outputs from the command line when we are in the patched code just after calling treeElement.onpopulate(): representedObject._nodeName "BODY" treeElement.representedObject._nodeName "HTML" treeElement.children.map(function(c){return c.representedObject._nodeName;}); ["HEAD", "BODY", "HTML"] So for an HTML tag, the representation has three children: HEAD, BODY, and -- surprise to me -- HTML. Evidently the closing tag is represented as child treeElement: treeElement.representedObject === treeElement.children[2].representedObject true Thus the DOMNode data is a tree, but the treeoutline never is. Suppose we have <html><head><body><div></div></body></html> and suppose we have the body treeElement built but not the div. In the old algorithm we visit children of HTML, and we should find BODY because isAncestor(BODY, DIV) should be true. But any failure that might cause this test to fail will result in infinite recursion because the last child will always be true, eg isAncestor(HTML, DIV). This does not explain how the test can fail but it does explain why my testing to verify that the treeoutline is itself a tree fails.
Note You need to log in before you can comment on or make changes to this bug.