Line data Source code
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/SelectionState.h"
11 : #include "mozilla/TextEditRules.h"
12 : #include "nsCOMPtr.h"
13 : #include "nsIEditActionListener.h"
14 : #include "nsIEditor.h"
15 : #include "nsIHTMLEditor.h"
16 : #include "nsISupportsImpl.h"
17 : #include "nsTArray.h"
18 : #include "nscore.h"
19 :
20 : class nsIAtom;
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 TextEditor;
35 : struct EditorDOMPoint;
36 : namespace dom {
37 : class Element;
38 : class Selection;
39 : } // namespace dom
40 :
41 0 : struct StyleCache final : public PropItem
42 : {
43 : bool mPresent;
44 :
45 0 : StyleCache()
46 0 : : PropItem()
47 0 : , mPresent(false)
48 : {
49 0 : MOZ_COUNT_CTOR(StyleCache);
50 0 : }
51 :
52 : StyleCache(nsIAtom* aTag,
53 : const nsAString& aAttr,
54 : const nsAString& aValue)
55 : : PropItem(aTag, aAttr, aValue)
56 : , mPresent(false)
57 : {
58 : MOZ_COUNT_CTOR(StyleCache);
59 : }
60 :
61 0 : StyleCache(nsIAtom* aTag,
62 : const nsAString& aAttr)
63 0 : : PropItem(aTag, aAttr, EmptyString())
64 0 : , mPresent(false)
65 : {
66 0 : MOZ_COUNT_CTOR(StyleCache);
67 0 : }
68 :
69 0 : ~StyleCache()
70 0 : {
71 0 : MOZ_COUNT_DTOR(StyleCache);
72 0 : }
73 : };
74 :
75 : #define SIZE_STYLE_TABLE 19
76 :
77 : class HTMLEditRules : public TextEditRules
78 : , public nsIEditActionListener
79 : {
80 : public:
81 : NS_DECL_ISUPPORTS_INHERITED
82 0 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditRules, TextEditRules)
83 :
84 : HTMLEditRules();
85 :
86 : // nsIEditRules methods
87 : NS_IMETHOD Init(TextEditor* aTextEditor) override;
88 : NS_IMETHOD DetachEditor() override;
89 : NS_IMETHOD BeforeEdit(EditAction action,
90 : nsIEditor::EDirection aDirection) override;
91 : NS_IMETHOD AfterEdit(EditAction action,
92 : nsIEditor::EDirection aDirection) override;
93 : NS_IMETHOD WillDoAction(Selection* aSelection, RulesInfo* aInfo,
94 : bool* aCancel, bool* aHandled) override;
95 : NS_IMETHOD DidDoAction(Selection* aSelection, RulesInfo* aInfo,
96 : nsresult aResult) override;
97 : NS_IMETHOD_(bool) DocumentIsEmpty() override;
98 : NS_IMETHOD DocumentModified() override;
99 :
100 : nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
101 : nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
102 : nsresult GetIndentState(bool* aCanIndent, bool* aCanOutdent);
103 : nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
104 : nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
105 : nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode);
106 :
107 : // nsIEditActionListener methods
108 :
109 : NS_IMETHOD WillCreateNode(const nsAString& aTag, nsIDOMNode* aParent,
110 : int32_t aPosition) override;
111 : NS_IMETHOD DidCreateNode(const nsAString& aTag, nsIDOMNode* aNode,
112 : nsIDOMNode* aParent, int32_t aPosition,
113 : nsresult aResult) override;
114 : NS_IMETHOD WillInsertNode(nsIDOMNode* aNode, nsIDOMNode* aParent,
115 : int32_t aPosition) override;
116 : NS_IMETHOD DidInsertNode(nsIDOMNode* aNode, nsIDOMNode* aParent,
117 : int32_t aPosition, nsresult aResult) override;
118 : NS_IMETHOD WillDeleteNode(nsIDOMNode* aChild) override;
119 : NS_IMETHOD DidDeleteNode(nsIDOMNode* aChild, nsresult aResult) override;
120 : NS_IMETHOD WillSplitNode(nsIDOMNode* aExistingRightNode,
121 : int32_t aOffset) override;
122 : NS_IMETHOD DidSplitNode(nsIDOMNode* aExistingRightNode, int32_t aOffset,
123 : nsIDOMNode* aNewLeftNode, nsresult aResult) override;
124 : NS_IMETHOD WillJoinNodes(nsIDOMNode* aLeftNode, nsIDOMNode* aRightNode,
125 : nsIDOMNode* aParent) override;
126 : NS_IMETHOD DidJoinNodes(nsIDOMNode* aLeftNode, nsIDOMNode* aRightNode,
127 : nsIDOMNode* aParent, nsresult aResult) override;
128 : NS_IMETHOD WillInsertText(nsIDOMCharacterData* aTextNode, int32_t aOffset,
129 : const nsAString &aString) override;
130 : NS_IMETHOD DidInsertText(nsIDOMCharacterData* aTextNode, int32_t aOffset,
131 : const nsAString &aString, nsresult aResult) override;
132 : NS_IMETHOD WillDeleteText(nsIDOMCharacterData* aTextNode, int32_t aOffset,
133 : int32_t aLength) override;
134 : NS_IMETHOD DidDeleteText(nsIDOMCharacterData* aTextNode, int32_t aOffset,
135 : int32_t aLength, nsresult aResult) override;
136 : NS_IMETHOD WillDeleteSelection(nsISelection* aSelection) override;
137 : NS_IMETHOD DidDeleteSelection(nsISelection* aSelection) override;
138 : void DeleteNodeIfCollapsedText(nsINode& aNode);
139 :
140 : protected:
141 : virtual ~HTMLEditRules();
142 :
143 : enum RulesEndpoint
144 : {
145 : kStart,
146 : kEnd
147 : };
148 :
149 : void InitFields();
150 :
151 : void WillInsert(Selection& aSelection, bool* aCancel);
152 : nsresult WillInsertText(EditAction aAction,
153 : Selection* aSelection,
154 : bool* aCancel,
155 : bool* aHandled,
156 : const nsAString* inString,
157 : nsAString* outString,
158 : int32_t aMaxLength);
159 : nsresult WillLoadHTML(Selection* aSelection, bool* aCancel);
160 : nsresult WillInsertBreak(Selection& aSelection, bool* aCancel,
161 : bool* aHandled);
162 : nsresult StandardBreakImpl(nsINode& aNode, int32_t aOffset,
163 : Selection& aSelection);
164 : nsresult DidInsertBreak(Selection* aSelection, nsresult aResult);
165 : nsresult SplitMailCites(Selection* aSelection, bool* aHandled);
166 : nsresult WillDeleteSelection(Selection* aSelection,
167 : nsIEditor::EDirection aAction,
168 : nsIEditor::EStripWrappers aStripWrappers,
169 : bool* aCancel, bool* aHandled);
170 : nsresult DidDeleteSelection(Selection* aSelection,
171 : nsIEditor::EDirection aDir,
172 : nsresult aResult);
173 : nsresult InsertBRIfNeeded(Selection* aSelection);
174 : mozilla::EditorDOMPoint GetGoodSelPointForNode(nsINode& aNode,
175 : nsIEditor::EDirection aAction);
176 :
177 : /**
178 : * TryToJoinBlocks() tries to join two block elements. The right element is
179 : * always joined to the left element. If the elements are the same type and
180 : * not nested within each other, JoinNodesSmart() is called (example, joining
181 : * two list items together into one). If the elements are not the same type,
182 : * or one is a descendant of the other, we instead destroy the right block
183 : * placing its children into leftblock. DTD containment rules are followed
184 : * throughout.
185 : *
186 : * @return Sets canceled to true if the operation should do
187 : * nothing anymore even if this doesn't join the blocks.
188 : * Sets handled to true if this actually handles the
189 : * request. Note that this may set it to true even if this
190 : * does not join the block. E.g., if the blocks shouldn't
191 : * be joined or it's impossible to join them but it's not
192 : * unexpected case, this returns true with this.
193 : */
194 : EditActionResult TryToJoinBlocks(nsIContent& aLeftNode,
195 : nsIContent& aRightNode);
196 :
197 : /**
198 : * MoveBlock() moves the content from aRightBlock starting from aRightOffset
199 : * into aLeftBlock at aLeftOffset. Note that the "block" can be inline nodes
200 : * between <br>s, or between blocks, etc. DTD containment rules are followed
201 : * throughout.
202 : *
203 : * @return Sets handled to true if this actually joins the nodes.
204 : * canceled is always false.
205 : */
206 : EditActionResult MoveBlock(Element& aLeftBlock, Element& aRightBlock,
207 : int32_t aLeftOffset, int32_t aRightOffset);
208 :
209 : /**
210 : * MoveNodeSmart() moves aNode to (aDestElement, aInOutDestOffset).
211 : * DTD containment rules are followed throughout.
212 : *
213 : * @param aOffset returns the point after inserted content.
214 : * @return Sets true to handled if this actually moves
215 : * the nodes.
216 : * canceled is always false.
217 : */
218 : EditActionResult MoveNodeSmart(nsIContent& aNode, Element& aDestElement,
219 : int32_t* aInOutDestOffset);
220 :
221 : /**
222 : * MoveContents() moves the contents of aElement to (aDestElement,
223 : * aInOutDestOffset). DTD containment rules are followed throughout.
224 : *
225 : * @param aInOutDestOffset updated to point after inserted content.
226 : * @return Sets true to handled if this actually moves
227 : * the nodes.
228 : * canceled is always false.
229 : */
230 : EditActionResult MoveContents(Element& aElement, Element& aDestElement,
231 : int32_t* aInOutDestOffset);
232 :
233 : nsresult DeleteNonTableElements(nsINode* aNode);
234 : nsresult WillMakeList(Selection* aSelection,
235 : const nsAString* aListType,
236 : bool aEntireList,
237 : const nsAString* aBulletType,
238 : bool* aCancel, bool* aHandled,
239 : const nsAString* aItemType = nullptr);
240 : nsresult WillRemoveList(Selection* aSelection, bool aOrdered, bool* aCancel,
241 : bool* aHandled);
242 : nsresult WillIndent(Selection* aSelection, bool* aCancel, bool* aHandled);
243 : nsresult WillCSSIndent(Selection* aSelection, bool* aCancel, bool* aHandled);
244 : nsresult WillHTMLIndent(Selection* aSelection, bool* aCancel,
245 : bool* aHandled);
246 : nsresult WillOutdent(Selection& aSelection, bool* aCancel, bool* aHandled);
247 : nsresult WillAlign(Selection& aSelection, const nsAString& aAlignType,
248 : bool* aCancel, bool* aHandled);
249 : nsresult WillAbsolutePosition(Selection& aSelection, bool* aCancel,
250 : bool* aHandled);
251 : nsresult WillRemoveAbsolutePosition(Selection* aSelection, bool* aCancel,
252 : bool* aHandled);
253 : nsresult WillRelativeChangeZIndex(Selection* aSelection, int32_t aChange,
254 : bool* aCancel, bool* aHandled);
255 : nsresult WillMakeDefListItem(Selection* aSelection,
256 : const nsAString* aBlockType, bool aEntireList,
257 : bool* aCancel, bool* aHandled);
258 : nsresult WillMakeBasicBlock(Selection& aSelection,
259 : const nsAString& aBlockType,
260 : bool* aCancel, bool* aHandled);
261 : nsresult MakeBasicBlock(Selection& aSelection, nsIAtom& aBlockType);
262 : nsresult DidMakeBasicBlock(Selection* aSelection, RulesInfo* aInfo,
263 : nsresult aResult);
264 : nsresult DidAbsolutePosition();
265 : nsresult AlignInnerBlocks(nsINode& aNode, const nsAString* alignType);
266 : nsresult AlignBlockContents(nsIDOMNode* aNode, const nsAString* alignType);
267 : nsresult AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
268 : nsINode* aNode);
269 : nsresult GetFormatString(nsIDOMNode* aNode, nsAString &outFormat);
270 : enum class Lists { no, yes };
271 : enum class Tables { no, yes };
272 : void GetInnerContent(nsINode& aNode,
273 : nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
274 : int32_t* aIndex, Lists aLists = Lists::yes,
275 : Tables aTables = Tables::yes);
276 : Element* IsInListItem(nsINode* aNode);
277 : nsIAtom& DefaultParagraphSeparator();
278 : nsresult ReturnInHeader(Selection& aSelection, Element& aHeader,
279 : nsINode& aNode, int32_t aOffset);
280 : nsresult ReturnInParagraph(Selection* aSelection, nsIDOMNode* aHeader,
281 : nsIDOMNode* aTextNode, int32_t aOffset,
282 : bool* aCancel, bool* aHandled);
283 : nsresult SplitParagraph(nsIDOMNode* aPara,
284 : nsIContent* aBRNode,
285 : Selection* aSelection,
286 : nsCOMPtr<nsIDOMNode>* aSelNode,
287 : int32_t* aOffset);
288 : nsresult ReturnInListItem(Selection& aSelection, Element& aHeader,
289 : nsINode& aNode, int32_t aOffset);
290 : nsresult AfterEditInner(EditAction action,
291 : nsIEditor::EDirection aDirection);
292 : nsresult RemovePartOfBlock(Element& aBlock, nsIContent& aStartChild,
293 : nsIContent& aEndChild);
294 : void SplitBlock(Element& aBlock,
295 : nsIContent& aStartChild,
296 : nsIContent& aEndChild,
297 : nsIContent** aOutLeftNode = nullptr,
298 : nsIContent** aOutRightNode = nullptr,
299 : nsIContent** aOutMiddleNode = nullptr);
300 : nsresult OutdentPartOfBlock(Element& aBlock,
301 : nsIContent& aStartChild,
302 : nsIContent& aEndChild,
303 : bool aIsBlockIndentedWithCSS,
304 : nsIContent** aOutLeftNode,
305 : nsIContent** aOutRightNode);
306 :
307 : nsresult ConvertListType(Element* aList, Element** aOutList,
308 : nsIAtom* aListType, nsIAtom* aItemType);
309 :
310 : nsresult CreateStyleForInsertText(Selection& aSelection, nsIDocument& aDoc);
311 : enum class MozBRCounts { yes, no };
312 : nsresult IsEmptyBlock(Element& aNode, bool* aOutIsEmptyBlock,
313 : MozBRCounts aMozBRCounts = MozBRCounts::yes);
314 : nsresult CheckForEmptyBlock(nsINode* aStartNode, Element* aBodyNode,
315 : Selection* aSelection,
316 : nsIEditor::EDirection aAction, bool* aHandled);
317 : enum class BRLocation { beforeBlock, blockEnd };
318 : Element* CheckForInvisibleBR(Element& aBlock, BRLocation aWhere,
319 : int32_t aOffset = 0);
320 : nsresult ExpandSelectionForDeletion(Selection& aSelection);
321 : bool IsFirstNode(nsIDOMNode* aNode);
322 : bool IsLastNode(nsIDOMNode* aNode);
323 : nsresult NormalizeSelection(Selection* aSelection);
324 : EditorDOMPoint GetPromotedPoint(RulesEndpoint aWhere, nsINode& aNode,
325 : int32_t aOffset, EditAction actionID);
326 : void GetPromotedRanges(Selection& aSelection,
327 : nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
328 : EditAction inOperationType);
329 : void PromoteRange(nsRange& aRange, EditAction inOperationType);
330 : enum class TouchContent { no, yes };
331 : nsresult GetNodesForOperation(
332 : nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
333 : nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
334 : EditAction aOperationType,
335 : TouchContent aTouchContent = TouchContent::yes);
336 : void GetChildNodesForOperation(
337 : nsINode& aNode,
338 : nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
339 : nsresult GetNodesFromPoint(EditorDOMPoint aPoint,
340 : EditAction aOperation,
341 : nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
342 : TouchContent aTouchContent);
343 : nsresult GetNodesFromSelection(
344 : Selection& aSelection,
345 : EditAction aOperation,
346 : nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
347 : TouchContent aTouchContent = TouchContent::yes);
348 : enum class EntireList { no, yes };
349 : nsresult GetListActionNodes(
350 : nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
351 : EntireList aEntireList,
352 : TouchContent aTouchContent = TouchContent::yes);
353 : void GetDefinitionListItemTypes(Element* aElement, bool* aDT, bool* aDD);
354 : nsresult GetParagraphFormatNodes(
355 : nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
356 : TouchContent aTouchContent = TouchContent::yes);
357 : void LookInsideDivBQandList(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
358 : nsresult BustUpInlinesAtRangeEndpoints(RangeItem& inRange);
359 : nsresult BustUpInlinesAtBRs(
360 : nsIContent& aNode,
361 : nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes);
362 : nsIContent* GetHighestInlineParent(nsINode& aNode);
363 : void MakeTransitionList(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
364 : nsTArray<bool>& aTransitionArray);
365 : nsresult RemoveBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
366 : nsresult ApplyBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
367 : nsIAtom& aBlockTag);
368 : nsresult MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
369 : nsresult SplitAsNeeded(nsIAtom& aTag, OwningNonNull<nsINode>& inOutParent,
370 : int32_t& inOutOffset);
371 : nsresult SplitAsNeeded(nsIAtom& aTag, nsCOMPtr<nsINode>& inOutParent,
372 : int32_t& inOutOffset);
373 : nsresult AddTerminatingBR(nsIDOMNode *aBlock);
374 : EditorDOMPoint JoinNodesSmart(nsIContent& aNodeLeft,
375 : nsIContent& aNodeRight);
376 : Element* GetTopEnclosingMailCite(nsINode& aNode);
377 : nsresult PopListItem(nsIContent& aListItem, bool* aOutOfList = nullptr);
378 : nsresult RemoveListStructure(Element& aList);
379 : nsresult CacheInlineStyles(nsIDOMNode* aNode);
380 : nsresult ReapplyCachedStyles();
381 : void ClearCachedStyles();
382 : void AdjustSpecialBreaks();
383 : nsresult AdjustWhitespace(Selection* aSelection);
384 : nsresult PinSelectionToNewBlock(Selection* aSelection);
385 : void CheckInterlinePosition(Selection& aSelection);
386 : nsresult AdjustSelection(Selection* aSelection,
387 : nsIEditor::EDirection aAction);
388 : nsresult FindNearSelectableNode(nsIDOMNode* aSelNode,
389 : int32_t aSelOffset,
390 : nsIEditor::EDirection& aDirection,
391 : nsCOMPtr<nsIDOMNode>* outSelectableNode);
392 : /**
393 : * Returns true if aNode1 or aNode2 or both is the descendant of some type of
394 : * table element, but their nearest table element ancestors differ. "Table
395 : * element" here includes not just <table> but also <td>, <tbody>, <tr>, etc.
396 : * The nodes count as being their own descendants for this purpose, so a
397 : * table element is its own nearest table element ancestor.
398 : */
399 : bool InDifferentTableElements(nsIDOMNode* aNode1, nsIDOMNode* aNode2);
400 : bool InDifferentTableElements(nsINode* aNode1, nsINode* aNode2);
401 : nsresult RemoveEmptyNodes();
402 : nsresult SelectionEndpointInNode(nsINode* aNode, bool* aResult);
403 : nsresult UpdateDocChangeRange(nsRange* aRange);
404 : nsresult ConfirmSelectionInBody();
405 : nsresult InsertMozBRIfNeeded(nsINode& aNode);
406 : bool IsEmptyInline(nsINode& aNode);
407 : bool ListIsEmptyLine(nsTArray<OwningNonNull<nsINode>>& arrayOfNodes);
408 : nsresult RemoveAlignment(nsINode& aNode, const nsAString& aAlignType,
409 : bool aChildrenOnly);
410 : nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode, bool aStarts);
411 : enum class ContentsOnly { no, yes };
412 : nsresult AlignBlock(Element& aElement,
413 : const nsAString& aAlignType, ContentsOnly aContentsOnly);
414 : enum class Change { minus, plus };
415 : nsresult ChangeIndentation(Element& aElement, Change aChange);
416 : void DocumentModifiedWorker();
417 :
418 : /**
419 : * InitStyleCacheArray() initializes aStyleCache for usable with
420 : * GetInlineStyles().
421 : */
422 : void InitStyleCacheArray(StyleCache aStyleCache[SIZE_STYLE_TABLE]);
423 :
424 : /**
425 : * GetInlineStyles() retrieves the style of aNode and modifies each item of
426 : * aStyleCache.
427 : */
428 : nsresult GetInlineStyles(nsIDOMNode* aNode,
429 : StyleCache aStyleCache[SIZE_STYLE_TABLE]);
430 :
431 : protected:
432 : HTMLEditor* mHTMLEditor;
433 : RefPtr<nsRange> mDocChangeRange;
434 : bool mListenerEnabled;
435 : bool mReturnInEmptyLIKillsList;
436 : bool mDidDeleteSelection;
437 : bool mDidRangedDelete;
438 : bool mRestoreContentEditableCount;
439 : RefPtr<nsRange> mUtilRange;
440 : // Need to remember an int across willJoin/didJoin...
441 : uint32_t mJoinOffset;
442 : nsCOMPtr<Element> mNewBlock;
443 : RefPtr<RangeItem> mRangeItem;
444 :
445 : // XXX In strict speaking, mCachedStyles isn't enough to cache inline styles
446 : // because inline style can be specified with "style" attribute and/or
447 : // CSS in <style> elements or CSS files. So, we need to look for better
448 : // implementation about this.
449 : StyleCache mCachedStyles[SIZE_STYLE_TABLE];
450 : };
451 :
452 : } // namespace mozilla
453 :
454 : #endif // #ifndef HTMLEditRules_h
455 :
|