Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / html / SearchInputType.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2014 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "SearchInputType.h"
34
35 #include "HTMLInputElement.h"
36 #include "HTMLNames.h"
37 #include "InputTypeNames.h"
38 #include "KeyboardEvent.h"
39 #include "RenderSearchField.h"
40 #include "ShadowRoot.h"
41 #include "TextControlInnerElements.h"
42
43 namespace WebCore {
44
45 using namespace HTMLNames;
46
47 SearchInputType::SearchInputType(HTMLInputElement& element)
48     : BaseTextInputType(element)
49     , m_resultsButton(nullptr)
50     , m_cancelButton(nullptr)
51     , m_searchEventTimer(*this, &SearchInputType::searchEventTimerFired)
52 {
53 }
54
55 SearchInputType::~SearchInputType() = default;
56
57 void SearchInputType::addSearchResult()
58 {
59 #if !PLATFORM(IOS)
60     // Normally we've got the correct renderer by the time we get here. However when the input type changes
61     // we don't update the associated renderers until after the next tree update, so we could actually end up here
62     // with a mismatched renderer (e.g. through form submission).
63     if (is<RenderSearchField>(element().renderer()))
64         downcast<RenderSearchField>(*element().renderer()).addSearchResult();
65 #endif
66 }
67
68 static void updateResultButtonPseudoType(SearchFieldResultsButtonElement& resultButton, int maxResults)
69 {
70     if (!maxResults)
71         resultButton.setPseudo(AtomicString("-webkit-search-results-decoration", AtomicString::ConstructFromLiteral));
72     else if (maxResults < 0)
73         resultButton.setPseudo(AtomicString("-webkit-search-decoration", AtomicString::ConstructFromLiteral));
74     else if (maxResults > 0)
75         resultButton.setPseudo(AtomicString("-webkit-search-results-button", AtomicString::ConstructFromLiteral));
76 }
77
78 void SearchInputType::maxResultsAttributeChanged()
79 {
80     if (m_resultsButton)
81         updateResultButtonPseudoType(*m_resultsButton, element().maxResults());
82 }
83
84 RenderPtr<RenderElement> SearchInputType::createInputRenderer(RenderStyle&& style)
85 {
86     return createRenderer<RenderSearchField>(element(), WTFMove(style));
87 }
88
89 const AtomicString& SearchInputType::formControlType() const
90 {
91     return InputTypeNames::search();
92 }
93
94 bool SearchInputType::isSearchField() const
95 {
96     return true;
97 }
98
99 bool SearchInputType::needsContainer() const
100 {
101     return true;
102 }
103
104 void SearchInputType::createShadowSubtree()
105 {
106     ASSERT(!m_resultsButton);
107     ASSERT(!m_cancelButton);
108
109     TextFieldInputType::createShadowSubtree();
110     RefPtr<HTMLElement> container = containerElement();
111     RefPtr<HTMLElement> textWrapper = innerBlockElement();
112     ASSERT(container);
113     ASSERT(textWrapper);
114
115     m_resultsButton = SearchFieldResultsButtonElement::create(element().document());
116     updateResultButtonPseudoType(*m_resultsButton, element().maxResults());
117     container->insertBefore(*m_resultsButton, textWrapper.get());
118
119     m_cancelButton = SearchFieldCancelButtonElement::create(element().document());
120     container->insertBefore(*m_cancelButton, textWrapper->nextSibling());
121 }
122
123 HTMLElement* SearchInputType::resultsButtonElement() const
124 {
125     return m_resultsButton.get();
126 }
127
128 HTMLElement* SearchInputType::cancelButtonElement() const
129 {
130     return m_cancelButton.get();
131 }
132
133 void SearchInputType::handleKeydownEvent(KeyboardEvent& event)
134 {
135     if (element().isDisabledOrReadOnly()) {
136         TextFieldInputType::handleKeydownEvent(event);
137         return;
138     }
139
140     const String& key = event.keyIdentifier();
141     if (key == "U+001B") {
142         Ref<HTMLInputElement> input(this->element());
143         input->setValueForUser(emptyString());
144         input->onSearch();
145         event.setDefaultHandled();
146         return;
147     }
148     TextFieldInputType::handleKeydownEvent(event);
149 }
150
151 void SearchInputType::destroyShadowSubtree()
152 {
153     TextFieldInputType::destroyShadowSubtree();
154     m_resultsButton = nullptr;
155     m_cancelButton = nullptr;
156 }
157
158 void SearchInputType::startSearchEventTimer()
159 {
160     ASSERT(element().renderer());
161     unsigned length = element().innerTextValue().length();
162
163     if (!length) {
164         m_searchEventTimer.startOneShot(0_ms);
165         return;
166     }
167
168     // After typing the first key, we wait 0.5 seconds.
169     // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
170     m_searchEventTimer.startOneShot(std::max(200_ms, 600_ms - 100_ms * length));
171 }
172
173 void SearchInputType::stopSearchEventTimer()
174 {
175     m_searchEventTimer.stop();
176 }
177
178 void SearchInputType::searchEventTimerFired()
179 {
180     element().onSearch();
181 }
182
183 bool SearchInputType::searchEventsShouldBeDispatched() const
184 {
185     return element().hasAttributeWithoutSynchronization(incrementalAttr);
186 }
187
188 void SearchInputType::didSetValueByUserEdit()
189 {
190     if (m_cancelButton && is<RenderSearchField>(element().renderer()))
191         downcast<RenderSearchField>(*element().renderer()).updateCancelButtonVisibility();
192     // If the incremental attribute is set, then dispatch the search event
193     if (searchEventsShouldBeDispatched())
194         startSearchEventTimer();
195
196     TextFieldInputType::didSetValueByUserEdit();
197 }
198
199 bool SearchInputType::sizeShouldIncludeDecoration(int, int& preferredSize) const
200 {
201     preferredSize = element().size();
202     return true;
203 }
204
205 float SearchInputType::decorationWidth() const
206 {
207     float width = 0;
208     if (m_resultsButton)
209         width += m_resultsButton->computedStyle()->logicalWidth().value();
210     if (m_cancelButton)
211         width += m_cancelButton->computedStyle()->logicalWidth().value();
212     return width;
213 }
214
215 } // namespace WebCore