Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/editor/libeditor/HTMLInlineTableEditor.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "mozilla/HTMLEditor.h"
6
7
#include "HTMLEditUtils.h"
8
#include "mozilla/dom/Element.h"
9
#include "nsAString.h"
10
#include "nsCOMPtr.h"
11
#include "nsDebug.h"
12
#include "nsError.h"
13
#include "nsGenericHTMLElement.h"
14
#include "nsIContent.h"
15
#include "nsIHTMLObjectResizer.h"
16
#include "nsIPresShell.h"
17
#include "nsLiteralString.h"
18
#include "nsReadableUtils.h"
19
#include "nsString.h"
20
#include "nscore.h"
21
22
namespace mozilla {
23
24
NS_IMETHODIMP
25
HTMLEditor::SetInlineTableEditingEnabled(bool aIsEnabled)
26
0
{
27
0
  EnableInlineTableEditor(aIsEnabled);
28
0
  return NS_OK;
29
0
}
30
31
NS_IMETHODIMP
32
HTMLEditor::GetInlineTableEditingEnabled(bool* aIsEnabled)
33
0
{
34
0
  *aIsEnabled = IsInlineTableEditorEnabled();
35
0
  return NS_OK;
36
0
}
37
38
nsresult
39
HTMLEditor::ShowInlineTableEditingUIInternal(Element& aCellElement)
40
0
{
41
0
  if (NS_WARN_IF(!HTMLEditUtils::IsTableCell(&aCellElement))) {
42
0
    return NS_OK;
43
0
  }
44
0
45
0
  if (NS_WARN_IF(!IsDescendantOfEditorRoot(&aCellElement))) {
46
0
    return NS_ERROR_FAILURE;
47
0
  }
48
0
49
0
  if (NS_WARN_IF(mInlineEditedCell)) {
50
0
    return NS_ERROR_FAILURE;
51
0
  }
52
0
53
0
  mInlineEditedCell = &aCellElement;
54
0
55
0
  // the resizers and the shadow will be anonymous children of the body
56
0
  RefPtr<Element> bodyElement = GetRoot();
57
0
  if (NS_WARN_IF(!bodyElement)) {
58
0
    return NS_ERROR_FAILURE;
59
0
  }
60
0
61
0
  do {
62
0
    // The buttons of inline table editor will be children of the <body>
63
0
    // element.  Creating the anonymous elements may cause calling
64
0
    // HideInlineTableEditingUIInternal() via a mutation event listener.
65
0
    // So, we should store new button to a local variable, then, check:
66
0
    //   - whether creating a button is already set to the member or not
67
0
    //   - whether already created buttons are changed to another set
68
0
    // If creating the buttons are canceled, we hit the latter check.
69
0
    // If buttons for another table are created during this, we hit the latter
70
0
    // check too.
71
0
    // If buttons are just created again for same element, we hit the former
72
0
    // check.
73
0
    ManualNACPtr addColumnBeforeButton =
74
0
      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
75
0
                             NS_LITERAL_STRING("mozTableAddColumnBefore"), false);
76
0
    if (NS_WARN_IF(!addColumnBeforeButton)) {
77
0
      break; // Hide unnecessary buttons created above.
78
0
    }
79
0
    if (NS_WARN_IF(mAddColumnBeforeButton) ||
80
0
        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
81
0
      return NS_ERROR_FAILURE; // Don't hide another set of buttons.
82
0
    }
83
0
    mAddColumnBeforeButton = std::move(addColumnBeforeButton);
84
0
85
0
    ManualNACPtr removeColumnButton =
86
0
      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
87
0
                             NS_LITERAL_STRING("mozTableRemoveColumn"), false);
88
0
    if (NS_WARN_IF(!removeColumnButton)) {
89
0
      break;
90
0
    }
91
0
    if (NS_WARN_IF(mRemoveColumnButton) ||
92
0
        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
93
0
      return NS_ERROR_FAILURE;
94
0
    }
95
0
    mRemoveColumnButton = std::move(removeColumnButton);
96
0
97
0
    ManualNACPtr addColumnAfterButton =
98
0
      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
99
0
                             NS_LITERAL_STRING("mozTableAddColumnAfter"),
100
0
                             false);
101
0
    if (NS_WARN_IF(!addColumnAfterButton)) {
102
0
      break;
103
0
    }
104
0
    if (NS_WARN_IF(mAddColumnAfterButton) ||
105
0
        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
106
0
      return NS_ERROR_FAILURE;
107
0
    }
108
0
    mAddColumnAfterButton = std::move(addColumnAfterButton);
109
0
110
0
    ManualNACPtr addRowBeforeButton =
111
0
      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
112
0
                             NS_LITERAL_STRING("mozTableAddRowBefore"), false);
113
0
    if (NS_WARN_IF(!addRowBeforeButton)) {
114
0
      break;
115
0
    }
116
0
    if (NS_WARN_IF(mAddRowBeforeButton) ||
117
0
        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
118
0
      return NS_ERROR_FAILURE;
119
0
    }
120
0
    mAddRowBeforeButton = std::move(addRowBeforeButton);
121
0
122
0
    ManualNACPtr removeRowButton =
123
0
      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
124
0
                             NS_LITERAL_STRING("mozTableRemoveRow"), false);
125
0
    if (NS_WARN_IF(!removeRowButton)) {
126
0
      break;
127
0
    }
128
0
    if (NS_WARN_IF(mRemoveRowButton) ||
129
0
        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
130
0
      return NS_ERROR_FAILURE;
131
0
    }
132
0
    mRemoveRowButton = std::move(removeRowButton);
133
0
134
0
    ManualNACPtr addRowAfterButton =
135
0
      CreateAnonymousElement(nsGkAtoms::a, *bodyElement,
136
0
                             NS_LITERAL_STRING("mozTableAddRowAfter"), false);
137
0
    if (NS_WARN_IF(!addRowAfterButton)) {
138
0
      break;
139
0
    }
140
0
    if (NS_WARN_IF(mAddRowAfterButton) ||
141
0
        NS_WARN_IF(mInlineEditedCell != &aCellElement)) {
142
0
      return NS_ERROR_FAILURE;
143
0
    }
144
0
    mAddRowAfterButton = std::move(addRowAfterButton);
145
0
146
0
    AddMouseClickListener(mAddColumnBeforeButton);
147
0
    AddMouseClickListener(mRemoveColumnButton);
148
0
    AddMouseClickListener(mAddColumnAfterButton);
149
0
    AddMouseClickListener(mAddRowBeforeButton);
150
0
    AddMouseClickListener(mRemoveRowButton);
151
0
    AddMouseClickListener(mAddRowAfterButton);
152
0
153
0
    mHasShownInlineTableEditor = true;
154
0
155
0
    nsresult rv = RefreshInlineTableEditingUIInternal();
156
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
157
0
      return rv;
158
0
    }
159
0
    return NS_OK;
160
0
  } while (true);
161
0
162
0
  HideInlineTableEditingUIInternal();
163
0
  return NS_ERROR_FAILURE;
164
0
}
165
166
void
167
HTMLEditor::HideInlineTableEditingUIInternal()
168
0
{
169
0
  mInlineEditedCell = nullptr;
170
0
171
0
  RemoveMouseClickListener(mAddColumnBeforeButton);
172
0
  RemoveMouseClickListener(mRemoveColumnButton);
173
0
  RemoveMouseClickListener(mAddColumnAfterButton);
174
0
  RemoveMouseClickListener(mAddRowBeforeButton);
175
0
  RemoveMouseClickListener(mRemoveRowButton);
176
0
  RemoveMouseClickListener(mAddRowAfterButton);
177
0
178
0
  // get the presshell's document observer interface.
179
0
  nsCOMPtr<nsIPresShell> ps = GetPresShell();
180
0
  // We allow the pres shell to be null; when it is, we presume there
181
0
  // are no document observers to notify, but we still want to
182
0
  // UnbindFromTree.
183
0
184
0
  // Calling DeleteRefToAnonymousNode() may cause showing the UI again.
185
0
  // Therefore, we should forget all anonymous contents first.
186
0
  // Otherwise, we could leak the old content because of overwritten by
187
0
  // ShowInlineTableEditingUIInternal().
188
0
  ManualNACPtr addColumnBeforeButton(std::move(mAddColumnBeforeButton));
189
0
  ManualNACPtr removeColumnButton(std::move(mRemoveColumnButton));
190
0
  ManualNACPtr addColumnAfterButton(std::move(mAddColumnAfterButton));
191
0
  ManualNACPtr addRowBeforeButton(std::move(mAddRowBeforeButton));
192
0
  ManualNACPtr removeRowButton(std::move(mRemoveRowButton));
193
0
  ManualNACPtr addRowAfterButton(std::move(mAddRowAfterButton));
194
0
195
0
  DeleteRefToAnonymousNode(std::move(addColumnBeforeButton), ps);
196
0
  DeleteRefToAnonymousNode(std::move(removeColumnButton), ps);
197
0
  DeleteRefToAnonymousNode(std::move(addColumnAfterButton), ps);
198
0
  DeleteRefToAnonymousNode(std::move(addRowBeforeButton), ps);
199
0
  DeleteRefToAnonymousNode(std::move(removeRowButton), ps);
200
0
  DeleteRefToAnonymousNode(std::move(addRowAfterButton), ps);
201
0
}
202
203
nsresult
204
HTMLEditor::DoInlineTableEditingAction(const Element& aElement)
205
0
{
206
0
  nsAutoString anonclass;
207
0
  aElement.GetAttr(kNameSpaceID_None, nsGkAtoms::_moz_anonclass, anonclass);
208
0
209
0
  if (!StringBeginsWith(anonclass, NS_LITERAL_STRING("mozTable"))) {
210
0
    return NS_OK;
211
0
  }
212
0
213
0
  RefPtr<Element> tableElement = GetEnclosingTable(mInlineEditedCell);
214
0
  int32_t rowCount, colCount;
215
0
  nsresult rv = GetTableSize(tableElement, &rowCount, &colCount);
216
0
  NS_ENSURE_SUCCESS(rv, rv);
217
0
218
0
  bool hideUI = false;
219
0
  bool hideResizersWithInlineTableUI = (mResizedObject == tableElement);
220
0
221
0
  if (anonclass.EqualsLiteral("mozTableAddColumnBefore")) {
222
0
    DebugOnly<nsresult> rv =
223
0
      InsertTableColumnsWithTransaction(1, InsertPosition::eBeforeSelectedCell);
224
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
225
0
                         "Failed to insert a column before the selected cell");
226
0
  } else if (anonclass.EqualsLiteral("mozTableAddColumnAfter")) {
227
0
    DebugOnly<nsresult> rv =
228
0
      InsertTableColumnsWithTransaction(1, InsertPosition::eAfterSelectedCell);
229
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
230
0
                         "Failed to insert a column after the selected cell");
231
0
  } else if (anonclass.EqualsLiteral("mozTableAddRowBefore")) {
232
0
    DebugOnly<nsresult> rv =
233
0
      InsertTableRowsWithTransaction(1, InsertPosition::eBeforeSelectedCell);
234
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
235
0
                         "Failed to insert a row before the selected cell");
236
0
  } else if (anonclass.EqualsLiteral("mozTableAddRowAfter")) {
237
0
    DebugOnly<nsresult> rv =
238
0
      InsertTableRowsWithTransaction(1, InsertPosition::eAfterSelectedCell);
239
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
240
0
                         "Failed to insert a row after the selected cell");
241
0
  } else if (anonclass.EqualsLiteral("mozTableRemoveColumn")) {
242
0
    DebugOnly<nsresult> rv = DeleteSelectedTableColumnsWithTransaction(1);
243
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
244
0
                         "Failed to delete the selected table column");
245
0
    hideUI = (colCount == 1);
246
0
  } else if (anonclass.EqualsLiteral("mozTableRemoveRow")) {
247
0
    DebugOnly<nsresult> rv = DeleteSelectedTableRowsWithTransaction(1);
248
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
249
0
                         "Failed to delete the selected table row");
250
0
    hideUI = (rowCount == 1);
251
0
  } else {
252
0
    return NS_OK;
253
0
  }
254
0
255
0
  ++mInlineTableEditorUsedCount;
256
0
257
0
  // InsertTableRowsWithTransaction() might causes reframe.
258
0
  if (Destroyed()) {
259
0
    return NS_OK;
260
0
  }
261
0
262
0
  if (hideUI) {
263
0
    HideInlineTableEditingUIInternal();
264
0
    if (hideResizersWithInlineTableUI) {
265
0
      DebugOnly<nsresult> rv = HideResizersInternal();
266
0
      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to hide resizers");
267
0
    }
268
0
  }
269
0
270
0
  return NS_OK;
271
0
}
272
273
void
274
HTMLEditor::AddMouseClickListener(Element* aElement)
275
0
{
276
0
  if (aElement) {
277
0
    aElement->AddEventListener(NS_LITERAL_STRING("click"),
278
0
             mEventListener, true);
279
0
  }
280
0
}
281
282
void
283
HTMLEditor::RemoveMouseClickListener(Element* aElement)
284
0
{
285
0
  if (aElement) {
286
0
    aElement->RemoveEventListener(NS_LITERAL_STRING("click"),
287
0
                                  mEventListener, true);
288
0
  }
289
0
}
290
291
NS_IMETHODIMP
292
HTMLEditor::RefreshInlineTableEditingUI()
293
0
{
294
0
  nsresult rv = RefreshInlineTableEditingUIInternal();
295
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
296
0
    return rv;
297
0
  }
298
0
  return NS_OK;
299
0
}
300
301
nsresult
302
HTMLEditor::RefreshInlineTableEditingUIInternal()
303
0
{
304
0
  if (!mInlineEditedCell) {
305
0
   return NS_OK;
306
0
  }
307
0
308
0
  RefPtr<nsGenericHTMLElement> inlineEditingCellElement =
309
0
    nsGenericHTMLElement::FromNode(mInlineEditedCell);
310
0
  if (NS_WARN_IF(!inlineEditingCellElement)) {
311
0
    return NS_ERROR_FAILURE;
312
0
  }
313
0
314
0
  int32_t cellX = 0, cellY = 0;
315
0
  GetElementOrigin(*mInlineEditedCell, cellX, cellY);
316
0
317
0
  int32_t cellWidth = inlineEditingCellElement->OffsetWidth();
318
0
  int32_t cellHeight = inlineEditingCellElement->OffsetHeight();
319
0
320
0
  int32_t centerOfCellX = cellX + cellWidth / 2;
321
0
  int32_t centerOfCellY = cellY + cellHeight / 2;
322
0
323
0
  RefPtr<Element> tableElement = GetEnclosingTable(mInlineEditedCell);
324
0
  int32_t rowCount = 0, colCount = 0;
325
0
  nsresult rv = GetTableSize(tableElement, &rowCount, &colCount);
326
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
327
0
    return rv;
328
0
  }
329
0
330
0
  RefPtr<Element> addColumunBeforeButton = mAddColumnBeforeButton.get();
331
0
  SetAnonymousElementPosition(centerOfCellX - 10, cellY - 7,
332
0
                              addColumunBeforeButton);
333
0
  if (NS_WARN_IF(addColumunBeforeButton != mAddColumnBeforeButton.get())) {
334
0
    return NS_ERROR_FAILURE;
335
0
  }
336
0
337
0
  RefPtr<Element> removeColumnButton = mRemoveColumnButton.get();
338
0
  SetAnonymousElementPosition(centerOfCellX - 4, cellY - 7, removeColumnButton);
339
0
  if (NS_WARN_IF(removeColumnButton != mRemoveColumnButton.get())) {
340
0
    return NS_ERROR_FAILURE;
341
0
  }
342
0
343
0
  RefPtr<Element> addColumnAfterButton = mAddColumnAfterButton.get();
344
0
  SetAnonymousElementPosition(centerOfCellX + 6, cellY - 7,
345
0
                              addColumnAfterButton);
346
0
  if (NS_WARN_IF(addColumnAfterButton != mAddColumnAfterButton.get())) {
347
0
    return NS_ERROR_FAILURE;
348
0
  }
349
0
350
0
  RefPtr<Element> addRowBeforeButton = mAddRowBeforeButton.get();
351
0
  SetAnonymousElementPosition(cellX - 7, centerOfCellY - 10,
352
0
                              addRowBeforeButton);
353
0
  if (NS_WARN_IF(addRowBeforeButton != mAddRowBeforeButton.get())) {
354
0
    return NS_ERROR_FAILURE;
355
0
  }
356
0
357
0
  RefPtr<Element> removeRowButton = mRemoveRowButton.get();
358
0
  SetAnonymousElementPosition(cellX - 7, centerOfCellY - 4, removeRowButton);
359
0
  if (NS_WARN_IF(removeRowButton != mRemoveRowButton.get())) {
360
0
    return NS_ERROR_FAILURE;
361
0
  }
362
0
363
0
  RefPtr<Element> addRowAfterButton = mAddRowAfterButton.get();
364
0
  SetAnonymousElementPosition(cellX - 7, centerOfCellY + 6, addRowAfterButton);
365
0
  if (NS_WARN_IF(addRowAfterButton != mAddRowAfterButton.get())) {
366
0
    return NS_ERROR_FAILURE;
367
0
  }
368
0
369
0
  return NS_OK;
370
0
}
371
372
} // namespace mozilla