2 * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved.
3 * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4 * Copyright (C) 2010 Google Inc. All rights reserved.
5 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
25 #include "RenderSearchField.h"
27 #include "CSSFontSelector.h"
28 #include "CSSValueKeywords.h"
31 #include "FrameSelection.h"
32 #include "FrameView.h"
33 #include "HTMLInputElement.h"
34 #include "HTMLNames.h"
35 #include "HitTestResult.h"
36 #include "LocalizedStrings.h"
38 #include "PlatformKeyboardEvent.h"
39 #include "RenderLayer.h"
40 #include "RenderScrollbar.h"
41 #include "RenderTheme.h"
42 #include "RenderView.h"
43 #include "SearchPopupMenu.h"
45 #include "SimpleFontData.h"
46 #include "StyleResolver.h"
47 #include "TextControlInnerElements.h"
51 using namespace HTMLNames;
53 RenderSearchField::RenderSearchField(HTMLInputElement& element, PassRef<RenderStyle> style)
54 : RenderTextControlSingleLine(element, std::move(style))
55 , m_searchPopupIsVisible(false)
58 ASSERT(element.isSearchField());
61 RenderSearchField::~RenderSearchField()
64 m_searchPopup->popupMenu()->disconnectClient();
69 inline HTMLElement* RenderSearchField::resultsButtonElement() const
71 return inputElement().resultsButtonElement();
74 inline HTMLElement* RenderSearchField::cancelButtonElement() const
76 return inputElement().cancelButtonElement();
79 void RenderSearchField::addSearchResult()
81 if (inputElement().maxResults() <= 0)
84 String value = inputElement().value();
88 if (frame().page()->usesEphemeralSession())
91 int size = static_cast<int>(m_recentSearches.size());
92 for (int i = size - 1; i >= 0; --i) {
93 if (m_recentSearches[i] == value)
94 m_recentSearches.remove(i);
97 m_recentSearches.insert(0, value);
98 while (static_cast<int>(m_recentSearches.size()) > inputElement().maxResults())
99 m_recentSearches.removeLast();
101 const AtomicString& name = autosaveName();
103 m_searchPopup = document().page()->chrome().createSearchPopupMenu(this);
105 m_searchPopup->saveRecentSearches(name, m_recentSearches);
108 void RenderSearchField::showPopup()
110 if (m_searchPopupIsVisible)
114 m_searchPopup = document().page()->chrome().createSearchPopupMenu(this);
116 if (!m_searchPopup->enabled())
119 m_searchPopupIsVisible = true;
121 const AtomicString& name = autosaveName();
122 m_searchPopup->loadRecentSearches(name, m_recentSearches);
124 // Trim the recent searches list if the maximum size has changed since we last saved.
125 if (static_cast<int>(m_recentSearches.size()) > inputElement().maxResults()) {
127 m_recentSearches.removeLast();
128 } while (static_cast<int>(m_recentSearches.size()) > inputElement().maxResults());
130 m_searchPopup->saveRecentSearches(name, m_recentSearches);
133 m_searchPopup->popupMenu()->show(pixelSnappedIntRect(absoluteBoundingBoxRect()), &view().frameView(), -1);
136 void RenderSearchField::hidePopup()
139 m_searchPopup->popupMenu()->hide();
142 LayoutUnit RenderSearchField::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
144 HTMLElement* resultsButton = resultsButtonElement();
145 if (RenderBox* resultsRenderer = resultsButton ? resultsButton->renderBox() : 0) {
146 resultsRenderer->updateLogicalHeight();
147 nonContentHeight = std::max(nonContentHeight, resultsRenderer->borderAndPaddingLogicalHeight() + resultsRenderer->marginLogicalHeight());
148 lineHeight = std::max(lineHeight, resultsRenderer->logicalHeight());
150 HTMLElement* cancelButton = cancelButtonElement();
151 if (RenderBox* cancelRenderer = cancelButton ? cancelButton->renderBox() : 0) {
152 cancelRenderer->updateLogicalHeight();
153 nonContentHeight = std::max(nonContentHeight, cancelRenderer->borderAndPaddingLogicalHeight() + cancelRenderer->marginLogicalHeight());
154 lineHeight = std::max(lineHeight, cancelRenderer->logicalHeight());
157 return lineHeight + nonContentHeight;
160 void RenderSearchField::updateFromElement()
162 RenderTextControlSingleLine::updateFromElement();
164 if (cancelButtonElement())
165 updateCancelButtonVisibility();
167 if (m_searchPopupIsVisible)
168 m_searchPopup->popupMenu()->updateFromElement();
171 void RenderSearchField::updateCancelButtonVisibility() const
173 RenderElement* cancelButtonRenderer = cancelButtonElement()->renderer();
174 if (!cancelButtonRenderer)
177 const RenderStyle& curStyle = cancelButtonRenderer->style();
178 EVisibility buttonVisibility = visibilityForCancelButton();
179 if (curStyle.visibility() == buttonVisibility)
182 auto cancelButtonStyle = RenderStyle::clone(&curStyle);
183 cancelButtonStyle.get().setVisibility(buttonVisibility);
184 cancelButtonRenderer->setStyle(std::move(cancelButtonStyle));
187 EVisibility RenderSearchField::visibilityForCancelButton() const
189 return (style().visibility() == HIDDEN || inputElement().value().isEmpty()) ? HIDDEN : VISIBLE;
192 const AtomicString& RenderSearchField::autosaveName() const
194 return inputElement().getAttribute(autosaveAttr);
197 // PopupMenuClient methods
198 void RenderSearchField::valueChanged(unsigned listIndex, bool fireEvents)
200 ASSERT(static_cast<int>(listIndex) < listSize());
201 if (static_cast<int>(listIndex) == (listSize() - 1)) {
203 m_recentSearches.clear();
204 const AtomicString& name = autosaveName();
205 if (!name.isEmpty()) {
207 m_searchPopup = document().page()->chrome().createSearchPopupMenu(this);
208 m_searchPopup->saveRecentSearches(name, m_recentSearches);
212 inputElement().setValue(itemText(listIndex));
214 inputElement().onSearch();
215 inputElement().select();
219 String RenderSearchField::itemText(unsigned listIndex) const
222 int size = listSize();
225 return searchMenuNoRecentSearchesText();
228 return searchMenuRecentSearchesText();
230 if (itemIsSeparator(listIndex))
233 if (static_cast<int>(listIndex) == (size - 1))
234 return searchMenuClearRecentSearchesText();
236 return m_recentSearches[listIndex - 1];
239 String RenderSearchField::itemLabel(unsigned) const
244 String RenderSearchField::itemIcon(unsigned) const
249 bool RenderSearchField::itemIsEnabled(unsigned listIndex) const
251 if (!listIndex || itemIsSeparator(listIndex))
256 PopupMenuStyle RenderSearchField::itemStyle(unsigned) const
261 PopupMenuStyle RenderSearchField::menuStyle() const
263 return PopupMenuStyle(style().visitedDependentColor(CSSPropertyColor), style().visitedDependentColor(CSSPropertyBackgroundColor), style().font(), style().visibility() == VISIBLE,
264 style().display() == NONE, style().textIndent(), style().direction(), isOverride(style().unicodeBidi()), PopupMenuStyle::CustomBackgroundColor);
267 int RenderSearchField::clientInsetLeft() const
269 // Inset the menu by the radius of the cap on the left so that
270 // it only runs along the straight part of the bezel.
274 int RenderSearchField::clientInsetRight() const
276 // Inset the menu by the radius of the cap on the right so that
277 // it only runs along the straight part of the bezel (unless it needs
282 LayoutUnit RenderSearchField::clientPaddingLeft() const
284 LayoutUnit padding = paddingLeft();
285 if (RenderBox* box = innerBlockElement() ? innerBlockElement()->renderBox() : 0)
290 LayoutUnit RenderSearchField::clientPaddingRight() const
292 LayoutUnit padding = paddingRight();
293 if (RenderBox* containerBox = containerElement() ? containerElement()->renderBox() : 0) {
294 if (RenderBox* innerBlockBox = innerBlockElement() ? innerBlockElement()->renderBox() : 0)
295 padding += containerBox->width() - (innerBlockBox->x() + innerBlockBox->width());
300 int RenderSearchField::listSize() const
302 // If there are no recent searches, then our menu will have 1 "No recent searches" item.
303 if (!m_recentSearches.size())
305 // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item.
306 return m_recentSearches.size() + 3;
309 int RenderSearchField::selectedIndex() const
314 void RenderSearchField::popupDidHide()
316 m_searchPopupIsVisible = false;
319 bool RenderSearchField::itemIsSeparator(unsigned listIndex) const
321 // The separator will be the second to last item in our list.
322 return static_cast<int>(listIndex) == (listSize() - 2);
325 bool RenderSearchField::itemIsLabel(unsigned listIndex) const
330 bool RenderSearchField::itemIsSelected(unsigned) const
335 void RenderSearchField::setTextFromItem(unsigned listIndex)
337 inputElement().setValue(itemText(listIndex));
340 FontSelector* RenderSearchField::fontSelector() const
342 return document().ensureStyleResolver().fontSelector();
345 HostWindow* RenderSearchField::hostWindow() const
347 return view().frameView().hostWindow();
350 PassRefPtr<Scrollbar> RenderSearchField::createScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
352 RefPtr<Scrollbar> widget;
353 bool hasCustomScrollbarStyle = style().hasPseudoStyle(SCROLLBAR);
354 if (hasCustomScrollbarStyle)
355 widget = RenderScrollbar::createCustomScrollbar(scrollableArea, orientation, &inputElement());
357 widget = Scrollbar::createNativeScrollbar(scrollableArea, orientation, controlSize);
358 return widget.release();
361 LayoutUnit RenderSearchField::computeLogicalHeightLimit() const
363 return logicalHeight();
366 void RenderSearchField::centerContainerIfNeeded(RenderBox* containerRenderer) const
368 if (!containerRenderer)
371 if (containerRenderer->logicalHeight() <= contentLogicalHeight())
374 // A quirk for find-in-page box on Safari Windows.
375 // http://webkit.org/b/63157
376 centerRenderer(*containerRenderer);