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