ContentDistribution should be only used for details elements
[WebKit-https.git] / Source / WebCore / html / HTMLDetailsElement.cpp
1 /*
2  * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  *
19  */
20
21 #include "config.h"
22 #include "HTMLDetailsElement.h"
23
24 #if ENABLE(DETAILS_ELEMENT)
25 #include "AXObjectCache.h"
26 #include "ContentDistributor.h"
27 #include "ElementIterator.h"
28 #include "HTMLSummaryElement.h"
29 #include "InsertionPoint.h"
30 #include "LocalizedStrings.h"
31 #include "MouseEvent.h"
32 #include "RenderBlockFlow.h"
33 #include "Text.h"
34
35 namespace WebCore {
36
37 using namespace HTMLNames;
38
39 static const AtomicString& summaryQuerySelector()
40 {
41     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, selector, ("summary:first-of-type", AtomicString::ConstructFromLiteral));
42     return selector;
43 };
44
45 class DetailsContentElement final : public InsertionPoint {
46 public:
47     static Ref<DetailsContentElement> create(Document&);
48
49 private:
50     DetailsContentElement(Document& document)
51         : InsertionPoint(webkitShadowContentTag, document)
52     {
53     }
54
55     virtual MatchType matchTypeFor(Node* node) const override
56     {
57         if (node->isElementNode() && node == node->parentNode()->querySelector(summaryQuerySelector(), ASSERT_NO_EXCEPTION))
58             return NeverMatches;
59         return AlwaysMatches;
60     }
61 };
62
63 Ref<DetailsContentElement> DetailsContentElement::create(Document& document)
64 {
65     return adoptRef(*new DetailsContentElement(document));
66 }
67
68 class DetailsSummaryElement final : public InsertionPoint {
69 public:
70     static Ref<DetailsSummaryElement> create(Document&);
71
72     Element* fallbackSummary()
73     {
74         ASSERT(firstChild() && firstChild()->hasTagName(summaryTag));
75         return downcast<Element>(firstChild());
76     }
77
78 private:
79     DetailsSummaryElement(Document& document)
80         : InsertionPoint(webkitShadowContentTag, document)
81     {
82     }
83
84     virtual MatchType matchTypeFor(Node* node) const override
85     {
86         if (node->isElementNode() && node == node->parentNode()->querySelector(summaryQuerySelector(), ASSERT_NO_EXCEPTION))
87             return AlwaysMatches;
88         return NeverMatches;
89     }
90 };
91
92 Ref<DetailsSummaryElement> DetailsSummaryElement::create(Document& document)
93 {
94     Ref<HTMLSummaryElement> summary = HTMLSummaryElement::create(summaryTag, document);
95     summary->appendChild(Text::create(document, defaultDetailsSummaryText()), ASSERT_NO_EXCEPTION);
96
97     Ref<DetailsSummaryElement> detailsSummary = adoptRef(*new DetailsSummaryElement(document));
98     detailsSummary->appendChild(WTF::move(summary));
99     return detailsSummary;
100 }
101
102 Ref<HTMLDetailsElement> HTMLDetailsElement::create(const QualifiedName& tagName, Document& document)
103 {
104     Ref<HTMLDetailsElement> details = adoptRef(*new HTMLDetailsElement(tagName, document));
105     details->addShadowRoot(ShadowRootWithInsertionPoints::create(document));
106     return details;
107 }
108
109 HTMLDetailsElement::HTMLDetailsElement(const QualifiedName& tagName, Document& document)
110     : HTMLElement(tagName, document)
111     , m_isOpen(false)
112 {
113     ASSERT(hasTagName(detailsTag));
114 }
115
116 RenderPtr<RenderElement> HTMLDetailsElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
117 {
118     return createRenderer<RenderBlockFlow>(*this, WTF::move(style));
119 }
120
121 void HTMLDetailsElement::didAddUserAgentShadowRoot(ShadowRoot* root)
122 {
123     root->appendChild(DetailsSummaryElement::create(document()), ASSERT_NO_EXCEPTION);
124     root->appendChild(DetailsContentElement::create(document()), ASSERT_NO_EXCEPTION);
125 }
126
127 const Element* HTMLDetailsElement::findMainSummary() const
128 {
129     if (auto summary = childrenOfType<HTMLSummaryElement>(*this).first())
130         return summary;
131
132     return static_cast<DetailsSummaryElement*>(userAgentShadowRoot()->firstChild())->fallbackSummary();
133 }
134
135 void HTMLDetailsElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
136 {
137     if (name == openAttr) {
138         bool oldValue = m_isOpen;
139         m_isOpen = !value.isNull();
140         if (oldValue != m_isOpen)
141             setNeedsStyleRecalc(ReconstructRenderTree);
142     } else
143         HTMLElement::parseAttribute(name, value);
144 }
145
146 bool HTMLDetailsElement::childShouldCreateRenderer(const Node& child) const
147 {
148     if (child.isPseudoElement())
149         return HTMLElement::childShouldCreateRenderer(child);
150
151     if (!hasShadowRootOrActiveInsertionPointParent(child))
152         return false;
153
154     if (m_isOpen)
155         return HTMLElement::childShouldCreateRenderer(child);
156
157     if (!child.hasTagName(summaryTag))
158         return false;
159
160     return &child == findMainSummary() && HTMLElement::childShouldCreateRenderer(child);
161 }
162
163 void HTMLDetailsElement::toggleOpen()
164 {
165     setAttribute(openAttr, m_isOpen ? nullAtom : emptyAtom);
166
167     // We need to post to the document because toggling this element will delete it.
168     if (AXObjectCache* cache = document().existingAXObjectCache())
169         cache->postNotification(nullptr, &document(), AXObjectCache::AXExpandedChanged);
170 }
171
172 }
173
174 #endif