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_EditorBase_h
7 #define mozilla_EditorBase_h
8
9 #include "mozilla/intl/BidiEmbeddingLevel.h"
10 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
11 #include "mozilla/EditAction.h" // for EditAction and EditSubAction
12 #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
13 #include "mozilla/EventForwards.h" // for InputEventTargetRanges
14 #include "mozilla/Maybe.h" // for Maybe
15 #include "mozilla/OwningNonNull.h" // for OwningNonNull
16 #include "mozilla/TypeInState.h" // for PropItem, StyleCache
17 #include "mozilla/RangeBoundary.h" // for RawRangeBoundary, RangeBoundary
18 #include "mozilla/SelectionState.h" // for RangeUpdater, etc.
19 #include "mozilla/StyleSheet.h" // for StyleSheet
20 #include "mozilla/TransactionManager.h" // for TransactionManager
21 #include "mozilla/WeakPtr.h" // for WeakPtr
22 #include "mozilla/dom/DataTransfer.h" // for dom::DataTransfer
23 #include "mozilla/dom/HTMLBRElement.h" // for dom::HTMLBRElement
24 #include "mozilla/dom/Selection.h"
25 #include "mozilla/dom/Text.h"
26 #include "nsAtom.h" // for nsAtom, nsStaticAtom
27 #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr
28 #include "nsCycleCollectionParticipant.h"
29 #include "nsGkAtoms.h"
30 #include "nsIContentInlines.h" // for nsINode::IsEditable()
31 #include "nsIEditor.h" // for nsIEditor, etc.
32 #include "nsISelectionController.h" // for nsISelectionController constants
33 #include "nsISelectionListener.h" // for nsISelectionListener
34 #include "nsISupportsImpl.h" // for EditorBase::Release, etc.
35 #include "nsIWeakReferenceUtils.h" // for nsWeakPtr
36 #include "nsLiteralString.h" // for NS_LITERAL_STRING
37 #include "nsPIDOMWindow.h" // for nsPIDOMWindowInner, etc.
38 #include "nsString.h" // for nsCString
39 #include "nsTArray.h" // for nsTArray and nsAutoTArray
40 #include "nsWeakReference.h" // for nsSupportsWeakReference
41 #include "nscore.h" // for nsresult, nsAString, etc.
42
43 #include <tuple> // for std::tuple
44
45 class mozInlineSpellChecker;
46 class nsAtom;
47 class nsCaret;
48 class nsIContent;
49 class nsIDocumentEncoder;
50 class nsIDocumentStateListener;
51 class nsIEditActionListener;
52 class nsINode;
53 class nsIPrincipal;
54 class nsISupports;
55 class nsITransferable;
56 class nsITransaction;
57 class nsITransactionListener;
58 class nsIWidget;
59 class nsRange;
60
61 namespace mozilla {
62 class AlignStateAtSelection;
63 class AutoRangeArray;
64 class AutoTopLevelEditSubActionNotifier;
65 class AutoTransactionsConserveSelection;
66 class AutoUpdateViewBatch;
67 class ChangeAttributeTransaction;
68 class CompositionTransaction;
69 class CSSEditUtils;
70 class DeleteNodeTransaction;
71 class DeleteRangeTransaction;
72 class DeleteTextTransaction;
73 class EditActionResult;
74 class EditAggregateTransaction;
75 class EditorEventListener;
76 class EditTransactionBase;
77 class ErrorResult;
78 class HTMLEditor;
79 class HTMLEditUtils;
80 class IMEContentObserver;
81 class InsertNodeTransaction;
82 class InsertTextTransaction;
83 class JoinNodesTransaction;
84 class ListElementSelectionState;
85 class ListItemElementSelectionState;
86 class ParagraphStateAtSelection;
87 class PlaceholderTransaction;
88 class PresShell;
89 class ReplaceTextTransaction;
90 class SplitNodeResult;
91 class SplitNodeTransaction;
92 class TextComposition;
93 class TextEditor;
94 class TextInputListener;
95 class TextServicesDocument;
96 class TypeInState;
97 class WhiteSpaceVisibilityKeeper;
98
99 enum class SplitNodeDirection; // Declrared in HTMLEditor.h
100
101 template <typename NodeType>
102 class CreateNodeResultBase;
103 typedef CreateNodeResultBase<dom::Element> CreateElementResult;
104
105 namespace dom {
106 class AbstractRange;
107 class DataTransfer;
108 class Document;
109 class DragEvent;
110 class Element;
111 class EventTarget;
112 class HTMLBRElement;
113 } // namespace dom
114
115 namespace widget {
116 struct IMEState;
117 } // namespace widget
118
119 /**
120 * Implementation of an editor object. it will be the controller/focal point
121 * for the main editor services. i.e. the GUIManager, publishing, transaction
122 * manager, event interfaces. the idea for the event interfaces is to have them
123 * delegate the actual commands to the editor independent of the XPFE
124 * implementation.
125 */
126 class EditorBase : public nsIEditor,
127 public nsISelectionListener,
128 public nsSupportsWeakReference {
129 public:
130 /****************************************************************************
131 * NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other
132 * classes under libeditor except EditorEventListener and
133 * HTMLEditorEventListener because each public method which may fire
134 * eEditorInput event will need to instantiate new stack class for
135 * managing input type value of eEditorInput and cache some objects
136 * for smarter handling. In other words, when you add new root
137 * method to edit the DOM tree, you can make your new method public.
138 ****************************************************************************/
139
140 typedef dom::Document Document;
141 typedef dom::Element Element;
142 typedef dom::Selection Selection;
143 typedef dom::Text Text;
144
145 enum class EditorType { Text, HTML };
146
147 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
148 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor)
149
150 // nsIEditor methods
151 NS_DECL_NSIEDITOR
152
153 // nsISelectionListener method
154 NS_DECL_NSISELECTIONLISTENER
155
156 /**
157 * The default constructor. This should suffice. the setting of the
158 * interfaces is done after the construction of the editor class.
159 */
160 EditorBase();
161
IsInitialized()162 bool IsInitialized() const { return !!mDocument; }
Destroyed()163 bool Destroyed() const { return mDidPreDestroy; }
164
GetDocument()165 Document* GetDocument() const { return mDocument; }
166 nsPIDOMWindowOuter* GetWindow() const;
167 nsPIDOMWindowInner* GetInnerWindow() const;
168
169 /**
170 * MayHaveMutationEventListeners() returns true when the window may have
171 * mutation event listeners.
172 *
173 * @param aMutationEventType One or multiple of NS_EVENT_BITS_MUTATION_*.
174 * @return true if the editor is an HTMLEditor instance,
175 * and at least one of NS_EVENT_BITS_MUTATION_* is
176 * set to the window or in debug build.
177 */
178 bool MayHaveMutationEventListeners(
179 uint32_t aMutationEventType = 0xFFFFFFFF) const {
180 if (IsTextEditor()) {
181 // DOM mutation event listeners cannot catch the changes of
182 // <input type="text"> nor <textarea>.
183 return false;
184 }
185 #ifdef DEBUG
186 // On debug build, this should always return true for testing complicated
187 // path without mutation event listeners because when mutation event
188 // listeners do not touch the DOM, editor needs to run as there is no
189 // mutation event listeners.
190 return true;
191 #else // #ifdef DEBUG
192 nsPIDOMWindowInner* window = GetInnerWindow();
193 return window ? window->HasMutationListeners(aMutationEventType) : false;
194 #endif // #ifdef DEBUG #else
195 }
196
197 /**
198 * MayHaveBeforeInputEventListenersForTelemetry() returns true when the
199 * window may have or have had one or more `beforeinput` event listeners.
200 * Note that this may return false even if there is a `beforeinput`.
201 * See nsPIDOMWindowInner::HasBeforeInputEventListenersForTelemetry()'s
202 * comment for the detail.
203 */
MayHaveBeforeInputEventListenersForTelemetry()204 bool MayHaveBeforeInputEventListenersForTelemetry() const {
205 if (const nsPIDOMWindowInner* window = GetInnerWindow()) {
206 return window->HasBeforeInputEventListenersForTelemetry();
207 }
208 return false;
209 }
210
211 /**
212 * MutationObserverHasObservedNodeForTelemetry() returns true when a node in
213 * the window may have been observed by the web apps with a mutation observer
214 * (i.e., `MutationObserver.observe()` called by chrome script and addon's
215 * script does not make this returns true).
216 * Note that this may return false even if there is a node observed by
217 * a MutationObserver. See
218 * nsPIDOMWindowInner::MutationObserverHasObservedNodeForTelemetry()'s comment
219 * for the detail.
220 */
MutationObserverHasObservedNodeForTelemetry()221 bool MutationObserverHasObservedNodeForTelemetry() const {
222 if (const nsPIDOMWindowInner* window = GetInnerWindow()) {
223 return window->MutationObserverHasObservedNodeForTelemetry();
224 }
225 return false;
226 }
227
228 PresShell* GetPresShell() const;
229 nsPresContext* GetPresContext() const;
230 already_AddRefed<nsCaret> GetCaret() const;
231
232 already_AddRefed<nsIWidget> GetWidget();
233
234 nsISelectionController* GetSelectionController() const;
235
236 nsresult GetSelection(SelectionType aSelectionType,
237 Selection** aSelection) const;
238
239 Selection* GetSelection(
240 SelectionType aSelectionType = SelectionType::eNormal) const {
241 if (aSelectionType == SelectionType::eNormal &&
242 IsEditActionDataAvailable()) {
243 return &SelectionRef();
244 }
245 nsISelectionController* sc = GetSelectionController();
246 if (!sc) {
247 return nullptr;
248 }
249 Selection* selection = sc->GetSelection(ToRawSelectionType(aSelectionType));
250 return selection;
251 }
252
253 /**
254 * Fast non-refcounting editor root element accessor
255 */
GetRoot()256 Element* GetRoot() const { return mRootElement; }
257
258 /**
259 * Likewise, but gets the text control element instead of the root for
260 * plaintext editors.
261 */
262 Element* GetExposedRoot() const;
263
264 /**
265 * Set or unset TextInputListener. If setting non-nullptr when the editor
266 * already has a TextInputListener, this will crash in debug build.
267 */
268 void SetTextInputListener(TextInputListener* aTextInputListener);
269
270 /**
271 * Set or unset IMEContentObserver. If setting non-nullptr when the editor
272 * already has an IMEContentObserver, this will crash in debug build.
273 */
274 void SetIMEContentObserver(IMEContentObserver* aIMEContentObserver);
275
276 /**
277 * Returns current composition.
278 */
279 TextComposition* GetComposition() const;
280
281 /**
282 * Get preferred IME status of current widget.
283 */
284 virtual nsresult GetPreferredIMEState(widget::IMEState* aState);
285
286 /**
287 * Returns true if there is composition string and not fixed.
288 */
289 bool IsIMEComposing() const;
290
291 /**
292 * Commit composition if there is.
293 * Note that when there is a composition, this requests to commit composition
294 * to native IME. Therefore, when there is composition, this can do anything.
295 * For example, the editor instance, the widget or the process itself may
296 * be destroyed.
297 */
298 nsresult CommitComposition();
299
300 /**
301 * ToggleTextDirection() toggles text-direction of the root element.
302 *
303 * @param aPrincipal Set subject principal if it may be called by
304 * JS. If set to nullptr, will be treated as
305 * called by system.
306 */
307 MOZ_CAN_RUN_SCRIPT nsresult
308 ToggleTextDirectionAsAction(nsIPrincipal* aPrincipal = nullptr);
309
310 /**
311 * SwitchTextDirectionTo() sets the text-direction of the root element to
312 * LTR or RTL.
313 */
314 enum class TextDirection {
315 eLTR,
316 eRTL,
317 };
318 MOZ_CAN_RUN_SCRIPT void SwitchTextDirectionTo(TextDirection aTextDirection);
319
320 /**
321 * Finalizes selection and caret for the editor.
322 */
323 nsresult FinalizeSelection();
324
325 /**
326 * Returns true if selection is in an editable element and both the range
327 * start and the range end are editable. E.g., even if the selection range
328 * includes non-editable elements, returns true when one of common ancestors
329 * of the range start and the range end is editable. Otherwise, false.
330 */
331 bool IsSelectionEditable();
332
333 /**
334 * Returns number of undo or redo items.
335 */
NumberOfUndoItems()336 size_t NumberOfUndoItems() const {
337 return mTransactionManager ? mTransactionManager->NumberOfUndoItems() : 0;
338 }
NumberOfRedoItems()339 size_t NumberOfRedoItems() const {
340 return mTransactionManager ? mTransactionManager->NumberOfRedoItems() : 0;
341 }
342
343 /**
344 * Returns number of maximum undo/redo transactions.
345 */
NumberOfMaximumTransactions()346 int32_t NumberOfMaximumTransactions() const {
347 return mTransactionManager
348 ? mTransactionManager->NumberOfMaximumTransactions()
349 : 0;
350 }
351
352 /**
353 * Returns true if this editor can store transactions for undo/redo.
354 */
IsUndoRedoEnabled()355 bool IsUndoRedoEnabled() const {
356 return mTransactionManager &&
357 mTransactionManager->NumberOfMaximumTransactions();
358 }
359
360 /**
361 * Return true if it's possible to undo/redo right now.
362 */
CanUndo()363 bool CanUndo() const {
364 return IsUndoRedoEnabled() && NumberOfUndoItems() > 0;
365 }
CanRedo()366 bool CanRedo() const {
367 return IsUndoRedoEnabled() && NumberOfRedoItems() > 0;
368 }
369
370 /**
371 * Enables or disables undo/redo feature. Returns true if it succeeded,
372 * otherwise, e.g., we're undoing or redoing, returns false.
373 */
374 bool EnableUndoRedo(int32_t aMaxTransactionCount = -1) {
375 if (!mTransactionManager) {
376 mTransactionManager = new TransactionManager();
377 }
378 return mTransactionManager->EnableUndoRedo(aMaxTransactionCount);
379 }
DisableUndoRedo()380 bool DisableUndoRedo() {
381 if (!mTransactionManager) {
382 return true;
383 }
384 return mTransactionManager->DisableUndoRedo();
385 }
ClearUndoRedo()386 bool ClearUndoRedo() {
387 if (!mTransactionManager) {
388 return true;
389 }
390 return mTransactionManager->ClearUndoRedo();
391 }
392
393 /**
394 * See Document::AreClipboardCommandsUnconditionallyEnabled.
395 */
396 bool AreClipboardCommandsUnconditionallyEnabled() const;
397
398 /**
399 * IsCutCommandEnabled() returns whether cut command can be enabled or
400 * disabled. This always returns true if we're in non-chrome HTML/XHTML
401 * document. Otherwise, same as the result of `IsCopyToClipboardAllowed()`.
402 */
403 MOZ_CAN_RUN_SCRIPT bool IsCutCommandEnabled() const;
404
405 /**
406 * IsCopyCommandEnabled() returns copy command can be enabled or disabled.
407 * This always returns true if we're in non-chrome HTML/XHTML document.
408 * Otherwise, same as the result of `IsCopyToClipboardAllowed()`.
409 */
410 MOZ_CAN_RUN_SCRIPT bool IsCopyCommandEnabled() const;
411
412 /**
413 * IsCopyToClipboardAllowed() returns true if the selected content can
414 * be copied into the clipboard. This returns true when:
415 * - `Selection` is not collapsed and we're not a password editor.
416 * - `Selection` is not collapsed and we're a password editor but selection
417 * range is in unmasked range.
418 */
IsCopyToClipboardAllowed()419 bool IsCopyToClipboardAllowed() const {
420 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
421 if (NS_WARN_IF(!editActionData.CanHandle())) {
422 return false;
423 }
424 return IsCopyToClipboardAllowedInternal();
425 }
426
427 /**
428 * HandleDropEvent() is called from EditorEventListener::Drop that is handler
429 * of drop event.
430 */
431 MOZ_CAN_RUN_SCRIPT nsresult HandleDropEvent(dom::DragEvent* aDropEvent);
432
433 MOZ_CAN_RUN_SCRIPT virtual nsresult HandleKeyPressEvent(
434 WidgetKeyboardEvent* aKeyboardEvent);
435
436 virtual dom::EventTarget* GetDOMEventTarget() const = 0;
437
438 /**
439 * OnCompositionStart() is called when editor receives eCompositionStart
440 * event which should be handled in this editor.
441 */
442 nsresult OnCompositionStart(WidgetCompositionEvent& aCompositionStartEvent);
443
444 /**
445 * OnCompositionChange() is called when editor receives an eCompositioChange
446 * event which should be handled in this editor.
447 *
448 * @param aCompositionChangeEvent eCompositionChange event which should
449 * be handled in this editor.
450 */
451 MOZ_CAN_RUN_SCRIPT nsresult
452 OnCompositionChange(WidgetCompositionEvent& aCompositionChangeEvent);
453
454 /**
455 * OnCompositionEnd() is called when editor receives an eCompositionChange
456 * event and it's followed by eCompositionEnd event and after
457 * OnCompositionChange() is called.
458 */
459 MOZ_CAN_RUN_SCRIPT void OnCompositionEnd(
460 WidgetCompositionEvent& aCompositionEndEvent);
461
462 /**
463 * Similar to the setter for wrapWidth, but just sets the editor
464 * internal state without actually changing the content being edited
465 * to wrap at that column. This should only be used by callers who
466 * are sure that their content is already set up correctly.
467 */
SetWrapColumn(int32_t aWrapColumn)468 void SetWrapColumn(int32_t aWrapColumn) { mWrapColumn = aWrapColumn; }
469
470 /**
471 * Accessor methods to flags.
472 */
Flags()473 uint32_t Flags() const { return mFlags; }
474
AddFlags(uint32_t aFlags)475 MOZ_CAN_RUN_SCRIPT nsresult AddFlags(uint32_t aFlags) {
476 const uint32_t kOldFlags = Flags();
477 const uint32_t kNewFlags = (kOldFlags | aFlags);
478 if (kNewFlags == kOldFlags) {
479 return NS_OK;
480 }
481 return SetFlags(kNewFlags); // virtual call and may be expensive.
482 }
RemoveFlags(uint32_t aFlags)483 MOZ_CAN_RUN_SCRIPT nsresult RemoveFlags(uint32_t aFlags) {
484 const uint32_t kOldFlags = Flags();
485 const uint32_t kNewFlags = (kOldFlags & ~aFlags);
486 if (kNewFlags == kOldFlags) {
487 return NS_OK;
488 }
489 return SetFlags(kNewFlags); // virtual call and may be expensive.
490 }
AddAndRemoveFlags(uint32_t aAddingFlags,uint32_t aRemovingFlags)491 MOZ_CAN_RUN_SCRIPT nsresult AddAndRemoveFlags(uint32_t aAddingFlags,
492 uint32_t aRemovingFlags) {
493 MOZ_ASSERT(!(aAddingFlags & aRemovingFlags),
494 "Same flags are specified both adding and removing");
495 const uint32_t kOldFlags = Flags();
496 const uint32_t kNewFlags = ((kOldFlags | aAddingFlags) & ~aRemovingFlags);
497 if (kNewFlags == kOldFlags) {
498 return NS_OK;
499 }
500 return SetFlags(kNewFlags); // virtual call and may be expensive.
501 }
502
IsInPlaintextMode()503 bool IsInPlaintextMode() const {
504 const bool isPlaintextMode =
505 (mFlags & nsIEditor::eEditorPlaintextMask) != 0;
506 MOZ_ASSERT_IF(IsTextEditor(), isPlaintextMode);
507 return isPlaintextMode;
508 }
509
IsSingleLineEditor()510 bool IsSingleLineEditor() const {
511 const bool isSingleLineEditor =
512 (mFlags & nsIEditor::eEditorSingleLineMask) != 0;
513 MOZ_ASSERT_IF(isSingleLineEditor, IsTextEditor());
514 return isSingleLineEditor;
515 }
516
IsPasswordEditor()517 bool IsPasswordEditor() const {
518 const bool isPasswordEditor =
519 (mFlags & nsIEditor::eEditorPasswordMask) != 0;
520 MOZ_ASSERT_IF(isPasswordEditor, IsTextEditor());
521 return isPasswordEditor;
522 }
523
524 // FYI: Both IsRightToLeft() and IsLeftToRight() may return false if
525 // the editor inherits the content node's direction.
IsRightToLeft()526 bool IsRightToLeft() const {
527 return (mFlags & nsIEditor::eEditorRightToLeft) != 0;
528 }
IsLeftToRight()529 bool IsLeftToRight() const {
530 return (mFlags & nsIEditor::eEditorLeftToRight) != 0;
531 }
532
IsReadonly()533 bool IsReadonly() const {
534 return (mFlags & nsIEditor::eEditorReadonlyMask) != 0;
535 }
536
IsMailEditor()537 bool IsMailEditor() const {
538 return (mFlags & nsIEditor::eEditorMailMask) != 0;
539 }
540
IsWrapHackEnabled()541 bool IsWrapHackEnabled() const {
542 return (mFlags & nsIEditor::eEditorEnableWrapHackMask) != 0;
543 }
544
IsInteractionAllowed()545 bool IsInteractionAllowed() const {
546 const bool isInteractionAllowed =
547 (mFlags & nsIEditor::eEditorAllowInteraction) != 0;
548 MOZ_ASSERT_IF(isInteractionAllowed, IsHTMLEditor());
549 return isInteractionAllowed;
550 }
551
ShouldSkipSpellCheck()552 bool ShouldSkipSpellCheck() const {
553 return (mFlags & nsIEditor::eEditorSkipSpellCheck) != 0;
554 }
555
HasIndependentSelection()556 bool HasIndependentSelection() const {
557 MOZ_ASSERT_IF(mSelectionController, IsTextEditor());
558 return !!mSelectionController;
559 }
560
IsModifiable()561 bool IsModifiable() const { return !IsReadonly(); }
562
563 /**
564 * IsInEditSubAction() return true while the instance is handling an edit
565 * sub-action. Otherwise, false.
566 */
IsInEditSubAction()567 bool IsInEditSubAction() const { return mIsInEditSubAction; }
568
569 /**
570 * IsEmpty() checks whether the editor is empty. If editor has only padding
571 * <br> element for empty editor, returns true. If editor's root element has
572 * non-empty text nodes or other nodes like <br>, returns false.
573 */
574 virtual bool IsEmpty() const = 0;
575
576 /**
577 * SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching
578 * "input" event.
579 */
SuppressDispatchingInputEvent(bool aSuppress)580 void SuppressDispatchingInputEvent(bool aSuppress) {
581 mDispatchInputEvent = !aSuppress;
582 }
583
584 /**
585 * IsSuppressingDispatchingInputEvent() returns true if the editor stops
586 * dispatching input event. Otherwise, false.
587 */
IsSuppressingDispatchingInputEvent()588 bool IsSuppressingDispatchingInputEvent() const {
589 return !mDispatchInputEvent;
590 }
591
592 /**
593 * Returns true if markNodeDirty() has any effect. Returns false if
594 * markNodeDirty() is a no-op.
595 */
OutputsMozDirty()596 bool OutputsMozDirty() const {
597 // Return true for Composer (!IsInteractionAllowed()) or mail
598 // (IsMailEditor()), but false for webpages.
599 return !IsInteractionAllowed() || IsMailEditor();
600 }
601
602 /**
603 * Get the focused content, if we're focused. Returns null otherwise.
604 */
605 virtual nsIContent* GetFocusedContent() const;
606
607 /**
608 * Whether the aGUIEvent should be handled by this editor or not. When this
609 * returns false, The aGUIEvent shouldn't be handled on this editor,
610 * i.e., The aGUIEvent should be handled by another inner editor or ancestor
611 * elements.
612 */
613 virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const;
614
615 /**
616 * FindSelectionRoot() returns a selection root of this editor when aNode
617 * gets focus. aNode must be a content node or a document node. When the
618 * target isn't a part of this editor, returns nullptr. If this is for
619 * designMode, you should set the document node to aNode except that an
620 * element in the document has focus.
621 */
622 virtual Element* FindSelectionRoot(nsINode* aNode) const;
623
624 /**
625 * This method has to be called by EditorEventListener::Focus.
626 * All actions that have to be done when the editor is focused needs to be
627 * added here.
628 */
629 MOZ_CAN_RUN_SCRIPT void OnFocus(nsINode& aFocusEventTargetNode);
630
631 /** Resyncs spellchecking state (enabled/disabled). This should be called
632 * when anything that affects spellchecking state changes, such as the
633 * spellcheck attribute value.
634 */
635 void SyncRealTimeSpell();
636
637 /**
638 * This method re-initializes the selection and caret state that are for
639 * current editor state. When editor session is destroyed, it always reset
640 * selection state even if this has no focus. So if destroying editor,
641 * we have to call this method for focused editor to set selection state.
642 */
643 MOZ_CAN_RUN_SCRIPT void ReinitializeSelection(Element& aElement);
644
645 /**
646 * Do "cut".
647 *
648 * @param aPrincipal If you know current context is subject
649 * principal or system principal, set it.
650 * When nullptr, this checks it automatically.
651 */
652 MOZ_CAN_RUN_SCRIPT nsresult CutAsAction(nsIPrincipal* aPrincipal = nullptr);
653
654 /**
655 * CanPaste() returns true if user can paste something at current selection.
656 */
657 virtual bool CanPaste(int32_t aClipboardType) const = 0;
658
659 /**
660 * Do "undo" or "redo".
661 *
662 * @param aCount How many count of transactions should be
663 * handled.
664 * @param aPrincipal Set subject principal if it may be called by
665 * JS. If set to nullptr, will be treated as
666 * called by system.
667 */
668 MOZ_CAN_RUN_SCRIPT nsresult UndoAsAction(uint32_t aCount,
669 nsIPrincipal* aPrincipal = nullptr);
670 MOZ_CAN_RUN_SCRIPT nsresult RedoAsAction(uint32_t aCount,
671 nsIPrincipal* aPrincipal = nullptr);
672
673 /**
674 * InsertTextAsAction() inserts aStringToInsert at selection.
675 * Although this method is implementation of nsIEditor.insertText(),
676 * this treats the input is an edit action. If you'd like to insert text
677 * as part of edit action, you probably should use InsertTextAsSubAction().
678 *
679 * @param aStringToInsert The string to insert.
680 * @param aPrincipal Set subject principal if it may be called by
681 * JS. If set to nullptr, will be treated as
682 * called by system.
683 */
684 MOZ_CAN_RUN_SCRIPT nsresult InsertTextAsAction(
685 const nsAString& aStringToInsert, nsIPrincipal* aPrincipal = nullptr);
686
687 /**
688 * InsertLineBreakAsAction() is called when user inputs a line break with
689 * Enter or something. If the instance is `HTMLEditor`, this is called
690 * when Shift + Enter or "insertlinebreak" command.
691 *
692 * @param aPrincipal Set subject principal if it may be called by
693 * JS. If set to nullptr, will be treated as
694 * called by system.
695 */
696 MOZ_CAN_RUN_SCRIPT virtual nsresult InsertLineBreakAsAction(
697 nsIPrincipal* aPrincipal = nullptr) = 0;
698
699 /**
700 * CanDeleteSelection() returns true if `Selection` is not collapsed and
701 * it's allowed to be removed.
702 */
CanDeleteSelection()703 bool CanDeleteSelection() const {
704 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
705 if (NS_WARN_IF(!editActionData.CanHandle())) {
706 return false;
707 }
708 return IsModifiable() && !SelectionRef().IsCollapsed();
709 }
710
711 /**
712 * DeleteSelectionAsAction() removes selection content or content around
713 * caret with transactions. This should be used for handling it as an
714 * edit action. If you'd like to remove selection for preparing to insert
715 * something, you probably should use DeleteSelectionAsSubAction().
716 *
717 * @param aDirectionAndAmount How much range should be removed.
718 * @param aStripWrappers Whether the parent blocks should be removed
719 * when they become empty.
720 * @param aPrincipal Set subject principal if it may be called by
721 * JS. If set to nullptr, will be treated as
722 * called by system.
723 */
724 MOZ_CAN_RUN_SCRIPT nsresult
725 DeleteSelectionAsAction(nsIEditor::EDirection aDirectionAndAmount,
726 nsIEditor::EStripWrappers aStripWrappers,
727 nsIPrincipal* aPrincipal = nullptr);
728
729 enum class AllowBeforeInputEventCancelable {
730 No,
731 Yes,
732 };
733
734 /**
735 * Replace text in aReplaceRange or all text in this editor with aString and
736 * treat the change as inserting the string.
737 *
738 * @param aString The string to set.
739 * @param aReplaceRange The range to be replaced.
740 * If nullptr, all contents will be replaced.
741 * NOTE: Currently, nullptr is not allowed if
742 * the editor is an HTMLEditor.
743 * @param aAllowBeforeInputEventCancelable
744 * Whether `beforeinput` event which will be
745 * dispatched for this can be cancelable or not.
746 * @param aPrincipal Set subject principal if it may be called by
747 * JS. If set to nullptr, will be treated as
748 * called by system.
749 */
750 MOZ_CAN_RUN_SCRIPT nsresult ReplaceTextAsAction(
751 const nsAString& aString, nsRange* aReplaceRange,
752 AllowBeforeInputEventCancelable aAllowBeforeInputEventCancelable,
753 nsIPrincipal* aPrincipal = nullptr);
754
755 /**
756 * Can we paste |aTransferable| or, if |aTransferable| is null, will a call
757 * to pasteTransferable later possibly succeed if given an instance of
758 * nsITransferable then? True if the doc is modifiable, and, if
759 * |aTransfeable| is non-null, we have pasteable data in |aTransfeable|.
760 */
761 virtual bool CanPasteTransferable(nsITransferable* aTransferable) = 0;
762
763 /**
764 * PasteAsAction() pastes clipboard content to Selection. This method
765 * may dispatch ePaste event first. If its defaultPrevent() is called,
766 * this does nothing but returns NS_OK.
767 *
768 * @param aClipboardType nsIClipboard::kGlobalClipboard or
769 * nsIClipboard::kSelectionClipboard.
770 * @param aDispatchPasteEvent true if this should dispatch ePaste event
771 * before pasting. Otherwise, false.
772 * @param aPrincipal Set subject principal if it may be called by
773 * JS. If set to nullptr, will be treated as
774 * called by system.
775 */
776 MOZ_CAN_RUN_SCRIPT virtual nsresult PasteAsAction(
777 int32_t aClipboardType, bool aDispatchPasteEvent,
778 nsIPrincipal* aPrincipal = nullptr) = 0;
779
780 /**
781 * Paste aTransferable at Selection.
782 *
783 * @param aTransferable Must not be nullptr.
784 * @param aPrincipal Set subject principal if it may be called by
785 * JS. If set to nullptr, will be treated as
786 * called by system.
787 */
788 MOZ_CAN_RUN_SCRIPT virtual nsresult PasteTransferableAsAction(
789 nsITransferable* aTransferable, nsIPrincipal* aPrincipal = nullptr) = 0;
790
791 /**
792 * PasteAsQuotationAsAction() pastes content in clipboard as quotation.
793 * If the editor is TextEditor or in plaintext mode, will paste the content
794 * with appending ">" to start of each line.
795 * if the editor is HTMLEditor and is not in plaintext mode, will patste it
796 * into newly created blockquote element.
797 *
798 * @param aClipboardType nsIClipboard::kGlobalClipboard or
799 * nsIClipboard::kSelectionClipboard.
800 * @param aDispatchPasteEvent true if this should dispatch ePaste event
801 * before pasting. Otherwise, false.
802 * @param aPrincipal Set subject principal if it may be called by
803 * JS. If set to nullptr, will be treated as
804 * called by system.
805 */
806 MOZ_CAN_RUN_SCRIPT virtual nsresult PasteAsQuotationAsAction(
807 int32_t aClipboardType, bool aDispatchPasteEvent,
808 nsIPrincipal* aPrincipal = nullptr) = 0;
809
810 protected: // May be used by friends.
811 class AutoEditActionDataSetter;
812
813 /**
814 * TopLevelEditSubActionData stores temporary data while we're handling
815 * top-level edit sub-action.
816 */
817 struct MOZ_STACK_CLASS TopLevelEditSubActionData final {
818 friend class AutoEditActionDataSetter;
819
820 // If we have created a new block element, set to it.
821 RefPtr<Element> mNewBlockElement;
822
823 // Set selected range before edit. Then, RangeUpdater keep modifying
824 // the range while we're changing the DOM tree.
825 RefPtr<RangeItem> mSelectedRange;
826
827 // Computing changed range while we're handling sub actions.
828 RefPtr<nsRange> mChangedRange;
829
830 // XXX In strict speaking, mCachedInlineStyles isn't enough to cache inline
831 // styles because inline style can be specified with "style" attribute
832 // and/or CSS in <style> elements or CSS files. So, we need to look
833 // for better implementation about this.
834 // FYI: Initialization cost of AutoStyleCacheArray is expensive and it is
835 // not used by TextEditor so that we should construct it only when
836 // we're an HTMLEditor.
837 Maybe<AutoStyleCacheArray> mCachedInlineStyles;
838
839 // If we tried to delete selection, set to true.
840 bool mDidDeleteSelection;
841
842 // If we have explicitly set selection inter line, set to true.
843 // `AfterEdit()` or something shouldn't overwrite it in such case.
844 bool mDidExplicitlySetInterLine;
845
846 // If we have deleted non-collapsed range set to true, there are only 2
847 // cases for now:
848 // - non-collapsed range was selected.
849 // - selection was collapsed in a text node and a Unicode character
850 // was removed.
851 bool mDidDeleteNonCollapsedRange;
852
853 // If we have deleted parent empty blocks, set to true.
854 bool mDidDeleteEmptyParentBlocks;
855
856 // If we're a contenteditable editor, we temporarily increase edit count
857 // of the document between `BeforeEdit()` and `AfterEdit()`. I.e., if
858 // we increased the count in `BeforeEdit()`, we need to decrease it in
859 // `AfterEdit()`, however, the document may be changed to designMode or
860 // non-editable. Therefore, we need to store with this whether we need
861 // to restore it.
862 bool mRestoreContentEditableCount;
863
864 // If we explicitly normalized whitespaces around the changed range,
865 // set to true.
866 bool mDidNormalizeWhitespaces;
867
868 // Set to true by default. If somebody inserts an HTML fragment
869 // intentionally, any empty elements shouldn't be cleaned up later. In the
870 // case this is set to false.
871 // TODO: We should not do this by default. If it's necessary, each edit
872 // action handler do it by itself instead. Then, we can avoid such
873 // unnecessary DOM tree scan.
874 bool mNeedsToCleanUpEmptyElements;
875
876 /**
877 * The following methods modifies some data of this struct and
878 * `EditSubActionData` struct. Currently, these are required only
879 * by `HTMLEditor`. Therefore, for cutting the runtime cost of
880 * `TextEditor`, these methods should be called only by `HTMLEditor`.
881 * But it's fine to use these methods in `TextEditor` if necessary.
882 * If so, you need to call `DidDeleteText()` and `DidInsertText()`
883 * from `SetTextNodeWithoutTransaction()`.
884 */
885 void DidCreateElement(EditorBase& aEditorBase, Element& aNewElement);
886 void DidInsertContent(EditorBase& aEditorBase, nsIContent& aNewContent);
887 void WillDeleteContent(EditorBase& aEditorBase,
888 nsIContent& aRemovingContent);
889 void DidSplitContent(EditorBase& aEditorBase, nsIContent& aSplitContent,
890 nsIContent& aNewContent,
891 SplitNodeDirection aSplitNodeDirection);
892 void DidJoinContents(EditorBase& aEditorBase,
893 const EditorRawDOMPoint& aJoinedPoint);
894 void DidInsertText(EditorBase& aEditorBase,
895 const EditorRawDOMPoint& aInsertionBegin,
896 const EditorRawDOMPoint& aInsertionEnd);
897 void DidDeleteText(EditorBase& aEditorBase,
898 const EditorRawDOMPoint& aStartInTextNode);
899 void WillDeleteRange(EditorBase& aEditorBase,
900 const EditorRawDOMPoint& aStart,
901 const EditorRawDOMPoint& aEnd);
902
903 private:
Clearfinal904 void Clear() {
905 mDidExplicitlySetInterLine = false;
906 // We don't need to clear other members which are referred only when the
907 // editor is an HTML editor anymore. Note that if `mSelectedRange` is
908 // non-nullptr, that means that we're in `HTMLEditor`.
909 if (!mSelectedRange) {
910 return;
911 }
912 mNewBlockElement = nullptr;
913 mSelectedRange->Clear();
914 mChangedRange->Reset();
915 if (mCachedInlineStyles.isSome()) {
916 mCachedInlineStyles->Clear();
917 }
918 mDidDeleteSelection = false;
919 mDidDeleteNonCollapsedRange = false;
920 mDidDeleteEmptyParentBlocks = false;
921 mRestoreContentEditableCount = false;
922 mDidNormalizeWhitespaces = false;
923 mNeedsToCleanUpEmptyElements = true;
924 }
925
926 /**
927 * Extend mChangedRange to include `aNode`.
928 */
929 nsresult AddNodeToChangedRange(const HTMLEditor& aHTMLEditor,
930 nsINode& aNode);
931
932 /**
933 * Extend mChangedRange to include `aPoint`.
934 */
935 nsresult AddPointToChangedRange(const HTMLEditor& aHTMLEditor,
936 const EditorRawDOMPoint& aPoint);
937
938 /**
939 * Extend mChangedRange to include `aStart` and `aEnd`.
940 */
941 nsresult AddRangeToChangedRange(const HTMLEditor& aHTMLEditor,
942 const EditorRawDOMPoint& aStart,
943 const EditorRawDOMPoint& aEnd);
944
945 TopLevelEditSubActionData() = default;
946 TopLevelEditSubActionData(const TopLevelEditSubActionData& aOther) = delete;
947 };
948
949 struct MOZ_STACK_CLASS EditSubActionData final {
950 // While this is set to false, TopLevelEditSubActionData::mChangedRange
951 // shouldn't be modified since in some cases, modifying it in the setter
952 // itself may be faster. Note that we should affect this only for current
953 // edit sub action since mutation event listener may edit different range.
954 bool mAdjustChangedRangeFromListener;
955
956 private:
Clearfinal957 void Clear() { mAdjustChangedRangeFromListener = true; }
958
959 friend EditorBase;
960 };
961
962 protected: // AutoEditActionDataSetter, this shouldn't be accessed by friends.
963 /**
964 * SettingDataTransfer enum class is used to specify whether DataTransfer
965 * should be initialized with or without format. For example, when user
966 * uses Accel + Shift + V to paste text without format, DataTransfer should
967 * have only plain/text data to make web apps treat it without format.
968 */
969 enum class SettingDataTransfer {
970 eWithFormat,
971 eWithoutFormat,
972 };
973
974 /**
975 * AutoEditActionDataSetter grabs some necessary objects for handling any
976 * edit actions and store the edit action what we're handling. When this is
977 * created, its pointer is set to the mEditActionData, and this guarantees
978 * the lifetime of grabbing objects until it's destroyed.
979 */
980 class MOZ_STACK_CLASS AutoEditActionDataSetter final {
981 public:
982 // NOTE: aPrincipal will be used when we implement "beforeinput" event.
983 // It's set only when maybe we shouldn't dispatch it because of
984 // called by JS. I.e., if this is nullptr, we can always dispatch
985 // it.
986 AutoEditActionDataSetter(const EditorBase& aEditorBase,
987 EditAction aEditAction,
988 nsIPrincipal* aPrincipal = nullptr);
989 ~AutoEditActionDataSetter();
990
UpdateEditAction(EditAction aEditAction)991 void UpdateEditAction(EditAction aEditAction) {
992 MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent,
993 "It's too late to update EditAction since this may have "
994 "already dispatched a beforeinput event");
995 mEditAction = aEditAction;
996 }
997
998 /**
999 * CanHandle() or CanHandleAndHandleBeforeInput() must be called
1000 * immediately after creating the instance. If caller does not need to
1001 * handle "beforeinput" event or caller needs to set additional information
1002 * the events later, use the former. Otherwise, use the latter. If caller
1003 * uses the former, it's required to call MaybeDispatchBeforeInputEvent() by
1004 * itself.
1005 *
1006 */
CanHandle()1007 [[nodiscard]] bool CanHandle() const {
1008 #ifdef DEBUG
1009 mHasCanHandleChecked = true;
1010 #endif // #ifdefn DEBUG
1011 // Don't allow to run new edit action when an edit action caused
1012 // destroying the editor while it's being handled.
1013 if (mEditAction != EditAction::eInitializing &&
1014 mEditorWasDestroyedDuringHandlingEditAction) {
1015 NS_WARNING("Editor was destroyed during an edit action being handled");
1016 return false;
1017 }
1018 return IsDataAvailable();
1019 }
1020 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
CanHandleAndMaybeDispatchBeforeInputEvent()1021 CanHandleAndMaybeDispatchBeforeInputEvent() {
1022 if (NS_WARN_IF(!CanHandle())) {
1023 return NS_ERROR_NOT_INITIALIZED;
1024 }
1025 return MaybeDispatchBeforeInputEvent();
1026 }
1027
IsDataAvailable()1028 [[nodiscard]] bool IsDataAvailable() const {
1029 return mSelection && mEditorBase.IsInitialized();
1030 }
1031
1032 /**
1033 * MaybeDispatchBeforeInputEvent() considers whether this instance needs to
1034 * dispatch "beforeinput" event or not. Then,
1035 * mHasTriedToDispatchBeforeInputEvent is set to true.
1036 *
1037 * @param aDeleteDirectionAndAmount
1038 * If `MayEditActionDeleteAroundCollapsedSelection(
1039 * mEditAction)` returns true, this must be set.
1040 * Otherwise, don't set explicitly.
1041 * @return If this method actually dispatches "beforeinput" event
1042 * and it's canceled, returns
1043 * NS_ERROR_EDITOR_ACTION_CANCELED.
1044 */
1045 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MaybeDispatchBeforeInputEvent(
1046 nsIEditor::EDirection aDeleteDirectionAndAmount = nsIEditor::eNone);
1047
1048 /**
1049 * MarkAsBeforeInputHasBeenDispatched() should be called only when updating
1050 * the DOM occurs asynchronously from user input (e.g., inserting blob
1051 * object which is loaded asynchronously) and `beforeinput` has already
1052 * been dispatched (always should be so).
1053 */
MarkAsBeforeInputHasBeenDispatched()1054 void MarkAsBeforeInputHasBeenDispatched() {
1055 MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent());
1056 MOZ_ASSERT(mEditAction == EditAction::ePaste ||
1057 mEditAction == EditAction::ePasteAsQuotation ||
1058 mEditAction == EditAction::eDrop);
1059 mHasTriedToDispatchBeforeInputEvent = true;
1060 }
1061
1062 /**
1063 * MarkAsHandled() is called before dispatching `input` event and notifying
1064 * editor observers. After this is called, any nested edit action become
1065 * non illegal case.
1066 */
MarkAsHandled()1067 void MarkAsHandled() {
1068 MOZ_ASSERT(!mHandled);
1069 mHandled = true;
1070 }
1071
1072 /**
1073 * ShouldAlreadyHaveHandledBeforeInputEventDispatching() returns true if the
1074 * edit action requires to handle "beforeinput" event but not yet dispatched
1075 * it nor considered as not dispatched it and can dispatch it when this is
1076 * called.
1077 */
ShouldAlreadyHaveHandledBeforeInputEventDispatching()1078 bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const {
1079 return !HasTriedToDispatchBeforeInputEvent() &&
1080 NeedsBeforeInputEventHandling(mEditAction) &&
1081 IsBeforeInputEventEnabled() /* &&
1082 // If we still need to dispatch a clipboard event, we should
1083 // dispatch it first, then, we need to dispatch beforeinput
1084 // event later.
1085 !NeedsToDispatchClipboardEvent()*/
1086 ;
1087 }
1088
1089 /**
1090 * HasTriedToDispatchBeforeInputEvent() returns true if the instance's
1091 * MaybeDispatchBeforeInputEvent() has already been called.
1092 */
HasTriedToDispatchBeforeInputEvent()1093 bool HasTriedToDispatchBeforeInputEvent() const {
1094 return mHasTriedToDispatchBeforeInputEvent;
1095 }
1096
IsCanceled()1097 bool IsCanceled() const { return mBeforeInputEventCanceled; }
1098
1099 /**
1100 * Returns a `Selection` for normal selection. The lifetime is guaranteed
1101 * during alive this instance in the stack.
1102 */
SelectionRef()1103 MOZ_KNOWN_LIVE Selection& SelectionRef() const {
1104 MOZ_ASSERT(!mSelection ||
1105 (mSelection->GetType() == SelectionType::eNormal));
1106 return *mSelection;
1107 }
1108
GetPrincipal()1109 nsIPrincipal* GetPrincipal() const { return mPrincipal; }
GetEditAction()1110 EditAction GetEditAction() const { return mEditAction; }
1111
1112 template <typename PT, typename CT>
SetSpellCheckRestartPoint(const EditorDOMPointBase<PT,CT> & aPoint)1113 void SetSpellCheckRestartPoint(const EditorDOMPointBase<PT, CT>& aPoint) {
1114 MOZ_ASSERT(aPoint.IsSet());
1115 // We should store only container and offset because new content may
1116 // be inserted before referring child.
1117 // XXX Shouldn't we compare whether aPoint is before
1118 // mSpellCheckRestartPoint if it's set.
1119 mSpellCheckRestartPoint =
1120 EditorDOMPoint(aPoint.GetContainer(), aPoint.Offset());
1121 }
ClearSpellCheckRestartPoint()1122 void ClearSpellCheckRestartPoint() { mSpellCheckRestartPoint.Clear(); }
GetSpellCheckRestartPoint()1123 const EditorDOMPoint& GetSpellCheckRestartPoint() const {
1124 return mSpellCheckRestartPoint;
1125 }
1126
SetData(const nsAString & aData)1127 void SetData(const nsAString& aData) {
1128 MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent,
1129 "It's too late to set data since this may have already "
1130 "dispatched a beforeinput event");
1131 mData = aData;
1132 }
GetData()1133 const nsString& GetData() const { return mData; }
1134
1135 void SetColorData(const nsAString& aData);
1136
1137 /**
1138 * InitializeDataTransfer(DataTransfer*) sets mDataTransfer to
1139 * aDataTransfer. In this case, aDataTransfer should not be read/write
1140 * because it'll be set to InputEvent.dataTransfer and which should be
1141 * read-only.
1142 */
1143 void InitializeDataTransfer(dom::DataTransfer* aDataTransfer);
1144 /**
1145 * InitializeDataTransfer(nsITransferable*) creates new DataTransfer
1146 * instance, initializes it with aTransferable and sets mDataTransfer to
1147 * it.
1148 */
1149 void InitializeDataTransfer(nsITransferable* aTransferable);
1150 /**
1151 * InitializeDataTransfer(const nsAString&) creates new DataTransfer
1152 * instance, initializes it with aString and sets mDataTransfer to it.
1153 */
1154 void InitializeDataTransfer(const nsAString& aString);
1155 /**
1156 * InitializeDataTransferWithClipboard() creates new DataTransfer instance,
1157 * initializes it with clipboard and sets mDataTransfer to it.
1158 */
1159 void InitializeDataTransferWithClipboard(
1160 SettingDataTransfer aSettingDataTransfer, int32_t aClipboardType);
GetDataTransfer()1161 dom::DataTransfer* GetDataTransfer() const { return mDataTransfer; }
1162
1163 /**
1164 * AppendTargetRange() appends aTargetRange to target ranges. This should
1165 * be used only by edit action handlers which do not want to set target
1166 * ranges to selection ranges.
1167 */
1168 void AppendTargetRange(dom::StaticRange& aTargetRange);
1169
1170 /**
1171 * Make dispatching `beforeinput` forcibly non-cancelable.
1172 */
MakeBeforeInputEventNonCancelable()1173 void MakeBeforeInputEventNonCancelable() {
1174 mMakeBeforeInputEventNonCancelable = true;
1175 }
1176
1177 /**
1178 * NotifyOfDispatchingClipboardEvent() is called after dispatching
1179 * a clipboard event.
1180 */
NotifyOfDispatchingClipboardEvent()1181 void NotifyOfDispatchingClipboardEvent() {
1182 MOZ_ASSERT(NeedsToDispatchClipboardEvent());
1183 MOZ_ASSERT(!mHasTriedToDispatchClipboardEvent);
1184 mHasTriedToDispatchClipboardEvent = true;
1185 }
1186
Abort()1187 void Abort() { mAborted = true; }
IsAborted()1188 bool IsAborted() const { return mAborted; }
1189
OnEditorDestroy()1190 void OnEditorDestroy() {
1191 if (!mHandled && mHasTriedToDispatchBeforeInputEvent) {
1192 // Remember the editor was destroyed only when this edit action is being
1193 // handled because they are caused by mutation event listeners or
1194 // something other unexpected event listeners. In the cases, new child
1195 // edit action shouldn't been aborted.
1196 mEditorWasDestroyedDuringHandlingEditAction = true;
1197 }
1198 if (mParentData) {
1199 mParentData->OnEditorDestroy();
1200 }
1201 }
HasEditorDestroyedDuringHandlingEditAction()1202 bool HasEditorDestroyedDuringHandlingEditAction() const {
1203 return mEditorWasDestroyedDuringHandlingEditAction;
1204 }
1205
1206 void SetTopLevelEditSubAction(EditSubAction aEditSubAction,
1207 EDirection aDirection = eNone) {
1208 mTopLevelEditSubAction = aEditSubAction;
1209 TopLevelEditSubActionDataRef().Clear();
1210 switch (mTopLevelEditSubAction) {
1211 case EditSubAction::eInsertNode:
1212 case EditSubAction::eCreateNode:
1213 case EditSubAction::eSplitNode:
1214 case EditSubAction::eInsertText:
1215 case EditSubAction::eInsertTextComingFromIME:
1216 case EditSubAction::eSetTextProperty:
1217 case EditSubAction::eRemoveTextProperty:
1218 case EditSubAction::eRemoveAllTextProperties:
1219 case EditSubAction::eSetText:
1220 case EditSubAction::eInsertLineBreak:
1221 case EditSubAction::eInsertParagraphSeparator:
1222 case EditSubAction::eCreateOrChangeList:
1223 case EditSubAction::eIndent:
1224 case EditSubAction::eOutdent:
1225 case EditSubAction::eSetOrClearAlignment:
1226 case EditSubAction::eCreateOrRemoveBlock:
1227 case EditSubAction::eMergeBlockContents:
1228 case EditSubAction::eRemoveList:
1229 case EditSubAction::eCreateOrChangeDefinitionListItem:
1230 case EditSubAction::eInsertElement:
1231 case EditSubAction::eInsertQuotation:
1232 case EditSubAction::eInsertQuotedText:
1233 case EditSubAction::ePasteHTMLContent:
1234 case EditSubAction::eInsertHTMLSource:
1235 case EditSubAction::eSetPositionToAbsolute:
1236 case EditSubAction::eSetPositionToStatic:
1237 case EditSubAction::eDecreaseZIndex:
1238 case EditSubAction::eIncreaseZIndex:
1239 MOZ_ASSERT(aDirection == eNext);
1240 mDirectionOfTopLevelEditSubAction = eNext;
1241 break;
1242 case EditSubAction::eJoinNodes:
1243 case EditSubAction::eDeleteText:
1244 MOZ_ASSERT(aDirection == ePrevious);
1245 mDirectionOfTopLevelEditSubAction = ePrevious;
1246 break;
1247 case EditSubAction::eUndo:
1248 case EditSubAction::eRedo:
1249 case EditSubAction::eComputeTextToOutput:
1250 case EditSubAction::eCreatePaddingBRElementForEmptyEditor:
1251 case EditSubAction::eNone:
1252 MOZ_ASSERT(aDirection == eNone);
1253 mDirectionOfTopLevelEditSubAction = eNone;
1254 break;
1255 case EditSubAction::eReplaceHeadWithHTMLSource:
1256 // NOTE: Not used with AutoTopLevelEditSubActionNotifier.
1257 mDirectionOfTopLevelEditSubAction = eNone;
1258 break;
1259 case EditSubAction::eDeleteNode:
1260 case EditSubAction::eDeleteSelectedContent:
1261 // Unfortunately, eDeleteNode and eDeleteSelectedContent is used with
1262 // any direction. We might have specific sub-action for each
1263 // direction, but there are some points referencing
1264 // eDeleteSelectedContent so that we should keep storing direction
1265 // as-is for now.
1266 mDirectionOfTopLevelEditSubAction = aDirection;
1267 break;
1268 }
1269 }
GetTopLevelEditSubAction()1270 EditSubAction GetTopLevelEditSubAction() const {
1271 MOZ_ASSERT(IsDataAvailable());
1272 return mTopLevelEditSubAction;
1273 }
GetDirectionOfTopLevelEditSubAction()1274 EDirection GetDirectionOfTopLevelEditSubAction() const {
1275 return mDirectionOfTopLevelEditSubAction;
1276 }
1277
TopLevelEditSubActionDataRef()1278 const TopLevelEditSubActionData& TopLevelEditSubActionDataRef() const {
1279 return mParentData ? mParentData->TopLevelEditSubActionDataRef()
1280 : mTopLevelEditSubActionData;
1281 }
TopLevelEditSubActionDataRef()1282 TopLevelEditSubActionData& TopLevelEditSubActionDataRef() {
1283 return mParentData ? mParentData->TopLevelEditSubActionDataRef()
1284 : mTopLevelEditSubActionData;
1285 }
1286
EditSubActionDataRef()1287 const EditSubActionData& EditSubActionDataRef() const {
1288 return mEditSubActionData;
1289 }
EditSubActionDataRef()1290 EditSubActionData& EditSubActionDataRef() { return mEditSubActionData; }
1291
SavedSelectionRef()1292 SelectionState& SavedSelectionRef() {
1293 return mParentData ? mParentData->SavedSelectionRef() : mSavedSelection;
1294 }
SavedSelectionRef()1295 const SelectionState& SavedSelectionRef() const {
1296 return mParentData ? mParentData->SavedSelectionRef() : mSavedSelection;
1297 }
1298
RangeUpdaterRef()1299 RangeUpdater& RangeUpdaterRef() {
1300 return mParentData ? mParentData->RangeUpdaterRef() : mRangeUpdater;
1301 }
RangeUpdaterRef()1302 const RangeUpdater& RangeUpdaterRef() const {
1303 return mParentData ? mParentData->RangeUpdaterRef() : mRangeUpdater;
1304 }
1305
1306 void UpdateSelectionCache(Selection& aSelection);
1307
1308 private:
1309 bool IsBeforeInputEventEnabled() const;
1310
NeedsBeforeInputEventHandling(EditAction aEditAction)1311 static bool NeedsBeforeInputEventHandling(EditAction aEditAction) {
1312 MOZ_ASSERT(aEditAction != EditAction::eNone);
1313 switch (aEditAction) {
1314 case EditAction::eNone:
1315 // If we're not handling edit action, we don't need to handle
1316 // "beforeinput" event.
1317 case EditAction::eNotEditing:
1318 // If we're being initialized, we may need to create a padding <br>
1319 // element, but it shouldn't cause `beforeinput` event.
1320 case EditAction::eInitializing:
1321 // If raw level transaction API is used, the API user needs to handle
1322 // both "beforeinput" event and "input" event if it's necessary.
1323 case EditAction::eUnknown:
1324 // Hiding/showing password affects only layout so that we don't need
1325 // to handle beforeinput event for it.
1326 case EditAction::eHidePassword:
1327 // We don't need to dispatch "beforeinput" event before
1328 // "compositionstart".
1329 case EditAction::eStartComposition:
1330 // We don't need to let web apps know the mode change.
1331 case EditAction::eEnableOrDisableCSS:
1332 case EditAction::eEnableOrDisableAbsolutePositionEditor:
1333 case EditAction::eEnableOrDisableResizer:
1334 case EditAction::eEnableOrDisableInlineTableEditingUI:
1335 // We don't need to let contents in chrome's editor to know the size
1336 // change.
1337 case EditAction::eSetWrapWidth:
1338 // While resizing or moving element, we update only shadow, i.e.,
1339 // don't touch to the DOM in content. Therefore, we don't need to
1340 // dispatch "beforeinput" event.
1341 case EditAction::eResizingElement:
1342 case EditAction::eMovingElement:
1343 // Perhaps, we don't need to dispatch "beforeinput" event for
1344 // padding `<br>` element for empty editor because it's internal
1345 // handling and it should be occurred by another change.
1346 case EditAction::eCreatePaddingBRElementForEmptyEditor:
1347 return false;
1348 default:
1349 return true;
1350 }
1351 }
1352
NeedsToDispatchClipboardEvent()1353 bool NeedsToDispatchClipboardEvent() const {
1354 if (mHasTriedToDispatchClipboardEvent) {
1355 return false;
1356 }
1357 switch (mEditAction) {
1358 case EditAction::ePaste:
1359 case EditAction::ePasteAsQuotation:
1360 case EditAction::eCut:
1361 case EditAction::eCopy:
1362 return true;
1363 default:
1364 return false;
1365 }
1366 }
1367
1368 EditorBase& mEditorBase;
1369 RefPtr<Selection> mSelection;
1370 nsTArray<OwningNonNull<Selection>> mRetiredSelections;
1371 nsCOMPtr<nsIPrincipal> mPrincipal;
1372 // EditAction may be nested, for example, a command may be executed
1373 // from mutation event listener which is run while editor changes
1374 // the DOM tree. In such case, we need to handle edit action separately.
1375 AutoEditActionDataSetter* mParentData;
1376
1377 // Cached selection for HTMLEditor::AutoSelectionRestorer.
1378 SelectionState mSavedSelection;
1379
1380 // Utility class object for maintaining preserved ranges.
1381 RangeUpdater mRangeUpdater;
1382
1383 // The data should be set to InputEvent.data.
1384 nsString mData;
1385
1386 // The dataTransfer should be set to InputEvent.dataTransfer.
1387 RefPtr<dom::DataTransfer> mDataTransfer;
1388
1389 // They are used for result of InputEvent.getTargetRanges() of beforeinput.
1390 OwningNonNullStaticRangeArray mTargetRanges;
1391
1392 // Start point where spell checker should check from. This is used only
1393 // by TextEditor.
1394 EditorDOMPoint mSpellCheckRestartPoint;
1395
1396 // Different from mTopLevelEditSubAction, its data should be stored only
1397 // in the most ancestor AutoEditActionDataSetter instance since we don't
1398 // want to pay the copying cost and sync cost.
1399 TopLevelEditSubActionData mTopLevelEditSubActionData;
1400
1401 // Different from mTopLevelEditSubActionData, this stores temporaly data
1402 // for current edit sub action.
1403 EditSubActionData mEditSubActionData;
1404
1405 EditAction mEditAction;
1406
1407 // Different from its data, you can refer "current" AutoEditActionDataSetter
1408 // instance's mTopLevelEditSubAction member since it's copied from the
1409 // parent instance at construction and it's always cleared before this
1410 // won't be overwritten and cleared before destruction.
1411 EditSubAction mTopLevelEditSubAction;
1412
1413 EDirection mDirectionOfTopLevelEditSubAction;
1414
1415 bool mAborted;
1416
1417 // Set to true when this handles "beforeinput" event dispatching. Note
1418 // that even if "beforeinput" event shouldn't be dispatched for this,
1419 // instance, this is set to true when it's considered.
1420 bool mHasTriedToDispatchBeforeInputEvent;
1421 // Set to true if "beforeinput" event was dispatched and it's canceled.
1422 bool mBeforeInputEventCanceled;
1423 // Set to true if `beforeinput` event must not be cancelable even if
1424 // its inputType is defined as cancelable by the standards.
1425 bool mMakeBeforeInputEventNonCancelable;
1426 // Set to true when the edit action handler tries to dispatch a clipboard
1427 // event.
1428 bool mHasTriedToDispatchClipboardEvent;
1429 // The editor instance may be destroyed once temporarily if `document.write`
1430 // etc runs. In such case, we should mark this flag of being handled
1431 // edit action.
1432 bool mEditorWasDestroyedDuringHandlingEditAction;
1433 // This is set before dispatching `input` event and notifying editor
1434 // observers.
1435 bool mHandled;
1436
1437 #ifdef DEBUG
1438 mutable bool mHasCanHandleChecked = false;
1439 #endif // #ifdef DEBUG
1440
1441 AutoEditActionDataSetter() = delete;
1442 AutoEditActionDataSetter(const AutoEditActionDataSetter& aOther) = delete;
1443 };
1444
UpdateEditActionData(const nsAString & aData)1445 void UpdateEditActionData(const nsAString& aData) {
1446 mEditActionData->SetData(aData);
1447 }
1448
NotifyOfDispatchingClipboardEvent()1449 void NotifyOfDispatchingClipboardEvent() {
1450 MOZ_ASSERT(mEditActionData);
1451 mEditActionData->NotifyOfDispatchingClipboardEvent();
1452 }
1453
1454 protected: // May be called by friends.
1455 /****************************************************************************
1456 * Some friend classes are allowed to call the following protected methods.
1457 * However, those methods won't prepare caches of some objects which are
1458 * necessary for them. So, if you call them from friend classes, you need
1459 * to make sure that AutoEditActionDataSetter is created.
1460 ****************************************************************************/
1461
IsEditActionCanceled()1462 bool IsEditActionCanceled() const {
1463 MOZ_ASSERT(mEditActionData);
1464 return mEditActionData->IsCanceled();
1465 }
1466
ShouldAlreadyHaveHandledBeforeInputEventDispatching()1467 bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const {
1468 MOZ_ASSERT(mEditActionData);
1469 return mEditActionData
1470 ->ShouldAlreadyHaveHandledBeforeInputEventDispatching();
1471 }
1472
MaybeDispatchBeforeInputEvent()1473 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MaybeDispatchBeforeInputEvent() {
1474 MOZ_ASSERT(mEditActionData);
1475 return mEditActionData->MaybeDispatchBeforeInputEvent();
1476 }
1477
MarkAsBeforeInputHasBeenDispatched()1478 void MarkAsBeforeInputHasBeenDispatched() {
1479 MOZ_ASSERT(mEditActionData);
1480 return mEditActionData->MarkAsBeforeInputHasBeenDispatched();
1481 }
1482
HasTriedToDispatchBeforeInputEvent()1483 bool HasTriedToDispatchBeforeInputEvent() const {
1484 return mEditActionData &&
1485 mEditActionData->HasTriedToDispatchBeforeInputEvent();
1486 }
1487
IsEditActionDataAvailable()1488 bool IsEditActionDataAvailable() const {
1489 return mEditActionData && mEditActionData->IsDataAvailable();
1490 }
1491
IsTopLevelEditSubActionDataAvailable()1492 bool IsTopLevelEditSubActionDataAvailable() const {
1493 return mEditActionData && !!GetTopLevelEditSubAction();
1494 }
1495
IsEditActionAborted()1496 bool IsEditActionAborted() const {
1497 MOZ_ASSERT(mEditActionData);
1498 return mEditActionData->IsAborted();
1499 }
1500
1501 /**
1502 * SelectionRef() returns cached normal Selection. This is pretty faster than
1503 * EditorBase::GetSelection() if available.
1504 * Note that this never crash unless public methods ignore the result of
1505 * AutoEditActionDataSetter::CanHandle() and keep handling edit action but any
1506 * methods should stop handling edit action if it returns false.
1507 */
SelectionRef()1508 MOZ_KNOWN_LIVE Selection& SelectionRef() const {
1509 MOZ_ASSERT(mEditActionData);
1510 MOZ_ASSERT(mEditActionData->SelectionRef().GetType() ==
1511 SelectionType::eNormal);
1512 return mEditActionData->SelectionRef();
1513 }
1514
GetEditActionPrincipal()1515 nsIPrincipal* GetEditActionPrincipal() const {
1516 MOZ_ASSERT(mEditActionData);
1517 return mEditActionData->GetPrincipal();
1518 }
1519
1520 /**
1521 * GetEditAction() returns EditAction which is being handled. If some
1522 * edit actions are nested, this returns the innermost edit action.
1523 */
GetEditAction()1524 EditAction GetEditAction() const {
1525 return mEditActionData ? mEditActionData->GetEditAction()
1526 : EditAction::eNone;
1527 }
1528
1529 /**
1530 * GetInputEventData() returns inserting or inserted text value with
1531 * current edit action. The result is proper for InputEvent.data value.
1532 */
GetInputEventData()1533 const nsString& GetInputEventData() const {
1534 return mEditActionData ? mEditActionData->GetData() : VoidString();
1535 }
1536
1537 /**
1538 * GetInputEventDataTransfer() returns inserting or inserted transferable
1539 * content with current edit action. The result is proper for
1540 * InputEvent.dataTransfer value.
1541 */
GetInputEventDataTransfer()1542 dom::DataTransfer* GetInputEventDataTransfer() const {
1543 return mEditActionData ? mEditActionData->GetDataTransfer() : nullptr;
1544 }
1545
1546 /**
1547 * GetTopLevelEditSubAction() returns the top level edit sub-action.
1548 * For example, if selected content is being replaced with inserted text,
1549 * while removing selected content, the top level edit sub-action may be
1550 * EditSubAction::eDeleteSelectedContent. However, while inserting new
1551 * text, the top level edit sub-action may be EditSubAction::eInsertText.
1552 * So, this result means what we are doing right now unless you're looking
1553 * for a case which the method is called via mutation event listener or
1554 * selectionchange event listener which are fired while handling the edit
1555 * sub-action.
1556 */
GetTopLevelEditSubAction()1557 EditSubAction GetTopLevelEditSubAction() const {
1558 return mEditActionData ? mEditActionData->GetTopLevelEditSubAction()
1559 : EditSubAction::eNone;
1560 }
1561
1562 /**
1563 * GetDirectionOfTopLevelEditSubAction() returns direction which user
1564 * intended for doing the edit sub-action.
1565 */
GetDirectionOfTopLevelEditSubAction()1566 EDirection GetDirectionOfTopLevelEditSubAction() const {
1567 return mEditActionData
1568 ? mEditActionData->GetDirectionOfTopLevelEditSubAction()
1569 : eNone;
1570 }
1571
1572 /**
1573 * SavedSelection() returns reference to saved selection which are
1574 * stored by HTMLEditor::AutoSelectionRestorer.
1575 */
SavedSelectionRef()1576 SelectionState& SavedSelectionRef() {
1577 MOZ_ASSERT(IsHTMLEditor());
1578 MOZ_ASSERT(IsEditActionDataAvailable());
1579 return mEditActionData->SavedSelectionRef();
1580 }
SavedSelectionRef()1581 const SelectionState& SavedSelectionRef() const {
1582 MOZ_ASSERT(IsHTMLEditor());
1583 MOZ_ASSERT(IsEditActionDataAvailable());
1584 return mEditActionData->SavedSelectionRef();
1585 }
1586
RangeUpdaterRef()1587 RangeUpdater& RangeUpdaterRef() {
1588 MOZ_ASSERT(IsEditActionDataAvailable());
1589 return mEditActionData->RangeUpdaterRef();
1590 }
RangeUpdaterRef()1591 const RangeUpdater& RangeUpdaterRef() const {
1592 MOZ_ASSERT(IsEditActionDataAvailable());
1593 return mEditActionData->RangeUpdaterRef();
1594 }
1595
1596 template <typename PT, typename CT>
SetSpellCheckRestartPoint(const EditorDOMPointBase<PT,CT> & aPoint)1597 void SetSpellCheckRestartPoint(const EditorDOMPointBase<PT, CT>& aPoint) {
1598 MOZ_ASSERT(IsEditActionDataAvailable());
1599 return mEditActionData->SetSpellCheckRestartPoint(aPoint);
1600 }
1601
ClearSpellCheckRestartPoint()1602 void ClearSpellCheckRestartPoint() {
1603 MOZ_ASSERT(IsEditActionDataAvailable());
1604 return mEditActionData->ClearSpellCheckRestartPoint();
1605 }
1606
GetSpellCheckRestartPoint()1607 const EditorDOMPoint& GetSpellCheckRestartPoint() const {
1608 MOZ_ASSERT(IsEditActionDataAvailable());
1609 return mEditActionData->GetSpellCheckRestartPoint();
1610 }
1611
TopLevelEditSubActionDataRef()1612 const TopLevelEditSubActionData& TopLevelEditSubActionDataRef() const {
1613 MOZ_ASSERT(IsEditActionDataAvailable());
1614 return mEditActionData->TopLevelEditSubActionDataRef();
1615 }
TopLevelEditSubActionDataRef()1616 TopLevelEditSubActionData& TopLevelEditSubActionDataRef() {
1617 MOZ_ASSERT(IsEditActionDataAvailable());
1618 return mEditActionData->TopLevelEditSubActionDataRef();
1619 }
1620
EditSubActionDataRef()1621 const EditSubActionData& EditSubActionDataRef() const {
1622 MOZ_ASSERT(IsEditActionDataAvailable());
1623 return mEditActionData->EditSubActionDataRef();
1624 }
EditSubActionDataRef()1625 EditSubActionData& EditSubActionDataRef() {
1626 MOZ_ASSERT(IsEditActionDataAvailable());
1627 return mEditActionData->EditSubActionDataRef();
1628 }
1629
1630 /**
1631 * GetCompositionStartPoint() and GetCompositionEndPoint() returns start and
1632 * end point of composition string if there is. Otherwise, returns non-set
1633 * DOM point.
1634 */
1635 EditorRawDOMPoint GetCompositionStartPoint() const;
1636 EditorRawDOMPoint GetCompositionEndPoint() const;
1637
1638 /**
1639 * IsSelectionRangeContainerNotContent() returns true if one of container
1640 * of selection ranges is not a content node, i.e., a Document node.
1641 */
1642 bool IsSelectionRangeContainerNotContent() const;
1643
1644 /**
1645 * OnInputText() is called when user inputs text with keyboard or something.
1646 *
1647 * @param aStringToInsert The string to insert.
1648 */
1649 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1650 OnInputText(const nsAString& aStringToInsert);
1651
1652 /**
1653 * InsertTextAsSubAction() inserts aStringToInsert at selection. This
1654 * should be used for handling it as an edit sub-action.
1655 *
1656 * @param aStringToInsert The string to insert.
1657 * @param aSelectionHandling Specify whether selected content should be
1658 * deleted or ignored.
1659 */
1660 enum class SelectionHandling { Ignore, Delete };
1661 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertTextAsSubAction(
1662 const nsAString& aStringToInsert, SelectionHandling aSelectionHandling);
1663
1664 /**
1665 * InsertTextWithTransaction() inserts aStringToInsert to aPointToInsert or
1666 * better insertion point around it. If aPointToInsert isn't in a text node,
1667 * this method looks for the nearest point in a text node with
1668 * FindBetterInsertionPoint(). If there is no text node, this creates
1669 * new text node and put aStringToInsert to it.
1670 *
1671 * @param aDocument The document of this editor.
1672 * @param aStringToInsert The string to insert.
1673 * @param aPointToInser The point to insert aStringToInsert.
1674 * Must be valid DOM point.
1675 * @param aPointAfterInsertedString
1676 * The point after inserted aStringToInsert.
1677 * So, when this method actually inserts string,
1678 * this is set to a point in the text node.
1679 * Otherwise, this may be set to aPointToInsert.
1680 * @return When this succeeds to insert the string or
1681 * does nothing during composition, returns NS_OK.
1682 * Otherwise, an error code.
1683 */
1684 MOZ_CAN_RUN_SCRIPT virtual nsresult InsertTextWithTransaction(
1685 Document& aDocument, const nsAString& aStringToInsert,
1686 const EditorRawDOMPoint& aPointToInsert,
1687 EditorRawDOMPoint* aPointAfterInsertedString = nullptr);
1688
1689 /**
1690 * InsertTextIntoTextNodeWithTransaction() inserts aStringToInsert into
1691 * aOffset of aTextNode with transaction.
1692 *
1693 * @param aStringToInsert String to be inserted.
1694 * @param aPointToInsert The insertion point.
1695 * @param aSuppressIME true if it's not a part of IME composition.
1696 * E.g., adjusting white-spaces during composition.
1697 * false, otherwise.
1698 */
1699 MOZ_CAN_RUN_SCRIPT nsresult InsertTextIntoTextNodeWithTransaction(
1700 const nsAString& aStringToInsert,
1701 const EditorDOMPointInText& aPointToInsert, bool aSuppressIME = false);
1702
1703 /**
1704 * SetTextNodeWithoutTransaction() is optimized path to set new value to
1705 * the text node directly and without transaction. This is used when
1706 * setting `<input>.value` and `<textarea>.value`.
1707 */
1708 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1709 SetTextNodeWithoutTransaction(const nsAString& aString, Text& aTextNode);
1710
1711 /**
1712 * DeleteNodeWithTransaction() removes aContent from the DOM tree.
1713 *
1714 * @param aContent The node which will be removed form the DOM tree.
1715 */
1716 virtual MOZ_CAN_RUN_SCRIPT nsresult
1717 DeleteNodeWithTransaction(nsIContent& aContent);
1718
1719 /**
1720 * InsertNodeWithTransaction() inserts aContentToInsert before the child
1721 * specified by aPointToInsert.
1722 *
1723 * @param aContentToInsert The node to be inserted.
1724 * @param aPointToInsert The insertion point of aContentToInsert.
1725 * If this refers end of the container, the
1726 * transaction will append the node to the
1727 * container. Otherwise, will insert the node
1728 * before child node referred by this.
1729 */
1730 MOZ_CAN_RUN_SCRIPT nsresult InsertNodeWithTransaction(
1731 nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
1732
1733 /**
1734 * InsertPaddingBRElementForEmptyLastLineWithTransaction() creates a padding
1735 * <br> element with setting flags to NS_PADDING_FOR_EMPTY_LAST_LINE and
1736 * inserts it around aPointToInsert.
1737 *
1738 * @param aPointToInsert The DOM point where should be <br> node inserted
1739 * before.
1740 */
1741 [[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateElementResult
1742 InsertPaddingBRElementForEmptyLastLineWithTransaction(
1743 const EditorDOMPoint& aPointToInsert);
1744
1745 /**
1746 * CloneAttributesWithTransaction() clones all attributes from
1747 * aSourceElement to aDestElement after removing all attributes in
1748 * aDestElement.
1749 */
1750 MOZ_CAN_RUN_SCRIPT void CloneAttributesWithTransaction(
1751 Element& aDestElement, Element& aSourceElement);
1752
1753 /**
1754 * CloneAttributeWithTransaction() copies aAttribute of aSourceElement to
1755 * aDestElement. If aSourceElement doesn't have aAttribute, this removes
1756 * aAttribute from aDestElement.
1757 *
1758 * @param aAttribute Attribute name to be cloned.
1759 * @param aDestElement Element node which will be set aAttribute or
1760 * whose aAttribute will be removed.
1761 * @param aSourceElement Element node which provides the value of
1762 * aAttribute in aDestElement.
1763 */
1764 MOZ_CAN_RUN_SCRIPT nsresult CloneAttributeWithTransaction(
1765 nsAtom& aAttribute, Element& aDestElement, Element& aSourceElement);
1766
1767 /**
1768 * RemoveAttributeWithTransaction() removes aAttribute from aElement.
1769 *
1770 * @param aElement Element node which will lose aAttribute.
1771 * @param aAttribute Attribute name to be removed from aElement.
1772 */
1773 MOZ_CAN_RUN_SCRIPT nsresult
1774 RemoveAttributeWithTransaction(Element& aElement, nsAtom& aAttribute);
1775
1776 MOZ_CAN_RUN_SCRIPT virtual nsresult RemoveAttributeOrEquivalent(
1777 Element* aElement, nsAtom* aAttribute, bool aSuppressTransaction) = 0;
1778
1779 /**
1780 * SetAttributeWithTransaction() sets aAttribute of aElement to aValue.
1781 *
1782 * @param aElement Element node which will have aAttribute.
1783 * @param aAttribute Attribute name to be set.
1784 * @param aValue Attribute value be set to aAttribute.
1785 */
1786 MOZ_CAN_RUN_SCRIPT nsresult SetAttributeWithTransaction(
1787 Element& aElement, nsAtom& aAttribute, const nsAString& aValue);
1788
1789 MOZ_CAN_RUN_SCRIPT virtual nsresult SetAttributeOrEquivalent(
1790 Element* aElement, nsAtom* aAttribute, const nsAString& aValue,
1791 bool aSuppressTransaction) = 0;
1792
1793 /**
1794 * Method to replace certain CreateElementNS() calls.
1795 *
1796 * @param aTag Tag you want.
1797 */
1798 already_AddRefed<Element> CreateHTMLContent(const nsAtom* aTag);
1799
1800 /**
1801 * Creates text node which is marked as "maybe modified frequently" and
1802 * "maybe masked" if this is a password editor.
1803 */
1804 already_AddRefed<nsTextNode> CreateTextNode(const nsAString& aData);
1805
1806 /**
1807 * DoInsertText(), DoDeleteText(), DoReplaceText() and DoSetText() are
1808 * wrapper of `CharacterData::InsertData()`, `CharacterData::DeleteData()`,
1809 * `CharacterData::ReplaceData()` and `CharacterData::SetData()`.
1810 */
1811 MOZ_CAN_RUN_SCRIPT void DoInsertText(dom::Text& aText, uint32_t aOffset,
1812 const nsAString& aStringToInsert,
1813 ErrorResult& aRv);
1814 MOZ_CAN_RUN_SCRIPT void DoDeleteText(dom::Text& aText, uint32_t aOffset,
1815 uint32_t aCount, ErrorResult& aRv);
1816 MOZ_CAN_RUN_SCRIPT void DoReplaceText(dom::Text& aText, uint32_t aOffset,
1817 uint32_t aCount,
1818 const nsAString& aStringToInsert,
1819 ErrorResult& aRv);
1820 MOZ_CAN_RUN_SCRIPT void DoSetText(dom::Text& aText,
1821 const nsAString& aStringToSet,
1822 ErrorResult& aRv);
1823
1824 /**
1825 * DeleteTextWithTransaction() removes text in the range from aTextNode.
1826 *
1827 * @param aTextNode The text node which should be modified.
1828 * @param aOffset Start offset of removing text in aTextNode.
1829 * @param aLength Length of removing text.
1830 */
1831 MOZ_CAN_RUN_SCRIPT nsresult DeleteTextWithTransaction(dom::Text& aTextNode,
1832 uint32_t aOffset,
1833 uint32_t aLength);
1834
1835 /**
1836 * MarkElementDirty() sets a special dirty attribute on the element.
1837 * Usually this will be called immediately after creating a new node.
1838 *
1839 * @param aElement The element for which to insert formatting.
1840 */
1841 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MarkElementDirty(Element& aElement);
1842
1843 MOZ_CAN_RUN_SCRIPT nsresult
1844 DoTransactionInternal(nsITransaction* aTransaction);
1845
1846 /**
1847 * Returns true if aNode is our root node.
1848 */
1849 bool IsRoot(const nsINode* inNode) const;
1850 bool IsEditorRoot(const nsINode* aNode) const;
1851
1852 /**
1853 * Returns true if aNode is a descendant of our root node.
1854 */
1855 bool IsDescendantOfRoot(const nsINode* inNode) const;
1856 bool IsDescendantOfEditorRoot(const nsINode* aNode) const;
1857
1858 /**
1859 * Returns true when inserting text should be a part of current composition.
1860 */
1861 bool ShouldHandleIMEComposition() const;
1862
1863 static EditorRawDOMPoint GetStartPoint(const Selection& aSelection);
1864 static EditorRawDOMPoint GetEndPoint(const Selection& aSelection);
1865
1866 static nsresult GetEndChildNode(const Selection& aSelection,
1867 nsIContent** aEndNode);
1868
1869 /**
1870 * CollapseSelectionToEnd() collapses the selection to the end of the editor.
1871 */
1872 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult CollapseSelectionToEnd() const;
1873
1874 /**
1875 * AllowsTransactionsToChangeSelection() returns true if editor allows any
1876 * transactions to change Selection. Otherwise, transactions shouldn't
1877 * change Selection.
1878 */
AllowsTransactionsToChangeSelection()1879 inline bool AllowsTransactionsToChangeSelection() const {
1880 return mAllowsTransactionsToChangeSelection;
1881 }
1882
1883 /**
1884 * MakeThisAllowTransactionsToChangeSelection() with true makes this editor
1885 * allow transactions to change Selection. Otherwise, i.e., with false,
1886 * makes this editor not allow transactions to change Selection.
1887 */
MakeThisAllowTransactionsToChangeSelection(bool aAllow)1888 inline void MakeThisAllowTransactionsToChangeSelection(bool aAllow) {
1889 mAllowsTransactionsToChangeSelection = aAllow;
1890 }
1891
1892 nsresult HandleInlineSpellCheck(
1893 const EditorDOMPoint& aPreviouslySelectedStart,
1894 const dom::AbstractRange* aRange = nullptr);
1895
1896 /**
1897 * Likewise, but gets the editor's root instead, which is different for HTML
1898 * editors.
1899 */
1900 virtual Element* GetEditorRoot() const;
1901
1902 /**
1903 * Whether the editor is active on the DOM window. Note that when this
1904 * returns true but GetFocusedContent() returns null, it means that this
1905 * editor was focused when the DOM window was active.
1906 */
1907 virtual bool IsActiveInDOMWindow() const;
1908
1909 /**
1910 * FindBetterInsertionPoint() tries to look for better insertion point which
1911 * is typically the nearest text node and offset in it.
1912 *
1913 * @param aPoint Insertion point which the callers found.
1914 * @return Better insertion point if there is. If not returns
1915 * same point as aPoint.
1916 */
1917 EditorRawDOMPoint FindBetterInsertionPoint(
1918 const EditorRawDOMPoint& aPoint) const;
1919
1920 /**
1921 * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
1922 * with nsCaret::RemoveForceHide(). This does NOT set visibility of
1923 * nsCaret. Therefore, this is stateless.
1924 */
1925 void HideCaret(bool aHide);
1926
1927 protected: // Edit sub-action handler
1928 /**
1929 * AutoCaretBidiLevelManager() computes bidi level of caret, deleting
1930 * character(s) from aPointAtCaret at construction. Then, if you'll
1931 * need to extend the selection, you should calls `UpdateCaretBidiLevel()`,
1932 * then, this class may update caret bidi level for you if it's required.
1933 */
1934 class MOZ_RAII AutoCaretBidiLevelManager final {
1935 public:
1936 /**
1937 * @param aEditorBase The editor.
1938 * @param aPointAtCaret Collapsed `Selection` point.
1939 * @param aDirectionAndAmount The direction and amount to delete.
1940 */
1941 template <typename PT, typename CT>
1942 AutoCaretBidiLevelManager(const EditorBase& aEditorBase,
1943 nsIEditor::EDirection aDirectionAndAmount,
1944 const EditorDOMPointBase<PT, CT>& aPointAtCaret);
1945
1946 /**
1947 * Failed() returns true if the constructor failed to handle the bidi
1948 * information.
1949 */
Failed()1950 bool Failed() const { return mFailed; }
1951
1952 /**
1953 * Canceled() returns true if when the caller should stop deleting
1954 * characters since caret position is not visually adjacent the deleting
1955 * characters and user does not wand to delete them in that case.
1956 */
Canceled()1957 bool Canceled() const { return mCanceled; }
1958
1959 /**
1960 * MaybeUpdateCaretBidiLevel() may update caret bidi level and schedule to
1961 * paint it if they are necessary.
1962 */
1963 void MaybeUpdateCaretBidiLevel(const EditorBase& aEditorBase) const;
1964
1965 private:
1966 Maybe<mozilla::intl::BidiEmbeddingLevel> mNewCaretBidiLevel;
1967 bool mFailed = false;
1968 bool mCanceled = false;
1969 };
1970
1971 /**
1972 * UndefineCaretBidiLevel() resets bidi level of the caret.
1973 */
1974 void UndefineCaretBidiLevel() const;
1975
1976 /**
1977 * Flushing pending notifications if nsFrameSelection requires the latest
1978 * layout information to compute deletion range. This may destroy the
1979 * editor instance itself. When this returns false, don't keep doing
1980 * anything.
1981 */
1982 [[nodiscard]] MOZ_CAN_RUN_SCRIPT bool
1983 FlushPendingNotificationsIfToHandleDeletionWithFrameSelection(
1984 nsIEditor::EDirection aDirectionAndAmount) const;
1985
1986 /**
1987 * DeleteSelectionAsSubAction() removes selection content or content around
1988 * caret with transactions. This should be used for handling it as an
1989 * edit sub-action.
1990 *
1991 * @param aDirectionAndAmount How much range should be removed.
1992 * @param aStripWrappers Whether the parent blocks should be removed
1993 * when they become empty. If this instance is
1994 * a TextEditor, Must be nsIEditor::eNoStrip.
1995 */
1996 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
1997 DeleteSelectionAsSubAction(nsIEditor::EDirection aDirectionAndAmount,
1998 nsIEditor::EStripWrappers aStripWrappers);
1999
2000 /**
2001 * This method handles "delete selection" commands.
2002 * NOTE: Don't call this method recursively from the helper methods since
2003 * when nobody handled it without canceling and returing an error,
2004 * this falls it back to `DeleteSelectionWithTransaction()`.
2005 *
2006 * @param aDirectionAndAmount Direction of the deletion.
2007 * @param aStripWrappers Must be nsIEditor::eNoStrip if this is a
2008 * TextEditor instance. Otherwise,
2009 * nsIEditor::eStrip is also valid.
2010 */
2011 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual EditActionResult
2012 HandleDeleteSelection(nsIEditor::EDirection aDirectionAndAmount,
2013 nsIEditor::EStripWrappers aStripWrappers) = 0;
2014
2015 /**
2016 * ReplaceSelectionAsSubAction() replaces selection with aString.
2017 *
2018 * @param aString The string to replace.
2019 */
2020 MOZ_CAN_RUN_SCRIPT nsresult
2021 ReplaceSelectionAsSubAction(const nsAString& aString);
2022
2023 /**
2024 * HandleInsertText() handles inserting text at selection.
2025 *
2026 * @param aEditSubAction Must be EditSubAction::eInsertText or
2027 * EditSubAction::eInsertTextComingFromIME.
2028 * @param aInsertionString String to be inserted at selection.
2029 * @param aSelectionHandling Specify whether selected content should be
2030 * deleted or ignored.
2031 */
2032 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual EditActionResult HandleInsertText(
2033 EditSubAction aEditSubAction, const nsAString& aInsertionString,
2034 SelectionHandling aSelectionHandling) = 0;
2035
2036 /**
2037 * InsertWithQuotationsAsSubAction() inserts aQuotedText with appending ">"
2038 * to start of every line.
2039 *
2040 * @param aQuotedText String to insert. This will be quoted by ">"
2041 * automatically.
2042 */
2043 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult
2044 InsertWithQuotationsAsSubAction(const nsAString& aQuotedText) = 0;
2045
2046 /**
2047 * PrepareInsertContent() is a helper method of InsertTextAt(),
2048 * HTMLEditor::HTMLWithContextInserter::Run(). They insert content coming
2049 * from clipboard or drag and drop. Before that, they may need to remove
2050 * selected contents and adjust selection. This does them instead.
2051 *
2052 * @param aPointToInsert Point to insert. Must be set. Callers
2053 * shouldn't use this instance after calling this
2054 * method because this method may cause changing
2055 * the DOM tree and Selection.
2056 * @param aDoDeleteSelection true if selected content should be removed.
2057 */
2058 MOZ_CAN_RUN_SCRIPT nsresult PrepareToInsertContent(
2059 const EditorDOMPoint& aPointToInsert, bool aDoDeleteSelection);
2060
2061 /**
2062 * InsertTextAt() inserts aStringToInsert at aPointToInsert.
2063 *
2064 * @param aStringToInsert The string which you want to insert.
2065 * @param aPointToInsert The insertion point.
2066 * @param aDoDeleteSelection true if you want this to delete selected
2067 * content. Otherwise, false.
2068 */
2069 MOZ_CAN_RUN_SCRIPT nsresult InsertTextAt(const nsAString& aStringToInsert,
2070 const EditorDOMPoint& aPointToInsert,
2071 bool aDoDeleteSelection);
2072
2073 /**
2074 * Return true if the data is safe to insert as the source and destination
2075 * principals match, or we are in a editor context where this doesn't matter.
2076 * Otherwise, the data must be sanitized first.
2077 */
2078 bool IsSafeToInsertData(nsIPrincipal* aSourcePrincipal) const;
2079
2080 protected: // Called by helper classes.
2081 /**
2082 * OnStartToHandleTopLevelEditSubAction() is called when
2083 * GetTopLevelEditSubAction() is EditSubAction::eNone and somebody starts to
2084 * handle aEditSubAction.
2085 *
2086 * @param aTopLevelEditSubAction Top level edit sub action which
2087 * will be handled soon.
2088 * @param aDirectionOfTopLevelEditSubAction Direction of aEditSubAction.
2089 */
2090 MOZ_CAN_RUN_SCRIPT virtual void OnStartToHandleTopLevelEditSubAction(
2091 EditSubAction aTopLevelEditSubAction,
2092 nsIEditor::EDirection aDirectionOfTopLevelEditSubAction,
2093 ErrorResult& aRv);
2094
2095 /**
2096 * OnEndHandlingTopLevelEditSubAction() is called after
2097 * SetTopLevelEditSubAction() is handled.
2098 */
2099 MOZ_CAN_RUN_SCRIPT virtual nsresult OnEndHandlingTopLevelEditSubAction();
2100
2101 /**
2102 * OnStartToHandleEditSubAction() and OnEndHandlingEditSubAction() are called
2103 * when starting to handle an edit sub action and ending handling an edit
2104 * sub action.
2105 */
OnStartToHandleEditSubAction()2106 void OnStartToHandleEditSubAction() { EditSubActionDataRef().Clear(); }
OnEndHandlingEditSubAction()2107 void OnEndHandlingEditSubAction() { EditSubActionDataRef().Clear(); }
2108
2109 /**
2110 * (Begin|End)PlaceholderTransaction() are called by AutoPlaceholderBatch.
2111 * This set of methods are similar to the (Begin|End)Transaction(), but do
2112 * not use the transaction managers batching feature. Instead we use a
2113 * placeholder transaction to wrap up any further transaction while the
2114 * batch is open. The advantage of this is that placeholder transactions
2115 * can later merge, if needed. Merging is unavailable between transaction
2116 * manager batches.
2117 */
2118 MOZ_CAN_RUN_SCRIPT_BOUNDARY void BeginPlaceholderTransaction(
2119 nsStaticAtom& aTransactionName, const char* aRequesterFuncName);
2120 enum class ScrollSelectionIntoView { No, Yes };
2121 MOZ_CAN_RUN_SCRIPT_BOUNDARY void EndPlaceholderTransaction(
2122 ScrollSelectionIntoView aScrollSelectionIntoView,
2123 const char* aRequesterFuncName);
2124
2125 void BeginUpdateViewBatch(const char* aRequesterFuncName);
2126 MOZ_CAN_RUN_SCRIPT void EndUpdateViewBatch(const char* aRequesterFuncName);
2127
2128 /**
2129 * Used by HTMLEditor::AutoTransactionBatch, nsIEditor::BeginTransaction
2130 * and nsIEditor::EndTransation. After calling BeginTransactionInternal(),
2131 * all transactions will be treated as an atomic transaction. I.e., two or
2132 * more transactions are undid once.
2133 * XXX What's the difference with PlaceholderTransaction? Should we always
2134 * use it instead?
2135 */
2136 MOZ_CAN_RUN_SCRIPT void BeginTransactionInternal(
2137 const char* aRequesterFuncName);
2138 MOZ_CAN_RUN_SCRIPT void EndTransactionInternal(
2139 const char* aRequesterFuncName);
2140
2141 protected: // Shouldn't be used by friend classes
2142 /**
2143 * The default destructor. This should suffice. Should this be pure virtual
2144 * for someone to derive from the EditorBase later? I don't believe so.
2145 */
2146 virtual ~EditorBase();
2147
2148 /**
2149 * @param aDocument The dom document interface being observed
2150 * @param aRootElement
2151 * This is the root of the editable section of this
2152 * document. If it is null then we get root from document
2153 * body.
2154 * @param aSelectionController
2155 * The selection controller of selections which will be
2156 * used in this editor.
2157 * @param aFlags Some of nsIEditor::eEditor*Mask flags.
2158 */
2159 MOZ_CAN_RUN_SCRIPT nsresult
2160 InitInternal(Document& aDocument, Element* aRootElement,
2161 nsISelectionController& aSelectionController, uint32_t aFlags);
2162
2163 /**
2164 * PostCreateInternal() should be called after InitInternal(), and is the time
2165 * that the editor tells its documentStateObservers that the document has been
2166 * created.
2167 */
2168 MOZ_CAN_RUN_SCRIPT nsresult PostCreateInternal();
2169
2170 /**
2171 * PreDestroyInternal() is called before the editor goes away, and gives the
2172 * editor a chance to tell its documentStateObservers that the document is
2173 * going away.
2174 */
2175 MOZ_CAN_RUN_SCRIPT virtual void PreDestroyInternal();
2176
GetEditorType()2177 MOZ_ALWAYS_INLINE EditorType GetEditorType() const {
2178 return mIsHTMLEditorClass ? EditorType::HTML : EditorType::Text;
2179 }
2180
2181 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult EnsureEmptyTextFirstChild();
2182
2183 /**
2184 * InitEditorContentAndSelection() may insert a padding `<br>` element for
2185 * if it's required in the anonymous `<div>` element or `<body>` element and
2186 * collapse selection at the end if there is no selection ranges.
2187 */
2188 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InitEditorContentAndSelection();
2189
WrapWidth()2190 int32_t WrapWidth() const { return mWrapColumn; }
2191
2192 /**
2193 * ToGenericNSResult() computes proper nsresult value for the editor users.
2194 * This should be used only when public methods return result of internal
2195 * methods.
2196 */
ToGenericNSResult(nsresult aRv)2197 static inline nsresult ToGenericNSResult(nsresult aRv) {
2198 switch (aRv) {
2199 // If the editor is destroyed while handling an edit action, editor needs
2200 // to stop handling it. However, editor throw exception in this case
2201 // because Chrome does not throw exception even in this case.
2202 case NS_ERROR_EDITOR_DESTROYED:
2203 return NS_OK;
2204 // If editor meets unexpected DOM tree due to modified by mutation event
2205 // listener, editor needs to stop handling it. However, editor shouldn't
2206 // return error for the users because Chrome does not throw exception in
2207 // this case.
2208 case NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE:
2209 return NS_OK;
2210 // If the editing action is canceled by event listeners, editor needs
2211 // to stop handling it. However, editor shouldn't return error for
2212 // the callers but they should be able to distinguish whether it's
2213 // canceled or not. Although it's DOM specific code, let's return
2214 // DOM_SUCCESS_DOM_NO_OPERATION here.
2215 case NS_ERROR_EDITOR_ACTION_CANCELED:
2216 return NS_SUCCESS_DOM_NO_OPERATION;
2217 // If there is no selection range or editable selection ranges, editor
2218 // needs to stop handling it. However, editor shouldn't return error for
2219 // the callers to avoid throwing exception. However, they may want to
2220 // check whether it works or not. Therefore, we should return
2221 // NS_SUCCESS_DOM_NO_OPERATION instead.
2222 case NS_ERROR_EDITOR_NO_EDITABLE_RANGE:
2223 return NS_SUCCESS_DOM_NO_OPERATION;
2224 default:
2225 return aRv;
2226 }
2227 }
2228
2229 /**
2230 * GetDocumentCharsetInternal() returns charset of the document.
2231 */
2232 nsresult GetDocumentCharsetInternal(nsACString& aCharset) const;
2233
2234 /**
2235 * ComputeValueInternal() computes string value of this editor for given
2236 * format. This may be too expensive if it's in hot path.
2237 *
2238 * @param aFormatType MIME type like "text/plain".
2239 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder.
2240 * @param aCharset Encoding of the document.
2241 */
2242 nsresult ComputeValueInternal(const nsAString& aFormatType,
2243 uint32_t aDocumentEncoderFlags,
2244 nsAString& aOutputString) const;
2245
2246 /**
2247 * GetAndInitDocEncoder() returns a document encoder instance for aFormatType
2248 * after initializing it. The result may be cached for saving recreation
2249 * cost.
2250 *
2251 * @param aFormatType MIME type like "text/plain".
2252 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder.
2253 * @param aCharset Encoding of the document.
2254 */
2255 already_AddRefed<nsIDocumentEncoder> GetAndInitDocEncoder(
2256 const nsAString& aFormatType, uint32_t aDocumentEncoderFlags,
2257 const nsACString& aCharset) const;
2258
2259 /**
2260 * EnsurePaddingBRElementInMultilineEditor() creates a padding `<br>` element
2261 * at end of multiline text editor.
2262 */
2263 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2264 EnsurePaddingBRElementInMultilineEditor();
2265
2266 /**
2267 * SelectAllInternal() should be used instead of SelectAll() in editor
2268 * because SelectAll() creates AutoEditActionSetter but we should avoid
2269 * to create it as far as possible.
2270 */
2271 MOZ_CAN_RUN_SCRIPT virtual nsresult SelectAllInternal();
2272
2273 nsresult DetermineCurrentDirection();
2274
2275 /**
2276 * DispatchInputEvent() dispatches an "input" event synchronously or
2277 * asynchronously if it's not safe to dispatch.
2278 */
2279 MOZ_CAN_RUN_SCRIPT void DispatchInputEvent();
2280
2281 /**
2282 * Called after a transaction is done successfully.
2283 */
2284 MOZ_CAN_RUN_SCRIPT void DoAfterDoTransaction(nsITransaction* aTransaction);
2285
2286 /**
2287 * Called after a transaction is undone successfully.
2288 */
2289
2290 MOZ_CAN_RUN_SCRIPT void DoAfterUndoTransaction();
2291
2292 /**
2293 * Called after a transaction is redone successfully.
2294 */
2295 MOZ_CAN_RUN_SCRIPT void DoAfterRedoTransaction();
2296
2297 /**
2298 * Tell the doc state listeners that the doc state has changed.
2299 */
2300 enum TDocumentListenerNotification {
2301 eDocumentCreated,
2302 eDocumentToBeDestroyed,
2303 eDocumentStateChanged
2304 };
2305 MOZ_CAN_RUN_SCRIPT nsresult
2306 NotifyDocumentListeners(TDocumentListenerNotification aNotificationType);
2307
2308 /**
2309 * Make the given selection span the entire document.
2310 */
2311 MOZ_CAN_RUN_SCRIPT virtual nsresult SelectEntireDocument() = 0;
2312
2313 /**
2314 * Helper method for scrolling the selection into view after
2315 * an edit operation.
2316 *
2317 * Editor methods *should* call this method instead of the versions
2318 * in the various selection interfaces, since this makes sure that
2319 * the editor's sync/async settings for reflowing, painting, and scrolling
2320 * match.
2321 */
2322 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ScrollSelectionFocusIntoView();
2323
2324 virtual nsresult InstallEventListeners();
2325 virtual void CreateEventListeners();
2326 virtual void RemoveEventListeners();
2327
2328 /**
2329 * Called if and only if this editor is in readonly mode.
2330 */
2331 void HandleKeyPressEventInReadOnlyMode(
2332 WidgetKeyboardEvent& aKeyboardEvent) const;
2333
2334 /**
2335 * Get the input event target. This might return null.
2336 */
2337 virtual already_AddRefed<Element> GetInputEventTargetElement() const = 0;
2338
2339 /**
2340 * Return true if spellchecking should be enabled for this editor.
2341 */
2342 bool GetDesiredSpellCheckState();
2343
CanEnableSpellCheck()2344 bool CanEnableSpellCheck() {
2345 // Check for password/readonly/disabled, which are not spellchecked
2346 // regardless of DOM. Also, check to see if spell check should be skipped
2347 // or not.
2348 return !IsPasswordEditor() && !IsReadonly() && !ShouldSkipSpellCheck();
2349 }
2350
2351 /**
2352 * InitializeSelectionAncestorLimit() is called by InitializeSelection().
2353 * When this is called, each implementation has to call
2354 * Selection::SetAncestorLimiter() with aAnotherLimit.
2355 *
2356 * @param aAncestorLimit New ancestor limit of Selection. This always
2357 * has parent node. So, it's always safe to
2358 * call SetAncestorLimit() with this node.
2359 */
2360 virtual void InitializeSelectionAncestorLimit(
2361 nsIContent& aAncestorLimit) const;
2362
2363 /**
2364 * Initializes selection and caret for the editor. If aEventTarget isn't
2365 * a host of the editor, i.e., the editor doesn't get focus, this does
2366 * nothing.
2367 */
2368 MOZ_CAN_RUN_SCRIPT nsresult InitializeSelection(nsINode& aFocusEventTarget);
2369
2370 enum NotificationForEditorObservers {
2371 eNotifyEditorObserversOfEnd,
2372 eNotifyEditorObserversOfBefore,
2373 eNotifyEditorObserversOfCancel
2374 };
2375 MOZ_CAN_RUN_SCRIPT void NotifyEditorObservers(
2376 NotificationForEditorObservers aNotification);
2377
2378 /**
2379 * HowToHandleCollapsedRange indicates how collapsed range should be treated.
2380 */
2381 enum class HowToHandleCollapsedRange {
2382 // Ignore collapsed range.
2383 Ignore,
2384 // Extend collapsed range for removing previous content.
2385 ExtendBackward,
2386 // Extend collapsed range for removing next content.
2387 ExtendForward,
2388 };
2389
HowToHandleCollapsedRangeFor(nsIEditor::EDirection aDirectionAndAmount)2390 static HowToHandleCollapsedRange HowToHandleCollapsedRangeFor(
2391 nsIEditor::EDirection aDirectionAndAmount) {
2392 switch (aDirectionAndAmount) {
2393 case nsIEditor::eNone:
2394 return HowToHandleCollapsedRange::Ignore;
2395 case nsIEditor::ePrevious:
2396 return HowToHandleCollapsedRange::ExtendBackward;
2397 case nsIEditor::eNext:
2398 return HowToHandleCollapsedRange::ExtendForward;
2399 case nsIEditor::ePreviousWord:
2400 case nsIEditor::eNextWord:
2401 case nsIEditor::eToBeginningOfLine:
2402 case nsIEditor::eToEndOfLine:
2403 // If the amount is word or
2404 // line,`AutoRangeArray::ExtendAnchorFocusRangeFor()` must have already
2405 // been extended collapsed ranges before.
2406 return HowToHandleCollapsedRange::Ignore;
2407 }
2408 MOZ_ASSERT_UNREACHABLE("Invalid nsIEditor::EDirection value");
2409 return HowToHandleCollapsedRange::Ignore;
2410 }
2411
2412 /**
2413 * InsertDroppedDataTransferAsAction() inserts all data items in aDataTransfer
2414 * at aDroppedAt unless the editor is destroyed.
2415 *
2416 * @param aEditActionData The edit action data whose edit action must be
2417 * EditAction::eDrop.
2418 * @param aDataTransfer The data transfer object which is dropped.
2419 * @param aDroppedAt The DOM tree position whether aDataTransfer
2420 * is dropped.
2421 * @param aSourcePrincipal Principal of the source of the drag.
2422 * May be nullptr if it comes from another app
2423 * or process.
2424 */
2425 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult
2426 InsertDroppedDataTransferAsAction(AutoEditActionDataSetter& aEditActionData,
2427 dom::DataTransfer& aDataTransfer,
2428 const EditorDOMPoint& aDroppedAt,
2429 nsIPrincipal* aSourcePrincipal) = 0;
2430
2431 /**
2432 * DeleteSelectionByDragAsAction() removes selection and dispatch "input"
2433 * event whose inputType is "deleteByDrag".
2434 */
2435 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2436 DeleteSelectionByDragAsAction(bool aDispatchInputEvent);
2437
2438 /**
2439 * DeleteSelectionWithTransaction() removes selected content or content
2440 * around caret with transactions and remove empty inclusive ancestor
2441 * inline elements of collapsed selection after removing the contents.
2442 *
2443 * @param aDirectionAndAmount How much range should be removed.
2444 * @param aStripWrappers Whether the parent blocks should be removed
2445 * when they become empty.
2446 * Note that this must be `nsIEditor::eNoStrip`
2447 * if this is a TextEditor because anyway it'll
2448 * be ignored.
2449 */
2450 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2451 DeleteSelectionWithTransaction(nsIEditor::EDirection aDirectionAndAmount,
2452 nsIEditor::EStripWrappers aStripWrappers);
2453
2454 /**
2455 * DeleteRangesWithTransaction() removes content in aRangesToDelete or content
2456 * around collapsed ranges in aRangesToDelete with transactions and remove
2457 * empty inclusive ancestor inline elements of collapsed ranges after
2458 * removing the contents.
2459 *
2460 * @param aDirectionAndAmount How much range should be removed.
2461 * @param aStripWrappers Whether the parent blocks should be removed
2462 * when they become empty.
2463 * Note that this must be `nsIEditor::eNoStrip`
2464 * if this is a TextEditor because anyway it'll
2465 * be ignored.
2466 * @param aRangesToDelete The ranges to delete content.
2467 */
2468 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
2469 DeleteRangesWithTransaction(nsIEditor::EDirection aDirectionAndAmount,
2470 nsIEditor::EStripWrappers aStripWrappers,
2471 const AutoRangeArray& aRangesToDelete);
2472
2473 /**
2474 * Create an aggregate transaction for delete the content in aRangesToDelete.
2475 * The result may include DeleteNodeTransactions and/or DeleteTextTransactions
2476 * as its children.
2477 *
2478 * @param aHowToHandleCollapsedRange
2479 * How to handle collapsed ranges.
2480 * @param aRangesToDelete The ranges to delete content.
2481 * @return If it can remove the content in ranges, returns
2482 * an aggregate transaction which has some
2483 * DeleteNodeTransactions and/or
2484 * DeleteTextTransactions as its children.
2485 */
2486 already_AddRefed<EditAggregateTransaction>
2487 CreateTransactionForDeleteSelection(
2488 HowToHandleCollapsedRange aHowToHandleCollapsedRange,
2489 const AutoRangeArray& aRangesToDelete);
2490
2491 /**
2492 * Create a transaction for removing the nodes and/or text around
2493 * aRangeToDelete.
2494 *
2495 * @param aCollapsedRange The range to be removed. This must be
2496 * collapsed.
2497 * @param aHowToHandleCollapsedRange
2498 * How to handle aCollapsedRange. Must
2499 * be HowToHandleCollapsedRange::ExtendBackward or
2500 * HowToHandleCollapsedRange::ExtendForward.
2501 * @return The transaction to remove content around the
2502 * range. Its type is DeleteNodeTransaction or
2503 * DeleteTextTransaction.
2504 */
2505 already_AddRefed<EditTransactionBase> CreateTransactionForCollapsedRange(
2506 const nsRange& aCollapsedRange,
2507 HowToHandleCollapsedRange aHowToHandleCollapsedRange);
2508
2509 /**
2510 * ComputeInsertedRange() returns actual range modified by inserting string
2511 * in a text node. If mutation event listener changed the text data, this
2512 * returns a range which covers all over the text data.
2513 */
2514 std::tuple<EditorDOMPointInText, EditorDOMPointInText> ComputeInsertedRange(
2515 const EditorDOMPointInText& aInsertedPoint,
2516 const nsAString& aInsertedString) const;
2517
2518 /**
2519 * EnsureComposition() should be called by composition event handlers. This
2520 * tries to get the composition for the event and set it to mComposition.
2521 * However, this may fail because the composition may be committed before
2522 * the event comes to the editor.
2523 *
2524 * @return true if there is a composition. Otherwise, for example,
2525 * a composition event handler in web contents moved focus
2526 * for committing the composition, returns false.
2527 */
2528 bool EnsureComposition(WidgetCompositionEvent& aCompositionEvent);
2529
2530 /**
2531 * See comment of IsCopyToClipboardAllowed() for the detail.
2532 */
IsCopyToClipboardAllowedInternal()2533 virtual bool IsCopyToClipboardAllowedInternal() const {
2534 MOZ_ASSERT(IsEditActionDataAvailable());
2535 return !SelectionRef().IsCollapsed();
2536 }
2537
2538 /**
2539 * Helper for Is{Cut|Copy}CommandEnabled.
2540 * Look for a listener for the given command, including up the target chain.
2541 */
2542 MOZ_CAN_RUN_SCRIPT bool CheckForClipboardCommandListener(
2543 nsAtom* aCommand, EventMessage aEventMessage) const;
2544
2545 /**
2546 * FireClipboardEvent() may dispatch a clipboard event.
2547 *
2548 * @param aEventMessage The event message which may be set to the
2549 * dispatching event.
2550 * @param aClipboardType Working with global clipboard or selection.
2551 * @param aActionTaken [optional][out] If set to non-nullptr, will be
2552 * set to true if the action for the event is
2553 * handled or prevented default.
2554 * @return false if dispatching event is canceled.
2555 */
2556 bool FireClipboardEvent(EventMessage aEventMessage, int32_t aClipboardType,
2557 bool* aActionTaken = nullptr);
2558
2559 private:
2560 nsCOMPtr<nsISelectionController> mSelectionController;
2561 RefPtr<Document> mDocument;
2562
2563 AutoEditActionDataSetter* mEditActionData;
2564
2565 /**
2566 * SetTextDirectionTo() sets text-direction of the root element.
2567 * Should use SwitchTextDirectionTo() or ToggleTextDirection() instead.
2568 * This is a helper class of them.
2569 */
2570 nsresult SetTextDirectionTo(TextDirection aTextDirection);
2571
2572 protected: // helper classes which may be used by friends
2573 /**
2574 * Stack based helper class for batching a collection of transactions inside
2575 * a placeholder transaction. Different from AutoTransactionBatch, this
2576 * notifies editor observers of before/end edit action handling, and
2577 * dispatches "input" event if it's necessary.
2578 */
2579 class MOZ_RAII AutoPlaceholderBatch final {
2580 public:
2581 /**
2582 * @param aRequesterFuncName function name which wants to end the batch.
2583 * This won't be stored nor exposed to selection listeners etc, used only
2584 * for logging. This MUST be alive when the destructor runs.
2585 */
AutoPlaceholderBatch(EditorBase & aEditorBase,ScrollSelectionIntoView aScrollSelectionIntoView,const char * aRequesterFuncName)2586 AutoPlaceholderBatch(EditorBase& aEditorBase,
2587 ScrollSelectionIntoView aScrollSelectionIntoView,
2588 const char* aRequesterFuncName)
2589 : mEditorBase(aEditorBase),
2590 mScrollSelectionIntoView(aScrollSelectionIntoView),
2591 mRequesterFuncName(aRequesterFuncName) {
2592 mEditorBase->BeginPlaceholderTransaction(*nsGkAtoms::_empty,
2593 mRequesterFuncName);
2594 }
2595
AutoPlaceholderBatch(EditorBase & aEditorBase,nsStaticAtom & aTransactionName,ScrollSelectionIntoView aScrollSelectionIntoView,const char * aRequesterFuncName)2596 AutoPlaceholderBatch(EditorBase& aEditorBase,
2597 nsStaticAtom& aTransactionName,
2598 ScrollSelectionIntoView aScrollSelectionIntoView,
2599 const char* aRequesterFuncName)
2600 : mEditorBase(aEditorBase),
2601 mScrollSelectionIntoView(aScrollSelectionIntoView),
2602 mRequesterFuncName(aRequesterFuncName) {
2603 mEditorBase->BeginPlaceholderTransaction(aTransactionName,
2604 mRequesterFuncName);
2605 }
2606
~AutoPlaceholderBatch()2607 ~AutoPlaceholderBatch() {
2608 mEditorBase->EndPlaceholderTransaction(mScrollSelectionIntoView,
2609 mRequesterFuncName);
2610 }
2611
2612 protected:
2613 const OwningNonNull<EditorBase> mEditorBase;
2614 const ScrollSelectionIntoView mScrollSelectionIntoView;
2615 const char* const mRequesterFuncName;
2616 };
2617
2618 /**
2619 * AutoEditSubActionNotifier notifies editor of start to handle
2620 * top level edit sub-action and end handling top level edit sub-action.
2621 */
2622 class MOZ_RAII AutoEditSubActionNotifier final {
2623 public:
AutoEditSubActionNotifier(EditorBase & aEditorBase,EditSubAction aEditSubAction,nsIEditor::EDirection aDirection,ErrorResult & aRv)2624 MOZ_CAN_RUN_SCRIPT AutoEditSubActionNotifier(
2625 EditorBase& aEditorBase, EditSubAction aEditSubAction,
2626 nsIEditor::EDirection aDirection, ErrorResult& aRv)
2627 : mEditorBase(aEditorBase), mIsTopLevel(true) {
2628 // The top level edit sub action has already be set if this is nested call
2629 // XXX Looks like that this is not aware of unexpected nested edit action
2630 // handling via selectionchange event listener or mutation event
2631 // listener.
2632 if (!mEditorBase.GetTopLevelEditSubAction()) {
2633 MOZ_KnownLive(mEditorBase)
2634 .OnStartToHandleTopLevelEditSubAction(aEditSubAction, aDirection,
2635 aRv);
2636 } else {
2637 mIsTopLevel = false;
2638 }
2639 mEditorBase.OnStartToHandleEditSubAction();
2640 }
2641
~AutoEditSubActionNotifier()2642 MOZ_CAN_RUN_SCRIPT ~AutoEditSubActionNotifier() {
2643 mEditorBase.OnEndHandlingEditSubAction();
2644 if (mIsTopLevel) {
2645 MOZ_KnownLive(mEditorBase).OnEndHandlingTopLevelEditSubAction();
2646 }
2647 }
2648
2649 protected:
2650 EditorBase& mEditorBase;
2651 bool mIsTopLevel;
2652 };
2653
2654 /**
2655 * Stack based helper class for turning off active selection adjustment
2656 * by low level transactions
2657 */
2658 class MOZ_RAII AutoTransactionsConserveSelection final {
2659 public:
AutoTransactionsConserveSelection(EditorBase & aEditorBase)2660 explicit AutoTransactionsConserveSelection(EditorBase& aEditorBase)
2661 : mEditorBase(aEditorBase),
2662 mAllowedTransactionsToChangeSelection(
2663 aEditorBase.AllowsTransactionsToChangeSelection()) {
2664 mEditorBase.MakeThisAllowTransactionsToChangeSelection(false);
2665 }
2666
~AutoTransactionsConserveSelection()2667 ~AutoTransactionsConserveSelection() {
2668 mEditorBase.MakeThisAllowTransactionsToChangeSelection(
2669 mAllowedTransactionsToChangeSelection);
2670 }
2671
2672 protected:
2673 EditorBase& mEditorBase;
2674 bool mAllowedTransactionsToChangeSelection;
2675 };
2676
2677 /***************************************************************************
2678 * stack based helper class for batching reflow and paint requests.
2679 */
2680 class MOZ_RAII AutoUpdateViewBatch final {
2681 public:
2682 /**
2683 * @param aRequesterFuncName function name which wants to end the batch.
2684 * This won't be stored nor exposed to selection listeners etc, used only
2685 * for logging. This MUST be alive when the destructor runs.
2686 */
AutoUpdateViewBatch(EditorBase & aEditorBase,const char * aRequesterFuncName)2687 MOZ_CAN_RUN_SCRIPT explicit AutoUpdateViewBatch(
2688 EditorBase& aEditorBase, const char* aRequesterFuncName)
2689 : mEditorBase(aEditorBase), mRequesterFuncName(aRequesterFuncName) {
2690 mEditorBase.BeginUpdateViewBatch(mRequesterFuncName);
2691 }
2692
~AutoUpdateViewBatch()2693 MOZ_CAN_RUN_SCRIPT ~AutoUpdateViewBatch() {
2694 MOZ_KnownLive(mEditorBase).EndUpdateViewBatch(mRequesterFuncName);
2695 }
2696
2697 protected:
2698 EditorBase& mEditorBase;
2699 const char* const mRequesterFuncName;
2700 };
2701
2702 protected:
2703 enum Tristate { eTriUnset, eTriFalse, eTriTrue };
2704
2705 // MIME type of the doc we are editing.
2706 nsString mContentMIMEType;
2707
2708 RefPtr<mozInlineSpellChecker> mInlineSpellChecker;
2709 // Reference to text services document for mInlineSpellChecker.
2710 RefPtr<TextServicesDocument> mTextServicesDocument;
2711
2712 RefPtr<TransactionManager> mTransactionManager;
2713 // Cached root node.
2714 RefPtr<Element> mRootElement;
2715
2716 // The form field as an event receiver.
2717 nsCOMPtr<dom::EventTarget> mEventTarget;
2718 RefPtr<EditorEventListener> mEventListener;
2719 // Strong reference to placeholder for begin/end batch purposes.
2720 RefPtr<PlaceholderTransaction> mPlaceholderTransaction;
2721 // Name of placeholder transaction.
2722 nsStaticAtom* mPlaceholderName;
2723 // Saved selection state for placeholder transaction batching.
2724 mozilla::Maybe<SelectionState> mSelState;
2725 // IME composition this is not null between compositionstart and
2726 // compositionend.
2727 RefPtr<TextComposition> mComposition;
2728
2729 RefPtr<TextInputListener> mTextInputListener;
2730
2731 RefPtr<IMEContentObserver> mIMEContentObserver;
2732
2733 // These members cache last encoder and its type for the performance in
2734 // TextEditor::ComputeTextValue() which is the implementation of
2735 // `<input>.value` and `<textarea>.value`. See `GetAndInitDocEncoder()`.
2736 mutable nsCOMPtr<nsIDocumentEncoder> mCachedDocumentEncoder;
2737 mutable nsString mCachedDocumentEncoderType;
2738
2739 // Listens to all low level actions on the doc.
2740 // Edit action listener is currently used by highlighter of the findbar and
2741 // the spellchecker. So, we should reserve only 2 items.
2742 typedef AutoTArray<OwningNonNull<nsIEditActionListener>, 2>
2743 AutoActionListenerArray;
2744 AutoActionListenerArray mActionListeners;
2745 // Listen to overall doc state (dirty or not, just created, etc.).
2746 // Document state listener is currently used by FinderHighlighter and
2747 // BlueGriffon so that reserving only one is enough.
2748 typedef AutoTArray<OwningNonNull<nsIDocumentStateListener>, 1>
2749 AutoDocumentStateListenerArray;
2750 AutoDocumentStateListenerArray mDocStateListeners;
2751
2752 // Number of modifications (for undo/redo stack).
2753 uint32_t mModCount;
2754 // Behavior flags. See nsIEditor.idl for the flags we use.
2755 uint32_t mFlags;
2756
2757 int32_t mUpdateCount;
2758
2759 // Nesting count for batching.
2760 int32_t mPlaceholderBatch;
2761
2762 int32_t mWrapColumn;
2763 int32_t mNewlineHandling;
2764 int32_t mCaretStyle;
2765
2766 // -1 = not initialized
2767 int8_t mDocDirtyState;
2768 // A Tristate value.
2769 uint8_t mSpellcheckCheckboxState;
2770
2771 // If true, initialization was succeeded.
2772 bool mInitSucceeded;
2773 // If false, transactions should not change Selection even after modifying
2774 // the DOM tree.
2775 bool mAllowsTransactionsToChangeSelection;
2776 // Whether PreDestroy has been called.
2777 bool mDidPreDestroy;
2778 // Whether PostCreate has been called.
2779 bool mDidPostCreate;
2780 bool mDispatchInputEvent;
2781 // True while the instance is handling an edit sub-action.
2782 bool mIsInEditSubAction;
2783 // Whether caret is hidden forcibly.
2784 bool mHidingCaret;
2785 // Whether spellchecker dictionary is initialized after focused.
2786 bool mSpellCheckerDictionaryUpdated;
2787 // Whether we are an HTML editor class.
2788 bool mIsHTMLEditorClass;
2789
2790 friend class AlignStateAtSelection;
2791 friend class AutoRangeArray;
2792 friend class CompositionTransaction;
2793 friend class CSSEditUtils;
2794 friend class DeleteNodeTransaction;
2795 friend class DeleteRangeTransaction;
2796 friend class DeleteTextTransaction;
2797 friend class HTMLEditUtils;
2798 friend class InsertNodeTransaction;
2799 friend class InsertTextTransaction;
2800 friend class JoinNodesTransaction;
2801 friend class ListElementSelectionState;
2802 friend class ListItemElementSelectionState;
2803 friend class ParagraphStateAtSelection;
2804 friend class ReplaceTextTransaction;
2805 friend class SplitNodeTransaction;
2806 friend class TypeInState;
2807 friend class WhiteSpaceVisibilityKeeper;
2808 friend class WSRunScanner;
2809 friend class nsIEditor;
2810 };
2811
2812 } // namespace mozilla
2813
IsTextEditor()2814 bool nsIEditor::IsTextEditor() const {
2815 return !AsEditorBase()->mIsHTMLEditorClass;
2816 }
2817
IsHTMLEditor()2818 bool nsIEditor::IsHTMLEditor() const {
2819 return AsEditorBase()->mIsHTMLEditorClass;
2820 }
2821
AsEditorBase()2822 mozilla::EditorBase* nsIEditor::AsEditorBase() {
2823 return static_cast<mozilla::EditorBase*>(this);
2824 }
2825
AsEditorBase()2826 const mozilla::EditorBase* nsIEditor::AsEditorBase() const {
2827 return static_cast<const mozilla::EditorBase*>(this);
2828 }
2829
2830 #endif // #ifndef mozilla_EditorBase_h
2831