• 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 "SimpleFontData.h"
43 #include "TextControlInnerElements.h"
44 
45 using namespace std;
46 
47 namespace WebCore {
48 
49 using namespace HTMLNames;
50 
RenderTextControlSingleLine(Node * node)51 RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node)
52     : RenderTextControl(node)
53     , m_placeholderVisible(false)
54     , m_searchPopupIsVisible(false)
55     , m_shouldDrawCapsLockIndicator(false)
56     , m_searchEventTimer(this, &RenderTextControlSingleLine::searchEventTimerFired)
57     , m_searchPopup(0)
58 {
59 }
60 
~RenderTextControlSingleLine()61 RenderTextControlSingleLine::~RenderTextControlSingleLine()
62 {
63     if (m_searchPopup) {
64         m_searchPopup->disconnectClient();
65         m_searchPopup = 0;
66     }
67 
68     if (m_innerBlock)
69         m_innerBlock->detach();
70 }
71 
placeholderShouldBeVisible() const72 bool RenderTextControlSingleLine::placeholderShouldBeVisible() const
73 {
74     return inputElement()->placeholderShouldBeVisible();
75 }
76 
updatePlaceholderVisibility()77 void RenderTextControlSingleLine::updatePlaceholderVisibility()
78 {
79     RenderStyle* parentStyle = m_innerBlock ? m_innerBlock->renderer()->style() : style();
80 
81     RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(parentStyle);
82     HTMLElement* innerText = innerTextElement();
83     innerText->renderer()->setStyle(textBlockStyle);
84 
85     for (Node* n = innerText->firstChild(); n; n = n->traverseNextNode(innerText)) {
86         if (RenderObject* renderer = n->renderer())
87             renderer->setStyle(textBlockStyle);
88     }
89 
90     updateFromElement();
91 }
92 
addSearchResult()93 void RenderTextControlSingleLine::addSearchResult()
94 {
95     ASSERT(node()->isHTMLElement());
96     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
97     if (input->maxResults() <= 0)
98         return;
99 
100     String value = input->value();
101     if (value.isEmpty())
102         return;
103 
104     Settings* settings = document()->settings();
105     if (!settings || settings->privateBrowsingEnabled())
106         return;
107 
108     int size = static_cast<int>(m_recentSearches.size());
109     for (int i = size - 1; i >= 0; --i) {
110         if (m_recentSearches[i] == value)
111             m_recentSearches.remove(i);
112     }
113 
114     m_recentSearches.insert(0, value);
115     while (static_cast<int>(m_recentSearches.size()) > input->maxResults())
116         m_recentSearches.removeLast();
117 
118     const AtomicString& name = autosaveName();
119     if (!m_searchPopup)
120         m_searchPopup = SearchPopupMenu::create(this);
121 
122     m_searchPopup->saveRecentSearches(name, m_recentSearches);
123 }
124 
stopSearchEventTimer()125 void RenderTextControlSingleLine::stopSearchEventTimer()
126 {
127     ASSERT(node()->isHTMLElement());
128     m_searchEventTimer.stop();
129 }
130 
showPopup()131 void RenderTextControlSingleLine::showPopup()
132 {
133     ASSERT(node()->isHTMLElement());
134     if (m_searchPopupIsVisible)
135         return;
136 
137     if (!m_searchPopup)
138         m_searchPopup = SearchPopupMenu::create(this);
139 
140     if (!m_searchPopup->enabled())
141         return;
142 
143     m_searchPopupIsVisible = true;
144 
145     const AtomicString& name = autosaveName();
146     m_searchPopup->loadRecentSearches(name, m_recentSearches);
147 
148     // Trim the recent searches list if the maximum size has changed since we last saved.
149     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
150     if (static_cast<int>(m_recentSearches.size()) > input->maxResults()) {
151         do {
152             m_recentSearches.removeLast();
153         } while (static_cast<int>(m_recentSearches.size()) > input->maxResults());
154 
155         m_searchPopup->saveRecentSearches(name, m_recentSearches);
156     }
157 
158     m_searchPopup->show(absoluteBoundingBoxRect(true), document()->view(), -1);
159 }
160 
hidePopup()161 void RenderTextControlSingleLine::hidePopup()
162 {
163     ASSERT(node()->isHTMLElement());
164     if (m_searchPopup)
165         m_searchPopup->hide();
166 
167     m_searchPopupIsVisible = false;
168 }
169 
subtreeHasChanged()170 void RenderTextControlSingleLine::subtreeHasChanged()
171 {
172     bool wasEdited = isEdited();
173     RenderTextControl::subtreeHasChanged();
174 
175     InputElement* input = inputElement();
176     input->setValueFromRenderer(input->constrainValue(text()));
177 
178     if (m_cancelButton)
179         updateCancelButtonVisibility();
180 
181     // If the incremental attribute is set, then dispatch the search event
182     if (input->searchEventsShouldBeDispatched())
183         startSearchEventTimer();
184 
185     if (!wasEdited && node()->focused()) {
186         if (Frame* frame = document()->frame())
187             frame->textFieldDidBeginEditing(static_cast<Element*>(node()));
188     }
189 
190     if (node()->focused()) {
191         if (Frame* frame = document()->frame())
192             frame->textDidChangeInTextField(static_cast<Element*>(node()));
193     }
194 }
195 
paint(PaintInfo & paintInfo,int tx,int ty)196 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, int tx, int ty)
197 {
198     RenderTextControl::paint(paintInfo, tx, ty);
199 
200     if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
201         IntRect contentsRect = contentBoxRect();
202 
203         // Convert the rect into the coords used for painting the content
204         contentsRect.move(tx + x(), ty + y());
205         theme()->paintCapsLockIndicator(this, paintInfo, contentsRect);
206     }
207 }
208 
layout()209 void RenderTextControlSingleLine::layout()
210 {
211     int oldHeight = height();
212     calcHeight();
213 
214 #ifdef ANDROID_LAYOUT
215     int oldVisibleWidth = m_visibleWidth;
216 #endif
217 
218     int oldWidth = width();
219     calcWidth();
220 
221     bool relayoutChildren = oldHeight != height() || oldWidth != width();
222 
223 #ifdef ANDROID_LAYOUT
224     if (oldVisibleWidth != m_visibleWidth
225             && document()->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) {
226         relayoutChildren = true;
227     }
228 #endif
229 
230     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
231     RenderBox* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderBox() : 0;
232 
233     // Set the text block height
234     int desiredHeight = textBlockHeight();
235     int currentHeight = innerTextRenderer->height();
236 
237     if (currentHeight > height()) {
238         if (desiredHeight != currentHeight)
239             relayoutChildren = true;
240         innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed));
241         if (m_innerBlock)
242             innerBlockRenderer->style()->setHeight(Length(desiredHeight, Fixed));
243     }
244 
245     // Set the text block width
246     int desiredWidth = textBlockWidth();
247     if (desiredWidth != innerTextRenderer->width())
248         relayoutChildren = true;
249     innerTextRenderer->style()->setWidth(Length(desiredWidth, Fixed));
250 
251     if (m_innerBlock) {
252         int innerBlockWidth = width() - paddingLeft() - paddingRight() - borderLeft() - borderRight();
253         if (innerBlockWidth != innerBlockRenderer->width())
254             relayoutChildren = true;
255         innerBlockRenderer->style()->setWidth(Length(innerBlockWidth, Fixed));
256     }
257 
258     RenderBlock::layoutBlock(relayoutChildren);
259 
260     // Center the child block vertically
261     RenderBox* childBlock = innerBlockRenderer ? innerBlockRenderer : innerTextRenderer;
262     currentHeight = childBlock->height();
263     if (currentHeight < height())
264         childBlock->setLocation(childBlock->x(), (height() - currentHeight) / 2);
265 }
266 
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int xPos,int yPos,int tx,int ty,HitTestAction hitTestAction)267 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction hitTestAction)
268 {
269     // 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
270     // was on the control but not on the inner element (see Radar 4617841).
271 
272     // 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,
273     // and act as if we've hit the close block if we're to the right of the inner text block.
274 
275     if (!RenderTextControl::nodeAtPoint(request, result, xPos, yPos, tx, ty, hitTestAction))
276         return false;
277 
278     // If we hit a node inside the inner text element, say that we hit that element,
279     // and if we hit our node (e.g. we're over the border or padding), also say that we hit the
280     // inner text element so that it gains focus.
281     if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node())
282         hitInnerTextElement(result, xPos, yPos, tx, ty);
283 
284     // If we're not a search field, or we already found the results or cancel buttons, we're done.
285     if (!m_innerBlock || result.innerNode() == m_resultsButton || result.innerNode() == m_cancelButton)
286         return true;
287 
288     Node* innerNode = 0;
289     RenderBox* innerBlockRenderer = m_innerBlock->renderBox();
290     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
291 
292     IntPoint localPoint = result.localPoint();
293     localPoint.move(-innerBlockRenderer->x(), -innerBlockRenderer->y());
294 
295     int textLeft = tx + x() + innerBlockRenderer->x() + innerTextRenderer->x();
296     if (m_resultsButton && m_resultsButton->renderer() && xPos < textLeft)
297         innerNode = m_resultsButton.get();
298 
299     if (!innerNode) {
300         int textRight = textLeft + innerTextRenderer->width();
301         if (m_cancelButton && m_cancelButton->renderer() && xPos > textRight)
302             innerNode = m_cancelButton.get();
303     }
304 
305     if (innerNode) {
306         result.setInnerNode(innerNode);
307         localPoint.move(-innerNode->renderBox()->x(), -innerNode->renderBox()->y());
308     }
309 
310     result.setLocalPoint(localPoint);
311     return true;
312 }
313 
forwardEvent(Event * event)314 void RenderTextControlSingleLine::forwardEvent(Event* event)
315 {
316     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
317 
318     if (event->type() == eventNames().blurEvent) {
319         if (innerTextRenderer) {
320             if (RenderLayer* innerLayer = innerTextRenderer->layer())
321                 innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0);
322         }
323 
324         capsLockStateMayHaveChanged();
325     } else if (event->type() == eventNames().focusEvent)
326         capsLockStateMayHaveChanged();
327 
328     if (!event->isMouseEvent()) {
329         RenderTextControl::forwardEvent(event);
330         return;
331     }
332 
333     FloatPoint localPoint = innerTextRenderer->absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true);
334     if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBoxRect().x())
335         m_resultsButton->defaultEventHandler(event);
336     else if (m_cancelButton && localPoint.x() > innerTextRenderer->borderBoxRect().right())
337         m_cancelButton->defaultEventHandler(event);
338     else
339         RenderTextControl::forwardEvent(event);
340 }
341 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)342 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
343 {
344     RenderTextControl::styleDidChange(diff, oldStyle);
345 
346     if (RenderObject* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderer() : 0) {
347         // We may have set the width and the height in the old style in layout().
348         // Reset them now to avoid getting a spurious layout hint.
349         innerBlockRenderer->style()->setHeight(Length());
350         innerBlockRenderer->style()->setWidth(Length());
351         innerBlockRenderer->setStyle(createInnerBlockStyle(style()));
352     }
353 
354     if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0)
355         resultsRenderer->setStyle(createResultsButtonStyle(style()));
356 
357     if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0)
358         cancelRenderer->setStyle(createCancelButtonStyle(style()));
359 
360     setHasOverflowClip(false);
361 }
362 
capsLockStateMayHaveChanged()363 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
364 {
365     if (!node() || !document())
366         return;
367 
368     // Only draw the caps lock indicator if these things are true:
369     // 1) The field is a password field
370     // 2) The frame is active
371     // 3) The element is focused
372     // 4) The caps lock is on
373     bool shouldDrawCapsLockIndicator = false;
374 
375     if (Frame* frame = document()->frame())
376         shouldDrawCapsLockIndicator = inputElement()->isPasswordField()
377                                       && frame->selection()->isFocusedAndActive()
378                                       && document()->focusedNode() == node()
379                                       && PlatformKeyboardEvent::currentCapsLockState();
380 
381     if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
382         m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
383         repaint();
384     }
385 }
386 
textBlockWidth() const387 int RenderTextControlSingleLine::textBlockWidth() const
388 {
389     int width = RenderTextControl::textBlockWidth();
390 
391     if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) {
392         resultsRenderer->calcWidth();
393         width -= resultsRenderer->width() + resultsRenderer->marginLeft() + resultsRenderer->marginRight();
394     }
395 
396     if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) {
397         cancelRenderer->calcWidth();
398         width -= cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->marginRight();
399     }
400 
401     return width;
402 }
403 
preferredContentWidth(float charWidth) const404 int RenderTextControlSingleLine::preferredContentWidth(float charWidth) const
405 {
406     int factor = inputElement()->size();
407     if (factor <= 0)
408         factor = 20;
409 
410     int result = static_cast<int>(ceilf(charWidth * factor));
411 
412     // For text inputs, IE adds some extra width.
413     result += style()->font().primaryFont()->maxCharWidth() - charWidth;
414 
415     if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0)
416         result += resultsRenderer->borderLeft() + resultsRenderer->borderRight() +
417                   resultsRenderer->paddingLeft() + resultsRenderer->paddingRight();
418 
419     if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0)
420         result += cancelRenderer->borderLeft() + cancelRenderer->borderRight() +
421                   cancelRenderer->paddingLeft() + cancelRenderer->paddingRight();
422 
423     return result;
424 }
425 
adjustControlHeightBasedOnLineHeight(int lineHeight)426 void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineHeight)
427 {
428     if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) {
429         toRenderBlock(resultsRenderer)->calcHeight();
430         setHeight(max(height(),
431                   resultsRenderer->borderTop() + resultsRenderer->borderBottom() +
432                   resultsRenderer->paddingTop() + resultsRenderer->paddingBottom() +
433                   resultsRenderer->marginTop() + resultsRenderer->marginBottom()));
434         lineHeight = max(lineHeight, resultsRenderer->height());
435     }
436 
437     if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) {
438         toRenderBlock(cancelRenderer)->calcHeight();
439         setHeight(max(height(),
440                   cancelRenderer->borderTop() + cancelRenderer->borderBottom() +
441                   cancelRenderer->paddingTop() + cancelRenderer->paddingBottom() +
442                   cancelRenderer->marginTop() + cancelRenderer->marginBottom()));
443         lineHeight = max(lineHeight, cancelRenderer->height());
444     }
445 
446     setHeight(height() + lineHeight);
447 }
448 
createSubtreeIfNeeded()449 void RenderTextControlSingleLine::createSubtreeIfNeeded()
450 {
451     if (!inputElement()->isSearchField()) {
452         RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
453         return;
454     }
455 
456     if (!m_innerBlock) {
457         // Create the inner block element
458         m_innerBlock = new TextControlInnerElement(document(), node());
459         m_innerBlock->attachInnerElement(node(), createInnerBlockStyle(style()), renderArena());
460     }
461 
462     if (!m_resultsButton) {
463         // Create the search results button element
464         m_resultsButton = new SearchFieldResultsButtonElement(document());
465         m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena());
466     }
467 
468     // Create innerText element before adding the cancel button
469     RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
470 
471     if (!m_cancelButton) {
472         // Create the cancel button element
473         m_cancelButton = new SearchFieldCancelButtonElement(document());
474         m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena());
475     }
476 }
477 
updateFromElement()478 void RenderTextControlSingleLine::updateFromElement()
479 {
480     createSubtreeIfNeeded();
481     RenderTextControl::updateFromElement();
482 
483     bool placeholderVisibilityShouldChange = m_placeholderVisible != placeholderShouldBeVisible();
484     m_placeholderVisible = placeholderShouldBeVisible();
485 
486     if (m_cancelButton)
487         updateCancelButtonVisibility();
488 
489     if (m_placeholderVisible) {
490         ExceptionCode ec = 0;
491         innerTextElement()->setInnerText(inputElement()->placeholder(), ec);
492         ASSERT(!ec);
493     } else if (!static_cast<Element*>(node())->formControlValueMatchesRenderer() || placeholderVisibilityShouldChange)
494         setInnerTextValue(inputElement()->value());
495 
496     if (m_searchPopupIsVisible)
497         m_searchPopup->updateFromElement();
498 }
499 
cacheSelection(int start,int end)500 void RenderTextControlSingleLine::cacheSelection(int start, int end)
501 {
502     inputElement()->cacheSelection(start, end);
503 }
504 
createInnerTextStyle(const RenderStyle * startStyle) const505 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
506 {
507     RefPtr<RenderStyle> textBlockStyle;
508     if (placeholderShouldBeVisible()) {
509         if (RenderStyle* pseudoStyle = getCachedPseudoStyle(INPUT_PLACEHOLDER))
510             textBlockStyle = RenderStyle::clone(pseudoStyle);
511     }
512     if (!textBlockStyle) {
513         textBlockStyle = RenderStyle::create();
514         textBlockStyle->inheritFrom(startStyle);
515     }
516 
517     adjustInnerTextStyle(startStyle, textBlockStyle.get());
518 
519     textBlockStyle->setWhiteSpace(PRE);
520     textBlockStyle->setWordWrap(NormalWordWrap);
521     textBlockStyle->setOverflowX(OHIDDEN);
522     textBlockStyle->setOverflowY(OHIDDEN);
523 
524     // Do not allow line-height to be smaller than our default.
525     if (textBlockStyle->font().lineSpacing() > lineHeight(true, true))
526         textBlockStyle->setLineHeight(Length(-100.0f, Percent));
527 
528     textBlockStyle->setDisplay(m_innerBlock ? INLINE_BLOCK : BLOCK);
529 
530     // We're adding one extra pixel of padding to match WinIE.
531     textBlockStyle->setPaddingLeft(Length(1, Fixed));
532     textBlockStyle->setPaddingRight(Length(1, Fixed));
533 
534     // When the placeholder is going to be displayed, temporarily override the text security to be "none".
535     // After this, updateFromElement will immediately update the text displayed.
536     // When the placeholder is no longer visible, updatePlaceholderVisiblity will reset the style,
537     // and the text security mode will be set back to the computed value correctly.
538     if (placeholderShouldBeVisible())
539         textBlockStyle->setTextSecurity(TSNONE);
540 
541     return textBlockStyle.release();
542 }
543 
createInnerBlockStyle(const RenderStyle * startStyle) const544 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const
545 {
546     ASSERT(node()->isHTMLElement());
547 
548     RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create();
549     innerBlockStyle->inheritFrom(startStyle);
550 
551     innerBlockStyle->setDisplay(BLOCK);
552     innerBlockStyle->setDirection(LTR);
553 
554     // 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.
555     innerBlockStyle->setUserModify(READ_ONLY);
556 
557     return innerBlockStyle.release();
558 }
559 
createResultsButtonStyle(const RenderStyle * startStyle) const560 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createResultsButtonStyle(const RenderStyle* startStyle) const
561 {
562     ASSERT(node()->isHTMLElement());
563     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
564 
565     RefPtr<RenderStyle> resultsBlockStyle;
566     if (input->maxResults() < 0)
567         resultsBlockStyle = getCachedPseudoStyle(SEARCH_DECORATION);
568     else if (!input->maxResults())
569         resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_DECORATION);
570     else
571         resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_BUTTON);
572 
573     if (!resultsBlockStyle)
574         resultsBlockStyle = RenderStyle::create();
575 
576     if (startStyle)
577         resultsBlockStyle->inheritFrom(startStyle);
578 
579     return resultsBlockStyle.release();
580 }
581 
createCancelButtonStyle(const RenderStyle * startStyle) const582 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createCancelButtonStyle(const RenderStyle* startStyle) const
583 {
584     ASSERT(node()->isHTMLElement());
585     RefPtr<RenderStyle> cancelBlockStyle;
586 
587     if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(SEARCH_CANCEL_BUTTON))
588         // We may be sharing style with another search field, but we must not share the cancel button style.
589         cancelBlockStyle = RenderStyle::clone(pseudoStyle.get());
590     else
591         cancelBlockStyle = RenderStyle::create();
592 
593     if (startStyle)
594         cancelBlockStyle->inheritFrom(startStyle);
595 
596     cancelBlockStyle->setVisibility(visibilityForCancelButton());
597     return cancelBlockStyle.release();
598 }
599 
updateCancelButtonVisibility() const600 void RenderTextControlSingleLine::updateCancelButtonVisibility() const
601 {
602     if (!m_cancelButton->renderer())
603         return;
604 
605     const RenderStyle* curStyle = m_cancelButton->renderer()->style();
606     EVisibility buttonVisibility = visibilityForCancelButton();
607     if (curStyle->visibility() == buttonVisibility)
608         return;
609 
610     RefPtr<RenderStyle> cancelButtonStyle = RenderStyle::clone(curStyle);
611     cancelButtonStyle->setVisibility(buttonVisibility);
612     m_cancelButton->renderer()->setStyle(cancelButtonStyle);
613 }
614 
visibilityForCancelButton() const615 EVisibility RenderTextControlSingleLine::visibilityForCancelButton() const
616 {
617     ASSERT(node()->isHTMLElement());
618     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
619     return input->value().isEmpty() ? HIDDEN : VISIBLE;
620 }
621 
autosaveName() const622 const AtomicString& RenderTextControlSingleLine::autosaveName() const
623 {
624     return static_cast<Element*>(node())->getAttribute(autosaveAttr);
625 }
626 
startSearchEventTimer()627 void RenderTextControlSingleLine::startSearchEventTimer()
628 {
629     ASSERT(node()->isHTMLElement());
630     unsigned length = text().length();
631 
632     // If there's no text, fire the event right away.
633     if (!length) {
634         stopSearchEventTimer();
635         static_cast<HTMLInputElement*>(node())->onSearch();
636         return;
637     }
638 
639     // After typing the first key, we wait 0.5 seconds.
640     // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
641     m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length));
642 }
643 
searchEventTimerFired(Timer<RenderTextControlSingleLine> *)644 void RenderTextControlSingleLine::searchEventTimerFired(Timer<RenderTextControlSingleLine>*)
645 {
646     ASSERT(node()->isHTMLElement());
647     static_cast<HTMLInputElement*>(node())->onSearch();
648 }
649 
650 // PopupMenuClient methods
valueChanged(unsigned listIndex,bool fireEvents)651 void RenderTextControlSingleLine::valueChanged(unsigned listIndex, bool fireEvents)
652 {
653     ASSERT(node()->isHTMLElement());
654     ASSERT(static_cast<int>(listIndex) < listSize());
655     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
656     if (static_cast<int>(listIndex) == (listSize() - 1)) {
657         if (fireEvents) {
658             m_recentSearches.clear();
659             const AtomicString& name = autosaveName();
660             if (!name.isEmpty()) {
661                 if (!m_searchPopup)
662                     m_searchPopup = SearchPopupMenu::create(this);
663                 m_searchPopup->saveRecentSearches(name, m_recentSearches);
664             }
665         }
666     } else {
667         input->setValue(itemText(listIndex));
668         if (fireEvents)
669             input->onSearch();
670         input->select();
671     }
672 }
673 
itemText(unsigned listIndex) const674 String RenderTextControlSingleLine::itemText(unsigned listIndex) const
675 {
676     int size = listSize();
677     if (size == 1) {
678         ASSERT(!listIndex);
679         return searchMenuNoRecentSearchesText();
680     }
681     if (!listIndex)
682         return searchMenuRecentSearchesText();
683     if (itemIsSeparator(listIndex))
684         return String();
685     if (static_cast<int>(listIndex) == (size - 1))
686         return searchMenuClearRecentSearchesText();
687     return m_recentSearches[listIndex - 1];
688 }
689 
itemIsEnabled(unsigned listIndex) const690 bool RenderTextControlSingleLine::itemIsEnabled(unsigned listIndex) const
691 {
692      if (!listIndex || itemIsSeparator(listIndex))
693         return false;
694     return true;
695 }
696 
itemStyle(unsigned) const697 PopupMenuStyle RenderTextControlSingleLine::itemStyle(unsigned) const
698 {
699     return menuStyle();
700 }
701 
menuStyle() const702 PopupMenuStyle RenderTextControlSingleLine::menuStyle() const
703 {
704     return PopupMenuStyle(style()->color(), style()->backgroundColor(), style()->font(), style()->visibility() == VISIBLE, style()->textIndent(), style()->direction());
705 }
706 
clientInsetLeft() const707 int RenderTextControlSingleLine::clientInsetLeft() const
708 {
709     // Inset the menu by the radius of the cap on the left so that
710     // it only runs along the straight part of the bezel.
711     return height() / 2;
712 }
713 
clientInsetRight() const714 int RenderTextControlSingleLine::clientInsetRight() const
715 {
716     // Inset the menu by the radius of the cap on the right so that
717     // it only runs along the straight part of the bezel (unless it needs
718     // to be wider).
719     return height() / 2;
720 }
721 
clientPaddingLeft() const722 int RenderTextControlSingleLine::clientPaddingLeft() const
723 {
724     int padding = paddingLeft();
725 
726     if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0)
727         padding += resultsRenderer->width();
728 
729     return padding;
730 }
731 
clientPaddingRight() const732 int RenderTextControlSingleLine::clientPaddingRight() const
733 {
734     int padding = paddingRight();
735 
736     if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0)
737         padding += cancelRenderer->width();
738 
739     return padding;
740 }
741 
listSize() const742 int RenderTextControlSingleLine::listSize() const
743 {
744     // If there are no recent searches, then our menu will have 1 "No recent searches" item.
745     if (!m_recentSearches.size())
746         return 1;
747     // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item.
748     return m_recentSearches.size() + 3;
749 }
750 
selectedIndex() const751 int RenderTextControlSingleLine::selectedIndex() const
752 {
753     return -1;
754 }
755 
itemIsSeparator(unsigned listIndex) const756 bool RenderTextControlSingleLine::itemIsSeparator(unsigned listIndex) const
757 {
758     // The separator will be the second to last item in our list.
759     return static_cast<int>(listIndex) == (listSize() - 2);
760 }
761 
itemIsLabel(unsigned listIndex) const762 bool RenderTextControlSingleLine::itemIsLabel(unsigned listIndex) const
763 {
764     return listIndex == 0;
765 }
766 
itemIsSelected(unsigned) const767 bool RenderTextControlSingleLine::itemIsSelected(unsigned) const
768 {
769     return false;
770 }
771 
setTextFromItem(unsigned listIndex)772 void RenderTextControlSingleLine::setTextFromItem(unsigned listIndex)
773 {
774     ASSERT(node()->isHTMLElement());
775     static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex));
776 }
777 
fontSelector() const778 FontSelector* RenderTextControlSingleLine::fontSelector() const
779 {
780     return document()->styleSelector()->fontSelector();
781 }
782 
hostWindow() const783 HostWindow* RenderTextControlSingleLine::hostWindow() const
784 {
785     return document()->view()->hostWindow();
786 }
787 
autoscroll()788 void RenderTextControlSingleLine::autoscroll()
789 {
790     RenderLayer* layer = innerTextElement()->renderBox()->layer();
791     if (layer)
792         layer->autoscroll();
793 }
794 
scrollWidth() const795 int RenderTextControlSingleLine::scrollWidth() const
796 {
797     if (innerTextElement())
798         return innerTextElement()->scrollWidth();
799     return RenderBlock::scrollWidth();
800 }
801 
scrollHeight() const802 int RenderTextControlSingleLine::scrollHeight() const
803 {
804     if (innerTextElement())
805         return innerTextElement()->scrollHeight();
806     return RenderBlock::scrollHeight();
807 }
808 
scrollLeft() const809 int RenderTextControlSingleLine::scrollLeft() const
810 {
811     if (innerTextElement())
812         return innerTextElement()->scrollLeft();
813     return RenderBlock::scrollLeft();
814 }
815 
scrollTop() const816 int RenderTextControlSingleLine::scrollTop() const
817 {
818     if (innerTextElement())
819         return innerTextElement()->scrollTop();
820     return RenderBlock::scrollTop();
821 }
822 
setScrollLeft(int newLeft)823 void RenderTextControlSingleLine::setScrollLeft(int newLeft)
824 {
825     if (innerTextElement())
826         innerTextElement()->setScrollLeft(newLeft);
827 }
828 
setScrollTop(int newTop)829 void RenderTextControlSingleLine::setScrollTop(int newTop)
830 {
831     if (innerTextElement())
832         innerTextElement()->setScrollTop(newTop);
833 }
834 
scroll(ScrollDirection direction,ScrollGranularity granularity,float multiplier)835 bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
836 {
837     RenderLayer* layer = innerTextElement()->renderBox()->layer();
838     if (layer && layer->scroll(direction, granularity, multiplier))
839         return true;
840     return RenderBlock::scroll(direction, granularity, multiplier);
841 }
842 
createScrollbar(ScrollbarClient * client,ScrollbarOrientation orientation,ScrollbarControlSize controlSize)843 PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
844 {
845     RefPtr<Scrollbar> widget;
846     bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
847     if (hasCustomScrollbarStyle)
848         widget = RenderScrollbar::createCustomScrollbar(client, orientation, this);
849     else
850         widget = Scrollbar::createNativeScrollbar(client, orientation, controlSize);
851     return widget.release();
852 }
853 
inputElement() const854 InputElement* RenderTextControlSingleLine::inputElement() const
855 {
856     return toInputElement(static_cast<Element*>(node()));
857 }
858 
859 }
860