Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/editor/libeditor/HTMLEditRules.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=2 sw=2 et tw=79: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "HTMLEditRules.h"
8
9
#include <stdlib.h>
10
11
#include "HTMLEditUtils.h"
12
#include "TextEditUtils.h"
13
#include "WSRunObject.h"
14
#include "mozilla/Assertions.h"
15
#include "mozilla/CSSEditUtils.h"
16
#include "mozilla/EditAction.h"
17
#include "mozilla/EditorDOMPoint.h"
18
#include "mozilla/EditorUtils.h"
19
#include "mozilla/HTMLEditor.h"
20
#include "mozilla/MathAlgorithms.h"
21
#include "mozilla/Move.h"
22
#include "mozilla/Preferences.h"
23
#include "mozilla/UniquePtr.h"
24
#include "mozilla/Unused.h"
25
#include "mozilla/dom/Selection.h"
26
#include "mozilla/dom/Element.h"
27
#include "mozilla/dom/RangeBinding.h"
28
#include "mozilla/OwningNonNull.h"
29
#include "mozilla/mozalloc.h"
30
#include "nsAString.h"
31
#include "nsAlgorithm.h"
32
#include "nsCRT.h"
33
#include "nsCRTGlue.h"
34
#include "nsComponentManagerUtils.h"
35
#include "nsContentUtils.h"
36
#include "nsDebug.h"
37
#include "nsError.h"
38
#include "nsGkAtoms.h"
39
#include "nsAtom.h"
40
#include "nsHTMLDocument.h"
41
#include "nsIContent.h"
42
#include "nsIContentIterator.h"
43
#include "nsID.h"
44
#include "nsIFrame.h"
45
#include "nsIHTMLAbsPosEditor.h"
46
#include "nsINode.h"
47
#include "nsLiteralString.h"
48
#include "nsRange.h"
49
#include "nsReadableUtils.h"
50
#include "nsString.h"
51
#include "nsStringFwd.h"
52
#include "nsTArray.h"
53
#include "nsTextNode.h"
54
#include "nsThreadUtils.h"
55
#include "nsUnicharUtils.h"
56
#include <algorithm>
57
58
// Workaround for windows headers
59
#ifdef SetProp
60
#undef SetProp
61
#endif
62
63
class nsISupports;
64
65
namespace mozilla {
66
67
using namespace dom;
68
69
//const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
70
//const static char* kMOZEditorBogusNodeValue="TRUE";
71
72
enum
73
{
74
  kLonely = 0,
75
  kPrevSib = 1,
76
  kNextSib = 2,
77
  kBothSibs = 3
78
};
79
80
/********************************************************
81
 *  first some helpful functors we will use
82
 ********************************************************/
83
84
static bool
85
IsStyleCachePreservingSubAction(EditSubAction aEditSubAction)
86
0
{
87
0
  return aEditSubAction == EditSubAction::eDeleteSelectedContent ||
88
0
         aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
89
0
         aEditSubAction == EditSubAction::eCreateOrChangeList ||
90
0
         aEditSubAction == EditSubAction::eIndent ||
91
0
         aEditSubAction == EditSubAction::eOutdent ||
92
0
         aEditSubAction == EditSubAction::eSetOrClearAlignment ||
93
0
         aEditSubAction == EditSubAction::eCreateOrRemoveBlock ||
94
0
         aEditSubAction == EditSubAction::eRemoveList ||
95
0
         aEditSubAction == EditSubAction::eCreateOrChangeDefinitionList ||
96
0
         aEditSubAction == EditSubAction::eInsertElement ||
97
0
         aEditSubAction == EditSubAction::eInsertQuotation;
98
0
}
99
100
static nsAtom&
101
ParagraphSeparatorElement(ParagraphSeparator separator)
102
0
{
103
0
  switch (separator) {
104
0
    default:
105
0
      MOZ_FALLTHROUGH_ASSERT("Unexpected paragraph separator!");
106
0
107
0
    case ParagraphSeparator::div:
108
0
      return *nsGkAtoms::div;
109
0
110
0
    case ParagraphSeparator::p:
111
0
      return *nsGkAtoms::p;
112
0
113
0
    case ParagraphSeparator::br:
114
0
      return *nsGkAtoms::br;
115
0
  }
116
0
}
117
118
class TableCellAndListItemFunctor final : public BoolDomIterFunctor
119
{
120
public:
121
  // Used to build list of all li's, td's & th's iterator covers
122
  virtual bool operator()(nsINode* aNode) const override
123
0
  {
124
0
    return HTMLEditUtils::IsTableCell(aNode) ||
125
0
           HTMLEditUtils::IsListItem(aNode);
126
0
  }
127
};
128
129
class BRNodeFunctor final : public BoolDomIterFunctor
130
{
131
public:
132
  virtual bool operator()(nsINode* aNode) const override
133
0
  {
134
0
    return aNode->IsHTMLElement(nsGkAtoms::br);
135
0
  }
136
};
137
138
class EmptyEditableFunctor final : public BoolDomIterFunctor
139
{
140
public:
141
  explicit EmptyEditableFunctor(HTMLEditor* aHTMLEditor)
142
    : mHTMLEditor(aHTMLEditor)
143
0
  {}
144
145
  virtual bool operator()(nsINode* aNode) const override
146
0
  {
147
0
    if (mHTMLEditor->IsEditable(aNode) &&
148
0
        (HTMLEditUtils::IsListItem(aNode) ||
149
0
         HTMLEditUtils::IsTableCellOrCaption(*aNode))) {
150
0
      bool bIsEmptyNode;
151
0
      nsresult rv =
152
0
        mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, false, false);
153
0
      NS_ENSURE_SUCCESS(rv, false);
154
0
      if (bIsEmptyNode) {
155
0
        return true;
156
0
      }
157
0
    }
158
0
    return false;
159
0
  }
160
161
protected:
162
  HTMLEditor* mHTMLEditor;
163
};
164
165
/********************************************************
166
 * mozilla::HTMLEditRules
167
 ********************************************************/
168
169
HTMLEditRules::HTMLEditRules()
170
  : mHTMLEditor(nullptr)
171
  , mListenerEnabled(false)
172
  , mReturnInEmptyLIKillsList(false)
173
  , mDidDeleteSelection(false)
174
  , mDidRangedDelete(false)
175
  , mRestoreContentEditableCount(false)
176
  , mJoinOffset(0)
177
0
{
178
0
  mIsHTMLEditRules = true;
179
0
  InitFields();
180
0
}
181
182
void
183
HTMLEditRules::InitFields()
184
0
{
185
0
  mHTMLEditor = nullptr;
186
0
  mDocChangeRange = nullptr;
187
0
  mReturnInEmptyLIKillsList = true;
188
0
  mDidDeleteSelection = false;
189
0
  mDidRangedDelete = false;
190
0
  mRestoreContentEditableCount = false;
191
0
  mUtilRange = nullptr;
192
0
  mJoinOffset = 0;
193
0
  mNewBlock = nullptr;
194
0
  mRangeItem = new RangeItem();
195
0
196
0
  InitStyleCacheArray(mCachedStyles);
197
0
}
198
199
void
200
HTMLEditRules::InitStyleCacheArray(StyleCache aStyleCache[SIZE_STYLE_TABLE])
201
0
{
202
0
  aStyleCache[0] = StyleCache(nsGkAtoms::b, nullptr);
203
0
  aStyleCache[1] = StyleCache(nsGkAtoms::i, nullptr);
204
0
  aStyleCache[2] = StyleCache(nsGkAtoms::u, nullptr);
205
0
  aStyleCache[3] = StyleCache(nsGkAtoms::font, nsGkAtoms::face);
206
0
  aStyleCache[4] = StyleCache(nsGkAtoms::font, nsGkAtoms::size);
207
0
  aStyleCache[5] = StyleCache(nsGkAtoms::font, nsGkAtoms::color);
208
0
  aStyleCache[6] = StyleCache(nsGkAtoms::tt, nullptr);
209
0
  aStyleCache[7] = StyleCache(nsGkAtoms::em, nullptr);
210
0
  aStyleCache[8] = StyleCache(nsGkAtoms::strong, nullptr);
211
0
  aStyleCache[9] = StyleCache(nsGkAtoms::dfn, nullptr);
212
0
  aStyleCache[10] = StyleCache(nsGkAtoms::code, nullptr);
213
0
  aStyleCache[11] = StyleCache(nsGkAtoms::samp, nullptr);
214
0
  aStyleCache[12] = StyleCache(nsGkAtoms::var, nullptr);
215
0
  aStyleCache[13] = StyleCache(nsGkAtoms::cite, nullptr);
216
0
  aStyleCache[14] = StyleCache(nsGkAtoms::abbr, nullptr);
217
0
  aStyleCache[15] = StyleCache(nsGkAtoms::acronym, nullptr);
218
0
  aStyleCache[16] = StyleCache(nsGkAtoms::backgroundColor, nullptr);
219
0
  aStyleCache[17] = StyleCache(nsGkAtoms::sub, nullptr);
220
0
  aStyleCache[18] = StyleCache(nsGkAtoms::sup, nullptr);
221
0
}
222
223
HTMLEditRules::~HTMLEditRules()
224
0
{
225
0
}
226
227
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLEditRules, TextEditRules)
228
229
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules,
230
                                   mDocChangeRange, mUtilRange, mNewBlock,
231
                                   mRangeItem)
232
233
nsresult
234
HTMLEditRules::Init(TextEditor* aTextEditor)
235
0
{
236
0
  if (NS_WARN_IF(!aTextEditor) ||
237
0
      NS_WARN_IF(!aTextEditor->AsHTMLEditor())) {
238
0
    return NS_ERROR_INVALID_ARG;
239
0
  }
240
0
241
0
  InitFields();
242
0
243
0
  mHTMLEditor = aTextEditor->AsHTMLEditor();
244
0
  if (NS_WARN_IF(!mHTMLEditor)) {
245
0
    return NS_ERROR_FAILURE;
246
0
  }
247
0
  Selection* selection = aTextEditor->GetSelection();
248
0
  if (NS_WARN_IF(!selection)) {
249
0
    return NS_ERROR_FAILURE;
250
0
  }
251
0
252
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
253
0
254
0
  nsresult rv = TextEditRules::Init(aTextEditor);
255
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
256
0
    return rv;
257
0
  }
258
0
259
0
  if (NS_WARN_IF(!mHTMLEditor)) {
260
0
    return NS_ERROR_FAILURE;
261
0
  }
262
0
263
0
  // cache any prefs we care about
264
0
  static const char kPrefName[] =
265
0
    "editor.html.typing.returnInEmptyListItemClosesList";
266
0
  nsAutoCString returnInEmptyLIKillsList;
267
0
  Preferences::GetCString(kPrefName, returnInEmptyLIKillsList);
268
0
269
0
  // only when "false", becomes FALSE.  Otherwise (including empty), TRUE.
270
0
  // XXX Why was this pref designed as a string and not bool?
271
0
  mReturnInEmptyLIKillsList = !returnInEmptyLIKillsList.EqualsLiteral("false");
272
0
273
0
  // make a utility range for use by the listenter
274
0
  nsCOMPtr<nsINode> node = HTMLEditorRef().GetRoot();
275
0
  if (!node) {
276
0
    node = HTMLEditorRef().GetDocument();
277
0
    if (NS_WARN_IF(!node)) {
278
0
      return NS_ERROR_FAILURE;
279
0
    }
280
0
  }
281
0
282
0
  mUtilRange = new nsRange(node);
283
0
284
0
  // set up mDocChangeRange to be whole doc
285
0
  // temporarily turn off rules sniffing
286
0
  AutoLockRulesSniffing lockIt(this);
287
0
  if (!mDocChangeRange) {
288
0
    mDocChangeRange = new nsRange(node);
289
0
  }
290
0
291
0
  if (node->IsElement()) {
292
0
    ErrorResult error;
293
0
    mDocChangeRange->SelectNode(*node, error);
294
0
    if (NS_WARN_IF(error.Failed())) {
295
0
      return error.StealNSResult();
296
0
    }
297
0
    nsresult rv = InsertBRElementToEmptyListItemsAndTableCellsInChangedRange();
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 insert <br> elements to empty list items and table cells");
303
0
  }
304
0
305
0
  StartToListenToEditSubActions();
306
0
307
0
  return NS_OK;
308
0
}
309
310
nsresult
311
HTMLEditRules::DetachEditor()
312
0
{
313
0
  EndListeningToEditSubActions();
314
0
  mHTMLEditor = nullptr;
315
0
  return TextEditRules::DetachEditor();
316
0
}
317
318
nsresult
319
HTMLEditRules::BeforeEdit(EditSubAction aEditSubAction,
320
                          nsIEditor::EDirection aDirection)
321
0
{
322
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
323
0
    return NS_ERROR_EDITOR_DESTROYED;
324
0
  }
325
0
326
0
  if (mLockRulesSniffing) {
327
0
    return NS_OK;
328
0
  }
329
0
330
0
  AutoLockRulesSniffing lockIt(this);
331
0
  mDidExplicitlySetInterline = false;
332
0
333
0
  if (!mActionNesting) {
334
0
    mActionNesting++;
335
0
336
0
    // Clear our flag about if just deleted a range
337
0
    mDidRangedDelete = false;
338
0
339
0
    Selection* selection = mHTMLEditor->GetSelection();
340
0
    if (NS_WARN_IF(!selection)) {
341
0
      return NS_ERROR_FAILURE;
342
0
    }
343
0
344
0
    AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
345
0
346
0
    // Remember where our selection was before edit action took place:
347
0
348
0
    // Get the selection location
349
0
    if (!SelectionRef().RangeCount()) {
350
0
      return NS_ERROR_UNEXPECTED;
351
0
    }
352
0
    mRangeItem->StoreRange(SelectionRef().GetRangeAt(0));
353
0
    nsCOMPtr<nsINode> selStartNode = mRangeItem->mStartContainer;
354
0
    nsCOMPtr<nsINode> selEndNode = mRangeItem->mEndContainer;
355
0
356
0
    // Register with range updater to track this as we perturb the doc
357
0
    HTMLEditorRef().mRangeUpdater.RegisterRangeItem(mRangeItem);
358
0
359
0
    // Clear deletion state bool
360
0
    mDidDeleteSelection = false;
361
0
362
0
    // Clear out mDocChangeRange and mUtilRange
363
0
    if (mDocChangeRange) {
364
0
      // Clear out our accounting of what changed
365
0
      mDocChangeRange->Reset();
366
0
    }
367
0
    if (mUtilRange) {
368
0
      // Ditto for mUtilRange.
369
0
      mUtilRange->Reset();
370
0
    }
371
0
372
0
    // Remember current inline styles for deletion and normal insertion ops
373
0
    if (aEditSubAction == EditSubAction::eInsertText ||
374
0
        aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
375
0
        aEditSubAction == EditSubAction::eDeleteSelectedContent ||
376
0
        IsStyleCachePreservingSubAction(aEditSubAction)) {
377
0
      nsCOMPtr<nsINode> selNode =
378
0
        aDirection == nsIEditor::eNext ? selEndNode : selStartNode;
379
0
      nsresult rv = CacheInlineStyles(selNode);
380
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
381
0
        return rv;
382
0
      }
383
0
    }
384
0
385
0
    // Stabilize the document against contenteditable count changes
386
0
    nsHTMLDocument* htmlDoc = HTMLEditorRef().GetHTMLDocument();
387
0
    if (NS_WARN_IF(!htmlDoc)) {
388
0
      return NS_ERROR_FAILURE;
389
0
    }
390
0
    if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
391
0
      htmlDoc->ChangeContentEditableCount(nullptr, +1);
392
0
      mRestoreContentEditableCount = true;
393
0
    }
394
0
395
0
    // Check that selection is in subtree defined by body node
396
0
    nsresult rv = ConfirmSelectionInBody();
397
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
398
0
      return NS_ERROR_EDITOR_DESTROYED;
399
0
    }
400
0
    // Let rules remember the top level action
401
0
    mTopLevelEditSubAction = aEditSubAction;
402
0
  }
403
0
  return NS_OK;
404
0
}
405
406
407
nsresult
408
HTMLEditRules::AfterEdit(EditSubAction aEditSubAction,
409
                         nsIEditor::EDirection aDirection)
410
0
{
411
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
412
0
    return NS_ERROR_EDITOR_DESTROYED;
413
0
  }
414
0
415
0
  if (mLockRulesSniffing) {
416
0
    return NS_OK;
417
0
  }
418
0
419
0
  AutoLockRulesSniffing lockIt(this);
420
0
421
0
  MOZ_ASSERT(mActionNesting > 0);
422
0
  nsresult rv = NS_OK;
423
0
  mActionNesting--;
424
0
  if (!mActionNesting) {
425
0
    Selection* selection = mHTMLEditor->GetSelection();
426
0
    if (NS_WARN_IF(!selection)) {
427
0
      return NS_ERROR_FAILURE;
428
0
    }
429
0
430
0
    AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
431
0
432
0
    // Do all the tricky stuff
433
0
    rv = AfterEditInner(aEditSubAction, aDirection);
434
0
    // Perhaps, we need to do the following jobs even if the editor has been
435
0
    // destroyed since they adjust some states of HTML document but don't
436
0
    // modify the DOM tree nor Selection.
437
0
438
0
    // Free up selectionState range item
439
0
    HTMLEditorRef().mRangeUpdater.DropRangeItem(mRangeItem);
440
0
441
0
    // Reset the contenteditable count to its previous value
442
0
    if (mRestoreContentEditableCount) {
443
0
      nsHTMLDocument* htmlDoc = HTMLEditorRef().GetHTMLDocument();
444
0
      if (NS_WARN_IF(!htmlDoc)) {
445
0
        return NS_ERROR_FAILURE;
446
0
      }
447
0
      if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
448
0
        htmlDoc->ChangeContentEditableCount(nullptr, -1);
449
0
      }
450
0
      mRestoreContentEditableCount = false;
451
0
    }
452
0
  }
453
0
454
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
455
0
    return rv;
456
0
  }
457
0
  return NS_OK;
458
0
}
459
460
nsresult
461
HTMLEditRules::AfterEditInner(EditSubAction aEditSubAction,
462
                              nsIEditor::EDirection aDirection)
463
0
{
464
0
  MOZ_ASSERT(IsEditorDataAvailable());
465
0
466
0
  nsresult rv = ConfirmSelectionInBody();
467
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
468
0
    return NS_ERROR_EDITOR_DESTROYED;
469
0
  }
470
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to normalize Selection");
471
0
  if (aEditSubAction == EditSubAction::eReplaceHeadWithHTMLSource ||
472
0
      aEditSubAction == EditSubAction::eCreateBogusNode) {
473
0
    return NS_OK;
474
0
  }
475
0
476
0
  nsCOMPtr<nsINode> rangeStartContainer, rangeEndContainer;
477
0
  uint32_t rangeStartOffset = 0, rangeEndOffset = 0;
478
0
  // do we have a real range to act on?
479
0
  bool bDamagedRange = false;
480
0
  if (mDocChangeRange) {
481
0
    rangeStartContainer = mDocChangeRange->GetStartContainer();
482
0
    rangeEndContainer = mDocChangeRange->GetEndContainer();
483
0
    rangeStartOffset = mDocChangeRange->StartOffset();
484
0
    rangeEndOffset = mDocChangeRange->EndOffset();
485
0
    if (rangeStartContainer && rangeEndContainer) {
486
0
      bDamagedRange = true;
487
0
    }
488
0
  }
489
0
490
0
  if (bDamagedRange && !((aEditSubAction == EditSubAction::eUndo) ||
491
0
                         (aEditSubAction == EditSubAction::eRedo))) {
492
0
    // don't let any txns in here move the selection around behind our back.
493
0
    // Note that this won't prevent explicit selection setting from working.
494
0
    AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
495
0
496
0
    // expand the "changed doc range" as needed
497
0
    PromoteRange(*mDocChangeRange, aEditSubAction);
498
0
499
0
    // if we did a ranged deletion or handling backspace key, make sure we have
500
0
    // a place to put caret.
501
0
    // Note we only want to do this if the overall operation was deletion,
502
0
    // not if deletion was done along the way for
503
0
    // EditSubAction::eInsertHTMLSource, EditSubAction::eInsertText, etc.
504
0
    // That's why this is here rather than DidDeleteSelection().
505
0
    if (aEditSubAction == EditSubAction::eDeleteSelectedContent &&
506
0
        mDidRangedDelete) {
507
0
      nsresult rv = InsertBRIfNeeded();
508
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
509
0
        return rv;
510
0
      }
511
0
    }
512
0
513
0
    // add in any needed <br>s, and remove any unneeded ones.
514
0
    nsresult rv = InsertBRElementToEmptyListItemsAndTableCellsInChangedRange();
515
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
516
0
      return NS_ERROR_EDITOR_DESTROYED;
517
0
    }
518
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
519
0
      "Failed to insert <br> elements to empty list items and table cells");
520
0
521
0
    // merge any adjacent text nodes
522
0
    if (aEditSubAction != EditSubAction::eInsertText &&
523
0
        aEditSubAction != EditSubAction::eInsertTextComingFromIME) {
524
0
      nsresult rv = HTMLEditorRef().CollapseAdjacentTextNodes(mDocChangeRange);
525
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
526
0
        return NS_ERROR_EDITOR_DESTROYED;
527
0
      }
528
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
529
0
        return rv;
530
0
      }
531
0
    }
532
0
533
0
    // clean up any empty nodes in the selection
534
0
    rv = RemoveEmptyNodesInChangedRange();
535
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
536
0
      return rv;
537
0
    }
538
0
539
0
    // attempt to transform any unneeded nbsp's into spaces after doing various operations
540
0
    if (aEditSubAction == EditSubAction::eInsertText ||
541
0
        aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
542
0
        aEditSubAction == EditSubAction::eDeleteSelectedContent ||
543
0
        aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
544
0
        aEditSubAction == EditSubAction::ePasteHTMLContent ||
545
0
        aEditSubAction == EditSubAction::eInsertHTMLSource) {
546
0
      rv = AdjustWhitespace();
547
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
548
0
        return rv;
549
0
      }
550
0
551
0
      // also do this for original selection endpoints.
552
0
      NS_ENSURE_STATE(mRangeItem->mStartContainer);
553
0
      NS_ENSURE_STATE(mRangeItem->mEndContainer);
554
0
      WSRunObject(&HTMLEditorRef(), mRangeItem->mStartContainer,
555
0
                  mRangeItem->mStartOffset).AdjustWhitespace();
556
0
      // we only need to handle old selection endpoint if it was different from start
557
0
      if (mRangeItem->mStartContainer != mRangeItem->mEndContainer ||
558
0
          mRangeItem->mStartOffset != mRangeItem->mEndOffset) {
559
0
        WSRunObject(&HTMLEditorRef(), mRangeItem->mEndContainer,
560
0
                    mRangeItem->mEndOffset).AdjustWhitespace();
561
0
      }
562
0
    }
563
0
564
0
    // if we created a new block, make sure selection lands in it
565
0
    if (mNewBlock) {
566
0
      rv = PinSelectionToNewBlock();
567
0
      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
568
0
        return NS_ERROR_EDITOR_DESTROYED;
569
0
      }
570
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
571
0
        "Failed to pin selection to the new block");
572
0
      mNewBlock = nullptr;
573
0
    }
574
0
575
0
    // adjust selection for insert text, html paste, and delete actions
576
0
    if (aEditSubAction == EditSubAction::eInsertText ||
577
0
        aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
578
0
        aEditSubAction == EditSubAction::eDeleteSelectedContent ||
579
0
        aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
580
0
        aEditSubAction == EditSubAction::ePasteHTMLContent ||
581
0
        aEditSubAction == EditSubAction::eInsertHTMLSource) {
582
0
      rv = AdjustSelection(aDirection);
583
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
584
0
        return rv;
585
0
      }
586
0
    }
587
0
588
0
    // check for any styles which were removed inappropriately
589
0
    if (aEditSubAction == EditSubAction::eInsertText ||
590
0
        aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
591
0
        aEditSubAction == EditSubAction::eDeleteSelectedContent ||
592
0
        IsStyleCachePreservingSubAction(aEditSubAction)) {
593
0
      HTMLEditorRef().mTypeInState->UpdateSelState(&SelectionRef());
594
0
      rv = ReapplyCachedStyles();
595
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
596
0
        return rv;
597
0
      }
598
0
      ClearCachedStyles();
599
0
    }
600
0
  }
601
0
602
0
  rv = HTMLEditorRef().HandleInlineSpellCheck(aEditSubAction, SelectionRef(),
603
0
                                              mRangeItem->mStartContainer,
604
0
                                              mRangeItem->mStartOffset,
605
0
                                              rangeStartContainer,
606
0
                                              rangeStartOffset,
607
0
                                              rangeEndContainer,
608
0
                                              rangeEndOffset);
609
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
610
0
    return rv;
611
0
  }
612
0
613
0
  // detect empty doc
614
0
  rv = CreateBogusNodeIfNeeded();
615
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
616
0
    return rv;
617
0
  }
618
0
619
0
  // adjust selection HINT if needed
620
0
  if (!mDidExplicitlySetInterline) {
621
0
    CheckInterlinePosition();
622
0
  }
623
0
624
0
  return NS_OK;
625
0
}
626
627
nsresult
628
HTMLEditRules::WillDoAction(Selection* aSelection,
629
                            EditSubActionInfo& aInfo,
630
                            bool* aCancel,
631
                            bool* aHandled)
632
0
{
633
0
  if (NS_WARN_IF(!aSelection)) {
634
0
    return NS_ERROR_INVALID_ARG;
635
0
  }
636
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
637
0
    return NS_ERROR_EDITOR_DESTROYED;
638
0
  }
639
0
640
0
  MOZ_ASSERT(aCancel);
641
0
  MOZ_ASSERT(aHandled);
642
0
643
0
  *aCancel = false;
644
0
  *aHandled = false;
645
0
646
0
  // Deal with actions for which we don't need to check whether the selection is
647
0
  // editable.
648
0
  if (aInfo.mEditSubAction == EditSubAction::eComputeTextToOutput ||
649
0
      aInfo.mEditSubAction == EditSubAction::eUndo ||
650
0
      aInfo.mEditSubAction == EditSubAction::eRedo) {
651
0
    return TextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
652
0
  }
653
0
654
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
655
0
656
0
  // Nothing to do if there's no selection to act on
657
0
  if (NS_WARN_IF(!SelectionRef().RangeCount())) {
658
0
    return NS_OK;
659
0
  }
660
0
661
0
  RefPtr<nsRange> range = SelectionRef().GetRangeAt(0);
662
0
  nsCOMPtr<nsINode> selStartNode = range->GetStartContainer();
663
0
  if (NS_WARN_IF(!selStartNode)) {
664
0
    return NS_ERROR_FAILURE;
665
0
  }
666
0
667
0
  if (!HTMLEditorRef().IsModifiableNode(*selStartNode)) {
668
0
    *aCancel = true;
669
0
    return NS_OK;
670
0
  }
671
0
672
0
  nsCOMPtr<nsINode> selEndNode = range->GetEndContainer();
673
0
  if (NS_WARN_IF(!selEndNode)) {
674
0
    return NS_ERROR_FAILURE;
675
0
  }
676
0
677
0
  if (selStartNode != selEndNode) {
678
0
    if (!HTMLEditorRef().IsModifiableNode(*selEndNode)) {
679
0
      *aCancel = true;
680
0
      return NS_OK;
681
0
    }
682
0
683
0
    nsINode* commonAncestor = range->GetCommonAncestor();
684
0
    if (NS_WARN_IF(!commonAncestor)) {
685
0
      return NS_ERROR_FAILURE;
686
0
    }
687
0
    if (!HTMLEditorRef().IsModifiableNode(*commonAncestor)) {
688
0
      *aCancel = true;
689
0
      return NS_OK;
690
0
    }
691
0
  }
692
0
693
0
  switch (aInfo.mEditSubAction) {
694
0
    case EditSubAction::eInsertText:
695
0
    case EditSubAction::eInsertTextComingFromIME:
696
0
      UndefineCaretBidiLevel();
697
0
      return WillInsertText(aInfo.mEditSubAction, aCancel, aHandled,
698
0
                            aInfo.inString, aInfo.outString,
699
0
                            aInfo.maxLength);
700
0
    case EditSubAction::eInsertHTMLSource:
701
0
      return WillLoadHTML();
702
0
    case EditSubAction::eInsertParagraphSeparator:
703
0
      UndefineCaretBidiLevel();
704
0
      return WillInsertBreak(aCancel, aHandled);
705
0
    case EditSubAction::eDeleteSelectedContent:
706
0
      return WillDeleteSelection(aInfo.collapsedAction, aInfo.stripWrappers,
707
0
                                 aCancel, aHandled);
708
0
    case EditSubAction::eCreateOrChangeList:
709
0
      return WillMakeList(aInfo.blockType, aInfo.entireList,
710
0
                          aInfo.bulletType, aCancel, aHandled);
711
0
    case EditSubAction::eIndent:
712
0
      return WillIndent(aCancel, aHandled);
713
0
    case EditSubAction::eOutdent:
714
0
      return WillOutdent(aCancel, aHandled);
715
0
    case EditSubAction::eSetPositionToAbsolute:
716
0
      return WillAbsolutePosition(aCancel, aHandled);
717
0
    case EditSubAction::eSetPositionToStatic:
718
0
      return WillRemoveAbsolutePosition(aCancel, aHandled);
719
0
    case EditSubAction::eSetOrClearAlignment:
720
0
      return WillAlign(*aInfo.alignType, aCancel, aHandled);
721
0
    case EditSubAction::eCreateOrRemoveBlock:
722
0
      return WillMakeBasicBlock(*aInfo.blockType, aCancel, aHandled);
723
0
    case EditSubAction::eRemoveList: {
724
0
      nsresult rv = WillRemoveList(aCancel, aHandled);
725
0
      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
726
0
          NS_WARN_IF(!CanHandleEditAction())) {
727
0
        return NS_ERROR_EDITOR_DESTROYED;
728
0
      }
729
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
730
0
        return rv;
731
0
      }
732
0
      return NS_OK;
733
0
    }
734
0
    case EditSubAction::eCreateOrChangeDefinitionList:
735
0
      return WillMakeDefListItem(aInfo.blockType,
736
0
                                 aInfo.entireList, aCancel, aHandled);
737
0
    case EditSubAction::eInsertElement: {
738
0
      nsresult rv = WillInsert(aCancel);
739
0
      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
740
0
        return NS_ERROR_EDITOR_DESTROYED;
741
0
      }
742
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
743
0
      return NS_OK;
744
0
    }
745
0
    case EditSubAction::eDecreaseZIndex:
746
0
      return WillRelativeChangeZIndex(-1, aCancel, aHandled);
747
0
    case EditSubAction::eIncreaseZIndex:
748
0
      return WillRelativeChangeZIndex(1, aCancel, aHandled);
749
0
    default:
750
0
      return TextEditRules::WillDoAction(&SelectionRef(), aInfo,
751
0
                                         aCancel, aHandled);
752
0
  }
753
0
}
754
755
nsresult
756
HTMLEditRules::DidDoAction(Selection* aSelection,
757
                           EditSubActionInfo& aInfo,
758
                           nsresult aResult)
759
0
{
760
0
  if (NS_WARN_IF(!aSelection)) {
761
0
    return NS_ERROR_INVALID_ARG;
762
0
  }
763
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
764
0
    return NS_ERROR_EDITOR_DESTROYED;
765
0
  }
766
0
767
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
768
0
769
0
  switch (aInfo.mEditSubAction) {
770
0
    case EditSubAction::eInsertText:
771
0
    case EditSubAction::eInsertParagraphSeparator:
772
0
    case EditSubAction::eInsertTextComingFromIME:
773
0
      return NS_OK;
774
0
    case EditSubAction::eDeleteSelectedContent:
775
0
      return DidDeleteSelection();
776
0
    case EditSubAction::eCreateOrRemoveBlock:
777
0
    case EditSubAction::eIndent:
778
0
    case EditSubAction::eOutdent:
779
0
    case EditSubAction::eSetOrClearAlignment:
780
0
      return DidMakeBasicBlock();
781
0
    case EditSubAction::eSetPositionToAbsolute: {
782
0
      nsresult rv = DidMakeBasicBlock();
783
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
784
0
        return rv;
785
0
      }
786
0
      return DidAbsolutePosition();
787
0
    }
788
0
    default:
789
0
      return TextEditRules::DidDoAction(aSelection, aInfo, aResult);
790
0
  }
791
0
}
792
793
bool
794
HTMLEditRules::DocumentIsEmpty()
795
0
{
796
0
  return !!mBogusNode;
797
0
}
798
799
nsresult
800
HTMLEditRules::GetListState(bool* aMixed,
801
                            bool* aOL,
802
                            bool* aUL,
803
                            bool* aDL)
804
0
{
805
0
  NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
806
0
  *aMixed = false;
807
0
  *aOL = false;
808
0
  *aUL = false;
809
0
  *aDL = false;
810
0
  bool bNonList = false;
811
0
812
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
813
0
    return NS_ERROR_EDITOR_DESTROYED;
814
0
  }
815
0
816
0
  Selection* selection = mHTMLEditor->GetSelection();
817
0
  if (NS_WARN_IF(!selection)) {
818
0
    return NS_ERROR_FAILURE;
819
0
  }
820
0
821
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
822
0
823
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
824
0
  nsresult rv = GetListActionNodes(arrayOfNodes, EntireList::no,
825
0
                                   TouchContent::no);
826
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
827
0
    return rv;
828
0
  }
829
0
830
0
  // Examine list type for nodes in selection.
831
0
  for (const auto& curNode : arrayOfNodes) {
832
0
    if (!curNode->IsElement()) {
833
0
      bNonList = true;
834
0
    } else if (curNode->IsHTMLElement(nsGkAtoms::ul)) {
835
0
      *aUL = true;
836
0
    } else if (curNode->IsHTMLElement(nsGkAtoms::ol)) {
837
0
      *aOL = true;
838
0
    } else if (curNode->IsHTMLElement(nsGkAtoms::li)) {
839
0
      if (dom::Element* parent = curNode->GetParentElement()) {
840
0
        if (parent->IsHTMLElement(nsGkAtoms::ul)) {
841
0
          *aUL = true;
842
0
        } else if (parent->IsHTMLElement(nsGkAtoms::ol)) {
843
0
          *aOL = true;
844
0
        }
845
0
      }
846
0
    } else if (curNode->IsAnyOfHTMLElements(nsGkAtoms::dl,
847
0
                                            nsGkAtoms::dt,
848
0
                                            nsGkAtoms::dd)) {
849
0
      *aDL = true;
850
0
    } else {
851
0
      bNonList = true;
852
0
    }
853
0
  }
854
0
855
0
  // hokey arithmetic with booleans
856
0
  if ((*aUL + *aOL + *aDL + bNonList) > 1) {
857
0
    *aMixed = true;
858
0
  }
859
0
860
0
  return NS_OK;
861
0
}
862
863
nsresult
864
HTMLEditRules::GetListItemState(bool* aMixed,
865
                                bool* aLI,
866
                                bool* aDT,
867
                                bool* aDD)
868
0
{
869
0
  NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
870
0
  *aMixed = false;
871
0
  *aLI = false;
872
0
  *aDT = false;
873
0
  *aDD = false;
874
0
  bool bNonList = false;
875
0
876
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
877
0
    return NS_ERROR_EDITOR_DESTROYED;
878
0
  }
879
0
880
0
  Selection* selection = mHTMLEditor->GetSelection();
881
0
  if (NS_WARN_IF(!selection)) {
882
0
    return NS_ERROR_FAILURE;
883
0
  }
884
0
885
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
886
0
887
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
888
0
  nsresult rv = GetListActionNodes(arrayOfNodes, EntireList::no,
889
0
                                   TouchContent::no);
890
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
891
0
    return rv;
892
0
  }
893
0
894
0
  // examine list type for nodes in selection
895
0
  for (const auto& node : arrayOfNodes) {
896
0
    if (!node->IsElement()) {
897
0
      bNonList = true;
898
0
    } else if (node->IsAnyOfHTMLElements(nsGkAtoms::ul,
899
0
                                         nsGkAtoms::ol,
900
0
                                         nsGkAtoms::li)) {
901
0
      *aLI = true;
902
0
    } else if (node->IsHTMLElement(nsGkAtoms::dt)) {
903
0
      *aDT = true;
904
0
    } else if (node->IsHTMLElement(nsGkAtoms::dd)) {
905
0
      *aDD = true;
906
0
    } else if (node->IsHTMLElement(nsGkAtoms::dl)) {
907
0
      // need to look inside dl and see which types of items it has
908
0
      bool bDT, bDD;
909
0
      GetDefinitionListItemTypes(node->AsElement(), &bDT, &bDD);
910
0
      *aDT |= bDT;
911
0
      *aDD |= bDD;
912
0
    } else {
913
0
      bNonList = true;
914
0
    }
915
0
  }
916
0
917
0
  // hokey arithmetic with booleans
918
0
  if (*aDT + *aDD + bNonList > 1) {
919
0
    *aMixed = true;
920
0
  }
921
0
922
0
  return NS_OK;
923
0
}
924
925
nsresult
926
HTMLEditRules::GetAlignment(bool* aMixed,
927
                            nsIHTMLEditor::EAlignment* aAlign)
928
0
{
929
0
  MOZ_ASSERT(aMixed && aAlign);
930
0
931
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
932
0
    return NS_ERROR_EDITOR_DESTROYED;
933
0
  }
934
0
935
0
  Selection* selection = mHTMLEditor->GetSelection();
936
0
  if (NS_WARN_IF(!selection)) {
937
0
    return NS_ERROR_FAILURE;
938
0
  }
939
0
940
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
941
0
942
0
  // For now, just return first alignment.  We'll lie about if it's mixed.
943
0
  // This is for efficiency given that our current ui doesn't care if it's
944
0
  // mixed.
945
0
  // cmanske: NOT TRUE! We would like to pay attention to mixed state in Format
946
0
  // | Align submenu!
947
0
948
0
  // This routine assumes that alignment is done ONLY via divs
949
0
950
0
  // Default alignment is left
951
0
  *aMixed = false;
952
0
  *aAlign = nsIHTMLEditor::eLeft;
953
0
954
0
  // Get selection location
955
0
  if (NS_WARN_IF(!HTMLEditorRef().GetRoot())) {
956
0
    return NS_ERROR_FAILURE;
957
0
  }
958
0
  OwningNonNull<Element> root = *HTMLEditorRef().GetRoot();
959
0
960
0
  int32_t rootOffset = root->GetParentNode() ?
961
0
                       root->GetParentNode()->ComputeIndexOf(root) : -1;
962
0
963
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
964
0
  if (NS_WARN_IF(!firstRange)) {
965
0
    return NS_ERROR_FAILURE;
966
0
  }
967
0
  EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
968
0
  if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
969
0
    return NS_ERROR_FAILURE;
970
0
  }
971
0
  MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
972
0
973
0
  // Is the selection collapsed?
974
0
  nsCOMPtr<nsINode> nodeToExamine;
975
0
  if (SelectionRef().IsCollapsed() || atStartOfSelection.GetContainerAsText()) {
976
0
    // If selection is collapsed, we want to look at the container of selection
977
0
    // start and its ancestors for divs with alignment on them.  If we are in a
978
0
    // text node, then that is the node of interest.
979
0
    nodeToExamine = atStartOfSelection.GetContainer();
980
0
    if (NS_WARN_IF(!nodeToExamine)) {
981
0
      return NS_ERROR_FAILURE;
982
0
    }
983
0
  } else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
984
0
             atStartOfSelection.Offset() == static_cast<uint32_t>(rootOffset)) {
985
0
    // If we have selected the body, let's look at the first editable node
986
0
    nodeToExamine = HTMLEditorRef().GetNextEditableNode(atStartOfSelection);
987
0
    if (NS_WARN_IF(!nodeToExamine)) {
988
0
      return NS_ERROR_FAILURE;
989
0
    }
990
0
  } else {
991
0
    nsTArray<RefPtr<nsRange>> arrayOfRanges;
992
0
    GetPromotedRanges(arrayOfRanges, EditSubAction::eSetOrClearAlignment);
993
0
994
0
    // Use these ranges to construct a list of nodes to act on.
995
0
    nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
996
0
    nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
997
0
                                       EditSubAction::eSetOrClearAlignment,
998
0
                                       TouchContent::no);
999
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1000
0
      return rv;
1001
0
    }
1002
0
    nodeToExamine = arrayOfNodes.SafeElementAt(0);
1003
0
    if (NS_WARN_IF(!nodeToExamine)) {
1004
0
      return NS_ERROR_FAILURE;
1005
0
    }
1006
0
  }
1007
0
1008
0
  RefPtr<Element> blockParent = HTMLEditorRef().GetBlock(*nodeToExamine);
1009
0
  if (NS_WARN_IF(!blockParent)) {
1010
0
    return NS_ERROR_FAILURE;
1011
0
  }
1012
0
1013
0
  if (HTMLEditorRef().IsCSSEnabled() &&
1014
0
      CSSEditUtils::IsCSSEditableProperty(blockParent, nullptr,
1015
0
                                          nsGkAtoms::align)) {
1016
0
    // We are in CSS mode and we know how to align this element with CSS
1017
0
    nsAutoString value;
1018
0
    // Let's get the value(s) of text-align or margin-left/margin-right
1019
0
    CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSet(
1020
0
      blockParent, nullptr, nsGkAtoms::align, value, CSSEditUtils::eComputed);
1021
0
    if (value.EqualsLiteral("center") ||
1022
0
        value.EqualsLiteral("-moz-center") ||
1023
0
        value.EqualsLiteral("auto auto")) {
1024
0
      *aAlign = nsIHTMLEditor::eCenter;
1025
0
      return NS_OK;
1026
0
    }
1027
0
    if (value.EqualsLiteral("right") ||
1028
0
        value.EqualsLiteral("-moz-right") ||
1029
0
        value.EqualsLiteral("auto 0px")) {
1030
0
      *aAlign = nsIHTMLEditor::eRight;
1031
0
      return NS_OK;
1032
0
    }
1033
0
    if (value.EqualsLiteral("justify")) {
1034
0
      *aAlign = nsIHTMLEditor::eJustify;
1035
0
      return NS_OK;
1036
0
    }
1037
0
    *aAlign = nsIHTMLEditor::eLeft;
1038
0
    return NS_OK;
1039
0
  }
1040
0
1041
0
  // Check up the ladder for divs with alignment
1042
0
  bool isFirstNodeToExamine = true;
1043
0
  for (; nodeToExamine; nodeToExamine = nodeToExamine->GetParentNode()) {
1044
0
    if (!isFirstNodeToExamine &&
1045
0
        nodeToExamine->IsHTMLElement(nsGkAtoms::table)) {
1046
0
      // The node to examine is a table and this is not the first node we
1047
0
      // examine; let's break here to materialize the 'inline-block' behaviour
1048
0
      // of html tables regarding to text alignment
1049
0
      return NS_OK;
1050
0
    }
1051
0
1052
0
    if (CSSEditUtils::IsCSSEditableProperty(nodeToExamine, nullptr,
1053
0
                                            nsGkAtoms::align)) {
1054
0
      nsAutoString value;
1055
0
      CSSEditUtils::GetSpecifiedProperty(*nodeToExamine,
1056
0
                                         *nsGkAtoms::textAlign,
1057
0
                                         value);
1058
0
      if (!value.IsEmpty()) {
1059
0
        if (value.EqualsLiteral("center")) {
1060
0
          *aAlign = nsIHTMLEditor::eCenter;
1061
0
          return NS_OK;
1062
0
        }
1063
0
        if (value.EqualsLiteral("right")) {
1064
0
          *aAlign = nsIHTMLEditor::eRight;
1065
0
          return NS_OK;
1066
0
        }
1067
0
        if (value.EqualsLiteral("justify")) {
1068
0
          *aAlign = nsIHTMLEditor::eJustify;
1069
0
          return NS_OK;
1070
0
        }
1071
0
        if (value.EqualsLiteral("left")) {
1072
0
          *aAlign = nsIHTMLEditor::eLeft;
1073
0
          return NS_OK;
1074
0
        }
1075
0
        // XXX
1076
0
        // text-align: start and end aren't supported yet
1077
0
      }
1078
0
    }
1079
0
1080
0
    if (HTMLEditUtils::SupportsAlignAttr(*nodeToExamine)) {
1081
0
      // Check for alignment
1082
0
      nsAutoString typeAttrVal;
1083
0
      nodeToExamine->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::align,
1084
0
                                          typeAttrVal);
1085
0
      ToLowerCase(typeAttrVal);
1086
0
      if (!typeAttrVal.IsEmpty()) {
1087
0
        if (typeAttrVal.EqualsLiteral("center")) {
1088
0
          *aAlign = nsIHTMLEditor::eCenter;
1089
0
        } else if (typeAttrVal.EqualsLiteral("right")) {
1090
0
          *aAlign = nsIHTMLEditor::eRight;
1091
0
        } else if (typeAttrVal.EqualsLiteral("justify")) {
1092
0
          *aAlign = nsIHTMLEditor::eJustify;
1093
0
        } else {
1094
0
          *aAlign = nsIHTMLEditor::eLeft;
1095
0
        }
1096
0
        return NS_OK;
1097
0
      }
1098
0
    }
1099
0
    isFirstNodeToExamine = false;
1100
0
  }
1101
0
  return NS_OK;
1102
0
}
1103
1104
static nsAtom&
1105
MarginPropertyAtomForIndent(nsINode& aNode)
1106
0
{
1107
0
  nsAutoString direction;
1108
0
  CSSEditUtils::GetComputedProperty(aNode, *nsGkAtoms::direction, direction);
1109
0
  return direction.EqualsLiteral("rtl") ?
1110
0
    *nsGkAtoms::marginRight : *nsGkAtoms::marginLeft;
1111
0
}
1112
1113
nsresult
1114
HTMLEditRules::GetParagraphState(bool* aMixed,
1115
                                 nsAString& outFormat)
1116
0
{
1117
0
  if (NS_WARN_IF(!aMixed)) {
1118
0
    return NS_ERROR_INVALID_ARG;
1119
0
  }
1120
0
1121
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
1122
0
    return NS_ERROR_EDITOR_DESTROYED;
1123
0
  }
1124
0
1125
0
  // This routine is *heavily* tied to our ui choices in the paragraph
1126
0
  // style popup.  I can't see a way around that.
1127
0
  *aMixed = true;
1128
0
  outFormat.Truncate(0);
1129
0
1130
0
  Selection* selection = mHTMLEditor->GetSelection();
1131
0
  if (NS_WARN_IF(!selection)) {
1132
0
    return NS_ERROR_FAILURE;
1133
0
  }
1134
0
1135
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
1136
0
1137
0
  bool bMixed = false;
1138
0
  // using "x" as an uninitialized value, since "" is meaningful
1139
0
  nsAutoString formatStr(NS_LITERAL_STRING("x"));
1140
0
1141
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
1142
0
  nsresult rv = GetParagraphFormatNodes(arrayOfNodes);
1143
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1144
0
    return rv;
1145
0
  }
1146
0
1147
0
  // post process list.  We need to replace any block nodes that are not format
1148
0
  // nodes with their content.  This is so we only have to look "up" the hierarchy
1149
0
  // to find format nodes, instead of both up and down.
1150
0
  for (int32_t i = arrayOfNodes.Length() - 1; i >= 0; i--) {
1151
0
    auto& curNode = arrayOfNodes[i];
1152
0
    nsAutoString format;
1153
0
    // if it is a known format node we have it easy
1154
0
    if (IsBlockNode(curNode) && !HTMLEditUtils::IsFormatNode(curNode)) {
1155
0
      // arrayOfNodes.RemoveObject(curNode);
1156
0
      rv = AppendInnerFormatNodes(arrayOfNodes, curNode);
1157
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1158
0
        return rv;
1159
0
      }
1160
0
    }
1161
0
  }
1162
0
1163
0
  // we might have an empty node list.  if so, find selection parent
1164
0
  // and put that on the list
1165
0
  if (arrayOfNodes.IsEmpty()) {
1166
0
    EditorRawDOMPoint selectionStartPoint(
1167
0
                        EditorBase::GetStartPoint(&SelectionRef()));
1168
0
    if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
1169
0
      return NS_ERROR_FAILURE;
1170
0
    }
1171
0
    arrayOfNodes.AppendElement(*selectionStartPoint.GetContainer());
1172
0
  }
1173
0
1174
0
  // remember root node
1175
0
  Element* rootElement = HTMLEditorRef().GetRoot();
1176
0
  if (NS_WARN_IF(!rootElement)) {
1177
0
    return NS_ERROR_FAILURE;
1178
0
  }
1179
0
1180
0
  // loop through the nodes in selection and examine their paragraph format
1181
0
  for (auto& curNode : Reversed(arrayOfNodes)) {
1182
0
    nsAutoString format;
1183
0
    // if it is a known format node we have it easy
1184
0
    if (HTMLEditUtils::IsFormatNode(curNode)) {
1185
0
      GetFormatString(curNode, format);
1186
0
    } else if (IsBlockNode(curNode)) {
1187
0
      // this is a div or some other non-format block.
1188
0
      // we should ignore it.  Its children were appended to this list
1189
0
      // by AppendInnerFormatNodes() call above.  We will get needed
1190
0
      // info when we examine them instead.
1191
0
      continue;
1192
0
    } else {
1193
0
      nsINode* node = curNode->GetParentNode();
1194
0
      while (node) {
1195
0
        if (node == rootElement) {
1196
0
          format.Truncate(0);
1197
0
          break;
1198
0
        } else if (HTMLEditUtils::IsFormatNode(node)) {
1199
0
          GetFormatString(node, format);
1200
0
          break;
1201
0
        }
1202
0
        // else keep looking up
1203
0
        node = node->GetParentNode();
1204
0
      }
1205
0
    }
1206
0
1207
0
    // if this is the first node, we've found, remember it as the format
1208
0
    if (formatStr.EqualsLiteral("x")) {
1209
0
      formatStr = format;
1210
0
    }
1211
0
    // else make sure it matches previously found format
1212
0
    else if (format != formatStr) {
1213
0
      bMixed = true;
1214
0
      break;
1215
0
    }
1216
0
  }
1217
0
1218
0
  *aMixed = bMixed;
1219
0
  outFormat = formatStr;
1220
0
  return NS_OK;
1221
0
}
1222
1223
nsresult
1224
HTMLEditRules::AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
1225
                                      nsINode* aNode)
1226
0
{
1227
0
  MOZ_ASSERT(aNode);
1228
0
1229
0
  // we only need to place any one inline inside this node onto
1230
0
  // the list.  They are all the same for purposes of determining
1231
0
  // paragraph style.  We use foundInline to track this as we are
1232
0
  // going through the children in the loop below.
1233
0
  bool foundInline = false;
1234
0
  for (nsIContent* child = aNode->GetFirstChild();
1235
0
       child;
1236
0
       child = child->GetNextSibling()) {
1237
0
    bool isBlock = IsBlockNode(*child);
1238
0
    bool isFormat = HTMLEditUtils::IsFormatNode(child);
1239
0
    if (isBlock && !isFormat) {
1240
0
      // if it's a div, etc., recurse
1241
0
      AppendInnerFormatNodes(aArray, child);
1242
0
    } else if (isFormat) {
1243
0
      aArray.AppendElement(*child);
1244
0
    } else if (!foundInline) {
1245
0
      // if this is the first inline we've found, use it
1246
0
      foundInline = true;
1247
0
      aArray.AppendElement(*child);
1248
0
    }
1249
0
  }
1250
0
  return NS_OK;
1251
0
}
1252
1253
nsresult
1254
HTMLEditRules::GetFormatString(nsINode* aNode,
1255
                               nsAString& outFormat)
1256
0
{
1257
0
  NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1258
0
1259
0
  if (HTMLEditUtils::IsFormatNode(aNode)) {
1260
0
    aNode->NodeInfo()->NameAtom()->ToString(outFormat);
1261
0
  } else {
1262
0
    outFormat.Truncate();
1263
0
  }
1264
0
  return NS_OK;
1265
0
}
1266
1267
nsresult
1268
HTMLEditRules::WillInsert(bool* aCancel)
1269
0
{
1270
0
  MOZ_ASSERT(IsEditorDataAvailable());
1271
0
1272
0
  nsresult rv = TextEditRules::WillInsert(aCancel);
1273
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1274
0
    return rv;
1275
0
  }
1276
0
1277
0
  // Adjust selection to prevent insertion after a moz-BR.  This next only
1278
0
  // works for collapsed selections right now, because selection is a pain to
1279
0
  // work with when not collapsed.  (no good way to extend start or end of
1280
0
  // selection), so we ignore those types of selections.
1281
0
  if (!SelectionRef().IsCollapsed()) {
1282
0
    return NS_OK;
1283
0
  }
1284
0
1285
0
  // If we are after a mozBR in the same block, then move selection to be
1286
0
  // before it
1287
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
1288
0
  if (NS_WARN_IF(!firstRange)) {
1289
0
    return NS_ERROR_FAILURE;
1290
0
  }
1291
0
1292
0
  EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
1293
0
  if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
1294
0
    return NS_ERROR_FAILURE;
1295
0
  }
1296
0
  MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
1297
0
1298
0
  // Get prior node
1299
0
  nsCOMPtr<nsIContent> priorNode =
1300
0
    HTMLEditorRef().GetPreviousEditableHTMLNode(atStartOfSelection);
1301
0
  if (priorNode && TextEditUtils::IsMozBR(priorNode)) {
1302
0
    RefPtr<Element> block1 =
1303
0
      HTMLEditorRef().GetBlock(*atStartOfSelection.GetContainer());
1304
0
    RefPtr<Element> block2 = HTMLEditorRef().GetBlockNodeParent(priorNode);
1305
0
1306
0
    if (block1 && block1 == block2) {
1307
0
      // If we are here then the selection is right after a mozBR that is in
1308
0
      // the same block as the selection.  We need to move the selection start
1309
0
      // to be before the mozBR.
1310
0
      EditorRawDOMPoint point(priorNode);
1311
0
      ErrorResult error;
1312
0
      SelectionRef().Collapse(point, error);
1313
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
1314
0
        error.SuppressException();
1315
0
        return NS_ERROR_EDITOR_DESTROYED;
1316
0
      }
1317
0
      if (NS_WARN_IF(error.Failed())) {
1318
0
        return error.StealNSResult();
1319
0
      }
1320
0
    }
1321
0
  }
1322
0
1323
0
  if (mDidDeleteSelection &&
1324
0
      (mTopLevelEditSubAction == EditSubAction::eInsertText ||
1325
0
       mTopLevelEditSubAction == EditSubAction::eInsertTextComingFromIME ||
1326
0
       mTopLevelEditSubAction == EditSubAction::eDeleteSelectedContent)) {
1327
0
    nsresult rv = ReapplyCachedStyles();
1328
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1329
0
      return rv;
1330
0
    }
1331
0
  }
1332
0
  // For most actions we want to clear the cached styles, but there are
1333
0
  // exceptions
1334
0
  if (!IsStyleCachePreservingSubAction(mTopLevelEditSubAction)) {
1335
0
    ClearCachedStyles();
1336
0
  }
1337
0
  return NS_OK;
1338
0
}
1339
1340
nsresult
1341
HTMLEditRules::WillInsertText(EditSubAction aEditSubAction,
1342
                              bool* aCancel,
1343
                              bool* aHandled,
1344
                              const nsAString* inString,
1345
                              nsAString* outString,
1346
                              int32_t aMaxLength)
1347
0
{
1348
0
  MOZ_ASSERT(IsEditorDataAvailable());
1349
0
1350
0
  if (NS_WARN_IF(!aCancel) ||
1351
0
      NS_WARN_IF(!aHandled)) {
1352
0
    return NS_ERROR_NULL_POINTER;
1353
0
  }
1354
0
1355
0
  // initialize out param
1356
0
  *aCancel = false;
1357
0
  *aHandled = true;
1358
0
  // If the selection isn't collapsed, delete it.  Don't delete existing inline
1359
0
  // tags, because we're hopefully going to insert text (bug 787432).
1360
0
  if (!SelectionRef().IsCollapsed()) {
1361
0
    nsresult rv =
1362
0
      HTMLEditorRef().DeleteSelectionAsSubAction(nsIEditor::eNone,
1363
0
                                                 nsIEditor::eNoStrip);
1364
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1365
0
      return NS_ERROR_EDITOR_DESTROYED;
1366
0
    }
1367
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1368
0
      return rv;
1369
0
    }
1370
0
  }
1371
0
1372
0
  // FYI: Ignore cancel result of WillInsert().
1373
0
  nsresult rv = WillInsert();
1374
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
1375
0
    return NS_ERROR_EDITOR_DESTROYED;
1376
0
  }
1377
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
1378
0
1379
0
  // we need to get the doc
1380
0
  nsCOMPtr<nsIDocument> doc = HTMLEditorRef().GetDocument();
1381
0
  if (NS_WARN_IF(!doc)) {
1382
0
    return NS_ERROR_FAILURE;
1383
0
  }
1384
0
1385
0
  // for every property that is set, insert a new inline style node
1386
0
  rv = CreateStyleForInsertText(*doc);
1387
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1388
0
    return rv;
1389
0
  }
1390
0
1391
0
  // get the (collapsed) selection location
1392
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
1393
0
  if (NS_WARN_IF(!firstRange)) {
1394
0
    return NS_ERROR_FAILURE;
1395
0
  }
1396
0
  EditorDOMPoint pointToInsert(firstRange->StartRef());
1397
0
  if (NS_WARN_IF(!pointToInsert.IsSet())) {
1398
0
    return NS_ERROR_FAILURE;
1399
0
  }
1400
0
  MOZ_ASSERT(pointToInsert.IsSetAndValid());
1401
0
1402
0
  // dont put text in places that can't have it
1403
0
  if (!EditorBase::IsTextNode(pointToInsert.GetContainer()) &&
1404
0
      !HTMLEditorRef().CanContainTag(*pointToInsert.GetContainer(),
1405
0
                                     *nsGkAtoms::textTagName)) {
1406
0
    return NS_ERROR_FAILURE;
1407
0
  }
1408
0
1409
0
  if (aEditSubAction == EditSubAction::eInsertTextComingFromIME) {
1410
0
    // Right now the WSRunObject code bails on empty strings, but IME needs
1411
0
    // the InsertTextWithTransaction() call to still happen since empty strings
1412
0
    // are meaningful there.
1413
0
    // If there is one or more IME selections, its minimum offset should be
1414
0
    // the insertion point.
1415
0
    int32_t IMESelectionOffset =
1416
0
      HTMLEditorRef().GetIMESelectionStartOffsetIn(
1417
0
                        pointToInsert.GetContainer());
1418
0
    if (IMESelectionOffset >= 0) {
1419
0
      pointToInsert.Set(pointToInsert.GetContainer(), IMESelectionOffset);
1420
0
    }
1421
0
1422
0
    if (inString->IsEmpty()) {
1423
0
      rv = HTMLEditorRef().InsertTextWithTransaction(
1424
0
                             *doc, *inString, EditorRawDOMPoint(pointToInsert));
1425
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
1426
0
        return NS_ERROR_EDITOR_DESTROYED;
1427
0
      }
1428
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1429
0
        return rv;
1430
0
      }
1431
0
      return NS_OK;
1432
0
    }
1433
0
1434
0
    WSRunObject wsObj(&HTMLEditorRef(), pointToInsert);
1435
0
    rv = wsObj.InsertText(*doc, *inString, pointToInsert);
1436
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1437
0
      return NS_ERROR_EDITOR_DESTROYED;
1438
0
    }
1439
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1440
0
      return rv;
1441
0
    }
1442
0
    return NS_OK;
1443
0
  }
1444
0
1445
0
  // aEditSubAction == kInsertText
1446
0
1447
0
  // find where we are
1448
0
  EditorDOMPoint currentPoint(pointToInsert);
1449
0
1450
0
  // is our text going to be PREformatted?
1451
0
  // We remember this so that we know how to handle tabs.
1452
0
  bool isPRE = EditorBase::IsPreformatted(pointToInsert.GetContainer());
1453
0
1454
0
  // turn off the edit listener: we know how to
1455
0
  // build the "doc changed range" ourselves, and it's
1456
0
  // must faster to do it once here than to track all
1457
0
  // the changes one at a time.
1458
0
  AutoLockListener lockit(&mListenerEnabled);
1459
0
1460
0
  // don't change my selection in subtransactions
1461
0
  AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
1462
0
  nsAutoString tString(*inString);
1463
0
  const char16_t *unicodeBuf = tString.get();
1464
0
  int32_t pos = 0;
1465
0
  NS_NAMED_LITERAL_STRING(newlineStr, LFSTR);
1466
0
1467
0
  {
1468
0
    AutoTrackDOMPoint tracker(HTMLEditorRef().mRangeUpdater, &pointToInsert);
1469
0
1470
0
    // for efficiency, break out the pre case separately.  This is because
1471
0
    // its a lot cheaper to search the input string for only newlines than
1472
0
    // it is to search for both tabs and newlines.
1473
0
    if (isPRE || IsPlaintextEditor()) {
1474
0
      while (unicodeBuf && pos != -1 &&
1475
0
             pos < static_cast<int32_t>(inString->Length())) {
1476
0
        int32_t oldPos = pos;
1477
0
        int32_t subStrLen;
1478
0
        pos = tString.FindChar(nsCRT::LF, oldPos);
1479
0
1480
0
        if (pos != -1) {
1481
0
          subStrLen = pos - oldPos;
1482
0
          // if first char is newline, then use just it
1483
0
          if (!subStrLen) {
1484
0
            subStrLen = 1;
1485
0
          }
1486
0
        } else {
1487
0
          subStrLen = tString.Length() - oldPos;
1488
0
          pos = tString.Length();
1489
0
        }
1490
0
1491
0
        nsDependentSubstring subStr(tString, oldPos, subStrLen);
1492
0
1493
0
        // is it a return?
1494
0
        if (subStr.Equals(newlineStr)) {
1495
0
          RefPtr<Element> brElement =
1496
0
            HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
1497
0
                                                           currentPoint,
1498
0
                                                           nsIEditor::eNone);
1499
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
1500
0
            return NS_ERROR_EDITOR_DESTROYED;
1501
0
          }
1502
0
          if (NS_WARN_IF(!brElement)) {
1503
0
            return NS_ERROR_FAILURE;
1504
0
          }
1505
0
          pos++;
1506
0
          if (brElement->GetNextSibling()) {
1507
0
            pointToInsert.Set(brElement->GetNextSibling());
1508
0
          } else {
1509
0
            pointToInsert.SetToEndOf(currentPoint.GetContainer());
1510
0
          }
1511
0
          // XXX In most cases, pointToInsert and currentPoint are same here.
1512
0
          //     But if the <br> element has been moved to different point by
1513
0
          //     mutation observer, those points become different.
1514
0
          currentPoint.Set(brElement);
1515
0
          DebugOnly<bool> advanced = currentPoint.AdvanceOffset();
1516
0
          NS_WARNING_ASSERTION(advanced,
1517
0
            "Failed to advance offset after the new <br> element");
1518
0
          NS_WARNING_ASSERTION(currentPoint == pointToInsert,
1519
0
            "Perhaps, <br> element position has been moved to different point "
1520
0
            "by mutation observer");
1521
0
        } else {
1522
0
          EditorRawDOMPoint pointAfterInsertedString;
1523
0
          rv = HTMLEditorRef().InsertTextWithTransaction(
1524
0
                                 *doc, subStr,
1525
0
                                 EditorRawDOMPoint(currentPoint),
1526
0
                                 &pointAfterInsertedString);
1527
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
1528
0
            return NS_ERROR_EDITOR_DESTROYED;
1529
0
          }
1530
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
1531
0
            return rv;
1532
0
          }
1533
0
          currentPoint = pointAfterInsertedString;
1534
0
          pointToInsert = pointAfterInsertedString;
1535
0
        }
1536
0
      }
1537
0
    } else {
1538
0
      NS_NAMED_LITERAL_STRING(tabStr, "\t");
1539
0
      NS_NAMED_LITERAL_STRING(spacesStr, "    ");
1540
0
      char specialChars[] = {TAB, nsCRT::LF, 0};
1541
0
      while (unicodeBuf && pos != -1 &&
1542
0
             pos < static_cast<int32_t>(inString->Length())) {
1543
0
        int32_t oldPos = pos;
1544
0
        int32_t subStrLen;
1545
0
        pos = tString.FindCharInSet(specialChars, oldPos);
1546
0
1547
0
        if (pos != -1) {
1548
0
          subStrLen = pos - oldPos;
1549
0
          // if first char is newline, then use just it
1550
0
          if (!subStrLen) {
1551
0
            subStrLen = 1;
1552
0
          }
1553
0
        } else {
1554
0
          subStrLen = tString.Length() - oldPos;
1555
0
          pos = tString.Length();
1556
0
        }
1557
0
1558
0
        nsDependentSubstring subStr(tString, oldPos, subStrLen);
1559
0
        WSRunObject wsObj(&HTMLEditorRef(), currentPoint);
1560
0
1561
0
        // is it a tab?
1562
0
        if (subStr.Equals(tabStr)) {
1563
0
          EditorRawDOMPoint pointAfterInsertedSpaces;
1564
0
          rv = wsObj.InsertText(*doc, spacesStr, currentPoint,
1565
0
                                &pointAfterInsertedSpaces);
1566
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
1567
0
            return NS_ERROR_EDITOR_DESTROYED;
1568
0
          }
1569
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
1570
0
            return rv;
1571
0
          }
1572
0
          pos++;
1573
0
          currentPoint = pointAfterInsertedSpaces;
1574
0
          pointToInsert = pointAfterInsertedSpaces;
1575
0
        }
1576
0
        // is it a return?
1577
0
        else if (subStr.Equals(newlineStr)) {
1578
0
          RefPtr<Element> newBRElement =
1579
0
            wsObj.InsertBreak(SelectionRef(), currentPoint, nsIEditor::eNone);
1580
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
1581
0
            return NS_ERROR_EDITOR_DESTROYED;
1582
0
          }
1583
0
          if (NS_WARN_IF(!newBRElement)) {
1584
0
            return NS_ERROR_FAILURE;
1585
0
          }
1586
0
          pos++;
1587
0
          if (newBRElement->GetNextSibling()) {
1588
0
            pointToInsert.Set(newBRElement->GetNextSibling());
1589
0
          } else {
1590
0
            pointToInsert.SetToEndOf(currentPoint.GetContainer());
1591
0
          }
1592
0
          currentPoint.Set(newBRElement);
1593
0
          DebugOnly<bool> advanced = currentPoint.AdvanceOffset();
1594
0
          NS_WARNING_ASSERTION(advanced,
1595
0
            "Failed to advance offset to after the new <br> node");
1596
0
          // XXX If the newBRElement has been moved or removed by mutation
1597
0
          //     observer, we hit this assert.  We need to check if
1598
0
          //     newBRElement is in expected point, though, we must have
1599
0
          //     a lot of same bugs...
1600
0
          NS_WARNING_ASSERTION(currentPoint == pointToInsert,
1601
0
            "Perhaps, newBRElement has been moved or removed unexpectedly");
1602
0
        } else {
1603
0
          EditorRawDOMPoint pointAfterInsertedString;
1604
0
          rv = wsObj.InsertText(*doc, subStr, currentPoint,
1605
0
                                &pointAfterInsertedString);
1606
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
1607
0
            return NS_ERROR_EDITOR_DESTROYED;
1608
0
          }
1609
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
1610
0
            return rv;
1611
0
          }
1612
0
          currentPoint = pointAfterInsertedString;
1613
0
          pointToInsert = pointAfterInsertedString;
1614
0
        }
1615
0
      }
1616
0
    }
1617
0
1618
0
    // After this block, pointToInsert is updated by AutoTrackDOMPoint.
1619
0
  }
1620
0
1621
0
  IgnoredErrorResult ignoredError;
1622
0
  SelectionRef().SetInterlinePosition(false, ignoredError);
1623
0
  NS_WARNING_ASSERTION(!ignoredError.Failed(),
1624
0
    "Failed to unset interline position");
1625
0
1626
0
  if (currentPoint.IsSet()) {
1627
0
    IgnoredErrorResult ignoredError;
1628
0
    SelectionRef().Collapse(currentPoint, ignoredError);
1629
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1630
0
      return NS_ERROR_EDITOR_DESTROYED;
1631
0
    }
1632
0
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
1633
0
      "Failed to collapse at current point");
1634
0
  }
1635
0
1636
0
  // manually update the doc changed range so that AfterEdit will clean up
1637
0
  // the correct portion of the document.
1638
0
  if (!mDocChangeRange) {
1639
0
    mDocChangeRange = new nsRange(pointToInsert.GetContainer());
1640
0
  }
1641
0
1642
0
  if (currentPoint.IsSet()) {
1643
0
    rv = mDocChangeRange->SetStartAndEnd(pointToInsert, currentPoint);
1644
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1645
0
      return rv;
1646
0
    }
1647
0
    return NS_OK;
1648
0
  }
1649
0
1650
0
  rv = mDocChangeRange->CollapseTo(pointToInsert);
1651
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1652
0
    return rv;
1653
0
  }
1654
0
  return NS_OK;
1655
0
}
1656
1657
nsresult
1658
HTMLEditRules::WillLoadHTML()
1659
0
{
1660
0
  MOZ_ASSERT(IsEditorDataAvailable());
1661
0
1662
0
  // Delete mBogusNode if it exists. If we really need one,
1663
0
  // it will be added during post-processing in AfterEditInner().
1664
0
1665
0
  if (mBogusNode) {
1666
0
    DebugOnly<nsresult> rv =
1667
0
      HTMLEditorRef().DeleteNodeWithTransaction(*mBogusNode);
1668
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1669
0
      return NS_ERROR_EDITOR_DESTROYED;
1670
0
    }
1671
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1672
0
      "Failed to remove the bogus node");
1673
0
    mBogusNode = nullptr;
1674
0
  }
1675
0
1676
0
  return NS_OK;
1677
0
}
1678
1679
bool
1680
HTMLEditRules::CanContainParagraph(Element& aElement) const
1681
0
{
1682
0
  MOZ_ASSERT(IsEditorDataAvailable());
1683
0
1684
0
  if (HTMLEditorRef().CanContainTag(aElement, *nsGkAtoms::p)) {
1685
0
    return true;
1686
0
  }
1687
0
1688
0
  // Even if the element cannot have a <p> element as a child, it can contain
1689
0
  // <p> element as a descendant if it's one of the following elements.
1690
0
  if (aElement.IsAnyOfHTMLElements(nsGkAtoms::ol,
1691
0
                                   nsGkAtoms::ul,
1692
0
                                   nsGkAtoms::dl,
1693
0
                                   nsGkAtoms::table,
1694
0
                                   nsGkAtoms::thead,
1695
0
                                   nsGkAtoms::tbody,
1696
0
                                   nsGkAtoms::tfoot,
1697
0
                                   nsGkAtoms::tr)) {
1698
0
    return true;
1699
0
  }
1700
0
1701
0
  // XXX Otherwise, Chromium checks the CSS box is a block, but we don't do it
1702
0
  //     for now.
1703
0
  return false;
1704
0
}
1705
1706
nsresult
1707
HTMLEditRules::WillInsertBreak(bool* aCancel,
1708
                               bool* aHandled)
1709
0
{
1710
0
  MOZ_ASSERT(IsEditorDataAvailable());
1711
0
1712
0
  MOZ_ASSERT(aCancel && aHandled);
1713
0
  *aCancel = false;
1714
0
  *aHandled = false;
1715
0
1716
0
  // If the selection isn't collapsed, delete it.
1717
0
  if (!SelectionRef().IsCollapsed()) {
1718
0
    nsresult rv =
1719
0
      HTMLEditorRef().DeleteSelectionAsSubAction(nsIEditor::eNone,
1720
0
                                                 nsIEditor::eStrip);
1721
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1722
0
      return NS_ERROR_EDITOR_DESTROYED;
1723
0
    }
1724
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1725
0
      return rv;
1726
0
    }
1727
0
  }
1728
0
1729
0
  // FYI: Ignore cancel result of WillInsert().
1730
0
  nsresult rv = WillInsert();
1731
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
1732
0
    return NS_ERROR_EDITOR_DESTROYED;
1733
0
  }
1734
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
1735
0
1736
0
  // Split any mailcites in the way.  Should we abort this if we encounter
1737
0
  // table cell boundaries?
1738
0
  if (IsMailEditor()) {
1739
0
    nsresult rv = SplitMailCites(aHandled);
1740
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1741
0
      return rv;
1742
0
    }
1743
0
    if (*aHandled) {
1744
0
      return NS_OK;
1745
0
    }
1746
0
  }
1747
0
1748
0
  // Smart splitting rules
1749
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
1750
0
  if (NS_WARN_IF(!firstRange)) {
1751
0
    return NS_ERROR_FAILURE;
1752
0
  }
1753
0
1754
0
  EditorDOMPoint atStartOfSelection(firstRange->StartRef());
1755
0
  if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
1756
0
    return NS_ERROR_FAILURE;
1757
0
  }
1758
0
  MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
1759
0
1760
0
  // Do nothing if the node is read-only
1761
0
  if (!HTMLEditorRef().IsModifiableNode(*atStartOfSelection.GetContainer())) {
1762
0
    *aCancel = true;
1763
0
    return NS_OK;
1764
0
  }
1765
0
1766
0
  // If the active editing host is an inline element, or if the active editing
1767
0
  // host is the block parent itself and we're configured to use <br> as a
1768
0
  // paragraph separator, just append a <br>.
1769
0
  RefPtr<Element> host = HTMLEditorRef().GetActiveEditingHost();
1770
0
  if (NS_WARN_IF(!host)) {
1771
0
    return NS_ERROR_FAILURE;
1772
0
  }
1773
0
1774
0
  // Look for the nearest parent block.  However, don't return error even if
1775
0
  // there is no block parent here because in such case, i.e., editing host
1776
0
  // is an inline element, we should insert <br> simply.
1777
0
  RefPtr<Element> blockParent =
1778
0
    HTMLEditor::GetBlock(*atStartOfSelection.GetContainer(), host);
1779
0
1780
0
  ParagraphSeparator separator = HTMLEditorRef().GetDefaultParagraphSeparator();
1781
0
  bool insertBRElement;
1782
0
  // If there is no block parent in the editing host, i.e., the editing host
1783
0
  // itself is also a non-block element, we should insert a <br> element.
1784
0
  if (!blockParent) {
1785
0
    // XXX Chromium checks if the CSS box of the editing host is block.
1786
0
    insertBRElement = true;
1787
0
  }
1788
0
  // If only the editing host is block, and the default paragraph separator
1789
0
  // is <br> or the editing host cannot contain a <p> element, we should
1790
0
  // insert a <br> element.
1791
0
  else if (host == blockParent) {
1792
0
    insertBRElement =
1793
0
      separator == ParagraphSeparator::br || !CanContainParagraph(*host);
1794
0
  }
1795
0
  // If the nearest block parent is a single-line container declared in
1796
0
  // the execCommand spec and not the editing host, we should separate the
1797
0
  // block even if the default paragraph separator is <br> element.
1798
0
  else if (HTMLEditUtils::IsSingleLineContainer(*blockParent)) {
1799
0
    insertBRElement = false;
1800
0
  }
1801
0
  // Otherwise, unless there is no block ancestor which can contain <p>
1802
0
  // element, we shouldn't insert a <br> element here.
1803
0
  else {
1804
0
    insertBRElement = true;
1805
0
    for (Element* blockAncestor = blockParent;
1806
0
         blockAncestor && insertBRElement;
1807
0
         blockAncestor = HTMLEditor::GetBlockNodeParent(blockAncestor, host)) {
1808
0
      insertBRElement = !CanContainParagraph(*blockAncestor);
1809
0
    }
1810
0
  }
1811
0
1812
0
  // If we cannot insert a <p>/<div> element at the selection, we should insert
1813
0
  // a <br> element instead.
1814
0
  if (insertBRElement) {
1815
0
    nsresult rv = InsertBRElement(atStartOfSelection);
1816
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1817
0
      return rv;
1818
0
    }
1819
0
    *aHandled = true;
1820
0
    return NS_OK;
1821
0
  }
1822
0
1823
0
  if (host == blockParent && separator != ParagraphSeparator::br) {
1824
0
    // Insert a new block first
1825
0
    MOZ_ASSERT(separator == ParagraphSeparator::div ||
1826
0
               separator == ParagraphSeparator::p);
1827
0
    // MakeBasicBlock() creates AutoSelectionRestorer.
1828
0
    // Therefore, even if it returns NS_OK, editor might have been destroyed
1829
0
    // at restoring Selection.
1830
0
    nsresult rv = MakeBasicBlock(ParagraphSeparatorElement(separator));
1831
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
1832
0
        NS_WARN_IF(!CanHandleEditAction())) {
1833
0
      return NS_ERROR_EDITOR_DESTROYED;
1834
0
    }
1835
0
    // We warn on failure, but don't handle it, because it might be harmless.
1836
0
    // Instead we just check that a new block was actually created.
1837
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1838
0
                         "HTMLEditRules::MakeBasicBlock() failed");
1839
0
1840
0
    firstRange = SelectionRef().GetRangeAt(0);
1841
0
    if (NS_WARN_IF(!firstRange)) {
1842
0
      return NS_ERROR_FAILURE;
1843
0
    }
1844
0
1845
0
    atStartOfSelection = firstRange->StartRef();
1846
0
    if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
1847
0
      return NS_ERROR_FAILURE;
1848
0
    }
1849
0
    MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
1850
0
1851
0
    blockParent =
1852
0
      HTMLEditor::GetBlock(*atStartOfSelection.GetContainer(), host);
1853
0
    if (NS_WARN_IF(!blockParent)) {
1854
0
      return NS_ERROR_UNEXPECTED;
1855
0
    }
1856
0
    if (NS_WARN_IF(blockParent == host)) {
1857
0
      // Didn't create a new block for some reason, fall back to <br>
1858
0
      rv = InsertBRElement(atStartOfSelection);
1859
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1860
0
        return rv;
1861
0
      }
1862
0
      *aHandled = true;
1863
0
      return NS_OK;
1864
0
    }
1865
0
    // Now, mNewBlock is last created block element for wrapping inline
1866
0
    // elements around the caret position and AfterEditInner() will move
1867
0
    // caret into it.  However, it may be different from block parent of
1868
0
    // the caret position.  E.g., MakeBasicBlock() may wrap following
1869
0
    // inline elements of a <br> element which is next sibling of container
1870
0
    // of the caret.  So, we need to adjust mNewBlock here for avoiding
1871
0
    // jumping caret to odd position.
1872
0
    mNewBlock = blockParent;
1873
0
  }
1874
0
1875
0
  // If block is empty, populate with br.  (For example, imagine a div that
1876
0
  // contains the word "text".  The user selects "text" and types return.
1877
0
  // "Text" is deleted leaving an empty block.  We want to put in one br to
1878
0
  // make block have a line.  Then code further below will put in a second br.)
1879
0
  if (IsEmptyBlockElement(*blockParent, IgnoreSingleBR::eNo)) {
1880
0
    AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection);
1881
0
    EditorRawDOMPoint endOfBlockParent;
1882
0
    endOfBlockParent.SetToEndOf(blockParent);
1883
0
    RefPtr<Element> brElement =
1884
0
      HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
1885
0
                                                     endOfBlockParent);
1886
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1887
0
      return NS_ERROR_EDITOR_DESTROYED;
1888
0
    }
1889
0
    if (NS_WARN_IF(!brElement)) {
1890
0
      return NS_ERROR_FAILURE;
1891
0
    }
1892
0
  }
1893
0
1894
0
  nsCOMPtr<Element> listItem = IsInListItem(blockParent);
1895
0
  if (listItem && listItem != host) {
1896
0
    nsresult rv =
1897
0
      ReturnInListItem(*listItem, *atStartOfSelection.GetContainer(),
1898
0
                       atStartOfSelection.Offset());
1899
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
1900
0
      return NS_ERROR_EDITOR_DESTROYED;
1901
0
    }
1902
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1903
0
      "Failed to insert break into list item");
1904
0
    *aHandled = true;
1905
0
    return NS_OK;
1906
0
  }
1907
0
1908
0
  if (HTMLEditUtils::IsHeader(*blockParent)) {
1909
0
    // Headers: close (or split) header
1910
0
    nsresult rv =
1911
0
      ReturnInHeader(*blockParent, *atStartOfSelection.GetContainer(),
1912
0
                     atStartOfSelection.Offset());
1913
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
1914
0
      return NS_ERROR_EDITOR_DESTROYED;
1915
0
    }
1916
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1917
0
      "Failed to handle insertParagraph in the heading element");
1918
0
    *aHandled = true;
1919
0
    return NS_OK;
1920
0
  }
1921
0
1922
0
  // XXX Ideally, we should take same behavior with both <p> container and
1923
0
  //     <div> container.  However, we are still using <br> as default
1924
0
  //     paragraph separator (non-standard) and we've split only <p> container
1925
0
  //     long time.  Therefore, some web apps may depend on this behavior like
1926
0
  //     Gmail.  So, let's use traditional odd behavior only when the default
1927
0
  //     paragraph separator is <br>.  Otherwise, take consistent behavior
1928
0
  //     between <p> container and <div> container.
1929
0
  if ((separator == ParagraphSeparator::br &&
1930
0
       blockParent->IsHTMLElement(nsGkAtoms::p)) ||
1931
0
      (separator != ParagraphSeparator::br &&
1932
0
       blockParent->IsAnyOfHTMLElements(nsGkAtoms::p, nsGkAtoms::div))) {
1933
0
    AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection);
1934
0
    // Paragraphs: special rules to look for <br>s
1935
0
    EditActionResult result = ReturnInParagraph(*blockParent);
1936
0
    if (NS_WARN_IF(result.Failed())) {
1937
0
      return result.Rv();
1938
0
    }
1939
0
    *aHandled = result.Handled();
1940
0
    *aCancel = result.Canceled();
1941
0
    if (result.Handled()) {
1942
0
      // Now, atStartOfSelection may be invalid because the left paragraph
1943
0
      // may have less children than its offset.  For avoiding warnings of
1944
0
      // validation of EditorDOMPoint, we should not touch it anymore.
1945
0
      lockOffset.Cancel();
1946
0
      return NS_OK;
1947
0
    }
1948
0
    // Fall through, if ReturnInParagraph() didn't handle it.
1949
0
    MOZ_ASSERT(!*aCancel, "ReturnInParagraph canceled this edit action, "
1950
0
                          "WillInsertBreak() needs to handle such case");
1951
0
  }
1952
0
1953
0
  // If nobody handles this edit action, let's insert new <br> at the selection.
1954
0
  MOZ_ASSERT(!*aHandled, "Reached last resort of WillInsertBreak() "
1955
0
                         "after the edit action is handled");
1956
0
  rv = InsertBRElement(atStartOfSelection);
1957
0
  *aHandled = true;
1958
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1959
0
    return rv;
1960
0
  }
1961
0
  return NS_OK;
1962
0
}
1963
1964
nsresult
1965
HTMLEditRules::InsertBRElement(const EditorDOMPoint& aPointToBreak)
1966
0
{
1967
0
  MOZ_ASSERT(IsEditorDataAvailable());
1968
0
1969
0
  if (NS_WARN_IF(!aPointToBreak.IsSet())) {
1970
0
    return NS_ERROR_INVALID_ARG;
1971
0
  }
1972
0
1973
0
  bool brElementIsAfterBlock = false;
1974
0
  bool brElementIsBeforeBlock = false;
1975
0
1976
0
  // First, insert a <br> element.
1977
0
  RefPtr<Element> brElement;
1978
0
  if (IsPlaintextEditor()) {
1979
0
    brElement =
1980
0
      HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
1981
0
                                                     aPointToBreak);
1982
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
1983
0
      return NS_ERROR_EDITOR_DESTROYED;
1984
0
    }
1985
0
    if (NS_WARN_IF(!brElement)) {
1986
0
      return NS_ERROR_FAILURE;
1987
0
    }
1988
0
  } else {
1989
0
    EditorDOMPoint pointToBreak(aPointToBreak);
1990
0
    WSRunObject wsObj(&HTMLEditorRef(), pointToBreak);
1991
0
    WSType wsType;
1992
0
    wsObj.PriorVisibleNode(pointToBreak, &wsType);
1993
0
    if (wsType & WSType::block) {
1994
0
      brElementIsAfterBlock = true;
1995
0
    }
1996
0
    wsObj.NextVisibleNode(pointToBreak, &wsType);
1997
0
    if (wsType & WSType::block) {
1998
0
      brElementIsBeforeBlock = true;
1999
0
    }
2000
0
    // If the container of the break is a link, we need to split it and
2001
0
    // insert new <br> between the split links.
2002
0
    RefPtr<Element> linkNode =
2003
0
      HTMLEditor::GetLinkElement(pointToBreak.GetContainer());
2004
0
    if (linkNode) {
2005
0
      SplitNodeResult splitLinkNodeResult =
2006
0
        HTMLEditorRef().SplitNodeDeepWithTransaction(
2007
0
                          *linkNode, pointToBreak,
2008
0
                          SplitAtEdges::eDoNotCreateEmptyContainer);
2009
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2010
0
        return NS_ERROR_EDITOR_DESTROYED;
2011
0
      }
2012
0
      if (NS_WARN_IF(splitLinkNodeResult.Failed())) {
2013
0
        return splitLinkNodeResult.Rv();
2014
0
      }
2015
0
      pointToBreak = splitLinkNodeResult.SplitPoint();
2016
0
    }
2017
0
    brElement =
2018
0
      wsObj.InsertBreak(SelectionRef(), pointToBreak, nsIEditor::eNone);
2019
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
2020
0
      return NS_ERROR_EDITOR_DESTROYED;
2021
0
    }
2022
0
    if (NS_WARN_IF(!brElement)) {
2023
0
      return NS_ERROR_FAILURE;
2024
0
    }
2025
0
  }
2026
0
2027
0
  // If the <br> element has already been removed from the DOM tree by a
2028
0
  // mutation observer, don't continue handling this.
2029
0
  if (NS_WARN_IF(!brElement->GetParentNode())) {
2030
0
    return NS_ERROR_FAILURE;
2031
0
  }
2032
0
2033
0
  if (brElementIsAfterBlock && brElementIsBeforeBlock) {
2034
0
    // We just placed a <br> between block boundaries.  This is the one case
2035
0
    // where we want the selection to be before the br we just placed, as the
2036
0
    // br will be on a new line, rather than at end of prior line.
2037
0
    // XXX brElementIsAfterBlock and brElementIsBeforeBlock were set before
2038
0
    //     modifying the DOM tree.  So, now, the <br> element may not be
2039
0
    //     between blocks.
2040
0
    ErrorResult error;
2041
0
    SelectionRef().SetInterlinePosition(true, error);
2042
0
    NS_WARNING_ASSERTION(!error.Failed(), "Failed to set interline position");
2043
0
    EditorRawDOMPoint point(brElement);
2044
0
    error = NS_OK;
2045
0
    SelectionRef().Collapse(point, error);
2046
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
2047
0
      error.SuppressException();
2048
0
      return NS_ERROR_EDITOR_DESTROYED;
2049
0
    }
2050
0
    if (NS_WARN_IF(error.Failed())) {
2051
0
      return error.StealNSResult();
2052
0
    }
2053
0
    return NS_OK;
2054
0
  }
2055
0
2056
0
  EditorDOMPoint afterBRElement(brElement);
2057
0
  DebugOnly<bool> advanced = afterBRElement.AdvanceOffset();
2058
0
  NS_WARNING_ASSERTION(advanced,
2059
0
    "Failed to advance offset after the new <br> element");
2060
0
  WSRunObject wsObj(&HTMLEditorRef(), afterBRElement);
2061
0
  nsCOMPtr<nsINode> maybeSecondBRNode;
2062
0
  WSType wsType;
2063
0
  wsObj.NextVisibleNode(afterBRElement,
2064
0
                        address_of(maybeSecondBRNode), nullptr, &wsType);
2065
0
  if (wsType == WSType::br) {
2066
0
    // The next thing after the break we inserted is another break.  Move the
2067
0
    // second break to be the first break's sibling.  This will prevent them
2068
0
    // from being in different inline nodes, which would break
2069
0
    // SetInterlinePosition().  It will also assure that if the user clicks
2070
0
    // away and then clicks back on their new blank line, they will still get
2071
0
    // the style from the line above.
2072
0
    EditorDOMPoint atSecondBRElement(maybeSecondBRNode);
2073
0
    if (brElement->GetNextSibling() != maybeSecondBRNode) {
2074
0
      nsresult rv =
2075
0
        HTMLEditorRef().MoveNodeWithTransaction(*maybeSecondBRNode->AsContent(),
2076
0
                                                afterBRElement);
2077
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2078
0
        return NS_ERROR_EDITOR_DESTROYED;
2079
0
      }
2080
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2081
0
        return rv;
2082
0
      }
2083
0
    }
2084
0
  }
2085
0
2086
0
  // SetInterlinePosition(true) means we want the caret to stick to the
2087
0
  // content on the "right".  We want the caret to stick to whatever is past
2088
0
  // the break.  This is because the break is on the same line we were on,
2089
0
  // but the next content will be on the following line.
2090
0
2091
0
  // An exception to this is if the break has a next sibling that is a block
2092
0
  // node.  Then we stick to the left to avoid an uber caret.
2093
0
  nsIContent* nextSiblingOfBRElement = brElement->GetNextSibling();
2094
0
  ErrorResult error;
2095
0
  SelectionRef().SetInterlinePosition(!(nextSiblingOfBRElement &&
2096
0
                                        IsBlockNode(*nextSiblingOfBRElement)),
2097
0
                                      error);
2098
0
  NS_WARNING_ASSERTION(!error.Failed(),
2099
0
    "Failed to set or unset interline position");
2100
0
  error = NS_OK;
2101
0
  SelectionRef().Collapse(afterBRElement, error);
2102
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
2103
0
    error.SuppressException();
2104
0
    return NS_ERROR_EDITOR_DESTROYED;
2105
0
  }
2106
0
  if (NS_WARN_IF(error.Failed())) {
2107
0
    return error.StealNSResult();
2108
0
  }
2109
0
  return NS_OK;
2110
0
}
2111
2112
nsresult
2113
HTMLEditRules::SplitMailCites(bool* aHandled)
2114
0
{
2115
0
  MOZ_ASSERT(IsEditorDataAvailable());
2116
0
2117
0
  if (NS_WARN_IF(!aHandled)) {
2118
0
    return NS_ERROR_INVALID_ARG;
2119
0
  }
2120
0
2121
0
  EditorRawDOMPoint pointToSplit(EditorBase::GetStartPoint(&SelectionRef()));
2122
0
  if (NS_WARN_IF(!pointToSplit.IsSet())) {
2123
0
    return NS_ERROR_FAILURE;
2124
0
  }
2125
0
2126
0
  RefPtr<Element> citeNode =
2127
0
    GetTopEnclosingMailCite(*pointToSplit.GetContainer());
2128
0
  if (!citeNode) {
2129
0
    return NS_OK;
2130
0
  }
2131
0
2132
0
  // If our selection is just before a break, nudge it to be just after it.
2133
0
  // This does two things for us.  It saves us the trouble of having to add
2134
0
  // a break here ourselves to preserve the "blockness" of the inline span
2135
0
  // mailquote (in the inline case), and :
2136
0
  // it means the break won't end up making an empty line that happens to be
2137
0
  // inside a mailquote (in either inline or block case).
2138
0
  // The latter can confuse a user if they click there and start typing,
2139
0
  // because being in the mailquote may affect wrapping behavior, or font
2140
0
  // color, etc.
2141
0
  WSRunObject wsObj(&HTMLEditorRef(), pointToSplit);
2142
0
  nsCOMPtr<nsINode> visNode;
2143
0
  WSType wsType;
2144
0
  wsObj.NextVisibleNode(pointToSplit, address_of(visNode), nullptr, &wsType);
2145
0
  // If selection start point is before a break and it's inside the mailquote,
2146
0
  // let's split it after the visible node.
2147
0
  if (wsType == WSType::br &&
2148
0
      visNode != citeNode && citeNode->Contains(visNode)) {
2149
0
    pointToSplit.Set(visNode);
2150
0
    DebugOnly<bool> advanced = pointToSplit.AdvanceOffset();
2151
0
    NS_WARNING_ASSERTION(advanced,
2152
0
      "Failed to advance offset to after the visible node");
2153
0
  }
2154
0
2155
0
  if (NS_WARN_IF(!pointToSplit.GetContainerAsContent())) {
2156
0
    return NS_ERROR_FAILURE;
2157
0
  }
2158
0
2159
0
  SplitNodeResult splitCiteNodeResult =
2160
0
    HTMLEditorRef().SplitNodeDeepWithTransaction(
2161
0
                      *citeNode, pointToSplit,
2162
0
                      SplitAtEdges::eDoNotCreateEmptyContainer);
2163
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
2164
0
    return NS_ERROR_EDITOR_DESTROYED;
2165
0
  }
2166
0
  if (NS_WARN_IF(splitCiteNodeResult.Failed())) {
2167
0
    return splitCiteNodeResult.Rv();
2168
0
  }
2169
0
  pointToSplit.Clear();
2170
0
2171
0
  // Add an invisible <br> to the end of current cite node (If new left cite
2172
0
  // has not been created, we're at the end of it.  Otherwise, we're still at
2173
0
  // the right node) if it was a <span> of style="display: block". This is
2174
0
  // important, since when serializing the cite to plain text, the span which
2175
0
  // caused the visual break is discarded.  So the added <br> will guarantee
2176
0
  // that the serializer will insert a break where the user saw one.
2177
0
  // FYI: splitCiteNodeResult grabs the previous node with nsCOMPtr.  So, it's
2178
0
  //      safe to access previousNodeOfSplitPoint even after changing the DOM
2179
0
  //      tree and/or selection even though it's raw pointer.
2180
0
  nsIContent* previousNodeOfSplitPoint =
2181
0
    splitCiteNodeResult.GetPreviousNode();
2182
0
  if (previousNodeOfSplitPoint &&
2183
0
      previousNodeOfSplitPoint->IsHTMLElement(nsGkAtoms::span) &&
2184
0
      previousNodeOfSplitPoint->GetPrimaryFrame() &&
2185
0
      previousNodeOfSplitPoint->GetPrimaryFrame()->
2186
0
                                  IsFrameOfType(nsIFrame::eBlockFrame)) {
2187
0
    nsCOMPtr<nsINode> lastChild =
2188
0
      previousNodeOfSplitPoint->GetLastChild();
2189
0
    if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
2190
0
      // We ignore the result here.
2191
0
      EditorRawDOMPoint endOfPreviousNodeOfSplitPoint;
2192
0
      endOfPreviousNodeOfSplitPoint.SetToEndOf(previousNodeOfSplitPoint);
2193
0
      RefPtr<Element> invisibleBrElement =
2194
0
        HTMLEditorRef().InsertBrElementWithTransaction(
2195
0
                          SelectionRef(),
2196
0
                          endOfPreviousNodeOfSplitPoint);
2197
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2198
0
        return NS_ERROR_EDITOR_DESTROYED;
2199
0
      }
2200
0
      NS_WARNING_ASSERTION(invisibleBrElement,
2201
0
        "Failed to create an invisible <br> element");
2202
0
    }
2203
0
  }
2204
0
2205
0
  // In most cases, <br> should be inserted after current cite.  However, if
2206
0
  // left cite hasn't been created because the split point was start of the
2207
0
  // cite node, <br> should be inserted before the current cite.
2208
0
  EditorRawDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
2209
0
  RefPtr<Element> brElement =
2210
0
    HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
2211
0
                                                   pointToInsertBrNode);
2212
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
2213
0
    return NS_ERROR_EDITOR_DESTROYED;
2214
0
  }
2215
0
  if (NS_WARN_IF(!brElement)) {
2216
0
    return NS_ERROR_FAILURE;
2217
0
  }
2218
0
  // Now, offset of pointToInsertBrNode is invalid.  Let's clear it.
2219
0
  pointToInsertBrNode.Clear();
2220
0
2221
0
  // Want selection before the break, and on same line.
2222
0
  EditorDOMPoint atBrNode(brElement);
2223
0
  Unused << atBrNode.Offset(); // Needs offset after collapsing the selection.
2224
0
  ErrorResult error;
2225
0
  SelectionRef().SetInterlinePosition(true, error);
2226
0
  NS_WARNING_ASSERTION(!error.Failed(),
2227
0
    "Failed to set interline position");
2228
0
  error = NS_OK;
2229
0
  SelectionRef().Collapse(atBrNode, error);
2230
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
2231
0
    error.SuppressException();
2232
0
    return NS_ERROR_EDITOR_DESTROYED;
2233
0
  }
2234
0
  if (NS_WARN_IF(error.Failed())) {
2235
0
    return error.StealNSResult();
2236
0
  }
2237
0
2238
0
  // if citeNode wasn't a block, we might also want another break before it.
2239
0
  // We need to examine the content both before the br we just added and also
2240
0
  // just after it.  If we don't have another br or block boundary adjacent,
2241
0
  // then we will need a 2nd br added to achieve blank line that user expects.
2242
0
  if (IsInlineNode(*citeNode)) {
2243
0
    // Use DOM point which we tried to collapse to.
2244
0
    EditorRawDOMPoint pointToCreateNewBrNode(atBrNode.GetContainer(),
2245
0
                                             atBrNode.Offset());
2246
0
2247
0
    WSRunObject wsObj(&HTMLEditorRef(), pointToCreateNewBrNode);
2248
0
    WSType wsType;
2249
0
    wsObj.PriorVisibleNode(pointToCreateNewBrNode, nullptr, nullptr,
2250
0
                           &wsType);
2251
0
    if (wsType == WSType::normalWS || wsType == WSType::text ||
2252
0
        wsType == WSType::special) {
2253
0
      EditorRawDOMPoint pointAfterNewBrNode(pointToCreateNewBrNode);
2254
0
      DebugOnly<bool> advanced = pointAfterNewBrNode.AdvanceOffset();
2255
0
      NS_WARNING_ASSERTION(advanced,
2256
0
        "Failed to advance offset after the <br> node");
2257
0
      WSRunObject wsObjAfterBR(&HTMLEditorRef(), pointAfterNewBrNode);
2258
0
      wsObjAfterBR.NextVisibleNode(pointAfterNewBrNode, &wsType);
2259
0
      if (wsType == WSType::normalWS || wsType == WSType::text ||
2260
0
          wsType == WSType::special ||
2261
0
          // In case we're at the very end.
2262
0
          wsType == WSType::thisBlock) {
2263
0
        brElement =
2264
0
          HTMLEditorRef().InsertBrElementWithTransaction(
2265
0
                            SelectionRef(), pointToCreateNewBrNode);
2266
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
2267
0
          return NS_ERROR_EDITOR_DESTROYED;
2268
0
        }
2269
0
        if (NS_WARN_IF(!brElement)) {
2270
0
          return NS_ERROR_FAILURE;
2271
0
        }
2272
0
        // Now, those points may be invalid.
2273
0
        pointToCreateNewBrNode.Clear();
2274
0
        pointAfterNewBrNode.Clear();
2275
0
      }
2276
0
    }
2277
0
  }
2278
0
2279
0
  // delete any empty cites
2280
0
  bool bEmptyCite = false;
2281
0
  if (previousNodeOfSplitPoint) {
2282
0
    nsresult rv =
2283
0
      HTMLEditorRef().IsEmptyNode(previousNodeOfSplitPoint, &bEmptyCite,
2284
0
                                  true, false);
2285
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2286
0
      return rv;
2287
0
    }
2288
0
    if (bEmptyCite) {
2289
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*previousNodeOfSplitPoint);
2290
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2291
0
        return NS_ERROR_EDITOR_DESTROYED;
2292
0
      }
2293
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2294
0
        return rv;
2295
0
      }
2296
0
    }
2297
0
  }
2298
0
2299
0
  if (citeNode) {
2300
0
    nsresult rv =
2301
0
      HTMLEditorRef().IsEmptyNode(citeNode, &bEmptyCite, true, false);
2302
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2303
0
      return rv;
2304
0
    }
2305
0
    if (bEmptyCite) {
2306
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*citeNode);
2307
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2308
0
        return NS_ERROR_EDITOR_DESTROYED;
2309
0
      }
2310
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2311
0
        return rv;
2312
0
      }
2313
0
    }
2314
0
  }
2315
0
2316
0
  *aHandled = true;
2317
0
  return NS_OK;
2318
0
}
2319
2320
2321
nsresult
2322
HTMLEditRules::WillDeleteSelection(nsIEditor::EDirection aAction,
2323
                                   nsIEditor::EStripWrappers aStripWrappers,
2324
                                   bool* aCancel,
2325
                                   bool* aHandled)
2326
0
{
2327
0
  MOZ_ASSERT(IsEditorDataAvailable());
2328
0
  MOZ_ASSERT(aStripWrappers == nsIEditor::eStrip ||
2329
0
             aStripWrappers == nsIEditor::eNoStrip);
2330
0
2331
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
2332
0
    return NS_ERROR_INVALID_ARG;
2333
0
  }
2334
0
  // Initialize out params
2335
0
  *aCancel = false;
2336
0
  *aHandled = false;
2337
0
2338
0
  // Remember that we did a selection deletion.  Used by
2339
0
  // CreateStyleForInsertText()
2340
0
  mDidDeleteSelection = true;
2341
0
2342
0
  // If there is only bogus content, cancel the operation
2343
0
  if (mBogusNode) {
2344
0
    *aCancel = true;
2345
0
    return NS_OK;
2346
0
  }
2347
0
2348
0
  // First check for table selection mode.  If so, hand off to table editor.
2349
0
  ErrorResult error;
2350
0
  RefPtr<Element> cellElement =
2351
0
    HTMLEditorRef().GetFirstSelectedTableCellElement(SelectionRef(),
2352
0
                                                     error);
2353
0
  if (cellElement) {
2354
0
    error.SuppressException();
2355
0
    nsresult rv = HTMLEditorRef().DeleteTableCellContentsWithTransaction();
2356
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
2357
0
      return NS_ERROR_EDITOR_DESTROYED;
2358
0
    }
2359
0
    *aHandled = true;
2360
0
    return rv;
2361
0
  }
2362
0
  nsresult rv = error.StealNSResult();
2363
0
  cellElement = nullptr;
2364
0
2365
0
  // origCollapsed is used later to determine whether we should join blocks. We
2366
0
  // don't really care about bCollapsed because it will be modified by
2367
0
  // ExtendSelectionForDelete later. TryToJoinBlocksWithTransaction() should
2368
0
  // happen if the original selection is collapsed and the cursor is at the end
2369
0
  // of a block element, in which case ExtendSelectionForDelete would always
2370
0
  // make the selection not collapsed.
2371
0
  bool join = false;
2372
0
  bool origCollapsed = SelectionRef().IsCollapsed();
2373
0
2374
0
  if (origCollapsed) {
2375
0
    EditorDOMPoint startPoint(EditorBase::GetStartPoint(&SelectionRef()));
2376
0
    if (NS_WARN_IF(!startPoint.IsSet())) {
2377
0
      return NS_ERROR_FAILURE;
2378
0
    }
2379
0
2380
0
    // If we are inside an empty block, delete it.
2381
0
    RefPtr<Element> host = HTMLEditorRef().GetActiveEditingHost();
2382
0
    if (NS_WARN_IF(!host)) {
2383
0
      return NS_ERROR_FAILURE;
2384
0
    }
2385
0
2386
0
    {
2387
0
      AutoEditorDOMPointChildInvalidator lockOffset(startPoint);
2388
0
2389
0
      rv = MaybeDeleteTopMostEmptyAncestor(*startPoint.GetContainer(), *host,
2390
0
                                           aAction, aHandled);
2391
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2392
0
        return rv;
2393
0
      }
2394
0
      if (*aHandled) {
2395
0
        return NS_OK;
2396
0
      }
2397
0
   }
2398
0
2399
0
    // Test for distance between caret and text that will be deleted
2400
0
    rv = CheckBidiLevelForDeletion(startPoint, aAction, aCancel);
2401
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2402
0
      return rv;
2403
0
    }
2404
0
    if (*aCancel) {
2405
0
      return NS_OK;
2406
0
    }
2407
0
2408
0
    rv = HTMLEditorRef().ExtendSelectionForDelete(&SelectionRef(), &aAction);
2409
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2410
0
      return rv;
2411
0
    }
2412
0
2413
0
    // We should delete nothing.
2414
0
    if (aAction == nsIEditor::eNone) {
2415
0
      return NS_OK;
2416
0
    }
2417
0
  }
2418
0
2419
0
  if (SelectionRef().IsCollapsed()) {
2420
0
    // ExtendSelectionForDelete() won't change the selection.
2421
0
2422
0
    EditorDOMPoint startPoint(EditorBase::GetStartPoint(&SelectionRef()));
2423
0
    if (NS_WARN_IF(!startPoint.IsSet())) {
2424
0
      return NS_ERROR_FAILURE;
2425
0
    }
2426
0
2427
0
    // What's in the direction we are deleting?
2428
0
    WSRunObject wsObj(&HTMLEditorRef(), startPoint);
2429
0
    nsCOMPtr<nsINode> visNode;
2430
0
    int32_t visOffset;
2431
0
    WSType wsType;
2432
0
2433
0
    // Find next visible node
2434
0
    if (aAction == nsIEditor::eNext) {
2435
0
      wsObj.NextVisibleNode(startPoint,
2436
0
                            address_of(visNode), &visOffset, &wsType);
2437
0
    } else {
2438
0
      wsObj.PriorVisibleNode(startPoint,
2439
0
                             address_of(visNode), &visOffset, &wsType);
2440
0
    }
2441
0
2442
0
    if (!visNode) {
2443
0
      // Can't find anything to delete!
2444
0
      *aCancel = true;
2445
0
      // XXX This is the result of
2446
0
      //     HTMLEditorRef().GetFirstSelectedTableCellElement().
2447
0
      //     The value could be both an error and NS_OK.
2448
0
      return rv;
2449
0
    }
2450
0
2451
0
    if (wsType == WSType::normalWS) {
2452
0
      // We found some visible ws to delete.  Let ws code handle it.
2453
0
      *aHandled = true;
2454
0
      if (aAction == nsIEditor::eNext) {
2455
0
        rv = wsObj.DeleteWSForward();
2456
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
2457
0
          return NS_ERROR_EDITOR_DESTROYED;
2458
0
        }
2459
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
2460
0
          return rv;
2461
0
        }
2462
0
      } else {
2463
0
        rv = wsObj.DeleteWSBackward();
2464
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
2465
0
          return NS_ERROR_EDITOR_DESTROYED;
2466
0
        }
2467
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
2468
0
          return rv;
2469
0
        }
2470
0
      }
2471
0
      rv = InsertBRIfNeeded();
2472
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2473
0
        return rv;
2474
0
      }
2475
0
      return NS_OK;
2476
0
    }
2477
0
2478
0
    if (wsType == WSType::text) {
2479
0
      // Found normal text to delete.
2480
0
      OwningNonNull<Text> nodeAsText = *visNode->GetAsText();
2481
0
      int32_t so = visOffset;
2482
0
      int32_t eo = visOffset + 1;
2483
0
      if (aAction == nsIEditor::ePrevious) {
2484
0
        if (!so) {
2485
0
          return NS_ERROR_UNEXPECTED;
2486
0
        }
2487
0
        so--;
2488
0
        eo--;
2489
0
        // Bug 1068979: delete both codepoints if surrogate pair
2490
0
        if (so > 0) {
2491
0
          const nsTextFragment *text = nodeAsText->GetText();
2492
0
          if (NS_IS_LOW_SURROGATE(text->CharAt(so)) &&
2493
0
              NS_IS_HIGH_SURROGATE(text->CharAt(so - 1))) {
2494
0
            so--;
2495
0
          }
2496
0
        }
2497
0
      } else {
2498
0
        RefPtr<nsRange> range = SelectionRef().GetRangeAt(0);
2499
0
        if (NS_WARN_IF(!range)) {
2500
0
          return NS_ERROR_FAILURE;
2501
0
        }
2502
0
2503
0
        NS_ASSERTION(range->GetStartContainer() == visNode,
2504
0
                     "selection start not in visNode");
2505
0
        NS_ASSERTION(range->GetEndContainer() == visNode,
2506
0
                     "selection end not in visNode");
2507
0
2508
0
        so = range->StartOffset();
2509
0
        eo = range->EndOffset();
2510
0
      }
2511
0
      rv = WSRunObject::PrepareToDeleteRange(&HTMLEditorRef(),
2512
0
                                             address_of(visNode),
2513
0
                                             &so, address_of(visNode), &eo);
2514
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2515
0
        return NS_ERROR_EDITOR_DESTROYED;
2516
0
      }
2517
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2518
0
        return rv;
2519
0
      }
2520
0
      *aHandled = true;
2521
0
      rv = HTMLEditorRef().DeleteTextWithTransaction(nodeAsText,
2522
0
                                                     std::min(so, eo),
2523
0
                                                     DeprecatedAbs(eo - so));
2524
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2525
0
        return NS_ERROR_EDITOR_DESTROYED;
2526
0
      }
2527
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2528
0
        return rv;
2529
0
      }
2530
0
2531
0
      // XXX When Backspace key is pressed, Chromium removes following empty
2532
0
      //     text nodes when removing the last character of the non-empty text
2533
0
      //     node.  However, Edge never removes empty text nodes even if
2534
0
      //     selection is in the following empty text node(s).  For now, we
2535
0
      //     should keep our traditional behavior same as Edge for backward
2536
0
      //     compatibility.
2537
0
      // XXX When Delete key is pressed, Edge removes all preceding empty
2538
0
      //     text nodes when removing the first character of the non-empty
2539
0
      //     text node.  Chromium removes only selected empty text node and
2540
0
      //     following empty text nodes and the first character of the
2541
0
      //     non-empty text node.  For now, we should keep our traditional
2542
0
      //     behavior same as Chromium for backward compatibility.
2543
0
2544
0
      rv = DeleteNodeIfCollapsedText(nodeAsText);
2545
0
      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
2546
0
        return NS_ERROR_EDITOR_DESTROYED;
2547
0
      }
2548
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove collapsed text");
2549
0
2550
0
      rv = InsertBRIfNeeded();
2551
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2552
0
        return rv;
2553
0
      }
2554
0
2555
0
      // Remember that we did a ranged delete for the benefit of
2556
0
      // AfterEditInner().
2557
0
      mDidRangedDelete = true;
2558
0
2559
0
      return NS_OK;
2560
0
    }
2561
0
2562
0
    if (wsType == WSType::special || wsType == WSType::br ||
2563
0
        visNode->IsHTMLElement(nsGkAtoms::hr)) {
2564
0
      // Short circuit for invisible breaks.  delete them and recurse.
2565
0
      if (visNode->IsHTMLElement(nsGkAtoms::br) &&
2566
0
          !HTMLEditorRef().IsVisibleBRElement(visNode)) {
2567
0
        rv = HTMLEditorRef().DeleteNodeWithTransaction(*visNode);
2568
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
2569
0
          return NS_ERROR_EDITOR_DESTROYED;
2570
0
        }
2571
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
2572
0
          return rv;
2573
0
        }
2574
0
        rv = WillDeleteSelection(aAction, aStripWrappers, aCancel, aHandled);
2575
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
2576
0
          return rv;
2577
0
        }
2578
0
        return NS_OK;
2579
0
      }
2580
0
2581
0
      // Special handling for backspace when positioned after <hr>
2582
0
      if (aAction == nsIEditor::ePrevious &&
2583
0
          visNode->IsHTMLElement(nsGkAtoms::hr)) {
2584
0
        // Only if the caret is positioned at the end-of-hr-line position, we
2585
0
        // want to delete the <hr>.
2586
0
        //
2587
0
        // In other words, we only want to delete, if our selection position
2588
0
        // (indicated by startPoint) is the position directly
2589
0
        // after the <hr>, on the same line as the <hr>.
2590
0
        //
2591
0
        // To detect this case we check:
2592
0
        // startPoint's container == parentOfVisNode
2593
0
        // and
2594
0
        // startPoint's offset -1 == visNodeOffsetToVisNodeParent
2595
0
        // and
2596
0
        // interline position is false (left)
2597
0
        //
2598
0
        // In any other case we set the position to startPoint's container -1
2599
0
        // and interlineposition to false, only moving the caret to the
2600
0
        // end-of-hr-line position.
2601
0
        bool moveOnly = true;
2602
0
2603
0
        EditorDOMPoint selPoint(visNode);
2604
0
2605
0
        ErrorResult err;
2606
0
        bool interLineIsRight = SelectionRef().GetInterlinePosition(err);
2607
0
        if (NS_WARN_IF(err.Failed())) {
2608
0
          return err.StealNSResult();
2609
0
        }
2610
0
2611
0
        if (startPoint.GetContainer() == selPoint.GetContainer() &&
2612
0
            startPoint.Offset() - 1 == selPoint.Offset() &&
2613
0
            !interLineIsRight) {
2614
0
          moveOnly = false;
2615
0
        }
2616
0
2617
0
        if (moveOnly) {
2618
0
          // Go to the position after the <hr>, but to the end of the <hr> line
2619
0
          // by setting the interline position to left.
2620
0
          DebugOnly<bool> advanced = selPoint.AdvanceOffset();
2621
0
          NS_WARNING_ASSERTION(advanced,
2622
0
            "Failed to advance offset after <hr> element");
2623
0
2624
0
          {
2625
0
            AutoEditorDOMPointChildInvalidator lockOffset(selPoint);
2626
0
2627
0
            IgnoredErrorResult ignoredError;
2628
0
            SelectionRef().Collapse(selPoint, ignoredError);
2629
0
            if (NS_WARN_IF(!CanHandleEditAction())) {
2630
0
              return NS_ERROR_EDITOR_DESTROYED;
2631
0
            }
2632
0
            NS_WARNING_ASSERTION(!ignoredError.Failed(),
2633
0
              "Failed to collapse selection at after the <hr>");
2634
0
          }
2635
0
2636
0
          IgnoredErrorResult ignoredError;
2637
0
          SelectionRef().SetInterlinePosition(false, ignoredError);
2638
0
          NS_WARNING_ASSERTION(!ignoredError.Failed(),
2639
0
            "Failed to unset interline position");
2640
0
          mDidExplicitlySetInterline = true;
2641
0
          *aHandled = true;
2642
0
2643
0
          // There is one exception to the move only case.  If the <hr> is
2644
0
          // followed by a <br> we want to delete the <br>.
2645
0
2646
0
          WSType otherWSType;
2647
0
          nsCOMPtr<nsINode> otherNode;
2648
0
2649
0
          wsObj.NextVisibleNode(startPoint,
2650
0
                                address_of(otherNode), nullptr,
2651
0
                                &otherWSType);
2652
0
2653
0
          if (otherWSType == WSType::br) {
2654
0
            // Delete the <br>
2655
0
            if (NS_WARN_IF(!otherNode->IsContent())) {
2656
0
              return NS_ERROR_FAILURE;
2657
0
            }
2658
0
            nsIContent* otherContent = otherNode->AsContent();
2659
0
            rv = WSRunObject::PrepareToDeleteNode(&HTMLEditorRef(),
2660
0
                                                  otherContent);
2661
0
            if (NS_WARN_IF(!CanHandleEditAction())) {
2662
0
              return NS_ERROR_EDITOR_DESTROYED;
2663
0
            }
2664
0
            if (NS_WARN_IF(NS_FAILED(rv))) {
2665
0
              return rv;
2666
0
            }
2667
0
            rv = HTMLEditorRef().DeleteNodeWithTransaction(*otherContent);
2668
0
            if (NS_WARN_IF(!CanHandleEditAction())) {
2669
0
              return NS_ERROR_EDITOR_DESTROYED;
2670
0
            }
2671
0
            if (NS_WARN_IF(NS_FAILED(rv))) {
2672
0
              return rv;
2673
0
            }
2674
0
          }
2675
0
2676
0
          return NS_OK;
2677
0
        }
2678
0
        // Else continue with normal delete code
2679
0
      }
2680
0
2681
0
      if (NS_WARN_IF(!visNode->IsContent())) {
2682
0
        return NS_ERROR_FAILURE;
2683
0
      }
2684
0
      // Found break or image, or hr.
2685
0
      rv = WSRunObject::PrepareToDeleteNode(&HTMLEditorRef(),
2686
0
                                            visNode->AsContent());
2687
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2688
0
        return NS_ERROR_EDITOR_DESTROYED;
2689
0
      }
2690
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2691
0
        return rv;
2692
0
      }
2693
0
      // Remember sibling to visnode, if any
2694
0
      nsCOMPtr<nsIContent> sibling =
2695
0
        HTMLEditorRef().GetPriorHTMLSibling(visNode);
2696
0
      // Delete the node, and join like nodes if appropriate
2697
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*visNode);
2698
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2699
0
        return NS_ERROR_EDITOR_DESTROYED;
2700
0
      }
2701
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2702
0
        return rv;
2703
0
      }
2704
0
      // We did something, so let's say so.
2705
0
      *aHandled = true;
2706
0
      // Is there a prior node and are they siblings?
2707
0
      nsCOMPtr<nsINode> stepbrother;
2708
0
      if (sibling) {
2709
0
        stepbrother = HTMLEditorRef().GetNextHTMLSibling(sibling);
2710
0
      }
2711
0
      // Are they both text nodes?  If so, join them!
2712
0
      if (startPoint.GetContainer() == stepbrother &&
2713
0
          startPoint.GetContainerAsText() &&
2714
0
          sibling->GetAsText()) {
2715
0
        EditorDOMPoint pt;
2716
0
        nsresult rv =
2717
0
          JoinNearestEditableNodesWithTransaction(
2718
0
            *sibling, *startPoint.GetContainerAsContent(), &pt);
2719
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
2720
0
          return rv;
2721
0
        }
2722
0
        if (NS_WARN_IF(!pt.IsSet())) {
2723
0
          return NS_ERROR_FAILURE;
2724
0
        }
2725
0
        // Fix up selection
2726
0
        ErrorResult error;
2727
0
        SelectionRef().Collapse(pt, error);
2728
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
2729
0
          error.SuppressException();
2730
0
          return NS_ERROR_EDITOR_DESTROYED;
2731
0
        }
2732
0
        if (NS_WARN_IF(error.Failed())) {
2733
0
          return error.StealNSResult();
2734
0
        }
2735
0
      }
2736
0
      rv = InsertBRIfNeeded();
2737
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2738
0
        return rv;
2739
0
      }
2740
0
      return NS_OK;
2741
0
    }
2742
0
2743
0
    if (wsType == WSType::otherBlock) {
2744
0
      // Make sure it's not a table element.  If so, cancel the operation
2745
0
      // (translation: users cannot backspace or delete across table cells)
2746
0
      if (HTMLEditUtils::IsTableElement(visNode)) {
2747
0
        *aCancel = true;
2748
0
        return NS_OK;
2749
0
      }
2750
0
2751
0
      // Next to a block.  See if we are between a block and a br.  If so, we
2752
0
      // really want to delete the br.  Else join content at selection to the
2753
0
      // block.
2754
0
      bool bDeletedBR = false;
2755
0
      WSType otherWSType;
2756
0
      nsCOMPtr<nsINode> otherNode;
2757
0
2758
0
      // Find node in other direction
2759
0
      if (aAction == nsIEditor::eNext) {
2760
0
        wsObj.PriorVisibleNode(startPoint,
2761
0
                               address_of(otherNode), nullptr,
2762
0
                               &otherWSType);
2763
0
      } else {
2764
0
        wsObj.NextVisibleNode(startPoint,
2765
0
                              address_of(otherNode), nullptr,
2766
0
                              &otherWSType);
2767
0
      }
2768
0
2769
0
      // First find the adjacent node in the block
2770
0
      nsCOMPtr<nsIContent> leafNode;
2771
0
      nsCOMPtr<nsINode> leftNode, rightNode;
2772
0
      if (aAction == nsIEditor::ePrevious) {
2773
0
        leafNode = HTMLEditorRef().GetLastEditableLeaf(*visNode);
2774
0
        leftNode = leafNode;
2775
0
        rightNode = startPoint.GetContainer();
2776
0
      } else {
2777
0
        leafNode = HTMLEditorRef().GetFirstEditableLeaf(*visNode);
2778
0
        leftNode = startPoint.GetContainer();
2779
0
        rightNode = leafNode;
2780
0
      }
2781
0
2782
0
      if (otherNode->IsHTMLElement(nsGkAtoms::br)) {
2783
0
        rv = HTMLEditorRef().DeleteNodeWithTransaction(*otherNode);
2784
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
2785
0
          return NS_ERROR_EDITOR_DESTROYED;
2786
0
        }
2787
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
2788
0
          return rv;
2789
0
        }
2790
0
        // XXX Only in this case, setting "handled" to true only when it
2791
0
        //     succeeds?
2792
0
        *aHandled = true;
2793
0
        bDeletedBR = true;
2794
0
      }
2795
0
2796
0
      // Don't cross table boundaries
2797
0
      if (leftNode && rightNode &&
2798
0
          InDifferentTableElements(leftNode, rightNode)) {
2799
0
        return NS_OK;
2800
0
      }
2801
0
2802
0
      if (bDeletedBR) {
2803
0
        // Put selection at edge of block and we are done.
2804
0
        if (NS_WARN_IF(!leafNode)) {
2805
0
          return NS_ERROR_FAILURE;
2806
0
        }
2807
0
        EditorDOMPoint newSel = GetGoodSelPointForNode(*leafNode, aAction);
2808
0
        if (NS_WARN_IF(!newSel.IsSet())) {
2809
0
          return NS_ERROR_FAILURE;
2810
0
        }
2811
0
        IgnoredErrorResult error;
2812
0
        SelectionRef().Collapse(newSel, error);
2813
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
2814
0
          return NS_ERROR_EDITOR_DESTROYED;
2815
0
        }
2816
0
        NS_WARNING_ASSERTION(!error.Failed(),
2817
0
          "Failed to collapse selection at edge of the block");
2818
0
        return NS_OK;
2819
0
      }
2820
0
2821
0
      // Else we are joining content to block
2822
0
2823
0
      EditorDOMPoint selPoint(startPoint);
2824
0
      {
2825
0
        AutoTrackDOMPoint tracker(HTMLEditorRef().mRangeUpdater, &selPoint);
2826
0
        if (NS_WARN_IF(!leftNode) ||
2827
0
            NS_WARN_IF(!leftNode->IsContent()) ||
2828
0
            NS_WARN_IF(!rightNode) ||
2829
0
            NS_WARN_IF(!rightNode->IsContent())) {
2830
0
          return NS_ERROR_FAILURE;
2831
0
        }
2832
0
        EditActionResult ret =
2833
0
          TryToJoinBlocksWithTransaction(*leftNode->AsContent(),
2834
0
                                         *rightNode->AsContent());
2835
0
        *aHandled |= ret.Handled();
2836
0
        *aCancel |= ret.Canceled();
2837
0
        if (NS_WARN_IF(ret.Failed())) {
2838
0
          return ret.Rv();
2839
0
        }
2840
0
      }
2841
0
2842
0
      // If TryToJoinBlocksWithTransaction() didn't handle it  and it's not
2843
0
      // canceled, user may want to modify the start leaf node or the last leaf
2844
0
      // node of the block.
2845
0
      if (!*aHandled && !*aCancel && leafNode != startPoint.GetContainer()) {
2846
0
        int32_t offset =
2847
0
          aAction == nsIEditor::ePrevious ?
2848
0
            static_cast<int32_t>(leafNode->Length()) : 0;
2849
0
        rv = SelectionRef().Collapse(leafNode, offset);
2850
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
2851
0
          return NS_ERROR_EDITOR_DESTROYED;
2852
0
        }
2853
0
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
2854
0
          "Failed to collapse selection at the leaf node");
2855
0
        rv = WillDeleteSelection(aAction, aStripWrappers, aCancel, aHandled);
2856
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
2857
0
          return rv;
2858
0
        }
2859
0
        return NS_OK;
2860
0
      }
2861
0
2862
0
      // Otherwise, we must have deleted the selection as user expected.
2863
0
      IgnoredErrorResult ignored;
2864
0
      SelectionRef().Collapse(selPoint, ignored);
2865
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2866
0
        return NS_ERROR_EDITOR_DESTROYED;
2867
0
      }
2868
0
      NS_WARNING_ASSERTION(!ignored.Failed(),
2869
0
        "Failed to selection at deleted point");
2870
0
      return NS_OK;
2871
0
    }
2872
0
2873
0
    if (wsType == WSType::thisBlock) {
2874
0
      // At edge of our block.  Look beside it and see if we can join to an
2875
0
      // adjacent block
2876
0
2877
0
      // Make sure it's not a table element.  If so, cancel the operation
2878
0
      // (translation: users cannot backspace or delete across table cells)
2879
0
      if (HTMLEditUtils::IsTableElement(visNode)) {
2880
0
        *aCancel = true;
2881
0
        return NS_OK;
2882
0
      }
2883
0
2884
0
      // First find the relevant nodes
2885
0
      nsCOMPtr<nsINode> leftNode, rightNode;
2886
0
      if (aAction == nsIEditor::ePrevious) {
2887
0
        leftNode = HTMLEditorRef().GetPreviousEditableHTMLNode(*visNode);
2888
0
        rightNode = startPoint.GetContainer();
2889
0
      } else {
2890
0
        rightNode = HTMLEditorRef().GetNextEditableHTMLNode(*visNode);
2891
0
        leftNode = startPoint.GetContainer();
2892
0
      }
2893
0
2894
0
      // Nothing to join
2895
0
      if (!leftNode || !rightNode) {
2896
0
        *aCancel = true;
2897
0
        return NS_OK;
2898
0
      }
2899
0
2900
0
      // Don't cross table boundaries -- cancel it
2901
0
      if (InDifferentTableElements(leftNode, rightNode)) {
2902
0
        *aCancel = true;
2903
0
        return NS_OK;
2904
0
      }
2905
0
2906
0
      EditorDOMPoint selPoint(startPoint);
2907
0
      {
2908
0
        AutoTrackDOMPoint tracker(HTMLEditorRef().mRangeUpdater, &selPoint);
2909
0
        if (NS_WARN_IF(!leftNode->IsContent()) ||
2910
0
            NS_WARN_IF(!rightNode->IsContent())) {
2911
0
          return NS_ERROR_FAILURE;
2912
0
        }
2913
0
        EditActionResult ret =
2914
0
          TryToJoinBlocksWithTransaction(*leftNode->AsContent(),
2915
0
                                         *rightNode->AsContent());
2916
0
        // This should claim that trying to join the block means that
2917
0
        // this handles the action because the caller shouldn't do anything
2918
0
        // anymore in this case.
2919
0
        *aHandled = true;
2920
0
        *aCancel |= ret.Canceled();
2921
0
        if (NS_WARN_IF(ret.Failed())) {
2922
0
          return ret.Rv();
2923
0
        }
2924
0
      }
2925
0
      IgnoredErrorResult ignored;
2926
0
      SelectionRef().Collapse(selPoint, ignored);
2927
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2928
0
        return NS_ERROR_EDITOR_DESTROYED;
2929
0
      }
2930
0
      NS_WARNING_ASSERTION(!ignored.Failed(), "Failed to collapse selection");
2931
0
      return NS_OK;
2932
0
    }
2933
0
  }
2934
0
2935
0
2936
0
  // Else we have a non-collapsed selection.  First adjust the selection.
2937
0
  rv = ExpandSelectionForDeletion();
2938
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2939
0
    return rv;
2940
0
  }
2941
0
2942
0
  // Remember that we did a ranged delete for the benefit of AfterEditInner().
2943
0
  mDidRangedDelete = true;
2944
0
2945
0
  // Refresh start and end points
2946
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
2947
0
  if (NS_WARN_IF(!firstRange)) {
2948
0
    return NS_ERROR_FAILURE;
2949
0
  }
2950
0
  nsCOMPtr<nsINode> startNode = firstRange->GetStartContainer();
2951
0
  if (NS_WARN_IF(!startNode)) {
2952
0
    return NS_ERROR_FAILURE;
2953
0
  }
2954
0
  int32_t startOffset = firstRange->StartOffset();
2955
0
  nsCOMPtr<nsINode> endNode = firstRange->GetEndContainer();
2956
0
  if (NS_WARN_IF(!endNode)) {
2957
0
    return NS_ERROR_FAILURE;
2958
0
  }
2959
0
  int32_t endOffset = firstRange->EndOffset();
2960
0
2961
0
  // Figure out if the endpoints are in nodes that can be merged.  Adjust
2962
0
  // surrounding whitespace in preparation to delete selection.
2963
0
  if (!IsPlaintextEditor()) {
2964
0
    AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
2965
0
    rv = WSRunObject::PrepareToDeleteRange(&HTMLEditorRef(),
2966
0
                                           address_of(startNode), &startOffset,
2967
0
                                           address_of(endNode), &endOffset);
2968
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
2969
0
      return NS_ERROR_EDITOR_DESTROYED;
2970
0
    }
2971
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2972
0
      return rv;
2973
0
    }
2974
0
  }
2975
0
2976
0
  {
2977
0
    // Track location of where we are deleting
2978
0
    AutoTrackDOMPoint startTracker(HTMLEditorRef().mRangeUpdater,
2979
0
                                   address_of(startNode), &startOffset);
2980
0
    AutoTrackDOMPoint endTracker(HTMLEditorRef().mRangeUpdater,
2981
0
                                 address_of(endNode), &endOffset);
2982
0
    // We are handling all ranged deletions directly now.
2983
0
    *aHandled = true;
2984
0
2985
0
    if (endNode == startNode) {
2986
0
      rv = HTMLEditorRef().DeleteSelectionWithTransaction(aAction,
2987
0
                                                          aStripWrappers);
2988
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
2989
0
        return NS_ERROR_EDITOR_DESTROYED;
2990
0
      }
2991
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2992
0
        return rv;
2993
0
      }
2994
0
    } else {
2995
0
      // Figure out mailcite ancestors
2996
0
      nsCOMPtr<Element> startCiteNode = GetTopEnclosingMailCite(*startNode);
2997
0
      nsCOMPtr<Element> endCiteNode = GetTopEnclosingMailCite(*endNode);
2998
0
2999
0
      // If we only have a mailcite at one of the two endpoints, set the
3000
0
      // directionality of the deletion so that the selection will end up
3001
0
      // outside the mailcite.
3002
0
      if (startCiteNode && !endCiteNode) {
3003
0
        aAction = nsIEditor::eNext;
3004
0
      } else if (!startCiteNode && endCiteNode) {
3005
0
        aAction = nsIEditor::ePrevious;
3006
0
      }
3007
0
3008
0
      // Figure out block parents
3009
0
      nsCOMPtr<Element> leftParent = HTMLEditor::GetBlock(*startNode);
3010
0
      nsCOMPtr<Element> rightParent = HTMLEditor::GetBlock(*endNode);
3011
0
3012
0
      // Are endpoint block parents the same?  Use default deletion
3013
0
      if (leftParent && leftParent == rightParent) {
3014
0
        HTMLEditorRef().DeleteSelectionWithTransaction(aAction, aStripWrappers);
3015
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
3016
0
          return NS_ERROR_EDITOR_DESTROYED;
3017
0
        }
3018
0
      } else {
3019
0
        // Deleting across blocks.  Are the blocks of same type?
3020
0
        if (NS_WARN_IF(!leftParent) || NS_WARN_IF(!rightParent)) {
3021
0
          return NS_ERROR_FAILURE;
3022
0
        }
3023
0
3024
0
        // Are the blocks siblings?
3025
0
        nsCOMPtr<nsINode> leftBlockParent = leftParent->GetParentNode();
3026
0
        nsCOMPtr<nsINode> rightBlockParent = rightParent->GetParentNode();
3027
0
3028
0
        // MOOSE: this could conceivably screw up a table.. fix me.
3029
0
        if (leftBlockParent == rightBlockParent &&
3030
0
            HTMLEditorRef().AreNodesSameType(*leftParent, *rightParent) &&
3031
0
            // XXX What's special about these three types of block?
3032
0
            (leftParent->IsHTMLElement(nsGkAtoms::p) ||
3033
0
             HTMLEditUtils::IsListItem(leftParent) ||
3034
0
             HTMLEditUtils::IsHeader(*leftParent))) {
3035
0
          // First delete the selection
3036
0
          rv = HTMLEditorRef().DeleteSelectionWithTransaction(aAction,
3037
0
                                                              aStripWrappers);
3038
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
3039
0
            return NS_ERROR_EDITOR_DESTROYED;
3040
0
          }
3041
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
3042
0
            return rv;
3043
0
          }
3044
0
          // Join blocks
3045
0
          EditorDOMPoint pt =
3046
0
            HTMLEditorRef().JoinNodesDeepWithTransaction(*leftParent,
3047
0
                                                         *rightParent);
3048
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
3049
0
            return NS_ERROR_EDITOR_DESTROYED;
3050
0
          }
3051
0
          if (NS_WARN_IF(!pt.IsSet())) {
3052
0
            return NS_ERROR_FAILURE;
3053
0
          }
3054
0
          // Fix up selection
3055
0
          ErrorResult error;
3056
0
          SelectionRef().Collapse(pt, error);
3057
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
3058
0
            error.SuppressException();
3059
0
            return NS_ERROR_EDITOR_DESTROYED;
3060
0
          }
3061
0
          if (NS_WARN_IF(error.Failed())) {
3062
0
            return error.StealNSResult();
3063
0
          }
3064
0
          return NS_OK;
3065
0
        }
3066
0
3067
0
        // Else blocks not same type, or not siblings.  Delete everything
3068
0
        // except table elements.
3069
0
        join = true;
3070
0
3071
0
        AutoRangeArray arrayOfRanges(&SelectionRef());
3072
0
        for (auto& range : arrayOfRanges.mRanges) {
3073
0
          // Build a list of nodes in the range
3074
0
          nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
3075
0
          TrivialFunctor functor;
3076
0
          DOMSubtreeIterator iter;
3077
0
          nsresult rv = iter.Init(*range);
3078
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
3079
0
            return rv;
3080
0
          }
3081
0
          iter.AppendList(functor, arrayOfNodes);
3082
0
3083
0
          // Now that we have the list, delete non-table elements
3084
0
          int32_t listCount = arrayOfNodes.Length();
3085
0
          for (int32_t j = 0; j < listCount; j++) {
3086
0
            OwningNonNull<nsINode> node = arrayOfNodes[0];
3087
0
            nsresult rv = DeleteElementsExceptTableRelatedElements(node);
3088
0
            if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
3089
0
              return NS_ERROR_EDITOR_DESTROYED;
3090
0
            }
3091
0
            NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
3092
0
              "Failed to elements except table related elements");
3093
0
            arrayOfNodes.RemoveElementAt(0);
3094
0
            // If something visible is deleted, no need to join.  Visible means
3095
0
            // all nodes except non-visible textnodes and breaks.
3096
0
            if (join && origCollapsed) {
3097
0
              if (!node->IsContent()) {
3098
0
                join = false;
3099
0
                continue;
3100
0
              }
3101
0
              nsIContent* content = node->AsContent();
3102
0
              if (Text* text = content->GetAsText()) {
3103
0
                join = !HTMLEditorRef().IsInVisibleTextFrames(*text);
3104
0
              } else {
3105
0
                join = content->IsHTMLElement(nsGkAtoms::br) &&
3106
0
                       !HTMLEditorRef().IsVisibleBRElement(node);
3107
0
              }
3108
0
            }
3109
0
          }
3110
0
        }
3111
0
3112
0
        // Check endpoints for possible text deletion.  We can assume that if
3113
0
        // text node is found, we can delete to end or to begining as
3114
0
        // appropriate, since the case where both sel endpoints in same text
3115
0
        // node was already handled (we wouldn't be here)
3116
0
        if (startNode->GetAsText() &&
3117
0
            startNode->Length() > static_cast<uint32_t>(startOffset)) {
3118
0
          // Delete to last character
3119
0
          OwningNonNull<CharacterData> dataNode =
3120
0
            *static_cast<CharacterData*>(startNode.get());
3121
0
          rv = HTMLEditorRef().DeleteTextWithTransaction(
3122
0
                                 dataNode, startOffset,
3123
0
                                 startNode->Length() - startOffset);
3124
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
3125
0
            return NS_ERROR_EDITOR_DESTROYED;
3126
0
          }
3127
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
3128
0
            return rv;
3129
0
          }
3130
0
        }
3131
0
        if (endNode->GetAsText() && endOffset) {
3132
0
          // Delete to first character
3133
0
          OwningNonNull<CharacterData> dataNode =
3134
0
            *static_cast<CharacterData*>(endNode.get());
3135
0
          rv = HTMLEditorRef().DeleteTextWithTransaction(dataNode, 0,
3136
0
                                                         endOffset);
3137
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
3138
0
            return NS_ERROR_EDITOR_DESTROYED;
3139
0
          }
3140
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
3141
0
            return rv;
3142
0
          }
3143
0
        }
3144
0
3145
0
        if (join) {
3146
0
          EditActionResult ret =
3147
0
            TryToJoinBlocksWithTransaction(*leftParent, *rightParent);
3148
0
          MOZ_ASSERT(*aHandled);
3149
0
          *aCancel |= ret.Canceled();
3150
0
          if (NS_WARN_IF(ret.Failed())) {
3151
0
            return ret.Rv();
3152
0
          }
3153
0
        }
3154
0
      }
3155
0
    }
3156
0
  }
3157
0
3158
0
  // We might have left only collapsed whitespace in the start/end nodes
3159
0
  {
3160
0
    AutoTrackDOMPoint startTracker(HTMLEditorRef().mRangeUpdater,
3161
0
                                   address_of(startNode), &startOffset);
3162
0
    AutoTrackDOMPoint endTracker(HTMLEditorRef().mRangeUpdater,
3163
0
                                 address_of(endNode), &endOffset);
3164
0
3165
0
    nsresult rv = DeleteNodeIfCollapsedText(*startNode);
3166
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
3167
0
      return NS_ERROR_EDITOR_DESTROYED;
3168
0
    }
3169
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
3170
0
      "Failed to delete start node even though it's collapsed text");
3171
0
    rv = DeleteNodeIfCollapsedText(*endNode);
3172
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
3173
0
      return NS_ERROR_EDITOR_DESTROYED;
3174
0
    }
3175
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
3176
0
      "Failed to delete end node even though it's collapsed text");
3177
0
  }
3178
0
3179
0
  // If we're joining blocks: if deleting forward the selection should be
3180
0
  // collapsed to the end of the selection, if deleting backward the selection
3181
0
  // should be collapsed to the beginning of the selection. But if we're not
3182
0
  // joining then the selection should collapse to the beginning of the
3183
0
  // selection if we'redeleting forward, because the end of the selection will
3184
0
  // still be in the next block. And same thing for deleting backwards
3185
0
  // (selection should collapse to the end, because the beginning will still be
3186
0
  // in the first block). See Bug 507936
3187
0
  if (aAction == (join ? nsIEditor::eNext : nsIEditor::ePrevious)) {
3188
0
    rv = SelectionRef().Collapse(endNode, endOffset);
3189
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
3190
0
      return NS_ERROR_EDITOR_DESTROYED;
3191
0
    }
3192
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3193
0
      return rv;
3194
0
    }
3195
0
  } else {
3196
0
    rv = SelectionRef().Collapse(startNode, startOffset);
3197
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
3198
0
      return NS_ERROR_EDITOR_DESTROYED;
3199
0
    }
3200
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3201
0
      return rv;
3202
0
    }
3203
0
  }
3204
0
  return NS_OK;
3205
0
}
3206
3207
nsresult
3208
HTMLEditRules::DeleteNodeIfCollapsedText(nsINode& aNode)
3209
0
{
3210
0
  MOZ_ASSERT(IsEditorDataAvailable());
3211
0
3212
0
  Text* text = aNode.GetAsText();
3213
0
  if (!text) {
3214
0
    return NS_OK;
3215
0
  }
3216
0
3217
0
  if (HTMLEditorRef().IsVisibleTextNode(*text)) {
3218
0
    return NS_OK;
3219
0
  }
3220
0
3221
0
  nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(aNode);
3222
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
3223
0
    return NS_ERROR_EDITOR_DESTROYED;
3224
0
  }
3225
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3226
0
    return rv;
3227
0
  }
3228
0
  return NS_OK;
3229
0
}
3230
3231
nsresult
3232
HTMLEditRules::InsertBRIfNeeded()
3233
0
{
3234
0
  MOZ_ASSERT(IsEditorDataAvailable());
3235
0
3236
0
  EditorRawDOMPoint atStartOfSelection(
3237
0
                      EditorBase::GetStartPoint(&SelectionRef()));
3238
0
  if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
3239
0
    return NS_ERROR_FAILURE;
3240
0
  }
3241
0
3242
0
  // inline elements don't need any br
3243
0
  if (!IsBlockNode(*atStartOfSelection.GetContainer())) {
3244
0
    return NS_OK;
3245
0
  }
3246
0
3247
0
  // examine selection
3248
0
  WSRunObject wsObj(&HTMLEditorRef(), atStartOfSelection);
3249
0
  if (((wsObj.mStartReason & WSType::block) ||
3250
0
       (wsObj.mStartReason & WSType::br)) &&
3251
0
      (wsObj.mEndReason & WSType::block)) {
3252
0
    // if we are tucked between block boundaries then insert a br
3253
0
    // first check that we are allowed to
3254
0
    if (HTMLEditorRef().CanContainTag(*atStartOfSelection.GetContainer(),
3255
0
                                      *nsGkAtoms::br)) {
3256
0
      RefPtr<Element> brElement =
3257
0
        HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
3258
0
                                                       atStartOfSelection,
3259
0
                                                       nsIEditor::ePrevious);
3260
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
3261
0
        return NS_ERROR_EDITOR_DESTROYED;
3262
0
      }
3263
0
      if (NS_WARN_IF(!brElement)) {
3264
0
        return NS_ERROR_FAILURE;
3265
0
      }
3266
0
      return NS_OK;
3267
0
    }
3268
0
  }
3269
0
  return NS_OK;
3270
0
}
3271
3272
EditorDOMPoint
3273
HTMLEditRules::GetGoodSelPointForNode(nsINode& aNode,
3274
                                      nsIEditor::EDirection aAction)
3275
0
{
3276
0
  MOZ_ASSERT(IsEditorDataAvailable());
3277
0
  MOZ_ASSERT(aAction == nsIEditor::eNext ||
3278
0
             aAction == nsIEditor::eNextWord ||
3279
0
             aAction == nsIEditor::ePrevious ||
3280
0
             aAction == nsIEditor::ePreviousWord ||
3281
0
             aAction == nsIEditor::eToBeginningOfLine ||
3282
0
             aAction == nsIEditor::eToEndOfLine);
3283
0
3284
0
  bool isPreviousAction = (aAction == nsIEditor::ePrevious ||
3285
0
                           aAction == nsIEditor::ePreviousWord ||
3286
0
                           aAction == nsIEditor::eToBeginningOfLine);
3287
0
3288
0
  if (aNode.GetAsText() || HTMLEditorRef().IsContainer(&aNode) ||
3289
0
      NS_WARN_IF(!aNode.GetParentNode())) {
3290
0
    return EditorDOMPoint(&aNode, isPreviousAction ? aNode.Length() : 0);
3291
0
  }
3292
0
3293
0
  if (NS_WARN_IF(!aNode.IsContent())) {
3294
0
    return EditorDOMPoint();
3295
0
  }
3296
0
3297
0
  EditorDOMPoint ret(&aNode);
3298
0
  if ((!aNode.IsHTMLElement(nsGkAtoms::br) ||
3299
0
       HTMLEditorRef().IsVisibleBRElement(&aNode)) && isPreviousAction) {
3300
0
    ret.AdvanceOffset();
3301
0
  }
3302
0
  return ret;
3303
0
}
3304
3305
EditActionResult
3306
HTMLEditRules::TryToJoinBlocksWithTransaction(nsIContent& aLeftNode,
3307
                                              nsIContent& aRightNode)
3308
0
{
3309
0
  MOZ_ASSERT(IsEditorDataAvailable());
3310
0
3311
0
  RefPtr<Element> leftBlock = HTMLEditorRef().GetBlock(aLeftNode);
3312
0
  RefPtr<Element> rightBlock = HTMLEditorRef().GetBlock(aRightNode);
3313
0
3314
0
  // Sanity checks
3315
0
  if (NS_WARN_IF(!leftBlock) || NS_WARN_IF(!rightBlock)) {
3316
0
    return EditActionIgnored(NS_ERROR_NULL_POINTER);
3317
0
  }
3318
0
  if (NS_WARN_IF(leftBlock == rightBlock)) {
3319
0
    return EditActionIgnored(NS_ERROR_UNEXPECTED);
3320
0
  }
3321
0
3322
0
  if (HTMLEditUtils::IsTableElement(leftBlock) ||
3323
0
      HTMLEditUtils::IsTableElement(rightBlock)) {
3324
0
    // Do not try to merge table elements
3325
0
    return EditActionCanceled();
3326
0
  }
3327
0
3328
0
  // Make sure we don't try to move things into HR's, which look like blocks
3329
0
  // but aren't containers
3330
0
  if (leftBlock->IsHTMLElement(nsGkAtoms::hr)) {
3331
0
    leftBlock = HTMLEditorRef().GetBlockNodeParent(leftBlock);
3332
0
    if (NS_WARN_IF(!leftBlock)) {
3333
0
      return EditActionIgnored(NS_ERROR_UNEXPECTED);
3334
0
    }
3335
0
  }
3336
0
  if (rightBlock->IsHTMLElement(nsGkAtoms::hr)) {
3337
0
    rightBlock = HTMLEditorRef().GetBlockNodeParent(rightBlock);
3338
0
    if (NS_WARN_IF(!rightBlock)) {
3339
0
      return EditActionIgnored(NS_ERROR_UNEXPECTED);
3340
0
    }
3341
0
  }
3342
0
3343
0
  // Bail if both blocks the same
3344
0
  if (leftBlock == rightBlock) {
3345
0
    return EditActionIgnored();
3346
0
  }
3347
0
3348
0
  // Joining a list item to its parent is a NOP.
3349
0
  if (HTMLEditUtils::IsList(leftBlock) &&
3350
0
      HTMLEditUtils::IsListItem(rightBlock) &&
3351
0
      rightBlock->GetParentNode() == leftBlock) {
3352
0
    return EditActionHandled();
3353
0
  }
3354
0
3355
0
  // Special rule here: if we are trying to join list items, and they are in
3356
0
  // different lists, join the lists instead.
3357
0
  bool mergeLists = false;
3358
0
  nsAtom* existingList = nsGkAtoms::_empty;
3359
0
  EditorDOMPoint atChildInBlock;
3360
0
  nsCOMPtr<Element> leftList, rightList;
3361
0
  if (HTMLEditUtils::IsListItem(leftBlock) &&
3362
0
      HTMLEditUtils::IsListItem(rightBlock)) {
3363
0
    leftList = leftBlock->GetParentElement();
3364
0
    rightList = rightBlock->GetParentElement();
3365
0
    if (leftList && rightList && leftList != rightList &&
3366
0
        !EditorUtils::IsDescendantOf(*leftList, *rightBlock, &atChildInBlock) &&
3367
0
        !EditorUtils::IsDescendantOf(*rightList, *leftBlock, &atChildInBlock)) {
3368
0
      // There are some special complications if the lists are descendants of
3369
0
      // the other lists' items.  Note that it is okay for them to be
3370
0
      // descendants of the other lists themselves, which is the usual case for
3371
0
      // sublists in our implementation.
3372
0
      MOZ_DIAGNOSTIC_ASSERT(!atChildInBlock.IsSet());
3373
0
      leftBlock = leftList;
3374
0
      rightBlock = rightList;
3375
0
      mergeLists = true;
3376
0
      existingList = leftList->NodeInfo()->NameAtom();
3377
0
    }
3378
0
  }
3379
0
3380
0
  AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
3381
0
3382
0
  // offset below is where you find yourself in rightBlock when you traverse
3383
0
  // upwards from leftBlock
3384
0
  EditorDOMPoint atRightBlockChild;
3385
0
  if (EditorUtils::IsDescendantOf(*leftBlock, *rightBlock,
3386
0
                                  &atRightBlockChild)) {
3387
0
    // Tricky case.  Left block is inside right block.  Do ws adjustment.  This
3388
0
    // just destroys non-visible ws at boundaries we will be joining.
3389
0
    DebugOnly<bool> advanced = atRightBlockChild.AdvanceOffset();
3390
0
    NS_WARNING_ASSERTION(advanced,
3391
0
      "Failed to advance offset to after child of rightBlock, "
3392
0
      "leftBlock is a descendant of the child");
3393
0
    nsresult rv = WSRunObject::ScrubBlockBoundary(&HTMLEditorRef(),
3394
0
                                                  WSRunObject::kBlockEnd,
3395
0
                                                  leftBlock);
3396
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
3397
0
      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3398
0
    }
3399
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3400
0
      return EditActionIgnored(rv);
3401
0
    }
3402
0
3403
0
    {
3404
0
      // We can't just track rightBlock because it's an Element.
3405
0
      AutoTrackDOMPoint tracker(HTMLEditorRef().mRangeUpdater,
3406
0
                                &atRightBlockChild);
3407
0
      rv = WSRunObject::ScrubBlockBoundary(&HTMLEditorRef(),
3408
0
                                           WSRunObject::kAfterBlock,
3409
0
                                           atRightBlockChild.GetContainer(),
3410
0
                                           atRightBlockChild.Offset());
3411
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
3412
0
        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3413
0
      }
3414
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
3415
0
        return EditActionIgnored(rv);
3416
0
      }
3417
0
3418
0
      // XXX AutoTrackDOMPoint instance, tracker, hasn't been destroyed here.
3419
0
      //     Do we really need to do update rightBlock here??
3420
0
      MOZ_ASSERT(rightBlock == atRightBlockChild.GetContainer());
3421
0
      if (atRightBlockChild.GetContainerAsElement()) {
3422
0
        rightBlock = atRightBlockChild.GetContainerAsElement();
3423
0
      } else {
3424
0
        if (NS_WARN_IF(!atRightBlockChild.GetContainer()->GetParentElement())) {
3425
0
          return EditActionIgnored(NS_ERROR_UNEXPECTED);
3426
0
        }
3427
0
        rightBlock = atRightBlockChild.GetContainer()->GetParentElement();
3428
0
      }
3429
0
    }
3430
0
3431
0
    // Do br adjustment.
3432
0
    RefPtr<Element> brNode =
3433
0
      CheckForInvisibleBR(*leftBlock, BRLocation::blockEnd);
3434
0
    EditActionResult ret(NS_OK);
3435
0
    if (NS_WARN_IF(mergeLists)) {
3436
0
      // Since 2002, here was the following comment:
3437
0
      // > The idea here is to take all children in rightList that are past
3438
0
      // > offset, and pull them into leftlist.
3439
0
      // However, this has never been performed because we are here only when
3440
0
      // neither left list nor right list is a descendant of the other but
3441
0
      // in such case, getting a list item in the right list node almost
3442
0
      // always failed since a variable for offset of rightList->GetChildAt()
3443
0
      // was not initialized.  So, it might be a bug, but we should keep this
3444
0
      // traditional behavior for now.  If you find when we get here, please
3445
0
      // remove this comment if we don't need to do it.  Otherwise, please
3446
0
      // move children of the right list node to the end of the left list node.
3447
0
      MOZ_DIAGNOSTIC_ASSERT(!atChildInBlock.IsSet());
3448
0
3449
0
      // XXX Although, we don't do nothing here, but for keeping traditional
3450
0
      //     behavior, we should mark as handled.
3451
0
      ret.MarkAsHandled();
3452
0
    } else {
3453
0
      // XXX Why do we ignore the result of MoveBlock()?
3454
0
      EditActionResult retMoveBlock =
3455
0
        MoveBlock(*leftBlock, *rightBlock,
3456
0
                  -1, atRightBlockChild.Offset());
3457
0
      if (NS_WARN_IF(retMoveBlock.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
3458
0
        return ret;
3459
0
      }
3460
0
      NS_WARNING_ASSERTION(retMoveBlock.Succeeded(),
3461
0
        "Failed to move contents of the right block to the left block");
3462
0
      if (retMoveBlock.Handled()) {
3463
0
        ret.MarkAsHandled();
3464
0
      }
3465
0
      // Now, all children of rightBlock were moved to leftBlock.  So,
3466
0
      // atRightBlockChild is now invalid.
3467
0
      atRightBlockChild.Clear();
3468
0
    }
3469
0
    if (brNode) {
3470
0
      nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(*brNode);
3471
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
3472
0
        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3473
0
      }
3474
0
      if (NS_SUCCEEDED(rv)) {
3475
0
        ret.MarkAsHandled();
3476
0
      } else {
3477
0
        NS_WARNING("Failed to remove the <br> element");
3478
0
      }
3479
0
    }
3480
0
    return ret;
3481
0
  }
3482
0
3483
0
  MOZ_DIAGNOSTIC_ASSERT(!atRightBlockChild.IsSet());
3484
0
3485
0
  // Offset below is where you find yourself in leftBlock when you traverse
3486
0
  // upwards from rightBlock
3487
0
  EditorDOMPoint leftBlockChild;
3488
0
  if (EditorUtils::IsDescendantOf(*rightBlock, *leftBlock, &leftBlockChild)) {
3489
0
    // Tricky case.  Right block is inside left block.  Do ws adjustment.  This
3490
0
    // just destroys non-visible ws at boundaries we will be joining.
3491
0
    nsresult rv = WSRunObject::ScrubBlockBoundary(&HTMLEditorRef(),
3492
0
                                                  WSRunObject::kBlockStart,
3493
0
                                                  rightBlock);
3494
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
3495
0
      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3496
0
    }
3497
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3498
0
      return EditActionIgnored(rv);
3499
0
    }
3500
0
3501
0
    {
3502
0
      // We can't just track leftBlock because it's an Element, so track
3503
0
      // something else.
3504
0
      AutoTrackDOMPoint tracker(HTMLEditorRef().mRangeUpdater, &leftBlockChild);
3505
0
      rv = WSRunObject::ScrubBlockBoundary(&HTMLEditorRef(),
3506
0
                                           WSRunObject::kBeforeBlock,
3507
0
                                           leftBlock, leftBlockChild.Offset());
3508
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
3509
0
        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3510
0
      }
3511
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
3512
0
        return EditActionIgnored(rv);
3513
0
      }
3514
0
      // XXX AutoTrackDOMPoint instance, tracker, hasn't been destroyed here.
3515
0
      //     Do we really need to do update rightBlock here??
3516
0
      MOZ_DIAGNOSTIC_ASSERT(leftBlock == leftBlockChild.GetContainer());
3517
0
      if (leftBlockChild.GetContainerAsElement()) {
3518
0
        leftBlock = leftBlockChild.GetContainerAsElement();
3519
0
      } else {
3520
0
        if (NS_WARN_IF(!leftBlockChild.GetContainer()->GetParentElement())) {
3521
0
          return EditActionIgnored(NS_ERROR_UNEXPECTED);
3522
0
        }
3523
0
        leftBlock = leftBlockChild.GetContainer()->GetParentElement();
3524
0
      }
3525
0
    }
3526
0
    // Do br adjustment.
3527
0
    RefPtr<Element> brNode =
3528
0
      CheckForInvisibleBR(*leftBlock, BRLocation::beforeBlock,
3529
0
                          leftBlockChild.Offset());
3530
0
    EditActionResult ret(NS_OK);
3531
0
    if (mergeLists) {
3532
0
      // XXX Why do we ignore the result of MoveContents()?
3533
0
      int32_t offset = leftBlockChild.Offset();
3534
0
      EditActionResult retMoveContents =
3535
0
        MoveContents(*rightList, *leftList, &offset);
3536
0
      if (NS_WARN_IF(retMoveContents.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
3537
0
        return ret;
3538
0
      }
3539
0
      NS_WARNING_ASSERTION(retMoveContents.Succeeded(),
3540
0
        "Failed to move contents from the right list to the left list");
3541
0
      if (retMoveContents.Handled()) {
3542
0
        ret.MarkAsHandled();
3543
0
      }
3544
0
      // leftBlockChild was moved to rightList.  So, it's invalid now.
3545
0
      leftBlockChild.Clear();
3546
0
    } else {
3547
0
      // Left block is a parent of right block, and the parent of the previous
3548
0
      // visible content.  Right block is a child and contains the contents we
3549
0
      // want to move.
3550
0
3551
0
      EditorDOMPoint previousContent;
3552
0
      if (&aLeftNode == leftBlock) {
3553
0
        // We are working with valid HTML, aLeftNode is a block node, and is
3554
0
        // therefore allowed to contain rightBlock.  This is the simple case,
3555
0
        // we will simply move the content in rightBlock out of its block.
3556
0
        previousContent = leftBlockChild;
3557
0
      } else {
3558
0
        // We try to work as well as possible with HTML that's already invalid.
3559
0
        // Although "right block" is a block, and a block must not be contained
3560
0
        // in inline elements, reality is that broken documents do exist.  The
3561
0
        // DIRECT parent of "left NODE" might be an inline element.  Previous
3562
0
        // versions of this code skipped inline parents until the first block
3563
0
        // parent was found (and used "left block" as the destination).
3564
0
        // However, in some situations this strategy moves the content to an
3565
0
        // unexpected position.  (see bug 200416) The new idea is to make the
3566
0
        // moving content a sibling, next to the previous visible content.
3567
0
        previousContent.Set(&aLeftNode);
3568
0
3569
0
        // We want to move our content just after the previous visible node.
3570
0
        previousContent.AdvanceOffset();
3571
0
      }
3572
0
3573
0
      // Because we don't want the moving content to receive the style of the
3574
0
      // previous content, we split the previous content's style.
3575
0
3576
0
      RefPtr<Element> editorRoot = HTMLEditorRef().GetEditorRoot();
3577
0
      if (!editorRoot || &aLeftNode != editorRoot) {
3578
0
        nsCOMPtr<nsIContent> splittedPreviousContent;
3579
0
        nsCOMPtr<nsINode> previousContentParent =
3580
0
          previousContent.GetContainer();
3581
0
        int32_t previousContentOffset = previousContent.Offset();
3582
0
        rv = HTMLEditorRef().SplitStyleAbovePoint(
3583
0
                               address_of(previousContentParent),
3584
0
                               &previousContentOffset,
3585
0
                               nullptr, nullptr, nullptr,
3586
0
                               getter_AddRefs(splittedPreviousContent));
3587
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
3588
0
          return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3589
0
        }
3590
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
3591
0
          return EditActionIgnored(rv);
3592
0
        }
3593
0
3594
0
        if (splittedPreviousContent) {
3595
0
          previousContent.Set(splittedPreviousContent);
3596
0
        } else {
3597
0
          previousContent.Set(previousContentParent, previousContentOffset);
3598
0
        }
3599
0
      }
3600
0
3601
0
      if (NS_WARN_IF(!previousContent.IsSet())) {
3602
0
        return EditActionIgnored(NS_ERROR_NULL_POINTER);
3603
0
      }
3604
0
3605
0
      ret |= MoveBlock(*previousContent.GetContainerAsElement(), *rightBlock,
3606
0
                       previousContent.Offset(), 0);
3607
0
      if (NS_WARN_IF(ret.Failed())) {
3608
0
        return ret;
3609
0
      }
3610
0
    }
3611
0
    if (brNode) {
3612
0
      nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(*brNode);
3613
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
3614
0
        return ret.SetResult(NS_ERROR_EDITOR_DESTROYED);
3615
0
      }
3616
0
      if (NS_SUCCEEDED(rv)) {
3617
0
        ret.MarkAsHandled();
3618
0
      } else {
3619
0
        NS_WARNING("Failed to remove the <br> element");
3620
0
      }
3621
0
    }
3622
0
    return ret;
3623
0
  }
3624
0
3625
0
  MOZ_DIAGNOSTIC_ASSERT(!atRightBlockChild.IsSet());
3626
0
  MOZ_DIAGNOSTIC_ASSERT(!leftBlockChild.IsSet());
3627
0
3628
0
  // Normal case.  Blocks are siblings, or at least close enough.  An example
3629
0
  // of the latter is <p>paragraph</p><ul><li>one<li>two<li>three</ul>.  The
3630
0
  // first li and the p are not true siblings, but we still want to join them
3631
0
  // if you backspace from li into p.
3632
0
3633
0
  // Adjust whitespace at block boundaries
3634
0
  nsresult rv =
3635
0
    WSRunObject::PrepareToJoinBlocks(&HTMLEditorRef(), leftBlock, rightBlock);
3636
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
3637
0
    return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3638
0
  }
3639
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3640
0
    return EditActionIgnored(rv);
3641
0
  }
3642
0
  // Do br adjustment.
3643
0
  nsCOMPtr<Element> brNode =
3644
0
    CheckForInvisibleBR(*leftBlock, BRLocation::blockEnd);
3645
0
  EditActionResult ret(NS_OK);
3646
0
  if (mergeLists || leftBlock->NodeInfo()->NameAtom() ==
3647
0
                    rightBlock->NodeInfo()->NameAtom()) {
3648
0
    // Nodes are same type.  merge them.
3649
0
    EditorDOMPoint pt;
3650
0
    nsresult rv =
3651
0
      JoinNearestEditableNodesWithTransaction(*leftBlock, *rightBlock, &pt);
3652
0
    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
3653
0
      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3654
0
    }
3655
0
    if (pt.IsSet() && mergeLists) {
3656
0
      CreateElementResult convertListTypeResult =
3657
0
        ConvertListType(*rightBlock, *existingList, *nsGkAtoms::li);
3658
0
      if (NS_WARN_IF(convertListTypeResult.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
3659
0
        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3660
0
      }
3661
0
    }
3662
0
    ret.MarkAsHandled();
3663
0
  } else {
3664
0
    // Nodes are dissimilar types.
3665
0
    ret |= MoveBlock(*leftBlock, *rightBlock, -1, 0);
3666
0
    if (NS_WARN_IF(ret.Failed())) {
3667
0
      return ret;
3668
0
    }
3669
0
  }
3670
0
  if (brNode) {
3671
0
    rv = HTMLEditorRef().DeleteNodeWithTransaction(*brNode);
3672
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
3673
0
      return ret.SetResult(NS_ERROR_EDITOR_DESTROYED);
3674
0
    }
3675
0
    // XXX In other top level if blocks, the result of
3676
0
    //     DeleteNodeWithTransaction() is ignored.  Why does only this result
3677
0
    //     is respected?
3678
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3679
0
      return ret.SetResult(rv);
3680
0
    }
3681
0
    ret.MarkAsHandled();
3682
0
  }
3683
0
  return ret;
3684
0
}
3685
3686
EditActionResult
3687
HTMLEditRules::MoveBlock(Element& aLeftBlock,
3688
                         Element& aRightBlock,
3689
                         int32_t aLeftOffset,
3690
                         int32_t aRightOffset)
3691
0
{
3692
0
  MOZ_ASSERT(IsEditorDataAvailable());
3693
0
3694
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
3695
0
  // GetNodesFromPoint is the workhorse that figures out what we wnat to move.
3696
0
  nsresult rv = GetNodesFromPoint(EditorDOMPoint(&aRightBlock, aRightOffset),
3697
0
                                  EditSubAction::eCreateOrChangeList,
3698
0
                                  arrayOfNodes, TouchContent::yes);
3699
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3700
0
    return EditActionIgnored(rv);
3701
0
  }
3702
0
3703
0
  EditActionResult ret(NS_OK);
3704
0
  for (uint32_t i = 0; i < arrayOfNodes.Length(); i++) {
3705
0
    // get the node to act on
3706
0
    if (IsBlockNode(arrayOfNodes[i])) {
3707
0
      // For block nodes, move their contents only, then delete block.
3708
0
      ret |=
3709
0
        MoveContents(*arrayOfNodes[i]->AsElement(), aLeftBlock, &aLeftOffset);
3710
0
      if (NS_WARN_IF(ret.Failed())) {
3711
0
        return ret;
3712
0
      }
3713
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*arrayOfNodes[i]);
3714
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
3715
0
        return ret.SetResult(NS_ERROR_EDITOR_DESTROYED);
3716
0
      }
3717
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
3718
0
        "Failed to remove a block node");
3719
0
      ret.MarkAsHandled();
3720
0
    } else {
3721
0
      // Otherwise move the content as is, checking against the DTD.
3722
0
      ret |=
3723
0
        MoveNodeSmart(*arrayOfNodes[i]->AsContent(), aLeftBlock, &aLeftOffset);
3724
0
      if (NS_WARN_IF(ret.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
3725
0
        return ret;
3726
0
      }
3727
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
3728
0
        "Failed to move current node to the left block");
3729
0
    }
3730
0
  }
3731
0
3732
0
  // XXX We're only checking return value of the last iteration
3733
0
  if (NS_WARN_IF(ret.Failed())) {
3734
0
    return ret;
3735
0
  }
3736
0
3737
0
  return ret;
3738
0
}
3739
3740
EditActionResult
3741
HTMLEditRules::MoveNodeSmart(nsIContent& aNode,
3742
                             Element& aDestElement,
3743
                             int32_t* aInOutDestOffset)
3744
0
{
3745
0
  MOZ_ASSERT(IsEditorDataAvailable());
3746
0
  MOZ_ASSERT(aInOutDestOffset);
3747
0
3748
0
  // Check if this node can go into the destination node
3749
0
  if (HTMLEditorRef().CanContain(aDestElement, aNode)) {
3750
0
    // If it can, move it there.
3751
0
    if (*aInOutDestOffset == -1) {
3752
0
      nsresult rv =
3753
0
        HTMLEditorRef().MoveNodeToEndWithTransaction(aNode, aDestElement);
3754
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
3755
0
        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3756
0
      }
3757
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
3758
0
        return EditActionIgnored(rv);
3759
0
      }
3760
0
    } else {
3761
0
      EditorRawDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset);
3762
0
      nsresult rv =
3763
0
        HTMLEditorRef().MoveNodeWithTransaction(aNode, pointToInsert);
3764
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
3765
0
        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
3766
0
      }
3767
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
3768
0
        return EditActionIgnored(rv);
3769
0
      }
3770
0
    }
3771
0
    if (*aInOutDestOffset != -1) {
3772
0
      (*aInOutDestOffset)++;
3773
0
    }
3774
0
    // XXX Should we check if the node is actually moved in this case?
3775
0
    return EditActionHandled();
3776
0
  }
3777
0
3778
0
  // If it can't, move its children (if any), and then delete it.
3779
0
  EditActionResult ret(NS_OK);
3780
0
  if (aNode.IsElement()) {
3781
0
    ret = MoveContents(*aNode.AsElement(), aDestElement, aInOutDestOffset);
3782
0
    if (NS_WARN_IF(ret.Failed())) {
3783
0
      return ret;
3784
0
    }
3785
0
  }
3786
0
3787
0
  nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(aNode);
3788
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
3789
0
    return ret.SetResult(NS_ERROR_EDITOR_DESTROYED);
3790
0
  }
3791
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3792
0
    return ret.SetResult(rv);
3793
0
  }
3794
0
  return ret.MarkAsHandled();
3795
0
}
3796
3797
EditActionResult
3798
HTMLEditRules::MoveContents(Element& aElement,
3799
                            Element& aDestElement,
3800
                            int32_t* aInOutDestOffset)
3801
0
{
3802
0
  MOZ_ASSERT(aInOutDestOffset);
3803
0
3804
0
  if (NS_WARN_IF(&aElement == &aDestElement)) {
3805
0
    return EditActionIgnored(NS_ERROR_ILLEGAL_VALUE);
3806
0
  }
3807
0
3808
0
  EditActionResult ret(NS_OK);
3809
0
  while (aElement.GetFirstChild()) {
3810
0
    ret |=
3811
0
      MoveNodeSmart(*aElement.GetFirstChild(), aDestElement, aInOutDestOffset);
3812
0
    if (NS_WARN_IF(ret.Failed())) {
3813
0
      return ret;
3814
0
    }
3815
0
  }
3816
0
  return ret;
3817
0
}
3818
3819
nsresult
3820
HTMLEditRules::DeleteElementsExceptTableRelatedElements(nsINode& aNode)
3821
0
{
3822
0
  MOZ_ASSERT(IsEditorDataAvailable());
3823
0
3824
0
  if (!HTMLEditUtils::IsTableElementButNotTable(&aNode)) {
3825
0
    nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(aNode);
3826
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
3827
0
      return NS_ERROR_EDITOR_DESTROYED;
3828
0
    }
3829
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3830
0
      return rv;
3831
0
    }
3832
0
    return NS_OK;
3833
0
  }
3834
0
3835
0
  // XXX For performance, this should just call
3836
0
  //     DeleteElementsExceptTableRelatedElements() while there are children
3837
0
  //     in aNode.  If we need to avoid infinite loop because mutation event
3838
0
  //     listeners can add unexpected nodes into aNode, we should just loop
3839
0
  //     only original count of the children.
3840
0
  AutoTArray<OwningNonNull<nsIContent>, 10> childList;
3841
0
  for (nsIContent* child = aNode.GetFirstChild();
3842
0
       child; child = child->GetNextSibling()) {
3843
0
    childList.AppendElement(*child);
3844
0
  }
3845
0
3846
0
  for (const auto& child: childList) {
3847
0
    nsresult rv = DeleteElementsExceptTableRelatedElements(child);
3848
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3849
0
      return rv;
3850
0
    }
3851
0
  }
3852
0
  return NS_OK;
3853
0
}
3854
3855
nsresult
3856
HTMLEditRules::DidDeleteSelection()
3857
0
{
3858
0
  MOZ_ASSERT(IsEditorDataAvailable());
3859
0
3860
0
  // find where we are
3861
0
  EditorDOMPoint atStartOfSelection(EditorBase::GetStartPoint(&SelectionRef()));
3862
0
  if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
3863
0
    return NS_ERROR_FAILURE;
3864
0
  }
3865
0
3866
0
  // find any enclosing mailcite
3867
0
  RefPtr<Element> citeNode =
3868
0
    GetTopEnclosingMailCite(*atStartOfSelection.GetContainer());
3869
0
  if (citeNode) {
3870
0
    bool isEmpty = true, seenBR = false;
3871
0
    HTMLEditorRef().IsEmptyNodeImpl(citeNode, &isEmpty, true, true, false,
3872
0
                                    &seenBR);
3873
0
    if (isEmpty) {
3874
0
      EditorDOMPoint atCiteNode(citeNode);
3875
0
      {
3876
0
        AutoEditorDOMPointChildInvalidator lockOffset(atCiteNode);
3877
0
        nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(*citeNode);
3878
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
3879
0
          return NS_ERROR_EDITOR_DESTROYED;
3880
0
        }
3881
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
3882
0
          return rv;
3883
0
        }
3884
0
      }
3885
0
      if (atCiteNode.IsSet() && seenBR) {
3886
0
        RefPtr<Element> brElement =
3887
0
          HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
3888
0
                                                         atCiteNode);
3889
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
3890
0
          return NS_ERROR_EDITOR_DESTROYED;
3891
0
        }
3892
0
        if (NS_WARN_IF(!brElement)) {
3893
0
          return NS_ERROR_FAILURE;
3894
0
        }
3895
0
        IgnoredErrorResult error;
3896
0
        SelectionRef().Collapse(EditorRawDOMPoint(brElement), error);
3897
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
3898
0
          return NS_ERROR_EDITOR_DESTROYED;
3899
0
        }
3900
0
        NS_WARNING_ASSERTION(!error.Failed(),
3901
0
          "Failed to collapse selection at the new <br> element");
3902
0
      }
3903
0
    }
3904
0
  }
3905
0
3906
0
  // call through to base class
3907
0
  nsresult rv = TextEditRules::DidDeleteSelection();
3908
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3909
0
    return rv;
3910
0
  }
3911
0
  return NS_OK;
3912
0
}
3913
3914
nsresult
3915
HTMLEditRules::WillMakeList(const nsAString* aListType,
3916
                            bool aEntireList,
3917
                            const nsAString* aBulletType,
3918
                            bool* aCancel,
3919
                            bool* aHandled,
3920
                            const nsAString* aItemType)
3921
0
{
3922
0
  MOZ_ASSERT(IsEditorDataAvailable());
3923
0
3924
0
  if (NS_WARN_IF(!aListType) || NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
3925
0
    return NS_ERROR_INVALID_ARG;
3926
0
  }
3927
0
3928
0
  *aCancel = false;
3929
0
  *aHandled = false;
3930
0
3931
0
  OwningNonNull<nsAtom> listType = NS_Atomize(*aListType);
3932
0
3933
0
  // FYI: Ignore cancel result of WillInsert().
3934
0
  nsresult rv = WillInsert();
3935
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
3936
0
    return NS_ERROR_EDITOR_DESTROYED;
3937
0
  }
3938
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
3939
0
3940
0
  // deduce what tag to use for list items
3941
0
  RefPtr<nsAtom> itemType;
3942
0
  if (aItemType) {
3943
0
    itemType = NS_Atomize(*aItemType);
3944
0
  } else if (listType == nsGkAtoms::dl) {
3945
0
    itemType = nsGkAtoms::dd;
3946
0
  } else {
3947
0
    itemType = nsGkAtoms::li;
3948
0
  }
3949
0
3950
0
  // convert the selection ranges into "promoted" selection ranges:
3951
0
  // this basically just expands the range to include the immediate
3952
0
  // block parent, and then further expands to include any ancestors
3953
0
  // whose children are all in the range
3954
0
3955
0
  *aHandled = true;
3956
0
3957
0
  rv = NormalizeSelection();
3958
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3959
0
    return rv;
3960
0
  }
3961
0
3962
0
  // MakeList() creates AutoSelectionRestorer.
3963
0
  // Therefore, even if it returns NS_OK, editor might have been destroyed
3964
0
  // at restoring Selection.
3965
0
  rv = MakeList(listType, aEntireList, aBulletType, aCancel, *itemType);
3966
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
3967
0
      NS_WARN_IF(!CanHandleEditAction())) {
3968
0
    return NS_ERROR_EDITOR_DESTROYED;
3969
0
  }
3970
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3971
0
    return rv;
3972
0
  }
3973
0
  return NS_OK;
3974
0
}
3975
3976
nsresult
3977
HTMLEditRules::MakeList(nsAtom& aListType,
3978
                        bool aEntireList,
3979
                        const nsAString* aBulletType,
3980
                        bool* aCancel,
3981
                        nsAtom& aItemType)
3982
0
{
3983
0
  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
3984
0
3985
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
3986
0
  nsresult rv =
3987
0
    GetListActionNodes(arrayOfNodes,
3988
0
                       aEntireList ? EntireList::yes : EntireList::no,
3989
0
                       TouchContent::yes);
3990
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3991
0
    return rv;
3992
0
  }
3993
0
3994
0
  // check if all our nodes are <br>s, or empty inlines
3995
0
  bool bOnlyBreaks = true;
3996
0
  for (auto& curNode : arrayOfNodes) {
3997
0
    // if curNode is not a Break or empty inline, we're done
3998
0
    if (!TextEditUtils::IsBreak(curNode) &&
3999
0
        !IsEmptyInline(curNode)) {
4000
0
      bOnlyBreaks = false;
4001
0
      break;
4002
0
    }
4003
0
  }
4004
0
4005
0
  // if no nodes, we make empty list.  Ditto if the user tried to make a list
4006
0
  // of some # of breaks.
4007
0
  if (arrayOfNodes.IsEmpty() || bOnlyBreaks) {
4008
0
    // if only breaks, delete them
4009
0
    if (bOnlyBreaks) {
4010
0
      for (auto& node : arrayOfNodes) {
4011
0
        rv = HTMLEditorRef().DeleteNodeWithTransaction(*node);
4012
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4013
0
          return NS_ERROR_EDITOR_DESTROYED;
4014
0
        }
4015
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4016
0
          return rv;
4017
0
        }
4018
0
      }
4019
0
    }
4020
0
4021
0
    nsRange* firstRange = SelectionRef().GetRangeAt(0);
4022
0
    if (NS_WARN_IF(!firstRange)) {
4023
0
      return NS_ERROR_FAILURE;
4024
0
    }
4025
0
4026
0
    EditorDOMPoint atStartOfSelection(firstRange->StartRef());
4027
0
    if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
4028
0
      return NS_ERROR_FAILURE;
4029
0
    }
4030
0
4031
0
    // Make sure we can put a list here.
4032
0
    if (!HTMLEditorRef().CanContainTag(*atStartOfSelection.GetContainer(),
4033
0
                                       aListType)) {
4034
0
      *aCancel = true;
4035
0
      return NS_OK;
4036
0
    }
4037
0
4038
0
    SplitNodeResult splitAtSelectionStartResult =
4039
0
      MaybeSplitAncestorsForInsertWithTransaction(aListType,
4040
0
                                                  atStartOfSelection);
4041
0
    if (NS_WARN_IF(splitAtSelectionStartResult.Failed())) {
4042
0
      return splitAtSelectionStartResult.Rv();
4043
0
    }
4044
0
    RefPtr<Element> theList =
4045
0
      HTMLEditorRef().CreateNodeWithTransaction(
4046
0
                        aListType, splitAtSelectionStartResult.SplitPoint());
4047
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
4048
0
      return NS_ERROR_EDITOR_DESTROYED;
4049
0
    }
4050
0
    if (NS_WARN_IF(!theList)) {
4051
0
      return NS_ERROR_FAILURE;
4052
0
    }
4053
0
4054
0
    EditorRawDOMPoint atFirstListItemToInsertBefore(theList, 0);
4055
0
    RefPtr<Element> theListItem =
4056
0
      HTMLEditorRef().CreateNodeWithTransaction(aItemType,
4057
0
                                                atFirstListItemToInsertBefore);
4058
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
4059
0
      return NS_ERROR_EDITOR_DESTROYED;
4060
0
    }
4061
0
    if (NS_WARN_IF(!theListItem)) {
4062
0
      return NS_ERROR_FAILURE;
4063
0
    }
4064
0
4065
0
    // remember our new block for postprocessing
4066
0
    mNewBlock = theListItem;
4067
0
    // Put selection in new list item and don't restore the Selection.
4068
0
    selectionRestorer.Abort();
4069
0
    ErrorResult error;
4070
0
    SelectionRef().Collapse(EditorRawDOMPoint(theListItem, 0), error);
4071
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
4072
0
      error.SuppressException();
4073
0
      return NS_ERROR_EDITOR_DESTROYED;
4074
0
    }
4075
0
    if (NS_WARN_IF(!error.Failed())) {
4076
0
      return error.StealNSResult();
4077
0
    }
4078
0
    return NS_OK;
4079
0
  }
4080
0
4081
0
  // if there is only one node in the array, and it is a list, div, or
4082
0
  // blockquote, then look inside of it until we find inner list or content.
4083
0
4084
0
  LookInsideDivBQandList(arrayOfNodes);
4085
0
4086
0
  // Ok, now go through all the nodes and put then in the list,
4087
0
  // or whatever is approriate.  Wohoo!
4088
0
4089
0
  uint32_t listCount = arrayOfNodes.Length();
4090
0
  RefPtr<Element> curList, prevListItem;
4091
0
4092
0
  for (uint32_t i = 0; i < listCount; i++) {
4093
0
    // here's where we actually figure out what to do
4094
0
    RefPtr<Element> newBlock;
4095
0
    if (NS_WARN_IF(!arrayOfNodes[i]->IsContent())) {
4096
0
      return NS_ERROR_FAILURE;
4097
0
    }
4098
0
    OwningNonNull<nsIContent> curNode = *arrayOfNodes[i]->AsContent();
4099
0
4100
0
    // make sure we don't assemble content that is in different table cells
4101
0
    // into the same list.  respect table cell boundaries when listifying.
4102
0
    if (curList && InDifferentTableElements(curList, curNode)) {
4103
0
      curList = nullptr;
4104
0
    }
4105
0
4106
0
    // If curNode is a break, delete it, and quit remembering prev list item.
4107
0
    // If an empty inline container, delete it, but still remember the previous
4108
0
    // item.
4109
0
    if (HTMLEditorRef().IsEditable(curNode) &&
4110
0
        (TextEditUtils::IsBreak(curNode) || IsEmptyInline(curNode))) {
4111
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*curNode);
4112
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4113
0
        return NS_ERROR_EDITOR_DESTROYED;
4114
0
      }
4115
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
4116
0
        return rv;
4117
0
      }
4118
0
      if (TextEditUtils::IsBreak(curNode)) {
4119
0
        prevListItem = nullptr;
4120
0
      }
4121
0
      continue;
4122
0
    }
4123
0
4124
0
    if (HTMLEditUtils::IsList(curNode)) {
4125
0
      // do we have a curList already?
4126
0
      if (curList && !EditorUtils::IsDescendantOf(*curNode, *curList)) {
4127
0
        // move all of our children into curList.  cheezy way to do it: move
4128
0
        // whole list and then RemoveContainerWithTransaction() on the list.
4129
0
        // ConvertListType first: that routine handles converting the list
4130
0
        // item types, if needed.
4131
0
        rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode, *curList);
4132
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4133
0
          return NS_ERROR_EDITOR_DESTROYED;
4134
0
        }
4135
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4136
0
          return rv;
4137
0
        }
4138
0
        CreateElementResult convertListTypeResult =
4139
0
          ConvertListType(*curNode->AsElement(), aListType, aItemType);
4140
0
        if (NS_WARN_IF(convertListTypeResult.Failed())) {
4141
0
          return convertListTypeResult.Rv();
4142
0
        }
4143
0
        rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
4144
0
                               *convertListTypeResult.GetNewNode());
4145
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4146
0
          return NS_ERROR_EDITOR_DESTROYED;
4147
0
        }
4148
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4149
0
          return rv;
4150
0
        }
4151
0
        newBlock = convertListTypeResult.forget();
4152
0
      } else {
4153
0
        // replace list with new list type
4154
0
        CreateElementResult convertListTypeResult =
4155
0
          ConvertListType(*curNode->AsElement(), aListType, aItemType);
4156
0
        if (NS_WARN_IF(convertListTypeResult.Failed())) {
4157
0
          return convertListTypeResult.Rv();
4158
0
        }
4159
0
        curList = convertListTypeResult.forget();
4160
0
      }
4161
0
      prevListItem = nullptr;
4162
0
      continue;
4163
0
    }
4164
0
4165
0
    EditorRawDOMPoint atCurNode(curNode);
4166
0
    if (NS_WARN_IF(!atCurNode.IsSet())) {
4167
0
      return NS_ERROR_FAILURE;
4168
0
    }
4169
0
    MOZ_ASSERT(atCurNode.IsSetAndValid());
4170
0
    if (HTMLEditUtils::IsListItem(curNode)) {
4171
0
      if (!atCurNode.IsContainerHTMLElement(&aListType)) {
4172
0
        // list item is in wrong type of list. if we don't have a curList,
4173
0
        // split the old list and make a new list of correct type.
4174
0
        if (!curList || EditorUtils::IsDescendantOf(*curNode, *curList)) {
4175
0
          if (NS_WARN_IF(!atCurNode.GetContainerAsContent())) {
4176
0
            return NS_ERROR_FAILURE;
4177
0
          }
4178
0
          ErrorResult error;
4179
0
          nsCOMPtr<nsIContent> newLeftNode =
4180
0
            HTMLEditorRef().SplitNodeWithTransaction(atCurNode, error);
4181
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
4182
0
            error.SuppressException();
4183
0
            return NS_ERROR_EDITOR_DESTROYED;
4184
0
          }
4185
0
          if (NS_WARN_IF(error.Failed())) {
4186
0
            return error.StealNSResult();
4187
0
          }
4188
0
          newBlock = newLeftNode ? newLeftNode->AsElement() : nullptr;
4189
0
          EditorRawDOMPoint atParentOfCurNode(atCurNode.GetContainer());
4190
0
          curList =
4191
0
            HTMLEditorRef().CreateNodeWithTransaction(aListType,
4192
0
                                                      atParentOfCurNode);
4193
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
4194
0
            return NS_ERROR_EDITOR_DESTROYED;
4195
0
          }
4196
0
          if (NS_WARN_IF(!curList)) {
4197
0
            return NS_ERROR_FAILURE;
4198
0
          }
4199
0
        }
4200
0
        // move list item to new list
4201
0
        rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode, *curList);
4202
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4203
0
          return NS_ERROR_EDITOR_DESTROYED;
4204
0
        }
4205
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4206
0
          return rv;
4207
0
        }
4208
0
        // convert list item type if needed
4209
0
        if (!curNode->IsHTMLElement(&aItemType)) {
4210
0
          newBlock =
4211
0
            HTMLEditorRef().ReplaceContainerWithTransaction(
4212
0
                              *curNode->AsElement(), aItemType);
4213
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
4214
0
            return NS_ERROR_EDITOR_DESTROYED;
4215
0
          }
4216
0
          if (NS_WARN_IF(!newBlock)) {
4217
0
            return NS_ERROR_FAILURE;
4218
0
          }
4219
0
        }
4220
0
      } else {
4221
0
        // item is in right type of list.  But we might still have to move it.
4222
0
        // and we might need to convert list item types.
4223
0
        if (!curList) {
4224
0
          curList = atCurNode.GetContainerAsElement();
4225
0
        } else if (atCurNode.GetContainer() != curList) {
4226
0
          // move list item to new list
4227
0
          rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode, *curList);
4228
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
4229
0
            return NS_ERROR_EDITOR_DESTROYED;
4230
0
          }
4231
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
4232
0
            return rv;
4233
0
          }
4234
0
        }
4235
0
        if (!curNode->IsHTMLElement(&aItemType)) {
4236
0
          newBlock =
4237
0
            HTMLEditorRef().ReplaceContainerWithTransaction(
4238
0
                              *curNode->AsElement(), aItemType);
4239
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
4240
0
            return NS_ERROR_EDITOR_DESTROYED;
4241
0
          }
4242
0
          if (NS_WARN_IF(!newBlock)) {
4243
0
            return NS_ERROR_FAILURE;
4244
0
          }
4245
0
        }
4246
0
      }
4247
0
      nsCOMPtr<Element> curElement = do_QueryInterface(curNode);
4248
0
      if (NS_WARN_IF(!curElement)) {
4249
0
        return NS_ERROR_FAILURE;
4250
0
      }
4251
0
      if (aBulletType && !aBulletType->IsEmpty()) {
4252
0
        rv = HTMLEditorRef().SetAttributeWithTransaction(*curElement,
4253
0
                                                         *nsGkAtoms::type,
4254
0
                                                         *aBulletType);
4255
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4256
0
          return NS_ERROR_EDITOR_DESTROYED;
4257
0
        }
4258
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4259
0
          return rv;
4260
0
        }
4261
0
      } else {
4262
0
        rv = HTMLEditorRef().RemoveAttributeWithTransaction(*curElement,
4263
0
                                                            *nsGkAtoms::type);
4264
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4265
0
          return NS_ERROR_EDITOR_DESTROYED;
4266
0
        }
4267
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4268
0
          return rv;
4269
0
        }
4270
0
      }
4271
0
      continue;
4272
0
    }
4273
0
4274
0
    // if we hit a div clear our prevListItem, insert divs contents
4275
0
    // into our node array, and remove the div
4276
0
    if (curNode->IsHTMLElement(nsGkAtoms::div)) {
4277
0
      prevListItem = nullptr;
4278
0
      int32_t j = i + 1;
4279
0
      GetInnerContent(*curNode, arrayOfNodes, &j);
4280
0
      rv = HTMLEditorRef().RemoveContainerWithTransaction(
4281
0
                             *curNode->AsElement());
4282
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4283
0
        return NS_ERROR_EDITOR_DESTROYED;
4284
0
      }
4285
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
4286
0
        return rv;
4287
0
      }
4288
0
      listCount = arrayOfNodes.Length();
4289
0
      continue;
4290
0
    }
4291
0
4292
0
    // need to make a list to put things in if we haven't already,
4293
0
    if (!curList) {
4294
0
      SplitNodeResult splitCurNodeResult =
4295
0
        MaybeSplitAncestorsForInsertWithTransaction(aListType, atCurNode);
4296
0
      if (NS_WARN_IF(splitCurNodeResult.Failed())) {
4297
0
        return splitCurNodeResult.Rv();
4298
0
      }
4299
0
      curList =
4300
0
        HTMLEditorRef().CreateNodeWithTransaction(
4301
0
                          aListType, splitCurNodeResult.SplitPoint());
4302
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4303
0
        return NS_ERROR_EDITOR_DESTROYED;
4304
0
      }
4305
0
      if (NS_WARN_IF(!curList)) {
4306
0
        return NS_ERROR_FAILURE;
4307
0
      }
4308
0
      // remember our new block for postprocessing
4309
0
      mNewBlock = curList;
4310
0
      // curList is now the correct thing to put curNode in
4311
0
      prevListItem = nullptr;
4312
0
4313
0
      // atCurNode is now referring the right node with mOffset but
4314
0
      // referring the left node with mRef.  So, invalidate it now.
4315
0
      atCurNode.Clear();
4316
0
    }
4317
0
4318
0
    // if curNode isn't a list item, we must wrap it in one
4319
0
    nsCOMPtr<Element> listItem;
4320
0
    if (!HTMLEditUtils::IsListItem(curNode)) {
4321
0
      if (IsInlineNode(curNode) && prevListItem) {
4322
0
        // this is a continuation of some inline nodes that belong together in
4323
0
        // the same list item.  use prevListItem
4324
0
        rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode,
4325
0
                                                          *prevListItem);
4326
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4327
0
          return NS_ERROR_EDITOR_DESTROYED;
4328
0
        }
4329
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4330
0
          return rv;
4331
0
        }
4332
0
      } else {
4333
0
        // don't wrap li around a paragraph.  instead replace paragraph with li
4334
0
        if (curNode->IsHTMLElement(nsGkAtoms::p)) {
4335
0
          listItem =
4336
0
            HTMLEditorRef().ReplaceContainerWithTransaction(
4337
0
                              *curNode->AsElement(), aItemType);
4338
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
4339
0
            return NS_ERROR_EDITOR_DESTROYED;
4340
0
          }
4341
0
          if (NS_WARN_IF(!listItem)) {
4342
0
            return NS_ERROR_FAILURE;
4343
0
          }
4344
0
        } else {
4345
0
          listItem =
4346
0
            HTMLEditorRef().InsertContainerWithTransaction(*curNode, aItemType);
4347
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
4348
0
            return NS_ERROR_EDITOR_DESTROYED;
4349
0
          }
4350
0
          if (NS_WARN_IF(!listItem)) {
4351
0
            return NS_ERROR_FAILURE;
4352
0
          }
4353
0
        }
4354
0
        if (IsInlineNode(curNode)) {
4355
0
          prevListItem = listItem;
4356
0
        } else {
4357
0
          prevListItem = nullptr;
4358
0
        }
4359
0
      }
4360
0
    } else {
4361
0
      listItem = curNode->AsElement();
4362
0
    }
4363
0
4364
0
    if (listItem) {
4365
0
      // if we made a new list item, deal with it: tuck the listItem into the
4366
0
      // end of the active list
4367
0
      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*listItem, *curList);
4368
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4369
0
        return NS_ERROR_EDITOR_DESTROYED;
4370
0
      }
4371
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
4372
0
        return rv;
4373
0
      }
4374
0
    }
4375
0
  }
4376
0
4377
0
  return NS_OK;
4378
0
}
4379
4380
nsresult
4381
HTMLEditRules::WillRemoveList(bool* aCancel,
4382
                              bool* aHandled)
4383
0
{
4384
0
  MOZ_ASSERT(IsEditorDataAvailable());
4385
0
4386
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
4387
0
    return NS_ERROR_INVALID_ARG;
4388
0
  }
4389
0
  // initialize out param
4390
0
  *aCancel = false;
4391
0
  *aHandled = true;
4392
0
4393
0
  nsresult rv = NormalizeSelection();
4394
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4395
0
    return rv;
4396
0
  }
4397
0
4398
0
  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
4399
0
4400
0
  nsTArray<RefPtr<nsRange>> arrayOfRanges;
4401
0
  GetPromotedRanges(arrayOfRanges, EditSubAction::eCreateOrChangeList);
4402
0
4403
0
  // use these ranges to contruct a list of nodes to act on.
4404
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
4405
0
  rv = GetListActionNodes(arrayOfNodes, EntireList::no, TouchContent::yes);
4406
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4407
0
    return rv;
4408
0
  }
4409
0
4410
0
  // Remove all non-editable nodes.  Leave them be.
4411
0
  for (int32_t i = arrayOfNodes.Length() - 1; i >= 0; i--) {
4412
0
    OwningNonNull<nsINode> testNode = arrayOfNodes[i];
4413
0
    if (!HTMLEditorRef().IsEditable(testNode)) {
4414
0
      arrayOfNodes.RemoveElementAt(i);
4415
0
    }
4416
0
  }
4417
0
4418
0
  // Only act on lists or list items in the array
4419
0
  for (auto& curNode : arrayOfNodes) {
4420
0
    // here's where we actually figure out what to do
4421
0
    if (HTMLEditUtils::IsListItem(curNode)) {
4422
0
      // unlist this listitem
4423
0
      bool bOutOfList;
4424
0
      do {
4425
0
        rv = PopListItem(*curNode->AsContent(), &bOutOfList);
4426
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4427
0
          return rv;
4428
0
        }
4429
0
      } while (!bOutOfList); // keep popping it out until it's not in a list anymore
4430
0
    } else if (HTMLEditUtils::IsList(curNode)) {
4431
0
      // node is a list, move list items out
4432
0
      rv = RemoveListStructure(*curNode->AsElement());
4433
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
4434
0
        return rv;
4435
0
      }
4436
0
    }
4437
0
  }
4438
0
  return NS_OK;
4439
0
}
4440
4441
nsresult
4442
HTMLEditRules::WillMakeDefListItem(const nsAString *aItemType,
4443
                                   bool aEntireList,
4444
                                   bool* aCancel,
4445
                                   bool* aHandled)
4446
0
{
4447
0
  MOZ_ASSERT(IsEditorDataAvailable());
4448
0
4449
0
  // for now we let WillMakeList handle this
4450
0
  NS_NAMED_LITERAL_STRING(listType, "dl");
4451
0
  nsresult rv = WillMakeList(&listType.AsString(), aEntireList,
4452
0
                             nullptr, aCancel, aHandled, aItemType);
4453
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4454
0
    return rv;
4455
0
  }
4456
0
  return NS_OK;
4457
0
}
4458
4459
nsresult
4460
HTMLEditRules::WillMakeBasicBlock(const nsAString& aBlockType,
4461
                                  bool* aCancel,
4462
                                  bool* aHandled)
4463
0
{
4464
0
  MOZ_ASSERT(IsEditorDataAvailable());
4465
0
  MOZ_ASSERT(aCancel && aHandled);
4466
0
4467
0
  OwningNonNull<nsAtom> blockType = NS_Atomize(aBlockType);
4468
0
4469
0
  // FYI: Ignore cancel result of WillInsert().
4470
0
  nsresult rv = WillInsert();
4471
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
4472
0
    return NS_ERROR_EDITOR_DESTROYED;
4473
0
  }
4474
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
4475
0
4476
0
  *aCancel = false;
4477
0
  *aHandled = true;
4478
0
4479
0
  // MakeBasicBlock() creates AutoSelectionRestorer.
4480
0
  // Therefore, even if it returns NS_OK, editor might have been destroyed
4481
0
  // at restoring Selection.
4482
0
  rv = MakeBasicBlock(blockType);
4483
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
4484
0
    return NS_ERROR_EDITOR_DESTROYED;
4485
0
  }
4486
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4487
0
    return rv;
4488
0
  }
4489
0
  return NS_OK;
4490
0
}
4491
4492
nsresult
4493
HTMLEditRules::MakeBasicBlock(nsAtom& blockType)
4494
0
{
4495
0
  MOZ_ASSERT(IsEditorDataAvailable());
4496
0
4497
0
  nsresult rv = NormalizeSelection();
4498
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4499
0
    return rv;
4500
0
  }
4501
0
4502
0
  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
4503
0
  AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
4504
0
4505
0
  // Contruct a list of nodes to act on.
4506
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
4507
0
  rv = GetNodesFromSelection(EditSubAction::eCreateOrRemoveBlock, arrayOfNodes,
4508
0
                             TouchContent::yes);
4509
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4510
0
    return rv;
4511
0
  }
4512
0
4513
0
  // If nothing visible in list, make an empty block
4514
0
  if (ListIsEmptyLine(arrayOfNodes)) {
4515
0
    nsRange* firstRange = SelectionRef().GetRangeAt(0);
4516
0
    if (NS_WARN_IF(!firstRange)) {
4517
0
      return NS_ERROR_FAILURE;
4518
0
    }
4519
0
4520
0
    EditorDOMPoint pointToInsertBlock(firstRange->StartRef());
4521
0
    if (&blockType == nsGkAtoms::normal ||
4522
0
        &blockType == nsGkAtoms::_empty) {
4523
0
      // We are removing blocks (going to "body text")
4524
0
      RefPtr<Element> curBlock =
4525
0
        HTMLEditorRef().GetBlock(*pointToInsertBlock.GetContainer());
4526
0
      if (NS_WARN_IF(!curBlock)) {
4527
0
        return NS_ERROR_FAILURE;
4528
0
      }
4529
0
      if (!HTMLEditUtils::IsFormatNode(curBlock)) {
4530
0
        return NS_OK;
4531
0
      }
4532
0
4533
0
      // If the first editable node after selection is a br, consume it.
4534
0
      // Otherwise it gets pushed into a following block after the split,
4535
0
      // which is visually bad.
4536
0
      nsCOMPtr<nsIContent> brContent =
4537
0
        HTMLEditorRef().GetNextEditableHTMLNode(pointToInsertBlock);
4538
0
      if (brContent && brContent->IsHTMLElement(nsGkAtoms::br)) {
4539
0
        AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
4540
0
        rv = HTMLEditorRef().DeleteNodeWithTransaction(*brContent);
4541
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4542
0
          return NS_ERROR_EDITOR_DESTROYED;
4543
0
        }
4544
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4545
0
          return rv;
4546
0
        }
4547
0
      }
4548
0
      // Do the splits!
4549
0
      SplitNodeResult splitNodeResult =
4550
0
        HTMLEditorRef().SplitNodeDeepWithTransaction(
4551
0
                          *curBlock, pointToInsertBlock,
4552
0
                          SplitAtEdges::eDoNotCreateEmptyContainer);
4553
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4554
0
        return NS_ERROR_EDITOR_DESTROYED;
4555
0
      }
4556
0
      if (NS_WARN_IF(splitNodeResult.Failed())) {
4557
0
        return splitNodeResult.Rv();
4558
0
      }
4559
0
      EditorRawDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
4560
0
      // Put a <br> element at the split point
4561
0
      brContent =
4562
0
        HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
4563
0
                                                       pointToInsertBrNode);
4564
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4565
0
        return NS_ERROR_EDITOR_DESTROYED;
4566
0
      }
4567
0
      if (NS_WARN_IF(!brContent)) {
4568
0
        return NS_ERROR_FAILURE;
4569
0
      }
4570
0
      // Put selection at the split point
4571
0
      EditorRawDOMPoint atBrNode(brContent);
4572
0
      // Don't restore the selection
4573
0
      selectionRestorer.Abort();
4574
0
      ErrorResult error;
4575
0
      SelectionRef().Collapse(atBrNode, error);
4576
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4577
0
        error.SuppressException();
4578
0
        return NS_ERROR_EDITOR_DESTROYED;
4579
0
      }
4580
0
      if (NS_WARN_IF(error.Failed())) {
4581
0
        return error.StealNSResult();
4582
0
      }
4583
0
      return NS_OK;
4584
0
    }
4585
0
4586
0
    // We are making a block.  Consume a br, if needed.
4587
0
    nsCOMPtr<nsIContent> brNode =
4588
0
      HTMLEditorRef().GetNextEditableHTMLNodeInBlock(pointToInsertBlock);
4589
0
    if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
4590
0
      AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
4591
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*brNode);
4592
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4593
0
        return NS_ERROR_EDITOR_DESTROYED;
4594
0
      }
4595
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
4596
0
        return rv;
4597
0
      }
4598
0
      // We don't need to act on this node any more
4599
0
      arrayOfNodes.RemoveElement(brNode);
4600
0
    }
4601
0
    // Make sure we can put a block here.
4602
0
    SplitNodeResult splitNodeResult =
4603
0
      MaybeSplitAncestorsForInsertWithTransaction(blockType,
4604
0
                                                  pointToInsertBlock);
4605
0
    if (NS_WARN_IF(splitNodeResult.Failed())) {
4606
0
      return splitNodeResult.Rv();
4607
0
    }
4608
0
    RefPtr<Element> block =
4609
0
      HTMLEditorRef().CreateNodeWithTransaction(blockType,
4610
0
                                                splitNodeResult.SplitPoint());
4611
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
4612
0
      return NS_ERROR_EDITOR_DESTROYED;
4613
0
    }
4614
0
    if (NS_WARN_IF(!block)) {
4615
0
      return NS_ERROR_FAILURE;
4616
0
    }
4617
0
    // Remember our new block for postprocessing
4618
0
    mNewBlock = block;
4619
0
    // Delete anything that was in the list of nodes
4620
0
    while (!arrayOfNodes.IsEmpty()) {
4621
0
      OwningNonNull<nsINode> curNode = arrayOfNodes[0];
4622
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*curNode);
4623
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4624
0
        return NS_ERROR_EDITOR_DESTROYED;
4625
0
      }
4626
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
4627
0
        return rv;
4628
0
      }
4629
0
      arrayOfNodes.RemoveElementAt(0);
4630
0
    }
4631
0
    // Don't restore the selection
4632
0
    selectionRestorer.Abort();
4633
0
    // Put selection in new block
4634
0
    rv = SelectionRef().Collapse(block, 0);
4635
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
4636
0
      return NS_ERROR_EDITOR_DESTROYED;
4637
0
    }
4638
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
4639
0
      return rv;
4640
0
    }
4641
0
    return NS_OK;
4642
0
  }
4643
0
  // Okay, now go through all the nodes and make the right kind of blocks, or
4644
0
  // whatever is approriate.  Woohoo!  Note: blockquote is handled a little
4645
0
  // differently.
4646
0
  if (&blockType == nsGkAtoms::blockquote) {
4647
0
    rv = MakeBlockquote(arrayOfNodes);
4648
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
4649
0
      return rv;
4650
0
    }
4651
0
  } else if (&blockType == nsGkAtoms::normal ||
4652
0
             &blockType == nsGkAtoms::_empty) {
4653
0
    rv = RemoveBlockStyle(arrayOfNodes);
4654
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
4655
0
      return rv;
4656
0
    }
4657
0
  } else {
4658
0
    rv = ApplyBlockStyle(arrayOfNodes, blockType);
4659
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
4660
0
      return rv;
4661
0
    }
4662
0
  }
4663
0
  return NS_OK;
4664
0
}
4665
4666
nsresult
4667
HTMLEditRules::DidMakeBasicBlock()
4668
0
{
4669
0
  MOZ_ASSERT(IsEditorDataAvailable());
4670
0
4671
0
  // check for empty block.  if so, put a moz br in it.
4672
0
  if (!SelectionRef().IsCollapsed()) {
4673
0
    return NS_OK;
4674
0
  }
4675
0
4676
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
4677
0
  if (NS_WARN_IF(!firstRange)) {
4678
0
    return NS_ERROR_FAILURE;
4679
0
  }
4680
0
  const RangeBoundary& atStartOfSelection = firstRange->StartRef();
4681
0
  if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
4682
0
    return NS_ERROR_FAILURE;
4683
0
  }
4684
0
  nsresult rv = InsertMozBRIfNeeded(*atStartOfSelection.Container());
4685
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4686
0
    return rv;
4687
0
  }
4688
0
  return NS_OK;
4689
0
}
4690
4691
nsresult
4692
HTMLEditRules::WillIndent(bool* aCancel,
4693
                          bool* aHandled)
4694
0
{
4695
0
  MOZ_ASSERT(IsEditorDataAvailable());
4696
0
4697
0
  if (HTMLEditorRef().IsCSSEnabled()) {
4698
0
    nsresult rv = WillCSSIndent(aCancel, aHandled);
4699
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
4700
0
      return rv;
4701
0
    }
4702
0
  } else {
4703
0
    nsresult rv = WillHTMLIndent(aCancel, aHandled);
4704
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
4705
0
      return rv;
4706
0
    }
4707
0
  }
4708
0
  return NS_OK;
4709
0
}
4710
4711
nsresult
4712
HTMLEditRules::WillCSSIndent(bool* aCancel,
4713
                             bool* aHandled)
4714
0
{
4715
0
  MOZ_ASSERT(IsEditorDataAvailable());
4716
0
4717
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
4718
0
    return NS_ERROR_INVALID_ARG;
4719
0
  }
4720
0
4721
0
  // FYI: Ignore cancel result of WillInsert().
4722
0
  nsresult rv = WillInsert();
4723
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
4724
0
    return NS_ERROR_EDITOR_DESTROYED;
4725
0
  }
4726
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
4727
0
4728
0
  *aCancel = false;
4729
0
  *aHandled = true;
4730
0
4731
0
  rv = NormalizeSelection();
4732
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4733
0
    return rv;
4734
0
  }
4735
0
4736
0
  // IndentAroundSelectionWithCSS() creates AutoSelectionRestorer.
4737
0
  // Therefore, even if it returns NS_OK, editor might have been destroyed
4738
0
  // at restoring Selection.
4739
0
  rv = IndentAroundSelectionWithCSS();
4740
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
4741
0
    return NS_ERROR_EDITOR_DESTROYED;
4742
0
  }
4743
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
4744
0
    return rv;
4745
0
  }
4746
0
  return NS_OK;
4747
0
}
4748
4749
nsresult
4750
HTMLEditRules::IndentAroundSelectionWithCSS()
4751
0
{
4752
0
  MOZ_ASSERT(IsEditorDataAvailable());
4753
0
4754
0
  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
4755
0
  nsTArray<OwningNonNull<nsRange>> arrayOfRanges;
4756
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
4757
0
4758
0
  // short circuit: detect case of collapsed selection inside an <li>.
4759
0
  // just sublist that <li>.  This prevents bug 97797.
4760
0
4761
0
  nsCOMPtr<Element> liNode;
4762
0
  if (SelectionRef().IsCollapsed()) {
4763
0
    EditorRawDOMPoint selectionStartPoint(
4764
0
                        EditorBase::GetStartPoint(&SelectionRef()));
4765
0
    if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
4766
0
      return NS_ERROR_FAILURE;
4767
0
    }
4768
0
    Element* block =
4769
0
      HTMLEditorRef().GetBlock(*selectionStartPoint.GetContainer());
4770
0
    if (block && HTMLEditUtils::IsListItem(block)) {
4771
0
      liNode = block;
4772
0
    }
4773
0
  }
4774
0
4775
0
  if (liNode) {
4776
0
    arrayOfNodes.AppendElement(*liNode);
4777
0
  } else {
4778
0
    // convert the selection ranges into "promoted" selection ranges:
4779
0
    // this basically just expands the range to include the immediate
4780
0
    // block parent, and then further expands to include any ancestors
4781
0
    // whose children are all in the range
4782
0
    nsresult rv =
4783
0
      GetNodesFromSelection(EditSubAction::eIndent, arrayOfNodes,
4784
0
                            TouchContent::yes);
4785
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
4786
0
      return rv;
4787
0
    }
4788
0
  }
4789
0
4790
0
  // if nothing visible in list, make an empty block
4791
0
  if (ListIsEmptyLine(arrayOfNodes)) {
4792
0
    // get selection location
4793
0
    nsRange* firstRange = SelectionRef().GetRangeAt(0);
4794
0
    if (NS_WARN_IF(!firstRange)) {
4795
0
      return NS_ERROR_FAILURE;
4796
0
    }
4797
0
4798
0
    EditorDOMPoint atStartOfSelection(firstRange->StartRef());
4799
0
    if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
4800
0
      return NS_ERROR_FAILURE;
4801
0
    }
4802
0
4803
0
    // make sure we can put a block here
4804
0
    SplitNodeResult splitNodeResult =
4805
0
      MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
4806
0
                                                  atStartOfSelection);
4807
0
    if (NS_WARN_IF(splitNodeResult.Failed())) {
4808
0
      return splitNodeResult.Rv();
4809
0
    }
4810
0
    RefPtr<Element> theBlock =
4811
0
      HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
4812
0
                                                splitNodeResult.SplitPoint());
4813
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
4814
0
      return NS_ERROR_EDITOR_DESTROYED;
4815
0
    }
4816
0
    if (NS_WARN_IF(!theBlock)) {
4817
0
      return NS_ERROR_FAILURE;
4818
0
    }
4819
0
    // remember our new block for postprocessing
4820
0
    mNewBlock = theBlock;
4821
0
    nsresult rv = IncreaseMarginToIndent(*theBlock);
4822
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
4823
0
      return NS_ERROR_EDITOR_DESTROYED;
4824
0
    }
4825
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to increase indentation");
4826
0
    // delete anything that was in the list of nodes
4827
0
    while (!arrayOfNodes.IsEmpty()) {
4828
0
      OwningNonNull<nsINode> curNode = arrayOfNodes[0];
4829
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*curNode);
4830
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4831
0
        return NS_ERROR_EDITOR_DESTROYED;
4832
0
      }
4833
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
4834
0
        return rv;
4835
0
      }
4836
0
      arrayOfNodes.RemoveElementAt(0);
4837
0
    }
4838
0
    // put selection in new block
4839
0
    EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
4840
0
    // Don't restore the selection
4841
0
    selectionRestorer.Abort();
4842
0
    ErrorResult error;
4843
0
    SelectionRef().Collapse(atStartOfTheBlock, error);
4844
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
4845
0
      error.SuppressException();
4846
0
      return NS_ERROR_EDITOR_DESTROYED;
4847
0
    }
4848
0
    if (NS_WARN_IF(!error.Failed())) {
4849
0
      return error.StealNSResult();
4850
0
    }
4851
0
    return NS_OK;
4852
0
  }
4853
0
4854
0
  // Ok, now go through all the nodes and put them in a blockquote,
4855
0
  // or whatever is appropriate.  Wohoo!
4856
0
  nsCOMPtr<Element> curList, curQuote;
4857
0
  nsCOMPtr<nsIContent> sibling;
4858
0
  for (OwningNonNull<nsINode>& curNode : arrayOfNodes) {
4859
0
    // Here's where we actually figure out what to do.
4860
0
    EditorDOMPoint atCurNode(curNode);
4861
0
    if (NS_WARN_IF(!atCurNode.IsSet())) {
4862
0
      continue;
4863
0
    }
4864
0
4865
0
    // Ignore all non-editable nodes.  Leave them be.
4866
0
    if (!HTMLEditorRef().IsEditable(curNode)) {
4867
0
      continue;
4868
0
    }
4869
0
4870
0
    // some logic for putting list items into nested lists...
4871
0
    if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
4872
0
      // Check for whether we should join a list that follows curNode.
4873
0
      // We do this if the next element is a list, and the list is of the
4874
0
      // same type (li/ol) as curNode was a part it.
4875
0
      sibling = HTMLEditorRef().GetNextHTMLSibling(curNode);
4876
0
      if (sibling && HTMLEditUtils::IsList(sibling) &&
4877
0
          atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
4878
0
            sibling->NodeInfo()->NameAtom() &&
4879
0
          atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
4880
0
            sibling->NodeInfo()->NamespaceID()) {
4881
0
        nsresult rv =
4882
0
          HTMLEditorRef().MoveNodeWithTransaction(
4883
0
                            *curNode->AsContent(),
4884
0
                            EditorRawDOMPoint(sibling, 0));
4885
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4886
0
          return NS_ERROR_EDITOR_DESTROYED;
4887
0
        }
4888
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4889
0
          return rv;
4890
0
        }
4891
0
        continue;
4892
0
      }
4893
0
4894
0
      // Check for whether we should join a list that preceeds curNode.
4895
0
      // We do this if the previous element is a list, and the list is of
4896
0
      // the same type (li/ol) as curNode was a part of.
4897
0
      sibling = HTMLEditorRef().GetPriorHTMLSibling(curNode);
4898
0
      if (sibling && HTMLEditUtils::IsList(sibling) &&
4899
0
          atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
4900
0
            sibling->NodeInfo()->NameAtom() &&
4901
0
          atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
4902
0
            sibling->NodeInfo()->NamespaceID()) {
4903
0
        nsresult rv =
4904
0
          HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
4905
0
                                                       *sibling);
4906
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4907
0
          return NS_ERROR_EDITOR_DESTROYED;
4908
0
        }
4909
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
4910
0
          return rv;
4911
0
        }
4912
0
        continue;
4913
0
      }
4914
0
4915
0
      // check to see if curList is still appropriate.  Which it is if
4916
0
      // curNode is still right after it in the same list.
4917
0
      sibling = nullptr;
4918
0
      if (curList) {
4919
0
        sibling = HTMLEditorRef().GetPriorHTMLSibling(curNode);
4920
0
      }
4921
0
4922
0
      if (!curList || (sibling && sibling != curList)) {
4923
0
        nsAtom* containerName =
4924
0
          atCurNode.GetContainer()->NodeInfo()->NameAtom();
4925
0
        // Create a new nested list of correct type.
4926
0
        SplitNodeResult splitNodeResult =
4927
0
          MaybeSplitAncestorsForInsertWithTransaction(*containerName,
4928
0
                                                      atCurNode);
4929
0
        if (NS_WARN_IF(splitNodeResult.Failed())) {
4930
0
          return splitNodeResult.Rv();
4931
0
        }
4932
0
        curList =
4933
0
          HTMLEditorRef().CreateNodeWithTransaction(
4934
0
                            *containerName, splitNodeResult.SplitPoint());
4935
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
4936
0
          return NS_ERROR_EDITOR_DESTROYED;
4937
0
        }
4938
0
        if (NS_WARN_IF(!curList)) {
4939
0
          return NS_ERROR_FAILURE;
4940
0
        }
4941
0
        // curList is now the correct thing to put curNode in
4942
0
        // remember our new block for postprocessing
4943
0
        mNewBlock = curList;
4944
0
      }
4945
0
      // tuck the node into the end of the active list
4946
0
      nsresult rv =
4947
0
        HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
4948
0
                                                     *curList);
4949
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4950
0
        return NS_ERROR_EDITOR_DESTROYED;
4951
0
      }
4952
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
4953
0
        return rv;
4954
0
      }
4955
0
      continue;
4956
0
    }
4957
0
4958
0
    // Not a list item.
4959
0
4960
0
    if (IsBlockNode(*curNode)) {
4961
0
      nsresult rv = IncreaseMarginToIndent(*curNode->AsElement());
4962
0
      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
4963
0
        return NS_ERROR_EDITOR_DESTROYED;
4964
0
      }
4965
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to inrease indentation");
4966
0
      curQuote = nullptr;
4967
0
      continue;
4968
0
    }
4969
0
4970
0
    if (!curQuote) {
4971
0
      // First, check that our element can contain a div.
4972
0
      if (!HTMLEditorRef().CanContainTag(*atCurNode.GetContainer(),
4973
0
                                         *nsGkAtoms::div)) {
4974
0
        return NS_OK; // cancelled
4975
0
      }
4976
0
4977
0
      SplitNodeResult splitNodeResult =
4978
0
        MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div, atCurNode);
4979
0
      if (NS_WARN_IF(splitNodeResult.Failed())) {
4980
0
        return splitNodeResult.Rv();
4981
0
      }
4982
0
      curQuote =
4983
0
        HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
4984
0
                                                  splitNodeResult.SplitPoint());
4985
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
4986
0
        return NS_ERROR_EDITOR_DESTROYED;
4987
0
      }
4988
0
      if (NS_WARN_IF(!curQuote)) {
4989
0
        return NS_ERROR_FAILURE;
4990
0
      }
4991
0
      nsresult rv = IncreaseMarginToIndent(*curQuote);
4992
0
      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
4993
0
        return NS_ERROR_EDITOR_DESTROYED;
4994
0
      }
4995
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to increase indentation");
4996
0
      // remember our new block for postprocessing
4997
0
      mNewBlock = curQuote;
4998
0
      // curQuote is now the correct thing to put curNode in
4999
0
    }
5000
0
5001
0
    // tuck the node into the end of the active blockquote
5002
0
    nsresult rv =
5003
0
      HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
5004
0
                                                   *curQuote);
5005
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
5006
0
      return NS_ERROR_EDITOR_DESTROYED;
5007
0
    }
5008
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
5009
0
      return rv;
5010
0
    }
5011
0
  }
5012
0
  return NS_OK;
5013
0
}
5014
5015
nsresult
5016
HTMLEditRules::WillHTMLIndent(bool* aCancel,
5017
                              bool* aHandled)
5018
0
{
5019
0
  MOZ_ASSERT(IsEditorDataAvailable());
5020
0
5021
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
5022
0
    return NS_ERROR_INVALID_ARG;
5023
0
  }
5024
0
5025
0
  // FYI: Ignore cancel result of WillInsert().
5026
0
  nsresult rv = WillInsert();
5027
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
5028
0
    return NS_ERROR_EDITOR_DESTROYED;
5029
0
  }
5030
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
5031
0
5032
0
  *aCancel = false;
5033
0
  *aHandled = true;
5034
0
5035
0
  rv = NormalizeSelection();
5036
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
5037
0
    return rv;
5038
0
  }
5039
0
5040
0
  // IndentAroundSelectionWithHTML() creates AutoSelectionRestorer.
5041
0
  // Therefore, even if it returns NS_OK, editor might have been destroyed
5042
0
  // at restoring Selection.
5043
0
  rv = IndentAroundSelectionWithHTML();
5044
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
5045
0
    return NS_ERROR_EDITOR_DESTROYED;
5046
0
  }
5047
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
5048
0
    return rv;
5049
0
  }
5050
0
  return NS_OK;
5051
0
}
5052
5053
nsresult
5054
HTMLEditRules::IndentAroundSelectionWithHTML()
5055
0
{
5056
0
  MOZ_ASSERT(IsEditorDataAvailable());
5057
0
5058
0
  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
5059
0
5060
0
  // convert the selection ranges into "promoted" selection ranges:
5061
0
  // this basically just expands the range to include the immediate
5062
0
  // block parent, and then further expands to include any ancestors
5063
0
  // whose children are all in the range
5064
0
5065
0
  nsTArray<RefPtr<nsRange>> arrayOfRanges;
5066
0
  GetPromotedRanges(arrayOfRanges, EditSubAction::eIndent);
5067
0
5068
0
  // use these ranges to contruct a list of nodes to act on.
5069
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
5070
0
  nsresult rv =
5071
0
    GetNodesForOperation(arrayOfRanges, arrayOfNodes, EditSubAction::eIndent,
5072
0
                         TouchContent::yes);
5073
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
5074
0
    return rv;
5075
0
  }
5076
0
5077
0
  // if nothing visible in list, make an empty block
5078
0
  if (ListIsEmptyLine(arrayOfNodes)) {
5079
0
    nsRange* firstRange = SelectionRef().GetRangeAt(0);
5080
0
    if (NS_WARN_IF(!firstRange)) {
5081
0
      return NS_ERROR_FAILURE;
5082
0
    }
5083
0
5084
0
    EditorDOMPoint atStartOfSelection(firstRange->StartRef());
5085
0
    if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
5086
0
      return NS_ERROR_FAILURE;
5087
0
    }
5088
0
5089
0
    // Make sure we can put a block here.
5090
0
    SplitNodeResult splitNodeResult =
5091
0
      MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote,
5092
0
                                                  atStartOfSelection);
5093
0
    if (NS_WARN_IF(splitNodeResult.Failed())) {
5094
0
      return splitNodeResult.Rv();
5095
0
    }
5096
0
    RefPtr<Element> theBlock =
5097
0
      HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::blockquote,
5098
0
                                                splitNodeResult.SplitPoint());
5099
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
5100
0
      return NS_ERROR_EDITOR_DESTROYED;
5101
0
    }
5102
0
    if (NS_WARN_IF(!theBlock)) {
5103
0
      return NS_ERROR_FAILURE;
5104
0
    }
5105
0
    // remember our new block for postprocessing
5106
0
    mNewBlock = theBlock;
5107
0
    // delete anything that was in the list of nodes
5108
0
    while (!arrayOfNodes.IsEmpty()) {
5109
0
      OwningNonNull<nsINode> curNode = arrayOfNodes[0];
5110
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*curNode);
5111
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5112
0
        return NS_ERROR_EDITOR_DESTROYED;
5113
0
      }
5114
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
5115
0
        return rv;
5116
0
      }
5117
0
      arrayOfNodes.RemoveElementAt(0);
5118
0
    }
5119
0
    EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
5120
0
    // Don't restore the selection
5121
0
    selectionRestorer.Abort();
5122
0
    ErrorResult error;
5123
0
    SelectionRef().Collapse(atStartOfTheBlock, error);
5124
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
5125
0
      error.SuppressException();
5126
0
      return NS_ERROR_EDITOR_DESTROYED;
5127
0
    }
5128
0
    if (NS_WARN_IF(!error.Failed())) {
5129
0
      return error.StealNSResult();
5130
0
    }
5131
0
    return NS_OK;
5132
0
  }
5133
0
5134
0
  // Ok, now go through all the nodes and put them in a blockquote,
5135
0
  // or whatever is appropriate.  Wohoo!
5136
0
  nsCOMPtr<nsIContent> sibling;
5137
0
  nsCOMPtr<Element> curList, curQuote, indentedLI;
5138
0
  for (OwningNonNull<nsINode>& curNode: arrayOfNodes) {
5139
0
    // Here's where we actually figure out what to do.
5140
0
    EditorDOMPoint atCurNode(curNode);
5141
0
    if (NS_WARN_IF(!atCurNode.IsSet())) {
5142
0
      continue;
5143
0
    }
5144
0
5145
0
    // Ignore all non-editable nodes.  Leave them be.
5146
0
    if (!HTMLEditorRef().IsEditable(curNode)) {
5147
0
      continue;
5148
0
    }
5149
0
5150
0
    // some logic for putting list items into nested lists...
5151
0
    if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
5152
0
      // Check for whether we should join a list that follows curNode.
5153
0
      // We do this if the next element is a list, and the list is of the
5154
0
      // same type (li/ol) as curNode was a part it.
5155
0
      sibling = HTMLEditorRef().GetNextHTMLSibling(curNode);
5156
0
      if (sibling && HTMLEditUtils::IsList(sibling) &&
5157
0
          atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
5158
0
            sibling->NodeInfo()->NameAtom() &&
5159
0
          atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
5160
0
            sibling->NodeInfo()->NamespaceID()) {
5161
0
        rv = HTMLEditorRef().MoveNodeWithTransaction(
5162
0
                               *curNode->AsContent(),
5163
0
                               EditorRawDOMPoint(sibling, 0));
5164
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
5165
0
          return NS_ERROR_EDITOR_DESTROYED;
5166
0
        }
5167
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
5168
0
          return rv;
5169
0
        }
5170
0
        continue;
5171
0
      }
5172
0
5173
0
      // Check for whether we should join a list that preceeds curNode.
5174
0
      // We do this if the previous element is a list, and the list is of
5175
0
      // the same type (li/ol) as curNode was a part of.
5176
0
      sibling = HTMLEditorRef().GetPriorHTMLSibling(curNode);
5177
0
      if (sibling && HTMLEditUtils::IsList(sibling) &&
5178
0
          atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
5179
0
            sibling->NodeInfo()->NameAtom() &&
5180
0
          atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
5181
0
            sibling->NodeInfo()->NamespaceID()) {
5182
0
        rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
5183
0
                                                          *sibling);
5184
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
5185
0
          return NS_ERROR_EDITOR_DESTROYED;
5186
0
        }
5187
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
5188
0
          return rv;
5189
0
        }
5190
0
        continue;
5191
0
      }
5192
0
5193
0
      // check to see if curList is still appropriate.  Which it is if
5194
0
      // curNode is still right after it in the same list.
5195
0
      sibling = nullptr;
5196
0
      if (curList) {
5197
0
        sibling = HTMLEditorRef().GetPriorHTMLSibling(curNode);
5198
0
      }
5199
0
5200
0
      if (!curList || (sibling && sibling != curList)) {
5201
0
        nsAtom* containerName =
5202
0
          atCurNode.GetContainer()->NodeInfo()->NameAtom();
5203
0
        // Create a new nested list of correct type.
5204
0
        SplitNodeResult splitNodeResult =
5205
0
          MaybeSplitAncestorsForInsertWithTransaction(*containerName,
5206
0
                                                      atCurNode);
5207
0
        if (NS_WARN_IF(splitNodeResult.Failed())) {
5208
0
          return splitNodeResult.Rv();
5209
0
        }
5210
0
        curList =
5211
0
          HTMLEditorRef().CreateNodeWithTransaction(
5212
0
                            *containerName, splitNodeResult.SplitPoint());
5213
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
5214
0
          return NS_ERROR_EDITOR_DESTROYED;
5215
0
        }
5216
0
        if (NS_WARN_IF(!curList)) {
5217
0
          return NS_ERROR_FAILURE;
5218
0
        }
5219
0
        // curList is now the correct thing to put curNode in
5220
0
        // remember our new block for postprocessing
5221
0
        mNewBlock = curList;
5222
0
      }
5223
0
      // tuck the node into the end of the active list
5224
0
      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
5225
0
                                                        *curList);
5226
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5227
0
        return NS_ERROR_EDITOR_DESTROYED;
5228
0
      }
5229
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
5230
0
        return rv;
5231
0
      }
5232
0
      // forget curQuote, if any
5233
0
      curQuote = nullptr;
5234
0
5235
0
      continue;
5236
0
    }
5237
0
5238
0
    // Not a list item, use blockquote?
5239
0
5240
0
    // if we are inside a list item, we don't want to blockquote, we want
5241
0
    // to sublist the list item.  We may have several nodes listed in the
5242
0
    // array of nodes to act on, that are in the same list item.  Since
5243
0
    // we only want to indent that li once, we must keep track of the most
5244
0
    // recent indented list item, and not indent it if we find another node
5245
0
    // to act on that is still inside the same li.
5246
0
    RefPtr<Element> listItem = IsInListItem(curNode);
5247
0
    if (listItem) {
5248
0
      if (indentedLI == listItem) {
5249
0
        // already indented this list item
5250
0
        continue;
5251
0
      }
5252
0
      // check to see if curList is still appropriate.  Which it is if
5253
0
      // curNode is still right after it in the same list.
5254
0
      if (curList) {
5255
0
        sibling = HTMLEditorRef().GetPriorHTMLSibling(listItem);
5256
0
      }
5257
0
5258
0
      if (!curList || (sibling && sibling != curList)) {
5259
0
        EditorDOMPoint atListItem(listItem);
5260
0
        if (NS_WARN_IF(!listItem)) {
5261
0
          return NS_ERROR_FAILURE;
5262
0
        }
5263
0
        nsAtom* containerName =
5264
0
          atListItem.GetContainer()->NodeInfo()->NameAtom();
5265
0
        // Create a new nested list of correct type.
5266
0
        SplitNodeResult splitNodeResult =
5267
0
          MaybeSplitAncestorsForInsertWithTransaction(*containerName,
5268
0
                                                      atListItem);
5269
0
        if (NS_WARN_IF(splitNodeResult.Failed())) {
5270
0
          return splitNodeResult.Rv();
5271
0
        }
5272
0
        curList =
5273
0
          HTMLEditorRef().CreateNodeWithTransaction(
5274
0
                            *containerName, splitNodeResult.SplitPoint());
5275
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
5276
0
          return NS_ERROR_EDITOR_DESTROYED;
5277
0
        }
5278
0
        if (NS_WARN_IF(!curList)) {
5279
0
          return NS_ERROR_FAILURE;
5280
0
        }
5281
0
      }
5282
0
5283
0
      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*listItem, *curList);
5284
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5285
0
        return NS_ERROR_EDITOR_DESTROYED;
5286
0
      }
5287
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
5288
0
        return rv;
5289
0
      }
5290
0
5291
0
      // remember we indented this li
5292
0
      indentedLI = listItem;
5293
0
5294
0
      continue;
5295
0
    }
5296
0
5297
0
    // need to make a blockquote to put things in if we haven't already,
5298
0
    // or if this node doesn't go in blockquote we used earlier.
5299
0
    // One reason it might not go in prio blockquote is if we are now
5300
0
    // in a different table cell.
5301
0
    if (curQuote && InDifferentTableElements(curQuote, curNode)) {
5302
0
      curQuote = nullptr;
5303
0
    }
5304
0
5305
0
    if (!curQuote) {
5306
0
      // First, check that our element can contain a blockquote.
5307
0
      if (!HTMLEditorRef().CanContainTag(*atCurNode.GetContainer(),
5308
0
                                         *nsGkAtoms::blockquote)) {
5309
0
        return NS_OK; // cancelled
5310
0
      }
5311
0
5312
0
      SplitNodeResult splitNodeResult =
5313
0
        MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote,
5314
0
                                                    atCurNode);
5315
0
      if (NS_WARN_IF(splitNodeResult.Failed())) {
5316
0
        return splitNodeResult.Rv();
5317
0
      }
5318
0
      curQuote =
5319
0
        HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::blockquote,
5320
0
                                                  splitNodeResult.SplitPoint());
5321
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5322
0
        return NS_ERROR_EDITOR_DESTROYED;
5323
0
      }
5324
0
      if (NS_WARN_IF(!curQuote)) {
5325
0
        return NS_ERROR_FAILURE;
5326
0
      }
5327
0
      // remember our new block for postprocessing
5328
0
      mNewBlock = curQuote;
5329
0
      // curQuote is now the correct thing to put curNode in
5330
0
    }
5331
0
5332
0
    // tuck the node into the end of the active blockquote
5333
0
    rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
5334
0
                                                      *curQuote);
5335
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
5336
0
      return NS_ERROR_EDITOR_DESTROYED;
5337
0
    }
5338
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
5339
0
      return rv;
5340
0
    }
5341
0
    // forget curList, if any
5342
0
    curList = nullptr;
5343
0
  }
5344
0
  return NS_OK;
5345
0
}
5346
5347
5348
nsresult
5349
HTMLEditRules::WillOutdent(bool* aCancel,
5350
                           bool* aHandled)
5351
0
{
5352
0
  MOZ_ASSERT(IsEditorDataAvailable());
5353
0
  MOZ_ASSERT(aCancel && aHandled);
5354
0
  *aCancel = false;
5355
0
  *aHandled = true;
5356
0
5357
0
  nsresult rv = NormalizeSelection();
5358
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
5359
0
    return rv;
5360
0
  }
5361
0
5362
0
  // OutdentAroundSelection() creates AutoSelectionRestorer.  Therefore,
5363
0
  // even if it returns NS_OK, the editor might have been destroyed at
5364
0
  // restoring Selection.
5365
0
  SplitRangeOffFromNodeResult outdentResult = OutdentAroundSelection();
5366
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
5367
0
    return NS_ERROR_EDITOR_DESTROYED;
5368
0
  }
5369
0
  if (NS_WARN_IF(outdentResult.Failed())) {
5370
0
    return outdentResult.Rv();
5371
0
  }
5372
0
5373
0
  // Make sure selection didn't stick to last piece of content in old bq (only
5374
0
  // a problem for collapsed selections)
5375
0
  if (!outdentResult.GetLeftContent() && !outdentResult.GetRightContent()) {
5376
0
    return NS_OK;
5377
0
  }
5378
0
5379
0
  if (!SelectionRef().IsCollapsed()) {
5380
0
    return NS_OK;
5381
0
  }
5382
0
5383
0
  // Push selection past end of left element of last split indented element.
5384
0
  if (outdentResult.GetLeftContent()) {
5385
0
    nsRange* firstRange = SelectionRef().GetRangeAt(0);
5386
0
    if (NS_WARN_IF(!firstRange)) {
5387
0
      return NS_OK;
5388
0
    }
5389
0
    const RangeBoundary& atStartOfSelection = firstRange->StartRef();
5390
0
    if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
5391
0
      return NS_ERROR_FAILURE;
5392
0
    }
5393
0
    if (atStartOfSelection.Container() == outdentResult.GetLeftContent() ||
5394
0
        EditorUtils::IsDescendantOf(*atStartOfSelection.Container(),
5395
0
                                    *outdentResult.GetLeftContent())) {
5396
0
      // Selection is inside the left node - push it past it.
5397
0
      EditorRawDOMPoint afterRememberedLeftBQ(outdentResult.GetLeftContent());
5398
0
      afterRememberedLeftBQ.AdvanceOffset();
5399
0
      IgnoredErrorResult error;
5400
0
      SelectionRef().Collapse(afterRememberedLeftBQ, error);
5401
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
5402
0
        return NS_ERROR_EDITOR_DESTROYED;
5403
0
      }
5404
0
      NS_WARNING_ASSERTION(!error.Failed(),
5405
0
        "Failed to collapse selection after the left <blockquote>");
5406
0
    }
5407
0
  }
5408
0
  // And pull selection before beginning of right element of last split
5409
0
  // indented element.
5410
0
  if (outdentResult.GetRightContent()) {
5411
0
    nsRange* firstRange = SelectionRef().GetRangeAt(0);
5412
0
    const RangeBoundary& atStartOfSelection = firstRange->StartRef();
5413
0
    if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
5414
0
      return NS_ERROR_FAILURE;
5415
0
    }
5416
0
    if (atStartOfSelection.Container() == outdentResult.GetRightContent() ||
5417
0
        EditorUtils::IsDescendantOf(*atStartOfSelection.Container(),
5418
0
                                    *outdentResult.GetRightContent())) {
5419
0
      // Selection is inside the right element - push it before it.
5420
0
      EditorRawDOMPoint atRememberedRightBQ(outdentResult.GetRightContent());
5421
0
      IgnoredErrorResult error;
5422
0
      SelectionRef().Collapse(atRememberedRightBQ, error);
5423
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5424
0
        return NS_ERROR_EDITOR_DESTROYED;
5425
0
      }
5426
0
      NS_WARNING_ASSERTION(!error.Failed(),
5427
0
        "Failed to collapse selection after the right <blockquote>");
5428
0
    }
5429
0
  }
5430
0
  return NS_OK;
5431
0
}
5432
5433
SplitRangeOffFromNodeResult
5434
HTMLEditRules::OutdentAroundSelection()
5435
0
{
5436
0
  MOZ_ASSERT(IsEditorDataAvailable());
5437
0
5438
0
  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
5439
0
5440
0
  bool useCSS = HTMLEditorRef().IsCSSEnabled();
5441
0
5442
0
  // Convert the selection ranges into "promoted" selection ranges: this
5443
0
  // basically just expands the range to include the immediate block parent,
5444
0
  // and then further expands to include any ancestors whose children are all
5445
0
  // in the range
5446
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
5447
0
  nsresult rv =
5448
0
    GetNodesFromSelection(EditSubAction::eOutdent, arrayOfNodes,
5449
0
                          TouchContent::yes);
5450
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
5451
0
    return SplitRangeOffFromNodeResult(rv);
5452
0
  }
5453
0
5454
0
  // Okay, now go through all the nodes and remove a level of blockquoting,
5455
0
  // or whatever is appropriate.  Wohoo!
5456
0
5457
0
  nsCOMPtr<nsIContent> leftContentOfLastOutdented;
5458
0
  nsCOMPtr<nsIContent> middleContentOfLastOutdented;
5459
0
  nsCOMPtr<nsIContent> rightContentOfLastOutdented;
5460
0
  nsCOMPtr<Element> curBlockQuote;
5461
0
  nsCOMPtr<nsIContent> firstBQChild, lastBQChild;
5462
0
  bool curBlockQuoteIsIndentedWithCSS = false;
5463
0
  for (uint32_t i = 0; i < arrayOfNodes.Length(); i++) {
5464
0
    if (!arrayOfNodes[i]->IsContent()) {
5465
0
      continue;
5466
0
    }
5467
0
    OwningNonNull<nsIContent> curNode = *arrayOfNodes[i]->AsContent();
5468
0
5469
0
    // Here's where we actually figure out what to do
5470
0
    int32_t offset;
5471
0
    nsCOMPtr<nsINode> curParent =
5472
0
      EditorBase::GetNodeLocation(curNode, &offset);
5473
0
    if (!curParent) {
5474
0
      continue;
5475
0
    }
5476
0
5477
0
    // Is it a blockquote?
5478
0
    if (curNode->IsHTMLElement(nsGkAtoms::blockquote)) {
5479
0
      // If it is a blockquote, remove it.  So we need to finish up dealng
5480
0
      // with any curBlockQuote first.
5481
0
      if (curBlockQuote) {
5482
0
        SplitRangeOffFromNodeResult outdentResult =
5483
0
          OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
5484
0
                             curBlockQuoteIsIndentedWithCSS);
5485
0
        if (NS_WARN_IF(outdentResult.Failed())) {
5486
0
          return outdentResult;
5487
0
        }
5488
0
        leftContentOfLastOutdented = outdentResult.GetLeftContent();
5489
0
        middleContentOfLastOutdented = outdentResult.GetMiddleContent();
5490
0
        rightContentOfLastOutdented = outdentResult.GetRightContent();
5491
0
        curBlockQuote = nullptr;
5492
0
        firstBQChild = nullptr;
5493
0
        lastBQChild = nullptr;
5494
0
        curBlockQuoteIsIndentedWithCSS = false;
5495
0
      }
5496
0
      rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
5497
0
                             *curNode->AsElement());
5498
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5499
0
        return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5500
0
      }
5501
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
5502
0
        return SplitRangeOffFromNodeResult(rv);
5503
0
      }
5504
0
      continue;
5505
0
    }
5506
0
5507
0
    // Is it a block with a 'margin' property?
5508
0
    if (useCSS && IsBlockNode(curNode)) {
5509
0
      nsAtom& marginProperty = MarginPropertyAtomForIndent(curNode);
5510
0
      nsAutoString value;
5511
0
      CSSEditUtils::GetSpecifiedProperty(curNode, marginProperty, value);
5512
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5513
0
        return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5514
0
      }
5515
0
      float f;
5516
0
      RefPtr<nsAtom> unit;
5517
0
      CSSEditUtils::ParseLength(value, &f, getter_AddRefs(unit));
5518
0
      if (f > 0) {
5519
0
        nsresult rv = DecreaseMarginToOutdent(*curNode->AsElement());
5520
0
        if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
5521
0
          return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5522
0
        }
5523
0
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
5524
0
          "Failed to decrease indentation");
5525
0
        continue;
5526
0
      }
5527
0
    }
5528
0
5529
0
    // Is it a list item?
5530
0
    if (HTMLEditUtils::IsListItem(curNode)) {
5531
0
      // If it is a list item, that means we are not outdenting whole list.
5532
0
      // So we need to finish up dealing with any curBlockQuote, and then pop
5533
0
      // this list item.
5534
0
      if (curBlockQuote) {
5535
0
        SplitRangeOffFromNodeResult outdentResult =
5536
0
          OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
5537
0
                             curBlockQuoteIsIndentedWithCSS);
5538
0
        if (NS_WARN_IF(outdentResult.Failed())) {
5539
0
          return outdentResult;
5540
0
        }
5541
0
        leftContentOfLastOutdented = outdentResult.GetLeftContent();
5542
0
        middleContentOfLastOutdented = outdentResult.GetMiddleContent();
5543
0
        rightContentOfLastOutdented = outdentResult.GetRightContent();
5544
0
        curBlockQuote = nullptr;
5545
0
        firstBQChild = nullptr;
5546
0
        lastBQChild = nullptr;
5547
0
        curBlockQuoteIsIndentedWithCSS = false;
5548
0
      }
5549
0
      rv = PopListItem(*curNode->AsContent());
5550
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
5551
0
        return SplitRangeOffFromNodeResult(rv);
5552
0
      }
5553
0
      continue;
5554
0
    }
5555
0
5556
0
    // Do we have a blockquote that we are already committed to removing?
5557
0
    if (curBlockQuote) {
5558
0
      // If so, is this node a descendant?
5559
0
      if (EditorUtils::IsDescendantOf(*curNode, *curBlockQuote)) {
5560
0
        lastBQChild = curNode;
5561
0
        // Then we don't need to do anything different for this node
5562
0
        continue;
5563
0
      }
5564
0
      // Otherwise, we have progressed beyond end of curBlockQuote, so
5565
0
      // let's handle it now.  We need to remove the portion of
5566
0
      // curBlockQuote that contains [firstBQChild - lastBQChild].
5567
0
      SplitRangeOffFromNodeResult outdentResult =
5568
0
        OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
5569
0
                           curBlockQuoteIsIndentedWithCSS);
5570
0
      if (NS_WARN_IF(outdentResult.Failed())) {
5571
0
        return outdentResult;
5572
0
      }
5573
0
      leftContentOfLastOutdented = outdentResult.GetLeftContent();
5574
0
      middleContentOfLastOutdented = outdentResult.GetMiddleContent();
5575
0
      rightContentOfLastOutdented = outdentResult.GetRightContent();
5576
0
      curBlockQuote = nullptr;
5577
0
      firstBQChild = nullptr;
5578
0
      lastBQChild = nullptr;
5579
0
      curBlockQuoteIsIndentedWithCSS = false;
5580
0
      // Fall out and handle curNode
5581
0
    }
5582
0
5583
0
    // Are we inside a blockquote?
5584
0
    OwningNonNull<nsINode> n = curNode;
5585
0
    curBlockQuoteIsIndentedWithCSS = false;
5586
0
    // Keep looking up the hierarchy as long as we don't hit the body or the
5587
0
    // active editing host or a table element (other than an entire table)
5588
0
    while (!n->IsHTMLElement(nsGkAtoms::body) &&
5589
0
           HTMLEditorRef().IsDescendantOfEditorRoot(n) &&
5590
0
           (n->IsHTMLElement(nsGkAtoms::table) ||
5591
0
            !HTMLEditUtils::IsTableElement(n))) {
5592
0
      if (!n->GetParentNode()) {
5593
0
        break;
5594
0
      }
5595
0
      n = *n->GetParentNode();
5596
0
      if (n->IsHTMLElement(nsGkAtoms::blockquote)) {
5597
0
        // If so, remember it and the first node we are taking out of it.
5598
0
        curBlockQuote = n->AsElement();
5599
0
        firstBQChild = curNode;
5600
0
        lastBQChild = curNode;
5601
0
        break;
5602
0
      }
5603
0
5604
0
      if (!useCSS) {
5605
0
        continue;
5606
0
      }
5607
0
5608
0
      nsAtom& marginProperty = MarginPropertyAtomForIndent(curNode);
5609
0
      nsAutoString value;
5610
0
      CSSEditUtils::GetSpecifiedProperty(*n, marginProperty, value);
5611
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5612
0
        return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5613
0
      }
5614
0
      float f;
5615
0
      RefPtr<nsAtom> unit;
5616
0
      CSSEditUtils::ParseLength(value, &f, getter_AddRefs(unit));
5617
0
      if (f > 0 && !(HTMLEditUtils::IsList(curParent) &&
5618
0
                     HTMLEditUtils::IsList(curNode))) {
5619
0
        curBlockQuote = n->AsElement();
5620
0
        firstBQChild = curNode;
5621
0
        lastBQChild = curNode;
5622
0
        curBlockQuoteIsIndentedWithCSS = true;
5623
0
        break;
5624
0
      }
5625
0
    }
5626
0
5627
0
    if (curBlockQuote) {
5628
0
      continue;
5629
0
    }
5630
0
5631
0
    // Couldn't find enclosing blockquote.
5632
0
    if (HTMLEditUtils::IsList(curParent)) {
5633
0
      // Move node out of list
5634
0
      if (HTMLEditUtils::IsList(curNode)) {
5635
0
        // Just unwrap this sublist
5636
0
        rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
5637
0
                               *curNode->AsElement());
5638
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
5639
0
          return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5640
0
        }
5641
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
5642
0
          return SplitRangeOffFromNodeResult(rv);
5643
0
        }
5644
0
      }
5645
0
      continue;
5646
0
    }
5647
0
5648
0
    if (HTMLEditUtils::IsList(curNode)) {
5649
0
      // node is a list, but parent is non-list: move list items out
5650
0
      nsCOMPtr<nsIContent> child = curNode->GetLastChild();
5651
0
      while (child) {
5652
0
        if (HTMLEditUtils::IsListItem(child)) {
5653
0
          rv = PopListItem(*child);
5654
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
5655
0
            return SplitRangeOffFromNodeResult(rv);
5656
0
          }
5657
0
        } else if (HTMLEditUtils::IsList(child)) {
5658
0
          // We have an embedded list, so move it out from under the parent
5659
0
          // list. Be sure to put it after the parent list because this
5660
0
          // loop iterates backwards through the parent's list of children.
5661
0
          EditorRawDOMPoint afterCurrentList(curParent, offset + 1);
5662
0
          rv = HTMLEditorRef().MoveNodeWithTransaction(*child,
5663
0
                                                       afterCurrentList);
5664
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
5665
0
            return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5666
0
          }
5667
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
5668
0
            return SplitRangeOffFromNodeResult(rv);
5669
0
          }
5670
0
        } else {
5671
0
          // Delete any non-list items for now
5672
0
          rv = HTMLEditorRef().DeleteNodeWithTransaction(*child);
5673
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
5674
0
            return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5675
0
          }
5676
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
5677
0
            return SplitRangeOffFromNodeResult(rv);
5678
0
          }
5679
0
        }
5680
0
        child = curNode->GetLastChild();
5681
0
      }
5682
0
      // Delete the now-empty list
5683
0
      rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
5684
0
                             *curNode->AsElement());
5685
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5686
0
        return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5687
0
      }
5688
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
5689
0
        return SplitRangeOffFromNodeResult(rv);
5690
0
      }
5691
0
      continue;
5692
0
    }
5693
0
5694
0
    if (useCSS) {
5695
0
      nsCOMPtr<Element> element;
5696
0
      if (curNode->GetAsText()) {
5697
0
        // We want to outdent the parent of text nodes
5698
0
        element = curNode->GetParentElement();
5699
0
      } else if (curNode->IsElement()) {
5700
0
        element = curNode->AsElement();
5701
0
      }
5702
0
      if (element) {
5703
0
        nsresult rv = DecreaseMarginToOutdent(*element);
5704
0
        if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
5705
0
          return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5706
0
        }
5707
0
        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
5708
0
          "Failed to decrease indentation");
5709
0
      }
5710
0
      continue;
5711
0
    }
5712
0
  }
5713
0
5714
0
  if (!curBlockQuote) {
5715
0
    return SplitRangeOffFromNodeResult(leftContentOfLastOutdented,
5716
0
                                       middleContentOfLastOutdented,
5717
0
                                       rightContentOfLastOutdented);
5718
0
  }
5719
0
5720
0
  // We have a <blockquote> we haven't finished handling.
5721
0
  SplitRangeOffFromNodeResult outdentResult =
5722
0
    OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
5723
0
                       curBlockQuoteIsIndentedWithCSS);
5724
0
  if (NS_WARN_IF(outdentResult.Failed())) {
5725
0
    return outdentResult;
5726
0
  }
5727
0
  return outdentResult;
5728
0
}
5729
5730
SplitRangeOffFromNodeResult
5731
HTMLEditRules::SplitRangeOffFromBlockAndRemoveMiddleContainer(
5732
                 Element& aBlockElement,
5733
                 nsIContent& aStartOfRange,
5734
                 nsIContent& aEndOfRange)
5735
0
{
5736
0
  MOZ_ASSERT(IsEditorDataAvailable());
5737
0
5738
0
  SplitRangeOffFromNodeResult splitResult =
5739
0
    SplitRangeOffFromBlock(aBlockElement, aStartOfRange, aEndOfRange);
5740
0
  if (NS_WARN_IF(splitResult.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
5741
0
    return splitResult;
5742
0
  }
5743
0
  NS_WARNING_ASSERTION(splitResult.Succeeded(),
5744
0
    "Failed to split the range off from the block element");
5745
0
  nsresult rv =
5746
0
    HTMLEditorRef().RemoveBlockContainerWithTransaction(aBlockElement);
5747
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
5748
0
    return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5749
0
  }
5750
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
5751
0
    return SplitRangeOffFromNodeResult(rv);
5752
0
  }
5753
0
  return SplitRangeOffFromNodeResult(splitResult.GetLeftContent(),
5754
0
                                     nullptr,
5755
0
                                     splitResult.GetRightContent());
5756
0
}
5757
5758
SplitRangeOffFromNodeResult
5759
HTMLEditRules::SplitRangeOffFromBlock(Element& aBlockElement,
5760
                                      nsIContent& aStartOfMiddleElement,
5761
                                      nsIContent& aEndOfMiddleElement)
5762
0
{
5763
0
  MOZ_ASSERT(IsEditorDataAvailable());
5764
0
5765
0
  // aStartOfMiddleElement and aEndOfMiddleElement must be exclusive
5766
0
  // descendants of aBlockElement.
5767
0
  MOZ_ASSERT(EditorUtils::IsDescendantOf(aStartOfMiddleElement, aBlockElement));
5768
0
  MOZ_ASSERT(EditorUtils::IsDescendantOf(aEndOfMiddleElement, aBlockElement));
5769
0
5770
0
  // Split at the start.
5771
0
  SplitNodeResult splitAtStartResult =
5772
0
    HTMLEditorRef().SplitNodeDeepWithTransaction(
5773
0
                      aBlockElement, EditorRawDOMPoint(&aStartOfMiddleElement),
5774
0
                      SplitAtEdges::eDoNotCreateEmptyContainer);
5775
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
5776
0
    return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5777
0
  }
5778
0
  NS_WARNING_ASSERTION(splitAtStartResult.Succeeded(),
5779
0
    "Failed to split aBlockElement at start");
5780
0
5781
0
  // Split at after the end
5782
0
  EditorRawDOMPoint atAfterEnd(&aEndOfMiddleElement);
5783
0
  DebugOnly<bool> advanced = atAfterEnd.AdvanceOffset();
5784
0
  NS_WARNING_ASSERTION(advanced,
5785
0
    "Failed to advance offset after the end node");
5786
0
  SplitNodeResult splitAtEndResult =
5787
0
    HTMLEditorRef().SplitNodeDeepWithTransaction(
5788
0
                      aBlockElement, atAfterEnd,
5789
0
                      SplitAtEdges::eDoNotCreateEmptyContainer);
5790
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
5791
0
    return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5792
0
  }
5793
0
  NS_WARNING_ASSERTION(splitAtEndResult.Succeeded(),
5794
0
    "Failed to split aBlockElement at after end");
5795
0
5796
0
  return SplitRangeOffFromNodeResult(splitAtStartResult, splitAtEndResult);
5797
0
}
5798
5799
SplitRangeOffFromNodeResult
5800
HTMLEditRules::OutdentPartOfBlock(Element& aBlockElement,
5801
                                  nsIContent& aStartOfOutdent,
5802
                                  nsIContent& aEndOfOutdent,
5803
                                  bool aIsBlockIndentedWithCSS)
5804
0
{
5805
0
  MOZ_ASSERT(IsEditorDataAvailable());
5806
0
5807
0
  SplitRangeOffFromNodeResult splitResult =
5808
0
    SplitRangeOffFromBlock(aBlockElement, aStartOfOutdent, aEndOfOutdent);
5809
0
  if (NS_WARN_IF(splitResult.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
5810
0
    return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5811
0
  }
5812
0
5813
0
  if (NS_WARN_IF(!splitResult.GetMiddleContentAsElement())) {
5814
0
    return SplitRangeOffFromNodeResult(NS_ERROR_FAILURE);
5815
0
  }
5816
0
5817
0
  if (!aIsBlockIndentedWithCSS) {
5818
0
    nsresult rv =
5819
0
      HTMLEditorRef().RemoveBlockContainerWithTransaction(
5820
0
                        *splitResult.GetMiddleContentAsElement());
5821
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
5822
0
      return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
5823
0
    }
5824
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
5825
0
      return SplitRangeOffFromNodeResult(rv);
5826
0
    }
5827
0
    return SplitRangeOffFromNodeResult(splitResult.GetLeftContent(),
5828
0
                                       nullptr,
5829
0
                                       splitResult.GetRightContent());
5830
0
  }
5831
0
5832
0
  if (splitResult.GetMiddleContentAsElement()) {
5833
0
    nsresult rv =
5834
0
      DecreaseMarginToOutdent(*splitResult.GetMiddleContentAsElement());
5835
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
5836
0
      return SplitRangeOffFromNodeResult(rv);
5837
0
    }
5838
0
    return splitResult;
5839
0
  }
5840
0
5841
0
  return splitResult;
5842
0
}
5843
5844
CreateElementResult
5845
HTMLEditRules::ConvertListType(Element& aListElement,
5846
                               nsAtom& aNewListTag,
5847
                               nsAtom& aNewListItemTag)
5848
0
{
5849
0
  MOZ_ASSERT(IsEditorDataAvailable());
5850
0
5851
0
  nsCOMPtr<nsINode> child = aListElement.GetFirstChild();
5852
0
  while (child) {
5853
0
    if (child->IsElement()) {
5854
0
      Element* element = child->AsElement();
5855
0
      if (HTMLEditUtils::IsListItem(element) &&
5856
0
          !element->IsHTMLElement(&aNewListItemTag)) {
5857
0
        child =
5858
0
          HTMLEditorRef().ReplaceContainerWithTransaction(*element,
5859
0
                                                          aNewListItemTag);
5860
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
5861
0
          return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
5862
0
        }
5863
0
        if (NS_WARN_IF(!child)) {
5864
0
          return CreateElementResult(NS_ERROR_FAILURE);
5865
0
        }
5866
0
      } else if (HTMLEditUtils::IsList(element) &&
5867
0
                 !element->IsHTMLElement(&aNewListTag)) {
5868
0
        // XXX List elements shouldn't have other list elements as their
5869
0
        //     child.  Why do we handle such invalid tree?
5870
0
        CreateElementResult convertListTypeResult =
5871
0
          ConvertListType(*child->AsElement(), aNewListTag, aNewListItemTag);
5872
0
        if (NS_WARN_IF(convertListTypeResult.Failed())) {
5873
0
          return convertListTypeResult;
5874
0
        }
5875
0
        child = convertListTypeResult.forget();
5876
0
      }
5877
0
    }
5878
0
    child = child->GetNextSibling();
5879
0
  }
5880
0
5881
0
  if (aListElement.IsHTMLElement(&aNewListTag)) {
5882
0
    return CreateElementResult(&aListElement);
5883
0
  }
5884
0
5885
0
  RefPtr<Element> listElement =
5886
0
    HTMLEditorRef().ReplaceContainerWithTransaction(aListElement, aNewListTag);
5887
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
5888
0
    return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
5889
0
  }
5890
0
  NS_WARNING_ASSERTION(listElement != nullptr, "Failed to create list element");
5891
0
  return CreateElementResult(listElement.forget());
5892
0
}
5893
5894
nsresult
5895
HTMLEditRules::CreateStyleForInsertText(nsIDocument& aDocument)
5896
0
{
5897
0
  MOZ_ASSERT(IsEditorDataAvailable());
5898
0
  MOZ_ASSERT(HTMLEditorRef().mTypeInState);
5899
0
5900
0
  bool weDidSomething = false;
5901
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
5902
0
  if (NS_WARN_IF(!firstRange)) {
5903
0
    return NS_ERROR_FAILURE;
5904
0
  }
5905
0
  nsCOMPtr<nsINode> node = firstRange->GetStartContainer();
5906
0
  int32_t offset = firstRange->StartOffset();
5907
0
5908
0
  RefPtr<Element> rootElement = aDocument.GetRootElement();
5909
0
  if (NS_WARN_IF(!rootElement)) {
5910
0
    return NS_ERROR_FAILURE;
5911
0
  }
5912
0
5913
0
  // process clearing any styles first
5914
0
  UniquePtr<PropItem> item = HTMLEditorRef().mTypeInState->TakeClearProperty();
5915
0
5916
0
  {
5917
0
    // Transactions may set selection, but we will set selection if necessary.
5918
0
    AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
5919
0
5920
0
    while (item && node != rootElement) {
5921
0
      // XXX If we redesign ClearStyle(), we can use EditorDOMPoint in this
5922
0
      //     method.
5923
0
      nsresult rv =
5924
0
        HTMLEditorRef().ClearStyle(address_of(node), &offset,
5925
0
                                   item->tag, item->attr);
5926
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5927
0
        return NS_ERROR_EDITOR_DESTROYED;
5928
0
      }
5929
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
5930
0
        return rv;
5931
0
      }
5932
0
      item = HTMLEditorRef().mTypeInState->TakeClearProperty();
5933
0
      weDidSomething = true;
5934
0
    }
5935
0
  }
5936
0
5937
0
  // then process setting any styles
5938
0
  int32_t relFontSize = HTMLEditorRef().mTypeInState->TakeRelativeFontSize();
5939
0
  item = HTMLEditorRef().mTypeInState->TakeSetProperty();
5940
0
5941
0
  if (item || relFontSize) {
5942
0
    // we have at least one style to add; make a new text node to insert style
5943
0
    // nodes above.
5944
0
    if (RefPtr<Text> text = node->GetAsText()) {
5945
0
      // if we are in a text node, split it
5946
0
      SplitNodeResult splitTextNodeResult =
5947
0
        HTMLEditorRef().SplitNodeDeepWithTransaction(
5948
0
                          *text, EditorRawDOMPoint(text, offset),
5949
0
                          SplitAtEdges::eAllowToCreateEmptyContainer);
5950
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5951
0
        return NS_ERROR_EDITOR_DESTROYED;
5952
0
      }
5953
0
      if (NS_WARN_IF(splitTextNodeResult.Failed())) {
5954
0
        return splitTextNodeResult.Rv();
5955
0
      }
5956
0
      EditorRawDOMPoint splitPoint(splitTextNodeResult.SplitPoint());
5957
0
      node = splitPoint.GetContainer();
5958
0
      offset = splitPoint.Offset();
5959
0
    }
5960
0
    if (!HTMLEditorRef().IsContainer(node)) {
5961
0
      return NS_OK;
5962
0
    }
5963
0
    OwningNonNull<Text> newNode =
5964
0
      EditorBase::CreateTextNode(aDocument, EmptyString());
5965
0
    nsresult rv =
5966
0
      HTMLEditorRef().InsertNodeWithTransaction(
5967
0
                        *newNode, EditorRawDOMPoint(node, offset));
5968
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
5969
0
      return NS_ERROR_EDITOR_DESTROYED;
5970
0
    }
5971
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
5972
0
      return rv;
5973
0
    }
5974
0
    node = newNode;
5975
0
    offset = 0;
5976
0
    weDidSomething = true;
5977
0
5978
0
    if (relFontSize) {
5979
0
      // dir indicated bigger versus smaller.  1 = bigger, -1 = smaller
5980
0
      HTMLEditor::FontSize dir = relFontSize > 0 ?
5981
0
        HTMLEditor::FontSize::incr : HTMLEditor::FontSize::decr;
5982
0
      for (int32_t j = 0; j < DeprecatedAbs(relFontSize); j++) {
5983
0
        rv = HTMLEditorRef().RelativeFontChangeOnTextNode(dir, newNode, 0, -1);
5984
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
5985
0
          return NS_ERROR_EDITOR_DESTROYED;
5986
0
        }
5987
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
5988
0
          return rv;
5989
0
        }
5990
0
      }
5991
0
    }
5992
0
5993
0
    while (item) {
5994
0
      rv = HTMLEditorRef().SetInlinePropertyOnNode(*node->AsContent(),
5995
0
                                                   *item->tag, item->attr,
5996
0
                                                   item->value);
5997
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
5998
0
        return NS_ERROR_EDITOR_DESTROYED;
5999
0
      }
6000
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
6001
0
        return rv;
6002
0
      }
6003
0
      item = HTMLEditorRef().mTypeInState->TakeSetProperty();
6004
0
    }
6005
0
  }
6006
0
6007
0
  if (!weDidSomething) {
6008
0
    return NS_OK;
6009
0
  }
6010
0
6011
0
  nsresult rv = SelectionRef().Collapse(node, offset);
6012
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
6013
0
    return NS_ERROR_EDITOR_DESTROYED;
6014
0
  }
6015
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
6016
0
    return rv;
6017
0
  }
6018
0
  return NS_OK;
6019
0
}
6020
6021
bool
6022
HTMLEditRules::IsEmptyBlockElement(Element& aElement,
6023
                                   IgnoreSingleBR aIgnoreSingleBR)
6024
0
{
6025
0
  MOZ_ASSERT(IsEditorDataAvailable());
6026
0
6027
0
  if (NS_WARN_IF(!IsBlockNode(aElement))) {
6028
0
    return false;
6029
0
  }
6030
0
  bool isEmpty = true;
6031
0
  nsresult rv =
6032
0
    HTMLEditorRef().IsEmptyNode(&aElement, &isEmpty,
6033
0
                                aIgnoreSingleBR == IgnoreSingleBR::eYes);
6034
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
6035
0
    return false;
6036
0
  }
6037
0
  return isEmpty;
6038
0
}
6039
6040
nsresult
6041
HTMLEditRules::WillAlign(const nsAString& aAlignType,
6042
                         bool* aCancel,
6043
                         bool* aHandled)
6044
0
{
6045
0
  MOZ_ASSERT(IsEditorDataAvailable());
6046
0
  MOZ_ASSERT(aCancel && aHandled);
6047
0
6048
0
  *aCancel = false;
6049
0
  *aHandled = false;
6050
0
6051
0
  // FYI: Ignore cancel result of WillInsert().
6052
0
  nsresult rv = WillInsert();
6053
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
6054
0
    return NS_ERROR_EDITOR_DESTROYED;
6055
0
  }
6056
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
6057
0
6058
0
  rv = NormalizeSelection();
6059
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
6060
0
    return rv;
6061
0
  }
6062
0
6063
0
  *aHandled = true;
6064
0
  rv = AlignContentsAtSelection(aAlignType);
6065
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
6066
0
      NS_WARN_IF(!CanHandleEditAction())) {
6067
0
    return NS_ERROR_EDITOR_DESTROYED;
6068
0
  }
6069
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
6070
0
    return rv;
6071
0
  }
6072
0
  return NS_OK;
6073
0
}
6074
6075
nsresult
6076
HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType)
6077
0
{
6078
0
  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
6079
0
6080
0
  // Convert the selection ranges into "promoted" selection ranges: This
6081
0
  // basically just expands the range to include the immediate block parent,
6082
0
  // and then further expands to include any ancestors whose children are all
6083
0
  // in the range
6084
0
  nsTArray<OwningNonNull<nsINode>> nodeArray;
6085
0
  nsresult rv =
6086
0
    GetNodesFromSelection(EditSubAction::eSetOrClearAlignment, nodeArray,
6087
0
                          TouchContent::yes);
6088
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
6089
0
    return rv;
6090
0
  }
6091
0
6092
0
  // If we don't have any nodes, or we have only a single br, then we are
6093
0
  // creating an empty alignment div.  We have to do some different things for
6094
0
  // these.
6095
0
  bool emptyDiv = nodeArray.IsEmpty();
6096
0
  if (nodeArray.Length() == 1) {
6097
0
    OwningNonNull<nsINode> node = nodeArray[0];
6098
0
6099
0
    if (HTMLEditUtils::SupportsAlignAttr(*node)) {
6100
0
      // The node is a table element, an hr, a paragraph, a div or a section
6101
0
      // header; in HTML 4, it can directly carry the ALIGN attribute and we
6102
0
      // don't need to make a div! If we are in CSS mode, all the work is done
6103
0
      // in AlignBlock
6104
0
      rv = AlignBlock(*node->AsElement(), aAlignType,
6105
0
                      ResetAlignOf::OnlyDescendants);
6106
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
6107
0
        return rv;
6108
0
      }
6109
0
      return NS_OK;
6110
0
    }
6111
0
6112
0
    if (TextEditUtils::IsBreak(node)) {
6113
0
      // The special case emptyDiv code (below) that consumes BRs can cause
6114
0
      // tables to split if the start node of the selection is not in a table
6115
0
      // cell or caption, for example parent is a <tr>.  Avoid this unnecessary
6116
0
      // splitting if possible by leaving emptyDiv FALSE so that we fall
6117
0
      // through to the normal case alignment code.
6118
0
      //
6119
0
      // XXX: It seems a little error prone for the emptyDiv special case code
6120
0
      // to assume that the start node of the selection is the parent of the
6121
0
      // single node in the nodeArray, as the paragraph above points out. Do we
6122
0
      // rely on the selection start node because of the fact that nodeArray
6123
0
      // can be empty?  We should probably revisit this issue. - kin
6124
0
6125
0
      nsRange* firstRange = SelectionRef().GetRangeAt(0);
6126
0
      if (NS_WARN_IF(!firstRange)) {
6127
0
        return NS_ERROR_FAILURE;
6128
0
      }
6129
0
      const RangeBoundary& atStartOfSelection = firstRange->StartRef();
6130
0
      if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
6131
0
        return NS_ERROR_FAILURE;
6132
0
      }
6133
0
      nsINode* parent = atStartOfSelection.Container();
6134
0
      emptyDiv = !HTMLEditUtils::IsTableElement(parent) ||
6135
0
                 HTMLEditUtils::IsTableCellOrCaption(*parent);
6136
0
    }
6137
0
  }
6138
0
  if (emptyDiv) {
6139
0
    nsRange* firstRange = SelectionRef().GetRangeAt(0);
6140
0
    if (NS_WARN_IF(!firstRange)) {
6141
0
      return NS_ERROR_FAILURE;
6142
0
    }
6143
0
6144
0
    EditorDOMPoint atStartOfSelection(firstRange->StartRef());
6145
0
    if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
6146
0
      return NS_ERROR_FAILURE;
6147
0
    }
6148
0
6149
0
    SplitNodeResult splitNodeResult =
6150
0
      MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
6151
0
                                                  atStartOfSelection);
6152
0
    if (NS_WARN_IF(splitNodeResult.Failed())) {
6153
0
      return splitNodeResult.Rv();
6154
0
    }
6155
0
6156
0
    // Consume a trailing br, if any.  This is to keep an alignment from
6157
0
    // creating extra lines, if possible.
6158
0
    nsCOMPtr<nsIContent> brContent =
6159
0
      HTMLEditorRef().GetNextEditableHTMLNodeInBlock(
6160
0
                        splitNodeResult.SplitPoint());
6161
0
    EditorDOMPoint pointToInsertDiv(splitNodeResult.SplitPoint());
6162
0
    if (brContent && TextEditUtils::IsBreak(brContent)) {
6163
0
      // Making use of html structure... if next node after where we are
6164
0
      // putting our div is not a block, then the br we found is in same block
6165
0
      // we are, so it's safe to consume it.
6166
0
      nsCOMPtr<nsIContent> sibling;
6167
0
      if (pointToInsertDiv.GetChild()) {
6168
0
        sibling =
6169
0
          HTMLEditorRef().GetNextHTMLSibling(pointToInsertDiv.GetChild());
6170
0
      }
6171
0
      if (sibling && !IsBlockNode(*sibling)) {
6172
0
        AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertDiv);
6173
0
        rv = HTMLEditorRef().DeleteNodeWithTransaction(*brContent);
6174
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
6175
0
          return NS_ERROR_EDITOR_DESTROYED;
6176
0
        }
6177
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
6178
0
          return rv;
6179
0
        }
6180
0
      }
6181
0
    }
6182
0
    RefPtr<Element> div =
6183
0
      HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
6184
0
                                                pointToInsertDiv);
6185
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
6186
0
      return NS_ERROR_EDITOR_DESTROYED;
6187
0
    }
6188
0
    if (NS_WARN_IF(!div)) {
6189
0
      return NS_ERROR_FAILURE;
6190
0
    }
6191
0
    // Remember our new block for postprocessing
6192
0
    mNewBlock = div;
6193
0
    // Set up the alignment on the div, using HTML or CSS
6194
0
    rv = AlignBlock(*div, aAlignType, ResetAlignOf::OnlyDescendants);
6195
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6196
0
      return rv;
6197
0
    }
6198
0
    // Put in a moz-br so that it won't get deleted
6199
0
    CreateElementResult createMozBrResult =
6200
0
      CreateMozBR(EditorRawDOMPoint(div, 0));
6201
0
    if (NS_WARN_IF(createMozBrResult.Failed())) {
6202
0
      return createMozBrResult.Rv();
6203
0
    }
6204
0
    EditorRawDOMPoint atStartOfDiv(div, 0);
6205
0
    // Don't restore the selection
6206
0
    selectionRestorer.Abort();
6207
0
    ErrorResult error;
6208
0
    SelectionRef().Collapse(atStartOfDiv, error);
6209
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
6210
0
      error.SuppressException();
6211
0
      return NS_ERROR_EDITOR_DESTROYED;
6212
0
    }
6213
0
    if (NS_WARN_IF(error.Failed())) {
6214
0
      return error.StealNSResult();
6215
0
    }
6216
0
    return NS_OK;
6217
0
  }
6218
0
6219
0
  // Next we detect all the transitions in the array, where a transition
6220
0
  // means that adjacent nodes in the array don't have the same parent.
6221
0
6222
0
  nsTArray<bool> transitionList;
6223
0
  MakeTransitionList(nodeArray, transitionList);
6224
0
6225
0
  // Okay, now go through all the nodes and give them an align attrib or put
6226
0
  // them in a div, or whatever is appropriate.  Woohoo!
6227
0
6228
0
  nsCOMPtr<Element> curDiv;
6229
0
  bool useCSS = HTMLEditorRef().IsCSSEnabled();
6230
0
  int32_t indexOfTransitionList = -1;
6231
0
  for (OwningNonNull<nsINode>& curNode : nodeArray) {
6232
0
    ++indexOfTransitionList;
6233
0
6234
0
    // Ignore all non-editable nodes.  Leave them be.
6235
0
    if (!HTMLEditorRef().IsEditable(curNode)) {
6236
0
      continue;
6237
0
    }
6238
0
6239
0
    // The node is a table element, an hr, a paragraph, a div or a section
6240
0
    // header; in HTML 4, it can directly carry the ALIGN attribute and we
6241
0
    // don't need to nest it, just set the alignment.  In CSS, assign the
6242
0
    // corresponding CSS styles in AlignBlock
6243
0
    if (HTMLEditUtils::SupportsAlignAttr(*curNode)) {
6244
0
      rv = AlignBlock(*curNode->AsElement(), aAlignType,
6245
0
                      ResetAlignOf::ElementAndDescendants);
6246
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
6247
0
        return rv;
6248
0
      }
6249
0
      // Clear out curDiv so that we don't put nodes after this one into it
6250
0
      curDiv = nullptr;
6251
0
      continue;
6252
0
    }
6253
0
6254
0
    EditorDOMPoint atCurNode(curNode);
6255
0
    if (NS_WARN_IF(!atCurNode.IsSet())) {
6256
0
      continue;
6257
0
    }
6258
0
6259
0
    // Skip insignificant formatting text nodes to prevent unnecessary
6260
0
    // structure splitting!
6261
0
    bool isEmptyTextNode = false;
6262
0
    if (curNode->GetAsText() &&
6263
0
        ((HTMLEditUtils::IsTableElement(atCurNode.GetContainer()) &&
6264
0
          !HTMLEditUtils::IsTableCellOrCaption(*atCurNode.GetContainer())) ||
6265
0
         HTMLEditUtils::IsList(atCurNode.GetContainer()) ||
6266
0
         (NS_SUCCEEDED(HTMLEditorRef().IsEmptyNode(curNode,
6267
0
                                                   &isEmptyTextNode)) &&
6268
0
          isEmptyTextNode))) {
6269
0
      continue;
6270
0
    }
6271
0
6272
0
    // If it's a list item, or a list inside a list, forget any "current" div,
6273
0
    // and instead put divs inside the appropriate block (td, li, etc.)
6274
0
    if (HTMLEditUtils::IsListItem(curNode) ||
6275
0
        HTMLEditUtils::IsList(curNode)) {
6276
0
      AutoEditorDOMPointOffsetInvalidator lockChild(atCurNode);
6277
0
      rv = RemoveAlignment(*curNode, aAlignType, true);
6278
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
6279
0
        return rv;
6280
0
      }
6281
0
      if (useCSS) {
6282
0
        HTMLEditorRef().mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
6283
0
                                         curNode->AsElement(), nullptr,
6284
0
                                         nsGkAtoms::align, &aAlignType, false);
6285
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
6286
0
          return NS_ERROR_EDITOR_DESTROYED;
6287
0
        }
6288
0
        curDiv = nullptr;
6289
0
        continue;
6290
0
      }
6291
0
      if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
6292
0
        // If we don't use CSS, add a content to list element: they have to
6293
0
        // be inside another list, i.e., >= second level of nesting.
6294
0
        // XXX AlignInnerBlocks() handles list item elements and table cells.
6295
0
        //     Is it intentional to change alignment of nested other type
6296
0
        //     descendants too?
6297
0
        rv = AlignInnerBlocks(*curNode, aAlignType);
6298
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
6299
0
          return rv;
6300
0
        }
6301
0
        curDiv = nullptr;
6302
0
        continue;
6303
0
      }
6304
0
      // Clear out curDiv so that we don't put nodes after this one into it
6305
0
    }
6306
0
6307
0
    // Need to make a div to put things in if we haven't already, or if this
6308
0
    // node doesn't go in div we used earlier.
6309
0
    if (!curDiv || transitionList[indexOfTransitionList]) {
6310
0
      // First, check that our element can contain a div.
6311
0
      if (!HTMLEditorRef().CanContainTag(*atCurNode.GetContainer(),
6312
0
                                         *nsGkAtoms::div)) {
6313
0
        // Cancelled
6314
0
        return NS_OK;
6315
0
      }
6316
0
6317
0
      SplitNodeResult splitNodeResult =
6318
0
        MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div, atCurNode);
6319
0
      if (NS_WARN_IF(splitNodeResult.Failed())) {
6320
0
        return splitNodeResult.Rv();
6321
0
      }
6322
0
      curDiv =
6323
0
        HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
6324
0
                                                  splitNodeResult.SplitPoint());
6325
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
6326
0
        return NS_ERROR_EDITOR_DESTROYED;
6327
0
      }
6328
0
      if (NS_WARN_IF(!curDiv)) {
6329
0
        return NS_ERROR_FAILURE;
6330
0
      }
6331
0
      // Remember our new block for postprocessing
6332
0
      mNewBlock = curDiv;
6333
0
      // Set up the alignment on the div
6334
0
      rv = AlignBlock(*curDiv, aAlignType, ResetAlignOf::OnlyDescendants);
6335
0
      if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
6336
0
        return NS_ERROR_EDITOR_DESTROYED;
6337
0
      }
6338
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to align the <div>");
6339
0
    }
6340
0
6341
0
    // Tuck the node into the end of the active div
6342
0
    rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
6343
0
                                                      *curDiv);
6344
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
6345
0
      return NS_ERROR_EDITOR_DESTROYED;
6346
0
    }
6347
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6348
0
      return rv;
6349
0
    }
6350
0
  }
6351
0
6352
0
  return NS_OK;
6353
0
}
6354
6355
nsresult
6356
HTMLEditRules::AlignInnerBlocks(nsINode& aNode,
6357
                                const nsAString& aAlignType)
6358
0
{
6359
0
  MOZ_ASSERT(IsEditorDataAvailable());
6360
0
6361
0
  // Gather list of table cells or list items
6362
0
  nsTArray<OwningNonNull<nsINode>> nodeArray;
6363
0
  TableCellAndListItemFunctor functor;
6364
0
  DOMIterator iter(aNode);
6365
0
  iter.AppendList(functor, nodeArray);
6366
0
6367
0
  // Now that we have the list, align their contents as requested
6368
0
  for (auto& node : nodeArray) {
6369
0
    nsresult rv = AlignBlockContents(*node, aAlignType);
6370
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6371
0
      return rv;
6372
0
    }
6373
0
  }
6374
0
6375
0
  return NS_OK;
6376
0
}
6377
6378
6379
nsresult
6380
HTMLEditRules::AlignBlockContents(nsINode& aNode,
6381
                                  const nsAString& aAlignType)
6382
0
{
6383
0
  MOZ_ASSERT(IsEditorDataAvailable());
6384
0
6385
0
  nsCOMPtr<nsIContent> firstChild =
6386
0
    HTMLEditorRef().GetFirstEditableChild(aNode);
6387
0
  if (!firstChild) {
6388
0
    // this cell has no content, nothing to align
6389
0
    return NS_OK;
6390
0
  }
6391
0
6392
0
  nsCOMPtr<nsIContent> lastChild = HTMLEditorRef().GetLastEditableChild(aNode);
6393
0
  if (firstChild == lastChild && firstChild->IsHTMLElement(nsGkAtoms::div)) {
6394
0
    // the cell already has a div containing all of its content: just
6395
0
    // act on this div.
6396
0
    nsresult rv =
6397
0
      HTMLEditorRef().SetAttributeOrEquivalent(firstChild->AsElement(),
6398
0
                                               nsGkAtoms::align,
6399
0
                                               aAlignType, false);
6400
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
6401
0
      return NS_ERROR_EDITOR_DESTROYED;
6402
0
    }
6403
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6404
0
      return rv;
6405
0
    }
6406
0
    return NS_OK;
6407
0
  }
6408
0
6409
0
  // else we need to put in a div, set the alignment, and toss in all the
6410
0
  // children
6411
0
  EditorRawDOMPoint atStartOfNode(&aNode, 0);
6412
0
  RefPtr<Element> divElem =
6413
0
    HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div, atStartOfNode);
6414
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
6415
0
    return NS_ERROR_EDITOR_DESTROYED;
6416
0
  }
6417
0
  if (NS_WARN_IF(!divElem)) {
6418
0
    return NS_ERROR_FAILURE;
6419
0
  }
6420
0
  // set up the alignment on the div
6421
0
  nsresult rv =
6422
0
    HTMLEditorRef().SetAttributeOrEquivalent(divElem, nsGkAtoms::align,
6423
0
                                             aAlignType, false);
6424
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
6425
0
    return NS_ERROR_EDITOR_DESTROYED;
6426
0
  }
6427
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
6428
0
    return rv;
6429
0
  }
6430
0
  // tuck the children into the end of the active div
6431
0
  while (lastChild && (lastChild != divElem)) {
6432
0
    nsresult rv =
6433
0
      HTMLEditorRef().MoveNodeWithTransaction(*lastChild,
6434
0
                                              EditorRawDOMPoint(divElem, 0));
6435
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
6436
0
      return NS_ERROR_EDITOR_DESTROYED;
6437
0
    }
6438
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6439
0
      return rv;
6440
0
    }
6441
0
    lastChild = HTMLEditorRef().GetLastEditableChild(aNode);
6442
0
  }
6443
0
  return NS_OK;
6444
0
}
6445
6446
nsresult
6447
HTMLEditRules::MaybeDeleteTopMostEmptyAncestor(nsINode& aStartNode,
6448
                                               Element& aEditingHostElement,
6449
                                               nsIEditor::EDirection aAction,
6450
                                               bool* aHandled)
6451
0
{
6452
0
  MOZ_ASSERT(IsEditorDataAvailable());
6453
0
6454
0
  // If the editing host is an inline element, bail out early.
6455
0
  if (IsInlineNode(aEditingHostElement)) {
6456
0
    return NS_OK;
6457
0
  }
6458
0
6459
0
  // If we are inside an empty block, delete it.  Note: do NOT delete table
6460
0
  // elements this way.
6461
0
  RefPtr<Element> block = HTMLEditorRef().GetBlock(aStartNode);
6462
0
  RefPtr<Element> emptyBlock;
6463
0
  if (block && block != &aEditingHostElement) {
6464
0
    // Efficiency hack, avoiding IsEmptyNode() call when in body
6465
0
    bool isEmptyNode = false;
6466
0
    nsresult rv =
6467
0
      HTMLEditorRef().IsEmptyNode(block, &isEmptyNode, true, false);
6468
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6469
0
      return rv;
6470
0
    }
6471
0
    while (block && isEmptyNode && !HTMLEditUtils::IsTableElement(block) &&
6472
0
           block != &aEditingHostElement) {
6473
0
      emptyBlock = block;
6474
0
      block = HTMLEditorRef().GetBlockNodeParent(emptyBlock);
6475
0
      if (block) {
6476
0
        rv = HTMLEditorRef().IsEmptyNode(block, &isEmptyNode, true, false);
6477
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
6478
0
          return rv;
6479
0
        }
6480
0
      }
6481
0
    }
6482
0
  }
6483
0
6484
0
  if (!emptyBlock || !emptyBlock->IsEditable()) {
6485
0
    return NS_OK;
6486
0
  }
6487
0
6488
0
  nsCOMPtr<nsINode> blockParent = emptyBlock->GetParentNode();
6489
0
  if (NS_WARN_IF(!blockParent)) {
6490
0
    return NS_ERROR_FAILURE;
6491
0
  }
6492
0
6493
0
  if (HTMLEditUtils::IsListItem(emptyBlock)) {
6494
0
    // If the found empty block is a list item element and its grand parent
6495
0
    // (i.e., parent of list element) is NOT a list element, insert <br>
6496
0
    // element before the list element which has the empty list item.
6497
0
    // XXX Typically, list element shouldn't have another list element.
6498
0
    //     So, what's the purpose of this block?
6499
0
    if (HTMLEditorRef().IsFirstEditableChild(emptyBlock)) {
6500
0
      EditorDOMPoint atBlockParent(blockParent);
6501
0
      if (NS_WARN_IF(!atBlockParent.IsSet())) {
6502
0
        return NS_ERROR_FAILURE;
6503
0
      }
6504
0
      // If the grand parent IS a list element, we'll adjust Selection in
6505
0
      // AfterEdit().
6506
0
      if (!HTMLEditUtils::IsList(atBlockParent.GetContainer())) {
6507
0
        RefPtr<Element> brElement =
6508
0
          HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
6509
0
                                                         atBlockParent);
6510
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
6511
0
          return NS_ERROR_EDITOR_DESTROYED;
6512
0
        }
6513
0
        if (NS_WARN_IF(!brElement)) {
6514
0
          return NS_ERROR_FAILURE;
6515
0
        }
6516
0
        ErrorResult error;
6517
0
        SelectionRef().Collapse(EditorRawDOMPoint(brElement), error);
6518
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
6519
0
          error.SuppressException();
6520
0
          return NS_ERROR_EDITOR_DESTROYED;
6521
0
        }
6522
0
        if (NS_WARN_IF(error.Failed())) {
6523
0
          return error.StealNSResult();
6524
0
        }
6525
0
      }
6526
0
    }
6527
0
  } else {
6528
0
    switch (aAction) {
6529
0
      case nsIEditor::eNext:
6530
0
      case nsIEditor::eNextWord:
6531
0
      case nsIEditor::eToEndOfLine: {
6532
0
        // Collapse Selection to next node of after empty block element
6533
0
        // if there is.  Otherwise, to just after the empty block.
6534
0
        EditorRawDOMPoint afterEmptyBlock(emptyBlock);
6535
0
        bool advancedFromEmptyBlock = afterEmptyBlock.AdvanceOffset();
6536
0
        NS_WARNING_ASSERTION(advancedFromEmptyBlock,
6537
0
          "Failed to set selection to the after the empty block");
6538
0
        nsCOMPtr<nsIContent> nextNode =
6539
0
          HTMLEditorRef().GetNextNode(afterEmptyBlock);
6540
0
        if (nextNode) {
6541
0
          EditorDOMPoint pt = GetGoodSelPointForNode(*nextNode, aAction);
6542
0
          ErrorResult error;
6543
0
          SelectionRef().Collapse(pt, error);
6544
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
6545
0
            error.SuppressException();
6546
0
            return NS_ERROR_EDITOR_DESTROYED;
6547
0
          }
6548
0
          if (NS_WARN_IF(error.Failed())) {
6549
0
            return error.StealNSResult();
6550
0
          }
6551
0
          break;
6552
0
        }
6553
0
        if (NS_WARN_IF(!advancedFromEmptyBlock)) {
6554
0
          return NS_ERROR_FAILURE;
6555
0
        }
6556
0
        ErrorResult error;
6557
0
        SelectionRef().Collapse(afterEmptyBlock, error);
6558
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
6559
0
          error.SuppressException();
6560
0
          return NS_ERROR_EDITOR_DESTROYED;
6561
0
        }
6562
0
        if (NS_WARN_IF(error.Failed())) {
6563
0
          return error.StealNSResult();
6564
0
        }
6565
0
        break;
6566
0
      }
6567
0
      case nsIEditor::ePrevious:
6568
0
      case nsIEditor::ePreviousWord:
6569
0
      case nsIEditor::eToBeginningOfLine: {
6570
0
        // Collapse Selection to previous editable node of the empty block
6571
0
        // if there is.  Otherwise, to after the empty block.
6572
0
        EditorRawDOMPoint atEmptyBlock(emptyBlock);
6573
0
        nsCOMPtr<nsIContent> priorNode =
6574
0
          HTMLEditorRef().GetPreviousEditableNode(atEmptyBlock);
6575
0
        if (priorNode) {
6576
0
          EditorDOMPoint pt = GetGoodSelPointForNode(*priorNode, aAction);
6577
0
          ErrorResult error;
6578
0
          SelectionRef().Collapse(pt, error);
6579
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
6580
0
            error.SuppressException();
6581
0
            return NS_ERROR_EDITOR_DESTROYED;
6582
0
          }
6583
0
          if (NS_WARN_IF(error.Failed())) {
6584
0
            return error.StealNSResult();
6585
0
          }
6586
0
          break;
6587
0
        }
6588
0
        EditorRawDOMPoint afterEmptyBlock(emptyBlock);
6589
0
        if (NS_WARN_IF(!afterEmptyBlock.AdvanceOffset())) {
6590
0
          return NS_ERROR_FAILURE;
6591
0
        }
6592
0
        ErrorResult error;
6593
0
        SelectionRef().Collapse(afterEmptyBlock, error);
6594
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
6595
0
          error.SuppressException();
6596
0
          return NS_ERROR_EDITOR_DESTROYED;
6597
0
        }
6598
0
        if (NS_WARN_IF(error.Failed())) {
6599
0
          return error.StealNSResult();
6600
0
        }
6601
0
        break;
6602
0
      }
6603
0
      case nsIEditor::eNone:
6604
0
        break;
6605
0
      default:
6606
0
        MOZ_CRASH("CheckForEmptyBlock doesn't support this action yet");
6607
0
    }
6608
0
  }
6609
0
  *aHandled = true;
6610
0
  nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(*emptyBlock);
6611
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
6612
0
    return NS_ERROR_EDITOR_DESTROYED;
6613
0
  }
6614
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
6615
0
    return rv;
6616
0
  }
6617
0
  return NS_OK;
6618
0
}
6619
6620
Element*
6621
HTMLEditRules::CheckForInvisibleBR(Element& aBlock,
6622
                                   BRLocation aWhere,
6623
                                   int32_t aOffset)
6624
0
{
6625
0
  MOZ_ASSERT(IsEditorDataAvailable());
6626
0
6627
0
  nsCOMPtr<nsINode> testNode;
6628
0
  int32_t testOffset = 0;
6629
0
6630
0
  if (aWhere == BRLocation::blockEnd) {
6631
0
    // No block crossing
6632
0
    nsCOMPtr<nsIContent> rightmostNode =
6633
0
      HTMLEditorRef().GetRightmostChild(&aBlock, true);
6634
0
6635
0
    if (!rightmostNode) {
6636
0
      return nullptr;
6637
0
    }
6638
0
6639
0
    testNode = rightmostNode->GetParentNode();
6640
0
    // Since rightmostNode is always the last child, its index is equal to the
6641
0
    // child count, so instead of ComputeIndexOf() we use the faster
6642
0
    // GetChildCount(), and assert the equivalence below.
6643
0
    testOffset = testNode->GetChildCount();
6644
0
6645
0
    // Use offset + 1, so last node is included in our evaluation
6646
0
    MOZ_ASSERT(testNode->ComputeIndexOf(rightmostNode) + 1 == testOffset);
6647
0
  } else if (aOffset) {
6648
0
    testNode = &aBlock;
6649
0
    // We'll check everything to the left of the input position
6650
0
    testOffset = aOffset;
6651
0
  } else {
6652
0
    return nullptr;
6653
0
  }
6654
0
6655
0
  WSRunObject wsTester(&HTMLEditorRef(), testNode, testOffset);
6656
0
  if (WSType::br == wsTester.mStartReason) {
6657
0
    return wsTester.mStartReasonNode->AsElement();
6658
0
  }
6659
0
6660
0
  return nullptr;
6661
0
}
6662
6663
void
6664
HTMLEditRules::GetInnerContent(
6665
                 nsINode& aNode,
6666
                 nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
6667
                 int32_t* aIndex,
6668
                 Lists aLists,
6669
                 Tables aTables)
6670
0
{
6671
0
  MOZ_ASSERT(IsEditorDataAvailable());
6672
0
  MOZ_ASSERT(aIndex);
6673
0
6674
0
  for (nsCOMPtr<nsIContent> node = HTMLEditorRef().GetFirstEditableChild(aNode);
6675
0
       node; node = node->GetNextSibling()) {
6676
0
    if ((aLists == Lists::yes && (HTMLEditUtils::IsList(node) ||
6677
0
                                  HTMLEditUtils::IsListItem(node))) ||
6678
0
        (aTables == Tables::yes && HTMLEditUtils::IsTableElement(node))) {
6679
0
      GetInnerContent(*node, aOutArrayOfNodes, aIndex, aLists, aTables);
6680
0
    } else {
6681
0
      aOutArrayOfNodes.InsertElementAt(*aIndex, *node);
6682
0
      (*aIndex)++;
6683
0
    }
6684
0
  }
6685
0
}
6686
6687
nsresult
6688
HTMLEditRules::ExpandSelectionForDeletion()
6689
0
{
6690
0
  MOZ_ASSERT(IsEditorDataAvailable());
6691
0
6692
0
  // Don't need to touch collapsed selections
6693
0
  if (SelectionRef().IsCollapsed()) {
6694
0
    return NS_OK;
6695
0
  }
6696
0
6697
0
  // We don't need to mess with cell selections, and we assume multirange
6698
0
  // selections are those.
6699
0
  if (SelectionRef().RangeCount() != 1) {
6700
0
    return NS_OK;
6701
0
  }
6702
0
6703
0
  // Find current sel start and end
6704
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
6705
0
  if (NS_WARN_IF(!firstRange)) {
6706
0
    return NS_ERROR_FAILURE;
6707
0
  }
6708
0
6709
0
  nsCOMPtr<nsINode> selStartNode = firstRange->GetStartContainer();
6710
0
  int32_t selStartOffset = firstRange->StartOffset();
6711
0
  nsCOMPtr<nsINode> selEndNode = firstRange->GetEndContainer();
6712
0
  int32_t selEndOffset = firstRange->EndOffset();
6713
0
6714
0
  // Find current selection common block parent
6715
0
  RefPtr<Element> selCommon =
6716
0
    HTMLEditor::GetBlock(*firstRange->GetCommonAncestor());
6717
0
  if (NS_WARN_IF(!selCommon)) {
6718
0
    return NS_ERROR_FAILURE;
6719
0
  }
6720
0
6721
0
  // Set up for loops and cache our root element
6722
0
  nsCOMPtr<nsINode> firstBRParent;
6723
0
  int32_t firstBROffset = 0;
6724
0
  WSType wsType;
6725
0
  RefPtr<Element> root = HTMLEditorRef().GetActiveEditingHost();
6726
0
  if (NS_WARN_IF(!root)) {
6727
0
    return NS_ERROR_FAILURE;
6728
0
  }
6729
0
6730
0
  // Find previous visible things before start of selection
6731
0
  if (selStartNode != selCommon && selStartNode != root) {
6732
0
    while (true) {
6733
0
      WSRunObject wsObj(&HTMLEditorRef(), selStartNode, selStartOffset);
6734
0
      wsObj.PriorVisibleNode(EditorRawDOMPoint(selStartNode, selStartOffset),
6735
0
                             &wsType);
6736
0
      if (wsType != WSType::thisBlock) {
6737
0
        break;
6738
0
      }
6739
0
      // We want to keep looking up.  But stop if we are crossing table
6740
0
      // element boundaries, or if we hit the root.
6741
0
      if (HTMLEditUtils::IsTableElement(wsObj.mStartReasonNode) ||
6742
0
          selCommon == wsObj.mStartReasonNode ||
6743
0
          root == wsObj.mStartReasonNode) {
6744
0
        break;
6745
0
      }
6746
0
      selStartNode = wsObj.mStartReasonNode->GetParentNode();
6747
0
      selStartOffset = selStartNode ?
6748
0
        selStartNode->ComputeIndexOf(wsObj.mStartReasonNode) : -1;
6749
0
    }
6750
0
  }
6751
0
6752
0
  // Find next visible things after end of selection
6753
0
  if (selEndNode != selCommon && selEndNode != root) {
6754
0
    for (;;) {
6755
0
      WSRunObject wsObj(&HTMLEditorRef(), selEndNode, selEndOffset);
6756
0
      wsObj.NextVisibleNode(EditorRawDOMPoint(selEndNode, selEndOffset),
6757
0
                            &wsType);
6758
0
      if (wsType == WSType::br) {
6759
0
        if (HTMLEditorRef().IsVisibleBRElement(wsObj.mEndReasonNode)) {
6760
0
          break;
6761
0
        }
6762
0
        if (!firstBRParent) {
6763
0
          firstBRParent = selEndNode;
6764
0
          firstBROffset = selEndOffset;
6765
0
        }
6766
0
        selEndNode = wsObj.mEndReasonNode->GetParentNode();
6767
0
        selEndOffset = selEndNode
6768
0
          ? selEndNode->ComputeIndexOf(wsObj.mEndReasonNode) + 1 : 0;
6769
0
      } else if (wsType == WSType::thisBlock) {
6770
0
        // We want to keep looking up.  But stop if we are crossing table
6771
0
        // element boundaries, or if we hit the root.
6772
0
        if (HTMLEditUtils::IsTableElement(wsObj.mEndReasonNode) ||
6773
0
            selCommon == wsObj.mEndReasonNode ||
6774
0
            root == wsObj.mEndReasonNode) {
6775
0
          break;
6776
0
        }
6777
0
        selEndNode = wsObj.mEndReasonNode->GetParentNode();
6778
0
        selEndOffset = 1 + selEndNode->ComputeIndexOf(wsObj.mEndReasonNode);
6779
0
      } else {
6780
0
        break;
6781
0
      }
6782
0
    }
6783
0
  }
6784
0
  // Now set the selection to the new range
6785
0
  DebugOnly<nsresult> rv =
6786
0
    SelectionRef().Collapse(selStartNode, selStartOffset);
6787
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
6788
0
    return NS_ERROR_EDITOR_DESTROYED;
6789
0
  }
6790
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to collapse selection");
6791
0
6792
0
  // Expand selection endpoint only if we didn't pass a <br>, or if we really
6793
0
  // needed to pass that <br> (i.e., its block is now totally selected).
6794
0
  bool doEndExpansion = true;
6795
0
  if (firstBRParent) {
6796
0
    // Find block node containing <br>.
6797
0
    nsCOMPtr<Element> brBlock = HTMLEditor::GetBlock(*firstBRParent);
6798
0
    bool nodeBefore = false, nodeAfter = false;
6799
0
6800
0
    // Create a range that represents expanded selection
6801
0
    RefPtr<nsRange> range = new nsRange(selStartNode);
6802
0
    nsresult rv = range->SetStartAndEnd(selStartNode, selStartOffset,
6803
0
                                        selEndNode, selEndOffset);
6804
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6805
0
      return rv;
6806
0
    }
6807
0
6808
0
    // Check if block is entirely inside range
6809
0
    if (brBlock) {
6810
0
      nsRange::CompareNodeToRange(brBlock, range, &nodeBefore, &nodeAfter);
6811
0
    }
6812
0
6813
0
    // If block isn't contained, forgo grabbing the <br> in expanded selection.
6814
0
    if (nodeBefore || nodeAfter) {
6815
0
      doEndExpansion = false;
6816
0
    }
6817
0
  }
6818
0
  if (doEndExpansion) {
6819
0
    nsresult rv = SelectionRef().Extend(selEndNode, selEndOffset);
6820
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
6821
0
      return NS_ERROR_EDITOR_DESTROYED;
6822
0
    }
6823
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6824
0
      return rv;
6825
0
    }
6826
0
  } else {
6827
0
    // Only expand to just before <br>.
6828
0
    nsresult rv = SelectionRef().Extend(firstBRParent, firstBROffset);
6829
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
6830
0
      return NS_ERROR_EDITOR_DESTROYED;
6831
0
    }
6832
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
6833
0
      return rv;
6834
0
    }
6835
0
  }
6836
0
6837
0
  return NS_OK;
6838
0
}
6839
6840
nsresult
6841
HTMLEditRules::NormalizeSelection()
6842
0
{
6843
0
  MOZ_ASSERT(IsEditorDataAvailable());
6844
0
6845
0
  // NormalizeSelection() tweaks non-collapsed selections to be more "natural".
6846
0
  // Idea here is to adjust selection endpoint so that they do not cross breaks
6847
0
  // or block boundaries unless something editable beyond that boundary is also
6848
0
  // selected.  This adjustment makes it much easier for the various block
6849
0
  // operations to determine what nodes to act on.
6850
0
6851
0
  // don't need to touch collapsed selections
6852
0
  if (SelectionRef().IsCollapsed()) {
6853
0
    return NS_OK;
6854
0
  }
6855
0
6856
0
  // We don't need to mess with cell selections, and we assume multirange
6857
0
  // selections are those.
6858
0
  // XXX Why?  Even in <input>, user can select 2 or more ranges.
6859
0
  if (SelectionRef().RangeCount() != 1) {
6860
0
    return NS_OK;
6861
0
  }
6862
0
6863
0
  RefPtr<nsRange> range = SelectionRef().GetRangeAt(0);
6864
0
  if (NS_WARN_IF(!range)) {
6865
0
    return NS_ERROR_FAILURE;
6866
0
  }
6867
0
6868
0
  nsCOMPtr<nsINode> startNode = range->GetStartContainer();
6869
0
  if (NS_WARN_IF(!startNode)) {
6870
0
    return NS_ERROR_FAILURE;
6871
0
  }
6872
0
  nsCOMPtr<nsINode> endNode = range->GetEndContainer();
6873
0
  if (NS_WARN_IF(!endNode)) {
6874
0
    return NS_ERROR_FAILURE;
6875
0
  }
6876
0
  nsIContent* startChild = range->GetChildAtStartOffset();
6877
0
  nsIContent* endChild = range->GetChildAtEndOffset();
6878
0
  uint32_t startOffset = range->StartOffset();
6879
0
  uint32_t endOffset = range->EndOffset();
6880
0
6881
0
  // adjusted values default to original values
6882
0
  nsCOMPtr<nsINode> newStartNode = startNode;
6883
0
  uint32_t newStartOffset = startOffset;
6884
0
  nsCOMPtr<nsINode> newEndNode = endNode;
6885
0
  uint32_t newEndOffset = endOffset;
6886
0
6887
0
  // some locals we need for whitespace code
6888
0
  WSType wsType;
6889
0
6890
0
  // let the whitespace code do the heavy lifting
6891
0
  WSRunObject wsEndObj(&HTMLEditorRef(), endNode,
6892
0
                       static_cast<int32_t>(endOffset));
6893
0
  // Is there any intervening visible whitespace?  If so we can't push
6894
0
  // selection past that, it would visibly change meaning of users selection.
6895
0
  wsEndObj.PriorVisibleNode(EditorRawDOMPoint(endNode, endOffset), &wsType);
6896
0
  if (wsType != WSType::text && wsType != WSType::normalWS) {
6897
0
    // eThisBlock and eOtherBlock conveniently distinguish cases
6898
0
    // of going "down" into a block and "up" out of a block.
6899
0
    if (wsEndObj.mStartReason == WSType::otherBlock) {
6900
0
      // endpoint is just after the close of a block.
6901
0
      nsINode* child =
6902
0
        HTMLEditorRef().GetRightmostChild(wsEndObj.mStartReasonNode, true);
6903
0
      if (child) {
6904
0
        int32_t offset = -1;
6905
0
        newEndNode = EditorBase::GetNodeLocation(child, &offset);
6906
0
        // offset *after* child
6907
0
        newEndOffset = static_cast<uint32_t>(offset + 1);
6908
0
      }
6909
0
      // else block is empty - we can leave selection alone here, i think.
6910
0
    } else if (wsEndObj.mStartReason == WSType::thisBlock) {
6911
0
      // endpoint is just after start of this block
6912
0
      EditorRawDOMPoint atEnd(endNode, endChild, endOffset);
6913
0
      nsINode* child = HTMLEditorRef().GetPreviousEditableHTMLNode(atEnd);
6914
0
      if (child) {
6915
0
        int32_t offset = -1;
6916
0
        newEndNode = EditorBase::GetNodeLocation(child, &offset);
6917
0
        // offset *after* child
6918
0
        newEndOffset = static_cast<uint32_t>(offset + 1);
6919
0
      }
6920
0
      // else block is empty - we can leave selection alone here, i think.
6921
0
    } else if (wsEndObj.mStartReason == WSType::br) {
6922
0
      // endpoint is just after break.  lets adjust it to before it.
6923
0
      int32_t offset = -1;
6924
0
      newEndNode =
6925
0
        EditorBase::GetNodeLocation(wsEndObj.mStartReasonNode, &offset);
6926
0
      newEndOffset = static_cast<uint32_t>(offset);;
6927
0
    }
6928
0
  }
6929
0
6930
0
6931
0
  // similar dealio for start of range
6932
0
  WSRunObject wsStartObj(&HTMLEditorRef(), startNode,
6933
0
                         static_cast<int32_t>(startOffset));
6934
0
  // Is there any intervening visible whitespace?  If so we can't push
6935
0
  // selection past that, it would visibly change meaning of users selection.
6936
0
  wsStartObj.NextVisibleNode(EditorRawDOMPoint(startNode, startOffset),
6937
0
                             &wsType);
6938
0
  if (wsType != WSType::text && wsType != WSType::normalWS) {
6939
0
    // eThisBlock and eOtherBlock conveniently distinguish cases
6940
0
    // of going "down" into a block and "up" out of a block.
6941
0
    if (wsStartObj.mEndReason == WSType::otherBlock) {
6942
0
      // startpoint is just before the start of a block.
6943
0
      nsINode* child =
6944
0
        HTMLEditorRef().GetLeftmostChild(wsStartObj.mEndReasonNode, true);
6945
0
      if (child) {
6946
0
        int32_t offset = -1;
6947
0
        newStartNode = EditorBase::GetNodeLocation(child, &offset);
6948
0
        newStartOffset = static_cast<uint32_t>(offset);
6949
0
      }
6950
0
      // else block is empty - we can leave selection alone here, i think.
6951
0
    } else if (wsStartObj.mEndReason == WSType::thisBlock) {
6952
0
      // startpoint is just before end of this block
6953
0
      nsINode* child =
6954
0
        HTMLEditorRef().GetNextEditableHTMLNode(
6955
0
                          EditorRawDOMPoint(startNode,
6956
0
                                            startChild, startOffset));
6957
0
      if (child) {
6958
0
        int32_t offset = -1;
6959
0
        newStartNode = EditorBase::GetNodeLocation(child, &offset);
6960
0
        newStartOffset = static_cast<uint32_t>(offset);
6961
0
      }
6962
0
      // else block is empty - we can leave selection alone here, i think.
6963
0
    } else if (wsStartObj.mEndReason == WSType::br) {
6964
0
      // startpoint is just before a break.  lets adjust it to after it.
6965
0
      int32_t offset = -1;
6966
0
      newStartNode =
6967
0
        EditorBase::GetNodeLocation(wsStartObj.mEndReasonNode, &offset);
6968
0
      // offset *after* break
6969
0
      newStartOffset = static_cast<uint32_t>(offset + 1);
6970
0
    }
6971
0
  }
6972
0
6973
0
  // There is a demented possiblity we have to check for.  We might have a very
6974
0
  // strange selection that is not collapsed and yet does not contain any
6975
0
  // editable content, and satisfies some of the above conditions that cause
6976
0
  // tweaking.  In this case we don't want to tweak the selection into a block
6977
0
  // it was never in, etc.  There are a variety of strategies one might use to
6978
0
  // try to detect these cases, but I think the most straightforward is to see
6979
0
  // if the adjusted locations "cross" the old values: i.e., new end before old
6980
0
  // start, or new start after old end.  If so then just leave things alone.
6981
0
6982
0
  int16_t comp;
6983
0
  comp = nsContentUtils::ComparePoints(startNode, startOffset,
6984
0
                                       newEndNode, newEndOffset);
6985
0
  if (comp == 1) {
6986
0
    return NS_OK;  // New end before old start.
6987
0
  }
6988
0
  comp = nsContentUtils::ComparePoints(newStartNode, newStartOffset,
6989
0
                                       endNode, endOffset);
6990
0
  if (comp == 1) {
6991
0
    return NS_OK;  // New start after old end.
6992
0
  }
6993
0
6994
0
  // otherwise set selection to new values.
6995
0
  // XXX Why don't we use SetBaseAndExtent()?
6996
0
  DebugOnly<nsresult> rv =
6997
0
    SelectionRef().Collapse(newStartNode, newStartOffset);
6998
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
6999
0
    return NS_ERROR_EDITOR_DESTROYED;
7000
0
  }
7001
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
7002
0
    "Failed to collapse selection");
7003
0
  rv = SelectionRef().Extend(newEndNode, newEndOffset);
7004
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
7005
0
    return NS_ERROR_EDITOR_DESTROYED;
7006
0
  }
7007
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
7008
0
    "Failed to extend selection");
7009
0
  return NS_OK;
7010
0
}
7011
7012
EditorDOMPoint
7013
HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere,
7014
                                nsINode& aNode,
7015
                                int32_t aOffset,
7016
                                EditSubAction aEditSubAction)
7017
0
{
7018
0
  MOZ_ASSERT(IsEditorDataAvailable());
7019
0
7020
0
  // we do one thing for text actions, something else entirely for other
7021
0
  // actions
7022
0
  if (aEditSubAction == EditSubAction::eInsertText ||
7023
0
      aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
7024
0
      aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
7025
0
      aEditSubAction == EditSubAction::eDeleteText) {
7026
0
    bool isSpace, isNBSP;
7027
0
    nsCOMPtr<nsIContent> content =
7028
0
      aNode.IsContent() ? aNode.AsContent() : nullptr;
7029
0
    nsCOMPtr<nsIContent> temp;
7030
0
    int32_t newOffset = aOffset;
7031
0
    // for text actions, we want to look backwards (or forwards, as
7032
0
    // appropriate) for additional whitespace or nbsp's.  We may have to act on
7033
0
    // these later even though they are outside of the initial selection.  Even
7034
0
    // if they are in another node!
7035
0
    while (content) {
7036
0
      int32_t offset;
7037
0
      if (aWhere == kStart) {
7038
0
        HTMLEditorRef().IsPrevCharInNodeWhitespace(content, newOffset,
7039
0
                                                   &isSpace, &isNBSP,
7040
0
                                                   getter_AddRefs(temp),
7041
0
                                                   &offset);
7042
0
      } else {
7043
0
        HTMLEditorRef().IsNextCharInNodeWhitespace(content, newOffset,
7044
0
                                                   &isSpace, &isNBSP,
7045
0
                                                   getter_AddRefs(temp),
7046
0
                                                   &offset);
7047
0
      }
7048
0
      if (isSpace || isNBSP) {
7049
0
        content = temp;
7050
0
        newOffset = offset;
7051
0
      } else {
7052
0
        break;
7053
0
      }
7054
0
    }
7055
0
7056
0
    return EditorDOMPoint(content, newOffset);
7057
0
  }
7058
0
7059
0
  EditorDOMPoint point(&aNode, aOffset);
7060
0
7061
0
  // else not a text section.  In this case we want to see if we should grab
7062
0
  // any adjacent inline nodes and/or parents and other ancestors
7063
0
  if (aWhere == kStart) {
7064
0
    // some special casing for text nodes
7065
0
    if (point.IsInTextNode()) {
7066
0
      if (!point.GetContainer()->GetParentNode()) {
7067
0
        // Okay, can't promote any further
7068
0
        return point;
7069
0
      }
7070
0
      point.Set(point.GetContainer());
7071
0
    }
7072
0
7073
0
    // look back through any further inline nodes that aren't across a <br>
7074
0
    // from us, and that are enclosed in the same block.
7075
0
    nsCOMPtr<nsINode> priorNode =
7076
0
      HTMLEditorRef().GetPreviousEditableHTMLNodeInBlock(point);
7077
0
7078
0
    while (priorNode && priorNode->GetParentNode() &&
7079
0
           !HTMLEditorRef().IsVisibleBRElement(priorNode) &&
7080
0
           !IsBlockNode(*priorNode)) {
7081
0
      point.Set(priorNode);
7082
0
      priorNode = HTMLEditorRef().GetPreviousEditableHTMLNodeInBlock(point);
7083
0
    }
7084
0
7085
0
    // finding the real start for this point.  look up the tree for as long as
7086
0
    // we are the first node in the container, and as long as we haven't hit
7087
0
    // the body node.
7088
0
    nsCOMPtr<nsIContent> nearNode =
7089
0
      HTMLEditorRef().GetPreviousEditableHTMLNodeInBlock(point);
7090
0
    while (!nearNode &&
7091
0
           !point.IsContainerHTMLElement(nsGkAtoms::body) &&
7092
0
           point.GetContainer()->GetParentNode()) {
7093
0
      // some cutoffs are here: we don't need to also include them in the
7094
0
      // aWhere == kEnd case.  as long as they are in one or the other it will
7095
0
      // work.  special case for outdent: don't keep looking up if we have
7096
0
      // found a blockquote element to act on
7097
0
      if (aEditSubAction == EditSubAction::eOutdent &&
7098
0
          point.IsContainerHTMLElement(nsGkAtoms::blockquote)) {
7099
0
        break;
7100
0
      }
7101
0
7102
0
      // Don't walk past the editable section. Note that we need to check
7103
0
      // before walking up to a parent because we need to return the parent
7104
0
      // object, so the parent itself might not be in the editable area, but
7105
0
      // it's OK if we're not performing a block-level action.
7106
0
      bool blockLevelAction =
7107
0
        aEditSubAction == EditSubAction::eIndent ||
7108
0
        aEditSubAction == EditSubAction::eOutdent ||
7109
0
        aEditSubAction == EditSubAction::eSetOrClearAlignment ||
7110
0
        aEditSubAction == EditSubAction::eCreateOrRemoveBlock;
7111
0
      if (!HTMLEditorRef().IsDescendantOfEditorRoot(
7112
0
                             point.GetContainer()->GetParentNode()) &&
7113
0
          (blockLevelAction ||
7114
0
           !HTMLEditorRef().IsDescendantOfEditorRoot(point.GetContainer()))) {
7115
0
        break;
7116
0
      }
7117
0
7118
0
      point.Set(point.GetContainer());
7119
0
      nearNode = HTMLEditorRef().GetPreviousEditableHTMLNodeInBlock(point);
7120
0
    }
7121
0
    return point;
7122
0
  }
7123
0
7124
0
  // aWhere == kEnd
7125
0
  // some special casing for text nodes
7126
0
  if (point.IsInTextNode()) {
7127
0
    if (!point.GetContainer()->GetParentNode()) {
7128
0
      // Okay, can't promote any further
7129
0
      return point;
7130
0
    }
7131
0
    // want to be after the text node
7132
0
    point.Set(point.GetContainer());
7133
0
    DebugOnly<bool> advanced = point.AdvanceOffset();
7134
0
    NS_WARNING_ASSERTION(advanced,
7135
0
      "Failed to advance offset to after the text node");
7136
0
  }
7137
0
7138
0
  // look ahead through any further inline nodes that aren't across a <br> from
7139
0
  // us, and that are enclosed in the same block.
7140
0
  // XXX Currently, we stop block-extending when finding visible <br> element.
7141
0
  //     This might be different from "block-extend" of execCommand spec.
7142
0
  //     However, the spec is really unclear.
7143
0
  // XXX Probably, scanning only editable nodes is wrong for
7144
0
  //     EditSubAction::eCreateOrRemoveBlock because it might be better to wrap
7145
0
  //     existing inline elements even if it's non-editable.  For example,
7146
0
  //     following examples with insertParagraph causes different result:
7147
0
  //     * <div contenteditable>foo[]<b contenteditable="false">bar</b></div>
7148
0
  //     * <div contenteditable>foo[]<b>bar</b></div>
7149
0
  //     * <div contenteditable>foo[]<b contenteditable="false">bar</b>baz</div>
7150
0
  //     Only in the first case, after the caret position isn't wrapped with
7151
0
  //     new <div> element.
7152
0
  nsCOMPtr<nsIContent> nextNode =
7153
0
    HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
7154
0
7155
0
  while (nextNode && !IsBlockNode(*nextNode) && nextNode->GetParentNode()) {
7156
0
    point.Set(nextNode);
7157
0
    if (NS_WARN_IF(!point.AdvanceOffset())) {
7158
0
      break;
7159
0
    }
7160
0
    if (HTMLEditorRef().IsVisibleBRElement(nextNode)) {
7161
0
      break;
7162
0
    }
7163
0
7164
0
    // Check for newlines in pre-formatted text nodes.
7165
0
    if (EditorBase::IsPreformatted(nextNode) &&
7166
0
        EditorBase::IsTextNode(nextNode)) {
7167
0
      nsAutoString tempString;
7168
0
      nextNode->GetAsText()->GetData(tempString);
7169
0
      int32_t newlinePos = tempString.FindChar(nsCRT::LF);
7170
0
      if (newlinePos >= 0) {
7171
0
        if (static_cast<uint32_t>(newlinePos) + 1 == tempString.Length()) {
7172
0
          // No need for special processing if the newline is at the end.
7173
0
          break;
7174
0
        }
7175
0
        return EditorDOMPoint(nextNode, newlinePos + 1);
7176
0
      }
7177
0
    }
7178
0
    nextNode = HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
7179
0
  }
7180
0
7181
0
  // finding the real end for this point.  look up the tree for as long as we
7182
0
  // are the last node in the container, and as long as we haven't hit the body
7183
0
  // node.
7184
0
  nsCOMPtr<nsIContent> nearNode =
7185
0
    HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
7186
0
  while (!nearNode &&
7187
0
         !point.IsContainerHTMLElement(nsGkAtoms::body) &&
7188
0
         point.GetContainer()->GetParentNode()) {
7189
0
    // Don't walk past the editable section. Note that we need to check before
7190
0
    // walking up to a parent because we need to return the parent object, so
7191
0
    // the parent itself might not be in the editable area, but it's OK.
7192
0
    if (!HTMLEditorRef().IsDescendantOfEditorRoot(point.GetContainer()) &&
7193
0
        !HTMLEditorRef().IsDescendantOfEditorRoot(
7194
0
                           point.GetContainer()->GetParentNode())) {
7195
0
      break;
7196
0
    }
7197
0
7198
0
    point.Set(point.GetContainer());
7199
0
    if (NS_WARN_IF(!point.AdvanceOffset())) {
7200
0
      break;
7201
0
    }
7202
0
    nearNode = HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
7203
0
  }
7204
0
  return point;
7205
0
}
7206
7207
void
7208
HTMLEditRules::GetPromotedRanges(nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
7209
                                 EditSubAction aEditSubAction)
7210
0
{
7211
0
  MOZ_ASSERT(IsEditorDataAvailable());
7212
0
7213
0
  uint32_t rangeCount = SelectionRef().RangeCount();
7214
0
  for (uint32_t i = 0; i < rangeCount; i++) {
7215
0
    RefPtr<nsRange> selectionRange = SelectionRef().GetRangeAt(i);
7216
0
    MOZ_ASSERT(selectionRange);
7217
0
7218
0
    // Clone range so we don't muck with actual selection ranges
7219
0
    RefPtr<nsRange> opRange = selectionRange->CloneRange();
7220
0
7221
0
    // Make a new adjusted range to represent the appropriate block content.
7222
0
    // The basic idea is to push out the range endpoints to truly enclose the
7223
0
    // blocks that we will affect.  This call alters opRange.
7224
0
    PromoteRange(*opRange, aEditSubAction);
7225
0
7226
0
    // Stuff new opRange into array
7227
0
    outArrayOfRanges.AppendElement(opRange);
7228
0
  }
7229
0
}
7230
7231
void
7232
HTMLEditRules::PromoteRange(nsRange& aRange,
7233
                            EditSubAction aEditSubAction)
7234
0
{
7235
0
  MOZ_ASSERT(IsEditorDataAvailable());
7236
0
  MOZ_ASSERT(!aRange.IsInSelection());
7237
0
7238
0
  if (!aRange.IsPositioned()) {
7239
0
    return;
7240
0
  }
7241
0
7242
0
  nsCOMPtr<nsINode> startNode = aRange.GetStartContainer();
7243
0
  nsCOMPtr<nsINode> endNode = aRange.GetEndContainer();
7244
0
  int32_t startOffset = aRange.StartOffset();
7245
0
  int32_t endOffset = aRange.EndOffset();
7246
0
7247
0
  // MOOSE major hack:
7248
0
  // GetPromotedPoint doesn't really do the right thing for collapsed ranges
7249
0
  // inside block elements that contain nothing but a solo <br>.  It's easier
7250
0
  // to put a workaround here than to revamp GetPromotedPoint.  :-(
7251
0
  if (startNode == endNode && startOffset == endOffset) {
7252
0
    RefPtr<Element> block = HTMLEditorRef().GetBlock(*startNode);
7253
0
    if (block) {
7254
0
      bool bIsEmptyNode = false;
7255
0
      nsIContent* host = HTMLEditorRef().GetActiveEditingHost();
7256
0
      if (NS_WARN_IF(!host)) {
7257
0
        return;
7258
0
      }
7259
0
      // Make sure we don't go higher than our root element in the content tree
7260
0
      if (!nsContentUtils::ContentIsDescendantOf(host, block)) {
7261
0
        HTMLEditorRef().IsEmptyNode(block, &bIsEmptyNode, true, false);
7262
0
      }
7263
0
      if (bIsEmptyNode) {
7264
0
        startNode = block;
7265
0
        endNode = block;
7266
0
        startOffset = 0;
7267
0
        endOffset = block->Length();
7268
0
      }
7269
0
    }
7270
0
  }
7271
0
7272
0
  if (aEditSubAction == EditSubAction::eInsertText ||
7273
0
      aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
7274
0
      aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
7275
0
      aEditSubAction == EditSubAction::eDeleteText) {
7276
0
     if (!startNode->IsContent() ||
7277
0
         !endNode->IsContent()) {
7278
0
       // GetPromotedPoint cannot promote node when action type is text
7279
0
       // operation and selected node isn't content node.
7280
0
       return;
7281
0
     }
7282
0
  }
7283
0
7284
0
  // Make a new adjusted range to represent the appropriate block content.
7285
0
  // This is tricky.  The basic idea is to push out the range endpoints to
7286
0
  // truly enclose the blocks that we will affect.
7287
0
7288
0
  // Make sure that the new range ends up to be in the editable section.
7289
0
  // XXX Looks like that this check wastes the time.  Perhaps, we should
7290
0
  //     implement a method which checks both two DOM points in the editor
7291
0
  //     root.
7292
0
  EditorDOMPoint startPoint =
7293
0
    GetPromotedPoint(kStart, *startNode, startOffset, aEditSubAction);
7294
0
  if (!HTMLEditorRef().IsDescendantOfEditorRoot(
7295
0
                         EditorBase::GetNodeAtRangeOffsetPoint(startPoint))) {
7296
0
    return;
7297
0
  }
7298
0
  EditorDOMPoint endPoint =
7299
0
    GetPromotedPoint(kEnd, *endNode, endOffset, aEditSubAction);
7300
0
  EditorRawDOMPoint lastRawPoint(endPoint);
7301
0
  lastRawPoint.RewindOffset();
7302
0
  if (!HTMLEditorRef().IsDescendantOfEditorRoot(
7303
0
                         EditorBase::GetNodeAtRangeOffsetPoint(lastRawPoint))) {
7304
0
    return;
7305
0
  }
7306
0
7307
0
  DebugOnly<nsresult> rv = aRange.SetStartAndEnd(startPoint, endPoint);
7308
0
  MOZ_ASSERT(NS_SUCCEEDED(rv));
7309
0
}
7310
7311
class UniqueFunctor final : public BoolDomIterFunctor
7312
{
7313
public:
7314
  explicit UniqueFunctor(nsTArray<OwningNonNull<nsINode>>& aArray)
7315
    : mArray(aArray)
7316
0
  {
7317
0
  }
7318
7319
  // Used to build list of all nodes iterator covers.
7320
  virtual bool operator()(nsINode* aNode) const override
7321
0
  {
7322
0
    return !mArray.Contains(aNode);
7323
0
  }
7324
7325
private:
7326
  nsTArray<OwningNonNull<nsINode>>& mArray;
7327
};
7328
7329
nsresult
7330
HTMLEditRules::GetNodesForOperation(
7331
                 nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
7332
                 nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
7333
                 EditSubAction aEditSubAction,
7334
                 TouchContent aTouchContent)
7335
0
{
7336
0
  MOZ_ASSERT(IsEditorDataAvailable());
7337
0
7338
0
  if (aTouchContent == TouchContent::yes) {
7339
0
    // Split text nodes. This is necessary, since GetPromotedPoint() may return a
7340
0
    // range ending in a text node in case where part of a pre-formatted
7341
0
    // elements needs to be moved.
7342
0
    for (RefPtr<nsRange>& range : aArrayOfRanges) {
7343
0
      EditorDOMPoint atEnd(range->EndRef());
7344
0
      if (NS_WARN_IF(!atEnd.IsSet()) || !atEnd.IsInTextNode()) {
7345
0
        continue;
7346
0
      }
7347
0
7348
0
      if (!atEnd.IsStartOfContainer() && !atEnd.IsEndOfContainer()) {
7349
0
        // Split the text node.
7350
0
        ErrorResult error;
7351
0
        nsCOMPtr<nsIContent> newLeftNode =
7352
0
          HTMLEditorRef().SplitNodeWithTransaction(atEnd, error);
7353
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
7354
0
          error.SuppressException();
7355
0
          return NS_ERROR_EDITOR_DESTROYED;
7356
0
        }
7357
0
        if (NS_WARN_IF(error.Failed())) {
7358
0
          return error.StealNSResult();
7359
0
        }
7360
0
7361
0
        // Correct the range.
7362
0
        // The new end parent becomes the parent node of the text.
7363
0
        EditorRawDOMPoint atContainerOfSplitNode(atEnd.GetContainer());
7364
0
        MOZ_ASSERT(!range->IsInSelection());
7365
0
        range->SetEnd(atContainerOfSplitNode, error);
7366
0
        if (NS_WARN_IF(error.Failed())) {
7367
0
          error.SuppressException();
7368
0
        }
7369
0
      }
7370
0
    }
7371
0
  }
7372
0
7373
0
  // Bust up any inlines that cross our range endpoints, but only if we are
7374
0
  // allowed to touch content.
7375
0
  // XXX Why don't we merge this block with the previous block?
7376
0
  if (aTouchContent == TouchContent::yes) {
7377
0
    nsTArray<OwningNonNull<RangeItem>> rangeItemArray;
7378
0
    rangeItemArray.AppendElements(aArrayOfRanges.Length());
7379
0
7380
0
    // First register ranges for special editor gravity
7381
0
    for (auto& rangeItem : rangeItemArray) {
7382
0
      rangeItem = new RangeItem();
7383
0
      rangeItem->StoreRange(aArrayOfRanges[0]);
7384
0
      HTMLEditorRef().mRangeUpdater.RegisterRangeItem(rangeItem);
7385
0
      aArrayOfRanges.RemoveElementAt(0);
7386
0
    }
7387
0
    // Now bust up inlines.
7388
0
    for (auto& item : Reversed(rangeItemArray)) {
7389
0
      nsresult rv = BustUpInlinesAtRangeEndpoints(*item);
7390
0
      if (NS_FAILED(rv)) {
7391
0
        break;
7392
0
      }
7393
0
    }
7394
0
    // Then unregister the ranges
7395
0
    for (auto& item : rangeItemArray) {
7396
0
      HTMLEditorRef().mRangeUpdater.DropRangeItem(item);
7397
0
      RefPtr<nsRange> range = item->GetRange();
7398
0
      if (range) {
7399
0
        aArrayOfRanges.AppendElement(range);
7400
0
      }
7401
0
    }
7402
0
  }
7403
0
  // Gather up a list of all the nodes
7404
0
  for (auto& range : aArrayOfRanges) {
7405
0
    DOMSubtreeIterator iter;
7406
0
    nsresult rv = iter.Init(*range);
7407
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
7408
0
      return rv;
7409
0
    }
7410
0
    if (aOutArrayOfNodes.IsEmpty()) {
7411
0
      iter.AppendList(TrivialFunctor(), aOutArrayOfNodes);
7412
0
    } else {
7413
0
      // We don't want duplicates in aOutArrayOfNodes, so we use an
7414
0
      // iterator/functor that only return nodes that are not already in
7415
0
      // aOutArrayOfNodes.
7416
0
      nsTArray<OwningNonNull<nsINode>> nodes;
7417
0
      iter.AppendList(UniqueFunctor(aOutArrayOfNodes), nodes);
7418
0
      aOutArrayOfNodes.AppendElements(nodes);
7419
0
    }
7420
0
  }
7421
0
7422
0
  // Certain operations should not act on li's and td's, but rather inside
7423
0
  // them.  Alter the list as needed.
7424
0
  if (aEditSubAction == EditSubAction::eCreateOrRemoveBlock) {
7425
0
    for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
7426
0
      OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
7427
0
      if (HTMLEditUtils::IsListItem(node)) {
7428
0
        int32_t j = i;
7429
0
        aOutArrayOfNodes.RemoveElementAt(i);
7430
0
        GetInnerContent(*node, aOutArrayOfNodes, &j);
7431
0
      }
7432
0
    }
7433
0
    // Empty text node shouldn't be selected if unnecessary
7434
0
    for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
7435
0
      if (Text* text = aOutArrayOfNodes[i]->GetAsText()) {
7436
0
        // Don't select empty text except to empty block
7437
0
        if (!HTMLEditorRef().IsVisibleTextNode(*text)) {
7438
0
          aOutArrayOfNodes.RemoveElementAt(i);
7439
0
        }
7440
0
      }
7441
0
    }
7442
0
  }
7443
0
  // Indent/outdent already do something special for list items, but we still
7444
0
  // need to make sure we don't act on table elements
7445
0
  else if (aEditSubAction == EditSubAction::eOutdent ||
7446
0
           aEditSubAction == EditSubAction::eIndent ||
7447
0
           aEditSubAction == EditSubAction::eSetPositionToAbsolute) {
7448
0
    for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
7449
0
      OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
7450
0
      if (HTMLEditUtils::IsTableElementButNotTable(node)) {
7451
0
        int32_t j = i;
7452
0
        aOutArrayOfNodes.RemoveElementAt(i);
7453
0
        GetInnerContent(*node, aOutArrayOfNodes, &j);
7454
0
      }
7455
0
    }
7456
0
  }
7457
0
  // Outdent should look inside of divs.
7458
0
  if (aEditSubAction == EditSubAction::eOutdent &&
7459
0
      !HTMLEditorRef().IsCSSEnabled()) {
7460
0
    for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
7461
0
      OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
7462
0
      if (node->IsHTMLElement(nsGkAtoms::div)) {
7463
0
        int32_t j = i;
7464
0
        aOutArrayOfNodes.RemoveElementAt(i);
7465
0
        GetInnerContent(*node, aOutArrayOfNodes, &j, Lists::no, Tables::no);
7466
0
      }
7467
0
    }
7468
0
  }
7469
0
7470
0
7471
0
  // Post-process the list to break up inline containers that contain br's, but
7472
0
  // only for operations that might care, like making lists or paragraphs
7473
0
  if (aEditSubAction == EditSubAction::eCreateOrRemoveBlock ||
7474
0
      aEditSubAction == EditSubAction::eCreateOrChangeList ||
7475
0
      aEditSubAction == EditSubAction::eSetOrClearAlignment ||
7476
0
      aEditSubAction == EditSubAction::eSetPositionToAbsolute ||
7477
0
      aEditSubAction == EditSubAction::eIndent ||
7478
0
      aEditSubAction == EditSubAction::eOutdent) {
7479
0
    for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
7480
0
      OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
7481
0
      // XXX Why do we run this loop even when aTouchContent is "no"?
7482
0
      if (aTouchContent == TouchContent::yes && IsInlineNode(node) &&
7483
0
          HTMLEditorRef().IsContainer(node) && !EditorBase::IsTextNode(node)) {
7484
0
        nsTArray<OwningNonNull<nsINode>> arrayOfInlines;
7485
0
        nsresult rv = BustUpInlinesAtBRs(*node->AsContent(), arrayOfInlines);
7486
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
7487
0
          return rv;
7488
0
        }
7489
0
7490
0
        // Put these nodes in aOutArrayOfNodes, replacing the current node
7491
0
        aOutArrayOfNodes.RemoveElementAt(i);
7492
0
        aOutArrayOfNodes.InsertElementsAt(i, arrayOfInlines);
7493
0
      }
7494
0
    }
7495
0
  }
7496
0
  return NS_OK;
7497
0
}
7498
7499
void
7500
HTMLEditRules::GetChildNodesForOperation(
7501
                 nsINode& aNode,
7502
                 nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes)
7503
0
{
7504
0
  for (nsCOMPtr<nsIContent> child = aNode.GetFirstChild();
7505
0
       child; child = child->GetNextSibling()) {
7506
0
    outArrayOfNodes.AppendElement(*child);
7507
0
  }
7508
0
}
7509
7510
nsresult
7511
HTMLEditRules::GetListActionNodes(
7512
                 nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
7513
                 EntireList aEntireList,
7514
                 TouchContent aTouchContent)
7515
0
{
7516
0
  MOZ_ASSERT(IsEditorDataAvailable());
7517
0
7518
0
  // Added this in so that ui code can ask to change an entire list, even if
7519
0
  // selection is only in part of it.  used by list item dialog.
7520
0
  if (aEntireList == EntireList::yes) {
7521
0
    uint32_t rangeCount = SelectionRef().RangeCount();
7522
0
    for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
7523
0
      RefPtr<nsRange> range = SelectionRef().GetRangeAt(rangeIdx);
7524
0
      for (nsCOMPtr<nsINode> parent = range->GetCommonAncestor();
7525
0
           parent; parent = parent->GetParentNode()) {
7526
0
        if (HTMLEditUtils::IsList(parent)) {
7527
0
          aOutArrayOfNodes.AppendElement(*parent);
7528
0
          break;
7529
0
        }
7530
0
      }
7531
0
    }
7532
0
    // If we didn't find any nodes this way, then try the normal way.  Perhaps
7533
0
    // the selection spans multiple lists but with no common list parent.
7534
0
    if (!aOutArrayOfNodes.IsEmpty()) {
7535
0
      return NS_OK;
7536
0
    }
7537
0
  }
7538
0
7539
0
  {
7540
0
    // We don't like other people messing with our selection!
7541
0
    AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
7542
0
7543
0
    // contruct a list of nodes to act on.
7544
0
    nsresult rv = GetNodesFromSelection(EditSubAction::eCreateOrChangeList,
7545
0
                                        aOutArrayOfNodes, aTouchContent);
7546
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
7547
0
      return rv;
7548
0
    }
7549
0
  }
7550
0
7551
0
  // Pre-process our list of nodes
7552
0
  for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
7553
0
    OwningNonNull<nsINode> testNode = aOutArrayOfNodes[i];
7554
0
7555
0
    // Remove all non-editable nodes.  Leave them be.
7556
0
    if (!HTMLEditorRef().IsEditable(testNode)) {
7557
0
      aOutArrayOfNodes.RemoveElementAt(i);
7558
0
      continue;
7559
0
    }
7560
0
7561
0
    // Scan for table elements and divs.  If we find table elements other than
7562
0
    // table, replace it with a list of any editable non-table content.
7563
0
    if (HTMLEditUtils::IsTableElementButNotTable(testNode)) {
7564
0
      int32_t j = i;
7565
0
      aOutArrayOfNodes.RemoveElementAt(i);
7566
0
      GetInnerContent(*testNode, aOutArrayOfNodes, &j, Lists::no);
7567
0
    }
7568
0
  }
7569
0
7570
0
  // If there is only one node in the array, and it is a list, div, or
7571
0
  // blockquote, then look inside of it until we find inner list or content.
7572
0
  LookInsideDivBQandList(aOutArrayOfNodes);
7573
0
7574
0
  return NS_OK;
7575
0
}
7576
7577
void
7578
HTMLEditRules::LookInsideDivBQandList(
7579
                 nsTArray<OwningNonNull<nsINode>>& aNodeArray)
7580
0
{
7581
0
  MOZ_ASSERT(IsEditorDataAvailable());
7582
0
7583
0
  // If there is only one node in the array, and it is a list, div, or
7584
0
  // blockquote, then look inside of it until we find inner list or content.
7585
0
  if (aNodeArray.Length() != 1) {
7586
0
    return;
7587
0
  }
7588
0
7589
0
  OwningNonNull<nsINode> curNode = aNodeArray[0];
7590
0
7591
0
  while (curNode->IsHTMLElement(nsGkAtoms::div) ||
7592
0
         HTMLEditUtils::IsList(curNode) ||
7593
0
         curNode->IsHTMLElement(nsGkAtoms::blockquote)) {
7594
0
    // Dive as long as there's only one child, and it's a list, div, blockquote
7595
0
    uint32_t numChildren = HTMLEditorRef().CountEditableChildren(curNode);
7596
0
    if (numChildren != 1) {
7597
0
      break;
7598
0
    }
7599
0
7600
0
    // Keep diving!  XXX One would expect to dive into the one editable node.
7601
0
    nsCOMPtr<nsIContent> child = curNode->GetFirstChild();
7602
0
    if (!child->IsHTMLElement(nsGkAtoms::div) &&
7603
0
        !HTMLEditUtils::IsList(child) &&
7604
0
        !child->IsHTMLElement(nsGkAtoms::blockquote)) {
7605
0
      break;
7606
0
    }
7607
0
7608
0
    // check editability XXX floppy moose
7609
0
    curNode = child;
7610
0
  }
7611
0
7612
0
  // We've found innermost list/blockquote/div: replace the one node in the
7613
0
  // array with these nodes
7614
0
  aNodeArray.RemoveElementAt(0);
7615
0
  if (curNode->IsAnyOfHTMLElements(nsGkAtoms::div,
7616
0
                                   nsGkAtoms::blockquote)) {
7617
0
    int32_t j = 0;
7618
0
    GetInnerContent(*curNode, aNodeArray, &j, Lists::no, Tables::no);
7619
0
    return;
7620
0
  }
7621
0
7622
0
  aNodeArray.AppendElement(*curNode);
7623
0
}
7624
7625
void
7626
HTMLEditRules::GetDefinitionListItemTypes(dom::Element* aElement,
7627
                                          bool* aDT,
7628
                                          bool* aDD)
7629
0
{
7630
0
  MOZ_ASSERT(aElement);
7631
0
  MOZ_ASSERT(aElement->IsHTMLElement(nsGkAtoms::dl));
7632
0
  MOZ_ASSERT(aDT);
7633
0
  MOZ_ASSERT(aDD);
7634
0
7635
0
  *aDT = *aDD = false;
7636
0
  for (nsIContent* child = aElement->GetFirstChild();
7637
0
       child;
7638
0
       child = child->GetNextSibling()) {
7639
0
    if (child->IsHTMLElement(nsGkAtoms::dt)) {
7640
0
      *aDT = true;
7641
0
    } else if (child->IsHTMLElement(nsGkAtoms::dd)) {
7642
0
      *aDD = true;
7643
0
    }
7644
0
  }
7645
0
}
7646
7647
nsresult
7648
HTMLEditRules::GetParagraphFormatNodes(
7649
                 nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes)
7650
0
{
7651
0
  MOZ_ASSERT(IsEditorDataAvailable());
7652
0
7653
0
  // Contruct a list of nodes to act on.
7654
0
  nsresult rv =
7655
0
   GetNodesFromSelection(EditSubAction::eCreateOrRemoveBlock,
7656
0
                         outArrayOfNodes, TouchContent::no);
7657
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
7658
0
    return rv;
7659
0
  }
7660
0
7661
0
  // Pre-process our list of nodes
7662
0
  for (int32_t i = outArrayOfNodes.Length() - 1; i >= 0; i--) {
7663
0
    OwningNonNull<nsINode> testNode = outArrayOfNodes[i];
7664
0
7665
0
    // Remove all non-editable nodes.  Leave them be.
7666
0
    if (!HTMLEditorRef().IsEditable(testNode)) {
7667
0
      outArrayOfNodes.RemoveElementAt(i);
7668
0
      continue;
7669
0
    }
7670
0
7671
0
    // Scan for table elements.  If we find table elements other than table,
7672
0
    // replace it with a list of any editable non-table content.  Ditto for
7673
0
    // list elements.
7674
0
    if (HTMLEditUtils::IsTableElement(testNode) ||
7675
0
        HTMLEditUtils::IsList(testNode) ||
7676
0
        HTMLEditUtils::IsListItem(testNode)) {
7677
0
      int32_t j = i;
7678
0
      outArrayOfNodes.RemoveElementAt(i);
7679
0
      GetInnerContent(testNode, outArrayOfNodes, &j);
7680
0
    }
7681
0
  }
7682
0
  return NS_OK;
7683
0
}
7684
7685
nsresult
7686
HTMLEditRules::BustUpInlinesAtRangeEndpoints(RangeItem& aRangeItem)
7687
0
{
7688
0
  MOZ_ASSERT(IsEditorDataAvailable());
7689
0
7690
0
  bool isCollapsed = aRangeItem.mStartContainer == aRangeItem.mEndContainer &&
7691
0
                     aRangeItem.mStartOffset == aRangeItem.mEndOffset;
7692
0
7693
0
  nsCOMPtr<nsIContent> endInline =
7694
0
    GetHighestInlineParent(*aRangeItem.mEndContainer);
7695
0
7696
0
  // XXX Oh, then, if the range is collapsed, we don't need to call
7697
0
  //     GetHighestInlineParent(), isn't it?
7698
0
  if (endInline && !isCollapsed) {
7699
0
    SplitNodeResult splitEndInlineResult =
7700
0
      HTMLEditorRef().SplitNodeDeepWithTransaction(
7701
0
                        *endInline,
7702
0
                        EditorRawDOMPoint(aRangeItem.mEndContainer,
7703
0
                                          aRangeItem.mEndOffset),
7704
0
                        SplitAtEdges::eDoNotCreateEmptyContainer);
7705
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
7706
0
      return NS_ERROR_EDITOR_DESTROYED;
7707
0
    }
7708
0
    if (NS_WARN_IF(splitEndInlineResult.Failed())) {
7709
0
      return splitEndInlineResult.Rv();
7710
0
    }
7711
0
    EditorRawDOMPoint splitPointAtEnd(splitEndInlineResult.SplitPoint());
7712
0
    if (NS_WARN_IF(!splitPointAtEnd.IsSet())) {
7713
0
      return NS_ERROR_FAILURE;
7714
0
    }
7715
0
    aRangeItem.mEndContainer = splitPointAtEnd.GetContainer();
7716
0
    aRangeItem.mEndOffset = splitPointAtEnd.Offset();
7717
0
  }
7718
0
7719
0
  nsCOMPtr<nsIContent> startInline =
7720
0
    GetHighestInlineParent(*aRangeItem.mStartContainer);
7721
0
7722
0
  if (startInline) {
7723
0
    SplitNodeResult splitStartInlineResult =
7724
0
      HTMLEditorRef().SplitNodeDeepWithTransaction(
7725
0
                        *startInline,
7726
0
                        EditorRawDOMPoint(aRangeItem.mStartContainer,
7727
0
                                          aRangeItem.mStartOffset),
7728
0
                        SplitAtEdges::eDoNotCreateEmptyContainer);
7729
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
7730
0
      return NS_ERROR_EDITOR_DESTROYED;
7731
0
    }
7732
0
    if (NS_WARN_IF(splitStartInlineResult.Failed())) {
7733
0
      return splitStartInlineResult.Rv();
7734
0
    }
7735
0
    // XXX If we split only here because of collapsed range, we're modifying
7736
0
    //     only start point of aRangeItem.  Shouldn't we modify end point here
7737
0
    //     if it's collapsed?
7738
0
    EditorRawDOMPoint splitPointAtStart(splitStartInlineResult.SplitPoint());
7739
0
    if (NS_WARN_IF(!splitPointAtStart.IsSet())) {
7740
0
      return NS_ERROR_FAILURE;
7741
0
    }
7742
0
    aRangeItem.mStartContainer = splitPointAtStart.GetContainer();
7743
0
    aRangeItem.mStartOffset = splitPointAtStart.Offset();
7744
0
  }
7745
0
7746
0
  return NS_OK;
7747
0
}
7748
7749
nsresult
7750
HTMLEditRules::BustUpInlinesAtBRs(
7751
                 nsIContent& aNode,
7752
                 nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes)
7753
0
{
7754
0
  MOZ_ASSERT(IsEditorDataAvailable());
7755
0
7756
0
  // First build up a list of all the break nodes inside the inline container.
7757
0
  nsTArray<OwningNonNull<nsINode>> arrayOfBreaks;
7758
0
  BRNodeFunctor functor;
7759
0
  DOMIterator iter(aNode);
7760
0
  iter.AppendList(functor, arrayOfBreaks);
7761
0
7762
0
  // If there aren't any breaks, just put inNode itself in the array
7763
0
  if (arrayOfBreaks.IsEmpty()) {
7764
0
    aOutArrayOfNodes.AppendElement(aNode);
7765
0
    return NS_OK;
7766
0
  }
7767
0
7768
0
  // Else we need to bust up aNode along all the breaks
7769
0
  nsCOMPtr<nsIContent> nextNode = &aNode;
7770
0
  for (OwningNonNull<nsINode>& brNode : arrayOfBreaks) {
7771
0
    EditorRawDOMPoint atBrNode(brNode);
7772
0
    if (NS_WARN_IF(!atBrNode.IsSet())) {
7773
0
      return NS_ERROR_FAILURE;
7774
0
    }
7775
0
    SplitNodeResult splitNodeResult =
7776
0
      HTMLEditorRef().SplitNodeDeepWithTransaction(
7777
0
                        *nextNode, atBrNode,
7778
0
                        SplitAtEdges::eAllowToCreateEmptyContainer);
7779
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
7780
0
      return NS_ERROR_EDITOR_DESTROYED;
7781
0
    }
7782
0
    if (NS_WARN_IF(splitNodeResult.Failed())) {
7783
0
      return splitNodeResult.Rv();
7784
0
    }
7785
0
7786
0
    // Put previous node at the split point.
7787
0
    if (splitNodeResult.GetPreviousNode()) {
7788
0
      // Might not be a left node.  A break might have been at the very
7789
0
      // beginning of inline container, in which case
7790
0
      // SplitNodeDeepWithTransaction() would not actually split anything.
7791
0
      aOutArrayOfNodes.AppendElement(*splitNodeResult.GetPreviousNode());
7792
0
    }
7793
0
7794
0
    // Move break outside of container and also put in node list
7795
0
    EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode());
7796
0
    nsresult rv =
7797
0
      HTMLEditorRef().MoveNodeWithTransaction(*brNode->AsContent(), atNextNode);
7798
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
7799
0
      return NS_ERROR_EDITOR_DESTROYED;
7800
0
    }
7801
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
7802
0
      return rv;
7803
0
    }
7804
0
    aOutArrayOfNodes.AppendElement(*brNode);
7805
0
7806
0
    nextNode = splitNodeResult.GetNextNode();
7807
0
  }
7808
0
7809
0
  // Now tack on remaining next node.
7810
0
  aOutArrayOfNodes.AppendElement(*nextNode);
7811
0
7812
0
  return NS_OK;
7813
0
}
7814
7815
nsIContent*
7816
HTMLEditRules::GetHighestInlineParent(nsINode& aNode)
7817
0
{
7818
0
  MOZ_ASSERT(IsEditorDataAvailable());
7819
0
7820
0
  if (!aNode.IsContent() || IsBlockNode(aNode)) {
7821
0
    return nullptr;
7822
0
  }
7823
0
7824
0
  Element* host = HTMLEditorRef().GetActiveEditingHost();
7825
0
  if (NS_WARN_IF(!host)) {
7826
0
    return nullptr;
7827
0
  }
7828
0
7829
0
  // If aNode is the editing host itself, there is no modifiable inline parent.
7830
0
  if (&aNode == host) {
7831
0
    return nullptr;
7832
0
  }
7833
0
7834
0
  // If aNode is outside of the <body> element, we don't support to edit
7835
0
  // such elements for now.
7836
0
  // XXX This should be MOZ_ASSERT after fixing bug 1413131 for avoiding
7837
0
  //     calling this expensive method.
7838
0
  if (NS_WARN_IF(!EditorUtils::IsDescendantOf(aNode, *host))) {
7839
0
    return nullptr;
7840
0
  }
7841
0
7842
0
  // Looks for the highest inline parent in the editing host.
7843
0
  nsIContent* content = aNode.AsContent();
7844
0
  for (nsIContent* parent = content->GetParent();
7845
0
       parent && parent != host && IsInlineNode(*parent);
7846
0
       parent = parent->GetParent()) {
7847
0
    content = parent;
7848
0
  }
7849
0
  return content;
7850
0
}
7851
7852
nsresult
7853
HTMLEditRules::GetNodesFromPoint(
7854
                 const EditorDOMPoint& aPoint,
7855
                 EditSubAction aEditSubAction,
7856
                 nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
7857
                 TouchContent aTouchContent)
7858
0
{
7859
0
  if (NS_WARN_IF(!aPoint.IsSet())) {
7860
0
    return NS_ERROR_INVALID_ARG;
7861
0
  }
7862
0
  RefPtr<nsRange> range = new nsRange(aPoint.GetContainer());
7863
0
  IgnoredErrorResult ignoredError;
7864
0
  range->SetStart(aPoint, ignoredError);
7865
0
  // error will assert on failure, because we are not cleaning it up,
7866
0
  // but we're asserting in that case anyway.
7867
0
  MOZ_ASSERT(!ignoredError.Failed());
7868
0
7869
0
  // Expand the range to include adjacent inlines
7870
0
  PromoteRange(*range, aEditSubAction);
7871
0
7872
0
  // Make array of ranges
7873
0
  nsTArray<RefPtr<nsRange>> arrayOfRanges;
7874
0
7875
0
  // Stuff new opRange into array
7876
0
  arrayOfRanges.AppendElement(range);
7877
0
7878
0
  // Use these ranges to contruct a list of nodes to act on
7879
0
  nsresult rv =
7880
0
    GetNodesForOperation(arrayOfRanges, outArrayOfNodes, aEditSubAction,
7881
0
                         aTouchContent);
7882
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
7883
0
    return rv;
7884
0
  }
7885
0
7886
0
  return NS_OK;
7887
0
}
7888
7889
nsresult
7890
HTMLEditRules::GetNodesFromSelection(
7891
                 EditSubAction aEditSubAction,
7892
                 nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
7893
                 TouchContent aTouchContent)
7894
0
{
7895
0
  MOZ_ASSERT(IsEditorDataAvailable());
7896
0
7897
0
  // Promote selection ranges
7898
0
  nsTArray<RefPtr<nsRange>> arrayOfRanges;
7899
0
  GetPromotedRanges(arrayOfRanges, aEditSubAction);
7900
0
7901
0
  // Use these ranges to contruct a list of nodes to act on.
7902
0
  nsresult rv = GetNodesForOperation(arrayOfRanges, outArrayOfNodes,
7903
0
                                     aEditSubAction, aTouchContent);
7904
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
7905
0
    return rv;
7906
0
  }
7907
0
7908
0
  return NS_OK;
7909
0
}
7910
7911
void
7912
HTMLEditRules::MakeTransitionList(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
7913
                                  nsTArray<bool>& aTransitionArray)
7914
0
{
7915
0
  nsCOMPtr<nsINode> prevParent;
7916
0
7917
0
  aTransitionArray.EnsureLengthAtLeast(aNodeArray.Length());
7918
0
  for (uint32_t i = 0; i < aNodeArray.Length(); i++) {
7919
0
    if (aNodeArray[i]->GetParentNode() != prevParent) {
7920
0
      // Different parents: transition point
7921
0
      aTransitionArray[i] = true;
7922
0
    } else {
7923
0
      // Same parents: these nodes grew up together
7924
0
      aTransitionArray[i] = false;
7925
0
    }
7926
0
    prevParent = aNodeArray[i]->GetParentNode();
7927
0
  }
7928
0
}
7929
7930
Element*
7931
HTMLEditRules::IsInListItem(nsINode* aNode)
7932
0
{
7933
0
  MOZ_ASSERT(IsEditorDataAvailable());
7934
0
7935
0
  NS_ENSURE_TRUE(aNode, nullptr);
7936
0
  if (HTMLEditUtils::IsListItem(aNode)) {
7937
0
    return aNode->AsElement();
7938
0
  }
7939
0
7940
0
  Element* parent = aNode->GetParentElement();
7941
0
  while (parent &&
7942
0
         HTMLEditorRef().IsDescendantOfEditorRoot(parent) &&
7943
0
         !HTMLEditUtils::IsTableElement(parent)) {
7944
0
    if (HTMLEditUtils::IsListItem(parent)) {
7945
0
      return parent;
7946
0
    }
7947
0
    parent = parent->GetParentElement();
7948
0
  }
7949
0
  return nullptr;
7950
0
}
7951
7952
nsAtom&
7953
HTMLEditRules::DefaultParagraphSeparator()
7954
0
{
7955
0
  MOZ_ASSERT(IsEditorDataAvailable());
7956
0
  return ParagraphSeparatorElement(
7957
0
           HTMLEditorRef().GetDefaultParagraphSeparator());
7958
0
}
7959
7960
nsresult
7961
HTMLEditRules::ReturnInHeader(Element& aHeader,
7962
                              nsINode& aNode,
7963
                              int32_t aOffset)
7964
0
{
7965
0
  MOZ_ASSERT(IsEditorDataAvailable());
7966
0
7967
0
  // Remember where the header is
7968
0
  nsCOMPtr<nsINode> headerParent = aHeader.GetParentNode();
7969
0
  int32_t offset = headerParent ? headerParent->ComputeIndexOf(&aHeader) : -1;
7970
0
7971
0
  // Get ws code to adjust any ws
7972
0
  nsCOMPtr<nsINode> node = &aNode;
7973
0
  nsresult rv = WSRunObject::PrepareToSplitAcrossBlocks(&HTMLEditorRef(),
7974
0
                                                        address_of(node),
7975
0
                                                        &aOffset);
7976
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
7977
0
    return NS_ERROR_EDITOR_DESTROYED;
7978
0
  }
7979
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
7980
0
    return rv;
7981
0
  }
7982
0
  if (NS_WARN_IF(!node->IsContent())) {
7983
0
    return NS_ERROR_FAILURE;
7984
0
  }
7985
0
7986
0
  // Split the header
7987
0
  SplitNodeResult splitHeaderResult =
7988
0
    HTMLEditorRef().SplitNodeDeepWithTransaction(
7989
0
                      aHeader, EditorRawDOMPoint(node, aOffset),
7990
0
                      SplitAtEdges::eAllowToCreateEmptyContainer);
7991
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
7992
0
    return NS_ERROR_EDITOR_DESTROYED;
7993
0
  }
7994
0
  NS_WARNING_ASSERTION(splitHeaderResult.Succeeded(),
7995
0
    "Failed to split aHeader");
7996
0
7997
0
  // If the previous heading of split point is empty, put a mozbr into it.
7998
0
  nsCOMPtr<nsIContent> prevItem = HTMLEditorRef().GetPriorHTMLSibling(&aHeader);
7999
0
  if (prevItem) {
8000
0
    MOZ_DIAGNOSTIC_ASSERT(
8001
0
      HTMLEditUtils::IsHeader(*prevItem));
8002
0
    bool isEmptyNode;
8003
0
    rv = HTMLEditorRef().IsEmptyNode(prevItem, &isEmptyNode);
8004
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
8005
0
      return rv;
8006
0
    }
8007
0
    if (isEmptyNode) {
8008
0
      CreateElementResult createMozBrResult =
8009
0
        CreateMozBR(EditorRawDOMPoint(prevItem, 0));
8010
0
      if (NS_WARN_IF(createMozBrResult.Failed())) {
8011
0
        return createMozBrResult.Rv();
8012
0
      }
8013
0
    }
8014
0
  }
8015
0
8016
0
  // If the new (righthand) header node is empty, delete it
8017
0
  if (IsEmptyBlockElement(aHeader, IgnoreSingleBR::eYes)) {
8018
0
    rv = HTMLEditorRef().DeleteNodeWithTransaction(aHeader);
8019
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
8020
0
      return NS_ERROR_EDITOR_DESTROYED;
8021
0
    }
8022
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
8023
0
      return rv;
8024
0
    }
8025
0
    // Layout tells the caret to blink in a weird place if we don't place a
8026
0
    // break after the header.
8027
0
    nsCOMPtr<nsIContent> sibling;
8028
0
    if (aHeader.GetNextSibling()) {
8029
0
      sibling = HTMLEditorRef().GetNextHTMLSibling(aHeader.GetNextSibling());
8030
0
    }
8031
0
    if (!sibling || !sibling->IsHTMLElement(nsGkAtoms::br)) {
8032
0
      ClearCachedStyles();
8033
0
      HTMLEditorRef().mTypeInState->ClearAllProps();
8034
0
8035
0
      // Create a paragraph
8036
0
      nsAtom& paraAtom = DefaultParagraphSeparator();
8037
0
      // We want a wrapper element even if we separate with <br>
8038
0
      EditorRawDOMPoint nextToHeader(headerParent, offset + 1);
8039
0
      RefPtr<Element> pNode =
8040
0
        HTMLEditorRef().CreateNodeWithTransaction(&paraAtom == nsGkAtoms::br ?
8041
0
                                                    *nsGkAtoms::p : paraAtom,
8042
0
                                                  nextToHeader);
8043
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8044
0
        return NS_ERROR_EDITOR_DESTROYED;
8045
0
      }
8046
0
      if (NS_WARN_IF(!pNode)) {
8047
0
        return NS_ERROR_FAILURE;
8048
0
      }
8049
0
8050
0
      // Append a <br> to it
8051
0
      RefPtr<Element> brElement =
8052
0
        HTMLEditorRef().InsertBrElementWithTransaction(
8053
0
                          SelectionRef(), EditorRawDOMPoint(pNode, 0));
8054
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8055
0
        return NS_ERROR_EDITOR_DESTROYED;
8056
0
      }
8057
0
      if (NS_WARN_IF(!brElement)) {
8058
0
        return NS_ERROR_FAILURE;
8059
0
      }
8060
0
8061
0
      // Set selection to before the break
8062
0
      ErrorResult error;
8063
0
      SelectionRef().Collapse(EditorRawDOMPoint(pNode, 0), error);
8064
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8065
0
        error.SuppressException();
8066
0
        return NS_ERROR_EDITOR_DESTROYED;
8067
0
      }
8068
0
      if (NS_WARN_IF(error.Failed())) {
8069
0
        return error.StealNSResult();
8070
0
      }
8071
0
    } else {
8072
0
      EditorRawDOMPoint afterSibling(sibling);
8073
0
      if (NS_WARN_IF(!afterSibling.AdvanceOffset())) {
8074
0
        return NS_ERROR_FAILURE;
8075
0
      }
8076
0
      // Put selection after break
8077
0
      ErrorResult error;
8078
0
      SelectionRef().Collapse(afterSibling, error);
8079
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8080
0
        error.SuppressException();
8081
0
        return NS_ERROR_EDITOR_DESTROYED;
8082
0
      }
8083
0
      if (NS_WARN_IF(error.Failed())) {
8084
0
        return error.StealNSResult();
8085
0
      }
8086
0
    }
8087
0
  } else {
8088
0
    // Put selection at front of righthand heading
8089
0
    ErrorResult error;
8090
0
    SelectionRef().Collapse(RawRangeBoundary(&aHeader, 0), error);
8091
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
8092
0
      error.SuppressException();
8093
0
      return NS_ERROR_EDITOR_DESTROYED;
8094
0
    }
8095
0
    if (NS_WARN_IF(error.Failed())) {
8096
0
      return error.StealNSResult();
8097
0
    }
8098
0
  }
8099
0
  return NS_OK;
8100
0
}
8101
8102
EditActionResult
8103
HTMLEditRules::ReturnInParagraph(Element& aParentDivOrP)
8104
0
{
8105
0
  MOZ_ASSERT(IsEditorDataAvailable());
8106
0
8107
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
8108
0
  if (NS_WARN_IF(!firstRange)) {
8109
0
    return EditActionResult(NS_ERROR_FAILURE);
8110
0
  }
8111
0
8112
0
  EditorDOMPoint atStartOfSelection(firstRange->StartRef());
8113
0
  if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
8114
0
    return EditActionResult(NS_ERROR_FAILURE);
8115
0
  }
8116
0
  MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
8117
0
8118
0
  // We shouldn't create new anchor element which has non-empty href unless
8119
0
  // splitting middle of it because we assume that users don't want to create
8120
0
  // *same* anchor element across two or more paragraphs in most cases.
8121
0
  // So, adjust selection start if it's edge of anchor element(s).
8122
0
  // XXX We don't support whitespace collapsing in these cases since it needs
8123
0
  //     some additional work with WSRunObject but it's not usual case.
8124
0
  //     E.g., |<a href="foo"><b>foo []</b> </a>|
8125
0
  if (atStartOfSelection.IsStartOfContainer()) {
8126
0
    for (nsIContent* container = atStartOfSelection.GetContainerAsContent();
8127
0
         container && container != &aParentDivOrP;
8128
0
         container = container->GetParent()) {
8129
0
      if (HTMLEditUtils::IsLink(container)) {
8130
0
        // Found link should be only in right node.  So, we shouldn't split it.
8131
0
        atStartOfSelection.Set(container);
8132
0
        // Even if we found an anchor element, don't break because DOM API
8133
0
        // allows to nest anchor elements.
8134
0
      }
8135
0
      // If the container is middle of its parent, stop adjusting split point.
8136
0
      if (container->GetPreviousSibling()) {
8137
0
        // XXX Should we check if previous sibling is visible content?
8138
0
        //     E.g., should we ignore comment node, invisible <br> element?
8139
0
        break;
8140
0
      }
8141
0
    }
8142
0
  }
8143
0
  // We also need to check if selection is at invisible <br> element at end
8144
0
  // of an <a href="foo"> element because editor inserts a <br> element when
8145
0
  // user types Enter key after a whitespace which is at middle of
8146
0
  // <a href="foo"> element and when setting selection at end of the element,
8147
0
  // selection becomes referring the <br> element.  We may need to change this
8148
0
  // behavior later if it'd be standardized.
8149
0
  else if (atStartOfSelection.IsEndOfContainer() ||
8150
0
           atStartOfSelection.IsBRElementAtEndOfContainer()) {
8151
0
    // If there are 2 <br> elements, the first <br> element is visible.  E.g.,
8152
0
    // |<a href="foo"><b>boo[]<br></b><br></a>|, we should split the <a>
8153
0
    // element.  Otherwise, E.g., |<a href="foo"><b>boo[]<br></b></a>|,
8154
0
    // we should not split the <a> element and ignore inline elements in it.
8155
0
    bool foundBRElement = atStartOfSelection.IsBRElementAtEndOfContainer();
8156
0
    for (nsIContent* container = atStartOfSelection.GetContainerAsContent();
8157
0
         container && container != &aParentDivOrP;
8158
0
         container = container->GetParent()) {
8159
0
      if (HTMLEditUtils::IsLink(container)) {
8160
0
        // Found link should be only in left node.  So, we shouldn't split it.
8161
0
        atStartOfSelection.SetAfter(container);
8162
0
        // Even if we found an anchor element, don't break because DOM API
8163
0
        // allows to nest anchor elements.
8164
0
      }
8165
0
      // If the container is middle of its parent, stop adjusting split point.
8166
0
      if (nsIContent* nextSibling = container->GetNextSibling()) {
8167
0
        if (foundBRElement) {
8168
0
          // If we've already found a <br> element, we assume found node is
8169
0
          // visible <br> or something other node.
8170
0
          // XXX Should we check if non-text data node like comment?
8171
0
          break;
8172
0
        }
8173
0
8174
0
        // XXX Should we check if non-text data node like comment?
8175
0
        if (!nextSibling->IsHTMLElement(nsGkAtoms::br)) {
8176
0
          break;
8177
0
        }
8178
0
        foundBRElement = true;
8179
0
      }
8180
0
    }
8181
0
  }
8182
0
8183
0
  bool doesCRCreateNewP =
8184
0
    HTMLEditorRef().GetReturnInParagraphCreatesNewParagraph();
8185
0
8186
0
  bool splitAfterNewBR = false;
8187
0
  nsCOMPtr<nsIContent> brContent;
8188
0
8189
0
  EditorDOMPoint pointToSplitParentDivOrP(atStartOfSelection);
8190
0
8191
0
  EditorRawDOMPoint pointToInsertBR;
8192
0
  if (doesCRCreateNewP &&
8193
0
      atStartOfSelection.GetContainer() == &aParentDivOrP) {
8194
0
    // We are at the edges of the block, so, we don't need to create new <br>.
8195
0
    brContent = nullptr;
8196
0
  } else if (atStartOfSelection.IsInTextNode()) {
8197
0
    // at beginning of text node?
8198
0
    if (atStartOfSelection.IsStartOfContainer()) {
8199
0
      // is there a BR prior to it?
8200
0
      brContent =
8201
0
        HTMLEditorRef().GetPriorHTMLSibling(atStartOfSelection.GetContainer());
8202
0
      if (!brContent ||
8203
0
          !HTMLEditorRef().IsVisibleBRElement(brContent) ||
8204
0
          TextEditUtils::HasMozAttr(brContent)) {
8205
0
        pointToInsertBR.Set(atStartOfSelection.GetContainer());
8206
0
        brContent = nullptr;
8207
0
      }
8208
0
    } else if (atStartOfSelection.IsEndOfContainer()) {
8209
0
      // we're at the end of text node...
8210
0
      // is there a BR after to it?
8211
0
      brContent =
8212
0
        HTMLEditorRef().GetNextHTMLSibling(atStartOfSelection.GetContainer());
8213
0
      if (!brContent ||
8214
0
          !HTMLEditorRef().IsVisibleBRElement(brContent) ||
8215
0
          TextEditUtils::HasMozAttr(brContent)) {
8216
0
        pointToInsertBR.Set(atStartOfSelection.GetContainer());
8217
0
        DebugOnly<bool> advanced = pointToInsertBR.AdvanceOffset();
8218
0
        NS_WARNING_ASSERTION(advanced,
8219
0
          "Failed to advance offset to after the container of selection start");
8220
0
        brContent = nullptr;
8221
0
      }
8222
0
    } else {
8223
0
      if (doesCRCreateNewP) {
8224
0
        ErrorResult error;
8225
0
        nsCOMPtr<nsIContent> newLeftDivOrP =
8226
0
          HTMLEditorRef().SplitNodeWithTransaction(pointToSplitParentDivOrP,
8227
0
                                                   error);
8228
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
8229
0
          error.SuppressException();
8230
0
          return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
8231
0
        }
8232
0
        if (NS_WARN_IF(error.Failed())) {
8233
0
          return EditActionResult(error.StealNSResult());
8234
0
        }
8235
0
        pointToSplitParentDivOrP.SetToEndOf(newLeftDivOrP);
8236
0
      }
8237
0
8238
0
      // We need to put new <br> after the left node if given node was split
8239
0
      // above.
8240
0
      pointToInsertBR.Set(pointToSplitParentDivOrP.GetContainer());
8241
0
      DebugOnly<bool> advanced = pointToInsertBR.AdvanceOffset();
8242
0
      NS_WARNING_ASSERTION(advanced,
8243
0
        "Failed to advance offset to after the container of selection start");
8244
0
    }
8245
0
  } else {
8246
0
    // not in a text node.
8247
0
    // is there a BR prior to it?
8248
0
    nsCOMPtr<nsIContent> nearNode;
8249
0
    nearNode = HTMLEditorRef().GetPreviousEditableHTMLNode(atStartOfSelection);
8250
0
    if (!nearNode || !HTMLEditorRef().IsVisibleBRElement(nearNode) ||
8251
0
        TextEditUtils::HasMozAttr(nearNode)) {
8252
0
      // is there a BR after it?
8253
0
      nearNode = HTMLEditorRef().GetNextEditableHTMLNode(atStartOfSelection);
8254
0
      if (!nearNode || !HTMLEditorRef().IsVisibleBRElement(nearNode) ||
8255
0
          TextEditUtils::HasMozAttr(nearNode)) {
8256
0
        pointToInsertBR = atStartOfSelection;
8257
0
        splitAfterNewBR = true;
8258
0
      }
8259
0
    }
8260
0
    if (!pointToInsertBR.IsSet() && TextEditUtils::IsBreak(nearNode)) {
8261
0
      brContent = nearNode;
8262
0
    }
8263
0
  }
8264
0
  if (pointToInsertBR.IsSet()) {
8265
0
    // if CR does not create a new P, default to BR creation
8266
0
    if (NS_WARN_IF(!doesCRCreateNewP)) {
8267
0
      return EditActionResult(NS_OK);
8268
0
    }
8269
0
8270
0
    brContent =
8271
0
      HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
8272
0
                                                     pointToInsertBR);
8273
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
8274
0
      return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
8275
0
    }
8276
0
    NS_WARNING_ASSERTION(brContent, "Failed to create a <br> element");
8277
0
    if (splitAfterNewBR) {
8278
0
      // We split the parent after the br we've just inserted.
8279
0
      pointToSplitParentDivOrP.Set(brContent);
8280
0
      DebugOnly<bool> advanced = pointToSplitParentDivOrP.AdvanceOffset();
8281
0
      NS_WARNING_ASSERTION(advanced,
8282
0
        "Failed to advance offset after the new <br>");
8283
0
    }
8284
0
  }
8285
0
  EditActionResult result(
8286
0
    SplitParagraph(aParentDivOrP, pointToSplitParentDivOrP, brContent));
8287
0
  result.MarkAsHandled();
8288
0
  if (NS_WARN_IF(result.Failed())) {
8289
0
    return result;
8290
0
  }
8291
0
  return result;
8292
0
}
8293
8294
template<typename PT, typename CT>
8295
nsresult
8296
HTMLEditRules::SplitParagraph(
8297
                 Element& aParentDivOrP,
8298
                 const EditorDOMPointBase<PT, CT>& aStartOfRightNode,
8299
                 nsIContent* aNextBRNode)
8300
0
{
8301
0
  MOZ_ASSERT(IsEditorDataAvailable());
8302
0
8303
0
  // split para
8304
0
  // get ws code to adjust any ws
8305
0
  nsCOMPtr<nsINode> selNode = aStartOfRightNode.GetContainer();
8306
0
  int32_t selOffset = aStartOfRightNode.Offset();
8307
0
  nsresult rv =
8308
0
    WSRunObject::PrepareToSplitAcrossBlocks(&HTMLEditorRef(),
8309
0
                                            address_of(selNode), &selOffset);
8310
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
8311
0
    return NS_ERROR_EDITOR_DESTROYED;
8312
0
  }
8313
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
8314
0
    return rv;
8315
0
  }
8316
0
  if (NS_WARN_IF(!selNode->IsContent())) {
8317
0
    return NS_ERROR_FAILURE;
8318
0
  }
8319
0
8320
0
  // Split the paragraph.
8321
0
  SplitNodeResult splitDivOrPResult =
8322
0
    HTMLEditorRef().SplitNodeDeepWithTransaction(
8323
0
                      aParentDivOrP,
8324
0
                      EditorRawDOMPoint(selNode, selOffset),
8325
0
                      SplitAtEdges::eAllowToCreateEmptyContainer);
8326
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
8327
0
    return NS_ERROR_EDITOR_DESTROYED;
8328
0
  }
8329
0
  if (NS_WARN_IF(splitDivOrPResult.Failed())) {
8330
0
    return splitDivOrPResult.Rv();
8331
0
  }
8332
0
  if (NS_WARN_IF(!splitDivOrPResult.DidSplit())) {
8333
0
    return NS_ERROR_FAILURE;
8334
0
  }
8335
0
8336
0
  // Get rid of the break, if it is visible (otherwise it may be needed to
8337
0
  // prevent an empty p).
8338
0
  if (aNextBRNode && HTMLEditorRef().IsVisibleBRElement(aNextBRNode)) {
8339
0
    rv = HTMLEditorRef().DeleteNodeWithTransaction(*aNextBRNode);
8340
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
8341
0
      return NS_ERROR_EDITOR_DESTROYED;
8342
0
    }
8343
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
8344
0
      return rv;
8345
0
    }
8346
0
  }
8347
0
8348
0
  // Remove ID attribute on the paragraph from the existing right node.
8349
0
  rv = HTMLEditorRef().RemoveAttributeWithTransaction(aParentDivOrP,
8350
0
                                                      *nsGkAtoms::id);
8351
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
8352
0
    return NS_ERROR_EDITOR_DESTROYED;
8353
0
  }
8354
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
8355
0
    return rv;
8356
0
  }
8357
0
8358
0
  // We need to ensure to both paragraphs visible even if they are empty.
8359
0
  // However, moz-<br> element isn't useful in this case because moz-<br>
8360
0
  // elements will be ignored by PlaintextSerializer.  Additionally,
8361
0
  // moz-<br> will be exposed as <br> with Element.innerHTML.  Therefore,
8362
0
  // we can use normal <br> elements for placeholder in this case.
8363
0
  // Note that Chromium also behaves so.
8364
0
  rv = InsertBRIfNeeded(*splitDivOrPResult.GetPreviousNode());
8365
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
8366
0
    return rv;
8367
0
  }
8368
0
  rv = InsertBRIfNeeded(*splitDivOrPResult.GetNextNode());
8369
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
8370
0
    return rv;
8371
0
  }
8372
0
8373
0
  // selection to beginning of right hand para;
8374
0
  // look inside any containers that are up front.
8375
0
  nsIContent* child = HTMLEditorRef().GetLeftmostChild(&aParentDivOrP, true);
8376
0
  if (EditorBase::IsTextNode(child) || HTMLEditorRef().IsContainer(child)) {
8377
0
    EditorRawDOMPoint atStartOfChild(child, 0);
8378
0
    IgnoredErrorResult ignoredError;
8379
0
    SelectionRef().Collapse(atStartOfChild, ignoredError);
8380
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
8381
0
      return NS_ERROR_EDITOR_DESTROYED;
8382
0
    }
8383
0
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
8384
0
      "Failed to collapse selection at the end of the child");
8385
0
  } else {
8386
0
    EditorRawDOMPoint atChild(child);
8387
0
    IgnoredErrorResult ignoredError;
8388
0
    SelectionRef().Collapse(atChild, ignoredError);
8389
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
8390
0
      return NS_ERROR_EDITOR_DESTROYED;
8391
0
    }
8392
0
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
8393
0
      "Failed to collapse selection at the child");
8394
0
  }
8395
0
  return NS_OK;
8396
0
}
8397
8398
nsresult
8399
HTMLEditRules::ReturnInListItem(Element& aListItem,
8400
                                nsINode& aNode,
8401
                                int32_t aOffset)
8402
0
{
8403
0
  MOZ_ASSERT(IsEditorDataAvailable());
8404
0
  MOZ_ASSERT(HTMLEditUtils::IsListItem(&aListItem));
8405
0
8406
0
  // Get the item parent and the active editing host.
8407
0
  RefPtr<Element> host = HTMLEditorRef().GetActiveEditingHost();
8408
0
8409
0
  // If we are in an empty item, then we want to pop up out of the list, but
8410
0
  // only if prefs say it's okay and if the parent isn't the active editing
8411
0
  // host.
8412
0
  if (mReturnInEmptyLIKillsList &&
8413
0
      host != aListItem.GetParentElement() &&
8414
0
      IsEmptyBlockElement(aListItem, IgnoreSingleBR::eYes)) {
8415
0
    nsCOMPtr<nsIContent> leftListNode = aListItem.GetParent();
8416
0
    // Are we the last list item in the list?
8417
0
    if (!HTMLEditorRef().IsLastEditableChild(&aListItem)) {
8418
0
      // We need to split the list!
8419
0
      EditorRawDOMPoint atListItem(&aListItem);
8420
0
      ErrorResult error;
8421
0
      leftListNode =
8422
0
        HTMLEditorRef().SplitNodeWithTransaction(atListItem, error);
8423
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8424
0
        error.SuppressException();
8425
0
        return NS_ERROR_EDITOR_DESTROYED;
8426
0
      }
8427
0
      if (NS_WARN_IF(error.Failed())) {
8428
0
        return error.StealNSResult();
8429
0
      }
8430
0
    }
8431
0
8432
0
    // Are we in a sublist?
8433
0
    EditorRawDOMPoint atNextSiblingOfLeftList(leftListNode);
8434
0
    DebugOnly<bool> advanced = atNextSiblingOfLeftList.AdvanceOffset();
8435
0
    NS_WARNING_ASSERTION(advanced,
8436
0
      "Failed to advance offset after the right list node");
8437
0
    if (HTMLEditUtils::IsList(atNextSiblingOfLeftList.GetContainer())) {
8438
0
      // If so, move item out of this list and into the grandparent list
8439
0
      nsresult rv =
8440
0
        HTMLEditorRef().MoveNodeWithTransaction(aListItem,
8441
0
                                                atNextSiblingOfLeftList);
8442
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8443
0
        return NS_ERROR_EDITOR_DESTROYED;
8444
0
      }
8445
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
8446
0
        return rv;
8447
0
      }
8448
0
      ErrorResult error;
8449
0
      SelectionRef().Collapse(RawRangeBoundary(&aListItem, 0), error);
8450
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8451
0
        error.SuppressException();
8452
0
        return NS_ERROR_EDITOR_DESTROYED;
8453
0
      }
8454
0
      if (NS_WARN_IF(error.Failed())) {
8455
0
        return error.StealNSResult();
8456
0
      }
8457
0
    } else {
8458
0
      // Otherwise kill this item
8459
0
      nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(aListItem);
8460
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8461
0
        return NS_ERROR_EDITOR_DESTROYED;
8462
0
      }
8463
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
8464
0
        return rv;
8465
0
      }
8466
0
8467
0
      // Time to insert a paragraph
8468
0
      nsAtom& paraAtom = DefaultParagraphSeparator();
8469
0
      // We want a wrapper even if we separate with <br>
8470
0
      RefPtr<Element> pNode =
8471
0
        HTMLEditorRef().CreateNodeWithTransaction(&paraAtom == nsGkAtoms::br ?
8472
0
                                                    *nsGkAtoms::p : paraAtom,
8473
0
                                                  atNextSiblingOfLeftList);
8474
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8475
0
        return NS_ERROR_EDITOR_DESTROYED;
8476
0
      }
8477
0
      if (NS_WARN_IF(!pNode)) {
8478
0
        return NS_ERROR_FAILURE;
8479
0
      }
8480
0
8481
0
      // Append a <br> to it
8482
0
      RefPtr<Element> brElement =
8483
0
        HTMLEditorRef().InsertBrElementWithTransaction(
8484
0
                          SelectionRef(), EditorRawDOMPoint(pNode, 0));
8485
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8486
0
        return NS_ERROR_EDITOR_DESTROYED;
8487
0
      }
8488
0
      if (NS_WARN_IF(!brElement)) {
8489
0
        return NS_ERROR_FAILURE;
8490
0
      }
8491
0
8492
0
      // Set selection to before the break
8493
0
      ErrorResult error;
8494
0
      SelectionRef().Collapse(EditorRawDOMPoint(pNode, 0), error);
8495
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8496
0
        error.SuppressException();
8497
0
        return NS_ERROR_EDITOR_DESTROYED;
8498
0
      }
8499
0
      if (NS_WARN_IF(error.Failed())) {
8500
0
        return error.StealNSResult();
8501
0
      }
8502
0
    }
8503
0
    return NS_OK;
8504
0
  }
8505
0
8506
0
  // Else we want a new list item at the same list level.  Get ws code to
8507
0
  // adjust any ws.
8508
0
  nsCOMPtr<nsINode> selNode = &aNode;
8509
0
  nsresult rv =
8510
0
    WSRunObject::PrepareToSplitAcrossBlocks(&HTMLEditorRef(),
8511
0
                                            address_of(selNode), &aOffset);
8512
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
8513
0
    return NS_ERROR_EDITOR_DESTROYED;
8514
0
  }
8515
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
8516
0
    return rv;
8517
0
  }
8518
0
  if (NS_WARN_IF(!selNode->IsContent())) {
8519
0
    return NS_ERROR_FAILURE;
8520
0
  }
8521
0
8522
0
  // Now split the list item.
8523
0
  SplitNodeResult splitListItemResult =
8524
0
    HTMLEditorRef().SplitNodeDeepWithTransaction(
8525
0
                      aListItem, EditorRawDOMPoint(selNode, aOffset),
8526
0
                      SplitAtEdges::eAllowToCreateEmptyContainer);
8527
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
8528
0
    return NS_ERROR_EDITOR_DESTROYED;
8529
0
  }
8530
0
  NS_WARNING_ASSERTION(splitListItemResult.Succeeded(),
8531
0
    "Failed to split the list item");
8532
0
8533
0
  // Hack: until I can change the damaged doc range code back to being
8534
0
  // extra-inclusive, I have to manually detect certain list items that may be
8535
0
  // left empty.
8536
0
  nsCOMPtr<nsIContent> prevItem =
8537
0
    HTMLEditorRef().GetPriorHTMLSibling(&aListItem);
8538
0
  if (prevItem && HTMLEditUtils::IsListItem(prevItem)) {
8539
0
    bool isEmptyNode;
8540
0
    rv = HTMLEditorRef().IsEmptyNode(prevItem, &isEmptyNode);
8541
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
8542
0
      return rv;
8543
0
    }
8544
0
    if (isEmptyNode) {
8545
0
      CreateElementResult createMozBrResult =
8546
0
        CreateMozBR(EditorRawDOMPoint(prevItem, 0));
8547
0
      if (NS_WARN_IF(createMozBrResult.Failed())) {
8548
0
        return createMozBrResult.Rv();
8549
0
      }
8550
0
    } else {
8551
0
      rv = HTMLEditorRef().IsEmptyNode(&aListItem, &isEmptyNode, true);
8552
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
8553
0
        return rv;
8554
0
      }
8555
0
      if (isEmptyNode) {
8556
0
        RefPtr<nsAtom> nodeAtom = aListItem.NodeInfo()->NameAtom();
8557
0
        if (nodeAtom == nsGkAtoms::dd || nodeAtom == nsGkAtoms::dt) {
8558
0
          nsCOMPtr<nsINode> list = aListItem.GetParentNode();
8559
0
          int32_t itemOffset = list ? list->ComputeIndexOf(&aListItem) : -1;
8560
0
8561
0
          nsAtom* listAtom = nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd
8562
0
                                                        : nsGkAtoms::dt;
8563
0
          MOZ_DIAGNOSTIC_ASSERT(itemOffset != -1);
8564
0
          EditorRawDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
8565
0
                                           itemOffset + 1);
8566
0
          RefPtr<Element> newListItem =
8567
0
            HTMLEditorRef().CreateNodeWithTransaction(*listAtom,
8568
0
                                                      atNextListItem);
8569
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
8570
0
            return NS_ERROR_EDITOR_DESTROYED;
8571
0
          }
8572
0
          if (NS_WARN_IF(!newListItem)) {
8573
0
            return NS_ERROR_FAILURE;
8574
0
          }
8575
0
          rv = HTMLEditorRef().DeleteNodeWithTransaction(aListItem);
8576
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
8577
0
            return NS_ERROR_EDITOR_DESTROYED;
8578
0
          }
8579
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
8580
0
            return rv;
8581
0
          }
8582
0
          ErrorResult error;
8583
0
          SelectionRef().Collapse(EditorRawDOMPoint(newListItem, 0), error);
8584
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
8585
0
            error.SuppressException();
8586
0
            return NS_ERROR_EDITOR_DESTROYED;
8587
0
          }
8588
0
          if (NS_WARN_IF(error.Failed())) {
8589
0
            return error.StealNSResult();
8590
0
          }
8591
0
          return NS_OK;
8592
0
        }
8593
0
8594
0
        RefPtr<Element> brElement;
8595
0
        nsresult rv =
8596
0
          HTMLEditorRef().CopyLastEditableChildStylesWithTransaction(
8597
0
                            *prevItem->AsElement(), aListItem,
8598
0
                            address_of(brElement));
8599
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
8600
0
          return NS_ERROR_EDITOR_DESTROYED;
8601
0
        }
8602
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
8603
0
          return NS_ERROR_FAILURE;
8604
0
        }
8605
0
        if (brElement) {
8606
0
          EditorRawDOMPoint atBrNode(brElement);
8607
0
          if (NS_WARN_IF(!atBrNode.IsSetAndValid())) {
8608
0
            return NS_ERROR_FAILURE;
8609
0
          }
8610
0
          ErrorResult error;
8611
0
          SelectionRef().Collapse(atBrNode, error);
8612
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
8613
0
            error.SuppressException();
8614
0
            return NS_ERROR_EDITOR_DESTROYED;
8615
0
          }
8616
0
          if (NS_WARN_IF(error.Failed())) {
8617
0
            return error.StealNSResult();
8618
0
          }
8619
0
          return NS_OK;
8620
0
        }
8621
0
      } else {
8622
0
        WSRunObject wsObj(&HTMLEditorRef(), &aListItem, 0);
8623
0
        nsCOMPtr<nsINode> visNode;
8624
0
        int32_t visOffset = 0;
8625
0
        WSType wsType;
8626
0
        wsObj.NextVisibleNode(EditorRawDOMPoint(&aListItem, 0),
8627
0
                              address_of(visNode), &visOffset, &wsType);
8628
0
        if (wsType == WSType::special || wsType == WSType::br ||
8629
0
            visNode->IsHTMLElement(nsGkAtoms::hr)) {
8630
0
          EditorRawDOMPoint atVisNode(visNode);
8631
0
          if (NS_WARN_IF(!atVisNode.IsSetAndValid())) {
8632
0
            return NS_ERROR_FAILURE;
8633
0
          }
8634
0
          ErrorResult error;
8635
0
          SelectionRef().Collapse(atVisNode, error);
8636
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
8637
0
            error.SuppressException();
8638
0
            return NS_ERROR_EDITOR_DESTROYED;
8639
0
          }
8640
0
          if (NS_WARN_IF(error.Failed())) {
8641
0
            return error.StealNSResult();
8642
0
          }
8643
0
          return NS_OK;
8644
0
        }
8645
0
8646
0
        rv = SelectionRef().Collapse(visNode, visOffset);
8647
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
8648
0
          return NS_ERROR_EDITOR_DESTROYED;
8649
0
        }
8650
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
8651
0
          return rv;
8652
0
        }
8653
0
        return NS_OK;
8654
0
      }
8655
0
    }
8656
0
  }
8657
0
8658
0
  ErrorResult error;
8659
0
  SelectionRef().Collapse(EditorRawDOMPoint(&aListItem, 0), error);
8660
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
8661
0
    error.SuppressException();
8662
0
    return NS_ERROR_EDITOR_DESTROYED;
8663
0
  }
8664
0
  if (NS_WARN_IF(error.Failed())) {
8665
0
    return error.StealNSResult();
8666
0
  }
8667
0
  return NS_OK;
8668
0
}
8669
8670
nsresult
8671
HTMLEditRules::MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray)
8672
0
{
8673
0
  MOZ_ASSERT(IsEditorDataAvailable());
8674
0
8675
0
  // The idea here is to put the nodes into a minimal number of blockquotes.
8676
0
  // When the user blockquotes something, they expect one blockquote.  That may
8677
0
  // not be possible (for instance, if they have two table cells selected, you
8678
0
  // need two blockquotes inside the cells).
8679
0
  RefPtr<Element> curBlock;
8680
0
  nsCOMPtr<nsINode> prevParent;
8681
0
8682
0
  for (auto& curNode : aNodeArray) {
8683
0
    // Get the node to act on, and its location
8684
0
    if (NS_WARN_IF(!curNode->IsContent())) {
8685
0
      return NS_ERROR_FAILURE;
8686
0
    }
8687
0
8688
0
    // If the node is a table element or list item, dive inside
8689
0
    if (HTMLEditUtils::IsTableElementButNotTable(curNode) ||
8690
0
        HTMLEditUtils::IsListItem(curNode)) {
8691
0
      // Forget any previous block
8692
0
      curBlock = nullptr;
8693
0
      // Recursion time
8694
0
      nsTArray<OwningNonNull<nsINode>> childArray;
8695
0
      GetChildNodesForOperation(*curNode, childArray);
8696
0
      nsresult rv = MakeBlockquote(childArray);
8697
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
8698
0
        return rv;
8699
0
      }
8700
0
    }
8701
0
8702
0
    // If the node has different parent than previous node, further nodes in a
8703
0
    // new parent
8704
0
    if (prevParent) {
8705
0
      if (prevParent != curNode->GetParentNode()) {
8706
0
        // Forget any previous blockquote node we were using
8707
0
        curBlock = nullptr;
8708
0
        prevParent = curNode->GetParentNode();
8709
0
      }
8710
0
    } else {
8711
0
      prevParent = curNode->GetParentNode();
8712
0
    }
8713
0
8714
0
    // If no curBlock, make one
8715
0
    if (!curBlock) {
8716
0
      EditorDOMPoint atCurNode(curNode);
8717
0
      SplitNodeResult splitNodeResult =
8718
0
        MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote,
8719
0
                                                    atCurNode);
8720
0
      if (NS_WARN_IF(splitNodeResult.Failed())) {
8721
0
        return splitNodeResult.Rv();
8722
0
      }
8723
0
      curBlock =
8724
0
        HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::blockquote,
8725
0
                                                  splitNodeResult.SplitPoint());
8726
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8727
0
        return NS_ERROR_EDITOR_DESTROYED;
8728
0
      }
8729
0
      if (NS_WARN_IF(!curBlock)) {
8730
0
        return NS_ERROR_FAILURE;
8731
0
      }
8732
0
      // remember our new block for postprocessing
8733
0
      mNewBlock = curBlock;
8734
0
      // note: doesn't matter if we set mNewBlock multiple times.
8735
0
    }
8736
0
8737
0
    nsresult rv =
8738
0
      HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
8739
0
                                                   *curBlock);
8740
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
8741
0
      return NS_ERROR_EDITOR_DESTROYED;
8742
0
    }
8743
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
8744
0
      return rv;
8745
0
    }
8746
0
  }
8747
0
  return NS_OK;
8748
0
}
8749
8750
nsresult
8751
HTMLEditRules::RemoveBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray)
8752
0
{
8753
0
  MOZ_ASSERT(IsEditorDataAvailable());
8754
0
8755
0
  // Intent of this routine is to be used for converting to/from headers,
8756
0
  // paragraphs, pre, and address.  Those blocks that pretty much just contain
8757
0
  // inline things...
8758
0
  nsCOMPtr<Element> curBlock;
8759
0
  nsCOMPtr<nsIContent> firstNode, lastNode;
8760
0
  for (auto& curNode : aNodeArray) {
8761
0
    // If curNode is an <address>, <p>, <hn>, or <pre>, remove it.
8762
0
    if (HTMLEditUtils::IsFormatNode(curNode)) {
8763
0
      // Process any partial progress saved
8764
0
      if (curBlock) {
8765
0
        SplitRangeOffFromNodeResult removeMiddleContainerResult =
8766
0
          SplitRangeOffFromBlockAndRemoveMiddleContainer(*curBlock,
8767
0
                                                         *firstNode, *lastNode);
8768
0
        if (NS_WARN_IF(removeMiddleContainerResult.Failed())) {
8769
0
          return removeMiddleContainerResult.Rv();
8770
0
        }
8771
0
        firstNode = lastNode = curBlock = nullptr;
8772
0
      }
8773
0
      if (!HTMLEditorRef().IsEditable(curNode)) {
8774
0
        continue;
8775
0
      }
8776
0
      // Remove current block
8777
0
      nsresult rv =
8778
0
        HTMLEditorRef().RemoveBlockContainerWithTransaction(
8779
0
                          *curNode->AsElement());
8780
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8781
0
        return NS_ERROR_EDITOR_DESTROYED;
8782
0
      }
8783
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
8784
0
        return rv;
8785
0
      }
8786
0
      continue;
8787
0
    }
8788
0
8789
0
    // XXX How about, <th>, <thead>, <tfoot>, <dt>, <dl>?
8790
0
    if (curNode->IsAnyOfHTMLElements(nsGkAtoms::table,
8791
0
                                     nsGkAtoms::tr,
8792
0
                                     nsGkAtoms::tbody,
8793
0
                                     nsGkAtoms::td,
8794
0
                                     nsGkAtoms::li,
8795
0
                                     nsGkAtoms::blockquote,
8796
0
                                     nsGkAtoms::div) ||
8797
0
        HTMLEditUtils::IsList(curNode)) {
8798
0
      // Process any partial progress saved
8799
0
      if (curBlock) {
8800
0
        SplitRangeOffFromNodeResult removeMiddleContainerResult =
8801
0
          SplitRangeOffFromBlockAndRemoveMiddleContainer(*curBlock,
8802
0
                                                         *firstNode, *lastNode);
8803
0
        if (NS_WARN_IF(removeMiddleContainerResult.Failed())) {
8804
0
          return removeMiddleContainerResult.Rv();
8805
0
        }
8806
0
        firstNode = lastNode = curBlock = nullptr;
8807
0
      }
8808
0
      if (!HTMLEditorRef().IsEditable(curNode)) {
8809
0
        continue;
8810
0
      }
8811
0
      // Recursion time
8812
0
      nsTArray<OwningNonNull<nsINode>> childArray;
8813
0
      GetChildNodesForOperation(*curNode, childArray);
8814
0
      nsresult rv = RemoveBlockStyle(childArray);
8815
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
8816
0
        return rv;
8817
0
      }
8818
0
      continue;
8819
0
    }
8820
0
8821
0
    if (IsInlineNode(curNode)) {
8822
0
      if (curBlock) {
8823
0
        // If so, is this node a descendant?
8824
0
        if (EditorUtils::IsDescendantOf(*curNode, *curBlock)) {
8825
0
          // Then we don't need to do anything different for this node
8826
0
          lastNode = curNode->AsContent();
8827
0
          continue;
8828
0
        }
8829
0
        // Otherwise, we have progressed beyond end of curBlock, so let's
8830
0
        // handle it now.  We need to remove the portion of curBlock that
8831
0
        // contains [firstNode - lastNode].
8832
0
        SplitRangeOffFromNodeResult removeMiddleContainerResult =
8833
0
          SplitRangeOffFromBlockAndRemoveMiddleContainer(*curBlock,
8834
0
                                                         *firstNode, *lastNode);
8835
0
        if (NS_WARN_IF(removeMiddleContainerResult.Failed())) {
8836
0
          return removeMiddleContainerResult.Rv();
8837
0
        }
8838
0
        firstNode = lastNode = curBlock = nullptr;
8839
0
        // Fall out and handle curNode
8840
0
      }
8841
0
      curBlock = HTMLEditorRef().GetBlockNodeParent(curNode);
8842
0
      if (!curBlock || !HTMLEditUtils::IsFormatNode(curBlock) ||
8843
0
          !HTMLEditorRef().IsEditable(curBlock)) {
8844
0
        // Not a block kind that we care about.
8845
0
        curBlock = nullptr;
8846
0
      } else {
8847
0
        firstNode = lastNode = curNode->AsContent();
8848
0
      }
8849
0
      continue;
8850
0
    }
8851
0
8852
0
    if (curBlock) {
8853
0
      // Some node that is already sans block style.  Skip over it and process
8854
0
      // any partial progress saved.
8855
0
      SplitRangeOffFromNodeResult removeMiddleContainerResult =
8856
0
        SplitRangeOffFromBlockAndRemoveMiddleContainer(*curBlock,
8857
0
                                                       *firstNode, *lastNode);
8858
0
      if (NS_WARN_IF(removeMiddleContainerResult.Failed())) {
8859
0
        return removeMiddleContainerResult.Rv();
8860
0
      }
8861
0
      firstNode = lastNode = curBlock = nullptr;
8862
0
      continue;
8863
0
    }
8864
0
  }
8865
0
  // Process any partial progress saved
8866
0
  if (curBlock) {
8867
0
    SplitRangeOffFromNodeResult removeMiddleContainerResult =
8868
0
      SplitRangeOffFromBlockAndRemoveMiddleContainer(*curBlock,
8869
0
                                                     *firstNode, *lastNode);
8870
0
    if (NS_WARN_IF(removeMiddleContainerResult.Failed())) {
8871
0
      return removeMiddleContainerResult.Rv();
8872
0
    }
8873
0
    firstNode = lastNode = curBlock = nullptr;
8874
0
  }
8875
0
  return NS_OK;
8876
0
}
8877
8878
nsresult
8879
HTMLEditRules::ApplyBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
8880
                               nsAtom& aBlockTag)
8881
0
{
8882
0
  // Intent of this routine is to be used for converting to/from headers,
8883
0
  // paragraphs, pre, and address.  Those blocks that pretty much just contain
8884
0
  // inline things...
8885
0
  MOZ_ASSERT(IsEditorDataAvailable());
8886
0
8887
0
  nsCOMPtr<Element> newBlock;
8888
0
8889
0
  nsCOMPtr<Element> curBlock;
8890
0
  for (auto& curNode : aNodeArray) {
8891
0
    EditorDOMPoint atCurNode(curNode);
8892
0
8893
0
    // Is it already the right kind of block, or an uneditable block?
8894
0
    if (curNode->IsHTMLElement(&aBlockTag) ||
8895
0
        (!HTMLEditorRef().IsEditable(curNode) && IsBlockNode(curNode))) {
8896
0
      // Forget any previous block used for previous inline nodes
8897
0
      curBlock = nullptr;
8898
0
      // Do nothing to this block
8899
0
      continue;
8900
0
    }
8901
0
8902
0
    // If curNode is a address, p, header, address, or pre, replace it with a
8903
0
    // new block of correct type.
8904
0
    // XXX: pre can't hold everything the others can
8905
0
    if (HTMLEditUtils::IsMozDiv(curNode) ||
8906
0
        HTMLEditUtils::IsFormatNode(curNode)) {
8907
0
      // Forget any previous block used for previous inline nodes
8908
0
      curBlock = nullptr;
8909
0
      newBlock =
8910
0
        HTMLEditorRef().ReplaceContainerAndCloneAttributesWithTransaction(
8911
0
                          *curNode->AsElement(), aBlockTag);
8912
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8913
0
        return NS_ERROR_EDITOR_DESTROYED;
8914
0
      }
8915
0
      if (NS_WARN_IF(!newBlock)) {
8916
0
        return NS_ERROR_FAILURE;
8917
0
      }
8918
0
      continue;
8919
0
    }
8920
0
8921
0
    if (HTMLEditUtils::IsTable(curNode) ||
8922
0
        HTMLEditUtils::IsList(curNode) ||
8923
0
        curNode->IsAnyOfHTMLElements(nsGkAtoms::tbody,
8924
0
                                     nsGkAtoms::tr,
8925
0
                                     nsGkAtoms::td,
8926
0
                                     nsGkAtoms::li,
8927
0
                                     nsGkAtoms::blockquote,
8928
0
                                     nsGkAtoms::div)) {
8929
0
      // Forget any previous block used for previous inline nodes
8930
0
      curBlock = nullptr;
8931
0
      // Recursion time
8932
0
      nsTArray<OwningNonNull<nsINode>> childArray;
8933
0
      GetChildNodesForOperation(*curNode, childArray);
8934
0
      if (!childArray.IsEmpty()) {
8935
0
        nsresult rv = ApplyBlockStyle(childArray, aBlockTag);
8936
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
8937
0
          return rv;
8938
0
        }
8939
0
        continue;
8940
0
      }
8941
0
8942
0
      // Make sure we can put a block here
8943
0
      SplitNodeResult splitNodeResult =
8944
0
        MaybeSplitAncestorsForInsertWithTransaction(aBlockTag, atCurNode);
8945
0
      if (NS_WARN_IF(splitNodeResult.Failed())) {
8946
0
        return splitNodeResult.Rv();
8947
0
      }
8948
0
      RefPtr<Element> theBlock =
8949
0
        HTMLEditorRef().CreateNodeWithTransaction(aBlockTag,
8950
0
                                                  splitNodeResult.SplitPoint());
8951
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8952
0
        return NS_ERROR_EDITOR_DESTROYED;
8953
0
      }
8954
0
      if (NS_WARN_IF(!theBlock)) {
8955
0
        return NS_ERROR_FAILURE;
8956
0
      }
8957
0
      // Remember our new block for postprocessing
8958
0
      mNewBlock = theBlock;
8959
0
      continue;
8960
0
    }
8961
0
8962
0
    if (curNode->IsHTMLElement(nsGkAtoms::br)) {
8963
0
      // If the node is a break, we honor it by putting further nodes in a new
8964
0
      // parent
8965
0
      if (curBlock) {
8966
0
        // Forget any previous block used for previous inline nodes
8967
0
        curBlock = nullptr;
8968
0
        nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(*curNode);
8969
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
8970
0
          return NS_ERROR_EDITOR_DESTROYED;
8971
0
        }
8972
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
8973
0
          return rv;
8974
0
        }
8975
0
        continue;
8976
0
      }
8977
0
8978
0
      // The break is the first (or even only) node we encountered.  Create a
8979
0
      // block for it.
8980
0
      SplitNodeResult splitNodeResult =
8981
0
        MaybeSplitAncestorsForInsertWithTransaction(aBlockTag, atCurNode);
8982
0
      if (NS_WARN_IF(splitNodeResult.Failed())) {
8983
0
        return splitNodeResult.Rv();
8984
0
      }
8985
0
      curBlock =
8986
0
        HTMLEditorRef().CreateNodeWithTransaction(aBlockTag,
8987
0
                                                  splitNodeResult.SplitPoint());
8988
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
8989
0
        return NS_ERROR_EDITOR_DESTROYED;
8990
0
      }
8991
0
      if (NS_WARN_IF(!curBlock)) {
8992
0
        return NS_ERROR_FAILURE;
8993
0
      }
8994
0
      // Remember our new block for postprocessing
8995
0
      mNewBlock = curBlock;
8996
0
      // Note: doesn't matter if we set mNewBlock multiple times.
8997
0
      nsresult rv =
8998
0
        HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
8999
0
                                                     *curBlock);
9000
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
9001
0
        return NS_ERROR_EDITOR_DESTROYED;
9002
0
      }
9003
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
9004
0
        return rv;
9005
0
      }
9006
0
      continue;
9007
0
    }
9008
0
9009
0
    if (IsInlineNode(curNode)) {
9010
0
      // If curNode is inline, pull it into curBlock.  Note: it's assumed that
9011
0
      // consecutive inline nodes in aNodeArray are actually members of the
9012
0
      // same block parent.  This happens to be true now as a side effect of
9013
0
      // how aNodeArray is contructed, but some additional logic should be
9014
0
      // added here if that should change
9015
0
      //
9016
0
      // If curNode is a non editable, drop it if we are going to <pre>.
9017
0
      if (&aBlockTag == nsGkAtoms::pre &&
9018
0
          !HTMLEditorRef().IsEditable(curNode)) {
9019
0
        // Do nothing to this block
9020
0
        continue;
9021
0
      }
9022
0
9023
0
      // If no curBlock, make one
9024
0
      if (!curBlock) {
9025
0
        AutoEditorDOMPointOffsetInvalidator lockChild(atCurNode);
9026
0
9027
0
        SplitNodeResult splitNodeResult =
9028
0
          MaybeSplitAncestorsForInsertWithTransaction(aBlockTag, atCurNode);
9029
0
        if (NS_WARN_IF(splitNodeResult.Failed())) {
9030
0
          return splitNodeResult.Rv();
9031
0
        }
9032
0
        curBlock =
9033
0
          HTMLEditorRef().CreateNodeWithTransaction(
9034
0
                            aBlockTag, splitNodeResult.SplitPoint());
9035
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
9036
0
          return NS_ERROR_EDITOR_DESTROYED;
9037
0
        }
9038
0
        if (NS_WARN_IF(!curBlock)) {
9039
0
          return NS_ERROR_FAILURE;
9040
0
        }
9041
0
        // Remember our new block for postprocessing
9042
0
        mNewBlock = curBlock;
9043
0
        // Note: doesn't matter if we set mNewBlock multiple times.
9044
0
      }
9045
0
9046
0
      if (NS_WARN_IF(!atCurNode.IsSet())) {
9047
0
        // This is possible due to mutation events, let's not assert
9048
0
        return NS_ERROR_UNEXPECTED;
9049
0
      }
9050
0
9051
0
      // XXX If curNode is a br, replace it with a return if going to <pre>
9052
0
9053
0
      // This is a continuation of some inline nodes that belong together in
9054
0
      // the same block item.  Use curBlock.
9055
0
      nsresult rv =
9056
0
        HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
9057
0
                                                     *curBlock);
9058
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
9059
0
        return NS_ERROR_EDITOR_DESTROYED;
9060
0
      }
9061
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
9062
0
        return rv;
9063
0
      }
9064
0
    }
9065
0
  }
9066
0
  return NS_OK;
9067
0
}
9068
9069
template<typename PT, typename CT>
9070
SplitNodeResult
9071
HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction(
9072
                 nsAtom& aTag,
9073
                 const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode)
9074
0
{
9075
0
  MOZ_ASSERT(IsEditorDataAvailable());
9076
0
9077
0
  if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
9078
0
    return SplitNodeResult(NS_ERROR_INVALID_ARG);
9079
0
  }
9080
0
  MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
9081
0
9082
0
  RefPtr<Element> host = HTMLEditorRef().GetActiveEditingHost();
9083
0
  if (NS_WARN_IF(!host)) {
9084
0
    return SplitNodeResult(NS_ERROR_FAILURE);
9085
0
  }
9086
0
9087
0
  // The point must be descendant of editing host.
9088
0
  if (NS_WARN_IF(aStartOfDeepestRightNode.GetContainer() != host &&
9089
0
                 !EditorUtils::IsDescendantOf(
9090
0
                   *aStartOfDeepestRightNode.GetContainer(), *host))) {
9091
0
    return SplitNodeResult(NS_ERROR_INVALID_ARG);
9092
0
  }
9093
0
9094
0
  // Look for a node that can legally contain the tag.
9095
0
  EditorRawDOMPoint pointToInsert(aStartOfDeepestRightNode);
9096
0
  for (; pointToInsert.IsSet();
9097
0
       pointToInsert.Set(pointToInsert.GetContainer())) {
9098
0
    // We cannot split active editing host and its ancestor.  So, there is
9099
0
    // no element to contain the specified element.
9100
0
    if (NS_WARN_IF(pointToInsert.GetChild() == host)) {
9101
0
      return SplitNodeResult(NS_ERROR_FAILURE);
9102
0
    }
9103
0
9104
0
    if (HTMLEditorRef().CanContainTag(*pointToInsert.GetContainer(), aTag)) {
9105
0
      // Found an ancestor node which can contain the element.
9106
0
      break;
9107
0
    }
9108
0
  }
9109
0
9110
0
  MOZ_DIAGNOSTIC_ASSERT(pointToInsert.IsSet());
9111
0
9112
0
  // If the point itself can contain the tag, we don't need to split any
9113
0
  // ancestor nodes.  In this case, we should return the given split point
9114
0
  // as is.
9115
0
  if (pointToInsert.GetContainer() == aStartOfDeepestRightNode.GetContainer()) {
9116
0
    return SplitNodeResult(aStartOfDeepestRightNode);
9117
0
  }
9118
0
9119
0
  SplitNodeResult splitNodeResult =
9120
0
    HTMLEditorRef().SplitNodeDeepWithTransaction(
9121
0
                      *pointToInsert.GetChild(),
9122
0
                      aStartOfDeepestRightNode,
9123
0
                      SplitAtEdges::eAllowToCreateEmptyContainer);
9124
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
9125
0
    return SplitNodeResult(NS_ERROR_EDITOR_DESTROYED);
9126
0
  }
9127
0
  NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
9128
0
    "Failed to split the node for insert the element");
9129
0
  return splitNodeResult;
9130
0
}
Unexecuted instantiation: mozilla::SplitNodeResult mozilla::HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(nsAtom&, mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&)
Unexecuted instantiation: mozilla::SplitNodeResult mozilla::HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction<nsINode*, nsIContent*>(nsAtom&, mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&)
9131
9132
nsresult
9133
HTMLEditRules::JoinNearestEditableNodesWithTransaction(
9134
                 nsIContent& aNodeLeft,
9135
                 nsIContent& aNodeRight,
9136
                 EditorDOMPoint* aNewFirstChildOfRightNode)
9137
0
{
9138
0
  MOZ_ASSERT(IsEditorDataAvailable());
9139
0
  MOZ_ASSERT(aNewFirstChildOfRightNode);
9140
0
9141
0
  // Caller responsible for left and right node being the same type
9142
0
  nsCOMPtr<nsINode> parent = aNodeLeft.GetParentNode();
9143
0
  if (NS_WARN_IF(!parent)) {
9144
0
    return NS_ERROR_FAILURE;
9145
0
  }
9146
0
  nsCOMPtr<nsINode> rightParent = aNodeRight.GetParentNode();
9147
0
9148
0
  // If they don't have the same parent, first move the right node to after the
9149
0
  // left one
9150
0
  if (parent != rightParent) {
9151
0
    int32_t parOffset = parent->ComputeIndexOf(&aNodeLeft);
9152
0
    nsresult rv =
9153
0
      HTMLEditorRef().MoveNodeWithTransaction(
9154
0
                        aNodeRight, EditorRawDOMPoint(parent, parOffset));
9155
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
9156
0
      return NS_ERROR_EDITOR_DESTROYED;
9157
0
    }
9158
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
9159
0
      return rv;
9160
0
    }
9161
0
  }
9162
0
9163
0
  EditorDOMPoint ret(&aNodeRight, aNodeLeft.Length());
9164
0
9165
0
  // Separate join rules for differing blocks
9166
0
  if (HTMLEditUtils::IsList(&aNodeLeft) || aNodeLeft.GetAsText()) {
9167
0
    // For lists, merge shallow (wouldn't want to combine list items)
9168
0
    nsresult rv =
9169
0
      HTMLEditorRef().JoinNodesWithTransaction(aNodeLeft, aNodeRight);
9170
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
9171
0
      return NS_ERROR_EDITOR_DESTROYED;
9172
0
    }
9173
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
9174
0
      return rv;
9175
0
    }
9176
0
    *aNewFirstChildOfRightNode = std::move(ret);
9177
0
    return NS_OK;
9178
0
  }
9179
0
9180
0
  // Remember the last left child, and first right child
9181
0
  nsCOMPtr<nsIContent> lastLeft =
9182
0
    HTMLEditorRef().GetLastEditableChild(aNodeLeft);
9183
0
  if (NS_WARN_IF(!lastLeft)) {
9184
0
    return NS_ERROR_FAILURE;
9185
0
  }
9186
0
9187
0
  nsCOMPtr<nsIContent> firstRight =
9188
0
    HTMLEditorRef().GetFirstEditableChild(aNodeRight);
9189
0
  if (NS_WARN_IF(!firstRight)) {
9190
0
    return NS_ERROR_FAILURE;
9191
0
  }
9192
0
9193
0
  // For list items, divs, etc., merge smart
9194
0
  nsresult rv = HTMLEditorRef().JoinNodesWithTransaction(aNodeLeft, aNodeRight);
9195
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
9196
0
    return NS_ERROR_EDITOR_DESTROYED;
9197
0
  }
9198
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
9199
0
    return rv;
9200
0
  }
9201
0
9202
0
  if (lastLeft && firstRight &&
9203
0
      HTMLEditorRef().AreNodesSameType(*lastLeft, *firstRight) &&
9204
0
      (lastLeft->GetAsText() ||
9205
0
       (lastLeft->IsElement() && firstRight->IsElement() &&
9206
0
        CSSEditUtils::ElementsSameStyle(lastLeft->AsElement(),
9207
0
                                        firstRight->AsElement())))) {
9208
0
    nsresult rv =
9209
0
      JoinNearestEditableNodesWithTransaction(*lastLeft, *firstRight,
9210
0
                                              aNewFirstChildOfRightNode);
9211
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
9212
0
      return rv;
9213
0
    }
9214
0
    return NS_OK;
9215
0
  }
9216
0
  *aNewFirstChildOfRightNode = std::move(ret);
9217
0
  return NS_OK;
9218
0
}
9219
9220
Element*
9221
HTMLEditRules::GetTopEnclosingMailCite(nsINode& aNode)
9222
0
{
9223
0
  nsCOMPtr<Element> ret;
9224
0
9225
0
  for (nsCOMPtr<nsINode> node = &aNode; node; node = node->GetParentNode()) {
9226
0
    if ((IsPlaintextEditor() && node->IsHTMLElement(nsGkAtoms::pre)) ||
9227
0
        HTMLEditUtils::IsMailCite(node)) {
9228
0
      ret = node->AsElement();
9229
0
    }
9230
0
    if (node->IsHTMLElement(nsGkAtoms::body)) {
9231
0
      break;
9232
0
    }
9233
0
  }
9234
0
9235
0
  return ret;
9236
0
}
9237
9238
nsresult
9239
HTMLEditRules::CacheInlineStyles(nsINode* aNode)
9240
0
{
9241
0
  MOZ_ASSERT(IsEditorDataAvailable());
9242
0
9243
0
  if (NS_WARN_IF(!aNode)) {
9244
0
    return NS_ERROR_INVALID_ARG;
9245
0
  }
9246
0
9247
0
  nsresult rv = GetInlineStyles(aNode, mCachedStyles);
9248
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
9249
0
    return rv;
9250
0
  }
9251
0
  return NS_OK;
9252
0
}
9253
9254
nsresult
9255
HTMLEditRules::GetInlineStyles(nsINode* aNode,
9256
                               StyleCache aStyleCache[SIZE_STYLE_TABLE])
9257
0
{
9258
0
  MOZ_ASSERT(IsEditorDataAvailable());
9259
0
  MOZ_ASSERT(aNode);
9260
0
9261
0
  bool useCSS = HTMLEditorRef().IsCSSEnabled();
9262
0
9263
0
  for (size_t j = 0; j < SIZE_STYLE_TABLE; ++j) {
9264
0
    // If type-in state is set, don't intervene
9265
0
    bool typeInSet, unused;
9266
0
    HTMLEditorRef().mTypeInState->GetTypingState(
9267
0
                                    typeInSet, unused, aStyleCache[j].tag,
9268
0
                                    aStyleCache[j].attr, nullptr);
9269
0
    if (typeInSet) {
9270
0
      continue;
9271
0
    }
9272
0
9273
0
    bool isSet = false;
9274
0
    nsAutoString outValue;
9275
0
    // Don't use CSS for <font size>, we don't support it usefully (bug 780035)
9276
0
    if (!useCSS || (aStyleCache[j].tag == nsGkAtoms::font &&
9277
0
                    aStyleCache[j].attr == nsGkAtoms::size)) {
9278
0
      isSet =
9279
0
        HTMLEditorRef().IsTextPropertySetByContent(aNode, aStyleCache[j].tag,
9280
0
                                                   aStyleCache[j].attr,
9281
0
                                                   nullptr, &outValue);
9282
0
    } else {
9283
0
      isSet = CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
9284
0
                aNode, aStyleCache[j].tag, aStyleCache[j].attr, outValue,
9285
0
                CSSEditUtils::eComputed);
9286
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
9287
0
        return NS_ERROR_EDITOR_DESTROYED;
9288
0
      }
9289
0
    }
9290
0
    if (isSet) {
9291
0
      aStyleCache[j].mPresent = true;
9292
0
      aStyleCache[j].value.Assign(outValue);
9293
0
    }
9294
0
  }
9295
0
  return NS_OK;
9296
0
}
9297
9298
nsresult
9299
HTMLEditRules::ReapplyCachedStyles()
9300
0
{
9301
0
  MOZ_ASSERT(IsEditorDataAvailable());
9302
0
9303
0
  // The idea here is to examine our cached list of styles and see if any have
9304
0
  // been removed.  If so, add typeinstate for them, so that they will be
9305
0
  // reinserted when new content is added.
9306
0
9307
0
  // remember if we are in css mode
9308
0
  bool useCSS = HTMLEditorRef().IsCSSEnabled();
9309
0
9310
0
  if (!SelectionRef().RangeCount()) {
9311
0
    // Nothing to do
9312
0
    return NS_OK;
9313
0
  }
9314
0
  const RangeBoundary& atStartOfSelection =
9315
0
    SelectionRef().GetRangeAt(0)->StartRef();
9316
0
  nsCOMPtr<nsIContent> selNode =
9317
0
    atStartOfSelection.Container() &&
9318
0
    atStartOfSelection.Container()->IsContent() ?
9319
0
      atStartOfSelection.Container()->AsContent() : nullptr;
9320
0
  if (!selNode) {
9321
0
    // Nothing to do
9322
0
    return NS_OK;
9323
0
  }
9324
0
9325
0
  StyleCache styleAtInsertionPoint[SIZE_STYLE_TABLE];
9326
0
  InitStyleCacheArray(styleAtInsertionPoint);
9327
0
  nsresult rv = GetInlineStyles(selNode, styleAtInsertionPoint);
9328
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
9329
0
    return rv == NS_ERROR_EDITOR_DESTROYED ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
9330
0
  }
9331
0
9332
0
  for (size_t i = 0; i < SIZE_STYLE_TABLE; ++i) {
9333
0
    if (mCachedStyles[i].mPresent) {
9334
0
      bool bFirst, bAny, bAll;
9335
0
      bFirst = bAny = bAll = false;
9336
0
9337
0
      nsAutoString curValue;
9338
0
      if (useCSS) {
9339
0
        // check computed style first in css case
9340
0
        bAny = CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
9341
0
                 selNode, mCachedStyles[i].tag, mCachedStyles[i].attr, curValue,
9342
0
                 CSSEditUtils::eComputed);
9343
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
9344
0
          return NS_ERROR_EDITOR_DESTROYED;
9345
0
        }
9346
0
      }
9347
0
      if (!bAny) {
9348
0
        // then check typeinstate and html style
9349
0
        nsresult rv =
9350
0
          HTMLEditorRef().GetInlinePropertyBase(*mCachedStyles[i].tag,
9351
0
                                                mCachedStyles[i].attr,
9352
0
                                                &(mCachedStyles[i].value),
9353
0
                                                &bFirst, &bAny, &bAll,
9354
0
                                                &curValue);
9355
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
9356
0
          return NS_ERROR_EDITOR_DESTROYED;
9357
0
        }
9358
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
9359
0
          return rv;
9360
0
        }
9361
0
      }
9362
0
      // This style has disappeared through deletion.  Let's add the styles to
9363
0
      // mTypeInState when same style isn't applied to the node already.
9364
0
      if ((!bAny || IsStyleCachePreservingSubAction(mTopLevelEditSubAction)) &&
9365
0
           (!styleAtInsertionPoint[i].mPresent ||
9366
0
            styleAtInsertionPoint[i].value != mCachedStyles[i].value)) {
9367
0
        HTMLEditorRef().mTypeInState->SetProp(mCachedStyles[i].tag,
9368
0
                                              mCachedStyles[i].attr,
9369
0
                                              mCachedStyles[i].value);
9370
0
      }
9371
0
    }
9372
0
  }
9373
0
9374
0
  return NS_OK;
9375
0
}
9376
9377
void
9378
HTMLEditRules::ClearCachedStyles()
9379
0
{
9380
0
  // clear the mPresent bits in mCachedStyles array
9381
0
  for (size_t j = 0; j < SIZE_STYLE_TABLE; j++) {
9382
0
    mCachedStyles[j].mPresent = false;
9383
0
    mCachedStyles[j].value.Truncate();
9384
0
  }
9385
0
}
9386
9387
nsresult
9388
HTMLEditRules::InsertBRElementToEmptyListItemsAndTableCellsInChangedRange()
9389
0
{
9390
0
  MOZ_ASSERT(IsEditorDataAvailable());
9391
0
9392
0
  // Gather list of empty nodes
9393
0
  nsTArray<OwningNonNull<nsINode>> nodeArray;
9394
0
  EmptyEditableFunctor functor(&HTMLEditorRef());
9395
0
  DOMIterator iter;
9396
0
  if (NS_WARN_IF(NS_FAILED(iter.Init(*mDocChangeRange)))) {
9397
0
    return NS_ERROR_FAILURE;
9398
0
  }
9399
0
  iter.AppendList(functor, nodeArray);
9400
0
9401
0
  // Put moz-br's into these empty li's and td's
9402
0
  for (auto& node : nodeArray) {
9403
0
    // Need to put br at END of node.  It may have empty containers in it and
9404
0
    // still pass the "IsEmptyNode" test, and we want the br's to be after
9405
0
    // them.  Also, we want the br to be after the selection if the selection
9406
0
    // is in this node.
9407
0
    EditorRawDOMPoint endOfNode;
9408
0
    endOfNode.SetToEndOf(node);
9409
0
    // XXX This method should return nsreuslt due to may be destroyed by this
9410
0
    //     CreateMozBr() call.
9411
0
    CreateElementResult createMozBrResult = CreateMozBR(endOfNode);
9412
0
    if (NS_WARN_IF(createMozBrResult.Failed())) {
9413
0
      return createMozBrResult.Rv();
9414
0
    }
9415
0
  }
9416
0
  return NS_OK;
9417
0
}
9418
9419
nsresult
9420
HTMLEditRules::AdjustWhitespace()
9421
0
{
9422
0
  MOZ_ASSERT(IsEditorDataAvailable());
9423
0
9424
0
  EditorRawDOMPoint selectionStartPoint(
9425
0
                      EditorBase::GetStartPoint(&SelectionRef()));
9426
0
  if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
9427
0
    return NS_ERROR_FAILURE;
9428
0
  }
9429
0
9430
0
  // Ask whitespace object to tweak nbsp's
9431
0
  nsresult rv =
9432
0
    WSRunObject(&HTMLEditorRef(), selectionStartPoint).AdjustWhitespace();
9433
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
9434
0
    return NS_ERROR_EDITOR_DESTROYED;
9435
0
  }
9436
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
9437
0
    return rv;
9438
0
  }
9439
0
  return NS_OK;
9440
0
}
9441
9442
nsresult
9443
HTMLEditRules::PinSelectionToNewBlock()
9444
0
{
9445
0
  MOZ_ASSERT(IsEditorDataAvailable());
9446
0
9447
0
  if (!SelectionRef().IsCollapsed()) {
9448
0
    return NS_OK;
9449
0
  }
9450
0
9451
0
  if (NS_WARN_IF(!mNewBlock)) {
9452
0
    return NS_ERROR_NULL_POINTER;
9453
0
  }
9454
0
9455
0
  EditorRawDOMPoint selectionStartPoint(
9456
0
                      EditorBase::GetStartPoint(&SelectionRef()));
9457
0
  if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
9458
0
    return NS_ERROR_FAILURE;
9459
0
  }
9460
0
9461
0
  // Use ranges and nsRange::CompareNodeToRange() to compare selection start
9462
0
  // to new block.
9463
0
  // XXX It's too expensive to use nsRange and set it only for comparing a
9464
0
  //     DOM point with a node.
9465
0
  RefPtr<nsRange> range = new nsRange(selectionStartPoint.GetContainer());
9466
0
  nsresult rv = range->CollapseTo(selectionStartPoint);
9467
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
9468
0
    return rv;
9469
0
  }
9470
0
9471
0
  bool nodeBefore, nodeAfter;
9472
0
  rv = nsRange::CompareNodeToRange(mNewBlock, range, &nodeBefore, &nodeAfter);
9473
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
9474
0
    return rv;
9475
0
  }
9476
0
9477
0
  if (nodeBefore && nodeAfter) {
9478
0
    return NS_OK;  // selection is inside block
9479
0
  }
9480
0
9481
0
  if (nodeBefore) {
9482
0
    // selection is after block.  put at end of block.
9483
0
    nsCOMPtr<nsINode> tmp = HTMLEditorRef().GetLastEditableChild(*mNewBlock);
9484
0
    if (!tmp) {
9485
0
      tmp = mNewBlock;
9486
0
    }
9487
0
    EditorRawDOMPoint endPoint;
9488
0
    if (EditorBase::IsTextNode(tmp) ||
9489
0
        HTMLEditorRef().IsContainer(tmp)) {
9490
0
      endPoint.SetToEndOf(tmp);
9491
0
    } else {
9492
0
      endPoint.Set(tmp);
9493
0
      if (NS_WARN_IF(!endPoint.AdvanceOffset())) {
9494
0
        return NS_ERROR_FAILURE;
9495
0
      }
9496
0
    }
9497
0
    ErrorResult error;
9498
0
    SelectionRef().Collapse(endPoint, error);
9499
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
9500
0
      error.SuppressException();
9501
0
      return NS_ERROR_EDITOR_DESTROYED;
9502
0
    }
9503
0
    if (NS_WARN_IF(error.Failed())) {
9504
0
      return error.StealNSResult();
9505
0
    }
9506
0
    return NS_OK;
9507
0
  }
9508
0
9509
0
  // selection is before block.  put at start of block.
9510
0
  nsCOMPtr<nsINode> tmp = HTMLEditorRef().GetFirstEditableChild(*mNewBlock);
9511
0
  if (!tmp) {
9512
0
    tmp = mNewBlock;
9513
0
  }
9514
0
  EditorRawDOMPoint atStartOfBlock;
9515
0
  if (EditorBase::IsTextNode(tmp) ||
9516
0
      HTMLEditorRef().IsContainer(tmp)) {
9517
0
    atStartOfBlock.Set(tmp);
9518
0
  } else {
9519
0
    atStartOfBlock.Set(tmp, 0);
9520
0
  }
9521
0
  ErrorResult error;
9522
0
  SelectionRef().Collapse(atStartOfBlock, error);
9523
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
9524
0
    error.SuppressException();
9525
0
    return NS_ERROR_EDITOR_DESTROYED;
9526
0
  }
9527
0
  if (NS_WARN_IF(error.Failed())) {
9528
0
    return error.StealNSResult();
9529
0
  }
9530
0
  return NS_OK;
9531
0
}
9532
9533
void
9534
HTMLEditRules::CheckInterlinePosition()
9535
0
{
9536
0
  MOZ_ASSERT(IsEditorDataAvailable());
9537
0
9538
0
  // If the selection isn't collapsed, do nothing.
9539
0
  if (!SelectionRef().IsCollapsed()) {
9540
0
    return;
9541
0
  }
9542
0
9543
0
  // Get the (collapsed) selection location
9544
0
  nsRange* firstRange = SelectionRef().GetRangeAt(0);
9545
0
  if (NS_WARN_IF(!firstRange)) {
9546
0
    return;
9547
0
  }
9548
0
9549
0
  EditorDOMPoint atStartOfSelection(firstRange->StartRef());
9550
0
  if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
9551
0
    return;
9552
0
  }
9553
0
  MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
9554
0
9555
0
  // First, let's check to see if we are after a <br>.  We take care of this
9556
0
  // special-case first so that we don't accidentally fall through into one of
9557
0
  // the other conditionals.
9558
0
  nsCOMPtr<nsIContent> node =
9559
0
    HTMLEditorRef().GetPreviousEditableHTMLNodeInBlock(atStartOfSelection);
9560
0
  if (node && node->IsHTMLElement(nsGkAtoms::br)) {
9561
0
    IgnoredErrorResult ignoredError;
9562
0
    SelectionRef().SetInterlinePosition(true, ignoredError);
9563
0
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
9564
0
      "Failed to set interline position");
9565
0
    return;
9566
0
  }
9567
0
9568
0
  // Are we after a block?  If so try set caret to following content
9569
0
  if (atStartOfSelection.GetChild()) {
9570
0
    node = HTMLEditorRef().GetPriorHTMLSibling(atStartOfSelection.GetChild());
9571
0
  } else {
9572
0
    node = nullptr;
9573
0
  }
9574
0
  if (node && IsBlockNode(*node)) {
9575
0
    IgnoredErrorResult ignoredError;
9576
0
    SelectionRef().SetInterlinePosition(true, ignoredError);
9577
0
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
9578
0
      "Failed to set interline position");
9579
0
    return;
9580
0
  }
9581
0
9582
0
  // Are we before a block?  If so try set caret to prior content
9583
0
  if (atStartOfSelection.GetChild()) {
9584
0
    node = HTMLEditorRef().GetNextHTMLSibling(atStartOfSelection.GetChild());
9585
0
  } else {
9586
0
    node = nullptr;
9587
0
  }
9588
0
  if (node && IsBlockNode(*node)) {
9589
0
    IgnoredErrorResult ignoredError;
9590
0
    SelectionRef().SetInterlinePosition(false, ignoredError);
9591
0
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
9592
0
      "Failed to unset interline position");
9593
0
  }
9594
0
}
9595
9596
nsresult
9597
HTMLEditRules::AdjustSelection(nsIEditor::EDirection aAction)
9598
0
{
9599
0
  MOZ_ASSERT(IsEditorDataAvailable());
9600
0
9601
0
  // if the selection isn't collapsed, do nothing.
9602
0
  // moose: one thing to do instead is check for the case of
9603
0
  // only a single break selected, and collapse it.  Good thing?  Beats me.
9604
0
  if (!SelectionRef().IsCollapsed()) {
9605
0
    return NS_OK;
9606
0
  }
9607
0
9608
0
  // get the (collapsed) selection location
9609
0
  EditorDOMPoint point(EditorBase::GetStartPoint(&SelectionRef()));
9610
0
  if (NS_WARN_IF(!point.IsSet())) {
9611
0
    return NS_ERROR_FAILURE;
9612
0
  }
9613
0
9614
0
  // are we in an editable node?
9615
0
  while (!HTMLEditorRef().IsEditable(point.GetContainer())) {
9616
0
    // scan up the tree until we find an editable place to be
9617
0
    point.Set(point.GetContainer());
9618
0
    if (NS_WARN_IF(!point.IsSet())) {
9619
0
      return NS_ERROR_FAILURE;
9620
0
    }
9621
0
  }
9622
0
9623
0
  // make sure we aren't in an empty block - user will see no cursor.  If this
9624
0
  // is happening, put a <br> in the block if allowed.
9625
0
  RefPtr<Element> theblock = HTMLEditorRef().GetBlock(*point.GetContainer());
9626
0
9627
0
  if (theblock && HTMLEditorRef().IsEditable(theblock)) {
9628
0
    bool isEmptyNode;
9629
0
    nsresult rv =
9630
0
      HTMLEditorRef().IsEmptyNode(theblock, &isEmptyNode, false, false);
9631
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
9632
0
      return rv;
9633
0
    }
9634
0
    // check if br can go into the destination node
9635
0
    if (isEmptyNode &&
9636
0
        HTMLEditorRef().CanContainTag(*point.GetContainer(), *nsGkAtoms::br)) {
9637
0
      Element* rootElement = HTMLEditorRef().GetRoot();
9638
0
      if (NS_WARN_IF(!rootElement)) {
9639
0
        return NS_ERROR_FAILURE;
9640
0
      }
9641
0
      if (point.GetContainer() == rootElement) {
9642
0
        // Our root node is completely empty. Don't add a <br> here.
9643
0
        // AfterEditInner() will add one for us when it calls
9644
0
        // CreateBogusNodeIfNeeded()!
9645
0
        return NS_OK;
9646
0
      }
9647
0
9648
0
      // we know we can skip the rest of this routine given the cirumstance
9649
0
      CreateElementResult createMozBrResult = CreateMozBR(point);
9650
0
      if (NS_WARN_IF(createMozBrResult.Failed())) {
9651
0
        return createMozBrResult.Rv();
9652
0
      }
9653
0
      return NS_OK;
9654
0
    }
9655
0
  }
9656
0
9657
0
  // are we in a text node?
9658
0
  if (point.IsInTextNode()) {
9659
0
    return NS_OK; // we LIKE it when we are in a text node.  that RULZ
9660
0
  }
9661
0
9662
0
  // do we need to insert a special mozBR?  We do if we are:
9663
0
  // 1) prior node is in same block where selection is AND
9664
0
  // 2) prior node is a br AND
9665
0
  // 3) that br is not visible
9666
0
9667
0
  nsCOMPtr<nsIContent> nearNode =
9668
0
    HTMLEditorRef().GetPreviousEditableHTMLNode(point);
9669
0
  if (nearNode) {
9670
0
    // is nearNode also a descendant of same block?
9671
0
    RefPtr<Element> block = HTMLEditorRef().GetBlock(*point.GetContainer());
9672
0
    RefPtr<Element> nearBlock = HTMLEditorRef().GetBlockNodeParent(nearNode);
9673
0
    if (block && block == nearBlock) {
9674
0
      if (nearNode && TextEditUtils::IsBreak(nearNode)) {
9675
0
        if (!HTMLEditorRef().IsVisibleBRElement(nearNode)) {
9676
0
          // need to insert special moz BR. Why?  Because if we don't
9677
0
          // the user will see no new line for the break.  Also, things
9678
0
          // like table cells won't grow in height.
9679
0
          CreateElementResult createMozBrResult = CreateMozBR(point);
9680
0
          if (NS_WARN_IF(createMozBrResult.Failed())) {
9681
0
            return createMozBrResult.Rv();
9682
0
          }
9683
0
          point.Set(createMozBrResult.GetNewNode());
9684
0
          // selection stays *before* moz-br, sticking to it
9685
0
          ErrorResult error;
9686
0
          SelectionRef().SetInterlinePosition(true, error);
9687
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
9688
0
            error.SuppressException();
9689
0
            return NS_ERROR_EDITOR_DESTROYED;
9690
0
          }
9691
0
          NS_WARNING_ASSERTION(!error.Failed(),
9692
0
            "Failed to set interline position");
9693
0
          error = NS_OK;
9694
0
          SelectionRef().Collapse(point, error);
9695
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
9696
0
            error.SuppressException();
9697
0
            return NS_ERROR_EDITOR_DESTROYED;
9698
0
          }
9699
0
          if (NS_WARN_IF(error.Failed())) {
9700
0
            return error.StealNSResult();
9701
0
          }
9702
0
        } else {
9703
0
          nsCOMPtr<nsIContent> nextNode =
9704
0
            HTMLEditorRef().GetNextEditableHTMLNodeInBlock(*nearNode);
9705
0
          if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
9706
0
            // selection between br and mozbr.  make it stick to mozbr
9707
0
            // so that it will be on blank line.
9708
0
            IgnoredErrorResult ignoredError;
9709
0
            SelectionRef().SetInterlinePosition(true, ignoredError);
9710
0
            NS_WARNING_ASSERTION(!ignoredError.Failed(),
9711
0
              "Failed to set interline position");
9712
0
          }
9713
0
        }
9714
0
      }
9715
0
    }
9716
0
  }
9717
0
9718
0
  // we aren't in a textnode: are we adjacent to text or a break or an image?
9719
0
  nearNode = HTMLEditorRef().GetPreviousEditableHTMLNodeInBlock(point);
9720
0
  if (nearNode && (TextEditUtils::IsBreak(nearNode) ||
9721
0
                   EditorBase::IsTextNode(nearNode) ||
9722
0
                   HTMLEditUtils::IsImage(nearNode) ||
9723
0
                   nearNode->IsHTMLElement(nsGkAtoms::hr))) {
9724
0
    // this is a good place for the caret to be
9725
0
    return NS_OK;
9726
0
  }
9727
0
  nearNode = HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
9728
0
  if (nearNode && (TextEditUtils::IsBreak(nearNode) ||
9729
0
                   EditorBase::IsTextNode(nearNode) ||
9730
0
                   nearNode->IsAnyOfHTMLElements(nsGkAtoms::img,
9731
0
                                                 nsGkAtoms::hr))) {
9732
0
    return NS_OK; // this is a good place for the caret to be
9733
0
  }
9734
0
9735
0
  // look for a nearby text node.
9736
0
  // prefer the correct direction.
9737
0
  nearNode = FindNearEditableNode(point, aAction);
9738
0
  if (!nearNode) {
9739
0
    return NS_OK;
9740
0
  }
9741
0
9742
0
  EditorDOMPoint pt = GetGoodSelPointForNode(*nearNode, aAction);
9743
0
  ErrorResult error;
9744
0
  SelectionRef().Collapse(pt, error);
9745
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
9746
0
    error.SuppressException();
9747
0
    return NS_ERROR_EDITOR_DESTROYED;
9748
0
  }
9749
0
  if (NS_WARN_IF(error.Failed())) {
9750
0
    return error.StealNSResult();
9751
0
  }
9752
0
  return NS_OK;
9753
0
}
9754
9755
template<typename PT, typename CT>
9756
nsIContent*
9757
HTMLEditRules::FindNearEditableNode(const EditorDOMPointBase<PT, CT>& aPoint,
9758
                                    nsIEditor::EDirection aDirection)
9759
0
{
9760
0
  MOZ_ASSERT(IsEditorDataAvailable());
9761
0
9762
0
  if (NS_WARN_IF(!aPoint.IsSet())) {
9763
0
    return nullptr;
9764
0
  }
9765
0
  MOZ_ASSERT(aPoint.IsSetAndValid());
9766
0
9767
0
  nsIContent* nearNode = nullptr;
9768
0
  if (aDirection == nsIEditor::ePrevious) {
9769
0
    nearNode = HTMLEditorRef().GetPreviousEditableHTMLNode(aPoint);
9770
0
    if (!nearNode) {
9771
0
      return nullptr; // Not illegal.
9772
0
    }
9773
0
  } else {
9774
0
    nearNode = HTMLEditorRef().GetNextEditableHTMLNode(aPoint);
9775
0
    if (NS_WARN_IF(!nearNode)) {
9776
0
      // Perhaps, illegal because the node pointed by aPoint isn't editable
9777
0
      // and nobody of previous nodes is editable.
9778
0
      return nullptr;
9779
0
    }
9780
0
  }
9781
0
9782
0
  // scan in the right direction until we find an eligible text node,
9783
0
  // but don't cross any breaks, images, or table elements.
9784
0
  // XXX This comment sounds odd.  |nearNode| may have already crossed breaks
9785
0
  //     and/or images.
9786
0
  while (nearNode && !(EditorBase::IsTextNode(nearNode) ||
9787
0
                       TextEditUtils::IsBreak(nearNode) ||
9788
0
                       HTMLEditUtils::IsImage(nearNode))) {
9789
0
    if (aDirection == nsIEditor::ePrevious) {
9790
0
      nearNode = HTMLEditorRef().GetPreviousEditableHTMLNode(*nearNode);
9791
0
      if (NS_WARN_IF(!nearNode)) {
9792
0
        return nullptr;
9793
0
      }
9794
0
    } else {
9795
0
      nearNode = HTMLEditorRef().GetNextEditableHTMLNode(*nearNode);
9796
0
      if (NS_WARN_IF(!nearNode)) {
9797
0
        return nullptr;
9798
0
      }
9799
0
    }
9800
0
  }
9801
0
9802
0
  // don't cross any table elements
9803
0
  if (InDifferentTableElements(nearNode, aPoint.GetContainer())) {
9804
0
    return nullptr;
9805
0
  }
9806
0
9807
0
  // otherwise, ok, we have found a good spot to put the selection
9808
0
  return nearNode;
9809
0
}
9810
9811
bool
9812
HTMLEditRules::InDifferentTableElements(nsINode* aNode1,
9813
                                        nsINode* aNode2)
9814
0
{
9815
0
  MOZ_ASSERT(aNode1 && aNode2);
9816
0
9817
0
  while (aNode1 && !HTMLEditUtils::IsTableElement(aNode1)) {
9818
0
    aNode1 = aNode1->GetParentNode();
9819
0
  }
9820
0
9821
0
  while (aNode2 && !HTMLEditUtils::IsTableElement(aNode2)) {
9822
0
    aNode2 = aNode2->GetParentNode();
9823
0
  }
9824
0
9825
0
  return aNode1 != aNode2;
9826
0
}
9827
9828
9829
nsresult
9830
HTMLEditRules::RemoveEmptyNodesInChangedRange()
9831
0
{
9832
0
  MOZ_ASSERT(IsEditorDataAvailable());
9833
0
9834
0
  // Some general notes on the algorithm used here: the goal is to examine all
9835
0
  // the nodes in mDocChangeRange, and remove the empty ones.  We do this by
9836
0
  // using a content iterator to traverse all the nodes in the range, and
9837
0
  // placing the empty nodes into an array.  After finishing the iteration, we
9838
0
  // delete the empty nodes in the array.  (They cannot be deleted as we find
9839
0
  // them because that would invalidate the iterator.)
9840
0
  //
9841
0
  // Since checking to see if a node is empty can be costly for nodes with many
9842
0
  // descendants, there are some optimizations made.  I rely on the fact that
9843
0
  // the iterator is post-order: it will visit children of a node before
9844
0
  // visiting the parent node.  So if I find that a child node is not empty, I
9845
0
  // know that its parent is not empty without even checking.  So I put the
9846
0
  // parent on a "skipList" which is just a voidArray of nodes I can skip the
9847
0
  // empty check on.  If I encounter a node on the skiplist, i skip the
9848
0
  // processing for that node and replace its slot in the skiplist with that
9849
0
  // node's parent.
9850
0
  //
9851
0
  // An interesting idea is to go ahead and regard parent nodes that are NOT on
9852
0
  // the skiplist as being empty (without even doing the IsEmptyNode check) on
9853
0
  // the theory that if they weren't empty, we would have encountered a
9854
0
  // non-empty child earlier and thus put this parent node on the skiplist.
9855
0
  //
9856
0
  // Unfortunately I can't use that strategy here, because the range may
9857
0
  // include some children of a node while excluding others.  Thus I could find
9858
0
  // all the _examined_ children empty, but still not have an empty parent.
9859
0
9860
0
  // need an iterator
9861
0
  nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
9862
0
9863
0
  nsresult rv = iter->Init(mDocChangeRange);
9864
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
9865
0
    return rv;
9866
0
  }
9867
0
9868
0
  nsTArray<OwningNonNull<nsINode>> arrayOfEmptyNodes, arrayOfEmptyCites, skipList;
9869
0
9870
0
  // Check for empty nodes
9871
0
  while (!iter->IsDone()) {
9872
0
    OwningNonNull<nsINode> node = *iter->GetCurrentNode();
9873
0
9874
0
    nsCOMPtr<nsINode> parent = node->GetParentNode();
9875
0
9876
0
    size_t idx = skipList.IndexOf(node);
9877
0
    if (idx != skipList.NoIndex) {
9878
0
      // This node is on our skip list.  Skip processing for this node, and
9879
0
      // replace its value in the skip list with the value of its parent
9880
0
      if (parent) {
9881
0
        skipList[idx] = parent;
9882
0
      }
9883
0
    } else {
9884
0
      bool isCandidate = false;
9885
0
      bool isEmptyNode = false;
9886
0
      bool isMailCite = false;
9887
0
9888
0
      if (node->IsElement()) {
9889
0
        if (node->IsHTMLElement(nsGkAtoms::body)) {
9890
0
          // Don't delete the body
9891
0
        } else if ((isMailCite = HTMLEditUtils::IsMailCite(node)) ||
9892
0
                   node->IsHTMLElement(nsGkAtoms::a) ||
9893
0
                   HTMLEditUtils::IsInlineStyle(node) ||
9894
0
                   HTMLEditUtils::IsList(node) ||
9895
0
                   node->IsHTMLElement(nsGkAtoms::div)) {
9896
0
          // Only consider certain nodes to be empty for purposes of removal
9897
0
          isCandidate = true;
9898
0
        } else if (HTMLEditUtils::IsFormatNode(node) ||
9899
0
                   HTMLEditUtils::IsListItem(node) ||
9900
0
                   node->IsHTMLElement(nsGkAtoms::blockquote)) {
9901
0
          // These node types are candidates if selection is not in them.  If
9902
0
          // it is one of these, don't delete if selection inside.  This is so
9903
0
          // we can create empty headings, etc., for the user to type into.
9904
0
          bool isSelectionEndInNode;
9905
0
          rv = SelectionEndpointInNode(node, &isSelectionEndInNode);
9906
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
9907
0
            return rv;
9908
0
          }
9909
0
          if (!isSelectionEndInNode) {
9910
0
            isCandidate = true;
9911
0
          }
9912
0
        }
9913
0
      }
9914
0
9915
0
      if (isCandidate) {
9916
0
        // We delete mailcites even if they have a solo br in them.  Other
9917
0
        // nodes we require to be empty.
9918
0
        rv = HTMLEditorRef().IsEmptyNode(node, &isEmptyNode,
9919
0
                                         isMailCite, true);
9920
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
9921
0
          return rv;
9922
0
        }
9923
0
        if (isEmptyNode) {
9924
0
          if (isMailCite) {
9925
0
            // mailcites go on a separate list from other empty nodes
9926
0
            arrayOfEmptyCites.AppendElement(*node);
9927
0
          } else {
9928
0
            arrayOfEmptyNodes.AppendElement(*node);
9929
0
          }
9930
0
        }
9931
0
      }
9932
0
9933
0
      if (!isEmptyNode && parent) {
9934
0
        // put parent on skip list
9935
0
        skipList.AppendElement(*parent);
9936
0
      }
9937
0
    }
9938
0
9939
0
    iter->Next();
9940
0
  }
9941
0
9942
0
  // now delete the empty nodes
9943
0
  for (OwningNonNull<nsINode>& delNode : arrayOfEmptyNodes) {
9944
0
    if (HTMLEditorRef().IsModifiableNode(delNode)) {
9945
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*delNode);
9946
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
9947
0
        return NS_ERROR_EDITOR_DESTROYED;
9948
0
      }
9949
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
9950
0
        return rv;
9951
0
      }
9952
0
    }
9953
0
  }
9954
0
9955
0
  // Now delete the empty mailcites.  This is a separate step because we want
9956
0
  // to pull out any br's and preserve them.
9957
0
  for (OwningNonNull<nsINode>& delNode : arrayOfEmptyCites) {
9958
0
    bool isEmptyNode;
9959
0
    rv = HTMLEditorRef().IsEmptyNode(delNode, &isEmptyNode, false, true);
9960
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
9961
0
      return rv;
9962
0
    }
9963
0
    if (!isEmptyNode) {
9964
0
      // We are deleting a cite that has just a br.  We want to delete cite,
9965
0
      // but preserve br.
9966
0
      RefPtr<Element> brElement =
9967
0
        HTMLEditorRef().InsertBrElementWithTransaction(
9968
0
                          SelectionRef(), EditorRawDOMPoint(delNode));
9969
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
9970
0
        return NS_ERROR_EDITOR_DESTROYED;
9971
0
      }
9972
0
      if (NS_WARN_IF(!brElement)) {
9973
0
        return NS_ERROR_FAILURE;
9974
0
      }
9975
0
    }
9976
0
    rv = HTMLEditorRef().DeleteNodeWithTransaction(*delNode);
9977
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
9978
0
      return NS_ERROR_EDITOR_DESTROYED;
9979
0
    }
9980
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
9981
0
      return rv;
9982
0
    }
9983
0
  }
9984
0
9985
0
  return NS_OK;
9986
0
}
9987
9988
nsresult
9989
HTMLEditRules::SelectionEndpointInNode(nsINode* aNode,
9990
                                       bool* aResult)
9991
0
{
9992
0
  MOZ_ASSERT(IsEditorDataAvailable());
9993
0
9994
0
  NS_ENSURE_TRUE(aNode && aResult, NS_ERROR_NULL_POINTER);
9995
0
9996
0
  *aResult = false;
9997
0
9998
0
  uint32_t rangeCount = SelectionRef().RangeCount();
9999
0
  for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
10000
0
    RefPtr<nsRange> range = SelectionRef().GetRangeAt(rangeIdx);
10001
0
    nsINode* startContainer = range->GetStartContainer();
10002
0
    if (startContainer) {
10003
0
      if (aNode == startContainer) {
10004
0
        *aResult = true;
10005
0
        return NS_OK;
10006
0
      }
10007
0
      if (EditorUtils::IsDescendantOf(*startContainer, *aNode)) {
10008
0
        *aResult = true;
10009
0
        return NS_OK;
10010
0
      }
10011
0
    }
10012
0
    nsINode* endContainer = range->GetEndContainer();
10013
0
    if (startContainer == endContainer) {
10014
0
      continue;
10015
0
    }
10016
0
    if (endContainer) {
10017
0
      if (aNode == endContainer) {
10018
0
        *aResult = true;
10019
0
        return NS_OK;
10020
0
      }
10021
0
      if (EditorUtils::IsDescendantOf(*endContainer, *aNode)) {
10022
0
        *aResult = true;
10023
0
        return NS_OK;
10024
0
      }
10025
0
    }
10026
0
  }
10027
0
  return NS_OK;
10028
0
}
10029
10030
bool
10031
HTMLEditRules::IsEmptyInline(nsINode& aNode)
10032
0
{
10033
0
  MOZ_ASSERT(IsEditorDataAvailable());
10034
0
10035
0
  if (IsInlineNode(aNode) && HTMLEditorRef().IsContainer(&aNode)) {
10036
0
    bool isEmpty = true;
10037
0
    HTMLEditorRef().IsEmptyNode(&aNode, &isEmpty);
10038
0
    return isEmpty;
10039
0
  }
10040
0
  return false;
10041
0
}
10042
10043
10044
bool
10045
HTMLEditRules::ListIsEmptyLine(nsTArray<OwningNonNull<nsINode>>& aArrayOfNodes)
10046
0
{
10047
0
  MOZ_ASSERT(IsEditorDataAvailable());
10048
0
10049
0
  // We have a list of nodes which we are candidates for being moved into a new
10050
0
  // block.  Determine if it's anything more than a blank line.  Look for
10051
0
  // editable content above and beyond one single BR.
10052
0
  if (NS_WARN_IF(!aArrayOfNodes.Length())) {
10053
0
    return true;
10054
0
  }
10055
0
10056
0
  int32_t brCount = 0;
10057
0
  for (auto& node : aArrayOfNodes) {
10058
0
    if (!HTMLEditorRef().IsEditable(node)) {
10059
0
      continue;
10060
0
    }
10061
0
    if (TextEditUtils::IsBreak(node)) {
10062
0
      // First break doesn't count
10063
0
      if (brCount) {
10064
0
        return false;
10065
0
      }
10066
0
      brCount++;
10067
0
    } else if (IsEmptyInline(node)) {
10068
0
      // Empty inline, keep looking
10069
0
    } else {
10070
0
      return false;
10071
0
    }
10072
0
  }
10073
0
  return true;
10074
0
}
10075
10076
10077
nsresult
10078
HTMLEditRules::PopListItem(nsIContent& aListItem,
10079
                           bool* aOutOfList)
10080
0
{
10081
0
  MOZ_ASSERT(IsEditorDataAvailable());
10082
0
10083
0
  if (aOutOfList) {
10084
0
    *aOutOfList = false;
10085
0
  }
10086
0
10087
0
  if (NS_WARN_IF(!aListItem.GetParent()) ||
10088
0
      NS_WARN_IF(!aListItem.GetParent()->GetParentNode()) ||
10089
0
      !HTMLEditUtils::IsListItem(&aListItem)) {
10090
0
    return NS_ERROR_FAILURE;
10091
0
  }
10092
0
10093
0
  // if it's first or last list item, don't need to split the list
10094
0
  // otherwise we do.
10095
0
  bool isFirstListItem = HTMLEditorRef().IsFirstEditableChild(&aListItem);
10096
0
  bool isLastListItem = HTMLEditorRef().IsLastEditableChild(&aListItem);
10097
0
10098
0
  nsCOMPtr<nsIContent> leftListNode = aListItem.GetParent();
10099
0
10100
0
  // If it's at middle of parent list element, split the parent list element.
10101
0
  // Then, aListItem becomes the first list item of the right list element.
10102
0
  nsCOMPtr<nsIContent> listItem(&aListItem);
10103
0
  if (!isFirstListItem && !isLastListItem) {
10104
0
    EditorDOMPoint atListItem(listItem);
10105
0
    if (NS_WARN_IF(!atListItem.IsSet())) {
10106
0
      return NS_ERROR_INVALID_ARG;
10107
0
    }
10108
0
    MOZ_ASSERT(atListItem.IsSetAndValid());
10109
0
    ErrorResult error;
10110
0
    leftListNode = HTMLEditorRef().SplitNodeWithTransaction(atListItem, error);
10111
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
10112
0
      error.SuppressException();
10113
0
      return NS_ERROR_EDITOR_DESTROYED;
10114
0
    }
10115
0
    if (NS_WARN_IF(error.Failed())) {
10116
0
      return error.StealNSResult();
10117
0
    }
10118
0
  }
10119
0
10120
0
  // In most cases, insert the list item into the new left list node..
10121
0
  EditorDOMPoint pointToInsertListItem(leftListNode);
10122
0
  if (NS_WARN_IF(!pointToInsertListItem.IsSet())) {
10123
0
    return NS_ERROR_FAILURE;
10124
0
  }
10125
0
  MOZ_ASSERT(pointToInsertListItem.IsSetAndValid());
10126
0
10127
0
  // But when the list item was the first child of the right list, it should
10128
0
  // be inserted between the both list elements.  This allows user to hit
10129
0
  // Enter twice at a list item breaks the parent list node.
10130
0
  if (!isFirstListItem) {
10131
0
    DebugOnly<bool> advanced = pointToInsertListItem.AdvanceOffset();
10132
0
    NS_WARNING_ASSERTION(advanced,
10133
0
      "Failed to advance offset to right list node");
10134
0
  }
10135
0
10136
0
  nsresult rv =
10137
0
    HTMLEditorRef().MoveNodeWithTransaction(*listItem, pointToInsertListItem);
10138
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10139
0
    return NS_ERROR_EDITOR_DESTROYED;
10140
0
  }
10141
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10142
0
    return rv;
10143
0
  }
10144
0
10145
0
  // unwrap list item contents if they are no longer in a list
10146
0
  // XXX If the parent list element is a child of another list element
10147
0
  //     (although invalid tree), the list item element won't be unwrapped.
10148
0
  //     That makes the parent ancestor element tree valid, but might be
10149
0
  //     unexpected result.
10150
0
  // XXX If aListItem is <dl> or <dd> and current parent is <ul> or <ol>,
10151
0
  //     the list items won't be unwrapped.  If aListItem is <li> and its
10152
0
  //     current parent is <dl>, there is same issue.
10153
0
  if (!HTMLEditUtils::IsList(pointToInsertListItem.GetContainer()) &&
10154
0
      HTMLEditUtils::IsListItem(listItem)) {
10155
0
    rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(
10156
0
                           *listItem->AsElement());
10157
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
10158
0
      return NS_ERROR_EDITOR_DESTROYED;
10159
0
    }
10160
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
10161
0
      return rv;
10162
0
    }
10163
0
    if (aOutOfList) {
10164
0
      *aOutOfList = true;
10165
0
    }
10166
0
  }
10167
0
  return NS_OK;
10168
0
}
10169
10170
nsresult
10171
HTMLEditRules::RemoveListStructure(Element& aListElement)
10172
0
{
10173
0
  MOZ_ASSERT(IsEditorDataAvailable());
10174
0
  MOZ_ASSERT(HTMLEditUtils::IsList(&aListElement));
10175
0
10176
0
  while (aListElement.GetFirstChild()) {
10177
0
    OwningNonNull<nsIContent> child = *aListElement.GetFirstChild();
10178
0
10179
0
    if (HTMLEditUtils::IsListItem(child)) {
10180
0
      bool isOutOfList;
10181
0
      // Keep popping it out until it's not in a list anymore
10182
0
      // XXX Using PopuListItem() is too expensive for this purpose.  Looks
10183
0
      //     like the reason why this method uses it is, only this loop
10184
0
      //     wants to work with first child of aList.  However, what it
10185
0
      //     actually does is removing <li> as container.  So, just using
10186
0
      //     RemoveBlockContainerWithTransaction() is reasonable.
10187
0
      // XXX This loop means that if aListElement is is a child of another
10188
0
      //     list element (although it's invalid tree), this moves the
10189
0
      //     list item to outside of aListElement's parent.  Is that really
10190
0
      //     intentional behavior?
10191
0
      do {
10192
0
        nsresult rv = PopListItem(child, &isOutOfList);
10193
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
10194
0
          return rv;
10195
0
        }
10196
0
      } while (!isOutOfList);
10197
0
      continue;
10198
0
    }
10199
0
10200
0
    if (HTMLEditUtils::IsList(child)) {
10201
0
      nsresult rv = RemoveListStructure(*child->AsElement());
10202
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
10203
0
        return rv;
10204
0
      }
10205
0
      continue;
10206
0
    }
10207
0
10208
0
    // Delete any non-list items for now
10209
0
    // XXX This is not HTML5 aware.  HTML5 allows all list elements to have
10210
0
    //     <script> and <template> and <dl> element to have <div> to group
10211
0
    //     some <dt> and <dd> elements.  So, this may break valid children.
10212
0
    nsresult rv = HTMLEditorRef().DeleteNodeWithTransaction(*child);
10213
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
10214
0
      return NS_ERROR_EDITOR_DESTROYED;
10215
0
    }
10216
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
10217
0
      return rv;
10218
0
    }
10219
0
  }
10220
0
10221
0
  // Delete the now-empty list
10222
0
  nsresult rv =
10223
0
    HTMLEditorRef().RemoveBlockContainerWithTransaction(aListElement);
10224
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10225
0
    return NS_ERROR_EDITOR_DESTROYED;
10226
0
  }
10227
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10228
0
    return rv;
10229
0
  }
10230
0
  return NS_OK;
10231
0
}
10232
10233
nsresult
10234
HTMLEditRules::ConfirmSelectionInBody()
10235
0
{
10236
0
  MOZ_ASSERT(IsEditorDataAvailable());
10237
0
10238
0
  Element* rootElement = HTMLEditorRef().GetRoot();
10239
0
  if (NS_WARN_IF(!rootElement)) {
10240
0
    return NS_ERROR_UNEXPECTED;
10241
0
  }
10242
0
10243
0
  EditorRawDOMPoint selectionStartPoint(
10244
0
                      EditorBase::GetStartPoint(&SelectionRef()));
10245
0
  if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
10246
0
    return NS_ERROR_FAILURE;
10247
0
  }
10248
0
10249
0
  // Check that selection start container is inside the <body> element.
10250
0
  //XXXsmaug this code is insane.
10251
0
  nsINode* temp = selectionStartPoint.GetContainer();
10252
0
  while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
10253
0
    temp = temp->GetParentOrHostNode();
10254
0
  }
10255
0
10256
0
  // If we aren't in the <body> element, force the issue.
10257
0
  if (!temp) {
10258
0
    IgnoredErrorResult ignoredError;
10259
0
    SelectionRef().Collapse(RawRangeBoundary(rootElement, 0), ignoredError);
10260
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
10261
0
      return NS_ERROR_EDITOR_DESTROYED;
10262
0
    }
10263
0
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
10264
0
      "Failed to collapse selection at start of the root element");
10265
0
    return NS_OK;
10266
0
  }
10267
0
10268
0
  EditorRawDOMPoint selectionEndPoint(EditorBase::GetEndPoint(&SelectionRef()));
10269
0
  if (NS_WARN_IF(!selectionEndPoint.IsSet())) {
10270
0
    return NS_ERROR_FAILURE;
10271
0
  }
10272
0
10273
0
  // check that selNode is inside body
10274
0
  //XXXsmaug this code is insane.
10275
0
  temp = selectionEndPoint.GetContainer();
10276
0
  while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
10277
0
    temp = temp->GetParentOrHostNode();
10278
0
  }
10279
0
10280
0
  // If we aren't in the <body> element, force the issue.
10281
0
  if (!temp) {
10282
0
    IgnoredErrorResult ignoredError;
10283
0
    SelectionRef().Collapse(RawRangeBoundary(rootElement, 0), ignoredError);
10284
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
10285
0
      return NS_ERROR_EDITOR_DESTROYED;
10286
0
    }
10287
0
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
10288
0
      "Failed to collapse selection at start of the root element");
10289
0
  }
10290
0
10291
0
  return NS_OK;
10292
0
}
10293
10294
nsresult
10295
HTMLEditRules::UpdateDocChangeRange(nsRange* aRange)
10296
0
{
10297
0
  MOZ_ASSERT(IsEditorDataAvailable());
10298
0
10299
0
  // first make sure aRange is in the document.  It might not be if
10300
0
  // portions of our editting action involved manipulating nodes
10301
0
  // prior to placing them in the document (e.g., populating a list item
10302
0
  // before placing it in its list)
10303
0
  const RangeBoundary& atStart = aRange->StartRef();
10304
0
  if (NS_WARN_IF(!atStart.IsSet())) {
10305
0
    return NS_ERROR_FAILURE;
10306
0
  }
10307
0
  if (!HTMLEditorRef().IsDescendantOfRoot(atStart.Container())) {
10308
0
    // just return - we don't need to adjust mDocChangeRange in this case
10309
0
    return NS_OK;
10310
0
  }
10311
0
10312
0
  if (!mDocChangeRange) {
10313
0
    // clone aRange.
10314
0
    mDocChangeRange = aRange->CloneRange();
10315
0
  } else {
10316
0
    // compare starts of ranges
10317
0
    ErrorResult error;
10318
0
    int16_t result =
10319
0
      mDocChangeRange->CompareBoundaryPoints(Range_Binding::START_TO_START,
10320
0
                                             *aRange, error);
10321
0
    if (error.ErrorCodeIs(NS_ERROR_NOT_INITIALIZED)) {
10322
0
      // This will happen is mDocChangeRange is non-null, but the range is
10323
0
      // uninitialized. In this case we'll set the start to aRange start.
10324
0
      // The same test won't be needed further down since after we've set
10325
0
      // the start the range will be collapsed to that point.
10326
0
      result = 1;
10327
0
      error.SuppressException();
10328
0
    }
10329
0
    if (NS_WARN_IF(error.Failed())) {
10330
0
      return error.StealNSResult();
10331
0
    }
10332
0
10333
0
    // Positive result means mDocChangeRange start is after aRange start.
10334
0
    if (result > 0) {
10335
0
      mDocChangeRange->SetStart(atStart.AsRaw(), error);
10336
0
      if (NS_WARN_IF(error.Failed())) {
10337
0
        return error.StealNSResult();
10338
0
      }
10339
0
    }
10340
0
10341
0
    // compare ends of ranges
10342
0
    result =
10343
0
      mDocChangeRange->CompareBoundaryPoints(Range_Binding::END_TO_END,
10344
0
                                             *aRange, error);
10345
0
    if (NS_WARN_IF(error.Failed())) {
10346
0
      return error.StealNSResult();
10347
0
    }
10348
0
10349
0
    // Negative result means mDocChangeRange end is before aRange end.
10350
0
    if (result < 0) {
10351
0
      const RangeBoundary& atEnd = aRange->EndRef();
10352
0
      if (NS_WARN_IF(!atEnd.IsSet())) {
10353
0
        return NS_ERROR_FAILURE;
10354
0
      }
10355
0
      mDocChangeRange->SetEnd(atEnd.AsRaw(), error);
10356
0
      if (NS_WARN_IF(error.Failed())) {
10357
0
        return error.StealNSResult();
10358
0
      }
10359
0
    }
10360
0
  }
10361
0
  return NS_OK;
10362
0
}
10363
10364
nsresult
10365
HTMLEditRules::InsertBRIfNeededInternal(nsINode& aNode,
10366
                                        bool aInsertMozBR)
10367
0
{
10368
0
  MOZ_ASSERT(IsEditorDataAvailable());
10369
0
10370
0
  if (!IsBlockNode(aNode)) {
10371
0
    return NS_OK;
10372
0
  }
10373
0
10374
0
  bool isEmpty;
10375
0
  nsresult rv = HTMLEditorRef().IsEmptyNode(&aNode, &isEmpty);
10376
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10377
0
    return rv;
10378
0
  }
10379
0
  if (!isEmpty) {
10380
0
    return NS_OK;
10381
0
  }
10382
0
10383
0
  CreateElementResult createBrResult =
10384
0
    !aInsertMozBR ? CreateBR(EditorRawDOMPoint(&aNode, 0)) :
10385
0
                    CreateMozBR(EditorRawDOMPoint(&aNode, 0));
10386
0
  if (NS_WARN_IF(createBrResult.Failed())) {
10387
0
    return createBrResult.Rv();
10388
0
  }
10389
0
  return NS_OK;
10390
0
}
10391
10392
void
10393
HTMLEditRules::DidCreateNode(Selection& aSelection,
10394
                             Element& aNewElement)
10395
0
{
10396
0
  if (!mListenerEnabled) {
10397
0
    return;
10398
0
  }
10399
0
10400
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10401
0
    return;
10402
0
  }
10403
0
10404
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
10405
0
10406
0
  // assumption that Join keeps the righthand node
10407
0
  IgnoredErrorResult ignoredError;
10408
0
  mUtilRange->SelectNode(aNewElement, ignoredError);
10409
0
  if (NS_WARN_IF(ignoredError.Failed())) {
10410
0
    return;
10411
0
  }
10412
0
  UpdateDocChangeRange(mUtilRange);
10413
0
}
10414
10415
void
10416
HTMLEditRules::DidInsertNode(Selection& aSelection,
10417
                             nsIContent& aContent)
10418
0
{
10419
0
  if (!mListenerEnabled) {
10420
0
    return;
10421
0
  }
10422
0
10423
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10424
0
    return;
10425
0
  }
10426
0
10427
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
10428
0
10429
0
  IgnoredErrorResult ignoredError;
10430
0
  mUtilRange->SelectNode(aContent, ignoredError);
10431
0
  if (NS_WARN_IF(ignoredError.Failed())) {
10432
0
    return;
10433
0
  }
10434
0
  UpdateDocChangeRange(mUtilRange);
10435
0
}
10436
10437
void
10438
HTMLEditRules::WillDeleteNode(Selection& aSelection,
10439
                              nsINode& aChild)
10440
0
{
10441
0
  if (!mListenerEnabled) {
10442
0
    return;
10443
0
  }
10444
0
10445
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10446
0
    return;
10447
0
  }
10448
0
10449
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
10450
0
10451
0
  IgnoredErrorResult ignoredError;
10452
0
  mUtilRange->SelectNode(aChild, ignoredError);
10453
0
  if (NS_WARN_IF(ignoredError.Failed())) {
10454
0
    return;
10455
0
  }
10456
0
  UpdateDocChangeRange(mUtilRange);
10457
0
}
10458
10459
void
10460
HTMLEditRules::DidSplitNode(Selection& aSelection,
10461
                            nsINode& aExistingRightNode,
10462
                            nsINode& aNewLeftNode)
10463
0
{
10464
0
  if (!mListenerEnabled) {
10465
0
    return;
10466
0
  }
10467
0
10468
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10469
0
    return;
10470
0
  }
10471
0
10472
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
10473
0
10474
0
  nsresult rv = mUtilRange->SetStartAndEnd(&aNewLeftNode, 0,
10475
0
                                           &aExistingRightNode, 0);
10476
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10477
0
    return;
10478
0
  }
10479
0
  UpdateDocChangeRange(mUtilRange);
10480
0
}
10481
10482
void
10483
HTMLEditRules::WillJoinNodes(nsINode& aLeftNode,
10484
                             nsINode& aRightNode)
10485
0
{
10486
0
  if (!mListenerEnabled) {
10487
0
    return;
10488
0
  }
10489
0
10490
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10491
0
    return;
10492
0
  }
10493
0
10494
0
  // remember split point
10495
0
  mJoinOffset = aLeftNode.Length();
10496
0
}
10497
10498
void
10499
HTMLEditRules::DidJoinNodes(Selection& aSelection,
10500
                            nsINode& aLeftNode,
10501
                            nsINode& aRightNode)
10502
0
{
10503
0
  if (!mListenerEnabled) {
10504
0
    return;
10505
0
  }
10506
0
10507
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10508
0
    return;
10509
0
  }
10510
0
10511
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
10512
0
10513
0
  // assumption that Join keeps the righthand node
10514
0
  nsresult rv = mUtilRange->CollapseTo(&aRightNode, mJoinOffset);
10515
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10516
0
    return;
10517
0
  }
10518
0
  UpdateDocChangeRange(mUtilRange);
10519
0
}
10520
10521
void
10522
HTMLEditRules::DidInsertText(Selection& aSelection,
10523
                             nsINode& aTextNode,
10524
                             int32_t aOffset,
10525
                             const nsAString& aString)
10526
0
{
10527
0
  if (!mListenerEnabled) {
10528
0
    return;
10529
0
  }
10530
0
10531
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10532
0
    return;
10533
0
  }
10534
0
10535
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
10536
0
10537
0
  int32_t length = aString.Length();
10538
0
  nsresult rv = mUtilRange->SetStartAndEnd(&aTextNode, aOffset,
10539
0
                                           &aTextNode, aOffset + length);
10540
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10541
0
    return;
10542
0
  }
10543
0
  UpdateDocChangeRange(mUtilRange);
10544
0
}
10545
10546
void
10547
HTMLEditRules::DidDeleteText(Selection& aSelection,
10548
                             nsINode& aTextNode,
10549
                             int32_t aOffset,
10550
                             int32_t aLength)
10551
0
{
10552
0
  if (!mListenerEnabled) {
10553
0
    return;
10554
0
  }
10555
0
10556
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10557
0
    return;
10558
0
  }
10559
0
10560
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
10561
0
10562
0
  nsresult rv = mUtilRange->CollapseTo(&aTextNode, aOffset);
10563
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10564
0
    return;
10565
0
  }
10566
0
  UpdateDocChangeRange(mUtilRange);
10567
0
}
10568
10569
void
10570
HTMLEditRules::WillDeleteSelection(Selection& aSelection)
10571
0
{
10572
0
  if (!mListenerEnabled) {
10573
0
    return;
10574
0
  }
10575
0
10576
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10577
0
    return;
10578
0
  }
10579
0
10580
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
10581
0
10582
0
  EditorRawDOMPoint startPoint = EditorBase::GetStartPoint(&SelectionRef());
10583
0
  if (NS_WARN_IF(!startPoint.IsSet())) {
10584
0
    return;
10585
0
  }
10586
0
  EditorRawDOMPoint endPoint = EditorBase::GetEndPoint(&SelectionRef());
10587
0
  if (NS_WARN_IF(!endPoint.IsSet())) {
10588
0
    return;
10589
0
  }
10590
0
  nsresult rv = mUtilRange->SetStartAndEnd(startPoint, endPoint);
10591
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10592
0
    return;
10593
0
  }
10594
0
  UpdateDocChangeRange(mUtilRange);
10595
0
}
10596
10597
nsresult
10598
HTMLEditRules::RemoveAlignment(nsINode& aNode,
10599
                               const nsAString& aAlignType,
10600
                               bool aDescendantsOnly)
10601
0
{
10602
0
  MOZ_ASSERT(IsEditorDataAvailable());
10603
0
10604
0
  if (EditorBase::IsTextNode(&aNode) || HTMLEditUtils::IsTable(&aNode)) {
10605
0
    return NS_OK;
10606
0
  }
10607
0
10608
0
  nsCOMPtr<nsINode> child, tmp;
10609
0
  if (aDescendantsOnly) {
10610
0
    child = aNode.GetFirstChild();
10611
0
  } else {
10612
0
    child = &aNode;
10613
0
  }
10614
0
10615
0
  bool useCSS = HTMLEditorRef().IsCSSEnabled();
10616
0
10617
0
10618
0
  // Let's remove all alignment hints in the children of aNode; it can
10619
0
  // be an ALIGN attribute (in case we just remove it) or a CENTER
10620
0
  // element (here we have to remove the container and keep its
10621
0
  // children). We break on tables and don't look at their children.
10622
0
  while (child) {
10623
0
    if (aDescendantsOnly) {
10624
0
      // get the next sibling right now because we could have to remove child
10625
0
      tmp = child->GetNextSibling();
10626
0
    } else {
10627
0
      tmp = nullptr;
10628
0
    }
10629
0
10630
0
    if (child->IsHTMLElement(nsGkAtoms::center)) {
10631
0
      // the current node is a CENTER element
10632
0
      // first remove children's alignment
10633
0
      nsresult rv = RemoveAlignment(*child, aAlignType, true);
10634
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
10635
0
        return rv;
10636
0
      }
10637
0
10638
0
      // we may have to insert BRs in first and last position of element's children
10639
0
      // if the nodes before/after are not blocks and not BRs
10640
0
      rv = MakeSureElemStartsAndEndsOnCR(*child);
10641
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
10642
0
        return rv;
10643
0
      }
10644
0
10645
0
      // now remove the CENTER container
10646
0
      rv = HTMLEditorRef().RemoveContainerWithTransaction(*child->AsElement());
10647
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
10648
0
        return NS_ERROR_EDITOR_DESTROYED;
10649
0
      }
10650
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
10651
0
        return rv;
10652
0
      }
10653
0
    } else if (IsBlockNode(*child) || child->IsHTMLElement(nsGkAtoms::hr)) {
10654
0
      // the current node is a block element
10655
0
      if (HTMLEditUtils::SupportsAlignAttr(*child)) {
10656
0
        // remove the ALIGN attribute if this element can have it
10657
0
        nsresult rv =
10658
0
          HTMLEditorRef().RemoveAttributeWithTransaction(*child->AsElement(),
10659
0
                                                         *nsGkAtoms::align);
10660
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
10661
0
          return NS_ERROR_EDITOR_DESTROYED;
10662
0
        }
10663
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
10664
0
          return rv;
10665
0
        }
10666
0
      }
10667
0
      if (useCSS) {
10668
0
        if (child->IsAnyOfHTMLElements(nsGkAtoms::table, nsGkAtoms::hr)) {
10669
0
          nsresult rv =
10670
0
            HTMLEditorRef().SetAttributeOrEquivalent(child->AsElement(),
10671
0
                                                     nsGkAtoms::align,
10672
0
                                                     aAlignType, false);
10673
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
10674
0
            return NS_ERROR_EDITOR_DESTROYED;
10675
0
          }
10676
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
10677
0
            return rv;
10678
0
          }
10679
0
        } else {
10680
0
          nsAutoString dummyCssValue;
10681
0
          nsresult rv = HTMLEditorRef().mCSSEditUtils->RemoveCSSInlineStyle(
10682
0
                                                         *child,
10683
0
                                                         nsGkAtoms::textAlign,
10684
0
                                                         dummyCssValue);
10685
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
10686
0
            return NS_ERROR_EDITOR_DESTROYED;
10687
0
          }
10688
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
10689
0
            return rv;
10690
0
          }
10691
0
        }
10692
0
      }
10693
0
      if (!child->IsHTMLElement(nsGkAtoms::table)) {
10694
0
        // unless this is a table, look at children
10695
0
        nsresult rv = RemoveAlignment(*child, aAlignType, true);
10696
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
10697
0
          return rv;
10698
0
        }
10699
0
      }
10700
0
    }
10701
0
    child = tmp;
10702
0
  }
10703
0
  return NS_OK;
10704
0
}
10705
10706
nsresult
10707
HTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsINode& aNode,
10708
                                            bool aStarts)
10709
0
{
10710
0
  MOZ_ASSERT(IsEditorDataAvailable());
10711
0
10712
0
  nsINode* child = aStarts ? HTMLEditorRef().GetFirstEditableChild(aNode) :
10713
0
                             HTMLEditorRef().GetLastEditableChild(aNode);
10714
0
  if (NS_WARN_IF(!child)) {
10715
0
    return NS_OK;
10716
0
  }
10717
0
10718
0
  bool foundCR = false;
10719
0
  if (IsBlockNode(*child) || child->IsHTMLElement(nsGkAtoms::br)) {
10720
0
    foundCR = true;
10721
0
  } else {
10722
0
    nsINode* sibling =
10723
0
      aStarts ? HTMLEditorRef().GetPriorHTMLSibling(&aNode) :
10724
0
                HTMLEditorRef().GetNextHTMLSibling(&aNode);
10725
0
    if (sibling) {
10726
0
      if (IsBlockNode(*sibling) || sibling->IsHTMLElement(nsGkAtoms::br)) {
10727
0
        foundCR = true;
10728
0
      }
10729
0
    } else {
10730
0
      foundCR = true;
10731
0
    }
10732
0
  }
10733
0
  if (!foundCR) {
10734
0
    EditorRawDOMPoint pointToInsert;
10735
0
    if (!aStarts) {
10736
0
      pointToInsert.SetToEndOf(&aNode);
10737
0
    } else {
10738
0
      pointToInsert.Set(&aNode, 0);
10739
0
    }
10740
0
    RefPtr<Element> brElement =
10741
0
      HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
10742
0
                                                     pointToInsert);
10743
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
10744
0
      return NS_ERROR_EDITOR_DESTROYED;
10745
0
    }
10746
0
    if (NS_WARN_IF(!brElement)) {
10747
0
      return NS_ERROR_FAILURE;
10748
0
    }
10749
0
  }
10750
0
  return NS_OK;
10751
0
}
10752
10753
nsresult
10754
HTMLEditRules::MakeSureElemStartsAndEndsOnCR(nsINode& aNode)
10755
0
{
10756
0
  MOZ_ASSERT(IsEditorDataAvailable());
10757
0
10758
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10759
0
    return NS_ERROR_EDITOR_DESTROYED;
10760
0
  }
10761
0
10762
0
  Selection* selection = mHTMLEditor->GetSelection();
10763
0
  if (NS_WARN_IF(!selection)) {
10764
0
    return NS_ERROR_FAILURE;
10765
0
  }
10766
0
10767
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
10768
0
10769
0
  nsresult rv = MakeSureElemStartsOrEndsOnCR(aNode, false);
10770
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10771
0
    return rv;
10772
0
  }
10773
0
  rv = MakeSureElemStartsOrEndsOnCR(aNode, true);
10774
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10775
0
    return rv;
10776
0
  }
10777
0
  return NS_OK;
10778
0
}
10779
10780
nsresult
10781
HTMLEditRules::AlignBlock(Element& aElement,
10782
                          const nsAString& aAlignType,
10783
                          ResetAlignOf aResetAlignOf)
10784
0
{
10785
0
  MOZ_ASSERT(IsEditorDataAvailable());
10786
0
10787
0
  if (!IsBlockNode(aElement) && !aElement.IsHTMLElement(nsGkAtoms::hr)) {
10788
0
    // We deal only with blocks; early way out
10789
0
    return NS_OK;
10790
0
  }
10791
0
10792
0
  nsresult rv = RemoveAlignment(aElement, aAlignType,
10793
0
                                aResetAlignOf == ResetAlignOf::OnlyDescendants);
10794
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10795
0
    return rv;
10796
0
  }
10797
0
  if (HTMLEditorRef().IsCSSEnabled()) {
10798
0
    // Let's use CSS alignment; we use margin-left and margin-right for tables
10799
0
    // and text-align for other block-level elements
10800
0
    nsresult rv =
10801
0
      HTMLEditorRef().SetAttributeOrEquivalent(&aElement, nsGkAtoms::align,
10802
0
                                               aAlignType, false);
10803
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
10804
0
      return NS_ERROR_EDITOR_DESTROYED;
10805
0
    }
10806
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
10807
0
      return rv;
10808
0
    }
10809
0
    return NS_OK;
10810
0
  }
10811
0
10812
0
  // HTML case; this code is supposed to be called ONLY if the element
10813
0
  // supports the align attribute but we'll never know...
10814
0
  if (NS_WARN_IF(!HTMLEditUtils::SupportsAlignAttr(aElement))) {
10815
0
    // XXX error?
10816
0
    return NS_OK;
10817
0
  }
10818
0
10819
0
  rv = HTMLEditorRef().SetAttributeOrEquivalent(&aElement, nsGkAtoms::align,
10820
0
                                                aAlignType, false);
10821
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10822
0
    return NS_ERROR_EDITOR_DESTROYED;
10823
0
  }
10824
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10825
0
    return rv;
10826
0
  }
10827
0
  return NS_OK;
10828
0
}
10829
10830
nsresult
10831
HTMLEditRules::ChangeMarginStart(Element& aElement,
10832
                                 bool aIncrease)
10833
0
{
10834
0
  MOZ_ASSERT(IsEditorDataAvailable());
10835
0
10836
0
  nsAtom& marginProperty = MarginPropertyAtomForIndent(aElement);
10837
0
  nsAutoString value;
10838
0
  CSSEditUtils::GetSpecifiedProperty(aElement, marginProperty, value);
10839
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10840
0
    return NS_ERROR_EDITOR_DESTROYED;
10841
0
  }
10842
0
  float f;
10843
0
  RefPtr<nsAtom> unit;
10844
0
  CSSEditUtils::ParseLength(value, &f, getter_AddRefs(unit));
10845
0
  if (!f) {
10846
0
    nsAutoString defaultLengthUnit;
10847
0
    CSSEditUtils::GetDefaultLengthUnit(defaultLengthUnit);
10848
0
    unit = NS_Atomize(defaultLengthUnit);
10849
0
  }
10850
0
  int8_t multiplier = aIncrease ? +1 : -1;
10851
0
  if (nsGkAtoms::in == unit) {
10852
0
    f += NS_EDITOR_INDENT_INCREMENT_IN * multiplier;
10853
0
  } else if (nsGkAtoms::cm == unit) {
10854
0
    f += NS_EDITOR_INDENT_INCREMENT_CM * multiplier;
10855
0
  } else if (nsGkAtoms::mm == unit) {
10856
0
    f += NS_EDITOR_INDENT_INCREMENT_MM * multiplier;
10857
0
  } else if (nsGkAtoms::pt == unit) {
10858
0
    f += NS_EDITOR_INDENT_INCREMENT_PT * multiplier;
10859
0
  } else if (nsGkAtoms::pc == unit) {
10860
0
    f += NS_EDITOR_INDENT_INCREMENT_PC * multiplier;
10861
0
  } else if (nsGkAtoms::em == unit) {
10862
0
    f += NS_EDITOR_INDENT_INCREMENT_EM * multiplier;
10863
0
  } else if (nsGkAtoms::ex == unit) {
10864
0
    f += NS_EDITOR_INDENT_INCREMENT_EX * multiplier;
10865
0
  } else if (nsGkAtoms::px == unit) {
10866
0
    f += NS_EDITOR_INDENT_INCREMENT_PX * multiplier;
10867
0
  } else if (nsGkAtoms::percentage == unit) {
10868
0
    f += NS_EDITOR_INDENT_INCREMENT_PERCENT * multiplier;
10869
0
  }
10870
0
10871
0
  if (0 < f) {
10872
0
    nsAutoString newValue;
10873
0
    newValue.AppendFloat(f);
10874
0
    newValue.Append(nsDependentAtomString(unit));
10875
0
    HTMLEditorRef().mCSSEditUtils->SetCSSProperty(aElement, marginProperty,
10876
0
                                                  newValue);
10877
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
10878
0
      return NS_ERROR_EDITOR_DESTROYED;
10879
0
    }
10880
0
    return NS_OK;
10881
0
  }
10882
0
10883
0
  HTMLEditorRef().mCSSEditUtils->RemoveCSSProperty(aElement, marginProperty,
10884
0
                                                   value);
10885
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10886
0
    return NS_ERROR_EDITOR_DESTROYED;
10887
0
  }
10888
0
10889
0
  // Remove unnecessary divs
10890
0
  if (!aElement.IsHTMLElement(nsGkAtoms::div) ||
10891
0
      &aElement == HTMLEditorRef().GetActiveEditingHost() ||
10892
0
      !HTMLEditorRef().IsDescendantOfEditorRoot(&aElement) ||
10893
0
      HTMLEditor::HasAttributes(&aElement)) {
10894
0
    return NS_OK;
10895
0
  }
10896
0
10897
0
  nsresult rv = HTMLEditorRef().RemoveContainerWithTransaction(aElement);
10898
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10899
0
    return NS_ERROR_EDITOR_DESTROYED;
10900
0
  }
10901
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10902
0
    return rv;
10903
0
  }
10904
0
  return NS_OK;
10905
0
}
10906
10907
nsresult
10908
HTMLEditRules::WillAbsolutePosition(bool* aCancel,
10909
                                    bool* aHandled)
10910
0
{
10911
0
  MOZ_ASSERT(IsEditorDataAvailable());
10912
0
  MOZ_ASSERT(aCancel && aHandled);
10913
0
10914
0
  // FYI: Ignore cancel result of WillInsert().
10915
0
  nsresult rv = WillInsert();
10916
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
10917
0
    return NS_ERROR_EDITOR_DESTROYED;
10918
0
  }
10919
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
10920
0
10921
0
  *aCancel = false;
10922
0
  *aHandled = true;
10923
0
10924
0
  RefPtr<Element> focusElement =
10925
0
    HTMLEditorRef().GetSelectionContainerElement(SelectionRef());
10926
0
  if (focusElement && HTMLEditUtils::IsImage(focusElement)) {
10927
0
    mNewBlock = focusElement;
10928
0
    return NS_OK;
10929
0
  }
10930
0
10931
0
  rv = NormalizeSelection();
10932
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10933
0
    return rv;
10934
0
  }
10935
0
10936
0
  rv = PrepareToMakeElementAbsolutePosition(aHandled, address_of(mNewBlock));
10937
0
  // PrepareToMakeElementAbsolutePosition() may restore selection with
10938
0
  // AutoSelectionRestorer.  Therefore, the editor might have already been
10939
0
  // destroyed now.
10940
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
10941
0
    return NS_ERROR_EDITOR_DESTROYED;
10942
0
  }
10943
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10944
0
    return rv;
10945
0
  }
10946
0
  return NS_OK;
10947
0
}
10948
10949
nsresult
10950
HTMLEditRules::PrepareToMakeElementAbsolutePosition(
10951
                 bool* aHandled,
10952
                 RefPtr<Element>* aTargetElement)
10953
0
{
10954
0
  MOZ_ASSERT(IsEditorDataAvailable());
10955
0
10956
0
  MOZ_ASSERT(aHandled);
10957
0
  MOZ_ASSERT(aTargetElement);
10958
0
10959
0
  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
10960
0
10961
0
  // Convert the selection ranges into "promoted" selection ranges: this
10962
0
  // basically just expands the range to include the immediate block parent,
10963
0
  // and then further expands to include any ancestors whose children are all
10964
0
  // in the range.
10965
0
10966
0
  nsTArray<RefPtr<nsRange>> arrayOfRanges;
10967
0
  GetPromotedRanges(arrayOfRanges, EditSubAction::eSetPositionToAbsolute);
10968
0
10969
0
  // Use these ranges to contruct a list of nodes to act on.
10970
0
  nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
10971
0
  nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
10972
0
                                     EditSubAction::eSetPositionToAbsolute,
10973
0
                                     TouchContent::yes);
10974
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
10975
0
    return rv;
10976
0
  }
10977
0
10978
0
  // If nothing visible in list, make an empty block
10979
0
  if (ListIsEmptyLine(arrayOfNodes)) {
10980
0
    nsRange* firstRange = SelectionRef().GetRangeAt(0);
10981
0
    if (NS_WARN_IF(!firstRange)) {
10982
0
      return NS_ERROR_FAILURE;
10983
0
    }
10984
0
10985
0
    EditorDOMPoint atStartOfSelection(firstRange->StartRef());
10986
0
    if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
10987
0
      return NS_ERROR_FAILURE;
10988
0
    }
10989
0
10990
0
    // Make sure we can put a block here.
10991
0
    SplitNodeResult splitNodeResult =
10992
0
      MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
10993
0
                                                  atStartOfSelection);
10994
0
    if (NS_WARN_IF(splitNodeResult.Failed())) {
10995
0
      return splitNodeResult.Rv();
10996
0
    }
10997
0
    RefPtr<Element> positionedDiv =
10998
0
      HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
10999
0
                                                splitNodeResult.SplitPoint());
11000
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
11001
0
      return NS_ERROR_EDITOR_DESTROYED;
11002
0
    }
11003
0
    if (NS_WARN_IF(!positionedDiv)) {
11004
0
      return NS_ERROR_FAILURE;
11005
0
    }
11006
0
    // Remember our new block for postprocessing
11007
0
    *aTargetElement = positionedDiv;
11008
0
    // Delete anything that was in the list of nodes
11009
0
    while (!arrayOfNodes.IsEmpty()) {
11010
0
      OwningNonNull<nsINode> curNode = arrayOfNodes[0];
11011
0
      rv = HTMLEditorRef().DeleteNodeWithTransaction(*curNode);
11012
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
11013
0
        return NS_ERROR_EDITOR_DESTROYED;
11014
0
      }
11015
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
11016
0
        return rv;
11017
0
      }
11018
0
      arrayOfNodes.RemoveElementAt(0);
11019
0
    }
11020
0
    // Put selection in new block
11021
0
    *aHandled = true;
11022
0
    // Don't restore the selection
11023
0
    selectionRestorer.Abort();
11024
0
    ErrorResult error;
11025
0
    SelectionRef().Collapse(RawRangeBoundary(positionedDiv, 0), error);
11026
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
11027
0
      error.SuppressException();
11028
0
      return NS_ERROR_EDITOR_DESTROYED;
11029
0
    }
11030
0
    if (NS_WARN_IF(error.Failed())) {
11031
0
      return error.StealNSResult();
11032
0
    }
11033
0
    return NS_OK;
11034
0
  }
11035
0
11036
0
  // Okay, now go through all the nodes and put them in a blockquote, or
11037
0
  // whatever is appropriate.  Woohoo!
11038
0
  nsCOMPtr<Element> curList, curPositionedDiv, indentedLI;
11039
0
  for (OwningNonNull<nsINode>& curNode : arrayOfNodes) {
11040
0
    // Here's where we actually figure out what to do.
11041
0
    EditorDOMPoint atCurNode(curNode);
11042
0
    if (NS_WARN_IF(!atCurNode.IsSet())) {
11043
0
      return NS_ERROR_FAILURE; // XXX not continue??
11044
0
    }
11045
0
11046
0
    // Ignore all non-editable nodes.  Leave them be.
11047
0
    if (!HTMLEditorRef().IsEditable(curNode)) {
11048
0
      continue;
11049
0
    }
11050
0
11051
0
    nsCOMPtr<nsIContent> sibling;
11052
0
11053
0
    // Some logic for putting list items into nested lists...
11054
0
    if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
11055
0
      // Check to see if curList is still appropriate.  Which it is if curNode
11056
0
      // is still right after it in the same list.
11057
0
      if (curList) {
11058
0
        sibling = HTMLEditorRef().GetPriorHTMLSibling(curNode);
11059
0
      }
11060
0
11061
0
      if (!curList || (sibling && sibling != curList)) {
11062
0
        nsAtom* containerName =
11063
0
          atCurNode.GetContainer()->NodeInfo()->NameAtom();
11064
0
        // Create a new nested list of correct type.
11065
0
        SplitNodeResult splitNodeResult =
11066
0
          MaybeSplitAncestorsForInsertWithTransaction(*containerName,
11067
0
                                                      atCurNode);
11068
0
        if (NS_WARN_IF(splitNodeResult.Failed())) {
11069
0
          return splitNodeResult.Rv();
11070
0
        }
11071
0
        if (!curPositionedDiv) {
11072
0
          curPositionedDiv =
11073
0
            HTMLEditorRef().CreateNodeWithTransaction(
11074
0
                              *nsGkAtoms::div, splitNodeResult.SplitPoint());
11075
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
11076
0
            return NS_ERROR_EDITOR_DESTROYED;
11077
0
          }
11078
0
          NS_WARNING_ASSERTION(curPositionedDiv,
11079
0
            "Failed to create current positioned div element");
11080
0
          *aTargetElement = curPositionedDiv;
11081
0
        }
11082
0
        EditorRawDOMPoint atEndOfCurPositionedDiv;
11083
0
        atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
11084
0
        curList =
11085
0
          HTMLEditorRef().CreateNodeWithTransaction(*containerName,
11086
0
                                                    atEndOfCurPositionedDiv);
11087
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
11088
0
          return NS_ERROR_EDITOR_DESTROYED;
11089
0
        }
11090
0
        if (NS_WARN_IF(!curList)) {
11091
0
          return NS_ERROR_FAILURE;
11092
0
        }
11093
0
        // curList is now the correct thing to put curNode in.  Remember our
11094
0
        // new block for postprocessing.
11095
0
      }
11096
0
      // Tuck the node into the end of the active list
11097
0
      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
11098
0
                                                        *curList);
11099
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
11100
0
        return NS_ERROR_EDITOR_DESTROYED;
11101
0
      }
11102
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
11103
0
        return rv;
11104
0
      }
11105
0
      continue;
11106
0
    }
11107
0
11108
0
    // Not a list item, use blockquote?  If we are inside a list item, we
11109
0
    // don't want to blockquote, we want to sublist the list item.  We may
11110
0
    // have several nodes listed in the array of nodes to act on, that are in
11111
0
    // the same list item.  Since we only want to indent that li once, we
11112
0
    // must keep track of the most recent indented list item, and not indent
11113
0
    // it if we find another node to act on that is still inside the same li.
11114
0
    RefPtr<Element> listItem = IsInListItem(curNode);
11115
0
    if (listItem) {
11116
0
      if (indentedLI == listItem) {
11117
0
        // Already indented this list item
11118
0
        continue;
11119
0
      }
11120
0
      // Check to see if curList is still appropriate.  Which it is if
11121
0
      // curNode is still right after it in the same list.
11122
0
      if (curList) {
11123
0
        sibling = HTMLEditorRef().GetPriorHTMLSibling(listItem);
11124
0
      }
11125
0
11126
0
      if (!curList || (sibling && sibling != curList)) {
11127
0
        EditorDOMPoint atListItem(listItem);
11128
0
        if (NS_WARN_IF(!atListItem.IsSet())) {
11129
0
          return NS_ERROR_FAILURE;
11130
0
        }
11131
0
        nsAtom* containerName =
11132
0
          atListItem.GetContainer()->NodeInfo()->NameAtom();
11133
0
        // Create a new nested list of correct type
11134
0
        SplitNodeResult splitNodeResult =
11135
0
          MaybeSplitAncestorsForInsertWithTransaction(*containerName,
11136
0
                                                      atListItem);
11137
0
        if (NS_WARN_IF(splitNodeResult.Failed())) {
11138
0
          return splitNodeResult.Rv();
11139
0
        }
11140
0
        if (!curPositionedDiv) {
11141
0
          EditorRawDOMPoint atListItemParent(atListItem.GetContainer());
11142
0
          curPositionedDiv =
11143
0
            HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
11144
0
                                                      atListItemParent);
11145
0
          if (NS_WARN_IF(!CanHandleEditAction())) {
11146
0
            return NS_ERROR_EDITOR_DESTROYED;
11147
0
          }
11148
0
          NS_WARNING_ASSERTION(curPositionedDiv,
11149
0
            "Failed to create current positioned div element");
11150
0
          *aTargetElement = curPositionedDiv;
11151
0
        }
11152
0
        EditorRawDOMPoint atEndOfCurPositionedDiv;
11153
0
        atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
11154
0
        curList =
11155
0
          HTMLEditorRef().CreateNodeWithTransaction(*containerName,
11156
0
                                                    atEndOfCurPositionedDiv);
11157
0
        if (NS_WARN_IF(!CanHandleEditAction())) {
11158
0
          return NS_ERROR_EDITOR_DESTROYED;
11159
0
        }
11160
0
        if (NS_WARN_IF(!curList)) {
11161
0
          return NS_ERROR_FAILURE;
11162
0
        }
11163
0
      }
11164
0
      rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*listItem, *curList);
11165
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
11166
0
        return NS_ERROR_EDITOR_DESTROYED;
11167
0
      }
11168
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
11169
0
        return rv;
11170
0
      }
11171
0
      // Remember we indented this li
11172
0
      indentedLI = listItem;
11173
0
      continue;
11174
0
    }
11175
0
11176
0
    // Need to make a div to put things in if we haven't already
11177
0
    if (!curPositionedDiv) {
11178
0
      if (curNode->IsHTMLElement(nsGkAtoms::div)) {
11179
0
        curPositionedDiv = curNode->AsElement();
11180
0
        *aTargetElement = curPositionedDiv;
11181
0
        curList = nullptr;
11182
0
        continue;
11183
0
      }
11184
0
      SplitNodeResult splitNodeResult =
11185
0
        MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
11186
0
                                                    atCurNode);
11187
0
      if (NS_WARN_IF(splitNodeResult.Failed())) {
11188
0
        return splitNodeResult.Rv();
11189
0
      }
11190
0
      curPositionedDiv =
11191
0
        HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
11192
0
                                                  splitNodeResult.SplitPoint());
11193
0
      if (NS_WARN_IF(!CanHandleEditAction())) {
11194
0
        return NS_ERROR_EDITOR_DESTROYED;
11195
0
      }
11196
0
      if (NS_WARN_IF(!curPositionedDiv)) {
11197
0
        return NS_ERROR_FAILURE;
11198
0
      }
11199
0
      // Remember our new block for postprocessing
11200
0
      *aTargetElement = curPositionedDiv;
11201
0
      // curPositionedDiv is now the correct thing to put curNode in
11202
0
    }
11203
0
11204
0
    // Tuck the node into the end of the active blockquote
11205
0
    rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
11206
0
                                                      *curPositionedDiv);
11207
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
11208
0
      return NS_ERROR_EDITOR_DESTROYED;
11209
0
    }
11210
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
11211
0
      return rv;
11212
0
    }
11213
0
    // Forget curList, if any
11214
0
    curList = nullptr;
11215
0
  }
11216
0
  return NS_OK;
11217
0
}
11218
11219
nsresult
11220
HTMLEditRules::DidAbsolutePosition()
11221
0
{
11222
0
  MOZ_ASSERT(IsEditorDataAvailable());
11223
0
11224
0
  if (!mNewBlock) {
11225
0
    return NS_OK;
11226
0
  }
11227
0
  nsresult rv = HTMLEditorRef().SetPositionToAbsoluteOrStatic(*mNewBlock, true);
11228
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
11229
0
    return NS_ERROR_EDITOR_DESTROYED;
11230
0
  }
11231
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
11232
0
    return rv;
11233
0
  }
11234
0
  return NS_OK;
11235
0
}
11236
11237
nsresult
11238
HTMLEditRules::WillRemoveAbsolutePosition(bool* aCancel,
11239
                                          bool* aHandled)
11240
0
{
11241
0
  MOZ_ASSERT(IsEditorDataAvailable());
11242
0
11243
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
11244
0
    return NS_ERROR_INVALID_ARG;
11245
0
  }
11246
0
11247
0
  // FYI: Ignore cancel result of WillInsert().
11248
0
  nsresult rv = WillInsert();
11249
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
11250
0
    return NS_ERROR_EDITOR_DESTROYED;
11251
0
  }
11252
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
11253
0
11254
0
  *aCancel = false;
11255
0
  *aHandled = true;
11256
0
11257
0
  RefPtr<Element> element =
11258
0
    HTMLEditorRef().GetAbsolutelyPositionedSelectionContainer();
11259
0
  if (NS_WARN_IF(!element)) {
11260
0
    return NS_ERROR_FAILURE;
11261
0
  }
11262
0
11263
0
  {
11264
0
    AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
11265
0
11266
0
    nsresult rv = HTMLEditorRef().SetPositionToAbsoluteOrStatic(*element, false);
11267
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
11268
0
      return NS_ERROR_EDITOR_DESTROYED;
11269
0
    }
11270
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
11271
0
      return rv;
11272
0
    }
11273
0
  }
11274
0
11275
0
  // Restoring Selection might cause destroying the HTML editor.
11276
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
11277
0
    return NS_ERROR_EDITOR_DESTROYED;
11278
0
  }
11279
0
  return NS_OK;
11280
0
}
11281
11282
nsresult
11283
HTMLEditRules::WillRelativeChangeZIndex(int32_t aChange,
11284
                                        bool* aCancel,
11285
                                        bool* aHandled)
11286
0
{
11287
0
  MOZ_ASSERT(IsEditorDataAvailable());
11288
0
11289
0
  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
11290
0
    return NS_ERROR_INVALID_ARG;
11291
0
  }
11292
0
11293
0
  // FYI: Ignore cancel result of WillInsert().
11294
0
  nsresult rv = WillInsert();
11295
0
  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
11296
0
    return NS_ERROR_EDITOR_DESTROYED;
11297
0
  }
11298
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
11299
0
11300
0
  *aCancel = false;
11301
0
  *aHandled = true;
11302
0
11303
0
  RefPtr<Element> element =
11304
0
    HTMLEditorRef().GetAbsolutelyPositionedSelectionContainer();
11305
0
  if (NS_WARN_IF(!element)) {
11306
0
    return NS_ERROR_FAILURE;
11307
0
  }
11308
0
11309
0
  {
11310
0
    AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
11311
0
11312
0
    int32_t zIndex;
11313
0
    nsresult rv = HTMLEditorRef().RelativeChangeElementZIndex(*element, aChange,
11314
0
                                                              &zIndex);
11315
0
    if (NS_WARN_IF(!CanHandleEditAction())) {
11316
0
      return NS_ERROR_EDITOR_DESTROYED;
11317
0
    }
11318
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
11319
0
      return rv;
11320
0
    }
11321
0
  }
11322
0
11323
0
  // Restoring Selection might cause destroying the HTML editor.
11324
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
11325
0
    return NS_ERROR_EDITOR_DESTROYED;
11326
0
  }
11327
0
  return NS_OK;
11328
0
}
11329
11330
nsresult
11331
HTMLEditRules::DocumentModified()
11332
0
{
11333
0
  nsContentUtils::AddScriptRunner(
11334
0
    NewRunnableMethod("HTMLEditRules::DocumentModifiedWorker",
11335
0
                      this,
11336
0
                      &HTMLEditRules::DocumentModifiedWorker));
11337
0
  // Be aware, if DocumentModifiedWorker() is called synchronously, the
11338
0
  // editor might have been destroyed here.
11339
0
  return NS_OK;
11340
0
}
11341
11342
void
11343
HTMLEditRules::DocumentModifiedWorker()
11344
0
{
11345
0
  if (NS_WARN_IF(!CanHandleEditAction())) {
11346
0
    return;
11347
0
  }
11348
0
11349
0
  Selection* selection = mHTMLEditor->GetSelection();
11350
0
  if (NS_WARN_IF(!selection)) {
11351
0
    return;
11352
0
  }
11353
0
11354
0
  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
11355
0
11356
0
  // DeleteNodeWithTransaction() below may cause a flush, which could destroy
11357
0
  // the editor
11358
0
  nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
11359
0
11360
0
  // Delete our bogus node, if we have one, since the document might not be
11361
0
  // empty any more.
11362
0
  if (mBogusNode) {
11363
0
    DebugOnly<nsresult> rv =
11364
0
      HTMLEditorRef().DeleteNodeWithTransaction(*mBogusNode);
11365
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
11366
0
      "Failed to remove the bogus node");
11367
0
    mBogusNode = nullptr;
11368
0
  }
11369
0
11370
0
  // Try to recreate the bogus node if needed.
11371
0
  DebugOnly<nsresult> rv = CreateBogusNodeIfNeeded();
11372
0
  NS_WARNING_ASSERTION(rv.value != NS_ERROR_EDITOR_DESTROYED,
11373
0
    "The editor has been destroyed during creating a bogus node");
11374
0
}
11375
11376
} // namespace mozilla