8bbdb97c2524b62ab39aa64e6840855ca469559c
[WebKit-https.git] / Source / WebCore / html / ColorInputType.cpp
1 /*
2  * Copyright (C) 2010 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 #if ENABLE(INPUT_TYPE_COLOR)
33 #include "ColorInputType.h"
34
35 #include "CSSPropertyNames.h"
36 #include "Chrome.h"
37 #include "Color.h"
38 #include "HTMLDataListElement.h"
39 #include "HTMLDivElement.h"
40 #include "HTMLInputElement.h"
41 #include "HTMLOptionElement.h"
42 #include "InputTypeNames.h"
43 #include "MouseEvent.h"
44 #include "RenderObject.h"
45 #include "RenderView.h"
46 #include "ScriptController.h"
47 #include "ShadowRoot.h"
48
49 namespace WebCore {
50
51 using namespace HTMLNames;
52
53 static bool isValidColorString(const String& value)
54 {
55     if (value.isEmpty())
56         return false;
57     if (value[0] != '#')
58         return false;
59
60     // We don't accept #rgb and #aarrggbb formats.
61     if (value.length() != 7)
62         return false;
63     Color color(value);
64     return color.isValid() && !color.hasAlpha();
65 }
66
67 ColorInputType::~ColorInputType()
68 {
69     endColorChooser();
70 }
71
72 bool ColorInputType::isColorControl() const
73 {
74     return true;
75 }
76
77 const AtomicString& ColorInputType::formControlType() const
78 {
79     return InputTypeNames::color();
80 }
81
82 bool ColorInputType::supportsRequired() const
83 {
84     return false;
85 }
86
87 String ColorInputType::fallbackValue() const
88 {
89     return String("#000000");
90 }
91
92 String ColorInputType::sanitizeValue(const String& proposedValue) const
93 {
94     if (!isValidColorString(proposedValue))
95         return fallbackValue();
96
97     return proposedValue.lower();
98 }
99
100 Color ColorInputType::valueAsColor() const
101 {
102     return Color(element().value());
103 }
104
105 void ColorInputType::createShadowSubtree()
106 {
107     ASSERT(element().shadowRoot());
108
109     Document& document = element().document();
110     RefPtr<HTMLDivElement> wrapperElement = HTMLDivElement::create(document);
111     wrapperElement->setPseudo(AtomicString("-webkit-color-swatch-wrapper", AtomicString::ConstructFromLiteral));
112     RefPtr<HTMLDivElement> colorSwatch = HTMLDivElement::create(document);
113     colorSwatch->setPseudo(AtomicString("-webkit-color-swatch", AtomicString::ConstructFromLiteral));
114     wrapperElement->appendChild(colorSwatch.release(), ASSERT_NO_EXCEPTION);
115     element().userAgentShadowRoot()->appendChild(wrapperElement.release(), ASSERT_NO_EXCEPTION);
116     
117     updateColorSwatch();
118 }
119
120 void ColorInputType::setValue(const String& value, bool valueChanged, TextFieldEventBehavior eventBehavior)
121 {
122     InputType::setValue(value, valueChanged, eventBehavior);
123
124     if (!valueChanged)
125         return;
126
127     updateColorSwatch();
128     if (m_chooser)
129         m_chooser->setSelectedColor(valueAsColor());
130 }
131
132 void ColorInputType::handleDOMActivateEvent(Event* event)
133 {
134     if (element().isDisabledOrReadOnly() || !element().renderer())
135         return;
136
137     if (!ScriptController::processingUserGesture())
138         return;
139
140     if (Chrome* chrome = this->chrome()) {
141         if (!m_chooser)
142             m_chooser = chrome->createColorChooser(this, valueAsColor());
143         else
144             m_chooser->reattachColorChooser(valueAsColor());
145     }
146
147     event->setDefaultHandled();
148 }
149
150 void ColorInputType::detach()
151 {
152     endColorChooser();
153 }
154
155 bool ColorInputType::shouldRespectListAttribute()
156 {
157     return InputType::themeSupportsDataListUI(this);
158 }
159
160 bool ColorInputType::typeMismatchFor(const String& value) const
161 {
162     return !isValidColorString(value);
163 }
164
165 bool ColorInputType::shouldResetOnDocumentActivation()
166 {
167     return true;
168 }
169
170 void ColorInputType::didChooseColor(const Color& color)
171 {
172     if (element().isDisabledOrReadOnly() || color == valueAsColor())
173         return;
174     element().setValueFromRenderer(color.serialized());
175     updateColorSwatch();
176     element().dispatchFormControlChangeEvent();
177 }
178
179 void ColorInputType::didEndChooser()
180 {
181     m_chooser = nullptr;
182 }
183
184 void ColorInputType::endColorChooser()
185 {
186     if (m_chooser)
187         m_chooser->endChooser();
188 }
189
190 void ColorInputType::updateColorSwatch()
191 {
192     HTMLElement* colorSwatch = shadowColorSwatch();
193     if (!colorSwatch)
194         return;
195
196     colorSwatch->setInlineStyleProperty(CSSPropertyBackgroundColor, element().value(), false);
197 }
198
199 HTMLElement* ColorInputType::shadowColorSwatch() const
200 {
201     ShadowRoot* shadow = element().userAgentShadowRoot();
202     return shadow ? downcast<HTMLElement>(shadow->firstChild()->firstChild()) : nullptr;
203 }
204
205 IntRect ColorInputType::elementRectRelativeToRootView() const
206 {
207     if (!element().renderer())
208         return IntRect();
209     return element().document().view()->contentsToRootView(element().renderer()->absoluteBoundingBoxRect());
210 }
211
212 Color ColorInputType::currentColor()
213 {
214     return valueAsColor();
215 }
216
217 bool ColorInputType::shouldShowSuggestions() const
218 {
219 #if ENABLE(DATALIST_ELEMENT)
220     return element().fastHasAttribute(listAttr);
221 #else
222     return false;
223 #endif
224 }
225
226 Vector<Color> ColorInputType::suggestions() const
227 {
228     Vector<Color> suggestions;
229 #if ENABLE(DATALIST_ELEMENT)
230     HTMLDataListElement* dataList = element().dataList();
231     if (dataList) {
232         Ref<HTMLCollection> options = dataList->options();
233         for (unsigned i = 0; HTMLOptionElement* option = downcast<HTMLOptionElement>(options->item(i)); ++i) {
234             if (!element().isValidValue(option->value()))
235                 continue;
236             Color color(option->value());
237             if (!color.isValid())
238                 continue;
239             suggestions.append(color);
240         }
241     }
242 #endif
243     return suggestions;
244 }
245
246 } // namespace WebCore
247
248 #endif // ENABLE(INPUT_TYPE_COLOR)