• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3  *           (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  */
21 
22 #include "config.h"
23 #include "RenderTextControlSingleLine.h"
24 
25 #include "CSSStyleSelector.h"
26 #include "Event.h"
27 #include "EventNames.h"
28 #include "Frame.h"
29 #include "FrameView.h"
30 #include "HitTestResult.h"
31 #include "HTMLInputElement.h"
32 #include "HTMLNames.h"
33 #include "InputElement.h"
34 #include "LocalizedStrings.h"
35 #include "MouseEvent.h"
36 #include "PlatformKeyboardEvent.h"
37 #include "RenderScrollbar.h"
38 #include "RenderTheme.h"
39 #include "SearchPopupMenu.h"
40 #include "SelectionController.h"
41 #include "Settings.h"
42 #include "TextControlInnerElements.h"
43 
44 using namespace std;
45 
46 namespace WebCore {
47 
48 using namespace HTMLNames;
49 
RenderTextControlSingleLine(Node * node)50 RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node)
51     : RenderTextControl(node)
52     , m_placeholderVisible(false)
53     , m_searchPopupIsVisible(false)
54     , m_shouldDrawCapsLockIndicator(false)
55     , m_searchEventTimer(this, &RenderTextControlSingleLine::searchEventTimerFired)
56     , m_searchPopup(0)
57 {
58 }
59 
~RenderTextControlSingleLine()60 RenderTextControlSingleLine::~RenderTextControlSingleLine()
61 {
62     if (m_searchPopup) {
63         m_searchPopup->disconnectClient();
64         m_searchPopup = 0;
65     }
66 
67     if (m_innerBlock)
68         m_innerBlock->detach();
69 }
70 
placeholderShouldBeVisible() const71 bool RenderTextControlSingleLine::placeholderShouldBeVisible() const
72 {
73     return inputElement()->placeholderShouldBeVisible();
74 }
75 
updatePlaceholderVisibility()76 void RenderTextControlSingleLine::updatePlaceholderVisibility()
77 {
78     RenderStyle* parentStyle = m_innerBlock ? m_innerBlock->renderer()->style() : style();
79 
80     RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(parentStyle);
81     HTMLElement* innerText = innerTextElement();
82     innerText->renderer()->setStyle(textBlockStyle);
83 
84     for (Node* n = innerText->firstChild(); n; n = n->traverseNextNode(innerText)) {
85         if (RenderObject* renderer = n->renderer())
86             renderer->setStyle(textBlockStyle);
87     }
88 
89     updateFromElement();
90 }
91 
addSearchResult()92 void RenderTextControlSingleLine::addSearchResult()
93 {
94     ASSERT(node()->isHTMLElement());
95     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
96     if (input->maxResults() <= 0)
97         return;
98 
99     String value = input->value();
100     if (value.isEmpty())
101         return;
102 
103     Settings* settings = document()->settings();
104     if (!settings || settings->privateBrowsingEnabled())
105         return;
106 
107     int size = static_cast<int>(m_recentSearches.size());
108     for (int i = size - 1; i >= 0; --i) {
109         if (m_recentSearches[i] == value)
110             m_recentSearches.remove(i);
111     }
112 
113     m_recentSearches.insert(0, value);
114     while (static_cast<int>(m_recentSearches.size()) > input->maxResults())
115         m_recentSearches.removeLast();
116 
117     const AtomicString& name = autosaveName();
118     if (!m_searchPopup)
119         m_searchPopup = SearchPopupMenu::create(this);
120 
121     m_searchPopup->saveRecentSearches(name, m_recentSearches);
122 }
123 
stopSearchEventTimer()124 void RenderTextControlSingleLine::stopSearchEventTimer()
125 {
126     ASSERT(node()->isHTMLElement());
127     m_searchEventTimer.stop();
128 }
129 
showPopup()130 void RenderTextControlSingleLine::showPopup()
131 {
132     ASSERT(node()->isHTMLElement());
133     if (m_searchPopupIsVisible)
134         return;
135 
136     if (!m_searchPopup)
137         m_searchPopup = SearchPopupMenu::create(this);
138 
139     if (!m_searchPopup->enabled())
140         return;
141 
142     m_searchPopupIsVisible = true;
143 
144     const AtomicString& name = autosaveName();
145     m_searchPopup->loadRecentSearches(name, m_recentSearches);
146 
147     // Trim the recent searches list if the maximum size has changed since we last saved.
148     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
149     if (static_cast<int>(m_recentSearches.size()) > input->maxResults()) {
150         do {
151             m_recentSearches.removeLast();
152         } while (static_cast<int>(m_recentSearches.size()) > input->maxResults());
153 
154         m_searchPopup->saveRecentSearches(name, m_recentSearches);
155     }
156 
157     m_searchPopup->show(absoluteBoundingBoxRect(true), document()->view(), -1);
158 }
159 
hidePopup()160 void RenderTextControlSingleLine::hidePopup()
161 {
162     ASSERT(node()->isHTMLElement());
163     if (m_searchPopup)
164         m_searchPopup->hide();
165 
166     m_searchPopupIsVisible = false;
167 }
168 
subtreeHasChanged()169 void RenderTextControlSingleLine::subtreeHasChanged()
170 {
171     bool wasEdited = isEdited();
172     RenderTextControl::subtreeHasChanged();
173 
174     InputElement* input = inputElement();
175     input->setValueFromRenderer(input->constrainValue(text()));
176 
177     if (RenderObject* cancelButtonRenderer = m_cancelButton ? m_cancelButton->renderer() : 0)
178         updateCancelButtonVisibility(cancelButtonRenderer->style());
179 
180     // If the incremental attribute is set, then dispatch the search event
181     if (input->searchEventsShouldBeDispatched())
182         startSearchEventTimer();
183 
184     if (!wasEdited && node()->focused()) {
185         if (Frame* frame = document()->frame())
186             frame->textFieldDidBeginEditing(static_cast<Element*>(node()));
187     }
188 
189     if (node()->focused()) {
190         if (Frame* frame = document()->frame())
191             frame->textDidChangeInTextField(static_cast<Element*>(node()));
192     }
193 }
194 
paint(PaintInfo & paintInfo,int tx,int ty)195 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, int tx, int ty)
196 {
197     RenderTextControl::paint(paintInfo, tx, ty);
198 
199     if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
200         IntRect contentsRect = contentBoxRect();
201 
202         // Convert the rect into the coords used for painting the content
203         contentsRect.move(tx + x(), ty + y());
204         theme()->paintCapsLockIndicator(this, paintInfo, contentsRect);
205     }
206 }
207 
layout()208 void RenderTextControlSingleLine::layout()
209 {
210     int oldHeight = height();
211     calcHeight();
212 
213 #ifdef ANDROID_LAYOUT
214     int oldVisibleWidth = m_visibleWidth;
215 #endif
216 
217     int oldWidth = width();
218     calcWidth();
219 
220     bool relayoutChildren = oldHeight != height() || oldWidth != width();
221 
222 #ifdef ANDROID_LAYOUT
223     if (oldVisibleWidth != m_visibleWidth
224             && document()->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) {
225         relayoutChildren = true;
226     }
227 #endif
228 
229     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
230     RenderBox* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderBox() : 0;
231 
232     // Set the text block height
233     int desiredHeight = textBlockHeight();
234     int currentHeight = innerTextRenderer->height();
235 
236     if (m_innerBlock || currentHeight > height()) {
237         if (desiredHeight != currentHeight)
238             relayoutChildren = true;
239         innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed));
240     }
241 
242     if (m_innerBlock) {
243         ASSERT(innerBlockRenderer);
244         if (desiredHeight != innerBlockRenderer->height())
245             relayoutChildren = true;
246         innerBlockRenderer->style()->setHeight(Length(desiredHeight, Fixed));
247     }
248 
249     // Set the text block width
250     int desiredWidth = textBlockWidth();
251     if (desiredWidth != innerTextRenderer->width())
252         relayoutChildren = true;
253     innerTextRenderer->style()->setWidth(Length(desiredWidth, Fixed));
254 
255     if (m_innerBlock) {
256         int innerBlockWidth = width() - paddingLeft() - paddingRight() - borderLeft() - borderRight();
257         if (innerBlockWidth != innerBlockRenderer->width())
258             relayoutChildren = true;
259         innerBlockRenderer->style()->setWidth(Length(innerBlockWidth, Fixed));
260     }
261 
262     RenderBlock::layoutBlock(relayoutChildren);
263 
264     // For text fields, center the inner text vertically
265     // Don't do this for search fields, since we don't honor height for them
266     if (!m_innerBlock) {
267         currentHeight = innerTextRenderer->height();
268         if (currentHeight < height())
269             innerTextRenderer->setLocation(innerTextRenderer->x(), (height() - currentHeight) / 2);
270     }
271 }
272 
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int xPos,int yPos,int tx,int ty,HitTestAction hitTestAction)273 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction hitTestAction)
274 {
275     // If we're within the text control, we want to act as if we've hit the inner text block element, in case the point
276     // was on the control but not on the inner element (see Radar 4617841).
277 
278     // In a search field, we want to act as if we've hit the results block if we're to the left of the inner text block,
279     // and act as if we've hit the close block if we're to the right of the inner text block.
280 
281     if (!RenderTextControl::nodeAtPoint(request, result, xPos, yPos, tx, ty, hitTestAction))
282         return false;
283 
284     if (result.innerNode() != element() && result.innerNode() != m_innerBlock.get())
285         return false;
286 
287     hitInnerTextBlock(result, xPos, yPos, tx, ty);
288 
289     if (!m_innerBlock)
290         return true;
291 
292     Node* innerNode = 0;
293     RenderBox* innerBlockRenderer = m_innerBlock->renderBox();
294     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
295 
296     IntPoint localPoint = result.localPoint();
297     localPoint.move(-innerBlockRenderer->x(), -innerBlockRenderer->y());
298 
299     int textLeft = tx + x() + innerBlockRenderer->x() + innerTextRenderer->x();
300     if (m_resultsButton && m_resultsButton->renderer() && xPos < textLeft)
301         innerNode = m_resultsButton.get();
302 
303     if (!innerNode) {
304         int textRight = textLeft + innerTextRenderer->width();
305         if (m_cancelButton && m_cancelButton->renderer() && xPos > textRight)
306             innerNode = m_cancelButton.get();
307     }
308 
309     if (innerNode) {
310         result.setInnerNode(innerNode);
311         localPoint.move(-innerNode->renderBox()->x(), -innerNode->renderBox()->y());
312     }
313 
314     result.setLocalPoint(localPoint);
315     return true;
316 }
317 
forwardEvent(Event * event)318 void RenderTextControlSingleLine::forwardEvent(Event* event)
319 {
320     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
321 
322     if (event->type() == eventNames().blurEvent) {
323         if (innerTextRenderer) {
324             if (RenderLayer* innerLayer = innerTextRenderer->layer())
325                 innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0);
326         }
327 
328         capsLockStateMayHaveChanged();
329     } else if (event->type() == eventNames().focusEvent)
330         capsLockStateMayHaveChanged();
331 
332     if (!event->isMouseEvent()) {
333         RenderTextControl::forwardEvent(event);
334         return;
335     }
336 
337     FloatPoint localPoint = innerTextRenderer->absoluteToLocal(FloatPoint(static_cast<MouseEvent*>(event)->pageX(), static_cast<MouseEvent*>(event)->pageY()), false, true);
338     if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBoxRect().x())
339         m_resultsButton->defaultEventHandler(event);
340     else if (m_cancelButton && localPoint.x() > innerTextRenderer->borderBoxRect().right())
341         m_cancelButton->defaultEventHandler(event);
342     else
343         RenderTextControl::forwardEvent(event);
344 }
345 
styleDidChange(RenderStyle::Diff diff,const RenderStyle * oldStyle)346 void RenderTextControlSingleLine::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle)
347 {
348     RenderTextControl::styleDidChange(diff, oldStyle);
349 
350     if (RenderObject* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderer() : 0) {
351         // We may have set the width and the height in the old style in layout().
352         // Reset them now to avoid getting a spurious layout hint.
353         innerBlockRenderer->style()->setHeight(Length());
354         innerBlockRenderer->style()->setWidth(Length());
355         innerBlockRenderer->setStyle(createInnerBlockStyle(style()));
356     }
357 
358     if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0)
359         resultsRenderer->setStyle(createResultsButtonStyle(style()));
360 
361     if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0)
362         cancelRenderer->setStyle(createCancelButtonStyle(style()));
363 }
364 
capsLockStateMayHaveChanged()365 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
366 {
367     if (!node() || !document())
368         return;
369 
370     // Only draw the caps lock indicator if these things are true:
371     // 1) The field is a password field
372     // 2) The frame is active
373     // 3) The element is focused
374     // 4) The caps lock is on
375     bool shouldDrawCapsLockIndicator = false;
376 
377     if (Frame* frame = document()->frame())
378         shouldDrawCapsLockIndicator = inputElement()->isPasswordField()
379                                       && frame->selection()->isFocusedAndActive()
380                                       && document()->focusedNode() == node()
381                                       && PlatformKeyboardEvent::currentCapsLockState();
382 
383     if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
384         m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
385         repaint();
386     }
387 }
388 
textBlockWidth() const389 int RenderTextControlSingleLine::textBlockWidth() const
390 {
391     int width = RenderTextControl::textBlockWidth();
392 
393     if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) {
394         resultsRenderer->calcWidth();
395         width -= resultsRenderer->width() + resultsRenderer->marginLeft() + resultsRenderer->marginRight();
396     }
397 
398     if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) {
399         cancelRenderer->calcWidth();
400         width -= cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->marginRight();
401     }
402 
403     return width;
404 }
405 
preferredContentWidth(float charWidth) const406 int RenderTextControlSingleLine::preferredContentWidth(float charWidth) const
407 {
408     int factor = inputElement()->size();
409     if (factor <= 0)
410         factor = 20;
411 
412     int result = static_cast<int>(ceilf(charWidth * factor));
413 
414     if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0)
415         result += resultsRenderer->borderLeft() + resultsRenderer->borderRight() +
416                   resultsRenderer->paddingLeft() + resultsRenderer->paddingRight();
417 
418     if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0)
419         result += cancelRenderer->borderLeft() + cancelRenderer->borderRight() +
420                   cancelRenderer->paddingLeft() + cancelRenderer->paddingRight();
421 
422     return result;
423 }
424 
adjustControlHeightBasedOnLineHeight(int lineHeight)425 void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineHeight)
426 {
427     if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) {
428         static_cast<RenderBlock*>(resultsRenderer)->calcHeight();
429         setHeight(max(height(),
430                   resultsRenderer->borderTop() + resultsRenderer->borderBottom() +
431                   resultsRenderer->paddingTop() + resultsRenderer->paddingBottom() +
432                   resultsRenderer->marginTop() + resultsRenderer->marginBottom()));
433         lineHeight = max(lineHeight, resultsRenderer->height());
434     }
435 
436     if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) {
437         static_cast<RenderBlock*>(cancelRenderer)->calcHeight();
438         setHeight(max(height(),
439                   cancelRenderer->borderTop() + cancelRenderer->borderBottom() +
440                   cancelRenderer->paddingTop() + cancelRenderer->paddingBottom() +
441                   cancelRenderer->marginTop() + cancelRenderer->marginBottom()));
442         lineHeight = max(lineHeight, cancelRenderer->height());
443     }
444 
445     setHeight(height() + lineHeight);
446 }
447 
createSubtreeIfNeeded()448 void RenderTextControlSingleLine::createSubtreeIfNeeded()
449 {
450     if (!inputElement()->isSearchField()) {
451         RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
452         return;
453     }
454 
455     if (!m_innerBlock) {
456         // Create the inner block element
457         m_innerBlock = new TextControlInnerElement(document(), node());
458         m_innerBlock->attachInnerElement(node(), createInnerBlockStyle(style()), renderArena());
459     }
460 
461     if (!m_resultsButton) {
462         // Create the search results button element
463         m_resultsButton = new SearchFieldResultsButtonElement(document());
464         m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena());
465     }
466 
467     // Create innerText element before adding the cancel button
468     RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
469 
470     if (!m_cancelButton) {
471         // Create the cancel button element
472         m_cancelButton = new SearchFieldCancelButtonElement(document());
473         m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena());
474     }
475 }
476 
updateFromElement()477 void RenderTextControlSingleLine::updateFromElement()
478 {
479     createSubtreeIfNeeded();
480     RenderTextControl::updateFromElement();
481 
482     bool placeholderVisibilityShouldChange = m_placeholderVisible != placeholderShouldBeVisible();
483     m_placeholderVisible = placeholderShouldBeVisible();
484 
485     if (RenderObject* cancelButtonRenderer = m_cancelButton ? m_cancelButton->renderer() : 0)
486         updateCancelButtonVisibility(cancelButtonRenderer->style());
487 
488     if (m_placeholderVisible) {
489         ExceptionCode ec = 0;
490         innerTextElement()->setInnerText(inputElement()->placeholderValue(), ec);
491         ASSERT(!ec);
492     } else if (!formControlElement()->valueMatchesRenderer() || placeholderVisibilityShouldChange)
493         setInnerTextValue(inputElement()->value());
494 
495     if (m_searchPopupIsVisible)
496         m_searchPopup->updateFromElement();
497 }
498 
cacheSelection(int start,int end)499 void RenderTextControlSingleLine::cacheSelection(int start, int end)
500 {
501     inputElement()->cacheSelection(start, end);
502 }
503 
createInnerTextStyle(const RenderStyle * startStyle) const504 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
505 {
506     RefPtr<RenderStyle> textBlockStyle;
507     if (placeholderShouldBeVisible()) {
508         RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::INPUT_PLACEHOLDER);
509         textBlockStyle = RenderStyle::clone(pseudoStyle);
510     } else {
511         textBlockStyle = RenderStyle::create();
512         textBlockStyle->inheritFrom(startStyle);
513     }
514 
515     adjustInnerTextStyle(startStyle, textBlockStyle.get());
516 
517     textBlockStyle->setWhiteSpace(PRE);
518     textBlockStyle->setWordWrap(NormalWordWrap);
519     textBlockStyle->setOverflowX(OHIDDEN);
520     textBlockStyle->setOverflowY(OHIDDEN);
521 
522     // Do not allow line-height to be smaller than our default.
523     if (textBlockStyle->font().lineSpacing() > lineHeight(true, true))
524         textBlockStyle->setLineHeight(Length(-100.0f, Percent));
525 
526     textBlockStyle->setDisplay(m_innerBlock ? INLINE_BLOCK : BLOCK);
527 
528     // We're adding one extra pixel of padding to match WinIE.
529     textBlockStyle->setPaddingLeft(Length(1, Fixed));
530     textBlockStyle->setPaddingRight(Length(1, Fixed));
531 
532     // When the placeholder is going to be displayed, temporarily override the text security to be "none".
533     // After this, updateFromElement will immediately update the text displayed.
534     // When the placeholder is no longer visible, updatePlaceholderVisiblity will reset the style,
535     // and the text security mode will be set back to the computed value correctly.
536     if (placeholderShouldBeVisible())
537         textBlockStyle->setTextSecurity(TSNONE);
538 
539     return textBlockStyle.release();
540 }
541 
createInnerBlockStyle(const RenderStyle * startStyle) const542 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const
543 {
544     ASSERT(node()->isHTMLElement());
545 
546     RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create();
547     innerBlockStyle->inheritFrom(startStyle);
548 
549     innerBlockStyle->setDisplay(BLOCK);
550     innerBlockStyle->setDirection(LTR);
551 
552     // We don't want the shadow dom to be editable, so we set this block to read-only in case the input itself is editable.
553     innerBlockStyle->setUserModify(READ_ONLY);
554 
555     return innerBlockStyle.release();
556 }
557 
createResultsButtonStyle(const RenderStyle * startStyle) const558 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createResultsButtonStyle(const RenderStyle* startStyle) const
559 {
560     ASSERT(node()->isHTMLElement());
561     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
562 
563     RefPtr<RenderStyle> resultsBlockStyle;
564     if (input->maxResults() < 0)
565         resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_DECORATION);
566     else if (!input->maxResults())
567         resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_RESULTS_DECORATION);
568     else
569         resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_RESULTS_BUTTON);
570 
571     if (!resultsBlockStyle)
572         resultsBlockStyle = RenderStyle::create();
573 
574     if (startStyle)
575         resultsBlockStyle->inheritFrom(startStyle);
576 
577     return resultsBlockStyle.release();
578 }
579 
createCancelButtonStyle(const RenderStyle * startStyle) const580 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createCancelButtonStyle(const RenderStyle* startStyle) const
581 {
582     ASSERT(node()->isHTMLElement());
583     RefPtr<RenderStyle> cancelBlockStyle;
584 
585     if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(RenderStyle::SEARCH_CANCEL_BUTTON))
586         // We may be sharing style with another search field, but we must not share the cancel button style.
587         cancelBlockStyle = RenderStyle::clone(pseudoStyle.get());
588     else
589         cancelBlockStyle = RenderStyle::create();
590 
591     if (startStyle)
592         cancelBlockStyle->inheritFrom(startStyle);
593 
594     updateCancelButtonVisibility(cancelBlockStyle.get());
595     return cancelBlockStyle.release();
596 }
597 
updateCancelButtonVisibility(RenderStyle * style) const598 void RenderTextControlSingleLine::updateCancelButtonVisibility(RenderStyle* style) const
599 {
600     ASSERT(node()->isHTMLElement());
601     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
602     style->setVisibility(input->value().isEmpty() ? HIDDEN : VISIBLE);
603 }
604 
autosaveName() const605 const AtomicString& RenderTextControlSingleLine::autosaveName() const
606 {
607     return static_cast<Element*>(node())->getAttribute(autosaveAttr);
608 }
609 
startSearchEventTimer()610 void RenderTextControlSingleLine::startSearchEventTimer()
611 {
612     ASSERT(node()->isHTMLElement());
613     unsigned length = text().length();
614 
615     // If there's no text, fire the event right away.
616     if (!length) {
617         stopSearchEventTimer();
618         static_cast<HTMLInputElement*>(node())->onSearch();
619         return;
620     }
621 
622     // After typing the first key, we wait 0.5 seconds.
623     // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
624     m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length));
625 }
626 
searchEventTimerFired(Timer<RenderTextControlSingleLine> *)627 void RenderTextControlSingleLine::searchEventTimerFired(Timer<RenderTextControlSingleLine>*)
628 {
629     ASSERT(node()->isHTMLElement());
630     static_cast<HTMLInputElement*>(node())->onSearch();
631 }
632 
633 // PopupMenuClient methods
valueChanged(unsigned listIndex,bool fireEvents)634 void RenderTextControlSingleLine::valueChanged(unsigned listIndex, bool fireEvents)
635 {
636     ASSERT(node()->isHTMLElement());
637     ASSERT(static_cast<int>(listIndex) < listSize());
638     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
639     if (static_cast<int>(listIndex) == (listSize() - 1)) {
640         if (fireEvents) {
641             m_recentSearches.clear();
642             const AtomicString& name = autosaveName();
643             if (!name.isEmpty()) {
644                 if (!m_searchPopup)
645                     m_searchPopup = SearchPopupMenu::create(this);
646                 m_searchPopup->saveRecentSearches(name, m_recentSearches);
647             }
648         }
649     } else {
650         input->setValue(itemText(listIndex));
651         if (fireEvents)
652             input->onSearch();
653         input->select();
654     }
655 }
656 
itemText(unsigned listIndex) const657 String RenderTextControlSingleLine::itemText(unsigned listIndex) const
658 {
659     int size = listSize();
660     if (size == 1) {
661         ASSERT(!listIndex);
662         return searchMenuNoRecentSearchesText();
663     }
664     if (!listIndex)
665         return searchMenuRecentSearchesText();
666     if (itemIsSeparator(listIndex))
667         return String();
668     if (static_cast<int>(listIndex) == (size - 1))
669         return searchMenuClearRecentSearchesText();
670     return m_recentSearches[listIndex - 1];
671 }
672 
itemIsEnabled(unsigned listIndex) const673 bool RenderTextControlSingleLine::itemIsEnabled(unsigned listIndex) const
674 {
675      if (!listIndex || itemIsSeparator(listIndex))
676         return false;
677     return true;
678 }
679 
itemStyle(unsigned) const680 PopupMenuStyle RenderTextControlSingleLine::itemStyle(unsigned) const
681 {
682     return menuStyle();
683 }
684 
menuStyle() const685 PopupMenuStyle RenderTextControlSingleLine::menuStyle() const
686 {
687     return PopupMenuStyle(style()->color(), style()->backgroundColor(), style()->font(), style()->visibility() == VISIBLE);
688 }
689 
clientInsetLeft() const690 int RenderTextControlSingleLine::clientInsetLeft() const
691 {
692     // Inset the menu by the radius of the cap on the left so that
693     // it only runs along the straight part of the bezel.
694     return height() / 2;
695 }
696 
clientInsetRight() const697 int RenderTextControlSingleLine::clientInsetRight() const
698 {
699     // Inset the menu by the radius of the cap on the right so that
700     // it only runs along the straight part of the bezel (unless it needs
701     // to be wider).
702     return height() / 2;
703 }
704 
clientPaddingLeft() const705 int RenderTextControlSingleLine::clientPaddingLeft() const
706 {
707     int padding = paddingLeft();
708 
709     if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0)
710         padding += resultsRenderer->width();
711 
712     return padding;
713 }
714 
clientPaddingRight() const715 int RenderTextControlSingleLine::clientPaddingRight() const
716 {
717     int padding = paddingRight();
718 
719     if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0)
720         padding += cancelRenderer->width();
721 
722     return padding;
723 }
724 
listSize() const725 int RenderTextControlSingleLine::listSize() const
726 {
727     // If there are no recent searches, then our menu will have 1 "No recent searches" item.
728     if (!m_recentSearches.size())
729         return 1;
730     // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item.
731     return m_recentSearches.size() + 3;
732 }
733 
selectedIndex() const734 int RenderTextControlSingleLine::selectedIndex() const
735 {
736     return -1;
737 }
738 
itemIsSeparator(unsigned listIndex) const739 bool RenderTextControlSingleLine::itemIsSeparator(unsigned listIndex) const
740 {
741    // The separator will be the second to last item in our list.
742    return static_cast<int>(listIndex) == (listSize() - 2);
743 }
744 
itemIsLabel(unsigned listIndex) const745 bool RenderTextControlSingleLine::itemIsLabel(unsigned listIndex) const
746 {
747     return listIndex == 0;
748 }
749 
itemIsSelected(unsigned) const750 bool RenderTextControlSingleLine::itemIsSelected(unsigned) const
751 {
752     return false;
753 }
754 
setTextFromItem(unsigned listIndex)755 void RenderTextControlSingleLine::setTextFromItem(unsigned listIndex)
756 {
757     ASSERT(node()->isHTMLElement());
758     static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex));
759 }
760 
fontSelector() const761 FontSelector* RenderTextControlSingleLine::fontSelector() const
762 {
763     return document()->styleSelector()->fontSelector();
764 }
765 
hostWindow() const766 HostWindow* RenderTextControlSingleLine::hostWindow() const
767 {
768     return document()->view()->hostWindow();
769 }
770 
createScrollbar(ScrollbarClient * client,ScrollbarOrientation orientation,ScrollbarControlSize controlSize)771 PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
772 {
773     RefPtr<Scrollbar> widget;
774     bool hasCustomScrollbarStyle = style()->hasPseudoStyle(RenderStyle::SCROLLBAR);
775     if (hasCustomScrollbarStyle)
776         widget = RenderScrollbar::createCustomScrollbar(client, orientation, this);
777     else
778         widget = Scrollbar::createNativeScrollbar(client, orientation, controlSize);
779     return widget.release();
780 }
781 
inputElement() const782 InputElement* RenderTextControlSingleLine::inputElement() const
783 {
784     return toInputElement(static_cast<Element*>(node()));
785 }
786 
787 }
788