1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/embedding/components/find/src/nsWebBrowserFind.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,848 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsWebBrowserFind.h" 1.11 + 1.12 +// Only need this for NS_FIND_CONTRACTID, 1.13 +// else we could use nsIDOMRange.h and nsIFind.h. 1.14 +#include "nsFind.h" 1.15 + 1.16 +#include "nsIComponentManager.h" 1.17 +#include "nsIScriptSecurityManager.h" 1.18 +#include "nsIInterfaceRequestor.h" 1.19 +#include "nsIInterfaceRequestorUtils.h" 1.20 +#include "nsPIDOMWindow.h" 1.21 +#include "nsIURI.h" 1.22 +#include "nsIDocShell.h" 1.23 +#include "nsIPresShell.h" 1.24 +#include "nsPresContext.h" 1.25 +#include "nsIDocument.h" 1.26 +#include "nsIDOMDocument.h" 1.27 +#include "nsISelectionController.h" 1.28 +#include "nsISelection.h" 1.29 +#include "nsIFrame.h" 1.30 +#include "nsITextControlFrame.h" 1.31 +#include "nsReadableUtils.h" 1.32 +#include "nsIDOMHTMLElement.h" 1.33 +#include "nsIDOMHTMLDocument.h" 1.34 +#include "nsIContent.h" 1.35 +#include "nsContentCID.h" 1.36 +#include "nsIServiceManager.h" 1.37 +#include "nsIObserverService.h" 1.38 +#include "nsISupportsPrimitives.h" 1.39 +#include "nsFind.h" 1.40 +#include "nsError.h" 1.41 +#include "nsFocusManager.h" 1.42 +#include "mozilla/Services.h" 1.43 +#include "mozilla/dom/Element.h" 1.44 +#include "nsISimpleEnumerator.h" 1.45 + 1.46 +#if DEBUG 1.47 +#include "nsIWebNavigation.h" 1.48 +#include "nsXPIDLString.h" 1.49 +#endif 1.50 + 1.51 +//***************************************************************************** 1.52 +// nsWebBrowserFind 1.53 +//***************************************************************************** 1.54 + 1.55 +nsWebBrowserFind::nsWebBrowserFind() : 1.56 + mFindBackwards(false), 1.57 + mWrapFind(false), 1.58 + mEntireWord(false), 1.59 + mMatchCase(false), 1.60 + mSearchSubFrames(true), 1.61 + mSearchParentFrames(true) 1.62 +{ 1.63 +} 1.64 + 1.65 +nsWebBrowserFind::~nsWebBrowserFind() 1.66 +{ 1.67 +} 1.68 + 1.69 +NS_IMPL_ISUPPORTS(nsWebBrowserFind, nsIWebBrowserFind, nsIWebBrowserFindInFrames) 1.70 + 1.71 + 1.72 +/* boolean findNext (); */ 1.73 +NS_IMETHODIMP nsWebBrowserFind::FindNext(bool *outDidFind) 1.74 +{ 1.75 + NS_ENSURE_ARG_POINTER(outDidFind); 1.76 + *outDidFind = false; 1.77 + 1.78 + NS_ENSURE_TRUE(CanFindNext(), NS_ERROR_NOT_INITIALIZED); 1.79 + 1.80 + nsresult rv = NS_OK; 1.81 + nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mCurrentSearchFrame); 1.82 + NS_ENSURE_TRUE(searchFrame, NS_ERROR_NOT_INITIALIZED); 1.83 + 1.84 + nsCOMPtr<nsIDOMWindow> rootFrame = do_QueryReferent(mRootSearchFrame); 1.85 + NS_ENSURE_TRUE(rootFrame, NS_ERROR_NOT_INITIALIZED); 1.86 + 1.87 + // first, if there's a "cmd_findagain" observer around, check to see if it 1.88 + // wants to perform the find again command . If it performs the find again 1.89 + // it will return true, in which case we exit ::FindNext() early. 1.90 + // Otherwise, nsWebBrowserFind needs to perform the find again command itself 1.91 + // this is used by nsTypeAheadFind, which controls find again when it was 1.92 + // the last executed find in the current window. 1.93 + nsCOMPtr<nsIObserverService> observerSvc = 1.94 + mozilla::services::GetObserverService(); 1.95 + if (observerSvc) { 1.96 + nsCOMPtr<nsISupportsInterfacePointer> windowSupportsData = 1.97 + do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv); 1.98 + NS_ENSURE_SUCCESS(rv, rv); 1.99 + nsCOMPtr<nsISupports> searchWindowSupports = 1.100 + do_QueryInterface(rootFrame); 1.101 + windowSupportsData->SetData(searchWindowSupports); 1.102 + NS_NAMED_LITERAL_STRING(dnStr, "down"); 1.103 + NS_NAMED_LITERAL_STRING(upStr, "up"); 1.104 + observerSvc->NotifyObservers(windowSupportsData, 1.105 + "nsWebBrowserFind_FindAgain", 1.106 + mFindBackwards? upStr.get(): dnStr.get()); 1.107 + windowSupportsData->GetData(getter_AddRefs(searchWindowSupports)); 1.108 + // findnext performed if search window data cleared out 1.109 + *outDidFind = searchWindowSupports == nullptr; 1.110 + if (*outDidFind) 1.111 + return NS_OK; 1.112 + } 1.113 + 1.114 + // next, look in the current frame. If found, return. 1.115 + 1.116 + // Beware! This may flush notifications via synchronous 1.117 + // ScrollSelectionIntoView. 1.118 + rv = SearchInFrame(searchFrame, false, outDidFind); 1.119 + if (NS_FAILED(rv)) return rv; 1.120 + if (*outDidFind) 1.121 + return OnFind(searchFrame); // we are done 1.122 + 1.123 + // if we are not searching other frames, return 1.124 + if (!mSearchSubFrames && !mSearchParentFrames) 1.125 + return NS_OK; 1.126 + 1.127 + nsIDocShell *rootDocShell = GetDocShellFromWindow(rootFrame); 1.128 + if (!rootDocShell) return NS_ERROR_FAILURE; 1.129 + 1.130 + int32_t enumDirection; 1.131 + if (mFindBackwards) 1.132 + enumDirection = nsIDocShell::ENUMERATE_BACKWARDS; 1.133 + else 1.134 + enumDirection = nsIDocShell::ENUMERATE_FORWARDS; 1.135 + 1.136 + nsCOMPtr<nsISimpleEnumerator> docShellEnumerator; 1.137 + rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll, 1.138 + enumDirection, getter_AddRefs(docShellEnumerator)); 1.139 + if (NS_FAILED(rv)) return rv; 1.140 + 1.141 + // remember where we started 1.142 + nsCOMPtr<nsIDocShellTreeItem> startingItem = 1.143 + do_QueryInterface(GetDocShellFromWindow(searchFrame), &rv); 1.144 + if (NS_FAILED(rv)) return rv; 1.145 + 1.146 + nsCOMPtr<nsIDocShellTreeItem> curItem; 1.147 + 1.148 + // XXX We should avoid searching in frameset documents here. 1.149 + // We also need to honour mSearchSubFrames and mSearchParentFrames. 1.150 + bool hasMore, doFind = false; 1.151 + while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && hasMore) 1.152 + { 1.153 + nsCOMPtr<nsISupports> curSupports; 1.154 + rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports)); 1.155 + if (NS_FAILED(rv)) break; 1.156 + curItem = do_QueryInterface(curSupports, &rv); 1.157 + if (NS_FAILED(rv)) break; 1.158 + 1.159 + if (doFind) 1.160 + { 1.161 + searchFrame = do_GetInterface(curItem, &rv); 1.162 + if (NS_FAILED(rv)) break; 1.163 + 1.164 + OnStartSearchFrame(searchFrame); 1.165 + 1.166 + // Beware! This may flush notifications via synchronous 1.167 + // ScrollSelectionIntoView. 1.168 + rv = SearchInFrame(searchFrame, false, outDidFind); 1.169 + if (NS_FAILED(rv)) return rv; 1.170 + if (*outDidFind) 1.171 + return OnFind(searchFrame); // we are done 1.172 + 1.173 + OnEndSearchFrame(searchFrame); 1.174 + } 1.175 + 1.176 + if (curItem.get() == startingItem.get()) 1.177 + doFind = true; // start looking in frames after this one 1.178 + }; 1.179 + 1.180 + if (!mWrapFind) 1.181 + { 1.182 + // remember where we left off 1.183 + SetCurrentSearchFrame(searchFrame); 1.184 + return NS_OK; 1.185 + } 1.186 + 1.187 + // From here on, we're wrapping, first through the other frames, 1.188 + // then finally from the beginning of the starting frame back to 1.189 + // the starting point. 1.190 + 1.191 + // because nsISimpleEnumerator is totally lame and isn't resettable, I 1.192 + // have to make a new one 1.193 + docShellEnumerator = nullptr; 1.194 + rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll, 1.195 + enumDirection, getter_AddRefs(docShellEnumerator)); 1.196 + if (NS_FAILED(rv)) return rv; 1.197 + 1.198 + while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && hasMore) 1.199 + { 1.200 + nsCOMPtr<nsISupports> curSupports; 1.201 + rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports)); 1.202 + if (NS_FAILED(rv)) break; 1.203 + curItem = do_QueryInterface(curSupports, &rv); 1.204 + if (NS_FAILED(rv)) break; 1.205 + 1.206 + searchFrame = do_GetInterface(curItem, &rv); 1.207 + if (NS_FAILED(rv)) break; 1.208 + 1.209 + if (curItem.get() == startingItem.get()) 1.210 + { 1.211 + // Beware! This may flush notifications via synchronous 1.212 + // ScrollSelectionIntoView. 1.213 + rv = SearchInFrame(searchFrame, true, outDidFind); 1.214 + if (NS_FAILED(rv)) return rv; 1.215 + if (*outDidFind) 1.216 + return OnFind(searchFrame); // we are done 1.217 + break; 1.218 + } 1.219 + 1.220 + OnStartSearchFrame(searchFrame); 1.221 + 1.222 + // Beware! This may flush notifications via synchronous 1.223 + // ScrollSelectionIntoView. 1.224 + rv = SearchInFrame(searchFrame, false, outDidFind); 1.225 + if (NS_FAILED(rv)) return rv; 1.226 + if (*outDidFind) 1.227 + return OnFind(searchFrame); // we are done 1.228 + 1.229 + OnEndSearchFrame(searchFrame); 1.230 + } 1.231 + 1.232 + // remember where we left off 1.233 + SetCurrentSearchFrame(searchFrame); 1.234 + 1.235 + NS_ASSERTION(NS_SUCCEEDED(rv), "Something failed"); 1.236 + return rv; 1.237 +} 1.238 + 1.239 + 1.240 +/* attribute wstring searchString; */ 1.241 +NS_IMETHODIMP nsWebBrowserFind::GetSearchString(char16_t * *aSearchString) 1.242 +{ 1.243 + NS_ENSURE_ARG_POINTER(aSearchString); 1.244 + *aSearchString = ToNewUnicode(mSearchString); 1.245 + return NS_OK; 1.246 +} 1.247 + 1.248 +NS_IMETHODIMP nsWebBrowserFind::SetSearchString(const char16_t * aSearchString) 1.249 +{ 1.250 + mSearchString.Assign(aSearchString); 1.251 + return NS_OK; 1.252 +} 1.253 + 1.254 +/* attribute boolean findBackwards; */ 1.255 +NS_IMETHODIMP nsWebBrowserFind::GetFindBackwards(bool *aFindBackwards) 1.256 +{ 1.257 + NS_ENSURE_ARG_POINTER(aFindBackwards); 1.258 + *aFindBackwards = mFindBackwards; 1.259 + return NS_OK; 1.260 +} 1.261 + 1.262 +NS_IMETHODIMP nsWebBrowserFind::SetFindBackwards(bool aFindBackwards) 1.263 +{ 1.264 + mFindBackwards = aFindBackwards; 1.265 + return NS_OK; 1.266 +} 1.267 + 1.268 +/* attribute boolean wrapFind; */ 1.269 +NS_IMETHODIMP nsWebBrowserFind::GetWrapFind(bool *aWrapFind) 1.270 +{ 1.271 + NS_ENSURE_ARG_POINTER(aWrapFind); 1.272 + *aWrapFind = mWrapFind; 1.273 + return NS_OK; 1.274 +} 1.275 +NS_IMETHODIMP nsWebBrowserFind::SetWrapFind(bool aWrapFind) 1.276 +{ 1.277 + mWrapFind = aWrapFind; 1.278 + return NS_OK; 1.279 +} 1.280 + 1.281 +/* attribute boolean entireWord; */ 1.282 +NS_IMETHODIMP nsWebBrowserFind::GetEntireWord(bool *aEntireWord) 1.283 +{ 1.284 + NS_ENSURE_ARG_POINTER(aEntireWord); 1.285 + *aEntireWord = mEntireWord; 1.286 + return NS_OK; 1.287 +} 1.288 +NS_IMETHODIMP nsWebBrowserFind::SetEntireWord(bool aEntireWord) 1.289 +{ 1.290 + mEntireWord = aEntireWord; 1.291 + return NS_OK; 1.292 +} 1.293 + 1.294 +/* attribute boolean matchCase; */ 1.295 +NS_IMETHODIMP nsWebBrowserFind::GetMatchCase(bool *aMatchCase) 1.296 +{ 1.297 + NS_ENSURE_ARG_POINTER(aMatchCase); 1.298 + *aMatchCase = mMatchCase; 1.299 + return NS_OK; 1.300 +} 1.301 +NS_IMETHODIMP nsWebBrowserFind::SetMatchCase(bool aMatchCase) 1.302 +{ 1.303 + mMatchCase = aMatchCase; 1.304 + return NS_OK; 1.305 +} 1.306 + 1.307 +static bool 1.308 +IsInNativeAnonymousSubtree(nsIContent* aContent) 1.309 +{ 1.310 + while (aContent) { 1.311 + nsIContent* bindingParent = aContent->GetBindingParent(); 1.312 + if (bindingParent == aContent) { 1.313 + return true; 1.314 + } 1.315 + 1.316 + aContent = bindingParent; 1.317 + } 1.318 + 1.319 + return false; 1.320 +} 1.321 + 1.322 +void nsWebBrowserFind::SetSelectionAndScroll(nsIDOMWindow* aWindow, 1.323 + nsIDOMRange* aRange) 1.324 +{ 1.325 + nsCOMPtr<nsIDOMDocument> domDoc; 1.326 + aWindow->GetDocument(getter_AddRefs(domDoc)); 1.327 + if (!domDoc) return; 1.328 + 1.329 + nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc)); 1.330 + nsIPresShell* presShell = doc->GetShell(); 1.331 + if (!presShell) return; 1.332 + 1.333 + nsCOMPtr<nsIDOMNode> node; 1.334 + aRange->GetStartContainer(getter_AddRefs(node)); 1.335 + nsCOMPtr<nsIContent> content(do_QueryInterface(node)); 1.336 + nsIFrame* frame = content->GetPrimaryFrame(); 1.337 + if (!frame) 1.338 + return; 1.339 + nsCOMPtr<nsISelectionController> selCon; 1.340 + frame->GetSelectionController(presShell->GetPresContext(), 1.341 + getter_AddRefs(selCon)); 1.342 + 1.343 + // since the match could be an anonymous textnode inside a 1.344 + // <textarea> or text <input>, we need to get the outer frame 1.345 + nsITextControlFrame *tcFrame = nullptr; 1.346 + for ( ; content; content = content->GetParent()) { 1.347 + if (!IsInNativeAnonymousSubtree(content)) { 1.348 + nsIFrame* f = content->GetPrimaryFrame(); 1.349 + if (!f) 1.350 + return; 1.351 + tcFrame = do_QueryFrame(f); 1.352 + break; 1.353 + } 1.354 + } 1.355 + 1.356 + nsCOMPtr<nsISelection> selection; 1.357 + 1.358 + selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); 1.359 + selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, 1.360 + getter_AddRefs(selection)); 1.361 + if (selection) { 1.362 + selection->RemoveAllRanges(); 1.363 + selection->AddRange(aRange); 1.364 + 1.365 + nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); 1.366 + if (fm) { 1.367 + if (tcFrame) { 1.368 + nsCOMPtr<nsIDOMElement> newFocusedElement(do_QueryInterface(content)); 1.369 + fm->SetFocus(newFocusedElement, nsIFocusManager::FLAG_NOSCROLL); 1.370 + } 1.371 + else { 1.372 + nsCOMPtr<nsIDOMElement> result; 1.373 + fm->MoveFocus(aWindow, nullptr, nsIFocusManager::MOVEFOCUS_CARET, 1.374 + nsIFocusManager::FLAG_NOSCROLL, 1.375 + getter_AddRefs(result)); 1.376 + } 1.377 + } 1.378 + 1.379 + // Scroll if necessary to make the selection visible: 1.380 + // Must be the last thing to do - bug 242056 1.381 + 1.382 + // After ScrollSelectionIntoView(), the pending notifications might be 1.383 + // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. 1.384 + selCon->ScrollSelectionIntoView 1.385 + (nsISelectionController::SELECTION_NORMAL, 1.386 + nsISelectionController::SELECTION_WHOLE_SELECTION, 1.387 + nsISelectionController::SCROLL_CENTER_VERTICALLY | 1.388 + nsISelectionController::SCROLL_SYNCHRONOUS); 1.389 + } 1.390 +} 1.391 + 1.392 +// Adapted from nsTextServicesDocument::GetDocumentContentRootNode 1.393 +nsresult nsWebBrowserFind::GetRootNode(nsIDOMDocument* aDomDoc, 1.394 + nsIDOMNode **aNode) 1.395 +{ 1.396 + nsresult rv; 1.397 + 1.398 + NS_ENSURE_ARG_POINTER(aNode); 1.399 + *aNode = 0; 1.400 + 1.401 + nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDomDoc); 1.402 + if (htmlDoc) 1.403 + { 1.404 + // For HTML documents, the content root node is the body. 1.405 + nsCOMPtr<nsIDOMHTMLElement> bodyElement; 1.406 + rv = htmlDoc->GetBody(getter_AddRefs(bodyElement)); 1.407 + NS_ENSURE_SUCCESS(rv, rv); 1.408 + NS_ENSURE_ARG_POINTER(bodyElement); 1.409 + return bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode), 1.410 + (void **)aNode); 1.411 + } 1.412 + 1.413 + // For non-HTML documents, the content root node will be the doc element. 1.414 + nsCOMPtr<nsIDOMElement> docElement; 1.415 + rv = aDomDoc->GetDocumentElement(getter_AddRefs(docElement)); 1.416 + NS_ENSURE_SUCCESS(rv, rv); 1.417 + NS_ENSURE_ARG_POINTER(docElement); 1.418 + return docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode); 1.419 +} 1.420 + 1.421 +nsresult nsWebBrowserFind::SetRangeAroundDocument(nsIDOMRange* aSearchRange, 1.422 + nsIDOMRange* aStartPt, 1.423 + nsIDOMRange* aEndPt, 1.424 + nsIDOMDocument* aDoc) 1.425 +{ 1.426 + nsCOMPtr<nsIDOMNode> bodyNode; 1.427 + nsresult rv = GetRootNode(aDoc, getter_AddRefs(bodyNode)); 1.428 + nsCOMPtr<nsIContent> bodyContent (do_QueryInterface(bodyNode)); 1.429 + NS_ENSURE_SUCCESS(rv, rv); 1.430 + NS_ENSURE_ARG_POINTER(bodyContent); 1.431 + 1.432 + uint32_t childCount = bodyContent->GetChildCount(); 1.433 + 1.434 + aSearchRange->SetStart(bodyNode, 0); 1.435 + aSearchRange->SetEnd(bodyNode, childCount); 1.436 + 1.437 + if (mFindBackwards) 1.438 + { 1.439 + aStartPt->SetStart(bodyNode, childCount); 1.440 + aStartPt->SetEnd(bodyNode, childCount); 1.441 + aEndPt->SetStart(bodyNode, 0); 1.442 + aEndPt->SetEnd(bodyNode, 0); 1.443 + } 1.444 + else 1.445 + { 1.446 + aStartPt->SetStart(bodyNode, 0); 1.447 + aStartPt->SetEnd(bodyNode, 0); 1.448 + aEndPt->SetStart(bodyNode, childCount); 1.449 + aEndPt->SetEnd(bodyNode, childCount); 1.450 + } 1.451 + 1.452 + return NS_OK; 1.453 +} 1.454 + 1.455 +// Set the range to go from the end of the current selection to the 1.456 +// end of the document (forward), or beginning to beginning (reverse). 1.457 +// or around the whole document if there's no selection. 1.458 +nsresult 1.459 +nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange, 1.460 + nsIDOMRange* aStartPt, 1.461 + nsIDOMRange* aEndPt, 1.462 + nsIDOMDocument* aDoc, 1.463 + nsISelection* aSel, 1.464 + bool aWrap) 1.465 +{ 1.466 + NS_ENSURE_ARG_POINTER(aSel); 1.467 + 1.468 + // There is a selection. 1.469 + int32_t count = -1; 1.470 + nsresult rv = aSel->GetRangeCount(&count); 1.471 + NS_ENSURE_SUCCESS(rv, rv); 1.472 + if (count < 1) 1.473 + return SetRangeAroundDocument(aSearchRange, aStartPt, aEndPt, aDoc); 1.474 + 1.475 + // Need bodyNode, for the start/end of the document 1.476 + nsCOMPtr<nsIDOMNode> bodyNode; 1.477 + rv = GetRootNode(aDoc, getter_AddRefs(bodyNode)); 1.478 + NS_ENSURE_SUCCESS(rv, rv); 1.479 + 1.480 + nsCOMPtr<nsIContent> bodyContent (do_QueryInterface(bodyNode)); 1.481 + NS_ENSURE_ARG_POINTER(bodyContent); 1.482 + 1.483 + uint32_t childCount = bodyContent->GetChildCount(); 1.484 + 1.485 + // There are four possible range endpoints we might use: 1.486 + // DocumentStart, SelectionStart, SelectionEnd, DocumentEnd. 1.487 + 1.488 + nsCOMPtr<nsIDOMRange> range; 1.489 + nsCOMPtr<nsIDOMNode> node; 1.490 + int32_t offset; 1.491 + 1.492 + // Forward, not wrapping: SelEnd to DocEnd 1.493 + if (!mFindBackwards && !aWrap) 1.494 + { 1.495 + // This isn't quite right, since the selection's ranges aren't 1.496 + // necessarily in order; but they usually will be. 1.497 + aSel->GetRangeAt(count-1, getter_AddRefs(range)); 1.498 + if (!range) return NS_ERROR_UNEXPECTED; 1.499 + range->GetEndContainer(getter_AddRefs(node)); 1.500 + if (!node) return NS_ERROR_UNEXPECTED; 1.501 + range->GetEndOffset(&offset); 1.502 + 1.503 + aSearchRange->SetStart(node, offset); 1.504 + aSearchRange->SetEnd(bodyNode, childCount); 1.505 + aStartPt->SetStart(node, offset); 1.506 + aStartPt->SetEnd(node, offset); 1.507 + aEndPt->SetStart(bodyNode, childCount); 1.508 + aEndPt->SetEnd(bodyNode, childCount); 1.509 + } 1.510 + // Backward, not wrapping: DocStart to SelStart 1.511 + else if (mFindBackwards && !aWrap) 1.512 + { 1.513 + aSel->GetRangeAt(0, getter_AddRefs(range)); 1.514 + if (!range) return NS_ERROR_UNEXPECTED; 1.515 + range->GetStartContainer(getter_AddRefs(node)); 1.516 + if (!node) return NS_ERROR_UNEXPECTED; 1.517 + range->GetStartOffset(&offset); 1.518 + 1.519 + aSearchRange->SetStart(bodyNode, 0); 1.520 + aSearchRange->SetEnd(bodyNode, childCount); 1.521 + aStartPt->SetStart(node, offset); 1.522 + aStartPt->SetEnd(node, offset); 1.523 + aEndPt->SetStart(bodyNode, 0); 1.524 + aEndPt->SetEnd(bodyNode, 0); 1.525 + } 1.526 + // Forward, wrapping: DocStart to SelEnd 1.527 + else if (!mFindBackwards && aWrap) 1.528 + { 1.529 + aSel->GetRangeAt(count-1, getter_AddRefs(range)); 1.530 + if (!range) return NS_ERROR_UNEXPECTED; 1.531 + range->GetEndContainer(getter_AddRefs(node)); 1.532 + if (!node) return NS_ERROR_UNEXPECTED; 1.533 + range->GetEndOffset(&offset); 1.534 + 1.535 + aSearchRange->SetStart(bodyNode, 0); 1.536 + aSearchRange->SetEnd(bodyNode, childCount); 1.537 + aStartPt->SetStart(bodyNode, 0); 1.538 + aStartPt->SetEnd(bodyNode, 0); 1.539 + aEndPt->SetStart(node, offset); 1.540 + aEndPt->SetEnd(node, offset); 1.541 + } 1.542 + // Backward, wrapping: SelStart to DocEnd 1.543 + else if (mFindBackwards && aWrap) 1.544 + { 1.545 + aSel->GetRangeAt(0, getter_AddRefs(range)); 1.546 + if (!range) return NS_ERROR_UNEXPECTED; 1.547 + range->GetStartContainer(getter_AddRefs(node)); 1.548 + if (!node) return NS_ERROR_UNEXPECTED; 1.549 + range->GetStartOffset(&offset); 1.550 + 1.551 + aSearchRange->SetStart(bodyNode, 0); 1.552 + aSearchRange->SetEnd(bodyNode, childCount); 1.553 + aStartPt->SetStart(bodyNode, childCount); 1.554 + aStartPt->SetEnd(bodyNode, childCount); 1.555 + aEndPt->SetStart(node, offset); 1.556 + aEndPt->SetEnd(node, offset); 1.557 + } 1.558 + return NS_OK; 1.559 +} 1.560 + 1.561 +/* attribute boolean searchFrames; */ 1.562 +NS_IMETHODIMP nsWebBrowserFind::GetSearchFrames(bool *aSearchFrames) 1.563 +{ 1.564 + NS_ENSURE_ARG_POINTER(aSearchFrames); 1.565 + // this only returns true if we are searching both sub and parent 1.566 + // frames. There is ambiguity if the caller has previously set 1.567 + // one, but not both of these. 1.568 + *aSearchFrames = mSearchSubFrames && mSearchParentFrames; 1.569 + return NS_OK; 1.570 +} 1.571 + 1.572 +NS_IMETHODIMP nsWebBrowserFind::SetSearchFrames(bool aSearchFrames) 1.573 +{ 1.574 + mSearchSubFrames = aSearchFrames; 1.575 + mSearchParentFrames = aSearchFrames; 1.576 + return NS_OK; 1.577 +} 1.578 + 1.579 +/* attribute nsIDOMWindow currentSearchFrame; */ 1.580 +NS_IMETHODIMP nsWebBrowserFind::GetCurrentSearchFrame(nsIDOMWindow * *aCurrentSearchFrame) 1.581 +{ 1.582 + NS_ENSURE_ARG_POINTER(aCurrentSearchFrame); 1.583 + nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mCurrentSearchFrame); 1.584 + NS_IF_ADDREF(*aCurrentSearchFrame = searchFrame); 1.585 + return (*aCurrentSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED; 1.586 +} 1.587 + 1.588 +NS_IMETHODIMP nsWebBrowserFind::SetCurrentSearchFrame(nsIDOMWindow * aCurrentSearchFrame) 1.589 +{ 1.590 + // is it ever valid to set this to null? 1.591 + NS_ENSURE_ARG(aCurrentSearchFrame); 1.592 + mCurrentSearchFrame = do_GetWeakReference(aCurrentSearchFrame); 1.593 + return NS_OK; 1.594 +} 1.595 + 1.596 +/* attribute nsIDOMWindow rootSearchFrame; */ 1.597 +NS_IMETHODIMP nsWebBrowserFind::GetRootSearchFrame(nsIDOMWindow * *aRootSearchFrame) 1.598 +{ 1.599 + NS_ENSURE_ARG_POINTER(aRootSearchFrame); 1.600 + nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mRootSearchFrame); 1.601 + NS_IF_ADDREF(*aRootSearchFrame = searchFrame); 1.602 + return (*aRootSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED; 1.603 +} 1.604 + 1.605 +NS_IMETHODIMP nsWebBrowserFind::SetRootSearchFrame(nsIDOMWindow * aRootSearchFrame) 1.606 +{ 1.607 + // is it ever valid to set this to null? 1.608 + NS_ENSURE_ARG(aRootSearchFrame); 1.609 + mRootSearchFrame = do_GetWeakReference(aRootSearchFrame); 1.610 + return NS_OK; 1.611 +} 1.612 + 1.613 +/* attribute boolean searchSubframes; */ 1.614 +NS_IMETHODIMP nsWebBrowserFind::GetSearchSubframes(bool *aSearchSubframes) 1.615 +{ 1.616 + NS_ENSURE_ARG_POINTER(aSearchSubframes); 1.617 + *aSearchSubframes = mSearchSubFrames; 1.618 + return NS_OK; 1.619 +} 1.620 + 1.621 +NS_IMETHODIMP nsWebBrowserFind::SetSearchSubframes(bool aSearchSubframes) 1.622 +{ 1.623 + mSearchSubFrames = aSearchSubframes; 1.624 + return NS_OK; 1.625 +} 1.626 + 1.627 +/* attribute boolean searchParentFrames; */ 1.628 +NS_IMETHODIMP nsWebBrowserFind::GetSearchParentFrames(bool *aSearchParentFrames) 1.629 +{ 1.630 + NS_ENSURE_ARG_POINTER(aSearchParentFrames); 1.631 + *aSearchParentFrames = mSearchParentFrames; 1.632 + return NS_OK; 1.633 +} 1.634 + 1.635 +NS_IMETHODIMP nsWebBrowserFind::SetSearchParentFrames(bool aSearchParentFrames) 1.636 +{ 1.637 + mSearchParentFrames = aSearchParentFrames; 1.638 + return NS_OK; 1.639 +} 1.640 + 1.641 +/* 1.642 + This method handles finding in a single window (aka frame). 1.643 + 1.644 +*/ 1.645 +nsresult nsWebBrowserFind::SearchInFrame(nsIDOMWindow* aWindow, 1.646 + bool aWrapping, 1.647 + bool* aDidFind) 1.648 +{ 1.649 + NS_ENSURE_ARG(aWindow); 1.650 + NS_ENSURE_ARG_POINTER(aDidFind); 1.651 + 1.652 + *aDidFind = false; 1.653 + 1.654 + nsCOMPtr<nsIDOMDocument> domDoc; 1.655 + nsresult rv = aWindow->GetDocument(getter_AddRefs(domDoc)); 1.656 + NS_ENSURE_SUCCESS(rv, rv); 1.657 + if (!domDoc) return NS_ERROR_FAILURE; 1.658 + 1.659 + // Do security check, to ensure that the frame we're searching is 1.660 + // acccessible from the frame where the Find is being run. 1.661 + 1.662 + // get a uri for the window 1.663 + nsCOMPtr<nsIDocument> theDoc = do_QueryInterface(domDoc); 1.664 + if (!theDoc) return NS_ERROR_FAILURE; 1.665 + 1.666 + nsCOMPtr<nsIScriptSecurityManager> secMan = 1.667 + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); 1.668 + NS_ENSURE_SUCCESS(rv, rv); 1.669 + 1.670 + nsCOMPtr<nsIPrincipal> subject; 1.671 + rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject)); 1.672 + NS_ENSURE_SUCCESS(rv, rv); 1.673 + 1.674 + if (subject) { 1.675 + bool subsumes; 1.676 + rv = subject->Subsumes(theDoc->NodePrincipal(), &subsumes); 1.677 + NS_ENSURE_SUCCESS(rv, rv); 1.678 + if (!subsumes) { 1.679 + return NS_ERROR_DOM_PROP_ACCESS_DENIED; 1.680 + } 1.681 + } 1.682 + 1.683 + nsCOMPtr<nsIFind> find = do_CreateInstance(NS_FIND_CONTRACTID, &rv); 1.684 + NS_ENSURE_SUCCESS(rv, rv); 1.685 + 1.686 + (void) find->SetCaseSensitive(mMatchCase); 1.687 + (void) find->SetFindBackwards(mFindBackwards); 1.688 + 1.689 + // XXX Make and set a line breaker here, once that's implemented. 1.690 + (void) find->SetWordBreaker(0); 1.691 + 1.692 + // Now make sure the content (for actual finding) and frame (for 1.693 + // selection) models are up to date. 1.694 + theDoc->FlushPendingNotifications(Flush_Frames); 1.695 + 1.696 + nsCOMPtr<nsISelection> sel; 1.697 + GetFrameSelection(aWindow, getter_AddRefs(sel)); 1.698 + NS_ENSURE_ARG_POINTER(sel); 1.699 + 1.700 + nsCOMPtr<nsIDOMRange> searchRange = nsFind::CreateRange(theDoc); 1.701 + NS_ENSURE_ARG_POINTER(searchRange); 1.702 + nsCOMPtr<nsIDOMRange> startPt = nsFind::CreateRange(theDoc); 1.703 + NS_ENSURE_ARG_POINTER(startPt); 1.704 + nsCOMPtr<nsIDOMRange> endPt = nsFind::CreateRange(theDoc); 1.705 + NS_ENSURE_ARG_POINTER(endPt); 1.706 + 1.707 + nsCOMPtr<nsIDOMRange> foundRange; 1.708 + 1.709 + // If !aWrapping, search from selection to end 1.710 + if (!aWrapping) 1.711 + rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel, 1.712 + false); 1.713 + 1.714 + // If aWrapping, search the part of the starting frame 1.715 + // up to the point where we left off. 1.716 + else 1.717 + rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel, 1.718 + true); 1.719 + 1.720 + NS_ENSURE_SUCCESS(rv, rv); 1.721 + 1.722 + rv = find->Find(mSearchString.get(), searchRange, startPt, endPt, 1.723 + getter_AddRefs(foundRange)); 1.724 + 1.725 + if (NS_SUCCEEDED(rv) && foundRange) 1.726 + { 1.727 + *aDidFind = true; 1.728 + sel->RemoveAllRanges(); 1.729 + // Beware! This may flush notifications via synchronous 1.730 + // ScrollSelectionIntoView. 1.731 + SetSelectionAndScroll(aWindow, foundRange); 1.732 + } 1.733 + 1.734 + return rv; 1.735 +} 1.736 + 1.737 + 1.738 +// called when we start searching a frame that is not the initial 1.739 +// focussed frame. Prepare the frame to be searched. 1.740 +// we clear the selection, so that the search starts from the top 1.741 +// of the frame. 1.742 +nsresult nsWebBrowserFind::OnStartSearchFrame(nsIDOMWindow *aWindow) 1.743 +{ 1.744 + return ClearFrameSelection(aWindow); 1.745 +} 1.746 + 1.747 +// called when we are done searching a frame and didn't find anything, 1.748 +// and about about to start searching the next frame. 1.749 +nsresult nsWebBrowserFind::OnEndSearchFrame(nsIDOMWindow *aWindow) 1.750 +{ 1.751 + return NS_OK; 1.752 +} 1.753 + 1.754 +void 1.755 +nsWebBrowserFind::GetFrameSelection(nsIDOMWindow* aWindow, 1.756 + nsISelection** aSel) 1.757 +{ 1.758 + *aSel = nullptr; 1.759 + 1.760 + nsCOMPtr<nsIDOMDocument> domDoc; 1.761 + aWindow->GetDocument(getter_AddRefs(domDoc)); 1.762 + if (!domDoc) return; 1.763 + 1.764 + nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc)); 1.765 + nsIPresShell* presShell = doc->GetShell(); 1.766 + if (!presShell) return; 1.767 + 1.768 + // text input controls have their independent selection controllers 1.769 + // that we must use when they have focus. 1.770 + nsPresContext *presContext = presShell->GetPresContext(); 1.771 + 1.772 + nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow)); 1.773 + 1.774 + nsCOMPtr<nsPIDOMWindow> focusedWindow; 1.775 + nsCOMPtr<nsIContent> focusedContent = 1.776 + nsFocusManager::GetFocusedDescendant(window, false, getter_AddRefs(focusedWindow)); 1.777 + 1.778 + nsIFrame *frame = focusedContent ? focusedContent->GetPrimaryFrame() : nullptr; 1.779 + 1.780 + nsCOMPtr<nsISelectionController> selCon; 1.781 + if (frame) { 1.782 + frame->GetSelectionController(presContext, getter_AddRefs(selCon)); 1.783 + selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSel); 1.784 + if (*aSel) { 1.785 + int32_t count = -1; 1.786 + (*aSel)->GetRangeCount(&count); 1.787 + if (count > 0) { 1.788 + return; 1.789 + } 1.790 + NS_RELEASE(*aSel); 1.791 + } 1.792 + } 1.793 + 1.794 + selCon = do_QueryInterface(presShell); 1.795 + selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSel); 1.796 +} 1.797 + 1.798 +nsresult nsWebBrowserFind::ClearFrameSelection(nsIDOMWindow *aWindow) 1.799 +{ 1.800 + NS_ENSURE_ARG(aWindow); 1.801 + nsCOMPtr<nsISelection> selection; 1.802 + GetFrameSelection(aWindow, getter_AddRefs(selection)); 1.803 + if (selection) 1.804 + selection->RemoveAllRanges(); 1.805 + 1.806 + return NS_OK; 1.807 +} 1.808 + 1.809 +nsresult nsWebBrowserFind::OnFind(nsIDOMWindow *aFoundWindow) 1.810 +{ 1.811 + SetCurrentSearchFrame(aFoundWindow); 1.812 + 1.813 + // We don't want a selection to appear in two frames simultaneously 1.814 + nsCOMPtr<nsIDOMWindow> lastFocusedWindow = do_QueryReferent(mLastFocusedWindow); 1.815 + if (lastFocusedWindow && lastFocusedWindow != aFoundWindow) 1.816 + ClearFrameSelection(lastFocusedWindow); 1.817 + 1.818 + nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); 1.819 + if (fm) { 1.820 + // get the containing frame and focus it. For top-level windows, 1.821 + // the right window should already be focused. 1.822 + nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aFoundWindow)); 1.823 + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); 1.824 + 1.825 + nsCOMPtr<nsIDOMElement> frameElement = 1.826 + do_QueryInterface(window->GetFrameElementInternal()); 1.827 + if (frameElement) 1.828 + fm->SetFocus(frameElement, 0); 1.829 + 1.830 + mLastFocusedWindow = do_GetWeakReference(aFoundWindow); 1.831 + } 1.832 + 1.833 + return NS_OK; 1.834 +} 1.835 + 1.836 +/*--------------------------------------------------------------------------- 1.837 + 1.838 + GetDocShellFromWindow 1.839 + 1.840 + Utility method. This will always return nullptr if no docShell 1.841 + is returned. Oh why isn't there a better way to do this? 1.842 +----------------------------------------------------------------------------*/ 1.843 +nsIDocShell * 1.844 +nsWebBrowserFind::GetDocShellFromWindow(nsIDOMWindow *inWindow) 1.845 +{ 1.846 + nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(inWindow)); 1.847 + if (!window) return nullptr; 1.848 + 1.849 + return window->GetDocShell(); 1.850 +} 1.851 +