Changeset 140807 in webkit


Ignore:
Timestamp:
Jan 25, 2013 2:48:14 AM (11 years ago)
Author:
commit-queue@webkit.org
Message:

Assert the connectedSubframeCount is consistent and fix over counting
https://bugs.webkit.org/show_bug.cgi?id=107302

Patch by Elliott Sprehn <Elliott Sprehn> on 2013-01-25
Reviewed by Alexey Proskuryakov.

Source/WebCore:

Add a debug assertion that walks the subtree during frame disconnection
and manually counts the number of connected subframes to assert that the
value from Node::connectedSubframeCount() is the same as if we traversed
through the tree.

In fixing the places where this assertion failed I made document destruction
faster by not walking the entire document looking for frames if the entire
frame tree has been destroyed by way of FrameLoader::detachChildren().
I had inadvertently introduced this improvement in r133933, but then I
regressed it in r140090 when we switched to counting because I didn't
realize we destroy the frame tree separate of frame disconnection on
document unload so all frames could have been destroyed but the counts
left on the ancestors.

I also fixed another overcounting case where the adoption agency algorithm
may call ContainerNode::takeAllChildrenFrom() which in turn calls
ContainerNode::removeAllChildren() and could have left a connected subframe
count on the node even though all the frames had been removed.

This assertion did not uncover any cases of undercounting the number of
frames.

This also fixes a rare edge case where removeChild of an iframe that
was already being unloaded would not unload the frame until the top level
unload was done, and a reparenting of the iframe would not cause it to load.

Test: fast/frames/reparent-in-unload-contentdocument.html

  • dom/ContainerNode.cpp:

(WebCore::ContainerNode::removeAllChildren):
(WebCore::ContainerNode::parserInsertBefore):
(WebCore::ContainerNode::parserRemoveChild):
(WebCore::ContainerNode::parserAppendChild):

  • dom/ContainerNodeAlgorithms.cpp:

(WebCore):
(WebCore::assertConnectedSubframeCountIsConsistent):

  • dom/ContainerNodeAlgorithms.h:

(WebCore):
(WebCore::ChildFrameDisconnector::disconnect):

  • dom/Node.cpp:

(WebCore::Node::updateAncestorConnectedSubframeCountForRemoval):
(WebCore):
(WebCore::Node::updateAncestorConnectedSubframeCountForInsertion):

  • dom/Node.h:

(Node):

  • html/HTMLFrameOwnerElement.cpp:

(WebCore::HTMLFrameOwnerElement::clearContentFrame):
(WebCore):
(WebCore::HTMLFrameOwnerElement::disconnectContentFrame):

  • html/HTMLFrameOwnerElement.h:

(HTMLFrameOwnerElement):

LayoutTests:

Add a test that removing an iframe in the middle of unload causes the
contentDocument to become immediately inaccessible.

  • fast/frames/reparent-in-unload-contentdocument-expected.txt: Added.
  • fast/frames/reparent-in-unload-contentdocument.html: Added.
Location:
trunk
Files:
2 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r140803 r140807  
     12013-01-25  Elliott Sprehn  <esprehn@gmail.com>
     2
     3        Assert the connectedSubframeCount is consistent and fix over counting
     4        https://bugs.webkit.org/show_bug.cgi?id=107302
     5
     6        Reviewed by Alexey Proskuryakov.
     7
     8        Add a test that removing an iframe in the middle of unload causes the
     9        contentDocument to become immediately inaccessible.
     10
     11        * fast/frames/reparent-in-unload-contentdocument-expected.txt: Added.
     12        * fast/frames/reparent-in-unload-contentdocument.html: Added.
     13
    1142013-01-25  Kent Tamura  <tkent@chromium.org>
    215
  • trunk/Source/WebCore/ChangeLog

    r140805 r140807  
     12013-01-25  Elliott Sprehn  <esprehn@gmail.com>
     2
     3        Assert the connectedSubframeCount is consistent and fix over counting
     4        https://bugs.webkit.org/show_bug.cgi?id=107302
     5
     6        Reviewed by Alexey Proskuryakov.
     7
     8        Add a debug assertion that walks the subtree during frame disconnection
     9        and manually counts the number of connected subframes to assert that the
     10        value from Node::connectedSubframeCount() is the same as if we traversed
     11        through the tree.
     12
     13        In fixing the places where this assertion failed I made document destruction
     14        faster by not walking the entire document looking for frames if the entire
     15        frame tree has been destroyed by way of FrameLoader::detachChildren().
     16        I had inadvertently introduced this improvement in r133933, but then I
     17        regressed it in r140090 when we switched to counting because I didn't
     18        realize we destroy the frame tree separate of frame disconnection on
     19        document unload so all frames could have been destroyed but the counts
     20        left on the ancestors.
     21
     22        I also fixed another overcounting case where the adoption agency algorithm
     23        may call ContainerNode::takeAllChildrenFrom() which in turn calls
     24        ContainerNode::removeAllChildren() and could have left a connected subframe
     25        count on the node even though all the frames had been removed.
     26
     27        This assertion did not uncover any cases of undercounting the number of
     28        frames.
     29
     30        This also fixes a rare edge case where removeChild of an iframe that
     31        was already being unloaded would not unload the frame until the top level
     32        unload was done, and a reparenting of the iframe would not cause it to load.
     33
     34        Test: fast/frames/reparent-in-unload-contentdocument.html
     35
     36        * dom/ContainerNode.cpp:
     37        (WebCore::ContainerNode::removeAllChildren):
     38        (WebCore::ContainerNode::parserInsertBefore):
     39        (WebCore::ContainerNode::parserRemoveChild):
     40        (WebCore::ContainerNode::parserAppendChild):
     41        * dom/ContainerNodeAlgorithms.cpp:
     42        (WebCore):
     43        (WebCore::assertConnectedSubframeCountIsConsistent):
     44        * dom/ContainerNodeAlgorithms.h:
     45        (WebCore):
     46        (WebCore::ChildFrameDisconnector::disconnect):
     47        * dom/Node.cpp:
     48        (WebCore::Node::updateAncestorConnectedSubframeCountForRemoval):
     49        (WebCore):
     50        (WebCore::Node::updateAncestorConnectedSubframeCountForInsertion):
     51        * dom/Node.h:
     52        (Node):
     53        * html/HTMLFrameOwnerElement.cpp:
     54        (WebCore::HTMLFrameOwnerElement::clearContentFrame):
     55        (WebCore):
     56        (WebCore::HTMLFrameOwnerElement::disconnectContentFrame):
     57        * html/HTMLFrameOwnerElement.h:
     58        (HTMLFrameOwnerElement):
     59
    1602013-01-25  Pavel Feldman  <pfeldman@chromium.org>
    261
  • trunk/Source/WebCore/dom/ContainerNode.cpp

    r140784 r140807  
    9393void ContainerNode::removeDetachedChildren()
    9494{
     95    if (connectedSubframeCount()) {
     96        for (Node* child = firstChild(); child; child = child->nextSibling())
     97            child->updateAncestorConnectedSubframeCountForRemoval();
     98    }
    9599    // FIXME: We should be able to ASSERT(!attached()) here: https://bugs.webkit.org/show_bug.cgi?id=107801
    96100    removeDetachedChildrenInContainer<Node, ContainerNode>(this);
     
    333337    insertBeforeCommon(nextChild, newChild.get());
    334338
    335     if (unsigned count = newChild->connectedSubframeCount()) {
    336         for (Node* node = newChild->parentOrHostNode(); node; node = node->parentOrHostNode())
    337             node->incrementConnectedSubframeCount(count);
    338     }
     339    newChild->updateAncestorConnectedSubframeCountForInsertion();
    339340
    340341    childrenChanged(true, newChild->previousSibling(), nextChild, 1);
     
    559560    Node* next = oldChild->nextSibling();
    560561
    561     if (unsigned count = oldChild->connectedSubframeCount()) {
    562         for (Node* node = oldChild->parentOrHostNode(); node; node = node->parentOrHostNode())
    563             node->decrementConnectedSubframeCount(count);
    564     }
     562    oldChild->updateAncestorConnectedSubframeCountForRemoval();
    565563
    566564    removeBetween(prev, next, oldChild);
     
    708706    }
    709707
    710     if (unsigned count = newChild->connectedSubframeCount()) {
    711         for (Node* node = newChild->parentOrHostNode(); node; node = node->parentOrHostNode())
    712             node->incrementConnectedSubframeCount(count);
    713     }
     708    newChild->updateAncestorConnectedSubframeCountForInsertion();
    714709
    715710    childrenChanged(true, last, 0, 1);
  • trunk/Source/WebCore/dom/ContainerNodeAlgorithms.cpp

    r140090 r140807  
    122122}
    123123
     124#ifndef NDEBUG
     125unsigned assertConnectedSubrameCountIsConsistent(Node* node)
     126{
     127    unsigned count = 0;
     128
     129    if (node->isElementNode()) {
     130        if (node->isFrameOwnerElement() && toFrameOwnerElement(node)->contentFrame())
     131            count++;
     132
     133        if (ElementShadow* shadow = toElement(node)->shadow()) {
     134            for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
     135                count += assertConnectedSubrameCountIsConsistent(root);
     136        }
     137    }
     138
     139    for (Node* child = node->firstChild(); child; child = child->nextSibling())
     140        count += assertConnectedSubrameCountIsConsistent(child);
     141
     142    // If we undercount there's possibly a security bug since we'd leave frames
     143    // in subtrees outside the document.
     144    ASSERT(node->connectedSubframeCount() >= count);
     145
     146    // If we overcount it's safe, but not optimal because it means we'll traverse
     147    // through the document in ChildFrameDisconnector looking for frames that have
     148    // already been disconnected.
     149    ASSERT(node->connectedSubframeCount() == count);
     150
     151    return count;
    124152}
     153#endif
     154
     155}
  • trunk/Source/WebCore/dom/ContainerNodeAlgorithms.h

    r140784 r140807  
    298298};
    299299
     300#ifndef NDEBUG
     301unsigned assertConnectedSubrameCountIsConsistent(Node*);
     302#endif
     303
    300304inline void ChildFrameDisconnector::collectFrameOwners(Node* root)
    301305{
     
    331335inline void ChildFrameDisconnector::disconnect(DisconnectPolicy policy)
    332336{
     337#ifndef NDEBUG
     338    assertConnectedSubrameCountIsConsistent(m_root);
     339#endif
     340
    333341    if (!m_root->connectedSubframeCount())
    334342        return;
  • trunk/Source/WebCore/dom/Node.cpp

    r140745 r140807  
    26212621}
    26222622
     2623void Node::updateAncestorConnectedSubframeCountForRemoval() const
     2624{
     2625    unsigned count = connectedSubframeCount();
     2626
     2627    if (!count)
     2628        return;
     2629
     2630    for (Node* node = parentOrHostNode(); node; node = node->parentOrHostNode())
     2631        node->decrementConnectedSubframeCount(count);
     2632}
     2633
     2634void Node::updateAncestorConnectedSubframeCountForInsertion() const
     2635{
     2636    unsigned count = connectedSubframeCount();
     2637
     2638    if (!count)
     2639        return;
     2640
     2641    for (Node* node = parentOrHostNode(); node; node = node->parentOrHostNode())
     2642        node->incrementConnectedSubframeCount(count);
     2643}
     2644
    26232645void Node::registerScopedHTMLStyleChild()
    26242646{
  • trunk/Source/WebCore/dom/Node.h

    r140546 r140807  
    682682    void incrementConnectedSubframeCount(unsigned amount = 1);
    683683    void decrementConnectedSubframeCount(unsigned amount = 1);
     684    void updateAncestorConnectedSubframeCountForRemoval() const;
     685    void updateAncestorConnectedSubframeCountForInsertion() const;
    684686
    685687private:
  • trunk/Source/WebCore/html/HTMLFrameOwnerElement.cpp

    r140090 r140807  
    6363}
    6464
     65void HTMLFrameOwnerElement::clearContentFrame()
     66{
     67    if (!m_contentFrame)
     68        return;
     69
     70    m_contentFrame = 0;
     71
     72    for (ContainerNode* node = this; node; node = node->parentOrHostNode())
     73        node->decrementConnectedSubframeCount();
     74}
     75
    6576void HTMLFrameOwnerElement::disconnectContentFrame()
    6677{
     
    7182    // see if this behavior is really needed as Gecko does not allow this.
    7283    if (Frame* frame = contentFrame()) {
    73         for (ContainerNode* node = this; node; node = node->parentOrHostNode())
    74             node->decrementConnectedSubframeCount();
    75 
    7684        RefPtr<Frame> protect(frame);
    7785        frame->loader()->frameDetached();
  • trunk/Source/WebCore/html/HTMLFrameOwnerElement.h

    r134775 r140807  
    4444
    4545    void setContentFrame(Frame*);
    46     void clearContentFrame() { m_contentFrame = 0; }
     46    void clearContentFrame();
    4747
    4848    void disconnectContentFrame();
Note: See TracChangeset for help on using the changeset viewer.