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