diff options
Diffstat (limited to 'editor/libeditor/EditorBase.h')
-rw-r--r-- | editor/libeditor/EditorBase.h | 1046 |
1 files changed, 1046 insertions, 0 deletions
diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h new file mode 100644 index 0000000000..dd4b9695e6 --- /dev/null +++ b/editor/libeditor/EditorBase.h @@ -0,0 +1,1046 @@ +/* -*- 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/dom/Text.h" +#include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr +#include "nsCycleCollectionParticipant.h" +#include "nsGkAtoms.h" +#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 nsIDOMDocument; +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 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 + +#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) + + already_AddRefed<nsIDOMDocument> GetDOMDocument(); + already_AddRefed<nsIDocument> GetDocument(); + already_AddRefed<nsIPresShell> GetPresShell(); + already_AddRefed<nsIWidget> 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<nsINode>* 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<Element> 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<Element> 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<Element> 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); + + /** + * Method to replace certain CreateElementNS() calls. + * + * @param aTag Tag you want. + */ + already_AddRefed<Element> CreateHTMLContent(nsIAtom* aTag); + + /** + * IME event handlers. + */ + virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent); + virtual nsresult UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent) = 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<ChangeAttributeTransaction> + CreateTxnForSetAttribute(Element& aElement, nsIAtom& aAttribute, + const nsAString& aValue); + + /** + * Create a transaction for removing aAttribute on aElement. Never returns + * null. + */ + already_AddRefed<ChangeAttributeTransaction> + CreateTxnForRemoveAttribute(Element& aElement, nsIAtom& aAttribute); + + /** + * Create a transaction for creating a new child node of aParent of type aTag. + */ + already_AddRefed<CreateElementTransaction> + CreateTxnForCreateElement(nsIAtom& aTag, + nsINode& aParent, + int32_t aPosition); + + already_AddRefed<Element> CreateNode(nsIAtom* aTag, nsINode* aParent, + int32_t aPosition); + + /** + * Create a transaction for inserting aNode as a child of aParent. + */ + already_AddRefed<InsertNodeTransaction> + 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<mozilla::InsertTextTransaction> + CreateTxnForInsertText(const nsAString& aStringToInsert, Text& aTextNode, + int32_t aOffset); + + /** + * Never returns null. + */ + already_AddRefed<mozilla::CompositionTransaction> + 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<DeleteTextTransaction> + CreateTxnForDeleteText(nsGenericDOMDataNode& aElement, + uint32_t aOffset, uint32_t aLength); + + already_AddRefed<DeleteTextTransaction> + CreateTxnForDeleteCharacter(nsGenericDOMDataNode& aData, uint32_t aOffset, + EDirection aDirection); + + already_AddRefed<SplitNodeTransaction> + CreateTxnForSplitNode(nsIContent& aNode, uint32_t aOffset); + + already_AddRefed<JoinNodeTransaction> + 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); + + 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<nsIDOMNode> 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<nsIDOMNode> 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<dom::EventTarget> 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 !!mSelConWeak; + } + + /** + * Get the input event target. This might return null. + */ + virtual already_AddRefed<nsIContent> GetInputEventTargetContent() = 0; + + /** + * Get the focused content, if we're focused. Returns null otherwise. + */ + virtual already_AddRefed<nsIContent> GetFocusedContent(); + + /** + * Get the focused content for the argument of some IMEStateManager's + * methods. + */ + virtual already_AddRefed<nsIContent> 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 aEvent should be handled by this editor or not. When this + * returns FALSE, The aEvent shouldn't be handled on this editor, + * i.e., The aEvent should be handled by another inner editor or ancestor + * elements. + */ + virtual bool IsAcceptableInputEvent(nsIDOMEvent* aEvent); + + /** + * 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<nsIContent> 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<nsIDOMNode> 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<nsIDOMNode>& aNode, + int32_t& aOffset); + void FindBetterInsertionPoint(nsCOMPtr<nsINode>& 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<nsIDocument> doc = GetDocument(); + if (doc) { + doc->FlushPendingNotifications(Flush_Frames); + } + } + +protected: + enum Tristate + { + eTriUnset, + eTriFalse, + eTriTrue + }; + + // MIME type of the doc we are editing. + nsCString mContentMIMEType; + + nsCOMPtr<nsIInlineSpellChecker> mInlineSpellChecker; + + RefPtr<nsTransactionManager> mTxnMgr; + // Cached root node. + nsCOMPtr<Element> mRootElement; + // Current IME text node. + RefPtr<Text> mIMETextNode; + // The form field as an event receiver. + nsCOMPtr<dom::EventTarget> mEventTarget; + nsCOMPtr<nsIDOMEventListener> mEventListener; + // Weak reference to the nsISelectionController. + nsWeakPtr mSelConWeak; + // Weak reference to placeholder for begin/end batch purposes. + nsWeakPtr mPlaceHolderTxn; + // Weak reference to the nsIDOMDocument. + nsWeakPtr mDocWeak; + // Name of placeholder transaction. + nsIAtom* mPlaceHolderName; + // Saved selection state for placeholder transaction batching. + SelectionState* mSelState; + nsString* mPhonetic; + // IME composition this is not null between compositionstart and + // compositionend. + RefPtr<TextComposition> mComposition; + + // Listens to all low level actions on the doc. + nsTArray<OwningNonNull<nsIEditActionListener>> mActionListeners; + // Just notify once per high level change. + nsTArray<OwningNonNull<nsIEditorObserver>> mEditorObservers; + // Listen to overall doc state (dirty or not, just created, etc.). + nsTArray<OwningNonNull<nsIDocumentStateListener>> 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 |