1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_EditorBase_h
7 #define mozilla_EditorBase_h
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/EditorForwards.h"
14 #include "mozilla/EventForwards.h" // for InputEventTargetRanges
15 #include "mozilla/Likely.h" // for MOZ_UNLIKELY, MOZ_LIKELY
16 #include "mozilla/Maybe.h" // for Maybe
17 #include "mozilla/OwningNonNull.h" // for OwningNonNull
18 #include "mozilla/PendingStyles.h" // for PendingStyle, PendingStyleCache
19 #include "mozilla/RangeBoundary.h" // for RawRangeBoundary, RangeBoundary
20 #include "mozilla/SelectionState.h" // for RangeUpdater, etc.
21 #include "mozilla/StyleSheet.h" // for StyleSheet
22 #include "mozilla/TransactionManager.h" // for TransactionManager
23 #include "mozilla/WeakPtr.h" // for WeakPtr
24 #include "mozilla/dom/DataTransfer.h" // for dom::DataTransfer
25 #include "mozilla/dom/HTMLBRElement.h" // for dom::HTMLBRElement
26 #include "mozilla/dom/Selection.h"
27 #include "mozilla/dom/Text.h"
28 #include "nsAtom.h" // for nsAtom, nsStaticAtom
29 #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr
30 #include "nsCycleCollectionParticipant.h"
31 #include "nsGkAtoms.h"
32 #include "nsIClipboard.h" // for nsIClipboard::ClipboardType
33 #include "nsIContentInlines.h" // for nsINode::IsEditable()
34 #include "nsIEditor.h" // for nsIEditor, etc.
35 #include "nsISelectionController.h" // for nsISelectionController constants
36 #include "nsISelectionListener.h" // for nsISelectionListener
37 #include "nsISupportsImpl.h" // for EditorBase::Release, etc.
38 #include "nsIWeakReferenceUtils.h" // for nsWeakPtr
39 #include "nsLiteralString.h" // for NS_LITERAL_STRING
40 #include "nsPIDOMWindow.h" // for nsPIDOMWindowInner, etc.
41 #include "nsString.h" // for nsCString
42 #include "nsTArray.h" // for nsTArray and AutoTArray
43 #include "nsWeakReference.h" // for nsSupportsWeakReference
44 #include "nscore.h" // for nsresult, nsAString, etc.
46 #include <tuple> // for std::tuple
48 class mozInlineSpellChecker
;
52 class nsIDocumentEncoder
;
53 class nsIDocumentStateListener
;
54 class nsIEditActionListener
;
58 class nsITransferable
;
64 class AlignStateAtSelection
;
65 class AutoTransactionsConserveSelection
;
66 class AutoUpdateViewBatch
;
68 class IMEContentObserver
;
69 class ListElementSelectionState
;
70 class ListItemElementSelectionState
;
71 class ParagraphStateAtSelection
;
73 class TextComposition
;
74 class TextInputListener
;
75 class TextServicesDocument
;
91 * Implementation of an editor object. it will be the controller/focal point
92 * for the main editor services. i.e. the GUIManager, publishing, transaction
93 * manager, event interfaces. the idea for the event interfaces is to have them
94 * delegate the actual commands to the editor independent of the XPFE
97 class EditorBase
: public nsIEditor
,
98 public nsISelectionListener
,
99 public nsSupportsWeakReference
{
101 /****************************************************************************
102 * NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other
103 * classes under libeditor except EditorEventListener and
104 * HTMLEditorEventListener because each public method which may fire
105 * eEditorInput event will need to instantiate new stack class for
106 * managing input type value of eEditorInput and cache some objects
107 * for smarter handling. In other words, when you add new root
108 * method to edit the DOM tree, you can make your new method public.
109 ****************************************************************************/
111 using DataTransfer
= dom::DataTransfer
;
112 using Document
= dom::Document
;
113 using Element
= dom::Element
;
114 using InterlinePosition
= dom::Selection::InterlinePosition
;
115 using Selection
= dom::Selection
;
116 using Text
= dom::Text
;
118 enum class EditorType
{ Text
, HTML
};
120 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
121 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase
, nsIEditor
)
126 // nsISelectionListener method
127 NS_DECL_NSISELECTIONLISTENER
130 * The default constructor. This should suffice. the setting of the
131 * interfaces is done after the construction of the editor class.
133 explicit EditorBase(EditorType aEditorType
);
135 [[nodiscard
]] bool IsInitialized() const {
136 return mDocument
&& mDidPostCreate
;
138 [[nodiscard
]] bool IsBeingInitialized() const {
139 return mDocument
&& !mDidPostCreate
;
141 [[nodiscard
]] bool Destroyed() const { return mDidPreDestroy
; }
143 Document
* GetDocument() const { return mDocument
; }
144 nsPIDOMWindowOuter
* GetWindow() const;
145 nsPIDOMWindowInner
* GetInnerWindow() const;
148 * MayHaveMutationEventListeners() returns true when the window may have
149 * mutation event listeners.
151 * @param aMutationEventType One or multiple of NS_EVENT_BITS_MUTATION_*.
152 * @return true if the editor is an HTMLEditor instance,
153 * and at least one of NS_EVENT_BITS_MUTATION_* is
154 * set to the window or in debug build.
156 bool MayHaveMutationEventListeners(
157 uint32_t aMutationEventType
= 0xFFFFFFFF) const {
158 if (IsTextEditor()) {
159 // DOM mutation event listeners cannot catch the changes of
160 // <input type="text"> nor <textarea>.
164 // On debug build, this should always return true for testing complicated
165 // path without mutation event listeners because when mutation event
166 // listeners do not touch the DOM, editor needs to run as there is no
167 // mutation event listeners.
169 #else // #ifdef DEBUG
170 nsPIDOMWindowInner
* window
= GetInnerWindow();
171 return window
? window
->HasMutationListeners(aMutationEventType
) : false;
172 #endif // #ifdef DEBUG #else
176 * MayHaveBeforeInputEventListenersForTelemetry() returns true when the
177 * window may have or have had one or more `beforeinput` event listeners.
178 * Note that this may return false even if there is a `beforeinput`.
179 * See nsPIDOMWindowInner::HasBeforeInputEventListenersForTelemetry()'s
180 * comment for the detail.
182 bool MayHaveBeforeInputEventListenersForTelemetry() const {
183 if (const nsPIDOMWindowInner
* window
= GetInnerWindow()) {
184 return window
->HasBeforeInputEventListenersForTelemetry();
190 * MutationObserverHasObservedNodeForTelemetry() returns true when a node in
191 * the window may have been observed by the web apps with a mutation observer
192 * (i.e., `MutationObserver.observe()` called by chrome script and addon's
193 * script does not make this returns true).
194 * Note that this may return false even if there is a node observed by
195 * a MutationObserver. See
196 * nsPIDOMWindowInner::MutationObserverHasObservedNodeForTelemetry()'s comment
199 bool MutationObserverHasObservedNodeForTelemetry() const {
200 if (const nsPIDOMWindowInner
* window
= GetInnerWindow()) {
201 return window
->MutationObserverHasObservedNodeForTelemetry();
207 * This checks whether the call with aPrincipal should or should not be
208 * treated as user input.
210 [[nodiscard
]] static bool TreatAsUserInput(nsIPrincipal
* aPrincipal
);
212 PresShell
* GetPresShell() const;
213 nsPresContext
* GetPresContext() const;
214 already_AddRefed
<nsCaret
> GetCaret() const;
216 already_AddRefed
<nsIWidget
> GetWidget() const;
218 nsISelectionController
* GetSelectionController() const;
220 nsresult
GetSelection(SelectionType aSelectionType
,
221 Selection
** aSelection
) const;
223 Selection
* GetSelection(
224 SelectionType aSelectionType
= SelectionType::eNormal
) const {
225 if (aSelectionType
== SelectionType::eNormal
&&
226 IsEditActionDataAvailable()) {
227 return &SelectionRef();
229 nsISelectionController
* sc
= GetSelectionController();
233 Selection
* selection
= sc
->GetSelection(ToRawSelectionType(aSelectionType
));
238 * @return Ancestor limiter of normal selection
240 [[nodiscard
]] nsIContent
* GetSelectionAncestorLimiter() const {
241 Selection
* selection
= GetSelection(SelectionType::eNormal
);
242 return selection
? selection
->GetAncestorLimiter() : nullptr;
246 * Create a DataTransfer object that can be shared between the paste event
247 * and pasting into a DOM element.
249 already_AddRefed
<DataTransfer
> CreateDataTransferForPaste(
250 EventMessage aEventMessage
,
251 nsIClipboard::ClipboardType aClipboardType
) const;
254 * Fast non-refcounting editor root element accessor
256 Element
* GetRoot() const { return mRootElement
; }
259 * Likewise, but gets the text control element instead of the root for
262 Element
* GetExposedRoot() const;
265 * Set or unset TextInputListener. If setting non-nullptr when the editor
266 * already has a TextInputListener, this will crash in debug build.
268 void SetTextInputListener(TextInputListener
* aTextInputListener
);
271 * Set or unset IMEContentObserver. If setting non-nullptr when the editor
272 * already has an IMEContentObserver, this will crash in debug build.
274 void SetIMEContentObserver(IMEContentObserver
* aIMEContentObserver
);
277 * Returns current composition.
279 TextComposition
* GetComposition() const;
282 * Get preferred IME status of current widget.
284 virtual nsresult
GetPreferredIMEState(widget::IMEState
* aState
);
287 * Returns true if there is composition string and not fixed.
289 bool IsIMEComposing() const;
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
298 nsresult
CommitComposition();
301 * ToggleTextDirection() toggles text-direction of the root element.
303 * @param aPrincipal Set subject principal if it may be called by
304 * JS. If set to nullptr, will be treated as
307 MOZ_CAN_RUN_SCRIPT nsresult
308 ToggleTextDirectionAsAction(nsIPrincipal
* aPrincipal
= nullptr);
311 * SwitchTextDirectionTo() sets the text-direction of the root element to
314 enum class TextDirection
{
318 MOZ_CAN_RUN_SCRIPT
void SwitchTextDirectionTo(TextDirection aTextDirection
);
321 * Finalizes selection and caret for the editor.
323 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
FinalizeSelection();
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.
331 bool IsSelectionEditable();
334 * Returns number of undo or redo items.
336 size_t NumberOfUndoItems() const {
337 return mTransactionManager
? mTransactionManager
->NumberOfUndoItems() : 0;
339 size_t NumberOfRedoItems() const {
340 return mTransactionManager
? mTransactionManager
->NumberOfRedoItems() : 0;
344 * Returns number of maximum undo/redo transactions.
346 int32_t NumberOfMaximumTransactions() const {
347 return mTransactionManager
348 ? mTransactionManager
->NumberOfMaximumTransactions()
353 * Returns true if this editor can store transactions for undo/redo.
355 bool IsUndoRedoEnabled() const {
356 return mTransactionManager
&&
357 mTransactionManager
->NumberOfMaximumTransactions();
361 * Return true if it's possible to undo/redo right now.
363 bool CanUndo() const {
364 return IsUndoRedoEnabled() && NumberOfUndoItems() > 0;
366 bool CanRedo() const {
367 return IsUndoRedoEnabled() && NumberOfRedoItems() > 0;
371 * Enables or disables undo/redo feature. Returns true if it succeeded,
372 * otherwise, e.g., we're undoing or redoing, returns false.
374 bool EnableUndoRedo(int32_t aMaxTransactionCount
= -1) {
375 if (!mTransactionManager
) {
376 mTransactionManager
= new TransactionManager();
378 return mTransactionManager
->EnableUndoRedo(aMaxTransactionCount
);
380 bool DisableUndoRedo() {
381 if (!mTransactionManager
) {
384 return mTransactionManager
->DisableUndoRedo();
386 bool ClearUndoRedo() {
387 if (!mTransactionManager
) {
390 return mTransactionManager
->ClearUndoRedo();
394 * See Document::AreClipboardCommandsUnconditionallyEnabled.
396 bool AreClipboardCommandsUnconditionallyEnabled() const;
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()`.
403 MOZ_CAN_RUN_SCRIPT
bool IsCutCommandEnabled() const;
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()`.
410 MOZ_CAN_RUN_SCRIPT
bool IsCopyCommandEnabled() const;
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.
419 bool IsCopyToClipboardAllowed() const {
420 AutoEditActionDataSetter
editActionData(*this, EditAction::eNotEditing
);
421 if (NS_WARN_IF(!editActionData
.CanHandle())) {
424 return IsCopyToClipboardAllowedInternal();
428 * HandleDropEvent() is called from EditorEventListener::Drop that is handler
431 MOZ_CAN_RUN_SCRIPT nsresult
HandleDropEvent(dom::DragEvent
* aDropEvent
);
433 MOZ_CAN_RUN_SCRIPT
virtual nsresult
HandleKeyPressEvent(
434 WidgetKeyboardEvent
* aKeyboardEvent
);
436 virtual dom::EventTarget
* GetDOMEventTarget() const = 0;
439 * OnCompositionStart() is called when editor receives eCompositionStart
440 * event which should be handled in this editor.
442 nsresult
OnCompositionStart(WidgetCompositionEvent
& aCompositionStartEvent
);
445 * OnCompositionChange() is called when editor receives an eCompositioChange
446 * event which should be handled in this editor.
448 * @param aCompositionChangeEvent eCompositionChange event which should
449 * be handled in this editor.
451 MOZ_CAN_RUN_SCRIPT nsresult
452 OnCompositionChange(WidgetCompositionEvent
& aCompositionChangeEvent
);
455 * OnCompositionEnd() is called when editor receives an eCompositionChange
456 * event and it's followed by eCompositionEnd event and after
457 * OnCompositionChange() is called.
459 MOZ_CAN_RUN_SCRIPT
void OnCompositionEnd(
460 WidgetCompositionEvent
& aCompositionEndEvent
);
463 * Accessor methods to flags.
465 uint32_t Flags() const { return mFlags
; }
467 MOZ_CAN_RUN_SCRIPT nsresult
AddFlags(uint32_t aFlags
) {
468 const uint32_t kOldFlags
= Flags();
469 const uint32_t kNewFlags
= (kOldFlags
| aFlags
);
470 if (kNewFlags
== kOldFlags
) {
473 return SetFlags(kNewFlags
); // virtual call and may be expensive.
475 MOZ_CAN_RUN_SCRIPT nsresult
RemoveFlags(uint32_t aFlags
) {
476 const uint32_t kOldFlags
= Flags();
477 const uint32_t kNewFlags
= (kOldFlags
& ~aFlags
);
478 if (kNewFlags
== kOldFlags
) {
481 return SetFlags(kNewFlags
); // virtual call and may be expensive.
483 MOZ_CAN_RUN_SCRIPT nsresult
AddAndRemoveFlags(uint32_t aAddingFlags
,
484 uint32_t aRemovingFlags
) {
485 MOZ_ASSERT(!(aAddingFlags
& aRemovingFlags
),
486 "Same flags are specified both adding and removing");
487 const uint32_t kOldFlags
= Flags();
488 const uint32_t kNewFlags
= ((kOldFlags
| aAddingFlags
) & ~aRemovingFlags
);
489 if (kNewFlags
== kOldFlags
) {
492 return SetFlags(kNewFlags
); // virtual call and may be expensive.
495 bool IsSingleLineEditor() const {
496 const bool isSingleLineEditor
=
497 (mFlags
& nsIEditor::eEditorSingleLineMask
) != 0;
498 MOZ_ASSERT_IF(isSingleLineEditor
, IsTextEditor());
499 return isSingleLineEditor
;
502 bool IsPasswordEditor() const {
503 const bool isPasswordEditor
=
504 (mFlags
& nsIEditor::eEditorPasswordMask
) != 0;
505 MOZ_ASSERT_IF(isPasswordEditor
, IsTextEditor());
506 return isPasswordEditor
;
509 // FYI: Both IsRightToLeft() and IsLeftToRight() may return false if
510 // the editor inherits the content node's direction.
511 bool IsRightToLeft() const {
512 return (mFlags
& nsIEditor::eEditorRightToLeft
) != 0;
514 bool IsLeftToRight() const {
515 return (mFlags
& nsIEditor::eEditorLeftToRight
) != 0;
518 bool IsReadonly() const {
519 return (mFlags
& nsIEditor::eEditorReadonlyMask
) != 0;
522 bool IsMailEditor() const {
523 return (mFlags
& nsIEditor::eEditorMailMask
) != 0;
526 bool IsInteractionAllowed() const {
527 const bool isInteractionAllowed
=
528 (mFlags
& nsIEditor::eEditorAllowInteraction
) != 0;
529 MOZ_ASSERT_IF(isInteractionAllowed
, IsHTMLEditor());
530 return isInteractionAllowed
;
533 bool ShouldSkipSpellCheck() const {
534 return (mFlags
& nsIEditor::eEditorSkipSpellCheck
) != 0;
537 bool HasIndependentSelection() const {
538 MOZ_ASSERT_IF(mSelectionController
, IsTextEditor());
539 return !!mSelectionController
;
542 bool IsModifiable() const { return !IsReadonly(); }
545 * IsInEditSubAction() return true while the instance is handling an edit
546 * sub-action. Otherwise, false.
548 bool IsInEditSubAction() const { return mIsInEditSubAction
; }
551 * IsEmpty() checks whether the editor is empty. If editor has only padding
552 * <br> element for empty editor, returns true. If editor's root element has
553 * non-empty text nodes or other nodes like <br>, returns false.
555 virtual bool IsEmpty() const = 0;
558 * SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching
561 void SuppressDispatchingInputEvent(bool aSuppress
) {
562 mDispatchInputEvent
= !aSuppress
;
566 * IsSuppressingDispatchingInputEvent() returns true if the editor stops
567 * dispatching input event. Otherwise, false.
569 bool IsSuppressingDispatchingInputEvent() const {
570 return !mDispatchInputEvent
;
574 * Returns true if markNodeDirty() has any effect. Returns false if
575 * markNodeDirty() is a no-op.
577 bool OutputsMozDirty() const {
578 // Return true for Composer (!IsInteractionAllowed()) or mail
579 // (IsMailEditor()), but false for webpages.
580 return !IsInteractionAllowed() || IsMailEditor();
584 * Get the focused element, if we're focused. Returns null otherwise.
586 virtual Element
* GetFocusedElement() const;
589 * Whether the aGUIEvent should be handled by this editor or not. When this
590 * returns false, The aGUIEvent shouldn't be handled on this editor,
591 * i.e., The aGUIEvent should be handled by another inner editor or ancestor
594 virtual bool IsAcceptableInputEvent(WidgetGUIEvent
* aGUIEvent
) const;
597 * FindSelectionRoot() returns a selection root of this editor when aNode
598 * gets focus. aNode must be a content node or a document node. When the
599 * target isn't a part of this editor, returns nullptr. If this is for
600 * designMode, you should set the document node to aNode except that an
601 * element in the document has focus.
603 [[nodiscard
]] virtual Element
* FindSelectionRoot(const nsINode
& aNode
) const;
606 * OnFocus() is called when we get a focus event.
608 * @param aOriginalEventTargetNode The original event target node of the
611 MOZ_CAN_RUN_SCRIPT
virtual nsresult
OnFocus(
612 const nsINode
& aOriginalEventTargetNode
);
615 * OnBlur() is called when we're blurred.
617 * @param aEventTarget The event target of the blur event.
619 virtual nsresult
OnBlur(const dom::EventTarget
* aEventTarget
) = 0;
621 /** Resyncs spellchecking state (enabled/disabled). This should be called
622 * when anything that affects spellchecking state changes, such as the
623 * spellcheck attribute value.
625 void SyncRealTimeSpell();
630 * @param aPrincipal If you know current context is subject
631 * principal or system principal, set it.
632 * When nullptr, this checks it automatically.
634 MOZ_CAN_RUN_SCRIPT nsresult
CutAsAction(nsIPrincipal
* aPrincipal
= nullptr);
637 * CanPaste() returns true if user can paste something at current selection.
639 virtual bool CanPaste(nsIClipboard::ClipboardType aClipboardType
) const = 0;
642 * Do "undo" or "redo".
644 * @param aCount How many count of transactions should be
646 * @param aPrincipal Set subject principal if it may be called by
647 * JS. If set to nullptr, will be treated as
650 MOZ_CAN_RUN_SCRIPT nsresult
UndoAsAction(uint32_t aCount
,
651 nsIPrincipal
* aPrincipal
= nullptr);
652 MOZ_CAN_RUN_SCRIPT nsresult
RedoAsAction(uint32_t aCount
,
653 nsIPrincipal
* aPrincipal
= nullptr);
656 * InsertTextAsAction() inserts aStringToInsert at selection.
657 * Although this method is implementation of nsIEditor.insertText(),
658 * this treats the input is an edit action. If you'd like to insert text
659 * as part of edit action, you probably should use InsertTextAsSubAction().
661 * @param aStringToInsert The string to insert.
662 * @param aPrincipal Set subject principal if it may be called by
663 * JS. If set to nullptr, will be treated as
666 MOZ_CAN_RUN_SCRIPT nsresult
InsertTextAsAction(
667 const nsAString
& aStringToInsert
, nsIPrincipal
* aPrincipal
= nullptr);
670 * InsertLineBreakAsAction() is called when user inputs a line break with
671 * Enter or something. If the instance is `HTMLEditor`, this is called
672 * when Shift + Enter or "insertlinebreak" command.
674 * @param aPrincipal Set subject principal if it may be called by
675 * JS. If set to nullptr, will be treated as
678 MOZ_CAN_RUN_SCRIPT
virtual nsresult
InsertLineBreakAsAction(
679 nsIPrincipal
* aPrincipal
= nullptr) = 0;
682 * CanDeleteSelection() returns true if `Selection` is not collapsed and
683 * it's allowed to be removed.
685 bool CanDeleteSelection() const {
686 AutoEditActionDataSetter
editActionData(*this, EditAction::eNotEditing
);
687 if (NS_WARN_IF(!editActionData
.CanHandle())) {
690 return IsModifiable() && !SelectionRef().IsCollapsed();
694 * DeleteSelectionAsAction() removes selection content or content around
695 * caret with transactions. This should be used for handling it as an
696 * edit action. If you'd like to remove selection for preparing to insert
697 * something, you probably should use DeleteSelectionAsSubAction().
699 * @param aDirectionAndAmount How much range should be removed.
700 * @param aStripWrappers Whether the parent blocks should be removed
701 * when they become empty.
702 * @param aPrincipal Set subject principal if it may be called by
703 * JS. If set to nullptr, will be treated as
706 MOZ_CAN_RUN_SCRIPT nsresult
707 DeleteSelectionAsAction(nsIEditor::EDirection aDirectionAndAmount
,
708 nsIEditor::EStripWrappers aStripWrappers
,
709 nsIPrincipal
* aPrincipal
= nullptr);
711 enum class AllowBeforeInputEventCancelable
{
716 enum class PreventSetSelection
{
722 * Replace text in aReplaceRange or all text in this editor with aString and
723 * treat the change as inserting the string.
725 * @param aString The string to set.
726 * @param aReplaceRange The range to be replaced.
727 * If nullptr, all contents will be replaced.
728 * NOTE: Currently, nullptr is not allowed if
729 * the editor is an HTMLEditor.
730 * @param aAllowBeforeInputEventCancelable
731 * Whether `beforeinput` event which will be
732 * dispatched for this can be cancelable or not.
733 * @param aPreventSetSelection
734 * Whether setting selection after replacing text.
735 * If No, selection is the tail of replaced text.
736 * If Yes, selection isn't changed.
737 * @param aPrincipal Set subject principal if it may be called by
738 * JS. If set to nullptr, will be treated as
741 MOZ_CAN_RUN_SCRIPT nsresult
ReplaceTextAsAction(
742 const nsAString
& aString
, nsRange
* aReplaceRange
,
743 AllowBeforeInputEventCancelable aAllowBeforeInputEventCancelable
,
744 PreventSetSelection aPreventSetSelection
= PreventSetSelection::No
,
745 nsIPrincipal
* aPrincipal
= nullptr);
748 * Can we paste |aTransferable| or, if |aTransferable| is null, will a call
749 * to pasteTransferable later possibly succeed if given an instance of
750 * nsITransferable then? True if the doc is modifiable, and, if
751 * |aTransfeable| is non-null, we have pasteable data in |aTransfeable|.
753 virtual bool CanPasteTransferable(nsITransferable
* aTransferable
) = 0;
756 * PasteAsAction() pastes clipboard content to Selection. This method
757 * may dispatch ePaste event first. If its defaultPrevent() is called,
758 * this does nothing but returns NS_OK.
760 * @param aClipboardType nsIClipboard::kGlobalClipboard or
761 * nsIClipboard::kSelectionClipboard.
762 * @param aDispatchPasteEvent Yes if this should dispatch ePaste event
763 * before pasting. Otherwise, No.
764 * @param aDataTransfer The object containing the data to use for the
765 * paste operation. May be nullptr, in which case
766 * this will just get the data from the clipboard.
767 * @param aPrincipal Set subject principal if it may be called by
768 * JS. If set to nullptr, will be treated as
771 enum class DispatchPasteEvent
{ No
, Yes
};
772 MOZ_CAN_RUN_SCRIPT nsresult
773 PasteAsAction(nsIClipboard::ClipboardType aClipboardType
,
774 DispatchPasteEvent aDispatchPasteEvent
,
775 DataTransfer
* aDataTransfer
= nullptr,
776 nsIPrincipal
* aPrincipal
= nullptr);
779 * Paste aTransferable at Selection.
781 * @param aTransferable Must not be nullptr.
782 * @param aDispatchPasteEvent Yes if this should dispatch ePaste event
783 * before pasting. Otherwise, No.
784 * @param aPrincipal Set subject principal if it may be called by
785 * JS. If set to nullptr, will be treated as
788 MOZ_CAN_RUN_SCRIPT nsresult
PasteTransferableAsAction(
789 nsITransferable
* aTransferable
, DispatchPasteEvent aDispatchPasteEvent
,
790 nsIPrincipal
* aPrincipal
= nullptr);
793 * PasteAsQuotationAsAction() pastes content in clipboard as quotation.
794 * If the editor is TextEditor or in plaintext mode, will paste the content
795 * with appending ">" to start of each line.
796 * if the editor is HTMLEditor and is not in plaintext mode, will patste it
797 * into newly created blockquote element.
799 * @param aClipboardType nsIClipboard::kGlobalClipboard or
800 * nsIClipboard::kSelectionClipboard.
801 * @param aDispatchPasteEvent Yes if this should dispatch ePaste event
802 * before pasting. Otherwise, No.
803 * @param aDataTransfer The object containing the data to use for the
804 * paste operation. May be nullptr, in which case
805 * this will just get the data from the clipboard.
806 * @param aPrincipal Set subject principal if it may be called by
807 * JS. If set to nullptr, will be treated as
810 MOZ_CAN_RUN_SCRIPT nsresult
811 PasteAsQuotationAsAction(nsIClipboard::ClipboardType aClipboardType
,
812 DispatchPasteEvent aDispatchPasteEvent
,
813 DataTransfer
* aDataTransfer
= nullptr,
814 nsIPrincipal
* aPrincipal
= nullptr);
817 * Return true if `beforeinput` or `input` event is being dispatched.
819 [[nodiscard
]] bool IsDispatchingInputEvent() const {
820 return mEditActionData
&& mEditActionData
->IsDispatchingInputEvent();
823 protected: // May be used by friends.
824 class AutoEditActionDataSetter
;
827 * TopLevelEditSubActionData stores temporary data while we're handling
828 * top-level edit sub-action.
830 struct MOZ_STACK_CLASS TopLevelEditSubActionData final
{
831 friend class AutoEditActionDataSetter
;
833 // Set selected range before edit. Then, RangeUpdater keep modifying
834 // the range while we're changing the DOM tree.
835 RefPtr
<RangeItem
> mSelectedRange
;
837 // Computing changed range while we're handling sub actions.
838 RefPtr
<nsRange
> mChangedRange
;
840 // XXX In strict speaking, mCachedPendingStyles isn't enough to cache
841 // inline styles because inline style can be specified with "style"
842 // attribute and/or CSS in <style> elements or CSS files. So, we need
843 // to look for better implementation about this.
844 // FYI: Initialization cost of AutoPendingStyleCacheArray is expensive and
845 // it is not used by TextEditor so that we should construct it only
846 // when we're an HTMLEditor.
847 Maybe
<AutoPendingStyleCacheArray
> mCachedPendingStyles
;
849 // If we tried to delete selection, set to true.
850 bool mDidDeleteSelection
;
852 // If we have explicitly set selection inter line, set to true.
853 // `AfterEdit()` or something shouldn't overwrite it in such case.
854 bool mDidExplicitlySetInterLine
;
856 // If we have deleted non-collapsed range set to true, there are only 2
858 // - non-collapsed range was selected.
859 // - selection was collapsed in a text node and a Unicode character
861 bool mDidDeleteNonCollapsedRange
;
863 // If we have deleted parent empty blocks, set to true.
864 bool mDidDeleteEmptyParentBlocks
;
866 // If we're a contenteditable editor, we temporarily increase edit count
867 // of the document between `BeforeEdit()` and `AfterEdit()`. I.e., if
868 // we increased the count in `BeforeEdit()`, we need to decrease it in
869 // `AfterEdit()`, however, the document may be changed to designMode or
870 // non-editable. Therefore, we need to store with this whether we need
872 bool mRestoreContentEditableCount
;
874 // If we explicitly normalized whitespaces around the changed range,
876 bool mDidNormalizeWhitespaces
;
878 // Set to true by default. If somebody inserts an HTML fragment
879 // intentionally, any empty elements shouldn't be cleaned up later. In the
880 // case this is set to false.
881 // TODO: We should not do this by default. If it's necessary, each edit
882 // action handler do it by itself instead. Then, we can avoid such
883 // unnecessary DOM tree scan.
884 bool mNeedsToCleanUpEmptyElements
;
887 * The following methods modifies some data of this struct and
888 * `EditSubActionData` struct. Currently, these are required only
889 * by `HTMLEditor`. Therefore, for cutting the runtime cost of
890 * `TextEditor`, these methods should be called only by `HTMLEditor`.
891 * But it's fine to use these methods in `TextEditor` if necessary.
892 * If so, you need to call `DidDeleteText()` and `DidInsertText()`
893 * from `SetTextNodeWithoutTransaction()`.
895 void DidCreateElement(EditorBase
& aEditorBase
, Element
& aNewElement
);
896 void DidInsertContent(EditorBase
& aEditorBase
, nsIContent
& aNewContent
);
897 void WillDeleteContent(EditorBase
& aEditorBase
,
898 nsIContent
& aRemovingContent
);
899 void DidSplitContent(EditorBase
& aEditorBase
, nsIContent
& aSplitContent
,
900 nsIContent
& aNewContent
);
901 void DidJoinContents(EditorBase
& aEditorBase
,
902 const EditorRawDOMPoint
& aJoinedPoint
);
903 void DidInsertText(EditorBase
& aEditorBase
,
904 const EditorRawDOMPoint
& aInsertionBegin
,
905 const EditorRawDOMPoint
& aInsertionEnd
);
906 void DidDeleteText(EditorBase
& aEditorBase
,
907 const EditorRawDOMPoint
& aStartInTextNode
);
908 void WillDeleteRange(EditorBase
& aEditorBase
,
909 const EditorRawDOMPoint
& aStart
,
910 const EditorRawDOMPoint
& aEnd
);
914 mDidExplicitlySetInterLine
= false;
915 // We don't need to clear other members which are referred only when the
916 // editor is an HTML editor anymore. Note that if `mSelectedRange` is
917 // non-nullptr, that means that we're in `HTMLEditor`.
918 if (!mSelectedRange
) {
921 mSelectedRange
->Clear();
922 mChangedRange
->Reset();
923 if (mCachedPendingStyles
.isSome()) {
924 mCachedPendingStyles
->Clear();
926 mDidDeleteSelection
= false;
927 mDidDeleteNonCollapsedRange
= false;
928 mDidDeleteEmptyParentBlocks
= false;
929 mRestoreContentEditableCount
= false;
930 mDidNormalizeWhitespaces
= false;
931 mNeedsToCleanUpEmptyElements
= true;
935 * Extend mChangedRange to include `aNode`.
937 nsresult
AddNodeToChangedRange(const HTMLEditor
& aHTMLEditor
,
941 * Extend mChangedRange to include `aPoint`.
943 nsresult
AddPointToChangedRange(const HTMLEditor
& aHTMLEditor
,
944 const EditorRawDOMPoint
& aPoint
);
947 * Extend mChangedRange to include `aStart` and `aEnd`.
949 nsresult
AddRangeToChangedRange(const HTMLEditor
& aHTMLEditor
,
950 const EditorRawDOMPoint
& aStart
,
951 const EditorRawDOMPoint
& aEnd
);
953 TopLevelEditSubActionData() = default;
954 TopLevelEditSubActionData(const TopLevelEditSubActionData
& aOther
) = delete;
957 struct MOZ_STACK_CLASS EditSubActionData final
{
958 // While this is set to false, TopLevelEditSubActionData::mChangedRange
959 // shouldn't be modified since in some cases, modifying it in the setter
960 // itself may be faster. Note that we should affect this only for current
961 // edit sub action since mutation event listener may edit different range.
962 bool mAdjustChangedRangeFromListener
;
965 void Clear() { mAdjustChangedRangeFromListener
= true; }
970 protected: // AutoEditActionDataSetter, this shouldn't be accessed by friends.
972 * SettingDataTransfer enum class is used to specify whether DataTransfer
973 * should be initialized with or without format. For example, when user
974 * uses Accel + Shift + V to paste text without format, DataTransfer should
975 * have only plain/text data to make web apps treat it without format.
977 enum class SettingDataTransfer
{
983 * AutoEditActionDataSetter grabs some necessary objects for handling any
984 * edit actions and store the edit action what we're handling. When this is
985 * created, its pointer is set to the mEditActionData, and this guarantees
986 * the lifetime of grabbing objects until it's destroyed.
988 class MOZ_STACK_CLASS AutoEditActionDataSetter final
{
990 // NOTE: aPrincipal will be used when we implement "beforeinput" event.
991 // It's set only when maybe we shouldn't dispatch it because of
992 // called by JS. I.e., if this is nullptr, we can always dispatch
994 AutoEditActionDataSetter(const EditorBase
& aEditorBase
,
995 EditAction aEditAction
,
996 nsIPrincipal
* aPrincipal
= nullptr);
997 ~AutoEditActionDataSetter();
999 void SetSelectionCreatedByDoubleclick(bool aSelectionCreatedByDoubleclick
) {
1000 mSelectionCreatedByDoubleclick
= aSelectionCreatedByDoubleclick
;
1003 [[nodiscard
]] bool SelectionCreatedByDoubleclick() const {
1004 return mSelectionCreatedByDoubleclick
;
1007 void UpdateEditAction(EditAction aEditAction
) {
1008 MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent
,
1009 "It's too late to update EditAction since this may have "
1010 "already dispatched a beforeinput event");
1011 mEditAction
= aEditAction
;
1015 * CanHandle() or CanHandleAndHandleBeforeInput() must be called
1016 * immediately after creating the instance. If caller does not need to
1017 * handle "beforeinput" event or caller needs to set additional information
1018 * the events later, use the former. Otherwise, use the latter. If caller
1019 * uses the former, it's required to call MaybeDispatchBeforeInputEvent() by
1023 [[nodiscard
]] bool CanHandle() const {
1025 mHasCanHandleChecked
= true;
1026 #endif // #ifdefn DEBUG
1027 // Don't allow to run new edit action when an edit action caused
1028 // destroying the editor while it's being handled.
1029 if (mEditAction
!= EditAction::eInitializing
&&
1030 mEditorWasDestroyedDuringHandlingEditAction
) {
1031 NS_WARNING("Editor was destroyed during an edit action being handled");
1034 return IsDataAvailable();
1036 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1037 CanHandleAndMaybeDispatchBeforeInputEvent() {
1038 if (MOZ_UNLIKELY(NS_WARN_IF(!CanHandle()))) {
1039 return NS_ERROR_NOT_INITIALIZED
;
1041 nsresult rv
= MaybeFlushPendingNotifications();
1042 if (MOZ_UNLIKELY(NS_FAILED(rv
))) {
1045 return MaybeDispatchBeforeInputEvent();
1047 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1048 CanHandleAndFlushPendingNotifications() {
1049 if (MOZ_UNLIKELY(NS_WARN_IF(!CanHandle()))) {
1050 return NS_ERROR_NOT_INITIALIZED
;
1052 MOZ_ASSERT(MayEditActionRequireLayout(mRawEditAction
));
1053 return MaybeFlushPendingNotifications();
1056 [[nodiscard
]] bool IsDataAvailable() const {
1057 return mSelection
&& mEditorBase
.mDocument
;
1061 * MaybeDispatchBeforeInputEvent() considers whether this instance needs to
1062 * dispatch "beforeinput" event or not. Then,
1063 * mHasTriedToDispatchBeforeInputEvent is set to true.
1065 * @param aDeleteDirectionAndAmount
1066 * If `MayEditActionDeleteAroundCollapsedSelection(
1067 * mEditAction)` returns true, this must be set.
1068 * Otherwise, don't set explicitly.
1069 * @return If this method actually dispatches "beforeinput" event
1070 * and it's canceled, returns
1071 * NS_ERROR_EDITOR_ACTION_CANCELED.
1073 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeDispatchBeforeInputEvent(
1074 nsIEditor::EDirection aDeleteDirectionAndAmount
= nsIEditor::eNone
);
1077 * MarkAsBeforeInputHasBeenDispatched() should be called only when updating
1078 * the DOM occurs asynchronously from user input (e.g., inserting blob
1079 * object which is loaded asynchronously) and `beforeinput` has already
1080 * been dispatched (always should be so).
1082 void MarkAsBeforeInputHasBeenDispatched() {
1083 MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent());
1084 MOZ_ASSERT(mEditAction
== EditAction::ePaste
||
1085 mEditAction
== EditAction::ePasteAsQuotation
||
1086 mEditAction
== EditAction::eDrop
);
1087 mHasTriedToDispatchBeforeInputEvent
= true;
1091 * MarkAsHandled() is called before dispatching `input` event and notifying
1092 * editor observers. After this is called, any nested edit action become
1095 void MarkAsHandled() {
1096 MOZ_ASSERT(!mHandled
);
1101 * ShouldAlreadyHaveHandledBeforeInputEventDispatching() returns true if the
1102 * edit action requires to handle "beforeinput" event but not yet dispatched
1103 * it nor considered as not dispatched it and can dispatch it when this is
1106 bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const {
1107 return !HasTriedToDispatchBeforeInputEvent() &&
1108 NeedsBeforeInputEventHandling(mEditAction
) &&
1109 IsBeforeInputEventEnabled() /* &&
1110 // If we still need to dispatch a clipboard event, we should
1111 // dispatch it first, then, we need to dispatch beforeinput
1113 !NeedsToDispatchClipboardEvent()*/
1118 * HasTriedToDispatchBeforeInputEvent() returns true if the instance's
1119 * MaybeDispatchBeforeInputEvent() has already been called.
1121 bool HasTriedToDispatchBeforeInputEvent() const {
1122 return mHasTriedToDispatchBeforeInputEvent
;
1125 bool IsCanceled() const { return mBeforeInputEventCanceled
; }
1128 * Returns a `Selection` for normal selection. The lifetime is guaranteed
1129 * during alive this instance in the stack.
1131 MOZ_KNOWN_LIVE Selection
& SelectionRef() const {
1132 MOZ_ASSERT(!mSelection
||
1133 (mSelection
->GetType() == SelectionType::eNormal
));
1137 nsIPrincipal
* GetPrincipal() const { return mPrincipal
; }
1138 EditAction
GetEditAction() const { return mEditAction
; }
1140 template <typename PT
, typename CT
>
1141 void SetSpellCheckRestartPoint(const EditorDOMPointBase
<PT
, CT
>& aPoint
) {
1142 MOZ_ASSERT(aPoint
.IsSet());
1143 // We should store only container and offset because new content may
1144 // be inserted before referring child.
1145 // XXX Shouldn't we compare whether aPoint is before
1146 // mSpellCheckRestartPoint if it's set.
1147 mSpellCheckRestartPoint
=
1148 EditorDOMPoint(aPoint
.GetContainer(), aPoint
.Offset());
1150 void ClearSpellCheckRestartPoint() { mSpellCheckRestartPoint
.Clear(); }
1151 const EditorDOMPoint
& GetSpellCheckRestartPoint() const {
1152 return mSpellCheckRestartPoint
;
1155 void SetData(const nsAString
& aData
) {
1156 MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent
,
1157 "It's too late to set data since this may have already "
1158 "dispatched a beforeinput event");
1161 const nsString
& GetData() const { return mData
; }
1163 void SetColorData(const nsAString
& aData
);
1166 * InitializeDataTransfer(DataTransfer*) sets mDataTransfer to
1167 * aDataTransfer. In this case, aDataTransfer should not be read/write
1168 * because it'll be set to InputEvent.dataTransfer and which should be
1171 void InitializeDataTransfer(DataTransfer
* aDataTransfer
);
1173 * InitializeDataTransfer(nsITransferable*) creates new DataTransfer
1174 * instance, initializes it with aTransferable and sets mDataTransfer to
1177 void InitializeDataTransfer(nsITransferable
* aTransferable
);
1179 * InitializeDataTransfer(const nsAString&) creates new DataTransfer
1180 * instance, initializes it with aString and sets mDataTransfer to it.
1182 void InitializeDataTransfer(const nsAString
& aString
);
1184 * InitializeDataTransferWithClipboard() creates new DataTransfer instance,
1185 * initializes it with clipboard and sets mDataTransfer to it.
1187 void InitializeDataTransferWithClipboard(
1188 SettingDataTransfer aSettingDataTransfer
, DataTransfer
* aDataTransfer
,
1189 nsIClipboard::ClipboardType aClipboardType
);
1190 DataTransfer
* GetDataTransfer() const { return mDataTransfer
; }
1193 * AppendTargetRange() appends aTargetRange to target ranges. This should
1194 * be used only by edit action handlers which do not want to set target
1195 * ranges to selection ranges.
1197 void AppendTargetRange(dom::StaticRange
& aTargetRange
);
1200 * Make dispatching `beforeinput` forcibly non-cancelable.
1202 void MakeBeforeInputEventNonCancelable() {
1203 mMakeBeforeInputEventNonCancelable
= true;
1207 * NotifyOfDispatchingClipboardEvent() is called after dispatching
1208 * a clipboard event.
1210 void NotifyOfDispatchingClipboardEvent() {
1211 MOZ_ASSERT(NeedsToDispatchClipboardEvent());
1212 MOZ_ASSERT(!mHasTriedToDispatchClipboardEvent
);
1213 mHasTriedToDispatchClipboardEvent
= true;
1216 void Abort() { mAborted
= true; }
1217 bool IsAborted() const { return mAborted
; }
1219 void OnEditorDestroy() {
1220 if (!mHandled
&& mHasTriedToDispatchBeforeInputEvent
) {
1221 // Remember the editor was destroyed only when this edit action is being
1222 // handled because they are caused by mutation event listeners or
1223 // something other unexpected event listeners. In the cases, new child
1224 // edit action shouldn't been aborted.
1225 mEditorWasDestroyedDuringHandlingEditAction
= true;
1228 mParentData
->OnEditorDestroy();
1231 bool HasEditorDestroyedDuringHandlingEditAction() const {
1232 return mEditorWasDestroyedDuringHandlingEditAction
;
1235 void SetTopLevelEditSubAction(EditSubAction aEditSubAction
,
1236 EDirection aDirection
= eNone
) {
1237 mTopLevelEditSubAction
= aEditSubAction
;
1238 TopLevelEditSubActionDataRef().Clear();
1239 switch (mTopLevelEditSubAction
) {
1240 case EditSubAction::eInsertNode
:
1241 case EditSubAction::eMoveNode
:
1242 case EditSubAction::eCreateNode
:
1243 case EditSubAction::eSplitNode
:
1244 case EditSubAction::eInsertText
:
1245 case EditSubAction::eInsertTextComingFromIME
:
1246 case EditSubAction::eSetTextProperty
:
1247 case EditSubAction::eRemoveTextProperty
:
1248 case EditSubAction::eRemoveAllTextProperties
:
1249 case EditSubAction::eSetText
:
1250 case EditSubAction::eInsertLineBreak
:
1251 case EditSubAction::eInsertParagraphSeparator
:
1252 case EditSubAction::eCreateOrChangeList
:
1253 case EditSubAction::eIndent
:
1254 case EditSubAction::eOutdent
:
1255 case EditSubAction::eSetOrClearAlignment
:
1256 case EditSubAction::eCreateOrRemoveBlock
:
1257 case EditSubAction::eFormatBlockForHTMLCommand
:
1258 case EditSubAction::eMergeBlockContents
:
1259 case EditSubAction::eRemoveList
:
1260 case EditSubAction::eCreateOrChangeDefinitionListItem
:
1261 case EditSubAction::eInsertElement
:
1262 case EditSubAction::eInsertQuotation
:
1263 case EditSubAction::eInsertQuotedText
:
1264 case EditSubAction::ePasteHTMLContent
:
1265 case EditSubAction::eInsertHTMLSource
:
1266 case EditSubAction::eSetPositionToAbsolute
:
1267 case EditSubAction::eSetPositionToStatic
:
1268 case EditSubAction::eDecreaseZIndex
:
1269 case EditSubAction::eIncreaseZIndex
:
1270 MOZ_ASSERT(aDirection
== eNext
);
1271 mDirectionOfTopLevelEditSubAction
= eNext
;
1273 case EditSubAction::eJoinNodes
:
1274 case EditSubAction::eDeleteText
:
1275 MOZ_ASSERT(aDirection
== ePrevious
);
1276 mDirectionOfTopLevelEditSubAction
= ePrevious
;
1278 case EditSubAction::eUndo
:
1279 case EditSubAction::eRedo
:
1280 case EditSubAction::eComputeTextToOutput
:
1281 case EditSubAction::eCreatePaddingBRElementForEmptyEditor
:
1282 case EditSubAction::eMaintainWhiteSpaceVisibility
:
1283 case EditSubAction::eNone
:
1284 case EditSubAction::eReplaceHeadWithHTMLSource
:
1285 MOZ_ASSERT(aDirection
== eNone
);
1286 mDirectionOfTopLevelEditSubAction
= eNone
;
1288 case EditSubAction::eDeleteNode
:
1289 case EditSubAction::eDeleteSelectedContent
:
1290 // Unfortunately, eDeleteNode and eDeleteSelectedContent is used with
1291 // any direction. We might have specific sub-action for each
1292 // direction, but there are some points referencing
1293 // eDeleteSelectedContent so that we should keep storing direction
1295 mDirectionOfTopLevelEditSubAction
= aDirection
;
1299 EditSubAction
GetTopLevelEditSubAction() const {
1300 MOZ_ASSERT(IsDataAvailable());
1301 return mTopLevelEditSubAction
;
1303 EDirection
GetDirectionOfTopLevelEditSubAction() const {
1304 return mDirectionOfTopLevelEditSubAction
;
1307 const TopLevelEditSubActionData
& TopLevelEditSubActionDataRef() const {
1308 return mParentData
? mParentData
->TopLevelEditSubActionDataRef()
1309 : mTopLevelEditSubActionData
;
1311 TopLevelEditSubActionData
& TopLevelEditSubActionDataRef() {
1312 return mParentData
? mParentData
->TopLevelEditSubActionDataRef()
1313 : mTopLevelEditSubActionData
;
1316 const EditSubActionData
& EditSubActionDataRef() const {
1317 return mEditSubActionData
;
1319 EditSubActionData
& EditSubActionDataRef() { return mEditSubActionData
; }
1321 SelectionState
& SavedSelectionRef() {
1322 return mParentData
? mParentData
->SavedSelectionRef() : mSavedSelection
;
1324 const SelectionState
& SavedSelectionRef() const {
1325 return mParentData
? mParentData
->SavedSelectionRef() : mSavedSelection
;
1328 RangeUpdater
& RangeUpdaterRef() {
1329 return mParentData
? mParentData
->RangeUpdaterRef() : mRangeUpdater
;
1331 const RangeUpdater
& RangeUpdaterRef() const {
1332 return mParentData
? mParentData
->RangeUpdaterRef() : mRangeUpdater
;
1335 void UpdateSelectionCache(Selection
& aSelection
);
1337 bool IsDispatchingInputEvent() const {
1338 return mDispatchingInputEvent
||
1339 (mParentData
&& mParentData
->IsDispatchingInputEvent());
1341 void WillDispatchInputEvent() {
1342 MOZ_ASSERT(!mDispatchingInputEvent
);
1343 mDispatchingInputEvent
= true;
1345 void DidDispatchInputEvent() {
1346 MOZ_ASSERT(mDispatchingInputEvent
);
1347 mDispatchingInputEvent
= false;
1351 bool IsBeforeInputEventEnabled() const;
1353 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1354 MaybeFlushPendingNotifications() const;
1356 static bool NeedsBeforeInputEventHandling(EditAction aEditAction
) {
1357 MOZ_ASSERT(aEditAction
!= EditAction::eNone
);
1358 switch (aEditAction
) {
1359 case EditAction::eNone
:
1360 // If we're not handling edit action, we don't need to handle
1361 // "beforeinput" event.
1362 case EditAction::eNotEditing
:
1363 // If we're being initialized, we may need to create a padding <br>
1364 // element, but it shouldn't cause `beforeinput` event.
1365 case EditAction::eInitializing
:
1366 // If we're just selecting or getting table cells, we shouldn't
1367 // dispatch `beforeinput` event.
1368 case NS_EDIT_ACTION_CASES_ACCESSING_TABLE_DATA_WITHOUT_EDITING
:
1369 // If raw level transaction API is used, the API user needs to handle
1370 // both "beforeinput" event and "input" event if it's necessary.
1371 case EditAction::eUnknown
:
1372 // Hiding/showing password affects only layout so that we don't need
1373 // to handle beforeinput event for it.
1374 case EditAction::eHidePassword
:
1375 // We don't need to dispatch "beforeinput" event before
1376 // "compositionstart".
1377 case EditAction::eStartComposition
:
1378 // We don't need to let web apps know the mode change.
1379 case EditAction::eEnableOrDisableCSS
:
1380 case EditAction::eEnableOrDisableAbsolutePositionEditor
:
1381 case EditAction::eEnableOrDisableResizer
:
1382 case EditAction::eEnableOrDisableInlineTableEditingUI
:
1383 // We don't need to let contents in chrome's editor to know the size
1385 case EditAction::eSetWrapWidth
:
1386 // While resizing or moving element, we update only shadow, i.e.,
1387 // don't touch to the DOM in content. Therefore, we don't need to
1388 // dispatch "beforeinput" event.
1389 case EditAction::eResizingElement
:
1390 case EditAction::eMovingElement
:
1391 // Perhaps, we don't need to dispatch "beforeinput" event for
1392 // padding `<br>` element for empty editor because it's internal
1393 // handling and it should be occurred by another change.
1394 case EditAction::eCreatePaddingBRElementForEmptyEditor
:
1401 bool NeedsToDispatchClipboardEvent() const {
1402 if (mHasTriedToDispatchClipboardEvent
) {
1405 switch (mEditAction
) {
1406 case EditAction::ePaste
:
1407 case EditAction::ePasteAsQuotation
:
1408 case EditAction::eCut
:
1409 case EditAction::eCopy
:
1416 void MarkEditActionCanceled();
1418 EditorBase
& mEditorBase
;
1419 RefPtr
<Selection
> mSelection
;
1420 nsTArray
<OwningNonNull
<Selection
>> mRetiredSelections
;
1422 // True if the selection was created by doubleclicking a word.
1423 bool mSelectionCreatedByDoubleclick
{false};
1425 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
1426 // EditAction may be nested, for example, a command may be executed
1427 // from mutation event listener which is run while editor changes
1428 // the DOM tree. In such case, we need to handle edit action separately.
1429 AutoEditActionDataSetter
* mParentData
;
1431 // Cached selection for AutoSelectionRestorer.
1432 SelectionState mSavedSelection
;
1434 // Utility class object for maintaining preserved ranges.
1435 RangeUpdater mRangeUpdater
;
1437 // The data should be set to InputEvent.data.
1440 // The dataTransfer should be set to InputEvent.dataTransfer.
1441 RefPtr
<DataTransfer
> mDataTransfer
;
1443 // They are used for result of InputEvent.getTargetRanges() of beforeinput.
1444 OwningNonNullStaticRangeArray mTargetRanges
;
1446 // Start point where spell checker should check from. This is used only
1448 EditorDOMPoint mSpellCheckRestartPoint
;
1450 // Different from mTopLevelEditSubAction, its data should be stored only
1451 // in the most ancestor AutoEditActionDataSetter instance since we don't
1452 // want to pay the copying cost and sync cost.
1453 TopLevelEditSubActionData mTopLevelEditSubActionData
;
1455 // Different from mTopLevelEditSubActionData, this stores temporaly data
1456 // for current edit sub action.
1457 EditSubActionData mEditSubActionData
;
1459 // mEditAction and mRawEditActions stores edit action. The difference of
1460 // them is, if and only if edit actions are nested and parent edit action
1461 // is one of trying to edit something, but nested one is not so, it's
1462 // overwritten by the parent edit action.
1463 EditAction mEditAction
;
1464 EditAction mRawEditAction
;
1466 // Different from its data, you can refer "current" AutoEditActionDataSetter
1467 // instance's mTopLevelEditSubAction member since it's copied from the
1468 // parent instance at construction and it's always cleared before this
1469 // won't be overwritten and cleared before destruction.
1470 EditSubAction mTopLevelEditSubAction
;
1472 EDirection mDirectionOfTopLevelEditSubAction
;
1476 // Set to true when this handles "beforeinput" event dispatching. Note
1477 // that even if "beforeinput" event shouldn't be dispatched for this,
1478 // instance, this is set to true when it's considered.
1479 bool mHasTriedToDispatchBeforeInputEvent
;
1480 // Set to true if "beforeinput" event was dispatched and it's canceled.
1481 bool mBeforeInputEventCanceled
;
1482 // Set to true if `beforeinput` event must not be cancelable even if
1483 // its inputType is defined as cancelable by the standards.
1484 bool mMakeBeforeInputEventNonCancelable
;
1485 // Set to true when the edit action handler tries to dispatch a clipboard
1487 bool mHasTriedToDispatchClipboardEvent
;
1488 // The editor instance may be destroyed once temporarily if `document.write`
1489 // etc runs. In such case, we should mark this flag of being handled
1491 bool mEditorWasDestroyedDuringHandlingEditAction
;
1492 // This is set before dispatching `input` event and notifying editor
1495 // Whether the editor is dispatching a `beforeinput` or `input` event.
1496 bool mDispatchingInputEvent
= false;
1499 mutable bool mHasCanHandleChecked
= false;
1500 #endif // #ifdef DEBUG
1502 AutoEditActionDataSetter() = delete;
1503 AutoEditActionDataSetter(const AutoEditActionDataSetter
& aOther
) = delete;
1506 void UpdateEditActionData(const nsAString
& aData
) {
1507 mEditActionData
->SetData(aData
);
1510 void NotifyOfDispatchingClipboardEvent() {
1511 MOZ_ASSERT(mEditActionData
);
1512 mEditActionData
->NotifyOfDispatchingClipboardEvent();
1515 protected: // May be called by friends.
1516 /****************************************************************************
1517 * Some friend classes are allowed to call the following protected methods.
1518 * However, those methods won't prepare caches of some objects which are
1519 * necessary for them. So, if you call them from friend classes, you need
1520 * to make sure that AutoEditActionDataSetter is created.
1521 ****************************************************************************/
1523 bool IsEditActionCanceled() const {
1524 MOZ_ASSERT(mEditActionData
);
1525 return mEditActionData
->IsCanceled();
1528 bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const {
1529 MOZ_ASSERT(mEditActionData
);
1530 return mEditActionData
1531 ->ShouldAlreadyHaveHandledBeforeInputEventDispatching();
1534 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeDispatchBeforeInputEvent() {
1535 MOZ_ASSERT(mEditActionData
);
1536 return mEditActionData
->MaybeDispatchBeforeInputEvent();
1539 void MarkAsBeforeInputHasBeenDispatched() {
1540 MOZ_ASSERT(mEditActionData
);
1541 return mEditActionData
->MarkAsBeforeInputHasBeenDispatched();
1544 bool HasTriedToDispatchBeforeInputEvent() const {
1545 return mEditActionData
&&
1546 mEditActionData
->HasTriedToDispatchBeforeInputEvent();
1549 bool IsEditActionDataAvailable() const {
1550 return mEditActionData
&& mEditActionData
->IsDataAvailable();
1553 bool IsTopLevelEditSubActionDataAvailable() const {
1554 return mEditActionData
&& !!GetTopLevelEditSubAction();
1557 bool IsEditActionAborted() const {
1558 MOZ_ASSERT(mEditActionData
);
1559 return mEditActionData
->IsAborted();
1562 nsresult
GetDataFromDataTransferOrClipboard(
1563 DataTransfer
* aDataTransfer
, nsITransferable
* aTransferable
,
1564 nsIClipboard::ClipboardType aClipboardType
) const;
1567 * SelectionRef() returns cached normal Selection. This is pretty faster than
1568 * EditorBase::GetSelection() if available.
1569 * Note that this never crash unless public methods ignore the result of
1570 * AutoEditActionDataSetter::CanHandle() and keep handling edit action but any
1571 * methods should stop handling edit action if it returns false.
1573 MOZ_KNOWN_LIVE Selection
& SelectionRef() const {
1574 MOZ_ASSERT(mEditActionData
);
1575 MOZ_ASSERT(mEditActionData
->SelectionRef().GetType() ==
1576 SelectionType::eNormal
);
1577 return mEditActionData
->SelectionRef();
1580 nsIPrincipal
* GetEditActionPrincipal() const {
1581 MOZ_ASSERT(mEditActionData
);
1582 return mEditActionData
->GetPrincipal();
1586 * GetEditAction() returns EditAction which is being handled. If some
1587 * edit actions are nested, this returns the innermost edit action.
1589 EditAction
GetEditAction() const {
1590 return mEditActionData
? mEditActionData
->GetEditAction()
1591 : EditAction::eNone
;
1595 * GetInputEventData() returns inserting or inserted text value with
1596 * current edit action. The result is proper for InputEvent.data value.
1598 const nsString
& GetInputEventData() const {
1599 return mEditActionData
? mEditActionData
->GetData() : VoidString();
1603 * GetInputEventDataTransfer() returns inserting or inserted transferable
1604 * content with current edit action. The result is proper for
1605 * InputEvent.dataTransfer value.
1607 DataTransfer
* GetInputEventDataTransfer() const {
1608 return mEditActionData
? mEditActionData
->GetDataTransfer() : nullptr;
1612 * GetTopLevelEditSubAction() returns the top level edit sub-action.
1613 * For example, if selected content is being replaced with inserted text,
1614 * while removing selected content, the top level edit sub-action may be
1615 * EditSubAction::eDeleteSelectedContent. However, while inserting new
1616 * text, the top level edit sub-action may be EditSubAction::eInsertText.
1617 * So, this result means what we are doing right now unless you're looking
1618 * for a case which the method is called via mutation event listener or
1619 * selectionchange event listener which are fired while handling the edit
1622 EditSubAction
GetTopLevelEditSubAction() const {
1623 return mEditActionData
? mEditActionData
->GetTopLevelEditSubAction()
1624 : EditSubAction::eNone
;
1628 * GetDirectionOfTopLevelEditSubAction() returns direction which user
1629 * intended for doing the edit sub-action.
1631 EDirection
GetDirectionOfTopLevelEditSubAction() const {
1632 return mEditActionData
1633 ? mEditActionData
->GetDirectionOfTopLevelEditSubAction()
1638 * SavedSelection() returns reference to saved selection which are
1639 * stored by AutoSelectionRestorer.
1641 SelectionState
& SavedSelectionRef() {
1642 MOZ_ASSERT(IsEditActionDataAvailable());
1643 return mEditActionData
->SavedSelectionRef();
1645 const SelectionState
& SavedSelectionRef() const {
1646 MOZ_ASSERT(IsEditActionDataAvailable());
1647 return mEditActionData
->SavedSelectionRef();
1650 RangeUpdater
& RangeUpdaterRef() {
1651 MOZ_ASSERT(IsEditActionDataAvailable());
1652 return mEditActionData
->RangeUpdaterRef();
1654 const RangeUpdater
& RangeUpdaterRef() const {
1655 MOZ_ASSERT(IsEditActionDataAvailable());
1656 return mEditActionData
->RangeUpdaterRef();
1659 template <typename PT
, typename CT
>
1660 void SetSpellCheckRestartPoint(const EditorDOMPointBase
<PT
, CT
>& aPoint
) {
1661 MOZ_ASSERT(IsEditActionDataAvailable());
1662 return mEditActionData
->SetSpellCheckRestartPoint(aPoint
);
1665 void ClearSpellCheckRestartPoint() {
1666 MOZ_ASSERT(IsEditActionDataAvailable());
1667 return mEditActionData
->ClearSpellCheckRestartPoint();
1670 const EditorDOMPoint
& GetSpellCheckRestartPoint() const {
1671 MOZ_ASSERT(IsEditActionDataAvailable());
1672 return mEditActionData
->GetSpellCheckRestartPoint();
1675 const TopLevelEditSubActionData
& TopLevelEditSubActionDataRef() const {
1676 MOZ_ASSERT(IsEditActionDataAvailable());
1677 return mEditActionData
->TopLevelEditSubActionDataRef();
1679 TopLevelEditSubActionData
& TopLevelEditSubActionDataRef() {
1680 MOZ_ASSERT(IsEditActionDataAvailable());
1681 return mEditActionData
->TopLevelEditSubActionDataRef();
1684 const EditSubActionData
& EditSubActionDataRef() const {
1685 MOZ_ASSERT(IsEditActionDataAvailable());
1686 return mEditActionData
->EditSubActionDataRef();
1688 EditSubActionData
& EditSubActionDataRef() {
1689 MOZ_ASSERT(IsEditActionDataAvailable());
1690 return mEditActionData
->EditSubActionDataRef();
1694 * GetFirstIMESelectionStartPoint() and GetLastIMESelectionEndPoint() returns
1695 * start of first IME selection range or end of last IME selection range if
1696 * there is. Otherwise, returns non-set DOM point.
1698 template <typename EditorDOMPointType
>
1699 EditorDOMPointType
GetFirstIMESelectionStartPoint() const;
1700 template <typename EditorDOMPointType
>
1701 EditorDOMPointType
GetLastIMESelectionEndPoint() const;
1704 * IsSelectionRangeContainerNotContent() returns true if one of container
1705 * of selection ranges is not a content node, i.e., a Document node.
1707 bool IsSelectionRangeContainerNotContent() const;
1710 * OnInputText() is called when user inputs text with keyboard or something.
1712 * @param aStringToInsert The string to insert.
1714 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1715 OnInputText(const nsAString
& aStringToInsert
);
1718 * InsertTextAsSubAction() inserts aStringToInsert at selection. This
1719 * should be used for handling it as an edit sub-action.
1721 * @param aStringToInsert The string to insert.
1722 * @param aSelectionHandling Specify whether selected content should be
1723 * deleted or ignored.
1725 enum class SelectionHandling
{ Ignore
, Delete
};
1726 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
InsertTextAsSubAction(
1727 const nsAString
& aStringToInsert
, SelectionHandling aSelectionHandling
);
1730 * Insert aStringToInsert to aPointToInsert or better insertion point around
1731 * it. If aPointToInsert isn't in a text node, this method looks for the
1732 * nearest point in a text node with TextEditor::FindBetterInsertionPoint()
1733 * or EditorDOMPoint::GetPointInTextNodeIfPointingAroundTextNode().
1734 * If there is no text node, this creates new text node and put
1735 * aStringToInsert to it.
1737 * @param aDocument The document of this editor.
1738 * @param aStringToInsert The string to insert.
1739 * @param aPointToInsert The point to insert aStringToInsert.
1740 * Must be valid DOM point.
1741 * @param aInsertTextTo Whether forcibly creates a new `Text` node in
1742 * specific condition or use existing `Text` if
1745 enum class InsertTextTo
{
1746 ExistingTextNodeIfAvailable
,
1747 ExistingTextNodeIfAvailableAndNotStart
,
1748 AlwaysCreateNewTextNode
1750 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual Result
<InsertTextResult
, nsresult
>
1751 InsertTextWithTransaction(Document
& aDocument
,
1752 const nsAString
& aStringToInsert
,
1753 const EditorDOMPoint
& aPointToInsert
,
1754 InsertTextTo aInsertTextTo
);
1757 * Insert aStringToInsert to aPointToInsert.
1759 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT Result
<InsertTextResult
, nsresult
>
1760 InsertTextIntoTextNodeWithTransaction(
1761 const nsAString
& aStringToInsert
,
1762 const EditorDOMPointInText
& aPointToInsert
);
1765 * SetTextNodeWithoutTransaction() is optimized path to set new value to
1766 * the text node directly and without transaction. This is used when
1767 * setting `<input>.value` and `<textarea>.value`.
1769 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1770 SetTextNodeWithoutTransaction(const nsAString
& aString
, Text
& aTextNode
);
1773 * DeleteNodeWithTransaction() removes aContent from the DOM tree.
1775 * @param aContent The node which will be removed form the DOM tree.
1777 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1778 DeleteNodeWithTransaction(nsIContent
& aContent
);
1781 * InsertNodeWithTransaction() inserts aContentToInsert before the child
1782 * specified by aPointToInsert.
1784 * @param aContentToInsert The node to be inserted.
1785 * @param aPointToInsert The insertion point of aContentToInsert.
1786 * If this refers end of the container, the
1787 * transaction will append the node to the
1788 * container. Otherwise, will insert the node
1789 * before child node referred by this.
1790 * @return If succeeded, returns the new content node and
1791 * point to put caret.
1793 template <typename ContentNodeType
>
1794 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
1795 Result
<CreateNodeResultBase
<ContentNodeType
>, nsresult
>
1796 InsertNodeWithTransaction(ContentNodeType
& aContentToInsert
,
1797 const EditorDOMPoint
& aPointToInsert
);
1800 * InsertPaddingBRElementForEmptyLastLineWithTransaction() creates a padding
1801 * <br> element with setting flags to NS_PADDING_FOR_EMPTY_LAST_LINE and
1802 * inserts it around aPointToInsert.
1804 * @param aPointToInsert The DOM point where should be <br> node inserted
1806 * @return If succeeded, returns the new <br> element and
1807 * point to put caret around it.
1809 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT Result
<CreateElementResult
, nsresult
>
1810 InsertPaddingBRElementForEmptyLastLineWithTransaction(
1811 const EditorDOMPoint
& aPointToInsert
);
1813 enum class BRElementType
{
1815 PaddingForEmptyEditor
,
1816 PaddingForEmptyLastLine
1819 * Updates the type of aBRElement. If it will be hidden or shown from
1820 * IMEContentObserver and ContentEventHandler points of view, this temporarily
1821 * removes the node and reconnect to the same position.
1823 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1824 UpdateBRElementType(dom::HTMLBRElement
& aBRElement
, BRElementType aNewType
);
1827 * Create and insert a line break to aPointToInsert.
1829 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT Result
<CreateElementResult
, nsresult
>
1830 InsertBRElement(WithTransaction aWithTransaction
,
1831 BRElementType aBRElementType
,
1832 const EditorDOMPoint
& aPointToInsert
);
1835 * CloneAttributesWithTransaction() clones all attributes from
1836 * aSourceElement to aDestElement after removing all attributes in
1839 MOZ_CAN_RUN_SCRIPT
void CloneAttributesWithTransaction(
1840 Element
& aDestElement
, Element
& aSourceElement
);
1843 * CloneAttributeWithTransaction() copies aAttribute of aSourceElement to
1844 * aDestElement. If aSourceElement doesn't have aAttribute, this removes
1845 * aAttribute from aDestElement.
1847 * @param aAttribute Attribute name to be cloned.
1848 * @param aDestElement Element node which will be set aAttribute or
1849 * whose aAttribute will be removed.
1850 * @param aSourceElement Element node which provides the value of
1851 * aAttribute in aDestElement.
1853 MOZ_CAN_RUN_SCRIPT nsresult
CloneAttributeWithTransaction(
1854 nsAtom
& aAttribute
, Element
& aDestElement
, Element
& aSourceElement
);
1857 * RemoveAttributeWithTransaction() removes aAttribute from aElement.
1859 * @param aElement Element node which will lose aAttribute.
1860 * @param aAttribute Attribute name to be removed from aElement.
1862 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1863 RemoveAttributeWithTransaction(Element
& aElement
, nsAtom
& aAttribute
);
1865 MOZ_CAN_RUN_SCRIPT
virtual nsresult
RemoveAttributeOrEquivalent(
1866 Element
* aElement
, nsAtom
* aAttribute
, bool aSuppressTransaction
) = 0;
1869 * SetAttributeWithTransaction() sets aAttribute of aElement to aValue.
1871 * @param aElement Element node which will have aAttribute.
1872 * @param aAttribute Attribute name to be set.
1873 * @param aValue Attribute value be set to aAttribute.
1875 MOZ_CAN_RUN_SCRIPT nsresult
SetAttributeWithTransaction(
1876 Element
& aElement
, nsAtom
& aAttribute
, const nsAString
& aValue
);
1878 MOZ_CAN_RUN_SCRIPT
virtual nsresult
SetAttributeOrEquivalent(
1879 Element
* aElement
, nsAtom
* aAttribute
, const nsAString
& aValue
,
1880 bool aSuppressTransaction
) = 0;
1883 * Method to replace certain CreateElementNS() calls.
1885 * @param aTag Tag you want.
1887 already_AddRefed
<Element
> CreateHTMLContent(const nsAtom
* aTag
) const;
1890 * Creates text node which is marked as "maybe modified frequently" and
1891 * "maybe masked" if this is a password editor.
1893 already_AddRefed
<nsTextNode
> CreateTextNode(const nsAString
& aData
) const;
1896 * DoInsertText(), DoDeleteText(), DoReplaceText() and DoSetText() are
1897 * wrapper of `CharacterData::InsertData()`, `CharacterData::DeleteData()`,
1898 * `CharacterData::ReplaceData()` and `CharacterData::SetData()`.
1900 MOZ_CAN_RUN_SCRIPT
void DoInsertText(dom::Text
& aText
, uint32_t aOffset
,
1901 const nsAString
& aStringToInsert
,
1903 MOZ_CAN_RUN_SCRIPT
void DoDeleteText(dom::Text
& aText
, uint32_t aOffset
,
1904 uint32_t aCount
, ErrorResult
& aRv
);
1905 MOZ_CAN_RUN_SCRIPT
void DoReplaceText(dom::Text
& aText
, uint32_t aOffset
,
1907 const nsAString
& aStringToInsert
,
1909 MOZ_CAN_RUN_SCRIPT
void DoSetText(dom::Text
& aText
,
1910 const nsAString
& aStringToSet
,
1914 * Delete text in the range in aTextNode. Use
1915 * `HTMLEditor::ReplaceTextWithTransaction` if you'll insert text there (and
1916 * if you want to use it in `TextEditor`, move it into `EditorBase`).
1918 * @param aTextNode The text node which should be modified.
1919 * @param aOffset Start offset of removing text in aTextNode.
1920 * @param aLength Length of removing text.
1922 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT Result
<CaretPoint
, nsresult
>
1923 DeleteTextWithTransaction(dom::Text
& aTextNode
, uint32_t aOffset
,
1927 * MarkElementDirty() sets a special dirty attribute on the element.
1928 * Usually this will be called immediately after creating a new node.
1930 * @param aElement The element for which to insert formatting.
1932 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1933 MarkElementDirty(Element
& aElement
) const;
1935 MOZ_CAN_RUN_SCRIPT nsresult
1936 DoTransactionInternal(nsITransaction
* aTransaction
);
1939 * Returns true if aNode is our root node. The root is:
1940 * If TextEditor, the anonymous <div> element.
1941 * If HTMLEditor, a <body> element or the document element which may not be
1942 * editable if it's not in the design mode.
1944 bool IsRoot(const nsINode
* inNode
) const;
1947 * Returns true if aNode is a descendant of our root node.
1948 * See the comment for IsRoot() for what the root node means.
1950 bool IsDescendantOfRoot(const nsINode
* inNode
) const;
1953 * Returns true when inserting text should be a part of current composition.
1955 bool ShouldHandleIMEComposition() const;
1957 template <typename EditorDOMPointType
>
1958 EditorDOMPointType
GetFirstSelectionStartPoint() const;
1959 template <typename EditorDOMPointType
>
1960 EditorDOMPointType
GetFirstSelectionEndPoint() const;
1962 static nsresult
GetEndChildNode(const Selection
& aSelection
,
1963 nsIContent
** aEndNode
);
1965 template <typename PT
, typename CT
>
1966 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1967 CollapseSelectionTo(const EditorDOMPointBase
<PT
, CT
>& aPoint
) const {
1968 // We don't need to throw exception directly for a failure of updating
1969 // selection. Therefore, let's use IgnoredErrorResult for the performance.
1970 IgnoredErrorResult error
;
1971 CollapseSelectionTo(aPoint
, error
);
1972 return error
.StealNSResult();
1975 template <typename PT
, typename CT
>
1976 MOZ_CAN_RUN_SCRIPT
void CollapseSelectionTo(
1977 const EditorDOMPointBase
<PT
, CT
>& aPoint
, ErrorResult
& aRv
) const {
1978 MOZ_ASSERT(IsEditActionDataAvailable());
1979 MOZ_ASSERT(!aRv
.Failed());
1981 if (aPoint
.GetInterlinePosition() != InterlinePosition::Undefined
) {
1982 if (MOZ_UNLIKELY(NS_FAILED(SelectionRef().SetInterlinePosition(
1983 aPoint
.GetInterlinePosition())))) {
1984 NS_WARNING("Selection::SetInterlinePosition() failed");
1985 aRv
.Throw(NS_ERROR_FAILURE
);
1990 SelectionRef().CollapseInLimiter(aPoint
, aRv
);
1991 if (MOZ_UNLIKELY(Destroyed())) {
1992 NS_WARNING("Selection::CollapseInLimiter() caused destroying the editor");
1993 aRv
.Throw(NS_ERROR_EDITOR_DESTROYED
);
1996 NS_WARNING_ASSERTION(!aRv
.Failed(),
1997 "Selection::CollapseInLimiter() failed");
2000 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2001 CollapseSelectionToStartOf(nsINode
& aNode
) const {
2002 return CollapseSelectionTo(EditorRawDOMPoint(&aNode
, 0u));
2005 MOZ_CAN_RUN_SCRIPT
void CollapseSelectionToStartOf(nsINode
& aNode
,
2006 ErrorResult
& aRv
) const {
2007 CollapseSelectionTo(EditorRawDOMPoint(&aNode
, 0u), aRv
);
2010 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2011 CollapseSelectionToEndOf(nsINode
& aNode
) const {
2012 return CollapseSelectionTo(EditorRawDOMPoint::AtEndOf(aNode
));
2015 MOZ_CAN_RUN_SCRIPT
void CollapseSelectionToEndOf(nsINode
& aNode
,
2016 ErrorResult
& aRv
) const {
2017 CollapseSelectionTo(EditorRawDOMPoint::AtEndOf(aNode
), aRv
);
2021 * AllowsTransactionsToChangeSelection() returns true if editor allows any
2022 * transactions to change Selection. Otherwise, transactions shouldn't
2025 inline bool AllowsTransactionsToChangeSelection() const {
2026 return mAllowsTransactionsToChangeSelection
;
2030 * MakeThisAllowTransactionsToChangeSelection() with true makes this editor
2031 * allow transactions to change Selection. Otherwise, i.e., with false,
2032 * makes this editor not allow transactions to change Selection.
2034 inline void MakeThisAllowTransactionsToChangeSelection(bool aAllow
) {
2035 mAllowsTransactionsToChangeSelection
= aAllow
;
2038 nsresult
HandleInlineSpellCheck(
2039 const EditorDOMPoint
& aPreviouslySelectedStart
,
2040 const dom::AbstractRange
* aRange
= nullptr);
2043 * Whether the editor is active on the DOM window. Note that when this
2044 * returns true but GetFocusedElement() returns null, it means that this
2045 * editor was focused when the DOM window was active.
2047 virtual bool IsActiveInDOMWindow() const;
2050 * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
2051 * with nsCaret::RemoveForceHide(). This does NOT set visibility of
2052 * nsCaret. Therefore, this is stateless.
2054 void HideCaret(bool aHide
);
2056 protected: // Edit sub-action handler
2058 * AutoCaretBidiLevelManager() computes bidi level of caret, deleting
2059 * character(s) from aPointAtCaret at construction. Then, if you'll
2060 * need to extend the selection, you should calls `UpdateCaretBidiLevel()`,
2061 * then, this class may update caret bidi level for you if it's required.
2063 class MOZ_RAII AutoCaretBidiLevelManager final
{
2066 * @param aEditorBase The editor.
2067 * @param aPointAtCaret Collapsed `Selection` point.
2068 * @param aDirectionAndAmount The direction and amount to delete.
2070 template <typename PT
, typename CT
>
2071 AutoCaretBidiLevelManager(const EditorBase
& aEditorBase
,
2072 nsIEditor::EDirection aDirectionAndAmount
,
2073 const EditorDOMPointBase
<PT
, CT
>& aPointAtCaret
);
2076 * Failed() returns true if the constructor failed to handle the bidi
2079 bool Failed() const { return mFailed
; }
2082 * Canceled() returns true if when the caller should stop deleting
2083 * characters since caret position is not visually adjacent the deleting
2084 * characters and user does not wand to delete them in that case.
2086 bool Canceled() const { return mCanceled
; }
2089 * MaybeUpdateCaretBidiLevel() may update caret bidi level and schedule to
2090 * paint it if they are necessary.
2092 void MaybeUpdateCaretBidiLevel(const EditorBase
& aEditorBase
) const;
2095 Maybe
<mozilla::intl::BidiEmbeddingLevel
> mNewCaretBidiLevel
;
2096 bool mFailed
= false;
2097 bool mCanceled
= false;
2101 * UndefineCaretBidiLevel() resets bidi level of the caret.
2103 void UndefineCaretBidiLevel() const;
2106 * Flushing pending notifications if nsFrameSelection requires the latest
2107 * layout information to compute deletion range. This may destroy the
2108 * editor instance itself. When this returns false, don't keep doing
2111 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
bool
2112 FlushPendingNotificationsIfToHandleDeletionWithFrameSelection(
2113 nsIEditor::EDirection aDirectionAndAmount
) const;
2116 * DeleteSelectionAsSubAction() removes selection content or content around
2117 * caret with transactions. This should be used for handling it as an
2120 * @param aDirectionAndAmount How much range should be removed.
2121 * @param aStripWrappers Whether the parent blocks should be removed
2122 * when they become empty. If this instance is
2123 * a TextEditor, Must be nsIEditor::eNoStrip.
2125 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2126 DeleteSelectionAsSubAction(nsIEditor::EDirection aDirectionAndAmount
,
2127 nsIEditor::EStripWrappers aStripWrappers
);
2130 * This method handles "delete selection" commands.
2131 * NOTE: Don't call this method recursively from the helper methods since
2132 * when nobody handled it without canceling and returing an error,
2133 * this falls it back to `DeleteSelectionWithTransaction()`.
2135 * @param aDirectionAndAmount Direction of the deletion.
2136 * @param aStripWrappers Must be nsIEditor::eNoStrip if this is a
2137 * TextEditor instance. Otherwise,
2138 * nsIEditor::eStrip is also valid.
2140 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual Result
<EditActionResult
, nsresult
>
2141 HandleDeleteSelection(nsIEditor::EDirection aDirectionAndAmount
,
2142 nsIEditor::EStripWrappers aStripWrappers
) = 0;
2145 * ReplaceSelectionAsSubAction() replaces selection with aString.
2147 * @param aString The string to replace.
2149 MOZ_CAN_RUN_SCRIPT nsresult
2150 ReplaceSelectionAsSubAction(const nsAString
& aString
);
2153 * HandleInsertText() handles inserting text at selection.
2155 * @param aEditSubAction Must be EditSubAction::eInsertText or
2156 * EditSubAction::eInsertTextComingFromIME.
2157 * @param aInsertionString String to be inserted at selection.
2158 * @param aSelectionHandling Specify whether selected content should be
2159 * deleted or ignored.
2161 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual Result
<EditActionResult
, nsresult
>
2162 HandleInsertText(EditSubAction aEditSubAction
,
2163 const nsAString
& aInsertionString
,
2164 SelectionHandling aSelectionHandling
) = 0;
2167 * InsertWithQuotationsAsSubAction() inserts aQuotedText with appending ">"
2168 * to start of every line.
2170 * @param aQuotedText String to insert. This will be quoted by ">"
2173 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual nsresult
2174 InsertWithQuotationsAsSubAction(const nsAString
& aQuotedText
) = 0;
2177 * PrepareInsertContent() is a helper method of InsertTextAt(),
2178 * HTMLEditor::HTMLWithContextInserter::Run(). They insert content coming
2179 * from clipboard or drag and drop. Before that, they may need to remove
2180 * selected contents and adjust selection. This does them instead.
2182 * @param aPointToInsert Point to insert. Must be set. Callers
2183 * shouldn't use this instance after calling this
2184 * method because this method may cause changing
2185 * the DOM tree and Selection.
2187 enum class DeleteSelectedContent
: bool {
2188 No
, // Don't delete selection
2189 Yes
, // Delete selected content
2191 MOZ_CAN_RUN_SCRIPT nsresult
2192 PrepareToInsertContent(const EditorDOMPoint
& aPointToInsert
,
2193 DeleteSelectedContent aDeleteSelectedContent
);
2196 * InsertTextAt() inserts aStringToInsert at aPointToInsert.
2198 * @param aStringToInsert The string which you want to insert.
2199 * @param aPointToInsert The insertion point.
2201 MOZ_CAN_RUN_SCRIPT nsresult
InsertTextAt(
2202 const nsAString
& aStringToInsert
, const EditorDOMPoint
& aPointToInsert
,
2203 DeleteSelectedContent aDeleteSelectedContent
);
2206 * Return whether the data is safe to insert as the source and destination
2207 * principals match, or we are in a editor context where this doesn't matter.
2208 * Otherwise, the data must be sanitized first.
2210 enum class SafeToInsertData
: bool { No
, Yes
};
2211 SafeToInsertData
IsSafeToInsertData(nsIPrincipal
* aSourcePrincipal
) const;
2214 * Routines for managing the preservation of selection across
2215 * various editor actions.
2217 bool ArePreservingSelection() const;
2218 void PreserveSelectionAcrossActions();
2219 MOZ_CAN_RUN_SCRIPT nsresult
RestorePreservedSelection();
2220 void StopPreservingSelection();
2222 protected: // Called by helper classes.
2224 * OnStartToHandleTopLevelEditSubAction() is called when
2225 * GetTopLevelEditSubAction() is EditSubAction::eNone and somebody starts to
2226 * handle aEditSubAction.
2228 * @param aTopLevelEditSubAction Top level edit sub action which
2229 * will be handled soon.
2230 * @param aDirectionOfTopLevelEditSubAction Direction of aEditSubAction.
2232 MOZ_CAN_RUN_SCRIPT
virtual void OnStartToHandleTopLevelEditSubAction(
2233 EditSubAction aTopLevelEditSubAction
,
2234 nsIEditor::EDirection aDirectionOfTopLevelEditSubAction
,
2238 * OnEndHandlingTopLevelEditSubAction() is called after
2239 * SetTopLevelEditSubAction() is handled.
2241 MOZ_CAN_RUN_SCRIPT
virtual nsresult
OnEndHandlingTopLevelEditSubAction();
2244 * OnStartToHandleEditSubAction() and OnEndHandlingEditSubAction() are called
2245 * when starting to handle an edit sub action and ending handling an edit
2248 void OnStartToHandleEditSubAction() { EditSubActionDataRef().Clear(); }
2249 void OnEndHandlingEditSubAction() { EditSubActionDataRef().Clear(); }
2252 * (Begin|End)PlaceholderTransaction() are called by AutoPlaceholderBatch.
2253 * This set of methods are similar to the (Begin|End)Transaction(), but do
2254 * not use the transaction managers batching feature. Instead we use a
2255 * placeholder transaction to wrap up any further transaction while the
2256 * batch is open. The advantage of this is that placeholder transactions
2257 * can later merge, if needed. Merging is unavailable between transaction
2260 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void BeginPlaceholderTransaction(
2261 nsStaticAtom
& aTransactionName
, const char* aRequesterFuncName
);
2262 enum class ScrollSelectionIntoView
{ No
, Yes
};
2263 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void EndPlaceholderTransaction(
2264 ScrollSelectionIntoView aScrollSelectionIntoView
,
2265 const char* aRequesterFuncName
);
2267 void BeginUpdateViewBatch(const char* aRequesterFuncName
);
2268 MOZ_CAN_RUN_SCRIPT
void EndUpdateViewBatch(const char* aRequesterFuncName
);
2271 * Used by HTMLEditor::AutoTransactionBatch, nsIEditor::BeginTransaction
2272 * and nsIEditor::EndTransation. After calling BeginTransactionInternal(),
2273 * all transactions will be treated as an atomic transaction. I.e., two or
2274 * more transactions are undid once.
2275 * XXX What's the difference with PlaceholderTransaction? Should we always
2278 MOZ_CAN_RUN_SCRIPT
void BeginTransactionInternal(
2279 const char* aRequesterFuncName
);
2280 MOZ_CAN_RUN_SCRIPT
void EndTransactionInternal(
2281 const char* aRequesterFuncName
);
2283 protected: // Shouldn't be used by friend classes
2285 * The default destructor. This should suffice. Should this be pure virtual
2286 * for someone to derive from the EditorBase later? I don't believe so.
2288 virtual ~EditorBase();
2291 * @param aDocument The dom document interface being observed
2292 * @param aRootElement
2293 * This is the root of the editable section of this
2294 * document. If it is null then we get root from document
2296 * @param aSelectionController
2297 * The selection controller of selections which will be
2298 * used in this editor.
2299 * @param aFlags Some of nsIEditor::eEditor*Mask flags.
2301 MOZ_CAN_RUN_SCRIPT nsresult
2302 InitInternal(Document
& aDocument
, Element
* aRootElement
,
2303 nsISelectionController
& aSelectionController
, uint32_t aFlags
);
2306 * PostCreateInternal() should be called after InitInternal(), and is the time
2307 * that the editor tells its documentStateObservers that the document has been
2310 MOZ_CAN_RUN_SCRIPT nsresult
PostCreateInternal();
2313 * PreDestroyInternal() is called before the editor goes away, and gives the
2314 * editor a chance to tell its documentStateObservers that the document is
2317 MOZ_CAN_RUN_SCRIPT
virtual void PreDestroyInternal();
2319 MOZ_ALWAYS_INLINE EditorType
GetEditorType() const {
2320 return mIsHTMLEditorClass
? EditorType::HTML
: EditorType::Text
;
2324 * Check whether the caller can keep handling focus event.
2326 * @param aOriginalEventTargetNode The original event target of the focus
2329 [[nodiscard
]] bool CanKeepHandlingFocusEvent(
2330 const nsINode
& aOriginalEventTargetNode
) const;
2333 * If this editor has skipped spell checking and not yet flushed, this runs
2334 * the spell checker.
2336 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
FlushPendingSpellCheck();
2338 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
EnsureEmptyTextFirstChild();
2340 int32_t WrapWidth() const { return mWrapColumn
; }
2343 * ToGenericNSResult() computes proper nsresult value for the editor users.
2344 * This should be used only when public methods return result of internal
2347 static inline nsresult
ToGenericNSResult(nsresult aRv
) {
2349 // If the editor is destroyed while handling an edit action, editor needs
2350 // to stop handling it. However, editor throw exception in this case
2351 // because Chrome does not throw exception even in this case.
2352 case NS_ERROR_EDITOR_DESTROYED
:
2354 // If editor meets unexpected DOM tree due to modified by mutation event
2355 // listener, editor needs to stop handling it. However, editor shouldn't
2356 // return error for the users because Chrome does not throw exception in
2358 case NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE
:
2360 // If the editing action is canceled by event listeners, editor needs
2361 // to stop handling it. However, editor shouldn't return error for
2362 // the callers but they should be able to distinguish whether it's
2363 // canceled or not. Although it's DOM specific code, let's return
2364 // DOM_SUCCESS_DOM_NO_OPERATION here.
2365 case NS_ERROR_EDITOR_ACTION_CANCELED
:
2366 return NS_SUCCESS_DOM_NO_OPERATION
;
2367 // If there is no selection range or editable selection ranges, editor
2368 // needs to stop handling it. However, editor shouldn't return error for
2369 // the callers to avoid throwing exception. However, they may want to
2370 // check whether it works or not. Therefore, we should return
2371 // NS_SUCCESS_DOM_NO_OPERATION instead.
2372 case NS_ERROR_EDITOR_NO_EDITABLE_RANGE
:
2373 return NS_SUCCESS_DOM_NO_OPERATION
;
2374 // If CreateNodeResultBase::SuggestCaretPointTo etc is called with
2375 // SuggestCaret::AndIgnoreTrivialErrors and CollapseSelectionTo returns
2376 // non-critical error e.g., not NS_ERROR_EDITOR_DESTROYED, it returns
2377 // this success code instead of actual error code for making the caller
2378 // handle the case easier. Therefore, this should be mapped to NS_OK
2379 // for the users of editor.
2380 case NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR
:
2388 * GetDocumentCharsetInternal() returns charset of the document.
2390 nsresult
GetDocumentCharsetInternal(nsACString
& aCharset
) const;
2393 * ComputeValueInternal() computes string value of this editor for given
2394 * format. This may be too expensive if it's in hot path.
2396 * @param aFormatType MIME type like "text/plain".
2397 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder.
2398 * @param aCharset Encoding of the document.
2400 nsresult
ComputeValueInternal(const nsAString
& aFormatType
,
2401 uint32_t aDocumentEncoderFlags
,
2402 nsAString
& aOutputString
) const;
2405 * GetAndInitDocEncoder() returns a document encoder instance for aFormatType
2406 * after initializing it. The result may be cached for saving recreation
2409 * @param aFormatType MIME type like "text/plain".
2410 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder.
2411 * @param aCharset Encoding of the document.
2413 already_AddRefed
<nsIDocumentEncoder
> GetAndInitDocEncoder(
2414 const nsAString
& aFormatType
, uint32_t aDocumentEncoderFlags
,
2415 const nsACString
& aCharset
) const;
2418 * EnsurePaddingBRElementInMultilineEditor() creates a padding `<br>` element
2419 * at end of multiline text editor.
2421 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2422 EnsurePaddingBRElementInMultilineEditor();
2425 * SelectAllInternal() should be used instead of SelectAll() in editor
2426 * because SelectAll() creates AutoEditActionSetter but we should avoid
2427 * to create it as far as possible.
2429 MOZ_CAN_RUN_SCRIPT
virtual nsresult
SelectAllInternal();
2431 nsresult
DetermineCurrentDirection();
2434 * DispatchInputEvent() dispatches an "input" event synchronously or
2435 * asynchronously if it's not safe to dispatch.
2437 MOZ_CAN_RUN_SCRIPT
void DispatchInputEvent();
2440 * Called after a transaction is done successfully.
2442 MOZ_CAN_RUN_SCRIPT
void DoAfterDoTransaction(nsITransaction
* aTransaction
);
2445 * Called after a transaction is undone successfully.
2448 MOZ_CAN_RUN_SCRIPT
void DoAfterUndoTransaction();
2451 * Called after a transaction is redone successfully.
2453 MOZ_CAN_RUN_SCRIPT
void DoAfterRedoTransaction();
2456 * Tell the doc state listeners that the doc state has changed.
2458 enum TDocumentListenerNotification
{
2460 eDocumentToBeDestroyed
,
2461 eDocumentStateChanged
2463 MOZ_CAN_RUN_SCRIPT nsresult
2464 NotifyDocumentListeners(TDocumentListenerNotification aNotificationType
);
2467 * Make the given selection span the entire document.
2469 MOZ_CAN_RUN_SCRIPT
virtual nsresult
SelectEntireDocument() = 0;
2472 * Helper method for scrolling the selection into view after
2473 * an edit operation.
2475 * Editor methods *should* call this method instead of the versions
2476 * in the various selection interfaces, since this makes sure that
2477 * the editor's sync/async settings for reflowing, painting, and scrolling
2480 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2481 ScrollSelectionFocusIntoView() const;
2483 virtual nsresult
InstallEventListeners();
2484 virtual void CreateEventListeners();
2485 void RemoveEventListeners();
2486 [[nodiscard
]] bool IsListeningToEvents() const;
2489 * Called if and only if this editor is in readonly mode.
2491 void HandleKeyPressEventInReadOnlyMode(
2492 WidgetKeyboardEvent
& aKeyboardEvent
) const;
2495 * Get the input event target. This might return null.
2497 virtual already_AddRefed
<Element
> GetInputEventTargetElement() const = 0;
2500 * Return true if spellchecking should be enabled for this editor.
2502 [[nodiscard
]] bool GetDesiredSpellCheckState();
2504 [[nodiscard
]] bool CanEnableSpellCheck() const {
2505 // Check for password/readonly/disabled, which are not spellchecked
2506 // regardless of DOM. Also, check to see if spell check should be skipped
2508 return !IsPasswordEditor() && !IsReadonly() && !ShouldSkipSpellCheck();
2512 * InitializeSelectionAncestorLimit() is called by InitializeSelection().
2513 * When this is called, each implementation has to call
2514 * Selection::SetAncestorLimiter() with aAnotherLimit.
2516 * @param aAncestorLimit New ancestor limit of Selection. This always
2517 * has parent node. So, it's always safe to
2518 * call SetAncestorLimit() with this node.
2520 virtual void InitializeSelectionAncestorLimit(Element
& aAncestorLimit
) const;
2523 * Initializes selection and caret for the editor at getting focus. If
2524 * aOriginalEventTargetNode isn't a host of the editor, i.e., the editor
2525 * doesn't get focus, this does nothing.
2527 * @param aOriginalEventTargetNode The original event target node of the
2530 MOZ_CAN_RUN_SCRIPT nsresult
2531 InitializeSelection(const nsINode
& aOriginalEventTargetNode
);
2533 enum NotificationForEditorObservers
{
2534 eNotifyEditorObserversOfEnd
,
2535 eNotifyEditorObserversOfBefore
,
2536 eNotifyEditorObserversOfCancel
2538 MOZ_CAN_RUN_SCRIPT
void NotifyEditorObservers(
2539 NotificationForEditorObservers aNotification
);
2542 * HowToHandleCollapsedRange indicates how collapsed range should be treated.
2544 enum class HowToHandleCollapsedRange
{
2545 // Ignore collapsed range.
2547 // Extend collapsed range for removing previous content.
2549 // Extend collapsed range for removing next content.
2553 static HowToHandleCollapsedRange
HowToHandleCollapsedRangeFor(
2554 nsIEditor::EDirection aDirectionAndAmount
) {
2555 switch (aDirectionAndAmount
) {
2556 case nsIEditor::eNone
:
2557 return HowToHandleCollapsedRange::Ignore
;
2558 case nsIEditor::ePrevious
:
2559 return HowToHandleCollapsedRange::ExtendBackward
;
2560 case nsIEditor::eNext
:
2561 return HowToHandleCollapsedRange::ExtendForward
;
2562 case nsIEditor::ePreviousWord
:
2563 case nsIEditor::eNextWord
:
2564 case nsIEditor::eToBeginningOfLine
:
2565 case nsIEditor::eToEndOfLine
:
2566 // If the amount is word or
2567 // line,`AutoClonedSelectionRangeArray::ExtendAnchorFocusRangeFor()`
2568 // must have already been extended collapsed ranges before.
2569 return HowToHandleCollapsedRange::Ignore
;
2571 MOZ_ASSERT_UNREACHABLE("Invalid nsIEditor::EDirection value");
2572 return HowToHandleCollapsedRange::Ignore
;
2576 * InsertDroppedDataTransferAsAction() inserts all data items in aDataTransfer
2577 * at aDroppedAt unless the editor is destroyed.
2579 * @param aEditActionData The edit action data whose edit action must be
2580 * EditAction::eDrop.
2581 * @param aDataTransfer The data transfer object which is dropped.
2582 * @param aDroppedAt The DOM tree position whether aDataTransfer
2584 * @param aSourcePrincipal Principal of the source of the drag.
2585 * May be nullptr if it comes from another app
2588 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual nsresult
2589 InsertDroppedDataTransferAsAction(AutoEditActionDataSetter
& aEditActionData
,
2590 DataTransfer
& aDataTransfer
,
2591 const EditorDOMPoint
& aDroppedAt
,
2592 nsIPrincipal
* aSourcePrincipal
) = 0;
2595 * DeleteSelectionByDragAsAction() removes selection and dispatch "input"
2596 * event whose inputType is "deleteByDrag".
2598 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2599 DeleteSelectionByDragAsAction(bool aDispatchInputEvent
);
2602 * DeleteSelectionWithTransaction() removes selected content or content
2603 * around caret with transactions and remove empty inclusive ancestor
2604 * inline elements of collapsed selection after removing the contents.
2606 * @param aDirectionAndAmount How much range should be removed.
2607 * @param aStripWrappers Whether the parent blocks should be removed
2608 * when they become empty.
2609 * Note that this must be `nsIEditor::eNoStrip`
2610 * if this is a TextEditor because anyway it'll
2613 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2614 DeleteSelectionWithTransaction(nsIEditor::EDirection aDirectionAndAmount
,
2615 nsIEditor::EStripWrappers aStripWrappers
);
2618 * DeleteRangeWithTransaction() removes content in aRangeToDelete or content
2619 * around collapsed aRangeToDelete with transactions and remove empty
2620 * inclusive ancestor inline elements of the collapsed range after removing
2623 * @param aDirectionAndAmount How much range should be removed.
2624 * @param aStripWrappers Whether the parent blocks should be removed
2625 * when they become empty.
2626 * Note that this must be `nsIEditor::eNoStrip`
2627 * if this is a TextEditor because anyway it'll
2629 * @param aRangeToDelete The range to delete content.
2631 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT Result
<CaretPoint
, nsresult
>
2632 DeleteRangeWithTransaction(nsIEditor::EDirection aDirectionAndAmount
,
2633 nsIEditor::EStripWrappers aStripWrappers
,
2634 nsRange
& aRangeToDelete
);
2637 * DeleteRangesWithTransaction() removes content in aRangesToDelete or content
2638 * around collapsed ranges in aRangesToDelete with transactions and remove
2639 * empty inclusive ancestor inline elements of collapsed ranges after
2640 * removing the contents.
2642 * @param aDirectionAndAmount How much range should be removed.
2643 * @param aStripWrappers Whether the parent blocks should be removed
2644 * when they become empty.
2645 * Note that this must be `nsIEditor::eNoStrip`
2646 * if this is a TextEditor because anyway it'll
2648 * @param aRangesToDelete The ranges to delete content.
2650 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual Result
<CaretPoint
, nsresult
>
2651 DeleteRangesWithTransaction(nsIEditor::EDirection aDirectionAndAmount
,
2652 nsIEditor::EStripWrappers aStripWrappers
,
2653 const AutoClonedRangeArray
& aRangesToDelete
);
2656 * Create a transaction for delete the content in aRangesToDelete.
2657 * The result may include DeleteRangeTransaction (for deleting non-collapsed
2658 * range), DeleteNodeTransactions and DeleteTextTransactions (for deleting
2659 * collapsed range) as its children.
2661 * @param aHowToHandleCollapsedRange
2662 * How to handle collapsed ranges.
2663 * @param aRangesToDelete The ranges to delete content.
2665 already_AddRefed
<DeleteMultipleRangesTransaction
>
2666 CreateTransactionForDeleteSelection(
2667 HowToHandleCollapsedRange aHowToHandleCollapsedRange
,
2668 const AutoClonedRangeArray
& aRangesToDelete
);
2671 * Create a DeleteNodeTransaction or DeleteTextTransaction for removing a
2672 * nodes or some text around aRangeToDelete.
2674 * @param aCollapsedRange The range to be removed. This must be
2676 * @param aHowToHandleCollapsedRange
2677 * How to handle aCollapsedRange. Must
2678 * be HowToHandleCollapsedRange::ExtendBackward or
2679 * HowToHandleCollapsedRange::ExtendForward.
2681 already_AddRefed
<DeleteContentTransactionBase
>
2682 CreateTransactionForCollapsedRange(
2683 const nsRange
& aCollapsedRange
,
2684 HowToHandleCollapsedRange aHowToHandleCollapsedRange
);
2687 * ComputeInsertedRange() returns actual range modified by inserting string
2688 * in a text node. If mutation event listener changed the text data, this
2689 * returns a range which covers all over the text data.
2691 std::tuple
<EditorDOMPointInText
, EditorDOMPointInText
> ComputeInsertedRange(
2692 const EditorDOMPointInText
& aInsertedPoint
,
2693 const nsAString
& aInsertedString
) const;
2696 * EnsureComposition() should be called by composition event handlers. This
2697 * tries to get the composition for the event and set it to mComposition.
2698 * However, this may fail because the composition may be committed before
2699 * the event comes to the editor.
2701 * @return true if there is a composition. Otherwise, for example,
2702 * a composition event handler in web contents moved focus
2703 * for committing the composition, returns false.
2705 bool EnsureComposition(WidgetCompositionEvent
& aCompositionEvent
);
2708 * See comment of IsCopyToClipboardAllowed() for the detail.
2710 virtual bool IsCopyToClipboardAllowedInternal() const {
2711 MOZ_ASSERT(IsEditActionDataAvailable());
2712 return !SelectionRef().IsCollapsed();
2716 * Helper for Is{Cut|Copy}CommandEnabled.
2717 * Look for a listener for the given command, including up the target chain.
2719 MOZ_CAN_RUN_SCRIPT
bool CheckForClipboardCommandListener(
2720 nsAtom
* aCommand
, EventMessage aEventMessage
) const;
2723 * DispatchClipboardEventAndUpdateClipboard() may dispatch a clipboard event
2724 * and update clipboard if aEventMessage is eCopy or eCut.
2726 * @param aEventMessage The event message which may be set to the
2727 * dispatching event.
2728 * @param aClipboardType Working with global clipboard or selection.
2730 enum class ClipboardEventResult
{
2731 // We have met an error in nsCopySupport::FireClipboardEvent,
2732 // or, default of dispatched event is NOT prevented, the event is "cut"
2733 // and the event target is not editable.
2735 // A "paste" event is dispatched and prevented its default.
2736 DefaultPreventedOfPaste
,
2737 // Default of a "copy" or "cut" event is prevented but the clipboard is
2738 // updated unless the dataTransfer of the event is cleared by the listener.
2739 // Or, default of the event is NOT prevented but selection is collapsed
2740 // when the event target is editable or the event is "copy".
2742 // A clipboard event is maybe dispatched and not canceled by the web app.
2743 // In this case, the clipboard has been updated if aEventMessage is eCopy
2747 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT Result
<ClipboardEventResult
, nsresult
>
2748 DispatchClipboardEventAndUpdateClipboard(
2749 EventMessage aEventMessage
,
2750 mozilla::Maybe
<nsIClipboard::ClipboardType
> aClipboardType
,
2751 DataTransfer
* aDataTransfer
= nullptr);
2754 * Called after PasteAsAction() dispatches "paste" event and it's not
2757 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual nsresult
HandlePaste(
2758 AutoEditActionDataSetter
& aEditActionData
,
2759 nsIClipboard::ClipboardType aClipboardType
,
2760 DataTransfer
* aDataTransfer
) = 0;
2763 * Called after PasteAsQuotationAsAction() dispatches "paste" event and it's
2766 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual nsresult
HandlePasteAsQuotation(
2767 AutoEditActionDataSetter
& aEditActionData
,
2768 nsIClipboard::ClipboardType aClipboardType
,
2769 DataTransfer
* aDataTransfer
) = 0;
2772 * Called after PasteTransferableAsAction() dispatches "paste" event and
2773 * it's not canceled.
2775 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual nsresult
HandlePasteTransferable(
2776 AutoEditActionDataSetter
& aEditActionData
,
2777 nsITransferable
& aTransferable
) = 0;
2780 nsCOMPtr
<nsISelectionController
> mSelectionController
;
2781 RefPtr
<Document
> mDocument
;
2783 AutoEditActionDataSetter
* mEditActionData
;
2786 * SetTextDirectionTo() sets text-direction of the root element.
2787 * Should use SwitchTextDirectionTo() or ToggleTextDirection() instead.
2788 * This is a helper class of them.
2790 nsresult
SetTextDirectionTo(TextDirection aTextDirection
);
2792 protected: // helper classes which may be used by friends
2794 * Stack based helper class for batching a collection of transactions
2795 * inside a placeholder transaction. Different from AutoTransactionBatch,
2796 * this notifies editor observers of before/end edit action handling, and
2797 * dispatches "input" event if it's necessary.
2799 class MOZ_RAII AutoPlaceholderBatch final
{
2802 * @param aRequesterFuncName function name which wants to end the batch.
2803 * This won't be stored nor exposed to selection listeners etc, used
2804 * only for logging. This MUST be alive when the destructor runs.
2806 AutoPlaceholderBatch(EditorBase
& aEditorBase
,
2807 ScrollSelectionIntoView aScrollSelectionIntoView
,
2808 const char* aRequesterFuncName
)
2809 : mEditorBase(aEditorBase
),
2810 mScrollSelectionIntoView(aScrollSelectionIntoView
),
2811 mRequesterFuncName(aRequesterFuncName
) {
2812 mEditorBase
->BeginPlaceholderTransaction(*nsGkAtoms::_empty
,
2813 mRequesterFuncName
);
2816 AutoPlaceholderBatch(EditorBase
& aEditorBase
,
2817 nsStaticAtom
& aTransactionName
,
2818 ScrollSelectionIntoView aScrollSelectionIntoView
,
2819 const char* aRequesterFuncName
)
2820 : mEditorBase(aEditorBase
),
2821 mScrollSelectionIntoView(aScrollSelectionIntoView
),
2822 mRequesterFuncName(aRequesterFuncName
) {
2823 mEditorBase
->BeginPlaceholderTransaction(aTransactionName
,
2824 mRequesterFuncName
);
2827 ~AutoPlaceholderBatch() {
2828 mEditorBase
->EndPlaceholderTransaction(mScrollSelectionIntoView
,
2829 mRequesterFuncName
);
2833 const OwningNonNull
<EditorBase
> mEditorBase
;
2834 const ScrollSelectionIntoView mScrollSelectionIntoView
;
2835 const char* const mRequesterFuncName
;
2839 * AutoEditSubActionNotifier notifies editor of start to handle
2840 * top level edit sub-action and end handling top level edit sub-action.
2842 class MOZ_RAII AutoEditSubActionNotifier final
{
2844 MOZ_CAN_RUN_SCRIPT
AutoEditSubActionNotifier(
2845 EditorBase
& aEditorBase
, EditSubAction aEditSubAction
,
2846 nsIEditor::EDirection aDirection
, ErrorResult
& aRv
)
2847 : mEditorBase(aEditorBase
), mIsTopLevel(true) {
2848 // The top level edit sub action has already be set if this is nested
2850 // XXX Looks like that this is not aware of unexpected nested edit
2852 // handling via selectionchange event listener or mutation event
2854 if (!mEditorBase
.GetTopLevelEditSubAction()) {
2855 MOZ_KnownLive(mEditorBase
)
2856 .OnStartToHandleTopLevelEditSubAction(aEditSubAction
, aDirection
,
2859 mIsTopLevel
= false;
2861 mEditorBase
.OnStartToHandleEditSubAction();
2864 MOZ_CAN_RUN_SCRIPT
~AutoEditSubActionNotifier() {
2865 mEditorBase
.OnEndHandlingEditSubAction();
2867 MOZ_KnownLive(mEditorBase
).OnEndHandlingTopLevelEditSubAction();
2872 EditorBase
& mEditorBase
;
2877 * Stack based helper class for turning off active selection adjustment
2878 * by low level transactions
2880 class MOZ_RAII AutoTransactionsConserveSelection final
{
2882 explicit AutoTransactionsConserveSelection(EditorBase
& aEditorBase
)
2883 : mEditorBase(aEditorBase
),
2884 mAllowedTransactionsToChangeSelection(
2885 aEditorBase
.AllowsTransactionsToChangeSelection()) {
2886 mEditorBase
.MakeThisAllowTransactionsToChangeSelection(false);
2889 ~AutoTransactionsConserveSelection() {
2890 mEditorBase
.MakeThisAllowTransactionsToChangeSelection(
2891 mAllowedTransactionsToChangeSelection
);
2895 EditorBase
& mEditorBase
;
2896 bool mAllowedTransactionsToChangeSelection
;
2899 /***************************************************************************
2900 * stack based helper class for batching reflow and paint requests.
2902 class MOZ_RAII AutoUpdateViewBatch final
{
2905 * @param aRequesterFuncName function name which wants to end the batch.
2906 * This won't be stored nor exposed to selection listeners etc, used
2907 * only for logging. This MUST be alive when the destructor runs.
2909 MOZ_CAN_RUN_SCRIPT
explicit AutoUpdateViewBatch(
2910 EditorBase
& aEditorBase
, const char* aRequesterFuncName
)
2911 : mEditorBase(aEditorBase
), mRequesterFuncName(aRequesterFuncName
) {
2912 mEditorBase
.BeginUpdateViewBatch(mRequesterFuncName
);
2915 MOZ_CAN_RUN_SCRIPT
~AutoUpdateViewBatch() {
2916 MOZ_KnownLive(mEditorBase
).EndUpdateViewBatch(mRequesterFuncName
);
2920 EditorBase
& mEditorBase
;
2921 const char* const mRequesterFuncName
;
2925 enum Tristate
{ eTriUnset
, eTriFalse
, eTriTrue
};
2927 // MIME type of the doc we are editing.
2928 nsString mContentMIMEType
;
2930 RefPtr
<mozInlineSpellChecker
> mInlineSpellChecker
;
2931 // Reference to text services document for mInlineSpellChecker.
2932 RefPtr
<TextServicesDocument
> mTextServicesDocument
;
2934 RefPtr
<TransactionManager
> mTransactionManager
;
2935 // Cached root node.
2936 RefPtr
<Element
> mRootElement
;
2938 // The form field as an event receiver.
2939 nsCOMPtr
<dom::EventTarget
> mEventTarget
;
2940 RefPtr
<EditorEventListener
> mEventListener
;
2941 // Strong reference to placeholder for begin/end batch purposes.
2942 RefPtr
<PlaceholderTransaction
> mPlaceholderTransaction
;
2943 // Name of placeholder transaction.
2944 nsStaticAtom
* mPlaceholderName
;
2945 // Saved selection state for placeholder transaction batching.
2946 mozilla::Maybe
<SelectionState
> mSelState
;
2947 // IME composition this is not null between compositionstart and
2949 RefPtr
<TextComposition
> mComposition
;
2951 RefPtr
<TextInputListener
> mTextInputListener
;
2953 RefPtr
<IMEContentObserver
> mIMEContentObserver
;
2955 // These members cache last encoder and its type for the performance in
2956 // TextEditor::ComputeTextValue() which is the implementation of
2957 // `<input>.value` and `<textarea>.value`. See `GetAndInitDocEncoder()`.
2958 mutable nsCOMPtr
<nsIDocumentEncoder
> mCachedDocumentEncoder
;
2959 mutable nsString mCachedDocumentEncoderType
;
2961 // Listens to all low level actions on the doc.
2962 // Edit action listener is currently used by highlighter of the findbar
2963 // and the spellchecker. So, we should reserve only 2 items.
2964 using AutoActionListenerArray
=
2965 AutoTArray
<OwningNonNull
<nsIEditActionListener
>, 2>;
2966 AutoActionListenerArray mActionListeners
;
2967 // Listen to overall doc state (dirty or not, just created, etc.).
2968 // Document state listener is currently used by FinderHighlighter and
2969 // BlueGriffon so that reserving only one is enough.
2970 using AutoDocumentStateListenerArray
=
2971 AutoTArray
<OwningNonNull
<nsIDocumentStateListener
>, 1>;
2972 AutoDocumentStateListenerArray mDocStateListeners
;
2974 // Number of modifications (for undo/redo stack).
2976 // Behavior flags. See nsIEditor.idl for the flags we use.
2979 int32_t mUpdateCount
;
2981 // Nesting count for batching.
2982 int32_t mPlaceholderBatch
;
2984 int32_t mWrapColumn
= 0;
2985 int32_t mNewlineHandling
;
2986 int32_t mCaretStyle
;
2988 // -1 = not initialized
2989 int8_t mDocDirtyState
;
2990 // A Tristate value.
2991 uint8_t mSpellcheckCheckboxState
;
2993 // If true, initialization was succeeded.
2994 bool mInitSucceeded
;
2995 // If false, transactions should not change Selection even after modifying
2997 bool mAllowsTransactionsToChangeSelection
;
2998 // Whether PreDestroy has been called.
2999 bool mDidPreDestroy
;
3000 // Whether PostCreate has been called.
3001 bool mDidPostCreate
;
3002 bool mDispatchInputEvent
;
3003 // True while the instance is handling an edit sub-action.
3004 bool mIsInEditSubAction
;
3005 // Whether caret is hidden forcibly.
3007 // Whether spellchecker dictionary is initialized after focused.
3008 bool mSpellCheckerDictionaryUpdated
;
3009 // Whether we are an HTML editor class.
3010 bool mIsHTMLEditorClass
;
3012 friend class AlignStateAtSelection
; // AutoEditActionDataSetter,
3013 // ToGenericNSResult
3014 friend class AutoClonedRangeArray
; // IsSEditActionDataAvailable,
3016 friend class AutoClonedSelectionRangeArray
; // RangeUpdaterRef, SelectionRef
3017 friend class AutoSelectionRestorer
; // RangeUpdaterRef, SavedSelectionRef
3018 friend class CaretPoint
; // AllowsTransactionsToChangeSelection,
3019 // CollapseSelectionTo
3020 friend class CompositionTransaction
; // CollapseSelectionTo,
3021 // DoDeleteText, DoInsertText,
3022 // DoReplaceText, HideCaret,
3024 friend class DeleteNodeTransaction
; // RangeUpdaterRef
3025 friend class DeleteRangeTransaction
; // AllowsTransactionsToChangeSelection,
3026 // CollapseSelectionTo
3027 friend class DeleteTextTransaction
; // AllowsTransactionsToChangeSelection,
3028 // DoDeleteText, DoInsertText,
3030 friend class InsertNodeTransaction
; // AllowsTransactionsToChangeSelection,
3031 // CollapseSelectionTo,
3032 // MarkElementDirty, ToGenericNSResult
3033 friend class InsertTextTransaction
; // AllowsTransactionsToChangeSelection,
3034 // CollapseSelectionTo, DoDeleteText,
3035 // DoInsertText, RangeUpdaterRef
3036 friend class ListElementSelectionState
; // AutoEditActionDataSetter,
3037 // ToGenericNSResult
3038 friend class ListItemElementSelectionState
; // AutoEditActionDataSetter,
3039 // ToGenericNSResult
3040 friend class MoveNodeTransaction
; // ToGenericNSResult
3041 friend class ParagraphStateAtSelection
; // AutoEditActionDataSetter,
3042 // ToGenericNSResult
3043 friend class PendingStyles
; // GetEditAction,
3044 // GetFirstSelectionStartPoint,
3046 friend class ReplaceTextTransaction
; // AllowsTransactionsToChangeSelection,
3047 // CollapseSelectionTo, DoReplaceText,
3049 friend class SplitNodeTransaction
; // ToGenericNSResult
3050 friend class WhiteSpaceVisibilityKeeper
; // AutoTransactionsConserveSelection
3051 friend class nsIEditor
; // mIsHTMLEditorClass
3054 } // namespace mozilla
3056 bool nsIEditor::IsTextEditor() const {
3057 return !AsEditorBase()->mIsHTMLEditorClass
;
3060 bool nsIEditor::IsHTMLEditor() const {
3061 return AsEditorBase()->mIsHTMLEditorClass
;
3064 mozilla::EditorBase
* nsIEditor::AsEditorBase() {
3065 return static_cast<mozilla::EditorBase
*>(this);
3068 const mozilla::EditorBase
* nsIEditor::AsEditorBase() const {
3069 return static_cast<const mozilla::EditorBase
*>(this);
3072 #endif // #ifndef mozilla_EditorBase_h