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 HTMLEditRules_h
7 #define HTMLEditRules_h
8 
9 #include "TypeInState.h"
10 #include "mozilla/EditorDOMPoint.h"  // for EditorDOMPoint
11 #include "mozilla/SelectionState.h"
12 #include "mozilla/TextEditRules.h"
13 #include "nsCOMPtr.h"
14 #include "nsIEditor.h"
15 #include "nsIHTMLEditor.h"
16 #include "nsISupportsImpl.h"
17 #include "nsTArray.h"
18 #include "nscore.h"
19 
20 class nsAtom;
21 class nsIDOMCharacterData;
22 class nsIDOMDocument;
23 class nsIDOMElement;
24 class nsIDOMNode;
25 class nsIEditor;
26 class nsINode;
27 class nsRange;
28 
29 namespace mozilla {
30 
31 class EditActionResult;
32 class HTMLEditor;
33 class RulesInfo;
34 class SplitNodeResult;
35 class TextEditor;
36 enum class EditAction : int32_t;
37 
38 namespace dom {
39 class Element;
40 class Selection;
41 }  // namespace dom
42 
43 struct StyleCache final : public PropItem {
44   bool mPresent;
45 
StyleCachefinal46   StyleCache() : PropItem(), mPresent(false) { MOZ_COUNT_CTOR(StyleCache); }
47 
StyleCachefinal48   StyleCache(nsAtom* aTag, nsAtom* aAttr, const nsAString& aValue)
49       : PropItem(aTag, aAttr, aValue), mPresent(false) {
50     MOZ_COUNT_CTOR(StyleCache);
51   }
52 
StyleCachefinal53   StyleCache(nsAtom* aTag, nsAtom* aAttr)
54       : PropItem(aTag, aAttr, EmptyString()), mPresent(false) {
55     MOZ_COUNT_CTOR(StyleCache);
56   }
57 
~StyleCachefinal58   ~StyleCache() { MOZ_COUNT_DTOR(StyleCache); }
59 };
60 
61 #define SIZE_STYLE_TABLE 19
62 
63 class HTMLEditRules : public TextEditRules {
64  public:
65   NS_DECL_ISUPPORTS_INHERITED
66   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditRules, TextEditRules)
67 
68   HTMLEditRules();
69 
70   // TextEditRules methods
71   virtual nsresult Init(TextEditor* aTextEditor) override;
72   virtual nsresult DetachEditor() override;
73   virtual nsresult BeforeEdit(EditAction aAction,
74                               nsIEditor::EDirection aDirection) override;
75   virtual nsresult AfterEdit(EditAction aAction,
76                              nsIEditor::EDirection aDirection) override;
77   virtual nsresult WillDoAction(Selection* aSelection, RulesInfo* aInfo,
78                                 bool* aCancel, bool* aHandled) override;
79   virtual nsresult DidDoAction(Selection* aSelection, RulesInfo* aInfo,
80                                nsresult aResult) override;
81   virtual bool DocumentIsEmpty() override;
82   virtual nsresult DocumentModified() override;
83 
84   nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
85   nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
86   nsresult GetIndentState(bool* aCanIndent, bool* aCanOutdent);
87   nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
88   nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
89   nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode);
90 
91   void DidCreateNode(Element* aNewElement);
92   void DidInsertNode(nsIContent& aNode);
93   void WillDeleteNode(nsINode* aChild);
94   void DidSplitNode(nsINode* aExistingRightNode, nsINode* aNewLeftNode);
95   void WillJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
96   void DidJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
97   void DidInsertText(nsINode* aTextNode, int32_t aOffset,
98                      const nsAString& aString);
99   void DidDeleteText(nsINode* aTextNode, int32_t aOffset, int32_t aLength);
100   void WillDeleteSelection(Selection* aSelection);
101 
102   void DeleteNodeIfCollapsedText(nsINode& aNode);
103 
StartToListenToEditActions()104   void StartToListenToEditActions() { mListenerEnabled = true; }
EndListeningToEditActions()105   void EndListeningToEditActions() { mListenerEnabled = false; }
106 
107  protected:
108   virtual ~HTMLEditRules();
109 
110   enum RulesEndpoint { kStart, kEnd };
111 
112   void InitFields();
113 
114   void WillInsert(Selection& aSelection, bool* aCancel);
115   nsresult WillInsertText(EditAction aAction, Selection* aSelection,
116                           bool* aCancel, bool* aHandled,
117                           const nsAString* inString, nsAString* outString,
118                           int32_t aMaxLength);
119   nsresult WillLoadHTML(Selection* aSelection, bool* aCancel);
120   nsresult WillInsertBreak(Selection& aSelection, bool* aCancel,
121                            bool* aHandled);
122 
123   /**
124    * InsertBRElement() inserts a <br> element into aInsertToBreak.
125    *
126    * @param aSelection          The selection.
127    * @param aInsertToBreak      The point where new <br> element will be
128    *                            inserted before.
129    */
130   nsresult InsertBRElement(Selection& aSelection,
131                            const EditorDOMPoint& aInsertToBreak);
132 
133   nsresult DidInsertBreak(Selection* aSelection, nsresult aResult);
134   nsresult SplitMailCites(Selection* aSelection, bool* aHandled);
135   nsresult WillDeleteSelection(Selection* aSelection,
136                                nsIEditor::EDirection aAction,
137                                nsIEditor::EStripWrappers aStripWrappers,
138                                bool* aCancel, bool* aHandled);
139   nsresult DidDeleteSelection(Selection* aSelection, nsIEditor::EDirection aDir,
140                               nsresult aResult);
141   nsresult InsertBRIfNeeded(Selection* aSelection);
142 
143   /**
144    * CanContainParagraph() returns true if aElement can have a <p> element as
145    * its child or its descendant.
146    */
147   bool CanContainParagraph(Element& aElement) const;
148 
149   /**
150    * Insert a normal <br> element or a moz-<br> element to aNode when
151    * aNode is a block and it has no children.
152    *
153    * @param aNode           Reference to a block parent.
154    * @param aInsertMozBR    true if this should insert a moz-<br> element.
155    *                        Otherwise, i.e., this should insert a normal <br>
156    *                        element, false.
157    */
158   nsresult InsertBRIfNeededInternal(nsINode& aNode, bool aInsertMozBR);
159 
160   EditorDOMPoint GetGoodSelPointForNode(nsINode& aNode,
161                                         nsIEditor::EDirection aAction);
162 
163   /**
164    * TryToJoinBlocks() tries to join two block elements.  The right element is
165    * always joined to the left element.  If the elements are the same type and
166    * not nested within each other, JoinNodesSmart() is called (example, joining
167    * two list items together into one).  If the elements are not the same type,
168    * or one is a descendant of the other, we instead destroy the right block
169    * placing its children into leftblock.  DTD containment rules are followed
170    * throughout.
171    *
172    * @return            Sets canceled to true if the operation should do
173    *                    nothing anymore even if this doesn't join the blocks.
174    *                    Sets handled to true if this actually handles the
175    *                    request.  Note that this may set it to true even if this
176    *                    does not join the block.  E.g., if the blocks shouldn't
177    *                    be joined or it's impossible to join them but it's not
178    *                    unexpected case, this returns true with this.
179    */
180   EditActionResult TryToJoinBlocks(nsIContent& aLeftNode,
181                                    nsIContent& aRightNode);
182 
183   /**
184    * MoveBlock() moves the content from aRightBlock starting from aRightOffset
185    * into aLeftBlock at aLeftOffset. Note that the "block" can be inline nodes
186    * between <br>s, or between blocks, etc.  DTD containment rules are followed
187    * throughout.
188    *
189    * @return            Sets handled to true if this actually joins the nodes.
190    *                    canceled is always false.
191    */
192   EditActionResult MoveBlock(Element& aLeftBlock, Element& aRightBlock,
193                              int32_t aLeftOffset, int32_t aRightOffset);
194 
195   /**
196    * MoveNodeSmart() moves aNode to (aDestElement, aInOutDestOffset).
197    * DTD containment rules are followed throughout.
198    *
199    * @param aOffset                 returns the point after inserted content.
200    * @return                        Sets true to handled if this actually moves
201    *                                the nodes.
202    *                                canceled is always false.
203    */
204   EditActionResult MoveNodeSmart(nsIContent& aNode, Element& aDestElement,
205                                  int32_t* aInOutDestOffset);
206 
207   /**
208    * MoveContents() moves the contents of aElement to (aDestElement,
209    * aInOutDestOffset).  DTD containment rules are followed throughout.
210    *
211    * @param aInOutDestOffset        updated to point after inserted content.
212    * @return                        Sets true to handled if this actually moves
213    *                                the nodes.
214    *                                canceled is always false.
215    */
216   EditActionResult MoveContents(Element& aElement, Element& aDestElement,
217                                 int32_t* aInOutDestOffset);
218 
219   nsresult DeleteNonTableElements(nsINode* aNode);
220   nsresult WillMakeList(Selection* aSelection, const nsAString* aListType,
221                         bool aEntireList, const nsAString* aBulletType,
222                         bool* aCancel, bool* aHandled,
223                         const nsAString* aItemType = nullptr);
224   nsresult WillRemoveList(Selection* aSelection, bool aOrdered, bool* aCancel,
225                           bool* aHandled);
226   nsresult WillIndent(Selection* aSelection, bool* aCancel, bool* aHandled);
227   nsresult WillCSSIndent(Selection* aSelection, bool* aCancel, bool* aHandled);
228   nsresult WillHTMLIndent(Selection* aSelection, bool* aCancel, bool* aHandled);
229   nsresult WillOutdent(Selection& aSelection, bool* aCancel, bool* aHandled);
230   nsresult WillAlign(Selection& aSelection, const nsAString& aAlignType,
231                      bool* aCancel, bool* aHandled);
232   nsresult WillAbsolutePosition(Selection& aSelection, bool* aCancel,
233                                 bool* aHandled);
234   nsresult WillRemoveAbsolutePosition(Selection* aSelection, bool* aCancel,
235                                       bool* aHandled);
236   nsresult WillRelativeChangeZIndex(Selection* aSelection, int32_t aChange,
237                                     bool* aCancel, bool* aHandled);
238   nsresult WillMakeDefListItem(Selection* aSelection,
239                                const nsAString* aBlockType, bool aEntireList,
240                                bool* aCancel, bool* aHandled);
241   nsresult WillMakeBasicBlock(Selection& aSelection,
242                               const nsAString& aBlockType, bool* aCancel,
243                               bool* aHandled);
244   nsresult MakeBasicBlock(Selection& aSelection, nsAtom& aBlockType);
245   nsresult DidMakeBasicBlock(Selection* aSelection, RulesInfo* aInfo,
246                              nsresult aResult);
247   nsresult DidAbsolutePosition();
248   nsresult AlignInnerBlocks(nsINode& aNode, const nsAString* alignType);
249   nsresult AlignBlockContents(nsIDOMNode* aNode, const nsAString* alignType);
250   nsresult AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
251                                   nsINode* aNode);
252   nsresult GetFormatString(nsINode* aNode, nsAString& outFormat);
253   enum class Lists { no, yes };
254   enum class Tables { no, yes };
255   void GetInnerContent(nsINode& aNode,
256                        nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
257                        int32_t* aIndex, Lists aLists = Lists::yes,
258                        Tables aTables = Tables::yes);
259   Element* IsInListItem(nsINode* aNode);
260   nsAtom& DefaultParagraphSeparator();
261   nsresult ReturnInHeader(Selection& aSelection, Element& aHeader,
262                           nsINode& aNode, int32_t aOffset);
263 
264   /**
265    * ReturnInParagraph() does the right thing for Enter key press or
266    * 'insertParagraph' command in aParentDivOrP.  aParentDivOrP will be
267    * split at start of first selection range.
268    *
269    * @param aSelection      The selection.  aParentDivOrP will be split at
270    *                        start of the first selection range.
271    * @param aParentDivOrP   The parent block.  This must be <p> or <div>
272    *                        element.
273    * @return                Returns with NS_OK if this doesn't meat any
274    *                        unexpected situation.  If this method tries to
275    *                        split the paragraph, marked as handled.
276    */
277   EditActionResult ReturnInParagraph(Selection& aSelection,
278                                      Element& aParentDivOrP);
279 
280   /**
281    * SplitParagraph() splits the parent block, aPara, at aSelNode - aOffset.
282    *
283    * @param aSelection          The selection.
284    * @param aParentDivOrP       The parent block to be split.  This must be <p>
285    *                            or <div> element.
286    * @param aStartOfRightNode   The point to be start of right node after
287    *                            split.  This must be descendant of
288    *                            aParentDivOrP.
289    * @param aNextBRNode         Next <br> node if there is.  Otherwise, nullptr.
290    *                            If this is not nullptr, the <br> node may be
291    *                            removed.
292    */
293   nsresult SplitParagraph(Selection& aSelection, Element& aParentDivOrP,
294                           const EditorRawDOMPoint& aStartOfRightNode,
295                           nsIContent* aBRNode);
296 
297   nsresult ReturnInListItem(Selection& aSelection, Element& aHeader,
298                             nsINode& aNode, int32_t aOffset);
299   nsresult AfterEditInner(EditAction action, nsIEditor::EDirection aDirection);
300   nsresult RemovePartOfBlock(Element& aBlock, nsIContent& aStartChild,
301                              nsIContent& aEndChild);
302   void SplitBlock(Element& aBlock, nsIContent& aStartChild,
303                   nsIContent& aEndChild, nsIContent** aOutLeftNode = nullptr,
304                   nsIContent** aOutRightNode = nullptr,
305                   nsIContent** aOutMiddleNode = nullptr);
306   nsresult OutdentPartOfBlock(Element& aBlock, nsIContent& aStartChild,
307                               nsIContent& aEndChild,
308                               bool aIsBlockIndentedWithCSS,
309                               nsIContent** aOutLeftNode,
310                               nsIContent** aOutRightNode);
311 
312   already_AddRefed<Element> ConvertListType(Element* aList, nsAtom* aListType,
313                                             nsAtom* aItemType);
314 
315   nsresult CreateStyleForInsertText(Selection& aSelection, nsIDocument& aDoc);
316 
317   /**
318    * IsEmptyBlockElement() returns true if aElement is a block level element
319    * and it doesn't have any visible content.
320    */
321   enum class IgnoreSingleBR { eYes, eNo };
322   bool IsEmptyBlockElement(Element& aElement, IgnoreSingleBR aIgnoreSingleBR);
323 
324   nsresult CheckForEmptyBlock(nsINode* aStartNode, Element* aBodyNode,
325                               Selection* aSelection,
326                               nsIEditor::EDirection aAction, bool* aHandled);
327   enum class BRLocation { beforeBlock, blockEnd };
328   Element* CheckForInvisibleBR(Element& aBlock, BRLocation aWhere,
329                                int32_t aOffset = 0);
330   nsresult ExpandSelectionForDeletion(Selection& aSelection);
331   bool IsFirstNode(nsIDOMNode* aNode);
332   bool IsLastNode(nsIDOMNode* aNode);
333   nsresult NormalizeSelection(Selection* aSelection);
334   EditorDOMPoint GetPromotedPoint(RulesEndpoint aWhere, nsINode& aNode,
335                                   int32_t aOffset, EditAction actionID);
336   void GetPromotedRanges(Selection& aSelection,
337                          nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
338                          EditAction inOperationType);
339   void PromoteRange(nsRange& aRange, EditAction inOperationType);
340   enum class TouchContent { no, yes };
341   nsresult GetNodesForOperation(
342       nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
343       nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
344       EditAction aOperationType,
345       TouchContent aTouchContent = TouchContent::yes);
346   void GetChildNodesForOperation(
347       nsINode& aNode, nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
348   nsresult GetNodesFromPoint(const EditorDOMPoint& aPoint,
349                              EditAction aOperation,
350                              nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
351                              TouchContent aTouchContent);
352   nsresult GetNodesFromSelection(
353       Selection& aSelection, EditAction aOperation,
354       nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
355       TouchContent aTouchContent = TouchContent::yes);
356   enum class EntireList { no, yes };
357   nsresult GetListActionNodes(
358       nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
359       EntireList aEntireList, TouchContent aTouchContent = TouchContent::yes);
360   void GetDefinitionListItemTypes(Element* aElement, bool* aDT, bool* aDD);
361   nsresult GetParagraphFormatNodes(
362       nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
363       TouchContent aTouchContent = TouchContent::yes);
364   void LookInsideDivBQandList(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
365   nsresult BustUpInlinesAtRangeEndpoints(RangeItem& inRange);
366   nsresult BustUpInlinesAtBRs(
367       nsIContent& aNode, nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes);
368   /**
369    * GetHiestInlineParent() returns the highest inline node parent between
370    * aNode and the editing host.  Even if the editing host is an inline
371    * element, this method never returns the editing host as the result.
372    */
373   nsIContent* GetHighestInlineParent(nsINode& aNode);
374   void MakeTransitionList(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
375                           nsTArray<bool>& aTransitionArray);
376   nsresult RemoveBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
377 
378   /**
379    * ApplyBlockStyle() formats all nodes in aNodeArray with block elements
380    * whose name is aBlockTag.
381    * If aNodeArray has an inline element, a block element is created and the
382    * inline element and following inline elements are moved into the new block
383    * element.
384    * If aNodeArray has <br> elements, they'll be removed from the DOM tree and
385    * new block element will be created when there are some remaining inline
386    * elements.
387    * If aNodeArray has a block element, this calls itself with children of
388    * the block element.  Then, new block element will be created when there
389    * are some remaining inline elements.
390    *
391    * @param aNodeArray      Must be descendants of a node.
392    * @param aBlockTag       The element name of new block elements.
393    */
394   nsresult ApplyBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
395                            nsAtom& aBlockTag);
396 
397   nsresult MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
398 
399   /**
400    * MaybeSplitAncestorsForInsert() does nothing if container of
401    * aStartOfDeepestRightNode can have an element whose tag name is aTag.
402    * Otherwise, looks for an ancestor node which is or is in active editing
403    * host and can have an element whose name is aTag.  If there is such
404    * ancestor, its descendants are split.
405    *
406    * Note that this may create empty elements while splitting ancestors.
407    *
408    * @param aTag                        The name of element to be inserted
409    *                                    after calling this method.
410    * @param aStartOfDeepestRightNode    The start point of deepest right node.
411    *                                    This point must be descendant of
412    *                                    active editing host.
413    * @return                            When succeeded, SplitPoint() returns
414    *                                    the point to insert the element.
415    */
416   SplitNodeResult MaybeSplitAncestorsForInsert(
417       nsAtom& aTag, const EditorRawDOMPoint& aStartOfDeepestRightNode);
418 
419   nsresult AddTerminatingBR(nsIDOMNode* aBlock);
420   EditorDOMPoint JoinNodesSmart(nsIContent& aNodeLeft, nsIContent& aNodeRight);
421   Element* GetTopEnclosingMailCite(nsINode& aNode);
422   nsresult PopListItem(nsIContent& aListItem, bool* aOutOfList = nullptr);
423   nsresult RemoveListStructure(Element& aList);
424   nsresult CacheInlineStyles(nsINode* aNode);
425   nsresult ReapplyCachedStyles();
426   void ClearCachedStyles();
427   void AdjustSpecialBreaks();
428   nsresult AdjustWhitespace(Selection* aSelection);
429   nsresult PinSelectionToNewBlock(Selection* aSelection);
430   void CheckInterlinePosition(Selection& aSelection);
431   nsresult AdjustSelection(Selection* aSelection,
432                            nsIEditor::EDirection aAction);
433 
434   /**
435    * FindNearEditableNode() tries to find an editable node near aPoint.
436    *
437    * @param aPoint      The DOM point where to start to search from.
438    * @param aDirection  If nsIEditor::ePrevious is set, this searches an
439    *                    editable node from next nodes.  Otherwise, from
440    *                    previous nodes.
441    * @return            If found, returns non-nullptr.  Otherwise, nullptr.
442    *                    Note that if found node is in different table element,
443    *                    this returns nullptr.
444    *                    And also if aDirection is not nsIEditor::ePrevious,
445    *                    the result may be the node pointed by aPoint.
446    */
447   nsIContent* FindNearEditableNode(const EditorRawDOMPoint& aPoint,
448                                    nsIEditor::EDirection aDirection);
449   /**
450    * Returns true if aNode1 or aNode2 or both is the descendant of some type of
451    * table element, but their nearest table element ancestors differ.  "Table
452    * element" here includes not just <table> but also <td>, <tbody>, <tr>, etc.
453    * The nodes count as being their own descendants for this purpose, so a
454    * table element is its own nearest table element ancestor.
455    */
456   bool InDifferentTableElements(nsIDOMNode* aNode1, nsIDOMNode* aNode2);
457   bool InDifferentTableElements(nsINode* aNode1, nsINode* aNode2);
458   nsresult RemoveEmptyNodes();
459   nsresult SelectionEndpointInNode(nsINode* aNode, bool* aResult);
460   nsresult UpdateDocChangeRange(nsRange* aRange);
461   nsresult ConfirmSelectionInBody();
462 
463   /**
464    * Insert normal <br> element into aNode when aNode is a block and it has
465    * no children.
466    */
InsertBRIfNeeded(nsINode & aNode)467   nsresult InsertBRIfNeeded(nsINode& aNode) {
468     return InsertBRIfNeededInternal(aNode, false);
469   }
470 
471   /**
472    * Insert moz-<br> element (<br type="_moz">) into aNode when aNode is a
473    * block and it has no children.
474    */
InsertMozBRIfNeeded(nsINode & aNode)475   nsresult InsertMozBRIfNeeded(nsINode& aNode) {
476     return InsertBRIfNeededInternal(aNode, true);
477   }
478 
479   bool IsEmptyInline(nsINode& aNode);
480   bool ListIsEmptyLine(nsTArray<OwningNonNull<nsINode>>& arrayOfNodes);
481   nsresult RemoveAlignment(nsINode& aNode, const nsAString& aAlignType,
482                            bool aChildrenOnly);
483   nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode, bool aStarts);
484   enum class ContentsOnly { no, yes };
485   nsresult AlignBlock(Element& aElement, const nsAString& aAlignType,
486                       ContentsOnly aContentsOnly);
487   enum class Change { minus, plus };
488   nsresult ChangeIndentation(Element& aElement, Change aChange);
489   void DocumentModifiedWorker();
490 
491   /**
492    * InitStyleCacheArray() initializes aStyleCache for usable with
493    * GetInlineStyles().
494    */
495   void InitStyleCacheArray(StyleCache aStyleCache[SIZE_STYLE_TABLE]);
496 
497   /**
498    * GetInlineStyles() retrieves the style of aNode and modifies each item of
499    * aStyleCache.
500    */
501   nsresult GetInlineStyles(nsINode* aNode,
502                            StyleCache aStyleCache[SIZE_STYLE_TABLE]);
503 
504  protected:
505   HTMLEditor* mHTMLEditor;
506   RefPtr<nsRange> mDocChangeRange;
507   bool mListenerEnabled;
508   bool mReturnInEmptyLIKillsList;
509   bool mDidDeleteSelection;
510   bool mDidRangedDelete;
511   bool mRestoreContentEditableCount;
512   RefPtr<nsRange> mUtilRange;
513   // Need to remember an int across willJoin/didJoin...
514   uint32_t mJoinOffset;
515   nsCOMPtr<Element> mNewBlock;
516   RefPtr<RangeItem> mRangeItem;
517 
518   // XXX In strict speaking, mCachedStyles isn't enough to cache inline styles
519   //     because inline style can be specified with "style" attribute and/or
520   //     CSS in <style> elements or CSS files.  So, we need to look for better
521   //     implementation about this.
522   StyleCache mCachedStyles[SIZE_STYLE_TABLE];
523 };
524 
525 }  // namespace mozilla
526 
527 #endif  // #ifndef HTMLEditRules_h
528