nsRange.h (26757B)
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 #ifndef nsRange_h___ 12 #define nsRange_h___ 13 14 #include "mozilla/Attributes.h" 15 #include "mozilla/ErrorResult.h" 16 #include "mozilla/RangeBoundary.h" 17 #include "mozilla/RefPtr.h" 18 #include "mozilla/dom/AbstractRange.h" 19 #include "mozilla/dom/CrossShadowBoundaryRange.h" 20 #include "mozilla/dom/StaticRange.h" 21 #include "nsCOMPtr.h" 22 #include "nsStubMutationObserver.h" 23 #include "nsWrapperCache.h" 24 #include "prmon.h" 25 26 class nsIPrincipal; 27 28 namespace mozilla { 29 class RectCallback; 30 namespace dom { 31 struct ClientRectsAndTexts; 32 class DocGroup; 33 class DocumentFragment; 34 class DOMRect; 35 class DOMRectList; 36 class InspectorFontFace; 37 class Selection; 38 class TrustedHTMLOrString; 39 40 enum class RangeBehaviour : uint8_t { 41 // Keep both ranges 42 KeepDefaultRangeAndCrossShadowBoundaryRanges, 43 // Collapse the default range 44 CollapseDefaultRange, 45 // Collapse both the default range and the cross-shadow-boundary range 46 CollapseDefaultRangeAndCrossShadowBoundaryRanges 47 48 }; 49 } // namespace dom 50 } // namespace mozilla 51 52 class nsRange final : public mozilla::dom::AbstractRange, 53 public nsStubMutationObserver { 54 using ErrorResult = mozilla::ErrorResult; 55 using AbstractRange = mozilla::dom::AbstractRange; 56 using DocGroup = mozilla::dom::DocGroup; 57 using DOMRect = mozilla::dom::DOMRect; 58 using DOMRectList = mozilla::dom::DOMRectList; 59 using RangeBoundary = mozilla::RangeBoundary; 60 using RawRangeBoundary = mozilla::RawRangeBoundary; 61 using AllowRangeCrossShadowBoundary = 62 mozilla::dom::AllowRangeCrossShadowBoundary; 63 64 virtual ~nsRange(); 65 explicit nsRange(nsINode* aNode); 66 67 public: 68 /** 69 * The following Create() returns `nsRange` instance which is initialized 70 * only with aNode. The result is never positioned. 71 */ 72 static already_AddRefed<nsRange> Create(nsINode* aNode); 73 74 /** 75 * The following Create() may return `nsRange` instance which is initialized 76 * with given range or points. If it fails initializing new range with the 77 * arguments, returns `nullptr`. `ErrorResult` is set to an error only 78 * when this returns `nullptr`. The error code indicates the reason why 79 * it couldn't initialize the instance. 80 */ 81 static already_AddRefed<nsRange> Create(const AbstractRange* aAbstractRange, 82 ErrorResult& aRv) { 83 return nsRange::Create(aAbstractRange->StartRef(), aAbstractRange->EndRef(), 84 aRv); 85 } 86 static already_AddRefed<nsRange> Create( 87 nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer, 88 uint32_t aEndOffset, ErrorResult& aRv, 89 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 90 AllowRangeCrossShadowBoundary::No) { 91 return nsRange::Create(RawRangeBoundary(aStartContainer, aStartOffset), 92 RawRangeBoundary(aEndContainer, aEndOffset), aRv, 93 aAllowCrossShadowBoundary); 94 } 95 template <typename SPT, typename SRT, typename EPT, typename ERT> 96 static already_AddRefed<nsRange> Create( 97 const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary, 98 const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary, 99 ErrorResult& aRv, 100 AllowRangeCrossShadowBoundary = AllowRangeCrossShadowBoundary::No); 101 102 NS_DECL_ISUPPORTS_INHERITED 103 NS_IMETHODIMP_(void) DeleteCycleCollectable(void) override; 104 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsRange, AbstractRange) 105 106 nsrefcnt GetRefCount() const { return mRefCnt; } 107 108 nsINode* GetRoot() const { return mRoot; } 109 110 /** 111 * Return true if this range was generated. 112 * @see SetIsGenerated 113 */ 114 bool IsGenerated() const { return mIsGenerated; } 115 116 /** 117 * Mark this range as being generated or not. 118 * Currently it is used for marking ranges that are created when splitting up 119 * a range to exclude a -moz-user-select:none region. 120 * @see Selection::AddRangesForSelectableNodes 121 * @see ExcludeNonSelectableNodes 122 */ 123 void SetIsGenerated(bool aIsGenerated) { mIsGenerated = aIsGenerated; } 124 125 void Reset(); 126 127 /** 128 * SetStart() and SetEnd() sets start point or end point separately. 129 * However, this is expensive especially when it's a range of Selection. 130 * When you set both start and end of a range, you should use 131 * SetStartAndEnd() instead. 132 */ 133 nsresult SetStart(nsINode* aContainer, uint32_t aOffset, 134 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 135 AllowRangeCrossShadowBoundary::No) { 136 ErrorResult error; 137 SetStart(RawRangeBoundary(aContainer, aOffset), error, 138 aAllowCrossShadowBoundary); 139 return error.StealNSResult(); 140 } 141 nsresult SetEnd(nsINode* aContainer, uint32_t aOffset, 142 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 143 AllowRangeCrossShadowBoundary::No) { 144 ErrorResult error; 145 SetEnd(RawRangeBoundary(aContainer, aOffset), error, 146 aAllowCrossShadowBoundary); 147 return error.StealNSResult(); 148 } 149 150 already_AddRefed<nsRange> CloneRange() const; 151 152 /** 153 * SetStartAndEnd() works similar to call both SetStart() and SetEnd(). 154 * Different from calls them separately, this does nothing if either 155 * the start point or the end point is invalid point. 156 * If the specified start point is after the end point, the range will be 157 * collapsed at the end point. Similarly, if they are in different root, 158 * the range will be collapsed at the end point. 159 */ 160 nsresult SetStartAndEnd( 161 nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer, 162 uint32_t aEndOffset, 163 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 164 AllowRangeCrossShadowBoundary::No) { 165 return SetStartAndEnd(RawRangeBoundary(aStartContainer, aStartOffset), 166 RawRangeBoundary(aEndContainer, aEndOffset), 167 aAllowCrossShadowBoundary); 168 } 169 template <typename SPT, typename SRT, typename EPT, typename ERT> 170 nsresult SetStartAndEnd( 171 const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary, 172 const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary, 173 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 174 AllowRangeCrossShadowBoundary::No) { 175 return AbstractRange::SetStartAndEndInternal( 176 aStartBoundary, aEndBoundary, this, aAllowCrossShadowBoundary); 177 } 178 179 /** 180 * Adds all nodes between |aStartContent| and |aEndContent| to the range. 181 * The start offset will be set before |aStartContent|, 182 * while the end offset will be set immediately after |aEndContent|. 183 * 184 * Caller must guarantee both nodes are non null and 185 * children of |aContainer| and that |aEndContent| is after |aStartContent|. 186 */ 187 void SelectNodesInContainer(nsINode* aContainer, nsIContent* aStartContent, 188 nsIContent* aEndContent); 189 190 /** 191 * CollapseTo() works similar to call both SetStart() and SetEnd() with 192 * same node and offset. This just calls SetStartAndParent() to set 193 * collapsed range at aContainer and aOffset. 194 */ 195 nsresult CollapseTo(nsINode* aContainer, uint32_t aOffset) { 196 return CollapseTo(RawRangeBoundary(aContainer, aOffset)); 197 } 198 nsresult CollapseTo(const RawRangeBoundary& aPoint) { 199 return SetStartAndEnd(aPoint, aPoint); 200 } 201 202 // aMaxRanges is the maximum number of text ranges to record for each face 203 // (pass 0 to just get the list of faces, without recording exact ranges 204 // where each face was used). 205 nsresult GetUsedFontFaces( 206 nsTArray<mozilla::UniquePtr<mozilla::dom::InspectorFontFace>>& aResult, 207 uint32_t aMaxRanges, bool aSkipCollapsedWhitespace); 208 209 // nsIMutationObserver methods 210 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED 211 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED 212 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED 213 NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED 214 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED 215 216 // WebIDL 217 static already_AddRefed<nsRange> Constructor( 218 const mozilla::dom::GlobalObject& global, mozilla::ErrorResult& aRv); 219 220 already_AddRefed<mozilla::dom::DocumentFragment> CreateContextualFragment( 221 const nsAString& aString, ErrorResult& aError) const; 222 MOZ_CAN_RUN_SCRIPT already_AddRefed<mozilla::dom::DocumentFragment> 223 CreateContextualFragment(const mozilla::dom::TrustedHTMLOrString&, 224 nsIPrincipal* aSubjectPrincipal, 225 ErrorResult& aError) const; 226 already_AddRefed<mozilla::dom::DocumentFragment> CloneContents( 227 ErrorResult& aErr); 228 int16_t CompareBoundaryPoints(uint16_t aHow, const nsRange& aOtherRange, 229 ErrorResult& aRv); 230 int16_t ComparePoint(const nsINode& aContainer, uint32_t aOffset, 231 ErrorResult& aRv, 232 bool aAllowCrossShadowBoundary = false) const; 233 void DeleteContents(ErrorResult& aRv); 234 already_AddRefed<mozilla::dom::DocumentFragment> ExtractContents( 235 ErrorResult& aErr); 236 nsINode* GetCommonAncestorContainer( 237 ErrorResult& aRv, 238 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 239 AllowRangeCrossShadowBoundary::No) const { 240 if (!mIsPositioned) { 241 aRv.Throw(NS_ERROR_NOT_INITIALIZED); 242 return nullptr; 243 } 244 return GetClosestCommonInclusiveAncestor(aAllowCrossShadowBoundary); 245 } 246 void InsertNode(nsINode& aNode, ErrorResult& aErr); 247 bool IntersectsNode(nsINode& aNode, ErrorResult& aRv); 248 bool IsPointInRange(const nsINode& aContainer, uint32_t aOffset, 249 ErrorResult& aRv, 250 bool aAllowCrossShadowBoundary = false) const; 251 void ToString(nsAString& aReturn, ErrorResult& aErr); 252 void Detach(); 253 254 // *JS() methods are mapped to Range.*() of DOM. 255 // They may move focus only when the range represents normal selection. 256 // These methods shouldn't be used from internal. 257 void CollapseJS(bool aToStart); 258 void SelectNodeJS(nsINode& aNode, ErrorResult& aErr); 259 void SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr); 260 void SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr); 261 void SetEndAfterJS(nsINode& aNode, ErrorResult& aErr); 262 void SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr); 263 void SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr); 264 void SetStartAfterJS(nsINode& aNode, ErrorResult& aErr); 265 void SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr); 266 267 void SetStartAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset, 268 ErrorResult& aErr); 269 void SetEndAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset, 270 ErrorResult& aErr); 271 272 void SurroundContents(nsINode& aNode, ErrorResult& aErr); 273 already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true, 274 bool aFlushLayout = true); 275 already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true, 276 bool aFlushLayout = true); 277 // ChromeOnly 278 already_AddRefed<DOMRectList> GetAllowCrossShadowBoundaryClientRects( 279 bool aClampToEdge = true, bool aFlushLayout = true); 280 281 void GetClientRectsAndTexts(mozilla::dom::ClientRectsAndTexts& aResult, 282 ErrorResult& aErr); 283 284 // Following methods should be used for internal use instead of *JS(). 285 void SelectNode(nsINode& aNode, ErrorResult& aErr); 286 void SelectNodeContents(nsINode& aNode, ErrorResult& aErr); 287 void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr, 288 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 289 AllowRangeCrossShadowBoundary::No); 290 void SetEnd(const RawRangeBoundary& aPoint, ErrorResult& aErr, 291 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 292 AllowRangeCrossShadowBoundary::No); 293 void SetEndAfter(nsINode& aNode, ErrorResult& aErr); 294 void SetEndBefore(nsINode& aNode, ErrorResult& aErr, 295 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 296 AllowRangeCrossShadowBoundary::No); 297 void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr, 298 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 299 AllowRangeCrossShadowBoundary::No); 300 void SetStart(const RawRangeBoundary& aPoint, ErrorResult& aErr, 301 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 302 AllowRangeCrossShadowBoundary::No); 303 void SetStartAfter(nsINode& aNode, ErrorResult& aErr); 304 void SetStartBefore(nsINode& aNode, ErrorResult& aErr, 305 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 306 AllowRangeCrossShadowBoundary::No); 307 void Collapse(bool aToStart); 308 309 static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue, 310 mozilla::ErrorResult& aError, 311 nsIContent* aContainer); 312 313 virtual JSObject* WrapObject(JSContext* cx, 314 JS::Handle<JSObject*> aGivenProto) final; 315 DocGroup* GetDocGroup() const; 316 317 // Given a CharacterDataChangeInfo and an RangeBoundary of where the 318 // character changes occurred at, compute the new boundary. 319 static RawRangeBoundary ComputeNewBoundaryWhenBoundaryInsideChangedText( 320 const CharacterDataChangeInfo& aInfo, const RawRangeBoundary& aBoundary); 321 322 private: 323 // no copy's or assigns 324 nsRange(const nsRange&); 325 nsRange& operator=(const nsRange&); 326 327 template <typename SPT, typename SRT, typename EPT, typename ERT> 328 static void AssertIfMismatchRootAndRangeBoundaries( 329 const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary, 330 const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary, 331 const nsINode* aRootNode, bool aNotInsertedYet = false); 332 333 using ElementHandler = void (*)(mozilla::dom::Element*); 334 /** 335 * Cut or delete the range's contents. 336 * 337 * @param aFragment DocumentFragment containing the nodes. 338 * May be null to indicate the caller doesn't want a 339 * fragment. 340 * @param aElementHandler If this handler is provided, any element node 341 * subtree root fully contained in this range is 342 * passed to it, instead of being deleted. Any 343 * mutation that trips nsMutationGuard is disallowed. 344 * Currently incompatible with non-null aFragment. 345 * @param aRv The error if any. 346 */ 347 void CutContents(mozilla::dom::DocumentFragment** aFragment, 348 ElementHandler aElementHandler, ErrorResult& aRv); 349 350 static nsresult CloneParentsBetween(nsINode* aAncestor, nsINode* aNode, 351 nsINode** aClosestAncestor, 352 nsINode** aFarthestAncestor); 353 354 /** 355 * Returns whether a node is safe to be accessed by the current caller. 356 */ 357 bool CanAccess(const nsINode&) const; 358 359 void AdjustNextRefsOnCharacterDataSplit(const nsIContent& aContent, 360 const CharacterDataChangeInfo& aInfo); 361 362 struct RangeBoundariesAndRoot { 363 RawRangeBoundary mStart; 364 RawRangeBoundary mEnd; 365 nsINode* mRoot = nullptr; 366 }; 367 368 /** 369 * @param aContent Must be non-nullptr. 370 */ 371 RangeBoundariesAndRoot DetermineNewRangeBoundariesAndRootOnCharacterDataMerge( 372 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) const; 373 374 // @return true iff the range is positioned, aContainer belongs to the same 375 // document as the range, aContainer is a DOCUMENT_TYPE_NODE and 376 // aOffset doesn't exceed aContainer's length. 377 bool IsPointComparableToRange(const nsINode& aContainer, uint32_t aOffset, 378 bool aAllowCrossShadowBoundary, 379 ErrorResult& aErrorResult) const; 380 381 // @return true iff aContainer is a shadow including inclusive descendant of 382 // the common ancestor of the mCrossBoundaryRange. 383 bool IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor( 384 const nsINode& aContainer) const; 385 386 /** 387 * @brief Returns true if the range is part of exactly one |Selection|. 388 */ 389 bool IsPartOfOneSelectionOnly() const { return mSelections.Length() == 1; }; 390 391 already_AddRefed<DOMRectList> GetClientRectsInner( 392 AllowRangeCrossShadowBoundary = AllowRangeCrossShadowBoundary::No, 393 bool aClampToEdge = true, bool aFlushLayout = true); 394 395 public: 396 /** 397 * This helper function gets rects and correlated text for the given range. 398 * @param aTextList optional where nullptr = don't retrieve text 399 */ 400 static void CollectClientRectsAndText( 401 mozilla::RectCallback* aCollector, 402 mozilla::dom::Sequence<nsString>* aTextList, nsRange* aRange, 403 nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer, 404 uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout); 405 406 /** 407 * Scan this range for -moz-user-select:none nodes and split it up into 408 * multiple ranges to exclude those nodes. The resulting ranges are put 409 * in aOutRanges. If no -moz-user-select:none node is found in the range 410 * then |this| is unmodified and is the only range in aOutRanges. 411 * Otherwise, |this| will be modified so that it ends before the first 412 * -moz-user-select:none node and additional ranges may also be created. 413 * If all nodes in the range are -moz-user-select:none then aOutRanges 414 * will be empty. 415 * @param aOutRanges the resulting set of ranges 416 */ 417 void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges); 418 419 /** 420 * Notify the selection listeners after a range has been modified. 421 */ 422 MOZ_CAN_RUN_SCRIPT void NotifySelectionListenersAfterRangeSet(); 423 424 /** 425 * For a range for which IsInSelection() is true, return the closest common 426 * inclusive ancestor 427 * (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) 428 * for the range, which we had to compute when the common ancestor changed or 429 * IsInSelection became true, so we could register with it. That is, it's a 430 * faster version of GetClosestCommonInclusiveAncestor that only works for 431 * ranges in a Selection. The method will assert and the behavior is undefined 432 * if called on a range where IsInSelection() is false. 433 */ 434 nsINode* GetRegisteredClosestCommonInclusiveAncestor(); 435 436 template <typename SPT, typename SRT, typename EPT, typename ERT> 437 void CreateOrUpdateCrossShadowBoundaryRangeIfNeeded( 438 const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary, 439 const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary); 440 441 void ResetCrossShadowBoundaryRange() { mCrossShadowBoundaryRange = nullptr; } 442 443 bool CrossShadowBoundaryRangeCollapsed() const { 444 MOZ_ASSERT(mCrossShadowBoundaryRange); 445 446 return !mCrossShadowBoundaryRange->IsPositioned() || 447 (mCrossShadowBoundaryRange->GetStartContainer() == 448 mCrossShadowBoundaryRange->GetEndContainer() && 449 mCrossShadowBoundaryRange->StartOffset() == 450 mCrossShadowBoundaryRange->EndOffset()); 451 } 452 453 /* 454 * The methods marked with MayCrossShadowBoundary[..] additionally check for 455 * the existence of mCrossShadowBoundaryRange, which indicates a range that 456 * crosses a shadow DOM boundary (i.e. mStart and mEnd are in different 457 * trees). If the caller can guarantee that this does not happen, there are 458 * additional variants of these methods named without MayCrossShadowBoundary, 459 * which provide a slightly faster implementation. 460 * */ 461 462 nsIContent* GetMayCrossShadowBoundaryChildAtStartOffset() const { 463 return mCrossShadowBoundaryRange 464 ? mCrossShadowBoundaryRange->GetChildAtStartOffset() 465 : mStart.GetChildAtOffset(); 466 } 467 468 nsIContent* GetMayCrossShadowBoundaryChildAtEndOffset() const { 469 return mCrossShadowBoundaryRange 470 ? mCrossShadowBoundaryRange->GetChildAtEndOffset() 471 : mEnd.GetChildAtOffset(); 472 } 473 474 mozilla::dom::CrossShadowBoundaryRange* GetCrossShadowBoundaryRange() const { 475 return mCrossShadowBoundaryRange; 476 } 477 478 nsINode* GetMayCrossShadowBoundaryStartContainer() const { 479 return mCrossShadowBoundaryRange 480 ? mCrossShadowBoundaryRange->GetStartContainer() 481 : mStart.GetContainer(); 482 } 483 484 nsINode* GetMayCrossShadowBoundaryEndContainer() const { 485 return mCrossShadowBoundaryRange 486 ? mCrossShadowBoundaryRange->GetEndContainer() 487 : mEnd.GetContainer(); 488 } 489 490 uint32_t MayCrossShadowBoundaryStartOffset() const { 491 return mCrossShadowBoundaryRange ? mCrossShadowBoundaryRange->StartOffset() 492 : StartOffset(); 493 } 494 495 uint32_t MayCrossShadowBoundaryEndOffset() const { 496 return mCrossShadowBoundaryRange ? mCrossShadowBoundaryRange->EndOffset() 497 : EndOffset(); 498 } 499 500 const RangeBoundary& MayCrossShadowBoundaryStartRef() const { 501 return mCrossShadowBoundaryRange ? mCrossShadowBoundaryRange->StartRef() 502 : StartRef(); 503 } 504 505 const RangeBoundary& MayCrossShadowBoundaryEndRef() const { 506 return mCrossShadowBoundaryRange ? mCrossShadowBoundaryRange->EndRef() 507 : EndRef(); 508 } 509 510 /** 511 * Suppress rendering of selected nodes for print selection, assuming that 512 * this range represents a part of nodes that are outside of the selection. 513 * For all non-Element nodes, this behaves identically to DeleteContents(). 514 * Elements that are wholly contained by this range is instead marked with 515 * state that matches :-moz-suppress-for-print-selection. 516 * This is required to preserve matched style rules using tree-structural 517 * pseudo-classes, as well as any feature that depends on the location of 518 * the style node in the DOM tree (e.g. @scope with implicit scope). 519 * 520 * @param aRv The error, if any. 521 */ 522 void SuppressContentsForPrintSelection(ErrorResult& aRv); 523 524 protected: 525 /** 526 * DoSetRange() is called when `AbstractRange::SetStartAndEndInternal()` sets 527 * mStart and mEnd, or some other internal methods modify `mStart` and/or 528 * `mEnd`. Therefore, this shouldn't be a virtual method. 529 * 530 * @param aStartBoundary Computed start point. This must equals or be 531 * before aEndBoundary in the DOM tree order. 532 * @param aEndBoundary Computed end point. 533 * @param aRootNode The root node. 534 * @param aNotInsertedYet true if this is called by CharacterDataChanged() 535 * to disable assertion and suppress re-registering 536 * a range common ancestor node since the new text 537 * node of a splitText hasn't been inserted yet. 538 * CharacterDataChanged() does the re-registering 539 * when needed. Otherwise, false. 540 */ 541 template <typename SPT, typename SRT, typename EPT, typename ERT> 542 MOZ_CAN_RUN_SCRIPT_BOUNDARY void DoSetRange( 543 const mozilla::RangeBoundaryBase<SPT, SRT>& aStartBoundary, 544 const mozilla::RangeBoundaryBase<EPT, ERT>& aEndBoundary, 545 nsINode* aRootNode, bool aNotInsertedYet = false, 546 mozilla::dom::RangeBehaviour aRangeBehaviour = mozilla::dom:: 547 RangeBehaviour::CollapseDefaultRangeAndCrossShadowBoundaryRanges); 548 549 // Assume that this is guaranteed that this is held by the caller when 550 // this is used. (Note that we cannot use AutoRestore for mCalledByJS 551 // due to a bit field.) 552 class MOZ_RAII AutoCalledByJSRestore final { 553 private: 554 nsRange& mRange; 555 bool mOldValue; 556 557 public: 558 explicit AutoCalledByJSRestore(nsRange& aRange) 559 : mRange(aRange), mOldValue(aRange.mCalledByJS) {} 560 ~AutoCalledByJSRestore() { mRange.mCalledByJS = mOldValue; } 561 bool SavedValue() const { return mOldValue; } 562 }; 563 564 struct MOZ_STACK_CLASS AutoInvalidateSelection { 565 explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) { 566 if (!mRange->IsInAnySelection() || sIsNested) { 567 return; 568 } 569 sIsNested = true; 570 mCommonAncestor = mRange->GetRegisteredClosestCommonInclusiveAncestor(); 571 } 572 ~AutoInvalidateSelection(); 573 nsRange* mRange; 574 RefPtr<nsINode> mCommonAncestor; 575 static bool sIsNested; 576 }; 577 578 bool MaybeInterruptLastRelease(); 579 580 #ifdef DEBUG 581 bool IsCleared() const { 582 return !mRoot && !mRegisteredClosestCommonInclusiveAncestor && 583 mSelections.IsEmpty() && !mNextStartRef && !mNextEndRef; 584 } 585 #endif // #ifdef DEBUG 586 587 nsCOMPtr<nsINode> mRoot; 588 589 // These raw pointers are used to remember a child that is about 590 // to be inserted between a CharacterData call and a subsequent 591 // ContentInserted or ContentAppended call. It is safe to store 592 // these refs because the caller is guaranteed to trigger both 593 // notifications while holding a strong reference to the new child. 594 nsIContent* MOZ_NON_OWNING_REF mNextStartRef; 595 nsIContent* MOZ_NON_OWNING_REF mNextEndRef; 596 597 static nsTArray<RefPtr<nsRange>>* sCachedRanges; 598 599 // Used to keep track of the real start and end for a 600 // selection where the start and the end are in different trees. 601 // It's NULL when the nodes are in the same tree. 602 // 603 // mCrossShadowBoundaryRange doesn't deal with DOM mutations, because 604 // it's still an open question about how it should be handled. 605 // Spec: https://github.com/w3c/selection-api/issues/168. 606 // As a result, it'll be set to NULL if that happens. 607 // 608 // Theoretically, mCrossShadowBoundaryRange isn't really needed because 609 // we should be able to always store the real start and end, and 610 // just return one point when a collapse is needed. 611 // Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1886028 is going 612 // to be used to improve mCrossShadowBoundaryRange. 613 RefPtr<mozilla::dom::CrossShadowBoundaryRange> mCrossShadowBoundaryRange; 614 615 friend class mozilla::dom::AbstractRange; 616 }; 617 namespace mozilla::dom { 618 inline nsRange* AbstractRange::AsDynamicRange() { 619 MOZ_ASSERT(IsDynamicRange()); 620 return static_cast<nsRange*>(this); 621 } 622 inline const nsRange* AbstractRange::AsDynamicRange() const { 623 MOZ_ASSERT(IsDynamicRange()); 624 return static_cast<const nsRange*>(this); 625 } 626 } // namespace mozilla::dom 627 #endif /* nsRange_h___ */