AX: AXIsolatedTree::updateChildren sometimes fails to update isolated subtrees when...
[WebKit-https.git] / Source / WebCore / html / SearchInputType.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2014-2018 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 "ElementInlines.h"
36 #include "HTMLInputElement.h"
37 #include "HTMLNames.h"
38 #include "InputTypeNames.h"
39 #include "KeyboardEvent.h"
40 #include "RenderSearchField.h"
41 #include "ShadowPseudoIds.h"
42 #include "ShadowRoot.h"
43 #include "TextControlInnerElements.h"
44
45 namespace WebCore {
46
47 using namespace HTMLNames;
48
49 SearchInputType::SearchInputType(HTMLInputElement& element)
50     : BaseTextInputType(Type::Search, element)
51     , m_searchEventTimer(*this, &SearchInputType::searchEventTimerFired)
52 {
53     ASSERT(needsShadowSubtree());
54 }
55
56 void SearchInputType::addSearchResult()
57 {
58 #if !PLATFORM(IOS_FAMILY)
59     // Normally we've got the correct renderer by the time we get here. However when the input type changes
60     // we don't update the associated renderers until after the next tree update, so we could actually end up here
61     // with a mismatched renderer (e.g. through form submission).
62     ASSERT(element());
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(ShadowPseudoIds::webkitSearchResultsDecoration());
72     else if (maxResults < 0)
73         resultButton.setPseudo(ShadowPseudoIds::webkitSearchDecoration());
74     else
75         resultButton.setPseudo(ShadowPseudoIds::webkitSearchResultsButton());
76 }
77
78 void SearchInputType::attributeChanged(const QualifiedName& name)
79 {
80     if (name == resultsAttr) {
81         if (m_resultsButton) {
82             if (auto* element = this->element())
83                 updateResultButtonPseudoType(*m_resultsButton, element->maxResults());
84         }
85     }
86     BaseTextInputType::attributeChanged(name);
87 }
88
89 RenderPtr<RenderElement> SearchInputType::createInputRenderer(RenderStyle&& style)
90 {
91     ASSERT(element());
92     return createRenderer<RenderSearchField>(*element(), WTFMove(style));
93 }
94
95 const AtomString& SearchInputType::formControlType() const
96 {
97     return InputTypeNames::search();
98 }
99
100 bool SearchInputType::needsContainer() const
101 {
102     return true;
103 }
104
105 void SearchInputType::createShadowSubtree()
106 {
107     ASSERT(needsShadowSubtree());
108     ASSERT(!m_resultsButton);
109     ASSERT(!m_cancelButton);
110
111     TextFieldInputType::createShadowSubtree();
112     RefPtr<HTMLElement> container = containerElement();
113     RefPtr<HTMLElement> textWrapper = innerBlockElement();
114     ASSERT(container);
115     ASSERT(textWrapper);
116
117     ASSERT(element());
118     m_resultsButton = SearchFieldResultsButtonElement::create(element()->document());
119     updateResultButtonPseudoType(*m_resultsButton, element()->maxResults());
120     container->insertBefore(*m_resultsButton, textWrapper.get());
121
122     m_cancelButton = SearchFieldCancelButtonElement::create(element()->document());
123     container->insertBefore(*m_cancelButton, textWrapper->nextSibling());
124 }
125
126 HTMLElement* SearchInputType::resultsButtonElement() const
127 {
128     return m_resultsButton.get();
129 }
130
131 HTMLElement* SearchInputType::cancelButtonElement() const
132 {
133     return m_cancelButton.get();
134 }
135
136 auto SearchInputType::handleKeydownEvent(KeyboardEvent& event) -> ShouldCallBaseEventHandler
137 {
138     ASSERT(element());
139     if (element()->isDisabledOrReadOnly())
140         return TextFieldInputType::handleKeydownEvent(event);
141
142     const String& key = event.keyIdentifier();
143     if (key == "U+001B") {
144         Ref<HTMLInputElement> protectedInputElement(*element());
145         protectedInputElement->setValueForUser(emptyString());
146         protectedInputElement->onSearch();
147         event.setDefaultHandled();
148         return ShouldCallBaseEventHandler::Yes;
149     }
150     return TextFieldInputType::handleKeydownEvent(event);
151 }
152
153 void SearchInputType::destroyShadowSubtree()
154 {
155     TextFieldInputType::destroyShadowSubtree();
156     m_resultsButton = nullptr;
157     m_cancelButton = nullptr;
158 }
159
160 void SearchInputType::startSearchEventTimer()
161 {
162     ASSERT(element());
163     ASSERT(element()->renderer());
164     unsigned length = element()->innerTextValue().length();
165
166     if (!length) {
167         m_searchEventTimer.startOneShot(0_ms);
168         return;
169     }
170
171     // After typing the first key, we wait 0.5 seconds.
172     // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
173     m_searchEventTimer.startOneShot(std::max(200_ms, 600_ms - 100_ms * length));
174 }
175
176 void SearchInputType::stopSearchEventTimer()
177 {
178     m_searchEventTimer.stop();
179 }
180
181 void SearchInputType::searchEventTimerFired()
182 {
183     ASSERT(element());
184     element()->onSearch();
185 }
186
187 bool SearchInputType::searchEventsShouldBeDispatched() const
188 {
189     ASSERT(element());
190     return element()->hasAttributeWithoutSynchronization(incrementalAttr);
191 }
192
193 void SearchInputType::didSetValueByUserEdit()
194 {
195     ASSERT(element());
196     if (m_cancelButton && is<RenderSearchField>(element()->renderer()))
197         downcast<RenderSearchField>(*element()->renderer()).updateCancelButtonVisibility();
198     // If the incremental attribute is set, then dispatch the search event
199     if (searchEventsShouldBeDispatched())
200         startSearchEventTimer();
201
202     TextFieldInputType::didSetValueByUserEdit();
203 }
204
205 bool SearchInputType::sizeShouldIncludeDecoration(int, int& preferredSize) const
206 {
207     ASSERT(element());
208     preferredSize = element()->size();
209     return true;
210 }
211
212 float SearchInputType::decorationWidth() const
213 {
214     float width = 0;
215     if (m_resultsButton)
216         width += m_resultsButton->computedStyle()->logicalWidth().value();
217     if (m_cancelButton)
218         width += m_cancelButton->computedStyle()->logicalWidth().value();
219     return width;
220 }
221
222 void SearchInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior, TextControlSetValueSelection selection)
223 {
224     bool emptinessChanged = valueChanged && sanitizedValue.isEmpty() != element()->value().isEmpty();
225
226     BaseTextInputType::setValue(sanitizedValue, valueChanged, eventBehavior, selection);
227
228     if (m_cancelButton && emptinessChanged)
229         m_cancelButton->invalidateStyleInternal();
230 }
231
232 } // namespace WebCore