1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "SelectionState.h"
8 #include "AutoClonedRangeArray.h" // for AutoClonedRangeArray
9 #include "EditorUtils.h" // for EditorUtils
10 #include "EditorLineBreak.h" // for EditorLineBreak
11 #include "HTMLEditHelpers.h" // for DeleteRangeResult
13 #include "ErrorList.h"
14 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
15 #include "mozilla/IntegerRange.h" // for IntegerRange
16 #include "mozilla/Likely.h" // For MOZ_LIKELY and MOZ_UNLIKELY
17 #include "mozilla/RangeUtils.h" // for RangeUtils
18 #include "mozilla/dom/RangeBinding.h"
19 #include "mozilla/dom/Selection.h" // for Selection
20 #include "nsAString.h" // for nsAString::Length
21 #include "nsCycleCollectionParticipant.h"
22 #include "nsDebug.h" // for NS_WARNING, etc.
23 #include "nsError.h" // for NS_OK, etc.
24 #include "nsIContent.h" // for nsIContent
25 #include "nsISupportsImpl.h" // for nsRange::Release
26 #include "nsRange.h" // for nsRange
32 /*****************************************************************************
34 *****************************************************************************/
36 nsINode
* RangeItem::GetRoot() const {
37 if (MOZ_UNLIKELY(!IsPositioned())) {
40 nsINode
* rootNode
= RangeUtils::ComputeRootNode(mStartContainer
);
41 if (mStartContainer
== mEndContainer
) {
44 return MOZ_LIKELY(rootNode
== RangeUtils::ComputeRootNode(mEndContainer
))
49 /******************************************************************************
50 * mozilla::SelectionState
52 * Class for recording selection info. Stores selection as collection of
53 * { {startnode, startoffset} , {endnode, endoffset} } tuples. Can't store
54 * ranges since dom gravity will possibly change the ranges.
55 ******************************************************************************/
57 template nsresult
RangeUpdater::SelAdjCreateNode(const EditorDOMPoint
& aPoint
);
58 template nsresult
RangeUpdater::SelAdjCreateNode(
59 const EditorRawDOMPoint
& aPoint
);
60 template nsresult
RangeUpdater::SelAdjInsertNode(const EditorDOMPoint
& aPoint
);
61 template nsresult
RangeUpdater::SelAdjInsertNode(
62 const EditorRawDOMPoint
& aPoint
);
64 SelectionState::SelectionState(const AutoClonedSelectionRangeArray
& aRanges
)
65 : mDirection(aRanges
.GetDirection()) {
66 mArray
.SetCapacity(aRanges
.Ranges().Length());
67 for (const OwningNonNull
<nsRange
>& range
: aRanges
.Ranges()) {
68 RefPtr
<RangeItem
> rangeItem
= new RangeItem();
69 rangeItem
->StoreRange(range
);
70 mArray
.AppendElement(std::move(rangeItem
));
74 void SelectionState::SaveSelection(Selection
& aSelection
) {
75 // if we need more items in the array, new them
76 if (mArray
.Length() < aSelection
.RangeCount()) {
77 for (uint32_t i
= mArray
.Length(); i
< aSelection
.RangeCount(); i
++) {
78 mArray
.AppendElement();
79 mArray
[i
] = new RangeItem();
81 } else if (mArray
.Length() > aSelection
.RangeCount()) {
82 // else if we have too many, delete them
83 mArray
.TruncateLength(aSelection
.RangeCount());
86 // now store the selection ranges
87 const uint32_t rangeCount
= aSelection
.RangeCount();
88 for (const uint32_t i
: IntegerRange(rangeCount
)) {
89 MOZ_ASSERT(aSelection
.RangeCount() == rangeCount
);
90 const nsRange
* range
= aSelection
.GetRangeAt(i
);
92 if (MOZ_UNLIKELY(NS_WARN_IF(!range
))) {
95 mArray
[i
]->StoreRange(*range
);
98 mDirection
= aSelection
.GetDirection();
101 nsresult
SelectionState::RestoreSelection(Selection
& aSelection
) {
102 // clear out selection
103 IgnoredErrorResult ignoredError
;
104 aSelection
.RemoveAllRanges(ignoredError
);
105 NS_WARNING_ASSERTION(!ignoredError
.Failed(),
106 "Selection::RemoveAllRanges() failed, but ignored");
108 aSelection
.SetDirection(mDirection
);
111 const CopyableAutoTArray
<RefPtr
<RangeItem
>, 10> rangeItems(mArray
);
112 for (const RefPtr
<RangeItem
>& rangeItem
: rangeItems
) {
113 RefPtr
<nsRange
> range
= rangeItem
->GetRange();
115 NS_WARNING("RangeItem::GetRange() failed");
116 return NS_ERROR_FAILURE
;
118 aSelection
.AddRangeAndSelectFramesAndNotifyListeners(*range
, error
);
119 if (error
.Failed()) {
121 "Selection::AddRangeAndSelectFramesAndNotifyListeners() failed");
122 return error
.StealNSResult();
128 void SelectionState::ApplyTo(AutoClonedSelectionRangeArray
& aRanges
) {
129 aRanges
.RemoveAllRanges();
130 aRanges
.SetDirection(mDirection
);
131 for (const RefPtr
<RangeItem
>& rangeItem
: mArray
) {
132 RefPtr
<nsRange
> range
= rangeItem
->GetRange();
133 if (MOZ_UNLIKELY(!range
)) {
136 aRanges
.Ranges().AppendElement(std::move(range
));
140 bool SelectionState::Equals(const SelectionState
& aOther
) const {
141 if (mArray
.Length() != aOther
.mArray
.Length()) {
144 if (mArray
.IsEmpty()) {
145 return false; // XXX Why?
147 if (mDirection
!= aOther
.mDirection
) {
151 for (uint32_t i
: IntegerRange(mArray
.Length())) {
152 if (NS_WARN_IF(!mArray
[i
]) || NS_WARN_IF(!aOther
.mArray
[i
]) ||
153 !mArray
[i
]->Equals(*aOther
.mArray
[i
])) {
157 // if we got here, they are equal
161 /******************************************************************************
162 * mozilla::RangeUpdater
164 * Class for updating nsRanges in response to editor actions.
165 ******************************************************************************/
167 RangeUpdater::RangeUpdater() : mLocked(false) {}
169 void RangeUpdater::RegisterRangeItem(RangeItem
& aRangeItem
) {
170 if (mArray
.Contains(&aRangeItem
)) {
171 NS_ERROR("tried to register an already registered range");
172 return; // don't register it again. It would get doubly adjusted.
174 mArray
.AppendElement(&aRangeItem
);
177 void RangeUpdater::DropRangeItem(RangeItem
& aRangeItem
) {
178 NS_WARNING_ASSERTION(
179 mArray
.Contains(&aRangeItem
),
180 "aRangeItem is not in the range, but tried to removed from it");
181 mArray
.RemoveElement(&aRangeItem
);
184 void RangeUpdater::RegisterSelectionState(SelectionState
& aSelectionState
) {
185 for (RefPtr
<RangeItem
>& rangeItem
: aSelectionState
.mArray
) {
186 if (NS_WARN_IF(!rangeItem
)) {
189 RegisterRangeItem(*rangeItem
);
193 void RangeUpdater::DropSelectionState(SelectionState
& aSelectionState
) {
194 for (RefPtr
<RangeItem
>& rangeItem
: aSelectionState
.mArray
) {
195 if (NS_WARN_IF(!rangeItem
)) {
198 DropRangeItem(*rangeItem
);
204 template <typename PT
, typename CT
>
205 nsresult
RangeUpdater::SelAdjCreateNode(
206 const EditorDOMPointBase
<PT
, CT
>& aPoint
) {
208 // lock set by Will/DidReplaceParent, etc...
211 if (mArray
.IsEmpty()) {
215 if (NS_WARN_IF(!aPoint
.IsSetAndValid())) {
216 return NS_ERROR_INVALID_ARG
;
219 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
220 if (NS_WARN_IF(!rangeItem
)) {
221 return NS_ERROR_FAILURE
;
223 if (rangeItem
->mStartContainer
== aPoint
.GetContainer() &&
224 rangeItem
->mStartOffset
> aPoint
.Offset()) {
225 rangeItem
->mStartOffset
++;
227 if (rangeItem
->mEndContainer
== aPoint
.GetContainer() &&
228 rangeItem
->mEndOffset
> aPoint
.Offset()) {
229 rangeItem
->mEndOffset
++;
235 template <typename PT
, typename CT
>
236 nsresult
RangeUpdater::SelAdjInsertNode(
237 const EditorDOMPointBase
<PT
, CT
>& aPoint
) {
238 nsresult rv
= SelAdjCreateNode(aPoint
);
239 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
240 "RangeUpdater::SelAdjCreateNode() failed");
244 void RangeUpdater::SelAdjDeleteNode(nsINode
& aNodeToDelete
) {
246 // lock set by Will/DidReplaceParent, etc...
250 if (mArray
.IsEmpty()) {
254 EditorRawDOMPoint
atNodeToDelete(&aNodeToDelete
);
255 NS_ASSERTION(atNodeToDelete
.IsSetAndValid(),
256 "aNodeToDelete must be an orphan node or this is called "
258 // check for range endpoints that are after aNodeToDelete and in the same
260 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
261 MOZ_ASSERT(rangeItem
);
263 if (rangeItem
->mStartContainer
== atNodeToDelete
.GetContainer() &&
264 rangeItem
->mStartOffset
> atNodeToDelete
.Offset()) {
265 rangeItem
->mStartOffset
--;
267 if (rangeItem
->mEndContainer
== atNodeToDelete
.GetContainer() &&
268 rangeItem
->mEndOffset
> atNodeToDelete
.Offset()) {
269 rangeItem
->mEndOffset
--;
272 // check for range endpoints that are in aNodeToDelete
273 if (rangeItem
->mStartContainer
== &aNodeToDelete
) {
274 rangeItem
->mStartContainer
= atNodeToDelete
.GetContainer();
275 rangeItem
->mStartOffset
= atNodeToDelete
.Offset();
277 if (rangeItem
->mEndContainer
== &aNodeToDelete
) {
278 rangeItem
->mEndContainer
= atNodeToDelete
.GetContainer();
279 rangeItem
->mEndOffset
= atNodeToDelete
.Offset();
282 // check for range endpoints that are in descendants of aNodeToDelete
283 bool updateEndBoundaryToo
= false;
284 if (EditorUtils::IsDescendantOf(*rangeItem
->mStartContainer
,
286 updateEndBoundaryToo
=
287 rangeItem
->mStartContainer
== rangeItem
->mEndContainer
;
288 rangeItem
->mStartContainer
= atNodeToDelete
.GetContainer();
289 rangeItem
->mStartOffset
= atNodeToDelete
.Offset();
292 // avoid having to call IsDescendantOf() for common case of range startnode
294 if (updateEndBoundaryToo
||
295 EditorUtils::IsDescendantOf(*rangeItem
->mEndContainer
, aNodeToDelete
)) {
296 rangeItem
->mEndContainer
= atNodeToDelete
.GetContainer();
297 rangeItem
->mEndOffset
= atNodeToDelete
.Offset();
302 nsresult
RangeUpdater::SelAdjSplitNode(nsIContent
& aOriginalContent
,
303 uint32_t aSplitOffset
,
304 nsIContent
& aNewContent
) {
306 // lock set by Will/DidReplaceParent, etc...
310 if (mArray
.IsEmpty()) {
314 EditorRawDOMPoint
atNewNode(&aNewContent
);
315 if (NS_WARN_IF(!atNewNode
.IsSetAndValid())) {
316 return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE
;
319 auto AdjustDOMPoint
= [&](nsCOMPtr
<nsINode
>& aContainer
,
320 uint32_t& aOffset
) -> void {
321 if (aContainer
== atNewNode
.GetContainer()) {
322 // When we create a right node, we insert it after the left node.
324 // - `{}<left/>` should become `{}<left/><right/>` (0 -> 0)
325 // - `<left/>{}` should become `<left/><right/>{}` (1 -> 2)
326 // - `{<left/>}` should become `{<left/><right/>}` (0 -> 0, 1 -> 2}
327 // Therefore, we need to increate the offset only when the offset equals
328 // or is larger than the offset at the right node.
329 if (aOffset
>= atNewNode
.Offset()) {
333 // If point is in the range which are moved from aOriginalContent to
334 // aNewContent, we need to change its container to aNewContent and may need
335 // to adjust the offset. If point is in the range which are not moved from
336 // aOriginalContent, we may need to adjust the offset.
337 if (aContainer
!= &aOriginalContent
) {
340 if (aOffset
>= aSplitOffset
) {
341 aContainer
= &aNewContent
;
342 aOffset
-= aSplitOffset
;
346 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
347 if (NS_WARN_IF(!rangeItem
)) {
348 return NS_ERROR_FAILURE
;
350 AdjustDOMPoint(rangeItem
->mStartContainer
, rangeItem
->mStartOffset
);
351 AdjustDOMPoint(rangeItem
->mEndContainer
, rangeItem
->mEndOffset
);
356 nsresult
RangeUpdater::SelAdjJoinNodes(
357 const EditorRawDOMPoint
& aStartOfRightContent
,
358 const nsIContent
& aRemovedContent
,
359 const EditorDOMPoint
& aOldPointAtRightContent
) {
360 MOZ_ASSERT(aStartOfRightContent
.IsSetAndValid());
361 MOZ_ASSERT(aOldPointAtRightContent
.IsSet()); // Invalid point in most cases
362 MOZ_ASSERT(aOldPointAtRightContent
.HasOffset());
365 // lock set by Will/DidReplaceParent, etc...
369 if (mArray
.IsEmpty()) {
373 auto AdjustDOMPoint
= [&](nsCOMPtr
<nsINode
>& aContainer
,
374 uint32_t& aOffset
) -> void {
375 // FYI: Typically, containers of aOldPointAtRightContent and
376 // aStartOfRightContent are same. They are different when one of the
377 // node was moved to somewhere and they are joined by undoing splitting
379 if (aContainer
== &aRemovedContent
) {
380 // If the point is in the removed content, move the point to the new
381 // point in the joined node. If left node content is moved into
382 // right node, the offset should be same. Otherwise, we need to advance
383 // the offset to length of the removed content.
384 aContainer
= aStartOfRightContent
.GetContainer();
385 aOffset
+= aStartOfRightContent
.Offset();
387 // TODO: If aOldPointAtRightContent.GetContainer() was in aRemovedContent,
388 // we fail to adjust container and offset here because we need to
389 // make point to where aRemoveContent was. However, collecting all
390 // ancestors of the right content may be expensive. What's the best
391 // approach to fix this?
392 else if (aContainer
== aOldPointAtRightContent
.GetContainer()) {
393 // If the point is in common parent of joined content nodes and it
394 // pointed after the right content node, decrease the offset.
395 if (aOffset
> aOldPointAtRightContent
.Offset()) {
398 // If it pointed the right content node, adjust it to point ex-first
399 // content of the right node.
400 else if (aOffset
== aOldPointAtRightContent
.Offset()) {
401 aContainer
= aStartOfRightContent
.GetContainer();
402 aOffset
= aStartOfRightContent
.Offset();
407 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
408 if (NS_WARN_IF(!rangeItem
)) {
409 return NS_ERROR_FAILURE
;
411 AdjustDOMPoint(rangeItem
->mStartContainer
, rangeItem
->mStartOffset
);
412 AdjustDOMPoint(rangeItem
->mEndContainer
, rangeItem
->mEndOffset
);
418 void RangeUpdater::SelAdjReplaceText(const Text
& aTextNode
, uint32_t aOffset
,
419 uint32_t aReplacedLength
,
420 uint32_t aInsertedLength
) {
422 // lock set by Will/DidReplaceParent, etc...
426 // First, adjust selection for insertion because when offset is in the
427 // replaced range, it's adjusted to aOffset and never modified by the
428 // insertion if we adjust selection for deletion first.
429 SelAdjInsertText(aTextNode
, aOffset
, aInsertedLength
);
431 // Then, adjust selection for deletion.
432 SelAdjDeleteText(aTextNode
, aOffset
, aReplacedLength
);
435 void RangeUpdater::SelAdjInsertText(const Text
& aTextNode
, uint32_t aOffset
,
436 uint32_t aInsertedLength
) {
438 // lock set by Will/DidReplaceParent, etc...
442 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
443 MOZ_ASSERT(rangeItem
);
445 if (rangeItem
->mStartContainer
== &aTextNode
&&
446 rangeItem
->mStartOffset
> aOffset
) {
447 rangeItem
->mStartOffset
+= aInsertedLength
;
449 if (rangeItem
->mEndContainer
== &aTextNode
&&
450 rangeItem
->mEndOffset
> aOffset
) {
451 rangeItem
->mEndOffset
+= aInsertedLength
;
456 void RangeUpdater::SelAdjDeleteText(const Text
& aTextNode
, uint32_t aOffset
,
457 uint32_t aDeletedLength
) {
459 // lock set by Will/DidReplaceParent, etc...
463 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
464 MOZ_ASSERT(rangeItem
);
466 if (rangeItem
->mStartContainer
== &aTextNode
&&
467 rangeItem
->mStartOffset
> aOffset
) {
468 if (rangeItem
->mStartOffset
>= aDeletedLength
) {
469 rangeItem
->mStartOffset
-= aDeletedLength
;
471 rangeItem
->mStartOffset
= 0;
474 if (rangeItem
->mEndContainer
== &aTextNode
&&
475 rangeItem
->mEndOffset
> aOffset
) {
476 if (rangeItem
->mEndOffset
>= aDeletedLength
) {
477 rangeItem
->mEndOffset
-= aDeletedLength
;
479 rangeItem
->mEndOffset
= 0;
485 void RangeUpdater::DidReplaceContainer(const Element
& aRemovedElement
,
486 Element
& aInsertedElement
) {
487 if (NS_WARN_IF(!mLocked
)) {
492 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
493 if (NS_WARN_IF(!rangeItem
)) {
497 if (rangeItem
->mStartContainer
== &aRemovedElement
) {
498 rangeItem
->mStartContainer
= &aInsertedElement
;
500 if (rangeItem
->mEndContainer
== &aRemovedElement
) {
501 rangeItem
->mEndContainer
= &aInsertedElement
;
506 void RangeUpdater::DidRemoveContainer(const Element
& aRemovedElement
,
507 nsINode
& aRemovedElementContainerNode
,
508 uint32_t aOldOffsetOfRemovedElement
,
509 uint32_t aOldChildCountOfRemovedElement
) {
510 if (NS_WARN_IF(!mLocked
)) {
515 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
516 if (NS_WARN_IF(!rangeItem
)) {
520 if (rangeItem
->mStartContainer
== &aRemovedElement
) {
521 rangeItem
->mStartContainer
= &aRemovedElementContainerNode
;
522 rangeItem
->mStartOffset
+= aOldOffsetOfRemovedElement
;
523 } else if (rangeItem
->mStartContainer
== &aRemovedElementContainerNode
&&
524 rangeItem
->mStartOffset
> aOldOffsetOfRemovedElement
) {
525 rangeItem
->mStartOffset
+= aOldChildCountOfRemovedElement
- 1;
528 if (rangeItem
->mEndContainer
== &aRemovedElement
) {
529 rangeItem
->mEndContainer
= &aRemovedElementContainerNode
;
530 rangeItem
->mEndOffset
+= aOldOffsetOfRemovedElement
;
531 } else if (rangeItem
->mEndContainer
== &aRemovedElementContainerNode
&&
532 rangeItem
->mEndOffset
> aOldOffsetOfRemovedElement
) {
533 rangeItem
->mEndOffset
+= aOldChildCountOfRemovedElement
- 1;
538 void RangeUpdater::DidMoveNode(const nsINode
& aOldParent
, uint32_t aOldOffset
,
539 const nsINode
& aNewParent
, uint32_t aNewOffset
) {
541 // Do nothing if moving nodes is occurred while changing the container.
544 auto AdjustDOMPoint
= [&](nsCOMPtr
<nsINode
>& aNode
, uint32_t& aOffset
) {
545 if (aNode
== &aOldParent
) {
546 // If previously pointed the moved content, it should keep pointing it.
547 if (aOffset
== aOldOffset
) {
548 aNode
= const_cast<nsINode
*>(&aNewParent
);
549 aOffset
= aNewOffset
;
550 } else if (aOffset
> aOldOffset
) {
555 if (aNode
== &aNewParent
) {
556 if (aOffset
> aNewOffset
) {
561 for (RefPtr
<RangeItem
>& rangeItem
: mArray
) {
562 if (NS_WARN_IF(!rangeItem
)) {
566 AdjustDOMPoint(rangeItem
->mStartContainer
, rangeItem
->mStartOffset
);
567 AdjustDOMPoint(rangeItem
->mEndContainer
, rangeItem
->mEndOffset
);
571 /******************************************************************************
574 * Helper struct for SelectionState. This stores range endpoints.
575 ******************************************************************************/
577 NS_IMPL_CYCLE_COLLECTION(RangeItem
, mStartContainer
, mEndContainer
)
579 void RangeItem::StoreRange(const nsRange
& aRange
) {
580 mStartContainer
= aRange
.GetStartContainer();
581 mStartOffset
= aRange
.StartOffset();
582 mEndContainer
= aRange
.GetEndContainer();
583 mEndOffset
= aRange
.EndOffset();
586 already_AddRefed
<nsRange
> RangeItem::GetRange() const {
587 RefPtr
<nsRange
> range
= nsRange::Create(
588 mStartContainer
, mStartOffset
, mEndContainer
, mEndOffset
, IgnoreErrors());
589 NS_WARNING_ASSERTION(range
, "nsRange::Create() failed");
590 return range
.forget();
593 /******************************************************************************
594 * mozilla::AutoTrackDOMPoint
595 ******************************************************************************/
597 AutoTrackDOMPoint::AutoTrackDOMPoint(RangeUpdater
& aRangeUpdater
,
598 CaretPoint
* aCaretPoint
)
599 : AutoTrackDOMPoint(aRangeUpdater
, &aCaretPoint
->mCaretPoint
) {}
601 /******************************************************************************
602 * mozilla::AutoTrackDOMMoveNodeResult
603 ******************************************************************************/
605 AutoTrackDOMMoveNodeResult::AutoTrackDOMMoveNodeResult(
606 RangeUpdater
& aRangeUpdater
, MoveNodeResult
* aMoveNodeResult
)
607 : mTrackCaretPoint(aRangeUpdater
,
608 static_cast<CaretPoint
*>(aMoveNodeResult
)),
609 mTrackNextInsertionPoint(aRangeUpdater
,
610 &aMoveNodeResult
->mNextInsertionPoint
),
611 mTrackMovedContentRange(aRangeUpdater
,
612 &aMoveNodeResult
->mMovedContentRange
) {}
614 /******************************************************************************
615 * mozilla::AutoTrackDeleteRangeResult
616 ******************************************************************************/
618 AutoTrackDOMDeleteRangeResult::AutoTrackDOMDeleteRangeResult(
619 RangeUpdater
& aRangeUpdater
, DeleteRangeResult
* aDeleteRangeResult
)
620 : mTrackCaretPoint(aRangeUpdater
,
621 static_cast<CaretPoint
*>(aDeleteRangeResult
)),
622 mTrackDeleteRange(aRangeUpdater
, &aDeleteRangeResult
->mDeleteRange
) {}
624 /******************************************************************************
625 * mozilla::AutoTrackLineBreak
626 ******************************************************************************/
628 AutoTrackLineBreak::AutoTrackLineBreak(RangeUpdater
& aRangeUpdater
,
629 EditorLineBreak
* aLineBreak
)
630 : mLineBreak(aLineBreak
->IsPreformattedLineBreak() ? aLineBreak
: nullptr),
631 mPoint(mLineBreak
? mLineBreak
->To
<EditorDOMPoint
>() : EditorDOMPoint()),
632 mTracker(aRangeUpdater
, &mPoint
) {
633 MOZ_ASSERT(aLineBreak
->IsPreformattedLineBreak());
636 void AutoTrackLineBreak::FlushAndStopTracking() {
640 mTracker
.FlushAndStopTracking();
641 if (mPoint
.GetContainer() == mLineBreak
->mContent
) {
642 mLineBreak
->mOffsetInText
= Some(mPoint
.Offset());
644 mLineBreak
= nullptr;
647 } // namespace mozilla