Bug 1769033 - Add OpenBSD sandboxing support r=gaston
[gecko.git] / editor / libeditor / EditorBase.h
blobdd11d9f7005383bd64e7248c41ff27ec45f971ec
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;
48 class nsAtom;
49 class nsCaret;
50 class nsIContent;
51 class nsIDocumentEncoder;
52 class nsIDocumentStateListener;
53 class nsIEditActionListener;
54 class nsINode;
55 class nsIPrincipal;
56 class nsISupports;
57 class nsITransferable;
58 class nsITransaction;
59 class nsITransactionListener;
60 class nsIWidget;
61 class nsRange;
63 namespace mozilla {
64 class AlignStateAtSelection;
65 class AutoTransactionsConserveSelection;
66 class AutoUpdateViewBatch;
67 class ErrorResult;
68 class IMEContentObserver;
69 class ListElementSelectionState;
70 class ListItemElementSelectionState;
71 class ParagraphStateAtSelection;
72 class PresShell;
73 class TextComposition;
74 class TextInputListener;
75 class TextServicesDocument;
76 namespace dom {
77 class AbstractRange;
78 class DataTransfer;
79 class Document;
80 class DragEvent;
81 class Element;
82 class EventTarget;
83 class HTMLBRElement;
84 } // namespace dom
86 namespace widget {
87 struct IMEState;
88 } // namespace widget
90 /**
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
95 * implementation.
97 class EditorBase : public nsIEditor,
98 public nsISelectionListener,
99 public nsSupportsWeakReference {
100 public:
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)
122 // nsIEditor methods
123 NS_DECL_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>.
155 return false;
157 #ifdef DEBUG
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.
162 return true;
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();
180 return false;
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
191 * for the detail.
193 bool MutationObserverHasObservedNodeForTelemetry() const {
194 if (const nsPIDOMWindowInner* window = GetInnerWindow()) {
195 return window->MutationObserverHasObservedNodeForTelemetry();
197 return false;
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();
218 if (!sc) {
219 return nullptr;
221 Selection* selection = sc->GetSelection(ToRawSelectionType(aSelectionType));
222 return selection;
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
232 * plaintext editors.
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
268 * be destroyed.
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
277 * called by system.
279 MOZ_CAN_RUN_SCRIPT nsresult
280 ToggleTextDirectionAsAction(nsIPrincipal* aPrincipal = nullptr);
283 * SwitchTextDirectionTo() sets the text-direction of the root element to
284 * LTR or RTL.
286 enum class TextDirection {
287 eLTR,
288 eRTL,
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()
321 : 0;
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) {
354 return true;
356 return mTransactionManager->DisableUndoRedo();
358 bool ClearUndoRedo() {
359 if (!mTransactionManager) {
360 return true;
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())) {
394 return false;
396 return IsCopyToClipboardAllowedInternal();
400 * HandleDropEvent() is called from EditorEventListener::Drop that is handler
401 * of drop event.
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) {
451 return NS_OK;
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) {
459 return NS_OK;
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) {
470 return NS_OK;
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
550 * "input" event.
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
583 * elements.
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
599 * added here.
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);
618 * Do "cut".
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
635 * handled.
636 * @param aPrincipal Set subject principal if it may be called by
637 * JS. If set to nullptr, will be treated as
638 * called by system.
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
654 * called by system.
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
666 * called by system.
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())) {
678 return false;
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
694 * called by system.
696 MOZ_CAN_RUN_SCRIPT nsresult
697 DeleteSelectionAsAction(nsIEditor::EDirection aDirectionAndAmount,
698 nsIEditor::EStripWrappers aStripWrappers,
699 nsIPrincipal* aPrincipal = nullptr);
701 enum class AllowBeforeInputEventCancelable {
703 Yes,
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
720 * called by system.
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
746 * called by system.
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
758 * called by system.
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
776 * called by system.
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
819 // cases for now:
820 // - non-collapsed range was selected.
821 // - selection was collapsed in a text node and a Unicode character
822 // was removed.
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
833 // to restore it.
834 bool mRestoreContentEditableCount;
836 // If we explicitly normalized whitespaces around the changed range,
837 // set to true.
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);
875 private:
876 void Clear() {
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) {
882 return;
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,
902 nsINode& aNode);
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;
928 private:
929 void Clear() { mAdjustChangedRangeFromListener = true; }
931 friend EditorBase;
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 {
942 eWithFormat,
943 eWithoutFormat,
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 {
953 public:
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
957 // it.
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
976 * itself.
979 [[nodiscard]] bool CanHandle() const {
980 #ifdef DEBUG
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");
988 return false;
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))) {
999 return 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
1049 * non illegal case.
1051 void MarkAsHandled() {
1052 MOZ_ASSERT(!mHandled);
1053 mHandled = true;
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
1060 * called.
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
1068 // event later.
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));
1090 return *mSelection;
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");
1115 mData = aData;
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
1125 * read-only.
1127 void InitializeDataTransfer(dom::DataTransfer* aDataTransfer);
1129 * InitializeDataTransfer(nsITransferable*) creates new DataTransfer
1130 * instance, initializes it with aTransferable and sets mDataTransfer to
1131 * it.
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;
1182 if (mParentData) {
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;
1225 break;
1226 case EditSubAction::eJoinNodes:
1227 case EditSubAction::eDeleteText:
1228 MOZ_ASSERT(aDirection == ePrevious);
1229 mDirectionOfTopLevelEditSubAction = ePrevious;
1230 break;
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;
1239 break;
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
1246 // as-is for now.
1247 mDirectionOfTopLevelEditSubAction = aDirection;
1248 break;
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);
1289 private:
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
1323 // change.
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:
1334 return false;
1335 default:
1336 return true;
1340 bool NeedsToDispatchClipboardEvent() const {
1341 if (mHasTriedToDispatchClipboardEvent) {
1342 return false;
1344 switch (mEditAction) {
1345 case EditAction::ePaste:
1346 case EditAction::ePasteAsQuotation:
1347 case EditAction::eCut:
1348 case EditAction::eCopy:
1349 return true;
1350 default:
1351 return false;
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.
1371 nsString mData;
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
1380 // by TextEditor.
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;
1407 bool mAborted;
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
1419 // event.
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
1423 // edit action.
1424 bool mEditorWasDestroyedDuringHandlingEditAction;
1425 // This is set before dispatching `input` event and notifying editor
1426 // observers.
1427 bool mHandled;
1429 #ifdef DEBUG
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
1547 * sub-action.
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()
1561 : eNone;
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.
1687 * false, otherwise.
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
1733 * before.
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
1744 * aDestElement.
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,
1809 ErrorResult& aRv);
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,
1813 uint32_t aCount,
1814 const nsAString& aStringToInsert,
1815 ErrorResult& aRv);
1816 MOZ_CAN_RUN_SCRIPT void DoSetText(dom::Text& aText,
1817 const nsAString& aStringToSet,
1818 ErrorResult& aRv);
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,
1828 uint32_t aOffset,
1829 uint32_t aLength);
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);
1889 return;
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);
1897 return;
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
1925 * of the editor.
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
1932 * change Selection.
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
1953 * editors.
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 {
1991 public:
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
2004 * information.
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;
2021 private:
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
2036 * anything.
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
2045 * edit sub-action.
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 ">"
2097 * automatically.
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,
2149 ErrorResult& aRv);
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
2160 * sub action.
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
2172 * manager batches.
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
2190 * use it instead?
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
2209 * body.
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
2222 * created.
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
2229 * going away.
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
2251 * methods.
2253 static inline nsresult ToGenericNSResult(nsresult aRv) {
2254 switch (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:
2259 return NS_OK;
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
2263 // this case.
2264 case NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE:
2265 return NS_OK;
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:
2287 return NS_OK;
2288 default:
2289 return aRv;
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
2313 * cost.
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 {
2365 eDocumentCreated,
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
2384 * match.
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
2412 // or not.
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
2431 * nothing.
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.
2448 Ignore,
2449 // Extend collapsed range for removing previous content.
2450 ExtendBackward,
2451 // Extend collapsed range for removing next content.
2452 ExtendForward,
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
2485 * is dropped.
2486 * @param aSourcePrincipal Principal of the source of the drag.
2487 * May be nullptr if it comes from another app
2488 * or process.
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
2513 * be ignored.
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
2530 * be ignored.
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
2541 * as its children.
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
2558 * aRangeToDelete.
2560 * @param aCollapsedRange The range to be removed. This must be
2561 * collapsed.
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);
2624 private:
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 {
2645 public:
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);
2677 protected:
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 {
2688 public:
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
2696 // listener.
2697 if (!mEditorBase.GetTopLevelEditSubAction()) {
2698 MOZ_KnownLive(mEditorBase)
2699 .OnStartToHandleTopLevelEditSubAction(aEditSubAction, aDirection,
2700 aRv);
2701 } else {
2702 mIsTopLevel = false;
2704 mEditorBase.OnStartToHandleEditSubAction();
2707 MOZ_CAN_RUN_SCRIPT ~AutoEditSubActionNotifier() {
2708 mEditorBase.OnEndHandlingEditSubAction();
2709 if (mIsTopLevel) {
2710 MOZ_KnownLive(mEditorBase).OnEndHandlingTopLevelEditSubAction();
2714 protected:
2715 EditorBase& mEditorBase;
2716 bool mIsTopLevel;
2720 * Stack based helper class for turning off active selection adjustment
2721 * by low level transactions
2723 class MOZ_RAII AutoTransactionsConserveSelection final {
2724 public:
2725 explicit AutoTransactionsConserveSelection(EditorBase& aEditorBase)
2726 : mEditorBase(aEditorBase),
2727 mAllowedTransactionsToChangeSelection(
2728 aEditorBase.AllowsTransactionsToChangeSelection()) {
2729 mEditorBase.MakeThisAllowTransactionsToChangeSelection(false);
2732 ~AutoTransactionsConserveSelection() {
2733 mEditorBase.MakeThisAllowTransactionsToChangeSelection(
2734 mAllowedTransactionsToChangeSelection);
2737 protected:
2738 EditorBase& mEditorBase;
2739 bool mAllowedTransactionsToChangeSelection;
2742 /***************************************************************************
2743 * stack based helper class for batching reflow and paint requests.
2745 class MOZ_RAII AutoUpdateViewBatch final {
2746 public:
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);
2762 protected:
2763 EditorBase& mEditorBase;
2764 const char* const mRequesterFuncName;
2767 protected:
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
2791 // compositionend.
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).
2818 uint32_t mModCount;
2819 // Behavior flags. See nsIEditor.idl for the flags we use.
2820 uint32_t mFlags;
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
2839 // the DOM tree.
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.
2849 bool mHidingCaret;
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,
2866 // RangeUpdaterRef
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,
2881 // RangeUpdaterRef
2882 friend class SplitNodeTransaction; // ToGenericNSResult
2883 friend class TypeInState; // GetEditAction, GetFirstSelectionStartPoint,
2884 // SelectionRef
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