Bug 1384915 - Part 1: Refactor RangeBoundary out of nsRange so it can be used by other classes, r=masayuki
authorMichael Layzell <michael@thelayzells.com>
Thu, 07 Sep 2017 17:05:51 -0400
changeset 382853 228b74cf9509bc34a9541facf09c5ee87140d927
parent 382852 e507ee142c64d068c1d2f85f2c3d3a6efa51df42
child 382854 446c82956eda195a47ab9f12bddf7a199e854a6a
push id32577
push userarchaeopteryx@coole-files.de
push dateTue, 26 Sep 2017 09:54:52 +0000
treeherdermozilla-central@bc5672989895 [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 1: Refactor RangeBoundary out of nsRange so it can be used by other classes, r=masayuki
dom/base/RangeBoundary.h
dom/base/moz.build
dom/base/nsRange.cpp
dom/base/nsRange.h
new file mode 100644
--- /dev/null
+++ b/dom/base/RangeBoundary.h
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_RangeBoundary_h
+#define mozilla_RangeBoundary_h
+
+namespace mozilla {
+
+// This class will maintain a reference to the child immediately
+// before the boundary's offset. We try to avoid computing the
+// offset as much as possible and just ensure mRef points to the
+// correct child.
+//
+// mParent
+//    |
+// [child0] [child1] [child2]
+//            /      |
+//         mRef    mOffset=2
+//
+// If mOffset == 0, mRef is null.
+// For text nodes, mRef will always be null and the offset will
+// be kept up-to-date.
+
+template<typename ParentType, typename RefType>
+class RangeBoundaryBase;
+
+typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>> RangeBoundary;
+typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
+
+// This class has two specializations, one using reference counting
+// pointers and one using raw pointers. This helps us avoid unnecessary
+// AddRef/Release calls.
+template<typename ParentType, typename RefType>
+class RangeBoundaryBase
+{
+  template<typename T, typename U>
+  friend class RangeBoundaryBase;
+
+  friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&,
+                                          RangeBoundary&, const char*,
+                                          uint32_t);
+  friend void ImplCycleCollectionUnlink(RangeBoundary&);
+
+public:
+  RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef)
+    : mParent(aContainer)
+    , mRef(aRef)
+  {
+    if (!mRef) {
+      mOffset = mozilla::Some(0);
+    } else {
+      mOffset.reset();
+    }
+  }
+
+  RangeBoundaryBase(nsINode* aContainer, int32_t aOffset)
+    : mParent(aContainer)
+    , mRef(nullptr)
+    , mOffset(mozilla::Some(aOffset))
+  {
+    if (mParent && mParent->IsContainerNode()) {
+      // Find a reference node
+      if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
+        mRef = aContainer->GetLastChild();
+      } else if (aOffset != 0) {
+        mRef = mParent->GetChildAt(aOffset - 1);
+        MOZ_ASSERT(mRef);
+      }
+
+      MOZ_ASSERT_IF(!mRef, aOffset == 0);
+    }
+
+    MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
+  }
+
+  RangeBoundaryBase()
+    : mParent(nullptr)
+    , mRef(nullptr)
+  {
+  }
+
+  // Needed for initializing RawRangeBoundary from an existing RangeBoundary.
+  template<typename PT, typename RT>
+  explicit RangeBoundaryBase(const RangeBoundaryBase<PT, RT>& aOther)
+    : mParent(aOther.mParent)
+    , mRef(aOther.mRef)
+    , mOffset(aOther.mOffset)
+  {
+  }
+
+  nsIContent*
+  Ref() const
+  {
+    return mRef;
+  }
+
+  nsINode*
+  Container() const
+  {
+    return mParent;
+  }
+
+  nsIContent*
+  GetChildAtOffset() const
+  {
+    if (!mParent || !mParent->IsContainerNode()) {
+      return nullptr;
+    }
+    if (!mRef) {
+      MOZ_ASSERT(Offset() == 0);
+      return mParent->GetFirstChild();
+    }
+    MOZ_ASSERT(mParent->GetChildAt(Offset()) == mRef->GetNextSibling());
+    return mRef->GetNextSibling();
+  }
+
+  uint32_t
+  Offset() const
+  {
+    if (mOffset.isSome()) {
+      return mOffset.value();
+    }
+
+    if (!mParent) {
+      return 0;
+    }
+
+    MOZ_ASSERT(mRef);
+    MOZ_ASSERT(mRef->GetParentNode() == mParent);
+    mOffset = mozilla::Some(mParent->IndexOf(mRef) + 1);
+
+    return mOffset.value();
+  }
+
+  void
+  InvalidateOffset()
+  {
+    MOZ_ASSERT(mParent);
+    MOZ_ASSERT(mParent->IsContainerNode(), "Range is positioned on a text node!");
+
+    if (!mRef) {
+      MOZ_ASSERT(mOffset.isSome() && mOffset.value() == 0);
+      return;
+    }
+    mOffset.reset();
+  }
+
+  void
+  Set(nsINode* aContainer, int32_t aOffset)
+  {
+    mParent = aContainer;
+    if (mParent && mParent->IsContainerNode()) {
+      // Find a reference node
+      if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
+        mRef = aContainer->GetLastChild();
+      } else if (aOffset == 0) {
+        mRef = nullptr;
+      } else {
+        mRef = mParent->GetChildAt(aOffset - 1);
+        MOZ_ASSERT(mRef);
+      }
+
+      MOZ_ASSERT_IF(!mRef, aOffset == 0);
+    } else {
+      mRef = nullptr;
+    }
+
+    mOffset = mozilla::Some(aOffset);
+    MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
+  }
+
+  void
+  SetAfterRef(nsINode* aParent, nsIContent* aRef)
+  {
+    mParent = aParent;
+    mRef = aRef;
+    if (!mRef) {
+      mOffset = mozilla::Some(0);
+    } else {
+      mOffset.reset();
+    }
+  }
+
+  bool
+  IsSet() const
+  {
+    return mParent && (mRef || mOffset.isSome());
+  }
+
+  // Convenience methods for switching between the two types
+  // of RangeBoundary.
+  RangeBoundaryBase<nsINode*, nsIContent*>
+  AsRaw() const
+  {
+    return RangeBoundaryBase<nsINode*, nsIContent*>(*this);
+  }
+
+  template<typename A, typename B>
+  RangeBoundaryBase& operator=(const RangeBoundaryBase<A,B>& aOther)
+  {
+    mParent = aOther.mParent;
+    mRef = aOther.mRef;
+    mOffset = aOther.mOffset;
+    return *this;
+  }
+
+  template<typename A, typename B>
+  bool operator==(const RangeBoundaryBase<A, B>& aOther) const
+  {
+    return mParent == aOther.mParent &&
+      (mRef ? mRef == aOther.mRef : mOffset == aOther.mOffset);
+  }
+
+private:
+  ParentType mParent;
+  RefType mRef;
+
+  mutable mozilla::Maybe<uint32_t> mOffset;
+};
+
+inline void
+ImplCycleCollectionUnlink(RangeBoundary& aField)
+{
+  ImplCycleCollectionUnlink(aField.mParent);
+  ImplCycleCollectionUnlink(aField.mRef);
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            RangeBoundary& aField,
+                            const char* aName,
+                            uint32_t aFlags)
+{
+  ImplCycleCollectionTraverse(aCallback, aField.mParent, "mParent", 0);
+  ImplCycleCollectionTraverse(aCallback, aField.mRef, "mRef", 0);
+}
+
+} // namespace mozilla
+
+#endif // defined(mozilla_RangeBoundary_h)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -135,16 +135,17 @@ if CONFIG['MOZ_WEBRTC']:
         'nsDOMDataChannel.h',
         'nsDOMDataChannelDeclarations.h',
     ]
 
 EXPORTS.mozilla += [
     'CORSMode.h',
     'FeedWriterEnabled.h',
     'FlushType.h',
+    'RangeBoundary.h',
     'TextInputProcessor.h',
     'UseCounter.h',
 ]
 
 EXPORTS.mozilla.dom += [
     '!UseCounterList.h',
     'AnonymousContent.h',
     'Attr.h',
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -352,20 +352,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   // the result of IsInSelection() which is used by tmp->Reset().
   MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(),
                         "Shouldn't be registered now that we're unlinking");
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart.mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart.mRef)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd.mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd.mRef)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -18,16 +18,17 @@
 #include "nsIDOMNode.h"
 #include "nsLayoutUtils.h"
 #include "prmon.h"
 #include "nsStubMutationObserver.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
+#include "mozilla/RangeBoundary.h"
 
 namespace mozilla {
 class ErrorResult;
 namespace dom {
 struct ClientRectsAndTexts;
 class DocumentFragment;
 class DOMRect;
 class DOMRectList;
@@ -39,16 +40,18 @@ class nsRange final : public nsIDOMRange
                       public nsStubMutationObserver,
                       public nsWrapperCache,
                       // For linking together selection-associated ranges.
                       public mozilla::LinkedListElement<nsRange>
 {
   typedef mozilla::ErrorResult ErrorResult;
   typedef mozilla::dom::DOMRect DOMRect;
   typedef mozilla::dom::DOMRectList DOMRectList;
+  typedef mozilla::RangeBoundary RangeBoundary;
+  typedef mozilla::RawRangeBoundary RawRangeBoundary;
 
   virtual ~nsRange();
 
 public:
   explicit nsRange(nsINode* aNode);
 
   static nsresult CreateRange(nsIDOMNode* aStartContainer,
                               uint32_t aStartOffset,
@@ -407,225 +410,16 @@ public:
    * will be empty.
    * @param aOutRanges the resulting set of ranges
    */
   void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges);
 
   typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
 protected:
 
-  // This class has two specializations, one using reference counting
-  // pointers and one using raw pointers. This helps us avoid unnecessary
-  // AddRef/Release calls.
-  template<typename ParentType, typename RefType>
-  class RangeBoundaryBase
-  {
-    // This class will maintain a reference to the child immediately
-    // before the boundary's offset. We try to avoid computing the
-    // offset as much as possible and just ensure mRef points to the
-    // correct child.
-    //
-    // mParent
-    //    |
-    // [child0] [child1] [child2]
-    //            /      |
-    //         mRef    mOffset=2
-    //
-    // If mOffset == 0, mRef is null.
-    // For text nodes, mRef will always be null and the offset will
-    // be kept up-to-date.
-
-    // for cycle collecting mParent and mRef;
-    friend class nsRange;
-
-  public:
-    RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef)
-      : mParent(aContainer)
-      , mRef(aRef)
-    {
-      if (!mRef) {
-        mOffset = mozilla::Some(0);
-      } else {
-        mOffset.reset();
-      }
-    }
-
-    RangeBoundaryBase(nsINode* aContainer, int32_t aOffset)
-      : mParent(aContainer)
-      , mRef(nullptr)
-      , mOffset(mozilla::Some(aOffset))
-    {
-      if (mParent && mParent->IsContainerNode()) {
-        // Find a reference node
-        if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
-          mRef = aContainer->GetLastChild();
-        } else if (aOffset != 0) {
-          mRef = mParent->GetChildAt(aOffset - 1);
-          MOZ_ASSERT(mRef);
-        }
-
-        MOZ_ASSERT_IF(!mRef, aOffset == 0);
-      }
-
-      MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
-    }
-
-    RangeBoundaryBase()
-      : mParent(nullptr)
-      , mRef(nullptr)
-    {
-    }
-
-    // Needed for initializing RawRangeBoundary from an existing RangeBoundary.
-    explicit RangeBoundaryBase(const RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>>& aOther)
-      : mParent(aOther.mParent)
-      , mRef(aOther.mRef)
-      , mOffset(aOther.mOffset)
-    {
-    }
-
-    nsIContent*
-    Ref() const
-    {
-      return mRef;
-    }
-
-    nsINode*
-    Container() const
-    {
-      return mParent;
-    }
-
-    nsIContent*
-    GetChildAtOffset() const
-    {
-      if (!mParent || !mParent->IsContainerNode()) {
-        return nullptr;
-      }
-      if (!mRef) {
-        MOZ_ASSERT(Offset() == 0);
-        return mParent->GetFirstChild();
-      }
-      MOZ_ASSERT(mParent->GetChildAt(Offset()) == mRef->GetNextSibling());
-      return mRef->GetNextSibling();
-    }
-
-    uint32_t
-    Offset() const
-    {
-      if (mOffset.isSome()) {
-        return mOffset.value();
-      }
-
-      if (!mParent) {
-        return 0;
-      }
-
-      MOZ_ASSERT(mRef);
-      MOZ_ASSERT(mRef->GetParentNode() == mParent);
-      mOffset = mozilla::Some(mParent->IndexOf(mRef) + 1);
-
-      return mOffset.value();
-    }
-
-    void
-    InvalidateOffset()
-    {
-      MOZ_ASSERT(mParent);
-      MOZ_ASSERT(mParent->IsContainerNode(), "Range is positioned on a text node!");
-
-      if (!mRef) {
-        MOZ_ASSERT(mOffset.isSome() && mOffset.value() == 0);
-        return;
-      }
-      mOffset.reset();
-    }
-
-    void
-    AdjustOffset(int32_t aDelta)
-    {
-      MOZ_ASSERT(mRef);
-      mOffset = mozilla::Some(Offset() + aDelta);
-    }
-
-    void
-    Set(nsINode* aContainer, int32_t aOffset)
-    {
-      mParent = aContainer;
-      if (mParent && mParent->IsContainerNode()) {
-        // Find a reference node
-        if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
-          mRef = aContainer->GetLastChild();
-        } else if (aOffset == 0) {
-          mRef = nullptr;
-        } else {
-          mRef = mParent->GetChildAt(aOffset - 1);
-          MOZ_ASSERT(mRef);
-        }
-
-        MOZ_ASSERT_IF(!mRef, aOffset == 0);
-      } else {
-        mRef = nullptr;
-      }
-
-      mOffset = mozilla::Some(aOffset);
-      MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
-    }
-
-    void
-    SetAfterRef(nsINode* aParent, nsIContent* aRef)
-    {
-      mParent = aParent;
-      mRef = aRef;
-      if (!mRef) {
-        mOffset = mozilla::Some(0);
-      } else {
-        mOffset.reset();
-      }
-    }
-
-    bool
-    IsSet() const
-    {
-      return mParent && (mRef || mOffset.isSome());
-    }
-
-    // Convenience methods for switching between the two types
-    // of RangeBoundary.
-    RangeBoundaryBase<nsINode*, nsIContent*>
-    AsRaw() const
-    {
-      return RangeBoundaryBase<nsINode*, nsIContent*>(*this);
-    }
-
-    template<typename A, typename B>
-    RangeBoundaryBase& operator=(const RangeBoundaryBase<A,B>& aOther)
-    {
-      // Since the member variables may be nsCOMPtrs, better to try to avoid
-      // extra Release/AddRef calls.
-      if (mParent != aOther.mParent) {
-        mParent = aOther.mParent;
-      }
-      if (mRef != aOther.mRef) {
-        mRef = aOther.mRef;
-      }
-      mOffset = aOther.mOffset;
-      return *this;
-    }
-
-  private:
-    ParentType mParent;
-    RefType mRef;
-
-    mutable mozilla::Maybe<uint32_t> mOffset;
-  };
-
-  typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>> RangeBoundary;
-  typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
-
   void RegisterCommonAncestor(nsINode* aNode);
   void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
   nsINode* IsValidBoundary(nsINode* aNode) const
   {
     return ComputeRootNode(aNode, mMaySpanAnonymousSubtrees);
   }
 
   /**