Refactor ShadowRoot exception handling
[WebKit-https.git] / Source / WebCore / html / shadow / TextFieldDecorationElement.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "TextFieldDecorationElement.h"
33
34 #include "CSSPropertyNames.h"
35 #include "CSSValueKeywords.h"
36 #include "ElementShadow.h"
37 #include "Event.h"
38 #include "HTMLInputElement.h"
39 #include "HTMLShadowElement.h"
40 #include "NodeRenderStyle.h"
41 #include "RenderImage.h"
42 #include "ShadowRoot.h"
43 #include "StyleResolver.h"
44
45 namespace WebCore {
46
47 using namespace HTMLNames;
48
49 // TextFieldDecorator ----------------------------------------------------------------
50
51 TextFieldDecorator::~TextFieldDecorator()
52 {
53 }
54
55 // TextFieldDecorationElement ----------------------------------------------------------------
56
57 TextFieldDecorationElement::TextFieldDecorationElement(Document* document, TextFieldDecorator* decorator)
58     : HTMLDivElement(HTMLNames::divTag, document)
59     , m_textFieldDecorator(decorator)
60     , m_isInHoverState(false)
61 {
62     ASSERT(decorator);
63     setHasCustomCallbacks();
64 }
65
66 PassRefPtr<TextFieldDecorationElement> TextFieldDecorationElement::create(Document* document, TextFieldDecorator* decorator)
67 {
68     return adoptRef(new TextFieldDecorationElement(document, decorator));
69 }
70
71 TextFieldDecorationElement* TextFieldDecorationElement::fromShadowRoot(ShadowRoot* shadowRoot)
72 {
73     if (!shadowRoot->firstChild()
74         || !shadowRoot->firstChild()->lastChild()
75         || !shadowRoot->firstChild()->lastChild()->isElementNode()
76         || !toElement(shadowRoot->firstChild()->lastChild())->isTextFieldDecoration())
77         return 0;
78     return toTextFieldDecorationElement(shadowRoot->firstChild()->lastChild());
79 }
80
81 static inline void getDecorationRootAndDecoratedRoot(HTMLInputElement* input, ShadowRoot*& decorationRoot, ShadowRoot*& decoratedRoot)
82 {
83     ShadowRoot* existingRoot = input->youngestShadowRoot();
84     ShadowRoot* newRoot = 0;
85     while (existingRoot->childNodeCount() == 1 && existingRoot->firstChild()->hasTagName(shadowTag)) {
86         newRoot = existingRoot;
87         existingRoot = existingRoot->olderShadowRoot();
88         ASSERT(existingRoot);
89     }
90     if (newRoot)
91         newRoot->removeChild(newRoot->firstChild());
92     else
93         newRoot = input->ensureUserAgentShadowRoot();
94     decorationRoot = newRoot;
95     decoratedRoot = existingRoot;
96 }
97
98 void TextFieldDecorationElement::decorate(HTMLInputElement* input, bool visible)
99 {
100     ASSERT(input);
101     ShadowRoot* existingRoot;
102     ShadowRoot* decorationRoot;
103     getDecorationRootAndDecoratedRoot(input, decorationRoot, existingRoot);
104     ASSERT(decorationRoot);
105     ASSERT(existingRoot);
106     RefPtr<HTMLDivElement> box = HTMLDivElement::create(input->document());
107     decorationRoot->appendChild(box);
108     box->setInlineStyleProperty(CSSPropertyDisplay, CSSValueWebkitBox);
109     box->setInlineStyleProperty(CSSPropertyWebkitBoxAlign, CSSValueCenter);
110     ASSERT(existingRoot->childNodeCount() == 1);
111     toHTMLElement(existingRoot->firstChild())->setInlineStyleProperty(CSSPropertyWebkitBoxFlex, 1.0, CSSPrimitiveValue::CSS_NUMBER);
112 #if ENABLE(SHADOW_DOM)
113     box->appendChild(HTMLShadowElement::create(HTMLNames::shadowTag, input->document()));
114 #endif
115     setInlineStyleProperty(CSSPropertyDisplay, visible ? CSSValueBlock : CSSValueNone);
116     box->appendChild(this);
117 }
118
119 inline HTMLInputElement* TextFieldDecorationElement::hostInput()
120 {
121     // TextFieldDecorationElement is created only by C++ code, and it is always
122     // in <input> shadow.
123     ASSERT(!shadowHost() || shadowHost()->hasTagName(inputTag));
124     return static_cast<HTMLInputElement*>(shadowHost());
125 }
126
127 bool TextFieldDecorationElement::isTextFieldDecoration() const
128 {
129     return true;
130 }
131
132 void TextFieldDecorationElement::updateImage()
133 {
134     if (!renderer() || !renderer()->isImage())
135         return;
136     RenderImageResource* resource = toRenderImage(renderer())->imageResource();
137     CachedImage* image;
138     if (hostInput()->disabled())
139         image = m_textFieldDecorator->imageForDisabledState();
140     else if (hostInput()->readOnly())
141         image = m_textFieldDecorator->imageForReadonlyState();
142     else if (m_isInHoverState)
143         image = m_textFieldDecorator->imageForHoverState();
144     else
145         image = m_textFieldDecorator->imageForNormalState();
146     ASSERT(image);
147     resource->setCachedImage(image);
148 }
149
150 PassRefPtr<RenderStyle> TextFieldDecorationElement::customStyleForRenderer()
151 {
152     RefPtr<RenderStyle> originalStyle = document()->styleResolver()->styleForElement(this);
153     RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
154     RenderStyle* inputStyle = hostInput()->renderStyle();
155     ASSERT(inputStyle);
156     style->setWidth(Length(inputStyle->fontSize(), Fixed));
157     style->setHeight(Length(inputStyle->fontSize(), Fixed));
158     updateImage();
159     return style.release();
160 }
161
162 RenderObject* TextFieldDecorationElement::createRenderer(RenderArena* arena, RenderStyle*)
163 {
164     RenderImage* image = new (arena) RenderImage(this);
165     image->setImageResource(RenderImageResource::create());
166     return image;
167 }
168
169 void TextFieldDecorationElement::attach()
170 {
171     HTMLDivElement::attach();
172     updateImage();
173 }
174
175 void TextFieldDecorationElement::detach()
176 {
177     m_textFieldDecorator->willDetach(hostInput());
178     HTMLDivElement::detach();
179 }
180
181 bool TextFieldDecorationElement::isMouseFocusable() const
182 {
183     return false;
184 }
185
186 void TextFieldDecorationElement::defaultEventHandler(Event* event)
187 {
188     RefPtr<HTMLInputElement> input(hostInput());
189     if (!input || input->isDisabledOrReadOnly() || !event->isMouseEvent()) {
190         if (!event->defaultHandled())
191             HTMLDivElement::defaultEventHandler(event);
192         return;
193     }
194
195     RefPtr<TextFieldDecorationElement> protector(this);
196     if (event->type() == eventNames().clickEvent) {
197         m_textFieldDecorator->handleClick(input.get());
198         event->setDefaultHandled();
199     }
200
201     if (event->type() == eventNames().mouseoverEvent) {
202         m_isInHoverState = true;
203         updateImage();
204     }
205
206     if (event->type() == eventNames().mouseoutEvent) {
207         m_isInHoverState = false;
208         updateImage();
209     }
210
211     if (!event->defaultHandled())
212         HTMLDivElement::defaultEventHandler(event);
213 }
214
215 bool TextFieldDecorationElement::willRespondToMouseMoveEvents()
216 {
217     const HTMLInputElement* input = hostInput();
218     if (!input->isDisabledOrReadOnly())
219         return true;
220
221     return HTMLDivElement::willRespondToMouseMoveEvents();
222 }
223
224 bool TextFieldDecorationElement::willRespondToMouseClickEvents()
225 {
226     const HTMLInputElement* input = hostInput();
227     if (!input->isDisabledOrReadOnly())
228         return true;
229
230     return HTMLDivElement::willRespondToMouseClickEvents();
231 }
232
233 } // namespace WebCore