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/. */
5
6 #ifndef mozilla_HTMLEditor_h
7 #define mozilla_HTMLEditor_h
8
9 #include "mozilla/Attributes.h"
10 #include "mozilla/ComposerCommandsUpdater.h"
11 #include "mozilla/CSSEditUtils.h"
12 #include "mozilla/EditorUtils.h"
13 #include "mozilla/ManualNAC.h"
14 #include "mozilla/Result.h"
15 #include "mozilla/TextEditor.h"
16 #include "mozilla/UniquePtr.h"
17 #include "mozilla/dom/Element.h"
18 #include "mozilla/dom/File.h"
19
20 #include "nsAttrName.h"
21 #include "nsCOMPtr.h"
22 #include "nsIDocumentObserver.h"
23 #include "nsIDOMEventListener.h"
24 #include "nsIEditorMailSupport.h"
25 #include "nsIHTMLAbsPosEditor.h"
26 #include "nsIHTMLEditor.h"
27 #include "nsIHTMLInlineTableEditor.h"
28 #include "nsIHTMLObjectResizer.h"
29 #include "nsITableEditor.h"
30 #include "nsPoint.h"
31 #include "nsStubMutationObserver.h"
32 #include "nsTArray.h"
33
34 class nsDocumentFragment;
35 class nsHTMLDocument;
36 class nsITransferable;
37 class nsIClipboard;
38 class nsRange;
39 class nsStaticAtom;
40 class nsTableWrapperFrame;
41
42 namespace mozilla {
43 class AlignStateAtSelection;
44 class AutoSelectionSetterAfterTableEdit;
45 class AutoSetTemporaryAncestorLimiter;
46 class EditActionResult;
47 class EditResult;
48 class EmptyEditableFunctor;
49 class JoinNodeTransaction;
50 class ListElementSelectionState;
51 class ListItemElementSelectionState;
52 class MoveNodeResult;
53 class ParagraphStateAtSelection;
54 class ResizerSelectionListener;
55 class Runnable;
56 class SplitNodeTransaction;
57 class SplitRangeOffFromNodeResult;
58 class SplitRangeOffResult;
59 class WSRunObject;
60 class WSRunScanner;
61 class WSScanResult;
62 enum class EditSubAction : int32_t;
63 struct PropItem;
64 template <class T>
65 class OwningNonNull;
66 namespace dom {
67 class AbstractRange;
68 class Blob;
69 class DocumentFragment;
70 class Event;
71 class MouseEvent;
72 class StaticRange;
73 } // namespace dom
74 namespace widget {
75 struct IMEState;
76 } // namespace widget
77
78 enum class ParagraphSeparator { div, p, br };
79
80 /**
81 * The HTML editor implementation.<br>
82 * Use to edit HTML document represented as a DOM tree.
83 */
84 class HTMLEditor final : public TextEditor,
85 public nsIHTMLEditor,
86 public nsIHTMLObjectResizer,
87 public nsIHTMLAbsPosEditor,
88 public nsITableEditor,
89 public nsIHTMLInlineTableEditor,
90 public nsStubMutationObserver,
91 public nsIEditorMailSupport {
92 public:
93 /****************************************************************************
94 * NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other
95 * classes under libeditor except EditorEventListener and
96 * HTMLEditorEventListener because each public method which may fire
97 * eEditorInput event will need to instantiate new stack class for
98 * managing input type value of eEditorInput and cache some objects
99 * for smarter handling. In other words, when you add new root
100 * method to edit the DOM tree, you can make your new method public.
101 ****************************************************************************/
102
103 NS_DECL_ISUPPORTS_INHERITED
104 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditor, TextEditor)
105
106 // nsStubMutationObserver overrides
107 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
108 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
109 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
110
111 // nsIHTMLEditor methods
112 NS_DECL_NSIHTMLEDITOR
113
114 // nsIHTMLObjectResizer methods (implemented in HTMLObjectResizer.cpp)
115 NS_DECL_NSIHTMLOBJECTRESIZER
116
117 // nsIHTMLAbsPosEditor methods (implemented in HTMLAbsPositionEditor.cpp)
118 NS_DECL_NSIHTMLABSPOSEDITOR
119
120 // nsIHTMLInlineTableEditor methods (implemented in HTMLInlineTableEditor.cpp)
121 NS_DECL_NSIHTMLINLINETABLEEDITOR
122
123 // nsIEditorMailSupport methods
124 NS_DECL_NSIEDITORMAILSUPPORT
125
126 // nsITableEditor methods
127 NS_DECL_NSITABLEEDITOR
128
129 // nsISelectionListener overrides
130 NS_DECL_NSISELECTIONLISTENER
131
132 HTMLEditor();
133
134 nsHTMLDocument* GetHTMLDocument() const;
135
136 MOZ_CAN_RUN_SCRIPT virtual void PreDestroy(bool aDestroyingFrames) override;
137
138 bool GetReturnInParagraphCreatesNewParagraph();
139
140 // TextEditor overrides
141 MOZ_CAN_RUN_SCRIPT virtual nsresult Init(Document& aDoc, Element* aRoot,
142 nsISelectionController* aSelCon,
143 uint32_t aFlags,
144 const nsAString& aValue) override;
145 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD BeginningOfDocument() override;
146 NS_IMETHOD SetFlags(uint32_t aFlags) override;
147
148 /**
149 * IsEmpty() checks whether the editor is empty. If editor has only padding
150 * <br> element for empty editor, returns true. If editor's root element has
151 * non-empty text nodes or other nodes like <br>, returns false even if there
152 * are only empty blocks.
153 */
154 virtual bool IsEmpty() const override;
155
156 virtual bool CanPaste(int32_t aClipboardType) const override;
157 using EditorBase::CanPaste;
158
159 MOZ_CAN_RUN_SCRIPT virtual nsresult PasteTransferableAsAction(
160 nsITransferable* aTransferable,
161 nsIPrincipal* aPrincipal = nullptr) override;
162
163 MOZ_CAN_RUN_SCRIPT NS_IMETHOD DeleteNode(nsINode* aNode) override;
164
165 MOZ_CAN_RUN_SCRIPT NS_IMETHOD InsertLineBreak() override;
166
167 MOZ_CAN_RUN_SCRIPT virtual nsresult HandleKeyPressEvent(
168 WidgetKeyboardEvent* aKeyboardEvent) override;
169 virtual nsIContent* GetFocusedContent() const override;
170 virtual nsIContent* GetFocusedContentForIME() const override;
171 virtual bool IsActiveInDOMWindow() const override;
172 virtual dom::EventTarget* GetDOMEventTarget() const override;
173 virtual Element* FindSelectionRoot(nsINode* aNode) const override;
174 virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) override;
175 virtual nsresult GetPreferredIMEState(widget::IMEState* aState) override;
176
177 /**
178 * GetBackgroundColorState() returns what the background color of the
179 * selection.
180 *
181 * @param aMixed true if there is more than one font color
182 * @param aOutColor Color string. "" is returned for none.
183 */
184 MOZ_CAN_RUN_SCRIPT nsresult GetBackgroundColorState(bool* aMixed,
185 nsAString& aOutColor);
186
187 /**
188 * PasteNoFormatting() pastes content in clipboard without any style
189 * information.
190 *
191 * @param aSelectionType nsIClipboard::kGlobalClipboard or
192 * nsIClipboard::kSelectionClipboard.
193 * @param aPrincipal Set subject principal if it may be called by
194 * JS. If set to nullptr, will be treated as
195 * called by system.
196 */
197 MOZ_CAN_RUN_SCRIPT nsresult PasteNoFormattingAsAction(
198 int32_t aSelectionType, nsIPrincipal* aPrincipal = nullptr);
199
200 /**
201 * PasteAsQuotationAsAction() pastes content in clipboard with newly created
202 * blockquote element. If the editor is in plaintext mode, will paste the
203 * content with appending ">" to start of each line.
204 *
205 * @param aClipboardType nsIClipboard::kGlobalClipboard or
206 * nsIClipboard::kSelectionClipboard.
207 * @param aDispatchPasteEvent true if this should dispatch ePaste event
208 * before pasting. Otherwise, false.
209 * @param aPrincipal Set subject principal if it may be called by
210 * JS. If set to nullptr, will be treated as
211 * called by system.
212 */
213 MOZ_CAN_RUN_SCRIPT virtual nsresult PasteAsQuotationAsAction(
214 int32_t aClipboardType, bool aDispatchPasteEvent,
215 nsIPrincipal* aPrincipal = nullptr) override;
216
217 /**
218 * Can we paste |aTransferable| or, if |aTransferable| is null, will a call
219 * to pasteTransferable later possibly succeed if given an instance of
220 * nsITransferable then? True if the doc is modifiable, and, if
221 * |aTransfeable| is non-null, we have pasteable data in |aTransfeable|.
222 */
223 virtual bool CanPasteTransferable(nsITransferable* aTransferable) override;
224
225 /**
226 * InsertLineBreakAsAction() is called when user inputs a line break with
227 * Shift + Enter or something.
228 *
229 * @param aPrincipal Set subject principal if it may be called by
230 * JS. If set to nullptr, will be treated as
231 * called by system.
232 */
233 MOZ_CAN_RUN_SCRIPT virtual nsresult InsertLineBreakAsAction(
234 nsIPrincipal* aPrincipal = nullptr) override;
235
236 /**
237 * InsertParagraphSeparatorAsAction() is called when user tries to separate
238 * current paragraph with Enter key press in HTMLEditor or something.
239 *
240 * @param aPrincipal Set subject principal if it may be called by
241 * JS. If set to nullptr, will be treated as
242 * called by system.
243 */
244 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
245 InsertParagraphSeparatorAsAction(nsIPrincipal* aPrincipal = nullptr);
246
247 MOZ_CAN_RUN_SCRIPT nsresult
248 InsertElementAtSelectionAsAction(Element* aElement, bool aDeleteSelection,
249 nsIPrincipal* aPrincipal = nullptr);
250
251 MOZ_CAN_RUN_SCRIPT nsresult InsertLinkAroundSelectionAsAction(
252 Element* aAnchorElement, nsIPrincipal* aPrincipal = nullptr);
253
254 /**
255 * CreateElementWithDefaults() creates new element whose name is
256 * aTagName with some default attributes are set. Note that this is a
257 * public utility method. I.e., just creates element, not insert it
258 * into the DOM tree.
259 * NOTE: This is available for internal use too since this does not change
260 * the DOM tree nor undo transactions, and does not refer Selection,
261 * etc.
262 *
263 * @param aTagName The new element's tag name. If the name is
264 * one of "href", "anchor" or "namedanchor",
265 * this creates an <a> element.
266 * @return Newly created element.
267 */
268 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> CreateElementWithDefaults(
269 const nsAtom& aTagName);
270
271 /**
272 * Indent or outdent content around Selection.
273 *
274 * @param aPrincipal Set subject principal if it may be called by
275 * JS. If set to nullptr, will be treated as
276 * called by system.
277 */
278 MOZ_CAN_RUN_SCRIPT nsresult
279 IndentAsAction(nsIPrincipal* aPrincipal = nullptr);
280 MOZ_CAN_RUN_SCRIPT nsresult
281 OutdentAsAction(nsIPrincipal* aPrincipal = nullptr);
282
283 MOZ_CAN_RUN_SCRIPT nsresult SetParagraphFormatAsAction(
284 const nsAString& aParagraphFormat, nsIPrincipal* aPrincipal = nullptr);
285
286 MOZ_CAN_RUN_SCRIPT nsresult AlignAsAction(const nsAString& aAlignType,
287 nsIPrincipal* aPrincipal = nullptr);
288
289 MOZ_CAN_RUN_SCRIPT nsresult RemoveListAsAction(
290 const nsAString& aListType, nsIPrincipal* aPrincipal = nullptr);
291
292 /**
293 * MakeOrChangeListAsAction() makes selected hard lines list element(s).
294 *
295 * @param aListElementTagName The new list element tag name. Must be
296 * nsGkAtoms::ul, nsGkAtoms::ol or
297 * nsGkAtoms::dl.
298 * @param aBulletType If this is not empty string, it's set
299 * to `type` attribute of new list item
300 * elements. Otherwise, existing `type`
301 * attributes will be removed.
302 * @param aSelectAllOfCurrentList Yes if this should treat all of
303 * ancestor list element at selection.
304 */
305 enum class SelectAllOfCurrentList { Yes, No };
306 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MakeOrChangeListAsAction(
307 nsAtom& aListElementTagName, const nsAString& aBulletType,
308 SelectAllOfCurrentList aSelectAllOfCurrentList,
309 nsIPrincipal* aPrincipal = nullptr);
310
311 /**
312 * event callback when a mouse button is pressed
313 * @param aX [IN] horizontal position of the pointer
314 * @param aY [IN] vertical position of the pointer
315 * @param aTarget [IN] the element triggering the event
316 * @param aMouseEvent [IN] the event
317 */
318 MOZ_CAN_RUN_SCRIPT nsresult OnMouseDown(int32_t aX, int32_t aY,
319 Element* aTarget,
320 dom::Event* aMouseEvent);
321
322 /**
323 * event callback when a mouse button is released
324 * @param aX [IN] horizontal position of the pointer
325 * @param aY [IN] vertical position of the pointer
326 */
327 MOZ_CAN_RUN_SCRIPT nsresult OnMouseUp(int32_t aX, int32_t aY);
328
329 /**
330 * event callback when the mouse pointer is moved
331 * @param aMouseEvent [IN] the event
332 */
333 MOZ_CAN_RUN_SCRIPT nsresult OnMouseMove(dom::MouseEvent* aMouseEvent);
334
335 /**
336 * IsCSSEnabled() returns true if this editor treats styles with style
337 * attribute of HTML elements. Otherwise, if this editor treats all styles
338 * with "font style elements" like <b>, <i>, etc, and <blockquote> to indent,
339 * align attribute to align contents, returns false.
340 */
IsCSSEnabled()341 bool IsCSSEnabled() const {
342 // TODO: removal of mCSSAware and use only the presence of mCSSEditUtils
343 return mCSSAware && mCSSEditUtils && mCSSEditUtils->IsCSSPrefChecked();
344 }
345
346 /**
347 * Enable/disable object resizers for <img> elements, <table> elements,
348 * absolute positioned elements (required absolute position editor enabled).
349 */
EnableObjectResizer(bool aEnable)350 MOZ_CAN_RUN_SCRIPT void EnableObjectResizer(bool aEnable) {
351 if (mIsObjectResizingEnabled == aEnable) {
352 return;
353 }
354
355 AutoEditActionDataSetter editActionData(
356 *this, EditAction::eEnableOrDisableResizer);
357 if (NS_WARN_IF(!editActionData.CanHandle())) {
358 return;
359 }
360
361 mIsObjectResizingEnabled = aEnable;
362 RefreshEditingUI();
363 }
IsObjectResizerEnabled()364 bool IsObjectResizerEnabled() const { return mIsObjectResizingEnabled; }
365
GetResizerTarget()366 Element* GetResizerTarget() const { return mResizedObject; }
367
368 /**
369 * Enable/disable inline table editor, e.g., adding new row or column,
370 * removing existing row or column.
371 */
EnableInlineTableEditor(bool aEnable)372 MOZ_CAN_RUN_SCRIPT void EnableInlineTableEditor(bool aEnable) {
373 if (mIsInlineTableEditingEnabled == aEnable) {
374 return;
375 }
376
377 AutoEditActionDataSetter editActionData(
378 *this, EditAction::eEnableOrDisableInlineTableEditingUI);
379 if (NS_WARN_IF(!editActionData.CanHandle())) {
380 return;
381 }
382
383 mIsInlineTableEditingEnabled = aEnable;
384 RefreshEditingUI();
385 }
IsInlineTableEditorEnabled()386 bool IsInlineTableEditorEnabled() const {
387 return mIsInlineTableEditingEnabled;
388 }
389
390 /**
391 * Enable/disable absolute position editor, resizing absolute positioned
392 * elements (required object resizers enabled) or positioning them with
393 * dragging grabber.
394 */
EnableAbsolutePositionEditor(bool aEnable)395 MOZ_CAN_RUN_SCRIPT void EnableAbsolutePositionEditor(bool aEnable) {
396 if (mIsAbsolutelyPositioningEnabled == aEnable) {
397 return;
398 }
399
400 AutoEditActionDataSetter editActionData(
401 *this, EditAction::eEnableOrDisableAbsolutePositionEditor);
402 if (NS_WARN_IF(!editActionData.CanHandle())) {
403 return;
404 }
405
406 mIsAbsolutelyPositioningEnabled = aEnable;
407 RefreshEditingUI();
408 }
IsAbsolutePositionEditorEnabled()409 bool IsAbsolutePositionEditorEnabled() const {
410 return mIsAbsolutelyPositioningEnabled;
411 }
412
413 // non-virtual methods of interface methods
414
415 /**
416 * returns the deepest absolutely positioned container of the selection
417 * if it exists or null.
418 */
419 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element>
420 GetAbsolutelyPositionedSelectionContainer() const;
421
GetPositionedElement()422 Element* GetPositionedElement() const { return mAbsolutelyPositionedObject; }
423
424 /**
425 * extracts the selection from the normal flow of the document and
426 * positions it.
427 *
428 * @param aEnabled [IN] true to absolutely position the selection,
429 * false to put it back in the normal flow
430 * @param aPrincipal Set subject principal if it may be called by
431 * JS. If set to nullptr, will be treated as
432 * called by system.
433 */
434 MOZ_CAN_RUN_SCRIPT nsresult SetSelectionToAbsoluteOrStaticAsAction(
435 bool aEnabled, nsIPrincipal* aPrincipal = nullptr);
436
437 /**
438 * returns the absolute z-index of a positioned element. Never returns 'auto'
439 * @return the z-index of the element
440 * @param aElement [IN] the element.
441 */
442 MOZ_CAN_RUN_SCRIPT int32_t GetZIndex(Element& aElement);
443
444 /**
445 * adds aChange to the z-index of the currently positioned element.
446 *
447 * @param aChange [IN] relative change to apply to current z-index
448 * @param aPrincipal Set subject principal if it may be called by
449 * JS. If set to nullptr, will be treated as
450 * called by system.
451 */
452 MOZ_CAN_RUN_SCRIPT nsresult
453 AddZIndexAsAction(int32_t aChange, nsIPrincipal* aPrincipal = nullptr);
454
455 MOZ_CAN_RUN_SCRIPT nsresult SetBackgroundColorAsAction(
456 const nsAString& aColor, nsIPrincipal* aPrincipal = nullptr);
457
458 /**
459 * SetInlinePropertyAsAction() sets a property which changes inline style of
460 * text. E.g., bold, italic, super and sub.
461 * This automatically removes exclusive style, however, treats all changes
462 * as a transaction.
463 *
464 * @param aPrincipal Set subject principal if it may be called by
465 * JS. If set to nullptr, will be treated as
466 * called by system.
467 */
468 MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertyAsAction(
469 nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aValue,
470 nsIPrincipal* aPrincipal = nullptr);
471
472 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlineProperty(
473 nsAtom* aHTMLProperty, nsAtom* aAttribute, const nsAString& aValue,
474 bool* aFirst, bool* aAny, bool* aAll) const;
475 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlinePropertyWithAttrValue(
476 nsAtom* aHTMLProperty, nsAtom* aAttribute, const nsAString& aValue,
477 bool* aFirst, bool* aAny, bool* aAll, nsAString& outValue);
478
479 /**
480 * RemoveInlinePropertyAsAction() removes a property which changes inline
481 * style of text. E.g., bold, italic, super and sub.
482 *
483 * @param aHTMLProperty Tag name whcih represents the inline style you want
484 * to remove. E.g., nsGkAtoms::strong, nsGkAtoms::b,
485 * etc. If nsGkAtoms::href, <a> element which has
486 * href attribute will be removed.
487 * If nsGkAtoms::name, <a> element which has non-empty
488 * name attribute will be removed.
489 * @param aAttribute If aHTMLProperty is nsGkAtoms::font, aAttribute should
490 * be nsGkAtoms::fase, nsGkAtoms::size, nsGkAtoms::color
491 * or nsGkAtoms::bgcolor. Otherwise, set nullptr.
492 * Must not use nsGkAtoms::_empty here.
493 * @param aPrincipal Set subject principal if it may be called by JS. If
494 * set to nullptr, will be treated as called by system.
495 */
496 MOZ_CAN_RUN_SCRIPT nsresult RemoveInlinePropertyAsAction(
497 nsStaticAtom& aHTMLProperty, nsStaticAtom* aAttribute,
498 nsIPrincipal* aPrincipal = nullptr);
499
500 MOZ_CAN_RUN_SCRIPT nsresult
501 RemoveAllInlinePropertiesAsAction(nsIPrincipal* aPrincipal = nullptr);
502
503 MOZ_CAN_RUN_SCRIPT nsresult
504 IncreaseFontSizeAsAction(nsIPrincipal* aPrincipal = nullptr);
505
506 MOZ_CAN_RUN_SCRIPT nsresult
507 DecreaseFontSizeAsAction(nsIPrincipal* aPrincipal = nullptr);
508
509 /**
510 * GetFontColorState() returns foreground color information in first
511 * range of Selection.
512 * If first range of Selection is collapsed and there is a cache of style for
513 * new text, aIsMixed is set to false and aColor is set to the cached color.
514 * If first range of Selection is collapsed and there is no cached color,
515 * this returns the color of the node, aIsMixed is set to false and aColor is
516 * set to the color.
517 * If first range of Selection is not collapsed, this collects colors of
518 * each node in the range. If there are two or more colors, aIsMixed is set
519 * to true and aColor is truncated. If only one color is set to all of the
520 * range, aIsMixed is set to false and aColor is set to the color.
521 * If there is no Selection ranges, aIsMixed is set to false and aColor is
522 * truncated.
523 *
524 * @param aIsMixed Must not be nullptr. This is set to true
525 * if there is two or more colors in first
526 * range of Selection.
527 * @param aColor Returns the color if only one color is set to
528 * all of first range in Selection. Otherwise,
529 * returns empty string.
530 * @return Returns error only when illegal cases, e.g.,
531 * Selection instance has gone, first range
532 * Selection is broken.
533 */
534 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
535 GetFontColorState(bool* aIsMixed, nsAString& aColor);
536
537 /**
538 * SetComposerCommandsUpdater() sets or unsets mComposerCommandsUpdater.
539 * This will crash in debug build if the editor already has an instance
540 * but called with another instance.
541 */
SetComposerCommandsUpdater(ComposerCommandsUpdater * aComposerCommandsUpdater)542 void SetComposerCommandsUpdater(
543 ComposerCommandsUpdater* aComposerCommandsUpdater) {
544 MOZ_ASSERT(!aComposerCommandsUpdater || !mComposerCommandsUpdater ||
545 aComposerCommandsUpdater == mComposerCommandsUpdater);
546 mComposerCommandsUpdater = aComposerCommandsUpdater;
547 }
548
DefaultParagraphSeparatorTagName()549 nsStaticAtom& DefaultParagraphSeparatorTagName() const {
550 return HTMLEditor::ToParagraphSeparatorTagName(mDefaultParagraphSeparator);
551 }
GetDefaultParagraphSeparator()552 ParagraphSeparator GetDefaultParagraphSeparator() const {
553 return mDefaultParagraphSeparator;
554 }
SetDefaultParagraphSeparator(ParagraphSeparator aSep)555 void SetDefaultParagraphSeparator(ParagraphSeparator aSep) {
556 mDefaultParagraphSeparator = aSep;
557 }
ToParagraphSeparatorTagName(ParagraphSeparator aSeparator)558 static nsStaticAtom& ToParagraphSeparatorTagName(
559 ParagraphSeparator aSeparator) {
560 switch (aSeparator) {
561 case ParagraphSeparator::div:
562 return *nsGkAtoms::div;
563 case ParagraphSeparator::p:
564 return *nsGkAtoms::p;
565 case ParagraphSeparator::br:
566 return *nsGkAtoms::br;
567 default:
568 MOZ_ASSERT_UNREACHABLE("New paragraph separator isn't handled here");
569 return *nsGkAtoms::div;
570 }
571 }
572
573 /**
574 * Modifies the table containing the selection according to the
575 * activation of an inline table editing UI element
576 * @param aUIAnonymousElement [IN] the inline table editing UI element
577 */
578 MOZ_CAN_RUN_SCRIPT nsresult
579 DoInlineTableEditingAction(const Element& aUIAnonymousElement);
580
581 /**
582 * GetInclusiveAncestorByTagName() looks for an element node whose name
583 * matches aTagName from aNode or anchor node of Selection to <body> element.
584 *
585 * @param aTagName The tag name which you want to look for.
586 * Must not be nsGkAtoms::_empty.
587 * If nsGkAtoms::list, the result may be <ul>, <ol> or
588 * <dl> element.
589 * If nsGkAtoms::td, the result may be <td> or <th>.
590 * If nsGkAtoms::href, the result may be <a> element
591 * which has "href" attribute with non-empty value.
592 * If nsGkAtoms::anchor, the result may be <a> which
593 * has "name" attribute with non-empty value.
594 * @param aContent Start node to look for the result.
595 * @return If an element which matches aTagName, returns
596 * an Element. Otherwise, nullptr.
597 */
598 Element* GetInclusiveAncestorByTagName(const nsStaticAtom& aTagName,
599 nsIContent& aContent) const;
600
601 /**
602 * Get an active editor's editing host in DOM window. If this editor isn't
603 * active in the DOM window, this returns NULL.
604 */
605 Element* GetActiveEditingHost() const;
606
607 /**
608 * Retruns true if we're in designMode.
609 */
IsInDesignMode()610 bool IsInDesignMode() const {
611 Document* document = GetDocument();
612 return document && document->HasFlag(NODE_IS_EDITABLE);
613 }
614
615 /**
616 * NotifyEditingHostMaybeChanged() is called when new element becomes
617 * contenteditable when the document already had contenteditable elements.
618 */
619 void NotifyEditingHostMaybeChanged();
620
621 /** Insert a string as quoted text
622 * (whose representation is dependant on the editor type),
623 * replacing the selected text (if any).
624 *
625 * @param aQuotedText The actual text to be quoted
626 * @parem aNodeInserted Return the node which was inserted.
627 */
628 MOZ_CAN_RUN_SCRIPT // USED_BY_COMM_CENTRAL
629 nsresult
630 InsertAsQuotation(const nsAString& aQuotedText, nsINode** aNodeInserted);
631
632 /**
633 * Inserts a plaintext string at the current location,
634 * with special processing for lines beginning with ">",
635 * which will be treated as mail quotes and inserted
636 * as plaintext quoted blocks.
637 * If the selection is not collapsed, the selection is deleted
638 * and the insertion takes place at the resulting collapsed selection.
639 *
640 * @param aString the string to be inserted
641 */
642 MOZ_CAN_RUN_SCRIPT nsresult
643 InsertTextWithQuotations(const nsAString& aStringToInsert);
644
645 MOZ_CAN_RUN_SCRIPT nsresult InsertHTMLAsAction(
646 const nsAString& aInString, nsIPrincipal* aPrincipal = nullptr);
647
648 protected: // May be called by friends.
649 /****************************************************************************
650 * Some friend classes are allowed to call the following protected methods.
651 * However, those methods won't prepare caches of some objects which are
652 * necessary for them. So, if you call them from friend classes, you need
653 * to make sure that AutoEditActionDataSetter is created.
654 ****************************************************************************/
655
656 /**
657 * InsertBRElementWithTransaction() creates a <br> element and inserts it
658 * before aPointToInsert. Then, tries to collapse selection at or after the
659 * new <br> node if aSelect is not eNone.
660 *
661 * @param aPointToInsert The DOM point where should be <br> node inserted
662 * before.
663 * @param aSelect If eNone, this won't change selection.
664 * If eNext, selection will be collapsed after
665 * the <br> element.
666 * If ePrevious, selection will be collapsed at
667 * the <br> element.
668 * @return The new <br> node. If failed to create new
669 * <br> node, returns nullptr.
670 */
671 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertBRElementWithTransaction(
672 const EditorDOMPoint& aPointToInsert, EDirection aSelect = eNone);
673
674 /**
675 * DeleteNodeWithTransaction() removes aContent from the DOM tree if it's
676 * modifiable. Note that this is not an override of same method of
677 * EditorBase.
678 *
679 * @param aContent The node to be removed from the DOM tree.
680 */
681 MOZ_CAN_RUN_SCRIPT nsresult DeleteNodeWithTransaction(nsIContent& aContent);
682
683 /**
684 * DeleteTextWithTransaction() removes text in the range from aTextNode if
685 * it's modifiable. Note that this not an override of same method of
686 * EditorBase.
687 *
688 * @param aTextNode The text node which should be modified.
689 * @param aOffset Start offset of removing text in aTextNode.
690 * @param aLength Length of removing text.
691 */
692 MOZ_CAN_RUN_SCRIPT nsresult DeleteTextWithTransaction(dom::Text& aTextNode,
693 uint32_t aOffset,
694 uint32_t aLength);
695
696 /**
697 * ReplaceTextWithTransaction() replaces text in the range with
698 * aStringToInsert.
699 */
700 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ReplaceTextWithTransaction(
701 dom::Text& aTextNode, uint32_t aOffset, uint32_t aLength,
702 const nsAString& aStringToInsert);
703
704 /**
705 * DeleteParentBlocksIfEmpty() removes parent block elements if they
706 * don't have visible contents. Note that due performance issue of
707 * WSRunObject, this call may be expensive. And also note that this
708 * removes a empty block with a transaction. So, please make sure that
709 * you've already created `AutoPlaceholderBatch`.
710 *
711 * @param aPoint The point whether this method climbing up the DOM
712 * tree to remove empty parent blocks.
713 * @return NS_OK if one or more empty block parents are deleted.
714 * NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND if the point is
715 * not in empty block.
716 * Or NS_ERROR_* if something unexpected occurs.
717 */
718 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
719 DeleteParentBlocksWithTransactionIfEmpty(const EditorDOMPoint& aPoint);
720
721 /**
722 * InsertTextWithTransaction() inserts aStringToInsert at aPointToInsert.
723 */
724 MOZ_CAN_RUN_SCRIPT virtual nsresult InsertTextWithTransaction(
725 Document& aDocument, const nsAString& aStringToInsert,
726 const EditorRawDOMPoint& aPointToInsert,
727 EditorRawDOMPoint* aPointAfterInsertedString = nullptr) override;
728
729 /**
730 * CopyLastEditableChildStyles() clones inline container elements into
731 * aPreviousBlock to aNewBlock to keep using same style in it.
732 *
733 * @param aPreviousBlock The previous block element. All inline
734 * elements which are last sibling of each level
735 * are cloned to aNewBlock.
736 * @param aNewBlock New block container element.
737 * @param aNewBRElement If this method creates a new <br> element for
738 * placeholder, this is set to the new <br>
739 * element.
740 */
741 MOZ_CAN_RUN_SCRIPT nsresult CopyLastEditableChildStylesWithTransaction(
742 Element& aPreviousBlock, Element& aNewBlock,
743 RefPtr<Element>* aNewBRElement);
744
745 /**
746 * RemoveBlockContainerWithTransaction() removes aElement from the DOM tree
747 * but moves its all children to its parent node and if its parent needs <br>
748 * element to have at least one line-height, this inserts <br> element
749 * automatically.
750 *
751 * @param aElement Block element to be removed.
752 */
753 MOZ_CAN_RUN_SCRIPT nsresult
754 RemoveBlockContainerWithTransaction(Element& aElement);
755
756 virtual Element* GetEditorRoot() const override;
757 MOZ_CAN_RUN_SCRIPT virtual nsresult RemoveAttributeOrEquivalent(
758 Element* aElement, nsAtom* aAttribute,
759 bool aSuppressTransaction) override;
760 MOZ_CAN_RUN_SCRIPT virtual nsresult SetAttributeOrEquivalent(
761 Element* aElement, nsAtom* aAttribute, const nsAString& aValue,
762 bool aSuppressTransaction) override;
763 using EditorBase::RemoveAttributeOrEquivalent;
764 using EditorBase::SetAttributeOrEquivalent;
765
766 /**
767 * Returns container element of ranges in Selection. If Selection is
768 * collapsed, returns focus container node (or its parent element).
769 * If Selection selects only one element node, returns the element node.
770 * If Selection is only one range, returns common ancestor of the range.
771 * XXX If there are two or more Selection ranges, this returns parent node
772 * of start container of a range which starts with different node from
773 * start container of the first range.
774 */
775 Element* GetSelectionContainerElement() const;
776
777 /**
778 * GetFirstSelectedTableCellElement() returns a <td> or <th> element if
779 * first range of Selection (i.e., result of Selection::GetRangeAt(0))
780 * selects a <td> element or <th> element. Even if Selection is in
781 * a cell element, this returns nullptr. And even if 2nd or later
782 * range of Selection selects a cell element, also returns nullptr.
783 * Note that when this looks for a cell element, this resets the internal
784 * index of ranges of Selection. When you call
785 * GetNextSelectedTableCellElement() after a call of this, it'll return 2nd
786 * selected cell if there is.
787 *
788 * @param aRv Returns error if there is no selection or
789 * first range of Selection is unexpected.
790 * @return A <td> or <th> element is selected by first
791 * range of Selection. Note that the range must
792 * be: startContaienr and endContainer are same
793 * <tr> element, startOffset + 1 equals endOffset.
794 */
795 already_AddRefed<Element> GetFirstSelectedTableCellElement(
796 ErrorResult& aRv) const;
797
798 /**
799 * GetNextSelectedTableCellElement() is a stateful method to retrieve
800 * selected table cell elements which are selected by 2nd or later ranges
801 * of Selection. When you call GetFirstSelectedTableCellElement(), it
802 * resets internal counter of this method. Then, following calls of
803 * GetNextSelectedTableCellElement() scans the remaining ranges of Selection.
804 * If a range selects a <td> or <th> element, returns the cell element.
805 * If a range selects an element but neither <td> nor <th> element, this
806 * ignores the range. If a range is in a text node, returns null without
807 * throwing exception, but stops scanning the remaining ranges even you
808 * call this again.
809 * Note that this may cross <table> boundaries since this method just
810 * scans all ranges of Selection. Therefore, returning cells which
811 * belong to different <table> elements.
812 *
813 * @param aRv Returns error if Selection doesn't have
814 * range properly.
815 * @return A <td> or <th> element if one of remaining
816 * ranges selects a <td> or <th> element unless
817 * this does not meet a range in a text node.
818 */
819 already_AddRefed<Element> GetNextSelectedTableCellElement(
820 ErrorResult& aRv) const;
821
822 /**
823 * DeleteTableCellContentsWithTransaction() removes any contents in cell
824 * elements. If two or more cell elements are selected, this removes
825 * all selected cells' contents. Otherwise, this removes contents of
826 * a cell which contains first selection range. This does not return
827 * error even if selection is not in cell element, just does nothing.
828 */
829 MOZ_CAN_RUN_SCRIPT nsresult DeleteTableCellContentsWithTransaction();
830
831 static void IsNextCharInNodeWhitespace(nsIContent* aContent, int32_t aOffset,
832 bool* outIsSpace, bool* outIsNBSP,
833 nsIContent** outNode = nullptr,
834 int32_t* outOffset = 0);
835 static void IsPrevCharInNodeWhitespace(nsIContent* aContent, int32_t aOffset,
836 bool* outIsSpace, bool* outIsNBSP,
837 nsIContent** outNode = nullptr,
838 int32_t* outOffset = 0);
839
840 /**
841 * extracts an element from the normal flow of the document and
842 * positions it, and puts it back in the normal flow.
843 * @param aElement [IN] the element
844 * @param aEnabled [IN] true to absolutely position the element,
845 * false to put it back in the normal flow
846 */
847 MOZ_CAN_RUN_SCRIPT nsresult SetPositionToAbsoluteOrStatic(Element& aElement,
848 bool aEnabled);
849
850 /**
851 * adds aChange to the z-index of an arbitrary element.
852 * @param aElement [IN] the element
853 * @param aChange [IN] relative change to apply to current z-index of
854 * the element
855 * @param aReturn [OUT] the new z-index of the element
856 */
857 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RelativeChangeElementZIndex(
858 Element& aElement, int32_t aChange, int32_t* aReturn);
859
860 /**
861 * Join together any adjacent editable text nodes in the range.
862 */
863 MOZ_CAN_RUN_SCRIPT nsresult CollapseAdjacentTextNodes(nsRange& aRange);
864
865 /**
866 * IsInVisibleTextFrames() returns true if all text in aText is in visible
867 * text frames. Callers have to guarantee that there is no pending reflow.
868 */
869 bool IsInVisibleTextFrames(dom::Text& aText) const;
870
871 /**
872 * IsVisibleTextNode() returns true if aText has visible text. If it has
873 * only whitespaces and they are collapsed, returns false.
874 */
875 bool IsVisibleTextNode(Text& aText) const;
876
877 /**
878 * IsEmptyNode() figures out if aNode is an empty node. A block can have
879 * children and still be considered empty, if the children are empty or
880 * non-editable.
881 */
882 bool IsEmptyNode(nsINode& aNode, bool aSingleBRDoesntCount = false,
883 bool aListOrCellNotEmpty = false,
884 bool aSafeToAskFrames = false) const {
885 bool seenBR = false;
886 return IsEmptyNodeImpl(aNode, aSingleBRDoesntCount, aListOrCellNotEmpty,
887 aSafeToAskFrames, &seenBR);
888 }
889
890 bool IsEmptyNodeImpl(nsINode& aNode, bool aSingleBRDoesntCount,
891 bool aListOrCellNotEmpty, bool aSafeToAskFrames,
892 bool* aSeenBR) const;
893
HasAttributes(Element * aElement)894 static bool HasAttributes(Element* aElement) {
895 MOZ_ASSERT(aElement);
896 uint32_t attrCount = aElement->GetAttrCount();
897 return attrCount > 1 ||
898 (1 == attrCount &&
899 !aElement->GetAttrNameAt(0)->Equals(nsGkAtoms::mozdirty));
900 }
901
902 /**
903 * Content-based query returns true if <aProperty aAttribute=aValue> effects
904 * aNode. If <aProperty aAttribute=aValue> contains aNode, but
905 * <aProperty aAttribute=SomeOtherValue> also contains aNode and the second is
906 * more deeply nested than the first, then the first does not effect aNode.
907 *
908 * @param aNode The target of the query
909 * @param aProperty The property that we are querying for
910 * @param aAttribute The attribute of aProperty, example: color in
911 * <FONT color="blue"> May be null.
912 * @param aValue The value of aAttribute, example: blue in
913 * <FONT color="blue"> May be null. Ignored if aAttribute
914 * is null.
915 * @param outValue [OUT] the value of the attribute, if aIsSet is true
916 * @return true if <aProperty aAttribute=aValue> effects
917 * aNode.
918 *
919 * The nsIContent variant returns aIsSet instead of using an out parameter.
920 */
921 static bool IsTextPropertySetByContent(nsINode* aNode, nsAtom* aProperty,
922 nsAtom* aAttribute,
923 const nsAString* aValue,
924 nsAString* outValue = nullptr);
925
926 static dom::Element* GetLinkElement(nsINode* aNode);
927
928 /**
929 * Small utility routine to test if a break node is visible to user.
930 */
931 bool IsVisibleBRElement(const nsINode* aNode);
932
933 /**
934 * Helper routines for font size changing.
935 */
936 enum class FontSize { incr, decr };
937 MOZ_CAN_RUN_SCRIPT nsresult RelativeFontChangeOnTextNode(FontSize aDir,
938 Text& aTextNode,
939 int32_t aStartOffset,
940 int32_t aEndOffset);
941
942 MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertyOnNode(nsIContent& aNode,
943 nsAtom& aProperty,
944 nsAtom* aAttribute,
945 const nsAString& aValue);
946
947 /**
948 * SplitAncestorStyledInlineElementsAtRangeEdges() splits all ancestor inline
949 * elements in the block at both aStartPoint and aEndPoint if given style
950 * matches with some of them.
951 *
952 * @param aStartPoint Start of range to split ancestor inline elements.
953 * @param aEndPoint End of range to split ancestor inline elements.
954 * @param aProperty The style tag name which you want to split. Set
955 * nullptr if you want to split any styled elements.
956 * @param aAttribute Attribute name if aProperty has some styles like
957 * nsGkAtoms::font.
958 */
959 [[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffResult
960 SplitAncestorStyledInlineElementsAtRangeEdges(
961 const EditorDOMPoint& aStartPoint, const EditorDOMPoint& aEndPoint,
962 nsAtom* aProperty, nsAtom* aAttribute);
963
964 /**
965 * SplitAncestorStyledInlineElementsAt() splits ancestor inline elements at
966 * aPointToSplit if specified style matches with them.
967 *
968 * @param aPointToSplit The point to split style at.
969 * @param aProperty The style tag name which you want to split.
970 * Set nullptr if you want to split any styled
971 * elements.
972 * @param aAttribute Attribute name if aProperty has some styles
973 * like nsGkAtoms::font.
974 * @return The result of SplitNodeDeepWithTransaction()
975 * with topmost split element. If this didn't
976 * find inline elements to be split, Handled()
977 * returns false.
978 */
979 [[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
980 SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit,
981 nsAtom* aProperty, nsAtom* aAttribute);
982
983 /**
984 * GetPriorHTMLSibling() returns the previous editable sibling, if there is
985 * one within the parent, optionally skipping text nodes that are only
986 * whitespace.
987 */
988 enum class SkipWhitespace { Yes, No };
989 nsIContent* GetPriorHTMLSibling(nsINode* aNode,
990 SkipWhitespace = SkipWhitespace::No) const;
991
992 /**
993 * GetNextHTMLSibling() returns the next editable sibling, if there is
994 * one within the parent, optionally skipping text nodes that are only
995 * whitespace.
996 */
997 nsIContent* GetNextHTMLSibling(nsINode* aNode,
998 SkipWhitespace = SkipWhitespace::No) const;
999
1000 // Helper for GetPriorHTMLSibling/GetNextHTMLSibling.
SkippableWhitespace(nsINode * aNode,SkipWhitespace aSkipWS)1001 static bool SkippableWhitespace(nsINode* aNode, SkipWhitespace aSkipWS) {
1002 return aSkipWS == SkipWhitespace::Yes && aNode->IsText() &&
1003 aNode->AsText()->TextIsOnlyWhitespace();
1004 }
1005
1006 /**
1007 * GetPreviousHTMLElementOrText*() methods are similar to
1008 * EditorBase::GetPreviousElementOrText*() but this won't return nodes
1009 * outside active editing host.
1010 */
GetPreviousHTMLElementOrText(const nsINode & aNode)1011 nsIContent* GetPreviousHTMLElementOrText(const nsINode& aNode) const {
1012 return GetPreviousHTMLElementOrTextInternal(aNode, false);
1013 }
GetPreviousHTMLElementOrTextInBlock(const nsINode & aNode)1014 nsIContent* GetPreviousHTMLElementOrTextInBlock(const nsINode& aNode) const {
1015 return GetPreviousHTMLElementOrTextInternal(aNode, true);
1016 }
1017 template <typename PT, typename CT>
GetPreviousHTMLElementOrText(const EditorDOMPointBase<PT,CT> & aPoint)1018 nsIContent* GetPreviousHTMLElementOrText(
1019 const EditorDOMPointBase<PT, CT>& aPoint) const {
1020 return GetPreviousHTMLElementOrTextInternal(aPoint, false);
1021 }
1022 template <typename PT, typename CT>
GetPreviousHTMLElementOrTextInBlock(const EditorDOMPointBase<PT,CT> & aPoint)1023 nsIContent* GetPreviousHTMLElementOrTextInBlock(
1024 const EditorDOMPointBase<PT, CT>& aPoint) const {
1025 return GetPreviousHTMLElementOrTextInternal(aPoint, true);
1026 }
1027
1028 /**
1029 * GetPreviousHTMLElementOrTextInternal() methods are common implementation
1030 * of above methods. Please don't use this method directly.
1031 */
1032 nsIContent* GetPreviousHTMLElementOrTextInternal(const nsINode& aNode,
1033 bool aNoBlockCrossing) const;
1034 template <typename PT, typename CT>
1035 nsIContent* GetPreviousHTMLElementOrTextInternal(
1036 const EditorDOMPointBase<PT, CT>& aPoint, bool aNoBlockCrossing) const;
1037
1038 /**
1039 * GetPreviousEditableHTMLNode*() methods are similar to
1040 * EditorBase::GetPreviousEditableNode() but this won't return nodes outside
1041 * active editing host.
1042 */
GetPreviousEditableHTMLNode(nsINode & aNode)1043 nsIContent* GetPreviousEditableHTMLNode(nsINode& aNode) const {
1044 return GetPreviousEditableHTMLNodeInternal(aNode, false);
1045 }
GetPreviousEditableHTMLNodeInBlock(nsINode & aNode)1046 nsIContent* GetPreviousEditableHTMLNodeInBlock(nsINode& aNode) const {
1047 return GetPreviousEditableHTMLNodeInternal(aNode, true);
1048 }
1049 template <typename PT, typename CT>
GetPreviousEditableHTMLNode(const EditorDOMPointBase<PT,CT> & aPoint)1050 nsIContent* GetPreviousEditableHTMLNode(
1051 const EditorDOMPointBase<PT, CT>& aPoint) const {
1052 return GetPreviousEditableHTMLNodeInternal(aPoint, false);
1053 }
1054 template <typename PT, typename CT>
GetPreviousEditableHTMLNodeInBlock(const EditorDOMPointBase<PT,CT> & aPoint)1055 nsIContent* GetPreviousEditableHTMLNodeInBlock(
1056 const EditorDOMPointBase<PT, CT>& aPoint) const {
1057 return GetPreviousEditableHTMLNodeInternal(aPoint, true);
1058 }
1059
1060 /**
1061 * GetPreviousEditableHTMLNodeInternal() methods are common implementation
1062 * of above methods. Please don't use this method directly.
1063 */
1064 nsIContent* GetPreviousEditableHTMLNodeInternal(nsINode& aNode,
1065 bool aNoBlockCrossing) const;
1066 template <typename PT, typename CT>
1067 nsIContent* GetPreviousEditableHTMLNodeInternal(
1068 const EditorDOMPointBase<PT, CT>& aPoint, bool aNoBlockCrossing) const;
1069
1070 /**
1071 * GetNextHTMLElementOrText*() methods are similar to
1072 * EditorBase::GetNextElementOrText*() but this won't return nodes outside
1073 * active editing host.
1074 *
1075 * Note that same as EditorBase::GetTextEditableNode(), methods which take
1076 * |const EditorRawDOMPoint&| start to search from the node pointed by it.
1077 * On the other hand, methods which take |nsINode&| start to search from
1078 * next node of aNode.
1079 */
GetNextHTMLElementOrText(const nsINode & aNode)1080 nsIContent* GetNextHTMLElementOrText(const nsINode& aNode) const {
1081 return GetNextHTMLElementOrTextInternal(aNode, false);
1082 }
GetNextHTMLElementOrTextInBlock(const nsINode & aNode)1083 nsIContent* GetNextHTMLElementOrTextInBlock(const nsINode& aNode) const {
1084 return GetNextHTMLElementOrTextInternal(aNode, true);
1085 }
1086 template <typename PT, typename CT>
GetNextHTMLElementOrText(const EditorDOMPointBase<PT,CT> & aPoint)1087 nsIContent* GetNextHTMLElementOrText(
1088 const EditorDOMPointBase<PT, CT>& aPoint) const {
1089 return GetNextHTMLElementOrTextInternal(aPoint, false);
1090 }
1091 template <typename PT, typename CT>
GetNextHTMLElementOrTextInBlock(const EditorDOMPointBase<PT,CT> & aPoint)1092 nsIContent* GetNextHTMLElementOrTextInBlock(
1093 const EditorDOMPointBase<PT, CT>& aPoint) const {
1094 return GetNextHTMLElementOrTextInternal(aPoint, true);
1095 }
1096
1097 /**
1098 * GetNextHTMLNodeInternal() methods are common implementation
1099 * of above methods. Please don't use this method directly.
1100 */
1101 nsIContent* GetNextHTMLElementOrTextInternal(const nsINode& aNode,
1102 bool aNoBlockCrossing) const;
1103 template <typename PT, typename CT>
1104 nsIContent* GetNextHTMLElementOrTextInternal(
1105 const EditorDOMPointBase<PT, CT>& aPoint, bool aNoBlockCrossing) const;
1106
1107 /**
1108 * GetNextEditableHTMLNode*() methods are similar to
1109 * EditorBase::GetNextEditableNode() but this won't return nodes outside
1110 * active editing host.
1111 *
1112 * Note that same as EditorBase::GetTextEditableNode(), methods which take
1113 * |const EditorRawDOMPoint&| start to search from the node pointed by it.
1114 * On the other hand, methods which take |nsINode&| start to search from
1115 * next node of aNode.
1116 */
GetNextEditableHTMLNode(nsINode & aNode)1117 nsIContent* GetNextEditableHTMLNode(nsINode& aNode) const {
1118 return GetNextEditableHTMLNodeInternal(aNode, false);
1119 }
GetNextEditableHTMLNodeInBlock(nsINode & aNode)1120 nsIContent* GetNextEditableHTMLNodeInBlock(nsINode& aNode) const {
1121 return GetNextEditableHTMLNodeInternal(aNode, true);
1122 }
1123 template <typename PT, typename CT>
GetNextEditableHTMLNode(const EditorDOMPointBase<PT,CT> & aPoint)1124 nsIContent* GetNextEditableHTMLNode(
1125 const EditorDOMPointBase<PT, CT>& aPoint) const {
1126 return GetNextEditableHTMLNodeInternal(aPoint, false);
1127 }
1128 template <typename PT, typename CT>
GetNextEditableHTMLNodeInBlock(const EditorDOMPointBase<PT,CT> & aPoint)1129 nsIContent* GetNextEditableHTMLNodeInBlock(
1130 const EditorDOMPointBase<PT, CT>& aPoint) const {
1131 return GetNextEditableHTMLNodeInternal(aPoint, true);
1132 }
1133
1134 /**
1135 * GetNextEditableHTMLNodeInternal() methods are common implementation
1136 * of above methods. Please don't use this method directly.
1137 */
1138 nsIContent* GetNextEditableHTMLNodeInternal(nsINode& aNode,
1139 bool aNoBlockCrossing) const;
1140 template <typename PT, typename CT>
1141 nsIContent* GetNextEditableHTMLNodeInternal(
1142 const EditorDOMPointBase<PT, CT>& aPoint, bool aNoBlockCrossing) const;
1143
1144 bool IsFirstEditableChild(nsINode* aNode) const;
1145 bool IsLastEditableChild(nsINode* aNode) const;
1146 nsIContent* GetFirstEditableChild(nsINode& aNode) const;
1147 nsIContent* GetLastEditableChild(nsINode& aNode) const;
1148
1149 nsIContent* GetFirstEditableLeaf(nsINode& aNode) const;
1150 nsIContent* GetLastEditableLeaf(nsINode& aNode) const;
1151
1152 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlinePropertyBase(
1153 nsAtom& aHTMLProperty, nsAtom* aAttribute, const nsAString* aValue,
1154 bool* aFirst, bool* aAny, bool* aAll, nsAString* outValue) const;
1155
1156 /**
1157 * ClearStyleAt() splits parent elements to remove the specified style.
1158 * If this splits some parent elements at near their start or end, such
1159 * empty elements will be removed. Then, remove the specified style
1160 * from the point and returns DOM point to put caret.
1161 *
1162 * @param aPoint The point to clear style at.
1163 * @param aProperty An HTML tag name which represents a style.
1164 * Set nullptr if you want to clear all styles.
1165 * @param aAttribute Attribute name if aProperty has some styles like
1166 * nsGkAtoms::font.
1167 */
1168 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditResult ClearStyleAt(
1169 const EditorDOMPoint& aPoint, nsAtom* aProperty, nsAtom* aAttribute);
1170
1171 MOZ_CAN_RUN_SCRIPT nsresult SetPositionToAbsolute(Element& aElement);
1172 MOZ_CAN_RUN_SCRIPT nsresult SetPositionToStatic(Element& aElement);
1173
1174 /**
1175 * OnModifyDocument() is called when the editor is changed. This should
1176 * be called only by runnable in HTMLEditor::OnDocumentModified() to call
1177 * HTMLEditor::OnModifyDocument() with AutoEditActionDataSetter instance.
1178 */
1179 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult OnModifyDocument();
1180
1181 /**
1182 * DoSplitNode() creates a new node (left node) identical to an existing
1183 * node (right node), and split the contents between the same point in both
1184 * nodes.
1185 *
1186 * @param aStartOfRightNode The point to split. Its container will be
1187 * the right node, i.e., become the new node's
1188 * next sibling. And the point will be start
1189 * of the right node.
1190 * @param aNewLeftNode The new node called as left node, so, this
1191 * becomes the container of aPointToSplit's
1192 * previous sibling.
1193 * @param aError Must have not already failed.
1194 * If succeed to insert aLeftNode before the
1195 * right node and remove unnecessary contents
1196 * (and collapse selection at end of the left
1197 * node if necessary), returns no error.
1198 * Otherwise, an error.
1199 */
1200 MOZ_CAN_RUN_SCRIPT void DoSplitNode(const EditorDOMPoint& aStartOfRightNode,
1201 nsIContent& aNewLeftNode,
1202 ErrorResult& aError);
1203
1204 /**
1205 * DoJoinNodes() merges contents in aContentToJoin to aContentToKeep and
1206 * remove aContentToJoin from the DOM tree. aContentToJoin and aContentToKeep
1207 * must have same parent, aParent. Additionally, if one of aContentToJoin or
1208 * aContentToKeep is a text node, the other must be a text node.
1209 *
1210 * @param aContentToKeep The node that will remain after the join.
1211 * @param aContentToJoin The node that will be joined with aContentToKeep.
1212 * There is no requirement that the two nodes be of the
1213 * same type.
1214 */
1215 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1216 DoJoinNodes(nsIContent& aContentToKeep, nsIContent& aContentToJoin);
1217
1218 protected: // edit sub-action handler
1219 /**
1220 * CanHandleHTMLEditSubAction() checks whether there is at least one
1221 * selection range or not, and whether the first range is editable.
1222 * If it's not editable, `Canceled()` of the result returns true.
1223 * If `Selection` is in odd situation, returns an error.
1224 *
1225 * XXX I think that `IsSelectionEditable()` is better name, but it's already
1226 * in `EditorBase`...
1227 */
1228 EditActionResult CanHandleHTMLEditSubAction() const;
1229
1230 /**
1231 * EnsureCaretNotAfterPaddingBRElement() makes sure that caret is NOT after
1232 * padding `<br>` element for preventing insertion after padding `<br>`
1233 * element at empty last line.
1234 * NOTE: This method should be called only when `Selection` is collapsed
1235 * because `Selection` is a pain to work with when not collapsed.
1236 * (no good way to extend start or end of selection), so we need to
1237 * ignore those types of selections.
1238 */
1239 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1240 EnsureCaretNotAfterPaddingBRElement();
1241
1242 /**
1243 * PrepareInlineStylesForCaret() consider inline styles from top level edit
1244 * sub-action and setting it to `mTypeInState` and clear inline style cache
1245 * if necessary.
1246 * NOTE: This method should be called only when `Selection` is collapsed.
1247 */
1248 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult PrepareInlineStylesForCaret();
1249
1250 /**
1251 * HandleInsertText() handles inserting text at selection.
1252 *
1253 * @param aEditSubAction Must be EditSubAction::eInsertText or
1254 * EditSubAction::eInsertTextComingFromIME.
1255 * @param aInsertionString String to be inserted at selection.
1256 */
1257 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual EditActionResult HandleInsertText(
1258 EditSubAction aEditSubAction, const nsAString& aInsertionString) final;
1259
1260 /**
1261 * GetInlineStyles() retrieves the style of aNode and modifies each item of
1262 * aStyleCacheArray. This might cause flushing layout at retrieving computed
1263 * values of CSS properties.
1264 */
1265 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1266 GetInlineStyles(nsIContent& aContent, AutoStyleCacheArray& aStyleCacheArray);
1267
1268 /**
1269 * CacheInlineStyles() caches style of aContent into mCachedInlineStyles of
1270 * TopLevelEditSubAction. This may cause flushing layout at retrieving
1271 * computed value of CSS properties.
1272 */
1273 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1274 CacheInlineStyles(nsIContent& aContent);
1275
1276 /**
1277 * ReapplyCachedStyles() restores some styles which are disappeared during
1278 * handling edit action and it should be restored. This may cause flushing
1279 * layout at retrieving computed value of CSS properties.
1280 */
1281 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ReapplyCachedStyles();
1282
1283 /**
1284 * CreateStyleForInsertText() sets CSS properties which are stored in
1285 * TypeInState to proper element node.
1286 * XXX This modifies Selection, but should return insertion point instead.
1287 *
1288 * @param aAbstractRange Set current selection range where new text
1289 * should be inserted.
1290 */
1291 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1292 CreateStyleForInsertText(const dom::AbstractRange& aAbstractRange);
1293
1294 /**
1295 * GetMostAncestorMailCiteElement() returns most-ancestor mail cite element.
1296 * "mail cite element" is <pre> element when it's in plaintext editor mode
1297 * or an element with which calling HTMLEditUtils::IsMailCite() returns true.
1298 *
1299 * @param aNode The start node to look for parent mail cite elements.
1300 */
1301 Element* GetMostAncestorMailCiteElement(nsINode& aNode) const;
1302
1303 /**
1304 * SplitMailCiteElements() splits mail-cite elements at start of Selection if
1305 * Selection starts from inside a mail-cite element. Of course, if it's
1306 * necessary, this inserts <br> node to new left nodes or existing right
1307 * nodes.
1308 * XXX This modifies Selection, but should return SplitNodeResult() instead.
1309 */
1310 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
1311 SplitMailCiteElements(const EditorDOMPoint& aPointToSplit);
1312
1313 /**
1314 * InsertBRElement() inserts a <br> element into aInsertToBreak.
1315 * This may split container elements at the point and/or may move following
1316 * <br> element to immediately after the new <br> element if necessary.
1317 * XXX This method name is too generic and unclear whether such complicated
1318 * things will be done automatically or not.
1319 * XXX This modifies Selection, but should return CreateElementResult instead.
1320 *
1321 * @param aInsertToBreak The point where new <br> element will be
1322 * inserted before.
1323 */
1324 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1325 InsertBRElement(const EditorDOMPoint& aInsertToBreak);
1326
1327 /**
1328 * GetMostAncestorInlineElement() returns the most ancestor inline element
1329 * between aNode and the editing host. Even if the editing host is an inline
1330 * element, this method never returns the editing host as the result.
1331 */
1332 nsIContent* GetMostAncestorInlineElement(nsINode& aNode) const;
1333
1334 /**
1335 * SplitParentInlineElementsAtRangeEdges() splits parent inline nodes at both
1336 * start and end of aRangeItem. If this splits at every point, this modifies
1337 * aRangeItem to point each split point (typically, right node).
1338 *
1339 * @param aRangeItem [in/out] One or two DOM points where should be
1340 * split. Will be modified to split point if
1341 * they're split.
1342 */
1343 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1344 SplitParentInlineElementsAtRangeEdges(RangeItem& aRangeItem);
1345
1346 /**
1347 * SplitParentInlineElementsAtRangeEdges(nsTArray<RefPtr<nsRange>>&) calls
1348 * SplitParentInlineElementsAtRangeEdges(RangeItem&) for each range. Then,
1349 * updates given range to keep edit target ranges as expected.
1350 */
1351 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1352 SplitParentInlineElementsAtRangeEdges(
1353 nsTArray<RefPtr<nsRange>>& aArrayOfRanges);
1354
1355 /**
1356 * SplitElementsAtEveryBRElement() splits before all <br> elements in
1357 * aMostAncestorToBeSplit. All <br> nodes will be moved before right node
1358 * at splitting its parent. Finally, this returns left node, first <br>
1359 * element, next left node, second <br> element... and right-most node.
1360 *
1361 * @param aMostAncestorToBeSplit Most-ancestor element which should
1362 * be split.
1363 * @param aOutArrayOfNodes First left node, first <br> element,
1364 * Second left node, second <br> element,
1365 * ...right-most node. So, all nodes
1366 * in this list should be siblings (may be
1367 * broken the relation by mutation event
1368 * listener though). If first <br> element
1369 * is first leaf node of
1370 * aMostAncestorToBeSplit, starting from
1371 * the first <br> element.
1372 */
1373 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SplitElementsAtEveryBRElement(
1374 nsIContent& aMostAncestorToBeSplit,
1375 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents);
1376
1377 /**
1378 * MaybeSplitElementsAtEveryBRElement() calls SplitElementsAtEveryBRElement()
1379 * for each given node when this needs to do that for aEditSubAction.
1380 * If split a node, it in aArrayOfContents is replaced with split nodes and
1381 * <br> elements.
1382 */
1383 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MaybeSplitElementsAtEveryBRElement(
1384 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
1385 EditSubAction aEditSubAction);
1386
1387 /**
1388 * CollectEditableChildren() collects child nodes of aNode (starting from
1389 * first editable child, but may return non-editable children after it).
1390 *
1391 * @param aNode Parent node of retrieving children.
1392 * @param aOutArrayOfContents [out] This method will inserts found children
1393 * into this array.
1394 * @param aIndexToInsertChildren Starting from this index, found
1395 * children will be inserted to the array.
1396 * @param aCollectListChildren If Yes, will collect children of list
1397 * and list-item elements recursively.
1398 * @param aCollectTableChildren If Yes, will collect children of table
1399 * related elements recursively.
1400 * @param aCollectNonEditableNodes If Yes, will collect found children
1401 * even if they are not editable.
1402 * @return Number of found children.
1403 */
1404 enum class CollectListChildren { No, Yes };
1405 enum class CollectTableChildren { No, Yes };
1406 enum class CollectNonEditableNodes { No, Yes };
1407 size_t CollectChildren(
1408 nsINode& aNode, nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents,
1409 size_t aIndexToInsertChildren, CollectListChildren aCollectListChildren,
1410 CollectTableChildren aCollectTableChildren,
1411 CollectNonEditableNodes aCollectNonEditableNodes) const;
1412
1413 /**
1414 * SplitInlinessAndCollectEditTargetNodes() splits text nodes and inline
1415 * elements around aArrayOfRanges. Then, collects edit target nodes to
1416 * aOutArrayOfNodes. Finally, each edit target nodes is split at every
1417 * <br> element in it.
1418 * FYI: You can use SplitInlinesAndCollectEditTargetNodesInOneHardLine()
1419 * or SplitInlinesAndCollectEditTargetNodesInExtendedSelectionRanges()
1420 * instead if you want to call this with a hard line including
1421 * specific DOM point or extended selection ranges.
1422 */
1423 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1424 SplitInlinesAndCollectEditTargetNodes(
1425 nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
1426 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents,
1427 EditSubAction aEditSubAction,
1428 CollectNonEditableNodes aCollectNonEditableNodes);
1429
1430 /**
1431 * SplitTextNodesAtRangeEnd() splits text nodes if each range end is in
1432 * middle of a text node.
1433 */
1434 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1435 SplitTextNodesAtRangeEnd(nsTArray<RefPtr<nsRange>>& aArrayOfRanges);
1436
1437 /**
1438 * CollectEditTargetNodes() collects edit target nodes in aArrayOfRanges.
1439 * First, this collects all nodes in given ranges, then, modifies the
1440 * result for specific edit sub-actions.
1441 * FYI: You can use CollectEditTargetNodesInExtendedSelectionRanges() instead
1442 * if you want to call this with extended selection ranges.
1443 */
1444 nsresult CollectEditTargetNodes(
1445 nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
1446 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents,
1447 EditSubAction aEditSubAction,
1448 CollectNonEditableNodes aCollectNonEditableNodes);
1449
1450 /**
1451 * GetWhiteSpaceEndPoint() returns point at first or last ASCII whitespace
1452 * or non-breakable space starting from aPoint. I.e., this returns next or
1453 * previous point whether the character is neither ASCII whitespace nor
1454 * non-brekable space.
1455 */
1456 enum class ScanDirection { Backward, Forward };
1457 template <typename PT, typename RT>
1458 static EditorDOMPoint GetWhiteSpaceEndPoint(
1459 const RangeBoundaryBase<PT, RT>& aPoint, ScanDirection aScanDirection);
1460
1461 /**
1462 * GetCurrentHardLineStartPoint() returns start point of hard line
1463 * including aPoint. If the line starts after a `<br>` element, returns
1464 * next sibling of the `<br>` element. If the line is first line of a block,
1465 * returns point of the block.
1466 * NOTE: The result may be point of editing host. I.e., the container may
1467 * be outside of editing host.
1468 */
1469 template <typename PT, typename RT>
1470 EditorDOMPoint GetCurrentHardLineStartPoint(
1471 const RangeBoundaryBase<PT, RT>& aPoint, EditSubAction aEditSubAction);
1472
1473 /**
1474 * GetCurrentHardLineEndPoint() returns end point of hard line including
1475 * aPoint. If the line ends with a `<br>` element, returns the `<br>`
1476 * element unless it's the last node of a block. If the line is last line
1477 * of a block, returns next sibling of the block. Additionally, if the
1478 * line ends with a linefeed in pre-formated text node, returns point of
1479 * the linefeed.
1480 * NOTE: This result may be point of editing host. I.e., the container
1481 * may be outside of editing host.
1482 */
1483 template <typename PT, typename RT>
1484 EditorDOMPoint GetCurrentHardLineEndPoint(
1485 const RangeBoundaryBase<PT, RT>& aPoint);
1486
1487 /**
1488 * CreateRangeIncludingAdjuscentWhiteSpaces() creates an nsRange instance
1489 * which may be expanded from the given range to include adjuscent
1490 * whitespaces. If this fails handling something, returns nullptr.
1491 */
1492 already_AddRefed<nsRange> CreateRangeIncludingAdjuscentWhiteSpaces(
1493 const dom::AbstractRange& aAbstractRange);
1494 template <typename SPT, typename SRT, typename EPT, typename ERT>
1495 already_AddRefed<nsRange> CreateRangeIncludingAdjuscentWhiteSpaces(
1496 const RangeBoundaryBase<SPT, SRT>& aStartRef,
1497 const RangeBoundaryBase<EPT, ERT>& aEndRef);
1498
1499 /**
1500 * GetSelectionRangesExtendedToIncludeAdjuscentWhiteSpaces() collects
1501 * selection ranges with extending to include adjuscent whitespaces
1502 * of each range start and end.
1503 *
1504 * @param aOutArrayOfRanges [out] Always appended same number of ranges
1505 * as Selection::RangeCount(). Must be empty
1506 * when you call this.
1507 */
1508 void GetSelectionRangesExtendedToIncludeAdjuscentWhiteSpaces(
1509 nsTArray<RefPtr<nsRange>>& aOutArrayOfRanges);
1510
1511 /**
1512 * CreateRangeExtendedToHardLineStartAndEnd() creates an nsRange instance
1513 * which may be expanded to start/end of hard line at both edges of the given
1514 * range. If this fails handling something, returns nullptr.
1515 */
1516 already_AddRefed<nsRange> CreateRangeExtendedToHardLineStartAndEnd(
1517 const dom::AbstractRange& aAbstractRange, EditSubAction aEditSubAction);
1518 template <typename SPT, typename SRT, typename EPT, typename ERT>
1519 already_AddRefed<nsRange> CreateRangeExtendedToHardLineStartAndEnd(
1520 const RangeBoundaryBase<SPT, SRT>& aStartRef,
1521 const RangeBoundaryBase<EPT, ERT>& aEndRef, EditSubAction aEditSubAction);
1522
1523 /**
1524 * GetSelectionRangesExtendedToHardLineStartAndEnd() collects selection ranges
1525 * with extending to start/end of hard line from each range start and end.
1526 * XXX This means that same range may be included in the result.
1527 *
1528 * @param aOutArrayOfRanges [out] Always appended same number of ranges
1529 * as Selection::RangeCount(). Must be empty
1530 * when you call this.
1531 */
1532 void GetSelectionRangesExtendedToHardLineStartAndEnd(
1533 nsTArray<RefPtr<nsRange>>& aOutArrayOfRanges,
1534 EditSubAction aEditSubAction);
1535
1536 /**
1537 * SplitInlinesAndCollectEditTargetNodesInExtendedSelectionRanges() calls
1538 * SplitInlinesAndCollectEditTargetNodes() with result of
1539 * GetSelectionRangesExtendedToHardLineStartAndEnd(). See comments for these
1540 * methods for the detail.
1541 */
1542 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
SplitInlinesAndCollectEditTargetNodesInExtendedSelectionRanges(nsTArray<OwningNonNull<nsIContent>> & aOutArrayOfContents,EditSubAction aEditSubAction,CollectNonEditableNodes aCollectNonEditableNodes)1543 SplitInlinesAndCollectEditTargetNodesInExtendedSelectionRanges(
1544 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents,
1545 EditSubAction aEditSubAction,
1546 CollectNonEditableNodes aCollectNonEditableNodes) {
1547 AutoTArray<RefPtr<nsRange>, 4> extendedSelectionRanges;
1548 GetSelectionRangesExtendedToHardLineStartAndEnd(extendedSelectionRanges,
1549 aEditSubAction);
1550 nsresult rv = SplitInlinesAndCollectEditTargetNodes(
1551 extendedSelectionRanges, aOutArrayOfContents, aEditSubAction,
1552 aCollectNonEditableNodes);
1553 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1554 "SplitInlinesAndCollectEditTargetNodes() failed");
1555 return rv;
1556 }
1557
1558 /**
1559 * SplitInlinesAndCollectEditTargetNodesInOneHardLine() just calls
1560 * SplitInlinesAndCollectEditTargetNodes() with result of calling
1561 * CreateRangeExtendedToHardLineStartAndEnd() with aPointInOneHardLine.
1562 * In other words, returns nodes in the hard line including
1563 * `aPointInOneHardLine`. See the comments for these methods for the
1564 * detail.
1565 */
1566 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
SplitInlinesAndCollectEditTargetNodesInOneHardLine(const EditorDOMPoint & aPointInOneHardLine,nsTArray<OwningNonNull<nsIContent>> & aOutArrayOfContents,EditSubAction aEditSubAction,CollectNonEditableNodes aCollectNonEditableNodes)1567 SplitInlinesAndCollectEditTargetNodesInOneHardLine(
1568 const EditorDOMPoint& aPointInOneHardLine,
1569 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents,
1570 EditSubAction aEditSubAction,
1571 CollectNonEditableNodes aCollectNonEditableNodes) {
1572 if (NS_WARN_IF(!aPointInOneHardLine.IsSet())) {
1573 return NS_ERROR_INVALID_ARG;
1574 }
1575 RefPtr<nsRange> oneLineRange = CreateRangeExtendedToHardLineStartAndEnd(
1576 aPointInOneHardLine.ToRawRangeBoundary(),
1577 aPointInOneHardLine.ToRawRangeBoundary(), aEditSubAction);
1578 if (!oneLineRange) {
1579 // XXX It seems odd to create collapsed range for one line range...
1580 ErrorResult error;
1581 oneLineRange =
1582 nsRange::Create(aPointInOneHardLine.ToRawRangeBoundary(),
1583 aPointInOneHardLine.ToRawRangeBoundary(), error);
1584 if (NS_WARN_IF(error.Failed())) {
1585 return error.StealNSResult();
1586 }
1587 }
1588 AutoTArray<RefPtr<nsRange>, 1> arrayOfLineRanges;
1589 arrayOfLineRanges.AppendElement(oneLineRange);
1590 nsresult rv = SplitInlinesAndCollectEditTargetNodes(
1591 arrayOfLineRanges, aOutArrayOfContents, aEditSubAction,
1592 aCollectNonEditableNodes);
1593 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1594 "SplitInlinesAndCollectEditTargetNodes() failed");
1595 return rv;
1596 }
1597
1598 /**
1599 * CollectEditTargetNodesInExtendedSelectionRanges() calls
1600 * CollectEditTargetNodes() with result of
1601 * GetSelectionRangesExtendedToHardLineStartAndEnd(). See comments for these
1602 * methods for the detail.
1603 */
CollectEditTargetNodesInExtendedSelectionRanges(nsTArray<OwningNonNull<nsIContent>> & aOutArrayOfContents,EditSubAction aEditSubAction,CollectNonEditableNodes aCollectNonEditableNodes)1604 nsresult CollectEditTargetNodesInExtendedSelectionRanges(
1605 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents,
1606 EditSubAction aEditSubAction,
1607 CollectNonEditableNodes aCollectNonEditableNodes) {
1608 AutoTArray<RefPtr<nsRange>, 4> extendedSelectionRanges;
1609 GetSelectionRangesExtendedToHardLineStartAndEnd(extendedSelectionRanges,
1610 aEditSubAction);
1611 nsresult rv =
1612 CollectEditTargetNodes(extendedSelectionRanges, aOutArrayOfContents,
1613 aEditSubAction, aCollectNonEditableNodes);
1614 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "CollectEditTargetNodes() failed");
1615 return rv;
1616 }
1617
1618 /**
1619 * SelectBRElementIfCollapsedInEmptyBlock() helper method for
1620 * CreateRangeIncludingAdjuscentWhiteSpaces() and
1621 * CreateRangeExtendedToLineStartAndEnd(). If the given range is collapsed
1622 * in a block and the block has only one `<br>` element, this makes
1623 * aStartRef and aEndRef select the `<br>` element.
1624 */
1625 template <typename SPT, typename SRT, typename EPT, typename ERT>
1626 void SelectBRElementIfCollapsedInEmptyBlock(
1627 RangeBoundaryBase<SPT, SRT>& aStartRef,
1628 RangeBoundaryBase<EPT, ERT>& aEndRef);
1629
1630 /**
1631 * GetChildNodesOf() returns all child nodes of aParent with an array.
1632 */
GetChildNodesOf(nsINode & aParentNode,nsTArray<OwningNonNull<nsIContent>> & aOutArrayOfContents)1633 static void GetChildNodesOf(
1634 nsINode& aParentNode,
1635 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents) {
1636 MOZ_ASSERT(aOutArrayOfContents.IsEmpty());
1637 aOutArrayOfContents.SetCapacity(aParentNode.GetChildCount());
1638 for (nsIContent* childContent = aParentNode.GetFirstChild(); childContent;
1639 childContent = childContent->GetNextSibling()) {
1640 aOutArrayOfContents.AppendElement(*childContent);
1641 }
1642 }
1643
1644 /**
1645 * GetDeepestEditableOnlyChildDivBlockquoteOrListElement() returns a `<div>`,
1646 * `<blockquote>` or one of list elements. This method climbs down from
1647 * aContent while there is only one editable children and the editable child
1648 * is `<div>`, `<blockquote>` or a list element. When it reaches different
1649 * kind of node, returns the last found element.
1650 */
1651 Element* GetDeepestEditableOnlyChildDivBlockquoteOrListElement(
1652 nsINode& aNode);
1653
1654 /**
1655 * Try to get parent list element at `Selection`. This returns first find
1656 * parent list element of common ancestor of ranges (looking for it from
1657 * first range to last range).
1658 */
1659 Element* GetParentListElementAtSelection() const;
1660
1661 /**
1662 * MaybeExtendSelectionToHardLineEdgesForBlockEditAction() adjust Selection if
1663 * there is only one range. If range start and/or end point is <br> node or
1664 * something non-editable point, they should be moved to nearest text node or
1665 * something where the other methods easier to handle edit action.
1666 */
1667 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1668 MaybeExtendSelectionToHardLineEdgesForBlockEditAction();
1669
1670 /**
1671 * IsEmptyInlineNode() returns true if aContent is an inline node and it does
1672 * not have meaningful content.
1673 */
1674 bool IsEmptyInlineNode(nsIContent& aContent) const;
1675
1676 /**
1677 * IsEmptyOneHardLine() returns true if aArrayOfContents does not represent
1678 * 2 or more lines and have meaningful content.
1679 */
IsEmptyOneHardLine(nsTArray<OwningNonNull<nsIContent>> & aArrayOfContents)1680 bool IsEmptyOneHardLine(
1681 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents) const {
1682 if (NS_WARN_IF(aArrayOfContents.IsEmpty())) {
1683 return true;
1684 }
1685
1686 bool brElementHasFound = false;
1687 for (OwningNonNull<nsIContent>& content : aArrayOfContents) {
1688 if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
1689 continue;
1690 }
1691 if (content->IsHTMLElement(nsGkAtoms::br)) {
1692 // If there are 2 or more `<br>` elements, it's not empty line since
1693 // there may be only one `<br>` element in a hard line.
1694 if (brElementHasFound) {
1695 return false;
1696 }
1697 brElementHasFound = true;
1698 continue;
1699 }
1700 if (!IsEmptyInlineNode(content)) {
1701 return false;
1702 }
1703 }
1704 return true;
1705 }
1706
1707 /**
1708 * MaybeSplitAncestorsForInsertWithTransaction() does nothing if container of
1709 * aStartOfDeepestRightNode can have an element whose tag name is aTag.
1710 * Otherwise, looks for an ancestor node which is or is in active editing
1711 * host and can have an element whose name is aTag. If there is such
1712 * ancestor, its descendants are split.
1713 *
1714 * Note that this may create empty elements while splitting ancestors.
1715 *
1716 * @param aTag The name of element to be inserted
1717 * after calling this method.
1718 * @param aStartOfDeepestRightNode The start point of deepest right node.
1719 * This point must be descendant of
1720 * active editing host.
1721 * @return When succeeded, SplitPoint() returns
1722 * the point to insert the element.
1723 */
1724 [[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
1725 MaybeSplitAncestorsForInsertWithTransaction(
1726 nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode);
1727
1728 /**
1729 * SplitRangeOffFromBlock() splits aBlockElement at two points, before
1730 * aStartOfMiddleElement and after aEndOfMiddleElement. If they are very
1731 * start or very end of aBlcok, this won't create empty block.
1732 *
1733 * @param aBlockElement A block element which will be split.
1734 * @param aStartOfMiddleElement Start node of middle block element.
1735 * @param aEndOfMiddleElement End node of middle block element.
1736 */
1737 [[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffFromNodeResult
1738 SplitRangeOffFromBlock(Element& aBlockElement,
1739 nsIContent& aStartOfMiddleElement,
1740 nsIContent& aEndOfMiddleElement);
1741
1742 /**
1743 * SplitRangeOffFromBlockAndRemoveMiddleContainer() splits the nodes
1744 * between aStartOfRange and aEndOfRange, then, removes the middle element
1745 * and moves its content to where the middle element was.
1746 *
1747 * @param aBlockElement The node which will be split.
1748 * @param aStartOfRange The first node which will be unwrapped
1749 * from aBlockElement.
1750 * @param aEndOfRange The last node which will be unwrapped from
1751 * aBlockElement.
1752 * @return The left content is new created left
1753 * element of aBlockElement.
1754 * The right content is split element,
1755 * i.e., must be aBlockElement.
1756 * The middle content is nullptr since
1757 * removing it is the job of this method.
1758 */
1759 [[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffFromNodeResult
1760 SplitRangeOffFromBlockAndRemoveMiddleContainer(Element& aBlockElement,
1761 nsIContent& aStartOfRange,
1762 nsIContent& aEndOfRange);
1763
1764 /**
1765 * MoveNodesIntoNewBlockquoteElement() inserts at least one <blockquote>
1766 * element and moves nodes in aArrayOfContents into new <blockquote>
1767 * elements.
1768 * If aArrayOfContents includes a table related element except <table>,
1769 * this calls itself recursively to insert <blockquote> into the cell.
1770 *
1771 * @param aArrayOfContents Nodes which will be moved into created
1772 * <blockquote> elements.
1773 */
1774 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MoveNodesIntoNewBlockquoteElement(
1775 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents);
1776
1777 /**
1778 * RemoveBlockContainerElements() removes all format blocks, table related
1779 * element, etc in aArrayOfContents from the DOM tree.
1780 * If aArrayOfContents has a format node, it will be removed and its contents
1781 * will be moved to where it was.
1782 * If aArrayOfContents has a table related element, <li>, <blockquote> or
1783 * <div>, it will be removed and its contents will be moved to where it was.
1784 */
1785 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveBlockContainerElements(
1786 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents);
1787
1788 /**
1789 * CreateOrChangeBlockContainerElement() formats all nodes in aArrayOfContents
1790 * with block elements whose name is aBlockTag.
1791 * If aArrayOfContents has an inline element, a block element is created and
1792 * the inline element and following inline elements are moved into the new
1793 * block element.
1794 * If aArrayOfContents has <br> elements, they'll be removed from the DOM
1795 * tree and new block element will be created when there are some remaining
1796 * inline elements.
1797 * If aArrayOfContents has a block element, this calls itself with children
1798 * of the block element. Then, new block element will be created when there
1799 * are some remaining inline elements.
1800 *
1801 * @param aArrayOfContents Must be descendants of a node.
1802 * @param aBlockTag The element name of new block elements.
1803 */
1804 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult CreateOrChangeBlockContainerElement(
1805 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, nsAtom& aBlockTag);
1806
1807 /**
1808 * FormatBlockContainerWithTransaction() is implementation of "formatBlock"
1809 * command of `Document.execCommand()`. This applies block style or removes
1810 * it.
1811 * NOTE: This creates AutoSelectionRestorer. Therefore, even when this
1812 * return NS_OK, editor may have been destroyed.
1813 *
1814 * @param aBlockType New block tag name.
1815 * If nsGkAtoms::normal or nsGkAtoms::_empty,
1816 * RemoveBlockContainerElements() will be called.
1817 * If nsGkAtoms::blockquote,
1818 * MoveNodesIntoNewBlockquoteElement() will be
1819 * called. Otherwise,
1820 * CreateOrChangeBlockContainerElement() will be
1821 * called.
1822 */
1823 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1824 FormatBlockContainerWithTransaction(nsAtom& aBlockType);
1825
1826 /**
1827 * InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() determines if
1828 * aPointToInsert is start of a hard line and end of the line (i.e, the
1829 * line is empty) and the line ends with block boundary, inserts a `<br>`
1830 * element.
1831 */
1832 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1833 InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary(
1834 const EditorDOMPoint& aPointToInsert);
1835
1836 /**
1837 * Insert a `<br>` element if aElement is a block element and empty.
1838 */
1839 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1840 InsertBRElementIfEmptyBlockElement(Element& aElement);
1841
1842 /**
1843 * Insert padding `<br>` element for empty last line into aElement if
1844 * aElement is a block element and empty.
1845 */
1846 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1847 InsertPaddingBRElementForEmptyLastLineIfNeeded(Element& aElement);
1848
1849 /**
1850 * This method inserts a padding `<br>` element for empty last line if
1851 * selection is collapsed and container of the range needs it.
1852 */
1853 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1854 MaybeInsertPaddingBRElementForEmptyLastLineAtSelection();
1855
1856 /**
1857 * IsEmptyBlockElement() returns true if aElement is a block level element
1858 * and it doesn't have any visible content.
1859 */
1860 enum class IgnoreSingleBR { Yes, No };
1861 bool IsEmptyBlockElement(Element& aElement,
1862 IgnoreSingleBR aIgnoreSingleBR) const;
1863
1864 /**
1865 * SplitParagraph() splits the parent block, aParentDivOrP, at
1866 * aStartOfRightNode.
1867 *
1868 * @param aParentDivOrP The parent block to be split. This must be <p>
1869 * or <div> element.
1870 * @param aStartOfRightNode The point to be start of right node after
1871 * split. This must be descendant of
1872 * aParentDivOrP.
1873 * @param aNextBRNode Next <br> node if there is. Otherwise, nullptr.
1874 * If this is not nullptr, the <br> node may be
1875 * removed.
1876 */
1877 template <typename PT, typename CT>
1878 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SplitParagraph(
1879 Element& aParentDivOrP,
1880 const EditorDOMPointBase<PT, CT>& aStartOfRightNode, nsIContent* aBRNode);
1881
1882 /**
1883 * HandleInsertParagraphInParagraph() does the right thing for Enter key
1884 * press or 'insertParagraph' command in aParentDivOrP. aParentDivOrP will
1885 * be split at start of first selection range.
1886 *
1887 * @param aParentDivOrP The parent block. This must be <p> or <div>
1888 * element.
1889 * @return Returns with NS_OK if this doesn't meat any
1890 * unexpected situation. If this method tries to
1891 * split the paragraph, marked as handled.
1892 */
1893 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
1894 HandleInsertParagraphInParagraph(Element& aParentDivOrP);
1895
1896 /**
1897 * HandleInsertParagraphInHeadingElement() handles insertParagraph command
1898 * (i.e., handling Enter key press) in a heading element. This splits
1899 * aHeader element at aOffset in aNode. Then, if right heading element is
1900 * empty, it'll be removed and new paragraph is created (its type is decided
1901 * with default paragraph separator).
1902 *
1903 * @param aHeader The heading element to be split.
1904 * @param aNode Typically, Selection start container,
1905 * where to be split.
1906 * @param aOffset Typically, Selection start offset in the
1907 * start container, where to be split.
1908 */
1909 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1910 HandleInsertParagraphInHeadingElement(Element& aHeader, nsINode& aNode,
1911 int32_t aOffset);
1912
1913 /**
1914 * HandleInsertParagraphInListItemElement() handles insertParagraph command
1915 * (i.e., handling Enter key press) in a list item element.
1916 *
1917 * @param aListItem The list item which has the following point.
1918 * @param aNode Typically, Selection start container, where to
1919 * insert a break.
1920 * @param aOffset Typically, Selection start offset in the
1921 * start container, where to insert a break.
1922 */
1923 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1924 HandleInsertParagraphInListItemElement(Element& aListItem, nsINode& aNode,
1925 int32_t aOffset);
1926
1927 /**
1928 * GetNearestAncestorListItemElement() returns a list item element if
1929 * aContent or its ancestor in editing host is one. However, this won't
1930 * cross table related element.
1931 */
1932 Element* GetNearestAncestorListItemElement(nsIContent& aContent) const;
1933
1934 /**
1935 * InsertParagraphSeparatorAsSubAction() handles insertPargraph commad
1936 * (i.e., handling Enter key press) with the above helper methods.
1937 */
1938 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
1939 InsertParagraphSeparatorAsSubAction();
1940
1941 /**
1942 * Returns true if aNode1 or aNode2 or both is the descendant of some type of
1943 * table element, but their nearest table element ancestors differ. "Table
1944 * element" here includes not just <table> but also <td>, <tbody>, <tr>, etc.
1945 * The nodes count as being their own descendants for this purpose, so a
1946 * table element is its own nearest table element ancestor.
1947 */
1948 static bool NodesInDifferentTableElements(nsINode& aNode1, nsINode& aNode2);
1949
1950 /**
1951 * ChangeListElementType() replaces child list items of aListElement with
1952 * new list item element whose tag name is aNewListItemTag.
1953 * Note that if there are other list elements as children of aListElement,
1954 * this calls itself recursively even though it's invalid structure.
1955 *
1956 * @param aListElement The list element whose list items will be
1957 * replaced.
1958 * @param aNewListTag New list tag name.
1959 * @param aNewListItemTag New list item tag name.
1960 * @return New list element or an error code if it fails.
1961 * New list element may be aListElement if its
1962 * tag name is same as aNewListTag.
1963 */
1964 [[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateElementResult ChangeListElementType(
1965 Element& aListElement, nsAtom& aListType, nsAtom& aItemType);
1966
1967 /**
1968 * ChangeSelectedHardLinesToList() converts selected ranges to specified
1969 * list element. If there is different type of list elements, this method
1970 * converts them to specified list items too. Basically, each hard line
1971 * will be wrapped with a list item element. However, only when `<p>`
1972 * element is selected, its child `<br>` elements won't be treated as
1973 * hard line separators. Perhaps, this is a bug.
1974 * NOTE: This method creates AutoSelectionRestorer. Therefore, each caller
1975 * need to check if the editor is still available even if this returns
1976 * NS_OK.
1977 *
1978 * @param aListElementTagName The new list element tag name.
1979 * @param aListItemElementTagName The new list item element tag name.
1980 * @param aBulletType If this is not empty string, it's set
1981 * to `type` attribute of new list item
1982 * elements. Otherwise, existing `type`
1983 * attributes will be removed.
1984 * @param aSelectAllOfCurrentList Yes if this should treat all of
1985 * ancestor list element at selection.
1986 */
1987 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
1988 ChangeSelectedHardLinesToList(nsAtom& aListElementTagName,
1989 nsAtom& aListItemElementTagName,
1990 const nsAString& aBulletType,
1991 SelectAllOfCurrentList aSelectAllOfCurrentList);
1992
1993 /**
1994 * MakeOrChangeListAndListItemAsSubAction() handles create list commands with
1995 * current selection. If
1996 *
1997 * @param aListElementOrListItemElementTagName
1998 * The new list element tag name or
1999 * new list item tag name.
2000 * If the former, list item tag name will
2001 * be computed automatically. Otherwise,
2002 * list tag name will be computed.
2003 * @param aBulletType If this is not empty string, it's set
2004 * to `type` attribute of new list item
2005 * elements. Otherwise, existing `type`
2006 * attributes will be removed.
2007 * @param aSelectAllOfCurrentList Yes if this should treat all of
2008 * ancestor list element at selection.
2009 */
2010 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2011 MakeOrChangeListAndListItemAsSubAction(
2012 nsAtom& aListElementOrListItemElementTagName,
2013 const nsAString& aBulletType,
2014 SelectAllOfCurrentList aSelectAllOfCurrentList);
2015
2016 /**
2017 * If aContent is a text node that contains only collapsed whitespace or empty
2018 * and editable.
2019 */
2020 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2021 DeleteNodeIfInvisibleAndEditableTextNode(nsIContent& aContent);
2022
2023 /**
2024 * DeleteTextAndTextNodesWithTransaction() removes text nodes which are in
2025 * the given range and delete some characters in start and/or end of
2026 * the range.
2027 */
2028 template <typename EditorDOMPointType>
2029 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2030 DeleteTextAndTextNodesWithTransaction(const EditorDOMPointType& aStartPoint,
2031 const EditorDOMPointType& aEndPoint);
2032
2033 /**
2034 * If aPoint follows invisible `<br>` element, returns the invisible `<br>`
2035 * element. Otherwise, nullptr.
2036 */
2037 template <typename PT, typename CT>
2038 Element* GetInvisibleBRElementAt(const EditorDOMPointBase<PT, CT>& aPoint);
2039
2040 /**
2041 * JoinNodesWithTransaction() joins aLeftNode and aRightNode. Content of
2042 * aLeftNode will be merged into aRightNode. Actual implemenation of this
2043 * method is JoinNodesImpl(). So, see its explanation for the detail.
2044 *
2045 * @param aLeftNode Will be removed from the DOM tree.
2046 * @param aRightNode The node which will be new container of the content of
2047 * aLeftNode.
2048 */
2049 MOZ_CAN_RUN_SCRIPT nsresult JoinNodesWithTransaction(nsINode& aLeftNode,
2050 nsINode& aRightNode);
2051
2052 /**
2053 * JoinNearestEditableNodesWithTransaction() joins two editable nodes which
2054 * are themselves or the nearest editable node of aLeftNode and aRightNode.
2055 * XXX This method's behavior is odd. For example, if user types Backspace
2056 * key at the second editable paragraph in this case:
2057 * <div contenteditable>
2058 * <p>first editable paragraph</p>
2059 * <p contenteditable="false">non-editable paragraph</p>
2060 * <p>second editable paragraph</p>
2061 * </div>
2062 * The first editable paragraph's content will be moved into the second
2063 * editable paragraph and the non-editable paragraph becomes the first
2064 * paragraph of the editor. I don't think that it's expected behavior of
2065 * any users...
2066 *
2067 * @param aLeftNode The node which will be removed.
2068 * @param aRightNode The node which will be inserted the content of
2069 * aLeftNode.
2070 * @param aNewFirstChildOfRightNode
2071 * [out] The point at the first child of aRightNode.
2072 */
2073 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2074 JoinNearestEditableNodesWithTransaction(
2075 nsIContent& aLeftNode, nsIContent& aRightNode,
2076 EditorDOMPoint* aNewFirstChildOfRightNode);
2077
2078 /**
2079 * ReplaceContainerAndCloneAttributesWithTransaction() creates new element
2080 * whose name is aTagName, copies all attributes from aOldContainer to the
2081 * new element, moves all children in aOldContainer to the new element, then,
2082 * removes aOldContainer from the DOM tree.
2083 *
2084 * @param aOldContainer The element node which should be replaced
2085 * with new element.
2086 * @param aTagName The name of new element node.
2087 */
2088 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element>
ReplaceContainerAndCloneAttributesWithTransaction(Element & aOldContainer,nsAtom & aTagName)2089 ReplaceContainerAndCloneAttributesWithTransaction(Element& aOldContainer,
2090 nsAtom& aTagName) {
2091 return ReplaceContainerWithTransactionInternal(
2092 aOldContainer, aTagName, *nsGkAtoms::_empty, EmptyString(), true);
2093 }
2094
2095 /**
2096 * ReplaceContainerWithTransaction() creates new element whose name is
2097 * aTagName, sets aAttributes of the new element to aAttributeValue, moves
2098 * all children in aOldContainer to the new element, then, removes
2099 * aOldContainer from the DOM tree.
2100 *
2101 * @param aOldContainer The element node which should be replaced
2102 * with new element.
2103 * @param aTagName The name of new element node.
2104 * @param aAttribute Attribute name to be set to the new element.
2105 * @param aAttributeValue Attribute value to be set to aAttribute.
2106 */
ReplaceContainerWithTransaction(Element & aOldContainer,nsAtom & aTagName,nsAtom & aAttribute,const nsAString & aAttributeValue)2107 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> ReplaceContainerWithTransaction(
2108 Element& aOldContainer, nsAtom& aTagName, nsAtom& aAttribute,
2109 const nsAString& aAttributeValue) {
2110 return ReplaceContainerWithTransactionInternal(
2111 aOldContainer, aTagName, aAttribute, aAttributeValue, false);
2112 }
2113
2114 /**
2115 * ReplaceContainerWithTransaction() creates new element whose name is
2116 * aTagName, moves all children in aOldContainer to the new element, then,
2117 * removes aOldContainer from the DOM tree.
2118 *
2119 * @param aOldContainer The element node which should be replaced
2120 * with new element.
2121 * @param aTagName The name of new element node.
2122 */
ReplaceContainerWithTransaction(Element & aOldContainer,nsAtom & aTagName)2123 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> ReplaceContainerWithTransaction(
2124 Element& aOldContainer, nsAtom& aTagName) {
2125 return ReplaceContainerWithTransactionInternal(
2126 aOldContainer, aTagName, *nsGkAtoms::_empty, EmptyString(), false);
2127 }
2128
2129 /**
2130 * RemoveContainerWithTransaction() removes aElement from the DOM tree and
2131 * moves all its children to the parent of aElement.
2132 *
2133 * @param aElement The element to be removed.
2134 */
2135 MOZ_CAN_RUN_SCRIPT nsresult RemoveContainerWithTransaction(Element& aElement);
2136
2137 /**
2138 * InsertContainerWithTransaction() creates new element whose name is
2139 * aTagName, moves aContent into the new element, then, inserts the new
2140 * element into where aContent was.
2141 * Note that this method does not check if aContent is valid child of
2142 * the new element. So, callers need to guarantee it.
2143 *
2144 * @param aContent The content which will be wrapped with new
2145 * element.
2146 * @param aTagName Element name of new element which will wrap
2147 * aContent and be inserted into where aContent
2148 * was.
2149 * @return The new element.
2150 */
InsertContainerWithTransaction(nsIContent & aContent,nsAtom & aTagName)2151 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertContainerWithTransaction(
2152 nsIContent& aContent, nsAtom& aTagName) {
2153 return InsertContainerWithTransactionInternal(
2154 aContent, aTagName, *nsGkAtoms::_empty, EmptyString());
2155 }
2156
2157 /**
2158 * InsertContainerWithTransaction() creates new element whose name is
2159 * aTagName, sets its aAttribute to aAttributeValue, moves aContent into the
2160 * new element, then, inserts the new element into where aContent was.
2161 * Note that this method does not check if aContent is valid child of
2162 * the new element. So, callers need to guarantee it.
2163 *
2164 * @param aContent The content which will be wrapped with new
2165 * element.
2166 * @param aTagName Element name of new element which will wrap
2167 * aContent and be inserted into where aContent
2168 * was.
2169 * @param aAttribute Attribute which should be set to the new
2170 * element.
2171 * @param aAttributeValue Value to be set to aAttribute.
2172 * @return The new element.
2173 */
InsertContainerWithTransaction(nsIContent & aContent,nsAtom & aTagName,nsAtom & aAttribute,const nsAString & aAttributeValue)2174 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertContainerWithTransaction(
2175 nsIContent& aContent, nsAtom& aTagName, nsAtom& aAttribute,
2176 const nsAString& aAttributeValue) {
2177 return InsertContainerWithTransactionInternal(aContent, aTagName,
2178 aAttribute, aAttributeValue);
2179 }
2180
2181 /**
2182 * MoveNodeWithTransaction() moves aContent to aPointToInsert.
2183 *
2184 * @param aContent The node to be moved.
2185 */
2186 MOZ_CAN_RUN_SCRIPT nsresult MoveNodeWithTransaction(
2187 nsIContent& aContent, const EditorDOMPoint& aPointToInsert);
2188
2189 /**
2190 * MoveNodeToEndWithTransaction() moves aContent to end of aNewContainer.
2191 *
2192 * @param aContent The node to be moved.
2193 * @param aNewContainer The new container which will contain aContent as
2194 * its last child.
2195 */
2196 MOZ_CAN_RUN_SCRIPT nsresult
MoveNodeToEndWithTransaction(nsIContent & aContent,nsINode & aNewContainer)2197 MoveNodeToEndWithTransaction(nsIContent& aContent, nsINode& aNewContainer) {
2198 EditorDOMPoint pointToInsert;
2199 pointToInsert.SetToEndOf(&aNewContainer);
2200 return MoveNodeWithTransaction(aContent, pointToInsert);
2201 }
2202
2203 /**
2204 * MoveNodeOrChildrenWithTransaction() moves aContent to aPointToInsert. If
2205 * cannot insert aContent due to invalid relation, moves only its children
2206 * recursively and removes aContent from the DOM tree.
2207 *
2208 * @param aContent Content which should be moved.
2209 * @param aPointToInsert The point to be inserted aContent or its
2210 * descendants.
2211 */
2212 [[nodiscard]] MOZ_CAN_RUN_SCRIPT MoveNodeResult
2213 MoveNodeOrChildrenWithTransaction(nsIContent& aNode,
2214 const EditorDOMPoint& aPointToInsert);
2215
2216 /**
2217 * MoveChildrenWithTransaction() moves the children of aElement to
2218 * aPointToInsert. If cannot insert some children due to invalid relation,
2219 * calls MoveNodeOrChildrenWithTransaction() to remove the children but keep
2220 * moving its children.
2221 *
2222 * @param aElement Container element whose children should be
2223 * moved.
2224 * @param aPointToInsert The point to be inserted children of aElement
2225 * or its descendants.
2226 */
2227 [[nodiscard]] MOZ_CAN_RUN_SCRIPT MoveNodeResult MoveChildrenWithTransaction(
2228 Element& aElement, const EditorDOMPoint& aPointToInsert);
2229
2230 /**
2231 * MoveAllChildren() moves all children of aContainer to before
2232 * aPointToInsert.GetChild().
2233 * See explanation of MoveChildrenBetween() for the detail of the behavior.
2234 *
2235 * @param aContainer The container node whose all children should
2236 * be moved.
2237 * @param aPointToInsert The insertion point. The container must not
2238 * be a data node like a text node.
2239 * @param aError The result. If this succeeds to move children,
2240 * returns NS_OK. Otherwise, an error.
2241 */
2242 void MoveAllChildren(nsINode& aContainer,
2243 const EditorRawDOMPoint& aPointToInsert,
2244 ErrorResult& aError);
2245
2246 /**
2247 * MoveChildrenBetween() moves all children between aFirstChild and aLastChild
2248 * to before aPointToInsert.GetChild(). If some children are moved to
2249 * different container while this method moves other children, they are just
2250 * ignored. If the child node referred by aPointToInsert is moved to different
2251 * container while this method moves children, returns error.
2252 *
2253 * @param aFirstChild The first child which should be moved to
2254 * aPointToInsert.
2255 * @param aLastChild The last child which should be moved. This
2256 * must be a sibling of aFirstChild and it should
2257 * be positioned after aFirstChild in the DOM tree
2258 * order.
2259 * @param aPointToInsert The insertion point. The container must not
2260 * be a data node like a text node.
2261 * @param aError The result. If this succeeds to move children,
2262 * returns NS_OK. Otherwise, an error.
2263 */
2264 void MoveChildrenBetween(nsIContent& aFirstChild, nsIContent& aLastChild,
2265 const EditorRawDOMPoint& aPointToInsert,
2266 ErrorResult& aError);
2267
2268 /**
2269 * MovePreviousSiblings() moves all siblings before aChild (i.e., aChild
2270 * won't be moved) to before aPointToInsert.GetChild().
2271 * See explanation of MoveChildrenBetween() for the detail of the behavior.
2272 *
2273 * @param aChild The node which is next sibling of the last
2274 * node to be moved.
2275 * @param aPointToInsert The insertion point. The container must not
2276 * be a data node like a text node.
2277 * @param aError The result. If this succeeds to move children,
2278 * returns NS_OK. Otherwise, an error.
2279 */
2280 void MovePreviousSiblings(nsIContent& aChild,
2281 const EditorRawDOMPoint& aPointToInsert,
2282 ErrorResult& aError);
2283
2284 /**
2285 * MoveOneHardLineContents() moves the content in a hard line which contains
2286 * aPointInHardLine to aPointToInsert or end of aPointToInsert's container.
2287 *
2288 * @param aPointInHardLine A point in a hard line. All nodes in
2289 * same hard line will be moved.
2290 * @param aPointToInsert Point to insert contents of the hard
2291 * line.
2292 * @param aMoveToEndOfContainer If `Yes`, aPointToInsert.Offset() will
2293 * be ignored and instead, all contents
2294 * will be appended to the container of
2295 * aPointToInsert. The result may be
2296 * different from setting this to `No`
2297 * and aPointToInsert points end of the
2298 * container because mutation event
2299 * listeners may modify children of the
2300 * container while we're moving nodes.
2301 */
2302 enum class MoveToEndOfContainer { Yes, No };
2303 [[nodiscard]] MOZ_CAN_RUN_SCRIPT MoveNodeResult MoveOneHardLineContents(
2304 const EditorDOMPoint& aPointInHardLine,
2305 const EditorDOMPoint& aPointToInsert,
2306 MoveToEndOfContainer aMoveToEndOfContainer = MoveToEndOfContainer::No);
2307
2308 /**
2309 * SplitNodeWithTransaction() creates a transaction to create a new node
2310 * (left node) identical to an existing node (right node), and split the
2311 * contents between the same point in both nodes, then, execute the
2312 * transaction.
2313 *
2314 * @param aStartOfRightNode The point to split. Its container will be
2315 * the right node, i.e., become the new node's
2316 * next sibling. And the point will be start
2317 * of the right node.
2318 * @param aError If succeed, returns no error. Otherwise, an
2319 * error.
2320 */
2321 MOZ_CAN_RUN_SCRIPT already_AddRefed<nsIContent> SplitNodeWithTransaction(
2322 const EditorDOMPoint& aStartOfRightNode, ErrorResult& aResult);
2323
2324 enum class SplitAtEdges {
2325 // SplitNodeDeepWithTransaction() won't split container element
2326 // nodes at their edges. I.e., when split point is start or end of
2327 // container, it won't be split.
2328 eDoNotCreateEmptyContainer,
2329 // SplitNodeDeepWithTransaction() always splits containers even
2330 // if the split point is at edge of a container. E.g., if split point is
2331 // start of an inline element, empty inline element is created as a new left
2332 // node.
2333 eAllowToCreateEmptyContainer,
2334 };
2335
2336 /**
2337 * SplitNodeDeepWithTransaction() splits aMostAncestorToSplit deeply.
2338 *
2339 * @param aMostAncestorToSplit The most ancestor node which should be
2340 * split.
2341 * @param aStartOfDeepestRightNode The start point of deepest right node.
2342 * This point must be descendant of
2343 * aMostAncestorToSplit.
2344 * @param aSplitAtEdges Whether the caller allows this to
2345 * create empty container element when
2346 * split point is start or end of an
2347 * element.
2348 * @return SplitPoint() returns split point in
2349 * aMostAncestorToSplit. The point must
2350 * be good to insert something if the
2351 * caller want to do it.
2352 */
2353 MOZ_CAN_RUN_SCRIPT SplitNodeResult
2354 SplitNodeDeepWithTransaction(nsIContent& aMostAncestorToSplit,
2355 const EditorDOMPoint& aDeepestStartOfRightNode,
2356 SplitAtEdges aSplitAtEdges);
2357
2358 /**
2359 * JoinNodesDeepWithTransaction() joins aLeftNode and aRightNode "deeply".
2360 * First, they are joined simply, then, new right node is assumed as the
2361 * child at length of the left node before joined and new left node is
2362 * assumed as its previous sibling. Then, they will be joined again.
2363 * And then, these steps are repeated.
2364 *
2365 * @param aLeftContent The node which will be removed form the tree.
2366 * @param aRightContent The node which will be inserted the contents of
2367 * aRightContent.
2368 * @return The point of the first child of the last right node.
2369 * The result is always set if this succeeded.
2370 */
2371 MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
2372 JoinNodesDeepWithTransaction(nsIContent& aLeftContent,
2373 nsIContent& aRightContent);
2374
2375 /**
2376 * TryToJoinBlocksWithTransaction() tries to join two block elements. The
2377 * right element is always joined to the left element. If the elements are
2378 * the same type and not nested within each other,
2379 * JoinEditableNodesWithTransaction() is called (example, joining two list
2380 * items together into one). If the elements are not the same type, or one
2381 * is a descendant of the other, we instead destroy the right block placing
2382 * its children into leftblock.
2383 *
2384 * @return Sets canceled to true if the operation should do
2385 * nothing anymore even if this doesn't join the blocks.
2386 * Sets handled to true if this actually handles the
2387 * request. Note that this may set it to true even if this
2388 * does not join the block. E.g., if the blocks shouldn't
2389 * be joined or it's impossible to join them but it's not
2390 * unexpected case, this returns true with this.
2391 */
2392 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2393 TryToJoinBlocksWithTransaction(nsIContent& aLeftContentInBlock,
2394 nsIContent& aRightContentInBlock);
2395
2396 /**
2397 * GetGoodCaretPointFor() returns a good point to collapse `Selection`
2398 * after handling edit action with aDirectionAndAmount.
2399 *
2400 * @param aContent The content where you want to put caret
2401 * around.
2402 * @param aDirectionAndAmount Muse be one of eNext, eNextWord, eToEndOfLine,
2403 * ePrevious, ePreviousWord and eToBeggingOfLine.
2404 * Set the direction of handled edit action.
2405 */
2406 EditorDOMPoint GetGoodCaretPointFor(
2407 nsIContent& aContent, nsIEditor::EDirection aDirectionAndAmount);
2408
2409 /**
2410 * RemoveEmptyInclusiveAncestorInlineElements() removes empty inclusive
2411 * ancestor inline elements in inclusive ancestor block element of aContent.
2412 *
2413 * @param aContent Must be an empty content.
2414 */
2415 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2416 RemoveEmptyInclusiveAncestorInlineElements(nsIContent& aContent);
2417
2418 /**
2419 * MaybeDeleteTopMostEmptyAncestor() looks for top most empty block ancestor
2420 * of aStartContent in aEditingHostElement.
2421 * If found empty ancestor is a list item element, inserts a <br> element
2422 * before its parent element if grand parent is a list element. Then,
2423 * collapse Selection to after the empty block.
2424 * If found empty ancestor is not a list item element, collapse Selection to
2425 * somewhere depending on aAction.
2426 * Finally, removes the empty block ancestor.
2427 *
2428 * @param aStartContent Start content to look for empty ancestors.
2429 * @param aEditingHostElement Current editing host.
2430 * @param aDirectionAndAmount If found empty ancestor block is a list item
2431 * element, this is ignored. Otherwise:
2432 * - If eNext, eNextWord or eToEndOfLine, collapse
2433 * Selection to after found empty ancestor.
2434 * - If ePrevious, ePreviousWord or
2435 * eToBeginningOfLine, collapse Selection to
2436 * end of previous editable node.
2437 * Otherwise, eNone is allowed but does nothing.
2438 */
2439 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2440 MaybeDeleteTopMostEmptyAncestor(nsIContent& aStartContent,
2441 Element& aEditingHostElement,
2442 nsIEditor::EDirection aDirectionAndAmount);
2443
2444 /**
2445 * GetRangeExtendedToIncludeInvisibleNodes() returns extended range.
2446 * If there are some invisible nodes around aAbstractRange, they may
2447 * be included.
2448 *
2449 * @param aAbstractRange Original range. This must not be collapsed
2450 * and must be positioned.
2451 * @return Extended range.
2452 */
2453 already_AddRefed<dom::StaticRange> GetRangeExtendedToIncludeInvisibleNodes(
2454 const dom::AbstractRange& aAbstractRange);
2455
2456 /**
2457 * HandleDeleteCollapsedSelectionAtWhiteSpaces() handles deletion of
2458 * collapsed selection at whitespaces in a text node.
2459 *
2460 * @param aDirectionAndAmount Direction of the deletion.
2461 * @param aWSRunObjectAtCaret WSRunObject instance which was initialized with
2462 * the caret point.
2463 */
2464 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2465 HandleDeleteCollapsedSelectionAtWhiteSpaces(
2466 nsIEditor::EDirection aDirectionAndAmount,
2467 WSRunObject& aWSRunObjectAtCaret);
2468
2469 /**
2470 * HandleDeleteCollapsedSelectionAtTextNode() handles deletion of
2471 * collapsed selection in a text node.
2472 *
2473 * @param aDirectionAndAmount Direction of the deletion.
2474 * @param aPointToDelete The point in a text node to delete character(s).
2475 * Caller must guarantee that this is in a text
2476 * node.
2477 */
2478 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2479 HandleDeleteCollapsedSelectionAtTextNode(
2480 nsIEditor::EDirection aDirectionAndAmount,
2481 const EditorDOMPoint& aPointToDelete);
2482
2483 /**
2484 * HandleDeleteCollapsedSelectionAtAtomicContent() handles deletion of
2485 * atomic elements like `<br>`, `<hr>`, `<img>`, `<input>`, etc and
2486 * data nodes except text node (e.g., comment node).
2487 * If aAtomicContent is a invisible `<br>` element, this will call
2488 * `WillDeleteSelection()` recursively after deleting it.
2489 *
2490 * @param aDirectionAndAmount Direction of the deletion.
2491 * @param aStripWrappers Must be eStrip or eNoStrip.
2492 * @param aAtomicContent The atomic content to be deleted.
2493 * @param aCaretPoint The caret point (i.e., selection start or
2494 * end).
2495 * @param aWSRunScannerAtCaret WSRunScanner instance which was initialized
2496 * with the caret point.
2497 */
2498 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2499 HandleDeleteCollapsedSelectionAtAtomicContent(
2500 nsIEditor::EDirection aDirectionAndAmount,
2501 nsIEditor::EStripWrappers aStripWrappers, nsIContent& aAtomicContent,
2502 const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret);
2503
2504 /**
2505 * HandleDeleteCollapsedSelectionAtOtherBlockBoundary() handles deletion at
2506 * other block boundary (i.e., immediately before or after a block).
2507 * If this does not join blocks, `WillDeleteSelection()` may be called
2508 * recursively.
2509 *
2510 * @param aDirectionAndAmount Direction of the deletion.
2511 * @param aStripWrappers Must be eStrip or eNoStrip.
2512 * @param aOtherBlockElement The block element which follows the caret or
2513 * is followed by caret.
2514 * @param aCaretPoint The caret point (i.e., selection start or
2515 * end).
2516 * @param aWSRunScannerAtCaret WSRunScanner instance which was initialized
2517 * with the caret point.
2518 */
2519 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2520 HandleDeleteCollapsedSelectionAtOtherBlockBoundary(
2521 nsIEditor::EDirection aDirectionAndAmount,
2522 nsIEditor::EStripWrappers aStripWrappers, Element& aOtherBlockElement,
2523 const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret);
2524
2525 /**
2526 * HandleDeleteCollapsedSelectionAtCurrentBlockBoundary() handles deletion
2527 * at current block boundary (i.e., at start or end of current block).
2528 *
2529 * @param aDirectionAndAmount Direction of the deletion.
2530 * @param aCurrentBlockElement The current block element.
2531 * @param aCaretPoint The caret point (i.e., selection start
2532 * or end).
2533 */
2534 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2535 HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(
2536 nsIEditor::EDirection aDirectionAndAmount, Element& aCurrentBlockElement,
2537 const EditorDOMPoint& aCaretPoint);
2538
2539 /**
2540 * DeleteUnnecessaryNodesAndCollapseSelection() removes unnecessary nodes
2541 * around aSelectionStartPoint and aSelectionEndPoint. Then, collapse
2542 * selection at aSelectionStartPoint or aSelectionEndPoint (depending on
2543 * aDirectionAndAmount).
2544 *
2545 * @param aDirectionAndAmount Direction of the deletion.
2546 * If nsIEditor::ePrevious, selection will
2547 * be collapsed to aSelectionEndPoint.
2548 * Otherwise, selection will be collapsed
2549 * to aSelectionStartPoint.
2550 * @param aSelectionStartPoint First selection range start after
2551 * computing the deleting range.
2552 * @param aSelectionEndPoint First selection range end after
2553 * computing the deleting range.
2554 */
2555 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2556 DeleteUnnecessaryNodesAndCollapseSelection(
2557 nsIEditor::EDirection aDirectionAndAmount,
2558 const EditorDOMPoint& aSelectionStartPoint,
2559 const EditorDOMPoint& aSelectionEndPoint);
2560
2561 /**
2562 * HandleDeleteAroundCollapsedSelection() handles deletion with collapsed
2563 * `Selection`. Callers must guarantee that this is called only when
2564 * `Selection` is collapsed.
2565 *
2566 * @param aDirectionAndAmount Direction of the deletion.
2567 * @param aStripWrappers Must be eStrip or eNoStrip.
2568 */
2569 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2570 HandleDeleteAroundCollapsedSelection(
2571 nsIEditor::EDirection aDirectionAndAmount,
2572 nsIEditor::EStripWrappers aStripWrappers);
2573
2574 /**
2575 * HandleDeleteNonCollapsedSelection() handles deletion with non-collapsed
2576 * `Selection`. Callers must guarantee that this is called only when
2577 * `Selection` is NOT collapsed.
2578 *
2579 * @param aDirectionAndAmount Direction of the deletion.
2580 * @param aStripWrappers Must be eStrip or eNoStrip.
2581 * @param aSelectionWasCollpased If the caller extended `Selection`
2582 * from collapsed, set this to `Yes`.
2583 * Otherwise, i.e., `Selection` is not
2584 * collapsed from the beginning, set
2585 * this to `No`.
2586 */
2587 enum class SelectionWasCollapsed { Yes, No };
2588 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2589 HandleDeleteNonCollapsedSelection(
2590 nsIEditor::EDirection aDirectionAndAmount,
2591 nsIEditor::EStripWrappers aStripWrappers,
2592 SelectionWasCollapsed aSelectionWasCollapsed);
2593
2594 /**
2595 * DeleteElementsExceptTableRelatedElements() removes elements except
2596 * table related elements (except <table> itself) and their contents
2597 * from the DOM tree.
2598 *
2599 * @param aNode If this is not a table related element, this
2600 * node will be removed from the DOM tree.
2601 * Otherwise, this method calls itself recursively
2602 * with its children.
2603 *
2604 */
2605 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2606 DeleteElementsExceptTableRelatedElements(nsINode& aNode);
2607
2608 /**
2609 * HandleDeleteSelectionInternal() is a helper method of
2610 * HandleDeleteSelection(). This can be called recursively by the helper
2611 * methods.
2612 * NOTE: This method creates SelectionBatcher. Therefore, each caller
2613 * needs to check if the editor is still available even if this returns
2614 * NS_OK.
2615 */
2616 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2617 HandleDeleteSelectionInternal(nsIEditor::EDirection aDirectionAndAmount,
2618 nsIEditor::EStripWrappers aStripWrappers);
2619
2620 /**
2621 * This method handles "delete selection" commands.
2622 * NOTE: Don't call this method recursively from the helper methods since
2623 * when nobody handled it without canceling and returing an error,
2624 * this falls it back to `DeleteSelectionWithTransaction()`.
2625 *
2626 * @param aDirectionAndAmount Direction of the deletion.
2627 * @param aStripWrappers Must be eStrip or eNoStrip.
2628 */
2629 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual EditActionResult
2630 HandleDeleteSelection(nsIEditor::EDirection aDirectionAndAmount,
2631 nsIEditor::EStripWrappers aStripWrappers) final;
2632
2633 /**
2634 * DeleteMostAncestorMailCiteElementIfEmpty() deletes most ancestor
2635 * mail cite element (`<blockquote type="cite">` or
2636 * `<span _moz_quote="true">`, the former can be created with middle click
2637 * paste with `Control` or `Command` even in the web) of aContent if it
2638 * becomes empty.
2639 */
2640 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2641 DeleteMostAncestorMailCiteElementIfEmpty(nsIContent& aContent);
2642
2643 /**
2644 * LiftUpListItemElement() moves aListItemElement outside its parent.
2645 * If it's in a middle of a list element, the parent list element is split
2646 * before aListItemElement. Then, moves aListItemElement to before its
2647 * parent list element. I.e., moves aListItemElement between the 2 list
2648 * elements if original parent was split. Then, if new parent becomes not a
2649 * list element, the list item element is removed and its contents are moved
2650 * to where the list item element was. If aListItemElement becomse not a
2651 * child of list element, its contents are unwrapped from aListItemElement.
2652 *
2653 * @param aListItemElement Must be a <li>, <dt> or <dd> element.
2654 * @param aLiftUpFromAllParentListElements
2655 * If Yes, this method calls itself recursively
2656 * to unwrap the contents in aListItemElement
2657 * from any ancestor list elements.
2658 * XXX This checks only direct parent of list
2659 * elements. Therefore, if a parent list
2660 * element in a list item element, the
2661 * list item element and its list element
2662 * won't be unwrapped.
2663 */
2664 enum class LiftUpFromAllParentListElements { Yes, No };
2665 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult LiftUpListItemElement(
2666 dom::Element& aListItemElement,
2667 LiftUpFromAllParentListElements aLiftUpFromAllParentListElements);
2668
2669 /**
2670 * DestroyListStructureRecursively() destroys the list structure of
2671 * aListElement recursively.
2672 * If aListElement has <li>, <dl> or <dt> as a child, the element is removed
2673 * but its descendants are moved to where the list item element was.
2674 * If aListElement has another <ul>, <ol> or <dl> as a child, this method is
2675 * called recursively.
2676 * If aListElement has other nodes as its child, they are just removed.
2677 * Finally, aListElement is removed. and its all children are moved to
2678 * where the aListElement was.
2679 *
2680 * @param aListElement A <ul>, <ol> or <dl> element.
2681 */
2682 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2683 DestroyListStructureRecursively(Element& aListElement);
2684
2685 /**
2686 * RemoveListAtSelectionAsSubAction() removes list elements and list item
2687 * elements at Selection. And move contents in them where the removed list
2688 * was.
2689 */
2690 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveListAtSelectionAsSubAction();
2691
2692 /**
2693 * ChangeMarginStart() changes margin of aElement to indent or outdent.
2694 * If it's rtl text, margin-right will be changed. Otherwise, margin-left.
2695 * XXX This is not aware of vertical writing-mode.
2696 */
2697 enum class ChangeMargin { Increase, Decrease };
2698 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2699 ChangeMarginStart(Element& aElement, ChangeMargin aChangeMargin);
2700
2701 /**
2702 * HandleCSSIndentAtSelectionInternal() indents around Selection with CSS.
2703 * This method creates AutoSelectionRestorer. Therefore, each caller
2704 * need to check if the editor is still available even if this returns
2705 * NS_OK.
2706 * NOTE: Use HandleCSSIndentAtSelection() instead.
2707 */
2708 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2709 HandleCSSIndentAtSelectionInternal();
2710
2711 /**
2712 * HandleHTMLIndentAtSelectionInternal() indents around Selection with HTML.
2713 * This method creates AutoSelectionRestorer. Therefore, each caller
2714 * need to check if the editor is still available even if this returns
2715 * NS_OK.
2716 * NOTE: Use HandleHTMLIndentAtSelection() instead.
2717 */
2718 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2719 HandleHTMLIndentAtSelectionInternal();
2720
2721 /**
2722 * HandleCSSIndentAtSelection() indents around Selection with CSS.
2723 * NOTE: This is a helper method of `HandleIndentAtSelection()`. If you
2724 * want to call this directly, you should check whether you need
2725 * do do something which `HandleIndentAtSelection()` does.
2726 */
2727 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleCSSIndentAtSelection();
2728
2729 /**
2730 * HandleHTMLIndentAtSelection() indents around Selection with HTML.
2731 * NOTE: This is a helper method of `HandleIndentAtSelection()`. If you
2732 * want to call this directly, you should check whether you need
2733 * do do something which `HandleIndentAtSelection()` does.
2734 */
2735 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleHTMLIndentAtSelection();
2736
2737 /**
2738 * HandleIndentAtSelection() indents around Selection with HTML or CSS.
2739 */
2740 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult HandleIndentAtSelection();
2741
2742 /**
2743 * OutdentPartOfBlock() outdents the nodes between aStartOfOutdent and
2744 * aEndOfOutdent. This splits the range off from aBlockElement first.
2745 * Then, removes the middle element if aIsBlockIndentedWithCSS is false.
2746 * Otherwise, decreases the margin of the middle element.
2747 *
2748 * @param aBlockElement A block element which includes both
2749 * aStartOfOutdent and aEndOfOutdent.
2750 * @param aStartOfOutdent First node which is descendant of
2751 * aBlockElement will be outdented.
2752 * @param aEndOfOutdent Last node which is descandant of
2753 * aBlockElement will be outdented.
2754 * @param aBlockIndentedWith `CSS` if aBlockElement is indented with
2755 * CSS margin property.
2756 * `HTML` if aBlockElement is `<blockquote>`
2757 * or something.
2758 * @return The left content is new created element
2759 * splitting before aStartOfOutdent.
2760 * The right content is existing element.
2761 * The middle content is outdented element
2762 * if aBlockIndentedWith is `CSS`.
2763 * Otherwise, nullptr.
2764 */
2765 enum class BlockIndentedWith { CSS, HTML };
2766 [[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffFromNodeResult
2767 OutdentPartOfBlock(Element& aBlockElement, nsIContent& aStartOfOutdent,
2768 nsIContent& aEndOutdent,
2769 BlockIndentedWith aBlockIndentedWith);
2770
2771 /**
2772 * HandleOutdentAtSelectionInternal() outdents contents around Selection.
2773 * This method creates AutoSelectionRestorer. Therefore, each caller
2774 * needs to check if the editor is still available even if this returns
2775 * NS_OK.
2776 * NOTE: Call `HandleOutdentAtSelection()` instead.
2777 *
2778 * @return The left content is left content of last
2779 * outdented element.
2780 * The right content is right content of last
2781 * outdented element.
2782 * The middle content is middle content of last
2783 * outdented element.
2784 */
2785 [[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffFromNodeResult
2786 HandleOutdentAtSelectionInternal();
2787
2788 /**
2789 * HandleOutdentAtSelection() outdents contents around Selection.
2790 */
2791 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult HandleOutdentAtSelection();
2792
2793 /**
2794 * AlignBlockContentsWithDivElement() sets align attribute of <div> element
2795 * which is only child of aBlockElement to aAlignType. If aBlockElement
2796 * has 2 or more children or does not have a `<div>` element, inserts a
2797 * new `<div>` element into aBlockElement and move all children of
2798 * aBlockElement into the new `<div>` element.
2799 *
2800 * @param aBlockElement The element node whose contents should be
2801 * aligned to aAlignType. This should be
2802 * an element which can have `<div>` element
2803 * as its child.
2804 * @param aAlignType New value of align attribute of `<div>`
2805 * element.
2806 */
2807 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AlignBlockContentsWithDivElement(
2808 dom::Element& aBlockElement, const nsAString& aAlignType);
2809
2810 /**
2811 * AlignContentsInAllTableCellsAndListItems() calls
2812 * AlignBlockContentsWithDivElement() for aligning contents in every list
2813 * item element and table cell element in aElement.
2814 *
2815 * @param aElement The node which is or whose descendants should
2816 * be aligned to aAlignType.
2817 * @param aAlignType New value of `align` attribute of `<div>`.
2818 */
2819 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2820 AlignContentsInAllTableCellsAndListItems(dom::Element& aElement,
2821 const nsAString& aAlignType);
2822
2823 /**
2824 * MakeTransitionList() detects all the transitions in the array, where a
2825 * transition means that adjacent nodes in the array don't have the same
2826 * parent.
2827 */
2828 static void MakeTransitionList(
2829 const nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
2830 nsTArray<bool>& aTransitionArray);
2831
2832 /**
2833 * EnsureHardLineBeginsWithFirstChildOf() inserts `<br>` element before
2834 * first child of aRemovingContainerElement if it will not be start of a
2835 * hard line after removing aRemovingContainerElement.
2836 */
2837 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2838 EnsureHardLineBeginsWithFirstChildOf(dom::Element& aRemovingContainerElement);
2839
2840 /**
2841 * EnsureHardLineEndsWithLastChildOf() inserts `<br>` element after last
2842 * child of aRemovingContainerElement if it will not be end of a hard line
2843 * after removing aRemovingContainerElement.
2844 */
2845 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2846 EnsureHardLineEndsWithLastChildOf(dom::Element& aRemovingContainerElement);
2847
2848 /**
2849 * RemoveAlignFromDescendants() removes align attributes, text-align
2850 * properties and <center> elements in aElement.
2851 *
2852 * @param aElement Alignment information of the node and/or its
2853 * descendants will be removed.
2854 * NOTE: aElement must not be a `<table>` element.
2855 * @param aAlignType New align value to be set only when it's in
2856 * CSS mode and this method meets <table> or <hr>.
2857 * XXX This is odd and not clear when you see caller of
2858 * this method. Do you have better idea?
2859 * @param aEditTarget If `OnlyDescendantsExceptTable`, modifies only
2860 * descendants of aElement.
2861 * If `NodeAndDescendantsExceptTable`, modifies `aElement`
2862 * and its descendants.
2863 */
2864 enum class EditTarget {
2865 OnlyDescendantsExceptTable,
2866 NodeAndDescendantsExceptTable
2867 };
2868 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveAlignFromDescendants(
2869 Element& aElement, const nsAString& aAlignType, EditTarget aEditTarget);
2870
2871 /**
2872 * SetBlockElementAlign() resets `align` attribute, `text-align` property
2873 * of descendants of aBlockOrHRElement except `<table>` element descendants.
2874 * Then, set `align` attribute or `text-align` property of aBlockOrHRElement.
2875 *
2876 * @param aBlockOrHRElement The element whose contents will be aligned.
2877 * This must be a block element or `<hr>` element.
2878 * If we're not in CSS mode, this element has
2879 * to support `align` attribute (i.e.,
2880 * `HTMLEditUtils::SupportsAlignAttr()` must
2881 * return true).
2882 * @param aAlignType Boundary or "center" which contents should be
2883 * aligned on.
2884 * @param aEditTarget If `OnlyDescendantsExceptTable`, modifies only
2885 * descendants of aBlockOrHRElement.
2886 * If `NodeAndDescendantsExceptTable`, modifies
2887 * aBlockOrHRElement and its descendants.
2888 */
2889 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2890 SetBlockElementAlign(Element& aBlockOrHRElement, const nsAString& aAlignType,
2891 EditTarget aEditTarget);
2892
2893 /**
2894 * AlignContentsAtSelectionWithEmptyDivElement() inserts new `<div>` element
2895 * at `Selection` to align selected contents. This returns as "handled"
2896 * if this modifies `Selection` so that callers shouldn't modify `Selection`
2897 * in such case especially when using AutoSelectionRestorer.
2898 *
2899 * @param aAlignType New align attribute value where the contents
2900 * should be aligned to.
2901 */
2902 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2903 AlignContentsAtSelectionWithEmptyDivElement(const nsAString& aAlignType);
2904
2905 /**
2906 * AlignNodesAndDescendants() make contents of nodes in aArrayOfContents and
2907 * their descendants aligned to aAlignType.
2908 *
2909 * @param aAlignType New align attribute value where the contents
2910 * should be aligned to.
2911 */
2912 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AlignNodesAndDescendants(
2913 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
2914 const nsAString& aAlignType);
2915
2916 /**
2917 * AlignContentsAtSelection() aligns contents around Selection to aAlignType.
2918 * This creates AutoSelectionRestorer. Therefore, even if this returns
2919 * NS_OK, we might have been destroyed. So, every caller needs to check if
2920 * Destroyed() returns false before modifying the DOM tree or changing
2921 * Selection.
2922 * NOTE: Call AlignAsSubAction() instead.
2923 *
2924 * @param aAlignType New align attribute value where the contents
2925 * should be aligned to.
2926 */
2927 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2928 AlignContentsAtSelection(const nsAString& aAlignType);
2929
2930 /**
2931 * AlignAsSubAction() handles "align" command with `Selection`.
2932 *
2933 * @param aAlignType New align attribute value where the contents
2934 * should be aligned to.
2935 */
2936 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
2937 AlignAsSubAction(const nsAString& aAlignType);
2938
2939 /**
2940 * StartOrEndOfSelectionRangesIsIn() returns true if start or end of one
2941 * of selection ranges is in aContent.
2942 */
2943 bool StartOrEndOfSelectionRangesIsIn(nsIContent& aContent) const;
2944
2945 /**
2946 * FindNearEditableContent() tries to find an editable node near aPoint.
2947 *
2948 * @param aPoint The DOM point where to start to search from.
2949 * @param aDirection If nsIEditor::ePrevious is set, this searches an
2950 * editable node from next nodes. Otherwise, from
2951 * previous nodes.
2952 * @return If found, returns non-nullptr. Otherwise, nullptr.
2953 * Note that if found node is in different table element,
2954 * this returns nullptr.
2955 * And also if aDirection is not nsIEditor::ePrevious,
2956 * the result may be the node pointed by aPoint.
2957 */
2958 template <typename PT, typename CT>
2959 nsIContent* FindNearEditableContent(const EditorDOMPointBase<PT, CT>& aPoint,
2960 nsIEditor::EDirection aDirection);
2961
2962 /**
2963 * AdjustCaretPositionAndEnsurePaddingBRElement() may adjust caret
2964 * position to nearest editable content and if padding `<br>` element is
2965 * necessary at caret position, this creates it.
2966 *
2967 * @param aDirectionAndAmount Direction of the edit action.
2968 */
2969 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2970 AdjustCaretPositionAndEnsurePaddingBRElement(
2971 nsIEditor::EDirection aDirectionAndAmount);
2972
2973 /**
2974 * EnsureSelectionInBodyOrDocumentElement() collapse `Selection` to the
2975 * primary `<body>` element or document element when `Selection` crosses
2976 * `<body>` element's boundary.
2977 */
2978 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2979 EnsureSelectionInBodyOrDocumentElement();
2980
2981 /**
2982 * InsertBRElementToEmptyListItemsAndTableCellsInRange() inserts
2983 * `<br>` element into empty list item or table cell elements between
2984 * aStartRef and aEndRef.
2985 */
2986 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2987 InsertBRElementToEmptyListItemsAndTableCellsInRange(
2988 const RawRangeBoundary& aStartRef, const RawRangeBoundary& aEndRef);
2989
2990 /**
2991 * RemoveEmptyNodesIn() removes all empty nodes in aRange. However, if
2992 * mail-cite node has only a `<br>` element, the node will be removed
2993 * but <br> element is moved to where the mail-cite node was.
2994 * XXX This method is expensive if aRange is too wide and may remove
2995 * unexpected empty element, e.g., it was created by JS, but we haven't
2996 * touched it. Cannot we remove this method and make guarantee that
2997 * empty nodes won't be created?
2998 *
2999 * @param aRange Must be positioned.
3000 */
3001 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveEmptyNodesIn(nsRange& aRange);
3002
3003 /**
3004 * SetSelectionInterlinePosition() may set interline position if caret is
3005 * positioned around `<br>` or block boundary. Don't call this when
3006 * `Selection` is not collapsed.
3007 */
3008 void SetSelectionInterlinePosition();
3009
3010 /**
3011 * EnsureSelectionInBlockElement() may move caret into aElement or its
3012 * parent block if caret is outside of them. Don't call this when
3013 * `Selection` is not collapsed.
3014 */
3015 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
3016 EnsureCaretInBlockElement(dom::Element& aElement);
3017
3018 /**
3019 * Called by `HTMLEditor::OnEndHandlingTopLevelEditSubAction()`. This may
3020 * adjust Selection, remove unnecessary empty nodes, create `<br>` elements
3021 * if needed, etc.
3022 */
3023 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
3024 OnEndHandlingTopLevelEditSubActionInternal();
3025
3026 /**
3027 * MoveSelectedContentsToDivElementToMakeItAbsolutePosition() looks for
3028 * a `<div>` element in selection first. If not, creates new `<div>`
3029 * element. Then, move all selected contents into the target `<div>`
3030 * element.
3031 * Note that this creates AutoSelectionRestorer. Therefore, callers need
3032 * to check whether we have been destroyed even when this returns NS_OK.
3033 *
3034 * @param aTargetElement Returns target `<div>` element which should be
3035 * changed to absolute positioned.
3036 */
3037 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
3038 MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
3039 RefPtr<Element>* aTargetElement);
3040
3041 /**
3042 * SetSelectionToAbsoluteAsSubAction() move selected contents to first
3043 * selected `<div>` element or newly created `<div>` element and make
3044 * the `<div>` element positioned absolutely.
3045 * mNewBlockElement of TopLevelEditSubActionData will be set to the `<div>`
3046 * element.
3047 */
3048 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
3049 SetSelectionToAbsoluteAsSubAction();
3050
3051 /**
3052 * SetSelectionToStaticAsSubAction() sets the `position` property of a
3053 * selection parent's block whose `position` is `absolute` to `static`.
3054 */
3055 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
3056 SetSelectionToStaticAsSubAction();
3057
3058 /**
3059 * AddZIndexAsSubAction() adds aChange to `z-index` of nearest parent
3060 * absolute-positioned element from current selection.
3061 *
3062 * @param aChange Amount to change `z-index`.
3063 */
3064 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
3065 AddZIndexAsSubAction(int32_t aChange);
3066
3067 /**
3068 * OnDocumentModified() is called when editor content is changed.
3069 */
3070 MOZ_CAN_RUN_SCRIPT nsresult OnDocumentModified();
3071
3072 protected: // Called by helper classes.
3073 MOZ_CAN_RUN_SCRIPT virtual void OnStartToHandleTopLevelEditSubAction(
3074 EditSubAction aTopLevelEditSubAction,
3075 nsIEditor::EDirection aDirectionOfTopLevelEditSubAction,
3076 ErrorResult& aRv) override;
3077 MOZ_CAN_RUN_SCRIPT virtual nsresult OnEndHandlingTopLevelEditSubAction()
3078 override;
3079
3080 protected: // Shouldn't be used by friend classes
3081 virtual ~HTMLEditor();
3082
3083 template <typename PT, typename CT>
3084 [[nodiscard]] MOZ_CAN_RUN_SCRIPT MOZ_NEVER_INLINE_DEBUG nsresult
CollapseSelectionTo(const EditorDOMPointBase<PT,CT> & aPoint)3085 CollapseSelectionTo(const EditorDOMPointBase<PT, CT>& aPoint) {
3086 ErrorResult error;
3087 CollapseSelectionTo(aPoint, error);
3088 return error.StealNSResult();
3089 }
3090
3091 template <typename PT, typename CT>
CollapseSelectionTo(const EditorDOMPointBase<PT,CT> & aPoint,ErrorResult & aRv)3092 MOZ_CAN_RUN_SCRIPT MOZ_NEVER_INLINE_DEBUG void CollapseSelectionTo(
3093 const EditorDOMPointBase<PT, CT>& aPoint, ErrorResult& aRv) {
3094 MOZ_ASSERT(IsEditActionDataAvailable());
3095 MOZ_ASSERT(!aRv.Failed());
3096
3097 SelectionRefPtr()->Collapse(aPoint, aRv);
3098 if (NS_WARN_IF(Destroyed())) {
3099 aRv = NS_ERROR_EDITOR_DESTROYED;
3100 return;
3101 }
3102 NS_WARNING_ASSERTION(!aRv.Failed(), "Selection::Collapse() failed");
3103 }
3104
3105 [[nodiscard]] MOZ_CAN_RUN_SCRIPT MOZ_NEVER_INLINE_DEBUG nsresult
CollapseSelectionToStartOf(nsINode & aNode)3106 CollapseSelectionToStartOf(nsINode& aNode) {
3107 return CollapseSelectionTo(EditorRawDOMPoint(&aNode, 0));
3108 }
3109
CollapseSelectionToStartOf(nsINode & aNode,ErrorResult & aRv)3110 MOZ_CAN_RUN_SCRIPT MOZ_NEVER_INLINE_DEBUG void CollapseSelectionToStartOf(
3111 nsINode& aNode, ErrorResult& aRv) {
3112 CollapseSelectionTo(EditorRawDOMPoint(&aNode, 0), aRv);
3113 }
3114
3115 /**
3116 * InitEditorContentAndSelection() may insert `<br>` elements and padding
3117 * `<br>` elements if they are required for `<body>` or document element.
3118 * And collapse selection at the end if there is no selection ranges.
3119 * XXX I think that this should work with active editing host unless
3120 * all over the document is ediable (i.e., in design mode or `<body>`
3121 * or `<html>` has `contenteditable` attribute).
3122 */
3123 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InitEditorContentAndSelection();
3124
3125 MOZ_CAN_RUN_SCRIPT virtual nsresult SelectAllInternal() override;
3126
3127 /**
3128 * SelectContentInternal() sets Selection to aContentToSelect to
3129 * aContentToSelect + 1 in parent of aContentToSelect.
3130 *
3131 * @param aContentToSelect The content which should be selected.
3132 */
3133 MOZ_CAN_RUN_SCRIPT nsresult
3134 SelectContentInternal(nsIContent& aContentToSelect);
3135
3136 /**
3137 * CollapseSelectionAfter() collapses Selection after aElement.
3138 * If aElement is an orphan node or not in editing host, returns error.
3139 */
3140 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
3141 CollapseSelectionAfter(Element& aElement);
3142
3143 /**
3144 * GetInclusiveAncestorByTagNameAtSelection() looks for an element node whose
3145 * name matches aTagName from anchor node of Selection to <body> element.
3146 *
3147 * @param aTagName The tag name which you want to look for.
3148 * Must not be nsGkAtoms::_empty.
3149 * If nsGkAtoms::list, the result may be <ul>, <ol> or
3150 * <dl> element.
3151 * If nsGkAtoms::td, the result may be <td> or <th>.
3152 * If nsGkAtoms::href, the result may be <a> element
3153 * which has "href" attribute with non-empty value.
3154 * If nsGkAtoms::anchor, the result may be <a> which
3155 * has "name" attribute with non-empty value.
3156 * @return If an element which matches aTagName, returns
3157 * an Element. Otherwise, nullptr.
3158 */
3159 Element* GetInclusiveAncestorByTagNameAtSelection(
3160 const nsStaticAtom& aTagName) const;
3161
3162 /**
3163 * GetInclusiveAncestorByTagNameInternal() looks for an element node whose
3164 * name matches aTagName from aNode to <body> element.
3165 *
3166 * @param aTagName The tag name which you want to look for.
3167 * Must not be nsGkAtoms::_empty.
3168 * If nsGkAtoms::list, the result may be <ul>, <ol> or
3169 * <dl> element.
3170 * If nsGkAtoms::td, the result may be <td> or <th>.
3171 * If nsGkAtoms::href, the result may be <a> element
3172 * which has "href" attribute with non-empty value.
3173 * If nsGkAtoms::anchor, the result may be <a> which
3174 * has "name" attribute with non-empty value.
3175 * @param aContent Start node to look for the element. This should
3176 * not be an orphan node.
3177 * @return If an element which matches aTagName, returns
3178 * an Element. Otherwise, nullptr.
3179 */
3180 Element* GetInclusiveAncestorByTagNameInternal(const nsStaticAtom& aTagName,
3181 nsIContent& aContent) const;
3182
3183 /**
3184 * GetSelectedElement() returns a "selected" element node. "selected" means:
3185 * - there is only one selection range
3186 * - the range starts from an element node or in an element
3187 * - the range ends at immediately after same element
3188 * - and the range does not include any other element nodes.
3189 * Additionally, only when aTagName is nsGkAtoms::href, this thinks that an
3190 * <a> element which has non-empty "href" attribute includes the range, the
3191 * <a> element is selected.
3192 *
3193 * NOTE: This method is implementation of nsIHTMLEditor.getSelectedElement()
3194 * and comm-central depends on this behavior. Therefore, if you need to use
3195 * this method internally but you need to change, perhaps, you should create
3196 * another method for avoiding breakage of comm-central apps.
3197 *
3198 * @param aTagName The atom of tag name in lower case. Set this to
3199 * result of EditorUtils::GetTagNameAtom() if you have a
3200 * tag name with nsString.
3201 * If nullptr, this returns any element node or nullptr.
3202 * If nsGkAtoms::href, this returns an <a> element which
3203 * has non-empty "href" attribute or nullptr.
3204 * If nsGkAtoms::anchor, this returns an <a> element which
3205 * has non-empty "name" attribute or nullptr.
3206 * Otherwise, returns an element node whose name is
3207 * same as aTagName or nullptr.
3208 * @param aRv Returns error code.
3209 * @return A "selected" element.
3210 */
3211 already_AddRefed<Element> GetSelectedElement(const nsAtom* aTagName,
3212 ErrorResult& aRv);
3213
3214 /**
3215 * GetFirstTableRowElement() returns the first <tr> element in the most
3216 * nearest ancestor of aTableOrElementInTable or itself.
3217 *
3218 * @param aTableOrElementInTable <table> element or another element.
3219 * If this is a <table> element, returns
3220 * first <tr> element in it. Otherwise,
3221 * returns first <tr> element in nearest
3222 * ancestor <table> element.
3223 * @param aRv Returns an error code. When
3224 * aTableOrElementInTable is neither
3225 * <table> nor in a <table> element,
3226 * returns NS_ERROR_FAILURE.
3227 * However, if <table> does not have
3228 * <tr> element, returns NS_OK.
3229 */
3230 Element* GetFirstTableRowElement(Element& aTableOrElementInTable,
3231 ErrorResult& aRv) const;
3232
3233 /**
3234 * GetNextTableRowElement() returns next <tr> element of aTableRowElement.
3235 * This won't cross <table> element boundary but may cross table section
3236 * elements like <tbody>.
3237 *
3238 * @param aTableRowElement A <tr> element.
3239 * @param aRv Returns error. If given element is <tr> but
3240 * there is no next <tr> element, this returns
3241 * nullptr but does not return error.
3242 */
3243 Element* GetNextTableRowElement(Element& aTableRowElement,
3244 ErrorResult& aRv) const;
3245
3246 struct CellAndIndexes;
3247 struct CellData;
3248
3249 /**
3250 * CellIndexes store both row index and column index of a table cell.
3251 */
3252 struct MOZ_STACK_CLASS CellIndexes final {
3253 int32_t mRow;
3254 int32_t mColumn;
3255
3256 /**
3257 * This constructor initializes mRowIndex and mColumnIndex with indexes of
3258 * aCellElement.
3259 *
3260 * @param aCellElement An <td> or <th> element.
3261 * @param aRv Returns error if layout information is not
3262 * available or given element is not a table cell.
3263 */
CellIndexesfinal3264 MOZ_CAN_RUN_SCRIPT CellIndexes(Element& aCellElement, PresShell* aPresShell,
3265 ErrorResult& aRv)
3266 : mRow(-1), mColumn(-1) {
3267 MOZ_ASSERT(!aRv.Failed());
3268 Update(aCellElement, aPresShell, aRv);
3269 }
3270
3271 /**
3272 * Update mRowIndex and mColumnIndex with indexes of aCellElement.
3273 *
3274 * @param See above.
3275 */
3276 MOZ_CAN_RUN_SCRIPT void Update(Element& aCellElement, PresShell* aPresShell,
3277 ErrorResult& aRv);
3278
3279 /**
3280 * This constructor initializes mRowIndex and mColumnIndex with indexes of
3281 * cell element which contains anchor of Selection.
3282 *
3283 * @param aHTMLEditor The editor which creates the instance.
3284 * @param aSelection The Selection for the editor.
3285 * @param aRv Returns error if there is no cell element
3286 * which contains anchor of Selection, or layout
3287 * information is not available.
3288 */
CellIndexesfinal3289 MOZ_CAN_RUN_SCRIPT CellIndexes(HTMLEditor& aHTMLEditor,
3290 Selection& aSelection, ErrorResult& aRv)
3291 : mRow(-1), mColumn(-1) {
3292 Update(aHTMLEditor, aSelection, aRv);
3293 }
3294
3295 /**
3296 * Update mRowIndex and mColumnIndex with indexes of cell element which
3297 * contains anchor of Selection.
3298 *
3299 * @param See above.
3300 */
3301 MOZ_CAN_RUN_SCRIPT void Update(HTMLEditor& aHTMLEditor,
3302 Selection& aSelection, ErrorResult& aRv);
3303
3304 bool operator==(const CellIndexes& aOther) const {
3305 return mRow == aOther.mRow && mColumn == aOther.mColumn;
3306 }
3307 bool operator!=(const CellIndexes& aOther) const {
3308 return mRow != aOther.mRow || mColumn != aOther.mColumn;
3309 }
3310
3311 private:
CellIndexesfinal3312 CellIndexes() : mRow(-1), mColumn(-1) {}
3313
3314 friend struct CellAndIndexes;
3315 friend struct CellData;
3316 };
3317
3318 struct MOZ_STACK_CLASS CellAndIndexes final {
3319 RefPtr<Element> mElement;
3320 CellIndexes mIndexes;
3321
3322 /**
3323 * This constructor initializes the members with cell element which is
3324 * selected by first range of the Selection. Note that even if the
3325 * first range is in the cell element, this does not treat it as the
3326 * cell element is selected.
3327 */
CellAndIndexesfinal3328 MOZ_CAN_RUN_SCRIPT CellAndIndexes(HTMLEditor& aHTMLEditor,
3329 Selection& aSelection, ErrorResult& aRv) {
3330 Update(aHTMLEditor, aSelection, aRv);
3331 }
3332
3333 /**
3334 * Update mElement and mIndexes with cell element which is selected by
3335 * first range of the Selection. Note that even if the first range is
3336 * in the cell element, this does not treat it as the cell element is
3337 * selected.
3338 */
3339 MOZ_CAN_RUN_SCRIPT void Update(HTMLEditor& aHTMLEditor,
3340 Selection& aSelection, ErrorResult& aRv);
3341 };
3342
3343 struct MOZ_STACK_CLASS CellData final {
3344 RefPtr<Element> mElement;
3345 // Current indexes which this is initialized with.
3346 CellIndexes mCurrent;
3347 // First column/row indexes of the cell. When current position is spanned
3348 // from other column/row, this value becomes different from mCurrent.
3349 CellIndexes mFirst;
3350 // Computed rowspan/colspan values which are specified to the cell.
3351 // Note that if the cell has larger rowspan/colspan value than actual
3352 // table size, these values are the larger values.
3353 int32_t mRowSpan;
3354 int32_t mColSpan;
3355 // Effective rowspan/colspan value at the index. For example, if first
3356 // cell element in first row has rowspan="3", then, if this is initialized
3357 // with 0-0 indexes, effective rowspan is 3. However, if this is
3358 // initialized with 1-0 indexes, effective rowspan is 2.
3359 int32_t mEffectiveRowSpan;
3360 int32_t mEffectiveColSpan;
3361 // mIsSelected is set to true if mElement itself or its parent <tr> or
3362 // <table> is selected. Otherwise, e.g., the cell just contains selection
3363 // range, this is set to false.
3364 bool mIsSelected;
3365
CellDatafinal3366 CellData()
3367 : mRowSpan(-1),
3368 mColSpan(-1),
3369 mEffectiveRowSpan(-1),
3370 mEffectiveColSpan(-1),
3371 mIsSelected(false) {}
3372
3373 /**
3374 * Those constructors initializes the members with a <table> element and
3375 * both row and column index to specify a cell element.
3376 */
CellDatafinal3377 CellData(HTMLEditor& aHTMLEditor, Element& aTableElement, int32_t aRowIndex,
3378 int32_t aColumnIndex, ErrorResult& aRv) {
3379 Update(aHTMLEditor, aTableElement, aRowIndex, aColumnIndex, aRv);
3380 }
3381
CellDatafinal3382 CellData(HTMLEditor& aHTMLEditor, Element& aTableElement,
3383 const CellIndexes& aIndexes, ErrorResult& aRv) {
3384 Update(aHTMLEditor, aTableElement, aIndexes, aRv);
3385 }
3386
3387 /**
3388 * Those Update() methods updates the members with a <table> element and
3389 * both row and column index to specify a cell element.
3390 */
Updatefinal3391 void Update(HTMLEditor& aHTMLEditor, Element& aTableElement,
3392 int32_t aRowIndex, int32_t aColumnIndex, ErrorResult& aRv) {
3393 mCurrent.mRow = aRowIndex;
3394 mCurrent.mColumn = aColumnIndex;
3395 Update(aHTMLEditor, aTableElement, aRv);
3396 }
3397
Updatefinal3398 void Update(HTMLEditor& aHTMLEditor, Element& aTableElement,
3399 const CellIndexes& aIndexes, ErrorResult& aRv) {
3400 mCurrent = aIndexes;
3401 Update(aHTMLEditor, aTableElement, aRv);
3402 }
3403
3404 void Update(HTMLEditor& aHTMLEditor, Element& aTableElement,
3405 ErrorResult& aRv);
3406
3407 /**
3408 * FailedOrNotFound() returns true if this failed to initialize/update
3409 * or succeeded but found no cell element.
3410 */
FailedOrNotFoundfinal3411 bool FailedOrNotFound() const { return !mElement; }
3412
3413 /**
3414 * IsSpannedFromOtherRowOrColumn(), IsSpannedFromOtherColumn and
3415 * IsSpannedFromOtherRow() return true if there is no cell element
3416 * at the index because of spanning from other row and/or column.
3417 */
IsSpannedFromOtherRowOrColumnfinal3418 bool IsSpannedFromOtherRowOrColumn() const {
3419 return mElement && mCurrent != mFirst;
3420 }
IsSpannedFromOtherColumnfinal3421 bool IsSpannedFromOtherColumn() const {
3422 return mElement && mCurrent.mColumn != mFirst.mColumn;
3423 }
IsSpannedFromOtherRowfinal3424 bool IsSpannedFromOtherRow() const {
3425 return mElement && mCurrent.mRow != mFirst.mRow;
3426 }
3427
3428 /**
3429 * NextColumnIndex() and NextRowIndex() return column/row index of
3430 * next cell. Note that this does not check whether there is next
3431 * cell or not actually.
3432 */
NextColumnIndexfinal3433 int32_t NextColumnIndex() const {
3434 if (NS_WARN_IF(FailedOrNotFound())) {
3435 return -1;
3436 }
3437 return mCurrent.mColumn + mEffectiveColSpan;
3438 }
NextRowIndexfinal3439 int32_t NextRowIndex() const {
3440 if (NS_WARN_IF(FailedOrNotFound())) {
3441 return -1;
3442 }
3443 return mCurrent.mRow + mEffectiveRowSpan;
3444 }
3445
3446 /**
3447 * LastColumnIndex() and LastRowIndex() return column/row index of
3448 * column/row which is spanned by the cell.
3449 */
LastColumnIndexfinal3450 int32_t LastColumnIndex() const {
3451 if (NS_WARN_IF(FailedOrNotFound())) {
3452 return -1;
3453 }
3454 return NextColumnIndex() - 1;
3455 }
LastRowIndexfinal3456 int32_t LastRowIndex() const {
3457 if (NS_WARN_IF(FailedOrNotFound())) {
3458 return -1;
3459 }
3460 return NextRowIndex() - 1;
3461 }
3462
3463 /**
3464 * NumberOfPrecedingColmuns() and NumberOfPrecedingRows() return number of
3465 * preceding columns/rows if current index is spanned from other column/row.
3466 * Otherwise, i.e., current point is not spanned form other column/row,
3467 * returns 0.
3468 */
NumberOfPrecedingColmunsfinal3469 int32_t NumberOfPrecedingColmuns() const {
3470 if (NS_WARN_IF(FailedOrNotFound())) {
3471 return -1;
3472 }
3473 return mCurrent.mColumn - mFirst.mColumn;
3474 }
NumberOfPrecedingRowsfinal3475 int32_t NumberOfPrecedingRows() const {
3476 if (NS_WARN_IF(FailedOrNotFound())) {
3477 return -1;
3478 }
3479 return mCurrent.mRow - mFirst.mRow;
3480 }
3481
3482 /**
3483 * NumberOfFollowingColumns() and NumberOfFollowingRows() return
3484 * number of remaining columns/rows if the cell spans to other
3485 * column/row.
3486 */
NumberOfFollowingColumnsfinal3487 int32_t NumberOfFollowingColumns() const {
3488 if (NS_WARN_IF(FailedOrNotFound())) {
3489 return -1;
3490 }
3491 return mEffectiveColSpan - 1;
3492 }
NumberOfFollowingRowsfinal3493 int32_t NumberOfFollowingRows() const {
3494 if (NS_WARN_IF(FailedOrNotFound())) {
3495 return -1;
3496 }
3497 return mEffectiveRowSpan - 1;
3498 }
3499 };
3500
3501 /**
3502 * TableSize stores and computes number of rows and columns of a <table>
3503 * element.
3504 */
3505 struct MOZ_STACK_CLASS TableSize final {
3506 int32_t mRowCount;
3507 int32_t mColumnCount;
3508
3509 /**
3510 * @param aHTMLEditor The editor which creates the instance.
3511 * @param aTableOrElementInTable If a <table> element, computes number
3512 * of rows and columns of it.
3513 * If another element in a <table> element,
3514 * computes number of rows and columns
3515 * of nearest ancestor <table> element.
3516 * Otherwise, i.e., non-<table> element
3517 * not in <table>, returns error.
3518 * @param aRv Returns error if the element is not
3519 * in <table> or layout information is
3520 * not available.
3521 */
TableSizefinal3522 TableSize(HTMLEditor& aHTMLEditor, Element& aTableOrElementInTable,
3523 ErrorResult& aRv)
3524 : mRowCount(-1), mColumnCount(-1) {
3525 MOZ_ASSERT(!aRv.Failed());
3526 Update(aHTMLEditor, aTableOrElementInTable, aRv);
3527 }
3528
3529 /**
3530 * Update mRowCount and mColumnCount for aTableOrElementInTable.
3531 * See above for the detail.
3532 */
3533 void Update(HTMLEditor& aHTMLEditor, Element& aTableOrElementInTable,
3534 ErrorResult& aRv);
3535
IsEmptyfinal3536 bool IsEmpty() const { return !mRowCount || !mColumnCount; }
3537 };
3538
3539 /**
3540 * GetTableCellElementAt() returns a <td> or <th> element of aTableElement
3541 * if there is a cell at the indexes.
3542 *
3543 * @param aTableElement Must be a <table> element.
3544 * @param aCellIndexes Indexes of cell which you want.
3545 * If rowspan and/or colspan is specified 2 or
3546 * larger, any indexes are allowed to retrieve
3547 * the cell in the area.
3548 * @return The cell element if there is in the <table>.
3549 * Returns nullptr without error if the indexes
3550 * are out of bounds.
3551 */
GetTableCellElementAt(Element & aTableElement,const CellIndexes & aCellIndexes)3552 Element* GetTableCellElementAt(Element& aTableElement,
3553 const CellIndexes& aCellIndexes) const {
3554 return GetTableCellElementAt(aTableElement, aCellIndexes.mRow,
3555 aCellIndexes.mColumn);
3556 }
3557 Element* GetTableCellElementAt(Element& aTableElement, int32_t aRowIndex,
3558 int32_t aColumnIndex) const;
3559
3560 /**
3561 * GetSelectedOrParentTableElement() returns <td>, <th>, <tr> or <table>
3562 * element:
3563 * #1 if the first selection range selects a cell, returns it.
3564 * #2 if the first selection range does not select a cell and
3565 * the selection anchor refers a <table>, returns it.
3566 * #3 if the first selection range does not select a cell and
3567 * the selection anchor refers a <tr>, returns it.
3568 * #4 if the first selection range does not select a cell and
3569 * the selection anchor refers a <td>, returns it.
3570 * #5 otherwise, nearest ancestor <td> or <th> element of the
3571 * selection anchor if there is.
3572 * In #1 and #4, *aIsCellSelected will be set to true (i.e,, when
3573 * a selection range selects a cell element).
3574 */
3575 already_AddRefed<Element> GetSelectedOrParentTableElement(
3576 ErrorResult& aRv, bool* aIsCellSelected = nullptr) const;
3577
3578 /**
3579 * PasteInternal() pasts text with replacing selected content.
3580 * This tries to dispatch ePaste event first. If its defaultPrevent() is
3581 * called, this does nothing but returns NS_OK.
3582 *
3583 * @param aClipboardType nsIClipboard::kGlobalClipboard or
3584 * nsIClipboard::kSelectionClipboard.
3585 */
3586 MOZ_CAN_RUN_SCRIPT nsresult PasteInternal(int32_t aClipboardType);
3587
3588 /**
3589 * InsertWithQuotationsAsSubAction() inserts aQuotedText with appending ">"
3590 * to start of every line.
3591 *
3592 * @param aQuotedText String to insert. This will be quoted by ">"
3593 * automatically.
3594 */
3595 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult
3596 InsertWithQuotationsAsSubAction(const nsAString& aQuotedText) final;
3597
3598 /**
3599 * InsertAsCitedQuotationInternal() inserts a <blockquote> element whose
3600 * cite attribute is aCitation and whose content is aQuotedText.
3601 * Note that this shouldn't be called when IsPlaintextEditor() is true.
3602 *
3603 * @param aQuotedText HTML source if aInsertHTML is true. Otherwise,
3604 * plain text. This is inserted into new <blockquote>
3605 * element.
3606 * @param aCitation cite attribute value of new <blockquote> element.
3607 * @param aInsertHTML true if aQuotedText should be treated as HTML
3608 * source.
3609 * false if aQuotedText should be treated as plain
3610 * text.
3611 * @param aNodeInserted [OUT] The new <blockquote> element.
3612 */
3613 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertAsCitedQuotationInternal(
3614 const nsAString& aQuotedText, const nsAString& aCitation,
3615 bool aInsertHTML, nsINode** aNodeInserted);
3616
3617 /**
3618 * InsertNodeIntoProperAncestorWithTransaction() attempts to insert aNode
3619 * into the document, at aPointToInsert. Checks with strict dtd to see if
3620 * containment is allowed. If not allowed, will attempt to find a parent
3621 * in the parent hierarchy of aPointToInsert.GetContainer() that will accept
3622 * aNode as a child. If such a parent is found, will split the document
3623 * tree from aPointToInsert up to parent, and then insert aNode.
3624 * aPointToInsert is then adjusted to point to the actual location that
3625 * aNode was inserted at. aSplitAtEdges specifies if the splitting process
3626 * is allowed to result in empty nodes.
3627 *
3628 * @param aNode Node to insert.
3629 * @param aPointToInsert Insertion point.
3630 * @param aSplitAtEdges Splitting can result in empty nodes?
3631 * @return Returns inserted point if succeeded.
3632 * Otherwise, the result is not set.
3633 */
3634 MOZ_CAN_RUN_SCRIPT EditorDOMPoint InsertNodeIntoProperAncestorWithTransaction(
3635 nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
3636 SplitAtEdges aSplitAtEdges);
3637
3638 /**
3639 * InsertBRElementAtSelectionWithTransaction() inserts a new <br> element at
3640 * selection. If there is non-collapsed selection ranges, the selected
3641 * ranges is deleted first.
3642 */
3643 MOZ_CAN_RUN_SCRIPT nsresult InsertBRElementAtSelectionWithTransaction();
3644
3645 /**
3646 * InsertTextWithQuotationsInternal() replaces selection with new content.
3647 * First, this method splits aStringToInsert to multiple chunks which start
3648 * with non-linebreaker except first chunk and end with a linebreaker except
3649 * last chunk. Then, each chunk starting with ">" is inserted after wrapping
3650 * with <span _moz_quote="true">, and each chunk not starting with ">" is
3651 * inserted as normal text.
3652 */
3653 MOZ_CAN_RUN_SCRIPT nsresult
3654 InsertTextWithQuotationsInternal(const nsAString& aStringToInsert);
3655
3656 /**
3657 * ReplaceContainerWithTransactionInternal() is implementation of
3658 * ReplaceContainerWithTransaction() and
3659 * ReplaceContainerAndCloneAttributesWithTransaction().
3660 *
3661 * @param aOldContainer The element which will be replaced with new
3662 * element.
3663 * @param aTagName The name of new element node.
3664 * @param aAttribute Attribute name which will be set to the new
3665 * element. This will be ignored if
3666 * aCloneAllAttributes is set to true.
3667 * @param aAttributeValue Attribute value which will be set to
3668 * aAttribute.
3669 * @param aCloneAllAttributes If true, all attributes of aOldContainer will
3670 * be copied to the new element.
3671 */
3672 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element>
3673 ReplaceContainerWithTransactionInternal(Element& aElement, nsAtom& aTagName,
3674 nsAtom& aAttribute,
3675 const nsAString& aAttributeValue,
3676 bool aCloneAllAttributes);
3677
3678 /**
3679 * InsertContainerWithTransactionInternal() creates new element whose name is
3680 * aTagName, moves aContent into the new element, then, inserts the new
3681 * element into where aContent was. If aAttribute is not nsGkAtoms::_empty,
3682 * aAttribute of the new element will be set to aAttributeValue.
3683 *
3684 * @param aContent The content which will be wrapped with new
3685 * element.
3686 * @param aTagName Element name of new element which will wrap
3687 * aContent and be inserted into where aContent
3688 * was.
3689 * @param aAttribute Attribute which should be set to the new
3690 * element. If this is nsGkAtoms::_empty,
3691 * this does not set any attributes to the new
3692 * element.
3693 * @param aAttributeValue Value to be set to aAttribute.
3694 * @return The new element.
3695 */
3696 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element>
3697 InsertContainerWithTransactionInternal(nsIContent& aContent, nsAtom& aTagName,
3698 nsAtom& aAttribute,
3699 const nsAString& aAttributeValue);
3700
3701 /**
3702 * DeleteSelectionAndCreateElement() creates a element whose name is aTag.
3703 * And insert it into the DOM tree after removing the selected content.
3704 *
3705 * @param aTag The element name to be created.
3706 * @return Created new element.
3707 */
3708 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> DeleteSelectionAndCreateElement(
3709 nsAtom& aTag);
3710
3711 /**
3712 * This method first deletes the selection, if it's not collapsed. Then if
3713 * the selection lies in a CharacterData node, it splits it. If the
3714 * selection is at this point collapsed in a CharacterData node, it's
3715 * adjusted to be collapsed right before or after the node instead (which is
3716 * always possible, since the node was split).
3717 */
3718 MOZ_CAN_RUN_SCRIPT nsresult DeleteSelectionAndPrepareToCreateNode();
3719
3720 /**
3721 * PrepareToInsertBRElement() returns a point where new <br> element should
3722 * be inserted. If aPointToInsert points middle of a text node, this method
3723 * splits the text node and returns the point before right node.
3724 *
3725 * @param aPointToInsert Candidate point to insert new <br> element.
3726 * @return Computed point to insert new <br> element.
3727 * If something failed, this is unset.
3728 */
3729 MOZ_CAN_RUN_SCRIPT EditorDOMPoint
3730 PrepareToInsertBRElement(const EditorDOMPoint& aPointToInsert);
3731
3732 /**
3733 * IndentAsSubAction() indents the content around Selection.
3734 */
3735 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult IndentAsSubAction();
3736
3737 /**
3738 * OutdentAsSubAction() outdents the content around Selection.
3739 */
3740 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult OutdentAsSubAction();
3741
3742 MOZ_CAN_RUN_SCRIPT nsresult LoadHTML(const nsAString& aInputString);
3743
3744 /**
3745 * SetInlinePropertyInternal() stores new style with `mTypeInState` if
3746 * `Selection` is collapsed. Otherwise, applying the style at all selection
3747 * ranges.
3748 *
3749 * @param aProperty One of the presentation tag names which we
3750 * support in style editor.
3751 * @param aAttribute For some aProperty values, needs to be set to
3752 * its attribute name. Otherwise, nullptr.
3753 * @param aAttributeValue The value of aAttribute.
3754 */
3755 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertyInternal(
3756 nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aValue);
3757
3758 /**
3759 * RemoveInlinePropertyInternal() removes specified style from `mTypeInState`
3760 * if `Selection` is collapsed. Otherwise, removing the style.
3761 *
3762 * @param aHTMLProperty nullptr if you want to remove all inline styles.
3763 * Otherwise, one of the presentation tag names
3764 * which we support in style editor.
3765 * @param aAttribute For some aHTMLProperty values, need to be set to
3766 * its attribute name. Otherwise, nullptr.
3767 * @param aRemoveRelatedElements If Yes, this method removes different
3768 * name's elements in the block if
3769 * necessary. For example, if
3770 * aHTMLProperty is nsGkAtoms::b,
3771 * `<strong>` elements are also removed.
3772 */
3773 enum class RemoveRelatedElements { Yes, No };
3774 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveInlinePropertyInternal(
3775 nsStaticAtom* aHTMLProperty, nsStaticAtom* aAttribute,
3776 RemoveRelatedElements aRemoveRelatedElements);
3777
3778 /**
3779 * ReplaceHeadContentsWithSourceWithTransaction() replaces all children of
3780 * <head> element with given source code. This is undoable.
3781 *
3782 * @param aSourceToInsert HTML source fragment to replace the children
3783 * of <head> element.
3784 */
3785 MOZ_CAN_RUN_SCRIPT nsresult ReplaceHeadContentsWithSourceWithTransaction(
3786 const nsAString& aSourceToInsert);
3787
3788 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetCSSBackgroundColorState(
3789 bool* aMixed, nsAString& aOutColor, bool aBlockLevel);
3790 nsresult GetHTMLBackgroundColorState(bool* aMixed, nsAString& outColor);
3791
3792 nsresult GetLastCellInRow(nsINode* aRowNode, nsINode** aCellNode);
3793
3794 /**
3795 * This sets background on the appropriate container element (table, cell,)
3796 * or calls into nsTextEditor to set the page background.
3797 */
3798 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
3799 SetCSSBackgroundColorWithTransaction(const nsAString& aColor);
3800 MOZ_CAN_RUN_SCRIPT nsresult
3801 SetHTMLBackgroundColorWithTransaction(const nsAString& aColor);
3802
3803 MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void InitializeSelectionAncestorLimit(
3804 nsIContent& aAncestorLimit) override;
3805
3806 /**
3807 * Make the given selection span the entire document.
3808 */
3809 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult SelectEntireDocument()
3810 override;
3811
3812 /**
3813 * Use this to assure that selection is set after attribute nodes when
3814 * trying to collapse selection at begining of a block node
3815 * e.g., when setting at beginning of a table cell
3816 * This will stop at a table, however, since we don't want to
3817 * "drill down" into nested tables.
3818 */
3819 MOZ_CAN_RUN_SCRIPT void CollapseSelectionToDeepestNonTableFirstChild(
3820 nsINode* aNode);
3821 /**
3822 * MaybeCollapseSelectionAtFirstEditableNode() may collapse selection at
3823 * proper position to staring to edit. If there is a non-editable node
3824 * before any editable text nodes or inline elements which can have text
3825 * nodes as their children, collapse selection at start of the editing
3826 * host. If there is an editable text node which is not collapsed, collapses
3827 * selection at the start of the text node. If there is an editable inline
3828 * element which cannot have text nodes as its child, collapses selection at
3829 * before the element node. Otherwise, collapses selection at start of the
3830 * editing host.
3831 *
3832 * @param aIgnoreIfSelectionInEditingHost
3833 * This method does nothing if selection is in the
3834 * editing host except if it's collapsed at start of
3835 * the editing host.
3836 * Note that if selection ranges were outside of
3837 * current selection limiter, selection was collapsed
3838 * at the start of the editing host therefore, if
3839 * you call this with setting this to true, you can
3840 * keep selection ranges if user has already been
3841 * changed.
3842 */
3843 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
3844 MaybeCollapseSelectionAtFirstEditableNode(
3845 bool aIgnoreIfSelectionInEditingHost);
3846
3847 class BlobReader final {
3848 typedef EditorBase::AutoEditActionDataSetter AutoEditActionDataSetter;
3849
3850 public:
3851 BlobReader(dom::BlobImpl* aBlob, HTMLEditor* aHTMLEditor, bool aIsSafe,
3852 Document* aSourceDoc, const EditorDOMPoint& aPointToInsert,
3853 bool aDoDeleteSelection);
3854
3855 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BlobReader)
3856 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(BlobReader)
3857
3858 MOZ_CAN_RUN_SCRIPT nsresult OnResult(const nsACString& aResult);
3859 nsresult OnError(const nsAString& aErrorName);
3860
3861 private:
3862 ~BlobReader() = default;
3863
3864 RefPtr<dom::BlobImpl> mBlob;
3865 RefPtr<HTMLEditor> mHTMLEditor;
3866 RefPtr<dom::DataTransfer> mDataTransfer;
3867 nsCOMPtr<Document> mSourceDoc;
3868 EditorDOMPoint mPointToInsert;
3869 EditAction mEditAction;
3870 bool mIsSafe;
3871 bool mDoDeleteSelection;
3872 bool mNeedsToDispatchBeforeInputEvent;
3873 };
3874
3875 virtual void CreateEventListeners() override;
3876 virtual nsresult InstallEventListeners() override;
3877 virtual void RemoveEventListeners() override;
3878
3879 bool ShouldReplaceRootElement() const;
3880 MOZ_CAN_RUN_SCRIPT void NotifyRootChanged();
3881 Element* GetBodyElement() const;
3882
3883 /**
3884 * Get the focused node of this editor.
3885 * @return If the editor has focus, this returns the focused node.
3886 * Otherwise, returns null.
3887 */
3888 nsINode* GetFocusedNode() const;
3889
3890 virtual already_AddRefed<Element> GetInputEventTargetElement() const override;
3891
3892 /**
3893 * Return TRUE if aElement is a table-related elemet and caret was set.
3894 */
3895 MOZ_CAN_RUN_SCRIPT bool SetCaretInTableCell(dom::Element* aElement);
3896
3897 /**
3898 * HandleTabKeyPressInTable() handles "Tab" key press in table if selection
3899 * is in a `<table>` element.
3900 */
3901 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
3902 HandleTabKeyPressInTable(WidgetKeyboardEvent* aKeyboardEvent);
3903
3904 /**
3905 * InsertPosition is an enum to indicate where the method should insert to.
3906 */
3907 enum class InsertPosition {
3908 // Before selected cell or a cell containing first selection range.
3909 eBeforeSelectedCell,
3910 // After selected cell or a cell containing first selection range.
3911 eAfterSelectedCell,
3912 };
3913
3914 /**
3915 * InsertTableCellsWithTransaction() inserts <td> elements before or after
3916 * a cell element containing first selection range. I.e., if the cell
3917 * spans columns and aInsertPosition is eAfterSelectedCell, new columns
3918 * will be inserted after the right-most column which contains the cell.
3919 * Note that this simply inserts <td> elements, i.e., colspan and rowspan
3920 * around the cell containing selection are not modified. So, for example,
3921 * adding a cell to rectangular table changes non-rectangular table.
3922 * And if the cell containing selection is at left of row-spanning cell,
3923 * it may be moved to right side of the row-spanning cell after inserting
3924 * some cell elements before it. Similarly, colspan won't be adjusted
3925 * for keeping table rectangle.
3926 * If first selection range is not in table cell element, this does nothing
3927 * but does not return error.
3928 *
3929 * @param aNumberOfCellssToInsert Number of cells to insert.
3930 * @param aInsertPosition Before or after the target cell which
3931 * contains first selection range.
3932 */
3933 MOZ_CAN_RUN_SCRIPT nsresult InsertTableCellsWithTransaction(
3934 int32_t aNumberOfCellsToInsert, InsertPosition aInsertPosition);
3935
3936 /**
3937 * InsertTableColumnsWithTransaction() inserts columns before or after
3938 * a cell element containing first selection range. I.e., if the cell
3939 * spans columns and aInsertPosition is eAfterSelectedCell, new columns
3940 * will be inserted after the right-most row which contains the cell.
3941 * If first selection range is not in table cell element, this does nothing
3942 * but does not return error.
3943 *
3944 * @param aNumberOfColumnsToInsert Number of columns to insert.
3945 * @param aInsertPosition Before or after the target cell which
3946 * contains first selection range.
3947 */
3948 MOZ_CAN_RUN_SCRIPT nsresult InsertTableColumnsWithTransaction(
3949 int32_t aNumberOfColumnsToInsert, InsertPosition aInsertPosition);
3950
3951 /**
3952 * InsertTableRowsWithTransaction() inserts <tr> elements before or after
3953 * a cell element containing first selection range. I.e., if the cell
3954 * spans rows and aInsertPosition is eAfterSelectedCell, new rows will be
3955 * inserted after the most-bottom row which contains the cell. If first
3956 * selection range is not in table cell element, this does nothing but
3957 * does not return error.
3958 *
3959 * @param aNumberOfRowsToInsert Number of rows to insert.
3960 * @param aInsertPosition Before or after the target cell which
3961 * contains first selection range.
3962 */
3963 MOZ_CAN_RUN_SCRIPT nsresult InsertTableRowsWithTransaction(
3964 int32_t aNumberOfRowsToInsert, InsertPosition aInsertPosition);
3965
3966 /**
3967 * Insert a new cell after or before supplied aCell.
3968 * Optional: If aNewCell supplied, returns the newly-created cell (addref'd,
3969 * of course)
3970 * This doesn't change or use the current selection.
3971 */
3972 MOZ_CAN_RUN_SCRIPT nsresult InsertCell(Element* aCell, int32_t aRowSpan,
3973 int32_t aColSpan, bool aAfter,
3974 bool aIsHeader, Element** aNewCell);
3975
3976 /**
3977 * DeleteSelectedTableColumnsWithTransaction() removes cell elements which
3978 * belong to same columns of selected cell elements.
3979 * If only one cell element is selected or first selection range is
3980 * in a cell, removes cell elements which belong to same column.
3981 * If 2 or more cell elements are selected, removes cell elements which
3982 * belong to any of all selected columns. In this case,
3983 * aNumberOfColumnsToDelete is ignored.
3984 * If there is no selection ranges, returns error.
3985 * If selection is not in a cell element, this does not return error,
3986 * just does nothing.
3987 * WARNING: This does not remove <col> nor <colgroup> elements.
3988 *
3989 * @param aNumberOfColumnsToDelete Number of columns to remove. This is
3990 * ignored if 2 ore more cells are
3991 * selected.
3992 */
3993 MOZ_CAN_RUN_SCRIPT nsresult
3994 DeleteSelectedTableColumnsWithTransaction(int32_t aNumberOfColumnsToDelete);
3995
3996 /**
3997 * DeleteTableColumnWithTransaction() removes cell elements which belong
3998 * to the specified column.
3999 * This method adjusts colspan attribute value if cells spanning the
4000 * column to delete.
4001 * WARNING: This does not remove <col> nor <colgroup> elements.
4002 *
4003 * @param aTableElement The <table> element which contains the
4004 * column which you want to remove.
4005 * @param aRowIndex Index of the column which you want to remove.
4006 * 0 is the first column.
4007 */
4008 MOZ_CAN_RUN_SCRIPT nsresult DeleteTableColumnWithTransaction(
4009 Element& aTableElement, int32_t aColumnIndex);
4010
4011 /**
4012 * DeleteSelectedTableRowsWithTransaction() removes <tr> elements.
4013 * If only one cell element is selected or first selection range is
4014 * in a cell, removes <tr> elements starting from a <tr> element
4015 * containing the selected cell or first selection range.
4016 * If 2 or more cell elements are selected, all <tr> elements
4017 * which contains selected cell(s). In this case, aNumberOfRowsToDelete
4018 * is ignored.
4019 * If there is no selection ranges, returns error.
4020 * If selection is not in a cell element, this does not return error,
4021 * just does nothing.
4022 *
4023 * @param aNumberOfRowsToDelete Number of rows to remove. This is ignored
4024 * if 2 or more cells are selected.
4025 */
4026 MOZ_CAN_RUN_SCRIPT nsresult
4027 DeleteSelectedTableRowsWithTransaction(int32_t aNumberOfRowsToDelete);
4028
4029 /**
4030 * DeleteTableRowWithTransaction() removes a <tr> element whose index in
4031 * the <table> is aRowIndex.
4032 * This method adjusts rowspan attribute value if the <tr> element contains
4033 * cells which spans rows.
4034 *
4035 * @param aTableElement The <table> element which contains the
4036 * <tr> element which you want to remove.
4037 * @param aRowIndex Index of the <tr> element which you want to
4038 * remove. 0 is the first row.
4039 */
4040 MOZ_CAN_RUN_SCRIPT nsresult
4041 DeleteTableRowWithTransaction(Element& aTableElement, int32_t aRowIndex);
4042
4043 /**
4044 * DeleteTableCellWithTransaction() removes table cell elements. If two or
4045 * more cell elements are selected, this removes all selected cell elements.
4046 * Otherwise, this removes some cell elements starting from selected cell
4047 * element or a cell containing first selection range. When this removes
4048 * last cell element in <tr> or <table>, this removes the <tr> or the
4049 * <table> too. Note that when removing a cell causes number of its row
4050 * becomes less than the others, this method does NOT fill the place with
4051 * rowspan nor colspan. This does not return error even if selection is not
4052 * in cell element, just does nothing.
4053 *
4054 * @param aNumberOfCellsToDelete Number of cells to remove. This is ignored
4055 * if 2 or more cells are selected.
4056 */
4057 MOZ_CAN_RUN_SCRIPT nsresult
4058 DeleteTableCellWithTransaction(int32_t aNumberOfCellsToDelete);
4059
4060 /**
4061 * DeleteAllChildrenWithTransaction() removes all children of aElement from
4062 * the tree.
4063 *
4064 * @param aElement The element whose children you want to remove.
4065 */
4066 MOZ_CAN_RUN_SCRIPT nsresult
4067 DeleteAllChildrenWithTransaction(Element& aElement);
4068
4069 /**
4070 * Move all contents from aCellToMerge into aTargetCell (append at end).
4071 */
4072 MOZ_CAN_RUN_SCRIPT nsresult MergeCells(RefPtr<Element> aTargetCell,
4073 RefPtr<Element> aCellToMerge,
4074 bool aDeleteCellToMerge);
4075
4076 /**
4077 * DeleteTableElementAndChildren() removes aTableElement (and its children)
4078 * from the DOM tree with transaction.
4079 *
4080 * @param aTableElement The <table> element which you want to remove.
4081 */
4082 MOZ_CAN_RUN_SCRIPT nsresult
4083 DeleteTableElementAndChildrenWithTransaction(Element& aTableElement);
4084
4085 MOZ_CAN_RUN_SCRIPT nsresult SetColSpan(Element* aCell, int32_t aColSpan);
4086 MOZ_CAN_RUN_SCRIPT nsresult SetRowSpan(Element* aCell, int32_t aRowSpan);
4087
4088 /**
4089 * Helper used to get nsTableWrapperFrame for a table.
4090 */
4091 static nsTableWrapperFrame* GetTableFrame(Element* aTable);
4092
4093 /**
4094 * GetNumberOfCellsInRow() returns number of actual cell elements in the row.
4095 * If some cells appear by "rowspan" in other rows, they are ignored.
4096 *
4097 * @param aTableElement The <table> element.
4098 * @param aRowIndex Valid row index in aTableElement. This method
4099 * counts cell elements in the row.
4100 * @return -1 if this meets unexpected error.
4101 * Otherwise, number of cells which this method found.
4102 */
4103 int32_t GetNumberOfCellsInRow(Element& aTableElement, int32_t aRowIndex);
4104
4105 /**
4106 * Test if all cells in row or column at given index are selected.
4107 */
4108 bool AllCellsInRowSelected(Element* aTable, int32_t aRowIndex,
4109 int32_t aNumberOfColumns);
4110 bool AllCellsInColumnSelected(Element* aTable, int32_t aColIndex,
4111 int32_t aNumberOfRows);
4112
4113 bool IsEmptyCell(Element* aCell);
4114
4115 /**
4116 * Most insert methods need to get the same basic context data.
4117 * Any of the pointers may be null if you don't need that datum (for more
4118 * efficiency).
4119 * Input: *aCell is a known cell,
4120 * if null, cell is obtained from the anchor node of the selection.
4121 * Returns NS_EDITOR_ELEMENT_NOT_FOUND if cell is not found even if aCell is
4122 * null.
4123 */
4124 MOZ_CAN_RUN_SCRIPT nsresult GetCellContext(Element** aTable, Element** aCell,
4125 nsINode** aCellParent,
4126 int32_t* aCellOffset,
4127 int32_t* aRowIndex,
4128 int32_t* aColIndex);
4129
4130 nsresult GetCellSpansAt(Element* aTable, int32_t aRowIndex, int32_t aColIndex,
4131 int32_t& aActualRowSpan, int32_t& aActualColSpan);
4132
4133 MOZ_CAN_RUN_SCRIPT nsresult SplitCellIntoColumns(
4134 Element* aTable, int32_t aRowIndex, int32_t aColIndex,
4135 int32_t aColSpanLeft, int32_t aColSpanRight, Element** aNewCell);
4136
4137 MOZ_CAN_RUN_SCRIPT nsresult SplitCellIntoRows(
4138 Element* aTable, int32_t aRowIndex, int32_t aColIndex,
4139 int32_t aRowSpanAbove, int32_t aRowSpanBelow, Element** aNewCell);
4140
4141 MOZ_CAN_RUN_SCRIPT nsresult CopyCellBackgroundColor(Element* aDestCell,
4142 Element* aSourceCell);
4143
4144 /**
4145 * Reduce rowspan/colspan when cells span into nonexistent rows/columns.
4146 */
4147 MOZ_CAN_RUN_SCRIPT nsresult FixBadRowSpan(Element* aTable, int32_t aRowIndex,
4148 int32_t& aNewRowCount);
4149 MOZ_CAN_RUN_SCRIPT nsresult FixBadColSpan(Element* aTable, int32_t aColIndex,
4150 int32_t& aNewColCount);
4151
4152 /**
4153 * XXX NormalizeTableInternal() is broken. If it meets a cell which has
4154 * bigger or smaller rowspan or colspan than actual number of cells,
4155 * this always failed to scan the table. Therefore, this does nothing
4156 * when the table should be normalized.
4157 *
4158 * @param aTableOrElementInTable An element which is in a <table> element
4159 * or <table> element itself. Otherwise,
4160 * this returns NS_OK but does nothing.
4161 */
4162 MOZ_CAN_RUN_SCRIPT nsresult
4163 NormalizeTableInternal(Element& aTableOrElementInTable);
4164
4165 /**
4166 * Fallback method: Call this after using ClearSelection() and you
4167 * failed to set selection to some other content in the document.
4168 */
4169 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetSelectionAtDocumentStart();
4170
4171 // Methods for handling plaintext quotations
4172 MOZ_CAN_RUN_SCRIPT nsresult PasteAsPlaintextQuotation(int32_t aSelectionType);
4173
4174 /**
4175 * Insert a string as quoted text, replacing the selected text (if any).
4176 * @param aQuotedText The string to insert.
4177 * @param aAddCites Whether to prepend extra ">" to each line
4178 * (usually true, unless those characters
4179 * have already been added.)
4180 * @return aNodeInserted The node spanning the insertion, if applicable.
4181 * If aAddCites is false, this will be null.
4182 */
4183 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertAsPlaintextQuotation(
4184 const nsAString& aQuotedText, bool aAddCites, nsINode** aNodeInserted);
4185
4186 /**
4187 * InsertObject() inserts given object at aPointToInsert.
4188 */
4189 MOZ_CAN_RUN_SCRIPT nsresult InsertObject(const nsACString& aType,
4190 nsISupports* aObject, bool aIsSafe,
4191 Document* aSourceDoc,
4192 const EditorDOMPoint& aPointToInsert,
4193 bool aDoDeleteSelection);
4194
4195 // factored methods for handling insertion of data from transferables
4196 // (drag&drop or clipboard)
4197 virtual nsresult PrepareTransferable(
4198 nsITransferable** aTransferable) override;
4199 nsresult PrepareHTMLTransferable(nsITransferable** aTransferable);
4200 MOZ_CAN_RUN_SCRIPT nsresult InsertFromTransferable(
4201 nsITransferable* aTransferable, Document* aSourceDoc,
4202 const nsAString& aContextStr, const nsAString& aInfoStr,
4203 bool aHavePrivateHTMLFlavor, bool aDoDeleteSelection);
4204
4205 /**
4206 * InsertFromDataTransfer() is called only when user drops data into
4207 * this editor. Don't use this method for other purposes.
4208 */
4209 MOZ_CAN_RUN_SCRIPT nsresult InsertFromDataTransfer(
4210 dom::DataTransfer* aDataTransfer, int32_t aIndex, Document* aSourceDoc,
4211 const EditorDOMPoint& aDroppedAt, bool aDoDeleteSelection);
4212
4213 bool HavePrivateHTMLFlavor(nsIClipboard* clipboard);
4214 nsresult ParseCFHTML(nsCString& aCfhtml, char16_t** aStuffToPaste,
4215 char16_t** aCfcontext);
4216
4217 nsresult StripFormattingNodes(nsIContent& aNode, bool aOnlyList = false);
4218 nsresult CreateDOMFragmentFromPaste(
4219 const nsAString& aInputString, const nsAString& aContextStr,
4220 const nsAString& aInfoStr, nsCOMPtr<nsINode>* aOutFragNode,
4221 nsCOMPtr<nsINode>* aOutStartNode, nsCOMPtr<nsINode>* aOutEndNode,
4222 int32_t* aOutStartOffset, int32_t* aOutEndOffset, bool aTrustedInput);
4223 nsresult ParseFragment(const nsAString& aStr, nsAtom* aContextLocalName,
4224 Document* aTargetDoc,
4225 dom::DocumentFragment** aFragment, bool aTrustedInput);
4226 /**
4227 * CollectTopMostChildContentsCompletelyInRange() collects topmost child
4228 * contents which are completely in the given range.
4229 * For example, if the range points a node with its container node, the
4230 * result is only the node (meaning does not include its descendants).
4231 * If the range starts start of a node and ends end of it, and if the node
4232 * does not have children, returns no nodes, otherwise, if the node has
4233 * some children, the result includes its all children (not including their
4234 * descendants).
4235 *
4236 * @param aStartPoint Start point of the range.
4237 * @param aEndPoint End point of the range.
4238 * @param aOutArrayOfContents [Out] Topmost children which are completely in
4239 * the range.
4240 */
4241 static void CollectTopMostChildNodesCompletelyInRange(
4242 const EditorRawDOMPoint& aStartPoint, const EditorRawDOMPoint& aEndPoint,
4243 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents);
4244
4245 /**
4246 * AutoHTMLFragmentBoundariesFixer fixes both edges of topmost child nodes
4247 * which are created with SubtreeContentIterator.
4248 */
4249 class MOZ_STACK_CLASS AutoHTMLFragmentBoundariesFixer final {
4250 public:
4251 /**
4252 * @param aArrayOfTopMostChildContents
4253 * [in/out] The topmost child nodes which will be
4254 * inserted into the DOM tree. Both edges, i.e.,
4255 * first node and last node in this array will be
4256 * checked whether they can be insertted into
4257 * another DOM tree. If not, it'll replaces some
4258 * orphan nodes around nodes with proper parent.
4259 */
4260 explicit AutoHTMLFragmentBoundariesFixer(
4261 nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents);
4262
4263 private:
4264 /**
4265 * EnsureBeginsOrEndsWithValidContent() replaces some nodes starting from
4266 * start or end with proper element node if it's necessary.
4267 * If first or last node of aArrayOfTopMostChildContents is in list and/or
4268 * `<table>` element, looks for topmost list element or `<table>` element
4269 * with `CollectListAndTableRelatedElementsAt()` and
4270 * `GetMostAncestorListOrTableElement()`. Then, checks whether
4271 * some nodes are in aArrayOfTopMostChildContents are the topmost list/table
4272 * element or its descendant and if so, removes the nodes from
4273 * aArrayOfTopMostChildContents and inserts the list/table element instead.
4274 * Then, aArrayOfTopMostChildContents won't start/end with list-item nor
4275 * table cells.
4276 */
4277 enum class StartOrEnd { start, end };
4278 void EnsureBeginsOrEndsWithValidContent(
4279 StartOrEnd aStartOrEnd,
4280 nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents)
4281 const;
4282
4283 /**
4284 * CollectListAndTableRelatedElementsAt() collects list elements and
4285 * table related elements from aNode (meaning aNode may be in the first of
4286 * the result) to the root element.
4287 */
4288 void CollectListAndTableRelatedElementsAt(
4289 nsIContent& aContent,
4290 nsTArray<OwningNonNull<Element>>& aOutArrayOfListAndTableElements)
4291 const;
4292
4293 /**
4294 * GetMostAncestorListOrTableElement() returns a list or a `<table>`
4295 * element which is in aArrayOfListAndTableElements and they are
4296 * actually valid ancestor of at least one of aArrayOfTopMostChildContents.
4297 */
4298 Element* GetMostAncestorListOrTableElement(
4299 const nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents,
4300 const nsTArray<OwningNonNull<Element>>&
4301 aArrayOfListAndTableRelatedElements) const;
4302
4303 /**
4304 * FindReplaceableTableElement() is a helper method of
4305 * EnsureBeginsOrEndsWithValidContent(). If aNodeMaybeInTableElement is
4306 * a descendant of aTableElement, returns aNodeMaybeInTableElement or its
4307 * nearest ancestor whose tag name is `<td>`, `<th>`, `<tr>`, `<thead>`,
4308 * `<tfoot>`, `<tbody>` or `<caption>`.
4309 *
4310 * @param aTableElement Must be a `<table>` element.
4311 * @param aContentMaybeInTableElement A node which may be in aTableElement.
4312 */
4313 Element* FindReplaceableTableElement(
4314 Element& aTableElement, nsIContent& aContentMaybeInTableElement) const;
4315
4316 /**
4317 * IsReplaceableListElement() is a helper method of
4318 * EnsureBeginsOrEndsWithValidContent(). If aNodeMaybeInListElement is a
4319 * descendant of aListElement, returns true. Otherwise, false.
4320 *
4321 * @param aListElement Must be a list element.
4322 * @param aContentMaybeInListElement A node which may be in aListElement.
4323 */
4324 bool IsReplaceableListElement(Element& aListElement,
4325 nsIContent& aContentMaybeInListElement) const;
4326 };
4327
4328 /**
4329 * GetBetterInsertionPointFor() returns better insertion point to insert
4330 * aContentToInsert.
4331 *
4332 * @param aContentToInsert The content to insert.
4333 * @param aPointToInsert A candidate point to insert the node.
4334 * @return Better insertion point if next visible node
4335 * is a <br> element and previous visible node
4336 * is neither none, another <br> element nor
4337 * different block level element.
4338 */
4339 EditorRawDOMPoint GetBetterInsertionPointFor(
4340 nsIContent& aContentToInsert, const EditorRawDOMPoint& aPointToInsert);
4341
4342 /**
4343 * MakeDefinitionListItemWithTransaction() replaces parent list of current
4344 * selection with <dl> or create new <dl> element and creates a definition
4345 * list item whose name is aTagName.
4346 *
4347 * @param aTagName Must be nsGkAtoms::dt or nsGkAtoms::dd.
4348 */
4349 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
4350 MakeDefinitionListItemWithTransaction(nsAtom& aTagName);
4351
4352 /**
4353 * FormatBlockContainerAsSubAction() inserts a block element whose name
4354 * is aTagName at selection. If selection is not collapsed and aTagName is
4355 * nsGkAtoms::normal or nsGkAtoms::_empty, this removes block containers.
4356 *
4357 * @param aTagName A block level element name. Must NOT be
4358 * nsGkAtoms::dt nor nsGkAtoms::dd.
4359 */
4360 MOZ_CAN_RUN_SCRIPT nsresult FormatBlockContainerAsSubAction(nsAtom& aTagName);
4361
4362 /**
4363 * Increase/decrease the font size of selection.
4364 */
4365 MOZ_CAN_RUN_SCRIPT nsresult RelativeFontChange(FontSize aDir);
4366
4367 MOZ_CAN_RUN_SCRIPT nsresult RelativeFontChangeOnNode(int32_t aSizeChange,
4368 nsIContent* aNode);
4369 MOZ_CAN_RUN_SCRIPT nsresult RelativeFontChangeHelper(int32_t aSizeChange,
4370 nsINode* aNode);
4371
4372 /**
4373 * Helper routines for inline style.
4374 */
4375 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertyOnTextNode(
4376 Text& aData, uint32_t aStartOffset, uint32_t aEndOffset,
4377 nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aValue);
4378
4379 nsresult PromoteInlineRange(nsRange& aRange);
4380 nsresult PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange);
4381
4382 /**
4383 * RemoveStyleInside() removes elements which represent aProperty/aAttribute
4384 * and removes CSS style. This handles aElement and all its descendants
4385 * (including leaf text nodes) recursively.
4386 */
4387 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
4388 RemoveStyleInside(Element& aElement, nsAtom* aProperty, nsAtom* aAttribute);
4389
4390 /**
4391 * CollectEditableLeafTextNodes() collects text nodes in aElement.
4392 */
4393 void CollectEditableLeafTextNodes(
4394 Element& aElement, nsTArray<OwningNonNull<Text>>& aLeafTextNodes) const;
4395
4396 /**
4397 * IsRemovableParentStyleWithNewSpanElement() checks whether
4398 * aProperty/aAttribute of parent block can be removed from aContent with
4399 * creating `<span>` element. Note that this does NOT check whether the
4400 * specified style comes from parent block or not.
4401 * XXX This may destroy the editor, but using `Result<bool, nsresult>`
4402 * is not reasonable because code for accessing the result becomes
4403 * messy. However, anybody must forget to check `Destroyed()` after
4404 * calling this. Which is the way to smart to make every caller
4405 * must check the editor state?
4406 */
4407 MOZ_CAN_RUN_SCRIPT bool IsRemovableParentStyleWithNewSpanElement(
4408 nsIContent& aContent, nsAtom* aHTMLProperty, nsAtom* aAttribute) const;
4409
4410 /**
4411 * XXX These methods seem odd and except the only caller,
4412 * `PromoteInlineRange()`, cannot use them.
4413 */
4414 bool IsStartOfContainerOrBeforeFirstEditableChild(
4415 const EditorRawDOMPoint& aPoint) const;
4416 bool IsEndOfContainerOrEqualsOrAfterLastEditableChild(
4417 const EditorRawDOMPoint& aPoint) const;
4418
4419 bool IsOnlyAttribute(const Element* aElement, nsAtom* aAttribute);
4420
4421 /**
4422 * HasStyleOrIdOrClassAttribute() returns true when at least one of
4423 * `style`, `id` or `class` attribute value of aElement is not empty.
4424 */
4425 static bool HasStyleOrIdOrClassAttribute(Element& aElement);
4426
4427 /**
4428 * Whether the outer window of the DOM event target has focus or not.
4429 */
4430 bool OurWindowHasFocus() const;
4431
4432 /**
4433 * This function is used to insert a string of HTML input optionally with some
4434 * context information into the editable field. The HTML input either comes
4435 * from a transferable object created as part of a drop/paste operation, or
4436 * from the InsertHTML method. We may want the HTML input to be sanitized
4437 * (for example, if it's coming from a transferable object), in which case
4438 * aTrustedInput should be set to false, otherwise, the caller should set it
4439 * to true, which means that the HTML will be inserted in the DOM verbatim.
4440 *
4441 * aClearStyle should be set to false if you want the paste to be affected by
4442 * local style (e.g., for the insertHTML command).
4443 */
4444 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult DoInsertHTMLWithContext(
4445 const nsAString& aInputString, const nsAString& aContextStr,
4446 const nsAString& aInfoStr, const nsAString& aFlavor, Document* aSourceDoc,
4447 const EditorDOMPoint& aPointToInsert, bool aDeleteSelection,
4448 bool aTrustedInput, bool aClearStyle = true);
4449
4450 /**
4451 * sets the position of an element; warning it does NOT check if the
4452 * element is already positioned or not and that's on purpose.
4453 * @param aElement [IN] the element
4454 * @param aX [IN] the x position in pixels.
4455 * @param aY [IN] the y position in pixels.
4456 */
4457 MOZ_CAN_RUN_SCRIPT void SetTopAndLeft(Element& aElement, int32_t aX,
4458 int32_t aY);
4459
4460 /**
4461 * Reset a selected cell or collapsed selection (the caret) after table
4462 * editing.
4463 *
4464 * @param aTable A table in the document.
4465 * @param aRow The row ...
4466 * @param aCol ... and column defining the cell where we will try to
4467 * place the caret.
4468 * @param aSelected If true, we select the whole cell instead of setting
4469 * caret.
4470 * @param aDirection If cell at (aCol, aRow) is not found, search for
4471 * previous cell in the same column (aPreviousColumn) or
4472 * row (ePreviousRow) or don't search for another cell
4473 * (aNoSearch). If no cell is found, caret is place just
4474 * before table; and if that fails, at beginning of
4475 * document. Thus we generally don't worry about the
4476 * return value and can use the
4477 * AutoSelectionSetterAfterTableEdit stack-based object to
4478 * insure we reset the caret in a table-editing method.
4479 */
4480 MOZ_CAN_RUN_SCRIPT void SetSelectionAfterTableEdit(Element* aTable,
4481 int32_t aRow, int32_t aCol,
4482 int32_t aDirection,
4483 bool aSelected);
4484
4485 void RemoveListenerAndDeleteRef(const nsAString& aEvent,
4486 nsIDOMEventListener* aListener,
4487 bool aUseCapture, ManualNACPtr aElement,
4488 PresShell* aPresShell);
4489 void DeleteRefToAnonymousNode(ManualNACPtr aContent, PresShell* aPresShell);
4490
4491 /**
4492 * RefreshEditingUI() may refresh editing UIs for current Selection, focus,
4493 * etc. If this shows or hides some UIs, it causes reflow. So, this is
4494 * not safe method.
4495 */
4496 MOZ_CAN_RUN_SCRIPT nsresult RefreshEditingUI();
4497
4498 /**
4499 * Returns the offset of an element's frame to its absolute containing block.
4500 */
4501 nsresult GetElementOrigin(Element& aElement, int32_t& aX, int32_t& aY);
4502 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetPositionAndDimensions(
4503 Element& aElement, int32_t& aX, int32_t& aY, int32_t& aW, int32_t& aH,
4504 int32_t& aBorderLeft, int32_t& aBorderTop, int32_t& aMarginLeft,
4505 int32_t& aMarginTop);
4506
4507 bool IsInObservedSubtree(nsIContent* aChild);
4508
4509 void UpdateRootElement();
4510
4511 /**
4512 * SetAllResizersPosition() moves all resizers to proper position.
4513 * If the resizers are hidden or replaced with another set of resizers
4514 * while this is running, this returns error. So, callers shouldn't
4515 * keep handling the resizers if this returns error.
4516 */
4517 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetAllResizersPosition();
4518
4519 /**
4520 * Shows active resizers around an element's frame
4521 * @param aResizedElement [IN] a DOM Element
4522 */
4523 MOZ_CAN_RUN_SCRIPT nsresult ShowResizersInternal(Element& aResizedElement);
4524
4525 /**
4526 * Hide resizers if they are visible. If this is called while there is no
4527 * visible resizers, this does not return error, but does nothing.
4528 */
4529 nsresult HideResizersInternal();
4530
4531 /**
4532 * RefreshResizersInternal() moves resizers to proper position. This does
4533 * nothing if there is no resizing target.
4534 */
4535 MOZ_CAN_RUN_SCRIPT nsresult RefreshResizersInternal();
4536
4537 ManualNACPtr CreateResizer(int16_t aLocation, nsIContent& aParentContent);
4538 MOZ_CAN_RUN_SCRIPT void SetAnonymousElementPosition(int32_t aX, int32_t aY,
4539 Element* aResizer);
4540
4541 ManualNACPtr CreateShadow(nsIContent& aParentContent,
4542 Element& aOriginalObject);
4543
4544 /**
4545 * SetShadowPosition() moves the shadow element to proper position.
4546 *
4547 * @param aShadowElement Must be mResizingShadow or mPositioningShadow.
4548 * @param aElement The element which has the shadow.
4549 * @param aElementX Left of aElement.
4550 * @param aElementY Top of aElement.
4551 */
4552 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
4553 SetShadowPosition(Element& aShadowElement, Element& aElement,
4554 int32_t aElementLeft, int32_t aElementTop);
4555
4556 ManualNACPtr CreateResizingInfo(nsIContent& aParentContent);
4557 MOZ_CAN_RUN_SCRIPT nsresult SetResizingInfoPosition(int32_t aX, int32_t aY,
4558 int32_t aW, int32_t aH);
4559
4560 enum class ResizeAt {
4561 eX,
4562 eY,
4563 eWidth,
4564 eHeight,
4565 };
4566 int32_t GetNewResizingIncrement(int32_t aX, int32_t aY, ResizeAt aResizeAt);
4567
4568 MOZ_CAN_RUN_SCRIPT nsresult StartResizing(Element* aHandle);
4569 int32_t GetNewResizingX(int32_t aX, int32_t aY);
4570 int32_t GetNewResizingY(int32_t aX, int32_t aY);
4571 int32_t GetNewResizingWidth(int32_t aX, int32_t aY);
4572 int32_t GetNewResizingHeight(int32_t aX, int32_t aY);
4573 void HideShadowAndInfo();
4574 MOZ_CAN_RUN_SCRIPT void SetFinalSize(int32_t aX, int32_t aY);
4575 void SetResizeIncrements(int32_t aX, int32_t aY, int32_t aW, int32_t aH,
4576 bool aPreserveRatio);
4577
4578 /**
4579 * HideAnonymousEditingUIs() forcibly hides all editing UIs (resizers,
4580 * inline-table-editing UI, absolute positioning UI).
4581 */
4582 void HideAnonymousEditingUIs();
4583
4584 /**
4585 * HideAnonymousEditingUIsIfUnnecessary() hides all editing UIs if some of
4586 * visible UIs are now unnecessary.
4587 */
4588 void HideAnonymousEditingUIsIfUnnecessary();
4589
4590 /**
4591 * sets the z-index of an element.
4592 * @param aElement [IN] the element
4593 * @param aZorder [IN] the z-index
4594 */
4595 MOZ_CAN_RUN_SCRIPT void SetZIndex(Element& aElement, int32_t aZorder);
4596
4597 /**
4598 * shows a grabber attached to an arbitrary element. The grabber is an image
4599 * positioned on the left hand side of the top border of the element. Draggin
4600 * and dropping it allows to change the element's absolute position in the
4601 * document. See chrome://editor/content/images/grabber.gif
4602 * @param aElement [IN] the element
4603 */
4604 MOZ_CAN_RUN_SCRIPT nsresult ShowGrabberInternal(Element& aElement);
4605
4606 /**
4607 * Setting grabber to proper position for current mAbsolutelyPositionedObject.
4608 * For example, while an element has grabber, the element may be resized
4609 * or repositioned by script or something. Then, you need to reset grabber
4610 * position with this.
4611 */
4612 MOZ_CAN_RUN_SCRIPT nsresult RefreshGrabberInternal();
4613
4614 /**
4615 * hide the grabber if it shown.
4616 */
4617 void HideGrabberInternal();
4618
4619 /**
4620 * CreateGrabberInternal() creates a grabber for moving aParentContent.
4621 * This sets mGrabber to the new grabber. If this returns true, it's
4622 * always non-nullptr. Otherwise, i.e., the grabber is hidden during
4623 * creation, this returns false.
4624 */
4625 bool CreateGrabberInternal(nsIContent& aParentContent);
4626
4627 MOZ_CAN_RUN_SCRIPT nsresult StartMoving();
4628 MOZ_CAN_RUN_SCRIPT nsresult SetFinalPosition(int32_t aX, int32_t aY);
4629 void AddPositioningOffset(int32_t& aX, int32_t& aY);
4630 void SnapToGrid(int32_t& newX, int32_t& newY);
4631 nsresult GrabberClicked();
4632 MOZ_CAN_RUN_SCRIPT nsresult EndMoving();
4633 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
4634 GetTemporaryStyleForFocusedPositionedElement(Element& aElement,
4635 nsAString& aReturn);
4636
4637 /**
4638 * Shows inline table editing UI around a <table> element which contains
4639 * aCellElement. This returns error if creating UI is hidden during this,
4640 * or detects another set of UI during this. In such case, callers
4641 * shouldn't keep handling anything for the UI.
4642 *
4643 * @param aCellElement Must be an <td> or <th> element.
4644 */
4645 MOZ_CAN_RUN_SCRIPT nsresult
4646 ShowInlineTableEditingUIInternal(Element& aCellElement);
4647
4648 /**
4649 * Hide all inline table editing UI.
4650 */
4651 void HideInlineTableEditingUIInternal();
4652
4653 /**
4654 * RefreshInlineTableEditingUIInternal() moves inline table editing UI to
4655 * proper position. This returns error if the UI is hidden or replaced
4656 * during moving.
4657 */
4658 MOZ_CAN_RUN_SCRIPT nsresult RefreshInlineTableEditingUIInternal();
4659
4660 /**
4661 * IsEmptyTextNode() returns true if aNode is a text node and does not have
4662 * any visible characters.
4663 */
IsEmptyTextNode(nsINode & aNode)4664 bool IsEmptyTextNode(nsINode& aNode) const {
4665 return aNode.IsText() && IsEmptyNode(aNode);
4666 }
4667
4668 MOZ_CAN_RUN_SCRIPT bool IsSimpleModifiableNode(nsIContent* aContent,
4669 nsAtom* aProperty,
4670 nsAtom* aAttribute,
4671 const nsAString* aValue);
4672 MOZ_CAN_RUN_SCRIPT nsresult
4673 SetInlinePropertyOnNodeImpl(nsIContent& aNode, nsAtom& aProperty,
4674 nsAtom* aAttribute, const nsAString& aValue);
4675 typedef enum { eInserted, eAppended } InsertedOrAppended;
4676 MOZ_CAN_RUN_SCRIPT void DoContentInserted(
4677 nsIContent* aChild, InsertedOrAppended aInsertedOrAppended);
4678
4679 /**
4680 * Returns an anonymous Element of type aTag,
4681 * child of aParentContent. If aIsCreatedHidden is true, the class
4682 * "hidden" is added to the created element. If aAnonClass is not
4683 * the empty string, it becomes the value of the attribute "_moz_anonclass"
4684 * @return a Element
4685 * @param aTag [IN] desired type of the element to create
4686 * @param aParentContent [IN] the parent node of the created anonymous
4687 * element
4688 * @param aAnonClass [IN] contents of the _moz_anonclass attribute
4689 * @param aIsCreatedHidden [IN] a boolean specifying if the class "hidden"
4690 * is to be added to the created anonymous
4691 * element
4692 */
4693 ManualNACPtr CreateAnonymousElement(nsAtom* aTag, nsIContent& aParentContent,
4694 const nsAString& aAnonClass,
4695 bool aIsCreatedHidden);
4696
4697 /**
4698 * Reads a blob into memory and notifies the BlobReader object when the read
4699 * operation is finished.
4700 *
4701 * @param aBlob The input blob
4702 * @param aWindow The global object under which the read should happen.
4703 * @param aBlobReader The blob reader object to be notified when finished.
4704 */
4705 static nsresult SlurpBlob(dom::Blob* aBlob, nsPIDOMWindowOuter* aWindow,
4706 BlobReader* aBlobReader);
4707
4708 /**
4709 * OnModifyDocumentInternal() is called by OnModifyDocument().
4710 */
4711 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult OnModifyDocumentInternal();
4712
4713 /**
4714 * For saving allocation cost in the constructor of
4715 * EditorBase::TopLevelEditSubActionData, we should reuse same RangeItem
4716 * instance with all top level edit sub actions.
4717 * The instance is always cleared when TopLevelEditSubActionData is
4718 * destructed and the class is stack only class so that we don't need
4719 * to (and also should not) add the RangeItem into the cycle collection.
4720 */
GetSelectedRangeItemForTopLevelEditSubAction()4721 already_AddRefed<RangeItem> GetSelectedRangeItemForTopLevelEditSubAction()
4722 const {
4723 if (!mSelectedRangeForTopLevelEditSubAction) {
4724 mSelectedRangeForTopLevelEditSubAction = new RangeItem();
4725 }
4726 return do_AddRef(mSelectedRangeForTopLevelEditSubAction);
4727 }
4728
4729 /**
4730 * For saving allocation cost in the constructor of
4731 * EditorBase::TopLevelEditSubActionData, we should reuse same nsRange
4732 * instance with all top level edit sub actions.
4733 * The instance is always cleared when TopLevelEditSubActionData is
4734 * destructed, but AbstractRange::mOwner keeps grabbing the owner document
4735 * so that we need to make it in the cycle collection.
4736 */
GetChangedRangeForTopLevelEditSubAction()4737 already_AddRefed<nsRange> GetChangedRangeForTopLevelEditSubAction() const {
4738 if (!mChangedRangeForTopLevelEditSubAction) {
4739 mChangedRangeForTopLevelEditSubAction = nsRange::Create(GetDocument());
4740 }
4741 return do_AddRef(mChangedRangeForTopLevelEditSubAction);
4742 }
4743
4744 protected:
4745 // Helper for Handle[CSS|HTML]IndentAtSelectionInternal
4746 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
4747 IndentListChild(RefPtr<Element>* aCurList, const EditorDOMPoint& aCurPoint,
4748 nsIContent& aContent);
4749
4750 RefPtr<TypeInState> mTypeInState;
4751 RefPtr<ComposerCommandsUpdater> mComposerCommandsUpdater;
4752
4753 // Used by TopLevelEditSubActionData::mSelectedRange.
4754 mutable RefPtr<RangeItem> mSelectedRangeForTopLevelEditSubAction;
4755 // Used by TopLevelEditSubActionData::mChangedRange.
4756 mutable RefPtr<nsRange> mChangedRangeForTopLevelEditSubAction;
4757
4758 RefPtr<Runnable> mPendingRootElementUpdatedRunner;
4759 RefPtr<Runnable> mPendingDocumentModifiedRunner;
4760
4761 bool mCRInParagraphCreatesParagraph;
4762
4763 bool mCSSAware;
4764 UniquePtr<CSSEditUtils> mCSSEditUtils;
4765
4766 // mSelectedCellIndex is reset by GetFirstSelectedTableCellElement(),
4767 // then, it'll be referred and incremented by
4768 // GetNextSelectedTableCellElement().
4769 mutable uint32_t mSelectedCellIndex;
4770
4771 // resizing
4772 bool mIsObjectResizingEnabled;
4773 bool mIsResizing;
4774 bool mPreserveRatio;
4775 bool mResizedObjectIsAnImage;
4776
4777 // absolute positioning
4778 bool mIsAbsolutelyPositioningEnabled;
4779 bool mResizedObjectIsAbsolutelyPositioned;
4780 bool mGrabberClicked;
4781 bool mIsMoving;
4782
4783 bool mSnapToGridEnabled;
4784
4785 // inline table editing
4786 bool mIsInlineTableEditingEnabled;
4787
4788 // resizing
4789 ManualNACPtr mTopLeftHandle;
4790 ManualNACPtr mTopHandle;
4791 ManualNACPtr mTopRightHandle;
4792 ManualNACPtr mLeftHandle;
4793 ManualNACPtr mRightHandle;
4794 ManualNACPtr mBottomLeftHandle;
4795 ManualNACPtr mBottomHandle;
4796 ManualNACPtr mBottomRightHandle;
4797
4798 RefPtr<Element> mActivatedHandle;
4799
4800 ManualNACPtr mResizingShadow;
4801 ManualNACPtr mResizingInfo;
4802
4803 RefPtr<Element> mResizedObject;
4804
4805 int32_t mOriginalX;
4806 int32_t mOriginalY;
4807
4808 int32_t mResizedObjectX;
4809 int32_t mResizedObjectY;
4810 int32_t mResizedObjectWidth;
4811 int32_t mResizedObjectHeight;
4812
4813 int32_t mResizedObjectMarginLeft;
4814 int32_t mResizedObjectMarginTop;
4815 int32_t mResizedObjectBorderLeft;
4816 int32_t mResizedObjectBorderTop;
4817
4818 int32_t mXIncrementFactor;
4819 int32_t mYIncrementFactor;
4820 int32_t mWidthIncrementFactor;
4821 int32_t mHeightIncrementFactor;
4822
4823 int8_t mInfoXIncrement;
4824 int8_t mInfoYIncrement;
4825
4826 // absolute positioning
4827 int32_t mPositionedObjectX;
4828 int32_t mPositionedObjectY;
4829 int32_t mPositionedObjectWidth;
4830 int32_t mPositionedObjectHeight;
4831
4832 int32_t mPositionedObjectMarginLeft;
4833 int32_t mPositionedObjectMarginTop;
4834 int32_t mPositionedObjectBorderLeft;
4835 int32_t mPositionedObjectBorderTop;
4836
4837 RefPtr<Element> mAbsolutelyPositionedObject;
4838 ManualNACPtr mGrabber;
4839 ManualNACPtr mPositioningShadow;
4840
4841 int32_t mGridSize;
4842
4843 // inline table editing
4844 RefPtr<Element> mInlineEditedCell;
4845
4846 ManualNACPtr mAddColumnBeforeButton;
4847 ManualNACPtr mRemoveColumnButton;
4848 ManualNACPtr mAddColumnAfterButton;
4849
4850 ManualNACPtr mAddRowBeforeButton;
4851 ManualNACPtr mRemoveRowButton;
4852 ManualNACPtr mAddRowAfterButton;
4853
4854 void AddMouseClickListener(Element* aElement);
4855 void RemoveMouseClickListener(Element* aElement);
4856
4857 bool mDisabledLinkHandling = false;
4858 bool mOldLinkHandlingEnabled = false;
4859
4860 ParagraphSeparator mDefaultParagraphSeparator;
4861
4862 friend class AlignStateAtSelection;
4863 friend class AutoSelectionSetterAfterTableEdit;
4864 friend class AutoSetTemporaryAncestorLimiter;
4865 friend class CSSEditUtils;
4866 friend class EditorBase;
4867 friend class EmptyEditableFunctor;
4868 friend class JoinNodeTransaction;
4869 friend class ListElementSelectionState;
4870 friend class ListItemElementSelectionState;
4871 friend class ParagraphStateAtSelection;
4872 friend class SlurpBlobEventListener;
4873 friend class SplitNodeTransaction;
4874 friend class TextEditor;
4875 friend class WSRunObject;
4876 friend class WSRunScanner;
4877 friend class WSScanResult;
4878 };
4879
4880 /**
4881 * ListElementSelectionState class gets which list element is selected right
4882 * now.
4883 */
4884 class MOZ_STACK_CLASS ListElementSelectionState final {
4885 public:
4886 ListElementSelectionState() = delete;
4887 ListElementSelectionState(HTMLEditor& aHTMLEditor, ErrorResult& aRv);
4888
IsOLElementSelected()4889 bool IsOLElementSelected() const { return mIsOLElementSelected; }
IsULElementSelected()4890 bool IsULElementSelected() const { return mIsULElementSelected; }
IsDLElementSelected()4891 bool IsDLElementSelected() const { return mIsDLElementSelected; }
IsNotOneTypeListElementSelected()4892 bool IsNotOneTypeListElementSelected() const {
4893 return (mIsOLElementSelected + mIsULElementSelected + mIsDLElementSelected +
4894 mIsOtherContentSelected) > 1;
4895 }
4896
4897 private:
4898 bool mIsOLElementSelected = false;
4899 bool mIsULElementSelected = false;
4900 bool mIsDLElementSelected = false;
4901 bool mIsOtherContentSelected = false;
4902 };
4903
4904 /**
4905 * ListItemElementSelectionState class gets which list item element is selected
4906 * right now.
4907 */
4908 class MOZ_STACK_CLASS ListItemElementSelectionState final {
4909 public:
4910 ListItemElementSelectionState() = delete;
4911 ListItemElementSelectionState(HTMLEditor& aHTMLEditor, ErrorResult& aRv);
4912
IsLIElementSelected()4913 bool IsLIElementSelected() const { return mIsLIElementSelected; }
IsDTElementSelected()4914 bool IsDTElementSelected() const { return mIsDTElementSelected; }
IsDDElementSelected()4915 bool IsDDElementSelected() const { return mIsDDElementSelected; }
IsNotOneTypeDefinitionListItemElementSelected()4916 bool IsNotOneTypeDefinitionListItemElementSelected() const {
4917 return (mIsDTElementSelected + mIsDDElementSelected +
4918 mIsOtherElementSelected) > 1;
4919 }
4920
4921 private:
4922 bool mIsLIElementSelected = false;
4923 bool mIsDTElementSelected = false;
4924 bool mIsDDElementSelected = false;
4925 bool mIsOtherElementSelected = false;
4926 };
4927
4928 /**
4929 * AlignStateAtSelection class gets alignment at selection.
4930 * XXX This currently returns only first alignment.
4931 */
4932 class MOZ_STACK_CLASS AlignStateAtSelection final {
4933 public:
4934 AlignStateAtSelection() = delete;
4935 MOZ_CAN_RUN_SCRIPT AlignStateAtSelection(HTMLEditor& aHTMLEditor,
4936 ErrorResult& aRv);
4937
AlignmentAtSelectionStart()4938 nsIHTMLEditor::EAlignment AlignmentAtSelectionStart() const {
4939 return mFirstAlign;
4940 }
IsSelectionRangesFound()4941 bool IsSelectionRangesFound() const { return mFoundSelectionRanges; }
4942
4943 private:
4944 nsIHTMLEditor::EAlignment mFirstAlign = nsIHTMLEditor::eLeft;
4945 bool mFoundSelectionRanges = false;
4946 };
4947
4948 /**
4949 * ParagraphStateAtSelection class gets format block types around selection.
4950 */
4951 class MOZ_STACK_CLASS ParagraphStateAtSelection final {
4952 public:
4953 ParagraphStateAtSelection() = delete;
4954 ParagraphStateAtSelection(HTMLEditor& aHTMLEditor, ErrorResult& aRv);
4955
4956 /**
4957 * GetFirstParagraphStateAtSelection() returns:
4958 * - nullptr if there is no format blocks nor inline nodes.
4959 * - nsGkAtoms::_empty if first node is not in any format block.
4960 * - a tag name of format block at first node.
4961 * XXX See the private method explanations. If selection ranges contains
4962 * non-format block first, it'll be check after its siblings. Therefore,
4963 * this may return non-first paragraph state.
4964 */
GetFirstParagraphStateAtSelection()4965 nsAtom* GetFirstParagraphStateAtSelection() const {
4966 return mFirstParagraphState;
4967 }
4968
4969 /**
4970 * If selected nodes are not in same format node nor only in no-format blocks,
4971 * this returns true.
4972 */
IsMixed()4973 bool IsMixed() const { return mIsMixed; }
4974
4975 private:
4976 using EditorType = EditorBase::EditorType;
4977
4978 /**
4979 * AppendDescendantFormatNodesAndFirstInlineNode() appends descendant
4980 * format blocks and first inline child node in aNonFormatBlockElement to
4981 * the last of the array (not inserting where aNonFormatBlockElement is,
4982 * so that the node order becomes randomly).
4983 *
4984 * @param aArrayOfContents [in/out] Found descendant format blocks
4985 * and first inline node in each non-format
4986 * block will be appended to this.
4987 * @param aNonFormatBlockElement Must be a non-format block element.
4988 */
4989 static void AppendDescendantFormatNodesAndFirstInlineNode(
4990 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
4991 dom::Element& aNonFormatBlockElement);
4992
4993 /**
4994 * CollectEditableFormatNodesInSelection() collects only editable nodes
4995 * around selection ranges (with
4996 * `HTMLEditor::CollectEditTargetNodesInExtendedSelectionRanges()`, see its
4997 * document for the detail). If it includes list, list item or table
4998 * related elements, they will be replaced their children.
4999 */
5000 static nsresult CollectEditableFormatNodesInSelection(
5001 HTMLEditor& aHTMLEditor,
5002 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents);
5003
5004 RefPtr<nsAtom> mFirstParagraphState;
5005 bool mIsMixed = false;
5006 };
5007
5008 } // namespace mozilla
5009
AsHTMLEditor()5010 mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() {
5011 return static_cast<mozilla::EditorBase*>(this)->IsHTMLEditor()
5012 ? static_cast<mozilla::HTMLEditor*>(this)
5013 : nullptr;
5014 }
5015
AsHTMLEditor()5016 const mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() const {
5017 return static_cast<const mozilla::EditorBase*>(this)->IsHTMLEditor()
5018 ? static_cast<const mozilla::HTMLEditor*>(this)
5019 : nullptr;
5020 }
5021
5022 #endif // #ifndef mozilla_HTMLEditor_h
5023