Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / editor / libeditor / WhiteSpaceVisibilityKeeper.h
blobe6a5d8c7d82e785d8eff4122a5744bee4ced097c
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 #ifndef WhiteSpaceVisibilityKeeper_h
7 #define WhiteSpaceVisibilityKeeper_h
9 #include "EditAction.h"
10 #include "EditorBase.h"
11 #include "EditorForwards.h"
12 #include "EditorDOMPoint.h" // for EditorDOMPoint
13 #include "EditorUtils.h" // for CaretPoint
14 #include "HTMLEditHelpers.h"
15 #include "HTMLEditor.h"
16 #include "HTMLEditUtils.h"
17 #include "WSRunScanner.h"
19 #include "mozilla/Assertions.h"
20 #include "mozilla/Maybe.h"
21 #include "mozilla/Result.h"
22 #include "mozilla/dom/Element.h"
23 #include "mozilla/dom/HTMLBRElement.h"
24 #include "mozilla/dom/Text.h"
25 #include "nsCOMPtr.h"
26 #include "nsIContent.h"
28 namespace mozilla {
30 /**
31 * WhiteSpaceVisibilityKeeper class helps `HTMLEditor` modifying the DOM tree
32 * with keeps white-space sequence visibility automatically. E.g., invisible
33 * leading/trailing white-spaces becomes visible, this class members delete
34 * them. E.g., when splitting visible-white-space sequence, this class may
35 * replace ASCII white-spaces at split edges with NBSPs.
37 class WhiteSpaceVisibilityKeeper final {
38 private:
39 using AutoTransactionsConserveSelection =
40 EditorBase::AutoTransactionsConserveSelection;
41 using EditorType = EditorBase::EditorType;
42 using Element = dom::Element;
43 using HTMLBRElement = dom::HTMLBRElement;
44 using IgnoreNonEditableNodes = WSRunScanner::IgnoreNonEditableNodes;
45 using InsertTextTo = EditorBase::InsertTextTo;
46 using LineBreakType = HTMLEditor::LineBreakType;
47 using PointPosition = WSRunScanner::PointPosition;
48 using Scan = WSRunScanner::Scan;
49 using TextFragmentData = WSRunScanner::TextFragmentData;
50 using VisibleWhiteSpacesData = WSRunScanner::VisibleWhiteSpacesData;
52 public:
53 WhiteSpaceVisibilityKeeper() = delete;
54 explicit WhiteSpaceVisibilityKeeper(
55 const WhiteSpaceVisibilityKeeper& aOther) = delete;
56 WhiteSpaceVisibilityKeeper(WhiteSpaceVisibilityKeeper&& aOther) = delete;
58 /**
59 * Remove invisible leading white-spaces and trailing white-spaces if there
60 * are around aPoint.
62 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CaretPoint, nsresult>
63 DeleteInvisibleASCIIWhiteSpaces(HTMLEditor& aHTMLEditor,
64 const EditorDOMPoint& aPoint);
66 /**
67 * Fix up white-spaces before aStartPoint and after aEndPoint in preparation
68 * for content to keep the white-spaces visibility after the range is deleted.
69 * Note that the nodes and offsets are adjusted in response to any dom changes
70 * we make while adjusting white-spaces.
72 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CaretPoint, nsresult>
73 PrepareToDeleteRangeAndTrackPoints(HTMLEditor& aHTMLEditor,
74 EditorDOMPoint* aStartPoint,
75 EditorDOMPoint* aEndPoint,
76 const Element& aEditingHost) {
77 MOZ_ASSERT(aStartPoint->IsSetAndValid());
78 MOZ_ASSERT(aEndPoint->IsSetAndValid());
79 AutoTrackDOMPoint trackerStart(aHTMLEditor.RangeUpdaterRef(), aStartPoint);
80 AutoTrackDOMPoint trackerEnd(aHTMLEditor.RangeUpdaterRef(), aEndPoint);
81 Result<CaretPoint, nsresult> caretPointOrError =
82 WhiteSpaceVisibilityKeeper::PrepareToDeleteRange(
83 aHTMLEditor, EditorDOMRange(*aStartPoint, *aEndPoint),
84 aEditingHost);
85 NS_WARNING_ASSERTION(
86 caretPointOrError.isOk(),
87 "WhiteSpaceVisibilityKeeper::PrepareToDeleteRange() failed");
88 return caretPointOrError;
90 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CaretPoint, nsresult>
91 PrepareToDeleteRange(HTMLEditor& aHTMLEditor,
92 const EditorDOMPoint& aStartPoint,
93 const EditorDOMPoint& aEndPoint,
94 const Element& aEditingHost) {
95 MOZ_ASSERT(aStartPoint.IsSetAndValid());
96 MOZ_ASSERT(aEndPoint.IsSetAndValid());
97 Result<CaretPoint, nsresult> caretPointOrError =
98 WhiteSpaceVisibilityKeeper::PrepareToDeleteRange(
99 aHTMLEditor, EditorDOMRange(aStartPoint, aEndPoint), aEditingHost);
100 NS_WARNING_ASSERTION(
101 caretPointOrError.isOk(),
102 "WhiteSpaceVisibilityKeeper::PrepareToDeleteRange() failed");
103 return caretPointOrError;
105 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CaretPoint, nsresult>
106 PrepareToDeleteRange(HTMLEditor& aHTMLEditor, const EditorDOMRange& aRange,
107 const Element& aEditingHost) {
108 MOZ_ASSERT(aRange.IsPositionedAndValid());
109 Result<CaretPoint, nsresult> caretPointOrError =
110 WhiteSpaceVisibilityKeeper::
111 MakeSureToKeepVisibleStateOfWhiteSpacesAroundDeletingRange(
112 aHTMLEditor, aRange, aEditingHost);
113 NS_WARNING_ASSERTION(
114 caretPointOrError.isOk(),
115 "WhiteSpaceVisibilityKeeper::"
116 "MakeSureToKeepVisibleStateOfWhiteSpacesAroundDeletingRange() failed");
117 return caretPointOrError;
121 * PrepareToSplitBlockElement() makes sure that the invisible white-spaces
122 * not to become visible and returns splittable point.
124 * @param aHTMLEditor The HTML editor.
125 * @param aPointToSplit The splitting point in aSplittingBlockElement.
126 * @param aSplittingBlockElement A block element which will be split.
128 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<EditorDOMPoint, nsresult>
129 PrepareToSplitBlockElement(HTMLEditor& aHTMLEditor,
130 const EditorDOMPoint& aPointToSplit,
131 const Element& aSplittingBlockElement);
134 * MergeFirstLineOfRightBlockElementIntoDescendantLeftBlockElement() merges
135 * first line in aRightBlockElement into end of aLeftBlockElement which
136 * is a descendant of aRightBlockElement.
138 * @param aHTMLEditor The HTML editor.
139 * @param aLeftBlockElement The content will be merged into end of
140 * this element.
141 * @param aRightBlockElement The first line in this element will be
142 * moved to aLeftBlockElement.
143 * @param aAtRightBlockChild At a child of aRightBlockElement and inclusive
144 * ancestor of aLeftBlockElement.
145 * @param aListElementTagName Set some if aRightBlockElement is a list
146 * element and it'll be merged with another
147 * list element.
148 * @param aEditingHost The editing host.
150 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<MoveNodeResult, nsresult>
151 MergeFirstLineOfRightBlockElementIntoDescendantLeftBlockElement(
152 HTMLEditor& aHTMLEditor, Element& aLeftBlockElement,
153 Element& aRightBlockElement, const EditorDOMPoint& aAtRightBlockChild,
154 const Maybe<nsAtom*>& aListElementTagName,
155 const HTMLBRElement* aPrecedingInvisibleBRElement,
156 const Element& aEditingHost);
159 * MergeFirstLineOfRightBlockElementIntoAncestorLeftBlockElement() merges
160 * first line in aRightBlockElement into end of aLeftBlockElement which
161 * is an ancestor of aRightBlockElement, then, removes aRightBlockElement
162 * if it becomes empty.
164 * @param aHTMLEditor The HTML editor.
165 * @param aLeftBlockElement The content will be merged into end of
166 * this element.
167 * @param aRightBlockElement The first line in this element will be
168 * moved to aLeftBlockElement and maybe
169 * removed when this becomes empty.
170 * @param aAtLeftBlockChild At a child of aLeftBlockElement and inclusive
171 * ancestor of aRightBlockElement.
172 * @param aLeftContentInBlock The content whose inclusive ancestor is
173 * aLeftBlockElement.
174 * @param aListElementTagName Set some if aRightBlockElement is a list
175 * element and it'll be merged with another
176 * list element.
177 * @param aEditingHost The editing host.
179 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<MoveNodeResult, nsresult>
180 MergeFirstLineOfRightBlockElementIntoAncestorLeftBlockElement(
181 HTMLEditor& aHTMLEditor, Element& aLeftBlockElement,
182 Element& aRightBlockElement, const EditorDOMPoint& aAtLeftBlockChild,
183 nsIContent& aLeftContentInBlock,
184 const Maybe<nsAtom*>& aListElementTagName,
185 const HTMLBRElement* aPrecedingInvisibleBRElement,
186 const Element& aEditingHost);
189 * MergeFirstLineOfRightBlockElementIntoLeftBlockElement() merges first
190 * line in aRightBlockElement into end of aLeftBlockElement and removes
191 * aRightBlockElement when it has only one line.
193 * @param aHTMLEditor The HTML editor.
194 * @param aLeftBlockElement The content will be merged into end of
195 * this element.
196 * @param aRightBlockElement The first line in this element will be
197 * moved to aLeftBlockElement and maybe
198 * removed when this becomes empty.
199 * @param aListElementTagName Set some if aRightBlockElement is a list
200 * element and its type needs to be changed.
201 * @param aEditingHost The editing host.
203 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<MoveNodeResult, nsresult>
204 MergeFirstLineOfRightBlockElementIntoLeftBlockElement(
205 HTMLEditor& aHTMLEditor, Element& aLeftBlockElement,
206 Element& aRightBlockElement, const Maybe<nsAtom*>& aListElementTagName,
207 const HTMLBRElement* aPrecedingInvisibleBRElement,
208 const Element& aEditingHost);
211 * InsertLineBreak() inserts a line break at (before) aPointToInsert and
212 * delete unnecessary white-spaces around there and/or replaces white-spaces
213 * with non-breaking spaces. Note that if the point is in a text node, the
214 * text node will be split and insert new <br> node between the left node
215 * and the right node.
217 * @param aPointToInsert The point to insert new line break. Note that
218 * it'll be inserted before this point. I.e., the
219 * point will be the point of new line break.
220 * @return If succeeded, returns the new line break and
221 * point to put caret.
223 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CreateLineBreakResult,
224 nsresult>
225 InsertLineBreak(LineBreakType aLineBreakType, HTMLEditor& aHTMLEditor,
226 const EditorDOMPoint& aPointToInsert);
229 * Insert aStringToInsert to aPointToInsert and makes any needed adjustments
230 * to white-spaces around the insertion point.
232 * @param aStringToInsert The string to insert.
233 * @param aRangeToBeReplaced The range to be replaced.
234 * @param aInsertTextTo Whether forcibly creates a new `Text` node in
235 * specific condition or use existing `Text` if
236 * available.
238 template <typename EditorDOMPointType>
239 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<InsertTextResult, nsresult>
240 InsertText(HTMLEditor& aHTMLEditor, const nsAString& aStringToInsert,
241 const EditorDOMPointType& aPointToInsert,
242 InsertTextTo aInsertTextTo) {
243 return WhiteSpaceVisibilityKeeper::ReplaceText(
244 aHTMLEditor, aStringToInsert, EditorDOMRange(aPointToInsert),
245 aInsertTextTo);
249 * Replace aRangeToReplace with aStringToInsert and makes any needed
250 * adjustments to white-spaces around both start of the range and end of the
251 * range.
253 * @param aStringToInsert The string to insert.
254 * @param aRangeToBeReplaced The range to be replaced.
255 * @param aInsertTextTo Whether forcibly creates a new `Text` node in
256 * specific condition or use existing `Text` if
257 * available.
259 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<InsertTextResult, nsresult>
260 ReplaceText(HTMLEditor& aHTMLEditor, const nsAString& aStringToInsert,
261 const EditorDOMRange& aRangeToBeReplaced,
262 InsertTextTo aInsertTextTo);
265 * Delete previous white-space of aPoint. This automatically keeps visibility
266 * of white-spaces around aPoint. E.g., may remove invisible leading
267 * white-spaces.
269 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CaretPoint, nsresult>
270 DeletePreviousWhiteSpace(HTMLEditor& aHTMLEditor,
271 const EditorDOMPoint& aPoint,
272 const Element& aEditingHost);
275 * Delete inclusive next white-space of aPoint. This automatically keeps
276 * visiblity of white-spaces around aPoint. E.g., may remove invisible
277 * trailing white-spaces.
279 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CaretPoint, nsresult>
280 DeleteInclusiveNextWhiteSpace(HTMLEditor& aHTMLEditor,
281 const EditorDOMPoint& aPoint,
282 const Element& aEditingHost);
285 * Delete aContentToDelete and may remove/replace white-spaces around it.
286 * Then, if deleting content makes 2 text nodes around it are adjacent
287 * siblings, this joins them and put selection at the joined point.
289 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CaretPoint, nsresult>
290 DeleteContentNodeAndJoinTextNodesAroundIt(HTMLEditor& aHTMLEditor,
291 nsIContent& aContentToDelete,
292 const EditorDOMPoint& aCaretPoint,
293 const Element& aEditingHost);
296 * Try to normalize visible white-space sequence around aPoint.
297 * This may collapse `Selection` after replaced text. Therefore, the callers
298 * of this need to restore `Selection` by themselves (this does not do it for
299 * performance reason of multiple calls).
301 template <typename EditorDOMPointType>
302 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static nsresult
303 NormalizeVisibleWhiteSpacesAt(HTMLEditor& aHTMLEditor,
304 const EditorDOMPointType& aPoint,
305 const Element& aEditingHost);
307 private:
309 * Maybe delete invisible white-spaces for keeping make them invisible and/or
310 * may replace ASCII white-spaces with NBSPs for making visible white-spaces
311 * to keep visible.
313 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<CaretPoint, nsresult>
314 MakeSureToKeepVisibleStateOfWhiteSpacesAroundDeletingRange(
315 HTMLEditor& aHTMLEditor, const EditorDOMRange& aRangeToDelete,
316 const Element& aEditingHost);
319 * MakeSureToKeepVisibleWhiteSpacesVisibleAfterSplit() replaces ASCII white-
320 * spaces which becomes invisible after split with NBSPs.
322 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static nsresult
323 MakeSureToKeepVisibleWhiteSpacesVisibleAfterSplit(
324 HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit);
327 * ReplaceTextAndRemoveEmptyTextNodes() replaces the range between
328 * aRangeToReplace with aReplaceString simply. Additionally, removes
329 * empty text nodes in the range.
331 * @param aRangeToReplace Range to replace text.
332 * @param aReplaceString The new string. Empty string is allowed.
334 [[nodiscard]] MOZ_CAN_RUN_SCRIPT static nsresult
335 ReplaceTextAndRemoveEmptyTextNodes(
336 HTMLEditor& aHTMLEditor, const EditorDOMRangeInTexts& aRangeToReplace,
337 const nsAString& aReplaceString);
340 } // namespace mozilla
342 #endif // #ifndef WhiteSpaceVisibilityKeeper_h