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