/work/obj-fuzz/dist/include/mozilla/EditorBase.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 | | #ifndef mozilla_EditorBase_h |
7 | | #define mozilla_EditorBase_h |
8 | | |
9 | | #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc. |
10 | | #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint |
11 | | #include "mozilla/Maybe.h" // for Maybe |
12 | | #include "mozilla/OwningNonNull.h" // for OwningNonNull |
13 | | #include "mozilla/PresShell.h" // for PresShell |
14 | | #include "mozilla/RangeBoundary.h" // for RawRangeBoundary, RangeBoundary |
15 | | #include "mozilla/SelectionState.h" // for RangeUpdater, etc. |
16 | | #include "mozilla/StyleSheet.h" // for StyleSheet |
17 | | #include "mozilla/TransactionManager.h" // for TransactionManager |
18 | | #include "mozilla/WeakPtr.h" // for WeakPtr |
19 | | #include "mozilla/dom/Selection.h" |
20 | | #include "mozilla/dom/Text.h" |
21 | | #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr |
22 | | #include "nsCycleCollectionParticipant.h" |
23 | | #include "nsGkAtoms.h" |
24 | | #include "nsIDocument.h" // for nsIDocument |
25 | | #include "nsIContentInlines.h" // for nsINode::IsEditable() |
26 | | #include "nsIEditor.h" // for nsIEditor, etc. |
27 | | #include "nsIObserver.h" // for NS_DECL_NSIOBSERVER, etc. |
28 | | #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc. |
29 | | #include "nsISelectionController.h" // for nsISelectionController constants |
30 | | #include "nsISelectionListener.h" // for nsISelectionListener |
31 | | #include "nsISupportsImpl.h" // for EditorBase::Release, etc. |
32 | | #include "nsIWeakReferenceUtils.h" // for nsWeakPtr |
33 | | #include "nsLiteralString.h" // for NS_LITERAL_STRING |
34 | | #include "nsString.h" // for nsCString |
35 | | #include "nsTArray.h" // for nsTArray and nsAutoTArray |
36 | | #include "nsWeakReference.h" // for nsSupportsWeakReference |
37 | | #include "nscore.h" // for nsresult, nsAString, etc. |
38 | | |
39 | | class mozInlineSpellChecker; |
40 | | class nsAtom; |
41 | | class nsIContent; |
42 | | class nsIDocumentStateListener; |
43 | | class nsIEditActionListener; |
44 | | class nsIEditorObserver; |
45 | | class nsINode; |
46 | | class nsIPresShell; |
47 | | class nsISupports; |
48 | | class nsITransaction; |
49 | | class nsITransactionListener; |
50 | | class nsIWidget; |
51 | | class nsRange; |
52 | | |
53 | | namespace mozilla { |
54 | | class AutoSelectionRestorer; |
55 | | class AutoTopLevelEditSubActionNotifier; |
56 | | class AutoTransactionBatch; |
57 | | class AutoTransactionsConserveSelection; |
58 | | class AutoUpdateViewBatch; |
59 | | class ChangeAttributeTransaction; |
60 | | class CompositionTransaction; |
61 | | class CreateElementTransaction; |
62 | | class CSSEditUtils; |
63 | | class DeleteNodeTransaction; |
64 | | class DeleteRangeTransaction; |
65 | | class DeleteTextTransaction; |
66 | | class EditAggregateTransaction; |
67 | | class EditorEventListener; |
68 | | class EditTransactionBase; |
69 | | class ErrorResult; |
70 | | class HTMLEditor; |
71 | | class HTMLEditUtils; |
72 | | class IMEContentObserver; |
73 | | class InsertNodeTransaction; |
74 | | class InsertTextTransaction; |
75 | | class JoinNodeTransaction; |
76 | | class PlaceholderTransaction; |
77 | | class SplitNodeResult; |
78 | | class SplitNodeTransaction; |
79 | | class TextComposition; |
80 | | class TextEditor; |
81 | | class TextEditRules; |
82 | | class TextInputListener; |
83 | | class TextServicesDocument; |
84 | | class TypeInState; |
85 | | class WSRunObject; |
86 | | enum class EditSubAction : int32_t; |
87 | | |
88 | | namespace dom { |
89 | | class DataTransfer; |
90 | | class DragEvent; |
91 | | class Element; |
92 | | class EventTarget; |
93 | | class Text; |
94 | | } // namespace dom |
95 | | |
96 | | namespace widget { |
97 | | struct IMEState; |
98 | | } // namespace widget |
99 | | |
100 | | /** |
101 | | * CachedWeakPtr stores a pointer to a class which inherits nsIWeakReference. |
102 | | * If the instance of the class has already been destroyed, this returns |
103 | | * nullptr. Otherwise, returns cached pointer. |
104 | | * If class T inherits nsISupports a lot, specify Base explicitly for avoiding |
105 | | * ambiguous conversion to nsISupports. |
106 | | */ |
107 | | template<class T, class Base = nsISupports> |
108 | | class CachedWeakPtr final |
109 | | { |
110 | | public: |
111 | | CachedWeakPtr<T, Base>() |
112 | | : mCache(nullptr) |
113 | | { |
114 | | } |
115 | | explicit CachedWeakPtr<T, Base>(T* aObject) |
116 | 0 | { |
117 | 0 | mWeakPtr = do_GetWeakReference(static_cast<Base*>(aObject)); |
118 | 0 | mCache = aObject; |
119 | 0 | } |
120 | | explicit CachedWeakPtr<T, Base>(const nsCOMPtr<T>& aOther) |
121 | | { |
122 | | mWeakPtr = do_GetWeakReference(static_cast<Base*>(aOther.get())); |
123 | | mCache = aOther; |
124 | | } |
125 | | explicit CachedWeakPtr<T, Base>(already_AddRefed<T>& aOther) |
126 | | { |
127 | | RefPtr<T> other = aOther; |
128 | | mWeakPtr = do_GetWeakReference(static_cast<Base*>(other.get())); |
129 | | mCache = other; |
130 | | } |
131 | | |
132 | | CachedWeakPtr<T, Base>& operator=(T* aObject) |
133 | | { |
134 | | mWeakPtr = do_GetWeakReference(static_cast<Base*>(aObject)); |
135 | | mCache = aObject; |
136 | | return *this; |
137 | | } |
138 | | CachedWeakPtr<T, Base>& operator=(const nsCOMPtr<T>& aOther) |
139 | | { |
140 | | mWeakPtr = do_GetWeakReference(static_cast<Base*>(aOther.get())); |
141 | | mCache = aOther; |
142 | | return *this; |
143 | | } |
144 | | CachedWeakPtr<T, Base>& operator=(already_AddRefed<T>& aOther) |
145 | | { |
146 | | RefPtr<T> other = aOther; |
147 | | mWeakPtr = do_GetWeakReference(static_cast<Base*>(other.get())); |
148 | | mCache = other; |
149 | | return *this; |
150 | | } |
151 | | |
152 | | bool IsAlive() const { return mWeakPtr && mWeakPtr->IsAlive(); } |
153 | | |
154 | | explicit operator bool() const { return mWeakPtr; } |
155 | | operator T*() const { return get(); } |
156 | | T* get() const |
157 | 0 | { |
158 | 0 | if (mCache && !mWeakPtr->IsAlive()) { |
159 | 0 | const_cast<CachedWeakPtr<T, Base>*>(this)->mCache = nullptr; |
160 | 0 | } |
161 | 0 | return mCache; |
162 | 0 | } |
163 | | |
164 | | private: |
165 | | nsWeakPtr mWeakPtr; |
166 | | T* MOZ_NON_OWNING_REF mCache; |
167 | | }; |
168 | | |
169 | 0 | #define kMOZEditorBogusNodeAttrAtom nsGkAtoms::mozeditorbogusnode |
170 | 0 | #define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE") |
171 | | |
172 | | /** |
173 | | * SplitAtEdges is for EditorBase::SplitNodeDeepWithTransaction(), |
174 | | * HTMLEditor::InsertNodeAtPoint() |
175 | | */ |
176 | | enum class SplitAtEdges |
177 | | { |
178 | | // EditorBase::SplitNodeDeepWithTransaction() won't split container element |
179 | | // nodes at their edges. I.e., when split point is start or end of |
180 | | // container, it won't be split. |
181 | | eDoNotCreateEmptyContainer, |
182 | | // EditorBase::SplitNodeDeepWithTransaction() always splits containers even |
183 | | // if the split point is at edge of a container. E.g., if split point is |
184 | | // start of an inline element, empty inline element is created as a new left |
185 | | // node. |
186 | | eAllowToCreateEmptyContainer, |
187 | | }; |
188 | | |
189 | | /** |
190 | | * Implementation of an editor object. it will be the controller/focal point |
191 | | * for the main editor services. i.e. the GUIManager, publishing, transaction |
192 | | * manager, event interfaces. the idea for the event interfaces is to have them |
193 | | * delegate the actual commands to the editor independent of the XPFE |
194 | | * implementation. |
195 | | */ |
196 | | class EditorBase : public nsIEditor |
197 | | , public nsISelectionListener |
198 | | , public nsSupportsWeakReference |
199 | | { |
200 | | public: |
201 | | /**************************************************************************** |
202 | | * NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other |
203 | | * classes under libeditor except EditorEventListener and |
204 | | * HTMLEditorEventListener because each public method which may fire |
205 | | * eEditorInput event will need to instantiate new stack class for |
206 | | * managing input type value of eEditorInput and cache some objects |
207 | | * for smarter handling. In other words, when you add new root |
208 | | * method to edit the DOM tree, you can make your new method public. |
209 | | ****************************************************************************/ |
210 | | |
211 | | typedef dom::Element Element; |
212 | | typedef dom::Selection Selection; |
213 | | typedef dom::Text Text; |
214 | | |
215 | | NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
216 | | NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor) |
217 | | |
218 | | // nsIEditor methods |
219 | | NS_DECL_NSIEDITOR |
220 | | |
221 | | // nsISelectionListener method |
222 | | NS_DECL_NSISELECTIONLISTENER |
223 | | |
224 | | /** |
225 | | * The default constructor. This should suffice. the setting of the |
226 | | * interfaces is done after the construction of the editor class. |
227 | | */ |
228 | | EditorBase(); |
229 | | |
230 | | /** |
231 | | * Init is to tell the implementation of nsIEditor to begin its services |
232 | | * @param aDoc The dom document interface being observed |
233 | | * @param aRoot This is the root of the editable section of this |
234 | | * document. If it is null then we get root |
235 | | * from document body. |
236 | | * @param aSelCon this should be used to get the selection location |
237 | | * (will be null for HTML editors) |
238 | | * @param aFlags A bitmask of flags for specifying the behavior |
239 | | * of the editor. |
240 | | */ |
241 | | virtual nsresult Init(nsIDocument& doc, |
242 | | Element* aRoot, |
243 | | nsISelectionController* aSelCon, |
244 | | uint32_t aFlags, |
245 | | const nsAString& aInitialValue); |
246 | | |
247 | | /** |
248 | | * PostCreate should be called after Init, and is the time that the editor |
249 | | * tells its documentStateObservers that the document has been created. |
250 | | */ |
251 | | nsresult PostCreate(); |
252 | | |
253 | | /** |
254 | | * PreDestroy is called before the editor goes away, and gives the editor a |
255 | | * chance to tell its documentStateObservers that the document is going away. |
256 | | * @param aDestroyingFrames set to true when the frames being edited |
257 | | * are being destroyed (so there is no need to modify any nsISelections, |
258 | | * nor is it safe to do so) |
259 | | */ |
260 | | virtual void PreDestroy(bool aDestroyingFrames); |
261 | | |
262 | 0 | bool IsInitialized() const { return !!mDocument; } |
263 | 0 | bool Destroyed() const { return mDidPreDestroy; } |
264 | | |
265 | 0 | nsIDocument* GetDocument() const { return mDocument; } |
266 | | |
267 | | nsIPresShell* GetPresShell() const |
268 | 0 | { |
269 | 0 | return mDocument ? mDocument->GetShell() : nullptr; |
270 | 0 | } |
271 | | nsPresContext* GetPresContext() const |
272 | 0 | { |
273 | 0 | nsIPresShell* presShell = GetPresShell(); |
274 | 0 | return presShell ? presShell->GetPresContext() : nullptr; |
275 | 0 | } |
276 | | |
277 | | already_AddRefed<nsIWidget> GetWidget(); |
278 | | |
279 | | nsISelectionController* GetSelectionController() const |
280 | 0 | { |
281 | 0 | if (mSelectionController) { |
282 | 0 | return mSelectionController; |
283 | 0 | } |
284 | 0 | if (!mDocument) { |
285 | 0 | return nullptr; |
286 | 0 | } |
287 | 0 | nsIPresShell* presShell = mDocument->GetShell(); |
288 | 0 | if (!presShell) { |
289 | 0 | return nullptr; |
290 | 0 | } |
291 | 0 | nsISelectionController* sc = static_cast<PresShell*>(presShell); |
292 | 0 | return sc; |
293 | 0 | } |
294 | | |
295 | | nsresult GetSelection(SelectionType aSelectionType, |
296 | | Selection** aSelection) const; |
297 | | |
298 | | Selection* GetSelection(SelectionType aSelectionType = |
299 | | SelectionType::eNormal) const |
300 | 0 | { |
301 | 0 | nsISelectionController* sc = GetSelectionController(); |
302 | 0 | if (!sc) { |
303 | 0 | return nullptr; |
304 | 0 | } |
305 | 0 | Selection* selection = sc->GetSelection(ToRawSelectionType(aSelectionType)); |
306 | 0 | return selection; |
307 | 0 | } |
308 | | |
309 | | /** |
310 | | * Fast non-refcounting editor root element accessor |
311 | | */ |
312 | 0 | Element* GetRoot() const { return mRootElement; } |
313 | | |
314 | 0 | RangeUpdater& RangeUpdaterRef() { return mRangeUpdater; } |
315 | | |
316 | | /** |
317 | | * Set or unset TextInputListener. If setting non-nullptr when the editor |
318 | | * already has a TextInputListener, this will crash in debug build. |
319 | | */ |
320 | | void SetTextInputListener(TextInputListener* aTextInputListener); |
321 | | |
322 | | /** |
323 | | * Set or unset IMEContentObserver. If setting non-nullptr when the editor |
324 | | * already has an IMEContentObserver, this will crash in debug build. |
325 | | */ |
326 | | void SetIMEContentObserver(IMEContentObserver* aIMEContentObserver); |
327 | | |
328 | | /** |
329 | | * Returns current composition. |
330 | | */ |
331 | | TextComposition* GetComposition() const; |
332 | | |
333 | | /** |
334 | | * Get preferred IME status of current widget. |
335 | | */ |
336 | | virtual nsresult GetPreferredIMEState(widget::IMEState* aState); |
337 | | |
338 | | /** |
339 | | * Returns true if there is composition string and not fixed. |
340 | | */ |
341 | | bool IsIMEComposing() const; |
342 | | |
343 | | /** |
344 | | * Commit composition if there is. |
345 | | * Note that when there is a composition, this requests to commit composition |
346 | | * to native IME. Therefore, when there is composition, this can do anything. |
347 | | * For example, the editor instance, the widget or the process itself may |
348 | | * be destroyed. |
349 | | */ |
350 | | nsresult CommitComposition(); |
351 | | |
352 | | /** |
353 | | * ToggleTextDirection() toggles text-direction of the root element. |
354 | | */ |
355 | | nsresult ToggleTextDirection(); |
356 | | |
357 | | /** |
358 | | * SwitchTextDirectionTo() sets the text-direction of the root element to |
359 | | * LTR or RTL. |
360 | | */ |
361 | | enum class TextDirection |
362 | | { |
363 | | eLTR, |
364 | | eRTL, |
365 | | }; |
366 | | void SwitchTextDirectionTo(TextDirection aTextDirection); |
367 | | |
368 | | /** |
369 | | * Finalizes selection and caret for the editor. |
370 | | */ |
371 | | nsresult FinalizeSelection(); |
372 | | |
373 | | /** |
374 | | * Returns true if selection is in an editable element and both the range |
375 | | * start and the range end are editable. E.g., even if the selection range |
376 | | * includes non-editable elements, returns true when one of common ancestors |
377 | | * of the range start and the range end is editable. Otherwise, false. |
378 | | */ |
379 | | bool IsSelectionEditable(); |
380 | | |
381 | | /** |
382 | | * Returns number of undo or redo items. |
383 | | */ |
384 | | size_t NumberOfUndoItems() const |
385 | 0 | { |
386 | 0 | return mTransactionManager ? mTransactionManager->NumberOfUndoItems() : 0; |
387 | 0 | } |
388 | | size_t NumberOfRedoItems() const |
389 | 0 | { |
390 | 0 | return mTransactionManager ? mTransactionManager->NumberOfRedoItems() : 0; |
391 | 0 | } |
392 | | |
393 | | /** |
394 | | * Returns number of maximum undo/redo transactions. |
395 | | */ |
396 | | int32_t NumberOfMaximumTransactions() const |
397 | 0 | { |
398 | 0 | return mTransactionManager ? |
399 | 0 | mTransactionManager->NumberOfMaximumTransactions() : 0; |
400 | 0 | } |
401 | | |
402 | | /** |
403 | | * Returns true if this editor can store transactions for undo/redo. |
404 | | */ |
405 | | bool IsUndoRedoEnabled() const |
406 | 0 | { |
407 | 0 | return mTransactionManager && |
408 | 0 | mTransactionManager->NumberOfMaximumTransactions(); |
409 | 0 | } |
410 | | |
411 | | /** |
412 | | * Return true if it's possible to undo/redo right now. |
413 | | */ |
414 | | bool CanUndo() const |
415 | 0 | { |
416 | 0 | return IsUndoRedoEnabled() && NumberOfUndoItems() > 0; |
417 | 0 | } |
418 | | bool CanRedo() const |
419 | 0 | { |
420 | 0 | return IsUndoRedoEnabled() && NumberOfRedoItems() > 0; |
421 | 0 | } |
422 | | |
423 | | /** |
424 | | * Enables or disables undo/redo feature. Returns true if it succeeded, |
425 | | * otherwise, e.g., we're undoing or redoing, returns false. |
426 | | */ |
427 | | bool EnableUndoRedo(int32_t aMaxTransactionCount = -1) |
428 | 0 | { |
429 | 0 | if (!mTransactionManager) { |
430 | 0 | mTransactionManager = new TransactionManager(); |
431 | 0 | } |
432 | 0 | return mTransactionManager->EnableUndoRedo(aMaxTransactionCount); |
433 | 0 | } |
434 | | bool DisableUndoRedo() |
435 | 0 | { |
436 | 0 | if (!mTransactionManager) { |
437 | 0 | return true; |
438 | 0 | } |
439 | 0 | return mTransactionManager->DisableUndoRedo(); |
440 | 0 | } |
441 | | bool ClearUndoRedo() |
442 | 0 | { |
443 | 0 | if (!mTransactionManager) { |
444 | 0 | return true; |
445 | 0 | } |
446 | 0 | return mTransactionManager->ClearUndoRedo(); |
447 | 0 | } |
448 | | |
449 | | /** |
450 | | * Adds or removes transaction listener to or from the transaction manager. |
451 | | * Note that TransactionManager does not check if the listener is in the |
452 | | * array. So, caller of AddTransactionListener() needs to manage if it's |
453 | | * already been registered to the transaction manager. |
454 | | */ |
455 | | bool AddTransactionListener(nsITransactionListener& aListener) |
456 | 0 | { |
457 | 0 | if (!mTransactionManager) { |
458 | 0 | return false; |
459 | 0 | } |
460 | 0 | return mTransactionManager->AddTransactionListener(aListener); |
461 | 0 | } |
462 | | bool RemoveTransactionListener(nsITransactionListener& aListener) |
463 | 0 | { |
464 | 0 | if (!mTransactionManager) { |
465 | 0 | return false; |
466 | 0 | } |
467 | 0 | return mTransactionManager->RemoveTransactionListener(aListener); |
468 | 0 | } |
469 | | |
470 | | virtual nsresult HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent); |
471 | | |
472 | | virtual dom::EventTarget* GetDOMEventTarget() = 0; |
473 | | |
474 | | /** |
475 | | * Accessor methods to flags. |
476 | | */ |
477 | 0 | uint32_t Flags() const { return mFlags; } |
478 | | |
479 | | nsresult AddFlags(uint32_t aFlags) |
480 | 0 | { |
481 | 0 | const uint32_t kOldFlags = Flags(); |
482 | 0 | const uint32_t kNewFlags = (kOldFlags | aFlags); |
483 | 0 | if (kNewFlags == kOldFlags) { |
484 | 0 | return NS_OK; |
485 | 0 | } |
486 | 0 | return SetFlags(kNewFlags); // virtual call and may be expensive. |
487 | 0 | } |
488 | | nsresult RemoveFlags(uint32_t aFlags) |
489 | 0 | { |
490 | 0 | const uint32_t kOldFlags = Flags(); |
491 | 0 | const uint32_t kNewFlags = (kOldFlags & ~aFlags); |
492 | 0 | if (kNewFlags == kOldFlags) { |
493 | 0 | return NS_OK; |
494 | 0 | } |
495 | 0 | return SetFlags(kNewFlags); // virtual call and may be expensive. |
496 | 0 | } |
497 | | nsresult AddAndRemoveFlags(uint32_t aAddingFlags, uint32_t aRemovingFlags) |
498 | 0 | { |
499 | 0 | MOZ_ASSERT(!(aAddingFlags & aRemovingFlags), |
500 | 0 | "Same flags are specified both adding and removing"); |
501 | 0 | const uint32_t kOldFlags = Flags(); |
502 | 0 | const uint32_t kNewFlags = ((kOldFlags | aAddingFlags) & ~aRemovingFlags); |
503 | 0 | if (kNewFlags == kOldFlags) { |
504 | 0 | return NS_OK; |
505 | 0 | } |
506 | 0 | return SetFlags(kNewFlags); // virtual call and may be expensive. |
507 | 0 | } |
508 | | |
509 | | bool IsPlaintextEditor() const |
510 | 0 | { |
511 | 0 | return (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0; |
512 | 0 | } |
513 | | |
514 | | bool IsSingleLineEditor() const |
515 | 0 | { |
516 | 0 | return (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) != 0; |
517 | 0 | } |
518 | | |
519 | | bool IsPasswordEditor() const |
520 | 0 | { |
521 | 0 | return (mFlags & nsIPlaintextEditor::eEditorPasswordMask) != 0; |
522 | 0 | } |
523 | | |
524 | | // FYI: Both IsRightToLeft() and IsLeftToRight() may return false if |
525 | | // the editor inherits the content node's direction. |
526 | | bool IsRightToLeft() const |
527 | 0 | { |
528 | 0 | return (mFlags & nsIPlaintextEditor::eEditorRightToLeft) != 0; |
529 | 0 | } |
530 | | bool IsLeftToRight() const |
531 | 0 | { |
532 | 0 | return (mFlags & nsIPlaintextEditor::eEditorLeftToRight) != 0; |
533 | 0 | } |
534 | | |
535 | | bool IsReadonly() const |
536 | 0 | { |
537 | 0 | return (mFlags & nsIPlaintextEditor::eEditorReadonlyMask) != 0; |
538 | 0 | } |
539 | | |
540 | | bool IsDisabled() const |
541 | 0 | { |
542 | 0 | return (mFlags & nsIPlaintextEditor::eEditorDisabledMask) != 0; |
543 | 0 | } |
544 | | |
545 | | bool IsInputFiltered() const |
546 | 0 | { |
547 | 0 | return (mFlags & nsIPlaintextEditor::eEditorFilterInputMask) != 0; |
548 | 0 | } |
549 | | |
550 | | bool IsMailEditor() const |
551 | 0 | { |
552 | 0 | return (mFlags & nsIPlaintextEditor::eEditorMailMask) != 0; |
553 | 0 | } |
554 | | |
555 | | bool IsWrapHackEnabled() const |
556 | 0 | { |
557 | 0 | return (mFlags & nsIPlaintextEditor::eEditorEnableWrapHackMask) != 0; |
558 | 0 | } |
559 | | |
560 | | bool IsFormWidget() const |
561 | 0 | { |
562 | 0 | return (mFlags & nsIPlaintextEditor::eEditorWidgetMask) != 0; |
563 | 0 | } |
564 | | |
565 | | bool NoCSS() const |
566 | 0 | { |
567 | 0 | return (mFlags & nsIPlaintextEditor::eEditorNoCSSMask) != 0; |
568 | 0 | } |
569 | | |
570 | | bool IsInteractionAllowed() const |
571 | 0 | { |
572 | 0 | return (mFlags & nsIPlaintextEditor::eEditorAllowInteraction) != 0; |
573 | 0 | } |
574 | | |
575 | | bool DontEchoPassword() const |
576 | 0 | { |
577 | 0 | return (mFlags & nsIPlaintextEditor::eEditorDontEchoPassword) != 0; |
578 | 0 | } |
579 | | |
580 | | bool ShouldSkipSpellCheck() const |
581 | 0 | { |
582 | 0 | return (mFlags & nsIPlaintextEditor::eEditorSkipSpellCheck) != 0; |
583 | 0 | } |
584 | | |
585 | | bool IsTabbable() const |
586 | 0 | { |
587 | 0 | return IsSingleLineEditor() || IsPasswordEditor() || IsFormWidget() || |
588 | 0 | IsInteractionAllowed(); |
589 | 0 | } |
590 | | |
591 | | bool HasIndependentSelection() const |
592 | 0 | { |
593 | 0 | return !!mSelectionController; |
594 | 0 | } |
595 | | |
596 | | bool IsModifiable() const |
597 | 0 | { |
598 | 0 | return !IsReadonly(); |
599 | 0 | } |
600 | | |
601 | | /** |
602 | | * IsInEditSubAction() return true while the instance is handling an edit |
603 | | * sub-action. Otherwise, false. |
604 | | */ |
605 | 0 | bool IsInEditSubAction() const { return mIsInEditSubAction; } |
606 | | |
607 | | /** |
608 | | * SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching |
609 | | * "input" event. |
610 | | */ |
611 | | void SuppressDispatchingInputEvent(bool aSuppress) |
612 | 0 | { |
613 | 0 | mDispatchInputEvent = !aSuppress; |
614 | 0 | } |
615 | | |
616 | | /** |
617 | | * IsSuppressingDispatchingInputEvent() returns true if the editor stops |
618 | | * dispatching input event. Otherwise, false. |
619 | | */ |
620 | | bool IsSuppressingDispatchingInputEvent() const |
621 | 0 | { |
622 | 0 | return !mDispatchInputEvent; |
623 | 0 | } |
624 | | |
625 | | /** |
626 | | * Returns true if markNodeDirty() has any effect. Returns false if |
627 | | * markNodeDirty() is a no-op. |
628 | | */ |
629 | | bool OutputsMozDirty() const |
630 | 0 | { |
631 | 0 | // Return true for Composer (!IsInteractionAllowed()) or mail |
632 | 0 | // (IsMailEditor()), but false for webpages. |
633 | 0 | return !IsInteractionAllowed() || IsMailEditor(); |
634 | 0 | } |
635 | | |
636 | | /** |
637 | | * Get the focused content, if we're focused. Returns null otherwise. |
638 | | */ |
639 | | virtual nsIContent* GetFocusedContent(); |
640 | | |
641 | | /** |
642 | | * Get the focused content for the argument of some IMEStateManager's |
643 | | * methods. |
644 | | */ |
645 | | virtual already_AddRefed<nsIContent> GetFocusedContentForIME(); |
646 | | |
647 | | /** |
648 | | * Whether the aGUIEvent should be handled by this editor or not. When this |
649 | | * returns false, The aGUIEvent shouldn't be handled on this editor, |
650 | | * i.e., The aGUIEvent should be handled by another inner editor or ancestor |
651 | | * elements. |
652 | | */ |
653 | | virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent); |
654 | | |
655 | | /** |
656 | | * FindSelectionRoot() returns a selection root of this editor when aNode |
657 | | * gets focus. aNode must be a content node or a document node. When the |
658 | | * target isn't a part of this editor, returns nullptr. If this is for |
659 | | * designMode, you should set the document node to aNode except that an |
660 | | * element in the document has focus. |
661 | | */ |
662 | | virtual already_AddRefed<nsIContent> FindSelectionRoot(nsINode* aNode); |
663 | | |
664 | | /** |
665 | | * This method has to be called by EditorEventListener::Focus. |
666 | | * All actions that have to be done when the editor is focused needs to be |
667 | | * added here. |
668 | | */ |
669 | | void OnFocus(dom::EventTarget* aFocusEventTarget); |
670 | | |
671 | | /** Resyncs spellchecking state (enabled/disabled). This should be called |
672 | | * when anything that affects spellchecking state changes, such as the |
673 | | * spellcheck attribute value. |
674 | | */ |
675 | | void SyncRealTimeSpell(); |
676 | | |
677 | | /** |
678 | | * This method re-initializes the selection and caret state that are for |
679 | | * current editor state. When editor session is destroyed, it always reset |
680 | | * selection state even if this has no focus. So if destroying editor, |
681 | | * we have to call this method for focused editor to set selection state. |
682 | | */ |
683 | | void ReinitializeSelection(Element& aElement); |
684 | | |
685 | | protected: // May be called by friends. |
686 | | /**************************************************************************** |
687 | | * Some classes like TextEditRules, HTMLEditRules, WSRunObject which are |
688 | | * part of handling edit actions are allowed to call the following protected |
689 | | * methods. However, those methods won't prepare caches of some objects |
690 | | * which are necessary for them. So, if you want some following methods |
691 | | * to do that for you, you need to create a wrapper method in public scope |
692 | | * and call it. |
693 | | ****************************************************************************/ |
694 | | |
695 | | /** |
696 | | * InsertTextWithTransaction() inserts aStringToInsert to aPointToInsert or |
697 | | * better insertion point around it. If aPointToInsert isn't in a text node, |
698 | | * this method looks for the nearest point in a text node with |
699 | | * FindBetterInsertionPoint(). If there is no text node, this creates |
700 | | * new text node and put aStringToInsert to it. |
701 | | * |
702 | | * @param aDocument The document of this editor. |
703 | | * @param aStringToInsert The string to insert. |
704 | | * @param aPointToInser The point to insert aStringToInsert. |
705 | | * Must be valid DOM point. |
706 | | * @param aPointAfterInsertedString |
707 | | * The point after inserted aStringToInsert. |
708 | | * So, when this method actually inserts string, |
709 | | * this is set to a point in the text node. |
710 | | * Otherwise, this may be set to aPointToInsert. |
711 | | * @return When this succeeds to insert the string or |
712 | | * does nothing during composition, returns NS_OK. |
713 | | * Otherwise, an error code. |
714 | | */ |
715 | | virtual nsresult |
716 | | InsertTextWithTransaction(nsIDocument& aDocument, |
717 | | const nsAString& aStringToInsert, |
718 | | const EditorRawDOMPoint& aPointToInsert, |
719 | | EditorRawDOMPoint* aPointAfterInsertedString = |
720 | | nullptr); |
721 | | |
722 | | /** |
723 | | * InsertTextIntoTextNodeWithTransaction() inserts aStringToInsert into |
724 | | * aOffset of aTextNode with transaction. |
725 | | * |
726 | | * @param aStringToInsert String to be inserted. |
727 | | * @param aTextNode Text node to contain aStringToInsert. |
728 | | * @param aOffset Offset at insertion point in aTextNode. |
729 | | * @param aSuppressIME true if it's not a part of IME composition. |
730 | | * E.g., adjusting whitespaces during composition. |
731 | | * false, otherwise. |
732 | | */ |
733 | | nsresult |
734 | | InsertTextIntoTextNodeWithTransaction(const nsAString& aStringToInsert, |
735 | | Text& aTextNode, int32_t aOffset, |
736 | | bool aSuppressIME = false); |
737 | | |
738 | | nsresult SetTextImpl(Selection& aSelection, |
739 | | const nsAString& aString, |
740 | | Text& aTextNode); |
741 | | |
742 | | /** |
743 | | * DeleteNodeWithTransaction() removes aNode from the DOM tree. |
744 | | * |
745 | | * @param aNode The node which will be removed form the DOM tree. |
746 | | */ |
747 | | nsresult DeleteNodeWithTransaction(nsINode& aNode); |
748 | | |
749 | | /** |
750 | | * InsertNodeWithTransaction() inserts aContentToInsert before the child |
751 | | * specified by aPointToInsert. |
752 | | * |
753 | | * @param aContentToInsert The node to be inserted. |
754 | | * @param aPointToInsert The insertion point of aContentToInsert. |
755 | | * If this refers end of the container, the |
756 | | * transaction will append the node to the |
757 | | * container. Otherwise, will insert the node |
758 | | * before child node referred by this. |
759 | | */ |
760 | | template<typename PT, typename CT> |
761 | | nsresult |
762 | | InsertNodeWithTransaction(nsIContent& aContentToInsert, |
763 | | const EditorDOMPointBase<PT, CT>& aPointToInsert); |
764 | | |
765 | | /** |
766 | | * ReplaceContainerWithTransaction() creates new element whose name is |
767 | | * aTagName, moves all children in aOldContainer to the new element, then, |
768 | | * removes aOldContainer from the DOM tree. |
769 | | * |
770 | | * @param aOldContainer The element node which should be replaced |
771 | | * with new element. |
772 | | * @param aTagName The name of new element node. |
773 | | */ |
774 | | already_AddRefed<Element> |
775 | | ReplaceContainerWithTransaction(Element& aOldContainer, |
776 | | nsAtom& aTagName) |
777 | 0 | { |
778 | 0 | return ReplaceContainerWithTransactionInternal(aOldContainer, aTagName, |
779 | 0 | *nsGkAtoms::_empty, |
780 | 0 | EmptyString(), false); |
781 | 0 | } |
782 | | |
783 | | /** |
784 | | * ReplaceContainerAndCloneAttributesWithTransaction() creates new element |
785 | | * whose name is aTagName, copies all attributes from aOldContainer to the |
786 | | * new element, moves all children in aOldContainer to the new element, then, |
787 | | * removes aOldContainer from the DOM tree. |
788 | | * |
789 | | * @param aOldContainer The element node which should be replaced |
790 | | * with new element. |
791 | | * @param aTagName The name of new element node. |
792 | | */ |
793 | | already_AddRefed<Element> |
794 | | ReplaceContainerAndCloneAttributesWithTransaction(Element& aOldContainer, |
795 | | nsAtom& aTagName) |
796 | 0 | { |
797 | 0 | return ReplaceContainerWithTransactionInternal(aOldContainer, aTagName, |
798 | 0 | *nsGkAtoms::_empty, |
799 | 0 | EmptyString(), true); |
800 | 0 | } |
801 | | |
802 | | /** |
803 | | * ReplaceContainerWithTransaction() creates new element whose name is |
804 | | * aTagName, sets aAttributes of the new element to aAttributeValue, moves |
805 | | * all children in aOldContainer to the new element, then, removes |
806 | | * aOldContainer from the DOM tree. |
807 | | * |
808 | | * @param aOldContainer The element node which should be replaced |
809 | | * with new element. |
810 | | * @param aTagName The name of new element node. |
811 | | * @param aAttribute Attribute name to be set to the new element. |
812 | | * @param aAttributeValue Attribute value to be set to aAttribute. |
813 | | */ |
814 | | already_AddRefed<Element> |
815 | | ReplaceContainerWithTransaction(Element& aOldContainer, |
816 | | nsAtom& aTagName, |
817 | | nsAtom& aAttribute, |
818 | | const nsAString& aAttributeValue) |
819 | 0 | { |
820 | 0 | return ReplaceContainerWithTransactionInternal(aOldContainer, aTagName, |
821 | 0 | aAttribute, |
822 | 0 | aAttributeValue, false); |
823 | 0 | } |
824 | | |
825 | | /** |
826 | | * CloneAttributesWithTransaction() clones all attributes from |
827 | | * aSourceElement to aDestElement after removing all attributes in |
828 | | * aDestElement. |
829 | | */ |
830 | | void CloneAttributesWithTransaction(Element& aDestElement, |
831 | | Element& aSourceElement); |
832 | | |
833 | | /** |
834 | | * RemoveContainerWithTransaction() removes aElement from the DOM tree and |
835 | | * moves all its children to the parent of aElement. |
836 | | * |
837 | | * @param aElement The element to be removed. |
838 | | */ |
839 | | nsresult RemoveContainerWithTransaction(Element& aElement); |
840 | | |
841 | | /** |
842 | | * InsertContainerWithTransaction() creates new element whose name is |
843 | | * aTagName, moves aContent into the new element, then, inserts the new |
844 | | * element into where aContent was. |
845 | | * Note that this method does not check if aContent is valid child of |
846 | | * the new element. So, callers need to guarantee it. |
847 | | * |
848 | | * @param aContent The content which will be wrapped with new |
849 | | * element. |
850 | | * @param aTagName Element name of new element which will wrap |
851 | | * aContent and be inserted into where aContent |
852 | | * was. |
853 | | * @return The new element. |
854 | | */ |
855 | | already_AddRefed<Element> |
856 | | InsertContainerWithTransaction(nsIContent& aContent, nsAtom& aTagName) |
857 | 0 | { |
858 | 0 | return InsertContainerWithTransactionInternal(aContent, aTagName, |
859 | 0 | *nsGkAtoms::_empty, |
860 | 0 | EmptyString()); |
861 | 0 | } |
862 | | |
863 | | /** |
864 | | * InsertContainerWithTransaction() creates new element whose name is |
865 | | * aTagName, sets its aAttribute to aAttributeValue, moves aContent into the |
866 | | * new element, then, inserts the new element into where aContent was. |
867 | | * Note that this method does not check if aContent is valid child of |
868 | | * the new element. So, callers need to guarantee it. |
869 | | * |
870 | | * @param aContent The content which will be wrapped with new |
871 | | * element. |
872 | | * @param aTagName Element name of new element which will wrap |
873 | | * aContent and be inserted into where aContent |
874 | | * was. |
875 | | * @param aAttribute Attribute which should be set to the new |
876 | | * element. |
877 | | * @param aAttributeValue Value to be set to aAttribute. |
878 | | * @return The new element. |
879 | | */ |
880 | | already_AddRefed<Element> |
881 | | InsertContainerWithTransaction(nsIContent& aContent, nsAtom& aTagName, |
882 | | nsAtom& aAttribute, |
883 | | const nsAString& aAttributeValue) |
884 | 0 | { |
885 | 0 | return InsertContainerWithTransactionInternal(aContent, aTagName, |
886 | 0 | aAttribute, aAttributeValue); |
887 | 0 | } |
888 | | |
889 | | /** |
890 | | * SplitNodeWithTransaction() creates a transaction to create a new node |
891 | | * (left node) identical to an existing node (right node), and split the |
892 | | * contents between the same point in both nodes, then, execute the |
893 | | * transaction. |
894 | | * |
895 | | * @param aStartOfRightNode The point to split. Its container will be |
896 | | * the right node, i.e., become the new node's |
897 | | * next sibling. And the point will be start |
898 | | * of the right node. |
899 | | * @param aError If succeed, returns no error. Otherwise, an |
900 | | * error. |
901 | | */ |
902 | | template<typename PT, typename CT> |
903 | | already_AddRefed<nsIContent> |
904 | | SplitNodeWithTransaction(const EditorDOMPointBase<PT, CT>& aStartOfRightNode, |
905 | | ErrorResult& aResult); |
906 | | |
907 | | /** |
908 | | * JoinNodesWithTransaction() joins aLeftNode and aRightNode. Content of |
909 | | * aLeftNode will be merged into aRightNode. Actual implemenation of this |
910 | | * method is JoinNodesImpl(). So, see its explanation for the detail. |
911 | | * |
912 | | * @param aLeftNode Will be removed from the DOM tree. |
913 | | * @param aRightNode The node which will be new container of the content of |
914 | | * aLeftNode. |
915 | | */ |
916 | | nsresult JoinNodesWithTransaction(nsINode& aLeftNode, nsINode& aRightNode); |
917 | | |
918 | | /** |
919 | | * MoveNodeWithTransaction() moves aContent to aPointToInsert. |
920 | | * |
921 | | * @param aContent The node to be moved. |
922 | | */ |
923 | | template<typename PT, typename CT> |
924 | | nsresult |
925 | | MoveNodeWithTransaction(nsIContent& aContent, |
926 | | const EditorDOMPointBase<PT, CT>& aPointToInsert); |
927 | | |
928 | | /** |
929 | | * MoveNodeToEndWithTransaction() moves aContent to end of aNewContainer. |
930 | | * |
931 | | * @param aContent The node to be moved. |
932 | | * @param aNewContainer The new container which will contain aContent as |
933 | | * its last child. |
934 | | */ |
935 | | nsresult |
936 | | MoveNodeToEndWithTransaction(nsIContent& aContent, |
937 | | nsINode& aNewContainer) |
938 | 0 | { |
939 | 0 | EditorRawDOMPoint pointToInsert; |
940 | 0 | pointToInsert.SetToEndOf(&aNewContainer); |
941 | 0 | return MoveNodeWithTransaction(aContent, pointToInsert); |
942 | 0 | } |
943 | | |
944 | | /** |
945 | | * MoveAllChildren() moves all children of aContainer to before |
946 | | * aPointToInsert.GetChild(). |
947 | | * See explanation of MoveChildren() for the detail of the behavior. |
948 | | * |
949 | | * @param aContainer The container node whose all children should |
950 | | * be moved. |
951 | | * @param aPointToInsert The insertion point. The container must not |
952 | | * be a data node like a text node. |
953 | | * @param aError The result. If this succeeds to move children, |
954 | | * returns NS_OK. Otherwise, an error. |
955 | | */ |
956 | | void MoveAllChildren(nsINode& aContainer, |
957 | | const EditorRawDOMPoint& aPointToInsert, |
958 | | ErrorResult& aError); |
959 | | |
960 | | /** |
961 | | * MovePreviousSiblings() moves all siblings before aChild (i.e., aChild |
962 | | * won't be moved) to before aPointToInsert.GetChild(). |
963 | | * See explanation of MoveChildren() for the detail of the behavior. |
964 | | * |
965 | | * @param aChild The node which is next sibling of the last |
966 | | * node to be moved. |
967 | | * @param aPointToInsert The insertion point. The container must not |
968 | | * be a data node like a text node. |
969 | | * @param aError The result. If this succeeds to move children, |
970 | | * returns NS_OK. Otherwise, an error. |
971 | | */ |
972 | | void MovePreviousSiblings(nsIContent& aChild, |
973 | | const EditorRawDOMPoint& aPointToInsert, |
974 | | ErrorResult& aError); |
975 | | |
976 | | /** |
977 | | * MoveChildren() moves all children between aFirstChild and aLastChild to |
978 | | * before aPointToInsert.GetChild(). |
979 | | * If some children are moved to different container while this method |
980 | | * moves other children, they are just ignored. |
981 | | * If the child node referred by aPointToInsert is moved to different |
982 | | * container while this method moves children, returns error. |
983 | | * |
984 | | * @param aFirstChild The first child which should be moved to |
985 | | * aPointToInsert. |
986 | | * @param aLastChild The last child which should be moved. This |
987 | | * must be a sibling of aFirstChild and it should |
988 | | * be positioned after aFirstChild in the DOM tree |
989 | | * order. |
990 | | * @param aPointToInsert The insertion point. The container must not |
991 | | * be a data node like a text node. |
992 | | * @param aError The result. If this succeeds to move children, |
993 | | * returns NS_OK. Otherwise, an error. |
994 | | */ |
995 | | void MoveChildren(nsIContent& aFirstChild, |
996 | | nsIContent& aLastChild, |
997 | | const EditorRawDOMPoint& aPointToInsert, |
998 | | ErrorResult& aError); |
999 | | |
1000 | | /** |
1001 | | * CloneAttributeWithTransaction() copies aAttribute of aSourceElement to |
1002 | | * aDestElement. If aSourceElement doesn't have aAttribute, this removes |
1003 | | * aAttribute from aDestElement. |
1004 | | * |
1005 | | * @param aAttribute Attribute name to be cloned. |
1006 | | * @param aDestElement Element node which will be set aAttribute or |
1007 | | * whose aAttribute will be removed. |
1008 | | * @param aSourceElement Element node which provides the value of |
1009 | | * aAttribute in aDestElement. |
1010 | | */ |
1011 | | nsresult CloneAttributeWithTransaction(nsAtom& aAttribute, |
1012 | | Element& aDestElement, |
1013 | | Element& aSourceElement); |
1014 | | |
1015 | | /** |
1016 | | * RemoveAttributeWithTransaction() removes aAttribute from aElement. |
1017 | | * |
1018 | | * @param aElement Element node which will lose aAttribute. |
1019 | | * @param aAttribute Attribute name to be removed from aElement. |
1020 | | */ |
1021 | | nsresult RemoveAttributeWithTransaction(Element& aElement, |
1022 | | nsAtom& aAttribute); |
1023 | | |
1024 | | virtual nsresult RemoveAttributeOrEquivalent(Element* aElement, |
1025 | | nsAtom* aAttribute, |
1026 | | bool aSuppressTransaction) = 0; |
1027 | | |
1028 | | /** |
1029 | | * SetAttributeWithTransaction() sets aAttribute of aElement to aValue. |
1030 | | * |
1031 | | * @param aElement Element node which will have aAttribute. |
1032 | | * @param aAttribute Attribute name to be set. |
1033 | | * @param aValue Attribute value be set to aAttribute. |
1034 | | */ |
1035 | | nsresult SetAttributeWithTransaction(Element& aElement, |
1036 | | nsAtom& aAttribute, |
1037 | | const nsAString& aValue); |
1038 | | |
1039 | | virtual nsresult SetAttributeOrEquivalent(Element* aElement, |
1040 | | nsAtom* aAttribute, |
1041 | | const nsAString& aValue, |
1042 | | bool aSuppressTransaction) = 0; |
1043 | | |
1044 | | /** |
1045 | | * Method to replace certain CreateElementNS() calls. |
1046 | | * |
1047 | | * @param aTag Tag you want. |
1048 | | */ |
1049 | | already_AddRefed<Element> CreateHTMLContent(const nsAtom* aTag); |
1050 | | |
1051 | | /** |
1052 | | * Creates text node which is marked as "maybe modified frequently". |
1053 | | */ |
1054 | | static already_AddRefed<nsTextNode> CreateTextNode(nsIDocument& aDocument, |
1055 | | const nsAString& aData); |
1056 | | |
1057 | | /** |
1058 | | * Create an element node whose name is aTag at before aPointToInsert. When |
1059 | | * this succeed to create an element node, this sets aPointToInsert to the |
1060 | | * new element because the relation of child and offset may be broken. |
1061 | | * If the caller needs to collapse the selection to next to the new element |
1062 | | * node, it should call |aPointToInsert.AdvanceOffset()| after calling this. |
1063 | | * |
1064 | | * @param aTag The element name to create. |
1065 | | * @param aPointToInsert The insertion point of new element. If this refers |
1066 | | * end of the container or after, the transaction |
1067 | | * will append the element to the container. |
1068 | | * Otherwise, will insert the element before the |
1069 | | * child node referred by this. |
1070 | | * @return The created new element node. |
1071 | | */ |
1072 | | template<typename PT, typename CT> |
1073 | | already_AddRefed<Element> |
1074 | | CreateNodeWithTransaction(nsAtom& aTag, |
1075 | | const EditorDOMPointBase<PT, CT>& aPointToInsert); |
1076 | | |
1077 | | /** |
1078 | | * Create an aggregate transaction for delete selection. The result may |
1079 | | * include DeleteNodeTransactions and/or DeleteTextTransactions as its |
1080 | | * children. |
1081 | | * |
1082 | | * @param aAction The action caused removing the selection. |
1083 | | * @param aRemovingNode The node to be removed. |
1084 | | * @param aOffset The start offset of the range in aRemovingNode. |
1085 | | * @param aLength The length of the range in aRemovingNode. |
1086 | | * @return If it can remove the selection, returns an |
1087 | | * aggregate transaction which has some |
1088 | | * DeleteNodeTransactions and/or |
1089 | | * DeleteTextTransactions as its children. |
1090 | | */ |
1091 | | already_AddRefed<EditAggregateTransaction> |
1092 | | CreateTxnForDeleteSelection(EDirection aAction, |
1093 | | nsINode** aNode, |
1094 | | int32_t* aOffset, |
1095 | | int32_t* aLength); |
1096 | | |
1097 | | /** |
1098 | | * Create a transaction for removing the nodes and/or text in aRange. |
1099 | | * |
1100 | | * @param aRangeToDelete The range to be removed. |
1101 | | * @param aAction The action caused removing the range. |
1102 | | * @param aRemovingNode The node to be removed. |
1103 | | * @param aOffset The start offset of the range in aRemovingNode. |
1104 | | * @param aLength The length of the range in aRemovingNode. |
1105 | | * @return The transaction to remove the range. Its type |
1106 | | * is DeleteNodeTransaction or |
1107 | | * DeleteTextTransaction. |
1108 | | */ |
1109 | | already_AddRefed<EditTransactionBase> |
1110 | | CreateTxnForDeleteRange(nsRange* aRangeToDelete, |
1111 | | EDirection aAction, |
1112 | | nsINode** aRemovingNode, |
1113 | | int32_t* aOffset, |
1114 | | int32_t* aLength); |
1115 | | |
1116 | | /** |
1117 | | * DeleteTextWithTransaction() removes text in the range from aCharData. |
1118 | | * |
1119 | | * @param aCharData The data node which should be modified. |
1120 | | * @param aOffset Start offset of removing text in aCharData. |
1121 | | * @param aLength Length of removing text. |
1122 | | */ |
1123 | | nsresult DeleteTextWithTransaction(dom::CharacterData& aCharacterData, |
1124 | | uint32_t aOffset, uint32_t aLength); |
1125 | | |
1126 | | /** |
1127 | | * ReplaceContainerWithTransactionInternal() is implementation of |
1128 | | * ReplaceContainerWithTransaction() and |
1129 | | * ReplaceContainerAndCloneAttributesWithTransaction(). |
1130 | | * |
1131 | | * @param aOldContainer The element which will be replaced with new |
1132 | | * element. |
1133 | | * @param aTagName The name of new element node. |
1134 | | * @param aAttribute Attribute name which will be set to the new |
1135 | | * element. This will be ignored if |
1136 | | * aCloneAllAttributes is set to true. |
1137 | | * @param aAttributeValue Attribute value which will be set to |
1138 | | * aAttribute. |
1139 | | * @param aCloneAllAttributes If true, all attributes of aOldContainer will |
1140 | | * be copied to the new element. |
1141 | | */ |
1142 | | already_AddRefed<Element> |
1143 | | ReplaceContainerWithTransactionInternal(Element& aElement, |
1144 | | nsAtom& aTagName, |
1145 | | nsAtom& aAttribute, |
1146 | | const nsAString& aAttributeValue, |
1147 | | bool aCloneAllAttributes); |
1148 | | |
1149 | | /** |
1150 | | * InsertContainerWithTransactionInternal() creates new element whose name is |
1151 | | * aTagName, moves aContent into the new element, then, inserts the new |
1152 | | * element into where aContent was. If aAttribute is not nsGkAtoms::_empty, |
1153 | | * aAttribute of the new element will be set to aAttributeValue. |
1154 | | * |
1155 | | * @param aContent The content which will be wrapped with new |
1156 | | * element. |
1157 | | * @param aTagName Element name of new element which will wrap |
1158 | | * aContent and be inserted into where aContent |
1159 | | * was. |
1160 | | * @param aAttribute Attribute which should be set to the new |
1161 | | * element. If this is nsGkAtoms::_empty, |
1162 | | * this does not set any attributes to the new |
1163 | | * element. |
1164 | | * @param aAttributeValue Value to be set to aAttribute. |
1165 | | * @return The new element. |
1166 | | */ |
1167 | | already_AddRefed<Element> |
1168 | | InsertContainerWithTransactionInternal(nsIContent& aContent, |
1169 | | nsAtom& aTagName, |
1170 | | nsAtom& aAttribute, |
1171 | | const nsAString& aAttributeValue); |
1172 | | |
1173 | | /** |
1174 | | * DoSplitNode() creates a new node (left node) identical to an existing |
1175 | | * node (right node), and split the contents between the same point in both |
1176 | | * nodes. |
1177 | | * |
1178 | | * @param aStartOfRightNode The point to split. Its container will be |
1179 | | * the right node, i.e., become the new node's |
1180 | | * next sibling. And the point will be start |
1181 | | * of the right node. |
1182 | | * @param aNewLeftNode The new node called as left node, so, this |
1183 | | * becomes the container of aPointToSplit's |
1184 | | * previous sibling. |
1185 | | * @param aError Must have not already failed. |
1186 | | * If succeed to insert aLeftNode before the |
1187 | | * right node and remove unnecessary contents |
1188 | | * (and collapse selection at end of the left |
1189 | | * node if necessary), returns no error. |
1190 | | * Otherwise, an error. |
1191 | | */ |
1192 | | void DoSplitNode(const EditorDOMPoint& aStartOfRightNode, |
1193 | | nsIContent& aNewLeftNode, |
1194 | | ErrorResult& aError); |
1195 | | |
1196 | | /** |
1197 | | * DoJoinNodes() merges contents in aNodeToJoin to aNodeToKeep and remove |
1198 | | * aNodeToJoin from the DOM tree. aNodeToJoin and aNodeToKeep must have |
1199 | | * same parent, aParent. Additionally, if one of aNodeToJoin or aNodeToKeep |
1200 | | * is a text node, the other must be a text node. |
1201 | | * |
1202 | | * @param aNodeToKeep The node that will remain after the join. |
1203 | | * @param aNodeToJoin The node that will be joined with aNodeToKeep. |
1204 | | * There is no requirement that the two nodes be of the |
1205 | | * same type. |
1206 | | * @param aParent The parent of aNodeToKeep |
1207 | | */ |
1208 | | nsresult DoJoinNodes(nsINode* aNodeToKeep, |
1209 | | nsINode* aNodeToJoin, |
1210 | | nsINode* aParent); |
1211 | | |
1212 | | /** |
1213 | | * SplitNodeDeepWithTransaction() splits aMostAncestorToSplit deeply. |
1214 | | * |
1215 | | * @param aMostAncestorToSplit The most ancestor node which should be |
1216 | | * split. |
1217 | | * @param aStartOfDeepestRightNode The start point of deepest right node. |
1218 | | * This point must be descendant of |
1219 | | * aMostAncestorToSplit. |
1220 | | * @param aSplitAtEdges Whether the caller allows this to |
1221 | | * create empty container element when |
1222 | | * split point is start or end of an |
1223 | | * element. |
1224 | | * @return SplitPoint() returns split point in |
1225 | | * aMostAncestorToSplit. The point must |
1226 | | * be good to insert something if the |
1227 | | * caller want to do it. |
1228 | | */ |
1229 | | template<typename PT, typename CT> |
1230 | | SplitNodeResult |
1231 | | SplitNodeDeepWithTransaction( |
1232 | | nsIContent& aMostAncestorToSplit, |
1233 | | const EditorDOMPointBase<PT, CT>& aDeepestStartOfRightNode, |
1234 | | SplitAtEdges aSplitAtEdges); |
1235 | | |
1236 | | /** |
1237 | | * JoinNodesDeepWithTransaction() joins aLeftNode and aRightNode "deeply". |
1238 | | * First, they are joined simply, then, new right node is assumed as the |
1239 | | * child at length of the left node before joined and new left node is |
1240 | | * assumed as its previous sibling. Then, they will be joined again. |
1241 | | * And then, these steps are repeated. |
1242 | | * |
1243 | | * @param aLeftNode The node which will be removed form the tree. |
1244 | | * @param aRightNode The node which will be inserted the contents of |
1245 | | * aLeftNode. |
1246 | | * @return The point of the first child of the last right node. |
1247 | | */ |
1248 | | EditorDOMPoint JoinNodesDeepWithTransaction(nsIContent& aLeftNode, |
1249 | | nsIContent& aRightNode); |
1250 | | |
1251 | | /** |
1252 | | * Note that aSelection is optional and can be nullptr. |
1253 | | */ |
1254 | | nsresult DoTransaction(Selection* aSelection, |
1255 | | nsITransaction* aTxn); |
1256 | | |
1257 | | virtual bool IsBlockNode(nsINode* aNode); |
1258 | | |
1259 | | /** |
1260 | | * Set outOffset to the offset of aChild in the parent. |
1261 | | * Returns the parent of aChild. |
1262 | | */ |
1263 | | static nsINode* GetNodeLocation(nsINode* aChild, int32_t* aOffset); |
1264 | | |
1265 | | /** |
1266 | | * Get the previous node. |
1267 | | */ |
1268 | | nsIContent* GetPreviousNode(const EditorRawDOMPoint& aPoint) |
1269 | 0 | { |
1270 | 0 | return GetPreviousNodeInternal(aPoint, false, true, false); |
1271 | 0 | } |
1272 | | nsIContent* GetPreviousElementOrText(const EditorRawDOMPoint& aPoint) |
1273 | 0 | { |
1274 | 0 | return GetPreviousNodeInternal(aPoint, false, false, false); |
1275 | 0 | } |
1276 | | nsIContent* GetPreviousEditableNode(const EditorRawDOMPoint& aPoint) |
1277 | 0 | { |
1278 | 0 | return GetPreviousNodeInternal(aPoint, true, true, false); |
1279 | 0 | } |
1280 | | nsIContent* GetPreviousNodeInBlock(const EditorRawDOMPoint& aPoint) |
1281 | 0 | { |
1282 | 0 | return GetPreviousNodeInternal(aPoint, false, true, true); |
1283 | 0 | } |
1284 | | nsIContent* GetPreviousElementOrTextInBlock(const EditorRawDOMPoint& aPoint) |
1285 | 0 | { |
1286 | 0 | return GetPreviousNodeInternal(aPoint, false, false, true); |
1287 | 0 | } |
1288 | | nsIContent* GetPreviousEditableNodeInBlock( |
1289 | | const EditorRawDOMPoint& aPoint) |
1290 | 0 | { |
1291 | 0 | return GetPreviousNodeInternal(aPoint, true, true, true); |
1292 | 0 | } |
1293 | | nsIContent* GetPreviousNode(nsINode& aNode) |
1294 | 0 | { |
1295 | 0 | return GetPreviousNodeInternal(aNode, false, true, false); |
1296 | 0 | } |
1297 | | nsIContent* GetPreviousElementOrText(nsINode& aNode) |
1298 | 0 | { |
1299 | 0 | return GetPreviousNodeInternal(aNode, false, false, false); |
1300 | 0 | } |
1301 | | nsIContent* GetPreviousEditableNode(nsINode& aNode) |
1302 | 0 | { |
1303 | 0 | return GetPreviousNodeInternal(aNode, true, true, false); |
1304 | 0 | } |
1305 | | nsIContent* GetPreviousNodeInBlock(nsINode& aNode) |
1306 | 0 | { |
1307 | 0 | return GetPreviousNodeInternal(aNode, false, true, true); |
1308 | 0 | } |
1309 | | nsIContent* GetPreviousElementOrTextInBlock(nsINode& aNode) |
1310 | 0 | { |
1311 | 0 | return GetPreviousNodeInternal(aNode, false, false, true); |
1312 | 0 | } |
1313 | | nsIContent* GetPreviousEditableNodeInBlock(nsINode& aNode) |
1314 | 0 | { |
1315 | 0 | return GetPreviousNodeInternal(aNode, true, true, true); |
1316 | 0 | } |
1317 | | |
1318 | | /** |
1319 | | * Get the next node. |
1320 | | * |
1321 | | * Note that methods taking EditorRawDOMPoint behavior includes the |
1322 | | * child at offset as search target. E.g., following code causes infinite |
1323 | | * loop. |
1324 | | * |
1325 | | * EditorRawDOMPoint point(aEditableNode); |
1326 | | * while (nsIContent* content = GetNextEditableNode(point)) { |
1327 | | * // Do something... |
1328 | | * point.Set(content); |
1329 | | * } |
1330 | | * |
1331 | | * Following code must be you expected: |
1332 | | * |
1333 | | * while (nsIContent* content = GetNextEditableNode(point)) { |
1334 | | * // Do something... |
1335 | | * DebugOnly<bool> advanced = point.Advanced(); |
1336 | | * MOZ_ASSERT(advanced); |
1337 | | * point.Set(point.GetChild()); |
1338 | | * } |
1339 | | * |
1340 | | * On the other hand, the methods taking nsINode behavior must be what |
1341 | | * you want. They start to search the result from next node of the given |
1342 | | * node. |
1343 | | */ |
1344 | | template<typename PT, typename CT> |
1345 | | nsIContent* GetNextNode(const EditorDOMPointBase<PT, CT>& aPoint) |
1346 | 0 | { |
1347 | 0 | return GetNextNodeInternal(aPoint, false, true, false); |
1348 | 0 | } |
1349 | | template<typename PT, typename CT> |
1350 | | nsIContent* GetNextElementOrText(const EditorDOMPointBase<PT, CT>& aPoint) |
1351 | | { |
1352 | | return GetNextNodeInternal(aPoint, false, false, false); |
1353 | | } |
1354 | | template<typename PT, typename CT> |
1355 | | nsIContent* GetNextEditableNode(const EditorDOMPointBase<PT, CT>& aPoint) |
1356 | 0 | { |
1357 | 0 | return GetNextNodeInternal(aPoint, true, true, false); |
1358 | 0 | } Unexecuted instantiation: nsIContent* mozilla::EditorBase::GetNextEditableNode<nsINode*, nsIContent*>(mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&) Unexecuted instantiation: nsIContent* mozilla::EditorBase::GetNextEditableNode<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&) |
1359 | | template<typename PT, typename CT> |
1360 | | nsIContent* GetNextNodeInBlock(const EditorDOMPointBase<PT, CT>& aPoint) |
1361 | | { |
1362 | | return GetNextNodeInternal(aPoint, false, true, true); |
1363 | | } |
1364 | | template<typename PT, typename CT> |
1365 | | nsIContent* GetNextElementOrTextInBlock( |
1366 | | const EditorDOMPointBase<PT, CT>& aPoint) |
1367 | | { |
1368 | | return GetNextNodeInternal(aPoint, false, false, true); |
1369 | | } |
1370 | | template<typename PT, typename CT> |
1371 | | nsIContent* GetNextEditableNodeInBlock( |
1372 | | const EditorDOMPointBase<PT, CT>& aPoint) |
1373 | 0 | { |
1374 | 0 | return GetNextNodeInternal(aPoint, true, true, true); |
1375 | 0 | } Unexecuted instantiation: nsIContent* mozilla::EditorBase::GetNextEditableNodeInBlock<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&) Unexecuted instantiation: nsIContent* mozilla::EditorBase::GetNextEditableNodeInBlock<nsINode*, nsIContent*>(mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&) |
1376 | | nsIContent* GetNextNode(nsINode& aNode) |
1377 | 0 | { |
1378 | 0 | return GetNextNodeInternal(aNode, false, true, false); |
1379 | 0 | } |
1380 | | nsIContent* GetNextElementOrText(nsINode& aNode) |
1381 | 0 | { |
1382 | 0 | return GetNextNodeInternal(aNode, false, false, false); |
1383 | 0 | } |
1384 | | nsIContent* GetNextEditableNode(nsINode& aNode) |
1385 | 0 | { |
1386 | 0 | return GetNextNodeInternal(aNode, true, true, false); |
1387 | 0 | } |
1388 | | nsIContent* GetNextNodeInBlock(nsINode& aNode) |
1389 | 0 | { |
1390 | 0 | return GetNextNodeInternal(aNode, false, true, true); |
1391 | 0 | } |
1392 | | nsIContent* GetNextElementOrTextInBlock(nsINode& aNode) |
1393 | 0 | { |
1394 | 0 | return GetNextNodeInternal(aNode, false, false, true); |
1395 | 0 | } |
1396 | | nsIContent* GetNextEditableNodeInBlock(nsINode& aNode) |
1397 | 0 | { |
1398 | 0 | return GetNextNodeInternal(aNode, true, true, true); |
1399 | 0 | } |
1400 | | |
1401 | | /** |
1402 | | * Get the rightmost child of aCurrentNode; |
1403 | | * return nullptr if aCurrentNode has no children. |
1404 | | */ |
1405 | | nsIContent* GetRightmostChild(nsINode* aCurrentNode, |
1406 | | bool bNoBlockCrossing = false); |
1407 | | |
1408 | | /** |
1409 | | * Get the leftmost child of aCurrentNode; |
1410 | | * return nullptr if aCurrentNode has no children. |
1411 | | */ |
1412 | | nsIContent* GetLeftmostChild(nsINode *aCurrentNode, |
1413 | | bool bNoBlockCrossing = false); |
1414 | | |
1415 | | /** |
1416 | | * Returns true if aParent can contain a child of type aTag. |
1417 | | */ |
1418 | | bool CanContain(nsINode& aParent, nsIContent& aChild) const; |
1419 | | bool CanContainTag(nsINode& aParent, nsAtom& aTag) const; |
1420 | | bool TagCanContain(nsAtom& aParentTag, nsIContent& aChild) const; |
1421 | | virtual bool TagCanContainTag(nsAtom& aParentTag, nsAtom& aChildTag) const; |
1422 | | |
1423 | | /** |
1424 | | * Returns true if aNode is our root node. |
1425 | | */ |
1426 | | bool IsRoot(nsINode* inNode) const; |
1427 | | bool IsEditorRoot(nsINode* aNode) const; |
1428 | | |
1429 | | /** |
1430 | | * Returns true if aNode is a descendant of our root node. |
1431 | | */ |
1432 | | bool IsDescendantOfRoot(nsINode* inNode) const; |
1433 | | bool IsDescendantOfEditorRoot(nsINode* aNode) const; |
1434 | | |
1435 | | /** |
1436 | | * Returns true if aNode is a container. |
1437 | | */ |
1438 | | virtual bool IsContainer(nsINode* aNode); |
1439 | | |
1440 | | /** |
1441 | | * returns true if aNode is an editable node. |
1442 | | */ |
1443 | | bool IsEditable(nsINode* aNode) |
1444 | 0 | { |
1445 | 0 | if (NS_WARN_IF(!aNode)) { |
1446 | 0 | return false; |
1447 | 0 | } |
1448 | 0 | |
1449 | 0 | if (!aNode->IsContent() || IsMozEditorBogusNode(aNode) || |
1450 | 0 | !IsModifiableNode(*aNode)) { |
1451 | 0 | return false; |
1452 | 0 | } |
1453 | 0 | |
1454 | 0 | switch (aNode->NodeType()) { |
1455 | 0 | case nsINode::ELEMENT_NODE: |
1456 | 0 | // In HTML editors, if we're dealing with an element, then ask it |
1457 | 0 | // whether it's editable. |
1458 | 0 | return mIsHTMLEditorClass ? aNode->IsEditable() : true; |
1459 | 0 | case nsINode::TEXT_NODE: |
1460 | 0 | // Text nodes are considered to be editable by both typed of editors. |
1461 | 0 | return true; |
1462 | 0 | default: |
1463 | 0 | return false; |
1464 | 0 | } |
1465 | 0 | } |
1466 | | |
1467 | | /** |
1468 | | * Returns true if aNode is a usual element node (not bogus node) or |
1469 | | * a text node. In other words, returns true if aNode is a usual element |
1470 | | * node or visible data node. |
1471 | | */ |
1472 | | bool IsElementOrText(const nsINode& aNode) const |
1473 | 0 | { |
1474 | 0 | if (!aNode.IsContent() || IsMozEditorBogusNode(&aNode)) { |
1475 | 0 | return false; |
1476 | 0 | } |
1477 | 0 | return aNode.NodeType() == nsINode::ELEMENT_NODE || |
1478 | 0 | aNode.NodeType() == nsINode::TEXT_NODE; |
1479 | 0 | } |
1480 | | |
1481 | | /** |
1482 | | * Returns true if aNode is a MozEditorBogus node. |
1483 | | */ |
1484 | | bool IsMozEditorBogusNode(const nsINode* aNode) const |
1485 | 0 | { |
1486 | 0 | return aNode && aNode->IsElement() && |
1487 | 0 | aNode->AsElement()->AttrValueIs(kNameSpaceID_None, |
1488 | 0 | kMOZEditorBogusNodeAttrAtom, kMOZEditorBogusNodeValue, |
1489 | 0 | eCaseMatters); |
1490 | 0 | } |
1491 | | |
1492 | | /** |
1493 | | * Counts number of editable child nodes. |
1494 | | */ |
1495 | | uint32_t CountEditableChildren(nsINode* aNode); |
1496 | | |
1497 | | /** |
1498 | | * Find the deep first and last children. |
1499 | | */ |
1500 | | nsINode* GetFirstEditableNode(nsINode* aRoot); |
1501 | | |
1502 | | /** |
1503 | | * Returns true when inserting text should be a part of current composition. |
1504 | | */ |
1505 | | bool ShouldHandleIMEComposition() const; |
1506 | | |
1507 | | /** |
1508 | | * AreNodesSameType() returns true if aNode1 and aNode2 are same type. |
1509 | | * If the instance is TextEditor, only their names are checked. |
1510 | | * If the instance is HTMLEditor in CSS mode and both of them are <span> |
1511 | | * element, their styles are also checked. |
1512 | | */ |
1513 | | bool AreNodesSameType(nsIContent& aNode1, nsIContent& aNode2) const; |
1514 | | |
1515 | | static bool IsTextNode(nsINode* aNode) |
1516 | 0 | { |
1517 | 0 | return aNode->NodeType() == nsINode::TEXT_NODE; |
1518 | 0 | } |
1519 | | |
1520 | | /** |
1521 | | * IsModifiableNode() checks whether the node is editable or not. |
1522 | | */ |
1523 | | bool IsModifiableNode(const nsINode& aNode) const; |
1524 | | |
1525 | | /** |
1526 | | * GetNodeAtRangeOffsetPoint() returns the node at this position in a range, |
1527 | | * assuming that the container is the node itself if it's a text node, or |
1528 | | * the node's parent otherwise. |
1529 | | */ |
1530 | | static nsIContent* GetNodeAtRangeOffsetPoint(nsINode* aContainer, |
1531 | | int32_t aOffset) |
1532 | 0 | { |
1533 | 0 | return GetNodeAtRangeOffsetPoint(RawRangeBoundary(aContainer, aOffset)); |
1534 | 0 | } |
1535 | | static nsIContent* GetNodeAtRangeOffsetPoint(const RawRangeBoundary& aPoint); |
1536 | | |
1537 | | static EditorRawDOMPoint GetStartPoint(Selection* aSelection); |
1538 | | static EditorRawDOMPoint GetEndPoint(Selection* aSelection); |
1539 | | |
1540 | | static nsresult GetEndChildNode(Selection* aSelection, |
1541 | | nsIContent** aEndNode); |
1542 | | |
1543 | | /** |
1544 | | * CollapseSelectionToEnd() collapses the selection to the end of the editor. |
1545 | | */ |
1546 | | nsresult CollapseSelectionToEnd(Selection* aSelection); |
1547 | | |
1548 | | /** |
1549 | | * Helpers to add a node to the selection. |
1550 | | * Used by table cell selection methods. |
1551 | | */ |
1552 | | nsresult CreateRange(nsINode* aStartContainer, int32_t aStartOffset, |
1553 | | nsINode* aEndContainer, int32_t aEndOffset, |
1554 | | nsRange** aRange); |
1555 | | |
1556 | | static bool IsPreformatted(nsINode* aNode); |
1557 | | |
1558 | | /** |
1559 | | * AllowsTransactionsToChangeSelection() returns true if editor allows any |
1560 | | * transactions to change Selection. Otherwise, transactions shouldn't |
1561 | | * change Selection. |
1562 | | */ |
1563 | | inline bool AllowsTransactionsToChangeSelection() const |
1564 | 0 | { |
1565 | 0 | return mAllowsTransactionsToChangeSelection; |
1566 | 0 | } |
1567 | | |
1568 | | /** |
1569 | | * MakeThisAllowTransactionsToChangeSelection() with true makes this editor |
1570 | | * allow transactions to change Selection. Otherwise, i.e., with false, |
1571 | | * makes this editor not allow transactions to change Selection. |
1572 | | */ |
1573 | | inline void MakeThisAllowTransactionsToChangeSelection(bool aAllow) |
1574 | 0 | { |
1575 | 0 | mAllowsTransactionsToChangeSelection = aAllow; |
1576 | 0 | } |
1577 | | |
1578 | | nsresult HandleInlineSpellCheck(EditSubAction aEditSubAction, |
1579 | | Selection& aSelection, |
1580 | | nsINode* previousSelectedNode, |
1581 | | uint32_t previousSelectedOffset, |
1582 | | nsINode* aStartContainer, |
1583 | | uint32_t aStartOffset, |
1584 | | nsINode* aEndContainer, |
1585 | | uint32_t aEndOffset); |
1586 | | |
1587 | | /** |
1588 | | * Likewise, but gets the editor's root instead, which is different for HTML |
1589 | | * editors. |
1590 | | */ |
1591 | | virtual Element* GetEditorRoot() const; |
1592 | | |
1593 | | /** |
1594 | | * Likewise, but gets the text control element instead of the root for |
1595 | | * plaintext editors. |
1596 | | */ |
1597 | | Element* GetExposedRoot() const; |
1598 | | |
1599 | | /** |
1600 | | * Whether the editor is active on the DOM window. Note that when this |
1601 | | * returns true but GetFocusedContent() returns null, it means that this editor was |
1602 | | * focused when the DOM window was active. |
1603 | | */ |
1604 | | virtual bool IsActiveInDOMWindow(); |
1605 | | |
1606 | | /** |
1607 | | * GetIMESelectionStartOffsetIn() returns the start offset of IME selection in |
1608 | | * the aTextNode. If there is no IME selection, returns -1. |
1609 | | */ |
1610 | | int32_t GetIMESelectionStartOffsetIn(nsINode* aTextNode); |
1611 | | |
1612 | | /** |
1613 | | * FindBetterInsertionPoint() tries to look for better insertion point which |
1614 | | * is typically the nearest text node and offset in it. |
1615 | | * |
1616 | | * @param aPoint Insertion point which the callers found. |
1617 | | * @return Better insertion point if there is. If not returns |
1618 | | * same point as aPoint. |
1619 | | */ |
1620 | | EditorRawDOMPoint FindBetterInsertionPoint(const EditorRawDOMPoint& aPoint); |
1621 | | |
1622 | | /** |
1623 | | * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent |
1624 | | * with nsCaret::RemoveForceHide(). This does NOT set visibility of |
1625 | | * nsCaret. Therefore, this is stateless. |
1626 | | */ |
1627 | | void HideCaret(bool aHide); |
1628 | | |
1629 | | protected: // Called by helper classes. |
1630 | | |
1631 | | /** |
1632 | | * OnStartToHandleTopLevelEditSubAction() is called when |
1633 | | * mTopLevelEditSubAction is EditSubAction::eNone and somebody starts to |
1634 | | * handle aEditSubAction. |
1635 | | * |
1636 | | * @param aEditSubAction Top level edit sub action which will be |
1637 | | * handled soon. |
1638 | | * @param aDirection Direction of aEditSubAction. |
1639 | | */ |
1640 | | virtual void |
1641 | | OnStartToHandleTopLevelEditSubAction(EditSubAction aEditSubAction, |
1642 | | nsIEditor::EDirection aDirection); |
1643 | | |
1644 | | /** |
1645 | | * OnEndHandlingTopLevelEditSubAction() is called after |
1646 | | * mTopLevelEditSubAction is handled. |
1647 | | */ |
1648 | | virtual void OnEndHandlingTopLevelEditSubAction(); |
1649 | | |
1650 | | /** |
1651 | | * Routines for managing the preservation of selection across |
1652 | | * various editor actions. |
1653 | | */ |
1654 | | bool ArePreservingSelection(); |
1655 | | void PreserveSelectionAcrossActions(Selection* aSel); |
1656 | | nsresult RestorePreservedSelection(Selection* aSel); |
1657 | | void StopPreservingSelection(); |
1658 | | |
1659 | | /** |
1660 | | * (Begin|End)PlaceholderTransaction() are called by AutoPlaceholderBatch. |
1661 | | * This set of methods are similar to the (Begin|End)Transaction(), but do |
1662 | | * not use the transaction managers batching feature. Instead we use a |
1663 | | * placeholder transaction to wrap up any further transaction while the |
1664 | | * batch is open. The advantage of this is that placeholder transactions |
1665 | | * can later merge, if needed. Merging is unavailable between transaction |
1666 | | * manager batches. |
1667 | | */ |
1668 | | void BeginPlaceholderTransaction(nsAtom* aTransactionName); |
1669 | | void EndPlaceholderTransaction(); |
1670 | | |
1671 | | void BeginUpdateViewBatch(); |
1672 | | void EndUpdateViewBatch(); |
1673 | | |
1674 | | /** |
1675 | | * Used by AutoTransactionBatch. After calling BeginTransactionInternal(), |
1676 | | * all transactions will be treated as an atomic transaction. I.e., |
1677 | | * two or more transactions are undid once. |
1678 | | * XXX What's the difference with PlaceholderTransaction? Should we always |
1679 | | * use it instead? |
1680 | | */ |
1681 | | void BeginTransactionInternal(); |
1682 | | void EndTransactionInternal(); |
1683 | | |
1684 | | protected: // Shouldn't be used by friend classes |
1685 | | /** |
1686 | | * The default destructor. This should suffice. Should this be pure virtual |
1687 | | * for someone to derive from the EditorBase later? I don't believe so. |
1688 | | */ |
1689 | | virtual ~EditorBase(); |
1690 | | |
1691 | | /** |
1692 | | * GetDocumentCharsetInternal() returns charset of the document. |
1693 | | */ |
1694 | | nsresult GetDocumentCharsetInternal(nsACString& aCharset) const; |
1695 | | |
1696 | | /** |
1697 | | * SelectAllInternal() should be used instead of SelectAll() in editor |
1698 | | * because SelectAll() creates AutoEditActionSetter but we should avoid |
1699 | | * to create it as far as possible. |
1700 | | */ |
1701 | | virtual nsresult SelectAllInternal(); |
1702 | | |
1703 | | nsresult DetermineCurrentDirection(); |
1704 | | void FireInputEvent(); |
1705 | | |
1706 | | /** |
1707 | | * Called after a transaction is done successfully. |
1708 | | */ |
1709 | | void DoAfterDoTransaction(nsITransaction *aTxn); |
1710 | | |
1711 | | /** |
1712 | | * Called after a transaction is undone successfully. |
1713 | | */ |
1714 | | |
1715 | | void DoAfterUndoTransaction(); |
1716 | | |
1717 | | /** |
1718 | | * Called after a transaction is redone successfully. |
1719 | | */ |
1720 | | void DoAfterRedoTransaction(); |
1721 | | |
1722 | | /** |
1723 | | * Tell the doc state listeners that the doc state has changed. |
1724 | | */ |
1725 | | enum TDocumentListenerNotification |
1726 | | { |
1727 | | eDocumentCreated, |
1728 | | eDocumentToBeDestroyed, |
1729 | | eDocumentStateChanged |
1730 | | }; |
1731 | | nsresult NotifyDocumentListeners( |
1732 | | TDocumentListenerNotification aNotificationType); |
1733 | | |
1734 | | /** |
1735 | | * Make the given selection span the entire document. |
1736 | | */ |
1737 | | virtual nsresult SelectEntireDocument(Selection* aSelection); |
1738 | | |
1739 | | /** |
1740 | | * Helper method for scrolling the selection into view after |
1741 | | * an edit operation. aScrollToAnchor should be true if you |
1742 | | * want to scroll to the point where the selection was started. |
1743 | | * If false, it attempts to scroll the end of the selection into view. |
1744 | | * |
1745 | | * Editor methods *should* call this method instead of the versions |
1746 | | * in the various selection interfaces, since this version makes sure |
1747 | | * that the editor's sync/async settings for reflowing, painting, and |
1748 | | * scrolling match. |
1749 | | */ |
1750 | | nsresult ScrollSelectionIntoView(bool aScrollToAnchor); |
1751 | | |
1752 | | /** |
1753 | | * Helper for GetPreviousNodeInternal() and GetNextNodeInternal(). |
1754 | | */ |
1755 | | nsIContent* FindNextLeafNode(nsINode* aCurrentNode, |
1756 | | bool aGoForward, |
1757 | | bool bNoBlockCrossing); |
1758 | | nsIContent* FindNode(nsINode* aCurrentNode, |
1759 | | bool aGoForward, |
1760 | | bool aEditableNode, |
1761 | | bool aFindAnyDataNode, |
1762 | | bool bNoBlockCrossing); |
1763 | | |
1764 | | /** |
1765 | | * Get the node immediately previous node of aNode. |
1766 | | * @param atNode The node from which we start the search. |
1767 | | * @param aFindEditableNode If true, only return an editable node. |
1768 | | * @param aFindAnyDataNode If true, may return invisible data node |
1769 | | * like Comment. |
1770 | | * @param aNoBlockCrossing If true, don't move across "block" nodes, |
1771 | | * whatever that means. |
1772 | | * @return The node that occurs before aNode in |
1773 | | * the tree, skipping non-editable nodes if |
1774 | | * aFindEditableNode is true. If there is no |
1775 | | * previous node, returns nullptr. |
1776 | | */ |
1777 | | nsIContent* GetPreviousNodeInternal(nsINode& aNode, |
1778 | | bool aFindEditableNode, |
1779 | | bool aFindAnyDataNode, |
1780 | | bool aNoBlockCrossing); |
1781 | | |
1782 | | /** |
1783 | | * And another version that takes a point in DOM tree rather than a node. |
1784 | | */ |
1785 | | nsIContent* GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint, |
1786 | | bool aFindEditableNode, |
1787 | | bool aFindAnyDataNode, |
1788 | | bool aNoBlockCrossing); |
1789 | | |
1790 | | /** |
1791 | | * Get the node immediately next node of aNode. |
1792 | | * @param aNode The node from which we start the search. |
1793 | | * @param aFindEditableNode If true, only return an editable node. |
1794 | | * @param aFindAnyDataNode If true, may return invisible data node |
1795 | | * like Comment. |
1796 | | * @param aNoBlockCrossing If true, don't move across "block" nodes, |
1797 | | * whatever that means. |
1798 | | * @return The node that occurs after aNode in the |
1799 | | * tree, skipping non-editable nodes if |
1800 | | * aFindEditableNode is true. If there is no |
1801 | | * next node, returns nullptr. |
1802 | | */ |
1803 | | nsIContent* GetNextNodeInternal(nsINode& aNode, |
1804 | | bool aFindEditableNode, |
1805 | | bool aFindAnyDataNode, |
1806 | | bool bNoBlockCrossing); |
1807 | | |
1808 | | /** |
1809 | | * And another version that takes a point in DOM tree rather than a node. |
1810 | | */ |
1811 | | nsIContent* GetNextNodeInternal(const EditorRawDOMPoint& aPoint, |
1812 | | bool aFindEditableNode, |
1813 | | bool aFindAnyDataNode, |
1814 | | bool aNoBlockCrossing); |
1815 | | |
1816 | | |
1817 | | virtual nsresult InstallEventListeners(); |
1818 | | virtual void CreateEventListeners(); |
1819 | | virtual void RemoveEventListeners(); |
1820 | | |
1821 | | /** |
1822 | | * Get the input event target. This might return null. |
1823 | | */ |
1824 | | virtual already_AddRefed<nsIContent> GetInputEventTargetContent() = 0; |
1825 | | |
1826 | | /** |
1827 | | * Return true if spellchecking should be enabled for this editor. |
1828 | | */ |
1829 | | bool GetDesiredSpellCheckState(); |
1830 | | |
1831 | | bool CanEnableSpellCheck() |
1832 | 0 | { |
1833 | 0 | // Check for password/readonly/disabled, which are not spellchecked |
1834 | 0 | // regardless of DOM. Also, check to see if spell check should be skipped |
1835 | 0 | // or not. |
1836 | 0 | return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() && |
1837 | 0 | !ShouldSkipSpellCheck(); |
1838 | 0 | } |
1839 | | |
1840 | | /** |
1841 | | * InitializeSelectionAncestorLimit() is called by InitializeSelection(). |
1842 | | * When this is called, each implementation has to call |
1843 | | * aSelection.SetAncestorLimiter() with aAnotherLimit. |
1844 | | * |
1845 | | * @param aSelection The selection. |
1846 | | * @param aAncestorLimit New ancestor limit of aSelection. This always |
1847 | | * has parent node. So, it's always safe to |
1848 | | * call SetAncestorLimit() with this node. |
1849 | | */ |
1850 | | virtual void InitializeSelectionAncestorLimit(Selection& aSelection, |
1851 | | nsIContent& aAncestorLimit); |
1852 | | |
1853 | | /** |
1854 | | * Return the offset of aChild in aParent. Asserts fatally if parent or |
1855 | | * child is null, or parent is not child's parent. |
1856 | | * FYI: aChild must not be being removed from aParent. In such case, these |
1857 | | * methods may return wrong index if aChild doesn't have previous |
1858 | | * sibling or next sibling. |
1859 | | */ |
1860 | | static int32_t GetChildOffset(nsINode* aChild, |
1861 | | nsINode* aParent); |
1862 | | |
1863 | | /** |
1864 | | * Creates a range with just the supplied node and appends that to the |
1865 | | * selection. |
1866 | | */ |
1867 | | nsresult AppendNodeToSelectionAsRange(nsINode* aNode); |
1868 | | |
1869 | | /** |
1870 | | * When you are using AppendNodeToSelectionAsRange(), call this first to |
1871 | | * start a new selection. |
1872 | | */ |
1873 | | nsresult ClearSelection(); |
1874 | | |
1875 | | /** |
1876 | | * Initializes selection and caret for the editor. If aEventTarget isn't |
1877 | | * a host of the editor, i.e., the editor doesn't get focus, this does |
1878 | | * nothing. |
1879 | | */ |
1880 | | nsresult InitializeSelection(dom::EventTarget* aFocusEventTarget); |
1881 | | |
1882 | | /** |
1883 | | * Used to insert content from a data transfer into the editable area. |
1884 | | * This is called for each item in the data transfer, with the index of |
1885 | | * each item passed as aIndex. |
1886 | | */ |
1887 | | virtual nsresult InsertFromDataTransfer(dom::DataTransfer* aDataTransfer, |
1888 | | int32_t aIndex, |
1889 | | nsIDocument* aSourceDoc, |
1890 | | nsINode* aDestinationNode, |
1891 | | int32_t aDestOffset, |
1892 | | bool aDoDeleteSelection) = 0; |
1893 | | |
1894 | | enum NotificationForEditorObservers |
1895 | | { |
1896 | | eNotifyEditorObserversOfEnd, |
1897 | | eNotifyEditorObserversOfBefore, |
1898 | | eNotifyEditorObserversOfCancel |
1899 | | }; |
1900 | | void NotifyEditorObservers(NotificationForEditorObservers aNotification); |
1901 | | |
1902 | | private: |
1903 | | nsCOMPtr<nsISelectionController> mSelectionController; |
1904 | | nsCOMPtr<nsIDocument> mDocument; |
1905 | | |
1906 | | |
1907 | | /** |
1908 | | * SetTextDirectionTo() sets text-direction of the root element. |
1909 | | * Should use SwitchTextDirectionTo() or ToggleTextDirection() instead. |
1910 | | * This is a helper class of them. |
1911 | | */ |
1912 | | nsresult SetTextDirectionTo(TextDirection aTextDirection); |
1913 | | protected: |
1914 | | enum Tristate |
1915 | | { |
1916 | | eTriUnset, |
1917 | | eTriFalse, |
1918 | | eTriTrue |
1919 | | }; |
1920 | | |
1921 | | // MIME type of the doc we are editing. |
1922 | | nsCString mContentMIMEType; |
1923 | | |
1924 | | RefPtr<mozInlineSpellChecker> mInlineSpellChecker; |
1925 | | // Reference to text services document for mInlineSpellChecker. |
1926 | | RefPtr<TextServicesDocument> mTextServicesDocument; |
1927 | | |
1928 | | RefPtr<TransactionManager> mTransactionManager; |
1929 | | // Cached root node. |
1930 | | RefPtr<Element> mRootElement; |
1931 | | // The form field as an event receiver. |
1932 | | nsCOMPtr<dom::EventTarget> mEventTarget; |
1933 | | RefPtr<EditorEventListener> mEventListener; |
1934 | | // Strong reference to placeholder for begin/end batch purposes. |
1935 | | RefPtr<PlaceholderTransaction> mPlaceholderTransaction; |
1936 | | // Name of placeholder transaction. |
1937 | | nsAtom* mPlaceholderName; |
1938 | | // Saved selection state for placeholder transaction batching. |
1939 | | mozilla::Maybe<SelectionState> mSelState; |
1940 | | // IME composition this is not null between compositionstart and |
1941 | | // compositionend. |
1942 | | RefPtr<TextComposition> mComposition; |
1943 | | |
1944 | | RefPtr<TextEditRules> mRules; |
1945 | | |
1946 | | RefPtr<TextInputListener> mTextInputListener; |
1947 | | |
1948 | | RefPtr<IMEContentObserver> mIMEContentObserver; |
1949 | | |
1950 | | // Listens to all low level actions on the doc. |
1951 | | typedef AutoTArray<OwningNonNull<nsIEditActionListener>, 5> |
1952 | | AutoActionListenerArray; |
1953 | | AutoActionListenerArray mActionListeners; |
1954 | | // Just notify once per high level change. |
1955 | | typedef AutoTArray<OwningNonNull<nsIEditorObserver>, 3> |
1956 | | AutoEditorObserverArray; |
1957 | | AutoEditorObserverArray mEditorObservers; |
1958 | | // Listen to overall doc state (dirty or not, just created, etc.). |
1959 | | typedef AutoTArray<OwningNonNull<nsIDocumentStateListener>, 1> |
1960 | | AutoDocumentStateListenerArray; |
1961 | | AutoDocumentStateListenerArray mDocStateListeners; |
1962 | | |
1963 | | // Cached selection for AutoSelectionRestorer. |
1964 | | SelectionState mSavedSel; |
1965 | | // Utility class object for maintaining preserved ranges. |
1966 | | RangeUpdater mRangeUpdater; |
1967 | | |
1968 | | // Number of modifications (for undo/redo stack). |
1969 | | uint32_t mModCount; |
1970 | | // Behavior flags. See nsIPlaintextEditor.idl for the flags we use. |
1971 | | uint32_t mFlags; |
1972 | | |
1973 | | int32_t mUpdateCount; |
1974 | | |
1975 | | // Nesting count for batching. |
1976 | | int32_t mPlaceholderBatch; |
1977 | | // The top level edit sub-action. |
1978 | | EditSubAction mTopLevelEditSubAction; |
1979 | | |
1980 | | // The top level edit sub-action's direction. |
1981 | | EDirection mDirection; |
1982 | | // -1 = not initialized |
1983 | | int8_t mDocDirtyState; |
1984 | | // A Tristate value. |
1985 | | uint8_t mSpellcheckCheckboxState; |
1986 | | |
1987 | | // If false, transactions should not change Selection even after modifying |
1988 | | // the DOM tree. |
1989 | | bool mAllowsTransactionsToChangeSelection; |
1990 | | // Whether PreDestroy has been called. |
1991 | | bool mDidPreDestroy; |
1992 | | // Whether PostCreate has been called. |
1993 | | bool mDidPostCreate; |
1994 | | bool mDispatchInputEvent; |
1995 | | // True while the instance is handling an edit sub-action. |
1996 | | bool mIsInEditSubAction; |
1997 | | // Whether caret is hidden forcibly. |
1998 | | bool mHidingCaret; |
1999 | | // Whether spellchecker dictionary is initialized after focused. |
2000 | | bool mSpellCheckerDictionaryUpdated; |
2001 | | // Whether we are an HTML editor class. |
2002 | | bool mIsHTMLEditorClass; |
2003 | | |
2004 | | friend class AutoPlaceholderBatch; |
2005 | | friend class AutoSelectionRestorer; |
2006 | | friend class AutoTopLevelEditSubActionNotifier; |
2007 | | friend class AutoTransactionBatch; |
2008 | | friend class AutoTransactionsConserveSelection; |
2009 | | friend class AutoUpdateViewBatch; |
2010 | | friend class CompositionTransaction; |
2011 | | friend class CreateElementTransaction; |
2012 | | friend class CSSEditUtils; |
2013 | | friend class DeleteNodeTransaction; |
2014 | | friend class DeleteRangeTransaction; |
2015 | | friend class DeleteTextTransaction; |
2016 | | friend class HTMLEditRules; |
2017 | | friend class HTMLEditUtils; |
2018 | | friend class InsertNodeTransaction; |
2019 | | friend class InsertTextTransaction; |
2020 | | friend class JoinNodeTransaction; |
2021 | | friend class SplitNodeTransaction; |
2022 | | friend class TextEditRules; |
2023 | | friend class TypeInState; |
2024 | | friend class WSRunObject; |
2025 | | friend class nsIEditor; |
2026 | | }; |
2027 | | |
2028 | | } // namespace mozilla |
2029 | | |
2030 | | mozilla::EditorBase* |
2031 | | nsIEditor::AsEditorBase() |
2032 | 0 | { |
2033 | 0 | return static_cast<mozilla::EditorBase*>(this); |
2034 | 0 | } |
2035 | | |
2036 | | const mozilla::EditorBase* |
2037 | | nsIEditor::AsEditorBase() const |
2038 | 0 | { |
2039 | 0 | return static_cast<const mozilla::EditorBase*>(this); |
2040 | 0 | } |
2041 | | |
2042 | | #endif // #ifndef mozilla_EditorBase_h |