AbstractRange.cpp (24370B)
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/. */ 6 7 #include "mozilla/dom/AbstractRange.h" 8 9 #include "mozilla/Assertions.h" 10 #include "mozilla/RangeUtils.h" 11 #include "mozilla/SelectionMovementUtils.h" 12 #include "mozilla/dom/AbstractRangeBinding.h" 13 #include "mozilla/dom/ChildIterator.h" 14 #include "mozilla/dom/CrossShadowBoundaryRange.h" 15 #include "mozilla/dom/Document.h" 16 #include "mozilla/dom/DocumentInlines.h" 17 #include "mozilla/dom/Selection.h" 18 #include "mozilla/dom/ShadowIncludingTreeIterator.h" 19 #include "mozilla/dom/StaticRange.h" 20 #include "mozilla/dom/TreeIterator.h" 21 #include "nsContentUtils.h" 22 #include "nsCycleCollectionParticipant.h" 23 #include "nsGkAtoms.h" 24 #include "nsINode.h" 25 #include "nsRange.h" 26 #include "nsTArray.h" 27 28 namespace mozilla::dom { 29 30 template nsresult AbstractRange::SetStartAndEndInternal( 31 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, 32 nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 33 template nsresult AbstractRange::SetStartAndEndInternal( 34 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary, 35 nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 36 template nsresult AbstractRange::SetStartAndEndInternal( 37 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, 38 nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 39 template nsresult AbstractRange::SetStartAndEndInternal( 40 const RawRangeBoundary& aStartBoundary, 41 const RawRangeBoundary& aEndBoundary, nsRange* aRange, 42 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 43 template nsresult AbstractRange::SetStartAndEndInternal( 44 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, 45 StaticRange* aRange, 46 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 47 template nsresult AbstractRange::SetStartAndEndInternal( 48 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary, 49 StaticRange* aRange, 50 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 51 template nsresult AbstractRange::SetStartAndEndInternal( 52 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, 53 StaticRange* aRange, 54 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 55 template nsresult AbstractRange::SetStartAndEndInternal( 56 const RawRangeBoundary& aStartBoundary, 57 const RawRangeBoundary& aEndBoundary, StaticRange* aRange, 58 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 59 template bool AbstractRange::MaybeCacheToReuse(nsRange& aInstance); 60 template bool AbstractRange::MaybeCacheToReuse(StaticRange& aInstance); 61 template bool AbstractRange::MaybeCacheToReuse( 62 CrossShadowBoundaryRange& aInstance); 63 64 bool AbstractRange::sHasShutDown = false; 65 66 NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractRange) 67 NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractRange) 68 69 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbstractRange) 70 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 71 NS_INTERFACE_MAP_ENTRY(nsISupports) 72 NS_INTERFACE_MAP_END 73 74 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(AbstractRange) 75 76 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractRange) 77 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner); 78 // mStart and mEnd may depend on or be depended on some other members in 79 // concrete classes so that they should be unlinked in sub classes. 80 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 81 tmp->mSelections.Clear(); 82 // Unregistering of the common inclusive ancestors would by design 83 // also happen when the actual implementations unlink `mStart`/`mEnd`. 84 // This may introduce additional overhead which is not needed when unlinking, 85 // therefore this is done here beforehand. 86 if (tmp->mRegisteredClosestCommonInclusiveAncestor) { 87 tmp->UnregisterClosestCommonInclusiveAncestor(IsUnlinking::Yes); 88 } 89 MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(), 90 "Shouldn't be registered now that we're unlinking"); 91 92 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 93 94 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractRange) 95 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) 96 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart) 97 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd) 98 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegisteredClosestCommonInclusiveAncestor) 99 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 100 101 static void UpdateDescendantsInSameTree(const nsINode& aNode, 102 bool aMarkDesendants) { 103 MOZ_ASSERT(!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()); 104 // don't set the Descendant bit on |aNode| itself 105 nsINode* node = aNode.GetNextNode(&aNode); 106 while (node) { 107 if (aMarkDesendants) { 108 node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 109 } else { 110 node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 111 } 112 113 if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) { 114 node = node->GetNextNode(&aNode); 115 } else { 116 // We found an ancestor of an overlapping range, skip its descendants. 117 node = node->GetNextNonChildNode(&aNode); 118 } 119 } 120 } 121 122 void AbstractRange::UpdateDescendantsInFlattenedTree(nsINode& aNode, 123 bool aMarkDescendants) { 124 MOZ_ASSERT(StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()); 125 126 auto UpdateDescendant = [aMarkDescendants](nsINode* node) { 127 if (aMarkDescendants) { 128 node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 129 } else { 130 node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 131 } 132 }; 133 134 nsINode* target = &aNode; 135 136 if (target->IsDocument()) { 137 if (auto* rootElement = aNode.AsDocument()->GetRootElement()) { 138 target = rootElement; 139 UpdateDescendant(target); 140 } 141 } 142 143 if (!target || !target->IsContent()) { 144 return; 145 } 146 147 TreeIterator<FlattenedChildIterator> iter(*target->AsContent()); 148 iter.GetNext(); // Skip aNode itself. 149 while (nsIContent* curNode = iter.GetCurrent()) { 150 UpdateDescendant(curNode); 151 if (curNode->IsClosestCommonInclusiveAncestorForRangeInSelection()) { 152 iter.GetNextSkippingChildren(); 153 } else { 154 iter.GetNext(); 155 } 156 } 157 } 158 159 void AbstractRange::MarkDescendants(nsINode& aNode) { 160 // Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on 161 // aNode's descendants unless aNode is already marked as a range common 162 // ancestor or a descendant of one, in which case all of our descendants have 163 // the bit set already. 164 if (!aNode.IsMaybeSelected()) { 165 // If aNode has a web-exposed shadow root, use this shadow tree and ignore 166 // the children of aNode. 167 168 if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { 169 UpdateDescendantsInFlattenedTree(aNode, true /* aMarkDescendants */); 170 } else { 171 UpdateDescendantsInSameTree(aNode, true /* aMarkDescendants */); 172 } 173 } 174 } 175 176 void AbstractRange::UnmarkDescendants(nsINode& aNode) { 177 // Unset NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection 178 // on aNode's descendants unless aNode is a descendant of another range common 179 // ancestor. Also, exclude descendants of range common ancestors (but not the 180 // common ancestor itself). 181 if (!aNode 182 .IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { 183 if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { 184 UpdateDescendantsInFlattenedTree(aNode, false /* aMarkDescendants */); 185 } else { 186 UpdateDescendantsInSameTree(aNode, false /* aMarkDescendants */); 187 } 188 } 189 } 190 191 // NOTE: If you need to change default value of members of AbstractRange, 192 // update nsRange::Create(nsINode* aNode) and ClearForReuse() too. 193 AbstractRange::AbstractRange(nsINode* aNode, bool aIsDynamicRange, 194 TreeKind aBoundaryTreeKind) 195 : mStart(aBoundaryTreeKind), 196 mEnd(aBoundaryTreeKind), 197 mRegisteredClosestCommonInclusiveAncestor(nullptr), 198 mIsPositioned(false), 199 mIsGenerated(false), 200 mCalledByJS(false), 201 mIsDynamicRange(aIsDynamicRange) { 202 mRefCnt.SetIsOnMainThread(); 203 Init(aNode); 204 } 205 206 AbstractRange::~AbstractRange() = default; 207 208 void AbstractRange::Init(nsINode* aNode) { 209 MOZ_ASSERT(aNode, "range isn't in a document!"); 210 mOwner = aNode->OwnerDoc(); 211 } 212 213 // static 214 void AbstractRange::Shutdown() { 215 sHasShutDown = true; 216 if (nsTArray<RefPtr<nsRange>>* cachedRanges = nsRange::sCachedRanges) { 217 nsRange::sCachedRanges = nullptr; 218 cachedRanges->Clear(); 219 delete cachedRanges; 220 } 221 if (nsTArray<RefPtr<StaticRange>>* cachedRanges = 222 StaticRange::sCachedRanges) { 223 StaticRange::sCachedRanges = nullptr; 224 cachedRanges->Clear(); 225 delete cachedRanges; 226 } 227 if (nsTArray<RefPtr<CrossShadowBoundaryRange>>* cachedRanges = 228 CrossShadowBoundaryRange::sCachedRanges) { 229 CrossShadowBoundaryRange::sCachedRanges = nullptr; 230 cachedRanges->Clear(); 231 delete cachedRanges; 232 } 233 } 234 235 // static 236 template <class RangeType> 237 bool AbstractRange::MaybeCacheToReuse(RangeType& aInstance) { 238 static const size_t kMaxRangeCache = 64; 239 240 // If the instance is not used by JS and the cache is not yet full, we 241 // should reuse it. Otherwise, delete it. 242 if (sHasShutDown || aInstance.GetWrapperMaybeDead() || aInstance.GetFlags() || 243 (RangeType::sCachedRanges && 244 RangeType::sCachedRanges->Length() == kMaxRangeCache)) { 245 return false; 246 } 247 248 aInstance.ClearForReuse(); 249 250 if (!RangeType::sCachedRanges) { 251 RangeType::sCachedRanges = new nsTArray<RefPtr<RangeType>>(16); 252 } 253 RangeType::sCachedRanges->AppendElement(&aInstance); 254 return true; 255 } 256 257 nsINode* AbstractRange::GetClosestCommonInclusiveAncestor( 258 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const { 259 if (!mIsPositioned) { 260 return nullptr; 261 } 262 nsINode* startContainer = ShadowDOMSelectionHelpers::GetStartContainer( 263 this, aAllowCrossShadowBoundary); 264 nsINode* endContainer = ShadowDOMSelectionHelpers::GetEndContainer( 265 this, aAllowCrossShadowBoundary); 266 267 if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) { 268 if (startContainer == endContainer) { 269 return startContainer; 270 } 271 // Since both the start container and the end container are 272 // guaranteed to be in the same composed document. 273 // If one of the boundary is a document, use that document 274 // as the common ancestor since both nodes. 275 const bool oneBoundaryIsDocument = 276 (startContainer && startContainer->IsDocument()) || 277 (endContainer && endContainer->IsDocument()); 278 if (oneBoundaryIsDocument) { 279 MOZ_ASSERT_IF( 280 startContainer && startContainer->IsDocument(), 281 !endContainer || endContainer->GetComposedDoc() == startContainer); 282 MOZ_ASSERT_IF( 283 endContainer && endContainer->IsDocument(), 284 !startContainer || startContainer->GetComposedDoc() == endContainer); 285 286 return startContainer ? startContainer->GetComposedDoc() 287 : endContainer->GetComposedDoc(); 288 } 289 290 const auto rescope = [](nsINode*& aContainer) { 291 if (!aContainer) { 292 return; 293 } 294 // RangeBoundary allows the container to be shadow roots; When 295 // this happens, we should use the shadow host here. 296 if (auto* shadowRoot = ShadowRoot::FromNode(aContainer)) { 297 aContainer = shadowRoot->GetHost(); 298 return; 299 } 300 }; 301 302 rescope(startContainer); 303 rescope(endContainer); 304 305 return nsContentUtils::GetCommonFlattenedTreeAncestorForSelection( 306 startContainer ? startContainer->AsContent() : nullptr, 307 endContainer ? endContainer->AsContent() : nullptr); 308 } 309 return nsContentUtils::GetClosestCommonInclusiveAncestor(startContainer, 310 endContainer); 311 } 312 313 // static 314 template <typename SPT, typename SRT, typename EPT, typename ERT, 315 typename RangeType> 316 nsresult AbstractRange::SetStartAndEndInternal( 317 const RangeBoundaryBase<SPT, SRT>& aStartBoundary, 318 const RangeBoundaryBase<EPT, ERT>& aEndBoundary, RangeType* aRange, 319 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 320 if (NS_WARN_IF(!aStartBoundary.IsSet()) || 321 NS_WARN_IF(!aEndBoundary.IsSet())) { 322 return NS_ERROR_INVALID_ARG; 323 } 324 325 nsINode* newStartRoot = 326 RangeUtils::ComputeRootNode(aStartBoundary.GetContainer()); 327 if (!newStartRoot) { 328 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; 329 } 330 if (!aStartBoundary.IsSetAndValid()) { 331 return NS_ERROR_DOM_INDEX_SIZE_ERR; 332 } 333 334 if (aStartBoundary.GetContainer() == aEndBoundary.GetContainer()) { 335 if (!aEndBoundary.IsSetAndValid()) { 336 return NS_ERROR_DOM_INDEX_SIZE_ERR; 337 } 338 // XXX: Offsets - handle this more efficiently. 339 // If the end offset is less than the start offset, this should be 340 // collapsed at the end offset. 341 if (*aStartBoundary.Offset( 342 RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOffsets) > 343 *aEndBoundary.Offset( 344 RangeBoundaryBase<EPT, ERT>::OffsetFilter::kValidOffsets)) { 345 aRange->DoSetRange(aEndBoundary, aEndBoundary, newStartRoot); 346 } else { 347 aRange->DoSetRange(aStartBoundary, aEndBoundary, newStartRoot); 348 } 349 return NS_OK; 350 } 351 352 nsINode* newEndRoot = 353 RangeUtils::ComputeRootNode(aEndBoundary.GetContainer()); 354 if (!newEndRoot) { 355 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; 356 } 357 if (!aEndBoundary.IsSetAndValid()) { 358 return NS_ERROR_DOM_INDEX_SIZE_ERR; 359 } 360 361 // Different root 362 if (newStartRoot != newEndRoot) { 363 if (aRange->IsStaticRange()) { 364 // StaticRange allows nodes in different trees, so set start and end 365 // accordingly 366 aRange->DoSetRange(aStartBoundary, aEndBoundary, newEndRoot); 367 } else { 368 MOZ_ASSERT(aRange->IsDynamicRange()); 369 // In contrast, nsRange keeps both. It has a pair of start and end 370 // which they have been collapsed to one end, and it also may have a pair 371 // of start and end which are the original value. 372 aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot); 373 374 // Don't create the cross shadow bounday range if the one of the roots is 375 // an UA widget regardless whether the boundaries are allowed to cross 376 // shadow boundary or not. 377 if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes && 378 !IsRootUAWidget(newStartRoot) && !IsRootUAWidget(newEndRoot)) { 379 aRange->AsDynamicRange() 380 ->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 381 aStartBoundary.AsRangeBoundaryInFlatTree(), 382 aEndBoundary.AsRangeBoundaryInFlatTree()); 383 } 384 } 385 return NS_OK; 386 } 387 388 const Maybe<int32_t> pointOrder = 389 aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes && 390 StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() 391 ? nsContentUtils::ComparePoints<TreeKind::Flat>(aStartBoundary, 392 aEndBoundary) 393 : nsContentUtils::ComparePoints(aStartBoundary, aEndBoundary); 394 if (!pointOrder) { 395 // Safely return a value but also detected this in debug builds. 396 MOZ_ASSERT_UNREACHABLE(); 397 return NS_ERROR_INVALID_ARG; 398 } 399 400 // If the end point is before the start point, this should be collapsed at 401 // the end point. 402 if (*pointOrder == 1) { 403 aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot); 404 return NS_OK; 405 } 406 407 // Otherwise, set the range as specified. 408 aRange->DoSetRange(aStartBoundary, aEndBoundary, newStartRoot); 409 410 if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes && 411 aRange->IsDynamicRange()) { 412 auto startInFlat = aStartBoundary.AsRangeBoundaryInFlatTree(); 413 auto endInFlat = aEndBoundary.AsRangeBoundaryInFlatTree(); 414 415 aRange->AsDynamicRange()->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 416 startInFlat, endInFlat); 417 } 418 419 return NS_OK; 420 } 421 422 bool AbstractRange::IsInSelection(const Selection& aSelection) const { 423 return mSelections.Contains(&aSelection); 424 } 425 426 void AbstractRange::RegisterSelection(Selection& aSelection) { 427 if (IsInSelection(aSelection)) { 428 return; 429 } 430 bool isFirstSelection = mSelections.IsEmpty(); 431 mSelections.AppendElement(&aSelection); 432 if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) { 433 nsINode* commonAncestor = GetClosestCommonInclusiveAncestor( 434 StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() 435 ? AllowRangeCrossShadowBoundary::Yes 436 : AllowRangeCrossShadowBoundary::No); 437 MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes"); 438 RegisterClosestCommonInclusiveAncestor(commonAncestor); 439 } 440 } 441 442 const nsTArray<WeakPtr<Selection>>& AbstractRange::GetSelections() const { 443 return mSelections; 444 } 445 446 void AbstractRange::UnregisterSelection(const Selection& aSelection, 447 IsUnlinking aIsUnlinking) { 448 mSelections.RemoveElement(&aSelection); 449 if (mSelections.IsEmpty() && mRegisteredClosestCommonInclusiveAncestor) { 450 UnregisterClosestCommonInclusiveAncestor(aIsUnlinking); 451 MOZ_DIAGNOSTIC_ASSERT( 452 !mRegisteredClosestCommonInclusiveAncestor, 453 "How can we have a registered common ancestor when we " 454 "just unregistered?"); 455 MOZ_DIAGNOSTIC_ASSERT( 456 !isInList(), 457 "Shouldn't be registered if we have no " 458 "mRegisteredClosestCommonInclusiveAncestor after unregistering"); 459 } 460 } 461 462 void AbstractRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) { 463 MOZ_ASSERT(aNode, "bad arg"); 464 465 MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(), 466 "registering range not in selection"); 467 468 mRegisteredClosestCommonInclusiveAncestor = aNode; 469 470 MarkDescendants(*aNode); 471 472 UniquePtr<LinkedList<AbstractRange>>& ranges = 473 aNode->GetClosestCommonInclusiveAncestorRangesPtr(); 474 if (!ranges) { 475 ranges = MakeUnique<LinkedList<AbstractRange>>(); 476 } 477 478 MOZ_DIAGNOSTIC_ASSERT(!isInList()); 479 ranges->insertBack(this); 480 aNode->SetClosestCommonInclusiveAncestorForRangeInSelection(); 481 } 482 483 void AbstractRange::UnregisterClosestCommonInclusiveAncestor( 484 IsUnlinking aIsUnlinking) { 485 if (!mRegisteredClosestCommonInclusiveAncestor) { 486 return; 487 } 488 nsCOMPtr oldClosestCommonInclusiveAncestor = 489 mRegisteredClosestCommonInclusiveAncestor; 490 mRegisteredClosestCommonInclusiveAncestor = nullptr; 491 LinkedList<AbstractRange>* ranges = 492 oldClosestCommonInclusiveAncestor 493 ->GetExistingClosestCommonInclusiveAncestorRanges(); 494 MOZ_ASSERT(ranges); 495 496 #ifdef DEBUG 497 bool found = false; 498 for (AbstractRange* range : *ranges) { 499 if (range == this) { 500 found = true; 501 break; 502 } 503 } 504 MOZ_ASSERT(found, 505 "We should be in the list on our registered common ancestor"); 506 #endif // DEBUG 507 508 remove(); 509 510 // We don't want to waste time unmarking flags on nodes that are 511 // being unlinked anyway. 512 if (aIsUnlinking == IsUnlinking::No && ranges->isEmpty()) { 513 oldClosestCommonInclusiveAncestor 514 ->ClearClosestCommonInclusiveAncestorForRangeInSelection(); 515 UnmarkDescendants(*oldClosestCommonInclusiveAncestor); 516 } 517 oldClosestCommonInclusiveAncestor = nullptr; 518 } 519 520 void AbstractRange::UpdateCommonAncestorIfNecessary() { 521 nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor; 522 nsINode* newCommonAncestor = 523 GetClosestCommonInclusiveAncestor(AllowRangeCrossShadowBoundary::Yes); 524 if (newCommonAncestor != oldCommonAncestor) { 525 UnregisterClosestCommonInclusiveAncestor(); 526 527 if (newCommonAncestor) { 528 RegisterClosestCommonInclusiveAncestor(newCommonAncestor); 529 } else { 530 MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes"); 531 mSelections.Clear(); 532 MOZ_DIAGNOSTIC_ASSERT( 533 !mRegisteredClosestCommonInclusiveAncestor, 534 "How can we have a registered common ancestor when we " 535 "didn't register ourselves?"); 536 MOZ_DIAGNOSTIC_ASSERT(!isInList(), 537 "Shouldn't be registered if we have no " 538 "mRegisteredClosestCommonInclusiveAncestor"); 539 } 540 } 541 } 542 543 const RangeBoundary& AbstractRange::MayCrossShadowBoundaryStartRef() const { 544 return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryStartRef() 545 : mStart; 546 } 547 548 const RangeBoundary& AbstractRange::MayCrossShadowBoundaryEndRef() const { 549 return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryEndRef() 550 : mEnd; 551 } 552 553 nsIContent* AbstractRange::GetMayCrossShadowBoundaryChildAtStartOffset() const { 554 return IsDynamicRange() 555 ? AsDynamicRange()->GetMayCrossShadowBoundaryChildAtStartOffset() 556 : mStart.GetChildAtOffset(); 557 } 558 559 nsIContent* AbstractRange::GetMayCrossShadowBoundaryChildAtEndOffset() const { 560 return IsDynamicRange() 561 ? AsDynamicRange()->GetMayCrossShadowBoundaryChildAtEndOffset() 562 : mEnd.GetChildAtOffset(); 563 } 564 565 nsINode* AbstractRange::GetMayCrossShadowBoundaryStartContainer() const { 566 return IsDynamicRange() 567 ? AsDynamicRange()->GetMayCrossShadowBoundaryStartContainer() 568 : mStart.GetContainer(); 569 } 570 571 nsINode* AbstractRange::GetMayCrossShadowBoundaryEndContainer() const { 572 return IsDynamicRange() 573 ? AsDynamicRange()->GetMayCrossShadowBoundaryEndContainer() 574 : mEnd.GetContainer(); 575 } 576 577 bool AbstractRange::MayCrossShadowBoundary() const { 578 return IsDynamicRange() ? !!AsDynamicRange()->GetCrossShadowBoundaryRange() 579 : false; 580 } 581 582 uint32_t AbstractRange::MayCrossShadowBoundaryStartOffset() const { 583 return IsDynamicRange() 584 ? AsDynamicRange()->MayCrossShadowBoundaryStartOffset() 585 : static_cast<uint32_t>(*mStart.Offset( 586 RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)); 587 } 588 589 uint32_t AbstractRange::MayCrossShadowBoundaryEndOffset() const { 590 return IsDynamicRange() 591 ? AsDynamicRange()->MayCrossShadowBoundaryEndOffset() 592 : static_cast<uint32_t>(*mEnd.Offset( 593 RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)); 594 } 595 596 nsINode* AbstractRange::GetParentObject() const { return mOwner; } 597 598 JSObject* AbstractRange::WrapObject(JSContext* aCx, 599 JS::Handle<JSObject*> aGivenProto) { 600 MOZ_CRASH("Must be overridden"); 601 } 602 603 bool AbstractRange::AreNormalRangeAndCrossShadowBoundaryRangeCollapsed() const { 604 if (!Collapsed()) { 605 return false; 606 } 607 608 // We know normal range is collapsed at this point 609 if (IsStaticRange()) { 610 return true; 611 } 612 613 if (const CrossShadowBoundaryRange* crossShadowBoundaryRange = 614 AsDynamicRange()->GetCrossShadowBoundaryRange()) { 615 return crossShadowBoundaryRange->Collapsed(); 616 } 617 618 return true; 619 } 620 621 void AbstractRange::ClearForReuse() { 622 mOwner = nullptr; 623 mStart = RangeBoundary(mStart.GetTreeKind()); 624 mEnd = RangeBoundary(mEnd.GetTreeKind()); 625 mIsPositioned = false; 626 mIsGenerated = false; 627 mCalledByJS = false; 628 } 629 630 /*static*/ 631 bool AbstractRange::IsRootUAWidget(const nsINode* aRoot) { 632 MOZ_ASSERT(aRoot); 633 if (const ShadowRoot* shadowRoot = ShadowRoot::FromNode(aRoot)) { 634 return shadowRoot->IsUAWidget(); 635 } 636 return false; 637 } 638 639 already_AddRefed<StaticRange> AbstractRange::GetShrunkenRangeToVisibleLeaves() 640 const { 641 if (NS_WARN_IF(!IsPositioned()) || NS_WARN_IF(Collapsed()) || 642 NS_WARN_IF(IsStaticRange() && !AsStaticRange()->IsValid())) { 643 return nullptr; 644 } 645 646 const RawRangeBoundary startBoundary = 647 SelectionMovementUtils::GetFirstVisiblePointAtLeaf(*this); 648 if (MOZ_UNLIKELY(!startBoundary.IsSet())) { 649 return nullptr; 650 } 651 const RawRangeBoundary endBoundary = 652 SelectionMovementUtils::GetLastVisiblePointAtLeaf(*this); 653 if (MOZ_UNLIKELY(!endBoundary.IsSet())) { 654 return nullptr; 655 } 656 IgnoredErrorResult error; 657 RefPtr<StaticRange> range = 658 StaticRange::Create(startBoundary, endBoundary, error); 659 if (NS_WARN_IF(error.Failed())) { 660 error.SuppressException(); 661 return nullptr; 662 } 663 return range.forget(); 664 } 665 666 } // namespace mozilla::dom