static unsigned assertConnectedSubrameCountIsConsistent(ContainerNode& node) { unsigned count = 0; if (is<Element>(node)) { if (is<HTMLFrameOwnerElement>(node) && downcast<HTMLFrameOwnerElement>(node).contentFrame()) ++count; if (ShadowRoot* root = downcast<Element>(node).shadowRoot()) count += assertConnectedSubrameCountIsConsistent(*root); } for (auto& child : childrenOfType<Element>(node)) count += assertConnectedSubrameCountIsConsistent(child); // If we undercount there's possibly a security bug since we'd leave frames // in subtrees outside the document. ASSERT(node.connectedSubframeCount() >= count); // If we overcount it's safe, but not optimal because it means we'll traverse // through the document in disconnectSubframes looking for frames that have // already been disconnected. ASSERT(node.connectedSubframeCount() == count); return count; }
void disconnectSubframes(ContainerNode& root, SubframeDisconnectPolicy policy) { #ifndef NDEBUG assertConnectedSubrameCountIsConsistent(root); #endif ASSERT(root.connectedSubframeCount()); Vector<Ref<HTMLFrameOwnerElement>> frameOwners; if (policy == RootAndDescendants) { if (is<HTMLFrameOwnerElement>(root)) frameOwners.append(downcast<HTMLFrameOwnerElement>(root)); } collectFrameOwners(frameOwners, root); // Must disable frame loading in the subtree so an unload handler cannot // insert more frames and create loaded frames in detached subtrees. SubframeLoadingDisabler disabler(root); bool isFirst = true; for (auto& owner : frameOwners) { // Don't need to traverse up the tree for the first owner since no // script could have moved it. if (isFirst || root.containsIncludingShadowDOM(&owner.get())) owner.get().disconnectContentFrame(); isFirst = false; } }