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"
26 namespace mozilla::dom
{
28 template nsresult
AbstractRange::SetStartAndEndInternal(
29 const RangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
,
31 template nsresult
AbstractRange::SetStartAndEndInternal(
32 const RangeBoundary
& aStartBoundary
, const RawRangeBoundary
& aEndBoundary
,
34 template nsresult
AbstractRange::SetStartAndEndInternal(
35 const RawRangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
,
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
,
43 template nsresult
AbstractRange::SetStartAndEndInternal(
44 const RangeBoundary
& aStartBoundary
, const RawRangeBoundary
& aEndBoundary
,
46 template nsresult
AbstractRange::SetStartAndEndInternal(
47 const RawRangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
,
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
)
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
104 nsINode
* node
= *iter
;
105 if (aMarkDesendants
) {
106 node
->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
108 node
->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
111 if (node
->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
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);
131 // don't set the Descendant bit on |aNode| itself
132 nsINode
* node
= aNode
.GetNextNode(&aNode
);
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
);
141 node
= node
->GetNextNode(&aNode
);
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).
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);
164 // we know |aNode| doesn't have any bit set
165 nsINode
* node
= aNode
.GetNextNode(&aNode
);
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
);
174 node
= node
->GetNextNode(&aNode
);
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),
191 mIsDynamicRange(aIsDynamicRange
) {
192 mRefCnt
.SetIsOnMainThread();
196 AbstractRange::~AbstractRange() = default;
198 void AbstractRange::Init(nsINode
* aNode
) {
199 MOZ_ASSERT(aNode
, "range isn't in a document!");
200 mOwner
= aNode
->OwnerDoc();
204 void AbstractRange::Shutdown() {
206 if (nsTArray
<RefPtr
<nsRange
>>* cachedRanges
= nsRange::sCachedRanges
) {
207 nsRange::sCachedRanges
= nullptr;
208 cachedRanges
->Clear();
211 if (nsTArray
<RefPtr
<StaticRange
>>* cachedRanges
=
212 StaticRange::sCachedRanges
) {
213 StaticRange::sCachedRanges
= nullptr;
214 cachedRanges
->Clear();
217 if (nsTArray
<RefPtr
<CrossShadowBoundaryRange
>>* cachedRanges
=
218 CrossShadowBoundaryRange::sCachedRanges
) {
219 CrossShadowBoundaryRange::sCachedRanges
= nullptr;
220 cachedRanges
->Clear();
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
)) {
238 aInstance
.ClearForReuse();
240 if (!RangeType::sCachedRanges
) {
241 RangeType::sCachedRanges
= new nsTArray
<RefPtr
<RangeType
>>(16);
243 RangeType::sCachedRanges
->AppendElement(&aInstance
);
247 nsINode
* AbstractRange::GetClosestCommonInclusiveAncestor(
248 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary
) const {
249 if (!mIsPositioned
) {
252 nsINode
* startContainer
=
253 aAllowCrossShadowBoundary
== AllowRangeCrossShadowBoundary::Yes
254 ? GetMayCrossShadowBoundaryStartContainer()
255 : GetStartContainer();
256 nsINode
* endContainer
=
257 aAllowCrossShadowBoundary
== AllowRangeCrossShadowBoundary::Yes
258 ? GetMayCrossShadowBoundaryEndContainer()
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
) {
272 startContainer
&& startContainer
->IsDocument(),
273 !endContainer
|| endContainer
->GetComposedDoc() == startContainer
);
275 endContainer
&& endContainer
->IsDocument(),
276 !startContainer
|| startContainer
->GetComposedDoc() == endContainer
);
278 return startContainer
? startContainer
->GetComposedDoc()
279 : endContainer
->GetComposedDoc();
282 const auto rescope
= [](nsINode
*& aContainer
) {
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();
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
,
306 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
,
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());
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
);
338 aRange
->DoSetRange(aStartBoundary
, aEndBoundary
, newStartRoot
);
343 nsINode
* newEndRoot
= RangeUtils::ComputeRootNode(aEndBoundary
.Container());
345 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
347 if (!aEndBoundary
.IsSetAndValid()) {
348 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
352 if (newStartRoot
!= newEndRoot
) {
353 if (aRange
->IsStaticRange()) {
354 // StaticRange allows nodes in different trees, so set start and end
356 aRange
->DoSetRange(aStartBoundary
, aEndBoundary
, newEndRoot
);
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
,
376 const Maybe
<int32_t> pointOrder
=
377 nsContentUtils::ComparePoints(aStartBoundary
, aEndBoundary
);
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
386 if (*pointOrder
== 1) {
387 aRange
->DoSetRange(aEndBoundary
, aEndBoundary
, newEndRoot
);
391 // Otherwise, set the range as specified.
392 aRange
->DoSetRange(aStartBoundary
, aEndBoundary
, newStartRoot
);
396 bool AbstractRange::IsInSelection(const Selection
& aSelection
) const {
397 return mSelections
.Contains(&aSelection
);
400 void AbstractRange::RegisterSelection(Selection
& aSelection
) {
401 if (IsInSelection(aSelection
)) {
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 {
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(
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();
448 ranges
= MakeUnique
<LinkedList
<AbstractRange
>>();
451 MOZ_DIAGNOSTIC_ASSERT(!isInList());
452 ranges
->insertBack(this);
453 aNode
->SetClosestCommonInclusiveAncestorForRangeInSelection();
456 void AbstractRange::UnregisterClosestCommonInclusiveAncestor(
458 if (!mRegisteredClosestCommonInclusiveAncestor
) {
461 nsCOMPtr oldClosestCommonInclusiveAncestor
=
462 mRegisteredClosestCommonInclusiveAncestor
;
463 mRegisteredClosestCommonInclusiveAncestor
= nullptr;
464 LinkedList
<AbstractRange
>* ranges
=
465 oldClosestCommonInclusiveAncestor
466 ->GetExistingClosestCommonInclusiveAncestorRanges();
471 for (AbstractRange
* range
: *ranges
) {
478 "We should be in the list on our registered common ancestor");
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
);
503 MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned
, "unexpected disconnected nodes");
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()
521 const RangeBoundary
& AbstractRange::MayCrossShadowBoundaryEndRef() const {
522 return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryEndRef()
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()
550 bool AbstractRange::MayCrossShadowBoundary() const {
551 return IsDynamicRange() ? !!AsDynamicRange()->GetCrossShadowBoundaryRange()
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 {
581 // We know normal range is collapsed at this point
582 if (IsStaticRange()) {
586 if (const CrossShadowBoundaryRange
* crossShadowBoundaryRange
=
587 AsDynamicRange()->GetCrossShadowBoundaryRange()) {
588 return crossShadowBoundaryRange
->Collapsed();
594 void AbstractRange::ClearForReuse() {
596 mStart
= RangeBoundary();
597 mEnd
= RangeBoundary();
598 mIsPositioned
= false;
599 mIsGenerated
= false;
604 bool AbstractRange::IsRootUAWidget(const nsINode
* aRoot
) {
606 if (const ShadowRoot
* shadowRoot
= ShadowRoot::FromNode(aRoot
)) {
607 return shadowRoot
->IsUAWidget();
611 } // namespace mozilla::dom