Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/editor/libeditor/TextEditRules.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/TextEditRules.h"
7
8
#include "HTMLEditRules.h"
9
#include "TextEditUtils.h"
10
#include "mozilla/Assertions.h"
11
#include "mozilla/EditAction.h"
12
#include "mozilla/EditorDOMPoint.h"
13
#include "mozilla/EditorUtils.h"
14
#include "mozilla/LookAndFeel.h"
15
#include "mozilla/Preferences.h"
16
#include "mozilla/TextComposition.h"
17
#include "mozilla/TextEditor.h"
18
#include "mozilla/dom/Element.h"
19
#include "mozilla/dom/NodeFilterBinding.h"
20
#include "mozilla/dom/NodeIterator.h"
21
#include "mozilla/dom/Selection.h"
22
#include "nsAString.h"
23
#include "nsCOMPtr.h"
24
#include "nsCRT.h"
25
#include "nsCRTGlue.h"
26
#include "nsComponentManagerUtils.h"
27
#include "nsContentUtils.h"
28
#include "nsDebug.h"
29
#include "nsError.h"
30
#include "nsGkAtoms.h"
31
#include "nsIContent.h"
32
#include "nsIDocumentEncoder.h"
33
#include "nsNameSpaceManager.h"
34
#include "nsINode.h"
35
#include "nsIPlaintextEditor.h"
36
#include "nsISupportsBase.h"
37
#include "nsLiteralString.h"
38
#include "nsTextNode.h"
39
#include "nsUnicharUtils.h"
40
#include "nsIHTMLCollection.h"
41
#include "nsPrintfCString.h"
42
43
namespace mozilla {
44
45
using namespace dom;
46
47
template CreateElementResult
48
TextEditRules::CreateBRInternal(const EditorDOMPoint& aPointToInsert,
49
                                bool aCreateMozBR);
50
template CreateElementResult
51
TextEditRules::CreateBRInternal(const EditorRawDOMPoint& aPointToInsert,
52
                                bool aCreateMozBR);
53
54
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
55
0
  if (IsReadonly() || IsDisabled()) \
56
0
  {                     \
57
0
    *aCancel = true; \
58
0
    return NS_OK;       \
59
0
  };
60
61
/********************************************************
62
 * mozilla::TextEditRules
63
 ********************************************************/
64
65
TextEditRules::TextEditRules()
66
  : mTextEditor(nullptr)
67
  , mData(nullptr)
68
  , mPasswordIMEIndex(0)
69
  , mCachedSelectionOffset(0)
70
  , mActionNesting(0)
71
  , mLockRulesSniffing(false)
72
  , mDidExplicitlySetInterline(false)
73
  , mDeleteBidiImmediately(false)
74
  , mIsHTMLEditRules(false)
75
  , mTopLevelEditSubAction(EditSubAction::eNone)
76
  , mLastStart(0)
77
  , mLastLength(0)
78
0
{
79
0
  InitFields();
80
0
}
81
82
void
83
TextEditRules::InitFields()
84
0
{
85
0
  mTextEditor = nullptr;
86
0
  mPasswordText.Truncate();
87
0
  mPasswordIMEText.Truncate();
88
0
  mPasswordIMEIndex = 0;
89
0
  mBogusNode = nullptr;
90
0
  mCachedSelectionNode = nullptr;
91
0
  mCachedSelectionOffset = 0;
92
0
  mActionNesting = 0;
93
0
  mLockRulesSniffing = false;
94
0
  mDidExplicitlySetInterline = false;
95
0
  mDeleteBidiImmediately = false;
96
0
  mTopLevelEditSubAction = EditSubAction::eNone;
97
0
  mTimer = nullptr;
98
0
  mLastStart = 0;
99
0
  mLastLength = 0;
100
0
}
101
102
TextEditRules::~TextEditRules()
103
0
{
104
0
   // do NOT delete mTextEditor here.  We do not hold a ref count to
105
0
   // mTextEditor.  mTextEditor owns our lifespan.
106
0
107
0
  if (mTimer) {
108
0
    mTimer->Cancel();
109
0
  }
110
0
}
111
112
HTMLEditRules*
113
TextEditRules::AsHTMLEditRules()
114
0
{
115
0
  return mIsHTMLEditRules ? static_cast<HTMLEditRules*>(this) : nullptr;
116
0
}
117
118
const HTMLEditRules*
119
TextEditRules::AsHTMLEditRules() const
120
0
{
121
0
  return mIsHTMLEditRules ? static_cast<const HTMLEditRules*>(this) : nullptr;
122
0
}
123
124
NS_IMPL_CYCLE_COLLECTION(TextEditRules, mBogusNode, mCachedSelectionNode)
125
126
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditRules)
127
0
  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
128
0
  NS_INTERFACE_MAP_ENTRY(nsINamed)
129
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
130
0
NS_INTERFACE_MAP_END
131
132
NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
133
NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
134
135
nsresult
136
TextEditRules::Init(TextEditor* aTextEditor)
137
0
{
138
0
  if (NS_WARN_IF(!aTextEditor)) {
139
0
    return NS_ERROR_INVALID_ARG;
140
0
  }
141
0
142
0
  Selection* selection = aTextEditor->GetSelection();
143
0
  if (NS_WARN_IF(!selection)) {
144
0
    return NS_ERROR_FAILURE;
145
0
  }
146
0
147
0
  InitFields();
148
0
149
0
  // We hold a non-refcounted reference back to our editor.
150
0
  mTextEditor = aTextEditor;
151
0
  AutoSafeEditorData setData(*this, *mTextEditor, *selection);
152
0
153
0
  // Put in a magic <br> if needed. This method handles null selection,
154
0
  // which should never happen anyway
155
0
  nsresult rv = CreateBogusNodeIfNeeded();
156
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
157
0
    return rv;
158
0
  }
159
0
160
0
  // If the selection hasn't been set up yet, set it up collapsed to the end of
161
0
  // our editable content.
162
0
  if (!SelectionRef().RangeCount()) {
163
0
    rv = TextEditorRef().CollapseSelectionToEnd(&SelectionRef());
164
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
165
0
      return rv;
166
0
    }
167
0
  }
168
0
169
0
  if (IsPlaintextEditor()) {
170
0
    // ensure trailing br node
171
0
    rv = CreateTrailingBRIfNeeded();
172
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
173
0
      return rv;
174
0
    }
175
0
  }
176
0
177
0
  // XXX We should use AddBoolVarCache and use "current" value at initializing.
178
0
  mDeleteBidiImmediately =
179
0
    Preferences::GetBool("bidi.edit.delete_immediately", false);
180
0
181
0
  return NS_OK;
182
0
}
183
184
nsresult
185
TextEditRules::SetInitialValue(const nsAString& aValue)
186
0
{
187
0
  if (IsPasswordEditor()) {
188
0
    mPasswordText = aValue;
189
0
  }
190
0
  return NS_OK;
191
0
}
192
193
nsresult
194
TextEditRules::DetachEditor()
195
0
{
196
0
  if (mTimer) {
197
0
    mTimer->Cancel();
198
0
  }
199
0
  mTextEditor = nullptr;
200
0
  return NS_OK;
201
0
}
202
203
nsresult
204
TextEditRules::BeforeEdit(EditSubAction aEditSubAction,
205
                          nsIEditor::EDirection aDirection)
206
0
{
207
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
208
0
    return NS_ERROR_EDITOR_DESTROYED;
209
0
  }
210
0
211
0
  if (mLockRulesSniffing) {
212
0
    return NS_OK;
213
0
  }
214
0
215
0
  AutoLockRulesSniffing lockIt(this);
216
0
  mDidExplicitlySetInterline = false;
217
0
  if (!mActionNesting) {
218
0
    // let rules remember the top level action
219
0
    mTopLevelEditSubAction = aEditSubAction;
220
0
  }
221
0
  mActionNesting++;
222
0
223
0
  if (aEditSubAction == EditSubAction::eSetText) {
224
0
    // setText replaces all text, so mCachedSelectionNode might be invalid on
225
0
    // AfterEdit.
226
0
    // Since this will be used as start position of spellchecker, we should
227
0
    // use root instead.
228
0
    mCachedSelectionNode = mTextEditor->GetRoot();
229
0
    mCachedSelectionOffset = 0;
230
0
  } else {
231
0
    Selection* selection = mTextEditor->GetSelection();
232
0
    if (NS_WARN_IF(!selection)) {
233
0
      return NS_ERROR_FAILURE;
234
0
    }
235
0
    mCachedSelectionNode = selection->GetAnchorNode();
236
0
    mCachedSelectionOffset = selection->AnchorOffset();
237
0
  }
238
0
239
0
  return NS_OK;
240
0
}
241
242
nsresult
243
TextEditRules::AfterEdit(EditSubAction aEditSubAction,
244
                         nsIEditor::EDirection aDirection)
245
0
{
246
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
247
0
    return NS_ERROR_EDITOR_DESTROYED;
248
0
  }
249
0
250
0
  if (mLockRulesSniffing) {
251
0
    return NS_OK;
252
0
  }
253
0
254
0
  AutoLockRulesSniffing lockIt(this);
255
0
256
0
  MOZ_ASSERT(mActionNesting>0, "bad action nesting!");
257
0
  if (!--mActionNesting) {
258
0
    Selection* selection = mTextEditor->GetSelection();
259
0
    if (NS_WARN_IF(!selection)) {
260
0
      return NS_ERROR_FAILURE;
261
0
    }
262
0
263
0
    AutoSafeEditorData setData(*this, *mTextEditor, *selection);
264
0
265
0
    nsresult rv =
266
0
      TextEditorRef().HandleInlineSpellCheck(aEditSubAction, *selection,
267
0
                                             mCachedSelectionNode,
268
0
                                             mCachedSelectionOffset,
269
0
                                             nullptr, 0, nullptr, 0);
270
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
271
0
      return rv;
272
0
    }
273
0
274
0
    // no longer uses mCachedSelectionNode, so release it.
275
0
    mCachedSelectionNode = nullptr;
276
0
277
0
    // if only trailing <br> remaining remove it
278
0
    rv = RemoveRedundantTrailingBR();
279
0
    if (NS_FAILED(rv)) {
280
0
      return rv;
281
0
    }
282
0
283
0
    // detect empty doc
284
0
    rv = CreateBogusNodeIfNeeded();
285
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
286
0
      return rv;
287
0
    }
288
0
289
0
    // ensure trailing br node
290
0
    rv = CreateTrailingBRIfNeeded();
291
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
292
0
      return rv;
293
0
    }
294
0
295
0
    // Collapse the selection to the trailing moz-<br> if it's at the end of
296
0
    // our text node.
297
0
    rv = CollapseSelectionToTrailingBRIfNeeded();
298
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
299
0
      return NS_ERROR_EDITOR_DESTROYED;
300
0
    }
301
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
302
0
      "Failed to selection to after the text node in TextEditor");
303
0
  }
304
0
  return NS_OK;
305
0
}
306
307
nsresult
308
TextEditRules::WillDoAction(Selection* aSelection,
309
                            EditSubActionInfo& aInfo,
310
                            bool* aCancel,
311
                            bool* aHandled)
312
0
{
313
0
  if (NS_WARN_IF(!aSelection)) {
314
0
    return NS_ERROR_INVALID_ARG;
315
0
  }
316
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
317
0
    return NS_ERROR_EDITOR_DESTROYED;
318
0
  }
319
0
320
0
  MOZ_ASSERT(aCancel);
321
0
  MOZ_ASSERT(aHandled);
322
0
323
0
  *aCancel = false;
324
0
  *aHandled = false;
325
0
326
0
  AutoSafeEditorData setData(*this, *mTextEditor, *aSelection);
327
0
328
0
  // my kingdom for dynamic cast
329
0
  switch (aInfo.mEditSubAction) {
330
0
    case EditSubAction::eInsertParagraphSeparator:
331
0
      UndefineCaretBidiLevel();
332
0
      return WillInsertBreak(aCancel, aHandled, aInfo.maxLength);
333
0
    case EditSubAction::eInsertText:
334
0
    case EditSubAction::eInsertTextComingFromIME:
335
0
      UndefineCaretBidiLevel();
336
0
      return WillInsertText(aInfo.mEditSubAction, aCancel, aHandled,
337
0
                            aInfo.inString, aInfo.outString,
338
0
                            aInfo.maxLength);
339
0
    case EditSubAction::eSetText:
340
0
      UndefineCaretBidiLevel();
341
0
      return WillSetText(aCancel, aHandled, aInfo.inString,
342
0
                         aInfo.maxLength);
343
0
    case EditSubAction::eDeleteSelectedContent:
344
0
      return WillDeleteSelection(aInfo.collapsedAction, aCancel, aHandled);
345
0
    case EditSubAction::eUndo:
346
0
      return WillUndo(aCancel, aHandled);
347
0
    case EditSubAction::eRedo:
348
0
      return WillRedo(aCancel, aHandled);
349
0
    case EditSubAction::eSetTextProperty:
350
0
      return WillSetTextProperty(aCancel, aHandled);
351
0
    case EditSubAction::eRemoveTextProperty:
352
0
      return WillRemoveTextProperty(aCancel, aHandled);
353
0
    case EditSubAction::eComputeTextToOutput:
354
0
      return WillOutputText(aInfo.outputFormat, aInfo.outString, aInfo.flags,
355
0
                            aCancel, aHandled);
356
0
    case EditSubAction::eInsertElement:
357
0
      // i had thought this would be html rules only.  but we put pre elements
358
0
      // into plaintext mail when doing quoting for reply!  doh!
359
0
      return WillInsert(aCancel);
360
0
    default:
361
0
      return NS_ERROR_FAILURE;
362
0
  }
363
0
}
364
365
nsresult
366
TextEditRules::DidDoAction(Selection* aSelection,
367
                           EditSubActionInfo& aInfo,
368
                           nsresult aResult)
369
0
{
370
0
  if (NS_WARN_IF(!aSelection)) {
371
0
    return NS_ERROR_INVALID_ARG;
372
0
  }
373
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
374
0
    return NS_ERROR_EDITOR_DESTROYED;
375
0
  }
376
0
377
0
  AutoSafeEditorData setData(*this, *mTextEditor, *aSelection);
378
0
379
0
  // don't let any txns in here move the selection around behind our back.
380
0
  // Note that this won't prevent explicit selection setting from working.
381
0
  AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
382
0
383
0
  switch (aInfo.mEditSubAction) {
384
0
    case EditSubAction::eDeleteSelectedContent:
385
0
      return DidDeleteSelection();
386
0
    case EditSubAction::eUndo:
387
0
      return DidUndo(aResult);
388
0
    case EditSubAction::eRedo:
389
0
      return DidRedo(aResult);
390
0
    default:
391
0
      // Don't fail on transactions we don't handle here!
392
0
      return NS_OK;
393
0
  }
394
0
}
395
396
bool
397
TextEditRules::DocumentIsEmpty()
398
0
{
399
0
  bool retVal = false;
400
0
  if (!mTextEditor || NS_FAILED(mTextEditor->IsEmpty(&retVal))) {
401
0
    retVal = true;
402
0
  }
403
0
404
0
  return retVal;
405
0
}
406
407
nsresult
408
TextEditRules::WillInsert(bool* aCancel)
409
0
{
410
0
  MOZ_ASSERT(IsEditorDataAvailable());
411
0
412
0
  if (IsReadonly() || IsDisabled()) {
413
0
    if (aCancel) {
414
0
      *aCancel = true;
415
0
    }
416
0
    return NS_OK;
417
0
  }
418
0
419
0
  // initialize out param
420
0
  if (aCancel) {
421
0
    *aCancel = false;
422
0
  }
423
0
424
0
  // check for the magic content node and delete it if it exists
425
0
  if (!mBogusNode) {
426
0
    return NS_OK;
427
0
  }
428
0
429
0
  DebugOnly<nsresult> rv =
430
0
    TextEditorRef().DeleteNodeWithTransaction(*mBogusNode);
431
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
432
0
    return NS_ERROR_EDITOR_DESTROYED;
433
0
  }
434
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
435
0
    "Failed to remove the bogus node");
436
0
  mBogusNode = nullptr;
437
0
  return NS_OK;
438
0
}
439
440
nsresult
441
TextEditRules::WillInsertBreak(bool* aCancel,
442
                               bool* aHandled,
443
                               int32_t aMaxLength)
444
0
{
445
0
  MOZ_ASSERT(IsEditorDataAvailable());
446
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
447
0
    return NS_ERROR_INVALID_ARG;
448
0
  }
449
0
  CANCEL_OPERATION_IF_READONLY_OR_DISABLED
450
0
  *aHandled = false;
451
0
  if (IsSingleLineEditor()) {
452
0
    *aCancel = true;
453
0
  } else {
454
0
    // handle docs with a max length
455
0
    // NOTE, this function copies inString into outString for us.
456
0
    NS_NAMED_LITERAL_STRING(inString, "\n");
457
0
    nsAutoString outString;
458
0
    bool didTruncate;
459
0
    nsresult rv =
460
0
      TruncateInsertionIfNeeded(&inString.AsString(),
461
0
                                &outString, aMaxLength, &didTruncate);
462
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
463
0
      return rv;
464
0
    }
465
0
    if (didTruncate) {
466
0
      *aCancel = true;
467
0
      return NS_OK;
468
0
    }
469
0
470
0
    *aCancel = false;
471
0
472
0
    // if the selection isn't collapsed, delete it.
473
0
    if (!SelectionRef().IsCollapsed()) {
474
0
      rv = TextEditorRef().DeleteSelectionAsSubAction(nsIEditor::eNone,
475
0
                                                      nsIEditor::eStrip);
476
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
477
0
        return NS_ERROR_EDITOR_DESTROYED;
478
0
      }
479
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
480
0
        return rv;
481
0
      }
482
0
    }
483
0
484
0
    rv = WillInsert();
485
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
486
0
      return rv;
487
0
    }
488
0
  }
489
0
  return NS_OK;
490
0
}
491
492
nsresult
493
TextEditRules::CollapseSelectionToTrailingBRIfNeeded()
494
0
{
495
0
  MOZ_ASSERT(IsEditorDataAvailable());
496
0
497
0
  // we only need to execute the stuff below if we are a plaintext editor.
498
0
  // html editors have a different mechanism for putting in mozBR's
499
0
  // (because there are a bunch more places you have to worry about it in html)
500
0
  if (!IsPlaintextEditor()) {
501
0
    return NS_OK;
502
0
  }
503
0
504
0
  // If there is no selection ranges, we should set to the end of the editor.
505
0
  // This is usually performed in TextEditRules::Init(), however, if the
506
0
  // editor is reframed, this may be called by AfterEdit().
507
0
  if (!SelectionRef().RangeCount()) {
508
0
    TextEditorRef().CollapseSelectionToEnd(&SelectionRef());
509
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
510
0
      return NS_ERROR_EDITOR_DESTROYED;
511
0
    }
512
0
  }
513
0
514
0
  // If we are at the end of the <textarea> element, we need to set the
515
0
  // selection to stick to the moz-<br> at the end of the <textarea>.
516
0
  EditorRawDOMPoint selectionStartPoint(
517
0
                      EditorBase::GetStartPoint(&SelectionRef()));
518
0
  if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
519
0
    return NS_ERROR_FAILURE;
520
0
  }
521
0
522
0
  // Nothing to do if we're not at the end of the text node.
523
0
  if (!selectionStartPoint.IsInTextNode() ||
524
0
      !selectionStartPoint.IsEndOfContainer()) {
525
0
    return NS_OK;
526
0
  }
527
0
528
0
  Element* rootElement = TextEditorRef().GetRoot();
529
0
  if (NS_WARN_IF(!rootElement)) {
530
0
    return NS_ERROR_NULL_POINTER;
531
0
  }
532
0
  nsINode* parentNode = selectionStartPoint.GetContainer()->GetParentNode();
533
0
  if (parentNode != rootElement) {
534
0
    return NS_OK;
535
0
  }
536
0
537
0
  nsINode* nextNode = selectionStartPoint.GetContainer()->GetNextSibling();
538
0
  if (!nextNode || !TextEditUtils::IsMozBR(nextNode)) {
539
0
    return NS_OK;
540
0
  }
541
0
542
0
  EditorRawDOMPoint afterStartContainer(selectionStartPoint.GetContainer());
543
0
  if (NS_WARN_IF(!afterStartContainer.AdvanceOffset())) {
544
0
    return NS_ERROR_FAILURE;
545
0
  }
546
0
  ErrorResult error;
547
0
  SelectionRef().Collapse(afterStartContainer, error);
548
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
549
0
    error.SuppressException();
550
0
    return NS_ERROR_EDITOR_DESTROYED;
551
0
  }
552
0
  if (NS_WARN_IF(error.Failed())) {
553
0
    return error.StealNSResult();
554
0
  }
555
0
  return NS_OK;
556
0
}
557
558
already_AddRefed<nsINode>
559
TextEditRules::GetTextNodeAroundSelectionStartContainer()
560
0
{
561
0
  MOZ_ASSERT(IsEditorDataAvailable());
562
0
563
0
  EditorRawDOMPoint selectionStartPoint(
564
0
                      EditorBase::GetStartPoint(&SelectionRef()));
565
0
  if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
566
0
    return nullptr;
567
0
  }
568
0
  if (selectionStartPoint.IsInTextNode()) {
569
0
    nsCOMPtr<nsINode> node = selectionStartPoint.GetContainer();
570
0
    return node.forget();
571
0
  }
572
0
  // This should be the root node, walk the tree looking for text nodes.
573
0
  // XXX NodeIterator sets mutation observer even for this temporary use.
574
0
  //     It's too expensive if this is called from a hot path.
575
0
  nsCOMPtr<nsINode> node = selectionStartPoint.GetContainer();
576
0
  RefPtr<NodeIterator> iter =
577
0
    new NodeIterator(node, NodeFilter_Binding::SHOW_TEXT, nullptr);
578
0
  while (!EditorBase::IsTextNode(node)) {
579
0
    node = iter->NextNode(IgnoreErrors());
580
0
    if (!node) {
581
0
      return nullptr;
582
0
    }
583
0
  }
584
0
  return node.forget();
585
0
}
586
587
#ifdef DEBUG
588
#define ASSERT_PASSWORD_LENGTHS_EQUAL()                                \
589
  if (IsPasswordEditor() && mTextEditor->GetRoot()) {                  \
590
    int32_t txtLen;                                                    \
591
    mTextEditor->GetTextLength(&txtLen);                               \
592
    NS_ASSERTION(mPasswordText.Length() == uint32_t(txtLen),           \
593
                 "password length not equal to number of asterisks");  \
594
  }
595
#else
596
#define ASSERT_PASSWORD_LENGTHS_EQUAL()
597
#endif
598
599
void
600
TextEditRules::HandleNewLines(nsString& aString)
601
0
{
602
0
  static const char16_t kLF = static_cast<char16_t>('\n');
603
0
  MOZ_ASSERT(IsEditorDataAvailable());
604
0
  MOZ_ASSERT(aString.FindChar(static_cast<uint16_t>('\r')) == kNotFound);
605
0
606
0
  // First of all, check if aString contains '\n' since if the string
607
0
  // does not include it, we don't need to do nothing here.
608
0
  int32_t firstLF = aString.FindChar(kLF, 0);
609
0
  if (firstLF == kNotFound) {
610
0
    return;
611
0
  }
612
0
613
0
  switch(TextEditorRef().mNewlineHandling) {
614
0
    case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
615
0
      // Default of Firefox:
616
0
      // Strip trailing newlines first so we don't wind up with trailing spaces
617
0
      aString.Trim(LFSTR, false, true);
618
0
      aString.ReplaceChar(kLF, ' ');
619
0
      break;
620
0
    case nsIPlaintextEditor::eNewlinesStrip:
621
0
      aString.StripChar(kLF);
622
0
      break;
623
0
    case nsIPlaintextEditor::eNewlinesPasteToFirst:
624
0
    default: {
625
0
      // we get first *non-empty* line.
626
0
      int32_t offset = 0;
627
0
      while (firstLF == offset) {
628
0
        offset++;
629
0
        firstLF = aString.FindChar(kLF, offset);
630
0
      }
631
0
      if (firstLF > 0) {
632
0
        aString.Truncate(firstLF);
633
0
      }
634
0
      if (offset > 0) {
635
0
        aString.Cut(0, offset);
636
0
      }
637
0
      break;
638
0
    }
639
0
    case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
640
0
      // Default of Thunderbird:
641
0
      aString.Trim(LFSTR, true, true);
642
0
      aString.ReplaceChar(kLF, ',');
643
0
      break;
644
0
    case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace: {
645
0
      nsAutoString result;
646
0
      uint32_t offset = 0;
647
0
      while (offset < aString.Length()) {
648
0
        int32_t nextLF =
649
0
          !offset ? firstLF : aString.FindChar(kLF, offset);
650
0
        if (nextLF < 0) {
651
0
          result.Append(nsDependentSubstring(aString, offset));
652
0
          break;
653
0
        }
654
0
        uint32_t wsBegin = nextLF;
655
0
        // look backwards for the first non-whitespace char
656
0
        while (wsBegin > offset && NS_IS_SPACE(aString[wsBegin - 1])) {
657
0
          --wsBegin;
658
0
        }
659
0
        result.Append(nsDependentSubstring(aString, offset, wsBegin - offset));
660
0
        offset = nextLF + 1;
661
0
        while (offset < aString.Length() && NS_IS_SPACE(aString[offset])) {
662
0
          ++offset;
663
0
        }
664
0
      }
665
0
      aString = result;
666
0
      break;
667
0
    }
668
0
    case nsIPlaintextEditor::eNewlinesPasteIntact:
669
0
      // even if we're pasting newlines, don't paste leading/trailing ones
670
0
      aString.Trim(LFSTR, true, true);
671
0
      break;
672
0
  }
673
0
}
674
675
nsresult
676
TextEditRules::WillInsertText(EditSubAction aEditSubAction,
677
                              bool* aCancel,
678
                              bool* aHandled,
679
                              const nsAString* inString,
680
                              nsAString* outString,
681
                              int32_t aMaxLength)
682
0
{
683
0
  MOZ_ASSERT(IsEditorDataAvailable());
684
0
685
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
686
0
    return NS_ERROR_INVALID_ARG;
687
0
  }
688
0
689
0
  if (inString->IsEmpty() &&
690
0
      aEditSubAction != EditSubAction::eInsertTextComingFromIME) {
691
0
    // HACK: this is a fix for bug 19395
692
0
    // I can't outlaw all empty insertions
693
0
    // because IME transaction depend on them
694
0
    // There is more work to do to make the
695
0
    // world safe for IME.
696
0
    *aCancel = true;
697
0
    *aHandled = false;
698
0
    return NS_OK;
699
0
  }
700
0
701
0
  // initialize out param
702
0
  *aCancel = false;
703
0
  *aHandled = true;
704
0
705
0
  // handle docs with a max length
706
0
  // NOTE, this function copies inString into outString for us.
707
0
  bool truncated = false;
708
0
  nsresult rv =
709
0
    TruncateInsertionIfNeeded(inString, outString, aMaxLength, &truncated);
710
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
711
0
    return rv;
712
0
  }
713
0
  // If we're exceeding the maxlength when composing IME, we need to clean up
714
0
  // the composing text, so we shouldn't return early.
715
0
  if (truncated && outString->IsEmpty() &&
716
0
      aEditSubAction != EditSubAction::eInsertTextComingFromIME) {
717
0
    *aCancel = true;
718
0
    return NS_OK;
719
0
  }
720
0
721
0
  uint32_t start = 0;
722
0
  uint32_t end = 0;
723
0
724
0
  // handle password field docs
725
0
  if (IsPasswordEditor()) {
726
0
    nsContentUtils::GetSelectionInTextControl(&SelectionRef(),
727
0
                                              TextEditorRef().GetRoot(),
728
0
                                              start, end);
729
0
  }
730
0
731
0
  // if the selection isn't collapsed, delete it.
732
0
  if (!SelectionRef().IsCollapsed()) {
733
0
    rv = TextEditorRef().DeleteSelectionAsSubAction(nsIEditor::eNone,
734
0
                                                    nsIEditor::eStrip);
735
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
736
0
      return NS_ERROR_EDITOR_DESTROYED;
737
0
    }
738
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
739
0
      return rv;
740
0
    }
741
0
  }
742
0
743
0
  rv = WillInsert(aCancel);
744
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
745
0
    return rv;
746
0
  }
747
0
748
0
  // handle password field data
749
0
  // this has the side effect of changing all the characters in aOutString
750
0
  // to the replacement character
751
0
  if (IsPasswordEditor() &&
752
0
      aEditSubAction == EditSubAction::eInsertTextComingFromIME) {
753
0
    RemoveIMETextFromPWBuf(start, outString);
754
0
  }
755
0
756
0
  // People have lots of different ideas about what text fields
757
0
  // should do with multiline pastes.  See bugs 21032, 23485, 23485, 50935.
758
0
  // The six possible options are:
759
0
  // 0. paste newlines intact
760
0
  // 1. paste up to the first newline (default)
761
0
  // 2. replace newlines with spaces
762
0
  // 3. strip newlines
763
0
  // 4. replace with commas
764
0
  // 5. strip newlines and surrounding whitespace
765
0
  // So find out what we're expected to do:
766
0
  if (IsSingleLineEditor()) {
767
0
    nsAutoString tString(*outString);
768
0
    // XXX Some callers of TextEditor::InsertTextAsAction()  already make the
769
0
    //     string use only \n as a linebreaker.  However, they are not hot
770
0
    //     path and nsContentUtils::PlatformToDOMLineBreaks() does nothing
771
0
    //     if the string doesn't include \r.  So, let's convert linebreakers
772
0
    //     here.  Note that there are too many callers of
773
0
    //     TextEditor::InsertTextAsAction().  So, it's difficult to keep
774
0
    //     maintaining all of them won't reach here without \r nor \r\n.
775
0
    nsContentUtils::PlatformToDOMLineBreaks(tString);
776
0
    HandleNewLines(tString);
777
0
    outString->Assign(tString);
778
0
  }
779
0
780
0
  if (IsPasswordEditor()) {
781
0
    // manage the password buffer
782
0
    mPasswordText.Insert(*outString, start);
783
0
784
0
    if (LookAndFeel::GetEchoPassword() && !DontEchoPassword()) {
785
0
      nsresult rv = HideLastPWInput();
786
0
      mLastStart = start;
787
0
      mLastLength = outString->Length();
788
0
      if (mTimer) {
789
0
        mTimer->Cancel();
790
0
      }
791
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
792
0
        return rv;
793
0
      }
794
0
      if (!mTimer) {
795
0
        mTimer = NS_NewTimer();
796
0
      }
797
0
      mTimer->InitWithCallback(this, LookAndFeel::GetPasswordMaskDelay(),
798
0
                               nsITimer::TYPE_ONE_SHOT);
799
0
    } else {
800
0
      FillBufWithPWChars(outString, outString->Length());
801
0
    }
802
0
  }
803
0
804
0
  // get the (collapsed) selection location
805
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
806
0
  if (NS_WARN_IF(!firstRange)) {
807
0
    return NS_ERROR_FAILURE;
808
0
  }
809
0
  EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
810
0
  if (NS_WARN_IF(!atStartOfSelection.IsSetAndValid())) {
811
0
    return NS_ERROR_FAILURE;
812
0
  }
813
0
814
0
  // don't put text in places that can't have it
815
0
  if (!atStartOfSelection.IsInTextNode() &&
816
0
      !TextEditorRef().CanContainTag(*atStartOfSelection.GetContainer(),
817
0
                                     *nsGkAtoms::textTagName)) {
818
0
    return NS_ERROR_FAILURE;
819
0
  }
820
0
821
0
  // we need to get the doc
822
0
  nsCOMPtr<nsIDocument> doc = TextEditorRef().GetDocument();
823
0
  if (NS_WARN_IF(!doc)) {
824
0
    return NS_ERROR_NOT_INITIALIZED;
825
0
  }
826
0
827
0
  if (aEditSubAction == EditSubAction::eInsertTextComingFromIME) {
828
0
    // Find better insertion point to insert text.
829
0
    EditorRawDOMPoint betterInsertionPoint =
830
0
      TextEditorRef().FindBetterInsertionPoint(atStartOfSelection);
831
0
    // If there is one or more IME selections, its minimum offset should be
832
0
    // the insertion point.
833
0
    int32_t IMESelectionOffset =
834
0
      TextEditorRef().GetIMESelectionStartOffsetIn(
835
0
                        betterInsertionPoint.GetContainer());
836
0
    if (IMESelectionOffset >= 0) {
837
0
      betterInsertionPoint.Set(betterInsertionPoint.GetContainer(),
838
0
                               IMESelectionOffset);
839
0
    }
840
0
    rv = TextEditorRef().InsertTextWithTransaction(*doc, *outString,
841
0
                                                   betterInsertionPoint);
842
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
843
0
      return NS_ERROR_EDITOR_DESTROYED;
844
0
    }
845
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
846
0
      return rv;
847
0
    }
848
0
  } else {
849
0
    // aEditSubAction == EditSubAction::eInsertText
850
0
851
0
    // don't change my selection in subtransactions
852
0
    AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
853
0
854
0
    EditorRawDOMPoint pointAfterStringInserted;
855
0
    rv = TextEditorRef().InsertTextWithTransaction(*doc, *outString,
856
0
                                                   atStartOfSelection,
857
0
                                                   &pointAfterStringInserted);
858
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
859
0
      return NS_ERROR_EDITOR_DESTROYED;
860
0
    }
861
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
862
0
      return rv;
863
0
    }
864
0
865
0
    if (pointAfterStringInserted.IsSet()) {
866
0
      // Make the caret attach to the inserted text, unless this text ends with a LF,
867
0
      // in which case make the caret attach to the next line.
868
0
      bool endsWithLF =
869
0
        !outString->IsEmpty() && outString->Last() == nsCRT::LF;
870
0
      IgnoredErrorResult error;
871
0
      SelectionRef().SetInterlinePosition(endsWithLF, error);
872
0
      NS_WARNING_ASSERTION(!error.Failed(),
873
0
        "Failed to set or unset interline position");
874
0
875
0
      MOZ_ASSERT(!pointAfterStringInserted.GetChild(),
876
0
        "After inserting text into a text node, pointAfterStringInserted."
877
0
        "GetChild() should be nullptr");
878
0
      error = IgnoredErrorResult();
879
0
      SelectionRef().Collapse(pointAfterStringInserted, error);
880
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
881
0
        return NS_ERROR_EDITOR_DESTROYED;
882
0
      }
883
0
      NS_WARNING_ASSERTION(!error.Failed(),
884
0
        "Failed to collapse selection after inserting string");
885
0
    }
886
0
  }
887
0
  ASSERT_PASSWORD_LENGTHS_EQUAL()
888
0
  return NS_OK;
889
0
}
890
891
nsresult
892
TextEditRules::WillSetText(bool* aCancel,
893
                           bool* aHandled,
894
                           const nsAString* aString,
895
                           int32_t aMaxLength)
896
0
{
897
0
  MOZ_ASSERT(IsEditorDataAvailable());
898
0
  MOZ_ASSERT(aCancel);
899
0
  MOZ_ASSERT(aHandled);
900
0
  MOZ_ASSERT(aString);
901
0
  MOZ_ASSERT(aString->FindChar(static_cast<char16_t>('\r')) == kNotFound);
902
0
903
0
  CANCEL_OPERATION_IF_READONLY_OR_DISABLED
904
0
905
0
  *aHandled = false;
906
0
  *aCancel = false;
907
0
908
0
  if (!IsPlaintextEditor() ||
909
0
      TextEditorRef().IsIMEComposing() ||
910
0
      TextEditorRef().IsUndoRedoEnabled() ||
911
0
      aMaxLength != -1) {
912
0
    // SetTextImpl only supports plain text editor without IME and
913
0
    // when we don't need to make it undoable.
914
0
    return NS_OK;
915
0
  }
916
0
917
0
  if (IsPasswordEditor() && LookAndFeel::GetEchoPassword() &&
918
0
      !DontEchoPassword()) {
919
0
    // Echo password timer will implement on InsertText.
920
0
    return NS_OK;
921
0
  }
922
0
923
0
  nsresult rv = WillInsert(aCancel);
924
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
925
0
    return rv;
926
0
  }
927
0
928
0
  RefPtr<Element> rootElement = TextEditorRef().GetRoot();
929
0
  uint32_t count = rootElement->GetChildCount();
930
0
931
0
  // handles only when there is only one node and it's a text node, or empty.
932
0
933
0
  if (count > 1) {
934
0
    return NS_OK;
935
0
  }
936
0
937
0
  nsAutoString tString(*aString);
938
0
939
0
  if (IsPasswordEditor()) {
940
0
    mPasswordText.Assign(tString);
941
0
    FillBufWithPWChars(&tString, tString.Length());
942
0
  } else if (IsSingleLineEditor()) {
943
0
    HandleNewLines(tString);
944
0
  }
945
0
946
0
  if (!count) {
947
0
    if (tString.IsEmpty()) {
948
0
      *aHandled = true;
949
0
      return NS_OK;
950
0
    }
951
0
    RefPtr<nsIDocument> doc = TextEditorRef().GetDocument();
952
0
    if (NS_WARN_IF(!doc)) {
953
0
      return NS_OK;
954
0
    }
955
0
    RefPtr<nsTextNode> newNode = EditorBase::CreateTextNode(*doc, tString);
956
0
    if (NS_WARN_IF(!newNode)) {
957
0
      return NS_OK;
958
0
    }
959
0
    nsresult rv =
960
0
      TextEditorRef().InsertNodeWithTransaction(
961
0
                        *newNode, EditorRawDOMPoint(rootElement, 0));
962
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
963
0
      return NS_ERROR_EDITOR_DESTROYED;
964
0
    }
965
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
966
0
      return rv;
967
0
    }
968
0
    *aHandled = true;
969
0
970
0
    ASSERT_PASSWORD_LENGTHS_EQUAL();
971
0
972
0
    return NS_OK;
973
0
  }
974
0
975
0
  nsINode* curNode = rootElement->GetFirstChild();
976
0
  if (NS_WARN_IF(!EditorBase::IsTextNode(curNode))) {
977
0
    return NS_OK;
978
0
  }
979
0
980
0
  // Even if empty text, we don't remove text node and set empty text
981
0
  // for performance
982
0
  rv = TextEditorRef().SetTextImpl(SelectionRef(), tString,
983
0
                                   *curNode->GetAsText());
984
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
985
0
    return NS_ERROR_EDITOR_DESTROYED;
986
0
  }
987
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
988
0
    return rv;
989
0
  }
990
0
991
0
  *aHandled = true;
992
0
993
0
  ASSERT_PASSWORD_LENGTHS_EQUAL();
994
0
995
0
  return NS_OK;
996
0
}
997
998
nsresult
999
TextEditRules::WillSetTextProperty(bool* aCancel,
1000
                                   bool* aHandled)
1001
0
{
1002
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
1003
0
    return NS_ERROR_INVALID_ARG;
1004
0
  }
1005
0
1006
0
  // XXX: should probably return a success value other than NS_OK that means "not allowed"
1007
0
  if (IsPlaintextEditor()) {
1008
0
    *aCancel = true;
1009
0
  }
1010
0
  return NS_OK;
1011
0
}
1012
1013
nsresult
1014
TextEditRules::WillRemoveTextProperty(bool* aCancel,
1015
                                      bool* aHandled)
1016
0
{
1017
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
1018
0
    return NS_ERROR_INVALID_ARG;
1019
0
  }
1020
0
1021
0
  // XXX: should probably return a success value other than NS_OK that means "not allowed"
1022
0
  if (IsPlaintextEditor()) {
1023
0
    *aCancel = true;
1024
0
  }
1025
0
  return NS_OK;
1026
0
}
1027
1028
nsresult
1029
TextEditRules::WillDeleteSelection(nsIEditor::EDirection aCollapsedAction,
1030
                                   bool* aCancel,
1031
                                   bool* aHandled)
1032
0
{
1033
0
  MOZ_ASSERT(IsEditorDataAvailable());
1034
0
1035
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
1036
0
    return NS_ERROR_INVALID_ARG;
1037
0
  }
1038
0
  CANCEL_OPERATION_IF_READONLY_OR_DISABLED
1039
0
1040
0
  // initialize out param
1041
0
  *aCancel = false;
1042
0
  *aHandled = false;
1043
0
1044
0
  // if there is only bogus content, cancel the operation
1045
0
  if (mBogusNode) {
1046
0
    *aCancel = true;
1047
0
    return NS_OK;
1048
0
  }
1049
0
  nsresult rv =
1050
0
    DeleteSelectionWithTransaction(aCollapsedAction, aCancel, aHandled);
1051
0
  // DeleteSelectionWithTransaction() creates SelectionBatcher.  Therefore,
1052
0
  // quitting from it might cause having destroyed the editor.
1053
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1054
0
    return NS_ERROR_EDITOR_DESTROYED;
1055
0
  }
1056
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1057
0
    return rv;
1058
0
  }
1059
0
  return NS_OK;
1060
0
}
1061
1062
nsresult
1063
TextEditRules::DeleteSelectionWithTransaction(
1064
                 nsIEditor::EDirection aCollapsedAction,
1065
                 bool* aCancel,
1066
                 bool* aHandled)
1067
0
{
1068
0
  MOZ_ASSERT(IsEditorDataAvailable());
1069
0
  MOZ_ASSERT(aCancel);
1070
0
  MOZ_ASSERT(aHandled);
1071
0
1072
0
  // If the current selection is empty (e.g the user presses backspace with
1073
0
  // a collapsed selection), then we want to avoid sending the selectstart
1074
0
  // event to the user, so we hide selection changes. However, we still
1075
0
  // want to send a single selectionchange event to the document, so we
1076
0
  // batch the selectionchange events, such that a single event fires after
1077
0
  // the AutoHideSelectionChanges destructor has been run.
1078
0
  SelectionBatcher selectionBatcher(&SelectionRef());
1079
0
  AutoHideSelectionChanges hideSelection(&SelectionRef());
1080
0
  nsAutoScriptBlocker scriptBlocker;
1081
0
1082
0
  if (IsPasswordEditor()) {
1083
0
    nsresult rv =
1084
0
      TextEditorRef().ExtendSelectionForDelete(&SelectionRef(),
1085
0
                                               &aCollapsedAction);
1086
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1087
0
      return rv;
1088
0
    }
1089
0
1090
0
    // manage the password buffer
1091
0
    uint32_t start, end;
1092
0
    nsContentUtils::GetSelectionInTextControl(&SelectionRef(),
1093
0
                                              TextEditorRef().GetRoot(),
1094
0
                                              start, end);
1095
0
1096
0
    if (LookAndFeel::GetEchoPassword()) {
1097
0
      rv = HideLastPWInput();
1098
0
      mLastStart = start;
1099
0
      mLastLength = 0;
1100
0
      if (mTimer) {
1101
0
        mTimer->Cancel();
1102
0
      }
1103
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1104
0
        return rv;
1105
0
      }
1106
0
    }
1107
0
1108
0
    // Collapsed selection.
1109
0
    if (end == start) {
1110
0
      // Deleting back.
1111
0
      if (nsIEditor::ePrevious == aCollapsedAction && start > 0) {
1112
0
        mPasswordText.Cut(start-1, 1);
1113
0
      }
1114
0
      // Deleting forward.
1115
0
      else if (nsIEditor::eNext == aCollapsedAction) {
1116
0
        mPasswordText.Cut(start, 1);
1117
0
      }
1118
0
      // Otherwise nothing to do for this collapsed selection.
1119
0
    }
1120
0
    // Extended selection.
1121
0
    else {
1122
0
      mPasswordText.Cut(start, end-start);
1123
0
    }
1124
0
  } else {
1125
0
    EditorRawDOMPoint selectionStartPoint(
1126
0
                        EditorBase::GetStartPoint(&SelectionRef()));
1127
0
    if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
1128
0
      return NS_ERROR_FAILURE;
1129
0
    }
1130
0
1131
0
    if (!SelectionRef().IsCollapsed()) {
1132
0
      return NS_OK;
1133
0
    }
1134
0
1135
0
    // Test for distance between caret and text that will be deleted
1136
0
    nsresult rv =
1137
0
      CheckBidiLevelForDeletion(selectionStartPoint, aCollapsedAction, aCancel);
1138
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1139
0
      return rv;
1140
0
    }
1141
0
    if (*aCancel) {
1142
0
      return NS_OK;
1143
0
    }
1144
0
1145
0
    rv = TextEditorRef().ExtendSelectionForDelete(&SelectionRef(),
1146
0
                                                  &aCollapsedAction);
1147
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1148
0
      return rv;
1149
0
    }
1150
0
  }
1151
0
1152
0
  nsresult rv =
1153
0
    TextEditorRef().DeleteSelectionWithTransaction(aCollapsedAction,
1154
0
                                                   nsIEditor::eStrip);
1155
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1156
0
    return NS_ERROR_EDITOR_DESTROYED;
1157
0
  }
1158
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1159
0
    return rv;
1160
0
  }
1161
0
1162
0
  *aHandled = true;
1163
0
  ASSERT_PASSWORD_LENGTHS_EQUAL()
1164
0
  return NS_OK;
1165
0
}
1166
1167
nsresult
1168
TextEditRules::DidDeleteSelection()
1169
0
{
1170
0
  MOZ_ASSERT(IsEditorDataAvailable());
1171
0
1172
0
  EditorRawDOMPoint selectionStartPoint(
1173
0
                      EditorBase::GetStartPoint(&SelectionRef()));
1174
0
  if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
1175
0
    return NS_ERROR_FAILURE;
1176
0
  }
1177
0
1178
0
  // Delete empty text nodes at selection.
1179
0
  if (selectionStartPoint.IsInTextNode() &&
1180
0
      !selectionStartPoint.GetContainer()->Length()) {
1181
0
    nsresult rv =
1182
0
      TextEditorRef().DeleteNodeWithTransaction(
1183
0
                        *selectionStartPoint.GetContainer());
1184
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1185
0
      return NS_ERROR_EDITOR_DESTROYED;
1186
0
    }
1187
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1188
0
      return rv;
1189
0
    }
1190
0
  }
1191
0
1192
0
  if (mDidExplicitlySetInterline) {
1193
0
    return NS_OK;
1194
0
  }
1195
0
  // We prevent the caret from sticking on the left of prior BR
1196
0
  // (i.e. the end of previous line) after this deletion.  Bug 92124
1197
0
  ErrorResult err;
1198
0
  SelectionRef().SetInterlinePosition(true, err);
1199
0
  NS_WARNING_ASSERTION(!err.Failed(), "Failed to set interline position");
1200
0
  return err.StealNSResult();
1201
0
}
1202
1203
nsresult
1204
TextEditRules::WillUndo(bool* aCancel,
1205
                        bool* aHandled)
1206
0
{
1207
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
1208
0
    return NS_ERROR_INVALID_ARG;
1209
0
  }
1210
0
  CANCEL_OPERATION_IF_READONLY_OR_DISABLED
1211
0
  // initialize out param
1212
0
  *aCancel = false;
1213
0
  *aHandled = false;
1214
0
  return NS_OK;
1215
0
}
1216
1217
nsresult
1218
TextEditRules::DidUndo(nsresult aResult)
1219
0
{
1220
0
  MOZ_ASSERT(IsEditorDataAvailable());
1221
0
1222
0
  // If aResult is an error, we return it.
1223
0
  if (NS_WARN_IF(NS_FAILED(aResult))) {
1224
0
    return aResult;
1225
0
  }
1226
0
1227
0
  Element* rootElement = TextEditorRef().GetRoot();
1228
0
  if (NS_WARN_IF(!rootElement)) {
1229
0
    return NS_ERROR_FAILURE;
1230
0
  }
1231
0
1232
0
  // The idea here is to see if the magic empty node has suddenly reappeared as
1233
0
  // the result of the undo.  If it has, set our state so we remember it.
1234
0
  // There is a tradeoff between doing here and at redo, or doing it everywhere
1235
0
  // else that might care.  Since undo and redo are relatively rare, it makes
1236
0
  // sense to take the (small) performance hit here.
1237
0
  nsIContent* node = TextEditorRef().GetLeftmostChild(rootElement);
1238
0
  if (node && TextEditorRef().IsMozEditorBogusNode(node)) {
1239
0
    mBogusNode = node;
1240
0
  } else {
1241
0
    mBogusNode = nullptr;
1242
0
  }
1243
0
  return aResult;
1244
0
}
1245
1246
nsresult
1247
TextEditRules::WillRedo(bool* aCancel,
1248
                        bool* aHandled)
1249
0
{
1250
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
1251
0
    return NS_ERROR_INVALID_ARG;
1252
0
  }
1253
0
  CANCEL_OPERATION_IF_READONLY_OR_DISABLED
1254
0
  // initialize out param
1255
0
  *aCancel = false;
1256
0
  *aHandled = false;
1257
0
  return NS_OK;
1258
0
}
1259
1260
nsresult
1261
TextEditRules::DidRedo(nsresult aResult)
1262
0
{
1263
0
  MOZ_ASSERT(IsEditorDataAvailable());
1264
0
1265
0
  if (NS_FAILED(aResult)) {
1266
0
    return aResult; // if aResult is an error, we return it.
1267
0
  }
1268
0
1269
0
  Element* rootElement = TextEditorRef().GetRoot();
1270
0
  if (NS_WARN_IF(!rootElement)) {
1271
0
    return NS_ERROR_FAILURE;
1272
0
  }
1273
0
1274
0
  nsCOMPtr<nsIHTMLCollection> nodeList =
1275
0
    rootElement->GetElementsByTagName(NS_LITERAL_STRING("br"));
1276
0
  MOZ_ASSERT(nodeList);
1277
0
  uint32_t len = nodeList->Length();
1278
0
1279
0
  if (len != 1) {
1280
0
    // only in the case of one br could there be the bogus node
1281
0
    mBogusNode = nullptr;
1282
0
    return NS_OK;
1283
0
  }
1284
0
1285
0
  Element* brElement = nodeList->Item(0);
1286
0
  if (TextEditorRef().IsMozEditorBogusNode(brElement)) {
1287
0
    mBogusNode = brElement;
1288
0
  } else {
1289
0
    mBogusNode = nullptr;
1290
0
  }
1291
0
  return NS_OK;
1292
0
}
1293
1294
nsresult
1295
TextEditRules::WillOutputText(const nsAString* aOutputFormat,
1296
                              nsAString* aOutString,
1297
                              uint32_t aFlags,
1298
                              bool* aCancel,
1299
                              bool* aHandled)
1300
0
{
1301
0
  MOZ_ASSERT(IsEditorDataAvailable());
1302
0
1303
0
  // null selection ok
1304
0
  if (NS_WARN_IF(!aOutString) || NS_WARN_IF(!aOutputFormat) ||
1305
0
      NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
1306
0
    return NS_ERROR_NULL_POINTER;
1307
0
  }
1308
0
1309
0
  // initialize out param
1310
0
  *aCancel = false;
1311
0
  *aHandled = false;
1312
0
1313
0
  if (!aOutputFormat->LowerCaseEqualsLiteral("text/plain")) {
1314
0
    return NS_OK;
1315
0
  }
1316
0
1317
0
  // XXX Looks like that even if it's password field, we need to use the
1318
0
  //     expensive path if the caller requests some complicated handling.
1319
0
  //     However, changing the behavior for password field might cause
1320
0
  //     security issue.  So, be careful when you touch here.
1321
0
  if (IsPasswordEditor()) {
1322
0
    *aOutString = mPasswordText;
1323
0
    *aHandled = true;
1324
0
    return NS_OK;
1325
0
  }
1326
0
1327
0
  // If there is a bogus node, there's no content.  So output empty string.
1328
0
  if (mBogusNode) {
1329
0
    aOutString->Truncate();
1330
0
    *aHandled = true;
1331
0
    return NS_OK;
1332
0
  }
1333
0
1334
0
  // If it's necessary to check selection range or the editor wraps hard,
1335
0
  // we need some complicated handling.  In such case, we need to use the
1336
0
  // expensive path.
1337
0
  // XXX Anything else what we cannot return plain text simply?
1338
0
  if (aFlags & nsIDocumentEncoder::OutputSelectionOnly ||
1339
0
      aFlags & nsIDocumentEncoder::OutputWrap) {
1340
0
    return NS_OK;
1341
0
  }
1342
0
1343
0
  // If it's neither <input type="text"> nor <textarea>, e.g., an HTML editor
1344
0
  // which is in plaintext mode (e.g., plaintext email composer on Thunderbird),
1345
0
  // it should be handled by the expensive path.
1346
0
  if (TextEditorRef().AsHTMLEditor()) {
1347
0
    return NS_OK;
1348
0
  }
1349
0
1350
0
  Element* root = TextEditorRef().GetRoot();
1351
0
  if (!root) { // Don't warn it, this is possible, e.g., 997805.html
1352
0
    aOutString->Truncate();
1353
0
    *aHandled = true;
1354
0
    return NS_OK;
1355
0
  }
1356
0
1357
0
  nsIContent* firstChild = root->GetFirstChild();
1358
0
  if (!firstChild) {
1359
0
    aOutString->Truncate();
1360
0
    *aHandled = true;
1361
0
    return NS_OK;
1362
0
  }
1363
0
1364
0
  // If it's an <input type="text"> element, the DOM tree should be:
1365
0
  // <div class="anonymous-div">
1366
0
  //   #text
1367
0
  // </div>
1368
0
  //
1369
0
  // If it's a <textarea> element, the DOM tree should be:
1370
0
  // <div class="anonymous-div">
1371
0
  //   #text (if there is)
1372
0
  //   <br type="_moz">
1373
0
  //   <scrollbar orient="horizontal">
1374
0
  //   ...
1375
0
  // </div>
1376
0
1377
0
  Text* text = firstChild->GetAsText();
1378
0
  nsIContent* firstChildExceptText =
1379
0
    text ? firstChild->GetNextSibling() : firstChild;
1380
0
  // If the DOM tree is unexpected, fall back to the expensive path.
1381
0
  bool isInput = IsSingleLineEditor();
1382
0
  bool isTextarea = !isInput;
1383
0
  if (NS_WARN_IF(isInput && firstChildExceptText) ||
1384
0
      NS_WARN_IF(isTextarea && !firstChildExceptText) ||
1385
0
      NS_WARN_IF(isTextarea &&
1386
0
                 !TextEditUtils::IsMozBR(firstChildExceptText) &&
1387
0
                 !firstChildExceptText->IsXULElement(nsGkAtoms::scrollbar))) {
1388
0
    return NS_OK;
1389
0
  }
1390
0
1391
0
  // If there is no text node in the expected DOM tree, we can say that it's
1392
0
  // just empty.
1393
0
  if (!text) {
1394
0
    aOutString->Truncate();
1395
0
    *aHandled = true;
1396
0
    return NS_OK;
1397
0
  }
1398
0
1399
0
  // Otherwise, the text is the value.
1400
0
  text->GetData(*aOutString);
1401
0
1402
0
  *aHandled = true;
1403
0
  return NS_OK;
1404
0
}
1405
1406
nsresult
1407
TextEditRules::RemoveRedundantTrailingBR()
1408
0
{
1409
0
  MOZ_ASSERT(IsEditorDataAvailable());
1410
0
1411
0
  // If the bogus node exists, we have no work to do
1412
0
  if (mBogusNode) {
1413
0
    return NS_OK;
1414
0
  }
1415
0
1416
0
  // Likewise, nothing to be done if we could never have inserted a trailing br
1417
0
  if (IsSingleLineEditor()) {
1418
0
    return NS_OK;
1419
0
  }
1420
0
1421
0
  Element* rootElement = TextEditorRef().GetRoot();
1422
0
  if (NS_WARN_IF(!rootElement)) {
1423
0
    return NS_ERROR_NULL_POINTER;
1424
0
  }
1425
0
1426
0
  uint32_t childCount = rootElement->GetChildCount();
1427
0
  if (childCount > 1) {
1428
0
    // The trailing br is redundant if it is the only remaining child node
1429
0
    return NS_OK;
1430
0
  }
1431
0
1432
0
  RefPtr<nsIContent> child = rootElement->GetFirstChild();
1433
0
  if (!child || !child->IsElement()) {
1434
0
    return NS_OK;
1435
0
  }
1436
0
1437
0
  RefPtr<Element> childElement = child->AsElement();
1438
0
  if (!TextEditUtils::IsMozBR(childElement)) {
1439
0
    return NS_OK;
1440
0
  }
1441
0
1442
0
  // Rather than deleting this node from the DOM tree we should instead
1443
0
  // morph this br into the bogus node
1444
0
  childElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
1445
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1446
0
    return NS_ERROR_EDITOR_DESTROYED;
1447
0
  }
1448
0
1449
0
  // set mBogusNode to be this <br>
1450
0
  mBogusNode = childElement;
1451
0
1452
0
  // give it the bogus node attribute
1453
0
  childElement->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
1454
0
                        kMOZEditorBogusNodeValue, false);
1455
0
  return NS_OK;
1456
0
}
1457
1458
nsresult
1459
TextEditRules::CreateTrailingBRIfNeeded()
1460
0
{
1461
0
  MOZ_ASSERT(IsEditorDataAvailable());
1462
0
1463
0
  // but only if we aren't a single line edit field
1464
0
  if (IsSingleLineEditor()) {
1465
0
    return NS_OK;
1466
0
  }
1467
0
1468
0
  Element* rootElement = TextEditorRef().GetRoot();
1469
0
  if (NS_WARN_IF(!rootElement)) {
1470
0
    return NS_ERROR_FAILURE;
1471
0
  }
1472
0
1473
0
  nsCOMPtr<nsIContent> lastChild = rootElement->GetLastChild();
1474
0
  // assuming CreateBogusNodeIfNeeded() has been called first
1475
0
  if (NS_WARN_IF(!lastChild)) {
1476
0
    return NS_ERROR_FAILURE;
1477
0
  }
1478
0
1479
0
  if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
1480
0
    AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
1481
0
    EditorRawDOMPoint endOfRoot;
1482
0
    endOfRoot.SetToEndOf(rootElement);
1483
0
    CreateElementResult createMozBrResult = CreateMozBR(endOfRoot);
1484
0
    if (NS_WARN_IF(createMozBrResult.Failed())) {
1485
0
      return createMozBrResult.Rv();
1486
0
    }
1487
0
    return NS_OK;
1488
0
  }
1489
0
1490
0
  // Check to see if the trailing BR is a former bogus node - this will have
1491
0
  // stuck around if we previously morphed a trailing node into a bogus node.
1492
0
  if (!TextEditorRef().IsMozEditorBogusNode(lastChild)) {
1493
0
    return NS_OK;
1494
0
  }
1495
0
1496
0
  // Morph it back to a mozBR
1497
0
  lastChild->AsElement()->UnsetAttr(kNameSpaceID_None,
1498
0
                                    kMOZEditorBogusNodeAttrAtom,
1499
0
                                    false);
1500
0
  lastChild->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
1501
0
                                  NS_LITERAL_STRING("_moz"),
1502
0
                                  true);
1503
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1504
0
    return NS_ERROR_EDITOR_DESTROYED;
1505
0
  }
1506
0
  return NS_OK;
1507
0
}
1508
1509
nsresult
1510
TextEditRules::CreateBogusNodeIfNeeded()
1511
0
{
1512
0
  MOZ_ASSERT(IsEditorDataAvailable());
1513
0
1514
0
  if (mBogusNode) {
1515
0
    // Let's not create more than one, ok?
1516
0
    return NS_OK;
1517
0
  }
1518
0
1519
0
  // tell rules system to not do any post-processing
1520
0
  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
1521
0
                                      TextEditorRef(),
1522
0
                                      EditSubAction::eCreateBogusNode,
1523
0
                                      nsIEditor::eNone);
1524
0
1525
0
  RefPtr<Element> rootElement = TextEditorRef().GetRoot();
1526
0
  if (!rootElement) {
1527
0
    // We don't even have a body yet, don't insert any bogus nodes at
1528
0
    // this point.
1529
0
    return NS_OK;
1530
0
  }
1531
0
1532
0
  // Now we've got the body element. Iterate over the body element's children,
1533
0
  // looking for editable content. If no editable content is found, insert the
1534
0
  // bogus node.
1535
0
  bool isRootEditable = TextEditorRef().IsEditable(rootElement);
1536
0
  for (nsIContent* rootChild = rootElement->GetFirstChild();
1537
0
       rootChild;
1538
0
       rootChild = rootChild->GetNextSibling()) {
1539
0
    if (TextEditorRef().IsMozEditorBogusNode(rootChild) ||
1540
0
        !isRootEditable ||
1541
0
        TextEditorRef().IsEditable(rootChild) ||
1542
0
        TextEditorRef().IsBlockNode(rootChild)) {
1543
0
      return NS_OK;
1544
0
    }
1545
0
  }
1546
0
1547
0
  // Skip adding the bogus node if body is read-only.
1548
0
  if (!TextEditorRef().IsModifiableNode(*rootElement)) {
1549
0
    return NS_OK;
1550
0
  }
1551
0
1552
0
  // Create a br.
1553
0
  RefPtr<Element> newBrElement =
1554
0
    TextEditorRef().CreateHTMLContent(nsGkAtoms::br);
1555
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1556
0
    return NS_ERROR_EDITOR_DESTROYED;
1557
0
  }
1558
0
  if (NS_WARN_IF(!newBrElement)) {
1559
0
    return NS_ERROR_FAILURE;
1560
0
  }
1561
0
1562
0
  // set mBogusNode to be the newly created <br>
1563
0
  mBogusNode = newBrElement;
1564
0
1565
0
  // Give it a special attribute.
1566
0
  newBrElement->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
1567
0
                        kMOZEditorBogusNodeValue, false);
1568
0
1569
0
  // Put the node in the document.
1570
0
  nsresult rv =
1571
0
    TextEditorRef().InsertNodeWithTransaction(
1572
0
                      *mBogusNode, EditorRawDOMPoint(rootElement, 0));
1573
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1574
0
    return NS_ERROR_EDITOR_DESTROYED;
1575
0
  }
1576
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1577
0
    return rv;
1578
0
  }
1579
0
1580
0
  // Set selection.
1581
0
  IgnoredErrorResult error;
1582
0
  SelectionRef().Collapse(EditorRawDOMPoint(rootElement, 0), error);
1583
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1584
0
    return NS_ERROR_EDITOR_DESTROYED;
1585
0
  }
1586
0
  NS_WARNING_ASSERTION(!error.Failed(),
1587
0
    "Failed to collapse selection at start of the root element");
1588
0
  return NS_OK;
1589
0
}
1590
1591
1592
nsresult
1593
TextEditRules::TruncateInsertionIfNeeded(const nsAString* aInString,
1594
                                         nsAString* aOutString,
1595
                                         int32_t aMaxLength,
1596
                                         bool* aTruncated)
1597
0
{
1598
0
  MOZ_ASSERT(IsEditorDataAvailable());
1599
0
1600
0
  if (NS_WARN_IF(!aInString) || NS_WARN_IF(!aOutString)) {
1601
0
    return NS_ERROR_INVALID_ARG;
1602
0
  }
1603
0
1604
0
  if (!aOutString->Assign(*aInString, mozilla::fallible)) {
1605
0
    return NS_ERROR_OUT_OF_MEMORY;
1606
0
  }
1607
0
  if (aTruncated) {
1608
0
    *aTruncated = false;
1609
0
  }
1610
0
1611
0
  if (-1 != aMaxLength && IsPlaintextEditor() &&
1612
0
      !TextEditorRef().IsIMEComposing()) {
1613
0
    // Get the current text length.
1614
0
    // Get the length of inString.
1615
0
    // Get the length of the selection.
1616
0
    //   If selection is collapsed, it is length 0.
1617
0
    //   Subtract the length of the selection from the len(doc)
1618
0
    //   since we'll delete the selection on insert.
1619
0
    //   This is resultingDocLength.
1620
0
    // Get old length of IME composing string
1621
0
    //   which will be replaced by new one.
1622
0
    // If (resultingDocLength) is at or over max, cancel the insert
1623
0
    // If (resultingDocLength) + (length of input) > max,
1624
0
    //    set aOutString to subset of inString so length = max
1625
0
    int32_t docLength;
1626
0
    nsresult rv = TextEditorRef().GetTextLength(&docLength);
1627
0
    if (NS_FAILED(rv)) {
1628
0
      return rv;
1629
0
    }
1630
0
1631
0
    uint32_t start, end;
1632
0
    nsContentUtils::GetSelectionInTextControl(&SelectionRef(),
1633
0
                                              TextEditorRef().GetRoot(),
1634
0
                                              start, end);
1635
0
1636
0
    TextComposition* composition = TextEditorRef().GetComposition();
1637
0
    uint32_t oldCompStrLength = composition ? composition->String().Length() : 0;
1638
0
1639
0
    const uint32_t selectionLength = end - start;
1640
0
    const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
1641
0
    if (resultingDocLength >= aMaxLength) {
1642
0
      // This call is guaranteed to reduce the capacity of the string, so it
1643
0
      // cannot cause an OOM.
1644
0
      aOutString->Truncate();
1645
0
      if (aTruncated) {
1646
0
        *aTruncated = true;
1647
0
      }
1648
0
    } else {
1649
0
      int32_t oldLength = aOutString->Length();
1650
0
      if (oldLength + resultingDocLength > aMaxLength) {
1651
0
        int32_t newLength = aMaxLength - resultingDocLength;
1652
0
        MOZ_ASSERT(newLength > 0);
1653
0
        char16_t newLastChar = aOutString->CharAt(newLength - 1);
1654
0
        char16_t removingFirstChar = aOutString->CharAt(newLength);
1655
0
        // Don't separate the string between a surrogate pair.
1656
0
        if (NS_IS_HIGH_SURROGATE(newLastChar) &&
1657
0
            NS_IS_LOW_SURROGATE(removingFirstChar)) {
1658
0
          newLength--;
1659
0
        }
1660
0
        // XXX What should we do if we're removing IVS and its preceding
1661
0
        //     character won't be removed?
1662
0
        // This call is guaranteed to reduce the capacity of the string, so it
1663
0
        // cannot cause an OOM.
1664
0
        aOutString->Truncate(newLength);
1665
0
        if (aTruncated) {
1666
0
          *aTruncated = true;
1667
0
        }
1668
0
      }
1669
0
    }
1670
0
  }
1671
0
  return NS_OK;
1672
0
}
1673
1674
void
1675
TextEditRules::ResetIMETextPWBuf()
1676
0
{
1677
0
  mPasswordIMEText.Truncate();
1678
0
}
1679
1680
void
1681
TextEditRules::RemoveIMETextFromPWBuf(uint32_t& aStart,
1682
                                      nsAString* aIMEString)
1683
0
{
1684
0
  MOZ_ASSERT(aIMEString);
1685
0
1686
0
  // initialize PasswordIME
1687
0
  if (mPasswordIMEText.IsEmpty()) {
1688
0
    mPasswordIMEIndex = aStart;
1689
0
  } else {
1690
0
    // manage the password buffer
1691
0
    mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
1692
0
    aStart = mPasswordIMEIndex;
1693
0
  }
1694
0
1695
0
  mPasswordIMEText.Assign(*aIMEString);
1696
0
}
1697
1698
NS_IMETHODIMP
1699
TextEditRules::Notify(nsITimer* aTimer)
1700
0
{
1701
0
  MOZ_ASSERT(mTimer);
1702
0
1703
0
  if (NS_WARN_IF(!mTextEditor)) {
1704
0
    return NS_ERROR_NOT_AVAILABLE;
1705
0
  }
1706
0
1707
0
  Selection* selection = mTextEditor->GetSelection();
1708
0
  if (NS_WARN_IF(!selection)) {
1709
0
    return NS_ERROR_FAILURE;
1710
0
  }
1711
0
1712
0
  AutoSafeEditorData setData(*this, *mTextEditor, *selection);
1713
0
1714
0
  // Check whether our text editor's password flag was changed before this
1715
0
  // "hide password character" timer actually fires.
1716
0
  nsresult rv = IsPasswordEditor() ? HideLastPWInput() : NS_OK;
1717
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to hide last password input");
1718
0
  ASSERT_PASSWORD_LENGTHS_EQUAL();
1719
0
  mLastLength = 0;
1720
0
  return rv;
1721
0
}
1722
1723
NS_IMETHODIMP
1724
TextEditRules::GetName(nsACString& aName)
1725
0
{
1726
0
  aName.AssignLiteral("TextEditRules");
1727
0
  return NS_OK;
1728
0
}
1729
1730
nsresult
1731
TextEditRules::HideLastPWInput()
1732
0
{
1733
0
  MOZ_ASSERT(IsEditorDataAvailable());
1734
0
1735
0
  if (!mLastLength) {
1736
0
    // Special case, we're trying to replace a range that no longer exists
1737
0
    return NS_OK;
1738
0
  }
1739
0
1740
0
  nsAutoString hiddenText;
1741
0
  FillBufWithPWChars(&hiddenText, mLastLength);
1742
0
1743
0
  uint32_t start, end;
1744
0
  nsContentUtils::GetSelectionInTextControl(&SelectionRef(),
1745
0
                                            TextEditorRef().GetRoot(),
1746
0
                                            start, end);
1747
0
1748
0
  nsCOMPtr<nsINode> selNode = GetTextNodeAroundSelectionStartContainer();
1749
0
  if (NS_WARN_IF(!selNode)) {
1750
0
    return NS_OK;
1751
0
  }
1752
0
1753
0
  selNode->GetAsText()->ReplaceData(mLastStart, mLastLength, hiddenText,
1754
0
                                    IgnoreErrors());
1755
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1756
0
    return NS_ERROR_EDITOR_DESTROYED;
1757
0
  }
1758
0
  // XXXbz Selection::Collapse/Extend take int32_t, but there are tons of
1759
0
  // callsites... Converting all that is a battle for another day.
1760
0
  DebugOnly<nsresult> rv = SelectionRef().Collapse(selNode, start);
1761
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1762
0
    return NS_ERROR_EDITOR_DESTROYED;
1763
0
  }
1764
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to collapse selection");
1765
0
  if (start != end) {
1766
0
    rv = SelectionRef().Extend(selNode, end);
1767
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1768
0
      return NS_ERROR_EDITOR_DESTROYED;
1769
0
    }
1770
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to extend selection");
1771
0
  }
1772
0
  return NS_OK;
1773
0
}
1774
1775
// static
1776
void
1777
TextEditRules::FillBufWithPWChars(nsAString* aOutString,
1778
                                  int32_t aLength)
1779
0
{
1780
0
  MOZ_ASSERT(aOutString);
1781
0
1782
0
  // change the output to the platform password character
1783
0
  char16_t passwordChar = LookAndFeel::GetPasswordCharacter();
1784
0
1785
0
  aOutString->Truncate();
1786
0
  for (int32_t i = 0; i < aLength; i++) {
1787
0
    aOutString->Append(passwordChar);
1788
0
  }
1789
0
}
1790
1791
template<typename PT, typename CT>
1792
CreateElementResult
1793
TextEditRules::CreateBRInternal(
1794
                 const EditorDOMPointBase<PT, CT>& aPointToInsert,
1795
                 bool aCreateMozBR)
1796
0
{
1797
0
  MOZ_ASSERT(IsEditorDataAvailable());
1798
0
1799
0
  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
1800
0
    return CreateElementResult(NS_ERROR_FAILURE);
1801
0
  }
1802
0
1803
0
  RefPtr<Element> brElement =
1804
0
    TextEditorRef().InsertBrElementWithTransaction(SelectionRef(),
1805
0
                                                   aPointToInsert);
1806
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1807
0
    return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
1808
0
  }
1809
0
  if (NS_WARN_IF(!brElement)) {
1810
0
    return CreateElementResult(NS_ERROR_FAILURE);
1811
0
  }
1812
0
1813
0
  // give it special moz attr
1814
0
  if (!aCreateMozBR) {
1815
0
    return CreateElementResult(brElement.forget());
1816
0
  }
1817
0
1818
0
  // XXX Why do we need to set this attribute with transaction?
1819
0
  nsresult rv =
1820
0
    TextEditorRef().SetAttributeWithTransaction(*brElement, *nsGkAtoms::type,
1821
0
                                                NS_LITERAL_STRING("_moz"));
1822
0
  // XXX Don't we need to remove the new <br> element from the DOM tree
1823
0
  //     in these case?
1824
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1825
0
    return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
1826
0
  }
1827
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1828
0
    return CreateElementResult(NS_ERROR_FAILURE);
1829
0
  }
1830
0
  return CreateElementResult(brElement.forget());
1831
0
}
Unexecuted instantiation: mozilla::CreateNodeResultBase<mozilla::dom::Element> mozilla::TextEditRules::CreateBRInternal<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&, bool)
Unexecuted instantiation: mozilla::CreateNodeResultBase<mozilla::dom::Element> mozilla::TextEditRules::CreateBRInternal<nsINode*, nsIContent*>(mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&, bool)
1832
1833
nsresult
1834
TextEditRules::DocumentModified()
1835
0
{
1836
0
  return NS_ERROR_NOT_IMPLEMENTED;
1837
0
}
1838
1839
bool
1840
TextEditRules::IsPasswordEditor() const
1841
0
{
1842
0
  return mTextEditor ? mTextEditor->IsPasswordEditor() : false;
1843
0
}
1844
1845
bool
1846
TextEditRules::IsSingleLineEditor() const
1847
0
{
1848
0
  return mTextEditor ? mTextEditor->IsSingleLineEditor() : false;
1849
0
}
1850
1851
bool
1852
TextEditRules::IsPlaintextEditor() const
1853
0
{
1854
0
  return mTextEditor ? mTextEditor->IsPlaintextEditor() : false;
1855
0
}
1856
1857
bool
1858
TextEditRules::IsReadonly() const
1859
0
{
1860
0
  return mTextEditor ? mTextEditor->IsReadonly() : false;
1861
0
}
1862
1863
bool
1864
TextEditRules::IsDisabled() const
1865
0
{
1866
0
  return mTextEditor ? mTextEditor->IsDisabled() : false;
1867
0
}
1868
bool
1869
TextEditRules::IsMailEditor() const
1870
0
{
1871
0
  return mTextEditor ? mTextEditor->IsMailEditor() : false;
1872
0
}
1873
1874
bool
1875
TextEditRules::DontEchoPassword() const
1876
0
{
1877
0
  return mTextEditor ? mTextEditor->DontEchoPassword() : false;
1878
0
}
1879
1880
} // namespace mozilla