| /** |
| * Copyright (C) 2006, 2007, 2014 Apple Inc. All rights reserved. |
| * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| #include "RenderTextControl.h" |
| |
| #include "HTMLTextFormControlElement.h" |
| #include "HitTestResult.h" |
| #include "RenderText.h" |
| #include "RenderTextControlSingleLine.h" |
| #include "RenderTheme.h" |
| #include "ScrollbarTheme.h" |
| #include "StyleInheritedData.h" |
| #include "StyleProperties.h" |
| #include "TextControlInnerElements.h" |
| #include "VisiblePosition.h" |
| #include <wtf/IsoMallocInlines.h> |
| #include <wtf/unicode/CharacterNames.h> |
| |
| namespace WebCore { |
| |
| WTF_MAKE_ISO_ALLOCATED_IMPL(RenderTextControl); |
| WTF_MAKE_ISO_ALLOCATED_IMPL(RenderTextControlInnerContainer); |
| |
| RenderTextControl::RenderTextControl(HTMLTextFormControlElement& element, RenderStyle&& style) |
| : RenderBlockFlow(element, WTFMove(style)) |
| { |
| } |
| |
| RenderTextControl::~RenderTextControl() = default; |
| |
| HTMLTextFormControlElement& RenderTextControl::textFormControlElement() const |
| { |
| return downcast<HTMLTextFormControlElement>(nodeForNonAnonymous()); |
| } |
| |
| RefPtr<TextControlInnerTextElement> RenderTextControl::innerTextElement() const |
| { |
| return textFormControlElement().innerTextElement(); |
| } |
| |
| void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
| { |
| RenderBlockFlow::styleDidChange(diff, oldStyle); |
| auto innerText = innerTextElement(); |
| if (!innerText) |
| return; |
| RenderTextControlInnerBlock* innerTextRenderer = innerText->renderer(); |
| if (innerTextRenderer && oldStyle) { |
| // FIXME: The height property of the inner text block style may be mutated by RenderTextControlSingleLine::layout. |
| // See if the original has changed before setting it and triggering a layout. |
| auto newInnerTextStyle = textFormControlElement().createInnerTextStyle(style()); |
| auto oldInnerTextStyle = textFormControlElement().createInnerTextStyle(*oldStyle); |
| if (newInnerTextStyle != oldInnerTextStyle) |
| innerTextRenderer->setStyle(WTFMove(newInnerTextStyle)); |
| else if (diff == StyleDifference::RepaintIfText || diff == StyleDifference::Repaint) { |
| // Repaint is expected to be propagated down to the shadow tree when non-inherited style property changes |
| // (e.g. text-decoration-color) since that's where the value actually takes effect. |
| innerTextRenderer->repaint(); |
| } |
| } |
| textFormControlElement().updatePlaceholderVisibility(); |
| } |
| |
| int RenderTextControl::textBlockLogicalHeight() const |
| { |
| return logicalHeight() - borderAndPaddingLogicalHeight(); |
| } |
| |
| int RenderTextControl::textBlockLogicalWidth() const |
| { |
| auto innerText = innerTextElement(); |
| ASSERT(innerText); |
| |
| LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth(); |
| if (innerText->renderer()) |
| unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd(); |
| |
| return unitWidth; |
| } |
| |
| int RenderTextControl::scrollbarThickness() const |
| { |
| // FIXME: We should get the size of the scrollbar from the RenderTheme instead. |
| return ScrollbarTheme::theme().scrollbarThickness(); |
| } |
| |
| RenderBox::LogicalExtentComputedValues RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop) const |
| { |
| auto innerText = innerTextElement(); |
| ASSERT(innerText); |
| if (RenderBox* innerTextBox = innerText->renderBox()) { |
| LayoutUnit nonContentHeight = innerTextBox->verticalBorderAndPaddingExtent() + innerTextBox->verticalMarginExtent(); |
| logicalHeight = computeControlLogicalHeight(innerTextBox->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes), nonContentHeight); |
| |
| // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap. |
| if ((isHorizontalWritingMode() && (style().overflowX() == Overflow::Scroll || (style().overflowX() == Overflow::Auto && innerText->renderer()->style().overflowWrap() == OverflowWrap::Normal))) |
| || (!isHorizontalWritingMode() && (style().overflowY() == Overflow::Scroll || (style().overflowY() == Overflow::Auto && innerText->renderer()->style().overflowWrap() == OverflowWrap::Normal)))) |
| logicalHeight += scrollbarThickness(); |
| |
| // FIXME: The logical height of the inner text box should have been added |
| // before calling computeLogicalHeight to avoid this hack. |
| cacheIntrinsicContentLogicalHeightForFlexItem(logicalHeight); |
| |
| logicalHeight += verticalBorderAndPaddingExtent(); |
| } |
| |
| return RenderBox::computeLogicalHeight(logicalHeight, logicalTop); |
| } |
| |
| void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset) |
| { |
| auto innerText = innerTextElement(); |
| if (!innerText->renderer()) |
| return; |
| |
| LayoutPoint adjustedLocation = accumulatedOffset + location(); |
| LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location()) + toLayoutSize(scrollPosition()); |
| result.setInnerNode(innerText.get()); |
| result.setInnerNonSharedNode(innerText.get()); |
| result.setLocalPoint(localPoint); |
| } |
| |
| float RenderTextControl::getAverageCharWidth() |
| { |
| float width; |
| if (style().fontCascade().fastAverageCharWidthIfAvailable(width)) |
| return width; |
| |
| const UChar ch = '0'; |
| const String str = String(&ch, 1); |
| const FontCascade& font = style().fontCascade(); |
| TextRun textRun = constructTextRun(str, style(), ExpansionBehavior::allowRightOnly()); |
| return font.width(textRun); |
| } |
| |
| float RenderTextControl::scaleEmToUnits(int x) const |
| { |
| // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table. |
| float unitsPerEm = 2048.0f; |
| return roundf(style().fontCascade().size() * x / unitsPerEm); |
| } |
| |
| void RenderTextControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const |
| { |
| if (shouldApplySizeContainment()) |
| return; |
| // Use average character width. Matches IE. |
| maxLogicalWidth = preferredContentLogicalWidth(const_cast<RenderTextControl*>(this)->getAverageCharWidth()); |
| if (RenderBox* innerTextRenderBox = innerTextElement()->renderBox()) |
| maxLogicalWidth += innerTextRenderBox->paddingStart() + innerTextRenderBox->paddingEnd(); |
| if (!style().logicalWidth().isPercentOrCalculated()) |
| minLogicalWidth = maxLogicalWidth; |
| } |
| |
| void RenderTextControl::computePreferredLogicalWidths() |
| { |
| ASSERT(preferredLogicalWidthsDirty()); |
| |
| m_minPreferredLogicalWidth = 0; |
| m_maxPreferredLogicalWidth = 0; |
| |
| if (style().logicalWidth().isFixed() && style().logicalWidth().value() >= 0) |
| m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style().logicalWidth()); |
| else |
| computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); |
| |
| RenderBox::computePreferredLogicalWidths(style().logicalMinWidth(), style().logicalMaxWidth(), borderAndPaddingLogicalWidth()); |
| |
| setPreferredLogicalWidthsDirty(false); |
| } |
| |
| void RenderTextControl::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*) |
| { |
| if (!size().isEmpty()) |
| rects.append(LayoutRect(additionalOffset, size())); |
| } |
| |
| void RenderTextControl::layoutExcludedChildren(bool relayoutChildren) |
| { |
| RenderBlockFlow::layoutExcludedChildren(relayoutChildren); |
| |
| HTMLElement* placeholder = textFormControlElement().placeholderElement(); |
| RenderElement* placeholderRenderer = placeholder ? placeholder->renderer() : 0; |
| if (!placeholderRenderer) |
| return; |
| placeholderRenderer->setIsExcludedFromNormalLayout(true); |
| |
| if (relayoutChildren) { |
| // The markParents arguments should be false because this function is |
| // called from layout() of the parent and the placeholder layout doesn't |
| // affect the parent layout. |
| placeholderRenderer->setChildNeedsLayout(MarkOnlyThis); |
| } |
| } |
| |
| #if PLATFORM(IOS_FAMILY) |
| bool RenderTextControl::canScroll() const |
| { |
| auto innerText = innerTextElement(); |
| return innerText && innerText->renderer() && innerText->renderer()->hasNonVisibleOverflow(); |
| } |
| |
| int RenderTextControl::innerLineHeight() const |
| { |
| auto innerText = innerTextElement(); |
| if (innerText && innerText->renderer()) |
| return innerText->renderer()->style().computedLineHeight(); |
| return style().computedLineHeight(); |
| } |
| #endif |
| |
| } // namespace WebCore |