initial import
[vuplus_webkit] / 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)
25
26 #include "HTMLNames.h"
27 #include "HTMLSummaryElement.h"
28 #include "LocalizedStrings.h"
29 #include "MouseEvent.h"
30 #include "RenderDetails.h"
31 #include "ShadowContentElement.h"
32 #include "ShadowRoot.h"
33 #include "Text.h"
34
35 namespace WebCore {
36
37 using namespace HTMLNames;
38
39 class DetailsContentElement : public ShadowContentElement {
40 public:
41     static PassRefPtr<DetailsContentElement> create(Document*);
42
43 private:
44     DetailsContentElement(Document* document)
45         : ShadowContentElement(HTMLNames::divTag, document)
46     {
47     }
48
49     virtual bool shouldInclude(Node*);
50 };
51
52 PassRefPtr<DetailsContentElement> DetailsContentElement::create(Document* document)
53 {
54     return adoptRef(new DetailsContentElement(document));
55 }
56
57 bool DetailsContentElement::shouldInclude(Node* node)
58 {
59     HTMLDetailsElement* details = static_cast<HTMLDetailsElement*>(shadowAncestorNode());
60     return details->mainSummary() != node;
61 }
62
63
64 class DetailsSummaryElement : public ShadowContentElement {
65 public:
66     static PassRefPtr<DetailsSummaryElement> create(Document*);
67
68 private:
69     DetailsSummaryElement(Document* document)
70         : ShadowContentElement(HTMLNames::divTag, document)
71     {
72     }
73
74     virtual bool shouldInclude(Node*);
75 };
76
77 PassRefPtr<DetailsSummaryElement> DetailsSummaryElement::create(Document* document)
78 {
79     return adoptRef(new DetailsSummaryElement(document));
80 }
81
82 bool DetailsSummaryElement::shouldInclude(Node* node)
83 {
84     HTMLDetailsElement* details = static_cast<HTMLDetailsElement*>(shadowAncestorNode());
85     return details->mainSummary() == node;
86 }
87
88
89 PassRefPtr<HTMLDetailsElement> HTMLDetailsElement::create(const QualifiedName& tagName, Document* document)
90 {
91     RefPtr<HTMLDetailsElement> result = adoptRef(new HTMLDetailsElement(tagName, document));
92     result->ensureShadowSubtreeOf(ForwardingSummary);
93     return result;
94 }
95
96 HTMLDetailsElement::HTMLDetailsElement(const QualifiedName& tagName, Document* document)
97     : HTMLElement(tagName, document)
98     , m_summaryType(NoSummary)
99     , m_mainSummary(0)
100     , m_isOpen(false)
101 {
102     ASSERT(hasTagName(detailsTag));
103 }
104
105 RenderObject* HTMLDetailsElement::createRenderer(RenderArena* arena, RenderStyle*)
106 {
107     return new (arena) RenderDetails(this);
108 }
109
110 void HTMLDetailsElement::ensureShadowSubtreeOf(SummaryType type)
111 {
112     if (type == m_summaryType)
113         return;
114     m_summaryType = type;
115     removeShadowRoot();
116     createShadowSubtree();
117 }
118
119 static Node* findSummaryFor(PassRefPtr<ContainerNode> container)
120 {
121     for (Node* child = container->firstChild(); child; child = child->nextSibling()) {
122         if (child->hasTagName(summaryTag))
123             return child;
124     }
125
126     return 0;
127 }
128
129 Node* HTMLDetailsElement::ensureMainSummary()
130 {
131     Node* summary = findSummaryFor(this);
132     if (summary) {
133         ensureShadowSubtreeOf(ForwardingSummary);
134         return summary;
135     }
136
137     ensureShadowSubtreeOf(DefaultSummary);
138     return findSummaryFor(shadowRoot());
139 }
140
141 void HTMLDetailsElement::refreshMainSummary(RefreshRenderer refreshRenderer)
142 {
143     RefPtr<Node> oldSummary = m_mainSummary;
144     m_mainSummary = ensureMainSummary();
145
146     if (oldSummary == m_mainSummary || !attached())
147         return;
148
149     if (oldSummary && oldSummary->parentNodeForRenderingAndStyle())
150         oldSummary->reattach();
151     if (m_mainSummary && refreshRenderer == RefreshRendererAllowed)
152         m_mainSummary->reattach();
153 }
154
155 void HTMLDetailsElement::createShadowSubtree()
156 {
157     ASSERT(!shadowRoot());
158     ExceptionCode ec = 0;
159     if (m_summaryType == DefaultSummary) {
160         RefPtr<HTMLSummaryElement> defaultSummary = HTMLSummaryElement::create(summaryTag, document());
161         defaultSummary->appendChild(Text::create(document(), defaultDetailsSummaryText()), ec);
162         ensureShadowRoot()->appendChild(defaultSummary, ec, true);
163         ensureShadowRoot()->appendChild(DetailsContentElement::create(document()), ec, true);
164     } else {
165         ASSERT(m_summaryType == ForwardingSummary);
166         ensureShadowRoot()->appendChild(DetailsSummaryElement::create(document()), ec, true);
167         ensureShadowRoot()->appendChild(DetailsContentElement::create(document()), ec, true);
168     }
169 }
170
171
172 void HTMLDetailsElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
173 {
174     HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
175     // If childCountDelta is less then zero and the main summary has changed it must be because previous main
176     // summary was removed. The new main summary was then inside the unrevealed content and needs to be
177     // reattached to create its renderer. If childCountDelta is not less then zero then a new <summary> element
178     // has been added and it will be attached without our help.
179     if (!changedByParser)
180         refreshMainSummary(childCountDelta < 0 ? RefreshRendererAllowed : RefreshRendererSupressed);
181 }
182
183 void HTMLDetailsElement::finishParsingChildren()
184 {
185     HTMLElement::finishParsingChildren();
186     refreshMainSummary(RefreshRendererAllowed);
187 }
188
189 void HTMLDetailsElement::parseMappedAttribute(Attribute* attr)
190 {
191     if (attr->name() == openAttr) {
192         bool oldValue = m_isOpen;
193         m_isOpen =  !attr->value().isNull();
194         if (oldValue != m_isOpen)
195             reattachIfAttached();
196     } else
197         HTMLElement::parseMappedAttribute(attr);
198 }
199
200 bool HTMLDetailsElement::childShouldCreateRenderer(Node* child) const
201 {
202     return m_isOpen || child == m_mainSummary;
203 }
204
205 void HTMLDetailsElement::toggleOpen()
206 {
207     setAttribute(openAttr, m_isOpen ? nullAtom : emptyAtom);
208 }
209
210 }
211
212 #endif