Home | History | Annotate | Download | only in rendering
      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 "core/rendering/RenderTextControlSingleLine.h"
     26 
     27 #include "CSSValueKeywords.h"
     28 #include "core/dom/shadow/ShadowRoot.h"
     29 #include "core/editing/FrameSelection.h"
     30 #include "core/html/shadow/ShadowElementNames.h"
     31 #include "core/frame/Frame.h"
     32 #include "core/rendering/HitTestResult.h"
     33 #include "core/rendering/LayoutRectRecorder.h"
     34 #include "core/rendering/RenderLayer.h"
     35 #include "core/rendering/RenderTheme.h"
     36 #include "platform/PlatformKeyboardEvent.h"
     37 #include "platform/fonts/SimpleFontData.h"
     38 
     39 using namespace std;
     40 
     41 namespace WebCore {
     42 
     43 using namespace HTMLNames;
     44 
     45 RenderTextControlSingleLine::RenderTextControlSingleLine(HTMLInputElement* element)
     46     : RenderTextControl(element)
     47     , m_shouldDrawCapsLockIndicator(false)
     48     , m_desiredInnerTextLogicalHeight(-1)
     49 {
     50     ASSERT(element->hasTagName(inputTag));
     51 }
     52 
     53 RenderTextControlSingleLine::~RenderTextControlSingleLine()
     54 {
     55 }
     56 
     57 inline Element* RenderTextControlSingleLine::containerElement() const
     58 {
     59     return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
     60 }
     61 
     62 inline Element* RenderTextControlSingleLine::editingViewPortElement() const
     63 {
     64     return inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::editingViewPort());
     65 }
     66 
     67 inline HTMLElement* RenderTextControlSingleLine::innerSpinButtonElement() const
     68 {
     69     return toHTMLElement(inputElement()->userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
     70 }
     71 
     72 void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
     73 {
     74     RenderTextControl::paint(paintInfo, paintOffset);
     75 
     76     if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
     77         LayoutRect contentsRect = contentBoxRect();
     78 
     79         // Center in the block progression direction.
     80         if (isHorizontalWritingMode())
     81             contentsRect.setY((height() - contentsRect.height()) / 2);
     82         else
     83             contentsRect.setX((width() - contentsRect.width()) / 2);
     84 
     85         // Convert the rect into the coords used for painting the content
     86         contentsRect.moveBy(paintOffset + location());
     87         RenderTheme::theme().paintCapsLockIndicator(this, paintInfo, pixelSnappedIntRect(contentsRect));
     88     }
     89 }
     90 
     91 LayoutUnit RenderTextControlSingleLine::computeLogicalHeightLimit() const
     92 {
     93     return containerElement() ? contentLogicalHeight() : logicalHeight();
     94 }
     95 
     96 void RenderTextControlSingleLine::layout()
     97 {
     98     LayoutRectRecorder recorder(*this);
     99     SubtreeLayoutScope layoutScope(this);
    100 
    101     // FIXME: We should remove the height-related hacks in layout() and
    102     // styleDidChange(). We need them because
    103     // - Center the inner elements vertically if the input height is taller than
    104     //   the intrinsic height of the inner elements.
    105     // - Shrink the inner elment heights if the input height is samller than the
    106     //   intrinsic heights of the inner elements.
    107 
    108     // We don't honor paddings and borders for textfields without decorations
    109     // and type=search if the text height is taller than the contentHeight()
    110     // because of compability.
    111 
    112     RenderBox* innerTextRenderer = innerTextElement()->renderBox();
    113     RenderBox* viewPortRenderer = editingViewPortElement() ? editingViewPortElement()->renderBox() : 0;
    114 
    115     // To ensure consistency between layouts, we need to reset any conditionally overriden height.
    116     if (innerTextRenderer && !innerTextRenderer->style()->logicalHeight().isAuto()) {
    117         innerTextRenderer->style()->setLogicalHeight(Length(Auto));
    118         layoutScope.setNeedsLayout(innerTextRenderer);
    119     }
    120     if (viewPortRenderer && !viewPortRenderer->style()->logicalHeight().isAuto()) {
    121         viewPortRenderer->style()->setLogicalHeight(Length(Auto));
    122         layoutScope.setNeedsLayout(viewPortRenderer);
    123     }
    124 
    125     RenderBlockFlow::layoutBlock(false);
    126 
    127     Element* container = containerElement();
    128     RenderBox* containerRenderer = container ? container->renderBox() : 0;
    129 
    130     // Set the text block height
    131     LayoutUnit desiredLogicalHeight = textBlockLogicalHeight();
    132     LayoutUnit logicalHeightLimit = computeLogicalHeightLimit();
    133     if (innerTextRenderer && innerTextRenderer->logicalHeight() > logicalHeightLimit) {
    134         if (desiredLogicalHeight != innerTextRenderer->logicalHeight())
    135             layoutScope.setNeedsLayout(this);
    136 
    137         m_desiredInnerTextLogicalHeight = desiredLogicalHeight;
    138 
    139         innerTextRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed));
    140         layoutScope.setNeedsLayout(innerTextRenderer);
    141         if (viewPortRenderer) {
    142             viewPortRenderer->style()->setLogicalHeight(Length(desiredLogicalHeight, Fixed));
    143             layoutScope.setNeedsLayout(viewPortRenderer);
    144         }
    145     }
    146     // The container might be taller because of decoration elements.
    147     if (containerRenderer) {
    148         containerRenderer->layoutIfNeeded();
    149         LayoutUnit containerLogicalHeight = containerRenderer->logicalHeight();
    150         if (containerLogicalHeight > logicalHeightLimit) {
    151             containerRenderer->style()->setLogicalHeight(Length(logicalHeightLimit, Fixed));
    152             layoutScope.setNeedsLayout(this);
    153         } else if (containerRenderer->logicalHeight() < contentLogicalHeight()) {
    154             containerRenderer->style()->setLogicalHeight(Length(contentLogicalHeight(), Fixed));
    155             layoutScope.setNeedsLayout(this);
    156         } else
    157             containerRenderer->style()->setLogicalHeight(Length(containerLogicalHeight, Fixed));
    158     }
    159 
    160     // If we need another layout pass, we have changed one of children's height so we need to relayout them.
    161     if (needsLayout())
    162         RenderBlockFlow::layoutBlock(true);
    163 
    164     // Center the child block in the block progression direction (vertical centering for horizontal text fields).
    165     if (!container && innerTextRenderer && innerTextRenderer->height() != contentLogicalHeight()) {
    166         LayoutUnit logicalHeightDiff = innerTextRenderer->logicalHeight() - contentLogicalHeight();
    167         innerTextRenderer->setLogicalTop(innerTextRenderer->logicalTop() - (logicalHeightDiff / 2 + layoutMod(logicalHeightDiff, 2)));
    168     } else
    169         centerContainerIfNeeded(containerRenderer);
    170 
    171     // Ignores the paddings for the inner spin button.
    172     if (RenderBox* innerSpinBox = innerSpinButtonElement() ? innerSpinButtonElement()->renderBox() : 0) {
    173         RenderBox* parentBox = innerSpinBox->parentBox();
    174         if (containerRenderer && !containerRenderer->style()->isLeftToRightDirection())
    175             innerSpinBox->setLogicalLocation(LayoutPoint(-paddingLogicalLeft(), -paddingBefore()));
    176         else
    177             innerSpinBox->setLogicalLocation(LayoutPoint(parentBox->logicalWidth() - innerSpinBox->logicalWidth() + paddingLogicalRight(), -paddingBefore()));
    178         innerSpinBox->setLogicalHeight(logicalHeight() - borderBefore() - borderAfter());
    179     }
    180 
    181     HTMLElement* placeholderElement = inputElement()->placeholderElement();
    182     if (RenderBox* placeholderBox = placeholderElement ? placeholderElement->renderBox() : 0) {
    183         LayoutSize innerTextSize;
    184         if (innerTextRenderer)
    185             innerTextSize = innerTextRenderer->size();
    186         placeholderBox->style()->setWidth(Length(innerTextSize.width() - placeholderBox->borderAndPaddingWidth(), Fixed));
    187         placeholderBox->style()->setHeight(Length(innerTextSize.height() - placeholderBox->borderAndPaddingHeight(), Fixed));
    188         bool neededLayout = placeholderBox->needsLayout();
    189         bool placeholderBoxHadLayout = placeholderBox->everHadLayout();
    190         placeholderBox->layoutIfNeeded();
    191         LayoutPoint textOffset;
    192         if (innerTextRenderer)
    193             textOffset = innerTextRenderer->location();
    194         if (editingViewPortElement() && editingViewPortElement()->renderBox())
    195             textOffset += toLayoutSize(editingViewPortElement()->renderBox()->location());
    196         if (containerRenderer)
    197             textOffset += toLayoutSize(containerRenderer->location());
    198         placeholderBox->setLocation(textOffset);
    199 
    200         if (!placeholderBoxHadLayout && placeholderBox->checkForRepaintDuringLayout()) {
    201             // This assumes a shadow tree without floats. If floats are added, the
    202             // logic should be shared with RenderBlock::layoutBlockChild.
    203             placeholderBox->repaint();
    204         }
    205         // The placeholder gets layout last, after the parent text control and its other children,
    206         // so in order to get the correct overflow from the placeholder we need to recompute it now.
    207         if (neededLayout)
    208             computeOverflow(clientLogicalBottom());
    209     }
    210 }
    211 
    212 bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
    213 {
    214     if (!RenderTextControl::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
    215         return false;
    216 
    217     // Say that we hit the inner text element if
    218     //  - we hit a node inside the inner text element,
    219     //  - we hit the <input> element (e.g. we're over the border or padding), or
    220     //  - we hit regions not in any decoration buttons.
    221     Element* container = containerElement();
    222     if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node() || (container && container == result.innerNode())) {
    223         LayoutPoint pointInParent = locationInContainer.point();
    224         if (container && editingViewPortElement()) {
    225             if (editingViewPortElement()->renderBox())
    226                 pointInParent -= toLayoutSize(editingViewPortElement()->renderBox()->location());
    227             if (container->renderBox())
    228                 pointInParent -= toLayoutSize(container->renderBox()->location());
    229         }
    230         hitInnerTextElement(result, pointInParent, accumulatedOffset);
    231     }
    232     return true;
    233 }
    234 
    235 void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
    236 {
    237     m_desiredInnerTextLogicalHeight = -1;
    238     RenderTextControl::styleDidChange(diff, oldStyle);
    239 
    240     // We may have set the width and the height in the old style in layout().
    241     // Reset them now to avoid getting a spurious layout hint.
    242     Element* viewPort = editingViewPortElement();
    243     if (RenderObject* viewPortRenderer = viewPort ? viewPort->renderer() : 0) {
    244         viewPortRenderer->style()->setHeight(Length());
    245         viewPortRenderer->style()->setWidth(Length());
    246     }
    247     Element* container = containerElement();
    248     if (RenderObject* containerRenderer = container ? container->renderer() : 0) {
    249         containerRenderer->style()->setHeight(Length());
    250         containerRenderer->style()->setWidth(Length());
    251     }
    252     RenderObject* innerTextRenderer = innerTextElement()->renderer();
    253     if (innerTextRenderer && diff == StyleDifferenceLayout)
    254         innerTextRenderer->setNeedsLayout();
    255     if (HTMLElement* placeholder = inputElement()->placeholderElement())
    256         placeholder->setInlineStyleProperty(CSSPropertyTextOverflow, textShouldBeTruncated() ? CSSValueEllipsis : CSSValueClip);
    257     setHasOverflowClip(false);
    258 }
    259 
    260 void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
    261 {
    262     if (!node())
    263         return;
    264 
    265     // Only draw the caps lock indicator if these things are true:
    266     // 1) The field is a password field
    267     // 2) The frame is active
    268     // 3) The element is focused
    269     // 4) The caps lock is on
    270     bool shouldDrawCapsLockIndicator = false;
    271 
    272     if (Frame* frame = document().frame())
    273         shouldDrawCapsLockIndicator = inputElement()->isPasswordField() && frame->selection().isFocusedAndActive() && document().focusedElement() == node() && PlatformKeyboardEvent::currentCapsLockState();
    274 
    275     if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
    276         m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
    277         repaint();
    278     }
    279 }
    280 
    281 bool RenderTextControlSingleLine::hasControlClip() const
    282 {
    283     // Apply control clip for text fields with decorations.
    284     return !!containerElement();
    285 }
    286 
    287 LayoutRect RenderTextControlSingleLine::controlClipRect(const LayoutPoint& additionalOffset) const
    288 {
    289     ASSERT(hasControlClip());
    290     LayoutRect clipRect = contentBoxRect();
    291     if (containerElement()->renderBox())
    292         clipRect = unionRect(clipRect, containerElement()->renderBox()->frameRect());
    293     clipRect.moveBy(additionalOffset);
    294     return clipRect;
    295 }
    296 
    297 float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
    298 {
    299     // Since Lucida Grande is the default font, we want this to match the width
    300     // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
    301     // IE for some encodings (in IE, the default font is encoding specific).
    302     // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg.
    303     if (family == "Lucida Grande")
    304         return scaleEmToUnits(901);
    305 
    306     return RenderTextControl::getAvgCharWidth(family);
    307 }
    308 
    309 LayoutUnit RenderTextControlSingleLine::preferredContentLogicalWidth(float charWidth) const
    310 {
    311     int factor;
    312     bool includesDecoration = inputElement()->sizeShouldIncludeDecoration(factor);
    313     if (factor <= 0)
    314         factor = 20;
    315 
    316     LayoutUnit result = LayoutUnit::fromFloatCeil(charWidth * factor);
    317 
    318     float maxCharWidth = 0.f;
    319     AtomicString family = style()->font().family().family();
    320     // Since Lucida Grande is the default font, we want this to match the width
    321     // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
    322     // IE for some encodings (in IE, the default font is encoding specific).
    323     // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg.
    324     if (family == "Lucida Grande")
    325         maxCharWidth = scaleEmToUnits(4027);
    326     else if (hasValidAvgCharWidth(family))
    327         maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth());
    328 
    329     // For text inputs, IE adds some extra width.
    330     if (maxCharWidth > 0.f)
    331         result += maxCharWidth - charWidth;
    332 
    333     if (includesDecoration) {
    334         HTMLElement* spinButton = innerSpinButtonElement();
    335         if (RenderBox* spinRenderer = spinButton ? spinButton->renderBox() : 0) {
    336             result += spinRenderer->borderAndPaddingLogicalWidth();
    337             // Since the width of spinRenderer is not calculated yet, spinRenderer->logicalWidth() returns 0.
    338             // So computedStyle()->logicalWidth() is used instead.
    339             result += spinButton->computedStyle()->logicalWidth().value();
    340         }
    341     }
    342 
    343     return result;
    344 }
    345 
    346 LayoutUnit RenderTextControlSingleLine::computeControlLogicalHeight(LayoutUnit lineHeight, LayoutUnit nonContentHeight) const
    347 {
    348     return lineHeight + nonContentHeight;
    349 }
    350 
    351 void RenderTextControlSingleLine::updateFromElement()
    352 {
    353     RenderTextControl::updateFromElement();
    354 }
    355 
    356 PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
    357 {
    358     RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
    359     textBlockStyle->inheritFrom(startStyle);
    360     adjustInnerTextStyle(textBlockStyle.get());
    361 
    362     textBlockStyle->setWhiteSpace(PRE);
    363     textBlockStyle->setOverflowWrap(NormalOverflowWrap);
    364     textBlockStyle->setOverflowX(OHIDDEN);
    365     textBlockStyle->setOverflowY(OHIDDEN);
    366     textBlockStyle->setTextOverflow(textShouldBeTruncated() ? TextOverflowEllipsis : TextOverflowClip);
    367 
    368     if (m_desiredInnerTextLogicalHeight >= 0)
    369         textBlockStyle->setLogicalHeight(Length(m_desiredInnerTextLogicalHeight, Fixed));
    370     // Do not allow line-height to be smaller than our default.
    371     if (textBlockStyle->fontMetrics().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes))
    372         textBlockStyle->setLineHeight(RenderStyle::initialLineHeight());
    373 
    374     textBlockStyle->setDisplay(BLOCK);
    375     textBlockStyle->setUnique();
    376 
    377     return textBlockStyle.release();
    378 }
    379 
    380 bool RenderTextControlSingleLine::textShouldBeTruncated() const
    381 {
    382     return document().focusedElement() != node() && style()->textOverflow() == TextOverflowEllipsis;
    383 }
    384 
    385 void RenderTextControlSingleLine::autoscroll(const IntPoint& position)
    386 {
    387     RenderBox* renderer = innerTextElement()->renderBox();
    388     if (!renderer)
    389         return;
    390 
    391     renderer->autoscroll(position);
    392 }
    393 
    394 int RenderTextControlSingleLine::scrollWidth() const
    395 {
    396     if (innerTextElement())
    397         return innerTextElement()->scrollWidth();
    398     return RenderBlock::scrollWidth();
    399 }
    400 
    401 int RenderTextControlSingleLine::scrollHeight() const
    402 {
    403     if (innerTextElement())
    404         return innerTextElement()->scrollHeight();
    405     return RenderBlock::scrollHeight();
    406 }
    407 
    408 int RenderTextControlSingleLine::scrollLeft() const
    409 {
    410     if (innerTextElement())
    411         return innerTextElement()->scrollLeft();
    412     return RenderBlock::scrollLeft();
    413 }
    414 
    415 int RenderTextControlSingleLine::scrollTop() const
    416 {
    417     if (innerTextElement())
    418         return innerTextElement()->scrollTop();
    419     return RenderBlock::scrollTop();
    420 }
    421 
    422 void RenderTextControlSingleLine::setScrollLeft(int newLeft)
    423 {
    424     if (innerTextElement())
    425         innerTextElement()->setScrollLeft(newLeft);
    426 }
    427 
    428 void RenderTextControlSingleLine::setScrollTop(int newTop)
    429 {
    430     if (innerTextElement())
    431         innerTextElement()->setScrollTop(newTop);
    432 }
    433 
    434 HTMLInputElement* RenderTextControlSingleLine::inputElement() const
    435 {
    436     return toHTMLInputElement(node());
    437 }
    438 
    439 }
    440