nsWebBrowserFind.cpp 24 KB

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