Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2009 Google Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions are |
| 6 | * met: |
| 7 | * |
| 8 | * * Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * * Redistributions in binary form must reproduce the above |
| 11 | * copyright notice, this list of conditions and the following disclaimer |
| 12 | * in the documentation and/or other materials provided with the |
| 13 | * distribution. |
| 14 | * * Neither the name of Google Inc. nor the names of its |
| 15 | * contributors may be used to endorse or promote products derived from |
| 16 | * this software without specific prior written permission. |
| 17 | * |
| 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | */ |
| 30 | |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 31 | |
| 32 | #include "config.h" |
| 33 | #include "TextFinder.h" |
| 34 | |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 35 | #include "FindInPageCoordinates.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 36 | #include "WebFindOptions.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 37 | #include "WebFrameClient.h" |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 38 | #include "WebFrameImpl.h" |
| 39 | #include "WebViewClient.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 40 | #include "WebViewImpl.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 41 | #include "core/dom/DocumentMarker.h" |
| 42 | #include "core/dom/DocumentMarkerController.h" |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 43 | #include "core/dom/Range.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 44 | #include "core/dom/shadow/ShadowRoot.h" |
| 45 | #include "core/editing/Editor.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 46 | #include "core/editing/TextIterator.h" |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 47 | #include "core/editing/VisibleSelection.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 48 | #include "core/frame/FrameView.h" |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 49 | #include "platform/Timer.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 50 | #include "public/platform/WebVector.h" |
| 51 | #include "wtf/CurrentTime.h" |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 52 | |
| 53 | using namespace WebCore; |
| 54 | |
| 55 | namespace blink { |
| 56 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 57 | TextFinder::FindMatch::FindMatch(PassRefPtr<Range> range, int ordinal) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 58 | : m_range(range) |
| 59 | , m_ordinal(ordinal) |
| 60 | { |
| 61 | } |
| 62 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 63 | class TextFinder::DeferredScopeStringMatches { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 64 | public: |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 65 | DeferredScopeStringMatches(TextFinder* textFinder, int identifier, const WebString& searchText, const WebFindOptions& options, bool reset) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 66 | : m_timer(this, &DeferredScopeStringMatches::doTimeout) |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 67 | , m_textFinder(textFinder) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 68 | , m_identifier(identifier) |
| 69 | , m_searchText(searchText) |
| 70 | , m_options(options) |
| 71 | , m_reset(reset) |
| 72 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 73 | m_timer.startOneShot(0.0, FROM_HERE); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 74 | } |
| 75 | |
| 76 | private: |
| 77 | void doTimeout(Timer<DeferredScopeStringMatches>*) |
| 78 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 79 | m_textFinder->callScopeStringMatches(this, m_identifier, m_searchText, m_options, m_reset); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | Timer<DeferredScopeStringMatches> m_timer; |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 83 | TextFinder* m_textFinder; |
| 84 | const int m_identifier; |
| 85 | const WebString m_searchText; |
| 86 | const WebFindOptions m_options; |
| 87 | const bool m_reset; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 88 | }; |
| 89 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 90 | bool TextFinder::find(int identifier, const WebString& searchText, const WebFindOptions& options, bool wrapWithinFrame, WebRect* selectionRect) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 91 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 92 | if (!m_ownerFrame.frame() || !m_ownerFrame.frame()->page()) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 93 | return false; |
| 94 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 95 | WebFrameImpl* mainFrameImpl = m_ownerFrame.viewImpl()->mainFrameImpl(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 96 | |
| 97 | if (!options.findNext) |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 98 | m_ownerFrame.frame()->page()->unmarkAllTextMatches(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 99 | else |
| 100 | setMarkerActive(m_activeMatch.get(), false); |
| 101 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 102 | if (m_activeMatch && &m_activeMatch->ownerDocument() != m_ownerFrame.frame()->document()) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 103 | m_activeMatch = nullptr; |
| 104 | |
| 105 | // If the user has selected something since the last Find operation we want |
| 106 | // to start from there. Otherwise, we start searching from where the last Find |
| 107 | // operation left off (either a Find or a FindNext operation). |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 108 | VisibleSelection selection(m_ownerFrame.frame()->selection().selection()); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 109 | bool activeSelection = !selection.isNone(); |
| 110 | if (activeSelection) { |
| 111 | m_activeMatch = selection.firstRange().get(); |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 112 | m_ownerFrame.frame()->selection().clear(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 113 | } |
| 114 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 115 | ASSERT(m_ownerFrame.frame() && m_ownerFrame.frame()->view()); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 116 | const FindOptions findOptions = (options.forward ? 0 : Backwards) |
| 117 | | (options.matchCase ? 0 : CaseInsensitive) |
| 118 | | (wrapWithinFrame ? WrapAround : 0) |
| 119 | | (options.wordStart ? AtWordStarts : 0) |
| 120 | | (options.medialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0) |
| 121 | | (options.findNext ? 0 : StartInSelection); |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 122 | m_activeMatch = m_ownerFrame.frame()->editor().findStringAndScrollToVisible(searchText, m_activeMatch.get(), findOptions); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 123 | |
| 124 | if (!m_activeMatch) { |
| 125 | // If we're finding next the next active match might not be in the current frame. |
| 126 | // In this case we don't want to clear the matches cache. |
| 127 | if (!options.findNext) |
| 128 | clearFindMatchesCache(); |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 129 | |
| 130 | m_ownerFrame.invalidateAll(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 131 | return false; |
| 132 | } |
| 133 | |
| 134 | #if OS(ANDROID) |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 135 | m_ownerFrame.viewImpl()->zoomToFindInPageRect(m_ownerFrame.frameView()->contentsToWindow(enclosingIntRect(RenderObject::absoluteBoundingBoxRectForRange(m_activeMatch.get())))); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 136 | #endif |
| 137 | |
| 138 | setMarkerActive(m_activeMatch.get(), true); |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 139 | WebFrameImpl* oldActiveFrame = mainFrameImpl->ensureTextFinder().m_currentActiveMatchFrame; |
| 140 | mainFrameImpl->ensureTextFinder().m_currentActiveMatchFrame = &m_ownerFrame; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 141 | |
| 142 | // Make sure no node is focused. See http://crbug.com/38700. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 143 | m_ownerFrame.frame()->document()->setFocusedElement(nullptr); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 144 | |
| 145 | if (!options.findNext || activeSelection) { |
| 146 | // This is either a Find operation or a Find-next from a new start point |
| 147 | // due to a selection, so we set the flag to ask the scoping effort |
| 148 | // to find the active rect for us and report it back to the UI. |
| 149 | m_locatingActiveRect = true; |
| 150 | } else { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 151 | if (oldActiveFrame != &m_ownerFrame) { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 152 | if (options.forward) |
| 153 | m_activeMatchIndexInCurrentFrame = 0; |
| 154 | else |
| 155 | m_activeMatchIndexInCurrentFrame = m_lastMatchCount - 1; |
| 156 | } else { |
| 157 | if (options.forward) |
| 158 | ++m_activeMatchIndexInCurrentFrame; |
| 159 | else |
| 160 | --m_activeMatchIndexInCurrentFrame; |
| 161 | |
| 162 | if (m_activeMatchIndexInCurrentFrame + 1 > m_lastMatchCount) |
| 163 | m_activeMatchIndexInCurrentFrame = 0; |
| 164 | if (m_activeMatchIndexInCurrentFrame == -1) |
| 165 | m_activeMatchIndexInCurrentFrame = m_lastMatchCount - 1; |
| 166 | } |
| 167 | if (selectionRect) { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 168 | *selectionRect = m_ownerFrame.frameView()->contentsToWindow(m_activeMatch->boundingBox()); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 169 | reportFindInPageSelection(*selectionRect, m_activeMatchIndexInCurrentFrame + 1, identifier); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | return true; |
| 174 | } |
| 175 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 176 | void TextFinder::stopFindingAndClearSelection() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 177 | { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 178 | cancelPendingScopingEffort(); |
| 179 | |
| 180 | // Remove all markers for matches found and turn off the highlighting. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 181 | m_ownerFrame.frame()->document()->markers().removeMarkers(DocumentMarker::TextMatch); |
| 182 | m_ownerFrame.frame()->editor().setMarkedTextMatchesAreHighlighted(false); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 183 | clearFindMatchesCache(); |
| 184 | |
| 185 | // Let the frame know that we don't want tickmarks or highlighting anymore. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 186 | m_ownerFrame.invalidateAll(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 187 | } |
| 188 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 189 | void TextFinder::scopeStringMatches(int identifier, const WebString& searchText, const WebFindOptions& options, bool reset) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 190 | { |
| 191 | if (reset) { |
| 192 | // This is a brand new search, so we need to reset everything. |
| 193 | // Scoping is just about to begin. |
| 194 | m_scopingInProgress = true; |
| 195 | |
| 196 | // Need to keep the current identifier locally in order to finish the |
| 197 | // request in case the frame is detached during the process. |
| 198 | m_findRequestIdentifier = identifier; |
| 199 | |
| 200 | // Clear highlighting for this frame. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 201 | LocalFrame* frame = m_ownerFrame.frame(); |
| 202 | if (frame && frame->page() && frame->editor().markedTextMatchesAreHighlighted()) |
| 203 | frame->page()->unmarkAllTextMatches(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 204 | |
| 205 | // Clear the tickmarks and results cache. |
| 206 | clearFindMatchesCache(); |
| 207 | |
| 208 | // Clear the counters from last operation. |
| 209 | m_lastMatchCount = 0; |
| 210 | m_nextInvalidateAfter = 0; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 211 | m_resumeScopingFromRange = nullptr; |
| 212 | |
| 213 | // The view might be null on detached frames. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 214 | if (frame && frame->page()) |
| 215 | m_ownerFrame.viewImpl()->mainFrameImpl()->ensureTextFinder().m_framesScopingCount++; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 216 | |
| 217 | // Now, defer scoping until later to allow find operation to finish quickly. |
| 218 | scopeStringMatchesSoon(identifier, searchText, options, false); // false means just reset, so don't do it again. |
| 219 | return; |
| 220 | } |
| 221 | |
| 222 | if (!shouldScopeMatches(searchText)) { |
| 223 | // Note that we want to defer the final update when resetting even if shouldScopeMatches returns false. |
| 224 | // This is done in order to prevent sending a final message based only on the results of the first frame |
| 225 | // since m_framesScopingCount would be 0 as other frames have yet to reset. |
| 226 | finishCurrentScopingEffort(identifier); |
| 227 | return; |
| 228 | } |
| 229 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 230 | WebFrameImpl* mainFrameImpl = m_ownerFrame.viewImpl()->mainFrameImpl(); |
| 231 | RefPtr<Range> searchRange(rangeOfContents(m_ownerFrame.frame()->document())); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 232 | |
| 233 | Node* originalEndContainer = searchRange->endContainer(); |
| 234 | int originalEndOffset = searchRange->endOffset(); |
| 235 | |
| 236 | TrackExceptionState exceptionState, exceptionState2; |
| 237 | if (m_resumeScopingFromRange) { |
| 238 | // This is a continuation of a scoping operation that timed out and didn't |
| 239 | // complete last time around, so we should start from where we left off. |
| 240 | searchRange->setStart(m_resumeScopingFromRange->startContainer(), m_resumeScopingFromRange->startOffset(exceptionState2) + 1, exceptionState); |
| 241 | if (exceptionState.hadException() || exceptionState2.hadException()) { |
| 242 | if (exceptionState2.hadException()) // A non-zero |exceptionState| happens when navigating during search. |
| 243 | ASSERT_NOT_REACHED(); |
| 244 | return; |
| 245 | } |
| 246 | } |
| 247 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 248 | // This timeout controls how long we scope before releasing control. This |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 249 | // value does not prevent us from running for longer than this, but it is |
| 250 | // periodically checked to see if we have exceeded our allocated time. |
| 251 | const double maxScopingDuration = 0.1; // seconds |
| 252 | |
| 253 | int matchCount = 0; |
| 254 | bool timedOut = false; |
| 255 | double startTime = currentTime(); |
| 256 | do { |
| 257 | // Find next occurrence of the search string. |
| 258 | // FIXME: (http://b/1088245) This WebKit operation may run for longer |
| 259 | // than the timeout value, and is not interruptible as it is currently |
| 260 | // written. We may need to rewrite it with interruptibility in mind, or |
| 261 | // find an alternative. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 262 | RefPtr<Range> resultRange(findPlainText( |
| 263 | searchRange.get(), searchText, options.matchCase ? 0 : CaseInsensitive)); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 264 | if (resultRange->collapsed(exceptionState)) { |
| 265 | if (!resultRange->startContainer()->isInShadowTree()) |
| 266 | break; |
| 267 | |
| 268 | searchRange->setStartAfter( |
| 269 | resultRange->startContainer()->deprecatedShadowAncestorNode(), exceptionState); |
| 270 | searchRange->setEnd(originalEndContainer, originalEndOffset, exceptionState); |
| 271 | continue; |
| 272 | } |
| 273 | |
| 274 | ++matchCount; |
| 275 | |
| 276 | // Catch a special case where Find found something but doesn't know what |
| 277 | // the bounding box for it is. In this case we set the first match we find |
| 278 | // as the active rect. |
| 279 | IntRect resultBounds = resultRange->boundingBox(); |
| 280 | IntRect activeSelectionRect; |
| 281 | if (m_locatingActiveRect) { |
| 282 | activeSelectionRect = m_activeMatch.get() ? |
| 283 | m_activeMatch->boundingBox() : resultBounds; |
| 284 | } |
| 285 | |
| 286 | // If the Find function found a match it will have stored where the |
| 287 | // match was found in m_activeSelectionRect on the current frame. If we |
| 288 | // find this rect during scoping it means we have found the active |
| 289 | // tickmark. |
| 290 | bool foundActiveMatch = false; |
| 291 | if (m_locatingActiveRect && (activeSelectionRect == resultBounds)) { |
| 292 | // We have found the active tickmark frame. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 293 | mainFrameImpl->ensureTextFinder().m_currentActiveMatchFrame = &m_ownerFrame; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 294 | foundActiveMatch = true; |
| 295 | // We also know which tickmark is active now. |
| 296 | m_activeMatchIndexInCurrentFrame = matchCount - 1; |
| 297 | // To stop looking for the active tickmark, we set this flag. |
| 298 | m_locatingActiveRect = false; |
| 299 | |
| 300 | // Notify browser of new location for the selected rectangle. |
| 301 | reportFindInPageSelection( |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 302 | m_ownerFrame.frameView()->contentsToWindow(resultBounds), |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 303 | m_activeMatchIndexInCurrentFrame + 1, |
| 304 | identifier); |
| 305 | } |
| 306 | |
| 307 | addMarker(resultRange.get(), foundActiveMatch); |
| 308 | |
| 309 | m_findMatchesCache.append(FindMatch(resultRange.get(), m_lastMatchCount + matchCount)); |
| 310 | |
| 311 | // Set the new start for the search range to be the end of the previous |
| 312 | // result range. There is no need to use a VisiblePosition here, |
| 313 | // since findPlainText will use a TextIterator to go over the visible |
| 314 | // text nodes. |
| 315 | searchRange->setStart(resultRange->endContainer(exceptionState), resultRange->endOffset(exceptionState), exceptionState); |
| 316 | |
| 317 | Node* shadowTreeRoot = searchRange->shadowRoot(); |
| 318 | if (searchRange->collapsed(exceptionState) && shadowTreeRoot) |
| 319 | searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->countChildren(), exceptionState); |
| 320 | |
| 321 | m_resumeScopingFromRange = resultRange; |
| 322 | timedOut = (currentTime() - startTime) >= maxScopingDuration; |
| 323 | } while (!timedOut); |
| 324 | |
| 325 | // Remember what we search for last time, so we can skip searching if more |
| 326 | // letters are added to the search string (and last outcome was 0). |
| 327 | m_lastSearchString = searchText; |
| 328 | |
| 329 | if (matchCount > 0) { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 330 | m_ownerFrame.frame()->editor().setMarkedTextMatchesAreHighlighted(true); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 331 | |
| 332 | m_lastMatchCount += matchCount; |
| 333 | |
| 334 | // Let the mainframe know how much we found during this pass. |
| 335 | mainFrameImpl->increaseMatchCount(matchCount, identifier); |
| 336 | } |
| 337 | |
| 338 | if (timedOut) { |
| 339 | // If we found anything during this pass, we should redraw. However, we |
| 340 | // don't want to spam too much if the page is extremely long, so if we |
| 341 | // reach a certain point we start throttling the redraw requests. |
| 342 | if (matchCount > 0) |
| 343 | invalidateIfNecessary(); |
| 344 | |
| 345 | // Scoping effort ran out of time, lets ask for another time-slice. |
| 346 | scopeStringMatchesSoon( |
| 347 | identifier, |
| 348 | searchText, |
| 349 | options, |
| 350 | false); // don't reset. |
| 351 | return; // Done for now, resume work later. |
| 352 | } |
| 353 | |
| 354 | finishCurrentScopingEffort(identifier); |
| 355 | } |
| 356 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 357 | void TextFinder::flushCurrentScopingEffort(int identifier) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 358 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 359 | if (!m_ownerFrame.frame() || !m_ownerFrame.frame()->page()) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 360 | return; |
| 361 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 362 | WebFrameImpl* mainFrameImpl = m_ownerFrame.viewImpl()->mainFrameImpl(); |
| 363 | mainFrameImpl->ensureTextFinder().decrementFramesScopingCount(identifier); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 364 | } |
| 365 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 366 | void TextFinder::finishCurrentScopingEffort(int identifier) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 367 | { |
| 368 | flushCurrentScopingEffort(identifier); |
| 369 | |
| 370 | m_scopingInProgress = false; |
| 371 | m_lastFindRequestCompletedWithNoMatches = !m_lastMatchCount; |
| 372 | |
| 373 | // This frame is done, so show any scrollbar tickmarks we haven't drawn yet. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 374 | m_ownerFrame.invalidateScrollbar(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 375 | } |
| 376 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 377 | void TextFinder::cancelPendingScopingEffort() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 378 | { |
| 379 | deleteAllValues(m_deferredScopingWork); |
| 380 | m_deferredScopingWork.clear(); |
| 381 | |
| 382 | m_activeMatchIndexInCurrentFrame = -1; |
| 383 | |
| 384 | // Last request didn't complete. |
| 385 | if (m_scopingInProgress) |
| 386 | m_lastFindRequestCompletedWithNoMatches = false; |
| 387 | |
| 388 | m_scopingInProgress = false; |
| 389 | } |
| 390 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 391 | void TextFinder::increaseMatchCount(int identifier, int count) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 392 | { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 393 | if (count) |
| 394 | ++m_findMatchMarkersVersion; |
| 395 | |
| 396 | m_totalMatchCount += count; |
| 397 | |
| 398 | // Update the UI with the latest findings. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 399 | if (m_ownerFrame.client()) |
| 400 | m_ownerFrame.client()->reportFindInPageMatchCount(identifier, m_totalMatchCount, !m_framesScopingCount); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 401 | } |
| 402 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 403 | void TextFinder::reportFindInPageSelection(const WebRect& selectionRect, int activeMatchOrdinal, int identifier) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 404 | { |
| 405 | // Update the UI with the latest selection rect. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 406 | if (m_ownerFrame.client()) |
| 407 | m_ownerFrame.client()->reportFindInPageSelection(identifier, ordinalOfFirstMatch() + activeMatchOrdinal, selectionRect); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 408 | } |
| 409 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 410 | void TextFinder::resetMatchCount() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 411 | { |
| 412 | if (m_totalMatchCount > 0) |
| 413 | ++m_findMatchMarkersVersion; |
| 414 | |
| 415 | m_totalMatchCount = 0; |
| 416 | m_framesScopingCount = 0; |
| 417 | } |
| 418 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 419 | void TextFinder::clearFindMatchesCache() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 420 | { |
| 421 | if (!m_findMatchesCache.isEmpty()) |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 422 | m_ownerFrame.viewImpl()->mainFrameImpl()->ensureTextFinder().m_findMatchMarkersVersion++; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 423 | |
| 424 | m_findMatchesCache.clear(); |
| 425 | m_findMatchRectsAreValid = false; |
| 426 | } |
| 427 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 428 | bool TextFinder::isActiveMatchFrameValid() const |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 429 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 430 | WebFrameImpl* mainFrameImpl = m_ownerFrame.viewImpl()->mainFrameImpl(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 431 | WebFrameImpl* activeMatchFrame = mainFrameImpl->activeMatchFrame(); |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 432 | return activeMatchFrame && activeMatchFrame->activeMatch() && activeMatchFrame->frame()->tree().isDescendantOf(mainFrameImpl->frame()); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 433 | } |
| 434 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 435 | void TextFinder::updateFindMatchRects() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 436 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 437 | IntSize currentContentsSize = m_ownerFrame.contentsSize(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 438 | if (m_contentsSizeForCurrentFindMatchRects != currentContentsSize) { |
| 439 | m_contentsSizeForCurrentFindMatchRects = currentContentsSize; |
| 440 | m_findMatchRectsAreValid = false; |
| 441 | } |
| 442 | |
| 443 | size_t deadMatches = 0; |
| 444 | for (Vector<FindMatch>::iterator it = m_findMatchesCache.begin(); it != m_findMatchesCache.end(); ++it) { |
| 445 | if (!it->m_range->boundaryPointsValid() || !it->m_range->startContainer()->inDocument()) |
| 446 | it->m_rect = FloatRect(); |
| 447 | else if (!m_findMatchRectsAreValid) |
| 448 | it->m_rect = findInPageRectFromRange(it->m_range.get()); |
| 449 | |
| 450 | if (it->m_rect.isEmpty()) |
| 451 | ++deadMatches; |
| 452 | } |
| 453 | |
| 454 | // Remove any invalid matches from the cache. |
| 455 | if (deadMatches) { |
| 456 | Vector<FindMatch> filteredMatches; |
| 457 | filteredMatches.reserveCapacity(m_findMatchesCache.size() - deadMatches); |
| 458 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 459 | for (Vector<FindMatch>::const_iterator it = m_findMatchesCache.begin(); it != m_findMatchesCache.end(); ++it) { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 460 | if (!it->m_rect.isEmpty()) |
| 461 | filteredMatches.append(*it); |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 462 | } |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 463 | |
| 464 | m_findMatchesCache.swap(filteredMatches); |
| 465 | } |
| 466 | |
| 467 | // Invalidate the rects in child frames. Will be updated later during traversal. |
| 468 | if (!m_findMatchRectsAreValid) |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 469 | for (WebFrame* child = m_ownerFrame.firstChild(); child; child = child->nextSibling()) |
| 470 | toWebFrameImpl(child)->ensureTextFinder().m_findMatchRectsAreValid = false; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 471 | |
| 472 | m_findMatchRectsAreValid = true; |
| 473 | } |
| 474 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 475 | WebFloatRect TextFinder::activeFindMatchRect() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 476 | { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 477 | if (!isActiveMatchFrameValid()) |
| 478 | return WebFloatRect(); |
| 479 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 480 | return WebFloatRect(findInPageRectFromRange(m_currentActiveMatchFrame->activeMatch())); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 481 | } |
| 482 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 483 | void TextFinder::findMatchRects(WebVector<WebFloatRect>& outputRects) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 484 | { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 485 | Vector<WebFloatRect> matchRects; |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 486 | for (WebFrameImpl* frame = &m_ownerFrame; frame; frame = toWebFrameImpl(frame->traverseNext(false))) |
| 487 | frame->ensureTextFinder().appendFindMatchRects(matchRects); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 488 | |
| 489 | outputRects = matchRects; |
| 490 | } |
| 491 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 492 | void TextFinder::appendFindMatchRects(Vector<WebFloatRect>& frameRects) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 493 | { |
| 494 | updateFindMatchRects(); |
| 495 | frameRects.reserveCapacity(frameRects.size() + m_findMatchesCache.size()); |
| 496 | for (Vector<FindMatch>::const_iterator it = m_findMatchesCache.begin(); it != m_findMatchesCache.end(); ++it) { |
| 497 | ASSERT(!it->m_rect.isEmpty()); |
| 498 | frameRects.append(it->m_rect); |
| 499 | } |
| 500 | } |
| 501 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 502 | int TextFinder::selectNearestFindMatch(const WebFloatPoint& point, WebRect* selectionRect) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 503 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 504 | TextFinder* bestFinder = 0; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 505 | int indexInBestFrame = -1; |
| 506 | float distanceInBestFrame = FLT_MAX; |
| 507 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 508 | for (WebFrameImpl* frame = &m_ownerFrame; frame; frame = toWebFrameImpl(frame->traverseNext(false))) { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 509 | float distanceInFrame; |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 510 | TextFinder& finder = frame->ensureTextFinder(); |
| 511 | int indexInFrame = finder.nearestFindMatch(point, distanceInFrame); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 512 | if (distanceInFrame < distanceInBestFrame) { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 513 | bestFinder = &finder; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 514 | indexInBestFrame = indexInFrame; |
| 515 | distanceInBestFrame = distanceInFrame; |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | if (indexInBestFrame != -1) |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 520 | return bestFinder->selectFindMatch(static_cast<unsigned>(indexInBestFrame), selectionRect); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 521 | |
| 522 | return -1; |
| 523 | } |
| 524 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 525 | int TextFinder::nearestFindMatch(const FloatPoint& point, float& distanceSquared) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 526 | { |
| 527 | updateFindMatchRects(); |
| 528 | |
| 529 | int nearest = -1; |
| 530 | distanceSquared = FLT_MAX; |
| 531 | for (size_t i = 0; i < m_findMatchesCache.size(); ++i) { |
| 532 | ASSERT(!m_findMatchesCache[i].m_rect.isEmpty()); |
| 533 | FloatSize offset = point - m_findMatchesCache[i].m_rect.center(); |
| 534 | float width = offset.width(); |
| 535 | float height = offset.height(); |
| 536 | float currentDistanceSquared = width * width + height * height; |
| 537 | if (currentDistanceSquared < distanceSquared) { |
| 538 | nearest = i; |
| 539 | distanceSquared = currentDistanceSquared; |
| 540 | } |
| 541 | } |
| 542 | return nearest; |
| 543 | } |
| 544 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 545 | int TextFinder::selectFindMatch(unsigned index, WebRect* selectionRect) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 546 | { |
| 547 | ASSERT_WITH_SECURITY_IMPLICATION(index < m_findMatchesCache.size()); |
| 548 | |
| 549 | RefPtr<Range> range = m_findMatchesCache[index].m_range; |
| 550 | if (!range->boundaryPointsValid() || !range->startContainer()->inDocument()) |
| 551 | return -1; |
| 552 | |
| 553 | // Check if the match is already selected. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 554 | TextFinder& mainFrameTextFinder = m_ownerFrame.viewImpl()->mainFrameImpl()->ensureTextFinder(); |
| 555 | WebFrameImpl* activeMatchFrame = mainFrameTextFinder.m_currentActiveMatchFrame; |
| 556 | if (&m_ownerFrame != activeMatchFrame || !m_activeMatch || !areRangesEqual(m_activeMatch.get(), range.get())) { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 557 | if (isActiveMatchFrameValid()) |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 558 | activeMatchFrame->ensureTextFinder().setMatchMarkerActive(false); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 559 | |
| 560 | m_activeMatchIndexInCurrentFrame = m_findMatchesCache[index].m_ordinal - 1; |
| 561 | |
| 562 | // Set this frame as the active frame (the one with the active highlight). |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 563 | mainFrameTextFinder.m_currentActiveMatchFrame = &m_ownerFrame; |
| 564 | m_ownerFrame.viewImpl()->setFocusedFrame(&m_ownerFrame); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 565 | |
| 566 | m_activeMatch = range.release(); |
| 567 | setMarkerActive(m_activeMatch.get(), true); |
| 568 | |
| 569 | // Clear any user selection, to make sure Find Next continues on from the match we just activated. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 570 | m_ownerFrame.frame()->selection().clear(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 571 | |
| 572 | // Make sure no node is focused. See http://crbug.com/38700. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 573 | m_ownerFrame.frame()->document()->setFocusedElement(nullptr); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 574 | } |
| 575 | |
| 576 | IntRect activeMatchRect; |
| 577 | IntRect activeMatchBoundingBox = enclosingIntRect(RenderObject::absoluteBoundingBoxRectForRange(m_activeMatch.get())); |
| 578 | |
| 579 | if (!activeMatchBoundingBox.isEmpty()) { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 580 | if (m_activeMatch->firstNode() && m_activeMatch->firstNode()->renderer()) { |
| 581 | m_activeMatch->firstNode()->renderer()->scrollRectToVisible( |
| 582 | activeMatchBoundingBox, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded); |
| 583 | } |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 584 | |
| 585 | // Zoom to the active match. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 586 | activeMatchRect = m_ownerFrame.frameView()->contentsToWindow(activeMatchBoundingBox); |
| 587 | m_ownerFrame.viewImpl()->zoomToFindInPageRect(activeMatchRect); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 588 | } |
| 589 | |
| 590 | if (selectionRect) |
| 591 | *selectionRect = activeMatchRect; |
| 592 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 593 | return ordinalOfFirstMatch() + m_activeMatchIndexInCurrentFrame + 1; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 594 | } |
| 595 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 596 | PassOwnPtr<TextFinder> TextFinder::create(WebFrameImpl& ownerFrame) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 597 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 598 | return adoptPtr(new TextFinder(ownerFrame)); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 599 | } |
| 600 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 601 | TextFinder::TextFinder(WebFrameImpl& ownerFrame) |
| 602 | : m_ownerFrame(ownerFrame) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 603 | , m_currentActiveMatchFrame(0) |
| 604 | , m_activeMatchIndexInCurrentFrame(-1) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 605 | , m_resumeScopingFromRange(nullptr) |
| 606 | , m_lastMatchCount(-1) |
| 607 | , m_totalMatchCount(-1) |
| 608 | , m_framesScopingCount(-1) |
| 609 | , m_findRequestIdentifier(-1) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 610 | , m_nextInvalidateAfter(0) |
| 611 | , m_findMatchMarkersVersion(0) |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 612 | , m_locatingActiveRect(false) |
| 613 | , m_scopingInProgress(false) |
| 614 | , m_lastFindRequestCompletedWithNoMatches(false) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 615 | , m_findMatchRectsAreValid(false) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 616 | { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 617 | } |
| 618 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 619 | TextFinder::~TextFinder() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 620 | { |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 621 | cancelPendingScopingEffort(); |
| 622 | } |
| 623 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 624 | void TextFinder::addMarker(Range* range, bool activeMatch) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 625 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 626 | m_ownerFrame.frame()->document()->markers().addTextMatchMarker(range, activeMatch); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 627 | } |
| 628 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 629 | void TextFinder::setMarkerActive(Range* range, bool active) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 630 | { |
| 631 | if (!range || range->collapsed(IGNORE_EXCEPTION)) |
| 632 | return; |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 633 | m_ownerFrame.frame()->document()->markers().setMarkersActive(range, active); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 634 | } |
| 635 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 636 | int TextFinder::ordinalOfFirstMatchForFrame(WebFrameImpl* frame) const |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 637 | { |
| 638 | int ordinal = 0; |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 639 | WebFrameImpl* mainFrameImpl = m_ownerFrame.viewImpl()->mainFrameImpl(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 640 | // Iterate from the main frame up to (but not including) |frame| and |
| 641 | // add up the number of matches found so far. |
| 642 | for (WebFrameImpl* it = mainFrameImpl; it != frame; it = toWebFrameImpl(it->traverseNext(true))) { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 643 | TextFinder& finder = it->ensureTextFinder(); |
| 644 | if (finder.m_lastMatchCount > 0) |
| 645 | ordinal += finder.m_lastMatchCount; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 646 | } |
| 647 | return ordinal; |
| 648 | } |
| 649 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 650 | bool TextFinder::shouldScopeMatches(const String& searchText) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 651 | { |
| 652 | // Don't scope if we can't find a frame or a view. |
| 653 | // The user may have closed the tab/application, so abort. |
| 654 | // Also ignore detached frames, as many find operations report to the main frame. |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 655 | LocalFrame* frame = m_ownerFrame.frame(); |
| 656 | if (!frame || !frame->view() || !frame->page() || !m_ownerFrame.hasVisibleContent()) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 657 | return false; |
| 658 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 659 | ASSERT(frame->document() && frame->view()); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 660 | |
| 661 | // If the frame completed the scoping operation and found 0 matches the last |
| 662 | // time it was searched, then we don't have to search it again if the user is |
| 663 | // just adding to the search string or sending the same search string again. |
| 664 | if (m_lastFindRequestCompletedWithNoMatches && !m_lastSearchString.isEmpty()) { |
| 665 | // Check to see if the search string prefixes match. |
| 666 | String previousSearchPrefix = |
| 667 | searchText.substring(0, m_lastSearchString.length()); |
| 668 | |
| 669 | if (previousSearchPrefix == m_lastSearchString) |
| 670 | return false; // Don't search this frame, it will be fruitless. |
| 671 | } |
| 672 | |
| 673 | return true; |
| 674 | } |
| 675 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 676 | void TextFinder::scopeStringMatchesSoon(int identifier, const WebString& searchText, const WebFindOptions& options, bool reset) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 677 | { |
| 678 | m_deferredScopingWork.append(new DeferredScopeStringMatches(this, identifier, searchText, options, reset)); |
| 679 | } |
| 680 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 681 | void TextFinder::callScopeStringMatches(DeferredScopeStringMatches* caller, int identifier, const WebString& searchText, const WebFindOptions& options, bool reset) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 682 | { |
| 683 | m_deferredScopingWork.remove(m_deferredScopingWork.find(caller)); |
| 684 | scopeStringMatches(identifier, searchText, options, reset); |
| 685 | |
| 686 | // This needs to happen last since searchText is passed by reference. |
| 687 | delete caller; |
| 688 | } |
| 689 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 690 | void TextFinder::invalidateIfNecessary() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 691 | { |
| 692 | if (m_lastMatchCount <= m_nextInvalidateAfter) |
| 693 | return; |
| 694 | |
| 695 | // FIXME: (http://b/1088165) Optimize the drawing of the tickmarks and |
| 696 | // remove this. This calculation sets a milestone for when next to |
| 697 | // invalidate the scrollbar and the content area. We do this so that we |
| 698 | // don't spend too much time drawing the scrollbar over and over again. |
| 699 | // Basically, up until the first 500 matches there is no throttle. |
| 700 | // After the first 500 matches, we set set the milestone further and |
| 701 | // further out (750, 1125, 1688, 2K, 3K). |
| 702 | static const int startSlowingDownAfter = 500; |
| 703 | static const int slowdown = 750; |
| 704 | |
| 705 | int i = m_lastMatchCount / startSlowingDownAfter; |
| 706 | m_nextInvalidateAfter += i * slowdown; |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 707 | m_ownerFrame.invalidateScrollbar(); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 708 | } |
| 709 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 710 | void TextFinder::flushCurrentScoping() |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 711 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 712 | flushCurrentScopingEffort(m_findRequestIdentifier); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 713 | } |
| 714 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 715 | void TextFinder::setMatchMarkerActive(bool active) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 716 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 717 | setMarkerActive(m_activeMatch.get(), active); |
| 718 | } |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 719 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 720 | void TextFinder::decrementFramesScopingCount(int identifier) |
| 721 | { |
| 722 | // This frame has no further scoping left, so it is done. Other frames might, |
| 723 | // of course, continue to scope matches. |
| 724 | --m_framesScopingCount; |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 725 | |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 726 | // If this is the last frame to finish scoping we need to trigger the final |
| 727 | // update to be sent. |
| 728 | if (!m_framesScopingCount) |
| 729 | m_ownerFrame.increaseMatchCount(0, identifier); |
| 730 | } |
| 731 | |
| 732 | int TextFinder::ordinalOfFirstMatch() const |
| 733 | { |
| 734 | return ordinalOfFirstMatchForFrame(&m_ownerFrame); |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 735 | } |
| 736 | |
| 737 | } // namespace blink |