Bug 1384915 - Part 4: Update RawRange to use RangeBoundaries, r=masayuki
authorMichael Layzell <michael@thelayzells.com>
Fri, 08 Sep 2017 14:07:00 -0400
changeset 436104 97ac8133fe794afa8181ede2e23eb1bd8935898f
parent 436103 785d03188e75a278064da1e40a4d8bb05353baaf
child 436105 b4c0c90d80783f46ef315bb070b640ce3c8416bb
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1384915
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1384915 - Part 4: Update RawRange to use RangeBoundaries, r=masayuki
dom/base/nsRange.h
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -80,21 +80,31 @@ public:
   // nsIDOMRange interface
   NS_DECL_NSIDOMRANGE
 
   nsINode* GetRoot() const
   {
     return mRoot;
   }
 
+  const RangeBoundary& StartRef() const
+  {
+    return mStart;
+  }
+
   nsINode* GetStartContainer() const
   {
     return mStart.Container();
   }
 
+  const RangeBoundary& EndRef() const
+  {
+    return mEnd;
+  }
+
   nsINode* GetEndContainer() const
   {
     return mEnd.Container();
   }
 
   uint32_t StartOffset() const
   {
     return static_cast<uint32_t>(mStart.Offset());
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -42,166 +42,145 @@ using namespace widget;
 /******************************************************************/
 /* ContentEventHandler::RawRange                                  */
 /******************************************************************/
 
 void
 ContentEventHandler::RawRange::AssertStartIsBeforeOrEqualToEnd()
 {
   MOZ_ASSERT(
-    nsContentUtils::ComparePoints(mStartContainer,
-                                  static_cast<int32_t>(mStartOffset),
-                                  mEndContainer,
-                                  static_cast<int32_t>(mEndOffset)) <= 0);
-}
-
-bool
-ContentEventHandler::RawRange::IsValidOffset(nsINode* aContainer,
-                                             uint32_t aOffset) const
-{
-  return aContainer && aOffset <= aContainer->Length();
+    nsContentUtils::ComparePoints(mStart.Container(),
+                                  static_cast<int32_t>(mStart.Offset()),
+                                  mEnd.Container(),
+                                  static_cast<int32_t>(mEnd.Offset())) <= 0);
 }
 
 nsresult
-ContentEventHandler::RawRange::SetStart(nsINode* aStartContainer,
-                                        uint32_t aStartOffset)
+ContentEventHandler::RawRange::SetStart(const RawRangeBoundary& aStart)
 {
-  nsINode* newRoot = nsRange::ComputeRootNode(aStartContainer);
+  nsINode* newRoot = nsRange::ComputeRootNode(aStart.Container());
   if (!newRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
 
-  if (!IsValidOffset(aStartContainer, aStartOffset)) {
+  if (!aStart.IsSetAndValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Collapse if not positioned yet, or if positioned in another document.
   if (!IsPositioned() || newRoot != mRoot) {
     mRoot = newRoot;
-    mStartContainer = mEndContainer = aStartContainer;
-    mStartOffset = mEndOffset = aStartOffset;
+    mStart = mEnd = aStart;
     return NS_OK;
   }
 
-  mStartContainer = aStartContainer;
-  mStartOffset = aStartOffset;
+  mStart = aStart;
   AssertStartIsBeforeOrEqualToEnd();
   return NS_OK;
 }
 
 nsresult
-ContentEventHandler::RawRange::SetEnd(nsINode* aEndContainer,
-                                      uint32_t aEndOffset)
+ContentEventHandler::RawRange::SetEnd(const RawRangeBoundary& aEnd)
 {
-  nsINode* newRoot = nsRange::ComputeRootNode(aEndContainer);
+  nsINode* newRoot = nsRange::ComputeRootNode(aEnd.Container());
   if (!newRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
 
-  if (!IsValidOffset(aEndContainer, aEndOffset)) {
+  if (!aEnd.IsSetAndValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Collapse if not positioned yet, or if positioned in another document.
   if (!IsPositioned() || newRoot != mRoot) {
     mRoot = newRoot;
-    mStartContainer = mEndContainer = aEndContainer;
-    mStartOffset = mEndOffset = aEndOffset;
+    mStart = mEnd = aEnd;
     return NS_OK;
   }
 
-  mEndContainer = aEndContainer;
-  mEndOffset = aEndOffset;
+  mEnd = aEnd;
   AssertStartIsBeforeOrEqualToEnd();
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::RawRange::SetEndAfter(nsINode* aEndContainer)
 {
   uint32_t offset = 0;
   nsINode* container =
     nsRange::GetContainerAndOffsetAfter(aEndContainer, &offset);
   return SetEnd(container, offset);
 }
 
 void
 ContentEventHandler::RawRange::SetStartAndEnd(const nsRange* aRange)
 {
-  DebugOnly<nsresult> rv = SetStartAndEnd(aRange->GetStartContainer(),
-                                          aRange->StartOffset(),
-                                          aRange->GetEndContainer(),
-                                          aRange->EndOffset());
+  DebugOnly<nsresult> rv = SetStartAndEnd(aRange->StartRef().AsRaw(),
+                                          aRange->EndRef().AsRaw());
   MOZ_ASSERT(!aRange->IsPositioned() || NS_SUCCEEDED(rv));
 }
 
 nsresult
-ContentEventHandler::RawRange::SetStartAndEnd(nsINode* aStartContainer,
-                                              uint32_t aStartOffset,
-                                              nsINode* aEndContainer,
-                                              uint32_t aEndOffset)
+ContentEventHandler::RawRange::SetStartAndEnd(const RawRangeBoundary& aStart,
+                                              const RawRangeBoundary& aEnd)
 {
-  nsINode* newStartRoot = nsRange::ComputeRootNode(aStartContainer);
+  nsINode* newStartRoot = nsRange::ComputeRootNode(aStart.Container());
   if (!newStartRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
-  if (!IsValidOffset(aStartContainer, aStartOffset)) {
+  if (!aStart.IsSetAndValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
-  if (aStartContainer == aEndContainer) {
-    if (!IsValidOffset(aEndContainer, aEndOffset)) {
+  if (aStart.Container() == aEnd.Container()) {
+    if (!aEnd.IsSetAndValid()) {
       return NS_ERROR_DOM_INDEX_SIZE_ERR;
     }
-    MOZ_ASSERT(aStartOffset <= aEndOffset);
+    MOZ_ASSERT(aStart.Offset() <= aEnd.Offset());
     mRoot = newStartRoot;
-    mStartContainer = mEndContainer = aStartContainer;
-    mStartOffset = aStartOffset;
-    mEndOffset = aEndOffset;
+    mStart = aStart;
+    mEnd = aEnd;
     return NS_OK;
   }
 
-  nsINode* newEndRoot = nsRange::ComputeRootNode(aEndContainer);
+  nsINode* newEndRoot = nsRange::ComputeRootNode(aEnd.Container());
   if (!newEndRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
-  if (!IsValidOffset(aEndContainer, aEndOffset)) {
+  if (!aEnd.IsSetAndValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // If they have different root, this should be collapsed at the end point.
   if (newStartRoot != newEndRoot) {
     mRoot = newEndRoot;
-    mStartContainer = mEndContainer = aEndContainer;
-    mStartOffset = mEndOffset = aEndOffset;
+    mStart = mEnd = aEnd;
     return NS_OK;
   }
 
   // Otherwise, set the range as specified.
   mRoot = newStartRoot;
-  mStartContainer = aStartContainer;
-  mStartOffset = aStartOffset;
-  mEndContainer = aEndContainer;
-  mEndOffset = aEndOffset;
+  mStart = aStart;
+  mEnd = aEnd;
   AssertStartIsBeforeOrEqualToEnd();
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::RawRange::SelectNodeContents(
                                  nsINode* aNodeToSelectContents)
 {
   nsINode* newRoot = nsRange::ComputeRootNode(aNodeToSelectContents);
   if (!newRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
   mRoot = newRoot;
-  mStartContainer = mEndContainer = aNodeToSelectContents;
-  mStartOffset = 0;
-  mEndOffset = aNodeToSelectContents->Length();
+  mStart = RawRangeBoundary(aNodeToSelectContents, nullptr);
+  mEnd = RawRangeBoundary(aNodeToSelectContents,
+                          aNodeToSelectContents->GetLastChild());
   return NS_OK;
 }
 
 /******************************************************************/
 /* ContentEventHandler                                            */
 /******************************************************************/
 
 // NOTE
@@ -411,17 +390,17 @@ ContentEventHandler::InitCommon(Selectio
   // is a special selection.
   if (aSelectionType != SelectionType::eNormal) {
     MOZ_ASSERT(!mFirstSelectedRawRange.IsPositioned());
     return NS_OK;
   }
 
   // But otherwise, we need to assume that there is a selection range at the
   // beginning of the root content if aSelectionType is eNormal.
-  rv = mFirstSelectedRawRange.CollapseTo(mRootContent, 0);
+  rv = mFirstSelectedRawRange.CollapseTo(RawRangeBoundary(mRootContent, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_UNEXPECTED;
   }
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
@@ -1155,17 +1134,17 @@ ContentEventHandler::SetRawRangeFromFlat
     *aNewOffset = aOffset;
   }
   if (aLastTextNode) {
     *aLastTextNode = nullptr;
   }
 
   // Special case like <br contenteditable>
   if (!mRootContent->HasChildren()) {
-    nsresult rv = aRawRange->CollapseTo(mRootContent, 0);
+    nsresult rv = aRawRange->CollapseTo(RawRangeBoundary(mRootContent, 0));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
   nsresult rv = iter->Init(mRootContent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -2912,18 +2891,17 @@ ContentEventHandler::GetFlatTextLengthIn
     iter = NS_NewPreContentIterator();
     nsresult rv = iter->Init(aStartPosition.Container());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   } else {
     RawRange prevRawRange;
     nsresult rv =
-      prevRawRange.SetStart(aStartPosition.Container(),
-                            aStartPosition.Offset());
+      prevRawRange.SetStart(aStartPosition.AsRaw());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // When the end position is immediately after non-root element's open tag,
     // we need to include a line break caused by the open tag.
     if (endPosition.Container() != aRootContent &&
         endPosition.IsImmediatelyAfterOpenTag()) {
@@ -2946,17 +2924,17 @@ ContentEventHandler::GetFlatTextLengthIn
           return NS_ERROR_FAILURE;
         }
         endPosition = NodePositionBefore(parentContent, indexInParent + 1);
       }
     }
 
     if (endPosition.IsSetAndValid()) {
       // Offset is within node's length; set end of range to that offset
-      rv = prevRawRange.SetEnd(endPosition.Container(), endPosition.Offset());
+      rv = prevRawRange.SetEnd(endPosition.AsRaw());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       iter = NS_NewPreContentIterator();
       rv =
         iter->Init(prevRawRange.GetStartContainer(), prevRawRange.StartOffset(),
                    prevRawRange.GetEndContainer(), prevRawRange.EndOffset());
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -2994,16 +2972,18 @@ ContentEventHandler::GetFlatTextLengthIn
     if (!node->IsContent()) {
       continue;
     }
     nsIContent* content = node->AsContent();
 
     if (node->IsNodeOfType(nsINode::eTEXT)) {
       // Note: our range always starts from offset 0
       if (node == endPosition.Container()) {
+        // NOTE: We should have an offset here, as endPosition.Container() is a
+        // nsINode::eTEXT, which always has an offset.
         *aLength += GetTextLength(content, aLineBreakType,
                                   endPosition.Offset());
       } else {
         *aLength += GetTextLength(content, aLineBreakType);
       }
     } else if (ShouldBreakLineBefore(content, aRootContent)) {
       // If the start position is start of this node but doesn't include the
       // open tag, don't append the line break length.
@@ -3094,17 +3074,18 @@ ContentEventHandler::AdjustCollapsedRang
   }
 
   // But if the found node isn't a text node, we cannot modify the range.
   if (!childNode || !childNode->IsNodeOfType(nsINode::eTEXT) ||
       NS_WARN_IF(offsetInChildNode < 0)) {
     return NS_OK;
   }
 
-  nsresult rv = aRawRange.CollapseTo(childNode, offsetInChildNode);
+  nsresult rv =
+    aRawRange.CollapseTo(RawRangeBoundary(childNode, offsetInChildNode));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::GetStartFrameAndOffset(const RawRange& aRawRange,
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -41,66 +41,72 @@ private:
   /**
    * RawRange is a helper class of ContentEventHandler class.  The caller is
    * responsible for making sure the start/end nodes are in document order.
    * This is enforced by assertions in DEBUG builds.
    */
   class MOZ_STACK_CLASS RawRange final
   {
   public:
-    RawRange()
-      : mStartOffset(0)
-      , mEndOffset(0)
-    {
-    }
+    RawRange() {}
 
     void Clear()
     {
-      mRoot = mStartContainer = mEndContainer = nullptr;
-      mStartOffset = mEndOffset = 0;
+      mRoot = nullptr;
+      mStart = RangeBoundary();
+      mEnd = RangeBoundary();
     }
 
     bool IsPositioned() const
     {
-      return mStartContainer && mEndContainer;
+      return mStart.IsSet() && mEnd.IsSet();
     }
     bool Collapsed() const
     {
-      return mStartContainer == mEndContainer &&
-             mStartOffset == mEndOffset &&
-             IsPositioned();
+      return mStart == mEnd && IsPositioned();
     }
-    nsINode* GetStartContainer() const { return mStartContainer; }
-    nsINode* GetEndContainer() const { return mEndContainer; }
-    uint32_t StartOffset() const { return mStartOffset; }
-    uint32_t EndOffset() const { return mEndOffset; }
+    nsINode* GetStartContainer() const { return mStart.Container(); }
+    nsINode* GetEndContainer() const { return mEnd.Container(); }
+    uint32_t StartOffset() const { return mStart.Offset(); }
+    uint32_t EndOffset() const { return mEnd.Offset(); }
+    nsIContent* StartRef() const { return mStart.Ref(); }
+    nsIContent* EndRef() const { return mEnd.Ref(); }
 
-    nsresult CollapseTo(nsINode* aContainer, uint32_t aOffset)
+    // XXX: Make these use RangeBoundaries...
+    nsresult CollapseTo(const RawRangeBoundary& aBoundary)
     {
-      return SetStartAndEnd(aContainer, aOffset, aContainer, aOffset);
+      return SetStartAndEnd(aBoundary, aBoundary);
     }
-    nsresult SetStart(nsINode* aStartContainer, uint32_t aStartOffset);
-    nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset);
+    nsresult SetStart(const RawRangeBoundary& aStart);
+    nsresult SetEnd(const RawRangeBoundary& aEnd);
+
+    // NOTE: These helpers can hide performance problems, as they perform a
+    // search to find aStartOffset in aStartContainer.
+    nsresult SetStart(nsINode* aStartContainer, uint32_t aStartOffset) {
+      return SetStart(RawRangeBoundary(aStartContainer, aStartOffset));
+    }
+    nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset) {
+      return SetEnd(RawRangeBoundary(aEndContainer, aEndOffset));
+    }
+
     nsresult SetEndAfter(nsINode* aEndContainer);
     void SetStartAndEnd(const nsRange* aRange);
-    nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
-                            nsINode* aEndContainer, uint32_t aEndOffset);
+    nsresult SetStartAndEnd(const RawRangeBoundary& aStart,
+                            const RawRangeBoundary& aEnd);
 
     nsresult SelectNodeContents(nsINode* aNodeToSelectContents);
 
   private:
-    bool IsValidOffset(nsINode* aContainer, uint32_t aOffset) const;
     nsINode* IsValidBoundary(nsINode* aNode) const;
     inline void AssertStartIsBeforeOrEqualToEnd();
 
     nsCOMPtr<nsINode> mRoot;
-    nsCOMPtr<nsINode> mStartContainer;
-    nsCOMPtr<nsINode> mEndContainer;
-    uint32_t mStartOffset;
-    uint32_t mEndOffset;
+
+    RangeBoundary mStart;
+    RangeBoundary mEnd;
   };
 
 public:
   typedef dom::Selection Selection;
 
   explicit ContentEventHandler(nsPresContext* aPresContext);
 
   // Handle aEvent in the current process.