michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef __editor_h__ michael@0: #define __editor_h__ michael@0: michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc. michael@0: #include "mozilla/TypedEnum.h" // for MOZ_BEGIN_ENUM_CLASS, etc. michael@0: #include "nsAutoPtr.h" // for nsRefPtr michael@0: #include "nsCOMArray.h" // for nsCOMArray michael@0: #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsEditProperty.h" // for nsEditProperty, etc michael@0: #include "nsIEditor.h" // for nsIEditor::EDirection, etc michael@0: #include "nsIEditorIMESupport.h" // for NS_DECL_NSIEDITORIMESUPPORT, etc michael@0: #include "nsIObserver.h" // for NS_DECL_NSIOBSERVER, etc michael@0: #include "nsIPhonetic.h" // for NS_DECL_NSIPHONETIC, etc michael@0: #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc michael@0: #include "nsISupportsImpl.h" // for nsEditor::Release, etc michael@0: #include "nsIWeakReferenceUtils.h" // for nsWeakPtr michael@0: #include "nsLiteralString.h" // for NS_LITERAL_STRING michael@0: #include "nsSelectionState.h" // for nsRangeUpdater, etc michael@0: #include "nsString.h" // for nsCString michael@0: #include "nsWeakReference.h" // for nsSupportsWeakReference michael@0: #include "nscore.h" // for nsresult, nsAString, etc michael@0: michael@0: class AddStyleSheetTxn; michael@0: class ChangeAttributeTxn; michael@0: class CreateElementTxn; michael@0: class DeleteNodeTxn; michael@0: class DeleteTextTxn; michael@0: class EditAggregateTxn; michael@0: class IMETextTxn; michael@0: class InsertElementTxn; michael@0: class InsertTextTxn; michael@0: class JoinElementTxn; michael@0: class RemoveStyleSheetTxn; michael@0: class SplitElementTxn; michael@0: class nsCSSStyleSheet; michael@0: class nsIAtom; michael@0: class nsIContent; michael@0: class nsIDOMCharacterData; michael@0: class nsIDOMDataTransfer; michael@0: class nsIDOMDocument; michael@0: class nsIDOMElement; michael@0: class nsIDOMEvent; michael@0: class nsIDOMEventListener; michael@0: class nsIDOMEventTarget; michael@0: class nsIDOMKeyEvent; michael@0: class nsIDOMNode; michael@0: class nsIDOMRange; michael@0: class nsIDocument; michael@0: class nsIDocumentStateListener; michael@0: class nsIEditActionListener; michael@0: class nsIEditorObserver; michael@0: class nsIInlineSpellChecker; michael@0: class nsINode; michael@0: class nsIPresShell; michael@0: class nsISelection; michael@0: class nsISupports; michael@0: class nsITransaction; michael@0: class nsIWidget; michael@0: class nsRange; michael@0: class nsString; michael@0: class nsTransactionManager; michael@0: michael@0: namespace mozilla { michael@0: class TextComposition; michael@0: michael@0: namespace dom { michael@0: class DataTransfer; michael@0: class Element; michael@0: class EventTarget; michael@0: class Selection; michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: struct IMEState; michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: michael@0: #define kMOZEditorBogusNodeAttrAtom nsEditProperty::mozEditorBogusNode michael@0: #define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE") michael@0: michael@0: // This is int32_t instead of int16_t because nsIInlineSpellChecker.idl's michael@0: // spellCheckAfterEditorChange is defined to take it as a long. michael@0: MOZ_BEGIN_ENUM_CLASS(EditAction, int32_t) michael@0: ignore = -1, michael@0: none = 0, michael@0: undo, michael@0: redo, michael@0: insertNode, michael@0: createNode, michael@0: deleteNode, michael@0: splitNode, michael@0: joinNode, michael@0: deleteText = 1003, michael@0: michael@0: // text commands michael@0: insertText = 2000, michael@0: insertIMEText = 2001, michael@0: deleteSelection = 2002, michael@0: setTextProperty = 2003, michael@0: removeTextProperty = 2004, michael@0: outputText = 2005, michael@0: michael@0: // html only action michael@0: insertBreak = 3000, michael@0: makeList = 3001, michael@0: indent = 3002, michael@0: outdent = 3003, michael@0: align = 3004, michael@0: makeBasicBlock = 3005, michael@0: removeList = 3006, michael@0: makeDefListItem = 3007, michael@0: insertElement = 3008, michael@0: insertQuotation = 3009, michael@0: htmlPaste = 3012, michael@0: loadHTML = 3013, michael@0: resetTextProperties = 3014, michael@0: setAbsolutePosition = 3015, michael@0: removeAbsolutePosition = 3016, michael@0: decreaseZIndex = 3017, michael@0: increaseZIndex = 3018 michael@0: MOZ_END_ENUM_CLASS(EditAction) michael@0: michael@0: inline bool operator!(const EditAction& aOp) michael@0: { michael@0: return aOp == EditAction::none; michael@0: } michael@0: michael@0: /** implementation of an editor object. it will be the controller/focal point michael@0: * for the main editor services. i.e. the GUIManager, publishing, transaction michael@0: * manager, event interfaces. the idea for the event interfaces is to have them michael@0: * delegate the actual commands to the editor independent of the XPFE implementation. michael@0: */ michael@0: class nsEditor : public nsIEditor, michael@0: public nsIEditorIMESupport, michael@0: public nsSupportsWeakReference, michael@0: public nsIObserver, michael@0: public nsIPhonetic michael@0: { michael@0: public: michael@0: michael@0: enum IterDirection michael@0: { michael@0: kIterForward, michael@0: kIterBackward michael@0: }; michael@0: michael@0: /** The default constructor. This should suffice. the setting of the interfaces is done michael@0: * after the construction of the editor class. michael@0: */ michael@0: nsEditor(); michael@0: /** The default destructor. This should suffice. Should this be pure virtual michael@0: * for someone to derive from the nsEditor later? I don't believe so. michael@0: */ michael@0: virtual ~nsEditor(); michael@0: michael@0: //Interfaces for addref and release and queryinterface michael@0: //NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsEditor michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsEditor, michael@0: nsIEditor) michael@0: michael@0: /* ------------ utility methods -------------- */ michael@0: already_AddRefed GetDOMDocument(); michael@0: already_AddRefed GetDocument(); michael@0: already_AddRefed GetPresShell(); michael@0: already_AddRefed GetWidget(); michael@0: void NotifyEditorObservers(); michael@0: michael@0: /* ------------ nsIEditor methods -------------- */ michael@0: NS_DECL_NSIEDITOR michael@0: michael@0: /* ------------ nsIEditorIMESupport methods -------------- */ michael@0: NS_DECL_NSIEDITORIMESUPPORT michael@0: michael@0: /* ------------ nsIObserver methods -------------- */ michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: // nsIPhonetic michael@0: NS_DECL_NSIPHONETIC michael@0: michael@0: public: michael@0: michael@0: virtual bool IsModifiableNode(nsINode *aNode); michael@0: michael@0: NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert, michael@0: nsCOMPtr *aInOutNode, michael@0: int32_t *aInOutOffset, michael@0: nsIDOMDocument *aDoc); michael@0: nsresult InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, michael@0: nsIDOMCharacterData *aTextNode, michael@0: int32_t aOffset, michael@0: bool aSuppressIME = false); michael@0: NS_IMETHOD DeleteSelectionImpl(EDirection aAction, michael@0: EStripWrappers aStripWrappers); michael@0: NS_IMETHOD DeleteSelectionAndCreateNode(const nsAString& aTag, michael@0: nsIDOMNode ** aNewNode); michael@0: michael@0: /* helper routines for node/parent manipulations */ michael@0: nsresult DeleteNode(nsINode* aNode); michael@0: nsresult ReplaceContainer(nsINode* inNode, michael@0: mozilla::dom::Element** outNode, michael@0: const nsAString& aNodeType, michael@0: const nsAString* aAttribute = nullptr, michael@0: const nsAString* aValue = nullptr, michael@0: bool aCloneAttributes = false); michael@0: nsresult ReplaceContainer(nsIDOMNode *inNode, michael@0: nsCOMPtr *outNode, michael@0: const nsAString &aNodeType, michael@0: const nsAString *aAttribute = nullptr, michael@0: const nsAString *aValue = nullptr, michael@0: bool aCloneAttributes = false); michael@0: michael@0: nsresult RemoveContainer(nsINode* aNode); michael@0: nsresult RemoveContainer(nsIDOMNode *inNode); michael@0: nsresult InsertContainerAbove(nsIContent* aNode, michael@0: mozilla::dom::Element** aOutNode, michael@0: const nsAString& aNodeType, michael@0: const nsAString* aAttribute = nullptr, michael@0: const nsAString* aValue = nullptr); michael@0: nsresult InsertContainerAbove(nsIDOMNode *inNode, michael@0: nsCOMPtr *outNode, michael@0: const nsAString &aNodeType, michael@0: const nsAString *aAttribute = nullptr, michael@0: const nsAString *aValue = nullptr); michael@0: nsresult JoinNodes(nsINode* aNodeToKeep, nsIContent* aNodeToMove); michael@0: nsresult MoveNode(nsINode* aNode, nsINode* aParent, int32_t aOffset); michael@0: nsresult MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aOffset); michael@0: michael@0: /* Method to replace certain CreateElementNS() calls. michael@0: Arguments: michael@0: nsString& aTag - tag you want michael@0: nsIContent** aContent - returned Content that was created with above namespace. michael@0: */ michael@0: nsresult CreateHTMLContent(const nsAString& aTag, michael@0: mozilla::dom::Element** aContent); michael@0: michael@0: // IME event handlers michael@0: virtual nsresult BeginIMEComposition(mozilla::WidgetCompositionEvent* aEvent); michael@0: virtual nsresult UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent) = 0; michael@0: void EndIMEComposition(); michael@0: michael@0: void SwitchTextDirectionTo(uint32_t aDirection); michael@0: michael@0: protected: michael@0: nsresult DetermineCurrentDirection(); michael@0: void FireInputEvent(); michael@0: michael@0: /** create a transaction for setting aAttribute to aValue on aElement michael@0: */ michael@0: NS_IMETHOD CreateTxnForSetAttribute(nsIDOMElement *aElement, michael@0: const nsAString & aAttribute, michael@0: const nsAString & aValue, michael@0: ChangeAttributeTxn ** aTxn); michael@0: michael@0: /** create a transaction for removing aAttribute on aElement michael@0: */ michael@0: NS_IMETHOD CreateTxnForRemoveAttribute(nsIDOMElement *aElement, michael@0: const nsAString & aAttribute, michael@0: ChangeAttributeTxn ** aTxn); michael@0: michael@0: /** create a transaction for creating a new child node of aParent of type aTag. michael@0: */ michael@0: NS_IMETHOD CreateTxnForCreateElement(const nsAString & aTag, michael@0: nsIDOMNode *aParent, michael@0: int32_t aPosition, michael@0: CreateElementTxn ** aTxn); michael@0: michael@0: /** create a transaction for inserting aNode as a child of aParent. michael@0: */ michael@0: NS_IMETHOD CreateTxnForInsertElement(nsIDOMNode * aNode, michael@0: nsIDOMNode * aParent, michael@0: int32_t aOffset, michael@0: InsertElementTxn ** aTxn); michael@0: michael@0: /** create a transaction for removing aNode from its parent. michael@0: */ michael@0: nsresult CreateTxnForDeleteNode(nsINode* aNode, DeleteNodeTxn** aTxn); michael@0: michael@0: michael@0: nsresult CreateTxnForDeleteSelection(EDirection aAction, michael@0: EditAggregateTxn** aTxn, michael@0: nsINode** aNode, michael@0: int32_t* aOffset, michael@0: int32_t* aLength); michael@0: michael@0: nsresult CreateTxnForDeleteInsertionPoint(nsRange* aRange, michael@0: EDirection aAction, michael@0: EditAggregateTxn* aTxn, michael@0: nsINode** aNode, michael@0: int32_t* aOffset, michael@0: int32_t* aLength); michael@0: michael@0: michael@0: /** create a transaction for inserting aStringToInsert into aTextNode michael@0: * if aTextNode is null, the string is inserted at the current selection. michael@0: */ michael@0: NS_IMETHOD CreateTxnForInsertText(const nsAString & aStringToInsert, michael@0: nsIDOMCharacterData *aTextNode, michael@0: int32_t aOffset, michael@0: InsertTextTxn ** aTxn); michael@0: michael@0: NS_IMETHOD CreateTxnForIMEText(const nsAString & aStringToInsert, michael@0: IMETextTxn ** aTxn); michael@0: michael@0: /** create a transaction for adding a style sheet michael@0: */ michael@0: NS_IMETHOD CreateTxnForAddStyleSheet(nsCSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn); michael@0: michael@0: /** create a transaction for removing a style sheet michael@0: */ michael@0: NS_IMETHOD CreateTxnForRemoveStyleSheet(nsCSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn); michael@0: michael@0: NS_IMETHOD DeleteText(nsIDOMCharacterData *aElement, michael@0: uint32_t aOffset, michael@0: uint32_t aLength); michael@0: michael@0: // NS_IMETHOD DeleteRange(nsIDOMRange *aRange); michael@0: michael@0: nsresult CreateTxnForDeleteText(nsIDOMCharacterData* aElement, michael@0: uint32_t aOffset, michael@0: uint32_t aLength, michael@0: DeleteTextTxn** aTxn); michael@0: michael@0: nsresult CreateTxnForDeleteCharacter(nsIDOMCharacterData* aData, michael@0: uint32_t aOffset, michael@0: EDirection aDirection, michael@0: DeleteTextTxn** aTxn); michael@0: michael@0: NS_IMETHOD CreateTxnForSplitNode(nsIDOMNode *aNode, michael@0: uint32_t aOffset, michael@0: SplitElementTxn **aTxn); michael@0: michael@0: NS_IMETHOD CreateTxnForJoinNode(nsIDOMNode *aLeftNode, michael@0: nsIDOMNode *aRightNode, michael@0: JoinElementTxn **aTxn); michael@0: michael@0: /** michael@0: * This method first deletes the selection, if it's not collapsed. Then if michael@0: * the selection lies in a CharacterData node, it splits it. If the michael@0: * selection is at this point collapsed in a CharacterData node, it's michael@0: * adjusted to be collapsed right before or after the node instead (which is michael@0: * always possible, since the node was split). michael@0: */ michael@0: nsresult DeleteSelectionAndPrepareToCreateNode(); michael@0: michael@0: michael@0: // called after a transaction is done successfully michael@0: void DoAfterDoTransaction(nsITransaction *aTxn); michael@0: // called after a transaction is undone successfully michael@0: void DoAfterUndoTransaction(); michael@0: // called after a transaction is redone successfully michael@0: void DoAfterRedoTransaction(); michael@0: michael@0: typedef enum { michael@0: eDocumentCreated, michael@0: eDocumentToBeDestroyed, michael@0: eDocumentStateChanged michael@0: } TDocumentListenerNotification; michael@0: michael@0: // tell the doc state listeners that the doc state has changed michael@0: NS_IMETHOD NotifyDocumentListeners(TDocumentListenerNotification aNotificationType); michael@0: michael@0: /** make the given selection span the entire document */ michael@0: NS_IMETHOD SelectEntireDocument(nsISelection *aSelection); michael@0: michael@0: /** helper method for scrolling the selection into view after michael@0: * an edit operation. aScrollToAnchor should be true if you michael@0: * want to scroll to the point where the selection was started. michael@0: * If false, it attempts to scroll the end of the selection into view. michael@0: * michael@0: * Editor methods *should* call this method instead of the versions michael@0: * in the various selection interfaces, since this version makes sure michael@0: * that the editor's sync/async settings for reflowing, painting, and michael@0: * scrolling match. michael@0: */ michael@0: NS_IMETHOD ScrollSelectionIntoView(bool aScrollToAnchor); michael@0: michael@0: // Convenience method; forwards to IsBlockNode(nsINode*). michael@0: bool IsBlockNode(nsIDOMNode* aNode); michael@0: // stub. see comment in source. michael@0: virtual bool IsBlockNode(nsINode* aNode); michael@0: michael@0: // helper for GetPriorNode and GetNextNode michael@0: nsIContent* FindNextLeafNode(nsINode *aCurrentNode, michael@0: bool aGoForward, michael@0: bool bNoBlockCrossing); michael@0: michael@0: // install the event listeners for the editor michael@0: virtual nsresult InstallEventListeners(); michael@0: michael@0: virtual void CreateEventListeners(); michael@0: michael@0: // unregister and release our event listeners michael@0: virtual void RemoveEventListeners(); michael@0: michael@0: /** michael@0: * Return true if spellchecking should be enabled for this editor. michael@0: */ michael@0: bool GetDesiredSpellCheckState(); michael@0: michael@0: bool CanEnableSpellCheck() michael@0: { michael@0: // Check for password/readonly/disabled, which are not spellchecked michael@0: // regardless of DOM. Also, check to see if spell check should be skipped or not. michael@0: return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() && !ShouldSkipSpellCheck(); michael@0: } michael@0: michael@0: /** michael@0: * EnsureComposition() should be composition event handlers or text event michael@0: * handler. This tries to get the composition for the event and set it to michael@0: * mComposition. michael@0: */ michael@0: void EnsureComposition(mozilla::WidgetGUIEvent* aEvent); michael@0: michael@0: public: michael@0: michael@0: /** All editor operations which alter the doc should be prefaced michael@0: * with a call to StartOperation, naming the action and direction */ michael@0: NS_IMETHOD StartOperation(EditAction opID, michael@0: nsIEditor::EDirection aDirection); michael@0: michael@0: /** All editor operations which alter the doc should be followed michael@0: * with a call to EndOperation */ michael@0: NS_IMETHOD EndOperation(); michael@0: michael@0: /** routines for managing the preservation of selection across michael@0: * various editor actions */ michael@0: bool ArePreservingSelection(); michael@0: void PreserveSelectionAcrossActions(mozilla::dom::Selection* aSel); michael@0: nsresult RestorePreservedSelection(nsISelection *aSel); michael@0: void StopPreservingSelection(); michael@0: michael@0: /** michael@0: * SplitNode() creates a new node identical to an existing node, and split the contents between the two nodes michael@0: * @param aExistingRightNode the node to split. It will become the new node's next sibling. michael@0: * @param aOffset the offset of aExistingRightNode's content|children to do the split at michael@0: * @param aNewLeftNode [OUT] the new node resulting from the split, becomes aExistingRightNode's previous sibling. michael@0: * @param aParent the parent of aExistingRightNode michael@0: */ michael@0: nsresult SplitNodeImpl(nsIDOMNode *aExistingRightNode, michael@0: int32_t aOffset, michael@0: nsIDOMNode *aNewLeftNode, michael@0: nsIDOMNode *aParent); michael@0: michael@0: /** michael@0: * JoinNodes() takes 2 nodes and merge their content|children. michael@0: * @param aNodeToKeep The node that will remain after the join. michael@0: * @param aNodeToJoin The node that will be joined with aNodeToKeep. michael@0: * There is no requirement that the two nodes be of the same type. michael@0: * @param aParent The parent of aNodeToKeep michael@0: */ michael@0: nsresult JoinNodesImpl(nsINode* aNodeToKeep, michael@0: nsINode* aNodeToJoin, michael@0: nsINode* aParent); michael@0: michael@0: /** michael@0: * Return the offset of aChild in aParent. Asserts fatally if parent or michael@0: * child is null, or parent is not child's parent. michael@0: */ michael@0: static int32_t GetChildOffset(nsIDOMNode *aChild, michael@0: nsIDOMNode *aParent); michael@0: michael@0: /** michael@0: * Set outOffset to the offset of aChild in the parent. michael@0: * Returns the parent of aChild. michael@0: */ michael@0: static already_AddRefed GetNodeLocation(nsIDOMNode* aChild, michael@0: int32_t* outOffset); michael@0: static nsINode* GetNodeLocation(nsINode* aChild, int32_t* aOffset); michael@0: michael@0: /** returns the number of things inside aNode in the out-param aCount. michael@0: * @param aNode is the node to get the length of. michael@0: * If aNode is text, returns number of characters. michael@0: * If not, returns number of children nodes. michael@0: * @param aCount [OUT] the result of the above calculation. michael@0: */ michael@0: static nsresult GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount); michael@0: michael@0: /** get the node immediately prior to aCurrentNode michael@0: * @param aCurrentNode the node from which we start the search michael@0: * @param aEditableNode if true, only return an editable node michael@0: * @param aResultNode [OUT] the node that occurs before aCurrentNode in the tree, michael@0: * skipping non-editable nodes if aEditableNode is true. michael@0: * If there is no prior node, aResultNode will be nullptr. michael@0: * @param bNoBlockCrossing If true, don't move across "block" nodes, whatever that means. michael@0: */ michael@0: nsresult GetPriorNode(nsIDOMNode *aCurrentNode, michael@0: bool aEditableNode, michael@0: nsCOMPtr *aResultNode, michael@0: bool bNoBlockCrossing = false); michael@0: nsIContent* GetPriorNode(nsINode* aCurrentNode, bool aEditableNode, michael@0: bool aNoBlockCrossing = false); michael@0: michael@0: // and another version that takes a {parent,offset} pair rather than a node michael@0: nsresult GetPriorNode(nsIDOMNode *aParentNode, michael@0: int32_t aOffset, michael@0: bool aEditableNode, michael@0: nsCOMPtr *aResultNode, michael@0: bool bNoBlockCrossing = false); michael@0: nsIContent* GetPriorNode(nsINode* aParentNode, michael@0: int32_t aOffset, michael@0: bool aEditableNode, michael@0: bool aNoBlockCrossing = false); michael@0: michael@0: michael@0: /** get the node immediately after to aCurrentNode michael@0: * @param aCurrentNode the node from which we start the search michael@0: * @param aEditableNode if true, only return an editable node michael@0: * @param aResultNode [OUT] the node that occurs after aCurrentNode in the tree, michael@0: * skipping non-editable nodes if aEditableNode is true. michael@0: * If there is no prior node, aResultNode will be nullptr. michael@0: */ michael@0: nsresult GetNextNode(nsIDOMNode *aCurrentNode, michael@0: bool aEditableNode, michael@0: nsCOMPtr *aResultNode, michael@0: bool bNoBlockCrossing = false); michael@0: nsIContent* GetNextNode(nsINode* aCurrentNode, michael@0: bool aEditableNode, michael@0: bool bNoBlockCrossing = false); michael@0: michael@0: // and another version that takes a {parent,offset} pair rather than a node michael@0: nsresult GetNextNode(nsIDOMNode *aParentNode, michael@0: int32_t aOffset, michael@0: bool aEditableNode, michael@0: nsCOMPtr *aResultNode, michael@0: bool bNoBlockCrossing = false); michael@0: nsIContent* GetNextNode(nsINode* aParentNode, michael@0: int32_t aOffset, michael@0: bool aEditableNode, michael@0: bool aNoBlockCrossing = false); michael@0: michael@0: // Helper for GetNextNode and GetPriorNode michael@0: nsIContent* FindNode(nsINode *aCurrentNode, michael@0: bool aGoForward, michael@0: bool aEditableNode, michael@0: bool bNoBlockCrossing); michael@0: /** michael@0: * Get the rightmost child of aCurrentNode; michael@0: * return nullptr if aCurrentNode has no children. michael@0: */ michael@0: nsIDOMNode* GetRightmostChild(nsIDOMNode* aCurrentNode, michael@0: bool bNoBlockCrossing = false); michael@0: nsIContent* GetRightmostChild(nsINode *aCurrentNode, michael@0: bool bNoBlockCrossing = false); michael@0: michael@0: /** michael@0: * Get the leftmost child of aCurrentNode; michael@0: * return nullptr if aCurrentNode has no children. michael@0: */ michael@0: nsIDOMNode* GetLeftmostChild(nsIDOMNode* aCurrentNode, michael@0: bool bNoBlockCrossing = false); michael@0: nsIContent* GetLeftmostChild(nsINode *aCurrentNode, michael@0: bool bNoBlockCrossing = false); michael@0: michael@0: /** returns true if aNode is of the type implied by aTag */ michael@0: static inline bool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag) michael@0: { michael@0: return GetTag(aNode) == aTag; michael@0: } michael@0: michael@0: /** returns true if aParent can contain a child of type aTag */ michael@0: bool CanContain(nsIDOMNode* aParent, nsIDOMNode* aChild); michael@0: bool CanContainTag(nsIDOMNode* aParent, nsIAtom* aTag); michael@0: bool TagCanContain(nsIAtom* aParentTag, nsIDOMNode* aChild); michael@0: virtual bool TagCanContainTag(nsIAtom* aParentTag, nsIAtom* aChildTag); michael@0: michael@0: /** returns true if aNode is our root node */ michael@0: bool IsRoot(nsIDOMNode* inNode); michael@0: bool IsRoot(nsINode* inNode); michael@0: bool IsEditorRoot(nsINode* aNode); michael@0: michael@0: /** returns true if aNode is a descendant of our root node */ michael@0: bool IsDescendantOfRoot(nsIDOMNode* inNode); michael@0: bool IsDescendantOfRoot(nsINode* inNode); michael@0: bool IsDescendantOfEditorRoot(nsIDOMNode* aNode); michael@0: bool IsDescendantOfEditorRoot(nsINode* aNode); michael@0: michael@0: /** returns true if aNode is a container */ michael@0: virtual bool IsContainer(nsIDOMNode *aNode); michael@0: michael@0: /** returns true if aNode is an editable node */ michael@0: bool IsEditable(nsIDOMNode *aNode); michael@0: virtual bool IsEditable(nsIContent *aNode); michael@0: michael@0: /** returns true if aNode is a MozEditorBogus node */ michael@0: bool IsMozEditorBogusNode(nsIContent *aNode); michael@0: michael@0: /** counts number of editable child nodes */ michael@0: uint32_t CountEditableChildren(nsINode* aNode); michael@0: michael@0: /** Find the deep first and last children. */ michael@0: nsINode* GetFirstEditableNode(nsINode* aRoot); michael@0: michael@0: /** michael@0: * Returns current composition. michael@0: */ michael@0: mozilla::TextComposition* GetComposition() const; michael@0: /** michael@0: * Returns true if there is composition string and not fixed. michael@0: */ michael@0: bool IsIMEComposing() const; michael@0: michael@0: /** from html rules code - migration in progress */ michael@0: static nsresult GetTagString(nsIDOMNode *aNode, nsAString& outString); michael@0: static nsIAtom *GetTag(nsIDOMNode *aNode); michael@0: michael@0: bool NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2); michael@0: virtual bool AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2); michael@0: michael@0: static bool IsTextNode(nsIDOMNode *aNode); michael@0: static bool IsTextNode(nsINode *aNode); michael@0: michael@0: static nsCOMPtr GetChildAt(nsIDOMNode *aParent, int32_t aOffset); michael@0: static nsCOMPtr GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode, int32_t aOffset); michael@0: michael@0: static nsresult GetStartNodeAndOffset(nsISelection *aSelection, nsIDOMNode **outStartNode, int32_t *outStartOffset); michael@0: static nsresult GetStartNodeAndOffset(mozilla::dom::Selection* aSelection, michael@0: nsINode** aStartNode, michael@0: int32_t* aStartOffset); michael@0: static nsresult GetEndNodeAndOffset(nsISelection *aSelection, nsIDOMNode **outEndNode, int32_t *outEndOffset); michael@0: static nsresult GetEndNodeAndOffset(mozilla::dom::Selection* aSelection, michael@0: nsINode** aEndNode, michael@0: int32_t* aEndOffset); michael@0: #if DEBUG_JOE michael@0: static void DumpNode(nsIDOMNode *aNode, int32_t indent=0); michael@0: #endif michael@0: mozilla::dom::Selection* GetSelection(); michael@0: michael@0: // Helpers to add a node to the selection. michael@0: // Used by table cell selection methods michael@0: nsresult CreateRange(nsIDOMNode *aStartParent, int32_t aStartOffset, michael@0: nsIDOMNode *aEndParent, int32_t aEndOffset, michael@0: nsIDOMRange **aRange); michael@0: michael@0: // Creates a range with just the supplied node and appends that to the selection michael@0: nsresult AppendNodeToSelectionAsRange(nsIDOMNode *aNode); michael@0: // When you are using AppendNodeToSelectionAsRange, call this first to start a new selection michael@0: nsresult ClearSelection(); michael@0: michael@0: nsresult IsPreformatted(nsIDOMNode *aNode, bool *aResult); michael@0: michael@0: nsresult SplitNodeDeep(nsIDOMNode *aNode, michael@0: nsIDOMNode *aSplitPointParent, michael@0: int32_t aSplitPointOffset, michael@0: int32_t *outOffset, michael@0: bool aNoEmptyContainers = false, michael@0: nsCOMPtr *outLeftNode = 0, michael@0: nsCOMPtr *outRightNode = 0); michael@0: nsresult JoinNodeDeep(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsCOMPtr *aOutJoinNode, int32_t *outOffset); michael@0: michael@0: nsresult GetString(const nsAString& name, nsAString& value); michael@0: michael@0: void BeginUpdateViewBatch(void); michael@0: virtual nsresult EndUpdateViewBatch(void); michael@0: michael@0: bool GetShouldTxnSetSelection(); michael@0: michael@0: virtual nsresult HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent); michael@0: michael@0: nsresult HandleInlineSpellCheck(EditAction action, michael@0: nsISelection *aSelection, michael@0: nsIDOMNode *previousSelectedNode, michael@0: int32_t previousSelectedOffset, michael@0: nsIDOMNode *aStartNode, michael@0: int32_t aStartOffset, michael@0: nsIDOMNode *aEndNode, michael@0: int32_t aEndOffset); michael@0: michael@0: virtual already_AddRefed GetDOMEventTarget() = 0; michael@0: michael@0: // Fast non-refcounting editor root element accessor michael@0: mozilla::dom::Element *GetRoot(); michael@0: michael@0: // Likewise, but gets the editor's root instead, which is different for HTML michael@0: // editors michael@0: virtual mozilla::dom::Element* GetEditorRoot(); michael@0: michael@0: // Likewise, but gets the text control element instead of the root for michael@0: // plaintext editors michael@0: mozilla::dom::Element* GetExposedRoot(); michael@0: michael@0: // Accessor methods to flags michael@0: bool IsPlaintextEditor() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0; michael@0: } michael@0: michael@0: bool IsSingleLineEditor() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) != 0; michael@0: } michael@0: michael@0: bool IsPasswordEditor() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorPasswordMask) != 0; michael@0: } michael@0: michael@0: bool IsReadonly() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorReadonlyMask) != 0; michael@0: } michael@0: michael@0: bool IsDisabled() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorDisabledMask) != 0; michael@0: } michael@0: michael@0: bool IsInputFiltered() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorFilterInputMask) != 0; michael@0: } michael@0: michael@0: bool IsMailEditor() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorMailMask) != 0; michael@0: } michael@0: michael@0: bool IsWrapHackEnabled() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorEnableWrapHackMask) != 0; michael@0: } michael@0: michael@0: bool IsFormWidget() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorWidgetMask) != 0; michael@0: } michael@0: michael@0: bool NoCSS() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorNoCSSMask) != 0; michael@0: } michael@0: michael@0: bool IsInteractionAllowed() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorAllowInteraction) != 0; michael@0: } michael@0: michael@0: bool DontEchoPassword() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorDontEchoPassword) != 0; michael@0: } michael@0: michael@0: bool ShouldSkipSpellCheck() const michael@0: { michael@0: return (mFlags & nsIPlaintextEditor::eEditorSkipSpellCheck) != 0; michael@0: } michael@0: michael@0: bool IsTabbable() const michael@0: { michael@0: return IsSingleLineEditor() || IsPasswordEditor() || IsFormWidget() || michael@0: IsInteractionAllowed(); michael@0: } michael@0: michael@0: bool HasIndependentSelection() const michael@0: { michael@0: return !!mSelConWeak; michael@0: } michael@0: michael@0: // Get the input event target. This might return null. michael@0: virtual already_AddRefed GetInputEventTargetContent() = 0; michael@0: michael@0: // Get the focused content, if we're focused. Returns null otherwise. michael@0: virtual already_AddRefed GetFocusedContent(); michael@0: michael@0: // Get the focused content for the argument of some IMEStateManager's michael@0: // methods. michael@0: virtual already_AddRefed GetFocusedContentForIME(); michael@0: michael@0: // Whether the editor is active on the DOM window. Note that when this michael@0: // returns true but GetFocusedContent() returns null, it means that this editor was michael@0: // focused when the DOM window was active. michael@0: virtual bool IsActiveInDOMWindow(); michael@0: michael@0: // Whether the aEvent should be handled by this editor or not. When this michael@0: // returns FALSE, The aEvent shouldn't be handled on this editor, michael@0: // i.e., The aEvent should be handled by another inner editor or ancestor michael@0: // elements. michael@0: virtual bool IsAcceptableInputEvent(nsIDOMEvent* aEvent); michael@0: michael@0: // FindSelectionRoot() returns a selection root of this editor when aNode michael@0: // gets focus. aNode must be a content node or a document node. When the michael@0: // target isn't a part of this editor, returns nullptr. If this is for michael@0: // designMode, you should set the document node to aNode except that an michael@0: // element in the document has focus. michael@0: virtual already_AddRefed FindSelectionRoot(nsINode* aNode); michael@0: michael@0: // Initializes selection and caret for the editor. If aEventTarget isn't michael@0: // a host of the editor, i.e., the editor doesn't get focus, this does michael@0: // nothing. michael@0: nsresult InitializeSelection(nsIDOMEventTarget* aFocusEventTarget); michael@0: michael@0: // Finalizes selection and caret for the editor. michael@0: void FinalizeSelection(); michael@0: michael@0: // This method has to be called by nsEditorEventListener::Focus. michael@0: // All actions that have to be done when the editor is focused needs to be michael@0: // added here. michael@0: void OnFocus(nsIDOMEventTarget* aFocusEventTarget); michael@0: michael@0: // Used to insert content from a data transfer into the editable area. michael@0: // This is called for each item in the data transfer, with the index of michael@0: // each item passed as aIndex. michael@0: virtual nsresult InsertFromDataTransfer(mozilla::dom::DataTransfer *aDataTransfer, michael@0: int32_t aIndex, michael@0: nsIDOMDocument *aSourceDoc, michael@0: nsIDOMNode *aDestinationNode, michael@0: int32_t aDestOffset, michael@0: bool aDoDeleteSelection) = 0; michael@0: michael@0: virtual nsresult InsertFromDrop(nsIDOMEvent* aDropEvent) = 0; michael@0: michael@0: virtual already_AddRefed FindUserSelectAllNode(nsIDOMNode* aNode) { return nullptr; } michael@0: michael@0: protected: michael@0: enum Tristate { michael@0: eTriUnset, michael@0: eTriFalse, michael@0: eTriTrue michael@0: }; michael@0: // Spellchecking michael@0: nsCString mContentMIMEType; // MIME type of the doc we are editing. michael@0: michael@0: nsCOMPtr mInlineSpellChecker; michael@0: michael@0: nsRefPtr mTxnMgr; michael@0: nsCOMPtr mRootElement; // cached root node michael@0: nsCOMPtr mIMETextNode; // current IME text node michael@0: nsCOMPtr mEventTarget; // The form field as an event receiver michael@0: nsCOMPtr mEventListener; michael@0: nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController michael@0: nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes michael@0: nsWeakPtr mDocWeak; // weak reference to the nsIDOMDocument michael@0: nsIAtom *mPlaceHolderName; // name of placeholder transaction michael@0: nsSelectionState *mSelState; // saved selection state for placeholder txn batching michael@0: nsString *mPhonetic; michael@0: // IME composition this is not null between compositionstart and michael@0: // compositionend. michael@0: nsRefPtr mComposition; michael@0: michael@0: // various listeners michael@0: nsCOMArray mActionListeners; // listens to all low level actions on the doc michael@0: nsCOMArray mEditorObservers; // just notify once per high level change michael@0: nsCOMArray mDocStateListeners;// listen to overall doc state (dirty or not, just created, etc) michael@0: michael@0: nsSelectionState mSavedSel; // cached selection for nsAutoSelectionReset michael@0: nsRangeUpdater mRangeUpdater; // utility class object for maintaining preserved ranges michael@0: michael@0: uint32_t mModCount; // number of modifications (for undo/redo stack) michael@0: uint32_t mFlags; // behavior flags. See nsIPlaintextEditor.idl for the flags we use. michael@0: michael@0: int32_t mUpdateCount; michael@0: michael@0: int32_t mPlaceHolderBatch; // nesting count for batching michael@0: EditAction mAction; // the current editor action michael@0: michael@0: uint32_t mIMETextOffset; // offset in text node where IME comp string begins michael@0: michael@0: EDirection mDirection; // the current direction of editor action michael@0: int8_t mDocDirtyState; // -1 = not initialized michael@0: uint8_t mSpellcheckCheckboxState; // a Tristate value michael@0: michael@0: bool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns michael@0: bool mDidPreDestroy; // whether PreDestroy has been called michael@0: bool mDidPostCreate; // whether PostCreate has been called michael@0: bool mDispatchInputEvent; michael@0: michael@0: friend bool NSCanUnload(nsISupports* serviceMgr); michael@0: friend class nsAutoTxnsConserveSelection; michael@0: friend class nsAutoSelectionReset; michael@0: friend class nsAutoRules; michael@0: friend class nsRangeUpdater; michael@0: }; michael@0: michael@0: michael@0: #endif