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/. */
6 #include "mozilla/DebugOnly.h" // for DebugOnly
8 #include <stdio.h> // for nullptr, stdout
9 #include <string.h> // for strcmp
11 #include "ChangeAttributeTxn.h" // for ChangeAttributeTxn
12 #include "CreateElementTxn.h" // for CreateElementTxn
13 #include "DeleteNodeTxn.h" // for DeleteNodeTxn
14 #include "DeleteRangeTxn.h" // for DeleteRangeTxn
15 #include "DeleteTextTxn.h" // for DeleteTextTxn
16 #include "EditAggregateTxn.h" // for EditAggregateTxn
17 #include "EditTxn.h" // for EditTxn
18 #include "IMETextTxn.h" // for IMETextTxn
19 #include "InsertNodeTxn.h" // for InsertNodeTxn
20 #include "InsertTextTxn.h" // for InsertTextTxn
21 #include "JoinElementTxn.h" // for JoinElementTxn
22 #include "PlaceholderTxn.h" // for PlaceholderTxn
23 #include "SplitElementTxn.h" // for SplitElementTxn
24 #include "mozFlushType.h" // for mozFlushType::Flush_Frames
25 #include "mozISpellCheckingEngine.h"
26 #include "mozInlineSpellChecker.h" // for mozInlineSpellChecker
27 #include "mozilla/IMEStateManager.h" // for IMEStateManager
28 #include "mozilla/Preferences.h" // for Preferences
29 #include "mozilla/dom/Selection.h" // for Selection, etc
30 #include "mozilla/Services.h" // for GetObserverService
31 #include "mozilla/TextComposition.h" // for TextComposition
32 #include "mozilla/TextEvents.h"
33 #include "mozilla/dom/Element.h" // for Element, nsINode::AsElement
34 #include "mozilla/dom/Text.h"
35 #include "mozilla/mozalloc.h" // for operator new, etc
36 #include "nsAString.h" // for nsAString_internal::Length, etc
37 #include "nsCCUncollectableMarker.h" // for nsCCUncollectableMarker
38 #include "nsCaret.h" // for nsCaret
39 #include "nsCaseTreatment.h"
40 #include "nsCharTraits.h" // for NS_IS_HIGH_SURROGATE, etc
41 #include "nsComponentManagerUtils.h" // for do_CreateInstance
42 #include "nsComputedDOMStyle.h" // for nsComputedDOMStyle
43 #include "nsContentUtils.h" // for nsContentUtils
44 #include "nsDOMString.h" // for DOMStringIsNull
45 #include "nsDebug.h" // for NS_ENSURE_TRUE, etc
47 #include "nsEditorEventListener.h" // for nsEditorEventListener
48 #include "nsEditorUtils.h" // for nsAutoRules, etc
49 #include "nsError.h" // for NS_OK, etc
50 #include "nsFocusManager.h" // for nsFocusManager
51 #include "nsFrameSelection.h" // for nsFrameSelection
52 #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::dir
53 #include "nsIAbsorbingTransaction.h" // for nsIAbsorbingTransaction
54 #include "nsIAtom.h" // for nsIAtom
55 #include "nsIContent.h" // for nsIContent
56 #include "nsIDOMAttr.h" // for nsIDOMAttr
57 #include "nsIDOMCharacterData.h" // for nsIDOMCharacterData
58 #include "nsIDOMDocument.h" // for nsIDOMDocument
59 #include "nsIDOMElement.h" // for nsIDOMElement
60 #include "nsIDOMEvent.h" // for nsIDOMEvent
61 #include "nsIDOMEventListener.h" // for nsIDOMEventListener
62 #include "nsIDOMEventTarget.h" // for nsIDOMEventTarget
63 #include "nsIDOMHTMLElement.h" // for nsIDOMHTMLElement
64 #include "nsIDOMKeyEvent.h" // for nsIDOMKeyEvent, etc
65 #include "nsIDOMMozNamedAttrMap.h" // for nsIDOMMozNamedAttrMap
66 #include "nsIDOMMouseEvent.h" // for nsIDOMMouseEvent
67 #include "nsIDOMNode.h" // for nsIDOMNode, etc
68 #include "nsIDOMNodeList.h" // for nsIDOMNodeList
69 #include "nsIDOMRange.h" // for nsIDOMRange
70 #include "nsIDOMText.h" // for nsIDOMText
71 #include "nsIDocument.h" // for nsIDocument
72 #include "nsIDocumentStateListener.h" // for nsIDocumentStateListener
73 #include "nsIEditActionListener.h" // for nsIEditActionListener
74 #include "nsIEditorObserver.h" // for nsIEditorObserver
75 #include "nsIEditorSpellCheck.h" // for nsIEditorSpellCheck
76 #include "nsIFrame.h" // for nsIFrame
77 #include "nsIHTMLDocument.h" // for nsIHTMLDocument
78 #include "nsIInlineSpellChecker.h" // for nsIInlineSpellChecker, etc
79 #include "nsNameSpaceManager.h" // for kNameSpaceID_None, etc
80 #include "nsINode.h" // for nsINode, etc
81 #include "nsIObserverService.h" // for nsIObserverService
82 #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc
83 #include "nsIPresShell.h" // for nsIPresShell
84 #include "nsISelection.h" // for nsISelection, etc
85 #include "nsISelectionController.h" // for nsISelectionController, etc
86 #include "nsISelectionDisplay.h" // for nsISelectionDisplay, etc
87 #include "nsISelectionPrivate.h" // for nsISelectionPrivate, etc
88 #include "nsISupportsBase.h" // for nsISupports
89 #include "nsISupportsUtils.h" // for NS_ADDREF, NS_IF_ADDREF
90 #include "nsITransaction.h" // for nsITransaction
91 #include "nsITransactionManager.h"
92 #include "nsIWeakReference.h" // for nsISupportsWeakReference
93 #include "nsIWidget.h" // for nsIWidget, IMEState, etc
94 #include "nsPIDOMWindow.h" // for nsPIDOMWindow
95 #include "nsPresContext.h" // for nsPresContext
96 #include "nsRange.h" // for nsRange
97 #include "nsReadableUtils.h" // for EmptyString, ToNewCString
98 #include "nsString.h" // for nsAutoString, nsString, etc
99 #include "nsStringFwd.h" // for nsAFlatString
100 #include "nsStyleConsts.h" // for NS_STYLE_DIRECTION_RTL, etc
101 #include "nsStyleContext.h" // for nsStyleContext
102 #include "nsStyleSheetTxns.h" // for AddStyleSheetTxn, etc
103 #include "nsStyleStruct.h" // for nsStyleDisplay, nsStyleText, etc
104 #include "nsStyleStructFwd.h" // for nsIFrame::StyleUIReset, etc
105 #include "nsTextEditUtils.h" // for nsTextEditUtils
106 #include "nsTextNode.h" // for nsTextNode
107 #include "nsThreadUtils.h" // for nsRunnable
108 #include "nsTransactionManager.h" // for nsTransactionManager
109 #include "prtime.h" // for PR_Now
111 class nsIOutputStream
;
112 class nsIParserService
;
113 class nsITransferable
;
116 #include "nsIDOMHTMLDocument.h" // for nsIDOMHTMLDocument
119 using namespace mozilla
;
120 using namespace mozilla::dom
;
121 using namespace mozilla::widget
;
123 // Defined in nsEditorRegistration.cpp
124 extern nsIParserService
*sParserService
;
126 //---------------------------------------------------------------------------
128 // nsEditor: base editor class implementation
130 //---------------------------------------------------------------------------
133 : mPlaceHolderName(nullptr)
139 , mPlaceHolderBatch(0)
140 , mAction(EditAction::none
)
144 , mSpellcheckCheckboxState(eTriUnset
)
145 , mShouldTxnSetSelection(true)
146 , mDidPreDestroy(false)
147 , mDidPostCreate(false)
148 , mDispatchInputEvent(true)
149 , mIsInEditAction(false)
153 nsEditor::~nsEditor()
155 NS_ASSERTION(!mDocWeak
|| mDidPreDestroy
, "Why PreDestroy hasn't been called?");
162 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEditor
)
164 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEditor
)
165 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement
)
166 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker
)
167 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTxnMgr
)
168 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMETextNode
)
169 NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners
)
170 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers
)
171 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners
)
172 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget
)
173 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventListener
)
174 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
176 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEditor
)
177 nsIDocument
* currentDoc
=
178 tmp
->mRootElement
? tmp
->mRootElement
->GetCurrentDoc() : nullptr;
180 nsCCUncollectableMarker::InGeneration(cb
, currentDoc
->GetMarkedCCGeneration())) {
181 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
183 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement
)
184 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker
)
185 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTxnMgr
)
186 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMETextNode
)
187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners
)
188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorObservers
)
189 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners
)
190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget
)
191 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener
)
192 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
194 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor
)
195 NS_INTERFACE_MAP_ENTRY(nsIPhonetic
)
196 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
197 NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport
)
198 NS_INTERFACE_MAP_ENTRY(nsIEditor
)
199 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
200 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIEditor
)
203 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditor
)
204 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor
)
208 nsEditor::Init(nsIDOMDocument
*aDoc
, nsIContent
*aRoot
,
209 nsISelectionController
*aSelCon
, uint32_t aFlags
,
210 const nsAString
& aValue
)
212 NS_PRECONDITION(aDoc
, "bad arg");
214 return NS_ERROR_NULL_POINTER
;
216 // First only set flags, but other stuff shouldn't be initialized now.
217 // Don't move this call after initializing mDocWeak.
218 // SetFlags() can check whether it's called during initialization or not by
219 // them. Note that SetFlags() will be called by PostCreate().
224 NS_ASSERTION(NS_SUCCEEDED(rv
), "SetFlags() failed");
226 mDocWeak
= do_GetWeakReference(aDoc
); // weak reference to doc
227 // HTML editors currently don't have their own selection controller,
228 // so they'll pass null as aSelCon, and we'll get the selection controller
229 // off of the presshell.
230 nsCOMPtr
<nsISelectionController
> selCon
;
232 mSelConWeak
= do_GetWeakReference(aSelCon
); // weak reference to selectioncontroller
235 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
236 selCon
= do_QueryInterface(presShell
);
238 NS_ASSERTION(selCon
, "Selection controller should be available at this point");
240 //set up root element if we are passed one.
242 mRootElement
= do_QueryInterface(aRoot
);
246 /* initialize IME stuff */
247 mIMETextNode
= nullptr;
250 selCon
->SetCaretReadOnly(false);
251 selCon
->SetDisplaySelection(nsISelectionController::SELECTION_ON
);
253 selCon
->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL
);//we want to see all the selection reflected to user
255 NS_POSTCONDITION(mDocWeak
, "bad state");
257 // Make sure that the editor will be destroyed properly
258 mDidPreDestroy
= false;
259 // Make sure that the ediotr will be created properly
260 mDidPostCreate
= false;
267 nsEditor::PostCreate()
269 // Synchronize some stuff for the flags. SetFlags() will initialize
270 // something by the flag difference. This is first time of that, so, all
271 // initializations must be run. For such reason, we need to invert mFlags
274 nsresult rv
= SetFlags(~mFlags
);
275 NS_ENSURE_SUCCESS(rv
, rv
);
277 // These operations only need to happen on the first PostCreate call
278 if (!mDidPostCreate
) {
279 mDidPostCreate
= true;
282 CreateEventListeners();
283 rv
= InstallEventListeners();
284 NS_ENSURE_SUCCESS(rv
, rv
);
286 // nuke the modification count, so the doc appears unmodified
287 // do this before we notify listeners
288 ResetModificationCount();
290 // update the UI with our state
291 NotifyDocumentListeners(eDocumentCreated
);
292 NotifyDocumentListeners(eDocumentStateChanged
);
294 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
296 obs
->AddObserver(this,
297 SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION
,
302 // update nsTextStateManager and caret if we have focus
303 nsCOMPtr
<nsIContent
> focusedContent
= GetFocusedContent();
304 if (focusedContent
) {
305 nsCOMPtr
<nsIDOMEventTarget
> target
= do_QueryInterface(focusedContent
);
307 InitializeSelection(target
);
310 // If the text control gets reframed during focus, Focus() would not be
311 // called, so take a chance here to see if we need to spell check the text
313 nsEditorEventListener
* listener
=
314 reinterpret_cast<nsEditorEventListener
*> (mEventListener
.get());
315 listener
->SpellCheckIfNeeded();
318 rv
= GetPreferredIMEState(&newState
);
319 NS_ENSURE_SUCCESS(rv
, NS_OK
);
320 nsCOMPtr
<nsIContent
> content
= GetFocusedContentForIME();
321 IMEStateManager::UpdateIMEState(newState
, content
);
328 nsEditor::CreateEventListeners()
330 // Don't create the handler twice
331 if (!mEventListener
) {
332 mEventListener
= new nsEditorEventListener();
337 nsEditor::InstallEventListeners()
339 NS_ENSURE_TRUE(mDocWeak
&& mEventListener
,
340 NS_ERROR_NOT_INITIALIZED
);
342 // Initialize the event target.
343 nsCOMPtr
<nsIContent
> rootContent
= GetRoot();
344 NS_ENSURE_TRUE(rootContent
, NS_ERROR_NOT_AVAILABLE
);
345 mEventTarget
= do_QueryInterface(rootContent
->GetParent());
346 NS_ENSURE_TRUE(mEventTarget
, NS_ERROR_NOT_AVAILABLE
);
348 nsEditorEventListener
* listener
=
349 reinterpret_cast<nsEditorEventListener
*>(mEventListener
.get());
350 return listener
->Connect(this);
354 nsEditor::RemoveEventListeners()
356 if (!mDocWeak
|| !mEventListener
) {
359 reinterpret_cast<nsEditorEventListener
*>(mEventListener
.get())->Disconnect();
361 mComposition
->EndHandlingComposition(this);
362 mComposition
= nullptr;
364 mEventTarget
= nullptr;
368 nsEditor::GetDesiredSpellCheckState()
370 // Check user override on this element
371 if (mSpellcheckCheckboxState
!= eTriUnset
) {
372 return (mSpellcheckCheckboxState
== eTriTrue
);
375 // Check user preferences
376 int32_t spellcheckLevel
= Preferences::GetInt("layout.spellcheckDefault", 1);
378 if (spellcheckLevel
== 0) {
379 return false; // Spellchecking forced off globally
382 if (!CanEnableSpellCheck()) {
386 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
388 nsPresContext
* context
= presShell
->GetPresContext();
389 if (context
&& !context
->IsDynamic()) {
395 nsCOMPtr
<nsIContent
> content
= GetExposedRoot();
400 nsCOMPtr
<nsIDOMHTMLElement
> element
= do_QueryInterface(content
);
405 if (!IsPlaintextEditor()) {
406 // Some of the page content might be editable and some not, if spellcheck=
407 // is explicitly set anywhere, so if there's anything editable on the page,
408 // return true and let the spellchecker figure it out.
409 nsCOMPtr
<nsIHTMLDocument
> doc
= do_QueryInterface(content
->GetCurrentDoc());
410 return doc
&& doc
->IsEditingOn();
414 element
->GetSpellcheck(&enable
);
420 nsEditor::PreDestroy(bool aDestroyingFrames
)
425 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
427 obs
->RemoveObserver(this,
428 SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION
);
431 // Let spellchecker clean up its observers etc. It is important not to
432 // actually free the spellchecker here, since the spellchecker could have
433 // caused flush notifications, which could have gotten here if a textbox
434 // is being removed. Setting the spellchecker to nullptr could free the
435 // object that is still in use! It will be freed when the editor is
437 if (mInlineSpellChecker
)
438 mInlineSpellChecker
->Cleanup(aDestroyingFrames
);
440 // tell our listeners that the doc is going away
441 NotifyDocumentListeners(eDocumentToBeDestroyed
);
443 // Unregister event listeners
444 RemoveEventListeners();
445 mActionListeners
.Clear();
446 mEditorObservers
.Clear();
447 mDocStateListeners
.Clear();
448 mInlineSpellChecker
= nullptr;
449 mSpellcheckCheckboxState
= eTriUnset
;
450 mRootElement
= nullptr;
452 mDidPreDestroy
= true;
457 nsEditor::GetFlags(uint32_t *aFlags
)
464 nsEditor::SetFlags(uint32_t aFlags
)
466 if (mFlags
== aFlags
) {
470 bool spellcheckerWasEnabled
= CanEnableSpellCheck();
474 // If we're initializing, we shouldn't do anything now.
475 // SetFlags() will be called by PostCreate(),
476 // we should synchronize some stuff for the flags at that time.
480 // The flag change may cause the spellchecker state change
481 if (CanEnableSpellCheck() != spellcheckerWasEnabled
) {
482 nsresult rv
= SyncRealTimeSpell();
483 NS_ENSURE_SUCCESS(rv
, rv
);
486 // If this is called from PostCreate(), it will update the IME state if it's
488 if (!mDidPostCreate
) {
492 // Might be changing editable state, so, we need to reset current IME state
493 // if we're focused and the flag change causes IME state change.
494 nsCOMPtr
<nsIContent
> focusedContent
= GetFocusedContent();
495 if (focusedContent
) {
497 nsresult rv
= GetPreferredIMEState(&newState
);
498 if (NS_SUCCEEDED(rv
)) {
499 // NOTE: When the enabled state isn't going to be modified, this method
500 // is going to do nothing.
501 nsCOMPtr
<nsIContent
> content
= GetFocusedContentForIME();
502 IMEStateManager::UpdateIMEState(newState
, content
);
510 nsEditor::GetIsSelectionEditable(bool *aIsSelectionEditable
)
512 NS_ENSURE_ARG_POINTER(aIsSelectionEditable
);
514 // get current selection
515 nsRefPtr
<Selection
> selection
= GetSelection();
516 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
518 // XXX we just check that the anchor node is editable at the moment
519 // we should check that all nodes in the selection are editable
520 nsCOMPtr
<nsINode
> anchorNode
= selection
->GetAnchorNode();
521 *aIsSelectionEditable
= anchorNode
&& IsEditable(anchorNode
);
527 nsEditor::GetIsDocumentEditable(bool *aIsDocumentEditable
)
529 NS_ENSURE_ARG_POINTER(aIsDocumentEditable
);
530 nsCOMPtr
<nsIDocument
> doc
= GetDocument();
531 *aIsDocumentEditable
= !!doc
;
536 already_AddRefed
<nsIDocument
>
537 nsEditor::GetDocument()
539 NS_PRECONDITION(mDocWeak
, "bad state, mDocWeak weak pointer not initialized");
540 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
544 already_AddRefed
<nsIDOMDocument
>
545 nsEditor::GetDOMDocument()
547 NS_PRECONDITION(mDocWeak
, "bad state, mDocWeak weak pointer not initialized");
548 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryReferent(mDocWeak
);
553 nsEditor::GetDocument(nsIDOMDocument
**aDoc
)
555 *aDoc
= GetDOMDocument().take();
556 return *aDoc
? NS_OK
: NS_ERROR_NOT_INITIALIZED
;
559 already_AddRefed
<nsIPresShell
>
560 nsEditor::GetPresShell()
562 NS_PRECONDITION(mDocWeak
, "bad state, null mDocWeak");
563 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
564 NS_ENSURE_TRUE(doc
, nullptr);
565 nsCOMPtr
<nsIPresShell
> ps
= doc
->GetShell();
569 already_AddRefed
<nsIWidget
>
570 nsEditor::GetWidget()
572 nsCOMPtr
<nsIPresShell
> ps
= GetPresShell();
573 NS_ENSURE_TRUE(ps
, nullptr);
574 nsPresContext
* pc
= ps
->GetPresContext();
575 NS_ENSURE_TRUE(pc
, nullptr);
576 nsCOMPtr
<nsIWidget
> widget
= pc
->GetRootWidget();
577 NS_ENSURE_TRUE(widget
.get(), nullptr);
578 return widget
.forget();
581 /* attribute string contentsMIMEType; */
583 nsEditor::GetContentsMIMEType(char * *aContentsMIMEType
)
585 NS_ENSURE_ARG_POINTER(aContentsMIMEType
);
586 *aContentsMIMEType
= ToNewCString(mContentMIMEType
);
591 nsEditor::SetContentsMIMEType(const char * aContentsMIMEType
)
593 mContentMIMEType
.Assign(aContentsMIMEType
? aContentsMIMEType
: "");
598 nsEditor::GetSelectionController(nsISelectionController
**aSel
)
600 NS_ENSURE_TRUE(aSel
, NS_ERROR_NULL_POINTER
);
601 *aSel
= nullptr; // init out param
602 nsCOMPtr
<nsISelectionController
> selCon
;
604 selCon
= do_QueryReferent(mSelConWeak
);
606 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
607 selCon
= do_QueryInterface(presShell
);
609 NS_ENSURE_TRUE(selCon
, NS_ERROR_NOT_INITIALIZED
);
610 NS_ADDREF(*aSel
= selCon
);
616 nsEditor::DeleteSelection(EDirection aAction
, EStripWrappers aStripWrappers
)
618 MOZ_ASSERT(aStripWrappers
== eStrip
|| aStripWrappers
== eNoStrip
);
619 return DeleteSelectionImpl(aAction
, aStripWrappers
);
625 nsEditor::GetSelection(nsISelection
**aSelection
)
627 NS_ENSURE_TRUE(aSelection
, NS_ERROR_NULL_POINTER
);
628 *aSelection
= nullptr;
629 nsCOMPtr
<nsISelectionController
> selcon
;
630 GetSelectionController(getter_AddRefs(selcon
));
631 NS_ENSURE_TRUE(selcon
, NS_ERROR_NOT_INITIALIZED
);
632 return selcon
->GetSelection(nsISelectionController::SELECTION_NORMAL
, aSelection
); // does an addref
636 nsEditor::GetSelection()
638 nsCOMPtr
<nsISelection
> sel
;
639 nsresult res
= GetSelection(getter_AddRefs(sel
));
640 NS_ENSURE_SUCCESS(res
, nullptr);
642 return static_cast<Selection
*>(sel
.get());
646 nsEditor::DoTransaction(nsITransaction
* aTxn
)
648 if (mPlaceHolderBatch
&& !mPlaceHolderTxn
) {
649 nsCOMPtr
<nsIAbsorbingTransaction
> plcTxn
= new PlaceholderTxn();
651 // save off weak reference to placeholder txn
652 mPlaceHolderTxn
= do_GetWeakReference(plcTxn
);
653 plcTxn
->Init(mPlaceHolderName
, mSelState
, this);
654 // placeholder txn took ownership of this pointer
657 // QI to an nsITransaction since that's what DoTransaction() expects
658 nsCOMPtr
<nsITransaction
> theTxn
= do_QueryInterface(plcTxn
);
659 // we will recurse, but will not hit this case in the nested call
660 DoTransaction(theTxn
);
663 nsCOMPtr
<nsITransaction
> topTxn
= mTxnMgr
->PeekUndoStack();
665 plcTxn
= do_QueryInterface(topTxn
);
667 // there is a placeholder transaction on top of the undo stack. It
668 // is either the one we just created, or an earlier one that we are
669 // now merging into. From here on out remember this placeholder
670 // instead of the one we just created.
671 mPlaceHolderTxn
= do_GetWeakReference(plcTxn
);
678 // XXX: Why are we doing selection specific batching stuff here?
679 // XXX: Most entry points into the editor have auto variables that
680 // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
681 // XXX: these selection batch calls no-ops.
683 // XXX: I suspect that this was placed here to avoid multiple
684 // XXX: selection changed notifications from happening until after
685 // XXX: the transaction was done. I suppose that can still happen
686 // XXX: if an embedding application called DoTransaction() directly
687 // XXX: to pump its own transactions through the system, but in that
688 // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
689 // XXX: its auto equivalent nsAutoUpdateViewBatch to ensure that
690 // XXX: selection listeners have access to accurate frame data?
692 // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
693 // XXX: we will need to make sure that they are disabled during
694 // XXX: the init of the editor for text widgets to avoid layout
695 // XXX: re-entry during initial reflow. - kin
697 // get the selection and start a batch change
698 nsRefPtr
<Selection
> selection
= GetSelection();
699 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
701 selection
->StartBatchChanges();
705 res
= mTxnMgr
->DoTransaction(aTxn
);
707 res
= aTxn
->DoTransaction();
709 if (NS_SUCCEEDED(res
)) {
710 DoAfterDoTransaction(aTxn
);
713 // no need to check res here, don't lose result of operation
714 selection
->EndBatchChanges();
716 NS_ENSURE_SUCCESS(res
, res
);
724 nsEditor::EnableUndo(bool aEnable
)
728 mTxnMgr
= new nsTransactionManager();
730 mTxnMgr
->SetMaxTransactionCount(-1);
731 } else if (mTxnMgr
) {
732 // disable the transaction manager if it is enabled
734 mTxnMgr
->SetMaxTransactionCount(0);
741 nsEditor::GetNumberOfUndoItems(int32_t* aNumItems
)
744 return mTxnMgr
? mTxnMgr
->GetNumberOfUndoItems(aNumItems
) : NS_OK
;
748 nsEditor::GetNumberOfRedoItems(int32_t* aNumItems
)
751 return mTxnMgr
? mTxnMgr
->GetNumberOfRedoItems(aNumItems
) : NS_OK
;
755 nsEditor::GetTransactionManager(nsITransactionManager
* *aTxnManager
)
757 NS_ENSURE_ARG_POINTER(aTxnManager
);
759 *aTxnManager
= nullptr;
760 NS_ENSURE_TRUE(mTxnMgr
, NS_ERROR_FAILURE
);
762 NS_ADDREF(*aTxnManager
= mTxnMgr
);
767 nsEditor::SetTransactionManager(nsITransactionManager
*aTxnManager
)
769 NS_ENSURE_TRUE(aTxnManager
, NS_ERROR_FAILURE
);
771 // nsITransactionManager is builtinclass, so this is safe
772 mTxnMgr
= static_cast<nsTransactionManager
*>(aTxnManager
);
777 nsEditor::Undo(uint32_t aCount
)
779 ForceCompositionEnd();
781 bool hasTxnMgr
, hasTransaction
= false;
782 CanUndo(&hasTxnMgr
, &hasTransaction
);
783 NS_ENSURE_TRUE(hasTransaction
, NS_OK
);
785 nsAutoRules
beginRulesSniffing(this, EditAction::undo
, nsIEditor::eNone
);
791 for (uint32_t i
= 0; i
< aCount
; ++i
) {
792 nsresult rv
= mTxnMgr
->UndoTransaction();
793 NS_ENSURE_SUCCESS(rv
, rv
);
795 DoAfterUndoTransaction();
802 NS_IMETHODIMP
nsEditor::CanUndo(bool *aIsEnabled
, bool *aCanUndo
)
804 NS_ENSURE_TRUE(aIsEnabled
&& aCanUndo
, NS_ERROR_NULL_POINTER
);
805 *aIsEnabled
= !!mTxnMgr
;
808 mTxnMgr
->GetNumberOfUndoItems(&numTxns
);
809 *aCanUndo
= !!numTxns
;
818 nsEditor::Redo(uint32_t aCount
)
820 bool hasTxnMgr
, hasTransaction
= false;
821 CanRedo(&hasTxnMgr
, &hasTransaction
);
822 NS_ENSURE_TRUE(hasTransaction
, NS_OK
);
824 nsAutoRules
beginRulesSniffing(this, EditAction::redo
, nsIEditor::eNone
);
830 for (uint32_t i
= 0; i
< aCount
; ++i
) {
831 nsresult rv
= mTxnMgr
->RedoTransaction();
832 NS_ENSURE_SUCCESS(rv
, rv
);
834 DoAfterRedoTransaction();
841 NS_IMETHODIMP
nsEditor::CanRedo(bool *aIsEnabled
, bool *aCanRedo
)
843 NS_ENSURE_TRUE(aIsEnabled
&& aCanRedo
, NS_ERROR_NULL_POINTER
);
845 *aIsEnabled
= !!mTxnMgr
;
848 mTxnMgr
->GetNumberOfRedoItems(&numTxns
);
849 *aCanRedo
= !!numTxns
;
858 nsEditor::BeginTransaction()
860 BeginUpdateViewBatch();
863 mTxnMgr
->BeginBatch(nullptr);
870 nsEditor::EndTransaction()
873 mTxnMgr
->EndBatch(false);
876 EndUpdateViewBatch();
882 // These two routines are similar to the above, but do not use
883 // the transaction managers batching feature. Instead we use
884 // a placeholder transaction to wrap up any further transaction
885 // while the batch is open. The advantage of this is that
886 // placeholder transactions can later merge, if needed. Merging
887 // is unavailable between transaction manager batches.
890 nsEditor::BeginPlaceHolderTransaction(nsIAtom
*aName
)
892 NS_PRECONDITION(mPlaceHolderBatch
>= 0, "negative placeholder batch count!");
893 if (!mPlaceHolderBatch
)
895 NotifyEditorObservers(eNotifyEditorObserversOfBefore
);
896 // time to turn on the batch
897 BeginUpdateViewBatch();
898 mPlaceHolderTxn
= nullptr;
899 mPlaceHolderName
= aName
;
900 nsRefPtr
<Selection
> selection
= GetSelection();
902 mSelState
= new nsSelectionState();
903 mSelState
->SaveSelection(selection
);
912 nsEditor::EndPlaceHolderTransaction()
914 NS_PRECONDITION(mPlaceHolderBatch
> 0, "zero or negative placeholder batch count when ending batch!");
915 if (mPlaceHolderBatch
== 1)
917 nsCOMPtr
<nsISelection
>selection
;
918 GetSelection(getter_AddRefs(selection
));
920 nsCOMPtr
<nsISelectionPrivate
>selPrivate(do_QueryInterface(selection
));
922 // By making the assumption that no reflow happens during the calls
923 // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to
924 // allow the selection to cache a frame offset which is used by the
925 // caret drawing code. We only enable this cache here; at other times,
926 // we have no way to know whether reflow invalidates it
927 // See bugs 35296 and 199412.
929 selPrivate
->SetCanCacheFrameOffset(true);
933 // Hide the caret here to avoid hiding it twice, once in EndUpdateViewBatch
934 // and once in ScrollSelectionIntoView.
935 nsRefPtr
<nsCaret
> caret
;
936 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
939 caret
= presShell
->GetCaret();
941 // time to turn off the batch
942 EndUpdateViewBatch();
943 // make sure selection is in view
945 // After ScrollSelectionIntoView(), the pending notifications might be
946 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
947 ScrollSelectionIntoView(false);
950 // cached for frame offset are Not available now
952 selPrivate
->SetCanCacheFrameOffset(false);
957 // we saved the selection state, but never got to hand it to placeholder
958 // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
962 if (mPlaceHolderTxn
) // we might have never made a placeholder if no action took place
964 nsCOMPtr
<nsIAbsorbingTransaction
> plcTxn
= do_QueryReferent(mPlaceHolderTxn
);
967 plcTxn
->EndPlaceHolderBatch();
971 // in the future we will check to make sure undo is off here,
972 // since that is the only known case where the placeholdertxn would disappear on us.
973 // For now just removing the assert.
975 // notify editor observers of action but if composing, it's done by
976 // text event handler.
978 NotifyEditorObservers(eNotifyEditorObserversOfEnd
);
981 NotifyEditorObservers(eNotifyEditorObserversOfCancel
);
990 nsEditor::ShouldTxnSetSelection(bool *aResult
)
992 NS_ENSURE_TRUE(aResult
, NS_ERROR_NULL_POINTER
);
993 *aResult
= mShouldTxnSetSelection
;
998 nsEditor::SetShouldTxnSetSelection(bool aShould
)
1000 mShouldTxnSetSelection
= aShould
;
1005 nsEditor::GetDocumentIsEmpty(bool *aDocumentIsEmpty
)
1007 *aDocumentIsEmpty
= true;
1009 dom::Element
* root
= GetRoot();
1010 NS_ENSURE_TRUE(root
, NS_ERROR_NULL_POINTER
);
1012 *aDocumentIsEmpty
= !root
->HasChildren();
1017 // XXX: the rule system should tell us which node to select all on (ie, the root, or the body)
1018 NS_IMETHODIMP
nsEditor::SelectAll()
1020 if (!mDocWeak
) { return NS_ERROR_NOT_INITIALIZED
; }
1021 ForceCompositionEnd();
1023 nsCOMPtr
<nsISelectionController
> selCon
;
1024 GetSelectionController(getter_AddRefs(selCon
));
1025 NS_ENSURE_TRUE(selCon
, NS_ERROR_NOT_INITIALIZED
);
1026 nsCOMPtr
<nsISelection
> selection
;
1027 nsresult result
= selCon
->GetSelection(nsISelectionController::SELECTION_NORMAL
, getter_AddRefs(selection
));
1028 if (NS_SUCCEEDED(result
) && selection
)
1030 result
= SelectEntireDocument(selection
);
1035 NS_IMETHODIMP
nsEditor::BeginningOfDocument()
1037 if (!mDocWeak
) { return NS_ERROR_NOT_INITIALIZED
; }
1039 // get the selection
1040 nsCOMPtr
<nsISelection
> selection
;
1041 nsresult result
= GetSelection(getter_AddRefs(selection
));
1042 NS_ENSURE_SUCCESS(result
, result
);
1043 NS_ENSURE_TRUE(selection
, NS_ERROR_NOT_INITIALIZED
);
1045 // get the root element
1046 dom::Element
* rootElement
= GetRoot();
1047 NS_ENSURE_TRUE(rootElement
, NS_ERROR_NULL_POINTER
);
1049 // find first editable thingy
1050 nsCOMPtr
<nsINode
> firstNode
= GetFirstEditableNode(rootElement
);
1052 // just the root node, set selection to inside the root
1053 return selection
->CollapseNative(rootElement
, 0);
1056 if (firstNode
->NodeType() == nsIDOMNode::TEXT_NODE
) {
1057 // If firstNode is text, set selection to beginning of the text node.
1058 return selection
->CollapseNative(firstNode
, 0);
1061 // Otherwise, it's a leaf node and we set the selection just in front of it.
1062 nsCOMPtr
<nsIContent
> parent
= firstNode
->GetParent();
1064 return NS_ERROR_NULL_POINTER
;
1067 int32_t offsetInParent
= parent
->IndexOf(firstNode
);
1068 return selection
->CollapseNative(parent
, offsetInParent
);
1072 nsEditor::EndOfDocument()
1074 NS_ENSURE_TRUE(mDocWeak
, NS_ERROR_NOT_INITIALIZED
);
1077 nsCOMPtr
<nsISelection
> selection
;
1078 nsresult rv
= GetSelection(getter_AddRefs(selection
));
1079 NS_ENSURE_SUCCESS(rv
, rv
);
1080 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
1082 // get the root element
1083 nsINode
* node
= GetRoot();
1084 NS_ENSURE_TRUE(node
, NS_ERROR_NULL_POINTER
);
1085 nsINode
* child
= node
->GetLastChild();
1087 while (child
&& IsContainer(child
->AsDOMNode())) {
1089 child
= node
->GetLastChild();
1092 uint32_t length
= node
->Length();
1093 return selection
->CollapseNative(node
, int32_t(length
));
1097 nsEditor::GetDocumentModified(bool *outDocModified
)
1099 NS_ENSURE_TRUE(outDocModified
, NS_ERROR_NULL_POINTER
);
1101 int32_t modCount
= 0;
1102 GetModificationCount(&modCount
);
1104 *outDocModified
= (modCount
!= 0);
1109 nsEditor::GetDocumentCharacterSet(nsACString
&characterSet
)
1111 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
1112 NS_ENSURE_TRUE(doc
, NS_ERROR_UNEXPECTED
);
1114 characterSet
= doc
->GetDocumentCharacterSet();
1119 nsEditor::SetDocumentCharacterSet(const nsACString
& characterSet
)
1121 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
1122 NS_ENSURE_TRUE(doc
, NS_ERROR_UNEXPECTED
);
1124 doc
->SetDocumentCharacterSet(characterSet
);
1131 return NS_ERROR_NOT_IMPLEMENTED
;
1135 nsEditor::CanCut(bool *aCanCut
)
1137 return NS_ERROR_NOT_IMPLEMENTED
;
1143 return NS_ERROR_NOT_IMPLEMENTED
;
1147 nsEditor::CanCopy(bool *aCanCut
)
1149 return NS_ERROR_NOT_IMPLEMENTED
;
1153 nsEditor::Paste(int32_t aSelectionType
)
1155 return NS_ERROR_NOT_IMPLEMENTED
;
1159 nsEditor::PasteTransferable(nsITransferable
*aTransferable
)
1161 return NS_ERROR_NOT_IMPLEMENTED
;
1165 nsEditor::CanPaste(int32_t aSelectionType
, bool *aCanPaste
)
1167 return NS_ERROR_NOT_IMPLEMENTED
;
1171 nsEditor::CanPasteTransferable(nsITransferable
*aTransferable
, bool *aCanPaste
)
1173 return NS_ERROR_NOT_IMPLEMENTED
;
1177 nsEditor::SetAttribute(nsIDOMElement
* aElement
, const nsAString
& aAttribute
,
1178 const nsAString
& aValue
)
1180 nsCOMPtr
<Element
> element
= do_QueryInterface(aElement
);
1181 NS_ENSURE_TRUE(element
, NS_ERROR_NULL_POINTER
);
1182 nsCOMPtr
<nsIAtom
> attribute
= do_GetAtom(aAttribute
);
1184 nsRefPtr
<ChangeAttributeTxn
> txn
=
1185 CreateTxnForSetAttribute(*element
, *attribute
, aValue
);
1186 return DoTransaction(txn
);
1190 nsEditor::GetAttributeValue(nsIDOMElement
*aElement
,
1191 const nsAString
& aAttribute
,
1192 nsAString
& aResultValue
,
1195 NS_ENSURE_TRUE(aResultIsSet
, NS_ERROR_NULL_POINTER
);
1196 *aResultIsSet
= false;
1201 nsresult rv
= aElement
->GetAttribute(aAttribute
, value
);
1202 NS_ENSURE_SUCCESS(rv
, rv
);
1203 if (!DOMStringIsNull(value
)) {
1204 *aResultIsSet
= true;
1205 aResultValue
= value
;
1211 nsEditor::RemoveAttribute(nsIDOMElement
* aElement
, const nsAString
& aAttribute
)
1213 nsCOMPtr
<Element
> element
= do_QueryInterface(aElement
);
1214 NS_ENSURE_TRUE(element
, NS_ERROR_NULL_POINTER
);
1215 nsCOMPtr
<nsIAtom
> attribute
= do_GetAtom(aAttribute
);
1217 nsRefPtr
<ChangeAttributeTxn
> txn
=
1218 CreateTxnForRemoveAttribute(*element
, *attribute
);
1219 return DoTransaction(txn
);
1224 nsEditor::OutputsMozDirty()
1226 // Return true for Composer (!eEditorAllowInteraction) or mail
1227 // (eEditorMailMask), but false for webpages.
1228 return !(mFlags
& nsIPlaintextEditor::eEditorAllowInteraction
) ||
1229 (mFlags
& nsIPlaintextEditor::eEditorMailMask
);
1234 nsEditor::MarkNodeDirty(nsIDOMNode
* aNode
)
1236 // Mark the node dirty, but not for webpages (bug 599983)
1237 if (!OutputsMozDirty()) {
1240 nsCOMPtr
<dom::Element
> element
= do_QueryInterface(aNode
);
1242 element
->SetAttr(kNameSpaceID_None
, nsGkAtoms::mozdirty
,
1243 EmptyString(), false);
1248 NS_IMETHODIMP
nsEditor::GetInlineSpellChecker(bool autoCreate
,
1249 nsIInlineSpellChecker
** aInlineSpellChecker
)
1251 NS_ENSURE_ARG_POINTER(aInlineSpellChecker
);
1253 if (mDidPreDestroy
) {
1254 // Don't allow people to get or create the spell checker once the editor
1256 *aInlineSpellChecker
= nullptr;
1257 return autoCreate
? NS_ERROR_NOT_AVAILABLE
: NS_OK
;
1260 // We don't want to show the spell checking UI if there are no spell check dictionaries available.
1261 bool canSpell
= mozInlineSpellChecker::CanEnableInlineSpellChecking();
1263 *aInlineSpellChecker
= nullptr;
1264 return NS_ERROR_FAILURE
;
1268 if (!mInlineSpellChecker
&& autoCreate
) {
1269 mInlineSpellChecker
= do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID
, &rv
);
1270 NS_ENSURE_SUCCESS(rv
, rv
);
1273 if (mInlineSpellChecker
) {
1274 rv
= mInlineSpellChecker
->Init(this);
1276 mInlineSpellChecker
= nullptr;
1277 NS_ENSURE_SUCCESS(rv
, rv
);
1280 NS_IF_ADDREF(*aInlineSpellChecker
= mInlineSpellChecker
);
1285 NS_IMETHODIMP
nsEditor::Observe(nsISupports
* aSubj
, const char *aTopic
,
1286 const char16_t
*aData
)
1288 NS_ASSERTION(!strcmp(aTopic
,
1289 SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION
),
1290 "Unexpected observer topic");
1292 // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes
1293 SyncRealTimeSpell();
1295 // When nsIEditorSpellCheck::GetCurrentDictionary changes
1296 if (mInlineSpellChecker
) {
1297 // if the current dictionary is no longer available, find another one
1298 nsCOMPtr
<nsIEditorSpellCheck
> editorSpellCheck
;
1299 mInlineSpellChecker
->GetSpellChecker(getter_AddRefs(editorSpellCheck
));
1300 if (editorSpellCheck
) {
1301 // Note: This might change the current dictionary, which may call
1302 // this observer recursively.
1303 editorSpellCheck
->CheckCurrentDictionary();
1306 // update the inline spell checker to reflect the new current dictionary
1307 mInlineSpellChecker
->SpellCheckRange(nullptr); // causes recheck
1313 NS_IMETHODIMP
nsEditor::SyncRealTimeSpell()
1315 bool enable
= GetDesiredSpellCheckState();
1317 // Initializes mInlineSpellChecker
1318 nsCOMPtr
<nsIInlineSpellChecker
> spellChecker
;
1319 GetInlineSpellChecker(enable
, getter_AddRefs(spellChecker
));
1321 if (mInlineSpellChecker
) {
1322 // We might have a mInlineSpellChecker even if there are no dictionaries
1323 // available since we don't destroy the mInlineSpellChecker when the last
1324 // dictionariy is removed, but in that case spellChecker is null
1325 mInlineSpellChecker
->SetEnableRealTimeSpell(enable
&& spellChecker
);
1331 NS_IMETHODIMP
nsEditor::SetSpellcheckUserOverride(bool enable
)
1333 mSpellcheckCheckboxState
= enable
? eTriTrue
: eTriFalse
;
1335 return SyncRealTimeSpell();
1339 nsEditor::CreateNode(const nsAString
& aTag
,
1340 nsIDOMNode
* aParent
,
1342 nsIDOMNode
** aNewNode
)
1344 nsCOMPtr
<nsIAtom
> tag
= do_GetAtom(aTag
);
1345 nsCOMPtr
<nsINode
> parent
= do_QueryInterface(aParent
);
1346 NS_ENSURE_STATE(parent
);
1347 *aNewNode
= GetAsDOMNode(CreateNode(tag
, parent
, aPosition
).take());
1348 NS_ENSURE_STATE(*aNewNode
);
1352 already_AddRefed
<Element
>
1353 nsEditor::CreateNode(nsIAtom
* aTag
,
1357 MOZ_ASSERT(aTag
&& aParent
);
1359 nsAutoRules
beginRulesSniffing(this, EditAction::createNode
, nsIEditor::eNext
);
1361 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
1362 mActionListeners
[i
]->WillCreateNode(nsDependentAtomString(aTag
),
1363 GetAsDOMNode(aParent
), aPosition
);
1366 nsCOMPtr
<Element
> ret
;
1368 nsRefPtr
<CreateElementTxn
> txn
=
1369 CreateTxnForCreateElement(*aTag
, *aParent
, aPosition
);
1370 nsresult res
= DoTransaction(txn
);
1371 if (NS_SUCCEEDED(res
)) {
1372 ret
= txn
->GetNewNode();
1376 mRangeUpdater
.SelAdjCreateNode(aParent
, aPosition
);
1378 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
1379 mActionListeners
[i
]->DidCreateNode(nsDependentAtomString(aTag
),
1381 GetAsDOMNode(aParent
), aPosition
,
1385 return ret
.forget();
1390 nsEditor::InsertNode(nsIDOMNode
* aNode
, nsIDOMNode
* aParent
, int32_t aPosition
)
1392 nsCOMPtr
<nsIContent
> node
= do_QueryInterface(aNode
);
1393 nsCOMPtr
<nsINode
> parent
= do_QueryInterface(aParent
);
1394 NS_ENSURE_TRUE(node
&& parent
, NS_ERROR_NULL_POINTER
);
1396 return InsertNode(*node
, *parent
, aPosition
);
1400 nsEditor::InsertNode(nsIContent
& aNode
, nsINode
& aParent
, int32_t aPosition
)
1402 nsAutoRules
beginRulesSniffing(this, EditAction::insertNode
, nsIEditor::eNext
);
1404 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
1405 mActionListeners
[i
]->WillInsertNode(aNode
.AsDOMNode(), aParent
.AsDOMNode(),
1409 nsRefPtr
<InsertNodeTxn
> txn
= CreateTxnForInsertNode(aNode
, aParent
,
1411 nsresult res
= DoTransaction(txn
);
1413 mRangeUpdater
.SelAdjInsertNode(aParent
.AsDOMNode(), aPosition
);
1415 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
1416 mActionListeners
[i
]->DidInsertNode(aNode
.AsDOMNode(), aParent
.AsDOMNode(),
1425 nsEditor::SplitNode(nsIDOMNode
* aNode
,
1427 nsIDOMNode
**aNewLeftNode
)
1430 nsAutoRules
beginRulesSniffing(this, EditAction::splitNode
, nsIEditor::eNext
);
1432 for (i
= 0; i
< mActionListeners
.Count(); i
++)
1433 mActionListeners
[i
]->WillSplitNode(aNode
, aOffset
);
1435 nsRefPtr
<SplitElementTxn
> txn
;
1436 nsresult result
= CreateTxnForSplitNode(aNode
, aOffset
, getter_AddRefs(txn
));
1437 if (NS_SUCCEEDED(result
))
1439 result
= DoTransaction(txn
);
1440 if (NS_SUCCEEDED(result
))
1442 result
= txn
->GetNewNode(aNewLeftNode
);
1443 NS_ASSERTION((NS_SUCCEEDED(result
)), "result must succeeded for GetNewNode");
1447 mRangeUpdater
.SelAdjSplitNode(aNode
, aOffset
, *aNewLeftNode
);
1449 for (i
= 0; i
< mActionListeners
.Count(); i
++)
1451 nsIDOMNode
*ptr
= *aNewLeftNode
;
1452 mActionListeners
[i
]->DidSplitNode(aNode
, aOffset
, ptr
, result
);
1460 nsEditor::JoinNodes(nsINode
* aNodeToKeep
, nsIContent
* aNodeToMove
)
1462 // We don't really need aNodeToMove's parent to be non-null -- we could just
1463 // skip adjusting any ranges in aNodeToMove's parent if there is none. But
1464 // the current implementation requires it.
1465 MOZ_ASSERT(aNodeToKeep
&& aNodeToMove
&& aNodeToMove
->GetParentNode());
1466 nsresult res
= JoinNodes(aNodeToKeep
->AsDOMNode(), aNodeToMove
->AsDOMNode(),
1467 aNodeToMove
->GetParentNode()->AsDOMNode());
1468 NS_ASSERTION(NS_SUCCEEDED(res
), "JoinNodes failed");
1469 NS_ENSURE_SUCCESS(res
, res
);
1474 nsEditor::JoinNodes(nsIDOMNode
* aLeftNode
,
1475 nsIDOMNode
* aRightNode
,
1476 nsIDOMNode
* aParent
)
1479 nsAutoRules
beginRulesSniffing(this, EditAction::joinNode
, nsIEditor::ePrevious
);
1481 // remember some values; later used for saved selection updating.
1482 // find the offset between the nodes to be joined.
1483 int32_t offset
= GetChildOffset(aRightNode
, aParent
);
1484 // find the number of children of the lefthand node
1485 uint32_t oldLeftNodeLen
;
1486 nsresult result
= GetLengthOfDOMNode(aLeftNode
, oldLeftNodeLen
);
1487 NS_ENSURE_SUCCESS(result
, result
);
1489 for (i
= 0; i
< mActionListeners
.Count(); i
++)
1490 mActionListeners
[i
]->WillJoinNodes(aLeftNode
, aRightNode
, aParent
);
1492 nsRefPtr
<JoinElementTxn
> txn
;
1493 result
= CreateTxnForJoinNode(aLeftNode
, aRightNode
, getter_AddRefs(txn
));
1494 if (NS_SUCCEEDED(result
)) {
1495 result
= DoTransaction(txn
);
1498 mRangeUpdater
.SelAdjJoinNodes(aLeftNode
, aRightNode
, aParent
, offset
, (int32_t)oldLeftNodeLen
);
1500 for (i
= 0; i
< mActionListeners
.Count(); i
++)
1501 mActionListeners
[i
]->DidJoinNodes(aLeftNode
, aRightNode
, aParent
, result
);
1508 nsEditor::DeleteNode(nsIDOMNode
* aNode
)
1510 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
1511 NS_ENSURE_STATE(node
);
1512 return DeleteNode(node
);
1516 nsEditor::DeleteNode(nsINode
* aNode
)
1518 nsAutoRules
beginRulesSniffing(this, EditAction::createNode
, nsIEditor::ePrevious
);
1520 // save node location for selection updating code.
1521 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
1522 mActionListeners
[i
]->WillDeleteNode(aNode
->AsDOMNode());
1525 nsRefPtr
<DeleteNodeTxn
> txn
;
1526 nsresult res
= CreateTxnForDeleteNode(aNode
, getter_AddRefs(txn
));
1527 if (NS_SUCCEEDED(res
)) {
1528 res
= DoTransaction(txn
);
1531 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
1532 mActionListeners
[i
]->DidDeleteNode(aNode
->AsDOMNode(), res
);
1535 NS_ENSURE_SUCCESS(res
, res
);
1539 ///////////////////////////////////////////////////////////////////////////
1540 // ReplaceContainer: replace inNode with a new node (outNode) which is contructed
1541 // to be of type aNodeType. Put inNodes children into outNode.
1542 // Callers responsibility to make sure inNode's children can
1544 already_AddRefed
<Element
>
1545 nsEditor::ReplaceContainer(Element
* aOldContainer
,
1547 nsIAtom
* aAttribute
,
1548 const nsAString
* aValue
,
1549 ECloneAttributes aCloneAttributes
)
1551 MOZ_ASSERT(aOldContainer
&& aNodeType
);
1553 nsCOMPtr
<nsIContent
> parent
= aOldContainer
->GetParent();
1554 NS_ENSURE_TRUE(parent
, nullptr);
1556 int32_t offset
= parent
->IndexOf(aOldContainer
);
1558 // create new container
1559 nsCOMPtr
<Element
> ret
= CreateHTMLContent(aNodeType
);
1560 NS_ENSURE_TRUE(ret
, nullptr);
1562 // set attribute if needed
1564 if (aAttribute
&& aValue
&& aAttribute
!= nsGkAtoms::_empty
) {
1565 res
= ret
->SetAttr(kNameSpaceID_None
, aAttribute
, *aValue
, true);
1566 NS_ENSURE_SUCCESS(res
, nullptr);
1568 if (aCloneAttributes
== eCloneAttributes
) {
1569 CloneAttributes(ret
, aOldContainer
);
1572 // notify our internal selection state listener
1573 // (Note: A nsAutoSelectionReset object must be created
1574 // before calling this to initialize mRangeUpdater)
1575 AutoReplaceContainerSelNotify
selStateNotify(mRangeUpdater
, aOldContainer
,
1578 nsAutoTxnsConserveSelection
conserveSelection(this);
1579 while (aOldContainer
->HasChildren()) {
1580 nsCOMPtr
<nsIContent
> child
= aOldContainer
->GetFirstChild();
1582 res
= DeleteNode(child
);
1583 NS_ENSURE_SUCCESS(res
, nullptr);
1585 res
= InsertNode(*child
, *ret
, -1);
1586 NS_ENSURE_SUCCESS(res
, nullptr);
1590 // insert new container into tree
1591 res
= InsertNode(*ret
, *parent
, offset
);
1592 NS_ENSURE_SUCCESS(res
, nullptr);
1594 // delete old container
1595 res
= DeleteNode(aOldContainer
);
1596 NS_ENSURE_SUCCESS(res
, nullptr);
1598 return ret
.forget();
1601 ///////////////////////////////////////////////////////////////////////////////
1602 // RemoveContainer: remove inNode, reparenting its children (if any) into the
1606 nsEditor::RemoveContainer(nsIContent
* aNode
)
1610 nsCOMPtr
<nsINode
> parent
= aNode
->GetParentNode();
1611 NS_ENSURE_STATE(parent
);
1613 int32_t offset
= parent
->IndexOf(aNode
);
1615 // Loop through the children of inNode and promote them into inNode's parent
1616 uint32_t nodeOrigLen
= aNode
->GetChildCount();
1618 // notify our internal selection state listener
1619 nsAutoRemoveContainerSelNotify
selNotify(mRangeUpdater
, aNode
, parent
,
1620 offset
, nodeOrigLen
);
1622 while (aNode
->HasChildren()) {
1623 nsCOMPtr
<nsIContent
> child
= aNode
->GetLastChild();
1624 nsresult rv
= DeleteNode(child
);
1625 NS_ENSURE_SUCCESS(rv
, rv
);
1627 rv
= InsertNode(*child
, *parent
, offset
);
1628 NS_ENSURE_SUCCESS(rv
, rv
);
1631 return DeleteNode(aNode
);
1635 ///////////////////////////////////////////////////////////////////////////////
1636 // InsertContainerAbove: Insert a new parent for inNode, which is contructed to
1637 // be of type aNodeType. outNode becomes a child of
1638 // inNode's earlier parent. Caller's responsibility to
1639 // make sure inNode's can be child of outNode, and
1640 // outNode can be child of old parent.
1641 already_AddRefed
<Element
>
1642 nsEditor::InsertContainerAbove(nsIContent
* aNode
,
1644 nsIAtom
* aAttribute
,
1645 const nsAString
* aValue
)
1647 MOZ_ASSERT(aNode
&& aNodeType
);
1649 nsCOMPtr
<nsIContent
> parent
= aNode
->GetParent();
1650 NS_ENSURE_TRUE(parent
, nullptr);
1651 int32_t offset
= parent
->IndexOf(aNode
);
1653 // Create new container
1654 nsCOMPtr
<Element
> newContent
= CreateHTMLContent(aNodeType
);
1655 NS_ENSURE_TRUE(newContent
, nullptr);
1657 // Set attribute if needed
1659 if (aAttribute
&& aValue
&& aAttribute
!= nsGkAtoms::_empty
) {
1660 res
= newContent
->SetAttr(kNameSpaceID_None
, aAttribute
, *aValue
, true);
1661 NS_ENSURE_SUCCESS(res
, nullptr);
1664 // Notify our internal selection state listener
1665 nsAutoInsertContainerSelNotify
selNotify(mRangeUpdater
);
1667 // Put inNode in new parent, outNode
1668 res
= DeleteNode(aNode
);
1669 NS_ENSURE_SUCCESS(res
, nullptr);
1672 nsAutoTxnsConserveSelection
conserveSelection(this);
1673 res
= InsertNode(*aNode
, *newContent
, 0);
1674 NS_ENSURE_SUCCESS(res
, nullptr);
1677 // Put new parent in doc
1678 res
= InsertNode(*newContent
, *parent
, offset
);
1679 NS_ENSURE_SUCCESS(res
, nullptr);
1681 return newContent
.forget();
1684 ///////////////////////////////////////////////////////////////////////////
1685 // MoveNode: move aNode to {aParent,aOffset}
1687 nsEditor::MoveNode(nsIContent
* aNode
, nsINode
* aParent
, int32_t aOffset
)
1690 MOZ_ASSERT(aParent
);
1691 MOZ_ASSERT(aOffset
== -1 ||
1693 AssertedCast
<uint32_t>(aOffset
) <= aParent
->Length()));
1695 nsCOMPtr
<nsINode
> oldParent
= aNode
->GetParentNode();
1696 int32_t oldOffset
= oldParent
? oldParent
->IndexOf(aNode
) : -1;
1698 if (aOffset
== -1) {
1699 // Magic value meaning "move to end of aParent"
1700 aOffset
= AssertedCast
<int32_t>(aParent
->Length());
1703 // Don't do anything if it's already in right place
1704 if (aParent
== oldParent
&& aOffset
== oldOffset
) {
1708 // Notify our internal selection state listener
1709 nsAutoMoveNodeSelNotify
selNotify(mRangeUpdater
, oldParent
, oldOffset
,
1712 // Need to adjust aOffset if we're moving aNode later in its current parent
1713 if (aParent
== oldParent
&& oldOffset
< aOffset
) {
1714 // When we delete aNode, it will make the offsets after it off by one
1718 // Hold a reference so aNode doesn't go away when we remove it (bug 772282)
1719 nsCOMPtr
<nsINode
> kungFuDeathGrip
= aNode
;
1721 nsresult rv
= DeleteNode(aNode
);
1722 NS_ENSURE_SUCCESS(rv
, rv
);
1724 return InsertNode(*aNode
, *aParent
, aOffset
);
1729 nsEditor::AddEditorObserver(nsIEditorObserver
*aObserver
)
1731 // we don't keep ownership of the observers. They must
1732 // remove themselves as observers before they are destroyed.
1734 NS_ENSURE_TRUE(aObserver
, NS_ERROR_NULL_POINTER
);
1736 // Make sure the listener isn't already on the list
1737 if (mEditorObservers
.IndexOf(aObserver
) == -1)
1739 if (!mEditorObservers
.AppendObject(aObserver
))
1740 return NS_ERROR_FAILURE
;
1748 nsEditor::RemoveEditorObserver(nsIEditorObserver
*aObserver
)
1750 NS_ENSURE_TRUE(aObserver
, NS_ERROR_FAILURE
);
1752 if (!mEditorObservers
.RemoveObject(aObserver
))
1753 return NS_ERROR_FAILURE
;
1758 class EditorInputEventDispatcher
: public nsRunnable
1761 EditorInputEventDispatcher(nsEditor
* aEditor
,
1762 nsIContent
* aTarget
,
1766 , mIsComposing(aIsComposing
)
1772 // Note that we don't need to check mDispatchInputEvent here. We need
1773 // to check it only when the editor requests to dispatch the input event.
1775 if (!mTarget
->IsInDoc()) {
1779 nsCOMPtr
<nsIPresShell
> ps
= mEditor
->GetPresShell();
1784 nsCOMPtr
<nsIWidget
> widget
= mEditor
->GetWidget();
1789 // Even if the change is caused by untrusted event, we need to dispatch
1790 // trusted input event since it's a fact.
1791 InternalEditorInputEvent
inputEvent(true, NS_EDITOR_INPUT
, widget
);
1792 inputEvent
.time
= static_cast<uint64_t>(PR_Now() / 1000);
1793 inputEvent
.mIsComposing
= mIsComposing
;
1794 nsEventStatus status
= nsEventStatus_eIgnore
;
1796 ps
->HandleEventWithTarget(&inputEvent
, nullptr, mTarget
, &status
);
1797 NS_ENSURE_SUCCESS(rv
, NS_OK
); // print the warning if error
1802 nsRefPtr
<nsEditor
> mEditor
;
1803 nsCOMPtr
<nsIContent
> mTarget
;
1808 nsEditor::NotifyEditorObservers(NotificationForEditorObservers aNotification
)
1810 switch (aNotification
) {
1811 case eNotifyEditorObserversOfEnd
:
1812 mIsInEditAction
= false;
1813 for (int32_t i
= 0; i
< mEditorObservers
.Count(); i
++) {
1814 mEditorObservers
[i
]->EditAction();
1817 if (!mDispatchInputEvent
) {
1823 case eNotifyEditorObserversOfBefore
:
1824 mIsInEditAction
= true;
1825 for (int32_t i
= 0; i
< mEditorObservers
.Count(); i
++) {
1826 mEditorObservers
[i
]->BeforeEditAction();
1829 case eNotifyEditorObserversOfCancel
:
1830 mIsInEditAction
= false;
1831 for (int32_t i
= 0; i
< mEditorObservers
.Count(); i
++) {
1832 mEditorObservers
[i
]->CancelEditAction();
1836 MOZ_CRASH("Handle all notifications here");
1842 nsEditor::FireInputEvent()
1844 // We don't need to dispatch multiple input events if there is a pending
1845 // input event. However, it may have different event target. If we resolved
1846 // this issue, we need to manage the pending events in an array. But it's
1847 // overwork. We don't need to do it for the very rare case.
1849 nsCOMPtr
<nsIContent
> target
= GetInputEventTargetContent();
1850 NS_ENSURE_TRUE_VOID(target
);
1852 // NOTE: Don't refer IsIMEComposing() because it returns false even before
1853 // compositionend. However, DOM Level 3 Events defines it should be
1854 // true after compositionstart and before compositionend.
1855 nsContentUtils::AddScriptRunner(
1856 new EditorInputEventDispatcher(this, target
, !!GetComposition()));
1860 nsEditor::AddEditActionListener(nsIEditActionListener
*aListener
)
1862 NS_ENSURE_TRUE(aListener
, NS_ERROR_NULL_POINTER
);
1864 // Make sure the listener isn't already on the list
1865 if (mActionListeners
.IndexOf(aListener
) == -1)
1867 if (!mActionListeners
.AppendObject(aListener
))
1868 return NS_ERROR_FAILURE
;
1876 nsEditor::RemoveEditActionListener(nsIEditActionListener
*aListener
)
1878 NS_ENSURE_TRUE(aListener
, NS_ERROR_FAILURE
);
1880 if (!mActionListeners
.RemoveObject(aListener
))
1881 return NS_ERROR_FAILURE
;
1888 nsEditor::AddDocumentStateListener(nsIDocumentStateListener
*aListener
)
1890 NS_ENSURE_TRUE(aListener
, NS_ERROR_NULL_POINTER
);
1892 if (mDocStateListeners
.IndexOf(aListener
) == -1)
1894 if (!mDocStateListeners
.AppendObject(aListener
))
1895 return NS_ERROR_FAILURE
;
1903 nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener
*aListener
)
1905 NS_ENSURE_TRUE(aListener
, NS_ERROR_NULL_POINTER
);
1907 if (!mDocStateListeners
.RemoveObject(aListener
))
1908 return NS_ERROR_FAILURE
;
1914 NS_IMETHODIMP
nsEditor::OutputToString(const nsAString
& aFormatType
,
1916 nsAString
& aOutputString
)
1918 // these should be implemented by derived classes.
1919 return NS_ERROR_NOT_IMPLEMENTED
;
1923 nsEditor::OutputToStream(nsIOutputStream
* aOutputStream
,
1924 const nsAString
& aFormatType
,
1925 const nsACString
& aCharsetOverride
,
1928 // these should be implemented by derived classes.
1929 return NS_ERROR_NOT_IMPLEMENTED
;
1933 nsEditor::DumpContentTree()
1937 mRootElement
->List(stdout
);
1945 nsEditor::DebugDumpContent()
1948 nsCOMPtr
<nsIDOMHTMLDocument
> doc
= do_QueryReferent(mDocWeak
);
1949 NS_ENSURE_TRUE(doc
, NS_ERROR_NOT_INITIALIZED
);
1951 nsCOMPtr
<nsIDOMHTMLElement
>bodyElem
;
1952 doc
->GetBody(getter_AddRefs(bodyElem
));
1953 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(bodyElem
);
1962 nsEditor::DebugUnitTests(int32_t *outNumTests
, int32_t *outNumTestsFailed
)
1965 NS_NOTREACHED("This should never get called. Overridden by subclasses");
1972 nsEditor::ArePreservingSelection()
1974 return !(mSavedSel
.IsEmpty());
1978 nsEditor::PreserveSelectionAcrossActions(Selection
* aSel
)
1980 mSavedSel
.SaveSelection(aSel
);
1981 mRangeUpdater
.RegisterSelectionState(mSavedSel
);
1985 nsEditor::RestorePreservedSelection(nsISelection
*aSel
)
1987 if (mSavedSel
.IsEmpty()) return NS_ERROR_FAILURE
;
1988 mSavedSel
.RestoreSelection(aSel
);
1989 StopPreservingSelection();
1994 nsEditor::StopPreservingSelection()
1996 mRangeUpdater
.DropSelectionState(mSavedSel
);
1997 mSavedSel
.MakeEmpty();
2001 nsEditor::EnsureComposition(mozilla::WidgetGUIEvent
* aEvent
)
2006 // The compositionstart event must cause creating new TextComposition
2007 // instance at being dispatched by IMEStateManager.
2008 mComposition
= IMEStateManager::GetTextCompositionFor(aEvent
);
2009 if (!mComposition
) {
2010 MOZ_CRASH("IMEStateManager doesn't return proper composition");
2012 mComposition
->StartHandlingComposition(this);
2016 nsEditor::BeginIMEComposition(WidgetCompositionEvent
* aCompositionEvent
)
2018 MOZ_ASSERT(!mComposition
, "There is composition already");
2019 EnsureComposition(aCompositionEvent
);
2021 mPhonetic
->Truncate(0);
2027 nsEditor::EndIMEComposition()
2029 NS_ENSURE_TRUE_VOID(mComposition
); // nothing to do
2031 // commit the IME transaction..we can get at it via the transaction mgr.
2032 // Note that this means IME won't work without an undo stack!
2034 nsCOMPtr
<nsITransaction
> txn
= mTxnMgr
->PeekUndoStack();
2035 nsCOMPtr
<nsIAbsorbingTransaction
> plcTxn
= do_QueryInterface(txn
);
2037 DebugOnly
<nsresult
> rv
= plcTxn
->Commit();
2038 NS_ASSERTION(NS_SUCCEEDED(rv
),
2039 "nsIAbsorbingTransaction::Commit() failed");
2043 /* reset the data we need to construct a transaction */
2044 mIMETextNode
= nullptr;
2046 mComposition
->EndHandlingComposition(this);
2047 mComposition
= nullptr;
2049 // notify editor observers of action
2050 NotifyEditorObservers(eNotifyEditorObserversOfEnd
);
2055 nsEditor::GetPhonetic(nsAString
& aPhonetic
)
2058 aPhonetic
= *mPhonetic
;
2060 aPhonetic
.Truncate(0);
2066 nsEditor::ForceCompositionEnd()
2068 nsCOMPtr
<nsIPresShell
> ps
= GetPresShell();
2070 return NS_ERROR_NOT_AVAILABLE
;
2072 nsPresContext
* pc
= ps
->GetPresContext();
2074 return NS_ERROR_NOT_AVAILABLE
;
2077 if (!mComposition
) {
2078 // XXXmnakano see bug 558976, ResetInputState() has two meaning which are
2079 // "commit the composition" and "cursor is moved". This method name is
2080 // "ForceCompositionEnd", so, ResetInputState() should be used only for the
2081 // former here. However, ResetInputState() is also used for the latter here
2082 // because even if we don't have composition, we call ResetInputState() on
2083 // Linux. Currently, nsGtkIMModule can know the timing of the cursor move,
2084 // so, the latter meaning should be gone.
2085 // XXX This may commit a composition in another editor.
2086 return IMEStateManager::NotifyIME(NOTIFY_IME_OF_CURSOR_POS_CHANGED
, pc
);
2089 return IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION
, pc
);
2093 nsEditor::GetPreferredIMEState(IMEState
*aState
)
2095 NS_ENSURE_ARG_POINTER(aState
);
2096 aState
->mEnabled
= IMEState::ENABLED
;
2097 aState
->mOpen
= IMEState::DONT_CHANGE_OPEN_STATE
;
2099 if (IsReadonly() || IsDisabled()) {
2100 aState
->mEnabled
= IMEState::DISABLED
;
2104 nsCOMPtr
<nsIContent
> content
= GetRoot();
2105 NS_ENSURE_TRUE(content
, NS_ERROR_FAILURE
);
2107 nsIFrame
* frame
= content
->GetPrimaryFrame();
2108 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
2110 switch (frame
->StyleUIReset()->mIMEMode
) {
2111 case NS_STYLE_IME_MODE_AUTO
:
2112 if (IsPasswordEditor())
2113 aState
->mEnabled
= IMEState::PASSWORD
;
2115 case NS_STYLE_IME_MODE_DISABLED
:
2116 // we should use password state for |ime-mode: disabled;|.
2117 aState
->mEnabled
= IMEState::PASSWORD
;
2119 case NS_STYLE_IME_MODE_ACTIVE
:
2120 aState
->mOpen
= IMEState::OPEN
;
2122 case NS_STYLE_IME_MODE_INACTIVE
:
2123 aState
->mOpen
= IMEState::CLOSED
;
2131 nsEditor::GetComposing(bool* aResult
)
2133 NS_ENSURE_ARG_POINTER(aResult
);
2134 *aResult
= IsIMEComposing();
2139 /* Non-interface, public methods */
2142 nsEditor::GetRootElement(nsIDOMElement
**aRootElement
)
2144 NS_ENSURE_ARG_POINTER(aRootElement
);
2145 NS_ENSURE_TRUE(mRootElement
, NS_ERROR_NOT_AVAILABLE
);
2146 nsCOMPtr
<nsIDOMElement
> rootElement
= do_QueryInterface(mRootElement
);
2147 rootElement
.forget(aRootElement
);
2152 /** All editor operations which alter the doc should be prefaced
2153 * with a call to StartOperation, naming the action and direction */
2155 nsEditor::StartOperation(EditAction opID
, nsIEditor::EDirection aDirection
)
2158 mDirection
= aDirection
;
2163 /** All editor operations which alter the doc should be followed
2164 * with a call to EndOperation */
2166 nsEditor::EndOperation()
2168 mAction
= EditAction::none
;
2174 nsEditor::CloneAttribute(const nsAString
& aAttribute
,
2175 nsIDOMNode
*aDestNode
, nsIDOMNode
*aSourceNode
)
2177 NS_ENSURE_TRUE(aDestNode
&& aSourceNode
, NS_ERROR_NULL_POINTER
);
2179 nsCOMPtr
<nsIDOMElement
> destElement
= do_QueryInterface(aDestNode
);
2180 nsCOMPtr
<nsIDOMElement
> sourceElement
= do_QueryInterface(aSourceNode
);
2181 NS_ENSURE_TRUE(destElement
&& sourceElement
, NS_ERROR_NO_INTERFACE
);
2183 nsAutoString attrValue
;
2185 nsresult rv
= GetAttributeValue(sourceElement
,
2189 NS_ENSURE_SUCCESS(rv
, rv
);
2191 rv
= SetAttribute(destElement
, aAttribute
, attrValue
);
2193 rv
= RemoveAttribute(destElement
, aAttribute
);
2198 // Objects must be DOM elements
2200 nsEditor::CloneAttributes(nsIDOMNode
* aDest
, nsIDOMNode
* aSource
)
2202 NS_ENSURE_TRUE(aDest
&& aSource
, NS_ERROR_NULL_POINTER
);
2204 nsCOMPtr
<Element
> dest
= do_QueryInterface(aDest
);
2205 nsCOMPtr
<Element
> source
= do_QueryInterface(aSource
);
2206 NS_ENSURE_TRUE(dest
&& source
, NS_ERROR_NO_INTERFACE
);
2208 CloneAttributes(dest
, source
);
2214 nsEditor::CloneAttributes(Element
* aDest
, Element
* aSource
)
2216 MOZ_ASSERT(aDest
&& aSource
);
2218 nsAutoEditBatch
beginBatching(this);
2220 // Use transaction system for undo only if destination is already in the
2222 NS_ENSURE_TRUE(GetRoot(), );
2223 bool destInBody
= GetRoot()->Contains(aDest
);
2225 // Clear existing attributes
2226 nsRefPtr
<nsDOMAttributeMap
> destAttributes
= aDest
->Attributes();
2227 while (nsRefPtr
<Attr
> attr
= destAttributes
->Item(0)) {
2229 RemoveAttribute(static_cast<nsIDOMElement
*>(GetAsDOMNode(aDest
)),
2232 ErrorResult ignored
;
2233 aDest
->RemoveAttribute(attr
->NodeName(), ignored
);
2237 // Set just the attributes that the source element has
2238 nsRefPtr
<nsDOMAttributeMap
> sourceAttributes
= aSource
->Attributes();
2239 uint32_t sourceCount
= sourceAttributes
->Length();
2240 for (uint32_t i
= 0; i
< sourceCount
; i
++) {
2241 nsRefPtr
<Attr
> attr
= sourceAttributes
->Item(i
);
2243 attr
->GetValue(value
);
2245 SetAttributeOrEquivalent(static_cast<nsIDOMElement
*>(GetAsDOMNode(aDest
)),
2246 attr
->NodeName(), value
, false);
2248 // The element is not inserted in the document yet, we don't want to put
2249 // a transaction on the UndoStack
2250 SetAttributeOrEquivalent(static_cast<nsIDOMElement
*>(GetAsDOMNode(aDest
)),
2251 attr
->NodeName(), value
, true);
2257 NS_IMETHODIMP
nsEditor::ScrollSelectionIntoView(bool aScrollToAnchor
)
2259 nsCOMPtr
<nsISelectionController
> selCon
;
2260 if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon
))) && selCon
)
2262 int16_t region
= nsISelectionController::SELECTION_FOCUS_REGION
;
2264 if (aScrollToAnchor
)
2265 region
= nsISelectionController::SELECTION_ANCHOR_REGION
;
2267 selCon
->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL
,
2268 region
, nsISelectionController::SCROLL_OVERFLOW_HIDDEN
);
2275 nsEditor::InsertTextImpl(const nsAString
& aStringToInsert
,
2276 nsCOMPtr
<nsIDOMNode
>* aInOutNode
,
2277 int32_t* aInOutOffset
,
2278 nsIDOMDocument
* aDoc
)
2280 // NOTE: caller *must* have already used nsAutoTxnsConserveSelection
2281 // stack-based class to turn off txn selection updating. Caller also turned
2282 // on rules sniffing if desired.
2284 NS_ENSURE_TRUE(aInOutNode
&& *aInOutNode
&& aInOutOffset
&& aDoc
,
2285 NS_ERROR_NULL_POINTER
);
2286 if (!mComposition
&& aStringToInsert
.IsEmpty()) {
2290 nsCOMPtr
<nsINode
> node
= do_QueryInterface(*aInOutNode
);
2291 NS_ENSURE_STATE(node
);
2292 uint32_t offset
= static_cast<uint32_t>(*aInOutOffset
);
2294 if (!node
->IsNodeOfType(nsINode::eTEXT
) && IsPlaintextEditor()) {
2295 nsCOMPtr
<nsINode
> root
= GetRoot();
2296 // In some cases, node is the anonymous DIV, and offset is 0. To avoid
2297 // injecting unneeded text nodes, we first look to see if we have one
2298 // available. In that case, we'll just adjust node and offset accordingly.
2299 if (node
== root
&& offset
== 0 && node
->HasChildren() &&
2300 node
->GetFirstChild()->IsNodeOfType(nsINode::eTEXT
)) {
2301 node
= node
->GetFirstChild();
2303 // In some other cases, node is the anonymous DIV, and offset points to the
2304 // terminating mozBR. In that case, we'll adjust aInOutNode and
2305 // aInOutOffset to the preceding text node, if any.
2306 if (node
== root
&& offset
> 0 && node
->GetChildAt(offset
- 1) &&
2307 node
->GetChildAt(offset
- 1)->IsNodeOfType(nsINode::eTEXT
)) {
2308 node
= node
->GetChildAt(offset
- 1);
2309 offset
= node
->Length();
2311 // Sometimes, node is the mozBR element itself. In that case, we'll adjust
2312 // the insertion point to the previous text node, if one exists, or to the
2313 // parent anonymous DIV.
2314 if (nsTextEditUtils::IsMozBR(node
) && offset
== 0) {
2315 if (node
->GetPreviousSibling() &&
2316 node
->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT
)) {
2317 node
= node
->GetPreviousSibling();
2318 offset
= node
->Length();
2319 } else if (node
->GetParentNode() && node
->GetParentNode() == root
) {
2320 node
= node
->GetParentNode();
2327 if (!node
->IsNodeOfType(nsINode::eTEXT
)) {
2328 // create a text node
2329 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(aDoc
);
2330 NS_ENSURE_STATE(doc
);
2331 nsRefPtr
<nsTextNode
> newNode
= doc
->CreateTextNode(EmptyString());
2332 // then we insert it into the dom tree
2333 res
= InsertNode(newNode
->AsDOMNode(), node
->AsDOMNode(), offset
);
2334 NS_ENSURE_SUCCESS(res
, res
);
2338 res
= InsertTextIntoTextNodeImpl(aStringToInsert
, *node
->GetAsText(),
2340 NS_ENSURE_SUCCESS(res
, res
);
2341 offset
+= aStringToInsert
.Length();
2343 if (node
->IsNodeOfType(nsINode::eTEXT
)) {
2344 // we are inserting text into an existing text node.
2345 res
= InsertTextIntoTextNodeImpl(aStringToInsert
, *node
->GetAsText(),
2347 NS_ENSURE_SUCCESS(res
, res
);
2348 offset
+= aStringToInsert
.Length();
2350 // we are inserting text into a non-text node. first we have to create a
2351 // textnode (this also populates it with the text)
2352 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(aDoc
);
2353 NS_ENSURE_STATE(doc
);
2354 nsRefPtr
<nsTextNode
> newNode
= doc
->CreateTextNode(aStringToInsert
);
2355 // then we insert it into the dom tree
2356 res
= InsertNode(newNode
->AsDOMNode(), node
->AsDOMNode(), offset
);
2357 NS_ENSURE_SUCCESS(res
, res
);
2359 offset
= aStringToInsert
.Length();
2363 *aInOutNode
= node
->AsDOMNode();
2364 *aInOutOffset
= static_cast<int32_t>(offset
);
2370 nsEditor::InsertTextIntoTextNodeImpl(const nsAString
& aStringToInsert
,
2372 int32_t aOffset
, bool aSuppressIME
)
2374 nsRefPtr
<EditTxn
> txn
;
2375 bool isIMETransaction
= false;
2376 // aSuppressIME is used when editor must insert text, yet this text is not
2377 // part of the current IME operation. Example: adjusting whitespace around an
2379 if (mComposition
&& !aSuppressIME
) {
2380 if (!mIMETextNode
) {
2381 mIMETextNode
= &aTextNode
;
2382 mIMETextOffset
= aOffset
;
2384 // Modify mPhonetic with raw text input clauses.
2385 const TextRangeArray
* ranges
= mComposition
->GetRanges();
2386 for (uint32_t i
= 0; i
< (ranges
? ranges
->Length() : 0); ++i
) {
2387 const TextRange
& textRange
= ranges
->ElementAt(i
);
2388 if (!textRange
.Length() ||
2389 textRange
.mRangeType
!= NS_TEXTRANGE_RAWINPUT
) {
2393 mPhonetic
= new nsString();
2395 nsAutoString
stringToInsert(aStringToInsert
);
2396 stringToInsert
.Mid(*mPhonetic
,
2397 textRange
.mStartOffset
, textRange
.Length());
2400 txn
= CreateTxnForIMEText(aStringToInsert
);
2401 isIMETransaction
= true;
2403 txn
= CreateTxnForInsertText(aStringToInsert
, aTextNode
, aOffset
);
2406 // Let listeners know what's up
2407 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
2408 mActionListeners
[i
]->WillInsertText(
2409 static_cast<nsIDOMCharacterData
*>(aTextNode
.AsDOMNode()), aOffset
,
2413 // XXX We may not need these view batches anymore. This is handled at a
2414 // higher level now I believe.
2415 BeginUpdateViewBatch();
2416 nsresult res
= DoTransaction(txn
);
2417 EndUpdateViewBatch();
2419 mRangeUpdater
.SelAdjInsertText(aTextNode
, aOffset
, aStringToInsert
);
2421 // let listeners know what happened
2422 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
2423 mActionListeners
[i
]->DidInsertText(
2424 static_cast<nsIDOMCharacterData
*>(aTextNode
.AsDOMNode()),
2425 aOffset
, aStringToInsert
, res
);
2428 // Added some cruft here for bug 43366. Layout was crashing because we left
2429 // an empty text node lying around in the document. So I delete empty text
2430 // nodes caused by IME. I have to mark the IME transaction as "fixed", which
2431 // means that furure IME txns won't merge with it. This is because we don't
2432 // want future IME txns trying to put their text into a node that is no
2433 // longer in the document. This does not break undo/redo, because all these
2434 // txns are wrapped in a parent PlaceHolder txn, and placeholder txns are
2435 // already savvy to having multiple ime txns inside them.
2437 // Delete empty IME text node if there is one
2438 if (isIMETransaction
&& mIMETextNode
) {
2439 uint32_t len
= mIMETextNode
->Length();
2441 DeleteNode(mIMETextNode
);
2442 mIMETextNode
= nullptr;
2443 static_cast<IMETextTxn
*>(txn
.get())->MarkFixed();
2451 NS_IMETHODIMP
nsEditor::SelectEntireDocument(nsISelection
*aSelection
)
2453 if (!aSelection
) { return NS_ERROR_NULL_POINTER
; }
2455 nsCOMPtr
<nsIDOMElement
> rootElement
= do_QueryInterface(GetRoot());
2456 if (!rootElement
) { return NS_ERROR_NOT_INITIALIZED
; }
2458 return aSelection
->SelectAllChildren(rootElement
);
2463 nsEditor::GetFirstEditableNode(nsINode
* aRoot
)
2467 nsIContent
* node
= GetLeftmostChild(aRoot
);
2468 if (node
&& !IsEditable(node
)) {
2469 node
= GetNextNode(node
, /* aEditableNode = */ true);
2472 return (node
!= aRoot
) ? node
: nullptr;
2477 nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType
)
2479 int32_t numListeners
= mDocStateListeners
.Count();
2480 if (!numListeners
) // maybe there just aren't any.
2483 nsCOMArray
<nsIDocumentStateListener
> listeners(mDocStateListeners
);
2484 nsresult rv
= NS_OK
;
2487 switch (aNotificationType
)
2489 case eDocumentCreated
:
2490 for (i
= 0; i
< numListeners
;i
++)
2492 rv
= listeners
[i
]->NotifyDocumentCreated();
2498 case eDocumentToBeDestroyed
:
2499 for (i
= 0; i
< numListeners
;i
++)
2501 rv
= listeners
[i
]->NotifyDocumentWillBeDestroyed();
2507 case eDocumentStateChanged
:
2510 rv
= GetDocumentModified(&docIsDirty
);
2511 NS_ENSURE_SUCCESS(rv
, rv
);
2513 if (static_cast<int8_t>(docIsDirty
) == mDocDirtyState
)
2516 mDocDirtyState
= docIsDirty
;
2518 for (i
= 0; i
< numListeners
;i
++)
2520 rv
= listeners
[i
]->NotifyDocumentStateChanged(mDocDirtyState
);
2528 NS_NOTREACHED("Unknown notification");
2535 already_AddRefed
<InsertTextTxn
>
2536 nsEditor::CreateTxnForInsertText(const nsAString
& aStringToInsert
,
2537 Text
& aTextNode
, int32_t aOffset
)
2539 nsRefPtr
<InsertTextTxn
> txn
= new InsertTextTxn(aTextNode
, aOffset
,
2540 aStringToInsert
, *this);
2541 return txn
.forget();
2546 nsEditor::DeleteText(nsGenericDOMDataNode
& aCharData
, uint32_t aOffset
,
2549 nsRefPtr
<DeleteTextTxn
> txn
=
2550 CreateTxnForDeleteText(aCharData
, aOffset
, aLength
);
2551 NS_ENSURE_STATE(txn
);
2553 nsAutoRules
beginRulesSniffing(this, EditAction::deleteText
, nsIEditor::ePrevious
);
2555 // Let listeners know what's up
2556 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
2557 mActionListeners
[i
]->WillDeleteText(
2558 static_cast<nsIDOMCharacterData
*>(GetAsDOMNode(&aCharData
)), aOffset
,
2562 nsresult res
= DoTransaction(txn
);
2564 // Let listeners know what happened
2565 for (int32_t i
= 0; i
< mActionListeners
.Count(); i
++) {
2566 mActionListeners
[i
]->DidDeleteText(
2567 static_cast<nsIDOMCharacterData
*>(GetAsDOMNode(&aCharData
)), aOffset
,
2575 already_AddRefed
<DeleteTextTxn
>
2576 nsEditor::CreateTxnForDeleteText(nsGenericDOMDataNode
& aCharData
,
2577 uint32_t aOffset
, uint32_t aLength
)
2579 nsRefPtr
<DeleteTextTxn
> txn
=
2580 new DeleteTextTxn(*this, aCharData
, aOffset
, aLength
, &mRangeUpdater
);
2582 nsresult res
= txn
->Init();
2583 NS_ENSURE_SUCCESS(res
, nullptr);
2585 return txn
.forget();
2591 NS_IMETHODIMP
nsEditor::CreateTxnForSplitNode(nsIDOMNode
*aNode
,
2593 SplitElementTxn
**aTxn
)
2595 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
2597 nsRefPtr
<SplitElementTxn
> txn
= new SplitElementTxn();
2599 nsresult rv
= txn
->Init(this, aNode
, aOffset
);
2600 if (NS_SUCCEEDED(rv
))
2608 NS_IMETHODIMP
nsEditor::CreateTxnForJoinNode(nsIDOMNode
*aLeftNode
,
2609 nsIDOMNode
*aRightNode
,
2610 JoinElementTxn
**aTxn
)
2612 NS_ENSURE_TRUE(aLeftNode
&& aRightNode
, NS_ERROR_NULL_POINTER
);
2614 nsRefPtr
<JoinElementTxn
> txn
= new JoinElementTxn();
2616 nsresult rv
= txn
->Init(this, aLeftNode
, aRightNode
);
2617 if (NS_SUCCEEDED(rv
))
2626 // END nsEditor core implementation
2629 // BEGIN nsEditor public helper methods
2632 nsEditor::SplitNodeImpl(nsIDOMNode
* aExistingRightNode
,
2634 nsIDOMNode
* aNewLeftNode
,
2635 nsIDOMNode
* aParent
)
2637 NS_ASSERTION(((nullptr!=aExistingRightNode
) &&
2638 (nullptr!=aNewLeftNode
) &&
2639 (nullptr!=aParent
)),
2642 if ((nullptr!=aExistingRightNode
) &&
2643 (nullptr!=aNewLeftNode
) &&
2647 nsCOMPtr
<nsISelection
> selection
;
2648 result
= GetSelection(getter_AddRefs(selection
));
2649 NS_ENSURE_SUCCESS(result
, result
);
2650 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2652 // remember some selection points
2653 nsCOMPtr
<nsIDOMNode
> selStartNode
, selEndNode
;
2654 int32_t selStartOffset
, selEndOffset
;
2655 result
= GetStartNodeAndOffset(selection
, getter_AddRefs(selStartNode
), &selStartOffset
);
2656 if (NS_FAILED(result
)) selStartNode
= nullptr; // if selection is cleared, remember that
2657 result
= GetEndNodeAndOffset(selection
, getter_AddRefs(selEndNode
), &selEndOffset
);
2658 if (NS_FAILED(result
)) selStartNode
= nullptr; // if selection is cleared, remember that
2660 nsCOMPtr
<nsIDOMNode
> resultNode
;
2661 result
= aParent
->InsertBefore(aNewLeftNode
, aExistingRightNode
, getter_AddRefs(resultNode
));
2662 //printf(" after insert\n"); content->List(); // DEBUG
2663 if (NS_SUCCEEDED(result
))
2665 // split the children between the 2 nodes
2666 // at this point, aExistingRightNode has all the children
2667 // move all the children whose index is < aOffset to aNewLeftNode
2668 if (0<=aOffset
) // don't bother unless we're going to move at least one child
2670 // if it's a text node, just shuffle around some text
2671 nsCOMPtr
<nsIDOMCharacterData
> rightNodeAsText( do_QueryInterface(aExistingRightNode
) );
2672 nsCOMPtr
<nsIDOMCharacterData
> leftNodeAsText( do_QueryInterface(aNewLeftNode
) );
2673 if (leftNodeAsText
&& rightNodeAsText
)
2676 nsAutoString leftText
;
2677 rightNodeAsText
->SubstringData(0, aOffset
, leftText
);
2678 rightNodeAsText
->DeleteData(0, aOffset
);
2680 leftNodeAsText
->SetData(leftText
);
2684 { // otherwise it's an interior node, so shuffle around the children
2685 // go through list backwards so deletes don't interfere with the iteration
2686 nsCOMPtr
<nsIDOMNodeList
> childNodes
;
2687 result
= aExistingRightNode
->GetChildNodes(getter_AddRefs(childNodes
));
2688 if ((NS_SUCCEEDED(result
)) && (childNodes
))
2690 int32_t i
=aOffset
-1;
2691 for ( ; ((NS_SUCCEEDED(result
)) && (0<=i
)); i
--)
2693 nsCOMPtr
<nsIDOMNode
> childNode
;
2694 result
= childNodes
->Item(i
, getter_AddRefs(childNode
));
2695 if ((NS_SUCCEEDED(result
)) && (childNode
))
2697 result
= aExistingRightNode
->RemoveChild(childNode
, getter_AddRefs(resultNode
));
2698 //printf(" after remove\n"); content->List(); // DEBUG
2699 if (NS_SUCCEEDED(result
))
2701 nsCOMPtr
<nsIDOMNode
> firstChild
;
2702 aNewLeftNode
->GetFirstChild(getter_AddRefs(firstChild
));
2703 result
= aNewLeftNode
->InsertBefore(childNode
, firstChild
, getter_AddRefs(resultNode
));
2704 //printf(" after append\n"); content->List(); // DEBUG
2711 nsCOMPtr
<nsIPresShell
> ps
= GetPresShell();
2713 ps
->FlushPendingNotifications(Flush_Frames
);
2715 if (GetShouldTxnSetSelection())
2717 // editor wants us to set selection at split point
2718 selection
->Collapse(aNewLeftNode
, aOffset
);
2720 else if (selStartNode
)
2722 // else adjust the selection if needed. if selStartNode is null, then there was no selection.
2723 // HACK: this is overly simplified - multi-range selections need more work than this
2724 if (selStartNode
.get() == aExistingRightNode
)
2726 if (selStartOffset
< aOffset
)
2728 selStartNode
= aNewLeftNode
;
2732 selStartOffset
-= aOffset
;
2735 if (selEndNode
.get() == aExistingRightNode
)
2737 if (selEndOffset
< aOffset
)
2739 selEndNode
= aNewLeftNode
;
2743 selEndOffset
-= aOffset
;
2746 selection
->Collapse(selStartNode
,selStartOffset
);
2747 selection
->Extend(selEndNode
,selEndOffset
);
2753 result
= NS_ERROR_INVALID_ARG
;
2759 nsEditor::JoinNodesImpl(nsINode
* aNodeToKeep
,
2760 nsINode
* aNodeToJoin
,
2763 MOZ_ASSERT(aNodeToKeep
);
2764 MOZ_ASSERT(aNodeToJoin
);
2765 MOZ_ASSERT(aParent
);
2767 nsRefPtr
<Selection
> selection
= GetSelection();
2768 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2770 // remember some selection points
2771 nsCOMPtr
<nsINode
> selStartNode
;
2772 int32_t selStartOffset
;
2773 nsresult result
= GetStartNodeAndOffset(selection
, getter_AddRefs(selStartNode
), &selStartOffset
);
2774 if (NS_FAILED(result
)) {
2775 selStartNode
= nullptr;
2778 nsCOMPtr
<nsINode
> selEndNode
;
2779 int32_t selEndOffset
;
2780 result
= GetEndNodeAndOffset(selection
, getter_AddRefs(selEndNode
), &selEndOffset
);
2781 // Joe or Kin should comment here on why the following line is not a copy/paste error
2782 if (NS_FAILED(result
)) {
2783 selStartNode
= nullptr;
2786 uint32_t firstNodeLength
= aNodeToJoin
->Length();
2789 GetNodeLocation(aNodeToJoin
, &joinOffset
);
2791 nsINode
* parent
= GetNodeLocation(aNodeToKeep
, &keepOffset
);
2793 // if selection endpoint is between the nodes, remember it as being
2794 // in the one that is going away instead. This simplifies later selection
2795 // adjustment logic at end of this method.
2797 if (selStartNode
== parent
&&
2798 joinOffset
< selStartOffset
&& selStartOffset
<= keepOffset
) {
2799 selStartNode
= aNodeToJoin
;
2800 selStartOffset
= firstNodeLength
;
2802 if (selEndNode
== parent
&&
2803 joinOffset
< selEndOffset
&& selEndOffset
<= keepOffset
) {
2804 selEndNode
= aNodeToJoin
;
2805 selEndOffset
= firstNodeLength
;
2809 // ok, ready to do join now.
2810 // if it's a text node, just shuffle around some text
2811 nsCOMPtr
<nsIDOMCharacterData
> keepNodeAsText( do_QueryInterface(aNodeToKeep
) );
2812 nsCOMPtr
<nsIDOMCharacterData
> joinNodeAsText( do_QueryInterface(aNodeToJoin
) );
2813 if (keepNodeAsText
&& joinNodeAsText
) {
2814 nsAutoString rightText
;
2815 nsAutoString leftText
;
2816 keepNodeAsText
->GetData(rightText
);
2817 joinNodeAsText
->GetData(leftText
);
2818 leftText
+= rightText
;
2819 keepNodeAsText
->SetData(leftText
);
2821 // otherwise it's an interior node, so shuffle around the children
2822 nsCOMPtr
<nsINodeList
> childNodes
= aNodeToJoin
->ChildNodes();
2823 MOZ_ASSERT(childNodes
);
2825 // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
2826 // GetFirstChild returns nullptr firstNode if aNodeToKeep has no children, that's ok.
2827 nsCOMPtr
<nsIContent
> firstNode
= aNodeToKeep
->GetFirstChild();
2829 // have to go through the list backwards to keep deletes from interfering with iteration
2830 for (uint32_t i
= childNodes
->Length(); i
> 0; --i
) {
2831 nsCOMPtr
<nsIContent
> childNode
= childNodes
->Item(i
- 1);
2833 // prepend children of aNodeToJoin
2835 aNodeToKeep
->InsertBefore(*childNode
, firstNode
, err
);
2836 NS_ENSURE_SUCCESS(err
.ErrorCode(), err
.ErrorCode());
2837 firstNode
= childNode
.forget();
2842 // delete the extra node
2844 aParent
->RemoveChild(*aNodeToJoin
, err
);
2846 if (GetShouldTxnSetSelection()) {
2847 // editor wants us to set selection at join point
2848 selection
->Collapse(aNodeToKeep
, AssertedCast
<int32_t>(firstNodeLength
));
2849 } else if (selStartNode
) {
2850 // and adjust the selection if needed
2851 // HACK: this is overly simplified - multi-range selections need more work than this
2852 bool bNeedToAdjust
= false;
2854 // check to see if we joined nodes where selection starts
2855 if (selStartNode
== aNodeToJoin
) {
2856 bNeedToAdjust
= true;
2857 selStartNode
= aNodeToKeep
;
2858 } else if (selStartNode
== aNodeToKeep
) {
2859 bNeedToAdjust
= true;
2860 selStartOffset
+= firstNodeLength
;
2863 // check to see if we joined nodes where selection ends
2864 if (selEndNode
== aNodeToJoin
) {
2865 bNeedToAdjust
= true;
2866 selEndNode
= aNodeToKeep
;
2867 } else if (selEndNode
== aNodeToKeep
) {
2868 bNeedToAdjust
= true;
2869 selEndOffset
+= firstNodeLength
;
2872 // adjust selection if needed
2873 if (bNeedToAdjust
) {
2874 selection
->Collapse(selStartNode
, selStartOffset
);
2875 selection
->Extend(selEndNode
, selEndOffset
);
2879 return err
.ErrorCode();
2884 nsEditor::GetChildOffset(nsIDOMNode
* aChild
, nsIDOMNode
* aParent
)
2886 MOZ_ASSERT(aChild
&& aParent
);
2888 nsCOMPtr
<nsINode
> parent
= do_QueryInterface(aParent
);
2889 nsCOMPtr
<nsINode
> child
= do_QueryInterface(aChild
);
2890 MOZ_ASSERT(parent
&& child
);
2892 int32_t idx
= parent
->IndexOf(child
);
2893 MOZ_ASSERT(idx
!= -1);
2898 already_AddRefed
<nsIDOMNode
>
2899 nsEditor::GetNodeLocation(nsIDOMNode
* aChild
, int32_t* outOffset
)
2901 MOZ_ASSERT(aChild
&& outOffset
);
2902 NS_ENSURE_TRUE(aChild
&& outOffset
, nullptr);
2905 nsCOMPtr
<nsIDOMNode
> parent
;
2907 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
2908 aChild
->GetParentNode(getter_AddRefs(parent
))));
2910 *outOffset
= GetChildOffset(aChild
, parent
);
2913 return parent
.forget();
2917 nsEditor::GetNodeLocation(nsINode
* aChild
, int32_t* aOffset
)
2920 MOZ_ASSERT(aOffset
);
2922 nsINode
* parent
= aChild
->GetParentNode();
2924 *aOffset
= parent
->IndexOf(aChild
);
2925 MOZ_ASSERT(*aOffset
!= -1);
2932 // returns the number of things inside aNode.
2933 // If aNode is text, returns number of characters. If not, returns number of children nodes.
2935 nsEditor::GetLengthOfDOMNode(nsIDOMNode
*aNode
, uint32_t &aCount
)
2938 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
2939 NS_ENSURE_TRUE(node
, NS_ERROR_NULL_POINTER
);
2940 aCount
= node
->Length();
2946 nsEditor::GetPriorNode(nsIDOMNode
*aParentNode
,
2949 nsCOMPtr
<nsIDOMNode
> *aResultNode
,
2950 bool bNoBlockCrossing
)
2952 NS_ENSURE_TRUE(aResultNode
, NS_ERROR_NULL_POINTER
);
2953 *aResultNode
= nullptr;
2955 nsCOMPtr
<nsINode
> parentNode
= do_QueryInterface(aParentNode
);
2956 NS_ENSURE_TRUE(parentNode
, NS_ERROR_NULL_POINTER
);
2958 *aResultNode
= do_QueryInterface(GetPriorNode(parentNode
, aOffset
,
2965 nsEditor::GetPriorNode(nsINode
* aParentNode
,
2968 bool aNoBlockCrossing
)
2970 MOZ_ASSERT(aParentNode
);
2972 // If we are at the beginning of the node, or it is a text node, then just
2974 if (!aOffset
|| aParentNode
->NodeType() == nsIDOMNode::TEXT_NODE
) {
2975 if (aNoBlockCrossing
&& IsBlockNode(aParentNode
)) {
2976 // If we aren't allowed to cross blocks, don't look before this block.
2979 return GetPriorNode(aParentNode
, aEditableNode
, aNoBlockCrossing
);
2982 // else look before the child at 'aOffset'
2983 if (nsIContent
* child
= aParentNode
->GetChildAt(aOffset
)) {
2984 return GetPriorNode(child
, aEditableNode
, aNoBlockCrossing
);
2987 // unless there isn't one, in which case we are at the end of the node
2988 // and want the deep-right child.
2989 nsIContent
* resultNode
= GetRightmostChild(aParentNode
, aNoBlockCrossing
);
2990 if (!resultNode
|| !aEditableNode
|| IsEditable(resultNode
)) {
2994 // restart the search from the non-editable node we just found
2995 return GetPriorNode(resultNode
, aEditableNode
, aNoBlockCrossing
);
3000 nsEditor::GetNextNode(nsIDOMNode
*aParentNode
,
3003 nsCOMPtr
<nsIDOMNode
> *aResultNode
,
3004 bool bNoBlockCrossing
)
3006 NS_ENSURE_TRUE(aResultNode
, NS_ERROR_NULL_POINTER
);
3007 *aResultNode
= nullptr;
3009 nsCOMPtr
<nsINode
> parentNode
= do_QueryInterface(aParentNode
);
3010 NS_ENSURE_TRUE(parentNode
, NS_ERROR_NULL_POINTER
);
3012 *aResultNode
= do_QueryInterface(GetNextNode(parentNode
, aOffset
,
3019 nsEditor::GetNextNode(nsINode
* aParentNode
,
3022 bool aNoBlockCrossing
)
3024 MOZ_ASSERT(aParentNode
);
3026 // if aParentNode is a text node, use its location instead
3027 if (aParentNode
->NodeType() == nsIDOMNode::TEXT_NODE
) {
3028 nsINode
* parent
= aParentNode
->GetParentNode();
3029 NS_ENSURE_TRUE(parent
, nullptr);
3030 aOffset
= parent
->IndexOf(aParentNode
) + 1; // _after_ the text node
3031 aParentNode
= parent
;
3034 // look at the child at 'aOffset'
3035 nsIContent
* child
= aParentNode
->GetChildAt(aOffset
);
3037 if (aNoBlockCrossing
&& IsBlockNode(child
)) {
3041 nsIContent
* resultNode
= GetLeftmostChild(child
, aNoBlockCrossing
);
3046 if (!IsDescendantOfEditorRoot(resultNode
)) {
3050 if (!aEditableNode
|| IsEditable(resultNode
)) {
3054 // restart the search from the non-editable node we just found
3055 return GetNextNode(resultNode
, aEditableNode
, aNoBlockCrossing
);
3058 // unless there isn't one, in which case we are at the end of the node
3059 // and want the next one.
3060 if (aNoBlockCrossing
&& IsBlockNode(aParentNode
)) {
3061 // don't cross out of parent block
3065 return GetNextNode(aParentNode
, aEditableNode
, aNoBlockCrossing
);
3070 nsEditor::GetPriorNode(nsIDOMNode
*aCurrentNode
,
3072 nsCOMPtr
<nsIDOMNode
> *aResultNode
,
3073 bool bNoBlockCrossing
)
3075 NS_ENSURE_TRUE(aResultNode
, NS_ERROR_NULL_POINTER
);
3077 nsCOMPtr
<nsINode
> currentNode
= do_QueryInterface(aCurrentNode
);
3078 NS_ENSURE_TRUE(currentNode
, NS_ERROR_NULL_POINTER
);
3080 *aResultNode
= do_QueryInterface(GetPriorNode(currentNode
, aEditableNode
,
3086 nsEditor::GetPriorNode(nsINode
* aCurrentNode
, bool aEditableNode
,
3087 bool aNoBlockCrossing
/* = false */)
3089 MOZ_ASSERT(aCurrentNode
);
3091 if (!IsDescendantOfEditorRoot(aCurrentNode
)) {
3095 return FindNode(aCurrentNode
, false, aEditableNode
, aNoBlockCrossing
);
3099 nsEditor::FindNextLeafNode(nsINode
*aCurrentNode
,
3101 bool bNoBlockCrossing
)
3103 // called only by GetPriorNode so we don't need to check params.
3104 NS_PRECONDITION(IsDescendantOfEditorRoot(aCurrentNode
) &&
3105 !IsEditorRoot(aCurrentNode
),
3108 nsINode
* cur
= aCurrentNode
;
3110 // if aCurrentNode has a sibling in the right direction, return
3111 // that sibling's closest child (or itself if it has no children)
3112 nsIContent
* sibling
=
3113 aGoForward
? cur
->GetNextSibling() : cur
->GetPreviousSibling();
3115 if (bNoBlockCrossing
&& IsBlockNode(sibling
)) {
3116 // don't look inside prevsib, since it is a block
3120 aGoForward
? GetLeftmostChild(sibling
, bNoBlockCrossing
) :
3121 GetRightmostChild(sibling
, bNoBlockCrossing
);
3129 nsINode
*parent
= cur
->GetParentNode();
3134 NS_ASSERTION(IsDescendantOfEditorRoot(parent
),
3135 "We started with a proper descendant of root, and should stop "
3136 "if we ever hit the root, so we better have a descendant of "
3138 if (IsEditorRoot(parent
) ||
3139 (bNoBlockCrossing
&& IsBlockNode(parent
))) {
3146 NS_NOTREACHED("What part of for(;;) do you not understand?");
3151 nsEditor::GetNextNode(nsIDOMNode
* aCurrentNode
,
3153 nsCOMPtr
<nsIDOMNode
> *aResultNode
,
3154 bool bNoBlockCrossing
)
3156 nsCOMPtr
<nsINode
> currentNode
= do_QueryInterface(aCurrentNode
);
3157 if (!currentNode
|| !aResultNode
) {
3158 return NS_ERROR_NULL_POINTER
;
3161 *aResultNode
= do_QueryInterface(GetNextNode(currentNode
, aEditableNode
,
3167 nsEditor::GetNextNode(nsINode
* aCurrentNode
,
3169 bool bNoBlockCrossing
)
3171 MOZ_ASSERT(aCurrentNode
);
3173 if (!IsDescendantOfEditorRoot(aCurrentNode
)) {
3177 return FindNode(aCurrentNode
, true, aEditableNode
, bNoBlockCrossing
);
3181 nsEditor::FindNode(nsINode
*aCurrentNode
,
3184 bool bNoBlockCrossing
)
3186 if (IsEditorRoot(aCurrentNode
)) {
3187 // Don't allow traversal above the root node! This helps
3188 // prevent us from accidentally editing browser content
3189 // when the editor is in a text widget.
3194 nsCOMPtr
<nsIContent
> candidate
=
3195 FindNextLeafNode(aCurrentNode
, aGoForward
, bNoBlockCrossing
);
3201 if (!aEditableNode
|| IsEditable(candidate
)) {
3205 return FindNode(candidate
, aGoForward
, aEditableNode
, bNoBlockCrossing
);
3209 nsEditor::GetRightmostChild(nsIDOMNode
* aCurrentNode
,
3210 bool bNoBlockCrossing
)
3212 nsCOMPtr
<nsINode
> currentNode
= do_QueryInterface(aCurrentNode
);
3213 nsIContent
* result
= GetRightmostChild(currentNode
, bNoBlockCrossing
);
3214 return result
? result
->AsDOMNode() : nullptr;
3218 nsEditor::GetRightmostChild(nsINode
*aCurrentNode
,
3219 bool bNoBlockCrossing
)
3221 NS_ENSURE_TRUE(aCurrentNode
, nullptr);
3222 nsIContent
*cur
= aCurrentNode
->GetLastChild();
3227 if (bNoBlockCrossing
&& IsBlockNode(cur
)) {
3230 nsIContent
* next
= cur
->GetLastChild();
3237 NS_NOTREACHED("What part of for(;;) do you not understand?");
3242 nsEditor::GetLeftmostChild(nsINode
*aCurrentNode
,
3243 bool bNoBlockCrossing
)
3245 NS_ENSURE_TRUE(aCurrentNode
, nullptr);
3246 nsIContent
*cur
= aCurrentNode
->GetFirstChild();
3251 if (bNoBlockCrossing
&& IsBlockNode(cur
)) {
3254 nsIContent
*next
= cur
->GetFirstChild();
3261 NS_NOTREACHED("What part of for(;;) do you not understand?");
3266 nsEditor::GetLeftmostChild(nsIDOMNode
* aCurrentNode
,
3267 bool bNoBlockCrossing
)
3269 nsCOMPtr
<nsINode
> currentNode
= do_QueryInterface(aCurrentNode
);
3270 nsIContent
* result
= GetLeftmostChild(currentNode
, bNoBlockCrossing
);
3271 return result
? result
->AsDOMNode() : nullptr;
3275 nsEditor::IsBlockNode(nsIDOMNode
* aNode
)
3277 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
3278 return IsBlockNode(node
);
3282 nsEditor::IsBlockNode(nsINode
* aNode
)
3284 // stub to be overridden in nsHTMLEditor.
3285 // screwing around with the class hierarchy here in order
3286 // to not duplicate the code in GetNextNode/GetPrevNode
3287 // across both nsEditor/nsHTMLEditor.
3292 nsEditor::CanContain(nsIDOMNode
* aParent
, nsIDOMNode
* aChild
)
3294 nsCOMPtr
<nsIContent
> parent
= do_QueryInterface(aParent
);
3295 NS_ENSURE_TRUE(parent
, false);
3297 switch (parent
->NodeType()) {
3298 case nsIDOMNode::ELEMENT_NODE
:
3299 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE
:
3300 return TagCanContain(parent
->Tag(), aChild
);
3306 nsEditor::CanContainTag(nsIDOMNode
* aParent
, nsIAtom
* aChildTag
)
3308 nsCOMPtr
<nsIContent
> parent
= do_QueryInterface(aParent
);
3309 NS_ENSURE_TRUE(parent
, false);
3311 switch (parent
->NodeType()) {
3312 case nsIDOMNode::ELEMENT_NODE
:
3313 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE
:
3314 return TagCanContainTag(parent
->Tag(), aChildTag
);
3320 nsEditor::TagCanContain(nsIAtom
* aParentTag
, nsIDOMNode
* aChild
)
3322 nsCOMPtr
<nsIContent
> child
= do_QueryInterface(aChild
);
3323 NS_ENSURE_TRUE(child
, false);
3325 switch (child
->NodeType()) {
3326 case nsIDOMNode::TEXT_NODE
:
3327 case nsIDOMNode::ELEMENT_NODE
:
3328 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE
:
3329 return TagCanContainTag(aParentTag
, child
->Tag());
3335 nsEditor::TagCanContainTag(nsIAtom
* aParentTag
, nsIAtom
* aChildTag
)
3341 nsEditor::IsRoot(nsIDOMNode
* inNode
)
3343 NS_ENSURE_TRUE(inNode
, false);
3345 nsCOMPtr
<nsIDOMNode
> rootNode
= do_QueryInterface(GetRoot());
3347 return inNode
== rootNode
;
3351 nsEditor::IsRoot(nsINode
* inNode
)
3353 NS_ENSURE_TRUE(inNode
, false);
3355 nsCOMPtr
<nsINode
> rootNode
= GetRoot();
3357 return inNode
== rootNode
;
3361 nsEditor::IsEditorRoot(nsINode
* aNode
)
3363 NS_ENSURE_TRUE(aNode
, false);
3364 nsCOMPtr
<nsINode
> rootNode
= GetEditorRoot();
3365 return aNode
== rootNode
;
3369 nsEditor::IsDescendantOfRoot(nsIDOMNode
* inNode
)
3371 nsCOMPtr
<nsINode
> node
= do_QueryInterface(inNode
);
3372 return IsDescendantOfRoot(node
);
3376 nsEditor::IsDescendantOfRoot(nsINode
* inNode
)
3378 NS_ENSURE_TRUE(inNode
, false);
3379 nsCOMPtr
<nsIContent
> root
= GetRoot();
3380 NS_ENSURE_TRUE(root
, false);
3382 return nsContentUtils::ContentIsDescendantOf(inNode
, root
);
3386 nsEditor::IsDescendantOfEditorRoot(nsIDOMNode
* aNode
)
3388 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
3389 return IsDescendantOfEditorRoot(node
);
3393 nsEditor::IsDescendantOfEditorRoot(nsINode
* aNode
)
3395 NS_ENSURE_TRUE(aNode
, false);
3396 nsCOMPtr
<nsIContent
> root
= GetEditorRoot();
3397 NS_ENSURE_TRUE(root
, false);
3399 return nsContentUtils::ContentIsDescendantOf(aNode
, root
);
3403 nsEditor::IsContainer(nsINode
* aNode
)
3405 return aNode
? true : false;
3409 nsEditor::IsContainer(nsIDOMNode
* aNode
)
3411 return aNode
? true : false;
3415 IsElementVisible(dom::Element
* aElement
)
3417 if (aElement
->GetPrimaryFrame()) {
3418 // It's visible, for our purposes
3422 nsIContent
*cur
= aElement
;
3424 // Walk up the tree looking for the nearest ancestor with a frame.
3425 // The state of the child right below it will determine whether
3426 // we might possibly have a frame or not.
3427 bool haveLazyBitOnChild
= cur
->HasFlag(NODE_NEEDS_FRAME
);
3428 cur
= cur
->GetFlattenedTreeParent();
3430 if (!haveLazyBitOnChild
) {
3431 // None of our ancestors have lazy bits set, so we shouldn't
3436 // The root has a lazy frame construction bit. We need to check
3441 if (cur
->GetPrimaryFrame()) {
3442 if (!haveLazyBitOnChild
) {
3443 // Our ancestor directly under |cur| doesn't have lazy bits;
3444 // that means we won't get a frame
3448 if (cur
->GetPrimaryFrame()->IsLeaf()) {
3449 // Nothing under here will ever get frames
3453 // Otherwise, we might end up with a frame when that lazy bit is
3454 // processed. Figure out our actual style.
3459 // Now it might be that we have no frame because we're in a
3460 // display:none subtree, or it might be that we're just dealing with
3461 // lazy frame construction and it hasn't happened yet. Check which
3463 nsRefPtr
<nsStyleContext
> styleContext
=
3464 nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement
,
3467 return styleContext
->StyleDisplay()->mDisplay
!= NS_STYLE_DISPLAY_NONE
;
3473 nsEditor::IsEditable(nsIDOMNode
*aNode
)
3475 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
3476 return IsEditable(content
);
3480 nsEditor::IsEditable(nsINode
* aNode
)
3482 NS_ENSURE_TRUE(aNode
, false);
3484 if (!aNode
->IsNodeOfType(nsINode::eCONTENT
) || IsMozEditorBogusNode(aNode
) ||
3485 !IsModifiableNode(aNode
)) {
3489 // see if it has a frame. If so, we'll edit it.
3490 // special case for textnodes: frame must have width.
3491 if (aNode
->IsElement() && !IsElementVisible(aNode
->AsElement())) {
3492 // If the element has no frame, it's not editable. Note that we
3493 // need to check IsElement() here, because some of our tests
3494 // rely on frameless textnodes being visible.
3497 switch (aNode
->NodeType()) {
3498 case nsIDOMNode::ELEMENT_NODE
:
3499 case nsIDOMNode::TEXT_NODE
:
3500 return true; // element or text node; not invisible
3507 nsEditor::IsMozEditorBogusNode(nsINode
* element
)
3509 return element
&& element
->IsElement() &&
3510 element
->AsElement()->AttrValueIs(kNameSpaceID_None
,
3511 kMOZEditorBogusNodeAttrAtom
, kMOZEditorBogusNodeValue
,
3516 nsEditor::CountEditableChildren(nsINode
* aNode
)
3520 for (nsIContent
* child
= aNode
->GetFirstChild();
3522 child
= child
->GetNextSibling()) {
3523 if (IsEditable(child
)) {
3530 //END nsEditor static utility methods
3533 NS_IMETHODIMP
nsEditor::IncrementModificationCount(int32_t inNumMods
)
3535 uint32_t oldModCount
= mModCount
;
3537 mModCount
+= inNumMods
;
3539 if ((oldModCount
== 0 && mModCount
!= 0)
3540 || (oldModCount
!= 0 && mModCount
== 0))
3541 NotifyDocumentListeners(eDocumentStateChanged
);
3546 NS_IMETHODIMP
nsEditor::GetModificationCount(int32_t *outModCount
)
3548 NS_ENSURE_ARG_POINTER(outModCount
);
3549 *outModCount
= mModCount
;
3554 NS_IMETHODIMP
nsEditor::ResetModificationCount()
3556 bool doNotify
= (mModCount
!= 0);
3561 NotifyDocumentListeners(eDocumentStateChanged
);
3565 //END nsEditor Private methods
3569 ///////////////////////////////////////////////////////////////////////////
3570 // GetTag: digs out the atom for the tag of this node
3573 nsEditor::GetTag(nsIDOMNode
*aNode
)
3575 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
3579 NS_ASSERTION(aNode
, "null node passed to nsEditor::Tag()");
3584 return content
->Tag();
3588 ///////////////////////////////////////////////////////////////////////////
3589 // GetTagString: digs out string for the tag of this node
3592 nsEditor::GetTagString(nsIDOMNode
*aNode
, nsAString
& outString
)
3596 NS_NOTREACHED("null node passed to nsEditor::GetTag()");
3597 return NS_ERROR_NULL_POINTER
;
3600 nsIAtom
*atom
= GetTag(aNode
);
3603 return NS_ERROR_FAILURE
;
3606 atom
->ToString(outString
);
3611 ///////////////////////////////////////////////////////////////////////////
3612 // NodesSameType: do these nodes have the same tag?
3615 nsEditor::NodesSameType(nsIDOMNode
*aNode1
, nsIDOMNode
*aNode2
)
3617 if (!aNode1
|| !aNode2
) {
3618 NS_NOTREACHED("null node passed to nsEditor::NodesSameType()");
3622 nsCOMPtr
<nsIContent
> content1
= do_QueryInterface(aNode1
);
3623 NS_ENSURE_TRUE(content1
, false);
3625 nsCOMPtr
<nsIContent
> content2
= do_QueryInterface(aNode2
);
3626 NS_ENSURE_TRUE(content2
, false);
3628 return AreNodesSameType(content1
, content2
);
3633 nsEditor::AreNodesSameType(nsIContent
* aNode1
, nsIContent
* aNode2
)
3637 return aNode1
->Tag() == aNode2
->Tag();
3641 ///////////////////////////////////////////////////////////////////////////
3642 // IsTextNode: true if node of dom type text
3645 nsEditor::IsTextNode(nsIDOMNode
*aNode
)
3649 NS_NOTREACHED("null node passed to IsTextNode()");
3654 aNode
->GetNodeType(&nodeType
);
3655 return (nodeType
== nsIDOMNode::TEXT_NODE
);
3659 nsEditor::IsTextNode(nsINode
*aNode
)
3661 return aNode
->NodeType() == nsIDOMNode::TEXT_NODE
;
3664 ///////////////////////////////////////////////////////////////////////////
3665 // GetChildAt: returns the node at this position index in the parent
3667 nsCOMPtr
<nsIDOMNode
>
3668 nsEditor::GetChildAt(nsIDOMNode
*aParent
, int32_t aOffset
)
3670 nsCOMPtr
<nsIDOMNode
> resultNode
;
3672 nsCOMPtr
<nsIContent
> parent
= do_QueryInterface(aParent
);
3674 NS_ENSURE_TRUE(parent
, resultNode
);
3676 resultNode
= do_QueryInterface(parent
->GetChildAt(aOffset
));
3681 ///////////////////////////////////////////////////////////////////////////
3682 // GetNodeAtRangeOffsetPoint: returns the node at this position in a range,
3683 // assuming that aParentOrNode is the node itself if it's a text node, or
3684 // the node's parent otherwise.
3686 nsCOMPtr
<nsIDOMNode
>
3687 nsEditor::GetNodeAtRangeOffsetPoint(nsIDOMNode
* aParentOrNode
, int32_t aOffset
)
3689 if (IsTextNode(aParentOrNode
)) {
3690 return aParentOrNode
;
3692 return GetChildAt(aParentOrNode
, aOffset
);
3696 ///////////////////////////////////////////////////////////////////////////
3697 // GetStartNodeAndOffset: returns whatever the start parent & offset is of
3698 // the first range in the selection.
3700 nsEditor::GetStartNodeAndOffset(nsISelection
*aSelection
,
3701 nsIDOMNode
**outStartNode
,
3702 int32_t *outStartOffset
)
3704 NS_ENSURE_TRUE(outStartNode
&& outStartOffset
&& aSelection
, NS_ERROR_NULL_POINTER
);
3706 nsCOMPtr
<nsINode
> startNode
;
3707 nsresult rv
= GetStartNodeAndOffset(static_cast<Selection
*>(aSelection
),
3708 getter_AddRefs(startNode
),
3710 NS_ENSURE_SUCCESS(rv
, rv
);
3713 NS_ADDREF(*outStartNode
= startNode
->AsDOMNode());
3715 *outStartNode
= nullptr;
3721 nsEditor::GetStartNodeAndOffset(Selection
* aSelection
, nsINode
** aStartNode
,
3722 int32_t* aStartOffset
)
3724 MOZ_ASSERT(aSelection
);
3725 MOZ_ASSERT(aStartNode
);
3726 MOZ_ASSERT(aStartOffset
);
3728 *aStartNode
= nullptr;
3731 NS_ENSURE_TRUE(aSelection
->GetRangeCount(), NS_ERROR_FAILURE
);
3733 const nsRange
* range
= aSelection
->GetRangeAt(0);
3734 NS_ENSURE_TRUE(range
, NS_ERROR_FAILURE
);
3736 NS_ENSURE_TRUE(range
->IsPositioned(), NS_ERROR_FAILURE
);
3738 NS_IF_ADDREF(*aStartNode
= range
->GetStartParent());
3739 *aStartOffset
= range
->StartOffset();
3744 ///////////////////////////////////////////////////////////////////////////
3745 // GetEndNodeAndOffset: returns whatever the end parent & offset is of
3746 // the first range in the selection.
3748 nsEditor::GetEndNodeAndOffset(nsISelection
*aSelection
,
3749 nsIDOMNode
**outEndNode
,
3750 int32_t *outEndOffset
)
3752 NS_ENSURE_TRUE(outEndNode
&& outEndOffset
&& aSelection
, NS_ERROR_NULL_POINTER
);
3754 nsCOMPtr
<nsINode
> endNode
;
3755 nsresult rv
= GetEndNodeAndOffset(static_cast<Selection
*>(aSelection
),
3756 getter_AddRefs(endNode
),
3758 NS_ENSURE_SUCCESS(rv
, rv
);
3761 NS_ADDREF(*outEndNode
= endNode
->AsDOMNode());
3763 *outEndNode
= nullptr;
3769 nsEditor::GetEndNodeAndOffset(Selection
* aSelection
, nsINode
** aEndNode
,
3770 int32_t* aEndOffset
)
3772 MOZ_ASSERT(aSelection
);
3773 MOZ_ASSERT(aEndNode
);
3774 MOZ_ASSERT(aEndOffset
);
3776 *aEndNode
= nullptr;
3779 NS_ENSURE_TRUE(aSelection
->GetRangeCount(), NS_ERROR_FAILURE
);
3781 const nsRange
* range
= aSelection
->GetRangeAt(0);
3782 NS_ENSURE_TRUE(range
, NS_ERROR_FAILURE
);
3784 NS_ENSURE_TRUE(range
->IsPositioned(), NS_ERROR_FAILURE
);
3786 NS_IF_ADDREF(*aEndNode
= range
->GetEndParent());
3787 *aEndOffset
= range
->EndOffset();
3792 ///////////////////////////////////////////////////////////////////////////
3793 // IsPreformatted: checks the style info for the node for the preformatted
3796 nsEditor::IsPreformatted(nsIDOMNode
*aNode
, bool *aResult
)
3798 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
3800 NS_ENSURE_TRUE(aResult
&& content
, NS_ERROR_NULL_POINTER
);
3802 nsCOMPtr
<nsIPresShell
> ps
= GetPresShell();
3803 NS_ENSURE_TRUE(ps
, NS_ERROR_NOT_INITIALIZED
);
3805 // Look at the node (and its parent if it's not an element), and grab its style context
3806 nsRefPtr
<nsStyleContext
> elementStyle
;
3807 if (!content
->IsElement()) {
3808 content
= content
->GetParent();
3810 if (content
&& content
->IsElement()) {
3811 elementStyle
= nsComputedDOMStyle::GetStyleContextForElementNoFlush(content
->AsElement(),
3818 // Consider nodes without a style context to be NOT preformatted:
3819 // For instance, this is true of JS tags inside the body (which show
3820 // up as #text nodes but have no style context).
3825 const nsStyleText
* styleText
= elementStyle
->StyleText();
3827 *aResult
= styleText
->WhiteSpaceIsSignificant();
3832 ///////////////////////////////////////////////////////////////////////////
3833 // SplitNodeDeep: this splits a node "deeply", splitting children as
3834 // appropriate. The place to split is represented by
3835 // a dom point at {splitPointParent, splitPointOffset}.
3836 // That dom point must be inside aNode, which is the node to
3837 // split. outOffset is set to the offset in the parent of aNode where
3838 // the split terminates - where you would want to insert
3839 // a new element, for instance, if that's why you were splitting
3843 nsEditor::SplitNodeDeep(nsIDOMNode
*aNode
,
3844 nsIDOMNode
*aSplitPointParent
,
3845 int32_t aSplitPointOffset
,
3847 bool aNoEmptyContainers
,
3848 nsCOMPtr
<nsIDOMNode
> *outLeftNode
,
3849 nsCOMPtr
<nsIDOMNode
> *outRightNode
)
3851 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
3852 NS_ENSURE_TRUE(node
&& aSplitPointParent
&& outOffset
, NS_ERROR_NULL_POINTER
);
3853 int32_t offset
= aSplitPointOffset
;
3855 if (outLeftNode
) *outLeftNode
= nullptr;
3856 if (outRightNode
) *outRightNode
= nullptr;
3858 nsCOMPtr
<nsINode
> nodeToSplit
= do_QueryInterface(aSplitPointParent
);
3859 while (nodeToSplit
) {
3860 // need to insert rules code call here to do things like
3861 // not split a list if you are after the last <li> or before the first, etc.
3862 // for now we just have some smarts about unneccessarily splitting
3863 // textnodes, which should be universal enough to put straight in
3864 // this nsEditor routine.
3866 nsCOMPtr
<nsIDOMCharacterData
> nodeAsText
= do_QueryInterface(nodeToSplit
);
3867 uint32_t len
= nodeToSplit
->Length();
3868 bool bDoSplit
= false;
3870 if (!(aNoEmptyContainers
|| nodeAsText
) || (offset
&& (offset
!= (int32_t)len
)))
3873 nsCOMPtr
<nsIDOMNode
> tempNode
;
3874 nsresult rv
= SplitNode(nodeToSplit
->AsDOMNode(), offset
,
3875 getter_AddRefs(tempNode
));
3876 NS_ENSURE_SUCCESS(rv
, rv
);
3879 *outRightNode
= nodeToSplit
->AsDOMNode();
3882 *outLeftNode
= tempNode
;
3886 nsINode
* parentNode
= nodeToSplit
->GetParentNode();
3887 NS_ENSURE_TRUE(parentNode
, NS_ERROR_FAILURE
);
3889 if (!bDoSplit
&& offset
) {
3890 // must be "end of text node" case, we didn't split it, just move past it
3891 offset
= parentNode
->IndexOf(nodeToSplit
) + 1;
3893 *outLeftNode
= nodeToSplit
->AsDOMNode();
3896 offset
= parentNode
->IndexOf(nodeToSplit
);
3898 *outRightNode
= nodeToSplit
->AsDOMNode();
3902 if (nodeToSplit
== node
) {
3903 // we split all the way up to (and including) aNode; we're done
3907 nodeToSplit
= parentNode
;
3911 NS_NOTREACHED("null node obtained in nsEditor::SplitNodeDeep()");
3912 return NS_ERROR_FAILURE
;
3915 *outOffset
= offset
;
3920 ///////////////////////////////////////////////////////////////////////////
3921 // JoinNodeDeep: this joins two like nodes "deeply", joining children as
3924 nsEditor::JoinNodeDeep(nsIDOMNode
*aLeftNode
,
3925 nsIDOMNode
*aRightNode
,
3926 nsCOMPtr
<nsIDOMNode
> *aOutJoinNode
,
3929 NS_ENSURE_TRUE(aLeftNode
&& aRightNode
&& aOutJoinNode
&& outOffset
, NS_ERROR_NULL_POINTER
);
3931 // while the rightmost children and their descendants of the left node
3932 // match the leftmost children and their descendants of the right node
3933 // join them up. Can you say that three times fast?
3935 nsCOMPtr
<nsIDOMNode
> leftNodeToJoin
= do_QueryInterface(aLeftNode
);
3936 nsCOMPtr
<nsIDOMNode
> rightNodeToJoin
= do_QueryInterface(aRightNode
);
3937 nsCOMPtr
<nsIDOMNode
> parentNode
,tmp
;
3938 nsresult res
= NS_OK
;
3940 rightNodeToJoin
->GetParentNode(getter_AddRefs(parentNode
));
3942 while (leftNodeToJoin
&& rightNodeToJoin
&& parentNode
&&
3943 NodesSameType(leftNodeToJoin
, rightNodeToJoin
))
3945 // adjust out params
3947 res
= GetLengthOfDOMNode(leftNodeToJoin
, length
);
3948 NS_ENSURE_SUCCESS(res
, res
);
3950 *aOutJoinNode
= rightNodeToJoin
;
3951 *outOffset
= length
;
3954 res
= JoinNodes(leftNodeToJoin
, rightNodeToJoin
, parentNode
);
3955 NS_ENSURE_SUCCESS(res
, res
);
3957 if (IsTextNode(parentNode
)) // we've joined all the way down to text nodes, we're done!
3962 // get new left and right nodes, and begin anew
3963 parentNode
= rightNodeToJoin
;
3964 leftNodeToJoin
= GetChildAt(parentNode
, length
-1);
3965 rightNodeToJoin
= GetChildAt(parentNode
, length
);
3967 // skip over non-editable nodes
3968 while (leftNodeToJoin
&& !IsEditable(leftNodeToJoin
))
3970 leftNodeToJoin
->GetPreviousSibling(getter_AddRefs(tmp
));
3971 leftNodeToJoin
= tmp
;
3973 if (!leftNodeToJoin
) break;
3975 while (rightNodeToJoin
&& !IsEditable(rightNodeToJoin
))
3977 rightNodeToJoin
->GetNextSibling(getter_AddRefs(tmp
));
3978 rightNodeToJoin
= tmp
;
3980 if (!rightNodeToJoin
) break;
3988 nsEditor::BeginUpdateViewBatch()
3990 NS_PRECONDITION(mUpdateCount
>= 0, "bad state");
3992 if (0 == mUpdateCount
)
3994 // Turn off selection updates and notifications.
3996 nsCOMPtr
<nsISelection
> selection
;
3997 GetSelection(getter_AddRefs(selection
));
4001 nsCOMPtr
<nsISelectionPrivate
> selPrivate(do_QueryInterface(selection
));
4002 selPrivate
->StartBatchChanges();
4010 nsresult
nsEditor::EndUpdateViewBatch()
4012 NS_PRECONDITION(mUpdateCount
> 0, "bad state");
4014 if (mUpdateCount
<= 0)
4017 return NS_ERROR_FAILURE
;
4022 if (0 == mUpdateCount
)
4024 // Turn selection updating and notifications back on.
4026 nsCOMPtr
<nsISelection
>selection
;
4027 GetSelection(getter_AddRefs(selection
));
4030 nsCOMPtr
<nsISelectionPrivate
>selPrivate(do_QueryInterface(selection
));
4031 selPrivate
->EndBatchChanges();
4039 nsEditor::GetShouldTxnSetSelection()
4041 return mShouldTxnSetSelection
;
4046 nsEditor::DeleteSelectionImpl(EDirection aAction
,
4047 EStripWrappers aStripWrappers
)
4049 MOZ_ASSERT(aStripWrappers
== eStrip
|| aStripWrappers
== eNoStrip
);
4051 nsCOMPtr
<nsISelection
>selection
;
4052 nsresult res
= GetSelection(getter_AddRefs(selection
));
4053 NS_ENSURE_SUCCESS(res
, res
);
4054 nsRefPtr
<EditAggregateTxn
> txn
;
4055 nsCOMPtr
<nsINode
> deleteNode
;
4056 int32_t deleteCharOffset
= 0, deleteCharLength
= 0;
4057 res
= CreateTxnForDeleteSelection(aAction
, getter_AddRefs(txn
),
4058 getter_AddRefs(deleteNode
),
4059 &deleteCharOffset
, &deleteCharLength
);
4060 nsCOMPtr
<nsIDOMCharacterData
> deleteCharData(do_QueryInterface(deleteNode
));
4062 if (NS_SUCCEEDED(res
))
4064 nsAutoRules
beginRulesSniffing(this, EditAction::deleteSelection
, aAction
);
4066 // Notify nsIEditActionListener::WillDelete[Selection|Text|Node]
4068 for (i
= 0; i
< mActionListeners
.Count(); i
++)
4069 mActionListeners
[i
]->WillDeleteSelection(selection
);
4070 else if (deleteCharData
)
4071 for (i
= 0; i
< mActionListeners
.Count(); i
++)
4072 mActionListeners
[i
]->WillDeleteText(deleteCharData
, deleteCharOffset
, 1);
4074 for (i
= 0; i
< mActionListeners
.Count(); i
++)
4075 mActionListeners
[i
]->WillDeleteNode(deleteNode
->AsDOMNode());
4077 // Delete the specified amount
4078 res
= DoTransaction(txn
);
4080 // Notify nsIEditActionListener::DidDelete[Selection|Text|Node]
4082 for (i
= 0; i
< mActionListeners
.Count(); i
++)
4083 mActionListeners
[i
]->DidDeleteSelection(selection
);
4084 else if (deleteCharData
)
4085 for (i
= 0; i
< mActionListeners
.Count(); i
++)
4086 mActionListeners
[i
]->DidDeleteText(deleteCharData
, deleteCharOffset
, 1, res
);
4088 for (i
= 0; i
< mActionListeners
.Count(); i
++)
4089 mActionListeners
[i
]->DidDeleteNode(deleteNode
->AsDOMNode(), res
);
4095 // XXX: error handling in this routine needs to be cleaned up!
4097 nsEditor::DeleteSelectionAndCreateNode(const nsAString
& aTag
,
4098 nsIDOMNode
** aNewNode
)
4100 nsCOMPtr
<nsIAtom
> tag
= do_GetAtom(aTag
);
4102 nsresult result
= DeleteSelectionAndPrepareToCreateNode();
4103 NS_ENSURE_SUCCESS(result
, result
);
4105 nsRefPtr
<Selection
> selection
= GetSelection();
4106 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
4108 nsCOMPtr
<nsINode
> node
= selection
->GetAnchorNode();
4109 uint32_t offset
= selection
->AnchorOffset();
4111 nsCOMPtr
<nsIDOMNode
> newNode
;
4112 *aNewNode
= GetAsDOMNode(CreateNode(tag
, node
, offset
).take());
4113 // XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly
4114 // in success/failure cases
4116 // we want the selection to be just after the new node
4117 return selection
->Collapse(node
, offset
+ 1);
4121 /* Non-interface, protected methods */
4124 nsEditor::GetComposition() const
4126 return mComposition
;
4130 nsEditor::IsIMEComposing() const
4132 return mComposition
&& mComposition
->IsComposing();
4136 nsEditor::DeleteSelectionAndPrepareToCreateNode()
4139 nsRefPtr
<Selection
> selection
= GetSelection();
4140 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
4141 MOZ_ASSERT(selection
->GetAnchorFocusRange());
4143 if (!selection
->GetAnchorFocusRange()->Collapsed()) {
4144 res
= DeleteSelection(nsIEditor::eNone
, nsIEditor::eStrip
);
4145 NS_ENSURE_SUCCESS(res
, res
);
4147 MOZ_ASSERT(selection
->GetAnchorFocusRange() &&
4148 selection
->GetAnchorFocusRange()->Collapsed(),
4149 "Selection not collapsed after delete");
4152 // If the selection is a chardata node, split it if necessary and compute
4153 // where to put the new node
4154 nsCOMPtr
<nsINode
> node
= selection
->GetAnchorNode();
4155 MOZ_ASSERT(node
, "Selection has no ranges in it");
4157 if (node
&& node
->IsNodeOfType(nsINode::eDATA_NODE
)) {
4158 NS_ASSERTION(node
->GetParentNode(),
4159 "It's impossible to insert into chardata with no parent -- "
4161 NS_ENSURE_STATE(node
->GetParentNode());
4163 uint32_t offset
= selection
->AnchorOffset();
4166 res
= selection
->Collapse(node
->GetParentNode(),
4167 node
->GetParentNode()->IndexOf(node
));
4168 MOZ_ASSERT(NS_SUCCEEDED(res
));
4169 NS_ENSURE_SUCCESS(res
, res
);
4170 } else if (offset
== node
->Length()) {
4171 res
= selection
->Collapse(node
->GetParentNode(),
4172 node
->GetParentNode()->IndexOf(node
) + 1);
4173 MOZ_ASSERT(NS_SUCCEEDED(res
));
4174 NS_ENSURE_SUCCESS(res
, res
);
4176 nsCOMPtr
<nsIDOMNode
> tmp
;
4177 res
= SplitNode(node
->AsDOMNode(), offset
, getter_AddRefs(tmp
));
4178 NS_ENSURE_SUCCESS(res
, res
);
4179 res
= selection
->Collapse(node
->GetParentNode(),
4180 node
->GetParentNode()->IndexOf(node
));
4181 MOZ_ASSERT(NS_SUCCEEDED(res
));
4182 NS_ENSURE_SUCCESS(res
, res
);
4191 nsEditor::DoAfterDoTransaction(nsITransaction
*aTxn
)
4193 bool isTransientTransaction
;
4194 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
4195 aTxn
->GetIsTransient(&isTransientTransaction
)));
4197 if (!isTransientTransaction
)
4199 // we need to deal here with the case where the user saved after some
4200 // edits, then undid one or more times. Then, the undo count is -ve,
4201 // but we can't let a do take it back to zero. So we flip it up to
4204 GetModificationCount(&modCount
);
4206 modCount
= -modCount
;
4208 // don't count transient transactions
4209 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
4210 IncrementModificationCount(1)));
4216 nsEditor::DoAfterUndoTransaction()
4218 // all undoable transactions are non-transient
4219 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
4220 IncrementModificationCount(-1)));
4224 nsEditor::DoAfterRedoTransaction()
4226 // all redoable transactions are non-transient
4227 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
4228 IncrementModificationCount(1)));
4231 already_AddRefed
<ChangeAttributeTxn
>
4232 nsEditor::CreateTxnForSetAttribute(Element
& aElement
, nsIAtom
& aAttribute
,
4233 const nsAString
& aValue
)
4235 nsRefPtr
<ChangeAttributeTxn
> txn
=
4236 new ChangeAttributeTxn(aElement
, aAttribute
, &aValue
);
4238 return txn
.forget();
4242 already_AddRefed
<ChangeAttributeTxn
>
4243 nsEditor::CreateTxnForRemoveAttribute(Element
& aElement
, nsIAtom
& aAttribute
)
4245 nsRefPtr
<ChangeAttributeTxn
> txn
=
4246 new ChangeAttributeTxn(aElement
, aAttribute
, nullptr);
4248 return txn
.forget();
4252 already_AddRefed
<CreateElementTxn
>
4253 nsEditor::CreateTxnForCreateElement(nsIAtom
& aTag
,
4257 nsRefPtr
<CreateElementTxn
> txn
=
4258 new CreateElementTxn(*this, aTag
, aParent
, aPosition
);
4260 return txn
.forget();
4264 already_AddRefed
<InsertNodeTxn
>
4265 nsEditor::CreateTxnForInsertNode(nsIContent
& aNode
,
4269 nsRefPtr
<InsertNodeTxn
> txn
= new InsertNodeTxn(aNode
, aParent
, aPosition
,
4271 return txn
.forget();
4275 nsEditor::CreateTxnForDeleteNode(nsINode
* aNode
, DeleteNodeTxn
** aTxn
)
4277 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
4279 nsRefPtr
<DeleteNodeTxn
> txn
= new DeleteNodeTxn();
4281 nsresult res
= txn
->Init(this, aNode
, &mRangeUpdater
);
4282 NS_ENSURE_SUCCESS(res
, res
);
4288 already_AddRefed
<IMETextTxn
>
4289 nsEditor::CreateTxnForIMEText(const nsAString
& aStringToInsert
)
4291 // During handling IME composition, mComposition must have been initialized.
4292 // TODO: We can simplify IMETextTxn::Init() with TextComposition class.
4293 nsRefPtr
<IMETextTxn
> txn
= new IMETextTxn(*mIMETextNode
, mIMETextOffset
,
4294 mComposition
->String().Length(),
4295 mComposition
->GetRanges(),
4296 aStringToInsert
, *this);
4297 return txn
.forget();
4302 nsEditor::CreateTxnForAddStyleSheet(CSSStyleSheet
* aSheet
, AddStyleSheetTxn
* *aTxn
)
4304 nsRefPtr
<AddStyleSheetTxn
> txn
= new AddStyleSheetTxn();
4306 nsresult rv
= txn
->Init(this, aSheet
);
4307 if (NS_SUCCEEDED(rv
))
4318 nsEditor::CreateTxnForRemoveStyleSheet(CSSStyleSheet
* aSheet
, RemoveStyleSheetTxn
* *aTxn
)
4320 nsRefPtr
<RemoveStyleSheetTxn
> txn
= new RemoveStyleSheetTxn();
4322 nsresult rv
= txn
->Init(this, aSheet
);
4323 if (NS_SUCCEEDED(rv
))
4333 nsEditor::CreateTxnForDeleteSelection(EDirection aAction
,
4334 EditAggregateTxn
** aTxn
,
4342 nsRefPtr
<Selection
> selection
= GetSelection();
4343 NS_ENSURE_STATE(selection
);
4345 // Check whether the selection is collapsed and we should do nothing:
4346 if (selection
->Collapsed() && aAction
== eNone
) {
4350 // allocate the out-param transaction
4351 nsRefPtr
<EditAggregateTxn
> aggTxn
= new EditAggregateTxn();
4353 for (int32_t rangeIdx
= 0; rangeIdx
< selection
->GetRangeCount(); ++rangeIdx
) {
4354 nsRefPtr
<nsRange
> range
= selection
->GetRangeAt(rangeIdx
);
4355 NS_ENSURE_STATE(range
);
4357 // Same with range as with selection; if it is collapsed and action
4358 // is eNone, do nothing.
4359 if (!range
->Collapsed()) {
4360 nsRefPtr
<DeleteRangeTxn
> txn
= new DeleteRangeTxn();
4361 txn
->Init(this, range
, &mRangeUpdater
);
4362 aggTxn
->AppendChild(txn
);
4363 } else if (aAction
!= eNone
) {
4364 // we have an insertion point. delete the thing in front of it or
4365 // behind it, depending on aAction
4366 nsresult res
= CreateTxnForDeleteInsertionPoint(range
, aAction
, aggTxn
,
4367 aNode
, aOffset
, aLength
);
4368 NS_ENSURE_SUCCESS(res
, res
);
4372 aggTxn
.forget(aTxn
);
4377 already_AddRefed
<DeleteTextTxn
>
4378 nsEditor::CreateTxnForDeleteCharacter(nsGenericDOMDataNode
& aData
,
4379 uint32_t aOffset
, EDirection aDirection
)
4381 NS_ASSERTION(aDirection
== eNext
|| aDirection
== ePrevious
,
4382 "Invalid direction");
4384 aData
.GetData(data
);
4385 NS_ASSERTION(data
.Length(), "Trying to delete from a zero-length node");
4386 NS_ENSURE_TRUE(data
.Length(), nullptr);
4388 uint32_t segOffset
= aOffset
, segLength
= 1;
4389 if (aDirection
== eNext
) {
4390 if (segOffset
+ 1 < data
.Length() &&
4391 NS_IS_HIGH_SURROGATE(data
[segOffset
]) &&
4392 NS_IS_LOW_SURROGATE(data
[segOffset
+1])) {
4393 // Delete both halves of the surrogate pair
4396 } else if (aOffset
> 0) {
4398 if (segOffset
> 0 &&
4399 NS_IS_LOW_SURROGATE(data
[segOffset
]) &&
4400 NS_IS_HIGH_SURROGATE(data
[segOffset
-1])) {
4407 return CreateTxnForDeleteText(aData
, segOffset
, segLength
);
4410 //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
4411 //are not implemented
4413 nsEditor::CreateTxnForDeleteInsertionPoint(nsRange
* aRange
,
4415 EditAggregateTxn
* aTxn
,
4420 MOZ_ASSERT(aAction
!= eNone
);
4424 // get the node and offset of the insertion point
4425 nsCOMPtr
<nsINode
> node
= aRange
->GetStartParent();
4426 NS_ENSURE_STATE(node
);
4428 int32_t offset
= aRange
->StartOffset();
4430 // determine if the insertion point is at the beginning, middle, or end of
4433 uint32_t count
= node
->Length();
4435 bool isFirst
= (0 == offset
);
4436 bool isLast
= (count
== (uint32_t)offset
);
4438 // XXX: if isFirst && isLast, then we'll need to delete the node
4439 // as well as the 1 child
4441 // build a transaction for deleting the appropriate data
4442 // XXX: this has to come from rule section
4443 if (aAction
== ePrevious
&& isFirst
) {
4444 // we're backspacing from the beginning of the node. Delete the first
4445 // thing to our left
4446 nsCOMPtr
<nsIContent
> priorNode
= GetPriorNode(node
, true);
4447 NS_ENSURE_STATE(priorNode
);
4449 // there is a priorNode, so delete its last child (if chardata, delete the
4450 // last char). if it has no children, delete it
4451 if (priorNode
->IsNodeOfType(nsINode::eDATA_NODE
)) {
4452 nsRefPtr
<nsGenericDOMDataNode
> priorNodeAsCharData
=
4453 static_cast<nsGenericDOMDataNode
*>(priorNode
.get());
4454 uint32_t length
= priorNode
->Length();
4455 // Bail out for empty chardata XXX: Do we want to do something else?
4456 NS_ENSURE_STATE(length
);
4457 nsRefPtr
<DeleteTextTxn
> txn
=
4458 CreateTxnForDeleteCharacter(*priorNodeAsCharData
, length
, ePrevious
);
4459 NS_ENSURE_STATE(txn
);
4461 *aOffset
= txn
->GetOffset();
4462 *aLength
= txn
->GetNumCharsToDelete();
4463 aTxn
->AppendChild(txn
);
4465 // priorNode is not chardata, so tell its parent to delete it
4466 nsRefPtr
<DeleteNodeTxn
> txn
;
4467 res
= CreateTxnForDeleteNode(priorNode
, getter_AddRefs(txn
));
4468 NS_ENSURE_SUCCESS(res
, res
);
4470 aTxn
->AppendChild(txn
);
4473 NS_ADDREF(*aNode
= priorNode
);
4478 if (aAction
== eNext
&& isLast
) {
4479 // we're deleting from the end of the node. Delete the first thing to our
4481 nsCOMPtr
<nsIContent
> nextNode
= GetNextNode(node
, true);
4482 NS_ENSURE_STATE(nextNode
);
4484 // there is a nextNode, so delete its first child (if chardata, delete the
4485 // first char). if it has no children, delete it
4486 if (nextNode
->IsNodeOfType(nsINode::eDATA_NODE
)) {
4487 nsRefPtr
<nsGenericDOMDataNode
> nextNodeAsCharData
=
4488 static_cast<nsGenericDOMDataNode
*>(nextNode
.get());
4489 uint32_t length
= nextNode
->Length();
4490 // Bail out for empty chardata XXX: Do we want to do something else?
4491 NS_ENSURE_STATE(length
);
4492 nsRefPtr
<DeleteTextTxn
> txn
=
4493 CreateTxnForDeleteCharacter(*nextNodeAsCharData
, 0, eNext
);
4494 NS_ENSURE_STATE(txn
);
4496 *aOffset
= txn
->GetOffset();
4497 *aLength
= txn
->GetNumCharsToDelete();
4498 aTxn
->AppendChild(txn
);
4500 // nextNode is not chardata, so tell its parent to delete it
4501 nsRefPtr
<DeleteNodeTxn
> txn
;
4502 res
= CreateTxnForDeleteNode(nextNode
, getter_AddRefs(txn
));
4503 NS_ENSURE_SUCCESS(res
, res
);
4504 aTxn
->AppendChild(txn
);
4507 NS_ADDREF(*aNode
= nextNode
);
4512 if (node
->IsNodeOfType(nsINode::eDATA_NODE
)) {
4513 nsRefPtr
<nsGenericDOMDataNode
> nodeAsCharData
=
4514 static_cast<nsGenericDOMDataNode
*>(node
.get());
4515 // we have chardata, so delete a char at the proper offset
4516 nsRefPtr
<DeleteTextTxn
> txn
= CreateTxnForDeleteCharacter(*nodeAsCharData
,
4518 NS_ENSURE_STATE(txn
);
4520 aTxn
->AppendChild(txn
);
4521 NS_ADDREF(*aNode
= node
);
4522 *aOffset
= txn
->GetOffset();
4523 *aLength
= txn
->GetNumCharsToDelete();
4525 // we're either deleting a node or chardata, need to dig into the next/prev
4527 nsCOMPtr
<nsINode
> selectedNode
;
4528 if (aAction
== ePrevious
) {
4529 selectedNode
= GetPriorNode(node
, offset
, true);
4530 } else if (aAction
== eNext
) {
4531 selectedNode
= GetNextNode(node
, offset
, true);
4534 while (selectedNode
&&
4535 selectedNode
->IsNodeOfType(nsINode::eDATA_NODE
) &&
4536 !selectedNode
->Length()) {
4537 // Can't delete an empty chardata node (bug 762183)
4538 if (aAction
== ePrevious
) {
4539 selectedNode
= GetPriorNode(selectedNode
, true);
4540 } else if (aAction
== eNext
) {
4541 selectedNode
= GetNextNode(selectedNode
, true);
4544 NS_ENSURE_STATE(selectedNode
);
4546 if (selectedNode
->IsNodeOfType(nsINode::eDATA_NODE
)) {
4547 nsRefPtr
<nsGenericDOMDataNode
> selectedNodeAsCharData
=
4548 static_cast<nsGenericDOMDataNode
*>(selectedNode
.get());
4549 // we are deleting from a chardata node, so do a character deletion
4550 uint32_t position
= 0;
4551 if (aAction
== ePrevious
) {
4552 position
= selectedNode
->Length();
4554 nsRefPtr
<DeleteTextTxn
> delTextTxn
=
4555 CreateTxnForDeleteCharacter(*selectedNodeAsCharData
, position
,
4557 NS_ENSURE_TRUE(delTextTxn
, NS_ERROR_NULL_POINTER
);
4559 aTxn
->AppendChild(delTextTxn
);
4560 *aOffset
= delTextTxn
->GetOffset();
4561 *aLength
= delTextTxn
->GetNumCharsToDelete();
4563 nsRefPtr
<DeleteNodeTxn
> delElementTxn
;
4564 res
= CreateTxnForDeleteNode(selectedNode
, getter_AddRefs(delElementTxn
));
4565 NS_ENSURE_SUCCESS(res
, res
);
4566 NS_ENSURE_TRUE(delElementTxn
, NS_ERROR_NULL_POINTER
);
4568 aTxn
->AppendChild(delElementTxn
);
4571 NS_ADDREF(*aNode
= selectedNode
);
4578 nsEditor::CreateRange(nsIDOMNode
*aStartParent
, int32_t aStartOffset
,
4579 nsIDOMNode
*aEndParent
, int32_t aEndOffset
,
4580 nsIDOMRange
**aRange
)
4582 return nsRange::CreateRange(aStartParent
, aStartOffset
, aEndParent
,
4583 aEndOffset
, aRange
);
4587 nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode
*aNode
)
4589 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
4590 nsCOMPtr
<nsISelection
> selection
;
4591 nsresult res
= GetSelection(getter_AddRefs(selection
));
4592 NS_ENSURE_SUCCESS(res
, res
);
4593 if(!selection
) return NS_ERROR_FAILURE
;
4595 nsCOMPtr
<nsIDOMNode
> parentNode
;
4596 res
= aNode
->GetParentNode(getter_AddRefs(parentNode
));
4597 NS_ENSURE_SUCCESS(res
, res
);
4598 NS_ENSURE_TRUE(parentNode
, NS_ERROR_NULL_POINTER
);
4600 int32_t offset
= GetChildOffset(aNode
, parentNode
);
4602 nsCOMPtr
<nsIDOMRange
> range
;
4603 res
= CreateRange(parentNode
, offset
, parentNode
, offset
+1, getter_AddRefs(range
));
4604 NS_ENSURE_SUCCESS(res
, res
);
4605 NS_ENSURE_TRUE(range
, NS_ERROR_NULL_POINTER
);
4607 return selection
->AddRange(range
);
4610 nsresult
nsEditor::ClearSelection()
4612 nsCOMPtr
<nsISelection
> selection
;
4613 nsresult res
= nsEditor::GetSelection(getter_AddRefs(selection
));
4614 NS_ENSURE_SUCCESS(res
, res
);
4615 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
4616 return selection
->RemoveAllRanges();
4619 already_AddRefed
<Element
>
4620 nsEditor::CreateHTMLContent(nsIAtom
* aTag
)
4624 nsCOMPtr
<nsIDocument
> doc
= GetDocument();
4629 // XXX Wallpaper over editor bug (editor tries to create elements with an
4631 if (aTag
== nsGkAtoms::_empty
) {
4632 NS_ERROR("Don't pass an empty tag to nsEditor::CreateHTMLContent, "
4637 nsCOMPtr
<nsIContent
> ret
;
4638 nsresult res
= doc
->CreateElem(nsDependentAtomString(aTag
), nullptr,
4639 kNameSpaceID_XHTML
, getter_AddRefs(ret
));
4640 NS_ENSURE_SUCCESS(res
, nullptr);
4641 return dont_AddRef(ret
.forget().take()->AsElement());
4645 nsEditor::SetAttributeOrEquivalent(nsIDOMElement
* aElement
,
4646 const nsAString
& aAttribute
,
4647 const nsAString
& aValue
,
4648 bool aSuppressTransaction
)
4650 return SetAttribute(aElement
, aAttribute
, aValue
);
4654 nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement
* aElement
,
4655 const nsAString
& aAttribute
,
4656 bool aSuppressTransaction
)
4658 return RemoveAttribute(aElement
, aAttribute
);
4662 nsEditor::HandleKeyPressEvent(nsIDOMKeyEvent
* aKeyEvent
)
4664 // NOTE: When you change this method, you should also change:
4665 // * editor/libeditor/tests/test_texteditor_keyevent_handling.html
4666 // * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
4668 // And also when you add new key handling, you need to change the subclass's
4669 // HandleKeyPressEvent()'s switch statement.
4671 WidgetKeyboardEvent
* nativeKeyEvent
=
4672 aKeyEvent
->GetInternalNSEvent()->AsKeyboardEvent();
4673 NS_ENSURE_TRUE(nativeKeyEvent
, NS_ERROR_UNEXPECTED
);
4674 NS_ASSERTION(nativeKeyEvent
->message
== NS_KEY_PRESS
,
4675 "HandleKeyPressEvent gets non-keypress event");
4677 // if we are readonly or disabled, then do nothing.
4678 if (IsReadonly() || IsDisabled()) {
4679 // consume backspace for disabled and readonly textfields, to prevent
4680 // back in history, which could be confusing to users
4681 if (nativeKeyEvent
->keyCode
== nsIDOMKeyEvent::DOM_VK_BACK_SPACE
) {
4682 aKeyEvent
->PreventDefault();
4687 switch (nativeKeyEvent
->keyCode
) {
4688 case nsIDOMKeyEvent::DOM_VK_META
:
4689 case nsIDOMKeyEvent::DOM_VK_WIN
:
4690 case nsIDOMKeyEvent::DOM_VK_SHIFT
:
4691 case nsIDOMKeyEvent::DOM_VK_CONTROL
:
4692 case nsIDOMKeyEvent::DOM_VK_ALT
:
4693 aKeyEvent
->PreventDefault(); // consumed
4695 case nsIDOMKeyEvent::DOM_VK_BACK_SPACE
:
4696 if (nativeKeyEvent
->IsControl() || nativeKeyEvent
->IsAlt() ||
4697 nativeKeyEvent
->IsMeta() || nativeKeyEvent
->IsOS()) {
4700 DeleteSelection(nsIEditor::ePrevious
, nsIEditor::eStrip
);
4701 aKeyEvent
->PreventDefault(); // consumed
4703 case nsIDOMKeyEvent::DOM_VK_DELETE
:
4704 // on certain platforms (such as windows) the shift key
4705 // modifies what delete does (cmd_cut in this case).
4706 // bailing here to allow the keybindings to do the cut.
4707 if (nativeKeyEvent
->IsShift() || nativeKeyEvent
->IsControl() ||
4708 nativeKeyEvent
->IsAlt() || nativeKeyEvent
->IsMeta() ||
4709 nativeKeyEvent
->IsOS()) {
4712 DeleteSelection(nsIEditor::eNext
, nsIEditor::eStrip
);
4713 aKeyEvent
->PreventDefault(); // consumed
4720 nsEditor::HandleInlineSpellCheck(EditAction action
,
4721 nsISelection
*aSelection
,
4722 nsIDOMNode
*previousSelectedNode
,
4723 int32_t previousSelectedOffset
,
4724 nsIDOMNode
*aStartNode
,
4725 int32_t aStartOffset
,
4726 nsIDOMNode
*aEndNode
,
4729 // Have to cast action here because this method is from an IDL
4730 return mInlineSpellChecker
? mInlineSpellChecker
->SpellCheckAfterEditorChange(
4731 (int32_t)action
, aSelection
,
4732 previousSelectedNode
, previousSelectedOffset
,
4733 aStartNode
, aStartOffset
, aEndNode
,
4738 already_AddRefed
<nsIContent
>
4739 nsEditor::FindSelectionRoot(nsINode
*aNode
)
4741 nsCOMPtr
<nsIContent
> rootContent
= GetRoot();
4742 return rootContent
.forget();
4746 nsEditor::InitializeSelection(nsIDOMEventTarget
* aFocusEventTarget
)
4748 nsCOMPtr
<nsINode
> targetNode
= do_QueryInterface(aFocusEventTarget
);
4749 NS_ENSURE_TRUE(targetNode
, NS_ERROR_INVALID_ARG
);
4750 nsCOMPtr
<nsIContent
> selectionRootContent
= FindSelectionRoot(targetNode
);
4751 if (!selectionRootContent
) {
4756 targetNode
->NodeType() == nsIDOMNode::DOCUMENT_NODE
&&
4757 targetNode
->HasFlag(NODE_IS_EDITABLE
);
4759 nsCOMPtr
<nsISelection
> selection
;
4760 nsresult rv
= GetSelection(getter_AddRefs(selection
));
4761 NS_ENSURE_SUCCESS(rv
, rv
);
4763 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
4764 NS_ENSURE_TRUE(presShell
, NS_ERROR_NOT_INITIALIZED
);
4766 nsCOMPtr
<nsISelectionController
> selCon
;
4767 rv
= GetSelectionController(getter_AddRefs(selCon
));
4768 NS_ENSURE_SUCCESS(rv
, rv
);
4770 nsCOMPtr
<nsISelectionPrivate
> selectionPrivate
=
4771 do_QueryInterface(selection
);
4772 NS_ENSURE_TRUE(selectionPrivate
, NS_ERROR_UNEXPECTED
);
4775 nsRefPtr
<nsCaret
> caret
= presShell
->GetCaret();
4776 NS_ENSURE_TRUE(caret
, NS_ERROR_UNEXPECTED
);
4777 caret
->SetIgnoreUserModify(false);
4778 caret
->SetSelection(selection
);
4779 selCon
->SetCaretReadOnly(IsReadonly());
4780 selCon
->SetCaretEnabled(true);
4783 selCon
->SetDisplaySelection(nsISelectionController::SELECTION_ON
);
4784 selCon
->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL
);
4785 selCon
->RepaintSelection(nsISelectionController::SELECTION_NORMAL
);
4786 // If the computed selection root isn't root content, we should set it
4787 // as selection ancestor limit. However, if that is root element, it means
4788 // there is not limitation of the selection, then, we must set nullptr.
4789 // NOTE: If we set a root element to the ancestor limit, some selection
4790 // methods don't work fine.
4791 if (selectionRootContent
->GetParent()) {
4792 selectionPrivate
->SetAncestorLimiter(selectionRootContent
);
4794 selectionPrivate
->SetAncestorLimiter(nullptr);
4797 // XXX What case needs this?
4800 selection
->GetRangeCount(&rangeCount
);
4801 if (rangeCount
== 0) {
4802 BeginningOfDocument();
4810 nsEditor::FinalizeSelection()
4812 nsCOMPtr
<nsISelectionController
> selCon
;
4813 nsresult rv
= GetSelectionController(getter_AddRefs(selCon
));
4814 NS_ENSURE_SUCCESS_VOID(rv
);
4816 nsCOMPtr
<nsISelection
> selection
;
4817 rv
= selCon
->GetSelection(nsISelectionController::SELECTION_NORMAL
,
4818 getter_AddRefs(selection
));
4819 NS_ENSURE_SUCCESS_VOID(rv
);
4821 nsCOMPtr
<nsISelectionPrivate
> selectionPrivate
= do_QueryInterface(selection
);
4822 NS_ENSURE_TRUE_VOID(selectionPrivate
);
4824 selectionPrivate
->SetAncestorLimiter(nullptr);
4826 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
4827 NS_ENSURE_TRUE_VOID(presShell
);
4829 selCon
->SetCaretEnabled(false);
4831 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
4832 NS_ENSURE_TRUE_VOID(fm
);
4833 fm
->UpdateCaretForCaretBrowsingMode();
4835 if (!HasIndependentSelection()) {
4836 // If this editor doesn't have an independent selection, i.e., it must
4837 // mean that it is an HTML editor, the selection controller is shared with
4838 // presShell. So, even this editor loses focus, other part of the document
4839 // may still have focus.
4840 nsCOMPtr
<nsIDocument
> doc
= GetDocument();
4842 if (!doc
|| !doc
->HasFocus(ret
)) {
4843 // If the document already lost focus, mark the selection as disabled.
4844 selCon
->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED
);
4846 // Otherwise, mark selection as normal because outside of a
4847 // contenteditable element should be selected with normal selection
4848 // color after here.
4849 selCon
->SetDisplaySelection(nsISelectionController::SELECTION_ON
);
4851 } else if (IsFormWidget() || IsPasswordEditor() ||
4852 IsReadonly() || IsDisabled() || IsInputFiltered()) {
4853 // In <input> or <textarea>, the independent selection should be hidden
4854 // while this editor doesn't have focus.
4855 selCon
->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN
);
4857 // Otherwise, although we're not sure how this case happens, the
4858 // independent selection should be marked as disabled.
4859 selCon
->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED
);
4862 selCon
->RepaintSelection(nsISelectionController::SELECTION_NORMAL
);
4870 nsCOMPtr
<nsIDOMElement
> root
;
4872 // Let GetRootElement() do the work
4873 GetRootElement(getter_AddRefs(root
));
4876 return mRootElement
;
4880 nsEditor::GetEditorRoot()
4886 nsEditor::GetExposedRoot()
4888 Element
* rootElement
= GetRoot();
4890 // For plaintext editors, we need to ask the input/textarea element directly.
4891 if (rootElement
&& rootElement
->IsRootOfNativeAnonymousSubtree()) {
4892 rootElement
= rootElement
->GetParent()->AsElement();
4899 nsEditor::DetermineCurrentDirection()
4901 // Get the current root direction from its frame
4902 nsIContent
* rootElement
= GetExposedRoot();
4903 NS_ENSURE_TRUE(rootElement
, NS_ERROR_FAILURE
);
4905 // If we don't have an explicit direction, determine our direction
4906 // from the content's direction
4907 if (!(mFlags
& (nsIPlaintextEditor::eEditorLeftToRight
|
4908 nsIPlaintextEditor::eEditorRightToLeft
))) {
4910 nsIFrame
* frame
= rootElement
->GetPrimaryFrame();
4911 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
4913 // Set the flag here, to enable us to use the same code path below.
4914 // It will be flipped before returning from the function.
4915 if (frame
->StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
4916 mFlags
|= nsIPlaintextEditor::eEditorRightToLeft
;
4918 mFlags
|= nsIPlaintextEditor::eEditorLeftToRight
;
4926 nsEditor::SwitchTextDirection()
4928 // Get the current root direction from its frame
4929 nsIContent
* rootElement
= GetExposedRoot();
4931 nsresult rv
= DetermineCurrentDirection();
4932 NS_ENSURE_SUCCESS(rv
, rv
);
4934 // Apply the opposite direction
4935 if (mFlags
& nsIPlaintextEditor::eEditorRightToLeft
) {
4936 NS_ASSERTION(!(mFlags
& nsIPlaintextEditor::eEditorLeftToRight
),
4937 "Unexpected mutually exclusive flag");
4938 mFlags
&= ~nsIPlaintextEditor::eEditorRightToLeft
;
4939 mFlags
|= nsIPlaintextEditor::eEditorLeftToRight
;
4940 rv
= rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, NS_LITERAL_STRING("ltr"), true);
4941 } else if (mFlags
& nsIPlaintextEditor::eEditorLeftToRight
) {
4942 NS_ASSERTION(!(mFlags
& nsIPlaintextEditor::eEditorRightToLeft
),
4943 "Unexpected mutually exclusive flag");
4944 mFlags
|= nsIPlaintextEditor::eEditorRightToLeft
;
4945 mFlags
&= ~nsIPlaintextEditor::eEditorLeftToRight
;
4946 rv
= rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, NS_LITERAL_STRING("rtl"), true);
4949 if (NS_SUCCEEDED(rv
)) {
4957 nsEditor::SwitchTextDirectionTo(uint32_t aDirection
)
4959 // Get the current root direction from its frame
4960 nsIContent
* rootElement
= GetExposedRoot();
4962 nsresult rv
= DetermineCurrentDirection();
4963 NS_ENSURE_SUCCESS_VOID(rv
);
4965 // Apply the requested direction
4966 if (aDirection
== nsIPlaintextEditor::eEditorLeftToRight
&&
4967 (mFlags
& nsIPlaintextEditor::eEditorRightToLeft
)) {
4968 NS_ASSERTION(!(mFlags
& nsIPlaintextEditor::eEditorLeftToRight
),
4969 "Unexpected mutually exclusive flag");
4970 mFlags
&= ~nsIPlaintextEditor::eEditorRightToLeft
;
4971 mFlags
|= nsIPlaintextEditor::eEditorLeftToRight
;
4972 rv
= rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, NS_LITERAL_STRING("ltr"), true);
4973 } else if (aDirection
== nsIPlaintextEditor::eEditorRightToLeft
&&
4974 (mFlags
& nsIPlaintextEditor::eEditorLeftToRight
)) {
4975 NS_ASSERTION(!(mFlags
& nsIPlaintextEditor::eEditorRightToLeft
),
4976 "Unexpected mutually exclusive flag");
4977 mFlags
|= nsIPlaintextEditor::eEditorRightToLeft
;
4978 mFlags
&= ~nsIPlaintextEditor::eEditorLeftToRight
;
4979 rv
= rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, NS_LITERAL_STRING("rtl"), true);
4982 if (NS_SUCCEEDED(rv
)) {
4989 nsEditor::DumpNode(nsIDOMNode
*aNode
, int32_t indent
)
4992 for (i
=0; i
<indent
; i
++)
4995 nsCOMPtr
<nsIDOMElement
> element
= do_QueryInterface(aNode
);
4996 nsCOMPtr
<nsIDOMDocumentFragment
> docfrag
= do_QueryInterface(aNode
);
4998 if (element
|| docfrag
)
5003 element
->GetTagName(tag
);
5004 printf("<%s>\n", NS_LossyConvertUTF16toASCII(tag
).get());
5008 printf("<document fragment>\n");
5010 nsCOMPtr
<nsIDOMNodeList
> childList
;
5011 aNode
->GetChildNodes(getter_AddRefs(childList
));
5012 NS_ENSURE_TRUE(childList
, NS_ERROR_NULL_POINTER
);
5013 uint32_t numChildren
;
5014 childList
->GetLength(&numChildren
);
5015 nsCOMPtr
<nsIDOMNode
> child
, tmp
;
5016 aNode
->GetFirstChild(getter_AddRefs(child
));
5017 for (i
=0; i
<numChildren
; i
++)
5019 DumpNode(child
, indent
+1);
5020 child
->GetNextSibling(getter_AddRefs(tmp
));
5024 else if (IsTextNode(aNode
))
5026 nsCOMPtr
<nsIDOMCharacterData
> textNode
= do_QueryInterface(aNode
);
5028 textNode
->GetData(str
);
5030 LossyCopyUTF16toASCII(str
, cstr
);
5031 cstr
.ReplaceChar('\n', ' ');
5032 printf("<textnode> %s\n", cstr
.get());
5038 nsEditor::IsModifiableNode(nsIDOMNode
*aNode
)
5044 nsEditor::IsModifiableNode(nsINode
*aNode
)
5049 already_AddRefed
<nsIContent
>
5050 nsEditor::GetFocusedContent()
5052 nsCOMPtr
<nsIDOMEventTarget
> piTarget
= GetDOMEventTarget();
5057 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
5058 NS_ENSURE_TRUE(fm
, nullptr);
5060 nsCOMPtr
<nsIContent
> content
= fm
->GetFocusedContent();
5061 return SameCOMIdentity(content
, piTarget
) ? content
.forget() : nullptr;
5064 already_AddRefed
<nsIContent
>
5065 nsEditor::GetFocusedContentForIME()
5067 return GetFocusedContent();
5071 nsEditor::IsActiveInDOMWindow()
5073 nsCOMPtr
<nsIDOMEventTarget
> piTarget
= GetDOMEventTarget();
5078 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
5079 NS_ENSURE_TRUE(fm
, false);
5081 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
5082 nsPIDOMWindow
* ourWindow
= doc
->GetWindow();
5083 nsCOMPtr
<nsPIDOMWindow
> win
;
5084 nsIContent
* content
=
5085 nsFocusManager::GetFocusedDescendant(ourWindow
, false,
5086 getter_AddRefs(win
));
5087 return SameCOMIdentity(content
, piTarget
);
5091 nsEditor::IsAcceptableInputEvent(nsIDOMEvent
* aEvent
)
5093 // If the event is trusted, the event should always cause input.
5094 NS_ENSURE_TRUE(aEvent
, false);
5096 WidgetEvent
* widgetEvent
= aEvent
->GetInternalNSEvent();
5097 if (NS_WARN_IF(!widgetEvent
)) {
5101 // If this is dispatched by using cordinates but this editor doesn't have
5102 // focus, we shouldn't handle it.
5103 if (widgetEvent
->IsUsingCoordinates()) {
5104 nsCOMPtr
<nsIContent
> focusedContent
= GetFocusedContent();
5105 if (!focusedContent
) {
5110 // If composition event or text event isn't dispatched via widget,
5111 // we need to ignore them since they cannot be managed by TextComposition.
5112 // E.g., the event was created by chrome JS.
5113 // Note that if we allow to handle such events, editor may be confused by
5114 // strange event order.
5115 bool needsWidget
= false;
5116 WidgetGUIEvent
* widgetGUIEvent
= nullptr;
5117 switch (widgetEvent
->message
) {
5118 case NS_USER_DEFINED_EVENT
:
5119 // If events are not created with proper event interface, their message
5120 // are initialized with NS_USER_DEFINED_EVENT. Let's ignore such event.
5123 // Don't allow text events whose internal event are not
5125 widgetGUIEvent
= aEvent
->GetInternalNSEvent()->AsTextEvent();
5128 case NS_COMPOSITION_START
:
5129 case NS_COMPOSITION_END
:
5130 case NS_COMPOSITION_UPDATE
:
5131 // Don't allow composition events whose internal event are not
5132 // WidgetCompositionEvent.
5133 widgetGUIEvent
= aEvent
->GetInternalNSEvent()->AsCompositionEvent();
5140 (!widgetGUIEvent
|| !widgetGUIEvent
->widget
)) {
5144 // Accept all trusted events.
5145 if (widgetEvent
->mFlags
.mIsTrusted
) {
5149 // Ignore untrusted mouse event.
5150 // XXX Why are we handling other untrusted input events?
5151 if (widgetEvent
->AsMouseEventBase()) {
5155 // Otherwise, we shouldn't handle any input events when we're not an active
5156 // element of the DOM window.
5157 return IsActiveInDOMWindow();
5161 nsEditor::OnFocus(nsIDOMEventTarget
* aFocusEventTarget
)
5163 InitializeSelection(aFocusEventTarget
);
5164 if (mInlineSpellChecker
) {
5165 mInlineSpellChecker
->UpdateCurrentDictionary();
5170 nsEditor::GetSuppressDispatchingInputEvent(bool *aSuppressed
)
5172 NS_ENSURE_ARG_POINTER(aSuppressed
);
5173 *aSuppressed
= !mDispatchInputEvent
;
5178 nsEditor::SetSuppressDispatchingInputEvent(bool aSuppress
)
5180 mDispatchInputEvent
= !aSuppress
;
5185 nsEditor::GetIsInEditAction(bool* aIsInEditAction
)
5187 MOZ_ASSERT(aIsInEditAction
, "aIsInEditAction must not be null");
5188 *aIsInEditAction
= mIsInEditAction
;