Replace PassRef with Ref/Ref&& across the board.
[WebKit-https.git] / Source / WebCore / rendering / RenderSearchField.cpp
1 /**
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).
6  *
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.
11  *
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.
16  *
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.
21  *
22  */
23
24 #include "config.h"
25 #include "RenderSearchField.h"
26
27 #include "CSSFontSelector.h"
28 #include "CSSValueKeywords.h"
29 #include "Chrome.h"
30 #include "Frame.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"
37 #include "Page.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"
44 #include "Settings.h"
45 #include "SimpleFontData.h"
46 #include "StyleResolver.h"
47 #include "TextControlInnerElements.h"
48
49 namespace WebCore {
50
51 using namespace HTMLNames;
52
53 RenderSearchField::RenderSearchField(HTMLInputElement& element, Ref<RenderStyle>&& style)
54     : RenderTextControlSingleLine(element, WTF::move(style))
55     , m_searchPopupIsVisible(false)
56     , m_searchPopup(0)
57 {
58     ASSERT(element.isSearchField());
59 }
60
61 RenderSearchField::~RenderSearchField()
62 {
63     if (m_searchPopup) {
64         m_searchPopup->popupMenu()->disconnectClient();
65         m_searchPopup = 0;
66     }
67 }
68
69 inline HTMLElement* RenderSearchField::resultsButtonElement() const
70 {
71     return inputElement().resultsButtonElement();
72 }
73
74 inline HTMLElement* RenderSearchField::cancelButtonElement() const
75 {
76     return inputElement().cancelButtonElement();
77 }
78
79 void RenderSearchField::addSearchResult()
80 {
81     if (inputElement().maxResults() <= 0)
82         return;
83
84     String value = inputElement().value();
85     if (value.isEmpty())
86         return;
87
88     if (frame().page()->usesEphemeralSession())
89         return;
90
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);
95     }
96
97     m_recentSearches.insert(0, value);
98     while (static_cast<int>(m_recentSearches.size()) > inputElement().maxResults())
99         m_recentSearches.removeLast();
100
101     const AtomicString& name = autosaveName();
102     if (!m_searchPopup)
103         m_searchPopup = document().page()->chrome().createSearchPopupMenu(this);
104
105     m_searchPopup->saveRecentSearches(name, m_recentSearches);
106 }
107
108 void RenderSearchField::showPopup()
109 {
110     if (m_searchPopupIsVisible)
111         return;
112
113     if (!m_searchPopup)
114         m_searchPopup = document().page()->chrome().createSearchPopupMenu(this);
115
116     if (!m_searchPopup->enabled())
117         return;
118
119     m_searchPopupIsVisible = true;
120
121     const AtomicString& name = autosaveName();
122     m_searchPopup->loadRecentSearches(name, m_recentSearches);
123
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()) {
126         do {
127             m_recentSearches.removeLast();
128         } while (static_cast<int>(m_recentSearches.size()) > inputElement().maxResults());
129
130         m_searchPopup->saveRecentSearches(name, m_recentSearches);
131     }
132
133     m_searchPopup->popupMenu()->show(snappedIntRect(absoluteBoundingBoxRect()), &view().frameView(), -1);
134 }
135
136 void RenderSearchField::hidePopup()
137 {
138     if (m_searchPopup)
139         m_searchPopup->popupMenu()->hide();
140 }
141
142 LayoutUnit RenderSearchField::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
143 {
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());
149     }
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());
155     }
156
157     return lineHeight + nonContentHeight;
158 }
159
160 void RenderSearchField::updateFromElement()
161 {
162     RenderTextControlSingleLine::updateFromElement();
163
164     if (cancelButtonElement())
165         updateCancelButtonVisibility();
166
167     if (m_searchPopupIsVisible)
168         m_searchPopup->popupMenu()->updateFromElement();
169 }
170
171 void RenderSearchField::updateCancelButtonVisibility() const
172 {
173     RenderElement* cancelButtonRenderer = cancelButtonElement()->renderer();
174     if (!cancelButtonRenderer)
175         return;
176
177     const RenderStyle& curStyle = cancelButtonRenderer->style();
178     EVisibility buttonVisibility = visibilityForCancelButton();
179     if (curStyle.visibility() == buttonVisibility)
180         return;
181
182     auto cancelButtonStyle = RenderStyle::clone(&curStyle);
183     cancelButtonStyle.get().setVisibility(buttonVisibility);
184     cancelButtonRenderer->setStyle(WTF::move(cancelButtonStyle));
185 }
186
187 EVisibility RenderSearchField::visibilityForCancelButton() const
188 {
189     return (style().visibility() == HIDDEN || inputElement().value().isEmpty()) ? HIDDEN : VISIBLE;
190 }
191
192 const AtomicString& RenderSearchField::autosaveName() const
193 {
194     return inputElement().fastGetAttribute(autosaveAttr);
195 }
196
197 // PopupMenuClient methods
198 void RenderSearchField::valueChanged(unsigned listIndex, bool fireEvents)
199 {
200     ASSERT(static_cast<int>(listIndex) < listSize());
201     if (static_cast<int>(listIndex) == (listSize() - 1)) {
202         if (fireEvents) {
203             m_recentSearches.clear();
204             const AtomicString& name = autosaveName();
205             if (!name.isEmpty()) {
206                 if (!m_searchPopup)
207                     m_searchPopup = document().page()->chrome().createSearchPopupMenu(this);
208                 m_searchPopup->saveRecentSearches(name, m_recentSearches);
209             }
210         }
211     } else {
212         inputElement().setValue(itemText(listIndex));
213         if (fireEvents)
214             inputElement().onSearch();
215         inputElement().select();
216     }
217 }
218
219 String RenderSearchField::itemText(unsigned listIndex) const
220 {
221 #if !PLATFORM(IOS)
222     int size = listSize();
223     if (size == 1) {
224         ASSERT(!listIndex);
225         return searchMenuNoRecentSearchesText();
226     }
227     if (!listIndex)
228         return searchMenuRecentSearchesText();
229 #endif
230     if (itemIsSeparator(listIndex))
231         return String();
232 #if !PLATFORM(IOS)
233     if (static_cast<int>(listIndex) == (size - 1))
234         return searchMenuClearRecentSearchesText();
235 #endif
236     return m_recentSearches[listIndex - 1];
237 }
238
239 String RenderSearchField::itemLabel(unsigned) const
240 {
241     return String();
242 }
243
244 String RenderSearchField::itemIcon(unsigned) const
245 {
246     return String();
247 }
248
249 bool RenderSearchField::itemIsEnabled(unsigned listIndex) const
250 {
251      if (!listIndex || itemIsSeparator(listIndex))
252         return false;
253     return true;
254 }
255
256 PopupMenuStyle RenderSearchField::itemStyle(unsigned) const
257 {
258     return menuStyle();
259 }
260
261 PopupMenuStyle RenderSearchField::menuStyle() const
262 {
263     return PopupMenuStyle(style().visitedDependentColor(CSSPropertyColor), style().visitedDependentColor(CSSPropertyBackgroundColor), style().font(), style().visibility() == VISIBLE,
264         style().display() == NONE, true, style().textIndent(), style().direction(), isOverride(style().unicodeBidi()), PopupMenuStyle::CustomBackgroundColor);
265 }
266
267 int RenderSearchField::clientInsetLeft() const
268 {
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.
271     return height() / 2;
272 }
273
274 int RenderSearchField::clientInsetRight() const
275 {
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
278     // to be wider).
279     return height() / 2;
280 }
281
282 LayoutUnit RenderSearchField::clientPaddingLeft() const
283 {
284     LayoutUnit padding = paddingLeft();
285     if (RenderBox* box = innerBlockElement() ? innerBlockElement()->renderBox() : 0)
286         padding += box->x();
287     return padding;
288 }
289
290 LayoutUnit RenderSearchField::clientPaddingRight() const
291 {
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());
296     }
297     return padding;
298 }
299
300 int RenderSearchField::listSize() const
301 {
302     // If there are no recent searches, then our menu will have 1 "No recent searches" item.
303     if (!m_recentSearches.size())
304         return 1;
305     // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item.
306     return m_recentSearches.size() + 3;
307 }
308
309 int RenderSearchField::selectedIndex() const
310 {
311     return -1;
312 }
313
314 void RenderSearchField::popupDidHide()
315 {
316     m_searchPopupIsVisible = false;
317 }
318
319 bool RenderSearchField::itemIsSeparator(unsigned listIndex) const
320 {
321     // The separator will be the second to last item in our list.
322     return static_cast<int>(listIndex) == (listSize() - 2);
323 }
324
325 bool RenderSearchField::itemIsLabel(unsigned listIndex) const
326 {
327     return !listIndex;
328 }
329
330 bool RenderSearchField::itemIsSelected(unsigned) const
331 {
332     return false;
333 }
334
335 void RenderSearchField::setTextFromItem(unsigned listIndex)
336 {
337     inputElement().setValue(itemText(listIndex));
338 }
339
340 FontSelector* RenderSearchField::fontSelector() const
341 {
342     return document().ensureStyleResolver().fontSelector();
343 }
344
345 HostWindow* RenderSearchField::hostWindow() const
346 {
347     return view().frameView().hostWindow();
348 }
349
350 PassRefPtr<Scrollbar> RenderSearchField::createScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
351 {
352     RefPtr<Scrollbar> widget;
353     bool hasCustomScrollbarStyle = style().hasPseudoStyle(SCROLLBAR);
354     if (hasCustomScrollbarStyle)
355         widget = RenderScrollbar::createCustomScrollbar(scrollableArea, orientation, &inputElement());
356     else
357         widget = Scrollbar::createNativeScrollbar(scrollableArea, orientation, controlSize);
358     return widget.release();
359 }
360
361 LayoutUnit RenderSearchField::computeLogicalHeightLimit() const
362 {
363     return logicalHeight();
364 }
365
366 void RenderSearchField::centerContainerIfNeeded(RenderBox* containerRenderer) const
367 {
368     if (!containerRenderer)
369         return;
370
371     if (containerRenderer->logicalHeight() <= contentLogicalHeight())
372         return;
373
374     // A quirk for find-in-page box on Safari Windows.
375     // http://webkit.org/b/63157
376     centerRenderer(*containerRenderer);
377 }
378
379 }