Coverage Report

Created: 2018-09-25 14:53

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