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/. */
8 * Implementation of the DOM Range object.
11 #include "RangeBoundary.h"
17 #include "nsReadableUtils.h"
18 #include "nsIContent.h"
19 #include "mozilla/dom/Document.h"
21 #include "nsINodeList.h"
22 #include "nsGkAtoms.h"
23 #include "nsContentUtils.h"
24 #include "nsLayoutUtils.h"
25 #include "nsTextFrame.h"
26 #include "nsContainerFrame.h"
27 #include "mozilla/Assertions.h"
28 #include "mozilla/CheckedInt.h"
29 #include "mozilla/ContentIterator.h"
30 #include "mozilla/dom/CharacterData.h"
31 #include "mozilla/dom/ChildIterator.h"
32 #include "mozilla/dom/DOMRect.h"
33 #include "mozilla/dom/DOMStringList.h"
34 #include "mozilla/dom/DocumentFragment.h"
35 #include "mozilla/dom/DocumentType.h"
36 #include "mozilla/dom/RangeBinding.h"
37 #include "mozilla/dom/Selection.h"
38 #include "mozilla/dom/Text.h"
39 #include "mozilla/Logging.h"
40 #include "mozilla/Maybe.h"
41 #include "mozilla/PresShell.h"
42 #include "mozilla/RangeUtils.h"
43 #include "mozilla/Telemetry.h"
44 #include "mozilla/ToString.h"
45 #include "mozilla/UniquePtr.h"
46 #include "mozilla/Likely.h"
47 #include "nsCSSFrameConstructor.h"
48 #include "nsStyleStruct.h"
49 #include "nsStyleStructInlines.h"
50 #include "nsComputedDOMStyle.h"
51 #include "mozilla/dom/InspectorFontFace.h"
54 extern LazyLogModule sSelectionAPILog
;
55 extern void LogStackForSelectionAPI();
57 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
58 static void LogSelectionAPI(const dom::Selection
* aSelection
,
59 const char* aFuncName
, const char* aArgName1
,
60 const RangeBoundaryBase
<SPT
, SRT
>& aBoundary1
,
61 const char* aArgName2
,
62 const RangeBoundaryBase
<EPT
, ERT
>& aBoundary2
,
63 const char* aArgName3
, bool aBoolArg
) {
64 if (aBoundary1
== aBoundary2
) {
65 MOZ_LOG(sSelectionAPILog
, LogLevel::Info
,
66 ("%p nsRange::%s(%s=%s=%s, %s=%s)", aSelection
, aFuncName
,
67 aArgName1
, aArgName2
, ToString(aBoundary1
).c_str(), aArgName3
,
68 aBoolArg
? "true" : "false"));
71 sSelectionAPILog
, LogLevel::Info
,
72 ("%p nsRange::%s(%s=%s, %s=%s, %s=%s)", aSelection
, aFuncName
,
73 aArgName1
, ToString(aBoundary1
).c_str(), aArgName2
,
74 ToString(aBoundary2
).c_str(), aArgName3
, aBoolArg
? "true" : "false"));
77 } // namespace mozilla
79 using namespace mozilla
;
80 using namespace mozilla::dom
;
82 template already_AddRefed
<nsRange
> nsRange::Create(
83 const RangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
,
85 template already_AddRefed
<nsRange
> nsRange::Create(
86 const RangeBoundary
& aStartBoundary
, const RawRangeBoundary
& aEndBoundary
,
88 template already_AddRefed
<nsRange
> nsRange::Create(
89 const RawRangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
,
91 template already_AddRefed
<nsRange
> nsRange::Create(
92 const RawRangeBoundary
& aStartBoundary
,
93 const RawRangeBoundary
& aEndBoundary
, ErrorResult
& aRv
);
95 template nsresult
nsRange::SetStartAndEnd(const RangeBoundary
& aStartBoundary
,
96 const RangeBoundary
& aEndBoundary
);
97 template nsresult
nsRange::SetStartAndEnd(const RangeBoundary
& aStartBoundary
,
98 const RawRangeBoundary
& aEndBoundary
);
99 template nsresult
nsRange::SetStartAndEnd(
100 const RawRangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
);
101 template nsresult
nsRange::SetStartAndEnd(
102 const RawRangeBoundary
& aStartBoundary
,
103 const RawRangeBoundary
& aEndBoundary
);
105 template void nsRange::DoSetRange(const RangeBoundary
& aStartBoundary
,
106 const RangeBoundary
& aEndBoundary
,
107 nsINode
* aRootNode
, bool aNotInsertedYet
);
108 template void nsRange::DoSetRange(const RangeBoundary
& aStartBoundary
,
109 const RawRangeBoundary
& aEndBoundary
,
110 nsINode
* aRootNode
, bool aNotInsertedYet
);
111 template void nsRange::DoSetRange(const RawRangeBoundary
& aStartBoundary
,
112 const RangeBoundary
& aEndBoundary
,
113 nsINode
* aRootNode
, bool aNotInsertedYet
);
114 template void nsRange::DoSetRange(const RawRangeBoundary
& aStartBoundary
,
115 const RawRangeBoundary
& aEndBoundary
,
116 nsINode
* aRootNode
, bool aNotInsertedYet
);
118 JSObject
* nsRange::WrapObject(JSContext
* aCx
,
119 JS::Handle
<JSObject
*> aGivenProto
) {
120 return Range_Binding::Wrap(aCx
, this, aGivenProto
);
123 DocGroup
* nsRange::GetDocGroup() const {
124 return mOwner
? mOwner
->GetDocGroup() : nullptr;
127 /******************************************************
128 * stack based utility class for managing monitor
129 ******************************************************/
131 static void InvalidateAllFrames(nsINode
* aNode
) {
132 MOZ_ASSERT(aNode
, "bad arg");
134 nsIFrame
* frame
= nullptr;
135 switch (aNode
->NodeType()) {
136 case nsINode::TEXT_NODE
:
137 case nsINode::ELEMENT_NODE
: {
138 nsIContent
* content
= static_cast<nsIContent
*>(aNode
);
139 frame
= content
->GetPrimaryFrame();
142 case nsINode::DOCUMENT_NODE
: {
143 Document
* doc
= static_cast<Document
*>(aNode
);
144 PresShell
* presShell
= doc
? doc
->GetPresShell() : nullptr;
145 frame
= presShell
? presShell
->GetRootFrame() : nullptr;
149 for (nsIFrame
* f
= frame
; f
; f
= f
->GetNextContinuation()) {
150 f
->InvalidateFrameSubtree();
154 /******************************************************
155 * constructor/destructor
156 ******************************************************/
158 nsTArray
<RefPtr
<nsRange
>>* nsRange::sCachedRanges
= nullptr;
160 nsRange::~nsRange() {
161 NS_ASSERTION(!IsInAnySelection(), "deleting nsRange that is in use");
163 // we want the side effects (releases and list removals)
164 DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
167 nsRange::nsRange(nsINode
* aNode
)
168 : AbstractRange(aNode
, /* aIsDynamicRange = */ true),
169 mNextStartRef(nullptr),
170 mNextEndRef(nullptr) {
171 // printf("Size of nsRange: %zu\n", sizeof(nsRange));
173 static_assert(sizeof(nsRange
) <= 240,
174 "nsRange size shouldn't be increased as far as possible");
178 already_AddRefed
<nsRange
> nsRange::Create(nsINode
* aNode
) {
180 if (!sCachedRanges
|| sCachedRanges
->IsEmpty()) {
181 return do_AddRef(new nsRange(aNode
));
183 RefPtr
<nsRange
> range
= sCachedRanges
->PopLastElement().forget();
185 return range
.forget();
189 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
190 already_AddRefed
<nsRange
> nsRange::Create(
191 const RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
192 const RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
, ErrorResult
& aRv
) {
193 // If we fail to initialize the range a lot, nsRange should have a static
194 // initializer since the allocation cost is not cheap in hot path.
195 RefPtr
<nsRange
> range
= nsRange::Create(aStartBoundary
.Container());
196 aRv
= range
->SetStartAndEnd(aStartBoundary
, aEndBoundary
);
197 if (NS_WARN_IF(aRv
.Failed())) {
200 return range
.forget();
203 /******************************************************
205 ******************************************************/
207 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange
)
208 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_INTERRUPTABLE_LAST_RELEASE(
209 nsRange
, DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr),
210 MaybeInterruptLastRelease())
212 // QueryInterface implementation for nsRange
213 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange
)
214 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
215 NS_INTERFACE_MAP_END_INHERITING(AbstractRange
)
217 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange
)
219 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsRange
, AbstractRange
)
220 // `Reset()` unlinks `mStart`, `mEnd` and `mRoot`.
222 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
224 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsRange
, AbstractRange
)
225 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot
)
226 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
228 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsRange
, AbstractRange
)
229 NS_IMPL_CYCLE_COLLECTION_TRACE_END
231 bool nsRange::MaybeInterruptLastRelease() {
232 bool interrupt
= AbstractRange::MaybeCacheToReuse(*this);
233 MOZ_ASSERT(!interrupt
|| IsCleared());
237 void nsRange::AdjustNextRefsOnCharacterDataSplit(
238 const nsIContent
& aContent
, const CharacterDataChangeInfo
& aInfo
) {
239 // If the splitted text node is immediately before a range boundary point
240 // that refers to a child index (i.e. its parent is the boundary container)
241 // then we need to adjust the corresponding boundary to account for the new
242 // text node that will be inserted. However, because the new sibling hasn't
243 // been inserted yet, that would result in an invalid boundary. Therefore,
244 // we store the new child in mNext*Ref to make sure we adjust the boundary
245 // in the next ContentInserted or ContentAppended call.
246 nsINode
* parentNode
= aContent
.GetParentNode();
247 if (parentNode
== mEnd
.Container()) {
248 if (&aContent
== mEnd
.Ref()) {
249 MOZ_ASSERT(aInfo
.mDetails
->mNextSibling
);
250 mNextEndRef
= aInfo
.mDetails
->mNextSibling
;
254 if (parentNode
== mStart
.Container()) {
255 if (&aContent
== mStart
.Ref()) {
256 MOZ_ASSERT(aInfo
.mDetails
->mNextSibling
);
257 mNextStartRef
= aInfo
.mDetails
->mNextSibling
;
262 nsRange::RangeBoundariesAndRoot
263 nsRange::DetermineNewRangeBoundariesAndRootOnCharacterDataMerge(
264 nsIContent
* aContent
, const CharacterDataChangeInfo
& aInfo
) const {
265 RawRangeBoundary newStart
;
266 RawRangeBoundary newEnd
;
267 nsINode
* newRoot
= nullptr;
269 // normalize(), aInfo.mDetails->mNextSibling is the merged text node
270 // that will be removed
271 nsIContent
* removed
= aInfo
.mDetails
->mNextSibling
;
272 if (removed
== mStart
.Container()) {
273 CheckedUint32 newStartOffset
{
274 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
)};
275 newStartOffset
+= aInfo
.mChangeStart
;
277 // newStartOffset.isValid() isn't checked explicitly here, because
278 // newStartOffset.value() contains an assertion.
279 newStart
= {aContent
, newStartOffset
.value()};
280 if (MOZ_UNLIKELY(removed
== mRoot
)) {
281 newRoot
= RangeUtils::ComputeRootNode(newStart
.Container());
284 if (removed
== mEnd
.Container()) {
285 CheckedUint32 newEndOffset
{
286 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
)};
287 newEndOffset
+= aInfo
.mChangeStart
;
289 // newEndOffset.isValid() isn't checked explicitly here, because
290 // newEndOffset.value() contains an assertion.
291 newEnd
= {aContent
, newEndOffset
.value()};
292 if (MOZ_UNLIKELY(removed
== mRoot
)) {
293 newRoot
= {RangeUtils::ComputeRootNode(newEnd
.Container())};
296 // When the removed text node's parent is one of our boundary nodes we may
297 // need to adjust the offset to account for the removed node. However,
298 // there will also be a ContentRemoved notification later so the only cases
299 // we need to handle here is when the removed node is the text node after
300 // the boundary. (The m*Offset > 0 check is an optimization - a boundary
301 // point before the first child is never affected by normalize().)
302 nsINode
* parentNode
= aContent
->GetParentNode();
303 if (parentNode
== mStart
.Container() &&
304 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) > 0 &&
305 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <
306 parentNode
->GetChildCount() &&
307 removed
== mStart
.GetChildAtOffset()) {
308 newStart
= {aContent
, aInfo
.mChangeStart
};
310 if (parentNode
== mEnd
.Container() &&
311 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) > 0 &&
312 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <
313 parentNode
->GetChildCount() &&
314 removed
== mEnd
.GetChildAtOffset()) {
315 newEnd
= {aContent
, aInfo
.mChangeEnd
};
318 return {newStart
, newEnd
, newRoot
};
321 /******************************************************
322 * nsIMutationObserver implementation
323 ******************************************************/
324 void nsRange::CharacterDataChanged(nsIContent
* aContent
,
325 const CharacterDataChangeInfo
& aInfo
) {
326 MOZ_ASSERT(aContent
);
327 MOZ_ASSERT(mIsPositioned
);
328 MOZ_ASSERT(!mNextEndRef
);
329 MOZ_ASSERT(!mNextStartRef
);
331 nsINode
* newRoot
= nullptr;
332 RawRangeBoundary newStart
;
333 RawRangeBoundary newEnd
;
335 if (aInfo
.mDetails
&&
336 aInfo
.mDetails
->mType
== CharacterDataChangeInfo::Details::eSplit
) {
337 AdjustNextRefsOnCharacterDataSplit(*aContent
, aInfo
);
340 // If the changed node contains our start boundary and the change starts
341 // before the boundary we'll need to adjust the offset.
342 if (aContent
== mStart
.Container() &&
344 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
)) {
345 if (aInfo
.mDetails
) {
346 // splitText(), aInfo->mDetails->mNextSibling is the new text node
348 aInfo
.mDetails
->mType
== CharacterDataChangeInfo::Details::eSplit
,
349 "only a split can start before the end");
351 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <=
352 aInfo
.mChangeEnd
+ 1,
353 "mStart.Offset() is beyond the end of this node");
354 const uint32_t newStartOffset
=
355 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) -
357 newStart
= {aInfo
.mDetails
->mNextSibling
, newStartOffset
};
358 if (MOZ_UNLIKELY(aContent
== mRoot
)) {
359 newRoot
= RangeUtils::ComputeRootNode(newStart
.Container());
362 bool isCommonAncestor
=
363 IsInAnySelection() && mStart
.Container() == mEnd
.Container();
364 if (isCommonAncestor
) {
365 UnregisterClosestCommonInclusiveAncestor(mStart
.Container(), false);
366 RegisterClosestCommonInclusiveAncestor(newStart
.Container());
368 if (mStart
.Container()
369 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
371 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
374 // If boundary is inside changed text, position it before change
375 // else adjust start offset for the change in length.
376 CheckedUint32 newStartOffset
{0};
377 if (*mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <=
379 newStartOffset
= aInfo
.mChangeStart
;
382 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
);
383 newStartOffset
-= aInfo
.LengthOfRemovedText();
384 newStartOffset
+= aInfo
.mReplaceLength
;
387 // newStartOffset.isValid() isn't checked explicitly here, because
388 // newStartOffset.value() contains an assertion.
389 newStart
= {mStart
.Container(), newStartOffset
.value()};
393 // Do the same thing for the end boundary, except for splitText of a node
394 // with no parent then only switch to the new node if the start boundary
395 // did so too (otherwise the range would end up with disconnected nodes).
396 if (aContent
== mEnd
.Container() &&
398 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
)) {
399 if (aInfo
.mDetails
&& (aContent
->GetParentNode() || newStart
.Container())) {
400 // splitText(), aInfo.mDetails->mNextSibling is the new text node
402 aInfo
.mDetails
->mType
== CharacterDataChangeInfo::Details::eSplit
,
403 "only a split can start before the end");
405 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <=
406 aInfo
.mChangeEnd
+ 1,
407 "mEnd.Offset() is beyond the end of this node");
409 const uint32_t newEndOffset
{
410 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) -
412 newEnd
= {aInfo
.mDetails
->mNextSibling
, newEndOffset
};
414 bool isCommonAncestor
=
415 IsInAnySelection() && mStart
.Container() == mEnd
.Container();
416 if (isCommonAncestor
&& !newStart
.Container()) {
417 // The split occurs inside the range.
418 UnregisterClosestCommonInclusiveAncestor(mStart
.Container(), false);
419 RegisterClosestCommonInclusiveAncestor(
420 mStart
.Container()->GetParentNode());
422 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
425 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
427 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
430 CheckedUint32 newEndOffset
{0};
431 if (*mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <=
433 newEndOffset
= aInfo
.mChangeStart
;
436 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
);
437 newEndOffset
-= aInfo
.LengthOfRemovedText();
438 newEndOffset
+= aInfo
.mReplaceLength
;
441 // newEndOffset.isValid() isn't checked explicitly here, because
442 // newEndOffset.value() contains an assertion.
443 newEnd
= {mEnd
.Container(), newEndOffset
.value()};
447 if (aInfo
.mDetails
&&
448 aInfo
.mDetails
->mType
== CharacterDataChangeInfo::Details::eMerge
) {
449 MOZ_ASSERT(!newStart
.IsSet());
450 MOZ_ASSERT(!newEnd
.IsSet());
452 RangeBoundariesAndRoot rangeBoundariesAndRoot
=
453 DetermineNewRangeBoundariesAndRootOnCharacterDataMerge(aContent
, aInfo
);
455 newStart
= rangeBoundariesAndRoot
.mStart
;
456 newEnd
= rangeBoundariesAndRoot
.mEnd
;
457 newRoot
= rangeBoundariesAndRoot
.mRoot
;
460 if (newStart
.IsSet() || newEnd
.IsSet()) {
461 if (!newStart
.IsSet()) {
462 newStart
.CopyFrom(mStart
, RangeBoundaryIsMutationObserved::Yes
);
464 if (!newEnd
.IsSet()) {
465 newEnd
.CopyFrom(mEnd
, RangeBoundaryIsMutationObserved::Yes
);
467 DoSetRange(newStart
, newEnd
, newRoot
? newRoot
: mRoot
.get(),
468 !newEnd
.Container()->GetParentNode() ||
469 !newStart
.Container()->GetParentNode());
471 nsRange::AssertIfMismatchRootAndRangeBoundaries(
473 (mStart
.IsSet() && !mStart
.Container()->GetParentNode()) ||
474 (mEnd
.IsSet() && !mEnd
.Container()->GetParentNode()));
478 void nsRange::ContentAppended(nsIContent
* aFirstNewContent
) {
479 MOZ_ASSERT(mIsPositioned
);
481 nsINode
* container
= aFirstNewContent
->GetParentNode();
482 MOZ_ASSERT(container
);
483 if (container
->IsMaybeSelected() && IsInAnySelection()) {
484 nsINode
* child
= aFirstNewContent
;
487 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
488 MarkDescendants(*child
);
490 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
492 child
= child
->GetNextSibling();
496 if (mNextStartRef
|| mNextEndRef
) {
497 // A splitText has occurred, if any mNext*Ref was set, we need to adjust
498 // the range boundaries.
500 mStart
= {mStart
.Container(), mNextStartRef
};
501 MOZ_ASSERT(mNextStartRef
== aFirstNewContent
);
502 mNextStartRef
= nullptr;
505 mEnd
= {mEnd
.Container(), mNextEndRef
};
506 MOZ_ASSERT(mNextEndRef
== aFirstNewContent
);
507 mNextEndRef
= nullptr;
509 DoSetRange(mStart
, mEnd
, mRoot
, true);
511 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart
, mEnd
, mRoot
);
515 void nsRange::ContentInserted(nsIContent
* aChild
) {
516 MOZ_ASSERT(mIsPositioned
);
518 bool updateBoundaries
= false;
519 nsINode
* container
= aChild
->GetParentNode();
520 MOZ_ASSERT(container
);
521 RawRangeBoundary
newStart(mStart
, RangeBoundaryIsMutationObserved::Yes
);
522 RawRangeBoundary
newEnd(mEnd
, RangeBoundaryIsMutationObserved::Yes
);
523 MOZ_ASSERT(aChild
->GetParentNode() == container
);
525 // Invalidate boundary offsets if a child that may have moved them was
527 if (container
== mStart
.Container()) {
528 newStart
.InvalidateOffset();
529 updateBoundaries
= true;
532 if (container
== mEnd
.Container()) {
533 newEnd
.InvalidateOffset();
534 updateBoundaries
= true;
537 if (container
->IsMaybeSelected() &&
539 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
540 MarkDescendants(*aChild
);
541 aChild
->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
544 if (mNextStartRef
|| mNextEndRef
) {
546 newStart
= {mStart
.Container(), mNextStartRef
};
547 MOZ_ASSERT(mNextStartRef
== aChild
);
548 mNextStartRef
= nullptr;
551 newEnd
= {mEnd
.Container(), mNextEndRef
};
552 MOZ_ASSERT(mNextEndRef
== aChild
);
553 mNextEndRef
= nullptr;
556 updateBoundaries
= true;
559 if (updateBoundaries
) {
560 DoSetRange(newStart
, newEnd
, mRoot
);
562 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart
, mEnd
, mRoot
);
566 void nsRange::ContentRemoved(nsIContent
* aChild
, nsIContent
* aPreviousSibling
) {
567 MOZ_ASSERT(mIsPositioned
);
569 nsINode
* container
= aChild
->GetParentNode();
570 MOZ_ASSERT(container
);
572 nsINode
* startContainer
= mStart
.Container();
573 nsINode
* endContainer
= mEnd
.Container();
575 RawRangeBoundary newStart
;
576 RawRangeBoundary newEnd
;
577 Maybe
<bool> gravitateStart
;
580 // Adjust position if a sibling was removed...
581 if (container
== startContainer
) {
582 // We're only interested if our boundary reference was removed, otherwise
583 // we can just invalidate the offset.
584 if (aChild
== mStart
.Ref()) {
585 newStart
= {container
, aPreviousSibling
};
587 newStart
.CopyFrom(mStart
, RangeBoundaryIsMutationObserved::Yes
);
588 newStart
.InvalidateOffset();
591 gravitateStart
= Some(startContainer
->IsInclusiveDescendantOf(aChild
));
592 if (gravitateStart
.value()) {
593 newStart
= {container
, aPreviousSibling
};
597 // Do same thing for end boundry.
598 if (container
== endContainer
) {
599 if (aChild
== mEnd
.Ref()) {
600 newEnd
= {container
, aPreviousSibling
};
602 newEnd
.CopyFrom(mEnd
, RangeBoundaryIsMutationObserved::Yes
);
603 newEnd
.InvalidateOffset();
606 if (startContainer
== endContainer
&& gravitateStart
.isSome()) {
607 gravitateEnd
= gravitateStart
.value();
609 gravitateEnd
= endContainer
->IsInclusiveDescendantOf(aChild
);
612 newEnd
= {container
, aPreviousSibling
};
616 bool newStartIsSet
= newStart
.IsSet();
617 bool newEndIsSet
= newEnd
.IsSet();
618 if (newStartIsSet
|| newEndIsSet
) {
619 DoSetRange(newStartIsSet
? newStart
: mStart
.AsRaw(),
620 newEndIsSet
? newEnd
: mEnd
.AsRaw(), mRoot
);
622 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart
, mEnd
, mRoot
);
625 MOZ_ASSERT(mStart
.Ref() != aChild
);
626 MOZ_ASSERT(mEnd
.Ref() != aChild
);
628 if (container
->IsMaybeSelected() &&
630 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
632 ->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
633 UnmarkDescendants(*aChild
);
637 void nsRange::ParentChainChanged(nsIContent
* aContent
) {
638 NS_ASSERTION(mRoot
== aContent
, "Wrong ParentChainChanged notification?");
639 nsINode
* newRoot
= RangeUtils::ComputeRootNode(mStart
.Container());
640 NS_ASSERTION(newRoot
, "No valid boundary or root found!");
641 if (newRoot
!= RangeUtils::ComputeRootNode(mEnd
.Container())) {
642 // Sometimes ordering involved in cycle collection can lead to our
643 // start parent and/or end parent being disconnected from our root
644 // without our getting a ContentRemoved notification.
645 // See bug 846096 for more details.
646 NS_ASSERTION(mEnd
.Container()->IsInNativeAnonymousSubtree(),
647 "This special case should happen only with "
648 "native-anonymous content");
649 // When that happens, bail out and set pointers to null; since we're
650 // in cycle collection and unreachable it shouldn't matter.
654 // This is safe without holding a strong ref to self as long as the change
655 // of mRoot is the last thing in DoSetRange.
656 DoSetRange(mStart
, mEnd
, newRoot
);
659 bool nsRange::IsPointComparableToRange(const nsINode
& aContainer
,
661 ErrorResult
& aRv
) const {
662 // our range is in a good state?
663 if (!mIsPositioned
) {
664 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
668 if (!aContainer
.IsInclusiveDescendantOf(mRoot
)) {
669 // TODO(emilio): Switch to ThrowWrongDocumentError, but IsPointInRange
670 // relies on the error code right now in order to suppress the exception.
671 aRv
.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR
);
675 auto chromeOnlyAccess
= mStart
.Container()->ChromeOnlyAccess();
676 NS_ASSERTION(chromeOnlyAccess
== mEnd
.Container()->ChromeOnlyAccess(),
677 "Start and end of a range must be either both native anonymous "
679 if (aContainer
.ChromeOnlyAccess() != chromeOnlyAccess
) {
680 aRv
.ThrowInvalidNodeTypeError(
681 "Trying to compare restricted with unrestricted nodes");
685 if (aContainer
.NodeType() == nsINode::DOCUMENT_TYPE_NODE
) {
686 aRv
.ThrowInvalidNodeTypeError("Trying to compare with a document");
690 if (aOffset
> aContainer
.Length()) {
691 aRv
.ThrowIndexSizeError("Offset is out of bounds");
698 bool nsRange::IsPointInRange(const nsINode
& aContainer
, uint32_t aOffset
,
699 ErrorResult
& aRv
) const {
700 uint16_t compareResult
= ComparePoint(aContainer
, aOffset
, aRv
);
701 // If the node isn't in the range's document, it clearly isn't in the range.
702 if (aRv
.ErrorCodeIs(NS_ERROR_DOM_WRONG_DOCUMENT_ERR
)) {
703 aRv
.SuppressException();
707 return compareResult
== 0;
710 int16_t nsRange::ComparePoint(const nsINode
& aContainer
, uint32_t aOffset
,
711 ErrorResult
& aRv
) const {
712 if (!IsPointComparableToRange(aContainer
, aOffset
, aRv
)) {
716 const RawRangeBoundary point
{const_cast<nsINode
*>(&aContainer
), aOffset
};
718 MOZ_ASSERT(point
.IsSetAndValid());
720 if (Maybe
<int32_t> order
= nsContentUtils::ComparePoints(point
, mStart
);
721 order
&& *order
<= 0) {
722 return int16_t(*order
);
724 if (Maybe
<int32_t> order
= nsContentUtils::ComparePoints(mEnd
, point
);
725 order
&& *order
== -1) {
731 bool nsRange::IntersectsNode(nsINode
& aNode
, ErrorResult
& aRv
) {
732 if (!mIsPositioned
) {
733 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
737 nsINode
* parent
= aNode
.GetParentNode();
739 // |parent| is null, so |node|'s root is |node| itself.
740 return GetRoot() == &aNode
;
743 const Maybe
<uint32_t> nodeIndex
= parent
->ComputeIndexOf(&aNode
);
744 if (nodeIndex
.isNothing()) {
748 if (!IsPointComparableToRange(*parent
, *nodeIndex
, IgnoreErrors())) {
752 const Maybe
<int32_t> startOrder
= nsContentUtils::ComparePoints(
754 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
), parent
,
756 if (startOrder
&& (*startOrder
< 0)) {
757 const Maybe
<int32_t> endOrder
= nsContentUtils::ComparePoints(
758 parent
, *nodeIndex
, mEnd
.Container(),
759 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
));
760 return endOrder
&& (*endOrder
< 0);
766 void nsRange::NotifySelectionListenersAfterRangeSet() {
767 if (!mSelections
.IsEmpty()) {
768 // Our internal code should not move focus with using this instance while
769 // it's calling Selection::NotifySelectionListeners() which may move focus
770 // or calls selection listeners. So, let's set mCalledByJS to false here
771 // since non-*JS() methods don't set it to false.
772 AutoCalledByJSRestore
calledByJSRestorer(*this);
775 // Notify all Selections. This may modify the range,
776 // remove it from the selection, or the selection itself may have gone after
777 // the call. Also, new selections may be added.
778 // To ensure that listeners are notified for all *current* selections,
779 // create a copy of the list of selections and use that for iterating. This
780 // way selections can be added or removed safely during iteration.
781 // To save allocation cost, the copy is only created if there is more than
782 // one Selection present (which will barely ever be the case).
783 if (IsPartOfOneSelectionOnly()) {
784 RefPtr
<Selection
> selection
= mSelections
[0].get();
785 selection
->NotifySelectionListeners(calledByJSRestorer
.SavedValue());
787 nsTArray
<WeakPtr
<Selection
>> copiedSelections
= mSelections
.Clone();
788 for (const auto& weakSelection
: copiedSelections
) {
789 RefPtr
<Selection
> selection
= weakSelection
.get();
790 selection
->NotifySelectionListeners(calledByJSRestorer
.SavedValue());
796 /******************************************************
797 * Private helper routines
798 ******************************************************/
801 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
802 void nsRange::AssertIfMismatchRootAndRangeBoundaries(
803 const RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
804 const RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
, const nsINode
* aRootNode
,
805 bool aNotInsertedYet
/* = false */) {
808 MOZ_ASSERT(!aStartBoundary
.IsSet());
809 MOZ_ASSERT(!aEndBoundary
.IsSet());
813 MOZ_ASSERT(aStartBoundary
.IsSet());
814 MOZ_ASSERT(aEndBoundary
.IsSet());
815 if (!aNotInsertedYet
) {
816 // Compute temporary root for given range boundaries. If a range in native
817 // anonymous subtree is being removed, tempRoot may return the fragment's
818 // root content, but it shouldn't be used for new root node because the node
819 // may be bound to the root element again.
820 nsINode
* tempRoot
= RangeUtils::ComputeRootNode(aStartBoundary
.Container());
821 // The new range should be in the temporary root node at least.
822 MOZ_ASSERT(tempRoot
==
823 RangeUtils::ComputeRootNode(aEndBoundary
.Container()));
824 MOZ_ASSERT(aStartBoundary
.Container()->IsInclusiveDescendantOf(tempRoot
));
825 MOZ_ASSERT(aEndBoundary
.Container()->IsInclusiveDescendantOf(tempRoot
));
826 // If the new range is not disconnected or not in native anonymous subtree,
827 // the temporary root must be same as the new root node. Otherwise,
828 // aRootNode should be the parent of root of the NAC (e.g., `<input>` if the
829 // range is in NAC under `<input>`), but tempRoot is now root content node
830 // of the disconnected subtree (e.g., `<div>` element in `<input>` element).
831 const bool tempRootIsDisconnectedNAC
=
832 tempRoot
->IsInNativeAnonymousSubtree() && !tempRoot
->GetParentNode();
833 MOZ_ASSERT_IF(!tempRootIsDisconnectedNAC
, tempRoot
== aRootNode
);
835 MOZ_ASSERT(aRootNode
->IsDocument() || aRootNode
->IsAttr() ||
836 aRootNode
->IsDocumentFragment() || aRootNode
->IsContent());
837 #endif // #ifdef DEBUG
840 // It's important that all setting of the range start/end points
841 // go through this function, which will do all the right voodoo
842 // for content notification of range ownership.
843 // Calling DoSetRange with either parent argument null will collapse
844 // the range to have both endpoints point to the other node
845 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
846 void nsRange::DoSetRange(const RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
847 const RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
,
849 bool aNotInsertedYet
/* = false */) {
850 mIsPositioned
= aStartBoundary
.IsSetAndValid() &&
851 aEndBoundary
.IsSetAndValid() && aRootNode
;
852 MOZ_ASSERT_IF(!mIsPositioned
, !aStartBoundary
.IsSet());
853 MOZ_ASSERT_IF(!mIsPositioned
, !aEndBoundary
.IsSet());
854 MOZ_ASSERT_IF(!mIsPositioned
, !aRootNode
);
856 nsRange::AssertIfMismatchRootAndRangeBoundaries(aStartBoundary
, aEndBoundary
,
857 aRootNode
, aNotInsertedYet
);
859 if (mRoot
!= aRootNode
) {
861 mRoot
->RemoveMutationObserver(this);
864 aRootNode
->AddMutationObserver(this);
867 bool checkCommonAncestor
=
868 (mStart
.Container() != aStartBoundary
.Container() ||
869 mEnd
.Container() != aEndBoundary
.Container()) &&
870 IsInAnySelection() && !aNotInsertedYet
;
872 // GetClosestCommonInclusiveAncestor is unreliable while we're unlinking
873 // (could return null if our start/end have already been unlinked), so make
874 // sure to not use it here to determine our "old" current ancestor.
875 mStart
.CopyFrom(aStartBoundary
, RangeBoundaryIsMutationObserved::Yes
);
876 mEnd
.CopyFrom(aEndBoundary
, RangeBoundaryIsMutationObserved::Yes
);
878 if (checkCommonAncestor
) {
879 UpdateCommonAncestorIfNecessary();
882 // This needs to be the last thing this function does, other than notifying
883 // selection listeners. See comment in ParentChainChanged.
884 if (mRoot
!= aRootNode
) {
888 // Notify any selection listeners. This has to occur last because otherwise
889 // the world could be observed by a selection listener while the range was in
890 // an invalid state. So we run it off of a script runner to ensure it runs
891 // after the mutation observers have finished running.
892 if (!mSelections
.IsEmpty()) {
893 if (MOZ_LOG_TEST(sSelectionAPILog
, LogLevel::Info
)) {
894 for (const auto& selection
: mSelections
) {
895 if (selection
&& selection
->Type() == SelectionType::eNormal
) {
896 LogSelectionAPI(selection
, __FUNCTION__
, "aStartBoundary",
897 aStartBoundary
, "aEndBoundary", aEndBoundary
,
898 "aNotInsertedYet", aNotInsertedYet
);
899 LogStackForSelectionAPI();
903 nsContentUtils::AddScriptRunner(
904 NewRunnableMethod("NotifySelectionListenersAfterRangeSet", this,
905 &nsRange::NotifySelectionListenersAfterRangeSet
));
909 void nsRange::Reset() {
910 DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
913 /******************************************************
914 * public functionality
915 ******************************************************/
917 void nsRange::SetStartJS(nsINode
& aNode
, uint32_t aOffset
, ErrorResult
& aErr
) {
918 AutoCalledByJSRestore
calledByJSRestorer(*this);
920 SetStart(aNode
, aOffset
, aErr
);
923 bool nsRange::CanAccess(const nsINode
& aNode
) const {
924 if (nsContentUtils::LegacyIsCallerNativeCode()) {
927 return nsContentUtils::CanCallerAccess(&aNode
);
930 void nsRange::SetStart(nsINode
& aNode
, uint32_t aOffset
, ErrorResult
& aRv
) {
931 if (!CanAccess(aNode
)) {
932 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
936 AutoInvalidateSelection
atEndOfBlock(this);
937 SetStart(RawRangeBoundary(&aNode
, aOffset
), aRv
);
940 void nsRange::SetStart(const RawRangeBoundary
& aPoint
, ErrorResult
& aRv
) {
941 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aPoint
.Container());
943 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
947 if (!aPoint
.IsSetAndValid()) {
948 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
952 // Collapse if not positioned yet, if positioned in another doc or
953 // if the new start is after end.
954 const bool collapse
= [&]() {
955 if (!mIsPositioned
|| (newRoot
!= mRoot
)) {
959 const Maybe
<int32_t> order
= nsContentUtils::ComparePoints(aPoint
, mEnd
);
964 MOZ_ASSERT_UNREACHABLE();
969 DoSetRange(aPoint
, aPoint
, newRoot
);
973 DoSetRange(aPoint
, mEnd
, mRoot
);
976 void nsRange::SetStartBeforeJS(nsINode
& aNode
, ErrorResult
& aErr
) {
977 AutoCalledByJSRestore
calledByJSRestorer(*this);
979 SetStartBefore(aNode
, aErr
);
982 void nsRange::SetStartBefore(nsINode
& aNode
, ErrorResult
& aRv
) {
983 if (!CanAccess(aNode
)) {
984 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
988 AutoInvalidateSelection
atEndOfBlock(this);
989 // If the node is being removed from its parent, GetRawRangeBoundaryBefore()
990 // returns unset instance. Then, SetStart() will throw
991 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
992 SetStart(RangeUtils::GetRawRangeBoundaryBefore(&aNode
), aRv
);
995 void nsRange::SetStartAfterJS(nsINode
& aNode
, ErrorResult
& aErr
) {
996 AutoCalledByJSRestore
calledByJSRestorer(*this);
998 SetStartAfter(aNode
, aErr
);
1001 void nsRange::SetStartAfter(nsINode
& aNode
, ErrorResult
& aRv
) {
1002 if (!CanAccess(aNode
)) {
1003 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1007 AutoInvalidateSelection
atEndOfBlock(this);
1008 // If the node is being removed from its parent, GetRawRangeBoundaryAfter()
1009 // returns unset instance. Then, SetStart() will throw
1010 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1011 SetStart(RangeUtils::GetRawRangeBoundaryAfter(&aNode
), aRv
);
1014 void nsRange::SetEndJS(nsINode
& aNode
, uint32_t aOffset
, ErrorResult
& aErr
) {
1015 AutoCalledByJSRestore
calledByJSRestorer(*this);
1017 SetEnd(aNode
, aOffset
, aErr
);
1020 void nsRange::SetEnd(nsINode
& aNode
, uint32_t aOffset
, ErrorResult
& aRv
) {
1021 if (!CanAccess(aNode
)) {
1022 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1025 AutoInvalidateSelection
atEndOfBlock(this);
1026 SetEnd(RawRangeBoundary(&aNode
, aOffset
), aRv
);
1029 void nsRange::SetEnd(const RawRangeBoundary
& aPoint
, ErrorResult
& aRv
) {
1030 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aPoint
.Container());
1032 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1036 if (!aPoint
.IsSetAndValid()) {
1037 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
1041 // Collapse if not positioned yet, if positioned in another doc or
1042 // if the new end is before start.
1043 const bool collapse
= [&]() {
1044 if (!mIsPositioned
|| (newRoot
!= mRoot
)) {
1048 const Maybe
<int32_t> order
= nsContentUtils::ComparePoints(mStart
, aPoint
);
1053 MOZ_ASSERT_UNREACHABLE();
1058 DoSetRange(aPoint
, aPoint
, newRoot
);
1062 DoSetRange(mStart
, aPoint
, mRoot
);
1065 void nsRange::SelectNodesInContainer(nsINode
* aContainer
,
1066 nsIContent
* aStartContent
,
1067 nsIContent
* aEndContent
) {
1068 MOZ_ASSERT(aContainer
);
1069 MOZ_ASSERT(aContainer
->ComputeIndexOf(aStartContent
).valueOr(0) <=
1070 aContainer
->ComputeIndexOf(aEndContent
).valueOr(0));
1071 MOZ_ASSERT(aStartContent
&&
1072 aContainer
->ComputeIndexOf(aStartContent
).isSome());
1073 MOZ_ASSERT(aEndContent
&& aContainer
->ComputeIndexOf(aEndContent
).isSome());
1075 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aContainer
);
1076 MOZ_ASSERT(newRoot
);
1081 RawRangeBoundary
start(aContainer
, aStartContent
->GetPreviousSibling());
1082 RawRangeBoundary
end(aContainer
, aEndContent
);
1083 DoSetRange(start
, end
, newRoot
);
1086 void nsRange::SetEndBeforeJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1087 AutoCalledByJSRestore
calledByJSRestorer(*this);
1089 SetEndBefore(aNode
, aErr
);
1092 void nsRange::SetEndBefore(nsINode
& aNode
, ErrorResult
& aRv
) {
1093 if (!CanAccess(aNode
)) {
1094 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1098 AutoInvalidateSelection
atEndOfBlock(this);
1099 // If the node is being removed from its parent, GetRawRangeBoundaryBefore()
1100 // returns unset instance. Then, SetEnd() will throw
1101 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1102 SetEnd(RangeUtils::GetRawRangeBoundaryBefore(&aNode
), aRv
);
1105 void nsRange::SetEndAfterJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1106 AutoCalledByJSRestore
calledByJSRestorer(*this);
1108 SetEndAfter(aNode
, aErr
);
1111 void nsRange::SetEndAfter(nsINode
& aNode
, ErrorResult
& aRv
) {
1112 if (!CanAccess(aNode
)) {
1113 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1117 AutoInvalidateSelection
atEndOfBlock(this);
1118 // If the node is being removed from its parent, GetRawRangeBoundaryAfter()
1119 // returns unset instance. Then, SetEnd() will throw
1120 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1121 SetEnd(RangeUtils::GetRawRangeBoundaryAfter(&aNode
), aRv
);
1124 void nsRange::Collapse(bool aToStart
) {
1125 if (!mIsPositioned
) return;
1127 AutoInvalidateSelection
atEndOfBlock(this);
1129 DoSetRange(mStart
, mStart
, mRoot
);
1131 DoSetRange(mEnd
, mEnd
, mRoot
);
1135 void nsRange::CollapseJS(bool aToStart
) {
1136 AutoCalledByJSRestore
calledByJSRestorer(*this);
1141 void nsRange::SelectNodeJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1142 AutoCalledByJSRestore
calledByJSRestorer(*this);
1144 SelectNode(aNode
, aErr
);
1147 void nsRange::SelectNode(nsINode
& aNode
, ErrorResult
& aRv
) {
1148 if (!CanAccess(aNode
)) {
1149 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1153 nsINode
* container
= aNode
.GetParentNode();
1154 nsINode
* newRoot
= RangeUtils::ComputeRootNode(container
);
1156 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1160 const Maybe
<uint32_t> index
= container
->ComputeIndexOf(&aNode
);
1161 // MOZ_ASSERT(index.isSome());
1162 // We need to compute the index here unfortunately, because, while we have
1163 // support for XBL, |container| may be the node's binding parent without
1164 // actually containing it.
1165 if (MOZ_UNLIKELY(NS_WARN_IF(index
.isNothing()))) {
1166 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1170 AutoInvalidateSelection
atEndOfBlock(this);
1171 DoSetRange(RawRangeBoundary
{container
, *index
},
1172 RawRangeBoundary
{container
, *index
+ 1u}, newRoot
);
1175 void nsRange::SelectNodeContentsJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1176 AutoCalledByJSRestore
calledByJSRestorer(*this);
1178 SelectNodeContents(aNode
, aErr
);
1181 void nsRange::SelectNodeContents(nsINode
& aNode
, ErrorResult
& aRv
) {
1182 if (!CanAccess(aNode
)) {
1183 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1187 nsINode
* newRoot
= RangeUtils::ComputeRootNode(&aNode
);
1189 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1193 AutoInvalidateSelection
atEndOfBlock(this);
1194 DoSetRange(RawRangeBoundary(&aNode
, 0u),
1195 RawRangeBoundary(&aNode
, aNode
.Length()), newRoot
);
1198 // The Subtree Content Iterator only returns subtrees that are
1199 // completely within a given range. It doesn't return a CharacterData
1200 // node that contains either the start or end point of the range.,
1201 // nor does it return element nodes when nothing in the element is selected.
1202 // We need an iterator that will also include these start/end points
1203 // so that our methods/algorithms aren't cluttered with special
1204 // case code that tries to include these points while iterating.
1206 // The RangeSubtreeIterator class mimics the ContentSubtreeIterator
1207 // methods we need, so should the Content Iterator support the
1208 // start/end points in the future, we can switchover relatively
1211 class MOZ_STACK_CLASS RangeSubtreeIterator
{
1213 enum RangeSubtreeIterState
{ eDone
= 0, eUseStart
, eUseIterator
, eUseEnd
};
1215 Maybe
<ContentSubtreeIterator
> mSubtreeIter
;
1216 RangeSubtreeIterState mIterState
;
1218 nsCOMPtr
<nsINode
> mStart
;
1219 nsCOMPtr
<nsINode
> mEnd
;
1222 RangeSubtreeIterator() : mIterState(eDone
) {}
1223 ~RangeSubtreeIterator() = default;
1225 nsresult
Init(nsRange
* aRange
);
1226 already_AddRefed
<nsINode
> GetCurrentNode();
1232 bool IsDone() { return mIterState
== eDone
; }
1235 nsresult
RangeSubtreeIterator::Init(nsRange
* aRange
) {
1237 if (aRange
->Collapsed()) {
1241 // Grab the start point of the range and QI it to
1242 // a CharacterData pointer. If it is CharacterData store
1243 // a pointer to the node.
1245 if (!aRange
->IsPositioned()) {
1246 return NS_ERROR_FAILURE
;
1249 nsINode
* node
= aRange
->GetStartContainer();
1250 if (NS_WARN_IF(!node
)) {
1251 return NS_ERROR_FAILURE
;
1254 if (node
->IsCharacterData() ||
1255 (node
->IsElement() &&
1256 node
->AsElement()->GetChildCount() == aRange
->StartOffset())) {
1260 // Grab the end point of the range and QI it to
1261 // a CharacterData pointer. If it is CharacterData store
1262 // a pointer to the node.
1264 node
= aRange
->GetEndContainer();
1265 if (NS_WARN_IF(!node
)) {
1266 return NS_ERROR_FAILURE
;
1269 if (node
->IsCharacterData() ||
1270 (node
->IsElement() && aRange
->EndOffset() == 0)) {
1274 if (mStart
&& mStart
== mEnd
) {
1275 // The range starts and stops in the same CharacterData
1276 // node. Null out the end pointer so we only visit the
1281 // Now create a Content Subtree Iterator to be used
1282 // for the subtrees between the end points!
1284 mSubtreeIter
.emplace();
1286 nsresult res
= mSubtreeIter
->Init(aRange
);
1287 if (NS_FAILED(res
)) return res
;
1289 if (mSubtreeIter
->IsDone()) {
1290 // The subtree iterator thinks there's nothing
1291 // to iterate over, so just free it up so we
1292 // don't accidentally call into it.
1294 mSubtreeIter
.reset();
1298 // Initialize the iterator by calling First().
1299 // Note that we are ignoring the return value on purpose!
1306 already_AddRefed
<nsINode
> RangeSubtreeIterator::GetCurrentNode() {
1307 nsCOMPtr
<nsINode
> node
;
1309 if (mIterState
== eUseStart
&& mStart
) {
1311 } else if (mIterState
== eUseEnd
&& mEnd
) {
1313 } else if (mIterState
== eUseIterator
&& mSubtreeIter
) {
1314 node
= mSubtreeIter
->GetCurrentNode();
1317 return node
.forget();
1320 void RangeSubtreeIterator::First() {
1322 mIterState
= eUseStart
;
1323 else if (mSubtreeIter
) {
1324 mSubtreeIter
->First();
1326 mIterState
= eUseIterator
;
1328 mIterState
= eUseEnd
;
1333 void RangeSubtreeIterator::Last() {
1335 mIterState
= eUseEnd
;
1336 else if (mSubtreeIter
) {
1337 mSubtreeIter
->Last();
1339 mIterState
= eUseIterator
;
1341 mIterState
= eUseStart
;
1346 void RangeSubtreeIterator::Next() {
1347 if (mIterState
== eUseStart
) {
1349 mSubtreeIter
->First();
1351 mIterState
= eUseIterator
;
1353 mIterState
= eUseEnd
;
1356 } else if (mIterState
== eUseIterator
) {
1357 mSubtreeIter
->Next();
1359 if (mSubtreeIter
->IsDone()) {
1361 mIterState
= eUseEnd
;
1369 void RangeSubtreeIterator::Prev() {
1370 if (mIterState
== eUseEnd
) {
1372 mSubtreeIter
->Last();
1374 mIterState
= eUseIterator
;
1376 mIterState
= eUseStart
;
1379 } else if (mIterState
== eUseIterator
) {
1380 mSubtreeIter
->Prev();
1382 if (mSubtreeIter
->IsDone()) {
1384 mIterState
= eUseStart
;
1392 // CollapseRangeAfterDelete() is a utility method that is used by
1393 // DeleteContents() and ExtractContents() to collapse the range
1394 // in the correct place, under the range's root container (the
1395 // range end points common container) as outlined by the Range spec:
1397 // http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html
1398 // The assumption made by this method is that the delete or extract
1399 // has been done already, and left the range in a state where there is
1400 // no content between the 2 end points.
1402 static nsresult
CollapseRangeAfterDelete(nsRange
* aRange
) {
1403 NS_ENSURE_ARG_POINTER(aRange
);
1405 // Check if range gravity took care of collapsing the range for us!
1406 if (aRange
->Collapsed()) {
1407 // aRange is collapsed so there's nothing for us to do.
1409 // There are 2 possible scenarios here:
1411 // 1. aRange could've been collapsed prior to the delete/extract,
1412 // which would've resulted in nothing being removed, so aRange
1413 // is already where it should be.
1415 // 2. Prior to the delete/extract, aRange's start and end were in
1416 // the same container which would mean everything between them
1417 // was removed, causing range gravity to collapse the range.
1422 // aRange isn't collapsed so figure out the appropriate place to collapse!
1423 // First get both end points and their common ancestor.
1425 if (!aRange
->IsPositioned()) {
1426 return NS_ERROR_NOT_INITIALIZED
;
1429 nsCOMPtr
<nsINode
> commonAncestor
=
1430 aRange
->GetClosestCommonInclusiveAncestor();
1432 nsCOMPtr
<nsINode
> startContainer
= aRange
->GetStartContainer();
1433 nsCOMPtr
<nsINode
> endContainer
= aRange
->GetEndContainer();
1435 // Collapse to one of the end points if they are already in the
1436 // commonAncestor. This should work ok since this method is called
1437 // immediately after a delete or extract that leaves no content
1438 // between the 2 end points!
1440 if (startContainer
== commonAncestor
) {
1441 aRange
->Collapse(true);
1444 if (endContainer
== commonAncestor
) {
1445 aRange
->Collapse(false);
1449 // End points are at differing levels. We want to collapse to the
1450 // point that is between the 2 subtrees that contain each point,
1451 // under the common ancestor.
1453 nsCOMPtr
<nsINode
> nodeToSelect(startContainer
);
1455 while (nodeToSelect
) {
1456 nsCOMPtr
<nsINode
> parent
= nodeToSelect
->GetParentNode();
1457 if (parent
== commonAncestor
) break; // We found the nodeToSelect!
1459 nodeToSelect
= parent
;
1462 if (!nodeToSelect
) return NS_ERROR_FAILURE
; // This should never happen!
1465 aRange
->SelectNode(*nodeToSelect
, error
);
1466 if (error
.Failed()) {
1467 return error
.StealNSResult();
1470 aRange
->Collapse(false);
1475 PrependChild(nsINode
* aContainer
, nsINode
* aChild
) {
1476 nsCOMPtr
<nsINode
> first
= aContainer
->GetFirstChild();
1478 aContainer
->InsertBefore(*aChild
, first
, rv
);
1479 return rv
.StealNSResult();
1482 // Helper function for CutContents, making sure that the current node wasn't
1483 // removed by mutation events (bug 766426)
1484 static bool ValidateCurrentNode(nsRange
* aRange
, RangeSubtreeIterator
& aIter
) {
1486 nsCOMPtr
<nsINode
> node
= aIter
.GetCurrentNode();
1488 // We don't have to worry that the node was removed if it doesn't exist,
1489 // e.g., the iterator is done.
1493 nsresult rv
= RangeUtils::CompareNodeToRange(node
, aRange
, &before
, &after
);
1494 if (NS_WARN_IF(NS_FAILED(rv
))) {
1498 if (before
|| after
) {
1499 if (node
->IsCharacterData()) {
1500 // If we're dealing with the start/end container which is a character
1501 // node, pretend that the node is in the range.
1502 if (before
&& node
== aRange
->GetStartContainer()) {
1505 if (after
&& node
== aRange
->GetEndContainer()) {
1511 return !before
&& !after
;
1514 void nsRange::CutContents(DocumentFragment
** aFragment
, ErrorResult
& aRv
) {
1516 *aFragment
= nullptr;
1519 if (!CanAccess(*mStart
.Container()) || !CanAccess(*mEnd
.Container())) {
1520 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1524 nsCOMPtr
<Document
> doc
= mStart
.Container()->OwnerDoc();
1526 nsCOMPtr
<nsINode
> commonAncestor
= GetCommonAncestorContainer(aRv
);
1531 // If aFragment isn't null, create a temporary fragment to hold our return.
1532 RefPtr
<DocumentFragment
> retval
;
1535 new (doc
->NodeInfoManager()) DocumentFragment(doc
->NodeInfoManager());
1537 nsCOMPtr
<nsINode
> commonCloneAncestor
= retval
.get();
1539 // Batch possible DOMSubtreeModified events.
1540 mozAutoSubtreeModified
subtree(mRoot
? mRoot
->OwnerDoc() : nullptr, nullptr);
1542 // Save the range end points locally to avoid interference
1543 // of Range gravity during our edits!
1545 nsCOMPtr
<nsINode
> startContainer
= mStart
.Container();
1546 // `GetCommonAncestorContainer()` above ensures the range is positioned, hence
1547 // there have to be valid offsets.
1548 uint32_t startOffset
=
1549 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
1550 nsCOMPtr
<nsINode
> endContainer
= mEnd
.Container();
1551 uint32_t endOffset
= *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
1554 // For extractContents(), abort early if there's a doctype (bug 719533).
1555 // This can happen only if the common ancestor is a document, in which case
1556 // we just need to find its doctype child and check if that's in the range.
1557 nsCOMPtr
<Document
> commonAncestorDocument
=
1558 do_QueryInterface(commonAncestor
);
1559 if (commonAncestorDocument
) {
1560 RefPtr
<DocumentType
> doctype
= commonAncestorDocument
->GetDoctype();
1562 // `GetCommonAncestorContainer()` above ensured the range is positioned.
1563 // Hence, start and end are both set and valid. If available, `doctype`
1564 // has a common ancestor with start and end, hence both have to be
1565 // comparable to it.
1567 *nsContentUtils::ComparePoints(startContainer
, startOffset
, doctype
,
1569 *nsContentUtils::ComparePoints(doctype
, 0, endContainer
, endOffset
) <
1571 aRv
.ThrowHierarchyRequestError("Start or end position isn't valid.");
1577 // Create and initialize a subtree iterator that will give
1578 // us all the subtrees within the range.
1580 RangeSubtreeIterator iter
;
1582 aRv
= iter
.Init(this);
1587 if (iter
.IsDone()) {
1588 // There's nothing for us to delete.
1589 aRv
= CollapseRangeAfterDelete(this);
1590 if (!aRv
.Failed() && aFragment
) {
1591 retval
.forget(aFragment
);
1598 bool handled
= false;
1600 // With the exception of text nodes that contain one of the range
1601 // end points, the subtree iterator should only give us back subtrees
1602 // that are completely contained between the range's end points.
1604 while (!iter
.IsDone()) {
1605 nsCOMPtr
<nsINode
> nodeToResult
;
1606 nsCOMPtr
<nsINode
> node
= iter
.GetCurrentNode();
1608 // Before we delete anything, advance the iterator to the next node that's
1609 // not a descendant of this one. XXX It's a bit silly to iterate through
1610 // the descendants only to throw them out, we should use an iterator that
1611 // skips the descendants to begin with.
1614 nsCOMPtr
<nsINode
> nextNode
= iter
.GetCurrentNode();
1615 while (nextNode
&& nextNode
->IsInclusiveDescendantOf(node
)) {
1617 nextNode
= iter
.GetCurrentNode();
1622 // If it's CharacterData, make sure we might need to delete
1623 // part of the data, instead of removing the whole node.
1625 // XXX_kin: We need to also handle ProcessingInstruction
1626 // XXX_kin: according to the spec.
1628 if (auto charData
= CharacterData::FromNode(node
)) {
1629 uint32_t dataLength
= 0;
1631 if (node
== startContainer
) {
1632 if (node
== endContainer
) {
1633 // This range is completely contained within a single text node.
1634 // Delete or extract the data between startOffset and endOffset.
1636 if (endOffset
> startOffset
) {
1638 nsAutoString cutValue
;
1639 charData
->SubstringData(startOffset
, endOffset
- startOffset
,
1641 if (NS_WARN_IF(aRv
.Failed())) {
1644 nsCOMPtr
<nsINode
> clone
= node
->CloneNode(false, aRv
);
1645 if (NS_WARN_IF(aRv
.Failed())) {
1648 clone
->SetNodeValue(cutValue
, aRv
);
1649 if (NS_WARN_IF(aRv
.Failed())) {
1652 nodeToResult
= clone
;
1655 nsMutationGuard guard
;
1656 charData
->DeleteData(startOffset
, endOffset
- startOffset
, aRv
);
1657 if (NS_WARN_IF(aRv
.Failed())) {
1660 if (guard
.Mutated(0) && !ValidateCurrentNode(this, iter
)) {
1661 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1668 // Delete or extract everything after startOffset.
1670 dataLength
= charData
->Length();
1672 if (dataLength
>= startOffset
) {
1674 nsAutoString cutValue
;
1675 charData
->SubstringData(startOffset
, dataLength
, cutValue
, aRv
);
1676 if (NS_WARN_IF(aRv
.Failed())) {
1679 nsCOMPtr
<nsINode
> clone
= node
->CloneNode(false, aRv
);
1680 if (NS_WARN_IF(aRv
.Failed())) {
1683 clone
->SetNodeValue(cutValue
, aRv
);
1684 if (NS_WARN_IF(aRv
.Failed())) {
1687 nodeToResult
= clone
;
1690 nsMutationGuard guard
;
1691 charData
->DeleteData(startOffset
, dataLength
, aRv
);
1692 if (NS_WARN_IF(aRv
.Failed())) {
1695 if (guard
.Mutated(0) && !ValidateCurrentNode(this, iter
)) {
1696 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1703 } else if (node
== endContainer
) {
1704 // Delete or extract everything before endOffset.
1706 nsAutoString cutValue
;
1707 charData
->SubstringData(0, endOffset
, cutValue
, aRv
);
1708 if (NS_WARN_IF(aRv
.Failed())) {
1711 nsCOMPtr
<nsINode
> clone
= node
->CloneNode(false, aRv
);
1712 if (NS_WARN_IF(aRv
.Failed())) {
1715 clone
->SetNodeValue(cutValue
, aRv
);
1716 if (NS_WARN_IF(aRv
.Failed())) {
1719 nodeToResult
= clone
;
1722 nsMutationGuard guard
;
1723 charData
->DeleteData(0, endOffset
, aRv
);
1724 if (NS_WARN_IF(aRv
.Failed())) {
1727 if (guard
.Mutated(0) && !ValidateCurrentNode(this, iter
)) {
1728 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1735 if (!handled
&& (node
== endContainer
|| node
== startContainer
)) {
1736 if (node
&& node
->IsElement() &&
1737 ((node
== endContainer
&& endOffset
== 0) ||
1738 (node
== startContainer
&&
1739 node
->AsElement()->GetChildCount() == startOffset
))) {
1741 nodeToResult
= node
->CloneNode(false, aRv
);
1751 // node was not handled above, so it must be completely contained
1752 // within the range. Just remove it from the tree!
1753 nodeToResult
= node
;
1756 uint32_t parentCount
= 0;
1757 // Set the result to document fragment if we have 'retval'.
1759 nsCOMPtr
<nsINode
> oldCommonAncestor
= commonAncestor
;
1760 if (!iter
.IsDone()) {
1761 // Setup the parameters for the next iteration of the loop.
1763 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1767 // Get node's and nextNode's common parent. Do this before moving
1768 // nodes from original DOM to result fragment.
1770 nsContentUtils::GetClosestCommonInclusiveAncestor(node
, nextNode
);
1771 if (!commonAncestor
) {
1772 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1776 nsCOMPtr
<nsINode
> parentCounterNode
= node
;
1777 while (parentCounterNode
&& parentCounterNode
!= commonAncestor
) {
1779 parentCounterNode
= parentCounterNode
->GetParentNode();
1780 if (!parentCounterNode
) {
1781 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1787 // Clone the parent hierarchy between commonAncestor and node.
1788 nsCOMPtr
<nsINode
> closestAncestor
, farthestAncestor
;
1789 aRv
= CloneParentsBetween(oldCommonAncestor
, node
,
1790 getter_AddRefs(closestAncestor
),
1791 getter_AddRefs(farthestAncestor
));
1796 if (farthestAncestor
) {
1797 commonCloneAncestor
->AppendChild(*farthestAncestor
, aRv
);
1798 if (NS_WARN_IF(aRv
.Failed())) {
1803 nsMutationGuard guard
;
1804 nsCOMPtr
<nsINode
> parent
= nodeToResult
->GetParentNode();
1805 if (closestAncestor
) {
1806 closestAncestor
->AppendChild(*nodeToResult
, aRv
);
1808 commonCloneAncestor
->AppendChild(*nodeToResult
, aRv
);
1810 if (NS_WARN_IF(aRv
.Failed())) {
1813 if (guard
.Mutated(parent
? 2 : 1) && !ValidateCurrentNode(this, iter
)) {
1814 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1817 } else if (nodeToResult
) {
1818 nsMutationGuard guard
;
1819 nsCOMPtr
<nsINode
> node
= nodeToResult
;
1820 nsCOMPtr
<nsINode
> parent
= node
->GetParentNode();
1822 parent
->RemoveChild(*node
, aRv
);
1827 if (guard
.Mutated(1) && !ValidateCurrentNode(this, iter
)) {
1828 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1833 if (!iter
.IsDone() && retval
) {
1834 // Find the equivalent of commonAncestor in the cloned tree.
1835 nsCOMPtr
<nsINode
> newCloneAncestor
= nodeToResult
;
1836 for (uint32_t i
= parentCount
; i
; --i
) {
1837 newCloneAncestor
= newCloneAncestor
->GetParentNode();
1838 if (!newCloneAncestor
) {
1839 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1843 commonCloneAncestor
= newCloneAncestor
;
1847 aRv
= CollapseRangeAfterDelete(this);
1848 if (!aRv
.Failed() && aFragment
) {
1849 retval
.forget(aFragment
);
1853 void nsRange::DeleteContents(ErrorResult
& aRv
) { CutContents(nullptr, aRv
); }
1855 already_AddRefed
<DocumentFragment
> nsRange::ExtractContents(ErrorResult
& rv
) {
1856 RefPtr
<DocumentFragment
> fragment
;
1857 CutContents(getter_AddRefs(fragment
), rv
);
1858 return fragment
.forget();
1861 int16_t nsRange::CompareBoundaryPoints(uint16_t aHow
,
1862 const nsRange
& aOtherRange
,
1864 if (!mIsPositioned
|| !aOtherRange
.IsPositioned()) {
1865 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
1869 nsINode
*ourNode
, *otherNode
;
1870 uint32_t ourOffset
, otherOffset
;
1873 case Range_Binding::START_TO_START
:
1874 ourNode
= mStart
.Container();
1875 ourOffset
= *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
1876 otherNode
= aOtherRange
.GetStartContainer();
1877 otherOffset
= aOtherRange
.StartOffset();
1879 case Range_Binding::START_TO_END
:
1880 ourNode
= mEnd
.Container();
1881 ourOffset
= *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
1882 otherNode
= aOtherRange
.GetStartContainer();
1883 otherOffset
= aOtherRange
.StartOffset();
1885 case Range_Binding::END_TO_START
:
1886 ourNode
= mStart
.Container();
1887 ourOffset
= *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
1888 otherNode
= aOtherRange
.GetEndContainer();
1889 otherOffset
= aOtherRange
.EndOffset();
1891 case Range_Binding::END_TO_END
:
1892 ourNode
= mEnd
.Container();
1893 ourOffset
= *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
1894 otherNode
= aOtherRange
.GetEndContainer();
1895 otherOffset
= aOtherRange
.EndOffset();
1898 // We were passed an illegal value
1899 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
1903 if (mRoot
!= aOtherRange
.GetRoot()) {
1904 aRv
.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR
);
1908 const Maybe
<int32_t> order
=
1909 nsContentUtils::ComparePoints(ourNode
, ourOffset
, otherNode
, otherOffset
);
1911 // `this` and `aOtherRange` share the same root and (ourNode, ourOffset),
1912 // (otherNode, otherOffset) correspond to some of their boundaries. Hence,
1913 // (ourNode, ourOffset) and (otherNode, otherOffset) have to be comparable.
1918 nsresult
nsRange::CloneParentsBetween(nsINode
* aAncestor
, nsINode
* aNode
,
1919 nsINode
** aClosestAncestor
,
1920 nsINode
** aFarthestAncestor
) {
1921 NS_ENSURE_ARG_POINTER(
1922 (aAncestor
&& aNode
&& aClosestAncestor
&& aFarthestAncestor
));
1924 *aClosestAncestor
= nullptr;
1925 *aFarthestAncestor
= nullptr;
1927 if (aAncestor
== aNode
) return NS_OK
;
1929 AutoTArray
<nsCOMPtr
<nsINode
>, 16> parentStack
;
1931 nsCOMPtr
<nsINode
> parent
= aNode
->GetParentNode();
1932 while (parent
&& parent
!= aAncestor
) {
1933 parentStack
.AppendElement(parent
);
1934 parent
= parent
->GetParentNode();
1937 nsCOMPtr
<nsINode
> firstParent
;
1938 nsCOMPtr
<nsINode
> lastParent
;
1939 for (int32_t i
= parentStack
.Length() - 1; i
>= 0; i
--) {
1941 nsCOMPtr
<nsINode
> clone
= parentStack
[i
]->CloneNode(false, rv
);
1944 return rv
.StealNSResult();
1947 return NS_ERROR_FAILURE
;
1953 firstParent
->AppendChild(*clone
, rv
);
1955 return rv
.StealNSResult();
1959 firstParent
= clone
;
1962 firstParent
.forget(aClosestAncestor
);
1963 lastParent
.forget(aFarthestAncestor
);
1968 already_AddRefed
<DocumentFragment
> nsRange::CloneContents(ErrorResult
& aRv
) {
1969 nsCOMPtr
<nsINode
> commonAncestor
= GetCommonAncestorContainer(aRv
);
1970 MOZ_ASSERT(!aRv
.Failed(), "GetCommonAncestorContainer() shouldn't fail!");
1972 nsCOMPtr
<Document
> doc
= mStart
.Container()->OwnerDoc();
1973 NS_ASSERTION(doc
, "CloneContents needs a document to continue.");
1975 aRv
.Throw(NS_ERROR_FAILURE
);
1979 // Create a new document fragment in the context of this document,
1980 // which might be null
1982 RefPtr
<DocumentFragment
> clonedFrag
=
1983 new (doc
->NodeInfoManager()) DocumentFragment(doc
->NodeInfoManager());
1986 return clonedFrag
.forget();
1989 nsCOMPtr
<nsINode
> commonCloneAncestor
= clonedFrag
.get();
1991 // Create and initialize a subtree iterator that will give
1992 // us all the subtrees within the range.
1994 RangeSubtreeIterator iter
;
1996 aRv
= iter
.Init(this);
2001 if (iter
.IsDone()) {
2002 // There's nothing to add to the doc frag, we must be done!
2003 return clonedFrag
.forget();
2008 // With the exception of text nodes that contain one of the range
2009 // end points and elements which don't have any content selected the subtree
2010 // iterator should only give us back subtrees that are completely contained
2011 // between the range's end points.
2013 // Unfortunately these subtrees don't contain the parent hierarchy/context
2014 // that the Range spec requires us to return. This loop clones the
2015 // parent hierarchy, adds a cloned version of the subtree, to it, then
2016 // correctly places this new subtree into the doc fragment.
2018 while (!iter
.IsDone()) {
2019 nsCOMPtr
<nsINode
> node
= iter
.GetCurrentNode();
2021 !node
->IsElement() ||
2022 (!(node
== mEnd
.Container() &&
2023 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
) == 0) &&
2024 !(node
== mStart
.Container() &&
2025 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
) ==
2026 node
->AsElement()->GetChildCount()));
2028 // Clone the current subtree!
2030 nsCOMPtr
<nsINode
> clone
= node
->CloneNode(deepClone
, aRv
);
2035 // If it's CharacterData, make sure we only clone what
2038 // XXX_kin: We need to also handle ProcessingInstruction
2039 // XXX_kin: according to the spec.
2041 if (auto charData
= CharacterData::FromNode(clone
)) {
2042 if (node
== mEnd
.Container()) {
2043 // We only need the data before mEndOffset, so get rid of any
2046 uint32_t dataLength
= charData
->Length();
2048 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
)) {
2049 charData
->DeleteData(
2050 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2052 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2060 if (node
== mStart
.Container()) {
2061 // We don't need any data before mStartOffset, so just
2064 if (*mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
) > 0) {
2065 charData
->DeleteData(
2066 0, *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2075 // Clone the parent hierarchy between commonAncestor and node.
2077 nsCOMPtr
<nsINode
> closestAncestor
, farthestAncestor
;
2079 aRv
= CloneParentsBetween(commonAncestor
, node
,
2080 getter_AddRefs(closestAncestor
),
2081 getter_AddRefs(farthestAncestor
));
2087 // Hook the parent hierarchy/context of the subtree into the clone tree.
2089 if (farthestAncestor
) {
2090 commonCloneAncestor
->AppendChild(*farthestAncestor
, aRv
);
2097 // Place the cloned subtree into the cloned doc frag tree!
2099 nsCOMPtr
<nsINode
> cloneNode
= clone
;
2100 if (closestAncestor
) {
2101 // Append the subtree under closestAncestor since it is the
2102 // immediate parent of the subtree.
2104 closestAncestor
->AppendChild(*cloneNode
, aRv
);
2106 // If we get here, there is no missing parent hierarchy between
2107 // commonAncestor and node, so just append clone to commonCloneAncestor.
2109 commonCloneAncestor
->AppendChild(*cloneNode
, aRv
);
2115 // Get the next subtree to be processed. The idea here is to setup
2116 // the parameters for the next iteration of the loop.
2120 if (iter
.IsDone()) break; // We must be done!
2122 nsCOMPtr
<nsINode
> nextNode
= iter
.GetCurrentNode();
2124 aRv
.Throw(NS_ERROR_FAILURE
);
2128 // Get node and nextNode's common parent.
2130 nsContentUtils::GetClosestCommonInclusiveAncestor(node
, nextNode
);
2132 if (!commonAncestor
) {
2133 aRv
.Throw(NS_ERROR_FAILURE
);
2137 // Find the equivalent of commonAncestor in the cloned tree!
2139 while (node
&& node
!= commonAncestor
) {
2140 node
= node
->GetParentNode();
2146 aRv
.Throw(NS_ERROR_FAILURE
);
2150 cloneNode
= cloneNode
->GetParentNode();
2152 aRv
.Throw(NS_ERROR_FAILURE
);
2157 commonCloneAncestor
= cloneNode
;
2160 return clonedFrag
.forget();
2163 already_AddRefed
<nsRange
> nsRange::CloneRange() const {
2164 RefPtr
<nsRange
> range
= nsRange::Create(mOwner
);
2165 range
->DoSetRange(mStart
, mEnd
, mRoot
);
2166 return range
.forget();
2169 void nsRange::InsertNode(nsINode
& aNode
, ErrorResult
& aRv
) {
2170 if (!CanAccess(aNode
)) {
2171 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
2175 if (!IsPositioned()) {
2176 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
2180 uint32_t tStartOffset
= StartOffset();
2182 nsCOMPtr
<nsINode
> tStartContainer
= GetStartContainer();
2184 if (!CanAccess(*tStartContainer
)) {
2185 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
2189 if (&aNode
== tStartContainer
) {
2190 aRv
.ThrowHierarchyRequestError(
2191 "The inserted node can not be range's start node.");
2195 // This is the node we'll be inserting before, and its parent
2196 nsCOMPtr
<nsINode
> referenceNode
;
2197 nsCOMPtr
<nsINode
> referenceParentNode
= tStartContainer
;
2199 RefPtr
<Text
> startTextNode
= tStartContainer
->GetAsText();
2200 nsCOMPtr
<nsINodeList
> tChildList
;
2201 if (startTextNode
) {
2202 referenceParentNode
= tStartContainer
->GetParentNode();
2203 if (!referenceParentNode
) {
2204 aRv
.ThrowHierarchyRequestError(
2205 "Can not get range's start node's parent.");
2209 referenceParentNode
->EnsurePreInsertionValidity(aNode
, tStartContainer
,
2215 RefPtr
<Text
> secondPart
= startTextNode
->SplitText(tStartOffset
, aRv
);
2220 referenceNode
= secondPart
;
2222 tChildList
= tStartContainer
->ChildNodes();
2224 // find the insertion point in the DOM and insert the Node
2225 referenceNode
= tChildList
->Item(tStartOffset
);
2227 tStartContainer
->EnsurePreInsertionValidity(aNode
, referenceNode
, aRv
);
2233 // We might need to update the end to include the new node (bug 433662).
2234 // Ideally we'd only do this if needed, but it's tricky to know when it's
2235 // needed in advance (bug 765799).
2238 if (referenceNode
) {
2239 Maybe
<uint32_t> indexInParent
= referenceNode
->ComputeIndexInParentNode();
2240 if (MOZ_UNLIKELY(NS_WARN_IF(indexInParent
.isNothing()))) {
2241 aRv
.Throw(NS_ERROR_FAILURE
);
2244 newOffset
= *indexInParent
;
2246 newOffset
= tChildList
->Length();
2249 if (aNode
.NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE
) {
2250 newOffset
+= aNode
.GetChildCount();
2255 // Now actually insert the node
2256 nsCOMPtr
<nsINode
> tResultNode
;
2257 tResultNode
= referenceParentNode
->InsertBefore(aNode
, referenceNode
, aRv
);
2263 aRv
= SetEnd(referenceParentNode
, newOffset
);
2267 void nsRange::SurroundContents(nsINode
& aNewParent
, ErrorResult
& aRv
) {
2268 if (!CanAccess(aNewParent
)) {
2269 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
2274 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2277 // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text
2279 if (mStart
.Container() != mEnd
.Container()) {
2280 bool startIsText
= mStart
.Container()->IsText();
2281 bool endIsText
= mEnd
.Container()->IsText();
2282 nsINode
* startGrandParent
= mStart
.Container()->GetParentNode();
2283 nsINode
* endGrandParent
= mEnd
.Container()->GetParentNode();
2284 if (!((startIsText
&& endIsText
&& startGrandParent
&&
2285 startGrandParent
== endGrandParent
) ||
2286 (startIsText
&& startGrandParent
&&
2287 startGrandParent
== mEnd
.Container()) ||
2288 (endIsText
&& endGrandParent
&&
2289 endGrandParent
== mStart
.Container()))) {
2290 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2295 // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted
2296 // (Document, DocumentType, DocumentFragment)
2297 uint16_t nodeType
= aNewParent
.NodeType();
2298 if (nodeType
== nsINode::DOCUMENT_NODE
||
2299 nodeType
== nsINode::DOCUMENT_TYPE_NODE
||
2300 nodeType
== nsINode::DOCUMENT_FRAGMENT_NODE
) {
2301 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
2305 // Extract the contents within the range.
2307 RefPtr
<DocumentFragment
> docFrag
= ExtractContents(aRv
);
2314 aRv
.Throw(NS_ERROR_FAILURE
);
2318 // Spec says we need to remove all of aNewParent's
2319 // children prior to insertion.
2321 nsCOMPtr
<nsINodeList
> children
= aNewParent
.ChildNodes();
2323 aRv
.Throw(NS_ERROR_FAILURE
);
2327 uint32_t numChildren
= children
->Length();
2329 while (numChildren
) {
2330 nsCOMPtr
<nsINode
> child
= children
->Item(--numChildren
);
2332 aRv
.Throw(NS_ERROR_FAILURE
);
2336 aNewParent
.RemoveChild(*child
, aRv
);
2342 // Insert aNewParent at the range's start point.
2344 InsertNode(aNewParent
, aRv
);
2349 // Append the content we extracted under aNewParent.
2350 aNewParent
.AppendChild(*docFrag
, aRv
);
2355 // Select aNewParent, and its contents.
2357 SelectNode(aNewParent
, aRv
);
2360 void nsRange::ToString(nsAString
& aReturn
, ErrorResult
& aErr
) {
2364 // If we're unpositioned, return the empty string
2365 if (!mIsPositioned
) {
2370 printf("Range dump: -----------------------\n");
2373 // effeciency hack for simple case
2374 if (mStart
.Container() == mEnd
.Container()) {
2376 mStart
.Container() ? mStart
.Container()->GetAsText() : nullptr;
2380 // If debug, dump it:
2381 textNode
->List(stdout
);
2382 printf("End Range dump: -----------------------\n");
2386 textNode
->SubstringData(
2387 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2388 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
) -
2389 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2395 /* complex case: mStart.Container() != mEnd.Container(), or mStartParent not a
2396 text node revisit - there are potential optimizations here and also
2400 PostContentIterator postOrderIter
;
2401 nsresult rv
= postOrderIter
.Init(this);
2402 if (NS_WARN_IF(NS_FAILED(rv
))) {
2407 nsString tempString
;
2409 // loop through the content iterator, which returns nodes in the range in
2410 // close tag order, and grab the text from any text node
2411 for (; !postOrderIter
.IsDone(); postOrderIter
.Next()) {
2412 nsINode
* n
= postOrderIter
.GetCurrentNode();
2415 // If debug, dump it:
2418 Text
* textNode
= n
->GetAsText();
2419 if (textNode
) // if it's a text node, get the text
2421 if (n
== mStart
.Container()) { // only include text past start offset
2422 uint32_t strLength
= textNode
->Length();
2423 textNode
->SubstringData(
2424 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2426 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2427 tempString
, IgnoreErrors());
2428 aReturn
+= tempString
;
2430 mEnd
.Container()) { // only include text before end offset
2431 textNode
->SubstringData(
2432 0, *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2433 tempString
, IgnoreErrors());
2434 aReturn
+= tempString
;
2435 } else { // grab the whole kit-n-kaboodle
2436 textNode
->GetData(tempString
);
2437 aReturn
+= tempString
;
2443 printf("End Range dump: -----------------------\n");
2447 void nsRange::Detach() {}
2449 already_AddRefed
<DocumentFragment
> nsRange::CreateContextualFragment(
2450 const nsAString
& aFragment
, ErrorResult
& aRv
) const {
2451 if (!mIsPositioned
) {
2452 aRv
.Throw(NS_ERROR_FAILURE
);
2456 return nsContentUtils::CreateContextualFragment(mStart
.Container(), aFragment
,
2460 static void ExtractRectFromOffset(nsIFrame
* aFrame
, const int32_t aOffset
,
2461 nsRect
* aR
, bool aFlushToOriginEdge
,
2462 bool aClampToEdge
) {
2467 aFrame
->GetPointFromOffset(aOffset
, &point
);
2469 // Determine if aFrame has a vertical writing mode, which will change our math
2470 // on the output rect.
2471 bool isVertical
= aFrame
->GetWritingMode().IsVertical();
2473 if (!aClampToEdge
&& !aR
->Contains(point
)) {
2474 // If point is outside aR, and we aren't clamping, output an empty rect
2475 // with origin at the point.
2487 point
= aR
->ClampPoint(point
);
2490 // point is within aR, and now we'll modify aR to output a rect that has point
2491 // on one edge. But which edge?
2492 if (aFlushToOriginEdge
) {
2493 // The output rect should be flush to the edge of aR that contains the
2496 aR
->SetHeight(point
.y
- aR
->y
);
2498 aR
->SetWidth(point
.x
- aR
->x
);
2501 // The output rect should be flush to the edge of aR opposite the origin.
2503 aR
->SetHeight(aR
->YMost() - point
.y
);
2506 aR
->SetWidth(aR
->XMost() - point
.x
);
2512 static nsTextFrame
* GetTextFrameForContent(nsIContent
* aContent
,
2513 bool aFlushLayout
) {
2514 RefPtr
<Document
> doc
= aContent
->OwnerDoc();
2515 PresShell
* presShell
= doc
->GetPresShell();
2520 // Try to un-suppress whitespace if needed, but only if we'll be able to flush
2521 // to immediately see the results of the un-suppression. If we can't flush
2522 // here, then calling EnsureFrameForTextNodeIsCreatedAfterFlush would be
2523 // pointless anyway.
2525 const bool frameWillBeUnsuppressed
=
2526 presShell
->FrameConstructor()
2527 ->EnsureFrameForTextNodeIsCreatedAfterFlush(
2528 static_cast<CharacterData
*>(aContent
));
2529 if (frameWillBeUnsuppressed
) {
2530 doc
->FlushPendingNotifications(FlushType::Layout
);
2534 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
2535 if (!frame
|| !frame
->IsTextFrame()) {
2538 return static_cast<nsTextFrame
*>(frame
);
2541 static nsresult
GetPartialTextRect(RectCallback
* aCallback
,
2542 Sequence
<nsString
>* aTextList
,
2543 nsIContent
* aContent
, int32_t aStartOffset
,
2544 int32_t aEndOffset
, bool aClampToEdge
,
2545 bool aFlushLayout
) {
2546 nsTextFrame
* textFrame
= GetTextFrameForContent(aContent
, aFlushLayout
);
2548 nsIFrame
* relativeTo
=
2549 nsLayoutUtils::GetContainingBlockForClientRect(textFrame
);
2551 for (nsTextFrame
* f
= textFrame
->FindContinuationForOffset(aStartOffset
); f
;
2552 f
= static_cast<nsTextFrame
*>(f
->GetNextContinuation())) {
2553 int32_t fstart
= f
->GetContentOffset(), fend
= f
->GetContentEnd();
2554 if (fend
<= aStartOffset
) {
2557 if (fstart
>= aEndOffset
) {
2561 // Calculate the text content offsets we'll need if text is requested.
2562 int32_t textContentStart
= fstart
;
2563 int32_t textContentEnd
= fend
;
2565 // overlapping with the offset we want
2566 f
->EnsureTextRun(nsTextFrame::eInflated
);
2567 NS_ENSURE_TRUE(f
->GetTextRun(nsTextFrame::eInflated
),
2568 NS_ERROR_OUT_OF_MEMORY
);
2569 bool topLeftToBottomRight
=
2570 !f
->GetTextRun(nsTextFrame::eInflated
)->IsInlineReversed();
2571 nsRect r
= f
->GetRectRelativeToSelf();
2572 if (fstart
< aStartOffset
) {
2573 // aStartOffset is within this frame
2574 ExtractRectFromOffset(f
, aStartOffset
, &r
, !topLeftToBottomRight
,
2576 textContentStart
= aStartOffset
;
2578 if (fend
> aEndOffset
) {
2579 // aEndOffset is in the middle of this frame
2580 ExtractRectFromOffset(f
, aEndOffset
, &r
, topLeftToBottomRight
,
2582 textContentEnd
= aEndOffset
;
2584 r
= nsLayoutUtils::TransformFrameRectToAncestor(f
, r
, relativeTo
);
2585 aCallback
->AddRect(r
);
2587 // Finally capture the text, if requested.
2589 nsIFrame::RenderedText renderedText
=
2590 f
->GetRenderedText(textContentStart
, textContentEnd
,
2591 nsIFrame::TextOffsetType::OffsetsInContentText
,
2592 nsIFrame::TrailingWhitespace::DontTrim
);
2594 NS_ENSURE_TRUE(aTextList
->AppendElement(renderedText
.mString
, fallible
),
2595 NS_ERROR_OUT_OF_MEMORY
);
2602 static void CollectClientRectsForSubtree(
2603 nsINode
* aNode
, RectCallback
* aCollector
, Sequence
<nsString
>* aTextList
,
2604 nsINode
* aStartContainer
, uint32_t aStartOffset
, nsINode
* aEndContainer
,
2605 uint32_t aEndOffset
, bool aClampToEdge
, bool aFlushLayout
) {
2606 auto* content
= nsIContent::FromNode(aNode
);
2611 if (content
->IsText()) {
2612 if (aNode
== aStartContainer
) {
2613 int32_t offset
= aStartContainer
== aEndContainer
2614 ? static_cast<int32_t>(aEndOffset
)
2615 : content
->AsText()->TextDataLength();
2616 GetPartialTextRect(aCollector
, aTextList
, content
,
2617 static_cast<int32_t>(aStartOffset
), offset
,
2618 aClampToEdge
, aFlushLayout
);
2622 if (aNode
== aEndContainer
) {
2623 GetPartialTextRect(aCollector
, aTextList
, content
, 0,
2624 static_cast<int32_t>(aEndOffset
), aClampToEdge
,
2630 if (aNode
->IsElement() && aNode
->AsElement()->IsDisplayContents()) {
2631 FlattenedChildIterator
childIter(content
);
2633 for (nsIContent
* child
= childIter
.GetNextChild(); child
;
2634 child
= childIter
.GetNextChild()) {
2635 CollectClientRectsForSubtree(child
, aCollector
, aTextList
,
2636 aStartContainer
, aStartOffset
, aEndContainer
,
2637 aEndOffset
, aClampToEdge
, aFlushLayout
);
2639 } else if (nsIFrame
* frame
= content
->GetPrimaryFrame()) {
2640 nsLayoutUtils::GetAllInFlowRectsAndTexts(
2641 frame
, nsLayoutUtils::GetContainingBlockForClientRect(frame
),
2642 aCollector
, aTextList
, nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS
);
2647 void nsRange::CollectClientRectsAndText(
2648 RectCallback
* aCollector
, Sequence
<nsString
>* aTextList
, nsRange
* aRange
,
2649 nsINode
* aStartContainer
, uint32_t aStartOffset
, nsINode
* aEndContainer
,
2650 uint32_t aEndOffset
, bool aClampToEdge
, bool aFlushLayout
) {
2651 // Currently, this method is called with start of end offset of nsRange.
2652 // So, they must be between 0 - INT32_MAX.
2653 MOZ_ASSERT(RangeUtils::IsValidOffset(aStartOffset
));
2654 MOZ_ASSERT(RangeUtils::IsValidOffset(aEndOffset
));
2656 // Hold strong pointers across the flush
2657 nsCOMPtr
<nsINode
> startContainer
= aStartContainer
;
2658 nsCOMPtr
<nsINode
> endContainer
= aEndContainer
;
2660 // Flush out layout so our frames are up to date.
2661 if (!aStartContainer
->IsInComposedDoc()) {
2666 aStartContainer
->OwnerDoc()->FlushPendingNotifications(FlushType::Layout
);
2667 // Recheck whether we're still in the document
2668 if (!aStartContainer
->IsInComposedDoc()) {
2673 RangeSubtreeIterator iter
;
2675 nsresult rv
= iter
.Init(aRange
);
2676 if (NS_FAILED(rv
)) return;
2678 if (iter
.IsDone()) {
2679 // the range is collapsed, only continue if the cursor is in a text node
2680 if (aStartContainer
->IsText()) {
2681 nsTextFrame
* textFrame
=
2682 GetTextFrameForContent(aStartContainer
->AsText(), aFlushLayout
);
2686 textFrame
->GetChildFrameContainingOffset(
2687 static_cast<int32_t>(aStartOffset
), false, &outOffset
, &outFrame
);
2689 nsIFrame
* relativeTo
=
2690 nsLayoutUtils::GetContainingBlockForClientRect(outFrame
);
2691 nsRect r
= outFrame
->GetRectRelativeToSelf();
2692 ExtractRectFromOffset(outFrame
, static_cast<int32_t>(aStartOffset
),
2693 &r
, false, aClampToEdge
);
2695 r
= nsLayoutUtils::TransformFrameRectToAncestor(outFrame
, r
,
2697 aCollector
->AddRect(r
);
2705 nsCOMPtr
<nsINode
> node
= iter
.GetCurrentNode();
2708 CollectClientRectsForSubtree(node
, aCollector
, aTextList
, aStartContainer
,
2709 aStartOffset
, aEndContainer
, aEndOffset
,
2710 aClampToEdge
, aFlushLayout
);
2711 } while (!iter
.IsDone());
2714 already_AddRefed
<DOMRect
> nsRange::GetBoundingClientRect(bool aClampToEdge
,
2715 bool aFlushLayout
) {
2716 RefPtr
<DOMRect
> rect
= new DOMRect(ToSupports(mOwner
));
2717 if (!mIsPositioned
) {
2718 return rect
.forget();
2721 nsLayoutUtils::RectAccumulator accumulator
;
2722 CollectClientRectsAndText(
2723 &accumulator
, nullptr, this, mStart
.Container(),
2724 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2726 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
), aClampToEdge
,
2729 nsRect r
= accumulator
.mResultRect
.IsEmpty() ? accumulator
.mFirstRect
2730 : accumulator
.mResultRect
;
2731 rect
->SetLayoutRect(r
);
2732 return rect
.forget();
2735 already_AddRefed
<DOMRectList
> nsRange::GetClientRects(bool aClampToEdge
,
2736 bool aFlushLayout
) {
2737 if (!mIsPositioned
) {
2741 RefPtr
<DOMRectList
> rectList
= new DOMRectList(ToSupports(mOwner
));
2743 nsLayoutUtils::RectListBuilder
builder(rectList
);
2745 CollectClientRectsAndText(
2746 &builder
, nullptr, this, mStart
.Container(),
2747 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2749 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
), aClampToEdge
,
2751 return rectList
.forget();
2754 void nsRange::GetClientRectsAndTexts(mozilla::dom::ClientRectsAndTexts
& aResult
,
2755 ErrorResult
& aErr
) {
2756 if (!mIsPositioned
) {
2760 aResult
.mRectList
= new DOMRectList(ToSupports(mOwner
));
2762 nsLayoutUtils::RectListBuilder
builder(aResult
.mRectList
);
2764 CollectClientRectsAndText(
2765 &builder
, &aResult
.mTextList
, this, mStart
.Container(),
2766 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2768 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
), true, true);
2771 nsresult
nsRange::GetUsedFontFaces(nsLayoutUtils::UsedFontFaceList
& aResult
,
2772 uint32_t aMaxRanges
,
2773 bool aSkipCollapsedWhitespace
) {
2774 NS_ENSURE_TRUE(mIsPositioned
, NS_ERROR_UNEXPECTED
);
2776 nsCOMPtr
<nsINode
> startContainer
= mStart
.Container();
2777 nsCOMPtr
<nsINode
> endContainer
= mEnd
.Container();
2779 // Flush out layout so our frames are up to date.
2780 Document
* doc
= mStart
.Container()->OwnerDoc();
2781 NS_ENSURE_TRUE(doc
, NS_ERROR_UNEXPECTED
);
2782 doc
->FlushPendingNotifications(FlushType::Frames
);
2784 // Recheck whether we're still in the document
2785 NS_ENSURE_TRUE(mStart
.Container()->IsInComposedDoc(), NS_ERROR_UNEXPECTED
);
2787 // A table to map gfxFontEntry objects to InspectorFontFace objects.
2788 // This table does NOT own the InspectorFontFace objects, it only holds
2789 // raw pointers to them. They are owned by the aResult array.
2790 nsLayoutUtils::UsedFontFaceTable fontFaces
;
2792 RangeSubtreeIterator iter
;
2793 nsresult rv
= iter
.Init(this);
2794 NS_ENSURE_SUCCESS(rv
, rv
);
2796 while (!iter
.IsDone()) {
2797 // only collect anything if the range is not collapsed
2798 nsCOMPtr
<nsINode
> node
= iter
.GetCurrentNode();
2801 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(node
);
2805 nsIFrame
* frame
= content
->GetPrimaryFrame();
2810 if (content
->IsText()) {
2811 if (node
== startContainer
) {
2813 startContainer
== endContainer
2814 ? *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
)
2815 : content
->AsText()->TextDataLength();
2816 nsLayoutUtils::GetFontFacesForText(
2817 frame
, *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2818 offset
, true, aResult
, fontFaces
, aMaxRanges
,
2819 aSkipCollapsedWhitespace
);
2822 if (node
== endContainer
) {
2823 nsLayoutUtils::GetFontFacesForText(
2824 frame
, 0, *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2825 true, aResult
, fontFaces
, aMaxRanges
, aSkipCollapsedWhitespace
);
2830 nsLayoutUtils::GetFontFacesForFrames(frame
, aResult
, fontFaces
, aMaxRanges
,
2831 aSkipCollapsedWhitespace
);
2837 nsINode
* nsRange::GetRegisteredClosestCommonInclusiveAncestor() {
2838 MOZ_ASSERT(IsInAnySelection(),
2839 "GetRegisteredClosestCommonInclusiveAncestor only valid for range "
2841 MOZ_ASSERT(mRegisteredClosestCommonInclusiveAncestor
);
2842 return mRegisteredClosestCommonInclusiveAncestor
;
2846 bool nsRange::AutoInvalidateSelection::sIsNested
;
2848 nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() {
2849 if (!mCommonAncestor
) {
2853 ::InvalidateAllFrames(mCommonAncestor
);
2855 // Our range might not be in a selection anymore, because one of our selection
2856 // listeners might have gone ahead and run script of various sorts that messed
2857 // with selections, ranges, etc. But if it still is, we should check whether
2858 // we have a different common ancestor now, and if so invalidate its subtree
2859 // so it paints the selection it's in now.
2860 if (mRange
->IsInAnySelection()) {
2861 nsINode
* commonAncestor
=
2862 mRange
->GetRegisteredClosestCommonInclusiveAncestor();
2863 // XXXbz can commonAncestor really be null here? I wouldn't think so! If
2864 // it _were_, then in a debug build
2865 // GetRegisteredClosestCommonInclusiveAncestor() would have fatally
2867 if (commonAncestor
&& commonAncestor
!= mCommonAncestor
) {
2868 ::InvalidateAllFrames(commonAncestor
);
2874 already_AddRefed
<nsRange
> nsRange::Constructor(const GlobalObject
& aGlobal
,
2876 nsCOMPtr
<nsPIDOMWindowInner
> window
=
2877 do_QueryInterface(aGlobal
.GetAsSupports());
2878 if (!window
|| !window
->GetDoc()) {
2879 aRv
.Throw(NS_ERROR_FAILURE
);
2883 return window
->GetDoc()->CreateRange(aRv
);
2886 static bool ExcludeIfNextToNonSelectable(nsIContent
* aContent
) {
2887 return aContent
->IsText() &&
2888 aContent
->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE
);
2891 void nsRange::ExcludeNonSelectableNodes(nsTArray
<RefPtr
<nsRange
>>* aOutRanges
) {
2892 if (!mIsPositioned
) {
2896 MOZ_ASSERT(mEnd
.Container());
2897 MOZ_ASSERT(mStart
.Container());
2899 nsRange
* range
= this;
2900 RefPtr
<nsRange
> newRange
;
2902 PreContentIterator preOrderIter
;
2903 nsresult rv
= preOrderIter
.Init(range
);
2904 if (NS_FAILED(rv
)) {
2909 bool seenSelectable
= false;
2910 // |firstNonSelectableContent| is the first node in a consecutive sequence
2911 // of non-IsSelectable nodes. When we find a selectable node after such
2912 // a sequence we'll end the last nsRange, create a new one and restart
2914 nsIContent
* firstNonSelectableContent
= nullptr;
2916 nsINode
* node
= preOrderIter
.GetCurrentNode();
2917 preOrderIter
.Next();
2918 bool selectable
= true;
2919 nsIContent
* content
=
2920 node
&& node
->IsContent() ? node
->AsContent() : nullptr;
2922 if (firstNonSelectableContent
&&
2923 ExcludeIfNextToNonSelectable(content
)) {
2924 // Ignorable whitespace next to a sequence of non-selectable nodes
2925 // counts as non-selectable (bug 1216001).
2929 nsIFrame
* frame
= content
->GetPrimaryFrame();
2930 for (nsIContent
* p
= content
; !frame
&& (p
= p
->GetParent());) {
2931 frame
= p
->GetPrimaryFrame();
2934 selectable
= frame
->IsSelectable(nullptr);
2940 if (!firstNonSelectableContent
) {
2941 firstNonSelectableContent
= content
;
2943 if (preOrderIter
.IsDone()) {
2944 if (seenSelectable
) {
2945 // The tail end of the initial range is non-selectable - truncate
2946 // the current range before the first non-selectable node.
2947 range
->SetEndBefore(*firstNonSelectableContent
, IgnoreErrors());
2954 if (firstNonSelectableContent
) {
2955 if (range
== this && !seenSelectable
) {
2956 // This is the initial range and all its nodes until now are
2957 // non-selectable so just trim them from the start.
2958 IgnoredErrorResult err
;
2959 range
->SetStartBefore(*node
, err
);
2963 break; // restart the same range with a new iterator
2966 // Save the end point before truncating the range.
2967 nsINode
* endContainer
= range
->mEnd
.Container();
2968 const uint32_t endOffset
=
2969 *range
->mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
2971 // Truncate the current range before the first non-selectable node.
2972 IgnoredErrorResult err
;
2973 range
->SetEndBefore(*firstNonSelectableContent
, err
);
2975 // Store it in the result (strong ref) - do this before creating
2976 // a new range in |newRange| below so we don't drop the last ref
2977 // to the range created in the previous iteration.
2978 if (!added
&& !err
.Failed()) {
2979 aOutRanges
->AppendElement(range
);
2982 // Create a new range for the remainder.
2983 nsINode
* startContainer
= node
;
2984 Maybe
<uint32_t> startOffset
= Some(0);
2985 // Don't start *inside* a node with independent selection though
2987 if (content
&& content
->HasIndependentSelection()) {
2988 nsINode
* parent
= startContainer
->GetParent();
2990 startOffset
= parent
->ComputeIndexOf(startContainer
);
2991 startContainer
= parent
;
2995 nsRange::Create(startContainer
, startOffset
.valueOr(UINT32_MAX
),
2996 endContainer
, endOffset
, IgnoreErrors());
2997 if (!newRange
|| newRange
->Collapsed()) {
3001 break; // create a new iterator for the new range, if any
3004 seenSelectable
= true;
3007 aOutRanges
->AppendElement(range
);
3009 if (preOrderIter
.IsDone()) {
3016 struct InnerTextAccumulator
{
3017 explicit InnerTextAccumulator(mozilla::dom::DOMString
& aValue
)
3018 : mString(aValue
.AsAString()), mRequiredLineBreakCount(0) {}
3019 void FlushLineBreaks() {
3020 while (mRequiredLineBreakCount
> 0) {
3021 // Required line breaks at the start of the text are suppressed.
3022 if (!mString
.IsEmpty()) {
3023 mString
.Append('\n');
3025 --mRequiredLineBreakCount
;
3028 void Append(char aCh
) { Append(nsAutoString(aCh
)); }
3029 void Append(const nsAString
& aString
) {
3030 if (aString
.IsEmpty()) {
3034 mString
.Append(aString
);
3036 void AddRequiredLineBreakCount(int8_t aCount
) {
3037 mRequiredLineBreakCount
= std::max(mRequiredLineBreakCount
, aCount
);
3041 int8_t mRequiredLineBreakCount
;
3044 static bool IsVisibleAndNotInReplacedElement(nsIFrame
* aFrame
) {
3045 if (!aFrame
|| !aFrame
->StyleVisibility()->IsVisible() ||
3046 aFrame
->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
)) {
3049 if (aFrame
->HidesContent()) {
3052 for (nsIFrame
* f
= aFrame
->GetParent(); f
; f
= f
->GetParent()) {
3053 if (f
->HidesContent()) {
3056 if (f
->IsReplaced() &&
3057 !f
->GetContent()->IsAnyOfHTMLElements(nsGkAtoms::button
,
3058 nsGkAtoms::select
) &&
3059 !f
->GetContent()->IsSVGElement()) {
3066 static void AppendTransformedText(InnerTextAccumulator
& aResult
,
3067 nsIContent
* aContainer
) {
3068 auto textNode
= static_cast<CharacterData
*>(aContainer
);
3070 nsIFrame
* frame
= textNode
->GetPrimaryFrame();
3071 if (!IsVisibleAndNotInReplacedElement(frame
)) {
3075 nsIFrame::RenderedText text
=
3076 frame
->GetRenderedText(0, aContainer
->GetChildCount());
3077 aResult
.Append(text
.mString
);
3081 * States for tree traversal. AT_NODE means that we are about to enter
3082 * the current DOM node. AFTER_NODE means that we have just finished traversing
3083 * the children of the current DOM node and are about to apply any
3084 * "after processing the node's children" steps before we finish visiting
3087 enum TreeTraversalState
{ AT_NODE
, AFTER_NODE
};
3089 static int8_t GetRequiredInnerTextLineBreakCount(nsIFrame
* aFrame
) {
3090 if (aFrame
->GetContent()->IsHTMLElement(nsGkAtoms::p
)) {
3093 const nsStyleDisplay
* styleDisplay
= aFrame
->StyleDisplay();
3094 if (styleDisplay
->IsBlockOutside(aFrame
) ||
3095 styleDisplay
->mDisplay
== StyleDisplay::TableCaption
) {
3101 static bool IsLastCellOfRow(nsIFrame
* aFrame
) {
3102 LayoutFrameType type
= aFrame
->Type();
3103 if (type
!= LayoutFrameType::TableCell
) {
3106 for (nsIFrame
* c
= aFrame
; c
; c
= c
->GetNextContinuation()) {
3107 if (c
->GetNextSibling()) {
3114 static bool IsLastRowOfRowGroup(nsIFrame
* aFrame
) {
3115 if (!aFrame
->IsTableRowFrame()) {
3118 for (nsIFrame
* c
= aFrame
; c
; c
= c
->GetNextContinuation()) {
3119 if (c
->GetNextSibling()) {
3126 static bool IsLastNonemptyRowGroupOfTable(nsIFrame
* aFrame
) {
3127 if (!aFrame
->IsTableRowGroupFrame()) {
3130 for (nsIFrame
* c
= aFrame
; c
; c
= c
->GetNextContinuation()) {
3131 for (nsIFrame
* next
= c
->GetNextSibling(); next
;
3132 next
= next
->GetNextSibling()) {
3133 if (next
->PrincipalChildList().FirstChild()) {
3141 void nsRange::GetInnerTextNoFlush(DOMString
& aValue
, ErrorResult
& aError
,
3142 nsIContent
* aContainer
) {
3143 InnerTextAccumulator
result(aValue
);
3145 if (aContainer
->IsText()) {
3146 AppendTransformedText(result
, aContainer
);
3150 nsIContent
* currentNode
= aContainer
;
3151 TreeTraversalState currentState
= AFTER_NODE
;
3153 nsIContent
* endNode
= aContainer
;
3154 TreeTraversalState endState
= AFTER_NODE
;
3156 nsIContent
* firstChild
= aContainer
->GetFirstChild();
3158 currentNode
= firstChild
;
3159 currentState
= AT_NODE
;
3162 while (currentNode
!= endNode
|| currentState
!= endState
) {
3163 nsIFrame
* f
= currentNode
->GetPrimaryFrame();
3164 bool isVisibleAndNotReplaced
= IsVisibleAndNotInReplacedElement(f
);
3165 if (currentState
== AT_NODE
) {
3166 bool isText
= currentNode
->IsText();
3167 if (isVisibleAndNotReplaced
) {
3168 result
.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f
));
3170 nsIFrame::RenderedText text
= f
->GetRenderedText();
3171 result
.Append(text
.mString
);
3174 nsIContent
* child
= currentNode
->GetFirstChild();
3176 currentNode
= child
;
3179 currentState
= AFTER_NODE
;
3181 if (currentNode
== endNode
&& currentState
== endState
) {
3184 if (isVisibleAndNotReplaced
) {
3185 if (currentNode
->IsHTMLElement(nsGkAtoms::br
)) {
3186 result
.Append('\n');
3188 switch (f
->StyleDisplay()->DisplayInside()) {
3189 case StyleDisplayInside::TableCell
:
3190 if (!IsLastCellOfRow(f
)) {
3191 result
.Append('\t');
3194 case StyleDisplayInside::TableRow
:
3195 if (!IsLastRowOfRowGroup(f
) ||
3196 !IsLastNonemptyRowGroupOfTable(f
->GetParent())) {
3197 result
.Append('\n');
3201 break; // Do nothing
3203 result
.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f
));
3205 nsIContent
* next
= currentNode
->GetNextSibling();
3208 currentState
= AT_NODE
;
3210 currentNode
= currentNode
->GetParent();
3214 // Do not flush trailing line breaks! Required breaks at the end of the text