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 :
7 : #ifndef mozilla_EditorUtils_h
8 : #define mozilla_EditorUtils_h
9 :
10 : #include "mozilla/dom/Selection.h"
11 : #include "mozilla/EditorBase.h"
12 : #include "mozilla/GuardObjects.h"
13 : #include "nsCOMPtr.h"
14 : #include "nsDebug.h"
15 : #include "nsIDOMNode.h"
16 : #include "nsIEditor.h"
17 : #include "nscore.h"
18 :
19 : class nsIAtom;
20 : class nsIContentIterator;
21 : class nsIDOMDocument;
22 : class nsIDOMEvent;
23 : class nsISimpleEnumerator;
24 : class nsITransferable;
25 : class nsRange;
26 :
27 : namespace mozilla {
28 : template <class T> class OwningNonNull;
29 :
30 : /***************************************************************************
31 : * EditActionResult is useful to return multiple results of an editor
32 : * action handler without out params.
33 : * Note that when you return an anonymous instance from a method, you should
34 : * use EditActionIgnored(), EditActionHandled() or EditActionCanceled() for
35 : * easier to read. In other words, EditActionResult should be used when
36 : * declaring return type of a method, being an argument or defined as a local
37 : * variable.
38 : */
39 : class MOZ_STACK_CLASS EditActionResult final
40 : {
41 : public:
42 : bool Succeeded() const { return NS_SUCCEEDED(mRv); }
43 0 : bool Failed() const { return NS_FAILED(mRv); }
44 0 : nsresult Rv() const { return mRv; }
45 0 : bool Canceled() const { return mCanceled; }
46 0 : bool Handled() const { return mHandled; }
47 :
48 0 : EditActionResult SetResult(nsresult aRv)
49 : {
50 0 : mRv = aRv;
51 0 : return *this;
52 : }
53 : EditActionResult MarkAsCanceled()
54 : {
55 : mCanceled = true;
56 : return *this;
57 : }
58 0 : EditActionResult MarkAsHandled()
59 : {
60 0 : mHandled = true;
61 0 : return *this;
62 : }
63 :
64 0 : explicit EditActionResult(nsresult aRv)
65 0 : : mRv(aRv)
66 : , mCanceled(false)
67 0 : , mHandled(false)
68 : {
69 0 : }
70 :
71 0 : EditActionResult& operator|=(const EditActionResult& aOther)
72 : {
73 0 : mCanceled |= aOther.mCanceled;
74 0 : mHandled |= aOther.mHandled;
75 : // When both result are same, keep the result.
76 0 : if (mRv == aOther.mRv) {
77 0 : return *this;
78 : }
79 : // If one of the results is error, use NS_ERROR_FAILURE.
80 0 : if (Failed() || aOther.Failed()) {
81 0 : mRv = NS_ERROR_FAILURE;
82 : } else {
83 : // Otherwise, use generic success code, NS_OK.
84 0 : mRv = NS_OK;
85 : }
86 0 : return *this;
87 : }
88 :
89 : private:
90 : nsresult mRv;
91 : bool mCanceled;
92 : bool mHandled;
93 :
94 0 : EditActionResult(nsresult aRv, bool aCanceled, bool aHandled)
95 0 : : mRv(aRv)
96 : , mCanceled(aCanceled)
97 0 : , mHandled(aHandled)
98 : {
99 0 : }
100 :
101 : EditActionResult()
102 : : mRv(NS_ERROR_NOT_INITIALIZED)
103 : , mCanceled(false)
104 : , mHandled(false)
105 : {
106 : }
107 :
108 : friend EditActionResult EditActionIgnored(nsresult aRv);
109 : friend EditActionResult EditActionHandled(nsresult aRv);
110 : friend EditActionResult EditActionCanceled(nsresult aRv);
111 : };
112 :
113 : /***************************************************************************
114 : * When an edit action handler (or its helper) does nothing,
115 : * EditActionIgnored should be returned.
116 : */
117 : inline EditActionResult
118 0 : EditActionIgnored(nsresult aRv = NS_OK)
119 : {
120 0 : return EditActionResult(aRv, false, false);
121 : }
122 :
123 : /***************************************************************************
124 : * When an edit action handler (or its helper) handled and not canceled,
125 : * EditActionHandled should be returned.
126 : */
127 : inline EditActionResult
128 0 : EditActionHandled(nsresult aRv = NS_OK)
129 : {
130 0 : return EditActionResult(aRv, false, true);
131 : }
132 :
133 : /***************************************************************************
134 : * When an edit action handler (or its helper) handled and canceled,
135 : * EditActionHandled should be returned.
136 : */
137 : inline EditActionResult
138 0 : EditActionCanceled(nsresult aRv = NS_OK)
139 : {
140 0 : return EditActionResult(aRv, true, true);
141 : }
142 :
143 : /***************************************************************************
144 : * stack based helper class for batching a collection of transactions inside a
145 : * placeholder transaction.
146 : * XXX This is used by mozInlineSpellChecker. Therefore, cannot use concrete
147 : * editor class.
148 : */
149 : class MOZ_RAII AutoPlaceHolderBatch
150 : {
151 : private:
152 : nsCOMPtr<nsIEditor> mEditor;
153 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
154 :
155 : public:
156 1 : AutoPlaceHolderBatch(nsIEditor* aEditor,
157 : nsIAtom* aAtom
158 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
159 1 : : mEditor(aEditor)
160 : {
161 1 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
162 1 : if (mEditor) {
163 1 : mEditor->BeginPlaceHolderTransaction(aAtom);
164 : }
165 1 : }
166 1 : ~AutoPlaceHolderBatch()
167 1 : {
168 1 : if (mEditor) {
169 1 : mEditor->EndPlaceHolderTransaction();
170 : }
171 1 : }
172 : };
173 :
174 : /***************************************************************************
175 : * stack based helper class for batching a collection of txns.
176 : * Note: I changed this to use placeholder batching so that we get
177 : * proper selection save/restore across undo/redo.
178 : */
179 : class MOZ_RAII AutoEditBatch final : public AutoPlaceHolderBatch
180 : {
181 : private:
182 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
183 :
184 : public:
185 0 : explicit AutoEditBatch(nsIEditor* aEditor
186 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
187 0 : : AutoPlaceHolderBatch(aEditor, nullptr)
188 : {
189 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
190 0 : }
191 0 : ~AutoEditBatch() {}
192 : };
193 :
194 : /***************************************************************************
195 : * stack based helper class for saving/restoring selection. Note that this
196 : * assumes that the nodes involved are still around afterwards!
197 : */
198 : class MOZ_RAII AutoSelectionRestorer final
199 : {
200 : private:
201 : // Ref-counted reference to the selection that we are supposed to restore.
202 : RefPtr<dom::Selection> mSelection;
203 : EditorBase* mEditorBase; // Non-owning ref to EditorBase.
204 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
205 :
206 : public:
207 : /**
208 : * Constructor responsible for remembering all state needed to restore
209 : * aSelection.
210 : */
211 : AutoSelectionRestorer(dom::Selection* aSelection,
212 : EditorBase* aEditorBase
213 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
214 :
215 : /**
216 : * Destructor restores mSelection to its former state
217 : */
218 : ~AutoSelectionRestorer();
219 :
220 : /**
221 : * Abort() cancels to restore the selection.
222 : */
223 : void Abort();
224 : };
225 :
226 : /***************************************************************************
227 : * stack based helper class for StartOperation()/EndOperation() sandwich
228 : */
229 : class MOZ_RAII AutoRules final
230 : {
231 : public:
232 8 : AutoRules(EditorBase* aEditorBase, EditAction aAction,
233 : nsIEditor::EDirection aDirection
234 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
235 8 : : mEditorBase(aEditorBase)
236 8 : , mDoNothing(false)
237 : {
238 8 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
239 : // mAction will already be set if this is nested call
240 8 : if (mEditorBase && !mEditorBase->mAction) {
241 3 : mEditorBase->StartOperation(aAction, aDirection);
242 : } else {
243 5 : mDoNothing = true; // nested calls will end up here
244 : }
245 8 : }
246 :
247 8 : ~AutoRules()
248 8 : {
249 8 : if (mEditorBase && !mDoNothing) {
250 3 : mEditorBase->EndOperation();
251 : }
252 8 : }
253 :
254 : protected:
255 : EditorBase* mEditorBase;
256 : bool mDoNothing;
257 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
258 : };
259 :
260 : /***************************************************************************
261 : * stack based helper class for turning off active selection adjustment
262 : * by low level transactions
263 : */
264 : class MOZ_RAII AutoTransactionsConserveSelection final
265 : {
266 : public:
267 1 : explicit AutoTransactionsConserveSelection(EditorBase* aEditorBase
268 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
269 1 : : mEditorBase(aEditorBase)
270 1 : , mOldState(true)
271 : {
272 1 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
273 1 : if (mEditorBase) {
274 1 : mOldState = mEditorBase->GetShouldTxnSetSelection();
275 1 : mEditorBase->SetShouldTxnSetSelection(false);
276 : }
277 1 : }
278 :
279 1 : ~AutoTransactionsConserveSelection()
280 1 : {
281 1 : if (mEditorBase) {
282 1 : mEditorBase->SetShouldTxnSetSelection(mOldState);
283 : }
284 1 : }
285 :
286 : protected:
287 : EditorBase* mEditorBase;
288 : bool mOldState;
289 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
290 : };
291 :
292 : /***************************************************************************
293 : * stack based helper class for batching reflow and paint requests.
294 : */
295 : class MOZ_RAII AutoUpdateViewBatch final
296 : {
297 : public:
298 0 : explicit AutoUpdateViewBatch(EditorBase* aEditorBase
299 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
300 0 : : mEditorBase(aEditorBase)
301 : {
302 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
303 0 : NS_ASSERTION(mEditorBase, "null mEditorBase pointer!");
304 :
305 0 : if (mEditorBase) {
306 0 : mEditorBase->BeginUpdateViewBatch();
307 : }
308 0 : }
309 :
310 0 : ~AutoUpdateViewBatch()
311 0 : {
312 0 : if (mEditorBase) {
313 0 : mEditorBase->EndUpdateViewBatch();
314 : }
315 0 : }
316 :
317 : protected:
318 : EditorBase* mEditorBase;
319 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
320 : };
321 :
322 0 : class MOZ_STACK_CLASS AutoRangeArray final
323 : {
324 : public:
325 0 : explicit AutoRangeArray(dom::Selection* aSelection)
326 0 : {
327 0 : if (!aSelection) {
328 0 : return;
329 : }
330 0 : uint32_t rangeCount = aSelection->RangeCount();
331 0 : for (uint32_t i = 0; i < rangeCount; i++) {
332 0 : mRanges.AppendElement(*aSelection->GetRangeAt(i));
333 : }
334 : }
335 :
336 : AutoTArray<mozilla::OwningNonNull<nsRange>, 8> mRanges;
337 : };
338 :
339 : /******************************************************************************
340 : * some helper classes for iterating the dom tree
341 : *****************************************************************************/
342 :
343 0 : class BoolDomIterFunctor
344 : {
345 : public:
346 : virtual bool operator()(nsINode* aNode) const = 0;
347 : };
348 :
349 : class MOZ_RAII DOMIterator
350 : {
351 : public:
352 : explicit DOMIterator(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
353 :
354 : explicit DOMIterator(nsINode& aNode MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
355 : virtual ~DOMIterator();
356 :
357 : nsresult Init(nsRange& aRange);
358 :
359 : void AppendList(
360 : const BoolDomIterFunctor& functor,
361 : nsTArray<mozilla::OwningNonNull<nsINode>>& arrayOfNodes) const;
362 :
363 : protected:
364 : nsCOMPtr<nsIContentIterator> mIter;
365 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
366 : };
367 :
368 : class MOZ_RAII DOMSubtreeIterator final : public DOMIterator
369 : {
370 : public:
371 : explicit DOMSubtreeIterator(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
372 : virtual ~DOMSubtreeIterator();
373 :
374 : nsresult Init(nsRange& aRange);
375 : };
376 :
377 0 : class TrivialFunctor final : public BoolDomIterFunctor
378 : {
379 : public:
380 : // Used to build list of all nodes iterator covers
381 0 : virtual bool operator()(nsINode* aNode) const
382 : {
383 0 : return true;
384 : }
385 : };
386 :
387 : /******************************************************************************
388 : * general dom point utility struct
389 : *****************************************************************************/
390 0 : struct MOZ_STACK_CLASS EditorDOMPoint final
391 : {
392 : nsCOMPtr<nsINode> node;
393 : int32_t offset;
394 :
395 0 : EditorDOMPoint()
396 0 : : node(nullptr)
397 0 : , offset(-1)
398 0 : {}
399 0 : EditorDOMPoint(nsINode* aNode, int32_t aOffset)
400 0 : : node(aNode)
401 0 : , offset(aOffset)
402 0 : {}
403 : EditorDOMPoint(nsIDOMNode* aNode, int32_t aOffset)
404 : : node(do_QueryInterface(aNode))
405 : , offset(aOffset)
406 : {}
407 :
408 0 : void SetPoint(nsINode* aNode, int32_t aOffset)
409 : {
410 0 : node = aNode;
411 0 : offset = aOffset;
412 0 : }
413 : void SetPoint(nsIDOMNode* aNode, int32_t aOffset)
414 : {
415 : node = do_QueryInterface(aNode);
416 : offset = aOffset;
417 : }
418 : };
419 :
420 : class EditorUtils final
421 : {
422 : public:
423 : static bool IsDescendantOf(nsINode* aNode, nsINode* aParent,
424 : int32_t* aOffset = 0);
425 : static bool IsDescendantOf(nsIDOMNode* aNode, nsIDOMNode* aParent,
426 : int32_t* aOffset = 0);
427 : static bool IsLeafNode(nsIDOMNode* aNode);
428 : };
429 :
430 : class EditorHookUtils final
431 : {
432 : public:
433 : static bool DoInsertionHook(nsIDOMDocument* aDoc, nsIDOMEvent* aEvent,
434 : nsITransferable* aTrans);
435 :
436 : private:
437 : static nsresult GetHookEnumeratorFromDocument(
438 : nsIDOMDocument*aDoc,
439 : nsISimpleEnumerator** aEnumerator);
440 : };
441 :
442 : } // namespace mozilla
443 :
444 : #endif // #ifndef mozilla_EditorUtils_h
|