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