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/SearchInputType.h"
33
34#include "bindings/core/v8/ExceptionStatePlaceholder.h"
35#include "core/HTMLNames.h"
36#include "core/InputTypeNames.h"
37#include "core/events/KeyboardEvent.h"
38#include "core/dom/shadow/ShadowRoot.h"
39#include "core/html/HTMLInputElement.h"
40#include "core/html/shadow/ShadowElementNames.h"
41#include "core/html/shadow/TextControlInnerElements.h"
42#include "core/rendering/RenderSearchField.h"
43#include "wtf/PassOwnPtr.h"
44
45namespace blink {
46
47using namespace HTMLNames;
48
49inline SearchInputType::SearchInputType(HTMLInputElement& element)
50    : BaseTextInputType(element)
51    , m_searchEventTimer(this, &SearchInputType::searchEventTimerFired)
52{
53}
54
55PassRefPtrWillBeRawPtr<InputType> SearchInputType::create(HTMLInputElement& element)
56{
57    return adoptRefWillBeNoop(new SearchInputType(element));
58}
59
60void SearchInputType::countUsage()
61{
62    countUsageIfVisible(UseCounter::InputTypeSearch);
63}
64
65RenderObject* SearchInputType::createRenderer(RenderStyle*) const
66{
67    return new RenderSearchField(&element());
68}
69
70const AtomicString& SearchInputType::formControlType() const
71{
72    return InputTypeNames::search;
73}
74
75bool SearchInputType::shouldRespectSpeechAttribute()
76{
77    return true;
78}
79
80bool SearchInputType::needsContainer() const
81{
82    return true;
83}
84
85void SearchInputType::createShadowSubtree()
86{
87    TextFieldInputType::createShadowSubtree();
88    Element* container = containerElement();
89    Element* viewPort = element().userAgentShadowRoot()->getElementById(ShadowElementNames::editingViewPort());
90    ASSERT(container);
91    ASSERT(viewPort);
92
93    container->insertBefore(SearchFieldDecorationElement::create(element().document()), viewPort);
94    container->insertBefore(SearchFieldCancelButtonElement::create(element().document()), viewPort->nextSibling());
95}
96
97void SearchInputType::handleKeydownEvent(KeyboardEvent* event)
98{
99    if (element().isDisabledOrReadOnly()) {
100        TextFieldInputType::handleKeydownEvent(event);
101        return;
102    }
103
104    const String& key = event->keyIdentifier();
105    if (key == "U+001B") {
106        RefPtrWillBeRawPtr<HTMLInputElement> input(element());
107        input->setValueForUser("");
108        input->onSearch();
109        event->setDefaultHandled();
110        return;
111    }
112    TextFieldInputType::handleKeydownEvent(event);
113}
114
115void SearchInputType::startSearchEventTimer()
116{
117    ASSERT(element().renderer());
118    unsigned length = element().innerEditorValue().length();
119
120    if (!length) {
121        stopSearchEventTimer();
122        element().onSearch();
123        return;
124    }
125
126    // After typing the first key, we wait 0.5 seconds.
127    // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
128    m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length), FROM_HERE);
129}
130
131void SearchInputType::stopSearchEventTimer()
132{
133    m_searchEventTimer.stop();
134}
135
136void SearchInputType::searchEventTimerFired(Timer<SearchInputType>*)
137{
138    element().onSearch();
139}
140
141bool SearchInputType::searchEventsShouldBeDispatched() const
142{
143    return element().hasAttribute(incrementalAttr);
144}
145
146void SearchInputType::didSetValueByUserEdit(ValueChangeState state)
147{
148    updateCancelButtonVisibility();
149
150    // If the incremental attribute is set, then dispatch the search event
151    if (searchEventsShouldBeDispatched())
152        startSearchEventTimer();
153
154    TextFieldInputType::didSetValueByUserEdit(state);
155}
156
157void SearchInputType::updateView()
158{
159    BaseTextInputType::updateView();
160    updateCancelButtonVisibility();
161}
162
163void SearchInputType::updateCancelButtonVisibility()
164{
165    Element* button = element().userAgentShadowRoot()->getElementById(ShadowElementNames::clearButton());
166    if (!button)
167        return;
168    if (element().value().isEmpty()) {
169        button->setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
170        button->setInlineStyleProperty(CSSPropertyPointerEvents, CSSValueNone);
171    } else {
172        button->removeInlineStyleProperty(CSSPropertyOpacity);
173        button->removeInlineStyleProperty(CSSPropertyPointerEvents);
174    }
175}
176
177bool SearchInputType::supportsInputModeAttribute() const
178{
179    return true;
180}
181
182} // namespace blink
183