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