2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "SearchInputType.h"
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"
47 using namespace HTMLNames;
49 SearchInputType::SearchInputType(HTMLInputElement& element)
50 : BaseTextInputType(Type::Search, element)
51 , m_searchEventTimer(*this, &SearchInputType::searchEventTimerFired)
53 ASSERT(needsShadowSubtree());
56 void SearchInputType::addSearchResult()
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).
63 if (is<RenderSearchField>(element()->renderer()))
64 downcast<RenderSearchField>(*element()->renderer()).addSearchResult();
68 static void updateResultButtonPseudoType(SearchFieldResultsButtonElement& resultButton, int maxResults)
71 resultButton.setPseudo(ShadowPseudoIds::webkitSearchResultsDecoration());
72 else if (maxResults < 0)
73 resultButton.setPseudo(ShadowPseudoIds::webkitSearchDecoration());
75 resultButton.setPseudo(ShadowPseudoIds::webkitSearchResultsButton());
78 void SearchInputType::attributeChanged(const QualifiedName& name)
80 if (name == resultsAttr) {
81 if (m_resultsButton) {
82 if (auto* element = this->element())
83 updateResultButtonPseudoType(*m_resultsButton, element->maxResults());
86 BaseTextInputType::attributeChanged(name);
89 RenderPtr<RenderElement> SearchInputType::createInputRenderer(RenderStyle&& style)
92 return createRenderer<RenderSearchField>(*element(), WTFMove(style));
95 const AtomString& SearchInputType::formControlType() const
97 return InputTypeNames::search();
100 bool SearchInputType::needsContainer() const
105 void SearchInputType::createShadowSubtree()
107 ASSERT(needsShadowSubtree());
108 ASSERT(!m_resultsButton);
109 ASSERT(!m_cancelButton);
111 TextFieldInputType::createShadowSubtree();
112 RefPtr<HTMLElement> container = containerElement();
113 RefPtr<HTMLElement> textWrapper = innerBlockElement();
118 m_resultsButton = SearchFieldResultsButtonElement::create(element()->document());
119 updateResultButtonPseudoType(*m_resultsButton, element()->maxResults());
120 container->insertBefore(*m_resultsButton, textWrapper.get());
122 m_cancelButton = SearchFieldCancelButtonElement::create(element()->document());
123 container->insertBefore(*m_cancelButton, textWrapper->nextSibling());
126 HTMLElement* SearchInputType::resultsButtonElement() const
128 return m_resultsButton.get();
131 HTMLElement* SearchInputType::cancelButtonElement() const
133 return m_cancelButton.get();
136 auto SearchInputType::handleKeydownEvent(KeyboardEvent& event) -> ShouldCallBaseEventHandler
139 if (element()->isDisabledOrReadOnly())
140 return TextFieldInputType::handleKeydownEvent(event);
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;
150 return TextFieldInputType::handleKeydownEvent(event);
153 void SearchInputType::destroyShadowSubtree()
155 TextFieldInputType::destroyShadowSubtree();
156 m_resultsButton = nullptr;
157 m_cancelButton = nullptr;
160 void SearchInputType::startSearchEventTimer()
163 ASSERT(element()->renderer());
164 unsigned length = element()->innerTextValue().length();
167 m_searchEventTimer.startOneShot(0_ms);
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));
176 void SearchInputType::stopSearchEventTimer()
178 m_searchEventTimer.stop();
181 void SearchInputType::searchEventTimerFired()
184 element()->onSearch();
187 bool SearchInputType::searchEventsShouldBeDispatched() const
190 return element()->hasAttributeWithoutSynchronization(incrementalAttr);
193 void SearchInputType::didSetValueByUserEdit()
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();
202 TextFieldInputType::didSetValueByUserEdit();
205 bool SearchInputType::sizeShouldIncludeDecoration(int, int& preferredSize) const
208 preferredSize = element()->size();
212 float SearchInputType::decorationWidth() const
216 width += m_resultsButton->computedStyle()->logicalWidth().value();
218 width += m_cancelButton->computedStyle()->logicalWidth().value();
222 void SearchInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior, TextControlSetValueSelection selection)
224 bool emptinessChanged = valueChanged && sanitizedValue.isEmpty() != element()->value().isEmpty();
226 BaseTextInputType::setValue(sanitizedValue, valueChanged, eventBehavior, selection);
228 if (m_cancelButton && emptinessChanged)
229 m_cancelButton->invalidateStyleInternal();
232 } // namespace WebCore