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 "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