diff options
Diffstat (limited to 'Source/WebCore/html/HTMLDetailsElement.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLDetailsElement.cpp | 184 |
1 files changed, 95 insertions, 89 deletions
diff --git a/Source/WebCore/html/HTMLDetailsElement.cpp b/Source/WebCore/html/HTMLDetailsElement.cpp index 0efa66694..7775ce3ed 100644 --- a/Source/WebCore/html/HTMLDetailsElement.cpp +++ b/Source/WebCore/html/HTMLDetailsElement.cpp @@ -21,114 +21,124 @@ #include "config.h" #include "HTMLDetailsElement.h" -#if ENABLE(DETAILS_ELEMENT) +#include "AXObjectCache.h" +#include "ElementIterator.h" +#include "EventNames.h" +#include "EventSender.h" +#include "HTMLSlotElement.h" #include "HTMLSummaryElement.h" -#include "InsertionPoint.h" #include "LocalizedStrings.h" #include "MouseEvent.h" #include "RenderBlockFlow.h" +#include "ShadowRoot.h" +#include "SlotAssignment.h" #include "Text.h" +#include <wtf/NeverDestroyed.h> namespace WebCore { using namespace HTMLNames; -static const AtomicString& summaryQuerySelector() +static DetailEventSender& detailToggleEventSender() { - DEFINE_STATIC_LOCAL(AtomicString, selector, ("summary:first-of-type", AtomicString::ConstructFromLiteral)); - return selector; -}; - -class DetailsContentElement : public InsertionPoint { -public: - static PassRefPtr<DetailsContentElement> create(Document&); - -private: - DetailsContentElement(Document& document) - : InsertionPoint(webkitShadowContentTag, document) - { - } - - virtual MatchType matchTypeFor(Node* node) const override - { - if (node->isElementNode() && node == node->parentNode()->querySelector(summaryQuerySelector(), ASSERT_NO_EXCEPTION)) - return NeverMatches; - return AlwaysMatches; - } -}; + static NeverDestroyed<DetailEventSender> sharedToggleEventSender(eventNames().toggleEvent); + return sharedToggleEventSender; +} -PassRefPtr<DetailsContentElement> DetailsContentElement::create(Document& document) +static const AtomicString& summarySlotName() { - return adoptRef(new DetailsContentElement(document)); + static NeverDestroyed<AtomicString> summarySlot("summarySlot"); + return summarySlot; } -class DetailsSummaryElement : public InsertionPoint { -public: - static PassRefPtr<DetailsSummaryElement> create(Document&); - - Element* fallbackSummary() - { - ASSERT(firstChild() && firstChild()->hasTagName(summaryTag)); - return toElement(firstChild()); - } - +class DetailsSlotAssignment final : public SlotAssignment { private: - DetailsSummaryElement(Document& document) - : InsertionPoint(webkitShadowContentTag, document) - { - } - - virtual MatchType matchTypeFor(Node* node) const override - { - if (node->isElementNode() && node == node->parentNode()->querySelector(summaryQuerySelector(), ASSERT_NO_EXCEPTION)) - return AlwaysMatches; - return NeverMatches; - } + void hostChildElementDidChange(const Element&, ShadowRoot&) override; + const AtomicString& slotNameForHostChild(const Node&) const override; }; -PassRefPtr<DetailsSummaryElement> DetailsSummaryElement::create(Document& document) +void DetailsSlotAssignment::hostChildElementDidChange(const Element& childElement, ShadowRoot& shadowRoot) { - RefPtr<HTMLSummaryElement> summary = HTMLSummaryElement::create(summaryTag, document); - summary->appendChild(Text::create(document, defaultDetailsSummaryText()), ASSERT_NO_EXCEPTION); + if (is<HTMLSummaryElement>(childElement)) { + // Don't check whether this is the first summary element + // since we don't know the answer when this function is called inside Element::removedFrom. + didChangeSlot(summarySlotName(), shadowRoot); + } else + didChangeSlot(SlotAssignment::defaultSlotName(), shadowRoot); +} - RefPtr<DetailsSummaryElement> detailsSummary = adoptRef(new DetailsSummaryElement(document)); - detailsSummary->appendChild(summary); - return detailsSummary.release(); +const AtomicString& DetailsSlotAssignment::slotNameForHostChild(const Node& child) const +{ + auto& parent = *child.parentNode(); + ASSERT(is<HTMLDetailsElement>(parent)); + auto& details = downcast<HTMLDetailsElement>(parent); + + // The first summary child gets assigned to the summary slot. + if (is<HTMLSummaryElement>(child)) { + if (&child == childrenOfType<HTMLSummaryElement>(details).first()) + return summarySlotName(); + } + return SlotAssignment::defaultSlotName(); } -PassRefPtr<HTMLDetailsElement> HTMLDetailsElement::create(const QualifiedName& tagName, Document& document) +Ref<HTMLDetailsElement> HTMLDetailsElement::create(const QualifiedName& tagName, Document& document) { - RefPtr<HTMLDetailsElement> details = adoptRef(new HTMLDetailsElement(tagName, document)); - details->ensureUserAgentShadowRoot(); - return details.release(); + auto details = adoptRef(*new HTMLDetailsElement(tagName, document)); + details->addShadowRoot(ShadowRoot::create(document, std::make_unique<DetailsSlotAssignment>())); + return details; } HTMLDetailsElement::HTMLDetailsElement(const QualifiedName& tagName, Document& document) : HTMLElement(tagName, document) - , m_isOpen(false) { ASSERT(hasTagName(detailsTag)); } -RenderPtr<RenderElement> HTMLDetailsElement::createElementRenderer(PassRef<RenderStyle> style) +HTMLDetailsElement::~HTMLDetailsElement() +{ + detailToggleEventSender().cancelEvent(*this); +} + +RenderPtr<RenderElement> HTMLDetailsElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) { - return createRenderer<RenderBlockFlow>(*this, std::move(style)); + return createRenderer<RenderBlockFlow>(*this, WTFMove(style)); } void HTMLDetailsElement::didAddUserAgentShadowRoot(ShadowRoot* root) { - root->appendChild(DetailsSummaryElement::create(document()), ASSERT_NO_EXCEPTION); - root->appendChild(DetailsContentElement::create(document()), ASSERT_NO_EXCEPTION); + auto summarySlot = HTMLSlotElement::create(slotTag, document()); + summarySlot->setAttributeWithoutSynchronization(nameAttr, summarySlotName()); + m_summarySlot = summarySlot.ptr(); + + auto defaultSummary = HTMLSummaryElement::create(summaryTag, document()); + defaultSummary->appendChild(Text::create(document(), defaultDetailsSummaryText())); + m_defaultSummary = defaultSummary.ptr(); + + summarySlot->appendChild(defaultSummary); + root->appendChild(summarySlot); + + m_defaultSlot = HTMLSlotElement::create(slotTag, document()); + ASSERT(!m_isOpen); } -Element* HTMLDetailsElement::findMainSummary() const +bool HTMLDetailsElement::isActiveSummary(const HTMLSummaryElement& summary) const { - for (Node* child = firstChild(); child; child = child->nextSibling()) { - if (child->hasTagName(summaryTag)) - return toElement(child); - } + if (!m_summarySlot->assignedNodes()) + return &summary == m_defaultSummary; - return static_cast<DetailsSummaryElement*>(userAgentShadowRoot()->firstChild())->fallbackSummary(); + if (summary.parentNode() != this) + return false; + + auto* slot = shadowRoot()->findAssignedSlot(summary); + if (!slot) + return false; + return slot == m_summarySlot; +} + +void HTMLDetailsElement::dispatchPendingEvent(DetailEventSender* eventSender) +{ + ASSERT_UNUSED(eventSender, eventSender == &detailToggleEventSender()); + dispatchEvent(Event::create(eventNames().toggleEvent, false, false)); } void HTMLDetailsElement::parseAttribute(const QualifiedName& name, const AtomicString& value) @@ -136,34 +146,30 @@ void HTMLDetailsElement::parseAttribute(const QualifiedName& name, const AtomicS if (name == openAttr) { bool oldValue = m_isOpen; m_isOpen = !value.isNull(); - if (oldValue != m_isOpen) - setNeedsStyleRecalc(ReconstructRenderTree); + if (oldValue != m_isOpen) { + auto* root = shadowRoot(); + ASSERT(root); + if (m_isOpen) + root->appendChild(*m_defaultSlot); + else + root->removeChild(*m_defaultSlot); + + // https://html.spec.whatwg.org/#details-notification-task-steps. + detailToggleEventSender().cancelEvent(*this); + detailToggleEventSender().dispatchEventSoon(*this); + } } else HTMLElement::parseAttribute(name, value); } -bool HTMLDetailsElement::childShouldCreateRenderer(const Node& child) const -{ - if (child.isPseudoElement()) - return HTMLElement::childShouldCreateRenderer(child); - - if (!hasShadowRootOrActiveInsertionPointParent(child)) - return false; - - if (m_isOpen) - return HTMLElement::childShouldCreateRenderer(child); - - if (!child.hasTagName(summaryTag)) - return false; - - return &child == findMainSummary() && HTMLElement::childShouldCreateRenderer(child); -} void HTMLDetailsElement::toggleOpen() { - setAttribute(openAttr, m_isOpen ? nullAtom : emptyAtom); -} + setAttributeWithoutSynchronization(openAttr, m_isOpen ? nullAtom : emptyAtom); + // We need to post to the document because toggling this element will delete it. + if (AXObjectCache* cache = document().existingAXObjectCache()) + cache->postNotification(nullptr, &document(), AXObjectCache::AXExpandedChanged); } -#endif +} |