VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const { if (index <= 0) return VisiblePosition(firstPositionInNode(innerEditorElement()), DOWNSTREAM); RefPtrWillBeRawPtr<Range> range = Range::create(document()); range->selectNodeContents(innerEditorElement(), ASSERT_NO_EXCEPTION); CharacterIterator it(range.get()); it.advance(index - 1); return VisiblePosition(it.range()->endPosition(), UPSTREAM); }
VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const { if (index <= 0) return createVisiblePosition(Position::firstPositionInNode(innerEditorElement())); Position start, end; bool selected = Range::selectNodeContents(innerEditorElement(), start, end); if (!selected) return VisiblePosition(); CharacterIterator it(start, end); it.advance(index - 1); return createVisiblePosition(it.endPosition(), TextAffinity::Upstream); }
VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const { if (index <= 0) return VisiblePosition(firstPositionInNode(innerEditorElement()), DOWNSTREAM); Position start, end; bool selected = Range::selectNodeContents(innerEditorElement(), start, end); if (!selected) return VisiblePosition(); CharacterIterator it(start, end); it.advance(index - 1); return VisiblePosition(it.endPosition(), UPSTREAM); }
void LayoutTextControl::computeIntrinsicLogicalWidths( LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { // Use average character width. Matches IE. AtomicString family = style()->font().getFontDescription().family().family(); maxLogicalWidth = preferredContentLogicalWidth( const_cast<LayoutTextControl*>(this)->getAvgCharWidth(family)); if (innerEditorElement()) { if (LayoutBox* innerEditorLayoutBox = innerEditorElement()->layoutBox()) maxLogicalWidth += innerEditorLayoutBox->paddingStart() + innerEditorLayoutBox->paddingEnd(); } if (!style()->logicalWidth().isPercentOrCalc()) minLogicalWidth = maxLogicalWidth; }
int LayoutTextControl::firstLineBoxBaseline() const { int result = LayoutBlock::firstLineBoxBaseline(); if (result != -1) return result; // When the text is empty, |LayoutBlock::firstLineBoxBaseline()| cannot // compute the baseline because lineboxes do not exist. Element* innerEditor = innerEditorElement(); if (!innerEditor || !innerEditor->layoutObject()) return -1; LayoutBlock* innerEditorLayoutObject = toLayoutBlock(innerEditor->layoutObject()); const SimpleFontData* fontData = innerEditorLayoutObject->style(true)->font().primaryFont(); DCHECK(fontData); if (!fontData) return -1; LayoutUnit baseline(fontData->getFontMetrics().ascent(AlphabeticBaseline)); for (LayoutObject* box = innerEditorLayoutObject; box && box != this; box = box->parent()) { if (box->isBox()) baseline += toLayoutBox(box)->logicalTop(); } return baseline.toInt(); }
void LayoutTextControl::computeLogicalHeight( LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const { HTMLElement* innerEditor = innerEditorElement(); ASSERT(innerEditor); if (LayoutBox* innerEditorBox = innerEditor->layoutBox()) { LayoutUnit nonContentHeight = innerEditorBox->borderAndPaddingHeight() + innerEditorBox->marginHeight(); logicalHeight = computeControlLogicalHeight( innerEditorBox->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 (style()->overflowInlineDirection() == OverflowScroll || (style()->overflowInlineDirection() == OverflowAuto && innerEditor->layoutObject()->style()->overflowWrap() == NormalOverflowWrap)) logicalHeight += scrollbarThickness(); // FIXME: The logical height of the inner text box should have been added // before calling computeLogicalHeight to avoid this hack. setIntrinsicContentLogicalHeight(logicalHeight); logicalHeight += borderAndPaddingHeight(); } LayoutBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues); }
void HTMLTextFormControlElement::setInnerEditorValue(const String& value) { ASSERT(!openShadowRoot()); if (!isTextFormControl() || openShadowRoot()) return; bool textIsChanged = value != innerEditorValue(); HTMLElement* innerEditor = innerEditorElement(); if (!textIsChanged && innerEditor->hasChildren()) return; // If the last child is a trailing <br> that's appended below, remove it // first so as to enable setInnerText() fast path of updating a text node. if (isHTMLBRElement(innerEditor->lastChild())) innerEditor->removeChild(innerEditor->lastChild(), ASSERT_NO_EXCEPTION); // We don't use setTextContent. It triggers unnecessary paint. if (value.isEmpty()) innerEditor->removeChildren(); else replaceChildrenWithText(innerEditor, value, ASSERT_NO_EXCEPTION); // Add <br> so that we can put the caret at the next line of the last // newline. addPlaceholderBreakElementIfNecessary(); if (textIsChanged && layoutObject()) { if (AXObjectCache* cache = document().existingAXObjectCache()) cache->handleTextFormControlChanged(this); } }
void HTMLTextAreaElement::subtreeHasChanged() { #if DCHECK_IS_ON() // The innerEditor should have either Text nodes or a placeholder break // element. If we see other nodes, it's a bug in editing code and we should // fix it. Element* innerEditor = innerEditorElement(); for (Node& node : NodeTraversal::descendantsOf(*innerEditor)) { if (node.isTextNode()) continue; DCHECK(isHTMLBRElement(node)); DCHECK_EQ(&node, innerEditor->lastChild()); } #endif addPlaceholderBreakElementIfNecessary(); setChangedSinceLastFormControlChangeEvent(true); m_valueIsUpToDate = false; setNeedsValidityCheck(); setAutofilled(false); updatePlaceholderVisibility(); if (!isFocused()) return; // When typing in a textarea, childrenChanged is not called, so we need to // force the directionality check. calculateAndAdjustDirectionality(); DCHECK(document().isActive()); document().frameHost()->chromeClient().didChangeValueInTextField(*this); }
void HTMLTextFormControlElement::setInnerEditorValue(const String& value) { ASSERT(!hasAuthorShadowRoot()); if (!isTextFormControl() || hasAuthorShadowRoot()) return; bool textIsChanged = value != innerEditorValue(); if (textIsChanged || !innerEditorElement()->hasChildren()) { if (textIsChanged && renderer()) { if (AXObjectCache* cache = document().existingAXObjectCache()) cache->postNotification(this, AXObjectCache::AXValueChanged, false); } innerEditorElement()->setInnerText(value, ASSERT_NO_EXCEPTION); if (value.endsWith('\n') || value.endsWith('\r')) innerEditorElement()->appendChild(HTMLBRElement::create(document())); } }
int HTMLTextFormControlElement::computeSelectionEnd() const { ASSERT(isTextFormControl()); LocalFrame* frame = document().frame(); if (!frame) return 0; return indexForPosition(innerEditorElement(), frame->selection().end()); }
void HTMLTextFormControlElement::setInnerEditorValue(const String& value) { ASSERT(!hasOpenShadowRoot()); if (!isTextFormControl() || hasOpenShadowRoot()) return; bool textIsChanged = value != innerEditorValue(); if (textIsChanged || !innerEditorElement()->hasChildren()) { if (textIsChanged && layoutObject()) { if (AXObjectCache* cache = document().existingAXObjectCache()) cache->handleTextFormControlChanged(this); } innerEditorElement()->setInnerText(value, ASSERT_NO_EXCEPTION); if (value.endsWith('\n') || value.endsWith('\r')) innerEditorElement()->appendChild(HTMLBRElement::create(document())); } }
bool LayoutTextControlMultiLine::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { if (!LayoutTextControl::nodeAtPoint(result, locationInContainer, accumulatedOffset, hitTestAction)) return false; if (result.innerNode() == node() || result.innerNode() == innerEditorElement()) hitInnerEditorElement(result, locationInContainer.point(), accumulatedOffset); return true; }
int RenderTextControl::textBlockLogicalWidth() const { Element* innerEditor = innerEditorElement(); ASSERT(innerEditor); LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth(); if (innerEditor->renderer()) unitWidth -= innerEditor->renderBox()->paddingStart() + innerEditor->renderBox()->paddingEnd(); return unitWidth; }
int LayoutTextControl::textBlockLogicalWidth() const { Element* innerEditor = innerEditorElement(); ASSERT(innerEditor); LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth(); if (innerEditor->layoutObject()) unitWidth -= innerEditor->layoutBox()->paddingStart() + innerEditor->layoutBox()->paddingEnd(); return unitWidth.toInt(); }
int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const { Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent(); if (enclosingTextFormControl(indexPosition) != this) return 0; ASSERT(indexPosition.document()); RefPtrWillBeRawPtr<Range> range = Range::create(*indexPosition.document()); range->setStart(innerEditorElement(), 0, ASSERT_NO_EXCEPTION); range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ASSERT_NO_EXCEPTION); return TextIterator::rangeLength(range.get()); }
void HTMLTextFormControlElement::addPlaceholderBreakElementIfNecessary() { HTMLElement* innerEditor = innerEditorElement(); if (innerEditor->layoutObject() && !innerEditor->layoutObject()->style()->preserveNewline()) return; Node* lastChild = innerEditor->lastChild(); if (!lastChild || !lastChild->isTextNode()) return; if (toText(lastChild)->data().endsWith('\n') || toText(lastChild)->data().endsWith('\r')) innerEditor->appendChild(createPlaceholderBreakElement()); }
void LayoutTextControl::hitInnerEditorElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset) { HTMLElement* innerEditor = innerEditorElement(); if (!innerEditor->layoutObject()) return; LayoutPoint adjustedLocation = accumulatedOffset + location(); LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerEditor->layoutBox()->location()); if (hasOverflowClip()) localPoint += scrolledContentOffset(); result.setNodeAndPosition(innerEditor, localPoint); }
void RenderTextControl::hitInnerEditorElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset) { HTMLElement* innerEditor = innerEditorElement(); if (!innerEditor->renderer()) return; LayoutPoint adjustedLocation = accumulatedOffset + location(); LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerEditor->renderBox()->location()); if (hasOverflowClip()) localPoint += scrolledContentOffset(); result.setInnerNode(innerEditor); result.setInnerNonSharedNode(innerEditor); result.setLocalPoint(localPoint); }
String HTMLTextFormControlElement::innerEditorValue() const { ASSERT(!hasOpenShadowRoot()); HTMLElement* innerEditor = innerEditorElement(); if (!innerEditor || !isTextFormControl()) return emptyString(); StringBuilder result; for (Node& node : NodeTraversal::inclusiveDescendantsOf(*innerEditor)) { if (isHTMLBRElement(node)) result.append(newlineCharacter); else if (node.isTextNode()) result.append(toText(node).data()); } return finishText(result); }
String HTMLTextFormControlElement::innerEditorValue() const { ASSERT(!hasAuthorShadowRoot()); HTMLElement* innerEditor = innerEditorElement(); if (!innerEditor || !isTextFormControl()) return emptyString(); StringBuilder result; for (Node* node = innerEditor; node; node = NodeTraversal::next(*node, innerEditor)) { if (isHTMLBRElement(*node)) result.append(newlineCharacter); else if (node->isTextNode()) result.append(toText(node)->data()); } return finishText(result); }
void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction, NeedToDispatchSelectEvent eventBehaviour, SelectionOption selectionOption) { if (openShadowRoot() || !isTextFormControl()) return; const int editorValueLength = static_cast<int>(innerEditorValue().length()); ASSERT(editorValueLength >= 0); end = std::max(std::min(end, editorValueLength), 0); start = std::min(std::max(start, 0), end); cacheSelection(start, end, direction); if (selectionOption == NotChangeSelection || (selectionOption == ChangeSelectionIfFocused && document().focusedElement() != this) || !inShadowIncludingDocument()) { if (eventBehaviour == DispatchSelectEvent) scheduleSelectEvent(); return; } LocalFrame* frame = document().frame(); HTMLElement* innerEditor = innerEditorElement(); if (!frame || !innerEditor) return; Position startPosition = positionForIndex(innerEditor, start); Position endPosition = start == end ? startPosition : positionForIndex(innerEditor, end); ASSERT(start == indexForPosition(innerEditor, startPosition)); ASSERT(end == indexForPosition(innerEditor, endPosition)); #if ENABLE(ASSERT) // startPosition and endPosition can be null position for example when // "-webkit-user-select: none" style attribute is specified. if (startPosition.isNotNull() && endPosition.isNotNull()) { ASSERT(startPosition.anchorNode()->shadowHost() == this && endPosition.anchorNode()->shadowHost() == this); } #endif // ENABLE(ASSERT) VisibleSelection newSelection; if (direction == SelectionHasBackwardDirection) newSelection.setWithoutValidation(endPosition, startPosition); else newSelection.setWithoutValidation(startPosition, endPosition); newSelection.setIsDirectional(direction != SelectionHasNoDirection); frame->selection().setSelection(newSelection, FrameSelection::DoNotAdjustInFlatTree | FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | (selectionOption == ChangeSelectionAndFocus ? 0 : FrameSelection::DoNotSetFocus)); if (eventBehaviour == DispatchSelectEvent) scheduleSelectEvent(); }
void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlockFlow::styleDidChange(diff, oldStyle); Element* innerEditor = innerEditorElement(); if (!innerEditor) return; RenderBlock* innerEditorRenderer = toRenderBlock(innerEditor->renderer()); if (innerEditorRenderer) { // We may have set the width and the height in the old style in layout(). // Reset them now to avoid getting a spurious layout hint. innerEditorRenderer->style()->setHeight(Length()); innerEditorRenderer->style()->setWidth(Length()); innerEditorRenderer->setStyle(createInnerEditorStyle(style())); innerEditor->setNeedsStyleRecalc(SubtreeStyleChange); } textFormControlElement()->updatePlaceholderVisibility(false); }
void LayoutTextControl::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle) { LayoutBlockFlow::styleDidChange(diff, oldStyle); Element* innerEditor = innerEditorElement(); if (!innerEditor) return; LayoutBlock* innerEditorLayoutObject = toLayoutBlock(innerEditor->layoutObject()); if (innerEditorLayoutObject) { // We may have set the width and the height in the old style in layout(). // Reset them now to avoid getting a spurious layout hint. innerEditorLayoutObject->mutableStyleRef().setHeight(Length()); innerEditorLayoutObject->mutableStyleRef().setWidth(Length()); innerEditorLayoutObject->setStyle(createInnerEditorStyle(styleRef())); innerEditor->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::Control)); } textFormControlElement()->updatePlaceholderVisibility(false); }
String HTMLTextFormControlElement::valueWithHardLineBreaks() const { // FIXME: It's not acceptable to ignore the HardWrap setting when there is no layoutObject. // While we have no evidence this has ever been a practical problem, it would be best to fix it some day. HTMLElement* innerText = innerEditorElement(); if (!innerText || !isTextFormControl()) return value(); LayoutBlockFlow* layoutObject = toLayoutBlockFlow(innerText->layoutObject()); if (!layoutObject) return value(); Node* breakNode; unsigned breakOffset; RootInlineBox* line = layoutObject->firstRootBox(); if (!line) return value(); getNextSoftBreak(line, breakNode, breakOffset); StringBuilder result; for (Node& node : NodeTraversal::descendantsOf(*innerText)) { if (isHTMLBRElement(node)) { ASSERT(&node == innerText->lastChild()); if (&node != innerText->lastChild()) result.append(newlineCharacter); } else if (node.isTextNode()) { String data = toText(node).data(); unsigned length = data.length(); unsigned position = 0; while (breakNode == node && breakOffset <= length) { if (breakOffset > position) { result.append(data, position, breakOffset - position); position = breakOffset; result.append(newlineCharacter); } getNextSoftBreak(line, breakNode, breakOffset); } result.append(data, position, length - position); } while (breakNode == node) getNextSoftBreak(line, breakNode, breakOffset); } return result.toString(); }
void HTMLTextAreaElement::updatePlaceholderText() { HTMLElement* placeholder = placeholderElement(); const AtomicString& placeholderText = fastGetAttribute(placeholderAttr); if (placeholderText.isEmpty()) { if (placeholder) userAgentShadowRoot()->removeChild(placeholder); return; } if (!placeholder) { RefPtrWillBeRawPtr<HTMLDivElement> newElement = HTMLDivElement::create(document()); placeholder = newElement.get(); placeholder->setShadowPseudoId(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral)); placeholder->setAttribute(idAttr, ShadowElementNames::placeholder()); userAgentShadowRoot()->insertBefore(placeholder, innerEditorElement()->nextSibling()); } placeholder->setTextContent(placeholderText); }
String HTMLTextFormControlElement::valueWithHardLineBreaks() const { // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer. // While we have no evidence this has ever been a practical problem, it would be best to fix it some day. HTMLElement* innerText = innerEditorElement(); if (!innerText || !isTextFormControl()) return value(); RenderBlock* renderer = toRenderBlock(innerText->renderer()); if (!renderer) return value(); Node* breakNode; unsigned breakOffset; RootInlineBox* line = renderer->firstRootBox(); if (!line) return value(); getNextSoftBreak(line, breakNode, breakOffset); StringBuilder result; for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) { if (isHTMLBRElement(*node)) result.append(newlineCharacter); else if (node->isTextNode()) { String data = toText(node)->data(); unsigned length = data.length(); unsigned position = 0; while (breakNode == node && breakOffset <= length) { if (breakOffset > position) { result.append(data, position, breakOffset - position); position = breakOffset; result.append(newlineCharacter); } getNextSoftBreak(line, breakNode, breakOffset); } result.append(data, position, length - position); } while (breakNode == node) getNextSoftBreak(line, breakNode, breakOffset); } return finishText(result); }
void HTMLTextAreaElement::updatePlaceholderText() { HTMLElement* placeholder = placeholderElement(); const AtomicString& placeholderText = fastGetAttribute(placeholderAttr); if (placeholderText.isEmpty()) { if (placeholder) userAgentShadowRoot()->removeChild(placeholder); return; } if (!placeholder) { HTMLDivElement* newElement = HTMLDivElement::create(document()); placeholder = newElement; placeholder->setShadowPseudoId(AtomicString("-webkit-input-placeholder")); placeholder->setAttribute(idAttr, ShadowElementNames::placeholder()); placeholder->setInlineStyleProperty( CSSPropertyDisplay, isPlaceholderVisible() ? CSSValueBlock : CSSValueNone, true); userAgentShadowRoot()->insertBefore(placeholder, innerEditorElement()); } placeholder->setTextContent(placeholderText); }
Range* HTMLTextFormControlElement::selection() const { if (!layoutObject() || !isTextFormControl()) return nullptr; int start = m_cachedSelectionStart; int end = m_cachedSelectionEnd; ASSERT(start <= end); HTMLElement* innerText = innerEditorElement(); if (!innerText) return nullptr; if (!innerText->hasChildren()) return Range::create(document(), innerText, 0, innerText, 0); int offset = 0; Node* startNode = 0; Node* endNode = 0; for (Node& node : NodeTraversal::descendantsOf(*innerText)) { ASSERT(!node.hasChildren()); ASSERT(node.isTextNode() || isHTMLBRElement(node)); int length = node.isTextNode() ? lastOffsetInNode(&node) : 1; if (offset <= start && start <= offset + length) setContainerAndOffsetForRange(&node, start - offset, startNode, start); if (offset <= end && end <= offset + length) { setContainerAndOffsetForRange(&node, end - offset, endNode, end); break; } offset += length; } if (!startNode || !endNode) return nullptr; return Range::create(document(), startNode, start, endNode, end); }
PassRefPtrWillBeRawPtr<Range> HTMLTextFormControlElement::selection() const { if (!renderer() || !isTextFormControl()) return nullptr; int start = m_cachedSelectionStart; int end = m_cachedSelectionEnd; ASSERT(start <= end); HTMLElement* innerText = innerEditorElement(); if (!innerText) return nullptr; if (!innerText->firstChild()) return Range::create(document(), innerText, 0, innerText, 0); int offset = 0; Node* startNode = 0; Node* endNode = 0; for (Node* node = innerText->firstChild(); node; node = NodeTraversal::next(*node, innerText)) { ASSERT(!node->firstChild()); ASSERT(node->isTextNode() || isHTMLBRElement(*node)); int length = node->isTextNode() ? lastOffsetInNode(node) : 1; if (offset <= start && start <= offset + length) setContainerAndOffsetForRange(node, start - offset, startNode, start); if (offset <= end && end <= offset + length) { setContainerAndOffsetForRange(node, end - offset, endNode, end); break; } offset += length; } if (!startNode || !endNode) return nullptr; return Range::create(document(), startNode, start, endNode, end); }
void RenderTextControl::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const { HTMLElement* innerEditor = innerEditorElement(); ASSERT(innerEditor); if (RenderBox* innerEditorBox = innerEditor->renderBox()) { LayoutUnit nonContentHeight = innerEditorBox->borderAndPaddingHeight() + innerEditorBox->marginHeight(); logicalHeight = computeControlLogicalHeight(innerEditorBox->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() == OSCROLL || (style()->overflowX() == OAUTO && innerEditor->renderer()->style()->overflowWrap() == NormalOverflowWrap))) || (!isHorizontalWritingMode() && (style()->overflowY() == OSCROLL || (style()->overflowY() == OAUTO && innerEditor->renderer()->style()->overflowWrap() == NormalOverflowWrap)))) logicalHeight += scrollbarThickness(); // FIXME: The logical height of the inner text box should have been added before calling computeLogicalHeight to // avoid this hack. updateIntrinsicContentLogicalHeight(logicalHeight); logicalHeight += borderAndPaddingHeight(); } RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues); }