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_EditorUtils_h
7 #define mozilla_EditorUtils_h
8 
9 #include "mozilla/dom/Selection.h"
10 #include "mozilla/EditAction.h"
11 #include "mozilla/EditorBase.h"
12 #include "mozilla/EditorDOMPoint.h"
13 #include "mozilla/GuardObjects.h"
14 #include "nsCOMPtr.h"
15 #include "nsDebug.h"
16 #include "nsIDOMNode.h"
17 #include "nsIEditor.h"
18 #include "nscore.h"
19 
20 class nsAtom;
21 class nsIContentIterator;
22 class nsIDOMDocument;
23 class nsIDOMEvent;
24 class nsISimpleEnumerator;
25 class nsITransferable;
26 class nsRange;
27 
28 namespace mozilla {
29 template <class T>
30 class OwningNonNull;
31 
32 /***************************************************************************
33  * EditActionResult is useful to return multiple results of an editor
34  * action handler without out params.
35  * Note that when you return an anonymous instance from a method, you should
36  * use EditActionIgnored(), EditActionHandled() or EditActionCanceled() for
37  * easier to read.  In other words, EditActionResult should be used when
38  * declaring return type of a method, being an argument or defined as a local
39  * variable.
40  */
41 class MOZ_STACK_CLASS EditActionResult final {
42  public:
Succeeded()43   bool Succeeded() const { return NS_SUCCEEDED(mRv); }
Failed()44   bool Failed() const { return NS_FAILED(mRv); }
Rv()45   nsresult Rv() const { return mRv; }
Canceled()46   bool Canceled() const { return mCanceled; }
Handled()47   bool Handled() const { return mHandled; }
48 
SetResult(nsresult aRv)49   EditActionResult SetResult(nsresult aRv) {
50     mRv = aRv;
51     return *this;
52   }
MarkAsCanceled()53   EditActionResult MarkAsCanceled() {
54     mCanceled = true;
55     return *this;
56   }
MarkAsHandled()57   EditActionResult MarkAsHandled() {
58     mHandled = true;
59     return *this;
60   }
61 
EditActionResult(nsresult aRv)62   explicit EditActionResult(nsresult aRv)
63       : mRv(aRv), mCanceled(false), mHandled(false) {}
64 
65   EditActionResult& operator|=(const EditActionResult& aOther) {
66     mCanceled |= aOther.mCanceled;
67     mHandled |= aOther.mHandled;
68     // When both result are same, keep the result.
69     if (mRv == aOther.mRv) {
70       return *this;
71     }
72     // If one of the results is error, use NS_ERROR_FAILURE.
73     if (Failed() || aOther.Failed()) {
74       mRv = NS_ERROR_FAILURE;
75     } else {
76       // Otherwise, use generic success code, NS_OK.
77       mRv = NS_OK;
78     }
79     return *this;
80   }
81 
82  private:
83   nsresult mRv;
84   bool mCanceled;
85   bool mHandled;
86 
EditActionResult(nsresult aRv,bool aCanceled,bool aHandled)87   EditActionResult(nsresult aRv, bool aCanceled, bool aHandled)
88       : mRv(aRv), mCanceled(aCanceled), mHandled(aHandled) {}
89 
EditActionResult()90   EditActionResult()
91       : mRv(NS_ERROR_NOT_INITIALIZED), mCanceled(false), mHandled(false) {}
92 
93   friend EditActionResult EditActionIgnored(nsresult aRv);
94   friend EditActionResult EditActionHandled(nsresult aRv);
95   friend EditActionResult EditActionCanceled(nsresult aRv);
96 };
97 
98 /***************************************************************************
99  * When an edit action handler (or its helper) does nothing,
100  * EditActionIgnored should be returned.
101  */
102 inline EditActionResult EditActionIgnored(nsresult aRv = NS_OK) {
103   return EditActionResult(aRv, false, false);
104 }
105 
106 /***************************************************************************
107  * When an edit action handler (or its helper) handled and not canceled,
108  * EditActionHandled should be returned.
109  */
110 inline EditActionResult EditActionHandled(nsresult aRv = NS_OK) {
111   return EditActionResult(aRv, false, true);
112 }
113 
114 /***************************************************************************
115  * When an edit action handler (or its helper) handled and canceled,
116  * EditActionHandled should be returned.
117  */
118 inline EditActionResult EditActionCanceled(nsresult aRv = NS_OK) {
119   return EditActionResult(aRv, true, true);
120 }
121 
122 /***************************************************************************
123  * SplitNodeResult is a simple class for EditorBase::SplitNodeDeep().
124  * This makes the callers' code easier to read.
125  */
126 class MOZ_STACK_CLASS SplitNodeResult final {
127  public:
Succeeded()128   bool Succeeded() const { return NS_SUCCEEDED(mRv); }
Failed()129   bool Failed() const { return NS_FAILED(mRv); }
Rv()130   nsresult Rv() const { return mRv; }
131 
132   /**
133    * DidSplit() returns true if a node was actually split.
134    */
DidSplit()135   bool DidSplit() const { return mPreviousNode && mNextNode; }
136 
137   /**
138    * GetLeftNode() simply returns the left node which was created at splitting.
139    * This returns nullptr if the node wasn't split.
140    */
GetLeftNode()141   nsIContent* GetLeftNode() const {
142     return mPreviousNode && mNextNode ? mPreviousNode.get() : nullptr;
143   }
144 
145   /**
146    * GetRightNode() simply returns the right node which was split.
147    * This won't return nullptr unless failed to split due to invalid arguments.
148    */
GetRightNode()149   nsIContent* GetRightNode() const {
150     if (mGivenSplitPoint.IsSet()) {
151       return mGivenSplitPoint.GetChild();
152     }
153     return mPreviousNode && !mNextNode ? mPreviousNode : mNextNode;
154   }
155 
156   /**
157    * GetPreviousNode() returns previous node at the split point.
158    */
GetPreviousNode()159   nsIContent* GetPreviousNode() const {
160     if (mGivenSplitPoint.IsSet()) {
161       return mGivenSplitPoint.IsEndOfContainer() ? mGivenSplitPoint.GetChild()
162                                                  : nullptr;
163     }
164     return mPreviousNode;
165   }
166 
167   /**
168    * GetNextNode() returns next node at the split point.
169    */
GetNextNode()170   nsIContent* GetNextNode() const {
171     if (mGivenSplitPoint.IsSet()) {
172       return !mGivenSplitPoint.IsEndOfContainer() ? mGivenSplitPoint.GetChild()
173                                                   : nullptr;
174     }
175     return mNextNode;
176   }
177 
178   /**
179    * SplitPoint() returns the split point in the container.
180    * This is useful when callers insert an element at split point with
181    * EditorBase::CreateNode() or something similar methods.
182    *
183    * Note that the result is EditorRawDOMPoint but the nodes are grabbed
184    * by this instance.  Therefore, the life time of both container node
185    * and child node are guaranteed while using the result temporarily.
186    */
SplitPoint()187   EditorRawDOMPoint SplitPoint() const {
188     if (Failed()) {
189       return EditorRawDOMPoint();
190     }
191     if (mGivenSplitPoint.IsSet()) {
192       return mGivenSplitPoint.AsRaw();
193     }
194     if (!mPreviousNode) {
195       return EditorRawDOMPoint(mNextNode);
196     }
197     EditorRawDOMPoint point(mPreviousNode);
198     DebugOnly<bool> advanced = point.AdvanceOffset();
199     NS_WARNING_ASSERTION(advanced,
200                          "Failed to advance offset to after previous node");
201     return point;
202   }
203 
204   /**
205    * This constructor shouldn't be used by anybody except methods which
206    * use this as result when it succeeds.
207    *
208    * @param aPreviousNodeOfSplitPoint   Previous node immediately before
209    *                                    split point.
210    * @param aNextNodeOfSplitPoint       Next node immediately after split
211    *                                    point.
212    */
SplitNodeResult(nsIContent * aPreviousNodeOfSplitPoint,nsIContent * aNextNodeOfSplitPoint)213   SplitNodeResult(nsIContent* aPreviousNodeOfSplitPoint,
214                   nsIContent* aNextNodeOfSplitPoint)
215       : mPreviousNode(aPreviousNodeOfSplitPoint),
216         mNextNode(aNextNodeOfSplitPoint),
217         mRv(NS_OK) {
218     MOZ_DIAGNOSTIC_ASSERT(mPreviousNode || mNextNode);
219   }
220 
221   /**
222    * This constructor should be used when the method didn't split any nodes
223    * but want to return given split point as right point.
224    */
SplitNodeResult(const EditorRawDOMPoint & aGivenSplitPoint)225   explicit SplitNodeResult(const EditorRawDOMPoint& aGivenSplitPoint)
226       : mGivenSplitPoint(aGivenSplitPoint), mRv(NS_OK) {
227     MOZ_DIAGNOSTIC_ASSERT(mGivenSplitPoint.IsSet());
228   }
229 
230   /**
231    * This constructor shouldn't be used by anybody except methods which
232    * use this as error result when it fails.
233    */
SplitNodeResult(nsresult aRv)234   explicit SplitNodeResult(nsresult aRv) : mRv(aRv) {
235     MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv));
236   }
237 
238  private:
239   // When methods which return this class split some nodes actually, they
240   // need to set a set of left node and right node to this class.  However,
241   // one or both of them may be moved or removed by mutation observer.
242   // In such case, we cannot represent the point with EditorDOMPoint since
243   // it requires current container node.  Therefore, we need to use
244   // nsCOMPtr<nsIContent> here instead.
245   nsCOMPtr<nsIContent> mPreviousNode;
246   nsCOMPtr<nsIContent> mNextNode;
247 
248   // Methods which return this class may not split any nodes actually.  Then,
249   // they may want to return given split point as is since such behavior makes
250   // their callers simpler.  In this case, the point may be in a text node
251   // which cannot be represented as a node.  Therefore, we need EditorDOMPoint
252   // for representing the point.
253   EditorDOMPoint mGivenSplitPoint;
254 
255   nsresult mRv;
256 
257   SplitNodeResult() = delete;
258 };
259 
260 /***************************************************************************
261  * stack based helper class for batching a collection of transactions inside a
262  * placeholder transaction.
263  */
264 class MOZ_RAII AutoPlaceholderBatch final {
265  private:
266   RefPtr<EditorBase> mEditorBase;
267   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
268 
269  public:
AutoPlaceholderBatch(EditorBase * aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)270   explicit AutoPlaceholderBatch(
271       EditorBase* aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
272       : mEditorBase(aEditorBase) {
273     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
274     BeginPlaceholderTransaction(nullptr);
275   }
AutoPlaceholderBatch(EditorBase * aEditorBase,nsAtom * aTransactionName MOZ_GUARD_OBJECT_NOTIFIER_PARAM)276   AutoPlaceholderBatch(EditorBase* aEditorBase,
277                        nsAtom* aTransactionName MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
278       : mEditorBase(aEditorBase) {
279     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
280     BeginPlaceholderTransaction(aTransactionName);
281   }
~AutoPlaceholderBatch()282   ~AutoPlaceholderBatch() {
283     if (mEditorBase) {
284       mEditorBase->EndPlaceholderTransaction();
285     }
286   }
287 
288  private:
BeginPlaceholderTransaction(nsAtom * aTransactionName)289   void BeginPlaceholderTransaction(nsAtom* aTransactionName) {
290     if (mEditorBase) {
291       mEditorBase->BeginPlaceholderTransaction(aTransactionName);
292     }
293   }
294 };
295 
296 /***************************************************************************
297  * stack based helper class for saving/restoring selection.  Note that this
298  * assumes that the nodes involved are still around afterwards!
299  */
300 class MOZ_RAII AutoSelectionRestorer final {
301  private:
302   // Ref-counted reference to the selection that we are supposed to restore.
303   RefPtr<dom::Selection> mSelection;
304   EditorBase* mEditorBase;  // Non-owning ref to EditorBase.
305   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
306 
307  public:
308   /**
309    * Constructor responsible for remembering all state needed to restore
310    * aSelection.
311    */
312   AutoSelectionRestorer(dom::Selection* aSelection,
313                         EditorBase* aEditorBase
314                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
315 
316   /**
317    * Destructor restores mSelection to its former state
318    */
319   ~AutoSelectionRestorer();
320 
321   /**
322    * Abort() cancels to restore the selection.
323    */
324   void Abort();
325 };
326 
327 /***************************************************************************
328  * stack based helper class for StartOperation()/EndOperation() sandwich
329  */
330 class MOZ_RAII AutoRules final {
331  public:
AutoRules(EditorBase * aEditorBase,EditAction aAction,nsIEditor::EDirection aDirection MOZ_GUARD_OBJECT_NOTIFIER_PARAM)332   AutoRules(EditorBase* aEditorBase, EditAction aAction,
333             nsIEditor::EDirection aDirection MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
334       : mEditorBase(aEditorBase), mDoNothing(false) {
335     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
336     // mAction will already be set if this is nested call
337     if (mEditorBase && !mEditorBase->mAction) {
338       mEditorBase->StartOperation(aAction, aDirection);
339     } else {
340       mDoNothing = true;  // nested calls will end up here
341     }
342   }
343 
~AutoRules()344   ~AutoRules() {
345     if (mEditorBase && !mDoNothing) {
346       mEditorBase->EndOperation();
347     }
348   }
349 
350  protected:
351   EditorBase* mEditorBase;
352   bool mDoNothing;
353   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
354 };
355 
356 /***************************************************************************
357  * stack based helper class for turning off active selection adjustment
358  * by low level transactions
359  */
360 class MOZ_RAII AutoTransactionsConserveSelection final {
361  public:
AutoTransactionsConserveSelection(EditorBase * aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)362   explicit AutoTransactionsConserveSelection(
363       EditorBase* aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
364       : mEditorBase(aEditorBase), mOldState(true) {
365     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
366     if (mEditorBase) {
367       mOldState = mEditorBase->GetShouldTxnSetSelection();
368       mEditorBase->SetShouldTxnSetSelection(false);
369     }
370   }
371 
~AutoTransactionsConserveSelection()372   ~AutoTransactionsConserveSelection() {
373     if (mEditorBase) {
374       mEditorBase->SetShouldTxnSetSelection(mOldState);
375     }
376   }
377 
378  protected:
379   EditorBase* mEditorBase;
380   bool mOldState;
381   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
382 };
383 
384 /***************************************************************************
385  * stack based helper class for batching reflow and paint requests.
386  */
387 class MOZ_RAII AutoUpdateViewBatch final {
388  public:
AutoUpdateViewBatch(EditorBase * aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)389   explicit AutoUpdateViewBatch(
390       EditorBase* aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
391       : mEditorBase(aEditorBase) {
392     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
393     NS_ASSERTION(mEditorBase, "null mEditorBase pointer!");
394 
395     if (mEditorBase) {
396       mEditorBase->BeginUpdateViewBatch();
397     }
398   }
399 
~AutoUpdateViewBatch()400   ~AutoUpdateViewBatch() {
401     if (mEditorBase) {
402       mEditorBase->EndUpdateViewBatch();
403     }
404   }
405 
406  protected:
407   EditorBase* mEditorBase;
408   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
409 };
410 
411 class MOZ_STACK_CLASS AutoRangeArray final {
412  public:
AutoRangeArray(dom::Selection * aSelection)413   explicit AutoRangeArray(dom::Selection* aSelection) {
414     if (!aSelection) {
415       return;
416     }
417     uint32_t rangeCount = aSelection->RangeCount();
418     for (uint32_t i = 0; i < rangeCount; i++) {
419       mRanges.AppendElement(*aSelection->GetRangeAt(i));
420     }
421   }
422 
423   AutoTArray<mozilla::OwningNonNull<nsRange>, 8> mRanges;
424 };
425 
426 /******************************************************************************
427  * some helper classes for iterating the dom tree
428  *****************************************************************************/
429 
430 class BoolDomIterFunctor {
431  public:
432   virtual bool operator()(nsINode* aNode) const = 0;
433 };
434 
435 class MOZ_RAII DOMIterator {
436  public:
437   explicit DOMIterator(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
438 
439   explicit DOMIterator(nsINode& aNode MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
440   virtual ~DOMIterator();
441 
442   nsresult Init(nsRange& aRange);
443 
444   void AppendList(
445       const BoolDomIterFunctor& functor,
446       nsTArray<mozilla::OwningNonNull<nsINode>>& arrayOfNodes) const;
447 
448  protected:
449   nsCOMPtr<nsIContentIterator> mIter;
450   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
451 };
452 
453 class MOZ_RAII DOMSubtreeIterator final : public DOMIterator {
454  public:
455   explicit DOMSubtreeIterator(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
456   virtual ~DOMSubtreeIterator();
457 
458   nsresult Init(nsRange& aRange);
459 };
460 
461 class TrivialFunctor final : public BoolDomIterFunctor {
462  public:
463   // Used to build list of all nodes iterator covers
operator()464   virtual bool operator()(nsINode* aNode) const override { return true; }
465 };
466 
467 class EditorUtils final {
468  public:
469   /**
470    * IsDescendantOf() checks if aNode is a child or a descendant of aParent.
471    * aOutPoint is set to the child of aParent.
472    *
473    * @return            true if aNode is a child or a descendant of aParent.
474    */
475   static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
476                              EditorRawDOMPoint* aOutPoint = nullptr);
477   static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
478                              EditorDOMPoint* aOutPoint);
479 };
480 
481 class EditorHookUtils final {
482  public:
483   static bool DoInsertionHook(nsIDOMDocument* aDoc, nsIDOMEvent* aEvent,
484                               nsITransferable* aTrans);
485 
486  private:
487   static nsresult GetHookEnumeratorFromDocument(
488       nsIDOMDocument* aDoc, nsISimpleEnumerator** aEnumerator);
489 };
490 
491 }  // namespace mozilla
492 
493 #endif  // #ifndef mozilla_EditorUtils_h
494