embedding/components/find/src/nsWebBrowserFind.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial