static ContainerNode* nextTraversingShadowTree(const ContainerNode& node, const ContainerNode* rootNode) { if (ShadowRoot* shadowRoot = authorShadowRootOf(node)) return shadowRoot; const ContainerNode* current = &node; while (current) { if (Element* next = ElementTraversal::next(*current, rootNode)) return next; if (!current->isInShadowTree()) return 0; ShadowRoot* shadowRoot = current->containingShadowRoot(); if (shadowRoot == rootNode) return 0; if (ShadowRoot* youngerShadowRoot = shadowRoot->youngerShadowRoot()) { // Should not obtain any elements in user-agent shadow root. ASSERT(youngerShadowRoot->type() == ShadowRoot::AuthorShadowRoot); return youngerShadowRoot; } current = shadowRoot->host(); } return 0; }
void SlotAssignment::assignSlots(ShadowRoot& shadowRoot) { ASSERT(!m_slotAssignmentsIsValid); m_slotAssignmentsIsValid = true; auto* host = shadowRoot.host(); RELEASE_ASSERT(host); for (auto& entry : m_slots) entry.value->assignedNodes.shrink(0); for (Node* child = host->firstChild(); child; child = child->nextSibling()) { if (is<Element>(child)) { auto& slotName = downcast<Element>(*child).fastGetAttribute(slotAttr); if (!slotName.isNull()) { auto addResult = m_slots.add(treatNullAsEmpty(slotName), std::make_unique<SlotInfo>()); addResult.iterator->value->assignedNodes.append(child); continue; } } auto defaultSlotEntry = m_slots.find(emptyAtom); if (defaultSlotEntry != m_slots.end()) defaultSlotEntry->value->assignedNodes.append(child); } for (auto& entry : m_slots) entry.value->assignedNodes.shrinkToFit(); }
void SlotAssignment::addSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement, ShadowRoot& shadowRoot) { #ifndef NDEBUG ASSERT(!m_slotElementsForConsistencyCheck.contains(&slotElement)); m_slotElementsForConsistencyCheck.add(&slotElement); #endif // FIXME: We should be able to do a targeted reconstruction. shadowRoot.host()->setNeedsStyleRecalc(ReconstructRenderTree); const AtomicString& key = treatNullAsEmpty(name); auto addResult = m_slots.add(key, std::unique_ptr<SlotInfo>()); if (addResult.isNewEntry) { addResult.iterator->value = std::make_unique<SlotInfo>(slotElement); if (key == emptyAtom) // Because assignSlots doesn't collect nodes assgined to the default slot as an optimzation. m_slotAssignmentsIsValid = false; return; } auto& slotInfo = *addResult.iterator->value; if (!slotInfo.hasSlotElements()) slotInfo.element = &slotElement; else { slotInfo.element = nullptr; #ifndef NDEBUG m_needsToResolveSlotElements = true; #endif } slotInfo.elementCount++; }
static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target) { Node* targetNode = target.toNode(); #if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) // Video-only full screen is a mode where we use the shadow DOM as an implementation // detail that should not be detectable by the web content. if (targetNode) { if (Element* element = targetNode->document().webkitCurrentFullScreenElement()) { // FIXME: We assume that if the full screen element is a media element that it's // the video-only full screen. Both here and elsewhere. But that is probably wrong. if (element->isMediaElement() && shadowRoot.host() == element) return false; } } #endif // WebKit never allowed selectstart event to cross the the shadow DOM boundary. // Changing this breaks existing sites. // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. const AtomicString& eventType = event.type(); bool targetIsInShadowRoot = targetNode && &targetNode->treeScope().rootNode() == &shadowRoot; return !targetIsInShadowRoot || !(eventType == eventNames().abortEvent || eventType == eventNames().changeEvent || eventType == eventNames().errorEvent || eventType == eventNames().loadEvent || eventType == eventNames().resetEvent || eventType == eventNames().resizeEvent || eventType == eventNames().scrollEvent || eventType == eventNames().selectEvent || eventType == eventNames().selectstartEvent); }
bool InspectorOverlay::handleMouseMove(const PlatformMouseEvent& event) { if (!shouldSearchForNode()) return false; LocalFrame* frame = m_webViewImpl->mainFrameImpl()->frame(); if (!frame->view() || !frame->contentLayoutObject()) return false; Node* node = hoveredNodeForEvent(frame, event, event.shiftKey()); // Do not highlight within user agent shadow root unless requested. if (m_inspectMode != InspectorDOMAgent::SearchingForUAShadow) { ShadowRoot* shadowRoot = InspectorDOMAgent::userAgentShadowRoot(node); if (shadowRoot) node = shadowRoot->host(); } // Shadow roots don't have boxes - use host element instead. if (node && node->isShadowRoot()) node = node->parentOrShadowHostNode(); if (!node) return true; Node* eventTarget = event.shiftKey() ? hoveredNodeForEvent(m_webViewImpl->mainFrameImpl()->frame(), event, false) : nullptr; if (eventTarget == node) eventTarget = nullptr; if (node && m_inspectModeHighlightConfig) { m_hoveredNodeForInspectMode = node; m_domAgent->nodeHighlightedInOverlay(node); highlightNode(node, eventTarget, *m_inspectModeHighlightConfig, event.ctrlKey() || event.metaKey()); } return true; }
void SlotAssignment::removeSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement, ShadowRoot& shadowRoot) { #ifndef NDEBUG ASSERT(m_slotElementsForConsistencyCheck.contains(&slotElement)); m_slotElementsForConsistencyCheck.remove(&slotElement); #endif if (auto* host = shadowRoot.host()) // FIXME: We should be able to do a targeted reconstruction. host->setNeedsStyleRecalc(ReconstructRenderTree); auto it = m_slots.find(treatNullAsEmpty(name)); RELEASE_ASSERT(it != m_slots.end()); auto& slotInfo = *it->value; RELEASE_ASSERT(slotInfo.hasSlotElements()); slotInfo.elementCount--; if (slotInfo.element == &slotElement) { slotInfo.element = nullptr; #ifndef NDEBUG m_needsToResolveSlotElements = true; #endif } ASSERT(slotInfo.element || m_needsToResolveSlotElements); }
Element* FocusNavigationScope::owner() const { Node* root = rootNode(); if (root->isShadowRoot()) { ShadowRoot* shadowRoot = toShadowRoot(root); return shadowRoot->host(); } return 0; }
static inline bool shouldStopAtShadowRoot(Event& event, ShadowRoot& shadowRoot, EventTarget& target) { // WebKit never allowed selectstart event to cross the the shadow DOM boundary. // Changing this breaks existing sites. // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. const AtomicString eventType = event.type(); return target.toNode() && target.toNode()->shadowHost() == shadowRoot.host() && event.scoped(); }
void SlotAssignment::resolveAssignment(const ShadowRoot& shadowRoot) { m_assignment.clear(); using Name2Slot = WillBeHeapHashMap<AtomicString, RefPtrWillBeMember<HTMLSlotElement>>; Name2Slot name2slot; HTMLSlotElement* defaultSlot = nullptr; WillBeHeapVector<RefPtrWillBeMember<HTMLSlotElement>> slots; // TODO(hayato): Cache slots elements so that we do not have to travese the shadow tree. See ShadowRoot::descendantInsertionPoints() for (HTMLSlotElement& slot : Traversal<HTMLSlotElement>::descendantsOf(shadowRoot)) { slot.clearDistribution(); slots.append(&slot); AtomicString name = slot.fastGetAttribute(HTMLNames::nameAttr); if (name.isNull() || name.isEmpty()) { if (!defaultSlot) defaultSlot = &slot; } else { name2slot.add(name, &slot); } } for (Node& child : NodeTraversal::childrenOf(*shadowRoot.host())) { if (child.isElementNode()) { if (isActiveInsertionPoint(child)) { // TODO(hayato): Support re-distribution across v0 and v1 shadow trees detachNotAssignedNode(child); continue; } AtomicString slotName = toElement(child).fastGetAttribute(HTMLNames::slotAttr); if (slotName.isNull() || slotName.isEmpty()) { if (defaultSlot) assign(child, *defaultSlot); else detachNotAssignedNode(child); } else { HTMLSlotElement* slot = name2slot.get(slotName); if (slot) assign(child, *slot); else detachNotAssignedNode(child); } } else if (defaultSlot) { assign(child, *defaultSlot); } else { detachNotAssignedNode(child); } } // Update each slot's distribution in reverse tree order so that a child slot is visited before its parent slot. for (auto slot = slots.rbegin(); slot != slots.rend(); ++slot) (*slot)->updateDistributedNodesWithFallback(); }
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(); }
ContainerNode* FlatTreeTraversal::traverseParentOrHost(const Node& node) { ContainerNode* parent = node.parentNode(); if (!parent) return nullptr; if (!parent->isShadowRoot()) return parent; ShadowRoot* shadowRoot = toShadowRoot(parent); DCHECK(!shadowRoot->shadowInsertionPointOfYoungerShadowRoot()); if (!shadowRoot->isYoungest()) return nullptr; return &shadowRoot->host(); }
void InsertionPoint::removedFrom(ContainerNode* insertionPoint) { if (insertionPoint->inDocument()) { ShadowRoot* root = containingShadowRoot(); if (!root) root = insertionPoint->containingShadowRoot(); // host can be null when removedFrom() is called from ElementShadow destructor. if (root && root->host()) root->owner()->invalidateDistribution(); // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. clearDistribution(); } HTMLElement::removedFrom(insertionPoint); }
void SlotAssignment::didChangeSlot(const AtomicString& slotAttrValue, ShadowRoot& shadowRoot) { auto& slotName = slotNameFromAttributeValue(slotAttrValue); auto it = m_slots.find(slotName); if (it == m_slots.end()) return; HTMLSlotElement* slotElement = findFirstSlotElement(*it->value, shadowRoot); if (!slotElement) return; shadowRoot.host()->invalidateStyleAndRenderersForSubtree(); m_slotAssignmentsIsValid = false; if (shadowRoot.mode() == ShadowRootMode::UserAgent) return; slotElement->enqueueSlotChangeEvent(); }
void ScopedStyleResolver::addHostRule(StyleRuleHost* hostRule, bool hasDocumentSecurityOrigin, const ContainerNode* scopingNode) { if (!scopingNode) return; ShadowRoot* shadowRoot = scopingNode->containingShadowRoot(); if (!shadowRoot || !shadowRoot->host()) return; RuleSet* rule = ensureAtHostRuleSetFor(shadowRoot); const Vector<RefPtr<StyleRuleBase> >& childRules = hostRule->childRules(); AddRuleFlags addRuleFlags = static_cast<AddRuleFlags>(hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : RuleHasNoSpecialState); for (unsigned i = 0; i < childRules.size(); ++i) { StyleRuleBase* hostStylingRule = childRules[i].get(); if (hostStylingRule->isStyleRule()) rule->addStyleRule(static_cast<StyleRule*>(hostStylingRule), addRuleFlags); } }
void SlotAssignment::assignSlots(ShadowRoot& shadowRoot) { ASSERT(!m_slotAssignmentsIsValid); m_slotAssignmentsIsValid = true; for (auto& entry : m_slots) entry.value->assignedNodes.shrink(0); auto& host = *shadowRoot.host(); for (auto* child = host.firstChild(); child; child = child->nextSibling()) { if (!is<Text>(*child) && !is<Element>(*child)) continue; auto slotName = slotNameForHostChild(*child); assignToSlot(*child, slotName); } for (auto& entry : m_slots) entry.value->assignedNodes.shrinkToFit(); }
static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target) { Node* targetNode = target.toNode(); #if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) // Video-only full screen is a mode where we use the shadow DOM as an implementation // detail that should not be detectable by the web content. if (targetNode) { if (Element* element = targetNode->document().webkitCurrentFullScreenElement()) { // FIXME: We assume that if the full screen element is a media element that it's // the video-only full screen. Both here and elsewhere. But that is probably wrong. if (element->isMediaElement() && shadowRoot.host() == element) return false; } } #endif bool targetIsInShadowRoot = targetNode && &targetNode->treeScope().rootNode() == &shadowRoot; return !targetIsInShadowRoot || !event.scoped(); }
Node::InsertionNotificationRequest HTMLShadowElement::insertedInto(ContainerNode* insertionPoint) { if (insertionPoint->inDocument()) { // Warn if trying to reproject between user agent and author shadows. ShadowRoot* root = containingShadowRoot(); if (root && root->olderShadowRoot() && root->type() != root->olderShadowRoot()->type()) { String message = String::format("<shadow> doesn't work for %s element host.", root->host()->tagName().utf8().data()); document().addConsoleMessage(RenderingMessageSource, WarningMessageLevel, message); } } return InsertionPoint::insertedInto(insertionPoint); }
void SlotAssignment::invalidate(ShadowRoot& shadowRoot) { // FIXME: We should be able to do a targeted reconstruction. shadowRoot.host()->setNeedsStyleRecalc(ReconstructRenderTree); m_slotAssignmentsIsValid = false; }