Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsRange.cpp
blob50e2af88634f3ba1b2e4b2b43f7b643fa2f98789
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/. */
7 /*
8 * Implementation of the DOM Range object.
9 */
11 #include "RangeBoundary.h"
12 #include "nscore.h"
13 #include "nsRange.h"
15 #include "nsDebug.h"
16 #include "nsString.h"
17 #include "nsReadableUtils.h"
18 #include "nsIContent.h"
19 #include "mozilla/dom/Document.h"
20 #include "nsError.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"
54 namespace mozilla {
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"));
70 } else {
71 MOZ_LOG(
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,
85 ErrorResult& aRv);
86 template already_AddRefed<nsRange> nsRange::Create(
87 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
88 ErrorResult& aRv);
89 template already_AddRefed<nsRange> nsRange::Create(
90 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
91 ErrorResult& aRv);
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();
155 break;
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;
161 break;
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");
192 /* static */
193 already_AddRefed<nsRange> nsRange::Create(nsINode* aNode) {
194 MOZ_ASSERT(aNode);
195 if (!sCachedRanges || sCachedRanges->IsEmpty()) {
196 return do_AddRef(new nsRange(aNode));
198 RefPtr<nsRange> range = sCachedRanges->PopLastElement().forget();
199 range->Init(aNode);
200 return range.forget();
203 /* static */
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())) {
213 return nullptr;
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,
226 * false otherwise
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
248 // or not.
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,
269 aNewBoundary);
271 if (order) {
272 if (*order != 1) {
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 =
303 aIsSetStart
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 /******************************************************
322 * nsISupports
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);
340 tmp->Reset();
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());
355 return interrupt;
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() &&
464 aInfo.mChangeStart <
465 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)) {
466 if (aInfo.mDetails) {
467 // splitText(), aInfo->mDetails->mNextSibling is the new text node
468 NS_ASSERTION(
469 aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit,
470 "only a split can start before the end");
471 NS_ASSERTION(
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) -
477 aInfo.mChangeStart;
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()) {
491 newStart.Container()
492 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
494 } else {
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) <=
499 aInfo.mChangeEnd) {
500 newStartOffset = aInfo.mChangeStart;
501 } else {
502 newStartOffset =
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() &&
518 aInfo.mChangeStart <
519 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)) {
520 if (aInfo.mDetails && (aContent->GetParentNode() || newStart.Container())) {
521 // splitText(), aInfo.mDetails->mNextSibling is the new text node
522 NS_ASSERTION(
523 aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit,
524 "only a split can start before the end");
525 MOZ_ASSERT(
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) -
532 aInfo.mChangeStart};
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());
542 newEnd.Container()
543 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
544 } else if (
545 mEnd.Container()
546 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
547 newEnd.Container()
548 ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
550 } else {
551 CheckedUint32 newEndOffset{0};
552 if (*mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets) <=
553 aInfo.mChangeEnd) {
554 newEndOffset = aInfo.mChangeStart;
555 } else {
556 newEndOffset =
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());
591 } else {
592 nsRange::AssertIfMismatchRootAndRangeBoundaries(
593 mStart, mEnd, mRoot,
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;
606 while (child) {
607 if (!child
608 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
609 MarkDescendants(*child);
610 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.
620 if (mNextStartRef) {
621 mStart = {mStart.Container(), mNextStartRef};
622 MOZ_ASSERT(mNextStartRef == aFirstNewContent);
623 mNextStartRef = nullptr;
625 if (mNextEndRef) {
626 mEnd = {mEnd.Container(), mNextEndRef};
627 MOZ_ASSERT(mNextEndRef == aFirstNewContent);
628 mNextEndRef = nullptr;
630 DoSetRange(mStart, mEnd, mRoot, true);
631 } else {
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
647 // inserted.
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() &&
659 !aChild
660 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
661 MarkDescendants(*aChild);
662 aChild->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
665 if (mNextStartRef || mNextEndRef) {
666 if (mNextStartRef) {
667 newStart = {mStart.Container(), mNextStartRef};
668 MOZ_ASSERT(mNextStartRef == aChild);
669 mNextStartRef = nullptr;
671 if (mNextEndRef) {
672 newEnd = {mEnd.Container(), mNextEndRef};
673 MOZ_ASSERT(mNextEndRef == aChild);
674 mNextEndRef = nullptr;
677 updateBoundaries = true;
680 if (updateBoundaries) {
681 DoSetRange(newStart, newEnd, mRoot);
682 } else {
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
698 // verify this.
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;
714 bool gravitateEnd;
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};
722 } else {
723 newStart.CopyFrom(mStart, RangeBoundaryIsMutationObserved::Yes);
724 newStart.InvalidateOffset();
726 } else {
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};
737 } else {
738 newEnd.CopyFrom(mEnd, RangeBoundaryIsMutationObserved::Yes);
739 newEnd.InvalidateOffset();
741 } else {
742 if (startContainer == endContainer && gravitateStart.isSome()) {
743 gravitateEnd = gravitateStart.value();
744 } else {
745 gravitateEnd = endContainer->IsInclusiveDescendantOf(aChild);
747 if (gravitateEnd) {
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);
757 } else {
758 nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart, mEnd, mRoot);
761 MOZ_ASSERT(mStart.Ref() != aChild);
762 MOZ_ASSERT(mEnd.Ref() != aChild);
764 if (container->IsMaybeSelected() &&
765 aChild
766 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
767 aChild
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.
787 Reset();
788 return;
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,
804 uint32_t aOffset,
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);
810 return false;
813 const bool isContainerInRange =
814 aContainer.IsInclusiveDescendantOf(mRoot) ||
815 (aAllowCrossShadowBoundary && mCrossShadowBoundaryRange &&
816 IsShadowIncludingInclusiveDescendantOfCrossBoundaryRangeAncestor(
817 aContainer));
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);
823 return false;
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 "
829 "content or not.");
830 if (aContainer.ChromeOnlyAccess() != chromeOnlyAccess) {
831 aRv.ThrowInvalidNodeTypeError(
832 "Trying to compare restricted with unrestricted nodes");
833 return false;
836 if (aContainer.NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
837 aRv.ThrowInvalidNodeTypeError("Trying to compare with a document");
838 return false;
841 if (aOffset > aContainer.Length()) {
842 aRv.ThrowIndexSizeError("Offset is out of bounds");
843 return false;
846 return true;
849 bool nsRange::IsPointInRange(const nsINode& aContainer, uint32_t aOffset,
850 ErrorResult& aRv,
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();
857 return false;
860 return compareResult == 0;
863 int16_t nsRange::ComparePoint(const nsINode& aContainer, uint32_t aOffset,
864 ErrorResult& aRv,
865 bool aAllowCrossShadowBoundary) const {
866 if (!IsPointComparableToRange(aContainer, aOffset, aAllowCrossShadowBoundary,
867 aRv)) {
868 return 0;
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()
877 : StartRef());
878 order && *order <= 0) {
879 return int16_t(*order);
881 if (Maybe<int32_t> order = nsContentUtils::ComparePoints(
882 aAllowCrossShadowBoundary ? MayCrossShadowBoundaryEndRef() : EndRef(),
883 point);
884 order && *order == -1) {
885 return 1;
887 return 0;
890 bool nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) {
891 if (!mIsPositioned) {
892 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
893 return false;
896 nsINode* parent = aNode.GetParentNode();
897 if (!parent) {
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()) {
904 return false;
907 if (!IsPointComparableToRange(*parent, *nodeIndex,
908 false /* aAllowCrossShadowBoundary */,
909 IgnoreErrors())) {
910 return false;
913 const Maybe<int32_t> startOrder = nsContentUtils::ComparePoints(
914 mStart.Container(),
915 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), parent,
916 *nodeIndex + 1u);
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);
924 return false;
927 void nsRange::NotifySelectionListenersAfterRangeSet() {
928 if (mSelections.IsEmpty()) {
929 return;
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);
937 mCalledByJS = false;
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()
948 : nullptr;
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,
955 IgnoreErrors());
956 } else {
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.
969 return;
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());
983 } else {
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 ******************************************************/
998 // static
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 */) {
1004 #ifdef DEBUG
1005 if (!aRootNode) {
1006 MOZ_ASSERT(!aStartBoundary.IsSet());
1007 MOZ_ASSERT(!aEndBoundary.IsSet());
1008 return;
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 */,
1048 CollapsePolicy
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) {
1060 if (mRoot) {
1061 mRoot->RemoveMutationObserver(this);
1063 if (aRootNode) {
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) {
1090 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);
1124 mCalledByJS = true;
1125 SetStart(aNode, aOffset, aErr);
1128 bool nsRange::CanAccess(const nsINode& aNode) const {
1129 if (nsContentUtils::LegacyIsCallerNativeCode()) {
1130 return true;
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);
1140 return;
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());
1151 if (!newRoot) {
1152 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1153 return;
1156 if (!aPoint.IsSetAndValid()) {
1157 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1158 return;
1161 CollapsePolicy policy =
1162 ShouldCollapseBoundary(this, newRoot, aPoint, true /* aIsSetStart= */,
1163 aAllowCrossShadowBoundary);
1165 switch (policy) {
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());
1174 } else {
1175 // The normal range is good enough for this case, just use that.
1176 ResetCrossShadowBoundaryRange();
1179 DoSetRange(aPoint, mEnd, mRoot, false, policy);
1180 break;
1181 case CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges:
1182 DoSetRange(aPoint, aPoint, newRoot, false, policy);
1183 break;
1184 case CollapsePolicy::DefaultRange:
1185 MOZ_ASSERT(aAllowCrossShadowBoundary ==
1186 AllowRangeCrossShadowBoundary::Yes);
1187 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
1188 aPoint, MayCrossShadowBoundaryEndRef());
1189 DoSetRange(aPoint, aPoint, newRoot, false, policy);
1190 break;
1191 default:
1192 MOZ_ASSERT_UNREACHABLE();
1196 void nsRange::SetStartAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset,
1197 ErrorResult& aErr) {
1198 AutoCalledByJSRestore calledByJSRestorer(*this);
1199 mCalledByJS = true;
1200 SetStart(aNode, aOffset, aErr, AllowRangeCrossShadowBoundary::Yes);
1203 void nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr) {
1204 AutoCalledByJSRestore calledByJSRestorer(*this);
1205 mCalledByJS = true;
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);
1214 return;
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);
1227 mCalledByJS = true;
1228 SetStartAfter(aNode, aErr);
1231 void nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv) {
1232 if (!CanAccess(aNode)) {
1233 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1234 return;
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);
1246 mCalledByJS = true;
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);
1254 return;
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());
1263 if (!newRoot) {
1264 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1265 return;
1268 if (!aPoint.IsSetAndValid()) {
1269 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1270 return;
1273 CollapsePolicy policy =
1274 ShouldCollapseBoundary(this, newRoot, aPoint, false /* aIsStartStart */,
1275 aAllowCrossShadowBoundary);
1277 switch (policy) {
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);
1286 } else {
1287 // The normal range is good enough for this case, just use that.
1288 ResetCrossShadowBoundaryRange();
1291 DoSetRange(mStart, aPoint, mRoot, false, policy);
1292 break;
1293 case CollapsePolicy::DefaultRangeAndCrossShadowBoundaryRanges:
1294 DoSetRange(aPoint, aPoint, newRoot, false, policy);
1295 break;
1296 case CollapsePolicy::DefaultRange:
1297 MOZ_ASSERT(aAllowCrossShadowBoundary ==
1298 AllowRangeCrossShadowBoundary::Yes);
1299 CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(
1300 MayCrossShadowBoundaryStartRef(), aPoint);
1301 DoSetRange(aPoint, aPoint, newRoot, false, policy);
1302 break;
1303 default:
1304 MOZ_ASSERT_UNREACHABLE();
1308 void nsRange::SetEndAllowCrossShadowBoundary(nsINode& aNode, uint32_t aOffset,
1309 ErrorResult& aErr) {
1310 AutoCalledByJSRestore calledByJSRestorer(*this);
1311 mCalledByJS = true;
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);
1328 if (!newRoot) {
1329 return;
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);
1339 mCalledByJS = true;
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);
1348 return;
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);
1361 mCalledByJS = true;
1362 SetEndAfter(aNode, aErr);
1365 void nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv) {
1366 if (!CanAccess(aNode)) {
1367 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1368 return;
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);
1382 if (aToStart) {
1383 DoSetRange(mStart, mStart, mRoot);
1384 } else {
1385 DoSetRange(mEnd, mEnd, mRoot);
1389 void nsRange::CollapseJS(bool aToStart) {
1390 AutoCalledByJSRestore calledByJSRestorer(*this);
1391 mCalledByJS = true;
1392 Collapse(aToStart);
1395 void nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr) {
1396 AutoCalledByJSRestore calledByJSRestorer(*this);
1397 mCalledByJS = true;
1398 SelectNode(aNode, aErr);
1401 void nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv) {
1402 if (!CanAccess(aNode)) {
1403 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1404 return;
1407 nsINode* container = aNode.GetParentNode();
1408 nsINode* newRoot = RangeUtils::ComputeRootNode(container);
1409 if (!newRoot) {
1410 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1411 return;
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);
1421 return;
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);
1431 mCalledByJS = true;
1432 SelectNodeContents(aNode, aErr);
1435 void nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv) {
1436 if (!CanAccess(aNode)) {
1437 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1438 return;
1441 nsINode* newRoot = RangeUtils::ComputeRootNode(&aNode);
1442 if (!newRoot) {
1443 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1444 return;
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
1463 // easy.
1465 class MOZ_STACK_CLASS RangeSubtreeIterator {
1466 private:
1467 enum RangeSubtreeIterState { eDone = 0, eUseStart, eUseIterator, eUseEnd };
1469 Maybe<ContentSubtreeIterator> mSubtreeIter;
1470 RangeSubtreeIterState mIterState;
1472 nsCOMPtr<nsINode> mStart;
1473 nsCOMPtr<nsINode> mEnd;
1475 public:
1476 RangeSubtreeIterator() : mIterState(eDone) {}
1477 ~RangeSubtreeIterator() = default;
1479 nsresult Init(nsRange* aRange);
1480 already_AddRefed<nsINode> GetCurrentNode();
1481 void First();
1482 void Last();
1483 void Next();
1484 void Prev();
1486 bool IsDone() { return mIterState == eDone; }
1489 nsresult RangeSubtreeIterator::Init(nsRange* aRange) {
1490 mIterState = eDone;
1491 if (aRange->Collapsed()) {
1492 return NS_OK;
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())) {
1511 mStart = node;
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)) {
1525 mEnd = node;
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
1531 // node once!
1533 mEnd = nullptr;
1534 } else {
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!
1555 First();
1557 return NS_OK;
1560 already_AddRefed<nsINode> RangeSubtreeIterator::GetCurrentNode() {
1561 nsCOMPtr<nsINode> node;
1563 if (mIterState == eUseStart && mStart) {
1564 node = mStart;
1565 } else if (mIterState == eUseEnd && mEnd) {
1566 node = mEnd;
1567 } else if (mIterState == eUseIterator && mSubtreeIter) {
1568 node = mSubtreeIter->GetCurrentNode();
1571 return node.forget();
1574 void RangeSubtreeIterator::First() {
1575 if (mStart)
1576 mIterState = eUseStart;
1577 else if (mSubtreeIter) {
1578 mSubtreeIter->First();
1580 mIterState = eUseIterator;
1581 } else if (mEnd)
1582 mIterState = eUseEnd;
1583 else
1584 mIterState = eDone;
1587 void RangeSubtreeIterator::Last() {
1588 if (mEnd)
1589 mIterState = eUseEnd;
1590 else if (mSubtreeIter) {
1591 mSubtreeIter->Last();
1593 mIterState = eUseIterator;
1594 } else if (mStart)
1595 mIterState = eUseStart;
1596 else
1597 mIterState = eDone;
1600 void RangeSubtreeIterator::Next() {
1601 if (mIterState == eUseStart) {
1602 if (mSubtreeIter) {
1603 mSubtreeIter->First();
1605 mIterState = eUseIterator;
1606 } else if (mEnd)
1607 mIterState = eUseEnd;
1608 else
1609 mIterState = eDone;
1610 } else if (mIterState == eUseIterator) {
1611 mSubtreeIter->Next();
1613 if (mSubtreeIter->IsDone()) {
1614 if (mEnd)
1615 mIterState = eUseEnd;
1616 else
1617 mIterState = eDone;
1619 } else
1620 mIterState = eDone;
1623 void RangeSubtreeIterator::Prev() {
1624 if (mIterState == eUseEnd) {
1625 if (mSubtreeIter) {
1626 mSubtreeIter->Last();
1628 mIterState = eUseIterator;
1629 } else if (mStart)
1630 mIterState = eUseStart;
1631 else
1632 mIterState = eDone;
1633 } else if (mIterState == eUseIterator) {
1634 mSubtreeIter->Prev();
1636 if (mSubtreeIter->IsDone()) {
1637 if (mStart)
1638 mIterState = eUseStart;
1639 else
1640 mIterState = eDone;
1642 } else
1643 mIterState = eDone;
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.
1673 return NS_OK;
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);
1696 return NS_OK;
1698 if (endContainer == commonAncestor) {
1699 aRange->Collapse(false);
1700 return NS_OK;
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!
1718 ErrorResult error;
1719 aRange->SelectNode(*nodeToSelect, error);
1720 if (error.Failed()) {
1721 return error.StealNSResult();
1724 aRange->Collapse(false);
1725 return NS_OK;
1728 NS_IMETHODIMP
1729 PrependChild(nsINode* aContainer, nsINode* aChild) {
1730 nsCOMPtr<nsINode> first = aContainer->GetFirstChild();
1731 ErrorResult rv;
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) {
1739 bool before, after;
1740 nsCOMPtr<nsINode> node = aIter.GetCurrentNode();
1741 if (!node) {
1742 // We don't have to worry that the node was removed if it doesn't exist,
1743 // e.g., the iterator is done.
1744 return true;
1747 nsresult rv = RangeUtils::CompareNodeToRange(node, aRange, &before, &after);
1748 if (NS_WARN_IF(NS_FAILED(rv))) {
1749 return false;
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()) {
1757 before = false;
1759 if (after && node == aRange->GetEndContainer()) {
1760 after = false;
1765 return !before && !after;
1768 void nsRange::CutContents(DocumentFragment** aFragment, ErrorResult& aRv) {
1769 if (aFragment) {
1770 *aFragment = nullptr;
1773 if (!CanAccess(*mStart.Container()) || !CanAccess(*mEnd.Container())) {
1774 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1775 return;
1778 nsCOMPtr<Document> doc = mStart.Container()->OwnerDoc();
1780 nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv);
1781 if (aRv.Failed()) {
1782 return;
1785 // If aFragment isn't null, create a temporary fragment to hold our return.
1786 RefPtr<DocumentFragment> retval;
1787 if (aFragment) {
1788 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);
1807 if (retval) {
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.
1820 if (doctype &&
1821 *nsContentUtils::ComparePoints(startContainer, startOffset, doctype,
1822 0) < 0 &&
1823 *nsContentUtils::ComparePoints(doctype, 0, endContainer, endOffset) <
1824 0) {
1825 aRv.ThrowHierarchyRequestError("Start or end position isn't valid.");
1826 return;
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);
1837 if (aRv.Failed()) {
1838 return;
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);
1847 return;
1850 iter.First();
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.
1867 iter.Next();
1868 nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
1869 while (nextNode && nextNode->IsInclusiveDescendantOf(node)) {
1870 iter.Next();
1871 nextNode = iter.GetCurrentNode();
1874 handled = false;
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) {
1891 if (retval) {
1892 nsAutoString cutValue;
1893 charData->SubstringData(startOffset, endOffset - startOffset,
1894 cutValue, aRv);
1895 if (NS_WARN_IF(aRv.Failed())) {
1896 return;
1898 nsCOMPtr<nsINode> clone = node->CloneNode(false, aRv);
1899 if (NS_WARN_IF(aRv.Failed())) {
1900 return;
1902 clone->SetNodeValue(cutValue, aRv);
1903 if (NS_WARN_IF(aRv.Failed())) {
1904 return;
1906 nodeToResult = clone;
1909 nsMutationGuard guard;
1910 charData->DeleteData(startOffset, endOffset - startOffset, aRv);
1911 if (NS_WARN_IF(aRv.Failed())) {
1912 return;
1914 if (guard.Mutated(0) && !ValidateCurrentNode(this, iter)) {
1915 aRv.Throw(NS_ERROR_UNEXPECTED);
1916 return;
1920 handled = true;
1921 } else {
1922 // Delete or extract everything after startOffset.
1924 dataLength = charData->Length();
1926 if (dataLength >= startOffset) {
1927 if (retval) {
1928 nsAutoString cutValue;
1929 charData->SubstringData(startOffset, dataLength, cutValue, aRv);
1930 if (NS_WARN_IF(aRv.Failed())) {
1931 return;
1933 nsCOMPtr<nsINode> clone = node->CloneNode(false, aRv);
1934 if (NS_WARN_IF(aRv.Failed())) {
1935 return;
1937 clone->SetNodeValue(cutValue, aRv);
1938 if (NS_WARN_IF(aRv.Failed())) {
1939 return;
1941 nodeToResult = clone;
1944 nsMutationGuard guard;
1945 charData->DeleteData(startOffset, dataLength, aRv);
1946 if (NS_WARN_IF(aRv.Failed())) {
1947 return;
1949 if (guard.Mutated(0) && !ValidateCurrentNode(this, iter)) {
1950 aRv.Throw(NS_ERROR_UNEXPECTED);
1951 return;
1955 handled = true;
1957 } else if (node == endContainer) {
1958 // Delete or extract everything before endOffset.
1959 if (retval) {
1960 nsAutoString cutValue;
1961 charData->SubstringData(0, endOffset, cutValue, aRv);
1962 if (NS_WARN_IF(aRv.Failed())) {
1963 return;
1965 nsCOMPtr<nsINode> clone = node->CloneNode(false, aRv);
1966 if (NS_WARN_IF(aRv.Failed())) {
1967 return;
1969 clone->SetNodeValue(cutValue, aRv);
1970 if (NS_WARN_IF(aRv.Failed())) {
1971 return;
1973 nodeToResult = clone;
1976 nsMutationGuard guard;
1977 charData->DeleteData(0, endOffset, aRv);
1978 if (NS_WARN_IF(aRv.Failed())) {
1979 return;
1981 if (guard.Mutated(0) && !ValidateCurrentNode(this, iter)) {
1982 aRv.Throw(NS_ERROR_UNEXPECTED);
1983 return;
1985 handled = true;
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))) {
1994 if (retval) {
1995 nodeToResult = node->CloneNode(false, aRv);
1996 if (aRv.Failed()) {
1997 return;
2000 handled = true;
2004 if (!handled) {
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'.
2012 if (retval) {
2013 nsCOMPtr<nsINode> oldCommonAncestor = commonAncestor;
2014 if (!iter.IsDone()) {
2015 // Setup the parameters for the next iteration of the loop.
2016 if (!nextNode) {
2017 aRv.Throw(NS_ERROR_UNEXPECTED);
2018 return;
2021 // Get node's and nextNode's common parent. Do this before moving
2022 // nodes from original DOM to result fragment.
2023 commonAncestor =
2024 nsContentUtils::GetClosestCommonInclusiveAncestor(node, nextNode);
2025 if (!commonAncestor) {
2026 aRv.Throw(NS_ERROR_UNEXPECTED);
2027 return;
2030 nsCOMPtr<nsINode> parentCounterNode = node;
2031 while (parentCounterNode && parentCounterNode != commonAncestor) {
2032 ++parentCount;
2033 parentCounterNode = parentCounterNode->GetParentNode();
2034 if (!parentCounterNode) {
2035 aRv.Throw(NS_ERROR_UNEXPECTED);
2036 return;
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));
2046 if (aRv.Failed()) {
2047 return;
2050 if (farthestAncestor) {
2051 commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
2052 if (NS_WARN_IF(aRv.Failed())) {
2053 return;
2057 nsMutationGuard guard;
2058 nsCOMPtr<nsINode> parent = nodeToResult->GetParentNode();
2059 if (closestAncestor) {
2060 closestAncestor->AppendChild(*nodeToResult, aRv);
2061 } else {
2062 commonCloneAncestor->AppendChild(*nodeToResult, aRv);
2064 if (NS_WARN_IF(aRv.Failed())) {
2065 return;
2067 if (guard.Mutated(parent ? 2 : 1) && !ValidateCurrentNode(this, iter)) {
2068 aRv.Throw(NS_ERROR_UNEXPECTED);
2069 return;
2071 } else if (nodeToResult) {
2072 nsMutationGuard guard;
2073 nsCOMPtr<nsINode> node = nodeToResult;
2074 nsCOMPtr<nsINode> parent = node->GetParentNode();
2075 if (parent) {
2076 parent->RemoveChild(*node, aRv);
2077 if (aRv.Failed()) {
2078 return;
2081 if (guard.Mutated(1) && !ValidateCurrentNode(this, iter)) {
2082 aRv.Throw(NS_ERROR_UNEXPECTED);
2083 return;
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);
2094 return;
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,
2117 ErrorResult& aRv) {
2118 if (!mIsPositioned || !aOtherRange.IsPositioned()) {
2119 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
2120 return 0;
2123 nsINode *ourNode, *otherNode;
2124 uint32_t ourOffset, otherOffset;
2126 switch (aHow) {
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();
2132 break;
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();
2138 break;
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();
2144 break;
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();
2150 break;
2151 default:
2152 // We were passed an illegal value
2153 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2154 return 0;
2157 if (mRoot != aOtherRange.GetRoot()) {
2158 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
2159 return 0;
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.
2168 return *order;
2171 /* static */
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--) {
2194 ErrorResult rv;
2195 nsCOMPtr<nsINode> clone = parentStack[i]->CloneNode(false, rv);
2197 if (rv.Failed()) {
2198 return rv.StealNSResult();
2200 if (!clone) {
2201 return NS_ERROR_FAILURE;
2204 if (!lastParent) {
2205 lastParent = clone;
2206 } else {
2207 firstParent->AppendChild(*clone, rv);
2208 if (rv.Failed()) {
2209 return rv.StealNSResult();
2213 firstParent = clone;
2216 firstParent.forget(aClosestAncestor);
2217 lastParent.forget(aFarthestAncestor);
2219 return NS_OK;
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.");
2228 if (!doc) {
2229 aRv.Throw(NS_ERROR_FAILURE);
2230 return nullptr;
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());
2239 if (Collapsed()) {
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);
2251 if (aRv.Failed()) {
2252 return nullptr;
2255 if (iter.IsDone()) {
2256 // There's nothing to add to the doc frag, we must be done!
2257 return clonedFrag.forget();
2260 iter.First();
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();
2274 bool deepClone =
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);
2285 if (aRv.Failed()) {
2286 return nullptr;
2289 // If it's CharacterData, make sure we only clone what
2290 // is in the range.
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
2298 // data after it.
2300 uint32_t dataLength = charData->Length();
2301 if (dataLength >
2302 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets)) {
2303 charData->DeleteData(
2304 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
2305 dataLength -
2306 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
2307 aRv);
2308 if (aRv.Failed()) {
2309 return nullptr;
2314 if (node == mStart.Container()) {
2315 // We don't need any data before mStartOffset, so just
2316 // delete it!
2318 if (*mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets) > 0) {
2319 charData->DeleteData(
2320 0, *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
2321 aRv);
2322 if (aRv.Failed()) {
2323 return nullptr;
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));
2337 if (aRv.Failed()) {
2338 return nullptr;
2341 // Hook the parent hierarchy/context of the subtree into the clone tree.
2343 if (farthestAncestor) {
2344 commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
2346 if (aRv.Failed()) {
2347 return nullptr;
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);
2359 } else {
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);
2365 if (aRv.Failed()) {
2366 return nullptr;
2369 // Get the next subtree to be processed. The idea here is to setup
2370 // the parameters for the next iteration of the loop.
2372 iter.Next();
2374 if (iter.IsDone()) break; // We must be done!
2376 nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
2377 if (!nextNode) {
2378 aRv.Throw(NS_ERROR_FAILURE);
2379 return nullptr;
2382 // Get node and nextNode's common parent.
2383 commonAncestor =
2384 nsContentUtils::GetClosestCommonInclusiveAncestor(node, nextNode);
2386 if (!commonAncestor) {
2387 aRv.Throw(NS_ERROR_FAILURE);
2388 return nullptr;
2391 // Find the equivalent of commonAncestor in the cloned tree!
2393 while (node && node != commonAncestor) {
2394 node = node->GetParentNode();
2395 if (aRv.Failed()) {
2396 return nullptr;
2399 if (!node) {
2400 aRv.Throw(NS_ERROR_FAILURE);
2401 return nullptr;
2404 cloneNode = cloneNode->GetParentNode();
2405 if (!cloneNode) {
2406 aRv.Throw(NS_ERROR_FAILURE);
2407 return nullptr;
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);
2431 return;
2434 if (!IsPositioned()) {
2435 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
2436 return;
2439 uint32_t tStartOffset = StartOffset();
2441 nsCOMPtr<nsINode> tStartContainer = GetStartContainer();
2443 if (!CanAccess(*tStartContainer)) {
2444 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
2445 return;
2448 if (&aNode == tStartContainer) {
2449 aRv.ThrowHierarchyRequestError(
2450 "The inserted node can not be range's start node.");
2451 return;
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.");
2465 return;
2468 referenceParentNode->EnsurePreInsertionValidity(aNode, tStartContainer,
2469 aRv);
2470 if (aRv.Failed()) {
2471 return;
2474 RefPtr<Text> secondPart = startTextNode->SplitText(tStartOffset, aRv);
2475 if (aRv.Failed()) {
2476 return;
2479 referenceNode = secondPart;
2480 } else {
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);
2487 if (aRv.Failed()) {
2488 return;
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).
2495 uint32_t newOffset;
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);
2501 return;
2503 newOffset = *indexInParent;
2504 } else {
2505 newOffset = tChildList->Length();
2508 if (aNode.NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
2509 newOffset += aNode.GetChildCount();
2510 } else {
2511 newOffset++;
2514 // Now actually insert the node
2515 nsCOMPtr<nsINode> tResultNode;
2516 tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv);
2517 if (aRv.Failed()) {
2518 return;
2521 if (Collapsed()) {
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);
2529 return;
2532 if (!mRoot) {
2533 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2534 return;
2536 // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text
2537 // node.
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);
2550 return;
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);
2561 return;
2564 // Extract the contents within the range.
2566 RefPtr<DocumentFragment> docFrag = ExtractContents(aRv);
2568 if (aRv.Failed()) {
2569 return;
2572 if (!docFrag) {
2573 aRv.Throw(NS_ERROR_FAILURE);
2574 return;
2577 // Spec says we need to remove all of aNewParent's
2578 // children prior to insertion.
2580 nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes();
2581 if (!children) {
2582 aRv.Throw(NS_ERROR_FAILURE);
2583 return;
2586 uint32_t numChildren = children->Length();
2588 while (numChildren) {
2589 nsCOMPtr<nsINode> child = children->Item(--numChildren);
2590 if (!child) {
2591 aRv.Throw(NS_ERROR_FAILURE);
2592 return;
2595 aNewParent.RemoveChild(*child, aRv);
2596 if (aRv.Failed()) {
2597 return;
2601 // Insert aNewParent at the range's start point.
2603 InsertNode(aNewParent, aRv);
2604 if (aRv.Failed()) {
2605 return;
2608 // Append the content we extracted under aNewParent.
2609 aNewParent.AppendChild(*docFrag, aRv);
2610 if (aRv.Failed()) {
2611 return;
2614 // Select aNewParent, and its contents.
2616 SelectNode(aNewParent, aRv);
2619 void nsRange::ToString(nsAString& aReturn, ErrorResult& aErr) {
2620 // clear the string
2621 aReturn.Truncate();
2623 // If we're unpositioned, return the empty string
2624 if (!mIsPositioned) {
2625 return;
2628 #ifdef DEBUG_range
2629 printf("Range dump: -----------------------\n");
2630 #endif /* DEBUG */
2632 // effeciency hack for simple case
2633 if (mStart.Container() == mEnd.Container()) {
2634 Text* textNode =
2635 mStart.Container() ? mStart.Container()->GetAsText() : nullptr;
2637 if (textNode) {
2638 #ifdef DEBUG_range
2639 // If debug, dump it:
2640 textNode->List(stdout);
2641 printf("End Range dump: -----------------------\n");
2642 #endif /* DEBUG */
2644 // grab the text
2645 textNode->SubstringData(
2646 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
2647 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets) -
2648 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
2649 aReturn, aErr);
2650 return;
2654 /* complex case: mStart.Container() != mEnd.Container(), or mStartParent not a
2655 text node revisit - there are potential optimizations here and also
2656 tradeoffs.
2659 PostContentIterator postOrderIter;
2660 nsresult rv = postOrderIter.Init(this);
2661 if (NS_WARN_IF(NS_FAILED(rv))) {
2662 aErr.Throw(rv);
2663 return;
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();
2673 #ifdef DEBUG_range
2674 // If debug, dump it:
2675 n->List(stdout);
2676 #endif /* DEBUG */
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),
2684 strLength -
2685 *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
2686 tempString, IgnoreErrors());
2687 aReturn += tempString;
2688 } else if (n ==
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;
2701 #ifdef DEBUG_range
2702 printf("End Range dump: -----------------------\n");
2703 #endif /* DEBUG */
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);
2712 return nullptr;
2715 return nsContentUtils::CreateContextualFragment(mStart.Container(), aFragment,
2716 false, aRv);
2719 static void ExtractRectFromOffset(nsIFrame* aFrame, const int32_t aOffset,
2720 nsRect* aR, bool aFlushToOriginEdge,
2721 bool aClampToEdge) {
2722 MOZ_ASSERT(aFrame);
2723 MOZ_ASSERT(aR);
2725 nsPoint point;
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.
2735 if (isVertical) {
2736 aR->SetHeight(0);
2737 aR->y = point.y;
2738 } else {
2739 aR->SetWidth(0);
2740 aR->x = point.x;
2742 return;
2745 if (aClampToEdge) {
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
2753 // origin.
2754 if (isVertical) {
2755 aR->SetHeight(point.y - aR->y);
2756 } else {
2757 aR->SetWidth(point.x - aR->x);
2759 } else {
2760 // The output rect should be flush to the edge of aR opposite the origin.
2761 if (isVertical) {
2762 aR->SetHeight(aR->YMost() - point.y);
2763 aR->y = point.y;
2764 } else {
2765 aR->SetWidth(aR->XMost() - point.x);
2766 aR->x = point.x;
2771 static nsTextFrame* GetTextFrameForContent(nsIContent* aContent,
2772 bool aFlushLayout) {
2773 RefPtr<Document> doc = aContent->OwnerDoc();
2774 PresShell* presShell = doc->GetPresShell();
2775 if (!presShell) {
2776 return nullptr;
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.
2783 if (aFlushLayout) {
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()) {
2795 return nullptr;
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);
2806 if (textFrame) {
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) {
2814 continue;
2816 if (fstart >= aEndOffset) {
2817 break;
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,
2834 aClampToEdge);
2835 textContentStart = aStartOffset;
2837 if (fend > aEndOffset) {
2838 // aEndOffset is in the middle of this frame
2839 ExtractRectFromOffset(f, aEndOffset, &r, topLeftToBottomRight,
2840 aClampToEdge);
2841 textContentEnd = aEndOffset;
2843 r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, relativeTo);
2844 aCallback->AddRect(r);
2846 // Finally capture the text, if requested.
2847 if (aTextList) {
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);
2858 return NS_OK;
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);
2866 if (!content) {
2867 return;
2870 const bool isText = content->IsText();
2871 if (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);
2879 return;
2882 if (aNode == aEndContainer) {
2883 GetPartialTextRect(aCollector, aTextList, content, 0,
2884 static_cast<int32_t>(aEndOffset), aClampToEdge,
2885 aFlushLayout);
2886 return;
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);
2895 if (isText) {
2896 return;
2898 aTextOnly = true;
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()) {
2905 return;
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);
2917 /* static */
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()) {
2933 return;
2936 if (aFlushLayout) {
2937 aStartContainer->OwnerDoc()->FlushPendingNotifications(FlushType::Layout);
2938 // Recheck whether we're still in the document
2939 if (!aStartContainer->IsInComposedDoc()) {
2940 return;
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);
2954 if (textFrame) {
2955 int32_t outOffset;
2956 nsIFrame* outFrame;
2957 textFrame->GetChildFrameContainingOffset(
2958 static_cast<int32_t>(aStartOffset), false, &outOffset, &outFrame);
2959 if (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);
2965 r.SetWidth(0);
2966 r = nsLayoutUtils::TransformFrameRectToAncestor(outFrame, r,
2967 relativeTo);
2968 aCollector->AddRect(r);
2972 return;
2975 do {
2976 nsCOMPtr<nsINode> node = iter.GetCurrentNode();
2977 iter.Next();
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),
2996 mEnd.Container(),
2997 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), aClampToEdge,
2998 aFlushLayout);
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) {
3009 return nullptr;
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),
3019 mEnd.Container(),
3020 *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets), aClampToEdge,
3021 aFlushLayout);
3022 return rectList.forget();
3025 void nsRange::GetClientRectsAndTexts(mozilla::dom::ClientRectsAndTexts& aResult,
3026 ErrorResult& aErr) {
3027 if (!mIsPositioned) {
3028 return;
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),
3038 mEnd.Container(),
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();
3070 iter.Next();
3072 nsCOMPtr<nsIContent> content = do_QueryInterface(node);
3073 if (!content) {
3074 continue;
3076 nsIFrame* frame = content->GetPrimaryFrame();
3077 if (!frame) {
3078 continue;
3081 if (content->IsText()) {
3082 if (node == startContainer) {
3083 int32_t offset =
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);
3091 continue;
3093 if (node == endContainer) {
3094 nsLayoutUtils::GetFontFacesForText(
3095 frame, 0, *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets),
3096 true, aResult, fontFaces, aMaxRanges, aSkipCollapsedWhitespace);
3097 continue;
3101 nsLayoutUtils::GetFontFacesForFrames(frame, aResult, fontFaces, aMaxRanges,
3102 aSkipCollapsedWhitespace);
3105 return NS_OK;
3108 nsINode* nsRange::GetRegisteredClosestCommonInclusiveAncestor() {
3109 MOZ_ASSERT(IsInAnySelection(),
3110 "GetRegisteredClosestCommonInclusiveAncestor only valid for range "
3111 "in selection");
3112 MOZ_ASSERT(mRegisteredClosestCommonInclusiveAncestor);
3113 return mRegisteredClosestCommonInclusiveAncestor;
3116 /* static */
3117 bool nsRange::AutoInvalidateSelection::sIsNested;
3119 nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() {
3120 if (!mCommonAncestor) {
3121 return;
3123 sIsNested = false;
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
3137 // asserted.
3138 if (commonAncestor && commonAncestor != mCommonAncestor) {
3139 ::InvalidateAllFrames(commonAncestor);
3144 /* static */
3145 already_AddRefed<nsRange> nsRange::Constructor(const GlobalObject& aGlobal,
3146 ErrorResult& aRv) {
3147 nsCOMPtr<nsPIDOMWindowInner> window =
3148 do_QueryInterface(aGlobal.GetAsSupports());
3149 if (!window || !window->GetDoc()) {
3150 aRv.Throw(NS_ERROR_FAILURE);
3151 return nullptr;
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) {
3164 MOZ_ASSERT(false);
3165 return;
3167 MOZ_ASSERT(mEnd.Container());
3168 MOZ_ASSERT(mStart.Container());
3170 nsRange* range = this;
3171 RefPtr<nsRange> newRange;
3172 while (range) {
3173 PreContentIterator preOrderIter;
3174 nsresult rv = preOrderIter.Init(range);
3175 if (NS_FAILED(rv)) {
3176 return;
3179 bool added = false;
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
3184 // the outer loop.
3185 nsIContent* firstNonSelectableContent = nullptr;
3186 while (true) {
3187 nsINode* node = preOrderIter.GetCurrentNode();
3188 preOrderIter.Next();
3189 bool selectable = true;
3190 nsIContent* content =
3191 node && node->IsContent() ? node->AsContent() : nullptr;
3192 if (content) {
3193 if (firstNonSelectableContent &&
3194 ExcludeIfNextToNonSelectable(content)) {
3195 // Ignorable whitespace next to a sequence of non-selectable nodes
3196 // counts as non-selectable (bug 1216001).
3197 selectable = false;
3199 if (selectable) {
3200 nsIFrame* frame = content->GetPrimaryFrame();
3201 for (nsIContent* p = content; !frame && (p = p->GetParent());) {
3202 frame = p->GetPrimaryFrame();
3204 if (frame) {
3205 selectable = frame->IsSelectable(nullptr);
3210 if (!selectable) {
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());
3220 return;
3222 continue;
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);
3231 if (err.Failed()) {
3232 return;
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
3258 // (e.g. <input>).
3259 if (content && content->HasIndependentSelection()) {
3260 nsINode* parent = startContainer->GetParent();
3261 if (parent) {
3262 startOffset = parent->ComputeIndexOf(startContainer);
3263 startContainer = parent;
3266 newRange =
3267 nsRange::Create(startContainer, startOffset.valueOr(UINT32_MAX),
3268 endContainer, endOffset, IgnoreErrors());
3269 if (!newRange || newRange->Collapsed()) {
3270 newRange = nullptr;
3272 range = newRange;
3273 break; // create a new iterator for the new range, if any
3276 seenSelectable = true;
3277 if (!added) {
3278 added = true;
3279 aOutRanges->AppendElement(range);
3281 if (preOrderIter.IsDone()) {
3282 return;
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()) {
3303 return;
3305 FlushLineBreaks();
3306 mString.Append(aString);
3308 void AddRequiredLineBreakCount(int8_t aCount) {
3309 mRequiredLineBreakCount = std::max(mRequiredLineBreakCount, aCount);
3312 nsAString& mString;
3313 int8_t mRequiredLineBreakCount;
3316 static bool IsVisibleAndNotInReplacedElement(nsIFrame* aFrame) {
3317 if (!aFrame || !aFrame->StyleVisibility()->IsVisible() ||
3318 aFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
3319 return false;
3321 if (aFrame->HidesContent()) {
3322 return false;
3324 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3325 if (f->HidesContent()) {
3326 return false;
3328 if (f->IsReplaced() &&
3329 !f->GetContent()->IsAnyOfHTMLElements(nsGkAtoms::button,
3330 nsGkAtoms::select) &&
3331 !f->GetContent()->IsSVGElement()) {
3332 return false;
3335 return true;
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)) {
3344 return;
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
3357 * the node.
3359 enum TreeTraversalState { AT_NODE, AFTER_NODE };
3361 static int8_t GetRequiredInnerTextLineBreakCount(nsIFrame* aFrame) {
3362 if (aFrame->GetContent()->IsHTMLElement(nsGkAtoms::p)) {
3363 return 2;
3365 const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
3366 if (styleDisplay->IsBlockOutside(aFrame) ||
3367 styleDisplay->mDisplay == StyleDisplay::TableCaption) {
3368 return 1;
3370 return 0;
3373 static bool IsLastCellOfRow(nsIFrame* aFrame) {
3374 LayoutFrameType type = aFrame->Type();
3375 if (type != LayoutFrameType::TableCell) {
3376 return true;
3378 for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
3379 if (c->GetNextSibling()) {
3380 return false;
3383 return true;
3386 static bool IsLastRowOfRowGroup(nsIFrame* aFrame) {
3387 if (!aFrame->IsTableRowFrame()) {
3388 return true;
3390 for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
3391 if (c->GetNextSibling()) {
3392 return false;
3395 return true;
3398 static bool IsLastNonemptyRowGroupOfTable(nsIFrame* aFrame) {
3399 if (!aFrame->IsTableRowGroupFrame()) {
3400 return true;
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()) {
3406 return false;
3410 return true;
3413 void nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError,
3414 nsIContent* aContainer) {
3415 InnerTextAccumulator result(aValue);
3417 if (aContainer->IsText()) {
3418 AppendTransformedText(result, aContainer);
3419 return;
3422 nsIContent* currentNode = aContainer;
3423 TreeTraversalState currentState = AFTER_NODE;
3425 nsIContent* endNode = aContainer;
3426 TreeTraversalState endState = AFTER_NODE;
3428 nsIContent* firstChild = aContainer->GetFirstChild();
3429 if (firstChild) {
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));
3441 if (isText) {
3442 nsIFrame::RenderedText text = f->GetRenderedText();
3443 result.Append(text.mString);
3446 nsIContent* child = currentNode->GetFirstChild();
3447 if (child) {
3448 currentNode = child;
3449 continue;
3451 currentState = AFTER_NODE;
3453 if (currentNode == endNode && currentState == endState) {
3454 break;
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');
3465 break;
3466 case StyleDisplayInside::TableRow:
3467 if (!IsLastRowOfRowGroup(f) ||
3468 !IsLastNonemptyRowGroupOfTable(f->GetParent())) {
3469 result.Append('\n');
3471 break;
3472 default:
3473 break; // Do nothing
3475 result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
3477 nsIContent* next = currentNode->GetNextSibling();
3478 if (next) {
3479 currentNode = next;
3480 currentState = AT_NODE;
3481 } else {
3482 currentNode = currentNode->GetParent();
3486 // Do not flush trailing line breaks! Required breaks at the end of the text
3487 // are suppressed.
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()) {
3495 return;
3498 MOZ_ASSERT(aStartBoundary.IsSetAndValid() && aEndBoundary.IsSetAndValid());
3500 nsINode* startNode = aStartBoundary.Container();
3501 nsINode* endNode = aEndBoundary.Container();
3503 if (!startNode && !endNode) {
3504 ResetCrossShadowBoundaryRange();
3505 return;
3508 auto CanBecomeCrossShadowBoundaryPoint = [](nsINode* aContainer) -> bool {
3509 if (!aContainer) {
3510 return true;
3513 // Unlike normal ranges, shadow cross ranges don't work
3514 // when the nodes aren't in document.
3515 if (!aContainer->IsInComposedDoc()) {
3516 return false;
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();
3527 return;
3530 if (!mCrossShadowBoundaryRange) {
3531 mCrossShadowBoundaryRange =
3532 CrossShadowBoundaryRange::Create(aStartBoundary, aEndBoundary);
3533 return;
3536 mCrossShadowBoundaryRange->SetStartAndEnd(aStartBoundary, aEndBoundary);