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