RefPtr<TextIndicator> TextIndicator::createWithRange(const Range& range, TextIndicatorPresentationTransition presentationTransition, unsigned margin) { Frame* frame = range.startContainer()->document().frame(); if (!frame) return nullptr; #if PLATFORM(IOS) frame->editor().setIgnoreCompositionSelectionChange(true); frame->selection().setUpdateAppearanceEnabled(true); #endif VisibleSelection oldSelection = frame->selection().selection(); frame->selection().setSelection(range); RefPtr<TextIndicator> indicator = TextIndicator::createWithSelectionInFrame(*frame, presentationTransition, margin); frame->selection().setSelection(oldSelection); if (indicator) indicator->setWantsMargin(!areRangesEqual(&range, oldSelection.toNormalizedRange().get())); #if PLATFORM(IOS) frame->editor().setIgnoreCompositionSelectionChange(false, Editor::RevealSelection::No); frame->selection().setUpdateAppearanceEnabled(false); #endif return indicator.release(); }
int TextFinder::selectFindMatch(unsigned index, WebRect* selectionRect) { SECURITY_DCHECK(index < m_findMatchesCache.size()); Range* range = m_findMatchesCache[index].m_range; if (!range->boundaryPointsValid() || !range->startContainer()->isConnected()) return -1; // Check if the match is already selected. if (!m_currentActiveMatchFrame || !m_activeMatch || !areRangesEqual(m_activeMatch.get(), range)) { m_activeMatchIndex = m_findMatchesCache[index].m_ordinal - 1; // Set this frame as the active frame (the one with the active highlight). m_currentActiveMatchFrame = true; ownerFrame().viewImpl()->setFocusedFrame(&ownerFrame()); if (m_activeMatch) setMarkerActive(m_activeMatch.get(), false); m_activeMatch = range; setMarkerActive(m_activeMatch.get(), true); // Clear any user selection, to make sure Find Next continues on from the // match we just activated. ownerFrame().frame()->selection().clear(); // Make sure no node is focused. See http://crbug.com/38700. ownerFrame().frame()->document()->clearFocusedElement(); } IntRect activeMatchRect; IntRect activeMatchBoundingBox = enclosingIntRect( LayoutObject::absoluteBoundingBoxRectForRange(m_activeMatch.get())); if (!activeMatchBoundingBox.isEmpty()) { if (m_activeMatch->firstNode() && m_activeMatch->firstNode()->layoutObject()) { m_activeMatch->firstNode()->layoutObject()->scrollRectToVisible( LayoutRect(activeMatchBoundingBox), ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded, UserScroll); } // Zoom to the active match. activeMatchRect = ownerFrame().frameView()->contentsToRootFrame(activeMatchBoundingBox); ownerFrame().viewImpl()->zoomToFindInPageRect(activeMatchRect); } if (selectionRect) *selectionRect = activeMatchRect; return m_activeMatchIndex + 1; }
int TextFinder::selectFindMatch(unsigned index, WebRect* selectionRect) { ASSERT_WITH_SECURITY_IMPLICATION(index < m_findMatchesCache.size()); RefPtrWillBeRawPtr<Range> range = m_findMatchesCache[index].m_range; if (!range->boundaryPointsValid() || !range->startContainer()->inDocument()) return -1; // Check if the match is already selected. TextFinder& mainFrameTextFinder = m_ownerFrame.viewImpl()->mainFrameImpl()->ensureTextFinder(); WebLocalFrameImpl* activeMatchFrame = mainFrameTextFinder.m_currentActiveMatchFrame; if (&m_ownerFrame != activeMatchFrame || !m_activeMatch || !areRangesEqual(m_activeMatch.get(), range.get())) { if (isActiveMatchFrameValid()) activeMatchFrame->ensureTextFinder().setMatchMarkerActive(false); m_activeMatchIndexInCurrentFrame = m_findMatchesCache[index].m_ordinal - 1; // Set this frame as the active frame (the one with the active highlight). mainFrameTextFinder.m_currentActiveMatchFrame = &m_ownerFrame; m_ownerFrame.viewImpl()->setFocusedFrame(&m_ownerFrame); m_activeMatch = range.release(); setMarkerActive(m_activeMatch.get(), true); // Clear any user selection, to make sure Find Next continues on from the match we just activated. m_ownerFrame.frame()->selection().clear(); // Make sure no node is focused. See http://crbug.com/38700. m_ownerFrame.frame()->document()->setFocusedElement(nullptr); } IntRect activeMatchRect; IntRect activeMatchBoundingBox = enclosingIntRect(RenderObject::absoluteBoundingBoxRectForRange(m_activeMatch.get())); if (!activeMatchBoundingBox.isEmpty()) { if (m_activeMatch->firstNode() && m_activeMatch->firstNode()->renderer()) { m_activeMatch->firstNode()->renderer()->scrollRectToVisible( activeMatchBoundingBox, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded); } // Zoom to the active match. activeMatchRect = m_ownerFrame.frameView()->contentsToWindow(activeMatchBoundingBox); m_ownerFrame.viewImpl()->zoomToFindInPageRect(activeMatchRect); } if (selectionRect) *selectionRect = activeMatchRect; return ordinalOfFirstMatch() + m_activeMatchIndexInCurrentFrame + 1; }
PassRefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRange, FindOptions options) { if (target.isEmpty()) return nullptr; // Start from an edge of the reference range. Which edge is used depends on whether we're searching forward or // backward, and whether startInSelection is set. Position searchStart = firstPositionInNode(m_frame.document()); Position searchEnd = lastPositionInNode(m_frame.document()); bool forward = !(options & Backwards); bool startInReferenceRange = referenceRange && (options & StartInSelection); if (referenceRange) { if (forward) searchStart = startInReferenceRange ? referenceRange->startPosition() : referenceRange->endPosition(); else searchEnd = startInReferenceRange ? referenceRange->endPosition() : referenceRange->startPosition(); } RefPtr<Range> resultRange = findStringBetweenPositions(target, searchStart, searchEnd, options); // If we started in the reference range and the found range exactly matches the reference range, find again. // Build a selection with the found range to remove collapsed whitespace. // Compare ranges instead of selection objects to ignore the way that the current selection was made. if (resultRange && startInReferenceRange && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), referenceRange)) { if (forward) searchStart = resultRange->endPosition(); else searchEnd = resultRange->startPosition(); resultRange = findStringBetweenPositions(target, searchStart, searchEnd, options); } if (!resultRange && options & WrapAround) { searchStart = firstPositionInNode(m_frame.document()); searchEnd = lastPositionInNode(m_frame.document()); resultRange = findStringBetweenPositions(target, searchStart, searchEnd, options); } return resultRange.release(); }
RefPtr<TextIndicator> TextIndicator::createWithRange(const Range& range, TextIndicatorOptions options, TextIndicatorPresentationTransition presentationTransition, FloatSize margin) { Frame* frame = range.startContainer().document().frame(); if (!frame) return nullptr; #if PLATFORM(IOS) frame->editor().setIgnoreCompositionSelectionChange(true); frame->selection().setUpdateAppearanceEnabled(true); #endif VisibleSelection oldSelection = frame->selection().selection(); frame->selection().setSelection(range); TextIndicatorData data; data.presentationTransition = presentationTransition; data.options = options; bool indicatesCurrentSelection = areRangesEqual(&range, oldSelection.toNormalizedRange().get()); if (!initializeIndicator(data, *frame, range, margin, indicatesCurrentSelection)) return nullptr; RefPtr<TextIndicator> indicator = TextIndicator::create(data); frame->selection().setSelection(oldSelection); #if PLATFORM(IOS) frame->editor().setIgnoreCompositionSelectionChange(false, Editor::RevealSelection::No); frame->selection().setUpdateAppearanceEnabled(false); #endif return indicator; }
void InPageSearchManager::scopeStringMatches(const String& text, bool reset, bool locateActiveMatchOnly, Frame* scopingFrame) { if (reset) { if (!locateActiveMatchOnly) { m_activeMatchCount = 0; m_scopingComplete = false; } m_resumeScopingFromRange = 0; m_locatingActiveMatch = true; m_activeMatchIndex = 0; // New search should always start from mainFrame. scopeStringMatchesSoon(m_webPage->mainFrame(), text, false /* reset */, locateActiveMatchOnly); return; } if (m_resumeScopingFromRange && scopingFrame != m_resumeScopingFromRange->ownerDocument().frame()) m_resumeScopingFromRange = 0; RefPtr<Range> searchRange(rangeOfContents(scopingFrame->document())); Node* originalEndContainer = searchRange->endContainer(); int originalEndOffset = searchRange->endOffset(); ExceptionCode ec = 0, ec2 = 0; if (m_resumeScopingFromRange) { searchRange->setStart(m_resumeScopingFromRange->startContainer(), m_resumeScopingFromRange->startOffset(ec2) + 1, ec); if (ec || ec2) { m_scopingComplete = true; // We should stop scoping because of some stale data. return; } } int matchCount = 0; bool timeout = false; double startTime = currentTime(); do { RefPtr<Range> resultRange(findPlainText(searchRange.get(), text, m_scopingCaseInsensitive ? CaseInsensitive : 0)); if (resultRange->collapsed(ec)) { if (!resultRange->startContainer()->isInShadowTree()) break; searchRange->setStartAfter(resultRange->startContainer()->deprecatedShadowAncestorNode(), ec); searchRange->setEnd(originalEndContainer, originalEndOffset, ec); continue; } ++matchCount; bool foundActiveMatch = false; if (m_locatingActiveMatch && areRangesEqual(resultRange.get(), m_activeMatch.get())) { foundActiveMatch = true; m_locatingActiveMatch = false; if (locateActiveMatchOnly) { m_activeMatchIndex += matchCount; m_webPage->m_client->updateFindStringResult(m_activeMatchCount, m_activeMatchIndex); return; } m_activeMatchIndex = m_activeMatchCount + matchCount; } if (!locateActiveMatchOnly && m_highlightAllMatches) resultRange->ownerDocument().markers().addTextMatchMarker(resultRange.get(), foundActiveMatch); searchRange->setStart(resultRange->endContainer(ec), resultRange->endOffset(ec), ec); ShadowRoot* shadowTreeRoot = searchRange->shadowRoot(); if (searchRange->collapsed(ec) && shadowTreeRoot) searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); m_resumeScopingFromRange = resultRange; timeout = (currentTime() - startTime) >= MaxScopingDuration; } while (!timeout); if (matchCount > 0) { if (locateActiveMatchOnly) { // We have not found it yet. // m_activeMatchIndex now temporarily remember where we left over in this time slot. m_activeMatchIndex += matchCount; } else { if (m_highlightAllMatches) scopingFrame->editor().setMarkedTextMatchesAreHighlighted(true /* highlight */); m_activeMatchCount += matchCount; m_webPage->m_client->updateFindStringResult(m_activeMatchCount, m_activeMatchIndex); } } if (timeout) scopeStringMatchesSoon(scopingFrame, text, false /* reset */, locateActiveMatchOnly); else { // Scoping is done for this frame. Frame* nextFrame = DOMSupport::incrementFrame(scopingFrame, true /* forward */, false /* wrapFlag */); if (!nextFrame) { m_scopingComplete = true; return; // Scoping is done for all frames; } scopeStringMatchesSoon(nextFrame, text, false /* reset */, locateActiveMatchOnly); } }
void InPageSearchManager::scopeStringMatches(const String& text, bool reset, Frame* scopingFrame) { if (reset) { m_activeMatchCount = 0; m_resumeScopingFromRange = 0; m_scopingComplete = false; m_locatingActiveMatch = true; // New search should always start from mainFrame. scopeStringMatchesSoon(m_webPage->mainFrame(), text, false /* reset */); return; } if (m_resumeScopingFromRange && scopingFrame != m_resumeScopingFromRange->ownerDocument()->frame()) m_resumeScopingFromRange = 0; RefPtr<Range> searchRange(rangeOfContents(scopingFrame->document())); Node* originalEndContainer = searchRange->endContainer(); int originalEndOffset = searchRange->endOffset(); ExceptionCode ec = 0, ec2 = 0; if (m_resumeScopingFromRange) { searchRange->setStart(m_resumeScopingFromRange->startContainer(), m_resumeScopingFromRange->startOffset(ec2) + 1, ec); if (ec || ec2) { m_scopingComplete = true; // We should stop scoping because of some stale data. return; } } int matchCount = 0; bool timeout = false; double startTime = currentTime(); do { RefPtr<Range> resultRange(findPlainText(searchRange.get(), text, CaseInsensitive)); if (resultRange->collapsed(ec)) { if (!resultRange->startContainer()->isInShadowTree()) break; searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec); searchRange->setEnd(originalEndContainer, originalEndOffset, ec); continue; } if (scopingFrame->editor()->insideVisibleArea(resultRange.get())) { ++matchCount; bool foundActiveMatch = false; if (m_locatingActiveMatch && areRangesEqual(resultRange.get(), m_activeMatch.get())) { foundActiveMatch = true; m_locatingActiveMatch = false; m_activeMatchIndex = m_activeMatchCount + matchCount; // FIXME: We need to notify client with m_activeMatchIndex. } resultRange->ownerDocument()->markers()->addTextMatchMarker(resultRange.get(), foundActiveMatch); } searchRange->setStart(resultRange->endContainer(ec), resultRange->endOffset(ec), ec); Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); if (searchRange->collapsed(ec) && shadowTreeRoot) searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); m_resumeScopingFromRange = resultRange; timeout = (currentTime() - startTime) >= MaxScopingDuration; } while (!timeout); if (matchCount > 0) { scopingFrame->editor()->setMarkedTextMatchesAreHighlighted(true /* highlight */); m_activeMatchCount += matchCount; } if (timeout) scopeStringMatchesSoon(scopingFrame, text, false /* reset */); else { // Scoping is done for this frame. Frame* nextFrame = DOMSupport::incrementFrame(scopingFrame, true /* forward */, false /* wrapFlag */); if (!nextFrame) { m_scopingComplete = true; return; // Scoping is done for all frames; } scopeStringMatchesSoon(nextFrame, text, false /* reset */); } }