content/base/src/nsContentIterator.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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 #include "nsISupports.h"
michael@0 7 #include "nsIDOMNodeList.h"
michael@0 8 #include "nsIContentIterator.h"
michael@0 9 #include "nsRange.h"
michael@0 10 #include "nsIContent.h"
michael@0 11 #include "nsCOMPtr.h"
michael@0 12 #include "nsTArray.h"
michael@0 13 #include "nsContentUtils.h"
michael@0 14 #include "nsINode.h"
michael@0 15 #include "nsCycleCollectionParticipant.h"
michael@0 16
michael@0 17 // couple of utility static functs
michael@0 18
michael@0 19 ///////////////////////////////////////////////////////////////////////////
michael@0 20 // NodeToParentOffset: returns the node's parent and offset.
michael@0 21 //
michael@0 22
michael@0 23 static nsINode*
michael@0 24 NodeToParentOffset(nsINode* aNode, int32_t* aOffset)
michael@0 25 {
michael@0 26 *aOffset = 0;
michael@0 27
michael@0 28 nsINode* parent = aNode->GetParentNode();
michael@0 29
michael@0 30 if (parent) {
michael@0 31 *aOffset = parent->IndexOf(aNode);
michael@0 32 }
michael@0 33
michael@0 34 return parent;
michael@0 35 }
michael@0 36
michael@0 37 ///////////////////////////////////////////////////////////////////////////
michael@0 38 // NodeIsInTraversalRange: returns true if content is visited during
michael@0 39 // the traversal of the range in the specified mode.
michael@0 40 //
michael@0 41 static bool
michael@0 42 NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
michael@0 43 nsINode* aStartNode, int32_t aStartOffset,
michael@0 44 nsINode* aEndNode, int32_t aEndOffset)
michael@0 45 {
michael@0 46 if (!aStartNode || !aEndNode || !aNode) {
michael@0 47 return false;
michael@0 48 }
michael@0 49
michael@0 50 // If a chardata node contains an end point of the traversal range, it is
michael@0 51 // always in the traversal range.
michael@0 52 if (aNode->IsNodeOfType(nsINode::eDATA_NODE) &&
michael@0 53 (aNode == aStartNode || aNode == aEndNode)) {
michael@0 54 return true;
michael@0 55 }
michael@0 56
michael@0 57 nsINode* parent = aNode->GetParentNode();
michael@0 58 if (!parent) {
michael@0 59 return false;
michael@0 60 }
michael@0 61
michael@0 62 int32_t indx = parent->IndexOf(aNode);
michael@0 63
michael@0 64 if (!aIsPreMode) {
michael@0 65 ++indx;
michael@0 66 }
michael@0 67
michael@0 68 return nsContentUtils::ComparePoints(aStartNode, aStartOffset,
michael@0 69 parent, indx) <= 0 &&
michael@0 70 nsContentUtils::ComparePoints(aEndNode, aEndOffset,
michael@0 71 parent, indx) >= 0;
michael@0 72 }
michael@0 73
michael@0 74
michael@0 75
michael@0 76 /*
michael@0 77 * A simple iterator class for traversing the content in "close tag" order
michael@0 78 */
michael@0 79 class nsContentIterator : public nsIContentIterator
michael@0 80 {
michael@0 81 public:
michael@0 82 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
michael@0 83 NS_DECL_CYCLE_COLLECTION_CLASS(nsContentIterator)
michael@0 84
michael@0 85 explicit nsContentIterator(bool aPre);
michael@0 86 virtual ~nsContentIterator();
michael@0 87
michael@0 88 // nsIContentIterator interface methods ------------------------------
michael@0 89
michael@0 90 virtual nsresult Init(nsINode* aRoot);
michael@0 91
michael@0 92 virtual nsresult Init(nsIDOMRange* aRange);
michael@0 93
michael@0 94 virtual void First();
michael@0 95
michael@0 96 virtual void Last();
michael@0 97
michael@0 98 virtual void Next();
michael@0 99
michael@0 100 virtual void Prev();
michael@0 101
michael@0 102 virtual nsINode* GetCurrentNode();
michael@0 103
michael@0 104 virtual bool IsDone();
michael@0 105
michael@0 106 virtual nsresult PositionAt(nsINode* aCurNode);
michael@0 107
michael@0 108 protected:
michael@0 109
michael@0 110 // Recursively get the deepest first/last child of aRoot. This will return
michael@0 111 // aRoot itself if it has no children.
michael@0 112 nsINode* GetDeepFirstChild(nsINode* aRoot,
michael@0 113 nsTArray<int32_t>* aIndexes = nullptr);
michael@0 114 nsIContent* GetDeepFirstChild(nsIContent* aRoot,
michael@0 115 nsTArray<int32_t>* aIndexes = nullptr);
michael@0 116 nsINode* GetDeepLastChild(nsINode* aRoot,
michael@0 117 nsTArray<int32_t>* aIndexes = nullptr);
michael@0 118 nsIContent* GetDeepLastChild(nsIContent* aRoot,
michael@0 119 nsTArray<int32_t>* aIndexes = nullptr);
michael@0 120
michael@0 121 // Get the next/previous sibling of aNode, or its parent's, or grandparent's,
michael@0 122 // etc. Returns null if aNode and all its ancestors have no next/previous
michael@0 123 // sibling.
michael@0 124 nsIContent* GetNextSibling(nsINode* aNode,
michael@0 125 nsTArray<int32_t>* aIndexes = nullptr);
michael@0 126 nsIContent* GetPrevSibling(nsINode* aNode,
michael@0 127 nsTArray<int32_t>* aIndexes = nullptr);
michael@0 128
michael@0 129 nsINode* NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes = nullptr);
michael@0 130 nsINode* PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes = nullptr);
michael@0 131
michael@0 132 // WARNING: This function is expensive
michael@0 133 nsresult RebuildIndexStack();
michael@0 134
michael@0 135 void MakeEmpty();
michael@0 136
michael@0 137 virtual void LastRelease();
michael@0 138
michael@0 139 nsCOMPtr<nsINode> mCurNode;
michael@0 140 nsCOMPtr<nsINode> mFirst;
michael@0 141 nsCOMPtr<nsINode> mLast;
michael@0 142 nsCOMPtr<nsINode> mCommonParent;
michael@0 143
michael@0 144 // used by nsContentIterator to cache indices
michael@0 145 nsAutoTArray<int32_t, 8> mIndexes;
michael@0 146
michael@0 147 // used by nsSubtreeIterator to cache indices. Why put them in the base
michael@0 148 // class? Because otherwise I have to duplicate the routines GetNextSibling
michael@0 149 // etc across both classes, with slight variations for caching. Or
michael@0 150 // alternately, create a base class for the cache itself and have all the
michael@0 151 // cache manipulation go through a vptr. I think this is the best space and
michael@0 152 // speed combo, even though it's ugly.
michael@0 153 int32_t mCachedIndex;
michael@0 154 // another note about mCachedIndex: why should the subtree iterator use a
michael@0 155 // trivial cached index instead of the mre robust array of indicies (which is
michael@0 156 // what the basic content iterator uses)? The reason is that subtree
michael@0 157 // iterators do not do much transitioning between parents and children. They
michael@0 158 // tend to stay at the same level. In fact, you can prove (though I won't
michael@0 159 // attempt it here) that they change levels at most n+m times, where n is the
michael@0 160 // height of the parent hierarchy from the range start to the common
michael@0 161 // ancestor, and m is the the height of the parent hierarchy from the range
michael@0 162 // end to the common ancestor. If we used the index array, we would pay the
michael@0 163 // price up front for n, and then pay the cost for m on the fly later on.
michael@0 164 // With the simple cache, we only "pay as we go". Either way, we call
michael@0 165 // IndexOf() once for each change of level in the hierarchy. Since a trivial
michael@0 166 // index is much simpler, we use it for the subtree iterator.
michael@0 167
michael@0 168 bool mIsDone;
michael@0 169 bool mPre;
michael@0 170
michael@0 171 private:
michael@0 172
michael@0 173 // no copies or assigns FIX ME
michael@0 174 nsContentIterator(const nsContentIterator&);
michael@0 175 nsContentIterator& operator=(const nsContentIterator&);
michael@0 176
michael@0 177 };
michael@0 178
michael@0 179
michael@0 180 /******************************************************
michael@0 181 * repository cruft
michael@0 182 ******************************************************/
michael@0 183
michael@0 184 already_AddRefed<nsIContentIterator>
michael@0 185 NS_NewContentIterator()
michael@0 186 {
michael@0 187 nsCOMPtr<nsIContentIterator> iter = new nsContentIterator(false);
michael@0 188 return iter.forget();
michael@0 189 }
michael@0 190
michael@0 191
michael@0 192 already_AddRefed<nsIContentIterator>
michael@0 193 NS_NewPreContentIterator()
michael@0 194 {
michael@0 195 nsCOMPtr<nsIContentIterator> iter = new nsContentIterator(true);
michael@0 196 return iter.forget();
michael@0 197 }
michael@0 198
michael@0 199
michael@0 200 /******************************************************
michael@0 201 * XPCOM cruft
michael@0 202 ******************************************************/
michael@0 203
michael@0 204 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentIterator)
michael@0 205 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsContentIterator,
michael@0 206 LastRelease())
michael@0 207
michael@0 208 NS_INTERFACE_MAP_BEGIN(nsContentIterator)
michael@0 209 NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
michael@0 210 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentIterator)
michael@0 211 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsContentIterator)
michael@0 212 NS_INTERFACE_MAP_END
michael@0 213
michael@0 214 NS_IMPL_CYCLE_COLLECTION(nsContentIterator,
michael@0 215 mCurNode,
michael@0 216 mFirst,
michael@0 217 mLast,
michael@0 218 mCommonParent)
michael@0 219
michael@0 220 void
michael@0 221 nsContentIterator::LastRelease()
michael@0 222 {
michael@0 223 mCurNode = nullptr;
michael@0 224 mFirst = nullptr;
michael@0 225 mLast = nullptr;
michael@0 226 mCommonParent = nullptr;
michael@0 227 }
michael@0 228
michael@0 229 /******************************************************
michael@0 230 * constructor/destructor
michael@0 231 ******************************************************/
michael@0 232
michael@0 233 nsContentIterator::nsContentIterator(bool aPre) :
michael@0 234 // don't need to explicitly initialize |nsCOMPtr|s, they will automatically
michael@0 235 // be nullptr
michael@0 236 mCachedIndex(0), mIsDone(false), mPre(aPre)
michael@0 237 {
michael@0 238 }
michael@0 239
michael@0 240
michael@0 241 nsContentIterator::~nsContentIterator()
michael@0 242 {
michael@0 243 }
michael@0 244
michael@0 245
michael@0 246 /******************************************************
michael@0 247 * Init routines
michael@0 248 ******************************************************/
michael@0 249
michael@0 250
michael@0 251 nsresult
michael@0 252 nsContentIterator::Init(nsINode* aRoot)
michael@0 253 {
michael@0 254 if (!aRoot) {
michael@0 255 return NS_ERROR_NULL_POINTER;
michael@0 256 }
michael@0 257
michael@0 258 mIsDone = false;
michael@0 259 mIndexes.Clear();
michael@0 260
michael@0 261 if (mPre) {
michael@0 262 mFirst = aRoot;
michael@0 263 mLast = GetDeepLastChild(aRoot);
michael@0 264 } else {
michael@0 265 mFirst = GetDeepFirstChild(aRoot);
michael@0 266 mLast = aRoot;
michael@0 267 }
michael@0 268
michael@0 269 mCommonParent = aRoot;
michael@0 270 mCurNode = mFirst;
michael@0 271 RebuildIndexStack();
michael@0 272 return NS_OK;
michael@0 273 }
michael@0 274
michael@0 275 nsresult
michael@0 276 nsContentIterator::Init(nsIDOMRange* aDOMRange)
michael@0 277 {
michael@0 278 NS_ENSURE_ARG_POINTER(aDOMRange);
michael@0 279 nsRange* range = static_cast<nsRange*>(aDOMRange);
michael@0 280
michael@0 281 mIsDone = false;
michael@0 282
michael@0 283 // get common content parent
michael@0 284 mCommonParent = range->GetCommonAncestor();
michael@0 285 NS_ENSURE_TRUE(mCommonParent, NS_ERROR_FAILURE);
michael@0 286
michael@0 287 // get the start node and offset
michael@0 288 int32_t startIndx = range->StartOffset();
michael@0 289 nsINode* startNode = range->GetStartParent();
michael@0 290 NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
michael@0 291
michael@0 292 // get the end node and offset
michael@0 293 int32_t endIndx = range->EndOffset();
michael@0 294 nsINode* endNode = range->GetEndParent();
michael@0 295 NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
michael@0 296
michael@0 297 bool startIsData = startNode->IsNodeOfType(nsINode::eDATA_NODE);
michael@0 298
michael@0 299 // short circuit when start node == end node
michael@0 300 if (startNode == endNode) {
michael@0 301 // Check to see if we have a collapsed range, if so, there is nothing to
michael@0 302 // iterate over.
michael@0 303 //
michael@0 304 // XXX: CharacterDataNodes (text nodes) are currently an exception, since
michael@0 305 // we always want to be able to iterate text nodes at the end points
michael@0 306 // of a range.
michael@0 307
michael@0 308 if (!startIsData && startIndx == endIndx) {
michael@0 309 MakeEmpty();
michael@0 310 return NS_OK;
michael@0 311 }
michael@0 312
michael@0 313 if (startIsData) {
michael@0 314 // It's a character data node.
michael@0 315 mFirst = startNode->AsContent();
michael@0 316 mLast = mFirst;
michael@0 317 mCurNode = mFirst;
michael@0 318
michael@0 319 RebuildIndexStack();
michael@0 320 return NS_OK;
michael@0 321 }
michael@0 322 }
michael@0 323
michael@0 324 // Find first node in range.
michael@0 325
michael@0 326 nsIContent* cChild = nullptr;
michael@0 327
michael@0 328 if (!startIsData && startNode->HasChildren()) {
michael@0 329 cChild = startNode->GetChildAt(startIndx);
michael@0 330 }
michael@0 331
michael@0 332 if (!cChild) {
michael@0 333 // no children, must be a text node
michael@0 334 //
michael@0 335 // XXXbz no children might also just mean no children. So I'm not
michael@0 336 // sure what that comment above is talking about.
michael@0 337
michael@0 338 if (mPre) {
michael@0 339 // XXX: In the future, if start offset is after the last
michael@0 340 // character in the cdata node, should we set mFirst to
michael@0 341 // the next sibling?
michael@0 342
michael@0 343 if (!startIsData) {
michael@0 344 mFirst = GetNextSibling(startNode);
michael@0 345
michael@0 346 // Does mFirst node really intersect the range? The range could be
michael@0 347 // 'degenerate', i.e., not collapsed but still contain no content.
michael@0 348
michael@0 349 if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, startNode,
michael@0 350 startIndx, endNode, endIndx)) {
michael@0 351 mFirst = nullptr;
michael@0 352 }
michael@0 353 } else {
michael@0 354 mFirst = startNode->AsContent();
michael@0 355 }
michael@0 356 } else {
michael@0 357 // post-order
michael@0 358 if (startNode->IsContent()) {
michael@0 359 mFirst = startNode->AsContent();
michael@0 360 } else {
michael@0 361 // What else can we do?
michael@0 362 mFirst = nullptr;
michael@0 363 }
michael@0 364 }
michael@0 365 } else {
michael@0 366 if (mPre) {
michael@0 367 mFirst = cChild;
michael@0 368 } else {
michael@0 369 // post-order
michael@0 370 mFirst = GetDeepFirstChild(cChild);
michael@0 371
michael@0 372 // Does mFirst node really intersect the range? The range could be
michael@0 373 // 'degenerate', i.e., not collapsed but still contain no content.
michael@0 374
michael@0 375 if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx,
michael@0 376 endNode, endIndx)) {
michael@0 377 mFirst = nullptr;
michael@0 378 }
michael@0 379 }
michael@0 380 }
michael@0 381
michael@0 382
michael@0 383 // Find last node in range.
michael@0 384
michael@0 385 bool endIsData = endNode->IsNodeOfType(nsINode::eDATA_NODE);
michael@0 386
michael@0 387 if (endIsData || !endNode->HasChildren() || endIndx == 0) {
michael@0 388 if (mPre) {
michael@0 389 if (endNode->IsContent()) {
michael@0 390 mLast = endNode->AsContent();
michael@0 391 } else {
michael@0 392 // Not much else to do here...
michael@0 393 mLast = nullptr;
michael@0 394 }
michael@0 395 } else {
michael@0 396 // post-order
michael@0 397 //
michael@0 398 // XXX: In the future, if end offset is before the first character in the
michael@0 399 // cdata node, should we set mLast to the prev sibling?
michael@0 400
michael@0 401 if (!endIsData) {
michael@0 402 mLast = GetPrevSibling(endNode);
michael@0 403
michael@0 404 if (!NodeIsInTraversalRange(mLast, mPre, startNode, startIndx,
michael@0 405 endNode, endIndx)) {
michael@0 406 mLast = nullptr;
michael@0 407 }
michael@0 408 } else {
michael@0 409 mLast = endNode->AsContent();
michael@0 410 }
michael@0 411 }
michael@0 412 } else {
michael@0 413 int32_t indx = endIndx;
michael@0 414
michael@0 415 cChild = endNode->GetChildAt(--indx);
michael@0 416
michael@0 417 if (!cChild) {
michael@0 418 // No child at offset!
michael@0 419 NS_NOTREACHED("nsContentIterator::nsContentIterator");
michael@0 420 return NS_ERROR_FAILURE;
michael@0 421 }
michael@0 422
michael@0 423 if (mPre) {
michael@0 424 mLast = GetDeepLastChild(cChild);
michael@0 425
michael@0 426 if (!NodeIsInTraversalRange(mLast, mPre, startNode, startIndx,
michael@0 427 endNode, endIndx)) {
michael@0 428 mLast = nullptr;
michael@0 429 }
michael@0 430 } else {
michael@0 431 // post-order
michael@0 432 mLast = cChild;
michael@0 433 }
michael@0 434 }
michael@0 435
michael@0 436 // If either first or last is null, they both have to be null!
michael@0 437
michael@0 438 if (!mFirst || !mLast) {
michael@0 439 mFirst = nullptr;
michael@0 440 mLast = nullptr;
michael@0 441 }
michael@0 442
michael@0 443 mCurNode = mFirst;
michael@0 444 mIsDone = !mCurNode;
michael@0 445
michael@0 446 if (!mCurNode) {
michael@0 447 mIndexes.Clear();
michael@0 448 } else {
michael@0 449 RebuildIndexStack();
michael@0 450 }
michael@0 451
michael@0 452 return NS_OK;
michael@0 453 }
michael@0 454
michael@0 455
michael@0 456 /******************************************************
michael@0 457 * Helper routines
michael@0 458 ******************************************************/
michael@0 459 // WARNING: This function is expensive
michael@0 460 nsresult
michael@0 461 nsContentIterator::RebuildIndexStack()
michael@0 462 {
michael@0 463 // Make sure we start at the right indexes on the stack! Build array up
michael@0 464 // to common parent of start and end. Perhaps it's too many entries, but
michael@0 465 // that's far better than too few.
michael@0 466 nsINode* parent;
michael@0 467 nsINode* current;
michael@0 468
michael@0 469 mIndexes.Clear();
michael@0 470 current = mCurNode;
michael@0 471 if (!current) {
michael@0 472 return NS_OK;
michael@0 473 }
michael@0 474
michael@0 475 while (current != mCommonParent) {
michael@0 476 parent = current->GetParentNode();
michael@0 477
michael@0 478 if (!parent) {
michael@0 479 return NS_ERROR_FAILURE;
michael@0 480 }
michael@0 481
michael@0 482 mIndexes.InsertElementAt(0, parent->IndexOf(current));
michael@0 483
michael@0 484 current = parent;
michael@0 485 }
michael@0 486
michael@0 487 return NS_OK;
michael@0 488 }
michael@0 489
michael@0 490 void
michael@0 491 nsContentIterator::MakeEmpty()
michael@0 492 {
michael@0 493 mCurNode = nullptr;
michael@0 494 mFirst = nullptr;
michael@0 495 mLast = nullptr;
michael@0 496 mCommonParent = nullptr;
michael@0 497 mIsDone = true;
michael@0 498 mIndexes.Clear();
michael@0 499 }
michael@0 500
michael@0 501 nsINode*
michael@0 502 nsContentIterator::GetDeepFirstChild(nsINode* aRoot,
michael@0 503 nsTArray<int32_t>* aIndexes)
michael@0 504 {
michael@0 505 if (!aRoot || !aRoot->HasChildren()) {
michael@0 506 return aRoot;
michael@0 507 }
michael@0 508 // We can't pass aRoot itself to the full GetDeepFirstChild, because that
michael@0 509 // will only take nsIContent and aRoot might be a document. Pass aRoot's
michael@0 510 // child, but be sure to preserve aIndexes.
michael@0 511 if (aIndexes) {
michael@0 512 aIndexes->AppendElement(0);
michael@0 513 }
michael@0 514 return GetDeepFirstChild(aRoot->GetFirstChild(), aIndexes);
michael@0 515 }
michael@0 516
michael@0 517 nsIContent*
michael@0 518 nsContentIterator::GetDeepFirstChild(nsIContent* aRoot,
michael@0 519 nsTArray<int32_t>* aIndexes)
michael@0 520 {
michael@0 521 if (!aRoot) {
michael@0 522 return nullptr;
michael@0 523 }
michael@0 524
michael@0 525 nsIContent* node = aRoot;
michael@0 526 nsIContent* child = node->GetFirstChild();
michael@0 527
michael@0 528 while (child) {
michael@0 529 if (aIndexes) {
michael@0 530 // Add this node to the stack of indexes
michael@0 531 aIndexes->AppendElement(0);
michael@0 532 }
michael@0 533 node = child;
michael@0 534 child = node->GetFirstChild();
michael@0 535 }
michael@0 536
michael@0 537 return node;
michael@0 538 }
michael@0 539
michael@0 540 nsINode*
michael@0 541 nsContentIterator::GetDeepLastChild(nsINode* aRoot,
michael@0 542 nsTArray<int32_t>* aIndexes)
michael@0 543 {
michael@0 544 if (!aRoot || !aRoot->HasChildren()) {
michael@0 545 return aRoot;
michael@0 546 }
michael@0 547 // We can't pass aRoot itself to the full GetDeepLastChild, because that will
michael@0 548 // only take nsIContent and aRoot might be a document. Pass aRoot's child,
michael@0 549 // but be sure to preserve aIndexes.
michael@0 550 if (aIndexes) {
michael@0 551 aIndexes->AppendElement(aRoot->GetChildCount() - 1);
michael@0 552 }
michael@0 553 return GetDeepLastChild(aRoot->GetLastChild(), aIndexes);
michael@0 554 }
michael@0 555
michael@0 556 nsIContent*
michael@0 557 nsContentIterator::GetDeepLastChild(nsIContent* aRoot,
michael@0 558 nsTArray<int32_t>* aIndexes)
michael@0 559 {
michael@0 560 if (!aRoot) {
michael@0 561 return nullptr;
michael@0 562 }
michael@0 563
michael@0 564 nsIContent* node = aRoot;
michael@0 565 int32_t numChildren = node->GetChildCount();
michael@0 566
michael@0 567 while (numChildren) {
michael@0 568 nsIContent* child = node->GetChildAt(--numChildren);
michael@0 569
michael@0 570 if (aIndexes) {
michael@0 571 // Add this node to the stack of indexes
michael@0 572 aIndexes->AppendElement(numChildren);
michael@0 573 }
michael@0 574 numChildren = child->GetChildCount();
michael@0 575 node = child;
michael@0 576 }
michael@0 577
michael@0 578 return node;
michael@0 579 }
michael@0 580
michael@0 581 // Get the next sibling, or parent's next sibling, or grandpa's next sibling...
michael@0 582 nsIContent*
michael@0 583 nsContentIterator::GetNextSibling(nsINode* aNode,
michael@0 584 nsTArray<int32_t>* aIndexes)
michael@0 585 {
michael@0 586 if (!aNode) {
michael@0 587 return nullptr;
michael@0 588 }
michael@0 589
michael@0 590 nsINode* parent = aNode->GetParentNode();
michael@0 591 if (!parent) {
michael@0 592 return nullptr;
michael@0 593 }
michael@0 594
michael@0 595 int32_t indx = 0;
michael@0 596
michael@0 597 NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
michael@0 598 "ContentIterator stack underflow");
michael@0 599 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 600 // use the last entry on the Indexes array for the current index
michael@0 601 indx = (*aIndexes)[aIndexes->Length()-1];
michael@0 602 } else {
michael@0 603 indx = mCachedIndex;
michael@0 604 }
michael@0 605
michael@0 606 // reverify that the index of the current node hasn't changed.
michael@0 607 // not super cheap, but a lot cheaper than IndexOf(), and still O(1).
michael@0 608 // ignore result this time - the index may now be out of range.
michael@0 609 nsIContent* sib = parent->GetChildAt(indx);
michael@0 610 if (sib != aNode) {
michael@0 611 // someone changed our index - find the new index the painful way
michael@0 612 indx = parent->IndexOf(aNode);
michael@0 613 }
michael@0 614
michael@0 615 // indx is now canonically correct
michael@0 616 if ((sib = parent->GetChildAt(++indx))) {
michael@0 617 // update index cache
michael@0 618 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 619 aIndexes->ElementAt(aIndexes->Length()-1) = indx;
michael@0 620 } else {
michael@0 621 mCachedIndex = indx;
michael@0 622 }
michael@0 623 } else {
michael@0 624 if (parent != mCommonParent) {
michael@0 625 if (aIndexes) {
michael@0 626 // pop node off the stack, go up one level and return parent or fail.
michael@0 627 // Don't leave the index empty, especially if we're
michael@0 628 // returning nullptr. This confuses other parts of the code.
michael@0 629 if (aIndexes->Length() > 1) {
michael@0 630 aIndexes->RemoveElementAt(aIndexes->Length()-1);
michael@0 631 }
michael@0 632 }
michael@0 633 }
michael@0 634
michael@0 635 // ok to leave cache out of date here if parent == mCommonParent?
michael@0 636 sib = GetNextSibling(parent, aIndexes);
michael@0 637 }
michael@0 638
michael@0 639 return sib;
michael@0 640 }
michael@0 641
michael@0 642 // Get the prev sibling, or parent's prev sibling, or grandpa's prev sibling...
michael@0 643 nsIContent*
michael@0 644 nsContentIterator::GetPrevSibling(nsINode* aNode,
michael@0 645 nsTArray<int32_t>* aIndexes)
michael@0 646 {
michael@0 647 if (!aNode) {
michael@0 648 return nullptr;
michael@0 649 }
michael@0 650
michael@0 651 nsINode* parent = aNode->GetParentNode();
michael@0 652 if (!parent) {
michael@0 653 return nullptr;
michael@0 654 }
michael@0 655
michael@0 656 int32_t indx = 0;
michael@0 657
michael@0 658 NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
michael@0 659 "ContentIterator stack underflow");
michael@0 660 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 661 // use the last entry on the Indexes array for the current index
michael@0 662 indx = (*aIndexes)[aIndexes->Length()-1];
michael@0 663 } else {
michael@0 664 indx = mCachedIndex;
michael@0 665 }
michael@0 666
michael@0 667 // reverify that the index of the current node hasn't changed
michael@0 668 // ignore result this time - the index may now be out of range.
michael@0 669 nsIContent* sib = parent->GetChildAt(indx);
michael@0 670 if (sib != aNode) {
michael@0 671 // someone changed our index - find the new index the painful way
michael@0 672 indx = parent->IndexOf(aNode);
michael@0 673 }
michael@0 674
michael@0 675 // indx is now canonically correct
michael@0 676 if (indx > 0 && (sib = parent->GetChildAt(--indx))) {
michael@0 677 // update index cache
michael@0 678 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 679 aIndexes->ElementAt(aIndexes->Length()-1) = indx;
michael@0 680 } else {
michael@0 681 mCachedIndex = indx;
michael@0 682 }
michael@0 683 } else if (parent != mCommonParent) {
michael@0 684 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 685 // pop node off the stack, go up one level and try again.
michael@0 686 aIndexes->RemoveElementAt(aIndexes->Length()-1);
michael@0 687 }
michael@0 688 return GetPrevSibling(parent, aIndexes);
michael@0 689 }
michael@0 690
michael@0 691 return sib;
michael@0 692 }
michael@0 693
michael@0 694 nsINode*
michael@0 695 nsContentIterator::NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
michael@0 696 {
michael@0 697 nsINode* node = aNode;
michael@0 698
michael@0 699 // if we are a Pre-order iterator, use pre-order
michael@0 700 if (mPre) {
michael@0 701 // if it has children then next node is first child
michael@0 702 if (node->HasChildren()) {
michael@0 703 nsIContent* firstChild = node->GetFirstChild();
michael@0 704
michael@0 705 // update cache
michael@0 706 if (aIndexes) {
michael@0 707 // push an entry on the index stack
michael@0 708 aIndexes->AppendElement(0);
michael@0 709 } else {
michael@0 710 mCachedIndex = 0;
michael@0 711 }
michael@0 712
michael@0 713 return firstChild;
michael@0 714 }
michael@0 715
michael@0 716 // else next sibling is next
michael@0 717 return GetNextSibling(node, aIndexes);
michael@0 718 }
michael@0 719
michael@0 720 // post-order
michael@0 721 nsINode* parent = node->GetParentNode();
michael@0 722 nsIContent* sibling = nullptr;
michael@0 723 int32_t indx = 0;
michael@0 724
michael@0 725 // get the cached index
michael@0 726 NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
michael@0 727 "ContentIterator stack underflow");
michael@0 728 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 729 // use the last entry on the Indexes array for the current index
michael@0 730 indx = (*aIndexes)[aIndexes->Length()-1];
michael@0 731 } else {
michael@0 732 indx = mCachedIndex;
michael@0 733 }
michael@0 734
michael@0 735 // reverify that the index of the current node hasn't changed. not super
michael@0 736 // cheap, but a lot cheaper than IndexOf(), and still O(1). ignore result
michael@0 737 // this time - the index may now be out of range.
michael@0 738 if (indx >= 0) {
michael@0 739 sibling = parent->GetChildAt(indx);
michael@0 740 }
michael@0 741 if (sibling != node) {
michael@0 742 // someone changed our index - find the new index the painful way
michael@0 743 indx = parent->IndexOf(node);
michael@0 744 }
michael@0 745
michael@0 746 // indx is now canonically correct
michael@0 747 sibling = parent->GetChildAt(++indx);
michael@0 748 if (sibling) {
michael@0 749 // update cache
michael@0 750 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 751 // replace an entry on the index stack
michael@0 752 aIndexes->ElementAt(aIndexes->Length()-1) = indx;
michael@0 753 } else {
michael@0 754 mCachedIndex = indx;
michael@0 755 }
michael@0 756
michael@0 757 // next node is sibling's "deep left" child
michael@0 758 return GetDeepFirstChild(sibling, aIndexes);
michael@0 759 }
michael@0 760
michael@0 761 // else it's the parent, update cache
michael@0 762 if (aIndexes) {
michael@0 763 // Pop an entry off the index stack. Don't leave the index empty,
michael@0 764 // especially if we're returning nullptr. This confuses other parts of the
michael@0 765 // code.
michael@0 766 if (aIndexes->Length() > 1) {
michael@0 767 aIndexes->RemoveElementAt(aIndexes->Length()-1);
michael@0 768 }
michael@0 769 } else {
michael@0 770 // this might be wrong, but we are better off guessing
michael@0 771 mCachedIndex = 0;
michael@0 772 }
michael@0 773
michael@0 774 return parent;
michael@0 775 }
michael@0 776
michael@0 777 nsINode*
michael@0 778 nsContentIterator::PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
michael@0 779 {
michael@0 780 nsINode* node = aNode;
michael@0 781
michael@0 782 // if we are a Pre-order iterator, use pre-order
michael@0 783 if (mPre) {
michael@0 784 nsINode* parent = node->GetParentNode();
michael@0 785 nsIContent* sibling = nullptr;
michael@0 786 int32_t indx = 0;
michael@0 787
michael@0 788 // get the cached index
michael@0 789 NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
michael@0 790 "ContentIterator stack underflow");
michael@0 791 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 792 // use the last entry on the Indexes array for the current index
michael@0 793 indx = (*aIndexes)[aIndexes->Length()-1];
michael@0 794 } else {
michael@0 795 indx = mCachedIndex;
michael@0 796 }
michael@0 797
michael@0 798 // reverify that the index of the current node hasn't changed. not super
michael@0 799 // cheap, but a lot cheaper than IndexOf(), and still O(1). ignore result
michael@0 800 // this time - the index may now be out of range.
michael@0 801 if (indx >= 0) {
michael@0 802 sibling = parent->GetChildAt(indx);
michael@0 803 }
michael@0 804
michael@0 805 if (sibling != node) {
michael@0 806 // someone changed our index - find the new index the painful way
michael@0 807 indx = parent->IndexOf(node);
michael@0 808 }
michael@0 809
michael@0 810 // indx is now canonically correct
michael@0 811 if (indx && (sibling = parent->GetChildAt(--indx))) {
michael@0 812 // update cache
michael@0 813 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 814 // replace an entry on the index stack
michael@0 815 aIndexes->ElementAt(aIndexes->Length()-1) = indx;
michael@0 816 } else {
michael@0 817 mCachedIndex = indx;
michael@0 818 }
michael@0 819
michael@0 820 // prev node is sibling's "deep right" child
michael@0 821 return GetDeepLastChild(sibling, aIndexes);
michael@0 822 }
michael@0 823
michael@0 824 // else it's the parent, update cache
michael@0 825 if (aIndexes && !aIndexes->IsEmpty()) {
michael@0 826 // pop an entry off the index stack
michael@0 827 aIndexes->RemoveElementAt(aIndexes->Length()-1);
michael@0 828 } else {
michael@0 829 // this might be wrong, but we are better off guessing
michael@0 830 mCachedIndex = 0;
michael@0 831 }
michael@0 832 return parent;
michael@0 833 }
michael@0 834
michael@0 835 // post-order
michael@0 836 int32_t numChildren = node->GetChildCount();
michael@0 837
michael@0 838 // if it has children then prev node is last child
michael@0 839 if (numChildren) {
michael@0 840 nsIContent* lastChild = node->GetLastChild();
michael@0 841 numChildren--;
michael@0 842
michael@0 843 // update cache
michael@0 844 if (aIndexes) {
michael@0 845 // push an entry on the index stack
michael@0 846 aIndexes->AppendElement(numChildren);
michael@0 847 } else {
michael@0 848 mCachedIndex = numChildren;
michael@0 849 }
michael@0 850
michael@0 851 return lastChild;
michael@0 852 }
michael@0 853
michael@0 854 // else prev sibling is previous
michael@0 855 return GetPrevSibling(node, aIndexes);
michael@0 856 }
michael@0 857
michael@0 858 /******************************************************
michael@0 859 * ContentIterator routines
michael@0 860 ******************************************************/
michael@0 861
michael@0 862 void
michael@0 863 nsContentIterator::First()
michael@0 864 {
michael@0 865 if (mFirst) {
michael@0 866 #ifdef DEBUG
michael@0 867 nsresult rv =
michael@0 868 #endif
michael@0 869 PositionAt(mFirst);
michael@0 870
michael@0 871 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
michael@0 872 }
michael@0 873
michael@0 874 mIsDone = mFirst == nullptr;
michael@0 875 }
michael@0 876
michael@0 877
michael@0 878 void
michael@0 879 nsContentIterator::Last()
michael@0 880 {
michael@0 881 NS_ASSERTION(mLast, "No last node!");
michael@0 882
michael@0 883 if (mLast) {
michael@0 884 #ifdef DEBUG
michael@0 885 nsresult rv =
michael@0 886 #endif
michael@0 887 PositionAt(mLast);
michael@0 888
michael@0 889 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
michael@0 890 }
michael@0 891
michael@0 892 mIsDone = mLast == nullptr;
michael@0 893 }
michael@0 894
michael@0 895
michael@0 896 void
michael@0 897 nsContentIterator::Next()
michael@0 898 {
michael@0 899 if (mIsDone || !mCurNode) {
michael@0 900 return;
michael@0 901 }
michael@0 902
michael@0 903 if (mCurNode == mLast) {
michael@0 904 mIsDone = true;
michael@0 905 return;
michael@0 906 }
michael@0 907
michael@0 908 mCurNode = NextNode(mCurNode, &mIndexes);
michael@0 909 }
michael@0 910
michael@0 911
michael@0 912 void
michael@0 913 nsContentIterator::Prev()
michael@0 914 {
michael@0 915 if (mIsDone || !mCurNode) {
michael@0 916 return;
michael@0 917 }
michael@0 918
michael@0 919 if (mCurNode == mFirst) {
michael@0 920 mIsDone = true;
michael@0 921 return;
michael@0 922 }
michael@0 923
michael@0 924 mCurNode = PrevNode(mCurNode, &mIndexes);
michael@0 925 }
michael@0 926
michael@0 927
michael@0 928 bool
michael@0 929 nsContentIterator::IsDone()
michael@0 930 {
michael@0 931 return mIsDone;
michael@0 932 }
michael@0 933
michael@0 934
michael@0 935 // Keeping arrays of indexes for the stack of nodes makes PositionAt
michael@0 936 // interesting...
michael@0 937 nsresult
michael@0 938 nsContentIterator::PositionAt(nsINode* aCurNode)
michael@0 939 {
michael@0 940 if (!aCurNode) {
michael@0 941 return NS_ERROR_NULL_POINTER;
michael@0 942 }
michael@0 943
michael@0 944 nsINode* newCurNode = aCurNode;
michael@0 945 nsINode* tempNode = mCurNode;
michael@0 946
michael@0 947 mCurNode = aCurNode;
michael@0 948 // take an early out if this doesn't actually change the position
michael@0 949 if (mCurNode == tempNode) {
michael@0 950 mIsDone = false; // paranoia
michael@0 951 return NS_OK;
michael@0 952 }
michael@0 953
michael@0 954 // Check to see if the node falls within the traversal range.
michael@0 955
michael@0 956 nsINode* firstNode = mFirst;
michael@0 957 nsINode* lastNode = mLast;
michael@0 958 int32_t firstOffset = 0, lastOffset = 0;
michael@0 959
michael@0 960 if (firstNode && lastNode) {
michael@0 961 if (mPre) {
michael@0 962 firstNode = NodeToParentOffset(mFirst, &firstOffset);
michael@0 963
michael@0 964 if (lastNode->GetChildCount()) {
michael@0 965 lastOffset = 0;
michael@0 966 } else {
michael@0 967 lastNode = NodeToParentOffset(mLast, &lastOffset);
michael@0 968 ++lastOffset;
michael@0 969 }
michael@0 970 } else {
michael@0 971 uint32_t numChildren = firstNode->GetChildCount();
michael@0 972
michael@0 973 if (numChildren) {
michael@0 974 firstOffset = numChildren;
michael@0 975 } else {
michael@0 976 firstNode = NodeToParentOffset(mFirst, &firstOffset);
michael@0 977 }
michael@0 978
michael@0 979 lastNode = NodeToParentOffset(mLast, &lastOffset);
michael@0 980 ++lastOffset;
michael@0 981 }
michael@0 982 }
michael@0 983
michael@0 984 // The end positions are always in the range even if it has no parent. We
michael@0 985 // need to allow that or 'iter->Init(root)' would assert in Last() or First()
michael@0 986 // for example, bug 327694.
michael@0 987 if (mFirst != mCurNode && mLast != mCurNode &&
michael@0 988 (!firstNode || !lastNode ||
michael@0 989 !NodeIsInTraversalRange(mCurNode, mPre, firstNode, firstOffset,
michael@0 990 lastNode, lastOffset))) {
michael@0 991 mIsDone = true;
michael@0 992 return NS_ERROR_FAILURE;
michael@0 993 }
michael@0 994
michael@0 995 // We can be at ANY node in the sequence. Need to regenerate the array of
michael@0 996 // indexes back to the root or common parent!
michael@0 997 nsAutoTArray<nsINode*, 8> oldParentStack;
michael@0 998 nsAutoTArray<int32_t, 8> newIndexes;
michael@0 999
michael@0 1000 // Get a list of the parents up to the root, then compare the new node with
michael@0 1001 // entries in that array until we find a match (lowest common ancestor). If
michael@0 1002 // no match, use IndexOf, take the parent, and repeat. This avoids using
michael@0 1003 // IndexOf() N times on possibly large arrays. We still end up doing it a
michael@0 1004 // fair bit. It's better to use Clone() if possible.
michael@0 1005
michael@0 1006 // we know the depth we're down (though we may not have started at the top).
michael@0 1007 oldParentStack.SetCapacity(mIndexes.Length() + 1);
michael@0 1008
michael@0 1009 // We want to loop mIndexes.Length() + 1 times here, because we want to make
michael@0 1010 // sure we include mCommonParent in the oldParentStack, for use in the next
michael@0 1011 // for loop, and mIndexes only has entries for nodes from tempNode up through
michael@0 1012 // an ancestor of tempNode that's a child of mCommonParent.
michael@0 1013 for (int32_t i = mIndexes.Length() + 1; i > 0 && tempNode; i--) {
michael@0 1014 // Insert at head since we're walking up
michael@0 1015 oldParentStack.InsertElementAt(0, tempNode);
michael@0 1016
michael@0 1017 nsINode* parent = tempNode->GetParentNode();
michael@0 1018
michael@0 1019 if (!parent) {
michael@0 1020 // this node has no parent, and thus no index
michael@0 1021 break;
michael@0 1022 }
michael@0 1023
michael@0 1024 if (parent == mCurNode) {
michael@0 1025 // The position was moved to a parent of the current position. All we
michael@0 1026 // need to do is drop some indexes. Shortcut here.
michael@0 1027 mIndexes.RemoveElementsAt(mIndexes.Length() - oldParentStack.Length(),
michael@0 1028 oldParentStack.Length());
michael@0 1029 mIsDone = false;
michael@0 1030 return NS_OK;
michael@0 1031 }
michael@0 1032 tempNode = parent;
michael@0 1033 }
michael@0 1034
michael@0 1035 // Ok. We have the array of old parents. Look for a match.
michael@0 1036 while (newCurNode) {
michael@0 1037 nsINode* parent = newCurNode->GetParentNode();
michael@0 1038
michael@0 1039 if (!parent) {
michael@0 1040 // this node has no parent, and thus no index
michael@0 1041 break;
michael@0 1042 }
michael@0 1043
michael@0 1044 int32_t indx = parent->IndexOf(newCurNode);
michael@0 1045
michael@0 1046 // insert at the head!
michael@0 1047 newIndexes.InsertElementAt(0, indx);
michael@0 1048
michael@0 1049 // look to see if the parent is in the stack
michael@0 1050 indx = oldParentStack.IndexOf(parent);
michael@0 1051 if (indx >= 0) {
michael@0 1052 // ok, the parent IS on the old stack! Rework things. We want
michael@0 1053 // newIndexes to replace all nodes equal to or below the match. Note
michael@0 1054 // that index oldParentStack.Length() - 1 is the last node, which is one
michael@0 1055 // BELOW the last index in the mIndexes stack. In other words, we want
michael@0 1056 // to remove elements starting at index (indx + 1).
michael@0 1057 int32_t numToDrop = oldParentStack.Length() - (1 + indx);
michael@0 1058 if (numToDrop > 0) {
michael@0 1059 mIndexes.RemoveElementsAt(mIndexes.Length() - numToDrop, numToDrop);
michael@0 1060 }
michael@0 1061 mIndexes.AppendElements(newIndexes);
michael@0 1062
michael@0 1063 break;
michael@0 1064 }
michael@0 1065 newCurNode = parent;
michael@0 1066 }
michael@0 1067
michael@0 1068 // phew!
michael@0 1069
michael@0 1070 mIsDone = false;
michael@0 1071 return NS_OK;
michael@0 1072 }
michael@0 1073
michael@0 1074 nsINode*
michael@0 1075 nsContentIterator::GetCurrentNode()
michael@0 1076 {
michael@0 1077 if (mIsDone) {
michael@0 1078 return nullptr;
michael@0 1079 }
michael@0 1080
michael@0 1081 NS_ASSERTION(mCurNode, "Null current node in an iterator that's not done!");
michael@0 1082
michael@0 1083 return mCurNode;
michael@0 1084 }
michael@0 1085
michael@0 1086
michael@0 1087
michael@0 1088
michael@0 1089
michael@0 1090 /*====================================================================================*/
michael@0 1091 /*====================================================================================*/
michael@0 1092
michael@0 1093
michael@0 1094
michael@0 1095
michael@0 1096
michael@0 1097
michael@0 1098 /******************************************************
michael@0 1099 * nsContentSubtreeIterator
michael@0 1100 ******************************************************/
michael@0 1101
michael@0 1102
michael@0 1103 /*
michael@0 1104 * A simple iterator class for traversing the content in "top subtree" order
michael@0 1105 */
michael@0 1106 class nsContentSubtreeIterator : public nsContentIterator
michael@0 1107 {
michael@0 1108 public:
michael@0 1109 nsContentSubtreeIterator() : nsContentIterator(false) {}
michael@0 1110 virtual ~nsContentSubtreeIterator() {}
michael@0 1111
michael@0 1112 NS_DECL_ISUPPORTS_INHERITED
michael@0 1113 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsContentSubtreeIterator, nsContentIterator)
michael@0 1114
michael@0 1115 // nsContentIterator overrides ------------------------------
michael@0 1116
michael@0 1117 virtual nsresult Init(nsINode* aRoot);
michael@0 1118
michael@0 1119 virtual nsresult Init(nsIDOMRange* aRange);
michael@0 1120
michael@0 1121 virtual void Next();
michael@0 1122
michael@0 1123 virtual void Prev();
michael@0 1124
michael@0 1125 virtual nsresult PositionAt(nsINode* aCurNode);
michael@0 1126
michael@0 1127 // Must override these because we don't do PositionAt
michael@0 1128 virtual void First();
michael@0 1129
michael@0 1130 // Must override these because we don't do PositionAt
michael@0 1131 virtual void Last();
michael@0 1132
michael@0 1133 protected:
michael@0 1134
michael@0 1135 // Returns the highest inclusive ancestor of aNode that's in the range
michael@0 1136 // (possibly aNode itself). Returns null if aNode is null, or is not itself
michael@0 1137 // in the range. A node is in the range if (node, 0) comes strictly after
michael@0 1138 // the range endpoint, and (node, node.length) comes strictly before it, so
michael@0 1139 // the range's start and end nodes will never be considered "in" it.
michael@0 1140 nsIContent* GetTopAncestorInRange(nsINode* aNode);
michael@0 1141
michael@0 1142 // no copy's or assigns FIX ME
michael@0 1143 nsContentSubtreeIterator(const nsContentSubtreeIterator&);
michael@0 1144 nsContentSubtreeIterator& operator=(const nsContentSubtreeIterator&);
michael@0 1145
michael@0 1146 virtual void LastRelease() MOZ_OVERRIDE;
michael@0 1147
michael@0 1148 nsRefPtr<nsRange> mRange;
michael@0 1149
michael@0 1150 // these arrays all typically are used and have elements
michael@0 1151 nsAutoTArray<nsIContent*, 8> mEndNodes;
michael@0 1152 nsAutoTArray<int32_t, 8> mEndOffsets;
michael@0 1153 };
michael@0 1154
michael@0 1155 NS_IMPL_ADDREF_INHERITED(nsContentSubtreeIterator, nsContentIterator)
michael@0 1156 NS_IMPL_RELEASE_INHERITED(nsContentSubtreeIterator, nsContentIterator)
michael@0 1157
michael@0 1158 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsContentSubtreeIterator)
michael@0 1159 NS_INTERFACE_MAP_END_INHERITING(nsContentIterator)
michael@0 1160
michael@0 1161 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsContentSubtreeIterator, nsContentIterator,
michael@0 1162 mRange)
michael@0 1163
michael@0 1164 void
michael@0 1165 nsContentSubtreeIterator::LastRelease()
michael@0 1166 {
michael@0 1167 mRange = nullptr;
michael@0 1168 nsContentIterator::LastRelease();
michael@0 1169 }
michael@0 1170
michael@0 1171 /******************************************************
michael@0 1172 * repository cruft
michael@0 1173 ******************************************************/
michael@0 1174
michael@0 1175 already_AddRefed<nsIContentIterator>
michael@0 1176 NS_NewContentSubtreeIterator()
michael@0 1177 {
michael@0 1178 nsCOMPtr<nsIContentIterator> iter = new nsContentSubtreeIterator();
michael@0 1179 return iter.forget();
michael@0 1180 }
michael@0 1181
michael@0 1182
michael@0 1183
michael@0 1184 /******************************************************
michael@0 1185 * Init routines
michael@0 1186 ******************************************************/
michael@0 1187
michael@0 1188
michael@0 1189 nsresult
michael@0 1190 nsContentSubtreeIterator::Init(nsINode* aRoot)
michael@0 1191 {
michael@0 1192 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 1193 }
michael@0 1194
michael@0 1195
michael@0 1196 nsresult
michael@0 1197 nsContentSubtreeIterator::Init(nsIDOMRange* aRange)
michael@0 1198 {
michael@0 1199 MOZ_ASSERT(aRange);
michael@0 1200
michael@0 1201 mIsDone = false;
michael@0 1202
michael@0 1203 mRange = static_cast<nsRange*>(aRange);
michael@0 1204
michael@0 1205 // get the start node and offset, convert to nsINode
michael@0 1206 mCommonParent = mRange->GetCommonAncestor();
michael@0 1207 nsINode* startParent = mRange->GetStartParent();
michael@0 1208 int32_t startOffset = mRange->StartOffset();
michael@0 1209 nsINode* endParent = mRange->GetEndParent();
michael@0 1210 int32_t endOffset = mRange->EndOffset();
michael@0 1211 MOZ_ASSERT(mCommonParent && startParent && endParent);
michael@0 1212 // Bug 767169
michael@0 1213 MOZ_ASSERT(uint32_t(startOffset) <= startParent->Length() &&
michael@0 1214 uint32_t(endOffset) <= endParent->Length());
michael@0 1215
michael@0 1216 // short circuit when start node == end node
michael@0 1217 if (startParent == endParent) {
michael@0 1218 nsINode* child = startParent->GetFirstChild();
michael@0 1219
michael@0 1220 if (!child || startOffset == endOffset) {
michael@0 1221 // Text node, empty container, or collapsed
michael@0 1222 MakeEmpty();
michael@0 1223 return NS_OK;
michael@0 1224 }
michael@0 1225 }
michael@0 1226
michael@0 1227 // cache ancestors
michael@0 1228 nsContentUtils::GetAncestorsAndOffsets(endParent->AsDOMNode(), endOffset,
michael@0 1229 &mEndNodes, &mEndOffsets);
michael@0 1230
michael@0 1231 nsIContent* firstCandidate = nullptr;
michael@0 1232 nsIContent* lastCandidate = nullptr;
michael@0 1233
michael@0 1234 // find first node in range
michael@0 1235 int32_t offset = mRange->StartOffset();
michael@0 1236
michael@0 1237 nsINode* node;
michael@0 1238 if (!startParent->GetChildCount()) {
michael@0 1239 // no children, start at the node itself
michael@0 1240 node = startParent;
michael@0 1241 } else {
michael@0 1242 nsIContent* child = startParent->GetChildAt(offset);
michael@0 1243 if (!child) {
michael@0 1244 // offset after last child
michael@0 1245 node = startParent;
michael@0 1246 } else {
michael@0 1247 firstCandidate = child;
michael@0 1248 }
michael@0 1249 }
michael@0 1250
michael@0 1251 if (!firstCandidate) {
michael@0 1252 // then firstCandidate is next node after node
michael@0 1253 firstCandidate = GetNextSibling(node);
michael@0 1254
michael@0 1255 if (!firstCandidate) {
michael@0 1256 MakeEmpty();
michael@0 1257 return NS_OK;
michael@0 1258 }
michael@0 1259 }
michael@0 1260
michael@0 1261 firstCandidate = GetDeepFirstChild(firstCandidate);
michael@0 1262
michael@0 1263 // confirm that this first possible contained node is indeed contained. Else
michael@0 1264 // we have a range that does not fully contain any node.
michael@0 1265
michael@0 1266 bool nodeBefore, nodeAfter;
michael@0 1267 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
michael@0 1268 nsRange::CompareNodeToRange(firstCandidate, mRange, &nodeBefore, &nodeAfter)));
michael@0 1269
michael@0 1270 if (nodeBefore || nodeAfter) {
michael@0 1271 MakeEmpty();
michael@0 1272 return NS_OK;
michael@0 1273 }
michael@0 1274
michael@0 1275 // cool, we have the first node in the range. Now we walk up its ancestors
michael@0 1276 // to find the most senior that is still in the range. That's the real first
michael@0 1277 // node.
michael@0 1278 mFirst = GetTopAncestorInRange(firstCandidate);
michael@0 1279
michael@0 1280 // now to find the last node
michael@0 1281 offset = mRange->EndOffset();
michael@0 1282 int32_t numChildren = endParent->GetChildCount();
michael@0 1283
michael@0 1284 if (offset > numChildren) {
michael@0 1285 // Can happen for text nodes
michael@0 1286 offset = numChildren;
michael@0 1287 }
michael@0 1288 if (!offset || !numChildren) {
michael@0 1289 node = endParent;
michael@0 1290 } else {
michael@0 1291 lastCandidate = endParent->GetChildAt(--offset);
michael@0 1292 NS_ASSERTION(lastCandidate,
michael@0 1293 "tree traversal trouble in nsContentSubtreeIterator::Init");
michael@0 1294 }
michael@0 1295
michael@0 1296 if (!lastCandidate) {
michael@0 1297 // then lastCandidate is prev node before node
michael@0 1298 lastCandidate = GetPrevSibling(node);
michael@0 1299 }
michael@0 1300
michael@0 1301 if (!lastCandidate) {
michael@0 1302 MakeEmpty();
michael@0 1303 return NS_OK;
michael@0 1304 }
michael@0 1305
michael@0 1306 lastCandidate = GetDeepLastChild(lastCandidate);
michael@0 1307
michael@0 1308 // confirm that this last possible contained node is indeed contained. Else
michael@0 1309 // we have a range that does not fully contain any node.
michael@0 1310
michael@0 1311 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
michael@0 1312 nsRange::CompareNodeToRange(lastCandidate, mRange, &nodeBefore, &nodeAfter)));
michael@0 1313
michael@0 1314 if (nodeBefore || nodeAfter) {
michael@0 1315 MakeEmpty();
michael@0 1316 return NS_OK;
michael@0 1317 }
michael@0 1318
michael@0 1319 // cool, we have the last node in the range. Now we walk up its ancestors to
michael@0 1320 // find the most senior that is still in the range. That's the real first
michael@0 1321 // node.
michael@0 1322 mLast = GetTopAncestorInRange(lastCandidate);
michael@0 1323
michael@0 1324 mCurNode = mFirst;
michael@0 1325
michael@0 1326 return NS_OK;
michael@0 1327 }
michael@0 1328
michael@0 1329 /****************************************************************
michael@0 1330 * nsContentSubtreeIterator overrides of ContentIterator routines
michael@0 1331 ****************************************************************/
michael@0 1332
michael@0 1333 // we can't call PositionAt in a subtree iterator...
michael@0 1334 void
michael@0 1335 nsContentSubtreeIterator::First()
michael@0 1336 {
michael@0 1337 mIsDone = mFirst == nullptr;
michael@0 1338
michael@0 1339 mCurNode = mFirst;
michael@0 1340 }
michael@0 1341
michael@0 1342 // we can't call PositionAt in a subtree iterator...
michael@0 1343 void
michael@0 1344 nsContentSubtreeIterator::Last()
michael@0 1345 {
michael@0 1346 mIsDone = mLast == nullptr;
michael@0 1347
michael@0 1348 mCurNode = mLast;
michael@0 1349 }
michael@0 1350
michael@0 1351
michael@0 1352 void
michael@0 1353 nsContentSubtreeIterator::Next()
michael@0 1354 {
michael@0 1355 if (mIsDone || !mCurNode) {
michael@0 1356 return;
michael@0 1357 }
michael@0 1358
michael@0 1359 if (mCurNode == mLast) {
michael@0 1360 mIsDone = true;
michael@0 1361 return;
michael@0 1362 }
michael@0 1363
michael@0 1364 nsINode* nextNode = GetNextSibling(mCurNode);
michael@0 1365 NS_ASSERTION(nextNode, "No next sibling!?! This could mean deadlock!");
michael@0 1366
michael@0 1367 int32_t i = mEndNodes.IndexOf(nextNode);
michael@0 1368 while (i != -1) {
michael@0 1369 // as long as we are finding ancestors of the endpoint of the range,
michael@0 1370 // dive down into their children
michael@0 1371 nextNode = nextNode->GetFirstChild();
michael@0 1372 NS_ASSERTION(nextNode, "Iterator error, expected a child node!");
michael@0 1373
michael@0 1374 // should be impossible to get a null pointer. If we went all the way
michael@0 1375 // down the child chain to the bottom without finding an interior node,
michael@0 1376 // then the previous node should have been the last, which was
michael@0 1377 // was tested at top of routine.
michael@0 1378 i = mEndNodes.IndexOf(nextNode);
michael@0 1379 }
michael@0 1380
michael@0 1381 mCurNode = nextNode;
michael@0 1382
michael@0 1383 // This shouldn't be needed, but since our selection code can put us
michael@0 1384 // in a situation where mLast is in generated content, we need this
michael@0 1385 // to stop the iterator when we've walked past past the last node!
michael@0 1386 mIsDone = mCurNode == nullptr;
michael@0 1387 }
michael@0 1388
michael@0 1389
michael@0 1390 void
michael@0 1391 nsContentSubtreeIterator::Prev()
michael@0 1392 {
michael@0 1393 // Prev should be optimized to use the mStartNodes, just as Next
michael@0 1394 // uses mEndNodes.
michael@0 1395 if (mIsDone || !mCurNode) {
michael@0 1396 return;
michael@0 1397 }
michael@0 1398
michael@0 1399 if (mCurNode == mFirst) {
michael@0 1400 mIsDone = true;
michael@0 1401 return;
michael@0 1402 }
michael@0 1403
michael@0 1404 // If any of these function calls return null, so will all succeeding ones,
michael@0 1405 // so mCurNode will wind up set to null.
michael@0 1406 nsINode* prevNode = GetDeepFirstChild(mCurNode);
michael@0 1407
michael@0 1408 prevNode = PrevNode(prevNode);
michael@0 1409
michael@0 1410 prevNode = GetDeepLastChild(prevNode);
michael@0 1411
michael@0 1412 mCurNode = GetTopAncestorInRange(prevNode);
michael@0 1413
michael@0 1414 // This shouldn't be needed, but since our selection code can put us
michael@0 1415 // in a situation where mFirst is in generated content, we need this
michael@0 1416 // to stop the iterator when we've walked past past the first node!
michael@0 1417 mIsDone = mCurNode == nullptr;
michael@0 1418 }
michael@0 1419
michael@0 1420
michael@0 1421 nsresult
michael@0 1422 nsContentSubtreeIterator::PositionAt(nsINode* aCurNode)
michael@0 1423 {
michael@0 1424 NS_ERROR("Not implemented!");
michael@0 1425
michael@0 1426 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 1427 }
michael@0 1428
michael@0 1429 /****************************************************************
michael@0 1430 * nsContentSubtreeIterator helper routines
michael@0 1431 ****************************************************************/
michael@0 1432
michael@0 1433 nsIContent*
michael@0 1434 nsContentSubtreeIterator::GetTopAncestorInRange(nsINode* aNode)
michael@0 1435 {
michael@0 1436 if (!aNode || !aNode->GetParentNode()) {
michael@0 1437 return nullptr;
michael@0 1438 }
michael@0 1439
michael@0 1440 // aNode has a parent, so it must be content.
michael@0 1441 nsIContent* content = aNode->AsContent();
michael@0 1442
michael@0 1443 // sanity check: aNode is itself in the range
michael@0 1444 bool nodeBefore, nodeAfter;
michael@0 1445 nsresult res = nsRange::CompareNodeToRange(aNode, mRange,
michael@0 1446 &nodeBefore, &nodeAfter);
michael@0 1447 NS_ASSERTION(NS_SUCCEEDED(res) && !nodeBefore && !nodeAfter,
michael@0 1448 "aNode isn't in mRange, or something else weird happened");
michael@0 1449 if (NS_FAILED(res) || nodeBefore || nodeAfter) {
michael@0 1450 return nullptr;
michael@0 1451 }
michael@0 1452
michael@0 1453 while (content) {
michael@0 1454 nsIContent* parent = content->GetParent();
michael@0 1455 // content always has a parent. If its parent is the root, however --
michael@0 1456 // i.e., either it's not content, or it is content but its own parent is
michael@0 1457 // null -- then we're finished, since we don't go up to the root.
michael@0 1458 //
michael@0 1459 // We have to special-case this because CompareNodeToRange treats the root
michael@0 1460 // node differently -- see bug 765205.
michael@0 1461 if (!parent || !parent->GetParentNode()) {
michael@0 1462 return content;
michael@0 1463 }
michael@0 1464 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
michael@0 1465 nsRange::CompareNodeToRange(parent, mRange, &nodeBefore, &nodeAfter)));
michael@0 1466
michael@0 1467 if (nodeBefore || nodeAfter) {
michael@0 1468 return content;
michael@0 1469 }
michael@0 1470 content = parent;
michael@0 1471 }
michael@0 1472
michael@0 1473 MOZ_CRASH("This should only be possible if aNode was null");
michael@0 1474 }

mercurial