content/base/src/nsRange.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7  * Implementation of the DOM nsIDOMRange object.
     8  */
    10 #include "nscore.h"
    11 #include "nsRange.h"
    13 #include "nsString.h"
    14 #include "nsReadableUtils.h"
    15 #include "nsIDOMNode.h"
    16 #include "nsIDOMDocumentFragment.h"
    17 #include "nsIContent.h"
    18 #include "nsIDocument.h"
    19 #include "nsIDOMText.h"
    20 #include "nsError.h"
    21 #include "nsIContentIterator.h"
    22 #include "nsIDOMNodeList.h"
    23 #include "nsGkAtoms.h"
    24 #include "nsContentUtils.h"
    25 #include "nsGenericDOMDataNode.h"
    26 #include "nsLayoutUtils.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/ShadowRoot.h"
    34 #include "mozilla/Telemetry.h"
    35 #include "mozilla/Likely.h"
    36 #include "nsCSSFrameConstructor.h"
    38 using namespace mozilla;
    39 using namespace mozilla::dom;
    41 JSObject*
    42 nsRange::WrapObject(JSContext* aCx)
    43 {
    44   return RangeBinding::Wrap(aCx, this);
    45 }
    47 /******************************************************
    48  * stack based utilty class for managing monitor
    49  ******************************************************/
    51 static void InvalidateAllFrames(nsINode* aNode)
    52 {
    53   NS_PRECONDITION(aNode, "bad arg");
    55   nsIFrame* frame = nullptr;
    56   switch (aNode->NodeType()) {
    57     case nsIDOMNode::TEXT_NODE:
    58     case nsIDOMNode::ELEMENT_NODE:
    59     {
    60       nsIContent* content = static_cast<nsIContent*>(aNode);
    61       frame = content->GetPrimaryFrame();
    62       break;
    63     }
    64     case nsIDOMNode::DOCUMENT_NODE:
    65     {
    66       nsIDocument* doc = static_cast<nsIDocument*>(aNode);
    67       nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
    68       frame = shell ? shell->GetRootFrame() : nullptr;
    69       break;
    70     }
    71   }
    72   for (nsIFrame* f = frame; f; f = f->GetNextContinuation()) {
    73     f->InvalidateFrameSubtree();
    74   }
    75 }
    77 // Utility routine to detect if a content node is completely contained in a range
    78 // If outNodeBefore is returned true, then the node starts before the range does.
    79 // If outNodeAfter is returned true, then the node ends after the range does.
    80 // Note that both of the above might be true.
    81 // If neither are true, the node is contained inside of the range.
    82 // XXX - callers responsibility to ensure node in same doc as range! 
    84 // static
    85 nsresult
    86 nsRange::CompareNodeToRange(nsINode* aNode, nsRange* aRange,
    87                             bool *outNodeBefore, bool *outNodeAfter)
    88 {
    89   NS_ENSURE_STATE(aNode);
    90   // create a pair of dom points that expresses location of node:
    91   //     NODE(start), NODE(end)
    92   // Let incoming range be:
    93   //    {RANGE(start), RANGE(end)}
    94   // if (RANGE(start) <= NODE(start))  and (RANGE(end) => NODE(end))
    95   // then the Node is contained (completely) by the Range.
    97   if (!aRange || !aRange->IsPositioned()) 
    98     return NS_ERROR_UNEXPECTED; 
   100   // gather up the dom point info
   101   int32_t nodeStart, nodeEnd;
   102   nsINode* parent = aNode->GetParentNode();
   103   if (!parent) {
   104     // can't make a parent/offset pair to represent start or 
   105     // end of the root node, because it has no parent.
   106     // so instead represent it by (node,0) and (node,numChildren)
   107     parent = aNode;
   108     nodeStart = 0;
   109     nodeEnd = aNode->GetChildCount();
   110   }
   111   else {
   112     nodeStart = parent->IndexOf(aNode);
   113     nodeEnd = nodeStart + 1;
   114   }
   116   nsINode* rangeStartParent = aRange->GetStartParent();
   117   nsINode* rangeEndParent = aRange->GetEndParent();
   118   int32_t rangeStartOffset = aRange->StartOffset();
   119   int32_t rangeEndOffset = aRange->EndOffset();
   121   // is RANGE(start) <= NODE(start) ?
   122   bool disconnected = false;
   123   *outNodeBefore = nsContentUtils::ComparePoints(rangeStartParent,
   124                                                  rangeStartOffset,
   125                                                  parent, nodeStart,
   126                                                  &disconnected) > 0;
   127   NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
   129   // is RANGE(end) >= NODE(end) ?
   130   *outNodeAfter = nsContentUtils::ComparePoints(rangeEndParent,
   131                                                 rangeEndOffset,
   132                                                 parent, nodeEnd,
   133                                                 &disconnected) < 0;
   134   NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
   135   return NS_OK;
   136 }
   138 struct FindSelectedRangeData
   139 {
   140   nsINode*  mNode;
   141   nsRange* mResult;
   142   uint32_t  mStartOffset;
   143   uint32_t  mEndOffset;
   144 };
   146 static PLDHashOperator
   147 FindSelectedRange(nsPtrHashKey<nsRange>* aEntry, void* userArg)
   148 {
   149   nsRange* range = aEntry->GetKey();
   150   if (range->IsInSelection() && !range->Collapsed()) {
   151     FindSelectedRangeData* data = static_cast<FindSelectedRangeData*>(userArg);
   152     int32_t cmp = nsContentUtils::ComparePoints(data->mNode, data->mEndOffset,
   153                                                 range->GetStartParent(),
   154                                                 range->StartOffset());
   155     if (cmp == 1) {
   156       cmp = nsContentUtils::ComparePoints(data->mNode, data->mStartOffset,
   157                                           range->GetEndParent(),
   158                                           range->EndOffset());
   159       if (cmp == -1) {
   160         data->mResult = range;
   161         return PL_DHASH_STOP;
   162       }
   163     }
   164   }
   165   return PL_DHASH_NEXT;
   166 }
   168 static nsINode*
   169 GetNextRangeCommonAncestor(nsINode* aNode)
   170 {
   171   while (aNode && !aNode->IsCommonAncestorForRangeInSelection()) {
   172     if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
   173       return nullptr;
   174     }
   175     aNode = aNode->GetParentNode();
   176   }
   177   return aNode;
   178 }
   180 /* static */ bool
   181 nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
   182                         uint32_t aEndOffset)
   183 {
   184   NS_PRECONDITION(aNode, "bad arg");
   186   FindSelectedRangeData data = { aNode, nullptr, aStartOffset, aEndOffset };
   187   nsINode* n = GetNextRangeCommonAncestor(aNode);
   188   NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
   189                "orphan selection descendant");
   190   for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) {
   191     RangeHashTable* ranges =
   192       static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range));
   193     ranges->EnumerateEntries(FindSelectedRange, &data);
   194     if (data.mResult) {
   195       return true;
   196     }
   197   }
   198   return false;
   199 }
   201 /******************************************************
   202  * constructor/destructor
   203  ******************************************************/
   205 nsRange::~nsRange() 
   206 {
   207   NS_ASSERTION(!IsInSelection(), "deleting nsRange that is in use");
   209   // Maybe we can remove Detach() -- bug 702948.
   210   Telemetry::Accumulate(Telemetry::DOM_RANGE_DETACHED, mIsDetached);
   212   // we want the side effects (releases and list removals)
   213   DoSetRange(nullptr, 0, nullptr, 0, nullptr);
   214 }
   216 /* static */
   217 nsresult
   218 nsRange::CreateRange(nsINode* aStartParent, int32_t aStartOffset,
   219                      nsINode* aEndParent, int32_t aEndOffset,
   220                      nsRange** aRange)
   221 {
   222   nsCOMPtr<nsIDOMNode> startDomNode = do_QueryInterface(aStartParent);
   223   nsCOMPtr<nsIDOMNode> endDomNode = do_QueryInterface(aEndParent);
   225   nsresult rv = CreateRange(startDomNode, aStartOffset, endDomNode, aEndOffset,
   226                             aRange);
   228   return rv;
   230 }
   232 /* static */
   233 nsresult
   234 nsRange::CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset,
   235                      nsIDOMNode* aEndParent, int32_t aEndOffset,
   236                      nsRange** aRange)
   237 {
   238   MOZ_ASSERT(aRange);
   239   *aRange = nullptr;
   241   nsCOMPtr<nsINode> startParent = do_QueryInterface(aStartParent);
   242   NS_ENSURE_ARG_POINTER(startParent);
   244   nsRefPtr<nsRange> range = new nsRange(startParent);
   246   nsresult rv = range->SetStart(startParent, aStartOffset);
   247   NS_ENSURE_SUCCESS(rv, rv);
   249   rv = range->SetEnd(aEndParent, aEndOffset);
   250   NS_ENSURE_SUCCESS(rv, rv);
   252   range.forget(aRange);
   253   return NS_OK;
   254 }
   256 /* static */
   257 nsresult
   258 nsRange::CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset,
   259                      nsIDOMNode* aEndParent, int32_t aEndOffset,
   260                      nsIDOMRange** aRange)
   261 {
   262   nsRefPtr<nsRange> range;
   263   nsresult rv = nsRange::CreateRange(aStartParent, aStartOffset, aEndParent,
   264                                      aEndOffset, getter_AddRefs(range));
   265   range.forget(aRange);
   266   return rv;
   267 }
   269 /******************************************************
   270  * nsISupports
   271  ******************************************************/
   273 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange)
   274 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsRange,
   275                                                    DoSetRange(nullptr, 0, nullptr, 0, nullptr))
   277 // QueryInterface implementation for nsRange
   278 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange)
   279   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   280   NS_INTERFACE_MAP_ENTRY(nsIDOMRange)
   281   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   282   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMRange)
   283 NS_INTERFACE_MAP_END
   285 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
   287 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange)
   288   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   289   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
   290   tmp->Reset();
   291 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   293 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
   294   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
   295   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartParent)
   296   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndParent)
   297   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   298   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   299 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   301 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange)
   302   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   303 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   305 static void MarkDescendants(nsINode* aNode)
   306 {
   307   // Set NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
   308   // descendants unless aNode is already marked as a range common ancestor
   309   // or a descendant of one, in which case all of our descendants have the
   310   // bit set already.
   311   if (!aNode->IsSelectionDescendant()) {
   312     // don't set the Descendant bit on |aNode| itself
   313     nsINode* node = aNode->GetNextNode(aNode);
   314     while (node) {
   315       node->SetDescendantOfCommonAncestorForRangeInSelection();
   316       if (!node->IsCommonAncestorForRangeInSelection()) {
   317         node = node->GetNextNode(aNode);
   318       } else {
   319         // optimize: skip this sub-tree since it's marked already.
   320         node = node->GetNextNonChildNode(aNode);
   321       }
   322     }
   323   }
   324 }
   326 static void UnmarkDescendants(nsINode* aNode)
   327 {
   328   // Unset NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
   329   // descendants unless aNode is a descendant of another range common ancestor.
   330   // Also, exclude descendants of range common ancestors (but not the common
   331   // ancestor itself).
   332   if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
   333     // we know |aNode| doesn't have any bit set
   334     nsINode* node = aNode->GetNextNode(aNode);
   335     while (node) {
   336       node->ClearDescendantOfCommonAncestorForRangeInSelection();
   337       if (!node->IsCommonAncestorForRangeInSelection()) {
   338         node = node->GetNextNode(aNode);
   339       } else {
   340         // We found an ancestor of an overlapping range, skip its descendants.
   341         node = node->GetNextNonChildNode(aNode);
   342       }
   343     }
   344   }
   345 }
   347 void
   348 nsRange::RegisterCommonAncestor(nsINode* aNode)
   349 {
   350   NS_PRECONDITION(aNode, "bad arg");
   351   NS_ASSERTION(IsInSelection(), "registering range not in selection");
   353   MarkDescendants(aNode);
   355   RangeHashTable* ranges =
   356     static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
   357   if (!ranges) {
   358     ranges = new RangeHashTable;
   359     aNode->SetProperty(nsGkAtoms::range, ranges,
   360                        nsINode::DeleteProperty<nsRange::RangeHashTable>, true);
   361   }
   362   ranges->PutEntry(this);
   363   aNode->SetCommonAncestorForRangeInSelection();
   364 }
   366 void
   367 nsRange::UnregisterCommonAncestor(nsINode* aNode)
   368 {
   369   NS_PRECONDITION(aNode, "bad arg");
   370   NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
   371   RangeHashTable* ranges =
   372     static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
   373   NS_ASSERTION(ranges->GetEntry(this), "unknown range");
   375   if (ranges->Count() == 1) {
   376     aNode->ClearCommonAncestorForRangeInSelection();
   377     aNode->DeleteProperty(nsGkAtoms::range);
   378     UnmarkDescendants(aNode);
   379   } else {
   380     ranges->RemoveEntry(this);
   381   }
   382 }
   384 /******************************************************
   385  * nsIMutationObserver implementation
   386  ******************************************************/
   388 void
   389 nsRange::CharacterDataChanged(nsIDocument* aDocument,
   390                               nsIContent* aContent,
   391                               CharacterDataChangeInfo* aInfo)
   392 {
   393   MOZ_ASSERT(mAssertNextInsertOrAppendIndex == -1,
   394              "splitText failed to notify insert/append?");
   395   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
   397   nsINode* newRoot = nullptr;
   398   nsINode* newStartNode = nullptr;
   399   nsINode* newEndNode = nullptr;
   400   uint32_t newStartOffset = 0;
   401   uint32_t newEndOffset = 0;
   403   if (aInfo->mDetails &&
   404       aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eSplit) {
   405     // If the splitted text node is immediately before a range boundary point
   406     // that refers to a child index (i.e. its parent is the boundary container)
   407     // then we need to increment the corresponding offset to account for the new
   408     // text node that will be inserted.  If so, we need to prevent the next
   409     // ContentInserted or ContentAppended for this range from incrementing it
   410     // again (when the new text node is notified).
   411     nsINode* parentNode = aContent->GetParentNode();
   412     int32_t index = -1;
   413     if (parentNode == mEndParent && mEndOffset > 0 &&
   414         (index = parentNode->IndexOf(aContent)) + 1 == mEndOffset) {
   415       ++mEndOffset;
   416       mEndOffsetWasIncremented = true;
   417     }
   418     if (parentNode == mStartParent && mStartOffset > 0 &&
   419         (index != -1 ? index : parentNode->IndexOf(aContent)) + 1 == mStartOffset) {
   420       ++mStartOffset;
   421       mStartOffsetWasIncremented = true;
   422     }
   423 #ifdef DEBUG
   424     if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
   425       mAssertNextInsertOrAppendIndex =
   426         (mStartOffsetWasIncremented ? mStartOffset : mEndOffset) - 1;
   427       mAssertNextInsertOrAppendNode = aInfo->mDetails->mNextSibling;
   428     }
   429 #endif
   430   }
   432   // If the changed node contains our start boundary and the change starts
   433   // before the boundary we'll need to adjust the offset.
   434   if (aContent == mStartParent &&
   435       aInfo->mChangeStart < static_cast<uint32_t>(mStartOffset)) {
   436     if (aInfo->mDetails) {
   437       // splitText(), aInfo->mDetails->mNextSibling is the new text node
   438       NS_ASSERTION(aInfo->mDetails->mType ==
   439                    CharacterDataChangeInfo::Details::eSplit,
   440                    "only a split can start before the end");
   441       NS_ASSERTION(static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd + 1,
   442                    "mStartOffset is beyond the end of this node");
   443       newStartOffset = static_cast<uint32_t>(mStartOffset) - aInfo->mChangeStart;
   444       newStartNode = aInfo->mDetails->mNextSibling;
   445       if (MOZ_UNLIKELY(aContent == mRoot)) {
   446         newRoot = IsValidBoundary(newStartNode);
   447       }
   449       bool isCommonAncestor = IsInSelection() && mStartParent == mEndParent;
   450       if (isCommonAncestor) {
   451         UnregisterCommonAncestor(mStartParent);
   452         RegisterCommonAncestor(newStartNode);
   453       }
   454       if (mStartParent->IsDescendantOfCommonAncestorForRangeInSelection()) {
   455         newStartNode->SetDescendantOfCommonAncestorForRangeInSelection();
   456       }
   457     } else {
   458       // If boundary is inside changed text, position it before change
   459       // else adjust start offset for the change in length.
   460       mStartOffset = static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd ?
   461         aInfo->mChangeStart :
   462         mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
   463           aInfo->mReplaceLength;
   464     }
   465   }
   467   // Do the same thing for the end boundary, except for splitText of a node
   468   // with no parent then only switch to the new node if the start boundary
   469   // did so too (otherwise the range would end up with disconnected nodes).
   470   if (aContent == mEndParent &&
   471       aInfo->mChangeStart < static_cast<uint32_t>(mEndOffset)) {
   472     if (aInfo->mDetails && (aContent->GetParentNode() || newStartNode)) {
   473       // splitText(), aInfo->mDetails->mNextSibling is the new text node
   474       NS_ASSERTION(aInfo->mDetails->mType ==
   475                    CharacterDataChangeInfo::Details::eSplit,
   476                    "only a split can start before the end");
   477       NS_ASSERTION(static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd + 1,
   478                    "mEndOffset is beyond the end of this node");
   479       newEndOffset = static_cast<uint32_t>(mEndOffset) - aInfo->mChangeStart;
   480       newEndNode = aInfo->mDetails->mNextSibling;
   482       bool isCommonAncestor = IsInSelection() && mStartParent == mEndParent;
   483       if (isCommonAncestor && !newStartNode) {
   484         // The split occurs inside the range.
   485         UnregisterCommonAncestor(mStartParent);
   486         RegisterCommonAncestor(mStartParent->GetParentNode());
   487         newEndNode->SetDescendantOfCommonAncestorForRangeInSelection();
   488       } else if (mEndParent->IsDescendantOfCommonAncestorForRangeInSelection()) {
   489         newEndNode->SetDescendantOfCommonAncestorForRangeInSelection();
   490       }
   491     } else {
   492       mEndOffset = static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd ?
   493         aInfo->mChangeStart :
   494         mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
   495           aInfo->mReplaceLength;
   496     }
   497   }
   499   if (aInfo->mDetails &&
   500       aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eMerge) {
   501     // normalize(), aInfo->mDetails->mNextSibling is the merged text node
   502     // that will be removed
   503     nsIContent* removed = aInfo->mDetails->mNextSibling;
   504     if (removed == mStartParent) {
   505       newStartOffset = static_cast<uint32_t>(mStartOffset) + aInfo->mChangeStart;
   506       newStartNode = aContent;
   507       if (MOZ_UNLIKELY(removed == mRoot)) {
   508         newRoot = IsValidBoundary(newStartNode);
   509       }
   510     }
   511     if (removed == mEndParent) {
   512       newEndOffset = static_cast<uint32_t>(mEndOffset) + aInfo->mChangeStart;
   513       newEndNode = aContent;
   514       if (MOZ_UNLIKELY(removed == mRoot)) {
   515         newRoot = IsValidBoundary(newEndNode);
   516       }
   517     }
   518     // When the removed text node's parent is one of our boundary nodes we may
   519     // need to adjust the offset to account for the removed node. However,
   520     // there will also be a ContentRemoved notification later so the only cases
   521     // we need to handle here is when the removed node is the text node after
   522     // the boundary.  (The m*Offset > 0 check is an optimization - a boundary
   523     // point before the first child is never affected by normalize().)
   524     nsINode* parentNode = aContent->GetParentNode();
   525     if (parentNode == mStartParent && mStartOffset > 0 &&
   526         uint32_t(mStartOffset) < parentNode->GetChildCount() &&
   527         removed == parentNode->GetChildAt(mStartOffset)) {
   528       newStartNode = aContent;
   529       newStartOffset = aInfo->mChangeStart;
   530     }
   531     if (parentNode == mEndParent && mEndOffset > 0 &&
   532         uint32_t(mEndOffset) < parentNode->GetChildCount() &&
   533         removed == parentNode->GetChildAt(mEndOffset)) {
   534       newEndNode = aContent;
   535       newEndOffset = aInfo->mChangeEnd;
   536     }
   537   }
   539   if (newStartNode || newEndNode) {
   540     if (!newStartNode) {
   541       newStartNode = mStartParent;
   542       newStartOffset = mStartOffset;
   543     }
   544     if (!newEndNode) {
   545       newEndNode = mEndParent;
   546       newEndOffset = mEndOffset;
   547     }
   548     DoSetRange(newStartNode, newStartOffset, newEndNode, newEndOffset,
   549                newRoot ? newRoot : mRoot.get(),
   550                !newEndNode->GetParentNode() || !newStartNode->GetParentNode());
   551   }
   552 }
   554 void
   555 nsRange::ContentAppended(nsIDocument* aDocument,
   556                          nsIContent*  aContainer,
   557                          nsIContent*  aFirstNewContent,
   558                          int32_t      aNewIndexInContainer)
   559 {
   560   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
   562   nsINode* container = NODE_FROM(aContainer, aDocument);
   563   if (container->IsSelectionDescendant() && IsInSelection()) {
   564     nsINode* child = aFirstNewContent;
   565     while (child) {
   566       if (!child->IsDescendantOfCommonAncestorForRangeInSelection()) {
   567         MarkDescendants(child);
   568         child->SetDescendantOfCommonAncestorForRangeInSelection();
   569       }
   570       child = child->GetNextSibling();
   571     }
   572   }
   574   if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
   575     MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aNewIndexInContainer);
   576     MOZ_ASSERT(mAssertNextInsertOrAppendNode == aFirstNewContent);
   577     MOZ_ASSERT(aFirstNewContent->IsNodeOfType(nsINode::eDATA_NODE));
   578     mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
   579 #ifdef DEBUG
   580     mAssertNextInsertOrAppendIndex = -1;
   581     mAssertNextInsertOrAppendNode = nullptr;
   582 #endif
   583   }
   584 }
   586 void
   587 nsRange::ContentInserted(nsIDocument* aDocument,
   588                          nsIContent* aContainer,
   589                          nsIContent* aChild,
   590                          int32_t aIndexInContainer)
   591 {
   592   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
   594   nsINode* container = NODE_FROM(aContainer, aDocument);
   596   // Adjust position if a sibling was inserted.
   597   if (container == mStartParent && aIndexInContainer < mStartOffset &&
   598       !mStartOffsetWasIncremented) {
   599     ++mStartOffset;
   600   }
   601   if (container == mEndParent && aIndexInContainer < mEndOffset &&
   602       !mEndOffsetWasIncremented) {
   603     ++mEndOffset;
   604   }
   605   if (container->IsSelectionDescendant() &&
   606       !aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
   607     MarkDescendants(aChild);
   608     aChild->SetDescendantOfCommonAncestorForRangeInSelection();
   609   }
   611   if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
   612     MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aIndexInContainer);
   613     MOZ_ASSERT(mAssertNextInsertOrAppendNode == aChild);
   614     MOZ_ASSERT(aChild->IsNodeOfType(nsINode::eDATA_NODE));
   615     mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
   616 #ifdef DEBUG
   617     mAssertNextInsertOrAppendIndex = -1;
   618     mAssertNextInsertOrAppendNode = nullptr;
   619 #endif
   620   }
   621 }
   623 void
   624 nsRange::ContentRemoved(nsIDocument* aDocument,
   625                         nsIContent* aContainer,
   626                         nsIContent* aChild,
   627                         int32_t aIndexInContainer,
   628                         nsIContent* aPreviousSibling)
   629 {
   630   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
   631   MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented &&
   632              mAssertNextInsertOrAppendIndex == -1,
   633              "splitText failed to notify insert/append?");
   635   nsINode* container = NODE_FROM(aContainer, aDocument);
   636   bool gravitateStart = false;
   637   bool gravitateEnd = false;
   638   bool didCheckStartParentDescendant = false;
   640   // Adjust position if a sibling was removed...
   641   if (container == mStartParent) {
   642     if (aIndexInContainer < mStartOffset) {
   643       --mStartOffset;
   644     }
   645   } else { // ...or gravitate if an ancestor was removed.
   646     didCheckStartParentDescendant = true;
   647     gravitateStart = nsContentUtils::ContentIsDescendantOf(mStartParent, aChild);
   648   }
   650   // Do same thing for end boundry.
   651   if (container == mEndParent) {
   652     if (aIndexInContainer < mEndOffset) {
   653       --mEndOffset;
   654     }
   655   } else if (didCheckStartParentDescendant && mStartParent == mEndParent) {
   656     gravitateEnd = gravitateStart;
   657   } else {
   658     gravitateEnd = nsContentUtils::ContentIsDescendantOf(mEndParent, aChild);
   659   }
   661   if (!mEnableGravitationOnElementRemoval) {
   662     // Do not gravitate.
   663     return;
   664   }
   666   if (gravitateStart || gravitateEnd) {
   667     DoSetRange(gravitateStart ? container : mStartParent.get(),
   668                gravitateStart ? aIndexInContainer : mStartOffset,
   669                gravitateEnd ? container : mEndParent.get(),
   670                gravitateEnd ? aIndexInContainer : mEndOffset,
   671                mRoot);
   672   }
   673   if (container->IsSelectionDescendant() &&
   674       aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
   675     aChild->ClearDescendantOfCommonAncestorForRangeInSelection();
   676     UnmarkDescendants(aChild);
   677   }
   678 }
   680 void
   681 nsRange::ParentChainChanged(nsIContent *aContent)
   682 {
   683   MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented &&
   684              mAssertNextInsertOrAppendIndex == -1,
   685              "splitText failed to notify insert/append?");
   686   NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
   687   nsINode* newRoot = IsValidBoundary(mStartParent);
   688   NS_ASSERTION(newRoot, "No valid boundary or root found!");
   689   if (newRoot != IsValidBoundary(mEndParent)) {
   690     // Sometimes ordering involved in cycle collection can lead to our
   691     // start parent and/or end parent being disconnected from our root
   692     // without our getting a ContentRemoved notification.
   693     // See bug 846096 for more details.
   694     NS_ASSERTION(mEndParent->IsInNativeAnonymousSubtree(),
   695                  "This special case should happen only with "
   696                  "native-anonymous content");
   697     // When that happens, bail out and set pointers to null; since we're
   698     // in cycle collection and unreachable it shouldn't matter.
   699     Reset();
   700     return;
   701   }
   702   // This is safe without holding a strong ref to self as long as the change
   703   // of mRoot is the last thing in DoSetRange.
   704   DoSetRange(mStartParent, mStartOffset, mEndParent, mEndOffset, newRoot);
   705 }
   707 /******************************************************
   708  * Utilities for comparing points: API from nsIDOMRange
   709  ******************************************************/
   710 NS_IMETHODIMP
   711 nsRange::IsPointInRange(nsIDOMNode* aParent, int32_t aOffset, bool* aResult)
   712 {
   713   nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
   714   if (!parent) {
   715     return NS_ERROR_DOM_NOT_OBJECT_ERR;
   716   }
   718   ErrorResult rv;
   719   *aResult = IsPointInRange(*parent, aOffset, rv);
   720   return rv.ErrorCode();
   721 }
   723 bool
   724 nsRange::IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aRv)
   725 {
   726   uint16_t compareResult = ComparePoint(aParent, aOffset, aRv);
   727   // If the node isn't in the range's document, it clearly isn't in the range.
   728   if (aRv.ErrorCode() == NS_ERROR_DOM_WRONG_DOCUMENT_ERR) {
   729     aRv = NS_OK;
   730     return false;
   731   }
   733   return compareResult == 0;
   734 }
   736 // returns -1 if point is before range, 0 if point is in range,
   737 // 1 if point is after range.
   738 NS_IMETHODIMP
   739 nsRange::ComparePoint(nsIDOMNode* aParent, int32_t aOffset, int16_t* aResult)
   740 {
   741   nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
   742   NS_ENSURE_TRUE(parent, NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
   744   ErrorResult rv;
   745   *aResult = ComparePoint(*parent, aOffset, rv);
   746   return rv.ErrorCode();
   747 }
   749 int16_t
   750 nsRange::ComparePoint(nsINode& aParent, uint32_t aOffset, ErrorResult& aRv)
   751 {
   752   // our range is in a good state?
   753   if (!mIsPositioned) {
   754     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   755     return 0;
   756   }
   758   if (!nsContentUtils::ContentIsDescendantOf(&aParent, mRoot)) {
   759     aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
   760     return 0;
   761   }
   763   if (aParent.NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
   764     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   765     return 0;
   766   }
   768   if (aOffset > aParent.Length()) {
   769     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   770     return 0;
   771   }
   773   int32_t cmp;
   774   if ((cmp = nsContentUtils::ComparePoints(&aParent, aOffset,
   775                                            mStartParent, mStartOffset)) <= 0) {
   777     return cmp;
   778   }
   779   if (nsContentUtils::ComparePoints(mEndParent, mEndOffset,
   780                                     &aParent, aOffset) == -1) {
   781     return 1;
   782   }
   784   return 0;
   785 }
   787 NS_IMETHODIMP
   788 nsRange::IntersectsNode(nsIDOMNode* aNode, bool* aResult)
   789 {
   790   *aResult = false;
   792   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   793   // TODO: This should throw a TypeError.
   794   NS_ENSURE_ARG(node);
   796   ErrorResult rv;
   797   *aResult = IntersectsNode(*node, rv);
   798   return rv.ErrorCode();
   799 }
   801 bool
   802 nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv)
   803 {
   804   if (!mIsPositioned) {
   805     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   806     return false;
   807   }
   809   // Step 3.
   810   nsINode* parent = aNode.GetParentNode();
   811   if (!parent) {
   812     // Steps 2 and 4. 
   813     // |parent| is null, so |node|'s root is |node| itself.
   814     return GetRoot() == &aNode;
   815   }
   817   // Step 5.
   818   int32_t nodeIndex = parent->IndexOf(&aNode);
   820   // Steps 6-7.
   821   // Note: if disconnected is true, ComparePoints returns 1.
   822   bool disconnected = false;
   823   bool result = nsContentUtils::ComparePoints(mStartParent, mStartOffset,
   824                                              parent, nodeIndex + 1,
   825                                              &disconnected) < 0 &&
   826                nsContentUtils::ComparePoints(parent, nodeIndex,
   827                                              mEndParent, mEndOffset,
   828                                              &disconnected) < 0;
   830   // Step 2.
   831   if (disconnected) {
   832     result = false;
   833   }
   834   return result;
   835 }
   837 /******************************************************
   838  * Private helper routines
   839  ******************************************************/
   841 // It's important that all setting of the range start/end points 
   842 // go through this function, which will do all the right voodoo
   843 // for content notification of range ownership.  
   844 // Calling DoSetRange with either parent argument null will collapse
   845 // the range to have both endpoints point to the other node
   846 void
   847 nsRange::DoSetRange(nsINode* aStartN, int32_t aStartOffset,
   848                     nsINode* aEndN, int32_t aEndOffset,
   849                     nsINode* aRoot, bool aNotInsertedYet)
   850 {
   851   NS_PRECONDITION((aStartN && aEndN && aRoot) ||
   852                   (!aStartN && !aEndN && !aRoot),
   853                   "Set all or none");
   854   NS_PRECONDITION(!aRoot || aNotInsertedYet ||
   855                   (nsContentUtils::ContentIsDescendantOf(aStartN, aRoot) &&
   856                    nsContentUtils::ContentIsDescendantOf(aEndN, aRoot) &&
   857                    aRoot == IsValidBoundary(aStartN) &&
   858                    aRoot == IsValidBoundary(aEndN)),
   859                   "Wrong root");
   860   NS_PRECONDITION(!aRoot ||
   861                   (aStartN->IsNodeOfType(nsINode::eCONTENT) &&
   862                    aEndN->IsNodeOfType(nsINode::eCONTENT) &&
   863                    aRoot ==
   864                     static_cast<nsIContent*>(aStartN)->GetBindingParent() &&
   865                    aRoot ==
   866                     static_cast<nsIContent*>(aEndN)->GetBindingParent()) ||
   867                   (!aRoot->GetParentNode() &&
   868                    (aRoot->IsNodeOfType(nsINode::eDOCUMENT) ||
   869                     aRoot->IsNodeOfType(nsINode::eATTRIBUTE) ||
   870                     aRoot->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) ||
   871                      /*For backward compatibility*/
   872                     aRoot->IsNodeOfType(nsINode::eCONTENT))),
   873                   "Bad root");
   875   if (mRoot != aRoot) {
   876     if (mRoot) {
   877       mRoot->RemoveMutationObserver(this);
   878     }
   879     if (aRoot) {
   880       aRoot->AddMutationObserver(this);
   881     }
   882   }
   883   bool checkCommonAncestor = (mStartParent != aStartN || mEndParent != aEndN) &&
   884                              IsInSelection() && !aNotInsertedYet;
   885   nsINode* oldCommonAncestor = checkCommonAncestor ? GetCommonAncestor() : nullptr;
   886   mStartParent = aStartN;
   887   mStartOffset = aStartOffset;
   888   mEndParent = aEndN;
   889   mEndOffset = aEndOffset;
   890   mIsPositioned = !!mStartParent;
   891   if (checkCommonAncestor) {
   892     nsINode* newCommonAncestor = GetCommonAncestor();
   893     if (newCommonAncestor != oldCommonAncestor) {
   894       if (oldCommonAncestor) {
   895         UnregisterCommonAncestor(oldCommonAncestor);
   896       }
   897       if (newCommonAncestor) {
   898         RegisterCommonAncestor(newCommonAncestor);
   899       } else {
   900         NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes");
   901         mInSelection = false;
   902       }
   903     }
   904   }
   906   // This needs to be the last thing this function does.  See comment
   907   // in ParentChainChanged.
   908   mRoot = aRoot;
   909 }
   911 static int32_t
   912 IndexOf(nsINode* aChild)
   913 {
   914   nsINode* parent = aChild->GetParentNode();
   916   return parent ? parent->IndexOf(aChild) : -1;
   917 }
   919 nsINode*
   920 nsRange::GetCommonAncestor() const
   921 {
   922   return mIsPositioned ?
   923     nsContentUtils::GetCommonAncestor(mStartParent, mEndParent) :
   924     nullptr;
   925 }
   927 void
   928 nsRange::Reset()
   929 {
   930   DoSetRange(nullptr, 0, nullptr, 0, nullptr);
   931 }
   933 /******************************************************
   934  * public functionality
   935  ******************************************************/
   937 NS_IMETHODIMP
   938 nsRange::GetStartContainer(nsIDOMNode** aStartParent)
   939 {
   940   if (!mIsPositioned)
   941     return NS_ERROR_NOT_INITIALIZED;
   943   return CallQueryInterface(mStartParent, aStartParent);
   944 }
   946 nsINode*
   947 nsRange::GetStartContainer(ErrorResult& aRv) const
   948 {
   949   if (!mIsPositioned) {
   950     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   951     return nullptr;
   952   }
   954   return mStartParent;
   955 }
   957 NS_IMETHODIMP
   958 nsRange::GetStartOffset(int32_t* aStartOffset)
   959 {
   960   if (!mIsPositioned)
   961     return NS_ERROR_NOT_INITIALIZED;
   963   *aStartOffset = mStartOffset;
   965   return NS_OK;
   966 }
   968 uint32_t
   969 nsRange::GetStartOffset(ErrorResult& aRv) const
   970 {
   971   if (!mIsPositioned) {
   972     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   973     return 0;
   974   }
   976   return mStartOffset;
   977 }
   979 NS_IMETHODIMP
   980 nsRange::GetEndContainer(nsIDOMNode** aEndParent)
   981 {
   982   if (!mIsPositioned)
   983     return NS_ERROR_NOT_INITIALIZED;
   985   return CallQueryInterface(mEndParent, aEndParent);
   986 }
   988 nsINode*
   989 nsRange::GetEndContainer(ErrorResult& aRv) const
   990 {
   991   if (!mIsPositioned) {
   992     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   993     return nullptr;
   994   }
   996   return mEndParent;
   997 }
   999 NS_IMETHODIMP
  1000 nsRange::GetEndOffset(int32_t* aEndOffset)
  1002   if (!mIsPositioned)
  1003     return NS_ERROR_NOT_INITIALIZED;
  1005   *aEndOffset = mEndOffset;
  1007   return NS_OK;
  1010 uint32_t
  1011 nsRange::GetEndOffset(ErrorResult& aRv) const
  1013   if (!mIsPositioned) {
  1014     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
  1015     return 0;
  1018   return mEndOffset;
  1021 NS_IMETHODIMP
  1022 nsRange::GetCollapsed(bool* aIsCollapsed)
  1024   if (!mIsPositioned)
  1025     return NS_ERROR_NOT_INITIALIZED;
  1027   *aIsCollapsed = Collapsed();
  1029   return NS_OK;
  1032 nsINode*
  1033 nsRange::GetCommonAncestorContainer(ErrorResult& aRv) const
  1035   if (!mIsPositioned) {
  1036     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
  1037     return nullptr;
  1040   return nsContentUtils::GetCommonAncestor(mStartParent, mEndParent);
  1043 NS_IMETHODIMP
  1044 nsRange::GetCommonAncestorContainer(nsIDOMNode** aCommonParent)
  1046   ErrorResult rv;
  1047   nsINode* commonAncestor = GetCommonAncestorContainer(rv);
  1048   if (commonAncestor) {
  1049     NS_ADDREF(*aCommonParent = commonAncestor->AsDOMNode());
  1050   } else {
  1051     *aCommonParent = nullptr;
  1054   return rv.ErrorCode();
  1057 nsINode*
  1058 nsRange::IsValidBoundary(nsINode* aNode)
  1060   if (!aNode) {
  1061     return nullptr;
  1064   if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
  1065     nsIContent* content = static_cast<nsIContent*>(aNode);
  1066     if (content->Tag() == nsGkAtoms::documentTypeNodeName) {
  1067       return nullptr;
  1070     if (!mMaySpanAnonymousSubtrees) {
  1071       // If the node is in a shadow tree then the ShadowRoot is the root.
  1072       ShadowRoot* containingShadow = content->GetContainingShadow();
  1073       if (containingShadow) {
  1074         return containingShadow;
  1077       // If the node has a binding parent, that should be the root.
  1078       // XXXbz maybe only for native anonymous content?
  1079       nsINode* root = content->GetBindingParent();
  1080       if (root) {
  1081         return root;
  1086   // Elements etc. must be in document or in document fragment,
  1087   // text nodes in document, in document fragment or in attribute.
  1088   nsINode* root = aNode->GetCurrentDoc();
  1089   if (root) {
  1090     return root;
  1093   root = aNode->SubtreeRoot();
  1095   NS_ASSERTION(!root->IsNodeOfType(nsINode::eDOCUMENT),
  1096                "GetCurrentDoc should have returned a doc");
  1098   // We allow this because of backward compatibility.
  1099   return root;
  1102 void
  1103 nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
  1105  if (!nsContentUtils::CanCallerAccess(&aNode)) {
  1106     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1107     return;
  1110   AutoInvalidateSelection atEndOfBlock(this);
  1111   aRv = SetStart(&aNode, aOffset);
  1114 NS_IMETHODIMP
  1115 nsRange::SetStart(nsIDOMNode* aParent, int32_t aOffset)
  1117   nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
  1118   if (!parent) {
  1119     return NS_ERROR_DOM_NOT_OBJECT_ERR;
  1122   ErrorResult rv;
  1123   SetStart(*parent, aOffset, rv);
  1124   return rv.ErrorCode();
  1127 /* virtual */ nsresult
  1128 nsRange::SetStart(nsINode* aParent, int32_t aOffset)
  1130   nsINode* newRoot = IsValidBoundary(aParent);
  1131   NS_ENSURE_TRUE(newRoot, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
  1133   if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) {
  1134     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1137   // Collapse if not positioned yet, if positioned in another doc or
  1138   // if the new start is after end.
  1139   if (!mIsPositioned || newRoot != mRoot ||
  1140       nsContentUtils::ComparePoints(aParent, aOffset,
  1141                                     mEndParent, mEndOffset) == 1) {
  1142     DoSetRange(aParent, aOffset, aParent, aOffset, newRoot);
  1144     return NS_OK;
  1147   DoSetRange(aParent, aOffset, mEndParent, mEndOffset, mRoot);
  1149   return NS_OK;
  1152 void
  1153 nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv)
  1155   if (!nsContentUtils::CanCallerAccess(&aNode)) {
  1156     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1157     return;
  1160   AutoInvalidateSelection atEndOfBlock(this);
  1161   aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode));
  1164 NS_IMETHODIMP
  1165 nsRange::SetStartBefore(nsIDOMNode* aSibling)
  1167   nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
  1168   if (!sibling) {
  1169     return NS_ERROR_DOM_NOT_OBJECT_ERR;
  1172   ErrorResult rv;
  1173   SetStartBefore(*sibling, rv);
  1174   return rv.ErrorCode();
  1177 void
  1178 nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv)
  1180   if (!nsContentUtils::CanCallerAccess(&aNode)) {
  1181     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1182     return;
  1185   AutoInvalidateSelection atEndOfBlock(this);
  1186   aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode) + 1);
  1189 NS_IMETHODIMP
  1190 nsRange::SetStartAfter(nsIDOMNode* aSibling)
  1192   nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
  1193   if (!sibling) {
  1194     return NS_ERROR_DOM_NOT_OBJECT_ERR;
  1197   ErrorResult rv;
  1198   SetStartAfter(*sibling, rv);
  1199   return rv.ErrorCode();
  1202 void
  1203 nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
  1205  if (!nsContentUtils::CanCallerAccess(&aNode)) {
  1206     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1207     return;
  1209   AutoInvalidateSelection atEndOfBlock(this);
  1210   aRv = SetEnd(&aNode, aOffset);
  1213 NS_IMETHODIMP
  1214 nsRange::SetEnd(nsIDOMNode* aParent, int32_t aOffset)
  1216   nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
  1217   if (!parent) {
  1218     return NS_ERROR_DOM_NOT_OBJECT_ERR;
  1221   ErrorResult rv;
  1222   SetEnd(*parent, aOffset, rv);
  1223   return rv.ErrorCode();
  1226 /* virtual */ nsresult
  1227 nsRange::SetEnd(nsINode* aParent, int32_t aOffset)
  1229   nsINode* newRoot = IsValidBoundary(aParent);
  1230   NS_ENSURE_TRUE(newRoot, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
  1232   if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) {
  1233     return NS_ERROR_DOM_INDEX_SIZE_ERR;
  1236   // Collapse if not positioned yet, if positioned in another doc or
  1237   // if the new end is before start.
  1238   if (!mIsPositioned || newRoot != mRoot ||
  1239       nsContentUtils::ComparePoints(mStartParent, mStartOffset,
  1240                                     aParent, aOffset) == 1) {
  1241     DoSetRange(aParent, aOffset, aParent, aOffset, newRoot);
  1243     return NS_OK;
  1246   DoSetRange(mStartParent, mStartOffset, aParent, aOffset, mRoot);
  1248   return NS_OK;
  1251 void
  1252 nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
  1254   if (!nsContentUtils::CanCallerAccess(&aNode)) {
  1255     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1256     return;
  1259   AutoInvalidateSelection atEndOfBlock(this);
  1260   aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode));
  1263 NS_IMETHODIMP
  1264 nsRange::SetEndBefore(nsIDOMNode* aSibling)
  1266   nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
  1267   if (!sibling) {
  1268     return NS_ERROR_DOM_NOT_OBJECT_ERR;
  1271   ErrorResult rv;
  1272   SetEndBefore(*sibling, rv);
  1273   return rv.ErrorCode();
  1276 void
  1277 nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv)
  1279   if (!nsContentUtils::CanCallerAccess(&aNode)) {
  1280     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1281     return;
  1284   AutoInvalidateSelection atEndOfBlock(this);
  1285   aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode) + 1);
  1288 NS_IMETHODIMP
  1289 nsRange::SetEndAfter(nsIDOMNode* aSibling)
  1291   nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
  1292   if (!sibling) {
  1293     return NS_ERROR_DOM_NOT_OBJECT_ERR;
  1296   ErrorResult rv;
  1297   SetEndAfter(*sibling, rv);
  1298   return rv.ErrorCode();
  1301 NS_IMETHODIMP
  1302 nsRange::Collapse(bool aToStart)
  1304   if (!mIsPositioned)
  1305     return NS_ERROR_NOT_INITIALIZED;
  1307   AutoInvalidateSelection atEndOfBlock(this);
  1308   if (aToStart)
  1309     DoSetRange(mStartParent, mStartOffset, mStartParent, mStartOffset, mRoot);
  1310   else
  1311     DoSetRange(mEndParent, mEndOffset, mEndParent, mEndOffset, mRoot);
  1313   return NS_OK;
  1316 NS_IMETHODIMP
  1317 nsRange::SelectNode(nsIDOMNode* aN)
  1319   nsCOMPtr<nsINode> node = do_QueryInterface(aN);
  1320   NS_ENSURE_TRUE(node, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
  1322   ErrorResult rv;
  1323   SelectNode(*node, rv);
  1324   return rv.ErrorCode();
  1327 void
  1328 nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv)
  1330   if (!nsContentUtils::CanCallerAccess(&aNode)) {
  1331     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1332     return;
  1335   nsINode* parent = aNode.GetParentNode();
  1336   nsINode* newRoot = IsValidBoundary(parent);
  1337   if (!newRoot) {
  1338     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
  1339     return;
  1342   int32_t index = parent->IndexOf(&aNode);
  1343   if (index < 0) {
  1344     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
  1345     return;
  1348   AutoInvalidateSelection atEndOfBlock(this);
  1349   DoSetRange(parent, index, parent, index + 1, newRoot);
  1352 NS_IMETHODIMP
  1353 nsRange::SelectNodeContents(nsIDOMNode* aN)
  1355   nsCOMPtr<nsINode> node = do_QueryInterface(aN);
  1356   NS_ENSURE_TRUE(node, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
  1358   ErrorResult rv;
  1359   SelectNodeContents(*node, rv);
  1360   return rv.ErrorCode();
  1363 void
  1364 nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv)
  1366   if (!nsContentUtils::CanCallerAccess(&aNode)) {
  1367     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1368     return;
  1371   nsINode* newRoot = IsValidBoundary(&aNode);
  1372   if (!newRoot) {
  1373     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
  1374     return;
  1377   AutoInvalidateSelection atEndOfBlock(this);
  1378   DoSetRange(&aNode, 0, &aNode, aNode.Length(), newRoot);
  1381 // The Subtree Content Iterator only returns subtrees that are
  1382 // completely within a given range. It doesn't return a CharacterData
  1383 // node that contains either the start or end point of the range.,
  1384 // nor does it return element nodes when nothing in the element is selected.
  1385 // We need an iterator that will also include these start/end points
  1386 // so that our methods/algorithms aren't cluttered with special
  1387 // case code that tries to include these points while iterating.
  1388 //
  1389 // The RangeSubtreeIterator class mimics the nsIContentIterator
  1390 // methods we need, so should the Content Iterator support the
  1391 // start/end points in the future, we can switchover relatively
  1392 // easy.
  1394 class MOZ_STACK_CLASS RangeSubtreeIterator
  1396 private:
  1398   enum RangeSubtreeIterState { eDone=0,
  1399                                eUseStart,
  1400                                eUseIterator,
  1401                                eUseEnd };
  1403   nsCOMPtr<nsIContentIterator>  mIter;
  1404   RangeSubtreeIterState         mIterState;
  1406   nsCOMPtr<nsINode> mStart;
  1407   nsCOMPtr<nsINode> mEnd;
  1409 public:
  1411   RangeSubtreeIterator()
  1412     : mIterState(eDone)
  1415   ~RangeSubtreeIterator()
  1419   nsresult Init(nsRange *aRange);
  1420   already_AddRefed<nsINode> GetCurrentNode();
  1421   void First();
  1422   void Last();
  1423   void Next();
  1424   void Prev();
  1426   bool IsDone()
  1428     return mIterState == eDone;
  1430 };
  1432 nsresult
  1433 RangeSubtreeIterator::Init(nsRange *aRange)
  1435   mIterState = eDone;
  1436   if (aRange->Collapsed()) {
  1437     return NS_OK;
  1440   // Grab the start point of the range and QI it to
  1441   // a CharacterData pointer. If it is CharacterData store
  1442   // a pointer to the node.
  1444   ErrorResult rv;
  1445   nsCOMPtr<nsINode> node = aRange->GetStartContainer(rv);
  1446   if (!node) return NS_ERROR_FAILURE;
  1448   nsCOMPtr<nsIDOMCharacterData> startData = do_QueryInterface(node);
  1449   if (startData || (node->IsElement() &&
  1450                     node->AsElement()->GetChildCount() == aRange->GetStartOffset(rv))) {
  1451     mStart = node;
  1454   // Grab the end point of the range and QI it to
  1455   // a CharacterData pointer. If it is CharacterData store
  1456   // a pointer to the node.
  1458   node = aRange->GetEndContainer(rv);
  1459   if (!node) return NS_ERROR_FAILURE;
  1461   nsCOMPtr<nsIDOMCharacterData> endData = do_QueryInterface(node);
  1462   if (endData || (node->IsElement() && aRange->GetEndOffset(rv) == 0)) {
  1463     mEnd = node;
  1466   if (mStart && mStart == mEnd)
  1468     // The range starts and stops in the same CharacterData
  1469     // node. Null out the end pointer so we only visit the
  1470     // node once!
  1472     mEnd = nullptr;
  1474   else
  1476     // Now create a Content Subtree Iterator to be used
  1477     // for the subtrees between the end points!
  1479     mIter = NS_NewContentSubtreeIterator();
  1481     nsresult res = mIter->Init(aRange);
  1482     if (NS_FAILED(res)) return res;
  1484     if (mIter->IsDone())
  1486       // The subtree iterator thinks there's nothing
  1487       // to iterate over, so just free it up so we
  1488       // don't accidentally call into it.
  1490       mIter = nullptr;
  1494   // Initialize the iterator by calling First().
  1495   // Note that we are ignoring the return value on purpose!
  1497   First();
  1499   return NS_OK;
  1502 already_AddRefed<nsINode>
  1503 RangeSubtreeIterator::GetCurrentNode()
  1505   nsCOMPtr<nsINode> node;
  1507   if (mIterState == eUseStart && mStart) {
  1508     node = mStart;
  1509   } else if (mIterState == eUseEnd && mEnd) {
  1510     node = mEnd;
  1511   } else if (mIterState == eUseIterator && mIter) {
  1512     node = mIter->GetCurrentNode();
  1515   return node.forget();
  1518 void
  1519 RangeSubtreeIterator::First()
  1521   if (mStart)
  1522     mIterState = eUseStart;
  1523   else if (mIter)
  1525     mIter->First();
  1527     mIterState = eUseIterator;
  1529   else if (mEnd)
  1530     mIterState = eUseEnd;
  1531   else
  1532     mIterState = eDone;
  1535 void
  1536 RangeSubtreeIterator::Last()
  1538   if (mEnd)
  1539     mIterState = eUseEnd;
  1540   else if (mIter)
  1542     mIter->Last();
  1544     mIterState = eUseIterator;
  1546   else if (mStart)
  1547     mIterState = eUseStart;
  1548   else
  1549     mIterState = eDone;
  1552 void
  1553 RangeSubtreeIterator::Next()
  1555   if (mIterState == eUseStart)
  1557     if (mIter)
  1559       mIter->First();
  1561       mIterState = eUseIterator;
  1563     else if (mEnd)
  1564       mIterState = eUseEnd;
  1565     else
  1566       mIterState = eDone;
  1568   else if (mIterState == eUseIterator)
  1570     mIter->Next();
  1572     if (mIter->IsDone())
  1574       if (mEnd)
  1575         mIterState = eUseEnd;
  1576       else
  1577         mIterState = eDone;
  1580   else
  1581     mIterState = eDone;
  1584 void
  1585 RangeSubtreeIterator::Prev()
  1587   if (mIterState == eUseEnd)
  1589     if (mIter)
  1591       mIter->Last();
  1593       mIterState = eUseIterator;
  1595     else if (mStart)
  1596       mIterState = eUseStart;
  1597     else
  1598       mIterState = eDone;
  1600   else if (mIterState == eUseIterator)
  1602     mIter->Prev();
  1604     if (mIter->IsDone())
  1606       if (mStart)
  1607         mIterState = eUseStart;
  1608       else
  1609         mIterState = eDone;
  1612   else
  1613     mIterState = eDone;
  1617 // CollapseRangeAfterDelete() is a utility method that is used by
  1618 // DeleteContents() and ExtractContents() to collapse the range
  1619 // in the correct place, under the range's root container (the
  1620 // range end points common container) as outlined by the Range spec:
  1621 //
  1622 // http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html
  1623 // The assumption made by this method is that the delete or extract
  1624 // has been done already, and left the range in a state where there is
  1625 // no content between the 2 end points.
  1627 static nsresult
  1628 CollapseRangeAfterDelete(nsRange* aRange)
  1630   NS_ENSURE_ARG_POINTER(aRange);
  1632   // Check if range gravity took care of collapsing the range for us!
  1633   if (aRange->Collapsed())
  1635     // aRange is collapsed so there's nothing for us to do.
  1636     //
  1637     // There are 2 possible scenarios here:
  1638     //
  1639     // 1. aRange could've been collapsed prior to the delete/extract,
  1640     //    which would've resulted in nothing being removed, so aRange
  1641     //    is already where it should be.
  1642     //
  1643     // 2. Prior to the delete/extract, aRange's start and end were in
  1644     //    the same container which would mean everything between them
  1645     //    was removed, causing range gravity to collapse the range.
  1647     return NS_OK;
  1650   // aRange isn't collapsed so figure out the appropriate place to collapse!
  1651   // First get both end points and their common ancestor.
  1653   ErrorResult rv;
  1654   nsCOMPtr<nsINode> commonAncestor = aRange->GetCommonAncestorContainer(rv);
  1655   if (rv.Failed()) return rv.ErrorCode();
  1657   nsCOMPtr<nsINode> startContainer = aRange->GetStartContainer(rv);
  1658   if (rv.Failed()) return rv.ErrorCode();
  1659   nsCOMPtr<nsINode> endContainer = aRange->GetEndContainer(rv);
  1660   if (rv.Failed()) return rv.ErrorCode();
  1662   // Collapse to one of the end points if they are already in the
  1663   // commonAncestor. This should work ok since this method is called
  1664   // immediately after a delete or extract that leaves no content
  1665   // between the 2 end points!
  1667   if (startContainer == commonAncestor)
  1668     return aRange->Collapse(true);
  1669   if (endContainer == commonAncestor)
  1670     return aRange->Collapse(false);
  1672   // End points are at differing levels. We want to collapse to the
  1673   // point that is between the 2 subtrees that contain each point,
  1674   // under the common ancestor.
  1676   nsCOMPtr<nsINode> nodeToSelect(startContainer);
  1678   while (nodeToSelect)
  1680     nsCOMPtr<nsINode> parent = nodeToSelect->GetParentNode();
  1681     if (parent == commonAncestor)
  1682       break; // We found the nodeToSelect!
  1684     nodeToSelect = parent;
  1687   if (!nodeToSelect)
  1688     return NS_ERROR_FAILURE; // This should never happen!
  1690   aRange->SelectNode(*nodeToSelect, rv);
  1691   if (rv.Failed()) return rv.ErrorCode();
  1693   return aRange->Collapse(false);
  1696 /**
  1697  * Split a data node into two parts.
  1699  * @param aStartNode          The original node we are trying to split.
  1700  * @param aStartIndex         The index at which to split.
  1701  * @param aEndNode            The second node.
  1702  * @param aCloneAfterOriginal Set false if the original node should be the
  1703  *                            latter one after split.
  1704  */
  1705 static nsresult SplitDataNode(nsIDOMCharacterData* aStartNode,
  1706                               uint32_t aStartIndex,
  1707                               nsIDOMCharacterData** aEndNode,
  1708                               bool aCloneAfterOriginal = true)
  1710   nsresult rv;
  1711   nsCOMPtr<nsINode> node = do_QueryInterface(aStartNode);
  1712   NS_ENSURE_STATE(node && node->IsNodeOfType(nsINode::eDATA_NODE));
  1713   nsGenericDOMDataNode* dataNode = static_cast<nsGenericDOMDataNode*>(node.get());
  1715   nsCOMPtr<nsIContent> newData;
  1716   rv = dataNode->SplitData(aStartIndex, getter_AddRefs(newData),
  1717                            aCloneAfterOriginal);
  1718   NS_ENSURE_SUCCESS(rv, rv);
  1719   return CallQueryInterface(newData, aEndNode);
  1722 NS_IMETHODIMP
  1723 PrependChild(nsINode* aParent, nsINode* aChild)
  1725   nsCOMPtr<nsINode> first = aParent->GetFirstChild();
  1726   ErrorResult rv;
  1727   aParent->InsertBefore(*aChild, first, rv);
  1728   return rv.ErrorCode();
  1731 // Helper function for CutContents, making sure that the current node wasn't
  1732 // removed by mutation events (bug 766426)
  1733 static bool
  1734 ValidateCurrentNode(nsRange* aRange, RangeSubtreeIterator& aIter)
  1736   bool before, after;
  1737   nsCOMPtr<nsINode> node = aIter.GetCurrentNode();
  1738   if (!node) {
  1739     // We don't have to worry that the node was removed if it doesn't exist,
  1740     // e.g., the iterator is done.
  1741     return true;
  1744   nsresult res = nsRange::CompareNodeToRange(node, aRange, &before, &after);
  1746   return NS_SUCCEEDED(res) && !before && !after;
  1749 nsresult
  1750 nsRange::CutContents(DocumentFragment** aFragment)
  1752   if (aFragment) {
  1753     *aFragment = nullptr;
  1756   nsCOMPtr<nsIDocument> doc = mStartParent->OwnerDoc();
  1758   ErrorResult res;
  1759   nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(res);
  1760   NS_ENSURE_SUCCESS(res.ErrorCode(), res.ErrorCode());
  1762   // If aFragment isn't null, create a temporary fragment to hold our return.
  1763   nsRefPtr<DocumentFragment> retval;
  1764   if (aFragment) {
  1765     retval = new DocumentFragment(doc->NodeInfoManager());
  1767   nsCOMPtr<nsINode> commonCloneAncestor = retval.get();
  1769   // Batch possible DOMSubtreeModified events.
  1770   mozAutoSubtreeModified subtree(mRoot ? mRoot->OwnerDoc(): nullptr, nullptr);
  1772   // Save the range end points locally to avoid interference
  1773   // of Range gravity during our edits!
  1775   nsCOMPtr<nsINode> startContainer = mStartParent;
  1776   int32_t              startOffset = mStartOffset;
  1777   nsCOMPtr<nsINode> endContainer = mEndParent;
  1778   int32_t              endOffset = mEndOffset;
  1780   if (retval) {
  1781     // For extractContents(), abort early if there's a doctype (bug 719533).
  1782     // This can happen only if the common ancestor is a document, in which case
  1783     // we just need to find its doctype child and check if that's in the range.
  1784     nsCOMPtr<nsIDocument> commonAncestorDocument = do_QueryInterface(commonAncestor);
  1785     if (commonAncestorDocument) {
  1786       nsRefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype();
  1788       if (doctype &&
  1789           nsContentUtils::ComparePoints(startContainer, startOffset,
  1790                                         doctype, 0) < 0 &&
  1791           nsContentUtils::ComparePoints(doctype, 0,
  1792                                         endContainer, endOffset) < 0) {
  1793         return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
  1798   // Create and initialize a subtree iterator that will give
  1799   // us all the subtrees within the range.
  1801   RangeSubtreeIterator iter;
  1803   nsresult rv = iter.Init(this);
  1804   if (NS_FAILED(rv)) return rv;
  1806   if (iter.IsDone())
  1808     // There's nothing for us to delete.
  1809     rv = CollapseRangeAfterDelete(this);
  1810     if (NS_SUCCEEDED(rv) && aFragment) {
  1811       NS_ADDREF(*aFragment = retval);
  1813     return rv;
  1816   // We delete backwards to avoid iterator problems!
  1818   iter.Last();
  1820   bool handled = false;
  1822   // With the exception of text nodes that contain one of the range
  1823   // end points, the subtree iterator should only give us back subtrees
  1824   // that are completely contained between the range's end points.
  1826   while (!iter.IsDone())
  1828     nsCOMPtr<nsINode> nodeToResult;
  1829     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
  1831     // Before we delete anything, advance the iterator to the
  1832     // next subtree.
  1834     iter.Prev();
  1836     handled = false;
  1838     // If it's CharacterData, make sure we might need to delete
  1839     // part of the data, instead of removing the whole node.
  1840     //
  1841     // XXX_kin: We need to also handle ProcessingInstruction
  1842     // XXX_kin: according to the spec.
  1844     nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(node));
  1846     if (charData)
  1848       uint32_t dataLength = 0;
  1850       if (node == startContainer)
  1852         if (node == endContainer)
  1854           // This range is completely contained within a single text node.
  1855           // Delete or extract the data between startOffset and endOffset.
  1857           if (endOffset > startOffset)
  1859             if (retval) {
  1860               nsAutoString cutValue;
  1861               rv = charData->SubstringData(startOffset, endOffset - startOffset,
  1862                                            cutValue);
  1863               NS_ENSURE_SUCCESS(rv, rv);
  1864               nsCOMPtr<nsIDOMNode> clone;
  1865               rv = charData->CloneNode(false, 1, getter_AddRefs(clone));
  1866               NS_ENSURE_SUCCESS(rv, rv);
  1867               clone->SetNodeValue(cutValue);
  1868               nodeToResult = do_QueryInterface(clone);
  1871             nsMutationGuard guard;
  1872             rv = charData->DeleteData(startOffset, endOffset - startOffset);
  1873             NS_ENSURE_SUCCESS(rv, rv);
  1874             NS_ENSURE_STATE(!guard.Mutated(0) ||
  1875                             ValidateCurrentNode(this, iter));
  1878           handled = true;
  1880         else
  1882           // Delete or extract everything after startOffset.
  1884           rv = charData->GetLength(&dataLength);
  1885           NS_ENSURE_SUCCESS(rv, rv);
  1887           if (dataLength >= (uint32_t)startOffset)
  1889             nsMutationGuard guard;
  1890             nsCOMPtr<nsIDOMCharacterData> cutNode;
  1891             rv = SplitDataNode(charData, startOffset, getter_AddRefs(cutNode));
  1892             NS_ENSURE_SUCCESS(rv, rv);
  1893             NS_ENSURE_STATE(!guard.Mutated(1) ||
  1894                             ValidateCurrentNode(this, iter));
  1895             nodeToResult = do_QueryInterface(cutNode);
  1898           handled = true;
  1901       else if (node == endContainer)
  1903         // Delete or extract everything before endOffset.
  1905         if (endOffset >= 0)
  1907           nsMutationGuard guard;
  1908           nsCOMPtr<nsIDOMCharacterData> cutNode;
  1909           /* The Range spec clearly states clones get cut and original nodes
  1910              remain behind, so use false as the last parameter.
  1911           */
  1912           rv = SplitDataNode(charData, endOffset, getter_AddRefs(cutNode),
  1913                              false);
  1914           NS_ENSURE_SUCCESS(rv, rv);
  1915           NS_ENSURE_STATE(!guard.Mutated(1) ||
  1916                           ValidateCurrentNode(this, iter));
  1917           nodeToResult = do_QueryInterface(cutNode);
  1920         handled = true;
  1924     if (!handled && (node == endContainer || node == startContainer))
  1926       if (node && node->IsElement() &&
  1927           ((node == endContainer && endOffset == 0) ||
  1928            (node == startContainer &&
  1929             int32_t(node->AsElement()->GetChildCount()) == startOffset)))
  1931         if (retval) {
  1932           ErrorResult rv;
  1933           nodeToResult = node->CloneNode(false, rv);
  1934           NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
  1936         handled = true;
  1940     if (!handled)
  1942       // node was not handled above, so it must be completely contained
  1943       // within the range. Just remove it from the tree!
  1944       nodeToResult = node;
  1947     uint32_t parentCount = 0;
  1948     // Set the result to document fragment if we have 'retval'.
  1949     if (retval) {
  1950       nsCOMPtr<nsINode> oldCommonAncestor = commonAncestor;
  1951       if (!iter.IsDone()) {
  1952         // Setup the parameters for the next iteration of the loop.
  1953         nsCOMPtr<nsINode> prevNode = iter.GetCurrentNode();
  1954         NS_ENSURE_STATE(prevNode);
  1956         // Get node's and prevNode's common parent. Do this before moving
  1957         // nodes from original DOM to result fragment.
  1958         commonAncestor = nsContentUtils::GetCommonAncestor(node, prevNode);
  1959         NS_ENSURE_STATE(commonAncestor);
  1961         nsCOMPtr<nsINode> parentCounterNode = node;
  1962         while (parentCounterNode && parentCounterNode != commonAncestor)
  1964           ++parentCount;
  1965           parentCounterNode = parentCounterNode->GetParentNode();
  1966           NS_ENSURE_STATE(parentCounterNode);
  1970       // Clone the parent hierarchy between commonAncestor and node.
  1971       nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
  1972       rv = CloneParentsBetween(oldCommonAncestor, node,
  1973                                getter_AddRefs(closestAncestor),
  1974                                getter_AddRefs(farthestAncestor));
  1975       NS_ENSURE_SUCCESS(rv, rv);
  1977       if (farthestAncestor)
  1979         nsCOMPtr<nsINode> n = do_QueryInterface(commonCloneAncestor);
  1980         rv = PrependChild(n, farthestAncestor);
  1981         NS_ENSURE_SUCCESS(rv, rv);
  1984       nsMutationGuard guard;
  1985       nsCOMPtr<nsINode> parent = nodeToResult->GetParentNode();
  1986       rv = closestAncestor ? PrependChild(closestAncestor, nodeToResult)
  1987                            : PrependChild(commonCloneAncestor, nodeToResult);
  1988       NS_ENSURE_SUCCESS(rv, rv);
  1989       NS_ENSURE_STATE(!guard.Mutated(parent ? 2 : 1) ||
  1990                       ValidateCurrentNode(this, iter));
  1991     } else if (nodeToResult) {
  1992       nsMutationGuard guard;
  1993       nsCOMPtr<nsINode> node = nodeToResult;
  1994       nsINode* parent = node->GetParentNode();
  1995       if (parent) {
  1996         mozilla::ErrorResult error;
  1997         parent->RemoveChild(*node, error);
  1998         NS_ENSURE_FALSE(error.Failed(), error.ErrorCode());
  2000       NS_ENSURE_STATE(!guard.Mutated(1) ||
  2001                       ValidateCurrentNode(this, iter));
  2004     if (!iter.IsDone() && retval) {
  2005       // Find the equivalent of commonAncestor in the cloned tree.
  2006       nsCOMPtr<nsINode> newCloneAncestor = nodeToResult;
  2007       for (uint32_t i = parentCount; i; --i)
  2009         newCloneAncestor = newCloneAncestor->GetParentNode();
  2010         NS_ENSURE_STATE(newCloneAncestor);
  2012       commonCloneAncestor = newCloneAncestor;
  2016   rv = CollapseRangeAfterDelete(this);
  2017   if (NS_SUCCEEDED(rv) && aFragment) {
  2018     NS_ADDREF(*aFragment = retval);
  2020   return rv;
  2023 NS_IMETHODIMP
  2024 nsRange::DeleteContents()
  2026   return CutContents(nullptr);
  2029 void
  2030 nsRange::DeleteContents(ErrorResult& aRv)
  2032   aRv = CutContents(nullptr);
  2035 NS_IMETHODIMP
  2036 nsRange::ExtractContents(nsIDOMDocumentFragment** aReturn)
  2038   NS_ENSURE_ARG_POINTER(aReturn);
  2039   nsRefPtr<DocumentFragment> fragment;
  2040   nsresult rv = CutContents(getter_AddRefs(fragment));
  2041   fragment.forget(aReturn);
  2042   return rv;
  2045 already_AddRefed<DocumentFragment>
  2046 nsRange::ExtractContents(ErrorResult& rv)
  2048   nsRefPtr<DocumentFragment> fragment;
  2049   rv = CutContents(getter_AddRefs(fragment));
  2050   return fragment.forget();
  2053 NS_IMETHODIMP
  2054 nsRange::CompareBoundaryPoints(uint16_t aHow, nsIDOMRange* aOtherRange,
  2055                                int16_t* aCmpRet)
  2057   nsRange* otherRange = static_cast<nsRange*>(aOtherRange);
  2058   NS_ENSURE_TRUE(otherRange, NS_ERROR_NULL_POINTER);
  2060   ErrorResult rv;
  2061   *aCmpRet = CompareBoundaryPoints(aHow, *otherRange, rv);
  2062   return rv.ErrorCode();
  2065 int16_t
  2066 nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange,
  2067                                ErrorResult& rv)
  2069   if (!mIsPositioned || !aOtherRange.IsPositioned()) {
  2070     rv.Throw(NS_ERROR_NOT_INITIALIZED);
  2071     return 0;
  2074   nsINode *ourNode, *otherNode;
  2075   int32_t ourOffset, otherOffset;
  2077   switch (aHow) {
  2078     case nsIDOMRange::START_TO_START:
  2079       ourNode = mStartParent;
  2080       ourOffset = mStartOffset;
  2081       otherNode = aOtherRange.GetStartParent();
  2082       otherOffset = aOtherRange.StartOffset();
  2083       break;
  2084     case nsIDOMRange::START_TO_END:
  2085       ourNode = mEndParent;
  2086       ourOffset = mEndOffset;
  2087       otherNode = aOtherRange.GetStartParent();
  2088       otherOffset = aOtherRange.StartOffset();
  2089       break;
  2090     case nsIDOMRange::END_TO_START:
  2091       ourNode = mStartParent;
  2092       ourOffset = mStartOffset;
  2093       otherNode = aOtherRange.GetEndParent();
  2094       otherOffset = aOtherRange.EndOffset();
  2095       break;
  2096     case nsIDOMRange::END_TO_END:
  2097       ourNode = mEndParent;
  2098       ourOffset = mEndOffset;
  2099       otherNode = aOtherRange.GetEndParent();
  2100       otherOffset = aOtherRange.EndOffset();
  2101       break;
  2102     default:
  2103       // We were passed an illegal value
  2104       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  2105       return 0;
  2108   if (mRoot != aOtherRange.GetRoot()) {
  2109     rv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
  2110     return 0;
  2113   return nsContentUtils::ComparePoints(ourNode, ourOffset,
  2114                                        otherNode, otherOffset);
  2117 /* static */ nsresult
  2118 nsRange::CloneParentsBetween(nsINode *aAncestor,
  2119                              nsINode *aNode,
  2120                              nsINode **aClosestAncestor,
  2121                              nsINode **aFarthestAncestor)
  2123   NS_ENSURE_ARG_POINTER((aAncestor && aNode && aClosestAncestor && aFarthestAncestor));
  2125   *aClosestAncestor  = nullptr;
  2126   *aFarthestAncestor = nullptr;
  2128   if (aAncestor == aNode)
  2129     return NS_OK;
  2131   nsCOMPtr<nsINode> firstParent, lastParent;
  2132   nsCOMPtr<nsINode> parent = aNode->GetParentNode();
  2134   while(parent && parent != aAncestor)
  2136     ErrorResult rv;
  2137     nsCOMPtr<nsINode> clone = parent->CloneNode(false, rv);
  2139     if (rv.Failed()) {
  2140       return rv.ErrorCode();
  2142     if (!clone) {
  2143       return NS_ERROR_FAILURE;
  2146     if (! firstParent) {
  2147       firstParent = lastParent = clone;
  2148     } else {
  2149       clone->AppendChild(*lastParent, rv);
  2150       if (rv.Failed()) return rv.ErrorCode();
  2152       lastParent = clone;
  2155     parent = parent->GetParentNode();
  2158   *aClosestAncestor  = firstParent;
  2159   NS_IF_ADDREF(*aClosestAncestor);
  2161   *aFarthestAncestor = lastParent;
  2162   NS_IF_ADDREF(*aFarthestAncestor);
  2164   return NS_OK;
  2167 NS_IMETHODIMP
  2168 nsRange::CloneContents(nsIDOMDocumentFragment** aReturn)
  2170   ErrorResult rv;
  2171   *aReturn = CloneContents(rv).take();
  2172   return rv.ErrorCode();
  2175 already_AddRefed<DocumentFragment>
  2176 nsRange::CloneContents(ErrorResult& aRv)
  2178   nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv);
  2179   MOZ_ASSERT(!aRv.Failed(), "GetCommonAncestorContainer() shouldn't fail!");
  2181   nsCOMPtr<nsIDocument> doc = mStartParent->OwnerDoc();
  2182   NS_ASSERTION(doc, "CloneContents needs a document to continue.");
  2183   if (!doc) {
  2184     aRv.Throw(NS_ERROR_FAILURE);
  2185     return nullptr;
  2188   // Create a new document fragment in the context of this document,
  2189   // which might be null
  2192   nsRefPtr<DocumentFragment> clonedFrag =
  2193     new DocumentFragment(doc->NodeInfoManager());
  2195   nsCOMPtr<nsINode> commonCloneAncestor = clonedFrag.get();
  2197   // Create and initialize a subtree iterator that will give
  2198   // us all the subtrees within the range.
  2200   RangeSubtreeIterator iter;
  2202   aRv = iter.Init(this);
  2203   if (aRv.Failed()) {
  2204     return nullptr;
  2207   if (iter.IsDone())
  2209     // There's nothing to add to the doc frag, we must be done!
  2210     return clonedFrag.forget();
  2213   iter.First();
  2215   // With the exception of text nodes that contain one of the range
  2216   // end points and elements which don't have any content selected the subtree
  2217   // iterator should only give us back subtrees that are completely contained
  2218   // between the range's end points.
  2219   //
  2220   // Unfortunately these subtrees don't contain the parent hierarchy/context
  2221   // that the Range spec requires us to return. This loop clones the
  2222   // parent hierarchy, adds a cloned version of the subtree, to it, then
  2223   // correctly places this new subtree into the doc fragment.
  2225   while (!iter.IsDone())
  2227     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
  2228     bool deepClone = !node->IsElement() ||
  2229                        (!(node == mEndParent && mEndOffset == 0) &&
  2230                         !(node == mStartParent &&
  2231                           mStartOffset ==
  2232                             int32_t(node->AsElement()->GetChildCount())));
  2234     // Clone the current subtree!
  2236     nsCOMPtr<nsINode> clone = node->CloneNode(deepClone, aRv);
  2237     if (aRv.Failed()) {
  2238       return nullptr;
  2241     // If it's CharacterData, make sure we only clone what
  2242     // is in the range.
  2243     //
  2244     // XXX_kin: We need to also handle ProcessingInstruction
  2245     // XXX_kin: according to the spec.
  2247     nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(clone));
  2249     if (charData)
  2251       if (node == mEndParent)
  2253         // We only need the data before mEndOffset, so get rid of any
  2254         // data after it.
  2256         uint32_t dataLength = 0;
  2257         aRv = charData->GetLength(&dataLength);
  2258         if (aRv.Failed()) {
  2259           return nullptr;
  2262         if (dataLength > (uint32_t)mEndOffset)
  2264           aRv = charData->DeleteData(mEndOffset, dataLength - mEndOffset);
  2265           if (aRv.Failed()) {
  2266             return nullptr;
  2271       if (node == mStartParent)
  2273         // We don't need any data before mStartOffset, so just
  2274         // delete it!
  2276         if (mStartOffset > 0)
  2278           aRv = charData->DeleteData(0, mStartOffset);
  2279           if (aRv.Failed()) {
  2280             return nullptr;
  2286     // Clone the parent hierarchy between commonAncestor and node.
  2288     nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
  2290     aRv = CloneParentsBetween(commonAncestor, node,
  2291                               getter_AddRefs(closestAncestor),
  2292                               getter_AddRefs(farthestAncestor));
  2294     if (aRv.Failed()) {
  2295       return nullptr;
  2298     // Hook the parent hierarchy/context of the subtree into the clone tree.
  2300     if (farthestAncestor)
  2302       commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
  2304       if (aRv.Failed()) {
  2305         return nullptr;
  2309     // Place the cloned subtree into the cloned doc frag tree!
  2311     nsCOMPtr<nsINode> cloneNode = do_QueryInterface(clone);
  2312     if (closestAncestor)
  2314       // Append the subtree under closestAncestor since it is the
  2315       // immediate parent of the subtree.
  2317       closestAncestor->AppendChild(*cloneNode, aRv);
  2319     else
  2321       // If we get here, there is no missing parent hierarchy between
  2322       // commonAncestor and node, so just append clone to commonCloneAncestor.
  2324       commonCloneAncestor->AppendChild(*cloneNode, aRv);
  2326     if (aRv.Failed()) {
  2327       return nullptr;
  2330     // Get the next subtree to be processed. The idea here is to setup
  2331     // the parameters for the next iteration of the loop.
  2333     iter.Next();
  2335     if (iter.IsDone())
  2336       break; // We must be done!
  2338     nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
  2339     if (!nextNode) {
  2340       aRv.Throw(NS_ERROR_FAILURE);
  2341       return nullptr;
  2344     // Get node and nextNode's common parent.
  2345     commonAncestor = nsContentUtils::GetCommonAncestor(node, nextNode);
  2347     if (!commonAncestor) {
  2348       aRv.Throw(NS_ERROR_FAILURE);
  2349       return nullptr;
  2352     // Find the equivalent of commonAncestor in the cloned tree!
  2354     while (node && node != commonAncestor)
  2356       node = node->GetParentNode();
  2357       if (aRv.Failed()) {
  2358         return nullptr;
  2361       if (!node) {
  2362         aRv.Throw(NS_ERROR_FAILURE);
  2363         return nullptr;
  2366       cloneNode = cloneNode->GetParentNode();
  2367       if (!cloneNode) {
  2368         aRv.Throw(NS_ERROR_FAILURE);
  2369         return nullptr;
  2373     commonCloneAncestor = cloneNode;
  2376   return clonedFrag.forget();
  2379 already_AddRefed<nsRange>
  2380 nsRange::CloneRange() const
  2382   nsRefPtr<nsRange> range = new nsRange(mOwner);
  2384   range->SetMaySpanAnonymousSubtrees(mMaySpanAnonymousSubtrees);
  2386   range->DoSetRange(mStartParent, mStartOffset, mEndParent, mEndOffset, mRoot);
  2388   return range.forget();
  2391 NS_IMETHODIMP
  2392 nsRange::CloneRange(nsIDOMRange** aReturn)
  2394   *aReturn = CloneRange().take();
  2395   return NS_OK;
  2398 NS_IMETHODIMP
  2399 nsRange::InsertNode(nsIDOMNode* aNode)
  2401   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  2402   if (!node) {
  2403     return NS_ERROR_DOM_NOT_OBJECT_ERR;
  2406   ErrorResult rv;
  2407   InsertNode(*node, rv);
  2408   return rv.ErrorCode();
  2411 void
  2412 nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv)
  2414   if (!nsContentUtils::CanCallerAccess(&aNode)) {
  2415     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  2416     return;
  2419   int32_t tStartOffset = StartOffset();
  2421   nsCOMPtr<nsINode> tStartContainer = GetStartContainer(aRv);
  2422   if (aRv.Failed()) {
  2423     return;
  2426   // This is the node we'll be inserting before, and its parent
  2427   nsCOMPtr<nsINode> referenceNode;
  2428   nsCOMPtr<nsINode> referenceParentNode = tStartContainer;
  2430   nsCOMPtr<nsIDOMText> startTextNode(do_QueryInterface(tStartContainer));
  2431   nsCOMPtr<nsIDOMNodeList> tChildList;
  2432   if (startTextNode) {
  2433     referenceParentNode = tStartContainer->GetParentNode();
  2434     if (!referenceParentNode) {
  2435       aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  2436       return;
  2439     nsCOMPtr<nsIDOMText> secondPart;
  2440     aRv = startTextNode->SplitText(tStartOffset, getter_AddRefs(secondPart));
  2441     if (aRv.Failed()) {
  2442       return;
  2445     referenceNode = do_QueryInterface(secondPart);
  2446   } else {
  2447     aRv = tStartContainer->AsDOMNode()->GetChildNodes(getter_AddRefs(tChildList));
  2448     if (aRv.Failed()) {
  2449       return;
  2452     // find the insertion point in the DOM and insert the Node
  2453     nsCOMPtr<nsIDOMNode> q;
  2454     aRv = tChildList->Item(tStartOffset, getter_AddRefs(q));
  2455     referenceNode = do_QueryInterface(q);
  2456     if (aRv.Failed()) {
  2457       return;
  2461   // We might need to update the end to include the new node (bug 433662).
  2462   // Ideally we'd only do this if needed, but it's tricky to know when it's
  2463   // needed in advance (bug 765799).
  2464   int32_t newOffset;
  2466   if (referenceNode) {
  2467     newOffset = IndexOf(referenceNode);
  2468   } else {
  2469     uint32_t length;
  2470     aRv = tChildList->GetLength(&length);
  2471     if (aRv.Failed()) {
  2472       return;
  2475     newOffset = length;
  2478   if (aNode.NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
  2479     newOffset += aNode.GetChildCount();
  2480   } else {
  2481     newOffset++;
  2484   // Now actually insert the node
  2485   nsCOMPtr<nsINode> tResultNode;
  2486   tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv);
  2487   if (aRv.Failed()) {
  2488     return;
  2491   if (Collapsed()) {
  2492     aRv = SetEnd(referenceParentNode, newOffset);
  2496 NS_IMETHODIMP
  2497 nsRange::SurroundContents(nsIDOMNode* aNewParent)
  2499   nsCOMPtr<nsINode> node = do_QueryInterface(aNewParent);
  2500   if (!node) {
  2501     return NS_ERROR_DOM_NOT_OBJECT_ERR;
  2503   ErrorResult rv;
  2504   SurroundContents(*node, rv);
  2505   return rv.ErrorCode();
  2508 void
  2509 nsRange::SurroundContents(nsINode& aNewParent, ErrorResult& aRv)
  2511   if (!nsContentUtils::CanCallerAccess(&aNewParent)) {
  2512     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  2513     return;
  2516   if (!mRoot) {
  2517     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2518     return;
  2520   // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text
  2521   // node.
  2522   if (mStartParent != mEndParent) {
  2523     bool startIsText = mStartParent->IsNodeOfType(nsINode::eTEXT);
  2524     bool endIsText = mEndParent->IsNodeOfType(nsINode::eTEXT);
  2525     nsINode* startGrandParent = mStartParent->GetParentNode();
  2526     nsINode* endGrandParent = mEndParent->GetParentNode();
  2527     if (!((startIsText && endIsText &&
  2528            startGrandParent &&
  2529            startGrandParent == endGrandParent) ||
  2530           (startIsText &&
  2531            startGrandParent &&
  2532            startGrandParent == mEndParent) ||
  2533           (endIsText &&
  2534            endGrandParent &&
  2535            endGrandParent == mStartParent))) {
  2536       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2537       return;
  2541   // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted
  2542   // (Document, DocumentType, DocumentFragment)
  2543   uint16_t nodeType = aNewParent.NodeType();
  2544   if (nodeType == nsIDOMNode::DOCUMENT_NODE ||
  2545       nodeType == nsIDOMNode::DOCUMENT_TYPE_NODE ||
  2546       nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
  2547     aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
  2548     return;
  2551   // Extract the contents within the range.
  2553   nsRefPtr<DocumentFragment> docFrag = ExtractContents(aRv);
  2555   if (aRv.Failed()) {
  2556     return;
  2559   if (!docFrag) {
  2560     aRv.Throw(NS_ERROR_FAILURE);
  2561     return;
  2564   // Spec says we need to remove all of aNewParent's
  2565   // children prior to insertion.
  2567   nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes();
  2568   if (!children) {
  2569     aRv.Throw(NS_ERROR_FAILURE);
  2570     return;
  2573   uint32_t numChildren = children->Length();
  2575   while (numChildren)
  2577     nsCOMPtr<nsINode> child = children->Item(--numChildren);
  2578     if (!child) {
  2579       aRv.Throw(NS_ERROR_FAILURE);
  2580       return;
  2583     aNewParent.RemoveChild(*child, aRv);
  2584     if (aRv.Failed()) {
  2585       return;
  2589   // Insert aNewParent at the range's start point.
  2591   InsertNode(aNewParent, aRv);
  2592   if (aRv.Failed()) {
  2593     return;
  2596   // Append the content we extracted under aNewParent.
  2597   aNewParent.AppendChild(*docFrag, aRv);
  2598   if (aRv.Failed()) {
  2599     return;
  2602   // Select aNewParent, and its contents.
  2604   SelectNode(aNewParent, aRv);
  2607 NS_IMETHODIMP
  2608 nsRange::ToString(nsAString& aReturn)
  2610   // clear the string
  2611   aReturn.Truncate();
  2613   // If we're unpositioned, return the empty string
  2614   if (!mIsPositioned) {
  2615     return NS_OK;
  2618 #ifdef DEBUG_range
  2619       printf("Range dump: -----------------------\n");
  2620 #endif /* DEBUG */
  2622   // effeciency hack for simple case
  2623   if (mStartParent == mEndParent)
  2625     nsCOMPtr<nsIDOMText> textNode( do_QueryInterface(mStartParent) );
  2627     if (textNode)
  2629 #ifdef DEBUG_range
  2630       // If debug, dump it:
  2631       nsCOMPtr<nsIContent> cN (do_QueryInterface(mStartParent));
  2632       if (cN) cN->List(stdout);
  2633       printf("End Range dump: -----------------------\n");
  2634 #endif /* DEBUG */
  2636       // grab the text
  2637       if (NS_FAILED(textNode->SubstringData(mStartOffset,mEndOffset-mStartOffset,aReturn)))
  2638         return NS_ERROR_UNEXPECTED;
  2639       return NS_OK;
  2643   /* complex case: mStartParent != mEndParent, or mStartParent not a text node
  2644      revisit - there are potential optimizations here and also tradeoffs.
  2645   */
  2647   nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
  2648   nsresult rv = iter->Init(this);
  2649   NS_ENSURE_SUCCESS(rv, rv);
  2651   nsString tempString;
  2653   // loop through the content iterator, which returns nodes in the range in 
  2654   // close tag order, and grab the text from any text node
  2655   while (!iter->IsDone())
  2657     nsINode *n = iter->GetCurrentNode();
  2659 #ifdef DEBUG_range
  2660     // If debug, dump it:
  2661     n->List(stdout);
  2662 #endif /* DEBUG */
  2663     nsCOMPtr<nsIDOMText> textNode(do_QueryInterface(n));
  2664     if (textNode) // if it's a text node, get the text
  2666       if (n == mStartParent) // only include text past start offset
  2668         uint32_t strLength;
  2669         textNode->GetLength(&strLength);
  2670         textNode->SubstringData(mStartOffset,strLength-mStartOffset,tempString);
  2671         aReturn += tempString;
  2673       else if (n == mEndParent)  // only include text before end offset
  2675         textNode->SubstringData(0,mEndOffset,tempString);
  2676         aReturn += tempString;
  2678       else  // grab the whole kit-n-kaboodle
  2680         textNode->GetData(tempString);
  2681         aReturn += tempString;
  2685     iter->Next();
  2688 #ifdef DEBUG_range
  2689   printf("End Range dump: -----------------------\n");
  2690 #endif /* DEBUG */
  2691   return NS_OK;
  2696 NS_IMETHODIMP
  2697 nsRange::Detach()
  2699   // No-op, but still set mIsDetached for telemetry (bug 702948)
  2700   mIsDetached = true;
  2701   return NS_OK;
  2704 NS_IMETHODIMP
  2705 nsRange::CreateContextualFragment(const nsAString& aFragment,
  2706                                   nsIDOMDocumentFragment** aReturn)
  2708   if (mIsPositioned) {
  2709     return nsContentUtils::CreateContextualFragment(mStartParent, aFragment,
  2710                                                     false, aReturn);
  2712   return NS_ERROR_FAILURE;
  2715 already_AddRefed<DocumentFragment>
  2716 nsRange::CreateContextualFragment(const nsAString& aFragment, ErrorResult& aRv)
  2718   if (!mIsPositioned) {
  2719     aRv.Throw(NS_ERROR_FAILURE);
  2720     return nullptr;
  2723   return nsContentUtils::CreateContextualFragment(mStartParent, aFragment,
  2724                                                   false, aRv);
  2727 static void ExtractRectFromOffset(nsIFrame* aFrame,
  2728                                   const nsIFrame* aRelativeTo, 
  2729                                   const int32_t aOffset, nsRect* aR, bool aKeepLeft)
  2731   nsPoint point;
  2732   aFrame->GetPointFromOffset(aOffset, &point);
  2734   point += aFrame->GetOffsetTo(aRelativeTo);
  2736   //given a point.x, extract left or right portion of rect aR
  2737   //point.x has to be within this rect
  2738   NS_ASSERTION(aR->x <= point.x && point.x <= aR->XMost(),
  2739                    "point.x should not be outside of rect r");
  2741   if (aKeepLeft) {
  2742     aR->width = point.x - aR->x;
  2743   } else {
  2744     aR->width = aR->XMost() - point.x;
  2745     aR->x = point.x;
  2749 static nsTextFrame*
  2750 GetTextFrameForContent(nsIContent* aContent)
  2752   nsIPresShell* presShell = aContent->OwnerDoc()->GetShell();
  2753   if (presShell) {
  2754     presShell->FrameConstructor()->EnsureFrameForTextNode(
  2755         static_cast<nsGenericDOMDataNode*>(aContent));
  2756     aContent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
  2757     nsIFrame* frame = aContent->GetPrimaryFrame();
  2758     if (frame && frame->GetType() == nsGkAtoms::textFrame) {
  2759       return static_cast<nsTextFrame*>(frame);
  2762   return nullptr;
  2765 static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
  2766                                    nsIContent* aContent, int32_t aStartOffset, int32_t aEndOffset)
  2768   nsTextFrame* textFrame = GetTextFrameForContent(aContent);
  2769   if (textFrame) {
  2770     nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
  2771     for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
  2772       int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
  2773       if (fend <= aStartOffset || fstart >= aEndOffset)
  2774         continue;
  2776       // overlapping with the offset we want
  2777       f->EnsureTextRun(nsTextFrame::eInflated);
  2778       NS_ENSURE_TRUE(f->GetTextRun(nsTextFrame::eInflated), NS_ERROR_OUT_OF_MEMORY);
  2779       bool rtl = f->GetTextRun(nsTextFrame::eInflated)->IsRightToLeft();
  2780       nsRect r(f->GetOffsetTo(relativeTo), f->GetSize());
  2781       if (fstart < aStartOffset) {
  2782         // aStartOffset is within this frame
  2783         ExtractRectFromOffset(f, relativeTo, aStartOffset, &r, rtl);
  2785       if (fend > aEndOffset) {
  2786         // aEndOffset is in the middle of this frame
  2787         ExtractRectFromOffset(f, relativeTo, aEndOffset, &r, !rtl);
  2789       aCallback->AddRect(r);
  2792   return NS_OK;
  2795 static void CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
  2796                                nsRange* aRange,
  2797                                nsINode* aStartParent, int32_t aStartOffset,
  2798                                nsINode* aEndParent, int32_t aEndOffset)
  2800   // Hold strong pointers across the flush
  2801   nsCOMPtr<nsINode> startContainer = aStartParent;
  2802   nsCOMPtr<nsINode> endContainer = aEndParent;
  2804   // Flush out layout so our frames are up to date.
  2805   if (!aStartParent->IsInDoc()) {
  2806     return;
  2809   aStartParent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
  2811   // Recheck whether we're still in the document
  2812   if (!aStartParent->IsInDoc()) {
  2813     return;
  2816   RangeSubtreeIterator iter;
  2818   nsresult rv = iter.Init(aRange);
  2819   if (NS_FAILED(rv)) return;
  2821   if (iter.IsDone()) {
  2822     // the range is collapsed, only continue if the cursor is in a text node
  2823     nsCOMPtr<nsIContent> content = do_QueryInterface(aStartParent);
  2824     if (content && content->IsNodeOfType(nsINode::eTEXT)) {
  2825       nsTextFrame* textFrame = GetTextFrameForContent(content);
  2826       if (textFrame) {
  2827         int32_t outOffset;
  2828         nsIFrame* outFrame;
  2829         textFrame->GetChildFrameContainingOffset(aStartOffset, false, 
  2830           &outOffset, &outFrame);
  2831         if (outFrame) {
  2832            nsIFrame* relativeTo =
  2833              nsLayoutUtils::GetContainingBlockForClientRect(outFrame);
  2834            nsRect r(outFrame->GetOffsetTo(relativeTo), outFrame->GetSize());
  2835            ExtractRectFromOffset(outFrame, relativeTo, aStartOffset, &r, false);
  2836            r.width = 0;
  2837            aCollector->AddRect(r);
  2841     return;
  2844   do {
  2845     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
  2846     iter.Next();
  2847     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
  2848     if (!content)
  2849       continue;
  2850     if (content->IsNodeOfType(nsINode::eTEXT)) {
  2851        if (node == startContainer) {
  2852          int32_t offset = startContainer == endContainer ?
  2853            aEndOffset : content->GetText()->GetLength();
  2854          GetPartialTextRect(aCollector, content, aStartOffset, offset);
  2855          continue;
  2856        } else if (node == endContainer) {
  2857          GetPartialTextRect(aCollector, content, 0, aEndOffset);
  2858          continue;
  2862     nsIFrame* frame = content->GetPrimaryFrame();
  2863     if (frame) {
  2864       nsLayoutUtils::GetAllInFlowRects(frame,
  2865         nsLayoutUtils::GetContainingBlockForClientRect(frame), aCollector);
  2867   } while (!iter.IsDone());
  2870 NS_IMETHODIMP
  2871 nsRange::GetBoundingClientRect(nsIDOMClientRect** aResult)
  2873   *aResult = GetBoundingClientRect().take();
  2874   return NS_OK;
  2877 already_AddRefed<DOMRect>
  2878 nsRange::GetBoundingClientRect()
  2880   nsRefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
  2881   if (!mStartParent) {
  2882     return rect.forget();
  2885   nsLayoutUtils::RectAccumulator accumulator;
  2886   CollectClientRects(&accumulator, this, mStartParent, mStartOffset, 
  2887     mEndParent, mEndOffset);
  2889   nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect : 
  2890     accumulator.mResultRect;
  2891   rect->SetLayoutRect(r);
  2892   return rect.forget();
  2895 NS_IMETHODIMP
  2896 nsRange::GetClientRects(nsIDOMClientRectList** aResult)
  2898   *aResult = GetClientRects().take();
  2899   return NS_OK;
  2902 already_AddRefed<DOMRectList>
  2903 nsRange::GetClientRects()
  2905   if (!mStartParent) {
  2906     return nullptr;
  2909   nsRefPtr<DOMRectList> rectList =
  2910     new DOMRectList(static_cast<nsIDOMRange*>(this));
  2912   nsLayoutUtils::RectListBuilder builder(rectList);
  2914   CollectClientRects(&builder, this, mStartParent, mStartOffset, 
  2915     mEndParent, mEndOffset);
  2916   return rectList.forget();
  2919 NS_IMETHODIMP
  2920 nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult)
  2922   *aResult = nullptr;
  2924   NS_ENSURE_TRUE(mStartParent, NS_ERROR_UNEXPECTED);
  2926   nsCOMPtr<nsINode> startContainer = do_QueryInterface(mStartParent);
  2927   nsCOMPtr<nsINode> endContainer = do_QueryInterface(mEndParent);
  2929   // Flush out layout so our frames are up to date.
  2930   nsIDocument* doc = mStartParent->OwnerDoc();
  2931   NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
  2932   doc->FlushPendingNotifications(Flush_Frames);
  2934   // Recheck whether we're still in the document
  2935   NS_ENSURE_TRUE(mStartParent->IsInDoc(), NS_ERROR_UNEXPECTED);
  2937   nsRefPtr<nsFontFaceList> fontFaceList = new nsFontFaceList();
  2939   RangeSubtreeIterator iter;
  2940   nsresult rv = iter.Init(this);
  2941   NS_ENSURE_SUCCESS(rv, rv);
  2943   while (!iter.IsDone()) {
  2944     // only collect anything if the range is not collapsed
  2945     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
  2946     iter.Next();
  2948     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
  2949     if (!content) {
  2950       continue;
  2952     nsIFrame* frame = content->GetPrimaryFrame();
  2953     if (!frame) {
  2954       continue;
  2957     if (content->IsNodeOfType(nsINode::eTEXT)) {
  2958        if (node == startContainer) {
  2959          int32_t offset = startContainer == endContainer ?
  2960            mEndOffset : content->GetText()->GetLength();
  2961          nsLayoutUtils::GetFontFacesForText(frame, mStartOffset, offset,
  2962                                             true, fontFaceList);
  2963          continue;
  2965        if (node == endContainer) {
  2966          nsLayoutUtils::GetFontFacesForText(frame, 0, mEndOffset,
  2967                                             true, fontFaceList);
  2968          continue;
  2972     nsLayoutUtils::GetFontFacesForFrames(frame, fontFaceList);
  2975   fontFaceList.forget(aResult);
  2976   return NS_OK;
  2979 nsINode*
  2980 nsRange::GetRegisteredCommonAncestor()
  2982   NS_ASSERTION(IsInSelection(),
  2983                "GetRegisteredCommonAncestor only valid for range in selection");
  2984   nsINode* ancestor = GetNextRangeCommonAncestor(mStartParent);
  2985   while (ancestor) {
  2986     RangeHashTable* ranges =
  2987       static_cast<RangeHashTable*>(ancestor->GetProperty(nsGkAtoms::range));
  2988     if (ranges->GetEntry(this)) {
  2989       break;
  2991     ancestor = GetNextRangeCommonAncestor(ancestor->GetParentNode());
  2993   NS_ASSERTION(ancestor, "can't find common ancestor for selected range");
  2994   return ancestor;
  2997 /* static */ bool nsRange::AutoInvalidateSelection::mIsNested;
  2999 nsRange::AutoInvalidateSelection::~AutoInvalidateSelection()
  3001   NS_ASSERTION(mWasInSelection == mRange->IsInSelection(),
  3002                "Range got unselected in AutoInvalidateSelection block");
  3003   if (!mCommonAncestor) {
  3004     return;
  3006   mIsNested = false;
  3007   ::InvalidateAllFrames(mCommonAncestor);
  3008   nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor();
  3009   if (commonAncestor != mCommonAncestor) {
  3010     ::InvalidateAllFrames(commonAncestor);
  3014 /* static */ already_AddRefed<nsRange>
  3015 nsRange::Constructor(const GlobalObject& aGlobal,
  3016                      ErrorResult& aRv)
  3018   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
  3019   if (!window || !window->GetDoc()) {
  3020     aRv.Throw(NS_ERROR_FAILURE);
  3021     return nullptr;
  3024   return window->GetDoc()->CreateRange(aRv);

mercurial