/src/mozilla-central/toolkit/components/find/nsWebBrowserFind.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsWebBrowserFind.h" |
8 | | |
9 | | // Only need this for NS_FIND_CONTRACTID, |
10 | | // else we could use nsRange.h and nsIFind.h. |
11 | | #include "nsFind.h" |
12 | | |
13 | | #include "nsIComponentManager.h" |
14 | | #include "nsIScriptSecurityManager.h" |
15 | | #include "nsIInterfaceRequestor.h" |
16 | | #include "nsIInterfaceRequestorUtils.h" |
17 | | #include "nsPIDOMWindow.h" |
18 | | #include "nsIURI.h" |
19 | | #include "nsIDocShell.h" |
20 | | #include "nsIPresShell.h" |
21 | | #include "nsPresContext.h" |
22 | | #include "nsIDocument.h" |
23 | | #include "nsISelectionController.h" |
24 | | #include "nsIFrame.h" |
25 | | #include "nsITextControlFrame.h" |
26 | | #include "nsReadableUtils.h" |
27 | | #include "nsIContent.h" |
28 | | #include "nsContentCID.h" |
29 | | #include "nsIServiceManager.h" |
30 | | #include "nsIObserverService.h" |
31 | | #include "nsISupportsPrimitives.h" |
32 | | #include "nsFind.h" |
33 | | #include "nsError.h" |
34 | | #include "nsFocusManager.h" |
35 | | #include "nsRange.h" |
36 | | #include "mozilla/Services.h" |
37 | | #include "mozilla/dom/Element.h" |
38 | | #include "mozilla/dom/Selection.h" |
39 | | #include "nsISimpleEnumerator.h" |
40 | | #include "nsContentUtils.h" |
41 | | #include "nsGenericHTMLElement.h" |
42 | | |
43 | | #if DEBUG |
44 | | #include "nsIWebNavigation.h" |
45 | | #include "nsString.h" |
46 | | #endif |
47 | | |
48 | | using mozilla::dom::Selection; |
49 | | using mozilla::dom::Element; |
50 | | |
51 | | nsWebBrowserFind::nsWebBrowserFind() |
52 | | : mFindBackwards(false) |
53 | | , mWrapFind(false) |
54 | | , mEntireWord(false) |
55 | | , mMatchCase(false) |
56 | | , mSearchSubFrames(true) |
57 | | , mSearchParentFrames(true) |
58 | 0 | { |
59 | 0 | } |
60 | | |
61 | | nsWebBrowserFind::~nsWebBrowserFind() |
62 | 0 | { |
63 | 0 | } |
64 | | |
65 | | NS_IMPL_ISUPPORTS(nsWebBrowserFind, nsIWebBrowserFind, |
66 | | nsIWebBrowserFindInFrames) |
67 | | |
68 | | NS_IMETHODIMP |
69 | | nsWebBrowserFind::FindNext(bool* aResult) |
70 | 0 | { |
71 | 0 | NS_ENSURE_ARG_POINTER(aResult); |
72 | 0 | *aResult = false; |
73 | 0 |
|
74 | 0 | NS_ENSURE_TRUE(CanFindNext(), NS_ERROR_NOT_INITIALIZED); |
75 | 0 |
|
76 | 0 | nsresult rv = NS_OK; |
77 | 0 | nsCOMPtr<nsPIDOMWindowOuter> searchFrame = do_QueryReferent(mCurrentSearchFrame); |
78 | 0 | NS_ENSURE_TRUE(searchFrame, NS_ERROR_NOT_INITIALIZED); |
79 | 0 |
|
80 | 0 | nsCOMPtr<nsPIDOMWindowOuter> rootFrame = do_QueryReferent(mRootSearchFrame); |
81 | 0 | NS_ENSURE_TRUE(rootFrame, NS_ERROR_NOT_INITIALIZED); |
82 | 0 |
|
83 | 0 | // first, if there's a "cmd_findagain" observer around, check to see if it |
84 | 0 | // wants to perform the find again command . If it performs the find again |
85 | 0 | // it will return true, in which case we exit ::FindNext() early. |
86 | 0 | // Otherwise, nsWebBrowserFind needs to perform the find again command itself |
87 | 0 | // this is used by nsTypeAheadFind, which controls find again when it was |
88 | 0 | // the last executed find in the current window. |
89 | 0 | nsCOMPtr<nsIObserverService> observerSvc = |
90 | 0 | mozilla::services::GetObserverService(); |
91 | 0 | if (observerSvc) { |
92 | 0 | nsCOMPtr<nsISupportsInterfacePointer> windowSupportsData = |
93 | 0 | do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv); |
94 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
95 | 0 | nsCOMPtr<nsISupports> searchWindowSupports = do_QueryInterface(rootFrame); |
96 | 0 | windowSupportsData->SetData(searchWindowSupports); |
97 | 0 | observerSvc->NotifyObservers(windowSupportsData, |
98 | 0 | "nsWebBrowserFind_FindAgain", |
99 | 0 | mFindBackwards ? u"up" : u"down"); |
100 | 0 | windowSupportsData->GetData(getter_AddRefs(searchWindowSupports)); |
101 | 0 | // findnext performed if search window data cleared out |
102 | 0 | *aResult = searchWindowSupports == nullptr; |
103 | 0 | if (*aResult) { |
104 | 0 | return NS_OK; |
105 | 0 | } |
106 | 0 | } |
107 | 0 | |
108 | 0 | // next, look in the current frame. If found, return. |
109 | 0 | |
110 | 0 | // Beware! This may flush notifications via synchronous |
111 | 0 | // ScrollSelectionIntoView. |
112 | 0 | rv = SearchInFrame(searchFrame, false, aResult); |
113 | 0 | if (NS_FAILED(rv)) { |
114 | 0 | return rv; |
115 | 0 | } |
116 | 0 | if (*aResult) { |
117 | 0 | return OnFind(searchFrame); // we are done |
118 | 0 | } |
119 | 0 | |
120 | 0 | // if we are not searching other frames, return |
121 | 0 | if (!mSearchSubFrames && !mSearchParentFrames) { |
122 | 0 | return NS_OK; |
123 | 0 | } |
124 | 0 | |
125 | 0 | nsIDocShell* rootDocShell = rootFrame->GetDocShell(); |
126 | 0 | if (!rootDocShell) { |
127 | 0 | return NS_ERROR_FAILURE; |
128 | 0 | } |
129 | 0 | |
130 | 0 | int32_t enumDirection = mFindBackwards ? nsIDocShell::ENUMERATE_BACKWARDS : |
131 | 0 | nsIDocShell::ENUMERATE_FORWARDS; |
132 | 0 |
|
133 | 0 | nsCOMPtr<nsISimpleEnumerator> docShellEnumerator; |
134 | 0 | rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll, |
135 | 0 | enumDirection, |
136 | 0 | getter_AddRefs(docShellEnumerator)); |
137 | 0 | if (NS_FAILED(rv)) { |
138 | 0 | return rv; |
139 | 0 | } |
140 | 0 | |
141 | 0 | // remember where we started |
142 | 0 | nsCOMPtr<nsIDocShellTreeItem> startingItem = |
143 | 0 | do_QueryInterface(searchFrame->GetDocShell(), &rv); |
144 | 0 | if (NS_FAILED(rv)) { |
145 | 0 | return rv; |
146 | 0 | } |
147 | 0 | |
148 | 0 | nsCOMPtr<nsIDocShellTreeItem> curItem; |
149 | 0 |
|
150 | 0 | // XXX We should avoid searching in frameset documents here. |
151 | 0 | // We also need to honour mSearchSubFrames and mSearchParentFrames. |
152 | 0 | bool hasMore, doFind = false; |
153 | 0 | while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && |
154 | 0 | hasMore) { |
155 | 0 | nsCOMPtr<nsISupports> curSupports; |
156 | 0 | rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports)); |
157 | 0 | if (NS_FAILED(rv)) { |
158 | 0 | break; |
159 | 0 | } |
160 | 0 | curItem = do_QueryInterface(curSupports, &rv); |
161 | 0 | if (NS_FAILED(rv)) { |
162 | 0 | break; |
163 | 0 | } |
164 | 0 | |
165 | 0 | if (doFind) { |
166 | 0 | searchFrame = curItem->GetWindow(); |
167 | 0 | if (!searchFrame) { |
168 | 0 | break; |
169 | 0 | } |
170 | 0 | |
171 | 0 | OnStartSearchFrame(searchFrame); |
172 | 0 |
|
173 | 0 | // Beware! This may flush notifications via synchronous |
174 | 0 | // ScrollSelectionIntoView. |
175 | 0 | rv = SearchInFrame(searchFrame, false, aResult); |
176 | 0 | if (NS_FAILED(rv)) { |
177 | 0 | return rv; |
178 | 0 | } |
179 | 0 | if (*aResult) { |
180 | 0 | return OnFind(searchFrame); // we are done |
181 | 0 | } |
182 | 0 | |
183 | 0 | OnEndSearchFrame(searchFrame); |
184 | 0 | } |
185 | 0 |
|
186 | 0 | if (curItem.get() == startingItem.get()) { |
187 | 0 | doFind = true; // start looking in frames after this one |
188 | 0 | } |
189 | 0 | } |
190 | 0 |
|
191 | 0 | if (!mWrapFind) { |
192 | 0 | // remember where we left off |
193 | 0 | SetCurrentSearchFrame(searchFrame); |
194 | 0 | return NS_OK; |
195 | 0 | } |
196 | 0 | |
197 | 0 | // From here on, we're wrapping, first through the other frames, then finally |
198 | 0 | // from the beginning of the starting frame back to the starting point. |
199 | 0 | |
200 | 0 | // because nsISimpleEnumerator is totally lame and isn't resettable, I have to |
201 | 0 | // make a new one |
202 | 0 | docShellEnumerator = nullptr; |
203 | 0 | rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll, |
204 | 0 | enumDirection, |
205 | 0 | getter_AddRefs(docShellEnumerator)); |
206 | 0 | if (NS_FAILED(rv)) { |
207 | 0 | return rv; |
208 | 0 | } |
209 | 0 | |
210 | 0 | while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && |
211 | 0 | hasMore) { |
212 | 0 | nsCOMPtr<nsISupports> curSupports; |
213 | 0 | rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports)); |
214 | 0 | if (NS_FAILED(rv)) { |
215 | 0 | break; |
216 | 0 | } |
217 | 0 | curItem = do_QueryInterface(curSupports, &rv); |
218 | 0 | if (NS_FAILED(rv)) { |
219 | 0 | break; |
220 | 0 | } |
221 | 0 | |
222 | 0 | searchFrame = curItem->GetWindow(); |
223 | 0 | if (!searchFrame) { |
224 | 0 | rv = NS_ERROR_FAILURE; |
225 | 0 | break; |
226 | 0 | } |
227 | 0 | |
228 | 0 | if (curItem.get() == startingItem.get()) { |
229 | 0 | // Beware! This may flush notifications via synchronous |
230 | 0 | // ScrollSelectionIntoView. |
231 | 0 | rv = SearchInFrame(searchFrame, true, aResult); |
232 | 0 | if (NS_FAILED(rv)) { |
233 | 0 | return rv; |
234 | 0 | } |
235 | 0 | if (*aResult) { |
236 | 0 | return OnFind(searchFrame); // we are done |
237 | 0 | } |
238 | 0 | break; |
239 | 0 | } |
240 | 0 | |
241 | 0 | OnStartSearchFrame(searchFrame); |
242 | 0 |
|
243 | 0 | // Beware! This may flush notifications via synchronous |
244 | 0 | // ScrollSelectionIntoView. |
245 | 0 | rv = SearchInFrame(searchFrame, false, aResult); |
246 | 0 | if (NS_FAILED(rv)) { |
247 | 0 | return rv; |
248 | 0 | } |
249 | 0 | if (*aResult) { |
250 | 0 | return OnFind(searchFrame); // we are done |
251 | 0 | } |
252 | 0 | |
253 | 0 | OnEndSearchFrame(searchFrame); |
254 | 0 | } |
255 | 0 |
|
256 | 0 | // remember where we left off |
257 | 0 | SetCurrentSearchFrame(searchFrame); |
258 | 0 |
|
259 | 0 | NS_ASSERTION(NS_SUCCEEDED(rv), "Something failed"); |
260 | 0 | return rv; |
261 | 0 | } |
262 | | |
263 | | NS_IMETHODIMP |
264 | | nsWebBrowserFind::GetSearchString(nsAString& aSearchString) |
265 | 0 | { |
266 | 0 | aSearchString = mSearchString; |
267 | 0 | return NS_OK; |
268 | 0 | } |
269 | | |
270 | | NS_IMETHODIMP |
271 | | nsWebBrowserFind::SetSearchString(const nsAString& aSearchString) |
272 | 0 | { |
273 | 0 | mSearchString = aSearchString; |
274 | 0 | return NS_OK; |
275 | 0 | } |
276 | | |
277 | | NS_IMETHODIMP |
278 | | nsWebBrowserFind::GetFindBackwards(bool* aFindBackwards) |
279 | 0 | { |
280 | 0 | NS_ENSURE_ARG_POINTER(aFindBackwards); |
281 | 0 | *aFindBackwards = mFindBackwards; |
282 | 0 | return NS_OK; |
283 | 0 | } |
284 | | |
285 | | NS_IMETHODIMP |
286 | | nsWebBrowserFind::SetFindBackwards(bool aFindBackwards) |
287 | 0 | { |
288 | 0 | mFindBackwards = aFindBackwards; |
289 | 0 | return NS_OK; |
290 | 0 | } |
291 | | |
292 | | NS_IMETHODIMP |
293 | | nsWebBrowserFind::GetWrapFind(bool* aWrapFind) |
294 | 0 | { |
295 | 0 | NS_ENSURE_ARG_POINTER(aWrapFind); |
296 | 0 | *aWrapFind = mWrapFind; |
297 | 0 | return NS_OK; |
298 | 0 | } |
299 | | |
300 | | NS_IMETHODIMP |
301 | | nsWebBrowserFind::SetWrapFind(bool aWrapFind) |
302 | 0 | { |
303 | 0 | mWrapFind = aWrapFind; |
304 | 0 | return NS_OK; |
305 | 0 | } |
306 | | |
307 | | NS_IMETHODIMP |
308 | | nsWebBrowserFind::GetEntireWord(bool* aEntireWord) |
309 | 0 | { |
310 | 0 | NS_ENSURE_ARG_POINTER(aEntireWord); |
311 | 0 | *aEntireWord = mEntireWord; |
312 | 0 | return NS_OK; |
313 | 0 | } |
314 | | |
315 | | NS_IMETHODIMP |
316 | | nsWebBrowserFind::SetEntireWord(bool aEntireWord) |
317 | 0 | { |
318 | 0 | mEntireWord = aEntireWord; |
319 | 0 | return NS_OK; |
320 | 0 | } |
321 | | |
322 | | NS_IMETHODIMP |
323 | | nsWebBrowserFind::GetMatchCase(bool* aMatchCase) |
324 | 0 | { |
325 | 0 | NS_ENSURE_ARG_POINTER(aMatchCase); |
326 | 0 | *aMatchCase = mMatchCase; |
327 | 0 | return NS_OK; |
328 | 0 | } |
329 | | |
330 | | NS_IMETHODIMP |
331 | | nsWebBrowserFind::SetMatchCase(bool aMatchCase) |
332 | 0 | { |
333 | 0 | mMatchCase = aMatchCase; |
334 | 0 | return NS_OK; |
335 | 0 | } |
336 | | |
337 | | static bool |
338 | | IsInNativeAnonymousSubtree(nsIContent* aContent) |
339 | 0 | { |
340 | 0 | while (aContent) { |
341 | 0 | nsIContent* bindingParent = aContent->GetBindingParent(); |
342 | 0 | if (bindingParent == aContent) { |
343 | 0 | return true; |
344 | 0 | } |
345 | 0 | |
346 | 0 | aContent = bindingParent; |
347 | 0 | } |
348 | 0 |
|
349 | 0 | return false; |
350 | 0 | } |
351 | | |
352 | | void |
353 | | nsWebBrowserFind::SetSelectionAndScroll(nsPIDOMWindowOuter* aWindow, |
354 | | nsRange* aRange) |
355 | 0 | { |
356 | 0 | nsCOMPtr<nsIDocument> doc = aWindow->GetDoc(); |
357 | 0 | if (!doc) { |
358 | 0 | return; |
359 | 0 | } |
360 | 0 | |
361 | 0 | nsIPresShell* presShell = doc->GetShell(); |
362 | 0 | if (!presShell) { |
363 | 0 | return; |
364 | 0 | } |
365 | 0 | |
366 | 0 | nsCOMPtr<nsINode> node = aRange->GetStartContainer(); |
367 | 0 | nsCOMPtr<nsIContent> content(do_QueryInterface(node)); |
368 | 0 | nsIFrame* frame = content->GetPrimaryFrame(); |
369 | 0 | if (!frame) { |
370 | 0 | return; |
371 | 0 | } |
372 | 0 | nsCOMPtr<nsISelectionController> selCon; |
373 | 0 | frame->GetSelectionController(presShell->GetPresContext(), |
374 | 0 | getter_AddRefs(selCon)); |
375 | 0 |
|
376 | 0 | // since the match could be an anonymous textnode inside a |
377 | 0 | // <textarea> or text <input>, we need to get the outer frame |
378 | 0 | nsITextControlFrame* tcFrame = nullptr; |
379 | 0 | for (; content; content = content->GetParent()) { |
380 | 0 | if (!IsInNativeAnonymousSubtree(content)) { |
381 | 0 | nsIFrame* f = content->GetPrimaryFrame(); |
382 | 0 | if (!f) { |
383 | 0 | return; |
384 | 0 | } |
385 | 0 | tcFrame = do_QueryFrame(f); |
386 | 0 | break; |
387 | 0 | } |
388 | 0 | } |
389 | 0 |
|
390 | 0 | selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); |
391 | 0 | RefPtr<Selection> selection = |
392 | 0 | selCon->GetSelection(nsISelectionController::SELECTION_NORMAL); |
393 | 0 | if (selection) { |
394 | 0 | selection->RemoveAllRanges(IgnoreErrors()); |
395 | 0 | selection->AddRange(*aRange, IgnoreErrors()); |
396 | 0 |
|
397 | 0 | nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); |
398 | 0 | if (fm) { |
399 | 0 | if (tcFrame) { |
400 | 0 | RefPtr<Element> newFocusedElement = Element::FromNode(content); |
401 | 0 | fm->SetFocus(newFocusedElement, nsIFocusManager::FLAG_NOSCROLL); |
402 | 0 | } else { |
403 | 0 | RefPtr<Element> result; |
404 | 0 | fm->MoveFocus(aWindow, nullptr, nsIFocusManager::MOVEFOCUS_CARET, |
405 | 0 | nsIFocusManager::FLAG_NOSCROLL, getter_AddRefs(result)); |
406 | 0 | } |
407 | 0 | } |
408 | 0 |
|
409 | 0 | // Scroll if necessary to make the selection visible: |
410 | 0 | // Must be the last thing to do - bug 242056 |
411 | 0 |
|
412 | 0 | // After ScrollSelectionIntoView(), the pending notifications might be |
413 | 0 | // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. |
414 | 0 | selCon->ScrollSelectionIntoView( |
415 | 0 | nsISelectionController::SELECTION_NORMAL, |
416 | 0 | nsISelectionController::SELECTION_WHOLE_SELECTION, |
417 | 0 | nsISelectionController::SCROLL_CENTER_VERTICALLY | |
418 | 0 | nsISelectionController::SCROLL_SYNCHRONOUS); |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | | // Adapted from TextServicesDocument::GetDocumentContentRootNode |
423 | | nsresult |
424 | | nsWebBrowserFind::GetRootNode(nsIDocument* aDoc, Element** aNode) |
425 | 0 | { |
426 | 0 | NS_ENSURE_ARG_POINTER(aDoc); |
427 | 0 | NS_ENSURE_ARG_POINTER(aNode); |
428 | 0 | *aNode = 0; |
429 | 0 |
|
430 | 0 | if (aDoc->IsHTMLOrXHTML()) { |
431 | 0 | Element* body = aDoc->GetBody(); |
432 | 0 | NS_ENSURE_ARG_POINTER(body); |
433 | 0 | NS_ADDREF(*aNode = body); |
434 | 0 | return NS_OK; |
435 | 0 | } |
436 | 0 | |
437 | 0 | // For non-HTML documents, the content root node will be the doc element. |
438 | 0 | Element* root = aDoc->GetDocumentElement(); |
439 | 0 | NS_ENSURE_ARG_POINTER(root); |
440 | 0 | NS_ADDREF(*aNode = root); |
441 | 0 | return NS_OK; |
442 | 0 | } |
443 | | |
444 | | nsresult |
445 | | nsWebBrowserFind::SetRangeAroundDocument(nsRange* aSearchRange, |
446 | | nsRange* aStartPt, |
447 | | nsRange* aEndPt, |
448 | | nsIDocument* aDoc) |
449 | 0 | { |
450 | 0 | RefPtr<Element> bodyContent; |
451 | 0 | nsresult rv = GetRootNode(aDoc, getter_AddRefs(bodyContent)); |
452 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
453 | 0 | NS_ENSURE_ARG_POINTER(bodyContent); |
454 | 0 |
|
455 | 0 | uint32_t childCount = bodyContent->GetChildCount(); |
456 | 0 |
|
457 | 0 | aSearchRange->SetStart(*bodyContent, 0, IgnoreErrors()); |
458 | 0 | aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
459 | 0 |
|
460 | 0 | if (mFindBackwards) { |
461 | 0 | aStartPt->SetStart(*bodyContent, childCount, IgnoreErrors()); |
462 | 0 | aStartPt->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
463 | 0 | aEndPt->SetStart(*bodyContent, 0, IgnoreErrors()); |
464 | 0 | aEndPt->SetEnd(*bodyContent, 0, IgnoreErrors()); |
465 | 0 | } else { |
466 | 0 | aStartPt->SetStart(*bodyContent, 0, IgnoreErrors()); |
467 | 0 | aStartPt->SetEnd(*bodyContent, 0, IgnoreErrors()); |
468 | 0 | aEndPt->SetStart(*bodyContent, childCount, IgnoreErrors()); |
469 | 0 | aEndPt->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
470 | 0 | } |
471 | 0 |
|
472 | 0 | return NS_OK; |
473 | 0 | } |
474 | | |
475 | | // Set the range to go from the end of the current selection to the end of the |
476 | | // document (forward), or beginning to beginning (reverse). or around the whole |
477 | | // document if there's no selection. |
478 | | nsresult |
479 | | nsWebBrowserFind::GetSearchLimits(nsRange* aSearchRange, |
480 | | nsRange* aStartPt, nsRange* aEndPt, |
481 | | nsIDocument* aDoc, Selection* aSel, |
482 | | bool aWrap) |
483 | 0 | { |
484 | 0 | NS_ENSURE_ARG_POINTER(aSel); |
485 | 0 |
|
486 | 0 | // There is a selection. |
487 | 0 | uint32_t count = aSel->RangeCount(); |
488 | 0 | if (count < 1) { |
489 | 0 | return SetRangeAroundDocument(aSearchRange, aStartPt, aEndPt, aDoc); |
490 | 0 | } |
491 | 0 | |
492 | 0 | // Need bodyContent, for the start/end of the document |
493 | 0 | RefPtr<Element> bodyContent; |
494 | 0 | nsresult rv = GetRootNode(aDoc, getter_AddRefs(bodyContent)); |
495 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
496 | 0 | NS_ENSURE_ARG_POINTER(bodyContent); |
497 | 0 |
|
498 | 0 | uint32_t childCount = bodyContent->GetChildCount(); |
499 | 0 |
|
500 | 0 | // There are four possible range endpoints we might use: |
501 | 0 | // DocumentStart, SelectionStart, SelectionEnd, DocumentEnd. |
502 | 0 |
|
503 | 0 | RefPtr<nsRange> range; |
504 | 0 | nsCOMPtr<nsINode> node; |
505 | 0 | uint32_t offset; |
506 | 0 |
|
507 | 0 | // Forward, not wrapping: SelEnd to DocEnd |
508 | 0 | if (!mFindBackwards && !aWrap) { |
509 | 0 | // This isn't quite right, since the selection's ranges aren't |
510 | 0 | // necessarily in order; but they usually will be. |
511 | 0 | range = aSel->GetRangeAt(count - 1); |
512 | 0 | if (!range) { |
513 | 0 | return NS_ERROR_UNEXPECTED; |
514 | 0 | } |
515 | 0 | node = range->GetEndContainer(); |
516 | 0 | if (!node) { |
517 | 0 | return NS_ERROR_UNEXPECTED; |
518 | 0 | } |
519 | 0 | offset = range->EndOffset(); |
520 | 0 |
|
521 | 0 | aSearchRange->SetStart(*node, offset, IgnoreErrors()); |
522 | 0 | aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
523 | 0 | aStartPt->SetStart(*node, offset, IgnoreErrors()); |
524 | 0 | aStartPt->SetEnd(*node, offset, IgnoreErrors()); |
525 | 0 | aEndPt->SetStart(*bodyContent, childCount, IgnoreErrors()); |
526 | 0 | aEndPt->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
527 | 0 | } |
528 | 0 | // Backward, not wrapping: DocStart to SelStart |
529 | 0 | else if (mFindBackwards && !aWrap) { |
530 | 0 | range = aSel->GetRangeAt(0); |
531 | 0 | if (!range) { |
532 | 0 | return NS_ERROR_UNEXPECTED; |
533 | 0 | } |
534 | 0 | node = range->GetStartContainer(); |
535 | 0 | if (!node) { |
536 | 0 | return NS_ERROR_UNEXPECTED; |
537 | 0 | } |
538 | 0 | offset = range->StartOffset(); |
539 | 0 |
|
540 | 0 | aSearchRange->SetStart(*bodyContent, 0, IgnoreErrors()); |
541 | 0 | aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
542 | 0 | aStartPt->SetStart(*node, offset, IgnoreErrors()); |
543 | 0 | aStartPt->SetEnd(*node, offset, IgnoreErrors()); |
544 | 0 | aEndPt->SetStart(*bodyContent, 0, IgnoreErrors()); |
545 | 0 | aEndPt->SetEnd(*bodyContent, 0, IgnoreErrors()); |
546 | 0 | } |
547 | 0 | // Forward, wrapping: DocStart to SelEnd |
548 | 0 | else if (!mFindBackwards && aWrap) { |
549 | 0 | range = aSel->GetRangeAt(count - 1); |
550 | 0 | if (!range) { |
551 | 0 | return NS_ERROR_UNEXPECTED; |
552 | 0 | } |
553 | 0 | node = range->GetEndContainer(); |
554 | 0 | if (!node) { |
555 | 0 | return NS_ERROR_UNEXPECTED; |
556 | 0 | } |
557 | 0 | offset = range->EndOffset(); |
558 | 0 |
|
559 | 0 | aSearchRange->SetStart(*bodyContent, 0, IgnoreErrors()); |
560 | 0 | aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
561 | 0 | aStartPt->SetStart(*bodyContent, 0, IgnoreErrors()); |
562 | 0 | aStartPt->SetEnd(*bodyContent, 0, IgnoreErrors()); |
563 | 0 | aEndPt->SetStart(*node, offset, IgnoreErrors()); |
564 | 0 | aEndPt->SetEnd(*node, offset, IgnoreErrors()); |
565 | 0 | } |
566 | 0 | // Backward, wrapping: SelStart to DocEnd |
567 | 0 | else if (mFindBackwards && aWrap) { |
568 | 0 | range = aSel->GetRangeAt(0); |
569 | 0 | if (!range) { |
570 | 0 | return NS_ERROR_UNEXPECTED; |
571 | 0 | } |
572 | 0 | node = range->GetStartContainer(); |
573 | 0 | if (!node) { |
574 | 0 | return NS_ERROR_UNEXPECTED; |
575 | 0 | } |
576 | 0 | offset = range->StartOffset(); |
577 | 0 |
|
578 | 0 | aSearchRange->SetStart(*bodyContent, 0, IgnoreErrors()); |
579 | 0 | aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
580 | 0 | aStartPt->SetStart(*bodyContent, childCount, IgnoreErrors()); |
581 | 0 | aStartPt->SetEnd(*bodyContent, childCount, IgnoreErrors()); |
582 | 0 | aEndPt->SetStart(*node, offset, IgnoreErrors()); |
583 | 0 | aEndPt->SetEnd(*node, offset, IgnoreErrors()); |
584 | 0 | } |
585 | 0 | return NS_OK; |
586 | 0 | } |
587 | | |
588 | | NS_IMETHODIMP |
589 | | nsWebBrowserFind::GetSearchFrames(bool* aSearchFrames) |
590 | 0 | { |
591 | 0 | NS_ENSURE_ARG_POINTER(aSearchFrames); |
592 | 0 | // this only returns true if we are searching both sub and parent frames. |
593 | 0 | // There is ambiguity if the caller has previously set one, but not both of |
594 | 0 | // these. |
595 | 0 | *aSearchFrames = mSearchSubFrames && mSearchParentFrames; |
596 | 0 | return NS_OK; |
597 | 0 | } |
598 | | |
599 | | NS_IMETHODIMP |
600 | | nsWebBrowserFind::SetSearchFrames(bool aSearchFrames) |
601 | 0 | { |
602 | 0 | mSearchSubFrames = aSearchFrames; |
603 | 0 | mSearchParentFrames = aSearchFrames; |
604 | 0 | return NS_OK; |
605 | 0 | } |
606 | | |
607 | | NS_IMETHODIMP |
608 | | nsWebBrowserFind::GetCurrentSearchFrame(mozIDOMWindowProxy** aCurrentSearchFrame) |
609 | 0 | { |
610 | 0 | NS_ENSURE_ARG_POINTER(aCurrentSearchFrame); |
611 | 0 | nsCOMPtr<mozIDOMWindowProxy> searchFrame = do_QueryReferent(mCurrentSearchFrame); |
612 | 0 | searchFrame.forget(aCurrentSearchFrame); |
613 | 0 | return (*aCurrentSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED; |
614 | 0 | } |
615 | | |
616 | | NS_IMETHODIMP |
617 | | nsWebBrowserFind::SetCurrentSearchFrame(mozIDOMWindowProxy* aCurrentSearchFrame) |
618 | 0 | { |
619 | 0 | // is it ever valid to set this to null? |
620 | 0 | NS_ENSURE_ARG(aCurrentSearchFrame); |
621 | 0 | mCurrentSearchFrame = do_GetWeakReference(aCurrentSearchFrame); |
622 | 0 | return NS_OK; |
623 | 0 | } |
624 | | |
625 | | NS_IMETHODIMP |
626 | | nsWebBrowserFind::GetRootSearchFrame(mozIDOMWindowProxy** aRootSearchFrame) |
627 | 0 | { |
628 | 0 | NS_ENSURE_ARG_POINTER(aRootSearchFrame); |
629 | 0 | nsCOMPtr<mozIDOMWindowProxy> searchFrame = do_QueryReferent(mRootSearchFrame); |
630 | 0 | searchFrame.forget(aRootSearchFrame); |
631 | 0 | return (*aRootSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED; |
632 | 0 | } |
633 | | |
634 | | NS_IMETHODIMP |
635 | | nsWebBrowserFind::SetRootSearchFrame(mozIDOMWindowProxy* aRootSearchFrame) |
636 | 0 | { |
637 | 0 | // is it ever valid to set this to null? |
638 | 0 | NS_ENSURE_ARG(aRootSearchFrame); |
639 | 0 | mRootSearchFrame = do_GetWeakReference(aRootSearchFrame); |
640 | 0 | return NS_OK; |
641 | 0 | } |
642 | | |
643 | | NS_IMETHODIMP |
644 | | nsWebBrowserFind::GetSearchSubframes(bool* aSearchSubframes) |
645 | 0 | { |
646 | 0 | NS_ENSURE_ARG_POINTER(aSearchSubframes); |
647 | 0 | *aSearchSubframes = mSearchSubFrames; |
648 | 0 | return NS_OK; |
649 | 0 | } |
650 | | |
651 | | NS_IMETHODIMP |
652 | | nsWebBrowserFind::SetSearchSubframes(bool aSearchSubframes) |
653 | 0 | { |
654 | 0 | mSearchSubFrames = aSearchSubframes; |
655 | 0 | return NS_OK; |
656 | 0 | } |
657 | | |
658 | | NS_IMETHODIMP |
659 | | nsWebBrowserFind::GetSearchParentFrames(bool* aSearchParentFrames) |
660 | 0 | { |
661 | 0 | NS_ENSURE_ARG_POINTER(aSearchParentFrames); |
662 | 0 | *aSearchParentFrames = mSearchParentFrames; |
663 | 0 | return NS_OK; |
664 | 0 | } |
665 | | |
666 | | NS_IMETHODIMP |
667 | | nsWebBrowserFind::SetSearchParentFrames(bool aSearchParentFrames) |
668 | 0 | { |
669 | 0 | mSearchParentFrames = aSearchParentFrames; |
670 | 0 | return NS_OK; |
671 | 0 | } |
672 | | |
673 | | /* |
674 | | This method handles finding in a single window (aka frame). |
675 | | |
676 | | */ |
677 | | nsresult |
678 | | nsWebBrowserFind::SearchInFrame(nsPIDOMWindowOuter* aWindow, bool aWrapping, |
679 | | bool* aDidFind) |
680 | 0 | { |
681 | 0 | NS_ENSURE_ARG(aWindow); |
682 | 0 | NS_ENSURE_ARG_POINTER(aDidFind); |
683 | 0 |
|
684 | 0 | *aDidFind = false; |
685 | 0 |
|
686 | 0 | // Do security check, to ensure that the frame we're searching is |
687 | 0 | // accessible from the frame where the Find is being run. |
688 | 0 |
|
689 | 0 | // get a uri for the window |
690 | 0 | nsCOMPtr<nsIDocument> theDoc = aWindow->GetDoc(); |
691 | 0 | if (!theDoc) { |
692 | 0 | return NS_ERROR_FAILURE; |
693 | 0 | } |
694 | 0 | |
695 | 0 | if (!nsContentUtils::SubjectPrincipal()->Subsumes(theDoc->NodePrincipal())) { |
696 | 0 | return NS_ERROR_DOM_PROP_ACCESS_DENIED; |
697 | 0 | } |
698 | 0 | |
699 | 0 | nsresult rv; |
700 | 0 | nsCOMPtr<nsIFind> find = do_CreateInstance(NS_FIND_CONTRACTID, &rv); |
701 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
702 | 0 |
|
703 | 0 | (void)find->SetCaseSensitive(mMatchCase); |
704 | 0 | (void)find->SetFindBackwards(mFindBackwards); |
705 | 0 |
|
706 | 0 | (void)find->SetEntireWord(mEntireWord); |
707 | 0 |
|
708 | 0 | // Now make sure the content (for actual finding) and frame (for |
709 | 0 | // selection) models are up to date. |
710 | 0 | theDoc->FlushPendingNotifications(FlushType::Frames); |
711 | 0 |
|
712 | 0 | RefPtr<Selection> sel = GetFrameSelection(aWindow); |
713 | 0 | NS_ENSURE_ARG_POINTER(sel); |
714 | 0 |
|
715 | 0 | RefPtr<nsRange> searchRange = new nsRange(theDoc); |
716 | 0 | RefPtr<nsRange> startPt = new nsRange(theDoc); |
717 | 0 | RefPtr<nsRange> endPt = new nsRange(theDoc); |
718 | 0 | NS_ENSURE_ARG_POINTER(endPt); |
719 | 0 |
|
720 | 0 | RefPtr<nsRange> foundRange; |
721 | 0 |
|
722 | 0 | rv = GetSearchLimits(searchRange, startPt, endPt, theDoc, sel, aWrapping); |
723 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
724 | 0 |
|
725 | 0 | rv = find->Find(mSearchString.get(), searchRange, startPt, endPt, |
726 | 0 | getter_AddRefs(foundRange)); |
727 | 0 |
|
728 | 0 | if (NS_SUCCEEDED(rv) && foundRange) { |
729 | 0 | *aDidFind = true; |
730 | 0 | sel->RemoveAllRanges(IgnoreErrors()); |
731 | 0 | // Beware! This may flush notifications via synchronous |
732 | 0 | // ScrollSelectionIntoView. |
733 | 0 | SetSelectionAndScroll(aWindow, foundRange); |
734 | 0 | } |
735 | 0 |
|
736 | 0 | return rv; |
737 | 0 | } |
738 | | |
739 | | // called when we start searching a frame that is not the initial focussed |
740 | | // frame. Prepare the frame to be searched. we clear the selection, so that the |
741 | | // search starts from the top of the frame. |
742 | | nsresult |
743 | | nsWebBrowserFind::OnStartSearchFrame(nsPIDOMWindowOuter* aWindow) |
744 | 0 | { |
745 | 0 | return ClearFrameSelection(aWindow); |
746 | 0 | } |
747 | | |
748 | | // called when we are done searching a frame and didn't find anything, and about |
749 | | // about to start searching the next frame. |
750 | | nsresult |
751 | | nsWebBrowserFind::OnEndSearchFrame(nsPIDOMWindowOuter* aWindow) |
752 | 0 | { |
753 | 0 | return NS_OK; |
754 | 0 | } |
755 | | |
756 | | already_AddRefed<Selection> |
757 | | nsWebBrowserFind::GetFrameSelection(nsPIDOMWindowOuter* aWindow) |
758 | 0 | { |
759 | 0 | nsCOMPtr<nsIDocument> doc = aWindow->GetDoc(); |
760 | 0 | if (!doc) { |
761 | 0 | return nullptr; |
762 | 0 | } |
763 | 0 | |
764 | 0 | nsIPresShell* presShell = doc->GetShell(); |
765 | 0 | if (!presShell) { |
766 | 0 | return nullptr; |
767 | 0 | } |
768 | 0 | |
769 | 0 | // text input controls have their independent selection controllers that we |
770 | 0 | // must use when they have focus. |
771 | 0 | nsPresContext* presContext = presShell->GetPresContext(); |
772 | 0 |
|
773 | 0 | nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; |
774 | 0 | nsCOMPtr<nsIContent> focusedContent = |
775 | 0 | nsFocusManager::GetFocusedDescendant(aWindow, |
776 | 0 | nsFocusManager::eOnlyCurrentWindow, |
777 | 0 | getter_AddRefs(focusedWindow)); |
778 | 0 |
|
779 | 0 | nsIFrame* frame = |
780 | 0 | focusedContent ? focusedContent->GetPrimaryFrame() : nullptr; |
781 | 0 |
|
782 | 0 | nsCOMPtr<nsISelectionController> selCon; |
783 | 0 | RefPtr<Selection> sel; |
784 | 0 | if (frame) { |
785 | 0 | frame->GetSelectionController(presContext, getter_AddRefs(selCon)); |
786 | 0 | sel = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL); |
787 | 0 | if (sel && sel->RangeCount() > 0) { |
788 | 0 | return sel.forget(); |
789 | 0 | } |
790 | 0 | } |
791 | 0 | |
792 | 0 | selCon = do_QueryInterface(presShell); |
793 | 0 | sel = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL); |
794 | 0 | return sel.forget(); |
795 | 0 | } |
796 | | |
797 | | nsresult |
798 | | nsWebBrowserFind::ClearFrameSelection(nsPIDOMWindowOuter* aWindow) |
799 | 0 | { |
800 | 0 | NS_ENSURE_ARG(aWindow); |
801 | 0 | RefPtr<Selection> selection = GetFrameSelection(aWindow); |
802 | 0 | if (selection) { |
803 | 0 | selection->RemoveAllRanges(IgnoreErrors()); |
804 | 0 | } |
805 | 0 |
|
806 | 0 | return NS_OK; |
807 | 0 | } |
808 | | |
809 | | nsresult |
810 | | nsWebBrowserFind::OnFind(nsPIDOMWindowOuter* aFoundWindow) |
811 | 0 | { |
812 | 0 | SetCurrentSearchFrame(aFoundWindow); |
813 | 0 |
|
814 | 0 | // We don't want a selection to appear in two frames simultaneously |
815 | 0 | nsCOMPtr<nsPIDOMWindowOuter> lastFocusedWindow = |
816 | 0 | do_QueryReferent(mLastFocusedWindow); |
817 | 0 | if (lastFocusedWindow && lastFocusedWindow != aFoundWindow) { |
818 | 0 | ClearFrameSelection(lastFocusedWindow); |
819 | 0 | } |
820 | 0 |
|
821 | 0 | nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); |
822 | 0 | if (fm) { |
823 | 0 | // get the containing frame and focus it. For top-level windows, the right |
824 | 0 | // window should already be focused. |
825 | 0 | RefPtr<Element> frameElement = aFoundWindow->GetFrameElementInternal(); |
826 | 0 | if (frameElement) { |
827 | 0 | fm->SetFocus(frameElement, 0); |
828 | 0 | } |
829 | 0 |
|
830 | 0 | mLastFocusedWindow = do_GetWeakReference(aFoundWindow); |
831 | 0 | } |
832 | 0 |
|
833 | 0 | return NS_OK; |
834 | 0 | } |