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