nsRange.cpp (133647B)
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 /* 8 * Implementation of the DOM Range object. 9 */ 10 11 #include "nsRange.h" 12 13 #include "RangeBoundary.h" 14 #include "mozilla/Assertions.h" 15 #include "mozilla/CheckedInt.h" 16 #include "mozilla/ContentIterator.h" 17 #include "mozilla/Likely.h" 18 #include "mozilla/Logging.h" 19 #include "mozilla/Maybe.h" 20 #include "mozilla/PresShell.h" 21 #include "mozilla/RangeUtils.h" 22 #include "mozilla/ToString.h" 23 #include "mozilla/dom/CharacterData.h" 24 #include "mozilla/dom/ChildIterator.h" 25 #include "mozilla/dom/DOMRect.h" 26 #include "mozilla/dom/DOMStringList.h" 27 #include "mozilla/dom/Document.h" 28 #include "mozilla/dom/DocumentFragment.h" 29 #include "mozilla/dom/DocumentType.h" 30 #include "mozilla/dom/InspectorFontFace.h" 31 #include "mozilla/dom/RangeBinding.h" 32 #include "mozilla/dom/Selection.h" 33 #include "mozilla/dom/Text.h" 34 #include "mozilla/dom/TrustedTypeUtils.h" 35 #include "mozilla/dom/TrustedTypesConstants.h" 36 #include "nsCSSFrameConstructor.h" 37 #include "nsComputedDOMStyle.h" 38 #include "nsContainerFrame.h" 39 #include "nsContentUtils.h" 40 #include "nsDebug.h" 41 #include "nsError.h" 42 #include "nsFrameSelection.h" 43 #include "nsGkAtoms.h" 44 #include "nsIContent.h" 45 #include "nsINodeList.h" 46 #include "nsLayoutUtils.h" 47 #include "nsReadableUtils.h" 48 #include "nsString.h" 49 #include "nsStyleStruct.h" 50 #include "nsStyleStructInlines.h" 51 #include "nsTextFrame.h" 52 #include "nscore.h" 53 54 #ifdef ACCESSIBILITY 55 # include "nsAccessibilityService.h" 56 #endif 57 58 namespace mozilla { 59 extern LazyLogModule sSelectionAPILog; 60 extern void LogStackForSelectionAPI(); 61 62 template <typename SPT, typename SRT, typename EPT, typename ERT> 63 static void LogSelectionAPI(const dom::Selection* aSelection, 64 const char* aFuncName, const char* aArgName1, 65 const RangeBoundaryBase<SPT, SRT>& aBoundary1, 66 const char* aArgName2, 67 const RangeBoundaryBase<EPT, ERT>& aBoundary2, 68 const char* aArgName3, bool aBoolArg) { 69 if (aBoundary1 == aBoundary2) { 70 MOZ_LOG(sSelectionAPILog, LogLevel::Info, 71 ("%p nsRange::%s(%s=%s=%s, %s=%s)", aSelection, aFuncName, 72 aArgName1, aArgName2, ToString(aBoundary1).c_str(), aArgName3, 73 aBoolArg ? "true" : "false")); 74 } else { 75 MOZ_LOG( 76 sSelectionAPILog, LogLevel::Info, 77 ("%p nsRange::%s(%s=%s, %s=%s, %s=%s)", aSelection, aFuncName, 78 aArgName1, ToString(aBoundary1).c_str(), aArgName2, 79 ToString(aBoundary2).c_str(), aArgName3, aBoolArg ? "true" : "false")); 80 } 81 } 82 } // namespace mozilla 83 84 using namespace mozilla; 85 using namespace mozilla::dom; 86 87 template already_AddRefed<nsRange> nsRange::Create( 88 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, 89 ErrorResult& aRv, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 90 template already_AddRefed<nsRange> nsRange::Create( 91 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary, 92 ErrorResult& aRv, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 93 template already_AddRefed<nsRange> nsRange::Create( 94 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, 95 ErrorResult& aRv, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 96 template already_AddRefed<nsRange> nsRange::Create( 97 const RawRangeBoundary& aStartBoundary, 98 const RawRangeBoundary& aEndBoundary, ErrorResult& aRv, 99 AllowRangeCrossShadowBoundary aAlloCrossShadowBoundary); 100 101 template nsresult nsRange::SetStartAndEnd( 102 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, 103 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 104 template nsresult nsRange::SetStartAndEnd( 105 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary, 106 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 107 template nsresult nsRange::SetStartAndEnd( 108 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, 109 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 110 template nsresult nsRange::SetStartAndEnd( 111 const RawRangeBoundary& aStartBoundary, 112 const RawRangeBoundary& aEndBoundary, 113 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 114 115 template void nsRange::DoSetRange(const RangeBoundary& aStartBoundary, 116 const RangeBoundary& aEndBoundary, 117 nsINode* aRootNode, bool aNotInsertedYet, 118 RangeBehaviour aRangeBehaviour); 119 template void nsRange::DoSetRange(const RangeBoundary& aStartBoundary, 120 const RawRangeBoundary& aEndBoundary, 121 nsINode* aRootNode, bool aNotInsertedYet, 122 RangeBehaviour aRangeBehaviour); 123 template void nsRange::DoSetRange(const RawRangeBoundary& aStartBoundary, 124 const RangeBoundary& aEndBoundary, 125 nsINode* aRootNode, bool aNotInsertedYet, 126 RangeBehaviour aRangeBehaviour); 127 template void nsRange::DoSetRange(const RawRangeBoundary& aStartBoundary, 128 const RawRangeBoundary& aEndBoundary, 129 nsINode* aRootNode, bool aNotInsertedYet, 130 RangeBehaviour aRangeBehaviour); 131 132 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 133 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary); 134 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 135 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary); 136 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 137 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary); 138 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 139 const RawRangeBoundary& aStartBoundary, 140 const RawRangeBoundary& aEndBoundary); 141 142 JSObject* nsRange::WrapObject(JSContext* aCx, 143 JS::Handle<JSObject*> aGivenProto) { 144 return Range_Binding::Wrap(aCx, this, aGivenProto); 145 } 146 147 DocGroup* nsRange::GetDocGroup() const { 148 return mOwner ? mOwner->GetDocGroup() : nullptr; 149 } 150 151 /****************************************************** 152 * stack based utility class for managing monitor 153 ******************************************************/ 154 155 static void InvalidateAllFrames(nsINode* aNode) { 156 MOZ_ASSERT(aNode, "bad arg"); 157 158 nsIFrame* frame = nullptr; 159 switch (aNode->NodeType()) { 160 case nsINode::TEXT_NODE: 161 case nsINode::ELEMENT_NODE: { 162 nsIContent* content = static_cast<nsIContent*>(aNode); 163 frame = content->GetPrimaryFrame(); 164 break; 165 } 166 case nsINode::DOCUMENT_NODE: { 167 Document* doc = static_cast<Document*>(aNode); 168 PresShell* presShell = doc ? doc->GetPresShell() : nullptr; 169 frame = presShell ? presShell->GetRootFrame() : nullptr; 170 break; 171 } 172 } 173 for (nsIFrame* f = frame; f; f = f->GetNextContinuation()) { 174 f->InvalidateFrameSubtree(); 175 } 176 } 177 178 /****************************************************** 179 * constructor/destructor 180 ******************************************************/ 181 182 nsTArray<RefPtr<nsRange>>* nsRange::sCachedRanges = nullptr; 183 184 nsRange::~nsRange() { 185 NS_ASSERTION(!IsInAnySelection(), "deleting nsRange that is in use"); 186 187 // we want the side effects (releases and list removals) 188 DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr); 189 } 190 191 nsRange::nsRange(nsINode* aNode) 192 : AbstractRange(aNode, /* aIsDynamicRange = */ true, TreeKind::DOM), 193 mNextStartRef(nullptr), 194 mNextEndRef(nullptr) { 195 // printf("Size of nsRange: %zu\n", sizeof(nsRange)); 196 197 static_assert(sizeof(nsRange) <= 248, 198 "nsRange size shouldn't be increased as far as possible"); 199 } 200 201 /* static */ 202 already_AddRefed<nsRange> nsRange::Create(nsINode* aNode) { 203 MOZ_ASSERT(aNode); 204 if (!sCachedRanges || sCachedRanges->IsEmpty()) { 205 return do_AddRef(new nsRange(aNode)); 206 } 207 RefPtr<nsRange> range = sCachedRanges->PopLastElement().forget(); 208 range->Init(aNode); 209 return range.forget(); 210 } 211 212 /* static */ 213 template <typename SPT, typename SRT, typename EPT, typename ERT> 214 already_AddRefed<nsRange> nsRange::Create( 215 const RangeBoundaryBase<SPT, SRT>& aStartBoundary, 216 const RangeBoundaryBase<EPT, ERT>& aEndBoundary, ErrorResult& aRv, 217 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 218 MOZ_ASSERT(aStartBoundary.GetTreeKind() == aEndBoundary.GetTreeKind()); 219 220 // If we fail to initialize the range a lot, nsRange should have a static 221 // initializer since the allocation cost is not cheap in hot path. 222 RefPtr<nsRange> range = nsRange::Create(aStartBoundary.GetContainer()); 223 aRv = range->SetStartAndEnd(aStartBoundary, aEndBoundary, 224 aAllowCrossShadowBoundary); 225 if (NS_WARN_IF(aRv.Failed())) { 226 return nullptr; 227 } 228 return range.forget(); 229 } 230 231 /* 232 * When a new boundary is given to a nsRange, compare its position with other 233 * existing boundaries to see if we need to collapse the end points. 234 * 235 * aRange: The nsRange that aNewBoundary is being set to. 236 * aNewRoot: The shadow-including root of the container of aNewBoundary 237 * aNewBoundary: The new boundary 238 * aIsSetStart: true if GetRangeBehaviour is called by nsRange::SetStart, 239 * false otherwise 240 * aAllowCrossShadowBoundary: Indicates whether the boundaries allowed to cross 241 * shadow boundary or not 242 */ 243 static RangeBehaviour GetRangeBehaviour( 244 const nsRange* aRange, const nsINode* aNewRoot, 245 const RawRangeBoundary& aNewBoundaryInDOM, 246 const Maybe<RawRangeBoundary>& aNewBoundaryInFlat, const bool aIsSetStart, 247 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 248 if (!aRange->IsPositioned()) { 249 return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges; 250 } 251 252 MOZ_ASSERT(aRange->GetRoot()); 253 254 if (aNewRoot != aRange->GetRoot()) { 255 // Boundaries are in different document (or not connected), so collapse 256 // the both the default range and the crossBoundaryRange range. 257 if (aNewRoot->GetComposedDoc() != aRange->GetRoot()->GetComposedDoc()) { 258 return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges; 259 } 260 261 // Always collapse both ranges if the one of the roots is an UA widget 262 // regardless whether the boundaries are allowed to cross shadow boundary 263 // or not. 264 if (AbstractRange::IsRootUAWidget(aNewRoot) || 265 AbstractRange::IsRootUAWidget(aRange->GetRoot())) { 266 return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges; 267 } 268 269 if (const CrossShadowBoundaryRange* crossShadowBoundaryRange = 270 aRange->GetCrossShadowBoundaryRange()) { 271 // Check if the existing-other-side boundary in 272 // aRange::mCrossShadowBoundaryRange has the same root 273 // as aNewRoot. If this is the case, it means 274 // aRange::mCrossShadowBoundaryRange can be used to represent this 275 // cross-boundary selection, meanwhile we collapse the default range since 276 // this is a cross-boundary selection. 277 const RangeBoundary& otherSideExistingBoundary = 278 aIsSetStart ? crossShadowBoundaryRange->EndRef() 279 : crossShadowBoundaryRange->StartRef(); 280 const nsINode* otherSideRoot = 281 RangeUtils::ComputeRootNode(otherSideExistingBoundary.GetContainer()); 282 if (aNewRoot == otherSideRoot) { 283 return RangeBehaviour::CollapseDefaultRange; 284 } 285 } 286 287 // Different root, but same document. So we only collapse the 288 // default range if boundaries are allowed to cross shadow boundary. 289 return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes 290 ? RangeBehaviour::CollapseDefaultRange 291 : RangeBehaviour:: 292 CollapseDefaultRangeAndCrossShadowBoundaryRanges; 293 } 294 295 const RangeBoundary& otherSideExistingBoundaryInDOM = 296 aIsSetStart ? aRange->EndRef() : aRange->StartRef(); 297 298 auto CompareFlatTreeBoundaries = [&aNewBoundaryInFlat, aIsSetStart, 299 &aRange]() { 300 MOZ_ASSERT(aRange->GetCrossShadowBoundaryRange()); 301 MOZ_ASSERT(aNewBoundaryInFlat.isSome() && 302 aNewBoundaryInFlat->IsSetAndValid()); 303 const RangeBoundary& otherSideExistingCrossShadowBoundaryBoundaryInFlat = 304 aIsSetStart ? aRange->GetCrossShadowBoundaryRange()->EndRef() 305 : aRange->GetCrossShadowBoundaryRange()->StartRef(); 306 const Maybe<int32_t> withCrossShadowBoundaryOrder = 307 aIsSetStart 308 ? nsContentUtils::ComparePoints<TreeKind::Flat>( 309 aNewBoundaryInFlat.ref(), 310 otherSideExistingCrossShadowBoundaryBoundaryInFlat.AsRaw()) 311 : nsContentUtils::ComparePoints<TreeKind::Flat>( 312 otherSideExistingCrossShadowBoundaryBoundaryInFlat.AsRaw(), 313 aNewBoundaryInFlat.ref()); 314 if (withCrossShadowBoundaryOrder && *withCrossShadowBoundaryOrder != 1) { 315 return RangeBehaviour::CollapseDefaultRange; 316 } 317 318 // Not valid to both existing boundaries. 319 return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges; 320 }; 321 322 if (!aNewBoundaryInDOM.IsSetAndValid()) { 323 return CompareFlatTreeBoundaries(); 324 } 325 326 // Both boundaries are in the same root, now check for their position 327 const Maybe<int32_t> order = 328 aIsSetStart 329 ? nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>( 330 aNewBoundaryInDOM, otherSideExistingBoundaryInDOM.AsRaw()) 331 : nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>( 332 otherSideExistingBoundaryInDOM.AsRaw(), aNewBoundaryInDOM); 333 334 if (order) { 335 if (*order != 1) { 336 // aNewBoundary is at a valid position. 337 // 338 // If aIsSetStart is true, this means 339 // aNewBoundary <= otherSideExistingBoundary which is 340 // good because aNewBoundary intends to be the start. 341 // 342 // If aIsSetStart is false, this means 343 // otherSideExistingBoundary <= aNewBoundary which is good because 344 // aNewBoundary intends to be the end. 345 // 346 // So no collapse for above cases. 347 return RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges; 348 } 349 350 if (!aRange->MayCrossShadowBoundary() || 351 aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::No) { 352 return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges; 353 } 354 355 return CompareFlatTreeBoundaries(); 356 } 357 358 MOZ_ASSERT_UNREACHABLE(); 359 return RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges; 360 } 361 /****************************************************** 362 * nsISupports 363 ******************************************************/ 364 365 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange) 366 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_INTERRUPTABLE_LAST_RELEASE( 367 nsRange, DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr), 368 MaybeInterruptLastRelease()) 369 370 // QueryInterface implementation for nsRange 371 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange) 372 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) 373 NS_INTERFACE_MAP_END_INHERITING(AbstractRange) 374 375 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange) 376 377 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsRange, AbstractRange) 378 // `Reset()` unlinks `mStart`, `mEnd` and `mRoot`. 379 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrossShadowBoundaryRange); 380 tmp->Reset(); 381 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 382 383 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsRange, AbstractRange) 384 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) 385 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrossShadowBoundaryRange); 386 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 387 388 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsRange, AbstractRange) 389 NS_IMPL_CYCLE_COLLECTION_TRACE_END 390 391 bool nsRange::MaybeInterruptLastRelease() { 392 bool interrupt = AbstractRange::MaybeCacheToReuse(*this); 393 ResetCrossShadowBoundaryRange(); 394 MOZ_ASSERT(!interrupt || IsCleared()); 395 return interrupt; 396 } 397 398 void nsRange::AdjustNextRefsOnCharacterDataSplit( 399 const nsIContent& aContent, const CharacterDataChangeInfo& aInfo) { 400 // If the splitted text node is immediately before a range boundary point 401 // that refers to a child index (i.e. its parent is the boundary container) 402 // then we need to adjust the corresponding boundary to account for the new 403 // text node that will be inserted. However, because the new sibling hasn't 404 // been inserted yet, that would result in an invalid boundary. Therefore, 405 // we store the new child in mNext*Ref to make sure we adjust the boundary 406 // in the next ContentInserted or ContentAppended call. 407 nsINode* parentNode = aContent.GetParentNode(); 408 if (parentNode == mEnd.GetContainer()) { 409 if (&aContent == mEnd.Ref()) { 410 MOZ_ASSERT(aInfo.mDetails->mNextSibling); 411 mNextEndRef = aInfo.mDetails->mNextSibling; 412 } 413 } 414 415 if (parentNode == mStart.GetContainer()) { 416 if (&aContent == mStart.Ref()) { 417 MOZ_ASSERT(aInfo.mDetails->mNextSibling); 418 mNextStartRef = aInfo.mDetails->mNextSibling; 419 } 420 } 421 } 422 423 nsRange::RangeBoundariesAndRoot 424 nsRange::DetermineNewRangeBoundariesAndRootOnCharacterDataMerge( 425 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) const { 426 RawRangeBoundary newStart; 427 RawRangeBoundary newEnd; 428 nsINode* newRoot = nullptr; 429 430 // normalize(), aInfo.mDetails->mNextSibling is the merged text node 431 // that will be removed 432 nsIContent* removed = aInfo.mDetails->mNextSibling; 433 if (removed == mStart.GetContainer()) { 434 CheckedUint32 newStartOffset{ 435 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)}; 436 newStartOffset += aInfo.mChangeStart; 437 438 // newStartOffset.isValid() isn't checked explicitly here, because 439 // newStartOffset.value() contains an assertion. 440 newStart = {aContent, newStartOffset.value()}; 441 if (MOZ_UNLIKELY(removed == mRoot)) { 442 newRoot = RangeUtils::ComputeRootNode(newStart.GetContainer()); 443 } 444 } 445 if (removed == mEnd.GetContainer()) { 446 CheckedUint32 newEndOffset{ 447 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)}; 448 newEndOffset += aInfo.mChangeStart; 449 450 // newEndOffset.isValid() isn't checked explicitly here, because 451 // newEndOffset.value() contains an assertion. 452 newEnd = {aContent, newEndOffset.value()}; 453 if (MOZ_UNLIKELY(removed == mRoot)) { 454 newRoot = {RangeUtils::ComputeRootNode(newEnd.GetContainer())}; 455 } 456 } 457 // When the removed text node's parent is one of our boundary nodes we may 458 // need to adjust the offset to account for the removed node. However, 459 // there will also be a ContentRemoved notification later so the only cases 460 // we need to handle here is when the removed node is the text node after 461 // the boundary. (The m*Offset > 0 check is an optimization - a boundary 462 // point before the first child is never affected by normalize().) 463 nsINode* parentNode = aContent->GetParentNode(); 464 if (parentNode == mStart.GetContainer() && 465 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) > 0 && 466 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) < 467 parentNode->GetChildCount() && 468 removed == mStart.GetChildAtOffset()) { 469 newStart = {aContent, aInfo.mChangeStart}; 470 } 471 if (parentNode == mEnd.GetContainer() && 472 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) > 0 && 473 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) < 474 parentNode->GetChildCount() && 475 removed == mEnd.GetChildAtOffset()) { 476 newEnd = {aContent, aInfo.mChangeEnd}; 477 } 478 479 return {newStart, newEnd, newRoot}; 480 } 481 482 /****************************************************** 483 * nsIMutationObserver implementation 484 ******************************************************/ 485 void nsRange::CharacterDataChanged(nsIContent* aContent, 486 const CharacterDataChangeInfo& aInfo) { 487 MOZ_ASSERT(aContent); 488 MOZ_ASSERT(mIsPositioned); 489 MOZ_ASSERT(!mNextEndRef); 490 MOZ_ASSERT(!mNextStartRef); 491 492 nsINode* newRoot = nullptr; 493 RawRangeBoundary newStart; 494 RawRangeBoundary newEnd; 495 496 if (aInfo.mDetails && 497 aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit) { 498 AdjustNextRefsOnCharacterDataSplit(*aContent, aInfo); 499 } 500 501 // If the changed node contains our start boundary and the change starts 502 // before the boundary we'll need to adjust the offset. 503 if (aContent == mStart.GetContainer() && 504 aInfo.mChangeStart < 505 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)) { 506 if (aInfo.mDetails) { 507 // splitText(), aInfo->mDetails->mNextSibling is the new text node 508 NS_ASSERTION( 509 aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit, 510 "only a split can start before the end"); 511 NS_ASSERTION( 512 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <= 513 aInfo.mChangeEnd + 1, 514 "mStart.Offset() is beyond the end of this node"); 515 const uint32_t newStartOffset = 516 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) - 517 aInfo.mChangeStart; 518 newStart = {aInfo.mDetails->mNextSibling, newStartOffset}; 519 if (MOZ_UNLIKELY(aContent == mRoot)) { 520 newRoot = RangeUtils::ComputeRootNode(newStart.GetContainer()); 521 } 522 523 bool isCommonAncestor = 524 IsInAnySelection() && mStart.GetContainer() == mEnd.GetContainer(); 525 if (isCommonAncestor) { 526 MOZ_DIAGNOSTIC_ASSERT(mStart.GetContainer() == 527 mRegisteredClosestCommonInclusiveAncestor); 528 UnregisterClosestCommonInclusiveAncestor(); 529 RegisterClosestCommonInclusiveAncestor(newStart.GetContainer()); 530 } 531 if (mStart.GetContainer() 532 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { 533 newStart.GetContainer() 534 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 535 } 536 } else { 537 newStart = ComputeNewBoundaryWhenBoundaryInsideChangedText( 538 aInfo, mStart.AsRaw()); 539 } 540 } 541 542 // Do the same thing for the end boundary, except for splitText of a node 543 // with no parent then only switch to the new node if the start boundary 544 // did so too (otherwise the range would end up with disconnected nodes). 545 if (aContent == mEnd.GetContainer() && 546 aInfo.mChangeStart < 547 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)) { 548 if (aInfo.mDetails && 549 (aContent->GetParentNode() || newStart.GetContainer())) { 550 // splitText(), aInfo.mDetails->mNextSibling is the new text node 551 NS_ASSERTION( 552 aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit, 553 "only a split can start before the end"); 554 MOZ_ASSERT( 555 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <= 556 aInfo.mChangeEnd + 1, 557 "mEnd.Offset() is beyond the end of this node"); 558 559 const uint32_t newEndOffset{ 560 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) - 561 aInfo.mChangeStart}; 562 newEnd = {aInfo.mDetails->mNextSibling, newEndOffset}; 563 564 bool isCommonAncestor = 565 IsInAnySelection() && mStart.GetContainer() == mEnd.GetContainer(); 566 if (isCommonAncestor && !newStart.GetContainer()) { 567 MOZ_DIAGNOSTIC_ASSERT(mStart.GetContainer() == 568 mRegisteredClosestCommonInclusiveAncestor); 569 // The split occurs inside the range. 570 UnregisterClosestCommonInclusiveAncestor(); 571 RegisterClosestCommonInclusiveAncestor( 572 mStart.GetContainer()->GetParentNode()); 573 newEnd.GetContainer() 574 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 575 } else if ( 576 mEnd.GetContainer() 577 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { 578 newEnd.GetContainer() 579 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 580 } 581 } else { 582 newEnd = 583 ComputeNewBoundaryWhenBoundaryInsideChangedText(aInfo, mEnd.AsRaw()); 584 } 585 } 586 587 if (aInfo.mDetails && 588 aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eMerge) { 589 MOZ_ASSERT(!newStart.IsSet()); 590 MOZ_ASSERT(!newEnd.IsSet()); 591 592 RangeBoundariesAndRoot rangeBoundariesAndRoot = 593 DetermineNewRangeBoundariesAndRootOnCharacterDataMerge(aContent, aInfo); 594 595 newStart = rangeBoundariesAndRoot.mStart; 596 newEnd = rangeBoundariesAndRoot.mEnd; 597 newRoot = rangeBoundariesAndRoot.mRoot; 598 } 599 600 if (newStart.IsSet() || newEnd.IsSet()) { 601 if (!newStart.IsSet()) { 602 newStart.CopyFrom(mStart, RangeBoundaryIsMutationObserved::Yes); 603 } 604 if (!newEnd.IsSet()) { 605 newEnd.CopyFrom(mEnd, RangeBoundaryIsMutationObserved::Yes); 606 } 607 DoSetRange(newStart, newEnd, newRoot ? newRoot : mRoot.get(), 608 !newEnd.GetContainer()->GetParentNode() || 609 !newStart.GetContainer()->GetParentNode()); 610 } else { 611 nsRange::AssertIfMismatchRootAndRangeBoundaries( 612 mStart, mEnd, mRoot, 613 (mStart.IsSet() && !mStart.GetContainer()->GetParentNode()) || 614 (mEnd.IsSet() && !mEnd.GetContainer()->GetParentNode())); 615 } 616 } 617 618 void nsRange::ContentAppended(nsIContent* aFirstNewContent, 619 const ContentAppendInfo&) { 620 MOZ_ASSERT(mIsPositioned); 621 622 nsINode* container = aFirstNewContent->GetParentNode(); 623 MOZ_ASSERT(container); 624 if (container->IsMaybeSelected() && IsInAnySelection()) { 625 nsINode* child = aFirstNewContent; 626 while (child) { 627 if (!child 628 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { 629 MarkDescendants(*child); 630 child 631 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 632 } 633 child = child->GetNextSibling(); 634 } 635 } 636 637 if (mNextStartRef || mNextEndRef) { 638 // A splitText has occurred, if any mNext*Ref was set, we need to adjust 639 // the range boundaries. 640 if (mNextStartRef) { 641 mStart = {mStart.GetContainer(), mNextStartRef}; 642 MOZ_ASSERT(mNextStartRef == aFirstNewContent); 643 mNextStartRef = nullptr; 644 } 645 if (mNextEndRef) { 646 mEnd = {mEnd.GetContainer(), mNextEndRef}; 647 MOZ_ASSERT(mNextEndRef == aFirstNewContent); 648 mNextEndRef = nullptr; 649 } 650 DoSetRange(mStart, mEnd, mRoot, true); 651 } else { 652 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart, mEnd, mRoot); 653 } 654 } 655 656 void nsRange::ContentInserted(nsIContent* aChild, const ContentInsertInfo&) { 657 MOZ_ASSERT(mIsPositioned); 658 659 bool updateBoundaries = false; 660 nsINode* container = aChild->GetParentNode(); 661 MOZ_ASSERT(container); 662 RawRangeBoundary newStart(mStart, RangeBoundaryIsMutationObserved::Yes); 663 RawRangeBoundary newEnd(mEnd, RangeBoundaryIsMutationObserved::Yes); 664 MOZ_ASSERT(aChild->GetParentNode() == container); 665 666 // Invalidate boundary offsets if a child that may have moved them was 667 // inserted. 668 if (container == mStart.GetContainer()) { 669 newStart.InvalidateOffset(); 670 updateBoundaries = true; 671 } 672 673 if (container == mEnd.GetContainer()) { 674 newEnd.InvalidateOffset(); 675 updateBoundaries = true; 676 } 677 678 if (container->IsMaybeSelected() && 679 !aChild 680 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { 681 MarkDescendants(*aChild); 682 aChild->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 683 } 684 685 if (mNextStartRef || mNextEndRef) { 686 if (mNextStartRef) { 687 newStart = {mStart.GetContainer(), mNextStartRef}; 688 MOZ_ASSERT(mNextStartRef == aChild); 689 mNextStartRef = nullptr; 690 } 691 if (mNextEndRef) { 692 newEnd = {mEnd.GetContainer(), mNextEndRef}; 693 MOZ_ASSERT(mNextEndRef == aChild); 694 mNextEndRef = nullptr; 695 } 696 697 updateBoundaries = true; 698 } 699 700 if (updateBoundaries) { 701 DoSetRange(newStart, newEnd, mRoot); 702 } else { 703 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart, mEnd, mRoot); 704 } 705 } 706 707 void nsRange::ContentWillBeRemoved(nsIContent* aChild, 708 const ContentRemoveInfo&) { 709 MOZ_ASSERT(mIsPositioned); 710 711 nsINode* container = aChild->GetParentNode(); 712 MOZ_ASSERT(container); 713 714 nsINode* startContainer = mStart.GetContainer(); 715 nsINode* endContainer = mEnd.GetContainer(); 716 717 RawRangeBoundary newStart; 718 RawRangeBoundary newEnd; 719 Maybe<bool> gravitateStart; 720 bool gravitateEnd; 721 722 // Adjust position if a sibling was removed... 723 if (container == startContainer) { 724 // We're only interested if our boundary reference was removed, otherwise 725 // we can just invalidate the offset. 726 if (aChild == mStart.Ref()) { 727 newStart = {container, aChild->GetPreviousSibling()}; 728 } else { 729 newStart.CopyFrom(mStart, RangeBoundaryIsMutationObserved::Yes); 730 newStart.InvalidateOffset(); 731 } 732 } else { 733 gravitateStart = Some(startContainer->IsInclusiveDescendantOf(aChild)); 734 if (gravitateStart.value()) { 735 newStart = {container, aChild->GetPreviousSibling()}; 736 } 737 } 738 739 // Do same thing for end boundry. 740 if (container == endContainer) { 741 if (aChild == mEnd.Ref()) { 742 newEnd = {container, aChild->GetPreviousSibling()}; 743 } else { 744 newEnd.CopyFrom(mEnd, RangeBoundaryIsMutationObserved::Yes); 745 newEnd.InvalidateOffset(); 746 } 747 } else { 748 if (startContainer == endContainer && gravitateStart.isSome()) { 749 gravitateEnd = gravitateStart.value(); 750 } else { 751 gravitateEnd = endContainer->IsInclusiveDescendantOf(aChild); 752 } 753 if (gravitateEnd) { 754 newEnd = {container, aChild->GetPreviousSibling()}; 755 } 756 } 757 758 bool newStartIsSet = newStart.IsSet(); 759 bool newEndIsSet = newEnd.IsSet(); 760 if (newStartIsSet || newEndIsSet) { 761 DoSetRange( 762 newStartIsSet ? newStart : mStart.AsRaw(), 763 newEndIsSet ? newEnd : mEnd.AsRaw(), mRoot, false, 764 // CrossShadowBoundaryRange mutates content 765 // removal fot itself, so no need for nsRange to do anything with it. 766 RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges); 767 } else { 768 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart, mEnd, mRoot); 769 } 770 771 MOZ_ASSERT(mStart.Ref() != aChild); 772 MOZ_ASSERT(mEnd.Ref() != aChild); 773 774 if (container->IsMaybeSelected() && 775 aChild 776 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { 777 aChild 778 ->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); 779 UnmarkDescendants(*aChild); 780 } 781 } 782 783 void nsRange::ParentChainChanged(nsIContent* aContent) { 784 NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?"); 785 nsINode* newRoot = RangeUtils::ComputeRootNode(mStart.GetContainer()); 786 NS_ASSERTION(newRoot, "No valid boundary or root found!"); 787 if (newRoot != RangeUtils::ComputeRootNode(mEnd.GetContainer())) { 788 // Sometimes ordering involved in cycle collection can lead to our 789 // start parent and/or end parent being disconnected from our root 790 // without our getting a ContentRemoved notification. 791 // See bug 846096 for more details. 792 NS_ASSERTION(mEnd.GetContainer()->IsInNativeAnonymousSubtree(), 793 "This special case should happen only with " 794 "native-anonymous content"); 795 // When that happens, bail out and set pointers to null; since we're 796 // in cycle collection and unreachable it shouldn't matter. 797 Reset(); 798 return; 799 } 800 // This is safe without holding a strong ref to self as long as the change 801 // of mRoot is the last thing in DoSetRange. 802 DoSetRange(mStart, mEnd, newRoot); 803 } 804 805 bool nsRange::IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor( 806 const nsINode& aContainer) const { 807 MOZ_ASSERT(mCrossShadowBoundaryRange && 808 mCrossShadowBoundaryRange->GetCommonAncestor()); 809 return aContainer.IsShadowIncludingInclusiveDescendantOf( 810 mCrossShadowBoundaryRange->GetCommonAncestor()); 811 } 812 813 bool nsRange::IsPointComparableToRange(const nsINode& aContainer, 814 uint32_t aOffset, 815 bool aAllowCrossShadowBoundary, 816 ErrorResult& aRv) const { 817 // our range is in a good state? 818 if (!mIsPositioned) { 819 aRv.Throw(NS_ERROR_NOT_INITIALIZED); 820 return false; 821 } 822 823 const bool isContainerInRange = 824 aContainer.IsInclusiveDescendantOf(mRoot) || 825 (aAllowCrossShadowBoundary && mCrossShadowBoundaryRange && 826 IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor( 827 aContainer)); 828 829 if (!isContainerInRange) { 830 // TODO(emilio): Switch to ThrowWrongDocumentError, but IsPointInRange 831 // relies on the error code right now in order to suppress the exception. 832 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR); 833 return false; 834 } 835 836 auto chromeOnlyAccess = mStart.GetContainer()->ChromeOnlyAccess(); 837 NS_ASSERTION(chromeOnlyAccess == mEnd.GetContainer()->ChromeOnlyAccess(), 838 "Start and end of a range must be either both native anonymous " 839 "content or not."); 840 if (aContainer.ChromeOnlyAccess() != chromeOnlyAccess) { 841 aRv.ThrowInvalidNodeTypeError( 842 "Trying to compare restricted with unrestricted nodes"); 843 return false; 844 } 845 846 if (aContainer.NodeType() == nsINode::DOCUMENT_TYPE_NODE) { 847 aRv.ThrowInvalidNodeTypeError("Trying to compare with a document"); 848 return false; 849 } 850 851 if (aOffset > aContainer.Length()) { 852 aRv.ThrowIndexSizeError("Offset is out of bounds"); 853 return false; 854 } 855 856 return true; 857 } 858 859 bool nsRange::IsPointInRange(const nsINode& aContainer, uint32_t aOffset, 860 ErrorResult& aRv, 861 bool aAllowCrossShadowBoundary) const { 862 int16_t compareResult = 863 ComparePoint(aContainer, aOffset, aRv, aAllowCrossShadowBoundary); 864 // If the node isn't in the range's document, it clearly isn't in the range. 865 if (aRv.ErrorCodeIs(NS_ERROR_DOM_WRONG_DOCUMENT_ERR)) { 866 aRv.SuppressException(); 867 return false; 868 } 869 870 return compareResult == 0; 871 } 872 873 int16_t nsRange::ComparePoint(const nsINode& aContainer, uint32_t aOffset, 874 ErrorResult& aRv, 875 bool aAllowCrossShadowBoundary) const { 876 if (!IsPointComparableToRange(aContainer, aOffset, aAllowCrossShadowBoundary, 877 aRv)) { 878 return 0; 879 } 880 881 const auto& startRef = 882 aAllowCrossShadowBoundary ? MayCrossShadowBoundaryStartRef() : StartRef(); 883 884 const RawRangeBoundary point{const_cast<nsINode*>(&aContainer), aOffset, 885 RangeBoundaryIsMutationObserved::Yes, 886 startRef.GetTreeKind()}; 887 888 MOZ_ASSERT(point.IsSetAndValid()); 889 890 if (Maybe<int32_t> order = nsContentUtils::ComparePoints( 891 point, aAllowCrossShadowBoundary ? MayCrossShadowBoundaryStartRef() 892 : StartRef()); 893 order && *order <= 0) { 894 return int16_t(*order); 895 } 896 if (Maybe<int32_t> order = nsContentUtils::ComparePoints( 897 aAllowCrossShadowBoundary ? MayCrossShadowBoundaryEndRef() : EndRef(), 898 point); 899 order && *order == -1) { 900 return 1; 901 } 902 return 0; 903 } 904 905 bool nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) { 906 if (!mIsPositioned) { 907 aRv.Throw(NS_ERROR_NOT_INITIALIZED); 908 return false; 909 } 910 911 nsINode* parent = aNode.GetParentNode(); 912 if (!parent) { 913 // |parent| is null, so |node|'s root is |node| itself. 914 return GetRoot() == &aNode; 915 } 916 917 const Maybe<uint32_t> nodeIndex = parent->ComputeIndexOf(&aNode); 918 if (nodeIndex.isNothing()) { 919 return false; 920 } 921 922 if (!IsPointComparableToRange(*parent, *nodeIndex, 923 false /* aAllowCrossShadowBoundary */, 924 IgnoreErrors())) { 925 return false; 926 } 927 928 const Maybe<int32_t> startOrder = nsContentUtils::ComparePoints( 929 mStart, RawRangeBoundary(parent, aNode.AsContent(), *nodeIndex + 1u)); 930 if (startOrder && (*startOrder < 0)) { 931 const Maybe<int32_t> endOrder = nsContentUtils::ComparePoints( 932 RawRangeBoundary(parent, aNode.GetPreviousSibling(), *nodeIndex), mEnd); 933 return endOrder && (*endOrder < 0); 934 } 935 936 return false; 937 } 938 939 void nsRange::NotifySelectionListenersAfterRangeSet() { 940 if (mSelections.IsEmpty()) { 941 return; 942 } 943 944 // Our internal code should not move focus with using this instance while 945 // it's calling Selection::NotifySelectionListeners() which may move focus 946 // or calls selection listeners. So, let's set mCalledByJS to false here 947 // since non-*JS() methods don't set it to false. 948 AutoCalledByJSRestore calledByJSRestorer(*this); 949 mCalledByJS = false; 950 951 // If this instance is not a proper range for selection, we need to remove 952 // this from selections. 953 const Document* const docForSelf = mStart.GetComposedDoc(); 954 const nsFrameSelection* const frameSelection = 955 mSelections[0]->GetFrameSelection(); 956 const Document* const docForSelection = 957 frameSelection && frameSelection->GetPresShell() 958 ? frameSelection->GetPresShell()->GetDocument() 959 : nullptr; 960 if (!IsPositioned() || docForSelf != docForSelection) { 961 // XXX Why Selection::RemoveRangeAndUnselectFramesAndNotifyListeners() does 962 // not set whether the caller is JS or not? 963 if (IsPartOfOneSelectionOnly()) { 964 RefPtr<Selection> selection = mSelections[0].get(); 965 selection->RemoveRangeAndUnselectFramesAndNotifyListeners(*this, 966 IgnoreErrors()); 967 } else { 968 nsTArray<WeakPtr<Selection>> copiedSelections = mSelections.Clone(); 969 for (const auto& weakSelection : copiedSelections) { 970 RefPtr<Selection> selection = weakSelection.get(); 971 if (MOZ_LIKELY(selection)) { 972 selection->RemoveRangeAndUnselectFramesAndNotifyListeners( 973 *this, IgnoreErrors()); 974 } 975 } 976 } 977 // FYI: NotifySelectionListeners() should be called by 978 // RemoveRangeAndUnselectFramesAndNotifyListeners() if it's required. 979 // Therefore, we need to do nothing anymore. 980 return; 981 } 982 983 // Notify all Selections. This may modify the range, 984 // remove it from the selection, or the selection itself may have gone after 985 // the call. Also, new selections may be added. 986 // To ensure that listeners are notified for all *current* selections, 987 // create a copy of the list of selections and use that for iterating. This 988 // way selections can be added or removed safely during iteration. 989 // To save allocation cost, the copy is only created if there is more than 990 // one Selection present (which will barely ever be the case). 991 if (IsPartOfOneSelectionOnly()) { 992 RefPtr<Selection> selection = mSelections[0].get(); 993 #ifdef ACCESSIBILITY 994 a11y::SelectionManager::SelectionRangeChanged(selection->GetType(), *this); 995 #endif 996 selection->NotifySelectionListeners(calledByJSRestorer.SavedValue()); 997 } else { 998 nsTArray<WeakPtr<Selection>> copiedSelections = mSelections.Clone(); 999 for (const auto& weakSelection : copiedSelections) { 1000 RefPtr<Selection> selection = weakSelection.get(); 1001 if (MOZ_LIKELY(selection)) { 1002 #ifdef ACCESSIBILITY 1003 a11y::SelectionManager::SelectionRangeChanged(selection->GetType(), 1004 *this); 1005 #endif 1006 selection->NotifySelectionListeners(calledByJSRestorer.SavedValue()); 1007 } 1008 } 1009 } 1010 } 1011 1012 /****************************************************** 1013 * Private helper routines 1014 ******************************************************/ 1015 1016 // static 1017 template <typename SPT, typename SRT, typename EPT, typename ERT> 1018 void nsRange::AssertIfMismatchRootAndRangeBoundaries( 1019 const RangeBoundaryBase<SPT, SRT>& aStartBoundary, 1020 const RangeBoundaryBase<EPT, ERT>& aEndBoundary, const nsINode* aRootNode, 1021 bool aNotInsertedYet /* = false */) { 1022 #ifdef DEBUG 1023 if (!aRootNode) { 1024 MOZ_ASSERT(!aStartBoundary.IsSet()); 1025 MOZ_ASSERT(!aEndBoundary.IsSet()); 1026 return; 1027 } 1028 1029 MOZ_ASSERT(aStartBoundary.IsSet()); 1030 MOZ_ASSERT(aEndBoundary.IsSet()); 1031 MOZ_ASSERT(aStartBoundary.GetTreeKind() == aEndBoundary.GetTreeKind()); 1032 1033 if (!aNotInsertedYet) { 1034 // Compute temporary root for given range boundaries. If a range in native 1035 // anonymous subtree is being removed, tempRoot may return the fragment's 1036 // root content, but it shouldn't be used for new root node because the node 1037 // may be bound to the root element again. 1038 nsINode* tempRoot = 1039 RangeUtils::ComputeRootNode(aStartBoundary.GetContainer()); 1040 // The new range should be in the temporary root node at least. 1041 MOZ_ASSERT(tempRoot == 1042 RangeUtils::ComputeRootNode(aEndBoundary.GetContainer())); 1043 MOZ_ASSERT( 1044 aStartBoundary.GetContainer()->IsInclusiveDescendantOf(tempRoot)); 1045 MOZ_ASSERT(aEndBoundary.GetContainer()->IsInclusiveDescendantOf(tempRoot)); 1046 // If the new range is not disconnected or not in native anonymous subtree, 1047 // the temporary root must be same as the new root node. Otherwise, 1048 // aRootNode should be the parent of root of the NAC (e.g., `<input>` if the 1049 // range is in NAC under `<input>`), but tempRoot is now root content node 1050 // of the disconnected subtree (e.g., `<div>` element in `<input>` element). 1051 const bool tempRootIsDisconnectedNAC = 1052 tempRoot->IsInNativeAnonymousSubtree() && !tempRoot->GetParentNode(); 1053 MOZ_ASSERT_IF(!tempRootIsDisconnectedNAC, tempRoot == aRootNode); 1054 } 1055 MOZ_ASSERT(aRootNode->IsDocument() || aRootNode->IsAttr() || 1056 aRootNode->IsDocumentFragment() || aRootNode->IsContent()); 1057 #endif // #ifdef DEBUG 1058 } 1059 1060 // It's important that all setting of the range start/end points 1061 // go through this function, which will do all the right voodoo 1062 // for content notification of range ownership. 1063 // Calling DoSetRange with either parent argument null will collapse 1064 // the range to have both endpoints point to the other node 1065 template <typename SPT, typename SRT, typename EPT, typename ERT> 1066 void nsRange:: 1067 DoSetRange(const RangeBoundaryBase<SPT, SRT>& aStartBoundary, 1068 const RangeBoundaryBase<EPT, ERT>& aEndBoundary, 1069 nsINode* aRootNode, 1070 bool aNotInsertedYet /* = false */, RangeBehaviour aRangeBehaviour /* = CollapseDefaultRangeAndCrossShadowBoundaryRanges */) { 1071 mIsPositioned = aStartBoundary.IsSetAndValid() && 1072 aEndBoundary.IsSetAndValid() && aRootNode; 1073 MOZ_ASSERT_IF(!mIsPositioned, !aStartBoundary.IsSet()); 1074 MOZ_ASSERT_IF(!mIsPositioned, !aEndBoundary.IsSet()); 1075 MOZ_ASSERT_IF(!mIsPositioned, !aRootNode); 1076 MOZ_ASSERT(aStartBoundary.GetTreeKind() == aEndBoundary.GetTreeKind()); 1077 MOZ_ASSERT(aStartBoundary.GetTreeKind() == TreeKind::DOM); 1078 1079 nsRange::AssertIfMismatchRootAndRangeBoundaries(aStartBoundary, aEndBoundary, 1080 aRootNode, aNotInsertedYet); 1081 1082 if (mRoot != aRootNode) { 1083 if (mRoot) { 1084 mRoot->RemoveMutationObserver(this); 1085 } 1086 if (aRootNode) { 1087 aRootNode->AddMutationObserver(this); 1088 } 1089 } 1090 bool checkCommonAncestor = 1091 (mStart.GetContainer() != aStartBoundary.GetContainer() || 1092 mEnd.GetContainer() != aEndBoundary.GetContainer()) && 1093 IsInAnySelection() && !aNotInsertedYet; 1094 1095 // GetClosestCommonInclusiveAncestor is unreliable while we're unlinking 1096 // (could return null if our start/end have already been unlinked), so make 1097 // sure to not use it here to determine our "old" current ancestor. 1098 mStart.CopyFrom(aStartBoundary, RangeBoundaryIsMutationObserved::Yes); 1099 mEnd.CopyFrom(aEndBoundary, RangeBoundaryIsMutationObserved::Yes); 1100 1101 if (aRangeBehaviour == 1102 RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges) { 1103 ResetCrossShadowBoundaryRange(); 1104 } 1105 1106 if (checkCommonAncestor) { 1107 UpdateCommonAncestorIfNecessary(); 1108 } 1109 1110 // This needs to be the last thing this function does, other than notifying 1111 // selection listeners. See comment in ParentChainChanged. 1112 if (mRoot != aRootNode) { 1113 mRoot = aRootNode; 1114 } 1115 1116 // Notify any selection listeners. This has to occur last because otherwise 1117 // the world could be observed by a selection listener while the range was in 1118 // an invalid state. So we run it off of a script runner to ensure it runs 1119 // after the mutation observers have finished running. 1120 if (!mSelections.IsEmpty()) { 1121 if (MOZ_LOG_TEST(sSelectionAPILog, LogLevel::Info)) { 1122 for (const auto& selection : mSelections) { 1123 if (selection && selection->Type() == SelectionType::eNormal) { 1124 LogSelectionAPI(selection, __FUNCTION__, "aStartBoundary", 1125 aStartBoundary, "aEndBoundary", aEndBoundary, 1126 "aNotInsertedYet", aNotInsertedYet); 1127 LogStackForSelectionAPI(); 1128 } 1129 } 1130 } 1131 nsContentUtils::AddScriptRunner( 1132 NewRunnableMethod("NotifySelectionListenersAfterRangeSet", this, 1133 &nsRange::NotifySelectionListenersAfterRangeSet)); 1134 } 1135 } 1136 1137 void nsRange::Reset() { 1138 DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr); 1139 } 1140 1141 /****************************************************** 1142 * public functionality 1143 ******************************************************/ 1144 1145 void nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr) { 1146 AutoCalledByJSRestore calledByJSRestorer(*this); 1147 mCalledByJS = true; 1148 SetStart(aNode, aOffset, aErr); 1149 } 1150 1151 bool nsRange::CanAccess(const nsINode& aNode) const { 1152 if (nsContentUtils::LegacyIsCallerNativeCode()) { 1153 return true; 1154 } 1155 return nsContentUtils::CanCallerAccess(&aNode); 1156 } 1157 1158 void nsRange::SetStart( 1159 nsINode& aNode, uint32_t aOffset, ErrorResult& aRv, 1160 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 1161 if (!CanAccess(aNode)) { 1162 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1163 return; 1164 } 1165 1166 AutoInvalidateSelection atEndOfBlock(this); 1167 SetStart(RawRangeBoundary(&aNode, aOffset), aRv, aAllowCrossShadowBoundary); 1168 } 1169 1170 void nsRange::SetStart( 1171 const RawRangeBoundary& aPoint, ErrorResult& aRv, 1172 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 1173 nsINode* newRoot = RangeUtils::ComputeRootNode(aPoint.GetContainer()); 1174 if (!newRoot) { 1175 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); 1176 return; 1177 } 1178 1179 // PointInFlat is necessary when the aPoint looks like 1180 // RangeBoundary(<slot>, an arbitary offset), here aPoint 1181 // is not a valid RangeBoundary in DOM tree (when the <slot> 1182 // doesn't have light DOM children), however it could be 1183 // a valid RangeBoundary in Flat tree. SetStart should 1184 // still work for this case. 1185 1186 // It also makes more sense to have CrossShadowBoundaryRange 1187 // always use PointInFlat because this is the composed range 1188 // that we care about, and we care it in Flat tree. 1189 // It's error prone if we mix the usage of DOM RangeBoundary 1190 // versus Flat RangeBoundary. 1191 auto pointInFlat = 1192 aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes 1193 ? Some(aPoint.AsRangeBoundaryInFlatTree()) 1194 : Nothing(); 1195 1196 if (!aPoint.IsSetAndValid() && 1197 (!pointInFlat || !pointInFlat->IsSetAndValid())) { 1198 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 1199 return; 1200 } 1201 1202 MOZ_ASSERT_IF(pointInFlat, aPoint.IsSet()); 1203 RangeBehaviour behaviour = 1204 GetRangeBehaviour(this, newRoot, aPoint, pointInFlat, 1205 true /* aIsSetStart= */, aAllowCrossShadowBoundary); 1206 1207 switch (behaviour) { 1208 case RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges: 1209 // EndRef(..) may be same as mStart or not, depends on 1210 // the value of mCrossShadowBoundaryRange->mEnd, We need to update 1211 // mCrossShadowBoundaryRange and the default boundaries separately 1212 if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) { 1213 if (MayCrossShadowBoundaryEndRef() != mEnd) { 1214 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 1215 pointInFlat.ref(), 1216 MayCrossShadowBoundaryEndRef().AsRangeBoundaryInFlatTree()); 1217 } 1218 } 1219 if (aPoint.IsSetAndValid()) { 1220 DoSetRange(aPoint, mEnd, mRoot, false, behaviour); 1221 } 1222 break; 1223 case RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges: 1224 if (aPoint.IsSetAndValid()) { 1225 DoSetRange(aPoint, aPoint, newRoot, false, behaviour); 1226 } 1227 break; 1228 case RangeBehaviour::CollapseDefaultRange: 1229 MOZ_ASSERT(aAllowCrossShadowBoundary == 1230 AllowRangeCrossShadowBoundary::Yes); 1231 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 1232 pointInFlat.ref(), 1233 MayCrossShadowBoundaryEndRef().AsRangeBoundaryInFlatTree()); 1234 if (aPoint.IsSetAndValid()) { 1235 DoSetRange(aPoint, aPoint, newRoot, false, behaviour); 1236 } 1237 break; 1238 default: 1239 MOZ_ASSERT_UNREACHABLE(); 1240 } 1241 } 1242 1243 void nsRange::SetStartAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset, 1244 ErrorResult& aErr) { 1245 AutoCalledByJSRestore calledByJSRestorer(*this); 1246 mCalledByJS = true; 1247 SetStart(aNode, aOffset, aErr, AllowRangeCrossShadowBoundary::Yes); 1248 } 1249 1250 void nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr) { 1251 AutoCalledByJSRestore calledByJSRestorer(*this); 1252 mCalledByJS = true; 1253 SetStartBefore(aNode, aErr); 1254 } 1255 1256 void nsRange::SetStartBefore( 1257 nsINode& aNode, ErrorResult& aRv, 1258 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 1259 if (!CanAccess(aNode)) { 1260 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1261 return; 1262 } 1263 1264 AutoInvalidateSelection atEndOfBlock(this); 1265 // If the node is being removed from its parent, GetRawRangeBoundaryBefore() 1266 // returns unset instance. Then, SetStart() will throw 1267 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR. 1268 SetStart(RangeUtils::GetRawRangeBoundaryBefore(&aNode), aRv, 1269 aAllowCrossShadowBoundary); 1270 } 1271 1272 void nsRange::SetStartAfterJS(nsINode& aNode, ErrorResult& aErr) { 1273 AutoCalledByJSRestore calledByJSRestorer(*this); 1274 mCalledByJS = true; 1275 SetStartAfter(aNode, aErr); 1276 } 1277 1278 void nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv) { 1279 if (!CanAccess(aNode)) { 1280 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1281 return; 1282 } 1283 1284 AutoInvalidateSelection atEndOfBlock(this); 1285 // If the node is being removed from its parent, GetRawRangeBoundaryAfter() 1286 // returns unset instance. Then, SetStart() will throw 1287 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR. 1288 SetStart(RangeUtils::GetRawRangeBoundaryAfter(&aNode), aRv); 1289 } 1290 1291 void nsRange::SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr) { 1292 AutoCalledByJSRestore calledByJSRestorer(*this); 1293 mCalledByJS = true; 1294 SetEnd(aNode, aOffset, aErr); 1295 } 1296 1297 void nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv, 1298 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 1299 if (!CanAccess(aNode)) { 1300 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1301 return; 1302 } 1303 AutoInvalidateSelection atEndOfBlock(this); 1304 SetEnd(RawRangeBoundary(&aNode, aOffset), aRv, aAllowCrossShadowBoundary); 1305 } 1306 1307 void nsRange::SetEnd(const RawRangeBoundary& aPoint, ErrorResult& aRv, 1308 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 1309 nsINode* newRoot = RangeUtils::ComputeRootNode(aPoint.GetContainer()); 1310 if (!newRoot) { 1311 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); 1312 return; 1313 } 1314 1315 // PointInFlat is necessary when the aPoint looks like 1316 // RangeBoundary(<slot>, an arbitary offset), here aPoint 1317 // is not a valid RangeBoundary in DOM tree (when the <slot> 1318 // doesn't have light DOM children), however it could be 1319 // a valid RangeBoundary in Flat tree. SetEnd should 1320 // still work for this case. 1321 1322 // It also makes more sense to have CrossShadowBoundaryRange 1323 // always use PointInFlat because this is the composed range 1324 // that we care about, and we care it in Flat tree. 1325 // It's error prone if we mix the usage of DOM RangeBoundary 1326 // versus Flat RangeBoundary. 1327 auto pointInFlat = 1328 aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes 1329 ? Some(aPoint.AsRangeBoundaryInFlatTree()) 1330 : Nothing(); 1331 1332 if (!aPoint.IsSetAndValid() && 1333 (!pointInFlat || !pointInFlat->IsSetAndValid())) { 1334 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 1335 return; 1336 } 1337 1338 MOZ_ASSERT_IF(pointInFlat, aPoint.IsSet()); 1339 RangeBehaviour policy = 1340 GetRangeBehaviour(this, newRoot, aPoint, pointInFlat, 1341 false /* aIsStartStart */, aAllowCrossShadowBoundary); 1342 1343 switch (policy) { 1344 case RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges: 1345 // StartRef(..) may be same as mStart or not, depends on 1346 // the value of mCrossShadowBoundaryRange->mStart, so we need to update 1347 // mCrossShadowBoundaryRange and the default boundaries separately 1348 if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) { 1349 if (MayCrossShadowBoundaryStartRef() != mStart) { 1350 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 1351 MayCrossShadowBoundaryStartRef().AsRangeBoundaryInFlatTree(), 1352 pointInFlat.ref()); 1353 } 1354 } 1355 if (aPoint.IsSetAndValid()) { 1356 DoSetRange(mStart, aPoint, mRoot, false, policy); 1357 } 1358 break; 1359 case RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges: 1360 if (aPoint.IsSetAndValid()) { 1361 DoSetRange(aPoint, aPoint, newRoot, false, policy); 1362 } 1363 break; 1364 case RangeBehaviour::CollapseDefaultRange: 1365 MOZ_ASSERT(aAllowCrossShadowBoundary == 1366 AllowRangeCrossShadowBoundary::Yes); 1367 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 1368 MayCrossShadowBoundaryStartRef().AsRangeBoundaryInFlatTree(), 1369 pointInFlat.ref()); 1370 if (aPoint.IsSetAndValid()) { 1371 DoSetRange(aPoint, aPoint, newRoot, false, policy); 1372 } 1373 break; 1374 default: 1375 MOZ_ASSERT_UNREACHABLE(); 1376 } 1377 } 1378 1379 void nsRange::SetEndAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset, 1380 ErrorResult& aErr) { 1381 AutoCalledByJSRestore calledByJSRestorer(*this); 1382 mCalledByJS = true; 1383 SetEnd(aNode, aOffset, aErr, 1384 AllowRangeCrossShadowBoundary::Yes /* aAllowCrossShadowBoundary */); 1385 } 1386 1387 void nsRange::SelectNodesInContainer(nsINode* aContainer, 1388 nsIContent* aStartContent, 1389 nsIContent* aEndContent) { 1390 MOZ_ASSERT(aContainer); 1391 MOZ_ASSERT(aContainer->ComputeIndexOf(aStartContent).valueOr(0) <= 1392 aContainer->ComputeIndexOf(aEndContent).valueOr(0)); 1393 MOZ_ASSERT(aStartContent && 1394 aContainer->ComputeIndexOf(aStartContent).isSome()); 1395 MOZ_ASSERT(aEndContent && aContainer->ComputeIndexOf(aEndContent).isSome()); 1396 1397 nsINode* newRoot = RangeUtils::ComputeRootNode(aContainer); 1398 MOZ_ASSERT(newRoot); 1399 if (!newRoot) { 1400 return; 1401 } 1402 1403 RawRangeBoundary start(aContainer, aStartContent->GetPreviousSibling()); 1404 RawRangeBoundary end(aContainer, aEndContent); 1405 DoSetRange(start, end, newRoot); 1406 } 1407 1408 void nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr) { 1409 AutoCalledByJSRestore calledByJSRestorer(*this); 1410 mCalledByJS = true; 1411 SetEndBefore(aNode, aErr); 1412 } 1413 1414 void nsRange::SetEndBefore( 1415 nsINode& aNode, ErrorResult& aRv, 1416 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 1417 if (!CanAccess(aNode)) { 1418 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1419 return; 1420 } 1421 1422 AutoInvalidateSelection atEndOfBlock(this); 1423 // If the node is being removed from its parent, GetRawRangeBoundaryBefore() 1424 // returns unset instance. Then, SetEnd() will throw 1425 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR. 1426 SetEnd(RangeUtils::GetRawRangeBoundaryBefore(&aNode), aRv, 1427 aAllowCrossShadowBoundary); 1428 } 1429 1430 void nsRange::SetEndAfterJS(nsINode& aNode, ErrorResult& aErr) { 1431 AutoCalledByJSRestore calledByJSRestorer(*this); 1432 mCalledByJS = true; 1433 SetEndAfter(aNode, aErr); 1434 } 1435 1436 void nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv) { 1437 if (!CanAccess(aNode)) { 1438 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1439 return; 1440 } 1441 1442 AutoInvalidateSelection atEndOfBlock(this); 1443 // If the node is being removed from its parent, GetRawRangeBoundaryAfter() 1444 // returns unset instance. Then, SetEnd() will throw 1445 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR. 1446 SetEnd(RangeUtils::GetRawRangeBoundaryAfter(&aNode), aRv); 1447 } 1448 1449 void nsRange::Collapse(bool aToStart) { 1450 if (!mIsPositioned) return; 1451 1452 AutoInvalidateSelection atEndOfBlock(this); 1453 if (aToStart) { 1454 DoSetRange(mStart, mStart, mRoot); 1455 } else { 1456 DoSetRange(mEnd, mEnd, mRoot); 1457 } 1458 } 1459 1460 void nsRange::CollapseJS(bool aToStart) { 1461 AutoCalledByJSRestore calledByJSRestorer(*this); 1462 mCalledByJS = true; 1463 Collapse(aToStart); 1464 } 1465 1466 void nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr) { 1467 AutoCalledByJSRestore calledByJSRestorer(*this); 1468 mCalledByJS = true; 1469 SelectNode(aNode, aErr); 1470 } 1471 1472 void nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv) { 1473 if (!CanAccess(aNode)) { 1474 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1475 return; 1476 } 1477 1478 nsINode* container = aNode.GetParentNode(); 1479 nsINode* newRoot = RangeUtils::ComputeRootNode(container); 1480 if (!newRoot) { 1481 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); 1482 return; 1483 } 1484 1485 const Maybe<uint32_t> index = container->ComputeIndexOf(&aNode); 1486 // MOZ_ASSERT(index.isSome()); 1487 // We need to compute the index here unfortunately, because, while we have 1488 // support for XBL, |container| may be the node's binding parent without 1489 // actually containing it. 1490 if (MOZ_UNLIKELY(NS_WARN_IF(index.isNothing()))) { 1491 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); 1492 return; 1493 } 1494 1495 AutoInvalidateSelection atEndOfBlock(this); 1496 DoSetRange(RawRangeBoundary{container, *index}, 1497 RawRangeBoundary{container, *index + 1u}, newRoot); 1498 } 1499 1500 void nsRange::SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr) { 1501 AutoCalledByJSRestore calledByJSRestorer(*this); 1502 mCalledByJS = true; 1503 SelectNodeContents(aNode, aErr); 1504 } 1505 1506 void nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv) { 1507 if (!CanAccess(aNode)) { 1508 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1509 return; 1510 } 1511 1512 nsINode* newRoot = RangeUtils::ComputeRootNode(&aNode); 1513 if (!newRoot) { 1514 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); 1515 return; 1516 } 1517 1518 AutoInvalidateSelection atEndOfBlock(this); 1519 DoSetRange(RawRangeBoundary(&aNode, 0u), 1520 RawRangeBoundary(&aNode, aNode.Length()), newRoot); 1521 } 1522 1523 // The Subtree Content Iterator only returns subtrees that are 1524 // completely within a given range. It doesn't return a CharacterData 1525 // node that contains either the start or end point of the range., 1526 // nor does it return element nodes when nothing in the element is selected. 1527 // We need an iterator that will also include these start/end points 1528 // so that our methods/algorithms aren't cluttered with special 1529 // case code that tries to include these points while iterating. 1530 // 1531 // The RangeSubtreeIterator class mimics the ContentSubtreeIterator 1532 // methods we need, so should the Content Iterator support the 1533 // start/end points in the future, we can switchover relatively 1534 // easy. 1535 1536 class MOZ_STACK_CLASS RangeSubtreeIterator { 1537 private: 1538 enum RangeSubtreeIterState { eDone = 0, eUseStart, eUseIterator, eUseEnd }; 1539 1540 Maybe<ContentSubtreeIterator> mSubtreeIter; 1541 RangeSubtreeIterState mIterState; 1542 1543 nsCOMPtr<nsINode> mStart; 1544 nsCOMPtr<nsINode> mEnd; 1545 1546 public: 1547 RangeSubtreeIterator() : mIterState(eDone) {} 1548 ~RangeSubtreeIterator() = default; 1549 1550 nsresult Init(nsRange* aRange, AllowRangeCrossShadowBoundary = 1551 AllowRangeCrossShadowBoundary::No); 1552 already_AddRefed<nsINode> GetCurrentNode(); 1553 void First(); 1554 void Last(); 1555 void Next(); 1556 void Prev(); 1557 1558 bool IsDone() { return mIterState == eDone; } 1559 }; 1560 1561 nsresult RangeSubtreeIterator::Init( 1562 nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 1563 mIterState = eDone; 1564 if (aRange->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) { 1565 return NS_OK; 1566 } 1567 1568 // Grab the start point of the range and QI it to 1569 // a CharacterData pointer. If it is CharacterData store 1570 // a pointer to the node. 1571 1572 if (!aRange->IsPositioned()) { 1573 return NS_ERROR_FAILURE; 1574 } 1575 1576 nsINode* node = aRange->GetMayCrossShadowBoundaryStartContainer(); 1577 if (NS_WARN_IF(!node)) { 1578 return NS_ERROR_FAILURE; 1579 } 1580 1581 if (node->IsCharacterData() || 1582 (node->IsElement() && node->AsElement()->GetChildCount() == 1583 aRange->MayCrossShadowBoundaryStartOffset())) { 1584 mStart = node; 1585 } 1586 1587 // Grab the end point of the range and QI it to 1588 // a CharacterData pointer. If it is CharacterData store 1589 // a pointer to the node. 1590 1591 node = aRange->GetMayCrossShadowBoundaryEndContainer(); 1592 if (NS_WARN_IF(!node)) { 1593 return NS_ERROR_FAILURE; 1594 } 1595 1596 if (node->IsCharacterData() || 1597 (node->IsElement() && aRange->MayCrossShadowBoundaryEndOffset() == 0)) { 1598 mEnd = node; 1599 } 1600 1601 if (mStart && mStart == mEnd) { 1602 // The range starts and stops in the same CharacterData 1603 // node. Null out the end pointer so we only visit the 1604 // node once! 1605 1606 mEnd = nullptr; 1607 } else { 1608 // Now create a Content Subtree Iterator to be used 1609 // for the subtrees between the end points! 1610 1611 mSubtreeIter.emplace(); 1612 1613 nsresult res = 1614 aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes 1615 ? mSubtreeIter->InitWithAllowCrossShadowBoundary(aRange) 1616 : mSubtreeIter->Init(aRange); 1617 if (NS_FAILED(res)) return res; 1618 1619 if (mSubtreeIter->IsDone()) { 1620 // The subtree iterator thinks there's nothing 1621 // to iterate over, so just free it up so we 1622 // don't accidentally call into it. 1623 1624 mSubtreeIter.reset(); 1625 } 1626 } 1627 1628 // Initialize the iterator by calling First(). 1629 // Note that we are ignoring the return value on purpose! 1630 1631 First(); 1632 1633 return NS_OK; 1634 } 1635 1636 already_AddRefed<nsINode> RangeSubtreeIterator::GetCurrentNode() { 1637 nsCOMPtr<nsINode> node; 1638 1639 if (mIterState == eUseStart && mStart) { 1640 node = mStart; 1641 } else if (mIterState == eUseEnd && mEnd) { 1642 node = mEnd; 1643 } else if (mIterState == eUseIterator && mSubtreeIter) { 1644 node = mSubtreeIter->GetCurrentNode(); 1645 } 1646 1647 return node.forget(); 1648 } 1649 1650 void RangeSubtreeIterator::First() { 1651 if (mStart) 1652 mIterState = eUseStart; 1653 else if (mSubtreeIter) { 1654 mSubtreeIter->First(); 1655 1656 mIterState = eUseIterator; 1657 } else if (mEnd) 1658 mIterState = eUseEnd; 1659 else 1660 mIterState = eDone; 1661 } 1662 1663 void RangeSubtreeIterator::Last() { 1664 if (mEnd) 1665 mIterState = eUseEnd; 1666 else if (mSubtreeIter) { 1667 mSubtreeIter->Last(); 1668 1669 mIterState = eUseIterator; 1670 } else if (mStart) 1671 mIterState = eUseStart; 1672 else 1673 mIterState = eDone; 1674 } 1675 1676 void RangeSubtreeIterator::Next() { 1677 if (mIterState == eUseStart) { 1678 if (mSubtreeIter) { 1679 mSubtreeIter->First(); 1680 1681 mIterState = eUseIterator; 1682 } else if (mEnd) 1683 mIterState = eUseEnd; 1684 else 1685 mIterState = eDone; 1686 } else if (mIterState == eUseIterator) { 1687 mSubtreeIter->Next(); 1688 1689 if (mSubtreeIter->IsDone()) { 1690 if (mEnd) 1691 mIterState = eUseEnd; 1692 else 1693 mIterState = eDone; 1694 } 1695 } else 1696 mIterState = eDone; 1697 } 1698 1699 void RangeSubtreeIterator::Prev() { 1700 if (mIterState == eUseEnd) { 1701 if (mSubtreeIter) { 1702 mSubtreeIter->Last(); 1703 1704 mIterState = eUseIterator; 1705 } else if (mStart) 1706 mIterState = eUseStart; 1707 else 1708 mIterState = eDone; 1709 } else if (mIterState == eUseIterator) { 1710 mSubtreeIter->Prev(); 1711 1712 if (mSubtreeIter->IsDone()) { 1713 if (mStart) 1714 mIterState = eUseStart; 1715 else 1716 mIterState = eDone; 1717 } 1718 } else 1719 mIterState = eDone; 1720 } 1721 1722 // CollapseRangeAfterDelete() is a utility method that is used by 1723 // DeleteContents() and ExtractContents() to collapse the range 1724 // in the correct place, under the range's root container (the 1725 // range end points common container) as outlined by the Range spec: 1726 // 1727 // http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html 1728 // The assumption made by this method is that the delete or extract 1729 // has been done already, and left the range in a state where there is 1730 // no content between the 2 end points. 1731 1732 static nsresult CollapseRangeAfterDelete(nsRange* aRange) { 1733 NS_ENSURE_ARG_POINTER(aRange); 1734 1735 // Check if range gravity took care of collapsing the range for us! 1736 if (aRange->Collapsed()) { 1737 // aRange is collapsed so there's nothing for us to do. 1738 // 1739 // There are 2 possible scenarios here: 1740 // 1741 // 1. aRange could've been collapsed prior to the delete/extract, 1742 // which would've resulted in nothing being removed, so aRange 1743 // is already where it should be. 1744 // 1745 // 2. Prior to the delete/extract, aRange's start and end were in 1746 // the same container which would mean everything between them 1747 // was removed, causing range gravity to collapse the range. 1748 1749 return NS_OK; 1750 } 1751 1752 // aRange isn't collapsed so figure out the appropriate place to collapse! 1753 // First get both end points and their common ancestor. 1754 1755 if (!aRange->IsPositioned()) { 1756 return NS_ERROR_NOT_INITIALIZED; 1757 } 1758 1759 nsCOMPtr<nsINode> commonAncestor = 1760 aRange->GetClosestCommonInclusiveAncestor(); 1761 1762 nsCOMPtr<nsINode> startContainer = aRange->GetStartContainer(); 1763 nsCOMPtr<nsINode> endContainer = aRange->GetEndContainer(); 1764 1765 // Collapse to one of the end points if they are already in the 1766 // commonAncestor. This should work ok since this method is called 1767 // immediately after a delete or extract that leaves no content 1768 // between the 2 end points! 1769 1770 if (startContainer == commonAncestor) { 1771 aRange->Collapse(true); 1772 return NS_OK; 1773 } 1774 if (endContainer == commonAncestor) { 1775 aRange->Collapse(false); 1776 return NS_OK; 1777 } 1778 1779 // End points are at differing levels. We want to collapse to the 1780 // point that is between the 2 subtrees that contain each point, 1781 // under the common ancestor. 1782 1783 nsCOMPtr<nsINode> nodeToSelect(startContainer); 1784 1785 while (nodeToSelect) { 1786 nsCOMPtr<nsINode> parent = nodeToSelect->GetParentNode(); 1787 if (parent == commonAncestor) break; // We found the nodeToSelect! 1788 1789 nodeToSelect = parent; 1790 } 1791 1792 if (!nodeToSelect) return NS_ERROR_FAILURE; // This should never happen! 1793 1794 ErrorResult error; 1795 aRange->SelectNode(*nodeToSelect, error); 1796 if (error.Failed()) { 1797 return error.StealNSResult(); 1798 } 1799 1800 aRange->Collapse(false); 1801 return NS_OK; 1802 } 1803 1804 NS_IMETHODIMP 1805 PrependChild(nsINode* aContainer, nsINode* aChild) { 1806 nsCOMPtr<nsINode> first = aContainer->GetFirstChild(); 1807 ErrorResult rv; 1808 aContainer->InsertBefore(*aChild, first, rv); 1809 return rv.StealNSResult(); 1810 } 1811 1812 // Helper function for CutContents, making sure that the current node wasn't 1813 // removed by mutation events (bug 766426) 1814 static bool ValidateCurrentNode(nsRange* aRange, RangeSubtreeIterator& aIter) { 1815 bool before, after; 1816 nsCOMPtr<nsINode> node = aIter.GetCurrentNode(); 1817 if (!node) { 1818 // We don't have to worry that the node was removed if it doesn't exist, 1819 // e.g., the iterator is done. 1820 return true; 1821 } 1822 1823 nsresult rv = RangeUtils::CompareNodeToRange(node, aRange, &before, &after); 1824 if (NS_WARN_IF(NS_FAILED(rv))) { 1825 return false; 1826 } 1827 1828 if (before || after) { 1829 if (node->IsCharacterData()) { 1830 // If we're dealing with the start/end container which is a character 1831 // node, pretend that the node is in the range. 1832 if (before && node == aRange->GetStartContainer()) { 1833 before = false; 1834 } 1835 if (after && node == aRange->GetEndContainer()) { 1836 after = false; 1837 } 1838 } 1839 } 1840 1841 return !before && !after; 1842 } 1843 1844 /** 1845 * Delete unnecessary data from aCharacterData which must be the container of 1846 * aStartRef or aEndRef. Then, this returns a clone node of aCharacterData if 1847 * aCloneCutContent is true. 1848 */ 1849 static already_AddRefed<nsINode> CutCharacterData( 1850 CharacterData& aCharacterData, const RangeBoundary& aStartRef, 1851 const RangeBoundary& aEndRef, bool aCloneCutContent, ErrorResult& aRv) { 1852 MOZ_ASSERT(&aCharacterData == aStartRef.GetContainer() || 1853 &aCharacterData == aEndRef.GetContainer()); 1854 1855 const uint32_t startOffset = 1856 *aStartRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 1857 const uint32_t endOffset = 1858 *aEndRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 1859 if (aStartRef.GetContainer() == aEndRef.GetContainer()) { 1860 // This range is completely contained within a single text node. 1861 // Delete or extract the data between startOffset and endOffset. 1862 if (*aEndRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets) <= 1863 *aStartRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets)) { 1864 return nullptr; 1865 } 1866 nsCOMPtr<nsINode> clone; 1867 if (aCloneCutContent) { 1868 nsAutoString cutValue; 1869 aCharacterData.SubstringData(startOffset, endOffset - startOffset, 1870 cutValue, aRv); 1871 if (NS_WARN_IF(aRv.Failed())) { 1872 return nullptr; 1873 } 1874 clone = aCharacterData.CloneNode(false, aRv); 1875 if (NS_WARN_IF(aRv.Failed())) { 1876 return nullptr; 1877 } 1878 clone->SetNodeValueInternal(cutValue, aRv); 1879 if (NS_WARN_IF(aRv.Failed())) { 1880 return nullptr; 1881 } 1882 } 1883 1884 aCharacterData.DeleteData(startOffset, endOffset - startOffset, aRv); 1885 return clone.forget(); 1886 } 1887 1888 if (&aCharacterData == aStartRef.GetContainer()) { 1889 // Delete or extract everything after startOffset. 1890 const uint32_t dataLength = aCharacterData.TextDataLength(); 1891 if (dataLength < startOffset) { 1892 return nullptr; 1893 } 1894 nsCOMPtr<nsINode> clone; 1895 if (aCloneCutContent) { 1896 nsAutoString cutValue; 1897 aCharacterData.SubstringData(startOffset, dataLength, cutValue, aRv); 1898 if (NS_WARN_IF(aRv.Failed())) { 1899 return nullptr; 1900 } 1901 clone = aCharacterData.CloneNode(false, aRv); 1902 if (NS_WARN_IF(aRv.Failed())) { 1903 return nullptr; 1904 } 1905 clone->SetNodeValueInternal(cutValue, aRv); 1906 if (NS_WARN_IF(aRv.Failed())) { 1907 return nullptr; 1908 } 1909 } 1910 1911 aCharacterData.DeleteData(startOffset, dataLength, aRv); 1912 if (NS_WARN_IF(aRv.Failed())) { 1913 return nullptr; 1914 } 1915 return clone.forget(); 1916 } 1917 1918 MOZ_ASSERT(&aCharacterData == aEndRef.GetContainer()); 1919 // Delete or extract everything before endOffset. 1920 nsCOMPtr<nsINode> clone; 1921 if (aCloneCutContent) { 1922 nsAutoString cutValue; 1923 aCharacterData.SubstringData(0, endOffset, cutValue, aRv); 1924 if (NS_WARN_IF(aRv.Failed())) { 1925 return nullptr; 1926 } 1927 clone = aCharacterData.CloneNode(false, aRv); 1928 if (NS_WARN_IF(aRv.Failed())) { 1929 return nullptr; 1930 } 1931 clone->SetNodeValueInternal(cutValue, aRv); 1932 if (NS_WARN_IF(aRv.Failed())) { 1933 return nullptr; 1934 } 1935 } 1936 1937 aCharacterData.DeleteData(0, endOffset, aRv); 1938 if (NS_WARN_IF(aRv.Failed())) { 1939 return nullptr; 1940 } 1941 return clone.forget(); 1942 } 1943 1944 void nsRange::CutContents(DocumentFragment** aFragment, 1945 ElementHandler aElementHandler, ErrorResult& aRv) { 1946 if (aFragment && aElementHandler) { 1947 // Theoretically no reason it can't be handled, but not plumbed in enough to 1948 // test. 1949 MOZ_ASSERT_UNREACHABLE("Not handling both aFragment and aElementHandler"); 1950 aRv.Throw(NS_ERROR_UNEXPECTED); 1951 return; 1952 } 1953 if (aFragment) { 1954 *aFragment = nullptr; 1955 } 1956 1957 if (!CanAccess(*GetMayCrossShadowBoundaryStartContainer()) || 1958 !CanAccess(*GetMayCrossShadowBoundaryEndContainer())) { 1959 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1960 return; 1961 } 1962 1963 nsCOMPtr<Document> doc = mStart.GetContainer()->OwnerDoc(); 1964 1965 nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer( 1966 aRv, StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() 1967 ? AllowRangeCrossShadowBoundary::Yes 1968 : AllowRangeCrossShadowBoundary::No); 1969 if (aRv.Failed()) { 1970 return; 1971 } 1972 1973 // If aFragment isn't null, create a temporary fragment to hold our return. 1974 RefPtr<DocumentFragment> retval; 1975 if (aFragment) { 1976 retval = 1977 new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager()); 1978 } 1979 nsCOMPtr<nsINode> commonCloneAncestor = retval.get(); 1980 1981 // Save the range end points locally to avoid interference 1982 // of Range gravity during our edits! 1983 1984 const RangeBoundary startRef = MayCrossShadowBoundaryStartRef(); 1985 const RangeBoundary endRef = MayCrossShadowBoundaryEndRef(); 1986 1987 // `GetCommonAncestorContainer()` above ensures the range is positioned, hence 1988 // there have to be valid offsets. Fix them in startRef/endRef right now. 1989 const uint32_t startOffset = 1990 *startRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 1991 const uint32_t endOffset = 1992 *endRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 1993 1994 if (retval) { 1995 // For extractContents(), abort early if there's a doctype (bug 719533). 1996 // This can happen only if the common ancestor is a document, in which case 1997 // we just need to find its doctype child and check if that's in the range. 1998 nsCOMPtr<Document> commonAncestorDocument = 1999 do_QueryInterface(commonAncestor); 2000 if (commonAncestorDocument) { 2001 RefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype(); 2002 2003 // `GetCommonAncestorContainer()` above ensured the range is positioned. 2004 // Hence, start and end are both set and valid. If available, `doctype` 2005 // has a common ancestor with start and end, hence both have to be 2006 // comparable to it. 2007 if (doctype && 2008 *nsContentUtils::ComparePointsWithIndices( 2009 startRef.GetContainer(), startOffset, doctype, 0) < 0 && 2010 *nsContentUtils::ComparePointsWithIndices( 2011 doctype, 0, endRef.GetContainer(), endOffset) < 0) { 2012 aRv.ThrowHierarchyRequestError("Start or end position isn't valid."); 2013 return; 2014 } 2015 } 2016 } 2017 2018 // Create and initialize a subtree iterator that will give 2019 // us all the subtrees within the range. 2020 2021 RangeSubtreeIterator iter; 2022 2023 aRv = iter.Init(this, 2024 StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() 2025 ? AllowRangeCrossShadowBoundary::Yes 2026 : AllowRangeCrossShadowBoundary::No); 2027 if (aRv.Failed()) { 2028 return; 2029 } 2030 2031 if (iter.IsDone()) { 2032 // There's nothing for us to delete. 2033 aRv = CollapseRangeAfterDelete(this); 2034 if (!aRv.Failed() && aFragment) { 2035 retval.forget(aFragment); 2036 } 2037 return; 2038 } 2039 2040 iter.First(); 2041 2042 // With the exception of text nodes that contain one of the range 2043 // end points, the subtree iterator should only give us back subtrees 2044 // that are completely contained between the range's end points. 2045 2046 while (!iter.IsDone()) { 2047 nsCOMPtr<nsINode> nodeToResult; 2048 const nsCOMPtr<nsINode> node = iter.GetCurrentNode(); 2049 2050 // Before we delete anything, advance the iterator to the next node that's 2051 // not a descendant of this one. XXX It's a bit silly to iterate through 2052 // the descendants only to throw them out, we should use an iterator that 2053 // skips the descendants to begin with. 2054 2055 iter.Next(); 2056 nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode(); 2057 while (nextNode && nextNode->IsInclusiveDescendantOf(node)) { 2058 iter.Next(); 2059 nextNode = iter.GetCurrentNode(); 2060 } 2061 2062 if (node == startRef.GetContainer() || node == endRef.GetContainer()) { 2063 // If it's CharacterData, make sure we might need to delete 2064 // part of the data, instead of removing the whole node. 2065 // 2066 // XXX_kin: We need to also handle ProcessingInstruction 2067 // XXX_kin: according to the spec. 2068 if (auto* const charData = CharacterData::FromNode(node)) { 2069 nsMutationGuard guard; 2070 nodeToResult = 2071 CutCharacterData(*charData, startRef, endRef, !!retval, aRv); 2072 if (MOZ_UNLIKELY(aRv.Failed())) { 2073 return; 2074 } 2075 // FYI: Cutting some data from a CharacterData shouldn't cause any other 2076 // mutations of the composed tree. Therefore, basically, we should be 2077 // able to skip this check. However, the accessible caret may update 2078 // its caret and that appears as some numbers of mutations. Therefore, 2079 // this expensive validation needs to run if the accessible caret is now 2080 // enabled. 2081 if (guard.Mutated(0) && !ValidateCurrentNode(this, iter)) { 2082 aRv.Throw(NS_ERROR_UNEXPECTED); 2083 return; 2084 } 2085 } else if (auto* const element = Element::FromNode(node)) { 2086 // If the start boundary is the end of the start container which is an 2087 // element, RangeSubtreeIterator iterates the start container element as 2088 // the first node. Otherwise, and the end boundary is the start of the 2089 // end container which is an element, RangeSubtreeIterator iterates the 2090 // end container element as the last node. 2091 if ((element == endRef.GetContainer() && endRef.IsStartOfContainer()) || 2092 (element == startRef.GetContainer() && 2093 startRef.IsEndOfContainer())) { 2094 // Then, we want to return the empty cloned container since nothing is 2095 // selected by this range in the element. However, we don't want to 2096 // delete the element itself. 2097 // Note that even if the start/end container is a void element like 2098 // <img>, <input>, etc, we anyway clone the void element so that the 2099 // replaced content will be duplicated without deleting them from the 2100 // DOM. This behavior matches with Chrome 142 and Safari 26.1. 2101 if (retval) { 2102 nodeToResult = element->CloneNode(false, aRv); 2103 if (aRv.Failed()) { 2104 return; 2105 } 2106 } 2107 } else { 2108 // The current node is an element and the start or end container of 2109 // the range and the offset is middle of the element. However, 2110 // RangeSubtreeIterator should not iterate this because of outside of 2111 // the range. I.e., this should never happen. 2112 MOZ_DIAGNOSTIC_ASSERT( 2113 false, "The container shouldn't be iterated due to out of range"); 2114 continue; // Just ignore the illegal case in the release channel. 2115 } 2116 } else { 2117 // The current node which is the same as the start container or the end 2118 // container of the range is not a CharacterData nor an element but the 2119 // node is the start or end container of the range. This should be an 2120 // illegal case too. 2121 MOZ_ASSERT(node == startRef.GetContainer() || 2122 node == endRef.GetContainer()); 2123 MOZ_ASSERT(!node->IsCharacterData() && !node->IsElement()); 2124 NS_WARNING( 2125 nsPrintfCString("Unexpected type of content node (%s) is iterated " 2126 "by RangeSubtreeIterator", 2127 mozilla::ToString(*node).c_str()) 2128 .get()); 2129 MOZ_DIAGNOSTIC_ASSERT(false, 2130 "Unexpected type of content node is iterated by " 2131 "RangeSubtreeIterator"); 2132 continue; // Just ignore the illegal case in the release channel. 2133 } 2134 } else { 2135 // The current node is completely in the range so that we can remove it 2136 // from the document. 2137 MOZ_ASSERT(node != startRef.GetContainer() && 2138 node != endRef.GetContainer()); 2139 // When this is called, the current node must be completely contained 2140 // within the range. 2141 if (aElementHandler && node->IsElement()) { 2142 // This is an element, and the caller specified a handler for it, so 2143 // use it. 2144 MOZ_ASSERT(!aFragment, "Fragment requested when ElementHandler given?"); 2145 nsMutationGuard guard; 2146 auto* const element = node->AsElement(); 2147 aElementHandler(element); 2148 // No need to validate - we know this node is an element, so any 2149 // case that may cause the node to fail to validate is covered by 2150 // the mutation guard. 2151 if (MOZ_UNLIKELY(guard.Mutated(0))) { 2152 aRv.Throw(NS_ERROR_UNEXPECTED); 2153 return; 2154 } 2155 } else { 2156 // Otherwise, just remove it from the tree. 2157 nodeToResult = node; 2158 } 2159 } 2160 2161 uint32_t parentCount = 0; 2162 // Set the result to document fragment if we have 'retval'. 2163 if (retval) { 2164 nsCOMPtr<nsINode> oldCommonAncestor = commonAncestor; 2165 if (!iter.IsDone()) { 2166 // Setup the parameters for the next iteration of the loop. 2167 if (!nextNode) { 2168 aRv.Throw(NS_ERROR_UNEXPECTED); 2169 return; 2170 } 2171 2172 // Get node's and nextNode's common parent. Do this before moving 2173 // nodes from original DOM to result fragment. 2174 commonAncestor = 2175 nsContentUtils::GetClosestCommonInclusiveAncestor(node, nextNode); 2176 if (!commonAncestor) { 2177 aRv.Throw(NS_ERROR_UNEXPECTED); 2178 return; 2179 } 2180 2181 nsCOMPtr<nsINode> parentCounterNode = node; 2182 while (parentCounterNode && parentCounterNode != commonAncestor) { 2183 ++parentCount; 2184 parentCounterNode = parentCounterNode->GetParentNode(); 2185 if (!parentCounterNode) { 2186 aRv.Throw(NS_ERROR_UNEXPECTED); 2187 return; 2188 } 2189 } 2190 } 2191 2192 // Clone the parent hierarchy between commonAncestor and node. 2193 nsCOMPtr<nsINode> closestAncestor, farthestAncestor; 2194 aRv = CloneParentsBetween(oldCommonAncestor, node, 2195 getter_AddRefs(closestAncestor), 2196 getter_AddRefs(farthestAncestor)); 2197 if (aRv.Failed()) { 2198 return; 2199 } 2200 2201 if (farthestAncestor) { 2202 commonCloneAncestor->AppendChild(*farthestAncestor, aRv); 2203 if (NS_WARN_IF(aRv.Failed())) { 2204 return; 2205 } 2206 } 2207 2208 nsMutationGuard guard; 2209 const bool isCloneNode = !nodeToResult->GetParentNode(); 2210 if (closestAncestor) { 2211 closestAncestor->AppendChild(*nodeToResult, aRv); 2212 } else { 2213 commonCloneAncestor->AppendChild(*nodeToResult, aRv); 2214 } 2215 if (NS_WARN_IF(aRv.Failed())) { 2216 return; 2217 } 2218 // If the node is a clone, its removal is not happen. Therefore, only the 2219 // connecting mutation should be counted. 2220 // If the node is in the document, both it's removal from the document 2221 // and connecting to the new parent should be counted. 2222 // 2223 // When removing the node from document, DevTools may break on the removal 2224 // and the user may modify the DOM. Additionally, when the removing node 2225 // contains subdocuments, its `unload` and `beforeunload` are fired 2226 // synchronously. Then, the unloading is also counted as mutations. 2227 // Finally, if there is accessible caret, its mutations are also counted. 2228 // Therefore, we often need to run the expensive validation here. 2229 if (NS_WARN_IF(guard.Mutated(isCloneNode ? 1 : 2) && 2230 !ValidateCurrentNode(this, iter))) { 2231 aRv.Throw(NS_ERROR_UNEXPECTED); 2232 return; 2233 } 2234 } else if (nodeToResult) { 2235 if (const nsCOMPtr<nsINode> parent = nodeToResult->GetParentNode()) { 2236 nsMutationGuard guard; 2237 parent->RemoveChild(*nodeToResult, aRv); 2238 if (MOZ_UNLIKELY(aRv.Failed())) { 2239 return; 2240 } 2241 // When removing the node from document, DevTools may break on the 2242 // removal and the user may modify the DOM. Additionally, when the 2243 // removing node contains subdocuments, its `unload` and `beforeunload` 2244 // are fired synchronously. Then, the unloading is also counted as 2245 // mutations. Finally, if there is accessible caret, its mutations are 2246 // also counted. Therefore, we often need to run the expensive 2247 // validation here. 2248 if (NS_WARN_IF(guard.Mutated(1) && !ValidateCurrentNode(this, iter))) { 2249 aRv.Throw(NS_ERROR_UNEXPECTED); 2250 return; 2251 } 2252 } 2253 } 2254 2255 if (!iter.IsDone() && retval) { 2256 // Find the equivalent of commonAncestor in the cloned tree. 2257 nsCOMPtr<nsINode> newCloneAncestor = nodeToResult; 2258 for (uint32_t i = parentCount; i; --i) { 2259 newCloneAncestor = newCloneAncestor->GetParentNode(); 2260 if (!newCloneAncestor) { 2261 aRv.Throw(NS_ERROR_UNEXPECTED); 2262 return; 2263 } 2264 } 2265 commonCloneAncestor = newCloneAncestor; 2266 } 2267 } 2268 2269 aRv = CollapseRangeAfterDelete(this); 2270 if (!aRv.Failed() && aFragment) { 2271 retval.forget(aFragment); 2272 } 2273 } 2274 2275 void nsRange::DeleteContents(ErrorResult& aRv) { 2276 CutContents(nullptr, nullptr, aRv); 2277 } 2278 2279 already_AddRefed<DocumentFragment> nsRange::ExtractContents(ErrorResult& rv) { 2280 RefPtr<DocumentFragment> fragment; 2281 CutContents(getter_AddRefs(fragment), nullptr, rv); 2282 return fragment.forget(); 2283 } 2284 2285 int16_t nsRange::CompareBoundaryPoints(uint16_t aHow, 2286 const nsRange& aOtherRange, 2287 ErrorResult& aRv) { 2288 if (!mIsPositioned || !aOtherRange.IsPositioned()) { 2289 aRv.Throw(NS_ERROR_NOT_INITIALIZED); 2290 return 0; 2291 } 2292 2293 RawRangeBoundary ourBoundary, otherBoundary; 2294 switch (aHow) { 2295 case Range_Binding::START_TO_START: 2296 ourBoundary = mStart.AsRaw(); 2297 otherBoundary = aOtherRange.StartRef().AsRaw(); 2298 break; 2299 case Range_Binding::START_TO_END: 2300 ourBoundary = mEnd.AsRaw(); 2301 otherBoundary = aOtherRange.StartRef().AsRaw(); 2302 break; 2303 case Range_Binding::END_TO_START: 2304 ourBoundary = mStart.AsRaw(); 2305 otherBoundary = aOtherRange.EndRef().AsRaw(); 2306 break; 2307 case Range_Binding::END_TO_END: 2308 ourBoundary = mEnd.AsRaw(); 2309 otherBoundary = aOtherRange.EndRef().AsRaw(); 2310 break; 2311 default: 2312 // We were passed an illegal value 2313 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 2314 return 0; 2315 } 2316 2317 if (mRoot != aOtherRange.GetRoot()) { 2318 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR); 2319 return 0; 2320 } 2321 2322 const Maybe<int32_t> order = 2323 nsContentUtils::ComparePoints(ourBoundary, otherBoundary); 2324 2325 // `this` and `aOtherRange` share the same root and ourBoundary, otherBoundary 2326 // correspond to some of their boundaries. Hence, ourBoundary and 2327 // otherBoundary have to be comparable. 2328 return *order; 2329 } 2330 2331 /* static */ 2332 nsresult nsRange::CloneParentsBetween(nsINode* aAncestor, nsINode* aNode, 2333 nsINode** aClosestAncestor, 2334 nsINode** aFarthestAncestor) { 2335 NS_ENSURE_ARG_POINTER( 2336 (aAncestor && aNode && aClosestAncestor && aFarthestAncestor)); 2337 2338 *aClosestAncestor = nullptr; 2339 *aFarthestAncestor = nullptr; 2340 2341 if (aAncestor == aNode) return NS_OK; 2342 2343 AutoTArray<nsCOMPtr<nsINode>, 16> parentStack; 2344 2345 nsCOMPtr<nsINode> parent = aNode->GetParentNode(); 2346 while (parent && parent != aAncestor) { 2347 parentStack.AppendElement(parent); 2348 parent = parent->GetParentNode(); 2349 } 2350 2351 nsCOMPtr<nsINode> firstParent; 2352 nsCOMPtr<nsINode> lastParent; 2353 for (int32_t i = parentStack.Length() - 1; i >= 0; i--) { 2354 ErrorResult rv; 2355 nsCOMPtr<nsINode> clone = parentStack[i]->CloneNode(false, rv); 2356 2357 if (rv.Failed()) { 2358 return rv.StealNSResult(); 2359 } 2360 if (!clone) { 2361 return NS_ERROR_FAILURE; 2362 } 2363 2364 if (!lastParent) { 2365 lastParent = clone; 2366 } else { 2367 firstParent->AppendChild(*clone, rv); 2368 if (rv.Failed()) { 2369 return rv.StealNSResult(); 2370 } 2371 } 2372 2373 firstParent = clone; 2374 } 2375 2376 firstParent.forget(aClosestAncestor); 2377 lastParent.forget(aFarthestAncestor); 2378 2379 return NS_OK; 2380 } 2381 2382 already_AddRefed<DocumentFragment> nsRange::CloneContents(ErrorResult& aRv) { 2383 nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv); 2384 MOZ_ASSERT(!aRv.Failed(), "GetCommonAncestorContainer() shouldn't fail!"); 2385 2386 nsCOMPtr<Document> doc = mStart.GetContainer()->OwnerDoc(); 2387 NS_ASSERTION(doc, "CloneContents needs a document to continue."); 2388 if (!doc) { 2389 aRv.Throw(NS_ERROR_FAILURE); 2390 return nullptr; 2391 } 2392 2393 // Create a new document fragment in the context of this document, 2394 // which might be null 2395 2396 RefPtr<DocumentFragment> clonedFrag = 2397 new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager()); 2398 2399 if (Collapsed()) { 2400 return clonedFrag.forget(); 2401 } 2402 2403 nsCOMPtr<nsINode> commonCloneAncestor = clonedFrag.get(); 2404 2405 // Create and initialize a subtree iterator that will give 2406 // us all the subtrees within the range. 2407 2408 RangeSubtreeIterator iter; 2409 2410 aRv = iter.Init(this); 2411 if (aRv.Failed()) { 2412 return nullptr; 2413 } 2414 2415 if (iter.IsDone()) { 2416 // There's nothing to add to the doc frag, we must be done! 2417 return clonedFrag.forget(); 2418 } 2419 2420 iter.First(); 2421 2422 // With the exception of text nodes that contain one of the range 2423 // end points and elements which don't have any content selected the subtree 2424 // iterator should only give us back subtrees that are completely contained 2425 // between the range's end points. 2426 // 2427 // Unfortunately these subtrees don't contain the parent hierarchy/context 2428 // that the Range spec requires us to return. This loop clones the 2429 // parent hierarchy, adds a cloned version of the subtree, to it, then 2430 // correctly places this new subtree into the doc fragment. 2431 2432 while (!iter.IsDone()) { 2433 nsCOMPtr<nsINode> node = iter.GetCurrentNode(); 2434 bool deepClone = 2435 !node->IsElement() || 2436 (!(node == mEnd.GetContainer() && 2437 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets) == 0) && 2438 !(node == mStart.GetContainer() && 2439 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets) == 2440 node->AsElement()->GetChildCount())); 2441 2442 // Clone the current subtree! 2443 2444 nsCOMPtr<nsINode> clone = node->CloneNode(deepClone, aRv); 2445 if (aRv.Failed()) { 2446 return nullptr; 2447 } 2448 2449 // If it's CharacterData, make sure we only clone what 2450 // is in the range. 2451 // 2452 // XXX_kin: We need to also handle ProcessingInstruction 2453 // XXX_kin: according to the spec. 2454 2455 if (auto charData = CharacterData::FromNode(clone)) { 2456 if (node == mEnd.GetContainer()) { 2457 // We only need the data before mEndOffset, so get rid of any 2458 // data after it. 2459 2460 uint32_t dataLength = charData->Length(); 2461 if (dataLength > 2462 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets)) { 2463 charData->DeleteData( 2464 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 2465 dataLength - 2466 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 2467 aRv); 2468 if (aRv.Failed()) { 2469 return nullptr; 2470 } 2471 } 2472 } 2473 2474 if (node == mStart.GetContainer()) { 2475 // We don't need any data before mStartOffset, so just 2476 // delete it! 2477 2478 if (*mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets) > 0) { 2479 charData->DeleteData( 2480 0, *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 2481 aRv); 2482 if (aRv.Failed()) { 2483 return nullptr; 2484 } 2485 } 2486 } 2487 } 2488 2489 // Clone the parent hierarchy between commonAncestor and node. 2490 2491 nsCOMPtr<nsINode> closestAncestor, farthestAncestor; 2492 2493 aRv = CloneParentsBetween(commonAncestor, node, 2494 getter_AddRefs(closestAncestor), 2495 getter_AddRefs(farthestAncestor)); 2496 2497 if (aRv.Failed()) { 2498 return nullptr; 2499 } 2500 2501 // Hook the parent hierarchy/context of the subtree into the clone tree. 2502 2503 if (farthestAncestor) { 2504 commonCloneAncestor->AppendChild(*farthestAncestor, aRv); 2505 2506 if (aRv.Failed()) { 2507 return nullptr; 2508 } 2509 } 2510 2511 // Place the cloned subtree into the cloned doc frag tree! 2512 2513 nsCOMPtr<nsINode> cloneNode = clone; 2514 if (closestAncestor) { 2515 // Append the subtree under closestAncestor since it is the 2516 // immediate parent of the subtree. 2517 2518 closestAncestor->AppendChild(*cloneNode, aRv); 2519 } else { 2520 // If we get here, there is no missing parent hierarchy between 2521 // commonAncestor and node, so just append clone to commonCloneAncestor. 2522 2523 commonCloneAncestor->AppendChild(*cloneNode, aRv); 2524 } 2525 if (aRv.Failed()) { 2526 return nullptr; 2527 } 2528 2529 // Get the next subtree to be processed. The idea here is to setup 2530 // the parameters for the next iteration of the loop. 2531 2532 iter.Next(); 2533 2534 if (iter.IsDone()) break; // We must be done! 2535 2536 nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode(); 2537 if (!nextNode) { 2538 aRv.Throw(NS_ERROR_FAILURE); 2539 return nullptr; 2540 } 2541 2542 // Get node and nextNode's common parent. 2543 commonAncestor = 2544 nsContentUtils::GetClosestCommonInclusiveAncestor(node, nextNode); 2545 2546 if (!commonAncestor) { 2547 aRv.Throw(NS_ERROR_FAILURE); 2548 return nullptr; 2549 } 2550 2551 // Find the equivalent of commonAncestor in the cloned tree! 2552 2553 while (node && node != commonAncestor) { 2554 node = node->GetParentNode(); 2555 if (aRv.Failed()) { 2556 return nullptr; 2557 } 2558 2559 if (!node) { 2560 aRv.Throw(NS_ERROR_FAILURE); 2561 return nullptr; 2562 } 2563 2564 cloneNode = cloneNode->GetParentNode(); 2565 if (!cloneNode) { 2566 aRv.Throw(NS_ERROR_FAILURE); 2567 return nullptr; 2568 } 2569 } 2570 2571 commonCloneAncestor = cloneNode; 2572 } 2573 2574 return clonedFrag.forget(); 2575 } 2576 2577 already_AddRefed<nsRange> nsRange::CloneRange() const { 2578 RefPtr<nsRange> range = nsRange::Create(mOwner); 2579 range->DoSetRange(mStart, mEnd, mRoot); 2580 if (mCrossShadowBoundaryRange) { 2581 range->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 2582 mCrossShadowBoundaryRange->StartRef(), 2583 mCrossShadowBoundaryRange->EndRef()); 2584 } 2585 return range.forget(); 2586 } 2587 2588 void nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv) { 2589 if (!CanAccess(aNode)) { 2590 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 2591 return; 2592 } 2593 2594 if (!IsPositioned()) { 2595 aRv.Throw(NS_ERROR_NOT_INITIALIZED); 2596 return; 2597 } 2598 2599 uint32_t tStartOffset = StartOffset(); 2600 2601 nsCOMPtr<nsINode> tStartContainer = GetStartContainer(); 2602 2603 if (!CanAccess(*tStartContainer)) { 2604 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 2605 return; 2606 } 2607 2608 if (&aNode == tStartContainer) { 2609 aRv.ThrowHierarchyRequestError( 2610 "The inserted node can not be range's start node."); 2611 return; 2612 } 2613 2614 // This is the node we'll be inserting before, and its parent 2615 nsCOMPtr<nsINode> referenceNode; 2616 nsCOMPtr<nsINode> referenceParentNode = tStartContainer; 2617 2618 RefPtr<Text> startTextNode = tStartContainer->GetAsText(); 2619 nsCOMPtr<nsINodeList> tChildList; 2620 if (startTextNode) { 2621 referenceParentNode = tStartContainer->GetParentNode(); 2622 if (!referenceParentNode) { 2623 aRv.ThrowHierarchyRequestError( 2624 "Can not get range's start node's parent."); 2625 return; 2626 } 2627 2628 referenceParentNode->EnsurePreInsertionValidity(aNode, tStartContainer, 2629 aRv); 2630 if (aRv.Failed()) { 2631 return; 2632 } 2633 2634 RefPtr<Text> secondPart = startTextNode->SplitText(tStartOffset, aRv); 2635 if (aRv.Failed()) { 2636 return; 2637 } 2638 2639 referenceNode = secondPart; 2640 } else { 2641 tChildList = tStartContainer->ChildNodes(); 2642 2643 // find the insertion point in the DOM and insert the Node 2644 referenceNode = tChildList->Item(tStartOffset); 2645 2646 tStartContainer->EnsurePreInsertionValidity(aNode, referenceNode, aRv); 2647 if (aRv.Failed()) { 2648 return; 2649 } 2650 } 2651 2652 // We might need to update the end to include the new node (bug 433662). 2653 // Ideally we'd only do this if needed, but it's tricky to know when it's 2654 // needed in advance (bug 765799). 2655 uint32_t newOffset; 2656 2657 if (referenceNode) { 2658 Maybe<uint32_t> indexInParent = referenceNode->ComputeIndexInParentNode(); 2659 if (MOZ_UNLIKELY(NS_WARN_IF(indexInParent.isNothing()))) { 2660 aRv.Throw(NS_ERROR_FAILURE); 2661 return; 2662 } 2663 newOffset = *indexInParent; 2664 } else { 2665 newOffset = tChildList->Length(); 2666 } 2667 2668 if (aNode.NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) { 2669 newOffset += aNode.GetChildCount(); 2670 } else { 2671 newOffset++; 2672 } 2673 2674 // Now actually insert the node 2675 nsCOMPtr<nsINode> tResultNode; 2676 tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv); 2677 if (aRv.Failed()) { 2678 return; 2679 } 2680 2681 if (Collapsed()) { 2682 aRv = SetEnd(referenceParentNode, newOffset); 2683 } 2684 } 2685 2686 void nsRange::SurroundContents(nsINode& aNewParent, ErrorResult& aRv) { 2687 if (!CanAccess(aNewParent)) { 2688 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 2689 return; 2690 } 2691 2692 if (!mRoot) { 2693 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 2694 return; 2695 } 2696 // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text 2697 // node. 2698 if (mStart.GetContainer() != mEnd.GetContainer()) { 2699 bool startIsText = mStart.GetContainer()->IsText(); 2700 bool endIsText = mEnd.GetContainer()->IsText(); 2701 nsINode* startGrandParent = mStart.GetContainer()->GetParentNode(); 2702 nsINode* endGrandParent = mEnd.GetContainer()->GetParentNode(); 2703 if (!((startIsText && endIsText && startGrandParent && 2704 startGrandParent == endGrandParent) || 2705 (startIsText && startGrandParent && 2706 startGrandParent == mEnd.GetContainer()) || 2707 (endIsText && endGrandParent && 2708 endGrandParent == mStart.GetContainer()))) { 2709 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 2710 return; 2711 } 2712 } 2713 2714 // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted 2715 // (Document, DocumentType, DocumentFragment) 2716 uint16_t nodeType = aNewParent.NodeType(); 2717 if (nodeType == nsINode::DOCUMENT_NODE || 2718 nodeType == nsINode::DOCUMENT_TYPE_NODE || 2719 nodeType == nsINode::DOCUMENT_FRAGMENT_NODE) { 2720 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); 2721 return; 2722 } 2723 2724 // Extract the contents within the range. 2725 2726 RefPtr<DocumentFragment> docFrag = ExtractContents(aRv); 2727 2728 if (aRv.Failed()) { 2729 return; 2730 } 2731 2732 if (!docFrag) { 2733 aRv.Throw(NS_ERROR_FAILURE); 2734 return; 2735 } 2736 2737 // Spec says we need to remove all of aNewParent's 2738 // children prior to insertion. 2739 2740 nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes(); 2741 if (!children) { 2742 aRv.Throw(NS_ERROR_FAILURE); 2743 return; 2744 } 2745 2746 uint32_t numChildren = children->Length(); 2747 2748 while (numChildren) { 2749 nsCOMPtr<nsINode> child = children->Item(--numChildren); 2750 if (!child) { 2751 aRv.Throw(NS_ERROR_FAILURE); 2752 return; 2753 } 2754 2755 aNewParent.RemoveChild(*child, aRv); 2756 if (aRv.Failed()) { 2757 return; 2758 } 2759 } 2760 2761 // Insert aNewParent at the range's start point. 2762 2763 InsertNode(aNewParent, aRv); 2764 if (aRv.Failed()) { 2765 return; 2766 } 2767 2768 // Append the content we extracted under aNewParent. 2769 aNewParent.AppendChild(*docFrag, aRv); 2770 if (aRv.Failed()) { 2771 return; 2772 } 2773 2774 // Select aNewParent, and its contents. 2775 2776 SelectNode(aNewParent, aRv); 2777 } 2778 2779 void nsRange::ToString(nsAString& aReturn, ErrorResult& aErr) { 2780 // clear the string 2781 aReturn.Truncate(); 2782 2783 // If we're unpositioned, return the empty string 2784 if (!mIsPositioned) { 2785 return; 2786 } 2787 2788 #ifdef DEBUG_range 2789 printf("Range dump: -----------------------\n"); 2790 #endif /* DEBUG */ 2791 2792 // effeciency hack for simple case 2793 if (mStart.GetContainer() == mEnd.GetContainer()) { 2794 Text* textNode = 2795 mStart.GetContainer() ? mStart.GetContainer()->GetAsText() : nullptr; 2796 2797 if (textNode) { 2798 #ifdef DEBUG_range 2799 // If debug, dump it: 2800 textNode->List(stdout); 2801 printf("End Range dump: -----------------------\n"); 2802 #endif /* DEBUG */ 2803 2804 // grab the text 2805 textNode->SubstringData( 2806 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 2807 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets) - 2808 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 2809 aReturn, aErr); 2810 return; 2811 } 2812 } 2813 2814 /* complex case: mStart.GetContainer() != mEnd.GetContainer(), or mStartParent 2815 not a text node revisit - there are potential optimizations here and also 2816 tradeoffs. 2817 */ 2818 2819 PostContentIterator postOrderIter; 2820 nsresult rv = postOrderIter.Init(this); 2821 if (NS_WARN_IF(NS_FAILED(rv))) { 2822 aErr.Throw(rv); 2823 return; 2824 } 2825 2826 nsString tempString; 2827 2828 // loop through the content iterator, which returns nodes in the range in 2829 // close tag order, and grab the text from any text node 2830 for (; !postOrderIter.IsDone(); postOrderIter.Next()) { 2831 nsINode* n = postOrderIter.GetCurrentNode(); 2832 2833 #ifdef DEBUG_range 2834 // If debug, dump it: 2835 n->List(stdout); 2836 #endif /* DEBUG */ 2837 Text* textNode = n->GetAsText(); 2838 if (textNode) // if it's a text node, get the text 2839 { 2840 if (n == mStart.GetContainer()) { // only include text past start offset 2841 uint32_t strLength = textNode->Length(); 2842 textNode->SubstringData( 2843 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 2844 strLength - 2845 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 2846 tempString, IgnoreErrors()); 2847 aReturn += tempString; 2848 } else if (n == 2849 mEnd.GetContainer()) { // only include text before end offset 2850 textNode->SubstringData( 2851 0, *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 2852 tempString, IgnoreErrors()); 2853 aReturn += tempString; 2854 } else { // grab the whole kit-n-kaboodle 2855 textNode->GetData(tempString); 2856 aReturn += tempString; 2857 } 2858 } 2859 } 2860 2861 #ifdef DEBUG_range 2862 printf("End Range dump: -----------------------\n"); 2863 #endif /* DEBUG */ 2864 } 2865 2866 void nsRange::Detach() {} 2867 2868 already_AddRefed<DocumentFragment> nsRange::CreateContextualFragment( 2869 const nsAString& aFragment, ErrorResult& aRv) const { 2870 if (!mIsPositioned) { 2871 aRv.Throw(NS_ERROR_FAILURE); 2872 return nullptr; 2873 } 2874 2875 return nsContentUtils::CreateContextualFragment(mStart.GetContainer(), 2876 aFragment, false, aRv); 2877 } 2878 2879 already_AddRefed<DocumentFragment> nsRange::CreateContextualFragment( 2880 const TrustedHTMLOrString& aFragment, nsIPrincipal* aSubjectPrincipal, 2881 ErrorResult& aRv) const { 2882 if (!mIsPositioned) { 2883 aRv.Throw(NS_ERROR_FAILURE); 2884 return nullptr; 2885 } 2886 MOZ_ASSERT(mStart.GetContainer()); 2887 2888 constexpr nsLiteralString sink = u"Range createContextualFragment"_ns; 2889 Maybe<nsAutoString> compliantStringHolder; 2890 nsCOMPtr<nsINode> node = mStart.GetContainer(); 2891 const nsAString* compliantString = 2892 TrustedTypeUtils::GetTrustedTypesCompliantString( 2893 aFragment, sink, kTrustedTypesOnlySinkGroup, *node, aSubjectPrincipal, 2894 compliantStringHolder, aRv); 2895 if (aRv.Failed()) { 2896 return nullptr; 2897 } 2898 2899 return nsContentUtils::CreateContextualFragment(mStart.GetContainer(), 2900 *compliantString, false, aRv); 2901 } 2902 2903 static void ExtractRectFromOffset(nsIFrame* aFrame, const int32_t aOffset, 2904 nsRect* aR, bool aFlushToOriginEdge, 2905 bool aClampToEdge) { 2906 MOZ_ASSERT(aFrame); 2907 MOZ_ASSERT(aR); 2908 2909 nsPoint point; 2910 aFrame->GetPointFromOffset(aOffset, &point); 2911 2912 // Determine if aFrame has a vertical writing mode, which will change our math 2913 // on the output rect. 2914 bool isVertical = aFrame->GetWritingMode().IsVertical(); 2915 2916 if (!aClampToEdge && !aR->Contains(point)) { 2917 // If point is outside aR, and we aren't clamping, output an empty rect 2918 // with origin at the point. 2919 if (isVertical) { 2920 aR->SetHeight(0); 2921 aR->y = point.y; 2922 } else { 2923 aR->SetWidth(0); 2924 aR->x = point.x; 2925 } 2926 return; 2927 } 2928 2929 if (aClampToEdge) { 2930 point = aR->ClampPoint(point); 2931 } 2932 2933 // point is within aR, and now we'll modify aR to output a rect that has point 2934 // on one edge. But which edge? 2935 if (aFlushToOriginEdge) { 2936 // The output rect should be flush to the edge of aR that contains the 2937 // origin. 2938 if (isVertical) { 2939 aR->SetHeight(point.y - aR->y); 2940 } else { 2941 aR->SetWidth(point.x - aR->x); 2942 } 2943 } else { 2944 // The output rect should be flush to the edge of aR opposite the origin. 2945 if (isVertical) { 2946 aR->SetHeight(aR->YMost() - point.y); 2947 aR->y = point.y; 2948 } else { 2949 aR->SetWidth(aR->XMost() - point.x); 2950 aR->x = point.x; 2951 } 2952 } 2953 } 2954 2955 static nsTextFrame* GetTextFrameForContent(nsIContent* aContent, 2956 bool aFlushLayout) { 2957 RefPtr<Document> doc = aContent->OwnerDoc(); 2958 PresShell* presShell = doc->GetPresShell(); 2959 if (!presShell) { 2960 return nullptr; 2961 } 2962 2963 // Try to un-suppress whitespace if needed, but only if we'll be able to flush 2964 // to immediately see the results of the un-suppression. If we can't flush 2965 // here, then calling EnsureFrameForTextNodeIsCreatedAfterFlush would be 2966 // pointless anyway. 2967 if (aFlushLayout) { 2968 const bool frameWillBeUnsuppressed = 2969 presShell->FrameConstructor() 2970 ->EnsureFrameForTextNodeIsCreatedAfterFlush( 2971 static_cast<CharacterData*>(aContent)); 2972 if (frameWillBeUnsuppressed) { 2973 doc->FlushPendingNotifications(FlushType::Layout); 2974 } 2975 } 2976 2977 nsIFrame* frame = aContent->GetPrimaryFrame(); 2978 if (!frame || !frame->IsTextFrame()) { 2979 return nullptr; 2980 } 2981 return static_cast<nsTextFrame*>(frame); 2982 } 2983 2984 static nsresult GetPartialTextRect(RectCallback* aCallback, 2985 Sequence<nsString>* aTextList, 2986 nsIContent* aContent, int32_t aStartOffset, 2987 int32_t aEndOffset, bool aClampToEdge, 2988 bool aFlushLayout) { 2989 nsTextFrame* textFrame = GetTextFrameForContent(aContent, aFlushLayout); 2990 if (textFrame) { 2991 nsIFrame* relativeTo = 2992 nsLayoutUtils::GetContainingBlockForClientRect(textFrame); 2993 2994 for (nsTextFrame* f = textFrame->FindContinuationForOffset(aStartOffset); f; 2995 f = static_cast<nsTextFrame*>(f->GetNextContinuation())) { 2996 int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd(); 2997 if (fend <= aStartOffset) { 2998 continue; 2999 } 3000 if (fstart >= aEndOffset) { 3001 break; 3002 } 3003 3004 // Calculate the text content offsets we'll need if text is requested. 3005 int32_t textContentStart = fstart; 3006 int32_t textContentEnd = fend; 3007 3008 // overlapping with the offset we want 3009 f->EnsureTextRun(nsTextFrame::eInflated); 3010 NS_ENSURE_TRUE(f->GetTextRun(nsTextFrame::eInflated), 3011 NS_ERROR_OUT_OF_MEMORY); 3012 bool topLeftToBottomRight = 3013 !f->GetTextRun(nsTextFrame::eInflated)->IsInlineReversed(); 3014 nsRect r = f->GetRectRelativeToSelf(); 3015 if (fstart < aStartOffset) { 3016 // aStartOffset is within this frame 3017 ExtractRectFromOffset(f, aStartOffset, &r, !topLeftToBottomRight, 3018 aClampToEdge); 3019 textContentStart = aStartOffset; 3020 } 3021 if (fend > aEndOffset) { 3022 // aEndOffset is in the middle of this frame 3023 ExtractRectFromOffset(f, aEndOffset, &r, topLeftToBottomRight, 3024 aClampToEdge); 3025 textContentEnd = aEndOffset; 3026 } 3027 r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, relativeTo); 3028 aCallback->AddRect(r); 3029 3030 // Finally capture the text, if requested. 3031 if (aTextList) { 3032 nsIFrame::RenderedText renderedText = 3033 f->GetRenderedText(textContentStart, textContentEnd, 3034 nsIFrame::TextOffsetType::OffsetsInContentText, 3035 nsIFrame::TrailingWhitespace::DontTrim); 3036 3037 NS_ENSURE_TRUE(aTextList->AppendElement(renderedText.mString, fallible), 3038 NS_ERROR_OUT_OF_MEMORY); 3039 } 3040 } 3041 } 3042 return NS_OK; 3043 } 3044 3045 static void CollectClientRectsForSubtree( 3046 nsINode* aNode, RectCallback* aCollector, Sequence<nsString>* aTextList, 3047 nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer, 3048 uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout, bool aTextOnly) { 3049 auto* content = nsIContent::FromNode(aNode); 3050 if (!content) { 3051 return; 3052 } 3053 3054 const bool isText = content->IsText(); 3055 if (isText) { 3056 if (aNode == aStartContainer) { 3057 int32_t offset = aStartContainer == aEndContainer 3058 ? static_cast<int32_t>(aEndOffset) 3059 : content->AsText()->TextDataLength(); 3060 GetPartialTextRect(aCollector, aTextList, content, 3061 static_cast<int32_t>(aStartOffset), offset, 3062 aClampToEdge, aFlushLayout); 3063 return; 3064 } 3065 3066 if (aNode == aEndContainer) { 3067 GetPartialTextRect(aCollector, aTextList, content, 0, 3068 static_cast<int32_t>(aEndOffset), aClampToEdge, 3069 aFlushLayout); 3070 return; 3071 } 3072 } 3073 3074 if (nsIFrame* frame = content->GetPrimaryFrame()) { 3075 if (!aTextOnly || isText) { 3076 nsLayoutUtils::GetAllInFlowRectsAndTexts( 3077 frame, nsLayoutUtils::GetContainingBlockForClientRect(frame), 3078 aCollector, aTextList, 3079 nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms); 3080 if (isText) { 3081 return; 3082 } 3083 aTextOnly = true; 3084 // We just get the text when calling GetAllInFlowRectsAndTexts, so we 3085 // don't need to call it again when visiting the children. 3086 aTextList = nullptr; 3087 } 3088 } else if (!content->IsElement() || 3089 !content->AsElement()->IsDisplayContents()) { 3090 return; 3091 } 3092 3093 FlattenedChildIterator childIter(content); 3094 for (nsIContent* child = childIter.GetNextChild(); child; 3095 child = childIter.GetNextChild()) { 3096 CollectClientRectsForSubtree(child, aCollector, aTextList, aStartContainer, 3097 aStartOffset, aEndContainer, aEndOffset, 3098 aClampToEdge, aFlushLayout, aTextOnly); 3099 } 3100 } 3101 3102 /* static */ 3103 void nsRange::CollectClientRectsAndText( 3104 RectCallback* aCollector, Sequence<nsString>* aTextList, nsRange* aRange, 3105 nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer, 3106 uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout) { 3107 // Currently, this method is called with start of end offset of nsRange. 3108 // So, they must be between 0 - INT32_MAX. 3109 MOZ_ASSERT(RangeUtils::IsValidOffset(aStartOffset)); 3110 MOZ_ASSERT(RangeUtils::IsValidOffset(aEndOffset)); 3111 3112 // Hold strong pointers across the flush 3113 nsCOMPtr<nsINode> startContainer = aStartContainer; 3114 nsCOMPtr<nsINode> endContainer = aEndContainer; 3115 3116 // Flush out layout so our frames are up to date. 3117 if (!aStartContainer->IsInComposedDoc()) { 3118 return; 3119 } 3120 3121 if (aFlushLayout) { 3122 if (auto* content = nsIContent::FromNode(aStartContainer)) { 3123 content->GetPrimaryFrame(FlushType::Layout); 3124 } else { 3125 aStartContainer->OwnerDoc()->FlushPendingNotifications(FlushType::Layout); 3126 } 3127 // Recheck whether we're still in the document 3128 if (!aStartContainer->IsInComposedDoc()) { 3129 return; 3130 } 3131 } 3132 3133 RangeSubtreeIterator iter; 3134 3135 nsresult rv = iter.Init(aRange); 3136 if (NS_FAILED(rv)) return; 3137 3138 if (iter.IsDone()) { 3139 // the range is collapsed, only continue if the cursor is in a text node 3140 if (aStartContainer->IsText()) { 3141 nsTextFrame* textFrame = 3142 GetTextFrameForContent(aStartContainer->AsText(), aFlushLayout); 3143 if (textFrame) { 3144 int32_t outOffset; 3145 nsIFrame* outFrame; 3146 textFrame->GetChildFrameContainingOffset( 3147 static_cast<int32_t>(aStartOffset), false, &outOffset, &outFrame); 3148 if (outFrame) { 3149 nsIFrame* relativeTo = 3150 nsLayoutUtils::GetContainingBlockForClientRect(outFrame); 3151 nsRect r = outFrame->GetRectRelativeToSelf(); 3152 ExtractRectFromOffset(outFrame, static_cast<int32_t>(aStartOffset), 3153 &r, false, aClampToEdge); 3154 r.SetWidth(0); 3155 r = nsLayoutUtils::TransformFrameRectToAncestor(outFrame, r, 3156 relativeTo); 3157 aCollector->AddRect(r); 3158 } 3159 } 3160 } 3161 return; 3162 } 3163 3164 do { 3165 nsCOMPtr<nsINode> node = iter.GetCurrentNode(); 3166 iter.Next(); 3167 3168 CollectClientRectsForSubtree(node, aCollector, aTextList, aStartContainer, 3169 aStartOffset, aEndContainer, aEndOffset, 3170 aClampToEdge, aFlushLayout, false); 3171 } while (!iter.IsDone()); 3172 } 3173 3174 already_AddRefed<DOMRect> nsRange::GetBoundingClientRect(bool aClampToEdge, 3175 bool aFlushLayout) { 3176 RefPtr<DOMRect> rect = new DOMRect(ToSupports(mOwner)); 3177 if (!mIsPositioned) { 3178 return rect.forget(); 3179 } 3180 3181 nsLayoutUtils::RectAccumulator accumulator; 3182 CollectClientRectsAndText( 3183 &accumulator, nullptr, this, mStart.GetContainer(), 3184 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 3185 mEnd.GetContainer(), 3186 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), aClampToEdge, 3187 aFlushLayout); 3188 3189 nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect 3190 : accumulator.mResultRect; 3191 rect->SetLayoutRect(r); 3192 return rect.forget(); 3193 } 3194 3195 already_AddRefed<DOMRectList> nsRange::GetClientRects(bool aClampToEdge, 3196 bool aFlushLayout) { 3197 return GetClientRectsInner(AllowRangeCrossShadowBoundary::No, aClampToEdge, 3198 aFlushLayout); 3199 } 3200 3201 already_AddRefed<DOMRectList> nsRange::GetAllowCrossShadowBoundaryClientRects( 3202 bool aClampToEdge, bool aFlushLayout) { 3203 return GetClientRectsInner(AllowRangeCrossShadowBoundary::Yes, aClampToEdge, 3204 aFlushLayout); 3205 } 3206 3207 already_AddRefed<DOMRectList> nsRange::GetClientRectsInner( 3208 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundaryRange, 3209 bool aClampToEdge, bool aFlushLayout) { 3210 if (!mIsPositioned) { 3211 return nullptr; 3212 } 3213 3214 RefPtr<DOMRectList> rectList = new DOMRectList(ToSupports(mOwner)); 3215 3216 nsLayoutUtils::RectListBuilder builder(rectList); 3217 3218 const auto& startRef = 3219 aAllowCrossShadowBoundaryRange == AllowRangeCrossShadowBoundary::Yes 3220 ? MayCrossShadowBoundaryStartRef() 3221 : mStart; 3222 const auto& endRef = 3223 aAllowCrossShadowBoundaryRange == AllowRangeCrossShadowBoundary::Yes 3224 ? MayCrossShadowBoundaryEndRef() 3225 : mEnd; 3226 3227 CollectClientRectsAndText( 3228 &builder, nullptr, this, startRef.GetContainer(), 3229 *startRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 3230 endRef.GetContainer(), 3231 *endRef.Offset(RangeBoundary::OffsetFilter::kValidOffsets), aClampToEdge, 3232 aFlushLayout); 3233 return rectList.forget(); 3234 } 3235 3236 void nsRange::GetClientRectsAndTexts(mozilla::dom::ClientRectsAndTexts& aResult, 3237 ErrorResult& aErr) { 3238 if (!mIsPositioned) { 3239 return; 3240 } 3241 3242 aResult.mRectList = new DOMRectList(ToSupports(mOwner)); 3243 3244 nsLayoutUtils::RectListBuilder builder(aResult.mRectList); 3245 3246 CollectClientRectsAndText( 3247 &builder, &aResult.mTextList, this, mStart.GetContainer(), 3248 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 3249 mEnd.GetContainer(), 3250 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), true, true); 3251 } 3252 3253 nsresult nsRange::GetUsedFontFaces(nsLayoutUtils::UsedFontFaceList& aResult, 3254 uint32_t aMaxRanges, 3255 bool aSkipCollapsedWhitespace) { 3256 NS_ENSURE_TRUE(mIsPositioned, NS_ERROR_UNEXPECTED); 3257 3258 nsCOMPtr<nsINode> startContainer = mStart.GetContainer(); 3259 nsCOMPtr<nsINode> endContainer = mEnd.GetContainer(); 3260 3261 // Flush out layout so our frames are up to date. 3262 Document* doc = mStart.GetContainer()->OwnerDoc(); 3263 NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); 3264 doc->FlushPendingNotifications(FlushType::Frames); 3265 3266 // Recheck whether we're still in the document 3267 NS_ENSURE_TRUE(mStart.IsSetAndInComposedDoc(), NS_ERROR_UNEXPECTED); 3268 3269 // A table to map gfxFontEntry objects to InspectorFontFace objects. 3270 // This table does NOT own the InspectorFontFace objects, it only holds 3271 // raw pointers to them. They are owned by the aResult array. 3272 nsLayoutUtils::UsedFontFaceTable fontFaces; 3273 3274 RangeSubtreeIterator iter; 3275 nsresult rv = iter.Init(this); 3276 NS_ENSURE_SUCCESS(rv, rv); 3277 3278 while (!iter.IsDone()) { 3279 // only collect anything if the range is not collapsed 3280 nsCOMPtr<nsINode> node = iter.GetCurrentNode(); 3281 iter.Next(); 3282 3283 nsCOMPtr<nsIContent> content = do_QueryInterface(node); 3284 if (!content) { 3285 continue; 3286 } 3287 nsIFrame* frame = content->GetPrimaryFrame(); 3288 if (!frame) { 3289 continue; 3290 } 3291 3292 if (content->IsText()) { 3293 if (node == startContainer) { 3294 int32_t offset = 3295 startContainer == endContainer 3296 ? *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets) 3297 : content->AsText()->TextDataLength(); 3298 nsLayoutUtils::GetFontFacesForText( 3299 frame, *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 3300 offset, true, aResult, fontFaces, aMaxRanges, 3301 aSkipCollapsedWhitespace); 3302 continue; 3303 } 3304 if (node == endContainer) { 3305 nsLayoutUtils::GetFontFacesForText( 3306 frame, 0, *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), 3307 true, aResult, fontFaces, aMaxRanges, aSkipCollapsedWhitespace); 3308 continue; 3309 } 3310 } 3311 3312 nsLayoutUtils::GetFontFacesForFrames(frame, aResult, fontFaces, aMaxRanges, 3313 aSkipCollapsedWhitespace); 3314 } 3315 3316 return NS_OK; 3317 } 3318 3319 nsINode* nsRange::GetRegisteredClosestCommonInclusiveAncestor() { 3320 MOZ_ASSERT(IsInAnySelection(), 3321 "GetRegisteredClosestCommonInclusiveAncestor only valid for range " 3322 "in selection"); 3323 MOZ_ASSERT(mRegisteredClosestCommonInclusiveAncestor); 3324 return mRegisteredClosestCommonInclusiveAncestor; 3325 } 3326 3327 void nsRange::SuppressContentsForPrintSelection(ErrorResult& aRv) { 3328 CutContents( 3329 nullptr, 3330 [](Element* aElement) { 3331 // Elements need to be left as-is when we're deleting nodes for 3332 // printing, to preserve the style matches containing tree-structural 3333 // pseudo-classes, such as :first-child. Partial texts are still deleted 3334 // since we don't have a good way to suppress partial texts, but that'd 3335 // preserve e.g. ::first-letter. 3336 aElement->AddStates(ElementState::SUPPRESS_FOR_PRINT_SELECTION); 3337 }, 3338 aRv); 3339 } 3340 3341 /* static */ 3342 bool nsRange::AutoInvalidateSelection::sIsNested; 3343 3344 nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() { 3345 if (!mCommonAncestor) { 3346 return; 3347 } 3348 sIsNested = false; 3349 ::InvalidateAllFrames(mCommonAncestor); 3350 3351 // Our range might not be in a selection anymore, because one of our selection 3352 // listeners might have gone ahead and run script of various sorts that messed 3353 // with selections, ranges, etc. But if it still is, we should check whether 3354 // we have a different common ancestor now, and if so invalidate its subtree 3355 // so it paints the selection it's in now. 3356 if (mRange->IsInAnySelection()) { 3357 nsINode* commonAncestor = 3358 mRange->GetRegisteredClosestCommonInclusiveAncestor(); 3359 // XXXbz can commonAncestor really be null here? I wouldn't think so! If 3360 // it _were_, then in a debug build 3361 // GetRegisteredClosestCommonInclusiveAncestor() would have fatally 3362 // asserted. 3363 if (commonAncestor && commonAncestor != mCommonAncestor) { 3364 ::InvalidateAllFrames(commonAncestor); 3365 } 3366 } 3367 } 3368 3369 /* static */ 3370 already_AddRefed<nsRange> nsRange::Constructor(const GlobalObject& aGlobal, 3371 ErrorResult& aRv) { 3372 nsCOMPtr<nsPIDOMWindowInner> window = 3373 do_QueryInterface(aGlobal.GetAsSupports()); 3374 if (!window || !window->GetDoc()) { 3375 aRv.Throw(NS_ERROR_FAILURE); 3376 return nullptr; 3377 } 3378 3379 return window->GetDoc()->CreateRange(aRv); 3380 } 3381 3382 static bool ExcludeIfNextToNonSelectable(nsIContent* aContent) { 3383 return aContent->IsText() && 3384 aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE); 3385 } 3386 3387 void nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges) { 3388 if (!mIsPositioned) { 3389 MOZ_ASSERT(false); 3390 return; 3391 } 3392 MOZ_ASSERT(mEnd.GetContainer()); 3393 MOZ_ASSERT(mStart.GetContainer()); 3394 3395 nsRange* range = this; 3396 RefPtr<nsRange> newRange; 3397 while (range) { 3398 PreContentIterator preOrderIter; 3399 nsresult rv = preOrderIter.Init(range); 3400 if (NS_FAILED(rv)) { 3401 return; 3402 } 3403 3404 bool added = false; 3405 bool seenSelectable = false; 3406 // |firstNonSelectableContent| is the first node in a consecutive sequence 3407 // of non-IsSelectable nodes. When we find a selectable node after such 3408 // a sequence we'll end the last nsRange, create a new one and restart 3409 // the outer loop. 3410 nsIContent* firstNonSelectableContent = nullptr; 3411 while (true) { 3412 nsINode* node = preOrderIter.GetCurrentNode(); 3413 preOrderIter.Next(); 3414 bool selectable = true; 3415 nsIContent* content = nsIContent::FromNodeOrNull(node); 3416 if (content) { 3417 if (firstNonSelectableContent && 3418 ExcludeIfNextToNonSelectable(content)) { 3419 // Ignorable whitespace next to a sequence of non-selectable nodes 3420 // counts as non-selectable (bug 1216001). 3421 selectable = false; 3422 } 3423 if (selectable) { 3424 // FIXME: Use content->IsSelectable() 3425 nsIFrame* frame = content->GetPrimaryFrame(); 3426 for (nsIContent* p = content; !frame && (p = p->GetParent());) { 3427 frame = p->GetPrimaryFrame(); 3428 } 3429 if (frame) { 3430 selectable = frame->IsSelectable(); 3431 } 3432 } 3433 } 3434 3435 if (!selectable) { 3436 if (!firstNonSelectableContent) { 3437 firstNonSelectableContent = content; 3438 } 3439 if (preOrderIter.IsDone()) { 3440 if (seenSelectable) { 3441 // The tail end of the initial range is non-selectable - truncate 3442 // the current range before the first non-selectable node. 3443 range->SetEndBefore(*firstNonSelectableContent, IgnoreErrors()); 3444 } 3445 return; 3446 } 3447 continue; 3448 } 3449 3450 if (firstNonSelectableContent) { 3451 if (range == this && !seenSelectable) { 3452 // This is the initial range and all its nodes until now are 3453 // non-selectable so just trim them from the start. 3454 IgnoredErrorResult err; 3455 range->SetStartBefore(*node, err, AllowRangeCrossShadowBoundary::Yes); 3456 if (err.Failed()) { 3457 return; 3458 } 3459 break; // restart the same range with a new iterator 3460 } 3461 3462 // Save the end point before truncating the range. 3463 nsINode* endContainer = range->mEnd.GetContainer(); 3464 const uint32_t endOffset = 3465 *range->mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 3466 3467 // Truncate the current range before the first non-selectable node. 3468 IgnoredErrorResult err; 3469 range->SetEndBefore(*firstNonSelectableContent, err, 3470 AllowRangeCrossShadowBoundary::Yes); 3471 3472 // Store it in the result (strong ref) - do this before creating 3473 // a new range in |newRange| below so we don't drop the last ref 3474 // to the range created in the previous iteration. 3475 if (!added && !err.Failed()) { 3476 aOutRanges->AppendElement(range); 3477 } 3478 3479 // Create a new range for the remainder. 3480 nsINode* startContainer = node; 3481 Maybe<uint32_t> startOffset = Some(0); 3482 // Don't start *inside* a node with independent selection though 3483 // (e.g. <input>). 3484 if (content && content->HasIndependentSelection()) { 3485 nsINode* parent = startContainer->GetParent(); 3486 if (parent) { 3487 startOffset = parent->ComputeIndexOf(startContainer); 3488 startContainer = parent; 3489 } 3490 } 3491 newRange = 3492 nsRange::Create(startContainer, startOffset.valueOr(UINT32_MAX), 3493 endContainer, endOffset, IgnoreErrors()); 3494 if (!newRange || newRange->Collapsed()) { 3495 newRange = nullptr; 3496 } 3497 range = newRange; 3498 break; // create a new iterator for the new range, if any 3499 } 3500 3501 seenSelectable = true; 3502 if (!added) { 3503 added = true; 3504 aOutRanges->AppendElement(range); 3505 } 3506 if (preOrderIter.IsDone()) { 3507 return; 3508 } 3509 } 3510 } 3511 } 3512 3513 struct InnerTextAccumulator { 3514 explicit InnerTextAccumulator(mozilla::dom::DOMString& aValue) 3515 : mString(aValue.AsAString()), mRequiredLineBreakCount(0) {} 3516 void FlushLineBreaks() { 3517 while (mRequiredLineBreakCount > 0) { 3518 // Required line breaks at the start of the text are suppressed. 3519 if (!mString.IsEmpty()) { 3520 mString.Append('\n'); 3521 } 3522 --mRequiredLineBreakCount; 3523 } 3524 } 3525 void Append(char aCh) { Append(nsAutoString(aCh)); } 3526 void Append(const nsAString& aString) { 3527 if (aString.IsEmpty()) { 3528 return; 3529 } 3530 FlushLineBreaks(); 3531 mString.Append(aString); 3532 } 3533 void AddRequiredLineBreakCount(int8_t aCount) { 3534 mRequiredLineBreakCount = std::max(mRequiredLineBreakCount, aCount); 3535 } 3536 3537 nsAString& mString; 3538 int8_t mRequiredLineBreakCount; 3539 }; 3540 3541 static bool IsVisibleAndNotInReplacedElement(nsIFrame* aFrame) { 3542 if (!aFrame || !aFrame->StyleVisibility()->IsVisible() || 3543 aFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) { 3544 return false; 3545 } 3546 if (aFrame->HidesContent()) { 3547 return false; 3548 } 3549 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) { 3550 if (f->HidesContent()) { 3551 return false; 3552 } 3553 if (f->IsReplaced() && 3554 !f->GetContent()->IsAnyOfHTMLElements(nsGkAtoms::button, 3555 nsGkAtoms::select) && 3556 !f->GetContent()->IsSVGElement()) { 3557 return false; 3558 } 3559 } 3560 return true; 3561 } 3562 3563 static void AppendTransformedText(InnerTextAccumulator& aResult, 3564 nsIContent* aContainer) { 3565 auto textNode = static_cast<CharacterData*>(aContainer); 3566 3567 nsIFrame* frame = textNode->GetPrimaryFrame(); 3568 if (!IsVisibleAndNotInReplacedElement(frame)) { 3569 return; 3570 } 3571 3572 nsIFrame::RenderedText text = 3573 frame->GetRenderedText(0, aContainer->GetChildCount()); 3574 aResult.Append(text.mString); 3575 } 3576 3577 /** 3578 * States for tree traversal. AT_NODE means that we are about to enter 3579 * the current DOM node. AFTER_NODE means that we have just finished traversing 3580 * the children of the current DOM node and are about to apply any 3581 * "after processing the node's children" steps before we finish visiting 3582 * the node. 3583 */ 3584 enum TreeTraversalState { AT_NODE, AFTER_NODE }; 3585 3586 static int8_t GetRequiredInnerTextLineBreakCount(nsIFrame* aFrame) { 3587 if (aFrame->GetContent()->IsHTMLElement(nsGkAtoms::p)) { 3588 return 2; 3589 } 3590 const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay(); 3591 if (styleDisplay->IsBlockOutside(aFrame) || 3592 styleDisplay->mDisplay == StyleDisplay::TableCaption) { 3593 return 1; 3594 } 3595 return 0; 3596 } 3597 3598 static bool IsLastCellOfRow(nsIFrame* aFrame) { 3599 LayoutFrameType type = aFrame->Type(); 3600 if (type != LayoutFrameType::TableCell) { 3601 return true; 3602 } 3603 for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) { 3604 if (c->GetNextSibling()) { 3605 return false; 3606 } 3607 } 3608 return true; 3609 } 3610 3611 static bool IsLastRowOfRowGroup(nsIFrame* aFrame) { 3612 if (!aFrame->IsTableRowFrame()) { 3613 return true; 3614 } 3615 for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) { 3616 if (c->GetNextSibling()) { 3617 return false; 3618 } 3619 } 3620 return true; 3621 } 3622 3623 static bool IsLastNonemptyRowGroupOfTable(nsIFrame* aFrame) { 3624 if (!aFrame->IsTableRowGroupFrame()) { 3625 return true; 3626 } 3627 for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) { 3628 for (nsIFrame* next = c->GetNextSibling(); next; 3629 next = next->GetNextSibling()) { 3630 if (next->PrincipalChildList().FirstChild()) { 3631 return false; 3632 } 3633 } 3634 } 3635 return true; 3636 } 3637 3638 void nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError, 3639 nsIContent* aContainer) { 3640 InnerTextAccumulator result(aValue); 3641 3642 if (aContainer->IsText()) { 3643 AppendTransformedText(result, aContainer); 3644 return; 3645 } 3646 3647 nsIContent* currentNode = aContainer; 3648 TreeTraversalState currentState = AFTER_NODE; 3649 3650 nsIContent* endNode = aContainer; 3651 TreeTraversalState endState = AFTER_NODE; 3652 3653 nsIContent* firstChild = aContainer->GetFirstChild(); 3654 if (firstChild) { 3655 currentNode = firstChild; 3656 currentState = AT_NODE; 3657 } 3658 3659 while (currentNode != endNode || currentState != endState) { 3660 nsIFrame* f = currentNode->GetPrimaryFrame(); 3661 bool isVisibleAndNotReplaced = IsVisibleAndNotInReplacedElement(f); 3662 if (currentState == AT_NODE) { 3663 bool isText = currentNode->IsText(); 3664 if (isVisibleAndNotReplaced) { 3665 result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f)); 3666 if (isText) { 3667 nsIFrame::RenderedText text = f->GetRenderedText(); 3668 result.Append(text.mString); 3669 } 3670 } 3671 nsIContent* child = currentNode->GetFirstChild(); 3672 if (child) { 3673 currentNode = child; 3674 continue; 3675 } 3676 currentState = AFTER_NODE; 3677 } 3678 if (currentNode == endNode && currentState == endState) { 3679 break; 3680 } 3681 if (isVisibleAndNotReplaced) { 3682 if (currentNode->IsHTMLElement(nsGkAtoms::br)) { 3683 result.Append('\n'); 3684 } 3685 switch (f->StyleDisplay()->DisplayInside()) { 3686 case StyleDisplayInside::TableCell: 3687 if (!IsLastCellOfRow(f)) { 3688 result.Append('\t'); 3689 } 3690 break; 3691 case StyleDisplayInside::TableRow: 3692 if (!IsLastRowOfRowGroup(f) || 3693 !IsLastNonemptyRowGroupOfTable(f->GetParent())) { 3694 result.Append('\n'); 3695 } 3696 break; 3697 default: 3698 break; // Do nothing 3699 } 3700 result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f)); 3701 } 3702 nsIContent* next = currentNode->GetNextSibling(); 3703 if (next) { 3704 currentNode = next; 3705 currentState = AT_NODE; 3706 } else { 3707 currentNode = currentNode->GetParent(); 3708 } 3709 } 3710 3711 // Do not flush trailing line breaks! Required breaks at the end of the text 3712 // are suppressed. 3713 } 3714 3715 template <typename SPT, typename SRT, typename EPT, typename ERT> 3716 void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 3717 const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary, 3718 const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary) { 3719 if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { 3720 return; 3721 } 3722 3723 MOZ_ASSERT(aStartBoundary.IsSetAndValid() && aEndBoundary.IsSetAndValid()); 3724 MOZ_ASSERT(aStartBoundary.GetTreeKind() == aEndBoundary.GetTreeKind()); 3725 MOZ_ASSERT(aStartBoundary.GetTreeKind() == TreeKind::Flat); 3726 3727 nsINode* startNode = aStartBoundary.GetContainer(); 3728 nsINode* endNode = aEndBoundary.GetContainer(); 3729 3730 if (!startNode && !endNode) { 3731 ResetCrossShadowBoundaryRange(); 3732 return; 3733 } 3734 3735 // Nodes at least needs to be in the same document. 3736 if (startNode && endNode && 3737 startNode->GetComposedDoc() != endNode->GetComposedDoc()) { 3738 ResetCrossShadowBoundaryRange(); 3739 return; 3740 } 3741 3742 auto CanBecomeCrossShadowBoundaryPoint = [](nsINode* aContainer) -> bool { 3743 if (!aContainer) { 3744 return true; 3745 } 3746 3747 // Unlike normal ranges, shadow cross ranges don't work 3748 // when the nodes aren't in document. 3749 if (!aContainer->IsInComposedDoc()) { 3750 return false; 3751 } 3752 3753 // We don't allow ranges to span different NAC subtrees (because we don't 3754 // notify when unbinding NAC roots historically). nsRange can already deal 3755 // with the "same anonymous subtree" case. 3756 if (aContainer->IsInNativeAnonymousSubtree()) { 3757 return false; 3758 } 3759 3760 // AbstractRange::GetClosestCommonInclusiveAncestor only supports 3761 // Document and Content nodes. 3762 return aContainer->IsDocument() || aContainer->IsContent(); 3763 }; 3764 3765 if (!CanBecomeCrossShadowBoundaryPoint(startNode) || 3766 !CanBecomeCrossShadowBoundaryPoint(endNode)) { 3767 ResetCrossShadowBoundaryRange(); 3768 return; 3769 } 3770 3771 if (!mCrossShadowBoundaryRange) { 3772 mCrossShadowBoundaryRange = 3773 CrossShadowBoundaryRange::Create(aStartBoundary, aEndBoundary, this); 3774 return; 3775 } 3776 3777 mCrossShadowBoundaryRange->SetStartAndEnd(aStartBoundary, aEndBoundary); 3778 } 3779 3780 RawRangeBoundary nsRange::ComputeNewBoundaryWhenBoundaryInsideChangedText( 3781 const CharacterDataChangeInfo& aInfo, const RawRangeBoundary& aBoundary) { 3782 MOZ_ASSERT(aInfo.mChangeStart < 3783 *aBoundary.Offset( 3784 RawRangeBoundary::OffsetFilter::kValidOrInvalidOffsets)); 3785 // If boundary is inside changed text, position it before change 3786 // else adjust start offset for the change in length. 3787 CheckedUint32 newOffset{0}; 3788 if (*aBoundary.Offset( 3789 RawRangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <= 3790 aInfo.mChangeEnd) { 3791 newOffset = aInfo.mChangeStart; 3792 } else { 3793 newOffset = *aBoundary.Offset( 3794 RawRangeBoundary::OffsetFilter::kValidOrInvalidOffsets); 3795 newOffset -= aInfo.LengthOfRemovedText(); 3796 newOffset += aInfo.mReplaceLength; 3797 } 3798 3799 // newOffset.isValid() isn't checked explicitly here, because 3800 // newOffset.value() contains an assertion. 3801 return {aBoundary.GetContainer(), newOffset.value()}; 3802 }