/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_EditorBase_h #define mozilla_EditorBase_h #include "mozilla/intl/BidiEmbeddingLevel.h" #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc. #include "mozilla/EditAction.h" // for EditAction and EditSubAction #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint #include "mozilla/EditorForwards.h" #include "mozilla/EventForwards.h" // for InputEventTargetRanges #include "mozilla/Likely.h" // for MOZ_UNLIKELY, MOZ_LIKELY #include "mozilla/Maybe.h" // for Maybe #include "mozilla/OwningNonNull.h" // for OwningNonNull #include "mozilla/PendingStyles.h" // for PendingStyle, PendingStyleCache #include "mozilla/RangeBoundary.h" // for RawRangeBoundary, RangeBoundary #include "mozilla/SelectionState.h" // for RangeUpdater, etc. #include "mozilla/StyleSheet.h" // for StyleSheet #include "mozilla/TransactionManager.h" // for TransactionManager #include "mozilla/WeakPtr.h" // for WeakPtr #include "mozilla/dom/DataTransfer.h" // for dom::DataTransfer #include "mozilla/dom/HTMLBRElement.h" // for dom::HTMLBRElement #include "mozilla/dom/Selection.h" #include "mozilla/dom/Text.h" #include "nsAtom.h" // for nsAtom, nsStaticAtom #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr #include "nsCycleCollectionParticipant.h" #include "nsGkAtoms.h" #include "nsIClipboard.h" // for nsIClipboard::ClipboardType #include "nsIContentInlines.h" // for nsINode::IsEditable() #include "nsIEditor.h" // for nsIEditor, etc. #include "nsISelectionController.h" // for nsISelectionController constants #include "nsISelectionListener.h" // for nsISelectionListener #include "nsISupportsImpl.h" // for EditorBase::Release, etc. #include "nsIWeakReferenceUtils.h" // for nsWeakPtr #include "nsLiteralString.h" // for NS_LITERAL_STRING #include "nsPIDOMWindow.h" // for nsPIDOMWindowInner, etc. #include "nsString.h" // for nsCString #include "nsTArray.h" // for nsTArray and AutoTArray #include "nsWeakReference.h" // for nsSupportsWeakReference #include "nscore.h" // for nsresult, nsAString, etc. #include // for std::tuple class mozInlineSpellChecker; class nsAtom; class nsCaret; class nsIContent; class nsIDocumentEncoder; class nsIDocumentStateListener; class nsIEditActionListener; class nsINode; class nsIPrincipal; class nsISupports; class nsITransferable; class nsITransaction; class nsIWidget; class nsRange; namespace mozilla { class AlignStateAtSelection; class AutoTransactionsConserveSelection; class AutoUpdateViewBatch; class ErrorResult; class IMEContentObserver; class ListElementSelectionState; class ListItemElementSelectionState; class ParagraphStateAtSelection; class PresShell; class TextComposition; class TextInputListener; class TextServicesDocument; namespace dom { class AbstractRange; class DataTransfer; class Document; class DragEvent; class Element; class EventTarget; class HTMLBRElement; } // namespace dom namespace widget { struct IMEState; } // namespace widget /** * Implementation of an editor object. it will be the controller/focal point * for the main editor services. i.e. the GUIManager, publishing, transaction * manager, event interfaces. the idea for the event interfaces is to have them * delegate the actual commands to the editor independent of the XPFE * implementation. */ class EditorBase : public nsIEditor, public nsISelectionListener, public nsSupportsWeakReference { public: /**************************************************************************** * NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other * classes under libeditor except EditorEventListener and * HTMLEditorEventListener because each public method which may fire * eEditorInput event will need to instantiate new stack class for * managing input type value of eEditorInput and cache some objects * for smarter handling. In other words, when you add new root * method to edit the DOM tree, you can make your new method public. ****************************************************************************/ using DataTransfer = dom::DataTransfer; using Document = dom::Document; using Element = dom::Element; using InterlinePosition = dom::Selection::InterlinePosition; using Selection = dom::Selection; using Text = dom::Text; enum class EditorType { Text, HTML }; NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor) // nsIEditor methods NS_DECL_NSIEDITOR // nsISelectionListener method NS_DECL_NSISELECTIONLISTENER /** * The default constructor. This should suffice. the setting of the * interfaces is done after the construction of the editor class. */ explicit EditorBase(EditorType aEditorType); [[nodiscard]] bool IsInitialized() const { return mDocument && mDidPostCreate; } [[nodiscard]] bool IsBeingInitialized() const { return mDocument && !mDidPostCreate; } [[nodiscard]] bool Destroyed() const { return mDidPreDestroy; } Document* GetDocument() const { return mDocument; } nsPIDOMWindowOuter* GetWindow() const; nsPIDOMWindowInner* GetInnerWindow() const; /** * MaybeNodeRemovalsObservedByDevTools() returns true when the mutations in * the document is observed by DevTools. * * @return true if the editor is an HTMLEditor instance * and the mutations in the document is observed by * DevTools. */ [[nodiscard]] bool MaybeNodeRemovalsObservedByDevTools() const; /** * MayHaveBeforeInputEventListenersForTelemetry() returns true when the * window may have or have had one or more `beforeinput` event listeners. * Note that this may return false even if there is a `beforeinput`. * See nsPIDOMWindowInner::HasBeforeInputEventListenersForTelemetry()'s * comment for the detail. */ bool MayHaveBeforeInputEventListenersForTelemetry() const { if (const nsPIDOMWindowInner* window = GetInnerWindow()) { return window->HasBeforeInputEventListenersForTelemetry(); } return false; } /** * MutationObserverHasObservedNodeForTelemetry() returns true when a node in * the window may have been observed by the web apps with a mutation observer * (i.e., `MutationObserver.observe()` called by chrome script and addon's * script does not make this returns true). * Note that this may return false even if there is a node observed by * a MutationObserver. See * nsPIDOMWindowInner::MutationObserverHasObservedNodeForTelemetry()'s comment * for the detail. */ bool MutationObserverHasObservedNodeForTelemetry() const { if (const nsPIDOMWindowInner* window = GetInnerWindow()) { return window->MutationObserverHasObservedNodeForTelemetry(); } return false; } /** * This checks whether the call with aPrincipal should or should not be * treated as user input. */ [[nodiscard]] static bool TreatAsUserInput(nsIPrincipal* aPrincipal); PresShell* GetPresShell() const; nsPresContext* GetPresContext() const; already_AddRefed GetCaret() const; already_AddRefed GetWidget() const; nsISelectionController* GetSelectionController() const; nsresult GetSelection(SelectionType aSelectionType, Selection** aSelection) const; Selection* GetSelection( SelectionType aSelectionType = SelectionType::eNormal) const { if (aSelectionType == SelectionType::eNormal && IsEditActionDataAvailable()) { return &SelectionRef(); } nsISelectionController* sc = GetSelectionController(); if (!sc) { return nullptr; } Selection* selection = sc->GetSelection(ToRawSelectionType(aSelectionType)); return selection; } /** * @return Ancestor limiter of normal selection */ [[nodiscard]] nsIContent* GetSelectionAncestorLimiter() const { Selection* selection = GetSelection(SelectionType::eNormal); return selection ? selection->GetAncestorLimiter() : nullptr; } /** * Create a DataTransfer object that can be shared between the paste event * and pasting into a DOM element. */ already_AddRefed CreateDataTransferForPaste( EventMessage aEventMessage, nsIClipboard::ClipboardType aClipboardType) const; /** * Fast non-refcounting editor root element accessor */ Element* GetRoot() const { return mRootElement; } /** * Likewise, but gets the text control element instead of the root for * plaintext editors. */ Element* GetExposedRoot() const; /** * Set or unset TextInputListener. If setting non-nullptr when the editor * already has a TextInputListener, this will crash in debug build. */ void SetTextInputListener(TextInputListener* aTextInputListener); /** * Set or unset IMEContentObserver. If setting non-nullptr when the editor * already has an IMEContentObserver, this will crash in debug build. */ void SetIMEContentObserver(IMEContentObserver* aIMEContentObserver); /** * Returns current composition. */ TextComposition* GetComposition() const; /** * Get preferred IME status of current widget. */ [[nodiscard]] virtual Result GetPreferredIMEState() const = 0; /** * Returns true if there is composition string and not fixed. */ bool IsIMEComposing() const; /** * Commit composition if there is. * Note that when there is a composition, this requests to commit composition * to native IME. Therefore, when there is composition, this can do anything. * For example, the editor instance, the widget or the process itself may * be destroyed. */ nsresult CommitComposition(); /** * ToggleTextDirection() toggles text-direction of the root element. * * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ MOZ_CAN_RUN_SCRIPT nsresult ToggleTextDirectionAsAction(nsIPrincipal* aPrincipal = nullptr); /** * SwitchTextDirectionTo() sets the text-direction of the root element to * LTR or RTL. */ enum class TextDirection { eLTR, eRTL, }; MOZ_CAN_RUN_SCRIPT void SwitchTextDirectionTo(TextDirection aTextDirection); /** * Finalizes selection and caret for the editor. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult FinalizeSelection(); /** * Returns true if selection is in an editable element and both the range * start and the range end are editable. E.g., even if the selection range * includes non-editable elements, returns true when one of common ancestors * of the range start and the range end is editable. Otherwise, false. */ bool IsSelectionEditable(); /** * Returns number of undo or redo items. */ size_t NumberOfUndoItems() const { return mTransactionManager ? mTransactionManager->NumberOfUndoItems() : 0; } size_t NumberOfRedoItems() const { return mTransactionManager ? mTransactionManager->NumberOfRedoItems() : 0; } /** * Returns number of maximum undo/redo transactions. */ int32_t NumberOfMaximumTransactions() const { return mTransactionManager ? mTransactionManager->NumberOfMaximumTransactions() : 0; } /** * Returns true if this editor can store transactions for undo/redo. */ bool IsUndoRedoEnabled() const { return mTransactionManager && mTransactionManager->NumberOfMaximumTransactions(); } /** * Return true if it's possible to undo/redo right now. */ bool CanUndo() const { return IsUndoRedoEnabled() && NumberOfUndoItems() > 0; } bool CanRedo() const { return IsUndoRedoEnabled() && NumberOfRedoItems() > 0; } /** * Enables or disables undo/redo feature. Returns true if it succeeded, * otherwise, e.g., we're undoing or redoing, returns false. */ bool EnableUndoRedo(int32_t aMaxTransactionCount = -1) { if (!mTransactionManager) { mTransactionManager = new TransactionManager(); } return mTransactionManager->EnableUndoRedo(aMaxTransactionCount); } bool DisableUndoRedo() { if (!mTransactionManager) { return true; } return mTransactionManager->DisableUndoRedo(); } bool ClearUndoRedo() { if (!mTransactionManager) { return true; } return mTransactionManager->ClearUndoRedo(); } /** * See Document::AreClipboardCommandsUnconditionallyEnabled. */ bool AreClipboardCommandsUnconditionallyEnabled() const; /** * IsCutCommandEnabled() returns whether cut command can be enabled or * disabled. This always returns true if we're in non-chrome HTML/XHTML * document. Otherwise, same as the result of `IsCopyToClipboardAllowed()`. */ MOZ_CAN_RUN_SCRIPT bool IsCutCommandEnabled() const; /** * IsCopyCommandEnabled() returns copy command can be enabled or disabled. * This always returns true if we're in non-chrome HTML/XHTML document. * Otherwise, same as the result of `IsCopyToClipboardAllowed()`. */ MOZ_CAN_RUN_SCRIPT bool IsCopyCommandEnabled() const; /** * IsCopyToClipboardAllowed() returns true if the selected content can * be copied into the clipboard. This returns true when: * - `Selection` is not collapsed and we're not a password editor. * - `Selection` is not collapsed and we're a password editor but selection * range is in unmasked range. */ bool IsCopyToClipboardAllowed() const { AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); if (NS_WARN_IF(!editActionData.CanHandle())) { return false; } return IsCopyToClipboardAllowedInternal(); } /** * Called before starting to handle eMouseDown or eMouseUp in PresShell. * * @return true if IME consumed aMouseEvent. */ MOZ_CAN_RUN_SCRIPT bool WillHandleMouseButtonEvent( WidgetMouseEvent& aMouseEvent); /** * HandleDropEvent() is called from EditorEventListener::Drop that is handler * of drop event. */ MOZ_CAN_RUN_SCRIPT nsresult HandleDropEvent(dom::DragEvent* aDropEvent); MOZ_CAN_RUN_SCRIPT virtual nsresult HandleKeyPressEvent( WidgetKeyboardEvent* aKeyboardEvent); virtual dom::EventTarget* GetDOMEventTarget() const = 0; /** * OnCompositionStart() is called when editor receives eCompositionStart * event which should be handled in this editor. */ nsresult OnCompositionStart(WidgetCompositionEvent& aCompositionStartEvent); /** * OnCompositionChange() is called when editor receives an eCompositioChange * event which should be handled in this editor. * * @param aCompositionChangeEvent eCompositionChange event which should * be handled in this editor. */ MOZ_CAN_RUN_SCRIPT nsresult OnCompositionChange(WidgetCompositionEvent& aCompositionChangeEvent); /** * OnCompositionEnd() is called when editor receives an eCompositionChange * event and it's followed by eCompositionEnd event and after * OnCompositionChange() is called. */ MOZ_CAN_RUN_SCRIPT void OnCompositionEnd( WidgetCompositionEvent& aCompositionEndEvent); /** * Accessor methods to flags. */ uint32_t Flags() const { return mFlags; } MOZ_CAN_RUN_SCRIPT nsresult AddFlags(uint32_t aFlags) { const uint32_t kOldFlags = Flags(); const uint32_t kNewFlags = (kOldFlags | aFlags); if (kNewFlags == kOldFlags) { return NS_OK; } return SetFlags(kNewFlags); // virtual call and may be expensive. } MOZ_CAN_RUN_SCRIPT nsresult RemoveFlags(uint32_t aFlags) { const uint32_t kOldFlags = Flags(); const uint32_t kNewFlags = (kOldFlags & ~aFlags); if (kNewFlags == kOldFlags) { return NS_OK; } return SetFlags(kNewFlags); // virtual call and may be expensive. } MOZ_CAN_RUN_SCRIPT nsresult AddAndRemoveFlags(uint32_t aAddingFlags, uint32_t aRemovingFlags) { MOZ_ASSERT(!(aAddingFlags & aRemovingFlags), "Same flags are specified both adding and removing"); const uint32_t kOldFlags = Flags(); const uint32_t kNewFlags = ((kOldFlags | aAddingFlags) & ~aRemovingFlags); if (kNewFlags == kOldFlags) { return NS_OK; } return SetFlags(kNewFlags); // virtual call and may be expensive. } bool IsSingleLineEditor() const { const bool isSingleLineEditor = (mFlags & nsIEditor::eEditorSingleLineMask) != 0; MOZ_ASSERT_IF(isSingleLineEditor, IsTextEditor()); return isSingleLineEditor; } bool IsPasswordEditor() const { const bool isPasswordEditor = (mFlags & nsIEditor::eEditorPasswordMask) != 0; MOZ_ASSERT_IF(isPasswordEditor, IsTextEditor()); return isPasswordEditor; } // FYI: Both IsRightToLeft() and IsLeftToRight() may return false if // the editor inherits the content node's direction. bool IsRightToLeft() const { return (mFlags & nsIEditor::eEditorRightToLeft) != 0; } bool IsLeftToRight() const { return (mFlags & nsIEditor::eEditorLeftToRight) != 0; } bool IsReadonly() const { return (mFlags & nsIEditor::eEditorReadonlyMask) != 0; } bool IsMailEditor() const { return (mFlags & nsIEditor::eEditorMailMask) != 0; } bool IsInteractionAllowed() const { const bool isInteractionAllowed = (mFlags & nsIEditor::eEditorAllowInteraction) != 0; MOZ_ASSERT_IF(isInteractionAllowed, IsHTMLEditor()); return isInteractionAllowed; } bool ShouldSkipSpellCheck() const { return (mFlags & nsIEditor::eEditorSkipSpellCheck) != 0; } bool HasIndependentSelection() const { MOZ_ASSERT_IF(mSelectionController, IsTextEditor()); return !!mSelectionController; } bool IsModifiable() const { return !IsReadonly(); } /** * IsInEditSubAction() return true while the instance is handling an edit * sub-action. Otherwise, false. */ bool IsInEditSubAction() const { return mIsInEditSubAction; } /** * IsEmpty() checks whether the editor is empty. If editor has only padding *
element for empty editor, returns true. If editor's root element has * non-empty text nodes or other nodes like
, returns false. */ virtual bool IsEmpty() const = 0; /** * SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching * "input" event. */ void SuppressDispatchingInputEvent(bool aSuppress) { mDispatchInputEvent = !aSuppress; } /** * IsSuppressingDispatchingInputEvent() returns true if the editor stops * dispatching input event. Otherwise, false. */ bool IsSuppressingDispatchingInputEvent() const { return !mDispatchInputEvent; } /** * Returns true if markNodeDirty() has any effect. Returns false if * markNodeDirty() is a no-op. */ bool OutputsMozDirty() const { // Return true for Composer (!IsInteractionAllowed()) or mail // (IsMailEditor()), but false for webpages. return !IsInteractionAllowed() || IsMailEditor(); } /** * Get the focused element, if we're focused. Returns null otherwise. */ virtual Element* GetFocusedElement() const; /** * Whether the aGUIEvent should be handled by this editor or not. When this * returns false, The aGUIEvent shouldn't be handled on this editor, * i.e., The aGUIEvent should be handled by another inner editor or ancestor * elements. */ virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const; /** * FindSelectionRoot() returns a selection root of this editor when aNode * gets focus. aNode must be a content node or a document node. When the * target isn't a part of this editor, returns nullptr. If this is for * designMode, you should set the document node to aNode except that an * element in the document has focus. */ [[nodiscard]] virtual Element* FindSelectionRoot(const nsINode& aNode) const; /** * OnFocus() is called when we get a focus event. * * @param aOriginalEventTargetNode The original event target node of the * focus event. */ MOZ_CAN_RUN_SCRIPT virtual nsresult OnFocus( const nsINode& aOriginalEventTargetNode); /** * OnBlur() is called when we're blurred. * * @param aEventTarget The event target of the blur event. */ virtual nsresult OnBlur(const dom::EventTarget* aEventTarget) = 0; /** Resyncs spellchecking state (enabled/disabled). This should be called * when anything that affects spellchecking state changes, such as the * spellcheck attribute value. */ void SyncRealTimeSpell(); /** * Do "cut". * * @param aPrincipal If you know current context is subject * principal or system principal, set it. * When nullptr, this checks it automatically. */ MOZ_CAN_RUN_SCRIPT nsresult CutAsAction(nsIPrincipal* aPrincipal = nullptr); /** * CanPaste() returns true if user can paste something at current selection. */ virtual bool CanPaste(nsIClipboard::ClipboardType aClipboardType) const = 0; /** * Do "undo" or "redo". * * @param aCount How many count of transactions should be * handled. * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ MOZ_CAN_RUN_SCRIPT nsresult UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal = nullptr); MOZ_CAN_RUN_SCRIPT nsresult RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal = nullptr); /** * InsertTextAsAction() inserts aStringToInsert at selection. * Although this method is implementation of nsIEditor.insertText(), * this treats the input is an edit action. If you'd like to insert text * as part of edit action, you probably should use InsertTextAsSubAction(). * * @param aStringToInsert The string to insert. * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ MOZ_CAN_RUN_SCRIPT nsresult InsertTextAsAction( const nsAString& aStringToInsert, nsIPrincipal* aPrincipal = nullptr); /** * InsertLineBreakAsAction() is called when user inputs a line break with * Enter or something. If the instance is `HTMLEditor`, this is called * when Shift + Enter or "insertlinebreak" command. * * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ MOZ_CAN_RUN_SCRIPT virtual nsresult InsertLineBreakAsAction( nsIPrincipal* aPrincipal = nullptr) = 0; /** * CanDeleteSelection() returns true if `Selection` is not collapsed and * it's allowed to be removed. */ bool CanDeleteSelection() const { AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); if (NS_WARN_IF(!editActionData.CanHandle())) { return false; } return IsModifiable() && !SelectionRef().IsCollapsed(); } /** * DeleteSelectionAsAction() removes selection content or content around * caret with transactions. This should be used for handling it as an * edit action. If you'd like to remove selection for preparing to insert * something, you probably should use DeleteSelectionAsSubAction(). * * @param aDirectionAndAmount How much range should be removed. * @param aStripWrappers Whether the parent blocks should be removed * when they become empty. * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ MOZ_CAN_RUN_SCRIPT nsresult DeleteSelectionAsAction(nsIEditor::EDirection aDirectionAndAmount, nsIEditor::EStripWrappers aStripWrappers, nsIPrincipal* aPrincipal = nullptr); enum class AllowBeforeInputEventCancelable { No, Yes, }; enum class PreventSetSelection { No, Yes, }; /** * Replace text in aReplaceRange or all text in this editor with aString and * treat the change as inserting the string. * * @param aString The string to set. * @param aReplaceRange The range to be replaced. * If nullptr, all contents will be replaced. * NOTE: Currently, nullptr is not allowed if * the editor is an HTMLEditor. * @param aAllowBeforeInputEventCancelable * Whether `beforeinput` event which will be * dispatched for this can be cancelable or not. * @param aPreventSetSelection * Whether setting selection after replacing text. * If No, selection is the tail of replaced text. * If Yes, selection isn't changed. * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ MOZ_CAN_RUN_SCRIPT nsresult ReplaceTextAsAction( const nsAString& aString, nsRange* aReplaceRange, AllowBeforeInputEventCancelable aAllowBeforeInputEventCancelable, PreventSetSelection aPreventSetSelection = PreventSetSelection::No, nsIPrincipal* aPrincipal = nullptr); /** * Can we paste |aTransferable| or, if |aTransferable| is null, will a call * to pasteTransferable later possibly succeed if given an instance of * nsITransferable then? True if the doc is modifiable, and, if * |aTransfeable| is non-null, we have pasteable data in |aTransfeable|. */ virtual bool CanPasteTransferable(nsITransferable* aTransferable) = 0; /** * PasteAsAction() pastes clipboard content to Selection. This method * may dispatch ePaste event first. If its defaultPrevent() is called, * this does nothing but returns NS_OK. * * @param aClipboardType nsIClipboard::kGlobalClipboard or * nsIClipboard::kSelectionClipboard. * @param aDispatchPasteEvent Yes if this should dispatch ePaste event * before pasting. Otherwise, No. * @param aDataTransfer The object containing the data to use for the * paste operation. May be nullptr, in which case * this will just get the data from the clipboard. * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ enum class DispatchPasteEvent { No, Yes }; MOZ_CAN_RUN_SCRIPT nsresult PasteAsAction(nsIClipboard::ClipboardType aClipboardType, DispatchPasteEvent aDispatchPasteEvent, DataTransfer* aDataTransfer = nullptr, nsIPrincipal* aPrincipal = nullptr); /** * Paste aTransferable at Selection. * * @param aTransferable Must not be nullptr. * @param aDispatchPasteEvent Yes if this should dispatch ePaste event * before pasting. Otherwise, No. * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ MOZ_CAN_RUN_SCRIPT nsresult PasteTransferableAsAction( nsITransferable* aTransferable, DispatchPasteEvent aDispatchPasteEvent, nsIPrincipal* aPrincipal = nullptr); /** * PasteAsQuotationAsAction() pastes content in clipboard as quotation. * If the editor is TextEditor or in plaintext mode, will paste the content * with appending ">" to start of each line. * if the editor is HTMLEditor and is not in plaintext mode, will patste it * into newly created blockquote element. * * @param aClipboardType nsIClipboard::kGlobalClipboard or * nsIClipboard::kSelectionClipboard. * @param aDispatchPasteEvent Yes if this should dispatch ePaste event * before pasting. Otherwise, No. * @param aDataTransfer The object containing the data to use for the * paste operation. May be nullptr, in which case * this will just get the data from the clipboard. * @param aPrincipal Set subject principal if it may be called by * JS. If set to nullptr, will be treated as * called by system. */ MOZ_CAN_RUN_SCRIPT nsresult PasteAsQuotationAsAction(nsIClipboard::ClipboardType aClipboardType, DispatchPasteEvent aDispatchPasteEvent, DataTransfer* aDataTransfer = nullptr, nsIPrincipal* aPrincipal = nullptr); /** * Return true if `beforeinput` or `input` event is being dispatched. */ [[nodiscard]] bool IsDispatchingInputEvent() const { return mEditActionData && mEditActionData->IsDispatchingInputEvent(); } protected: // May be used by friends. class AutoEditActionDataSetter; /** * TopLevelEditSubActionData stores temporary data while we're handling * top-level edit sub-action. */ struct MOZ_STACK_CLASS TopLevelEditSubActionData final { friend class AutoEditActionDataSetter; // Set selected range before edit. Then, RangeUpdater keep modifying // the range while we're changing the DOM tree. RefPtr mSelectedRange; // Computing changed range while we're handling sub actions. RefPtr mChangedRange; // XXX In strict speaking, mCachedPendingStyles isn't enough to cache // inline styles because inline style can be specified with "style" // attribute and/or CSS in