Home | History | Annotate | Download | only in rendering
      1 /**
      2  * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
      3  *           (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  *
     20  */
     21 
     22 #include "config.h"
     23 #include "RenderTextControlSingleLine.h"
     24 
     25 #include "CSSStyleSelector.h"
     26 #include "Event.h"
     27 #include "EventNames.h"
     28 #include "Frame.h"
     29 #include "FrameView.h"
     30 #include "HitTestResult.h"
     31 #include "HTMLInputElement.h"
     32 #include "HTMLNames.h"
     33 #include "InputElement.h"
     34 #include "LocalizedStrings.h"
     35 #include "MouseEvent.h"
     36 #include "PlatformKeyboardEvent.h"
     37 #include "RenderScrollbar.h"
     38 #include "RenderTheme.h"
     39 #include "SearchPopupMenu.h"
     40 #include "SelectionController.h"
     41 #include "Settings.h"
     42 #include "SimpleFontData.h"
     43 #include "TextControlInnerElements.h"
     44 
     45 using namespace std;
     46 
     47 namespace WebCore {
     48 
     49 using namespace HTMLNames;
     50 
     51 RenderTextControlSingleLine::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 
     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 
     71 RenderStyle* RenderTextControlSingleLine::textBaseStyle() const
     72 {
     73     return m_innerBlock ? m_innerBlock->renderer()->style() : style();
     74 }
     75 
     76 void 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 
    108 void RenderTextControlSingleLine::stopSearchEventTimer()
    109 {
    110     ASSERT(node()->isHTMLElement());
    111     m_searchEventTimer.stop();
    112 }
    113 
    114 void 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 
    144 void RenderTextControlSingleLine::hidePopup()
    145 {
    146     ASSERT(node()->isHTMLElement());
    147     if (m_searchPopup)
    148         m_searchPopup->hide();
    149 }
    150 
    151 void 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 
    181 void 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 
    194 void 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 
    252 bool 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 
    299 void 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 
    327 void 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 
    348 void 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 
    372 int 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 
    389 int 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 
    411 void 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 
    434 void 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 
    463 void 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 
    486 void RenderTextControlSingleLine::cacheSelection(int start, int end)
    487 {
    488     inputElement()->cacheSelection(start, end);
    489 }
    490 
    491 PassRefPtr<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 
    530 PassRefPtr<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 
    546 PassRefPtr<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 
    568 PassRefPtr<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 
    586 void 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 
    601 EVisibility RenderTextControlSingleLine::visibilityForCancelButton() const
    602 {
    603     ASSERT(node()->isHTMLElement());
    604     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
    605     return input->value().isEmpty() ? HIDDEN : VISIBLE;
    606 }
    607 
    608 const AtomicString& RenderTextControlSingleLine::autosaveName() const
    609 {
    610     return static_cast<Element*>(node())->getAttribute(autosaveAttr);
    611 }
    612 
    613 void 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 
    630 void RenderTextControlSingleLine::searchEventTimerFired(Timer<RenderTextControlSingleLine>*)
    631 {
    632     ASSERT(node()->isHTMLElement());
    633     static_cast<HTMLInputElement*>(node())->onSearch();
    634 }
    635 
    636 // PopupMenuClient methods
    637 void 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 
    660 String 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 
    676 bool RenderTextControlSingleLine::itemIsEnabled(unsigned listIndex) const
    677 {
    678      if (!listIndex || itemIsSeparator(listIndex))
    679         return false;
    680     return true;
    681 }
    682 
    683 PopupMenuStyle RenderTextControlSingleLine::itemStyle(unsigned) const
    684 {
    685     return menuStyle();
    686 }
    687 
    688 PopupMenuStyle RenderTextControlSingleLine::menuStyle() const
    689 {
    690     return PopupMenuStyle(style()->color(), style()->backgroundColor(), style()->font(), style()->visibility() == VISIBLE, style()->textIndent(), style()->direction());
    691 }
    692 
    693 int 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 
    700 int 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 
    708 int 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 
    718 int 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 
    728 int 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 
    737 int RenderTextControlSingleLine::selectedIndex() const
    738 {
    739     return -1;
    740 }
    741 
    742 void RenderTextControlSingleLine::popupDidHide()
    743 {
    744     m_searchPopupIsVisible = false;
    745 }
    746 
    747 bool 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 
    753 bool RenderTextControlSingleLine::itemIsLabel(unsigned listIndex) const
    754 {
    755     return listIndex == 0;
    756 }
    757 
    758 bool RenderTextControlSingleLine::itemIsSelected(unsigned) const
    759 {
    760     return false;
    761 }
    762 
    763 void RenderTextControlSingleLine::setTextFromItem(unsigned listIndex)
    764 {
    765     ASSERT(node()->isHTMLElement());
    766     static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex));
    767 }
    768 
    769 FontSelector* RenderTextControlSingleLine::fontSelector() const
    770 {
    771     return document()->styleSelector()->fontSelector();
    772 }
    773 
    774 HostWindow* RenderTextControlSingleLine::hostWindow() const
    775 {
    776     return document()->view()->hostWindow();
    777 }
    778 
    779 void RenderTextControlSingleLine::autoscroll()
    780 {
    781     RenderLayer* layer = innerTextElement()->renderBox()->layer();
    782     if (layer)
    783         layer->autoscroll();
    784 }
    785 
    786 int RenderTextControlSingleLine::scrollWidth() const
    787 {
    788     if (innerTextElement())
    789         return innerTextElement()->scrollWidth();
    790     return RenderBlock::scrollWidth();
    791 }
    792 
    793 int RenderTextControlSingleLine::scrollHeight() const
    794 {
    795     if (innerTextElement())
    796         return innerTextElement()->scrollHeight();
    797     return RenderBlock::scrollHeight();
    798 }
    799 
    800 int RenderTextControlSingleLine::scrollLeft() const
    801 {
    802     if (innerTextElement())
    803         return innerTextElement()->scrollLeft();
    804     return RenderBlock::scrollLeft();
    805 }
    806 
    807 int RenderTextControlSingleLine::scrollTop() const
    808 {
    809     if (innerTextElement())
    810         return innerTextElement()->scrollTop();
    811     return RenderBlock::scrollTop();
    812 }
    813 
    814 void RenderTextControlSingleLine::setScrollLeft(int newLeft)
    815 {
    816     if (innerTextElement())
    817         innerTextElement()->setScrollLeft(newLeft);
    818 }
    819 
    820 void RenderTextControlSingleLine::setScrollTop(int newTop)
    821 {
    822     if (innerTextElement())
    823         innerTextElement()->setScrollTop(newTop);
    824 }
    825 
    826 bool 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 
    834 PassRefPtr<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 
    845 InputElement* RenderTextControlSingleLine::inputElement() const
    846 {
    847     return toInputElement(static_cast<Element*>(node()));
    848 }
    849 
    850 }
    851