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/OwningNonNull.h" // for OwningNonNull
11 : #include "mozilla/SelectionState.h" // for RangeUpdater, etc.
12 : #include "mozilla/StyleSheet.h" // for StyleSheet
13 : #include "mozilla/UniquePtr.h"
14 : #include "mozilla/WeakPtr.h" // for WeakPtr
15 : #include "mozilla/dom/Text.h"
16 : #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr
17 : #include "nsCycleCollectionParticipant.h"
18 : #include "nsGkAtoms.h"
19 : #include "nsIDocument.h" // for nsIDocument
20 : #include "nsIEditor.h" // for nsIEditor, etc.
21 : #include "nsIObserver.h" // for NS_DECL_NSIOBSERVER, etc.
22 : #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc.
23 : #include "nsISelectionController.h" // for nsISelectionController constants
24 : #include "nsISupportsImpl.h" // for EditorBase::Release, etc.
25 : #include "nsIWeakReferenceUtils.h" // for nsWeakPtr
26 : #include "nsLiteralString.h" // for NS_LITERAL_STRING
27 : #include "nsString.h" // for nsCString
28 : #include "nsTArray.h" // for nsTArray and nsAutoTArray
29 : #include "nsWeakReference.h" // for nsSupportsWeakReference
30 : #include "nscore.h" // for nsresult, nsAString, etc.
31 :
32 : class nsIAtom;
33 : class nsIContent;
34 : class nsIDOMDocument;
35 : class nsIDOMEvent;
36 : class nsIDOMEventListener;
37 : class nsIDOMEventTarget;
38 : class nsIDOMNode;
39 : class nsIDocumentStateListener;
40 : class nsIEditActionListener;
41 : class nsIEditorObserver;
42 : class nsIInlineSpellChecker;
43 : class nsINode;
44 : class nsIPresShell;
45 : class nsISupports;
46 : class nsITransaction;
47 : class nsIWidget;
48 : class nsRange;
49 : class nsString;
50 : class nsTransactionManager;
51 :
52 : // This is int32_t instead of int16_t because nsIInlineSpellChecker.idl's
53 : // spellCheckAfterEditorChange is defined to take it as a long.
54 : // XXX EditAction causes unnecessary include of EditorBase from some places.
55 : // Why don't you move this to nsIEditor.idl?
56 : enum class EditAction : int32_t
57 : {
58 : ignore = -1,
59 : none = 0,
60 : undo,
61 : redo,
62 : insertNode,
63 : createNode,
64 : deleteNode,
65 : splitNode,
66 : joinNode,
67 : deleteText = 1003,
68 :
69 : // text commands
70 : insertText = 2000,
71 : insertIMEText = 2001,
72 : deleteSelection = 2002,
73 : setTextProperty = 2003,
74 : removeTextProperty = 2004,
75 : outputText = 2005,
76 : setText = 2006,
77 :
78 : // html only action
79 : insertBreak = 3000,
80 : makeList = 3001,
81 : indent = 3002,
82 : outdent = 3003,
83 : align = 3004,
84 : makeBasicBlock = 3005,
85 : removeList = 3006,
86 : makeDefListItem = 3007,
87 : insertElement = 3008,
88 : insertQuotation = 3009,
89 : htmlPaste = 3012,
90 : loadHTML = 3013,
91 : resetTextProperties = 3014,
92 : setAbsolutePosition = 3015,
93 : removeAbsolutePosition = 3016,
94 : decreaseZIndex = 3017,
95 : increaseZIndex = 3018
96 : };
97 :
98 8 : inline bool operator!(const EditAction& aOp)
99 : {
100 8 : return aOp == EditAction::none;
101 : }
102 :
103 : namespace mozilla {
104 : class AddStyleSheetTransaction;
105 : class AutoRules;
106 : class AutoSelectionRestorer;
107 : class AutoTransactionsConserveSelection;
108 : class ChangeAttributeTransaction;
109 : class CompositionTransaction;
110 : class CreateElementTransaction;
111 : class DeleteNodeTransaction;
112 : class DeleteTextTransaction;
113 : class EditAggregateTransaction;
114 : class EditTransactionBase;
115 : class ErrorResult;
116 : class HTMLEditor;
117 : class InsertNodeTransaction;
118 : class InsertTextTransaction;
119 : class JoinNodeTransaction;
120 : class PlaceholderTransaction;
121 : class RemoveStyleSheetTransaction;
122 : class SetTextTransaction;
123 : class SplitNodeTransaction;
124 : class TextComposition;
125 : class TextEditor;
126 : struct EditorDOMPoint;
127 :
128 : namespace dom {
129 : class DataTransfer;
130 : class Element;
131 : class EventTarget;
132 : class Selection;
133 : class Text;
134 : } // namespace dom
135 :
136 : namespace widget {
137 : struct IMEState;
138 : } // namespace widget
139 :
140 : /**
141 : * CachedWeakPtr stores a pointer to a class which inherits nsIWeakReference.
142 : * If the instance of the class has already been destroyed, this returns
143 : * nullptr. Otherwise, returns cached pointer.
144 : * If class T inherits nsISupports a lot, specify Base explicitly for avoiding
145 : * ambiguous conversion to nsISupports.
146 : */
147 : template<class T, class Base = nsISupports>
148 0 : class CachedWeakPtr final
149 : {
150 : public:
151 2 : CachedWeakPtr<T, Base>()
152 2 : : mCache(nullptr)
153 : {
154 2 : }
155 0 : explicit CachedWeakPtr<T, Base>(T* aObject)
156 0 : {
157 0 : mWeakPtr = do_GetWeakReference(static_cast<Base*>(aObject));
158 0 : mCache = aObject;
159 0 : }
160 : explicit CachedWeakPtr<T, Base>(const nsCOMPtr<T>& aOther)
161 : {
162 : mWeakPtr = do_GetWeakReference(static_cast<Base*>(aOther.get()));
163 : mCache = aOther;
164 : }
165 : explicit CachedWeakPtr<T, Base>(already_AddRefed<T>& aOther)
166 : {
167 : RefPtr<T> other = aOther;
168 : mWeakPtr = do_GetWeakReference(static_cast<Base*>(other.get()));
169 : mCache = other;
170 : }
171 :
172 4 : CachedWeakPtr<T, Base>& operator=(T* aObject)
173 : {
174 4 : mWeakPtr = do_GetWeakReference(static_cast<Base*>(aObject));
175 4 : mCache = aObject;
176 4 : return *this;
177 : }
178 : CachedWeakPtr<T, Base>& operator=(const nsCOMPtr<T>& aOther)
179 : {
180 : mWeakPtr = do_GetWeakReference(static_cast<Base*>(aOther.get()));
181 : mCache = aOther;
182 : return *this;
183 : }
184 : CachedWeakPtr<T, Base>& operator=(already_AddRefed<T>& aOther)
185 : {
186 : RefPtr<T> other = aOther;
187 : mWeakPtr = do_GetWeakReference(static_cast<Base*>(other.get()));
188 : mCache = other;
189 : return *this;
190 : }
191 :
192 : bool IsAlive() const { return mWeakPtr && mWeakPtr->IsAlive(); }
193 :
194 36 : explicit operator bool() const { return mWeakPtr; }
195 0 : operator T*() const { return get(); }
196 31 : T* get() const
197 : {
198 31 : if (mCache && !mWeakPtr->IsAlive()) {
199 0 : const_cast<CachedWeakPtr<T, Base>*>(this)->mCache = nullptr;
200 : }
201 31 : return mCache;
202 : }
203 :
204 : private:
205 : nsWeakPtr mWeakPtr;
206 : T* MOZ_NON_OWNING_REF mCache;
207 : };
208 :
209 : #define kMOZEditorBogusNodeAttrAtom nsGkAtoms::mozeditorbogusnode
210 : #define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE")
211 :
212 : /**
213 : * Implementation of an editor object. it will be the controller/focal point
214 : * for the main editor services. i.e. the GUIManager, publishing, transaction
215 : * manager, event interfaces. the idea for the event interfaces is to have them
216 : * delegate the actual commands to the editor independent of the XPFE
217 : * implementation.
218 : */
219 : class EditorBase : public nsIEditor
220 : , public nsSupportsWeakReference
221 : {
222 : public:
223 : typedef dom::Element Element;
224 : typedef dom::Selection Selection;
225 : typedef dom::Text Text;
226 :
227 : enum IterDirection
228 : {
229 : kIterForward,
230 : kIterBackward
231 : };
232 :
233 : /**
234 : * The default constructor. This should suffice. the setting of the
235 : * interfaces is done after the construction of the editor class.
236 : */
237 : EditorBase();
238 :
239 : virtual TextEditor* AsTextEditor() = 0;
240 : virtual const TextEditor* AsTextEditor() const = 0;
241 : virtual HTMLEditor* AsHTMLEditor() = 0;
242 : virtual const HTMLEditor* AsHTMLEditor() const = 0;
243 :
244 : protected:
245 : /**
246 : * The default destructor. This should suffice. Should this be pure virtual
247 : * for someone to derive from the EditorBase later? I don't believe so.
248 : */
249 : virtual ~EditorBase();
250 :
251 : public:
252 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
253 69 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor)
254 :
255 10 : bool IsInitialized() const { return !!mDocumentWeak; }
256 : already_AddRefed<nsIDOMDocument> GetDOMDocument();
257 : already_AddRefed<nsIDocument> GetDocument();
258 : already_AddRefed<nsIPresShell> GetPresShell();
259 : already_AddRefed<nsIWidget> GetWidget();
260 : enum NotificationForEditorObservers
261 : {
262 : eNotifyEditorObserversOfEnd,
263 : eNotifyEditorObserversOfBefore,
264 : eNotifyEditorObserversOfCancel
265 : };
266 : void NotifyEditorObservers(NotificationForEditorObservers aNotification);
267 :
268 : // nsIEditor methods
269 : NS_DECL_NSIEDITOR
270 :
271 : public:
272 : virtual bool IsModifiableNode(nsINode* aNode);
273 :
274 : virtual nsresult InsertTextImpl(const nsAString& aStringToInsert,
275 : nsCOMPtr<nsINode>* aInOutNode,
276 : int32_t* aInOutOffset,
277 : nsIDocument* aDoc);
278 : nsresult InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
279 : Text& aTextNode, int32_t aOffset,
280 : bool aSuppressIME = false);
281 :
282 : nsresult SetTextImpl(const nsAString& aString,
283 : Text& aTextNode);
284 :
285 : NS_IMETHOD DeleteSelectionImpl(EDirection aAction,
286 : EStripWrappers aStripWrappers);
287 :
288 : already_AddRefed<Element> DeleteSelectionAndCreateElement(nsIAtom& aTag);
289 :
290 : /**
291 : * Helper routines for node/parent manipulations.
292 : */
293 : nsresult DeleteNode(nsINode* aNode);
294 : nsresult InsertNode(nsIContent& aNode, nsINode& aParent, int32_t aPosition);
295 : enum ECloneAttributes { eDontCloneAttributes, eCloneAttributes };
296 : already_AddRefed<Element> ReplaceContainer(Element* aOldContainer,
297 : nsIAtom* aNodeType,
298 : nsIAtom* aAttribute = nullptr,
299 : const nsAString* aValue = nullptr,
300 : ECloneAttributes aCloneAttributes
301 : = eDontCloneAttributes);
302 : void CloneAttributes(Element* aDest, Element* aSource);
303 :
304 : nsresult RemoveContainer(nsIContent* aNode);
305 : already_AddRefed<Element> InsertContainerAbove(nsIContent* aNode,
306 : nsIAtom* aNodeType,
307 : nsIAtom* aAttribute = nullptr,
308 : const nsAString* aValue =
309 : nullptr);
310 : nsIContent* SplitNode(nsIContent& aNode, int32_t aOffset,
311 : ErrorResult& aResult);
312 : nsresult JoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
313 : nsresult MoveNode(nsIContent* aNode, nsINode* aParent, int32_t aOffset);
314 :
315 : nsresult CloneAttribute(nsIAtom* aAttribute, Element* aDestElement,
316 : Element* aSourceElement);
317 : nsresult RemoveAttribute(Element* aElement, nsIAtom* aAttribute);
318 : virtual nsresult RemoveAttributeOrEquivalent(Element* aElement,
319 : nsIAtom* aAttribute,
320 : bool aSuppressTransaction) = 0;
321 : nsresult SetAttribute(Element* aElement, nsIAtom* aAttribute,
322 : const nsAString& aValue);
323 : virtual nsresult SetAttributeOrEquivalent(Element* aElement,
324 : nsIAtom* aAttribute,
325 : const nsAString& aValue,
326 : bool aSuppressTransaction) = 0;
327 :
328 : /**
329 : * Method to replace certain CreateElementNS() calls.
330 : *
331 : * @param aTag Tag you want.
332 : */
333 : already_AddRefed<Element> CreateHTMLContent(nsIAtom* aTag);
334 :
335 : /**
336 : * IME event handlers.
337 : */
338 : virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent);
339 : virtual nsresult UpdateIMEComposition(
340 : WidgetCompositionEvent* aCompositionChangeEvet) = 0;
341 : void EndIMEComposition();
342 :
343 : void SwitchTextDirectionTo(uint32_t aDirection);
344 :
345 : protected:
346 : nsresult DetermineCurrentDirection();
347 : void FireInputEvent();
348 :
349 : /**
350 : * Create a transaction for setting aAttribute to aValue on aElement. Never
351 : * returns null.
352 : */
353 : already_AddRefed<ChangeAttributeTransaction>
354 : CreateTxnForSetAttribute(Element& aElement, nsIAtom& aAttribute,
355 : const nsAString& aValue);
356 :
357 : /**
358 : * Create a transaction for removing aAttribute on aElement. Never returns
359 : * null.
360 : */
361 : already_AddRefed<ChangeAttributeTransaction>
362 : CreateTxnForRemoveAttribute(Element& aElement, nsIAtom& aAttribute);
363 :
364 : /**
365 : * Create a transaction for creating a new child node of aParent of type aTag.
366 : */
367 : already_AddRefed<CreateElementTransaction>
368 : CreateTxnForCreateElement(nsIAtom& aTag,
369 : nsINode& aParent,
370 : int32_t aPosition);
371 :
372 : already_AddRefed<Element> CreateNode(nsIAtom* aTag, nsINode* aParent,
373 : int32_t aPosition);
374 :
375 : /**
376 : * Create a transaction for inserting aNode as a child of aParent.
377 : */
378 : already_AddRefed<InsertNodeTransaction>
379 : CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent,
380 : int32_t aOffset);
381 :
382 : /**
383 : * Create a transaction for removing aNode from its parent.
384 : */
385 : already_AddRefed<DeleteNodeTransaction>
386 : CreateTxnForDeleteNode(nsINode* aNode);
387 :
388 : /**
389 : * Create an aggregate transaction for delete selection. The result may
390 : * include DeleteNodeTransactions and/or DeleteTextTransactions as its
391 : * children.
392 : *
393 : * @param aAction The action caused removing the selection.
394 : * @param aRemovingNode The node to be removed.
395 : * @param aOffset The start offset of the range in aRemovingNode.
396 : * @param aLength The length of the range in aRemovingNode.
397 : * @return If it can remove the selection, returns an
398 : * aggregate transaction which has some
399 : * DeleteNodeTransactions and/or
400 : * DeleteTextTransactions as its children.
401 : */
402 : already_AddRefed<EditAggregateTransaction>
403 : CreateTxnForDeleteSelection(EDirection aAction,
404 : nsINode** aNode,
405 : int32_t* aOffset,
406 : int32_t* aLength);
407 :
408 : /**
409 : * Create a transaction for removing the nodes and/or text in aRange.
410 : *
411 : * @param aRangeToDelete The range to be removed.
412 : * @param aAction The action caused removing the range.
413 : * @param aRemovingNode The node to be removed.
414 : * @param aOffset The start offset of the range in aRemovingNode.
415 : * @param aLength The length of the range in aRemovingNode.
416 : * @return The transaction to remove the range. Its type
417 : * is DeleteNodeTransaction or
418 : * DeleteTextTransaction.
419 : */
420 : already_AddRefed<EditTransactionBase>
421 : CreateTxnForDeleteRange(nsRange* aRangeToDelete,
422 : EDirection aAction,
423 : nsINode** aRemovingNode,
424 : int32_t* aOffset,
425 : int32_t* aLength);
426 :
427 : /**
428 : * Create a transaction for inserting aStringToInsert into aTextNode. Never
429 : * returns null.
430 : */
431 : already_AddRefed<mozilla::InsertTextTransaction>
432 : CreateTxnForInsertText(const nsAString& aStringToInsert, Text& aTextNode,
433 : int32_t aOffset);
434 :
435 : already_AddRefed<SetTextTransaction>
436 : CreateTxnForSetText(const nsAString& aString, Text& aTextNode);
437 :
438 : /**
439 : * Never returns null.
440 : */
441 : already_AddRefed<mozilla::CompositionTransaction>
442 : CreateTxnForComposition(const nsAString& aStringToInsert);
443 :
444 : /**
445 : * Create a transaction for adding a style sheet.
446 : */
447 : already_AddRefed<mozilla::AddStyleSheetTransaction>
448 : CreateTxnForAddStyleSheet(StyleSheet* aSheet);
449 :
450 : /**
451 : * Create a transaction for removing a style sheet.
452 : */
453 : already_AddRefed<mozilla::RemoveStyleSheetTransaction>
454 : CreateTxnForRemoveStyleSheet(StyleSheet* aSheet);
455 :
456 : nsresult DeleteText(nsGenericDOMDataNode& aElement,
457 : uint32_t aOffset, uint32_t aLength);
458 :
459 : already_AddRefed<DeleteTextTransaction>
460 : CreateTxnForDeleteText(nsGenericDOMDataNode& aElement,
461 : uint32_t aOffset, uint32_t aLength);
462 :
463 : already_AddRefed<DeleteTextTransaction>
464 : CreateTxnForDeleteCharacter(nsGenericDOMDataNode& aData, uint32_t aOffset,
465 : EDirection aDirection);
466 :
467 : already_AddRefed<SplitNodeTransaction>
468 : CreateTxnForSplitNode(nsIContent& aNode, uint32_t aOffset);
469 :
470 : already_AddRefed<JoinNodeTransaction>
471 : CreateTxnForJoinNode(nsINode& aLeftNode, nsINode& aRightNode);
472 :
473 : /**
474 : * This method first deletes the selection, if it's not collapsed. Then if
475 : * the selection lies in a CharacterData node, it splits it. If the
476 : * selection is at this point collapsed in a CharacterData node, it's
477 : * adjusted to be collapsed right before or after the node instead (which is
478 : * always possible, since the node was split).
479 : */
480 : nsresult DeleteSelectionAndPrepareToCreateNode();
481 :
482 : /**
483 : * Called after a transaction is done successfully.
484 : */
485 : void DoAfterDoTransaction(nsITransaction *aTxn);
486 :
487 : /**
488 : * Called after a transaction is undone successfully.
489 : */
490 :
491 : void DoAfterUndoTransaction();
492 :
493 : /**
494 : * Called after a transaction is redone successfully.
495 : */
496 : void DoAfterRedoTransaction();
497 :
498 : enum TDocumentListenerNotification
499 : {
500 : eDocumentCreated,
501 : eDocumentToBeDestroyed,
502 : eDocumentStateChanged
503 : };
504 :
505 : /**
506 : * Tell the doc state listeners that the doc state has changed.
507 : */
508 : nsresult NotifyDocumentListeners(
509 : TDocumentListenerNotification aNotificationType);
510 :
511 : /**
512 : * Make the given selection span the entire document.
513 : */
514 : virtual nsresult SelectEntireDocument(Selection* aSelection);
515 :
516 : /**
517 : * Helper method for scrolling the selection into view after
518 : * an edit operation. aScrollToAnchor should be true if you
519 : * want to scroll to the point where the selection was started.
520 : * If false, it attempts to scroll the end of the selection into view.
521 : *
522 : * Editor methods *should* call this method instead of the versions
523 : * in the various selection interfaces, since this version makes sure
524 : * that the editor's sync/async settings for reflowing, painting, and
525 : * scrolling match.
526 : */
527 : nsresult ScrollSelectionIntoView(bool aScrollToAnchor);
528 :
529 : virtual bool IsBlockNode(nsINode* aNode);
530 :
531 : /**
532 : * Helper for GetPriorNode() and GetNextNode().
533 : */
534 : nsIContent* FindNextLeafNode(nsINode* aCurrentNode,
535 : bool aGoForward,
536 : bool bNoBlockCrossing);
537 :
538 : virtual nsresult InstallEventListeners();
539 : virtual void CreateEventListeners();
540 : virtual void RemoveEventListeners();
541 :
542 : /**
543 : * Return true if spellchecking should be enabled for this editor.
544 : */
545 : bool GetDesiredSpellCheckState();
546 :
547 9 : bool CanEnableSpellCheck()
548 : {
549 : // Check for password/readonly/disabled, which are not spellchecked
550 : // regardless of DOM. Also, check to see if spell check should be skipped
551 : // or not.
552 16 : return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() &&
553 16 : !ShouldSkipSpellCheck();
554 : }
555 :
556 : /**
557 : * EnsureComposition() should be called by composition event handlers. This
558 : * tries to get the composition for the event and set it to mComposition.
559 : * However, this may fail because the composition may be committed before
560 : * the event comes to the editor.
561 : *
562 : * @return true if there is a composition. Otherwise, for example,
563 : * a composition event handler in web contents moved focus
564 : * for committing the composition, returns false.
565 : */
566 : bool EnsureComposition(WidgetCompositionEvent* aCompositionEvent);
567 :
568 : already_AddRefed<nsISelectionController> GetSelectionController();
569 : nsresult GetSelection(SelectionType aSelectionType,
570 : nsISelection** aSelection);
571 :
572 : public:
573 : /**
574 : * All editor operations which alter the doc should be prefaced
575 : * with a call to StartOperation, naming the action and direction.
576 : */
577 : NS_IMETHOD StartOperation(EditAction opID,
578 : nsIEditor::EDirection aDirection);
579 :
580 : /**
581 : * All editor operations which alter the doc should be followed
582 : * with a call to EndOperation.
583 : */
584 : NS_IMETHOD EndOperation();
585 :
586 : /**
587 : * Routines for managing the preservation of selection across
588 : * various editor actions.
589 : */
590 : bool ArePreservingSelection();
591 : void PreserveSelectionAcrossActions(Selection* aSel);
592 : nsresult RestorePreservedSelection(Selection* aSel);
593 : void StopPreservingSelection();
594 :
595 : /**
596 : * SplitNode() creates a new node identical to an existing node, and split
597 : * the contents between the two nodes
598 : * @param aExistingRightNode The node to split. It will become the new
599 : * node's next sibling.
600 : * @param aOffset The offset of aExistingRightNode's
601 : * content|children to do the split at
602 : * @param aNewLeftNode The new node resulting from the split, becomes
603 : * aExistingRightNode's previous sibling.
604 : */
605 : nsresult SplitNodeImpl(nsIContent& aExistingRightNode,
606 : int32_t aOffset,
607 : nsIContent& aNewLeftNode);
608 :
609 : /**
610 : * JoinNodes() takes 2 nodes and merge their content|children.
611 : * @param aNodeToKeep The node that will remain after the join.
612 : * @param aNodeToJoin The node that will be joined with aNodeToKeep.
613 : * There is no requirement that the two nodes be of the
614 : * same type.
615 : * @param aParent The parent of aNodeToKeep
616 : */
617 : nsresult JoinNodesImpl(nsINode* aNodeToKeep,
618 : nsINode* aNodeToJoin,
619 : nsINode* aParent);
620 :
621 : /**
622 : * Return the offset of aChild in aParent. Asserts fatally if parent or
623 : * child is null, or parent is not child's parent.
624 : */
625 : static int32_t GetChildOffset(nsIDOMNode* aChild,
626 : nsIDOMNode* aParent);
627 :
628 : /**
629 : * Set outOffset to the offset of aChild in the parent.
630 : * Returns the parent of aChild.
631 : */
632 : static already_AddRefed<nsIDOMNode> GetNodeLocation(nsIDOMNode* aChild,
633 : int32_t* outOffset);
634 : static nsINode* GetNodeLocation(nsINode* aChild, int32_t* aOffset);
635 :
636 : /**
637 : * Returns the number of things inside aNode in the out-param aCount.
638 : * @param aNode is the node to get the length of.
639 : * If aNode is text, returns number of characters.
640 : * If not, returns number of children nodes.
641 : * @param aCount [OUT] the result of the above calculation.
642 : */
643 : static nsresult GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount);
644 :
645 : /**
646 : * Get the node immediately prior to aCurrentNode.
647 : * @param aCurrentNode the node from which we start the search
648 : * @param aEditableNode if true, only return an editable node
649 : * @param aResultNode [OUT] the node that occurs before aCurrentNode in
650 : * the tree, skipping non-editable nodes if
651 : * aEditableNode is true. If there is no prior
652 : * node, aResultNode will be nullptr.
653 : * @param bNoBlockCrossing If true, don't move across "block" nodes,
654 : * whatever that means.
655 : */
656 : nsIContent* GetPriorNode(nsINode* aCurrentNode, bool aEditableNode,
657 : bool aNoBlockCrossing = false);
658 :
659 : /**
660 : * And another version that takes a {parent,offset} pair rather than a node.
661 : */
662 : nsIContent* GetPriorNode(nsINode* aParentNode,
663 : int32_t aOffset,
664 : bool aEditableNode,
665 : bool aNoBlockCrossing = false);
666 :
667 :
668 : /**
669 : * Get the node immediately after to aCurrentNode.
670 : * @param aCurrentNode the node from which we start the search
671 : * @param aEditableNode if true, only return an editable node
672 : * @param aResultNode [OUT] the node that occurs after aCurrentNode in the
673 : * tree, skipping non-editable nodes if
674 : * aEditableNode is true. If there is no prior
675 : * node, aResultNode will be nullptr.
676 : */
677 : nsIContent* GetNextNode(nsINode* aCurrentNode,
678 : bool aEditableNode,
679 : bool bNoBlockCrossing = false);
680 :
681 : /**
682 : * And another version that takes a {parent,offset} pair rather than a node.
683 : */
684 : nsIContent* GetNextNode(nsINode* aParentNode,
685 : int32_t aOffset,
686 : bool aEditableNode,
687 : bool aNoBlockCrossing = false);
688 :
689 : /**
690 : * Helper for GetNextNode() and GetPriorNode().
691 : */
692 : nsIContent* FindNode(nsINode* aCurrentNode,
693 : bool aGoForward,
694 : bool aEditableNode,
695 : bool bNoBlockCrossing);
696 : /**
697 : * Get the rightmost child of aCurrentNode;
698 : * return nullptr if aCurrentNode has no children.
699 : */
700 : nsIContent* GetRightmostChild(nsINode* aCurrentNode,
701 : bool bNoBlockCrossing = false);
702 :
703 : /**
704 : * Get the leftmost child of aCurrentNode;
705 : * return nullptr if aCurrentNode has no children.
706 : */
707 : nsIContent* GetLeftmostChild(nsINode *aCurrentNode,
708 : bool bNoBlockCrossing = false);
709 :
710 : /**
711 : * Returns true if aNode is of the type implied by aTag.
712 : */
713 0 : static inline bool NodeIsType(nsIDOMNode* aNode, nsIAtom* aTag)
714 : {
715 0 : return GetTag(aNode) == aTag;
716 : }
717 :
718 : /**
719 : * Returns true if aParent can contain a child of type aTag.
720 : */
721 : bool CanContain(nsINode& aParent, nsIContent& aChild);
722 : bool CanContainTag(nsINode& aParent, nsIAtom& aTag);
723 : bool TagCanContain(nsIAtom& aParentTag, nsIContent& aChild);
724 : virtual bool TagCanContainTag(nsIAtom& aParentTag, nsIAtom& aChildTag);
725 :
726 : /**
727 : * Returns true if aNode is our root node.
728 : */
729 : bool IsRoot(nsIDOMNode* inNode);
730 : bool IsRoot(nsINode* inNode);
731 : bool IsEditorRoot(nsINode* aNode);
732 :
733 : /**
734 : * Returns true if aNode is a descendant of our root node.
735 : */
736 : bool IsDescendantOfRoot(nsIDOMNode* inNode);
737 : bool IsDescendantOfRoot(nsINode* inNode);
738 : bool IsDescendantOfEditorRoot(nsINode* aNode);
739 :
740 : /**
741 : * Returns true if aNode is a container.
742 : */
743 : virtual bool IsContainer(nsINode* aNode);
744 : virtual bool IsContainer(nsIDOMNode* aNode);
745 :
746 : /**
747 : * returns true if aNode is an editable node.
748 : */
749 : bool IsEditable(nsIDOMNode* aNode);
750 : virtual bool IsEditable(nsINode* aNode);
751 :
752 : /**
753 : * Returns true if aNode is a MozEditorBogus node.
754 : */
755 : bool IsMozEditorBogusNode(nsINode* aNode);
756 :
757 : /**
758 : * Counts number of editable child nodes.
759 : */
760 : uint32_t CountEditableChildren(nsINode* aNode);
761 :
762 : /**
763 : * Find the deep first and last children.
764 : */
765 : nsINode* GetFirstEditableNode(nsINode* aRoot);
766 :
767 : /**
768 : * Returns current composition.
769 : */
770 : TextComposition* GetComposition() const;
771 :
772 : /**
773 : * Returns true if there is composition string and not fixed.
774 : */
775 : bool IsIMEComposing() const;
776 :
777 : /**
778 : * Returns true when inserting text should be a part of current composition.
779 : */
780 : bool ShouldHandleIMEComposition() const;
781 :
782 : /**
783 : * Returns number of undo or redo items. If TransactionManager returns
784 : * unexpected error, returns -1.
785 : */
786 : int32_t NumberOfUndoItems() const;
787 : int32_t NumberOfRedoItems() const;
788 :
789 : /**
790 : * From html rules code - migration in progress.
791 : */
792 : static nsresult GetTagString(nsIDOMNode* aNode, nsAString& outString);
793 : static nsIAtom* GetTag(nsIDOMNode* aNode);
794 :
795 : bool NodesSameType(nsIDOMNode* aNode1, nsIDOMNode* aNode2);
796 : virtual bool AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2);
797 :
798 : static bool IsTextNode(nsIDOMNode* aNode);
799 : static bool IsTextNode(nsINode* aNode);
800 :
801 : static nsCOMPtr<nsIDOMNode> GetChildAt(nsIDOMNode* aParent, int32_t aOffset);
802 : static nsIContent* GetNodeAtRangeOffsetPoint(nsINode* aParentOrNode,
803 : int32_t aOffset);
804 :
805 : static nsresult GetStartNodeAndOffset(Selection* aSelection,
806 : nsIDOMNode** outStartNode,
807 : int32_t* outStartOffset);
808 : static nsresult GetStartNodeAndOffset(Selection* aSelection,
809 : nsINode** aStartContainer,
810 : int32_t* aStartOffset);
811 : static nsresult GetEndNodeAndOffset(Selection* aSelection,
812 : nsIDOMNode** outEndNode,
813 : int32_t* outEndOffset);
814 : static nsresult GetEndNodeAndOffset(Selection* aSelection,
815 : nsINode** aEndContainer,
816 : int32_t* aEndOffset);
817 : #if DEBUG_JOE
818 : static void DumpNode(nsIDOMNode* aNode, int32_t indent = 0);
819 : #endif
820 : Selection* GetSelection(SelectionType aSelectionType =
821 : SelectionType::eNormal);
822 :
823 : /**
824 : * Helpers to add a node to the selection.
825 : * Used by table cell selection methods.
826 : */
827 : nsresult CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
828 : nsIDOMNode* aEndContainer, int32_t aEndOffset,
829 : nsRange** aRange);
830 :
831 : /**
832 : * Creates a range with just the supplied node and appends that to the
833 : * selection.
834 : */
835 : nsresult AppendNodeToSelectionAsRange(nsIDOMNode *aNode);
836 :
837 : /**
838 : * When you are using AppendNodeToSelectionAsRange(), call this first to
839 : * start a new selection.
840 : */
841 : nsresult ClearSelection();
842 :
843 : nsresult IsPreformatted(nsIDOMNode* aNode, bool* aResult);
844 :
845 : enum class EmptyContainers { no, yes };
846 : int32_t SplitNodeDeep(nsIContent& aNode, nsIContent& aSplitPointParent,
847 : int32_t aSplitPointOffset,
848 : EmptyContainers aEmptyContainers =
849 : EmptyContainers::yes,
850 : nsIContent** outLeftNode = nullptr,
851 : nsIContent** outRightNode = nullptr);
852 : EditorDOMPoint JoinNodeDeep(nsIContent& aLeftNode,
853 : nsIContent& aRightNode);
854 :
855 : nsresult GetString(const nsAString& name, nsAString& value);
856 :
857 : void BeginUpdateViewBatch();
858 : virtual nsresult EndUpdateViewBatch();
859 :
860 : bool GetShouldTxnSetSelection();
861 :
862 : virtual nsresult HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent);
863 :
864 : nsresult HandleInlineSpellCheck(EditAction action,
865 : Selection* aSelection,
866 : nsIDOMNode* previousSelectedNode,
867 : int32_t previousSelectedOffset,
868 : nsIDOMNode* aStartContainer,
869 : int32_t aStartOffset,
870 : nsIDOMNode* aEndContainer,
871 : int32_t aEndOffset);
872 :
873 : virtual already_AddRefed<dom::EventTarget> GetDOMEventTarget() = 0;
874 :
875 : /**
876 : * Fast non-refcounting editor root element accessor
877 : */
878 : Element* GetRoot();
879 :
880 : /**
881 : * Likewise, but gets the editor's root instead, which is different for HTML
882 : * editors.
883 : */
884 : virtual Element* GetEditorRoot();
885 :
886 : /**
887 : * Likewise, but gets the text control element instead of the root for
888 : * plaintext editors.
889 : */
890 : Element* GetExposedRoot();
891 :
892 : /**
893 : * Accessor methods to flags.
894 : */
895 3 : uint32_t Flags() const { return mFlags; }
896 :
897 6 : bool IsPlaintextEditor() const
898 : {
899 6 : return (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0;
900 : }
901 :
902 8 : bool IsSingleLineEditor() const
903 : {
904 8 : return (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) != 0;
905 : }
906 :
907 18 : bool IsPasswordEditor() const
908 : {
909 18 : return (mFlags & nsIPlaintextEditor::eEditorPasswordMask) != 0;
910 : }
911 :
912 : // FYI: Both IsRightToLeft() and IsLeftToRight() may return false if
913 : // the editor inherits the content node's direction.
914 1 : bool IsRightToLeft() const
915 : {
916 1 : return (mFlags & nsIPlaintextEditor::eEditorRightToLeft) != 0;
917 : }
918 1 : bool IsLeftToRight() const
919 : {
920 1 : return (mFlags & nsIPlaintextEditor::eEditorLeftToRight) != 0;
921 : }
922 :
923 9 : bool IsReadonly() const
924 : {
925 9 : return (mFlags & nsIPlaintextEditor::eEditorReadonlyMask) != 0;
926 : }
927 :
928 9 : bool IsDisabled() const
929 : {
930 9 : return (mFlags & nsIPlaintextEditor::eEditorDisabledMask) != 0;
931 : }
932 :
933 0 : bool IsInputFiltered() const
934 : {
935 0 : return (mFlags & nsIPlaintextEditor::eEditorFilterInputMask) != 0;
936 : }
937 :
938 1 : bool IsMailEditor() const
939 : {
940 1 : return (mFlags & nsIPlaintextEditor::eEditorMailMask) != 0;
941 : }
942 :
943 0 : bool IsWrapHackEnabled() const
944 : {
945 0 : return (mFlags & nsIPlaintextEditor::eEditorEnableWrapHackMask) != 0;
946 : }
947 :
948 0 : bool IsFormWidget() const
949 : {
950 0 : return (mFlags & nsIPlaintextEditor::eEditorWidgetMask) != 0;
951 : }
952 :
953 0 : bool NoCSS() const
954 : {
955 0 : return (mFlags & nsIPlaintextEditor::eEditorNoCSSMask) != 0;
956 : }
957 :
958 0 : bool IsInteractionAllowed() const
959 : {
960 0 : return (mFlags & nsIPlaintextEditor::eEditorAllowInteraction) != 0;
961 : }
962 :
963 0 : bool DontEchoPassword() const
964 : {
965 0 : return (mFlags & nsIPlaintextEditor::eEditorDontEchoPassword) != 0;
966 : }
967 :
968 7 : bool ShouldSkipSpellCheck() const
969 : {
970 7 : return (mFlags & nsIPlaintextEditor::eEditorSkipSpellCheck) != 0;
971 : }
972 :
973 0 : bool IsTabbable() const
974 : {
975 0 : return IsSingleLineEditor() || IsPasswordEditor() || IsFormWidget() ||
976 0 : IsInteractionAllowed();
977 : }
978 :
979 0 : bool HasIndependentSelection() const
980 : {
981 0 : return !!mSelectionControllerWeak;
982 : }
983 :
984 0 : bool IsModifiable() const
985 : {
986 0 : return !IsReadonly();
987 : }
988 :
989 : /**
990 : * IsInEditAction() return true while the instance is handling an edit action.
991 : * Otherwise, false.
992 : */
993 1 : bool IsInEditAction() const { return mIsInEditAction; }
994 :
995 : /**
996 : * IsSuppressingDispatchingInputEvent() returns true if the editor stops
997 : * dispatching input event. Otherwise, false.
998 : */
999 1 : bool IsSuppressingDispatchingInputEvent() const
1000 : {
1001 1 : return !mDispatchInputEvent;
1002 : }
1003 :
1004 : /**
1005 : * GetTransactionManager() returns transaction manager associated with the
1006 : * editor. This may return nullptr if undo/redo hasn't been enabled.
1007 : */
1008 : already_AddRefed<nsITransactionManager> GetTransactionManager() const;
1009 :
1010 : /**
1011 : * Get the input event target. This might return null.
1012 : */
1013 : virtual already_AddRefed<nsIContent> GetInputEventTargetContent() = 0;
1014 :
1015 : /**
1016 : * Get the focused content, if we're focused. Returns null otherwise.
1017 : */
1018 : virtual already_AddRefed<nsIContent> GetFocusedContent();
1019 :
1020 : /**
1021 : * Get the focused content for the argument of some IMEStateManager's
1022 : * methods.
1023 : */
1024 : virtual already_AddRefed<nsIContent> GetFocusedContentForIME();
1025 :
1026 : /**
1027 : * Whether the editor is active on the DOM window. Note that when this
1028 : * returns true but GetFocusedContent() returns null, it means that this editor was
1029 : * focused when the DOM window was active.
1030 : */
1031 : virtual bool IsActiveInDOMWindow();
1032 :
1033 : /**
1034 : * Whether the aGUIEvent should be handled by this editor or not. When this
1035 : * returns false, The aGUIEvent shouldn't be handled on this editor,
1036 : * i.e., The aGUIEvent should be handled by another inner editor or ancestor
1037 : * elements.
1038 : */
1039 : virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent);
1040 :
1041 : /**
1042 : * FindSelectionRoot() returns a selection root of this editor when aNode
1043 : * gets focus. aNode must be a content node or a document node. When the
1044 : * target isn't a part of this editor, returns nullptr. If this is for
1045 : * designMode, you should set the document node to aNode except that an
1046 : * element in the document has focus.
1047 : */
1048 : virtual already_AddRefed<nsIContent> FindSelectionRoot(nsINode* aNode);
1049 :
1050 : /**
1051 : * Initializes selection and caret for the editor. If aEventTarget isn't
1052 : * a host of the editor, i.e., the editor doesn't get focus, this does
1053 : * nothing.
1054 : */
1055 : nsresult InitializeSelection(nsIDOMEventTarget* aFocusEventTarget);
1056 :
1057 : /**
1058 : * This method has to be called by EditorEventListener::Focus.
1059 : * All actions that have to be done when the editor is focused needs to be
1060 : * added here.
1061 : */
1062 : void OnFocus(nsIDOMEventTarget* aFocusEventTarget);
1063 :
1064 : /**
1065 : * Used to insert content from a data transfer into the editable area.
1066 : * This is called for each item in the data transfer, with the index of
1067 : * each item passed as aIndex.
1068 : */
1069 : virtual nsresult InsertFromDataTransfer(dom::DataTransfer* aDataTransfer,
1070 : int32_t aIndex,
1071 : nsIDOMDocument* aSourceDoc,
1072 : nsIDOMNode* aDestinationNode,
1073 : int32_t aDestOffset,
1074 : bool aDoDeleteSelection) = 0;
1075 :
1076 : virtual nsresult InsertFromDrop(nsIDOMEvent* aDropEvent) = 0;
1077 :
1078 : /**
1079 : * GetIMESelectionStartOffsetIn() returns the start offset of IME selection in
1080 : * the aTextNode. If there is no IME selection, returns -1.
1081 : */
1082 : int32_t GetIMESelectionStartOffsetIn(nsINode* aTextNode);
1083 :
1084 : /**
1085 : * FindBetterInsertionPoint() tries to look for better insertion point which
1086 : * is typically the nearest text node and offset in it.
1087 : */
1088 : void FindBetterInsertionPoint(nsCOMPtr<nsIDOMNode>& aNode,
1089 : int32_t& aOffset);
1090 : void FindBetterInsertionPoint(nsCOMPtr<nsINode>& aNode,
1091 : int32_t& aOffset);
1092 :
1093 : /**
1094 : * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
1095 : * with nsCaret::RemoveForceHide(). This does NOT set visibility of
1096 : * nsCaret. Therefore, this is stateless.
1097 : */
1098 : void HideCaret(bool aHide);
1099 :
1100 : private:
1101 : // Weak reference to the nsISelectionController.
1102 : // Use GetSelectionController() to retrieve actual pointer.
1103 : CachedWeakPtr<nsISelectionController> mSelectionControllerWeak;
1104 : // Weak reference to the nsIDocument.
1105 : // Use GetDocument() to retrieve actual pointer.
1106 : CachedWeakPtr<nsIDocument> mDocumentWeak;
1107 :
1108 : protected:
1109 : enum Tristate
1110 : {
1111 : eTriUnset,
1112 : eTriFalse,
1113 : eTriTrue
1114 : };
1115 :
1116 : // MIME type of the doc we are editing.
1117 : nsCString mContentMIMEType;
1118 :
1119 : nsCOMPtr<nsIInlineSpellChecker> mInlineSpellChecker;
1120 :
1121 : RefPtr<nsTransactionManager> mTxnMgr;
1122 : // Cached root node.
1123 : nsCOMPtr<Element> mRootElement;
1124 : // Current IME text node.
1125 : RefPtr<Text> mIMETextNode;
1126 : // The form field as an event receiver.
1127 : nsCOMPtr<dom::EventTarget> mEventTarget;
1128 : nsCOMPtr<nsIDOMEventListener> mEventListener;
1129 : // Weak reference to placeholder for begin/end batch purposes.
1130 : WeakPtr<PlaceholderTransaction> mPlaceholderTransactionWeak;
1131 : // Name of placeholder transaction.
1132 : nsIAtom* mPlaceholderName;
1133 : // Saved selection state for placeholder transaction batching.
1134 : mozilla::UniquePtr<SelectionState> mSelState;
1135 : // IME composition this is not null between compositionstart and
1136 : // compositionend.
1137 : RefPtr<TextComposition> mComposition;
1138 :
1139 : // Listens to all low level actions on the doc.
1140 : typedef AutoTArray<OwningNonNull<nsIEditActionListener>, 5>
1141 : AutoActionListenerArray;
1142 : AutoActionListenerArray mActionListeners;
1143 : // Just notify once per high level change.
1144 : typedef AutoTArray<OwningNonNull<nsIEditorObserver>, 3>
1145 : AutoEditorObserverArray;
1146 : AutoEditorObserverArray mEditorObservers;
1147 : // Listen to overall doc state (dirty or not, just created, etc.).
1148 : typedef AutoTArray<OwningNonNull<nsIDocumentStateListener>, 1>
1149 : AutoDocumentStateListenerArray;
1150 : AutoDocumentStateListenerArray mDocStateListeners;
1151 :
1152 : // Cached selection for AutoSelectionRestorer.
1153 : SelectionState mSavedSel;
1154 : // Utility class object for maintaining preserved ranges.
1155 : RangeUpdater mRangeUpdater;
1156 :
1157 : // Number of modifications (for undo/redo stack).
1158 : uint32_t mModCount;
1159 : // Behavior flags. See nsIPlaintextEditor.idl for the flags we use.
1160 : uint32_t mFlags;
1161 :
1162 : int32_t mUpdateCount;
1163 :
1164 : // Nesting count for batching.
1165 : int32_t mPlaceholderBatch;
1166 : // The current editor action.
1167 : EditAction mAction;
1168 :
1169 : // Offset in text node where IME comp string begins.
1170 : uint32_t mIMETextOffset;
1171 : // The Length of the composition string or commit string. If this is length
1172 : // of commit string, the length is truncated by maxlength attribute.
1173 : uint32_t mIMETextLength;
1174 :
1175 : // The current direction of editor action.
1176 : EDirection mDirection;
1177 : // -1 = not initialized
1178 : int8_t mDocDirtyState;
1179 : // A Tristate value.
1180 : uint8_t mSpellcheckCheckboxState;
1181 :
1182 : // Turn off for conservative selection adjustment by transactions.
1183 : bool mShouldTxnSetSelection;
1184 : // Whether PreDestroy has been called.
1185 : bool mDidPreDestroy;
1186 : // Whether PostCreate has been called.
1187 : bool mDidPostCreate;
1188 : bool mDispatchInputEvent;
1189 : // True while the instance is handling an edit action.
1190 : bool mIsInEditAction;
1191 : // Whether caret is hidden forcibly.
1192 : bool mHidingCaret;
1193 : // Whether spellchecker dictionary is initialized after focused.
1194 : bool mSpellCheckerDictionaryUpdated;
1195 :
1196 : friend bool NSCanUnload(nsISupports* serviceMgr);
1197 : friend class AutoRules;
1198 : friend class AutoSelectionRestorer;
1199 : friend class AutoTransactionsConserveSelection;
1200 : friend class RangeUpdater;
1201 : };
1202 :
1203 : } // namespace mozilla
1204 :
1205 : #endif // #ifndef mozilla_EditorBase_h
|