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