Commit 61ed1c4c authored by Noemi Erli's avatar Noemi Erli
Browse files

Backed out 3 changesets (bug 1828469) for causing assertion in RangeBoundary.h

Backed out changeset ab92a12ba6ea (bug 1828469)
Backed out changeset 217801294c41 (bug 1828469)
Backed out changeset c78c717e4ec9 (bug 1828469)
parent 47646cf9
Loading
Loading
Loading
Loading
+1 −173
Original line number Diff line number Diff line
@@ -12,7 +12,6 @@
#include "mozilla/RangeUtils.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/StaticRange.h"
#include "mozilla/dom/Selection.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsGkAtoms.h"
@@ -66,73 +65,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractRange)
  // mStart and mEnd may depend on or be depended on some other members in
  // concrete classes so that they should be unlinked in sub classes.
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
  tmp->mSelections.Clear();
  // Unregistering of the common inclusive ancestors would by design
  // also happen when the actual implementations unlink `mStart`/`mEnd`.
  // This may introduce additional overhead which is not needed when unlinking,
  // therefore this is done here beforehand.
  if (tmp->mRegisteredClosestCommonInclusiveAncestor) {
    tmp->UnregisterClosestCommonInclusiveAncestor(
        tmp->mRegisteredClosestCommonInclusiveAncestor, true);
  }
  MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(),
                        "Shouldn't be registered now that we're unlinking");

NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractRange)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegisteredClosestCommonInclusiveAncestor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

void AbstractRange::MarkDescendants(const nsINode& aNode) {
  // Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on
  // aNode's descendants unless aNode is already marked as a range common
  // ancestor or a descendant of one, in which case all of our descendants have
  // the bit set already.
  if (!aNode.IsMaybeSelected()) {
    // don't set the Descendant bit on |aNode| itself
    nsINode* node = aNode.GetNextNode(&aNode);
    while (node) {
      node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
      if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
        node = node->GetNextNode(&aNode);
      } else {
        // optimize: skip this sub-tree since it's marked already.
        node = node->GetNextNonChildNode(&aNode);
      }
    }
  }
}

void AbstractRange::UnmarkDescendants(const nsINode& aNode) {
  // Unset NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection
  // on aNode's descendants unless aNode is a descendant of another range common
  // ancestor. Also, exclude descendants of range common ancestors (but not the
  // common ancestor itself).
  if (!aNode
           .IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
    // we know |aNode| doesn't have any bit set
    nsINode* node = aNode.GetNextNode(&aNode);
    while (node) {
      node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
      if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
        node = node->GetNextNode(&aNode);
      } else {
        // We found an ancestor of an overlapping range, skip its descendants.
        node = node->GetNextNonChildNode(&aNode);
      }
    }
  }
}

// NOTE: If you need to change default value of members of AbstractRange,
//       update nsRange::Create(nsINode* aNode) and ClearForReuse() too.
AbstractRange::AbstractRange(nsINode* aNode, bool aIsDynamicRange)
    : mRegisteredClosestCommonInclusiveAncestor(nullptr),
      mIsPositioned(false),
    : mIsPositioned(false),
      mIsGenerated(false),
      mCalledByJS(false),
      mIsDynamicRange(aIsDynamicRange) {
@@ -262,122 +206,6 @@ nsresult AbstractRange::SetStartAndEndInternal(
  return NS_OK;
}

bool AbstractRange::IsInSelection(const Selection& aSelection) const {
  return mSelections.Contains(&aSelection);
}

void AbstractRange::RegisterSelection(Selection& aSelection) {
  if (IsInSelection(aSelection)) {
    return;
  }
  bool isFirstSelection = mSelections.IsEmpty();
  mSelections.AppendElement(&aSelection);
  if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) {
    nsINode* commonAncestor = GetClosestCommonInclusiveAncestor();
    MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes");
    RegisterClosestCommonInclusiveAncestor(commonAncestor);
  }
}

const nsTArray<WeakPtr<Selection>>& AbstractRange::GetSelections() const {
  return mSelections;
}

void AbstractRange::UnregisterSelection(const Selection& aSelection) {
  mSelections.RemoveElement(&aSelection);
  if (mSelections.IsEmpty() && mRegisteredClosestCommonInclusiveAncestor) {
    UnregisterClosestCommonInclusiveAncestor(
        mRegisteredClosestCommonInclusiveAncestor, false);
    MOZ_DIAGNOSTIC_ASSERT(
        !mRegisteredClosestCommonInclusiveAncestor,
        "How can we have a registered common ancestor when we "
        "just unregistered?");
    MOZ_DIAGNOSTIC_ASSERT(
        !isInList(),
        "Shouldn't be registered if we have no "
        "mRegisteredClosestCommonInclusiveAncestor after unregistering");
  }
}

void AbstractRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) {
  MOZ_ASSERT(aNode, "bad arg");

  MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(),
                        "registering range not in selection");

  mRegisteredClosestCommonInclusiveAncestor = aNode;

  MarkDescendants(*aNode);

  UniquePtr<LinkedList<AbstractRange>>& ranges =
      aNode->GetClosestCommonInclusiveAncestorRangesPtr();
  if (!ranges) {
    ranges = MakeUnique<LinkedList<AbstractRange>>();
  }

  MOZ_DIAGNOSTIC_ASSERT(!isInList());
  ranges->insertBack(this);
  aNode->SetClosestCommonInclusiveAncestorForRangeInSelection();
}

void AbstractRange::UnregisterClosestCommonInclusiveAncestor(
    nsINode* aNode, bool aIsUnlinking) {
  MOZ_ASSERT(aNode, "bad arg");
  NS_ASSERTION(aNode->IsClosestCommonInclusiveAncestorForRangeInSelection(),
               "wrong node");
  MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredClosestCommonInclusiveAncestor,
                        "wrong node");
  LinkedList<AbstractRange>* ranges =
      aNode->GetExistingClosestCommonInclusiveAncestorRanges();
  MOZ_ASSERT(ranges);

  mRegisteredClosestCommonInclusiveAncestor = nullptr;

#ifdef DEBUG
  bool found = false;
  for (AbstractRange* range : *ranges) {
    if (range == this) {
      found = true;
      break;
    }
  }
  MOZ_ASSERT(found,
             "We should be in the list on our registered common ancestor");
#endif  // DEBUG

  remove();

  // We don't want to waste time unmarking flags on nodes that are
  // being unlinked anyway.
  if (!aIsUnlinking && ranges->isEmpty()) {
    aNode->ClearClosestCommonInclusiveAncestorForRangeInSelection();
    UnmarkDescendants(*aNode);
  }
}

void AbstractRange::UpdateCommonAncestorIfNecessary() {
  nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor;
  nsINode* newCommonAncestor = GetClosestCommonInclusiveAncestor();
  if (newCommonAncestor != oldCommonAncestor) {
    if (oldCommonAncestor) {
      UnregisterClosestCommonInclusiveAncestor(oldCommonAncestor, false);
    }
    if (newCommonAncestor) {
      RegisterClosestCommonInclusiveAncestor(newCommonAncestor);
    } else {
      MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes");
      mSelections.Clear();
      MOZ_DIAGNOSTIC_ASSERT(
          !mRegisteredClosestCommonInclusiveAncestor,
          "How can we have a registered common ancestor when we "
          "didn't register ourselves?");
      MOZ_DIAGNOSTIC_ASSERT(!isInList(),
                            "Shouldn't be registered if we have no "
                            "mRegisteredClosestCommonInclusiveAncestor");
    }
  }
}

nsINode* AbstractRange::GetParentObject() const { return mOwner; }

JSObject* AbstractRange::WrapObject(JSContext* aCx,
+2 −50
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
#include "mozilla/Maybe.h"
#include "mozilla/RangeBoundary.h"
#include "mozilla/RefPtr.h"
#include "mozilla/WeakPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupports.h"
#include "nsWrapperCache.h"
@@ -27,14 +26,10 @@ class nsRange;
struct JSContext;

namespace mozilla::dom {
class Document;
class Selection;
class StaticRange;
class Document;

class AbstractRange : public nsISupports,
                      public nsWrapperCache,
                      // For linking together selection-associated ranges.
                      public mozilla::LinkedListElement<AbstractRange> {
class AbstractRange : public nsISupports, public nsWrapperCache {
 protected:
  explicit AbstractRange(nsINode* aNode, bool aIsDynamicRange);
  virtual ~AbstractRange();
@@ -111,27 +106,6 @@ class AbstractRange : public nsISupports,
  inline StaticRange* AsStaticRange();
  inline const StaticRange* AsStaticRange() const;

  /**
   * Return true if this range is part of a Selection object
   * and isn't detached.
   */
  bool IsInAnySelection() const { return !mSelections.IsEmpty(); }

  MOZ_CAN_RUN_SCRIPT void RegisterSelection(
      mozilla::dom::Selection& aSelection);

  void UnregisterSelection(const mozilla::dom::Selection& aSelection);

  /**
   * Returns a list of all Selections the range is associated with.
   */
  const nsTArray<WeakPtr<Selection>>& GetSelections() const;

  /**
   * Return true if this range is in |aSelection|.
   */
  bool IsInSelection(const mozilla::dom::Selection& aSelection) const;

 protected:
  template <typename SPT, typename SRT, typename EPT, typename ERT,
            typename RangeType>
@@ -159,21 +133,6 @@ class AbstractRange : public nsISupports,
                   << (aRange.mIsDynamicRange ? "true" : "false") << " }";
  }

  /**
   * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
   */
  void RegisterClosestCommonInclusiveAncestor(nsINode* aNode);
  /**
   * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
   */
  void UnregisterClosestCommonInclusiveAncestor(nsINode* aNode,
                                                bool aIsUnlinking);

  void UpdateCommonAncestorIfNecessary();

  static void MarkDescendants(const nsINode& aNode);
  static void UnmarkDescendants(const nsINode& aNode);

 private:
  void ClearForReuse();

@@ -181,13 +140,6 @@ class AbstractRange : public nsISupports,
  RefPtr<Document> mOwner;
  RangeBoundary mStart;
  RangeBoundary mEnd;

  // A Range can be part of multiple |Selection|s. This is a very rare use case.
  AutoTArray<WeakPtr<Selection>, 1> mSelections;
  // mRegisteredClosestCommonInclusiveAncestor is only non-null when the range
  // IsInAnySelection().
  nsCOMPtr<nsINode> mRegisteredClosestCommonInclusiveAncestor;

  // `true` if `mStart` and `mEnd` are set for StaticRange or set and valid
  // for nsRange.
  bool mIsPositioned;
+2 −2
Original line number Diff line number Diff line
@@ -3003,12 +3003,12 @@ void Element::List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const {
          static_cast<unsigned long long>(State().GetInternalValue()));
  fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
  if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
    const LinkedList<AbstractRange>* ranges =
    const LinkedList<nsRange>* ranges =
        GetExistingClosestCommonInclusiveAncestorRanges();
    int32_t count = 0;
    if (ranges) {
      // Can't use range-based iteration on a const LinkedList, unfortunately.
      for (const AbstractRange* r = ranges->getFirst(); r; r = r->getNext()) {
      for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) {
        ++count;
      }
    }
+37 −156
Original line number Diff line number Diff line
@@ -41,30 +41,6 @@ typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>>
    RangeBoundary;
typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;

/**
 * There are two ways of ensuring that `mRef` points to the correct node.
 * In most cases, the `RangeBoundary` is used by an object that is a
 * `MutationObserver` (i.e. `nsRange`) and replaces its `RangeBoundary`
 * objects when its parent chain changes.
 * However, there are Ranges which are not `MutationObserver`s (i.e.
 * `StaticRange`). `mRef` may become invalid when a DOM mutation happens.
 * Therefore, it needs to be recomputed using `mOffset` before it is being
 * accessed.
 * Because recomputing / validating of `mRef` could be an expensive operation,
 * it should be ensured that `Ref()` is called as few times as possible, i.e.
 * only once per method of `RangeBoundaryBase`.
 *
 * Furthermore, there are special implications when the `RangeBoundary` is not
 * used by an `MutationObserver`:
 * After a DOM mutation, the Boundary may point to something that is not valid
 * anymore, i.e. the `mOffset` is larger than `Container()->Length()`. In this
 * case, `Ref()` and `Get*ChildAtOffset()` return `nullptr` as an indication
 * that this RangeBoundary is not valid anymore. Also, `IsSetAndValid()`
 * returns false. However, `IsSet()` will still return true.
 *
 */
enum class RangeBoundaryIsMutationObserved { No = 0, Yes = 1 };

// This class has two specializations, one using reference counting
// pointers and one using raw pointers. This helps us avoid unnecessary
// AddRef/Release calls.
@@ -86,7 +62,7 @@ class RangeBoundaryBase {

 public:
  RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef)
      : mParent(aContainer), mRef(aRef), mIsMutationObserved(true) {
      : mParent(aContainer), mRef(aRef) {
    if (mRef) {
      NS_WARNING_ASSERTION(mRef->GetParentNode() == mParent,
                           "Initializing RangeBoundary with invalid value");
@@ -95,107 +71,48 @@ class RangeBoundaryBase {
    }
  }

  RangeBoundaryBase(nsINode* aContainer, uint32_t aOffset,
                    RangeBoundaryIsMutationObserved aRangeIsMutationObserver =
                        RangeBoundaryIsMutationObserved::Yes)
      : mParent(aContainer),
        mRef(nullptr),
        mOffset(mozilla::Some(aOffset)),
        mIsMutationObserved(bool(aRangeIsMutationObserver)) {
    if (mIsMutationObserved && mParent && mParent->IsContainerNode()) {
  RangeBoundaryBase(nsINode* aContainer, uint32_t aOffset)
      : mParent(aContainer), mRef(nullptr), mOffset(mozilla::Some(aOffset)) {
    if (mParent && mParent->IsContainerNode()) {
      // Find a reference node
      if (aOffset == mParent->GetChildCount()) {
        mRef = mParent->GetLastChild();
      } else if (aOffset > 0) {
        mRef = mParent->GetChildAt_Deprecated(aOffset - 1);
      }

      NS_WARNING_ASSERTION(mRef || aOffset == 0,
                           "Constructing RangeBoundary with invalid value");
    }

      NS_WARNING_ASSERTION(!mRef || mRef->GetParentNode() == mParent,
                           "Constructing RangeBoundary with invalid value");
    }
  }

  RangeBoundaryBase()
      : mParent(nullptr), mRef(nullptr), mIsMutationObserved(true) {}
  RangeBoundaryBase() : mParent(nullptr), mRef(nullptr) {}

  // Needed for initializing RawRangeBoundary from an existing RangeBoundary.
  template <typename PT, typename RT>
  RangeBoundaryBase(const RangeBoundaryBase<PT, RT>& aOther,
                    RangeBoundaryIsMutationObserved aIsMutationObserved)
      : mParent(aOther.mParent),
        mRef(aOther.mRef),
        mOffset(aOther.mOffset),
        mIsMutationObserved(bool(aIsMutationObserved)) {}
  explicit RangeBoundaryBase(const RangeBoundaryBase<PT, RT>& aOther)
      : mParent(aOther.mParent), mRef(aOther.mRef), mOffset(aOther.mOffset) {}

  /**
   * This method may return `nullptr` in two cases:
   *  1. `mIsMutationObserved` is true and the boundary points to the first
   *      child of `mParent`.
   *  2. `mIsMutationObserved` is false and `mOffset` is out of bounds for
   *     `mParent`s child list.
   * If `mIsMutationObserved` is false, this method may do some significant
   * computation. Therefore it is advised to call it as seldom as possible.
   * Code inside of this class should call this method exactly one time and
   * afterwards refer to `mRef` directly.
   */
  nsIContent* Ref() const {
    if (mIsMutationObserved) {
      return mRef;
    }
    MOZ_ASSERT(mParent);
    MOZ_ASSERT(mOffset);

    // `mRef` may have become invalid due to some DOM mutation,
    // which is not monitored here. Therefore, we need to validate `mRef`
    // manually.
    if (*mOffset > Container()->Length()) {
      // offset > child count means that the range boundary has become invalid
      // due to a DOM mutation.
      mRef = nullptr;
    } else if (*mOffset == Container()->Length()) {
      mRef = mParent->GetLastChild();
    } else if (*mOffset) {
      // validate and update `mRef`.
      // If `ComputeIndexOf()` returns `Nothing`, then `mRef` is not a child of
      // `mParent` anymore.
      // If the returned index for `mRef` does not match to `mOffset`, `mRef`
      // needs to be updated.
      auto indexOfRefObject = mParent->ComputeIndexOf(mRef);
      if (indexOfRefObject.isNothing() || *mOffset != *indexOfRefObject + 1) {
        mRef = mParent->GetChildAt_Deprecated(*mOffset - 1);
      }
    } else {
      mRef = nullptr;
    }
    return mRef;
  }
  nsIContent* Ref() const { return mRef; }

  nsINode* Container() const { return mParent; }

  /**
   * This method may return `nullptr` if `mIsMutationObserved` is false and
   * `mOffset` is out of bounds.
   */
  nsIContent* GetChildAtOffset() const {
    if (!mParent || !mParent->IsContainerNode()) {
      return nullptr;
    }
    nsIContent* const ref = Ref();
    if (!ref) {
      if (!mIsMutationObserved && *mOffset != 0) {
        // This means that this boundary is invalid.
        // `mOffset` is out of bounds.
        return nullptr;
      }
    if (!mRef) {
      MOZ_ASSERT(*Offset(OffsetFilter::kValidOrInvalidOffsets) == 0,
                 "invalid RangeBoundary");
      return mParent->GetFirstChild();
    }
    MOZ_ASSERT(mParent->GetChildAt_Deprecated(
                   *Offset(OffsetFilter::kValidOrInvalidOffsets)) ==
               ref->GetNextSibling());
    return ref->GetNextSibling();
               mRef->GetNextSibling());
    return mRef->GetNextSibling();
  }

  /**
@@ -207,13 +124,7 @@ class RangeBoundaryBase {
    if (NS_WARN_IF(!mParent) || NS_WARN_IF(!mParent->IsContainerNode())) {
      return nullptr;
    }
    nsIContent* const ref = Ref();
    if (!ref) {
      if (!mIsMutationObserved && *mOffset != 0) {
        // This means that this boundary is invalid.
        // `mOffset` is out of bounds.
        return nullptr;
      }
    if (!mRef) {
      MOZ_ASSERT(*Offset(OffsetFilter::kValidOffsets) == 0,
                 "invalid RangeBoundary");
      nsIContent* firstChild = mParent->GetFirstChild();
@@ -223,11 +134,11 @@ class RangeBoundaryBase {
      }
      return firstChild->GetNextSibling();
    }
    if (NS_WARN_IF(!ref->GetNextSibling())) {
    if (NS_WARN_IF(!mRef->GetNextSibling())) {
      // Already referring the end of the container.
      return nullptr;
    }
    return ref->GetNextSibling()->GetNextSibling();
    return mRef->GetNextSibling()->GetNextSibling();
  }

  /**
@@ -239,12 +150,11 @@ class RangeBoundaryBase {
    if (NS_WARN_IF(!mParent) || NS_WARN_IF(!mParent->IsContainerNode())) {
      return nullptr;
    }
    nsIContent* const ref = Ref();
    if (NS_WARN_IF(!ref)) {
    if (NS_WARN_IF(!mRef)) {
      // Already referring the start of the container.
      return nullptr;
    }
    return ref;
    return mRef;
  }

  enum class OffsetFilter { kValidOffsets, kValidOrInvalidOffsets };
@@ -260,21 +170,18 @@ class RangeBoundaryBase {
    switch (aOffsetFilter) {
      case OffsetFilter::kValidOffsets: {
        if (IsSetAndValid()) {
          MOZ_ASSERT_IF(!mIsMutationObserved, mOffset);
          if (!mOffset && mIsMutationObserved) {
          if (!mOffset) {
            DetermineOffsetFromReference();
          }
        }
        return !mIsMutationObserved && *mOffset > Container()->Length()
                   ? Nothing{}
                   : mOffset;
        return mOffset;
      }
      case OffsetFilter::kValidOrInvalidOffsets: {
        if (mOffset.isSome()) {
          return mOffset;
        }
        MOZ_ASSERT_IF(!mIsMutationObserved, mOffset);
        if (mParent && mIsMutationObserved) {

        if (mParent) {
          DetermineOffsetFromReference();
          if (mOffset.isSome()) {
            return mOffset;
@@ -300,16 +207,11 @@ class RangeBoundaryBase {
      aStream << " (" << *aRangeBoundary.Container()
              << ", Length()=" << aRangeBoundary.Container()->Length() << ")";
    }
    if (aRangeBoundary.mIsMutationObserved) {
      aStream << ", mRef=" << aRangeBoundary.mRef;
      if (aRangeBoundary.mRef) {
        aStream << " (" << *aRangeBoundary.mRef << ")";
      }
    aStream << ", mRef=" << aRangeBoundary.Ref();
    if (aRangeBoundary.Ref()) {
      aStream << " (" << *aRangeBoundary.Ref() << ")";
    }

    aStream << ", mOffset=" << aRangeBoundary.mOffset;
    aStream << ", mIsMutationObserved="
            << (aRangeBoundary.mIsMutationObserved ? "true" : "false") << " }";
    aStream << ", mOffset=" << aRangeBoundary.mOffset << " }";
    return aStream;
  }

@@ -318,7 +220,6 @@ class RangeBoundaryBase {
    MOZ_ASSERT(mParent);
    MOZ_ASSERT(mRef);
    MOZ_ASSERT(mRef->GetParentNode() == mParent);
    MOZ_ASSERT(mIsMutationObserved);
    MOZ_ASSERT(mOffset.isNothing());

    if (mRef->IsBeingRemoved()) {
@@ -336,12 +237,7 @@ class RangeBoundaryBase {
    MOZ_ASSERT(mParent);
    MOZ_ASSERT(mParent->IsContainerNode(),
               "Range is positioned on a text node!");
    if (!mIsMutationObserved) {
      // RangeBoundaries that are not used in the context of a
      // `MutationObserver` use the offset as main source of truth to compute
      // `mRef`. Therefore, it must not be updated or invalidated.
      return;
    }

    if (!mRef) {
      MOZ_ASSERT(mOffset.isSome() && mOffset.value() == 0,
                 "Invalidating offset of invalid RangeBoundary?");
@@ -358,7 +254,7 @@ class RangeBoundaryBase {
      return false;
    }

    if (mIsMutationObserved && Ref()) {
    if (Ref()) {
      // XXX mRef refers previous sibling of pointing child.  Therefore, it
      //     seems odd that this becomes invalid due to its removal.  Should we
      //     change RangeBoundaryBase to refer child at offset directly?
@@ -373,8 +269,7 @@ class RangeBoundaryBase {
    // We're at the first point in the container if we don't have a reference,
    // and our offset is 0. If we don't have a Ref, we should already have an
    // offset, so we can just directly fetch it.
    return mIsMutationObserved ? !Ref() && mOffset.value() == 0
                               : mOffset.value() == 0;
    return !Ref() && mOffset.value() == 0;
  }

  bool IsEndOfContainer() const {
@@ -382,25 +277,18 @@ class RangeBoundaryBase {
    // child in Container(), or our Offset() is the same as the length of our
    // container. If we don't have a Ref, then we should already have an offset,
    // so we can just directly fetch it.
    return mIsMutationObserved && Ref()
               ? !Ref()->GetNextSibling()
    return Ref() ? !Ref()->GetNextSibling()
                 : mOffset.value() == Container()->Length();
  }

  // Convenience methods for switching between the two types
  // of RangeBoundary.
  RangeBoundaryBase<nsINode*, nsIContent*> AsRaw() const {
    return RangeBoundaryBase<nsINode*, nsIContent*>(
        *this, RangeBoundaryIsMutationObserved(mIsMutationObserved));
    return RangeBoundaryBase<nsINode*, nsIContent*>(*this);
  }

  template <typename A, typename B>
  RangeBoundaryBase& operator=(const RangeBoundaryBase<A, B>& aOther) = delete;

  template <typename A, typename B>
  RangeBoundaryBase& CopyFrom(
      const RangeBoundaryBase<A, B>& aOther,
      RangeBoundaryIsMutationObserved aIsMutationObserved) {
  RangeBoundaryBase& operator=(const RangeBoundaryBase<A, B>& aOther) {
    // mParent and mRef can be strong pointers, so better to try to avoid any
    // extra AddRef/Release calls.
    if (mParent != aOther.mParent) {
@@ -410,7 +298,6 @@ class RangeBoundaryBase {
      mRef = aOther.mRef;
    }
    mOffset = aOther.mOffset;
    mIsMutationObserved = bool(aIsMutationObserved);
    return *this;
  }

@@ -426,12 +313,7 @@ class RangeBoundaryBase {
  template <typename A, typename B>
  bool operator==(const RangeBoundaryBase<A, B>& aOther) const {
    return mParent == aOther.mParent &&
           (mIsMutationObserved && aOther.mIsMutationObserved && mRef
                ? mRef == aOther.mRef
                : Offset(OffsetFilter::kValidOrInvalidOffsets) ==
                      aOther.Offset(
                          RangeBoundaryBase<
                              A, B>::OffsetFilter::kValidOrInvalidOffsets));
           (mRef ? mRef == aOther.mRef : mOffset == aOther.mOffset);
  }

  template <typename A, typename B>
@@ -441,10 +323,9 @@ class RangeBoundaryBase {

 private:
  ParentType mParent;
  mutable RefType mRef;
  RefType mRef;

  mutable mozilla::Maybe<uint32_t> mOffset;
  bool mIsMutationObserved;
};

template <typename ParentType, typename RefType>
+9 −20

File changed.

Preview size limit exceeded, changes collapsed.

Loading