inline Node* ComposedTreeWalker::traverseParentOrHost(const Node* node) const { Node* parent = node->parentNode(); if (!parent) return 0; if (!parent->isShadowRoot()) return parent; ShadowRoot* shadowRoot = toShadowRoot(parent); ASSERT(!shadowRoot->shadowInsertionPointOfYoungerShadowRoot()); if (!shadowRoot->isYoungest()) return 0; return shadowRoot->host(); }
Node::InsertionNotificationRequest HTMLContentElement::insertedInto(ContainerNode* insertionPoint) { InsertionPoint::insertedInto(insertionPoint); if (insertionPoint->inDocument() && isActive()) { ShadowRoot* root = shadowRoot(); root->registerContentElement(); root->owner()->setShouldCollectSelectFeatureSet(); m_registeredWithShadowRoot = true; } return InsertionDone; }
// FIXME: Move the following helper functions, authorShadowRootOf, firstWithinTraversingShadowTree, // nextTraversingShadowTree to the best place, e.g. NodeTraversal. static ShadowRoot* authorShadowRootOf(const ContainerNode& node) { if (!node.isElementNode() || !isShadowHost(&node)) return nullptr; ElementShadow* shadow = toElement(node).shadow(); ASSERT(shadow); for (ShadowRoot* shadowRoot = shadow->oldestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->youngerShadowRoot()) { if (shadowRoot->type() == ShadowRootType::OpenByDefault || shadowRoot->type() == ShadowRootType::Open) return shadowRoot; } return nullptr; }
void TreeScope::setNeedsStyleRecalcForViewportUnits() { for (Element* element = ElementTraversal::firstWithin(rootNode()); element; element = ElementTraversal::nextIncludingPseudo(*element)) { for (ShadowRoot* root = element->youngestShadowRoot(); root; root = root->olderShadowRoot()) root->setNeedsStyleRecalcForViewportUnits(); const ComputedStyle* style = element->computedStyle(); if (style && style->hasViewportUnits()) element->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create( StyleChangeReason::ViewportUnits)); } }
void SVGTRefElement::updateReferencedText() { String textContent; if (Element* target = SVGURIReference::targetElementFromIRIString(href(), document())) textContent = target->textContent(); ASSERT(hasShadowRoot()); ShadowRoot* root = shadowTree()->oldestShadowRoot(); if (!root->firstChild()) root->appendChild(SVGShadowText::create(document(), textContent), ASSERT_NO_EXCEPTION); else root->firstChild()->setTextContent(textContent, ASSERT_NO_EXCEPTION); }
void ShadowRoot::removedFrom(ContainerNode* insertionPoint) { if (insertionPoint->inDocument() && m_registeredWithParentShadowRoot) { ShadowRoot* root = host()->containingShadowRoot(); if (!root) root = insertionPoint->containingShadowRoot(); if (root) root->removeChildShadowRoot(); m_registeredWithParentShadowRoot = false; } DocumentFragment::removedFrom(insertionPoint); }
ContainerNode* ComposedTreeTraversal::traverseParentOrHost(const Node& node) { ContainerNode* parent = node.parentNode(); if (!parent) return nullptr; if (!parent->isShadowRoot()) return parent; ShadowRoot* shadowRoot = toShadowRoot(parent); ASSERT(!shadowRoot->shadowInsertionPointOfYoungerShadowRoot()); if (!shadowRoot->isYoungest()) return nullptr; return shadowRoot->host(); }
void ShadowRoot::removedFrom(ContainerNode* insertionPoint) { ShadowRoot* root = host()->containingShadowRoot(); if (!root) root = insertionPoint->containingShadowRoot(); if (root) root->removeChildShadowRoot(); if (inActiveDocument()) document().styleEngine()->removeTreeScope(*this); DocumentFragment::removedFrom(insertionPoint); }
void ShadowTree::attach() { // Children of m_selector is populated lazily in // ensureSelector(), and here we just ensure that it is in clean state. ASSERT(!selector().hasPopulated()); selector().willSelect(); for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) { if (!root->attached()) root->attach(); } selector().didSelect(); }
Node* ComposedTreeWalker::traverseBackToYoungerShadowRoot(const Node* node, TraversalDirection direction) { ASSERT(node); if (node->parentNode() && node->parentNode()->isShadowRoot()) { ShadowRoot* parentShadowRoot = toShadowRoot(node->parentNode()); if (!parentShadowRoot->isYoungest()) { HTMLShadowElement* assignedInsertionPoint = parentShadowRoot->shadowInsertionPointOfYoungerShadowRoot(); ASSERT(assignedInsertionPoint); return traverseSiblingInCurrentTree(assignedInsertionPoint, direction); } } return 0; }
Node* ComposedShadowTreeWalker::traverseBackToYoungerShadowRoot(const Node* node, TraversalDirection direction) { ASSERT(node); if (node->parentNode() && node->parentNode()->isShadowRoot()) { ShadowRoot* parentShadowRoot = toShadowRoot(node->parentNode()); if (!parentShadowRoot->isYoungest()) { InsertionPoint* assignedInsertionPoint = ScopeContentDistribution::assignedTo(parentShadowRoot); ASSERT(assignedInsertionPoint); return traverseSiblingInCurrentTree(assignedInsertionPoint, direction); } } return 0; }
ShadowRoot* HTMLShadowElement::olderShadowRoot() { ShadowRoot* containingRoot = containingShadowRoot(); if (!containingRoot) return 0; ContentDistributor::ensureDistribution(containingRoot); ShadowRoot* older = containingRoot->olderShadowRoot(); if (!older || older->type() != ShadowRoot::AuthorShadowRoot || ScopeContentDistribution::assignedTo(older) != this) return 0; return older; }
static bool shouldBypassMainWorldCSP(Element* element) { // Main world CSP is bypassed within an isolated world. LocalFrame* frame = element->document().frame(); if (frame && frame->script().shouldBypassMainWorldCSP()) return true; // Main world CSP is bypassed for style elements in user agent shadow DOM. ShadowRoot* root = element->containingShadowRoot(); if (root && root->type() == ShadowRootType::UserAgent) return true; return false; }
static void attachShadowRoot(ShadowRoot& shadowRoot) { if (shadowRoot.attached()) return; StyleResolver& styleResolver = shadowRoot.document().ensureStyleResolver(); styleResolver.pushParentShadowRoot(&shadowRoot); attachChildren(shadowRoot); styleResolver.popParentShadowRoot(&shadowRoot); shadowRoot.clearNeedsStyleRecalc(); shadowRoot.setAttached(true); }
TEST(TreeScopeTest, CommonAncestorOfInclusiveTrees) { // document // | : Common ancestor is document. // shadowRoot Document* document = Document::create(); Element* html = document->createElement("html", StringOrDictionary()); document->appendChild(html); ShadowRoot* shadowRoot = html->createShadowRootInternal(ShadowRootType::V0, ASSERT_NO_EXCEPTION); EXPECT_EQ(document, document->commonAncestorTreeScope(*shadowRoot)); EXPECT_EQ(document, shadowRoot->commonAncestorTreeScope(*document)); }
void ShadowRoot::removedFrom(ContainerNode* insertionPoint) { if (insertionPoint->inDocument() && m_registeredWithParentShadowRoot) { ShadowRoot* root = host()->containingShadowRoot(); if (!root) root = insertionPoint->containingShadowRoot(); if (root && root->scopeDistribution()) root->scopeDistribution()->unregisterElementShadow(); m_registeredWithParentShadowRoot = false; } DocumentFragment::removedFrom(insertionPoint); }
// Test case for // - childAt // - countChildren // - hasChildren // - index // - isDescendantOf TEST_F(FlatTreeTraversalTest, childAt) { const char* mainHTML = "<div id='m0'>" "<span id='m00'>m00</span>" "<span id='m01'>m01</span>" "</div>"; const char* shadowHTML = "<a id='s00'>s00</a>" "<content select='#m01'></content>" "<a id='s02'>s02</a>" "<a id='s03'><content select='#m00'></content></a>" "<a id='s04'>s04</a>"; setupSampleHTML(mainHTML, shadowHTML, 0); Element* body = document().body(); Element* m0 = body->querySelector("#m0"); Element* m00 = m0->querySelector("#m00"); Element* m01 = m0->querySelector("#m01"); Element* shadowHost = m0; ShadowRoot* shadowRoot = shadowHost->openShadowRoot(); Element* s00 = shadowRoot->querySelector("#s00"); Element* s02 = shadowRoot->querySelector("#s02"); Element* s03 = shadowRoot->querySelector("#s03"); Element* s04 = shadowRoot->querySelector("#s04"); const unsigned numberOfChildNodes = 5; Node* expectedChildNodes[5] = {s00, m01, s02, s03, s04}; ASSERT_EQ(numberOfChildNodes, FlatTreeTraversal::countChildren(*shadowHost)); EXPECT_TRUE(FlatTreeTraversal::hasChildren(*shadowHost)); for (unsigned index = 0; index < numberOfChildNodes; ++index) { Node* child = FlatTreeTraversal::childAt(*shadowHost, index); EXPECT_EQ(expectedChildNodes[index], child) << "FlatTreeTraversal::childAt(*shadowHost, " << index << ")"; EXPECT_EQ(index, FlatTreeTraversal::index(*child)) << "FlatTreeTraversal::index(FlatTreeTraversal(*shadowHost, " << index << "))"; EXPECT_TRUE(FlatTreeTraversal::isDescendantOf(*child, *shadowHost)) << "FlatTreeTraversal::isDescendantOf(*FlatTreeTraversal(*shadowHost, " << index << "), *shadowHost)"; } EXPECT_EQ(nullptr, FlatTreeTraversal::childAt(*shadowHost, numberOfChildNodes + 1)) << "Out of bounds childAt() returns nullptr."; // Distribute node |m00| is child of node in shadow tree |s03|. EXPECT_EQ(m00, FlatTreeTraversal::childAt(*s03, 0)); }
void ElementShadow::collectSelectFeatureSetFrom(ShadowRoot& root) { if (!root.containsShadowRoots() && !root.containsContentElements()) return; for (Element& element : ElementTraversal::descendantsOf(root)) { if (ElementShadow* shadow = element.shadow()) m_selectFeatures.add(shadow->ensureSelectFeatureSet()); if (!isHTMLContentElement(element)) continue; const CSSSelectorList& list = toHTMLContentElement(element).selectorList(); m_selectFeatures.collectFeaturesFromSelectorList(list); } }
TEST_F(FlatTreeTraversalTest, previousPostOrder) { const char* mainHTML = "<div id='m0'>m0</div>" "<div id='m1'>" "<span id='m10'>m10</span>" "<span id='m11'>m11</span>" "</div>" "<div id='m2'>m2</div>"; const char* shadowHTML = "<content select='#m11'></content>" "<a id='s11'>s11</a>" "<a id='s12'>" "<b id='s120'>s120</b>" "<content select='#m10'></content>" "</a>"; setupSampleHTML(mainHTML, shadowHTML, 1); Element* body = document().body(); Element* m0 = body->querySelector("#m0"); Element* m1 = body->querySelector("#m1"); Element* m2 = body->querySelector("#m2"); Element* m10 = body->querySelector("#m10"); Element* m11 = body->querySelector("#m11"); ShadowRoot* shadowRoot = m1->openShadowRoot(); Element* s11 = shadowRoot->querySelector("#s11"); Element* s12 = shadowRoot->querySelector("#s12"); Element* s120 = shadowRoot->querySelector("#s120"); EXPECT_EQ(*m0->firstChild(), FlatTreeTraversal::previousPostOrder(*m0)); EXPECT_EQ(*s12, FlatTreeTraversal::previousPostOrder(*m1)); EXPECT_EQ(*m10->firstChild(), FlatTreeTraversal::previousPostOrder(*m10)); EXPECT_EQ(*s120, FlatTreeTraversal::previousPostOrder(*m10->firstChild())); EXPECT_EQ(*s120, FlatTreeTraversal::previousPostOrder(*m10->firstChild(), s12)); EXPECT_EQ(*m11->firstChild(), FlatTreeTraversal::previousPostOrder(*m11)); EXPECT_EQ(*m0, FlatTreeTraversal::previousPostOrder(*m11->firstChild())); EXPECT_EQ(nullptr, FlatTreeTraversal::previousPostOrder(*m11->firstChild(), m11)); EXPECT_EQ(*m2->firstChild(), FlatTreeTraversal::previousPostOrder(*m2)); EXPECT_EQ(*s11->firstChild(), FlatTreeTraversal::previousPostOrder(*s11)); EXPECT_EQ(*m10, FlatTreeTraversal::previousPostOrder(*s12)); EXPECT_EQ(*s120->firstChild(), FlatTreeTraversal::previousPostOrder(*s120)); EXPECT_EQ(*s11, FlatTreeTraversal::previousPostOrder(*s120->firstChild())); EXPECT_EQ(nullptr, FlatTreeTraversal::previousPostOrder(*s120->firstChild(), s12)); }
void TreeScopeAdopter::moveTreeToNewScope(Node& root) const { ASSERT(needsScopeChange()); #if !ENABLE(OILPAN) m_oldScope.guardRef(); #endif // If an element is moved from a document and then eventually back again the collection cache for // that element may contain stale data as changes made to it will have updated the DOMTreeVersion // of the document it was moved to. By increasing the DOMTreeVersion of the donating document here // we ensure that the collection cache will be invalidated as needed when the element is moved back. Document& oldDocument = m_oldScope.document(); Document& newDocument = m_newScope.document(); bool willMoveToNewDocument = oldDocument != newDocument; if (willMoveToNewDocument) oldDocument.incDOMTreeVersion(); for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) { updateTreeScope(*node); if (willMoveToNewDocument) moveNodeToNewDocument(*node, oldDocument, newDocument); else if (node->hasRareData()) { NodeRareData* rareData = node->rareData(); if (rareData->nodeLists()) rareData->nodeLists()->adoptTreeScope(); } if (!node->isElementNode()) continue; if (node->hasSyntheticAttrChildNodes()) { WillBeHeapVector<RefPtrWillBeMember<Attr> >& attrs = *toElement(node)->attrNodeList(); for (unsigned i = 0; i < attrs.size(); ++i) moveTreeToNewScope(*attrs[i]); } for (ShadowRoot* shadow = node->youngestShadowRoot(); shadow; shadow = shadow->olderShadowRoot()) { shadow->setParentTreeScope(m_newScope); if (willMoveToNewDocument) moveTreeToNewDocument(*shadow, oldDocument, newDocument); } } #if !ENABLE(OILPAN) m_oldScope.guardDeref(); #endif }
void TreeScopeAdopter::moveTreeToNewScope(Node& root) const { ASSERT(needsScopeChange()); #if !ENABLE(OILPAN) oldScope().guardRef(); #endif // If an element is moved from a document and then eventually back again the collection cache for // that element may contain stale data as changes made to it will have updated the DOMTreeVersion // of the document it was moved to. By increasing the DOMTreeVersion of the donating document here // we ensure that the collection cache will be invalidated as needed when the element is moved back. Document& oldDocument = oldScope().document(); Document& newDocument = newScope().document(); bool willMoveToNewDocument = oldDocument != newDocument; if (willMoveToNewDocument) oldDocument.incDOMTreeVersion(); for (Node& node : NodeTraversal::inclusiveDescendantsOf(root)) { updateTreeScope(node); if (willMoveToNewDocument) { moveNodeToNewDocument(node, oldDocument, newDocument); } else if (node.hasRareData()) { NodeRareData* rareData = node.rareData(); if (rareData->nodeLists()) rareData->nodeLists()->adoptTreeScope(); } if (!node.isElementNode()) continue; Element& element = toElement(node); if (WillBeHeapVector<RefPtrWillBeMember<Attr>>* attrs = element.attrNodeList()) { for (const auto& attr : *attrs) moveTreeToNewScope(*attr); } for (ShadowRoot* shadow = element.youngestShadowRoot(); shadow; shadow = shadow->olderShadowRoot()) { shadow->setParentTreeScope(newScope()); if (willMoveToNewDocument) moveTreeToNewDocument(*shadow, oldDocument, newDocument); } } #if !ENABLE(OILPAN) oldScope().guardDeref(); #endif }
void SVGTRefElement::updateReferencedText(Element* target) { String textContent; if (target) textContent = target->textContent(); ASSERT(shadowRoot()); ShadowRoot* root = shadowRoot(); if (!root->firstChild()) root->appendChild(Text::create(document(), textContent), ASSERT_NO_EXCEPTION); else { ASSERT(root->firstChild()->isTextNode()); root->firstChild()->setTextContent(textContent, ASSERT_NO_EXCEPTION); } }
void HTMLPlugInImageElement::checkSnapshotStatus() { if (!renderer()->isSnapshottedPlugIn()) { if (displayState() == Playing) checkSizeChangeForSnapshotting(); return; } ShadowRoot* root = userAgentShadowRoot(); if (!root) return; Element* shadowContainer = toElement(root->firstChild()); shadowContainer->setAttribute(classAttr, classNameForShadowRoot(this)); }
void HTMLSlotElement::enqueueSlotChangeEvent() { if (!m_slotchangeEventEnqueued) { Microtask::enqueueMicrotask(WTF::bind( &HTMLSlotElement::dispatchSlotChangeEvent, wrapPersistent(this))); m_slotchangeEventEnqueued = true; } ShadowRoot* root = containingShadowRoot(); DCHECK(root); DCHECK(root->isV1()); root->owner()->setNeedsDistributionRecalc(); // Check slotchange recursively since this slotchange may cause another // slotchange. checkSlotChange(); }
void HTMLOptGroupElement::didAddUserAgentShadowRoot(ShadowRoot& root) { DEFINE_STATIC_LOCAL(AtomicString, labelPadding, ("0 2px 1px 2px")); DEFINE_STATIC_LOCAL(AtomicString, labelMinHeight, ("1.2em")); HTMLDivElement* label = HTMLDivElement::create(document()); label->setAttribute(roleAttr, AtomicString("group")); label->setAttribute(aria_labelAttr, AtomicString()); label->setInlineStyleProperty(CSSPropertyPadding, labelPadding); label->setInlineStyleProperty(CSSPropertyMinHeight, labelMinHeight); label->setIdAttribute(ShadowElementNames::optGroupLabel()); root.appendChild(label); HTMLContentElement* content = HTMLContentElement::create(document()); content->setAttribute(selectAttr, "option,hr"); root.appendChild(content); }
void HTMLContentElement::removedFrom(ContainerNode* insertionPoint) { if (insertionPoint->inDocument() && m_registeredWithShadowRoot) { ShadowRoot* root = shadowRoot(); if (!root) root = insertionPoint->shadowRoot(); if (root) root->unregisterContentElement(); m_registeredWithShadowRoot = false; if (ElementShadow* elementShadow = root ? root->owner() : 0) elementShadow->setShouldCollectSelectFeatureSet(); } InsertionPoint::removedFrom(insertionPoint); }
void ContentDistributor::ensureDistribution(ShadowRoot* shadowRoot) { ASSERT(shadowRoot); Vector<ShadowRoot*, 8> shadowRoots; for (Element* current = shadowRoot->hostElement(); current; current = current->shadowHost()) { ShadowRoot* currentRoot = current->shadowRoot(); if (!currentRoot->distributor().needsDistribution()) break; shadowRoots.append(currentRoot); } for (size_t i = shadowRoots.size(); i > 0; --i) shadowRoots[i - 1]->distributor().distribute(shadowRoots[i - 1]->hostElement()); }
nsresult HTMLContentElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix, const nsAString& aValue, bool aNotify) { nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, aNotify); NS_ENSURE_SUCCESS(rv, rv); if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::select) { // Select attribute was updated, the insertion point may match different // elements. nsIDocument* doc = OwnerDoc(); nsCSSParser parser(doc->CSSLoader()); mValidSelector = true; mSelectorList = nullptr; nsresult rv = parser.ParseSelectorString(aValue, doc->GetDocumentURI(), // Bug 11240 0, // XXX get the line number! getter_Transfers(mSelectorList)); // We don't want to return an exception if parsing failed because // the spec does not define it as an exception case. if (NS_SUCCEEDED(rv)) { // Ensure that all the selectors are valid nsCSSSelectorList* selectors = mSelectorList; while (selectors) { if (!IsValidContentSelectors(selectors->mSelectors)) { // If we have an invalid selector, we can not match anything. mValidSelector = false; mSelectorList = nullptr; break; } selectors = selectors->mNext; } } ShadowRoot* containingShadow = GetContainingShadow(); if (containingShadow) { containingShadow->DistributeAllNodes(); } } return NS_OK; }
void HTMLMarqueeElement::didAddUserAgentShadowRoot(ShadowRoot& shadowRoot) { Element* style = HTMLStyleElement::create(document(), false); style->setTextContent( ":host { display: inline-block; overflow: hidden;" "text-align: initial; white-space: nowrap; }" ":host([direction=\"up\"]), :host([direction=\"down\"]) { overflow: " "initial; overflow-y: hidden; white-space: initial; }" ":host > div { will-change: transform; }"); shadowRoot.appendChild(style); Element* mover = HTMLDivElement::create(document()); shadowRoot.appendChild(mover); mover->appendChild(HTMLContentElement::create(document())); m_mover = mover; }
void InputType::destroyShadowSubtree() { ShadowRoot* root = element()->userAgentShadowRoot(); if (!root) return; root->removeAllChildren(); // It's ok to clear contents of all other ShadowRoots because they must have // been created by TextFieldDecorationElement, and we don't allow adding // AuthorShadowRoot to HTMLInputElement. while ((root = root->youngerShadowRoot())) { root->removeAllChildren(); root->appendChild(HTMLShadowElement::create(shadowTag, element()->document())); } }