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