/work/obj-fuzz/dist/include/mozilla/EditorUtils.h
Line | Count | Source (jump to first uncovered line) |
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/EditAction.h" |
12 | | #include "mozilla/EditorBase.h" |
13 | | #include "mozilla/EditorDOMPoint.h" |
14 | | #include "mozilla/GuardObjects.h" |
15 | | #include "nsCOMPtr.h" |
16 | | #include "nsDebug.h" |
17 | | #include "nsIEditor.h" |
18 | | #include "nscore.h" |
19 | | |
20 | | class nsAtom; |
21 | | class nsIContentIterator; |
22 | | class nsISimpleEnumerator; |
23 | | class nsITransferable; |
24 | | class nsRange; |
25 | | |
26 | | namespace mozilla { |
27 | | template <class T> class OwningNonNull; |
28 | | |
29 | | namespace dom { |
30 | | class Element; |
31 | | class Text; |
32 | | } // namespace dom |
33 | | |
34 | | /*************************************************************************** |
35 | | * EditActionResult is useful to return multiple results of an editor |
36 | | * action handler without out params. |
37 | | * Note that when you return an anonymous instance from a method, you should |
38 | | * use EditActionIgnored(), EditActionHandled() or EditActionCanceled() for |
39 | | * easier to read. In other words, EditActionResult should be used when |
40 | | * declaring return type of a method, being an argument or defined as a local |
41 | | * variable. |
42 | | */ |
43 | | class MOZ_STACK_CLASS EditActionResult final |
44 | | { |
45 | | public: |
46 | 0 | bool Succeeded() const { return NS_SUCCEEDED(mRv); } |
47 | 0 | bool Failed() const { return NS_FAILED(mRv); } |
48 | 0 | nsresult Rv() const { return mRv; } |
49 | 0 | bool Canceled() const { return mCanceled; } |
50 | 0 | bool Handled() const { return mHandled; } |
51 | 0 | bool EditorDestroyed() const { return mRv == NS_ERROR_EDITOR_DESTROYED; } |
52 | | |
53 | | EditActionResult SetResult(nsresult aRv) |
54 | 0 | { |
55 | 0 | mRv = aRv; |
56 | 0 | return *this; |
57 | 0 | } |
58 | | EditActionResult MarkAsCanceled() |
59 | 0 | { |
60 | 0 | mCanceled = true; |
61 | 0 | return *this; |
62 | 0 | } |
63 | | EditActionResult MarkAsHandled() |
64 | 0 | { |
65 | 0 | mHandled = true; |
66 | 0 | return *this; |
67 | 0 | } |
68 | | |
69 | | explicit EditActionResult(nsresult aRv) |
70 | | : mRv(aRv) |
71 | | , mCanceled(false) |
72 | | , mHandled(false) |
73 | 0 | { |
74 | 0 | } |
75 | | |
76 | | EditActionResult& operator|=(const EditActionResult& aOther) |
77 | 0 | { |
78 | 0 | mCanceled |= aOther.mCanceled; |
79 | 0 | mHandled |= aOther.mHandled; |
80 | 0 | // When both result are same, keep the result. |
81 | 0 | if (mRv == aOther.mRv) { |
82 | 0 | return *this; |
83 | 0 | } |
84 | 0 | // If one of the result is NS_ERROR_EDITOR_DESTROYED, use it since it's |
85 | 0 | // the most important error code for editor. |
86 | 0 | if (EditorDestroyed() || aOther.EditorDestroyed()) { |
87 | 0 | mRv = NS_ERROR_EDITOR_DESTROYED; |
88 | 0 | } |
89 | 0 | // If one of the results is error, use NS_ERROR_FAILURE. |
90 | 0 | else if (Failed() || aOther.Failed()) { |
91 | 0 | mRv = NS_ERROR_FAILURE; |
92 | 0 | } else { |
93 | 0 | // Otherwise, use generic success code, NS_OK. |
94 | 0 | mRv = NS_OK; |
95 | 0 | } |
96 | 0 | return *this; |
97 | 0 | } |
98 | | |
99 | | private: |
100 | | nsresult mRv; |
101 | | bool mCanceled; |
102 | | bool mHandled; |
103 | | |
104 | | EditActionResult(nsresult aRv, bool aCanceled, bool aHandled) |
105 | | : mRv(aRv) |
106 | | , mCanceled(aCanceled) |
107 | | , mHandled(aHandled) |
108 | 0 | { |
109 | 0 | } |
110 | | |
111 | | EditActionResult() |
112 | | : mRv(NS_ERROR_NOT_INITIALIZED) |
113 | | , mCanceled(false) |
114 | | , mHandled(false) |
115 | 0 | { |
116 | 0 | } |
117 | | |
118 | | friend EditActionResult EditActionIgnored(nsresult aRv); |
119 | | friend EditActionResult EditActionHandled(nsresult aRv); |
120 | | friend EditActionResult EditActionCanceled(nsresult aRv); |
121 | | }; |
122 | | |
123 | | /*************************************************************************** |
124 | | * When an edit action handler (or its helper) does nothing, |
125 | | * EditActionIgnored should be returned. |
126 | | */ |
127 | | inline EditActionResult |
128 | | EditActionIgnored(nsresult aRv = NS_OK) |
129 | 0 | { |
130 | 0 | return EditActionResult(aRv, false, false); |
131 | 0 | } |
132 | | |
133 | | /*************************************************************************** |
134 | | * When an edit action handler (or its helper) handled and not canceled, |
135 | | * EditActionHandled should be returned. |
136 | | */ |
137 | | inline EditActionResult |
138 | | EditActionHandled(nsresult aRv = NS_OK) |
139 | 0 | { |
140 | 0 | return EditActionResult(aRv, false, true); |
141 | 0 | } |
142 | | |
143 | | /*************************************************************************** |
144 | | * When an edit action handler (or its helper) handled and canceled, |
145 | | * EditActionHandled should be returned. |
146 | | */ |
147 | | inline EditActionResult |
148 | | EditActionCanceled(nsresult aRv = NS_OK) |
149 | 0 | { |
150 | 0 | return EditActionResult(aRv, true, true); |
151 | 0 | } |
152 | | |
153 | | /*************************************************************************** |
154 | | * CreateNodeResultBase is a simple class for CreateSomething() methods |
155 | | * which want to return new node. |
156 | | */ |
157 | | template<typename NodeType> |
158 | | class CreateNodeResultBase; |
159 | | |
160 | | typedef CreateNodeResultBase<dom::Element> CreateElementResult; |
161 | | |
162 | | template<typename NodeType> |
163 | | class MOZ_STACK_CLASS CreateNodeResultBase final |
164 | | { |
165 | | typedef CreateNodeResultBase<NodeType> SelfType; |
166 | | public: |
167 | | bool Succeeded() const { return NS_SUCCEEDED(mRv); } |
168 | 0 | bool Failed() const { return NS_FAILED(mRv); } |
169 | 0 | nsresult Rv() const { return mRv; } |
170 | 0 | NodeType* GetNewNode() const { return mNode; } |
171 | | |
172 | | CreateNodeResultBase() = delete; |
173 | | |
174 | | explicit CreateNodeResultBase(nsresult aRv) |
175 | | : mRv(aRv) |
176 | 0 | { |
177 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv)); |
178 | 0 | } |
179 | | |
180 | | explicit CreateNodeResultBase(NodeType* aNode) |
181 | | : mNode(aNode) |
182 | | , mRv(aNode ? NS_OK : NS_ERROR_FAILURE) |
183 | 0 | { |
184 | 0 | } |
185 | | |
186 | | explicit CreateNodeResultBase(already_AddRefed<NodeType>&& aNode) |
187 | | : mNode(aNode) |
188 | | , mRv(mNode.get() ? NS_OK : NS_ERROR_FAILURE) |
189 | 0 | { |
190 | 0 | } |
191 | | |
192 | | CreateNodeResultBase(const SelfType& aOther) = delete; |
193 | | SelfType& operator=(const SelfType& aOther) = delete; |
194 | | CreateNodeResultBase(SelfType&& aOther) = default; |
195 | | SelfType& operator=(SelfType&& aOther) = default; |
196 | | |
197 | | already_AddRefed<NodeType> forget() |
198 | 0 | { |
199 | 0 | mRv = NS_ERROR_NOT_INITIALIZED; |
200 | 0 | return mNode.forget(); |
201 | 0 | } |
202 | | |
203 | | private: |
204 | | RefPtr<NodeType> mNode; |
205 | | nsresult mRv; |
206 | | }; |
207 | | |
208 | | /*************************************************************************** |
209 | | * SplitNodeResult is a simple class for |
210 | | * EditorBase::SplitNodeDeepWithTransaction(). |
211 | | * This makes the callers' code easier to read. |
212 | | */ |
213 | | class MOZ_STACK_CLASS SplitNodeResult final |
214 | | { |
215 | | public: |
216 | 0 | bool Succeeded() const { return NS_SUCCEEDED(mRv); } |
217 | 0 | bool Failed() const { return NS_FAILED(mRv); } |
218 | 0 | nsresult Rv() const { return mRv; } |
219 | | |
220 | | /** |
221 | | * DidSplit() returns true if a node was actually split. |
222 | | */ |
223 | | bool DidSplit() const |
224 | 0 | { |
225 | 0 | return mPreviousNode && mNextNode; |
226 | 0 | } |
227 | | |
228 | | /** |
229 | | * GetLeftNode() simply returns the left node which was created at splitting. |
230 | | * This returns nullptr if the node wasn't split. |
231 | | */ |
232 | | nsIContent* GetLeftNode() const |
233 | 0 | { |
234 | 0 | return mPreviousNode && mNextNode ? mPreviousNode.get() : nullptr; |
235 | 0 | } |
236 | | |
237 | | /** |
238 | | * GetRightNode() simply returns the right node which was split. |
239 | | * This won't return nullptr unless failed to split due to invalid arguments. |
240 | | */ |
241 | | nsIContent* GetRightNode() const |
242 | 0 | { |
243 | 0 | if (mGivenSplitPoint.IsSet()) { |
244 | 0 | return mGivenSplitPoint.GetChild(); |
245 | 0 | } |
246 | 0 | return mPreviousNode && !mNextNode ? mPreviousNode : mNextNode; |
247 | 0 | } |
248 | | |
249 | | /** |
250 | | * GetPreviousNode() returns previous node at the split point. |
251 | | */ |
252 | | nsIContent* GetPreviousNode() const |
253 | 0 | { |
254 | 0 | if (mGivenSplitPoint.IsSet()) { |
255 | 0 | return mGivenSplitPoint.IsEndOfContainer() ? |
256 | 0 | mGivenSplitPoint.GetChild() : nullptr; |
257 | 0 | } |
258 | 0 | return mPreviousNode; |
259 | 0 | } |
260 | | |
261 | | /** |
262 | | * GetNextNode() returns next node at the split point. |
263 | | */ |
264 | | nsIContent* GetNextNode() const |
265 | 0 | { |
266 | 0 | if (mGivenSplitPoint.IsSet()) { |
267 | 0 | return !mGivenSplitPoint.IsEndOfContainer() ? |
268 | 0 | mGivenSplitPoint.GetChild() : nullptr; |
269 | 0 | } |
270 | 0 | return mNextNode; |
271 | 0 | } |
272 | | |
273 | | /** |
274 | | * SplitPoint() returns the split point in the container. |
275 | | * This is useful when callers insert an element at split point with |
276 | | * EditorBase::CreateNodeWithTransaction() or something similar methods. |
277 | | * |
278 | | * Note that the result is EditorRawDOMPoint but the nodes are grabbed |
279 | | * by this instance. Therefore, the life time of both container node |
280 | | * and child node are guaranteed while using the result temporarily. |
281 | | */ |
282 | | EditorRawDOMPoint SplitPoint() const |
283 | 0 | { |
284 | 0 | if (Failed()) { |
285 | 0 | return EditorRawDOMPoint(); |
286 | 0 | } |
287 | 0 | if (mGivenSplitPoint.IsSet()) { |
288 | 0 | return EditorRawDOMPoint(mGivenSplitPoint); |
289 | 0 | } |
290 | 0 | if (!mPreviousNode) { |
291 | 0 | return EditorRawDOMPoint(mNextNode); |
292 | 0 | } |
293 | 0 | EditorRawDOMPoint point(mPreviousNode); |
294 | 0 | DebugOnly<bool> advanced = point.AdvanceOffset(); |
295 | 0 | NS_WARNING_ASSERTION(advanced, |
296 | 0 | "Failed to advance offset to after previous node"); |
297 | 0 | return point; |
298 | 0 | } |
299 | | |
300 | | /** |
301 | | * This constructor shouldn't be used by anybody except methods which |
302 | | * use this as result when it succeeds. |
303 | | * |
304 | | * @param aPreviousNodeOfSplitPoint Previous node immediately before |
305 | | * split point. |
306 | | * @param aNextNodeOfSplitPoint Next node immediately after split |
307 | | * point. |
308 | | */ |
309 | | SplitNodeResult(nsIContent* aPreviousNodeOfSplitPoint, |
310 | | nsIContent* aNextNodeOfSplitPoint) |
311 | | : mPreviousNode(aPreviousNodeOfSplitPoint) |
312 | | , mNextNode(aNextNodeOfSplitPoint) |
313 | | , mRv(NS_OK) |
314 | 0 | { |
315 | 0 | MOZ_DIAGNOSTIC_ASSERT(mPreviousNode || mNextNode); |
316 | 0 | } |
317 | | |
318 | | /** |
319 | | * This constructor should be used when the method didn't split any nodes |
320 | | * but want to return given split point as right point. |
321 | | */ |
322 | | explicit SplitNodeResult(const EditorRawDOMPoint& aGivenSplitPoint) |
323 | | : mGivenSplitPoint(aGivenSplitPoint) |
324 | | , mRv(NS_OK) |
325 | 0 | { |
326 | 0 | MOZ_DIAGNOSTIC_ASSERT(mGivenSplitPoint.IsSet()); |
327 | 0 | } |
328 | | |
329 | | /** |
330 | | * This constructor shouldn't be used by anybody except methods which |
331 | | * use this as error result when it fails. |
332 | | */ |
333 | | explicit SplitNodeResult(nsresult aRv) |
334 | | : mRv(aRv) |
335 | 0 | { |
336 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv)); |
337 | 0 | } |
338 | | |
339 | | private: |
340 | | // When methods which return this class split some nodes actually, they |
341 | | // need to set a set of left node and right node to this class. However, |
342 | | // one or both of them may be moved or removed by mutation observer. |
343 | | // In such case, we cannot represent the point with EditorDOMPoint since |
344 | | // it requires current container node. Therefore, we need to use |
345 | | // nsCOMPtr<nsIContent> here instead. |
346 | | nsCOMPtr<nsIContent> mPreviousNode; |
347 | | nsCOMPtr<nsIContent> mNextNode; |
348 | | |
349 | | // Methods which return this class may not split any nodes actually. Then, |
350 | | // they may want to return given split point as is since such behavior makes |
351 | | // their callers simpler. In this case, the point may be in a text node |
352 | | // which cannot be represented as a node. Therefore, we need EditorDOMPoint |
353 | | // for representing the point. |
354 | | EditorDOMPoint mGivenSplitPoint; |
355 | | |
356 | | nsresult mRv; |
357 | | |
358 | | SplitNodeResult() = delete; |
359 | | }; |
360 | | |
361 | | /*************************************************************************** |
362 | | * SplitRangeOffFromNodeResult class is a simple class for methods which split a |
363 | | * node at 2 points for making part of the node split off from the node. |
364 | | */ |
365 | | class MOZ_STACK_CLASS SplitRangeOffFromNodeResult final |
366 | | { |
367 | | public: |
368 | 0 | bool Succeeded() const { return NS_SUCCEEDED(mRv); } |
369 | 0 | bool Failed() const { return NS_FAILED(mRv); } |
370 | 0 | nsresult Rv() const { return mRv; } |
371 | | |
372 | | /** |
373 | | * GetLeftContent() returns new created node before the part of quarried out. |
374 | | * This may return nullptr if the method didn't split at start edge of |
375 | | * the node. |
376 | | */ |
377 | 0 | nsIContent* GetLeftContent() const { return mLeftContent; } |
378 | | dom::Element* GetLeftContentAsElement() const |
379 | 0 | { |
380 | 0 | return Element::FromNodeOrNull(mLeftContent); |
381 | 0 | } |
382 | | |
383 | | /** |
384 | | * GetMiddleContent() returns new created node between left node and right |
385 | | * node. I.e., this is quarried out from the node. This may return nullptr |
386 | | * if the method unwrapped the middle node. |
387 | | */ |
388 | 0 | nsIContent* GetMiddleContent() const { return mMiddleContent; } |
389 | | dom::Element* GetMiddleContentAsElement() const |
390 | 0 | { |
391 | 0 | return Element::FromNodeOrNull(mMiddleContent); |
392 | 0 | } |
393 | | |
394 | | /** |
395 | | * GetRightContent() returns the right node after the part of quarried out. |
396 | | * This may return nullptr it the method didn't split at end edge of the |
397 | | * node. |
398 | | */ |
399 | 0 | nsIContent* GetRightContent() const { return mRightContent; } |
400 | | dom::Element* GetRightContentAsElement() const |
401 | 0 | { |
402 | 0 | return Element::FromNodeOrNull(mRightContent); |
403 | 0 | } |
404 | | |
405 | | SplitRangeOffFromNodeResult(nsIContent* aLeftContent, nsIContent* aMiddleContent, |
406 | | nsIContent* aRightContent) |
407 | | : mLeftContent(aLeftContent) |
408 | | , mMiddleContent(aMiddleContent) |
409 | | , mRightContent(aRightContent) |
410 | | , mRv(NS_OK) |
411 | 0 | { |
412 | 0 | } |
413 | | |
414 | | SplitRangeOffFromNodeResult(SplitNodeResult& aSplitResultAtLeftOfMiddleNode, |
415 | | SplitNodeResult& aSplitResultAtRightOfMiddleNode) |
416 | | : mRv(NS_OK) |
417 | 0 | { |
418 | 0 | if (aSplitResultAtLeftOfMiddleNode.Succeeded()) { |
419 | 0 | mLeftContent = aSplitResultAtLeftOfMiddleNode.GetPreviousNode(); |
420 | 0 | } |
421 | 0 | if (aSplitResultAtRightOfMiddleNode.Succeeded()) { |
422 | 0 | mRightContent = aSplitResultAtRightOfMiddleNode.GetNextNode(); |
423 | 0 | mMiddleContent = aSplitResultAtRightOfMiddleNode.GetPreviousNode(); |
424 | 0 | } |
425 | 0 | if (!mMiddleContent && aSplitResultAtLeftOfMiddleNode.Succeeded()) { |
426 | 0 | mMiddleContent = aSplitResultAtLeftOfMiddleNode.GetNextNode(); |
427 | 0 | } |
428 | 0 | } |
429 | | |
430 | | explicit SplitRangeOffFromNodeResult(nsresult aRv) |
431 | | : mRv(aRv) |
432 | 0 | { |
433 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv)); |
434 | 0 | } |
435 | | |
436 | | SplitRangeOffFromNodeResult( |
437 | | const SplitRangeOffFromNodeResult& aOther) = delete; |
438 | | SplitRangeOffFromNodeResult& |
439 | | operator=(const SplitRangeOffFromNodeResult& aOther) = delete; |
440 | 0 | SplitRangeOffFromNodeResult(SplitRangeOffFromNodeResult&& aOther) = default; |
441 | | SplitRangeOffFromNodeResult& |
442 | | operator=(SplitRangeOffFromNodeResult&& aOther) = default; |
443 | | |
444 | | private: |
445 | | nsCOMPtr<nsIContent> mLeftContent; |
446 | | nsCOMPtr<nsIContent> mMiddleContent; |
447 | | nsCOMPtr<nsIContent> mRightContent; |
448 | | |
449 | | nsresult mRv; |
450 | | |
451 | | SplitRangeOffFromNodeResult() = delete; |
452 | | }; |
453 | | |
454 | | /*************************************************************************** |
455 | | * stack based helper class for calling EditorBase::EndTransaction() after |
456 | | * EditorBase::BeginTransaction(). |
457 | | ***************************************************************************/ |
458 | | class MOZ_RAII AutoTransactionBatch final |
459 | | { |
460 | | private: |
461 | | OwningNonNull<EditorBase> mEditorBase; |
462 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
463 | | |
464 | | public: |
465 | | explicit AutoTransactionBatch(EditorBase& aEditorBase |
466 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
467 | | : mEditorBase(aEditorBase) |
468 | 0 | { |
469 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
470 | 0 | mEditorBase->BeginTransactionInternal(); |
471 | 0 | } |
472 | | |
473 | | ~AutoTransactionBatch() |
474 | 0 | { |
475 | 0 | mEditorBase->EndTransactionInternal(); |
476 | 0 | } |
477 | | }; |
478 | | |
479 | | /*************************************************************************** |
480 | | * stack based helper class for batching a collection of transactions inside a |
481 | | * placeholder transaction. |
482 | | */ |
483 | | class MOZ_RAII AutoPlaceholderBatch final |
484 | | { |
485 | | private: |
486 | | RefPtr<EditorBase> mEditorBase; |
487 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
488 | | |
489 | | public: |
490 | | explicit AutoPlaceholderBatch(EditorBase* aEditorBase |
491 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
492 | | : mEditorBase(aEditorBase) |
493 | 0 | { |
494 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
495 | 0 | BeginPlaceholderTransaction(nullptr); |
496 | 0 | } |
497 | | AutoPlaceholderBatch(EditorBase* aEditorBase, |
498 | | nsAtom* aTransactionName |
499 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
500 | | : mEditorBase(aEditorBase) |
501 | 0 | { |
502 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
503 | 0 | BeginPlaceholderTransaction(aTransactionName); |
504 | 0 | } |
505 | | ~AutoPlaceholderBatch() |
506 | 0 | { |
507 | 0 | if (mEditorBase) { |
508 | 0 | mEditorBase->EndPlaceholderTransaction(); |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | | private: |
513 | | void BeginPlaceholderTransaction(nsAtom* aTransactionName) |
514 | 0 | { |
515 | 0 | if (mEditorBase) { |
516 | 0 | mEditorBase->BeginPlaceholderTransaction(aTransactionName); |
517 | 0 | } |
518 | 0 | } |
519 | | }; |
520 | | |
521 | | /*************************************************************************** |
522 | | * stack based helper class for saving/restoring selection. Note that this |
523 | | * assumes that the nodes involved are still around afterwards! |
524 | | */ |
525 | | class MOZ_RAII AutoSelectionRestorer final |
526 | | { |
527 | | private: |
528 | | // Ref-counted reference to the selection that we are supposed to restore. |
529 | | RefPtr<dom::Selection> mSelection; |
530 | | EditorBase* mEditorBase; // Non-owning ref to EditorBase. |
531 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
532 | | |
533 | | public: |
534 | | /** |
535 | | * Constructor responsible for remembering all state needed to restore |
536 | | * aSelection. |
537 | | */ |
538 | | AutoSelectionRestorer(dom::Selection* aSelection, |
539 | | EditorBase* aEditorBase |
540 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM); |
541 | | |
542 | | /** |
543 | | * Destructor restores mSelection to its former state |
544 | | */ |
545 | | ~AutoSelectionRestorer(); |
546 | | |
547 | | /** |
548 | | * Abort() cancels to restore the selection. |
549 | | */ |
550 | | void Abort(); |
551 | | }; |
552 | | |
553 | | /*************************************************************************** |
554 | | * AutoTopLevelEditSubActionNotifier notifies editor of start to handle |
555 | | * top level edit sub-action and end handling top level edit sub-action. |
556 | | */ |
557 | | class MOZ_RAII AutoTopLevelEditSubActionNotifier final |
558 | | { |
559 | | public: |
560 | | AutoTopLevelEditSubActionNotifier(EditorBase& aEditorBase, |
561 | | EditSubAction aEditSubAction, |
562 | | nsIEditor::EDirection aDirection |
563 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
564 | | : mEditorBase(aEditorBase) |
565 | | , mDoNothing(false) |
566 | 0 | { |
567 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
568 | 0 | // mTopLevelEditSubAction will already be set if this is nested call |
569 | 0 | // XXX Looks like that this is not aware of unexpected nested edit action |
570 | 0 | // handling via selectionchange event listener or mutation event |
571 | 0 | // listener. |
572 | 0 | if (!mEditorBase.mTopLevelEditSubAction) { |
573 | 0 | mEditorBase.OnStartToHandleTopLevelEditSubAction(aEditSubAction, |
574 | 0 | aDirection); |
575 | 0 | } else { |
576 | 0 | mDoNothing = true; // nested calls will end up here |
577 | 0 | } |
578 | 0 | } |
579 | | |
580 | | ~AutoTopLevelEditSubActionNotifier() |
581 | 0 | { |
582 | 0 | if (!mDoNothing) { |
583 | 0 | mEditorBase.OnEndHandlingTopLevelEditSubAction(); |
584 | 0 | } |
585 | 0 | } |
586 | | |
587 | | protected: |
588 | | EditorBase& mEditorBase; |
589 | | bool mDoNothing; |
590 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
591 | | }; |
592 | | |
593 | | /*************************************************************************** |
594 | | * stack based helper class for turning off active selection adjustment |
595 | | * by low level transactions |
596 | | */ |
597 | | class MOZ_RAII AutoTransactionsConserveSelection final |
598 | | { |
599 | | public: |
600 | | explicit AutoTransactionsConserveSelection(EditorBase& aEditorBase |
601 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
602 | | : mEditorBase(aEditorBase) |
603 | | , mAllowedTransactionsToChangeSelection( |
604 | | aEditorBase.AllowsTransactionsToChangeSelection()) |
605 | 0 | { |
606 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
607 | 0 | mEditorBase.MakeThisAllowTransactionsToChangeSelection(false); |
608 | 0 | } |
609 | | |
610 | | ~AutoTransactionsConserveSelection() |
611 | 0 | { |
612 | 0 | mEditorBase.MakeThisAllowTransactionsToChangeSelection( |
613 | 0 | mAllowedTransactionsToChangeSelection); |
614 | 0 | } |
615 | | |
616 | | protected: |
617 | | EditorBase& mEditorBase; |
618 | | bool mAllowedTransactionsToChangeSelection; |
619 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
620 | | }; |
621 | | |
622 | | /*************************************************************************** |
623 | | * stack based helper class for batching reflow and paint requests. |
624 | | */ |
625 | | class MOZ_RAII AutoUpdateViewBatch final |
626 | | { |
627 | | public: |
628 | | explicit AutoUpdateViewBatch(EditorBase* aEditorBase |
629 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
630 | | : mEditorBase(aEditorBase) |
631 | 0 | { |
632 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
633 | 0 | NS_ASSERTION(mEditorBase, "null mEditorBase pointer!"); |
634 | 0 |
|
635 | 0 | if (mEditorBase) { |
636 | 0 | mEditorBase->BeginUpdateViewBatch(); |
637 | 0 | } |
638 | 0 | } |
639 | | |
640 | | ~AutoUpdateViewBatch() |
641 | 0 | { |
642 | 0 | if (mEditorBase) { |
643 | 0 | mEditorBase->EndUpdateViewBatch(); |
644 | 0 | } |
645 | 0 | } |
646 | | |
647 | | protected: |
648 | | EditorBase* mEditorBase; |
649 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
650 | | }; |
651 | | |
652 | | class MOZ_STACK_CLASS AutoRangeArray final |
653 | | { |
654 | | public: |
655 | | explicit AutoRangeArray(dom::Selection* aSelection) |
656 | 0 | { |
657 | 0 | if (!aSelection) { |
658 | 0 | return; |
659 | 0 | } |
660 | 0 | uint32_t rangeCount = aSelection->RangeCount(); |
661 | 0 | for (uint32_t i = 0; i < rangeCount; i++) { |
662 | 0 | mRanges.AppendElement(*aSelection->GetRangeAt(i)); |
663 | 0 | } |
664 | 0 | } |
665 | | |
666 | | AutoTArray<mozilla::OwningNonNull<nsRange>, 8> mRanges; |
667 | | }; |
668 | | |
669 | | /****************************************************************************** |
670 | | * some helper classes for iterating the dom tree |
671 | | *****************************************************************************/ |
672 | | |
673 | | class BoolDomIterFunctor |
674 | | { |
675 | | public: |
676 | | virtual bool operator()(nsINode* aNode) const = 0; |
677 | | }; |
678 | | |
679 | | class MOZ_RAII DOMIterator |
680 | | { |
681 | | public: |
682 | | explicit DOMIterator(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); |
683 | | |
684 | | explicit DOMIterator(nsINode& aNode MOZ_GUARD_OBJECT_NOTIFIER_PARAM); |
685 | | virtual ~DOMIterator(); |
686 | | |
687 | | nsresult Init(nsRange& aRange); |
688 | | |
689 | | void AppendList( |
690 | | const BoolDomIterFunctor& functor, |
691 | | nsTArray<mozilla::OwningNonNull<nsINode>>& arrayOfNodes) const; |
692 | | |
693 | | protected: |
694 | | nsCOMPtr<nsIContentIterator> mIter; |
695 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
696 | | }; |
697 | | |
698 | | class MOZ_RAII DOMSubtreeIterator final : public DOMIterator |
699 | | { |
700 | | public: |
701 | | explicit DOMSubtreeIterator(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); |
702 | | virtual ~DOMSubtreeIterator(); |
703 | | |
704 | | nsresult Init(nsRange& aRange); |
705 | | }; |
706 | | |
707 | | class TrivialFunctor final : public BoolDomIterFunctor |
708 | | { |
709 | | public: |
710 | | // Used to build list of all nodes iterator covers |
711 | | virtual bool operator()(nsINode* aNode) const override |
712 | 0 | { |
713 | 0 | return true; |
714 | 0 | } |
715 | | }; |
716 | | |
717 | | class EditorUtils final |
718 | | { |
719 | | public: |
720 | | /** |
721 | | * IsDescendantOf() checks if aNode is a child or a descendant of aParent. |
722 | | * aOutPoint is set to the child of aParent. |
723 | | * |
724 | | * @return true if aNode is a child or a descendant of aParent. |
725 | | */ |
726 | | static bool IsDescendantOf(const nsINode& aNode, |
727 | | const nsINode& aParent, |
728 | | EditorRawDOMPoint* aOutPoint = nullptr); |
729 | | static bool IsDescendantOf(const nsINode& aNode, |
730 | | const nsINode& aParent, |
731 | | EditorDOMPoint* aOutPoint); |
732 | | }; |
733 | | |
734 | | } // namespace mozilla |
735 | | |
736 | | #endif // #ifndef mozilla_EditorUtils_h |