Backed out changeset b462e7b742d8 (bug 1908261) for causing multiple reftest failures...
[gecko.git] / dom / base / AbstractRange.cpp
blob4d72fea8e12cf062e8083ee6de041dd137f59814
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/AbstractRange.h"
8 #include "mozilla/dom/AbstractRangeBinding.h"
9 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/RangeUtils.h"
14 #include "mozilla/dom/ChildIterator.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/StaticRange.h"
17 #include "mozilla/dom/Selection.h"
18 #include "mozilla/dom/CrossShadowBoundaryRange.h"
19 #include "nsContentUtils.h"
20 #include "nsCycleCollectionParticipant.h"
21 #include "nsGkAtoms.h"
22 #include "nsINode.h"
23 #include "nsRange.h"
24 #include "nsTArray.h"
26 namespace mozilla::dom {
28 template nsresult AbstractRange::SetStartAndEndInternal(
29 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
30 nsRange* aRange);
31 template nsresult AbstractRange::SetStartAndEndInternal(
32 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
33 nsRange* aRange);
34 template nsresult AbstractRange::SetStartAndEndInternal(
35 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
36 nsRange* aRange);
37 template nsresult AbstractRange::SetStartAndEndInternal(
38 const RawRangeBoundary& aStartBoundary,
39 const RawRangeBoundary& aEndBoundary, nsRange* aRange);
40 template nsresult AbstractRange::SetStartAndEndInternal(
41 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
42 StaticRange* aRange);
43 template nsresult AbstractRange::SetStartAndEndInternal(
44 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
45 StaticRange* aRange);
46 template nsresult AbstractRange::SetStartAndEndInternal(
47 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
48 StaticRange* aRange);
49 template nsresult AbstractRange::SetStartAndEndInternal(
50 const RawRangeBoundary& aStartBoundary,
51 const RawRangeBoundary& aEndBoundary, StaticRange* aRange);
52 template bool AbstractRange::MaybeCacheToReuse(nsRange& aInstance);
53 template bool AbstractRange::MaybeCacheToReuse(StaticRange& aInstance);
54 template bool AbstractRange::MaybeCacheToReuse(
55 CrossShadowBoundaryRange& aInstance);
57 bool AbstractRange::sHasShutDown = false;
59 NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractRange)
60 NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractRange)
62 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbstractRange)
63 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
64 NS_INTERFACE_MAP_ENTRY(nsISupports)
65 NS_INTERFACE_MAP_END
67 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(AbstractRange)
69 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractRange)
70 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
71 // mStart and mEnd may depend on or be depended on some other members in
72 // concrete classes so that they should be unlinked in sub classes.
73 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
74 tmp->mSelections.Clear();
75 // Unregistering of the common inclusive ancestors would by design
76 // also happen when the actual implementations unlink `mStart`/`mEnd`.
77 // This may introduce additional overhead which is not needed when unlinking,
78 // therefore this is done here beforehand.
79 if (tmp->mRegisteredClosestCommonInclusiveAncestor) {
80 tmp->UnregisterClosestCommonInclusiveAncestor(true);
82 MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(),
83 "Shouldn't be registered now that we're unlinking");
85 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
87 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractRange)
88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
89 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
90 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
91 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegisteredClosestCommonInclusiveAncestor)
92 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
94 // When aMarkDesendants is true, Set
95 // DescendantOfClosestCommonInclusiveAncestorForRangeInSelection flag for the
96 // shadow including children of aNode. When aMarkDesendants is false, unset that
97 // flag for the shadow including children of aNode.
98 void UpdateDescendantsByShadowIncludingOrder(const nsIContent& aNode,
99 bool aMarkDesendants) {
100 ShadowIncludingTreeIterator iter(*const_cast<nsIContent*>(&aNode));
101 ++iter; // We don't want to mark the root node
103 while (iter) {
104 nsINode* node = *iter;
105 if (aMarkDesendants) {
106 node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
107 } else {
108 node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
111 if (node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
112 iter.SkipChildren();
113 continue;
115 ++iter;
119 void AbstractRange::MarkDescendants(const nsINode& aNode) {
120 // Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on
121 // aNode's descendants unless aNode is already marked as a range common
122 // ancestor or a descendant of one, in which case all of our descendants have
123 // the bit set already.
124 if (!aNode.IsMaybeSelected()) {
125 // If aNode has a web-exposed shadow root, use this shadow tree and ignore
126 // the children of aNode.
127 if (aNode.GetShadowRootForSelection()) {
128 UpdateDescendantsByShadowIncludingOrder(*aNode.AsContent(), true);
129 return;
131 // don't set the Descendant bit on |aNode| itself
132 nsINode* node = aNode.GetNextNode(&aNode);
133 while (node) {
134 node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
135 if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
136 if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
137 UpdateDescendantsByShadowIncludingOrder(*node->AsContent(), true);
138 // sub-tree of node has been marked already
139 node = node->GetNextNonChildNode(&aNode);
140 } else {
141 node = node->GetNextNode(&aNode);
143 } else {
144 // optimize: skip this sub-tree since it's marked already.
145 node = node->GetNextNonChildNode(&aNode);
151 void AbstractRange::UnmarkDescendants(const nsINode& aNode) {
152 // Unset NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection
153 // on aNode's descendants unless aNode is a descendant of another range common
154 // ancestor. Also, exclude descendants of range common ancestors (but not the
155 // common ancestor itself).
156 if (!aNode
157 .IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
158 // If aNode has a web-exposed shadow root, use this shadow tree and ignore
159 // the children of aNode.
160 if (aNode.GetShadowRootForSelection()) {
161 UpdateDescendantsByShadowIncludingOrder(*aNode.AsContent(), false);
162 return;
164 // we know |aNode| doesn't have any bit set
165 nsINode* node = aNode.GetNextNode(&aNode);
166 while (node) {
167 node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
168 if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
169 if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
170 UpdateDescendantsByShadowIncludingOrder(*node->AsContent(), false);
171 // sub-tree has been marked already
172 node = node->GetNextNonChildNode(&aNode);
173 } else {
174 node = node->GetNextNode(&aNode);
176 } else {
177 // We found an ancestor of an overlapping range, skip its descendants.
178 node = node->GetNextNonChildNode(&aNode);
184 // NOTE: If you need to change default value of members of AbstractRange,
185 // update nsRange::Create(nsINode* aNode) and ClearForReuse() too.
186 AbstractRange::AbstractRange(nsINode* aNode, bool aIsDynamicRange)
187 : mRegisteredClosestCommonInclusiveAncestor(nullptr),
188 mIsPositioned(false),
189 mIsGenerated(false),
190 mCalledByJS(false),
191 mIsDynamicRange(aIsDynamicRange) {
192 mRefCnt.SetIsOnMainThread();
193 Init(aNode);
196 AbstractRange::~AbstractRange() = default;
198 void AbstractRange::Init(nsINode* aNode) {
199 MOZ_ASSERT(aNode, "range isn't in a document!");
200 mOwner = aNode->OwnerDoc();
203 // static
204 void AbstractRange::Shutdown() {
205 sHasShutDown = true;
206 if (nsTArray<RefPtr<nsRange>>* cachedRanges = nsRange::sCachedRanges) {
207 nsRange::sCachedRanges = nullptr;
208 cachedRanges->Clear();
209 delete cachedRanges;
211 if (nsTArray<RefPtr<StaticRange>>* cachedRanges =
212 StaticRange::sCachedRanges) {
213 StaticRange::sCachedRanges = nullptr;
214 cachedRanges->Clear();
215 delete cachedRanges;
217 if (nsTArray<RefPtr<CrossShadowBoundaryRange>>* cachedRanges =
218 CrossShadowBoundaryRange::sCachedRanges) {
219 CrossShadowBoundaryRange::sCachedRanges = nullptr;
220 cachedRanges->Clear();
221 delete cachedRanges;
225 // static
226 template <class RangeType>
227 bool AbstractRange::MaybeCacheToReuse(RangeType& aInstance) {
228 static const size_t kMaxRangeCache = 64;
230 // If the instance is not used by JS and the cache is not yet full, we
231 // should reuse it. Otherwise, delete it.
232 if (sHasShutDown || aInstance.GetWrapperMaybeDead() || aInstance.GetFlags() ||
233 (RangeType::sCachedRanges &&
234 RangeType::sCachedRanges->Length() == kMaxRangeCache)) {
235 return false;
238 aInstance.ClearForReuse();
240 if (!RangeType::sCachedRanges) {
241 RangeType::sCachedRanges = new nsTArray<RefPtr<RangeType>>(16);
243 RangeType::sCachedRanges->AppendElement(&aInstance);
244 return true;
247 nsINode* AbstractRange::GetClosestCommonInclusiveAncestor(
248 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const {
249 if (!mIsPositioned) {
250 return nullptr;
252 nsINode* startContainer =
253 aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
254 ? GetMayCrossShadowBoundaryStartContainer()
255 : GetStartContainer();
256 nsINode* endContainer =
257 aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
258 ? GetMayCrossShadowBoundaryEndContainer()
259 : GetEndContainer();
261 if (MayCrossShadowBoundary() &&
262 aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
263 // Since both the start container and the end container are
264 // guaranteed to be in the same composed document.
265 // If one of the boundary is a document, use that document
266 // as the common ancestor since both nodes.
267 const bool oneBoundaryIsDocument =
268 (startContainer && startContainer->IsDocument()) ||
269 (endContainer && endContainer->IsDocument());
270 if (oneBoundaryIsDocument) {
271 MOZ_ASSERT_IF(
272 startContainer && startContainer->IsDocument(),
273 !endContainer || endContainer->GetComposedDoc() == startContainer);
274 MOZ_ASSERT_IF(
275 endContainer && endContainer->IsDocument(),
276 !startContainer || startContainer->GetComposedDoc() == endContainer);
278 return startContainer ? startContainer->GetComposedDoc()
279 : endContainer->GetComposedDoc();
282 const auto rescope = [](nsINode*& aContainer) {
283 if (!aContainer) {
284 return;
286 // RangeBoundary allows the container to be shadow roots; When
287 // this happens, we should use the shadow host here.
288 if (auto* shadowRoot = ShadowRoot::FromNode(aContainer)) {
289 aContainer = shadowRoot->GetHost();
290 return;
294 rescope(startContainer);
295 rescope(endContainer);
297 return nsContentUtils::GetCommonFlattenedTreeAncestorForSelection(
298 startContainer ? startContainer->AsContent() : nullptr,
299 endContainer ? endContainer->AsContent() : nullptr);
301 return nsContentUtils::GetClosestCommonInclusiveAncestor(startContainer,
302 endContainer);
305 // static
306 template <typename SPT, typename SRT, typename EPT, typename ERT,
307 typename RangeType>
308 nsresult AbstractRange::SetStartAndEndInternal(
309 const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
310 const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange) {
311 if (NS_WARN_IF(!aStartBoundary.IsSet()) ||
312 NS_WARN_IF(!aEndBoundary.IsSet())) {
313 return NS_ERROR_INVALID_ARG;
316 nsINode* newStartRoot =
317 RangeUtils::ComputeRootNode(aStartBoundary.Container());
318 if (!newStartRoot) {
319 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
321 if (!aStartBoundary.IsSetAndValid()) {
322 return NS_ERROR_DOM_INDEX_SIZE_ERR;
325 if (aStartBoundary.Container() == aEndBoundary.Container()) {
326 if (!aEndBoundary.IsSetAndValid()) {
327 return NS_ERROR_DOM_INDEX_SIZE_ERR;
329 // XXX: Offsets - handle this more efficiently.
330 // If the end offset is less than the start offset, this should be
331 // collapsed at the end offset.
332 if (*aStartBoundary.Offset(
333 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOffsets) >
334 *aEndBoundary.Offset(
335 RangeBoundaryBase<EPT, ERT>::OffsetFilter::kValidOffsets)) {
336 aRange->DoSetRange(aEndBoundary, aEndBoundary, newStartRoot);
337 } else {
338 aRange->DoSetRange(aStartBoundary, aEndBoundary, newStartRoot);
340 return NS_OK;
343 nsINode* newEndRoot = RangeUtils::ComputeRootNode(aEndBoundary.Container());
344 if (!newEndRoot) {
345 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
347 if (!aEndBoundary.IsSetAndValid()) {
348 return NS_ERROR_DOM_INDEX_SIZE_ERR;
351 // Different root
352 if (newStartRoot != newEndRoot) {
353 if (aRange->IsStaticRange()) {
354 // StaticRange allows nodes in different trees, so set start and end
355 // accordingly
356 aRange->DoSetRange(aStartBoundary, aEndBoundary, newEndRoot);
357 } else {
358 MOZ_ASSERT(aRange->IsDynamicRange());
359 // In contrast, nsRange keeps both. It has a pair of start and end
360 // which they have been collapsed to one end, and it also may have a pair
361 // of start and end which are the original value.
362 aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot);
364 // Don't create the cross shadow bounday range if the one of the roots is
365 // an UA widget regardless whether the boundaries are allowed to cross
366 // shadow boundary or not.
367 if (!IsRootUAWidget(newStartRoot) && !IsRootUAWidget(newEndRoot)) {
368 aRange->AsDynamicRange()
369 ->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(aStartBoundary,
370 aEndBoundary);
373 return NS_OK;
376 const Maybe<int32_t> pointOrder =
377 nsContentUtils::ComparePoints(aStartBoundary, aEndBoundary);
378 if (!pointOrder) {
379 // Safely return a value but also detected this in debug builds.
380 MOZ_ASSERT_UNREACHABLE();
381 return NS_ERROR_INVALID_ARG;
384 // If the end point is before the start point, this should be collapsed at
385 // the end point.
386 if (*pointOrder == 1) {
387 aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot);
388 return NS_OK;
391 // Otherwise, set the range as specified.
392 aRange->DoSetRange(aStartBoundary, aEndBoundary, newStartRoot);
393 return NS_OK;
396 bool AbstractRange::IsInSelection(const Selection& aSelection) const {
397 return mSelections.Contains(&aSelection);
400 void AbstractRange::RegisterSelection(Selection& aSelection) {
401 if (IsInSelection(aSelection)) {
402 return;
404 bool isFirstSelection = mSelections.IsEmpty();
405 mSelections.AppendElement(&aSelection);
406 if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) {
407 nsINode* commonAncestor = GetClosestCommonInclusiveAncestor(
408 StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
409 ? AllowRangeCrossShadowBoundary::Yes
410 : AllowRangeCrossShadowBoundary::No);
411 MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes");
412 RegisterClosestCommonInclusiveAncestor(commonAncestor);
416 const nsTArray<WeakPtr<Selection>>& AbstractRange::GetSelections() const {
417 return mSelections;
420 void AbstractRange::UnregisterSelection(const Selection& aSelection) {
421 mSelections.RemoveElement(&aSelection);
422 if (mSelections.IsEmpty() && mRegisteredClosestCommonInclusiveAncestor) {
423 UnregisterClosestCommonInclusiveAncestor();
424 MOZ_DIAGNOSTIC_ASSERT(
425 !mRegisteredClosestCommonInclusiveAncestor,
426 "How can we have a registered common ancestor when we "
427 "just unregistered?");
428 MOZ_DIAGNOSTIC_ASSERT(
429 !isInList(),
430 "Shouldn't be registered if we have no "
431 "mRegisteredClosestCommonInclusiveAncestor after unregistering");
435 void AbstractRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) {
436 MOZ_ASSERT(aNode, "bad arg");
438 MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(),
439 "registering range not in selection");
441 mRegisteredClosestCommonInclusiveAncestor = aNode;
443 MarkDescendants(*aNode);
445 UniquePtr<LinkedList<AbstractRange>>& ranges =
446 aNode->GetClosestCommonInclusiveAncestorRangesPtr();
447 if (!ranges) {
448 ranges = MakeUnique<LinkedList<AbstractRange>>();
451 MOZ_DIAGNOSTIC_ASSERT(!isInList());
452 ranges->insertBack(this);
453 aNode->SetClosestCommonInclusiveAncestorForRangeInSelection();
456 void AbstractRange::UnregisterClosestCommonInclusiveAncestor(
457 bool aIsUnlinking) {
458 if (!mRegisteredClosestCommonInclusiveAncestor) {
459 return;
461 nsCOMPtr oldClosestCommonInclusiveAncestor =
462 mRegisteredClosestCommonInclusiveAncestor;
463 mRegisteredClosestCommonInclusiveAncestor = nullptr;
464 LinkedList<AbstractRange>* ranges =
465 oldClosestCommonInclusiveAncestor
466 ->GetExistingClosestCommonInclusiveAncestorRanges();
467 MOZ_ASSERT(ranges);
469 #ifdef DEBUG
470 bool found = false;
471 for (AbstractRange* range : *ranges) {
472 if (range == this) {
473 found = true;
474 break;
477 MOZ_ASSERT(found,
478 "We should be in the list on our registered common ancestor");
479 #endif // DEBUG
481 remove();
483 // We don't want to waste time unmarking flags on nodes that are
484 // being unlinked anyway.
485 if (!aIsUnlinking && ranges->isEmpty()) {
486 oldClosestCommonInclusiveAncestor
487 ->ClearClosestCommonInclusiveAncestorForRangeInSelection();
488 UnmarkDescendants(*oldClosestCommonInclusiveAncestor);
490 oldClosestCommonInclusiveAncestor = nullptr;
493 void AbstractRange::UpdateCommonAncestorIfNecessary() {
494 nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor;
495 nsINode* newCommonAncestor =
496 GetClosestCommonInclusiveAncestor(AllowRangeCrossShadowBoundary::Yes);
497 if (newCommonAncestor != oldCommonAncestor) {
498 UnregisterClosestCommonInclusiveAncestor();
500 if (newCommonAncestor) {
501 RegisterClosestCommonInclusiveAncestor(newCommonAncestor);
502 } else {
503 MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes");
504 mSelections.Clear();
505 MOZ_DIAGNOSTIC_ASSERT(
506 !mRegisteredClosestCommonInclusiveAncestor,
507 "How can we have a registered common ancestor when we "
508 "didn't register ourselves?");
509 MOZ_DIAGNOSTIC_ASSERT(!isInList(),
510 "Shouldn't be registered if we have no "
511 "mRegisteredClosestCommonInclusiveAncestor");
516 const RangeBoundary& AbstractRange::MayCrossShadowBoundaryStartRef() const {
517 return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryStartRef()
518 : mStart;
521 const RangeBoundary& AbstractRange::MayCrossShadowBoundaryEndRef() const {
522 return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryEndRef()
523 : mEnd;
526 nsIContent* AbstractRange::GetMayCrossShadowBoundaryChildAtStartOffset() const {
527 return IsDynamicRange()
528 ? AsDynamicRange()->GetMayCrossShadowBoundaryChildAtStartOffset()
529 : mStart.GetChildAtOffset();
532 nsIContent* AbstractRange::GetMayCrossShadowBoundaryChildAtEndOffset() const {
533 return IsDynamicRange()
534 ? AsDynamicRange()->GetMayCrossShadowBoundaryChildAtEndOffset()
535 : mEnd.GetChildAtOffset();
538 nsINode* AbstractRange::GetMayCrossShadowBoundaryStartContainer() const {
539 return IsDynamicRange()
540 ? AsDynamicRange()->GetMayCrossShadowBoundaryStartContainer()
541 : mStart.Container();
544 nsINode* AbstractRange::GetMayCrossShadowBoundaryEndContainer() const {
545 return IsDynamicRange()
546 ? AsDynamicRange()->GetMayCrossShadowBoundaryEndContainer()
547 : mEnd.Container();
550 bool AbstractRange::MayCrossShadowBoundary() const {
551 return IsDynamicRange() ? !!AsDynamicRange()->GetCrossShadowBoundaryRange()
552 : false;
555 uint32_t AbstractRange::MayCrossShadowBoundaryStartOffset() const {
556 return IsDynamicRange()
557 ? AsDynamicRange()->MayCrossShadowBoundaryStartOffset()
558 : static_cast<uint32_t>(*mStart.Offset(
559 RangeBoundary::OffsetFilter::kValidOrInvalidOffsets));
562 uint32_t AbstractRange::MayCrossShadowBoundaryEndOffset() const {
563 return IsDynamicRange()
564 ? AsDynamicRange()->MayCrossShadowBoundaryEndOffset()
565 : static_cast<uint32_t>(*mEnd.Offset(
566 RangeBoundary::OffsetFilter::kValidOrInvalidOffsets));
569 nsINode* AbstractRange::GetParentObject() const { return mOwner; }
571 JSObject* AbstractRange::WrapObject(JSContext* aCx,
572 JS::Handle<JSObject*> aGivenProto) {
573 MOZ_CRASH("Must be overridden");
576 bool AbstractRange::AreNormalRangeAndCrossShadowBoundaryRangeCollapsed() const {
577 if (!Collapsed()) {
578 return false;
581 // We know normal range is collapsed at this point
582 if (IsStaticRange()) {
583 return true;
586 if (const CrossShadowBoundaryRange* crossShadowBoundaryRange =
587 AsDynamicRange()->GetCrossShadowBoundaryRange()) {
588 return crossShadowBoundaryRange->Collapsed();
591 return true;
594 void AbstractRange::ClearForReuse() {
595 mOwner = nullptr;
596 mStart = RangeBoundary();
597 mEnd = RangeBoundary();
598 mIsPositioned = false;
599 mIsGenerated = false;
600 mCalledByJS = false;
603 /*static*/
604 bool AbstractRange::IsRootUAWidget(const nsINode* aRoot) {
605 MOZ_ASSERT(aRoot);
606 if (const ShadowRoot* shadowRoot = ShadowRoot::FromNode(aRoot)) {
607 return shadowRoot->IsUAWidget();
609 return false;
611 } // namespace mozilla::dom