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 "nsFrameSelection.h"
25 #include "nsLayoutUtils.h"
26 #include "nsTextFrame.h"
27 #include "nsContainerFrame.h"
28 #include "mozilla/Assertions.h"
29 #include "mozilla/CheckedInt.h"
30 #include "mozilla/ContentIterator.h"
31 #include "mozilla/dom/CharacterData.h"
32 #include "mozilla/dom/ChildIterator.h"
33 #include "mozilla/dom/DOMRect.h"
34 #include "mozilla/dom/DOMStringList.h"
35 #include "mozilla/dom/DocumentFragment.h"
36 #include "mozilla/dom/DocumentType.h"
37 #include "mozilla/dom/RangeBinding.h"
38 #include "mozilla/dom/Selection.h"
39 #include "mozilla/dom/Text.h"
40 #include "mozilla/Logging.h"
41 #include "mozilla/Maybe.h"
42 #include "mozilla/PresShell.h"
43 #include "mozilla/RangeUtils.h"
44 #include "mozilla/Telemetry.h"
45 #include "mozilla/ToString.h"
46 #include "mozilla/UniquePtr.h"
47 #include "mozilla/Likely.h"
48 #include "nsCSSFrameConstructor.h"
49 #include "nsStyleStruct.h"
50 #include "nsStyleStructInlines.h"
51 #include "nsComputedDOMStyle.h"
52 #include "mozilla/dom/InspectorFontFace.h"
55 extern LazyLogModule sSelectionAPILog
;
56 extern void LogStackForSelectionAPI();
58 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
59 static void LogSelectionAPI(const dom::Selection
* aSelection
,
60 const char* aFuncName
, const char* aArgName1
,
61 const RangeBoundaryBase
<SPT
, SRT
>& aBoundary1
,
62 const char* aArgName2
,
63 const RangeBoundaryBase
<EPT
, ERT
>& aBoundary2
,
64 const char* aArgName3
, bool aBoolArg
) {
65 if (aBoundary1
== aBoundary2
) {
66 MOZ_LOG(sSelectionAPILog
, LogLevel::Info
,
67 ("%p nsRange::%s(%s=%s=%s, %s=%s)", aSelection
, aFuncName
,
68 aArgName1
, aArgName2
, ToString(aBoundary1
).c_str(), aArgName3
,
69 aBoolArg
? "true" : "false"));
72 sSelectionAPILog
, LogLevel::Info
,
73 ("%p nsRange::%s(%s=%s, %s=%s, %s=%s)", aSelection
, aFuncName
,
74 aArgName1
, ToString(aBoundary1
).c_str(), aArgName2
,
75 ToString(aBoundary2
).c_str(), aArgName3
, aBoolArg
? "true" : "false"));
78 } // namespace mozilla
80 using namespace mozilla
;
81 using namespace mozilla::dom
;
83 template already_AddRefed
<nsRange
> nsRange::Create(
84 const RangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
,
86 template already_AddRefed
<nsRange
> nsRange::Create(
87 const RangeBoundary
& aStartBoundary
, const RawRangeBoundary
& aEndBoundary
,
89 template already_AddRefed
<nsRange
> nsRange::Create(
90 const RawRangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
,
92 template already_AddRefed
<nsRange
> nsRange::Create(
93 const RawRangeBoundary
& aStartBoundary
,
94 const RawRangeBoundary
& aEndBoundary
, ErrorResult
& aRv
);
96 template nsresult
nsRange::SetStartAndEnd(const RangeBoundary
& aStartBoundary
,
97 const RangeBoundary
& aEndBoundary
);
98 template nsresult
nsRange::SetStartAndEnd(const RangeBoundary
& aStartBoundary
,
99 const RawRangeBoundary
& aEndBoundary
);
100 template nsresult
nsRange::SetStartAndEnd(
101 const RawRangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
);
102 template nsresult
nsRange::SetStartAndEnd(
103 const RawRangeBoundary
& aStartBoundary
,
104 const RawRangeBoundary
& aEndBoundary
);
106 template void nsRange::DoSetRange(const RangeBoundary
& aStartBoundary
,
107 const RangeBoundary
& aEndBoundary
,
108 nsINode
* aRootNode
, bool aNotInsertedYet
,
109 CollapsePolicy aCollapsePolicy
);
110 template void nsRange::DoSetRange(const RangeBoundary
& aStartBoundary
,
111 const RawRangeBoundary
& aEndBoundary
,
112 nsINode
* aRootNode
, bool aNotInsertedYet
,
113 CollapsePolicy aCollapsePolicy
);
114 template void nsRange::DoSetRange(const RawRangeBoundary
& aStartBoundary
,
115 const RangeBoundary
& aEndBoundary
,
116 nsINode
* aRootNode
, bool aNotInsertedYet
,
117 CollapsePolicy aCollapsePolicy
);
118 template void nsRange::DoSetRange(const RawRangeBoundary
& aStartBoundary
,
119 const RawRangeBoundary
& aEndBoundary
,
120 nsINode
* aRootNode
, bool aNotInsertedYet
,
121 CollapsePolicy aCollapsePolicy
);
123 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
124 const RangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
);
125 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
126 const RangeBoundary
& aStartBoundary
, const RawRangeBoundary
& aEndBoundary
);
127 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
128 const RawRangeBoundary
& aStartBoundary
, const RangeBoundary
& aEndBoundary
);
129 template void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
130 const RawRangeBoundary
& aStartBoundary
,
131 const RawRangeBoundary
& aEndBoundary
);
133 JSObject
* nsRange::WrapObject(JSContext
* aCx
,
134 JS::Handle
<JSObject
*> aGivenProto
) {
135 return Range_Binding::Wrap(aCx
, this, aGivenProto
);
138 DocGroup
* nsRange::GetDocGroup() const {
139 return mOwner
? mOwner
->GetDocGroup() : nullptr;
142 /******************************************************
143 * stack based utility class for managing monitor
144 ******************************************************/
146 static void InvalidateAllFrames(nsINode
* aNode
) {
147 MOZ_ASSERT(aNode
, "bad arg");
149 nsIFrame
* frame
= nullptr;
150 switch (aNode
->NodeType()) {
151 case nsINode::TEXT_NODE
:
152 case nsINode::ELEMENT_NODE
: {
153 nsIContent
* content
= static_cast<nsIContent
*>(aNode
);
154 frame
= content
->GetPrimaryFrame();
157 case nsINode::DOCUMENT_NODE
: {
158 Document
* doc
= static_cast<Document
*>(aNode
);
159 PresShell
* presShell
= doc
? doc
->GetPresShell() : nullptr;
160 frame
= presShell
? presShell
->GetRootFrame() : nullptr;
164 for (nsIFrame
* f
= frame
; f
; f
= f
->GetNextContinuation()) {
165 f
->InvalidateFrameSubtree();
169 /******************************************************
170 * constructor/destructor
171 ******************************************************/
173 nsTArray
<RefPtr
<nsRange
>>* nsRange::sCachedRanges
= nullptr;
175 nsRange::~nsRange() {
176 NS_ASSERTION(!IsInAnySelection(), "deleting nsRange that is in use");
178 // we want the side effects (releases and list removals)
179 DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
182 nsRange::nsRange(nsINode
* aNode
)
183 : AbstractRange(aNode
, /* aIsDynamicRange = */ true),
184 mNextStartRef(nullptr),
185 mNextEndRef(nullptr) {
186 // printf("Size of nsRange: %zu\n", sizeof(nsRange));
188 static_assert(sizeof(nsRange
) <= 248,
189 "nsRange size shouldn't be increased as far as possible");
193 already_AddRefed
<nsRange
> nsRange::Create(nsINode
* aNode
) {
195 if (!sCachedRanges
|| sCachedRanges
->IsEmpty()) {
196 return do_AddRef(new nsRange(aNode
));
198 RefPtr
<nsRange
> range
= sCachedRanges
->PopLastElement().forget();
200 return range
.forget();
204 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
205 already_AddRefed
<nsRange
> nsRange::Create(
206 const RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
207 const RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
, ErrorResult
& aRv
) {
208 // If we fail to initialize the range a lot, nsRange should have a static
209 // initializer since the allocation cost is not cheap in hot path.
210 RefPtr
<nsRange
> range
= nsRange::Create(aStartBoundary
.Container());
211 aRv
= range
->SetStartAndEnd(aStartBoundary
, aEndBoundary
);
212 if (NS_WARN_IF(aRv
.Failed())) {
215 return range
.forget();
219 * When a new boundary is given to a nsRange, compare its position with other
220 * existing boundaries to see if we need to collapse the end points.
222 * aRange: The nsRange that aNewBoundary is being set to.
223 * aNewRoot: The shadow-including root of the container of aNewBoundary
224 * aNewBoundary: The new boundary
225 * aIsSetStart: true if ShouldCollapseBoundary is called by nsRange::SetStart,
227 * aAllowCrossShadowBoundary: Indicates whether the boundaries allowed to cross
228 * shadow boundary or not
230 static CollapsePolicy
ShouldCollapseBoundary(
231 const nsRange
* aRange
, const nsINode
* aNewRoot
,
232 const RawRangeBoundary
& aNewBoundary
, const bool aIsSetStart
,
233 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary
) {
234 if (!aRange
->IsPositioned()) {
235 return CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
;
238 MOZ_ASSERT(aRange
->GetRoot());
239 if (aNewRoot
!= aRange
->GetRoot()) {
240 // Boundaries are in different document (or not connected), so collapse
241 // the both the default range and the crossBoundaryRange range.
242 if (aNewRoot
->GetComposedDoc() != aRange
->GetRoot()->GetComposedDoc()) {
243 return CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
;
246 // Always collapse both ranges if the one of the roots is an UA widget
247 // regardless whether the boundaries are allowed to cross shadow boundary
249 if (AbstractRange::IsRootUAWidget(aNewRoot
) ||
250 AbstractRange::IsRootUAWidget(aRange
->GetRoot())) {
251 return CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
;
254 // Different root, but same document. So we only collapse the
255 // default range if boundaries are allowed to cross shadow boundary.
256 return aAllowCrossShadowBoundary
== AllowRangeCrossShadowBoundary::Yes
257 ? CollapsePolicy::DefaultRange
258 : CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
;
261 const RangeBoundary
& otherSideExistingBoundary
=
262 aIsSetStart
? aRange
->EndRef() : aRange
->StartRef();
264 // Both bondaries are in the same root, now check for their position
265 const Maybe
<int32_t> order
=
266 aIsSetStart
? nsContentUtils::ComparePoints(aNewBoundary
,
267 otherSideExistingBoundary
)
268 : nsContentUtils::ComparePoints(otherSideExistingBoundary
,
273 // aNewBoundary is at a valid position.
275 // If aIsSetStart is true, this means
276 // aNewBoundary <= otherSideExistingBoundary which is
277 // good because aNewBoundary intends to be the start.
279 // If aIsSetStart is false, this means
280 // otherSideExistingBoundary <= aNewBoundary which is good because
281 // aNewBoundary intends to be the end.
283 // So no collapse for above cases.
284 return CollapsePolicy::No
;
287 if (!aRange
->MayCrossShadowBoundary() ||
288 aAllowCrossShadowBoundary
== AllowRangeCrossShadowBoundary::No
) {
289 return CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
;
292 const RangeBoundary
& otherSideExistingCrossShadowBoundaryBoundary
=
293 aIsSetStart
? aRange
->MayCrossShadowBoundaryEndRef()
294 : aRange
->MayCrossShadowBoundaryStartRef();
296 // Please see the comment for (*order != 1) to see what "valid" means.
298 // We reach to this line when (*order == 1), it means aNewBoundary is
299 // at an invalid position, so we need to collapse aNewBoundary with
300 // otherSideExistingBoundary. However, it's possible that aNewBoundary
301 // is valid with the otherSideExistingCrossShadowBoundaryBoundary.
302 const Maybe
<int32_t> withCrossShadowBoundaryOrder
=
304 ? nsContentUtils::ComparePoints(
305 aNewBoundary
, otherSideExistingCrossShadowBoundaryBoundary
)
306 : nsContentUtils::ComparePoints(
307 otherSideExistingCrossShadowBoundaryBoundary
, aNewBoundary
);
309 // Valid to the cross boundary boundary.
310 if (withCrossShadowBoundaryOrder
&& *withCrossShadowBoundaryOrder
!= 1) {
311 return CollapsePolicy::DefaultRange
;
314 // Not valid to both existing boundaries.
315 return CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
;
318 MOZ_ASSERT_UNREACHABLE();
319 return CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
;
321 /******************************************************
323 ******************************************************/
325 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange
)
326 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_INTERRUPTABLE_LAST_RELEASE(
327 nsRange
, DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr),
328 MaybeInterruptLastRelease())
330 // QueryInterface implementation for nsRange
331 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange
)
332 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
333 NS_INTERFACE_MAP_END_INHERITING(AbstractRange
)
335 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange
)
337 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsRange
, AbstractRange
)
338 // `Reset()` unlinks `mStart`, `mEnd` and `mRoot`.
339 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrossShadowBoundaryRange
);
341 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
343 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsRange
, AbstractRange
)
344 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot
)
345 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrossShadowBoundaryRange
);
346 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
348 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsRange
, AbstractRange
)
349 NS_IMPL_CYCLE_COLLECTION_TRACE_END
351 bool nsRange::MaybeInterruptLastRelease() {
352 bool interrupt
= AbstractRange::MaybeCacheToReuse(*this);
353 ResetCrossShadowBoundaryRange();
354 MOZ_ASSERT(!interrupt
|| IsCleared());
358 void nsRange::AdjustNextRefsOnCharacterDataSplit(
359 const nsIContent
& aContent
, const CharacterDataChangeInfo
& aInfo
) {
360 // If the splitted text node is immediately before a range boundary point
361 // that refers to a child index (i.e. its parent is the boundary container)
362 // then we need to adjust the corresponding boundary to account for the new
363 // text node that will be inserted. However, because the new sibling hasn't
364 // been inserted yet, that would result in an invalid boundary. Therefore,
365 // we store the new child in mNext*Ref to make sure we adjust the boundary
366 // in the next ContentInserted or ContentAppended call.
367 nsINode
* parentNode
= aContent
.GetParentNode();
368 if (parentNode
== mEnd
.Container()) {
369 if (&aContent
== mEnd
.Ref()) {
370 MOZ_ASSERT(aInfo
.mDetails
->mNextSibling
);
371 mNextEndRef
= aInfo
.mDetails
->mNextSibling
;
375 if (parentNode
== mStart
.Container()) {
376 if (&aContent
== mStart
.Ref()) {
377 MOZ_ASSERT(aInfo
.mDetails
->mNextSibling
);
378 mNextStartRef
= aInfo
.mDetails
->mNextSibling
;
383 nsRange::RangeBoundariesAndRoot
384 nsRange::DetermineNewRangeBoundariesAndRootOnCharacterDataMerge(
385 nsIContent
* aContent
, const CharacterDataChangeInfo
& aInfo
) const {
386 RawRangeBoundary newStart
;
387 RawRangeBoundary newEnd
;
388 nsINode
* newRoot
= nullptr;
390 // normalize(), aInfo.mDetails->mNextSibling is the merged text node
391 // that will be removed
392 nsIContent
* removed
= aInfo
.mDetails
->mNextSibling
;
393 if (removed
== mStart
.Container()) {
394 CheckedUint32 newStartOffset
{
395 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
)};
396 newStartOffset
+= aInfo
.mChangeStart
;
398 // newStartOffset.isValid() isn't checked explicitly here, because
399 // newStartOffset.value() contains an assertion.
400 newStart
= {aContent
, newStartOffset
.value()};
401 if (MOZ_UNLIKELY(removed
== mRoot
)) {
402 newRoot
= RangeUtils::ComputeRootNode(newStart
.Container());
405 if (removed
== mEnd
.Container()) {
406 CheckedUint32 newEndOffset
{
407 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
)};
408 newEndOffset
+= aInfo
.mChangeStart
;
410 // newEndOffset.isValid() isn't checked explicitly here, because
411 // newEndOffset.value() contains an assertion.
412 newEnd
= {aContent
, newEndOffset
.value()};
413 if (MOZ_UNLIKELY(removed
== mRoot
)) {
414 newRoot
= {RangeUtils::ComputeRootNode(newEnd
.Container())};
417 // When the removed text node's parent is one of our boundary nodes we may
418 // need to adjust the offset to account for the removed node. However,
419 // there will also be a ContentRemoved notification later so the only cases
420 // we need to handle here is when the removed node is the text node after
421 // the boundary. (The m*Offset > 0 check is an optimization - a boundary
422 // point before the first child is never affected by normalize().)
423 nsINode
* parentNode
= aContent
->GetParentNode();
424 if (parentNode
== mStart
.Container() &&
425 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) > 0 &&
426 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <
427 parentNode
->GetChildCount() &&
428 removed
== mStart
.GetChildAtOffset()) {
429 newStart
= {aContent
, aInfo
.mChangeStart
};
431 if (parentNode
== mEnd
.Container() &&
432 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) > 0 &&
433 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <
434 parentNode
->GetChildCount() &&
435 removed
== mEnd
.GetChildAtOffset()) {
436 newEnd
= {aContent
, aInfo
.mChangeEnd
};
439 return {newStart
, newEnd
, newRoot
};
442 /******************************************************
443 * nsIMutationObserver implementation
444 ******************************************************/
445 void nsRange::CharacterDataChanged(nsIContent
* aContent
,
446 const CharacterDataChangeInfo
& aInfo
) {
447 MOZ_ASSERT(aContent
);
448 MOZ_ASSERT(mIsPositioned
);
449 MOZ_ASSERT(!mNextEndRef
);
450 MOZ_ASSERT(!mNextStartRef
);
452 nsINode
* newRoot
= nullptr;
453 RawRangeBoundary newStart
;
454 RawRangeBoundary newEnd
;
456 if (aInfo
.mDetails
&&
457 aInfo
.mDetails
->mType
== CharacterDataChangeInfo::Details::eSplit
) {
458 AdjustNextRefsOnCharacterDataSplit(*aContent
, aInfo
);
461 // If the changed node contains our start boundary and the change starts
462 // before the boundary we'll need to adjust the offset.
463 if (aContent
== mStart
.Container() &&
465 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
)) {
466 if (aInfo
.mDetails
) {
467 // splitText(), aInfo->mDetails->mNextSibling is the new text node
469 aInfo
.mDetails
->mType
== CharacterDataChangeInfo::Details::eSplit
,
470 "only a split can start before the end");
472 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <=
473 aInfo
.mChangeEnd
+ 1,
474 "mStart.Offset() is beyond the end of this node");
475 const uint32_t newStartOffset
=
476 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) -
478 newStart
= {aInfo
.mDetails
->mNextSibling
, newStartOffset
};
479 if (MOZ_UNLIKELY(aContent
== mRoot
)) {
480 newRoot
= RangeUtils::ComputeRootNode(newStart
.Container());
483 bool isCommonAncestor
=
484 IsInAnySelection() && mStart
.Container() == mEnd
.Container();
485 if (isCommonAncestor
) {
486 UnregisterClosestCommonInclusiveAncestor(mStart
.Container(), false);
487 RegisterClosestCommonInclusiveAncestor(newStart
.Container());
489 if (mStart
.Container()
490 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
492 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
495 // If boundary is inside changed text, position it before change
496 // else adjust start offset for the change in length.
497 CheckedUint32 newStartOffset
{0};
498 if (*mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <=
500 newStartOffset
= aInfo
.mChangeStart
;
503 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
);
504 newStartOffset
-= aInfo
.LengthOfRemovedText();
505 newStartOffset
+= aInfo
.mReplaceLength
;
508 // newStartOffset.isValid() isn't checked explicitly here, because
509 // newStartOffset.value() contains an assertion.
510 newStart
= {mStart
.Container(), newStartOffset
.value()};
514 // Do the same thing for the end boundary, except for splitText of a node
515 // with no parent then only switch to the new node if the start boundary
516 // did so too (otherwise the range would end up with disconnected nodes).
517 if (aContent
== mEnd
.Container() &&
519 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
)) {
520 if (aInfo
.mDetails
&& (aContent
->GetParentNode() || newStart
.Container())) {
521 // splitText(), aInfo.mDetails->mNextSibling is the new text node
523 aInfo
.mDetails
->mType
== CharacterDataChangeInfo::Details::eSplit
,
524 "only a split can start before the end");
526 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <=
527 aInfo
.mChangeEnd
+ 1,
528 "mEnd.Offset() is beyond the end of this node");
530 const uint32_t newEndOffset
{
531 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) -
533 newEnd
= {aInfo
.mDetails
->mNextSibling
, newEndOffset
};
535 bool isCommonAncestor
=
536 IsInAnySelection() && mStart
.Container() == mEnd
.Container();
537 if (isCommonAncestor
&& !newStart
.Container()) {
538 // The split occurs inside the range.
539 UnregisterClosestCommonInclusiveAncestor(mStart
.Container(), false);
540 RegisterClosestCommonInclusiveAncestor(
541 mStart
.Container()->GetParentNode());
543 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
546 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
548 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
551 CheckedUint32 newEndOffset
{0};
552 if (*mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
) <=
554 newEndOffset
= aInfo
.mChangeStart
;
557 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets
);
558 newEndOffset
-= aInfo
.LengthOfRemovedText();
559 newEndOffset
+= aInfo
.mReplaceLength
;
562 // newEndOffset.isValid() isn't checked explicitly here, because
563 // newEndOffset.value() contains an assertion.
564 newEnd
= {mEnd
.Container(), newEndOffset
.value()};
568 if (aInfo
.mDetails
&&
569 aInfo
.mDetails
->mType
== CharacterDataChangeInfo::Details::eMerge
) {
570 MOZ_ASSERT(!newStart
.IsSet());
571 MOZ_ASSERT(!newEnd
.IsSet());
573 RangeBoundariesAndRoot rangeBoundariesAndRoot
=
574 DetermineNewRangeBoundariesAndRootOnCharacterDataMerge(aContent
, aInfo
);
576 newStart
= rangeBoundariesAndRoot
.mStart
;
577 newEnd
= rangeBoundariesAndRoot
.mEnd
;
578 newRoot
= rangeBoundariesAndRoot
.mRoot
;
581 if (newStart
.IsSet() || newEnd
.IsSet()) {
582 if (!newStart
.IsSet()) {
583 newStart
.CopyFrom(mStart
, RangeBoundaryIsMutationObserved::Yes
);
585 if (!newEnd
.IsSet()) {
586 newEnd
.CopyFrom(mEnd
, RangeBoundaryIsMutationObserved::Yes
);
588 DoSetRange(newStart
, newEnd
, newRoot
? newRoot
: mRoot
.get(),
589 !newEnd
.Container()->GetParentNode() ||
590 !newStart
.Container()->GetParentNode());
592 nsRange::AssertIfMismatchRootAndRangeBoundaries(
594 (mStart
.IsSet() && !mStart
.Container()->GetParentNode()) ||
595 (mEnd
.IsSet() && !mEnd
.Container()->GetParentNode()));
599 void nsRange::ContentAppended(nsIContent
* aFirstNewContent
) {
600 MOZ_ASSERT(mIsPositioned
);
602 nsINode
* container
= aFirstNewContent
->GetParentNode();
603 MOZ_ASSERT(container
);
604 if (container
->IsMaybeSelected() && IsInAnySelection()) {
605 nsINode
* child
= aFirstNewContent
;
608 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
609 MarkDescendants(*child
);
611 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
613 child
= child
->GetNextSibling();
617 if (mNextStartRef
|| mNextEndRef
) {
618 // A splitText has occurred, if any mNext*Ref was set, we need to adjust
619 // the range boundaries.
621 mStart
= {mStart
.Container(), mNextStartRef
};
622 MOZ_ASSERT(mNextStartRef
== aFirstNewContent
);
623 mNextStartRef
= nullptr;
626 mEnd
= {mEnd
.Container(), mNextEndRef
};
627 MOZ_ASSERT(mNextEndRef
== aFirstNewContent
);
628 mNextEndRef
= nullptr;
630 DoSetRange(mStart
, mEnd
, mRoot
, true);
632 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart
, mEnd
, mRoot
);
636 void nsRange::ContentInserted(nsIContent
* aChild
) {
637 MOZ_ASSERT(mIsPositioned
);
639 bool updateBoundaries
= false;
640 nsINode
* container
= aChild
->GetParentNode();
641 MOZ_ASSERT(container
);
642 RawRangeBoundary
newStart(mStart
, RangeBoundaryIsMutationObserved::Yes
);
643 RawRangeBoundary
newEnd(mEnd
, RangeBoundaryIsMutationObserved::Yes
);
644 MOZ_ASSERT(aChild
->GetParentNode() == container
);
646 // Invalidate boundary offsets if a child that may have moved them was
648 if (container
== mStart
.Container()) {
649 newStart
.InvalidateOffset();
650 updateBoundaries
= true;
653 if (container
== mEnd
.Container()) {
654 newEnd
.InvalidateOffset();
655 updateBoundaries
= true;
658 if (container
->IsMaybeSelected() &&
660 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
661 MarkDescendants(*aChild
);
662 aChild
->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
665 if (mNextStartRef
|| mNextEndRef
) {
667 newStart
= {mStart
.Container(), mNextStartRef
};
668 MOZ_ASSERT(mNextStartRef
== aChild
);
669 mNextStartRef
= nullptr;
672 newEnd
= {mEnd
.Container(), mNextEndRef
};
673 MOZ_ASSERT(mNextEndRef
== aChild
);
674 mNextEndRef
= nullptr;
677 updateBoundaries
= true;
680 if (updateBoundaries
) {
681 DoSetRange(newStart
, newEnd
, mRoot
);
683 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart
, mEnd
, mRoot
);
687 void nsRange::ContentRemoved(nsIContent
* aChild
, nsIContent
* aPreviousSibling
) {
688 MOZ_ASSERT(mIsPositioned
);
690 nsINode
* container
= aChild
->GetParentNode();
691 MOZ_ASSERT(container
);
693 nsINode
* startContainer
= mStart
.Container();
694 nsINode
* endContainer
= mEnd
.Container();
696 // FIXME(sefeng): Temporary Solution for ContentRemoved
697 // editing/crashtests/removeformat-from-DOMNodeRemoved.html can be used to
699 if (mCrossShadowBoundaryRange
) {
700 if (mCrossShadowBoundaryRange
->GetStartContainer() == aChild
||
701 mCrossShadowBoundaryRange
->GetEndContainer() == aChild
) {
702 ResetCrossShadowBoundaryRange();
703 } else if (ShadowRoot
* shadowRoot
= aChild
->GetShadowRoot()) {
704 if (mCrossShadowBoundaryRange
->GetStartContainer() == shadowRoot
||
705 mCrossShadowBoundaryRange
->GetEndContainer() == shadowRoot
) {
706 ResetCrossShadowBoundaryRange();
711 RawRangeBoundary newStart
;
712 RawRangeBoundary newEnd
;
713 Maybe
<bool> gravitateStart
;
716 // Adjust position if a sibling was removed...
717 if (container
== startContainer
) {
718 // We're only interested if our boundary reference was removed, otherwise
719 // we can just invalidate the offset.
720 if (aChild
== mStart
.Ref()) {
721 newStart
= {container
, aPreviousSibling
};
723 newStart
.CopyFrom(mStart
, RangeBoundaryIsMutationObserved::Yes
);
724 newStart
.InvalidateOffset();
727 gravitateStart
= Some(startContainer
->IsInclusiveDescendantOf(aChild
));
728 if (gravitateStart
.value()) {
729 newStart
= {container
, aPreviousSibling
};
733 // Do same thing for end boundry.
734 if (container
== endContainer
) {
735 if (aChild
== mEnd
.Ref()) {
736 newEnd
= {container
, aPreviousSibling
};
738 newEnd
.CopyFrom(mEnd
, RangeBoundaryIsMutationObserved::Yes
);
739 newEnd
.InvalidateOffset();
742 if (startContainer
== endContainer
&& gravitateStart
.isSome()) {
743 gravitateEnd
= gravitateStart
.value();
745 gravitateEnd
= endContainer
->IsInclusiveDescendantOf(aChild
);
748 newEnd
= {container
, aPreviousSibling
};
752 bool newStartIsSet
= newStart
.IsSet();
753 bool newEndIsSet
= newEnd
.IsSet();
754 if (newStartIsSet
|| newEndIsSet
) {
755 DoSetRange(newStartIsSet
? newStart
: mStart
.AsRaw(),
756 newEndIsSet
? newEnd
: mEnd
.AsRaw(), mRoot
);
758 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart
, mEnd
, mRoot
);
761 MOZ_ASSERT(mStart
.Ref() != aChild
);
762 MOZ_ASSERT(mEnd
.Ref() != aChild
);
764 if (container
->IsMaybeSelected() &&
766 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
768 ->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
769 UnmarkDescendants(*aChild
);
773 void nsRange::ParentChainChanged(nsIContent
* aContent
) {
774 NS_ASSERTION(mRoot
== aContent
, "Wrong ParentChainChanged notification?");
775 nsINode
* newRoot
= RangeUtils::ComputeRootNode(mStart
.Container());
776 NS_ASSERTION(newRoot
, "No valid boundary or root found!");
777 if (newRoot
!= RangeUtils::ComputeRootNode(mEnd
.Container())) {
778 // Sometimes ordering involved in cycle collection can lead to our
779 // start parent and/or end parent being disconnected from our root
780 // without our getting a ContentRemoved notification.
781 // See bug 846096 for more details.
782 NS_ASSERTION(mEnd
.Container()->IsInNativeAnonymousSubtree(),
783 "This special case should happen only with "
784 "native-anonymous content");
785 // When that happens, bail out and set pointers to null; since we're
786 // in cycle collection and unreachable it shouldn't matter.
790 // This is safe without holding a strong ref to self as long as the change
791 // of mRoot is the last thing in DoSetRange.
792 DoSetRange(mStart
, mEnd
, newRoot
);
795 bool nsRange::IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
796 const nsINode
& aContainer
) const {
797 MOZ_ASSERT(mCrossShadowBoundaryRange
&&
798 mCrossShadowBoundaryRange
->GetCommonAncestor());
799 return aContainer
.IsShadowIncludingInclusiveDescendantOf(
800 mCrossShadowBoundaryRange
->GetCommonAncestor());
803 bool nsRange::IsPointComparableToRange(const nsINode
& aContainer
,
805 bool aAllowCrossShadowBoundary
,
806 ErrorResult
& aRv
) const {
807 // our range is in a good state?
808 if (!mIsPositioned
) {
809 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
813 const bool isContainerInRange
=
814 aContainer
.IsInclusiveDescendantOf(mRoot
) ||
815 (aAllowCrossShadowBoundary
&& mCrossShadowBoundaryRange
&&
816 IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
819 if (!isContainerInRange
) {
820 // TODO(emilio): Switch to ThrowWrongDocumentError, but IsPointInRange
821 // relies on the error code right now in order to suppress the exception.
822 aRv
.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR
);
826 auto chromeOnlyAccess
= mStart
.Container()->ChromeOnlyAccess();
827 NS_ASSERTION(chromeOnlyAccess
== mEnd
.Container()->ChromeOnlyAccess(),
828 "Start and end of a range must be either both native anonymous "
830 if (aContainer
.ChromeOnlyAccess() != chromeOnlyAccess
) {
831 aRv
.ThrowInvalidNodeTypeError(
832 "Trying to compare restricted with unrestricted nodes");
836 if (aContainer
.NodeType() == nsINode::DOCUMENT_TYPE_NODE
) {
837 aRv
.ThrowInvalidNodeTypeError("Trying to compare with a document");
841 if (aOffset
> aContainer
.Length()) {
842 aRv
.ThrowIndexSizeError("Offset is out of bounds");
849 bool nsRange::IsPointInRange(const nsINode
& aContainer
, uint32_t aOffset
,
851 bool aAllowCrossShadowBoundary
) const {
852 int16_t compareResult
=
853 ComparePoint(aContainer
, aOffset
, aRv
, aAllowCrossShadowBoundary
);
854 // If the node isn't in the range's document, it clearly isn't in the range.
855 if (aRv
.ErrorCodeIs(NS_ERROR_DOM_WRONG_DOCUMENT_ERR
)) {
856 aRv
.SuppressException();
860 return compareResult
== 0;
863 int16_t nsRange::ComparePoint(const nsINode
& aContainer
, uint32_t aOffset
,
865 bool aAllowCrossShadowBoundary
) const {
866 if (!IsPointComparableToRange(aContainer
, aOffset
, aAllowCrossShadowBoundary
,
871 const RawRangeBoundary point
{const_cast<nsINode
*>(&aContainer
), aOffset
};
873 MOZ_ASSERT(point
.IsSetAndValid());
875 if (Maybe
<int32_t> order
= nsContentUtils::ComparePoints(
876 point
, aAllowCrossShadowBoundary
? MayCrossShadowBoundaryStartRef()
878 order
&& *order
<= 0) {
879 return int16_t(*order
);
881 if (Maybe
<int32_t> order
= nsContentUtils::ComparePoints(
882 aAllowCrossShadowBoundary
? MayCrossShadowBoundaryEndRef() : EndRef(),
884 order
&& *order
== -1) {
890 bool nsRange::IntersectsNode(nsINode
& aNode
, ErrorResult
& aRv
) {
891 if (!mIsPositioned
) {
892 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
896 nsINode
* parent
= aNode
.GetParentNode();
898 // |parent| is null, so |node|'s root is |node| itself.
899 return GetRoot() == &aNode
;
902 const Maybe
<uint32_t> nodeIndex
= parent
->ComputeIndexOf(&aNode
);
903 if (nodeIndex
.isNothing()) {
907 if (!IsPointComparableToRange(*parent
, *nodeIndex
,
908 false /* aAllowCrossShadowBoundary */,
913 const Maybe
<int32_t> startOrder
= nsContentUtils::ComparePoints(
915 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
), parent
,
917 if (startOrder
&& (*startOrder
< 0)) {
918 const Maybe
<int32_t> endOrder
= nsContentUtils::ComparePoints(
919 parent
, *nodeIndex
, mEnd
.Container(),
920 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
));
921 return endOrder
&& (*endOrder
< 0);
927 void nsRange::NotifySelectionListenersAfterRangeSet() {
928 if (mSelections
.IsEmpty()) {
932 // Our internal code should not move focus with using this instance while
933 // it's calling Selection::NotifySelectionListeners() which may move focus
934 // or calls selection listeners. So, let's set mCalledByJS to false here
935 // since non-*JS() methods don't set it to false.
936 AutoCalledByJSRestore
calledByJSRestorer(*this);
939 // If this instance is not a proper range for selection, we need to remove
940 // this from selections.
941 const Document
* const docForSelf
=
942 mStart
.Container() ? mStart
.Container()->GetComposedDoc() : nullptr;
943 const nsFrameSelection
* const frameSelection
=
944 mSelections
[0]->GetFrameSelection();
945 const Document
* const docForSelection
=
946 frameSelection
&& frameSelection
->GetPresShell()
947 ? frameSelection
->GetPresShell()->GetDocument()
949 if (!IsPositioned() || docForSelf
!= docForSelection
) {
950 // XXX Why Selection::RemoveRangeAndUnselectFramesAndNotifyListeners() does
951 // not set whether the caller is JS or not?
952 if (IsPartOfOneSelectionOnly()) {
953 RefPtr
<Selection
> selection
= mSelections
[0].get();
954 selection
->RemoveRangeAndUnselectFramesAndNotifyListeners(*this,
957 nsTArray
<WeakPtr
<Selection
>> copiedSelections
= mSelections
.Clone();
958 for (const auto& weakSelection
: copiedSelections
) {
959 RefPtr
<Selection
> selection
= weakSelection
.get();
960 if (MOZ_LIKELY(selection
)) {
961 selection
->RemoveRangeAndUnselectFramesAndNotifyListeners(
962 *this, IgnoreErrors());
966 // FYI: NotifySelectionListeners() should be called by
967 // RemoveRangeAndUnselectFramesAndNotifyListeners() if it's required.
968 // Therefore, we need to do nothing anymore.
972 // Notify all Selections. This may modify the range,
973 // remove it from the selection, or the selection itself may have gone after
974 // the call. Also, new selections may be added.
975 // To ensure that listeners are notified for all *current* selections,
976 // create a copy of the list of selections and use that for iterating. This
977 // way selections can be added or removed safely during iteration.
978 // To save allocation cost, the copy is only created if there is more than
979 // one Selection present (which will barely ever be the case).
980 if (IsPartOfOneSelectionOnly()) {
981 RefPtr
<Selection
> selection
= mSelections
[0].get();
982 selection
->NotifySelectionListeners(calledByJSRestorer
.SavedValue());
984 nsTArray
<WeakPtr
<Selection
>> copiedSelections
= mSelections
.Clone();
985 for (const auto& weakSelection
: copiedSelections
) {
986 RefPtr
<Selection
> selection
= weakSelection
.get();
987 if (MOZ_LIKELY(selection
)) {
988 selection
->NotifySelectionListeners(calledByJSRestorer
.SavedValue());
994 /******************************************************
995 * Private helper routines
996 ******************************************************/
999 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
1000 void nsRange::AssertIfMismatchRootAndRangeBoundaries(
1001 const RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
1002 const RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
, const nsINode
* aRootNode
,
1003 bool aNotInsertedYet
/* = false */) {
1006 MOZ_ASSERT(!aStartBoundary
.IsSet());
1007 MOZ_ASSERT(!aEndBoundary
.IsSet());
1011 MOZ_ASSERT(aStartBoundary
.IsSet());
1012 MOZ_ASSERT(aEndBoundary
.IsSet());
1013 if (!aNotInsertedYet
) {
1014 // Compute temporary root for given range boundaries. If a range in native
1015 // anonymous subtree is being removed, tempRoot may return the fragment's
1016 // root content, but it shouldn't be used for new root node because the node
1017 // may be bound to the root element again.
1018 nsINode
* tempRoot
= RangeUtils::ComputeRootNode(aStartBoundary
.Container());
1019 // The new range should be in the temporary root node at least.
1020 MOZ_ASSERT(tempRoot
==
1021 RangeUtils::ComputeRootNode(aEndBoundary
.Container()));
1022 MOZ_ASSERT(aStartBoundary
.Container()->IsInclusiveDescendantOf(tempRoot
));
1023 MOZ_ASSERT(aEndBoundary
.Container()->IsInclusiveDescendantOf(tempRoot
));
1024 // If the new range is not disconnected or not in native anonymous subtree,
1025 // the temporary root must be same as the new root node. Otherwise,
1026 // aRootNode should be the parent of root of the NAC (e.g., `<input>` if the
1027 // range is in NAC under `<input>`), but tempRoot is now root content node
1028 // of the disconnected subtree (e.g., `<div>` element in `<input>` element).
1029 const bool tempRootIsDisconnectedNAC
=
1030 tempRoot
->IsInNativeAnonymousSubtree() && !tempRoot
->GetParentNode();
1031 MOZ_ASSERT_IF(!tempRootIsDisconnectedNAC
, tempRoot
== aRootNode
);
1033 MOZ_ASSERT(aRootNode
->IsDocument() || aRootNode
->IsAttr() ||
1034 aRootNode
->IsDocumentFragment() || aRootNode
->IsContent());
1035 #endif // #ifdef DEBUG
1038 // It's important that all setting of the range start/end points
1039 // go through this function, which will do all the right voodoo
1040 // for content notification of range ownership.
1041 // Calling DoSetRange with either parent argument null will collapse
1042 // the range to have both endpoints point to the other node
1043 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
1044 void nsRange::DoSetRange(
1045 const RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
1046 const RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
, nsINode
* aRootNode
,
1047 bool aNotInsertedYet
/* = false */,
1049 aCollapsePolicy
/* = DEFAULT_RANGE_AND_CROSS_BOUNDARY_RANGES */) {
1050 mIsPositioned
= aStartBoundary
.IsSetAndValid() &&
1051 aEndBoundary
.IsSetAndValid() && aRootNode
;
1052 MOZ_ASSERT_IF(!mIsPositioned
, !aStartBoundary
.IsSet());
1053 MOZ_ASSERT_IF(!mIsPositioned
, !aEndBoundary
.IsSet());
1054 MOZ_ASSERT_IF(!mIsPositioned
, !aRootNode
);
1056 nsRange::AssertIfMismatchRootAndRangeBoundaries(aStartBoundary
, aEndBoundary
,
1057 aRootNode
, aNotInsertedYet
);
1059 if (mRoot
!= aRootNode
) {
1061 mRoot
->RemoveMutationObserver(this);
1064 aRootNode
->AddMutationObserver(this);
1067 bool checkCommonAncestor
=
1068 (mStart
.Container() != aStartBoundary
.Container() ||
1069 mEnd
.Container() != aEndBoundary
.Container()) &&
1070 IsInAnySelection() && !aNotInsertedYet
;
1072 // GetClosestCommonInclusiveAncestor is unreliable while we're unlinking
1073 // (could return null if our start/end have already been unlinked), so make
1074 // sure to not use it here to determine our "old" current ancestor.
1075 mStart
.CopyFrom(aStartBoundary
, RangeBoundaryIsMutationObserved::Yes
);
1076 mEnd
.CopyFrom(aEndBoundary
, RangeBoundaryIsMutationObserved::Yes
);
1078 if (aCollapsePolicy
==
1079 CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
) {
1080 ResetCrossShadowBoundaryRange();
1083 if (checkCommonAncestor
) {
1084 UpdateCommonAncestorIfNecessary();
1087 // This needs to be the last thing this function does, other than notifying
1088 // selection listeners. See comment in ParentChainChanged.
1089 if (mRoot
!= aRootNode
) {
1093 // Notify any selection listeners. This has to occur last because otherwise
1094 // the world could be observed by a selection listener while the range was in
1095 // an invalid state. So we run it off of a script runner to ensure it runs
1096 // after the mutation observers have finished running.
1097 if (!mSelections
.IsEmpty()) {
1098 if (MOZ_LOG_TEST(sSelectionAPILog
, LogLevel::Info
)) {
1099 for (const auto& selection
: mSelections
) {
1100 if (selection
&& selection
->Type() == SelectionType::eNormal
) {
1101 LogSelectionAPI(selection
, __FUNCTION__
, "aStartBoundary",
1102 aStartBoundary
, "aEndBoundary", aEndBoundary
,
1103 "aNotInsertedYet", aNotInsertedYet
);
1104 LogStackForSelectionAPI();
1108 nsContentUtils::AddScriptRunner(
1109 NewRunnableMethod("NotifySelectionListenersAfterRangeSet", this,
1110 &nsRange::NotifySelectionListenersAfterRangeSet
));
1114 void nsRange::Reset() {
1115 DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
1118 /******************************************************
1119 * public functionality
1120 ******************************************************/
1122 void nsRange::SetStartJS(nsINode
& aNode
, uint32_t aOffset
, ErrorResult
& aErr
) {
1123 AutoCalledByJSRestore
calledByJSRestorer(*this);
1125 SetStart(aNode
, aOffset
, aErr
);
1128 bool nsRange::CanAccess(const nsINode
& aNode
) const {
1129 if (nsContentUtils::LegacyIsCallerNativeCode()) {
1132 return nsContentUtils::CanCallerAccess(&aNode
);
1135 void nsRange::SetStart(
1136 nsINode
& aNode
, uint32_t aOffset
, ErrorResult
& aRv
,
1137 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary
) {
1138 if (!CanAccess(aNode
)) {
1139 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1143 AutoInvalidateSelection
atEndOfBlock(this);
1144 SetStart(RawRangeBoundary(&aNode
, aOffset
), aRv
, aAllowCrossShadowBoundary
);
1147 void nsRange::SetStart(
1148 const RawRangeBoundary
& aPoint
, ErrorResult
& aRv
,
1149 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary
) {
1150 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aPoint
.Container());
1152 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1156 if (!aPoint
.IsSetAndValid()) {
1157 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
1161 CollapsePolicy policy
=
1162 ShouldCollapseBoundary(this, newRoot
, aPoint
, true /* aIsSetStart= */,
1163 aAllowCrossShadowBoundary
);
1166 case CollapsePolicy::No
:
1167 // EndRef(..) may be same as mStart or not, depends on
1168 // the value of mCrossShadowBoundaryRange->mEnd, We need to update
1169 // mCrossShadowBoundaryRange and the default boundaries separately
1170 if (aAllowCrossShadowBoundary
== AllowRangeCrossShadowBoundary::Yes
) {
1171 if (MayCrossShadowBoundaryEndRef() != mEnd
) {
1172 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
1173 aPoint
, MayCrossShadowBoundaryEndRef());
1175 // The normal range is good enough for this case, just use that.
1176 ResetCrossShadowBoundaryRange();
1179 DoSetRange(aPoint
, mEnd
, mRoot
, false, policy
);
1181 case CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
:
1182 DoSetRange(aPoint
, aPoint
, newRoot
, false, policy
);
1184 case CollapsePolicy::DefaultRange
:
1185 MOZ_ASSERT(aAllowCrossShadowBoundary
==
1186 AllowRangeCrossShadowBoundary::Yes
);
1187 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
1188 aPoint
, MayCrossShadowBoundaryEndRef());
1189 DoSetRange(aPoint
, aPoint
, newRoot
, false, policy
);
1192 MOZ_ASSERT_UNREACHABLE();
1196 void nsRange::SetStartAllowCrossShadowBoundary(nsINode
& aNode
, uint32_t aOffset
,
1197 ErrorResult
& aErr
) {
1198 AutoCalledByJSRestore
calledByJSRestorer(*this);
1200 SetStart(aNode
, aOffset
, aErr
, AllowRangeCrossShadowBoundary::Yes
);
1203 void nsRange::SetStartBeforeJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1204 AutoCalledByJSRestore
calledByJSRestorer(*this);
1206 SetStartBefore(aNode
, aErr
);
1209 void nsRange::SetStartBefore(
1210 nsINode
& aNode
, ErrorResult
& aRv
,
1211 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary
) {
1212 if (!CanAccess(aNode
)) {
1213 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1217 AutoInvalidateSelection
atEndOfBlock(this);
1218 // If the node is being removed from its parent, GetRawRangeBoundaryBefore()
1219 // returns unset instance. Then, SetStart() will throw
1220 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1221 SetStart(RangeUtils::GetRawRangeBoundaryBefore(&aNode
), aRv
,
1222 aAllowCrossShadowBoundary
);
1225 void nsRange::SetStartAfterJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1226 AutoCalledByJSRestore
calledByJSRestorer(*this);
1228 SetStartAfter(aNode
, aErr
);
1231 void nsRange::SetStartAfter(nsINode
& aNode
, ErrorResult
& aRv
) {
1232 if (!CanAccess(aNode
)) {
1233 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1237 AutoInvalidateSelection
atEndOfBlock(this);
1238 // If the node is being removed from its parent, GetRawRangeBoundaryAfter()
1239 // returns unset instance. Then, SetStart() will throw
1240 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1241 SetStart(RangeUtils::GetRawRangeBoundaryAfter(&aNode
), aRv
);
1244 void nsRange::SetEndJS(nsINode
& aNode
, uint32_t aOffset
, ErrorResult
& aErr
) {
1245 AutoCalledByJSRestore
calledByJSRestorer(*this);
1247 SetEnd(aNode
, aOffset
, aErr
);
1250 void nsRange::SetEnd(nsINode
& aNode
, uint32_t aOffset
, ErrorResult
& aRv
,
1251 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary
) {
1252 if (!CanAccess(aNode
)) {
1253 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1256 AutoInvalidateSelection
atEndOfBlock(this);
1257 SetEnd(RawRangeBoundary(&aNode
, aOffset
), aRv
, aAllowCrossShadowBoundary
);
1260 void nsRange::SetEnd(const RawRangeBoundary
& aPoint
, ErrorResult
& aRv
,
1261 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary
) {
1262 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aPoint
.Container());
1264 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1268 if (!aPoint
.IsSetAndValid()) {
1269 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
1273 CollapsePolicy policy
=
1274 ShouldCollapseBoundary(this, newRoot
, aPoint
, false /* aIsStartStart */,
1275 aAllowCrossShadowBoundary
);
1278 case CollapsePolicy::No
:
1279 // StartRef(..) may be same as mStart or not, depends on
1280 // the value of mCrossShadowBoundaryRange->mStart, so we need to update
1281 // mCrossShadowBoundaryRange and the default boundaries separately
1282 if (aAllowCrossShadowBoundary
== AllowRangeCrossShadowBoundary::Yes
) {
1283 if (MayCrossShadowBoundaryStartRef() != mStart
) {
1284 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
1285 MayCrossShadowBoundaryStartRef(), aPoint
);
1287 // The normal range is good enough for this case, just use that.
1288 ResetCrossShadowBoundaryRange();
1291 DoSetRange(mStart
, aPoint
, mRoot
, false, policy
);
1293 case CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges
:
1294 DoSetRange(aPoint
, aPoint
, newRoot
, false, policy
);
1296 case CollapsePolicy::DefaultRange
:
1297 MOZ_ASSERT(aAllowCrossShadowBoundary
==
1298 AllowRangeCrossShadowBoundary::Yes
);
1299 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
1300 MayCrossShadowBoundaryStartRef(), aPoint
);
1301 DoSetRange(aPoint
, aPoint
, newRoot
, false, policy
);
1304 MOZ_ASSERT_UNREACHABLE();
1308 void nsRange::SetEndAllowCrossShadowBoundary(nsINode
& aNode
, uint32_t aOffset
,
1309 ErrorResult
& aErr
) {
1310 AutoCalledByJSRestore
calledByJSRestorer(*this);
1312 SetEnd(aNode
, aOffset
, aErr
,
1313 AllowRangeCrossShadowBoundary::Yes
/* aAllowCrossShadowBoundary */);
1316 void nsRange::SelectNodesInContainer(nsINode
* aContainer
,
1317 nsIContent
* aStartContent
,
1318 nsIContent
* aEndContent
) {
1319 MOZ_ASSERT(aContainer
);
1320 MOZ_ASSERT(aContainer
->ComputeIndexOf(aStartContent
).valueOr(0) <=
1321 aContainer
->ComputeIndexOf(aEndContent
).valueOr(0));
1322 MOZ_ASSERT(aStartContent
&&
1323 aContainer
->ComputeIndexOf(aStartContent
).isSome());
1324 MOZ_ASSERT(aEndContent
&& aContainer
->ComputeIndexOf(aEndContent
).isSome());
1326 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aContainer
);
1327 MOZ_ASSERT(newRoot
);
1332 RawRangeBoundary
start(aContainer
, aStartContent
->GetPreviousSibling());
1333 RawRangeBoundary
end(aContainer
, aEndContent
);
1334 DoSetRange(start
, end
, newRoot
);
1337 void nsRange::SetEndBeforeJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1338 AutoCalledByJSRestore
calledByJSRestorer(*this);
1340 SetEndBefore(aNode
, aErr
);
1343 void nsRange::SetEndBefore(
1344 nsINode
& aNode
, ErrorResult
& aRv
,
1345 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary
) {
1346 if (!CanAccess(aNode
)) {
1347 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1351 AutoInvalidateSelection
atEndOfBlock(this);
1352 // If the node is being removed from its parent, GetRawRangeBoundaryBefore()
1353 // returns unset instance. Then, SetEnd() will throw
1354 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1355 SetEnd(RangeUtils::GetRawRangeBoundaryBefore(&aNode
), aRv
,
1356 aAllowCrossShadowBoundary
);
1359 void nsRange::SetEndAfterJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1360 AutoCalledByJSRestore
calledByJSRestorer(*this);
1362 SetEndAfter(aNode
, aErr
);
1365 void nsRange::SetEndAfter(nsINode
& aNode
, ErrorResult
& aRv
) {
1366 if (!CanAccess(aNode
)) {
1367 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1371 AutoInvalidateSelection
atEndOfBlock(this);
1372 // If the node is being removed from its parent, GetRawRangeBoundaryAfter()
1373 // returns unset instance. Then, SetEnd() will throw
1374 // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1375 SetEnd(RangeUtils::GetRawRangeBoundaryAfter(&aNode
), aRv
);
1378 void nsRange::Collapse(bool aToStart
) {
1379 if (!mIsPositioned
) return;
1381 AutoInvalidateSelection
atEndOfBlock(this);
1383 DoSetRange(mStart
, mStart
, mRoot
);
1385 DoSetRange(mEnd
, mEnd
, mRoot
);
1389 void nsRange::CollapseJS(bool aToStart
) {
1390 AutoCalledByJSRestore
calledByJSRestorer(*this);
1395 void nsRange::SelectNodeJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1396 AutoCalledByJSRestore
calledByJSRestorer(*this);
1398 SelectNode(aNode
, aErr
);
1401 void nsRange::SelectNode(nsINode
& aNode
, ErrorResult
& aRv
) {
1402 if (!CanAccess(aNode
)) {
1403 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1407 nsINode
* container
= aNode
.GetParentNode();
1408 nsINode
* newRoot
= RangeUtils::ComputeRootNode(container
);
1410 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1414 const Maybe
<uint32_t> index
= container
->ComputeIndexOf(&aNode
);
1415 // MOZ_ASSERT(index.isSome());
1416 // We need to compute the index here unfortunately, because, while we have
1417 // support for XBL, |container| may be the node's binding parent without
1418 // actually containing it.
1419 if (MOZ_UNLIKELY(NS_WARN_IF(index
.isNothing()))) {
1420 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1424 AutoInvalidateSelection
atEndOfBlock(this);
1425 DoSetRange(RawRangeBoundary
{container
, *index
},
1426 RawRangeBoundary
{container
, *index
+ 1u}, newRoot
);
1429 void nsRange::SelectNodeContentsJS(nsINode
& aNode
, ErrorResult
& aErr
) {
1430 AutoCalledByJSRestore
calledByJSRestorer(*this);
1432 SelectNodeContents(aNode
, aErr
);
1435 void nsRange::SelectNodeContents(nsINode
& aNode
, ErrorResult
& aRv
) {
1436 if (!CanAccess(aNode
)) {
1437 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1441 nsINode
* newRoot
= RangeUtils::ComputeRootNode(&aNode
);
1443 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
1447 AutoInvalidateSelection
atEndOfBlock(this);
1448 DoSetRange(RawRangeBoundary(&aNode
, 0u),
1449 RawRangeBoundary(&aNode
, aNode
.Length()), newRoot
);
1452 // The Subtree Content Iterator only returns subtrees that are
1453 // completely within a given range. It doesn't return a CharacterData
1454 // node that contains either the start or end point of the range.,
1455 // nor does it return element nodes when nothing in the element is selected.
1456 // We need an iterator that will also include these start/end points
1457 // so that our methods/algorithms aren't cluttered with special
1458 // case code that tries to include these points while iterating.
1460 // The RangeSubtreeIterator class mimics the ContentSubtreeIterator
1461 // methods we need, so should the Content Iterator support the
1462 // start/end points in the future, we can switchover relatively
1465 class MOZ_STACK_CLASS RangeSubtreeIterator
{
1467 enum RangeSubtreeIterState
{ eDone
= 0, eUseStart
, eUseIterator
, eUseEnd
};
1469 Maybe
<ContentSubtreeIterator
> mSubtreeIter
;
1470 RangeSubtreeIterState mIterState
;
1472 nsCOMPtr
<nsINode
> mStart
;
1473 nsCOMPtr
<nsINode
> mEnd
;
1476 RangeSubtreeIterator() : mIterState(eDone
) {}
1477 ~RangeSubtreeIterator() = default;
1479 nsresult
Init(nsRange
* aRange
);
1480 already_AddRefed
<nsINode
> GetCurrentNode();
1486 bool IsDone() { return mIterState
== eDone
; }
1489 nsresult
RangeSubtreeIterator::Init(nsRange
* aRange
) {
1491 if (aRange
->Collapsed()) {
1495 // Grab the start point of the range and QI it to
1496 // a CharacterData pointer. If it is CharacterData store
1497 // a pointer to the node.
1499 if (!aRange
->IsPositioned()) {
1500 return NS_ERROR_FAILURE
;
1503 nsINode
* node
= aRange
->GetStartContainer();
1504 if (NS_WARN_IF(!node
)) {
1505 return NS_ERROR_FAILURE
;
1508 if (node
->IsCharacterData() ||
1509 (node
->IsElement() &&
1510 node
->AsElement()->GetChildCount() == aRange
->StartOffset())) {
1514 // Grab the end point of the range and QI it to
1515 // a CharacterData pointer. If it is CharacterData store
1516 // a pointer to the node.
1518 node
= aRange
->GetEndContainer();
1519 if (NS_WARN_IF(!node
)) {
1520 return NS_ERROR_FAILURE
;
1523 if (node
->IsCharacterData() ||
1524 (node
->IsElement() && aRange
->EndOffset() == 0)) {
1528 if (mStart
&& mStart
== mEnd
) {
1529 // The range starts and stops in the same CharacterData
1530 // node. Null out the end pointer so we only visit the
1535 // Now create a Content Subtree Iterator to be used
1536 // for the subtrees between the end points!
1538 mSubtreeIter
.emplace();
1540 nsresult res
= mSubtreeIter
->Init(aRange
);
1541 if (NS_FAILED(res
)) return res
;
1543 if (mSubtreeIter
->IsDone()) {
1544 // The subtree iterator thinks there's nothing
1545 // to iterate over, so just free it up so we
1546 // don't accidentally call into it.
1548 mSubtreeIter
.reset();
1552 // Initialize the iterator by calling First().
1553 // Note that we are ignoring the return value on purpose!
1560 already_AddRefed
<nsINode
> RangeSubtreeIterator::GetCurrentNode() {
1561 nsCOMPtr
<nsINode
> node
;
1563 if (mIterState
== eUseStart
&& mStart
) {
1565 } else if (mIterState
== eUseEnd
&& mEnd
) {
1567 } else if (mIterState
== eUseIterator
&& mSubtreeIter
) {
1568 node
= mSubtreeIter
->GetCurrentNode();
1571 return node
.forget();
1574 void RangeSubtreeIterator::First() {
1576 mIterState
= eUseStart
;
1577 else if (mSubtreeIter
) {
1578 mSubtreeIter
->First();
1580 mIterState
= eUseIterator
;
1582 mIterState
= eUseEnd
;
1587 void RangeSubtreeIterator::Last() {
1589 mIterState
= eUseEnd
;
1590 else if (mSubtreeIter
) {
1591 mSubtreeIter
->Last();
1593 mIterState
= eUseIterator
;
1595 mIterState
= eUseStart
;
1600 void RangeSubtreeIterator::Next() {
1601 if (mIterState
== eUseStart
) {
1603 mSubtreeIter
->First();
1605 mIterState
= eUseIterator
;
1607 mIterState
= eUseEnd
;
1610 } else if (mIterState
== eUseIterator
) {
1611 mSubtreeIter
->Next();
1613 if (mSubtreeIter
->IsDone()) {
1615 mIterState
= eUseEnd
;
1623 void RangeSubtreeIterator::Prev() {
1624 if (mIterState
== eUseEnd
) {
1626 mSubtreeIter
->Last();
1628 mIterState
= eUseIterator
;
1630 mIterState
= eUseStart
;
1633 } else if (mIterState
== eUseIterator
) {
1634 mSubtreeIter
->Prev();
1636 if (mSubtreeIter
->IsDone()) {
1638 mIterState
= eUseStart
;
1646 // CollapseRangeAfterDelete() is a utility method that is used by
1647 // DeleteContents() and ExtractContents() to collapse the range
1648 // in the correct place, under the range's root container (the
1649 // range end points common container) as outlined by the Range spec:
1651 // http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html
1652 // The assumption made by this method is that the delete or extract
1653 // has been done already, and left the range in a state where there is
1654 // no content between the 2 end points.
1656 static nsresult
CollapseRangeAfterDelete(nsRange
* aRange
) {
1657 NS_ENSURE_ARG_POINTER(aRange
);
1659 // Check if range gravity took care of collapsing the range for us!
1660 if (aRange
->Collapsed()) {
1661 // aRange is collapsed so there's nothing for us to do.
1663 // There are 2 possible scenarios here:
1665 // 1. aRange could've been collapsed prior to the delete/extract,
1666 // which would've resulted in nothing being removed, so aRange
1667 // is already where it should be.
1669 // 2. Prior to the delete/extract, aRange's start and end were in
1670 // the same container which would mean everything between them
1671 // was removed, causing range gravity to collapse the range.
1676 // aRange isn't collapsed so figure out the appropriate place to collapse!
1677 // First get both end points and their common ancestor.
1679 if (!aRange
->IsPositioned()) {
1680 return NS_ERROR_NOT_INITIALIZED
;
1683 nsCOMPtr
<nsINode
> commonAncestor
=
1684 aRange
->GetClosestCommonInclusiveAncestor();
1686 nsCOMPtr
<nsINode
> startContainer
= aRange
->GetStartContainer();
1687 nsCOMPtr
<nsINode
> endContainer
= aRange
->GetEndContainer();
1689 // Collapse to one of the end points if they are already in the
1690 // commonAncestor. This should work ok since this method is called
1691 // immediately after a delete or extract that leaves no content
1692 // between the 2 end points!
1694 if (startContainer
== commonAncestor
) {
1695 aRange
->Collapse(true);
1698 if (endContainer
== commonAncestor
) {
1699 aRange
->Collapse(false);
1703 // End points are at differing levels. We want to collapse to the
1704 // point that is between the 2 subtrees that contain each point,
1705 // under the common ancestor.
1707 nsCOMPtr
<nsINode
> nodeToSelect(startContainer
);
1709 while (nodeToSelect
) {
1710 nsCOMPtr
<nsINode
> parent
= nodeToSelect
->GetParentNode();
1711 if (parent
== commonAncestor
) break; // We found the nodeToSelect!
1713 nodeToSelect
= parent
;
1716 if (!nodeToSelect
) return NS_ERROR_FAILURE
; // This should never happen!
1719 aRange
->SelectNode(*nodeToSelect
, error
);
1720 if (error
.Failed()) {
1721 return error
.StealNSResult();
1724 aRange
->Collapse(false);
1729 PrependChild(nsINode
* aContainer
, nsINode
* aChild
) {
1730 nsCOMPtr
<nsINode
> first
= aContainer
->GetFirstChild();
1732 aContainer
->InsertBefore(*aChild
, first
, rv
);
1733 return rv
.StealNSResult();
1736 // Helper function for CutContents, making sure that the current node wasn't
1737 // removed by mutation events (bug 766426)
1738 static bool ValidateCurrentNode(nsRange
* aRange
, RangeSubtreeIterator
& aIter
) {
1740 nsCOMPtr
<nsINode
> node
= aIter
.GetCurrentNode();
1742 // We don't have to worry that the node was removed if it doesn't exist,
1743 // e.g., the iterator is done.
1747 nsresult rv
= RangeUtils::CompareNodeToRange(node
, aRange
, &before
, &after
);
1748 if (NS_WARN_IF(NS_FAILED(rv
))) {
1752 if (before
|| after
) {
1753 if (node
->IsCharacterData()) {
1754 // If we're dealing with the start/end container which is a character
1755 // node, pretend that the node is in the range.
1756 if (before
&& node
== aRange
->GetStartContainer()) {
1759 if (after
&& node
== aRange
->GetEndContainer()) {
1765 return !before
&& !after
;
1768 void nsRange::CutContents(DocumentFragment
** aFragment
, ErrorResult
& aRv
) {
1770 *aFragment
= nullptr;
1773 if (!CanAccess(*mStart
.Container()) || !CanAccess(*mEnd
.Container())) {
1774 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1778 nsCOMPtr
<Document
> doc
= mStart
.Container()->OwnerDoc();
1780 nsCOMPtr
<nsINode
> commonAncestor
= GetCommonAncestorContainer(aRv
);
1785 // If aFragment isn't null, create a temporary fragment to hold our return.
1786 RefPtr
<DocumentFragment
> retval
;
1789 new (doc
->NodeInfoManager()) DocumentFragment(doc
->NodeInfoManager());
1791 nsCOMPtr
<nsINode
> commonCloneAncestor
= retval
.get();
1793 // Batch possible DOMSubtreeModified events.
1794 mozAutoSubtreeModified
subtree(mRoot
? mRoot
->OwnerDoc() : nullptr, nullptr);
1796 // Save the range end points locally to avoid interference
1797 // of Range gravity during our edits!
1799 nsCOMPtr
<nsINode
> startContainer
= mStart
.Container();
1800 // `GetCommonAncestorContainer()` above ensures the range is positioned, hence
1801 // there have to be valid offsets.
1802 uint32_t startOffset
=
1803 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
1804 nsCOMPtr
<nsINode
> endContainer
= mEnd
.Container();
1805 uint32_t endOffset
= *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
1808 // For extractContents(), abort early if there's a doctype (bug 719533).
1809 // This can happen only if the common ancestor is a document, in which case
1810 // we just need to find its doctype child and check if that's in the range.
1811 nsCOMPtr
<Document
> commonAncestorDocument
=
1812 do_QueryInterface(commonAncestor
);
1813 if (commonAncestorDocument
) {
1814 RefPtr
<DocumentType
> doctype
= commonAncestorDocument
->GetDoctype();
1816 // `GetCommonAncestorContainer()` above ensured the range is positioned.
1817 // Hence, start and end are both set and valid. If available, `doctype`
1818 // has a common ancestor with start and end, hence both have to be
1819 // comparable to it.
1821 *nsContentUtils::ComparePoints(startContainer
, startOffset
, doctype
,
1823 *nsContentUtils::ComparePoints(doctype
, 0, endContainer
, endOffset
) <
1825 aRv
.ThrowHierarchyRequestError("Start or end position isn't valid.");
1831 // Create and initialize a subtree iterator that will give
1832 // us all the subtrees within the range.
1834 RangeSubtreeIterator iter
;
1836 aRv
= iter
.Init(this);
1841 if (iter
.IsDone()) {
1842 // There's nothing for us to delete.
1843 aRv
= CollapseRangeAfterDelete(this);
1844 if (!aRv
.Failed() && aFragment
) {
1845 retval
.forget(aFragment
);
1852 bool handled
= false;
1854 // With the exception of text nodes that contain one of the range
1855 // end points, the subtree iterator should only give us back subtrees
1856 // that are completely contained between the range's end points.
1858 while (!iter
.IsDone()) {
1859 nsCOMPtr
<nsINode
> nodeToResult
;
1860 nsCOMPtr
<nsINode
> node
= iter
.GetCurrentNode();
1862 // Before we delete anything, advance the iterator to the next node that's
1863 // not a descendant of this one. XXX It's a bit silly to iterate through
1864 // the descendants only to throw them out, we should use an iterator that
1865 // skips the descendants to begin with.
1868 nsCOMPtr
<nsINode
> nextNode
= iter
.GetCurrentNode();
1869 while (nextNode
&& nextNode
->IsInclusiveDescendantOf(node
)) {
1871 nextNode
= iter
.GetCurrentNode();
1876 // If it's CharacterData, make sure we might need to delete
1877 // part of the data, instead of removing the whole node.
1879 // XXX_kin: We need to also handle ProcessingInstruction
1880 // XXX_kin: according to the spec.
1882 if (auto charData
= CharacterData::FromNode(node
)) {
1883 uint32_t dataLength
= 0;
1885 if (node
== startContainer
) {
1886 if (node
== endContainer
) {
1887 // This range is completely contained within a single text node.
1888 // Delete or extract the data between startOffset and endOffset.
1890 if (endOffset
> startOffset
) {
1892 nsAutoString cutValue
;
1893 charData
->SubstringData(startOffset
, endOffset
- startOffset
,
1895 if (NS_WARN_IF(aRv
.Failed())) {
1898 nsCOMPtr
<nsINode
> clone
= node
->CloneNode(false, aRv
);
1899 if (NS_WARN_IF(aRv
.Failed())) {
1902 clone
->SetNodeValue(cutValue
, aRv
);
1903 if (NS_WARN_IF(aRv
.Failed())) {
1906 nodeToResult
= clone
;
1909 nsMutationGuard guard
;
1910 charData
->DeleteData(startOffset
, endOffset
- startOffset
, aRv
);
1911 if (NS_WARN_IF(aRv
.Failed())) {
1914 if (guard
.Mutated(0) && !ValidateCurrentNode(this, iter
)) {
1915 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1922 // Delete or extract everything after startOffset.
1924 dataLength
= charData
->Length();
1926 if (dataLength
>= startOffset
) {
1928 nsAutoString cutValue
;
1929 charData
->SubstringData(startOffset
, dataLength
, cutValue
, aRv
);
1930 if (NS_WARN_IF(aRv
.Failed())) {
1933 nsCOMPtr
<nsINode
> clone
= node
->CloneNode(false, aRv
);
1934 if (NS_WARN_IF(aRv
.Failed())) {
1937 clone
->SetNodeValue(cutValue
, aRv
);
1938 if (NS_WARN_IF(aRv
.Failed())) {
1941 nodeToResult
= clone
;
1944 nsMutationGuard guard
;
1945 charData
->DeleteData(startOffset
, dataLength
, aRv
);
1946 if (NS_WARN_IF(aRv
.Failed())) {
1949 if (guard
.Mutated(0) && !ValidateCurrentNode(this, iter
)) {
1950 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1957 } else if (node
== endContainer
) {
1958 // Delete or extract everything before endOffset.
1960 nsAutoString cutValue
;
1961 charData
->SubstringData(0, endOffset
, cutValue
, aRv
);
1962 if (NS_WARN_IF(aRv
.Failed())) {
1965 nsCOMPtr
<nsINode
> clone
= node
->CloneNode(false, aRv
);
1966 if (NS_WARN_IF(aRv
.Failed())) {
1969 clone
->SetNodeValue(cutValue
, aRv
);
1970 if (NS_WARN_IF(aRv
.Failed())) {
1973 nodeToResult
= clone
;
1976 nsMutationGuard guard
;
1977 charData
->DeleteData(0, endOffset
, aRv
);
1978 if (NS_WARN_IF(aRv
.Failed())) {
1981 if (guard
.Mutated(0) && !ValidateCurrentNode(this, iter
)) {
1982 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1989 if (!handled
&& (node
== endContainer
|| node
== startContainer
)) {
1990 if (node
&& node
->IsElement() &&
1991 ((node
== endContainer
&& endOffset
== 0) ||
1992 (node
== startContainer
&&
1993 node
->AsElement()->GetChildCount() == startOffset
))) {
1995 nodeToResult
= node
->CloneNode(false, aRv
);
2005 // node was not handled above, so it must be completely contained
2006 // within the range. Just remove it from the tree!
2007 nodeToResult
= node
;
2010 uint32_t parentCount
= 0;
2011 // Set the result to document fragment if we have 'retval'.
2013 nsCOMPtr
<nsINode
> oldCommonAncestor
= commonAncestor
;
2014 if (!iter
.IsDone()) {
2015 // Setup the parameters for the next iteration of the loop.
2017 aRv
.Throw(NS_ERROR_UNEXPECTED
);
2021 // Get node's and nextNode's common parent. Do this before moving
2022 // nodes from original DOM to result fragment.
2024 nsContentUtils::GetClosestCommonInclusiveAncestor(node
, nextNode
);
2025 if (!commonAncestor
) {
2026 aRv
.Throw(NS_ERROR_UNEXPECTED
);
2030 nsCOMPtr
<nsINode
> parentCounterNode
= node
;
2031 while (parentCounterNode
&& parentCounterNode
!= commonAncestor
) {
2033 parentCounterNode
= parentCounterNode
->GetParentNode();
2034 if (!parentCounterNode
) {
2035 aRv
.Throw(NS_ERROR_UNEXPECTED
);
2041 // Clone the parent hierarchy between commonAncestor and node.
2042 nsCOMPtr
<nsINode
> closestAncestor
, farthestAncestor
;
2043 aRv
= CloneParentsBetween(oldCommonAncestor
, node
,
2044 getter_AddRefs(closestAncestor
),
2045 getter_AddRefs(farthestAncestor
));
2050 if (farthestAncestor
) {
2051 commonCloneAncestor
->AppendChild(*farthestAncestor
, aRv
);
2052 if (NS_WARN_IF(aRv
.Failed())) {
2057 nsMutationGuard guard
;
2058 nsCOMPtr
<nsINode
> parent
= nodeToResult
->GetParentNode();
2059 if (closestAncestor
) {
2060 closestAncestor
->AppendChild(*nodeToResult
, aRv
);
2062 commonCloneAncestor
->AppendChild(*nodeToResult
, aRv
);
2064 if (NS_WARN_IF(aRv
.Failed())) {
2067 if (guard
.Mutated(parent
? 2 : 1) && !ValidateCurrentNode(this, iter
)) {
2068 aRv
.Throw(NS_ERROR_UNEXPECTED
);
2071 } else if (nodeToResult
) {
2072 nsMutationGuard guard
;
2073 nsCOMPtr
<nsINode
> node
= nodeToResult
;
2074 nsCOMPtr
<nsINode
> parent
= node
->GetParentNode();
2076 parent
->RemoveChild(*node
, aRv
);
2081 if (guard
.Mutated(1) && !ValidateCurrentNode(this, iter
)) {
2082 aRv
.Throw(NS_ERROR_UNEXPECTED
);
2087 if (!iter
.IsDone() && retval
) {
2088 // Find the equivalent of commonAncestor in the cloned tree.
2089 nsCOMPtr
<nsINode
> newCloneAncestor
= nodeToResult
;
2090 for (uint32_t i
= parentCount
; i
; --i
) {
2091 newCloneAncestor
= newCloneAncestor
->GetParentNode();
2092 if (!newCloneAncestor
) {
2093 aRv
.Throw(NS_ERROR_UNEXPECTED
);
2097 commonCloneAncestor
= newCloneAncestor
;
2101 aRv
= CollapseRangeAfterDelete(this);
2102 if (!aRv
.Failed() && aFragment
) {
2103 retval
.forget(aFragment
);
2107 void nsRange::DeleteContents(ErrorResult
& aRv
) { CutContents(nullptr, aRv
); }
2109 already_AddRefed
<DocumentFragment
> nsRange::ExtractContents(ErrorResult
& rv
) {
2110 RefPtr
<DocumentFragment
> fragment
;
2111 CutContents(getter_AddRefs(fragment
), rv
);
2112 return fragment
.forget();
2115 int16_t nsRange::CompareBoundaryPoints(uint16_t aHow
,
2116 const nsRange
& aOtherRange
,
2118 if (!mIsPositioned
|| !aOtherRange
.IsPositioned()) {
2119 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
2123 nsINode
*ourNode
, *otherNode
;
2124 uint32_t ourOffset
, otherOffset
;
2127 case Range_Binding::START_TO_START
:
2128 ourNode
= mStart
.Container();
2129 ourOffset
= *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
2130 otherNode
= aOtherRange
.GetStartContainer();
2131 otherOffset
= aOtherRange
.StartOffset();
2133 case Range_Binding::START_TO_END
:
2134 ourNode
= mEnd
.Container();
2135 ourOffset
= *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
2136 otherNode
= aOtherRange
.GetStartContainer();
2137 otherOffset
= aOtherRange
.StartOffset();
2139 case Range_Binding::END_TO_START
:
2140 ourNode
= mStart
.Container();
2141 ourOffset
= *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
2142 otherNode
= aOtherRange
.GetEndContainer();
2143 otherOffset
= aOtherRange
.EndOffset();
2145 case Range_Binding::END_TO_END
:
2146 ourNode
= mEnd
.Container();
2147 ourOffset
= *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
2148 otherNode
= aOtherRange
.GetEndContainer();
2149 otherOffset
= aOtherRange
.EndOffset();
2152 // We were passed an illegal value
2153 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
2157 if (mRoot
!= aOtherRange
.GetRoot()) {
2158 aRv
.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR
);
2162 const Maybe
<int32_t> order
=
2163 nsContentUtils::ComparePoints(ourNode
, ourOffset
, otherNode
, otherOffset
);
2165 // `this` and `aOtherRange` share the same root and (ourNode, ourOffset),
2166 // (otherNode, otherOffset) correspond to some of their boundaries. Hence,
2167 // (ourNode, ourOffset) and (otherNode, otherOffset) have to be comparable.
2172 nsresult
nsRange::CloneParentsBetween(nsINode
* aAncestor
, nsINode
* aNode
,
2173 nsINode
** aClosestAncestor
,
2174 nsINode
** aFarthestAncestor
) {
2175 NS_ENSURE_ARG_POINTER(
2176 (aAncestor
&& aNode
&& aClosestAncestor
&& aFarthestAncestor
));
2178 *aClosestAncestor
= nullptr;
2179 *aFarthestAncestor
= nullptr;
2181 if (aAncestor
== aNode
) return NS_OK
;
2183 AutoTArray
<nsCOMPtr
<nsINode
>, 16> parentStack
;
2185 nsCOMPtr
<nsINode
> parent
= aNode
->GetParentNode();
2186 while (parent
&& parent
!= aAncestor
) {
2187 parentStack
.AppendElement(parent
);
2188 parent
= parent
->GetParentNode();
2191 nsCOMPtr
<nsINode
> firstParent
;
2192 nsCOMPtr
<nsINode
> lastParent
;
2193 for (int32_t i
= parentStack
.Length() - 1; i
>= 0; i
--) {
2195 nsCOMPtr
<nsINode
> clone
= parentStack
[i
]->CloneNode(false, rv
);
2198 return rv
.StealNSResult();
2201 return NS_ERROR_FAILURE
;
2207 firstParent
->AppendChild(*clone
, rv
);
2209 return rv
.StealNSResult();
2213 firstParent
= clone
;
2216 firstParent
.forget(aClosestAncestor
);
2217 lastParent
.forget(aFarthestAncestor
);
2222 already_AddRefed
<DocumentFragment
> nsRange::CloneContents(ErrorResult
& aRv
) {
2223 nsCOMPtr
<nsINode
> commonAncestor
= GetCommonAncestorContainer(aRv
);
2224 MOZ_ASSERT(!aRv
.Failed(), "GetCommonAncestorContainer() shouldn't fail!");
2226 nsCOMPtr
<Document
> doc
= mStart
.Container()->OwnerDoc();
2227 NS_ASSERTION(doc
, "CloneContents needs a document to continue.");
2229 aRv
.Throw(NS_ERROR_FAILURE
);
2233 // Create a new document fragment in the context of this document,
2234 // which might be null
2236 RefPtr
<DocumentFragment
> clonedFrag
=
2237 new (doc
->NodeInfoManager()) DocumentFragment(doc
->NodeInfoManager());
2240 return clonedFrag
.forget();
2243 nsCOMPtr
<nsINode
> commonCloneAncestor
= clonedFrag
.get();
2245 // Create and initialize a subtree iterator that will give
2246 // us all the subtrees within the range.
2248 RangeSubtreeIterator iter
;
2250 aRv
= iter
.Init(this);
2255 if (iter
.IsDone()) {
2256 // There's nothing to add to the doc frag, we must be done!
2257 return clonedFrag
.forget();
2262 // With the exception of text nodes that contain one of the range
2263 // end points and elements which don't have any content selected the subtree
2264 // iterator should only give us back subtrees that are completely contained
2265 // between the range's end points.
2267 // Unfortunately these subtrees don't contain the parent hierarchy/context
2268 // that the Range spec requires us to return. This loop clones the
2269 // parent hierarchy, adds a cloned version of the subtree, to it, then
2270 // correctly places this new subtree into the doc fragment.
2272 while (!iter
.IsDone()) {
2273 nsCOMPtr
<nsINode
> node
= iter
.GetCurrentNode();
2275 !node
->IsElement() ||
2276 (!(node
== mEnd
.Container() &&
2277 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
) == 0) &&
2278 !(node
== mStart
.Container() &&
2279 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
) ==
2280 node
->AsElement()->GetChildCount()));
2282 // Clone the current subtree!
2284 nsCOMPtr
<nsINode
> clone
= node
->CloneNode(deepClone
, aRv
);
2289 // If it's CharacterData, make sure we only clone what
2292 // XXX_kin: We need to also handle ProcessingInstruction
2293 // XXX_kin: according to the spec.
2295 if (auto charData
= CharacterData::FromNode(clone
)) {
2296 if (node
== mEnd
.Container()) {
2297 // We only need the data before mEndOffset, so get rid of any
2300 uint32_t dataLength
= charData
->Length();
2302 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
)) {
2303 charData
->DeleteData(
2304 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2306 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2314 if (node
== mStart
.Container()) {
2315 // We don't need any data before mStartOffset, so just
2318 if (*mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
) > 0) {
2319 charData
->DeleteData(
2320 0, *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2329 // Clone the parent hierarchy between commonAncestor and node.
2331 nsCOMPtr
<nsINode
> closestAncestor
, farthestAncestor
;
2333 aRv
= CloneParentsBetween(commonAncestor
, node
,
2334 getter_AddRefs(closestAncestor
),
2335 getter_AddRefs(farthestAncestor
));
2341 // Hook the parent hierarchy/context of the subtree into the clone tree.
2343 if (farthestAncestor
) {
2344 commonCloneAncestor
->AppendChild(*farthestAncestor
, aRv
);
2351 // Place the cloned subtree into the cloned doc frag tree!
2353 nsCOMPtr
<nsINode
> cloneNode
= clone
;
2354 if (closestAncestor
) {
2355 // Append the subtree under closestAncestor since it is the
2356 // immediate parent of the subtree.
2358 closestAncestor
->AppendChild(*cloneNode
, aRv
);
2360 // If we get here, there is no missing parent hierarchy between
2361 // commonAncestor and node, so just append clone to commonCloneAncestor.
2363 commonCloneAncestor
->AppendChild(*cloneNode
, aRv
);
2369 // Get the next subtree to be processed. The idea here is to setup
2370 // the parameters for the next iteration of the loop.
2374 if (iter
.IsDone()) break; // We must be done!
2376 nsCOMPtr
<nsINode
> nextNode
= iter
.GetCurrentNode();
2378 aRv
.Throw(NS_ERROR_FAILURE
);
2382 // Get node and nextNode's common parent.
2384 nsContentUtils::GetClosestCommonInclusiveAncestor(node
, nextNode
);
2386 if (!commonAncestor
) {
2387 aRv
.Throw(NS_ERROR_FAILURE
);
2391 // Find the equivalent of commonAncestor in the cloned tree!
2393 while (node
&& node
!= commonAncestor
) {
2394 node
= node
->GetParentNode();
2400 aRv
.Throw(NS_ERROR_FAILURE
);
2404 cloneNode
= cloneNode
->GetParentNode();
2406 aRv
.Throw(NS_ERROR_FAILURE
);
2411 commonCloneAncestor
= cloneNode
;
2414 return clonedFrag
.forget();
2417 already_AddRefed
<nsRange
> nsRange::CloneRange() const {
2418 RefPtr
<nsRange
> range
= nsRange::Create(mOwner
);
2419 range
->DoSetRange(mStart
, mEnd
, mRoot
);
2420 if (mCrossShadowBoundaryRange
) {
2421 range
->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
2422 mCrossShadowBoundaryRange
->StartRef(),
2423 mCrossShadowBoundaryRange
->EndRef());
2425 return range
.forget();
2428 void nsRange::InsertNode(nsINode
& aNode
, ErrorResult
& aRv
) {
2429 if (!CanAccess(aNode
)) {
2430 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
2434 if (!IsPositioned()) {
2435 aRv
.Throw(NS_ERROR_NOT_INITIALIZED
);
2439 uint32_t tStartOffset
= StartOffset();
2441 nsCOMPtr
<nsINode
> tStartContainer
= GetStartContainer();
2443 if (!CanAccess(*tStartContainer
)) {
2444 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
2448 if (&aNode
== tStartContainer
) {
2449 aRv
.ThrowHierarchyRequestError(
2450 "The inserted node can not be range's start node.");
2454 // This is the node we'll be inserting before, and its parent
2455 nsCOMPtr
<nsINode
> referenceNode
;
2456 nsCOMPtr
<nsINode
> referenceParentNode
= tStartContainer
;
2458 RefPtr
<Text
> startTextNode
= tStartContainer
->GetAsText();
2459 nsCOMPtr
<nsINodeList
> tChildList
;
2460 if (startTextNode
) {
2461 referenceParentNode
= tStartContainer
->GetParentNode();
2462 if (!referenceParentNode
) {
2463 aRv
.ThrowHierarchyRequestError(
2464 "Can not get range's start node's parent.");
2468 referenceParentNode
->EnsurePreInsertionValidity(aNode
, tStartContainer
,
2474 RefPtr
<Text
> secondPart
= startTextNode
->SplitText(tStartOffset
, aRv
);
2479 referenceNode
= secondPart
;
2481 tChildList
= tStartContainer
->ChildNodes();
2483 // find the insertion point in the DOM and insert the Node
2484 referenceNode
= tChildList
->Item(tStartOffset
);
2486 tStartContainer
->EnsurePreInsertionValidity(aNode
, referenceNode
, aRv
);
2492 // We might need to update the end to include the new node (bug 433662).
2493 // Ideally we'd only do this if needed, but it's tricky to know when it's
2494 // needed in advance (bug 765799).
2497 if (referenceNode
) {
2498 Maybe
<uint32_t> indexInParent
= referenceNode
->ComputeIndexInParentNode();
2499 if (MOZ_UNLIKELY(NS_WARN_IF(indexInParent
.isNothing()))) {
2500 aRv
.Throw(NS_ERROR_FAILURE
);
2503 newOffset
= *indexInParent
;
2505 newOffset
= tChildList
->Length();
2508 if (aNode
.NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE
) {
2509 newOffset
+= aNode
.GetChildCount();
2514 // Now actually insert the node
2515 nsCOMPtr
<nsINode
> tResultNode
;
2516 tResultNode
= referenceParentNode
->InsertBefore(aNode
, referenceNode
, aRv
);
2522 aRv
= SetEnd(referenceParentNode
, newOffset
);
2526 void nsRange::SurroundContents(nsINode
& aNewParent
, ErrorResult
& aRv
) {
2527 if (!CanAccess(aNewParent
)) {
2528 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
2533 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2536 // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text
2538 if (mStart
.Container() != mEnd
.Container()) {
2539 bool startIsText
= mStart
.Container()->IsText();
2540 bool endIsText
= mEnd
.Container()->IsText();
2541 nsINode
* startGrandParent
= mStart
.Container()->GetParentNode();
2542 nsINode
* endGrandParent
= mEnd
.Container()->GetParentNode();
2543 if (!((startIsText
&& endIsText
&& startGrandParent
&&
2544 startGrandParent
== endGrandParent
) ||
2545 (startIsText
&& startGrandParent
&&
2546 startGrandParent
== mEnd
.Container()) ||
2547 (endIsText
&& endGrandParent
&&
2548 endGrandParent
== mStart
.Container()))) {
2549 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2554 // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted
2555 // (Document, DocumentType, DocumentFragment)
2556 uint16_t nodeType
= aNewParent
.NodeType();
2557 if (nodeType
== nsINode::DOCUMENT_NODE
||
2558 nodeType
== nsINode::DOCUMENT_TYPE_NODE
||
2559 nodeType
== nsINode::DOCUMENT_FRAGMENT_NODE
) {
2560 aRv
.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
);
2564 // Extract the contents within the range.
2566 RefPtr
<DocumentFragment
> docFrag
= ExtractContents(aRv
);
2573 aRv
.Throw(NS_ERROR_FAILURE
);
2577 // Spec says we need to remove all of aNewParent's
2578 // children prior to insertion.
2580 nsCOMPtr
<nsINodeList
> children
= aNewParent
.ChildNodes();
2582 aRv
.Throw(NS_ERROR_FAILURE
);
2586 uint32_t numChildren
= children
->Length();
2588 while (numChildren
) {
2589 nsCOMPtr
<nsINode
> child
= children
->Item(--numChildren
);
2591 aRv
.Throw(NS_ERROR_FAILURE
);
2595 aNewParent
.RemoveChild(*child
, aRv
);
2601 // Insert aNewParent at the range's start point.
2603 InsertNode(aNewParent
, aRv
);
2608 // Append the content we extracted under aNewParent.
2609 aNewParent
.AppendChild(*docFrag
, aRv
);
2614 // Select aNewParent, and its contents.
2616 SelectNode(aNewParent
, aRv
);
2619 void nsRange::ToString(nsAString
& aReturn
, ErrorResult
& aErr
) {
2623 // If we're unpositioned, return the empty string
2624 if (!mIsPositioned
) {
2629 printf("Range dump: -----------------------\n");
2632 // effeciency hack for simple case
2633 if (mStart
.Container() == mEnd
.Container()) {
2635 mStart
.Container() ? mStart
.Container()->GetAsText() : nullptr;
2639 // If debug, dump it:
2640 textNode
->List(stdout
);
2641 printf("End Range dump: -----------------------\n");
2645 textNode
->SubstringData(
2646 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2647 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
) -
2648 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2654 /* complex case: mStart.Container() != mEnd.Container(), or mStartParent not a
2655 text node revisit - there are potential optimizations here and also
2659 PostContentIterator postOrderIter
;
2660 nsresult rv
= postOrderIter
.Init(this);
2661 if (NS_WARN_IF(NS_FAILED(rv
))) {
2666 nsString tempString
;
2668 // loop through the content iterator, which returns nodes in the range in
2669 // close tag order, and grab the text from any text node
2670 for (; !postOrderIter
.IsDone(); postOrderIter
.Next()) {
2671 nsINode
* n
= postOrderIter
.GetCurrentNode();
2674 // If debug, dump it:
2677 Text
* textNode
= n
->GetAsText();
2678 if (textNode
) // if it's a text node, get the text
2680 if (n
== mStart
.Container()) { // only include text past start offset
2681 uint32_t strLength
= textNode
->Length();
2682 textNode
->SubstringData(
2683 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2685 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2686 tempString
, IgnoreErrors());
2687 aReturn
+= tempString
;
2689 mEnd
.Container()) { // only include text before end offset
2690 textNode
->SubstringData(
2691 0, *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2692 tempString
, IgnoreErrors());
2693 aReturn
+= tempString
;
2694 } else { // grab the whole kit-n-kaboodle
2695 textNode
->GetData(tempString
);
2696 aReturn
+= tempString
;
2702 printf("End Range dump: -----------------------\n");
2706 void nsRange::Detach() {}
2708 already_AddRefed
<DocumentFragment
> nsRange::CreateContextualFragment(
2709 const nsAString
& aFragment
, ErrorResult
& aRv
) const {
2710 if (!mIsPositioned
) {
2711 aRv
.Throw(NS_ERROR_FAILURE
);
2715 return nsContentUtils::CreateContextualFragment(mStart
.Container(), aFragment
,
2719 static void ExtractRectFromOffset(nsIFrame
* aFrame
, const int32_t aOffset
,
2720 nsRect
* aR
, bool aFlushToOriginEdge
,
2721 bool aClampToEdge
) {
2726 aFrame
->GetPointFromOffset(aOffset
, &point
);
2728 // Determine if aFrame has a vertical writing mode, which will change our math
2729 // on the output rect.
2730 bool isVertical
= aFrame
->GetWritingMode().IsVertical();
2732 if (!aClampToEdge
&& !aR
->Contains(point
)) {
2733 // If point is outside aR, and we aren't clamping, output an empty rect
2734 // with origin at the point.
2746 point
= aR
->ClampPoint(point
);
2749 // point is within aR, and now we'll modify aR to output a rect that has point
2750 // on one edge. But which edge?
2751 if (aFlushToOriginEdge
) {
2752 // The output rect should be flush to the edge of aR that contains the
2755 aR
->SetHeight(point
.y
- aR
->y
);
2757 aR
->SetWidth(point
.x
- aR
->x
);
2760 // The output rect should be flush to the edge of aR opposite the origin.
2762 aR
->SetHeight(aR
->YMost() - point
.y
);
2765 aR
->SetWidth(aR
->XMost() - point
.x
);
2771 static nsTextFrame
* GetTextFrameForContent(nsIContent
* aContent
,
2772 bool aFlushLayout
) {
2773 RefPtr
<Document
> doc
= aContent
->OwnerDoc();
2774 PresShell
* presShell
= doc
->GetPresShell();
2779 // Try to un-suppress whitespace if needed, but only if we'll be able to flush
2780 // to immediately see the results of the un-suppression. If we can't flush
2781 // here, then calling EnsureFrameForTextNodeIsCreatedAfterFlush would be
2782 // pointless anyway.
2784 const bool frameWillBeUnsuppressed
=
2785 presShell
->FrameConstructor()
2786 ->EnsureFrameForTextNodeIsCreatedAfterFlush(
2787 static_cast<CharacterData
*>(aContent
));
2788 if (frameWillBeUnsuppressed
) {
2789 doc
->FlushPendingNotifications(FlushType::Layout
);
2793 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
2794 if (!frame
|| !frame
->IsTextFrame()) {
2797 return static_cast<nsTextFrame
*>(frame
);
2800 static nsresult
GetPartialTextRect(RectCallback
* aCallback
,
2801 Sequence
<nsString
>* aTextList
,
2802 nsIContent
* aContent
, int32_t aStartOffset
,
2803 int32_t aEndOffset
, bool aClampToEdge
,
2804 bool aFlushLayout
) {
2805 nsTextFrame
* textFrame
= GetTextFrameForContent(aContent
, aFlushLayout
);
2807 nsIFrame
* relativeTo
=
2808 nsLayoutUtils::GetContainingBlockForClientRect(textFrame
);
2810 for (nsTextFrame
* f
= textFrame
->FindContinuationForOffset(aStartOffset
); f
;
2811 f
= static_cast<nsTextFrame
*>(f
->GetNextContinuation())) {
2812 int32_t fstart
= f
->GetContentOffset(), fend
= f
->GetContentEnd();
2813 if (fend
<= aStartOffset
) {
2816 if (fstart
>= aEndOffset
) {
2820 // Calculate the text content offsets we'll need if text is requested.
2821 int32_t textContentStart
= fstart
;
2822 int32_t textContentEnd
= fend
;
2824 // overlapping with the offset we want
2825 f
->EnsureTextRun(nsTextFrame::eInflated
);
2826 NS_ENSURE_TRUE(f
->GetTextRun(nsTextFrame::eInflated
),
2827 NS_ERROR_OUT_OF_MEMORY
);
2828 bool topLeftToBottomRight
=
2829 !f
->GetTextRun(nsTextFrame::eInflated
)->IsInlineReversed();
2830 nsRect r
= f
->GetRectRelativeToSelf();
2831 if (fstart
< aStartOffset
) {
2832 // aStartOffset is within this frame
2833 ExtractRectFromOffset(f
, aStartOffset
, &r
, !topLeftToBottomRight
,
2835 textContentStart
= aStartOffset
;
2837 if (fend
> aEndOffset
) {
2838 // aEndOffset is in the middle of this frame
2839 ExtractRectFromOffset(f
, aEndOffset
, &r
, topLeftToBottomRight
,
2841 textContentEnd
= aEndOffset
;
2843 r
= nsLayoutUtils::TransformFrameRectToAncestor(f
, r
, relativeTo
);
2844 aCallback
->AddRect(r
);
2846 // Finally capture the text, if requested.
2848 nsIFrame::RenderedText renderedText
=
2849 f
->GetRenderedText(textContentStart
, textContentEnd
,
2850 nsIFrame::TextOffsetType::OffsetsInContentText
,
2851 nsIFrame::TrailingWhitespace::DontTrim
);
2853 NS_ENSURE_TRUE(aTextList
->AppendElement(renderedText
.mString
, fallible
),
2854 NS_ERROR_OUT_OF_MEMORY
);
2861 static void CollectClientRectsForSubtree(
2862 nsINode
* aNode
, RectCallback
* aCollector
, Sequence
<nsString
>* aTextList
,
2863 nsINode
* aStartContainer
, uint32_t aStartOffset
, nsINode
* aEndContainer
,
2864 uint32_t aEndOffset
, bool aClampToEdge
, bool aFlushLayout
, bool aTextOnly
) {
2865 auto* content
= nsIContent::FromNode(aNode
);
2870 const bool isText
= content
->IsText();
2872 if (aNode
== aStartContainer
) {
2873 int32_t offset
= aStartContainer
== aEndContainer
2874 ? static_cast<int32_t>(aEndOffset
)
2875 : content
->AsText()->TextDataLength();
2876 GetPartialTextRect(aCollector
, aTextList
, content
,
2877 static_cast<int32_t>(aStartOffset
), offset
,
2878 aClampToEdge
, aFlushLayout
);
2882 if (aNode
== aEndContainer
) {
2883 GetPartialTextRect(aCollector
, aTextList
, content
, 0,
2884 static_cast<int32_t>(aEndOffset
), aClampToEdge
,
2890 if (nsIFrame
* frame
= content
->GetPrimaryFrame()) {
2891 if (!aTextOnly
|| isText
) {
2892 nsLayoutUtils::GetAllInFlowRectsAndTexts(
2893 frame
, nsLayoutUtils::GetContainingBlockForClientRect(frame
),
2894 aCollector
, aTextList
, nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS
);
2899 // We just get the text when calling GetAllInFlowRectsAndTexts, so we
2900 // don't need to call it again when visiting the children.
2901 aTextList
= nullptr;
2903 } else if (!content
->IsElement() ||
2904 !content
->AsElement()->IsDisplayContents()) {
2908 FlattenedChildIterator
childIter(content
);
2909 for (nsIContent
* child
= childIter
.GetNextChild(); child
;
2910 child
= childIter
.GetNextChild()) {
2911 CollectClientRectsForSubtree(child
, aCollector
, aTextList
, aStartContainer
,
2912 aStartOffset
, aEndContainer
, aEndOffset
,
2913 aClampToEdge
, aFlushLayout
, aTextOnly
);
2918 void nsRange::CollectClientRectsAndText(
2919 RectCallback
* aCollector
, Sequence
<nsString
>* aTextList
, nsRange
* aRange
,
2920 nsINode
* aStartContainer
, uint32_t aStartOffset
, nsINode
* aEndContainer
,
2921 uint32_t aEndOffset
, bool aClampToEdge
, bool aFlushLayout
) {
2922 // Currently, this method is called with start of end offset of nsRange.
2923 // So, they must be between 0 - INT32_MAX.
2924 MOZ_ASSERT(RangeUtils::IsValidOffset(aStartOffset
));
2925 MOZ_ASSERT(RangeUtils::IsValidOffset(aEndOffset
));
2927 // Hold strong pointers across the flush
2928 nsCOMPtr
<nsINode
> startContainer
= aStartContainer
;
2929 nsCOMPtr
<nsINode
> endContainer
= aEndContainer
;
2931 // Flush out layout so our frames are up to date.
2932 if (!aStartContainer
->IsInComposedDoc()) {
2937 aStartContainer
->OwnerDoc()->FlushPendingNotifications(FlushType::Layout
);
2938 // Recheck whether we're still in the document
2939 if (!aStartContainer
->IsInComposedDoc()) {
2944 RangeSubtreeIterator iter
;
2946 nsresult rv
= iter
.Init(aRange
);
2947 if (NS_FAILED(rv
)) return;
2949 if (iter
.IsDone()) {
2950 // the range is collapsed, only continue if the cursor is in a text node
2951 if (aStartContainer
->IsText()) {
2952 nsTextFrame
* textFrame
=
2953 GetTextFrameForContent(aStartContainer
->AsText(), aFlushLayout
);
2957 textFrame
->GetChildFrameContainingOffset(
2958 static_cast<int32_t>(aStartOffset
), false, &outOffset
, &outFrame
);
2960 nsIFrame
* relativeTo
=
2961 nsLayoutUtils::GetContainingBlockForClientRect(outFrame
);
2962 nsRect r
= outFrame
->GetRectRelativeToSelf();
2963 ExtractRectFromOffset(outFrame
, static_cast<int32_t>(aStartOffset
),
2964 &r
, false, aClampToEdge
);
2966 r
= nsLayoutUtils::TransformFrameRectToAncestor(outFrame
, r
,
2968 aCollector
->AddRect(r
);
2976 nsCOMPtr
<nsINode
> node
= iter
.GetCurrentNode();
2979 CollectClientRectsForSubtree(node
, aCollector
, aTextList
, aStartContainer
,
2980 aStartOffset
, aEndContainer
, aEndOffset
,
2981 aClampToEdge
, aFlushLayout
, false);
2982 } while (!iter
.IsDone());
2985 already_AddRefed
<DOMRect
> nsRange::GetBoundingClientRect(bool aClampToEdge
,
2986 bool aFlushLayout
) {
2987 RefPtr
<DOMRect
> rect
= new DOMRect(ToSupports(mOwner
));
2988 if (!mIsPositioned
) {
2989 return rect
.forget();
2992 nsLayoutUtils::RectAccumulator accumulator
;
2993 CollectClientRectsAndText(
2994 &accumulator
, nullptr, this, mStart
.Container(),
2995 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
2997 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
), aClampToEdge
,
3000 nsRect r
= accumulator
.mResultRect
.IsEmpty() ? accumulator
.mFirstRect
3001 : accumulator
.mResultRect
;
3002 rect
->SetLayoutRect(r
);
3003 return rect
.forget();
3006 already_AddRefed
<DOMRectList
> nsRange::GetClientRects(bool aClampToEdge
,
3007 bool aFlushLayout
) {
3008 if (!mIsPositioned
) {
3012 RefPtr
<DOMRectList
> rectList
= new DOMRectList(ToSupports(mOwner
));
3014 nsLayoutUtils::RectListBuilder
builder(rectList
);
3016 CollectClientRectsAndText(
3017 &builder
, nullptr, this, mStart
.Container(),
3018 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
3020 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
), aClampToEdge
,
3022 return rectList
.forget();
3025 void nsRange::GetClientRectsAndTexts(mozilla::dom::ClientRectsAndTexts
& aResult
,
3026 ErrorResult
& aErr
) {
3027 if (!mIsPositioned
) {
3031 aResult
.mRectList
= new DOMRectList(ToSupports(mOwner
));
3033 nsLayoutUtils::RectListBuilder
builder(aResult
.mRectList
);
3035 CollectClientRectsAndText(
3036 &builder
, &aResult
.mTextList
, this, mStart
.Container(),
3037 *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
3039 *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
), true, true);
3042 nsresult
nsRange::GetUsedFontFaces(nsLayoutUtils::UsedFontFaceList
& aResult
,
3043 uint32_t aMaxRanges
,
3044 bool aSkipCollapsedWhitespace
) {
3045 NS_ENSURE_TRUE(mIsPositioned
, NS_ERROR_UNEXPECTED
);
3047 nsCOMPtr
<nsINode
> startContainer
= mStart
.Container();
3048 nsCOMPtr
<nsINode
> endContainer
= mEnd
.Container();
3050 // Flush out layout so our frames are up to date.
3051 Document
* doc
= mStart
.Container()->OwnerDoc();
3052 NS_ENSURE_TRUE(doc
, NS_ERROR_UNEXPECTED
);
3053 doc
->FlushPendingNotifications(FlushType::Frames
);
3055 // Recheck whether we're still in the document
3056 NS_ENSURE_TRUE(mStart
.Container()->IsInComposedDoc(), NS_ERROR_UNEXPECTED
);
3058 // A table to map gfxFontEntry objects to InspectorFontFace objects.
3059 // This table does NOT own the InspectorFontFace objects, it only holds
3060 // raw pointers to them. They are owned by the aResult array.
3061 nsLayoutUtils::UsedFontFaceTable fontFaces
;
3063 RangeSubtreeIterator iter
;
3064 nsresult rv
= iter
.Init(this);
3065 NS_ENSURE_SUCCESS(rv
, rv
);
3067 while (!iter
.IsDone()) {
3068 // only collect anything if the range is not collapsed
3069 nsCOMPtr
<nsINode
> node
= iter
.GetCurrentNode();
3072 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(node
);
3076 nsIFrame
* frame
= content
->GetPrimaryFrame();
3081 if (content
->IsText()) {
3082 if (node
== startContainer
) {
3084 startContainer
== endContainer
3085 ? *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
)
3086 : content
->AsText()->TextDataLength();
3087 nsLayoutUtils::GetFontFacesForText(
3088 frame
, *mStart
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
3089 offset
, true, aResult
, fontFaces
, aMaxRanges
,
3090 aSkipCollapsedWhitespace
);
3093 if (node
== endContainer
) {
3094 nsLayoutUtils::GetFontFacesForText(
3095 frame
, 0, *mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
),
3096 true, aResult
, fontFaces
, aMaxRanges
, aSkipCollapsedWhitespace
);
3101 nsLayoutUtils::GetFontFacesForFrames(frame
, aResult
, fontFaces
, aMaxRanges
,
3102 aSkipCollapsedWhitespace
);
3108 nsINode
* nsRange::GetRegisteredClosestCommonInclusiveAncestor() {
3109 MOZ_ASSERT(IsInAnySelection(),
3110 "GetRegisteredClosestCommonInclusiveAncestor only valid for range "
3112 MOZ_ASSERT(mRegisteredClosestCommonInclusiveAncestor
);
3113 return mRegisteredClosestCommonInclusiveAncestor
;
3117 bool nsRange::AutoInvalidateSelection::sIsNested
;
3119 nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() {
3120 if (!mCommonAncestor
) {
3124 ::InvalidateAllFrames(mCommonAncestor
);
3126 // Our range might not be in a selection anymore, because one of our selection
3127 // listeners might have gone ahead and run script of various sorts that messed
3128 // with selections, ranges, etc. But if it still is, we should check whether
3129 // we have a different common ancestor now, and if so invalidate its subtree
3130 // so it paints the selection it's in now.
3131 if (mRange
->IsInAnySelection()) {
3132 nsINode
* commonAncestor
=
3133 mRange
->GetRegisteredClosestCommonInclusiveAncestor();
3134 // XXXbz can commonAncestor really be null here? I wouldn't think so! If
3135 // it _were_, then in a debug build
3136 // GetRegisteredClosestCommonInclusiveAncestor() would have fatally
3138 if (commonAncestor
&& commonAncestor
!= mCommonAncestor
) {
3139 ::InvalidateAllFrames(commonAncestor
);
3145 already_AddRefed
<nsRange
> nsRange::Constructor(const GlobalObject
& aGlobal
,
3147 nsCOMPtr
<nsPIDOMWindowInner
> window
=
3148 do_QueryInterface(aGlobal
.GetAsSupports());
3149 if (!window
|| !window
->GetDoc()) {
3150 aRv
.Throw(NS_ERROR_FAILURE
);
3154 return window
->GetDoc()->CreateRange(aRv
);
3157 static bool ExcludeIfNextToNonSelectable(nsIContent
* aContent
) {
3158 return aContent
->IsText() &&
3159 aContent
->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE
);
3162 void nsRange::ExcludeNonSelectableNodes(nsTArray
<RefPtr
<nsRange
>>* aOutRanges
) {
3163 if (!mIsPositioned
) {
3167 MOZ_ASSERT(mEnd
.Container());
3168 MOZ_ASSERT(mStart
.Container());
3170 nsRange
* range
= this;
3171 RefPtr
<nsRange
> newRange
;
3173 PreContentIterator preOrderIter
;
3174 nsresult rv
= preOrderIter
.Init(range
);
3175 if (NS_FAILED(rv
)) {
3180 bool seenSelectable
= false;
3181 // |firstNonSelectableContent| is the first node in a consecutive sequence
3182 // of non-IsSelectable nodes. When we find a selectable node after such
3183 // a sequence we'll end the last nsRange, create a new one and restart
3185 nsIContent
* firstNonSelectableContent
= nullptr;
3187 nsINode
* node
= preOrderIter
.GetCurrentNode();
3188 preOrderIter
.Next();
3189 bool selectable
= true;
3190 nsIContent
* content
=
3191 node
&& node
->IsContent() ? node
->AsContent() : nullptr;
3193 if (firstNonSelectableContent
&&
3194 ExcludeIfNextToNonSelectable(content
)) {
3195 // Ignorable whitespace next to a sequence of non-selectable nodes
3196 // counts as non-selectable (bug 1216001).
3200 nsIFrame
* frame
= content
->GetPrimaryFrame();
3201 for (nsIContent
* p
= content
; !frame
&& (p
= p
->GetParent());) {
3202 frame
= p
->GetPrimaryFrame();
3205 selectable
= frame
->IsSelectable(nullptr);
3211 if (!firstNonSelectableContent
) {
3212 firstNonSelectableContent
= content
;
3214 if (preOrderIter
.IsDone()) {
3215 if (seenSelectable
) {
3216 // The tail end of the initial range is non-selectable - truncate
3217 // the current range before the first non-selectable node.
3218 range
->SetEndBefore(*firstNonSelectableContent
, IgnoreErrors());
3225 if (firstNonSelectableContent
) {
3226 if (range
== this && !seenSelectable
) {
3227 // This is the initial range and all its nodes until now are
3228 // non-selectable so just trim them from the start.
3229 IgnoredErrorResult err
;
3230 range
->SetStartBefore(*node
, err
, AllowRangeCrossShadowBoundary::Yes
);
3234 break; // restart the same range with a new iterator
3237 // Save the end point before truncating the range.
3238 nsINode
* endContainer
= range
->mEnd
.Container();
3239 const uint32_t endOffset
=
3240 *range
->mEnd
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
3242 // Truncate the current range before the first non-selectable node.
3243 IgnoredErrorResult err
;
3244 range
->SetEndBefore(*firstNonSelectableContent
, err
,
3245 AllowRangeCrossShadowBoundary::Yes
);
3247 // Store it in the result (strong ref) - do this before creating
3248 // a new range in |newRange| below so we don't drop the last ref
3249 // to the range created in the previous iteration.
3250 if (!added
&& !err
.Failed()) {
3251 aOutRanges
->AppendElement(range
);
3254 // Create a new range for the remainder.
3255 nsINode
* startContainer
= node
;
3256 Maybe
<uint32_t> startOffset
= Some(0);
3257 // Don't start *inside* a node with independent selection though
3259 if (content
&& content
->HasIndependentSelection()) {
3260 nsINode
* parent
= startContainer
->GetParent();
3262 startOffset
= parent
->ComputeIndexOf(startContainer
);
3263 startContainer
= parent
;
3267 nsRange::Create(startContainer
, startOffset
.valueOr(UINT32_MAX
),
3268 endContainer
, endOffset
, IgnoreErrors());
3269 if (!newRange
|| newRange
->Collapsed()) {
3273 break; // create a new iterator for the new range, if any
3276 seenSelectable
= true;
3279 aOutRanges
->AppendElement(range
);
3281 if (preOrderIter
.IsDone()) {
3288 struct InnerTextAccumulator
{
3289 explicit InnerTextAccumulator(mozilla::dom::DOMString
& aValue
)
3290 : mString(aValue
.AsAString()), mRequiredLineBreakCount(0) {}
3291 void FlushLineBreaks() {
3292 while (mRequiredLineBreakCount
> 0) {
3293 // Required line breaks at the start of the text are suppressed.
3294 if (!mString
.IsEmpty()) {
3295 mString
.Append('\n');
3297 --mRequiredLineBreakCount
;
3300 void Append(char aCh
) { Append(nsAutoString(aCh
)); }
3301 void Append(const nsAString
& aString
) {
3302 if (aString
.IsEmpty()) {
3306 mString
.Append(aString
);
3308 void AddRequiredLineBreakCount(int8_t aCount
) {
3309 mRequiredLineBreakCount
= std::max(mRequiredLineBreakCount
, aCount
);
3313 int8_t mRequiredLineBreakCount
;
3316 static bool IsVisibleAndNotInReplacedElement(nsIFrame
* aFrame
) {
3317 if (!aFrame
|| !aFrame
->StyleVisibility()->IsVisible() ||
3318 aFrame
->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
)) {
3321 if (aFrame
->HidesContent()) {
3324 for (nsIFrame
* f
= aFrame
->GetParent(); f
; f
= f
->GetParent()) {
3325 if (f
->HidesContent()) {
3328 if (f
->IsReplaced() &&
3329 !f
->GetContent()->IsAnyOfHTMLElements(nsGkAtoms::button
,
3330 nsGkAtoms::select
) &&
3331 !f
->GetContent()->IsSVGElement()) {
3338 static void AppendTransformedText(InnerTextAccumulator
& aResult
,
3339 nsIContent
* aContainer
) {
3340 auto textNode
= static_cast<CharacterData
*>(aContainer
);
3342 nsIFrame
* frame
= textNode
->GetPrimaryFrame();
3343 if (!IsVisibleAndNotInReplacedElement(frame
)) {
3347 nsIFrame::RenderedText text
=
3348 frame
->GetRenderedText(0, aContainer
->GetChildCount());
3349 aResult
.Append(text
.mString
);
3353 * States for tree traversal. AT_NODE means that we are about to enter
3354 * the current DOM node. AFTER_NODE means that we have just finished traversing
3355 * the children of the current DOM node and are about to apply any
3356 * "after processing the node's children" steps before we finish visiting
3359 enum TreeTraversalState
{ AT_NODE
, AFTER_NODE
};
3361 static int8_t GetRequiredInnerTextLineBreakCount(nsIFrame
* aFrame
) {
3362 if (aFrame
->GetContent()->IsHTMLElement(nsGkAtoms::p
)) {
3365 const nsStyleDisplay
* styleDisplay
= aFrame
->StyleDisplay();
3366 if (styleDisplay
->IsBlockOutside(aFrame
) ||
3367 styleDisplay
->mDisplay
== StyleDisplay::TableCaption
) {
3373 static bool IsLastCellOfRow(nsIFrame
* aFrame
) {
3374 LayoutFrameType type
= aFrame
->Type();
3375 if (type
!= LayoutFrameType::TableCell
) {
3378 for (nsIFrame
* c
= aFrame
; c
; c
= c
->GetNextContinuation()) {
3379 if (c
->GetNextSibling()) {
3386 static bool IsLastRowOfRowGroup(nsIFrame
* aFrame
) {
3387 if (!aFrame
->IsTableRowFrame()) {
3390 for (nsIFrame
* c
= aFrame
; c
; c
= c
->GetNextContinuation()) {
3391 if (c
->GetNextSibling()) {
3398 static bool IsLastNonemptyRowGroupOfTable(nsIFrame
* aFrame
) {
3399 if (!aFrame
->IsTableRowGroupFrame()) {
3402 for (nsIFrame
* c
= aFrame
; c
; c
= c
->GetNextContinuation()) {
3403 for (nsIFrame
* next
= c
->GetNextSibling(); next
;
3404 next
= next
->GetNextSibling()) {
3405 if (next
->PrincipalChildList().FirstChild()) {
3413 void nsRange::GetInnerTextNoFlush(DOMString
& aValue
, ErrorResult
& aError
,
3414 nsIContent
* aContainer
) {
3415 InnerTextAccumulator
result(aValue
);
3417 if (aContainer
->IsText()) {
3418 AppendTransformedText(result
, aContainer
);
3422 nsIContent
* currentNode
= aContainer
;
3423 TreeTraversalState currentState
= AFTER_NODE
;
3425 nsIContent
* endNode
= aContainer
;
3426 TreeTraversalState endState
= AFTER_NODE
;
3428 nsIContent
* firstChild
= aContainer
->GetFirstChild();
3430 currentNode
= firstChild
;
3431 currentState
= AT_NODE
;
3434 while (currentNode
!= endNode
|| currentState
!= endState
) {
3435 nsIFrame
* f
= currentNode
->GetPrimaryFrame();
3436 bool isVisibleAndNotReplaced
= IsVisibleAndNotInReplacedElement(f
);
3437 if (currentState
== AT_NODE
) {
3438 bool isText
= currentNode
->IsText();
3439 if (isVisibleAndNotReplaced
) {
3440 result
.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f
));
3442 nsIFrame::RenderedText text
= f
->GetRenderedText();
3443 result
.Append(text
.mString
);
3446 nsIContent
* child
= currentNode
->GetFirstChild();
3448 currentNode
= child
;
3451 currentState
= AFTER_NODE
;
3453 if (currentNode
== endNode
&& currentState
== endState
) {
3456 if (isVisibleAndNotReplaced
) {
3457 if (currentNode
->IsHTMLElement(nsGkAtoms::br
)) {
3458 result
.Append('\n');
3460 switch (f
->StyleDisplay()->DisplayInside()) {
3461 case StyleDisplayInside::TableCell
:
3462 if (!IsLastCellOfRow(f
)) {
3463 result
.Append('\t');
3466 case StyleDisplayInside::TableRow
:
3467 if (!IsLastRowOfRowGroup(f
) ||
3468 !IsLastNonemptyRowGroupOfTable(f
->GetParent())) {
3469 result
.Append('\n');
3473 break; // Do nothing
3475 result
.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f
));
3477 nsIContent
* next
= currentNode
->GetNextSibling();
3480 currentState
= AT_NODE
;
3482 currentNode
= currentNode
->GetParent();
3486 // Do not flush trailing line breaks! Required breaks at the end of the text
3490 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
3491 void nsRange::CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
3492 const mozilla::RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
3493 const mozilla::RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
) {
3494 if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
3498 MOZ_ASSERT(aStartBoundary
.IsSetAndValid() && aEndBoundary
.IsSetAndValid());
3500 nsINode
* startNode
= aStartBoundary
.Container();
3501 nsINode
* endNode
= aEndBoundary
.Container();
3503 if (!startNode
&& !endNode
) {
3504 ResetCrossShadowBoundaryRange();
3508 auto CanBecomeCrossShadowBoundaryPoint
= [](nsINode
* aContainer
) -> bool {
3513 // Unlike normal ranges, shadow cross ranges don't work
3514 // when the nodes aren't in document.
3515 if (!aContainer
->IsInComposedDoc()) {
3519 // AbstractRange::GetClosestCommonInclusiveAncestor only supports
3520 // Document and Content nodes.
3521 return aContainer
->IsDocument() || aContainer
->IsContent();
3524 if (!CanBecomeCrossShadowBoundaryPoint(startNode
) ||
3525 !CanBecomeCrossShadowBoundaryPoint(endNode
)) {
3526 ResetCrossShadowBoundaryRange();
3530 if (!mCrossShadowBoundaryRange
) {
3531 mCrossShadowBoundaryRange
=
3532 CrossShadowBoundaryRange::Create(aStartBoundary
, aEndBoundary
);
3536 mCrossShadowBoundaryRange
->SetStartAndEnd(aStartBoundary
, aEndBoundary
);