VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const { if (visiblePos.isNull()) return VisiblePositionRange(); // make a caret selection for the position before marker position (to make sure // we move off of a line start) VisiblePosition prevVisiblePos = visiblePos.previous(); if (prevVisiblePos.isNull()) return VisiblePositionRange(); VisiblePosition startPosition = startOfLine(prevVisiblePos); // keep searching for a valid line start position. Unless the VisiblePosition is at the very beginning, there should // always be a valid line range. However, startOfLine will return null for position next to a floating object, // since floating object doesn't really belong to any line. // This check will reposition the marker before the floating object, to ensure we get a line start. if (startPosition.isNull()) { while (startPosition.isNull() && prevVisiblePos.isNotNull()) { prevVisiblePos = prevVisiblePos.previous(); startPosition = startOfLine(prevVisiblePos); } } else startPosition = updateAXLineStartForVisiblePosition(startPosition); VisiblePosition endPosition = endOfLine(prevVisiblePos); return VisiblePositionRange(startPosition, endPosition); }
void IndentOutdentCommand::doApply() { if (endingSelection().isNone()) return; if (!endingSelection().rootEditableElement()) return; VisiblePosition visibleEnd = endingSelection().visibleEnd(); VisiblePosition visibleStart = endingSelection().visibleStart(); // When a selection ends at the start of a paragraph, we rarely paint // the selection gap before that paragraph, because there often is no gap. // In a case like this, it's not obvious to the user that the selection // ends "inside" that paragraph, so it would be confusing if Indent/Outdent // operated on that paragraph. // FIXME: We paint the gap before some paragraphs that are indented with left // margin/padding, but not others. We should make the gap painting more consistent and // then use a left margin/padding rule here. if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) setEndingSelection(Selection(visibleStart, visibleEnd.previous(true))); if (m_typeOfAction == Indent) indentRegion(); else outdentRegion(); }
RefPtr<Range> Frame::rangeForPoint(const IntPoint& framePoint) { VisiblePosition position = visiblePositionForPoint(framePoint); if (position.isNull()) return nullptr; Position deepPosition = position.deepEquivalent(); Text* containerText = deepPosition.containerText(); if (!containerText || !containerText->renderer() || containerText->renderer()->style().userSelect() == SELECT_NONE) return nullptr; VisiblePosition previous = position.previous(); if (previous.isNotNull()) { RefPtr<Range> previousCharacterRange = makeRange(previous, position); LayoutRect rect = editor().firstRectForRange(previousCharacterRange.get()); if (rect.contains(framePoint)) return previousCharacterRange; } VisiblePosition next = position.next(); if (RefPtr<Range> nextCharacterRange = makeRange(position, next)) { LayoutRect rect = editor().firstRectForRange(nextCharacterRange.get()); if (rect.contains(framePoint)) return nextCharacterRange; } return nullptr; }
static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition) { // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line. // So let's update the position to include that. VisiblePosition tempPosition; VisiblePosition startPosition = visiblePosition; Position p; RenderObject* renderer; while (true) { tempPosition = startPosition.previous(); if (tempPosition.isNull()) break; p = tempPosition.deepEquivalent(); if (!p.node()) break; renderer = p.node()->renderer(); if (!renderer || renderer->isRenderBlock() && !p.offset()) break; InlineBox* box; int ignoredCaretOffset; p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset); if (box) break; startPosition = tempPosition; } return startPosition; }
void InsertListCommand::unlistifyParagraph(const VisiblePosition& originalStart, HTMLElement* listNode, Node* listChildNode) { Node* nextListChild; Node* previousListChild; VisiblePosition start; VisiblePosition end; if (listChildNode->hasTagName(liTag)) { start = firstDeepEditingPositionForNode(listChildNode); end = lastDeepEditingPositionForNode(listChildNode); nextListChild = listChildNode->nextSibling(); previousListChild = listChildNode->previousSibling(); } else { // A paragraph is visually a list item minus a list marker. The paragraph will be moved. start = startOfParagraph(originalStart); end = endOfParagraph(start); nextListChild = enclosingListChild(end.next().deepEquivalent().node()); ASSERT(nextListChild != listChildNode); if (enclosingList(nextListChild) != listNode) nextListChild = 0; previousListChild = enclosingListChild(start.previous().deepEquivalent().node()); ASSERT(previousListChild != listChildNode); if (enclosingList(previousListChild) != listNode) previousListChild = 0; } // When removing a list, we must always create a placeholder to act as a point of insertion // for the list content being removed. RefPtr<Element> placeholder = createBreakElement(document()); RefPtr<Element> nodeToInsert = placeholder; // If the content of the list item will be moved into another list, put it in a list item // so that we don't create an orphaned list child. if (enclosingList(listNode)) { nodeToInsert = createListItemElement(document()); appendNode(placeholder, nodeToInsert); } if (nextListChild && previousListChild) { // We want to pull listChildNode out of listNode, and place it before nextListChild // and after previousListChild, so we split listNode and insert it between the two lists. // But to split listNode, we must first split ancestors of listChildNode between it and listNode, // if any exist. // FIXME: We appear to split at nextListChild as opposed to listChildNode so that when we remove // listChildNode below in moveParagraphs, previousListChild will be removed along with it if it is // unrendered. But we ought to remove nextListChild too, if it is unrendered. splitElement(listNode, splitTreeToNode(nextListChild, listNode)); insertNodeBefore(nodeToInsert, listNode); } else if (nextListChild || listChildNode->parentNode() != listNode) { // Just because listChildNode has no previousListChild doesn't mean there isn't any content // in listNode that comes before listChildNode, as listChildNode could have ancestors // between it and listNode. So, we split up to listNode before inserting the placeholder // where we're about to move listChildNode to. if (listChildNode->parentNode() != listNode) splitElement(listNode, splitTreeToNode(listChildNode, listNode).get()); insertNodeBefore(nodeToInsert, listNode); } else insertNodeAfter(nodeToInsert, listNode); VisiblePosition insertionPoint = VisiblePosition(Position(placeholder.get(), 0)); moveParagraphs(start, end, insertionPoint, true); }
bool isFirstVisiblePositionInNode(const VisiblePosition &pos, const NodeImpl *node) { if (pos.isNull()) return false; VisiblePosition previous = pos.previous(); return previous.isNull() || !previous.deepEquivalent().node()->isAncestor(node); }
void BlockCommand::formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection) { // might be null if the recursion below goes awry if (startOfSelection.isNull() || endOfSelection.isNull()) return; Node* startEnclosingCell = enclosingNodeOfType(startOfSelection.deepEquivalent(), &isTableCell); Node* endEnclosingCell = enclosingNodeOfType(endOfSelection.deepEquivalent(), &isTableCell); if (startEnclosingCell != endEnclosingCell) { if (startEnclosingCell && (!endEnclosingCell || !endEnclosingCell->isDescendantOf(startEnclosingCell))) { VisiblePosition newEnd = lastPositionInNode(startEnclosingCell); VisiblePosition nextStart = newEnd.next(); while (isTableElement(nextStart.deepEquivalent().anchorNode())) nextStart = nextStart.next(); // TODO: fix recursion! formatSelection(startOfSelection, newEnd); formatSelection(nextStart, endOfSelection); return; } ASSERT(endEnclosingCell); VisiblePosition nextStart = firstPositionInNode(endEnclosingCell); VisiblePosition newEnd = nextStart.previous(); while (isTableElement(newEnd.deepEquivalent().anchorNode())) newEnd = newEnd.previous(); // TODO: fix recursion! formatSelection(startOfSelection, newEnd); formatSelection(nextStart, endOfSelection); return; } Node* root = enclosingNodeOfType(startOfSelection.deepEquivalent(), &isTableCellOrRootEditable); if (!root || root == startOfSelection.deepEquivalent().anchorNode()) return; RefPtr<Node> currentNode = blockExtentStart(startOfSelection.deepEquivalent().anchorNode(), root); RefPtr<Node> endNode = blockExtentEnd(endOfSelection.deepEquivalent().anchorNode(), root); while (currentNode->isDescendantOf(endNode.get())) endNode = endNode->lastChild(); formatBlockExtent(currentNode, endNode, root); }
bool isFirstVisiblePositionOnLine(const VisiblePosition &pos) { if (pos.isNull()) return false; VisiblePosition previous = pos.previous(); return previous.isNull() || visiblePositionsOnDifferentLines(pos, previous); }
static VisiblePosition positionAvoidingFirstPositionInTable(const VisiblePosition& c) { // return table offset 0 instead of the first VisiblePosition inside the table VisiblePosition previous = c.previous(); if (isLastPositionBeforeTable(previous)) return previous; return c; }
void FormatBlockCommand::doApply() { if (endingSelection().isNone()) return; if (!endingSelection().rootEditableElement()) return; VisiblePosition visibleEnd = endingSelection().visibleEnd(); VisiblePosition visibleStart = endingSelection().visibleStart(); // When a selection ends at the start of a paragraph, we rarely paint // the selection gap before that paragraph, because there often is no gap. // In a case like this, it's not obvious to the user that the selection // ends "inside" that paragraph, so it would be confusing if FormatBlock // operated on that paragraph. // FIXME: We paint the gap before some paragraphs that are indented with left // margin/padding, but not others. We should make the gap painting more consistent and // then use a left margin/padding rule here. if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) setEndingSelection(Selection(visibleStart, visibleEnd.previous(true))); if (endingSelection().isRange() && modifyRange()) return; String localName, prefix; if (!Document::parseQualifiedName(m_tagName, prefix, localName)) return; QualifiedName qTypeOfBlock = QualifiedName(AtomicString(prefix), AtomicString(localName), xhtmlNamespaceURI); Node* refNode = enclosingBlockFlowElement(endingSelection().visibleStart()); if (refNode->hasTagName(qTypeOfBlock)) // We're already in a block with the format we want, so we don't have to do anything return; VisiblePosition paragraphStart = startOfParagraph(endingSelection().visibleStart()); VisiblePosition paragraphEnd = endOfParagraph(endingSelection().visibleStart()); VisiblePosition blockStart = startOfBlock(endingSelection().visibleStart()); VisiblePosition blockEnd = endOfBlock(endingSelection().visibleStart()); RefPtr<Node> blockNode = createElement(document(), m_tagName); RefPtr<Node> placeholder = createBreakElement(document()); Node* root = endingSelection().start().node()->rootEditableElement(); if (refNode == root || root->isDescendantOf(refNode)) refNode = paragraphStart.deepEquivalent().node(); Position upstreamStart = paragraphStart.deepEquivalent().upstream(); if ((validBlockTag(refNode->nodeName().lower()) && paragraphStart == blockStart && paragraphEnd == blockEnd) || !upstreamStart.node()->isDescendantOf(root)) // Already in a valid block tag that only contains the current paragraph, so we can swap with the new tag insertNodeBefore(blockNode.get(), refNode); else { insertNodeAt(blockNode.get(), upstreamStart.node(), upstreamStart.offset()); } appendNode(placeholder.get(), blockNode.get()); moveParagraph(paragraphStart, paragraphEnd, VisiblePosition(Position(placeholder.get(), 0)), true, false); }
bool isFirstVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) { if (visiblePosition.isNull()) return false; if (!visiblePosition.deepEquivalent().containerNode()->isDescendantOf(node)) return false; VisiblePosition previous = visiblePosition.previous(); return previous.isNull() || !previous.deepEquivalent().deprecatedNode()->isDescendantOf(node); }
bool isFirstVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) { if (visiblePosition.isNull()) return false; if (!visiblePosition.deepEquivalent().node()->isAncestor(node)) return false; VisiblePosition previous = visiblePosition.previous(); return previous.isNull() || !previous.deepEquivalent().node()->isAncestor(node); }
VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const { if (visiblePos.isNull()) return VisiblePosition(); // make sure we move off of a word start VisiblePosition prevVisiblePos = visiblePos.previous(); if (prevVisiblePos.isNull()) return VisiblePosition(); return startOfWord(prevVisiblePos, RightWordIfOnBoundary); }
VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const { if (visiblePos.isNull()) return VisiblePosition(); // make sure we move off of a paragraph start VisiblePosition previousPos = visiblePos.previous(); if (previousPos.isNull()) return VisiblePosition(); return startOfParagraph(previousPos); }
PassRefPtr<Range> trimWhitespaceFromRange(VisiblePosition startPosition, VisiblePosition endPosition) { if (startPosition == endPosition || isRangeTextAllWhitespace(startPosition, endPosition)) return 0; while (isWhitespace(startPosition.characterAfter())) startPosition = startPosition.next(); while (isWhitespace(endPosition.characterBefore())) endPosition = endPosition.previous(); return makeRange(startPosition, endPosition); }
VisiblePosition logicalEndOfLine(const VisiblePosition& c) { VisiblePosition visPos = logicalEndPositionForLine(c); // Make sure the end of line is at the same line as the given input position. For a wrapping line, the logical end // position for the not-last-2-lines might incorrectly hand back the logical beginning of the next line. // For example, <div contenteditable dir="rtl" style="line-break:before-white-space">abcdefg abcdefg abcdefg // a abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg </div> // In this case, use the previous position of the computed logical end position. if (!inSameLogicalLine(c, visPos)) visPos = visPos.previous(); return c.honorEditableBoundaryAtOrBefore(visPos); }
static Node* lastInSpecialElement(const Position& pos) { Node* rootEditableElement = pos.node()->rootEditableElement(); for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) if (isSpecialElement(n)) { VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); VisiblePosition lastInElement = VisiblePosition(n, n->childNodeCount(), DOWNSTREAM); if (isTableElement(n) && vPos == lastInElement.previous()) return n; if (vPos == lastInElement) return n; } return 0; }
static Node* lastInSpecialElement(const Position& pos) { Node* rootEditableElement = pos.containerNode()->rootEditableElement(); for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) if (isSpecialElement(n)) { VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(n), DOWNSTREAM); if (isTableElement(n) && vPos == lastInElement.previous()) return n; if (vPos == lastInElement) return n; } return 0; }
void ApplyBlockElementCommand::doApply() { if (!endingSelection().rootEditableElement()) return; VisiblePosition visibleEnd = endingSelection().visibleEnd(); VisiblePosition visibleStart = endingSelection().visibleStart(); if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || visibleEnd.isOrphan()) return; // When a selection ends at the start of a paragraph, we rarely paint // the selection gap before that paragraph, because there often is no gap. // In a case like this, it's not obvious to the user that the selection // ends "inside" that paragraph, so it would be confusing if Indent/Outdent // operated on that paragraph. // FIXME: We paint the gap before some paragraphs that are indented with left // margin/padding, but not others. We should make the gap painting more consistent and // then use a left margin/padding rule here. if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) { VisibleSelection newSelection(visibleStart, visibleEnd.previous(CannotCrossEditingBoundary), endingSelection().isDirectional()); if (newSelection.isNone()) return; setEndingSelection(newSelection); } VisibleSelection selection = selectionForParagraphIteration(endingSelection()); VisiblePosition startOfSelection = selection.visibleStart(); VisiblePosition endOfSelection = selection.visibleEnd(); ASSERT(!startOfSelection.isNull()); ASSERT(!endOfSelection.isNull()); RefPtrWillBeRawPtr<ContainerNode> startScope = nullptr; int startIndex = indexForVisiblePosition(startOfSelection, startScope); RefPtrWillBeRawPtr<ContainerNode> endScope = nullptr; int endIndex = indexForVisiblePosition(endOfSelection, endScope); formatSelection(startOfSelection, endOfSelection); document().updateLayoutIgnorePendingStylesheets(); ASSERT(startScope == endScope); ASSERT(startIndex >= 0); ASSERT(startIndex <= endIndex); if (startScope == endScope && startIndex >= 0 && startIndex <= endIndex) { VisiblePosition start(visiblePositionForIndex(startIndex, startScope.get())); VisiblePosition end(visiblePositionForIndex(endIndex, endScope.get())); if (start.isNotNull() && end.isNotNull()) setEndingSelection(VisibleSelection(start, end, endingSelection().isDirectional())); } }
static Node* lastInSpecialElement(const Position& pos) { // FIXME: This begins at pos.deprecatedNode(), which doesn't necessarily contain pos (suppose pos was [img, 0]). See <rdar://problem/5027702>. Node* rootEditableElement = pos.deprecatedNode()->rootEditableElement(); for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) if (isSpecialElement(n)) { VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); VisiblePosition lastInElement = VisiblePosition(Position(n, n->childNodeCount(), Position::PositionIsOffsetInAnchor), DOWNSTREAM); if (isTableElement(n) && vPos == lastInElement.previous()) return n; if (vPos == lastInElement) return n; } return 0; }
void Editor::transpose() { if (!canEdit()) return; VisibleSelection selection = m_frame.selection().selection(); if (!selection.isCaret()) return; // Make a selection that goes back one character and forward two characters. VisiblePosition caret = selection.visibleStart(); VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next(); VisiblePosition previous = next.previous(); if (next == previous) return; previous = previous.previous(); if (!inSameParagraph(next, previous)) return; RefPtr<Range> range = makeRange(previous, next); if (!range) return; VisibleSelection newSelection(range.get(), DOWNSTREAM); // Transpose the two characters. String text = plainText(range.get()); if (text.length() != 2) return; String transposed = text.right(1) + text.left(1); // Select the two characters. if (newSelection != m_frame.selection().selection()) m_frame.selection().setSelection(newSelection); // Insert the transposed characters. replaceSelectionWithText(transposed, false, false); }
VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const { if (visiblePos.isNull()) return VisiblePosition(); // make sure we move off of a line start VisiblePosition prevVisiblePos = visiblePos.previous(); if (prevVisiblePos.isNull()) return VisiblePosition(); VisiblePosition startPosition = startOfLine(prevVisiblePos); // as long as the position hasn't reached the beginning of the doc, keep searching for a valid line start position // There are cases like when the position is next to a floating object that'll return null for start of line. This code will avoid returning null. if (startPosition.isNull()) { while (startPosition.isNull() && prevVisiblePos.isNotNull()) { prevVisiblePos = prevVisiblePos.previous(); startPosition = startOfLine(prevVisiblePos); } } else startPosition = updateAXLineStartForVisiblePosition(startPosition); return startPosition; }
VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side) { VisiblePosition p = c; if (side == LeftWordIfOnBoundary) { if (isStartOfParagraph(c)) return c; p = c.previous(); if (p.isNull()) return c; } else if (isEndOfParagraph(c)) return c; return nextBoundary(p, endWordBoundary); }
void InsertListCommand::doApply() { if (endingSelection().isNone()) return; if (!endingSelection().rootEditableElement()) return; VisiblePosition visibleEnd = endingSelection().visibleEnd(); VisiblePosition visibleStart = endingSelection().visibleStart(); // When a selection ends at the start of a paragraph, we rarely paint // the selection gap before that paragraph, because there often is no gap. // In a case like this, it's not obvious to the user that the selection // ends "inside" that paragraph, so it would be confusing if InsertUn{Ordered}List // operated on that paragraph. // FIXME: We paint the gap before some paragraphs that are indented with left // margin/padding, but not others. We should make the gap painting more consistent and // then use a left margin/padding rule here. if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(true))); if (endingSelection().isRange() && modifyRange()) return; // FIXME: This will produce unexpected results for a selection that starts just before a // table and ends inside the first cell, selectionForParagraphIteration should probably // be renamed and deployed inside setEndingSelection(). Node* selectionNode = endingSelection().start().node(); const QualifiedName listTag = (m_type == OrderedList) ? olTag : ulTag; Node* listChildNode = enclosingListChild(selectionNode); bool switchListType = false; if (listChildNode) { // Remove the list chlild. HTMLElement* listNode = enclosingList(listChildNode); if (!listNode) listNode = fixOrphanedListChild(listChildNode); if (!listNode->hasTagName(listTag)) // listChildNode will be removed from the list and a list of type m_type will be created. switchListType = true; unlistifyParagraph(endingSelection().visibleStart(), listNode, listChildNode); } if (!listChildNode || switchListType || m_forceCreateList) m_listElement = listifyParagraph(endingSelection().visibleStart(), listTag); }
VisiblePosition endOfLine(const VisiblePosition& c) { VisiblePosition visPos = endPositionForLine(c); // Make sure the end of line is at the same line as the given input position. Else use the previous position to // obtain end of line. This condition happens when the input position is before the space character at the end // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style // versus lines without that style, which would break before a space by default. if (!inSameLine(c, visPos)) { visPos = c.previous(); if (visPos.isNull()) return VisiblePosition(); visPos = endPositionForLine(visPos); } return c.honorEditableBoundaryAtOrBefore(visPos); }
void IndentOutdentCommand::doApply() { if (!endingSelection().isNonOrphanedCaretOrRange()) return; if (!endingSelection().rootEditableElement()) return; VisiblePosition visibleEnd = endingSelection().visibleEnd(); VisiblePosition visibleStart = endingSelection().visibleStart(); // When a selection ends at the start of a paragraph, we rarely paint // the selection gap before that paragraph, because there often is no gap. // In a case like this, it's not obvious to the user that the selection // ends "inside" that paragraph, so it would be confusing if Indent/Outdent // operated on that paragraph. // FIXME: We paint the gap before some paragraphs that are indented with left // margin/padding, but not others. We should make the gap painting more consistent and // then use a left margin/padding rule here. if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(true))); VisibleSelection selection = selectionForParagraphIteration(endingSelection()); VisiblePosition startOfSelection = selection.visibleStart(); VisiblePosition endOfSelection = selection.visibleEnd(); int startIndex = indexForVisiblePosition(startOfSelection); int endIndex = indexForVisiblePosition(endOfSelection); ASSERT(!startOfSelection.isNull()); ASSERT(!endOfSelection.isNull()); if (m_typeOfAction == Indent) indentRegion(startOfSelection, endOfSelection); else outdentRegion(startOfSelection, endOfSelection); updateLayout(); RefPtr<Range> startRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), startIndex, 0, true); RefPtr<Range> endRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endIndex, 0, true); if (startRange && endRange) setEndingSelection(VisibleSelection(startRange->startPosition(), endRange->startPosition(), DOWNSTREAM)); }
VisiblePosition startOfLine(const VisiblePosition& c) { VisiblePosition visPos = startPositionForLine(c); if (visPos.isNotNull()) { // Make sure the start of line is not greater than the given input position. Else use the previous position to // obtain start of line. This condition happens when the input position is before the space character at the end // of a soft-wrapped non-editable line. In this scenario, startPositionForLine would incorrectly hand back a position // greater than the input position. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space // style versus lines without that style, which would break before a space by default. Position p = visPos.deepEquivalent(); if (p.deprecatedEditingOffset() > c.deepEquivalent().deprecatedEditingOffset() && p.node()->isSameNode(c.deepEquivalent().node())) { visPos = c.previous(); if (visPos.isNull()) return VisiblePosition(); visPos = startPositionForLine(visPos); } } return c.honorEditableBoundaryAtOrAfter(visPos); }
static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition) { // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line. // So let's update the position to include that. VisiblePosition tempPosition; VisiblePosition startPosition = visiblePosition; while (true) { tempPosition = startPosition.previous(); if (tempPosition.isNull()) break; Position p = tempPosition.deepEquivalent(); RenderObject* renderer = p.deprecatedNode()->renderer(); if (!renderer || (renderer->isRenderBlock() && !p.deprecatedEditingOffset())) break; if (!RenderedPosition(tempPosition).isNull()) break; startPosition = tempPosition; } return startPosition; }
VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const { // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer) // Related? <rdar://problem/3927736> Text selection broken in 8A336 if (visiblePos.isNull()) return VisiblePosition(); // make sure we move off of a sentence start VisiblePosition previousVisiblePos = visiblePos.previous(); if (previousVisiblePos.isNull()) return VisiblePosition(); // treat empty line as a separate sentence. VisiblePosition startPosition; String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get()); if (lineString.isEmpty()) startPosition = previousVisiblePos; else startPosition = startOfSentence(previousVisiblePos); return startPosition; }
PassRefPtr<Range> Frame::rangeForPoint(const LayoutPoint& framePoint) { VisiblePosition position = visiblePositionForPoint(framePoint); if (position.isNull()) return 0; VisiblePosition previous = position.previous(); if (previous.isNotNull()) { RefPtr<Range> previousCharacterRange = makeRange(previous, position); LayoutRect rect = editor()->firstRectForRange(previousCharacterRange.get()); if (rect.contains(framePoint)) return previousCharacterRange.release(); } VisiblePosition next = position.next(); if (RefPtr<Range> nextCharacterRange = makeRange(position, next)) { LayoutRect rect = editor()->firstRectForRange(nextCharacterRange.get()); if (rect.contains(framePoint)) return nextCharacterRange.release(); } return 0; }