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