Home | History | Annotate | Download | only in dom
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "core/dom/ChildFrameDisconnector.h"
      7 
      8 #include "core/dom/shadow/ElementShadow.h"
      9 #include "core/dom/shadow/ShadowRoot.h"
     10 #include "core/html/HTMLFrameOwnerElement.h"
     11 #include "wtf/Assertions.h"
     12 
     13 namespace blink {
     14 
     15 #if ENABLE(ASSERT)
     16 static unsigned checkConnectedSubframeCountIsConsistent(Node&);
     17 #endif
     18 
     19 void ChildFrameDisconnector::disconnect(DisconnectPolicy policy)
     20 {
     21 #if ENABLE(ASSERT)
     22     checkConnectedSubframeCountIsConsistent(root());
     23 #endif
     24 
     25     if (!root().connectedSubframeCount())
     26         return;
     27 
     28     if (policy == RootAndDescendants) {
     29         collectFrameOwners(root());
     30     } else {
     31         for (Node* child = root().firstChild(); child; child = child->nextSibling())
     32             collectFrameOwners(*child);
     33     }
     34 
     35     disconnectCollectedFrameOwners();
     36 }
     37 
     38 void ChildFrameDisconnector::collectFrameOwners(Node& root)
     39 {
     40     if (!root.connectedSubframeCount())
     41         return;
     42 
     43     if (root.isHTMLElement() && root.isFrameOwnerElement())
     44         m_frameOwners.append(&toHTMLFrameOwnerElement(root));
     45 
     46     for (Node* child = root.firstChild(); child; child = child->nextSibling())
     47         collectFrameOwners(*child);
     48 
     49     ElementShadow* shadow = root.isElementNode() ? toElement(root).shadow() : 0;
     50     if (shadow)
     51         collectFrameOwners(*shadow);
     52 }
     53 
     54 void ChildFrameDisconnector::disconnectCollectedFrameOwners()
     55 {
     56     // Must disable frame loading in the subtree so an unload handler cannot
     57     // insert more frames and create loaded frames in detached subtrees.
     58     SubframeLoadingDisabler disabler(root());
     59 
     60     for (unsigned i = 0; i < m_frameOwners.size(); ++i) {
     61         HTMLFrameOwnerElement* owner = m_frameOwners[i].get();
     62         // Don't need to traverse up the tree for the first owner since no
     63         // script could have moved it.
     64         if (!i || root().containsIncludingShadowDOM(owner))
     65             owner->disconnectContentFrame();
     66     }
     67 }
     68 
     69 void ChildFrameDisconnector::collectFrameOwners(ElementShadow& shadow)
     70 {
     71     for (ShadowRoot* root = shadow.youngestShadowRoot(); root; root = root->olderShadowRoot())
     72         collectFrameOwners(*root);
     73 }
     74 
     75 #if ENABLE(ASSERT)
     76 static unsigned checkConnectedSubframeCountIsConsistent(Node& node)
     77 {
     78     unsigned count = 0;
     79 
     80     if (node.isElementNode()) {
     81         if (node.isFrameOwnerElement() && toHTMLFrameOwnerElement(node).contentFrame())
     82             count++;
     83 
     84         if (ElementShadow* shadow = toElement(node).shadow()) {
     85             for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
     86                 count += checkConnectedSubframeCountIsConsistent(*root);
     87         }
     88     }
     89 
     90     for (Node* child = node.firstChild(); child; child = child->nextSibling())
     91         count += checkConnectedSubframeCountIsConsistent(*child);
     92 
     93     // If we undercount there's possibly a security bug since we'd leave frames
     94     // in subtrees outside the document.
     95     ASSERT(node.connectedSubframeCount() >= count);
     96 
     97     // If we overcount it's safe, but not optimal because it means we'll traverse
     98     // through the document in ChildFrameDisconnector looking for frames that have
     99     // already been disconnected.
    100     ASSERT(node.connectedSubframeCount() == count);
    101 
    102     return count;
    103 }
    104 #endif
    105 
    106 }
    107