Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "mozilla/EditorBase.h"
7 :
8 : #include "mozilla/DebugOnly.h" // for DebugOnly
9 : #include "mozilla/Encoding.h" // for Encoding
10 :
11 : #include <stdio.h> // for nullptr, stdout
12 : #include <string.h> // for strcmp
13 :
14 : #include "ChangeAttributeTransaction.h" // for ChangeAttributeTransaction
15 : #include "CompositionTransaction.h" // for CompositionTransaction
16 : #include "CreateElementTransaction.h" // for CreateElementTransaction
17 : #include "DeleteNodeTransaction.h" // for DeleteNodeTransaction
18 : #include "DeleteRangeTransaction.h" // for DeleteRangeTransaction
19 : #include "DeleteTextTransaction.h" // for DeleteTextTransaction
20 : #include "EditAggregateTransaction.h" // for EditAggregateTransaction
21 : #include "EditorEventListener.h" // for EditorEventListener
22 : #include "HTMLEditRules.h" // for HTMLEditRules
23 : #include "InsertNodeTransaction.h" // for InsertNodeTransaction
24 : #include "InsertTextTransaction.h" // for InsertTextTransaction
25 : #include "JoinNodeTransaction.h" // for JoinNodeTransaction
26 : #include "PlaceholderTransaction.h" // for PlaceholderTransaction
27 : #include "SplitNodeTransaction.h" // for SplitNodeTransaction
28 : #include "StyleSheetTransactions.h" // for AddStyleSheetTransaction, etc.
29 : #include "TextEditUtils.h" // for TextEditUtils
30 : #include "mozilla/CheckedInt.h" // for CheckedInt
31 : #include "mozilla/ComputedStyle.h" // for ComputedStyle
32 : #include "mozilla/EditAction.h" // for EditSubAction
33 : #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
34 : #include "mozilla/EditorSpellCheck.h" // for EditorSpellCheck
35 : #include "mozilla/EditorUtils.h" // for various helper classes.
36 : #include "mozilla/EditTransactionBase.h" // for EditTransactionBase
37 : #include "mozilla/FlushType.h" // for FlushType::Frames
38 : #include "mozilla/IMEContentObserver.h" // for IMEContentObserver
39 : #include "mozilla/IMEStateManager.h" // for IMEStateManager
40 : #include "mozilla/mozalloc.h" // for operator new, etc.
41 : #include "mozilla/mozInlineSpellChecker.h" // for mozInlineSpellChecker
42 : #include "mozilla/mozSpellChecker.h" // for mozSpellChecker
43 : #include "mozilla/Preferences.h" // for Preferences
44 : #include "mozilla/RangeBoundary.h" // for RawRangeBoundary, RangeBoundary
45 : #include "mozilla/dom/Selection.h" // for Selection, etc.
46 : #include "mozilla/Services.h" // for GetObserverService
47 : #include "mozilla/TextComposition.h" // for TextComposition
48 : #include "mozilla/TextInputListener.h" // for TextInputListener
49 : #include "mozilla/TextServicesDocument.h" // for TextServicesDocument
50 : #include "mozilla/TextEvents.h"
51 : #include "mozilla/TransactionManager.h" // for TransactionManager
52 : #include "mozilla/dom/CharacterData.h" // for CharacterData
53 : #include "mozilla/dom/Element.h" // for Element, nsINode::AsElement
54 : #include "mozilla/dom/EventTarget.h" // for EventTarget
55 : #include "mozilla/dom/HTMLBodyElement.h"
56 : #include "mozilla/dom/Text.h"
57 : #include "mozilla/dom/Event.h"
58 : #include "nsAString.h" // for nsAString::Length, etc.
59 : #include "nsCCUncollectableMarker.h" // for nsCCUncollectableMarker
60 : #include "nsCaret.h" // for nsCaret
61 : #include "nsCaseTreatment.h"
62 : #include "nsCharTraits.h" // for NS_IS_HIGH_SURROGATE, etc.
63 : #include "nsComponentManagerUtils.h" // for do_CreateInstance
64 : #include "nsComputedDOMStyle.h" // for nsComputedDOMStyle
65 : #include "nsContentUtils.h" // for nsContentUtils
66 : #include "nsDOMString.h" // for DOMStringIsNull
67 : #include "nsDebug.h" // for NS_ENSURE_TRUE, etc.
68 : #include "nsError.h" // for NS_OK, etc.
69 : #include "nsFocusManager.h" // for nsFocusManager
70 : #include "nsFrameSelection.h" // for nsFrameSelection
71 : #include "nsGenericHTMLElement.h" // for nsGenericHTMLElement
72 : #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::dir
73 : #include "nsIAbsorbingTransaction.h" // for nsIAbsorbingTransaction
74 : #include "nsAtom.h" // for nsAtom
75 : #include "nsIContent.h" // for nsIContent
76 : #include "nsIDocument.h" // for nsIDocument
77 : #include "nsIDOMEventListener.h" // for nsIDOMEventListener
78 : #include "nsIDocumentStateListener.h" // for nsIDocumentStateListener
79 : #include "nsIEditActionListener.h" // for nsIEditActionListener
80 : #include "nsIEditorObserver.h" // for nsIEditorObserver
81 : #include "nsIEditorSpellCheck.h" // for nsIEditorSpellCheck
82 : #include "nsIFrame.h" // for nsIFrame
83 : #include "nsIHTMLDocument.h" // for nsIHTMLDocument
84 : #include "nsIInlineSpellChecker.h" // for nsIInlineSpellChecker, etc.
85 : #include "nsNameSpaceManager.h" // for kNameSpaceID_None, etc.
86 : #include "nsINode.h" // for nsINode, etc.
87 : #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc.
88 : #include "nsIPresShell.h" // for nsIPresShell
89 : #include "nsISelectionController.h" // for nsISelectionController, etc.
90 : #include "nsISelectionDisplay.h" // for nsISelectionDisplay, etc.
91 : #include "nsISupportsBase.h" // for nsISupports
92 : #include "nsISupportsUtils.h" // for NS_ADDREF, NS_IF_ADDREF
93 : #include "nsITransaction.h" // for nsITransaction
94 : #include "nsITransactionManager.h"
95 : #include "nsIWeakReference.h" // for nsISupportsWeakReference
96 : #include "nsIWidget.h" // for nsIWidget, IMEState, etc.
97 : #include "nsPIDOMWindow.h" // for nsPIDOMWindow
98 : #include "nsPresContext.h" // for nsPresContext
99 : #include "nsRange.h" // for nsRange
100 : #include "nsReadableUtils.h" // for EmptyString, ToNewCString
101 : #include "nsString.h" // for nsAutoString, nsString, etc.
102 : #include "nsStringFwd.h" // for nsString
103 : #include "nsStyleConsts.h" // for NS_STYLE_DIRECTION_RTL, etc.
104 : #include "nsStyleStruct.h" // for nsStyleDisplay, nsStyleText, etc.
105 : #include "nsStyleStructFwd.h" // for nsIFrame::StyleUIReset, etc.
106 : #include "nsTextNode.h" // for nsTextNode
107 : #include "nsThreadUtils.h" // for nsRunnable
108 : #include "prtime.h" // for PR_Now
109 :
110 : class nsIOutputStream;
111 : class nsITransferable;
112 :
113 : namespace mozilla {
114 :
115 : using namespace dom;
116 : using namespace widget;
117 :
118 : /*****************************************************************************
119 : * mozilla::EditorBase
120 : *****************************************************************************/
121 :
122 : template already_AddRefed<Element>
123 : EditorBase::CreateNodeWithTransaction(nsAtom& aTag,
124 : const EditorDOMPoint& aPointToInsert);
125 : template already_AddRefed<Element>
126 : EditorBase::CreateNodeWithTransaction(nsAtom& aTag,
127 : const EditorRawDOMPoint& aPointToInsert);
128 : template nsresult
129 : EditorBase::InsertNodeWithTransaction(nsIContent& aContentToInsert,
130 : const EditorDOMPoint& aPointToInsert);
131 : template nsresult
132 : EditorBase::InsertNodeWithTransaction(nsIContent& aContentToInsert,
133 : const EditorRawDOMPoint& aPointToInsert);
134 : template already_AddRefed<nsIContent>
135 : EditorBase::SplitNodeWithTransaction(const EditorDOMPoint& aStartOfRightNode,
136 : ErrorResult& aError);
137 : template already_AddRefed<nsIContent>
138 : EditorBase::SplitNodeWithTransaction(const EditorRawDOMPoint& aStartOfRightNode,
139 : ErrorResult& aError);
140 : template SplitNodeResult
141 : EditorBase::SplitNodeDeepWithTransaction(
142 : nsIContent& aMostAncestorToSplit,
143 : const EditorDOMPoint& aStartOfDeepestRightNode,
144 : SplitAtEdges aSplitAtEdges);
145 : template SplitNodeResult
146 : EditorBase::SplitNodeDeepWithTransaction(
147 : nsIContent& aMostAncestorToSplit,
148 : const EditorRawDOMPoint& aStartOfDeepestRightNode,
149 : SplitAtEdges aSplitAtEdges);
150 : template nsresult
151 : EditorBase::MoveNodeWithTransaction(nsIContent& aContent,
152 : const EditorDOMPoint& aPointToInsert);
153 : template nsresult
154 : EditorBase::MoveNodeWithTransaction(nsIContent& aContent,
155 : const EditorRawDOMPoint& aPointToInsert);
156 :
157 0 : EditorBase::EditorBase()
158 : : mPlaceholderName(nullptr)
159 : , mModCount(0)
160 : , mFlags(0)
161 : , mUpdateCount(0)
162 : , mPlaceholderBatch(0)
163 : , mTopLevelEditSubAction(EditSubAction::eNone)
164 : , mDirection(eNone)
165 : , mDocDirtyState(-1)
166 : , mSpellcheckCheckboxState(eTriUnset)
167 : , mShouldTxnSetSelection(true)
168 : , mDidPreDestroy(false)
169 : , mDidPostCreate(false)
170 : , mDispatchInputEvent(true)
171 : , mIsInEditSubAction(false)
172 : , mHidingCaret(false)
173 : , mSpellCheckerDictionaryUpdated(true)
174 0 : , mIsHTMLEditorClass(false)
175 : {
176 0 : }
177 :
178 0 : EditorBase::~EditorBase()
179 : {
180 0 : MOZ_ASSERT(!IsInitialized() || mDidPreDestroy,
181 : "Why PreDestroy hasn't been called?");
182 :
183 0 : if (mComposition) {
184 0 : mComposition->OnEditorDestroyed();
185 0 : mComposition = nullptr;
186 : }
187 : // If this editor is still hiding the caret, we need to restore it.
188 0 : HideCaret(false);
189 0 : mTransactionManager = nullptr;
190 0 : }
191 :
192 : NS_IMPL_CYCLE_COLLECTION_CLASS(EditorBase)
193 :
194 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase)
195 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
196 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionController)
197 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
198 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMEContentObserver)
199 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker)
200 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextServicesDocument)
201 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextInputListener)
202 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransactionManager)
203 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners)
204 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers)
205 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners)
206 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget)
207 :
208 0 : if (tmp->mEventListener) {
209 0 : tmp->mEventListener->Disconnect();
210 0 : tmp->mEventListener = nullptr;
211 : }
212 :
213 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaceholderTransaction)
214 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mSavedSel);
215 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mRangeUpdater);
216 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
217 :
218 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EditorBase)
219 : nsIDocument* currentDoc =
220 0 : tmp->mRootElement ? tmp->mRootElement->GetUncomposedDoc() : nullptr;
221 0 : if (currentDoc &&
222 0 : nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) {
223 : return NS_SUCCESS_INTERRUPTED_TRAVERSE;
224 : }
225 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
226 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionController)
227 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
228 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
229 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker)
230 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextServicesDocument)
231 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextInputListener)
232 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransactionManager)
233 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners)
234 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorObservers)
235 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners)
236 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
237 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener)
238 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaceholderTransaction)
239 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSavedSel);
240 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRangeUpdater);
241 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
242 :
243 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EditorBase)
244 0 : NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
245 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
246 0 : NS_INTERFACE_MAP_ENTRY(nsIEditor)
247 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
248 0 : NS_INTERFACE_MAP_END
249 :
250 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(EditorBase)
251 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(EditorBase)
252 :
253 :
254 : nsresult
255 0 : EditorBase::Init(nsIDocument& aDocument,
256 : Element* aRoot,
257 : nsISelectionController* aSelectionController,
258 : uint32_t aFlags,
259 : const nsAString& aValue)
260 : {
261 0 : MOZ_ASSERT(mTopLevelEditSubAction == EditSubAction::eNone,
262 : "Initializing during an edit action is an error");
263 :
264 : // First only set flags, but other stuff shouldn't be initialized now.
265 : // Don't move this call after initializing mDocument.
266 : // SetFlags() can check whether it's called during initialization or not by
267 : // them. Note that SetFlags() will be called by PostCreate().
268 : #ifdef DEBUG
269 : nsresult rv =
270 : #endif
271 0 : SetFlags(aFlags);
272 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
273 :
274 0 : mDocument = &aDocument;
275 : // HTML editors currently don't have their own selection controller,
276 : // so they'll pass null as aSelCon, and we'll get the selection controller
277 : // off of the presshell.
278 0 : nsCOMPtr<nsISelectionController> selectionController;
279 0 : if (aSelectionController) {
280 0 : mSelectionController = aSelectionController;
281 0 : selectionController = aSelectionController;
282 : } else {
283 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
284 0 : selectionController = do_QueryInterface(presShell);
285 : }
286 0 : MOZ_ASSERT(selectionController,
287 : "Selection controller should be available at this point");
288 :
289 : //set up root element if we are passed one.
290 0 : if (aRoot) {
291 0 : mRootElement = aRoot;
292 : }
293 :
294 0 : mUpdateCount=0;
295 :
296 : // If this is an editor for <input> or <textarea>, the text node which
297 : // has composition string is always recreated with same content. Therefore,
298 : // we need to nodify mComposition of text node destruction and replacing
299 : // composing string when this receives eCompositionChange event next time.
300 0 : if (mComposition &&
301 0 : mComposition->GetContainerTextNode() &&
302 0 : !mComposition->GetContainerTextNode()->IsInComposedDoc()) {
303 0 : mComposition->OnTextNodeRemoved();
304 : }
305 :
306 : // Show the caret.
307 0 : selectionController->SetCaretReadOnly(false);
308 0 : selectionController->SetDisplaySelection(
309 0 : nsISelectionController::SELECTION_ON);
310 : // Show all the selection reflected to user.
311 0 : selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
312 :
313 0 : MOZ_ASSERT(IsInitialized());
314 :
315 0 : Selection* selection = GetSelection();
316 0 : if (selection) {
317 0 : selection->AddSelectionListener(this);
318 : }
319 :
320 : // Make sure that the editor will be destroyed properly
321 0 : mDidPreDestroy = false;
322 : // Make sure that the ediotr will be created properly
323 0 : mDidPostCreate = false;
324 :
325 0 : return NS_OK;
326 : }
327 :
328 : nsresult
329 0 : EditorBase::PostCreate()
330 : {
331 : // Synchronize some stuff for the flags. SetFlags() will initialize
332 : // something by the flag difference. This is first time of that, so, all
333 : // initializations must be run. For such reason, we need to invert mFlags
334 : // value first.
335 0 : mFlags = ~mFlags;
336 0 : nsresult rv = SetFlags(~mFlags);
337 0 : NS_ENSURE_SUCCESS(rv, rv);
338 :
339 : // These operations only need to happen on the first PostCreate call
340 0 : if (!mDidPostCreate) {
341 0 : mDidPostCreate = true;
342 :
343 : // Set up listeners
344 0 : CreateEventListeners();
345 0 : rv = InstallEventListeners();
346 0 : NS_ENSURE_SUCCESS(rv, rv);
347 :
348 : // nuke the modification count, so the doc appears unmodified
349 : // do this before we notify listeners
350 0 : ResetModificationCount();
351 :
352 : // update the UI with our state
353 0 : NotifyDocumentListeners(eDocumentCreated);
354 0 : NotifyDocumentListeners(eDocumentStateChanged);
355 : }
356 :
357 : // update nsTextStateManager and caret if we have focus
358 0 : nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
359 0 : if (focusedContent) {
360 0 : InitializeSelection(focusedContent);
361 :
362 : // If the text control gets reframed during focus, Focus() would not be
363 : // called, so take a chance here to see if we need to spell check the text
364 : // control.
365 0 : mEventListener->SpellCheckIfNeeded();
366 :
367 0 : IMEState newState;
368 0 : rv = GetPreferredIMEState(&newState);
369 0 : NS_ENSURE_SUCCESS(rv, NS_OK);
370 : // May be null in design mode
371 0 : nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
372 0 : IMEStateManager::UpdateIMEState(newState, content, this);
373 : }
374 :
375 : // FYI: This call might cause destroying this editor.
376 0 : IMEStateManager::OnEditorInitialized(*this);
377 :
378 0 : return NS_OK;
379 : }
380 :
381 : void
382 0 : EditorBase::SetTextInputListener(TextInputListener* aTextInputListener)
383 : {
384 0 : MOZ_ASSERT(!mTextInputListener || !aTextInputListener ||
385 : mTextInputListener == aTextInputListener);
386 0 : mTextInputListener = aTextInputListener;
387 0 : }
388 :
389 : void
390 0 : EditorBase::SetIMEContentObserver(IMEContentObserver* aIMEContentObserver)
391 : {
392 0 : MOZ_ASSERT(!mIMEContentObserver || !aIMEContentObserver ||
393 : mIMEContentObserver == aIMEContentObserver);
394 0 : mIMEContentObserver = aIMEContentObserver;
395 0 : }
396 :
397 : void
398 0 : EditorBase::CreateEventListeners()
399 : {
400 : // Don't create the handler twice
401 0 : if (!mEventListener) {
402 0 : mEventListener = new EditorEventListener();
403 : }
404 0 : }
405 :
406 : nsresult
407 0 : EditorBase::InstallEventListeners()
408 : {
409 0 : if (NS_WARN_IF(!IsInitialized()) || NS_WARN_IF(!mEventListener)) {
410 : return NS_ERROR_NOT_INITIALIZED;
411 : }
412 :
413 : // Initialize the event target.
414 0 : nsCOMPtr<nsIContent> rootContent = GetRoot();
415 0 : NS_ENSURE_TRUE(rootContent, NS_ERROR_NOT_AVAILABLE);
416 0 : mEventTarget = do_QueryInterface(rootContent->GetParent());
417 0 : NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_AVAILABLE);
418 :
419 0 : nsresult rv = mEventListener->Connect(this);
420 0 : if (mComposition) {
421 : // Restart to handle composition with new editor contents.
422 0 : mComposition->StartHandlingComposition(this);
423 : }
424 : return rv;
425 : }
426 :
427 : void
428 0 : EditorBase::RemoveEventListeners()
429 : {
430 0 : if (!IsInitialized() || !mEventListener) {
431 : return;
432 : }
433 0 : mEventListener->Disconnect();
434 0 : if (mComposition) {
435 : // Even if this is called, don't release mComposition because this is
436 : // may be reused after reframing.
437 0 : mComposition->EndHandlingComposition(this);
438 : }
439 0 : mEventTarget = nullptr;
440 : }
441 :
442 : bool
443 0 : EditorBase::GetDesiredSpellCheckState()
444 : {
445 : // Check user override on this element
446 0 : if (mSpellcheckCheckboxState != eTriUnset) {
447 0 : return (mSpellcheckCheckboxState == eTriTrue);
448 : }
449 :
450 : // Check user preferences
451 0 : int32_t spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1);
452 :
453 0 : if (!spellcheckLevel) {
454 : return false; // Spellchecking forced off globally
455 : }
456 :
457 0 : if (!CanEnableSpellCheck()) {
458 : return false;
459 : }
460 :
461 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
462 0 : if (presShell) {
463 0 : nsPresContext* context = presShell->GetPresContext();
464 0 : if (context && !context->IsDynamic()) {
465 : return false;
466 : }
467 : }
468 :
469 : // Check DOM state
470 0 : nsCOMPtr<nsIContent> content = GetExposedRoot();
471 0 : if (!content) {
472 : return false;
473 : }
474 :
475 0 : auto element = nsGenericHTMLElement::FromNode(content);
476 0 : if (!element) {
477 : return false;
478 : }
479 :
480 0 : if (!IsPlaintextEditor()) {
481 : // Some of the page content might be editable and some not, if spellcheck=
482 : // is explicitly set anywhere, so if there's anything editable on the page,
483 : // return true and let the spellchecker figure it out.
484 0 : nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(content->GetComposedDoc());
485 0 : return doc && doc->IsEditingOn();
486 : }
487 :
488 0 : return element->Spellcheck();
489 : }
490 :
491 : void
492 0 : EditorBase::PreDestroy(bool aDestroyingFrames)
493 : {
494 0 : if (mDidPreDestroy) {
495 : return;
496 : }
497 :
498 0 : Selection* selection = GetSelection();
499 0 : if (selection) {
500 0 : selection->RemoveSelectionListener(this);
501 : }
502 :
503 0 : IMEStateManager::OnEditorDestroying(*this);
504 :
505 : // Let spellchecker clean up its observers etc. It is important not to
506 : // actually free the spellchecker here, since the spellchecker could have
507 : // caused flush notifications, which could have gotten here if a textbox
508 : // is being removed. Setting the spellchecker to nullptr could free the
509 : // object that is still in use! It will be freed when the editor is
510 : // destroyed.
511 0 : if (mInlineSpellChecker)
512 0 : mInlineSpellChecker->Cleanup(aDestroyingFrames);
513 :
514 : // tell our listeners that the doc is going away
515 0 : NotifyDocumentListeners(eDocumentToBeDestroyed);
516 :
517 : // Unregister event listeners
518 0 : RemoveEventListeners();
519 : // If this editor is still hiding the caret, we need to restore it.
520 0 : HideCaret(false);
521 0 : mActionListeners.Clear();
522 0 : mEditorObservers.Clear();
523 0 : mDocStateListeners.Clear();
524 0 : mInlineSpellChecker = nullptr;
525 0 : mTextServicesDocument = nullptr;
526 0 : mTextInputListener = nullptr;
527 0 : mSpellcheckCheckboxState = eTriUnset;
528 0 : mRootElement = nullptr;
529 :
530 : // Transaction may grab this instance. Therefore, they should be released
531 : // here for stopping the circular reference with this instance.
532 0 : if (mTransactionManager) {
533 0 : DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
534 0 : NS_WARNING_ASSERTION(disabledUndoRedo,
535 : "Failed to disable undo/redo transactions");
536 0 : mTransactionManager = nullptr;
537 : }
538 :
539 0 : mDidPreDestroy = true;
540 : }
541 :
542 : NS_IMETHODIMP
543 0 : EditorBase::GetFlags(uint32_t* aFlags)
544 : {
545 : // NOTE: If you need to override this method, you need to make Flags()
546 : // virtual.
547 0 : *aFlags = Flags();
548 0 : return NS_OK;
549 : }
550 :
551 : NS_IMETHODIMP
552 0 : EditorBase::SetFlags(uint32_t aFlags)
553 : {
554 0 : if (mFlags == aFlags) {
555 : return NS_OK;
556 : }
557 :
558 0 : bool spellcheckerWasEnabled = CanEnableSpellCheck();
559 0 : mFlags = aFlags;
560 :
561 0 : if (!IsInitialized()) {
562 : // If we're initializing, we shouldn't do anything now.
563 : // SetFlags() will be called by PostCreate(),
564 : // we should synchronize some stuff for the flags at that time.
565 : return NS_OK;
566 : }
567 :
568 : // The flag change may cause the spellchecker state change
569 0 : if (CanEnableSpellCheck() != spellcheckerWasEnabled) {
570 0 : SyncRealTimeSpell();
571 : }
572 :
573 : // If this is called from PostCreate(), it will update the IME state if it's
574 : // necessary.
575 0 : if (!mDidPostCreate) {
576 : return NS_OK;
577 : }
578 :
579 : // Might be changing editable state, so, we need to reset current IME state
580 : // if we're focused and the flag change causes IME state change.
581 0 : nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
582 0 : if (focusedContent) {
583 0 : IMEState newState;
584 0 : nsresult rv = GetPreferredIMEState(&newState);
585 0 : if (NS_SUCCEEDED(rv)) {
586 : // NOTE: When the enabled state isn't going to be modified, this method
587 : // is going to do nothing.
588 0 : nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
589 0 : IMEStateManager::UpdateIMEState(newState, content, this);
590 : }
591 : }
592 :
593 : return NS_OK;
594 : }
595 :
596 : NS_IMETHODIMP
597 0 : EditorBase::GetIsSelectionEditable(bool* aIsSelectionEditable)
598 : {
599 0 : NS_ENSURE_ARG_POINTER(aIsSelectionEditable);
600 0 : *aIsSelectionEditable = IsSelectionEditable();
601 0 : return NS_OK;
602 : }
603 :
604 : bool
605 0 : EditorBase::IsSelectionEditable()
606 : {
607 : // get current selection
608 0 : RefPtr<Selection> selection = GetSelection();
609 0 : if (NS_WARN_IF(!selection)) {
610 : return false;
611 : }
612 :
613 0 : if (!mIsHTMLEditorClass) {
614 : // XXX we just check that the anchor node is editable at the moment
615 : // we should check that all nodes in the selection are editable
616 0 : nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode();
617 0 : return anchorNode && IsEditable(anchorNode);
618 : }
619 :
620 0 : nsINode* anchorNode = selection->GetAnchorNode();
621 0 : nsINode* focusNode = selection->GetFocusNode();
622 0 : if (!anchorNode || !focusNode) {
623 : return false;
624 : }
625 :
626 : // Per the editing spec as of June 2012: we have to have a selection whose
627 : // start and end nodes are editable, and which share an ancestor editing
628 : // host. (Bug 766387.)
629 0 : bool isSelectionEditable = selection->RangeCount() &&
630 0 : anchorNode->IsEditable() &&
631 0 : focusNode->IsEditable();
632 0 : if (!isSelectionEditable) {
633 : return false;
634 : }
635 :
636 : nsINode* commonAncestor =
637 0 : selection->GetAnchorFocusRange()->GetCommonAncestor();
638 0 : while (commonAncestor && !commonAncestor->IsEditable()) {
639 0 : commonAncestor = commonAncestor->GetParentNode();
640 : }
641 : // If there is no editable common ancestor, return false.
642 0 : return !!commonAncestor;
643 : }
644 :
645 : NS_IMETHODIMP
646 0 : EditorBase::GetIsDocumentEditable(bool* aIsDocumentEditable)
647 : {
648 0 : NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
649 0 : nsCOMPtr<nsIDocument> doc = GetDocument();
650 0 : *aIsDocumentEditable = doc && IsModifiable();
651 :
652 : return NS_OK;
653 : }
654 :
655 : NS_IMETHODIMP
656 0 : EditorBase::GetDocument(nsIDocument** aDoc)
657 : {
658 0 : NS_IF_ADDREF(*aDoc = mDocument);
659 0 : return *aDoc ? NS_OK : NS_ERROR_NOT_INITIALIZED;
660 : }
661 :
662 : already_AddRefed<nsIWidget>
663 0 : EditorBase::GetWidget()
664 : {
665 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
666 0 : NS_ENSURE_TRUE(ps, nullptr);
667 0 : nsPresContext* pc = ps->GetPresContext();
668 0 : NS_ENSURE_TRUE(pc, nullptr);
669 0 : nsCOMPtr<nsIWidget> widget = pc->GetRootWidget();
670 0 : NS_ENSURE_TRUE(widget.get(), nullptr);
671 0 : return widget.forget();
672 : }
673 :
674 : NS_IMETHODIMP
675 0 : EditorBase::GetContentsMIMEType(char** aContentsMIMEType)
676 : {
677 0 : NS_ENSURE_ARG_POINTER(aContentsMIMEType);
678 0 : *aContentsMIMEType = ToNewCString(mContentMIMEType);
679 0 : return NS_OK;
680 : }
681 :
682 : NS_IMETHODIMP
683 0 : EditorBase::SetContentsMIMEType(const char* aContentsMIMEType)
684 : {
685 0 : mContentMIMEType.Assign(aContentsMIMEType ? aContentsMIMEType : "");
686 0 : return NS_OK;
687 : }
688 :
689 : NS_IMETHODIMP
690 0 : EditorBase::GetSelectionController(nsISelectionController** aSel)
691 : {
692 0 : NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER);
693 0 : *aSel = nullptr; // init out param
694 0 : nsCOMPtr<nsISelectionController> selCon = GetSelectionController();
695 0 : if (NS_WARN_IF(!selCon)) {
696 : return NS_ERROR_NOT_INITIALIZED;
697 : }
698 0 : selCon.forget(aSel);
699 0 : return NS_OK;
700 : }
701 :
702 : NS_IMETHODIMP
703 0 : EditorBase::DeleteSelection(EDirection aAction,
704 : EStripWrappers aStripWrappers)
705 : {
706 0 : return NS_ERROR_NOT_IMPLEMENTED;
707 : }
708 :
709 : NS_IMETHODIMP
710 0 : EditorBase::GetSelection(Selection** aSelection)
711 : {
712 0 : return GetSelection(SelectionType::eNormal, aSelection);
713 : }
714 :
715 : nsresult
716 0 : EditorBase::GetSelection(SelectionType aSelectionType,
717 : Selection** aSelection)
718 : {
719 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
720 0 : *aSelection = nullptr;
721 0 : nsISelectionController* selcon = GetSelectionController();
722 0 : if (!selcon) {
723 : return NS_ERROR_NOT_INITIALIZED;
724 : }
725 : RefPtr<Selection> selection =
726 0 : selcon->GetSelection(ToRawSelectionType(aSelectionType));
727 0 : if (!selection) {
728 : return NS_ERROR_INVALID_ARG;
729 : }
730 0 : selection.forget(aSelection);
731 0 : return NS_OK;
732 : }
733 :
734 : NS_IMETHODIMP
735 0 : EditorBase::DoTransaction(nsITransaction* aTxn)
736 : {
737 0 : return DoTransaction(nullptr, aTxn);
738 : }
739 :
740 : nsresult
741 0 : EditorBase::DoTransaction(Selection* aSelection, nsITransaction* aTxn)
742 : {
743 0 : if (mPlaceholderBatch && !mPlaceholderTransaction) {
744 : mPlaceholderTransaction =
745 0 : PlaceholderTransaction::Create(*this, mPlaceholderName, std::move(mSelState));
746 0 : MOZ_ASSERT(mSelState.isNothing());
747 :
748 : // We will recurse, but will not hit this case in the nested call
749 0 : DoTransaction(mPlaceholderTransaction);
750 :
751 0 : if (mTransactionManager) {
752 : nsCOMPtr<nsITransaction> topTransaction =
753 0 : mTransactionManager->PeekUndoStack();
754 : nsCOMPtr<nsIAbsorbingTransaction> topAbsorbingTransaction =
755 0 : do_QueryInterface(topTransaction);
756 0 : if (topAbsorbingTransaction) {
757 : RefPtr<PlaceholderTransaction> topPlaceholderTransaction =
758 0 : topAbsorbingTransaction->AsPlaceholderTransaction();
759 0 : if (topPlaceholderTransaction) {
760 : // there is a placeholder transaction on top of the undo stack. It
761 : // is either the one we just created, or an earlier one that we are
762 : // now merging into. From here on out remember this placeholder
763 : // instead of the one we just created.
764 0 : mPlaceholderTransaction = topPlaceholderTransaction;
765 : }
766 : }
767 : }
768 : }
769 :
770 0 : if (aTxn) {
771 : // XXX: Why are we doing selection specific batching stuff here?
772 : // XXX: Most entry points into the editor have auto variables that
773 : // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
774 : // XXX: these selection batch calls no-ops.
775 : // XXX:
776 : // XXX: I suspect that this was placed here to avoid multiple
777 : // XXX: selection changed notifications from happening until after
778 : // XXX: the transaction was done. I suppose that can still happen
779 : // XXX: if an embedding application called DoTransaction() directly
780 : // XXX: to pump its own transactions through the system, but in that
781 : // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
782 : // XXX: its auto equivalent AutoUpdateViewBatch to ensure that
783 : // XXX: selection listeners have access to accurate frame data?
784 : // XXX:
785 : // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
786 : // XXX: we will need to make sure that they are disabled during
787 : // XXX: the init of the editor for text widgets to avoid layout
788 : // XXX: re-entry during initial reflow. - kin
789 :
790 : // get the selection and start a batch change
791 0 : RefPtr<Selection> selection = aSelection ? aSelection : GetSelection();
792 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
793 :
794 0 : SelectionBatcher selectionBatcher(selection);
795 :
796 : nsresult rv;
797 0 : if (mTransactionManager) {
798 0 : RefPtr<TransactionManager> transactionManager(mTransactionManager);
799 0 : rv = transactionManager->DoTransaction(aTxn);
800 : } else {
801 0 : rv = aTxn->DoTransaction();
802 : }
803 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
804 0 : return rv;
805 : }
806 :
807 0 : DoAfterDoTransaction(aTxn);
808 : }
809 :
810 : return NS_OK;
811 : }
812 :
813 : NS_IMETHODIMP
814 0 : EditorBase::EnableUndo(bool aEnable)
815 : {
816 : // XXX Should we return NS_ERROR_FAILURE if EdnableUndoRedo() or
817 : // DisableUndoRedo() returns false?
818 0 : if (aEnable) {
819 0 : DebugOnly<bool> enabledUndoRedo = EnableUndoRedo();
820 0 : NS_WARNING_ASSERTION(enabledUndoRedo,
821 : "Failed to enable undo/redo transactions");
822 : return NS_OK;
823 : }
824 0 : DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
825 0 : NS_WARNING_ASSERTION(disabledUndoRedo,
826 : "Failed to disable undo/redo transactions");
827 : return NS_OK;
828 : }
829 :
830 : NS_IMETHODIMP
831 0 : EditorBase::GetTransactionManager(nsITransactionManager** aTransactionManager)
832 : {
833 0 : if (NS_WARN_IF(!aTransactionManager)) {
834 : return NS_ERROR_INVALID_ARG;
835 : }
836 0 : if (NS_WARN_IF(!mTransactionManager)) {
837 : return NS_ERROR_FAILURE;
838 : }
839 0 : NS_IF_ADDREF(*aTransactionManager = mTransactionManager);
840 0 : return NS_OK;
841 : }
842 :
843 : NS_IMETHODIMP
844 0 : EditorBase::Undo(uint32_t aCount)
845 : {
846 0 : return NS_ERROR_NOT_IMPLEMENTED;
847 : }
848 :
849 : NS_IMETHODIMP
850 0 : EditorBase::CanUndo(bool* aIsEnabled,
851 : bool* aCanUndo)
852 : {
853 0 : if (NS_WARN_IF(!aIsEnabled) || NS_WARN_IF(!aCanUndo)) {
854 : return NS_ERROR_INVALID_ARG;
855 : }
856 0 : *aCanUndo = CanUndo();
857 0 : *aIsEnabled = IsUndoRedoEnabled();
858 0 : return NS_OK;
859 : }
860 :
861 : NS_IMETHODIMP
862 0 : EditorBase::Redo(uint32_t aCount)
863 : {
864 0 : return NS_ERROR_NOT_IMPLEMENTED;
865 : }
866 :
867 : NS_IMETHODIMP
868 0 : EditorBase::CanRedo(bool* aIsEnabled, bool* aCanRedo)
869 : {
870 0 : if (NS_WARN_IF(!aIsEnabled) || NS_WARN_IF(!aCanRedo)) {
871 : return NS_ERROR_INVALID_ARG;
872 : }
873 0 : *aCanRedo = CanRedo();
874 0 : *aIsEnabled = IsUndoRedoEnabled();
875 0 : return NS_OK;
876 : }
877 :
878 : NS_IMETHODIMP
879 0 : EditorBase::BeginTransaction()
880 : {
881 0 : BeginUpdateViewBatch();
882 :
883 0 : if (mTransactionManager) {
884 0 : RefPtr<TransactionManager> transactionManager(mTransactionManager);
885 0 : transactionManager->BeginBatch(nullptr);
886 : }
887 :
888 0 : return NS_OK;
889 : }
890 :
891 : NS_IMETHODIMP
892 0 : EditorBase::EndTransaction()
893 : {
894 0 : if (mTransactionManager) {
895 0 : RefPtr<TransactionManager> transactionManager(mTransactionManager);
896 0 : transactionManager->EndBatch(false);
897 : }
898 :
899 0 : EndUpdateViewBatch();
900 :
901 0 : return NS_OK;
902 : }
903 :
904 : void
905 0 : EditorBase::BeginPlaceholderTransaction(nsAtom* aTransactionName)
906 : {
907 0 : MOZ_ASSERT(mPlaceholderBatch >= 0, "negative placeholder batch count!");
908 0 : if (!mPlaceholderBatch) {
909 0 : NotifyEditorObservers(eNotifyEditorObserversOfBefore);
910 : // time to turn on the batch
911 0 : BeginUpdateViewBatch();
912 0 : mPlaceholderTransaction = nullptr;
913 0 : mPlaceholderName = aTransactionName;
914 0 : RefPtr<Selection> selection = GetSelection();
915 0 : if (selection) {
916 0 : mSelState.emplace();
917 0 : mSelState->SaveSelection(selection);
918 : // Composition transaction can modify multiple nodes and it merges text
919 : // node for ime into single text node.
920 : // So if current selection is into IME text node, it might be failed
921 : // to restore selection by UndoTransaction.
922 : // So we need update selection by range updater.
923 0 : if (mPlaceholderName == nsGkAtoms::IMETxnName) {
924 0 : mRangeUpdater.RegisterSelectionState(*mSelState);
925 : }
926 : }
927 : }
928 0 : mPlaceholderBatch++;
929 0 : }
930 :
931 : void
932 0 : EditorBase::EndPlaceholderTransaction()
933 : {
934 0 : MOZ_ASSERT(mPlaceholderBatch > 0,
935 : "zero or negative placeholder batch count when ending batch!");
936 0 : if (mPlaceholderBatch == 1) {
937 0 : RefPtr<Selection> selection = GetSelection();
938 :
939 : // By making the assumption that no reflow happens during the calls
940 : // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to
941 : // allow the selection to cache a frame offset which is used by the
942 : // caret drawing code. We only enable this cache here; at other times,
943 : // we have no way to know whether reflow invalidates it
944 : // See bugs 35296 and 199412.
945 0 : if (selection) {
946 0 : selection->SetCanCacheFrameOffset(true);
947 : }
948 :
949 : // time to turn off the batch
950 0 : EndUpdateViewBatch();
951 : // make sure selection is in view
952 :
953 : // After ScrollSelectionIntoView(), the pending notifications might be
954 : // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
955 0 : ScrollSelectionIntoView(false);
956 :
957 : // cached for frame offset are Not available now
958 0 : if (selection) {
959 0 : selection->SetCanCacheFrameOffset(false);
960 : }
961 :
962 0 : if (mSelState) {
963 : // we saved the selection state, but never got to hand it to placeholder
964 : // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
965 0 : if (mPlaceholderName == nsGkAtoms::IMETxnName) {
966 0 : mRangeUpdater.DropSelectionState(*mSelState);
967 : }
968 0 : mSelState.reset();
969 : }
970 : // We might have never made a placeholder if no action took place.
971 0 : if (mPlaceholderTransaction) {
972 0 : mPlaceholderTransaction->EndPlaceHolderBatch();
973 : // notify editor observers of action but if composing, it's done by
974 : // compositionchange event handler.
975 0 : if (!mComposition) {
976 0 : NotifyEditorObservers(eNotifyEditorObserversOfEnd);
977 : }
978 0 : mPlaceholderTransaction = nullptr;
979 : } else {
980 0 : NotifyEditorObservers(eNotifyEditorObserversOfCancel);
981 : }
982 : }
983 0 : mPlaceholderBatch--;
984 0 : }
985 :
986 : NS_IMETHODIMP
987 0 : EditorBase::ShouldTxnSetSelection(bool* aResult)
988 : {
989 0 : NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
990 0 : *aResult = mShouldTxnSetSelection;
991 0 : return NS_OK;
992 : }
993 :
994 : NS_IMETHODIMP
995 0 : EditorBase::SetShouldTxnSetSelection(bool aShould)
996 : {
997 0 : mShouldTxnSetSelection = aShould;
998 0 : return NS_OK;
999 : }
1000 :
1001 : NS_IMETHODIMP
1002 0 : EditorBase::GetDocumentIsEmpty(bool* aDocumentIsEmpty)
1003 : {
1004 0 : *aDocumentIsEmpty = true;
1005 :
1006 0 : dom::Element* root = GetRoot();
1007 0 : NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
1008 :
1009 0 : *aDocumentIsEmpty = !root->HasChildren();
1010 0 : return NS_OK;
1011 : }
1012 :
1013 : // XXX: The rule system should tell us which node to select all on (ie, the
1014 : // root, or the body)
1015 : NS_IMETHODIMP
1016 0 : EditorBase::SelectAll()
1017 : {
1018 : // XXX Why doesn't this check if the document is alive?
1019 0 : if (!IsInitialized()) {
1020 : return NS_ERROR_NOT_INITIALIZED;
1021 : }
1022 :
1023 0 : nsresult rv = SelectAllInternal();
1024 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1025 : return rv;
1026 : }
1027 0 : return NS_OK;
1028 : }
1029 :
1030 : nsresult
1031 0 : EditorBase::SelectAllInternal()
1032 : {
1033 0 : MOZ_ASSERT(IsInitialized());
1034 :
1035 0 : CommitComposition();
1036 0 : if (NS_WARN_IF(Destroyed())) {
1037 : return NS_ERROR_EDITOR_DESTROYED;
1038 : }
1039 :
1040 : // XXX Do we need to keep handling after committing composition causes moving
1041 : // focus to different element? Although TextEditor has independent
1042 : // selection, so, we may not see any odd behavior even in such case.
1043 :
1044 0 : RefPtr<Selection> selection = GetSelection();
1045 0 : if (NS_WARN_IF(!selection)) {
1046 : return NS_ERROR_FAILURE;
1047 : }
1048 0 : nsresult rv = SelectEntireDocument(selection);
1049 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1050 : return rv;
1051 : }
1052 0 : return NS_OK;
1053 : }
1054 :
1055 : NS_IMETHODIMP
1056 0 : EditorBase::BeginningOfDocument()
1057 : {
1058 : // XXX Why doesn't this check if the document is alive?
1059 0 : if (!IsInitialized()) {
1060 : return NS_ERROR_NOT_INITIALIZED;
1061 : }
1062 :
1063 : // get the selection
1064 0 : RefPtr<Selection> selection = GetSelection();
1065 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
1066 :
1067 : // get the root element
1068 0 : dom::Element* rootElement = GetRoot();
1069 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
1070 :
1071 : // find first editable thingy
1072 0 : nsCOMPtr<nsINode> firstNode = GetFirstEditableNode(rootElement);
1073 0 : if (!firstNode) {
1074 : // just the root node, set selection to inside the root
1075 0 : return selection->Collapse(rootElement, 0);
1076 : }
1077 :
1078 0 : if (firstNode->NodeType() == nsINode::TEXT_NODE) {
1079 : // If firstNode is text, set selection to beginning of the text node.
1080 0 : return selection->Collapse(firstNode, 0);
1081 : }
1082 :
1083 : // Otherwise, it's a leaf node and we set the selection just in front of it.
1084 0 : nsCOMPtr<nsIContent> parent = firstNode->GetParent();
1085 0 : if (!parent) {
1086 : return NS_ERROR_NULL_POINTER;
1087 : }
1088 :
1089 0 : MOZ_ASSERT(parent->ComputeIndexOf(firstNode) == 0,
1090 : "How come the first node isn't the left most child in its parent?");
1091 0 : return selection->Collapse(parent, 0);
1092 : }
1093 :
1094 : NS_IMETHODIMP
1095 0 : EditorBase::EndOfDocument()
1096 : {
1097 0 : RefPtr<Selection> selection = GetSelection();
1098 0 : return CollapseSelectionToEnd(selection);
1099 : }
1100 :
1101 : nsresult
1102 0 : EditorBase::CollapseSelectionToEnd(Selection* aSelection)
1103 : {
1104 : // XXX Why doesn't this check if the document is alive?
1105 0 : if (NS_WARN_IF(!IsInitialized())) {
1106 : return NS_ERROR_NOT_INITIALIZED;
1107 : }
1108 :
1109 0 : if (NS_WARN_IF(!aSelection)) {
1110 : return NS_ERROR_NULL_POINTER;
1111 : }
1112 :
1113 : // get the root element
1114 0 : nsINode* node = GetRoot();
1115 0 : if (NS_WARN_IF(!node)) {
1116 : return NS_ERROR_NULL_POINTER;
1117 : }
1118 :
1119 0 : nsINode* child = node->GetLastChild();
1120 0 : while (child && IsContainer(child)) {
1121 0 : node = child;
1122 0 : child = node->GetLastChild();
1123 : }
1124 :
1125 0 : uint32_t length = node->Length();
1126 0 : return aSelection->Collapse(node, static_cast<int32_t>(length));
1127 : }
1128 :
1129 : NS_IMETHODIMP
1130 0 : EditorBase::GetDocumentModified(bool* outDocModified)
1131 : {
1132 0 : NS_ENSURE_TRUE(outDocModified, NS_ERROR_NULL_POINTER);
1133 :
1134 0 : int32_t modCount = 0;
1135 0 : GetModificationCount(&modCount);
1136 :
1137 0 : *outDocModified = (modCount != 0);
1138 0 : return NS_OK;
1139 : }
1140 :
1141 : NS_IMETHODIMP
1142 0 : EditorBase::GetDocumentCharacterSet(nsACString& characterSet)
1143 : {
1144 0 : nsCOMPtr<nsIDocument> document = GetDocument();
1145 0 : if (NS_WARN_IF(!document)) {
1146 : return NS_ERROR_UNEXPECTED;
1147 : }
1148 0 : document->GetDocumentCharacterSet()->Name(characterSet);
1149 0 : return NS_OK;
1150 : }
1151 :
1152 : NS_IMETHODIMP
1153 0 : EditorBase::SetDocumentCharacterSet(const nsACString& characterSet)
1154 : {
1155 0 : nsCOMPtr<nsIDocument> document = GetDocument();
1156 0 : if (NS_WARN_IF(!document)) {
1157 : return NS_ERROR_UNEXPECTED;
1158 : }
1159 : // This method is scriptable, so add-ons could pass in something other
1160 : // than a canonical name.
1161 0 : auto encoding = Encoding::ForLabelNoReplacement(characterSet);
1162 0 : if (!encoding) {
1163 : return NS_ERROR_INVALID_ARG;
1164 : }
1165 0 : document->SetDocumentCharacterSet(WrapNotNull(encoding));
1166 0 : return NS_OK;
1167 : }
1168 :
1169 : NS_IMETHODIMP
1170 0 : EditorBase::Cut()
1171 : {
1172 0 : return NS_ERROR_NOT_IMPLEMENTED;
1173 : }
1174 :
1175 : NS_IMETHODIMP
1176 0 : EditorBase::CanCut(bool* aCanCut)
1177 : {
1178 0 : return NS_ERROR_NOT_IMPLEMENTED;
1179 : }
1180 :
1181 : NS_IMETHODIMP
1182 0 : EditorBase::Copy()
1183 : {
1184 0 : return NS_ERROR_NOT_IMPLEMENTED;
1185 : }
1186 :
1187 : NS_IMETHODIMP
1188 0 : EditorBase::CanCopy(bool* aCanCut)
1189 : {
1190 0 : return NS_ERROR_NOT_IMPLEMENTED;
1191 : }
1192 :
1193 : NS_IMETHODIMP
1194 0 : EditorBase::CanDelete(bool* aCanDelete)
1195 : {
1196 0 : return NS_ERROR_NOT_IMPLEMENTED;
1197 : }
1198 :
1199 : NS_IMETHODIMP
1200 0 : EditorBase::Paste(int32_t aSelectionType)
1201 : {
1202 0 : return NS_ERROR_NOT_IMPLEMENTED;
1203 : }
1204 :
1205 : NS_IMETHODIMP
1206 0 : EditorBase::PasteTransferable(nsITransferable* aTransferable)
1207 : {
1208 0 : return NS_ERROR_NOT_IMPLEMENTED;
1209 : }
1210 :
1211 : NS_IMETHODIMP
1212 0 : EditorBase::CanPaste(int32_t aSelectionType, bool* aCanPaste)
1213 : {
1214 0 : return NS_ERROR_NOT_IMPLEMENTED;
1215 : }
1216 :
1217 : NS_IMETHODIMP
1218 0 : EditorBase::SetAttribute(Element* aElement,
1219 : const nsAString& aAttribute,
1220 : const nsAString& aValue)
1221 : {
1222 0 : if (NS_WARN_IF(aAttribute.IsEmpty())) {
1223 : return NS_ERROR_INVALID_ARG;
1224 : }
1225 0 : if (NS_WARN_IF(!aElement)) {
1226 : return NS_ERROR_INVALID_ARG;
1227 : }
1228 0 : RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
1229 0 : return SetAttributeWithTransaction(*aElement, *attribute, aValue);
1230 : }
1231 :
1232 : nsresult
1233 0 : EditorBase::SetAttributeWithTransaction(Element& aElement,
1234 : nsAtom& aAttribute,
1235 : const nsAString& aValue)
1236 : {
1237 : RefPtr<ChangeAttributeTransaction> transaction =
1238 0 : ChangeAttributeTransaction::Create(aElement, aAttribute, aValue);
1239 0 : return DoTransaction(transaction);
1240 : }
1241 :
1242 : NS_IMETHODIMP
1243 0 : EditorBase::GetAttributeValue(Element* aElement,
1244 : const nsAString& aAttribute,
1245 : nsAString& aResultValue,
1246 : bool* aResultIsSet)
1247 : {
1248 0 : NS_ENSURE_TRUE(aResultIsSet, NS_ERROR_NULL_POINTER);
1249 0 : *aResultIsSet = false;
1250 0 : if (!aElement) {
1251 : return NS_OK;
1252 : }
1253 0 : nsAutoString value;
1254 0 : aElement->GetAttribute(aAttribute, value);
1255 0 : if (!DOMStringIsNull(value)) {
1256 0 : *aResultIsSet = true;
1257 : aResultValue = value;
1258 : }
1259 0 : return NS_OK;
1260 : }
1261 :
1262 : NS_IMETHODIMP
1263 0 : EditorBase::RemoveAttribute(Element* aElement,
1264 : const nsAString& aAttribute)
1265 : {
1266 0 : if (NS_WARN_IF(aAttribute.IsEmpty())) {
1267 : return NS_ERROR_INVALID_ARG;
1268 : }
1269 0 : if (NS_WARN_IF(!aElement)) {
1270 : return NS_ERROR_INVALID_ARG;
1271 : }
1272 0 : RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
1273 0 : return RemoveAttributeWithTransaction(*aElement, *attribute);
1274 : }
1275 :
1276 : nsresult
1277 0 : EditorBase::RemoveAttributeWithTransaction(Element& aElement,
1278 : nsAtom& aAttribute)
1279 : {
1280 : // XXX If aElement doesn't have aAttribute, shouldn't we stop creating
1281 : // the transaction? Otherwise, there will be added a transaction
1282 : // which does nothing at doing undo/redo.
1283 : RefPtr<ChangeAttributeTransaction> transaction =
1284 0 : ChangeAttributeTransaction::CreateToRemove(aElement, aAttribute);
1285 0 : return DoTransaction(transaction);
1286 : }
1287 :
1288 : NS_IMETHODIMP
1289 1 : EditorBase::MarkNodeDirty(nsINode* aNode)
1290 : {
1291 : // Mark the node dirty, but not for webpages (bug 599983)
1292 1 : if (!OutputsMozDirty()) {
1293 : return NS_OK;
1294 : }
1295 3 : if (RefPtr<Element> element = Element::FromNodeOrNull(aNode)) {
1296 1 : element->SetAttr(kNameSpaceID_None, nsGkAtoms::mozdirty, EmptyString(),
1297 0 : false);
1298 : }
1299 1 : return NS_OK;
1300 : }
1301 :
1302 : NS_IMETHODIMP
1303 0 : EditorBase::GetInlineSpellChecker(bool autoCreate,
1304 : nsIInlineSpellChecker** aInlineSpellChecker)
1305 : {
1306 0 : NS_ENSURE_ARG_POINTER(aInlineSpellChecker);
1307 :
1308 1 : if (mDidPreDestroy) {
1309 : // Don't allow people to get or create the spell checker once the editor
1310 : // is going away.
1311 0 : *aInlineSpellChecker = nullptr;
1312 0 : return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK;
1313 : }
1314 :
1315 : // We don't want to show the spell checking UI if there are no spell check dictionaries available.
1316 1 : bool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking();
1317 0 : if (!canSpell) {
1318 0 : *aInlineSpellChecker = nullptr;
1319 0 : return NS_ERROR_FAILURE;
1320 : }
1321 :
1322 : nsresult rv;
1323 0 : if (!mInlineSpellChecker && autoCreate) {
1324 0 : mInlineSpellChecker = new mozInlineSpellChecker();
1325 : }
1326 :
1327 2 : if (mInlineSpellChecker) {
1328 0 : rv = mInlineSpellChecker->Init(this);
1329 0 : if (NS_FAILED(rv)) {
1330 0 : mInlineSpellChecker = nullptr;
1331 : }
1332 0 : NS_ENSURE_SUCCESS(rv, rv);
1333 : }
1334 :
1335 2 : NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
1336 :
1337 0 : return NS_OK;
1338 : }
1339 :
1340 : void
1341 0 : EditorBase::SyncRealTimeSpell()
1342 : {
1343 1 : bool enable = GetDesiredSpellCheckState();
1344 :
1345 : // Initializes mInlineSpellChecker
1346 2 : nsCOMPtr<nsIInlineSpellChecker> spellChecker;
1347 0 : GetInlineSpellChecker(enable, getter_AddRefs(spellChecker));
1348 :
1349 2 : if (mInlineSpellChecker) {
1350 0 : if (!mSpellCheckerDictionaryUpdated && enable) {
1351 0 : mInlineSpellChecker->UpdateCurrentDictionary();
1352 0 : mSpellCheckerDictionaryUpdated = true;
1353 : }
1354 :
1355 : // We might have a mInlineSpellChecker even if there are no dictionaries
1356 : // available since we don't destroy the mInlineSpellChecker when the last
1357 : // dictionariy is removed, but in that case spellChecker is null
1358 0 : mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker);
1359 : }
1360 0 : }
1361 :
1362 : NS_IMETHODIMP
1363 0 : EditorBase::SetSpellcheckUserOverride(bool enable)
1364 : {
1365 0 : mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
1366 :
1367 0 : SyncRealTimeSpell();
1368 0 : return NS_OK;
1369 : }
1370 :
1371 : template<typename PT, typename CT>
1372 : already_AddRefed<Element>
1373 0 : EditorBase::CreateNodeWithTransaction(
1374 : nsAtom& aTagName,
1375 : const EditorDOMPointBase<PT, CT>& aPointToInsert)
1376 : {
1377 0 : MOZ_ASSERT(aPointToInsert.IsSetAndValid());
1378 :
1379 : // XXX We need offset at new node for mRangeUpdater. Therefore, we need
1380 : // to compute the offset now but this is expensive. So, if it's possible,
1381 : // we need to redesign mRangeUpdater as avoiding using indices.
1382 0 : Unused << aPointToInsert.Offset();
1383 :
1384 : AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
1385 : *this, EditSubAction::eCreateNode,
1386 0 : nsIEditor::eNext);
1387 :
1388 0 : RefPtr<Element> newElement;
1389 :
1390 : RefPtr<CreateElementTransaction> transaction =
1391 0 : CreateElementTransaction::Create(*this, aTagName, aPointToInsert);
1392 0 : nsresult rv = DoTransaction(transaction);
1393 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1394 : // XXX Why do we do this even when DoTransaction() returned error?
1395 0 : mRangeUpdater.SelAdjCreateNode(aPointToInsert);
1396 : } else {
1397 0 : newElement = transaction->GetNewNode();
1398 0 : MOZ_ASSERT(newElement);
1399 :
1400 : // If we succeeded to create and insert new element, we need to adjust
1401 : // ranges in mRangeUpdater. It currently requires offset of the new node.
1402 : // So, let's call it with original offset. Note that if aPointToInsert
1403 : // stores child node, it may not be at the offset since new element must
1404 : // be inserted before the old child. Although, mutation observer can do
1405 : // anything, but currently, we don't check it.
1406 0 : mRangeUpdater.SelAdjCreateNode(
1407 0 : EditorRawDOMPoint(aPointToInsert.GetContainer(),
1408 : aPointToInsert.Offset()));
1409 : }
1410 :
1411 0 : if (mRules && mRules->AsHTMLEditRules() && newElement) {
1412 0 : Selection* selection = GetSelection();
1413 0 : if (selection) {
1414 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
1415 0 : htmlEditRules->DidCreateNode(*selection, *newElement);
1416 : } else {
1417 0 : NS_WARNING("Selection has gone");
1418 : }
1419 : }
1420 :
1421 0 : if (!mActionListeners.IsEmpty()) {
1422 0 : AutoActionListenerArray listeners(mActionListeners);
1423 0 : for (auto& listener : listeners) {
1424 0 : listener->DidCreateNode(nsDependentAtomString(&aTagName),
1425 : newElement, rv);
1426 : }
1427 : }
1428 :
1429 0 : return newElement.forget();
1430 : }
1431 :
1432 : NS_IMETHODIMP
1433 0 : EditorBase::InsertNode(nsINode* aNodeToInsert,
1434 : nsINode* aContainer,
1435 : int32_t aOffset)
1436 : {
1437 0 : nsCOMPtr<nsIContent> contentToInsert = do_QueryInterface(aNodeToInsert);
1438 0 : if (NS_WARN_IF(!contentToInsert)) {
1439 : return NS_ERROR_NULL_POINTER;
1440 : }
1441 0 : if (NS_WARN_IF(!aContainer)) {
1442 : return NS_ERROR_NULL_POINTER;
1443 : }
1444 : int32_t offset =
1445 0 : aOffset < 0 ? static_cast<int32_t>(aContainer->Length()) :
1446 0 : std::min(aOffset, static_cast<int32_t>(aContainer->Length()));
1447 0 : return InsertNodeWithTransaction(*contentToInsert,
1448 0 : EditorRawDOMPoint(aContainer, offset));
1449 : }
1450 :
1451 : template<typename PT, typename CT>
1452 : nsresult
1453 1 : EditorBase::InsertNodeWithTransaction(
1454 : nsIContent& aContentToInsert,
1455 : const EditorDOMPointBase<PT, CT>& aPointToInsert)
1456 : {
1457 0 : if (NS_WARN_IF(!aPointToInsert.IsSet())) {
1458 : return NS_ERROR_INVALID_ARG;
1459 : }
1460 1 : MOZ_ASSERT(aPointToInsert.IsSetAndValid());
1461 :
1462 : AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
1463 : *this, EditSubAction::eInsertNode,
1464 2 : nsIEditor::eNext);
1465 :
1466 : RefPtr<InsertNodeTransaction> transaction =
1467 1 : InsertNodeTransaction::Create(*this, aContentToInsert, aPointToInsert);
1468 2 : nsresult rv = DoTransaction(transaction);
1469 :
1470 1 : mRangeUpdater.SelAdjInsertNode(aPointToInsert);
1471 :
1472 2 : if (mRules && mRules->AsHTMLEditRules()) {
1473 0 : Selection* selection = GetSelection();
1474 0 : if (selection) {
1475 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
1476 0 : htmlEditRules->DidInsertNode(*selection, aContentToInsert);
1477 : } else {
1478 0 : NS_WARNING("Selection has gone");
1479 : }
1480 : }
1481 :
1482 1 : if (!mActionListeners.IsEmpty()) {
1483 0 : AutoActionListenerArray listeners(mActionListeners);
1484 0 : for (auto& listener : listeners) {
1485 0 : listener->DidInsertNode(&aContentToInsert, rv);
1486 : }
1487 : }
1488 :
1489 : return rv;
1490 : }
1491 :
1492 : NS_IMETHODIMP
1493 0 : EditorBase::SplitNode(nsINode* aNode,
1494 : int32_t aOffset,
1495 : nsINode** aNewLeftNode)
1496 : {
1497 0 : if (NS_WARN_IF(!aNode)) {
1498 : return NS_ERROR_INVALID_ARG;
1499 : }
1500 :
1501 : int32_t offset = std::min(std::max(aOffset, 0),
1502 0 : static_cast<int32_t>(aNode->Length()));
1503 0 : ErrorResult error;
1504 : nsCOMPtr<nsIContent> newNode =
1505 0 : SplitNodeWithTransaction(EditorRawDOMPoint(aNode, offset), error);
1506 0 : newNode.forget(aNewLeftNode);
1507 0 : if (NS_WARN_IF(error.Failed())) {
1508 0 : return error.StealNSResult();
1509 : }
1510 : return NS_OK;
1511 : }
1512 :
1513 : template<typename PT, typename CT>
1514 : already_AddRefed<nsIContent>
1515 0 : EditorBase::SplitNodeWithTransaction(
1516 : const EditorDOMPointBase<PT, CT>& aStartOfRightNode,
1517 : ErrorResult& aError)
1518 : {
1519 0 : if (NS_WARN_IF(!aStartOfRightNode.IsSet()) ||
1520 0 : NS_WARN_IF(!aStartOfRightNode.GetContainerAsContent())) {
1521 0 : aError.Throw(NS_ERROR_INVALID_ARG);
1522 : return nullptr;
1523 : }
1524 0 : MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
1525 :
1526 : AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
1527 : *this, EditSubAction::eSplitNode,
1528 0 : nsIEditor::eNext);
1529 :
1530 : // XXX Unfortunately, storing offset of the split point in
1531 : // SplitNodeTransaction is necessary for now. We should fix this
1532 : // in a follow up bug.
1533 0 : Unused << aStartOfRightNode.Offset();
1534 :
1535 : RefPtr<SplitNodeTransaction> transaction =
1536 0 : SplitNodeTransaction::Create(*this, aStartOfRightNode);
1537 0 : aError = DoTransaction(transaction);
1538 :
1539 0 : nsCOMPtr<nsIContent> newNode = transaction->GetNewNode();
1540 0 : NS_WARNING_ASSERTION(newNode, "Failed to create a new left node");
1541 :
1542 : // XXX Some other transactions manage range updater by themselves.
1543 : // Why doesn't SplitNodeTransaction do it?
1544 0 : mRangeUpdater.SelAdjSplitNode(*aStartOfRightNode.GetContainerAsContent(),
1545 : newNode);
1546 :
1547 0 : if (mRules && mRules->AsHTMLEditRules() && newNode) {
1548 0 : Selection* selection = GetSelection();
1549 0 : if (selection) {
1550 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
1551 0 : htmlEditRules->DidSplitNode(*selection,
1552 0 : *aStartOfRightNode.GetContainer(), *newNode);
1553 : } else {
1554 0 : NS_WARNING("Selection has gone");
1555 : }
1556 : }
1557 :
1558 0 : if (mInlineSpellChecker) {
1559 0 : RefPtr<mozInlineSpellChecker> spellChecker = mInlineSpellChecker;
1560 0 : spellChecker->DidSplitNode(aStartOfRightNode.GetContainer(), newNode);
1561 : }
1562 :
1563 0 : if (!mActionListeners.IsEmpty()) {
1564 0 : AutoActionListenerArray listeners(mActionListeners);
1565 0 : for (auto& listener : listeners) {
1566 0 : listener->DidSplitNode(aStartOfRightNode.GetContainer(), newNode);
1567 : }
1568 : }
1569 :
1570 0 : if (NS_WARN_IF(aError.Failed())) {
1571 : return nullptr;
1572 : }
1573 :
1574 0 : return newNode.forget();
1575 : }
1576 :
1577 : NS_IMETHODIMP
1578 0 : EditorBase::JoinNodes(nsINode* aLeftNode,
1579 : nsINode* aRightNode,
1580 : nsINode*)
1581 : {
1582 0 : NS_ENSURE_STATE(aLeftNode && aRightNode && aLeftNode->GetParentNode());
1583 0 : return JoinNodesWithTransaction(*aLeftNode, *aRightNode);
1584 : }
1585 :
1586 : nsresult
1587 0 : EditorBase::JoinNodesWithTransaction(nsINode& aLeftNode,
1588 : nsINode& aRightNode)
1589 : {
1590 0 : nsCOMPtr<nsINode> parent = aLeftNode.GetParentNode();
1591 0 : MOZ_ASSERT(parent);
1592 :
1593 : AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
1594 : *this, EditSubAction::eJoinNodes,
1595 0 : nsIEditor::ePrevious);
1596 :
1597 : // Remember some values; later used for saved selection updating.
1598 : // Find the offset between the nodes to be joined.
1599 0 : int32_t offset = parent->ComputeIndexOf(&aRightNode);
1600 : // Find the number of children of the lefthand node
1601 0 : uint32_t oldLeftNodeLen = aLeftNode.Length();
1602 :
1603 0 : if (mRules && mRules->AsHTMLEditRules()) {
1604 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
1605 0 : htmlEditRules->WillJoinNodes(aLeftNode, aRightNode);
1606 : }
1607 :
1608 0 : nsresult rv = NS_OK;
1609 : RefPtr<JoinNodeTransaction> transaction =
1610 0 : JoinNodeTransaction::MaybeCreate(*this, aLeftNode, aRightNode);
1611 0 : if (transaction) {
1612 0 : rv = DoTransaction(transaction);
1613 : }
1614 :
1615 : // XXX Some other transactions manage range updater by themselves.
1616 : // Why doesn't JoinNodeTransaction do it?
1617 0 : mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, *parent, offset,
1618 0 : (int32_t)oldLeftNodeLen);
1619 :
1620 0 : if (mRules && mRules->AsHTMLEditRules()) {
1621 0 : Selection* selection = GetSelection();
1622 0 : if (selection) {
1623 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
1624 0 : htmlEditRules->DidJoinNodes(*selection, aLeftNode, aRightNode);
1625 : } else {
1626 0 : NS_WARNING("Selection has gone");
1627 : }
1628 : }
1629 :
1630 0 : if (mInlineSpellChecker) {
1631 0 : RefPtr<mozInlineSpellChecker> spellChecker = mInlineSpellChecker;
1632 0 : spellChecker->DidJoinNodes(aLeftNode, aRightNode);
1633 : }
1634 :
1635 0 : if (mTextServicesDocument && NS_SUCCEEDED(rv)) {
1636 0 : RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
1637 0 : textServicesDocument->DidJoinNodes(aLeftNode, aRightNode);
1638 : }
1639 :
1640 0 : if (!mActionListeners.IsEmpty()) {
1641 0 : AutoActionListenerArray listeners(mActionListeners);
1642 0 : for (auto& listener : listeners) {
1643 0 : listener->DidJoinNodes(&aLeftNode, &aRightNode, parent, rv);
1644 : }
1645 : }
1646 :
1647 0 : return rv;
1648 : }
1649 :
1650 : NS_IMETHODIMP
1651 0 : EditorBase::DeleteNode(nsINode* aNode)
1652 : {
1653 0 : if (NS_WARN_IF(!aNode)) {
1654 : return NS_ERROR_INVALID_ARG;
1655 : }
1656 0 : return DeleteNodeWithTransaction(*aNode);
1657 : }
1658 :
1659 : nsresult
1660 0 : EditorBase::DeleteNodeWithTransaction(nsINode& aNode)
1661 : {
1662 : AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
1663 : *this, EditSubAction::eCreateNode,
1664 0 : nsIEditor::ePrevious);
1665 :
1666 0 : if (mRules && mRules->AsHTMLEditRules()) {
1667 0 : Selection* selection = GetSelection();
1668 0 : if (selection) {
1669 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
1670 0 : htmlEditRules->WillDeleteNode(*selection, aNode);
1671 : } else {
1672 0 : NS_WARNING("Selection has gone");
1673 : }
1674 : }
1675 :
1676 : // FYI: DeleteNodeTransaction grabs aNode while it's alive. So, it's safe
1677 : // to refer aNode even after calling DoTransaction().
1678 : RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
1679 0 : DeleteNodeTransaction::MaybeCreate(*this, aNode);
1680 0 : nsresult rv = deleteNodeTransaction ? DoTransaction(deleteNodeTransaction) :
1681 0 : NS_ERROR_FAILURE;
1682 :
1683 0 : if (mTextServicesDocument && NS_SUCCEEDED(rv)) {
1684 0 : RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
1685 0 : textServicesDocument->DidDeleteNode(&aNode);
1686 : }
1687 :
1688 0 : if (!mActionListeners.IsEmpty()) {
1689 0 : AutoActionListenerArray listeners(mActionListeners);
1690 0 : for (auto& listener : listeners) {
1691 0 : listener->DidDeleteNode(&aNode, rv);
1692 : }
1693 : }
1694 :
1695 0 : NS_ENSURE_SUCCESS(rv, rv);
1696 : return NS_OK;
1697 : }
1698 :
1699 : already_AddRefed<Element>
1700 0 : EditorBase::ReplaceContainerWithTransactionInternal(
1701 : Element& aOldContainer,
1702 : nsAtom& aTagName,
1703 : nsAtom& aAttribute,
1704 : const nsAString& aAttributeValue,
1705 : bool aCloneAllAttributes)
1706 : {
1707 0 : EditorDOMPoint atOldContainer(&aOldContainer);
1708 0 : if (NS_WARN_IF(!atOldContainer.IsSet())) {
1709 : return nullptr;
1710 : }
1711 :
1712 0 : RefPtr<Element> newContainer = CreateHTMLContent(&aTagName);
1713 0 : if (NS_WARN_IF(!newContainer)) {
1714 : return nullptr;
1715 : }
1716 :
1717 : // Set or clone attribute if needed.
1718 0 : if (aCloneAllAttributes) {
1719 0 : MOZ_ASSERT(&aAttribute == nsGkAtoms::_empty);
1720 0 : CloneAttributesWithTransaction(*newContainer, aOldContainer);
1721 0 : } else if (&aAttribute != nsGkAtoms::_empty) {
1722 : nsresult rv =
1723 0 : newContainer->SetAttr(kNameSpaceID_None, &aAttribute, aAttributeValue,
1724 0 : true);
1725 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1726 : return nullptr;
1727 : }
1728 : }
1729 :
1730 : // Notify our internal selection state listener.
1731 : // Note: An AutoSelectionRestorer object must be created before calling this
1732 : // to initialize mRangeUpdater.
1733 : AutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, &aOldContainer,
1734 0 : newContainer);
1735 : {
1736 0 : AutoTransactionsConserveSelection conserveSelection(this);
1737 : // Move all children from the old container to the new container.
1738 0 : while (aOldContainer.HasChildren()) {
1739 0 : nsCOMPtr<nsIContent> child = aOldContainer.GetFirstChild();
1740 0 : if (NS_WARN_IF(!child)) {
1741 0 : return nullptr;
1742 : }
1743 0 : nsresult rv = DeleteNodeWithTransaction(*child);
1744 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1745 : return nullptr;
1746 : }
1747 :
1748 0 : rv = InsertNodeWithTransaction(*child,
1749 0 : EditorRawDOMPoint(newContainer,
1750 0 : newContainer->Length()));
1751 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1752 : return nullptr;
1753 : }
1754 : }
1755 : }
1756 :
1757 : // Insert new container into tree.
1758 0 : NS_WARNING_ASSERTION(atOldContainer.IsSetAndValid(),
1759 : "The old container might be moved by mutation observer");
1760 0 : nsresult rv = InsertNodeWithTransaction(*newContainer, atOldContainer);
1761 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1762 : return nullptr;
1763 : }
1764 :
1765 : // Delete old container.
1766 0 : rv = DeleteNodeWithTransaction(aOldContainer);
1767 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1768 : return nullptr;
1769 : }
1770 :
1771 : return newContainer.forget();
1772 : }
1773 :
1774 : nsresult
1775 0 : EditorBase::RemoveContainerWithTransaction(Element& aElement)
1776 : {
1777 0 : EditorDOMPoint pointToInsertChildren(&aElement);
1778 0 : if (NS_WARN_IF(!pointToInsertChildren.IsSet())) {
1779 : return NS_ERROR_FAILURE;
1780 : }
1781 :
1782 : // Notify our internal selection state listener.
1783 : AutoRemoveContainerSelNotify selNotify(mRangeUpdater, &aElement,
1784 : pointToInsertChildren.GetContainer(),
1785 0 : pointToInsertChildren.Offset(),
1786 0 : aElement.GetChildCount());
1787 :
1788 : // Move all children from aNode to its parent.
1789 0 : while (aElement.HasChildren()) {
1790 0 : nsCOMPtr<nsIContent> child = aElement.GetLastChild();
1791 0 : if (NS_WARN_IF(!child)) {
1792 0 : return NS_ERROR_FAILURE;
1793 : }
1794 0 : nsresult rv = DeleteNodeWithTransaction(*child);
1795 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1796 : return rv;
1797 : }
1798 :
1799 : // Insert the last child before the previous last child. So, we need to
1800 : // use offset here because previous child might have been moved to
1801 : // container.
1802 0 : rv = InsertNodeWithTransaction(*child,
1803 0 : EditorRawDOMPoint(
1804 : pointToInsertChildren.GetContainer(),
1805 0 : pointToInsertChildren.Offset()));
1806 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1807 : return rv;
1808 : }
1809 : }
1810 :
1811 0 : nsresult rv = DeleteNodeWithTransaction(aElement);
1812 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1813 : return rv;
1814 : }
1815 0 : return NS_OK;
1816 : }
1817 :
1818 : already_AddRefed<Element>
1819 0 : EditorBase::InsertContainerWithTransactionInternal(
1820 : nsIContent& aContent,
1821 : nsAtom& aTagName,
1822 : nsAtom& aAttribute,
1823 : const nsAString& aAttributeValue)
1824 : {
1825 0 : EditorDOMPoint pointToInsertNewContainer(&aContent);
1826 0 : if (NS_WARN_IF(!pointToInsertNewContainer.IsSet())) {
1827 : return nullptr;
1828 : }
1829 : // aContent will be moved to the new container before inserting the new
1830 : // container. So, when we insert the container, the insertion point
1831 : // is before the next sibling of aContent.
1832 : // XXX If pointerToInsertNewContainer stores offset here, the offset and
1833 : // referring child node become mismatched. Although, currently this
1834 : // is not a problem since InsertNodeTransaction refers only child node.
1835 0 : DebugOnly<bool> advanced = pointToInsertNewContainer.AdvanceOffset();
1836 0 : NS_WARNING_ASSERTION(advanced,
1837 : "Failed to advance offset to after aContent");
1838 :
1839 : // Create new container.
1840 0 : RefPtr<Element> newContainer = CreateHTMLContent(&aTagName);
1841 0 : if (NS_WARN_IF(!newContainer)) {
1842 : return nullptr;
1843 : }
1844 :
1845 : // Set attribute if needed.
1846 0 : if (&aAttribute != nsGkAtoms::_empty) {
1847 : nsresult rv =
1848 0 : newContainer->SetAttr(kNameSpaceID_None, &aAttribute, aAttributeValue,
1849 0 : true);
1850 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1851 : return nullptr;
1852 : }
1853 : }
1854 :
1855 : // Notify our internal selection state listener
1856 0 : AutoInsertContainerSelNotify selNotify(mRangeUpdater);
1857 :
1858 : // Put aNode in the new container, first.
1859 0 : nsresult rv = DeleteNodeWithTransaction(aContent);
1860 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1861 : return nullptr;
1862 : }
1863 :
1864 : {
1865 0 : AutoTransactionsConserveSelection conserveSelection(this);
1866 : rv = InsertNodeWithTransaction(aContent,
1867 0 : EditorRawDOMPoint(newContainer, 0));
1868 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1869 0 : return nullptr;
1870 : }
1871 : }
1872 :
1873 : // Put the new container where aNode was.
1874 0 : rv = InsertNodeWithTransaction(*newContainer, pointToInsertNewContainer);
1875 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1876 : return nullptr;
1877 : }
1878 :
1879 : return newContainer.forget();
1880 : }
1881 :
1882 : template<typename PT, typename CT>
1883 : nsresult
1884 0 : EditorBase::MoveNodeWithTransaction(
1885 : nsIContent& aContent,
1886 : const EditorDOMPointBase<PT, CT>& aPointToInsert)
1887 : {
1888 0 : MOZ_ASSERT(aPointToInsert.IsSetAndValid());
1889 :
1890 0 : EditorDOMPoint oldPoint(&aContent);
1891 0 : if (NS_WARN_IF(!oldPoint.IsSet())) {
1892 : return NS_ERROR_FAILURE;
1893 : }
1894 :
1895 : // Don't do anything if it's already in right place.
1896 0 : if (aPointToInsert == oldPoint) {
1897 : return NS_OK;
1898 : }
1899 :
1900 : // Notify our internal selection state listener
1901 0 : EditorDOMPoint newPoint(aPointToInsert);
1902 0 : AutoMoveNodeSelNotify selNotify(mRangeUpdater, oldPoint, newPoint);
1903 :
1904 : // Hold a reference so aNode doesn't go away when we remove it (bug 772282)
1905 0 : nsresult rv = DeleteNodeWithTransaction(aContent);
1906 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1907 : return rv;
1908 : }
1909 :
1910 : // Mutation event listener could break insertion point. Let's check it.
1911 0 : EditorRawDOMPoint pointToInsert(selNotify.ComputeInsertionPoint());
1912 0 : if (NS_WARN_IF(!pointToInsert.IsSet())) {
1913 : return NS_ERROR_FAILURE;
1914 : }
1915 : // If some children have removed from the container, let's append to the
1916 : // container.
1917 : // XXX Perhaps, if mutation event listener inserts or removes some children
1918 : // but the child node referring with aPointToInsert is still available,
1919 : // we should insert aContent before it. However, we should keep
1920 : // traditional behavior for now.
1921 0 : if (NS_WARN_IF(!pointToInsert.IsSetAndValid())) {
1922 0 : pointToInsert.SetToEndOf(pointToInsert.GetContainer());
1923 : }
1924 0 : rv = InsertNodeWithTransaction(aContent, pointToInsert);
1925 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1926 : return rv;
1927 : }
1928 0 : return NS_OK;
1929 : }
1930 :
1931 : void
1932 0 : EditorBase::MoveAllChildren(nsINode& aContainer,
1933 : const EditorRawDOMPoint& aPointToInsert,
1934 : ErrorResult& aError)
1935 : {
1936 0 : if (!aContainer.HasChildren()) {
1937 : return;
1938 : }
1939 0 : nsIContent* firstChild = aContainer.GetFirstChild();
1940 0 : if (NS_WARN_IF(!firstChild)) {
1941 0 : aError.Throw(NS_ERROR_FAILURE);
1942 0 : return;
1943 : }
1944 0 : nsIContent* lastChild = aContainer.GetLastChild();
1945 0 : if (NS_WARN_IF(!lastChild)) {
1946 0 : aError.Throw(NS_ERROR_FAILURE);
1947 0 : return;
1948 : }
1949 0 : return MoveChildren(*firstChild, *lastChild, aPointToInsert, aError);
1950 : }
1951 :
1952 : void
1953 0 : EditorBase::MovePreviousSiblings(nsIContent& aChild,
1954 : const EditorRawDOMPoint& aPointToInsert,
1955 : ErrorResult& aError)
1956 : {
1957 0 : if (NS_WARN_IF(!aChild.GetParentNode())) {
1958 0 : aError.Throw(NS_ERROR_INVALID_ARG);
1959 0 : return;
1960 : }
1961 0 : nsIContent* firstChild = aChild.GetParentNode()->GetFirstChild();
1962 0 : if (NS_WARN_IF(!firstChild)) {
1963 0 : aError.Throw(NS_ERROR_FAILURE);
1964 0 : return;
1965 : }
1966 : nsIContent* lastChild =
1967 0 : &aChild == firstChild ? firstChild : aChild.GetPreviousSibling();
1968 0 : if (NS_WARN_IF(!lastChild)) {
1969 0 : aError.Throw(NS_ERROR_FAILURE);
1970 0 : return;
1971 : }
1972 0 : return MoveChildren(*firstChild, *lastChild, aPointToInsert, aError);
1973 : }
1974 :
1975 : void
1976 0 : EditorBase::MoveChildren(nsIContent& aFirstChild,
1977 : nsIContent& aLastChild,
1978 : const EditorRawDOMPoint& aPointToInsert,
1979 : ErrorResult& aError)
1980 : {
1981 0 : nsCOMPtr<nsINode> oldContainer = aFirstChild.GetParentNode();
1982 0 : if (NS_WARN_IF(oldContainer != aLastChild.GetParentNode()) ||
1983 0 : NS_WARN_IF(!aPointToInsert.IsSet()) ||
1984 0 : NS_WARN_IF(!aPointToInsert.CanContainerHaveChildren())) {
1985 0 : aError.Throw(NS_ERROR_INVALID_ARG);
1986 0 : return;
1987 : }
1988 :
1989 : // First, store all children which should be moved to the new container.
1990 0 : AutoTArray<nsCOMPtr<nsIContent>, 10> children;
1991 0 : for (nsIContent* child = &aFirstChild;
1992 0 : child;
1993 0 : child = child->GetNextSibling()) {
1994 0 : children.AppendElement(child);
1995 0 : if (child == &aLastChild) {
1996 : break;
1997 : }
1998 : }
1999 :
2000 0 : if (NS_WARN_IF(children.LastElement() != &aLastChild)) {
2001 0 : aError.Throw(NS_ERROR_INVALID_ARG);
2002 0 : return;
2003 : }
2004 :
2005 0 : nsCOMPtr<nsINode> newContainer = aPointToInsert.GetContainer();
2006 0 : nsCOMPtr<nsIContent> nextNode = aPointToInsert.GetChild();
2007 0 : for (size_t i = children.Length(); i > 0; --i) {
2008 0 : nsCOMPtr<nsIContent>& child = children[i - 1];
2009 0 : if (child->GetParentNode() != oldContainer) {
2010 : // If the child has been moved to different container, we shouldn't
2011 : // touch it.
2012 : continue;
2013 : }
2014 0 : oldContainer->RemoveChild(*child, aError);
2015 0 : if (NS_WARN_IF(aError.Failed())) {
2016 0 : return;
2017 : }
2018 0 : if (nextNode) {
2019 : // If we're not appending the children to the new container, we should
2020 : // check if referring next node of insertion point is still in the new
2021 : // container.
2022 0 : EditorRawDOMPoint pointToInsert(nextNode);
2023 0 : if (NS_WARN_IF(!pointToInsert.IsSet()) ||
2024 0 : NS_WARN_IF(pointToInsert.GetContainer() != newContainer)) {
2025 : // The next node of insertion point has been moved by mutation observer.
2026 : // Let's stop moving the remaining nodes.
2027 : // XXX Or should we move remaining children after the last moved child?
2028 0 : aError.Throw(NS_ERROR_FAILURE);
2029 0 : return;
2030 : }
2031 : }
2032 0 : newContainer->InsertBefore(*child, nextNode, aError);
2033 0 : if (NS_WARN_IF(aError.Failed())) {
2034 : return;
2035 : }
2036 : // If the child was inserted or appended properly, the following children
2037 : // should be inserted before it. Otherwise, keep using current position.
2038 0 : if (child->GetParentNode() == newContainer) {
2039 0 : nextNode = child;
2040 : }
2041 : }
2042 : }
2043 :
2044 : NS_IMETHODIMP
2045 0 : EditorBase::AddEditorObserver(nsIEditorObserver* aObserver)
2046 : {
2047 : // we don't keep ownership of the observers. They must
2048 : // remove themselves as observers before they are destroyed.
2049 :
2050 0 : NS_ENSURE_TRUE(aObserver, NS_ERROR_NULL_POINTER);
2051 :
2052 : // Make sure the listener isn't already on the list
2053 0 : if (!mEditorObservers.Contains(aObserver)) {
2054 0 : mEditorObservers.AppendElement(*aObserver);
2055 0 : NS_WARNING_ASSERTION(mEditorObservers.Length() != 1,
2056 : "nsIEditorObserver installed, this editor becomes slower");
2057 : }
2058 :
2059 : return NS_OK;
2060 : }
2061 :
2062 : NS_IMETHODIMP
2063 0 : EditorBase::RemoveEditorObserver(nsIEditorObserver* aObserver)
2064 : {
2065 0 : NS_ENSURE_TRUE(aObserver, NS_ERROR_FAILURE);
2066 :
2067 0 : NS_WARNING_ASSERTION(mEditorObservers.Length() != 1,
2068 : "All nsIEditorObservers have been removed, this editor becomes faster");
2069 0 : mEditorObservers.RemoveElement(aObserver);
2070 :
2071 0 : return NS_OK;
2072 : }
2073 :
2074 : NS_IMETHODIMP
2075 8 : EditorBase::NotifySelectionChanged(nsIDocument* aDocument,
2076 : Selection* aSelection,
2077 : int16_t aReason)
2078 : {
2079 0 : if (NS_WARN_IF(!aDocument) || NS_WARN_IF(!aSelection)) {
2080 : return NS_ERROR_INVALID_ARG;
2081 : }
2082 :
2083 0 : if (mTextInputListener) {
2084 12 : RefPtr<TextInputListener> textInputListener = mTextInputListener;
2085 6 : textInputListener->OnSelectionChange(*aSelection, aReason);
2086 : }
2087 :
2088 0 : if (mIMEContentObserver) {
2089 0 : RefPtr<IMEContentObserver> observer = mIMEContentObserver;
2090 0 : observer->OnSelectionChange(*aSelection);
2091 : }
2092 :
2093 : return NS_OK;
2094 : }
2095 :
2096 0 : class EditorInputEventDispatcher final : public Runnable
2097 : {
2098 : public:
2099 0 : EditorInputEventDispatcher(EditorBase* aEditorBase,
2100 : nsIContent* aTarget,
2101 : bool aIsComposing)
2102 0 : : Runnable("EditorInputEventDispatcher")
2103 : , mEditorBase(aEditorBase)
2104 : , mTarget(aTarget)
2105 0 : , mIsComposing(aIsComposing)
2106 : {
2107 0 : }
2108 :
2109 0 : NS_IMETHOD Run() override
2110 : {
2111 : // Note that we don't need to check mDispatchInputEvent here. We need
2112 : // to check it only when the editor requests to dispatch the input event.
2113 :
2114 0 : if (!mTarget->IsInComposedDoc()) {
2115 : return NS_OK;
2116 : }
2117 :
2118 0 : nsCOMPtr<nsIPresShell> ps = mEditorBase->GetPresShell();
2119 0 : if (!ps) {
2120 : return NS_OK;
2121 : }
2122 :
2123 0 : nsCOMPtr<nsIWidget> widget = mEditorBase->GetWidget();
2124 0 : if (!widget) {
2125 : return NS_OK;
2126 : }
2127 :
2128 : // Even if the change is caused by untrusted event, we need to dispatch
2129 : // trusted input event since it's a fact.
2130 0 : InternalEditorInputEvent inputEvent(true, eEditorInput, widget);
2131 0 : inputEvent.mTime = static_cast<uint64_t>(PR_Now() / 1000);
2132 0 : inputEvent.mIsComposing = mIsComposing;
2133 0 : nsEventStatus status = nsEventStatus_eIgnore;
2134 : nsresult rv =
2135 0 : ps->HandleEventWithTarget(&inputEvent, nullptr, mTarget, &status);
2136 0 : NS_ENSURE_SUCCESS(rv, NS_OK); // print the warning if error
2137 : return NS_OK;
2138 : }
2139 :
2140 : private:
2141 : RefPtr<EditorBase> mEditorBase;
2142 : nsCOMPtr<nsIContent> mTarget;
2143 : bool mIsComposing;
2144 : };
2145 :
2146 : void
2147 0 : EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification)
2148 : {
2149 0 : switch (aNotification) {
2150 : case eNotifyEditorObserversOfEnd:
2151 0 : mIsInEditSubAction = false;
2152 :
2153 0 : if (mTextInputListener) {
2154 0 : RefPtr<TextInputListener> listener = mTextInputListener;
2155 0 : listener->OnEditActionHandled();
2156 : }
2157 :
2158 0 : if (mIMEContentObserver) {
2159 0 : RefPtr<IMEContentObserver> observer = mIMEContentObserver;
2160 0 : observer->OnEditActionHandled();
2161 : }
2162 :
2163 0 : if (!mEditorObservers.IsEmpty()) {
2164 : // Copy the observers since EditAction()s can modify mEditorObservers.
2165 0 : AutoEditorObserverArray observers(mEditorObservers);
2166 0 : for (auto& observer : observers) {
2167 0 : observer->EditAction();
2168 : }
2169 : }
2170 :
2171 0 : if (!mDispatchInputEvent) {
2172 : return;
2173 : }
2174 :
2175 0 : FireInputEvent();
2176 0 : break;
2177 : case eNotifyEditorObserversOfBefore:
2178 0 : if (NS_WARN_IF(mIsInEditSubAction)) {
2179 : break;
2180 : }
2181 :
2182 0 : mIsInEditSubAction = true;
2183 :
2184 0 : if (mIMEContentObserver) {
2185 0 : RefPtr<IMEContentObserver> observer = mIMEContentObserver;
2186 0 : observer->BeforeEditAction();
2187 : }
2188 : break;
2189 : case eNotifyEditorObserversOfCancel:
2190 0 : mIsInEditSubAction = false;
2191 :
2192 0 : if (mIMEContentObserver) {
2193 0 : RefPtr<IMEContentObserver> observer = mIMEContentObserver;
2194 0 : observer->CancelEditAction();
2195 : }
2196 : break;
2197 : default:
2198 0 : MOZ_CRASH("Handle all notifications here");
2199 : break;
2200 : }
2201 : }
2202 :
2203 : void
2204 0 : EditorBase::FireInputEvent()
2205 : {
2206 : // We don't need to dispatch multiple input events if there is a pending
2207 : // input event. However, it may have different event target. If we resolved
2208 : // this issue, we need to manage the pending events in an array. But it's
2209 : // overwork. We don't need to do it for the very rare case.
2210 :
2211 0 : nsCOMPtr<nsIContent> target = GetInputEventTargetContent();
2212 0 : NS_ENSURE_TRUE_VOID(target);
2213 :
2214 : // NOTE: Don't refer IsIMEComposing() because it returns false even before
2215 : // compositionend. However, DOM Level 3 Events defines it should be
2216 : // true after compositionstart and before compositionend.
2217 : nsContentUtils::AddScriptRunner(
2218 0 : new EditorInputEventDispatcher(this, target, !!GetComposition()));
2219 : }
2220 :
2221 : NS_IMETHODIMP
2222 0 : EditorBase::AddEditActionListener(nsIEditActionListener* aListener)
2223 : {
2224 0 : NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
2225 :
2226 : // If given edit action listener is text services document for the inline
2227 : // spell checker, store it as reference of concrete class for performance
2228 : // reason.
2229 0 : if (mInlineSpellChecker) {
2230 : EditorSpellCheck* editorSpellCheck =
2231 0 : mInlineSpellChecker->GetEditorSpellCheck();
2232 0 : if (editorSpellCheck) {
2233 0 : mozSpellChecker* spellChecker = editorSpellCheck->GetSpellChecker();
2234 0 : if (spellChecker) {
2235 : TextServicesDocument* textServicesDocument =
2236 0 : spellChecker->GetTextServicesDocument();
2237 0 : if (static_cast<nsIEditActionListener*>(textServicesDocument) ==
2238 : aListener) {
2239 0 : mTextServicesDocument = textServicesDocument;
2240 0 : return NS_OK;
2241 : }
2242 : }
2243 : }
2244 : }
2245 :
2246 : // Make sure the listener isn't already on the list
2247 0 : if (!mActionListeners.Contains(aListener)) {
2248 0 : mActionListeners.AppendElement(*aListener);
2249 0 : NS_WARNING_ASSERTION(mActionListeners.Length() != 1,
2250 : "nsIEditActionListener installed, this editor becomes slower");
2251 : }
2252 :
2253 : return NS_OK;
2254 : }
2255 :
2256 : NS_IMETHODIMP
2257 0 : EditorBase::RemoveEditActionListener(nsIEditActionListener* aListener)
2258 : {
2259 0 : NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
2260 :
2261 0 : if (static_cast<nsIEditActionListener*>(mTextServicesDocument) == aListener) {
2262 0 : mTextServicesDocument = nullptr;
2263 0 : return NS_OK;
2264 : }
2265 :
2266 0 : NS_WARNING_ASSERTION(mActionListeners.Length() != 1,
2267 : "All nsIEditActionListeners have been removed, this editor becomes faster");
2268 0 : mActionListeners.RemoveElement(aListener);
2269 :
2270 0 : return NS_OK;
2271 : }
2272 :
2273 : NS_IMETHODIMP
2274 0 : EditorBase::AddDocumentStateListener(nsIDocumentStateListener* aListener)
2275 : {
2276 0 : NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
2277 :
2278 0 : if (!mDocStateListeners.Contains(aListener)) {
2279 0 : mDocStateListeners.AppendElement(*aListener);
2280 : }
2281 :
2282 : return NS_OK;
2283 : }
2284 :
2285 : NS_IMETHODIMP
2286 0 : EditorBase::RemoveDocumentStateListener(nsIDocumentStateListener* aListener)
2287 : {
2288 0 : NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
2289 :
2290 0 : mDocStateListeners.RemoveElement(aListener);
2291 :
2292 0 : return NS_OK;
2293 : }
2294 :
2295 : NS_IMETHODIMP
2296 0 : EditorBase::OutputToString(const nsAString& aFormatType,
2297 : uint32_t aFlags,
2298 : nsAString& aOutputString)
2299 : {
2300 : // these should be implemented by derived classes.
2301 0 : return NS_ERROR_NOT_IMPLEMENTED;
2302 : }
2303 :
2304 : NS_IMETHODIMP
2305 0 : EditorBase::DumpContentTree()
2306 : {
2307 : #ifdef DEBUG
2308 0 : if (mRootElement) {
2309 0 : mRootElement->List(stdout);
2310 : }
2311 : #endif
2312 0 : return NS_OK;
2313 : }
2314 :
2315 : NS_IMETHODIMP
2316 0 : EditorBase::DebugDumpContent()
2317 : {
2318 : #ifdef DEBUG
2319 0 : nsCOMPtr<nsIDocument> document = GetDocument();
2320 0 : if (NS_WARN_IF(!document)) {
2321 : return NS_ERROR_NOT_INITIALIZED;
2322 : }
2323 0 : Element* body = document->GetBody();
2324 0 : if (body) {
2325 0 : body->List();
2326 : }
2327 : #endif
2328 : return NS_OK;
2329 : }
2330 :
2331 : NS_IMETHODIMP
2332 0 : EditorBase::DebugUnitTests(int32_t* outNumTests,
2333 : int32_t* outNumTestsFailed)
2334 : {
2335 : #ifdef DEBUG
2336 0 : NS_NOTREACHED("This should never get called. Overridden by subclasses");
2337 : #endif
2338 0 : return NS_OK;
2339 : }
2340 :
2341 : bool
2342 0 : EditorBase::ArePreservingSelection()
2343 : {
2344 0 : return !(mSavedSel.IsEmpty());
2345 : }
2346 :
2347 : void
2348 0 : EditorBase::PreserveSelectionAcrossActions(Selection* aSel)
2349 : {
2350 0 : mSavedSel.SaveSelection(aSel);
2351 0 : mRangeUpdater.RegisterSelectionState(mSavedSel);
2352 0 : }
2353 :
2354 : nsresult
2355 0 : EditorBase::RestorePreservedSelection(Selection* aSel)
2356 : {
2357 0 : if (mSavedSel.IsEmpty()) {
2358 : return NS_ERROR_FAILURE;
2359 : }
2360 0 : mSavedSel.RestoreSelection(aSel);
2361 0 : StopPreservingSelection();
2362 0 : return NS_OK;
2363 : }
2364 :
2365 : void
2366 0 : EditorBase::StopPreservingSelection()
2367 : {
2368 0 : mRangeUpdater.DropSelectionState(mSavedSel);
2369 0 : mSavedSel.MakeEmpty();
2370 0 : }
2371 :
2372 : NS_IMETHODIMP
2373 0 : EditorBase::ForceCompositionEnd()
2374 : {
2375 0 : return CommitComposition();
2376 : }
2377 :
2378 : nsresult
2379 0 : EditorBase::CommitComposition()
2380 : {
2381 0 : nsPresContext* pc = GetPresContext();
2382 0 : if (!pc) {
2383 : return NS_ERROR_NOT_AVAILABLE;
2384 : }
2385 :
2386 0 : return mComposition ?
2387 0 : IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc) : NS_OK;
2388 : }
2389 :
2390 : nsresult
2391 2 : EditorBase::GetPreferredIMEState(IMEState* aState)
2392 : {
2393 0 : NS_ENSURE_ARG_POINTER(aState);
2394 2 : aState->mEnabled = IMEState::ENABLED;
2395 0 : aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
2396 :
2397 6 : if (IsReadonly() || IsDisabled()) {
2398 0 : aState->mEnabled = IMEState::DISABLED;
2399 0 : return NS_OK;
2400 : }
2401 :
2402 4 : nsCOMPtr<nsIContent> content = GetRoot();
2403 0 : NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
2404 :
2405 0 : nsIFrame* frame = content->GetPrimaryFrame();
2406 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
2407 :
2408 2 : switch (frame->StyleUIReset()->mIMEMode) {
2409 : case NS_STYLE_IME_MODE_AUTO:
2410 4 : if (IsPasswordEditor())
2411 0 : aState->mEnabled = IMEState::PASSWORD;
2412 : break;
2413 : case NS_STYLE_IME_MODE_DISABLED:
2414 : // we should use password state for |ime-mode: disabled;|.
2415 0 : aState->mEnabled = IMEState::PASSWORD;
2416 0 : break;
2417 : case NS_STYLE_IME_MODE_ACTIVE:
2418 0 : aState->mOpen = IMEState::OPEN;
2419 0 : break;
2420 : case NS_STYLE_IME_MODE_INACTIVE:
2421 0 : aState->mOpen = IMEState::CLOSED;
2422 0 : break;
2423 : }
2424 :
2425 : return NS_OK;
2426 : }
2427 :
2428 : NS_IMETHODIMP
2429 0 : EditorBase::GetComposing(bool* aResult)
2430 : {
2431 0 : NS_ENSURE_ARG_POINTER(aResult);
2432 0 : *aResult = IsIMEComposing();
2433 0 : return NS_OK;
2434 : }
2435 :
2436 : NS_IMETHODIMP
2437 0 : EditorBase::GetRootElement(Element** aRootElement)
2438 : {
2439 0 : NS_ENSURE_ARG_POINTER(aRootElement);
2440 0 : NS_ENSURE_TRUE(mRootElement, NS_ERROR_NOT_AVAILABLE);
2441 0 : RefPtr<Element> rootElement = mRootElement;
2442 0 : rootElement.forget(aRootElement);
2443 : return NS_OK;
2444 : }
2445 :
2446 : void
2447 1 : EditorBase::OnStartToHandleTopLevelEditSubAction(
2448 : EditSubAction aEditSubAction,
2449 : nsIEditor::EDirection aDirection)
2450 : {
2451 0 : mTopLevelEditSubAction = aEditSubAction;
2452 1 : mDirection = aDirection;
2453 1 : }
2454 :
2455 : void
2456 0 : EditorBase::OnEndHandlingTopLevelEditSubAction()
2457 : {
2458 1 : mTopLevelEditSubAction = EditSubAction::eNone;
2459 0 : mDirection = eNone;
2460 1 : }
2461 :
2462 : NS_IMETHODIMP
2463 0 : EditorBase::CloneAttribute(const nsAString& aAttribute,
2464 : Element* aDestElement,
2465 : Element* aSourceElement)
2466 : {
2467 0 : NS_ENSURE_TRUE(aDestElement && aSourceElement, NS_ERROR_NULL_POINTER);
2468 0 : if (NS_WARN_IF(aAttribute.IsEmpty())) {
2469 : return NS_ERROR_FAILURE;
2470 : }
2471 :
2472 0 : RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
2473 0 : return CloneAttributeWithTransaction(*attribute, *aDestElement, *aSourceElement);
2474 : }
2475 :
2476 : nsresult
2477 0 : EditorBase::CloneAttributeWithTransaction(nsAtom& aAttribute,
2478 : Element& aDestElement,
2479 : Element& aSourceElement)
2480 : {
2481 0 : nsAutoString attrValue;
2482 0 : if (aSourceElement.GetAttr(kNameSpaceID_None, &aAttribute, attrValue)) {
2483 0 : return SetAttributeWithTransaction(aDestElement, aAttribute, attrValue);
2484 : }
2485 0 : return RemoveAttributeWithTransaction(aDestElement, aAttribute);
2486 : }
2487 :
2488 : /**
2489 : * @param aDest Must be a DOM element.
2490 : * @param aSource Must be a DOM element.
2491 : */
2492 : NS_IMETHODIMP
2493 0 : EditorBase::CloneAttributes(Element* aDestElement,
2494 : Element* aSourceElement)
2495 : {
2496 0 : if (NS_WARN_IF(!aDestElement) || NS_WARN_IF(!aSourceElement)) {
2497 : return NS_ERROR_INVALID_ARG;
2498 : }
2499 :
2500 0 : CloneAttributesWithTransaction(*aDestElement, *aSourceElement);
2501 :
2502 0 : return NS_OK;
2503 : }
2504 :
2505 : void
2506 0 : EditorBase::CloneAttributesWithTransaction(Element& aDestElement,
2507 : Element& aSourceElement)
2508 : {
2509 0 : AutoPlaceholderBatch beginBatching(this);
2510 :
2511 : // Use transaction system for undo only if destination is already in the
2512 : // document
2513 0 : Element* rootElement = GetRoot();
2514 0 : if (NS_WARN_IF(!rootElement)) {
2515 0 : return;
2516 : }
2517 :
2518 0 : OwningNonNull<Element> destElement(aDestElement);
2519 0 : OwningNonNull<Element> sourceElement(aSourceElement);
2520 0 : bool isDestElementInBody = rootElement->Contains(destElement);
2521 :
2522 : // Clear existing attributes
2523 0 : RefPtr<nsDOMAttributeMap> destAttributes = destElement->Attributes();
2524 0 : while (RefPtr<Attr> attr = destAttributes->Item(0)) {
2525 0 : if (isDestElementInBody) {
2526 : RemoveAttributeWithTransaction(destElement,
2527 0 : *attr->NodeInfo()->NameAtom());
2528 : } else {
2529 0 : destElement->UnsetAttr(kNameSpaceID_None, attr->NodeInfo()->NameAtom(),
2530 0 : true);
2531 : }
2532 : }
2533 :
2534 : // Set just the attributes that the source element has
2535 0 : RefPtr<nsDOMAttributeMap> sourceAttributes = sourceElement->Attributes();
2536 0 : uint32_t sourceCount = sourceAttributes->Length();
2537 0 : for (uint32_t i = 0; i < sourceCount; i++) {
2538 0 : RefPtr<Attr> attr = sourceAttributes->Item(i);
2539 0 : nsAutoString value;
2540 0 : attr->GetValue(value);
2541 0 : if (isDestElementInBody) {
2542 0 : SetAttributeOrEquivalent(destElement, attr->NodeInfo()->NameAtom(), value,
2543 0 : false);
2544 : } else {
2545 : // The element is not inserted in the document yet, we don't want to put
2546 : // a transaction on the UndoStack
2547 0 : SetAttributeOrEquivalent(destElement, attr->NodeInfo()->NameAtom(), value,
2548 0 : true);
2549 : }
2550 : }
2551 : }
2552 :
2553 : nsresult
2554 0 : EditorBase::ScrollSelectionIntoView(bool aScrollToAnchor)
2555 : {
2556 0 : nsISelectionController* selectionController = GetSelectionController();
2557 0 : if (!selectionController) {
2558 : return NS_OK;
2559 : }
2560 :
2561 0 : int16_t region = nsISelectionController::SELECTION_FOCUS_REGION;
2562 0 : if (aScrollToAnchor) {
2563 0 : region = nsISelectionController::SELECTION_ANCHOR_REGION;
2564 : }
2565 0 : selectionController->ScrollSelectionIntoView(
2566 : nsISelectionController::SELECTION_NORMAL,
2567 : region,
2568 0 : nsISelectionController::SCROLL_OVERFLOW_HIDDEN);
2569 0 : return NS_OK;
2570 : }
2571 :
2572 : EditorRawDOMPoint
2573 0 : EditorBase::FindBetterInsertionPoint(const EditorRawDOMPoint& aPoint)
2574 : {
2575 0 : if (NS_WARN_IF(!aPoint.IsSet())) {
2576 0 : return aPoint;
2577 : }
2578 :
2579 0 : MOZ_ASSERT(aPoint.IsSetAndValid());
2580 :
2581 0 : if (aPoint.IsInTextNode()) {
2582 : // There is no "better" insertion point.
2583 0 : return aPoint;
2584 : }
2585 :
2586 0 : if (!IsPlaintextEditor()) {
2587 : // We cannot find "better" insertion point in HTML editor.
2588 : // WARNING: When you add some code to find better node in HTML editor,
2589 : // you need to call this before calling InsertTextWithTransaction()
2590 : // in HTMLEditRules.
2591 0 : return aPoint;
2592 : }
2593 :
2594 0 : nsCOMPtr<nsINode> root = GetRoot();
2595 0 : if (aPoint.GetContainer() == root) {
2596 : // In some cases, aNode is the anonymous DIV, and offset is 0. To avoid
2597 : // injecting unneeded text nodes, we first look to see if we have one
2598 : // available. In that case, we'll just adjust node and offset accordingly.
2599 0 : if (aPoint.IsStartOfContainer() &&
2600 0 : aPoint.GetContainer()->HasChildren() &&
2601 0 : aPoint.GetContainer()->GetFirstChild()->IsText()) {
2602 0 : return EditorRawDOMPoint(aPoint.GetContainer()->GetFirstChild(), 0);
2603 : }
2604 :
2605 : // In some other cases, aNode is the anonymous DIV, and offset points to the
2606 : // terminating mozBR. In that case, we'll adjust aInOutNode and
2607 : // aInOutOffset to the preceding text node, if any.
2608 0 : if (!aPoint.IsStartOfContainer()) {
2609 0 : if (AsHTMLEditor()) {
2610 : // Fall back to a slow path that uses GetChildAt_Deprecated() for Thunderbird's
2611 : // plaintext editor.
2612 0 : nsIContent* child = aPoint.GetPreviousSiblingOfChild();
2613 0 : if (child && child->IsText()) {
2614 0 : if (NS_WARN_IF(child->Length() > INT32_MAX)) {
2615 0 : return aPoint;
2616 : }
2617 0 : return EditorRawDOMPoint(child, child->Length());
2618 : }
2619 : } else {
2620 : // If we're in a real plaintext editor, use a fast path that avoids
2621 : // calling GetChildAt_Deprecated() which may perform a linear search.
2622 0 : nsIContent* child = aPoint.GetContainer()->GetLastChild();
2623 0 : while (child) {
2624 0 : if (child->IsText()) {
2625 0 : if (NS_WARN_IF(child->Length() > INT32_MAX)) {
2626 0 : return aPoint;
2627 : }
2628 0 : return EditorRawDOMPoint(child, child->Length());
2629 : }
2630 0 : child = child->GetPreviousSibling();
2631 : }
2632 : }
2633 : }
2634 : }
2635 :
2636 : // Sometimes, aNode is the mozBR element itself. In that case, we'll adjust
2637 : // the insertion point to the previous text node, if one exists, or to the
2638 : // parent anonymous DIV.
2639 0 : if (TextEditUtils::IsMozBR(aPoint.GetContainer()) &&
2640 0 : aPoint.IsStartOfContainer()) {
2641 0 : nsIContent* previousSibling = aPoint.GetContainer()->GetPreviousSibling();
2642 0 : if (previousSibling && previousSibling->IsText()) {
2643 0 : if (NS_WARN_IF(previousSibling->Length() > INT32_MAX)) {
2644 0 : return aPoint;
2645 : }
2646 0 : return EditorRawDOMPoint(previousSibling, previousSibling->Length());
2647 : }
2648 :
2649 0 : nsINode* parentOfContainer = aPoint.GetContainer()->GetParentNode();
2650 0 : if (parentOfContainer && parentOfContainer == root) {
2651 : return EditorRawDOMPoint(parentOfContainer,
2652 0 : aPoint.GetContainerAsContent(), 0);
2653 : }
2654 : }
2655 :
2656 0 : return aPoint;
2657 : }
2658 :
2659 : nsresult
2660 0 : EditorBase::InsertTextWithTransaction(
2661 : nsIDocument& aDocument,
2662 : const nsAString& aStringToInsert,
2663 : const EditorRawDOMPoint& aPointToInsert,
2664 : EditorRawDOMPoint* aPointAfterInsertedString)
2665 : {
2666 : // NOTE: caller *must* have already used AutoTransactionsConserveSelection
2667 : // stack-based class to turn off txn selection updating. Caller also turned
2668 : // on rules sniffing if desired.
2669 :
2670 0 : if (NS_WARN_IF(!aPointToInsert.IsSet())) {
2671 : return NS_ERROR_INVALID_ARG;
2672 : }
2673 :
2674 0 : MOZ_ASSERT(aPointToInsert.IsSetAndValid());
2675 :
2676 0 : if (!ShouldHandleIMEComposition() && aStringToInsert.IsEmpty()) {
2677 0 : if (aPointAfterInsertedString) {
2678 0 : *aPointAfterInsertedString = aPointToInsert;
2679 : }
2680 : return NS_OK;
2681 : }
2682 :
2683 : // This method doesn't support over INT32_MAX length text since aInOutOffset
2684 : // is int32_t*.
2685 0 : CheckedInt<int32_t> lengthToInsert(aStringToInsert.Length());
2686 0 : if (NS_WARN_IF(!lengthToInsert.isValid())) {
2687 : return NS_ERROR_INVALID_ARG;
2688 : }
2689 :
2690 : // In some cases, the node may be the anonymous div elemnt or a mozBR
2691 : // element. Let's try to look for better insertion point in the nearest
2692 : // text node if there is.
2693 0 : EditorRawDOMPoint pointToInsert = FindBetterInsertionPoint(aPointToInsert);
2694 :
2695 : // If a neighboring text node already exists, use that
2696 0 : if (!pointToInsert.IsInTextNode()) {
2697 0 : nsIContent* child = nullptr;
2698 0 : if (!pointToInsert.IsStartOfContainer() &&
2699 0 : (child = pointToInsert.GetPreviousSiblingOfChild()) &&
2700 0 : child->IsText()) {
2701 0 : pointToInsert.Set(child, child->Length());
2702 0 : } else if (!pointToInsert.IsEndOfContainer() &&
2703 0 : (child = pointToInsert.GetChild()) &&
2704 0 : child->IsText()) {
2705 0 : pointToInsert.Set(child, 0);
2706 : }
2707 : }
2708 :
2709 0 : if (ShouldHandleIMEComposition()) {
2710 0 : CheckedInt<int32_t> newOffset;
2711 0 : if (!pointToInsert.IsInTextNode()) {
2712 : // create a text node
2713 : RefPtr<nsTextNode> newNode =
2714 0 : EditorBase::CreateTextNode(aDocument, EmptyString());
2715 : // then we insert it into the dom tree
2716 0 : nsresult rv = InsertNodeWithTransaction(*newNode, pointToInsert);
2717 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2718 0 : return rv;
2719 : }
2720 0 : pointToInsert.Set(newNode, 0);
2721 0 : newOffset = lengthToInsert;
2722 : } else {
2723 0 : newOffset = lengthToInsert + pointToInsert.Offset();
2724 0 : NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
2725 : }
2726 : nsresult rv =
2727 0 : InsertTextIntoTextNodeWithTransaction(aStringToInsert,
2728 0 : *pointToInsert.GetContainerAsText(),
2729 0 : pointToInsert.Offset());
2730 0 : NS_ENSURE_SUCCESS(rv, rv);
2731 0 : if (aPointAfterInsertedString) {
2732 0 : aPointAfterInsertedString->Set(pointToInsert.GetContainer(),
2733 0 : newOffset.value());
2734 : }
2735 : return NS_OK;
2736 : }
2737 :
2738 0 : if (pointToInsert.IsInTextNode()) {
2739 0 : CheckedInt<int32_t> newOffset = lengthToInsert + pointToInsert.Offset();
2740 0 : NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
2741 : // we are inserting text into an existing text node.
2742 : nsresult rv =
2743 0 : InsertTextIntoTextNodeWithTransaction(aStringToInsert,
2744 0 : *pointToInsert.GetContainerAsText(),
2745 0 : pointToInsert.Offset());
2746 0 : NS_ENSURE_SUCCESS(rv, rv);
2747 0 : if (aPointAfterInsertedString) {
2748 0 : aPointAfterInsertedString->Set(pointToInsert.GetContainer(),
2749 0 : newOffset.value());
2750 : }
2751 : return NS_OK;
2752 : }
2753 :
2754 : // we are inserting text into a non-text node. first we have to create a
2755 : // textnode (this also populates it with the text)
2756 : RefPtr<nsTextNode> newNode =
2757 0 : EditorBase::CreateTextNode(aDocument, aStringToInsert);
2758 : // then we insert it into the dom tree
2759 0 : nsresult rv = InsertNodeWithTransaction(*newNode, pointToInsert);
2760 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2761 : return rv;
2762 : }
2763 0 : if (aPointAfterInsertedString) {
2764 0 : aPointAfterInsertedString->Set(newNode, lengthToInsert.value());
2765 : }
2766 : return NS_OK;
2767 : }
2768 :
2769 : nsresult
2770 0 : EditorBase::InsertTextIntoTextNodeWithTransaction(
2771 : const nsAString& aStringToInsert,
2772 : Text& aTextNode,
2773 : int32_t aOffset,
2774 : bool aSuppressIME)
2775 : {
2776 0 : RefPtr<EditTransactionBase> transaction;
2777 0 : bool isIMETransaction = false;
2778 0 : RefPtr<Text> insertedTextNode = &aTextNode;
2779 0 : int32_t insertedOffset = aOffset;
2780 : // aSuppressIME is used when editor must insert text, yet this text is not
2781 : // part of the current IME operation. Example: adjusting whitespace around an
2782 : // IME insertion.
2783 0 : if (ShouldHandleIMEComposition() && !aSuppressIME) {
2784 : transaction =
2785 0 : CompositionTransaction::Create(*this, aStringToInsert,
2786 0 : aTextNode, aOffset);
2787 0 : isIMETransaction = true;
2788 : // All characters of the composition string will be replaced with
2789 : // aStringToInsert. So, we need to emulate to remove the composition
2790 : // string.
2791 : // FYI: The text node information in mComposition has been updated by
2792 : // CompositionTransaction::Create().
2793 0 : insertedTextNode = mComposition->GetContainerTextNode();
2794 0 : insertedOffset = mComposition->XPOffsetInTextNode();
2795 : } else {
2796 : transaction =
2797 0 : InsertTextTransaction::Create(*this, aStringToInsert, aTextNode, aOffset);
2798 : }
2799 :
2800 : // XXX We may not need these view batches anymore. This is handled at a
2801 : // higher level now I believe.
2802 0 : BeginUpdateViewBatch();
2803 0 : nsresult rv = DoTransaction(transaction);
2804 0 : EndUpdateViewBatch();
2805 :
2806 0 : if (mRules && mRules->AsHTMLEditRules() && insertedTextNode) {
2807 0 : Selection* selection = GetSelection();
2808 0 : if (selection) {
2809 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
2810 0 : htmlEditRules->DidInsertText(*selection, *insertedTextNode,
2811 0 : insertedOffset, aStringToInsert);
2812 : } else {
2813 0 : NS_WARNING("Selection has gone");
2814 : }
2815 : }
2816 :
2817 : // let listeners know what happened
2818 0 : if (!mActionListeners.IsEmpty()) {
2819 0 : AutoActionListenerArray listeners(mActionListeners);
2820 0 : for (auto& listener : listeners) {
2821 0 : listener->DidInsertText(insertedTextNode, insertedOffset,
2822 0 : aStringToInsert, rv);
2823 : }
2824 : }
2825 :
2826 : // Added some cruft here for bug 43366. Layout was crashing because we left
2827 : // an empty text node lying around in the document. So I delete empty text
2828 : // nodes caused by IME. I have to mark the IME transaction as "fixed", which
2829 : // means that furure IME txns won't merge with it. This is because we don't
2830 : // want future IME txns trying to put their text into a node that is no
2831 : // longer in the document. This does not break undo/redo, because all these
2832 : // txns are wrapped in a parent PlaceHolder txn, and placeholder txns are
2833 : // already savvy to having multiple ime txns inside them.
2834 :
2835 : // Delete empty IME text node if there is one
2836 0 : if (isIMETransaction && mComposition) {
2837 0 : Text* textNode = mComposition->GetContainerTextNode();
2838 0 : if (textNode && !textNode->Length()) {
2839 0 : DeleteNodeWithTransaction(*textNode);
2840 0 : mComposition->OnTextNodeRemoved();
2841 0 : static_cast<CompositionTransaction*>(transaction.get())->MarkFixed();
2842 : }
2843 : }
2844 :
2845 0 : return rv;
2846 : }
2847 :
2848 : nsresult
2849 0 : EditorBase::SelectEntireDocument(Selection* aSelection)
2850 : {
2851 0 : if (!aSelection) {
2852 : return NS_ERROR_NULL_POINTER;
2853 : }
2854 :
2855 0 : Element* rootElement = GetRoot();
2856 0 : if (!rootElement) {
2857 : return NS_ERROR_NOT_INITIALIZED;
2858 : }
2859 :
2860 0 : ErrorResult errorResult;
2861 0 : aSelection->SelectAllChildren(*rootElement, errorResult);
2862 0 : return errorResult.StealNSResult();
2863 : }
2864 :
2865 : nsINode*
2866 0 : EditorBase::GetFirstEditableNode(nsINode* aRoot)
2867 : {
2868 0 : MOZ_ASSERT(aRoot);
2869 :
2870 0 : nsIContent* node = GetLeftmostChild(aRoot);
2871 0 : if (node && !IsEditable(node)) {
2872 0 : node = GetNextEditableNode(*node);
2873 : }
2874 :
2875 0 : return (node != aRoot) ? node : nullptr;
2876 : }
2877 :
2878 : nsresult
2879 4 : EditorBase::NotifyDocumentListeners(
2880 : TDocumentListenerNotification aNotificationType)
2881 : {
2882 8 : if (!mDocStateListeners.Length()) {
2883 : // Maybe there just aren't any.
2884 : return NS_OK;
2885 : }
2886 :
2887 0 : AutoDocumentStateListenerArray listeners(mDocStateListeners);
2888 0 : nsresult rv = NS_OK;
2889 :
2890 0 : switch (aNotificationType) {
2891 : case eDocumentCreated:
2892 0 : for (auto& listener : listeners) {
2893 0 : rv = listener->NotifyDocumentCreated();
2894 0 : if (NS_FAILED(rv)) {
2895 : break;
2896 : }
2897 : }
2898 0 : break;
2899 :
2900 : case eDocumentToBeDestroyed:
2901 0 : for (auto& listener : listeners) {
2902 0 : rv = listener->NotifyDocumentWillBeDestroyed();
2903 0 : if (NS_FAILED(rv)) {
2904 : break;
2905 : }
2906 : }
2907 0 : break;
2908 :
2909 : case eDocumentStateChanged: {
2910 : bool docIsDirty;
2911 0 : rv = GetDocumentModified(&docIsDirty);
2912 0 : NS_ENSURE_SUCCESS(rv, rv);
2913 :
2914 0 : if (static_cast<int8_t>(docIsDirty) == mDocDirtyState) {
2915 : return NS_OK;
2916 : }
2917 :
2918 0 : mDocDirtyState = docIsDirty;
2919 :
2920 0 : for (auto& listener : listeners) {
2921 0 : rv = listener->NotifyDocumentStateChanged(mDocDirtyState);
2922 0 : if (NS_FAILED(rv)) {
2923 : break;
2924 : }
2925 : }
2926 0 : break;
2927 : }
2928 : default:
2929 0 : NS_NOTREACHED("Unknown notification");
2930 : }
2931 :
2932 : return rv;
2933 : }
2934 :
2935 : nsresult
2936 0 : EditorBase::SetTextImpl(Selection& aSelection, const nsAString& aString,
2937 : Text& aCharData)
2938 : {
2939 0 : const uint32_t length = aCharData.Length();
2940 :
2941 : AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
2942 : *this, EditSubAction::eSetText,
2943 0 : nsIEditor::eNext);
2944 :
2945 : // Let listeners know what's up
2946 0 : if (!mActionListeners.IsEmpty() && length) {
2947 0 : AutoActionListenerArray listeners(mActionListeners);
2948 0 : for (auto& listener : listeners) {
2949 0 : listener->WillDeleteText(&aCharData, 0, length);
2950 : }
2951 : }
2952 :
2953 : // We don't support undo here, so we don't really need all of the transaction
2954 : // machinery, therefore we can run our transaction directly, breaking all of
2955 : // the rules!
2956 0 : ErrorResult res;
2957 0 : aCharData.SetData(aString, res);
2958 0 : nsresult rv = res.StealNSResult();
2959 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2960 : return rv;
2961 : }
2962 :
2963 0 : RefPtr<Selection> selection = GetSelection();
2964 0 : if (NS_WARN_IF(!selection)) {
2965 : return NS_ERROR_FAILURE;
2966 : }
2967 :
2968 : {
2969 : // Create a nested scope to not overwrite rv from the outer scope.
2970 0 : DebugOnly<nsresult> rv = selection->Collapse(&aCharData, aString.Length());
2971 0 : NS_ASSERTION(NS_SUCCEEDED(rv),
2972 : "Selection could not be collapsed after insert");
2973 : }
2974 :
2975 0 : mRangeUpdater.SelAdjDeleteText(&aCharData, 0, length);
2976 0 : mRangeUpdater.SelAdjInsertText(aCharData, 0, aString);
2977 :
2978 0 : if (mRules && mRules->AsHTMLEditRules()) {
2979 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
2980 0 : if (length) {
2981 0 : htmlEditRules->DidDeleteText(*selection, aCharData, 0, length);
2982 : }
2983 0 : if (!aString.IsEmpty()) {
2984 0 : htmlEditRules->DidInsertText(*selection, aCharData, 0, aString);
2985 : }
2986 : }
2987 :
2988 : // Let listeners know what happened
2989 0 : if (!mActionListeners.IsEmpty()) {
2990 0 : AutoActionListenerArray listeners(mActionListeners);
2991 0 : for (auto& listener : listeners) {
2992 0 : if (length) {
2993 0 : listener->DidDeleteText(&aCharData, 0, length, rv);
2994 : }
2995 0 : if (!aString.IsEmpty()) {
2996 0 : listener->DidInsertText(&aCharData, 0, aString, rv);
2997 : }
2998 : }
2999 : }
3000 :
3001 : return rv;
3002 : }
3003 :
3004 : nsresult
3005 0 : EditorBase::DeleteTextWithTransaction(CharacterData& aCharData,
3006 : uint32_t aOffset,
3007 : uint32_t aLength)
3008 : {
3009 : RefPtr<DeleteTextTransaction> transaction =
3010 0 : DeleteTextTransaction::MaybeCreate(*this, aCharData, aOffset, aLength);
3011 0 : if (NS_WARN_IF(!transaction)) {
3012 : return NS_ERROR_FAILURE;
3013 : }
3014 :
3015 : AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
3016 : *this, EditSubAction::eDeleteText,
3017 0 : nsIEditor::ePrevious);
3018 :
3019 : // Let listeners know what's up
3020 0 : if (!mActionListeners.IsEmpty()) {
3021 0 : AutoActionListenerArray listeners(mActionListeners);
3022 0 : for (auto& listener : listeners) {
3023 0 : listener->WillDeleteText(&aCharData, aOffset, aLength);
3024 : }
3025 : }
3026 :
3027 0 : nsresult rv = DoTransaction(transaction);
3028 :
3029 0 : if (mRules && mRules->AsHTMLEditRules()) {
3030 0 : RefPtr<Selection> selection = GetSelection();
3031 0 : if (selection) {
3032 0 : RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
3033 0 : htmlEditRules->DidDeleteText(*selection, aCharData, aOffset, aLength);
3034 : } else {
3035 0 : NS_WARNING("Selection has gone");
3036 : }
3037 : }
3038 :
3039 : // Let listeners know what happened
3040 0 : if (!mActionListeners.IsEmpty()) {
3041 0 : AutoActionListenerArray listeners(mActionListeners);
3042 0 : for (auto& listener : listeners) {
3043 0 : listener->DidDeleteText(&aCharData, aOffset, aLength, rv);
3044 : }
3045 : }
3046 :
3047 : return rv;
3048 : }
3049 :
3050 0 : struct SavedRange final
3051 : {
3052 : RefPtr<Selection> mSelection;
3053 : nsCOMPtr<nsINode> mStartContainer;
3054 : nsCOMPtr<nsINode> mEndContainer;
3055 : int32_t mStartOffset;
3056 : int32_t mEndOffset;
3057 : };
3058 :
3059 : void
3060 0 : EditorBase::DoSplitNode(const EditorDOMPoint& aStartOfRightNode,
3061 : nsIContent& aNewLeftNode,
3062 : ErrorResult& aError)
3063 : {
3064 0 : if (NS_WARN_IF(aError.Failed())) {
3065 0 : return;
3066 : }
3067 :
3068 : // XXX Perhaps, aStartOfRightNode may be invalid if this is a redo
3069 : // operation after modifying DOM node with JS.
3070 0 : if (NS_WARN_IF(!aStartOfRightNode.IsSet())) {
3071 0 : aError.Throw(NS_ERROR_INVALID_ARG);
3072 0 : return;
3073 : }
3074 0 : MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
3075 :
3076 : // Remember all selection points.
3077 0 : AutoTArray<SavedRange, 10> savedRanges;
3078 0 : for (SelectionType selectionType : kPresentSelectionTypes) {
3079 0 : SavedRange range;
3080 0 : range.mSelection = GetSelection(selectionType);
3081 0 : if (NS_WARN_IF(!range.mSelection &&
3082 : selectionType == SelectionType::eNormal)) {
3083 0 : aError.Throw(NS_ERROR_FAILURE);
3084 0 : return;
3085 0 : } else if (!range.mSelection) {
3086 : // For non-normal selections, skip over the non-existing ones.
3087 0 : continue;
3088 : }
3089 :
3090 0 : for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
3091 0 : RefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
3092 0 : MOZ_ASSERT(r->IsPositioned());
3093 : // XXX Looks like that SavedRange should have mStart and mEnd which
3094 : // are RangeBoundary. Then, we can avoid to compute offset here.
3095 0 : range.mStartContainer = r->GetStartContainer();
3096 0 : range.mStartOffset = r->StartOffset();
3097 0 : range.mEndContainer = r->GetEndContainer();
3098 0 : range.mEndOffset = r->EndOffset();
3099 :
3100 0 : savedRanges.AppendElement(range);
3101 : }
3102 : }
3103 :
3104 0 : nsCOMPtr<nsINode> parent = aStartOfRightNode.GetContainer()->GetParentNode();
3105 0 : if (NS_WARN_IF(!parent)) {
3106 0 : aError.Throw(NS_ERROR_FAILURE);
3107 0 : return;
3108 : }
3109 :
3110 : // Fix the child before mutation observer may touch the DOM tree.
3111 0 : nsIContent* firstChildOfRightNode = aStartOfRightNode.GetChild();
3112 0 : parent->InsertBefore(aNewLeftNode, aStartOfRightNode.GetContainer(),
3113 0 : aError);
3114 0 : if (NS_WARN_IF(aError.Failed())) {
3115 : return;
3116 : }
3117 :
3118 : // At this point, the existing right node has all the children. Move all
3119 : // the children which are before aStartOfRightNode.
3120 0 : if (!aStartOfRightNode.IsStartOfContainer()) {
3121 : // If it's a text node, just shuffle around some text
3122 0 : Text* rightAsText = aStartOfRightNode.GetContainerAsText();
3123 0 : Text* leftAsText = aNewLeftNode.GetAsText();
3124 0 : if (rightAsText && leftAsText) {
3125 : // Fix right node
3126 0 : nsAutoString leftText;
3127 0 : rightAsText->SubstringData(0, aStartOfRightNode.Offset(),
3128 0 : leftText, IgnoreErrors());
3129 0 : rightAsText->DeleteData(0, aStartOfRightNode.Offset(), IgnoreErrors());
3130 : // Fix left node
3131 0 : leftAsText->GetAsText()->SetData(leftText, IgnoreErrors());
3132 : } else {
3133 0 : MOZ_DIAGNOSTIC_ASSERT(!rightAsText && !leftAsText);
3134 : // Otherwise it's an interior node, so shuffle around the children. Go
3135 : // through list backwards so deletes don't interfere with the iteration.
3136 0 : if (!firstChildOfRightNode) {
3137 0 : MoveAllChildren(*aStartOfRightNode.GetContainer(),
3138 0 : EditorRawDOMPoint(&aNewLeftNode, 0), aError);
3139 0 : NS_WARNING_ASSERTION(!aError.Failed(),
3140 : "Failed to move all children from the right node to the left node");
3141 0 : } else if (NS_WARN_IF(aStartOfRightNode.GetContainer() !=
3142 : firstChildOfRightNode->GetParentNode())) {
3143 : // firstChildOfRightNode has been moved by mutation observer.
3144 : // In this case, we what should we do? Use offset? But we cannot
3145 : // check if the offset is still expected.
3146 : } else {
3147 : MovePreviousSiblings(*firstChildOfRightNode,
3148 0 : EditorRawDOMPoint(&aNewLeftNode, 0), aError);
3149 0 : NS_WARNING_ASSERTION(!aError.Failed(),
3150 : "Failed to move some children from the right node to the left node");
3151 : }
3152 : }
3153 : }
3154 :
3155 : // XXX Why do we ignore an error while moving nodes from the right node to
3156 : // the left node?
3157 0 : aError.SuppressException();
3158 :
3159 : // Handle selection
3160 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
3161 0 : if (ps) {
3162 0 : ps->FlushPendingNotifications(FlushType::Frames);
3163 : }
3164 0 : NS_WARNING_ASSERTION(!Destroyed(),
3165 : "The editor is destroyed during splitting a node");
3166 :
3167 0 : bool shouldSetSelection = GetShouldTxnSetSelection();
3168 :
3169 0 : RefPtr<Selection> previousSelection;
3170 0 : for (size_t i = 0; i < savedRanges.Length(); ++i) {
3171 : // Adjust the selection if needed.
3172 0 : SavedRange& range = savedRanges[i];
3173 :
3174 : // If we have not seen the selection yet, clear all of its ranges.
3175 0 : if (range.mSelection != previousSelection) {
3176 0 : range.mSelection->RemoveAllRanges(aError);
3177 0 : if (NS_WARN_IF(aError.Failed())) {
3178 0 : return;
3179 : }
3180 0 : previousSelection = range.mSelection;
3181 : }
3182 :
3183 : // XXX Looks like that we don't need to modify normal selection here
3184 : // because selection will be modified by the caller if
3185 : // GetShouldTxnSetSelection() will return true.
3186 0 : if (shouldSetSelection &&
3187 0 : range.mSelection->Type() == SelectionType::eNormal) {
3188 : // If the editor should adjust the selection, don't bother restoring
3189 : // the ranges for the normal selection here.
3190 0 : continue;
3191 : }
3192 :
3193 : // Split the selection into existing node and new node.
3194 0 : if (range.mStartContainer == aStartOfRightNode.GetContainer()) {
3195 0 : if (static_cast<uint32_t>(range.mStartOffset) <
3196 0 : aStartOfRightNode.Offset()) {
3197 0 : range.mStartContainer = &aNewLeftNode;
3198 : } else {
3199 0 : range.mStartOffset -= aStartOfRightNode.Offset();
3200 : }
3201 : }
3202 :
3203 0 : if (range.mEndContainer == aStartOfRightNode.GetContainer()) {
3204 0 : if (static_cast<uint32_t>(range.mEndOffset) <
3205 0 : aStartOfRightNode.Offset()) {
3206 0 : range.mEndContainer = &aNewLeftNode;
3207 : } else {
3208 0 : range.mEndOffset -= aStartOfRightNode.Offset();
3209 : }
3210 : }
3211 :
3212 0 : RefPtr<nsRange> newRange;
3213 0 : nsresult rv = nsRange::CreateRange(range.mStartContainer,
3214 0 : range.mStartOffset,
3215 : range.mEndContainer,
3216 0 : range.mEndOffset,
3217 0 : getter_AddRefs(newRange));
3218 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3219 0 : aError.Throw(rv);
3220 0 : return;
3221 : }
3222 0 : range.mSelection->AddRange(*newRange, aError);
3223 0 : if (NS_WARN_IF(aError.Failed())) {
3224 : return;
3225 : }
3226 : }
3227 :
3228 : // We don't need to set selection here because the caller should do that
3229 : // in any case.
3230 : }
3231 :
3232 : nsresult
3233 0 : EditorBase::DoJoinNodes(nsINode* aNodeToKeep,
3234 : nsINode* aNodeToJoin,
3235 : nsINode* aParent)
3236 : {
3237 0 : MOZ_ASSERT(aNodeToKeep);
3238 0 : MOZ_ASSERT(aNodeToJoin);
3239 0 : MOZ_ASSERT(aParent);
3240 :
3241 0 : uint32_t firstNodeLength = aNodeToJoin->Length();
3242 :
3243 : int32_t joinOffset;
3244 0 : GetNodeLocation(aNodeToJoin, &joinOffset);
3245 : int32_t keepOffset;
3246 0 : nsINode* parent = GetNodeLocation(aNodeToKeep, &keepOffset);
3247 :
3248 : // Remember all selection points.
3249 0 : AutoTArray<SavedRange, 10> savedRanges;
3250 0 : for (SelectionType selectionType : kPresentSelectionTypes) {
3251 0 : SavedRange range;
3252 0 : range.mSelection = GetSelection(selectionType);
3253 0 : if (selectionType == SelectionType::eNormal) {
3254 0 : NS_ENSURE_TRUE(range.mSelection, NS_ERROR_NULL_POINTER);
3255 0 : } else if (!range.mSelection) {
3256 : // For non-normal selections, skip over the non-existing ones.
3257 0 : continue;
3258 : }
3259 :
3260 0 : for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
3261 0 : RefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
3262 0 : MOZ_ASSERT(r->IsPositioned());
3263 0 : range.mStartContainer = r->GetStartContainer();
3264 0 : range.mStartOffset = r->StartOffset();
3265 0 : range.mEndContainer = r->GetEndContainer();
3266 0 : range.mEndOffset = r->EndOffset();
3267 :
3268 : // If selection endpoint is between the nodes, remember it as being
3269 : // in the one that is going away instead. This simplifies later selection
3270 : // adjustment logic at end of this method.
3271 0 : if (range.mStartContainer) {
3272 0 : if (range.mStartContainer == parent &&
3273 0 : joinOffset < range.mStartOffset &&
3274 0 : range.mStartOffset <= keepOffset) {
3275 0 : range.mStartContainer = aNodeToJoin;
3276 0 : range.mStartOffset = firstNodeLength;
3277 : }
3278 0 : if (range.mEndContainer == parent &&
3279 0 : joinOffset < range.mEndOffset &&
3280 0 : range.mEndOffset <= keepOffset) {
3281 0 : range.mEndContainer = aNodeToJoin;
3282 0 : range.mEndOffset = firstNodeLength;
3283 : }
3284 : }
3285 :
3286 0 : savedRanges.AppendElement(range);
3287 : }
3288 : }
3289 :
3290 : // OK, ready to do join now.
3291 : // If it's a text node, just shuffle around some text.
3292 0 : if (IsTextNode(aNodeToKeep) && IsTextNode(aNodeToJoin)) {
3293 0 : nsAutoString rightText;
3294 0 : nsAutoString leftText;
3295 0 : aNodeToKeep->GetAsText()->GetData(rightText);
3296 0 : aNodeToJoin->GetAsText()->GetData(leftText);
3297 0 : leftText += rightText;
3298 0 : aNodeToKeep->GetAsText()->SetData(leftText, IgnoreErrors());
3299 : } else {
3300 : // Otherwise it's an interior node, so shuffle around the children.
3301 0 : nsCOMPtr<nsINodeList> childNodes = aNodeToJoin->ChildNodes();
3302 0 : MOZ_ASSERT(childNodes);
3303 :
3304 : // Remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
3305 : // GetFirstChild returns nullptr firstNode if aNodeToKeep has no children, that's OK.
3306 0 : nsCOMPtr<nsIContent> firstNode = aNodeToKeep->GetFirstChild();
3307 :
3308 : // Have to go through the list backwards to keep deletes from interfering with iteration.
3309 0 : for (uint32_t i = childNodes->Length(); i; --i) {
3310 0 : nsCOMPtr<nsIContent> childNode = childNodes->Item(i - 1);
3311 0 : if (childNode) {
3312 : // prepend children of aNodeToJoin
3313 0 : ErrorResult err;
3314 0 : aNodeToKeep->InsertBefore(*childNode, firstNode, err);
3315 0 : NS_ENSURE_TRUE(!err.Failed(), err.StealNSResult());
3316 0 : firstNode = childNode.forget();
3317 : }
3318 : }
3319 : }
3320 :
3321 : // Delete the extra node.
3322 0 : ErrorResult err;
3323 0 : aParent->RemoveChild(*aNodeToJoin, err);
3324 :
3325 0 : bool shouldSetSelection = GetShouldTxnSetSelection();
3326 :
3327 0 : RefPtr<Selection> previousSelection;
3328 0 : for (size_t i = 0; i < savedRanges.Length(); ++i) {
3329 : // And adjust the selection if needed.
3330 0 : SavedRange& range = savedRanges[i];
3331 :
3332 : // If we have not seen the selection yet, clear all of its ranges.
3333 0 : if (range.mSelection != previousSelection) {
3334 0 : ErrorResult rv;
3335 0 : range.mSelection->RemoveAllRanges(rv);
3336 0 : if (NS_WARN_IF(rv.Failed())) {
3337 0 : return rv.StealNSResult();
3338 : }
3339 0 : previousSelection = range.mSelection;
3340 : }
3341 :
3342 0 : if (shouldSetSelection &&
3343 0 : range.mSelection->Type() == SelectionType::eNormal) {
3344 : // If the editor should adjust the selection, don't bother restoring
3345 : // the ranges for the normal selection here.
3346 0 : continue;
3347 : }
3348 :
3349 : // Check to see if we joined nodes where selection starts.
3350 0 : if (range.mStartContainer == aNodeToJoin) {
3351 0 : range.mStartContainer = aNodeToKeep;
3352 0 : } else if (range.mStartContainer == aNodeToKeep) {
3353 0 : range.mStartOffset += firstNodeLength;
3354 : }
3355 :
3356 : // Check to see if we joined nodes where selection ends.
3357 0 : if (range.mEndContainer == aNodeToJoin) {
3358 0 : range.mEndContainer = aNodeToKeep;
3359 0 : } else if (range.mEndContainer == aNodeToKeep) {
3360 0 : range.mEndOffset += firstNodeLength;
3361 : }
3362 :
3363 0 : RefPtr<nsRange> newRange;
3364 0 : nsresult rv = nsRange::CreateRange(range.mStartContainer,
3365 0 : range.mStartOffset,
3366 : range.mEndContainer,
3367 0 : range.mEndOffset,
3368 0 : getter_AddRefs(newRange));
3369 0 : NS_ENSURE_SUCCESS(rv, rv);
3370 0 : ErrorResult err;
3371 0 : range.mSelection->AddRange(*newRange, err);
3372 0 : if (NS_WARN_IF(err.Failed())) {
3373 0 : return err.StealNSResult();
3374 : }
3375 : }
3376 :
3377 0 : if (shouldSetSelection) {
3378 : // Editor wants us to set selection at join point.
3379 0 : RefPtr<Selection> selection = GetSelection();
3380 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
3381 0 : selection->Collapse(aNodeToKeep, AssertedCast<int32_t>(firstNodeLength));
3382 : }
3383 :
3384 0 : return err.StealNSResult();
3385 : }
3386 :
3387 : // static
3388 : int32_t
3389 0 : EditorBase::GetChildOffset(nsINode* aChild,
3390 : nsINode* aParent)
3391 : {
3392 0 : MOZ_ASSERT(aChild);
3393 0 : MOZ_ASSERT(aParent);
3394 :
3395 : // nsINode::ComputeIndexOf() is expensive. So, if we can return index
3396 : // without calling it, we should do that.
3397 :
3398 : // If there is no previous siblings, it means that it's the first child.
3399 0 : if (aParent->GetFirstChild() == aChild) {
3400 0 : MOZ_ASSERT(aParent->ComputeIndexOf(aChild) == 0);
3401 : return 0;
3402 : }
3403 :
3404 : // If there is no next siblings, it means that it's the last child.
3405 0 : if (aParent->GetLastChild() == aChild) {
3406 0 : int32_t lastChildIndex = static_cast<int32_t>(aParent->Length() - 1);
3407 0 : MOZ_ASSERT(aParent->ComputeIndexOf(aChild) == lastChildIndex);
3408 : return lastChildIndex;
3409 : }
3410 :
3411 0 : int32_t index = aParent->ComputeIndexOf(aChild);
3412 0 : MOZ_ASSERT(index != -1);
3413 : return index;
3414 : }
3415 :
3416 : // static
3417 : nsINode*
3418 0 : EditorBase::GetNodeLocation(nsINode* aChild,
3419 : int32_t* aOffset)
3420 : {
3421 0 : MOZ_ASSERT(aChild);
3422 0 : MOZ_ASSERT(aOffset);
3423 :
3424 0 : nsINode* parent = aChild->GetParentNode();
3425 0 : if (parent) {
3426 0 : *aOffset = GetChildOffset(aChild, parent);
3427 0 : MOZ_ASSERT(*aOffset != -1);
3428 : } else {
3429 0 : *aOffset = -1;
3430 : }
3431 0 : return parent;
3432 : }
3433 :
3434 : nsIContent*
3435 0 : EditorBase::GetPreviousNodeInternal(nsINode& aNode,
3436 : bool aFindEditableNode,
3437 : bool aFindAnyDataNode,
3438 : bool aNoBlockCrossing)
3439 : {
3440 0 : if (!IsDescendantOfEditorRoot(&aNode)) {
3441 : return nullptr;
3442 : }
3443 0 : return FindNode(&aNode, false,
3444 0 : aFindEditableNode, aFindAnyDataNode, aNoBlockCrossing);
3445 : }
3446 :
3447 : nsIContent*
3448 0 : EditorBase::GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint,
3449 : bool aFindEditableNode,
3450 : bool aFindAnyDataNode,
3451 : bool aNoBlockCrossing)
3452 : {
3453 0 : MOZ_ASSERT(aPoint.IsSetAndValid());
3454 0 : NS_WARNING_ASSERTION(!aPoint.IsInDataNode() || aPoint.IsInTextNode(),
3455 : "GetPreviousNodeInternal() doesn't assume that the start point is a "
3456 : "data node except text node");
3457 :
3458 : // If we are at the beginning of the node, or it is a text node, then just
3459 : // look before it.
3460 0 : if (aPoint.IsStartOfContainer() || aPoint.IsInTextNode()) {
3461 0 : if (aNoBlockCrossing && IsBlockNode(aPoint.GetContainer())) {
3462 : // If we aren't allowed to cross blocks, don't look before this block.
3463 : return nullptr;
3464 : }
3465 0 : return GetPreviousNodeInternal(*aPoint.GetContainer(),
3466 : aFindEditableNode, aFindAnyDataNode,
3467 0 : aNoBlockCrossing);
3468 : }
3469 :
3470 : // else look before the child at 'aOffset'
3471 0 : if (aPoint.GetChild()) {
3472 0 : return GetPreviousNodeInternal(*aPoint.GetChild(),
3473 : aFindEditableNode, aFindAnyDataNode,
3474 0 : aNoBlockCrossing);
3475 : }
3476 :
3477 : // unless there isn't one, in which case we are at the end of the node
3478 : // and want the deep-right child.
3479 : nsIContent* rightMostNode =
3480 0 : GetRightmostChild(aPoint.GetContainer(), aNoBlockCrossing);
3481 0 : if (!rightMostNode) {
3482 : return nullptr;
3483 : }
3484 :
3485 0 : if ((!aFindEditableNode || IsEditable(rightMostNode)) &&
3486 0 : (aFindAnyDataNode || IsElementOrText(*rightMostNode))) {
3487 : return rightMostNode;
3488 : }
3489 :
3490 : // restart the search from the non-editable node we just found
3491 0 : return GetPreviousNodeInternal(*rightMostNode,
3492 : aFindEditableNode, aFindAnyDataNode,
3493 0 : aNoBlockCrossing);
3494 : }
3495 :
3496 : nsIContent*
3497 0 : EditorBase::GetNextNodeInternal(nsINode& aNode,
3498 : bool aFindEditableNode,
3499 : bool aFindAnyDataNode,
3500 : bool aNoBlockCrossing)
3501 : {
3502 0 : if (!IsDescendantOfEditorRoot(&aNode)) {
3503 : return nullptr;
3504 : }
3505 0 : return FindNode(&aNode, true,
3506 0 : aFindEditableNode, aFindAnyDataNode, aNoBlockCrossing);
3507 : }
3508 :
3509 : nsIContent*
3510 0 : EditorBase::GetNextNodeInternal(const EditorRawDOMPoint& aPoint,
3511 : bool aFindEditableNode,
3512 : bool aFindAnyDataNode,
3513 : bool aNoBlockCrossing)
3514 : {
3515 0 : MOZ_ASSERT(aPoint.IsSetAndValid());
3516 0 : NS_WARNING_ASSERTION(!aPoint.IsInDataNode() || aPoint.IsInTextNode(),
3517 : "GetNextNodeInternal() doesn't assume that the start point is a "
3518 : "data node except text node");
3519 :
3520 0 : EditorRawDOMPoint point(aPoint);
3521 :
3522 : // if the container is a text node, use its location instead
3523 0 : if (point.IsInTextNode()) {
3524 0 : point.Set(point.GetContainer());
3525 0 : bool advanced = point.AdvanceOffset();
3526 0 : if (NS_WARN_IF(!advanced)) {
3527 : return nullptr;
3528 : }
3529 : }
3530 :
3531 0 : if (point.GetChild()) {
3532 0 : if (aNoBlockCrossing && IsBlockNode(point.GetChild())) {
3533 0 : return point.GetChild();
3534 : }
3535 :
3536 : nsIContent* leftMostNode =
3537 0 : GetLeftmostChild(point.GetChild(), aNoBlockCrossing);
3538 0 : if (!leftMostNode) {
3539 0 : return point.GetChild();
3540 : }
3541 :
3542 0 : if (!IsDescendantOfEditorRoot(leftMostNode)) {
3543 : return nullptr;
3544 : }
3545 :
3546 0 : if ((!aFindEditableNode || IsEditable(leftMostNode)) &&
3547 0 : (aFindAnyDataNode || IsElementOrText(*leftMostNode))) {
3548 : return leftMostNode;
3549 : }
3550 :
3551 : // restart the search from the non-editable node we just found
3552 0 : return GetNextNodeInternal(*leftMostNode,
3553 : aFindEditableNode, aFindAnyDataNode,
3554 0 : aNoBlockCrossing);
3555 : }
3556 :
3557 : // unless there isn't one, in which case we are at the end of the node
3558 : // and want the next one.
3559 0 : if (aNoBlockCrossing && IsBlockNode(point.GetContainer())) {
3560 : // don't cross out of parent block
3561 : return nullptr;
3562 : }
3563 :
3564 0 : return GetNextNodeInternal(*point.GetContainer(),
3565 : aFindEditableNode, aFindAnyDataNode,
3566 0 : aNoBlockCrossing);
3567 : }
3568 :
3569 : nsIContent*
3570 0 : EditorBase::FindNextLeafNode(nsINode* aCurrentNode,
3571 : bool aGoForward,
3572 : bool bNoBlockCrossing)
3573 : {
3574 : // called only by GetPriorNode so we don't need to check params.
3575 0 : MOZ_ASSERT(IsDescendantOfEditorRoot(aCurrentNode) &&
3576 : !IsEditorRoot(aCurrentNode),
3577 : "Bogus arguments");
3578 :
3579 : nsINode* cur = aCurrentNode;
3580 : for (;;) {
3581 : // if aCurrentNode has a sibling in the right direction, return
3582 : // that sibling's closest child (or itself if it has no children)
3583 : nsIContent* sibling =
3584 0 : aGoForward ? cur->GetNextSibling() : cur->GetPreviousSibling();
3585 0 : if (sibling) {
3586 0 : if (bNoBlockCrossing && IsBlockNode(sibling)) {
3587 : // don't look inside prevsib, since it is a block
3588 : return sibling;
3589 : }
3590 : nsIContent *leaf =
3591 0 : aGoForward ? GetLeftmostChild(sibling, bNoBlockCrossing) :
3592 0 : GetRightmostChild(sibling, bNoBlockCrossing);
3593 0 : if (!leaf) {
3594 : return sibling;
3595 : }
3596 :
3597 0 : return leaf;
3598 : }
3599 :
3600 0 : nsINode *parent = cur->GetParentNode();
3601 0 : if (!parent) {
3602 : return nullptr;
3603 : }
3604 :
3605 0 : NS_ASSERTION(IsDescendantOfEditorRoot(parent),
3606 : "We started with a proper descendant of root, and should stop "
3607 : "if we ever hit the root, so we better have a descendant of "
3608 : "root now!");
3609 0 : if (IsEditorRoot(parent) ||
3610 0 : (bNoBlockCrossing && IsBlockNode(parent))) {
3611 : return nullptr;
3612 : }
3613 :
3614 : cur = parent;
3615 : }
3616 :
3617 : NS_NOTREACHED("What part of for(;;) do you not understand?");
3618 : return nullptr;
3619 : }
3620 :
3621 : nsIContent*
3622 0 : EditorBase::FindNode(nsINode* aCurrentNode,
3623 : bool aGoForward,
3624 : bool aEditableNode,
3625 : bool aFindAnyDataNode,
3626 : bool bNoBlockCrossing)
3627 : {
3628 0 : if (IsEditorRoot(aCurrentNode)) {
3629 : // Don't allow traversal above the root node! This helps
3630 : // prevent us from accidentally editing browser content
3631 : // when the editor is in a text widget.
3632 :
3633 : return nullptr;
3634 : }
3635 :
3636 : nsCOMPtr<nsIContent> candidate =
3637 0 : FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing);
3638 :
3639 0 : if (!candidate) {
3640 : return nullptr;
3641 : }
3642 :
3643 0 : if ((!aEditableNode || IsEditable(candidate)) &&
3644 0 : (aFindAnyDataNode || IsElementOrText(*candidate))) {
3645 0 : return candidate;
3646 : }
3647 :
3648 0 : return FindNode(candidate, aGoForward,
3649 0 : aEditableNode, aFindAnyDataNode, bNoBlockCrossing);
3650 : }
3651 :
3652 : nsIContent*
3653 0 : EditorBase::GetRightmostChild(nsINode* aCurrentNode,
3654 : bool bNoBlockCrossing)
3655 : {
3656 0 : NS_ENSURE_TRUE(aCurrentNode, nullptr);
3657 0 : nsIContent *cur = aCurrentNode->GetLastChild();
3658 0 : if (!cur) {
3659 : return nullptr;
3660 : }
3661 : for (;;) {
3662 0 : if (bNoBlockCrossing && IsBlockNode(cur)) {
3663 : return cur;
3664 : }
3665 0 : nsIContent* next = cur->GetLastChild();
3666 0 : if (!next) {
3667 : return cur;
3668 : }
3669 : cur = next;
3670 : }
3671 :
3672 : NS_NOTREACHED("What part of for(;;) do you not understand?");
3673 : return nullptr;
3674 : }
3675 :
3676 : nsIContent*
3677 0 : EditorBase::GetLeftmostChild(nsINode* aCurrentNode,
3678 : bool bNoBlockCrossing)
3679 : {
3680 0 : NS_ENSURE_TRUE(aCurrentNode, nullptr);
3681 0 : nsIContent *cur = aCurrentNode->GetFirstChild();
3682 0 : if (!cur) {
3683 : return nullptr;
3684 : }
3685 : for (;;) {
3686 0 : if (bNoBlockCrossing && IsBlockNode(cur)) {
3687 : return cur;
3688 : }
3689 0 : nsIContent *next = cur->GetFirstChild();
3690 0 : if (!next) {
3691 : return cur;
3692 : }
3693 : cur = next;
3694 : }
3695 :
3696 : NS_NOTREACHED("What part of for(;;) do you not understand?");
3697 : return nullptr;
3698 : }
3699 :
3700 : bool
3701 0 : EditorBase::IsBlockNode(nsINode* aNode)
3702 : {
3703 : // stub to be overridden in HTMLEditor.
3704 : // screwing around with the class hierarchy here in order
3705 : // to not duplicate the code in GetNextNode/GetPrevNode
3706 : // across both EditorBase/HTMLEditor.
3707 0 : return false;
3708 : }
3709 :
3710 : bool
3711 0 : EditorBase::CanContain(nsINode& aParent,
3712 : nsIContent& aChild) const
3713 : {
3714 0 : switch (aParent.NodeType()) {
3715 : case nsINode::ELEMENT_NODE:
3716 : case nsINode::DOCUMENT_FRAGMENT_NODE:
3717 0 : return TagCanContain(*aParent.NodeInfo()->NameAtom(), aChild);
3718 : }
3719 : return false;
3720 : }
3721 :
3722 : bool
3723 0 : EditorBase::CanContainTag(nsINode& aParent,
3724 : nsAtom& aChildTag) const
3725 : {
3726 0 : switch (aParent.NodeType()) {
3727 : case nsINode::ELEMENT_NODE:
3728 : case nsINode::DOCUMENT_FRAGMENT_NODE:
3729 0 : return TagCanContainTag(*aParent.NodeInfo()->NameAtom(), aChildTag);
3730 : }
3731 : return false;
3732 : }
3733 :
3734 : bool
3735 0 : EditorBase::TagCanContain(nsAtom& aParentTag,
3736 : nsIContent& aChild) const
3737 : {
3738 0 : switch (aChild.NodeType()) {
3739 : case nsINode::TEXT_NODE:
3740 : case nsINode::ELEMENT_NODE:
3741 : case nsINode::DOCUMENT_FRAGMENT_NODE:
3742 0 : return TagCanContainTag(aParentTag, *aChild.NodeInfo()->NameAtom());
3743 : }
3744 : return false;
3745 : }
3746 :
3747 : bool
3748 0 : EditorBase::TagCanContainTag(nsAtom& aParentTag,
3749 : nsAtom& aChildTag) const
3750 : {
3751 0 : return true;
3752 : }
3753 :
3754 : bool
3755 0 : EditorBase::IsRoot(nsINode* inNode)
3756 : {
3757 0 : NS_ENSURE_TRUE(inNode, false);
3758 :
3759 0 : nsCOMPtr<nsINode> rootNode = GetRoot();
3760 :
3761 0 : return inNode == rootNode;
3762 : }
3763 :
3764 : bool
3765 0 : EditorBase::IsEditorRoot(nsINode* aNode)
3766 : {
3767 0 : NS_ENSURE_TRUE(aNode, false);
3768 0 : nsCOMPtr<nsINode> rootNode = GetEditorRoot();
3769 0 : return aNode == rootNode;
3770 : }
3771 :
3772 : bool
3773 0 : EditorBase::IsDescendantOfRoot(nsINode* inNode)
3774 : {
3775 0 : NS_ENSURE_TRUE(inNode, false);
3776 0 : nsCOMPtr<nsIContent> root = GetRoot();
3777 0 : NS_ENSURE_TRUE(root, false);
3778 :
3779 0 : return nsContentUtils::ContentIsDescendantOf(inNode, root);
3780 : }
3781 :
3782 : bool
3783 0 : EditorBase::IsDescendantOfEditorRoot(nsINode* aNode)
3784 : {
3785 0 : NS_ENSURE_TRUE(aNode, false);
3786 0 : nsCOMPtr<nsIContent> root = GetEditorRoot();
3787 0 : NS_ENSURE_TRUE(root, false);
3788 :
3789 0 : return nsContentUtils::ContentIsDescendantOf(aNode, root);
3790 : }
3791 :
3792 : bool
3793 0 : EditorBase::IsContainer(nsINode* aNode)
3794 : {
3795 0 : return aNode ? true : false;
3796 : }
3797 :
3798 : uint32_t
3799 0 : EditorBase::CountEditableChildren(nsINode* aNode)
3800 : {
3801 0 : MOZ_ASSERT(aNode);
3802 0 : uint32_t count = 0;
3803 0 : for (nsIContent* child = aNode->GetFirstChild();
3804 0 : child;
3805 0 : child = child->GetNextSibling()) {
3806 0 : if (IsEditable(child)) {
3807 0 : ++count;
3808 : }
3809 : }
3810 0 : return count;
3811 : }
3812 :
3813 : NS_IMETHODIMP
3814 0 : EditorBase::IncrementModificationCount(int32_t inNumMods)
3815 : {
3816 0 : uint32_t oldModCount = mModCount;
3817 :
3818 0 : mModCount += inNumMods;
3819 :
3820 1 : if ((!oldModCount && mModCount) ||
3821 0 : (oldModCount && !mModCount)) {
3822 1 : NotifyDocumentListeners(eDocumentStateChanged);
3823 : }
3824 1 : return NS_OK;
3825 : }
3826 :
3827 :
3828 : NS_IMETHODIMP
3829 1 : EditorBase::GetModificationCount(int32_t* outModCount)
3830 : {
3831 1 : NS_ENSURE_ARG_POINTER(outModCount);
3832 1 : *outModCount = mModCount;
3833 1 : return NS_OK;
3834 : }
3835 :
3836 :
3837 : NS_IMETHODIMP
3838 1 : EditorBase::ResetModificationCount()
3839 : {
3840 0 : bool doNotify = (mModCount != 0);
3841 :
3842 1 : mModCount = 0;
3843 :
3844 1 : if (doNotify) {
3845 1 : NotifyDocumentListeners(eDocumentStateChanged);
3846 : }
3847 1 : return NS_OK;
3848 : }
3849 :
3850 : bool
3851 0 : EditorBase::AreNodesSameType(nsIContent* aNode1,
3852 : nsIContent* aNode2)
3853 : {
3854 0 : MOZ_ASSERT(aNode1);
3855 0 : MOZ_ASSERT(aNode2);
3856 0 : return aNode1->NodeInfo()->NameAtom() == aNode2->NodeInfo()->NameAtom();
3857 : }
3858 :
3859 : // static
3860 : nsIContent*
3861 0 : EditorBase::GetNodeAtRangeOffsetPoint(const RawRangeBoundary& aPoint)
3862 : {
3863 0 : if (NS_WARN_IF(!aPoint.IsSet())) {
3864 : return nullptr;
3865 : }
3866 0 : if (aPoint.Container()->GetAsText()) {
3867 0 : return aPoint.Container()->AsContent();
3868 : }
3869 0 : return aPoint.GetChildAtOffset();
3870 : }
3871 :
3872 : // static
3873 : EditorRawDOMPoint
3874 1 : EditorBase::GetStartPoint(Selection* aSelection)
3875 : {
3876 0 : MOZ_ASSERT(aSelection);
3877 :
3878 2 : if (NS_WARN_IF(!aSelection->RangeCount())) {
3879 : return EditorRawDOMPoint();
3880 : }
3881 :
3882 1 : const nsRange* range = aSelection->GetRangeAt(0);
3883 2 : if (NS_WARN_IF(!range) ||
3884 1 : NS_WARN_IF(!range->IsPositioned())) {
3885 : return EditorRawDOMPoint();
3886 : }
3887 :
3888 0 : return EditorRawDOMPoint(range->StartRef());
3889 : }
3890 :
3891 : // static
3892 : EditorRawDOMPoint
3893 0 : EditorBase::GetEndPoint(Selection* aSelection)
3894 : {
3895 0 : MOZ_ASSERT(aSelection);
3896 :
3897 0 : if (NS_WARN_IF(!aSelection->RangeCount())) {
3898 : return EditorRawDOMPoint();
3899 : }
3900 :
3901 0 : const nsRange* range = aSelection->GetRangeAt(0);
3902 0 : if (NS_WARN_IF(!range) ||
3903 0 : NS_WARN_IF(!range->IsPositioned())) {
3904 : return EditorRawDOMPoint();
3905 : }
3906 :
3907 0 : return EditorRawDOMPoint(range->EndRef());
3908 : }
3909 :
3910 : nsresult
3911 0 : EditorBase::GetEndChildNode(Selection* aSelection,
3912 : nsIContent** aEndNode)
3913 : {
3914 0 : MOZ_ASSERT(aSelection);
3915 0 : MOZ_ASSERT(aEndNode);
3916 :
3917 0 : *aEndNode = nullptr;
3918 :
3919 0 : if (NS_WARN_IF(!aSelection->RangeCount())) {
3920 : return NS_ERROR_FAILURE;
3921 : }
3922 :
3923 0 : const nsRange* range = aSelection->GetRangeAt(0);
3924 0 : if (NS_WARN_IF(!range)) {
3925 : return NS_ERROR_FAILURE;
3926 : }
3927 :
3928 0 : if (NS_WARN_IF(!range->IsPositioned())) {
3929 : return NS_ERROR_FAILURE;
3930 : }
3931 :
3932 0 : NS_IF_ADDREF(*aEndNode = range->GetChildAtEndOffset());
3933 0 : return NS_OK;
3934 : }
3935 :
3936 : /**
3937 : * IsPreformatted() checks the style info for the node for the preformatted
3938 : * text style.
3939 : */
3940 : // static
3941 : bool
3942 0 : EditorBase::IsPreformatted(nsINode* aNode)
3943 : {
3944 0 : if (NS_WARN_IF(!aNode)) {
3945 : return false;
3946 : }
3947 : // Look at the node (and its parent if it's not an element), and grab its
3948 : // ComputedStyle.
3949 0 : Element* element = Element::FromNode(aNode);
3950 0 : if (!element) {
3951 0 : element = aNode->GetParentElement();
3952 0 : if (!element) {
3953 : return false;
3954 : }
3955 : }
3956 :
3957 : RefPtr<ComputedStyle> elementStyle =
3958 0 : nsComputedDOMStyle::GetComputedStyleNoFlush(element, nullptr);
3959 0 : if (!elementStyle) {
3960 : // Consider nodes without a ComputedStyle to be NOT preformatted:
3961 : // For instance, this is true of JS tags inside the body (which show
3962 : // up as #text nodes but have no ComputedStyle).
3963 : return false;
3964 : }
3965 :
3966 0 : const nsStyleText* styleText = elementStyle->StyleText();
3967 :
3968 0 : return styleText->WhiteSpaceIsSignificant();
3969 : }
3970 :
3971 : template<typename PT, typename CT>
3972 : SplitNodeResult
3973 0 : EditorBase::SplitNodeDeepWithTransaction(
3974 : nsIContent& aMostAncestorToSplit,
3975 : const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode,
3976 : SplitAtEdges aSplitAtEdges)
3977 : {
3978 0 : MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
3979 0 : MOZ_ASSERT(aStartOfDeepestRightNode.GetContainer() == &aMostAncestorToSplit ||
3980 : EditorUtils::IsDescendantOf(
3981 : *aStartOfDeepestRightNode.GetContainer(),
3982 : aMostAncestorToSplit));
3983 :
3984 0 : if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
3985 0 : return SplitNodeResult(NS_ERROR_INVALID_ARG);
3986 : }
3987 :
3988 0 : nsCOMPtr<nsIContent> newLeftNodeOfMostAncestor;
3989 0 : EditorDOMPoint atStartOfRightNode(aStartOfDeepestRightNode);
3990 : while (true) {
3991 : // Need to insert rules code call here to do things like not split a list
3992 : // if you are after the last <li> or before the first, etc. For now we
3993 : // just have some smarts about unneccessarily splitting text nodes, which
3994 : // should be universal enough to put straight in this EditorBase routine.
3995 0 : if (NS_WARN_IF(!atStartOfRightNode.GetContainerAsContent())) {
3996 0 : return SplitNodeResult(NS_ERROR_FAILURE);
3997 : }
3998 : // If we meet an orphan node before meeting aMostAncestorToSplit, we need
3999 : // to stop splitting. This is a bug of the caller.
4000 0 : if (NS_WARN_IF(atStartOfRightNode.GetContainer() != &aMostAncestorToSplit &&
4001 : !atStartOfRightNode.GetContainer()->GetParent())) {
4002 0 : return SplitNodeResult(NS_ERROR_FAILURE);
4003 : }
4004 :
4005 0 : nsIContent* currentRightNode = atStartOfRightNode.GetContainerAsContent();
4006 :
4007 : // If the split point is middle of the node or the node is not a text node
4008 : // and we're allowed to create empty element node, split it.
4009 0 : if ((aSplitAtEdges == SplitAtEdges::eAllowToCreateEmptyContainer &&
4010 0 : !atStartOfRightNode.GetContainerAsText()) ||
4011 0 : (!atStartOfRightNode.IsStartOfContainer() &&
4012 0 : !atStartOfRightNode.IsEndOfContainer())) {
4013 0 : ErrorResult error;
4014 : nsCOMPtr<nsIContent> newLeftNode =
4015 0 : SplitNodeWithTransaction(atStartOfRightNode, error);
4016 0 : if (NS_WARN_IF(error.Failed())) {
4017 0 : return SplitNodeResult(error.StealNSResult());
4018 : }
4019 :
4020 0 : if (currentRightNode == &aMostAncestorToSplit) {
4021 : // Actually, we split aMostAncestorToSplit.
4022 0 : return SplitNodeResult(newLeftNode, &aMostAncestorToSplit);
4023 : }
4024 :
4025 : // Then, try to split its parent before current node.
4026 0 : atStartOfRightNode.Set(currentRightNode);
4027 : }
4028 : // If the split point is end of the node and it is a text node or we're not
4029 : // allowed to create empty container node, try to split its parent after it.
4030 0 : else if (!atStartOfRightNode.IsStartOfContainer()) {
4031 0 : if (currentRightNode == &aMostAncestorToSplit) {
4032 0 : return SplitNodeResult(&aMostAncestorToSplit, nullptr);
4033 : }
4034 :
4035 : // Try to split its parent after current node.
4036 0 : atStartOfRightNode.Set(currentRightNode);
4037 0 : DebugOnly<bool> advanced = atStartOfRightNode.AdvanceOffset();
4038 0 : NS_WARNING_ASSERTION(advanced,
4039 : "Failed to advance offset after current node");
4040 : }
4041 : // If the split point is start of the node and it is a text node or we're
4042 : // not allowed to create empty container node, try to split its parent.
4043 : else {
4044 0 : if (currentRightNode == &aMostAncestorToSplit) {
4045 0 : return SplitNodeResult(nullptr, &aMostAncestorToSplit);
4046 : }
4047 :
4048 : // Try to split its parent before current node.
4049 0 : atStartOfRightNode.Set(currentRightNode);
4050 : }
4051 : }
4052 :
4053 : return SplitNodeResult(NS_ERROR_FAILURE);
4054 : }
4055 :
4056 : EditorDOMPoint
4057 0 : EditorBase::JoinNodesDeepWithTransaction(nsIContent& aLeftNode,
4058 : nsIContent& aRightNode)
4059 : {
4060 : // While the rightmost children and their descendants of the left node match
4061 : // the leftmost children and their descendants of the right node, join them
4062 : // up.
4063 :
4064 0 : nsCOMPtr<nsIContent> leftNodeToJoin = &aLeftNode;
4065 0 : nsCOMPtr<nsIContent> rightNodeToJoin = &aRightNode;
4066 0 : nsCOMPtr<nsINode> parentNode = aRightNode.GetParentNode();
4067 :
4068 0 : EditorDOMPoint ret;
4069 0 : while (leftNodeToJoin && rightNodeToJoin && parentNode &&
4070 0 : AreNodesSameType(leftNodeToJoin, rightNodeToJoin)) {
4071 0 : uint32_t length = leftNodeToJoin->Length();
4072 :
4073 : // Do the join
4074 0 : nsresult rv = JoinNodesWithTransaction(*leftNodeToJoin, *rightNodeToJoin);
4075 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4076 : return EditorDOMPoint();
4077 : }
4078 :
4079 0 : ret.Set(rightNodeToJoin, length);
4080 :
4081 0 : if (parentNode->GetAsText()) {
4082 : // We've joined all the way down to text nodes, we're done!
4083 0 : return ret;
4084 : }
4085 :
4086 : // Get new left and right nodes, and begin anew
4087 0 : parentNode = rightNodeToJoin;
4088 0 : rightNodeToJoin = parentNode->GetChildAt_Deprecated(length);
4089 0 : if (rightNodeToJoin) {
4090 0 : leftNodeToJoin = rightNodeToJoin->GetPreviousSibling();
4091 : } else {
4092 0 : leftNodeToJoin = nullptr;
4093 : }
4094 :
4095 : // Skip over non-editable nodes
4096 0 : while (leftNodeToJoin && !IsEditable(leftNodeToJoin)) {
4097 0 : leftNodeToJoin = leftNodeToJoin->GetPreviousSibling();
4098 : }
4099 0 : if (!leftNodeToJoin) {
4100 0 : return ret;
4101 : }
4102 :
4103 0 : while (rightNodeToJoin && !IsEditable(rightNodeToJoin)) {
4104 0 : rightNodeToJoin = rightNodeToJoin->GetNextSibling();
4105 : }
4106 0 : if (!rightNodeToJoin) {
4107 0 : return ret;
4108 : }
4109 : }
4110 :
4111 0 : if (NS_WARN_IF(!ret.IsSet())) {
4112 : return EditorDOMPoint();
4113 : }
4114 :
4115 0 : return ret;
4116 : }
4117 :
4118 : void
4119 0 : EditorBase::BeginUpdateViewBatch()
4120 : {
4121 0 : MOZ_ASSERT(mUpdateCount >= 0, "bad state");
4122 :
4123 0 : if (!mUpdateCount) {
4124 : // Turn off selection updates and notifications.
4125 0 : RefPtr<Selection> selection = GetSelection();
4126 0 : if (selection) {
4127 0 : selection->StartBatchChanges();
4128 : }
4129 : }
4130 :
4131 0 : mUpdateCount++;
4132 0 : }
4133 :
4134 : nsresult
4135 0 : EditorBase::EndUpdateViewBatch()
4136 : {
4137 0 : MOZ_ASSERT(mUpdateCount > 0, "bad state");
4138 :
4139 0 : if (mUpdateCount <= 0) {
4140 0 : mUpdateCount = 0;
4141 0 : return NS_ERROR_FAILURE;
4142 : }
4143 :
4144 0 : mUpdateCount--;
4145 :
4146 0 : if (!mUpdateCount) {
4147 : // Turn selection updating and notifications back on.
4148 0 : RefPtr<Selection> selection = GetSelection();
4149 0 : if (selection) {
4150 0 : selection->EndBatchChanges();
4151 : }
4152 : }
4153 :
4154 : return NS_OK;
4155 : }
4156 :
4157 : bool
4158 1 : EditorBase::GetShouldTxnSetSelection()
4159 : {
4160 1 : return mShouldTxnSetSelection;
4161 : }
4162 :
4163 : TextComposition*
4164 0 : EditorBase::GetComposition() const
4165 : {
4166 0 : return mComposition;
4167 : }
4168 :
4169 : bool
4170 0 : EditorBase::IsIMEComposing() const
4171 : {
4172 0 : return mComposition && mComposition->IsComposing();
4173 : }
4174 :
4175 : bool
4176 0 : EditorBase::ShouldHandleIMEComposition() const
4177 : {
4178 : // When the editor is being reframed, the old value may be restored with
4179 : // InsertText(). In this time, the text should be inserted as not a part
4180 : // of the composition.
4181 0 : return mComposition && mDidPostCreate;
4182 : }
4183 :
4184 : void
4185 0 : EditorBase::DoAfterDoTransaction(nsITransaction* aTxn)
4186 : {
4187 : bool isTransientTransaction;
4188 0 : MOZ_ALWAYS_SUCCEEDS(aTxn->GetIsTransient(&isTransientTransaction));
4189 :
4190 1 : if (!isTransientTransaction) {
4191 : // we need to deal here with the case where the user saved after some
4192 : // edits, then undid one or more times. Then, the undo count is -ve,
4193 : // but we can't let a do take it back to zero. So we flip it up to
4194 : // a +ve number.
4195 : int32_t modCount;
4196 1 : GetModificationCount(&modCount);
4197 1 : if (modCount < 0) {
4198 0 : modCount = -modCount;
4199 : }
4200 :
4201 : // don't count transient transactions
4202 1 : MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(1));
4203 : }
4204 1 : }
4205 :
4206 : void
4207 0 : EditorBase::DoAfterUndoTransaction()
4208 : {
4209 : // all undoable transactions are non-transient
4210 0 : MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(-1));
4211 0 : }
4212 :
4213 : void
4214 0 : EditorBase::DoAfterRedoTransaction()
4215 : {
4216 : // all redoable transactions are non-transient
4217 0 : MOZ_ALWAYS_SUCCEEDS(IncrementModificationCount(1));
4218 0 : }
4219 :
4220 : already_AddRefed<EditAggregateTransaction>
4221 0 : EditorBase::CreateTxnForDeleteSelection(EDirection aAction,
4222 : nsINode** aRemovingNode,
4223 : int32_t* aOffset,
4224 : int32_t* aLength)
4225 : {
4226 0 : RefPtr<Selection> selection = GetSelection();
4227 0 : if (NS_WARN_IF(!selection)) {
4228 : return nullptr;
4229 : }
4230 :
4231 : // Check whether the selection is collapsed and we should do nothing:
4232 0 : if (NS_WARN_IF(selection->IsCollapsed() && aAction == eNone)) {
4233 : return nullptr;
4234 : }
4235 :
4236 : // allocate the out-param transaction
4237 : RefPtr<EditAggregateTransaction> aggregateTransaction =
4238 0 : EditAggregateTransaction::Create();
4239 :
4240 0 : for (uint32_t rangeIdx = 0; rangeIdx < selection->RangeCount(); ++rangeIdx) {
4241 0 : RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
4242 0 : if (NS_WARN_IF(!range)) {
4243 0 : return nullptr;
4244 : }
4245 :
4246 : // Same with range as with selection; if it is collapsed and action
4247 : // is eNone, do nothing.
4248 0 : if (!range->Collapsed()) {
4249 : RefPtr<DeleteRangeTransaction> deleteRangeTransaction =
4250 0 : DeleteRangeTransaction::Create(*this, *range);
4251 : // XXX Oh, not checking if deleteRangeTransaction can modify the range...
4252 0 : aggregateTransaction->AppendChild(deleteRangeTransaction);
4253 0 : } else if (aAction != eNone) {
4254 : // we have an insertion point. delete the thing in front of it or
4255 : // behind it, depending on aAction
4256 : // XXX Odd, when there are two or more ranges, this returns the last
4257 : // range information with aRemovingNode, aOffset and aLength.
4258 : RefPtr<EditTransactionBase> deleteRangeTransaction =
4259 0 : CreateTxnForDeleteRange(range, aAction,
4260 0 : aRemovingNode, aOffset, aLength);
4261 : // XXX When there are two or more ranges and at least one of them is
4262 : // not editable, deleteRangeTransaction may be nullptr.
4263 : // In such case, should we stop removing other ranges too?
4264 0 : if (NS_WARN_IF(!deleteRangeTransaction)) {
4265 0 : return nullptr;
4266 : }
4267 0 : aggregateTransaction->AppendChild(deleteRangeTransaction);
4268 : }
4269 : }
4270 :
4271 : return aggregateTransaction.forget();
4272 : }
4273 :
4274 : //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior
4275 : //are not implemented
4276 : already_AddRefed<EditTransactionBase>
4277 0 : EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete,
4278 : EDirection aAction,
4279 : nsINode** aRemovingNode,
4280 : int32_t* aOffset,
4281 : int32_t* aLength)
4282 : {
4283 0 : MOZ_ASSERT(aAction != eNone);
4284 :
4285 : // get the node and offset of the insertion point
4286 0 : nsCOMPtr<nsINode> node = aRangeToDelete->GetStartContainer();
4287 0 : if (NS_WARN_IF(!node)) {
4288 : return nullptr;
4289 : }
4290 :
4291 0 : nsIContent* child = aRangeToDelete->GetChildAtStartOffset();
4292 0 : int32_t offset = aRangeToDelete->StartOffset();
4293 :
4294 : // determine if the insertion point is at the beginning, middle, or end of
4295 : // the node
4296 :
4297 0 : uint32_t count = node->Length();
4298 :
4299 0 : bool isFirst = !offset;
4300 0 : bool isLast = (count == (uint32_t)offset);
4301 :
4302 : // XXX: if isFirst && isLast, then we'll need to delete the node
4303 : // as well as the 1 child
4304 :
4305 : // build a transaction for deleting the appropriate data
4306 : // XXX: this has to come from rule section
4307 0 : if (aAction == ePrevious && isFirst) {
4308 : // we're backspacing from the beginning of the node. Delete the first
4309 : // thing to our left
4310 0 : nsCOMPtr<nsIContent> priorNode = GetPreviousEditableNode(*node);
4311 0 : if (NS_WARN_IF(!priorNode)) {
4312 : return nullptr;
4313 : }
4314 :
4315 : // there is a priorNode, so delete its last child (if chardata, delete the
4316 : // last char). if it has no children, delete it
4317 0 : if (RefPtr<CharacterData> priorNodeAsCharData =
4318 0 : CharacterData::FromNode(priorNode)) {
4319 0 : uint32_t length = priorNode->Length();
4320 : // Bail out for empty chardata XXX: Do we want to do something else?
4321 0 : if (NS_WARN_IF(!length)) {
4322 : return nullptr;
4323 : }
4324 : RefPtr<DeleteTextTransaction> deleteTextTransaction =
4325 0 : DeleteTextTransaction::MaybeCreateForPreviousCharacter(
4326 0 : *this, *priorNodeAsCharData, length);
4327 0 : if (NS_WARN_IF(!deleteTextTransaction)) {
4328 : return nullptr;
4329 : }
4330 0 : *aOffset = deleteTextTransaction->Offset();
4331 0 : *aLength = deleteTextTransaction->LengthToDelete();
4332 0 : priorNode.forget(aRemovingNode);
4333 0 : return deleteTextTransaction.forget();
4334 : }
4335 :
4336 : // priorNode is not chardata, so tell its parent to delete it
4337 : RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
4338 0 : DeleteNodeTransaction::MaybeCreate(*this, *priorNode);
4339 0 : if (NS_WARN_IF(!deleteNodeTransaction)) {
4340 : return nullptr;
4341 : }
4342 0 : priorNode.forget(aRemovingNode);
4343 0 : return deleteNodeTransaction.forget();
4344 : }
4345 :
4346 0 : if (aAction == eNext && isLast) {
4347 : // we're deleting from the end of the node. Delete the first thing to our
4348 : // right
4349 0 : nsCOMPtr<nsIContent> nextNode = GetNextEditableNode(*node);
4350 0 : if (NS_WARN_IF(!nextNode)) {
4351 : return nullptr;
4352 : }
4353 :
4354 : // there is a nextNode, so delete its first child (if chardata, delete the
4355 : // first char). if it has no children, delete it
4356 0 : if (RefPtr<CharacterData> nextNodeAsCharData =
4357 0 : CharacterData::FromNode(nextNode)) {
4358 0 : uint32_t length = nextNode->Length();
4359 : // Bail out for empty chardata XXX: Do we want to do something else?
4360 0 : if (NS_WARN_IF(!length)) {
4361 : return nullptr;
4362 : }
4363 : RefPtr<DeleteTextTransaction> deleteTextTransaction =
4364 0 : DeleteTextTransaction::MaybeCreateForNextCharacter(
4365 0 : *this, *nextNodeAsCharData, 0);
4366 0 : if (NS_WARN_IF(!deleteTextTransaction)) {
4367 : return nullptr;
4368 : }
4369 0 : *aOffset = deleteTextTransaction->Offset();
4370 0 : *aLength = deleteTextTransaction->LengthToDelete();
4371 0 : nextNode.forget(aRemovingNode);
4372 0 : return deleteTextTransaction.forget();
4373 : }
4374 :
4375 : // nextNode is not chardata, so tell its parent to delete it
4376 : RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
4377 0 : DeleteNodeTransaction::MaybeCreate(*this, *nextNode);
4378 0 : if (NS_WARN_IF(!deleteNodeTransaction)) {
4379 : return nullptr;
4380 : }
4381 0 : nextNode.forget(aRemovingNode);
4382 0 : return deleteNodeTransaction.forget();
4383 : }
4384 :
4385 0 : if (RefPtr<CharacterData> nodeAsCharData = CharacterData::FromNode(node)) {
4386 0 : if (NS_WARN_IF(aAction != ePrevious && aAction != eNext)) {
4387 : return nullptr;
4388 : }
4389 : // We have chardata, so delete a char at the proper offset
4390 : RefPtr<DeleteTextTransaction> deleteTextTransaction =
4391 0 : aAction == ePrevious ?
4392 : DeleteTextTransaction::MaybeCreateForPreviousCharacter(
4393 : *this, *nodeAsCharData, offset) :
4394 : DeleteTextTransaction::MaybeCreateForNextCharacter(
4395 0 : *this, *nodeAsCharData, offset);
4396 0 : if (NS_WARN_IF(!deleteTextTransaction)) {
4397 : return nullptr;
4398 : }
4399 0 : *aOffset = deleteTextTransaction->Offset();
4400 0 : *aLength = deleteTextTransaction->LengthToDelete();
4401 0 : node.forget(aRemovingNode);
4402 0 : return deleteTextTransaction.forget();
4403 : }
4404 :
4405 : // we're either deleting a node or chardata, need to dig into the next/prev
4406 : // node to find out
4407 0 : nsCOMPtr<nsINode> selectedNode;
4408 0 : if (aAction == ePrevious) {
4409 : selectedNode =
4410 0 : GetPreviousEditableNode(EditorRawDOMPoint(node, child, offset));
4411 0 : } else if (aAction == eNext) {
4412 0 : selectedNode = GetNextEditableNode(EditorRawDOMPoint(node, child, offset));
4413 : }
4414 :
4415 0 : while (selectedNode &&
4416 0 : selectedNode->IsCharacterData() &&
4417 0 : !selectedNode->Length()) {
4418 : // Can't delete an empty chardata node (bug 762183)
4419 0 : if (aAction == ePrevious) {
4420 0 : selectedNode = GetPreviousEditableNode(*selectedNode);
4421 0 : } else if (aAction == eNext) {
4422 0 : selectedNode = GetNextEditableNode(*selectedNode);
4423 : }
4424 : }
4425 :
4426 0 : if (NS_WARN_IF(!selectedNode)) {
4427 : return nullptr;
4428 : }
4429 :
4430 0 : if (RefPtr<CharacterData> selectedNodeAsCharData =
4431 0 : CharacterData::FromNode(selectedNode)) {
4432 0 : if (NS_WARN_IF(aAction != ePrevious && aAction != eNext)) {
4433 : return nullptr;
4434 : }
4435 : // we are deleting from a chardata node, so do a character deletion
4436 0 : uint32_t position = 0;
4437 0 : if (aAction == ePrevious) {
4438 0 : position = selectedNode->Length();
4439 : }
4440 : RefPtr<DeleteTextTransaction> deleteTextTransaction =
4441 0 : aAction == ePrevious ?
4442 : DeleteTextTransaction::MaybeCreateForPreviousCharacter(
4443 : *this, *selectedNodeAsCharData, position) :
4444 : DeleteTextTransaction::MaybeCreateForNextCharacter(
4445 0 : *this, *selectedNodeAsCharData, position);
4446 0 : if (NS_WARN_IF(!deleteTextTransaction)) {
4447 : return nullptr;
4448 : }
4449 0 : *aOffset = deleteTextTransaction->Offset();
4450 0 : *aLength = deleteTextTransaction->LengthToDelete();
4451 0 : selectedNode.forget(aRemovingNode);
4452 0 : return deleteTextTransaction.forget();
4453 : }
4454 :
4455 : RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
4456 0 : DeleteNodeTransaction::MaybeCreate(*this, *selectedNode);
4457 0 : if (NS_WARN_IF(!deleteNodeTransaction)) {
4458 : return nullptr;
4459 : }
4460 0 : selectedNode.forget(aRemovingNode);
4461 0 : return deleteNodeTransaction.forget();
4462 : }
4463 :
4464 : nsresult
4465 0 : EditorBase::CreateRange(nsINode* aStartContainer,
4466 : int32_t aStartOffset,
4467 : nsINode* aEndContainer,
4468 : int32_t aEndOffset,
4469 : nsRange** aRange)
4470 : {
4471 0 : return nsRange::CreateRange(aStartContainer, aStartOffset,
4472 0 : aEndContainer, aEndOffset, aRange);
4473 : }
4474 :
4475 : nsresult
4476 0 : EditorBase::AppendNodeToSelectionAsRange(nsINode* aNode)
4477 : {
4478 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
4479 0 : RefPtr<Selection> selection = GetSelection();
4480 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
4481 :
4482 0 : nsCOMPtr<nsINode> parentNode = aNode->GetParentNode();
4483 0 : NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
4484 :
4485 0 : int32_t offset = GetChildOffset(aNode, parentNode);
4486 :
4487 0 : RefPtr<nsRange> range;
4488 0 : nsresult rv = CreateRange(parentNode, offset, parentNode, offset + 1,
4489 0 : getter_AddRefs(range));
4490 0 : NS_ENSURE_SUCCESS(rv, rv);
4491 0 : NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
4492 :
4493 0 : ErrorResult err;
4494 0 : selection->AddRange(*range, err);
4495 0 : return err.StealNSResult();
4496 : }
4497 :
4498 : nsresult
4499 0 : EditorBase::ClearSelection()
4500 : {
4501 0 : RefPtr<Selection> selection = GetSelection();
4502 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
4503 0 : ErrorResult rv;
4504 0 : selection->RemoveAllRanges(rv);
4505 0 : return rv.StealNSResult();
4506 : }
4507 :
4508 : already_AddRefed<Element>
4509 0 : EditorBase::CreateHTMLContent(nsAtom* aTag)
4510 : {
4511 1 : MOZ_ASSERT(aTag);
4512 :
4513 0 : nsCOMPtr<nsIDocument> doc = GetDocument();
4514 1 : if (!doc) {
4515 : return nullptr;
4516 : }
4517 :
4518 : // XXX Wallpaper over editor bug (editor tries to create elements with an
4519 : // empty nodename).
4520 1 : if (aTag == nsGkAtoms::_empty) {
4521 0 : NS_ERROR("Don't pass an empty tag to EditorBase::CreateHTMLContent, "
4522 : "check caller.");
4523 : return nullptr;
4524 : }
4525 :
4526 1 : return doc->CreateElem(nsDependentAtomString(aTag), nullptr,
4527 1 : kNameSpaceID_XHTML);
4528 : }
4529 :
4530 : // static
4531 : already_AddRefed<nsTextNode>
4532 0 : EditorBase::CreateTextNode(nsIDocument& aDocument,
4533 : const nsAString& aData)
4534 : {
4535 0 : RefPtr<nsTextNode> text = aDocument.CreateEmptyTextNode();
4536 0 : text->MarkAsMaybeModifiedFrequently();
4537 : // Don't notify; this node is still being created.
4538 0 : text->SetText(aData, false);
4539 0 : return text.forget();
4540 : }
4541 :
4542 : NS_IMETHODIMP
4543 0 : EditorBase::SetAttributeOrEquivalent(Element* aElement,
4544 : const nsAString& aAttribute,
4545 : const nsAString& aValue,
4546 : bool aSuppressTransaction)
4547 : {
4548 0 : if (NS_WARN_IF(!aElement)) {
4549 : return NS_ERROR_NULL_POINTER;
4550 : }
4551 0 : RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
4552 0 : return SetAttributeOrEquivalent(aElement, attribute, aValue,
4553 0 : aSuppressTransaction);
4554 : }
4555 :
4556 : NS_IMETHODIMP
4557 0 : EditorBase::RemoveAttributeOrEquivalent(Element* aElement,
4558 : const nsAString& aAttribute,
4559 : bool aSuppressTransaction)
4560 : {
4561 0 : if (NS_WARN_IF(!aElement)) {
4562 : return NS_ERROR_NULL_POINTER;
4563 : }
4564 0 : RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
4565 0 : return RemoveAttributeOrEquivalent(aElement, attribute, aSuppressTransaction);
4566 : }
4567 :
4568 : nsresult
4569 0 : EditorBase::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent)
4570 : {
4571 : // NOTE: When you change this method, you should also change:
4572 : // * editor/libeditor/tests/test_texteditor_keyevent_handling.html
4573 : // * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
4574 : //
4575 : // And also when you add new key handling, you need to change the subclass's
4576 : // HandleKeyPressEvent()'s switch statement.
4577 :
4578 0 : if (NS_WARN_IF(!aKeyboardEvent)) {
4579 : return NS_ERROR_UNEXPECTED;
4580 : }
4581 0 : MOZ_ASSERT(aKeyboardEvent->mMessage == eKeyPress,
4582 : "HandleKeyPressEvent gets non-keypress event");
4583 :
4584 : // if we are readonly or disabled, then do nothing.
4585 0 : if (IsReadonly() || IsDisabled()) {
4586 : // consume backspace for disabled and readonly textfields, to prevent
4587 : // back in history, which could be confusing to users
4588 0 : if (aKeyboardEvent->mKeyCode == NS_VK_BACK) {
4589 0 : aKeyboardEvent->PreventDefault();
4590 : }
4591 : return NS_OK;
4592 : }
4593 :
4594 0 : switch (aKeyboardEvent->mKeyCode) {
4595 : case NS_VK_META:
4596 : case NS_VK_WIN:
4597 : case NS_VK_SHIFT:
4598 : case NS_VK_CONTROL:
4599 : case NS_VK_ALT:
4600 0 : aKeyboardEvent->PreventDefault(); // consumed
4601 0 : return NS_OK;
4602 : }
4603 : return NS_OK;
4604 : }
4605 :
4606 : nsresult
4607 1 : EditorBase::HandleInlineSpellCheck(EditSubAction aEditSubAction,
4608 : Selection& aSelection,
4609 : nsINode* previousSelectedNode,
4610 : uint32_t previousSelectedOffset,
4611 : nsINode* aStartContainer,
4612 : uint32_t aStartOffset,
4613 : nsINode* aEndContainer,
4614 : uint32_t aEndOffset)
4615 : {
4616 2 : if (!mInlineSpellChecker) {
4617 : return NS_OK;
4618 : }
4619 0 : return mInlineSpellChecker->SpellCheckAfterEditorChange(
4620 : aEditSubAction, aSelection,
4621 : previousSelectedNode, previousSelectedOffset,
4622 : aStartContainer, aStartOffset, aEndContainer,
4623 0 : aEndOffset);
4624 : }
4625 :
4626 : already_AddRefed<nsIContent>
4627 0 : EditorBase::FindSelectionRoot(nsINode* aNode)
4628 : {
4629 4 : nsCOMPtr<nsIContent> rootContent = GetRoot();
4630 4 : return rootContent.forget();
4631 : }
4632 :
4633 : void
4634 1 : EditorBase::InitializeSelectionAncestorLimit(Selection& aSelection,
4635 : nsIContent& aAncestorLimit)
4636 : {
4637 0 : aSelection.SetAncestorLimiter(&aAncestorLimit);
4638 0 : }
4639 :
4640 : nsresult
4641 0 : EditorBase::InitializeSelection(EventTarget* aFocusEventTarget)
4642 : {
4643 2 : nsCOMPtr<nsINode> targetNode = do_QueryInterface(aFocusEventTarget);
4644 1 : NS_ENSURE_TRUE(targetNode, NS_ERROR_INVALID_ARG);
4645 0 : nsCOMPtr<nsIContent> selectionRootContent = FindSelectionRoot(targetNode);
4646 0 : if (!selectionRootContent) {
4647 : return NS_OK;
4648 : }
4649 :
4650 0 : RefPtr<Selection> selection = GetSelection();
4651 1 : NS_ENSURE_STATE(selection);
4652 :
4653 2 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
4654 1 : NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
4655 :
4656 : nsCOMPtr<nsISelectionController> selectionController =
4657 0 : GetSelectionController();
4658 0 : if (NS_WARN_IF(!selectionController)) {
4659 : return NS_ERROR_FAILURE;
4660 : }
4661 :
4662 : // Init the caret
4663 3 : RefPtr<nsCaret> caret = presShell->GetCaret();
4664 1 : NS_ENSURE_TRUE(caret, NS_ERROR_UNEXPECTED);
4665 1 : caret->SetIgnoreUserModify(false);
4666 0 : caret->SetSelection(selection);
4667 2 : selectionController->SetCaretReadOnly(IsReadonly());
4668 1 : selectionController->SetCaretEnabled(true);
4669 :
4670 : // Init selection
4671 1 : selectionController->SetDisplaySelection(
4672 1 : nsISelectionController::SELECTION_ON);
4673 0 : selectionController->SetSelectionFlags(
4674 1 : nsISelectionDisplay::DISPLAY_ALL);
4675 0 : selectionController->RepaintSelection(
4676 0 : nsISelectionController::SELECTION_NORMAL);
4677 :
4678 : // If the computed selection root isn't root content, we should set it
4679 : // as selection ancestor limit. However, if that is root element, it means
4680 : // there is not limitation of the selection, then, we must set nullptr.
4681 : // NOTE: If we set a root element to the ancestor limit, some selection
4682 : // methods don't work fine.
4683 1 : if (selectionRootContent->GetParent()) {
4684 1 : InitializeSelectionAncestorLimit(*selection, *selectionRootContent);
4685 : } else {
4686 0 : selection->SetAncestorLimiter(nullptr);
4687 : }
4688 :
4689 : // If there is composition when this is called, we may need to restore IME
4690 : // selection because if the editor is reframed, this already forgot IME
4691 : // selection and the transaction.
4692 2 : if (mComposition && mComposition->IsMovingToNewTextNode()) {
4693 : // We need to look for the new text node from current selection.
4694 : // XXX If selection is changed during reframe, this doesn't work well!
4695 0 : nsRange* firstRange = selection->GetRangeAt(0);
4696 0 : if (NS_WARN_IF(!firstRange)) {
4697 0 : return NS_ERROR_FAILURE;
4698 : }
4699 0 : EditorRawDOMPoint atStartOfFirstRange(firstRange->StartRef());
4700 : EditorRawDOMPoint betterInsertionPoint =
4701 0 : FindBetterInsertionPoint(atStartOfFirstRange);
4702 0 : Text* textNode = betterInsertionPoint.GetContainerAsText();
4703 0 : MOZ_ASSERT(textNode,
4704 : "There must be text node if composition string is not empty");
4705 0 : if (textNode) {
4706 0 : MOZ_ASSERT(textNode->Length() >= mComposition->XPEndOffsetInTextNode(),
4707 : "The text node must be different from the old text node");
4708 0 : CompositionTransaction::SetIMESelection(
4709 : *this, textNode,
4710 : mComposition->XPOffsetInTextNode(),
4711 : mComposition->XPLengthInTextNode(),
4712 0 : mComposition->GetRanges());
4713 : }
4714 : }
4715 :
4716 : return NS_OK;
4717 : }
4718 :
4719 0 : class RepaintSelectionRunner final : public Runnable {
4720 : public:
4721 0 : explicit RepaintSelectionRunner(nsISelectionController* aSelectionController)
4722 0 : : Runnable("RepaintSelectionRunner")
4723 0 : , mSelectionController(aSelectionController)
4724 : {
4725 0 : }
4726 :
4727 0 : NS_IMETHOD Run() override
4728 : {
4729 0 : nsCOMPtr<nsIPresShell> shell = do_QueryInterface(mSelectionController);
4730 0 : if (!shell || shell->IsDestroying()) {
4731 : return NS_OK;
4732 : }
4733 :
4734 0 : mSelectionController->RepaintSelection(
4735 0 : nsISelectionController::SELECTION_NORMAL);
4736 0 : return NS_OK;
4737 : }
4738 :
4739 : private:
4740 : nsCOMPtr<nsISelectionController> mSelectionController;
4741 : };
4742 :
4743 : nsresult
4744 0 : EditorBase::FinalizeSelection()
4745 : {
4746 : nsCOMPtr<nsISelectionController> selectionController =
4747 0 : GetSelectionController();
4748 0 : if (NS_WARN_IF(!selectionController)) {
4749 : return NS_ERROR_FAILURE;
4750 : }
4751 :
4752 0 : RefPtr<Selection> selection = GetSelection();
4753 0 : NS_ENSURE_STATE(selection);
4754 :
4755 0 : selection->SetAncestorLimiter(nullptr);
4756 :
4757 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
4758 0 : NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
4759 :
4760 0 : selectionController->SetCaretEnabled(false);
4761 :
4762 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
4763 0 : NS_ENSURE_TRUE(fm, NS_ERROR_NOT_INITIALIZED);
4764 0 : fm->UpdateCaretForCaretBrowsingMode();
4765 :
4766 0 : if (!HasIndependentSelection()) {
4767 : // If this editor doesn't have an independent selection, i.e., it must
4768 : // mean that it is an HTML editor, the selection controller is shared with
4769 : // presShell. So, even this editor loses focus, other part of the document
4770 : // may still have focus.
4771 0 : nsCOMPtr<nsIDocument> doc = GetDocument();
4772 0 : ErrorResult ret;
4773 0 : if (!doc || !doc->HasFocus(ret)) {
4774 : // If the document already lost focus, mark the selection as disabled.
4775 0 : selectionController->SetDisplaySelection(
4776 0 : nsISelectionController::SELECTION_DISABLED);
4777 : } else {
4778 : // Otherwise, mark selection as normal because outside of a
4779 : // contenteditable element should be selected with normal selection
4780 : // color after here.
4781 0 : selectionController->SetDisplaySelection(
4782 0 : nsISelectionController::SELECTION_ON);
4783 : }
4784 0 : } else if (IsFormWidget() || IsPasswordEditor() ||
4785 0 : IsReadonly() || IsDisabled() || IsInputFiltered()) {
4786 : // In <input> or <textarea>, the independent selection should be hidden
4787 : // while this editor doesn't have focus.
4788 0 : selectionController->SetDisplaySelection(
4789 0 : nsISelectionController::SELECTION_HIDDEN);
4790 : } else {
4791 : // Otherwise, although we're not sure how this case happens, the
4792 : // independent selection should be marked as disabled.
4793 0 : selectionController->SetDisplaySelection(
4794 0 : nsISelectionController::SELECTION_DISABLED);
4795 : }
4796 :
4797 : // FinalizeSelection might be called from ContentRemoved even if selection
4798 : // isn't updated. So we need to call RepaintSelection after updated it.
4799 : nsContentUtils::AddScriptRunner(
4800 0 : new RepaintSelectionRunner(selectionController));
4801 0 : return NS_OK;
4802 : }
4803 :
4804 : Element*
4805 0 : EditorBase::GetEditorRoot()
4806 : {
4807 0 : return GetRoot();
4808 : }
4809 :
4810 : Element*
4811 1 : EditorBase::GetExposedRoot()
4812 : {
4813 0 : Element* rootElement = GetRoot();
4814 :
4815 : // For plaintext editors, we need to ask the input/textarea element directly.
4816 1 : if (rootElement && rootElement->IsRootOfNativeAnonymousSubtree()) {
4817 1 : rootElement = rootElement->GetParent()->AsElement();
4818 : }
4819 :
4820 1 : return rootElement;
4821 : }
4822 :
4823 : nsresult
4824 0 : EditorBase::DetermineCurrentDirection()
4825 : {
4826 : // Get the current root direction from its frame
4827 0 : nsIContent* rootElement = GetExposedRoot();
4828 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
4829 :
4830 : // If we don't have an explicit direction, determine our direction
4831 : // from the content's direction
4832 0 : if (!IsRightToLeft() && !IsLeftToRight()) {
4833 0 : nsIFrame* frame = rootElement->GetPrimaryFrame();
4834 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
4835 :
4836 : // Set the flag here, to enable us to use the same code path below.
4837 : // It will be flipped before returning from the function.
4838 0 : if (frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
4839 0 : mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
4840 : } else {
4841 0 : mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
4842 : }
4843 : }
4844 :
4845 : return NS_OK;
4846 : }
4847 :
4848 : nsresult
4849 0 : EditorBase::ToggleTextDirection()
4850 : {
4851 : // XXX Oddly, Chrome does not dispatch beforeinput event in this case but
4852 : // dispatches input event.
4853 :
4854 0 : nsresult rv = DetermineCurrentDirection();
4855 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4856 : return rv;
4857 : }
4858 :
4859 0 : if (IsRightToLeft()) {
4860 0 : nsresult rv = SetTextDirectionTo(TextDirection::eLTR);
4861 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4862 : return rv;
4863 : }
4864 0 : } else if (IsLeftToRight()) {
4865 0 : nsresult rv = SetTextDirectionTo(TextDirection::eRTL);
4866 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4867 : return rv;
4868 : }
4869 : }
4870 :
4871 : // XXX When we don't change the text direction, do we really need to
4872 : // dispatch input event?
4873 0 : FireInputEvent();
4874 :
4875 0 : return NS_OK;
4876 : }
4877 :
4878 : void
4879 0 : EditorBase::SwitchTextDirectionTo(TextDirection aTextDirection)
4880 : {
4881 : // XXX Oddly, Chrome does not dispatch beforeinput event in this case but
4882 : // dispatches input event.
4883 :
4884 0 : nsresult rv = DetermineCurrentDirection();
4885 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4886 : return;
4887 : }
4888 :
4889 0 : if (aTextDirection == TextDirection::eLTR && IsRightToLeft()) {
4890 0 : if (NS_WARN_IF(NS_FAILED(SetTextDirectionTo(aTextDirection)))) {
4891 : return;
4892 : }
4893 0 : } else if (aTextDirection == TextDirection::eRTL && IsLeftToRight()) {
4894 0 : if (NS_WARN_IF(NS_FAILED(SetTextDirectionTo(aTextDirection)))) {
4895 : return;
4896 : }
4897 : }
4898 :
4899 : // XXX When we don't change the text direction, do we really need to
4900 : // dispatch input event?
4901 0 : FireInputEvent();
4902 : }
4903 :
4904 : nsresult
4905 0 : EditorBase::SetTextDirectionTo(TextDirection aTextDirection)
4906 : {
4907 0 : Element* rootElement = GetExposedRoot();
4908 :
4909 0 : if (aTextDirection == TextDirection::eLTR) {
4910 0 : NS_ASSERTION(!IsLeftToRight(), "Unexpected mutually exclusive flag");
4911 0 : mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft;
4912 0 : mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
4913 0 : nsresult rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
4914 0 : NS_LITERAL_STRING("ltr"), true);
4915 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4916 : return rv;
4917 : }
4918 0 : return NS_OK;
4919 : }
4920 :
4921 0 : if (aTextDirection == TextDirection::eRTL) {
4922 0 : NS_ASSERTION(!IsRightToLeft(), "Unexpected mutually exclusive flag");
4923 0 : mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
4924 0 : mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight;
4925 0 : nsresult rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
4926 0 : NS_LITERAL_STRING("rtl"), true);
4927 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4928 : return rv;
4929 : }
4930 0 : return NS_OK;
4931 : }
4932 :
4933 : return NS_OK;
4934 : }
4935 :
4936 : bool
4937 7 : EditorBase::IsModifiableNode(nsINode* aNode)
4938 : {
4939 0 : return true;
4940 : }
4941 :
4942 : nsIContent*
4943 4 : EditorBase::GetFocusedContent()
4944 : {
4945 4 : EventTarget* piTarget = GetDOMEventTarget();
4946 4 : if (!piTarget) {
4947 : return nullptr;
4948 : }
4949 :
4950 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
4951 4 : NS_ENSURE_TRUE(fm, nullptr);
4952 :
4953 8 : nsIContent* content = fm->GetFocusedElement();
4954 4 : MOZ_ASSERT((content == piTarget) == SameCOMIdentity(content, piTarget));
4955 :
4956 4 : return (content == piTarget) ? content : nullptr;
4957 : }
4958 :
4959 : already_AddRefed<nsIContent>
4960 0 : EditorBase::GetFocusedContentForIME()
4961 : {
4962 4 : nsCOMPtr<nsIContent> content = GetFocusedContent();
4963 4 : return content.forget();
4964 : }
4965 :
4966 : bool
4967 0 : EditorBase::IsActiveInDOMWindow()
4968 : {
4969 0 : EventTarget* piTarget = GetDOMEventTarget();
4970 0 : if (!piTarget) {
4971 : return false;
4972 : }
4973 :
4974 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
4975 0 : NS_ENSURE_TRUE(fm, false);
4976 :
4977 0 : nsCOMPtr<nsIDocument> document = GetDocument();
4978 0 : if (NS_WARN_IF(!document)) {
4979 : return false;
4980 : }
4981 0 : nsPIDOMWindowOuter* ourWindow = document->GetWindow();
4982 0 : nsCOMPtr<nsPIDOMWindowOuter> win;
4983 : nsIContent* content =
4984 0 : nsFocusManager::GetFocusedDescendant(ourWindow,
4985 : nsFocusManager::eOnlyCurrentWindow,
4986 0 : getter_AddRefs(win));
4987 0 : return SameCOMIdentity(content, piTarget);
4988 : }
4989 :
4990 : bool
4991 0 : EditorBase::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
4992 : {
4993 : // If the event is trusted, the event should always cause input.
4994 0 : if (NS_WARN_IF(!aGUIEvent)) {
4995 : return false;
4996 : }
4997 :
4998 : // If this is dispatched by using cordinates but this editor doesn't have
4999 : // focus, we shouldn't handle it.
5000 0 : if (aGUIEvent->IsUsingCoordinates()) {
5001 0 : nsIContent* focusedContent = GetFocusedContent();
5002 0 : if (!focusedContent) {
5003 : return false;
5004 : }
5005 : }
5006 :
5007 : // If a composition event isn't dispatched via widget, we need to ignore them
5008 : // since they cannot be managed by TextComposition. E.g., the event was
5009 : // created by chrome JS.
5010 : // Note that if we allow to handle such events, editor may be confused by
5011 : // strange event order.
5012 0 : bool needsWidget = false;
5013 0 : switch (aGUIEvent->mMessage) {
5014 : case eUnidentifiedEvent:
5015 : // If events are not created with proper event interface, their message
5016 : // are initialized with eUnidentifiedEvent. Let's ignore such event.
5017 : return false;
5018 : case eCompositionStart:
5019 : case eCompositionEnd:
5020 : case eCompositionUpdate:
5021 : case eCompositionChange:
5022 : case eCompositionCommitAsIs:
5023 : // Don't allow composition events whose internal event are not
5024 : // WidgetCompositionEvent.
5025 0 : if (!aGUIEvent->AsCompositionEvent()) {
5026 : return false;
5027 : }
5028 : needsWidget = true;
5029 : break;
5030 : default:
5031 : break;
5032 : }
5033 0 : if (needsWidget && !aGUIEvent->mWidget) {
5034 : return false;
5035 : }
5036 :
5037 : // Accept all trusted events.
5038 0 : if (aGUIEvent->IsTrusted()) {
5039 : return true;
5040 : }
5041 :
5042 : // Ignore untrusted mouse event.
5043 : // XXX Why are we handling other untrusted input events?
5044 0 : if (aGUIEvent->AsMouseEventBase()) {
5045 : return false;
5046 : }
5047 :
5048 : // Otherwise, we shouldn't handle any input events when we're not an active
5049 : // element of the DOM window.
5050 0 : return IsActiveInDOMWindow();
5051 : }
5052 :
5053 : void
5054 1 : EditorBase::OnFocus(EventTarget* aFocusEventTarget)
5055 : {
5056 1 : InitializeSelection(aFocusEventTarget);
5057 0 : mSpellCheckerDictionaryUpdated = false;
5058 0 : if (mInlineSpellChecker && CanEnableSpellCheck()) {
5059 0 : mInlineSpellChecker->UpdateCurrentDictionary();
5060 0 : mSpellCheckerDictionaryUpdated = true;
5061 : }
5062 0 : }
5063 :
5064 : int32_t
5065 0 : EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode)
5066 : {
5067 0 : MOZ_ASSERT(aTextNode, "aTextNode must not be nullptr");
5068 :
5069 0 : nsISelectionController* selectionController = GetSelectionController();
5070 0 : if (NS_WARN_IF(!selectionController)) {
5071 : return -1;
5072 : }
5073 :
5074 : uint32_t minOffset = UINT32_MAX;
5075 : static const SelectionType kIMESelectionTypes[] = {
5076 : SelectionType::eIMERawClause,
5077 : SelectionType::eIMESelectedRawClause,
5078 : SelectionType::eIMEConvertedClause,
5079 : SelectionType::eIMESelectedClause
5080 : };
5081 : for (auto selectionType : kIMESelectionTypes) {
5082 : RefPtr<Selection> selection = GetSelection(selectionType);
5083 : if (!selection) {
5084 : continue;
5085 : }
5086 : for (uint32_t i = 0; i < selection->RangeCount(); i++) {
5087 : RefPtr<nsRange> range = selection->GetRangeAt(i);
5088 : if (NS_WARN_IF(!range)) {
5089 : continue;
5090 : }
5091 : if (NS_WARN_IF(range->GetStartContainer() != aTextNode)) {
5092 : // ignore the start offset...
5093 : } else {
5094 : minOffset = std::min(minOffset, range->StartOffset());
5095 : }
5096 : if (NS_WARN_IF(range->GetEndContainer() != aTextNode)) {
5097 : // ignore the end offset...
5098 : } else {
5099 : minOffset = std::min(minOffset, range->EndOffset());
5100 : }
5101 : }
5102 : }
5103 : return minOffset < INT32_MAX ? minOffset : -1;
5104 : }
5105 :
5106 : void
5107 : EditorBase::HideCaret(bool aHide)
5108 : {
5109 : if (mHidingCaret == aHide) {
5110 : return;
5111 : }
5112 :
5113 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5114 : NS_ENSURE_TRUE_VOID(presShell);
5115 : RefPtr<nsCaret> caret = presShell->GetCaret();
5116 : NS_ENSURE_TRUE_VOID(caret);
5117 :
5118 : mHidingCaret = aHide;
5119 : if (aHide) {
5120 : caret->AddForceHide();
5121 : } else {
5122 : caret->RemoveForceHide();
5123 : }
5124 : }
5125 :
5126 : } // namespace mozilla
|