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++;
}
Exemple #4
0
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;
}
Exemple #8
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();
}
Exemple #12
0
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);
}
Exemple #13
0
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);
    }
}
Exemple #15
0
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();
}
Exemple #16
0
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();
}
Exemple #17
0
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;
}