1/*
2 * Copyright (C) 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 "HTMLSummaryElement.h"
23
24#if ENABLE(DETAILS_ELEMENT)
25#include "DetailsMarkerControl.h"
26#include "HTMLContentElement.h"
27#include "HTMLDetailsElement.h"
28#include "HTMLNames.h"
29#include "KeyboardEvent.h"
30#include "MouseEvent.h"
31#include "NodeRenderingContext.h"
32#include "PlatformMouseEvent.h"
33#include "RenderBlock.h"
34#include "ShadowRoot.h"
35
36namespace WebCore {
37
38using namespace HTMLNames;
39
40class SummaryContentElement : public InsertionPoint {
41public:
42    static PassRefPtr<SummaryContentElement> create(Document*);
43
44private:
45    SummaryContentElement(Document* document)
46        : InsertionPoint(HTMLNames::webkitShadowContentTag, document)
47    {
48    }
49};
50
51PassRefPtr<SummaryContentElement> SummaryContentElement::create(Document* document)
52{
53    return adoptRef(new SummaryContentElement(document));
54}
55
56PassRefPtr<HTMLSummaryElement> HTMLSummaryElement::create(const QualifiedName& tagName, Document* document)
57{
58    RefPtr<HTMLSummaryElement> summary = adoptRef(new HTMLSummaryElement(tagName, document));
59    summary->ensureUserAgentShadowRoot();
60    return summary.release();
61}
62
63HTMLSummaryElement::HTMLSummaryElement(const QualifiedName& tagName, Document* document)
64    : HTMLElement(tagName, document)
65{
66    ASSERT(hasTagName(summaryTag));
67}
68
69RenderObject* HTMLSummaryElement::createRenderer(RenderArena* arena, RenderStyle*)
70{
71    return new (arena) RenderBlock(this);
72}
73
74bool HTMLSummaryElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
75{
76    if (childContext.node()->isPseudoElement())
77        return HTMLElement::childShouldCreateRenderer(childContext);
78
79    return childContext.isOnEncapsulationBoundary() && HTMLElement::childShouldCreateRenderer(childContext);
80}
81
82void HTMLSummaryElement::didAddUserAgentShadowRoot(ShadowRoot* root)
83{
84    root->appendChild(DetailsMarkerControl::create(document()), ASSERT_NO_EXCEPTION, AttachLazily);
85    root->appendChild(SummaryContentElement::create(document()), ASSERT_NO_EXCEPTION, AttachLazily);
86}
87
88HTMLDetailsElement* HTMLSummaryElement::detailsElement() const
89{
90    Node* mayDetails = const_cast<HTMLSummaryElement*>(this)->parentNodeForRenderingAndStyle();
91    if (!mayDetails || !mayDetails->hasTagName(detailsTag))
92        return 0;
93    return static_cast<HTMLDetailsElement*>(mayDetails);
94}
95
96bool HTMLSummaryElement::isMainSummary() const
97{
98    if (HTMLDetailsElement* details = detailsElement())
99        return details->findMainSummary() == this;
100
101    return false;
102}
103
104static bool isClickableControl(Node* node)
105{
106    if (!node->isElementNode())
107        return false;
108    Element* element = toElement(node);
109    if (element->isFormControlElement())
110        return true;
111    Element* host = element->shadowHost();
112    return host && host->isFormControlElement();
113}
114
115bool HTMLSummaryElement::supportsFocus() const
116{
117    return isMainSummary();
118}
119
120void HTMLSummaryElement::defaultEventHandler(Event* event)
121{
122    if (isMainSummary() && renderer()) {
123        if (event->type() == eventNames().DOMActivateEvent && !isClickableControl(event->target()->toNode())) {
124            if (HTMLDetailsElement* details = detailsElement())
125                details->toggleOpen();
126            event->setDefaultHandled();
127            return;
128        }
129
130        if (event->isKeyboardEvent()) {
131            if (event->type() == eventNames().keydownEvent && static_cast<KeyboardEvent*>(event)->keyIdentifier() == "U+0020") {
132                setActive(true, true);
133                // No setDefaultHandled() - IE dispatches a keypress in this case.
134                return;
135            }
136            if (event->type() == eventNames().keypressEvent) {
137                switch (static_cast<KeyboardEvent*>(event)->charCode()) {
138                case '\r':
139                    dispatchSimulatedClick(event);
140                    event->setDefaultHandled();
141                    return;
142                case ' ':
143                    // Prevent scrolling down the page.
144                    event->setDefaultHandled();
145                    return;
146                }
147            }
148            if (event->type() == eventNames().keyupEvent && static_cast<KeyboardEvent*>(event)->keyIdentifier() == "U+0020") {
149                if (active())
150                    dispatchSimulatedClick(event);
151                event->setDefaultHandled();
152                return;
153            }
154        }
155    }
156
157    HTMLElement::defaultEventHandler(event);
158}
159
160bool HTMLSummaryElement::willRespondToMouseClickEvents()
161{
162    if (isMainSummary() && renderer())
163        return true;
164
165    return HTMLElement::willRespondToMouseClickEvents();
166}
167
168}
169
170#endif
171