Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/editor/libeditor/SplitNodeTransaction.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "SplitNodeTransaction.h"
7
8
#include "mozilla/EditorBase.h"         // for EditorBase
9
#include "mozilla/EditorDOMPoint.h"     // for RangeBoundary, EditorRawDOMPoint
10
#include "mozilla/dom/Selection.h"
11
#include "nsAString.h"
12
#include "nsDebug.h"                    // for NS_ASSERTION, etc.
13
#include "nsError.h"                    // for NS_ERROR_NOT_INITIALIZED, etc.
14
#include "nsIContent.h"                 // for nsIContent
15
16
namespace mozilla {
17
18
using namespace dom;
19
20
template already_AddRefed<SplitNodeTransaction>
21
SplitNodeTransaction::Create(
22
                        EditorBase& aEditorBase,
23
                        const EditorDOMPoint& aStartOfRightNode);
24
template already_AddRefed<SplitNodeTransaction>
25
SplitNodeTransaction::Create(
26
                        EditorBase& aEditorBase,
27
                        const EditorRawDOMPoint& aStartOfRightNode);
28
29
// static
30
template<typename PT, typename CT>
31
already_AddRefed<SplitNodeTransaction>
32
SplitNodeTransaction::Create(
33
                        EditorBase& aEditorBase,
34
                        const EditorDOMPointBase<PT, CT>& aStartOfRightNode)
35
0
{
36
0
  RefPtr<SplitNodeTransaction> transaction =
37
0
    new SplitNodeTransaction(aEditorBase, aStartOfRightNode);
38
0
  return transaction.forget();
39
0
}
Unexecuted instantiation: already_AddRefed<mozilla::SplitNodeTransaction> mozilla::SplitNodeTransaction::Create<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorBase&, mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&)
Unexecuted instantiation: already_AddRefed<mozilla::SplitNodeTransaction> mozilla::SplitNodeTransaction::Create<nsINode*, nsIContent*>(mozilla::EditorBase&, mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&)
40
41
template<typename PT, typename CT>
42
SplitNodeTransaction::SplitNodeTransaction(
43
                        EditorBase& aEditorBase,
44
                        const EditorDOMPointBase<PT, CT>& aStartOfRightNode)
45
  : mEditorBase(&aEditorBase)
46
  , mStartOfRightNode(aStartOfRightNode)
47
0
{
48
0
  MOZ_DIAGNOSTIC_ASSERT(aStartOfRightNode.IsSet());
49
0
  MOZ_DIAGNOSTIC_ASSERT(aStartOfRightNode.GetContainerAsContent());
50
0
}
Unexecuted instantiation: mozilla::SplitNodeTransaction::SplitNodeTransaction<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorBase&, mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&)
Unexecuted instantiation: mozilla::SplitNodeTransaction::SplitNodeTransaction<nsINode*, nsIContent*>(mozilla::EditorBase&, mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&)
51
52
SplitNodeTransaction::~SplitNodeTransaction()
53
0
{
54
0
}
55
56
NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitNodeTransaction, EditTransactionBase,
57
                                   mEditorBase,
58
                                   mStartOfRightNode,
59
                                   mParent,
60
                                   mNewLeftNode)
61
62
NS_IMPL_ADDREF_INHERITED(SplitNodeTransaction, EditTransactionBase)
63
NS_IMPL_RELEASE_INHERITED(SplitNodeTransaction, EditTransactionBase)
64
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SplitNodeTransaction)
65
0
NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
66
67
NS_IMETHODIMP
68
SplitNodeTransaction::DoTransaction()
69
0
{
70
0
  if (NS_WARN_IF(!mEditorBase) ||
71
0
      NS_WARN_IF(!mStartOfRightNode.IsSet())) {
72
0
    return NS_ERROR_NOT_INITIALIZED;
73
0
  }
74
0
  MOZ_ASSERT(mStartOfRightNode.IsSetAndValid());
75
0
76
0
  // Create a new node
77
0
  ErrorResult error;
78
0
  // Don't use .downcast directly because AsContent has an assertion we want
79
0
  nsCOMPtr<nsINode> clone =
80
0
    mStartOfRightNode.GetContainer()->CloneNode(false, error);
81
0
  if (NS_WARN_IF(error.Failed())) {
82
0
    return error.StealNSResult();
83
0
  }
84
0
  if (NS_WARN_IF(!clone)) {
85
0
    return NS_ERROR_UNEXPECTED;
86
0
  }
87
0
  mNewLeftNode = dont_AddRef(clone.forget().take()->AsContent());
88
0
  mEditorBase->MarkNodeDirty(mStartOfRightNode.GetContainer());
89
0
90
0
  // Get the parent node
91
0
  mParent = mStartOfRightNode.GetContainer()->GetParentNode();
92
0
  if (NS_WARN_IF(!mParent)) {
93
0
    return NS_ERROR_FAILURE;
94
0
  }
95
0
96
0
  // Insert the new node
97
0
  mEditorBase->DoSplitNode(EditorDOMPoint(mStartOfRightNode),
98
0
                           *mNewLeftNode, error);
99
0
100
0
  if (!mEditorBase->AllowsTransactionsToChangeSelection()) {
101
0
    if (NS_WARN_IF(error.Failed())) {
102
0
      return error.StealNSResult();
103
0
    }
104
0
    return NS_OK;
105
0
  }
106
0
107
0
  // XXX Really odd.  The result of DoSplitNode() is respected only when
108
0
  //     we shouldn't set selection.  Otherwise, it's overridden by the
109
0
  //     result of Selection.Collapse().
110
0
  NS_WARNING_ASSERTION(!mEditorBase->Destroyed(),
111
0
    "The editor has gone but SplitNodeTransaction keeps trying to modify "
112
0
    "Selection");
113
0
  RefPtr<Selection> selection = mEditorBase->GetSelection();
114
0
  if (NS_WARN_IF(!selection)) {
115
0
    return NS_ERROR_FAILURE;
116
0
  }
117
0
  if (NS_WARN_IF(error.Failed())) {
118
0
    // XXX This must be a bug.
119
0
    error.SuppressException();
120
0
  }
121
0
  MOZ_ASSERT(mStartOfRightNode.Offset() == mNewLeftNode->Length());
122
0
  EditorRawDOMPoint atEndOfLeftNode;
123
0
  atEndOfLeftNode.SetToEndOf(mNewLeftNode);
124
0
  selection->Collapse(atEndOfLeftNode, error);
125
0
  if (NS_WARN_IF(error.Failed())) {
126
0
    return error.StealNSResult();
127
0
  }
128
0
  return NS_OK;
129
0
}
130
131
NS_IMETHODIMP
132
SplitNodeTransaction::UndoTransaction()
133
0
{
134
0
  if (NS_WARN_IF(!mEditorBase) ||
135
0
      NS_WARN_IF(!mNewLeftNode) ||
136
0
      NS_WARN_IF(!mParent) ||
137
0
      NS_WARN_IF(!mStartOfRightNode.IsSet())) {
138
0
    return NS_ERROR_NOT_INITIALIZED;
139
0
  }
140
0
141
0
  // This assumes Do inserted the new node in front of the prior existing node
142
0
  // XXX Perhaps, we should reset mStartOfRightNode with current first child
143
0
  //     of the right node.
144
0
  return mEditorBase->DoJoinNodes(mStartOfRightNode.GetContainer(),
145
0
                                  mNewLeftNode, mParent);
146
0
}
147
148
/* Redo cannot simply resplit the right node, because subsequent transactions
149
 * on the redo stack may depend on the left node existing in its previous
150
 * state.
151
 */
152
NS_IMETHODIMP
153
SplitNodeTransaction::RedoTransaction()
154
0
{
155
0
  if (NS_WARN_IF(!mNewLeftNode) ||
156
0
      NS_WARN_IF(!mParent) ||
157
0
      NS_WARN_IF(!mStartOfRightNode.IsSet())) {
158
0
    return NS_ERROR_NOT_INITIALIZED;
159
0
  }
160
0
161
0
  // First, massage the existing node so it is in its post-split state
162
0
  if (mStartOfRightNode.IsInTextNode()) {
163
0
    Text* rightNodeAsText = mStartOfRightNode.GetContainerAsText();
164
0
    MOZ_DIAGNOSTIC_ASSERT(rightNodeAsText);
165
0
    ErrorResult rv;
166
0
    rightNodeAsText->DeleteData(0, mStartOfRightNode.Offset(), rv);
167
0
    if (NS_WARN_IF(rv.Failed())) {
168
0
      return rv.StealNSResult();
169
0
    }
170
0
  } else {
171
0
    nsCOMPtr<nsIContent> child =
172
0
      mStartOfRightNode.GetContainer()->GetFirstChild();
173
0
    nsCOMPtr<nsIContent> nextSibling;
174
0
    for (uint32_t i = 0; i < mStartOfRightNode.Offset(); i++) {
175
0
      // XXX This must be bad behavior.  Perhaps, we should work with
176
0
      //     mStartOfRightNode::GetChild().  Even if some children
177
0
      //     before the right node have been inserted or removed, we should
178
0
      //     move all children before the right node because user must focus
179
0
      //     on the right node, so, it must be the expected behavior.
180
0
      if (NS_WARN_IF(!child)) {
181
0
        return NS_ERROR_NULL_POINTER;
182
0
      }
183
0
      nextSibling = child->GetNextSibling();
184
0
      ErrorResult error;
185
0
      mStartOfRightNode.GetContainer()->RemoveChild(*child, error);
186
0
      if (NS_WARN_IF(error.Failed())) {
187
0
        return error.StealNSResult();
188
0
      }
189
0
      mNewLeftNode->AppendChild(*child, error);
190
0
      if (NS_WARN_IF(error.Failed())) {
191
0
        return error.StealNSResult();
192
0
      }
193
0
      child = nextSibling;
194
0
    }
195
0
  }
196
0
  // Second, re-insert the left node into the tree
197
0
  ErrorResult error;
198
0
  mParent->InsertBefore(*mNewLeftNode, mStartOfRightNode.GetContainer(), error);
199
0
  if (NS_WARN_IF(error.Failed())) {
200
0
    return error.StealNSResult();
201
0
  }
202
0
  return NS_OK;
203
0
}
204
205
nsIContent*
206
SplitNodeTransaction::GetNewNode()
207
0
{
208
0
  return mNewLeftNode;
209
0
}
210
211
} // namespace mozilla