static VisiblePosition visiblePositionForHitTestResult(const HitTestResult& hitTestResult) { Node* innerNode = hitTestResult.innerNode(); VisiblePosition position(innerNode->renderer()->positionForPoint(hitTestResult.localPoint())); if (!position.isNull()) return position; return VisiblePosition(firstPositionInOrBeforeNode(innerNode), DOWNSTREAM); }
// Neighbor-anchored positions are invalid DOM positions, so they need to be // fixed up before handing them off to the Range object. Position Position::parentAnchoredEquivalent() const { if (!m_anchorNode) return Position(); // FIXME: This should only be necessary for legacy positions, but is also needed for positions before and after Tables if (m_offset <= 0 && m_anchorType != PositionIsAfterAnchor) { if (m_anchorNode->parentNode() && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get()))) return positionInParentBeforeNode(m_anchorNode.get()); return firstPositionInOrBeforeNode(m_anchorNode.get()); } if (!m_anchorNode->offsetInCharacters() && (m_anchorType == PositionIsAfterAnchor || static_cast<unsigned>(m_offset) == m_anchorNode->childNodeCount()) && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get())) && containerNode()) { return positionInParentAfterNode(m_anchorNode.get()); } return Position(containerNode(), computeOffsetInContainerNode(), PositionIsOffsetInAnchor); }
// Currenntly ignoring TextCheckingResult::details but should be handled. See Bug 56368. void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results) { if (!isValid(sequence)) return; if (!m_requestNode->renderer()) { clearRequest(); return; } int startOffset = 0; PositionIterator start = firstPositionInOrBeforeNode(m_requestNode.get()); for (size_t i = 0; i < results.size(); ++i) { if (results[i].type != TextCheckingTypeSpelling && results[i].type != TextCheckingTypeGrammar) continue; // To avoid moving the position backward, we assume the given results are sorted with // startOffset as the ones returned by [NSSpellChecker requestCheckingOfString:]. ASSERT(startOffset <= results[i].location); if (!forwardIterator(start, results[i].location - startOffset)) break; PositionIterator end = start; if (!forwardIterator(end, results[i].length)) break; // Users or JavaScript applications may change text while a spell-checker checks its // spellings in the background. To avoid adding markers to the words modified by users or // JavaScript applications, retrieve the words in the specified region and compare them with // the original ones. RefPtr<Range> range = Range::create(m_requestNode->document(), start, end); // FIXME: Use textContent() compatible string conversion. String destination = range->text(); String source = m_requestText.substring(results[i].location, results[i].length); if (destination == source) m_requestNode->document()->markers()->addMarker(range.get(), toMarkerType(results[i].type)); startOffset = results[i].location; } clearRequest(); }
VisiblePosition LocalFrame::visiblePositionForPoint(const IntPoint& framePoint) { if (!contentRenderer() || !view() || !view()->didFirstLayout()) return VisiblePosition(); LayoutSize padding = LayoutSize(); HitTestResult result(framePoint, padding.height(), padding.width(), padding.height(), padding.width()); HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); contentRenderer()->hitTest(request, result); Node* node = result.innerNonSharedNode(); if (!node) return VisiblePosition(); RenderObject* renderer = node->renderer(); if (!renderer) return VisiblePosition(); VisiblePosition visiblePos = VisiblePosition(renderer->positionForPoint(result.localPoint())); if (visiblePos.isNull()) visiblePos = VisiblePosition(firstPositionInOrBeforeNode(node)); return visiblePos; }
void VisibleSelection::adjustSelectionToAvoidCrossingShadowBoundaries() { if (m_base.isNull() || m_start.isNull() || m_end.isNull()) return; Node* startRootNode = m_start.anchorNode()->nonBoundaryShadowTreeRootNode(); Node* endRootNode = m_end.anchorNode()->nonBoundaryShadowTreeRootNode(); if (!startRootNode && !endRootNode) return; if (startRootNode == endRootNode) return; if (m_baseIsFirst) { m_extent = startRootNode ? lastPositionInOrAfterNode(startRootNode) : positionBeforeNode(endRootNode->shadowAncestorNode()); m_end = m_extent; } else { m_extent = endRootNode ? firstPositionInOrBeforeNode(endRootNode) : positionAfterNode(startRootNode->shadowAncestorNode()); m_start = m_extent; } ASSERT(m_start.anchorNode()->treeScope() == m_end.anchorNode()->treeScope()); }
static HTMLElement* highestAncestorToWrapMarkup( const PositionTemplate<Strategy>& startPosition, const PositionTemplate<Strategy>& endPosition, EAnnotateForInterchange shouldAnnotate, Node* constrainingAncestor) { Node* firstNode = startPosition.nodeAsRangeFirstNode(); // For compatibility reason, we use container node of start and end // positions rather than first node and last node in selection. Node* commonAncestor = Strategy::commonAncestor(*startPosition.computeContainerNode(), *endPosition.computeContainerNode()); DCHECK(commonAncestor); HTMLElement* specialCommonAncestor = nullptr; if (shouldAnnotate == AnnotateForInterchange) { // Include ancestors that aren't completely inside the range but are // required to retain the structure and appearance of the copied markup. specialCommonAncestor = ancestorToRetainStructureAndAppearance(commonAncestor); if (Node* parentListNode = enclosingNodeOfType( firstPositionInOrBeforeNode(firstNode), isListItem)) { EphemeralRangeTemplate<Strategy> markupRange = EphemeralRangeTemplate<Strategy>(startPosition, endPosition); EphemeralRangeTemplate<Strategy> nodeRange = normalizeRange( EphemeralRangeTemplate<Strategy>::rangeOfContents(*parentListNode)); if (nodeRange == markupRange) { ContainerNode* ancestor = parentListNode->parentNode(); while (ancestor && !isHTMLListElement(ancestor)) ancestor = ancestor->parentNode(); specialCommonAncestor = toHTMLElement(ancestor); } } // Retain the Mail quote level by including all ancestor mail block quotes. if (HTMLQuoteElement* highestMailBlockquote = toHTMLQuoteElement(highestEnclosingNodeOfType( firstPositionInOrBeforeNode(firstNode), isMailHTMLBlockquoteElement, CanCrossEditingBoundary))) specialCommonAncestor = highestMailBlockquote; } Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor; if (checkAncestor->layoutObject()) { HTMLElement* newSpecialCommonAncestor = toHTMLElement(highestEnclosingNodeOfType( Position::firstPositionInNode(checkAncestor), &isPresentationalHTMLElement, CanCrossEditingBoundary, constrainingAncestor)); if (newSpecialCommonAncestor) specialCommonAncestor = newSpecialCommonAncestor; } // If a single tab is selected, commonAncestor will be a text node inside a // tab span. If two or more tabs are selected, commonAncestor will be the tab // span. In either case, if there is a specialCommonAncestor already, it will // necessarily be above any tab span that needs to be included. if (!specialCommonAncestor && isTabHTMLSpanElementTextNode(commonAncestor)) specialCommonAncestor = toHTMLSpanElement(Strategy::parent(*commonAncestor)); if (!specialCommonAncestor && isTabHTMLSpanElement(commonAncestor)) specialCommonAncestor = toHTMLSpanElement(commonAncestor); if (HTMLAnchorElement* enclosingAnchor = toHTMLAnchorElement(enclosingElementWithTag( Position::firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : commonAncestor), aTag))) specialCommonAncestor = enclosingAnchor; return specialCommonAncestor; }
static inline HTMLElement* ancestorToRetainStructureAndAppearanceWithNoLayoutObject(Node* commonAncestor) { HTMLElement* commonAncestorBlock = toHTMLElement(enclosingNodeOfType( firstPositionInOrBeforeNode(commonAncestor), isHTMLBlockElement)); return ancestorToRetainStructureAndAppearanceForBlock(commonAncestorBlock); }