/* -*- 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/Assertions.h" // for MOZ_ASSERT, etc. #include "mozFlushType.h" // for mozFlushType enum #include "mozilla/OwningNonNull.h" // for OwningNonNull #include "mozilla/SelectionState.h" // for RangeUpdater, etc. #include "mozilla/StyleSheet.h" // for StyleSheet #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" // for WeakPtr #include "mozilla/dom/Text.h" #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr #include "nsCycleCollectionParticipant.h" #include "nsGkAtoms.h" #include "nsIDocument.h" // for nsIDocument #include "nsIEditor.h" // for nsIEditor::EDirection, etc. #include "nsIEditorIMESupport.h" // for NS_DECL_NSIEDITORIMESUPPORT, etc. #include "nsIObserver.h" // for NS_DECL_NSIOBSERVER, etc. #include "nsIPhonetic.h" // for NS_DECL_NSIPHONETIC, etc. #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc. #include "nsISelectionController.h" // for nsISelectionController constants #include "nsISupportsImpl.h" // for EditorBase::Release, etc. #include "nsIWeakReferenceUtils.h" // for nsWeakPtr #include "nsLiteralString.h" // for NS_LITERAL_STRING #include "nsString.h" // for nsCString #include "nsWeakReference.h" // for nsSupportsWeakReference #include "nscore.h" // for nsresult, nsAString, etc. class nsIAtom; class nsIContent; class nsIDOMEvent; class nsIDOMEventListener; class nsIDOMEventTarget; class nsIDOMKeyEvent; class nsIDOMNode; class nsIDocument; class nsIDocumentStateListener; class nsIEditActionListener; class nsIEditorObserver; class nsIInlineSpellChecker; class nsINode; class nsIPresShell; class nsISupports; class nsITransaction; class nsIWidget; class nsRange; class nsString; class nsTransactionManager; // This is int32_t instead of int16_t because nsIInlineSpellChecker.idl's // spellCheckAfterEditorChange is defined to take it as a long. // XXX EditAction causes unnecessary include of EditorBase from some places. // Why don't you move this to nsIEditor.idl? enum class EditAction : int32_t { ignore = -1, none = 0, undo, redo, insertNode, createNode, deleteNode, splitNode, joinNode, deleteText = 1003, // text commands insertText = 2000, insertIMEText = 2001, deleteSelection = 2002, setTextProperty = 2003, removeTextProperty = 2004, outputText = 2005, // html only action insertBreak = 3000, makeList = 3001, indent = 3002, outdent = 3003, align = 3004, makeBasicBlock = 3005, removeList = 3006, makeDefListItem = 3007, insertElement = 3008, insertQuotation = 3009, htmlPaste = 3012, loadHTML = 3013, resetTextProperties = 3014, setAbsolutePosition = 3015, removeAbsolutePosition = 3016, decreaseZIndex = 3017, increaseZIndex = 3018 }; inline bool operator!(const EditAction& aOp) { return aOp == EditAction::none; } namespace mozilla { class AddStyleSheetTransaction; class AutoRules; class AutoSelectionRestorer; class AutoTransactionsConserveSelection; class ChangeAttributeTransaction; class CompositionTransaction; class CreateElementTransaction; class DeleteNodeTransaction; class DeleteTextTransaction; class EditAggregateTransaction; class ErrorResult; class InsertNodeTransaction; class InsertTextTransaction; class JoinNodeTransaction; class PlaceholderTransaction; class RemoveStyleSheetTransaction; class SplitNodeTransaction; class TextComposition; struct EditorDOMPoint; namespace dom { class DataTransfer; class Element; class EventTarget; class Selection; class Text; } // namespace dom namespace widget { struct IMEState; } // namespace widget /** * CachedWeakPtr stores a pointer to a class which inherits nsIWeakReference. * If the instance of the class has already been destroyed, this returns * nullptr. Otherwise, returns cached pointer. */ template class CachedWeakPtr final { public: CachedWeakPtr() : mCache(nullptr) { } CachedWeakPtr& operator=(T* aObject) { mWeakPtr = do_GetWeakReference(aObject); mCache = aObject; return *this; } CachedWeakPtr& operator=(const nsCOMPtr& aOther) { mWeakPtr = do_GetWeakReference(aOther); mCache = aOther; return *this; } CachedWeakPtr& operator=(already_AddRefed& aOther) { nsCOMPtr other = aOther; mWeakPtr = do_GetWeakReference(other); mCache = other; return *this; } bool IsAlive() const { return mWeakPtr && mWeakPtr->IsAlive(); } explicit operator bool() const { return mWeakPtr; } operator T*() const { return get(); } T* get() const { if (mCache && !mWeakPtr->IsAlive()) { const_cast*>(this)->mCache = nullptr; } return mCache; } private: nsWeakPtr mWeakPtr; T* MOZ_NON_OWNING_REF mCache; }; #define kMOZEditorBogusNodeAttrAtom nsGkAtoms::mozeditorbogusnode #define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE") /** * 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 nsIEditorIMESupport , public nsSupportsWeakReference , public nsIPhonetic { public: typedef dom::Element Element; typedef dom::Selection Selection; typedef dom::Text Text; enum IterDirection { kIterForward, kIterBackward }; /** * The default constructor. This should suffice. the setting of the * interfaces is done after the construction of the editor class. */ EditorBase(); protected: /** * The default destructor. This should suffice. Should this be pure virtual * for someone to derive from the EditorBase later? I don't believe so. */ virtual ~EditorBase(); public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor) bool IsInitialized() const { return !!mDocumentWeak; } already_AddRefed GetDOMDocument(); already_AddRefed GetDocument(); already_AddRefed GetPresShell(); already_AddRefed GetWidget(); enum NotificationForEditorObservers { eNotifyEditorObserversOfEnd, eNotifyEditorObserversOfBefore, eNotifyEditorObserversOfCancel }; void NotifyEditorObservers(NotificationForEditorObservers aNotification); // nsIEditor methods NS_DECL_NSIEDITOR // nsIEditorIMESupport methods NS_DECL_NSIEDITORIMESUPPORT // nsIPhonetic NS_DECL_NSIPHONETIC public: virtual bool IsModifiableNode(nsINode* aNode); virtual nsresult InsertTextImpl(const nsAString& aStringToInsert, nsCOMPtr* aInOutNode, int32_t* aInOutOffset, nsIDocument* aDoc); nsresult InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, Text& aTextNode, int32_t aOffset, bool aSuppressIME = false); NS_IMETHOD DeleteSelectionImpl(EDirection aAction, EStripWrappers aStripWrappers); already_AddRefed DeleteSelectionAndCreateElement(nsIAtom& aTag); /** * Helper routines for node/parent manipulations. */ nsresult DeleteNode(nsINode* aNode); nsresult InsertNode(nsIContent& aNode, nsINode& aParent, int32_t aPosition); enum ECloneAttributes { eDontCloneAttributes, eCloneAttributes }; already_AddRefed ReplaceContainer(Element* aOldContainer, nsIAtom* aNodeType, nsIAtom* aAttribute = nullptr, const nsAString* aValue = nullptr, ECloneAttributes aCloneAttributes = eDontCloneAttributes); void CloneAttributes(Element* aDest, Element* aSource); nsresult RemoveContainer(nsIContent* aNode); already_AddRefed InsertContainerAbove(nsIContent* aNode, nsIAtom* aNodeType, nsIAtom* aAttribute = nullptr, const nsAString* aValue = nullptr); nsIContent* SplitNode(nsIContent& aNode, int32_t aOffset, ErrorResult& aResult); nsresult JoinNodes(nsINode& aLeftNode, nsINode& aRightNode); nsresult MoveNode(nsIContent* aNode, nsINode* aParent, int32_t aOffset); nsresult CloneAttribute(nsIAtom* aAttribute, Element* aDestElement, Element* aSourceElement); nsresult RemoveAttribute(Element* aElement, nsIAtom* aAttribute); virtual nsresult RemoveAttributeOrEquivalent(Element* aElement, nsIAtom* aAttribute, bool aSuppressTransaction) = 0; nsresult SetAttribute(Element* aElement, nsIAtom* aAttribute, const nsAString& aValue); virtual nsresult SetAttributeOrEquivalent(Element* aElement, nsIAtom* aAttribute, const nsAString& aValue, bool aSuppressTransaction) = 0; /** * Method to replace certain CreateElementNS() calls. * * @param aTag Tag you want. */ already_AddRefed CreateHTMLContent(nsIAtom* aTag); /** * IME event handlers. */ virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent); virtual nsresult UpdateIMEComposition( WidgetCompositionEvent* aCompositionChangeEvent) = 0; void EndIMEComposition(); void SwitchTextDirectionTo(uint32_t aDirection); protected: nsresult DetermineCurrentDirection(); void FireInputEvent(); /** * Create a transaction for setting aAttribute to aValue on aElement. Never * returns null. */ already_AddRefed CreateTxnForSetAttribute(Element& aElement, nsIAtom& aAttribute, const nsAString& aValue); /** * Create a transaction for removing aAttribute on aElement. Never returns * null. */ already_AddRefed CreateTxnForRemoveAttribute(Element& aElement, nsIAtom& aAttribute); /** * Create a transaction for creating a new child node of aParent of type aTag. */ already_AddRefed CreateTxnForCreateElement(nsIAtom& aTag, nsINode& aParent, int32_t aPosition); already_AddRefed CreateNode(nsIAtom* aTag, nsINode* aParent, int32_t aPosition); /** * Create a transaction for inserting aNode as a child of aParent. */ already_AddRefed CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent, int32_t aOffset); /** * Create a transaction for removing aNode from its parent. */ nsresult CreateTxnForDeleteNode(nsINode* aNode, DeleteNodeTransaction** aTransaction); nsresult CreateTxnForDeleteSelection( EDirection aAction, EditAggregateTransaction** aTransaction, nsINode** aNode, int32_t* aOffset, int32_t* aLength); nsresult CreateTxnForDeleteInsertionPoint( nsRange* aRange, EDirection aAction, EditAggregateTransaction* aTransaction, nsINode** aNode, int32_t* aOffset, int32_t* aLength); /** * Create a transaction for inserting aStringToInsert into aTextNode. Never * returns null. */ already_AddRefed CreateTxnForInsertText(const nsAString& aStringToInsert, Text& aTextNode, int32_t aOffset); /** * Never returns null. */ already_AddRefed CreateTxnForComposition(const nsAString& aStringToInsert); /** * Create a transaction for adding a style sheet. */ NS_IMETHOD CreateTxnForAddStyleSheet( StyleSheet* aSheet, AddStyleSheetTransaction** aTransaction); /** * Create a transaction for removing a style sheet. */ NS_IMETHOD CreateTxnForRemoveStyleSheet( StyleSheet* aSheet, RemoveStyleSheetTransaction** aTransaction); nsresult DeleteText(nsGenericDOMDataNode& aElement, uint32_t aOffset, uint32_t aLength); already_AddRefed CreateTxnForDeleteText(nsGenericDOMDataNode& aElement, uint32_t aOffset, uint32_t aLength); already_AddRefed CreateTxnForDeleteCharacter(nsGenericDOMDataNode& aData, uint32_t aOffset, EDirection aDirection); already_AddRefed CreateTxnForSplitNode(nsIContent& aNode, uint32_t aOffset); already_AddRefed CreateTxnForJoinNode(nsINode& aLeftNode, nsINode& aRightNode); /** * This method first deletes the selection, if it's not collapsed. Then if * the selection lies in a CharacterData node, it splits it. If the * selection is at this point collapsed in a CharacterData node, it's * adjusted to be collapsed right before or after the node instead (which is * always possible, since the node was split). */ nsresult DeleteSelectionAndPrepareToCreateNode(); /** * Called after a transaction is done successfully. */ void DoAfterDoTransaction(nsITransaction *aTxn); /** * Called after a transaction is undone successfully. */ void DoAfterUndoTransaction(); /** * Called after a transaction is redone successfully. */ void DoAfterRedoTransaction(); enum TDocumentListenerNotification { eDocumentCreated, eDocumentToBeDestroyed, eDocumentStateChanged }; /** * Tell the doc state listeners that the doc state has changed. */ NS_IMETHOD NotifyDocumentListeners( TDocumentListenerNotification aNotificationType); /** * Make the given selection span the entire document. */ virtual nsresult SelectEntireDocument(Selection* aSelection); /** * Helper method for scrolling the selection into view after * an edit operation. aScrollToAnchor should be true if you * want to scroll to the point where the selection was started. * If false, it attempts to scroll the end of the selection into view. * * Editor methods *should* call this method instead of the versions * in the various selection interfaces, since this version makes sure * that the editor's sync/async settings for reflowing, painting, and * scrolling match. */ NS_IMETHOD ScrollSelectionIntoView(bool aScrollToAnchor); virtual bool IsBlockNode(nsINode* aNode); /** * Helper for GetPriorNode() and GetNextNode(). */ nsIContent* FindNextLeafNode(nsINode* aCurrentNode, bool aGoForward, bool bNoBlockCrossing); virtual nsresult InstallEventListeners(); virtual void CreateEventListeners(); virtual void RemoveEventListeners(); /** * Return true if spellchecking should be enabled for this editor. */ bool GetDesiredSpellCheckState(); bool CanEnableSpellCheck() { // Check for password/readonly/disabled, which are not spellchecked // regardless of DOM. Also, check to see if spell check should be skipped // or not. return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() && !ShouldSkipSpellCheck(); } /** * EnsureComposition() should be called by composition event handlers. This * tries to get the composition for the event and set it to mComposition. * However, this may fail because the composition may be committed before * the event comes to the editor. * * @return true if there is a composition. Otherwise, for example, * a composition event handler in web contents moved focus * for committing the composition, returns false. */ bool EnsureComposition(WidgetCompositionEvent* aCompositionEvent); already_AddRefed GetSelectionController(); nsresult GetSelection(SelectionType aSelectionType, nsISelection** aSelection); public: /** * All editor operations which alter the doc should be prefaced * with a call to StartOperation, naming the action and direction. */ NS_IMETHOD StartOperation(EditAction opID, nsIEditor::EDirection aDirection); /** * All editor operations which alter the doc should be followed * with a call to EndOperation. */ NS_IMETHOD EndOperation(); /** * Routines for managing the preservation of selection across * various editor actions. */ bool ArePreservingSelection(); void PreserveSelectionAcrossActions(Selection* aSel); nsresult RestorePreservedSelection(Selection* aSel); void StopPreservingSelection(); /** * SplitNode() creates a new node identical to an existing node, and split * the contents between the two nodes * @param aExistingRightNode The node to split. It will become the new * node's next sibling. * @param aOffset The offset of aExistingRightNode's * content|children to do the split at * @param aNewLeftNode The new node resulting from the split, becomes * aExistingRightNode's previous sibling. */ nsresult SplitNodeImpl(nsIContent& aExistingRightNode, int32_t aOffset, nsIContent& aNewLeftNode); /** * JoinNodes() takes 2 nodes and merge their content|children. * @param aNodeToKeep The node that will remain after the join. * @param aNodeToJoin The node that will be joined with aNodeToKeep. * There is no requirement that the two nodes be of the * same type. * @param aParent The parent of aNodeToKeep */ nsresult JoinNodesImpl(nsINode* aNodeToKeep, nsINode* aNodeToJoin, nsINode* aParent); /** * Return the offset of aChild in aParent. Asserts fatally if parent or * child is null, or parent is not child's parent. */ static int32_t GetChildOffset(nsIDOMNode* aChild, nsIDOMNode* aParent); /** * Set outOffset to the offset of aChild in the parent. * Returns the parent of aChild. */ static already_AddRefed GetNodeLocation(nsIDOMNode* aChild, int32_t* outOffset); static nsINode* GetNodeLocation(nsINode* aChild, int32_t* aOffset); /** * Returns the number of things inside aNode in the out-param aCount. * @param aNode is the node to get the length of. * If aNode is text, returns number of characters. * If not, returns number of children nodes. * @param aCount [OUT] the result of the above calculation. */ static nsresult GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount); /** * Get the node immediately prior to aCurrentNode. * @param aCurrentNode the node from which we start the search * @param aEditableNode if true, only return an editable node * @param aResultNode [OUT] the node that occurs before aCurrentNode in * the tree, skipping non-editable nodes if * aEditableNode is true. If there is no prior * node, aResultNode will be nullptr. * @param bNoBlockCrossing If true, don't move across "block" nodes, * whatever that means. */ nsIContent* GetPriorNode(nsINode* aCurrentNode, bool aEditableNode, bool aNoBlockCrossing = false); /** * And another version that takes a {parent,offset} pair rather than a node. */ nsIContent* GetPriorNode(nsINode* aParentNode, int32_t aOffset, bool aEditableNode, bool aNoBlockCrossing = false); /** * Get the node immediately after to aCurrentNode. * @param aCurrentNode the node from which we start the search * @param aEditableNode if true, only return an editable node * @param aResultNode [OUT] the node that occurs after aCurrentNode in the * tree, skipping non-editable nodes if * aEditableNode is true. If there is no prior * node, aResultNode will be nullptr. */ nsIContent* GetNextNode(nsINode* aCurrentNode, bool aEditableNode, bool bNoBlockCrossing = false); /** * And another version that takes a {parent,offset} pair rather than a node. */ nsIContent* GetNextNode(nsINode* aParentNode, int32_t aOffset, bool aEditableNode, bool aNoBlockCrossing = false); /** * Helper for GetNextNode() and GetPriorNode(). */ nsIContent* FindNode(nsINode* aCurrentNode, bool aGoForward, bool aEditableNode, bool bNoBlockCrossing); /** * Get the rightmost child of aCurrentNode; * return nullptr if aCurrentNode has no children. */ nsIContent* GetRightmostChild(nsINode* aCurrentNode, bool bNoBlockCrossing = false); /** * Get the leftmost child of aCurrentNode; * return nullptr if aCurrentNode has no children. */ nsIContent* GetLeftmostChild(nsINode *aCurrentNode, bool bNoBlockCrossing = false); /** * Returns true if aNode is of the type implied by aTag. */ static inline bool NodeIsType(nsIDOMNode* aNode, nsIAtom* aTag) { return GetTag(aNode) == aTag; } /** * Returns true if aParent can contain a child of type aTag. */ bool CanContain(nsINode& aParent, nsIContent& aChild); bool CanContainTag(nsINode& aParent, nsIAtom& aTag); bool TagCanContain(nsIAtom& aParentTag, nsIContent& aChild); virtual bool TagCanContainTag(nsIAtom& aParentTag, nsIAtom& aChildTag); /** * Returns true if aNode is our root node. */ bool IsRoot(nsIDOMNode* inNode); bool IsRoot(nsINode* inNode); bool IsEditorRoot(nsINode* aNode); /** * Returns true if aNode is a descendant of our root node. */ bool IsDescendantOfRoot(nsIDOMNode* inNode); bool IsDescendantOfRoot(nsINode* inNode); bool IsDescendantOfEditorRoot(nsINode* aNode); /** * Returns true if aNode is a container. */ virtual bool IsContainer(nsINode* aNode); virtual bool IsContainer(nsIDOMNode* aNode); /** * returns true if aNode is an editable node. */ bool IsEditable(nsIDOMNode* aNode); virtual bool IsEditable(nsINode* aNode); /** * Returns true if aNode is a MozEditorBogus node. */ bool IsMozEditorBogusNode(nsINode* aNode); /** * Counts number of editable child nodes. */ uint32_t CountEditableChildren(nsINode* aNode); /** * Find the deep first and last children. */ nsINode* GetFirstEditableNode(nsINode* aRoot); /** * Returns current composition. */ TextComposition* GetComposition() const; /** * Returns true if there is composition string and not fixed. */ bool IsIMEComposing() const; /** * Returns true when inserting text should be a part of current composition. */ bool ShouldHandleIMEComposition() const; /** * From html rules code - migration in progress. */ static nsresult GetTagString(nsIDOMNode* aNode, nsAString& outString); static nsIAtom* GetTag(nsIDOMNode* aNode); bool NodesSameType(nsIDOMNode* aNode1, nsIDOMNode* aNode2); virtual bool AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2); static bool IsTextNode(nsIDOMNode* aNode); static bool IsTextNode(nsINode* aNode); static nsCOMPtr GetChildAt(nsIDOMNode* aParent, int32_t aOffset); static nsIContent* GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode, int32_t aOffset); static nsresult GetStartNodeAndOffset(Selection* aSelection, nsIDOMNode** outStartNode, int32_t* outStartOffset); static nsresult GetStartNodeAndOffset(Selection* aSelection, nsINode** aStartNode, int32_t* aStartOffset); static nsresult GetEndNodeAndOffset(Selection* aSelection, nsIDOMNode** outEndNode, int32_t* outEndOffset); static nsresult GetEndNodeAndOffset(Selection* aSelection, nsINode** aEndNode, int32_t* aEndOffset); #if DEBUG_JOE static void DumpNode(nsIDOMNode* aNode, int32_t indent = 0); #endif Selection* GetSelection(SelectionType aSelectionType = SelectionType::eNormal); /** * Helpers to add a node to the selection. * Used by table cell selection methods. */ nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, nsIDOMNode* aEndParent, int32_t aEndOffset, nsRange** aRange); /** * Creates a range with just the supplied node and appends that to the * selection. */ nsresult AppendNodeToSelectionAsRange(nsIDOMNode *aNode); /** * When you are using AppendNodeToSelectionAsRange(), call this first to * start a new selection. */ nsresult ClearSelection(); nsresult IsPreformatted(nsIDOMNode* aNode, bool* aResult); enum class EmptyContainers { no, yes }; int32_t SplitNodeDeep(nsIContent& aNode, nsIContent& aSplitPointParent, int32_t aSplitPointOffset, EmptyContainers aEmptyContainers = EmptyContainers::yes, nsIContent** outLeftNode = nullptr, nsIContent** outRightNode = nullptr); EditorDOMPoint JoinNodeDeep(nsIContent& aLeftNode, nsIContent& aRightNode); nsresult GetString(const nsAString& name, nsAString& value); void BeginUpdateViewBatch(); virtual nsresult EndUpdateViewBatch(); bool GetShouldTxnSetSelection(); virtual nsresult HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent); nsresult HandleInlineSpellCheck(EditAction action, Selection* aSelection, nsIDOMNode* previousSelectedNode, int32_t previousSelectedOffset, nsIDOMNode* aStartNode, int32_t aStartOffset, nsIDOMNode* aEndNode, int32_t aEndOffset); virtual already_AddRefed GetDOMEventTarget() = 0; /** * Fast non-refcounting editor root element accessor */ Element* GetRoot(); /** * Likewise, but gets the editor's root instead, which is different for HTML * editors. */ virtual Element* GetEditorRoot(); /** * Likewise, but gets the text control element instead of the root for * plaintext editors. */ Element* GetExposedRoot(); /** * Accessor methods to flags. */ bool IsPlaintextEditor() const { return (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0; } bool IsSingleLineEditor() const { return (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) != 0; } bool IsPasswordEditor() const { return (mFlags & nsIPlaintextEditor::eEditorPasswordMask) != 0; } bool IsReadonly() const { return (mFlags & nsIPlaintextEditor::eEditorReadonlyMask) != 0; } bool IsDisabled() const { return (mFlags & nsIPlaintextEditor::eEditorDisabledMask) != 0; } bool IsInputFiltered() const { return (mFlags & nsIPlaintextEditor::eEditorFilterInputMask) != 0; } bool IsMailEditor() const { return (mFlags & nsIPlaintextEditor::eEditorMailMask) != 0; } bool IsWrapHackEnabled() const { return (mFlags & nsIPlaintextEditor::eEditorEnableWrapHackMask) != 0; } bool IsFormWidget() const { return (mFlags & nsIPlaintextEditor::eEditorWidgetMask) != 0; } bool NoCSS() const { return (mFlags & nsIPlaintextEditor::eEditorNoCSSMask) != 0; } bool IsInteractionAllowed() const { return (mFlags & nsIPlaintextEditor::eEditorAllowInteraction) != 0; } bool DontEchoPassword() const { return (mFlags & nsIPlaintextEditor::eEditorDontEchoPassword) != 0; } bool ShouldSkipSpellCheck() const { return (mFlags & nsIPlaintextEditor::eEditorSkipSpellCheck) != 0; } bool IsTabbable() const { return IsSingleLineEditor() || IsPasswordEditor() || IsFormWidget() || IsInteractionAllowed(); } bool HasIndependentSelection() const { return !!mSelectionControllerWeak; } /** * Get the input event target. This might return null. */ virtual already_AddRefed GetInputEventTargetContent() = 0; /** * Get the focused content, if we're focused. Returns null otherwise. */ virtual already_AddRefed GetFocusedContent(); /** * Get the focused content for the argument of some IMEStateManager's * methods. */ virtual already_AddRefed GetFocusedContentForIME(); /** * Whether the editor is active on the DOM window. Note that when this * returns true but GetFocusedContent() returns null, it means that this editor was * focused when the DOM window was active. */ virtual bool IsActiveInDOMWindow(); /** * 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); /** * 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. */ virtual already_AddRefed FindSelectionRoot(nsINode* aNode); /** * Initializes selection and caret for the editor. If aEventTarget isn't * a host of the editor, i.e., the editor doesn't get focus, this does * nothing. */ nsresult InitializeSelection(nsIDOMEventTarget* aFocusEventTarget); /** * This method has to be called by EditorEventListener::Focus. * All actions that have to be done when the editor is focused needs to be * added here. */ void OnFocus(nsIDOMEventTarget* aFocusEventTarget); /** * Used to insert content from a data transfer into the editable area. * This is called for each item in the data transfer, with the index of * each item passed as aIndex. */ virtual nsresult InsertFromDataTransfer(dom::DataTransfer* aDataTransfer, int32_t aIndex, nsIDOMDocument* aSourceDoc, nsIDOMNode* aDestinationNode, int32_t aDestOffset, bool aDoDeleteSelection) = 0; virtual nsresult InsertFromDrop(nsIDOMEvent* aDropEvent) = 0; virtual already_AddRefed FindUserSelectAllNode(nsIDOMNode* aNode) { return nullptr; } /** * GetIMESelectionStartOffsetIn() returns the start offset of IME selection in * the aTextNode. If there is no IME selection, returns -1. */ int32_t GetIMESelectionStartOffsetIn(nsINode* aTextNode); /** * FindBetterInsertionPoint() tries to look for better insertion point which * is typically the nearest text node and offset in it. */ void FindBetterInsertionPoint(nsCOMPtr& aNode, int32_t& aOffset); void FindBetterInsertionPoint(nsCOMPtr& aNode, int32_t& aOffset); /** * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent * with nsCaret::RemoveForceHide(). This does NOT set visibility of * nsCaret. Therefore, this is stateless. */ void HideCaret(bool aHide); void FlushFrames() { nsCOMPtr doc = GetDocument(); if (doc) { doc->FlushPendingNotifications(Flush_Frames); } } private: // Weak reference to the nsISelectionController. // Use GetSelectionController() to retrieve actual pointer. CachedWeakPtr mSelectionControllerWeak; // Weak reference to the nsIDocument. // Use GetDocument() to retrieve actual pointer. CachedWeakPtr mDocumentWeak; protected: enum Tristate { eTriUnset, eTriFalse, eTriTrue }; // MIME type of the doc we are editing. nsCString mContentMIMEType; nsCOMPtr mInlineSpellChecker; RefPtr mTxnMgr; // Cached root node. nsCOMPtr mRootElement; // Current IME text node. RefPtr mIMETextNode; // The form field as an event receiver. nsCOMPtr mEventTarget; nsCOMPtr mEventListener; // Weak reference to placeholder for begin/end batch purposes. WeakPtr mPlaceholderTransactionWeak; // Name of placeholder transaction. nsIAtom* mPlaceholderName; // Saved selection state for placeholder transaction batching. mozilla::UniquePtr mSelState; nsString* mPhonetic; // IME composition this is not null between compositionstart and // compositionend. RefPtr mComposition; // Listens to all low level actions on the doc. typedef AutoTArray, 5> AutoActionListenerArray; AutoActionListenerArray mActionListeners; // Just notify once per high level change. typedef AutoTArray, 3> AutoEditorObserverArray; AutoEditorObserverArray mEditorObservers; // Listen to overall doc state (dirty or not, just created, etc.). typedef AutoTArray, 1> AutoDocumentStateListenerArray; AutoDocumentStateListenerArray mDocStateListeners; // Cached selection for AutoSelectionRestorer. SelectionState mSavedSel; // Utility class object for maintaining preserved ranges. RangeUpdater mRangeUpdater; // Number of modifications (for undo/redo stack). uint32_t mModCount; // Behavior flags. See nsIPlaintextEditor.idl for the flags we use. uint32_t mFlags; int32_t mUpdateCount; // Nesting count for batching. int32_t mPlaceholderBatch; // The current editor action. EditAction mAction; // Offset in text node where IME comp string begins. uint32_t mIMETextOffset; // The Length of the composition string or commit string. If this is length // of commit string, the length is truncated by maxlength attribute. uint32_t mIMETextLength; // The current direction of editor action. EDirection mDirection; // -1 = not initialized int8_t mDocDirtyState; // A Tristate value. uint8_t mSpellcheckCheckboxState; // Turn off for conservative selection adjustment by transactions. bool mShouldTxnSetSelection; // Whether PreDestroy has been called. bool mDidPreDestroy; // Whether PostCreate has been called. bool mDidPostCreate; bool mDispatchInputEvent; // True while the instance is handling an edit action. bool mIsInEditAction; // Whether caret is hidden forcibly. bool mHidingCaret; friend bool NSCanUnload(nsISupports* serviceMgr); friend class AutoRules; friend class AutoSelectionRestorer; friend class AutoTransactionsConserveSelection; friend class RangeUpdater; }; } // namespace mozilla #endif // #ifndef mozilla_EditorBase_h