VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point) { InlineBox* box = inlineBoxWrapper(); if (!box) return createVisiblePosition(0, DOWNSTREAM); // FIXME: This code is buggy if the replaced element is relative positioned. RootInlineBox* root = box->root(); int top = root->selectionTop(); int bottom = root->selectionBottom(); int blockDirectionPosition = box->isHorizontal() ? point.y() + y() : point.x() + x(); int lineDirectionPosition = box->isHorizontal() ? point.x() + x() : point.y() + y(); if (blockDirectionPosition < top) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above if (blockDirectionPosition >= bottom) return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below if (node()) { if (lineDirectionPosition <= box->logicalLeft() + (box->logicalWidth() / 2)) return createVisiblePosition(0, DOWNSTREAM); return createVisiblePosition(1, DOWNSTREAM); } return RenderBox::positionForPoint(point); }
TEST_F(VisiblePositionTest, ShadowV0DistributedNodes) { const char* bodyContent = "<p id='host'>00<b id='one'>11</b><b id='two'>22</b>33</p>"; const char* shadowContent = "<a><span id='s4'>44</span><content select=#two></content><span " "id='s5'>55</span><content select=#one></content><span " "id='s6'>66</span></a>"; setBodyContent(bodyContent); ShadowRoot* shadowRoot = setShadowContent(shadowContent, "host"); Element* body = document().body(); Element* one = body->querySelector("#one"); Element* two = body->querySelector("#two"); Element* four = shadowRoot->querySelector("#s4"); Element* five = shadowRoot->querySelector("#s5"); EXPECT_EQ(Position(one->firstChild(), 0), canonicalPositionOf(Position(one, 0))); EXPECT_EQ(Position(one->firstChild(), 0), createVisiblePosition(Position(one, 0)).deepEquivalent()); EXPECT_EQ(Position(one->firstChild(), 2), canonicalPositionOf(Position(two, 0))); EXPECT_EQ(Position(one->firstChild(), 2), createVisiblePosition(Position(two, 0)).deepEquivalent()); EXPECT_EQ(PositionInFlatTree(five->firstChild(), 2), canonicalPositionOf(PositionInFlatTree(one, 0))); EXPECT_EQ(PositionInFlatTree(five->firstChild(), 2), createVisiblePosition(PositionInFlatTree(one, 0)).deepEquivalent()); EXPECT_EQ(PositionInFlatTree(four->firstChild(), 2), canonicalPositionOf(PositionInFlatTree(two, 0))); EXPECT_EQ(PositionInFlatTree(four->firstChild(), 2), createVisiblePosition(PositionInFlatTree(two, 0)).deepEquivalent()); }
TEST_F(EditingUtilitiesTest, isFirstPositionAfterTable) { const char* bodyContent = "<div contenteditable id=host><table id=table><tr><td>1</td></tr></table><b id=two>22</b></div>"; const char* shadowContent = "<content select=#two></content><content select=#table></content>"; setBodyContent(bodyContent); setShadowContent(shadowContent, "host"); updateLayoutAndStyleForPainting(); Node* host = document().getElementById("host"); Node* table = document().getElementById("table"); EXPECT_EQ(table, isFirstPositionAfterTable(createVisiblePosition(Position::afterNode(table)))); EXPECT_EQ(table, isFirstPositionAfterTable(createVisiblePosition(PositionInComposedTree::afterNode(table)))); EXPECT_EQ(table, isFirstPositionAfterTable(createVisiblePosition(Position::lastPositionInNode(table)))); EXPECT_EQ(table, isFirstPositionAfterTable(createVisiblePosition(PositionInComposedTree::lastPositionInNode(table)))); EXPECT_EQ(nullptr, isFirstPositionAfterTable(createVisiblePosition(Position(host, 2)))); EXPECT_EQ(table, isFirstPositionAfterTable(createVisiblePosition(PositionInComposedTree(host, 2)))); EXPECT_EQ(nullptr, isFirstPositionAfterTable(createVisiblePosition(Position::afterNode(host)))); EXPECT_EQ(nullptr, isFirstPositionAfterTable(createVisiblePosition(PositionInComposedTree::afterNode(host)))); EXPECT_EQ(nullptr, isFirstPositionAfterTable(createVisiblePosition(Position::lastPositionInNode(host)))); EXPECT_EQ(table, isFirstPositionAfterTable(createVisiblePosition(PositionInComposedTree::lastPositionInNode(host)))); }
void GranularityStrategyTest::parseText(const TextNodeVector& textNodes) { bool wordStarted = false; int wordStartIndex = 0; for (auto& text : textNodes) { int wordStartIndexOffset = m_letterPos.size(); String str = text->wholeText(); for (size_t i = 0; i < str.length(); i++) { m_letterPos.append(visiblePositionToContentsPoint(createVisiblePosition(Position(text, i)))); char c = str.characterAt(i); if (isASCIIAlphanumeric(c) && !wordStarted) { wordStartIndex = i + wordStartIndexOffset; wordStarted = true; } else if (!isASCIIAlphanumeric(c) && wordStarted) { IntPoint wordMiddle((m_letterPos[wordStartIndex].x() + m_letterPos[i + wordStartIndexOffset].x()) / 2, m_letterPos[wordStartIndex].y()); m_wordMiddles.append(wordMiddle); wordStarted = false; } } } if (wordStarted) { const auto& lastNode = textNodes.last(); int xEnd = visiblePositionToContentsPoint(createVisiblePosition(Position(lastNode, lastNode->wholeText().length()))).x(); IntPoint wordMiddle((m_letterPos[wordStartIndex].x() + xEnd) / 2, m_letterPos[wordStartIndex].y()); m_wordMiddles.append(wordMiddle); } }
void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionState& exceptionState) { if (!m_frame) return; if (baseOffset < 0) { exceptionState.throwDOMException(IndexSizeError, String::number(baseOffset) + " is not a valid base offset."); return; } if (extentOffset < 0) { exceptionState.throwDOMException(IndexSizeError, String::number(extentOffset) + " is not a valid extent offset."); return; } if (!baseNode || !extentNode) UseCounter::count(m_frame, UseCounter::SelectionSetBaseAndExtentNull); if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) return; VisiblePosition visibleBase = createVisiblePosition(createPosition(baseNode, baseOffset)); VisiblePosition visibleExtent = createVisiblePosition(createPosition(extentNode, extentOffset)); m_frame->selection().moveTo(visibleBase, visibleExtent); }
VisibleSelectionTemplate<Strategy> PendingSelection::calcVisibleSelectionAlgorithm(const VisibleSelectionTemplate<Strategy>& originalSelection) const { const PositionTemplate<Strategy> start = originalSelection.start(); const PositionTemplate<Strategy> end = originalSelection.end(); SelectionType selectionType = originalSelection.selectionType(); const TextAffinity affinity = originalSelection.affinity(); bool paintBlockCursor = m_frameSelection->shouldShowBlockCursor() && selectionType == SelectionType::CaretSelection && !isLogicalEndOfLine(createVisiblePosition(end, affinity)); VisibleSelectionTemplate<Strategy> selection; if (enclosingTextFormControl(start.computeContainerNode())) { // TODO(yosin) We should use |PositionMoveType::Character| to avoid // ending paint at middle of character. PositionTemplate<Strategy> endPosition = paintBlockCursor ? nextPositionOf(originalSelection.extent(), PositionMoveType::CodePoint) : end; selection.setWithoutValidation(start, endPosition); return selection; } const VisiblePositionTemplate<Strategy> visibleStart = createVisiblePosition(start, selectionType == SelectionType::RangeSelection ? TextAffinity::Downstream : affinity); if (paintBlockCursor) { VisiblePositionTemplate<Strategy> visibleExtent = createVisiblePosition(end, affinity); visibleExtent = nextPositionOf(visibleExtent, CanSkipOverEditingBoundary); return VisibleSelectionTemplate<Strategy>(visibleStart, visibleExtent); } const VisiblePositionTemplate<Strategy> visibleEnd = createVisiblePosition(end, selectionType == SelectionType::RangeSelection ? TextAffinity::Upstream : affinity); return VisibleSelectionTemplate<Strategy>(visibleStart, visibleEnd); }
VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point) { InlineBox* box = inlineBoxWrapper(); if (!box) return createVisiblePosition(0, DOWNSTREAM); // FIXME: This code is buggy if the replaced element is relative positioned. RootInlineBox* root = box->root(); int top = root->topOverflow(); int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow(); if (point.y() + y() < top) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above if (point.y() + y() >= bottom) return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below if (node()) { if (point.x() <= width() / 2) return createVisiblePosition(0, DOWNSTREAM); return createVisiblePosition(1, DOWNSTREAM); } return RenderBox::positionForPoint(point); }
VisiblePosition SelectionEditor::modifyMovingLeft(TextGranularity granularity) { VisiblePosition pos; switch (granularity) { case CharacterGranularity: if (m_selection.isRange()) { if (directionOfSelection() == LTR) pos = createVisiblePosition(m_selection.start(), m_selection.affinity()); else pos = createVisiblePosition(m_selection.end(), m_selection.affinity()); } else { pos = leftPositionOf(createVisiblePosition(m_selection.extent(), m_selection.affinity())); } break; case WordGranularity: { bool skipsSpaceWhenMovingRight = frame() && frame()->editor().behavior().shouldSkipSpaceWhenMovingRight(); pos = leftWordPosition(createVisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight); break; } case SentenceGranularity: case LineGranularity: case ParagraphGranularity: case SentenceBoundary: case ParagraphBoundary: case DocumentBoundary: // FIXME: Implement all of the above. pos = modifyMovingBackward(granularity); break; case LineBoundary: pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock()); break; } return pos; }
void testFunctionEquivalence(const Position& position, PositionFunction positionFunction, VisblePositionFunction visibleFunction) { VisiblePosition visiblePosition = createVisiblePosition(position); VisiblePosition expected = visibleFunction(visiblePosition); VisiblePosition actual = createVisiblePosition(positionFunction(position)); EXPECT_EQ(expected.deepEquivalent(), actual.deepEquivalent()); }
VisiblePosition RenderReplaced::positionForPoint(const LayoutPoint& point, const RenderRegion* region) { // FIXME: This code is buggy if the replaced element is relative positioned. InlineBox* box = inlineBoxWrapper(); const RootInlineBox* rootBox = box ? &box->root() : 0; LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); LayoutUnit blockDirectionPosition = isHorizontalWritingMode() ? point.y() + y() : point.x() + x(); LayoutUnit lineDirectionPosition = isHorizontalWritingMode() ? point.x() + x() : point.y() + y(); if (blockDirectionPosition < top) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above if (blockDirectionPosition >= bottom) return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below if (element()) { if (lineDirectionPosition <= logicalLeft() + (logicalWidth() / 2)) return createVisiblePosition(0, DOWNSTREAM); return createVisiblePosition(1, DOWNSTREAM); } return RenderBox::positionForPoint(point, region); }
VisiblePosition SelectionModifier::modifyMovingForward( TextGranularity granularity) { VisiblePosition pos; // FIXME: Stay in editable content for the less common granularities. switch (granularity) { case CharacterGranularity: if (m_selection.isRange()) pos = createVisiblePosition(m_selection.end(), m_selection.affinity()); else pos = nextPositionOf( createVisiblePosition(m_selection.extent(), m_selection.affinity()), CanSkipOverEditingBoundary); break; case WordGranularity: pos = nextWordPositionForPlatform( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case SentenceGranularity: pos = nextSentencePosition( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case LineGranularity: { // down-arrowing from a range selection that ends at the start of a line // needs to leave the selection at that line start (no need to call // nextLinePosition!) pos = endForPlatform(); if (!m_selection.isRange() || !isStartOfLine(pos)) pos = nextLinePosition( pos, lineDirectionPointForBlockDirectionNavigation(START)); break; } case ParagraphGranularity: pos = nextParagraphPosition( endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); break; case SentenceBoundary: pos = endOfSentence(endForPlatform()); break; case LineBoundary: pos = logicalEndOfLine(endForPlatform()); break; case ParagraphBoundary: pos = endOfParagraph(endForPlatform()); break; case DocumentBoundary: pos = endForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = endOfEditableContent(pos); else pos = endOfDocument(pos); break; } return pos; }
VisiblePosition RenderSVGInlineText::positionForPoint(const LayoutPoint& point, const RenderRegion*) { if (!firstTextBox() || !textLength()) return createVisiblePosition(0, DOWNSTREAM); float baseline = m_scaledFont.fontMetrics().floatAscent(); RenderBlock* containingBlock = this->containingBlock(); ASSERT(containingBlock); // Map local point to absolute point, as the character origins stored in the text fragments use absolute coordinates. FloatPoint absolutePoint(point); absolutePoint.moveBy(containingBlock->location()); float closestDistance = std::numeric_limits<float>::max(); float closestDistancePosition = 0; const SVGTextFragment* closestDistanceFragment = nullptr; SVGInlineTextBox* closestDistanceBox = nullptr; AffineTransform fragmentTransform; for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { if (!is<SVGInlineTextBox>(*box)) continue; auto& textBox = downcast<SVGInlineTextBox>(*box); Vector<SVGTextFragment>& fragments = textBox.textFragments(); unsigned textFragmentsSize = fragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { const SVGTextFragment& fragment = fragments.at(i); FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height); fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) fragmentRect = fragmentTransform.mapRect(fragmentRect); float distance = powf(fragmentRect.x() - absolutePoint.x(), 2) + powf(fragmentRect.y() + fragmentRect.height() / 2 - absolutePoint.y(), 2); if (distance < closestDistance) { closestDistance = distance; closestDistanceBox = &textBox; closestDistanceFragment = &fragment; closestDistancePosition = fragmentRect.x(); } } } if (!closestDistanceFragment) return createVisiblePosition(0, DOWNSTREAM); int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanceFragment, absolutePoint.x() - closestDistancePosition, true); return createVisiblePosition(offset + closestDistanceBox->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); }
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 RenderSVGInlineText::positionForPoint(const IntPoint& point) { if (!firstTextBox() || !textLength()) return createVisiblePosition(0, DOWNSTREAM); RenderStyle* style = this->style(); ASSERT(style); int baseline = style->font().ascent(); RenderBlock* containingBlock = this->containingBlock(); ASSERT(containingBlock); // Map local point to absolute point, as the character origins stored in the text fragments use absolute coordinates. FloatPoint absolutePoint(point); absolutePoint.move(containingBlock->x(), containingBlock->y()); float closestDistance = std::numeric_limits<float>::max(); float closestDistancePosition = 0; const SVGTextFragment* closestDistanceFragment = 0; SVGInlineTextBox* closestDistanceBox = 0; for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { ASSERT(box->isSVGInlineTextBox()); SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box); Vector<SVGTextFragment>& fragments = textBox->textFragments(); unsigned textFragmentsSize = fragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { const SVGTextFragment& fragment = fragments.at(i); FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height); if (!fragment.transform.isIdentity()) fragmentRect = fragment.transform.mapRect(fragmentRect); float distance = powf(fragmentRect.x() - absolutePoint.x(), 2) + powf(fragmentRect.y() + fragmentRect.height() / 2 - absolutePoint.y(), 2); if (distance < closestDistance) { closestDistance = distance; closestDistanceBox = textBox; closestDistanceFragment = &fragment; closestDistancePosition = fragmentRect.x(); } } } if (!closestDistanceFragment) return createVisiblePosition(0, DOWNSTREAM); int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanceFragment, absolutePoint.x() - closestDistancePosition, true); return createVisiblePosition(offset + closestDistanceBox->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); }
VisiblePosition SelectionModifier::modifyMovingBackward( TextGranularity granularity) { VisiblePosition pos; switch (granularity) { case CharacterGranularity: if (m_selection.isRange()) pos = createVisiblePosition(m_selection.start(), m_selection.affinity()); else pos = previousPositionOf( createVisiblePosition(m_selection.extent(), m_selection.affinity()), CanSkipOverEditingBoundary); break; case WordGranularity: pos = previousWordPosition( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case SentenceGranularity: pos = previousSentencePosition( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case LineGranularity: pos = previousLinePosition( startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); break; case ParagraphGranularity: pos = previousParagraphPosition( startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); break; case SentenceBoundary: pos = startOfSentence(startForPlatform()); break; case LineBoundary: pos = logicalStartOfLine(startForPlatform()); break; case ParagraphBoundary: pos = startOfParagraph(startForPlatform()); break; case DocumentBoundary: pos = startForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = startOfEditableContent(pos); else pos = startOfDocument(pos); break; } return pos; }
VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents, const RenderRegion* region) { RootInlineBox* rootBox = firstRootBox(); if (!rootBox) return createVisiblePosition(0, DOWNSTREAM); ASSERT(!rootBox->nextRootBox()); ASSERT(childrenInline()); InlineBox* closestBox = downcast<SVGRootInlineBox>(*rootBox).closestLeafChildForPosition(pointInContents); if (!closestBox) return createVisiblePosition(0, DOWNSTREAM); return closestBox->renderer().positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y()), region); }
void FrameCaret::updateAppearance() { // Paint a block cursor instead of a caret in overtype mode unless the caret // is at the end of a line (in this case the FrameSelection will paint a // blinking caret as usual). bool paintBlockCursor = m_shouldShowBlockCursor && isActive(); if (paintBlockCursor) { // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets // needs to be audited. see http://crbug.com/590369 for more details. // In the long term, we should defer the update of the caret's appearance // to prevent synchronous layout. m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets(); if (isLogicalEndOfLine(createVisiblePosition(caretPosition()))) paintBlockCursor = false; } bool shouldBlink = !paintBlockCursor && shouldBlinkCaret(); // If the caret moved, stop the blink timer so we can restart with a // black caret in the new location. if (!shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) stopCaretBlinkTimer(); // Start blinking with a black caret. Be sure not to restart if we're // already blinking in the right location. if (shouldBlink) startBlinkCaret(); }
// Test for the default CharacterGranularityStrategy TEST_F(GranularityStrategyTest, Character) { dummyPageHolder().frame().settings()->setSelectionStrategy(SelectionStrategy::Character); dummyPageHolder().frame().settings()->setDefaultFontSize(12); // "Foo Bar Baz," RefPtrWillBeRawPtr<Text> text = appendTextNode("Foo Bar Baz,"); // "Foo B^a|>r Baz," (^ means base, | means extent, , < means start, and > means end). selection().setSelection(VisibleSelection(Position(text, 5), Position(text, 6))); EXPECT_EQ_SELECTED_TEXT("a"); // "Foo B^ar B|>az," selection().moveRangeSelectionExtent(visiblePositionToContentsPoint(createVisiblePosition(Position(text, 9)))); EXPECT_EQ_SELECTED_TEXT("ar B"); // "F<|oo B^ar Baz," selection().moveRangeSelectionExtent(visiblePositionToContentsPoint(createVisiblePosition(Position(text, 1)))); EXPECT_EQ_SELECTED_TEXT("oo B"); }
TEST_F(VisiblePositionTest, NonNullIsValidBeforeMutation) { setBodyContent("<p>one</p>"); Element* paragraph = document().querySelector("p"); Position position(paragraph->firstChild(), 1); EXPECT_TRUE(createVisiblePosition(position).isValid()); }
VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents, const RenderRegion* region) { RootInlineBox* rootBox = firstRootBox(); if (!rootBox) return createVisiblePosition(0, DOWNSTREAM); ASSERT_WITH_SECURITY_IMPLICATION(rootBox->isSVGRootInlineBox()); ASSERT(!rootBox->nextRootBox()); ASSERT(childrenInline()); InlineBox* closestBox = toSVGRootInlineBox(rootBox)->closestLeafChildForPosition(pointInContents); if (!closestBox) return createVisiblePosition(0, DOWNSTREAM); return closestBox->renderer().positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y()), region); }
VisiblePosition RenderSVGText::positionForPoint(const IntPoint& pointInContents) { RootInlineBox* rootBox = firstRootBox(); if (!rootBox) return createVisiblePosition(0, DOWNSTREAM); ASSERT(rootBox->isSVGRootInlineBox()); ASSERT(!rootBox->nextRootBox()); ASSERT(childrenInline()); InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents); if (!closestBox) return createVisiblePosition(0, DOWNSTREAM); return closestBox->renderer()->positionForPoint(IntPoint(pointInContents.x(), closestBox->m_y)); }
static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward) { if (Node* rootUserSelectAll = EditingStrategy::rootUserSelectAllForNode( pos.deepEquivalent().anchorNode())) pos = createVisiblePosition( isForward ? mostForwardCaretPosition(Position::afterNode(rootUserSelectAll), CanCrossEditingBoundary) : mostBackwardCaretPosition(Position::beforeNode(rootUserSelectAll), CanCrossEditingBoundary)); }
IntRect FrameCaret::absoluteCaretBounds() { DCHECK_NE(m_frame->document()->lifecycle().state(), DocumentLifecycle::InPaintInvalidation); DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); DocumentLifecycle::DisallowTransitionScope disallowTransition( m_frame->document()->lifecycle()); if (!isActive()) { clearCaretRect(); } else { if (enclosingTextFormControl(caretPosition().position())) { if (isVisuallyEquivalentCandidate(caretPosition().position())) updateCaretRect(caretPosition()); else updateCaretRect(createVisiblePosition(caretPosition())); } else { updateCaretRect(createVisiblePosition(caretPosition())); } } return absoluteBoundsForLocalRect(caretPosition().anchorNode(), localCaretRectWithoutUpdate()); }
TEST_F(VisiblePositionTest, ShadowV0DistributedNodes) { const char* bodyContent = "<p id='host'>00<b id='one'>11</b><b id='two'>22</b>33</p>"; const char* shadowContent = "<a><span id='s4'>44</span><content select=#two></content><span id='s5'>55</span><content select=#one></content><span id='s6'>66</span></a>"; setBodyContent(bodyContent); RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = setShadowContent(shadowContent, "host"); RefPtrWillBeRawPtr<Element> body = document().body(); RefPtrWillBeRawPtr<Element> one = body->querySelector("#one", ASSERT_NO_EXCEPTION); RefPtrWillBeRawPtr<Element> two = body->querySelector("#two", ASSERT_NO_EXCEPTION); RefPtrWillBeRawPtr<Element> four = shadowRoot->querySelector("#s4", ASSERT_NO_EXCEPTION); RefPtrWillBeRawPtr<Element> five = shadowRoot->querySelector("#s5", ASSERT_NO_EXCEPTION); EXPECT_EQ(Position(one->firstChild(), 0), canonicalPositionOf(Position(one, 0))); EXPECT_EQ(Position(one->firstChild(), 0), createVisiblePosition(Position(one, 0)).deepEquivalent()); EXPECT_EQ(Position(one->firstChild(), 2), canonicalPositionOf(Position(two.get(), 0))); EXPECT_EQ(Position(one->firstChild(), 2), createVisiblePosition(Position(two.get(), 0)).deepEquivalent()); EXPECT_EQ(PositionInComposedTree(five->firstChild(), 2), canonicalPositionOf(PositionInComposedTree(one.get(), 0))); EXPECT_EQ(PositionInComposedTree(five->firstChild(), 2), createVisiblePosition(PositionInComposedTree(one.get(), 0)).deepEquivalent()); EXPECT_EQ(PositionInComposedTree(four->firstChild(), 2), canonicalPositionOf(PositionInComposedTree(two.get(), 0))); EXPECT_EQ(PositionInComposedTree(four->firstChild(), 2), createVisiblePosition(PositionInComposedTree(two.get(), 0)).deepEquivalent()); }
void DOMSelection::collapseToStart(ExceptionState& exceptionState) { if (!m_frame) return; const VisibleSelection& selection = m_frame->selection().selection(); if (selection.isNone()) { exceptionState.throwDOMException(InvalidStateError, "there is no selection."); return; } m_frame->selection().moveTo(createVisiblePosition(selection.start())); }
TEST_F(VisiblePositionTest, NonNullInvalidatedAfterStyleChange) { setBodyContent("<div>one</div><p>two</p>"); Element* paragraph = document().querySelector("p"); Element* div = document().querySelector("div"); Position position(paragraph->firstChild(), 1); VisiblePosition visiblePosition1 = createVisiblePosition(position); div->style()->setProperty("color", "red", "important", ASSERT_NO_EXCEPTION); EXPECT_FALSE(visiblePosition1.isValid()); updateAllLifecyclePhases(); VisiblePosition visiblePosition2 = createVisiblePosition(position); div->style()->setProperty("display", "none", "important", ASSERT_NO_EXCEPTION); EXPECT_FALSE(visiblePosition2.isValid()); updateAllLifecyclePhases(); // Invalid VisiblePosition can never become valid again. EXPECT_FALSE(visiblePosition1.isValid()); EXPECT_FALSE(visiblePosition2.isValid()); }
static EphemeralRange expandEndToSentenceBoundary(const EphemeralRange& range) { DCHECK(range.isNotNull()); const VisiblePosition& visibleEnd = createVisiblePosition(range.endPosition()); DCHECK(visibleEnd.isNotNull()); const Position& sentenceEnd = endOfSentence(visibleEnd).deepEquivalent(); // TODO(xiaochengh): |sentenceEnd < range.endPosition()| is possible, // which would trigger a DCHECK in EphemeralRange's constructor if we return // it directly. However, this shouldn't happen and needs to be fixed. return EphemeralRange( range.startPosition(), sentenceEnd.isNotNull() && sentenceEnd > range.endPosition() ? sentenceEnd : range.endPosition()); }
VisiblePosition RenderSVGInlineText::positionForPoint(const IntPoint& point) { SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(firstTextBox()); if (!textBox || textLength() == 0) return createVisiblePosition(0, DOWNSTREAM); SVGRootInlineBox* rootBox = textBox->svgRootInlineBox(); RenderBlock* object = rootBox ? rootBox->block() : 0; if (!object) return createVisiblePosition(0, DOWNSTREAM); int closestOffsetInBox = 0; // FIXME: This approach is wrong. The correct code would first find the // closest SVGInlineTextBox to the point, and *then* ask only that inline box // what the closest text offset to that point is. This code instead walks // through all boxes in order, so when you click "near" a box, you'll actually // end up returning the nearest offset in the last box, even if the // nearest offset to your click is contained in another box. for (SVGInlineTextBox* box = textBox; box; box = static_cast<SVGInlineTextBox*>(box->nextTextBox())) { if (box->svgCharacterHitsPosition(point.x() + object->x(), point.y() + object->y(), closestOffsetInBox)) { // If we're not at the end/start of the box, stop looking for other selected boxes. if (box->direction() == LTR) { if (closestOffsetInBox <= (int) box->end() + 1) break; } else { if (closestOffsetInBox > (int) box->start()) break; } } } return createVisiblePosition(closestOffsetInBox, DOWNSTREAM); }
VisiblePosition SelectionModifier::modifyExtendingBackward( TextGranularity granularity) { VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity()); // Extending a selection backward by word or character from just after a table // selects the table. This "makes sense" from the user perspective, esp. when // deleting. It was done here instead of in VisiblePosition because we want // VPs to iterate over everything. switch (granularity) { case CharacterGranularity: pos = previousPositionOf(pos, CanSkipOverEditingBoundary); break; case WordGranularity: pos = previousWordPosition(pos); break; case SentenceGranularity: pos = previousSentencePosition(pos); break; case LineGranularity: pos = previousLinePosition( pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); break; case ParagraphGranularity: pos = previousParagraphPosition( pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); break; case SentenceBoundary: pos = startOfSentence(startForPlatform()); break; case LineBoundary: pos = logicalStartOfLine(startForPlatform()); break; case ParagraphBoundary: pos = startOfParagraph(startForPlatform()); break; case DocumentBoundary: pos = startForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = startOfEditableContent(pos); else pos = startOfDocument(pos); break; } adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); return pos; }
TEST_F(VisiblePositionTest, NonNullInvalidatedAfterDOMChange) { setBodyContent("<p>one</p>"); Element* paragraph = document().querySelector("p"); Position position(paragraph->firstChild(), 1); VisiblePosition nullVisiblePosition; VisiblePosition nonNullVisiblePosition = createVisiblePosition(position); Element* div = document().createElement("div"); document().body()->appendChild(div); EXPECT_TRUE(nullVisiblePosition.isValid()); EXPECT_FALSE(nonNullVisiblePosition.isValid()); updateAllLifecyclePhases(); // Invalid VisiblePosition can never become valid again. EXPECT_FALSE(nonNullVisiblePosition.isValid()); }