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