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/TypeInState.h" // for PropItem, StyleCache
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 "nsIContentInlines.h" // for nsINode::IsEditable()
33 #include "nsIEditor.h" // for nsIEditor, etc.
34 #include "nsISelectionController.h" // for nsISelectionController constants
35 #include "nsISelectionListener.h" // for nsISelectionListener
36 #include "nsISupportsImpl.h" // for EditorBase::Release, etc.
37 #include "nsIWeakReferenceUtils.h" // for nsWeakPtr
38 #include "nsLiteralString.h" // for NS_LITERAL_STRING
39 #include "nsPIDOMWindow.h" // for nsPIDOMWindowInner, etc.
40 #include "nsString.h" // for nsCString
41 #include "nsTArray.h" // for nsTArray and nsAutoTArray
42 #include "nsWeakReference.h" // for nsSupportsWeakReference
43 #include "nscore.h" // for nsresult, nsAString, etc.
45 #include <tuple> // for std::tuple
47 class mozInlineSpellChecker
;
51 class nsIDocumentEncoder
;
52 class nsIDocumentStateListener
;
53 class nsIEditActionListener
;
57 class nsITransferable
;
59 class nsITransactionListener
;
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 Document
= dom::Document
;
112 using Element
= dom::Element
;
113 using InterlinePosition
= dom::Selection::InterlinePosition
;
114 using Selection
= dom::Selection
;
115 using Text
= dom::Text
;
117 enum class EditorType
{ Text
, HTML
};
119 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
120 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase
, nsIEditor
)
125 // nsISelectionListener method
126 NS_DECL_NSISELECTIONLISTENER
129 * The default constructor. This should suffice. the setting of the
130 * interfaces is done after the construction of the editor class.
132 explicit EditorBase(EditorType aEditorType
);
134 bool IsInitialized() const { return !!mDocument
; }
135 bool Destroyed() const { return mDidPreDestroy
; }
137 Document
* GetDocument() const { return mDocument
; }
138 nsPIDOMWindowOuter
* GetWindow() const;
139 nsPIDOMWindowInner
* GetInnerWindow() const;
142 * MayHaveMutationEventListeners() returns true when the window may have
143 * mutation event listeners.
145 * @param aMutationEventType One or multiple of NS_EVENT_BITS_MUTATION_*.
146 * @return true if the editor is an HTMLEditor instance,
147 * and at least one of NS_EVENT_BITS_MUTATION_* is
148 * set to the window or in debug build.
150 bool MayHaveMutationEventListeners(
151 uint32_t aMutationEventType
= 0xFFFFFFFF) const {
152 if (IsTextEditor()) {
153 // DOM mutation event listeners cannot catch the changes of
154 // <input type="text"> nor <textarea>.
158 // On debug build, this should always return true for testing complicated
159 // path without mutation event listeners because when mutation event
160 // listeners do not touch the DOM, editor needs to run as there is no
161 // mutation event listeners.
163 #else // #ifdef DEBUG
164 nsPIDOMWindowInner
* window
= GetInnerWindow();
165 return window
? window
->HasMutationListeners(aMutationEventType
) : false;
166 #endif // #ifdef DEBUG #else
170 * MayHaveBeforeInputEventListenersForTelemetry() returns true when the
171 * window may have or have had one or more `beforeinput` event listeners.
172 * Note that this may return false even if there is a `beforeinput`.
173 * See nsPIDOMWindowInner::HasBeforeInputEventListenersForTelemetry()'s
174 * comment for the detail.
176 bool MayHaveBeforeInputEventListenersForTelemetry() const {
177 if (const nsPIDOMWindowInner
* window
= GetInnerWindow()) {
178 return window
->HasBeforeInputEventListenersForTelemetry();
184 * MutationObserverHasObservedNodeForTelemetry() returns true when a node in
185 * the window may have been observed by the web apps with a mutation observer
186 * (i.e., `MutationObserver.observe()` called by chrome script and addon's
187 * script does not make this returns true).
188 * Note that this may return false even if there is a node observed by
189 * a MutationObserver. See
190 * nsPIDOMWindowInner::MutationObserverHasObservedNodeForTelemetry()'s comment
193 bool MutationObserverHasObservedNodeForTelemetry() const {
194 if (const nsPIDOMWindowInner
* window
= GetInnerWindow()) {
195 return window
->MutationObserverHasObservedNodeForTelemetry();
200 PresShell
* GetPresShell() const;
201 nsPresContext
* GetPresContext() const;
202 already_AddRefed
<nsCaret
> GetCaret() const;
204 already_AddRefed
<nsIWidget
> GetWidget() const;
206 nsISelectionController
* GetSelectionController() const;
208 nsresult
GetSelection(SelectionType aSelectionType
,
209 Selection
** aSelection
) const;
211 Selection
* GetSelection(
212 SelectionType aSelectionType
= SelectionType::eNormal
) const {
213 if (aSelectionType
== SelectionType::eNormal
&&
214 IsEditActionDataAvailable()) {
215 return &SelectionRef();
217 nsISelectionController
* sc
= GetSelectionController();
221 Selection
* selection
= sc
->GetSelection(ToRawSelectionType(aSelectionType
));
226 * Fast non-refcounting editor root element accessor
228 Element
* GetRoot() const { return mRootElement
; }
231 * Likewise, but gets the text control element instead of the root for
234 Element
* GetExposedRoot() const;
237 * Set or unset TextInputListener. If setting non-nullptr when the editor
238 * already has a TextInputListener, this will crash in debug build.
240 void SetTextInputListener(TextInputListener
* aTextInputListener
);
243 * Set or unset IMEContentObserver. If setting non-nullptr when the editor
244 * already has an IMEContentObserver, this will crash in debug build.
246 void SetIMEContentObserver(IMEContentObserver
* aIMEContentObserver
);
249 * Returns current composition.
251 TextComposition
* GetComposition() const;
254 * Get preferred IME status of current widget.
256 virtual nsresult
GetPreferredIMEState(widget::IMEState
* aState
);
259 * Returns true if there is composition string and not fixed.
261 bool IsIMEComposing() const;
264 * Commit composition if there is.
265 * Note that when there is a composition, this requests to commit composition
266 * to native IME. Therefore, when there is composition, this can do anything.
267 * For example, the editor instance, the widget or the process itself may
270 nsresult
CommitComposition();
273 * ToggleTextDirection() toggles text-direction of the root element.
275 * @param aPrincipal Set subject principal if it may be called by
276 * JS. If set to nullptr, will be treated as
279 MOZ_CAN_RUN_SCRIPT nsresult
280 ToggleTextDirectionAsAction(nsIPrincipal
* aPrincipal
= nullptr);
283 * SwitchTextDirectionTo() sets the text-direction of the root element to
286 enum class TextDirection
{
290 MOZ_CAN_RUN_SCRIPT
void SwitchTextDirectionTo(TextDirection aTextDirection
);
293 * Finalizes selection and caret for the editor.
295 nsresult
FinalizeSelection();
298 * Returns true if selection is in an editable element and both the range
299 * start and the range end are editable. E.g., even if the selection range
300 * includes non-editable elements, returns true when one of common ancestors
301 * of the range start and the range end is editable. Otherwise, false.
303 bool IsSelectionEditable();
306 * Returns number of undo or redo items.
308 size_t NumberOfUndoItems() const {
309 return mTransactionManager
? mTransactionManager
->NumberOfUndoItems() : 0;
311 size_t NumberOfRedoItems() const {
312 return mTransactionManager
? mTransactionManager
->NumberOfRedoItems() : 0;
316 * Returns number of maximum undo/redo transactions.
318 int32_t NumberOfMaximumTransactions() const {
319 return mTransactionManager
320 ? mTransactionManager
->NumberOfMaximumTransactions()
325 * Returns true if this editor can store transactions for undo/redo.
327 bool IsUndoRedoEnabled() const {
328 return mTransactionManager
&&
329 mTransactionManager
->NumberOfMaximumTransactions();
333 * Return true if it's possible to undo/redo right now.
335 bool CanUndo() const {
336 return IsUndoRedoEnabled() && NumberOfUndoItems() > 0;
338 bool CanRedo() const {
339 return IsUndoRedoEnabled() && NumberOfRedoItems() > 0;
343 * Enables or disables undo/redo feature. Returns true if it succeeded,
344 * otherwise, e.g., we're undoing or redoing, returns false.
346 bool EnableUndoRedo(int32_t aMaxTransactionCount
= -1) {
347 if (!mTransactionManager
) {
348 mTransactionManager
= new TransactionManager();
350 return mTransactionManager
->EnableUndoRedo(aMaxTransactionCount
);
352 bool DisableUndoRedo() {
353 if (!mTransactionManager
) {
356 return mTransactionManager
->DisableUndoRedo();
358 bool ClearUndoRedo() {
359 if (!mTransactionManager
) {
362 return mTransactionManager
->ClearUndoRedo();
366 * See Document::AreClipboardCommandsUnconditionallyEnabled.
368 bool AreClipboardCommandsUnconditionallyEnabled() const;
371 * IsCutCommandEnabled() returns whether cut command can be enabled or
372 * disabled. This always returns true if we're in non-chrome HTML/XHTML
373 * document. Otherwise, same as the result of `IsCopyToClipboardAllowed()`.
375 MOZ_CAN_RUN_SCRIPT
bool IsCutCommandEnabled() const;
378 * IsCopyCommandEnabled() returns copy command can be enabled or disabled.
379 * This always returns true if we're in non-chrome HTML/XHTML document.
380 * Otherwise, same as the result of `IsCopyToClipboardAllowed()`.
382 MOZ_CAN_RUN_SCRIPT
bool IsCopyCommandEnabled() const;
385 * IsCopyToClipboardAllowed() returns true if the selected content can
386 * be copied into the clipboard. This returns true when:
387 * - `Selection` is not collapsed and we're not a password editor.
388 * - `Selection` is not collapsed and we're a password editor but selection
389 * range is in unmasked range.
391 bool IsCopyToClipboardAllowed() const {
392 AutoEditActionDataSetter
editActionData(*this, EditAction::eNotEditing
);
393 if (NS_WARN_IF(!editActionData
.CanHandle())) {
396 return IsCopyToClipboardAllowedInternal();
400 * HandleDropEvent() is called from EditorEventListener::Drop that is handler
403 MOZ_CAN_RUN_SCRIPT nsresult
HandleDropEvent(dom::DragEvent
* aDropEvent
);
405 MOZ_CAN_RUN_SCRIPT
virtual nsresult
HandleKeyPressEvent(
406 WidgetKeyboardEvent
* aKeyboardEvent
);
408 virtual dom::EventTarget
* GetDOMEventTarget() const = 0;
411 * OnCompositionStart() is called when editor receives eCompositionStart
412 * event which should be handled in this editor.
414 nsresult
OnCompositionStart(WidgetCompositionEvent
& aCompositionStartEvent
);
417 * OnCompositionChange() is called when editor receives an eCompositioChange
418 * event which should be handled in this editor.
420 * @param aCompositionChangeEvent eCompositionChange event which should
421 * be handled in this editor.
423 MOZ_CAN_RUN_SCRIPT nsresult
424 OnCompositionChange(WidgetCompositionEvent
& aCompositionChangeEvent
);
427 * OnCompositionEnd() is called when editor receives an eCompositionChange
428 * event and it's followed by eCompositionEnd event and after
429 * OnCompositionChange() is called.
431 MOZ_CAN_RUN_SCRIPT
void OnCompositionEnd(
432 WidgetCompositionEvent
& aCompositionEndEvent
);
435 * Similar to the setter for wrapWidth, but just sets the editor
436 * internal state without actually changing the content being edited
437 * to wrap at that column. This should only be used by callers who
438 * are sure that their content is already set up correctly.
440 void SetWrapColumn(int32_t aWrapColumn
) { mWrapColumn
= aWrapColumn
; }
443 * Accessor methods to flags.
445 uint32_t Flags() const { return mFlags
; }
447 MOZ_CAN_RUN_SCRIPT nsresult
AddFlags(uint32_t aFlags
) {
448 const uint32_t kOldFlags
= Flags();
449 const uint32_t kNewFlags
= (kOldFlags
| aFlags
);
450 if (kNewFlags
== kOldFlags
) {
453 return SetFlags(kNewFlags
); // virtual call and may be expensive.
455 MOZ_CAN_RUN_SCRIPT nsresult
RemoveFlags(uint32_t aFlags
) {
456 const uint32_t kOldFlags
= Flags();
457 const uint32_t kNewFlags
= (kOldFlags
& ~aFlags
);
458 if (kNewFlags
== kOldFlags
) {
461 return SetFlags(kNewFlags
); // virtual call and may be expensive.
463 MOZ_CAN_RUN_SCRIPT nsresult
AddAndRemoveFlags(uint32_t aAddingFlags
,
464 uint32_t aRemovingFlags
) {
465 MOZ_ASSERT(!(aAddingFlags
& aRemovingFlags
),
466 "Same flags are specified both adding and removing");
467 const uint32_t kOldFlags
= Flags();
468 const uint32_t kNewFlags
= ((kOldFlags
| aAddingFlags
) & ~aRemovingFlags
);
469 if (kNewFlags
== kOldFlags
) {
472 return SetFlags(kNewFlags
); // virtual call and may be expensive.
475 bool IsInPlaintextMode() const {
476 const bool isPlaintextMode
=
477 (mFlags
& nsIEditor::eEditorPlaintextMask
) != 0;
478 MOZ_ASSERT_IF(IsTextEditor(), isPlaintextMode
);
479 return isPlaintextMode
;
482 bool IsSingleLineEditor() const {
483 const bool isSingleLineEditor
=
484 (mFlags
& nsIEditor::eEditorSingleLineMask
) != 0;
485 MOZ_ASSERT_IF(isSingleLineEditor
, IsTextEditor());
486 return isSingleLineEditor
;
489 bool IsPasswordEditor() const {
490 const bool isPasswordEditor
=
491 (mFlags
& nsIEditor::eEditorPasswordMask
) != 0;
492 MOZ_ASSERT_IF(isPasswordEditor
, IsTextEditor());
493 return isPasswordEditor
;
496 // FYI: Both IsRightToLeft() and IsLeftToRight() may return false if
497 // the editor inherits the content node's direction.
498 bool IsRightToLeft() const {
499 return (mFlags
& nsIEditor::eEditorRightToLeft
) != 0;
501 bool IsLeftToRight() const {
502 return (mFlags
& nsIEditor::eEditorLeftToRight
) != 0;
505 bool IsReadonly() const {
506 return (mFlags
& nsIEditor::eEditorReadonlyMask
) != 0;
509 bool IsMailEditor() const {
510 return (mFlags
& nsIEditor::eEditorMailMask
) != 0;
513 bool IsWrapHackEnabled() const {
514 return (mFlags
& nsIEditor::eEditorEnableWrapHackMask
) != 0;
517 bool IsInteractionAllowed() const {
518 const bool isInteractionAllowed
=
519 (mFlags
& nsIEditor::eEditorAllowInteraction
) != 0;
520 MOZ_ASSERT_IF(isInteractionAllowed
, IsHTMLEditor());
521 return isInteractionAllowed
;
524 bool ShouldSkipSpellCheck() const {
525 return (mFlags
& nsIEditor::eEditorSkipSpellCheck
) != 0;
528 bool HasIndependentSelection() const {
529 MOZ_ASSERT_IF(mSelectionController
, IsTextEditor());
530 return !!mSelectionController
;
533 bool IsModifiable() const { return !IsReadonly(); }
536 * IsInEditSubAction() return true while the instance is handling an edit
537 * sub-action. Otherwise, false.
539 bool IsInEditSubAction() const { return mIsInEditSubAction
; }
542 * IsEmpty() checks whether the editor is empty. If editor has only padding
543 * <br> element for empty editor, returns true. If editor's root element has
544 * non-empty text nodes or other nodes like <br>, returns false.
546 virtual bool IsEmpty() const = 0;
549 * SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching
552 void SuppressDispatchingInputEvent(bool aSuppress
) {
553 mDispatchInputEvent
= !aSuppress
;
557 * IsSuppressingDispatchingInputEvent() returns true if the editor stops
558 * dispatching input event. Otherwise, false.
560 bool IsSuppressingDispatchingInputEvent() const {
561 return !mDispatchInputEvent
;
565 * Returns true if markNodeDirty() has any effect. Returns false if
566 * markNodeDirty() is a no-op.
568 bool OutputsMozDirty() const {
569 // Return true for Composer (!IsInteractionAllowed()) or mail
570 // (IsMailEditor()), but false for webpages.
571 return !IsInteractionAllowed() || IsMailEditor();
575 * Get the focused content, if we're focused. Returns null otherwise.
577 virtual nsIContent
* GetFocusedContent() const;
580 * Whether the aGUIEvent should be handled by this editor or not. When this
581 * returns false, The aGUIEvent shouldn't be handled on this editor,
582 * i.e., The aGUIEvent should be handled by another inner editor or ancestor
585 virtual bool IsAcceptableInputEvent(WidgetGUIEvent
* aGUIEvent
) const;
588 * FindSelectionRoot() returns a selection root of this editor when aNode
589 * gets focus. aNode must be a content node or a document node. When the
590 * target isn't a part of this editor, returns nullptr. If this is for
591 * designMode, you should set the document node to aNode except that an
592 * element in the document has focus.
594 virtual Element
* FindSelectionRoot(nsINode
* aNode
) const;
597 * This method has to be called by EditorEventListener::Focus.
598 * All actions that have to be done when the editor is focused needs to be
601 MOZ_CAN_RUN_SCRIPT
void OnFocus(nsINode
& aFocusEventTargetNode
);
603 /** Resyncs spellchecking state (enabled/disabled). This should be called
604 * when anything that affects spellchecking state changes, such as the
605 * spellcheck attribute value.
607 void SyncRealTimeSpell();
610 * This method re-initializes the selection and caret state that are for
611 * current editor state. When editor session is destroyed, it always reset
612 * selection state even if this has no focus. So if destroying editor,
613 * we have to call this method for focused editor to set selection state.
615 MOZ_CAN_RUN_SCRIPT
void ReinitializeSelection(Element
& aElement
);
620 * @param aPrincipal If you know current context is subject
621 * principal or system principal, set it.
622 * When nullptr, this checks it automatically.
624 MOZ_CAN_RUN_SCRIPT nsresult
CutAsAction(nsIPrincipal
* aPrincipal
= nullptr);
627 * CanPaste() returns true if user can paste something at current selection.
629 virtual bool CanPaste(int32_t aClipboardType
) const = 0;
632 * Do "undo" or "redo".
634 * @param aCount How many count of transactions should be
636 * @param aPrincipal Set subject principal if it may be called by
637 * JS. If set to nullptr, will be treated as
640 MOZ_CAN_RUN_SCRIPT nsresult
UndoAsAction(uint32_t aCount
,
641 nsIPrincipal
* aPrincipal
= nullptr);
642 MOZ_CAN_RUN_SCRIPT nsresult
RedoAsAction(uint32_t aCount
,
643 nsIPrincipal
* aPrincipal
= nullptr);
646 * InsertTextAsAction() inserts aStringToInsert at selection.
647 * Although this method is implementation of nsIEditor.insertText(),
648 * this treats the input is an edit action. If you'd like to insert text
649 * as part of edit action, you probably should use InsertTextAsSubAction().
651 * @param aStringToInsert The string to insert.
652 * @param aPrincipal Set subject principal if it may be called by
653 * JS. If set to nullptr, will be treated as
656 MOZ_CAN_RUN_SCRIPT nsresult
InsertTextAsAction(
657 const nsAString
& aStringToInsert
, nsIPrincipal
* aPrincipal
= nullptr);
660 * InsertLineBreakAsAction() is called when user inputs a line break with
661 * Enter or something. If the instance is `HTMLEditor`, this is called
662 * when Shift + Enter or "insertlinebreak" command.
664 * @param aPrincipal Set subject principal if it may be called by
665 * JS. If set to nullptr, will be treated as
668 MOZ_CAN_RUN_SCRIPT
virtual nsresult
InsertLineBreakAsAction(
669 nsIPrincipal
* aPrincipal
= nullptr) = 0;
672 * CanDeleteSelection() returns true if `Selection` is not collapsed and
673 * it's allowed to be removed.
675 bool CanDeleteSelection() const {
676 AutoEditActionDataSetter
editActionData(*this, EditAction::eNotEditing
);
677 if (NS_WARN_IF(!editActionData
.CanHandle())) {
680 return IsModifiable() && !SelectionRef().IsCollapsed();
684 * DeleteSelectionAsAction() removes selection content or content around
685 * caret with transactions. This should be used for handling it as an
686 * edit action. If you'd like to remove selection for preparing to insert
687 * something, you probably should use DeleteSelectionAsSubAction().
689 * @param aDirectionAndAmount How much range should be removed.
690 * @param aStripWrappers Whether the parent blocks should be removed
691 * when they become empty.
692 * @param aPrincipal Set subject principal if it may be called by
693 * JS. If set to nullptr, will be treated as
696 MOZ_CAN_RUN_SCRIPT nsresult
697 DeleteSelectionAsAction(nsIEditor::EDirection aDirectionAndAmount
,
698 nsIEditor::EStripWrappers aStripWrappers
,
699 nsIPrincipal
* aPrincipal
= nullptr);
701 enum class AllowBeforeInputEventCancelable
{
707 * Replace text in aReplaceRange or all text in this editor with aString and
708 * treat the change as inserting the string.
710 * @param aString The string to set.
711 * @param aReplaceRange The range to be replaced.
712 * If nullptr, all contents will be replaced.
713 * NOTE: Currently, nullptr is not allowed if
714 * the editor is an HTMLEditor.
715 * @param aAllowBeforeInputEventCancelable
716 * Whether `beforeinput` event which will be
717 * dispatched for this can be cancelable or not.
718 * @param aPrincipal Set subject principal if it may be called by
719 * JS. If set to nullptr, will be treated as
722 MOZ_CAN_RUN_SCRIPT nsresult
ReplaceTextAsAction(
723 const nsAString
& aString
, nsRange
* aReplaceRange
,
724 AllowBeforeInputEventCancelable aAllowBeforeInputEventCancelable
,
725 nsIPrincipal
* aPrincipal
= nullptr);
728 * Can we paste |aTransferable| or, if |aTransferable| is null, will a call
729 * to pasteTransferable later possibly succeed if given an instance of
730 * nsITransferable then? True if the doc is modifiable, and, if
731 * |aTransfeable| is non-null, we have pasteable data in |aTransfeable|.
733 virtual bool CanPasteTransferable(nsITransferable
* aTransferable
) = 0;
736 * PasteAsAction() pastes clipboard content to Selection. This method
737 * may dispatch ePaste event first. If its defaultPrevent() is called,
738 * this does nothing but returns NS_OK.
740 * @param aClipboardType nsIClipboard::kGlobalClipboard or
741 * nsIClipboard::kSelectionClipboard.
742 * @param aDispatchPasteEvent true if this should dispatch ePaste event
743 * before pasting. Otherwise, false.
744 * @param aPrincipal Set subject principal if it may be called by
745 * JS. If set to nullptr, will be treated as
748 MOZ_CAN_RUN_SCRIPT
virtual nsresult
PasteAsAction(
749 int32_t aClipboardType
, bool aDispatchPasteEvent
,
750 nsIPrincipal
* aPrincipal
= nullptr) = 0;
753 * Paste aTransferable at Selection.
755 * @param aTransferable Must not be nullptr.
756 * @param aPrincipal Set subject principal if it may be called by
757 * JS. If set to nullptr, will be treated as
760 MOZ_CAN_RUN_SCRIPT
virtual nsresult
PasteTransferableAsAction(
761 nsITransferable
* aTransferable
, nsIPrincipal
* aPrincipal
= nullptr) = 0;
764 * PasteAsQuotationAsAction() pastes content in clipboard as quotation.
765 * If the editor is TextEditor or in plaintext mode, will paste the content
766 * with appending ">" to start of each line.
767 * if the editor is HTMLEditor and is not in plaintext mode, will patste it
768 * into newly created blockquote element.
770 * @param aClipboardType nsIClipboard::kGlobalClipboard or
771 * nsIClipboard::kSelectionClipboard.
772 * @param aDispatchPasteEvent true if this should dispatch ePaste event
773 * before pasting. Otherwise, false.
774 * @param aPrincipal Set subject principal if it may be called by
775 * JS. If set to nullptr, will be treated as
778 MOZ_CAN_RUN_SCRIPT
virtual nsresult
PasteAsQuotationAsAction(
779 int32_t aClipboardType
, bool aDispatchPasteEvent
,
780 nsIPrincipal
* aPrincipal
= nullptr) = 0;
782 protected: // May be used by friends.
783 class AutoEditActionDataSetter
;
786 * TopLevelEditSubActionData stores temporary data while we're handling
787 * top-level edit sub-action.
789 struct MOZ_STACK_CLASS TopLevelEditSubActionData final
{
790 friend class AutoEditActionDataSetter
;
792 // If we have created a new block element, set to it.
793 RefPtr
<Element
> mNewBlockElement
;
795 // Set selected range before edit. Then, RangeUpdater keep modifying
796 // the range while we're changing the DOM tree.
797 RefPtr
<RangeItem
> mSelectedRange
;
799 // Computing changed range while we're handling sub actions.
800 RefPtr
<nsRange
> mChangedRange
;
802 // XXX In strict speaking, mCachedInlineStyles isn't enough to cache inline
803 // styles because inline style can be specified with "style" attribute
804 // and/or CSS in <style> elements or CSS files. So, we need to look
805 // for better implementation about this.
806 // FYI: Initialization cost of AutoStyleCacheArray is expensive and it is
807 // not used by TextEditor so that we should construct it only when
808 // we're an HTMLEditor.
809 Maybe
<AutoStyleCacheArray
> mCachedInlineStyles
;
811 // If we tried to delete selection, set to true.
812 bool mDidDeleteSelection
;
814 // If we have explicitly set selection inter line, set to true.
815 // `AfterEdit()` or something shouldn't overwrite it in such case.
816 bool mDidExplicitlySetInterLine
;
818 // If we have deleted non-collapsed range set to true, there are only 2
820 // - non-collapsed range was selected.
821 // - selection was collapsed in a text node and a Unicode character
823 bool mDidDeleteNonCollapsedRange
;
825 // If we have deleted parent empty blocks, set to true.
826 bool mDidDeleteEmptyParentBlocks
;
828 // If we're a contenteditable editor, we temporarily increase edit count
829 // of the document between `BeforeEdit()` and `AfterEdit()`. I.e., if
830 // we increased the count in `BeforeEdit()`, we need to decrease it in
831 // `AfterEdit()`, however, the document may be changed to designMode or
832 // non-editable. Therefore, we need to store with this whether we need
834 bool mRestoreContentEditableCount
;
836 // If we explicitly normalized whitespaces around the changed range,
838 bool mDidNormalizeWhitespaces
;
840 // Set to true by default. If somebody inserts an HTML fragment
841 // intentionally, any empty elements shouldn't be cleaned up later. In the
842 // case this is set to false.
843 // TODO: We should not do this by default. If it's necessary, each edit
844 // action handler do it by itself instead. Then, we can avoid such
845 // unnecessary DOM tree scan.
846 bool mNeedsToCleanUpEmptyElements
;
849 * The following methods modifies some data of this struct and
850 * `EditSubActionData` struct. Currently, these are required only
851 * by `HTMLEditor`. Therefore, for cutting the runtime cost of
852 * `TextEditor`, these methods should be called only by `HTMLEditor`.
853 * But it's fine to use these methods in `TextEditor` if necessary.
854 * If so, you need to call `DidDeleteText()` and `DidInsertText()`
855 * from `SetTextNodeWithoutTransaction()`.
857 void DidCreateElement(EditorBase
& aEditorBase
, Element
& aNewElement
);
858 void DidInsertContent(EditorBase
& aEditorBase
, nsIContent
& aNewContent
);
859 void WillDeleteContent(EditorBase
& aEditorBase
,
860 nsIContent
& aRemovingContent
);
861 void DidSplitContent(EditorBase
& aEditorBase
, nsIContent
& aSplitContent
,
862 nsIContent
& aNewContent
,
863 SplitNodeDirection aSplitNodeDirection
);
864 void DidJoinContents(EditorBase
& aEditorBase
,
865 const EditorRawDOMPoint
& aJoinedPoint
);
866 void DidInsertText(EditorBase
& aEditorBase
,
867 const EditorRawDOMPoint
& aInsertionBegin
,
868 const EditorRawDOMPoint
& aInsertionEnd
);
869 void DidDeleteText(EditorBase
& aEditorBase
,
870 const EditorRawDOMPoint
& aStartInTextNode
);
871 void WillDeleteRange(EditorBase
& aEditorBase
,
872 const EditorRawDOMPoint
& aStart
,
873 const EditorRawDOMPoint
& aEnd
);
877 mDidExplicitlySetInterLine
= false;
878 // We don't need to clear other members which are referred only when the
879 // editor is an HTML editor anymore. Note that if `mSelectedRange` is
880 // non-nullptr, that means that we're in `HTMLEditor`.
881 if (!mSelectedRange
) {
884 mNewBlockElement
= nullptr;
885 mSelectedRange
->Clear();
886 mChangedRange
->Reset();
887 if (mCachedInlineStyles
.isSome()) {
888 mCachedInlineStyles
->Clear();
890 mDidDeleteSelection
= false;
891 mDidDeleteNonCollapsedRange
= false;
892 mDidDeleteEmptyParentBlocks
= false;
893 mRestoreContentEditableCount
= false;
894 mDidNormalizeWhitespaces
= false;
895 mNeedsToCleanUpEmptyElements
= true;
899 * Extend mChangedRange to include `aNode`.
901 nsresult
AddNodeToChangedRange(const HTMLEditor
& aHTMLEditor
,
905 * Extend mChangedRange to include `aPoint`.
907 nsresult
AddPointToChangedRange(const HTMLEditor
& aHTMLEditor
,
908 const EditorRawDOMPoint
& aPoint
);
911 * Extend mChangedRange to include `aStart` and `aEnd`.
913 nsresult
AddRangeToChangedRange(const HTMLEditor
& aHTMLEditor
,
914 const EditorRawDOMPoint
& aStart
,
915 const EditorRawDOMPoint
& aEnd
);
917 TopLevelEditSubActionData() = default;
918 TopLevelEditSubActionData(const TopLevelEditSubActionData
& aOther
) = delete;
921 struct MOZ_STACK_CLASS EditSubActionData final
{
922 // While this is set to false, TopLevelEditSubActionData::mChangedRange
923 // shouldn't be modified since in some cases, modifying it in the setter
924 // itself may be faster. Note that we should affect this only for current
925 // edit sub action since mutation event listener may edit different range.
926 bool mAdjustChangedRangeFromListener
;
929 void Clear() { mAdjustChangedRangeFromListener
= true; }
934 protected: // AutoEditActionDataSetter, this shouldn't be accessed by friends.
936 * SettingDataTransfer enum class is used to specify whether DataTransfer
937 * should be initialized with or without format. For example, when user
938 * uses Accel + Shift + V to paste text without format, DataTransfer should
939 * have only plain/text data to make web apps treat it without format.
941 enum class SettingDataTransfer
{
947 * AutoEditActionDataSetter grabs some necessary objects for handling any
948 * edit actions and store the edit action what we're handling. When this is
949 * created, its pointer is set to the mEditActionData, and this guarantees
950 * the lifetime of grabbing objects until it's destroyed.
952 class MOZ_STACK_CLASS AutoEditActionDataSetter final
{
954 // NOTE: aPrincipal will be used when we implement "beforeinput" event.
955 // It's set only when maybe we shouldn't dispatch it because of
956 // called by JS. I.e., if this is nullptr, we can always dispatch
958 AutoEditActionDataSetter(const EditorBase
& aEditorBase
,
959 EditAction aEditAction
,
960 nsIPrincipal
* aPrincipal
= nullptr);
961 ~AutoEditActionDataSetter();
963 void UpdateEditAction(EditAction aEditAction
) {
964 MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent
,
965 "It's too late to update EditAction since this may have "
966 "already dispatched a beforeinput event");
967 mEditAction
= aEditAction
;
971 * CanHandle() or CanHandleAndHandleBeforeInput() must be called
972 * immediately after creating the instance. If caller does not need to
973 * handle "beforeinput" event or caller needs to set additional information
974 * the events later, use the former. Otherwise, use the latter. If caller
975 * uses the former, it's required to call MaybeDispatchBeforeInputEvent() by
979 [[nodiscard
]] bool CanHandle() const {
981 mHasCanHandleChecked
= true;
982 #endif // #ifdefn DEBUG
983 // Don't allow to run new edit action when an edit action caused
984 // destroying the editor while it's being handled.
985 if (mEditAction
!= EditAction::eInitializing
&&
986 mEditorWasDestroyedDuringHandlingEditAction
) {
987 NS_WARNING("Editor was destroyed during an edit action being handled");
990 return IsDataAvailable();
992 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
993 CanHandleAndMaybeDispatchBeforeInputEvent() {
994 if (MOZ_UNLIKELY(NS_WARN_IF(!CanHandle()))) {
995 return NS_ERROR_NOT_INITIALIZED
;
997 nsresult rv
= MaybeFlushPendingNotifications();
998 if (MOZ_UNLIKELY(NS_FAILED(rv
))) {
1001 return MaybeDispatchBeforeInputEvent();
1003 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1004 CanHandleAndFlushPendingNotifications() {
1005 if (MOZ_UNLIKELY(NS_WARN_IF(!CanHandle()))) {
1006 return NS_ERROR_NOT_INITIALIZED
;
1008 MOZ_ASSERT(MayEditActionRequireLayout(mRawEditAction
));
1009 return MaybeFlushPendingNotifications();
1012 [[nodiscard
]] bool IsDataAvailable() const {
1013 return mSelection
&& mEditorBase
.IsInitialized();
1017 * MaybeDispatchBeforeInputEvent() considers whether this instance needs to
1018 * dispatch "beforeinput" event or not. Then,
1019 * mHasTriedToDispatchBeforeInputEvent is set to true.
1021 * @param aDeleteDirectionAndAmount
1022 * If `MayEditActionDeleteAroundCollapsedSelection(
1023 * mEditAction)` returns true, this must be set.
1024 * Otherwise, don't set explicitly.
1025 * @return If this method actually dispatches "beforeinput" event
1026 * and it's canceled, returns
1027 * NS_ERROR_EDITOR_ACTION_CANCELED.
1029 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeDispatchBeforeInputEvent(
1030 nsIEditor::EDirection aDeleteDirectionAndAmount
= nsIEditor::eNone
);
1033 * MarkAsBeforeInputHasBeenDispatched() should be called only when updating
1034 * the DOM occurs asynchronously from user input (e.g., inserting blob
1035 * object which is loaded asynchronously) and `beforeinput` has already
1036 * been dispatched (always should be so).
1038 void MarkAsBeforeInputHasBeenDispatched() {
1039 MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent());
1040 MOZ_ASSERT(mEditAction
== EditAction::ePaste
||
1041 mEditAction
== EditAction::ePasteAsQuotation
||
1042 mEditAction
== EditAction::eDrop
);
1043 mHasTriedToDispatchBeforeInputEvent
= true;
1047 * MarkAsHandled() is called before dispatching `input` event and notifying
1048 * editor observers. After this is called, any nested edit action become
1051 void MarkAsHandled() {
1052 MOZ_ASSERT(!mHandled
);
1057 * ShouldAlreadyHaveHandledBeforeInputEventDispatching() returns true if the
1058 * edit action requires to handle "beforeinput" event but not yet dispatched
1059 * it nor considered as not dispatched it and can dispatch it when this is
1062 bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const {
1063 return !HasTriedToDispatchBeforeInputEvent() &&
1064 NeedsBeforeInputEventHandling(mEditAction
) &&
1065 IsBeforeInputEventEnabled() /* &&
1066 // If we still need to dispatch a clipboard event, we should
1067 // dispatch it first, then, we need to dispatch beforeinput
1069 !NeedsToDispatchClipboardEvent()*/
1074 * HasTriedToDispatchBeforeInputEvent() returns true if the instance's
1075 * MaybeDispatchBeforeInputEvent() has already been called.
1077 bool HasTriedToDispatchBeforeInputEvent() const {
1078 return mHasTriedToDispatchBeforeInputEvent
;
1081 bool IsCanceled() const { return mBeforeInputEventCanceled
; }
1084 * Returns a `Selection` for normal selection. The lifetime is guaranteed
1085 * during alive this instance in the stack.
1087 MOZ_KNOWN_LIVE Selection
& SelectionRef() const {
1088 MOZ_ASSERT(!mSelection
||
1089 (mSelection
->GetType() == SelectionType::eNormal
));
1093 nsIPrincipal
* GetPrincipal() const { return mPrincipal
; }
1094 EditAction
GetEditAction() const { return mEditAction
; }
1096 template <typename PT
, typename CT
>
1097 void SetSpellCheckRestartPoint(const EditorDOMPointBase
<PT
, CT
>& aPoint
) {
1098 MOZ_ASSERT(aPoint
.IsSet());
1099 // We should store only container and offset because new content may
1100 // be inserted before referring child.
1101 // XXX Shouldn't we compare whether aPoint is before
1102 // mSpellCheckRestartPoint if it's set.
1103 mSpellCheckRestartPoint
=
1104 EditorDOMPoint(aPoint
.GetContainer(), aPoint
.Offset());
1106 void ClearSpellCheckRestartPoint() { mSpellCheckRestartPoint
.Clear(); }
1107 const EditorDOMPoint
& GetSpellCheckRestartPoint() const {
1108 return mSpellCheckRestartPoint
;
1111 void SetData(const nsAString
& aData
) {
1112 MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent
,
1113 "It's too late to set data since this may have already "
1114 "dispatched a beforeinput event");
1117 const nsString
& GetData() const { return mData
; }
1119 void SetColorData(const nsAString
& aData
);
1122 * InitializeDataTransfer(DataTransfer*) sets mDataTransfer to
1123 * aDataTransfer. In this case, aDataTransfer should not be read/write
1124 * because it'll be set to InputEvent.dataTransfer and which should be
1127 void InitializeDataTransfer(dom::DataTransfer
* aDataTransfer
);
1129 * InitializeDataTransfer(nsITransferable*) creates new DataTransfer
1130 * instance, initializes it with aTransferable and sets mDataTransfer to
1133 void InitializeDataTransfer(nsITransferable
* aTransferable
);
1135 * InitializeDataTransfer(const nsAString&) creates new DataTransfer
1136 * instance, initializes it with aString and sets mDataTransfer to it.
1138 void InitializeDataTransfer(const nsAString
& aString
);
1140 * InitializeDataTransferWithClipboard() creates new DataTransfer instance,
1141 * initializes it with clipboard and sets mDataTransfer to it.
1143 void InitializeDataTransferWithClipboard(
1144 SettingDataTransfer aSettingDataTransfer
, int32_t aClipboardType
);
1145 dom::DataTransfer
* GetDataTransfer() const { return mDataTransfer
; }
1148 * AppendTargetRange() appends aTargetRange to target ranges. This should
1149 * be used only by edit action handlers which do not want to set target
1150 * ranges to selection ranges.
1152 void AppendTargetRange(dom::StaticRange
& aTargetRange
);
1155 * Make dispatching `beforeinput` forcibly non-cancelable.
1157 void MakeBeforeInputEventNonCancelable() {
1158 mMakeBeforeInputEventNonCancelable
= true;
1162 * NotifyOfDispatchingClipboardEvent() is called after dispatching
1163 * a clipboard event.
1165 void NotifyOfDispatchingClipboardEvent() {
1166 MOZ_ASSERT(NeedsToDispatchClipboardEvent());
1167 MOZ_ASSERT(!mHasTriedToDispatchClipboardEvent
);
1168 mHasTriedToDispatchClipboardEvent
= true;
1171 void Abort() { mAborted
= true; }
1172 bool IsAborted() const { return mAborted
; }
1174 void OnEditorDestroy() {
1175 if (!mHandled
&& mHasTriedToDispatchBeforeInputEvent
) {
1176 // Remember the editor was destroyed only when this edit action is being
1177 // handled because they are caused by mutation event listeners or
1178 // something other unexpected event listeners. In the cases, new child
1179 // edit action shouldn't been aborted.
1180 mEditorWasDestroyedDuringHandlingEditAction
= true;
1183 mParentData
->OnEditorDestroy();
1186 bool HasEditorDestroyedDuringHandlingEditAction() const {
1187 return mEditorWasDestroyedDuringHandlingEditAction
;
1190 void SetTopLevelEditSubAction(EditSubAction aEditSubAction
,
1191 EDirection aDirection
= eNone
) {
1192 mTopLevelEditSubAction
= aEditSubAction
;
1193 TopLevelEditSubActionDataRef().Clear();
1194 switch (mTopLevelEditSubAction
) {
1195 case EditSubAction::eInsertNode
:
1196 case EditSubAction::eCreateNode
:
1197 case EditSubAction::eSplitNode
:
1198 case EditSubAction::eInsertText
:
1199 case EditSubAction::eInsertTextComingFromIME
:
1200 case EditSubAction::eSetTextProperty
:
1201 case EditSubAction::eRemoveTextProperty
:
1202 case EditSubAction::eRemoveAllTextProperties
:
1203 case EditSubAction::eSetText
:
1204 case EditSubAction::eInsertLineBreak
:
1205 case EditSubAction::eInsertParagraphSeparator
:
1206 case EditSubAction::eCreateOrChangeList
:
1207 case EditSubAction::eIndent
:
1208 case EditSubAction::eOutdent
:
1209 case EditSubAction::eSetOrClearAlignment
:
1210 case EditSubAction::eCreateOrRemoveBlock
:
1211 case EditSubAction::eMergeBlockContents
:
1212 case EditSubAction::eRemoveList
:
1213 case EditSubAction::eCreateOrChangeDefinitionListItem
:
1214 case EditSubAction::eInsertElement
:
1215 case EditSubAction::eInsertQuotation
:
1216 case EditSubAction::eInsertQuotedText
:
1217 case EditSubAction::ePasteHTMLContent
:
1218 case EditSubAction::eInsertHTMLSource
:
1219 case EditSubAction::eSetPositionToAbsolute
:
1220 case EditSubAction::eSetPositionToStatic
:
1221 case EditSubAction::eDecreaseZIndex
:
1222 case EditSubAction::eIncreaseZIndex
:
1223 MOZ_ASSERT(aDirection
== eNext
);
1224 mDirectionOfTopLevelEditSubAction
= eNext
;
1226 case EditSubAction::eJoinNodes
:
1227 case EditSubAction::eDeleteText
:
1228 MOZ_ASSERT(aDirection
== ePrevious
);
1229 mDirectionOfTopLevelEditSubAction
= ePrevious
;
1231 case EditSubAction::eUndo
:
1232 case EditSubAction::eRedo
:
1233 case EditSubAction::eComputeTextToOutput
:
1234 case EditSubAction::eCreatePaddingBRElementForEmptyEditor
:
1235 case EditSubAction::eNone
:
1236 case EditSubAction::eReplaceHeadWithHTMLSource
:
1237 MOZ_ASSERT(aDirection
== eNone
);
1238 mDirectionOfTopLevelEditSubAction
= eNone
;
1240 case EditSubAction::eDeleteNode
:
1241 case EditSubAction::eDeleteSelectedContent
:
1242 // Unfortunately, eDeleteNode and eDeleteSelectedContent is used with
1243 // any direction. We might have specific sub-action for each
1244 // direction, but there are some points referencing
1245 // eDeleteSelectedContent so that we should keep storing direction
1247 mDirectionOfTopLevelEditSubAction
= aDirection
;
1251 EditSubAction
GetTopLevelEditSubAction() const {
1252 MOZ_ASSERT(IsDataAvailable());
1253 return mTopLevelEditSubAction
;
1255 EDirection
GetDirectionOfTopLevelEditSubAction() const {
1256 return mDirectionOfTopLevelEditSubAction
;
1259 const TopLevelEditSubActionData
& TopLevelEditSubActionDataRef() const {
1260 return mParentData
? mParentData
->TopLevelEditSubActionDataRef()
1261 : mTopLevelEditSubActionData
;
1263 TopLevelEditSubActionData
& TopLevelEditSubActionDataRef() {
1264 return mParentData
? mParentData
->TopLevelEditSubActionDataRef()
1265 : mTopLevelEditSubActionData
;
1268 const EditSubActionData
& EditSubActionDataRef() const {
1269 return mEditSubActionData
;
1271 EditSubActionData
& EditSubActionDataRef() { return mEditSubActionData
; }
1273 SelectionState
& SavedSelectionRef() {
1274 return mParentData
? mParentData
->SavedSelectionRef() : mSavedSelection
;
1276 const SelectionState
& SavedSelectionRef() const {
1277 return mParentData
? mParentData
->SavedSelectionRef() : mSavedSelection
;
1280 RangeUpdater
& RangeUpdaterRef() {
1281 return mParentData
? mParentData
->RangeUpdaterRef() : mRangeUpdater
;
1283 const RangeUpdater
& RangeUpdaterRef() const {
1284 return mParentData
? mParentData
->RangeUpdaterRef() : mRangeUpdater
;
1287 void UpdateSelectionCache(Selection
& aSelection
);
1290 bool IsBeforeInputEventEnabled() const;
1292 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1293 MaybeFlushPendingNotifications() const;
1295 static bool NeedsBeforeInputEventHandling(EditAction aEditAction
) {
1296 MOZ_ASSERT(aEditAction
!= EditAction::eNone
);
1297 switch (aEditAction
) {
1298 case EditAction::eNone
:
1299 // If we're not handling edit action, we don't need to handle
1300 // "beforeinput" event.
1301 case EditAction::eNotEditing
:
1302 // If we're being initialized, we may need to create a padding <br>
1303 // element, but it shouldn't cause `beforeinput` event.
1304 case EditAction::eInitializing
:
1305 // If we're just selecting or getting table cells, we shouldn't
1306 // dispatch `beforeinput` event.
1307 case NS_EDIT_ACTION_CASES_ACCESSING_TABLE_DATA_WITHOUT_EDITING
:
1308 // If raw level transaction API is used, the API user needs to handle
1309 // both "beforeinput" event and "input" event if it's necessary.
1310 case EditAction::eUnknown
:
1311 // Hiding/showing password affects only layout so that we don't need
1312 // to handle beforeinput event for it.
1313 case EditAction::eHidePassword
:
1314 // We don't need to dispatch "beforeinput" event before
1315 // "compositionstart".
1316 case EditAction::eStartComposition
:
1317 // We don't need to let web apps know the mode change.
1318 case EditAction::eEnableOrDisableCSS
:
1319 case EditAction::eEnableOrDisableAbsolutePositionEditor
:
1320 case EditAction::eEnableOrDisableResizer
:
1321 case EditAction::eEnableOrDisableInlineTableEditingUI
:
1322 // We don't need to let contents in chrome's editor to know the size
1324 case EditAction::eSetWrapWidth
:
1325 // While resizing or moving element, we update only shadow, i.e.,
1326 // don't touch to the DOM in content. Therefore, we don't need to
1327 // dispatch "beforeinput" event.
1328 case EditAction::eResizingElement
:
1329 case EditAction::eMovingElement
:
1330 // Perhaps, we don't need to dispatch "beforeinput" event for
1331 // padding `<br>` element for empty editor because it's internal
1332 // handling and it should be occurred by another change.
1333 case EditAction::eCreatePaddingBRElementForEmptyEditor
:
1340 bool NeedsToDispatchClipboardEvent() const {
1341 if (mHasTriedToDispatchClipboardEvent
) {
1344 switch (mEditAction
) {
1345 case EditAction::ePaste
:
1346 case EditAction::ePasteAsQuotation
:
1347 case EditAction::eCut
:
1348 case EditAction::eCopy
:
1355 EditorBase
& mEditorBase
;
1356 RefPtr
<Selection
> mSelection
;
1357 nsTArray
<OwningNonNull
<Selection
>> mRetiredSelections
;
1358 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
1359 // EditAction may be nested, for example, a command may be executed
1360 // from mutation event listener which is run while editor changes
1361 // the DOM tree. In such case, we need to handle edit action separately.
1362 AutoEditActionDataSetter
* mParentData
;
1364 // Cached selection for HTMLEditor::AutoSelectionRestorer.
1365 SelectionState mSavedSelection
;
1367 // Utility class object for maintaining preserved ranges.
1368 RangeUpdater mRangeUpdater
;
1370 // The data should be set to InputEvent.data.
1373 // The dataTransfer should be set to InputEvent.dataTransfer.
1374 RefPtr
<dom::DataTransfer
> mDataTransfer
;
1376 // They are used for result of InputEvent.getTargetRanges() of beforeinput.
1377 OwningNonNullStaticRangeArray mTargetRanges
;
1379 // Start point where spell checker should check from. This is used only
1381 EditorDOMPoint mSpellCheckRestartPoint
;
1383 // Different from mTopLevelEditSubAction, its data should be stored only
1384 // in the most ancestor AutoEditActionDataSetter instance since we don't
1385 // want to pay the copying cost and sync cost.
1386 TopLevelEditSubActionData mTopLevelEditSubActionData
;
1388 // Different from mTopLevelEditSubActionData, this stores temporaly data
1389 // for current edit sub action.
1390 EditSubActionData mEditSubActionData
;
1392 // mEditAction and mRawEditActions stores edit action. The difference of
1393 // them is, if and only if edit actions are nested and parent edit action
1394 // is one of trying to edit something, but nested one is not so, it's
1395 // overwritten by the parent edit action.
1396 EditAction mEditAction
;
1397 EditAction mRawEditAction
;
1399 // Different from its data, you can refer "current" AutoEditActionDataSetter
1400 // instance's mTopLevelEditSubAction member since it's copied from the
1401 // parent instance at construction and it's always cleared before this
1402 // won't be overwritten and cleared before destruction.
1403 EditSubAction mTopLevelEditSubAction
;
1405 EDirection mDirectionOfTopLevelEditSubAction
;
1409 // Set to true when this handles "beforeinput" event dispatching. Note
1410 // that even if "beforeinput" event shouldn't be dispatched for this,
1411 // instance, this is set to true when it's considered.
1412 bool mHasTriedToDispatchBeforeInputEvent
;
1413 // Set to true if "beforeinput" event was dispatched and it's canceled.
1414 bool mBeforeInputEventCanceled
;
1415 // Set to true if `beforeinput` event must not be cancelable even if
1416 // its inputType is defined as cancelable by the standards.
1417 bool mMakeBeforeInputEventNonCancelable
;
1418 // Set to true when the edit action handler tries to dispatch a clipboard
1420 bool mHasTriedToDispatchClipboardEvent
;
1421 // The editor instance may be destroyed once temporarily if `document.write`
1422 // etc runs. In such case, we should mark this flag of being handled
1424 bool mEditorWasDestroyedDuringHandlingEditAction
;
1425 // This is set before dispatching `input` event and notifying editor
1430 mutable bool mHasCanHandleChecked
= false;
1431 #endif // #ifdef DEBUG
1433 AutoEditActionDataSetter() = delete;
1434 AutoEditActionDataSetter(const AutoEditActionDataSetter
& aOther
) = delete;
1437 void UpdateEditActionData(const nsAString
& aData
) {
1438 mEditActionData
->SetData(aData
);
1441 void NotifyOfDispatchingClipboardEvent() {
1442 MOZ_ASSERT(mEditActionData
);
1443 mEditActionData
->NotifyOfDispatchingClipboardEvent();
1446 protected: // May be called by friends.
1447 /****************************************************************************
1448 * Some friend classes are allowed to call the following protected methods.
1449 * However, those methods won't prepare caches of some objects which are
1450 * necessary for them. So, if you call them from friend classes, you need
1451 * to make sure that AutoEditActionDataSetter is created.
1452 ****************************************************************************/
1454 bool IsEditActionCanceled() const {
1455 MOZ_ASSERT(mEditActionData
);
1456 return mEditActionData
->IsCanceled();
1459 bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const {
1460 MOZ_ASSERT(mEditActionData
);
1461 return mEditActionData
1462 ->ShouldAlreadyHaveHandledBeforeInputEventDispatching();
1465 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeDispatchBeforeInputEvent() {
1466 MOZ_ASSERT(mEditActionData
);
1467 return mEditActionData
->MaybeDispatchBeforeInputEvent();
1470 void MarkAsBeforeInputHasBeenDispatched() {
1471 MOZ_ASSERT(mEditActionData
);
1472 return mEditActionData
->MarkAsBeforeInputHasBeenDispatched();
1475 bool HasTriedToDispatchBeforeInputEvent() const {
1476 return mEditActionData
&&
1477 mEditActionData
->HasTriedToDispatchBeforeInputEvent();
1480 bool IsEditActionDataAvailable() const {
1481 return mEditActionData
&& mEditActionData
->IsDataAvailable();
1484 bool IsTopLevelEditSubActionDataAvailable() const {
1485 return mEditActionData
&& !!GetTopLevelEditSubAction();
1488 bool IsEditActionAborted() const {
1489 MOZ_ASSERT(mEditActionData
);
1490 return mEditActionData
->IsAborted();
1494 * SelectionRef() returns cached normal Selection. This is pretty faster than
1495 * EditorBase::GetSelection() if available.
1496 * Note that this never crash unless public methods ignore the result of
1497 * AutoEditActionDataSetter::CanHandle() and keep handling edit action but any
1498 * methods should stop handling edit action if it returns false.
1500 MOZ_KNOWN_LIVE Selection
& SelectionRef() const {
1501 MOZ_ASSERT(mEditActionData
);
1502 MOZ_ASSERT(mEditActionData
->SelectionRef().GetType() ==
1503 SelectionType::eNormal
);
1504 return mEditActionData
->SelectionRef();
1507 nsIPrincipal
* GetEditActionPrincipal() const {
1508 MOZ_ASSERT(mEditActionData
);
1509 return mEditActionData
->GetPrincipal();
1513 * GetEditAction() returns EditAction which is being handled. If some
1514 * edit actions are nested, this returns the innermost edit action.
1516 EditAction
GetEditAction() const {
1517 return mEditActionData
? mEditActionData
->GetEditAction()
1518 : EditAction::eNone
;
1522 * GetInputEventData() returns inserting or inserted text value with
1523 * current edit action. The result is proper for InputEvent.data value.
1525 const nsString
& GetInputEventData() const {
1526 return mEditActionData
? mEditActionData
->GetData() : VoidString();
1530 * GetInputEventDataTransfer() returns inserting or inserted transferable
1531 * content with current edit action. The result is proper for
1532 * InputEvent.dataTransfer value.
1534 dom::DataTransfer
* GetInputEventDataTransfer() const {
1535 return mEditActionData
? mEditActionData
->GetDataTransfer() : nullptr;
1539 * GetTopLevelEditSubAction() returns the top level edit sub-action.
1540 * For example, if selected content is being replaced with inserted text,
1541 * while removing selected content, the top level edit sub-action may be
1542 * EditSubAction::eDeleteSelectedContent. However, while inserting new
1543 * text, the top level edit sub-action may be EditSubAction::eInsertText.
1544 * So, this result means what we are doing right now unless you're looking
1545 * for a case which the method is called via mutation event listener or
1546 * selectionchange event listener which are fired while handling the edit
1549 EditSubAction
GetTopLevelEditSubAction() const {
1550 return mEditActionData
? mEditActionData
->GetTopLevelEditSubAction()
1551 : EditSubAction::eNone
;
1555 * GetDirectionOfTopLevelEditSubAction() returns direction which user
1556 * intended for doing the edit sub-action.
1558 EDirection
GetDirectionOfTopLevelEditSubAction() const {
1559 return mEditActionData
1560 ? mEditActionData
->GetDirectionOfTopLevelEditSubAction()
1565 * SavedSelection() returns reference to saved selection which are
1566 * stored by HTMLEditor::AutoSelectionRestorer.
1568 SelectionState
& SavedSelectionRef() {
1569 MOZ_ASSERT(IsHTMLEditor());
1570 MOZ_ASSERT(IsEditActionDataAvailable());
1571 return mEditActionData
->SavedSelectionRef();
1573 const SelectionState
& SavedSelectionRef() const {
1574 MOZ_ASSERT(IsHTMLEditor());
1575 MOZ_ASSERT(IsEditActionDataAvailable());
1576 return mEditActionData
->SavedSelectionRef();
1579 RangeUpdater
& RangeUpdaterRef() {
1580 MOZ_ASSERT(IsEditActionDataAvailable());
1581 return mEditActionData
->RangeUpdaterRef();
1583 const RangeUpdater
& RangeUpdaterRef() const {
1584 MOZ_ASSERT(IsEditActionDataAvailable());
1585 return mEditActionData
->RangeUpdaterRef();
1588 template <typename PT
, typename CT
>
1589 void SetSpellCheckRestartPoint(const EditorDOMPointBase
<PT
, CT
>& aPoint
) {
1590 MOZ_ASSERT(IsEditActionDataAvailable());
1591 return mEditActionData
->SetSpellCheckRestartPoint(aPoint
);
1594 void ClearSpellCheckRestartPoint() {
1595 MOZ_ASSERT(IsEditActionDataAvailable());
1596 return mEditActionData
->ClearSpellCheckRestartPoint();
1599 const EditorDOMPoint
& GetSpellCheckRestartPoint() const {
1600 MOZ_ASSERT(IsEditActionDataAvailable());
1601 return mEditActionData
->GetSpellCheckRestartPoint();
1604 const TopLevelEditSubActionData
& TopLevelEditSubActionDataRef() const {
1605 MOZ_ASSERT(IsEditActionDataAvailable());
1606 return mEditActionData
->TopLevelEditSubActionDataRef();
1608 TopLevelEditSubActionData
& TopLevelEditSubActionDataRef() {
1609 MOZ_ASSERT(IsEditActionDataAvailable());
1610 return mEditActionData
->TopLevelEditSubActionDataRef();
1613 const EditSubActionData
& EditSubActionDataRef() const {
1614 MOZ_ASSERT(IsEditActionDataAvailable());
1615 return mEditActionData
->EditSubActionDataRef();
1617 EditSubActionData
& EditSubActionDataRef() {
1618 MOZ_ASSERT(IsEditActionDataAvailable());
1619 return mEditActionData
->EditSubActionDataRef();
1623 * GetFirstIMESelectionStartPoint() and GetLastIMESelectionEndPoint() returns
1624 * start of first IME selection range or end of last IME selection range if
1625 * there is. Otherwise, returns non-set DOM point.
1627 template <typename EditorDOMPointType
>
1628 EditorDOMPointType
GetFirstIMESelectionStartPoint() const;
1629 template <typename EditorDOMPointType
>
1630 EditorDOMPointType
GetLastIMESelectionEndPoint() const;
1633 * IsSelectionRangeContainerNotContent() returns true if one of container
1634 * of selection ranges is not a content node, i.e., a Document node.
1636 bool IsSelectionRangeContainerNotContent() const;
1639 * OnInputText() is called when user inputs text with keyboard or something.
1641 * @param aStringToInsert The string to insert.
1643 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1644 OnInputText(const nsAString
& aStringToInsert
);
1647 * InsertTextAsSubAction() inserts aStringToInsert at selection. This
1648 * should be used for handling it as an edit sub-action.
1650 * @param aStringToInsert The string to insert.
1651 * @param aSelectionHandling Specify whether selected content should be
1652 * deleted or ignored.
1654 enum class SelectionHandling
{ Ignore
, Delete
};
1655 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
InsertTextAsSubAction(
1656 const nsAString
& aStringToInsert
, SelectionHandling aSelectionHandling
);
1659 * InsertTextWithTransaction() inserts aStringToInsert to aPointToInsert or
1660 * better insertion point around it. If aPointToInsert isn't in a text node,
1661 * this method looks for the nearest point in a text node with
1662 * FindBetterInsertionPoint(). If there is no text node, this creates
1663 * new text node and put aStringToInsert to it.
1665 * @param aDocument The document of this editor.
1666 * @param aStringToInsert The string to insert.
1667 * @param aPointToInsert The point to insert aStringToInsert.
1668 * Must be valid DOM point.
1669 * @return If succeeded, returns the point after inserted
1670 * aStringToInsert. So, when this method actually
1671 * inserts string, returns a point in the text node.
1672 * Otherwise, returns aPointToInsert.
1674 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual Result
<EditorDOMPoint
, nsresult
>
1675 InsertTextWithTransaction(Document
& aDocument
,
1676 const nsAString
& aStringToInsert
,
1677 const EditorDOMPoint
& aPointToInsert
);
1680 * InsertTextIntoTextNodeWithTransaction() inserts aStringToInsert into
1681 * aOffset of aTextNode with transaction.
1683 * @param aStringToInsert String to be inserted.
1684 * @param aPointToInsert The insertion point.
1685 * @param aSuppressIME true if it's not a part of IME composition.
1686 * E.g., adjusting white-spaces during composition.
1689 MOZ_CAN_RUN_SCRIPT nsresult
InsertTextIntoTextNodeWithTransaction(
1690 const nsAString
& aStringToInsert
,
1691 const EditorDOMPointInText
& aPointToInsert
, bool aSuppressIME
= false);
1694 * SetTextNodeWithoutTransaction() is optimized path to set new value to
1695 * the text node directly and without transaction. This is used when
1696 * setting `<input>.value` and `<textarea>.value`.
1698 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1699 SetTextNodeWithoutTransaction(const nsAString
& aString
, Text
& aTextNode
);
1702 * DeleteNodeWithTransaction() removes aContent from the DOM tree.
1704 * @param aContent The node which will be removed form the DOM tree.
1706 virtual MOZ_CAN_RUN_SCRIPT nsresult
1707 DeleteNodeWithTransaction(nsIContent
& aContent
);
1710 * InsertNodeWithTransaction() inserts aContentToInsert before the child
1711 * specified by aPointToInsert.
1713 * @param aContentToInsert The node to be inserted.
1714 * @param aPointToInsert The insertion point of aContentToInsert.
1715 * If this refers end of the container, the
1716 * transaction will append the node to the
1717 * container. Otherwise, will insert the node
1718 * before child node referred by this.
1719 * @return If succeeded, returns the new content node and
1720 * point to put caret.
1722 template <typename ContentNodeType
>
1723 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT CreateNodeResultBase
<ContentNodeType
>
1724 InsertNodeWithTransaction(ContentNodeType
& aContentToInsert
,
1725 const EditorDOMPoint
& aPointToInsert
);
1728 * InsertPaddingBRElementForEmptyLastLineWithTransaction() creates a padding
1729 * <br> element with setting flags to NS_PADDING_FOR_EMPTY_LAST_LINE and
1730 * inserts it around aPointToInsert.
1732 * @param aPointToInsert The DOM point where should be <br> node inserted
1734 * @return If succeeded, returns the new <br> element and
1735 * point to put caret around it.
1737 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT CreateElementResult
1738 InsertPaddingBRElementForEmptyLastLineWithTransaction(
1739 const EditorDOMPoint
& aPointToInsert
);
1742 * CloneAttributesWithTransaction() clones all attributes from
1743 * aSourceElement to aDestElement after removing all attributes in
1746 MOZ_CAN_RUN_SCRIPT
void CloneAttributesWithTransaction(
1747 Element
& aDestElement
, Element
& aSourceElement
);
1750 * CloneAttributeWithTransaction() copies aAttribute of aSourceElement to
1751 * aDestElement. If aSourceElement doesn't have aAttribute, this removes
1752 * aAttribute from aDestElement.
1754 * @param aAttribute Attribute name to be cloned.
1755 * @param aDestElement Element node which will be set aAttribute or
1756 * whose aAttribute will be removed.
1757 * @param aSourceElement Element node which provides the value of
1758 * aAttribute in aDestElement.
1760 MOZ_CAN_RUN_SCRIPT nsresult
CloneAttributeWithTransaction(
1761 nsAtom
& aAttribute
, Element
& aDestElement
, Element
& aSourceElement
);
1764 * RemoveAttributeWithTransaction() removes aAttribute from aElement.
1766 * @param aElement Element node which will lose aAttribute.
1767 * @param aAttribute Attribute name to be removed from aElement.
1769 MOZ_CAN_RUN_SCRIPT nsresult
1770 RemoveAttributeWithTransaction(Element
& aElement
, nsAtom
& aAttribute
);
1772 MOZ_CAN_RUN_SCRIPT
virtual nsresult
RemoveAttributeOrEquivalent(
1773 Element
* aElement
, nsAtom
* aAttribute
, bool aSuppressTransaction
) = 0;
1776 * SetAttributeWithTransaction() sets aAttribute of aElement to aValue.
1778 * @param aElement Element node which will have aAttribute.
1779 * @param aAttribute Attribute name to be set.
1780 * @param aValue Attribute value be set to aAttribute.
1782 MOZ_CAN_RUN_SCRIPT nsresult
SetAttributeWithTransaction(
1783 Element
& aElement
, nsAtom
& aAttribute
, const nsAString
& aValue
);
1785 MOZ_CAN_RUN_SCRIPT
virtual nsresult
SetAttributeOrEquivalent(
1786 Element
* aElement
, nsAtom
* aAttribute
, const nsAString
& aValue
,
1787 bool aSuppressTransaction
) = 0;
1790 * Method to replace certain CreateElementNS() calls.
1792 * @param aTag Tag you want.
1794 already_AddRefed
<Element
> CreateHTMLContent(const nsAtom
* aTag
) const;
1797 * Creates text node which is marked as "maybe modified frequently" and
1798 * "maybe masked" if this is a password editor.
1800 already_AddRefed
<nsTextNode
> CreateTextNode(const nsAString
& aData
) const;
1803 * DoInsertText(), DoDeleteText(), DoReplaceText() and DoSetText() are
1804 * wrapper of `CharacterData::InsertData()`, `CharacterData::DeleteData()`,
1805 * `CharacterData::ReplaceData()` and `CharacterData::SetData()`.
1807 MOZ_CAN_RUN_SCRIPT
void DoInsertText(dom::Text
& aText
, uint32_t aOffset
,
1808 const nsAString
& aStringToInsert
,
1810 MOZ_CAN_RUN_SCRIPT
void DoDeleteText(dom::Text
& aText
, uint32_t aOffset
,
1811 uint32_t aCount
, ErrorResult
& aRv
);
1812 MOZ_CAN_RUN_SCRIPT
void DoReplaceText(dom::Text
& aText
, uint32_t aOffset
,
1814 const nsAString
& aStringToInsert
,
1816 MOZ_CAN_RUN_SCRIPT
void DoSetText(dom::Text
& aText
,
1817 const nsAString
& aStringToSet
,
1821 * DeleteTextWithTransaction() removes text in the range from aTextNode.
1823 * @param aTextNode The text node which should be modified.
1824 * @param aOffset Start offset of removing text in aTextNode.
1825 * @param aLength Length of removing text.
1827 MOZ_CAN_RUN_SCRIPT nsresult
DeleteTextWithTransaction(dom::Text
& aTextNode
,
1832 * MarkElementDirty() sets a special dirty attribute on the element.
1833 * Usually this will be called immediately after creating a new node.
1835 * @param aElement The element for which to insert formatting.
1837 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1838 MarkElementDirty(Element
& aElement
) const;
1840 MOZ_CAN_RUN_SCRIPT nsresult
1841 DoTransactionInternal(nsITransaction
* aTransaction
);
1844 * Returns true if aNode is our root node.
1846 bool IsRoot(const nsINode
* inNode
) const;
1847 bool IsEditorRoot(const nsINode
* aNode
) const;
1850 * Returns true if aNode is a descendant of our root node.
1852 bool IsDescendantOfRoot(const nsINode
* inNode
) const;
1853 bool IsDescendantOfEditorRoot(const nsINode
* aNode
) const;
1856 * Returns true when inserting text should be a part of current composition.
1858 bool ShouldHandleIMEComposition() const;
1860 template <typename EditorDOMPointType
>
1861 EditorDOMPointType
GetFirstSelectionStartPoint() const;
1862 template <typename EditorDOMPointType
>
1863 EditorDOMPointType
GetFirstSelectionEndPoint() const;
1865 static nsresult
GetEndChildNode(const Selection
& aSelection
,
1866 nsIContent
** aEndNode
);
1868 template <typename PT
, typename CT
>
1869 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1870 CollapseSelectionTo(const EditorDOMPointBase
<PT
, CT
>& aPoint
) const {
1871 // We don't need to throw exception directly for a failure of updating
1872 // selection. Therefore, let's use IgnoredErrorResult for the performance.
1873 IgnoredErrorResult error
;
1874 CollapseSelectionTo(aPoint
, error
);
1875 return error
.StealNSResult();
1878 template <typename PT
, typename CT
>
1879 MOZ_CAN_RUN_SCRIPT
void CollapseSelectionTo(
1880 const EditorDOMPointBase
<PT
, CT
>& aPoint
, ErrorResult
& aRv
) const {
1881 MOZ_ASSERT(IsEditActionDataAvailable());
1882 MOZ_ASSERT(!aRv
.Failed());
1884 if (aPoint
.GetInterlinePosition() != InterlinePosition::Undefined
) {
1885 if (MOZ_UNLIKELY(NS_FAILED(SelectionRef().SetInterlinePosition(
1886 aPoint
.GetInterlinePosition())))) {
1887 NS_WARNING("Selection::SetInterlinePosition() failed");
1888 aRv
.Throw(NS_ERROR_FAILURE
);
1893 SelectionRef().CollapseInLimiter(aPoint
, aRv
);
1894 if (MOZ_UNLIKELY(Destroyed())) {
1895 NS_WARNING("Selection::CollapseInLimiter() caused destroying the editor");
1896 aRv
.Throw(NS_ERROR_EDITOR_DESTROYED
);
1899 NS_WARNING_ASSERTION(!aRv
.Failed(),
1900 "Selection::CollapseInLimiter() failed");
1903 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1904 CollapseSelectionToStartOf(nsINode
& aNode
) const {
1905 return CollapseSelectionTo(EditorRawDOMPoint(&aNode
, 0u));
1908 MOZ_CAN_RUN_SCRIPT
void CollapseSelectionToStartOf(nsINode
& aNode
,
1909 ErrorResult
& aRv
) const {
1910 CollapseSelectionTo(EditorRawDOMPoint(&aNode
, 0u), aRv
);
1913 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1914 CollapseSelectionToEndOf(nsINode
& aNode
) const {
1915 return CollapseSelectionTo(EditorRawDOMPoint::AtEndOf(aNode
));
1918 MOZ_CAN_RUN_SCRIPT
void CollapseSelectionToEndOf(nsINode
& aNode
,
1919 ErrorResult
& aRv
) const {
1920 CollapseSelectionTo(EditorRawDOMPoint::AtEndOf(aNode
), aRv
);
1924 * CollapseSelectionToEnd() collapses the selection to the last leaf content
1927 MOZ_CAN_RUN_SCRIPT nsresult
CollapseSelectionToEndOfLastLeafNode() const;
1930 * AllowsTransactionsToChangeSelection() returns true if editor allows any
1931 * transactions to change Selection. Otherwise, transactions shouldn't
1934 inline bool AllowsTransactionsToChangeSelection() const {
1935 return mAllowsTransactionsToChangeSelection
;
1939 * MakeThisAllowTransactionsToChangeSelection() with true makes this editor
1940 * allow transactions to change Selection. Otherwise, i.e., with false,
1941 * makes this editor not allow transactions to change Selection.
1943 inline void MakeThisAllowTransactionsToChangeSelection(bool aAllow
) {
1944 mAllowsTransactionsToChangeSelection
= aAllow
;
1947 nsresult
HandleInlineSpellCheck(
1948 const EditorDOMPoint
& aPreviouslySelectedStart
,
1949 const dom::AbstractRange
* aRange
= nullptr);
1952 * Likewise, but gets the editor's root instead, which is different for HTML
1955 virtual Element
* GetEditorRoot() const;
1958 * Whether the editor is active on the DOM window. Note that when this
1959 * returns true but GetFocusedContent() returns null, it means that this
1960 * editor was focused when the DOM window was active.
1962 virtual bool IsActiveInDOMWindow() const;
1965 * FindBetterInsertionPoint() tries to look for better insertion point which
1966 * is typically the nearest text node and offset in it.
1968 * @param aPoint Insertion point which the callers found.
1969 * @return Better insertion point if there is. If not returns
1970 * same point as aPoint.
1972 template <typename EditorDOMPointType
>
1973 EditorDOMPointType
FindBetterInsertionPoint(
1974 const EditorDOMPointType
& aPoint
) const;
1977 * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
1978 * with nsCaret::RemoveForceHide(). This does NOT set visibility of
1979 * nsCaret. Therefore, this is stateless.
1981 void HideCaret(bool aHide
);
1983 protected: // Edit sub-action handler
1985 * AutoCaretBidiLevelManager() computes bidi level of caret, deleting
1986 * character(s) from aPointAtCaret at construction. Then, if you'll
1987 * need to extend the selection, you should calls `UpdateCaretBidiLevel()`,
1988 * then, this class may update caret bidi level for you if it's required.
1990 class MOZ_RAII AutoCaretBidiLevelManager final
{
1993 * @param aEditorBase The editor.
1994 * @param aPointAtCaret Collapsed `Selection` point.
1995 * @param aDirectionAndAmount The direction and amount to delete.
1997 template <typename PT
, typename CT
>
1998 AutoCaretBidiLevelManager(const EditorBase
& aEditorBase
,
1999 nsIEditor::EDirection aDirectionAndAmount
,
2000 const EditorDOMPointBase
<PT
, CT
>& aPointAtCaret
);
2003 * Failed() returns true if the constructor failed to handle the bidi
2006 bool Failed() const { return mFailed
; }
2009 * Canceled() returns true if when the caller should stop deleting
2010 * characters since caret position is not visually adjacent the deleting
2011 * characters and user does not wand to delete them in that case.
2013 bool Canceled() const { return mCanceled
; }
2016 * MaybeUpdateCaretBidiLevel() may update caret bidi level and schedule to
2017 * paint it if they are necessary.
2019 void MaybeUpdateCaretBidiLevel(const EditorBase
& aEditorBase
) const;
2022 Maybe
<mozilla::intl::BidiEmbeddingLevel
> mNewCaretBidiLevel
;
2023 bool mFailed
= false;
2024 bool mCanceled
= false;
2028 * UndefineCaretBidiLevel() resets bidi level of the caret.
2030 void UndefineCaretBidiLevel() const;
2033 * Flushing pending notifications if nsFrameSelection requires the latest
2034 * layout information to compute deletion range. This may destroy the
2035 * editor instance itself. When this returns false, don't keep doing
2038 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
bool
2039 FlushPendingNotificationsIfToHandleDeletionWithFrameSelection(
2040 nsIEditor::EDirection aDirectionAndAmount
) const;
2043 * DeleteSelectionAsSubAction() removes selection content or content around
2044 * caret with transactions. This should be used for handling it as an
2047 * @param aDirectionAndAmount How much range should be removed.
2048 * @param aStripWrappers Whether the parent blocks should be removed
2049 * when they become empty. If this instance is
2050 * a TextEditor, Must be nsIEditor::eNoStrip.
2052 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2053 DeleteSelectionAsSubAction(nsIEditor::EDirection aDirectionAndAmount
,
2054 nsIEditor::EStripWrappers aStripWrappers
);
2057 * This method handles "delete selection" commands.
2058 * NOTE: Don't call this method recursively from the helper methods since
2059 * when nobody handled it without canceling and returing an error,
2060 * this falls it back to `DeleteSelectionWithTransaction()`.
2062 * @param aDirectionAndAmount Direction of the deletion.
2063 * @param aStripWrappers Must be nsIEditor::eNoStrip if this is a
2064 * TextEditor instance. Otherwise,
2065 * nsIEditor::eStrip is also valid.
2067 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual EditActionResult
2068 HandleDeleteSelection(nsIEditor::EDirection aDirectionAndAmount
,
2069 nsIEditor::EStripWrappers aStripWrappers
) = 0;
2072 * ReplaceSelectionAsSubAction() replaces selection with aString.
2074 * @param aString The string to replace.
2076 MOZ_CAN_RUN_SCRIPT nsresult
2077 ReplaceSelectionAsSubAction(const nsAString
& aString
);
2080 * HandleInsertText() handles inserting text at selection.
2082 * @param aEditSubAction Must be EditSubAction::eInsertText or
2083 * EditSubAction::eInsertTextComingFromIME.
2084 * @param aInsertionString String to be inserted at selection.
2085 * @param aSelectionHandling Specify whether selected content should be
2086 * deleted or ignored.
2088 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual EditActionResult
HandleInsertText(
2089 EditSubAction aEditSubAction
, const nsAString
& aInsertionString
,
2090 SelectionHandling aSelectionHandling
) = 0;
2093 * InsertWithQuotationsAsSubAction() inserts aQuotedText with appending ">"
2094 * to start of every line.
2096 * @param aQuotedText String to insert. This will be quoted by ">"
2099 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual nsresult
2100 InsertWithQuotationsAsSubAction(const nsAString
& aQuotedText
) = 0;
2103 * PrepareInsertContent() is a helper method of InsertTextAt(),
2104 * HTMLEditor::HTMLWithContextInserter::Run(). They insert content coming
2105 * from clipboard or drag and drop. Before that, they may need to remove
2106 * selected contents and adjust selection. This does them instead.
2108 * @param aPointToInsert Point to insert. Must be set. Callers
2109 * shouldn't use this instance after calling this
2110 * method because this method may cause changing
2111 * the DOM tree and Selection.
2112 * @param aDoDeleteSelection true if selected content should be removed.
2114 MOZ_CAN_RUN_SCRIPT nsresult
PrepareToInsertContent(
2115 const EditorDOMPoint
& aPointToInsert
, bool aDoDeleteSelection
);
2118 * InsertTextAt() inserts aStringToInsert at aPointToInsert.
2120 * @param aStringToInsert The string which you want to insert.
2121 * @param aPointToInsert The insertion point.
2122 * @param aDoDeleteSelection true if you want this to delete selected
2123 * content. Otherwise, false.
2125 MOZ_CAN_RUN_SCRIPT nsresult
InsertTextAt(const nsAString
& aStringToInsert
,
2126 const EditorDOMPoint
& aPointToInsert
,
2127 bool aDoDeleteSelection
);
2130 * Return true if the data is safe to insert as the source and destination
2131 * principals match, or we are in a editor context where this doesn't matter.
2132 * Otherwise, the data must be sanitized first.
2134 bool IsSafeToInsertData(nsIPrincipal
* aSourcePrincipal
) const;
2136 protected: // Called by helper classes.
2138 * OnStartToHandleTopLevelEditSubAction() is called when
2139 * GetTopLevelEditSubAction() is EditSubAction::eNone and somebody starts to
2140 * handle aEditSubAction.
2142 * @param aTopLevelEditSubAction Top level edit sub action which
2143 * will be handled soon.
2144 * @param aDirectionOfTopLevelEditSubAction Direction of aEditSubAction.
2146 MOZ_CAN_RUN_SCRIPT
virtual void OnStartToHandleTopLevelEditSubAction(
2147 EditSubAction aTopLevelEditSubAction
,
2148 nsIEditor::EDirection aDirectionOfTopLevelEditSubAction
,
2152 * OnEndHandlingTopLevelEditSubAction() is called after
2153 * SetTopLevelEditSubAction() is handled.
2155 MOZ_CAN_RUN_SCRIPT
virtual nsresult
OnEndHandlingTopLevelEditSubAction();
2158 * OnStartToHandleEditSubAction() and OnEndHandlingEditSubAction() are called
2159 * when starting to handle an edit sub action and ending handling an edit
2162 void OnStartToHandleEditSubAction() { EditSubActionDataRef().Clear(); }
2163 void OnEndHandlingEditSubAction() { EditSubActionDataRef().Clear(); }
2166 * (Begin|End)PlaceholderTransaction() are called by AutoPlaceholderBatch.
2167 * This set of methods are similar to the (Begin|End)Transaction(), but do
2168 * not use the transaction managers batching feature. Instead we use a
2169 * placeholder transaction to wrap up any further transaction while the
2170 * batch is open. The advantage of this is that placeholder transactions
2171 * can later merge, if needed. Merging is unavailable between transaction
2174 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void BeginPlaceholderTransaction(
2175 nsStaticAtom
& aTransactionName
, const char* aRequesterFuncName
);
2176 enum class ScrollSelectionIntoView
{ No
, Yes
};
2177 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void EndPlaceholderTransaction(
2178 ScrollSelectionIntoView aScrollSelectionIntoView
,
2179 const char* aRequesterFuncName
);
2181 void BeginUpdateViewBatch(const char* aRequesterFuncName
);
2182 MOZ_CAN_RUN_SCRIPT
void EndUpdateViewBatch(const char* aRequesterFuncName
);
2185 * Used by HTMLEditor::AutoTransactionBatch, nsIEditor::BeginTransaction
2186 * and nsIEditor::EndTransation. After calling BeginTransactionInternal(),
2187 * all transactions will be treated as an atomic transaction. I.e., two or
2188 * more transactions are undid once.
2189 * XXX What's the difference with PlaceholderTransaction? Should we always
2192 MOZ_CAN_RUN_SCRIPT
void BeginTransactionInternal(
2193 const char* aRequesterFuncName
);
2194 MOZ_CAN_RUN_SCRIPT
void EndTransactionInternal(
2195 const char* aRequesterFuncName
);
2197 protected: // Shouldn't be used by friend classes
2199 * The default destructor. This should suffice. Should this be pure virtual
2200 * for someone to derive from the EditorBase later? I don't believe so.
2202 virtual ~EditorBase();
2205 * @param aDocument The dom document interface being observed
2206 * @param aRootElement
2207 * This is the root of the editable section of this
2208 * document. If it is null then we get root from document
2210 * @param aSelectionController
2211 * The selection controller of selections which will be
2212 * used in this editor.
2213 * @param aFlags Some of nsIEditor::eEditor*Mask flags.
2215 MOZ_CAN_RUN_SCRIPT nsresult
2216 InitInternal(Document
& aDocument
, Element
* aRootElement
,
2217 nsISelectionController
& aSelectionController
, uint32_t aFlags
);
2220 * PostCreateInternal() should be called after InitInternal(), and is the time
2221 * that the editor tells its documentStateObservers that the document has been
2224 MOZ_CAN_RUN_SCRIPT nsresult
PostCreateInternal();
2227 * PreDestroyInternal() is called before the editor goes away, and gives the
2228 * editor a chance to tell its documentStateObservers that the document is
2231 MOZ_CAN_RUN_SCRIPT
virtual void PreDestroyInternal();
2233 MOZ_ALWAYS_INLINE EditorType
GetEditorType() const {
2234 return mIsHTMLEditorClass
? EditorType::HTML
: EditorType::Text
;
2237 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
EnsureEmptyTextFirstChild();
2240 * InitEditorContentAndSelection() may insert a padding `<br>` element for
2241 * if it's required in the anonymous `<div>` element or `<body>` element and
2242 * collapse selection at the end if there is no selection ranges.
2244 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
InitEditorContentAndSelection();
2246 int32_t WrapWidth() const { return mWrapColumn
; }
2249 * ToGenericNSResult() computes proper nsresult value for the editor users.
2250 * This should be used only when public methods return result of internal
2253 static inline nsresult
ToGenericNSResult(nsresult aRv
) {
2255 // If the editor is destroyed while handling an edit action, editor needs
2256 // to stop handling it. However, editor throw exception in this case
2257 // because Chrome does not throw exception even in this case.
2258 case NS_ERROR_EDITOR_DESTROYED
:
2260 // If editor meets unexpected DOM tree due to modified by mutation event
2261 // listener, editor needs to stop handling it. However, editor shouldn't
2262 // return error for the users because Chrome does not throw exception in
2264 case NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE
:
2266 // If the editing action is canceled by event listeners, editor needs
2267 // to stop handling it. However, editor shouldn't return error for
2268 // the callers but they should be able to distinguish whether it's
2269 // canceled or not. Although it's DOM specific code, let's return
2270 // DOM_SUCCESS_DOM_NO_OPERATION here.
2271 case NS_ERROR_EDITOR_ACTION_CANCELED
:
2272 return NS_SUCCESS_DOM_NO_OPERATION
;
2273 // If there is no selection range or editable selection ranges, editor
2274 // needs to stop handling it. However, editor shouldn't return error for
2275 // the callers to avoid throwing exception. However, they may want to
2276 // check whether it works or not. Therefore, we should return
2277 // NS_SUCCESS_DOM_NO_OPERATION instead.
2278 case NS_ERROR_EDITOR_NO_EDITABLE_RANGE
:
2279 return NS_SUCCESS_DOM_NO_OPERATION
;
2280 // If CreateNodeResultBase::SuggestCaretPointTo etc is called with
2281 // SuggestCaret::AndIgnoreTrivialErrors and CollapseSelectionTo returns
2282 // non-critical error e.g., not NS_ERROR_EDITOR_DESTROYED, it returns
2283 // this success code instead of actual error code for making the caller
2284 // handle the case easier. Therefore, this should be mapped to NS_OK
2285 // for the users of editor.
2286 case NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR
:
2294 * GetDocumentCharsetInternal() returns charset of the document.
2296 nsresult
GetDocumentCharsetInternal(nsACString
& aCharset
) const;
2299 * ComputeValueInternal() computes string value of this editor for given
2300 * format. This may be too expensive if it's in hot path.
2302 * @param aFormatType MIME type like "text/plain".
2303 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder.
2304 * @param aCharset Encoding of the document.
2306 nsresult
ComputeValueInternal(const nsAString
& aFormatType
,
2307 uint32_t aDocumentEncoderFlags
,
2308 nsAString
& aOutputString
) const;
2311 * GetAndInitDocEncoder() returns a document encoder instance for aFormatType
2312 * after initializing it. The result may be cached for saving recreation
2315 * @param aFormatType MIME type like "text/plain".
2316 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder.
2317 * @param aCharset Encoding of the document.
2319 already_AddRefed
<nsIDocumentEncoder
> GetAndInitDocEncoder(
2320 const nsAString
& aFormatType
, uint32_t aDocumentEncoderFlags
,
2321 const nsACString
& aCharset
) const;
2324 * EnsurePaddingBRElementInMultilineEditor() creates a padding `<br>` element
2325 * at end of multiline text editor.
2327 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2328 EnsurePaddingBRElementInMultilineEditor();
2331 * SelectAllInternal() should be used instead of SelectAll() in editor
2332 * because SelectAll() creates AutoEditActionSetter but we should avoid
2333 * to create it as far as possible.
2335 MOZ_CAN_RUN_SCRIPT
virtual nsresult
SelectAllInternal();
2337 nsresult
DetermineCurrentDirection();
2340 * DispatchInputEvent() dispatches an "input" event synchronously or
2341 * asynchronously if it's not safe to dispatch.
2343 MOZ_CAN_RUN_SCRIPT
void DispatchInputEvent();
2346 * Called after a transaction is done successfully.
2348 MOZ_CAN_RUN_SCRIPT
void DoAfterDoTransaction(nsITransaction
* aTransaction
);
2351 * Called after a transaction is undone successfully.
2354 MOZ_CAN_RUN_SCRIPT
void DoAfterUndoTransaction();
2357 * Called after a transaction is redone successfully.
2359 MOZ_CAN_RUN_SCRIPT
void DoAfterRedoTransaction();
2362 * Tell the doc state listeners that the doc state has changed.
2364 enum TDocumentListenerNotification
{
2366 eDocumentToBeDestroyed
,
2367 eDocumentStateChanged
2369 MOZ_CAN_RUN_SCRIPT nsresult
2370 NotifyDocumentListeners(TDocumentListenerNotification aNotificationType
);
2373 * Make the given selection span the entire document.
2375 MOZ_CAN_RUN_SCRIPT
virtual nsresult
SelectEntireDocument() = 0;
2378 * Helper method for scrolling the selection into view after
2379 * an edit operation.
2381 * Editor methods *should* call this method instead of the versions
2382 * in the various selection interfaces, since this makes sure that
2383 * the editor's sync/async settings for reflowing, painting, and scrolling
2386 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2387 ScrollSelectionFocusIntoView() const;
2389 virtual nsresult
InstallEventListeners();
2390 virtual void CreateEventListeners();
2391 virtual void RemoveEventListeners();
2394 * Called if and only if this editor is in readonly mode.
2396 void HandleKeyPressEventInReadOnlyMode(
2397 WidgetKeyboardEvent
& aKeyboardEvent
) const;
2400 * Get the input event target. This might return null.
2402 virtual already_AddRefed
<Element
> GetInputEventTargetElement() const = 0;
2405 * Return true if spellchecking should be enabled for this editor.
2407 [[nodiscard
]] bool GetDesiredSpellCheckState();
2409 [[nodiscard
]] bool CanEnableSpellCheck() const {
2410 // Check for password/readonly/disabled, which are not spellchecked
2411 // regardless of DOM. Also, check to see if spell check should be skipped
2413 return !IsPasswordEditor() && !IsReadonly() && !ShouldSkipSpellCheck();
2417 * InitializeSelectionAncestorLimit() is called by InitializeSelection().
2418 * When this is called, each implementation has to call
2419 * Selection::SetAncestorLimiter() with aAnotherLimit.
2421 * @param aAncestorLimit New ancestor limit of Selection. This always
2422 * has parent node. So, it's always safe to
2423 * call SetAncestorLimit() with this node.
2425 virtual void InitializeSelectionAncestorLimit(
2426 nsIContent
& aAncestorLimit
) const;
2429 * Initializes selection and caret for the editor. If aEventTarget isn't
2430 * a host of the editor, i.e., the editor doesn't get focus, this does
2433 MOZ_CAN_RUN_SCRIPT nsresult
InitializeSelection(nsINode
& aFocusEventTarget
);
2435 enum NotificationForEditorObservers
{
2436 eNotifyEditorObserversOfEnd
,
2437 eNotifyEditorObserversOfBefore
,
2438 eNotifyEditorObserversOfCancel
2440 MOZ_CAN_RUN_SCRIPT
void NotifyEditorObservers(
2441 NotificationForEditorObservers aNotification
);
2444 * HowToHandleCollapsedRange indicates how collapsed range should be treated.
2446 enum class HowToHandleCollapsedRange
{
2447 // Ignore collapsed range.
2449 // Extend collapsed range for removing previous content.
2451 // Extend collapsed range for removing next content.
2455 static HowToHandleCollapsedRange
HowToHandleCollapsedRangeFor(
2456 nsIEditor::EDirection aDirectionAndAmount
) {
2457 switch (aDirectionAndAmount
) {
2458 case nsIEditor::eNone
:
2459 return HowToHandleCollapsedRange::Ignore
;
2460 case nsIEditor::ePrevious
:
2461 return HowToHandleCollapsedRange::ExtendBackward
;
2462 case nsIEditor::eNext
:
2463 return HowToHandleCollapsedRange::ExtendForward
;
2464 case nsIEditor::ePreviousWord
:
2465 case nsIEditor::eNextWord
:
2466 case nsIEditor::eToBeginningOfLine
:
2467 case nsIEditor::eToEndOfLine
:
2468 // If the amount is word or
2469 // line,`AutoRangeArray::ExtendAnchorFocusRangeFor()` must have already
2470 // been extended collapsed ranges before.
2471 return HowToHandleCollapsedRange::Ignore
;
2473 MOZ_ASSERT_UNREACHABLE("Invalid nsIEditor::EDirection value");
2474 return HowToHandleCollapsedRange::Ignore
;
2478 * InsertDroppedDataTransferAsAction() inserts all data items in aDataTransfer
2479 * at aDroppedAt unless the editor is destroyed.
2481 * @param aEditActionData The edit action data whose edit action must be
2482 * EditAction::eDrop.
2483 * @param aDataTransfer The data transfer object which is dropped.
2484 * @param aDroppedAt The DOM tree position whether aDataTransfer
2486 * @param aSourcePrincipal Principal of the source of the drag.
2487 * May be nullptr if it comes from another app
2490 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT
virtual nsresult
2491 InsertDroppedDataTransferAsAction(AutoEditActionDataSetter
& aEditActionData
,
2492 dom::DataTransfer
& aDataTransfer
,
2493 const EditorDOMPoint
& aDroppedAt
,
2494 nsIPrincipal
* aSourcePrincipal
) = 0;
2497 * DeleteSelectionByDragAsAction() removes selection and dispatch "input"
2498 * event whose inputType is "deleteByDrag".
2500 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2501 DeleteSelectionByDragAsAction(bool aDispatchInputEvent
);
2504 * DeleteSelectionWithTransaction() removes selected content or content
2505 * around caret with transactions and remove empty inclusive ancestor
2506 * inline elements of collapsed selection after removing the contents.
2508 * @param aDirectionAndAmount How much range should be removed.
2509 * @param aStripWrappers Whether the parent blocks should be removed
2510 * when they become empty.
2511 * Note that this must be `nsIEditor::eNoStrip`
2512 * if this is a TextEditor because anyway it'll
2515 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2516 DeleteSelectionWithTransaction(nsIEditor::EDirection aDirectionAndAmount
,
2517 nsIEditor::EStripWrappers aStripWrappers
);
2520 * DeleteRangesWithTransaction() removes content in aRangesToDelete or content
2521 * around collapsed ranges in aRangesToDelete with transactions and remove
2522 * empty inclusive ancestor inline elements of collapsed ranges after
2523 * removing the contents.
2525 * @param aDirectionAndAmount How much range should be removed.
2526 * @param aStripWrappers Whether the parent blocks should be removed
2527 * when they become empty.
2528 * Note that this must be `nsIEditor::eNoStrip`
2529 * if this is a TextEditor because anyway it'll
2531 * @param aRangesToDelete The ranges to delete content.
2533 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
2534 DeleteRangesWithTransaction(nsIEditor::EDirection aDirectionAndAmount
,
2535 nsIEditor::EStripWrappers aStripWrappers
,
2536 const AutoRangeArray
& aRangesToDelete
);
2539 * Create an aggregate transaction for delete the content in aRangesToDelete.
2540 * The result may include DeleteNodeTransactions and/or DeleteTextTransactions
2543 * @param aHowToHandleCollapsedRange
2544 * How to handle collapsed ranges.
2545 * @param aRangesToDelete The ranges to delete content.
2546 * @return If it can remove the content in ranges, returns
2547 * an aggregate transaction which has some
2548 * DeleteNodeTransactions and/or
2549 * DeleteTextTransactions as its children.
2551 already_AddRefed
<EditAggregateTransaction
>
2552 CreateTransactionForDeleteSelection(
2553 HowToHandleCollapsedRange aHowToHandleCollapsedRange
,
2554 const AutoRangeArray
& aRangesToDelete
);
2557 * Create a transaction for removing the nodes and/or text around
2560 * @param aCollapsedRange The range to be removed. This must be
2562 * @param aHowToHandleCollapsedRange
2563 * How to handle aCollapsedRange. Must
2564 * be HowToHandleCollapsedRange::ExtendBackward or
2565 * HowToHandleCollapsedRange::ExtendForward.
2566 * @return The transaction to remove content around the
2567 * range. Its type is DeleteNodeTransaction or
2568 * DeleteTextTransaction.
2570 already_AddRefed
<EditTransactionBase
> CreateTransactionForCollapsedRange(
2571 const nsRange
& aCollapsedRange
,
2572 HowToHandleCollapsedRange aHowToHandleCollapsedRange
);
2575 * ComputeInsertedRange() returns actual range modified by inserting string
2576 * in a text node. If mutation event listener changed the text data, this
2577 * returns a range which covers all over the text data.
2579 std::tuple
<EditorDOMPointInText
, EditorDOMPointInText
> ComputeInsertedRange(
2580 const EditorDOMPointInText
& aInsertedPoint
,
2581 const nsAString
& aInsertedString
) const;
2584 * EnsureComposition() should be called by composition event handlers. This
2585 * tries to get the composition for the event and set it to mComposition.
2586 * However, this may fail because the composition may be committed before
2587 * the event comes to the editor.
2589 * @return true if there is a composition. Otherwise, for example,
2590 * a composition event handler in web contents moved focus
2591 * for committing the composition, returns false.
2593 bool EnsureComposition(WidgetCompositionEvent
& aCompositionEvent
);
2596 * See comment of IsCopyToClipboardAllowed() for the detail.
2598 virtual bool IsCopyToClipboardAllowedInternal() const {
2599 MOZ_ASSERT(IsEditActionDataAvailable());
2600 return !SelectionRef().IsCollapsed();
2604 * Helper for Is{Cut|Copy}CommandEnabled.
2605 * Look for a listener for the given command, including up the target chain.
2607 MOZ_CAN_RUN_SCRIPT
bool CheckForClipboardCommandListener(
2608 nsAtom
* aCommand
, EventMessage aEventMessage
) const;
2611 * FireClipboardEvent() may dispatch a clipboard event.
2613 * @param aEventMessage The event message which may be set to the
2614 * dispatching event.
2615 * @param aClipboardType Working with global clipboard or selection.
2616 * @param aActionTaken [optional][out] If set to non-nullptr, will be
2617 * set to true if the action for the event is
2618 * handled or prevented default.
2619 * @return false if dispatching event is canceled.
2621 bool FireClipboardEvent(EventMessage aEventMessage
, int32_t aClipboardType
,
2622 bool* aActionTaken
= nullptr);
2625 nsCOMPtr
<nsISelectionController
> mSelectionController
;
2626 RefPtr
<Document
> mDocument
;
2628 AutoEditActionDataSetter
* mEditActionData
;
2631 * SetTextDirectionTo() sets text-direction of the root element.
2632 * Should use SwitchTextDirectionTo() or ToggleTextDirection() instead.
2633 * This is a helper class of them.
2635 nsresult
SetTextDirectionTo(TextDirection aTextDirection
);
2637 protected: // helper classes which may be used by friends
2639 * Stack based helper class for batching a collection of transactions inside
2640 * a placeholder transaction. Different from AutoTransactionBatch, this
2641 * notifies editor observers of before/end edit action handling, and
2642 * dispatches "input" event if it's necessary.
2644 class MOZ_RAII AutoPlaceholderBatch final
{
2647 * @param aRequesterFuncName function name which wants to end the batch.
2648 * This won't be stored nor exposed to selection listeners etc, used only
2649 * for logging. This MUST be alive when the destructor runs.
2651 AutoPlaceholderBatch(EditorBase
& aEditorBase
,
2652 ScrollSelectionIntoView aScrollSelectionIntoView
,
2653 const char* aRequesterFuncName
)
2654 : mEditorBase(aEditorBase
),
2655 mScrollSelectionIntoView(aScrollSelectionIntoView
),
2656 mRequesterFuncName(aRequesterFuncName
) {
2657 mEditorBase
->BeginPlaceholderTransaction(*nsGkAtoms::_empty
,
2658 mRequesterFuncName
);
2661 AutoPlaceholderBatch(EditorBase
& aEditorBase
,
2662 nsStaticAtom
& aTransactionName
,
2663 ScrollSelectionIntoView aScrollSelectionIntoView
,
2664 const char* aRequesterFuncName
)
2665 : mEditorBase(aEditorBase
),
2666 mScrollSelectionIntoView(aScrollSelectionIntoView
),
2667 mRequesterFuncName(aRequesterFuncName
) {
2668 mEditorBase
->BeginPlaceholderTransaction(aTransactionName
,
2669 mRequesterFuncName
);
2672 ~AutoPlaceholderBatch() {
2673 mEditorBase
->EndPlaceholderTransaction(mScrollSelectionIntoView
,
2674 mRequesterFuncName
);
2678 const OwningNonNull
<EditorBase
> mEditorBase
;
2679 const ScrollSelectionIntoView mScrollSelectionIntoView
;
2680 const char* const mRequesterFuncName
;
2684 * AutoEditSubActionNotifier notifies editor of start to handle
2685 * top level edit sub-action and end handling top level edit sub-action.
2687 class MOZ_RAII AutoEditSubActionNotifier final
{
2689 MOZ_CAN_RUN_SCRIPT
AutoEditSubActionNotifier(
2690 EditorBase
& aEditorBase
, EditSubAction aEditSubAction
,
2691 nsIEditor::EDirection aDirection
, ErrorResult
& aRv
)
2692 : mEditorBase(aEditorBase
), mIsTopLevel(true) {
2693 // The top level edit sub action has already be set if this is nested call
2694 // XXX Looks like that this is not aware of unexpected nested edit action
2695 // handling via selectionchange event listener or mutation event
2697 if (!mEditorBase
.GetTopLevelEditSubAction()) {
2698 MOZ_KnownLive(mEditorBase
)
2699 .OnStartToHandleTopLevelEditSubAction(aEditSubAction
, aDirection
,
2702 mIsTopLevel
= false;
2704 mEditorBase
.OnStartToHandleEditSubAction();
2707 MOZ_CAN_RUN_SCRIPT
~AutoEditSubActionNotifier() {
2708 mEditorBase
.OnEndHandlingEditSubAction();
2710 MOZ_KnownLive(mEditorBase
).OnEndHandlingTopLevelEditSubAction();
2715 EditorBase
& mEditorBase
;
2720 * Stack based helper class for turning off active selection adjustment
2721 * by low level transactions
2723 class MOZ_RAII AutoTransactionsConserveSelection final
{
2725 explicit AutoTransactionsConserveSelection(EditorBase
& aEditorBase
)
2726 : mEditorBase(aEditorBase
),
2727 mAllowedTransactionsToChangeSelection(
2728 aEditorBase
.AllowsTransactionsToChangeSelection()) {
2729 mEditorBase
.MakeThisAllowTransactionsToChangeSelection(false);
2732 ~AutoTransactionsConserveSelection() {
2733 mEditorBase
.MakeThisAllowTransactionsToChangeSelection(
2734 mAllowedTransactionsToChangeSelection
);
2738 EditorBase
& mEditorBase
;
2739 bool mAllowedTransactionsToChangeSelection
;
2742 /***************************************************************************
2743 * stack based helper class for batching reflow and paint requests.
2745 class MOZ_RAII AutoUpdateViewBatch final
{
2748 * @param aRequesterFuncName function name which wants to end the batch.
2749 * This won't be stored nor exposed to selection listeners etc, used only
2750 * for logging. This MUST be alive when the destructor runs.
2752 MOZ_CAN_RUN_SCRIPT
explicit AutoUpdateViewBatch(
2753 EditorBase
& aEditorBase
, const char* aRequesterFuncName
)
2754 : mEditorBase(aEditorBase
), mRequesterFuncName(aRequesterFuncName
) {
2755 mEditorBase
.BeginUpdateViewBatch(mRequesterFuncName
);
2758 MOZ_CAN_RUN_SCRIPT
~AutoUpdateViewBatch() {
2759 MOZ_KnownLive(mEditorBase
).EndUpdateViewBatch(mRequesterFuncName
);
2763 EditorBase
& mEditorBase
;
2764 const char* const mRequesterFuncName
;
2768 enum Tristate
{ eTriUnset
, eTriFalse
, eTriTrue
};
2770 // MIME type of the doc we are editing.
2771 nsString mContentMIMEType
;
2773 RefPtr
<mozInlineSpellChecker
> mInlineSpellChecker
;
2774 // Reference to text services document for mInlineSpellChecker.
2775 RefPtr
<TextServicesDocument
> mTextServicesDocument
;
2777 RefPtr
<TransactionManager
> mTransactionManager
;
2778 // Cached root node.
2779 RefPtr
<Element
> mRootElement
;
2781 // The form field as an event receiver.
2782 nsCOMPtr
<dom::EventTarget
> mEventTarget
;
2783 RefPtr
<EditorEventListener
> mEventListener
;
2784 // Strong reference to placeholder for begin/end batch purposes.
2785 RefPtr
<PlaceholderTransaction
> mPlaceholderTransaction
;
2786 // Name of placeholder transaction.
2787 nsStaticAtom
* mPlaceholderName
;
2788 // Saved selection state for placeholder transaction batching.
2789 mozilla::Maybe
<SelectionState
> mSelState
;
2790 // IME composition this is not null between compositionstart and
2792 RefPtr
<TextComposition
> mComposition
;
2794 RefPtr
<TextInputListener
> mTextInputListener
;
2796 RefPtr
<IMEContentObserver
> mIMEContentObserver
;
2798 // These members cache last encoder and its type for the performance in
2799 // TextEditor::ComputeTextValue() which is the implementation of
2800 // `<input>.value` and `<textarea>.value`. See `GetAndInitDocEncoder()`.
2801 mutable nsCOMPtr
<nsIDocumentEncoder
> mCachedDocumentEncoder
;
2802 mutable nsString mCachedDocumentEncoderType
;
2804 // Listens to all low level actions on the doc.
2805 // Edit action listener is currently used by highlighter of the findbar and
2806 // the spellchecker. So, we should reserve only 2 items.
2807 typedef AutoTArray
<OwningNonNull
<nsIEditActionListener
>, 2>
2808 AutoActionListenerArray
;
2809 AutoActionListenerArray mActionListeners
;
2810 // Listen to overall doc state (dirty or not, just created, etc.).
2811 // Document state listener is currently used by FinderHighlighter and
2812 // BlueGriffon so that reserving only one is enough.
2813 typedef AutoTArray
<OwningNonNull
<nsIDocumentStateListener
>, 1>
2814 AutoDocumentStateListenerArray
;
2815 AutoDocumentStateListenerArray mDocStateListeners
;
2817 // Number of modifications (for undo/redo stack).
2819 // Behavior flags. See nsIEditor.idl for the flags we use.
2822 int32_t mUpdateCount
;
2824 // Nesting count for batching.
2825 int32_t mPlaceholderBatch
;
2827 int32_t mWrapColumn
;
2828 int32_t mNewlineHandling
;
2829 int32_t mCaretStyle
;
2831 // -1 = not initialized
2832 int8_t mDocDirtyState
;
2833 // A Tristate value.
2834 uint8_t mSpellcheckCheckboxState
;
2836 // If true, initialization was succeeded.
2837 bool mInitSucceeded
;
2838 // If false, transactions should not change Selection even after modifying
2840 bool mAllowsTransactionsToChangeSelection
;
2841 // Whether PreDestroy has been called.
2842 bool mDidPreDestroy
;
2843 // Whether PostCreate has been called.
2844 bool mDidPostCreate
;
2845 bool mDispatchInputEvent
;
2846 // True while the instance is handling an edit sub-action.
2847 bool mIsInEditSubAction
;
2848 // Whether caret is hidden forcibly.
2850 // Whether spellchecker dictionary is initialized after focused.
2851 bool mSpellCheckerDictionaryUpdated
;
2852 // Whether we are an HTML editor class.
2853 bool mIsHTMLEditorClass
;
2855 friend class AlignStateAtSelection
; // AutoEditActionDataSetter,
2856 // ToGenericNSResult
2857 friend class AutoRangeArray
; // IsSEditActionDataAvailable, SelectionRef
2858 friend class CompositionTransaction
; // CollapseSelectionTo, DoDeleteText,
2859 // DoInsertText, DoReplaceText,
2860 // HideCaret, RangeupdaterRef
2861 friend class DeleteNodeTransaction
; // RangeUpdaterRef
2862 friend class DeleteRangeTransaction
; // AllowsTransactionsToChangeSelection,
2863 // CollapseSelectionTo
2864 friend class DeleteTextTransaction
; // AllowsTransactionsToChangeSelection,
2865 // DoDeleteText, DoInsertText,
2867 friend class InsertNodeTransaction
; // AllowsTransactionsToChangeSelection,
2868 // CollapseSelectionTo,
2869 // MarkElementDirty, ToGenericNSResult
2870 friend class InsertTextTransaction
; // AllowsTransactionsToChangeSelection,
2871 // CollapseSelectionTo, DoDeleteText,
2872 // DoInsertText, RangeUpdaterRef
2873 friend class ListElementSelectionState
; // AutoEditActionDataSetter,
2874 // ToGenericNSResult
2875 friend class ListItemElementSelectionState
; // AutoEditActionDataSetter,
2876 // ToGenericNSResult
2877 friend class ParagraphStateAtSelection
; // AutoEditActionDataSetter,
2878 // ToGenericNSResult
2879 friend class ReplaceTextTransaction
; // AllowsTransactionsToChangeSelection,
2880 // CollapseSelectionTo, DoReplaceText,
2882 friend class SplitNodeTransaction
; // ToGenericNSResult
2883 friend class TypeInState
; // GetEditAction, GetFirstSelectionStartPoint,
2885 friend class WhiteSpaceVisibilityKeeper
; // AutoTransactionsConserveSelection
2886 friend class nsIEditor
; // mIsHTMLEditorClass
2888 template <typename NodeType
>
2889 friend class CreateNodeResultBase
; // CollapseSelectionTo
2892 } // namespace mozilla
2894 bool nsIEditor::IsTextEditor() const {
2895 return !AsEditorBase()->mIsHTMLEditorClass
;
2898 bool nsIEditor::IsHTMLEditor() const {
2899 return AsEditorBase()->mIsHTMLEditorClass
;
2902 mozilla::EditorBase
* nsIEditor::AsEditorBase() {
2903 return static_cast<mozilla::EditorBase
*>(this);
2906 const mozilla::EditorBase
* nsIEditor::AsEditorBase() const {
2907 return static_cast<const mozilla::EditorBase
*>(this);
2910 #endif // #ifndef mozilla_EditorBase_h