Changeset 198115 in webkit


Ignore:
Timestamp:
Mar 14, 2016 5:02:21 AM (8 years ago)
Author:
rniwa@webkit.org
Message:

Add slotchange event
https://bugs.webkit.org/show_bug.cgi?id=155424
<rdar://problem/24997534>

Reviewed by Antti Koivisto.

Source/WebCore:

Added slotchange event as discussed on https://github.com/w3c/webcomponents/issues/288.

While the exact semantics of it could still evolve over time, this patch implements as
an asynchronous event that fires on a slot element whenever its distributed nodes change
(flattened assigned nodes):
http://w3c.github.io/webcomponents/spec/shadow/#dfn-distributed-nodes

Since inserting or removing an element from a shadow host could needs to enqueue this event
on the right slot element, this patch moves the invalidation point of element removals and
insertions from Element::childrenChanged to Element::insertedInto and Element::removedFrom.
Text nodes are still invalidated at Element::childrenChanged for performance reasons
since it could only appear within a default slot element.

Because this more fine-grained invalidation needs to be overridden by HTMLDetailsElement,
we now subclass SlotAssignment in HTMLDetailsElement instead of passing in a std::function.

Test: fast/shadow-dom/slotchange-event.html

  • dom/Document.cpp:

(WebCore::Document::enqueueSlotchangeEvent): Added.

  • dom/Document.h:
  • dom/Element.cpp:

(WebCore::Element::attributeChanged): Call hostChildElementDidChangeSlotAttr.
(WebCore::Element::insertedInto): Call hostChildElementDidChange.
(WebCore::Element::removedFrom): Ditto.
(WebCore::Element::childrenChanged): Don't invalidate the slots on ElementInserted and
ElementRemoved since they're now done in Element::insertedInto and Element::removedFrom.

  • dom/Event.cpp:

(WebCore::Event::scoped): slotchange event is scoped.

  • dom/EventNames.h: Added eventNames().slotchange.
  • dom/ShadowRoot.cpp:

(WebCore::ShadowRoot::invalidateSlotAssignments): Deleted.
(WebCore::ShadowRoot::invalidateDefaultSlotAssignments): Deleted.

  • dom/ShadowRoot.h:

(ShadowRoot): Added more fine-grained invalidators, mirroring changes to SlotAssignment.

  • dom/SlotAssignment.cpp:

(WebCore::SlotAssignment::SlotAssignment): Removed a variant that takes SlotNameFunction
since HTMLDetailsElement now subclasses SlotAssignment.
(WebCore::SlotAssignment::~SlotAssignment): Added now that the class is virtual.
(WebCore::recursivelyFireSlotChangeEvent): Added.
(WebCore::SlotAssignment::didChangeSlot): Added. Invalidates the style tree only if there
is a corresponding slot element, and fires slotchange event. When the slot element we found
in this shadow tree is assigned to a slot element inside an inner shadow tree, recursively
fire slotchange event on each such inner slots.
(WebCore::SlotAssignment::hostChildElementDidChange): Added. Update the matching slot when
an element is inserted or removed under a shadow host.
(WebCore::SlotAssignment::assignedNodesForSlot): Removed the superfluous early exit to an
release assert since addSlotElementByName should always create a SlotInfo for each element.
(WebCore::SlotAssignment::slotNameForHostChild): Added. This is the equivalent of old
m_slotNameFunction which DetailsSlotAssignment overrides.
(WebCore::SlotAssignment::invalidateDefaultSlot): Deleted.
(WebCore::SlotAssignment::findFirstSlotElement): Added an assertion. slotInfo.element must
be nullptr if elementCount is 0, and elementCount must be 0 if slotInfo.element is nullptr
after calling resolveAllSlotElements, which traverses the entire shadow tree to find all
slot elements.
(WebCore::SlotAssignment::assignSlots):

  • dom/SlotAssignment.h: Implemented inline functions of ShadowRoot here to avoid including

SlotAssignment.h in ShadowRoot.h. Not inlining them results in extra function calls for all
builtin elements with shadow root without slot elements, which impacts performance.
(WebCore::ShadowRoot::didRemoveAllChildrenOfShadowHost): Added.
(WebCore::ShadowRoot::didChangeDefaultSlot): Added.
(WebCore::ShadowRoot::hostChildElementDidChange): Added.
(WebCore::ShadowRoot::hostChildElementDidChangeSlotAttribute): Added.
(WebCore::ShadowRoot::innerSlotDidChange):

  • html/HTMLDetailsElement.cpp:

(WebCore::DetailsSlotAssignment): Added. Subclasses SlotAssignment to override
hostChildElementDidChange and slotNameForHostChild.
(WebCore::DetailsSlotAssignment::hostChildElementDidChange): Added. We don't check if this
is the first summary element since we don't know the answer when this function is called
inside Element::removedFrom.
(WebCore::DetailsSlotAssignment::slotNameForHostChild): Renamed from slotNameFunction. Also
removed the code to return nullAtom when details element is not open as that messes up new
fine-grained invalidation. Insert/remove the slot element in parseAttribute instead.
(WebCore::HTMLDetailsElement::didAddUserAgentShadowRoot): Don't insert the slot element for
the summary since the details element is not open now.
(WebCore::HTMLDetailsElement::parseAttribute): Remove and insert the slot element for the
summary here instead of changing the behavior of slotNameForHostChild.

  • html/HTMLDetailsElement.h:
  • html/HTMLSlotElement.cpp:

(WebCore::HTMLSlotElement::enqueueSlotChangeEvent): Added. Enqueues a new slotchange event
if we haven't done so for this element yet.
(WebCore::HTMLSlotElement::dispatchEvent): Added. Clear m_hasEnqueuedSlotChangeEvent when
dispatching a slotchange event so that a subsequent call to enqueueSlotChangeEvent would
enqueue a new event. Note scripts call EventTarget::dispatchEventForBindings instead.

  • html/HTMLSlotElement.h:

LayoutTests:

Added a W3C style testharness.js test.

  • fast/shadow-dom/ShadowRoot-interface-expected.txt:
  • fast/shadow-dom/ShadowRoot-interface.html: Don't import testharness.css from svn.webkit.org.
  • fast/shadow-dom/slotchange-event-expected.txt: Added.
  • fast/shadow-dom/slotchange-event.html: Added.
Location:
trunk
Files:
2 added
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r198096 r198115  
     12016-03-14  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Add slotchange event
     4        https://bugs.webkit.org/show_bug.cgi?id=155424
     5        <rdar://problem/24997534>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        Added a W3C style testharness.js test.
     10
     11        * fast/shadow-dom/ShadowRoot-interface-expected.txt:
     12        * fast/shadow-dom/ShadowRoot-interface.html: Don't import testharness.css from svn.webkit.org.
     13        * fast/shadow-dom/slotchange-event-expected.txt: Added.
     14        * fast/shadow-dom/slotchange-event.html: Added.
     15
    1162016-03-13  Darin Adler  <darin@apple.com>
    217
  • trunk/LayoutTests/fast/shadow-dom/ShadowRoot-interface-expected.txt

    r189841 r198115  
    1 Blocked access to external URL https://svn.webkit.org/repository/webkit/trunk/LayoutTests/resources/testharness.css
    21
    32PASS Check the existence of ShadowRoot interface
  • trunk/LayoutTests/fast/shadow-dom/ShadowRoot-interface.html

    r189841 r198115  
    88<script src="../../resources/testharness.js"></script>
    99<script src="../../resources/testharnessreport.js"></script>
    10 <link rel='stylesheet' href='https://svn.webkit.org/repository/webkit/trunk/LayoutTests/resources/testharness.css'>
     10<link rel='stylesheet' href='../../resources/testharness.css'>
    1111</head>
    1212<body>
  • trunk/Source/WebCore/ChangeLog

    r198102 r198115  
     12016-03-14  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Add slotchange event
     4        https://bugs.webkit.org/show_bug.cgi?id=155424
     5        <rdar://problem/24997534>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        Added `slotchange` event as discussed on https://github.com/w3c/webcomponents/issues/288.
     10
     11        While the exact semantics of it could still evolve over time, this patch implements as
     12        an asynchronous event that fires on a slot element whenever its distributed nodes change
     13        (flattened assigned nodes):
     14        http://w3c.github.io/webcomponents/spec/shadow/#dfn-distributed-nodes
     15
     16        Since inserting or removing an element from a shadow host could needs to enqueue this event
     17        on the right slot element, this patch moves the invalidation point of element removals and
     18        insertions from Element::childrenChanged to Element::insertedInto and Element::removedFrom.
     19        Text nodes are still invalidated at Element::childrenChanged for performance reasons
     20        since it could only appear within a default slot element.
     21
     22        Because this more fine-grained invalidation needs to be overridden by HTMLDetailsElement,
     23        we now subclass SlotAssignment in HTMLDetailsElement instead of passing in a std::function.
     24
     25        Test: fast/shadow-dom/slotchange-event.html
     26
     27        * dom/Document.cpp:
     28        (WebCore::Document::enqueueSlotchangeEvent): Added.
     29        * dom/Document.h:
     30        * dom/Element.cpp:
     31        (WebCore::Element::attributeChanged): Call hostChildElementDidChangeSlotAttr.
     32        (WebCore::Element::insertedInto): Call hostChildElementDidChange.
     33        (WebCore::Element::removedFrom): Ditto.
     34        (WebCore::Element::childrenChanged): Don't invalidate the slots on ElementInserted and
     35        ElementRemoved since they're now done in Element::insertedInto and Element::removedFrom.
     36        * dom/Event.cpp:
     37        (WebCore::Event::scoped): slotchange event is scoped.
     38        * dom/EventNames.h: Added eventNames().slotchange.
     39        * dom/ShadowRoot.cpp:
     40        (WebCore::ShadowRoot::invalidateSlotAssignments): Deleted.
     41        (WebCore::ShadowRoot::invalidateDefaultSlotAssignments): Deleted.
     42        * dom/ShadowRoot.h:
     43        (ShadowRoot): Added more fine-grained invalidators, mirroring changes to SlotAssignment.
     44        * dom/SlotAssignment.cpp:
     45        (WebCore::SlotAssignment::SlotAssignment): Removed a variant that takes SlotNameFunction
     46        since HTMLDetailsElement now subclasses SlotAssignment.
     47        (WebCore::SlotAssignment::~SlotAssignment): Added now that the class is virtual.
     48        (WebCore::recursivelyFireSlotChangeEvent): Added.
     49        (WebCore::SlotAssignment::didChangeSlot): Added. Invalidates the style tree only if there
     50        is a corresponding slot element, and fires slotchange event. When the slot element we found
     51        in this shadow tree is assigned to a slot element inside an inner shadow tree, recursively
     52        fire slotchange event on each such inner slots.
     53        (WebCore::SlotAssignment::hostChildElementDidChange): Added. Update the matching slot when
     54        an element is inserted or removed under a shadow host.
     55        (WebCore::SlotAssignment::assignedNodesForSlot): Removed the superfluous early exit to an
     56        release assert since addSlotElementByName should always create a SlotInfo for each element.
     57        (WebCore::SlotAssignment::slotNameForHostChild): Added. This is the equivalent of old
     58        m_slotNameFunction which DetailsSlotAssignment overrides.
     59        (WebCore::SlotAssignment::invalidateDefaultSlot): Deleted.
     60        (WebCore::SlotAssignment::findFirstSlotElement): Added an assertion. slotInfo.element must
     61        be nullptr if elementCount is 0, and elementCount must be 0 if slotInfo.element is nullptr
     62        after calling resolveAllSlotElements, which traverses the entire shadow tree to find all
     63        slot elements.
     64        (WebCore::SlotAssignment::assignSlots):
     65        * dom/SlotAssignment.h: Implemented inline functions of ShadowRoot here to avoid including
     66        SlotAssignment.h in ShadowRoot.h. Not inlining them results in extra function calls for all
     67        builtin elements with shadow root without slot elements, which impacts performance.
     68        (WebCore::ShadowRoot::didRemoveAllChildrenOfShadowHost): Added.
     69        (WebCore::ShadowRoot::didChangeDefaultSlot): Added.
     70        (WebCore::ShadowRoot::hostChildElementDidChange): Added.
     71        (WebCore::ShadowRoot::hostChildElementDidChangeSlotAttribute): Added.
     72        (WebCore::ShadowRoot::innerSlotDidChange):
     73        * html/HTMLDetailsElement.cpp:
     74        (WebCore::DetailsSlotAssignment): Added. Subclasses SlotAssignment to override
     75        hostChildElementDidChange and slotNameForHostChild.
     76        (WebCore::DetailsSlotAssignment::hostChildElementDidChange): Added. We don't check if this
     77        is the first summary element since we don't know the answer when this function is called
     78        inside Element::removedFrom.
     79        (WebCore::DetailsSlotAssignment::slotNameForHostChild): Renamed from slotNameFunction. Also
     80        removed the code to return nullAtom when details element is not open as that messes up new
     81        fine-grained invalidation. Insert/remove the slot element in parseAttribute instead.
     82        (WebCore::HTMLDetailsElement::didAddUserAgentShadowRoot): Don't insert the slot element for
     83        the summary since the details element is not open now.
     84        (WebCore::HTMLDetailsElement::parseAttribute): Remove and insert the slot element for the
     85        summary here instead of changing the behavior of slotNameForHostChild.
     86        * html/HTMLDetailsElement.h:
     87        * html/HTMLSlotElement.cpp:
     88        (WebCore::HTMLSlotElement::enqueueSlotChangeEvent): Added. Enqueues a new slotchange event
     89        if we haven't done so for this element yet.
     90        (WebCore::HTMLSlotElement::dispatchEvent): Added. Clear m_hasEnqueuedSlotChangeEvent when
     91        dispatching a slotchange event so that a subsequent call to enqueueSlotChangeEvent would
     92        enqueue a new event. Note scripts call EventTarget::dispatchEventForBindings instead.
     93        * html/HTMLSlotElement.h:
     94
    1952016-03-14  Youenn Fablet  <youenn.fablet@crf.canon.fr>
    296
  • trunk/Source/WebCore/dom/Document.cpp

    r197917 r198115  
    41734173}
    41744174
     4175void Document::enqueueSlotchangeEvent(Ref<Event>&& event)
     4176{
     4177    m_eventQueue.enqueueEvent(WTFMove(event));
     4178}
     4179
    41754180RefPtr<Event> Document::createEvent(const String& type, ExceptionCode& ec)
    41764181{
  • trunk/Source/WebCore/dom/Document.h

    r197887 r198115  
    10891089    void enqueueDocumentEvent(Ref<Event>&&);
    10901090    void enqueueOverflowEvent(Ref<Event>&&);
     1091    void enqueueSlotchangeEvent(Ref<Event>&&);
    10911092    void enqueuePageshowEvent(PageshowEventPersistence);
    10921093    void enqueueHashchangeEvent(const String& oldURL, const String& newURL);
  • trunk/Source/WebCore/dom/Element.cpp

    r197764 r198115  
    8484#include "Settings.h"
    8585#include "SimulatedClick.h"
     86#include "SlotAssignment.h"
    8687#include "StyleProperties.h"
    8788#include "StyleResolver.h"
     
    12551256            if (auto* parent = parentElement()) {
    12561257                if (auto* shadowRoot = parent->shadowRoot())
    1257                     shadowRoot->invalidateSlotAssignments();
     1258                    shadowRoot->hostChildElementDidChangeSlotAttribute(oldValue, newValue);
    12581259            }
    12591260        }
     
    14971498#endif
    14981499
     1500    if (parentNode() == &insertionPoint) {
     1501        if (auto* shadowRoot = parentNode()->shadowRoot())
     1502            shadowRoot->hostChildElementDidChange(*this);
     1503    }
     1504
    14991505    if (!insertionPoint.isInTreeScope())
    15001506        return InsertionDone;
     
    15741580                updateLabel(*oldScope, fastGetAttribute(forAttr), nullAtom);
    15751581        }
     1582    }
     1583
     1584    if (!parentNode()) {
     1585        if (auto* shadowRoot = insertionPoint.shadowRoot())
     1586            shadowRoot->hostChildElementDidChange(*this);
    15761587    }
    15771588
     
    18331844        case ElementInserted:
    18341845        case ElementRemoved:
     1846            // For elements, we notify shadowRoot in Element::insertedInto and Element::removedFrom.
     1847            break;
    18351848        case AllChildrenRemoved:
    1836             shadowRoot->invalidateSlotAssignments();
     1849            shadowRoot->didRemoveAllChildrenOfShadowHost();
    18371850            break;
    18381851        case TextInserted:
    18391852        case TextRemoved:
    18401853        case TextChanged:
    1841             shadowRoot->invalidateDefaultSlotAssignments();
     1854            shadowRoot->didChangeDefaultSlot();
    18421855            break;
    18431856        case NonContentsChildChanged:
  • trunk/Source/WebCore/dom/Event.cpp

    r198056 r198115  
    104104        || m_type == eventNames().scrollEvent
    105105        || m_type == eventNames().selectEvent
    106         || m_type == eventNames().selectstartEvent;
     106        || m_type == eventNames().selectstartEvent
     107        || m_type == eventNames().slotchangeEvent;
    107108}
    108109
  • trunk/Source/WebCore/dom/EventNames.h

    r197790 r198115  
    196196    macro(show) \
    197197    macro(signalingstatechange) \
     198    macro(slotchange) \
    198199    macro(soundend) \
    199200    macro(soundstart) \
  • trunk/Source/WebCore/dom/ShadowRoot.cpp

    r198090 r198115  
    202202}
    203203
    204 void ShadowRoot::invalidateSlotAssignments()
    205 {
    206     if (m_slotAssignment)
    207         m_slotAssignment->invalidate(*this);
    208 }
    209 
    210 void ShadowRoot::invalidateDefaultSlotAssignments()
    211 {
    212     if (m_slotAssignment)
    213         m_slotAssignment->invalidateDefaultSlot(*this);
    214 }
    215 
    216204const Vector<Node*>* ShadowRoot::assignedNodesForSlot(const HTMLSlotElement& slot)
    217205{
  • trunk/Source/WebCore/dom/ShadowRoot.h

    r197887 r198115  
    9292    void removeSlotElementByName(const AtomicString&, HTMLSlotElement&);
    9393
    94     void invalidateSlotAssignments();
    95     void invalidateDefaultSlotAssignments();
     94    void didRemoveAllChildrenOfShadowHost();
     95    void didChangeDefaultSlot();
     96    void hostChildElementDidChange(const Element&);
     97    void hostChildElementDidChangeSlotAttribute(const AtomicString& oldValue, const AtomicString& newValue);
     98    void innerSlotDidChange(const AtomicString&);
    9699
    97100    const Vector<Node*>* assignedNodesForSlot(const HTMLSlotElement&);
  • trunk/Source/WebCore/dom/SlotAssignment.cpp

    r198090 r198115  
    5151
    5252SlotAssignment::SlotAssignment()
    53     : m_slotNameFunction(slotNameFromSlotAttribute)
    54 {
    55 }
    56 
    57 SlotAssignment::SlotAssignment(SlotNameFunction function)
    58     : m_slotNameFunction(WTFMove(function))
     53{
     54}
     55
     56SlotAssignment::~SlotAssignment()
    5957{
    6058}
     
    6563        return nullptr;
    6664
    67     auto slotName = m_slotNameFunction(node);
    68     if (!slotName)
    69         return nullptr;
    70 
     65    auto slotName = slotNameForHostChild(node);
    7166    auto it = m_slots.find(slotName);
    7267    if (it == m_slots.end())
     
    134129}
    135130
     131static void recursivelyFireSlotChangeEvent(HTMLSlotElement& slotElement)
     132{
     133    slotElement.enqueueSlotChangeEvent();
     134
     135    auto* slotParent = slotElement.parentElement();
     136    if (!slotParent)
     137        return;
     138
     139    auto* shadowRootOfSlotParent = slotParent->shadowRoot();
     140    if (!shadowRootOfSlotParent)
     141        return;
     142
     143    shadowRootOfSlotParent->innerSlotDidChange(slotElement.fastGetAttribute(slotAttr));
     144}
     145
     146void SlotAssignment::didChangeSlot(const AtomicString& slotAttrValue, ChangeType changeType, ShadowRoot& shadowRoot)
     147{
     148    auto& slotName = slotNameFromAttributeValue(slotAttrValue);
     149    auto it = m_slots.find(slotName);
     150    if (it == m_slots.end())
     151        return;
     152
     153    HTMLSlotElement* slotElement = findFirstSlotElement(*it->value, shadowRoot);
     154    if (!slotElement)
     155        return;
     156
     157    if (changeType == ChangeType::DirectChild) {
     158        shadowRoot.host()->setNeedsStyleRecalc(ReconstructRenderTree);
     159        m_slotAssignmentsIsValid = false;
     160    }
     161
     162    if (shadowRoot.type() == ShadowRoot::Type::UserAgent)
     163        return;
     164
     165    recursivelyFireSlotChangeEvent(*slotElement);
     166}
     167
     168void SlotAssignment::hostChildElementDidChange(const Element& childElement, ShadowRoot& shadowRoot)
     169{
     170    didChangeSlot(childElement.fastGetAttribute(slotAttr), ChangeType::DirectChild, shadowRoot);
     171}
     172
    136173const Vector<Node*>* SlotAssignment::assignedNodesForSlot(const HTMLSlotElement& slotElement, ShadowRoot& shadowRoot)
    137174{
     175    ASSERT(slotElement.containingShadowRoot() == &shadowRoot);
     176    const AtomicString& slotName = slotNameFromAttributeValue(slotElement.fastGetAttribute(nameAttr));
     177    auto it = m_slots.find(slotName);
     178    RELEASE_ASSERT(it != m_slots.end());
     179
     180    auto& slotInfo = *it->value;
    138181    if (!m_slotAssignmentsIsValid)
    139182        assignSlots(shadowRoot);
    140183
    141     const AtomicString& slotName = slotNameFromAttributeValue(slotElement.fastGetAttribute(nameAttr));
    142     auto it = m_slots.find(slotName);
    143     if (it == m_slots.end())
    144         return nullptr;
    145 
    146     auto& slotInfo = *it->value;
    147 
    148184    if (!slotInfo.assignedNodes.size())
    149185        return nullptr;
     
    156192}
    157193
    158 void SlotAssignment::invalidate(ShadowRoot& shadowRoot)
    159 {
    160     // FIXME: We should be able to do a targeted reconstruction.
    161     shadowRoot.host()->setNeedsStyleRecalc(ReconstructRenderTree);
    162     m_slotAssignmentsIsValid = false;
    163 }
    164 
    165 void SlotAssignment::invalidateDefaultSlot(ShadowRoot& shadowRoot)
    166 {
    167     auto it = m_slots.find(defaultSlotName());
    168     if (it != m_slots.end() && it->value->elementCount)
    169         invalidate(shadowRoot); // FIXME: We should be able to reconstruct only under the default slot.
     194const AtomicString& SlotAssignment::slotNameForHostChild(const Node& child) const
     195{
     196    return slotNameFromSlotAttribute(child);
    170197}
    171198
     
    177204#ifndef NDEBUG
    178205    ASSERT(!slotInfo.element || m_slotElementsForConsistencyCheck.contains(slotInfo.element));
     206    ASSERT(!!slotInfo.element == !!slotInfo.elementCount);
    179207#endif
    180208
     
    224252        if (!is<Text>(*child) && !is<Element>(*child))
    225253            continue;
    226         auto slotName = m_slotNameFunction(*child);
    227         if (!slotName)
    228             continue;
     254        auto slotName = slotNameForHostChild(*child);
    229255        assignToSlot(*child, slotName);
    230256    }
  • trunk/Source/WebCore/dom/SlotAssignment.h

    r190840 r198115  
    2929#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
    3030
     31#include "ShadowRoot.h"
    3132#include <wtf/HashMap.h>
    3233#include <wtf/HashSet.h>
     
    3738namespace WebCore {
    3839
     40class Element;
    3941class HTMLSlotElement;
    4042class Node;
    41 class ShadowRoot;
    4243
    4344class SlotAssignment {
    4445    WTF_MAKE_NONCOPYABLE(SlotAssignment);
    4546public:
    46     using SlotNameFunction = std::function<AtomicString (const Node& child)>;
    47 
    4847    SlotAssignment();
    49     SlotAssignment(SlotNameFunction);
    50     ~SlotAssignment() { }
     48    virtual ~SlotAssignment();
    5149
    5250    static const AtomicString& defaultSlotName() { return emptyAtom; }
     
    5755    void removeSlotElementByName(const AtomicString&, HTMLSlotElement&, ShadowRoot&);
    5856
     57    enum class ChangeType { DirectChild, InnerSlot };
     58    void didChangeSlot(const AtomicString&, ChangeType, ShadowRoot&);
     59    void enqueueSlotChangeEvent(const AtomicString&, ShadowRoot&);
     60
    5961    const Vector<Node*>* assignedNodesForSlot(const HTMLSlotElement&, ShadowRoot&);
    6062
    61     void invalidate(ShadowRoot&);
    62     void invalidateDefaultSlot(ShadowRoot&);
     63    virtual void hostChildElementDidChange(const Element&, ShadowRoot&);
    6364
    6465private:
     
    7879        Vector<Node*> assignedNodes;
    7980    };
     81   
     82    virtual const AtomicString& slotNameForHostChild(const Node&) const;
    8083
    8184    HTMLSlotElement* findFirstSlotElement(SlotInfo&, ShadowRoot&);
     
    8487    void assignSlots(ShadowRoot&);
    8588    void assignToSlot(Node& child, const AtomicString& slotName);
    86 
    87     SlotNameFunction m_slotNameFunction;
    8889
    8990    HashMap<AtomicString, std::unique_ptr<SlotInfo>> m_slots;
     
    9798};
    9899
     100inline void ShadowRoot::didRemoveAllChildrenOfShadowHost()
     101{
     102    if (m_slotAssignment) // FIXME: This is incorrect when there were no elements or text nodes removed.
     103        m_slotAssignment->didChangeSlot(nullAtom, SlotAssignment::ChangeType::DirectChild, *this);
     104}
     105
     106inline void ShadowRoot::didChangeDefaultSlot()
     107{
     108    if (m_slotAssignment)
     109        m_slotAssignment->didChangeSlot(nullAtom, SlotAssignment::ChangeType::DirectChild, *this);
     110}
     111
     112inline void ShadowRoot::hostChildElementDidChange(const Element& childElement)
     113{
     114    if (m_slotAssignment)
     115        m_slotAssignment->hostChildElementDidChange(childElement, *this);
     116}
     117
     118inline void ShadowRoot::hostChildElementDidChangeSlotAttribute(const AtomicString& oldValue, const AtomicString& newValue)
     119{
     120    if (m_slotAssignment) {
     121        m_slotAssignment->didChangeSlot(oldValue, SlotAssignment::ChangeType::DirectChild, *this);
     122        m_slotAssignment->didChangeSlot(newValue, SlotAssignment::ChangeType::DirectChild, *this);
     123    }
     124}
     125
     126inline void ShadowRoot::innerSlotDidChange(const AtomicString& name)
     127{
     128    if (m_slotAssignment)
     129        m_slotAssignment->didChangeSlot(name, SlotAssignment::ChangeType::InnerSlot, *this);
     130}
     131
    99132}
    100133
  • trunk/Source/WebCore/html/HTMLDetailsElement.cpp

    r198090 r198115  
    4545}
    4646
    47 static AtomicString slotNameFunction(const Node& child)
     47class DetailsSlotAssignment final : public SlotAssignment {
     48private:
     49    void hostChildElementDidChange(const Element&, ShadowRoot&) override;
     50    const AtomicString& slotNameForHostChild(const Node&) const override;
     51};
     52
     53void DetailsSlotAssignment::hostChildElementDidChange(const Element& childElement, ShadowRoot& shadowRoot)
     54{
     55    if (is<HTMLSummaryElement>(childElement)) {
     56        // Don't check whether this is the first summary element
     57        // since we don't know the answer when this function is called inside Element::removedFrom.
     58        didChangeSlot(summarySlotName(), ChangeType::DirectChild, shadowRoot);
     59    } else
     60        didChangeSlot(SlotAssignment::defaultSlotName(), ChangeType::DirectChild, shadowRoot);
     61}
     62
     63const AtomicString& DetailsSlotAssignment::slotNameForHostChild(const Node& child) const
    4864{
    4965    auto& parent = *child.parentNode();
     
    5672            return summarySlotName();
    5773    }
    58     // Everything else is assigned to the default slot if details is open.
    59     if (details.isOpen())
    60         return SlotAssignment::defaultSlotName();
    61 
    62     // Otherwise don't render the content.
    63     return nullAtom;
    64 };
     74    return SlotAssignment::defaultSlotName();
     75}
    6576
    6677Ref<HTMLDetailsElement> HTMLDetailsElement::create(const QualifiedName& tagName, Document& document)
    6778{
    6879    auto details = adoptRef(*new HTMLDetailsElement(tagName, document));
    69     details->addShadowRoot(ShadowRoot::create(document, std::make_unique<SlotAssignment>(slotNameFunction)));
     80    details->addShadowRoot(ShadowRoot::create(document, std::make_unique<DetailsSlotAssignment>()));
    7081    return details;
    7182}
     
    95106    root->appendChild(WTFMove(summarySlot));
    96107
    97     auto defaultSlot = HTMLSlotElement::create(slotTag, document());
    98     root->appendChild(WTFMove(defaultSlot));
     108    m_defaultSlot = HTMLSlotElement::create(slotTag, document());
     109    ASSERT(!m_isOpen);
    99110}
    100111
     
    118129        bool oldValue = m_isOpen;
    119130        m_isOpen = !value.isNull();
    120         if (oldValue != m_isOpen)
    121             shadowRoot()->invalidateSlotAssignments();
     131        if (oldValue != m_isOpen) {
     132            auto* root = shadowRoot();
     133            ASSERT(root);
     134            if (m_isOpen)
     135                root->appendChild(*m_defaultSlot);
     136            else
     137                root->removeChild(*m_defaultSlot);
     138        }
    122139    } else
    123140        HTMLElement::parseAttribute(name, value);
  • trunk/Source/WebCore/html/HTMLDetailsElement.h

    r197566 r198115  
    4848    HTMLSlotElement* m_summarySlot { nullptr };
    4949    HTMLSummaryElement* m_defaultSummary { nullptr };
     50    RefPtr<HTMLSlotElement> m_defaultSlot;
    5051};
    5152
  • trunk/Source/WebCore/html/HTMLSlotElement.cpp

    r190845 r198115  
    3030
    3131#include "ElementChildIterator.h"
     32#include "Event.h"
     33#include "EventNames.h"
    3234#include "HTMLNames.h"
    3335#include "ShadowRoot.h"
     
    98100}
    99101
     102void HTMLSlotElement::enqueueSlotChangeEvent()
     103{
     104    if (m_hasEnqueuedSlotChangeEvent)
     105        return;
     106
     107    bool bubbles = false;
     108    bool cancelable = false;
     109    auto event = Event::create(eventNames().slotchangeEvent, bubbles, cancelable);
     110    event->setTarget(this);
     111    document().enqueueSlotchangeEvent(WTFMove(event));
     112
     113    m_hasEnqueuedSlotChangeEvent = true;
     114}
     115
     116bool HTMLSlotElement::dispatchEvent(Event& event)
     117{
     118    if (event.type() == eventNames().slotchangeEvent)
     119        m_hasEnqueuedSlotChangeEvent = false;
     120    return HTMLElement::dispatchEvent(event);
     121}
     122
    100123}
    101124
  • trunk/Source/WebCore/html/HTMLSlotElement.h

    r197563 r198115  
    4040    const Vector<Node*>* assignedNodes() const;
    4141
     42    void enqueueSlotChangeEvent();
     43
    4244private:
    4345    HTMLSlotElement(const QualifiedName&, Document&);
     
    4648    void removedFrom(ContainerNode&) override;
    4749    void attributeChanged(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason) override;
     50
     51    bool dispatchEvent(Event&) override;
     52
     53    bool m_hasEnqueuedSlotChangeEvent { false };
    4854};
    4955
Note: See TracChangeset for help on using the changeset viewer.