1 /** 2 * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. 3 * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 4 * Copyright (C) 2010 Google Inc. All rights reserved. 5 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24 #include "config.h" 25 #include "RenderTextControlSingleLine.h" 26 27 #include "Chrome.h" 28 #include "CSSStyleSelector.h" 29 #include "Event.h" 30 #include "EventNames.h" 31 #include "Frame.h" 32 #include "FrameView.h" 33 #include "HTMLInputElement.h" 34 #include "HTMLNames.h" 35 #include "HitTestResult.h" 36 #include "InputElement.h" 37 #include "LocalizedStrings.h" 38 #include "MouseEvent.h" 39 #include "PlatformKeyboardEvent.h" 40 #include "RenderLayer.h" 41 #include "RenderScrollbar.h" 42 #include "RenderTheme.h" 43 #include "SelectionController.h" 44 #include "Settings.h" 45 #include "SimpleFontData.h" 46 #include "TextControlInnerElements.h" 47 48 using namespace std; 49 50 namespace WebCore { 51 52 using namespace HTMLNames; 53 54 VisiblePosition RenderTextControlInnerBlock::positionForPoint(const IntPoint& point) 55 { 56 IntPoint contentsPoint(point); 57 58 // Multiline text controls have the scroll on shadowAncestorNode, so we need to take that 59 // into account here. 60 if (m_multiLine) { 61 RenderTextControl* renderer = toRenderTextControl(node()->shadowAncestorNode()->renderer()); 62 if (renderer->hasOverflowClip()) 63 contentsPoint += renderer->layer()->scrolledContentOffset(); 64 } 65 66 return RenderBlock::positionForPoint(contentsPoint); 67 } 68 69 // ---------------------------- 70 71 RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node, bool placeholderVisible) 72 : RenderTextControl(node, placeholderVisible) 73 , m_searchPopupIsVisible(false) 74 , m_shouldDrawCapsLockIndicator(false) 75 , m_searchEventTimer(this, &RenderTextControlSingleLine::searchEventTimerFired) 76 , m_searchPopup(0) 77 { 78 } 79 80 RenderTextControlSingleLine::~RenderTextControlSingleLine() 81 { 82 if (m_searchPopup) { 83 m_searchPopup->popupMenu()->disconnectClient(); 84 m_searchPopup = 0; 85 } 86 87 if (m_innerBlock) { 88 m_innerBlock->detach(); 89 m_innerBlock = 0; 90 } 91 92 if (m_innerSpinButton) 93 m_innerSpinButton->detach(); 94 if (m_outerSpinButton) 95 m_outerSpinButton->detach(); 96 #if ENABLE(INPUT_SPEECH) 97 if (m_speechButton) 98 m_speechButton->detach(); 99 #endif 100 } 101 102 RenderStyle* RenderTextControlSingleLine::textBaseStyle() const 103 { 104 return m_innerBlock ? m_innerBlock->renderer()->style() : style(); 105 } 106 107 void RenderTextControlSingleLine::addSearchResult() 108 { 109 ASSERT(node()->isHTMLElement()); 110 HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); 111 if (input->maxResults() <= 0) 112 return; 113 114 String value = input->value(); 115 if (value.isEmpty()) 116 return; 117 118 Settings* settings = document()->settings(); 119 if (!settings || settings->privateBrowsingEnabled()) 120 return; 121 122 int size = static_cast<int>(m_recentSearches.size()); 123 for (int i = size - 1; i >= 0; --i) { 124 if (m_recentSearches[i] == value) 125 m_recentSearches.remove(i); 126 } 127 128 m_recentSearches.insert(0, value); 129 while (static_cast<int>(m_recentSearches.size()) > input->maxResults()) 130 m_recentSearches.removeLast(); 131 132 const AtomicString& name = autosaveName(); 133 if (!m_searchPopup) 134 m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this); 135 136 m_searchPopup->saveRecentSearches(name, m_recentSearches); 137 } 138 139 void RenderTextControlSingleLine::stopSearchEventTimer() 140 { 141 ASSERT(node()->isHTMLElement()); 142 m_searchEventTimer.stop(); 143 } 144 145 void RenderTextControlSingleLine::showPopup() 146 { 147 ASSERT(node()->isHTMLElement()); 148 if (m_searchPopupIsVisible) 149 return; 150 151 if (!m_searchPopup) 152 m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this); 153 154 if (!m_searchPopup->enabled()) 155 return; 156 157 m_searchPopupIsVisible = true; 158 159 const AtomicString& name = autosaveName(); 160 m_searchPopup->loadRecentSearches(name, m_recentSearches); 161 162 // Trim the recent searches list if the maximum size has changed since we last saved. 163 HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); 164 if (static_cast<int>(m_recentSearches.size()) > input->maxResults()) { 165 do { 166 m_recentSearches.removeLast(); 167 } while (static_cast<int>(m_recentSearches.size()) > input->maxResults()); 168 169 m_searchPopup->saveRecentSearches(name, m_recentSearches); 170 } 171 172 m_searchPopup->popupMenu()->show(absoluteBoundingBoxRect(true), document()->view(), -1); 173 } 174 175 void RenderTextControlSingleLine::hidePopup() 176 { 177 ASSERT(node()->isHTMLElement()); 178 if (m_searchPopup) 179 m_searchPopup->popupMenu()->hide(); 180 } 181 182 void RenderTextControlSingleLine::subtreeHasChanged() 183 { 184 RenderTextControl::subtreeHasChanged(); 185 186 ASSERT(node()->isElementNode()); 187 Element* element = static_cast<Element*>(node()); 188 bool wasChanged = element->wasChangedSinceLastFormControlChangeEvent(); 189 element->setChangedSinceLastFormControlChangeEvent(true); 190 191 InputElement* input = inputElement(); 192 // We don't need to call sanitizeUserInputValue() function here because 193 // InputElement::handleBeforeTextInsertedEvent() has already called 194 // sanitizeUserInputValue(). 195 // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent. 196 String value = text(); 197 if (input->isAcceptableValue(value)) 198 input->setValueFromRenderer(input->sanitizeValue(input->convertFromVisibleValue(value))); 199 if (node()->isHTMLElement()) { 200 // Recalc for :invalid and hasUnacceptableValue() change. 201 static_cast<HTMLInputElement*>(input)->setNeedsStyleRecalc(); 202 } 203 204 if (m_cancelButton) 205 updateCancelButtonVisibility(); 206 207 // If the incremental attribute is set, then dispatch the search event 208 if (input->searchEventsShouldBeDispatched()) 209 startSearchEventTimer(); 210 211 if (!wasChanged && node()->focused()) { 212 if (Frame* frame = this->frame()) 213 frame->editor()->textFieldDidBeginEditing(static_cast<Element*>(node())); 214 } 215 216 if (node()->focused()) { 217 if (Frame* frame = document()->frame()) 218 frame->editor()->textDidChangeInTextField(static_cast<Element*>(node())); 219 } 220 } 221 222 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, int tx, int ty) 223 { 224 RenderTextControl::paint(paintInfo, tx, ty); 225 226 if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) { 227 IntRect contentsRect = contentBoxRect(); 228 229 // Center vertically like the text. 230 contentsRect.setY((height() - contentsRect.height()) / 2); 231 232 // Convert the rect into the coords used for painting the content 233 contentsRect.move(tx + x(), ty + y()); 234 theme()->paintCapsLockIndicator(this, paintInfo, contentsRect); 235 } 236 } 237 238 void RenderTextControlSingleLine::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) 239 { 240 paintBoxDecorationsWithSize(paintInfo, tx, ty, width() - decorationWidthRight(), height()); 241 } 242 243 void RenderTextControlSingleLine::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) 244 { 245 int w = width() - decorationWidthRight(); 246 if (w && height()) 247 rects.append(IntRect(tx, ty, w, height())); 248 } 249 250 void RenderTextControlSingleLine::layout() 251 { 252 int oldHeight = height(); 253 computeLogicalHeight(); 254 255 int oldWidth = width(); 256 computeLogicalWidth(); 257 258 bool relayoutChildren = oldHeight != height() || oldWidth != width(); 259 260 #ifdef ANDROID_LAYOUT 261 checkAndSetRelayoutChildren(&relayoutChildren); 262 #endif 263 264 RenderBox* innerTextRenderer = innerTextElement()->renderBox(); 265 RenderBox* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderBox() : 0; 266 267 // Set the text block height 268 int desiredHeight = textBlockHeight(); 269 int currentHeight = innerTextRenderer->height(); 270 271 if (currentHeight > height()) { 272 if (desiredHeight != currentHeight) 273 relayoutChildren = true; 274 innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed)); 275 if (m_innerBlock) 276 innerBlockRenderer->style()->setHeight(Length(desiredHeight, Fixed)); 277 } 278 279 // Set the text block width 280 int desiredWidth = textBlockWidth(); 281 if (desiredWidth != innerTextRenderer->width()) 282 relayoutChildren = true; 283 innerTextRenderer->style()->setWidth(Length(desiredWidth, Fixed)); 284 285 if (m_innerBlock) { 286 int innerBlockWidth = width() - borderAndPaddingWidth(); 287 if (innerBlockWidth != innerBlockRenderer->width()) 288 relayoutChildren = true; 289 innerBlockRenderer->style()->setWidth(Length(innerBlockWidth, Fixed)); 290 } 291 292 RenderBlock::layoutBlock(relayoutChildren); 293 294 // Center the child block vertically 295 RenderBox* childBlock = innerBlockRenderer ? innerBlockRenderer : innerTextRenderer; 296 currentHeight = childBlock->height(); 297 if (currentHeight < height()) 298 childBlock->setY((height() - currentHeight) / 2); 299 300 // Ignores the paddings for the inner spin button. 301 if (RenderBox* spinBox = m_innerSpinButton ? m_innerSpinButton->renderBox() : 0) { 302 spinBox->setLocation(spinBox->x() + paddingRight(), borderTop()); 303 spinBox->setHeight(height() - borderTop() - borderBottom()); 304 } 305 306 #if ENABLE(INPUT_SPEECH) 307 if (RenderBox* button = m_speechButton ? m_speechButton->renderBox() : 0) { 308 if (m_innerBlock) { 309 // This is mostly the case where this is a search field. The speech button is a sibling 310 // of the inner block and laid out at the far right. 311 int x = width() - borderAndPaddingWidth() - button->width() - button->borderAndPaddingWidth(); 312 int y = (height() - button->height()) / 2; 313 button->setLocation(x, y); 314 } else { 315 // For non-search fields which are simpler and we let the defaut layout handle things 316 // except for small tweaking below. 317 button->setLocation(button->x() + paddingRight(), (height() - button->height()) / 2); 318 } 319 } 320 #endif 321 322 // Center the spin button vertically, and move it to the right by 323 // padding + border of the text fields. 324 if (RenderBox* spinBox = m_outerSpinButton ? m_outerSpinButton->renderBox() : 0) { 325 int diff = height() - spinBox->height(); 326 // If the diff is odd, the top area over the spin button takes the 327 // remaining one pixel. It's good for Mac NSStepper because it has 328 // shadow at the bottom. 329 int y = (diff / 2) + (diff % 2); 330 int x = width() - borderRight() - paddingRight() - spinBox->width(); 331 spinBox->setLocation(x, y); 332 } 333 } 334 335 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction hitTestAction) 336 { 337 // 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 338 // was on the control but not on the inner element (see Radar 4617841). 339 340 // 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, 341 // and act as if we've hit the close block if we're to the right of the inner text block. 342 343 if (!RenderTextControl::nodeAtPoint(request, result, xPos, yPos, tx, ty, hitTestAction)) 344 return false; 345 346 // If we hit a node inside the inner text element, say that we hit that element, 347 // and if we hit our node (e.g. we're over the border or padding), also say that we hit the 348 // inner text element so that it gains focus. 349 if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node()) 350 hitInnerTextElement(result, xPos, yPos, tx, ty); 351 352 // If we found a spin button, we're done. 353 if (m_innerSpinButton && result.innerNode() == m_innerSpinButton) 354 return true; 355 if (m_outerSpinButton && result.innerNode() == m_outerSpinButton) 356 return true; 357 #if ENABLE(INPUT_SPEECH) 358 if (m_speechButton && result.innerNode() == m_speechButton) 359 return true; 360 #endif 361 // If we're not a search field, or we already found the speech, results or cancel buttons, we're done. 362 if (!m_innerBlock || result.innerNode() == m_resultsButton || result.innerNode() == m_cancelButton) 363 return true; 364 365 Node* innerNode = 0; 366 RenderBox* innerBlockRenderer = m_innerBlock->renderBox(); 367 RenderBox* innerTextRenderer = innerTextElement()->renderBox(); 368 369 IntPoint localPoint = result.localPoint(); 370 localPoint.move(-innerBlockRenderer->x(), -innerBlockRenderer->y()); 371 372 int textLeft = tx + x() + innerBlockRenderer->x() + innerTextRenderer->x(); 373 if (m_resultsButton && m_resultsButton->renderer() && xPos < textLeft) 374 innerNode = m_resultsButton.get(); 375 376 if (!innerNode) { 377 int textRight = textLeft + innerTextRenderer->width(); 378 if (m_cancelButton && m_cancelButton->renderer() && xPos > textRight) 379 innerNode = m_cancelButton.get(); 380 } 381 382 if (innerNode) { 383 result.setInnerNode(innerNode); 384 localPoint.move(-innerNode->renderBox()->x(), -innerNode->renderBox()->y()); 385 } 386 387 result.setLocalPoint(localPoint); 388 return true; 389 } 390 391 void RenderTextControlSingleLine::forwardEvent(Event* event) 392 { 393 RenderBox* innerTextRenderer = innerTextElement()->renderBox(); 394 395 if (event->type() == eventNames().blurEvent) { 396 if (innerTextRenderer) { 397 if (RenderLayer* innerLayer = innerTextRenderer->layer()) 398 innerLayer->scrollToOffset(!style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0); 399 } 400 401 capsLockStateMayHaveChanged(); 402 } else if (event->type() == eventNames().focusEvent) 403 capsLockStateMayHaveChanged(); 404 405 if (!event->isMouseEvent()) { 406 RenderTextControl::forwardEvent(event); 407 return; 408 } 409 410 #if ENABLE(INPUT_SPEECH) 411 if (RenderBox* speechBox = m_speechButton ? m_speechButton->renderBox() : 0) { 412 FloatPoint pointInTextControlCoords = absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true); 413 if (speechBox->frameRect().contains(roundedIntPoint(pointInTextControlCoords))) { 414 m_speechButton->defaultEventHandler(event); 415 return; 416 } 417 } 418 #endif 419 420 FloatPoint localPoint = innerTextRenderer->absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true); 421 int textRight = innerTextRenderer->borderBoxRect().maxX(); 422 423 if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBoxRect().x()) 424 m_resultsButton->defaultEventHandler(event); 425 else if (m_cancelButton && localPoint.x() > textRight) 426 m_cancelButton->defaultEventHandler(event); 427 else 428 RenderTextControl::forwardEvent(event); 429 } 430 431 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 432 { 433 RenderTextControl::styleDidChange(diff, oldStyle); 434 435 if (RenderObject* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderer() : 0) { 436 // We may have set the width and the height in the old style in layout(). 437 // Reset them now to avoid getting a spurious layout hint. 438 innerBlockRenderer->style()->setHeight(Length()); 439 innerBlockRenderer->style()->setWidth(Length()); 440 innerBlockRenderer->setStyle(createInnerBlockStyle(style())); 441 } 442 443 if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0) 444 resultsRenderer->setStyle(createResultsButtonStyle(style())); 445 446 if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) 447 cancelRenderer->setStyle(createCancelButtonStyle(style())); 448 449 if (RenderObject* spinRenderer = m_outerSpinButton ? m_outerSpinButton->renderer() : 0) 450 spinRenderer->setStyle(createOuterSpinButtonStyle()); 451 452 if (RenderObject* spinRenderer = m_innerSpinButton ? m_innerSpinButton->renderer() : 0) 453 spinRenderer->setStyle(createInnerSpinButtonStyle()); 454 455 #if ENABLE(INPUT_SPEECH) 456 if (RenderObject* speechRenderer = m_speechButton ? m_speechButton->renderer() : 0) 457 speechRenderer->setStyle(createSpeechButtonStyle()); 458 #endif 459 460 setHasOverflowClip(false); 461 } 462 463 void RenderTextControlSingleLine::capsLockStateMayHaveChanged() 464 { 465 if (!node() || !document()) 466 return; 467 468 // Only draw the caps lock indicator if these things are true: 469 // 1) The field is a password field 470 // 2) The frame is active 471 // 3) The element is focused 472 // 4) The caps lock is on 473 bool shouldDrawCapsLockIndicator = false; 474 475 if (Frame* frame = document()->frame()) 476 shouldDrawCapsLockIndicator = inputElement()->isPasswordField() 477 && frame->selection()->isFocusedAndActive() 478 && document()->focusedNode() == node() 479 && PlatformKeyboardEvent::currentCapsLockState(); 480 481 if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) { 482 m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator; 483 repaint(); 484 } 485 } 486 487 bool RenderTextControlSingleLine::hasControlClip() const 488 { 489 bool clip = m_cancelButton; 490 return clip; 491 } 492 493 IntRect RenderTextControlSingleLine::controlClipRect(int tx, int ty) const 494 { 495 // This should only get called for search & speech inputs. 496 ASSERT(hasControlClip()); 497 498 IntRect clipRect = IntRect(m_innerBlock->renderBox()->frameRect()); 499 clipRect.move(tx, ty); 500 return clipRect; 501 } 502 503 int RenderTextControlSingleLine::textBlockWidth() const 504 { 505 int width = RenderTextControl::textBlockWidth(); 506 507 if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) { 508 resultsRenderer->computeLogicalWidth(); 509 width -= resultsRenderer->width() + resultsRenderer->marginLeft() + resultsRenderer->marginRight(); 510 } 511 512 if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) { 513 cancelRenderer->computeLogicalWidth(); 514 width -= cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->marginRight(); 515 } 516 517 if (RenderBox* spinRenderer = m_innerSpinButton ? m_innerSpinButton->renderBox() : 0) { 518 spinRenderer->computeLogicalWidth(); 519 width -= spinRenderer->width() + spinRenderer->marginLeft() + spinRenderer->marginRight(); 520 } 521 522 #if ENABLE(INPUT_SPEECH) 523 if (RenderBox* speechRenderer = m_speechButton ? m_speechButton->renderBox() : 0) { 524 speechRenderer->computeLogicalWidth(); 525 width -= speechRenderer->width() + speechRenderer->marginLeft() + speechRenderer->marginRight(); 526 } 527 #endif 528 529 return width - decorationWidthRight(); 530 } 531 532 int RenderTextControlSingleLine::decorationWidthRight() const 533 { 534 int width = 0; 535 if (RenderBox* spinRenderer = m_outerSpinButton ? m_outerSpinButton->renderBox() : 0) { 536 spinRenderer->computeLogicalWidth(); 537 width += spinRenderer->width() + spinRenderer->marginLeft() + spinRenderer->marginRight(); 538 } 539 if (width > 0) 540 width += paddingRight() + borderRight(); 541 return width; 542 } 543 544 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family) 545 { 546 // Since Lucida Grande is the default font, we want this to match the width 547 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and 548 // IE for some encodings (in IE, the default font is encoding specific). 549 // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg. 550 if (family == AtomicString("Lucida Grande")) 551 return scaleEmToUnits(901); 552 553 return RenderTextControl::getAvgCharWidth(family); 554 } 555 556 int RenderTextControlSingleLine::preferredContentWidth(float charWidth) const 557 { 558 int factor = inputElement()->size(); 559 if (factor <= 0) 560 factor = 20; 561 562 int result = static_cast<int>(ceilf(charWidth * factor)); 563 564 float maxCharWidth = 0.f; 565 AtomicString family = style()->font().family().family(); 566 // Since Lucida Grande is the default font, we want this to match the width 567 // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and 568 // IE for some encodings (in IE, the default font is encoding specific). 569 // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg. 570 if (family == AtomicString("Lucida Grande")) 571 maxCharWidth = scaleEmToUnits(4027); 572 else if (hasValidAvgCharWidth(family)) 573 maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth()); 574 575 // For text inputs, IE adds some extra width. 576 if (maxCharWidth > 0.f) 577 result += maxCharWidth - charWidth; 578 579 if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) 580 result += resultsRenderer->borderLeft() + resultsRenderer->borderRight() + 581 resultsRenderer->paddingLeft() + resultsRenderer->paddingRight(); 582 583 if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) 584 result += cancelRenderer->borderLeft() + cancelRenderer->borderRight() + 585 cancelRenderer->paddingLeft() + cancelRenderer->paddingRight(); 586 587 #if ENABLE(INPUT_SPEECH) 588 if (RenderBox* speechRenderer = m_speechButton ? m_speechButton->renderBox() : 0) { 589 result += speechRenderer->borderLeft() + speechRenderer->borderRight() + 590 speechRenderer->paddingLeft() + speechRenderer->paddingRight(); 591 } 592 #endif 593 return result; 594 } 595 596 int RenderTextControlSingleLine::preferredDecorationWidthRight() const 597 { 598 int width = 0; 599 if (RenderBox* spinRenderer = m_outerSpinButton ? m_outerSpinButton->renderBox() : 0) { 600 spinRenderer->computeLogicalWidth(); 601 width += spinRenderer->minPreferredLogicalWidth() + spinRenderer->marginLeft() + spinRenderer->marginRight(); 602 } 603 if (width > 0) 604 width += paddingRight() + borderRight(); 605 return width; 606 } 607 608 void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineHeight) 609 { 610 if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) { 611 resultsRenderer->computeLogicalHeight(); 612 setHeight(max(height(), 613 resultsRenderer->borderTop() + resultsRenderer->borderBottom() + 614 resultsRenderer->paddingTop() + resultsRenderer->paddingBottom() + 615 resultsRenderer->marginTop() + resultsRenderer->marginBottom())); 616 lineHeight = max(lineHeight, resultsRenderer->height()); 617 } 618 if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) { 619 cancelRenderer->computeLogicalHeight(); 620 setHeight(max(height(), 621 cancelRenderer->borderTop() + cancelRenderer->borderBottom() + 622 cancelRenderer->paddingTop() + cancelRenderer->paddingBottom() + 623 cancelRenderer->marginTop() + cancelRenderer->marginBottom())); 624 lineHeight = max(lineHeight, cancelRenderer->height()); 625 } 626 627 setHeight(height() + lineHeight); 628 } 629 630 void RenderTextControlSingleLine::createSubtreeIfNeeded() 631 { 632 if (inputElement()->isSearchField()) { 633 if (!m_innerBlock) { 634 // Create the inner block element 635 m_innerBlock = TextControlInnerElement::create(toHTMLElement(node())); 636 m_innerBlock->attachInnerElement(node(), createInnerBlockStyle(style()), renderArena()); 637 } 638 639 #if ENABLE(INPUT_SPEECH) 640 if (inputElement()->isSpeechEnabled() && !m_speechButton) { 641 // Create the speech button element. 642 m_speechButton = InputFieldSpeechButtonElement::create(toHTMLElement(node())); 643 m_speechButton->attachInnerElement(node(), createSpeechButtonStyle(), renderArena()); 644 } 645 #endif 646 647 if (!m_resultsButton) { 648 // Create the search results button element. 649 m_resultsButton = SearchFieldResultsButtonElement::create(document()); 650 m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena()); 651 } 652 653 // Create innerText element before adding the other buttons. 654 RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get()); 655 656 if (!m_cancelButton) { 657 // Create the cancel button element. 658 m_cancelButton = SearchFieldCancelButtonElement::create(document()); 659 m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena()); 660 } 661 } else { 662 RenderTextControl::createSubtreeIfNeeded(0); 663 664 #if ENABLE(INPUT_SPEECH) 665 if (inputElement()->isSpeechEnabled() && !m_speechButton) { 666 // Create the speech button element. 667 m_speechButton = InputFieldSpeechButtonElement::create(toHTMLElement(node())); 668 m_speechButton->attachInnerElement(node(), createSpeechButtonStyle(), renderArena()); 669 } 670 #endif 671 672 bool hasSpinButton = inputElement()->hasSpinButton(); 673 674 if (hasSpinButton && !m_innerSpinButton) { 675 m_innerSpinButton = SpinButtonElement::create(toHTMLElement(node())); 676 m_innerSpinButton->attachInnerElement(node(), createInnerSpinButtonStyle(), renderArena()); 677 } 678 if (hasSpinButton && !m_outerSpinButton) { 679 m_outerSpinButton = SpinButtonElement::create(toHTMLElement(node())); 680 m_outerSpinButton->attachInnerElement(node(), createOuterSpinButtonStyle(), renderArena()); 681 } 682 } 683 } 684 685 void RenderTextControlSingleLine::updateFromElement() 686 { 687 createSubtreeIfNeeded(); 688 RenderTextControl::updateFromElement(); 689 690 if (m_cancelButton) 691 updateCancelButtonVisibility(); 692 693 if (!inputElement()->suggestedValue().isNull()) 694 setInnerTextValue(inputElement()->suggestedValue()); 695 else { 696 if (node()->hasTagName(inputTag)) { 697 // For HTMLInputElement, update the renderer value if the formControlValueMatchesRenderer() 698 // flag is false. It protects an unacceptable renderer value from 699 // being overwritten with the DOM value. 700 if (!static_cast<HTMLInputElement*>(node())->formControlValueMatchesRenderer()) 701 setInnerTextValue(inputElement()->visibleValue()); 702 } 703 } 704 705 if (m_searchPopupIsVisible) 706 m_searchPopup->popupMenu()->updateFromElement(); 707 } 708 709 void RenderTextControlSingleLine::cacheSelection(int start, int end) 710 { 711 inputElement()->cacheSelection(start, end); 712 } 713 714 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const 715 { 716 RefPtr<RenderStyle> textBlockStyle = RenderStyle::create(); 717 textBlockStyle->inheritFrom(startStyle); 718 adjustInnerTextStyle(startStyle, textBlockStyle.get()); 719 720 textBlockStyle->setWhiteSpace(PRE); 721 textBlockStyle->setWordWrap(NormalWordWrap); 722 textBlockStyle->setOverflowX(OHIDDEN); 723 textBlockStyle->setOverflowY(OHIDDEN); 724 725 // Do not allow line-height to be smaller than our default. 726 if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes)) 727 textBlockStyle->setLineHeight(Length(-100.0f, Percent)); 728 729 WebCore::EDisplay display = (m_innerBlock || inputElement()->hasSpinButton() ? INLINE_BLOCK : BLOCK); 730 #if ENABLE(INPUT_SPEECH) 731 if (inputElement()->isSpeechEnabled()) 732 display = INLINE_BLOCK; 733 #endif 734 textBlockStyle->setDisplay(display); 735 736 // We're adding one extra pixel of padding to match WinIE. 737 textBlockStyle->setPaddingLeft(Length(1, Fixed)); 738 textBlockStyle->setPaddingRight(Length(1, Fixed)); 739 740 return textBlockStyle.release(); 741 } 742 743 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const 744 { 745 ASSERT(node()->isHTMLElement()); 746 747 RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create(); 748 innerBlockStyle->inheritFrom(startStyle); 749 750 innerBlockStyle->setDisplay(BLOCK); 751 innerBlockStyle->setDirection(LTR); 752 753 // 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. 754 innerBlockStyle->setUserModify(READ_ONLY); 755 756 return innerBlockStyle.release(); 757 } 758 759 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createResultsButtonStyle(const RenderStyle* startStyle) const 760 { 761 ASSERT(node()->isHTMLElement()); 762 HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); 763 764 RefPtr<RenderStyle> resultsBlockStyle; 765 if (input->maxResults() < 0) 766 resultsBlockStyle = getCachedPseudoStyle(SEARCH_DECORATION); 767 else if (!input->maxResults()) 768 resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_DECORATION); 769 else 770 resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_BUTTON); 771 772 if (!resultsBlockStyle) 773 resultsBlockStyle = RenderStyle::create(); 774 775 if (startStyle) 776 resultsBlockStyle->inheritFrom(startStyle); 777 778 return resultsBlockStyle.release(); 779 } 780 781 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createCancelButtonStyle(const RenderStyle* startStyle) const 782 { 783 ASSERT(node()->isHTMLElement()); 784 RefPtr<RenderStyle> cancelBlockStyle; 785 786 if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(SEARCH_CANCEL_BUTTON)) 787 // We may be sharing style with another search field, but we must not share the cancel button style. 788 cancelBlockStyle = RenderStyle::clone(pseudoStyle.get()); 789 else 790 cancelBlockStyle = RenderStyle::create(); 791 792 if (startStyle) 793 cancelBlockStyle->inheritFrom(startStyle); 794 795 cancelBlockStyle->setVisibility(visibilityForCancelButton()); 796 return cancelBlockStyle.release(); 797 } 798 799 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerSpinButtonStyle() const 800 { 801 ASSERT(node()->isHTMLElement()); 802 RefPtr<RenderStyle> buttonStyle = getCachedPseudoStyle(INNER_SPIN_BUTTON); 803 if (!buttonStyle) 804 buttonStyle = RenderStyle::create(); 805 buttonStyle->inheritFrom(style()); 806 return buttonStyle.release(); 807 } 808 809 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createOuterSpinButtonStyle() const 810 { 811 ASSERT(node()->isHTMLElement()); 812 RefPtr<RenderStyle> buttonStyle = getCachedPseudoStyle(OUTER_SPIN_BUTTON); 813 if (!buttonStyle) 814 buttonStyle = RenderStyle::create(); 815 buttonStyle->inheritFrom(style()); 816 return buttonStyle.release(); 817 } 818 819 #if ENABLE(INPUT_SPEECH) 820 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createSpeechButtonStyle() const 821 { 822 ASSERT(node()->isHTMLElement()); 823 RefPtr<RenderStyle> buttonStyle = getCachedPseudoStyle(INPUT_SPEECH_BUTTON); 824 if (!buttonStyle) 825 buttonStyle = RenderStyle::create(); 826 buttonStyle->inheritFrom(style()); 827 return buttonStyle.release(); 828 } 829 #endif 830 831 void RenderTextControlSingleLine::updateCancelButtonVisibility() const 832 { 833 if (!m_cancelButton->renderer()) 834 return; 835 836 const RenderStyle* curStyle = m_cancelButton->renderer()->style(); 837 EVisibility buttonVisibility = visibilityForCancelButton(); 838 if (curStyle->visibility() == buttonVisibility) 839 return; 840 841 RefPtr<RenderStyle> cancelButtonStyle = RenderStyle::clone(curStyle); 842 cancelButtonStyle->setVisibility(buttonVisibility); 843 m_cancelButton->renderer()->setStyle(cancelButtonStyle); 844 } 845 846 EVisibility RenderTextControlSingleLine::visibilityForCancelButton() const 847 { 848 ASSERT(node()->isHTMLElement()); 849 HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); 850 return input->value().isEmpty() ? HIDDEN : VISIBLE; 851 } 852 853 const AtomicString& RenderTextControlSingleLine::autosaveName() const 854 { 855 return static_cast<Element*>(node())->getAttribute(autosaveAttr); 856 } 857 858 void RenderTextControlSingleLine::startSearchEventTimer() 859 { 860 ASSERT(node()->isHTMLElement()); 861 unsigned length = text().length(); 862 863 // If there's no text, fire the event right away. 864 if (!length) { 865 stopSearchEventTimer(); 866 static_cast<HTMLInputElement*>(node())->onSearch(); 867 return; 868 } 869 870 // After typing the first key, we wait 0.5 seconds. 871 // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on. 872 m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length)); 873 } 874 875 void RenderTextControlSingleLine::searchEventTimerFired(Timer<RenderTextControlSingleLine>*) 876 { 877 ASSERT(node()->isHTMLElement()); 878 static_cast<HTMLInputElement*>(node())->onSearch(); 879 } 880 881 // PopupMenuClient methods 882 void RenderTextControlSingleLine::valueChanged(unsigned listIndex, bool fireEvents) 883 { 884 ASSERT(node()->isHTMLElement()); 885 ASSERT(static_cast<int>(listIndex) < listSize()); 886 HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); 887 if (static_cast<int>(listIndex) == (listSize() - 1)) { 888 if (fireEvents) { 889 m_recentSearches.clear(); 890 const AtomicString& name = autosaveName(); 891 if (!name.isEmpty()) { 892 if (!m_searchPopup) 893 m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this); 894 m_searchPopup->saveRecentSearches(name, m_recentSearches); 895 } 896 } 897 } else { 898 input->setValue(itemText(listIndex)); 899 if (fireEvents) 900 input->onSearch(); 901 input->select(); 902 } 903 } 904 905 String RenderTextControlSingleLine::itemText(unsigned listIndex) const 906 { 907 int size = listSize(); 908 if (size == 1) { 909 ASSERT(!listIndex); 910 return searchMenuNoRecentSearchesText(); 911 } 912 if (!listIndex) 913 return searchMenuRecentSearchesText(); 914 if (itemIsSeparator(listIndex)) 915 return String(); 916 if (static_cast<int>(listIndex) == (size - 1)) 917 return searchMenuClearRecentSearchesText(); 918 return m_recentSearches[listIndex - 1]; 919 } 920 921 String RenderTextControlSingleLine::itemLabel(unsigned) const 922 { 923 return String(); 924 } 925 926 String RenderTextControlSingleLine::itemIcon(unsigned) const 927 { 928 return String(); 929 } 930 931 bool RenderTextControlSingleLine::itemIsEnabled(unsigned listIndex) const 932 { 933 if (!listIndex || itemIsSeparator(listIndex)) 934 return false; 935 return true; 936 } 937 938 PopupMenuStyle RenderTextControlSingleLine::itemStyle(unsigned) const 939 { 940 return menuStyle(); 941 } 942 943 PopupMenuStyle RenderTextControlSingleLine::menuStyle() const 944 { 945 return PopupMenuStyle(style()->visitedDependentColor(CSSPropertyColor), style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->font(), style()->visibility() == VISIBLE, style()->display() == NONE, style()->textIndent(), style()->direction(), style()->unicodeBidi() == Override); 946 } 947 948 int RenderTextControlSingleLine::clientInsetLeft() const 949 { 950 // Inset the menu by the radius of the cap on the left so that 951 // it only runs along the straight part of the bezel. 952 return height() / 2; 953 } 954 955 int RenderTextControlSingleLine::clientInsetRight() const 956 { 957 // Inset the menu by the radius of the cap on the right so that 958 // it only runs along the straight part of the bezel (unless it needs 959 // to be wider). 960 return height() / 2; 961 } 962 963 int RenderTextControlSingleLine::clientPaddingLeft() const 964 { 965 int padding = paddingLeft(); 966 967 if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) 968 padding += resultsRenderer->width() + resultsRenderer->marginLeft() + resultsRenderer->paddingLeft() + resultsRenderer->marginRight() + resultsRenderer->paddingRight(); 969 970 return padding; 971 } 972 973 int RenderTextControlSingleLine::clientPaddingRight() const 974 { 975 int padding = paddingRight(); 976 977 if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) 978 padding += cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->paddingLeft() + cancelRenderer->marginRight() + cancelRenderer->paddingRight(); 979 980 return padding; 981 } 982 983 int RenderTextControlSingleLine::listSize() const 984 { 985 // If there are no recent searches, then our menu will have 1 "No recent searches" item. 986 if (!m_recentSearches.size()) 987 return 1; 988 // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item. 989 return m_recentSearches.size() + 3; 990 } 991 992 int RenderTextControlSingleLine::selectedIndex() const 993 { 994 return -1; 995 } 996 997 void RenderTextControlSingleLine::popupDidHide() 998 { 999 m_searchPopupIsVisible = false; 1000 } 1001 1002 bool RenderTextControlSingleLine::itemIsSeparator(unsigned listIndex) const 1003 { 1004 // The separator will be the second to last item in our list. 1005 return static_cast<int>(listIndex) == (listSize() - 2); 1006 } 1007 1008 bool RenderTextControlSingleLine::itemIsLabel(unsigned listIndex) const 1009 { 1010 return listIndex == 0; 1011 } 1012 1013 bool RenderTextControlSingleLine::itemIsSelected(unsigned) const 1014 { 1015 return false; 1016 } 1017 1018 void RenderTextControlSingleLine::setTextFromItem(unsigned listIndex) 1019 { 1020 ASSERT(node()->isHTMLElement()); 1021 static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex)); 1022 } 1023 1024 FontSelector* RenderTextControlSingleLine::fontSelector() const 1025 { 1026 return document()->styleSelector()->fontSelector(); 1027 } 1028 1029 HostWindow* RenderTextControlSingleLine::hostWindow() const 1030 { 1031 return document()->view()->hostWindow(); 1032 } 1033 1034 void RenderTextControlSingleLine::autoscroll() 1035 { 1036 RenderLayer* layer = innerTextElement()->renderBox()->layer(); 1037 if (layer) 1038 layer->autoscroll(); 1039 } 1040 1041 int RenderTextControlSingleLine::scrollWidth() const 1042 { 1043 if (innerTextElement()) 1044 return innerTextElement()->scrollWidth(); 1045 return RenderBlock::scrollWidth(); 1046 } 1047 1048 int RenderTextControlSingleLine::scrollHeight() const 1049 { 1050 if (innerTextElement()) 1051 return innerTextElement()->scrollHeight(); 1052 return RenderBlock::scrollHeight(); 1053 } 1054 1055 int RenderTextControlSingleLine::scrollLeft() const 1056 { 1057 if (innerTextElement()) 1058 return innerTextElement()->scrollLeft(); 1059 return RenderBlock::scrollLeft(); 1060 } 1061 1062 int RenderTextControlSingleLine::scrollTop() const 1063 { 1064 if (innerTextElement()) 1065 return innerTextElement()->scrollTop(); 1066 return RenderBlock::scrollTop(); 1067 } 1068 1069 void RenderTextControlSingleLine::setScrollLeft(int newLeft) 1070 { 1071 if (innerTextElement()) 1072 innerTextElement()->setScrollLeft(newLeft); 1073 } 1074 1075 void RenderTextControlSingleLine::setScrollTop(int newTop) 1076 { 1077 if (innerTextElement()) 1078 innerTextElement()->setScrollTop(newTop); 1079 } 1080 1081 bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 1082 { 1083 RenderLayer* layer = innerTextElement()->renderBox()->layer(); 1084 if (layer && layer->scroll(direction, granularity, multiplier)) 1085 return true; 1086 return RenderBlock::scroll(direction, granularity, multiplier, stopNode); 1087 } 1088 1089 bool RenderTextControlSingleLine::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 1090 { 1091 RenderLayer* layer = innerTextElement()->renderBox()->layer(); 1092 if (layer && layer->scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier)) 1093 return true; 1094 return RenderBlock::logicalScroll(direction, granularity, multiplier, stopNode); 1095 } 1096 1097 PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) 1098 { 1099 RefPtr<Scrollbar> widget; 1100 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR); 1101 if (hasCustomScrollbarStyle) 1102 widget = RenderScrollbar::createCustomScrollbar(scrollableArea, orientation, this); 1103 else 1104 widget = Scrollbar::createNativeScrollbar(scrollableArea, orientation, controlSize); 1105 return widget.release(); 1106 } 1107 1108 InputElement* RenderTextControlSingleLine::inputElement() const 1109 { 1110 return node()->toInputElement(); 1111 } 1112 1113 int RenderTextControlSingleLine::textBlockInsetLeft() const 1114 { 1115 int inset = borderLeft() + clientPaddingLeft(); 1116 if (HTMLElement* innerText = innerTextElement()) { 1117 if (RenderBox* innerTextRenderer = innerText->renderBox()) 1118 inset += innerTextRenderer->paddingLeft(); 1119 } 1120 return inset; 1121 } 1122 1123 int RenderTextControlSingleLine::textBlockInsetRight() const 1124 { 1125 int inset = borderRight() + clientPaddingRight(); 1126 if (HTMLElement* innerText = innerTextElement()) { 1127 if (RenderBox* innerTextRenderer = innerText->renderBox()) 1128 inset += innerTextRenderer->paddingRight(); 1129 } 1130 return inset; 1131 } 1132 1133 int RenderTextControlSingleLine::textBlockInsetTop() const 1134 { 1135 RenderBox* innerRenderer = 0; 1136 if (m_innerBlock) 1137 innerRenderer = m_innerBlock->renderBox(); 1138 else if (HTMLElement* innerText = innerTextElement()) 1139 innerRenderer = innerText->renderBox(); 1140 1141 if (innerRenderer) 1142 return innerRenderer->y(); 1143 1144 return borderTop() + paddingTop(); 1145 } 1146 1147 } 1148