Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/scripting/   (Office von Apache Version 25.8.3.2©) image not shown  

Quelle  EditorBase.h   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 <tuple>  // 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;

  /**
   * MayHaveMutationEventListeners() returns true when the window may have
   * mutation event listeners.
   *
   * @param aMutationEventType  One or multiple of NS_EVENT_BITS_MUTATION_*.
   * @return                    true if the editor is an HTMLEditor instance,
   *                            and at least one of NS_EVENT_BITS_MUTATION_* is
   *                            set to the window or in debug build.
   */

  bool MayHaveMutationEventListeners(
      uint32_t aMutationEventType = 0xFFFFFFFF) const {
    if (IsTextEditor()) {
      // DOM mutation event listeners cannot catch the changes of
      // <input type="text"> nor <textarea>.
      return false;
    }
#ifdef DEBUG
    // On debug build, this should always return true for testing complicated
    // path without mutation event listeners because when mutation event
    // listeners do not touch the DOM, editor needs to run as there is no
    // mutation event listeners.
    return true;
#else   // #ifdef DEBUG
    nsPIDOMWindowInner* window = GetInnerWindow();
    return window ? window->HasMutationListeners(aMutationEventType) : false;
#endif  // #ifdef DEBUG #else
  }

  /**
   * 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<nsCaret> GetCaret() const;

  already_AddRefed<nsIWidget> 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<DataTransfer> 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.
   */

  virtual nsresult GetPreferredIMEState(widget::IMEState* aState);

  /**
   * 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();
  }

  /**
   * 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
   * <br> element for empty editor, returns true.  If editor's root element has
   * non-empty text nodes or other nodes like <br>, 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<RangeItem> mSelectedRange;

    // Computing changed range while we're handling sub actions.
    RefPtr<nsRange> 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 <style> elements or CSS files.  So, we need
    //     to look for better implementation about this.
    // FYI: Initialization cost of AutoPendingStyleCacheArray is expensive and
    //      it is not used by TextEditor so that we should construct it only
    //      when we're an HTMLEditor.
    Maybe<AutoPendingStyleCacheArray> mCachedPendingStyles;

    // If we tried to delete selection, set to true.
    bool mDidDeleteSelection;

    // If we have explicitly set selection inter line, set to true.
    // `AfterEdit()` or something shouldn't overwrite it in such case.
    bool mDidExplicitlySetInterLine;

    // If we have deleted non-collapsed range set to true, there are only 2
    // cases for now:
    //   - non-collapsed range was selected.
    //   - selection was collapsed in a text node and a Unicode character
    //     was removed.
    bool mDidDeleteNonCollapsedRange;

    // If we have deleted parent empty blocks, set to true.
    bool mDidDeleteEmptyParentBlocks;

    // If we're a contenteditable editor, we temporarily increase edit count
    // of the document between `BeforeEdit()` and `AfterEdit()`.  I.e., if
    // we increased the count in `BeforeEdit()`, we need to decrease it in
    // `AfterEdit()`, however, the document may be changed to designMode or
    // non-editable.  Therefore, we need to store with this whether we need
    // to restore it.
    bool mRestoreContentEditableCount;

    // If we explicitly normalized whitespaces around the changed range,
    // set to true.
    bool mDidNormalizeWhitespaces;

    // Set to true by default.  If somebody inserts an HTML fragment
    // intentionally, any empty elements shouldn't be cleaned up later.  In the
    // case this is set to false.
    // TODO: We should not do this by default.  If it's necessary, each edit
    //       action handler do it by itself instead.  Then, we can avoid such
    //       unnecessary DOM tree scan.
    bool mNeedsToCleanUpEmptyElements;

    /**
     * The following methods modifies some data of this struct and
     * `EditSubActionData` struct.  Currently, these are required only
     * by `HTMLEditor`.  Therefore, for cutting the runtime cost of
     * `TextEditor`, these methods should be called only by `HTMLEditor`.
     * But it's fine to use these methods in `TextEditor` if necessary.
     * If so, you need to call `DidDeleteText()` and `DidInsertText()`
     * from `SetTextNodeWithoutTransaction()`.
     */

    void DidCreateElement(EditorBase& aEditorBase, Element& aNewElement);
    void DidInsertContent(EditorBase& aEditorBase, nsIContent& aNewContent);
    void WillDeleteContent(EditorBase& aEditorBase,
                           nsIContent& aRemovingContent);
    void DidSplitContent(EditorBase& aEditorBase, nsIContent& aSplitContent,
                         nsIContent& aNewContent);
    void DidJoinContents(EditorBase& aEditorBase,
                         const EditorRawDOMPoint& aJoinedPoint);
    void DidInsertText(EditorBase& aEditorBase,
                       const EditorRawDOMPoint& aInsertionBegin,
                       const EditorRawDOMPoint& aInsertionEnd);
    void DidDeleteText(EditorBase& aEditorBase,
                       const EditorRawDOMPoint& aStartInTextNode);
    void WillDeleteRange(EditorBase& aEditorBase,
                         const EditorRawDOMPoint& aStart,
                         const EditorRawDOMPoint& aEnd);

   private:
    void Clear() {
      mDidExplicitlySetInterLine = false;
      // We don't need to clear other members which are referred only when the
      // editor is an HTML editor anymore.  Note that if `mSelectedRange` is
      // non-nullptr, that means that we're in `HTMLEditor`.
      if (!mSelectedRange) {
        return;
      }
      mSelectedRange->Clear();
      mChangedRange->Reset();
      if (mCachedPendingStyles.isSome()) {
        mCachedPendingStyles->Clear();
      }
      mDidDeleteSelection = false;
      mDidDeleteNonCollapsedRange = false;
      mDidDeleteEmptyParentBlocks = false;
      mRestoreContentEditableCount = false;
      mDidNormalizeWhitespaces = false;
      mNeedsToCleanUpEmptyElements = true;
    }

    /**
     * Extend mChangedRange to include `aNode`.
     */

    nsresult AddNodeToChangedRange(const HTMLEditor& aHTMLEditor,
                                   nsINode& aNode);

    /**
     * Extend mChangedRange to include `aPoint`.
     */

    nsresult AddPointToChangedRange(const HTMLEditor& aHTMLEditor,
                                    const EditorRawDOMPoint& aPoint);

    /**
     * Extend mChangedRange to include `aStart` and `aEnd`.
     */

    nsresult AddRangeToChangedRange(const HTMLEditor& aHTMLEditor,
                                    const EditorRawDOMPoint& aStart,
                                    const EditorRawDOMPoint& aEnd);

    TopLevelEditSubActionData() = default;
    TopLevelEditSubActionData(const TopLevelEditSubActionData& aOther) = delete;
  };

  struct MOZ_STACK_CLASS EditSubActionData final {
    // While this is set to false, TopLevelEditSubActionData::mChangedRange
    // shouldn't be modified since in some cases, modifying it in the setter
    // itself may be faster.  Note that we should affect this only for current
    // edit sub action since mutation event listener may edit different range.
    bool mAdjustChangedRangeFromListener;

   private:
    void Clear() { mAdjustChangedRangeFromListener = true; }

    friend EditorBase;
  };

 protected:  // AutoEditActionDataSetter, this shouldn't be accessed by friends.
  /**
   * SettingDataTransfer enum class is used to specify whether DataTransfer
   * should be initialized with or without format.  For example, when user
   * uses Accel + Shift + V to paste text without format, DataTransfer should
   * have only plain/text data to make web apps treat it without format.
   */

  enum class SettingDataTransfer {
    eWithFormat,
    eWithoutFormat,
  };

  /**
   * AutoEditActionDataSetter grabs some necessary objects for handling any
   * edit actions and store the edit action what we're handling.  When this is
   * created, its pointer is set to the mEditActionData, and this guarantees
   * the lifetime of grabbing objects until it's destroyed.
   */

  class MOZ_STACK_CLASS AutoEditActionDataSetter final {
   public:
    // NOTE: aPrincipal will be used when we implement "beforeinput" event.
    //       It's set only when maybe we shouldn't dispatch it because of
    //       called by JS.  I.e., if this is nullptr, we can always dispatch
    //       it.
    AutoEditActionDataSetter(const EditorBase& aEditorBase,
                             EditAction aEditAction,
                             nsIPrincipal* aPrincipal = nullptr);
    ~AutoEditActionDataSetter();

    void SetSelectionCreatedByDoubleclick(bool aSelectionCreatedByDoubleclick) {
      mSelectionCreatedByDoubleclick = aSelectionCreatedByDoubleclick;
    }

    [[nodiscard]] bool SelectionCreatedByDoubleclick() const {
      return mSelectionCreatedByDoubleclick;
    }

    void UpdateEditAction(EditAction aEditAction) {
      MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent,
                 "It's too late to update EditAction since this may have "
                 "already dispatched a beforeinput event");
      mEditAction = aEditAction;
    }

    /**
     * CanHandle() or CanHandleAndHandleBeforeInput() must be called
     * immediately after creating the instance.  If caller does not need to
     * handle "beforeinput" event or caller needs to set additional information
     * the events later, use the former.  Otherwise, use the latter.  If caller
     * uses the former, it's required to call MaybeDispatchBeforeInputEvent() by
     * itself.
     *
     */

    [[nodiscard]] bool CanHandle() const {
#ifdef DEBUG
      mHasCanHandleChecked = true;
#endif  // #ifdefn DEBUG
      // Don't allow to run new edit action when an edit action caused
      // destroying the editor while it's being handled.
      if (mEditAction != EditAction::eInitializing &&
          mEditorWasDestroyedDuringHandlingEditAction) {
        NS_WARNING("Editor was destroyed during an edit action being handled");
        return false;
      }
      return IsDataAvailable();
    }
    [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
    CanHandleAndMaybeDispatchBeforeInputEvent() {
      if (MOZ_UNLIKELY(NS_WARN_IF(!CanHandle()))) {
        return NS_ERROR_NOT_INITIALIZED;
      }
      nsresult rv = MaybeFlushPendingNotifications();
      if (MOZ_UNLIKELY(NS_FAILED(rv))) {
        return rv;
      }
      return MaybeDispatchBeforeInputEvent();
    }
    [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
    CanHandleAndFlushPendingNotifications() {
      if (MOZ_UNLIKELY(NS_WARN_IF(!CanHandle()))) {
        return NS_ERROR_NOT_INITIALIZED;
      }
      MOZ_ASSERT(MayEditActionRequireLayout(mRawEditAction));
      return MaybeFlushPendingNotifications();
    }

    [[nodiscard]] bool IsDataAvailable() const {
      return mSelection && mEditorBase.mDocument;
    }

    /**
     * MaybeDispatchBeforeInputEvent() considers whether this instance needs to
     * dispatch "beforeinput" event or not.  Then,
     * mHasTriedToDispatchBeforeInputEvent is set to true.
     *
     * @param aDeleteDirectionAndAmount
     *                  If `MayEditActionDeleteAroundCollapsedSelection(
     *                  mEditAction)` returns true, this must be set.
     *                  Otherwise, don't set explicitly.
     * @return          If this method actually dispatches "beforeinput" event
     *                  and it's canceled, returns
     *                  NS_ERROR_EDITOR_ACTION_CANCELED.
     */

    [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MaybeDispatchBeforeInputEvent(
        nsIEditor::EDirection aDeleteDirectionAndAmount = nsIEditor::eNone);

    /**
     * MarkAsBeforeInputHasBeenDispatched() should be called only when updating
     * the DOM occurs asynchronously from user input (e.g., inserting blob
     * object which is loaded asynchronously) and `beforeinput` has already
     * been dispatched (always should be so).
     */

    void MarkAsBeforeInputHasBeenDispatched() {
      MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent());
      MOZ_ASSERT(mEditAction == EditAction::ePaste ||
                 mEditAction == EditAction::ePasteAsQuotation ||
                 mEditAction == EditAction::eDrop);
      mHasTriedToDispatchBeforeInputEvent = true;
    }

    /**
     * MarkAsHandled() is called before dispatching `input` event and notifying
     * editor observers.  After this is called, any nested edit action become
     * non illegal case.
     */

    void MarkAsHandled() {
      MOZ_ASSERT(!mHandled);
      mHandled = true;
    }

    /**
     * ShouldAlreadyHaveHandledBeforeInputEventDispatching() returns true if the
     * edit action requires to handle "beforeinput" event but not yet dispatched
     * it nor considered as not dispatched it and can dispatch it when this is
     * called.
     */

    bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const {
      return !HasTriedToDispatchBeforeInputEvent() &&
             NeedsBeforeInputEventHandling(mEditAction) &&
             IsBeforeInputEventEnabled() /* &&
              // If we still need to dispatch a clipboard event, we should
              // dispatch it first, then, we need to dispatch beforeinput
              // event later.
              !NeedsToDispatchClipboardEvent()*/

          ;
    }

    /**
     * HasTriedToDispatchBeforeInputEvent() returns true if the instance's
     * MaybeDispatchBeforeInputEvent() has already been called.
     */

    bool HasTriedToDispatchBeforeInputEvent() const {
      return mHasTriedToDispatchBeforeInputEvent;
    }

    bool IsCanceled() const { return mBeforeInputEventCanceled; }

    /**
     * Returns a `Selection` for normal selection.  The lifetime is guaranteed
     * during alive this instance in the stack.
     */

    MOZ_KNOWN_LIVE Selection& SelectionRef() const {
      MOZ_ASSERT(!mSelection ||
                 (mSelection->GetType() == SelectionType::eNormal));
      return *mSelection;
    }

    nsIPrincipal* GetPrincipal() const { return mPrincipal; }
    EditAction GetEditAction() const { return mEditAction; }

    template <typename PT, typename CT>
    void SetSpellCheckRestartPoint(const EditorDOMPointBase<PT, CT>& aPoint) {
      MOZ_ASSERT(aPoint.IsSet());
      // We should store only container and offset because new content may
      // be inserted before referring child.
      // XXX Shouldn't we compare whether aPoint is before
      //     mSpellCheckRestartPoint if it's set.
      mSpellCheckRestartPoint =
          EditorDOMPoint(aPoint.GetContainer(), aPoint.Offset());
    }
    void ClearSpellCheckRestartPoint() { mSpellCheckRestartPoint.Clear(); }
    const EditorDOMPoint& GetSpellCheckRestartPoint() const {
      return mSpellCheckRestartPoint;
    }

    void SetData(const nsAString& aData) {
      MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent,
                 "It's too late to set data since this may have already "
                 "dispatched a beforeinput event");
      mData = aData;
    }
    const nsString& GetData() const { return mData; }

    void SetColorData(const nsAString& aData);

    /**
     * InitializeDataTransfer(DataTransfer*) sets mDataTransfer to
     * aDataTransfer.  In this case, aDataTransfer should not be read/write
     * because it'll be set to InputEvent.dataTransfer and which should be
     * read-only.
     */

    void InitializeDataTransfer(DataTransfer* aDataTransfer);
    /**
     * InitializeDataTransfer(nsITransferable*) creates new DataTransfer
     * instance, initializes it with aTransferable and sets mDataTransfer to
     * it.
     */

    void InitializeDataTransfer(nsITransferable* aTransferable);
    /**
     * InitializeDataTransfer(const nsAString&) creates new DataTransfer
     * instance, initializes it with aString and sets mDataTransfer to it.
     */

    void InitializeDataTransfer(const nsAString& aString);
    /**
     * InitializeDataTransferWithClipboard() creates new DataTransfer instance,
     * initializes it with clipboard and sets mDataTransfer to it.
     */

    void InitializeDataTransferWithClipboard(
        SettingDataTransfer aSettingDataTransfer, DataTransfer* aDataTransfer,
        nsIClipboard::ClipboardType aClipboardType);
    DataTransfer* GetDataTransfer() const { return mDataTransfer; }

    /**
     * AppendTargetRange() appends aTargetRange to target ranges.  This should
     * be used only by edit action handlers which do not want to set target
     * ranges to selection ranges.
     */

    void AppendTargetRange(dom::StaticRange& aTargetRange);

    /**
     * Make dispatching `beforeinput` forcibly non-cancelable.
     */

    void MakeBeforeInputEventNonCancelable() {
      mMakeBeforeInputEventNonCancelable = true;
    }

    /**
     * NotifyOfDispatchingClipboardEvent() is called after dispatching
     * a clipboard event.
     */

    void NotifyOfDispatchingClipboardEvent() {
      MOZ_ASSERT(NeedsToDispatchClipboardEvent());
      MOZ_ASSERT(!mHasTriedToDispatchClipboardEvent);
      mHasTriedToDispatchClipboardEvent = true;
    }

    void Abort() { mAborted = true; }
    bool IsAborted() const { return mAborted; }

    void OnEditorDestroy() {
      if (!mHandled && mHasTriedToDispatchBeforeInputEvent) {
        // Remember the editor was destroyed only when this edit action is being
        // handled because they are caused by mutation event listeners or
        // something other unexpected event listeners.  In the cases, new child
        // edit action shouldn't been aborted.
        mEditorWasDestroyedDuringHandlingEditAction = true;
      }
      if (mParentData) {
        mParentData->OnEditorDestroy();
      }
    }
    bool HasEditorDestroyedDuringHandlingEditAction() const {
      return mEditorWasDestroyedDuringHandlingEditAction;
    }

    void SetTopLevelEditSubAction(EditSubAction aEditSubAction,
                                  EDirection aDirection = eNone) {
      mTopLevelEditSubAction = aEditSubAction;
      TopLevelEditSubActionDataRef().Clear();
      switch (mTopLevelEditSubAction) {
        case EditSubAction::eInsertNode:
        case EditSubAction::eMoveNode:
        case EditSubAction::eCreateNode:
        case EditSubAction::eSplitNode:
        case EditSubAction::eInsertText:
        case EditSubAction::eInsertTextComingFromIME:
        case EditSubAction::eSetTextProperty:
        case EditSubAction::eRemoveTextProperty:
        case EditSubAction::eRemoveAllTextProperties:
        case EditSubAction::eSetText:
        case EditSubAction::eInsertLineBreak:
        case EditSubAction::eInsertParagraphSeparator:
        case EditSubAction::eCreateOrChangeList:
        case EditSubAction::eIndent:
        case EditSubAction::eOutdent:
        case EditSubAction::eSetOrClearAlignment:
        case EditSubAction::eCreateOrRemoveBlock:
        case EditSubAction::eFormatBlockForHTMLCommand:
        case EditSubAction::eMergeBlockContents:
        case EditSubAction::eRemoveList:
        case EditSubAction::eCreateOrChangeDefinitionListItem:
        case EditSubAction::eInsertElement:
        case EditSubAction::eInsertQuotation:
        case EditSubAction::eInsertQuotedText:
        case EditSubAction::ePasteHTMLContent:
        case EditSubAction::eInsertHTMLSource:
        case EditSubAction::eSetPositionToAbsolute:
        case EditSubAction::eSetPositionToStatic:
        case EditSubAction::eDecreaseZIndex:
        case EditSubAction::eIncreaseZIndex:
          MOZ_ASSERT(aDirection == eNext);
          mDirectionOfTopLevelEditSubAction = eNext;
          break;
        case EditSubAction::eJoinNodes:
        case EditSubAction::eDeleteText:
          MOZ_ASSERT(aDirection == ePrevious);
          mDirectionOfTopLevelEditSubAction = ePrevious;
          break;
        case EditSubAction::eUndo:
        case EditSubAction::eRedo:
        case EditSubAction::eComputeTextToOutput:
        case EditSubAction::eCreatePaddingBRElementForEmptyEditor:
        case EditSubAction::eMaintainWhiteSpaceVisibility:
        case EditSubAction::eNone:
        case EditSubAction::eReplaceHeadWithHTMLSource:
          MOZ_ASSERT(aDirection == eNone);
          mDirectionOfTopLevelEditSubAction = eNone;
          break;
        case EditSubAction::eDeleteNode:
        case EditSubAction::eDeleteSelectedContent:
          // Unfortunately, eDeleteNode and eDeleteSelectedContent is used with
          // any direction.  We might have specific sub-action for each
          // direction, but there are some points referencing
          // eDeleteSelectedContent so that we should keep storing direction
          // as-is for now.
          mDirectionOfTopLevelEditSubAction = aDirection;
          break;
      }
    }
    EditSubAction GetTopLevelEditSubAction() const {
      MOZ_ASSERT(IsDataAvailable());
      return mTopLevelEditSubAction;
    }
    EDirection GetDirectionOfTopLevelEditSubAction() const {
      return mDirectionOfTopLevelEditSubAction;
    }

    const TopLevelEditSubActionData& TopLevelEditSubActionDataRef() const {
      return mParentData ? mParentData->TopLevelEditSubActionDataRef()
                         : mTopLevelEditSubActionData;
    }
    TopLevelEditSubActionData& TopLevelEditSubActionDataRef() {
      return mParentData ? mParentData->TopLevelEditSubActionDataRef()
                         : mTopLevelEditSubActionData;
    }

    const EditSubActionData& EditSubActionDataRef() const {
      return mEditSubActionData;
    }
    EditSubActionData& EditSubActionDataRef() { return mEditSubActionData; }

    SelectionState& SavedSelectionRef() {
      return mParentData ? mParentData->SavedSelectionRef() : mSavedSelection;
    }
    const SelectionState& SavedSelectionRef() const {
      return mParentData ? mParentData->SavedSelectionRef() : mSavedSelection;
    }

    RangeUpdater& RangeUpdaterRef() {
      return mParentData ? mParentData->RangeUpdaterRef() : mRangeUpdater;
    }
    const RangeUpdater& RangeUpdaterRef() const {
      return mParentData ? mParentData->RangeUpdaterRef() : mRangeUpdater;
    }

    void UpdateSelectionCache(Selection& aSelection);

    bool IsDispatchingInputEvent() const {
      return mDispatchingInputEvent ||
             (mParentData && mParentData->IsDispatchingInputEvent());
    }
    void WillDispatchInputEvent() {
      MOZ_ASSERT(!mDispatchingInputEvent);
      mDispatchingInputEvent = true;
    }
    void DidDispatchInputEvent() {
      MOZ_ASSERT(mDispatchingInputEvent);
      mDispatchingInputEvent = false;
    }

   private:
    bool IsBeforeInputEventEnabled() const;

    [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
    MaybeFlushPendingNotifications() const;

    static bool NeedsBeforeInputEventHandling(EditAction aEditAction) {
      MOZ_ASSERT(aEditAction != EditAction::eNone);
      switch (aEditAction) {
        case EditAction::eNone:
        // If we're not handling edit action, we don't need to handle
        // "beforeinput" event.
        case EditAction::eNotEditing:
        // If we're being initialized, we may need to create a padding <br>
        // element, but it shouldn't cause `beforeinput` event.
        case EditAction::eInitializing:
        // If we're just selecting or getting table cells, we shouldn't
        // dispatch `beforeinput` event.
        case NS_EDIT_ACTION_CASES_ACCESSING_TABLE_DATA_WITHOUT_EDITING:
        // If raw level transaction API is used, the API user needs to handle
        // both "beforeinput" event and "input" event if it's necessary.
        case EditAction::eUnknown:
        // Hiding/showing password affects only layout so that we don't need
        // to handle beforeinput event for it.
        case EditAction::eHidePassword:
        // We don't need to dispatch "beforeinput" event before
        // "compositionstart".
        case EditAction::eStartComposition:
        // We don't need to let web apps know the mode change.
        case EditAction::eEnableOrDisableCSS:
        case EditAction::eEnableOrDisableAbsolutePositionEditor:
        case EditAction::eEnableOrDisableResizer:
        case EditAction::eEnableOrDisableInlineTableEditingUI:
        // We don't need to let contents in chrome's editor to know the size
        // change.
        case EditAction::eSetWrapWidth:
        // While resizing or moving element, we update only shadow, i.e.,
        // don't touch to the DOM in content.  Therefore, we don't need to
        // dispatch "beforeinput" event.
        case EditAction::eResizingElement:
        case EditAction::eMovingElement:
        // Perhaps, we don't need to dispatch "beforeinput" event for
        // padding `<br>` element for empty editor because it's internal
        // handling and it should be occurred by another change.
        case EditAction::eCreatePaddingBRElementForEmptyEditor:
          return false;
        default:
          return true;
      }
    }

    bool NeedsToDispatchClipboardEvent() const {
      if (mHasTriedToDispatchClipboardEvent) {
        return false;
      }
      switch (mEditAction) {
        case EditAction::ePaste:
        case EditAction::ePasteAsQuotation:
        case EditAction::eCut:
        case EditAction::eCopy:
          return true;
        default:
          return false;
      }
    }

    void MarkEditActionCanceled();

    EditorBase& mEditorBase;
    RefPtr<Selection> mSelection;
    nsTArray<OwningNonNull<Selection>> mRetiredSelections;

    // True if the selection was created by doubleclicking a word.
    bool mSelectionCreatedByDoubleclick{false};

    nsCOMPtr<nsIPrincipal> mPrincipal;
    // EditAction may be nested, for example, a command may be executed
    // from mutation event listener which is run while editor changes
    // the DOM tree.  In such case, we need to handle edit action separately.
    AutoEditActionDataSetter* mParentData;

    // Cached selection for AutoSelectionRestorer.
    SelectionState mSavedSelection;

    // Utility class object for maintaining preserved ranges.
    RangeUpdater mRangeUpdater;

    // The data should be set to InputEvent.data.
    nsString mData;

    // The dataTransfer should be set to InputEvent.dataTransfer.
    RefPtr<DataTransfer> mDataTransfer;

    // They are used for result of InputEvent.getTargetRanges() of beforeinput.
    OwningNonNullStaticRangeArray mTargetRanges;

    // Start point where spell checker should check from.  This is used only
    // by TextEditor.
    EditorDOMPoint mSpellCheckRestartPoint;

    // Different from mTopLevelEditSubAction, its data should be stored only
    // in the most ancestor AutoEditActionDataSetter instance since we don't
    // want to pay the copying cost and sync cost.
    TopLevelEditSubActionData mTopLevelEditSubActionData;

    // Different from mTopLevelEditSubActionData, this stores temporaly data
    // for current edit sub action.
    EditSubActionData mEditSubActionData;

    // mEditAction and mRawEditActions stores edit action.  The difference of
    // them is, if and only if edit actions are nested and parent edit action
    // is one of trying to edit something, but nested one is not so, it's
    // overwritten by the parent edit action.
    EditAction mEditAction;
    EditAction mRawEditAction;

    // Different from its data, you can refer "current" AutoEditActionDataSetter
    // instance's mTopLevelEditSubAction member since it's copied from the
    // parent instance at construction and it's always cleared before this
    // won't be overwritten and cleared before destruction.
    EditSubAction mTopLevelEditSubAction;

    EDirection mDirectionOfTopLevelEditSubAction;

    bool mAborted;

    // Set to true when this handles "beforeinput" event dispatching.  Note
    // that even if "beforeinput" event shouldn't be dispatched for this,
    // instance, this is set to true when it's considered.
    bool mHasTriedToDispatchBeforeInputEvent;
    // Set to true if "beforeinput" event was dispatched and it's canceled.
    bool mBeforeInputEventCanceled;
    // Set to true if `beforeinput` event must not be cancelable even if
    // its inputType is defined as cancelable by the standards.
    bool mMakeBeforeInputEventNonCancelable;
    // Set to true when the edit action handler tries to dispatch a clipboard
    // event.
    bool mHasTriedToDispatchClipboardEvent;
    // The editor instance may be destroyed once temporarily if `document.write`
    // etc runs.  In such case, we should mark this flag of being handled
    // edit action.
    bool mEditorWasDestroyedDuringHandlingEditAction;
    // This is set before dispatching `input` event and notifying editor
    // observers.
    bool mHandled;
    // Whether the editor is dispatching a `beforeinput` or `input` event.
    bool mDispatchingInputEvent = false;

#ifdef DEBUG
    mutable bool mHasCanHandleChecked = false;
#endif  // #ifdef DEBUG

    AutoEditActionDataSetter() = delete;
    AutoEditActionDataSetter(const AutoEditActionDataSetter& aOther) = delete;
  };

  void UpdateEditActionData(const nsAString& aData) {
    mEditActionData->SetData(aData);
  }

  void NotifyOfDispatchingClipboardEvent() {
    MOZ_ASSERT(mEditActionData);
    mEditActionData->NotifyOfDispatchingClipboardEvent();
  }

 protected:  // May be called by friends.
  /****************************************************************************
   * Some friend classes are allowed to call the following protected methods.
   * However, those methods won't prepare caches of some objects which are
   * necessary for them.  So, if you call them from friend classes, you need
   * to make sure that AutoEditActionDataSetter is created.
   ****************************************************************************/


  bool IsEditActionCanceled() const {
    MOZ_ASSERT(mEditActionData);
    return mEditActionData->IsCanceled();
  }

  bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const {
    MOZ_ASSERT(mEditActionData);
    return mEditActionData
        ->ShouldAlreadyHaveHandledBeforeInputEventDispatching();
  }

  [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MaybeDispatchBeforeInputEvent() {
    MOZ_ASSERT(mEditActionData);
    return mEditActionData->MaybeDispatchBeforeInputEvent();
  }

  void MarkAsBeforeInputHasBeenDispatched() {
    MOZ_ASSERT(mEditActionData);
    return mEditActionData->MarkAsBeforeInputHasBeenDispatched();
  }

  bool HasTriedToDispatchBeforeInputEvent() const {
    return mEditActionData &&
           mEditActionData->HasTriedToDispatchBeforeInputEvent();
  }

  bool IsEditActionDataAvailable() const {
    return mEditActionData && mEditActionData->IsDataAvailable();
  }

  bool IsTopLevelEditSubActionDataAvailable() const {
    return mEditActionData && !!GetTopLevelEditSubAction();
  }

  bool IsEditActionAborted() const {
    MOZ_ASSERT(mEditActionData);
    return mEditActionData->IsAborted();
  }

  nsresult GetDataFromDataTransferOrClipboard(
      DataTransfer* aDataTransfer, nsITransferable* aTransferable,
      nsIClipboard::ClipboardType aClipboardType) const;

  /**
   * SelectionRef() returns cached normal Selection.  This is pretty faster than
   * EditorBase::GetSelection() if available.
   * Note that this never crash unless public methods ignore the result of
   * AutoEditActionDataSetter::CanHandle() and keep handling edit action but any
   * methods should stop handling edit action if it returns false.
   */

  MOZ_KNOWN_LIVE Selection& SelectionRef() const {
    MOZ_ASSERT(mEditActionData);
    MOZ_ASSERT(mEditActionData->SelectionRef().GetType() ==
               SelectionType::eNormal);
    return mEditActionData->SelectionRef();
  }

  nsIPrincipal* GetEditActionPrincipal() const {
    MOZ_ASSERT(mEditActionData);
    return mEditActionData->GetPrincipal();
  }

  /**
   * GetEditAction() returns EditAction which is being handled.  If some
   * edit actions are nested, this returns the innermost edit action.
   */

  EditAction GetEditAction() const {
    return mEditActionData ? mEditActionData->GetEditAction()
                           : EditAction::eNone;
  }

  /**
   * GetInputEventData() returns inserting or inserted text value with
   * current edit action.  The result is proper for InputEvent.data value.
   */

  const nsString& GetInputEventData() const {
    return mEditActionData ? mEditActionData->GetData() : VoidString();
  }

  /**
   * GetInputEventDataTransfer() returns inserting or inserted transferable
   * content with current edit action.  The result is proper for
   * InputEvent.dataTransfer value.
   */

  DataTransfer* GetInputEventDataTransfer() const {
    return mEditActionData ? mEditActionData->GetDataTransfer() : nullptr;
  }

  /**
   * GetTopLevelEditSubAction() returns the top level edit sub-action.
   * For example, if selected content is being replaced with inserted text,
   * while removing selected content, the top level edit sub-action may be
   * EditSubAction::eDeleteSelectedContent.  However, while inserting new
   * text, the top level edit sub-action may be EditSubAction::eInsertText.
   * So, this result means what we are doing right now unless you're looking
   * for a case which the method is called via mutation event listener or
   * selectionchange event listener which are fired while handling the edit
   * sub-action.
   */

  EditSubAction GetTopLevelEditSubAction() const {
    return mEditActionData ? mEditActionData->GetTopLevelEditSubAction()
                           : EditSubAction::eNone;
  }

  /**
   * GetDirectionOfTopLevelEditSubAction() returns direction which user
   * intended for doing the edit sub-action.
   */

  EDirection GetDirectionOfTopLevelEditSubAction() const {
    return mEditActionData
               ? mEditActionData->GetDirectionOfTopLevelEditSubAction()
               : eNone;
  }

  /**
   * SavedSelection() returns reference to saved selection which are
   * stored by AutoSelectionRestorer.
   */

  SelectionState& SavedSelectionRef() {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->SavedSelectionRef();
  }
  const SelectionState& SavedSelectionRef() const {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->SavedSelectionRef();
  }

  RangeUpdater& RangeUpdaterRef() {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->RangeUpdaterRef();
  }
  const RangeUpdater& RangeUpdaterRef() const {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->RangeUpdaterRef();
  }

  template <typename PT, typename CT>
  void SetSpellCheckRestartPoint(const EditorDOMPointBase<PT, CT>& aPoint) {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->SetSpellCheckRestartPoint(aPoint);
  }

  void ClearSpellCheckRestartPoint() {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->ClearSpellCheckRestartPoint();
  }

  const EditorDOMPoint& GetSpellCheckRestartPoint() const {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->GetSpellCheckRestartPoint();
  }

  const TopLevelEditSubActionData& TopLevelEditSubActionDataRef() const {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->TopLevelEditSubActionDataRef();
  }
  TopLevelEditSubActionData& TopLevelEditSubActionDataRef() {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->TopLevelEditSubActionDataRef();
  }

  const EditSubActionData& EditSubActionDataRef() const {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->EditSubActionDataRef();
  }
  EditSubActionData& EditSubActionDataRef() {
    MOZ_ASSERT(IsEditActionDataAvailable());
    return mEditActionData->EditSubActionDataRef();
  }

  /**
--> --------------------

--> maximum size reached

--> --------------------

100%


¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.32Angebot  Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.