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