Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "InsertElementTxn.h" // for InsertElementTxn
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/mozalloc.h" // for operator new, etc
35 #include "nsAString.h" // for nsAString_internal::Length, etc
36 #include "nsCCUncollectableMarker.h" // for nsCCUncollectableMarker
37 #include "nsCaret.h" // for nsCaret
38 #include "nsCaseTreatment.h"
39 #include "nsCharTraits.h" // for NS_IS_HIGH_SURROGATE, etc
40 #include "nsComponentManagerUtils.h" // for do_CreateInstance
41 #include "nsComputedDOMStyle.h" // for nsComputedDOMStyle
42 #include "nsContentUtils.h" // for nsContentUtils
43 #include "nsDOMString.h" // for DOMStringIsNull
44 #include "nsDebug.h" // for NS_ENSURE_TRUE, etc
45 #include "nsEditProperty.h" // for nsEditProperty, etc
46 #include "nsEditor.h"
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;
115 #ifdef DEBUG
116 #include "nsIDOMHTMLDocument.h" // for nsIDOMHTMLDocument
117 #endif
119 using namespace mozilla;
120 using namespace mozilla::dom;
121 using namespace mozilla::widget;
123 // Defined in nsEditorRegistration.cpp
124 extern nsIParserService *sParserService;
126 //---------------------------------------------------------------------------
127 //
128 // nsEditor: base editor class implementation
129 //
130 //---------------------------------------------------------------------------
132 nsEditor::nsEditor()
133 : mPlaceHolderName(nullptr)
134 , mSelState(nullptr)
135 , mPhonetic(nullptr)
136 , mModCount(0)
137 , mFlags(0)
138 , mUpdateCount(0)
139 , mPlaceHolderBatch(0)
140 , mAction(EditAction::none)
141 , mIMETextOffset(0)
142 , mDirection(eNone)
143 , mDocDirtyState(-1)
144 , mSpellcheckCheckboxState(eTriUnset)
145 , mShouldTxnSetSelection(true)
146 , mDidPreDestroy(false)
147 , mDidPostCreate(false)
148 , mDispatchInputEvent(true)
149 {
150 }
152 nsEditor::~nsEditor()
153 {
154 NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?");
156 mTxnMgr = nullptr;
158 delete mPhonetic;
159 }
161 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEditor)
163 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEditor)
164 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
165 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker)
166 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTxnMgr)
167 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMETextNode)
168 NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners)
169 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers)
170 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners)
171 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget)
172 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventListener)
173 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
175 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEditor)
176 nsIDocument* currentDoc =
177 tmp->mRootElement ? tmp->mRootElement->GetCurrentDoc() : nullptr;
178 if (currentDoc &&
179 nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) {
180 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
181 }
182 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
183 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker)
184 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTxnMgr)
185 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMETextNode)
186 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners)
187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorObservers)
188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners)
189 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener)
191 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
193 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor)
194 NS_INTERFACE_MAP_ENTRY(nsIPhonetic)
195 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
196 NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport)
197 NS_INTERFACE_MAP_ENTRY(nsIEditor)
198 NS_INTERFACE_MAP_ENTRY(nsIObserver)
199 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
200 NS_INTERFACE_MAP_END
202 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditor)
203 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor)
206 NS_IMETHODIMP
207 nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
208 nsISelectionController *aSelCon, uint32_t aFlags,
209 const nsAString& aValue)
210 {
211 NS_PRECONDITION(aDoc, "bad arg");
212 if (!aDoc)
213 return NS_ERROR_NULL_POINTER;
215 // First only set flags, but other stuff shouldn't be initialized now.
216 // Don't move this call after initializing mDocWeak.
217 // SetFlags() can check whether it's called during initialization or not by
218 // them. Note that SetFlags() will be called by PostCreate().
219 #ifdef DEBUG
220 nsresult rv =
221 #endif
222 SetFlags(aFlags);
223 NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
225 mDocWeak = do_GetWeakReference(aDoc); // weak reference to doc
226 // HTML editors currently don't have their own selection controller,
227 // so they'll pass null as aSelCon, and we'll get the selection controller
228 // off of the presshell.
229 nsCOMPtr<nsISelectionController> selCon;
230 if (aSelCon) {
231 mSelConWeak = do_GetWeakReference(aSelCon); // weak reference to selectioncontroller
232 selCon = aSelCon;
233 } else {
234 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
235 selCon = do_QueryInterface(presShell);
236 }
237 NS_ASSERTION(selCon, "Selection controller should be available at this point");
239 //set up root element if we are passed one.
240 if (aRoot)
241 mRootElement = do_QueryInterface(aRoot);
243 mUpdateCount=0;
245 /* initialize IME stuff */
246 mIMETextNode = nullptr;
247 mIMETextOffset = 0;
248 /* Show the caret */
249 selCon->SetCaretReadOnly(false);
250 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
252 selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user
254 NS_POSTCONDITION(mDocWeak, "bad state");
256 // Make sure that the editor will be destroyed properly
257 mDidPreDestroy = false;
258 // Make sure that the ediotr will be created properly
259 mDidPostCreate = false;
261 return NS_OK;
262 }
265 NS_IMETHODIMP
266 nsEditor::PostCreate()
267 {
268 // Synchronize some stuff for the flags. SetFlags() will initialize
269 // something by the flag difference. This is first time of that, so, all
270 // initializations must be run. For such reason, we need to invert mFlags
271 // value first.
272 mFlags = ~mFlags;
273 nsresult rv = SetFlags(~mFlags);
274 NS_ENSURE_SUCCESS(rv, rv);
276 // These operations only need to happen on the first PostCreate call
277 if (!mDidPostCreate) {
278 mDidPostCreate = true;
280 // Set up listeners
281 CreateEventListeners();
282 rv = InstallEventListeners();
283 NS_ENSURE_SUCCESS(rv, rv);
285 // nuke the modification count, so the doc appears unmodified
286 // do this before we notify listeners
287 ResetModificationCount();
289 // update the UI with our state
290 NotifyDocumentListeners(eDocumentCreated);
291 NotifyDocumentListeners(eDocumentStateChanged);
293 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
294 if (obs) {
295 obs->AddObserver(this,
296 SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION,
297 false);
298 }
299 }
301 // update nsTextStateManager and caret if we have focus
302 nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
303 if (focusedContent) {
304 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(focusedContent);
305 if (target) {
306 InitializeSelection(target);
307 }
309 // If the text control gets reframed during focus, Focus() would not be
310 // called, so take a chance here to see if we need to spell check the text
311 // control.
312 nsEditorEventListener* listener =
313 reinterpret_cast<nsEditorEventListener*> (mEventListener.get());
314 listener->SpellCheckIfNeeded();
316 IMEState newState;
317 rv = GetPreferredIMEState(&newState);
318 NS_ENSURE_SUCCESS(rv, NS_OK);
319 nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
320 IMEStateManager::UpdateIMEState(newState, content);
321 }
322 return NS_OK;
323 }
325 /* virtual */
326 void
327 nsEditor::CreateEventListeners()
328 {
329 // Don't create the handler twice
330 if (!mEventListener) {
331 mEventListener = new nsEditorEventListener();
332 }
333 }
335 nsresult
336 nsEditor::InstallEventListeners()
337 {
338 NS_ENSURE_TRUE(mDocWeak && mEventListener,
339 NS_ERROR_NOT_INITIALIZED);
341 // Initialize the event target.
342 nsCOMPtr<nsIContent> rootContent = GetRoot();
343 NS_ENSURE_TRUE(rootContent, NS_ERROR_NOT_AVAILABLE);
344 mEventTarget = do_QueryInterface(rootContent->GetParent());
345 NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_AVAILABLE);
347 nsEditorEventListener* listener =
348 reinterpret_cast<nsEditorEventListener*>(mEventListener.get());
349 return listener->Connect(this);
350 }
352 void
353 nsEditor::RemoveEventListeners()
354 {
355 if (!mDocWeak || !mEventListener) {
356 return;
357 }
358 reinterpret_cast<nsEditorEventListener*>(mEventListener.get())->Disconnect();
359 if (mComposition) {
360 mComposition->EndHandlingComposition(this);
361 mComposition = nullptr;
362 }
363 mEventTarget = nullptr;
364 }
366 bool
367 nsEditor::GetDesiredSpellCheckState()
368 {
369 // Check user override on this element
370 if (mSpellcheckCheckboxState != eTriUnset) {
371 return (mSpellcheckCheckboxState == eTriTrue);
372 }
374 // Check user preferences
375 int32_t spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1);
377 if (spellcheckLevel == 0) {
378 return false; // Spellchecking forced off globally
379 }
381 if (!CanEnableSpellCheck()) {
382 return false;
383 }
385 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
386 if (presShell) {
387 nsPresContext* context = presShell->GetPresContext();
388 if (context && !context->IsDynamic()) {
389 return false;
390 }
391 }
393 // Check DOM state
394 nsCOMPtr<nsIContent> content = GetExposedRoot();
395 if (!content) {
396 return false;
397 }
399 nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(content);
400 if (!element) {
401 return false;
402 }
404 if (!IsPlaintextEditor()) {
405 // Some of the page content might be editable and some not, if spellcheck=
406 // is explicitly set anywhere, so if there's anything editable on the page,
407 // return true and let the spellchecker figure it out.
408 nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(content->GetCurrentDoc());
409 return doc && doc->IsEditingOn();
410 }
412 bool enable;
413 element->GetSpellcheck(&enable);
415 return enable;
416 }
418 NS_IMETHODIMP
419 nsEditor::PreDestroy(bool aDestroyingFrames)
420 {
421 if (mDidPreDestroy)
422 return NS_OK;
424 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
425 if (obs) {
426 obs->RemoveObserver(this,
427 SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION);
428 }
430 // Let spellchecker clean up its observers etc. It is important not to
431 // actually free the spellchecker here, since the spellchecker could have
432 // caused flush notifications, which could have gotten here if a textbox
433 // is being removed. Setting the spellchecker to nullptr could free the
434 // object that is still in use! It will be freed when the editor is
435 // destroyed.
436 if (mInlineSpellChecker)
437 mInlineSpellChecker->Cleanup(aDestroyingFrames);
439 // tell our listeners that the doc is going away
440 NotifyDocumentListeners(eDocumentToBeDestroyed);
442 // Unregister event listeners
443 RemoveEventListeners();
444 mActionListeners.Clear();
445 mEditorObservers.Clear();
446 mDocStateListeners.Clear();
447 mInlineSpellChecker = nullptr;
448 mSpellcheckCheckboxState = eTriUnset;
449 mRootElement = nullptr;
451 mDidPreDestroy = true;
452 return NS_OK;
453 }
455 NS_IMETHODIMP
456 nsEditor::GetFlags(uint32_t *aFlags)
457 {
458 *aFlags = mFlags;
459 return NS_OK;
460 }
462 NS_IMETHODIMP
463 nsEditor::SetFlags(uint32_t aFlags)
464 {
465 if (mFlags == aFlags) {
466 return NS_OK;
467 }
469 bool spellcheckerWasEnabled = CanEnableSpellCheck();
470 mFlags = aFlags;
472 if (!mDocWeak) {
473 // If we're initializing, we shouldn't do anything now.
474 // SetFlags() will be called by PostCreate(),
475 // we should synchronize some stuff for the flags at that time.
476 return NS_OK;
477 }
479 // The flag change may cause the spellchecker state change
480 if (CanEnableSpellCheck() != spellcheckerWasEnabled) {
481 nsresult rv = SyncRealTimeSpell();
482 NS_ENSURE_SUCCESS(rv, rv);
483 }
485 // If this is called from PostCreate(), it will update the IME state if it's
486 // necessary.
487 if (!mDidPostCreate) {
488 return NS_OK;
489 }
491 // Might be changing editable state, so, we need to reset current IME state
492 // if we're focused and the flag change causes IME state change.
493 nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
494 if (focusedContent) {
495 IMEState newState;
496 nsresult rv = GetPreferredIMEState(&newState);
497 if (NS_SUCCEEDED(rv)) {
498 // NOTE: When the enabled state isn't going to be modified, this method
499 // is going to do nothing.
500 nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
501 IMEStateManager::UpdateIMEState(newState, content);
502 }
503 }
505 return NS_OK;
506 }
508 NS_IMETHODIMP
509 nsEditor::GetIsSelectionEditable(bool *aIsSelectionEditable)
510 {
511 NS_ENSURE_ARG_POINTER(aIsSelectionEditable);
513 // get current selection
514 nsCOMPtr<nsISelection> selection;
515 nsresult res = GetSelection(getter_AddRefs(selection));
516 NS_ENSURE_SUCCESS(res, res);
517 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
519 // XXX we just check that the anchor node is editable at the moment
520 // we should check that all nodes in the selection are editable
521 nsCOMPtr<nsIDOMNode> anchorNode;
522 selection->GetAnchorNode(getter_AddRefs(anchorNode));
523 *aIsSelectionEditable = anchorNode && IsEditable(anchorNode);
525 return NS_OK;
526 }
528 NS_IMETHODIMP
529 nsEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
530 {
531 NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
532 nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
533 *aIsDocumentEditable = !!doc;
535 return NS_OK;
536 }
538 already_AddRefed<nsIDocument>
539 nsEditor::GetDocument()
540 {
541 NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
542 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
543 return doc.forget();
544 }
546 already_AddRefed<nsIDOMDocument>
547 nsEditor::GetDOMDocument()
548 {
549 NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
550 nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
551 return doc.forget();
552 }
554 NS_IMETHODIMP
555 nsEditor::GetDocument(nsIDOMDocument **aDoc)
556 {
557 nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
558 doc.forget(aDoc);
559 return *aDoc ? NS_OK : NS_ERROR_NOT_INITIALIZED;
560 }
562 already_AddRefed<nsIPresShell>
563 nsEditor::GetPresShell()
564 {
565 NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
566 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
567 NS_ENSURE_TRUE(doc, nullptr);
568 nsCOMPtr<nsIPresShell> ps = doc->GetShell();
569 return ps.forget();
570 }
572 already_AddRefed<nsIWidget>
573 nsEditor::GetWidget()
574 {
575 nsCOMPtr<nsIPresShell> ps = GetPresShell();
576 NS_ENSURE_TRUE(ps, nullptr);
577 nsPresContext* pc = ps->GetPresContext();
578 NS_ENSURE_TRUE(pc, nullptr);
579 nsCOMPtr<nsIWidget> widget = pc->GetRootWidget();
580 NS_ENSURE_TRUE(widget.get(), nullptr);
581 return widget.forget();
582 }
584 /* attribute string contentsMIMEType; */
585 NS_IMETHODIMP
586 nsEditor::GetContentsMIMEType(char * *aContentsMIMEType)
587 {
588 NS_ENSURE_ARG_POINTER(aContentsMIMEType);
589 *aContentsMIMEType = ToNewCString(mContentMIMEType);
590 return NS_OK;
591 }
593 NS_IMETHODIMP
594 nsEditor::SetContentsMIMEType(const char * aContentsMIMEType)
595 {
596 mContentMIMEType.Assign(aContentsMIMEType ? aContentsMIMEType : "");
597 return NS_OK;
598 }
600 NS_IMETHODIMP
601 nsEditor::GetSelectionController(nsISelectionController **aSel)
602 {
603 NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER);
604 *aSel = nullptr; // init out param
605 nsCOMPtr<nsISelectionController> selCon;
606 if (mSelConWeak) {
607 selCon = do_QueryReferent(mSelConWeak);
608 } else {
609 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
610 selCon = do_QueryInterface(presShell);
611 }
612 NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
613 NS_ADDREF(*aSel = selCon);
614 return NS_OK;
615 }
618 NS_IMETHODIMP
619 nsEditor::DeleteSelection(EDirection aAction, EStripWrappers aStripWrappers)
620 {
621 MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
622 return DeleteSelectionImpl(aAction, aStripWrappers);
623 }
627 NS_IMETHODIMP
628 nsEditor::GetSelection(nsISelection **aSelection)
629 {
630 NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
631 *aSelection = nullptr;
632 nsCOMPtr<nsISelectionController> selcon;
633 GetSelectionController(getter_AddRefs(selcon));
634 NS_ENSURE_TRUE(selcon, NS_ERROR_NOT_INITIALIZED);
635 return selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); // does an addref
636 }
638 Selection*
639 nsEditor::GetSelection()
640 {
641 nsCOMPtr<nsISelection> sel;
642 nsresult res = GetSelection(getter_AddRefs(sel));
643 NS_ENSURE_SUCCESS(res, nullptr);
645 return static_cast<Selection*>(sel.get());
646 }
648 NS_IMETHODIMP
649 nsEditor::DoTransaction(nsITransaction* aTxn)
650 {
651 if (mPlaceHolderBatch && !mPlaceHolderTxn) {
652 nsCOMPtr<nsIAbsorbingTransaction> plcTxn = new PlaceholderTxn();
654 // save off weak reference to placeholder txn
655 mPlaceHolderTxn = do_GetWeakReference(plcTxn);
656 plcTxn->Init(mPlaceHolderName, mSelState, this);
657 // placeholder txn took ownership of this pointer
658 mSelState = nullptr;
660 // QI to an nsITransaction since that's what DoTransaction() expects
661 nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
662 // we will recurse, but will not hit this case in the nested call
663 DoTransaction(theTxn);
665 if (mTxnMgr) {
666 nsCOMPtr<nsITransaction> topTxn = mTxnMgr->PeekUndoStack();
667 if (topTxn) {
668 plcTxn = do_QueryInterface(topTxn);
669 if (plcTxn) {
670 // there is a placeholder transaction on top of the undo stack. It
671 // is either the one we just created, or an earlier one that we are
672 // now merging into. From here on out remember this placeholder
673 // instead of the one we just created.
674 mPlaceHolderTxn = do_GetWeakReference(plcTxn);
675 }
676 }
677 }
678 }
680 if (aTxn) {
681 // XXX: Why are we doing selection specific batching stuff here?
682 // XXX: Most entry points into the editor have auto variables that
683 // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
684 // XXX: these selection batch calls no-ops.
685 // XXX:
686 // XXX: I suspect that this was placed here to avoid multiple
687 // XXX: selection changed notifications from happening until after
688 // XXX: the transaction was done. I suppose that can still happen
689 // XXX: if an embedding application called DoTransaction() directly
690 // XXX: to pump its own transactions through the system, but in that
691 // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
692 // XXX: its auto equivalent nsAutoUpdateViewBatch to ensure that
693 // XXX: selection listeners have access to accurate frame data?
694 // XXX:
695 // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
696 // XXX: we will need to make sure that they are disabled during
697 // XXX: the init of the editor for text widgets to avoid layout
698 // XXX: re-entry during initial reflow. - kin
700 // get the selection and start a batch change
701 nsRefPtr<Selection> selection = GetSelection();
702 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
704 selection->StartBatchChanges();
706 nsresult res;
707 if (mTxnMgr) {
708 res = mTxnMgr->DoTransaction(aTxn);
709 } else {
710 res = aTxn->DoTransaction();
711 }
712 if (NS_SUCCEEDED(res)) {
713 DoAfterDoTransaction(aTxn);
714 }
716 // no need to check res here, don't lose result of operation
717 selection->EndBatchChanges();
719 NS_ENSURE_SUCCESS(res, res);
720 }
722 return NS_OK;
723 }
726 NS_IMETHODIMP
727 nsEditor::EnableUndo(bool aEnable)
728 {
729 if (aEnable) {
730 if (!mTxnMgr) {
731 mTxnMgr = new nsTransactionManager();
732 }
733 mTxnMgr->SetMaxTransactionCount(-1);
734 } else if (mTxnMgr) {
735 // disable the transaction manager if it is enabled
736 mTxnMgr->Clear();
737 mTxnMgr->SetMaxTransactionCount(0);
738 }
740 return NS_OK;
741 }
743 NS_IMETHODIMP
744 nsEditor::GetNumberOfUndoItems(int32_t* aNumItems)
745 {
746 *aNumItems = 0;
747 return mTxnMgr ? mTxnMgr->GetNumberOfUndoItems(aNumItems) : NS_OK;
748 }
750 NS_IMETHODIMP
751 nsEditor::GetNumberOfRedoItems(int32_t* aNumItems)
752 {
753 *aNumItems = 0;
754 return mTxnMgr ? mTxnMgr->GetNumberOfRedoItems(aNumItems) : NS_OK;
755 }
757 NS_IMETHODIMP
758 nsEditor::GetTransactionManager(nsITransactionManager* *aTxnManager)
759 {
760 NS_ENSURE_ARG_POINTER(aTxnManager);
762 *aTxnManager = nullptr;
763 NS_ENSURE_TRUE(mTxnMgr, NS_ERROR_FAILURE);
765 NS_ADDREF(*aTxnManager = mTxnMgr);
766 return NS_OK;
767 }
769 NS_IMETHODIMP
770 nsEditor::SetTransactionManager(nsITransactionManager *aTxnManager)
771 {
772 NS_ENSURE_TRUE(aTxnManager, NS_ERROR_FAILURE);
774 // nsITransactionManager is builtinclass, so this is safe
775 mTxnMgr = static_cast<nsTransactionManager*>(aTxnManager);
776 return NS_OK;
777 }
779 NS_IMETHODIMP
780 nsEditor::Undo(uint32_t aCount)
781 {
782 ForceCompositionEnd();
784 bool hasTxnMgr, hasTransaction = false;
785 CanUndo(&hasTxnMgr, &hasTransaction);
786 NS_ENSURE_TRUE(hasTransaction, NS_OK);
788 nsAutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone);
790 if (!mTxnMgr) {
791 return NS_OK;
792 }
794 for (uint32_t i = 0; i < aCount; ++i) {
795 nsresult rv = mTxnMgr->UndoTransaction();
796 NS_ENSURE_SUCCESS(rv, rv);
798 DoAfterUndoTransaction();
799 }
801 return NS_OK;
802 }
805 NS_IMETHODIMP nsEditor::CanUndo(bool *aIsEnabled, bool *aCanUndo)
806 {
807 NS_ENSURE_TRUE(aIsEnabled && aCanUndo, NS_ERROR_NULL_POINTER);
808 *aIsEnabled = !!mTxnMgr;
809 if (*aIsEnabled) {
810 int32_t numTxns = 0;
811 mTxnMgr->GetNumberOfUndoItems(&numTxns);
812 *aCanUndo = !!numTxns;
813 } else {
814 *aCanUndo = false;
815 }
816 return NS_OK;
817 }
820 NS_IMETHODIMP
821 nsEditor::Redo(uint32_t aCount)
822 {
823 bool hasTxnMgr, hasTransaction = false;
824 CanRedo(&hasTxnMgr, &hasTransaction);
825 NS_ENSURE_TRUE(hasTransaction, NS_OK);
827 nsAutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone);
829 if (!mTxnMgr) {
830 return NS_OK;
831 }
833 for (uint32_t i = 0; i < aCount; ++i) {
834 nsresult rv = mTxnMgr->RedoTransaction();
835 NS_ENSURE_SUCCESS(rv, rv);
837 DoAfterRedoTransaction();
838 }
840 return NS_OK;
841 }
844 NS_IMETHODIMP nsEditor::CanRedo(bool *aIsEnabled, bool *aCanRedo)
845 {
846 NS_ENSURE_TRUE(aIsEnabled && aCanRedo, NS_ERROR_NULL_POINTER);
848 *aIsEnabled = !!mTxnMgr;
849 if (*aIsEnabled) {
850 int32_t numTxns = 0;
851 mTxnMgr->GetNumberOfRedoItems(&numTxns);
852 *aCanRedo = !!numTxns;
853 } else {
854 *aCanRedo = false;
855 }
856 return NS_OK;
857 }
860 NS_IMETHODIMP
861 nsEditor::BeginTransaction()
862 {
863 BeginUpdateViewBatch();
865 if (mTxnMgr) {
866 mTxnMgr->BeginBatch(nullptr);
867 }
869 return NS_OK;
870 }
872 NS_IMETHODIMP
873 nsEditor::EndTransaction()
874 {
875 if (mTxnMgr) {
876 mTxnMgr->EndBatch(false);
877 }
879 EndUpdateViewBatch();
881 return NS_OK;
882 }
885 // These two routines are similar to the above, but do not use
886 // the transaction managers batching feature. Instead we use
887 // a placeholder transaction to wrap up any further transaction
888 // while the batch is open. The advantage of this is that
889 // placeholder transactions can later merge, if needed. Merging
890 // is unavailable between transaction manager batches.
892 NS_IMETHODIMP
893 nsEditor::BeginPlaceHolderTransaction(nsIAtom *aName)
894 {
895 NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!");
896 if (!mPlaceHolderBatch)
897 {
898 // time to turn on the batch
899 BeginUpdateViewBatch();
900 mPlaceHolderTxn = nullptr;
901 mPlaceHolderName = aName;
902 nsRefPtr<Selection> selection = GetSelection();
903 if (selection) {
904 mSelState = new nsSelectionState();
905 mSelState->SaveSelection(selection);
906 }
907 }
908 mPlaceHolderBatch++;
910 return NS_OK;
911 }
913 NS_IMETHODIMP
914 nsEditor::EndPlaceHolderTransaction()
915 {
916 NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!");
917 if (mPlaceHolderBatch == 1)
918 {
919 nsCOMPtr<nsISelection>selection;
920 GetSelection(getter_AddRefs(selection));
922 nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
924 // By making the assumption that no reflow happens during the calls
925 // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to
926 // allow the selection to cache a frame offset which is used by the
927 // caret drawing code. We only enable this cache here; at other times,
928 // we have no way to know whether reflow invalidates it
929 // See bugs 35296 and 199412.
930 if (selPrivate) {
931 selPrivate->SetCanCacheFrameOffset(true);
932 }
934 {
935 // Hide the caret here to avoid hiding it twice, once in EndUpdateViewBatch
936 // and once in ScrollSelectionIntoView.
937 nsRefPtr<nsCaret> caret;
938 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
940 if (presShell)
941 caret = presShell->GetCaret();
943 // time to turn off the batch
944 EndUpdateViewBatch();
945 // make sure selection is in view
947 // After ScrollSelectionIntoView(), the pending notifications might be
948 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
949 ScrollSelectionIntoView(false);
950 }
952 // cached for frame offset are Not available now
953 if (selPrivate) {
954 selPrivate->SetCanCacheFrameOffset(false);
955 }
957 if (mSelState)
958 {
959 // we saved the selection state, but never got to hand it to placeholder
960 // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
961 delete mSelState;
962 mSelState = nullptr;
963 }
964 if (mPlaceHolderTxn) // we might have never made a placeholder if no action took place
965 {
966 nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
967 if (plcTxn)
968 {
969 plcTxn->EndPlaceHolderBatch();
970 }
971 else
972 {
973 // in the future we will check to make sure undo is off here,
974 // since that is the only known case where the placeholdertxn would disappear on us.
975 // For now just removing the assert.
976 }
977 // notify editor observers of action but if composing, it's done by
978 // text event handler.
979 if (!mComposition) {
980 NotifyEditorObservers();
981 }
982 }
983 }
984 mPlaceHolderBatch--;
986 return NS_OK;
987 }
989 NS_IMETHODIMP
990 nsEditor::ShouldTxnSetSelection(bool *aResult)
991 {
992 NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
993 *aResult = mShouldTxnSetSelection;
994 return NS_OK;
995 }
997 NS_IMETHODIMP
998 nsEditor::SetShouldTxnSetSelection(bool aShould)
999 {
1000 mShouldTxnSetSelection = aShould;
1001 return NS_OK;
1002 }
1004 NS_IMETHODIMP
1005 nsEditor::GetDocumentIsEmpty(bool *aDocumentIsEmpty)
1006 {
1007 *aDocumentIsEmpty = true;
1009 dom::Element* root = GetRoot();
1010 NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
1012 *aDocumentIsEmpty = !root->HasChildren();
1013 return NS_OK;
1014 }
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()
1019 {
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)
1029 {
1030 result = SelectEntireDocument(selection);
1031 }
1032 return result;
1033 }
1035 NS_IMETHODIMP nsEditor::BeginningOfDocument()
1036 {
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);
1051 if (!firstNode) {
1052 // just the root node, set selection to inside the root
1053 return selection->CollapseNative(rootElement, 0);
1054 }
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);
1059 }
1061 // Otherwise, it's a leaf node and we set the selection just in front of it.
1062 nsCOMPtr<nsIContent> parent = firstNode->GetParent();
1063 if (!parent) {
1064 return NS_ERROR_NULL_POINTER;
1065 }
1067 int32_t offsetInParent = parent->IndexOf(firstNode);
1068 return selection->CollapseNative(parent, offsetInParent);
1069 }
1071 NS_IMETHODIMP
1072 nsEditor::EndOfDocument()
1073 {
1074 NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
1076 // get selection
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())) {
1088 node = child;
1089 child = node->GetLastChild();
1090 }
1092 uint32_t length = node->Length();
1093 return selection->CollapseNative(node, int32_t(length));
1094 }
1096 NS_IMETHODIMP
1097 nsEditor::GetDocumentModified(bool *outDocModified)
1098 {
1099 NS_ENSURE_TRUE(outDocModified, NS_ERROR_NULL_POINTER);
1101 int32_t modCount = 0;
1102 GetModificationCount(&modCount);
1104 *outDocModified = (modCount != 0);
1105 return NS_OK;
1106 }
1108 NS_IMETHODIMP
1109 nsEditor::GetDocumentCharacterSet(nsACString &characterSet)
1110 {
1111 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
1112 NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
1114 characterSet = doc->GetDocumentCharacterSet();
1115 return NS_OK;
1116 }
1118 NS_IMETHODIMP
1119 nsEditor::SetDocumentCharacterSet(const nsACString& characterSet)
1120 {
1121 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
1122 NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
1124 doc->SetDocumentCharacterSet(characterSet);
1125 return NS_OK;
1126 }
1128 NS_IMETHODIMP
1129 nsEditor::Cut()
1130 {
1131 return NS_ERROR_NOT_IMPLEMENTED;
1132 }
1134 NS_IMETHODIMP
1135 nsEditor::CanCut(bool *aCanCut)
1136 {
1137 return NS_ERROR_NOT_IMPLEMENTED;
1138 }
1140 NS_IMETHODIMP
1141 nsEditor::Copy()
1142 {
1143 return NS_ERROR_NOT_IMPLEMENTED;
1144 }
1146 NS_IMETHODIMP
1147 nsEditor::CanCopy(bool *aCanCut)
1148 {
1149 return NS_ERROR_NOT_IMPLEMENTED;
1150 }
1152 NS_IMETHODIMP
1153 nsEditor::Paste(int32_t aSelectionType)
1154 {
1155 return NS_ERROR_NOT_IMPLEMENTED;
1156 }
1158 NS_IMETHODIMP
1159 nsEditor::PasteTransferable(nsITransferable *aTransferable)
1160 {
1161 return NS_ERROR_NOT_IMPLEMENTED;
1162 }
1164 NS_IMETHODIMP
1165 nsEditor::CanPaste(int32_t aSelectionType, bool *aCanPaste)
1166 {
1167 return NS_ERROR_NOT_IMPLEMENTED;
1168 }
1170 NS_IMETHODIMP
1171 nsEditor::CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste)
1172 {
1173 return NS_ERROR_NOT_IMPLEMENTED;
1174 }
1176 NS_IMETHODIMP
1177 nsEditor::SetAttribute(nsIDOMElement *aElement, const nsAString & aAttribute, const nsAString & aValue)
1178 {
1179 nsRefPtr<ChangeAttributeTxn> txn;
1180 nsresult result = CreateTxnForSetAttribute(aElement, aAttribute, aValue,
1181 getter_AddRefs(txn));
1182 if (NS_SUCCEEDED(result)) {
1183 result = DoTransaction(txn);
1184 }
1185 return result;
1186 }
1188 NS_IMETHODIMP
1189 nsEditor::GetAttributeValue(nsIDOMElement *aElement,
1190 const nsAString & aAttribute,
1191 nsAString & aResultValue,
1192 bool *aResultIsSet)
1193 {
1194 NS_ENSURE_TRUE(aResultIsSet, NS_ERROR_NULL_POINTER);
1195 *aResultIsSet = false;
1196 if (!aElement) {
1197 return NS_OK;
1198 }
1199 nsAutoString value;
1200 nsresult rv = aElement->GetAttribute(aAttribute, value);
1201 NS_ENSURE_SUCCESS(rv, rv);
1202 if (!DOMStringIsNull(value)) {
1203 *aResultIsSet = true;
1204 aResultValue = value;
1205 }
1206 return rv;
1207 }
1209 NS_IMETHODIMP
1210 nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsAString& aAttribute)
1211 {
1212 nsRefPtr<ChangeAttributeTxn> txn;
1213 nsresult result = CreateTxnForRemoveAttribute(aElement, aAttribute,
1214 getter_AddRefs(txn));
1215 if (NS_SUCCEEDED(result)) {
1216 result = DoTransaction(txn);
1217 }
1218 return result;
1219 }
1222 bool
1223 nsEditor::OutputsMozDirty()
1224 {
1225 // Return true for Composer (!eEditorAllowInteraction) or mail
1226 // (eEditorMailMask), but false for webpages.
1227 return !(mFlags & nsIPlaintextEditor::eEditorAllowInteraction) ||
1228 (mFlags & nsIPlaintextEditor::eEditorMailMask);
1229 }
1232 NS_IMETHODIMP
1233 nsEditor::MarkNodeDirty(nsIDOMNode* aNode)
1234 {
1235 // Mark the node dirty, but not for webpages (bug 599983)
1236 if (!OutputsMozDirty()) {
1237 return NS_OK;
1238 }
1239 nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
1240 if (element) {
1241 element->SetAttr(kNameSpaceID_None, nsEditProperty::mozdirty,
1242 EmptyString(), false);
1243 }
1244 return NS_OK;
1245 }
1247 NS_IMETHODIMP nsEditor::GetInlineSpellChecker(bool autoCreate,
1248 nsIInlineSpellChecker ** aInlineSpellChecker)
1249 {
1250 NS_ENSURE_ARG_POINTER(aInlineSpellChecker);
1252 if (mDidPreDestroy) {
1253 // Don't allow people to get or create the spell checker once the editor
1254 // is going away.
1255 *aInlineSpellChecker = nullptr;
1256 return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK;
1257 }
1259 // We don't want to show the spell checking UI if there are no spell check dictionaries available.
1260 bool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking();
1261 if (!canSpell) {
1262 *aInlineSpellChecker = nullptr;
1263 return NS_ERROR_FAILURE;
1264 }
1266 nsresult rv;
1267 if (!mInlineSpellChecker && autoCreate) {
1268 mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv);
1269 NS_ENSURE_SUCCESS(rv, rv);
1270 }
1272 if (mInlineSpellChecker) {
1273 rv = mInlineSpellChecker->Init(this);
1274 if (NS_FAILED(rv))
1275 mInlineSpellChecker = nullptr;
1276 NS_ENSURE_SUCCESS(rv, rv);
1277 }
1279 NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
1281 return NS_OK;
1282 }
1284 NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic,
1285 const char16_t *aData)
1286 {
1287 NS_ASSERTION(!strcmp(aTopic,
1288 SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION),
1289 "Unexpected observer topic");
1291 // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes
1292 SyncRealTimeSpell();
1294 // When nsIEditorSpellCheck::GetCurrentDictionary changes
1295 if (mInlineSpellChecker) {
1296 // if the current dictionary is no longer available, find another one
1297 nsCOMPtr<nsIEditorSpellCheck> editorSpellCheck;
1298 mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck));
1299 if (editorSpellCheck) {
1300 // Note: This might change the current dictionary, which may call
1301 // this observer recursively.
1302 editorSpellCheck->CheckCurrentDictionary();
1303 }
1305 // update the inline spell checker to reflect the new current dictionary
1306 mInlineSpellChecker->SpellCheckRange(nullptr); // causes recheck
1307 }
1309 return NS_OK;
1310 }
1312 NS_IMETHODIMP nsEditor::SyncRealTimeSpell()
1313 {
1314 bool enable = GetDesiredSpellCheckState();
1316 // Initializes mInlineSpellChecker
1317 nsCOMPtr<nsIInlineSpellChecker> spellChecker;
1318 GetInlineSpellChecker(enable, getter_AddRefs(spellChecker));
1320 if (mInlineSpellChecker) {
1321 // We might have a mInlineSpellChecker even if there are no dictionaries
1322 // available since we don't destroy the mInlineSpellChecker when the last
1323 // dictionariy is removed, but in that case spellChecker is null
1324 mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker);
1325 }
1327 return NS_OK;
1328 }
1330 NS_IMETHODIMP nsEditor::SetSpellcheckUserOverride(bool enable)
1331 {
1332 mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
1334 return SyncRealTimeSpell();
1335 }
1337 NS_IMETHODIMP nsEditor::CreateNode(const nsAString& aTag,
1338 nsIDOMNode * aParent,
1339 int32_t aPosition,
1340 nsIDOMNode ** aNewNode)
1341 {
1342 int32_t i;
1344 nsAutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
1346 for (i = 0; i < mActionListeners.Count(); i++)
1347 mActionListeners[i]->WillCreateNode(aTag, aParent, aPosition);
1349 nsRefPtr<CreateElementTxn> txn;
1350 nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition,
1351 getter_AddRefs(txn));
1352 if (NS_SUCCEEDED(result))
1353 {
1354 result = DoTransaction(txn);
1355 if (NS_SUCCEEDED(result))
1356 {
1357 result = txn->GetNewNode(aNewNode);
1358 NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::DoTransaction succeeded.");
1359 }
1360 }
1362 mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
1364 for (i = 0; i < mActionListeners.Count(); i++)
1365 mActionListeners[i]->DidCreateNode(aTag, *aNewNode, aParent, aPosition, result);
1367 return result;
1368 }
1371 NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
1372 nsIDOMNode * aParent,
1373 int32_t aPosition)
1374 {
1375 int32_t i;
1376 nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
1378 for (i = 0; i < mActionListeners.Count(); i++)
1379 mActionListeners[i]->WillInsertNode(aNode, aParent, aPosition);
1381 nsRefPtr<InsertElementTxn> txn;
1382 nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition,
1383 getter_AddRefs(txn));
1384 if (NS_SUCCEEDED(result)) {
1385 result = DoTransaction(txn);
1386 }
1388 mRangeUpdater.SelAdjInsertNode(aParent, aPosition);
1390 for (i = 0; i < mActionListeners.Count(); i++)
1391 mActionListeners[i]->DidInsertNode(aNode, aParent, aPosition, result);
1393 return result;
1394 }
1397 NS_IMETHODIMP
1398 nsEditor::SplitNode(nsIDOMNode * aNode,
1399 int32_t aOffset,
1400 nsIDOMNode **aNewLeftNode)
1401 {
1402 int32_t i;
1403 nsAutoRules beginRulesSniffing(this, EditAction::splitNode, nsIEditor::eNext);
1405 for (i = 0; i < mActionListeners.Count(); i++)
1406 mActionListeners[i]->WillSplitNode(aNode, aOffset);
1408 nsRefPtr<SplitElementTxn> txn;
1409 nsresult result = CreateTxnForSplitNode(aNode, aOffset, getter_AddRefs(txn));
1410 if (NS_SUCCEEDED(result))
1411 {
1412 result = DoTransaction(txn);
1413 if (NS_SUCCEEDED(result))
1414 {
1415 result = txn->GetNewNode(aNewLeftNode);
1416 NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode");
1417 }
1418 }
1420 mRangeUpdater.SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
1422 for (i = 0; i < mActionListeners.Count(); i++)
1423 {
1424 nsIDOMNode *ptr = *aNewLeftNode;
1425 mActionListeners[i]->DidSplitNode(aNode, aOffset, ptr, result);
1426 }
1428 return result;
1429 }
1432 nsresult
1433 nsEditor::JoinNodes(nsINode* aNodeToKeep, nsIContent* aNodeToMove)
1434 {
1435 // We don't really need aNodeToMove's parent to be non-null -- we could just
1436 // skip adjusting any ranges in aNodeToMove's parent if there is none. But
1437 // the current implementation requires it.
1438 MOZ_ASSERT(aNodeToKeep && aNodeToMove && aNodeToMove->GetParentNode());
1439 nsresult res = JoinNodes(aNodeToKeep->AsDOMNode(), aNodeToMove->AsDOMNode(),
1440 aNodeToMove->GetParentNode()->AsDOMNode());
1441 NS_ASSERTION(NS_SUCCEEDED(res), "JoinNodes failed");
1442 NS_ENSURE_SUCCESS(res, res);
1443 return NS_OK;
1444 }
1446 NS_IMETHODIMP
1447 nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
1448 nsIDOMNode * aRightNode,
1449 nsIDOMNode * aParent)
1450 {
1451 int32_t i;
1452 nsAutoRules beginRulesSniffing(this, EditAction::joinNode, nsIEditor::ePrevious);
1454 // remember some values; later used for saved selection updating.
1455 // find the offset between the nodes to be joined.
1456 int32_t offset = GetChildOffset(aRightNode, aParent);
1457 // find the number of children of the lefthand node
1458 uint32_t oldLeftNodeLen;
1459 nsresult result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen);
1460 NS_ENSURE_SUCCESS(result, result);
1462 for (i = 0; i < mActionListeners.Count(); i++)
1463 mActionListeners[i]->WillJoinNodes(aLeftNode, aRightNode, aParent);
1465 nsRefPtr<JoinElementTxn> txn;
1466 result = CreateTxnForJoinNode(aLeftNode, aRightNode, getter_AddRefs(txn));
1467 if (NS_SUCCEEDED(result)) {
1468 result = DoTransaction(txn);
1469 }
1471 mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (int32_t)oldLeftNodeLen);
1473 for (i = 0; i < mActionListeners.Count(); i++)
1474 mActionListeners[i]->DidJoinNodes(aLeftNode, aRightNode, aParent, result);
1476 return result;
1477 }
1480 NS_IMETHODIMP
1481 nsEditor::DeleteNode(nsIDOMNode* aNode)
1482 {
1483 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1484 NS_ENSURE_STATE(node);
1485 return DeleteNode(node);
1486 }
1488 nsresult
1489 nsEditor::DeleteNode(nsINode* aNode)
1490 {
1491 nsAutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::ePrevious);
1493 // save node location for selection updating code.
1494 for (int32_t i = 0; i < mActionListeners.Count(); i++) {
1495 mActionListeners[i]->WillDeleteNode(aNode->AsDOMNode());
1496 }
1498 nsRefPtr<DeleteNodeTxn> txn;
1499 nsresult res = CreateTxnForDeleteNode(aNode, getter_AddRefs(txn));
1500 if (NS_SUCCEEDED(res)) {
1501 res = DoTransaction(txn);
1502 }
1504 for (int32_t i = 0; i < mActionListeners.Count(); i++) {
1505 mActionListeners[i]->DidDeleteNode(aNode->AsDOMNode(), res);
1506 }
1508 NS_ENSURE_SUCCESS(res, res);
1509 return NS_OK;
1510 }
1512 ///////////////////////////////////////////////////////////////////////////
1513 // ReplaceContainer: replace inNode with a new node (outNode) which is contructed
1514 // to be of type aNodeType. Put inNodes children into outNode.
1515 // Callers responsibility to make sure inNode's children can
1516 // go in outNode.
1517 nsresult
1518 nsEditor::ReplaceContainer(nsIDOMNode *inNode,
1519 nsCOMPtr<nsIDOMNode> *outNode,
1520 const nsAString &aNodeType,
1521 const nsAString *aAttribute,
1522 const nsAString *aValue,
1523 bool aCloneAttributes)
1524 {
1525 NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER);
1527 nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
1528 NS_ENSURE_STATE(node);
1530 nsCOMPtr<dom::Element> element;
1531 nsresult rv = ReplaceContainer(node, getter_AddRefs(element), aNodeType,
1532 aAttribute, aValue, aCloneAttributes);
1533 *outNode = element ? element->AsDOMNode() : nullptr;
1534 return rv;
1535 }
1537 nsresult
1538 nsEditor::ReplaceContainer(nsINode* aNode,
1539 dom::Element** outNode,
1540 const nsAString& aNodeType,
1541 const nsAString* aAttribute,
1542 const nsAString* aValue,
1543 bool aCloneAttributes)
1544 {
1545 MOZ_ASSERT(aNode);
1546 MOZ_ASSERT(outNode);
1548 *outNode = nullptr;
1550 nsCOMPtr<nsIContent> parent = aNode->GetParent();
1551 NS_ENSURE_STATE(parent);
1553 int32_t offset = parent->IndexOf(aNode);
1555 // create new container
1556 //new call to use instead to get proper HTML element, bug# 39919
1557 nsresult res = CreateHTMLContent(aNodeType, outNode);
1558 NS_ENSURE_SUCCESS(res, res);
1560 nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(*outNode);
1562 nsIDOMNode* inNode = aNode->AsDOMNode();
1564 // set attribute if needed
1565 if (aAttribute && aValue && !aAttribute->IsEmpty()) {
1566 res = elem->SetAttribute(*aAttribute, *aValue);
1567 NS_ENSURE_SUCCESS(res, res);
1568 }
1569 if (aCloneAttributes) {
1570 res = CloneAttributes(elem, inNode);
1571 NS_ENSURE_SUCCESS(res, res);
1572 }
1574 // notify our internal selection state listener
1575 // (Note: A nsAutoSelectionReset object must be created
1576 // before calling this to initialize mRangeUpdater)
1577 nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, inNode, elem);
1578 {
1579 nsAutoTxnsConserveSelection conserveSelection(this);
1580 while (aNode->HasChildren()) {
1581 nsCOMPtr<nsIDOMNode> child = aNode->GetFirstChild()->AsDOMNode();
1583 res = DeleteNode(child);
1584 NS_ENSURE_SUCCESS(res, res);
1586 res = InsertNode(child, elem, -1);
1587 NS_ENSURE_SUCCESS(res, res);
1588 }
1589 }
1591 // insert new container into tree
1592 res = InsertNode(elem, parent->AsDOMNode(), offset);
1593 NS_ENSURE_SUCCESS(res, res);
1595 // delete old container
1596 return DeleteNode(inNode);
1597 }
1599 ///////////////////////////////////////////////////////////////////////////
1600 // RemoveContainer: remove inNode, reparenting its children into their
1601 // the parent of inNode
1602 //
1603 nsresult
1604 nsEditor::RemoveContainer(nsIDOMNode* aNode)
1605 {
1606 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1607 return RemoveContainer(node);
1608 }
1610 nsresult
1611 nsEditor::RemoveContainer(nsINode* aNode)
1612 {
1613 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1615 nsCOMPtr<nsINode> parent = aNode->GetParentNode();
1616 NS_ENSURE_STATE(parent);
1618 int32_t offset = parent->IndexOf(aNode);
1620 // loop through the child nodes of inNode and promote them
1621 // into inNode's parent.
1622 uint32_t nodeOrigLen = aNode->GetChildCount();
1624 // notify our internal selection state listener
1625 nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode, parent, offset, nodeOrigLen);
1627 while (aNode->HasChildren()) {
1628 nsCOMPtr<nsIContent> child = aNode->GetLastChild();
1629 nsresult rv = DeleteNode(child->AsDOMNode());
1630 NS_ENSURE_SUCCESS(rv, rv);
1632 rv = InsertNode(child->AsDOMNode(), parent->AsDOMNode(), offset);
1633 NS_ENSURE_SUCCESS(rv, rv);
1634 }
1636 return DeleteNode(aNode->AsDOMNode());
1637 }
1640 ///////////////////////////////////////////////////////////////////////////
1641 // InsertContainerAbove: insert a new parent for inNode, returned in outNode,
1642 // which is contructed to be of type aNodeType. outNode becomes
1643 // a child of inNode's earlier parent.
1644 // Callers responsibility to make sure inNode's can be child
1645 // of outNode, and outNode can be child of old parent.
1646 nsresult
1647 nsEditor::InsertContainerAbove( nsIDOMNode *inNode,
1648 nsCOMPtr<nsIDOMNode> *outNode,
1649 const nsAString &aNodeType,
1650 const nsAString *aAttribute,
1651 const nsAString *aValue)
1652 {
1653 NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER);
1655 nsCOMPtr<nsIContent> node = do_QueryInterface(inNode);
1656 NS_ENSURE_STATE(node);
1658 nsCOMPtr<dom::Element> element;
1659 nsresult rv = InsertContainerAbove(node, getter_AddRefs(element), aNodeType,
1660 aAttribute, aValue);
1661 *outNode = element ? element->AsDOMNode() : nullptr;
1662 return rv;
1663 }
1665 nsresult
1666 nsEditor::InsertContainerAbove(nsIContent* aNode,
1667 dom::Element** aOutNode,
1668 const nsAString& aNodeType,
1669 const nsAString* aAttribute,
1670 const nsAString* aValue)
1671 {
1672 MOZ_ASSERT(aNode);
1674 nsCOMPtr<nsIContent> parent = aNode->GetParent();
1675 NS_ENSURE_STATE(parent);
1676 int32_t offset = parent->IndexOf(aNode);
1678 // create new container
1679 nsCOMPtr<dom::Element> newContent;
1681 //new call to use instead to get proper HTML element, bug# 39919
1682 nsresult res = CreateHTMLContent(aNodeType, getter_AddRefs(newContent));
1683 NS_ENSURE_SUCCESS(res, res);
1685 // set attribute if needed
1686 if (aAttribute && aValue && !aAttribute->IsEmpty()) {
1687 nsIDOMNode* elem = newContent->AsDOMNode();
1688 res = static_cast<nsIDOMElement*>(elem)->SetAttribute(*aAttribute, *aValue);
1689 NS_ENSURE_SUCCESS(res, res);
1690 }
1692 // notify our internal selection state listener
1693 nsAutoInsertContainerSelNotify selNotify(mRangeUpdater);
1695 // put inNode in new parent, outNode
1696 res = DeleteNode(aNode->AsDOMNode());
1697 NS_ENSURE_SUCCESS(res, res);
1699 {
1700 nsAutoTxnsConserveSelection conserveSelection(this);
1701 res = InsertNode(aNode->AsDOMNode(), newContent->AsDOMNode(), 0);
1702 NS_ENSURE_SUCCESS(res, res);
1703 }
1705 // put new parent in doc
1706 res = InsertNode(newContent->AsDOMNode(), parent->AsDOMNode(), offset);
1707 newContent.forget(aOutNode);
1708 return res;
1709 }
1711 ///////////////////////////////////////////////////////////////////////////
1712 // MoveNode: move aNode to {aParent,aOffset}
1713 nsresult
1714 nsEditor::MoveNode(nsIDOMNode* aNode, nsIDOMNode* aParent, int32_t aOffset)
1715 {
1716 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1717 NS_ENSURE_STATE(node);
1719 nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
1720 NS_ENSURE_STATE(parent);
1722 return MoveNode(node, parent, aOffset);
1723 }
1725 nsresult
1726 nsEditor::MoveNode(nsINode* aNode, nsINode* aParent, int32_t aOffset)
1727 {
1728 MOZ_ASSERT(aNode);
1729 MOZ_ASSERT(aParent);
1730 MOZ_ASSERT(aOffset == -1 ||
1731 (0 <= aOffset && SafeCast<uint32_t>(aOffset) <= aParent->Length()));
1733 int32_t oldOffset;
1734 nsCOMPtr<nsINode> oldParent = GetNodeLocation(aNode, &oldOffset);
1736 if (aOffset == -1) {
1737 // Magic value meaning "move to end of aParent".
1738 aOffset = SafeCast<int32_t>(aParent->Length());
1739 }
1741 // Don't do anything if it's already in right place.
1742 if (aParent == oldParent && aOffset == oldOffset) {
1743 return NS_OK;
1744 }
1746 // Notify our internal selection state listener.
1747 nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset,
1748 aParent, aOffset);
1750 // Need to adjust aOffset if we are moving aNode further along in its current
1751 // parent.
1752 if (aParent == oldParent && oldOffset < aOffset) {
1753 // This is because when we delete aNode, it will make the offsets after it
1754 // off by one.
1755 aOffset--;
1756 }
1758 // Hold a reference so aNode doesn't go away when we remove it (bug 772282).
1759 nsCOMPtr<nsINode> kungFuDeathGrip = aNode;
1761 nsresult rv = DeleteNode(aNode);
1762 NS_ENSURE_SUCCESS(rv, rv);
1764 return InsertNode(aNode->AsDOMNode(), aParent->AsDOMNode(), aOffset);
1765 }
1768 NS_IMETHODIMP
1769 nsEditor::AddEditorObserver(nsIEditorObserver *aObserver)
1770 {
1771 // we don't keep ownership of the observers. They must
1772 // remove themselves as observers before they are destroyed.
1774 NS_ENSURE_TRUE(aObserver, NS_ERROR_NULL_POINTER);
1776 // Make sure the listener isn't already on the list
1777 if (mEditorObservers.IndexOf(aObserver) == -1)
1778 {
1779 if (!mEditorObservers.AppendObject(aObserver))
1780 return NS_ERROR_FAILURE;
1781 }
1783 return NS_OK;
1784 }
1787 NS_IMETHODIMP
1788 nsEditor::RemoveEditorObserver(nsIEditorObserver *aObserver)
1789 {
1790 NS_ENSURE_TRUE(aObserver, NS_ERROR_FAILURE);
1792 if (!mEditorObservers.RemoveObject(aObserver))
1793 return NS_ERROR_FAILURE;
1795 return NS_OK;
1796 }
1798 class EditorInputEventDispatcher : public nsRunnable
1799 {
1800 public:
1801 EditorInputEventDispatcher(nsEditor* aEditor,
1802 nsIContent* aTarget,
1803 bool aIsComposing)
1804 : mEditor(aEditor)
1805 , mTarget(aTarget)
1806 , mIsComposing(aIsComposing)
1807 {
1808 }
1810 NS_IMETHOD Run()
1811 {
1812 // Note that we don't need to check mDispatchInputEvent here. We need
1813 // to check it only when the editor requests to dispatch the input event.
1815 if (!mTarget->IsInDoc()) {
1816 return NS_OK;
1817 }
1819 nsCOMPtr<nsIPresShell> ps = mEditor->GetPresShell();
1820 if (!ps) {
1821 return NS_OK;
1822 }
1824 nsCOMPtr<nsIWidget> widget = mEditor->GetWidget();
1825 if (!widget) {
1826 return NS_OK;
1827 }
1829 // Even if the change is caused by untrusted event, we need to dispatch
1830 // trusted input event since it's a fact.
1831 InternalEditorInputEvent inputEvent(true, NS_EDITOR_INPUT, widget);
1832 inputEvent.time = static_cast<uint64_t>(PR_Now() / 1000);
1833 inputEvent.mIsComposing = mIsComposing;
1834 nsEventStatus status = nsEventStatus_eIgnore;
1835 nsresult rv =
1836 ps->HandleEventWithTarget(&inputEvent, nullptr, mTarget, &status);
1837 NS_ENSURE_SUCCESS(rv, NS_OK); // print the warning if error
1838 return NS_OK;
1839 }
1841 private:
1842 nsRefPtr<nsEditor> mEditor;
1843 nsCOMPtr<nsIContent> mTarget;
1844 bool mIsComposing;
1845 };
1847 void nsEditor::NotifyEditorObservers(void)
1848 {
1849 for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
1850 mEditorObservers[i]->EditAction();
1851 }
1853 if (!mDispatchInputEvent) {
1854 return;
1855 }
1857 FireInputEvent();
1858 }
1860 void
1861 nsEditor::FireInputEvent()
1862 {
1863 // We don't need to dispatch multiple input events if there is a pending
1864 // input event. However, it may have different event target. If we resolved
1865 // this issue, we need to manage the pending events in an array. But it's
1866 // overwork. We don't need to do it for the very rare case.
1868 nsCOMPtr<nsIContent> target = GetInputEventTargetContent();
1869 NS_ENSURE_TRUE_VOID(target);
1871 // NOTE: Don't refer IsIMEComposing() because it returns false even before
1872 // compositionend. However, DOM Level 3 Events defines it should be
1873 // true after compositionstart and before compositionend.
1874 nsContentUtils::AddScriptRunner(
1875 new EditorInputEventDispatcher(this, target, !!GetComposition()));
1876 }
1878 NS_IMETHODIMP
1879 nsEditor::AddEditActionListener(nsIEditActionListener *aListener)
1880 {
1881 NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
1883 // Make sure the listener isn't already on the list
1884 if (mActionListeners.IndexOf(aListener) == -1)
1885 {
1886 if (!mActionListeners.AppendObject(aListener))
1887 return NS_ERROR_FAILURE;
1888 }
1890 return NS_OK;
1891 }
1894 NS_IMETHODIMP
1895 nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener)
1896 {
1897 NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
1899 if (!mActionListeners.RemoveObject(aListener))
1900 return NS_ERROR_FAILURE;
1902 return NS_OK;
1903 }
1906 NS_IMETHODIMP
1907 nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener)
1908 {
1909 NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
1911 if (mDocStateListeners.IndexOf(aListener) == -1)
1912 {
1913 if (!mDocStateListeners.AppendObject(aListener))
1914 return NS_ERROR_FAILURE;
1915 }
1917 return NS_OK;
1918 }
1921 NS_IMETHODIMP
1922 nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener)
1923 {
1924 NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
1926 if (!mDocStateListeners.RemoveObject(aListener))
1927 return NS_ERROR_FAILURE;
1929 return NS_OK;
1930 }
1933 NS_IMETHODIMP nsEditor::OutputToString(const nsAString& aFormatType,
1934 uint32_t aFlags,
1935 nsAString& aOutputString)
1936 {
1937 // these should be implemented by derived classes.
1938 return NS_ERROR_NOT_IMPLEMENTED;
1939 }
1941 NS_IMETHODIMP
1942 nsEditor::OutputToStream(nsIOutputStream* aOutputStream,
1943 const nsAString& aFormatType,
1944 const nsACString& aCharsetOverride,
1945 uint32_t aFlags)
1946 {
1947 // these should be implemented by derived classes.
1948 return NS_ERROR_NOT_IMPLEMENTED;
1949 }
1951 NS_IMETHODIMP
1952 nsEditor::DumpContentTree()
1953 {
1954 #ifdef DEBUG
1955 if (mRootElement) {
1956 mRootElement->List(stdout);
1957 }
1958 #endif
1959 return NS_OK;
1960 }
1963 NS_IMETHODIMP
1964 nsEditor::DebugDumpContent()
1965 {
1966 #ifdef DEBUG
1967 nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryReferent(mDocWeak);
1968 NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
1970 nsCOMPtr<nsIDOMHTMLElement>bodyElem;
1971 doc->GetBody(getter_AddRefs(bodyElem));
1972 nsCOMPtr<nsIContent> content = do_QueryInterface(bodyElem);
1973 if (content)
1974 content->List();
1975 #endif
1976 return NS_OK;
1977 }
1980 NS_IMETHODIMP
1981 nsEditor::DebugUnitTests(int32_t *outNumTests, int32_t *outNumTestsFailed)
1982 {
1983 #ifdef DEBUG
1984 NS_NOTREACHED("This should never get called. Overridden by subclasses");
1985 #endif
1986 return NS_OK;
1987 }
1990 bool
1991 nsEditor::ArePreservingSelection()
1992 {
1993 return !(mSavedSel.IsEmpty());
1994 }
1996 void
1997 nsEditor::PreserveSelectionAcrossActions(Selection* aSel)
1998 {
1999 mSavedSel.SaveSelection(aSel);
2000 mRangeUpdater.RegisterSelectionState(mSavedSel);
2001 }
2003 nsresult
2004 nsEditor::RestorePreservedSelection(nsISelection *aSel)
2005 {
2006 if (mSavedSel.IsEmpty()) return NS_ERROR_FAILURE;
2007 mSavedSel.RestoreSelection(aSel);
2008 StopPreservingSelection();
2009 return NS_OK;
2010 }
2012 void
2013 nsEditor::StopPreservingSelection()
2014 {
2015 mRangeUpdater.DropSelectionState(mSavedSel);
2016 mSavedSel.MakeEmpty();
2017 }
2019 void
2020 nsEditor::EnsureComposition(mozilla::WidgetGUIEvent* aEvent)
2021 {
2022 if (mComposition) {
2023 return;
2024 }
2025 // The compositionstart event must cause creating new TextComposition
2026 // instance at being dispatched by IMEStateManager.
2027 mComposition = IMEStateManager::GetTextCompositionFor(aEvent);
2028 if (!mComposition) {
2029 MOZ_CRASH("IMEStateManager doesn't return proper composition");
2030 }
2031 mComposition->StartHandlingComposition(this);
2032 }
2034 nsresult
2035 nsEditor::BeginIMEComposition(WidgetCompositionEvent* aCompositionEvent)
2036 {
2037 MOZ_ASSERT(!mComposition, "There is composition already");
2038 EnsureComposition(aCompositionEvent);
2039 if (mPhonetic) {
2040 mPhonetic->Truncate(0);
2041 }
2042 return NS_OK;
2043 }
2045 void
2046 nsEditor::EndIMEComposition()
2047 {
2048 NS_ENSURE_TRUE_VOID(mComposition); // nothing to do
2050 // commit the IME transaction..we can get at it via the transaction mgr.
2051 // Note that this means IME won't work without an undo stack!
2052 if (mTxnMgr) {
2053 nsCOMPtr<nsITransaction> txn = mTxnMgr->PeekUndoStack();
2054 nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
2055 if (plcTxn) {
2056 DebugOnly<nsresult> rv = plcTxn->Commit();
2057 NS_ASSERTION(NS_SUCCEEDED(rv),
2058 "nsIAbsorbingTransaction::Commit() failed");
2059 }
2060 }
2062 /* reset the data we need to construct a transaction */
2063 mIMETextNode = nullptr;
2064 mIMETextOffset = 0;
2065 mComposition->EndHandlingComposition(this);
2066 mComposition = nullptr;
2068 // notify editor observers of action
2069 NotifyEditorObservers();
2070 }
2073 NS_IMETHODIMP
2074 nsEditor::GetPhonetic(nsAString& aPhonetic)
2075 {
2076 if (mPhonetic)
2077 aPhonetic = *mPhonetic;
2078 else
2079 aPhonetic.Truncate(0);
2081 return NS_OK;
2082 }
2084 NS_IMETHODIMP
2085 nsEditor::ForceCompositionEnd()
2086 {
2087 nsCOMPtr<nsIPresShell> ps = GetPresShell();
2088 if (!ps) {
2089 return NS_ERROR_NOT_AVAILABLE;
2090 }
2091 nsPresContext* pc = ps->GetPresContext();
2092 if (!pc) {
2093 return NS_ERROR_NOT_AVAILABLE;
2094 }
2096 if (!mComposition) {
2097 // XXXmnakano see bug 558976, ResetInputState() has two meaning which are
2098 // "commit the composition" and "cursor is moved". This method name is
2099 // "ForceCompositionEnd", so, ResetInputState() should be used only for the
2100 // former here. However, ResetInputState() is also used for the latter here
2101 // because even if we don't have composition, we call ResetInputState() on
2102 // Linux. Currently, nsGtkIMModule can know the timing of the cursor move,
2103 // so, the latter meaning should be gone.
2104 // XXX This may commit a composition in another editor.
2105 return IMEStateManager::NotifyIME(NOTIFY_IME_OF_CURSOR_POS_CHANGED, pc);
2106 }
2108 return IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc);
2109 }
2111 NS_IMETHODIMP
2112 nsEditor::GetPreferredIMEState(IMEState *aState)
2113 {
2114 NS_ENSURE_ARG_POINTER(aState);
2115 aState->mEnabled = IMEState::ENABLED;
2116 aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
2118 if (IsReadonly() || IsDisabled()) {
2119 aState->mEnabled = IMEState::DISABLED;
2120 return NS_OK;
2121 }
2123 nsCOMPtr<nsIContent> content = GetRoot();
2124 NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
2126 nsIFrame* frame = content->GetPrimaryFrame();
2127 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
2129 switch (frame->StyleUIReset()->mIMEMode) {
2130 case NS_STYLE_IME_MODE_AUTO:
2131 if (IsPasswordEditor())
2132 aState->mEnabled = IMEState::PASSWORD;
2133 break;
2134 case NS_STYLE_IME_MODE_DISABLED:
2135 // we should use password state for |ime-mode: disabled;|.
2136 aState->mEnabled = IMEState::PASSWORD;
2137 break;
2138 case NS_STYLE_IME_MODE_ACTIVE:
2139 aState->mOpen = IMEState::OPEN;
2140 break;
2141 case NS_STYLE_IME_MODE_INACTIVE:
2142 aState->mOpen = IMEState::CLOSED;
2143 break;
2144 }
2146 return NS_OK;
2147 }
2149 NS_IMETHODIMP
2150 nsEditor::GetComposing(bool* aResult)
2151 {
2152 NS_ENSURE_ARG_POINTER(aResult);
2153 *aResult = IsIMEComposing();
2154 return NS_OK;
2155 }
2158 /* Non-interface, public methods */
2160 NS_IMETHODIMP
2161 nsEditor::GetRootElement(nsIDOMElement **aRootElement)
2162 {
2163 NS_ENSURE_ARG_POINTER(aRootElement);
2164 NS_ENSURE_TRUE(mRootElement, NS_ERROR_NOT_AVAILABLE);
2165 nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mRootElement);
2166 rootElement.forget(aRootElement);
2167 return NS_OK;
2168 }
2171 /** All editor operations which alter the doc should be prefaced
2172 * with a call to StartOperation, naming the action and direction */
2173 NS_IMETHODIMP
2174 nsEditor::StartOperation(EditAction opID, nsIEditor::EDirection aDirection)
2175 {
2176 mAction = opID;
2177 mDirection = aDirection;
2178 return NS_OK;
2179 }
2182 /** All editor operations which alter the doc should be followed
2183 * with a call to EndOperation */
2184 NS_IMETHODIMP
2185 nsEditor::EndOperation()
2186 {
2187 mAction = EditAction::none;
2188 mDirection = eNone;
2189 return NS_OK;
2190 }
2192 NS_IMETHODIMP
2193 nsEditor::CloneAttribute(const nsAString & aAttribute,
2194 nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
2195 {
2196 NS_ENSURE_TRUE(aDestNode && aSourceNode, NS_ERROR_NULL_POINTER);
2198 nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
2199 nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
2200 NS_ENSURE_TRUE(destElement && sourceElement, NS_ERROR_NO_INTERFACE);
2202 nsAutoString attrValue;
2203 bool isAttrSet;
2204 nsresult rv = GetAttributeValue(sourceElement,
2205 aAttribute,
2206 attrValue,
2207 &isAttrSet);
2208 NS_ENSURE_SUCCESS(rv, rv);
2209 if (isAttrSet)
2210 rv = SetAttribute(destElement, aAttribute, attrValue);
2211 else
2212 rv = RemoveAttribute(destElement, aAttribute);
2214 return rv;
2215 }
2217 // Objects must be DOM elements
2218 NS_IMETHODIMP
2219 nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
2220 {
2221 NS_ENSURE_TRUE(aDestNode && aSourceNode, NS_ERROR_NULL_POINTER);
2223 nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
2224 nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
2225 NS_ENSURE_TRUE(destElement && sourceElement, NS_ERROR_NO_INTERFACE);
2227 nsCOMPtr<nsIDOMMozNamedAttrMap> sourceAttributes;
2228 sourceElement->GetAttributes(getter_AddRefs(sourceAttributes));
2229 nsCOMPtr<nsIDOMMozNamedAttrMap> destAttributes;
2230 destElement->GetAttributes(getter_AddRefs(destAttributes));
2231 NS_ENSURE_TRUE(sourceAttributes && destAttributes, NS_ERROR_FAILURE);
2233 nsAutoEditBatch beginBatching(this);
2235 // Use transaction system for undo only if destination
2236 // is already in the document
2237 nsCOMPtr<nsIDOMNode> p = aDestNode;
2238 nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot());
2239 NS_ENSURE_TRUE(rootNode, NS_ERROR_NULL_POINTER);
2240 bool destInBody = true;
2241 while (p && p != rootNode)
2242 {
2243 nsCOMPtr<nsIDOMNode> tmp;
2244 if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp)
2245 {
2246 destInBody = false;
2247 break;
2248 }
2249 p = tmp;
2250 }
2252 uint32_t sourceCount;
2253 sourceAttributes->GetLength(&sourceCount);
2254 uint32_t destCount;
2255 destAttributes->GetLength(&destCount);
2256 nsCOMPtr<nsIDOMAttr> attr;
2258 // Clear existing attributes
2259 for (uint32_t i = 0; i < destCount; i++) {
2260 // always remove item number 0 (first item in list)
2261 if (NS_SUCCEEDED(destAttributes->Item(0, getter_AddRefs(attr))) && attr) {
2262 nsString str;
2263 if (NS_SUCCEEDED(attr->GetName(str))) {
2264 if (destInBody) {
2265 RemoveAttribute(destElement, str);
2266 } else {
2267 destElement->RemoveAttribute(str);
2268 }
2269 }
2270 }
2271 }
2273 nsresult result = NS_OK;
2275 // Set just the attributes that the source element has
2276 for (uint32_t i = 0; i < sourceCount; i++)
2277 {
2278 if (NS_SUCCEEDED(sourceAttributes->Item(i, getter_AddRefs(attr))) && attr) {
2279 nsString sourceAttrName;
2280 if (NS_SUCCEEDED(attr->GetName(sourceAttrName))) {
2281 nsString sourceAttrValue;
2282 /* Presence of an attribute in the named node map indicates that it was
2283 * set on the element even if it has no value.
2284 */
2285 if (NS_SUCCEEDED(attr->GetValue(sourceAttrValue))) {
2286 if (destInBody) {
2287 result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, false);
2288 } else {
2289 // the element is not inserted in the document yet, we don't want to put a
2290 // transaction on the UndoStack
2291 result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, true);
2292 }
2293 } else {
2294 // Do we ever get here?
2295 #if DEBUG_cmanske
2296 printf("Attribute in sourceAttribute has empty value in nsEditor::CloneAttributes()\n");
2297 #endif
2298 }
2299 }
2300 }
2301 }
2302 return result;
2303 }
2306 NS_IMETHODIMP nsEditor::ScrollSelectionIntoView(bool aScrollToAnchor)
2307 {
2308 nsCOMPtr<nsISelectionController> selCon;
2309 if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
2310 {
2311 int16_t region = nsISelectionController::SELECTION_FOCUS_REGION;
2313 if (aScrollToAnchor)
2314 region = nsISelectionController::SELECTION_ANCHOR_REGION;
2316 selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
2317 region, nsISelectionController::SCROLL_OVERFLOW_HIDDEN);
2318 }
2320 return NS_OK;
2321 }
2323 NS_IMETHODIMP
2324 nsEditor::InsertTextImpl(const nsAString& aStringToInsert,
2325 nsCOMPtr<nsIDOMNode>* aInOutNode,
2326 int32_t* aInOutOffset,
2327 nsIDOMDocument* aDoc)
2328 {
2329 // NOTE: caller *must* have already used nsAutoTxnsConserveSelection
2330 // stack-based class to turn off txn selection updating. Caller also turned
2331 // on rules sniffing if desired.
2333 NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc,
2334 NS_ERROR_NULL_POINTER);
2335 if (!mComposition && aStringToInsert.IsEmpty()) {
2336 return NS_OK;
2337 }
2339 nsCOMPtr<nsINode> node = do_QueryInterface(*aInOutNode);
2340 NS_ENSURE_STATE(node);
2341 uint32_t offset = static_cast<uint32_t>(*aInOutOffset);
2343 if (!node->IsNodeOfType(nsINode::eTEXT) && IsPlaintextEditor()) {
2344 nsCOMPtr<nsINode> root = GetRoot();
2345 // In some cases, node is the anonymous DIV, and offset is 0. To avoid
2346 // injecting unneeded text nodes, we first look to see if we have one
2347 // available. In that case, we'll just adjust node and offset accordingly.
2348 if (node == root && offset == 0 && node->HasChildren() &&
2349 node->GetFirstChild()->IsNodeOfType(nsINode::eTEXT)) {
2350 node = node->GetFirstChild();
2351 }
2352 // In some other cases, node is the anonymous DIV, and offset points to the
2353 // terminating mozBR. In that case, we'll adjust aInOutNode and
2354 // aInOutOffset to the preceding text node, if any.
2355 if (node == root && offset > 0 && node->GetChildAt(offset - 1) &&
2356 node->GetChildAt(offset - 1)->IsNodeOfType(nsINode::eTEXT)) {
2357 node = node->GetChildAt(offset - 1);
2358 offset = node->Length();
2359 }
2360 // Sometimes, node is the mozBR element itself. In that case, we'll adjust
2361 // the insertion point to the previous text node, if one exists, or to the
2362 // parent anonymous DIV.
2363 if (nsTextEditUtils::IsMozBR(node) && offset == 0) {
2364 if (node->GetPreviousSibling() &&
2365 node->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT)) {
2366 node = node->GetPreviousSibling();
2367 offset = node->Length();
2368 } else if (node->GetParentNode() && node->GetParentNode() == root) {
2369 node = node->GetParentNode();
2370 }
2371 }
2372 }
2374 nsresult res;
2375 if (mComposition) {
2376 if (!node->IsNodeOfType(nsINode::eTEXT)) {
2377 // create a text node
2378 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
2379 NS_ENSURE_STATE(doc);
2380 nsRefPtr<nsTextNode> newNode = doc->CreateTextNode(EmptyString());
2381 // then we insert it into the dom tree
2382 res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset);
2383 NS_ENSURE_SUCCESS(res, res);
2384 node = newNode;
2385 offset = 0;
2386 }
2387 nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(node);
2388 NS_ENSURE_STATE(charDataNode);
2389 res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset);
2390 NS_ENSURE_SUCCESS(res, res);
2391 offset += aStringToInsert.Length();
2392 } else {
2393 if (node->IsNodeOfType(nsINode::eTEXT)) {
2394 // we are inserting text into an existing text node.
2395 nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(node);
2396 NS_ENSURE_STATE(charDataNode);
2397 res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset);
2398 NS_ENSURE_SUCCESS(res, res);
2399 offset += aStringToInsert.Length();
2400 } else {
2401 // we are inserting text into a non-text node. first we have to create a
2402 // textnode (this also populates it with the text)
2403 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
2404 NS_ENSURE_STATE(doc);
2405 nsRefPtr<nsTextNode> newNode = doc->CreateTextNode(aStringToInsert);
2406 // then we insert it into the dom tree
2407 res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset);
2408 NS_ENSURE_SUCCESS(res, res);
2409 node = newNode;
2410 offset = aStringToInsert.Length();
2411 }
2412 }
2414 *aInOutNode = node->AsDOMNode();
2415 *aInOutOffset = static_cast<int32_t>(offset);
2416 return NS_OK;
2417 }
2420 nsresult nsEditor::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
2421 nsIDOMCharacterData *aTextNode,
2422 int32_t aOffset,
2423 bool aSuppressIME)
2424 {
2425 nsRefPtr<EditTxn> txn;
2426 nsresult result = NS_OK;
2427 bool isIMETransaction = false;
2428 // aSuppressIME is used when editor must insert text, yet this text is not
2429 // part of current ime operation. example: adjusting whitespace around an ime insertion.
2430 if (mComposition && !aSuppressIME) {
2431 if (!mIMETextNode) {
2432 mIMETextNode = aTextNode;
2433 mIMETextOffset = aOffset;
2434 }
2435 // Modify mPhonetic with raw text input clauses.
2436 const TextRangeArray* ranges = mComposition->GetRanges();
2437 for (uint32_t i = 0; i < (ranges ? ranges->Length() : 0); ++i) {
2438 const TextRange& textRange = ranges->ElementAt(i);
2439 if (!textRange.Length() ||
2440 textRange.mRangeType != NS_TEXTRANGE_RAWINPUT) {
2441 continue;
2442 }
2443 if (!mPhonetic) {
2444 mPhonetic = new nsString();
2445 }
2446 nsAutoString stringToInsert(aStringToInsert);
2447 stringToInsert.Mid(*mPhonetic,
2448 textRange.mStartOffset, textRange.Length());
2449 }
2451 nsRefPtr<IMETextTxn> imeTxn;
2452 result = CreateTxnForIMEText(aStringToInsert, getter_AddRefs(imeTxn));
2453 txn = imeTxn;
2454 isIMETransaction = true;
2455 }
2456 else
2457 {
2458 nsRefPtr<InsertTextTxn> insertTxn;
2459 result = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset,
2460 getter_AddRefs(insertTxn));
2461 txn = insertTxn;
2462 }
2463 NS_ENSURE_SUCCESS(result, result);
2465 // let listeners know what's up
2466 int32_t i;
2467 for (i = 0; i < mActionListeners.Count(); i++)
2468 mActionListeners[i]->WillInsertText(aTextNode, aOffset, aStringToInsert);
2470 // XXX we may not need these view batches anymore. This is handled at a higher level now I believe
2471 BeginUpdateViewBatch();
2472 result = DoTransaction(txn);
2473 EndUpdateViewBatch();
2475 mRangeUpdater.SelAdjInsertText(aTextNode, aOffset, aStringToInsert);
2477 // let listeners know what happened
2478 for (i = 0; i < mActionListeners.Count(); i++)
2479 mActionListeners[i]->DidInsertText(aTextNode, aOffset, aStringToInsert, result);
2481 // Added some cruft here for bug 43366. Layout was crashing because we left an
2482 // empty text node lying around in the document. So I delete empty text nodes
2483 // caused by IME. I have to mark the IME transaction as "fixed", which means
2484 // that furure ime txns won't merge with it. This is because we don't want
2485 // future ime txns trying to put their text into a node that is no longer in
2486 // the document. This does not break undo/redo, because all these txns are
2487 // wrapped in a parent PlaceHolder txn, and placeholder txns are already
2488 // savvy to having multiple ime txns inside them.
2490 // delete empty ime text node if there is one
2491 if (isIMETransaction && mIMETextNode)
2492 {
2493 uint32_t len;
2494 mIMETextNode->GetLength(&len);
2495 if (!len)
2496 {
2497 DeleteNode(mIMETextNode);
2498 mIMETextNode = nullptr;
2499 static_cast<IMETextTxn*>(txn.get())->MarkFixed(); // mark the ime txn "fixed"
2500 }
2501 }
2503 return result;
2504 }
2507 NS_IMETHODIMP nsEditor::SelectEntireDocument(nsISelection *aSelection)
2508 {
2509 if (!aSelection) { return NS_ERROR_NULL_POINTER; }
2511 nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
2512 if (!rootElement) { return NS_ERROR_NOT_INITIALIZED; }
2514 return aSelection->SelectAllChildren(rootElement);
2515 }
2518 nsINode*
2519 nsEditor::GetFirstEditableNode(nsINode* aRoot)
2520 {
2521 MOZ_ASSERT(aRoot);
2523 nsIContent* node = GetLeftmostChild(aRoot);
2524 if (node && !IsEditable(node)) {
2525 node = GetNextNode(node, /* aEditableNode = */ true);
2526 }
2528 return (node != aRoot) ? node : nullptr;
2529 }
2532 NS_IMETHODIMP
2533 nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType)
2534 {
2535 int32_t numListeners = mDocStateListeners.Count();
2536 if (!numListeners) // maybe there just aren't any.
2537 return NS_OK;
2539 nsCOMArray<nsIDocumentStateListener> listeners(mDocStateListeners);
2540 nsresult rv = NS_OK;
2541 int32_t i;
2543 switch (aNotificationType)
2544 {
2545 case eDocumentCreated:
2546 for (i = 0; i < numListeners;i++)
2547 {
2548 rv = listeners[i]->NotifyDocumentCreated();
2549 if (NS_FAILED(rv))
2550 break;
2551 }
2552 break;
2554 case eDocumentToBeDestroyed:
2555 for (i = 0; i < numListeners;i++)
2556 {
2557 rv = listeners[i]->NotifyDocumentWillBeDestroyed();
2558 if (NS_FAILED(rv))
2559 break;
2560 }
2561 break;
2563 case eDocumentStateChanged:
2564 {
2565 bool docIsDirty;
2566 rv = GetDocumentModified(&docIsDirty);
2567 NS_ENSURE_SUCCESS(rv, rv);
2569 if (static_cast<int8_t>(docIsDirty) == mDocDirtyState)
2570 return NS_OK;
2572 mDocDirtyState = docIsDirty;
2574 for (i = 0; i < numListeners;i++)
2575 {
2576 rv = listeners[i]->NotifyDocumentStateChanged(mDocDirtyState);
2577 if (NS_FAILED(rv))
2578 break;
2579 }
2580 }
2581 break;
2583 default:
2584 NS_NOTREACHED("Unknown notification");
2585 }
2587 return rv;
2588 }
2591 NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsAString & aStringToInsert,
2592 nsIDOMCharacterData *aTextNode,
2593 int32_t aOffset,
2594 InsertTextTxn ** aTxn)
2595 {
2596 NS_ENSURE_TRUE(aTextNode && aTxn, NS_ERROR_NULL_POINTER);
2597 nsresult rv;
2599 nsRefPtr<InsertTextTxn> txn = new InsertTextTxn();
2600 rv = txn->Init(aTextNode, aOffset, aStringToInsert, this);
2601 if (NS_SUCCEEDED(rv))
2602 {
2603 txn.forget(aTxn);
2604 }
2606 return rv;
2607 }
2610 NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
2611 uint32_t aOffset,
2612 uint32_t aLength)
2613 {
2614 nsRefPtr<DeleteTextTxn> txn;
2615 nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength,
2616 getter_AddRefs(txn));
2617 nsAutoRules beginRulesSniffing(this, EditAction::deleteText, nsIEditor::ePrevious);
2618 if (NS_SUCCEEDED(result))
2619 {
2620 // let listeners know what's up
2621 int32_t i;
2622 for (i = 0; i < mActionListeners.Count(); i++)
2623 mActionListeners[i]->WillDeleteText(aElement, aOffset, aLength);
2625 result = DoTransaction(txn);
2627 // let listeners know what happened
2628 for (i = 0; i < mActionListeners.Count(); i++)
2629 mActionListeners[i]->DidDeleteText(aElement, aOffset, aLength, result);
2630 }
2631 return result;
2632 }
2635 nsresult
2636 nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData* aElement,
2637 uint32_t aOffset,
2638 uint32_t aLength,
2639 DeleteTextTxn** aTxn)
2640 {
2641 NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
2643 nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
2645 nsresult res = txn->Init(this, aElement, aOffset, aLength, &mRangeUpdater);
2646 NS_ENSURE_SUCCESS(res, res);
2648 txn.forget(aTxn);
2649 return NS_OK;
2650 }
2655 NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode,
2656 uint32_t aOffset,
2657 SplitElementTxn **aTxn)
2658 {
2659 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
2661 nsRefPtr<SplitElementTxn> txn = new SplitElementTxn();
2663 nsresult rv = txn->Init(this, aNode, aOffset);
2664 if (NS_SUCCEEDED(rv))
2665 {
2666 txn.forget(aTxn);
2667 }
2669 return rv;
2670 }
2672 NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode,
2673 nsIDOMNode *aRightNode,
2674 JoinElementTxn **aTxn)
2675 {
2676 NS_ENSURE_TRUE(aLeftNode && aRightNode, NS_ERROR_NULL_POINTER);
2678 nsRefPtr<JoinElementTxn> txn = new JoinElementTxn();
2680 nsresult rv = txn->Init(this, aLeftNode, aRightNode);
2681 if (NS_SUCCEEDED(rv))
2682 {
2683 txn.forget(aTxn);
2684 }
2686 return rv;
2687 }
2690 // END nsEditor core implementation
2693 // BEGIN nsEditor public helper methods
2695 nsresult
2696 nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode,
2697 int32_t aOffset,
2698 nsIDOMNode* aNewLeftNode,
2699 nsIDOMNode* aParent)
2700 {
2701 NS_ASSERTION(((nullptr!=aExistingRightNode) &&
2702 (nullptr!=aNewLeftNode) &&
2703 (nullptr!=aParent)),
2704 "null arg");
2705 nsresult result;
2706 if ((nullptr!=aExistingRightNode) &&
2707 (nullptr!=aNewLeftNode) &&
2708 (nullptr!=aParent))
2709 {
2710 // get selection
2711 nsCOMPtr<nsISelection> selection;
2712 result = GetSelection(getter_AddRefs(selection));
2713 NS_ENSURE_SUCCESS(result, result);
2714 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2716 // remember some selection points
2717 nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
2718 int32_t selStartOffset, selEndOffset;
2719 result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset);
2720 if (NS_FAILED(result)) selStartNode = nullptr; // if selection is cleared, remember that
2721 result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset);
2722 if (NS_FAILED(result)) selStartNode = nullptr; // if selection is cleared, remember that
2724 nsCOMPtr<nsIDOMNode> resultNode;
2725 result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode));
2726 //printf(" after insert\n"); content->List(); // DEBUG
2727 if (NS_SUCCEEDED(result))
2728 {
2729 // split the children between the 2 nodes
2730 // at this point, aExistingRightNode has all the children
2731 // move all the children whose index is < aOffset to aNewLeftNode
2732 if (0<=aOffset) // don't bother unless we're going to move at least one child
2733 {
2734 // if it's a text node, just shuffle around some text
2735 nsCOMPtr<nsIDOMCharacterData> rightNodeAsText( do_QueryInterface(aExistingRightNode) );
2736 nsCOMPtr<nsIDOMCharacterData> leftNodeAsText( do_QueryInterface(aNewLeftNode) );
2737 if (leftNodeAsText && rightNodeAsText)
2738 {
2739 // fix right node
2740 nsAutoString leftText;
2741 rightNodeAsText->SubstringData(0, aOffset, leftText);
2742 rightNodeAsText->DeleteData(0, aOffset);
2743 // fix left node
2744 leftNodeAsText->SetData(leftText);
2745 // moose
2746 }
2747 else
2748 { // otherwise it's an interior node, so shuffle around the children
2749 // go through list backwards so deletes don't interfere with the iteration
2750 nsCOMPtr<nsIDOMNodeList> childNodes;
2751 result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes));
2752 if ((NS_SUCCEEDED(result)) && (childNodes))
2753 {
2754 int32_t i=aOffset-1;
2755 for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--)
2756 {
2757 nsCOMPtr<nsIDOMNode> childNode;
2758 result = childNodes->Item(i, getter_AddRefs(childNode));
2759 if ((NS_SUCCEEDED(result)) && (childNode))
2760 {
2761 result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode));
2762 //printf(" after remove\n"); content->List(); // DEBUG
2763 if (NS_SUCCEEDED(result))
2764 {
2765 nsCOMPtr<nsIDOMNode> firstChild;
2766 aNewLeftNode->GetFirstChild(getter_AddRefs(firstChild));
2767 result = aNewLeftNode->InsertBefore(childNode, firstChild, getter_AddRefs(resultNode));
2768 //printf(" after append\n"); content->List(); // DEBUG
2769 }
2770 }
2771 }
2772 }
2773 }
2774 // handle selection
2775 nsCOMPtr<nsIPresShell> ps = GetPresShell();
2776 if (ps)
2777 ps->FlushPendingNotifications(Flush_Frames);
2779 if (GetShouldTxnSetSelection())
2780 {
2781 // editor wants us to set selection at split point
2782 selection->Collapse(aNewLeftNode, aOffset);
2783 }
2784 else if (selStartNode)
2785 {
2786 // else adjust the selection if needed. if selStartNode is null, then there was no selection.
2787 // HACK: this is overly simplified - multi-range selections need more work than this
2788 if (selStartNode.get() == aExistingRightNode)
2789 {
2790 if (selStartOffset < aOffset)
2791 {
2792 selStartNode = aNewLeftNode;
2793 }
2794 else
2795 {
2796 selStartOffset -= aOffset;
2797 }
2798 }
2799 if (selEndNode.get() == aExistingRightNode)
2800 {
2801 if (selEndOffset < aOffset)
2802 {
2803 selEndNode = aNewLeftNode;
2804 }
2805 else
2806 {
2807 selEndOffset -= aOffset;
2808 }
2809 }
2810 selection->Collapse(selStartNode,selStartOffset);
2811 selection->Extend(selEndNode,selEndOffset);
2812 }
2813 }
2814 }
2815 }
2816 else
2817 result = NS_ERROR_INVALID_ARG;
2819 return result;
2820 }
2822 nsresult
2823 nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
2824 nsINode* aNodeToJoin,
2825 nsINode* aParent)
2826 {
2827 MOZ_ASSERT(aNodeToKeep);
2828 MOZ_ASSERT(aNodeToJoin);
2829 MOZ_ASSERT(aParent);
2831 nsRefPtr<Selection> selection = GetSelection();
2832 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2834 // remember some selection points
2835 nsCOMPtr<nsINode> selStartNode;
2836 int32_t selStartOffset;
2837 nsresult result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset);
2838 if (NS_FAILED(result)) {
2839 selStartNode = nullptr;
2840 }
2842 nsCOMPtr<nsINode> selEndNode;
2843 int32_t selEndOffset;
2844 result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset);
2845 // Joe or Kin should comment here on why the following line is not a copy/paste error
2846 if (NS_FAILED(result)) {
2847 selStartNode = nullptr;
2848 }
2850 uint32_t firstNodeLength = aNodeToJoin->Length();
2852 int32_t joinOffset;
2853 GetNodeLocation(aNodeToJoin, &joinOffset);
2854 int32_t keepOffset;
2855 nsINode* parent = GetNodeLocation(aNodeToKeep, &keepOffset);
2857 // if selection endpoint is between the nodes, remember it as being
2858 // in the one that is going away instead. This simplifies later selection
2859 // adjustment logic at end of this method.
2860 if (selStartNode) {
2861 if (selStartNode == parent &&
2862 joinOffset < selStartOffset && selStartOffset <= keepOffset) {
2863 selStartNode = aNodeToJoin;
2864 selStartOffset = firstNodeLength;
2865 }
2866 if (selEndNode == parent &&
2867 joinOffset < selEndOffset && selEndOffset <= keepOffset) {
2868 selEndNode = aNodeToJoin;
2869 selEndOffset = firstNodeLength;
2870 }
2871 }
2873 // ok, ready to do join now.
2874 // if it's a text node, just shuffle around some text
2875 nsCOMPtr<nsIDOMCharacterData> keepNodeAsText( do_QueryInterface(aNodeToKeep) );
2876 nsCOMPtr<nsIDOMCharacterData> joinNodeAsText( do_QueryInterface(aNodeToJoin) );
2877 if (keepNodeAsText && joinNodeAsText) {
2878 nsAutoString rightText;
2879 nsAutoString leftText;
2880 keepNodeAsText->GetData(rightText);
2881 joinNodeAsText->GetData(leftText);
2882 leftText += rightText;
2883 keepNodeAsText->SetData(leftText);
2884 } else {
2885 // otherwise it's an interior node, so shuffle around the children
2886 nsCOMPtr<nsINodeList> childNodes = aNodeToJoin->ChildNodes();
2887 MOZ_ASSERT(childNodes);
2889 // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
2890 // GetFirstChild returns nullptr firstNode if aNodeToKeep has no children, that's ok.
2891 nsCOMPtr<nsIContent> firstNode = aNodeToKeep->GetFirstChild();
2893 // have to go through the list backwards to keep deletes from interfering with iteration
2894 for (uint32_t i = childNodes->Length(); i > 0; --i) {
2895 nsCOMPtr<nsIContent> childNode = childNodes->Item(i - 1);
2896 if (childNode) {
2897 // prepend children of aNodeToJoin
2898 ErrorResult err;
2899 aNodeToKeep->InsertBefore(*childNode, firstNode, err);
2900 NS_ENSURE_SUCCESS(err.ErrorCode(), err.ErrorCode());
2901 firstNode = childNode.forget();
2902 }
2903 }
2904 }
2906 // delete the extra node
2907 ErrorResult err;
2908 aParent->RemoveChild(*aNodeToJoin, err);
2910 if (GetShouldTxnSetSelection()) {
2911 // editor wants us to set selection at join point
2912 selection->Collapse(aNodeToKeep, SafeCast<int32_t>(firstNodeLength));
2913 } else if (selStartNode) {
2914 // and adjust the selection if needed
2915 // HACK: this is overly simplified - multi-range selections need more work than this
2916 bool bNeedToAdjust = false;
2918 // check to see if we joined nodes where selection starts
2919 if (selStartNode == aNodeToJoin) {
2920 bNeedToAdjust = true;
2921 selStartNode = aNodeToKeep;
2922 } else if (selStartNode == aNodeToKeep) {
2923 bNeedToAdjust = true;
2924 selStartOffset += firstNodeLength;
2925 }
2927 // check to see if we joined nodes where selection ends
2928 if (selEndNode == aNodeToJoin) {
2929 bNeedToAdjust = true;
2930 selEndNode = aNodeToKeep;
2931 } else if (selEndNode == aNodeToKeep) {
2932 bNeedToAdjust = true;
2933 selEndOffset += firstNodeLength;
2934 }
2936 // adjust selection if needed
2937 if (bNeedToAdjust) {
2938 selection->Collapse(selStartNode, selStartOffset);
2939 selection->Extend(selEndNode, selEndOffset);
2940 }
2941 }
2943 return err.ErrorCode();
2944 }
2947 int32_t
2948 nsEditor::GetChildOffset(nsIDOMNode* aChild, nsIDOMNode* aParent)
2949 {
2950 MOZ_ASSERT(aChild && aParent);
2952 nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
2953 nsCOMPtr<nsINode> child = do_QueryInterface(aChild);
2954 MOZ_ASSERT(parent && child);
2956 int32_t idx = parent->IndexOf(child);
2957 MOZ_ASSERT(idx != -1);
2958 return idx;
2959 }
2961 // static
2962 already_AddRefed<nsIDOMNode>
2963 nsEditor::GetNodeLocation(nsIDOMNode* aChild, int32_t* outOffset)
2964 {
2965 MOZ_ASSERT(aChild && outOffset);
2966 NS_ENSURE_TRUE(aChild && outOffset, nullptr);
2967 *outOffset = -1;
2969 nsCOMPtr<nsIDOMNode> parent;
2971 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
2972 aChild->GetParentNode(getter_AddRefs(parent))));
2973 if (parent) {
2974 *outOffset = GetChildOffset(aChild, parent);
2975 }
2977 return parent.forget();
2978 }
2980 nsINode*
2981 nsEditor::GetNodeLocation(nsINode* aChild, int32_t* aOffset)
2982 {
2983 MOZ_ASSERT(aChild);
2984 MOZ_ASSERT(aOffset);
2986 nsINode* parent = aChild->GetParentNode();
2987 if (parent) {
2988 *aOffset = parent->IndexOf(aChild);
2989 MOZ_ASSERT(*aOffset != -1);
2990 } else {
2991 *aOffset = -1;
2992 }
2993 return parent;
2994 }
2996 // returns the number of things inside aNode.
2997 // If aNode is text, returns number of characters. If not, returns number of children nodes.
2998 nsresult
2999 nsEditor::GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount)
3000 {
3001 aCount = 0;
3002 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
3003 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
3004 aCount = node->Length();
3005 return NS_OK;
3006 }
3009 nsresult
3010 nsEditor::GetPriorNode(nsIDOMNode *aParentNode,
3011 int32_t aOffset,
3012 bool aEditableNode,
3013 nsCOMPtr<nsIDOMNode> *aResultNode,
3014 bool bNoBlockCrossing)
3015 {
3016 NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
3017 *aResultNode = nullptr;
3019 nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
3020 NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
3022 *aResultNode = do_QueryInterface(GetPriorNode(parentNode, aOffset,
3023 aEditableNode,
3024 bNoBlockCrossing));
3025 return NS_OK;
3026 }
3028 nsIContent*
3029 nsEditor::GetPriorNode(nsINode* aParentNode,
3030 int32_t aOffset,
3031 bool aEditableNode,
3032 bool aNoBlockCrossing)
3033 {
3034 MOZ_ASSERT(aParentNode);
3036 // If we are at the beginning of the node, or it is a text node, then just
3037 // look before it.
3038 if (!aOffset || aParentNode->NodeType() == nsIDOMNode::TEXT_NODE) {
3039 if (aNoBlockCrossing && IsBlockNode(aParentNode)) {
3040 // If we aren't allowed to cross blocks, don't look before this block.
3041 return nullptr;
3042 }
3043 return GetPriorNode(aParentNode, aEditableNode, aNoBlockCrossing);
3044 }
3046 // else look before the child at 'aOffset'
3047 if (nsIContent* child = aParentNode->GetChildAt(aOffset)) {
3048 return GetPriorNode(child, aEditableNode, aNoBlockCrossing);
3049 }
3051 // unless there isn't one, in which case we are at the end of the node
3052 // and want the deep-right child.
3053 nsIContent* resultNode = GetRightmostChild(aParentNode, aNoBlockCrossing);
3054 if (!resultNode || !aEditableNode || IsEditable(resultNode)) {
3055 return resultNode;
3056 }
3058 // restart the search from the non-editable node we just found
3059 return GetPriorNode(resultNode, aEditableNode, aNoBlockCrossing);
3060 }
3063 nsresult
3064 nsEditor::GetNextNode(nsIDOMNode *aParentNode,
3065 int32_t aOffset,
3066 bool aEditableNode,
3067 nsCOMPtr<nsIDOMNode> *aResultNode,
3068 bool bNoBlockCrossing)
3069 {
3070 NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
3071 *aResultNode = nullptr;
3073 nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
3074 NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
3076 *aResultNode = do_QueryInterface(GetNextNode(parentNode, aOffset,
3077 aEditableNode,
3078 bNoBlockCrossing));
3079 return NS_OK;
3080 }
3082 nsIContent*
3083 nsEditor::GetNextNode(nsINode* aParentNode,
3084 int32_t aOffset,
3085 bool aEditableNode,
3086 bool aNoBlockCrossing)
3087 {
3088 MOZ_ASSERT(aParentNode);
3090 // if aParentNode is a text node, use its location instead
3091 if (aParentNode->NodeType() == nsIDOMNode::TEXT_NODE) {
3092 nsINode* parent = aParentNode->GetParentNode();
3093 NS_ENSURE_TRUE(parent, nullptr);
3094 aOffset = parent->IndexOf(aParentNode) + 1; // _after_ the text node
3095 aParentNode = parent;
3096 }
3098 // look at the child at 'aOffset'
3099 nsIContent* child = aParentNode->GetChildAt(aOffset);
3100 if (child) {
3101 if (aNoBlockCrossing && IsBlockNode(child)) {
3102 return child;
3103 }
3105 nsIContent* resultNode = GetLeftmostChild(child, aNoBlockCrossing);
3106 if (!resultNode) {
3107 return child;
3108 }
3110 if (!IsDescendantOfEditorRoot(resultNode)) {
3111 return nullptr;
3112 }
3114 if (!aEditableNode || IsEditable(resultNode)) {
3115 return resultNode;
3116 }
3118 // restart the search from the non-editable node we just found
3119 return GetNextNode(resultNode, aEditableNode, aNoBlockCrossing);
3120 }
3122 // unless there isn't one, in which case we are at the end of the node
3123 // and want the next one.
3124 if (aNoBlockCrossing && IsBlockNode(aParentNode)) {
3125 // don't cross out of parent block
3126 return nullptr;
3127 }
3129 return GetNextNode(aParentNode, aEditableNode, aNoBlockCrossing);
3130 }
3133 nsresult
3134 nsEditor::GetPriorNode(nsIDOMNode *aCurrentNode,
3135 bool aEditableNode,
3136 nsCOMPtr<nsIDOMNode> *aResultNode,
3137 bool bNoBlockCrossing)
3138 {
3139 NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
3141 nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
3142 NS_ENSURE_TRUE(currentNode, NS_ERROR_NULL_POINTER);
3144 *aResultNode = do_QueryInterface(GetPriorNode(currentNode, aEditableNode,
3145 bNoBlockCrossing));
3146 return NS_OK;
3147 }
3149 nsIContent*
3150 nsEditor::GetPriorNode(nsINode* aCurrentNode, bool aEditableNode,
3151 bool aNoBlockCrossing /* = false */)
3152 {
3153 MOZ_ASSERT(aCurrentNode);
3155 if (!IsDescendantOfEditorRoot(aCurrentNode)) {
3156 return nullptr;
3157 }
3159 return FindNode(aCurrentNode, false, aEditableNode, aNoBlockCrossing);
3160 }
3162 nsIContent*
3163 nsEditor::FindNextLeafNode(nsINode *aCurrentNode,
3164 bool aGoForward,
3165 bool bNoBlockCrossing)
3166 {
3167 // called only by GetPriorNode so we don't need to check params.
3168 NS_PRECONDITION(IsDescendantOfEditorRoot(aCurrentNode) &&
3169 !IsEditorRoot(aCurrentNode),
3170 "Bogus arguments");
3172 nsINode* cur = aCurrentNode;
3173 for (;;) {
3174 // if aCurrentNode has a sibling in the right direction, return
3175 // that sibling's closest child (or itself if it has no children)
3176 nsIContent* sibling =
3177 aGoForward ? cur->GetNextSibling() : cur->GetPreviousSibling();
3178 if (sibling) {
3179 if (bNoBlockCrossing && IsBlockNode(sibling)) {
3180 // don't look inside prevsib, since it is a block
3181 return sibling;
3182 }
3183 nsIContent *leaf =
3184 aGoForward ? GetLeftmostChild(sibling, bNoBlockCrossing) :
3185 GetRightmostChild(sibling, bNoBlockCrossing);
3186 if (!leaf) {
3187 return sibling;
3188 }
3190 return leaf;
3191 }
3193 nsINode *parent = cur->GetParentNode();
3194 if (!parent) {
3195 return nullptr;
3196 }
3198 NS_ASSERTION(IsDescendantOfEditorRoot(parent),
3199 "We started with a proper descendant of root, and should stop "
3200 "if we ever hit the root, so we better have a descendant of "
3201 "root now!");
3202 if (IsEditorRoot(parent) ||
3203 (bNoBlockCrossing && IsBlockNode(parent))) {
3204 return nullptr;
3205 }
3207 cur = parent;
3208 }
3210 NS_NOTREACHED("What part of for(;;) do you not understand?");
3211 return nullptr;
3212 }
3214 nsresult
3215 nsEditor::GetNextNode(nsIDOMNode* aCurrentNode,
3216 bool aEditableNode,
3217 nsCOMPtr<nsIDOMNode> *aResultNode,
3218 bool bNoBlockCrossing)
3219 {
3220 nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
3221 if (!currentNode || !aResultNode) {
3222 return NS_ERROR_NULL_POINTER;
3223 }
3225 *aResultNode = do_QueryInterface(GetNextNode(currentNode, aEditableNode,
3226 bNoBlockCrossing));
3227 return NS_OK;
3228 }
3230 nsIContent*
3231 nsEditor::GetNextNode(nsINode* aCurrentNode,
3232 bool aEditableNode,
3233 bool bNoBlockCrossing)
3234 {
3235 MOZ_ASSERT(aCurrentNode);
3237 if (!IsDescendantOfEditorRoot(aCurrentNode)) {
3238 return nullptr;
3239 }
3241 return FindNode(aCurrentNode, true, aEditableNode, bNoBlockCrossing);
3242 }
3244 nsIContent*
3245 nsEditor::FindNode(nsINode *aCurrentNode,
3246 bool aGoForward,
3247 bool aEditableNode,
3248 bool bNoBlockCrossing)
3249 {
3250 if (IsEditorRoot(aCurrentNode)) {
3251 // Don't allow traversal above the root node! This helps
3252 // prevent us from accidentally editing browser content
3253 // when the editor is in a text widget.
3255 return nullptr;
3256 }
3258 nsCOMPtr<nsIContent> candidate =
3259 FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing);
3261 if (!candidate) {
3262 return nullptr;
3263 }
3265 if (!aEditableNode || IsEditable(candidate)) {
3266 return candidate;
3267 }
3269 return FindNode(candidate, aGoForward, aEditableNode, bNoBlockCrossing);
3270 }
3272 nsIDOMNode*
3273 nsEditor::GetRightmostChild(nsIDOMNode* aCurrentNode,
3274 bool bNoBlockCrossing)
3275 {
3276 nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
3277 nsIContent* result = GetRightmostChild(currentNode, bNoBlockCrossing);
3278 return result ? result->AsDOMNode() : nullptr;
3279 }
3281 nsIContent*
3282 nsEditor::GetRightmostChild(nsINode *aCurrentNode,
3283 bool bNoBlockCrossing)
3284 {
3285 NS_ENSURE_TRUE(aCurrentNode, nullptr);
3286 nsIContent *cur = aCurrentNode->GetLastChild();
3287 if (!cur) {
3288 return nullptr;
3289 }
3290 for (;;) {
3291 if (bNoBlockCrossing && IsBlockNode(cur)) {
3292 return cur;
3293 }
3294 nsIContent* next = cur->GetLastChild();
3295 if (!next) {
3296 return cur;
3297 }
3298 cur = next;
3299 }
3301 NS_NOTREACHED("What part of for(;;) do you not understand?");
3302 return nullptr;
3303 }
3305 nsIContent*
3306 nsEditor::GetLeftmostChild(nsINode *aCurrentNode,
3307 bool bNoBlockCrossing)
3308 {
3309 NS_ENSURE_TRUE(aCurrentNode, nullptr);
3310 nsIContent *cur = aCurrentNode->GetFirstChild();
3311 if (!cur) {
3312 return nullptr;
3313 }
3314 for (;;) {
3315 if (bNoBlockCrossing && IsBlockNode(cur)) {
3316 return cur;
3317 }
3318 nsIContent *next = cur->GetFirstChild();
3319 if (!next) {
3320 return cur;
3321 }
3322 cur = next;
3323 }
3325 NS_NOTREACHED("What part of for(;;) do you not understand?");
3326 return nullptr;
3327 }
3329 nsIDOMNode*
3330 nsEditor::GetLeftmostChild(nsIDOMNode* aCurrentNode,
3331 bool bNoBlockCrossing)
3332 {
3333 nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
3334 nsIContent* result = GetLeftmostChild(currentNode, bNoBlockCrossing);
3335 return result ? result->AsDOMNode() : nullptr;
3336 }
3338 bool
3339 nsEditor::IsBlockNode(nsIDOMNode* aNode)
3340 {
3341 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
3342 return IsBlockNode(node);
3343 }
3345 bool
3346 nsEditor::IsBlockNode(nsINode* aNode)
3347 {
3348 // stub to be overridden in nsHTMLEditor.
3349 // screwing around with the class hierarchy here in order
3350 // to not duplicate the code in GetNextNode/GetPrevNode
3351 // across both nsEditor/nsHTMLEditor.
3352 return false;
3353 }
3355 bool
3356 nsEditor::CanContain(nsIDOMNode* aParent, nsIDOMNode* aChild)
3357 {
3358 nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent);
3359 NS_ENSURE_TRUE(parent, false);
3361 switch (parent->NodeType()) {
3362 case nsIDOMNode::ELEMENT_NODE:
3363 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
3364 return TagCanContain(parent->Tag(), aChild);
3365 }
3366 return false;
3367 }
3369 bool
3370 nsEditor::CanContainTag(nsIDOMNode* aParent, nsIAtom* aChildTag)
3371 {
3372 nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent);
3373 NS_ENSURE_TRUE(parent, false);
3375 switch (parent->NodeType()) {
3376 case nsIDOMNode::ELEMENT_NODE:
3377 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
3378 return TagCanContainTag(parent->Tag(), aChildTag);
3379 }
3380 return false;
3381 }
3383 bool
3384 nsEditor::TagCanContain(nsIAtom* aParentTag, nsIDOMNode* aChild)
3385 {
3386 nsCOMPtr<nsIContent> child = do_QueryInterface(aChild);
3387 NS_ENSURE_TRUE(child, false);
3389 switch (child->NodeType()) {
3390 case nsIDOMNode::TEXT_NODE:
3391 case nsIDOMNode::ELEMENT_NODE:
3392 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
3393 return TagCanContainTag(aParentTag, child->Tag());
3394 }
3395 return false;
3396 }
3398 bool
3399 nsEditor::TagCanContainTag(nsIAtom* aParentTag, nsIAtom* aChildTag)
3400 {
3401 return true;
3402 }
3404 bool
3405 nsEditor::IsRoot(nsIDOMNode* inNode)
3406 {
3407 NS_ENSURE_TRUE(inNode, false);
3409 nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot());
3411 return inNode == rootNode;
3412 }
3414 bool
3415 nsEditor::IsRoot(nsINode* inNode)
3416 {
3417 NS_ENSURE_TRUE(inNode, false);
3419 nsCOMPtr<nsINode> rootNode = GetRoot();
3421 return inNode == rootNode;
3422 }
3424 bool
3425 nsEditor::IsEditorRoot(nsINode* aNode)
3426 {
3427 NS_ENSURE_TRUE(aNode, false);
3428 nsCOMPtr<nsINode> rootNode = GetEditorRoot();
3429 return aNode == rootNode;
3430 }
3432 bool
3433 nsEditor::IsDescendantOfRoot(nsIDOMNode* inNode)
3434 {
3435 nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
3436 return IsDescendantOfRoot(node);
3437 }
3439 bool
3440 nsEditor::IsDescendantOfRoot(nsINode* inNode)
3441 {
3442 NS_ENSURE_TRUE(inNode, false);
3443 nsCOMPtr<nsIContent> root = GetRoot();
3444 NS_ENSURE_TRUE(root, false);
3446 return nsContentUtils::ContentIsDescendantOf(inNode, root);
3447 }
3449 bool
3450 nsEditor::IsDescendantOfEditorRoot(nsIDOMNode* aNode)
3451 {
3452 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
3453 return IsDescendantOfEditorRoot(node);
3454 }
3456 bool
3457 nsEditor::IsDescendantOfEditorRoot(nsINode* aNode)
3458 {
3459 NS_ENSURE_TRUE(aNode, false);
3460 nsCOMPtr<nsIContent> root = GetEditorRoot();
3461 NS_ENSURE_TRUE(root, false);
3463 return nsContentUtils::ContentIsDescendantOf(aNode, root);
3464 }
3466 bool
3467 nsEditor::IsContainer(nsIDOMNode *aNode)
3468 {
3469 return aNode ? true : false;
3470 }
3472 static inline bool
3473 IsElementVisible(dom::Element* aElement)
3474 {
3475 if (aElement->GetPrimaryFrame()) {
3476 // It's visible, for our purposes
3477 return true;
3478 }
3480 nsIContent *cur = aElement;
3481 for (; ;) {
3482 // Walk up the tree looking for the nearest ancestor with a frame.
3483 // The state of the child right below it will determine whether
3484 // we might possibly have a frame or not.
3485 bool haveLazyBitOnChild = cur->HasFlag(NODE_NEEDS_FRAME);
3486 cur = cur->GetFlattenedTreeParent();
3487 if (!cur) {
3488 if (!haveLazyBitOnChild) {
3489 // None of our ancestors have lazy bits set, so we shouldn't
3490 // have a frame
3491 return false;
3492 }
3494 // The root has a lazy frame construction bit. We need to check
3495 // our style.
3496 break;
3497 }
3499 if (cur->GetPrimaryFrame()) {
3500 if (!haveLazyBitOnChild) {
3501 // Our ancestor directly under |cur| doesn't have lazy bits;
3502 // that means we won't get a frame
3503 return false;
3504 }
3506 if (cur->GetPrimaryFrame()->IsLeaf()) {
3507 // Nothing under here will ever get frames
3508 return false;
3509 }
3511 // Otherwise, we might end up with a frame when that lazy bit is
3512 // processed. Figure out our actual style.
3513 break;
3514 }
3515 }
3517 // Now it might be that we have no frame because we're in a
3518 // display:none subtree, or it might be that we're just dealing with
3519 // lazy frame construction and it hasn't happened yet. Check which
3520 // one it is.
3521 nsRefPtr<nsStyleContext> styleContext =
3522 nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
3523 nullptr, nullptr);
3524 if (styleContext) {
3525 return styleContext->StyleDisplay()->mDisplay != NS_STYLE_DISPLAY_NONE;
3526 }
3527 return false;
3528 }
3530 bool
3531 nsEditor::IsEditable(nsIDOMNode *aNode)
3532 {
3533 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
3534 return IsEditable(content);
3535 }
3537 bool
3538 nsEditor::IsEditable(nsIContent *aNode)
3539 {
3540 NS_ENSURE_TRUE(aNode, false);
3542 if (IsMozEditorBogusNode(aNode) || !IsModifiableNode(aNode)) return false;
3544 // see if it has a frame. If so, we'll edit it.
3545 // special case for textnodes: frame must have width.
3546 if (aNode->IsElement() && !IsElementVisible(aNode->AsElement())) {
3547 // If the element has no frame, it's not editable. Note that we
3548 // need to check IsElement() here, because some of our tests
3549 // rely on frameless textnodes being visible.
3550 return false;
3551 }
3552 switch (aNode->NodeType()) {
3553 case nsIDOMNode::ELEMENT_NODE:
3554 case nsIDOMNode::TEXT_NODE:
3555 return true; // element or text node; not invisible
3556 default:
3557 return false;
3558 }
3559 }
3561 bool
3562 nsEditor::IsMozEditorBogusNode(nsIContent *element)
3563 {
3564 return element &&
3565 element->AttrValueIs(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
3566 kMOZEditorBogusNodeValue, eCaseMatters);
3567 }
3569 uint32_t
3570 nsEditor::CountEditableChildren(nsINode* aNode)
3571 {
3572 MOZ_ASSERT(aNode);
3573 uint32_t count = 0;
3574 for (nsIContent* child = aNode->GetFirstChild();
3575 child;
3576 child = child->GetNextSibling()) {
3577 if (IsEditable(child)) {
3578 ++count;
3579 }
3580 }
3581 return count;
3582 }
3584 //END nsEditor static utility methods
3587 NS_IMETHODIMP nsEditor::IncrementModificationCount(int32_t inNumMods)
3588 {
3589 uint32_t oldModCount = mModCount;
3591 mModCount += inNumMods;
3593 if ((oldModCount == 0 && mModCount != 0)
3594 || (oldModCount != 0 && mModCount == 0))
3595 NotifyDocumentListeners(eDocumentStateChanged);
3596 return NS_OK;
3597 }
3600 NS_IMETHODIMP nsEditor::GetModificationCount(int32_t *outModCount)
3601 {
3602 NS_ENSURE_ARG_POINTER(outModCount);
3603 *outModCount = mModCount;
3604 return NS_OK;
3605 }
3608 NS_IMETHODIMP nsEditor::ResetModificationCount()
3609 {
3610 bool doNotify = (mModCount != 0);
3612 mModCount = 0;
3614 if (doNotify)
3615 NotifyDocumentListeners(eDocumentStateChanged);
3616 return NS_OK;
3617 }
3619 //END nsEditor Private methods
3623 ///////////////////////////////////////////////////////////////////////////
3624 // GetTag: digs out the atom for the tag of this node
3625 //
3626 nsIAtom *
3627 nsEditor::GetTag(nsIDOMNode *aNode)
3628 {
3629 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
3631 if (!content)
3632 {
3633 NS_ASSERTION(aNode, "null node passed to nsEditor::Tag()");
3635 return nullptr;
3636 }
3638 return content->Tag();
3639 }
3642 ///////////////////////////////////////////////////////////////////////////
3643 // GetTagString: digs out string for the tag of this node
3644 //
3645 nsresult
3646 nsEditor::GetTagString(nsIDOMNode *aNode, nsAString& outString)
3647 {
3648 if (!aNode)
3649 {
3650 NS_NOTREACHED("null node passed to nsEditor::GetTag()");
3651 return NS_ERROR_NULL_POINTER;
3652 }
3654 nsIAtom *atom = GetTag(aNode);
3655 if (!atom)
3656 {
3657 return NS_ERROR_FAILURE;
3658 }
3660 atom->ToString(outString);
3661 return NS_OK;
3662 }
3665 ///////////////////////////////////////////////////////////////////////////
3666 // NodesSameType: do these nodes have the same tag?
3667 //
3668 bool
3669 nsEditor::NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
3670 {
3671 if (!aNode1 || !aNode2) {
3672 NS_NOTREACHED("null node passed to nsEditor::NodesSameType()");
3673 return false;
3674 }
3676 nsCOMPtr<nsIContent> content1 = do_QueryInterface(aNode1);
3677 NS_ENSURE_TRUE(content1, false);
3679 nsCOMPtr<nsIContent> content2 = do_QueryInterface(aNode2);
3680 NS_ENSURE_TRUE(content2, false);
3682 return AreNodesSameType(content1, content2);
3683 }
3685 /* virtual */
3686 bool
3687 nsEditor::AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2)
3688 {
3689 MOZ_ASSERT(aNode1);
3690 MOZ_ASSERT(aNode2);
3691 return aNode1->Tag() == aNode2->Tag();
3692 }
3695 ///////////////////////////////////////////////////////////////////////////
3696 // IsTextNode: true if node of dom type text
3697 //
3698 bool
3699 nsEditor::IsTextNode(nsIDOMNode *aNode)
3700 {
3701 if (!aNode)
3702 {
3703 NS_NOTREACHED("null node passed to IsTextNode()");
3704 return false;
3705 }
3707 uint16_t nodeType;
3708 aNode->GetNodeType(&nodeType);
3709 return (nodeType == nsIDOMNode::TEXT_NODE);
3710 }
3712 bool
3713 nsEditor::IsTextNode(nsINode *aNode)
3714 {
3715 return aNode->NodeType() == nsIDOMNode::TEXT_NODE;
3716 }
3718 ///////////////////////////////////////////////////////////////////////////
3719 // GetChildAt: returns the node at this position index in the parent
3720 //
3721 nsCOMPtr<nsIDOMNode>
3722 nsEditor::GetChildAt(nsIDOMNode *aParent, int32_t aOffset)
3723 {
3724 nsCOMPtr<nsIDOMNode> resultNode;
3726 nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent);
3728 NS_ENSURE_TRUE(parent, resultNode);
3730 resultNode = do_QueryInterface(parent->GetChildAt(aOffset));
3732 return resultNode;
3733 }
3735 ///////////////////////////////////////////////////////////////////////////
3736 // GetNodeAtRangeOffsetPoint: returns the node at this position in a range,
3737 // assuming that aParentOrNode is the node itself if it's a text node, or
3738 // the node's parent otherwise.
3739 //
3740 nsCOMPtr<nsIDOMNode>
3741 nsEditor::GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode, int32_t aOffset)
3742 {
3743 if (IsTextNode(aParentOrNode)) {
3744 return aParentOrNode;
3745 }
3746 return GetChildAt(aParentOrNode, aOffset);
3747 }
3750 ///////////////////////////////////////////////////////////////////////////
3751 // GetStartNodeAndOffset: returns whatever the start parent & offset is of
3752 // the first range in the selection.
3753 nsresult
3754 nsEditor::GetStartNodeAndOffset(nsISelection *aSelection,
3755 nsIDOMNode **outStartNode,
3756 int32_t *outStartOffset)
3757 {
3758 NS_ENSURE_TRUE(outStartNode && outStartOffset && aSelection, NS_ERROR_NULL_POINTER);
3760 nsCOMPtr<nsINode> startNode;
3761 nsresult rv = GetStartNodeAndOffset(static_cast<Selection*>(aSelection),
3762 getter_AddRefs(startNode),
3763 outStartOffset);
3764 NS_ENSURE_SUCCESS(rv, rv);
3766 if (startNode) {
3767 NS_ADDREF(*outStartNode = startNode->AsDOMNode());
3768 } else {
3769 *outStartNode = nullptr;
3770 }
3771 return NS_OK;
3772 }
3774 nsresult
3775 nsEditor::GetStartNodeAndOffset(Selection* aSelection, nsINode** aStartNode,
3776 int32_t* aStartOffset)
3777 {
3778 MOZ_ASSERT(aSelection);
3779 MOZ_ASSERT(aStartNode);
3780 MOZ_ASSERT(aStartOffset);
3782 *aStartNode = nullptr;
3783 *aStartOffset = 0;
3785 NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_ERROR_FAILURE);
3787 const nsRange* range = aSelection->GetRangeAt(0);
3788 NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
3790 NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE);
3792 NS_IF_ADDREF(*aStartNode = range->GetStartParent());
3793 *aStartOffset = range->StartOffset();
3794 return NS_OK;
3795 }
3798 ///////////////////////////////////////////////////////////////////////////
3799 // GetEndNodeAndOffset: returns whatever the end parent & offset is of
3800 // the first range in the selection.
3801 nsresult
3802 nsEditor::GetEndNodeAndOffset(nsISelection *aSelection,
3803 nsIDOMNode **outEndNode,
3804 int32_t *outEndOffset)
3805 {
3806 NS_ENSURE_TRUE(outEndNode && outEndOffset && aSelection, NS_ERROR_NULL_POINTER);
3808 nsCOMPtr<nsINode> endNode;
3809 nsresult rv = GetEndNodeAndOffset(static_cast<Selection*>(aSelection),
3810 getter_AddRefs(endNode),
3811 outEndOffset);
3812 NS_ENSURE_SUCCESS(rv, rv);
3814 if (endNode) {
3815 NS_ADDREF(*outEndNode = endNode->AsDOMNode());
3816 } else {
3817 *outEndNode = nullptr;
3818 }
3819 return NS_OK;
3820 }
3822 nsresult
3823 nsEditor::GetEndNodeAndOffset(Selection* aSelection, nsINode** aEndNode,
3824 int32_t* aEndOffset)
3825 {
3826 MOZ_ASSERT(aSelection);
3827 MOZ_ASSERT(aEndNode);
3828 MOZ_ASSERT(aEndOffset);
3830 *aEndNode = nullptr;
3831 *aEndOffset = 0;
3833 NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_ERROR_FAILURE);
3835 const nsRange* range = aSelection->GetRangeAt(0);
3836 NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
3838 NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE);
3840 NS_IF_ADDREF(*aEndNode = range->GetEndParent());
3841 *aEndOffset = range->EndOffset();
3842 return NS_OK;
3843 }
3846 ///////////////////////////////////////////////////////////////////////////
3847 // IsPreformatted: checks the style info for the node for the preformatted
3848 // text style.
3849 nsresult
3850 nsEditor::IsPreformatted(nsIDOMNode *aNode, bool *aResult)
3851 {
3852 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
3854 NS_ENSURE_TRUE(aResult && content, NS_ERROR_NULL_POINTER);
3856 nsCOMPtr<nsIPresShell> ps = GetPresShell();
3857 NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
3859 // Look at the node (and its parent if it's not an element), and grab its style context
3860 nsRefPtr<nsStyleContext> elementStyle;
3861 if (!content->IsElement()) {
3862 content = content->GetParent();
3863 }
3864 if (content && content->IsElement()) {
3865 elementStyle = nsComputedDOMStyle::GetStyleContextForElementNoFlush(content->AsElement(),
3866 nullptr,
3867 ps);
3868 }
3870 if (!elementStyle)
3871 {
3872 // Consider nodes without a style context to be NOT preformatted:
3873 // For instance, this is true of JS tags inside the body (which show
3874 // up as #text nodes but have no style context).
3875 *aResult = false;
3876 return NS_OK;
3877 }
3879 const nsStyleText* styleText = elementStyle->StyleText();
3881 *aResult = styleText->WhiteSpaceIsSignificant();
3882 return NS_OK;
3883 }
3886 ///////////////////////////////////////////////////////////////////////////
3887 // SplitNodeDeep: this splits a node "deeply", splitting children as
3888 // appropriate. The place to split is represented by
3889 // a dom point at {splitPointParent, splitPointOffset}.
3890 // That dom point must be inside aNode, which is the node to
3891 // split. outOffset is set to the offset in the parent of aNode where
3892 // the split terminates - where you would want to insert
3893 // a new element, for instance, if that's why you were splitting
3894 // the node.
3895 //
3896 nsresult
3897 nsEditor::SplitNodeDeep(nsIDOMNode *aNode,
3898 nsIDOMNode *aSplitPointParent,
3899 int32_t aSplitPointOffset,
3900 int32_t *outOffset,
3901 bool aNoEmptyContainers,
3902 nsCOMPtr<nsIDOMNode> *outLeftNode,
3903 nsCOMPtr<nsIDOMNode> *outRightNode)
3904 {
3905 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
3906 NS_ENSURE_TRUE(node && aSplitPointParent && outOffset, NS_ERROR_NULL_POINTER);
3907 int32_t offset = aSplitPointOffset;
3909 if (outLeftNode) *outLeftNode = nullptr;
3910 if (outRightNode) *outRightNode = nullptr;
3912 nsCOMPtr<nsINode> nodeToSplit = do_QueryInterface(aSplitPointParent);
3913 while (nodeToSplit) {
3914 // need to insert rules code call here to do things like
3915 // not split a list if you are after the last <li> or before the first, etc.
3916 // for now we just have some smarts about unneccessarily splitting
3917 // textnodes, which should be universal enough to put straight in
3918 // this nsEditor routine.
3920 nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToSplit);
3921 uint32_t len = nodeToSplit->Length();
3922 bool bDoSplit = false;
3924 if (!(aNoEmptyContainers || nodeAsText) || (offset && (offset != (int32_t)len)))
3925 {
3926 bDoSplit = true;
3927 nsCOMPtr<nsIDOMNode> tempNode;
3928 nsresult rv = SplitNode(nodeToSplit->AsDOMNode(), offset,
3929 getter_AddRefs(tempNode));
3930 NS_ENSURE_SUCCESS(rv, rv);
3932 if (outRightNode) {
3933 *outRightNode = nodeToSplit->AsDOMNode();
3934 }
3935 if (outLeftNode) {
3936 *outLeftNode = tempNode;
3937 }
3938 }
3940 nsINode* parentNode = nodeToSplit->GetParentNode();
3941 NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE);
3943 if (!bDoSplit && offset) {
3944 // must be "end of text node" case, we didn't split it, just move past it
3945 offset = parentNode->IndexOf(nodeToSplit) + 1;
3946 if (outLeftNode) {
3947 *outLeftNode = nodeToSplit->AsDOMNode();
3948 }
3949 } else {
3950 offset = parentNode->IndexOf(nodeToSplit);
3951 if (outRightNode) {
3952 *outRightNode = nodeToSplit->AsDOMNode();
3953 }
3954 }
3956 if (nodeToSplit == node) {
3957 // we split all the way up to (and including) aNode; we're done
3958 break;
3959 }
3961 nodeToSplit = parentNode;
3962 }
3964 if (!nodeToSplit) {
3965 NS_NOTREACHED("null node obtained in nsEditor::SplitNodeDeep()");
3966 return NS_ERROR_FAILURE;
3967 }
3969 *outOffset = offset;
3970 return NS_OK;
3971 }
3974 ///////////////////////////////////////////////////////////////////////////
3975 // JoinNodeDeep: this joins two like nodes "deeply", joining children as
3976 // appropriate.
3977 nsresult
3978 nsEditor::JoinNodeDeep(nsIDOMNode *aLeftNode,
3979 nsIDOMNode *aRightNode,
3980 nsCOMPtr<nsIDOMNode> *aOutJoinNode,
3981 int32_t *outOffset)
3982 {
3983 NS_ENSURE_TRUE(aLeftNode && aRightNode && aOutJoinNode && outOffset, NS_ERROR_NULL_POINTER);
3985 // while the rightmost children and their descendants of the left node
3986 // match the leftmost children and their descendants of the right node
3987 // join them up. Can you say that three times fast?
3989 nsCOMPtr<nsIDOMNode> leftNodeToJoin = do_QueryInterface(aLeftNode);
3990 nsCOMPtr<nsIDOMNode> rightNodeToJoin = do_QueryInterface(aRightNode);
3991 nsCOMPtr<nsIDOMNode> parentNode,tmp;
3992 nsresult res = NS_OK;
3994 rightNodeToJoin->GetParentNode(getter_AddRefs(parentNode));
3996 while (leftNodeToJoin && rightNodeToJoin && parentNode &&
3997 NodesSameType(leftNodeToJoin, rightNodeToJoin))
3998 {
3999 // adjust out params
4000 uint32_t length;
4001 res = GetLengthOfDOMNode(leftNodeToJoin, length);
4002 NS_ENSURE_SUCCESS(res, res);
4004 *aOutJoinNode = rightNodeToJoin;
4005 *outOffset = length;
4007 // do the join
4008 res = JoinNodes(leftNodeToJoin, rightNodeToJoin, parentNode);
4009 NS_ENSURE_SUCCESS(res, res);
4011 if (IsTextNode(parentNode)) // we've joined all the way down to text nodes, we're done!
4012 return NS_OK;
4014 else
4015 {
4016 // get new left and right nodes, and begin anew
4017 parentNode = rightNodeToJoin;
4018 leftNodeToJoin = GetChildAt(parentNode, length-1);
4019 rightNodeToJoin = GetChildAt(parentNode, length);
4021 // skip over non-editable nodes
4022 while (leftNodeToJoin && !IsEditable(leftNodeToJoin))
4023 {
4024 leftNodeToJoin->GetPreviousSibling(getter_AddRefs(tmp));
4025 leftNodeToJoin = tmp;
4026 }
4027 if (!leftNodeToJoin) break;
4029 while (rightNodeToJoin && !IsEditable(rightNodeToJoin))
4030 {
4031 rightNodeToJoin->GetNextSibling(getter_AddRefs(tmp));
4032 rightNodeToJoin = tmp;
4033 }
4034 if (!rightNodeToJoin) break;
4035 }
4036 }
4038 return res;
4039 }
4041 void
4042 nsEditor::BeginUpdateViewBatch()
4043 {
4044 NS_PRECONDITION(mUpdateCount >= 0, "bad state");
4046 if (0 == mUpdateCount)
4047 {
4048 // Turn off selection updates and notifications.
4050 nsCOMPtr<nsISelection> selection;
4051 GetSelection(getter_AddRefs(selection));
4053 if (selection)
4054 {
4055 nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
4056 selPrivate->StartBatchChanges();
4057 }
4058 }
4060 mUpdateCount++;
4061 }
4064 nsresult nsEditor::EndUpdateViewBatch()
4065 {
4066 NS_PRECONDITION(mUpdateCount > 0, "bad state");
4068 if (mUpdateCount <= 0)
4069 {
4070 mUpdateCount = 0;
4071 return NS_ERROR_FAILURE;
4072 }
4074 mUpdateCount--;
4076 if (0 == mUpdateCount)
4077 {
4078 // Turn selection updating and notifications back on.
4080 nsCOMPtr<nsISelection>selection;
4081 GetSelection(getter_AddRefs(selection));
4083 if (selection) {
4084 nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
4085 selPrivate->EndBatchChanges();
4086 }
4087 }
4089 return NS_OK;
4090 }
4092 bool
4093 nsEditor::GetShouldTxnSetSelection()
4094 {
4095 return mShouldTxnSetSelection;
4096 }
4099 NS_IMETHODIMP
4100 nsEditor::DeleteSelectionImpl(EDirection aAction,
4101 EStripWrappers aStripWrappers)
4102 {
4103 MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
4105 nsCOMPtr<nsISelection>selection;
4106 nsresult res = GetSelection(getter_AddRefs(selection));
4107 NS_ENSURE_SUCCESS(res, res);
4108 nsRefPtr<EditAggregateTxn> txn;
4109 nsCOMPtr<nsINode> deleteNode;
4110 int32_t deleteCharOffset = 0, deleteCharLength = 0;
4111 res = CreateTxnForDeleteSelection(aAction, getter_AddRefs(txn),
4112 getter_AddRefs(deleteNode),
4113 &deleteCharOffset, &deleteCharLength);
4114 nsCOMPtr<nsIDOMCharacterData> deleteCharData(do_QueryInterface(deleteNode));
4116 if (NS_SUCCEEDED(res))
4117 {
4118 nsAutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
4119 int32_t i;
4120 // Notify nsIEditActionListener::WillDelete[Selection|Text|Node]
4121 if (!deleteNode)
4122 for (i = 0; i < mActionListeners.Count(); i++)
4123 mActionListeners[i]->WillDeleteSelection(selection);
4124 else if (deleteCharData)
4125 for (i = 0; i < mActionListeners.Count(); i++)
4126 mActionListeners[i]->WillDeleteText(deleteCharData, deleteCharOffset, 1);
4127 else
4128 for (i = 0; i < mActionListeners.Count(); i++)
4129 mActionListeners[i]->WillDeleteNode(deleteNode->AsDOMNode());
4131 // Delete the specified amount
4132 res = DoTransaction(txn);
4134 // Notify nsIEditActionListener::DidDelete[Selection|Text|Node]
4135 if (!deleteNode)
4136 for (i = 0; i < mActionListeners.Count(); i++)
4137 mActionListeners[i]->DidDeleteSelection(selection);
4138 else if (deleteCharData)
4139 for (i = 0; i < mActionListeners.Count(); i++)
4140 mActionListeners[i]->DidDeleteText(deleteCharData, deleteCharOffset, 1, res);
4141 else
4142 for (i = 0; i < mActionListeners.Count(); i++)
4143 mActionListeners[i]->DidDeleteNode(deleteNode->AsDOMNode(), res);
4144 }
4146 return res;
4147 }
4149 // XXX: error handling in this routine needs to be cleaned up!
4150 NS_IMETHODIMP
4151 nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
4152 nsIDOMNode ** aNewNode)
4153 {
4154 nsresult result = DeleteSelectionAndPrepareToCreateNode();
4155 NS_ENSURE_SUCCESS(result, result);
4157 nsRefPtr<Selection> selection = GetSelection();
4158 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
4160 nsCOMPtr<nsINode> node = selection->GetAnchorNode();
4161 uint32_t offset = selection->AnchorOffset();
4163 nsCOMPtr<nsIDOMNode> newNode;
4164 result = CreateNode(aTag, node->AsDOMNode(), offset,
4165 getter_AddRefs(newNode));
4166 // XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly
4167 // in success/failure cases
4168 *aNewNode = newNode;
4169 NS_IF_ADDREF(*aNewNode);
4171 // we want the selection to be just after the new node
4172 return selection->Collapse(node, offset + 1);
4173 }
4176 /* Non-interface, protected methods */
4178 TextComposition*
4179 nsEditor::GetComposition() const
4180 {
4181 return mComposition;
4182 }
4184 bool
4185 nsEditor::IsIMEComposing() const
4186 {
4187 return mComposition && mComposition->IsComposing();
4188 }
4190 nsresult
4191 nsEditor::DeleteSelectionAndPrepareToCreateNode()
4192 {
4193 nsresult res;
4194 nsRefPtr<Selection> selection = GetSelection();
4195 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
4196 MOZ_ASSERT(selection->GetAnchorFocusRange());
4198 if (!selection->GetAnchorFocusRange()->Collapsed()) {
4199 res = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
4200 NS_ENSURE_SUCCESS(res, res);
4202 MOZ_ASSERT(selection->GetAnchorFocusRange() &&
4203 selection->GetAnchorFocusRange()->Collapsed(),
4204 "Selection not collapsed after delete");
4205 }
4207 // If the selection is a chardata node, split it if necessary and compute
4208 // where to put the new node
4209 nsCOMPtr<nsINode> node = selection->GetAnchorNode();
4210 MOZ_ASSERT(node, "Selection has no ranges in it");
4212 if (node && node->IsNodeOfType(nsINode::eDATA_NODE)) {
4213 NS_ASSERTION(node->GetParentNode(),
4214 "It's impossible to insert into chardata with no parent -- "
4215 "fix the caller");
4216 NS_ENSURE_STATE(node->GetParentNode());
4218 uint32_t offset = selection->AnchorOffset();
4220 if (offset == 0) {
4221 res = selection->Collapse(node->GetParentNode(),
4222 node->GetParentNode()->IndexOf(node));
4223 MOZ_ASSERT(NS_SUCCEEDED(res));
4224 NS_ENSURE_SUCCESS(res, res);
4225 } else if (offset == node->Length()) {
4226 res = selection->Collapse(node->GetParentNode(),
4227 node->GetParentNode()->IndexOf(node) + 1);
4228 MOZ_ASSERT(NS_SUCCEEDED(res));
4229 NS_ENSURE_SUCCESS(res, res);
4230 } else {
4231 nsCOMPtr<nsIDOMNode> tmp;
4232 res = SplitNode(node->AsDOMNode(), offset, getter_AddRefs(tmp));
4233 NS_ENSURE_SUCCESS(res, res);
4234 res = selection->Collapse(node->GetParentNode(),
4235 node->GetParentNode()->IndexOf(node));
4236 MOZ_ASSERT(NS_SUCCEEDED(res));
4237 NS_ENSURE_SUCCESS(res, res);
4238 }
4239 }
4240 return NS_OK;
4241 }
4245 void
4246 nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
4247 {
4248 bool isTransientTransaction;
4249 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
4250 aTxn->GetIsTransient(&isTransientTransaction)));
4252 if (!isTransientTransaction)
4253 {
4254 // we need to deal here with the case where the user saved after some
4255 // edits, then undid one or more times. Then, the undo count is -ve,
4256 // but we can't let a do take it back to zero. So we flip it up to
4257 // a +ve number.
4258 int32_t modCount;
4259 GetModificationCount(&modCount);
4260 if (modCount < 0)
4261 modCount = -modCount;
4263 // don't count transient transactions
4264 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
4265 IncrementModificationCount(1)));
4266 }
4267 }
4270 void
4271 nsEditor::DoAfterUndoTransaction()
4272 {
4273 // all undoable transactions are non-transient
4274 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
4275 IncrementModificationCount(-1)));
4276 }
4278 void
4279 nsEditor::DoAfterRedoTransaction()
4280 {
4281 // all redoable transactions are non-transient
4282 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
4283 IncrementModificationCount(1)));
4284 }
4286 NS_IMETHODIMP
4287 nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement,
4288 const nsAString& aAttribute,
4289 const nsAString& aValue,
4290 ChangeAttributeTxn ** aTxn)
4291 {
4292 NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
4294 nsRefPtr<ChangeAttributeTxn> txn = new ChangeAttributeTxn();
4296 nsresult rv = txn->Init(this, aElement, aAttribute, aValue, false);
4297 if (NS_SUCCEEDED(rv))
4298 {
4299 txn.forget(aTxn);
4300 }
4302 return rv;
4303 }
4306 NS_IMETHODIMP
4307 nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement,
4308 const nsAString& aAttribute,
4309 ChangeAttributeTxn ** aTxn)
4310 {
4311 NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
4313 nsRefPtr<ChangeAttributeTxn> txn = new ChangeAttributeTxn();
4315 nsresult rv = txn->Init(this, aElement, aAttribute, EmptyString(), true);
4316 if (NS_SUCCEEDED(rv))
4317 {
4318 txn.forget(aTxn);
4319 }
4321 return rv;
4322 }
4325 NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsAString& aTag,
4326 nsIDOMNode *aParent,
4327 int32_t aPosition,
4328 CreateElementTxn ** aTxn)
4329 {
4330 NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER);
4332 nsRefPtr<CreateElementTxn> txn = new CreateElementTxn();
4334 nsresult rv = txn->Init(this, aTag, aParent, aPosition);
4335 if (NS_SUCCEEDED(rv))
4336 {
4337 txn.forget(aTxn);
4338 }
4340 return rv;
4341 }
4344 NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode,
4345 nsIDOMNode * aParent,
4346 int32_t aPosition,
4347 InsertElementTxn ** aTxn)
4348 {
4349 NS_ENSURE_TRUE(aNode && aParent, NS_ERROR_NULL_POINTER);
4351 nsRefPtr<InsertElementTxn> txn = new InsertElementTxn();
4353 nsresult rv = txn->Init(aNode, aParent, aPosition, this);
4354 if (NS_SUCCEEDED(rv))
4355 {
4356 txn.forget(aTxn);
4357 }
4359 return rv;
4360 }
4362 nsresult
4363 nsEditor::CreateTxnForDeleteNode(nsINode* aNode, DeleteNodeTxn** aTxn)
4364 {
4365 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
4367 nsRefPtr<DeleteNodeTxn> txn = new DeleteNodeTxn();
4369 nsresult res = txn->Init(this, aNode, &mRangeUpdater);
4370 NS_ENSURE_SUCCESS(res, res);
4372 txn.forget(aTxn);
4373 return NS_OK;
4374 }
4376 NS_IMETHODIMP
4377 nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
4378 IMETextTxn ** aTxn)
4379 {
4380 NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn");
4382 nsRefPtr<IMETextTxn> txn = new IMETextTxn();
4384 // During handling IME composition, mComposition must have been initialized.
4385 // TODO: We can simplify IMETextTxn::Init() with TextComposition class.
4386 nsresult rv = txn->Init(mIMETextNode, mIMETextOffset,
4387 mComposition->String().Length(),
4388 mComposition->GetRanges(), aStringToInsert, this);
4389 if (NS_SUCCEEDED(rv))
4390 {
4391 txn.forget(aTxn);
4392 }
4394 return rv;
4395 }
4398 NS_IMETHODIMP
4399 nsEditor::CreateTxnForAddStyleSheet(nsCSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn)
4400 {
4401 nsRefPtr<AddStyleSheetTxn> txn = new AddStyleSheetTxn();
4403 nsresult rv = txn->Init(this, aSheet);
4404 if (NS_SUCCEEDED(rv))
4405 {
4406 txn.forget(aTxn);
4407 }
4409 return rv;
4410 }
4414 NS_IMETHODIMP
4415 nsEditor::CreateTxnForRemoveStyleSheet(nsCSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn)
4416 {
4417 nsRefPtr<RemoveStyleSheetTxn> txn = new RemoveStyleSheetTxn();
4419 nsresult rv = txn->Init(this, aSheet);
4420 if (NS_SUCCEEDED(rv))
4421 {
4422 txn.forget(aTxn);
4423 }
4425 return rv;
4426 }
4429 nsresult
4430 nsEditor::CreateTxnForDeleteSelection(EDirection aAction,
4431 EditAggregateTxn** aTxn,
4432 nsINode** aNode,
4433 int32_t* aOffset,
4434 int32_t* aLength)
4435 {
4436 MOZ_ASSERT(aTxn);
4437 *aTxn = nullptr;
4439 nsRefPtr<Selection> selection = GetSelection();
4440 NS_ENSURE_STATE(selection);
4442 // Check whether the selection is collapsed and we should do nothing:
4443 if (selection->Collapsed() && aAction == eNone) {
4444 return NS_OK;
4445 }
4447 // allocate the out-param transaction
4448 nsRefPtr<EditAggregateTxn> aggTxn = new EditAggregateTxn();
4450 for (int32_t rangeIdx = 0; rangeIdx < selection->GetRangeCount(); ++rangeIdx) {
4451 nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
4452 NS_ENSURE_STATE(range);
4454 // Same with range as with selection; if it is collapsed and action
4455 // is eNone, do nothing.
4456 if (!range->Collapsed()) {
4457 nsRefPtr<DeleteRangeTxn> txn = new DeleteRangeTxn();
4458 txn->Init(this, range, &mRangeUpdater);
4459 aggTxn->AppendChild(txn);
4460 } else if (aAction != eNone) {
4461 // we have an insertion point. delete the thing in front of it or
4462 // behind it, depending on aAction
4463 nsresult res = CreateTxnForDeleteInsertionPoint(range, aAction, aggTxn,
4464 aNode, aOffset, aLength);
4465 NS_ENSURE_SUCCESS(res, res);
4466 }
4467 }
4469 aggTxn.forget(aTxn);
4471 return NS_OK;
4472 }
4474 nsresult
4475 nsEditor::CreateTxnForDeleteCharacter(nsIDOMCharacterData* aData,
4476 uint32_t aOffset,
4477 EDirection aDirection,
4478 DeleteTextTxn** aTxn)
4479 {
4480 NS_ASSERTION(aDirection == eNext || aDirection == ePrevious,
4481 "invalid direction");
4482 nsAutoString data;
4483 aData->GetData(data);
4484 NS_ASSERTION(data.Length(), "Trying to delete from a zero-length node");
4485 NS_ENSURE_STATE(data.Length());
4487 uint32_t segOffset = aOffset, segLength = 1;
4488 if (aDirection == eNext) {
4489 if (segOffset + 1 < data.Length() &&
4490 NS_IS_HIGH_SURROGATE(data[segOffset]) &&
4491 NS_IS_LOW_SURROGATE(data[segOffset+1])) {
4492 // delete both halves of the surrogate pair
4493 ++segLength;
4494 }
4495 } else if (aOffset > 0) {
4496 --segOffset;
4497 if (segOffset > 0 &&
4498 NS_IS_LOW_SURROGATE(data[segOffset]) &&
4499 NS_IS_HIGH_SURROGATE(data[segOffset-1])) {
4500 ++segLength;
4501 --segOffset;
4502 }
4503 } else {
4504 return NS_ERROR_FAILURE;
4505 }
4506 return CreateTxnForDeleteText(aData, segOffset, segLength, aTxn);
4507 }
4509 //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
4510 //are not implemented
4511 nsresult
4512 nsEditor::CreateTxnForDeleteInsertionPoint(nsRange* aRange,
4513 EDirection aAction,
4514 EditAggregateTxn* aTxn,
4515 nsINode** aNode,
4516 int32_t* aOffset,
4517 int32_t* aLength)
4518 {
4519 MOZ_ASSERT(aAction != eNone);
4521 nsresult res;
4523 // get the node and offset of the insertion point
4524 nsCOMPtr<nsINode> node = aRange->GetStartParent();
4525 NS_ENSURE_STATE(node);
4527 int32_t offset = aRange->StartOffset();
4529 // determine if the insertion point is at the beginning, middle, or end of
4530 // the node
4531 nsCOMPtr<nsIDOMCharacterData> nodeAsCharData = do_QueryInterface(node);
4533 uint32_t count = node->Length();
4535 bool isFirst = (0 == offset);
4536 bool isLast = (count == (uint32_t)offset);
4538 // XXX: if isFirst && isLast, then we'll need to delete the node
4539 // as well as the 1 child
4541 // build a transaction for deleting the appropriate data
4542 // XXX: this has to come from rule section
4543 if (aAction == ePrevious && isFirst) {
4544 // we're backspacing from the beginning of the node. Delete the first
4545 // thing to our left
4546 nsCOMPtr<nsIContent> priorNode = GetPriorNode(node, true);
4547 NS_ENSURE_STATE(priorNode);
4549 // there is a priorNode, so delete its last child (if chardata, delete the
4550 // last char). if it has no children, delete it
4551 nsCOMPtr<nsIDOMCharacterData> priorNodeAsCharData =
4552 do_QueryInterface(priorNode);
4553 if (priorNodeAsCharData) {
4554 uint32_t length = priorNode->Length();
4555 // Bail out for empty chardata XXX: Do we want to do something else?
4556 NS_ENSURE_STATE(length);
4557 nsRefPtr<DeleteTextTxn> txn;
4558 res = CreateTxnForDeleteCharacter(priorNodeAsCharData, length,
4559 ePrevious, getter_AddRefs(txn));
4560 NS_ENSURE_SUCCESS(res, res);
4562 *aOffset = txn->GetOffset();
4563 *aLength = txn->GetNumCharsToDelete();
4564 aTxn->AppendChild(txn);
4565 } else {
4566 // priorNode is not chardata, so tell its parent to delete it
4567 nsRefPtr<DeleteNodeTxn> txn;
4568 res = CreateTxnForDeleteNode(priorNode, getter_AddRefs(txn));
4569 NS_ENSURE_SUCCESS(res, res);
4571 aTxn->AppendChild(txn);
4572 }
4574 NS_ADDREF(*aNode = priorNode);
4576 return NS_OK;
4577 }
4579 if (aAction == eNext && isLast) {
4580 // we're deleting from the end of the node. Delete the first thing to our
4581 // right
4582 nsCOMPtr<nsIContent> nextNode = GetNextNode(node, true);
4583 NS_ENSURE_STATE(nextNode);
4585 // there is a nextNode, so delete its first child (if chardata, delete the
4586 // first char). if it has no children, delete it
4587 nsCOMPtr<nsIDOMCharacterData> nextNodeAsCharData =
4588 do_QueryInterface(nextNode);
4589 if (nextNodeAsCharData) {
4590 uint32_t length = nextNode->Length();
4591 // Bail out for empty chardata XXX: Do we want to do something else?
4592 NS_ENSURE_STATE(length);
4593 nsRefPtr<DeleteTextTxn> txn;
4594 res = CreateTxnForDeleteCharacter(nextNodeAsCharData, 0, eNext,
4595 getter_AddRefs(txn));
4596 NS_ENSURE_SUCCESS(res, res);
4598 *aOffset = txn->GetOffset();
4599 *aLength = txn->GetNumCharsToDelete();
4600 aTxn->AppendChild(txn);
4601 } else {
4602 // nextNode is not chardata, so tell its parent to delete it
4603 nsRefPtr<DeleteNodeTxn> txn;
4604 res = CreateTxnForDeleteNode(nextNode, getter_AddRefs(txn));
4605 NS_ENSURE_SUCCESS(res, res);
4606 aTxn->AppendChild(txn);
4607 }
4609 NS_ADDREF(*aNode = nextNode);
4611 return NS_OK;
4612 }
4614 if (nodeAsCharData) {
4615 // we have chardata, so delete a char at the proper offset
4616 nsRefPtr<DeleteTextTxn> txn;
4617 res = CreateTxnForDeleteCharacter(nodeAsCharData, offset, aAction,
4618 getter_AddRefs(txn));
4619 NS_ENSURE_SUCCESS(res, res);
4621 aTxn->AppendChild(txn);
4622 NS_ADDREF(*aNode = node);
4623 *aOffset = txn->GetOffset();
4624 *aLength = txn->GetNumCharsToDelete();
4625 } else {
4626 // we're either deleting a node or chardata, need to dig into the next/prev
4627 // node to find out
4628 nsCOMPtr<nsINode> selectedNode;
4629 if (aAction == ePrevious) {
4630 selectedNode = GetPriorNode(node, offset, true);
4631 } else if (aAction == eNext) {
4632 selectedNode = GetNextNode(node, offset, true);
4633 }
4635 while (selectedNode &&
4636 selectedNode->IsNodeOfType(nsINode::eDATA_NODE) &&
4637 !selectedNode->Length()) {
4638 // Can't delete an empty chardata node (bug 762183)
4639 if (aAction == ePrevious) {
4640 selectedNode = GetPriorNode(selectedNode, true);
4641 } else if (aAction == eNext) {
4642 selectedNode = GetNextNode(selectedNode, true);
4643 }
4644 }
4645 NS_ENSURE_STATE(selectedNode);
4647 nsCOMPtr<nsIDOMCharacterData> selectedNodeAsCharData =
4648 do_QueryInterface(selectedNode);
4649 if (selectedNodeAsCharData) {
4650 // we are deleting from a chardata node, so do a character deletion
4651 uint32_t position = 0;
4652 if (aAction == ePrevious) {
4653 position = selectedNode->Length();
4654 }
4655 nsRefPtr<DeleteTextTxn> delTextTxn;
4656 res = CreateTxnForDeleteCharacter(selectedNodeAsCharData, position,
4657 aAction, getter_AddRefs(delTextTxn));
4658 NS_ENSURE_SUCCESS(res, res);
4659 NS_ENSURE_TRUE(delTextTxn, NS_ERROR_NULL_POINTER);
4661 aTxn->AppendChild(delTextTxn);
4662 *aOffset = delTextTxn->GetOffset();
4663 *aLength = delTextTxn->GetNumCharsToDelete();
4664 } else {
4665 nsRefPtr<DeleteNodeTxn> delElementTxn;
4666 res = CreateTxnForDeleteNode(selectedNode, getter_AddRefs(delElementTxn));
4667 NS_ENSURE_SUCCESS(res, res);
4668 NS_ENSURE_TRUE(delElementTxn, NS_ERROR_NULL_POINTER);
4670 aTxn->AppendChild(delElementTxn);
4671 }
4673 NS_ADDREF(*aNode = selectedNode);
4674 }
4676 return NS_OK;
4677 }
4679 nsresult
4680 nsEditor::CreateRange(nsIDOMNode *aStartParent, int32_t aStartOffset,
4681 nsIDOMNode *aEndParent, int32_t aEndOffset,
4682 nsIDOMRange **aRange)
4683 {
4684 return nsRange::CreateRange(aStartParent, aStartOffset, aEndParent,
4685 aEndOffset, aRange);
4686 }
4688 nsresult
4689 nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode *aNode)
4690 {
4691 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
4692 nsCOMPtr<nsISelection> selection;
4693 nsresult res = GetSelection(getter_AddRefs(selection));
4694 NS_ENSURE_SUCCESS(res, res);
4695 if(!selection) return NS_ERROR_FAILURE;
4697 nsCOMPtr<nsIDOMNode> parentNode;
4698 res = aNode->GetParentNode(getter_AddRefs(parentNode));
4699 NS_ENSURE_SUCCESS(res, res);
4700 NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
4702 int32_t offset = GetChildOffset(aNode, parentNode);
4704 nsCOMPtr<nsIDOMRange> range;
4705 res = CreateRange(parentNode, offset, parentNode, offset+1, getter_AddRefs(range));
4706 NS_ENSURE_SUCCESS(res, res);
4707 NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
4709 return selection->AddRange(range);
4710 }
4712 nsresult nsEditor::ClearSelection()
4713 {
4714 nsCOMPtr<nsISelection> selection;
4715 nsresult res = nsEditor::GetSelection(getter_AddRefs(selection));
4716 NS_ENSURE_SUCCESS(res, res);
4717 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
4718 return selection->RemoveAllRanges();
4719 }
4721 nsresult
4722 nsEditor::CreateHTMLContent(const nsAString& aTag, dom::Element** aContent)
4723 {
4724 nsCOMPtr<nsIDocument> doc = GetDocument();
4725 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
4727 // XXX Wallpaper over editor bug (editor tries to create elements with an
4728 // empty nodename).
4729 if (aTag.IsEmpty()) {
4730 NS_ERROR("Don't pass an empty tag to nsEditor::CreateHTMLContent, "
4731 "check caller.");
4732 return NS_ERROR_FAILURE;
4733 }
4735 return doc->CreateElem(aTag, nullptr, kNameSpaceID_XHTML,
4736 reinterpret_cast<nsIContent**>(aContent));
4737 }
4739 nsresult
4740 nsEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement,
4741 const nsAString & aAttribute,
4742 const nsAString & aValue,
4743 bool aSuppressTransaction)
4744 {
4745 return SetAttribute(aElement, aAttribute, aValue);
4746 }
4748 nsresult
4749 nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
4750 const nsAString & aAttribute,
4751 bool aSuppressTransaction)
4752 {
4753 return RemoveAttribute(aElement, aAttribute);
4754 }
4756 nsresult
4757 nsEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
4758 {
4759 // NOTE: When you change this method, you should also change:
4760 // * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
4761 // * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
4762 //
4763 // And also when you add new key handling, you need to change the subclass's
4764 // HandleKeyPressEvent()'s switch statement.
4766 WidgetKeyboardEvent* nativeKeyEvent =
4767 aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
4768 NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
4769 NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
4770 "HandleKeyPressEvent gets non-keypress event");
4772 // if we are readonly or disabled, then do nothing.
4773 if (IsReadonly() || IsDisabled()) {
4774 // consume backspace for disabled and readonly textfields, to prevent
4775 // back in history, which could be confusing to users
4776 if (nativeKeyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
4777 aKeyEvent->PreventDefault();
4778 }
4779 return NS_OK;
4780 }
4782 switch (nativeKeyEvent->keyCode) {
4783 case nsIDOMKeyEvent::DOM_VK_META:
4784 case nsIDOMKeyEvent::DOM_VK_WIN:
4785 case nsIDOMKeyEvent::DOM_VK_SHIFT:
4786 case nsIDOMKeyEvent::DOM_VK_CONTROL:
4787 case nsIDOMKeyEvent::DOM_VK_ALT:
4788 aKeyEvent->PreventDefault(); // consumed
4789 return NS_OK;
4790 case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
4791 if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() ||
4792 nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) {
4793 return NS_OK;
4794 }
4795 DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
4796 aKeyEvent->PreventDefault(); // consumed
4797 return NS_OK;
4798 case nsIDOMKeyEvent::DOM_VK_DELETE:
4799 // on certain platforms (such as windows) the shift key
4800 // modifies what delete does (cmd_cut in this case).
4801 // bailing here to allow the keybindings to do the cut.
4802 if (nativeKeyEvent->IsShift() || nativeKeyEvent->IsControl() ||
4803 nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
4804 nativeKeyEvent->IsOS()) {
4805 return NS_OK;
4806 }
4807 DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
4808 aKeyEvent->PreventDefault(); // consumed
4809 return NS_OK;
4810 }
4811 return NS_OK;
4812 }
4814 nsresult
4815 nsEditor::HandleInlineSpellCheck(EditAction action,
4816 nsISelection *aSelection,
4817 nsIDOMNode *previousSelectedNode,
4818 int32_t previousSelectedOffset,
4819 nsIDOMNode *aStartNode,
4820 int32_t aStartOffset,
4821 nsIDOMNode *aEndNode,
4822 int32_t aEndOffset)
4823 {
4824 // Have to cast action here because this method is from an IDL
4825 return mInlineSpellChecker ? mInlineSpellChecker->SpellCheckAfterEditorChange(
4826 (int32_t)action, aSelection,
4827 previousSelectedNode, previousSelectedOffset,
4828 aStartNode, aStartOffset, aEndNode,
4829 aEndOffset)
4830 : NS_OK;
4831 }
4833 already_AddRefed<nsIContent>
4834 nsEditor::FindSelectionRoot(nsINode *aNode)
4835 {
4836 nsCOMPtr<nsIContent> rootContent = GetRoot();
4837 return rootContent.forget();
4838 }
4840 nsresult
4841 nsEditor::InitializeSelection(nsIDOMEventTarget* aFocusEventTarget)
4842 {
4843 nsCOMPtr<nsINode> targetNode = do_QueryInterface(aFocusEventTarget);
4844 NS_ENSURE_TRUE(targetNode, NS_ERROR_INVALID_ARG);
4845 nsCOMPtr<nsIContent> selectionRootContent = FindSelectionRoot(targetNode);
4846 if (!selectionRootContent) {
4847 return NS_OK;
4848 }
4850 bool isTargetDoc =
4851 targetNode->NodeType() == nsIDOMNode::DOCUMENT_NODE &&
4852 targetNode->HasFlag(NODE_IS_EDITABLE);
4854 nsCOMPtr<nsISelection> selection;
4855 nsresult rv = GetSelection(getter_AddRefs(selection));
4856 NS_ENSURE_SUCCESS(rv, rv);
4858 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
4859 NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
4861 nsCOMPtr<nsISelectionController> selCon;
4862 rv = GetSelectionController(getter_AddRefs(selCon));
4863 NS_ENSURE_SUCCESS(rv, rv);
4865 nsCOMPtr<nsISelectionPrivate> selectionPrivate =
4866 do_QueryInterface(selection);
4867 NS_ENSURE_TRUE(selectionPrivate, NS_ERROR_UNEXPECTED);
4869 // Init the caret
4870 nsRefPtr<nsCaret> caret = presShell->GetCaret();
4871 NS_ENSURE_TRUE(caret, NS_ERROR_UNEXPECTED);
4872 caret->SetIgnoreUserModify(false);
4873 caret->SetCaretDOMSelection(selection);
4874 selCon->SetCaretReadOnly(IsReadonly());
4875 selCon->SetCaretEnabled(true);
4877 // Init selection
4878 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
4879 selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
4880 selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
4881 // If the computed selection root isn't root content, we should set it
4882 // as selection ancestor limit. However, if that is root element, it means
4883 // there is not limitation of the selection, then, we must set nullptr.
4884 // NOTE: If we set a root element to the ancestor limit, some selection
4885 // methods don't work fine.
4886 if (selectionRootContent->GetParent()) {
4887 selectionPrivate->SetAncestorLimiter(selectionRootContent);
4888 } else {
4889 selectionPrivate->SetAncestorLimiter(nullptr);
4890 }
4892 // XXX What case needs this?
4893 if (isTargetDoc) {
4894 int32_t rangeCount;
4895 selection->GetRangeCount(&rangeCount);
4896 if (rangeCount == 0) {
4897 BeginningOfDocument();
4898 }
4899 }
4901 return NS_OK;
4902 }
4904 void
4905 nsEditor::FinalizeSelection()
4906 {
4907 nsCOMPtr<nsISelectionController> selCon;
4908 nsresult rv = GetSelectionController(getter_AddRefs(selCon));
4909 NS_ENSURE_SUCCESS_VOID(rv);
4911 nsCOMPtr<nsISelection> selection;
4912 rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
4913 getter_AddRefs(selection));
4914 NS_ENSURE_SUCCESS_VOID(rv);
4916 nsCOMPtr<nsISelectionPrivate> selectionPrivate = do_QueryInterface(selection);
4917 NS_ENSURE_TRUE_VOID(selectionPrivate);
4919 selectionPrivate->SetAncestorLimiter(nullptr);
4921 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
4922 NS_ENSURE_TRUE_VOID(presShell);
4924 selCon->SetCaretEnabled(false);
4926 nsFocusManager* fm = nsFocusManager::GetFocusManager();
4927 NS_ENSURE_TRUE_VOID(fm);
4928 fm->UpdateCaretForCaretBrowsingMode();
4930 if (!HasIndependentSelection()) {
4931 // If this editor doesn't have an independent selection, i.e., it must
4932 // mean that it is an HTML editor, the selection controller is shared with
4933 // presShell. So, even this editor loses focus, other part of the document
4934 // may still have focus.
4935 nsCOMPtr<nsIDocument> doc = GetDocument();
4936 ErrorResult ret;
4937 if (!doc || !doc->HasFocus(ret)) {
4938 // If the document already lost focus, mark the selection as disabled.
4939 selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
4940 } else {
4941 // Otherwise, mark selection as normal because outside of a
4942 // contenteditable element should be selected with normal selection
4943 // color after here.
4944 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
4945 }
4946 } else if (IsFormWidget() || IsPasswordEditor() ||
4947 IsReadonly() || IsDisabled() || IsInputFiltered()) {
4948 // In <input> or <textarea>, the independent selection should be hidden
4949 // while this editor doesn't have focus.
4950 selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
4951 } else {
4952 // Otherwise, although we're not sure how this case happens, the
4953 // independent selection should be marked as disabled.
4954 selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
4955 }
4957 selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
4958 }
4960 dom::Element *
4961 nsEditor::GetRoot()
4962 {
4963 if (!mRootElement)
4964 {
4965 nsCOMPtr<nsIDOMElement> root;
4967 // Let GetRootElement() do the work
4968 GetRootElement(getter_AddRefs(root));
4969 }
4971 return mRootElement;
4972 }
4974 dom::Element*
4975 nsEditor::GetEditorRoot()
4976 {
4977 return GetRoot();
4978 }
4980 Element*
4981 nsEditor::GetExposedRoot()
4982 {
4983 Element* rootElement = GetRoot();
4985 // For plaintext editors, we need to ask the input/textarea element directly.
4986 if (rootElement && rootElement->IsRootOfNativeAnonymousSubtree()) {
4987 rootElement = rootElement->GetParent()->AsElement();
4988 }
4990 return rootElement;
4991 }
4993 nsresult
4994 nsEditor::DetermineCurrentDirection()
4995 {
4996 // Get the current root direction from its frame
4997 nsIContent* rootElement = GetExposedRoot();
4999 // If we don't have an explicit direction, determine our direction
5000 // from the content's direction
5001 if (!(mFlags & (nsIPlaintextEditor::eEditorLeftToRight |
5002 nsIPlaintextEditor::eEditorRightToLeft))) {
5004 nsIFrame* frame = rootElement->GetPrimaryFrame();
5005 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
5007 // Set the flag here, to enable us to use the same code path below.
5008 // It will be flipped before returning from the function.
5009 if (frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
5010 mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
5011 } else {
5012 mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
5013 }
5014 }
5016 return NS_OK;
5017 }
5019 NS_IMETHODIMP
5020 nsEditor::SwitchTextDirection()
5021 {
5022 // Get the current root direction from its frame
5023 nsIContent* rootElement = GetExposedRoot();
5025 nsresult rv = DetermineCurrentDirection();
5026 NS_ENSURE_SUCCESS(rv, rv);
5028 // Apply the opposite direction
5029 if (mFlags & nsIPlaintextEditor::eEditorRightToLeft) {
5030 NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorLeftToRight),
5031 "Unexpected mutually exclusive flag");
5032 mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft;
5033 mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
5034 rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), true);
5035 } else if (mFlags & nsIPlaintextEditor::eEditorLeftToRight) {
5036 NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorRightToLeft),
5037 "Unexpected mutually exclusive flag");
5038 mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
5039 mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight;
5040 rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), true);
5041 }
5043 if (NS_SUCCEEDED(rv)) {
5044 FireInputEvent();
5045 }
5047 return rv;
5048 }
5050 void
5051 nsEditor::SwitchTextDirectionTo(uint32_t aDirection)
5052 {
5053 // Get the current root direction from its frame
5054 nsIContent* rootElement = GetExposedRoot();
5056 nsresult rv = DetermineCurrentDirection();
5057 NS_ENSURE_SUCCESS_VOID(rv);
5059 // Apply the requested direction
5060 if (aDirection == nsIPlaintextEditor::eEditorLeftToRight &&
5061 (mFlags & nsIPlaintextEditor::eEditorRightToLeft)) {
5062 NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorLeftToRight),
5063 "Unexpected mutually exclusive flag");
5064 mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft;
5065 mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
5066 rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), true);
5067 } else if (aDirection == nsIPlaintextEditor::eEditorRightToLeft &&
5068 (mFlags & nsIPlaintextEditor::eEditorLeftToRight)) {
5069 NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorRightToLeft),
5070 "Unexpected mutually exclusive flag");
5071 mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
5072 mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight;
5073 rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), true);
5074 }
5076 if (NS_SUCCEEDED(rv)) {
5077 FireInputEvent();
5078 }
5079 }
5081 #if DEBUG_JOE
5082 void
5083 nsEditor::DumpNode(nsIDOMNode *aNode, int32_t indent)
5084 {
5085 int32_t i;
5086 for (i=0; i<indent; i++)
5087 printf(" ");
5089 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
5090 nsCOMPtr<nsIDOMDocumentFragment> docfrag = do_QueryInterface(aNode);
5092 if (element || docfrag)
5093 {
5094 if (element)
5095 {
5096 nsAutoString tag;
5097 element->GetTagName(tag);
5098 printf("<%s>\n", NS_LossyConvertUTF16toASCII(tag).get());
5099 }
5100 else
5101 {
5102 printf("<document fragment>\n");
5103 }
5104 nsCOMPtr<nsIDOMNodeList> childList;
5105 aNode->GetChildNodes(getter_AddRefs(childList));
5106 NS_ENSURE_TRUE(childList, NS_ERROR_NULL_POINTER);
5107 uint32_t numChildren;
5108 childList->GetLength(&numChildren);
5109 nsCOMPtr<nsIDOMNode> child, tmp;
5110 aNode->GetFirstChild(getter_AddRefs(child));
5111 for (i=0; i<numChildren; i++)
5112 {
5113 DumpNode(child, indent+1);
5114 child->GetNextSibling(getter_AddRefs(tmp));
5115 child = tmp;
5116 }
5117 }
5118 else if (IsTextNode(aNode))
5119 {
5120 nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(aNode);
5121 nsAutoString str;
5122 textNode->GetData(str);
5123 nsAutoCString cstr;
5124 LossyCopyUTF16toASCII(str, cstr);
5125 cstr.ReplaceChar('\n', ' ');
5126 printf("<textnode> %s\n", cstr.get());
5127 }
5128 }
5129 #endif
5131 bool
5132 nsEditor::IsModifiableNode(nsIDOMNode *aNode)
5133 {
5134 return true;
5135 }
5137 bool
5138 nsEditor::IsModifiableNode(nsINode *aNode)
5139 {
5140 return true;
5141 }
5143 already_AddRefed<nsIContent>
5144 nsEditor::GetFocusedContent()
5145 {
5146 nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget();
5147 if (!piTarget) {
5148 return nullptr;
5149 }
5151 nsFocusManager* fm = nsFocusManager::GetFocusManager();
5152 NS_ENSURE_TRUE(fm, nullptr);
5154 nsCOMPtr<nsIContent> content = fm->GetFocusedContent();
5155 return SameCOMIdentity(content, piTarget) ? content.forget() : nullptr;
5156 }
5158 already_AddRefed<nsIContent>
5159 nsEditor::GetFocusedContentForIME()
5160 {
5161 return GetFocusedContent();
5162 }
5164 bool
5165 nsEditor::IsActiveInDOMWindow()
5166 {
5167 nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget();
5168 if (!piTarget) {
5169 return false;
5170 }
5172 nsFocusManager* fm = nsFocusManager::GetFocusManager();
5173 NS_ENSURE_TRUE(fm, false);
5175 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5176 nsPIDOMWindow* ourWindow = doc->GetWindow();
5177 nsCOMPtr<nsPIDOMWindow> win;
5178 nsIContent* content =
5179 nsFocusManager::GetFocusedDescendant(ourWindow, false,
5180 getter_AddRefs(win));
5181 return SameCOMIdentity(content, piTarget);
5182 }
5184 bool
5185 nsEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
5186 {
5187 // If the event is trusted, the event should always cause input.
5188 NS_ENSURE_TRUE(aEvent, false);
5190 // If this is mouse event but this editor doesn't have focus, we shouldn't
5191 // handle it.
5192 nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
5193 if (mouseEvent) {
5194 nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
5195 if (!focusedContent) {
5196 return false;
5197 }
5198 } else {
5199 nsAutoString eventType;
5200 aEvent->GetType(eventType);
5201 // If composition event or text event isn't dispatched via widget,
5202 // we need to ignore them since they cannot be managed by TextComposition.
5203 // E.g., the event was created by chrome JS.
5204 // Note that if we allow to handle such events, editor may be confused by
5205 // strange event order.
5206 if (eventType.EqualsLiteral("text") ||
5207 eventType.EqualsLiteral("compositionstart") ||
5208 eventType.EqualsLiteral("compositionend")) {
5209 WidgetGUIEvent* widgetGUIEvent =
5210 aEvent->GetInternalNSEvent()->AsGUIEvent();
5211 if (!widgetGUIEvent || !widgetGUIEvent->widget) {
5212 return false;
5213 }
5214 }
5215 }
5217 bool isTrusted;
5218 nsresult rv = aEvent->GetIsTrusted(&isTrusted);
5219 NS_ENSURE_SUCCESS(rv, false);
5220 if (isTrusted) {
5221 return true;
5222 }
5224 // Ignore untrusted mouse event.
5225 // XXX Why are we handling other untrusted input events?
5226 if (mouseEvent) {
5227 return false;
5228 }
5230 // Otherwise, we shouldn't handle any input events when we're not an active
5231 // element of the DOM window.
5232 return IsActiveInDOMWindow();
5233 }
5235 void
5236 nsEditor::OnFocus(nsIDOMEventTarget* aFocusEventTarget)
5237 {
5238 InitializeSelection(aFocusEventTarget);
5239 if (mInlineSpellChecker) {
5240 mInlineSpellChecker->UpdateCurrentDictionary();
5241 }
5242 }
5244 NS_IMETHODIMP
5245 nsEditor::GetSuppressDispatchingInputEvent(bool *aSuppressed)
5246 {
5247 NS_ENSURE_ARG_POINTER(aSuppressed);
5248 *aSuppressed = !mDispatchInputEvent;
5249 return NS_OK;
5250 }
5252 NS_IMETHODIMP
5253 nsEditor::SetSuppressDispatchingInputEvent(bool aSuppress)
5254 {
5255 mDispatchInputEvent = !aSuppress;
5256 return NS_OK;
5257 }