InputType should return input renderers wrapped in RenderPtr.
[WebKit-https.git] / Source / WebCore / html / SearchInputType.cpp
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 "SearchInputType.h"
33
34 #include "HTMLInputElement.h"
35 #include "HTMLNames.h"
36 #include "InputTypeNames.h"
37 #include "KeyboardEvent.h"
38 #include "RenderSearchField.h"
39 #include "ShadowRoot.h"
40 #include "TextControlInnerElements.h"
41
42 namespace WebCore {
43
44 using namespace HTMLNames;
45
46 SearchInputType::SearchInputType(HTMLInputElement& element)
47     : BaseTextInputType(element)
48     , m_resultsButton(0)
49     , m_cancelButton(0)
50     , m_searchEventTimer(this, &SearchInputType::searchEventTimerFired)
51 {
52 }
53
54 void SearchInputType::attach()
55 {
56     TextFieldInputType::attach();
57     observeFeatureIfVisible(FeatureObserver::InputTypeSearch);
58 }
59
60 void SearchInputType::addSearchResult()
61 {
62 #if !PLATFORM(IOS)
63     if (RenderObject* renderer = element().renderer())
64         toRenderSearchField(renderer)->addSearchResult();
65 #endif
66 }
67
68 RenderPtr<RenderElement> SearchInputType::createInputRenderer(PassRef<RenderStyle> style)
69 {
70     return createRenderer<RenderSearchField>(element(), std::move(style));
71 }
72
73 const AtomicString& SearchInputType::formControlType() const
74 {
75     return InputTypeNames::search();
76 }
77
78 bool SearchInputType::shouldRespectSpeechAttribute()
79 {
80     return true;
81 }
82
83 bool SearchInputType::isSearchField() const
84 {
85     return true;
86 }
87
88 bool SearchInputType::needsContainer() const
89 {
90     return true;
91 }
92
93 void SearchInputType::createShadowSubtree()
94 {
95     ASSERT(!m_resultsButton);
96     ASSERT(!m_cancelButton);
97
98     TextFieldInputType::createShadowSubtree();
99     HTMLElement* container = containerElement();
100     HTMLElement* textWrapper = innerBlockElement();
101     ASSERT(container);
102     ASSERT(textWrapper);
103
104     RefPtr<SearchFieldResultsButtonElement> resultsButton = SearchFieldResultsButtonElement::create(element().document());
105     m_resultsButton = resultsButton.get();
106     container->insertBefore(m_resultsButton, textWrapper, IGNORE_EXCEPTION);
107
108     RefPtr<SearchFieldCancelButtonElement> cancelButton = SearchFieldCancelButtonElement::create(element().document());
109     m_cancelButton = cancelButton.get();
110     container->insertBefore(m_cancelButton, textWrapper->nextSibling(), IGNORE_EXCEPTION);
111 }
112
113 HTMLElement* SearchInputType::resultsButtonElement() const
114 {
115     return m_resultsButton;
116 }
117
118 HTMLElement* SearchInputType::cancelButtonElement() const
119 {
120     return m_cancelButton;
121 }
122
123 void SearchInputType::handleKeydownEvent(KeyboardEvent* event)
124 {
125     if (element().isDisabledOrReadOnly()) {
126         TextFieldInputType::handleKeydownEvent(event);
127         return;
128     }
129
130     const String& key = event->keyIdentifier();
131     if (key == "U+001B") {
132         Ref<HTMLInputElement> input(this->element());
133         input->setValueForUser("");
134         input->onSearch();
135         event->setDefaultHandled();
136         return;
137     }
138     TextFieldInputType::handleKeydownEvent(event);
139 }
140
141 void SearchInputType::destroyShadowSubtree()
142 {
143     TextFieldInputType::destroyShadowSubtree();
144     m_resultsButton = 0;
145     m_cancelButton = 0;
146 }
147
148 void SearchInputType::startSearchEventTimer()
149 {
150     ASSERT(element().renderer());
151     unsigned length = element().innerTextValue().length();
152
153     if (!length) {
154         stopSearchEventTimer();
155         element().onSearch();
156         return;
157     }
158
159     // After typing the first key, we wait 0.5 seconds.
160     // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
161     m_searchEventTimer.startOneShot(std::max(0.2, 0.6 - 0.1 * length));
162 }
163
164 void SearchInputType::stopSearchEventTimer()
165 {
166     m_searchEventTimer.stop();
167 }
168
169 void SearchInputType::searchEventTimerFired(Timer<SearchInputType>*)
170 {
171     element().onSearch();
172 }
173
174 bool SearchInputType::searchEventsShouldBeDispatched() const
175 {
176     return element().hasAttribute(incrementalAttr);
177 }
178
179 void SearchInputType::didSetValueByUserEdit(ValueChangeState state)
180 {
181     if (m_cancelButton)
182         toRenderSearchField(element().renderer())->updateCancelButtonVisibility();
183
184     // If the incremental attribute is set, then dispatch the search event
185     if (searchEventsShouldBeDispatched())
186         startSearchEventTimer();
187
188     TextFieldInputType::didSetValueByUserEdit(state);
189 }
190
191 bool SearchInputType::sizeShouldIncludeDecoration(int, int& preferredSize) const
192 {
193     preferredSize = element().size();
194     return true;
195 }
196
197 float SearchInputType::decorationWidth() const
198 {
199     float width = 0;
200     if (m_resultsButton)
201         width += m_resultsButton->computedStyle()->logicalWidth().value();
202     if (m_cancelButton)
203         width += m_cancelButton->computedStyle()->logicalWidth().value();
204     return width;
205 }
206
207 } // namespace WebCore