LCOV - code coverage report
Current view: top level - dom/base - nsRange.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 231 1805 12.8 %
Date: 2017-07-14 16:53:18 Functions: 36 142 25.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /*
       8             :  * Implementation of the DOM nsIDOMRange object.
       9             :  */
      10             : 
      11             : #include "nscore.h"
      12             : #include "nsRange.h"
      13             : 
      14             : #include "nsString.h"
      15             : #include "nsReadableUtils.h"
      16             : #include "nsIDOMNode.h"
      17             : #include "nsIDOMDocumentFragment.h"
      18             : #include "nsIContent.h"
      19             : #include "nsIDocument.h"
      20             : #include "nsIDOMText.h"
      21             : #include "nsError.h"
      22             : #include "nsIContentIterator.h"
      23             : #include "nsIDOMNodeList.h"
      24             : #include "nsGkAtoms.h"
      25             : #include "nsContentUtils.h"
      26             : #include "nsGenericDOMDataNode.h"
      27             : #include "nsTextFrame.h"
      28             : #include "nsFontFaceList.h"
      29             : #include "mozilla/dom/DocumentFragment.h"
      30             : #include "mozilla/dom/DocumentType.h"
      31             : #include "mozilla/dom/RangeBinding.h"
      32             : #include "mozilla/dom/DOMRect.h"
      33             : #include "mozilla/dom/DOMStringList.h"
      34             : #include "mozilla/dom/ShadowRoot.h"
      35             : #include "mozilla/dom/Selection.h"
      36             : #include "mozilla/Telemetry.h"
      37             : #include "mozilla/Likely.h"
      38             : #include "nsCSSFrameConstructor.h"
      39             : #include "nsStyleStruct.h"
      40             : #include "nsStyleStructInlines.h"
      41             : #include "nsComputedDOMStyle.h"
      42             : 
      43             : using namespace mozilla;
      44             : using namespace mozilla::dom;
      45             : 
      46             : JSObject*
      47           2 : nsRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
      48             : {
      49           2 :   return RangeBinding::Wrap(aCx, this, aGivenProto);
      50             : }
      51             : 
      52             : /******************************************************
      53             :  * stack based utilty class for managing monitor
      54             :  ******************************************************/
      55             : 
      56           0 : static void InvalidateAllFrames(nsINode* aNode)
      57             : {
      58           0 :   NS_PRECONDITION(aNode, "bad arg");
      59             : 
      60           0 :   nsIFrame* frame = nullptr;
      61           0 :   switch (aNode->NodeType()) {
      62             :     case nsIDOMNode::TEXT_NODE:
      63             :     case nsIDOMNode::ELEMENT_NODE:
      64             :     {
      65           0 :       nsIContent* content = static_cast<nsIContent*>(aNode);
      66           0 :       frame = content->GetPrimaryFrame();
      67           0 :       break;
      68             :     }
      69             :     case nsIDOMNode::DOCUMENT_NODE:
      70             :     {
      71           0 :       nsIDocument* doc = static_cast<nsIDocument*>(aNode);
      72           0 :       nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
      73           0 :       frame = shell ? shell->GetRootFrame() : nullptr;
      74           0 :       break;
      75             :     }
      76             :   }
      77           0 :   for (nsIFrame* f = frame; f; f = f->GetNextContinuation()) {
      78           0 :     f->InvalidateFrameSubtree();
      79             :   }
      80           0 : }
      81             : 
      82             : // Utility routine to detect if a content node is completely contained in a range
      83             : // If outNodeBefore is returned true, then the node starts before the range does.
      84             : // If outNodeAfter is returned true, then the node ends after the range does.
      85             : // Note that both of the above might be true.
      86             : // If neither are true, the node is contained inside of the range.
      87             : // XXX - callers responsibility to ensure node in same doc as range!
      88             : 
      89             : // static
      90             : nsresult
      91           0 : nsRange::CompareNodeToRange(nsINode* aNode, nsRange* aRange,
      92             :                             bool *outNodeBefore, bool *outNodeAfter)
      93             : {
      94           0 :   NS_ENSURE_STATE(aNode);
      95             :   // create a pair of dom points that expresses location of node:
      96             :   //     NODE(start), NODE(end)
      97             :   // Let incoming range be:
      98             :   //    {RANGE(start), RANGE(end)}
      99             :   // if (RANGE(start) <= NODE(start))  and (RANGE(end) => NODE(end))
     100             :   // then the Node is contained (completely) by the Range.
     101             : 
     102           0 :   if (!aRange || !aRange->IsPositioned())
     103           0 :     return NS_ERROR_UNEXPECTED;
     104             : 
     105             :   // gather up the dom point info
     106             :   int32_t nodeStart, nodeEnd;
     107           0 :   nsINode* parent = aNode->GetParentNode();
     108           0 :   if (!parent) {
     109             :     // can't make a parent/offset pair to represent start or
     110             :     // end of the root node, because it has no parent.
     111             :     // so instead represent it by (node,0) and (node,numChildren)
     112           0 :     parent = aNode;
     113           0 :     nodeStart = 0;
     114           0 :     nodeEnd = aNode->GetChildCount();
     115             :   }
     116             :   else {
     117           0 :     nodeStart = parent->IndexOf(aNode);
     118           0 :     nodeEnd = nodeStart + 1;
     119             :   }
     120             : 
     121           0 :   nsINode* rangeStartContainer = aRange->GetStartContainer();
     122           0 :   nsINode* rangeEndContainer = aRange->GetEndContainer();
     123           0 :   int32_t rangeStartOffset = aRange->StartOffset();
     124           0 :   int32_t rangeEndOffset = aRange->EndOffset();
     125             : 
     126             :   // is RANGE(start) <= NODE(start) ?
     127           0 :   bool disconnected = false;
     128           0 :   *outNodeBefore = nsContentUtils::ComparePoints(rangeStartContainer,
     129             :                                                  rangeStartOffset,
     130             :                                                  parent, nodeStart,
     131           0 :                                                  &disconnected) > 0;
     132           0 :   NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
     133             : 
     134             :   // is RANGE(end) >= NODE(end) ?
     135           0 :   *outNodeAfter = nsContentUtils::ComparePoints(rangeEndContainer,
     136             :                                                 rangeEndOffset,
     137             :                                                 parent, nodeEnd,
     138           0 :                                                 &disconnected) < 0;
     139           0 :   NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
     140           0 :   return NS_OK;
     141             : }
     142             : 
     143             : static nsINode*
     144           4 : GetNextRangeCommonAncestor(nsINode* aNode)
     145             : {
     146           4 :   while (aNode && !aNode->IsCommonAncestorForRangeInSelection()) {
     147           2 :     if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
     148           2 :       return nullptr;
     149             :     }
     150           0 :     aNode = aNode->GetParentNode();
     151             :   }
     152           2 :   return aNode;
     153             : }
     154             : 
     155             : /**
     156             :  * A Comparator suitable for mozilla::BinarySearchIf for searching a collection
     157             :  * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset).
     158             :  */
     159             : struct IsItemInRangeComparator
     160             : {
     161             :   nsINode* mNode;
     162             :   uint32_t mStartOffset;
     163             :   uint32_t mEndOffset;
     164             : 
     165           2 :   int operator()(const nsRange* const aRange) const
     166             :   {
     167           2 :     int32_t cmp = nsContentUtils::ComparePoints(mNode, mEndOffset,
     168             :                                                 aRange->GetStartContainer(),
     169           2 :                                                 aRange->StartOffset());
     170           2 :     if (cmp == 1) {
     171           2 :       cmp = nsContentUtils::ComparePoints(mNode, mStartOffset,
     172             :                                           aRange->GetEndContainer(),
     173           2 :                                           aRange->EndOffset());
     174           2 :       if (cmp == -1) {
     175           2 :         return 0;
     176             :       }
     177           0 :       return 1;
     178             :     }
     179           0 :     return -1;
     180             :   }
     181             : };
     182             : 
     183             : /* static */ bool
     184           2 : nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
     185             :                         uint32_t aEndOffset)
     186             : {
     187           2 :   NS_PRECONDITION(aNode, "bad arg");
     188             : 
     189           2 :   nsINode* n = GetNextRangeCommonAncestor(aNode);
     190           2 :   NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
     191             :                "orphan selection descendant");
     192             : 
     193             :   // Collect the potential ranges and their selection objects.
     194           4 :   RangeHashTable ancestorSelectionRanges;
     195           4 :   nsTHashtable<nsPtrHashKey<Selection>> ancestorSelections;
     196           2 :   uint32_t maxRangeCount = 0;
     197           6 :   for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) {
     198             :     RangeHashTable* ranges =
     199           2 :       static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range));
     200           6 :     for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) {
     201           4 :       nsRange* range = iter.Get()->GetKey();
     202           4 :       if (range->IsInSelection() && !range->Collapsed()) {
     203           2 :         ancestorSelectionRanges.PutEntry(range);
     204           2 :         Selection* selection = range->mSelection;
     205           2 :         ancestorSelections.PutEntry(selection);
     206           2 :         maxRangeCount = std::max(maxRangeCount, selection->RangeCount());
     207             :       }
     208             :     }
     209             :   }
     210             : 
     211           2 :   if (!ancestorSelectionRanges.IsEmpty()) {
     212           2 :     nsTArray<const nsRange*> sortedRanges(maxRangeCount);
     213           2 :     for (auto iter = ancestorSelections.ConstIter(); !iter.Done(); iter.Next()) {
     214           2 :       Selection* selection = iter.Get()->GetKey();
     215             :       // Sort the found ranges for |selection| in document order
     216             :       // (Selection::GetRangeAt returns its ranges ordered).
     217           4 :       for (uint32_t i = 0, len = selection->RangeCount(); i < len; ++i) {
     218           2 :         nsRange* range = selection->GetRangeAt(i);
     219           2 :         if (ancestorSelectionRanges.Contains(range)) {
     220           2 :           sortedRanges.AppendElement(range);
     221             :         }
     222             :       }
     223           2 :       MOZ_ASSERT(!sortedRanges.IsEmpty());
     224             :       // Binary search the now sorted ranges.
     225           2 :       IsItemInRangeComparator comparator = { aNode, aStartOffset, aEndOffset };
     226             :       size_t unused;
     227           2 :       if (mozilla::BinarySearchIf(sortedRanges, 0, sortedRanges.Length(), comparator, &unused)) {
     228           2 :         return true;
     229             :       }
     230           0 :       sortedRanges.ClearAndRetainStorage();
     231             :     }
     232             :   }
     233           0 :   return false;
     234             : }
     235             : 
     236             : /******************************************************
     237             :  * constructor/destructor
     238             :  ******************************************************/
     239             : 
     240          30 : nsRange::~nsRange()
     241             : {
     242          10 :   NS_ASSERTION(!IsInSelection(), "deleting nsRange that is in use");
     243             : 
     244             :   // we want the side effects (releases and list removals)
     245          10 :   DoSetRange(nullptr, 0, nullptr, 0, nullptr);
     246          30 : }
     247             : 
     248          15 : nsRange::nsRange(nsINode* aNode)
     249             :   : mRoot(nullptr)
     250             :   , mStartOffset(0)
     251             :   , mEndOffset(0)
     252             :   , mIsPositioned(false)
     253             :   , mMaySpanAnonymousSubtrees(false)
     254             :   , mIsGenerated(false)
     255             :   , mStartOffsetWasIncremented(false)
     256             :   , mEndOffsetWasIncremented(false)
     257             :   , mCalledByJS(false)
     258             : #ifdef DEBUG
     259             :   , mAssertNextInsertOrAppendIndex(-1)
     260          15 :   , mAssertNextInsertOrAppendNode(nullptr)
     261             : #endif
     262             : {
     263          15 :   MOZ_ASSERT(aNode, "range isn't in a document!");
     264          15 :   mOwner = aNode->OwnerDoc();
     265          15 : }
     266             : 
     267             : /* static */
     268             : nsresult
     269           0 : nsRange::CreateRange(nsINode* aStartContainer, int32_t aStartOffset,
     270             :                      nsINode* aEndParent, int32_t aEndOffset,
     271             :                      nsRange** aRange)
     272             : {
     273           0 :   MOZ_ASSERT(aRange);
     274           0 :   *aRange = nullptr;
     275             : 
     276           0 :   RefPtr<nsRange> range = new nsRange(aStartContainer);
     277           0 :   nsresult rv = range->SetStartAndEnd(aStartContainer, aStartOffset,
     278           0 :                                       aEndParent, aEndOffset);
     279           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     280           0 :     return rv;
     281             :   }
     282           0 :   range.forget(aRange);
     283           0 :   return NS_OK;
     284             : }
     285             : 
     286             : /* static */
     287             : nsresult
     288           0 : nsRange::CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
     289             :                      nsIDOMNode* aEndParent, int32_t aEndOffset,
     290             :                      nsRange** aRange)
     291             : {
     292           0 :   nsCOMPtr<nsINode> startContainer = do_QueryInterface(aStartContainer);
     293           0 :   nsCOMPtr<nsINode> endContainer = do_QueryInterface(aEndParent);
     294           0 :   return CreateRange(startContainer, aStartOffset,
     295           0 :                      endContainer, aEndOffset, aRange);
     296             : }
     297             : 
     298             : /* static */
     299             : nsresult
     300           0 : nsRange::CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
     301             :                      nsIDOMNode* aEndParent, int32_t aEndOffset,
     302             :                      nsIDOMRange** aRange)
     303             : {
     304           0 :   RefPtr<nsRange> range;
     305           0 :   nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset, aEndParent,
     306           0 :                                      aEndOffset, getter_AddRefs(range));
     307           0 :   range.forget(aRange);
     308           0 :   return rv;
     309             : }
     310             : 
     311             : /******************************************************
     312             :  * nsISupports
     313             :  ******************************************************/
     314             : 
     315          57 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange)
     316          57 : NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsRange,
     317             :                                                    DoSetRange(nullptr, 0, nullptr, 0, nullptr))
     318             : 
     319             : // QueryInterface implementation for nsRange
     320          93 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange)
     321           2 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     322           0 :   NS_INTERFACE_MAP_ENTRY(nsIDOMRange)
     323           0 :   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
     324           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMRange)
     325           0 : NS_INTERFACE_MAP_END
     326             : 
     327             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
     328             : 
     329           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange)
     330           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     331           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
     332           0 :   tmp->Reset();
     333             : 
     334             :   // This needs to be unlinked after Reset() is called, as it controls
     335             :   // the result of IsInSelection() which is used by tmp->Reset().
     336           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection);
     337           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     338             : 
     339           2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
     340           2 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
     341           2 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartContainer)
     342           2 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndContainer)
     343           2 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
     344           2 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
     345           2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     346             : 
     347          14 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange)
     348          14 :   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
     349          14 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     350             : 
     351          17 : static void MarkDescendants(nsINode* aNode)
     352             : {
     353             :   // Set NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
     354             :   // descendants unless aNode is already marked as a range common ancestor
     355             :   // or a descendant of one, in which case all of our descendants have the
     356             :   // bit set already.
     357          17 :   if (!aNode->IsSelectionDescendant()) {
     358             :     // don't set the Descendant bit on |aNode| itself
     359          13 :     nsINode* node = aNode->GetNextNode(aNode);
     360          31 :     while (node) {
     361           9 :       node->SetDescendantOfCommonAncestorForRangeInSelection();
     362           9 :       if (!node->IsCommonAncestorForRangeInSelection()) {
     363           9 :         node = node->GetNextNode(aNode);
     364             :       } else {
     365             :         // optimize: skip this sub-tree since it's marked already.
     366           0 :         node = node->GetNextNonChildNode(aNode);
     367             :       }
     368             :     }
     369             :   }
     370          17 : }
     371             : 
     372          11 : static void UnmarkDescendants(nsINode* aNode)
     373             : {
     374             :   // Unset NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
     375             :   // descendants unless aNode is a descendant of another range common ancestor.
     376             :   // Also, exclude descendants of range common ancestors (but not the common
     377             :   // ancestor itself).
     378          11 :   if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
     379             :     // we know |aNode| doesn't have any bit set
     380          11 :     nsINode* node = aNode->GetNextNode(aNode);
     381          29 :     while (node) {
     382           9 :       node->ClearDescendantOfCommonAncestorForRangeInSelection();
     383           9 :       if (!node->IsCommonAncestorForRangeInSelection()) {
     384           8 :         node = node->GetNextNode(aNode);
     385             :       } else {
     386             :         // We found an ancestor of an overlapping range, skip its descendants.
     387           1 :         node = node->GetNextNonChildNode(aNode);
     388             :       }
     389             :     }
     390             :   }
     391          11 : }
     392             : 
     393             : void
     394          17 : nsRange::RegisterCommonAncestor(nsINode* aNode)
     395             : {
     396          17 :   NS_PRECONDITION(aNode, "bad arg");
     397          17 :   NS_ASSERTION(IsInSelection(), "registering range not in selection");
     398             : 
     399          17 :   MarkDescendants(aNode);
     400             : 
     401             :   RangeHashTable* ranges =
     402          17 :     static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
     403          17 :   if (!ranges) {
     404          14 :     ranges = new RangeHashTable;
     405             :     aNode->SetProperty(nsGkAtoms::range, ranges,
     406          14 :                        nsINode::DeleteProperty<nsRange::RangeHashTable>, true);
     407             :   }
     408          17 :   ranges->PutEntry(this);
     409          17 :   aNode->SetCommonAncestorForRangeInSelection();
     410          17 : }
     411             : 
     412             : void
     413          13 : nsRange::UnregisterCommonAncestor(nsINode* aNode)
     414             : {
     415          13 :   NS_PRECONDITION(aNode, "bad arg");
     416          13 :   NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
     417             :   RangeHashTable* ranges =
     418          13 :     static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
     419          13 :   NS_ASSERTION(ranges->GetEntry(this), "unknown range");
     420             : 
     421          13 :   if (ranges->Count() == 1) {
     422          11 :     aNode->ClearCommonAncestorForRangeInSelection();
     423          11 :     aNode->DeleteProperty(nsGkAtoms::range);
     424          11 :     UnmarkDescendants(aNode);
     425             :   } else {
     426           2 :     ranges->RemoveEntry(this);
     427             :   }
     428          13 : }
     429             : 
     430             : /******************************************************
     431             :  * nsIMutationObserver implementation
     432             :  ******************************************************/
     433             : 
     434             : void
     435           0 : nsRange::CharacterDataChanged(nsIDocument* aDocument,
     436             :                               nsIContent* aContent,
     437             :                               CharacterDataChangeInfo* aInfo)
     438             : {
     439           0 :   MOZ_ASSERT(mAssertNextInsertOrAppendIndex == -1,
     440             :              "splitText failed to notify insert/append?");
     441           0 :   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
     442             : 
     443           0 :   nsINode* newRoot = nullptr;
     444           0 :   nsINode* newStartNode = nullptr;
     445           0 :   nsINode* newEndNode = nullptr;
     446           0 :   uint32_t newStartOffset = 0;
     447           0 :   uint32_t newEndOffset = 0;
     448             : 
     449           0 :   if (aInfo->mDetails &&
     450           0 :       aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eSplit) {
     451             :     // If the splitted text node is immediately before a range boundary point
     452             :     // that refers to a child index (i.e. its parent is the boundary container)
     453             :     // then we need to increment the corresponding offset to account for the new
     454             :     // text node that will be inserted.  If so, we need to prevent the next
     455             :     // ContentInserted or ContentAppended for this range from incrementing it
     456             :     // again (when the new text node is notified).
     457           0 :     nsINode* parentNode = aContent->GetParentNode();
     458           0 :     int32_t index = -1;
     459           0 :     if (parentNode == mEndContainer && mEndOffset > 0 &&
     460           0 :         (index = parentNode->IndexOf(aContent)) + 1 == mEndOffset) {
     461           0 :       ++mEndOffset;
     462           0 :       mEndOffsetWasIncremented = true;
     463             :     }
     464           0 :     if (parentNode == mStartContainer && mStartOffset > 0 &&
     465           0 :         (index != -1 ? index : parentNode->IndexOf(aContent)) + 1 == mStartOffset) {
     466           0 :       ++mStartOffset;
     467           0 :       mStartOffsetWasIncremented = true;
     468             :     }
     469             : #ifdef DEBUG
     470           0 :     if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
     471           0 :       mAssertNextInsertOrAppendIndex =
     472           0 :         (mStartOffsetWasIncremented ? mStartOffset : mEndOffset) - 1;
     473           0 :       mAssertNextInsertOrAppendNode = aInfo->mDetails->mNextSibling;
     474             :     }
     475             : #endif
     476             :   }
     477             : 
     478             :   // If the changed node contains our start boundary and the change starts
     479             :   // before the boundary we'll need to adjust the offset.
     480           0 :   if (aContent == mStartContainer &&
     481           0 :       aInfo->mChangeStart < static_cast<uint32_t>(mStartOffset)) {
     482           0 :     if (aInfo->mDetails) {
     483             :       // splitText(), aInfo->mDetails->mNextSibling is the new text node
     484           0 :       NS_ASSERTION(aInfo->mDetails->mType ==
     485             :                    CharacterDataChangeInfo::Details::eSplit,
     486             :                    "only a split can start before the end");
     487           0 :       NS_ASSERTION(static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd + 1,
     488             :                    "mStartOffset is beyond the end of this node");
     489           0 :       newStartOffset = static_cast<uint32_t>(mStartOffset) - aInfo->mChangeStart;
     490           0 :       newStartNode = aInfo->mDetails->mNextSibling;
     491           0 :       if (MOZ_UNLIKELY(aContent == mRoot)) {
     492           0 :         newRoot = IsValidBoundary(newStartNode);
     493             :       }
     494             : 
     495             :       bool isCommonAncestor =
     496           0 :         IsInSelection() && mStartContainer == mEndContainer;
     497           0 :       if (isCommonAncestor) {
     498           0 :         UnregisterCommonAncestor(mStartContainer);
     499           0 :         RegisterCommonAncestor(newStartNode);
     500             :       }
     501           0 :       if (mStartContainer->IsDescendantOfCommonAncestorForRangeInSelection()) {
     502           0 :         newStartNode->SetDescendantOfCommonAncestorForRangeInSelection();
     503             :       }
     504             :     } else {
     505             :       // If boundary is inside changed text, position it before change
     506             :       // else adjust start offset for the change in length.
     507           0 :       mStartOffset = static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd ?
     508           0 :         aInfo->mChangeStart :
     509           0 :         mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
     510           0 :           aInfo->mReplaceLength;
     511             :     }
     512             :   }
     513             : 
     514             :   // Do the same thing for the end boundary, except for splitText of a node
     515             :   // with no parent then only switch to the new node if the start boundary
     516             :   // did so too (otherwise the range would end up with disconnected nodes).
     517           0 :   if (aContent == mEndContainer &&
     518           0 :       aInfo->mChangeStart < static_cast<uint32_t>(mEndOffset)) {
     519           0 :     if (aInfo->mDetails && (aContent->GetParentNode() || newStartNode)) {
     520             :       // splitText(), aInfo->mDetails->mNextSibling is the new text node
     521           0 :       NS_ASSERTION(aInfo->mDetails->mType ==
     522             :                    CharacterDataChangeInfo::Details::eSplit,
     523             :                    "only a split can start before the end");
     524           0 :       NS_ASSERTION(static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd + 1,
     525             :                    "mEndOffset is beyond the end of this node");
     526           0 :       newEndOffset = static_cast<uint32_t>(mEndOffset) - aInfo->mChangeStart;
     527           0 :       newEndNode = aInfo->mDetails->mNextSibling;
     528             : 
     529             :       bool isCommonAncestor =
     530           0 :         IsInSelection() && mStartContainer == mEndContainer;
     531           0 :       if (isCommonAncestor && !newStartNode) {
     532             :         // The split occurs inside the range.
     533           0 :         UnregisterCommonAncestor(mStartContainer);
     534           0 :         RegisterCommonAncestor(mStartContainer->GetParentNode());
     535           0 :         newEndNode->SetDescendantOfCommonAncestorForRangeInSelection();
     536           0 :       } else if (mEndContainer->
     537           0 :                    IsDescendantOfCommonAncestorForRangeInSelection()) {
     538           0 :         newEndNode->SetDescendantOfCommonAncestorForRangeInSelection();
     539             :       }
     540             :     } else {
     541           0 :       mEndOffset = static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd ?
     542           0 :         aInfo->mChangeStart :
     543           0 :         mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
     544           0 :           aInfo->mReplaceLength;
     545             :     }
     546             :   }
     547             : 
     548           0 :   if (aInfo->mDetails &&
     549           0 :       aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eMerge) {
     550             :     // normalize(), aInfo->mDetails->mNextSibling is the merged text node
     551             :     // that will be removed
     552           0 :     nsIContent* removed = aInfo->mDetails->mNextSibling;
     553           0 :     if (removed == mStartContainer) {
     554           0 :       newStartOffset = static_cast<uint32_t>(mStartOffset) + aInfo->mChangeStart;
     555           0 :       newStartNode = aContent;
     556           0 :       if (MOZ_UNLIKELY(removed == mRoot)) {
     557           0 :         newRoot = IsValidBoundary(newStartNode);
     558             :       }
     559             :     }
     560           0 :     if (removed == mEndContainer) {
     561           0 :       newEndOffset = static_cast<uint32_t>(mEndOffset) + aInfo->mChangeStart;
     562           0 :       newEndNode = aContent;
     563           0 :       if (MOZ_UNLIKELY(removed == mRoot)) {
     564           0 :         newRoot = IsValidBoundary(newEndNode);
     565             :       }
     566             :     }
     567             :     // When the removed text node's parent is one of our boundary nodes we may
     568             :     // need to adjust the offset to account for the removed node. However,
     569             :     // there will also be a ContentRemoved notification later so the only cases
     570             :     // we need to handle here is when the removed node is the text node after
     571             :     // the boundary.  (The m*Offset > 0 check is an optimization - a boundary
     572             :     // point before the first child is never affected by normalize().)
     573           0 :     nsINode* parentNode = aContent->GetParentNode();
     574           0 :     if (parentNode == mStartContainer && mStartOffset > 0 &&
     575           0 :         uint32_t(mStartOffset) < parentNode->GetChildCount() &&
     576           0 :         removed == parentNode->GetChildAt(mStartOffset)) {
     577           0 :       newStartNode = aContent;
     578           0 :       newStartOffset = aInfo->mChangeStart;
     579             :     }
     580           0 :     if (parentNode == mEndContainer && mEndOffset > 0 &&
     581           0 :         uint32_t(mEndOffset) < parentNode->GetChildCount() &&
     582           0 :         removed == parentNode->GetChildAt(mEndOffset)) {
     583           0 :       newEndNode = aContent;
     584           0 :       newEndOffset = aInfo->mChangeEnd;
     585             :     }
     586             :   }
     587             : 
     588           0 :   if (newStartNode || newEndNode) {
     589           0 :     if (!newStartNode) {
     590           0 :       newStartNode = mStartContainer;
     591           0 :       newStartOffset = mStartOffset;
     592             :     }
     593           0 :     if (!newEndNode) {
     594           0 :       newEndNode = mEndContainer;
     595           0 :       newEndOffset = mEndOffset;
     596             :     }
     597           0 :     DoSetRange(newStartNode, newStartOffset, newEndNode, newEndOffset,
     598           0 :                newRoot ? newRoot : mRoot.get(),
     599           0 :                !newEndNode->GetParentNode() || !newStartNode->GetParentNode());
     600             :   }
     601           0 : }
     602             : 
     603             : void
     604           0 : nsRange::ContentAppended(nsIDocument* aDocument,
     605             :                          nsIContent*  aContainer,
     606             :                          nsIContent*  aFirstNewContent,
     607             :                          int32_t      aNewIndexInContainer)
     608             : {
     609           0 :   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
     610             : 
     611           0 :   nsINode* container = NODE_FROM(aContainer, aDocument);
     612           0 :   if (container->IsSelectionDescendant() && IsInSelection()) {
     613           0 :     nsINode* child = aFirstNewContent;
     614           0 :     while (child) {
     615           0 :       if (!child->IsDescendantOfCommonAncestorForRangeInSelection()) {
     616           0 :         MarkDescendants(child);
     617           0 :         child->SetDescendantOfCommonAncestorForRangeInSelection();
     618             :       }
     619           0 :       child = child->GetNextSibling();
     620             :     }
     621             :   }
     622             : 
     623           0 :   if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
     624           0 :     MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aNewIndexInContainer);
     625           0 :     MOZ_ASSERT(mAssertNextInsertOrAppendNode == aFirstNewContent);
     626           0 :     MOZ_ASSERT(aFirstNewContent->IsNodeOfType(nsINode::eDATA_NODE));
     627           0 :     mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
     628             : #ifdef DEBUG
     629           0 :     mAssertNextInsertOrAppendIndex = -1;
     630           0 :     mAssertNextInsertOrAppendNode = nullptr;
     631             : #endif
     632             :   }
     633           0 : }
     634             : 
     635             : void
     636           0 : nsRange::ContentInserted(nsIDocument* aDocument,
     637             :                          nsIContent* aContainer,
     638             :                          nsIContent* aChild,
     639             :                          int32_t aIndexInContainer)
     640             : {
     641           0 :   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
     642             : 
     643           0 :   nsINode* container = NODE_FROM(aContainer, aDocument);
     644             : 
     645             :   // Adjust position if a sibling was inserted.
     646           0 :   if (container == mStartContainer && aIndexInContainer < mStartOffset &&
     647           0 :       !mStartOffsetWasIncremented) {
     648           0 :     ++mStartOffset;
     649             :   }
     650           0 :   if (container == mEndContainer && aIndexInContainer < mEndOffset &&
     651           0 :       !mEndOffsetWasIncremented) {
     652           0 :     ++mEndOffset;
     653             :   }
     654           0 :   if (container->IsSelectionDescendant() &&
     655           0 :       !aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
     656           0 :     MarkDescendants(aChild);
     657           0 :     aChild->SetDescendantOfCommonAncestorForRangeInSelection();
     658             :   }
     659             : 
     660           0 :   if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
     661           0 :     MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aIndexInContainer);
     662           0 :     MOZ_ASSERT(mAssertNextInsertOrAppendNode == aChild);
     663           0 :     MOZ_ASSERT(aChild->IsNodeOfType(nsINode::eDATA_NODE));
     664           0 :     mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
     665             : #ifdef DEBUG
     666           0 :     mAssertNextInsertOrAppendIndex = -1;
     667           0 :     mAssertNextInsertOrAppendNode = nullptr;
     668             : #endif
     669             :   }
     670           0 : }
     671             : 
     672             : void
     673           0 : nsRange::ContentRemoved(nsIDocument* aDocument,
     674             :                         nsIContent* aContainer,
     675             :                         nsIContent* aChild,
     676             :                         int32_t aIndexInContainer,
     677             :                         nsIContent* aPreviousSibling)
     678             : {
     679           0 :   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
     680           0 :   MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented &&
     681             :              mAssertNextInsertOrAppendIndex == -1,
     682             :              "splitText failed to notify insert/append?");
     683             : 
     684           0 :   nsINode* container = NODE_FROM(aContainer, aDocument);
     685           0 :   bool gravitateStart = false;
     686           0 :   bool gravitateEnd = false;
     687           0 :   bool didCheckStartParentDescendant = false;
     688             : 
     689             :   // Adjust position if a sibling was removed...
     690           0 :   if (container == mStartContainer) {
     691           0 :     if (aIndexInContainer < mStartOffset) {
     692           0 :       --mStartOffset;
     693             :     }
     694             :   } else { // ...or gravitate if an ancestor was removed.
     695           0 :     didCheckStartParentDescendant = true;
     696             :     gravitateStart =
     697           0 :       nsContentUtils::ContentIsDescendantOf(mStartContainer, aChild);
     698             :   }
     699             : 
     700             :   // Do same thing for end boundry.
     701           0 :   if (container == mEndContainer) {
     702           0 :     if (aIndexInContainer < mEndOffset) {
     703           0 :       --mEndOffset;
     704             :     }
     705           0 :   } else if (didCheckStartParentDescendant &&
     706           0 :              mStartContainer == mEndContainer) {
     707           0 :     gravitateEnd = gravitateStart;
     708             :   } else {
     709           0 :     gravitateEnd = nsContentUtils::ContentIsDescendantOf(mEndContainer, aChild);
     710             :   }
     711             : 
     712           0 :   if (gravitateStart || gravitateEnd) {
     713           0 :     DoSetRange(gravitateStart ? container : mStartContainer.get(),
     714             :                gravitateStart ? aIndexInContainer : mStartOffset,
     715           0 :                gravitateEnd ? container : mEndContainer.get(),
     716             :                gravitateEnd ? aIndexInContainer : mEndOffset,
     717           0 :                mRoot);
     718             :   }
     719           0 :   if (container->IsSelectionDescendant() &&
     720           0 :       aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
     721           0 :     aChild->ClearDescendantOfCommonAncestorForRangeInSelection();
     722           0 :     UnmarkDescendants(aChild);
     723             :   }
     724           0 : }
     725             : 
     726             : void
     727           0 : nsRange::ParentChainChanged(nsIContent *aContent)
     728             : {
     729           0 :   MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented &&
     730             :              mAssertNextInsertOrAppendIndex == -1,
     731             :              "splitText failed to notify insert/append?");
     732           0 :   NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
     733           0 :   nsINode* newRoot = IsValidBoundary(mStartContainer);
     734           0 :   NS_ASSERTION(newRoot, "No valid boundary or root found!");
     735           0 :   if (newRoot != IsValidBoundary(mEndContainer)) {
     736             :     // Sometimes ordering involved in cycle collection can lead to our
     737             :     // start parent and/or end parent being disconnected from our root
     738             :     // without our getting a ContentRemoved notification.
     739             :     // See bug 846096 for more details.
     740           0 :     NS_ASSERTION(mEndContainer->IsInNativeAnonymousSubtree(),
     741             :                  "This special case should happen only with "
     742             :                  "native-anonymous content");
     743             :     // When that happens, bail out and set pointers to null; since we're
     744             :     // in cycle collection and unreachable it shouldn't matter.
     745           0 :     Reset();
     746           0 :     return;
     747             :   }
     748             :   // This is safe without holding a strong ref to self as long as the change
     749             :   // of mRoot is the last thing in DoSetRange.
     750           0 :   DoSetRange(mStartContainer, mStartOffset, mEndContainer, mEndOffset, newRoot);
     751             : }
     752             : 
     753             : /******************************************************
     754             :  * Utilities for comparing points: API from nsIDOMRange
     755             :  ******************************************************/
     756             : NS_IMETHODIMP
     757           0 : nsRange::IsPointInRange(nsIDOMNode* aContainer, int32_t aOffset, bool* aResult)
     758             : {
     759           0 :   nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
     760           0 :   if (!container) {
     761           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
     762             :   }
     763             : 
     764           0 :   ErrorResult rv;
     765           0 :   *aResult = IsPointInRange(*container, aOffset, rv);
     766           0 :   return rv.StealNSResult();
     767             : }
     768             : 
     769             : bool
     770           0 : nsRange::IsPointInRange(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
     771             : {
     772           0 :   uint16_t compareResult = ComparePoint(aContainer, aOffset, aRv);
     773             :   // If the node isn't in the range's document, it clearly isn't in the range.
     774           0 :   if (aRv.ErrorCodeIs(NS_ERROR_DOM_WRONG_DOCUMENT_ERR)) {
     775           0 :     aRv.SuppressException();
     776           0 :     return false;
     777             :   }
     778             : 
     779           0 :   return compareResult == 0;
     780             : }
     781             : 
     782             : // returns -1 if point is before range, 0 if point is in range,
     783             : // 1 if point is after range.
     784             : NS_IMETHODIMP
     785           0 : nsRange::ComparePoint(nsIDOMNode* aContainer, int32_t aOffset, int16_t* aResult)
     786             : {
     787           0 :   nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
     788           0 :   NS_ENSURE_TRUE(container, NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
     789             : 
     790           0 :   ErrorResult rv;
     791           0 :   *aResult = ComparePoint(*container, aOffset, rv);
     792           0 :   return rv.StealNSResult();
     793             : }
     794             : 
     795             : int16_t
     796           0 : nsRange::ComparePoint(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
     797             : {
     798             :   // our range is in a good state?
     799           0 :   if (!mIsPositioned) {
     800           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
     801           0 :     return 0;
     802             :   }
     803             : 
     804           0 :   if (!nsContentUtils::ContentIsDescendantOf(&aContainer, mRoot)) {
     805           0 :     aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
     806           0 :     return 0;
     807             :   }
     808             : 
     809           0 :   if (aContainer.NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
     810           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
     811           0 :     return 0;
     812             :   }
     813             : 
     814           0 :   if (aOffset > aContainer.Length()) {
     815           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     816           0 :     return 0;
     817             :   }
     818             : 
     819             :   int32_t cmp;
     820           0 :   if ((cmp = nsContentUtils::ComparePoints(&aContainer, aOffset,
     821             :                                            mStartContainer,
     822             :                                            mStartOffset)) <= 0) {
     823             : 
     824           0 :     return cmp;
     825             :   }
     826           0 :   if (nsContentUtils::ComparePoints(mEndContainer, mEndOffset,
     827             :                                     &aContainer, aOffset) == -1) {
     828           0 :     return 1;
     829             :   }
     830             : 
     831           0 :   return 0;
     832             : }
     833             : 
     834             : NS_IMETHODIMP
     835           0 : nsRange::IntersectsNode(nsIDOMNode* aNode, bool* aResult)
     836             : {
     837           0 :   *aResult = false;
     838             : 
     839           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
     840             :   // TODO: This should throw a TypeError.
     841           0 :   NS_ENSURE_ARG(node);
     842             : 
     843           0 :   ErrorResult rv;
     844           0 :   *aResult = IntersectsNode(*node, rv);
     845           0 :   return rv.StealNSResult();
     846             : }
     847             : 
     848             : bool
     849           0 : nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv)
     850             : {
     851           0 :   if (!mIsPositioned) {
     852           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
     853           0 :     return false;
     854             :   }
     855             : 
     856             :   // Step 3.
     857           0 :   nsINode* parent = aNode.GetParentNode();
     858           0 :   if (!parent) {
     859             :     // Steps 2 and 4.
     860             :     // |parent| is null, so |node|'s root is |node| itself.
     861           0 :     return GetRoot() == &aNode;
     862             :   }
     863             : 
     864             :   // Step 5.
     865           0 :   int32_t nodeIndex = parent->IndexOf(&aNode);
     866             : 
     867             :   // Steps 6-7.
     868             :   // Note: if disconnected is true, ComparePoints returns 1.
     869           0 :   bool disconnected = false;
     870           0 :   bool result = nsContentUtils::ComparePoints(mStartContainer, mStartOffset,
     871             :                                              parent, nodeIndex + 1,
     872           0 :                                              &disconnected) < 0 &&
     873           0 :                nsContentUtils::ComparePoints(parent, nodeIndex,
     874             :                                              mEndContainer, mEndOffset,
     875           0 :                                              &disconnected) < 0;
     876             : 
     877             :   // Step 2.
     878           0 :   if (disconnected) {
     879           0 :     result = false;
     880             :   }
     881           0 :   return result;
     882             : }
     883             : 
     884             : /******************************************************
     885             :  * Private helper routines
     886             :  ******************************************************/
     887             : 
     888             : // It's important that all setting of the range start/end points
     889             : // go through this function, which will do all the right voodoo
     890             : // for content notification of range ownership.
     891             : // Calling DoSetRange with either parent argument null will collapse
     892             : // the range to have both endpoints point to the other node
     893             : void
     894          41 : nsRange::DoSetRange(nsINode* aStartN, int32_t aStartOffset,
     895             :                     nsINode* aEndN, int32_t aEndOffset,
     896             :                     nsINode* aRoot, bool aNotInsertedYet)
     897             : {
     898          41 :   NS_PRECONDITION((aStartN && aEndN && aRoot) ||
     899             :                   (!aStartN && !aEndN && !aRoot),
     900             :                   "Set all or none");
     901          41 :   NS_PRECONDITION(!aRoot || aNotInsertedYet ||
     902             :                   (nsContentUtils::ContentIsDescendantOf(aStartN, aRoot) &&
     903             :                    nsContentUtils::ContentIsDescendantOf(aEndN, aRoot) &&
     904             :                    aRoot == IsValidBoundary(aStartN) &&
     905             :                    aRoot == IsValidBoundary(aEndN)),
     906             :                   "Wrong root");
     907          41 :   NS_PRECONDITION(!aRoot ||
     908             :                   (aStartN->IsNodeOfType(nsINode::eCONTENT) &&
     909             :                    aEndN->IsNodeOfType(nsINode::eCONTENT) &&
     910             :                    aRoot ==
     911             :                     static_cast<nsIContent*>(aStartN)->GetBindingParent() &&
     912             :                    aRoot ==
     913             :                     static_cast<nsIContent*>(aEndN)->GetBindingParent()) ||
     914             :                   (!aRoot->GetParentNode() &&
     915             :                    (aRoot->IsNodeOfType(nsINode::eDOCUMENT) ||
     916             :                     aRoot->IsNodeOfType(nsINode::eATTRIBUTE) ||
     917             :                     aRoot->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) ||
     918             :                      /*For backward compatibility*/
     919             :                     aRoot->IsNodeOfType(nsINode::eCONTENT))),
     920             :                   "Bad root");
     921             : 
     922          41 :   if (mRoot != aRoot) {
     923          27 :     if (mRoot) {
     924          12 :       mRoot->RemoveMutationObserver(this);
     925             :     }
     926          27 :     if (aRoot) {
     927          17 :       aRoot->AddMutationObserver(this);
     928             :     }
     929             :   }
     930             :   bool checkCommonAncestor =
     931          82 :     (mStartContainer != aStartN || mEndContainer != aEndN) &&
     932          68 :     IsInSelection() && !aNotInsertedYet;
     933          41 :   nsINode* oldCommonAncestor = checkCommonAncestor ? GetCommonAncestor() : nullptr;
     934          41 :   mStartContainer = aStartN;
     935          41 :   mStartOffset = aStartOffset;
     936          41 :   mEndContainer = aEndN;
     937          41 :   mEndOffset = aEndOffset;
     938          41 :   mIsPositioned = !!mStartContainer;
     939          41 :   if (checkCommonAncestor) {
     940           0 :     nsINode* newCommonAncestor = GetCommonAncestor();
     941           0 :     if (newCommonAncestor != oldCommonAncestor) {
     942           0 :       if (oldCommonAncestor) {
     943           0 :         UnregisterCommonAncestor(oldCommonAncestor);
     944             :       }
     945           0 :       if (newCommonAncestor) {
     946           0 :         RegisterCommonAncestor(newCommonAncestor);
     947             :       } else {
     948           0 :         NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes");
     949           0 :         mSelection = nullptr;
     950             :       }
     951             :     }
     952             :   }
     953             : 
     954             :   // This needs to be the last thing this function does, other than notifying
     955             :   // selection listeners. See comment in ParentChainChanged.
     956          41 :   mRoot = aRoot;
     957             : 
     958             :   // Notify any selection listeners. This has to occur last because otherwise the world
     959             :   // could be observed by a selection listener while the range was in an invalid state.
     960          41 :   if (mSelection) {
     961             :     // Our internal code should not move focus with using this instance while
     962             :     // it's calling Selection::NotifySelectionListeners() which may move focus
     963             :     // or calls selection listeners.  So, let's set mCalledByJS to false here
     964             :     // since non-*JS() methods don't set it to false.
     965           0 :     AutoCalledByJSRestore calledByJSRestorer(*this);
     966           0 :     mCalledByJS = false;
     967             :     // Be aware, this range may be modified or stop being a range for selection
     968             :     // after this call.  Additionally, the selection instance may have gone.
     969           0 :     RefPtr<Selection> selection = mSelection;
     970           0 :     selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
     971             :   }
     972          41 : }
     973             : 
     974             : static int32_t
     975           0 : IndexOf(nsINode* aChild)
     976             : {
     977           0 :   nsINode* parent = aChild->GetParentNode();
     978             : 
     979           0 :   return parent ? parent->IndexOf(aChild) : -1;
     980             : }
     981             : 
     982             : void
     983          30 : nsRange::SetSelection(mozilla::dom::Selection* aSelection)
     984             : {
     985          30 :   if (mSelection == aSelection) {
     986           0 :     return;
     987             :   }
     988             :   // At least one of aSelection and mSelection must be null
     989             :   // aSelection will be null when we are removing from a selection
     990             :   // and a range can't be in more than one selection at a time,
     991             :   // thus mSelection must be null too.
     992          30 :   MOZ_ASSERT(!aSelection || !mSelection);
     993             : 
     994          30 :   mSelection = aSelection;
     995          30 :   nsINode* commonAncestor = GetCommonAncestor();
     996          30 :   NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
     997          30 :   if (mSelection) {
     998          17 :     RegisterCommonAncestor(commonAncestor);
     999             :   } else {
    1000          13 :     UnregisterCommonAncestor(commonAncestor);
    1001             :   }
    1002             : }
    1003             : 
    1004             : nsINode*
    1005          30 : nsRange::GetCommonAncestor() const
    1006             : {
    1007          60 :   return mIsPositioned ?
    1008          30 :     nsContentUtils::GetCommonAncestor(mStartContainer, mEndContainer) :
    1009          30 :     nullptr;
    1010             : }
    1011             : 
    1012             : void
    1013           0 : nsRange::Reset()
    1014             : {
    1015           0 :   DoSetRange(nullptr, 0, nullptr, 0, nullptr);
    1016           0 : }
    1017             : 
    1018             : /******************************************************
    1019             :  * public functionality
    1020             :  ******************************************************/
    1021             : 
    1022             : NS_IMETHODIMP
    1023           0 : nsRange::GetStartContainer(nsIDOMNode** aStartContainer)
    1024             : {
    1025           0 :   if (!mIsPositioned)
    1026           0 :     return NS_ERROR_NOT_INITIALIZED;
    1027             : 
    1028           0 :   return CallQueryInterface(mStartContainer, aStartContainer);
    1029             : }
    1030             : 
    1031             : nsINode*
    1032          18 : nsRange::GetStartContainer(ErrorResult& aRv) const
    1033             : {
    1034          18 :   if (!mIsPositioned) {
    1035           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    1036           0 :     return nullptr;
    1037             :   }
    1038             : 
    1039          18 :   return mStartContainer;
    1040             : }
    1041             : 
    1042             : NS_IMETHODIMP
    1043           0 : nsRange::GetStartOffset(int32_t* aStartOffset)
    1044             : {
    1045           0 :   if (!mIsPositioned)
    1046           0 :     return NS_ERROR_NOT_INITIALIZED;
    1047             : 
    1048           0 :   *aStartOffset = mStartOffset;
    1049             : 
    1050           0 :   return NS_OK;
    1051             : }
    1052             : 
    1053             : uint32_t
    1054          18 : nsRange::GetStartOffset(ErrorResult& aRv) const
    1055             : {
    1056          18 :   if (!mIsPositioned) {
    1057           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    1058           0 :     return 0;
    1059             :   }
    1060             : 
    1061          18 :   return mStartOffset;
    1062             : }
    1063             : 
    1064             : NS_IMETHODIMP
    1065           0 : nsRange::GetEndContainer(nsIDOMNode** aEndContainer)
    1066             : {
    1067           0 :   if (!mIsPositioned)
    1068           0 :     return NS_ERROR_NOT_INITIALIZED;
    1069             : 
    1070           0 :   return CallQueryInterface(mEndContainer, aEndContainer);
    1071             : }
    1072             : 
    1073             : nsINode*
    1074          18 : nsRange::GetEndContainer(ErrorResult& aRv) const
    1075             : {
    1076          18 :   if (!mIsPositioned) {
    1077           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    1078           0 :     return nullptr;
    1079             :   }
    1080             : 
    1081          18 :   return mEndContainer;
    1082             : }
    1083             : 
    1084             : NS_IMETHODIMP
    1085           0 : nsRange::GetEndOffset(int32_t* aEndOffset)
    1086             : {
    1087           0 :   if (!mIsPositioned)
    1088           0 :     return NS_ERROR_NOT_INITIALIZED;
    1089             : 
    1090           0 :   *aEndOffset = mEndOffset;
    1091             : 
    1092           0 :   return NS_OK;
    1093             : }
    1094             : 
    1095             : uint32_t
    1096          17 : nsRange::GetEndOffset(ErrorResult& aRv) const
    1097             : {
    1098          17 :   if (!mIsPositioned) {
    1099           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    1100           0 :     return 0;
    1101             :   }
    1102             : 
    1103          17 :   return mEndOffset;
    1104             : }
    1105             : 
    1106             : NS_IMETHODIMP
    1107           0 : nsRange::GetCollapsed(bool* aIsCollapsed)
    1108             : {
    1109           0 :   if (!mIsPositioned)
    1110           0 :     return NS_ERROR_NOT_INITIALIZED;
    1111             : 
    1112           0 :   *aIsCollapsed = Collapsed();
    1113             : 
    1114           0 :   return NS_OK;
    1115             : }
    1116             : 
    1117             : nsINode*
    1118           0 : nsRange::GetCommonAncestorContainer(ErrorResult& aRv) const
    1119             : {
    1120           0 :   if (!mIsPositioned) {
    1121           0 :     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    1122           0 :     return nullptr;
    1123             :   }
    1124             : 
    1125           0 :   return nsContentUtils::GetCommonAncestor(mStartContainer, mEndContainer);
    1126             : }
    1127             : 
    1128             : NS_IMETHODIMP
    1129           0 : nsRange::GetCommonAncestorContainer(nsIDOMNode** aCommonParent)
    1130             : {
    1131           0 :   ErrorResult rv;
    1132           0 :   nsINode* commonAncestor = GetCommonAncestorContainer(rv);
    1133           0 :   if (commonAncestor) {
    1134           0 :     NS_ADDREF(*aCommonParent = commonAncestor->AsDOMNode());
    1135             :   } else {
    1136           0 :     *aCommonParent = nullptr;
    1137             :   }
    1138             : 
    1139           0 :   return rv.StealNSResult();
    1140             : }
    1141             : 
    1142             : /* static */
    1143             : bool
    1144          34 : nsRange::IsValidOffset(nsINode* aNode, int32_t aOffset)
    1145             : {
    1146          34 :   return aNode &&
    1147          68 :          aOffset >= 0 &&
    1148          68 :          static_cast<size_t>(aOffset) <= aNode->Length();
    1149             : }
    1150             : 
    1151             : nsINode*
    1152          63 : nsRange::IsValidBoundary(nsINode* aNode)
    1153             : {
    1154          63 :   if (!aNode) {
    1155           0 :     return nullptr;
    1156             :   }
    1157             : 
    1158          63 :   if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
    1159          57 :     if (aNode->NodeInfo()->NameAtom() == nsGkAtoms::documentTypeNodeName) {
    1160           0 :       return nullptr;
    1161             :     }
    1162             : 
    1163          57 :     nsIContent* content = static_cast<nsIContent*>(aNode);
    1164             : 
    1165          57 :     if (!mMaySpanAnonymousSubtrees) {
    1166             :       // If the node is in a shadow tree then the ShadowRoot is the root.
    1167          57 :       ShadowRoot* containingShadow = content->GetContainingShadow();
    1168          57 :       if (containingShadow) {
    1169           0 :         return containingShadow;
    1170             :       }
    1171             : 
    1172             :       // If the node has a binding parent, that should be the root.
    1173             :       // XXXbz maybe only for native anonymous content?
    1174          57 :       nsINode* root = content->GetBindingParent();
    1175          57 :       if (root) {
    1176          45 :         return root;
    1177             :       }
    1178             :     }
    1179             :   }
    1180             : 
    1181             :   // Elements etc. must be in document or in document fragment,
    1182             :   // text nodes in document, in document fragment or in attribute.
    1183          18 :   nsINode* root = aNode->GetUncomposedDoc();
    1184          18 :   if (root) {
    1185          18 :     return root;
    1186             :   }
    1187             : 
    1188           0 :   root = aNode->SubtreeRoot();
    1189             : 
    1190           0 :   NS_ASSERTION(!root->IsNodeOfType(nsINode::eDOCUMENT),
    1191             :                "GetUncomposedDoc should have returned a doc");
    1192             : 
    1193             :   // We allow this because of backward compatibility.
    1194           0 :   return root;
    1195             : }
    1196             : 
    1197             : void
    1198           2 : nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
    1199             : {
    1200           4 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1201           2 :   mCalledByJS = true;
    1202           2 :   SetStart(aNode, aOffset, aErr);
    1203           2 : }
    1204             : 
    1205             : void
    1206           2 : nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
    1207             : {
    1208           4 :  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    1209           2 :      !nsContentUtils::CanCallerAccess(&aNode)) {
    1210           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1211           0 :     return;
    1212             :   }
    1213             : 
    1214           4 :   AutoInvalidateSelection atEndOfBlock(this);
    1215           2 :   aRv = SetStart(&aNode, aOffset);
    1216             : }
    1217             : 
    1218             : NS_IMETHODIMP
    1219           0 : nsRange::SetStart(nsIDOMNode* aContainer, int32_t aOffset)
    1220             : {
    1221           0 :   nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
    1222           0 :   if (!container) {
    1223           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
    1224             :   }
    1225             : 
    1226           0 :   ErrorResult rv;
    1227           0 :   SetStart(*container, aOffset, rv);
    1228           0 :   return rv.StealNSResult();
    1229             : }
    1230             : 
    1231             : /* virtual */ nsresult
    1232           2 : nsRange::SetStart(nsINode* aContainer, int32_t aOffset)
    1233             : {
    1234           2 :   nsINode* newRoot = IsValidBoundary(aContainer);
    1235           2 :   if (!newRoot) {
    1236           0 :     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
    1237             :   }
    1238             : 
    1239           2 :   if (!IsValidOffset(aContainer, aOffset)) {
    1240           0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1241             :   }
    1242             : 
    1243             :   // Collapse if not positioned yet, if positioned in another doc or
    1244             :   // if the new start is after end.
    1245           2 :   if (!mIsPositioned || newRoot != mRoot ||
    1246           0 :       nsContentUtils::ComparePoints(aContainer, aOffset,
    1247             :                                     mEndContainer, mEndOffset) == 1) {
    1248           2 :     DoSetRange(aContainer, aOffset, aContainer, aOffset, newRoot);
    1249             : 
    1250           2 :     return NS_OK;
    1251             :   }
    1252             : 
    1253           0 :   DoSetRange(aContainer, aOffset, mEndContainer, mEndOffset, mRoot);
    1254             : 
    1255           0 :   return NS_OK;
    1256             : }
    1257             : 
    1258             : void
    1259           0 : nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr)
    1260             : {
    1261           0 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1262           0 :   mCalledByJS = true;
    1263           0 :   SetStartBefore(aNode, aErr);
    1264           0 : }
    1265             : 
    1266             : void
    1267           0 : nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv)
    1268             : {
    1269           0 :   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    1270           0 :       !nsContentUtils::CanCallerAccess(&aNode)) {
    1271           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1272           0 :     return;
    1273             :   }
    1274             : 
    1275           0 :   AutoInvalidateSelection atEndOfBlock(this);
    1276           0 :   int32_t offset = -1;
    1277           0 :   nsINode* container = GetContainerAndOffsetBefore(&aNode, &offset);
    1278           0 :   aRv = SetStart(container, offset);
    1279             : }
    1280             : 
    1281             : NS_IMETHODIMP
    1282           0 : nsRange::SetStartBefore(nsIDOMNode* aSibling)
    1283             : {
    1284           0 :   nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
    1285           0 :   if (!sibling) {
    1286           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
    1287             :   }
    1288             : 
    1289           0 :   ErrorResult rv;
    1290           0 :   SetStartBefore(*sibling, rv);
    1291           0 :   return rv.StealNSResult();
    1292             : }
    1293             : 
    1294             : void
    1295           0 : nsRange::SetStartAfterJS(nsINode& aNode, ErrorResult& aErr)
    1296             : {
    1297           0 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1298           0 :   mCalledByJS = true;
    1299           0 :   SetStartAfter(aNode, aErr);
    1300           0 : }
    1301             : 
    1302             : void
    1303           0 : nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv)
    1304             : {
    1305           0 :   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    1306           0 :       !nsContentUtils::CanCallerAccess(&aNode)) {
    1307           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1308           0 :     return;
    1309             :   }
    1310             : 
    1311           0 :   AutoInvalidateSelection atEndOfBlock(this);
    1312           0 :   int32_t offset = -1;
    1313           0 :   nsINode* container = GetContainerAndOffsetAfter(&aNode, &offset);
    1314           0 :   aRv = SetStart(container, offset);
    1315             : }
    1316             : 
    1317             : NS_IMETHODIMP
    1318           0 : nsRange::SetStartAfter(nsIDOMNode* aSibling)
    1319             : {
    1320           0 :   nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
    1321           0 :   if (!sibling) {
    1322           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
    1323             :   }
    1324             : 
    1325           0 :   ErrorResult rv;
    1326           0 :   SetStartAfter(*sibling, rv);
    1327           0 :   return rv.StealNSResult();
    1328             : }
    1329             : 
    1330             : void
    1331           2 : nsRange::SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
    1332             : {
    1333           4 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1334           2 :   mCalledByJS = true;
    1335           2 :   SetEnd(aNode, aOffset, aErr);
    1336           2 : }
    1337             : 
    1338             : void
    1339           2 : nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
    1340             : {
    1341           4 :  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    1342           2 :      !nsContentUtils::CanCallerAccess(&aNode)) {
    1343           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1344           0 :     return;
    1345             :   }
    1346           4 :   AutoInvalidateSelection atEndOfBlock(this);
    1347           2 :   aRv = SetEnd(&aNode, aOffset);
    1348             : }
    1349             : 
    1350             : NS_IMETHODIMP
    1351           0 : nsRange::SetEnd(nsIDOMNode* aContainer, int32_t aOffset)
    1352             : {
    1353           0 :   nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
    1354           0 :   if (!container) {
    1355           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
    1356             :   }
    1357             : 
    1358           0 :   ErrorResult rv;
    1359           0 :   SetEnd(*container, aOffset, rv);
    1360           0 :   return rv.StealNSResult();
    1361             : }
    1362             : 
    1363             : /* virtual */ nsresult
    1364           2 : nsRange::SetEnd(nsINode* aContainer, int32_t aOffset)
    1365             : {
    1366           2 :   nsINode* newRoot = IsValidBoundary(aContainer);
    1367           2 :   if (!newRoot) {
    1368           0 :     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
    1369             :   }
    1370             : 
    1371           2 :   if (!IsValidOffset(aContainer, aOffset)) {
    1372           0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1373             :   }
    1374             : 
    1375             :   // Collapse if not positioned yet, if positioned in another doc or
    1376             :   // if the new end is before start.
    1377           4 :   if (!mIsPositioned || newRoot != mRoot ||
    1378           2 :       nsContentUtils::ComparePoints(mStartContainer, mStartOffset,
    1379             :                                     aContainer, aOffset) == 1) {
    1380           0 :     DoSetRange(aContainer, aOffset, aContainer, aOffset, newRoot);
    1381             : 
    1382           0 :     return NS_OK;
    1383             :   }
    1384             : 
    1385           2 :   DoSetRange(mStartContainer, mStartOffset, aContainer, aOffset, mRoot);
    1386             : 
    1387           2 :   return NS_OK;
    1388             : }
    1389             : 
    1390             : nsresult
    1391          15 : nsRange::SetStartAndEnd(nsINode* aStartContainer, int32_t aStartOffset,
    1392             :                         nsINode* aEndContainer, int32_t aEndOffset)
    1393             : {
    1394          15 :   if (NS_WARN_IF(!aStartContainer) || NS_WARN_IF(!aEndContainer)) {
    1395           0 :     return NS_ERROR_INVALID_ARG;
    1396             :   }
    1397             : 
    1398          15 :   nsINode* newStartRoot = IsValidBoundary(aStartContainer);
    1399          15 :   if (!newStartRoot) {
    1400           0 :     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
    1401             :   }
    1402          15 :   if (!IsValidOffset(aStartContainer, aStartOffset)) {
    1403           0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1404             :   }
    1405             : 
    1406          15 :   if (aStartContainer == aEndContainer) {
    1407          15 :     if (!IsValidOffset(aEndContainer, aEndOffset)) {
    1408           0 :       return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1409             :     }
    1410             :     // If the end offset is less than the start offset, this should be
    1411             :     // collapsed at the end offset.
    1412          15 :     if (aStartOffset > aEndOffset) {
    1413             :       DoSetRange(aEndContainer, aEndOffset,
    1414           0 :                  aEndContainer, aEndOffset, newStartRoot);
    1415             :     } else {
    1416             :       DoSetRange(aStartContainer, aStartOffset,
    1417          15 :                  aEndContainer, aEndOffset, newStartRoot);
    1418             :     }
    1419          15 :     return NS_OK;
    1420             :   }
    1421             : 
    1422           0 :   nsINode* newEndRoot = IsValidBoundary(aEndContainer);
    1423           0 :   if (!newEndRoot) {
    1424           0 :     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
    1425             :   }
    1426           0 :   if (!IsValidOffset(aEndContainer, aEndOffset)) {
    1427           0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1428             :   }
    1429             : 
    1430             :   // If they have different root, this should be collapsed at the end point.
    1431           0 :   if (newStartRoot != newEndRoot) {
    1432             :     DoSetRange(aEndContainer, aEndOffset,
    1433           0 :                aEndContainer, aEndOffset, newEndRoot);
    1434           0 :     return NS_OK;
    1435             :   }
    1436             : 
    1437             :   // If the end point is before the start point, this should be collapsed at
    1438             :   // the end point.
    1439           0 :   if (nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
    1440             :                                     aEndContainer, aEndOffset) == 1) {
    1441             :     DoSetRange(aEndContainer, aEndOffset,
    1442           0 :                aEndContainer, aEndOffset, newEndRoot);
    1443           0 :     return NS_OK;
    1444             :   }
    1445             : 
    1446             :   // Otherwise, set the range as specified.
    1447             :   DoSetRange(aStartContainer, aStartOffset,
    1448           0 :              aEndContainer, aEndOffset, newStartRoot);
    1449           0 :   return NS_OK;
    1450             : }
    1451             : 
    1452             : void
    1453           0 : nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr)
    1454             : {
    1455           0 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1456           0 :   mCalledByJS = true;
    1457           0 :   SetEndBefore(aNode, aErr);
    1458           0 : }
    1459             : 
    1460             : void
    1461           0 : nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
    1462             : {
    1463           0 :   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    1464           0 :       !nsContentUtils::CanCallerAccess(&aNode)) {
    1465           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1466           0 :     return;
    1467             :   }
    1468             : 
    1469           0 :   AutoInvalidateSelection atEndOfBlock(this);
    1470           0 :   int32_t offset = -1;
    1471           0 :   nsINode* container = GetContainerAndOffsetBefore(&aNode, &offset);
    1472           0 :   aRv = SetEnd(container, offset);
    1473             : }
    1474             : 
    1475             : NS_IMETHODIMP
    1476           0 : nsRange::SetEndBefore(nsIDOMNode* aSibling)
    1477             : {
    1478           0 :   nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
    1479           0 :   if (!sibling) {
    1480           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
    1481             :   }
    1482             : 
    1483           0 :   ErrorResult rv;
    1484           0 :   SetEndBefore(*sibling, rv);
    1485           0 :   return rv.StealNSResult();
    1486             : }
    1487             : 
    1488             : void
    1489           0 : nsRange::SetEndAfterJS(nsINode& aNode, ErrorResult& aErr)
    1490             : {
    1491           0 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1492           0 :   mCalledByJS = true;
    1493           0 :   SetEndAfter(aNode, aErr);
    1494           0 : }
    1495             : 
    1496             : void
    1497           0 : nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv)
    1498             : {
    1499           0 :   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    1500           0 :       !nsContentUtils::CanCallerAccess(&aNode)) {
    1501           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1502           0 :     return;
    1503             :   }
    1504             : 
    1505           0 :   AutoInvalidateSelection atEndOfBlock(this);
    1506           0 :   int32_t offset = -1;
    1507           0 :   nsINode* container = GetContainerAndOffsetAfter(&aNode, &offset);
    1508           0 :   aRv = SetEnd(container, offset);
    1509             : }
    1510             : 
    1511             : NS_IMETHODIMP
    1512           0 : nsRange::SetEndAfter(nsIDOMNode* aSibling)
    1513             : {
    1514           0 :   nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
    1515           0 :   if (!sibling) {
    1516           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
    1517             :   }
    1518             : 
    1519           0 :   ErrorResult rv;
    1520           0 :   SetEndAfter(*sibling, rv);
    1521           0 :   return rv.StealNSResult();
    1522             : }
    1523             : 
    1524             : NS_IMETHODIMP
    1525           0 : nsRange::Collapse(bool aToStart)
    1526             : {
    1527           0 :   if (!mIsPositioned)
    1528           0 :     return NS_ERROR_NOT_INITIALIZED;
    1529             : 
    1530           0 :   AutoInvalidateSelection atEndOfBlock(this);
    1531           0 :   if (aToStart) {
    1532           0 :     DoSetRange(mStartContainer, mStartOffset,
    1533           0 :                mStartContainer, mStartOffset, mRoot);
    1534             :   } else {
    1535           0 :     DoSetRange(mEndContainer, mEndOffset,
    1536           0 :                mEndContainer, mEndOffset, mRoot);
    1537             :   }
    1538             : 
    1539           0 :   return NS_OK;
    1540             : }
    1541             : 
    1542             : void
    1543           0 : nsRange::CollapseJS(bool aToStart)
    1544             : {
    1545           0 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1546           0 :   mCalledByJS = true;
    1547           0 :   Unused << Collapse(aToStart);
    1548           0 : }
    1549             : 
    1550             : NS_IMETHODIMP
    1551           0 : nsRange::SelectNode(nsIDOMNode* aN)
    1552             : {
    1553           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aN);
    1554           0 :   NS_ENSURE_TRUE(node, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
    1555             : 
    1556           0 :   ErrorResult rv;
    1557           0 :   SelectNode(*node, rv);
    1558           0 :   return rv.StealNSResult();
    1559             : }
    1560             : 
    1561             : void
    1562           0 : nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr)
    1563             : {
    1564           0 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1565           0 :   mCalledByJS = true;
    1566           0 :   SelectNode(aNode, aErr);
    1567           0 : }
    1568             : 
    1569             : void
    1570           0 : nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv)
    1571             : {
    1572           0 :   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    1573           0 :       !nsContentUtils::CanCallerAccess(&aNode)) {
    1574           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1575           0 :     return;
    1576             :   }
    1577             : 
    1578           0 :   nsINode* container = aNode.GetParentNode();
    1579           0 :   nsINode* newRoot = IsValidBoundary(container);
    1580           0 :   if (!newRoot) {
    1581           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
    1582           0 :     return;
    1583             :   }
    1584             : 
    1585           0 :   int32_t index = container->IndexOf(&aNode);
    1586           0 :   if (index < 0) {
    1587           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
    1588           0 :     return;
    1589             :   }
    1590             : 
    1591           0 :   AutoInvalidateSelection atEndOfBlock(this);
    1592           0 :   DoSetRange(container, index, container, index + 1, newRoot);
    1593             : }
    1594             : 
    1595             : NS_IMETHODIMP
    1596           2 : nsRange::SelectNodeContents(nsIDOMNode* aN)
    1597             : {
    1598           4 :   nsCOMPtr<nsINode> node = do_QueryInterface(aN);
    1599           2 :   NS_ENSURE_TRUE(node, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
    1600             : 
    1601           4 :   ErrorResult rv;
    1602           2 :   SelectNodeContents(*node, rv);
    1603           2 :   return rv.StealNSResult();
    1604             : }
    1605             : 
    1606             : void
    1607           0 : nsRange::SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr)
    1608             : {
    1609           0 :   AutoCalledByJSRestore calledByJSRestorer(*this);
    1610           0 :   mCalledByJS = true;
    1611           0 :   SelectNodeContents(aNode, aErr);
    1612           0 : }
    1613             : 
    1614             : void
    1615           2 : nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv)
    1616             : {
    1617           2 :   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    1618           0 :       !nsContentUtils::CanCallerAccess(&aNode)) {
    1619           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1620           0 :     return;
    1621             :   }
    1622             : 
    1623           2 :   nsINode* newRoot = IsValidBoundary(&aNode);
    1624           2 :   if (!newRoot) {
    1625           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
    1626           0 :     return;
    1627             :   }
    1628             : 
    1629           4 :   AutoInvalidateSelection atEndOfBlock(this);
    1630           2 :   DoSetRange(&aNode, 0, &aNode, aNode.Length(), newRoot);
    1631             : }
    1632             : 
    1633             : // The Subtree Content Iterator only returns subtrees that are
    1634             : // completely within a given range. It doesn't return a CharacterData
    1635             : // node that contains either the start or end point of the range.,
    1636             : // nor does it return element nodes when nothing in the element is selected.
    1637             : // We need an iterator that will also include these start/end points
    1638             : // so that our methods/algorithms aren't cluttered with special
    1639             : // case code that tries to include these points while iterating.
    1640             : //
    1641             : // The RangeSubtreeIterator class mimics the nsIContentIterator
    1642             : // methods we need, so should the Content Iterator support the
    1643             : // start/end points in the future, we can switchover relatively
    1644             : // easy.
    1645             : 
    1646             : class MOZ_STACK_CLASS RangeSubtreeIterator
    1647             : {
    1648             : private:
    1649             : 
    1650             :   enum RangeSubtreeIterState { eDone=0,
    1651             :                                eUseStart,
    1652             :                                eUseIterator,
    1653             :                                eUseEnd };
    1654             : 
    1655             :   nsCOMPtr<nsIContentIterator>  mIter;
    1656             :   RangeSubtreeIterState         mIterState;
    1657             : 
    1658             :   nsCOMPtr<nsINode> mStart;
    1659             :   nsCOMPtr<nsINode> mEnd;
    1660             : 
    1661             : public:
    1662             : 
    1663           0 :   RangeSubtreeIterator()
    1664           0 :     : mIterState(eDone)
    1665             :   {
    1666           0 :   }
    1667           0 :   ~RangeSubtreeIterator()
    1668           0 :   {
    1669           0 :   }
    1670             : 
    1671             :   nsresult Init(nsRange *aRange);
    1672             :   already_AddRefed<nsINode> GetCurrentNode();
    1673             :   void First();
    1674             :   void Last();
    1675             :   void Next();
    1676             :   void Prev();
    1677             : 
    1678           0 :   bool IsDone()
    1679             :   {
    1680           0 :     return mIterState == eDone;
    1681             :   }
    1682             : };
    1683             : 
    1684             : nsresult
    1685           0 : RangeSubtreeIterator::Init(nsRange *aRange)
    1686             : {
    1687           0 :   mIterState = eDone;
    1688           0 :   if (aRange->Collapsed()) {
    1689           0 :     return NS_OK;
    1690             :   }
    1691             : 
    1692             :   // Grab the start point of the range and QI it to
    1693             :   // a CharacterData pointer. If it is CharacterData store
    1694             :   // a pointer to the node.
    1695             : 
    1696           0 :   ErrorResult rv;
    1697           0 :   nsCOMPtr<nsINode> node = aRange->GetStartContainer(rv);
    1698           0 :   if (!node) return NS_ERROR_FAILURE;
    1699             : 
    1700           0 :   nsCOMPtr<nsIDOMCharacterData> startData = do_QueryInterface(node);
    1701           0 :   if (startData || (node->IsElement() &&
    1702           0 :                     node->AsElement()->GetChildCount() == aRange->GetStartOffset(rv))) {
    1703           0 :     mStart = node;
    1704             :   }
    1705             : 
    1706             :   // Grab the end point of the range and QI it to
    1707             :   // a CharacterData pointer. If it is CharacterData store
    1708             :   // a pointer to the node.
    1709             : 
    1710           0 :   node = aRange->GetEndContainer(rv);
    1711           0 :   if (!node) return NS_ERROR_FAILURE;
    1712             : 
    1713           0 :   nsCOMPtr<nsIDOMCharacterData> endData = do_QueryInterface(node);
    1714           0 :   if (endData || (node->IsElement() && aRange->GetEndOffset(rv) == 0)) {
    1715           0 :     mEnd = node;
    1716             :   }
    1717             : 
    1718           0 :   if (mStart && mStart == mEnd)
    1719             :   {
    1720             :     // The range starts and stops in the same CharacterData
    1721             :     // node. Null out the end pointer so we only visit the
    1722             :     // node once!
    1723             : 
    1724           0 :     mEnd = nullptr;
    1725             :   }
    1726             :   else
    1727             :   {
    1728             :     // Now create a Content Subtree Iterator to be used
    1729             :     // for the subtrees between the end points!
    1730             : 
    1731           0 :     mIter = NS_NewContentSubtreeIterator();
    1732             : 
    1733           0 :     nsresult res = mIter->Init(aRange);
    1734           0 :     if (NS_FAILED(res)) return res;
    1735             : 
    1736           0 :     if (mIter->IsDone())
    1737             :     {
    1738             :       // The subtree iterator thinks there's nothing
    1739             :       // to iterate over, so just free it up so we
    1740             :       // don't accidentally call into it.
    1741             : 
    1742           0 :       mIter = nullptr;
    1743             :     }
    1744             :   }
    1745             : 
    1746             :   // Initialize the iterator by calling First().
    1747             :   // Note that we are ignoring the return value on purpose!
    1748             : 
    1749           0 :   First();
    1750             : 
    1751           0 :   return NS_OK;
    1752             : }
    1753             : 
    1754             : already_AddRefed<nsINode>
    1755           0 : RangeSubtreeIterator::GetCurrentNode()
    1756             : {
    1757           0 :   nsCOMPtr<nsINode> node;
    1758             : 
    1759           0 :   if (mIterState == eUseStart && mStart) {
    1760           0 :     node = mStart;
    1761           0 :   } else if (mIterState == eUseEnd && mEnd) {
    1762           0 :     node = mEnd;
    1763           0 :   } else if (mIterState == eUseIterator && mIter) {
    1764           0 :     node = mIter->GetCurrentNode();
    1765             :   }
    1766             : 
    1767           0 :   return node.forget();
    1768             : }
    1769             : 
    1770             : void
    1771           0 : RangeSubtreeIterator::First()
    1772             : {
    1773           0 :   if (mStart)
    1774           0 :     mIterState = eUseStart;
    1775           0 :   else if (mIter)
    1776             :   {
    1777           0 :     mIter->First();
    1778             : 
    1779           0 :     mIterState = eUseIterator;
    1780             :   }
    1781           0 :   else if (mEnd)
    1782           0 :     mIterState = eUseEnd;
    1783             :   else
    1784           0 :     mIterState = eDone;
    1785           0 : }
    1786             : 
    1787             : void
    1788           0 : RangeSubtreeIterator::Last()
    1789             : {
    1790           0 :   if (mEnd)
    1791           0 :     mIterState = eUseEnd;
    1792           0 :   else if (mIter)
    1793             :   {
    1794           0 :     mIter->Last();
    1795             : 
    1796           0 :     mIterState = eUseIterator;
    1797             :   }
    1798           0 :   else if (mStart)
    1799           0 :     mIterState = eUseStart;
    1800             :   else
    1801           0 :     mIterState = eDone;
    1802           0 : }
    1803             : 
    1804             : void
    1805           0 : RangeSubtreeIterator::Next()
    1806             : {
    1807           0 :   if (mIterState == eUseStart)
    1808             :   {
    1809           0 :     if (mIter)
    1810             :     {
    1811           0 :       mIter->First();
    1812             : 
    1813           0 :       mIterState = eUseIterator;
    1814             :     }
    1815           0 :     else if (mEnd)
    1816           0 :       mIterState = eUseEnd;
    1817             :     else
    1818           0 :       mIterState = eDone;
    1819             :   }
    1820           0 :   else if (mIterState == eUseIterator)
    1821             :   {
    1822           0 :     mIter->Next();
    1823             : 
    1824           0 :     if (mIter->IsDone())
    1825             :     {
    1826           0 :       if (mEnd)
    1827           0 :         mIterState = eUseEnd;
    1828             :       else
    1829           0 :         mIterState = eDone;
    1830             :     }
    1831             :   }
    1832             :   else
    1833           0 :     mIterState = eDone;
    1834           0 : }
    1835             : 
    1836             : void
    1837           0 : RangeSubtreeIterator::Prev()
    1838             : {
    1839           0 :   if (mIterState == eUseEnd)
    1840             :   {
    1841           0 :     if (mIter)
    1842             :     {
    1843           0 :       mIter->Last();
    1844             : 
    1845           0 :       mIterState = eUseIterator;
    1846             :     }
    1847           0 :     else if (mStart)
    1848           0 :       mIterState = eUseStart;
    1849             :     else
    1850           0 :       mIterState = eDone;
    1851             :   }
    1852           0 :   else if (mIterState == eUseIterator)
    1853             :   {
    1854           0 :     mIter->Prev();
    1855             : 
    1856           0 :     if (mIter->IsDone())
    1857             :     {
    1858           0 :       if (mStart)
    1859           0 :         mIterState = eUseStart;
    1860             :       else
    1861           0 :         mIterState = eDone;
    1862             :     }
    1863             :   }
    1864             :   else
    1865           0 :     mIterState = eDone;
    1866           0 : }
    1867             : 
    1868             : 
    1869             : // CollapseRangeAfterDelete() is a utility method that is used by
    1870             : // DeleteContents() and ExtractContents() to collapse the range
    1871             : // in the correct place, under the range's root container (the
    1872             : // range end points common container) as outlined by the Range spec:
    1873             : //
    1874             : // http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html
    1875             : // The assumption made by this method is that the delete or extract
    1876             : // has been done already, and left the range in a state where there is
    1877             : // no content between the 2 end points.
    1878             : 
    1879             : static nsresult
    1880           0 : CollapseRangeAfterDelete(nsRange* aRange)
    1881             : {
    1882           0 :   NS_ENSURE_ARG_POINTER(aRange);
    1883             : 
    1884             :   // Check if range gravity took care of collapsing the range for us!
    1885           0 :   if (aRange->Collapsed())
    1886             :   {
    1887             :     // aRange is collapsed so there's nothing for us to do.
    1888             :     //
    1889             :     // There are 2 possible scenarios here:
    1890             :     //
    1891             :     // 1. aRange could've been collapsed prior to the delete/extract,
    1892             :     //    which would've resulted in nothing being removed, so aRange
    1893             :     //    is already where it should be.
    1894             :     //
    1895             :     // 2. Prior to the delete/extract, aRange's start and end were in
    1896             :     //    the same container which would mean everything between them
    1897             :     //    was removed, causing range gravity to collapse the range.
    1898             : 
    1899           0 :     return NS_OK;
    1900             :   }
    1901             : 
    1902             :   // aRange isn't collapsed so figure out the appropriate place to collapse!
    1903             :   // First get both end points and their common ancestor.
    1904             : 
    1905           0 :   ErrorResult rv;
    1906           0 :   nsCOMPtr<nsINode> commonAncestor = aRange->GetCommonAncestorContainer(rv);
    1907           0 :   if (rv.Failed()) return rv.StealNSResult();
    1908             : 
    1909           0 :   nsCOMPtr<nsINode> startContainer = aRange->GetStartContainer(rv);
    1910           0 :   if (rv.Failed()) return rv.StealNSResult();
    1911           0 :   nsCOMPtr<nsINode> endContainer = aRange->GetEndContainer(rv);
    1912           0 :   if (rv.Failed()) return rv.StealNSResult();
    1913             : 
    1914             :   // Collapse to one of the end points if they are already in the
    1915             :   // commonAncestor. This should work ok since this method is called
    1916             :   // immediately after a delete or extract that leaves no content
    1917             :   // between the 2 end points!
    1918             : 
    1919           0 :   if (startContainer == commonAncestor)
    1920           0 :     return aRange->Collapse(true);
    1921           0 :   if (endContainer == commonAncestor)
    1922           0 :     return aRange->Collapse(false);
    1923             : 
    1924             :   // End points are at differing levels. We want to collapse to the
    1925             :   // point that is between the 2 subtrees that contain each point,
    1926             :   // under the common ancestor.
    1927             : 
    1928           0 :   nsCOMPtr<nsINode> nodeToSelect(startContainer);
    1929             : 
    1930           0 :   while (nodeToSelect)
    1931             :   {
    1932           0 :     nsCOMPtr<nsINode> parent = nodeToSelect->GetParentNode();
    1933           0 :     if (parent == commonAncestor)
    1934           0 :       break; // We found the nodeToSelect!
    1935             : 
    1936           0 :     nodeToSelect = parent;
    1937             :   }
    1938             : 
    1939           0 :   if (!nodeToSelect)
    1940           0 :     return NS_ERROR_FAILURE; // This should never happen!
    1941             : 
    1942           0 :   aRange->SelectNode(*nodeToSelect, rv);
    1943           0 :   if (rv.Failed()) return rv.StealNSResult();
    1944             : 
    1945           0 :   return aRange->Collapse(false);
    1946             : }
    1947             : 
    1948             : /**
    1949             :  * Split a data node into two parts.
    1950             :  *
    1951             :  * @param aStartContainer     The original node we are trying to split.
    1952             :  * @param aStartOffset        The offset at which to split.
    1953             :  * @param aEndContainer       The second node.
    1954             :  * @param aCloneAfterOriginal Set false if the original node should be the
    1955             :  *                            latter one after split.
    1956             :  */
    1957           0 : static nsresult SplitDataNode(nsIDOMCharacterData* aStartContainer,
    1958             :                               uint32_t aStartOffset,
    1959             :                               nsIDOMCharacterData** aEndContainer,
    1960             :                               bool aCloneAfterOriginal = true)
    1961             : {
    1962             :   nsresult rv;
    1963           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aStartContainer);
    1964           0 :   NS_ENSURE_STATE(node && node->IsNodeOfType(nsINode::eDATA_NODE));
    1965           0 :   nsGenericDOMDataNode* dataNode = static_cast<nsGenericDOMDataNode*>(node.get());
    1966             : 
    1967           0 :   nsCOMPtr<nsIContent> newData;
    1968           0 :   rv = dataNode->SplitData(aStartOffset, getter_AddRefs(newData),
    1969           0 :                            aCloneAfterOriginal);
    1970           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1971           0 :   return CallQueryInterface(newData, aEndContainer);
    1972             : }
    1973             : 
    1974             : NS_IMETHODIMP
    1975           0 : PrependChild(nsINode* aContainer, nsINode* aChild)
    1976             : {
    1977           0 :   nsCOMPtr<nsINode> first = aContainer->GetFirstChild();
    1978           0 :   ErrorResult rv;
    1979           0 :   aContainer->InsertBefore(*aChild, first, rv);
    1980           0 :   return rv.StealNSResult();
    1981             : }
    1982             : 
    1983             : // Helper function for CutContents, making sure that the current node wasn't
    1984             : // removed by mutation events (bug 766426)
    1985             : static bool
    1986           0 : ValidateCurrentNode(nsRange* aRange, RangeSubtreeIterator& aIter)
    1987             : {
    1988             :   bool before, after;
    1989           0 :   nsCOMPtr<nsINode> node = aIter.GetCurrentNode();
    1990           0 :   if (!node) {
    1991             :     // We don't have to worry that the node was removed if it doesn't exist,
    1992             :     // e.g., the iterator is done.
    1993           0 :     return true;
    1994             :   }
    1995             : 
    1996           0 :   nsresult res = nsRange::CompareNodeToRange(node, aRange, &before, &after);
    1997           0 :   NS_ENSURE_SUCCESS(res, false);
    1998             : 
    1999           0 :   if (before || after) {
    2000           0 :     nsCOMPtr<nsIDOMCharacterData> charData = do_QueryInterface(node);
    2001           0 :     if (charData) {
    2002             :       // If we're dealing with the start/end container which is a character
    2003             :       // node, pretend that the node is in the range.
    2004           0 :       if (before && node == aRange->GetStartContainer()) {
    2005           0 :         before = false;
    2006             :       }
    2007           0 :       if (after && node == aRange->GetEndContainer()) {
    2008           0 :         after = false;
    2009             :       }
    2010             :     }
    2011             :   }
    2012             : 
    2013           0 :   return !before && !after;
    2014             : }
    2015             : 
    2016             : nsresult
    2017           0 : nsRange::CutContents(DocumentFragment** aFragment)
    2018             : {
    2019           0 :   if (aFragment) {
    2020           0 :     *aFragment = nullptr;
    2021             :   }
    2022             : 
    2023           0 :   nsCOMPtr<nsIDocument> doc = mStartContainer->OwnerDoc();
    2024             : 
    2025           0 :   ErrorResult res;
    2026           0 :   nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(res);
    2027           0 :   NS_ENSURE_TRUE(!res.Failed(), res.StealNSResult());
    2028             : 
    2029             :   // If aFragment isn't null, create a temporary fragment to hold our return.
    2030           0 :   RefPtr<DocumentFragment> retval;
    2031           0 :   if (aFragment) {
    2032           0 :     retval = new DocumentFragment(doc->NodeInfoManager());
    2033             :   }
    2034           0 :   nsCOMPtr<nsINode> commonCloneAncestor = retval.get();
    2035             : 
    2036             :   // Batch possible DOMSubtreeModified events.
    2037           0 :   mozAutoSubtreeModified subtree(mRoot ? mRoot->OwnerDoc(): nullptr, nullptr);
    2038             : 
    2039             :   // Save the range end points locally to avoid interference
    2040             :   // of Range gravity during our edits!
    2041             : 
    2042           0 :   nsCOMPtr<nsINode> startContainer = mStartContainer;
    2043           0 :   int32_t              startOffset = mStartOffset;
    2044           0 :   nsCOMPtr<nsINode> endContainer = mEndContainer;
    2045           0 :   int32_t              endOffset = mEndOffset;
    2046             : 
    2047           0 :   if (retval) {
    2048             :     // For extractContents(), abort early if there's a doctype (bug 719533).
    2049             :     // This can happen only if the common ancestor is a document, in which case
    2050             :     // we just need to find its doctype child and check if that's in the range.
    2051           0 :     nsCOMPtr<nsIDocument> commonAncestorDocument = do_QueryInterface(commonAncestor);
    2052           0 :     if (commonAncestorDocument) {
    2053           0 :       RefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype();
    2054             : 
    2055           0 :       if (doctype &&
    2056           0 :           nsContentUtils::ComparePoints(startContainer, startOffset,
    2057           0 :                                         doctype, 0) < 0 &&
    2058           0 :           nsContentUtils::ComparePoints(doctype, 0,
    2059           0 :                                         endContainer, endOffset) < 0) {
    2060           0 :         return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
    2061             :       }
    2062             :     }
    2063             :   }
    2064             : 
    2065             :   // Create and initialize a subtree iterator that will give
    2066             :   // us all the subtrees within the range.
    2067             : 
    2068           0 :   RangeSubtreeIterator iter;
    2069             : 
    2070           0 :   nsresult rv = iter.Init(this);
    2071           0 :   if (NS_FAILED(rv)) return rv;
    2072             : 
    2073           0 :   if (iter.IsDone())
    2074             :   {
    2075             :     // There's nothing for us to delete.
    2076           0 :     rv = CollapseRangeAfterDelete(this);
    2077           0 :     if (NS_SUCCEEDED(rv) && aFragment) {
    2078           0 :       retval.forget(aFragment);
    2079             :     }
    2080           0 :     return rv;
    2081             :   }
    2082             : 
    2083             :   // We delete backwards to avoid iterator problems!
    2084             : 
    2085           0 :   iter.Last();
    2086             : 
    2087           0 :   bool handled = false;
    2088             : 
    2089             :   // With the exception of text nodes that contain one of the range
    2090             :   // end points, the subtree iterator should only give us back subtrees
    2091             :   // that are completely contained between the range's end points.
    2092             : 
    2093           0 :   while (!iter.IsDone())
    2094             :   {
    2095           0 :     nsCOMPtr<nsINode> nodeToResult;
    2096           0 :     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
    2097             : 
    2098             :     // Before we delete anything, advance the iterator to the
    2099             :     // next subtree.
    2100             : 
    2101           0 :     iter.Prev();
    2102             : 
    2103           0 :     handled = false;
    2104             : 
    2105             :     // If it's CharacterData, make sure we might need to delete
    2106             :     // part of the data, instead of removing the whole node.
    2107             :     //
    2108             :     // XXX_kin: We need to also handle ProcessingInstruction
    2109             :     // XXX_kin: according to the spec.
    2110             : 
    2111           0 :     nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(node));
    2112             : 
    2113           0 :     if (charData)
    2114             :     {
    2115           0 :       uint32_t dataLength = 0;
    2116             : 
    2117           0 :       if (node == startContainer)
    2118             :       {
    2119           0 :         if (node == endContainer)
    2120             :         {
    2121             :           // This range is completely contained within a single text node.
    2122             :           // Delete or extract the data between startOffset and endOffset.
    2123             : 
    2124           0 :           if (endOffset > startOffset)
    2125             :           {
    2126           0 :             if (retval) {
    2127           0 :               nsAutoString cutValue;
    2128           0 :               rv = charData->SubstringData(startOffset, endOffset - startOffset,
    2129           0 :                                            cutValue);
    2130           0 :               NS_ENSURE_SUCCESS(rv, rv);
    2131           0 :               nsCOMPtr<nsIDOMNode> clone;
    2132           0 :               rv = charData->CloneNode(false, 1, getter_AddRefs(clone));
    2133           0 :               NS_ENSURE_SUCCESS(rv, rv);
    2134           0 :               clone->SetNodeValue(cutValue);
    2135           0 :               nodeToResult = do_QueryInterface(clone);
    2136             :             }
    2137             : 
    2138           0 :             nsMutationGuard guard;
    2139           0 :             rv = charData->DeleteData(startOffset, endOffset - startOffset);
    2140           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2141           0 :             NS_ENSURE_STATE(!guard.Mutated(0) ||
    2142             :                             ValidateCurrentNode(this, iter));
    2143             :           }
    2144             : 
    2145           0 :           handled = true;
    2146             :         }
    2147             :         else
    2148             :         {
    2149             :           // Delete or extract everything after startOffset.
    2150             : 
    2151           0 :           rv = charData->GetLength(&dataLength);
    2152           0 :           NS_ENSURE_SUCCESS(rv, rv);
    2153             : 
    2154           0 :           if (dataLength >= (uint32_t)startOffset)
    2155             :           {
    2156           0 :             nsMutationGuard guard;
    2157           0 :             nsCOMPtr<nsIDOMCharacterData> cutNode;
    2158           0 :             rv = SplitDataNode(charData, startOffset, getter_AddRefs(cutNode));
    2159           0 :             NS_ENSURE_SUCCESS(rv, rv);
    2160           0 :             NS_ENSURE_STATE(!guard.Mutated(1) ||
    2161             :                             ValidateCurrentNode(this, iter));
    2162           0 :             nodeToResult = do_QueryInterface(cutNode);
    2163             :           }
    2164             : 
    2165           0 :           handled = true;
    2166             :         }
    2167             :       }
    2168           0 :       else if (node == endContainer)
    2169             :       {
    2170             :         // Delete or extract everything before endOffset.
    2171             : 
    2172           0 :         if (endOffset >= 0)
    2173             :         {
    2174           0 :           nsMutationGuard guard;
    2175           0 :           nsCOMPtr<nsIDOMCharacterData> cutNode;
    2176             :           /* The Range spec clearly states clones get cut and original nodes
    2177             :              remain behind, so use false as the last parameter.
    2178             :           */
    2179           0 :           rv = SplitDataNode(charData, endOffset, getter_AddRefs(cutNode),
    2180           0 :                              false);
    2181           0 :           NS_ENSURE_SUCCESS(rv, rv);
    2182           0 :           NS_ENSURE_STATE(!guard.Mutated(1) ||
    2183             :                           ValidateCurrentNode(this, iter));
    2184           0 :           nodeToResult = do_QueryInterface(cutNode);
    2185             :         }
    2186             : 
    2187           0 :         handled = true;
    2188             :       }
    2189             :     }
    2190             : 
    2191           0 :     if (!handled && (node == endContainer || node == startContainer))
    2192             :     {
    2193           0 :       if (node && node->IsElement() &&
    2194           0 :           ((node == endContainer && endOffset == 0) ||
    2195           0 :            (node == startContainer &&
    2196           0 :             int32_t(node->AsElement()->GetChildCount()) == startOffset)))
    2197             :       {
    2198           0 :         if (retval) {
    2199           0 :           ErrorResult rv;
    2200           0 :           nodeToResult = node->CloneNode(false, rv);
    2201           0 :           NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
    2202             :         }
    2203           0 :         handled = true;
    2204             :       }
    2205             :     }
    2206             : 
    2207           0 :     if (!handled)
    2208             :     {
    2209             :       // node was not handled above, so it must be completely contained
    2210             :       // within the range. Just remove it from the tree!
    2211           0 :       nodeToResult = node;
    2212             :     }
    2213             : 
    2214           0 :     uint32_t parentCount = 0;
    2215             :     // Set the result to document fragment if we have 'retval'.
    2216           0 :     if (retval) {
    2217           0 :       nsCOMPtr<nsINode> oldCommonAncestor = commonAncestor;
    2218           0 :       if (!iter.IsDone()) {
    2219             :         // Setup the parameters for the next iteration of the loop.
    2220           0 :         nsCOMPtr<nsINode> prevNode = iter.GetCurrentNode();
    2221           0 :         NS_ENSURE_STATE(prevNode);
    2222             : 
    2223             :         // Get node's and prevNode's common parent. Do this before moving
    2224             :         // nodes from original DOM to result fragment.
    2225           0 :         commonAncestor = nsContentUtils::GetCommonAncestor(node, prevNode);
    2226           0 :         NS_ENSURE_STATE(commonAncestor);
    2227             : 
    2228           0 :         nsCOMPtr<nsINode> parentCounterNode = node;
    2229           0 :         while (parentCounterNode && parentCounterNode != commonAncestor)
    2230             :         {
    2231           0 :           ++parentCount;
    2232           0 :           parentCounterNode = parentCounterNode->GetParentNode();
    2233           0 :           NS_ENSURE_STATE(parentCounterNode);
    2234             :         }
    2235             :       }
    2236             : 
    2237             :       // Clone the parent hierarchy between commonAncestor and node.
    2238           0 :       nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
    2239           0 :       rv = CloneParentsBetween(oldCommonAncestor, node,
    2240           0 :                                getter_AddRefs(closestAncestor),
    2241           0 :                                getter_AddRefs(farthestAncestor));
    2242           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2243             : 
    2244           0 :       if (farthestAncestor)
    2245             :       {
    2246           0 :         nsCOMPtr<nsINode> n = do_QueryInterface(commonCloneAncestor);
    2247           0 :         rv = PrependChild(n, farthestAncestor);
    2248           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2249             :       }
    2250             : 
    2251           0 :       nsMutationGuard guard;
    2252           0 :       nsCOMPtr<nsINode> parent = nodeToResult->GetParentNode();
    2253           0 :       rv = closestAncestor ? PrependChild(closestAncestor, nodeToResult)
    2254           0 :                            : PrependChild(commonCloneAncestor, nodeToResult);
    2255           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2256           0 :       NS_ENSURE_STATE(!guard.Mutated(parent ? 2 : 1) ||
    2257             :                       ValidateCurrentNode(this, iter));
    2258           0 :     } else if (nodeToResult) {
    2259           0 :       nsMutationGuard guard;
    2260           0 :       nsCOMPtr<nsINode> node = nodeToResult;
    2261           0 :       nsCOMPtr<nsINode> parent = node->GetParentNode();
    2262           0 :       if (parent) {
    2263           0 :         mozilla::ErrorResult error;
    2264           0 :         parent->RemoveChild(*node, error);
    2265           0 :         NS_ENSURE_FALSE(error.Failed(), error.StealNSResult());
    2266             :       }
    2267           0 :       NS_ENSURE_STATE(!guard.Mutated(1) ||
    2268             :                       ValidateCurrentNode(this, iter));
    2269             :     }
    2270             : 
    2271           0 :     if (!iter.IsDone() && retval) {
    2272             :       // Find the equivalent of commonAncestor in the cloned tree.
    2273           0 :       nsCOMPtr<nsINode> newCloneAncestor = nodeToResult;
    2274           0 :       for (uint32_t i = parentCount; i; --i)
    2275             :       {
    2276           0 :         newCloneAncestor = newCloneAncestor->GetParentNode();
    2277           0 :         NS_ENSURE_STATE(newCloneAncestor);
    2278             :       }
    2279           0 :       commonCloneAncestor = newCloneAncestor;
    2280             :     }
    2281             :   }
    2282             : 
    2283           0 :   rv = CollapseRangeAfterDelete(this);
    2284           0 :   if (NS_SUCCEEDED(rv) && aFragment) {
    2285           0 :     retval.forget(aFragment);
    2286             :   }
    2287           0 :   return rv;
    2288             : }
    2289             : 
    2290             : NS_IMETHODIMP
    2291           0 : nsRange::DeleteContents()
    2292             : {
    2293           0 :   return CutContents(nullptr);
    2294             : }
    2295             : 
    2296             : void
    2297           0 : nsRange::DeleteContents(ErrorResult& aRv)
    2298             : {
    2299           0 :   aRv = CutContents(nullptr);
    2300           0 : }
    2301             : 
    2302             : NS_IMETHODIMP
    2303           0 : nsRange::ExtractContents(nsIDOMDocumentFragment** aReturn)
    2304             : {
    2305           0 :   NS_ENSURE_ARG_POINTER(aReturn);
    2306           0 :   RefPtr<DocumentFragment> fragment;
    2307           0 :   nsresult rv = CutContents(getter_AddRefs(fragment));
    2308           0 :   fragment.forget(aReturn);
    2309           0 :   return rv;
    2310             : }
    2311             : 
    2312             : already_AddRefed<DocumentFragment>
    2313           0 : nsRange::ExtractContents(ErrorResult& rv)
    2314             : {
    2315           0 :   RefPtr<DocumentFragment> fragment;
    2316           0 :   rv = CutContents(getter_AddRefs(fragment));
    2317           0 :   return fragment.forget();
    2318             : }
    2319             : 
    2320             : NS_IMETHODIMP
    2321           0 : nsRange::CompareBoundaryPoints(uint16_t aHow, nsIDOMRange* aOtherRange,
    2322             :                                int16_t* aCmpRet)
    2323             : {
    2324           0 :   nsRange* otherRange = static_cast<nsRange*>(aOtherRange);
    2325           0 :   NS_ENSURE_TRUE(otherRange, NS_ERROR_NULL_POINTER);
    2326             : 
    2327           0 :   ErrorResult rv;
    2328           0 :   *aCmpRet = CompareBoundaryPoints(aHow, *otherRange, rv);
    2329           0 :   return rv.StealNSResult();
    2330             : }
    2331             : 
    2332             : int16_t
    2333           0 : nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange,
    2334             :                                ErrorResult& rv)
    2335             : {
    2336           0 :   if (!mIsPositioned || !aOtherRange.IsPositioned()) {
    2337           0 :     rv.Throw(NS_ERROR_NOT_INITIALIZED);
    2338           0 :     return 0;
    2339             :   }
    2340             : 
    2341             :   nsINode *ourNode, *otherNode;
    2342             :   int32_t ourOffset, otherOffset;
    2343             : 
    2344           0 :   switch (aHow) {
    2345             :     case nsIDOMRange::START_TO_START:
    2346           0 :       ourNode = mStartContainer;
    2347           0 :       ourOffset = mStartOffset;
    2348           0 :       otherNode = aOtherRange.GetStartContainer();
    2349           0 :       otherOffset = aOtherRange.StartOffset();
    2350           0 :       break;
    2351             :     case nsIDOMRange::START_TO_END:
    2352           0 :       ourNode = mEndContainer;
    2353           0 :       ourOffset = mEndOffset;
    2354           0 :       otherNode = aOtherRange.GetStartContainer();
    2355           0 :       otherOffset = aOtherRange.StartOffset();
    2356           0 :       break;
    2357             :     case nsIDOMRange::END_TO_START:
    2358           0 :       ourNode = mStartContainer;
    2359           0 :       ourOffset = mStartOffset;
    2360           0 :       otherNode = aOtherRange.GetEndContainer();
    2361           0 :       otherOffset = aOtherRange.EndOffset();
    2362           0 :       break;
    2363             :     case nsIDOMRange::END_TO_END:
    2364           0 :       ourNode = mEndContainer;
    2365           0 :       ourOffset = mEndOffset;
    2366           0 :       otherNode = aOtherRange.GetEndContainer();
    2367           0 :       otherOffset = aOtherRange.EndOffset();
    2368           0 :       break;
    2369             :     default:
    2370             :       // We were passed an illegal value
    2371           0 :       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
    2372           0 :       return 0;
    2373             :   }
    2374             : 
    2375           0 :   if (mRoot != aOtherRange.GetRoot()) {
    2376           0 :     rv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
    2377           0 :     return 0;
    2378             :   }
    2379             : 
    2380           0 :   return nsContentUtils::ComparePoints(ourNode, ourOffset,
    2381           0 :                                        otherNode, otherOffset);
    2382             : }
    2383             : 
    2384             : /* static */ nsresult
    2385           0 : nsRange::CloneParentsBetween(nsINode *aAncestor,
    2386             :                              nsINode *aNode,
    2387             :                              nsINode **aClosestAncestor,
    2388             :                              nsINode **aFarthestAncestor)
    2389             : {
    2390           0 :   NS_ENSURE_ARG_POINTER((aAncestor && aNode && aClosestAncestor && aFarthestAncestor));
    2391             : 
    2392           0 :   *aClosestAncestor  = nullptr;
    2393           0 :   *aFarthestAncestor = nullptr;
    2394             : 
    2395           0 :   if (aAncestor == aNode)
    2396           0 :     return NS_OK;
    2397             : 
    2398           0 :   nsCOMPtr<nsINode> firstParent, lastParent;
    2399           0 :   nsCOMPtr<nsINode> parent = aNode->GetParentNode();
    2400             : 
    2401           0 :   while(parent && parent != aAncestor)
    2402             :   {
    2403           0 :     ErrorResult rv;
    2404           0 :     nsCOMPtr<nsINode> clone = parent->CloneNode(false, rv);
    2405             : 
    2406           0 :     if (rv.Failed()) {
    2407           0 :       return rv.StealNSResult();
    2408             :     }
    2409           0 :     if (!clone) {
    2410           0 :       return NS_ERROR_FAILURE;
    2411             :     }
    2412             : 
    2413           0 :     if (! firstParent) {
    2414           0 :       firstParent = lastParent = clone;
    2415             :     } else {
    2416           0 :       clone->AppendChild(*lastParent, rv);
    2417           0 :       if (rv.Failed()) return rv.StealNSResult();
    2418             : 
    2419           0 :       lastParent = clone;
    2420             :     }
    2421             : 
    2422           0 :     parent = parent->GetParentNode();
    2423             :   }
    2424             : 
    2425           0 :   *aClosestAncestor  = firstParent;
    2426           0 :   NS_IF_ADDREF(*aClosestAncestor);
    2427             : 
    2428           0 :   *aFarthestAncestor = lastParent;
    2429           0 :   NS_IF_ADDREF(*aFarthestAncestor);
    2430             : 
    2431           0 :   return NS_OK;
    2432             : }
    2433             : 
    2434             : NS_IMETHODIMP
    2435           0 : nsRange::CloneContents(nsIDOMDocumentFragment** aReturn)
    2436             : {
    2437           0 :   ErrorResult rv;
    2438           0 :   *aReturn = CloneContents(rv).take();
    2439           0 :   return rv.StealNSResult();
    2440             : }
    2441             : 
    2442             : already_AddRefed<DocumentFragment>
    2443           0 : nsRange::CloneContents(ErrorResult& aRv)
    2444             : {
    2445           0 :   nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv);
    2446           0 :   MOZ_ASSERT(!aRv.Failed(), "GetCommonAncestorContainer() shouldn't fail!");
    2447             : 
    2448           0 :   nsCOMPtr<nsIDocument> doc = mStartContainer->OwnerDoc();
    2449           0 :   NS_ASSERTION(doc, "CloneContents needs a document to continue.");
    2450           0 :   if (!doc) {
    2451           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2452           0 :     return nullptr;
    2453             :   }
    2454             : 
    2455             :   // Create a new document fragment in the context of this document,
    2456             :   // which might be null
    2457             : 
    2458             : 
    2459             :   RefPtr<DocumentFragment> clonedFrag =
    2460           0 :     new DocumentFragment(doc->NodeInfoManager());
    2461             : 
    2462           0 :   nsCOMPtr<nsINode> commonCloneAncestor = clonedFrag.get();
    2463             : 
    2464             :   // Create and initialize a subtree iterator that will give
    2465             :   // us all the subtrees within the range.
    2466             : 
    2467           0 :   RangeSubtreeIterator iter;
    2468             : 
    2469           0 :   aRv = iter.Init(this);
    2470           0 :   if (aRv.Failed()) {
    2471           0 :     return nullptr;
    2472             :   }
    2473             : 
    2474           0 :   if (iter.IsDone())
    2475             :   {
    2476             :     // There's nothing to add to the doc frag, we must be done!
    2477           0 :     return clonedFrag.forget();
    2478             :   }
    2479             : 
    2480           0 :   iter.First();
    2481             : 
    2482             :   // With the exception of text nodes that contain one of the range
    2483             :   // end points and elements which don't have any content selected the subtree
    2484             :   // iterator should only give us back subtrees that are completely contained
    2485             :   // between the range's end points.
    2486             :   //
    2487             :   // Unfortunately these subtrees don't contain the parent hierarchy/context
    2488             :   // that the Range spec requires us to return. This loop clones the
    2489             :   // parent hierarchy, adds a cloned version of the subtree, to it, then
    2490             :   // correctly places this new subtree into the doc fragment.
    2491             : 
    2492           0 :   while (!iter.IsDone())
    2493             :   {
    2494           0 :     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
    2495           0 :     bool deepClone = !node->IsElement() ||
    2496           0 :                        (!(node == mEndContainer && mEndOffset == 0) &&
    2497           0 :                         !(node == mStartContainer &&
    2498           0 :                           mStartOffset ==
    2499           0 :                             int32_t(node->AsElement()->GetChildCount())));
    2500             : 
    2501             :     // Clone the current subtree!
    2502             : 
    2503           0 :     nsCOMPtr<nsINode> clone = node->CloneNode(deepClone, aRv);
    2504           0 :     if (aRv.Failed()) {
    2505           0 :       return nullptr;
    2506             :     }
    2507             : 
    2508             :     // If it's CharacterData, make sure we only clone what
    2509             :     // is in the range.
    2510             :     //
    2511             :     // XXX_kin: We need to also handle ProcessingInstruction
    2512             :     // XXX_kin: according to the spec.
    2513             : 
    2514           0 :     nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(clone));
    2515             : 
    2516           0 :     if (charData)
    2517             :     {
    2518           0 :       if (node == mEndContainer) {
    2519             :         // We only need the data before mEndOffset, so get rid of any
    2520             :         // data after it.
    2521             : 
    2522           0 :         uint32_t dataLength = 0;
    2523           0 :         aRv = charData->GetLength(&dataLength);
    2524           0 :         if (aRv.Failed()) {
    2525           0 :           return nullptr;
    2526             :         }
    2527             : 
    2528           0 :         if (dataLength > (uint32_t)mEndOffset)
    2529             :         {
    2530           0 :           aRv = charData->DeleteData(mEndOffset, dataLength - mEndOffset);
    2531           0 :           if (aRv.Failed()) {
    2532           0 :             return nullptr;
    2533             :           }
    2534             :         }
    2535             :       }
    2536             : 
    2537           0 :       if (node == mStartContainer) {
    2538             :         // We don't need any data before mStartOffset, so just
    2539             :         // delete it!
    2540             : 
    2541           0 :         if (mStartOffset > 0)
    2542             :         {
    2543           0 :           aRv = charData->DeleteData(0, mStartOffset);
    2544           0 :           if (aRv.Failed()) {
    2545           0 :             return nullptr;
    2546             :           }
    2547             :         }
    2548             :       }
    2549             :     }
    2550             : 
    2551             :     // Clone the parent hierarchy between commonAncestor and node.
    2552             : 
    2553           0 :     nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
    2554             : 
    2555           0 :     aRv = CloneParentsBetween(commonAncestor, node,
    2556           0 :                               getter_AddRefs(closestAncestor),
    2557           0 :                               getter_AddRefs(farthestAncestor));
    2558             : 
    2559           0 :     if (aRv.Failed()) {
    2560           0 :       return nullptr;
    2561             :     }
    2562             : 
    2563             :     // Hook the parent hierarchy/context of the subtree into the clone tree.
    2564             : 
    2565           0 :     if (farthestAncestor)
    2566             :     {
    2567           0 :       commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
    2568             : 
    2569           0 :       if (aRv.Failed()) {
    2570           0 :         return nullptr;
    2571             :       }
    2572             :     }
    2573             : 
    2574             :     // Place the cloned subtree into the cloned doc frag tree!
    2575             : 
    2576           0 :     nsCOMPtr<nsINode> cloneNode = do_QueryInterface(clone);
    2577           0 :     if (closestAncestor)
    2578             :     {
    2579             :       // Append the subtree under closestAncestor since it is the
    2580             :       // immediate parent of the subtree.
    2581             : 
    2582           0 :       closestAncestor->AppendChild(*cloneNode, aRv);
    2583             :     }
    2584             :     else
    2585             :     {
    2586             :       // If we get here, there is no missing parent hierarchy between
    2587             :       // commonAncestor and node, so just append clone to commonCloneAncestor.
    2588             : 
    2589           0 :       commonCloneAncestor->AppendChild(*cloneNode, aRv);
    2590             :     }
    2591           0 :     if (aRv.Failed()) {
    2592           0 :       return nullptr;
    2593             :     }
    2594             : 
    2595             :     // Get the next subtree to be processed. The idea here is to setup
    2596             :     // the parameters for the next iteration of the loop.
    2597             : 
    2598           0 :     iter.Next();
    2599             : 
    2600           0 :     if (iter.IsDone())
    2601           0 :       break; // We must be done!
    2602             : 
    2603           0 :     nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
    2604           0 :     if (!nextNode) {
    2605           0 :       aRv.Throw(NS_ERROR_FAILURE);
    2606           0 :       return nullptr;
    2607             :     }
    2608             : 
    2609             :     // Get node and nextNode's common parent.
    2610           0 :     commonAncestor = nsContentUtils::GetCommonAncestor(node, nextNode);
    2611             : 
    2612           0 :     if (!commonAncestor) {
    2613           0 :       aRv.Throw(NS_ERROR_FAILURE);
    2614           0 :       return nullptr;
    2615             :     }
    2616             : 
    2617             :     // Find the equivalent of commonAncestor in the cloned tree!
    2618             : 
    2619           0 :     while (node && node != commonAncestor)
    2620             :     {
    2621           0 :       node = node->GetParentNode();
    2622           0 :       if (aRv.Failed()) {
    2623           0 :         return nullptr;
    2624             :       }
    2625             : 
    2626           0 :       if (!node) {
    2627           0 :         aRv.Throw(NS_ERROR_FAILURE);
    2628           0 :         return nullptr;
    2629             :       }
    2630             : 
    2631           0 :       cloneNode = cloneNode->GetParentNode();
    2632           0 :       if (!cloneNode) {
    2633           0 :         aRv.Throw(NS_ERROR_FAILURE);
    2634           0 :         return nullptr;
    2635             :       }
    2636             :     }
    2637             : 
    2638           0 :     commonCloneAncestor = cloneNode;
    2639             :   }
    2640             : 
    2641           0 :   return clonedFrag.forget();
    2642             : }
    2643             : 
    2644             : already_AddRefed<nsRange>
    2645           0 : nsRange::CloneRange() const
    2646             : {
    2647           0 :   RefPtr<nsRange> range = new nsRange(mOwner);
    2648             : 
    2649           0 :   range->SetMaySpanAnonymousSubtrees(mMaySpanAnonymousSubtrees);
    2650             : 
    2651           0 :   range->DoSetRange(mStartContainer, mStartOffset,
    2652           0 :                     mEndContainer, mEndOffset, mRoot);
    2653             : 
    2654           0 :   return range.forget();
    2655             : }
    2656             : 
    2657             : NS_IMETHODIMP
    2658           0 : nsRange::CloneRange(nsIDOMRange** aReturn)
    2659             : {
    2660           0 :   *aReturn = CloneRange().take();
    2661           0 :   return NS_OK;
    2662             : }
    2663             : 
    2664             : NS_IMETHODIMP
    2665           0 : nsRange::InsertNode(nsIDOMNode* aNode)
    2666             : {
    2667           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
    2668           0 :   if (!node) {
    2669           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
    2670             :   }
    2671             : 
    2672           0 :   ErrorResult rv;
    2673           0 :   InsertNode(*node, rv);
    2674           0 :   return rv.StealNSResult();
    2675             : }
    2676             : 
    2677             : void
    2678           0 : nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv)
    2679             : {
    2680           0 :   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    2681           0 :       !nsContentUtils::CanCallerAccess(&aNode)) {
    2682           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    2683           0 :     return;
    2684             :   }
    2685             : 
    2686           0 :   int32_t tStartOffset = StartOffset();
    2687             : 
    2688           0 :   nsCOMPtr<nsINode> tStartContainer = GetStartContainer(aRv);
    2689           0 :   if (aRv.Failed()) {
    2690           0 :     return;
    2691             :   }
    2692             : 
    2693           0 :   if (&aNode == tStartContainer) {
    2694           0 :     aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
    2695           0 :     return;
    2696             :   }
    2697             : 
    2698             :   // This is the node we'll be inserting before, and its parent
    2699           0 :   nsCOMPtr<nsINode> referenceNode;
    2700           0 :   nsCOMPtr<nsINode> referenceParentNode = tStartContainer;
    2701             : 
    2702           0 :   nsCOMPtr<nsIDOMText> startTextNode(do_QueryInterface(tStartContainer));
    2703           0 :   nsCOMPtr<nsIDOMNodeList> tChildList;
    2704           0 :   if (startTextNode) {
    2705           0 :     referenceParentNode = tStartContainer->GetParentNode();
    2706           0 :     if (!referenceParentNode) {
    2707           0 :       aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
    2708           0 :       return;
    2709             :     }
    2710             : 
    2711           0 :     referenceParentNode->EnsurePreInsertionValidity(aNode, tStartContainer,
    2712           0 :                                                     aRv);
    2713           0 :     if (aRv.Failed()) {
    2714           0 :       return;
    2715             :     }
    2716             : 
    2717           0 :     nsCOMPtr<nsIDOMText> secondPart;
    2718           0 :     aRv = startTextNode->SplitText(tStartOffset, getter_AddRefs(secondPart));
    2719           0 :     if (aRv.Failed()) {
    2720           0 :       return;
    2721             :     }
    2722             : 
    2723           0 :     referenceNode = do_QueryInterface(secondPart);
    2724             :   } else {
    2725           0 :     aRv = tStartContainer->AsDOMNode()->GetChildNodes(getter_AddRefs(tChildList));
    2726           0 :     if (aRv.Failed()) {
    2727           0 :       return;
    2728             :     }
    2729             : 
    2730             :     // find the insertion point in the DOM and insert the Node
    2731           0 :     nsCOMPtr<nsIDOMNode> q;
    2732           0 :     aRv = tChildList->Item(tStartOffset, getter_AddRefs(q));
    2733           0 :     referenceNode = do_QueryInterface(q);
    2734           0 :     if (aRv.Failed()) {
    2735           0 :       return;
    2736             :     }
    2737             : 
    2738           0 :     tStartContainer->EnsurePreInsertionValidity(aNode, referenceNode, aRv);
    2739           0 :     if (aRv.Failed()) {
    2740           0 :       return;
    2741             :     }
    2742             :   }
    2743             : 
    2744             :   // We might need to update the end to include the new node (bug 433662).
    2745             :   // Ideally we'd only do this if needed, but it's tricky to know when it's
    2746             :   // needed in advance (bug 765799).
    2747             :   int32_t newOffset;
    2748             : 
    2749           0 :   if (referenceNode) {
    2750           0 :     newOffset = IndexOf(referenceNode);
    2751             :   } else {
    2752             :     uint32_t length;
    2753           0 :     aRv = tChildList->GetLength(&length);
    2754           0 :     if (aRv.Failed()) {
    2755           0 :       return;
    2756             :     }
    2757             : 
    2758           0 :     newOffset = length;
    2759             :   }
    2760             : 
    2761           0 :   if (aNode.NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
    2762           0 :     newOffset += aNode.GetChildCount();
    2763             :   } else {
    2764           0 :     newOffset++;
    2765             :   }
    2766             : 
    2767             :   // Now actually insert the node
    2768           0 :   nsCOMPtr<nsINode> tResultNode;
    2769           0 :   tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv);
    2770           0 :   if (aRv.Failed()) {
    2771           0 :     return;
    2772             :   }
    2773             : 
    2774           0 :   if (Collapsed()) {
    2775           0 :     aRv = SetEnd(referenceParentNode, newOffset);
    2776             :   }
    2777             : }
    2778             : 
    2779             : NS_IMETHODIMP
    2780           0 : nsRange::SurroundContents(nsIDOMNode* aNewParent)
    2781             : {
    2782           0 :   nsCOMPtr<nsINode> node = do_QueryInterface(aNewParent);
    2783           0 :   if (!node) {
    2784           0 :     return NS_ERROR_DOM_NOT_OBJECT_ERR;
    2785             :   }
    2786           0 :   ErrorResult rv;
    2787           0 :   SurroundContents(*node, rv);
    2788           0 :   return rv.StealNSResult();
    2789             : }
    2790             : 
    2791             : void
    2792           0 : nsRange::SurroundContents(nsINode& aNewParent, ErrorResult& aRv)
    2793             : {
    2794           0 :   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
    2795           0 :       !nsContentUtils::CanCallerAccess(&aNewParent)) {
    2796           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    2797           0 :     return;
    2798             :   }
    2799             : 
    2800           0 :   if (!mRoot) {
    2801           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2802           0 :     return;
    2803             :   }
    2804             :   // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text
    2805             :   // node.
    2806           0 :   if (mStartContainer != mEndContainer) {
    2807           0 :     bool startIsText = mStartContainer->IsNodeOfType(nsINode::eTEXT);
    2808           0 :     bool endIsText = mEndContainer->IsNodeOfType(nsINode::eTEXT);
    2809           0 :     nsINode* startGrandParent = mStartContainer->GetParentNode();
    2810           0 :     nsINode* endGrandParent = mEndContainer->GetParentNode();
    2811           0 :     if (!((startIsText && endIsText &&
    2812           0 :            startGrandParent &&
    2813           0 :            startGrandParent == endGrandParent) ||
    2814           0 :           (startIsText &&
    2815           0 :            startGrandParent &&
    2816           0 :            startGrandParent == mEndContainer) ||
    2817           0 :           (endIsText &&
    2818           0 :            endGrandParent &&
    2819           0 :            endGrandParent == mStartContainer))) {
    2820           0 :       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    2821           0 :       return;
    2822             :     }
    2823             :   }
    2824             : 
    2825             :   // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted
    2826             :   // (Document, DocumentType, DocumentFragment)
    2827           0 :   uint16_t nodeType = aNewParent.NodeType();
    2828           0 :   if (nodeType == nsIDOMNode::DOCUMENT_NODE ||
    2829           0 :       nodeType == nsIDOMNode::DOCUMENT_TYPE_NODE ||
    2830             :       nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
    2831           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
    2832           0 :     return;
    2833             :   }
    2834             : 
    2835             :   // Extract the contents within the range.
    2836             : 
    2837           0 :   RefPtr<DocumentFragment> docFrag = ExtractContents(aRv);
    2838             : 
    2839           0 :   if (aRv.Failed()) {
    2840           0 :     return;
    2841             :   }
    2842             : 
    2843           0 :   if (!docFrag) {
    2844           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2845           0 :     return;
    2846             :   }
    2847             : 
    2848             :   // Spec says we need to remove all of aNewParent's
    2849             :   // children prior to insertion.
    2850             : 
    2851           0 :   nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes();
    2852           0 :   if (!children) {
    2853           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2854           0 :     return;
    2855             :   }
    2856             : 
    2857           0 :   uint32_t numChildren = children->Length();
    2858             : 
    2859           0 :   while (numChildren)
    2860             :   {
    2861           0 :     nsCOMPtr<nsINode> child = children->Item(--numChildren);
    2862           0 :     if (!child) {
    2863           0 :       aRv.Throw(NS_ERROR_FAILURE);
    2864           0 :       return;
    2865             :     }
    2866             : 
    2867           0 :     aNewParent.RemoveChild(*child, aRv);
    2868           0 :     if (aRv.Failed()) {
    2869           0 :       return;
    2870             :     }
    2871             :   }
    2872             : 
    2873             :   // Insert aNewParent at the range's start point.
    2874             : 
    2875           0 :   InsertNode(aNewParent, aRv);
    2876           0 :   if (aRv.Failed()) {
    2877           0 :     return;
    2878             :   }
    2879             : 
    2880             :   // Append the content we extracted under aNewParent.
    2881           0 :   aNewParent.AppendChild(*docFrag, aRv);
    2882           0 :   if (aRv.Failed()) {
    2883           0 :     return;
    2884             :   }
    2885             : 
    2886             :   // Select aNewParent, and its contents.
    2887             : 
    2888           0 :   SelectNode(aNewParent, aRv);
    2889             : }
    2890             : 
    2891             : NS_IMETHODIMP
    2892           0 : nsRange::ToString(nsAString& aReturn)
    2893             : {
    2894             :   // clear the string
    2895           0 :   aReturn.Truncate();
    2896             : 
    2897             :   // If we're unpositioned, return the empty string
    2898           0 :   if (!mIsPositioned) {
    2899           0 :     return NS_OK;
    2900             :   }
    2901             : 
    2902             : #ifdef DEBUG_range
    2903             :       printf("Range dump: -----------------------\n");
    2904             : #endif /* DEBUG */
    2905             : 
    2906             :   // effeciency hack for simple case
    2907           0 :   if (mStartContainer == mEndContainer) {
    2908           0 :     nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(mStartContainer);
    2909             : 
    2910           0 :     if (textNode)
    2911             :     {
    2912             : #ifdef DEBUG_range
    2913             :       // If debug, dump it:
    2914             :       nsCOMPtr<nsIContent> cN = do_QueryInterface(mStartContainer);
    2915             :       if (cN) cN->List(stdout);
    2916             :       printf("End Range dump: -----------------------\n");
    2917             : #endif /* DEBUG */
    2918             : 
    2919             :       // grab the text
    2920           0 :       if (NS_FAILED(textNode->SubstringData(mStartOffset,mEndOffset-mStartOffset,aReturn)))
    2921           0 :         return NS_ERROR_UNEXPECTED;
    2922           0 :       return NS_OK;
    2923             :     }
    2924             :   }
    2925             : 
    2926             :   /* complex case: mStartContainer != mEndContainer, or mStartParent not a text
    2927             :      node revisit - there are potential optimizations here and also tradeoffs.
    2928             :   */
    2929             : 
    2930           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
    2931           0 :   nsresult rv = iter->Init(this);
    2932           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2933             : 
    2934           0 :   nsString tempString;
    2935             : 
    2936             :   // loop through the content iterator, which returns nodes in the range in
    2937             :   // close tag order, and grab the text from any text node
    2938           0 :   while (!iter->IsDone())
    2939             :   {
    2940           0 :     nsINode *n = iter->GetCurrentNode();
    2941             : 
    2942             : #ifdef DEBUG_range
    2943             :     // If debug, dump it:
    2944             :     n->List(stdout);
    2945             : #endif /* DEBUG */
    2946           0 :     nsCOMPtr<nsIDOMText> textNode(do_QueryInterface(n));
    2947           0 :     if (textNode) // if it's a text node, get the text
    2948             :     {
    2949           0 :       if (n == mStartContainer) { // only include text past start offset
    2950             :         uint32_t strLength;
    2951           0 :         textNode->GetLength(&strLength);
    2952           0 :         textNode->SubstringData(mStartOffset,strLength-mStartOffset,tempString);
    2953           0 :         aReturn += tempString;
    2954           0 :       } else if (n == mEndContainer) { // only include text before end offset
    2955           0 :         textNode->SubstringData(0,mEndOffset,tempString);
    2956           0 :         aReturn += tempString;
    2957             :       } else { // grab the whole kit-n-kaboodle
    2958           0 :         textNode->GetData(tempString);
    2959           0 :         aReturn += tempString;
    2960             :       }
    2961             :     }
    2962             : 
    2963           0 :     iter->Next();
    2964             :   }
    2965             : 
    2966             : #ifdef DEBUG_range
    2967             :   printf("End Range dump: -----------------------\n");
    2968             : #endif /* DEBUG */
    2969           0 :   return NS_OK;
    2970             : }
    2971             : 
    2972             : 
    2973             : 
    2974             : NS_IMETHODIMP
    2975           0 : nsRange::Detach()
    2976             : {
    2977           0 :   return NS_OK;
    2978             : }
    2979             : 
    2980             : NS_IMETHODIMP
    2981           0 : nsRange::CreateContextualFragment(const nsAString& aFragment,
    2982             :                                   nsIDOMDocumentFragment** aReturn)
    2983             : {
    2984           0 :   if (mIsPositioned) {
    2985           0 :     return nsContentUtils::CreateContextualFragment(mStartContainer, aFragment,
    2986           0 :                                                     false, aReturn);
    2987             :   }
    2988           0 :   return NS_ERROR_FAILURE;
    2989             : }
    2990             : 
    2991             : already_AddRefed<DocumentFragment>
    2992           0 : nsRange::CreateContextualFragment(const nsAString& aFragment, ErrorResult& aRv)
    2993             : {
    2994           0 :   if (!mIsPositioned) {
    2995           0 :     aRv.Throw(NS_ERROR_FAILURE);
    2996           0 :     return nullptr;
    2997             :   }
    2998             : 
    2999             :   return nsContentUtils::CreateContextualFragment(mStartContainer, aFragment,
    3000           0 :                                                   false, aRv);
    3001             : }
    3002             : 
    3003           0 : static void ExtractRectFromOffset(nsIFrame* aFrame,
    3004             :                                   const int32_t aOffset, nsRect* aR, bool aKeepLeft,
    3005             :                                   bool aClampToEdge)
    3006             : {
    3007           0 :   nsPoint point;
    3008           0 :   aFrame->GetPointFromOffset(aOffset, &point);
    3009             : 
    3010           0 :   if (!aClampToEdge && !aR->Contains(point)) {
    3011           0 :     aR->width = 0;
    3012           0 :     aR->x = point.x;
    3013           0 :     return;
    3014             :   }
    3015             : 
    3016           0 :   if (aClampToEdge) {
    3017           0 :     point = aR->ClampPoint(point);
    3018             :   }
    3019             : 
    3020           0 :   if (aKeepLeft) {
    3021           0 :     aR->width = point.x - aR->x;
    3022             :   } else {
    3023           0 :     aR->width = aR->XMost() - point.x;
    3024           0 :     aR->x = point.x;
    3025             :   }
    3026             : }
    3027             : 
    3028             : static nsTextFrame*
    3029           0 : GetTextFrameForContent(nsIContent* aContent, bool aFlushLayout)
    3030             : {
    3031           0 :   nsIPresShell* presShell = aContent->OwnerDoc()->GetShell();
    3032           0 :   if (presShell) {
    3033           0 :     presShell->FrameConstructor()->EnsureFrameForTextNode(
    3034           0 :         static_cast<nsGenericDOMDataNode*>(aContent));
    3035             : 
    3036           0 :     if (aFlushLayout) {
    3037           0 :       aContent->OwnerDoc()->FlushPendingNotifications(FlushType::Layout);
    3038             :     }
    3039             : 
    3040           0 :     nsIFrame* frame = aContent->GetPrimaryFrame();
    3041           0 :     if (frame && frame->IsTextFrame()) {
    3042           0 :       return static_cast<nsTextFrame*>(frame);
    3043             :     }
    3044             :   }
    3045           0 :   return nullptr;
    3046             : }
    3047             : 
    3048           0 : static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
    3049             :                                    Sequence<nsString>* aTextList,
    3050             :                                    nsIContent* aContent, int32_t aStartOffset,
    3051             :                                    int32_t aEndOffset, bool aClampToEdge,
    3052             :                                    bool aFlushLayout)
    3053             : {
    3054           0 :   nsTextFrame* textFrame = GetTextFrameForContent(aContent, aFlushLayout);
    3055           0 :   if (textFrame) {
    3056           0 :     nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
    3057           0 :     for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
    3058           0 :       int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
    3059           0 :       if (fend <= aStartOffset || fstart >= aEndOffset)
    3060           0 :         continue;
    3061             : 
    3062             :       // Calculate the text content offsets we'll need if text is requested.
    3063           0 :       int32_t textContentStart = fstart;
    3064           0 :       int32_t textContentEnd = fend;
    3065             : 
    3066             :       // overlapping with the offset we want
    3067           0 :       f->EnsureTextRun(nsTextFrame::eInflated);
    3068           0 :       NS_ENSURE_TRUE(f->GetTextRun(nsTextFrame::eInflated), NS_ERROR_OUT_OF_MEMORY);
    3069           0 :       bool rtl = f->GetTextRun(nsTextFrame::eInflated)->IsRightToLeft();
    3070           0 :       nsRect r = f->GetRectRelativeToSelf();
    3071           0 :       if (fstart < aStartOffset) {
    3072             :         // aStartOffset is within this frame
    3073           0 :         ExtractRectFromOffset(f, aStartOffset, &r, rtl, aClampToEdge);
    3074           0 :         textContentStart = aStartOffset;
    3075             :       }
    3076           0 :       if (fend > aEndOffset) {
    3077             :         // aEndOffset is in the middle of this frame
    3078           0 :         ExtractRectFromOffset(f, aEndOffset, &r, !rtl, aClampToEdge);
    3079           0 :         textContentEnd = aEndOffset;
    3080             :       }
    3081           0 :       r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, relativeTo);
    3082           0 :       aCallback->AddRect(r);
    3083             : 
    3084             :       // Finally capture the text, if requested.
    3085           0 :       if (aTextList) {
    3086             :         nsIFrame::RenderedText renderedText = f->GetRenderedText(
    3087             :           textContentStart,
    3088             :           textContentEnd,
    3089             :           nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
    3090           0 :           nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
    3091             : 
    3092           0 :         aTextList->AppendElement(renderedText.mString, fallible);
    3093             :       }
    3094             :     }
    3095             :   }
    3096           0 :   return NS_OK;
    3097             : }
    3098             : 
    3099             : /* static */ void
    3100           0 : nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
    3101             :                                    Sequence<nsString>* aTextList,
    3102             :                                    nsRange* aRange,
    3103             :                                    nsINode* aStartContainer,
    3104             :                                    int32_t aStartOffset,
    3105             :                                    nsINode* aEndContainer,
    3106             :                                    int32_t aEndOffset,
    3107             :                                    bool aClampToEdge, bool aFlushLayout)
    3108             : {
    3109             :   // Hold strong pointers across the flush
    3110           0 :   nsCOMPtr<nsINode> startContainer = aStartContainer;
    3111           0 :   nsCOMPtr<nsINode> endContainer = aEndContainer;
    3112             : 
    3113             :   // Flush out layout so our frames are up to date.
    3114           0 :   if (!aStartContainer->IsInUncomposedDoc()) {
    3115           0 :     return;
    3116             :   }
    3117             : 
    3118           0 :   if (aFlushLayout) {
    3119           0 :     aStartContainer->OwnerDoc()->FlushPendingNotifications(FlushType::Layout);
    3120             :     // Recheck whether we're still in the document
    3121           0 :     if (!aStartContainer->IsInUncomposedDoc()) {
    3122           0 :       return;
    3123             :     }
    3124             :   }
    3125             : 
    3126           0 :   RangeSubtreeIterator iter;
    3127             : 
    3128           0 :   nsresult rv = iter.Init(aRange);
    3129           0 :   if (NS_FAILED(rv)) return;
    3130             : 
    3131           0 :   if (iter.IsDone()) {
    3132             :     // the range is collapsed, only continue if the cursor is in a text node
    3133           0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(aStartContainer);
    3134           0 :     if (content && content->IsNodeOfType(nsINode::eTEXT)) {
    3135           0 :       nsTextFrame* textFrame = GetTextFrameForContent(content, aFlushLayout);
    3136           0 :       if (textFrame) {
    3137             :         int32_t outOffset;
    3138             :         nsIFrame* outFrame;
    3139             :         textFrame->GetChildFrameContainingOffset(aStartOffset, false,
    3140           0 :           &outOffset, &outFrame);
    3141           0 :         if (outFrame) {
    3142             :            nsIFrame* relativeTo =
    3143           0 :              nsLayoutUtils::GetContainingBlockForClientRect(outFrame);
    3144           0 :            nsRect r = outFrame->GetRectRelativeToSelf();
    3145           0 :            ExtractRectFromOffset(outFrame, aStartOffset, &r, false, aClampToEdge);
    3146           0 :            r.width = 0;
    3147           0 :            r = nsLayoutUtils::TransformFrameRectToAncestor(outFrame, r, relativeTo);
    3148           0 :            aCollector->AddRect(r);
    3149             :         }
    3150             :       }
    3151             :     }
    3152           0 :     return;
    3153             :   }
    3154             : 
    3155           0 :   do {
    3156           0 :     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
    3157           0 :     iter.Next();
    3158           0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
    3159           0 :     if (!content)
    3160           0 :       continue;
    3161           0 :     if (content->IsNodeOfType(nsINode::eTEXT)) {
    3162           0 :        if (node == startContainer) {
    3163           0 :          int32_t offset = startContainer == endContainer ?
    3164           0 :            aEndOffset : content->GetText()->GetLength();
    3165           0 :          GetPartialTextRect(aCollector, aTextList, content, aStartOffset, offset,
    3166           0 :                             aClampToEdge, aFlushLayout);
    3167           0 :          continue;
    3168           0 :        } else if (node == endContainer) {
    3169           0 :          GetPartialTextRect(aCollector, aTextList, content, 0, aEndOffset,
    3170           0 :                             aClampToEdge, aFlushLayout);
    3171           0 :          continue;
    3172             :        }
    3173             :     }
    3174             : 
    3175           0 :     nsIFrame* frame = content->GetPrimaryFrame();
    3176           0 :     if (frame) {
    3177           0 :       nsLayoutUtils::GetAllInFlowRectsAndTexts(frame,
    3178             :         nsLayoutUtils::GetContainingBlockForClientRect(frame), aCollector,
    3179             :         aTextList,
    3180           0 :         nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
    3181             :     }
    3182           0 :   } while (!iter.IsDone());
    3183             : }
    3184             : 
    3185             : NS_IMETHODIMP
    3186           0 : nsRange::GetBoundingClientRect(nsIDOMClientRect** aResult)
    3187             : {
    3188           0 :   *aResult = GetBoundingClientRect(true).take();
    3189           0 :   return NS_OK;
    3190             : }
    3191             : 
    3192             : already_AddRefed<DOMRect>
    3193           0 : nsRange::GetBoundingClientRect(bool aClampToEdge, bool aFlushLayout)
    3194             : {
    3195           0 :   RefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
    3196           0 :   if (!mStartContainer) {
    3197           0 :     return rect.forget();
    3198             :   }
    3199             : 
    3200           0 :   nsLayoutUtils::RectAccumulator accumulator;
    3201           0 :   CollectClientRectsAndText(&accumulator, nullptr, this, mStartContainer,
    3202           0 :     mStartOffset, mEndContainer, mEndOffset, aClampToEdge, aFlushLayout);
    3203             : 
    3204           0 :   nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
    3205           0 :     accumulator.mResultRect;
    3206           0 :   rect->SetLayoutRect(r);
    3207           0 :   return rect.forget();
    3208             : }
    3209             : 
    3210             : NS_IMETHODIMP
    3211           0 : nsRange::GetClientRects(nsIDOMClientRectList** aResult)
    3212             : {
    3213           0 :   *aResult = GetClientRects(true).take();
    3214           0 :   return NS_OK;
    3215             : }
    3216             : 
    3217             : already_AddRefed<DOMRectList>
    3218           0 : nsRange::GetClientRects(bool aClampToEdge, bool aFlushLayout)
    3219             : {
    3220           0 :   if (!mStartContainer) {
    3221           0 :     return nullptr;
    3222             :   }
    3223             : 
    3224             :   RefPtr<DOMRectList> rectList =
    3225           0 :     new DOMRectList(static_cast<nsIDOMRange*>(this));
    3226             : 
    3227           0 :   nsLayoutUtils::RectListBuilder builder(rectList);
    3228             : 
    3229           0 :   CollectClientRectsAndText(&builder, nullptr, this, mStartContainer,
    3230           0 :     mStartOffset, mEndContainer, mEndOffset, aClampToEdge, aFlushLayout);
    3231           0 :   return rectList.forget();
    3232             : }
    3233             : 
    3234             : void
    3235           0 : nsRange::GetClientRectsAndTexts(
    3236             :   mozilla::dom::ClientRectsAndTexts& aResult,
    3237             :   ErrorResult& aErr)
    3238             : {
    3239           0 :   if (!mStartContainer) {
    3240           0 :     return;
    3241             :   }
    3242             : 
    3243           0 :   aResult.mRectList = new DOMRectList(static_cast<nsIDOMRange*>(this));
    3244             : 
    3245           0 :   nsLayoutUtils::RectListBuilder builder(aResult.mRectList);
    3246             : 
    3247           0 :   CollectClientRectsAndText(&builder, &aResult.mTextList, this,
    3248           0 :     mStartContainer, mStartOffset, mEndContainer, mEndOffset, true, true);
    3249             : }
    3250             : 
    3251             : NS_IMETHODIMP
    3252           0 : nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult)
    3253             : {
    3254           0 :   *aResult = nullptr;
    3255             : 
    3256           0 :   NS_ENSURE_TRUE(mStartContainer, NS_ERROR_UNEXPECTED);
    3257             : 
    3258           0 :   nsCOMPtr<nsINode> startContainer = do_QueryInterface(mStartContainer);
    3259           0 :   nsCOMPtr<nsINode> endContainer = do_QueryInterface(mEndContainer);
    3260             : 
    3261             :   // Flush out layout so our frames are up to date.
    3262           0 :   nsIDocument* doc = mStartContainer->OwnerDoc();
    3263           0 :   NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
    3264           0 :   doc->FlushPendingNotifications(FlushType::Frames);
    3265             : 
    3266             :   // Recheck whether we're still in the document
    3267           0 :   NS_ENSURE_TRUE(mStartContainer->IsInUncomposedDoc(), NS_ERROR_UNEXPECTED);
    3268             : 
    3269           0 :   RefPtr<nsFontFaceList> fontFaceList = new nsFontFaceList();
    3270             : 
    3271           0 :   RangeSubtreeIterator iter;
    3272           0 :   nsresult rv = iter.Init(this);
    3273           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3274             : 
    3275           0 :   while (!iter.IsDone()) {
    3276             :     // only collect anything if the range is not collapsed
    3277           0 :     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
    3278           0 :     iter.Next();
    3279             : 
    3280           0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
    3281           0 :     if (!content) {
    3282           0 :       continue;
    3283             :     }
    3284           0 :     nsIFrame* frame = content->GetPrimaryFrame();
    3285           0 :     if (!frame) {
    3286           0 :       continue;
    3287             :     }
    3288             : 
    3289           0 :     if (content->IsNodeOfType(nsINode::eTEXT)) {
    3290           0 :        if (node == startContainer) {
    3291           0 :          int32_t offset = startContainer == endContainer ?
    3292           0 :            mEndOffset : content->GetText()->GetLength();
    3293           0 :          nsLayoutUtils::GetFontFacesForText(frame, mStartOffset, offset,
    3294           0 :                                             true, fontFaceList);
    3295           0 :          continue;
    3296             :        }
    3297           0 :        if (node == endContainer) {
    3298           0 :          nsLayoutUtils::GetFontFacesForText(frame, 0, mEndOffset,
    3299           0 :                                             true, fontFaceList);
    3300           0 :          continue;
    3301             :        }
    3302             :     }
    3303             : 
    3304           0 :     nsLayoutUtils::GetFontFacesForFrames(frame, fontFaceList);
    3305             :   }
    3306             : 
    3307           0 :   fontFaceList.forget(aResult);
    3308           0 :   return NS_OK;
    3309             : }
    3310             : 
    3311             : nsINode*
    3312           0 : nsRange::GetRegisteredCommonAncestor()
    3313             : {
    3314           0 :   NS_ASSERTION(IsInSelection(),
    3315             :                "GetRegisteredCommonAncestor only valid for range in selection");
    3316           0 :   nsINode* ancestor = GetNextRangeCommonAncestor(mStartContainer);
    3317           0 :   while (ancestor) {
    3318             :     RangeHashTable* ranges =
    3319           0 :       static_cast<RangeHashTable*>(ancestor->GetProperty(nsGkAtoms::range));
    3320           0 :     if (ranges->GetEntry(this)) {
    3321           0 :       break;
    3322             :     }
    3323           0 :     ancestor = GetNextRangeCommonAncestor(ancestor->GetParentNode());
    3324             :   }
    3325           0 :   NS_ASSERTION(ancestor, "can't find common ancestor for selected range");
    3326           0 :   return ancestor;
    3327             : }
    3328             : 
    3329             : /* static */ bool nsRange::AutoInvalidateSelection::mIsNested;
    3330             : 
    3331          12 : nsRange::AutoInvalidateSelection::~AutoInvalidateSelection()
    3332             : {
    3333           6 :   NS_ASSERTION(mWasInSelection == mRange->IsInSelection(),
    3334             :                "Range got unselected in AutoInvalidateSelection block");
    3335           6 :   if (!mCommonAncestor) {
    3336           6 :     return;
    3337             :   }
    3338           0 :   mIsNested = false;
    3339           0 :   ::InvalidateAllFrames(mCommonAncestor);
    3340           0 :   nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor();
    3341           0 :   if (commonAncestor && commonAncestor != mCommonAncestor) {
    3342           0 :     ::InvalidateAllFrames(commonAncestor);
    3343             :   }
    3344           6 : }
    3345             : 
    3346             : /* static */ already_AddRefed<nsRange>
    3347           0 : nsRange::Constructor(const GlobalObject& aGlobal,
    3348             :                      ErrorResult& aRv)
    3349             : {
    3350           0 :   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
    3351           0 :   if (!window || !window->GetDoc()) {
    3352           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3353           0 :     return nullptr;
    3354             :   }
    3355             : 
    3356           0 :   return window->GetDoc()->CreateRange(aRv);
    3357             : }
    3358             : 
    3359           0 : static bool ExcludeIfNextToNonSelectable(nsIContent* aContent)
    3360             : {
    3361           0 :   return aContent->IsNodeOfType(nsINode::eTEXT) &&
    3362           0 :     aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE);
    3363             : }
    3364             : 
    3365             : void
    3366           0 : nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
    3367             : {
    3368           0 :   MOZ_ASSERT(mIsPositioned);
    3369           0 :   MOZ_ASSERT(mEndContainer);
    3370           0 :   MOZ_ASSERT(mStartContainer);
    3371             : 
    3372           0 :   nsRange* range = this;
    3373           0 :   RefPtr<nsRange> newRange;
    3374           0 :   while (range) {
    3375           0 :     nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
    3376           0 :     nsresult rv = iter->Init(range);
    3377           0 :     if (NS_FAILED(rv)) {
    3378           0 :       return;
    3379             :     }
    3380             : 
    3381           0 :     bool added = false;
    3382           0 :     bool seenSelectable = false;
    3383             :     // |firstNonSelectableContent| is the first node in a consecutive sequence
    3384             :     // of non-IsSelectable nodes.  When we find a selectable node after such
    3385             :     // a sequence we'll end the last nsRange, create a new one and restart
    3386             :     // the outer loop.
    3387           0 :     nsIContent* firstNonSelectableContent = nullptr;
    3388             :     while (true) {
    3389           0 :       ErrorResult err;
    3390           0 :       nsINode* node = iter->GetCurrentNode();
    3391           0 :       iter->Next();
    3392           0 :       bool selectable = true;
    3393             :       nsIContent* content =
    3394           0 :         node && node->IsContent() ? node->AsContent() : nullptr;
    3395           0 :       if (content) {
    3396           0 :         if (firstNonSelectableContent && ExcludeIfNextToNonSelectable(content)) {
    3397             :           // Ignorable whitespace next to a sequence of non-selectable nodes
    3398             :           // counts as non-selectable (bug 1216001).
    3399           0 :           selectable = false;
    3400             :         }
    3401           0 :         if (selectable) {
    3402           0 :           nsIFrame* frame = content->GetPrimaryFrame();
    3403           0 :           for (nsIContent* p = content; !frame && (p = p->GetParent()); ) {
    3404           0 :             frame = p->GetPrimaryFrame();
    3405             :           }
    3406           0 :           if (frame) {
    3407           0 :             selectable = frame->IsSelectable(nullptr);
    3408             :           }
    3409             :         }
    3410             :       }
    3411             : 
    3412           0 :       if (!selectable) {
    3413           0 :         if (!firstNonSelectableContent) {
    3414           0 :           firstNonSelectableContent = content;
    3415             :         }
    3416           0 :         if (iter->IsDone() && seenSelectable) {
    3417             :           // The tail end of the initial range is non-selectable - truncate the
    3418             :           // current range before the first non-selectable node.
    3419           0 :           range->SetEndBefore(*firstNonSelectableContent, err);
    3420             :         }
    3421           0 :       } else if (firstNonSelectableContent) {
    3422           0 :         if (range == this && !seenSelectable) {
    3423             :           // This is the initial range and all its nodes until now are
    3424             :           // non-selectable so just trim them from the start.
    3425           0 :           range->SetStartBefore(*node, err);
    3426           0 :           if (err.Failed()) {
    3427           0 :             return;
    3428             :           }
    3429           0 :           break; // restart the same range with a new iterator
    3430             :         } else {
    3431             :           // Save the end point before truncating the range.
    3432           0 :           nsINode* endContainer = range->mEndContainer;
    3433           0 :           int32_t endOffset = range->mEndOffset;
    3434             : 
    3435             :           // Truncate the current range before the first non-selectable node.
    3436           0 :           range->SetEndBefore(*firstNonSelectableContent, err);
    3437             : 
    3438             :           // Store it in the result (strong ref) - do this before creating
    3439             :           // a new range in |newRange| below so we don't drop the last ref
    3440             :           // to the range created in the previous iteration.
    3441           0 :           if (!added && !err.Failed()) {
    3442           0 :             aOutRanges->AppendElement(range);
    3443             :           }
    3444             : 
    3445             :           // Create a new range for the remainder.
    3446           0 :           nsINode* startContainer = node;
    3447           0 :           int32_t startOffset = 0;
    3448             :           // Don't start *inside* a node with independent selection though
    3449             :           // (e.g. <input>).
    3450           0 :           if (content && content->HasIndependentSelection()) {
    3451           0 :             nsINode* parent = startContainer->GetParent();
    3452           0 :             if (parent) {
    3453           0 :               startOffset = parent->IndexOf(startContainer);
    3454           0 :               startContainer = parent;
    3455             :             }
    3456             :           }
    3457           0 :           rv = CreateRange(startContainer, startOffset, endContainer, endOffset,
    3458           0 :                            getter_AddRefs(newRange));
    3459           0 :           if (NS_FAILED(rv) || newRange->Collapsed()) {
    3460           0 :             newRange = nullptr;
    3461             :           }
    3462           0 :           range = newRange;
    3463           0 :           break; // create a new iterator for the new range, if any
    3464             :         }
    3465             :       } else {
    3466           0 :         seenSelectable = true;
    3467           0 :         if (!added) {
    3468           0 :           added = true;
    3469           0 :           aOutRanges->AppendElement(range);
    3470             :         }
    3471             :       }
    3472           0 :       if (iter->IsDone()) {
    3473           0 :         return;
    3474             :       }
    3475           0 :     }
    3476             :   }
    3477             : }
    3478             : 
    3479             : struct InnerTextAccumulator
    3480             : {
    3481           0 :   explicit InnerTextAccumulator(mozilla::dom::DOMString& aValue)
    3482           0 :     : mString(aValue.AsAString()), mRequiredLineBreakCount(0) {}
    3483           0 :   void FlushLineBreaks()
    3484             :   {
    3485           0 :     while (mRequiredLineBreakCount > 0) {
    3486             :       // Required line breaks at the start of the text are suppressed.
    3487           0 :       if (!mString.IsEmpty()) {
    3488           0 :         mString.Append('\n');
    3489             :       }
    3490           0 :       --mRequiredLineBreakCount;
    3491             :     }
    3492           0 :   }
    3493           0 :   void Append(char aCh)
    3494             :   {
    3495           0 :     Append(nsAutoString(aCh));
    3496           0 :   }
    3497           0 :   void Append(const nsAString& aString)
    3498             :   {
    3499           0 :     if (aString.IsEmpty()) {
    3500           0 :       return;
    3501             :     }
    3502           0 :     FlushLineBreaks();
    3503           0 :     mString.Append(aString);
    3504             :   }
    3505           0 :   void AddRequiredLineBreakCount(int8_t aCount)
    3506             :   {
    3507           0 :     mRequiredLineBreakCount = std::max(mRequiredLineBreakCount, aCount);
    3508           0 :   }
    3509             : 
    3510             :   nsAString& mString;
    3511             :   int8_t mRequiredLineBreakCount;
    3512             : };
    3513             : 
    3514             : static bool
    3515           0 : IsVisibleAndNotInReplacedElement(nsIFrame* aFrame)
    3516             : {
    3517           0 :   if (!aFrame || !aFrame->StyleVisibility()->IsVisible()) {
    3518           0 :     return false;
    3519             :   }
    3520           0 :   for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
    3521           0 :     if (f->IsFrameOfType(nsIFrame::eReplaced) &&
    3522           0 :         !f->GetContent()->IsHTMLElement(nsGkAtoms::button) &&
    3523           0 :         !f->GetContent()->IsHTMLElement(nsGkAtoms::select)) {
    3524           0 :       return false;
    3525             :     }
    3526             :   }
    3527           0 :   return true;
    3528             : }
    3529             : 
    3530             : static bool
    3531           0 : ElementIsVisibleNoFlush(Element* aElement)
    3532             : {
    3533           0 :   if (!aElement) {
    3534           0 :     return false;
    3535             :   }
    3536             :   RefPtr<nsStyleContext> sc =
    3537           0 :     nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr);
    3538           0 :   return sc && sc->StyleVisibility()->IsVisible();
    3539             : }
    3540             : 
    3541             : static void
    3542           0 : AppendTransformedText(InnerTextAccumulator& aResult,
    3543             :                       nsGenericDOMDataNode* aTextNode,
    3544             :                       int32_t aStart, int32_t aEnd)
    3545             : {
    3546           0 :   nsIFrame* frame = aTextNode->GetPrimaryFrame();
    3547           0 :   if (!IsVisibleAndNotInReplacedElement(frame)) {
    3548           0 :     return;
    3549             :   }
    3550           0 :   nsIFrame::RenderedText text = frame->GetRenderedText(aStart, aEnd);
    3551           0 :   aResult.Append(text.mString);
    3552             : }
    3553             : 
    3554             : /**
    3555             :  * States for tree traversal. AT_NODE means that we are about to enter
    3556             :  * the current DOM node. AFTER_NODE means that we have just finished traversing
    3557             :  * the children of the current DOM node and are about to apply any
    3558             :  * "after processing the node's children" steps before we finish visiting
    3559             :  * the node.
    3560             :  */
    3561             : enum TreeTraversalState {
    3562             :   AT_NODE,
    3563             :   AFTER_NODE
    3564             : };
    3565             : 
    3566             : static int8_t
    3567           0 : GetRequiredInnerTextLineBreakCount(nsIFrame* aFrame)
    3568             : {
    3569           0 :   if (aFrame->GetContent()->IsHTMLElement(nsGkAtoms::p)) {
    3570           0 :     return 2;
    3571             :   }
    3572           0 :   const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
    3573           0 :   if (styleDisplay->IsBlockOutside(aFrame) ||
    3574           0 :       styleDisplay->mDisplay == StyleDisplay::TableCaption) {
    3575           0 :     return 1;
    3576             :   }
    3577           0 :   return 0;
    3578             : }
    3579             : 
    3580             : static bool
    3581           0 : IsLastCellOfRow(nsIFrame* aFrame)
    3582             : {
    3583           0 :   LayoutFrameType type = aFrame->Type();
    3584           0 :   if (type != LayoutFrameType::TableCell &&
    3585             :       type != LayoutFrameType::BCTableCell) {
    3586           0 :     return true;
    3587             :   }
    3588           0 :   for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
    3589           0 :     if (c->GetNextSibling()) {
    3590           0 :       return false;
    3591             :     }
    3592             :   }
    3593           0 :   return true;
    3594             : }
    3595             : 
    3596             : static bool
    3597           0 : IsLastRowOfRowGroup(nsIFrame* aFrame)
    3598             : {
    3599           0 :   if (!aFrame->IsTableRowFrame()) {
    3600           0 :     return true;
    3601             :   }
    3602           0 :   for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
    3603           0 :     if (c->GetNextSibling()) {
    3604           0 :       return false;
    3605             :     }
    3606             :   }
    3607           0 :   return true;
    3608             : }
    3609             : 
    3610             : static bool
    3611           0 : IsLastNonemptyRowGroupOfTable(nsIFrame* aFrame)
    3612             : {
    3613           0 :   if (!aFrame->IsTableRowGroupFrame()) {
    3614           0 :     return true;
    3615             :   }
    3616           0 :   for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
    3617           0 :     for (nsIFrame* next = c->GetNextSibling(); next; next = next->GetNextSibling()) {
    3618           0 :       if (next->PrincipalChildList().FirstChild()) {
    3619           0 :         return false;
    3620             :       }
    3621             :     }
    3622             :   }
    3623           0 :   return true;
    3624             : }
    3625             : 
    3626             : void
    3627           0 : nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError,
    3628             :                              nsIContent* aStartContainer, uint32_t aStartOffset,
    3629             :                              nsIContent* aEndContainer, uint32_t aEndOffset)
    3630             : {
    3631           0 :   InnerTextAccumulator result(aValue);
    3632           0 :   nsIContent* currentNode = aStartContainer;
    3633           0 :   TreeTraversalState currentState = AFTER_NODE;
    3634           0 :   if (aStartContainer->IsNodeOfType(nsINode::eTEXT)) {
    3635           0 :     auto t = static_cast<nsGenericDOMDataNode*>(aStartContainer);
    3636           0 :     if (aStartContainer == aEndContainer) {
    3637           0 :       AppendTransformedText(result, t, aStartOffset, aEndOffset);
    3638           0 :       return;
    3639             :     }
    3640           0 :     AppendTransformedText(result, t, aStartOffset, t->TextLength());
    3641             :   } else {
    3642           0 :     if (uint32_t(aStartOffset) < aStartContainer->GetChildCount()) {
    3643           0 :       currentNode = aStartContainer->GetChildAt(aStartOffset);
    3644           0 :       currentState = AT_NODE;
    3645             :     }
    3646             :   }
    3647             : 
    3648           0 :   nsIContent* endNode = aEndContainer;
    3649           0 :   TreeTraversalState endState = AFTER_NODE;
    3650           0 :   if (aEndContainer->IsNodeOfType(nsINode::eTEXT)) {
    3651           0 :     endState = AT_NODE;
    3652             :   } else {
    3653           0 :     if (uint32_t(aEndOffset) < aEndContainer->GetChildCount()) {
    3654           0 :       endNode = aEndContainer->GetChildAt(aEndOffset);
    3655           0 :       endState = AT_NODE;
    3656             :     }
    3657             :   }
    3658             : 
    3659           0 :   while (currentNode != endNode || currentState != endState) {
    3660           0 :     nsIFrame* f = currentNode->GetPrimaryFrame();
    3661           0 :     bool isVisibleAndNotReplaced = IsVisibleAndNotInReplacedElement(f);
    3662           0 :     if (currentState == AT_NODE) {
    3663           0 :       bool isText = currentNode->IsNodeOfType(nsINode::eTEXT);
    3664           0 :       if (isText && currentNode->GetParent()->IsHTMLElement(nsGkAtoms::rp) &&
    3665           0 :           ElementIsVisibleNoFlush(currentNode->GetParent()->AsElement())) {
    3666           0 :         nsAutoString str;
    3667           0 :         currentNode->GetTextContent(str, aError);
    3668           0 :         result.Append(str);
    3669           0 :       } else if (isVisibleAndNotReplaced) {
    3670           0 :         result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
    3671           0 :         if (isText) {
    3672           0 :           nsIFrame::RenderedText text = f->GetRenderedText();
    3673           0 :           result.Append(text.mString);
    3674             :         }
    3675             :       }
    3676           0 :       nsIContent* child = currentNode->GetFirstChild();
    3677           0 :       if (child) {
    3678           0 :         currentNode = child;
    3679           0 :         continue;
    3680             :       }
    3681           0 :       currentState = AFTER_NODE;
    3682             :     }
    3683           0 :     if (currentNode == endNode && currentState == endState) {
    3684           0 :       break;
    3685             :     }
    3686           0 :     if (isVisibleAndNotReplaced) {
    3687           0 :       if (currentNode->IsHTMLElement(nsGkAtoms::br)) {
    3688           0 :         result.Append('\n');
    3689             :       }
    3690           0 :       switch (f->StyleDisplay()->mDisplay) {
    3691             :       case StyleDisplay::TableCell:
    3692           0 :         if (!IsLastCellOfRow(f)) {
    3693           0 :           result.Append('\t');
    3694             :         }
    3695           0 :         break;
    3696             :       case StyleDisplay::TableRow:
    3697           0 :         if (!IsLastRowOfRowGroup(f) ||
    3698           0 :             !IsLastNonemptyRowGroupOfTable(f->GetParent())) {
    3699           0 :           result.Append('\n');
    3700             :         }
    3701           0 :         break;
    3702             :       default:
    3703           0 :         break; // Do nothing
    3704             :       }
    3705           0 :       result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
    3706             :     }
    3707           0 :     nsIContent* next = currentNode->GetNextSibling();
    3708           0 :     if (next) {
    3709           0 :       currentNode = next;
    3710           0 :       currentState = AT_NODE;
    3711             :     } else {
    3712           0 :       currentNode = currentNode->GetParent();
    3713             :     }
    3714             :   }
    3715             : 
    3716           0 :   if (aEndContainer->IsNodeOfType(nsINode::eTEXT)) {
    3717           0 :     nsGenericDOMDataNode* t = static_cast<nsGenericDOMDataNode*>(aEndContainer);
    3718           0 :     AppendTransformedText(result, t, 0, aEndOffset);
    3719             :   }
    3720             :   // Do not flush trailing line breaks! Required breaks at the end of the text
    3721             :   // are suppressed.
    3722             : }

Generated by: LCOV version 1.13