summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/HTMLDetailsElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/html/HTMLDetailsElement.cpp')
-rw-r--r--Source/WebCore/html/HTMLDetailsElement.cpp184
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
+}