1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef mozilla_EditorBase_h
7 #define mozilla_EditorBase_h
8 
9 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc.
10 #include "mozFlushType.h"               // for mozFlushType enum
11 #include "mozilla/OwningNonNull.h"      // for OwningNonNull
12 #include "mozilla/SelectionState.h"     // for RangeUpdater, etc.
13 #include "mozilla/StyleSheet.h"   // for StyleSheet
14 #include "mozilla/dom/Text.h"
15 #include "nsCOMPtr.h"                   // for already_AddRefed, nsCOMPtr
16 #include "nsCycleCollectionParticipant.h"
17 #include "nsGkAtoms.h"
18 #include "nsIEditor.h"                  // for nsIEditor::EDirection, etc.
19 #include "nsIEditorIMESupport.h"        // for NS_DECL_NSIEDITORIMESUPPORT, etc.
20 #include "nsIObserver.h"                // for NS_DECL_NSIOBSERVER, etc.
21 #include "nsIPhonetic.h"                // for NS_DECL_NSIPHONETIC, etc.
22 #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor, etc.
23 #include "nsISelectionController.h"     // for nsISelectionController constants
24 #include "nsISupportsImpl.h"            // for EditorBase::Release, etc.
25 #include "nsIWeakReferenceUtils.h"      // for nsWeakPtr
26 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
27 #include "nsString.h"                   // for nsCString
28 #include "nsWeakReference.h"            // for nsSupportsWeakReference
29 #include "nscore.h"                     // for nsresult, nsAString, etc.
30 
31 class nsIAtom;
32 class nsIContent;
33 class nsIDOMDocument;
34 class nsIDOMEvent;
35 class nsIDOMEventListener;
36 class nsIDOMEventTarget;
37 class nsIDOMKeyEvent;
38 class nsIDOMNode;
39 class nsIDocument;
40 class nsIDocumentStateListener;
41 class nsIEditActionListener;
42 class nsIEditorObserver;
43 class nsIInlineSpellChecker;
44 class nsINode;
45 class nsIPresShell;
46 class nsISupports;
47 class nsITransaction;
48 class nsIWidget;
49 class nsRange;
50 class nsString;
51 class nsTransactionManager;
52 
53 // This is int32_t instead of int16_t because nsIInlineSpellChecker.idl's
54 // spellCheckAfterEditorChange is defined to take it as a long.
55 // XXX EditAction causes unnecessary include of EditorBase from some places.
56 //     Why don't you move this to nsIEditor.idl?
57 enum class EditAction : int32_t
58 {
59   ignore = -1,
60   none = 0,
61   undo,
62   redo,
63   insertNode,
64   createNode,
65   deleteNode,
66   splitNode,
67   joinNode,
68   deleteText = 1003,
69 
70   // text commands
71   insertText         = 2000,
72   insertIMEText      = 2001,
73   deleteSelection    = 2002,
74   setTextProperty    = 2003,
75   removeTextProperty = 2004,
76   outputText         = 2005,
77 
78   // html only action
79   insertBreak         = 3000,
80   makeList            = 3001,
81   indent              = 3002,
82   outdent             = 3003,
83   align               = 3004,
84   makeBasicBlock      = 3005,
85   removeList          = 3006,
86   makeDefListItem     = 3007,
87   insertElement       = 3008,
88   insertQuotation     = 3009,
89   htmlPaste           = 3012,
90   loadHTML            = 3013,
91   resetTextProperties = 3014,
92   setAbsolutePosition = 3015,
93   removeAbsolutePosition = 3016,
94   decreaseZIndex      = 3017,
95   increaseZIndex      = 3018
96 };
97 
98 inline bool operator!(const EditAction& aOp)
99 {
100   return aOp == EditAction::none;
101 }
102 
103 namespace mozilla {
104 class AddStyleSheetTransaction;
105 class AutoRules;
106 class AutoSelectionRestorer;
107 class AutoTransactionsConserveSelection;
108 class ChangeAttributeTransaction;
109 class CompositionTransaction;
110 class CreateElementTransaction;
111 class DeleteNodeTransaction;
112 class DeleteTextTransaction;
113 class EditAggregateTransaction;
114 class ErrorResult;
115 class InsertNodeTransaction;
116 class InsertTextTransaction;
117 class JoinNodeTransaction;
118 class RemoveStyleSheetTransaction;
119 class SplitNodeTransaction;
120 class TextComposition;
121 struct EditorDOMPoint;
122 
123 namespace dom {
124 class DataTransfer;
125 class Element;
126 class EventTarget;
127 class Selection;
128 class Text;
129 } // namespace dom
130 
131 namespace widget {
132 struct IMEState;
133 } // namespace widget
134 
135 #define kMOZEditorBogusNodeAttrAtom nsGkAtoms::mozeditorbogusnode
136 #define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE")
137 
138 /**
139  * Implementation of an editor object.  it will be the controller/focal point
140  * for the main editor services. i.e. the GUIManager, publishing, transaction
141  * manager, event interfaces. the idea for the event interfaces is to have them
142  * delegate the actual commands to the editor independent of the XPFE
143  * implementation.
144  */
145 class EditorBase : public nsIEditor
146                  , public nsIEditorIMESupport
147                  , public nsSupportsWeakReference
148                  , public nsIPhonetic
149 {
150 public:
151   typedef dom::Element Element;
152   typedef dom::Selection Selection;
153   typedef dom::Text Text;
154 
155   enum IterDirection
156   {
157     kIterForward,
158     kIterBackward
159   };
160 
161   /**
162    * The default constructor. This should suffice. the setting of the
163    * interfaces is done after the construction of the editor class.
164    */
165   EditorBase();
166 
167 protected:
168   /**
169    * The default destructor. This should suffice. Should this be pure virtual
170    * for someone to derive from the EditorBase later? I don't believe so.
171    */
172   virtual ~EditorBase();
173 
174 public:
175   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
176   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor)
177 
178   already_AddRefed<nsIDOMDocument> GetDOMDocument();
179   already_AddRefed<nsIDocument> GetDocument();
180   already_AddRefed<nsIPresShell> GetPresShell();
181   already_AddRefed<nsIWidget> GetWidget();
182   enum NotificationForEditorObservers
183   {
184     eNotifyEditorObserversOfEnd,
185     eNotifyEditorObserversOfBefore,
186     eNotifyEditorObserversOfCancel
187   };
188   void NotifyEditorObservers(NotificationForEditorObservers aNotification);
189 
190   // nsIEditor methods
191   NS_DECL_NSIEDITOR
192 
193   // nsIEditorIMESupport methods
194   NS_DECL_NSIEDITORIMESUPPORT
195 
196   // nsIPhonetic
197   NS_DECL_NSIPHONETIC
198 
199 public:
200   virtual bool IsModifiableNode(nsINode* aNode);
201 
202   virtual nsresult InsertTextImpl(const nsAString& aStringToInsert,
203                                   nsCOMPtr<nsINode>* aInOutNode,
204                                   int32_t* aInOutOffset,
205                                   nsIDocument* aDoc);
206   nsresult InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
207                                       Text& aTextNode, int32_t aOffset,
208                                       bool aSuppressIME = false);
209   NS_IMETHOD DeleteSelectionImpl(EDirection aAction,
210                                  EStripWrappers aStripWrappers);
211 
212   already_AddRefed<Element> DeleteSelectionAndCreateElement(nsIAtom& aTag);
213 
214   /**
215    * Helper routines for node/parent manipulations.
216    */
217   nsresult DeleteNode(nsINode* aNode);
218   nsresult InsertNode(nsIContent& aNode, nsINode& aParent, int32_t aPosition);
219   enum ECloneAttributes { eDontCloneAttributes, eCloneAttributes };
220   already_AddRefed<Element> ReplaceContainer(Element* aOldContainer,
221                                              nsIAtom* aNodeType,
222                                              nsIAtom* aAttribute = nullptr,
223                                              const nsAString* aValue = nullptr,
224                                              ECloneAttributes aCloneAttributes
225                                              = eDontCloneAttributes);
226   void CloneAttributes(Element* aDest, Element* aSource);
227 
228   nsresult RemoveContainer(nsIContent* aNode);
229   already_AddRefed<Element> InsertContainerAbove(nsIContent* aNode,
230                                                  nsIAtom* aNodeType,
231                                                  nsIAtom* aAttribute = nullptr,
232                                                  const nsAString* aValue =
233                                                  nullptr);
234   nsIContent* SplitNode(nsIContent& aNode, int32_t aOffset,
235                         ErrorResult& aResult);
236   nsresult JoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
237   nsresult MoveNode(nsIContent* aNode, nsINode* aParent, int32_t aOffset);
238 
239   /**
240    * Method to replace certain CreateElementNS() calls.
241    *
242    * @param aTag        Tag you want.
243    */
244   already_AddRefed<Element> CreateHTMLContent(nsIAtom* aTag);
245 
246   /**
247    * IME event handlers.
248    */
249   virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent);
250   virtual nsresult UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent) = 0;
251   void EndIMEComposition();
252 
253   void SwitchTextDirectionTo(uint32_t aDirection);
254 
255 protected:
256   nsresult DetermineCurrentDirection();
257   void FireInputEvent();
258 
259   /**
260    * Create a transaction for setting aAttribute to aValue on aElement.  Never
261    * returns null.
262    */
263   already_AddRefed<ChangeAttributeTransaction>
264     CreateTxnForSetAttribute(Element& aElement, nsIAtom& aAttribute,
265                              const nsAString& aValue);
266 
267   /**
268    * Create a transaction for removing aAttribute on aElement.  Never returns
269    * null.
270    */
271   already_AddRefed<ChangeAttributeTransaction>
272     CreateTxnForRemoveAttribute(Element& aElement, nsIAtom& aAttribute);
273 
274   /**
275    * Create a transaction for creating a new child node of aParent of type aTag.
276    */
277   already_AddRefed<CreateElementTransaction>
278     CreateTxnForCreateElement(nsIAtom& aTag,
279                               nsINode& aParent,
280                               int32_t aPosition);
281 
282   already_AddRefed<Element> CreateNode(nsIAtom* aTag, nsINode* aParent,
283                                        int32_t aPosition);
284 
285   /**
286    * Create a transaction for inserting aNode as a child of aParent.
287    */
288   already_AddRefed<InsertNodeTransaction>
289     CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent,
290                            int32_t aOffset);
291 
292   /**
293    * Create a transaction for removing aNode from its parent.
294    */
295   nsresult CreateTxnForDeleteNode(nsINode* aNode,
296                                   DeleteNodeTransaction** aTransaction);
297 
298   nsresult CreateTxnForDeleteSelection(
299              EDirection aAction,
300              EditAggregateTransaction** aTransaction,
301              nsINode** aNode,
302              int32_t* aOffset,
303              int32_t* aLength);
304 
305   nsresult CreateTxnForDeleteInsertionPoint(
306              nsRange* aRange,
307              EDirection aAction,
308              EditAggregateTransaction* aTransaction,
309              nsINode** aNode,
310              int32_t* aOffset,
311              int32_t* aLength);
312 
313 
314   /**
315    * Create a transaction for inserting aStringToInsert into aTextNode.  Never
316    * returns null.
317    */
318   already_AddRefed<mozilla::InsertTextTransaction>
319     CreateTxnForInsertText(const nsAString& aStringToInsert, Text& aTextNode,
320                            int32_t aOffset);
321 
322   /**
323    * Never returns null.
324    */
325   already_AddRefed<mozilla::CompositionTransaction>
326     CreateTxnForComposition(const nsAString& aStringToInsert);
327 
328   /**
329    * Create a transaction for adding a style sheet.
330    */
331   NS_IMETHOD CreateTxnForAddStyleSheet(
332                StyleSheet* aSheet,
333                AddStyleSheetTransaction** aTransaction);
334 
335   /**
336    * Create a transaction for removing a style sheet.
337    */
338   NS_IMETHOD CreateTxnForRemoveStyleSheet(
339                StyleSheet* aSheet,
340                RemoveStyleSheetTransaction** aTransaction);
341 
342   nsresult DeleteText(nsGenericDOMDataNode& aElement,
343                       uint32_t aOffset, uint32_t aLength);
344 
345   already_AddRefed<DeleteTextTransaction>
346     CreateTxnForDeleteText(nsGenericDOMDataNode& aElement,
347                            uint32_t aOffset, uint32_t aLength);
348 
349   already_AddRefed<DeleteTextTransaction>
350     CreateTxnForDeleteCharacter(nsGenericDOMDataNode& aData, uint32_t aOffset,
351                                 EDirection aDirection);
352 
353   already_AddRefed<SplitNodeTransaction>
354     CreateTxnForSplitNode(nsIContent& aNode, uint32_t aOffset);
355 
356   already_AddRefed<JoinNodeTransaction>
357     CreateTxnForJoinNode(nsINode& aLeftNode, nsINode& aRightNode);
358 
359   /**
360    * This method first deletes the selection, if it's not collapsed.  Then if
361    * the selection lies in a CharacterData node, it splits it.  If the
362    * selection is at this point collapsed in a CharacterData node, it's
363    * adjusted to be collapsed right before or after the node instead (which is
364    * always possible, since the node was split).
365    */
366   nsresult DeleteSelectionAndPrepareToCreateNode();
367 
368   /**
369    * Called after a transaction is done successfully.
370    */
371   void DoAfterDoTransaction(nsITransaction *aTxn);
372 
373   /**
374    * Called after a transaction is undone successfully.
375    */
376 
377   void DoAfterUndoTransaction();
378 
379   /**
380    * Called after a transaction is redone successfully.
381    */
382   void DoAfterRedoTransaction();
383 
384   enum TDocumentListenerNotification
385   {
386     eDocumentCreated,
387     eDocumentToBeDestroyed,
388     eDocumentStateChanged
389   };
390 
391   /**
392    * Tell the doc state listeners that the doc state has changed.
393    */
394   NS_IMETHOD NotifyDocumentListeners(
395                TDocumentListenerNotification aNotificationType);
396 
397   /**
398    * Make the given selection span the entire document.
399    */
400   virtual nsresult SelectEntireDocument(Selection* aSelection);
401 
402   /**
403    * Helper method for scrolling the selection into view after
404    * an edit operation. aScrollToAnchor should be true if you
405    * want to scroll to the point where the selection was started.
406    * If false, it attempts to scroll the end of the selection into view.
407    *
408    * Editor methods *should* call this method instead of the versions
409    * in the various selection interfaces, since this version makes sure
410    * that the editor's sync/async settings for reflowing, painting, and
411    * scrolling match.
412    */
413   NS_IMETHOD ScrollSelectionIntoView(bool aScrollToAnchor);
414 
415   virtual bool IsBlockNode(nsINode* aNode);
416 
417   /**
418    * Helper for GetPriorNode() and GetNextNode().
419    */
420   nsIContent* FindNextLeafNode(nsINode* aCurrentNode,
421                                bool aGoForward,
422                                bool bNoBlockCrossing);
423 
424   virtual nsresult InstallEventListeners();
425   virtual void CreateEventListeners();
426   virtual void RemoveEventListeners();
427 
428   /**
429    * Return true if spellchecking should be enabled for this editor.
430    */
431   bool GetDesiredSpellCheckState();
432 
CanEnableSpellCheck()433   bool CanEnableSpellCheck()
434   {
435     // Check for password/readonly/disabled, which are not spellchecked
436     // regardless of DOM. Also, check to see if spell check should be skipped
437     // or not.
438     return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() &&
439            !ShouldSkipSpellCheck();
440   }
441 
442   /**
443    * EnsureComposition() should be called by composition event handlers.  This
444    * tries to get the composition for the event and set it to mComposition.
445    * However, this may fail because the composition may be committed before
446    * the event comes to the editor.
447    *
448    * @return            true if there is a composition.  Otherwise, for example,
449    *                    a composition event handler in web contents moved focus
450    *                    for committing the composition, returns false.
451    */
452   bool EnsureComposition(WidgetCompositionEvent* aCompositionEvent);
453 
454   nsresult GetSelection(SelectionType aSelectionType,
455                         nsISelection** aSelection);
456 
457 public:
458   /**
459    * All editor operations which alter the doc should be prefaced
460    * with a call to StartOperation, naming the action and direction.
461    */
462   NS_IMETHOD StartOperation(EditAction opID,
463                             nsIEditor::EDirection aDirection);
464 
465   /**
466    * All editor operations which alter the doc should be followed
467    * with a call to EndOperation.
468    */
469   NS_IMETHOD EndOperation();
470 
471   /**
472    * Routines for managing the preservation of selection across
473    * various editor actions.
474    */
475   bool ArePreservingSelection();
476   void PreserveSelectionAcrossActions(Selection* aSel);
477   nsresult RestorePreservedSelection(Selection* aSel);
478   void StopPreservingSelection();
479 
480   /**
481    * SplitNode() creates a new node identical to an existing node, and split
482    * the contents between the two nodes
483    * @param aExistingRightNode  The node to split.  It will become the new
484    *                            node's next sibling.
485    * @param aOffset             The offset of aExistingRightNode's
486    *                            content|children to do the split at
487    * @param aNewLeftNode        The new node resulting from the split, becomes
488    *                            aExistingRightNode's previous sibling.
489    */
490   nsresult SplitNodeImpl(nsIContent& aExistingRightNode,
491                          int32_t aOffset,
492                          nsIContent& aNewLeftNode);
493 
494   /**
495    * JoinNodes() takes 2 nodes and merge their content|children.
496    * @param aNodeToKeep   The node that will remain after the join.
497    * @param aNodeToJoin   The node that will be joined with aNodeToKeep.
498    *                      There is no requirement that the two nodes be of the
499    *                      same type.
500    * @param aParent       The parent of aNodeToKeep
501    */
502   nsresult JoinNodesImpl(nsINode* aNodeToKeep,
503                          nsINode* aNodeToJoin,
504                          nsINode* aParent);
505 
506   /**
507    * Return the offset of aChild in aParent.  Asserts fatally if parent or
508    * child is null, or parent is not child's parent.
509    */
510   static int32_t GetChildOffset(nsIDOMNode* aChild,
511                                 nsIDOMNode* aParent);
512 
513   /**
514    * Set outOffset to the offset of aChild in the parent.
515    * Returns the parent of aChild.
516    */
517   static already_AddRefed<nsIDOMNode> GetNodeLocation(nsIDOMNode* aChild,
518                                                       int32_t* outOffset);
519   static nsINode* GetNodeLocation(nsINode* aChild, int32_t* aOffset);
520 
521   /**
522    * Returns the number of things inside aNode in the out-param aCount.
523    * @param  aNode is the node to get the length of.
524    *         If aNode is text, returns number of characters.
525    *         If not, returns number of children nodes.
526    * @param  aCount [OUT] the result of the above calculation.
527    */
528   static nsresult GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount);
529 
530   /**
531    * Get the node immediately prior to aCurrentNode.
532    * @param aCurrentNode   the node from which we start the search
533    * @param aEditableNode  if true, only return an editable node
534    * @param aResultNode    [OUT] the node that occurs before aCurrentNode in
535    *                             the tree, skipping non-editable nodes if
536    *                             aEditableNode is true.  If there is no prior
537    *                             node, aResultNode will be nullptr.
538    * @param bNoBlockCrossing If true, don't move across "block" nodes,
539    *                         whatever that means.
540    */
541   nsIContent* GetPriorNode(nsINode* aCurrentNode, bool aEditableNode,
542                            bool aNoBlockCrossing = false);
543 
544   /**
545    * And another version that takes a {parent,offset} pair rather than a node.
546    */
547   nsIContent* GetPriorNode(nsINode* aParentNode,
548                            int32_t aOffset,
549                            bool aEditableNode,
550                            bool aNoBlockCrossing = false);
551 
552 
553   /**
554    * Get the node immediately after to aCurrentNode.
555    * @param aCurrentNode   the node from which we start the search
556    * @param aEditableNode  if true, only return an editable node
557    * @param aResultNode    [OUT] the node that occurs after aCurrentNode in the
558    *                             tree, skipping non-editable nodes if
559    *                             aEditableNode is true.  If there is no prior
560    *                             node, aResultNode will be nullptr.
561    */
562   nsIContent* GetNextNode(nsINode* aCurrentNode,
563                           bool aEditableNode,
564                           bool bNoBlockCrossing = false);
565 
566   /**
567    * And another version that takes a {parent,offset} pair rather than a node.
568    */
569   nsIContent* GetNextNode(nsINode* aParentNode,
570                           int32_t aOffset,
571                           bool aEditableNode,
572                           bool aNoBlockCrossing = false);
573 
574   /**
575    * Helper for GetNextNode() and GetPriorNode().
576    */
577   nsIContent* FindNode(nsINode* aCurrentNode,
578                        bool aGoForward,
579                        bool aEditableNode,
580                        bool bNoBlockCrossing);
581   /**
582    * Get the rightmost child of aCurrentNode;
583    * return nullptr if aCurrentNode has no children.
584    */
585   nsIContent* GetRightmostChild(nsINode* aCurrentNode,
586                                 bool bNoBlockCrossing = false);
587 
588   /**
589    * Get the leftmost child of aCurrentNode;
590    * return nullptr if aCurrentNode has no children.
591    */
592   nsIContent* GetLeftmostChild(nsINode *aCurrentNode,
593                                bool bNoBlockCrossing = false);
594 
595   /**
596    * Returns true if aNode is of the type implied by aTag.
597    */
NodeIsType(nsIDOMNode * aNode,nsIAtom * aTag)598   static inline bool NodeIsType(nsIDOMNode* aNode, nsIAtom* aTag)
599   {
600     return GetTag(aNode) == aTag;
601   }
602 
603   /**
604    * Returns true if aParent can contain a child of type aTag.
605    */
606   bool CanContain(nsINode& aParent, nsIContent& aChild);
607   bool CanContainTag(nsINode& aParent, nsIAtom& aTag);
608   bool TagCanContain(nsIAtom& aParentTag, nsIContent& aChild);
609   virtual bool TagCanContainTag(nsIAtom& aParentTag, nsIAtom& aChildTag);
610 
611   /**
612    * Returns true if aNode is our root node.
613    */
614   bool IsRoot(nsIDOMNode* inNode);
615   bool IsRoot(nsINode* inNode);
616   bool IsEditorRoot(nsINode* aNode);
617 
618   /**
619    * Returns true if aNode is a descendant of our root node.
620    */
621   bool IsDescendantOfRoot(nsIDOMNode* inNode);
622   bool IsDescendantOfRoot(nsINode* inNode);
623   bool IsDescendantOfEditorRoot(nsINode* aNode);
624 
625   /**
626    * Returns true if aNode is a container.
627    */
628   virtual bool IsContainer(nsINode* aNode);
629   virtual bool IsContainer(nsIDOMNode* aNode);
630 
631   /**
632    * returns true if aNode is an editable node.
633    */
634   bool IsEditable(nsIDOMNode* aNode);
635   virtual bool IsEditable(nsINode* aNode);
636 
637   /**
638    * Returns true if aNode is a MozEditorBogus node.
639    */
640   bool IsMozEditorBogusNode(nsINode* aNode);
641 
642   /**
643    * Counts number of editable child nodes.
644    */
645   uint32_t CountEditableChildren(nsINode* aNode);
646 
647   /**
648    * Find the deep first and last children.
649    */
650   nsINode* GetFirstEditableNode(nsINode* aRoot);
651 
652   /**
653    * Returns current composition.
654    */
655   TextComposition* GetComposition() const;
656 
657   /**
658    * Returns true if there is composition string and not fixed.
659    */
660   bool IsIMEComposing() const;
661 
662   /**
663    * Returns true when inserting text should be a part of current composition.
664    */
665   bool ShouldHandleIMEComposition() const;
666 
667   /**
668    * From html rules code - migration in progress.
669    */
670   static nsresult GetTagString(nsIDOMNode* aNode, nsAString& outString);
671   static nsIAtom* GetTag(nsIDOMNode* aNode);
672 
673   bool NodesSameType(nsIDOMNode* aNode1, nsIDOMNode* aNode2);
674   virtual bool AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2);
675 
676   static bool IsTextNode(nsIDOMNode* aNode);
677   static bool IsTextNode(nsINode* aNode);
678 
679   static nsCOMPtr<nsIDOMNode> GetChildAt(nsIDOMNode* aParent, int32_t aOffset);
680   static nsIContent* GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode,
681                                                int32_t aOffset);
682 
683   static nsresult GetStartNodeAndOffset(Selection* aSelection,
684                                         nsIDOMNode** outStartNode,
685                                         int32_t* outStartOffset);
686   static nsresult GetStartNodeAndOffset(Selection* aSelection,
687                                         nsINode** aStartNode,
688                                         int32_t* aStartOffset);
689   static nsresult GetEndNodeAndOffset(Selection* aSelection,
690                                       nsIDOMNode** outEndNode,
691                                       int32_t* outEndOffset);
692   static nsresult GetEndNodeAndOffset(Selection* aSelection,
693                                       nsINode** aEndNode,
694                                       int32_t* aEndOffset);
695 #if DEBUG_JOE
696   static void DumpNode(nsIDOMNode* aNode, int32_t indent = 0);
697 #endif
698   Selection* GetSelection(SelectionType aSelectionType =
699                                           SelectionType::eNormal);
700 
701   /**
702    * Helpers to add a node to the selection.
703    * Used by table cell selection methods.
704    */
705   nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset,
706                        nsIDOMNode* aEndParent, int32_t aEndOffset,
707                        nsRange** aRange);
708 
709   /**
710    * Creates a range with just the supplied node and appends that to the
711    * selection.
712    */
713   nsresult AppendNodeToSelectionAsRange(nsIDOMNode *aNode);
714 
715   /**
716    * When you are using AppendNodeToSelectionAsRange(), call this first to
717    * start a new selection.
718    */
719   nsresult ClearSelection();
720 
721   nsresult IsPreformatted(nsIDOMNode* aNode, bool* aResult);
722 
723   enum class EmptyContainers { no, yes };
724   int32_t SplitNodeDeep(nsIContent& aNode, nsIContent& aSplitPointParent,
725                         int32_t aSplitPointOffset,
726                         EmptyContainers aEmptyContainers =
727                           EmptyContainers::yes,
728                         nsIContent** outLeftNode = nullptr,
729                         nsIContent** outRightNode = nullptr);
730   EditorDOMPoint JoinNodeDeep(nsIContent& aLeftNode,
731                               nsIContent& aRightNode);
732 
733   nsresult GetString(const nsAString& name, nsAString& value);
734 
735   void BeginUpdateViewBatch();
736   virtual nsresult EndUpdateViewBatch();
737 
738   bool GetShouldTxnSetSelection();
739 
740   virtual nsresult HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent);
741 
742   nsresult HandleInlineSpellCheck(EditAction action,
743                                   Selection* aSelection,
744                                   nsIDOMNode* previousSelectedNode,
745                                   int32_t previousSelectedOffset,
746                                   nsIDOMNode* aStartNode,
747                                   int32_t aStartOffset,
748                                   nsIDOMNode* aEndNode,
749                                   int32_t aEndOffset);
750 
751   virtual already_AddRefed<dom::EventTarget> GetDOMEventTarget() = 0;
752 
753   /**
754    * Fast non-refcounting editor root element accessor
755    */
756   Element* GetRoot();
757 
758   /**
759    * Likewise, but gets the editor's root instead, which is different for HTML
760    * editors.
761    */
762   virtual Element* GetEditorRoot();
763 
764   /**
765    * Likewise, but gets the text control element instead of the root for
766    * plaintext editors.
767    */
768   Element* GetExposedRoot();
769 
770   /**
771    * Accessor methods to flags.
772    */
IsPlaintextEditor()773   bool IsPlaintextEditor() const
774   {
775     return (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0;
776   }
777 
IsSingleLineEditor()778   bool IsSingleLineEditor() const
779   {
780     return (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) != 0;
781   }
782 
IsPasswordEditor()783   bool IsPasswordEditor() const
784   {
785     return (mFlags & nsIPlaintextEditor::eEditorPasswordMask) != 0;
786   }
787 
IsReadonly()788   bool IsReadonly() const
789   {
790     return (mFlags & nsIPlaintextEditor::eEditorReadonlyMask) != 0;
791   }
792 
IsDisabled()793   bool IsDisabled() const
794   {
795     return (mFlags & nsIPlaintextEditor::eEditorDisabledMask) != 0;
796   }
797 
IsInputFiltered()798   bool IsInputFiltered() const
799   {
800     return (mFlags & nsIPlaintextEditor::eEditorFilterInputMask) != 0;
801   }
802 
IsMailEditor()803   bool IsMailEditor() const
804   {
805     return (mFlags & nsIPlaintextEditor::eEditorMailMask) != 0;
806   }
807 
IsWrapHackEnabled()808   bool IsWrapHackEnabled() const
809   {
810     return (mFlags & nsIPlaintextEditor::eEditorEnableWrapHackMask) != 0;
811   }
812 
IsFormWidget()813   bool IsFormWidget() const
814   {
815     return (mFlags & nsIPlaintextEditor::eEditorWidgetMask) != 0;
816   }
817 
NoCSS()818   bool NoCSS() const
819   {
820     return (mFlags & nsIPlaintextEditor::eEditorNoCSSMask) != 0;
821   }
822 
IsInteractionAllowed()823   bool IsInteractionAllowed() const
824   {
825     return (mFlags & nsIPlaintextEditor::eEditorAllowInteraction) != 0;
826   }
827 
DontEchoPassword()828   bool DontEchoPassword() const
829   {
830     return (mFlags & nsIPlaintextEditor::eEditorDontEchoPassword) != 0;
831   }
832 
ShouldSkipSpellCheck()833   bool ShouldSkipSpellCheck() const
834   {
835     return (mFlags & nsIPlaintextEditor::eEditorSkipSpellCheck) != 0;
836   }
837 
IsTabbable()838   bool IsTabbable() const
839   {
840     return IsSingleLineEditor() || IsPasswordEditor() || IsFormWidget() ||
841            IsInteractionAllowed();
842   }
843 
HasIndependentSelection()844   bool HasIndependentSelection() const
845   {
846     return !!mSelConWeak;
847   }
848 
849   /**
850    * Get the input event target. This might return null.
851    */
852   virtual already_AddRefed<nsIContent> GetInputEventTargetContent() = 0;
853 
854   /**
855    * Get the focused content, if we're focused.  Returns null otherwise.
856    */
857   virtual already_AddRefed<nsIContent> GetFocusedContent();
858 
859   /**
860    * Get the focused content for the argument of some IMEStateManager's
861    * methods.
862    */
863   virtual already_AddRefed<nsIContent> GetFocusedContentForIME();
864 
865   /**
866    * Whether the editor is active on the DOM window.  Note that when this
867    * returns true but GetFocusedContent() returns null, it means that this editor was
868    * focused when the DOM window was active.
869    */
870   virtual bool IsActiveInDOMWindow();
871 
872   /**
873    * Whether the aEvent should be handled by this editor or not.  When this
874    * returns FALSE, The aEvent shouldn't be handled on this editor,
875    * i.e., The aEvent should be handled by another inner editor or ancestor
876    * elements.
877    */
878   virtual bool IsAcceptableInputEvent(nsIDOMEvent* aEvent);
879 
880   /**
881    * FindSelectionRoot() returns a selection root of this editor when aNode
882    * gets focus.  aNode must be a content node or a document node.  When the
883    * target isn't a part of this editor, returns nullptr.  If this is for
884    * designMode, you should set the document node to aNode except that an
885    * element in the document has focus.
886    */
887   virtual already_AddRefed<nsIContent> FindSelectionRoot(nsINode* aNode);
888 
889   /**
890    * Initializes selection and caret for the editor.  If aEventTarget isn't
891    * a host of the editor, i.e., the editor doesn't get focus, this does
892    * nothing.
893    */
894   nsresult InitializeSelection(nsIDOMEventTarget* aFocusEventTarget);
895 
896   /**
897    * This method has to be called by EditorEventListener::Focus.
898    * All actions that have to be done when the editor is focused needs to be
899    * added here.
900    */
901   void OnFocus(nsIDOMEventTarget* aFocusEventTarget);
902 
903   /**
904    * Used to insert content from a data transfer into the editable area.
905    * This is called for each item in the data transfer, with the index of
906    * each item passed as aIndex.
907    */
908   virtual nsresult InsertFromDataTransfer(dom::DataTransfer* aDataTransfer,
909                                           int32_t aIndex,
910                                           nsIDOMDocument* aSourceDoc,
911                                           nsIDOMNode* aDestinationNode,
912                                           int32_t aDestOffset,
913                                           bool aDoDeleteSelection) = 0;
914 
915   virtual nsresult InsertFromDrop(nsIDOMEvent* aDropEvent) = 0;
916 
FindUserSelectAllNode(nsIDOMNode * aNode)917   virtual already_AddRefed<nsIDOMNode> FindUserSelectAllNode(nsIDOMNode* aNode)
918   {
919     return nullptr;
920   }
921 
922   /**
923    * GetIMESelectionStartOffsetIn() returns the start offset of IME selection in
924    * the aTextNode.  If there is no IME selection, returns -1.
925    */
926   int32_t GetIMESelectionStartOffsetIn(nsINode* aTextNode);
927 
928   /**
929    * FindBetterInsertionPoint() tries to look for better insertion point which
930    * is typically the nearest text node and offset in it.
931    */
932   void FindBetterInsertionPoint(nsCOMPtr<nsIDOMNode>& aNode,
933                                 int32_t& aOffset);
934   void FindBetterInsertionPoint(nsCOMPtr<nsINode>& aNode,
935                                 int32_t& aOffset);
936 
937   /**
938    * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
939    * with nsCaret::RemoveForceHide().  This does NOT set visibility of
940    * nsCaret.  Therefore, this is stateless.
941    */
942   void HideCaret(bool aHide);
943 
FlushFrames()944   void FlushFrames()
945   {
946     nsCOMPtr<nsIDocument> doc = GetDocument();
947     if (doc) {
948       doc->FlushPendingNotifications(Flush_Frames);
949     }
950   }
951 
952 protected:
953   enum Tristate
954   {
955     eTriUnset,
956     eTriFalse,
957     eTriTrue
958   };
959 
960   // MIME type of the doc we are editing.
961   nsCString mContentMIMEType;
962 
963   nsCOMPtr<nsIInlineSpellChecker> mInlineSpellChecker;
964 
965   RefPtr<nsTransactionManager> mTxnMgr;
966   // Cached root node.
967   nsCOMPtr<Element> mRootElement;
968   // Current IME text node.
969   RefPtr<Text> mIMETextNode;
970   // The form field as an event receiver.
971   nsCOMPtr<dom::EventTarget> mEventTarget;
972   nsCOMPtr<nsIDOMEventListener> mEventListener;
973   // Weak reference to the nsISelectionController.
974   nsWeakPtr mSelConWeak;
975   // Weak reference to placeholder for begin/end batch purposes.
976   nsWeakPtr mPlaceHolderTxn;
977   // Weak reference to the nsIDOMDocument.
978   nsWeakPtr mDocWeak;
979   // Name of placeholder transaction.
980   nsIAtom* mPlaceHolderName;
981   // Saved selection state for placeholder transaction batching.
982   SelectionState* mSelState;
983   nsString* mPhonetic;
984   // IME composition this is not null between compositionstart and
985   // compositionend.
986   RefPtr<TextComposition> mComposition;
987 
988   // Listens to all low level actions on the doc.
989   nsTArray<OwningNonNull<nsIEditActionListener>> mActionListeners;
990   // Just notify once per high level change.
991   nsTArray<OwningNonNull<nsIEditorObserver>> mEditorObservers;
992   // Listen to overall doc state (dirty or not, just created, etc.).
993   nsTArray<OwningNonNull<nsIDocumentStateListener>> mDocStateListeners;
994 
995   // Cached selection for AutoSelectionRestorer.
996   SelectionState mSavedSel;
997   // Utility class object for maintaining preserved ranges.
998   RangeUpdater mRangeUpdater;
999 
1000   // Number of modifications (for undo/redo stack).
1001   uint32_t mModCount;
1002   // Behavior flags. See nsIPlaintextEditor.idl for the flags we use.
1003   uint32_t mFlags;
1004 
1005   int32_t mUpdateCount;
1006 
1007   // Nesting count for batching.
1008   int32_t mPlaceHolderBatch;
1009   // The current editor action.
1010   EditAction mAction;
1011 
1012   // Offset in text node where IME comp string begins.
1013   uint32_t mIMETextOffset;
1014   // The Length of the composition string or commit string.  If this is length
1015   // of commit string, the length is truncated by maxlength attribute.
1016   uint32_t mIMETextLength;
1017 
1018   // The current direction of editor action.
1019   EDirection mDirection;
1020   // -1 = not initialized
1021   int8_t mDocDirtyState;
1022   // A Tristate value.
1023   uint8_t mSpellcheckCheckboxState;
1024 
1025   // Turn off for conservative selection adjustment by transactions.
1026   bool mShouldTxnSetSelection;
1027   // Whether PreDestroy has been called.
1028   bool mDidPreDestroy;
1029   // Whether PostCreate has been called.
1030   bool mDidPostCreate;
1031   bool mDispatchInputEvent;
1032   // True while the instance is handling an edit action.
1033   bool mIsInEditAction;
1034   // Whether caret is hidden forcibly.
1035   bool mHidingCaret;
1036 
1037   friend bool NSCanUnload(nsISupports* serviceMgr);
1038   friend class AutoRules;
1039   friend class AutoSelectionRestorer;
1040   friend class AutoTransactionsConserveSelection;
1041   friend class RangeUpdater;
1042 };
1043 
1044 } // namespace mozilla
1045 
1046 #endif // #ifndef mozilla_EditorBase_h
1047