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