Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / editor / libeditor / SelectionState.cpp
blobbb6cd53e31937a07a09c4d302debf7d3673df8aa
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
28 namespace mozilla {
30 using namespace dom;
32 /*****************************************************************************
33 * mozilla::RangeItem
34 *****************************************************************************/
36 nsINode* RangeItem::GetRoot() const {
37 if (MOZ_UNLIKELY(!IsPositioned())) {
38 return nullptr;
40 nsINode* rootNode = RangeUtils::ComputeRootNode(mStartContainer);
41 if (mStartContainer == mEndContainer) {
42 return rootNode;
44 return MOZ_LIKELY(rootNode == RangeUtils::ComputeRootNode(mEndContainer))
45 ? rootNode
46 : nullptr;
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);
91 MOZ_ASSERT(range);
92 if (MOZ_UNLIKELY(NS_WARN_IF(!range))) {
93 continue;
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);
110 ErrorResult error;
111 const CopyableAutoTArray<RefPtr<RangeItem>, 10> rangeItems(mArray);
112 for (const RefPtr<RangeItem>& rangeItem : rangeItems) {
113 RefPtr<nsRange> range = rangeItem->GetRange();
114 if (!range) {
115 NS_WARNING("RangeItem::GetRange() failed");
116 return NS_ERROR_FAILURE;
118 aSelection.AddRangeAndSelectFramesAndNotifyListeners(*range, error);
119 if (error.Failed()) {
120 NS_WARNING(
121 "Selection::AddRangeAndSelectFramesAndNotifyListeners() failed");
122 return error.StealNSResult();
125 return NS_OK;
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)) {
134 continue;
136 aRanges.Ranges().AppendElement(std::move(range));
140 bool SelectionState::Equals(const SelectionState& aOther) const {
141 if (mArray.Length() != aOther.mArray.Length()) {
142 return false;
144 if (mArray.IsEmpty()) {
145 return false; // XXX Why?
147 if (mDirection != aOther.mDirection) {
148 return false;
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])) {
154 return false;
157 // if we got here, they are equal
158 return true;
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)) {
187 continue;
189 RegisterRangeItem(*rangeItem);
193 void RangeUpdater::DropSelectionState(SelectionState& aSelectionState) {
194 for (RefPtr<RangeItem>& rangeItem : aSelectionState.mArray) {
195 if (NS_WARN_IF(!rangeItem)) {
196 continue;
198 DropRangeItem(*rangeItem);
202 // gravity methods:
204 template <typename PT, typename CT>
205 nsresult RangeUpdater::SelAdjCreateNode(
206 const EditorDOMPointBase<PT, CT>& aPoint) {
207 if (mLocked) {
208 // lock set by Will/DidReplaceParent, etc...
209 return NS_OK;
211 if (mArray.IsEmpty()) {
212 return NS_OK;
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++;
232 return NS_OK;
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");
241 return rv;
244 void RangeUpdater::SelAdjDeleteNode(nsINode& aNodeToDelete) {
245 if (mLocked) {
246 // lock set by Will/DidReplaceParent, etc...
247 return;
250 if (mArray.IsEmpty()) {
251 return;
254 EditorRawDOMPoint atNodeToDelete(&aNodeToDelete);
255 NS_ASSERTION(atNodeToDelete.IsSetAndValid(),
256 "aNodeToDelete must be an orphan node or this is called "
257 "during mutation");
258 // check for range endpoints that are after aNodeToDelete and in the same
259 // parent
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,
285 aNodeToDelete)) {
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
293 // == range endnode.
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) {
305 if (mLocked) {
306 // lock set by Will/DidReplaceParent, etc...
307 return NS_OK;
310 if (mArray.IsEmpty()) {
311 return NS_OK;
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.
323 // In this case,
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()) {
330 aOffset++;
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) {
338 return;
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);
353 return NS_OK;
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());
364 if (mLocked) {
365 // lock set by Will/DidReplaceParent, etc...
366 return NS_OK;
369 if (mArray.IsEmpty()) {
370 return NS_OK;
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
378 // a node.
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()) {
396 aOffset--;
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);
415 return NS_OK;
418 void RangeUpdater::SelAdjReplaceText(const Text& aTextNode, uint32_t aOffset,
419 uint32_t aReplacedLength,
420 uint32_t aInsertedLength) {
421 if (mLocked) {
422 // lock set by Will/DidReplaceParent, etc...
423 return;
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) {
437 if (mLocked) {
438 // lock set by Will/DidReplaceParent, etc...
439 return;
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) {
458 if (mLocked) {
459 // lock set by Will/DidReplaceParent, etc...
460 return;
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;
470 } else {
471 rangeItem->mStartOffset = 0;
474 if (rangeItem->mEndContainer == &aTextNode &&
475 rangeItem->mEndOffset > aOffset) {
476 if (rangeItem->mEndOffset >= aDeletedLength) {
477 rangeItem->mEndOffset -= aDeletedLength;
478 } else {
479 rangeItem->mEndOffset = 0;
485 void RangeUpdater::DidReplaceContainer(const Element& aRemovedElement,
486 Element& aInsertedElement) {
487 if (NS_WARN_IF(!mLocked)) {
488 return;
490 mLocked = false;
492 for (RefPtr<RangeItem>& rangeItem : mArray) {
493 if (NS_WARN_IF(!rangeItem)) {
494 return;
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)) {
511 return;
513 mLocked = false;
515 for (RefPtr<RangeItem>& rangeItem : mArray) {
516 if (NS_WARN_IF(!rangeItem)) {
517 return;
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) {
540 if (mLocked) {
541 // Do nothing if moving nodes is occurred while changing the container.
542 return;
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) {
551 aOffset--;
553 return;
555 if (aNode == &aNewParent) {
556 if (aOffset > aNewOffset) {
557 aOffset++;
561 for (RefPtr<RangeItem>& rangeItem : mArray) {
562 if (NS_WARN_IF(!rangeItem)) {
563 return;
566 AdjustDOMPoint(rangeItem->mStartContainer, rangeItem->mStartOffset);
567 AdjustDOMPoint(rangeItem->mEndContainer, rangeItem->mEndOffset);
571 /******************************************************************************
572 * mozilla::RangeItem
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() {
637 if (!mLineBreak) {
638 return;
640 mTracker.FlushAndStopTracking();
641 if (mPoint.GetContainer() == mLineBreak->mContent) {
642 mLineBreak->mOffsetInText = Some(mPoint.Offset());
644 mLineBreak = nullptr;
647 } // namespace mozilla