embedding/components/find/src/nsFind.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: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 //#define DEBUG_FIND 1
michael@0 7
michael@0 8 #include "nsFind.h"
michael@0 9 #include "nsContentCID.h"
michael@0 10 #include "nsIContent.h"
michael@0 11 #include "nsIDOMNode.h"
michael@0 12 #include "nsIDOMNodeList.h"
michael@0 13 #include "nsISelection.h"
michael@0 14 #include "nsISelectionController.h"
michael@0 15 #include "nsIFrame.h"
michael@0 16 #include "nsITextControlFrame.h"
michael@0 17 #include "nsIFormControl.h"
michael@0 18 #include "nsIEditor.h"
michael@0 19 #include "nsIPlaintextEditor.h"
michael@0 20 #include "nsTextFragment.h"
michael@0 21 #include "nsString.h"
michael@0 22 #include "nsIAtom.h"
michael@0 23 #include "nsServiceManagerUtils.h"
michael@0 24 #include "nsUnicharUtils.h"
michael@0 25 #include "nsIDOMElement.h"
michael@0 26 #include "nsIWordBreaker.h"
michael@0 27 #include "nsCRT.h"
michael@0 28 #include "nsRange.h"
michael@0 29 #include "nsContentUtils.h"
michael@0 30 #include "mozilla/DebugOnly.h"
michael@0 31
michael@0 32 using namespace mozilla;
michael@0 33
michael@0 34 // Yikes! Casting a char to unichar can fill with ones!
michael@0 35 #define CHAR_TO_UNICHAR(c) ((char16_t)(const unsigned char)c)
michael@0 36
michael@0 37 static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
michael@0 38 static NS_DEFINE_CID(kCPreContentIteratorCID, NS_PRECONTENTITERATOR_CID);
michael@0 39
michael@0 40 #define CH_QUOTE ((char16_t) 0x22)
michael@0 41 #define CH_APOSTROPHE ((char16_t) 0x27)
michael@0 42 #define CH_LEFT_SINGLE_QUOTE ((char16_t) 0x2018)
michael@0 43 #define CH_RIGHT_SINGLE_QUOTE ((char16_t) 0x2019)
michael@0 44 #define CH_LEFT_DOUBLE_QUOTE ((char16_t) 0x201C)
michael@0 45 #define CH_RIGHT_DOUBLE_QUOTE ((char16_t) 0x201D)
michael@0 46
michael@0 47 #define CH_SHY ((char16_t) 0xAD)
michael@0 48
michael@0 49 // nsFind::Find casts CH_SHY to char before calling StripChars
michael@0 50 // This works correctly if and only if CH_SHY <= 255
michael@0 51 PR_STATIC_ASSERT(CH_SHY <= 255);
michael@0 52
michael@0 53 // -----------------------------------------------------------------------
michael@0 54 // nsFindContentIterator is a special iterator that also goes through
michael@0 55 // any existing <textarea>'s or text <input>'s editor to lookup the
michael@0 56 // anonymous DOM content there.
michael@0 57 //
michael@0 58 // Details:
michael@0 59 // 1) We use two iterators: The "outer-iterator" goes through the
michael@0 60 // normal DOM. The "inner-iterator" goes through the anonymous DOM
michael@0 61 // inside the editor.
michael@0 62 //
michael@0 63 // 2) [MaybeSetupInnerIterator] As soon as the outer-iterator's current
michael@0 64 // node is changed, a check is made to see if the node is a <textarea> or
michael@0 65 // a text <input> node. If so, an inner-iterator is created to lookup the
michael@0 66 // anynomous contents of the editor underneath the text control.
michael@0 67 //
michael@0 68 // 3) When the inner-iterator is created, we position the outer-iterator
michael@0 69 // 'after' (or 'before' in backward search) the text control to avoid
michael@0 70 // revisiting that control.
michael@0 71 //
michael@0 72 // 4) As a consequence of searching through text controls, we can be
michael@0 73 // called via FindNext with the current selection inside a <textarea>
michael@0 74 // or a text <input>. This means that we can be given an initial search
michael@0 75 // range that stretches across the anonymous DOM and the normal DOM. To
michael@0 76 // cater for this situation, we split the anonymous part into the
michael@0 77 // inner-iterator and then reposition the outer-iterator outside.
michael@0 78 //
michael@0 79 // 5) The implementation assumes that First() and Next() are only called
michael@0 80 // in find-forward mode, while Last() and Prev() are used in find-backward.
michael@0 81
michael@0 82 class nsFindContentIterator : public nsIContentIterator
michael@0 83 {
michael@0 84 public:
michael@0 85 nsFindContentIterator(bool aFindBackward)
michael@0 86 : mStartOffset(0),
michael@0 87 mEndOffset(0),
michael@0 88 mFindBackward(aFindBackward)
michael@0 89 {
michael@0 90 }
michael@0 91
michael@0 92 virtual ~nsFindContentIterator()
michael@0 93 {
michael@0 94 }
michael@0 95
michael@0 96 // nsISupports
michael@0 97 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
michael@0 98 NS_DECL_CYCLE_COLLECTION_CLASS(nsFindContentIterator)
michael@0 99
michael@0 100 // nsIContentIterator
michael@0 101 virtual nsresult Init(nsINode* aRoot)
michael@0 102 {
michael@0 103 NS_NOTREACHED("internal error");
michael@0 104 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 105 }
michael@0 106 virtual nsresult Init(nsIDOMRange* aRange)
michael@0 107 {
michael@0 108 NS_NOTREACHED("internal error");
michael@0 109 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 110 }
michael@0 111 // Not a range because one of the endpoints may be anonymous.
michael@0 112 nsresult Init(nsIDOMNode* aStartNode, int32_t aStartOffset,
michael@0 113 nsIDOMNode* aEndNode, int32_t aEndOffset);
michael@0 114 virtual void First();
michael@0 115 virtual void Last();
michael@0 116 virtual void Next();
michael@0 117 virtual void Prev();
michael@0 118 virtual nsINode* GetCurrentNode();
michael@0 119 virtual bool IsDone();
michael@0 120 virtual nsresult PositionAt(nsINode* aCurNode);
michael@0 121
michael@0 122 private:
michael@0 123 nsCOMPtr<nsIContentIterator> mOuterIterator;
michael@0 124 nsCOMPtr<nsIContentIterator> mInnerIterator;
michael@0 125 // Can't use a range here, since we want to represent part of the
michael@0 126 // flattened tree, including native anonymous content.
michael@0 127 nsCOMPtr<nsIDOMNode> mStartNode;
michael@0 128 int32_t mStartOffset;
michael@0 129 nsCOMPtr<nsIDOMNode> mEndNode;
michael@0 130 int32_t mEndOffset;
michael@0 131
michael@0 132 nsCOMPtr<nsIContent> mStartOuterContent;
michael@0 133 nsCOMPtr<nsIContent> mEndOuterContent;
michael@0 134 bool mFindBackward;
michael@0 135
michael@0 136 void Reset();
michael@0 137 void MaybeSetupInnerIterator();
michael@0 138 void SetupInnerIterator(nsIContent* aContent);
michael@0 139 };
michael@0 140
michael@0 141 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFindContentIterator)
michael@0 142 NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
michael@0 143 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 144 NS_INTERFACE_MAP_END
michael@0 145
michael@0 146 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFindContentIterator)
michael@0 147 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFindContentIterator)
michael@0 148
michael@0 149 NS_IMPL_CYCLE_COLLECTION(nsFindContentIterator, mOuterIterator, mInnerIterator,
michael@0 150 mStartOuterContent, mEndOuterContent, mEndNode, mStartNode)
michael@0 151
michael@0 152
michael@0 153 nsresult
michael@0 154 nsFindContentIterator::Init(nsIDOMNode* aStartNode, int32_t aStartOffset,
michael@0 155 nsIDOMNode* aEndNode, int32_t aEndOffset)
michael@0 156 {
michael@0 157 NS_ENSURE_ARG_POINTER(aStartNode);
michael@0 158 NS_ENSURE_ARG_POINTER(aEndNode);
michael@0 159 if (!mOuterIterator) {
michael@0 160 if (mFindBackward) {
michael@0 161 // Use post-order in the reverse case, so we get parents
michael@0 162 // before children in case we want to prevent descending
michael@0 163 // into a node.
michael@0 164 mOuterIterator = do_CreateInstance(kCContentIteratorCID);
michael@0 165 }
michael@0 166 else {
michael@0 167 // Use pre-order in the forward case, so we get parents
michael@0 168 // before children in case we want to prevent descending
michael@0 169 // into a node.
michael@0 170 mOuterIterator = do_CreateInstance(kCPreContentIteratorCID);
michael@0 171 }
michael@0 172 NS_ENSURE_ARG_POINTER(mOuterIterator);
michael@0 173 }
michael@0 174
michael@0 175 // Set up the search "range" that we will examine
michael@0 176 mStartNode = aStartNode;
michael@0 177 mStartOffset = aStartOffset;
michael@0 178 mEndNode = aEndNode;
michael@0 179 mEndOffset = aEndOffset;
michael@0 180
michael@0 181 return NS_OK;
michael@0 182 }
michael@0 183
michael@0 184 void
michael@0 185 nsFindContentIterator::First()
michael@0 186 {
michael@0 187 Reset();
michael@0 188 }
michael@0 189
michael@0 190 void
michael@0 191 nsFindContentIterator::Last()
michael@0 192 {
michael@0 193 Reset();
michael@0 194 }
michael@0 195
michael@0 196 void
michael@0 197 nsFindContentIterator::Next()
michael@0 198 {
michael@0 199 if (mInnerIterator) {
michael@0 200 mInnerIterator->Next();
michael@0 201 if (!mInnerIterator->IsDone())
michael@0 202 return;
michael@0 203
michael@0 204 // by construction, mOuterIterator is already on the next node
michael@0 205 }
michael@0 206 else {
michael@0 207 mOuterIterator->Next();
michael@0 208 }
michael@0 209 MaybeSetupInnerIterator();
michael@0 210 }
michael@0 211
michael@0 212 void
michael@0 213 nsFindContentIterator::Prev()
michael@0 214 {
michael@0 215 if (mInnerIterator) {
michael@0 216 mInnerIterator->Prev();
michael@0 217 if (!mInnerIterator->IsDone())
michael@0 218 return;
michael@0 219
michael@0 220 // by construction, mOuterIterator is already on the previous node
michael@0 221 }
michael@0 222 else {
michael@0 223 mOuterIterator->Prev();
michael@0 224 }
michael@0 225 MaybeSetupInnerIterator();
michael@0 226 }
michael@0 227
michael@0 228 nsINode*
michael@0 229 nsFindContentIterator::GetCurrentNode()
michael@0 230 {
michael@0 231 if (mInnerIterator && !mInnerIterator->IsDone()) {
michael@0 232 return mInnerIterator->GetCurrentNode();
michael@0 233 }
michael@0 234 return mOuterIterator->GetCurrentNode();
michael@0 235 }
michael@0 236
michael@0 237 bool
michael@0 238 nsFindContentIterator::IsDone() {
michael@0 239 if (mInnerIterator && !mInnerIterator->IsDone()) {
michael@0 240 return false;
michael@0 241 }
michael@0 242 return mOuterIterator->IsDone();
michael@0 243 }
michael@0 244
michael@0 245 nsresult
michael@0 246 nsFindContentIterator::PositionAt(nsINode* aCurNode)
michael@0 247 {
michael@0 248 nsINode* oldNode = mOuterIterator->GetCurrentNode();
michael@0 249 nsresult rv = mOuterIterator->PositionAt(aCurNode);
michael@0 250 if (NS_SUCCEEDED(rv)) {
michael@0 251 MaybeSetupInnerIterator();
michael@0 252 }
michael@0 253 else {
michael@0 254 mOuterIterator->PositionAt(oldNode);
michael@0 255 if (mInnerIterator)
michael@0 256 rv = mInnerIterator->PositionAt(aCurNode);
michael@0 257 }
michael@0 258 return rv;
michael@0 259 }
michael@0 260
michael@0 261 void
michael@0 262 nsFindContentIterator::Reset()
michael@0 263 {
michael@0 264 mInnerIterator = nullptr;
michael@0 265 mStartOuterContent = nullptr;
michael@0 266 mEndOuterContent = nullptr;
michael@0 267
michael@0 268 // As a consequence of searching through text controls, we may have been
michael@0 269 // initialized with a selection inside a <textarea> or a text <input>.
michael@0 270
michael@0 271 // see if the start node is an anonymous text node inside a text control
michael@0 272 nsCOMPtr<nsIContent> startContent(do_QueryInterface(mStartNode));
michael@0 273 if (startContent) {
michael@0 274 mStartOuterContent = startContent->FindFirstNonChromeOnlyAccessContent();
michael@0 275 }
michael@0 276
michael@0 277 // see if the end node is an anonymous text node inside a text control
michael@0 278 nsCOMPtr<nsIContent> endContent(do_QueryInterface(mEndNode));
michael@0 279 if (endContent) {
michael@0 280 mEndOuterContent = endContent->FindFirstNonChromeOnlyAccessContent();
michael@0 281 }
michael@0 282
michael@0 283 // Note: OK to just set up the outer iterator here; if our range has a native
michael@0 284 // anonymous endpoint we'll end up setting up an inner iterator, and
michael@0 285 // reset the outer one in the process.
michael@0 286 nsCOMPtr<nsINode> node = do_QueryInterface(mStartNode);
michael@0 287 NS_ENSURE_TRUE_VOID(node);
michael@0 288
michael@0 289 nsCOMPtr<nsIDOMRange> range = nsFind::CreateRange(node);
michael@0 290 range->SetStart(mStartNode, mStartOffset);
michael@0 291 range->SetEnd(mEndNode, mEndOffset);
michael@0 292 mOuterIterator->Init(range);
michael@0 293
michael@0 294 if (!mFindBackward) {
michael@0 295 if (mStartOuterContent != startContent) {
michael@0 296 // the start node was an anonymous text node
michael@0 297 SetupInnerIterator(mStartOuterContent);
michael@0 298 if (mInnerIterator)
michael@0 299 mInnerIterator->First();
michael@0 300 }
michael@0 301 if (!mOuterIterator->IsDone())
michael@0 302 mOuterIterator->First();
michael@0 303 }
michael@0 304 else {
michael@0 305 if (mEndOuterContent != endContent) {
michael@0 306 // the end node was an anonymous text node
michael@0 307 SetupInnerIterator(mEndOuterContent);
michael@0 308 if (mInnerIterator)
michael@0 309 mInnerIterator->Last();
michael@0 310 }
michael@0 311 if (!mOuterIterator->IsDone())
michael@0 312 mOuterIterator->Last();
michael@0 313 }
michael@0 314
michael@0 315 // if we didn't create an inner-iterator, the boundary node could still be
michael@0 316 // a text control, in which case we also need an inner-iterator straightaway
michael@0 317 if (!mInnerIterator) {
michael@0 318 MaybeSetupInnerIterator();
michael@0 319 }
michael@0 320 }
michael@0 321
michael@0 322 void
michael@0 323 nsFindContentIterator::MaybeSetupInnerIterator()
michael@0 324 {
michael@0 325 mInnerIterator = nullptr;
michael@0 326
michael@0 327 nsCOMPtr<nsIContent> content =
michael@0 328 do_QueryInterface(mOuterIterator->GetCurrentNode());
michael@0 329 if (!content || !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL))
michael@0 330 return;
michael@0 331
michael@0 332 nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
michael@0 333 if (!formControl->IsTextControl(true)) {
michael@0 334 return;
michael@0 335 }
michael@0 336
michael@0 337 SetupInnerIterator(content);
michael@0 338 if (mInnerIterator) {
michael@0 339 if (!mFindBackward) {
michael@0 340 mInnerIterator->First();
michael@0 341 // finish setup: position mOuterIterator on the actual "next"
michael@0 342 // node (this completes its re-init, @see SetupInnerIterator)
michael@0 343 if (!mOuterIterator->IsDone())
michael@0 344 mOuterIterator->First();
michael@0 345 }
michael@0 346 else {
michael@0 347 mInnerIterator->Last();
michael@0 348 // finish setup: position mOuterIterator on the actual "previous"
michael@0 349 // node (this completes its re-init, @see SetupInnerIterator)
michael@0 350 if (!mOuterIterator->IsDone())
michael@0 351 mOuterIterator->Last();
michael@0 352 }
michael@0 353 }
michael@0 354 }
michael@0 355
michael@0 356 void
michael@0 357 nsFindContentIterator::SetupInnerIterator(nsIContent* aContent)
michael@0 358 {
michael@0 359 if (!aContent) {
michael@0 360 return;
michael@0 361 }
michael@0 362 NS_ASSERTION(!aContent->IsRootOfNativeAnonymousSubtree(), "invalid call");
michael@0 363
michael@0 364 nsITextControlFrame* tcFrame = do_QueryFrame(aContent->GetPrimaryFrame());
michael@0 365 if (!tcFrame)
michael@0 366 return;
michael@0 367
michael@0 368 nsCOMPtr<nsIEditor> editor;
michael@0 369 tcFrame->GetEditor(getter_AddRefs(editor));
michael@0 370 if (!editor)
michael@0 371 return;
michael@0 372
michael@0 373 // don't mess with disabled input fields
michael@0 374 uint32_t editorFlags = 0;
michael@0 375 editor->GetFlags(&editorFlags);
michael@0 376 if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
michael@0 377 return;
michael@0 378
michael@0 379 nsCOMPtr<nsIDOMElement> rootElement;
michael@0 380 editor->GetRootElement(getter_AddRefs(rootElement));
michael@0 381
michael@0 382 nsCOMPtr<nsIDOMRange> innerRange = nsFind::CreateRange(aContent);
michael@0 383 nsCOMPtr<nsIDOMRange> outerRange = nsFind::CreateRange(aContent);
michael@0 384 if (!innerRange || !outerRange) {
michael@0 385 return;
michael@0 386 }
michael@0 387
michael@0 388 // now create the inner-iterator
michael@0 389 mInnerIterator = do_CreateInstance(kCPreContentIteratorCID);
michael@0 390
michael@0 391 if (mInnerIterator) {
michael@0 392 innerRange->SelectNodeContents(rootElement);
michael@0 393
michael@0 394 // fix up the inner bounds, we may have to only lookup a portion
michael@0 395 // of the text control if the current node is a boundary point
michael@0 396 if (aContent == mStartOuterContent) {
michael@0 397 innerRange->SetStart(mStartNode, mStartOffset);
michael@0 398 }
michael@0 399 if (aContent == mEndOuterContent) {
michael@0 400 innerRange->SetEnd(mEndNode, mEndOffset);
michael@0 401 }
michael@0 402 // Note: we just init here. We do First() or Last() later.
michael@0 403 mInnerIterator->Init(innerRange);
michael@0 404
michael@0 405 // make sure to place the outer-iterator outside
michael@0 406 // the text control so that we don't go there again.
michael@0 407 nsresult res1, res2;
michael@0 408 nsCOMPtr<nsIDOMNode> outerNode(do_QueryInterface(aContent));
michael@0 409 if (!mFindBackward) { // find forward
michael@0 410 // cut the outer-iterator after the current node
michael@0 411 res1 = outerRange->SetEnd(mEndNode, mEndOffset);
michael@0 412 res2 = outerRange->SetStartAfter(outerNode);
michael@0 413 }
michael@0 414 else { // find backward
michael@0 415 // cut the outer-iterator before the current node
michael@0 416 res1 = outerRange->SetStart(mStartNode, mStartOffset);
michael@0 417 res2 = outerRange->SetEndBefore(outerNode);
michael@0 418 }
michael@0 419 if (NS_FAILED(res1) || NS_FAILED(res2)) {
michael@0 420 // we are done with the outer-iterator, the
michael@0 421 // inner-iterator will traverse what we want
michael@0 422 outerRange->Collapse(true);
michael@0 423 }
michael@0 424
michael@0 425 // Note: we just re-init here, using the segment of our search range that
michael@0 426 // is yet to be visited. Thus when we later do mOuterIterator->First() [or
michael@0 427 // mOuterIterator->Last()], we will effectively be on the next node [or
michael@0 428 // the previous node] _with respect to_ the search range.
michael@0 429 mOuterIterator->Init(outerRange);
michael@0 430 }
michael@0 431 }
michael@0 432
michael@0 433 nsresult
michael@0 434 NS_NewFindContentIterator(bool aFindBackward,
michael@0 435 nsIContentIterator** aResult)
michael@0 436 {
michael@0 437 NS_ENSURE_ARG_POINTER(aResult);
michael@0 438 if (!aResult) {
michael@0 439 return NS_ERROR_NULL_POINTER;
michael@0 440 }
michael@0 441
michael@0 442 nsFindContentIterator* it = new nsFindContentIterator(aFindBackward);
michael@0 443 if (!it) {
michael@0 444 return NS_ERROR_OUT_OF_MEMORY;
michael@0 445 }
michael@0 446 return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void **)aResult);
michael@0 447 }
michael@0 448 // --------------------------------------------------------------------
michael@0 449
michael@0 450 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFind)
michael@0 451 NS_INTERFACE_MAP_ENTRY(nsIFind)
michael@0 452 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 453 NS_INTERFACE_MAP_END
michael@0 454
michael@0 455 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFind)
michael@0 456 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFind)
michael@0 457
michael@0 458 NS_IMPL_CYCLE_COLLECTION(nsFind, mLastBlockParent, mIterNode, mIterator)
michael@0 459
michael@0 460 nsFind::nsFind()
michael@0 461 : mFindBackward(false)
michael@0 462 , mCaseSensitive(false)
michael@0 463 , mIterOffset(0)
michael@0 464 {
michael@0 465 }
michael@0 466
michael@0 467 nsFind::~nsFind()
michael@0 468 {
michael@0 469 }
michael@0 470
michael@0 471 #ifdef DEBUG_FIND
michael@0 472 static void DumpNode(nsIDOMNode* aNode)
michael@0 473 {
michael@0 474 if (!aNode)
michael@0 475 {
michael@0 476 printf(">>>> Node: NULL\n");
michael@0 477 return;
michael@0 478 }
michael@0 479 nsAutoString nodeName;
michael@0 480 aNode->GetNodeName(nodeName);
michael@0 481 nsCOMPtr<nsIContent> textContent (do_QueryInterface(aNode));
michael@0 482 if (textContent && textContent->IsNodeOfType(nsINode::eTEXT))
michael@0 483 {
michael@0 484 nsAutoString newText;
michael@0 485 textContent->AppendTextTo(newText);
michael@0 486 printf(">>>> Text node (node name %s): '%s'\n",
michael@0 487 NS_LossyConvertUTF16toASCII(nodeName).get(),
michael@0 488 NS_LossyConvertUTF16toASCII(newText).get());
michael@0 489 }
michael@0 490 else
michael@0 491 printf(">>>> Node: %s\n", NS_LossyConvertUTF16toASCII(nodeName).get());
michael@0 492 }
michael@0 493 #endif
michael@0 494
michael@0 495 nsresult
michael@0 496 nsFind::InitIterator(nsIDOMNode* aStartNode, int32_t aStartOffset,
michael@0 497 nsIDOMNode* aEndNode, int32_t aEndOffset)
michael@0 498 {
michael@0 499 if (!mIterator)
michael@0 500 {
michael@0 501 mIterator = new nsFindContentIterator(mFindBackward);
michael@0 502 NS_ENSURE_TRUE(mIterator, NS_ERROR_OUT_OF_MEMORY);
michael@0 503 }
michael@0 504
michael@0 505 NS_ENSURE_ARG_POINTER(aStartNode);
michael@0 506 NS_ENSURE_ARG_POINTER(aEndNode);
michael@0 507
michael@0 508 #ifdef DEBUG_FIND
michael@0 509 printf("InitIterator search range:\n");
michael@0 510 printf(" -- start %d, ", aStartOffset); DumpNode(aStartNode);
michael@0 511 printf(" -- end %d, ", aEndOffset); DumpNode(aEndNode);
michael@0 512 #endif
michael@0 513
michael@0 514 nsresult rv =
michael@0 515 mIterator->Init(aStartNode, aStartOffset, aEndNode, aEndOffset);
michael@0 516 NS_ENSURE_SUCCESS(rv, rv);
michael@0 517 if (mFindBackward) {
michael@0 518 mIterator->Last();
michael@0 519 }
michael@0 520 else {
michael@0 521 mIterator->First();
michael@0 522 }
michael@0 523 return NS_OK;
michael@0 524 }
michael@0 525
michael@0 526 /* attribute boolean findBackward; */
michael@0 527 NS_IMETHODIMP
michael@0 528 nsFind::GetFindBackwards(bool *aFindBackward)
michael@0 529 {
michael@0 530 if (!aFindBackward)
michael@0 531 return NS_ERROR_NULL_POINTER;
michael@0 532
michael@0 533 *aFindBackward = mFindBackward;
michael@0 534 return NS_OK;
michael@0 535 }
michael@0 536 NS_IMETHODIMP
michael@0 537 nsFind::SetFindBackwards(bool aFindBackward)
michael@0 538 {
michael@0 539 mFindBackward = aFindBackward;
michael@0 540 return NS_OK;
michael@0 541 }
michael@0 542
michael@0 543 /* attribute boolean caseSensitive; */
michael@0 544 NS_IMETHODIMP
michael@0 545 nsFind::GetCaseSensitive(bool *aCaseSensitive)
michael@0 546 {
michael@0 547 if (!aCaseSensitive)
michael@0 548 return NS_ERROR_NULL_POINTER;
michael@0 549
michael@0 550 *aCaseSensitive = mCaseSensitive;
michael@0 551 return NS_OK;
michael@0 552 }
michael@0 553 NS_IMETHODIMP
michael@0 554 nsFind::SetCaseSensitive(bool aCaseSensitive)
michael@0 555 {
michael@0 556 mCaseSensitive = aCaseSensitive;
michael@0 557 return NS_OK;
michael@0 558 }
michael@0 559
michael@0 560 NS_IMETHODIMP
michael@0 561 nsFind::GetWordBreaker(nsIWordBreaker** aWordBreaker)
michael@0 562 {
michael@0 563 *aWordBreaker = mWordBreaker;
michael@0 564 NS_IF_ADDREF(*aWordBreaker);
michael@0 565 return NS_OK;
michael@0 566 }
michael@0 567
michael@0 568 NS_IMETHODIMP
michael@0 569 nsFind::SetWordBreaker(nsIWordBreaker* aWordBreaker)
michael@0 570 {
michael@0 571 mWordBreaker = aWordBreaker;
michael@0 572 return NS_OK;
michael@0 573 }
michael@0 574
michael@0 575 //
michael@0 576 // Here begins the find code.
michael@0 577 // A ten-thousand-foot view of how it works:
michael@0 578 // Find needs to be able to compare across inline (but not block) nodes,
michael@0 579 // e.g. find for "abc" should match a<b>b</b>c.
michael@0 580 // So after we've searched a node, we're not done with it;
michael@0 581 // in the case of a partial match we may need to reset the
michael@0 582 // iterator to go back to a previously visited node,
michael@0 583 // so we always save the "match anchor" node and offset.
michael@0 584 //
michael@0 585 // Text nodes store their text in an nsTextFragment, which is
michael@0 586 // effectively a union of a one-byte string or a two-byte string.
michael@0 587 // Single and double strings are intermixed in the dom.
michael@0 588 // We don't have string classes which can deal with intermixed strings,
michael@0 589 // so all the handling is done explicitly here.
michael@0 590 //
michael@0 591
michael@0 592 nsresult
michael@0 593 nsFind::NextNode(nsIDOMRange* aSearchRange,
michael@0 594 nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
michael@0 595 bool aContinueOk)
michael@0 596 {
michael@0 597 nsresult rv;
michael@0 598
michael@0 599 nsCOMPtr<nsIContent> content;
michael@0 600
michael@0 601 if (!mIterator || aContinueOk)
michael@0 602 {
michael@0 603 // If we are continuing, that means we have a match in progress.
michael@0 604 // In that case, we want to continue from the end point
michael@0 605 // (where we are now) to the beginning/end of the search range.
michael@0 606 nsCOMPtr<nsIDOMNode> startNode;
michael@0 607 nsCOMPtr<nsIDOMNode> endNode;
michael@0 608 int32_t startOffset, endOffset;
michael@0 609 if (aContinueOk)
michael@0 610 {
michael@0 611 #ifdef DEBUG_FIND
michael@0 612 printf("Match in progress: continuing past endpoint\n");
michael@0 613 #endif
michael@0 614 if (mFindBackward) {
michael@0 615 aSearchRange->GetStartContainer(getter_AddRefs(startNode));
michael@0 616 aSearchRange->GetStartOffset(&startOffset);
michael@0 617 aEndPoint->GetStartContainer(getter_AddRefs(endNode));
michael@0 618 aEndPoint->GetStartOffset(&endOffset);
michael@0 619 } else { // forward
michael@0 620 aEndPoint->GetEndContainer(getter_AddRefs(startNode));
michael@0 621 aEndPoint->GetEndOffset(&startOffset);
michael@0 622 aSearchRange->GetEndContainer(getter_AddRefs(endNode));
michael@0 623 aSearchRange->GetEndOffset(&endOffset);
michael@0 624 }
michael@0 625 }
michael@0 626 else // Normal, not continuing
michael@0 627 {
michael@0 628 if (mFindBackward) {
michael@0 629 aSearchRange->GetStartContainer(getter_AddRefs(startNode));
michael@0 630 aSearchRange->GetStartOffset(&startOffset);
michael@0 631 aStartPoint->GetEndContainer(getter_AddRefs(endNode));
michael@0 632 aStartPoint->GetEndOffset(&endOffset);
michael@0 633 // XXX Needs work:
michael@0 634 // Problem with this approach: if there is a match which starts
michael@0 635 // just before the current selection and continues into the selection,
michael@0 636 // we will miss it, because our search algorithm only starts
michael@0 637 // searching from the end of the word, so we would have to
michael@0 638 // search the current selection but discount any matches
michael@0 639 // that fall entirely inside it.
michael@0 640 } else { // forward
michael@0 641 aStartPoint->GetStartContainer(getter_AddRefs(startNode));
michael@0 642 aStartPoint->GetStartOffset(&startOffset);
michael@0 643 aEndPoint->GetEndContainer(getter_AddRefs(endNode));
michael@0 644 aEndPoint->GetEndOffset(&endOffset);
michael@0 645 }
michael@0 646 }
michael@0 647
michael@0 648 rv = InitIterator(startNode, startOffset, endNode, endOffset);
michael@0 649 NS_ENSURE_SUCCESS(rv, rv);
michael@0 650 if (!aStartPoint)
michael@0 651 aStartPoint = aSearchRange;
michael@0 652
michael@0 653 content = do_QueryInterface(mIterator->GetCurrentNode());
michael@0 654 #ifdef DEBUG_FIND
michael@0 655 nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content));
michael@0 656 printf(":::::: Got the first node "); DumpNode(dnode);
michael@0 657 #endif
michael@0 658 if (content && content->IsNodeOfType(nsINode::eTEXT) &&
michael@0 659 !SkipNode(content))
michael@0 660 {
michael@0 661 mIterNode = do_QueryInterface(content);
michael@0 662 // Also set mIterOffset if appropriate:
michael@0 663 nsCOMPtr<nsIDOMNode> node;
michael@0 664 if (mFindBackward) {
michael@0 665 aStartPoint->GetEndContainer(getter_AddRefs(node));
michael@0 666 if (mIterNode.get() == node.get())
michael@0 667 aStartPoint->GetEndOffset(&mIterOffset);
michael@0 668 else
michael@0 669 mIterOffset = -1; // sign to start from end
michael@0 670 }
michael@0 671 else
michael@0 672 {
michael@0 673 aStartPoint->GetStartContainer(getter_AddRefs(node));
michael@0 674 if (mIterNode.get() == node.get())
michael@0 675 aStartPoint->GetStartOffset(&mIterOffset);
michael@0 676 else
michael@0 677 mIterOffset = 0;
michael@0 678 }
michael@0 679 #ifdef DEBUG_FIND
michael@0 680 printf("Setting initial offset to %d\n", mIterOffset);
michael@0 681 #endif
michael@0 682 return NS_OK;
michael@0 683 }
michael@0 684 }
michael@0 685
michael@0 686 while (1)
michael@0 687 {
michael@0 688 if (mFindBackward)
michael@0 689 mIterator->Prev();
michael@0 690 else
michael@0 691 mIterator->Next();
michael@0 692
michael@0 693 content = do_QueryInterface(mIterator->GetCurrentNode());
michael@0 694 if (!content)
michael@0 695 break;
michael@0 696
michael@0 697 #ifdef DEBUG_FIND
michael@0 698 nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content));
michael@0 699 printf(":::::: Got another node "); DumpNode(dnode);
michael@0 700 #endif
michael@0 701
michael@0 702 // If we ever cross a block node, we might want to reset
michael@0 703 // the match anchor:
michael@0 704 // we don't match patterns extending across block boundaries.
michael@0 705 // But we can't depend on this test here now, because the iterator
michael@0 706 // doesn't give us the parent going in and going out, and we
michael@0 707 // need it both times to depend on this.
michael@0 708 //if (IsBlockNode(content))
michael@0 709
michael@0 710 // Now see if we need to skip this node --
michael@0 711 // e.g. is it part of a script or other invisible node?
michael@0 712 // Note that we don't ask for CSS information;
michael@0 713 // a node can be invisible due to CSS, and we'd still find it.
michael@0 714 if (SkipNode(content))
michael@0 715 continue;
michael@0 716
michael@0 717 if (content->IsNodeOfType(nsINode::eTEXT))
michael@0 718 break;
michael@0 719 #ifdef DEBUG_FIND
michael@0 720 dnode = do_QueryInterface(content);
michael@0 721 printf("Not a text node: "); DumpNode(dnode);
michael@0 722 #endif
michael@0 723 }
michael@0 724
michael@0 725 if (content)
michael@0 726 mIterNode = do_QueryInterface(content);
michael@0 727 else
michael@0 728 mIterNode = nullptr;
michael@0 729 mIterOffset = -1;
michael@0 730
michael@0 731 #ifdef DEBUG_FIND
michael@0 732 printf("Iterator gave: "); DumpNode(mIterNode);
michael@0 733 #endif
michael@0 734 return NS_OK;
michael@0 735 }
michael@0 736
michael@0 737 bool nsFind::IsBlockNode(nsIContent* aContent)
michael@0 738 {
michael@0 739 if (!aContent->IsHTML()) {
michael@0 740 return false;
michael@0 741 }
michael@0 742
michael@0 743 nsIAtom *atom = aContent->Tag();
michael@0 744
michael@0 745 if (atom == nsGkAtoms::img ||
michael@0 746 atom == nsGkAtoms::hr ||
michael@0 747 atom == nsGkAtoms::th ||
michael@0 748 atom == nsGkAtoms::td)
michael@0 749 return true;
michael@0 750
michael@0 751 return nsContentUtils::IsHTMLBlock(atom);
michael@0 752 }
michael@0 753
michael@0 754 bool nsFind::IsTextNode(nsIDOMNode* aNode)
michael@0 755 {
michael@0 756 uint16_t nodeType;
michael@0 757 aNode->GetNodeType(&nodeType);
michael@0 758
michael@0 759 return nodeType == nsIDOMNode::TEXT_NODE ||
michael@0 760 nodeType == nsIDOMNode::CDATA_SECTION_NODE;
michael@0 761 }
michael@0 762
michael@0 763 bool nsFind::IsVisibleNode(nsIDOMNode *aDOMNode)
michael@0 764 {
michael@0 765 nsCOMPtr<nsIContent> content(do_QueryInterface(aDOMNode));
michael@0 766 if (!content)
michael@0 767 return false;
michael@0 768
michael@0 769 nsIFrame *frame = content->GetPrimaryFrame();
michael@0 770 if (!frame) {
michael@0 771 // No frame! Not visible then.
michael@0 772 return false;
michael@0 773 }
michael@0 774
michael@0 775 return frame->StyleVisibility()->IsVisible();
michael@0 776 }
michael@0 777
michael@0 778 bool nsFind::SkipNode(nsIContent* aContent)
michael@0 779 {
michael@0 780 nsIAtom *atom;
michael@0 781
michael@0 782 #ifdef HAVE_BIDI_ITERATOR
michael@0 783 atom = aContent->Tag();
michael@0 784
michael@0 785 // We may not need to skip comment nodes,
michael@0 786 // now that IsTextNode distinguishes them from real text nodes.
michael@0 787 return (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
michael@0 788 (aContent->IsHTML() &&
michael@0 789 (atom == sScriptAtom ||
michael@0 790 atom == sNoframesAtom ||
michael@0 791 atom == sSelectAtom)));
michael@0 792
michael@0 793 #else /* HAVE_BIDI_ITERATOR */
michael@0 794 // Temporary: eventually we will have an iterator to do this,
michael@0 795 // but for now, we have to climb up the tree for each node
michael@0 796 // and see whether any parent is a skipped node,
michael@0 797 // and take the performance hit.
michael@0 798
michael@0 799 nsIContent *content = aContent;
michael@0 800 while (content)
michael@0 801 {
michael@0 802 atom = content->Tag();
michael@0 803
michael@0 804 if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
michael@0 805 (content->IsHTML() &&
michael@0 806 (atom == nsGkAtoms::script ||
michael@0 807 atom == nsGkAtoms::noframes ||
michael@0 808 atom == nsGkAtoms::select)))
michael@0 809 {
michael@0 810 #ifdef DEBUG_FIND
michael@0 811 printf("Skipping node: ");
michael@0 812 nsCOMPtr<nsIDOMNode> node (do_QueryInterface(content));
michael@0 813 DumpNode(node);
michael@0 814 #endif
michael@0 815
michael@0 816 return true;
michael@0 817 }
michael@0 818
michael@0 819 // Only climb to the nearest block node
michael@0 820 if (IsBlockNode(content))
michael@0 821 return false;
michael@0 822
michael@0 823 content = content->GetParent();
michael@0 824 }
michael@0 825
michael@0 826 return false;
michael@0 827 #endif /* HAVE_BIDI_ITERATOR */
michael@0 828 }
michael@0 829
michael@0 830 nsresult nsFind::GetBlockParent(nsIDOMNode* aNode, nsIDOMNode** aParent)
michael@0 831 {
michael@0 832 while (aNode)
michael@0 833 {
michael@0 834 nsCOMPtr<nsIDOMNode> parent;
michael@0 835 nsresult rv = aNode->GetParentNode(getter_AddRefs(parent));
michael@0 836 NS_ENSURE_SUCCESS(rv, rv);
michael@0 837 nsCOMPtr<nsIContent> content (do_QueryInterface(parent));
michael@0 838 if (content && IsBlockNode(content))
michael@0 839 {
michael@0 840 *aParent = parent;
michael@0 841 NS_ADDREF(*aParent);
michael@0 842 return NS_OK;
michael@0 843 }
michael@0 844 aNode = parent;
michael@0 845 }
michael@0 846 return NS_ERROR_FAILURE;
michael@0 847 }
michael@0 848
michael@0 849 // Call ResetAll before returning,
michael@0 850 // to remove all references to external objects.
michael@0 851 void nsFind::ResetAll()
michael@0 852 {
michael@0 853 mIterator = nullptr;
michael@0 854 mLastBlockParent = nullptr;
michael@0 855 }
michael@0 856
michael@0 857 #define NBSP_CHARCODE (CHAR_TO_UNICHAR(160))
michael@0 858 #define IsSpace(c) (nsCRT::IsAsciiSpace(c) || (c) == NBSP_CHARCODE)
michael@0 859 #define OVERFLOW_PINDEX (mFindBackward ? pindex < 0 : pindex > patLen)
michael@0 860 #define DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen)
michael@0 861 #define ALMOST_DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen-1)
michael@0 862
michael@0 863 //
michael@0 864 // Find:
michael@0 865 // Take nodes out of the tree with NextNode,
michael@0 866 // until null (NextNode will return 0 at the end of our range).
michael@0 867 //
michael@0 868 NS_IMETHODIMP
michael@0 869 nsFind::Find(const char16_t *aPatText, nsIDOMRange* aSearchRange,
michael@0 870 nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
michael@0 871 nsIDOMRange** aRangeRet)
michael@0 872 {
michael@0 873 #ifdef DEBUG_FIND
michael@0 874 printf("============== nsFind::Find('%s'%s, %p, %p, %p)\n",
michael@0 875 NS_LossyConvertUTF16toASCII(aPatText).get(),
michael@0 876 mFindBackward ? " (backward)" : " (forward)",
michael@0 877 (void*)aSearchRange, (void*)aStartPoint, (void*)aEndPoint);
michael@0 878 #endif
michael@0 879
michael@0 880 NS_ENSURE_ARG(aSearchRange);
michael@0 881 NS_ENSURE_ARG(aStartPoint);
michael@0 882 NS_ENSURE_ARG(aEndPoint);
michael@0 883 NS_ENSURE_ARG_POINTER(aRangeRet);
michael@0 884 *aRangeRet = 0;
michael@0 885
michael@0 886 if (!aPatText)
michael@0 887 return NS_ERROR_NULL_POINTER;
michael@0 888
michael@0 889 ResetAll();
michael@0 890
michael@0 891 nsAutoString patAutoStr(aPatText);
michael@0 892 if (!mCaseSensitive)
michael@0 893 ToLowerCase(patAutoStr);
michael@0 894
michael@0 895 // Ignore soft hyphens in the pattern
michael@0 896 static const char kShy[] = { char(CH_SHY), 0 };
michael@0 897 patAutoStr.StripChars(kShy);
michael@0 898
michael@0 899 const char16_t* patStr = patAutoStr.get();
michael@0 900 int32_t patLen = patAutoStr.Length() - 1;
michael@0 901
michael@0 902 // current offset into the pattern -- reset to beginning/end:
michael@0 903 int32_t pindex = (mFindBackward ? patLen : 0);
michael@0 904
michael@0 905 // Current offset into the fragment
michael@0 906 int32_t findex = 0;
michael@0 907
michael@0 908 // Direction to move pindex and ptr*
michael@0 909 int incr = (mFindBackward ? -1 : 1);
michael@0 910
michael@0 911 nsCOMPtr<nsIContent> tc;
michael@0 912 const nsTextFragment *frag = nullptr;
michael@0 913 int32_t fragLen = 0;
michael@0 914
michael@0 915 // Pointers into the current fragment:
michael@0 916 const char16_t *t2b = nullptr;
michael@0 917 const char *t1b = nullptr;
michael@0 918
michael@0 919 // Keep track of when we're in whitespace:
michael@0 920 // (only matters when we're matching)
michael@0 921 bool inWhitespace = false;
michael@0 922
michael@0 923 // Place to save the range start point in case we find a match:
michael@0 924 nsCOMPtr<nsIDOMNode> matchAnchorNode;
michael@0 925 int32_t matchAnchorOffset = 0;
michael@0 926
michael@0 927 // Get the end point, so we know when to end searches:
michael@0 928 nsCOMPtr<nsIDOMNode> endNode;
michael@0 929 int32_t endOffset;
michael@0 930 aEndPoint->GetEndContainer(getter_AddRefs(endNode));
michael@0 931 aEndPoint->GetEndOffset(&endOffset);
michael@0 932
michael@0 933 char16_t prevChar = 0;
michael@0 934 while (1)
michael@0 935 {
michael@0 936 #ifdef DEBUG_FIND
michael@0 937 printf("Loop ...\n");
michael@0 938 #endif
michael@0 939
michael@0 940 // If this is our first time on a new node, reset the pointers:
michael@0 941 if (!frag)
michael@0 942 {
michael@0 943
michael@0 944 tc = nullptr;
michael@0 945 NextNode(aSearchRange, aStartPoint, aEndPoint, false);
michael@0 946 if (!mIterNode) // Out of nodes
michael@0 947 {
michael@0 948 // Are we in the middle of a match?
michael@0 949 // If so, try again with continuation.
michael@0 950 if (matchAnchorNode)
michael@0 951 NextNode(aSearchRange, aStartPoint, aEndPoint, true);
michael@0 952
michael@0 953 // Reset the iterator, so this nsFind will be usable if
michael@0 954 // the user wants to search again (from beginning/end).
michael@0 955 ResetAll();
michael@0 956 return NS_OK;
michael@0 957 }
michael@0 958
michael@0 959 // We have a new text content. If its block parent is different
michael@0 960 // from the block parent of the last text content, then we
michael@0 961 // need to clear the match since we don't want to find
michael@0 962 // across block boundaries.
michael@0 963 nsCOMPtr<nsIDOMNode> blockParent;
michael@0 964 GetBlockParent(mIterNode, getter_AddRefs(blockParent));
michael@0 965 #ifdef DEBUG_FIND
michael@0 966 printf("New node: old blockparent = %p, new = %p\n",
michael@0 967 (void*)mLastBlockParent.get(), (void*)blockParent.get());
michael@0 968 #endif
michael@0 969 if (blockParent != mLastBlockParent)
michael@0 970 {
michael@0 971 #ifdef DEBUG_FIND
michael@0 972 printf("Different block parent!\n");
michael@0 973 #endif
michael@0 974 mLastBlockParent = blockParent;
michael@0 975 // End any pending match:
michael@0 976 matchAnchorNode = nullptr;
michael@0 977 matchAnchorOffset = 0;
michael@0 978 pindex = (mFindBackward ? patLen : 0);
michael@0 979 inWhitespace = false;
michael@0 980 }
michael@0 981
michael@0 982 // Get the text content:
michael@0 983 tc = do_QueryInterface(mIterNode);
michael@0 984 if (!tc || !(frag = tc->GetText())) // Out of nodes
michael@0 985 {
michael@0 986 mIterator = nullptr;
michael@0 987 mLastBlockParent = 0;
michael@0 988 ResetAll();
michael@0 989 return NS_OK;
michael@0 990 }
michael@0 991
michael@0 992 fragLen = frag->GetLength();
michael@0 993
michael@0 994 // Set our starting point in this node.
michael@0 995 // If we're going back to the anchor node, which means that we
michael@0 996 // just ended a partial match, use the saved offset:
michael@0 997 if (mIterNode == matchAnchorNode)
michael@0 998 findex = matchAnchorOffset + (mFindBackward ? 1 : 0);
michael@0 999
michael@0 1000 // mIterOffset, if set, is the range's idea of an offset,
michael@0 1001 // and points between characters. But when translated
michael@0 1002 // to a string index, it points to a character. If we're
michael@0 1003 // going backward, this is one character too late and
michael@0 1004 // we'll match part of our previous pattern.
michael@0 1005 else if (mIterOffset >= 0)
michael@0 1006 findex = mIterOffset - (mFindBackward ? 1 : 0);
michael@0 1007
michael@0 1008 // Otherwise, just start at the appropriate end of the fragment:
michael@0 1009 else if (mFindBackward)
michael@0 1010 findex = fragLen - 1;
michael@0 1011 else
michael@0 1012 findex = 0;
michael@0 1013
michael@0 1014 // Offset can only apply to the first node:
michael@0 1015 mIterOffset = -1;
michael@0 1016
michael@0 1017 // If this is outside the bounds of the string, then skip this node:
michael@0 1018 if (findex < 0 || findex > fragLen-1)
michael@0 1019 {
michael@0 1020 #ifdef DEBUG_FIND
michael@0 1021 printf("At the end of a text node -- skipping to the next\n");
michael@0 1022 #endif
michael@0 1023 frag = 0;
michael@0 1024 continue;
michael@0 1025 }
michael@0 1026
michael@0 1027 #ifdef DEBUG_FIND
michael@0 1028 printf("Starting from offset %d\n", findex);
michael@0 1029 #endif
michael@0 1030 if (frag->Is2b())
michael@0 1031 {
michael@0 1032 t2b = frag->Get2b();
michael@0 1033 t1b = nullptr;
michael@0 1034 #ifdef DEBUG_FIND
michael@0 1035 nsAutoString str2(t2b, fragLen);
michael@0 1036 printf("2 byte, '%s'\n", NS_LossyConvertUTF16toASCII(str2).get());
michael@0 1037 #endif
michael@0 1038 }
michael@0 1039 else
michael@0 1040 {
michael@0 1041 t1b = frag->Get1b();
michael@0 1042 t2b = nullptr;
michael@0 1043 #ifdef DEBUG_FIND
michael@0 1044 nsAutoCString str1(t1b, fragLen);
michael@0 1045 printf("1 byte, '%s'\n", str1.get());
michael@0 1046 #endif
michael@0 1047 }
michael@0 1048 }
michael@0 1049 else // still on the old node
michael@0 1050 {
michael@0 1051 // Still on the old node. Advance the pointers,
michael@0 1052 // then see if we need to pull a new node.
michael@0 1053 findex += incr;
michael@0 1054 #ifdef DEBUG_FIND
michael@0 1055 printf("Same node -- (%d, %d)\n", pindex, findex);
michael@0 1056 #endif
michael@0 1057 if (mFindBackward ? (findex < 0) : (findex >= fragLen))
michael@0 1058 {
michael@0 1059 #ifdef DEBUG_FIND
michael@0 1060 printf("Will need to pull a new node: mAO = %d, frag len=%d\n",
michael@0 1061 matchAnchorOffset, fragLen);
michael@0 1062 #endif
michael@0 1063 // Done with this node. Pull a new one.
michael@0 1064 frag = nullptr;
michael@0 1065 continue;
michael@0 1066 }
michael@0 1067 }
michael@0 1068
michael@0 1069 // Have we gone past the endpoint yet?
michael@0 1070 // If we have, and we're not in the middle of a match, return.
michael@0 1071 if (mIterNode == endNode &&
michael@0 1072 ((mFindBackward && (findex < endOffset)) ||
michael@0 1073 (!mFindBackward && (findex > endOffset))))
michael@0 1074 {
michael@0 1075 ResetAll();
michael@0 1076 return NS_OK;
michael@0 1077 }
michael@0 1078
michael@0 1079 // The two characters we'll be comparing:
michael@0 1080 char16_t c = (t2b ? t2b[findex] : CHAR_TO_UNICHAR(t1b[findex]));
michael@0 1081 char16_t patc = patStr[pindex];
michael@0 1082
michael@0 1083 #ifdef DEBUG_FIND
michael@0 1084 printf("Comparing '%c'=%x to '%c' (%d of %d), findex=%d%s\n",
michael@0 1085 (char)c, (int)c, patc, pindex, patLen, findex,
michael@0 1086 inWhitespace ? " (inWhitespace)" : "");
michael@0 1087 #endif
michael@0 1088
michael@0 1089 // Do we need to go back to non-whitespace mode?
michael@0 1090 // If inWhitespace, then this space in the pat str
michael@0 1091 // has already matched at least one space in the document.
michael@0 1092 if (inWhitespace && !IsSpace(c))
michael@0 1093 {
michael@0 1094 inWhitespace = false;
michael@0 1095 pindex += incr;
michael@0 1096 #ifdef DEBUG
michael@0 1097 // This shouldn't happen -- if we were still matching, and we
michael@0 1098 // were at the end of the pat string, then we should have
michael@0 1099 // caught it in the last iteration and returned success.
michael@0 1100 if (OVERFLOW_PINDEX)
michael@0 1101 NS_ASSERTION(false, "Missed a whitespace match");
michael@0 1102 #endif
michael@0 1103 patc = patStr[pindex];
michael@0 1104 }
michael@0 1105 if (!inWhitespace && IsSpace(patc))
michael@0 1106 inWhitespace = true;
michael@0 1107
michael@0 1108 // convert to lower case if necessary
michael@0 1109 else if (!inWhitespace && !mCaseSensitive && IsUpperCase(c))
michael@0 1110 c = ToLowerCase(c);
michael@0 1111
michael@0 1112 switch (c) {
michael@0 1113 // ignore soft hyphens in the document
michael@0 1114 case CH_SHY:
michael@0 1115 continue;
michael@0 1116 // treat curly and straight quotes as identical
michael@0 1117 case CH_LEFT_SINGLE_QUOTE:
michael@0 1118 case CH_RIGHT_SINGLE_QUOTE:
michael@0 1119 c = CH_APOSTROPHE;
michael@0 1120 break;
michael@0 1121 case CH_LEFT_DOUBLE_QUOTE:
michael@0 1122 case CH_RIGHT_DOUBLE_QUOTE:
michael@0 1123 c = CH_QUOTE;
michael@0 1124 break;
michael@0 1125 }
michael@0 1126
michael@0 1127 switch (patc) {
michael@0 1128 // treat curly and straight quotes as identical
michael@0 1129 case CH_LEFT_SINGLE_QUOTE:
michael@0 1130 case CH_RIGHT_SINGLE_QUOTE:
michael@0 1131 patc = CH_APOSTROPHE;
michael@0 1132 break;
michael@0 1133 case CH_LEFT_DOUBLE_QUOTE:
michael@0 1134 case CH_RIGHT_DOUBLE_QUOTE:
michael@0 1135 patc = CH_QUOTE;
michael@0 1136 break;
michael@0 1137 }
michael@0 1138
michael@0 1139 // a '\n' between CJ characters is ignored
michael@0 1140 if (pindex != (mFindBackward ? patLen : 0) && c != patc && !inWhitespace) {
michael@0 1141 if (c == '\n' && t2b && IS_CJ_CHAR(prevChar)) {
michael@0 1142 int32_t nindex = findex + incr;
michael@0 1143 if (mFindBackward ? (nindex >= 0) : (nindex < fragLen)) {
michael@0 1144 if (IS_CJ_CHAR(t2b[nindex]))
michael@0 1145 continue;
michael@0 1146 }
michael@0 1147 }
michael@0 1148 }
michael@0 1149
michael@0 1150 // Compare
michael@0 1151 if ((c == patc) || (inWhitespace && IsSpace(c)))
michael@0 1152 {
michael@0 1153 prevChar = c;
michael@0 1154 #ifdef DEBUG_FIND
michael@0 1155 if (inWhitespace)
michael@0 1156 printf("YES (whitespace)(%d of %d)\n", pindex, patLen);
michael@0 1157 else
michael@0 1158 printf("YES! '%c' == '%c' (%d of %d)\n", c, patc, pindex, patLen);
michael@0 1159 #endif
michael@0 1160
michael@0 1161 // Save the range anchors if we haven't already:
michael@0 1162 if (!matchAnchorNode) {
michael@0 1163 matchAnchorNode = mIterNode;
michael@0 1164 matchAnchorOffset = findex;
michael@0 1165 }
michael@0 1166
michael@0 1167 // Are we done?
michael@0 1168 if (DONE_WITH_PINDEX)
michael@0 1169 // Matched the whole string!
michael@0 1170 {
michael@0 1171 #ifdef DEBUG_FIND
michael@0 1172 printf("Found a match!\n");
michael@0 1173 #endif
michael@0 1174
michael@0 1175 // Make the range:
michael@0 1176 nsCOMPtr<nsIDOMNode> startParent;
michael@0 1177 nsCOMPtr<nsIDOMNode> endParent;
michael@0 1178 nsCOMPtr<nsIDOMRange> range = CreateRange(tc);
michael@0 1179 if (range)
michael@0 1180 {
michael@0 1181 int32_t matchStartOffset, matchEndOffset;
michael@0 1182 // convert char index to range point:
michael@0 1183 int32_t mao = matchAnchorOffset + (mFindBackward ? 1 : 0);
michael@0 1184 if (mFindBackward)
michael@0 1185 {
michael@0 1186 startParent = do_QueryInterface(tc);
michael@0 1187 endParent = matchAnchorNode;
michael@0 1188 matchStartOffset = findex;
michael@0 1189 matchEndOffset = mao;
michael@0 1190 }
michael@0 1191 else
michael@0 1192 {
michael@0 1193 startParent = matchAnchorNode;
michael@0 1194 endParent = do_QueryInterface(tc);
michael@0 1195 matchStartOffset = mao;
michael@0 1196 matchEndOffset = findex+1;
michael@0 1197 }
michael@0 1198 if (startParent && endParent &&
michael@0 1199 IsVisibleNode(startParent) && IsVisibleNode(endParent))
michael@0 1200 {
michael@0 1201 range->SetStart(startParent, matchStartOffset);
michael@0 1202 range->SetEnd(endParent, matchEndOffset);
michael@0 1203 *aRangeRet = range.get();
michael@0 1204 NS_ADDREF(*aRangeRet);
michael@0 1205 }
michael@0 1206 else {
michael@0 1207 startParent = nullptr; // This match is no good -- invisible or bad range
michael@0 1208 }
michael@0 1209 }
michael@0 1210
michael@0 1211 if (startParent) {
michael@0 1212 // If startParent == nullptr, we didn't successfully make range
michael@0 1213 // or, we didn't make a range because the start or end node were invisible
michael@0 1214 // Reset the offset to the other end of the found string:
michael@0 1215 mIterOffset = findex + (mFindBackward ? 1 : 0);
michael@0 1216 #ifdef DEBUG_FIND
michael@0 1217 printf("mIterOffset = %d, mIterNode = ", mIterOffset);
michael@0 1218 DumpNode(mIterNode);
michael@0 1219 #endif
michael@0 1220
michael@0 1221 ResetAll();
michael@0 1222 return NS_OK;
michael@0 1223 }
michael@0 1224 matchAnchorNode = nullptr; // This match is no good, continue on in document
michael@0 1225 }
michael@0 1226
michael@0 1227 if (matchAnchorNode) {
michael@0 1228 // Not done, but still matching.
michael@0 1229 // Advance and loop around for the next characters.
michael@0 1230 // But don't advance from a space to a non-space:
michael@0 1231 if (!inWhitespace || DONE_WITH_PINDEX || IsSpace(patStr[pindex+incr]))
michael@0 1232 {
michael@0 1233 pindex += incr;
michael@0 1234 inWhitespace = false;
michael@0 1235 #ifdef DEBUG_FIND
michael@0 1236 printf("Advancing pindex to %d\n", pindex);
michael@0 1237 #endif
michael@0 1238 }
michael@0 1239
michael@0 1240 continue;
michael@0 1241 }
michael@0 1242 }
michael@0 1243
michael@0 1244 #ifdef DEBUG_FIND
michael@0 1245 printf("NOT: %c == %c\n", c, patc);
michael@0 1246 #endif
michael@0 1247
michael@0 1248 // If we didn't match, go back to the beginning of patStr,
michael@0 1249 // and set findex back to the next char after
michael@0 1250 // we started the current match.
michael@0 1251 if (matchAnchorNode) // we're ending a partial match
michael@0 1252 {
michael@0 1253 findex = matchAnchorOffset;
michael@0 1254 mIterOffset = matchAnchorOffset;
michael@0 1255 // +incr will be added to findex when we continue
michael@0 1256
michael@0 1257 // Are we going back to a previous node?
michael@0 1258 if (matchAnchorNode != mIterNode)
michael@0 1259 {
michael@0 1260 nsCOMPtr<nsIContent> content (do_QueryInterface(matchAnchorNode));
michael@0 1261 DebugOnly<nsresult> rv = NS_ERROR_UNEXPECTED;
michael@0 1262 if (content)
michael@0 1263 rv = mIterator->PositionAt(content);
michael@0 1264 frag = 0;
michael@0 1265 NS_ASSERTION(NS_SUCCEEDED(rv), "Text content wasn't nsIContent!");
michael@0 1266 #ifdef DEBUG_FIND
michael@0 1267 printf("Repositioned anchor node\n");
michael@0 1268 #endif
michael@0 1269 }
michael@0 1270 #ifdef DEBUG_FIND
michael@0 1271 printf("Ending a partial match; findex -> %d, mIterOffset -> %d\n",
michael@0 1272 findex, mIterOffset);
michael@0 1273 #endif
michael@0 1274 }
michael@0 1275 matchAnchorNode = nullptr;
michael@0 1276 matchAnchorOffset = 0;
michael@0 1277 inWhitespace = false;
michael@0 1278 pindex = (mFindBackward ? patLen : 0);
michael@0 1279 #ifdef DEBUG_FIND
michael@0 1280 printf("Setting findex back to %d, pindex to %d\n", findex, pindex);
michael@0 1281
michael@0 1282 #endif
michael@0 1283 } // end while loop
michael@0 1284
michael@0 1285 // Out of nodes, and didn't match.
michael@0 1286 ResetAll();
michael@0 1287 return NS_OK;
michael@0 1288 }
michael@0 1289
michael@0 1290 /* static */
michael@0 1291 already_AddRefed<nsIDOMRange>
michael@0 1292 nsFind::CreateRange(nsINode* aNode)
michael@0 1293 {
michael@0 1294 nsRefPtr<nsRange> range = new nsRange(aNode);
michael@0 1295 range->SetMaySpanAnonymousSubtrees(true);
michael@0 1296 return range.forget();
michael@0 1297 }

mercurial