1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/embedding/components/find/src/nsFind.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1297 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +//#define DEBUG_FIND 1 1.10 + 1.11 +#include "nsFind.h" 1.12 +#include "nsContentCID.h" 1.13 +#include "nsIContent.h" 1.14 +#include "nsIDOMNode.h" 1.15 +#include "nsIDOMNodeList.h" 1.16 +#include "nsISelection.h" 1.17 +#include "nsISelectionController.h" 1.18 +#include "nsIFrame.h" 1.19 +#include "nsITextControlFrame.h" 1.20 +#include "nsIFormControl.h" 1.21 +#include "nsIEditor.h" 1.22 +#include "nsIPlaintextEditor.h" 1.23 +#include "nsTextFragment.h" 1.24 +#include "nsString.h" 1.25 +#include "nsIAtom.h" 1.26 +#include "nsServiceManagerUtils.h" 1.27 +#include "nsUnicharUtils.h" 1.28 +#include "nsIDOMElement.h" 1.29 +#include "nsIWordBreaker.h" 1.30 +#include "nsCRT.h" 1.31 +#include "nsRange.h" 1.32 +#include "nsContentUtils.h" 1.33 +#include "mozilla/DebugOnly.h" 1.34 + 1.35 +using namespace mozilla; 1.36 + 1.37 +// Yikes! Casting a char to unichar can fill with ones! 1.38 +#define CHAR_TO_UNICHAR(c) ((char16_t)(const unsigned char)c) 1.39 + 1.40 +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); 1.41 +static NS_DEFINE_CID(kCPreContentIteratorCID, NS_PRECONTENTITERATOR_CID); 1.42 + 1.43 +#define CH_QUOTE ((char16_t) 0x22) 1.44 +#define CH_APOSTROPHE ((char16_t) 0x27) 1.45 +#define CH_LEFT_SINGLE_QUOTE ((char16_t) 0x2018) 1.46 +#define CH_RIGHT_SINGLE_QUOTE ((char16_t) 0x2019) 1.47 +#define CH_LEFT_DOUBLE_QUOTE ((char16_t) 0x201C) 1.48 +#define CH_RIGHT_DOUBLE_QUOTE ((char16_t) 0x201D) 1.49 + 1.50 +#define CH_SHY ((char16_t) 0xAD) 1.51 + 1.52 +// nsFind::Find casts CH_SHY to char before calling StripChars 1.53 +// This works correctly if and only if CH_SHY <= 255 1.54 +PR_STATIC_ASSERT(CH_SHY <= 255); 1.55 + 1.56 +// ----------------------------------------------------------------------- 1.57 +// nsFindContentIterator is a special iterator that also goes through 1.58 +// any existing <textarea>'s or text <input>'s editor to lookup the 1.59 +// anonymous DOM content there. 1.60 +// 1.61 +// Details: 1.62 +// 1) We use two iterators: The "outer-iterator" goes through the 1.63 +// normal DOM. The "inner-iterator" goes through the anonymous DOM 1.64 +// inside the editor. 1.65 +// 1.66 +// 2) [MaybeSetupInnerIterator] As soon as the outer-iterator's current 1.67 +// node is changed, a check is made to see if the node is a <textarea> or 1.68 +// a text <input> node. If so, an inner-iterator is created to lookup the 1.69 +// anynomous contents of the editor underneath the text control. 1.70 +// 1.71 +// 3) When the inner-iterator is created, we position the outer-iterator 1.72 +// 'after' (or 'before' in backward search) the text control to avoid 1.73 +// revisiting that control. 1.74 +// 1.75 +// 4) As a consequence of searching through text controls, we can be 1.76 +// called via FindNext with the current selection inside a <textarea> 1.77 +// or a text <input>. This means that we can be given an initial search 1.78 +// range that stretches across the anonymous DOM and the normal DOM. To 1.79 +// cater for this situation, we split the anonymous part into the 1.80 +// inner-iterator and then reposition the outer-iterator outside. 1.81 +// 1.82 +// 5) The implementation assumes that First() and Next() are only called 1.83 +// in find-forward mode, while Last() and Prev() are used in find-backward. 1.84 + 1.85 +class nsFindContentIterator : public nsIContentIterator 1.86 +{ 1.87 +public: 1.88 + nsFindContentIterator(bool aFindBackward) 1.89 + : mStartOffset(0), 1.90 + mEndOffset(0), 1.91 + mFindBackward(aFindBackward) 1.92 + { 1.93 + } 1.94 + 1.95 + virtual ~nsFindContentIterator() 1.96 + { 1.97 + } 1.98 + 1.99 + // nsISupports 1.100 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.101 + NS_DECL_CYCLE_COLLECTION_CLASS(nsFindContentIterator) 1.102 + 1.103 + // nsIContentIterator 1.104 + virtual nsresult Init(nsINode* aRoot) 1.105 + { 1.106 + NS_NOTREACHED("internal error"); 1.107 + return NS_ERROR_NOT_IMPLEMENTED; 1.108 + } 1.109 + virtual nsresult Init(nsIDOMRange* aRange) 1.110 + { 1.111 + NS_NOTREACHED("internal error"); 1.112 + return NS_ERROR_NOT_IMPLEMENTED; 1.113 + } 1.114 + // Not a range because one of the endpoints may be anonymous. 1.115 + nsresult Init(nsIDOMNode* aStartNode, int32_t aStartOffset, 1.116 + nsIDOMNode* aEndNode, int32_t aEndOffset); 1.117 + virtual void First(); 1.118 + virtual void Last(); 1.119 + virtual void Next(); 1.120 + virtual void Prev(); 1.121 + virtual nsINode* GetCurrentNode(); 1.122 + virtual bool IsDone(); 1.123 + virtual nsresult PositionAt(nsINode* aCurNode); 1.124 + 1.125 +private: 1.126 + nsCOMPtr<nsIContentIterator> mOuterIterator; 1.127 + nsCOMPtr<nsIContentIterator> mInnerIterator; 1.128 + // Can't use a range here, since we want to represent part of the 1.129 + // flattened tree, including native anonymous content. 1.130 + nsCOMPtr<nsIDOMNode> mStartNode; 1.131 + int32_t mStartOffset; 1.132 + nsCOMPtr<nsIDOMNode> mEndNode; 1.133 + int32_t mEndOffset; 1.134 + 1.135 + nsCOMPtr<nsIContent> mStartOuterContent; 1.136 + nsCOMPtr<nsIContent> mEndOuterContent; 1.137 + bool mFindBackward; 1.138 + 1.139 + void Reset(); 1.140 + void MaybeSetupInnerIterator(); 1.141 + void SetupInnerIterator(nsIContent* aContent); 1.142 +}; 1.143 + 1.144 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFindContentIterator) 1.145 + NS_INTERFACE_MAP_ENTRY(nsIContentIterator) 1.146 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.147 +NS_INTERFACE_MAP_END 1.148 + 1.149 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFindContentIterator) 1.150 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFindContentIterator) 1.151 + 1.152 +NS_IMPL_CYCLE_COLLECTION(nsFindContentIterator, mOuterIterator, mInnerIterator, 1.153 + mStartOuterContent, mEndOuterContent, mEndNode, mStartNode) 1.154 + 1.155 + 1.156 +nsresult 1.157 +nsFindContentIterator::Init(nsIDOMNode* aStartNode, int32_t aStartOffset, 1.158 + nsIDOMNode* aEndNode, int32_t aEndOffset) 1.159 +{ 1.160 + NS_ENSURE_ARG_POINTER(aStartNode); 1.161 + NS_ENSURE_ARG_POINTER(aEndNode); 1.162 + if (!mOuterIterator) { 1.163 + if (mFindBackward) { 1.164 + // Use post-order in the reverse case, so we get parents 1.165 + // before children in case we want to prevent descending 1.166 + // into a node. 1.167 + mOuterIterator = do_CreateInstance(kCContentIteratorCID); 1.168 + } 1.169 + else { 1.170 + // Use pre-order in the forward case, so we get parents 1.171 + // before children in case we want to prevent descending 1.172 + // into a node. 1.173 + mOuterIterator = do_CreateInstance(kCPreContentIteratorCID); 1.174 + } 1.175 + NS_ENSURE_ARG_POINTER(mOuterIterator); 1.176 + } 1.177 + 1.178 + // Set up the search "range" that we will examine 1.179 + mStartNode = aStartNode; 1.180 + mStartOffset = aStartOffset; 1.181 + mEndNode = aEndNode; 1.182 + mEndOffset = aEndOffset; 1.183 + 1.184 + return NS_OK; 1.185 +} 1.186 + 1.187 +void 1.188 +nsFindContentIterator::First() 1.189 +{ 1.190 + Reset(); 1.191 +} 1.192 + 1.193 +void 1.194 +nsFindContentIterator::Last() 1.195 +{ 1.196 + Reset(); 1.197 +} 1.198 + 1.199 +void 1.200 +nsFindContentIterator::Next() 1.201 +{ 1.202 + if (mInnerIterator) { 1.203 + mInnerIterator->Next(); 1.204 + if (!mInnerIterator->IsDone()) 1.205 + return; 1.206 + 1.207 + // by construction, mOuterIterator is already on the next node 1.208 + } 1.209 + else { 1.210 + mOuterIterator->Next(); 1.211 + } 1.212 + MaybeSetupInnerIterator(); 1.213 +} 1.214 + 1.215 +void 1.216 +nsFindContentIterator::Prev() 1.217 +{ 1.218 + if (mInnerIterator) { 1.219 + mInnerIterator->Prev(); 1.220 + if (!mInnerIterator->IsDone()) 1.221 + return; 1.222 + 1.223 + // by construction, mOuterIterator is already on the previous node 1.224 + } 1.225 + else { 1.226 + mOuterIterator->Prev(); 1.227 + } 1.228 + MaybeSetupInnerIterator(); 1.229 +} 1.230 + 1.231 +nsINode* 1.232 +nsFindContentIterator::GetCurrentNode() 1.233 +{ 1.234 + if (mInnerIterator && !mInnerIterator->IsDone()) { 1.235 + return mInnerIterator->GetCurrentNode(); 1.236 + } 1.237 + return mOuterIterator->GetCurrentNode(); 1.238 +} 1.239 + 1.240 +bool 1.241 +nsFindContentIterator::IsDone() { 1.242 + if (mInnerIterator && !mInnerIterator->IsDone()) { 1.243 + return false; 1.244 + } 1.245 + return mOuterIterator->IsDone(); 1.246 +} 1.247 + 1.248 +nsresult 1.249 +nsFindContentIterator::PositionAt(nsINode* aCurNode) 1.250 +{ 1.251 + nsINode* oldNode = mOuterIterator->GetCurrentNode(); 1.252 + nsresult rv = mOuterIterator->PositionAt(aCurNode); 1.253 + if (NS_SUCCEEDED(rv)) { 1.254 + MaybeSetupInnerIterator(); 1.255 + } 1.256 + else { 1.257 + mOuterIterator->PositionAt(oldNode); 1.258 + if (mInnerIterator) 1.259 + rv = mInnerIterator->PositionAt(aCurNode); 1.260 + } 1.261 + return rv; 1.262 +} 1.263 + 1.264 +void 1.265 +nsFindContentIterator::Reset() 1.266 +{ 1.267 + mInnerIterator = nullptr; 1.268 + mStartOuterContent = nullptr; 1.269 + mEndOuterContent = nullptr; 1.270 + 1.271 + // As a consequence of searching through text controls, we may have been 1.272 + // initialized with a selection inside a <textarea> or a text <input>. 1.273 + 1.274 + // see if the start node is an anonymous text node inside a text control 1.275 + nsCOMPtr<nsIContent> startContent(do_QueryInterface(mStartNode)); 1.276 + if (startContent) { 1.277 + mStartOuterContent = startContent->FindFirstNonChromeOnlyAccessContent(); 1.278 + } 1.279 + 1.280 + // see if the end node is an anonymous text node inside a text control 1.281 + nsCOMPtr<nsIContent> endContent(do_QueryInterface(mEndNode)); 1.282 + if (endContent) { 1.283 + mEndOuterContent = endContent->FindFirstNonChromeOnlyAccessContent(); 1.284 + } 1.285 + 1.286 + // Note: OK to just set up the outer iterator here; if our range has a native 1.287 + // anonymous endpoint we'll end up setting up an inner iterator, and 1.288 + // reset the outer one in the process. 1.289 + nsCOMPtr<nsINode> node = do_QueryInterface(mStartNode); 1.290 + NS_ENSURE_TRUE_VOID(node); 1.291 + 1.292 + nsCOMPtr<nsIDOMRange> range = nsFind::CreateRange(node); 1.293 + range->SetStart(mStartNode, mStartOffset); 1.294 + range->SetEnd(mEndNode, mEndOffset); 1.295 + mOuterIterator->Init(range); 1.296 + 1.297 + if (!mFindBackward) { 1.298 + if (mStartOuterContent != startContent) { 1.299 + // the start node was an anonymous text node 1.300 + SetupInnerIterator(mStartOuterContent); 1.301 + if (mInnerIterator) 1.302 + mInnerIterator->First(); 1.303 + } 1.304 + if (!mOuterIterator->IsDone()) 1.305 + mOuterIterator->First(); 1.306 + } 1.307 + else { 1.308 + if (mEndOuterContent != endContent) { 1.309 + // the end node was an anonymous text node 1.310 + SetupInnerIterator(mEndOuterContent); 1.311 + if (mInnerIterator) 1.312 + mInnerIterator->Last(); 1.313 + } 1.314 + if (!mOuterIterator->IsDone()) 1.315 + mOuterIterator->Last(); 1.316 + } 1.317 + 1.318 + // if we didn't create an inner-iterator, the boundary node could still be 1.319 + // a text control, in which case we also need an inner-iterator straightaway 1.320 + if (!mInnerIterator) { 1.321 + MaybeSetupInnerIterator(); 1.322 + } 1.323 +} 1.324 + 1.325 +void 1.326 +nsFindContentIterator::MaybeSetupInnerIterator() 1.327 +{ 1.328 + mInnerIterator = nullptr; 1.329 + 1.330 + nsCOMPtr<nsIContent> content = 1.331 + do_QueryInterface(mOuterIterator->GetCurrentNode()); 1.332 + if (!content || !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) 1.333 + return; 1.334 + 1.335 + nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content)); 1.336 + if (!formControl->IsTextControl(true)) { 1.337 + return; 1.338 + } 1.339 + 1.340 + SetupInnerIterator(content); 1.341 + if (mInnerIterator) { 1.342 + if (!mFindBackward) { 1.343 + mInnerIterator->First(); 1.344 + // finish setup: position mOuterIterator on the actual "next" 1.345 + // node (this completes its re-init, @see SetupInnerIterator) 1.346 + if (!mOuterIterator->IsDone()) 1.347 + mOuterIterator->First(); 1.348 + } 1.349 + else { 1.350 + mInnerIterator->Last(); 1.351 + // finish setup: position mOuterIterator on the actual "previous" 1.352 + // node (this completes its re-init, @see SetupInnerIterator) 1.353 + if (!mOuterIterator->IsDone()) 1.354 + mOuterIterator->Last(); 1.355 + } 1.356 + } 1.357 +} 1.358 + 1.359 +void 1.360 +nsFindContentIterator::SetupInnerIterator(nsIContent* aContent) 1.361 +{ 1.362 + if (!aContent) { 1.363 + return; 1.364 + } 1.365 + NS_ASSERTION(!aContent->IsRootOfNativeAnonymousSubtree(), "invalid call"); 1.366 + 1.367 + nsITextControlFrame* tcFrame = do_QueryFrame(aContent->GetPrimaryFrame()); 1.368 + if (!tcFrame) 1.369 + return; 1.370 + 1.371 + nsCOMPtr<nsIEditor> editor; 1.372 + tcFrame->GetEditor(getter_AddRefs(editor)); 1.373 + if (!editor) 1.374 + return; 1.375 + 1.376 + // don't mess with disabled input fields 1.377 + uint32_t editorFlags = 0; 1.378 + editor->GetFlags(&editorFlags); 1.379 + if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask) 1.380 + return; 1.381 + 1.382 + nsCOMPtr<nsIDOMElement> rootElement; 1.383 + editor->GetRootElement(getter_AddRefs(rootElement)); 1.384 + 1.385 + nsCOMPtr<nsIDOMRange> innerRange = nsFind::CreateRange(aContent); 1.386 + nsCOMPtr<nsIDOMRange> outerRange = nsFind::CreateRange(aContent); 1.387 + if (!innerRange || !outerRange) { 1.388 + return; 1.389 + } 1.390 + 1.391 + // now create the inner-iterator 1.392 + mInnerIterator = do_CreateInstance(kCPreContentIteratorCID); 1.393 + 1.394 + if (mInnerIterator) { 1.395 + innerRange->SelectNodeContents(rootElement); 1.396 + 1.397 + // fix up the inner bounds, we may have to only lookup a portion 1.398 + // of the text control if the current node is a boundary point 1.399 + if (aContent == mStartOuterContent) { 1.400 + innerRange->SetStart(mStartNode, mStartOffset); 1.401 + } 1.402 + if (aContent == mEndOuterContent) { 1.403 + innerRange->SetEnd(mEndNode, mEndOffset); 1.404 + } 1.405 + // Note: we just init here. We do First() or Last() later. 1.406 + mInnerIterator->Init(innerRange); 1.407 + 1.408 + // make sure to place the outer-iterator outside 1.409 + // the text control so that we don't go there again. 1.410 + nsresult res1, res2; 1.411 + nsCOMPtr<nsIDOMNode> outerNode(do_QueryInterface(aContent)); 1.412 + if (!mFindBackward) { // find forward 1.413 + // cut the outer-iterator after the current node 1.414 + res1 = outerRange->SetEnd(mEndNode, mEndOffset); 1.415 + res2 = outerRange->SetStartAfter(outerNode); 1.416 + } 1.417 + else { // find backward 1.418 + // cut the outer-iterator before the current node 1.419 + res1 = outerRange->SetStart(mStartNode, mStartOffset); 1.420 + res2 = outerRange->SetEndBefore(outerNode); 1.421 + } 1.422 + if (NS_FAILED(res1) || NS_FAILED(res2)) { 1.423 + // we are done with the outer-iterator, the 1.424 + // inner-iterator will traverse what we want 1.425 + outerRange->Collapse(true); 1.426 + } 1.427 + 1.428 + // Note: we just re-init here, using the segment of our search range that 1.429 + // is yet to be visited. Thus when we later do mOuterIterator->First() [or 1.430 + // mOuterIterator->Last()], we will effectively be on the next node [or 1.431 + // the previous node] _with respect to_ the search range. 1.432 + mOuterIterator->Init(outerRange); 1.433 + } 1.434 +} 1.435 + 1.436 +nsresult 1.437 +NS_NewFindContentIterator(bool aFindBackward, 1.438 + nsIContentIterator** aResult) 1.439 +{ 1.440 + NS_ENSURE_ARG_POINTER(aResult); 1.441 + if (!aResult) { 1.442 + return NS_ERROR_NULL_POINTER; 1.443 + } 1.444 + 1.445 + nsFindContentIterator* it = new nsFindContentIterator(aFindBackward); 1.446 + if (!it) { 1.447 + return NS_ERROR_OUT_OF_MEMORY; 1.448 + } 1.449 + return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void **)aResult); 1.450 +} 1.451 +// -------------------------------------------------------------------- 1.452 + 1.453 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFind) 1.454 + NS_INTERFACE_MAP_ENTRY(nsIFind) 1.455 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.456 +NS_INTERFACE_MAP_END 1.457 + 1.458 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFind) 1.459 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFind) 1.460 + 1.461 + NS_IMPL_CYCLE_COLLECTION(nsFind, mLastBlockParent, mIterNode, mIterator) 1.462 + 1.463 +nsFind::nsFind() 1.464 + : mFindBackward(false) 1.465 + , mCaseSensitive(false) 1.466 + , mIterOffset(0) 1.467 +{ 1.468 +} 1.469 + 1.470 +nsFind::~nsFind() 1.471 +{ 1.472 +} 1.473 + 1.474 +#ifdef DEBUG_FIND 1.475 +static void DumpNode(nsIDOMNode* aNode) 1.476 +{ 1.477 + if (!aNode) 1.478 + { 1.479 + printf(">>>> Node: NULL\n"); 1.480 + return; 1.481 + } 1.482 + nsAutoString nodeName; 1.483 + aNode->GetNodeName(nodeName); 1.484 + nsCOMPtr<nsIContent> textContent (do_QueryInterface(aNode)); 1.485 + if (textContent && textContent->IsNodeOfType(nsINode::eTEXT)) 1.486 + { 1.487 + nsAutoString newText; 1.488 + textContent->AppendTextTo(newText); 1.489 + printf(">>>> Text node (node name %s): '%s'\n", 1.490 + NS_LossyConvertUTF16toASCII(nodeName).get(), 1.491 + NS_LossyConvertUTF16toASCII(newText).get()); 1.492 + } 1.493 + else 1.494 + printf(">>>> Node: %s\n", NS_LossyConvertUTF16toASCII(nodeName).get()); 1.495 +} 1.496 +#endif 1.497 + 1.498 +nsresult 1.499 +nsFind::InitIterator(nsIDOMNode* aStartNode, int32_t aStartOffset, 1.500 + nsIDOMNode* aEndNode, int32_t aEndOffset) 1.501 +{ 1.502 + if (!mIterator) 1.503 + { 1.504 + mIterator = new nsFindContentIterator(mFindBackward); 1.505 + NS_ENSURE_TRUE(mIterator, NS_ERROR_OUT_OF_MEMORY); 1.506 + } 1.507 + 1.508 + NS_ENSURE_ARG_POINTER(aStartNode); 1.509 + NS_ENSURE_ARG_POINTER(aEndNode); 1.510 + 1.511 +#ifdef DEBUG_FIND 1.512 + printf("InitIterator search range:\n"); 1.513 + printf(" -- start %d, ", aStartOffset); DumpNode(aStartNode); 1.514 + printf(" -- end %d, ", aEndOffset); DumpNode(aEndNode); 1.515 +#endif 1.516 + 1.517 + nsresult rv = 1.518 + mIterator->Init(aStartNode, aStartOffset, aEndNode, aEndOffset); 1.519 + NS_ENSURE_SUCCESS(rv, rv); 1.520 + if (mFindBackward) { 1.521 + mIterator->Last(); 1.522 + } 1.523 + else { 1.524 + mIterator->First(); 1.525 + } 1.526 + return NS_OK; 1.527 +} 1.528 + 1.529 +/* attribute boolean findBackward; */ 1.530 +NS_IMETHODIMP 1.531 +nsFind::GetFindBackwards(bool *aFindBackward) 1.532 +{ 1.533 + if (!aFindBackward) 1.534 + return NS_ERROR_NULL_POINTER; 1.535 + 1.536 + *aFindBackward = mFindBackward; 1.537 + return NS_OK; 1.538 +} 1.539 +NS_IMETHODIMP 1.540 +nsFind::SetFindBackwards(bool aFindBackward) 1.541 +{ 1.542 + mFindBackward = aFindBackward; 1.543 + return NS_OK; 1.544 +} 1.545 + 1.546 +/* attribute boolean caseSensitive; */ 1.547 +NS_IMETHODIMP 1.548 +nsFind::GetCaseSensitive(bool *aCaseSensitive) 1.549 +{ 1.550 + if (!aCaseSensitive) 1.551 + return NS_ERROR_NULL_POINTER; 1.552 + 1.553 + *aCaseSensitive = mCaseSensitive; 1.554 + return NS_OK; 1.555 +} 1.556 +NS_IMETHODIMP 1.557 +nsFind::SetCaseSensitive(bool aCaseSensitive) 1.558 +{ 1.559 + mCaseSensitive = aCaseSensitive; 1.560 + return NS_OK; 1.561 +} 1.562 + 1.563 +NS_IMETHODIMP 1.564 +nsFind::GetWordBreaker(nsIWordBreaker** aWordBreaker) 1.565 +{ 1.566 + *aWordBreaker = mWordBreaker; 1.567 + NS_IF_ADDREF(*aWordBreaker); 1.568 + return NS_OK; 1.569 +} 1.570 + 1.571 +NS_IMETHODIMP 1.572 +nsFind::SetWordBreaker(nsIWordBreaker* aWordBreaker) 1.573 +{ 1.574 + mWordBreaker = aWordBreaker; 1.575 + return NS_OK; 1.576 +} 1.577 + 1.578 +// 1.579 +// Here begins the find code. 1.580 +// A ten-thousand-foot view of how it works: 1.581 +// Find needs to be able to compare across inline (but not block) nodes, 1.582 +// e.g. find for "abc" should match a<b>b</b>c. 1.583 +// So after we've searched a node, we're not done with it; 1.584 +// in the case of a partial match we may need to reset the 1.585 +// iterator to go back to a previously visited node, 1.586 +// so we always save the "match anchor" node and offset. 1.587 +// 1.588 +// Text nodes store their text in an nsTextFragment, which is 1.589 +// effectively a union of a one-byte string or a two-byte string. 1.590 +// Single and double strings are intermixed in the dom. 1.591 +// We don't have string classes which can deal with intermixed strings, 1.592 +// so all the handling is done explicitly here. 1.593 +// 1.594 + 1.595 +nsresult 1.596 +nsFind::NextNode(nsIDOMRange* aSearchRange, 1.597 + nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint, 1.598 + bool aContinueOk) 1.599 +{ 1.600 + nsresult rv; 1.601 + 1.602 + nsCOMPtr<nsIContent> content; 1.603 + 1.604 + if (!mIterator || aContinueOk) 1.605 + { 1.606 + // If we are continuing, that means we have a match in progress. 1.607 + // In that case, we want to continue from the end point 1.608 + // (where we are now) to the beginning/end of the search range. 1.609 + nsCOMPtr<nsIDOMNode> startNode; 1.610 + nsCOMPtr<nsIDOMNode> endNode; 1.611 + int32_t startOffset, endOffset; 1.612 + if (aContinueOk) 1.613 + { 1.614 +#ifdef DEBUG_FIND 1.615 + printf("Match in progress: continuing past endpoint\n"); 1.616 +#endif 1.617 + if (mFindBackward) { 1.618 + aSearchRange->GetStartContainer(getter_AddRefs(startNode)); 1.619 + aSearchRange->GetStartOffset(&startOffset); 1.620 + aEndPoint->GetStartContainer(getter_AddRefs(endNode)); 1.621 + aEndPoint->GetStartOffset(&endOffset); 1.622 + } else { // forward 1.623 + aEndPoint->GetEndContainer(getter_AddRefs(startNode)); 1.624 + aEndPoint->GetEndOffset(&startOffset); 1.625 + aSearchRange->GetEndContainer(getter_AddRefs(endNode)); 1.626 + aSearchRange->GetEndOffset(&endOffset); 1.627 + } 1.628 + } 1.629 + else // Normal, not continuing 1.630 + { 1.631 + if (mFindBackward) { 1.632 + aSearchRange->GetStartContainer(getter_AddRefs(startNode)); 1.633 + aSearchRange->GetStartOffset(&startOffset); 1.634 + aStartPoint->GetEndContainer(getter_AddRefs(endNode)); 1.635 + aStartPoint->GetEndOffset(&endOffset); 1.636 + // XXX Needs work: 1.637 + // Problem with this approach: if there is a match which starts 1.638 + // just before the current selection and continues into the selection, 1.639 + // we will miss it, because our search algorithm only starts 1.640 + // searching from the end of the word, so we would have to 1.641 + // search the current selection but discount any matches 1.642 + // that fall entirely inside it. 1.643 + } else { // forward 1.644 + aStartPoint->GetStartContainer(getter_AddRefs(startNode)); 1.645 + aStartPoint->GetStartOffset(&startOffset); 1.646 + aEndPoint->GetEndContainer(getter_AddRefs(endNode)); 1.647 + aEndPoint->GetEndOffset(&endOffset); 1.648 + } 1.649 + } 1.650 + 1.651 + rv = InitIterator(startNode, startOffset, endNode, endOffset); 1.652 + NS_ENSURE_SUCCESS(rv, rv); 1.653 + if (!aStartPoint) 1.654 + aStartPoint = aSearchRange; 1.655 + 1.656 + content = do_QueryInterface(mIterator->GetCurrentNode()); 1.657 +#ifdef DEBUG_FIND 1.658 + nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content)); 1.659 + printf(":::::: Got the first node "); DumpNode(dnode); 1.660 +#endif 1.661 + if (content && content->IsNodeOfType(nsINode::eTEXT) && 1.662 + !SkipNode(content)) 1.663 + { 1.664 + mIterNode = do_QueryInterface(content); 1.665 + // Also set mIterOffset if appropriate: 1.666 + nsCOMPtr<nsIDOMNode> node; 1.667 + if (mFindBackward) { 1.668 + aStartPoint->GetEndContainer(getter_AddRefs(node)); 1.669 + if (mIterNode.get() == node.get()) 1.670 + aStartPoint->GetEndOffset(&mIterOffset); 1.671 + else 1.672 + mIterOffset = -1; // sign to start from end 1.673 + } 1.674 + else 1.675 + { 1.676 + aStartPoint->GetStartContainer(getter_AddRefs(node)); 1.677 + if (mIterNode.get() == node.get()) 1.678 + aStartPoint->GetStartOffset(&mIterOffset); 1.679 + else 1.680 + mIterOffset = 0; 1.681 + } 1.682 +#ifdef DEBUG_FIND 1.683 + printf("Setting initial offset to %d\n", mIterOffset); 1.684 +#endif 1.685 + return NS_OK; 1.686 + } 1.687 + } 1.688 + 1.689 + while (1) 1.690 + { 1.691 + if (mFindBackward) 1.692 + mIterator->Prev(); 1.693 + else 1.694 + mIterator->Next(); 1.695 + 1.696 + content = do_QueryInterface(mIterator->GetCurrentNode()); 1.697 + if (!content) 1.698 + break; 1.699 + 1.700 +#ifdef DEBUG_FIND 1.701 + nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content)); 1.702 + printf(":::::: Got another node "); DumpNode(dnode); 1.703 +#endif 1.704 + 1.705 + // If we ever cross a block node, we might want to reset 1.706 + // the match anchor: 1.707 + // we don't match patterns extending across block boundaries. 1.708 + // But we can't depend on this test here now, because the iterator 1.709 + // doesn't give us the parent going in and going out, and we 1.710 + // need it both times to depend on this. 1.711 + //if (IsBlockNode(content)) 1.712 + 1.713 + // Now see if we need to skip this node -- 1.714 + // e.g. is it part of a script or other invisible node? 1.715 + // Note that we don't ask for CSS information; 1.716 + // a node can be invisible due to CSS, and we'd still find it. 1.717 + if (SkipNode(content)) 1.718 + continue; 1.719 + 1.720 + if (content->IsNodeOfType(nsINode::eTEXT)) 1.721 + break; 1.722 +#ifdef DEBUG_FIND 1.723 + dnode = do_QueryInterface(content); 1.724 + printf("Not a text node: "); DumpNode(dnode); 1.725 +#endif 1.726 + } 1.727 + 1.728 + if (content) 1.729 + mIterNode = do_QueryInterface(content); 1.730 + else 1.731 + mIterNode = nullptr; 1.732 + mIterOffset = -1; 1.733 + 1.734 +#ifdef DEBUG_FIND 1.735 + printf("Iterator gave: "); DumpNode(mIterNode); 1.736 +#endif 1.737 + return NS_OK; 1.738 +} 1.739 + 1.740 +bool nsFind::IsBlockNode(nsIContent* aContent) 1.741 +{ 1.742 + if (!aContent->IsHTML()) { 1.743 + return false; 1.744 + } 1.745 + 1.746 + nsIAtom *atom = aContent->Tag(); 1.747 + 1.748 + if (atom == nsGkAtoms::img || 1.749 + atom == nsGkAtoms::hr || 1.750 + atom == nsGkAtoms::th || 1.751 + atom == nsGkAtoms::td) 1.752 + return true; 1.753 + 1.754 + return nsContentUtils::IsHTMLBlock(atom); 1.755 +} 1.756 + 1.757 +bool nsFind::IsTextNode(nsIDOMNode* aNode) 1.758 +{ 1.759 + uint16_t nodeType; 1.760 + aNode->GetNodeType(&nodeType); 1.761 + 1.762 + return nodeType == nsIDOMNode::TEXT_NODE || 1.763 + nodeType == nsIDOMNode::CDATA_SECTION_NODE; 1.764 +} 1.765 + 1.766 +bool nsFind::IsVisibleNode(nsIDOMNode *aDOMNode) 1.767 +{ 1.768 + nsCOMPtr<nsIContent> content(do_QueryInterface(aDOMNode)); 1.769 + if (!content) 1.770 + return false; 1.771 + 1.772 + nsIFrame *frame = content->GetPrimaryFrame(); 1.773 + if (!frame) { 1.774 + // No frame! Not visible then. 1.775 + return false; 1.776 + } 1.777 + 1.778 + return frame->StyleVisibility()->IsVisible(); 1.779 +} 1.780 + 1.781 +bool nsFind::SkipNode(nsIContent* aContent) 1.782 +{ 1.783 + nsIAtom *atom; 1.784 + 1.785 +#ifdef HAVE_BIDI_ITERATOR 1.786 + atom = aContent->Tag(); 1.787 + 1.788 + // We may not need to skip comment nodes, 1.789 + // now that IsTextNode distinguishes them from real text nodes. 1.790 + return (aContent->IsNodeOfType(nsINode::eCOMMENT) || 1.791 + (aContent->IsHTML() && 1.792 + (atom == sScriptAtom || 1.793 + atom == sNoframesAtom || 1.794 + atom == sSelectAtom))); 1.795 + 1.796 +#else /* HAVE_BIDI_ITERATOR */ 1.797 + // Temporary: eventually we will have an iterator to do this, 1.798 + // but for now, we have to climb up the tree for each node 1.799 + // and see whether any parent is a skipped node, 1.800 + // and take the performance hit. 1.801 + 1.802 + nsIContent *content = aContent; 1.803 + while (content) 1.804 + { 1.805 + atom = content->Tag(); 1.806 + 1.807 + if (aContent->IsNodeOfType(nsINode::eCOMMENT) || 1.808 + (content->IsHTML() && 1.809 + (atom == nsGkAtoms::script || 1.810 + atom == nsGkAtoms::noframes || 1.811 + atom == nsGkAtoms::select))) 1.812 + { 1.813 +#ifdef DEBUG_FIND 1.814 + printf("Skipping node: "); 1.815 + nsCOMPtr<nsIDOMNode> node (do_QueryInterface(content)); 1.816 + DumpNode(node); 1.817 +#endif 1.818 + 1.819 + return true; 1.820 + } 1.821 + 1.822 + // Only climb to the nearest block node 1.823 + if (IsBlockNode(content)) 1.824 + return false; 1.825 + 1.826 + content = content->GetParent(); 1.827 + } 1.828 + 1.829 + return false; 1.830 +#endif /* HAVE_BIDI_ITERATOR */ 1.831 +} 1.832 + 1.833 +nsresult nsFind::GetBlockParent(nsIDOMNode* aNode, nsIDOMNode** aParent) 1.834 +{ 1.835 + while (aNode) 1.836 + { 1.837 + nsCOMPtr<nsIDOMNode> parent; 1.838 + nsresult rv = aNode->GetParentNode(getter_AddRefs(parent)); 1.839 + NS_ENSURE_SUCCESS(rv, rv); 1.840 + nsCOMPtr<nsIContent> content (do_QueryInterface(parent)); 1.841 + if (content && IsBlockNode(content)) 1.842 + { 1.843 + *aParent = parent; 1.844 + NS_ADDREF(*aParent); 1.845 + return NS_OK; 1.846 + } 1.847 + aNode = parent; 1.848 + } 1.849 + return NS_ERROR_FAILURE; 1.850 +} 1.851 + 1.852 +// Call ResetAll before returning, 1.853 +// to remove all references to external objects. 1.854 +void nsFind::ResetAll() 1.855 +{ 1.856 + mIterator = nullptr; 1.857 + mLastBlockParent = nullptr; 1.858 +} 1.859 + 1.860 +#define NBSP_CHARCODE (CHAR_TO_UNICHAR(160)) 1.861 +#define IsSpace(c) (nsCRT::IsAsciiSpace(c) || (c) == NBSP_CHARCODE) 1.862 +#define OVERFLOW_PINDEX (mFindBackward ? pindex < 0 : pindex > patLen) 1.863 +#define DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen) 1.864 +#define ALMOST_DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen-1) 1.865 + 1.866 +// 1.867 +// Find: 1.868 +// Take nodes out of the tree with NextNode, 1.869 +// until null (NextNode will return 0 at the end of our range). 1.870 +// 1.871 +NS_IMETHODIMP 1.872 +nsFind::Find(const char16_t *aPatText, nsIDOMRange* aSearchRange, 1.873 + nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint, 1.874 + nsIDOMRange** aRangeRet) 1.875 +{ 1.876 +#ifdef DEBUG_FIND 1.877 + printf("============== nsFind::Find('%s'%s, %p, %p, %p)\n", 1.878 + NS_LossyConvertUTF16toASCII(aPatText).get(), 1.879 + mFindBackward ? " (backward)" : " (forward)", 1.880 + (void*)aSearchRange, (void*)aStartPoint, (void*)aEndPoint); 1.881 +#endif 1.882 + 1.883 + NS_ENSURE_ARG(aSearchRange); 1.884 + NS_ENSURE_ARG(aStartPoint); 1.885 + NS_ENSURE_ARG(aEndPoint); 1.886 + NS_ENSURE_ARG_POINTER(aRangeRet); 1.887 + *aRangeRet = 0; 1.888 + 1.889 + if (!aPatText) 1.890 + return NS_ERROR_NULL_POINTER; 1.891 + 1.892 + ResetAll(); 1.893 + 1.894 + nsAutoString patAutoStr(aPatText); 1.895 + if (!mCaseSensitive) 1.896 + ToLowerCase(patAutoStr); 1.897 + 1.898 + // Ignore soft hyphens in the pattern 1.899 + static const char kShy[] = { char(CH_SHY), 0 }; 1.900 + patAutoStr.StripChars(kShy); 1.901 + 1.902 + const char16_t* patStr = patAutoStr.get(); 1.903 + int32_t patLen = patAutoStr.Length() - 1; 1.904 + 1.905 + // current offset into the pattern -- reset to beginning/end: 1.906 + int32_t pindex = (mFindBackward ? patLen : 0); 1.907 + 1.908 + // Current offset into the fragment 1.909 + int32_t findex = 0; 1.910 + 1.911 + // Direction to move pindex and ptr* 1.912 + int incr = (mFindBackward ? -1 : 1); 1.913 + 1.914 + nsCOMPtr<nsIContent> tc; 1.915 + const nsTextFragment *frag = nullptr; 1.916 + int32_t fragLen = 0; 1.917 + 1.918 + // Pointers into the current fragment: 1.919 + const char16_t *t2b = nullptr; 1.920 + const char *t1b = nullptr; 1.921 + 1.922 + // Keep track of when we're in whitespace: 1.923 + // (only matters when we're matching) 1.924 + bool inWhitespace = false; 1.925 + 1.926 + // Place to save the range start point in case we find a match: 1.927 + nsCOMPtr<nsIDOMNode> matchAnchorNode; 1.928 + int32_t matchAnchorOffset = 0; 1.929 + 1.930 + // Get the end point, so we know when to end searches: 1.931 + nsCOMPtr<nsIDOMNode> endNode; 1.932 + int32_t endOffset; 1.933 + aEndPoint->GetEndContainer(getter_AddRefs(endNode)); 1.934 + aEndPoint->GetEndOffset(&endOffset); 1.935 + 1.936 + char16_t prevChar = 0; 1.937 + while (1) 1.938 + { 1.939 +#ifdef DEBUG_FIND 1.940 + printf("Loop ...\n"); 1.941 +#endif 1.942 + 1.943 + // If this is our first time on a new node, reset the pointers: 1.944 + if (!frag) 1.945 + { 1.946 + 1.947 + tc = nullptr; 1.948 + NextNode(aSearchRange, aStartPoint, aEndPoint, false); 1.949 + if (!mIterNode) // Out of nodes 1.950 + { 1.951 + // Are we in the middle of a match? 1.952 + // If so, try again with continuation. 1.953 + if (matchAnchorNode) 1.954 + NextNode(aSearchRange, aStartPoint, aEndPoint, true); 1.955 + 1.956 + // Reset the iterator, so this nsFind will be usable if 1.957 + // the user wants to search again (from beginning/end). 1.958 + ResetAll(); 1.959 + return NS_OK; 1.960 + } 1.961 + 1.962 + // We have a new text content. If its block parent is different 1.963 + // from the block parent of the last text content, then we 1.964 + // need to clear the match since we don't want to find 1.965 + // across block boundaries. 1.966 + nsCOMPtr<nsIDOMNode> blockParent; 1.967 + GetBlockParent(mIterNode, getter_AddRefs(blockParent)); 1.968 +#ifdef DEBUG_FIND 1.969 + printf("New node: old blockparent = %p, new = %p\n", 1.970 + (void*)mLastBlockParent.get(), (void*)blockParent.get()); 1.971 +#endif 1.972 + if (blockParent != mLastBlockParent) 1.973 + { 1.974 +#ifdef DEBUG_FIND 1.975 + printf("Different block parent!\n"); 1.976 +#endif 1.977 + mLastBlockParent = blockParent; 1.978 + // End any pending match: 1.979 + matchAnchorNode = nullptr; 1.980 + matchAnchorOffset = 0; 1.981 + pindex = (mFindBackward ? patLen : 0); 1.982 + inWhitespace = false; 1.983 + } 1.984 + 1.985 + // Get the text content: 1.986 + tc = do_QueryInterface(mIterNode); 1.987 + if (!tc || !(frag = tc->GetText())) // Out of nodes 1.988 + { 1.989 + mIterator = nullptr; 1.990 + mLastBlockParent = 0; 1.991 + ResetAll(); 1.992 + return NS_OK; 1.993 + } 1.994 + 1.995 + fragLen = frag->GetLength(); 1.996 + 1.997 + // Set our starting point in this node. 1.998 + // If we're going back to the anchor node, which means that we 1.999 + // just ended a partial match, use the saved offset: 1.1000 + if (mIterNode == matchAnchorNode) 1.1001 + findex = matchAnchorOffset + (mFindBackward ? 1 : 0); 1.1002 + 1.1003 + // mIterOffset, if set, is the range's idea of an offset, 1.1004 + // and points between characters. But when translated 1.1005 + // to a string index, it points to a character. If we're 1.1006 + // going backward, this is one character too late and 1.1007 + // we'll match part of our previous pattern. 1.1008 + else if (mIterOffset >= 0) 1.1009 + findex = mIterOffset - (mFindBackward ? 1 : 0); 1.1010 + 1.1011 + // Otherwise, just start at the appropriate end of the fragment: 1.1012 + else if (mFindBackward) 1.1013 + findex = fragLen - 1; 1.1014 + else 1.1015 + findex = 0; 1.1016 + 1.1017 + // Offset can only apply to the first node: 1.1018 + mIterOffset = -1; 1.1019 + 1.1020 + // If this is outside the bounds of the string, then skip this node: 1.1021 + if (findex < 0 || findex > fragLen-1) 1.1022 + { 1.1023 +#ifdef DEBUG_FIND 1.1024 + printf("At the end of a text node -- skipping to the next\n"); 1.1025 +#endif 1.1026 + frag = 0; 1.1027 + continue; 1.1028 + } 1.1029 + 1.1030 +#ifdef DEBUG_FIND 1.1031 + printf("Starting from offset %d\n", findex); 1.1032 +#endif 1.1033 + if (frag->Is2b()) 1.1034 + { 1.1035 + t2b = frag->Get2b(); 1.1036 + t1b = nullptr; 1.1037 +#ifdef DEBUG_FIND 1.1038 + nsAutoString str2(t2b, fragLen); 1.1039 + printf("2 byte, '%s'\n", NS_LossyConvertUTF16toASCII(str2).get()); 1.1040 +#endif 1.1041 + } 1.1042 + else 1.1043 + { 1.1044 + t1b = frag->Get1b(); 1.1045 + t2b = nullptr; 1.1046 +#ifdef DEBUG_FIND 1.1047 + nsAutoCString str1(t1b, fragLen); 1.1048 + printf("1 byte, '%s'\n", str1.get()); 1.1049 +#endif 1.1050 + } 1.1051 + } 1.1052 + else // still on the old node 1.1053 + { 1.1054 + // Still on the old node. Advance the pointers, 1.1055 + // then see if we need to pull a new node. 1.1056 + findex += incr; 1.1057 +#ifdef DEBUG_FIND 1.1058 + printf("Same node -- (%d, %d)\n", pindex, findex); 1.1059 +#endif 1.1060 + if (mFindBackward ? (findex < 0) : (findex >= fragLen)) 1.1061 + { 1.1062 +#ifdef DEBUG_FIND 1.1063 + printf("Will need to pull a new node: mAO = %d, frag len=%d\n", 1.1064 + matchAnchorOffset, fragLen); 1.1065 +#endif 1.1066 + // Done with this node. Pull a new one. 1.1067 + frag = nullptr; 1.1068 + continue; 1.1069 + } 1.1070 + } 1.1071 + 1.1072 + // Have we gone past the endpoint yet? 1.1073 + // If we have, and we're not in the middle of a match, return. 1.1074 + if (mIterNode == endNode && 1.1075 + ((mFindBackward && (findex < endOffset)) || 1.1076 + (!mFindBackward && (findex > endOffset)))) 1.1077 + { 1.1078 + ResetAll(); 1.1079 + return NS_OK; 1.1080 + } 1.1081 + 1.1082 + // The two characters we'll be comparing: 1.1083 + char16_t c = (t2b ? t2b[findex] : CHAR_TO_UNICHAR(t1b[findex])); 1.1084 + char16_t patc = patStr[pindex]; 1.1085 + 1.1086 +#ifdef DEBUG_FIND 1.1087 + printf("Comparing '%c'=%x to '%c' (%d of %d), findex=%d%s\n", 1.1088 + (char)c, (int)c, patc, pindex, patLen, findex, 1.1089 + inWhitespace ? " (inWhitespace)" : ""); 1.1090 +#endif 1.1091 + 1.1092 + // Do we need to go back to non-whitespace mode? 1.1093 + // If inWhitespace, then this space in the pat str 1.1094 + // has already matched at least one space in the document. 1.1095 + if (inWhitespace && !IsSpace(c)) 1.1096 + { 1.1097 + inWhitespace = false; 1.1098 + pindex += incr; 1.1099 +#ifdef DEBUG 1.1100 + // This shouldn't happen -- if we were still matching, and we 1.1101 + // were at the end of the pat string, then we should have 1.1102 + // caught it in the last iteration and returned success. 1.1103 + if (OVERFLOW_PINDEX) 1.1104 + NS_ASSERTION(false, "Missed a whitespace match"); 1.1105 +#endif 1.1106 + patc = patStr[pindex]; 1.1107 + } 1.1108 + if (!inWhitespace && IsSpace(patc)) 1.1109 + inWhitespace = true; 1.1110 + 1.1111 + // convert to lower case if necessary 1.1112 + else if (!inWhitespace && !mCaseSensitive && IsUpperCase(c)) 1.1113 + c = ToLowerCase(c); 1.1114 + 1.1115 + switch (c) { 1.1116 + // ignore soft hyphens in the document 1.1117 + case CH_SHY: 1.1118 + continue; 1.1119 + // treat curly and straight quotes as identical 1.1120 + case CH_LEFT_SINGLE_QUOTE: 1.1121 + case CH_RIGHT_SINGLE_QUOTE: 1.1122 + c = CH_APOSTROPHE; 1.1123 + break; 1.1124 + case CH_LEFT_DOUBLE_QUOTE: 1.1125 + case CH_RIGHT_DOUBLE_QUOTE: 1.1126 + c = CH_QUOTE; 1.1127 + break; 1.1128 + } 1.1129 + 1.1130 + switch (patc) { 1.1131 + // treat curly and straight quotes as identical 1.1132 + case CH_LEFT_SINGLE_QUOTE: 1.1133 + case CH_RIGHT_SINGLE_QUOTE: 1.1134 + patc = CH_APOSTROPHE; 1.1135 + break; 1.1136 + case CH_LEFT_DOUBLE_QUOTE: 1.1137 + case CH_RIGHT_DOUBLE_QUOTE: 1.1138 + patc = CH_QUOTE; 1.1139 + break; 1.1140 + } 1.1141 + 1.1142 + // a '\n' between CJ characters is ignored 1.1143 + if (pindex != (mFindBackward ? patLen : 0) && c != patc && !inWhitespace) { 1.1144 + if (c == '\n' && t2b && IS_CJ_CHAR(prevChar)) { 1.1145 + int32_t nindex = findex + incr; 1.1146 + if (mFindBackward ? (nindex >= 0) : (nindex < fragLen)) { 1.1147 + if (IS_CJ_CHAR(t2b[nindex])) 1.1148 + continue; 1.1149 + } 1.1150 + } 1.1151 + } 1.1152 + 1.1153 + // Compare 1.1154 + if ((c == patc) || (inWhitespace && IsSpace(c))) 1.1155 + { 1.1156 + prevChar = c; 1.1157 +#ifdef DEBUG_FIND 1.1158 + if (inWhitespace) 1.1159 + printf("YES (whitespace)(%d of %d)\n", pindex, patLen); 1.1160 + else 1.1161 + printf("YES! '%c' == '%c' (%d of %d)\n", c, patc, pindex, patLen); 1.1162 +#endif 1.1163 + 1.1164 + // Save the range anchors if we haven't already: 1.1165 + if (!matchAnchorNode) { 1.1166 + matchAnchorNode = mIterNode; 1.1167 + matchAnchorOffset = findex; 1.1168 + } 1.1169 + 1.1170 + // Are we done? 1.1171 + if (DONE_WITH_PINDEX) 1.1172 + // Matched the whole string! 1.1173 + { 1.1174 +#ifdef DEBUG_FIND 1.1175 + printf("Found a match!\n"); 1.1176 +#endif 1.1177 + 1.1178 + // Make the range: 1.1179 + nsCOMPtr<nsIDOMNode> startParent; 1.1180 + nsCOMPtr<nsIDOMNode> endParent; 1.1181 + nsCOMPtr<nsIDOMRange> range = CreateRange(tc); 1.1182 + if (range) 1.1183 + { 1.1184 + int32_t matchStartOffset, matchEndOffset; 1.1185 + // convert char index to range point: 1.1186 + int32_t mao = matchAnchorOffset + (mFindBackward ? 1 : 0); 1.1187 + if (mFindBackward) 1.1188 + { 1.1189 + startParent = do_QueryInterface(tc); 1.1190 + endParent = matchAnchorNode; 1.1191 + matchStartOffset = findex; 1.1192 + matchEndOffset = mao; 1.1193 + } 1.1194 + else 1.1195 + { 1.1196 + startParent = matchAnchorNode; 1.1197 + endParent = do_QueryInterface(tc); 1.1198 + matchStartOffset = mao; 1.1199 + matchEndOffset = findex+1; 1.1200 + } 1.1201 + if (startParent && endParent && 1.1202 + IsVisibleNode(startParent) && IsVisibleNode(endParent)) 1.1203 + { 1.1204 + range->SetStart(startParent, matchStartOffset); 1.1205 + range->SetEnd(endParent, matchEndOffset); 1.1206 + *aRangeRet = range.get(); 1.1207 + NS_ADDREF(*aRangeRet); 1.1208 + } 1.1209 + else { 1.1210 + startParent = nullptr; // This match is no good -- invisible or bad range 1.1211 + } 1.1212 + } 1.1213 + 1.1214 + if (startParent) { 1.1215 + // If startParent == nullptr, we didn't successfully make range 1.1216 + // or, we didn't make a range because the start or end node were invisible 1.1217 + // Reset the offset to the other end of the found string: 1.1218 + mIterOffset = findex + (mFindBackward ? 1 : 0); 1.1219 + #ifdef DEBUG_FIND 1.1220 + printf("mIterOffset = %d, mIterNode = ", mIterOffset); 1.1221 + DumpNode(mIterNode); 1.1222 + #endif 1.1223 + 1.1224 + ResetAll(); 1.1225 + return NS_OK; 1.1226 + } 1.1227 + matchAnchorNode = nullptr; // This match is no good, continue on in document 1.1228 + } 1.1229 + 1.1230 + if (matchAnchorNode) { 1.1231 + // Not done, but still matching. 1.1232 + // Advance and loop around for the next characters. 1.1233 + // But don't advance from a space to a non-space: 1.1234 + if (!inWhitespace || DONE_WITH_PINDEX || IsSpace(patStr[pindex+incr])) 1.1235 + { 1.1236 + pindex += incr; 1.1237 + inWhitespace = false; 1.1238 +#ifdef DEBUG_FIND 1.1239 + printf("Advancing pindex to %d\n", pindex); 1.1240 +#endif 1.1241 + } 1.1242 + 1.1243 + continue; 1.1244 + } 1.1245 + } 1.1246 + 1.1247 +#ifdef DEBUG_FIND 1.1248 + printf("NOT: %c == %c\n", c, patc); 1.1249 +#endif 1.1250 + 1.1251 + // If we didn't match, go back to the beginning of patStr, 1.1252 + // and set findex back to the next char after 1.1253 + // we started the current match. 1.1254 + if (matchAnchorNode) // we're ending a partial match 1.1255 + { 1.1256 + findex = matchAnchorOffset; 1.1257 + mIterOffset = matchAnchorOffset; 1.1258 + // +incr will be added to findex when we continue 1.1259 + 1.1260 + // Are we going back to a previous node? 1.1261 + if (matchAnchorNode != mIterNode) 1.1262 + { 1.1263 + nsCOMPtr<nsIContent> content (do_QueryInterface(matchAnchorNode)); 1.1264 + DebugOnly<nsresult> rv = NS_ERROR_UNEXPECTED; 1.1265 + if (content) 1.1266 + rv = mIterator->PositionAt(content); 1.1267 + frag = 0; 1.1268 + NS_ASSERTION(NS_SUCCEEDED(rv), "Text content wasn't nsIContent!"); 1.1269 +#ifdef DEBUG_FIND 1.1270 + printf("Repositioned anchor node\n"); 1.1271 +#endif 1.1272 + } 1.1273 +#ifdef DEBUG_FIND 1.1274 + printf("Ending a partial match; findex -> %d, mIterOffset -> %d\n", 1.1275 + findex, mIterOffset); 1.1276 +#endif 1.1277 + } 1.1278 + matchAnchorNode = nullptr; 1.1279 + matchAnchorOffset = 0; 1.1280 + inWhitespace = false; 1.1281 + pindex = (mFindBackward ? patLen : 0); 1.1282 +#ifdef DEBUG_FIND 1.1283 + printf("Setting findex back to %d, pindex to %d\n", findex, pindex); 1.1284 + 1.1285 +#endif 1.1286 + } // end while loop 1.1287 + 1.1288 + // Out of nodes, and didn't match. 1.1289 + ResetAll(); 1.1290 + return NS_OK; 1.1291 +} 1.1292 + 1.1293 +/* static */ 1.1294 +already_AddRefed<nsIDOMRange> 1.1295 +nsFind::CreateRange(nsINode* aNode) 1.1296 +{ 1.1297 + nsRefPtr<nsRange> range = new nsRange(aNode); 1.1298 + range->SetMaySpanAnonymousSubtrees(true); 1.1299 + return range.forget(); 1.1300 +}