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