Line data Source code
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 <stdio.h>
7 :
8 : #include "mozilla/HTMLEditor.h"
9 :
10 : #include "HTMLEditUtils.h"
11 : #include "mozilla/Assertions.h"
12 : #include "mozilla/EditorUtils.h"
13 : #include "mozilla/dom/Selection.h"
14 : #include "mozilla/dom/Element.h"
15 : #include "nsAString.h"
16 : #include "nsAlgorithm.h"
17 : #include "nsCOMPtr.h"
18 : #include "nsDebug.h"
19 : #include "nsError.h"
20 : #include "nsGkAtoms.h"
21 : #include "nsIAtom.h"
22 : #include "nsIContent.h"
23 : #include "nsIDOMElement.h"
24 : #include "nsIDOMNode.h"
25 : #include "nsIFrame.h"
26 : #include "nsINode.h"
27 : #include "nsIPresShell.h"
28 : #include "nsISupportsUtils.h"
29 : #include "nsITableCellLayout.h" // For efficient access to table cell
30 : #include "nsITableEditor.h"
31 : #include "nsLiteralString.h"
32 : #include "nsQueryFrame.h"
33 : #include "nsRange.h"
34 : #include "nsString.h"
35 : #include "nsTArray.h"
36 : #include "nsTableCellFrame.h"
37 : #include "nsTableWrapperFrame.h"
38 : #include "nscore.h"
39 : #include <algorithm>
40 :
41 : namespace mozilla {
42 :
43 : using namespace dom;
44 :
45 : /**
46 : * Stack based helper class for restoring selection after table edit.
47 : */
48 : class MOZ_STACK_CLASS AutoSelectionSetterAfterTableEdit final
49 : {
50 : private:
51 : nsCOMPtr<nsITableEditor> mTableEditor;
52 : nsCOMPtr<nsIDOMElement> mTable;
53 : int32_t mCol, mRow, mDirection, mSelected;
54 :
55 : public:
56 0 : AutoSelectionSetterAfterTableEdit(nsITableEditor* aTableEditor,
57 : nsIDOMElement* aTable,
58 : int32_t aRow,
59 : int32_t aCol,
60 : int32_t aDirection,
61 : bool aSelected)
62 0 : : mTableEditor(aTableEditor)
63 : , mTable(aTable)
64 : , mCol(aCol)
65 : , mRow(aRow)
66 : , mDirection(aDirection)
67 0 : , mSelected(aSelected)
68 : {
69 0 : }
70 :
71 0 : ~AutoSelectionSetterAfterTableEdit()
72 0 : {
73 0 : if (mTableEditor) {
74 0 : mTableEditor->SetSelectionAfterTableEdit(mTable, mRow, mCol, mDirection,
75 0 : mSelected);
76 : }
77 0 : }
78 :
79 : // This is needed to abort the caret reset in the destructor
80 : // when one method yields control to another
81 : void CancelSetCaret()
82 : {
83 : mTableEditor = nullptr;
84 : mTable = nullptr;
85 : }
86 : };
87 :
88 : NS_IMETHODIMP
89 0 : HTMLEditor::InsertCell(nsIDOMElement* aCell,
90 : int32_t aRowSpan,
91 : int32_t aColSpan,
92 : bool aAfter,
93 : bool aIsHeader,
94 : nsIDOMElement** aNewCell)
95 : {
96 0 : NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
97 0 : if (aNewCell) {
98 0 : *aNewCell = nullptr;
99 : }
100 :
101 : // And the parent and offsets needed to do an insert
102 0 : nsCOMPtr<nsIDOMNode> cellParent;
103 0 : nsresult rv = aCell->GetParentNode(getter_AddRefs(cellParent));
104 0 : NS_ENSURE_SUCCESS(rv, rv);
105 0 : NS_ENSURE_TRUE(cellParent, NS_ERROR_NULL_POINTER);
106 :
107 0 : int32_t cellOffset = GetChildOffset(aCell, cellParent);
108 :
109 0 : nsCOMPtr<nsIDOMElement> newCell;
110 0 : rv = CreateElementWithDefaults(aIsHeader ? NS_LITERAL_STRING("th") :
111 0 : NS_LITERAL_STRING("tb"),
112 0 : getter_AddRefs(newCell));
113 0 : if (NS_FAILED(rv)) {
114 0 : return rv;
115 : }
116 0 : if (!newCell) {
117 0 : return NS_ERROR_FAILURE;
118 : }
119 :
120 : //Optional: return new cell created
121 0 : if (aNewCell) {
122 0 : *aNewCell = newCell.get();
123 0 : NS_ADDREF(*aNewCell);
124 : }
125 :
126 0 : if (aRowSpan > 1) {
127 : // Note: Do NOT use editor transaction for this
128 0 : nsAutoString newRowSpan;
129 0 : newRowSpan.AppendInt(aRowSpan, 10);
130 0 : newCell->SetAttribute(NS_LITERAL_STRING("rowspan"), newRowSpan);
131 : }
132 0 : if (aColSpan > 1) {
133 : // Note: Do NOT use editor transaction for this
134 0 : nsAutoString newColSpan;
135 0 : newColSpan.AppendInt(aColSpan, 10);
136 0 : newCell->SetAttribute(NS_LITERAL_STRING("colspan"), newColSpan);
137 : }
138 0 : if (aAfter) {
139 0 : cellOffset++;
140 : }
141 :
142 : //Don't let Rules System change the selection
143 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
144 0 : return InsertNode(newCell, cellParent, cellOffset);
145 : }
146 :
147 : NS_IMETHODIMP
148 0 : HTMLEditor::SetColSpan(nsIDOMElement* aCell,
149 : int32_t aColSpan)
150 : {
151 0 : NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
152 0 : nsAutoString newSpan;
153 0 : newSpan.AppendInt(aColSpan, 10);
154 0 : return SetAttribute(aCell, NS_LITERAL_STRING("colspan"), newSpan);
155 : }
156 :
157 : NS_IMETHODIMP
158 0 : HTMLEditor::SetRowSpan(nsIDOMElement* aCell,
159 : int32_t aRowSpan)
160 : {
161 0 : NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
162 0 : nsAutoString newSpan;
163 0 : newSpan.AppendInt(aRowSpan, 10);
164 0 : return SetAttribute(aCell, NS_LITERAL_STRING("rowspan"), newSpan);
165 : }
166 :
167 : NS_IMETHODIMP
168 0 : HTMLEditor::InsertTableCell(int32_t aNumber,
169 : bool aAfter)
170 : {
171 0 : nsCOMPtr<nsIDOMElement> table;
172 0 : nsCOMPtr<nsIDOMElement> curCell;
173 0 : nsCOMPtr<nsIDOMNode> cellParent;
174 : int32_t cellOffset, startRowIndex, startColIndex;
175 0 : nsresult rv = GetCellContext(nullptr,
176 0 : getter_AddRefs(table),
177 0 : getter_AddRefs(curCell),
178 0 : getter_AddRefs(cellParent), &cellOffset,
179 0 : &startRowIndex, &startColIndex);
180 0 : NS_ENSURE_SUCCESS(rv, rv);
181 : // Don't fail if no cell found
182 0 : NS_ENSURE_TRUE(curCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
183 :
184 : // Get more data for current cell in row we are inserting at (we need COLSPAN)
185 : int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
186 : bool isSelected;
187 0 : rv = GetCellDataAt(table, startRowIndex, startColIndex,
188 0 : getter_AddRefs(curCell),
189 : &curStartRowIndex, &curStartColIndex, &rowSpan, &colSpan,
190 0 : &actualRowSpan, &actualColSpan, &isSelected);
191 0 : NS_ENSURE_SUCCESS(rv, rv);
192 0 : NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
193 0 : int32_t newCellIndex = aAfter ? (startColIndex+colSpan) : startColIndex;
194 : //We control selection resetting after the insert...
195 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
196 : newCellIndex, ePreviousColumn,
197 0 : false);
198 : //...so suppress Rules System selection munging
199 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
200 :
201 0 : for (int32_t i = 0; i < aNumber; i++) {
202 0 : nsCOMPtr<nsIDOMElement> newCell;
203 0 : rv = CreateElementWithDefaults(NS_LITERAL_STRING("td"),
204 0 : getter_AddRefs(newCell));
205 0 : if (NS_SUCCEEDED(rv) && newCell) {
206 0 : if (aAfter) {
207 0 : cellOffset++;
208 : }
209 0 : rv = InsertNode(newCell, cellParent, cellOffset);
210 0 : if (NS_FAILED(rv)) {
211 0 : break;
212 : }
213 : }
214 : }
215 : // XXX This is perhaps the result of the last call of InsertNode() or
216 : // CreateElementWithDefaults().
217 0 : return rv;
218 : }
219 :
220 : NS_IMETHODIMP
221 0 : HTMLEditor::GetFirstRow(nsIDOMElement* aTableElement,
222 : nsIDOMNode** aRowNode)
223 : {
224 0 : NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
225 :
226 0 : *aRowNode = nullptr;
227 :
228 0 : NS_ENSURE_TRUE(aTableElement, NS_ERROR_NULL_POINTER);
229 :
230 0 : nsCOMPtr<nsIDOMElement> tableElement;
231 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"),
232 : aTableElement,
233 0 : getter_AddRefs(tableElement));
234 0 : NS_ENSURE_SUCCESS(rv, rv);
235 0 : NS_ENSURE_TRUE(tableElement, NS_ERROR_NULL_POINTER);
236 :
237 0 : nsCOMPtr<nsIDOMNode> tableChild;
238 0 : rv = tableElement->GetFirstChild(getter_AddRefs(tableChild));
239 0 : NS_ENSURE_SUCCESS(rv, rv);
240 :
241 0 : while (tableChild) {
242 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(tableChild);
243 0 : if (content) {
244 0 : if (content->IsHTMLElement(nsGkAtoms::tr)) {
245 : // Found a row directly under <table>
246 0 : *aRowNode = tableChild;
247 0 : NS_ADDREF(*aRowNode);
248 0 : return NS_OK;
249 : }
250 : // Look for row in one of the row container elements
251 0 : if (content->IsAnyOfHTMLElements(nsGkAtoms::tbody,
252 : nsGkAtoms::thead,
253 : nsGkAtoms::tfoot)) {
254 0 : nsCOMPtr<nsIDOMNode> rowNode;
255 0 : rv = tableChild->GetFirstChild(getter_AddRefs(rowNode));
256 0 : NS_ENSURE_SUCCESS(rv, rv);
257 :
258 : // We can encounter textnodes here -- must find a row
259 0 : while (rowNode && !HTMLEditUtils::IsTableRow(rowNode)) {
260 0 : nsCOMPtr<nsIDOMNode> nextNode;
261 0 : rv = rowNode->GetNextSibling(getter_AddRefs(nextNode));
262 0 : NS_ENSURE_SUCCESS(rv, rv);
263 :
264 0 : rowNode = nextNode;
265 : }
266 0 : if (rowNode) {
267 0 : *aRowNode = rowNode.get();
268 0 : NS_ADDREF(*aRowNode);
269 0 : return NS_OK;
270 : }
271 : }
272 : }
273 : // Here if table child was a CAPTION or COLGROUP
274 : // or child of a row parent wasn't a row (bad HTML?),
275 : // or first child was a textnode
276 : // Look in next table child
277 0 : nsCOMPtr<nsIDOMNode> nextChild;
278 0 : rv = tableChild->GetNextSibling(getter_AddRefs(nextChild));
279 0 : NS_ENSURE_SUCCESS(rv, rv);
280 :
281 0 : tableChild = nextChild;
282 : }
283 : // If here, row was not found
284 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
285 : }
286 :
287 : NS_IMETHODIMP
288 0 : HTMLEditor::GetNextRow(nsIDOMNode* aCurrentRowNode,
289 : nsIDOMNode** aRowNode)
290 : {
291 0 : NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
292 :
293 0 : *aRowNode = nullptr;
294 :
295 0 : NS_ENSURE_TRUE(aCurrentRowNode, NS_ERROR_NULL_POINTER);
296 :
297 0 : if (!HTMLEditUtils::IsTableRow(aCurrentRowNode)) {
298 0 : return NS_ERROR_FAILURE;
299 : }
300 :
301 0 : nsCOMPtr<nsIDOMNode> nextRow;
302 0 : nsresult rv = aCurrentRowNode->GetNextSibling(getter_AddRefs(nextRow));
303 0 : NS_ENSURE_SUCCESS(rv, rv);
304 :
305 0 : nsCOMPtr<nsIDOMNode> nextNode;
306 :
307 : // Skip over any textnodes here
308 0 : while (nextRow && !HTMLEditUtils::IsTableRow(nextRow)) {
309 0 : rv = nextRow->GetNextSibling(getter_AddRefs(nextNode));
310 0 : NS_ENSURE_SUCCESS(rv, rv);
311 :
312 0 : nextRow = nextNode;
313 : }
314 0 : if (nextRow) {
315 0 : *aRowNode = nextRow.get();
316 0 : NS_ADDREF(*aRowNode);
317 0 : return NS_OK;
318 : }
319 :
320 : // No row found, search for rows in other table sections
321 0 : nsCOMPtr<nsIDOMNode> rowParent;
322 0 : rv = aCurrentRowNode->GetParentNode(getter_AddRefs(rowParent));
323 0 : NS_ENSURE_SUCCESS(rv, rv);
324 0 : NS_ENSURE_TRUE(rowParent, NS_ERROR_NULL_POINTER);
325 :
326 0 : nsCOMPtr<nsIDOMNode> parentSibling;
327 0 : rv = rowParent->GetNextSibling(getter_AddRefs(parentSibling));
328 0 : NS_ENSURE_SUCCESS(rv, rv);
329 :
330 0 : while (parentSibling) {
331 0 : rv = parentSibling->GetFirstChild(getter_AddRefs(nextRow));
332 0 : NS_ENSURE_SUCCESS(rv, rv);
333 :
334 : // We can encounter textnodes here -- must find a row
335 0 : while (nextRow && !HTMLEditUtils::IsTableRow(nextRow)) {
336 0 : rv = nextRow->GetNextSibling(getter_AddRefs(nextNode));
337 0 : NS_ENSURE_SUCCESS(rv, rv);
338 :
339 0 : nextRow = nextNode;
340 : }
341 0 : if (nextRow) {
342 0 : *aRowNode = nextRow.get();
343 0 : NS_ADDREF(*aRowNode);
344 0 : return NS_OK;
345 : }
346 :
347 : // We arrive here only if a table section has no children
348 : // or first child of section is not a row (bad HTML or more "_moz_text" nodes!)
349 : // So look for another section sibling
350 0 : rv = parentSibling->GetNextSibling(getter_AddRefs(nextNode));
351 0 : NS_ENSURE_SUCCESS(rv, rv);
352 :
353 0 : parentSibling = nextNode;
354 : }
355 : // If here, row was not found
356 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
357 : }
358 :
359 : nsresult
360 0 : HTMLEditor::GetLastCellInRow(nsIDOMNode* aRowNode,
361 : nsIDOMNode** aCellNode)
362 : {
363 0 : NS_ENSURE_TRUE(aCellNode, NS_ERROR_NULL_POINTER);
364 :
365 0 : *aCellNode = nullptr;
366 :
367 0 : NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
368 :
369 0 : nsCOMPtr<nsIDOMNode> rowChild;
370 0 : nsresult rv = aRowNode->GetLastChild(getter_AddRefs(rowChild));
371 0 : NS_ENSURE_SUCCESS(rv, rv);
372 :
373 0 : while (rowChild && !HTMLEditUtils::IsTableCell(rowChild)) {
374 : // Skip over textnodes
375 0 : nsCOMPtr<nsIDOMNode> previousChild;
376 0 : rv = rowChild->GetPreviousSibling(getter_AddRefs(previousChild));
377 0 : NS_ENSURE_SUCCESS(rv, rv);
378 :
379 0 : rowChild = previousChild;
380 : }
381 0 : if (rowChild) {
382 0 : *aCellNode = rowChild.get();
383 0 : NS_ADDREF(*aCellNode);
384 0 : return NS_OK;
385 : }
386 : // If here, cell was not found
387 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
388 : }
389 :
390 : NS_IMETHODIMP
391 0 : HTMLEditor::InsertTableColumn(int32_t aNumber,
392 : bool aAfter)
393 : {
394 0 : RefPtr<Selection> selection;
395 0 : nsCOMPtr<nsIDOMElement> table;
396 0 : nsCOMPtr<nsIDOMElement> curCell;
397 : int32_t startRowIndex, startColIndex;
398 0 : nsresult rv = GetCellContext(getter_AddRefs(selection),
399 0 : getter_AddRefs(table),
400 0 : getter_AddRefs(curCell),
401 : nullptr, nullptr,
402 0 : &startRowIndex, &startColIndex);
403 0 : NS_ENSURE_SUCCESS(rv, rv);
404 : // Don't fail if no cell found
405 0 : NS_ENSURE_TRUE(curCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
406 :
407 : // Get more data for current cell (we need ROWSPAN)
408 : int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
409 : bool isSelected;
410 0 : rv = GetCellDataAt(table, startRowIndex, startColIndex,
411 0 : getter_AddRefs(curCell),
412 : &curStartRowIndex, &curStartColIndex,
413 : &rowSpan, &colSpan,
414 0 : &actualRowSpan, &actualColSpan, &isSelected);
415 0 : NS_ENSURE_SUCCESS(rv, rv);
416 0 : NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
417 :
418 0 : AutoEditBatch beginBatching(this);
419 : // Prevent auto insertion of BR in new cell until we're done
420 0 : AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
421 :
422 : // Use column after current cell if requested
423 0 : if (aAfter) {
424 0 : startColIndex += actualColSpan;
425 : //Detect when user is adding after a COLSPAN=0 case
426 : // Assume they want to stop the "0" behavior and
427 : // really add a new column. Thus we set the
428 : // colspan to its true value
429 0 : if (!colSpan) {
430 0 : SetColSpan(curCell, actualColSpan);
431 : }
432 : }
433 :
434 : int32_t rowCount, colCount, rowIndex;
435 0 : rv = GetTableSize(table, &rowCount, &colCount);
436 0 : NS_ENSURE_SUCCESS(rv, rv);
437 :
438 : //We reset caret in destructor...
439 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
440 : startColIndex, ePreviousRow,
441 0 : false);
442 : //.. so suppress Rules System selection munging
443 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
444 :
445 : // If we are inserting after all existing columns
446 : // Make sure table is "well formed"
447 : // before appending new column
448 0 : if (startColIndex >= colCount) {
449 0 : NormalizeTable(table);
450 : }
451 :
452 0 : nsCOMPtr<nsIDOMNode> rowNode;
453 0 : for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
454 0 : if (startColIndex < colCount) {
455 : // We are inserting before an existing column
456 0 : rv = GetCellDataAt(table, rowIndex, startColIndex,
457 0 : getter_AddRefs(curCell),
458 : &curStartRowIndex, &curStartColIndex,
459 : &rowSpan, &colSpan,
460 0 : &actualRowSpan, &actualColSpan, &isSelected);
461 0 : NS_ENSURE_SUCCESS(rv, rv);
462 :
463 : // Don't fail entire process if we fail to find a cell
464 : // (may fail just in particular rows with < adequate cells per row)
465 0 : if (curCell) {
466 0 : if (curStartColIndex < startColIndex) {
467 : // We have a cell spanning this location
468 : // Simply increase its colspan to keep table rectangular
469 : // Note: we do nothing if colsSpan=0,
470 : // since it should automatically span the new column
471 0 : if (colSpan > 0) {
472 0 : SetColSpan(curCell, colSpan+aNumber);
473 : }
474 : } else {
475 : // Simply set selection to the current cell
476 : // so we can let InsertTableCell() do the work
477 : // Insert a new cell before current one
478 0 : selection->Collapse(curCell, 0);
479 0 : rv = InsertTableCell(aNumber, false);
480 : }
481 : }
482 : } else {
483 : // Get current row and append new cells after last cell in row
484 0 : if (!rowIndex) {
485 0 : rv = GetFirstRow(table.get(), getter_AddRefs(rowNode));
486 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
487 0 : return rv;
488 : }
489 : } else {
490 0 : nsCOMPtr<nsIDOMNode> nextRow;
491 0 : rv = GetNextRow(rowNode.get(), getter_AddRefs(nextRow));
492 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
493 0 : return rv;
494 : }
495 0 : rowNode = nextRow;
496 : }
497 :
498 0 : if (rowNode) {
499 0 : nsCOMPtr<nsIDOMNode> lastCell;
500 0 : rv = GetLastCellInRow(rowNode, getter_AddRefs(lastCell));
501 0 : NS_ENSURE_SUCCESS(rv, rv);
502 0 : NS_ENSURE_TRUE(lastCell, NS_ERROR_FAILURE);
503 :
504 0 : curCell = do_QueryInterface(lastCell);
505 0 : if (curCell) {
506 : // Simply add same number of cells to each row
507 : // Although tempted to check cell indexes for curCell,
508 : // the effects of COLSPAN>1 in some cells makes this futile!
509 : // We must use NormalizeTable first to assure
510 : // that there are cells in each cellmap location
511 0 : selection->Collapse(curCell, 0);
512 0 : rv = InsertTableCell(aNumber, true);
513 : }
514 : }
515 : }
516 : }
517 : // XXX This is perhaps the result of the last call of InsertTableCell().
518 0 : return rv;
519 : }
520 :
521 : NS_IMETHODIMP
522 0 : HTMLEditor::InsertTableRow(int32_t aNumber,
523 : bool aAfter)
524 : {
525 0 : nsCOMPtr<nsIDOMElement> table;
526 0 : nsCOMPtr<nsIDOMElement> curCell;
527 :
528 : int32_t startRowIndex, startColIndex;
529 0 : nsresult rv = GetCellContext(nullptr,
530 0 : getter_AddRefs(table),
531 0 : getter_AddRefs(curCell),
532 : nullptr, nullptr,
533 0 : &startRowIndex, &startColIndex);
534 0 : NS_ENSURE_SUCCESS(rv, rv);
535 : // Don't fail if no cell found
536 0 : NS_ENSURE_TRUE(curCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
537 :
538 : // Get more data for current cell in row we are inserting at (we need COLSPAN)
539 : int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
540 : bool isSelected;
541 0 : rv = GetCellDataAt(table, startRowIndex, startColIndex,
542 0 : getter_AddRefs(curCell),
543 : &curStartRowIndex, &curStartColIndex,
544 : &rowSpan, &colSpan,
545 0 : &actualRowSpan, &actualColSpan, &isSelected);
546 0 : NS_ENSURE_SUCCESS(rv, rv);
547 0 : NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
548 :
549 : int32_t rowCount, colCount;
550 0 : rv = GetTableSize(table, &rowCount, &colCount);
551 0 : NS_ENSURE_SUCCESS(rv, rv);
552 :
553 0 : AutoEditBatch beginBatching(this);
554 : // Prevent auto insertion of BR in new cell until we're done
555 0 : AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
556 :
557 0 : if (aAfter) {
558 : // Use row after current cell
559 0 : startRowIndex += actualRowSpan;
560 :
561 : //Detect when user is adding after a ROWSPAN=0 case
562 : // Assume they want to stop the "0" behavior and
563 : // really add a new row. Thus we set the
564 : // rowspan to its true value
565 0 : if (!rowSpan) {
566 0 : SetRowSpan(curCell, actualRowSpan);
567 : }
568 : }
569 :
570 : //We control selection resetting after the insert...
571 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
572 : startColIndex, ePreviousColumn,
573 0 : false);
574 : //...so suppress Rules System selection munging
575 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
576 :
577 0 : nsCOMPtr<nsIDOMElement> cellForRowParent;
578 0 : int32_t cellsInRow = 0;
579 0 : if (startRowIndex < rowCount) {
580 : // We are inserting above an existing row
581 : // Get each cell in the insert row to adjust for COLSPAN effects while we
582 : // count how many cells are needed
583 0 : int32_t colIndex = 0;
584 0 : while (NS_SUCCEEDED(GetCellDataAt(table, startRowIndex, colIndex,
585 : getter_AddRefs(curCell),
586 : &curStartRowIndex, &curStartColIndex,
587 : &rowSpan, &colSpan,
588 : &actualRowSpan, &actualColSpan,
589 : &isSelected))) {
590 0 : if (curCell) {
591 0 : if (curStartRowIndex < startRowIndex) {
592 : // We have a cell spanning this location
593 : // Simply increase its rowspan
594 : //Note that if rowSpan == 0, we do nothing,
595 : // since that cell should automatically extend into the new row
596 0 : if (rowSpan > 0) {
597 0 : SetRowSpan(curCell, rowSpan+aNumber);
598 : }
599 : } else {
600 : // We have a cell in the insert row
601 :
602 : // Count the number of cells we need to add to the new row
603 0 : cellsInRow += actualColSpan;
604 :
605 : // Save cell we will use below
606 0 : if (!cellForRowParent) {
607 0 : cellForRowParent = curCell;
608 : }
609 : }
610 : // Next cell in row
611 0 : colIndex += actualColSpan;
612 : } else {
613 0 : colIndex++;
614 : }
615 : }
616 : } else {
617 : // We are adding a new row after all others
618 : // If it weren't for colspan=0 effect,
619 : // we could simply use colCount for number of new cells...
620 : // XXX colspan=0 support has now been removed in table layout so maybe this can be cleaned up now? (bug 1243183)
621 0 : cellsInRow = colCount;
622 :
623 : // ...but we must compensate for all cells with rowSpan = 0 in the last row
624 0 : int32_t lastRow = rowCount-1;
625 0 : int32_t tempColIndex = 0;
626 0 : while (NS_SUCCEEDED(GetCellDataAt(table, lastRow, tempColIndex,
627 : getter_AddRefs(curCell),
628 : &curStartRowIndex, &curStartColIndex,
629 : &rowSpan, &colSpan,
630 : &actualRowSpan, &actualColSpan,
631 : &isSelected))) {
632 0 : if (!rowSpan) {
633 0 : cellsInRow -= actualColSpan;
634 : }
635 :
636 0 : tempColIndex += actualColSpan;
637 :
638 : // Save cell from the last row that we will use below
639 0 : if (!cellForRowParent && curStartRowIndex == lastRow) {
640 0 : cellForRowParent = curCell;
641 : }
642 : }
643 : }
644 :
645 0 : nsCOMPtr<nsINode> cellNodeForRowParent = do_QueryInterface(cellForRowParent);
646 :
647 0 : if (cellsInRow > 0) {
648 :
649 0 : NS_NAMED_LITERAL_STRING(trStr, "tr");
650 0 : if (!cellNodeForRowParent) {
651 0 : return NS_ERROR_FAILURE;
652 : }
653 :
654 : nsCOMPtr<Element> parentRow =
655 0 : GetElementOrParentByTagName(trStr, cellNodeForRowParent);
656 0 : NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER);
657 :
658 : // The row parent and offset where we will insert new row
659 0 : nsCOMPtr<nsINode> parentOfRow = parentRow->GetParentNode();
660 0 : NS_ENSURE_TRUE(parentOfRow, NS_ERROR_NULL_POINTER);
661 0 : int32_t newRowOffset = parentOfRow->IndexOf(parentRow);
662 :
663 : // Adjust for when adding past the end
664 0 : if (aAfter && startRowIndex >= rowCount) {
665 0 : newRowOffset++;
666 : }
667 :
668 0 : for (int32_t row = 0; row < aNumber; row++) {
669 : // Create a new row
670 0 : nsCOMPtr<Element> newRow = CreateElementWithDefaults(trStr);
671 0 : NS_ENSURE_TRUE(newRow, NS_ERROR_FAILURE);
672 :
673 0 : for (int32_t i = 0; i < cellsInRow; i++) {
674 : nsCOMPtr<Element> newCell =
675 0 : CreateElementWithDefaults(NS_LITERAL_STRING("td"));
676 0 : NS_ENSURE_TRUE(newCell, NS_ERROR_FAILURE);
677 :
678 : // Don't use transaction system yet! (not until entire row is
679 : // inserted)
680 0 : ErrorResult result;
681 0 : newRow->AppendChild(*newCell, result);
682 0 : if (NS_WARN_IF(result.Failed())) {
683 0 : return result.StealNSResult();
684 : }
685 : }
686 :
687 : // Use transaction system to insert the entire row+cells
688 : // (Note that rows are inserted at same childoffset each time)
689 0 : rv = InsertNode(*newRow, *parentOfRow, newRowOffset);
690 0 : NS_ENSURE_SUCCESS(rv, rv);
691 : }
692 : }
693 : // XXX This might be the result of the last call of
694 : // CreateElementWithDefaults(), otherwise, NS_OK.
695 0 : return rv;
696 : }
697 :
698 : // Editor helper only
699 : // XXX Code changed for bug 217717 and now we don't need aSelection param
700 : // TODO: Remove aSelection param
701 : nsresult
702 0 : HTMLEditor::DeleteTable2(nsIDOMElement* aTable,
703 : Selection* aSelection)
704 : {
705 0 : NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
706 :
707 : // Select the table
708 0 : nsresult rv = ClearSelection();
709 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
710 0 : return rv;
711 : }
712 0 : rv = AppendNodeToSelectionAsRange(aTable);
713 0 : NS_ENSURE_SUCCESS(rv, rv);
714 :
715 0 : return DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
716 : }
717 :
718 : NS_IMETHODIMP
719 0 : HTMLEditor::DeleteTable()
720 : {
721 0 : RefPtr<Selection> selection;
722 0 : nsCOMPtr<nsIDOMElement> table;
723 0 : nsresult rv = GetCellContext(getter_AddRefs(selection),
724 0 : getter_AddRefs(table),
725 0 : nullptr, nullptr, nullptr, nullptr, nullptr);
726 0 : NS_ENSURE_SUCCESS(rv, rv);
727 :
728 0 : AutoEditBatch beginBatching(this);
729 0 : return DeleteTable2(table, selection);
730 : }
731 :
732 : NS_IMETHODIMP
733 0 : HTMLEditor::DeleteTableCell(int32_t aNumber)
734 : {
735 0 : RefPtr<Selection> selection;
736 0 : nsCOMPtr<nsIDOMElement> table;
737 0 : nsCOMPtr<nsIDOMElement> cell;
738 : int32_t startRowIndex, startColIndex;
739 :
740 :
741 0 : nsresult rv = GetCellContext(getter_AddRefs(selection),
742 0 : getter_AddRefs(table),
743 0 : getter_AddRefs(cell),
744 : nullptr, nullptr,
745 0 : &startRowIndex, &startColIndex);
746 :
747 0 : NS_ENSURE_SUCCESS(rv, rv);
748 : // Don't fail if we didn't find a table or cell
749 0 : NS_ENSURE_TRUE(table && cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
750 :
751 0 : AutoEditBatch beginBatching(this);
752 : // Prevent rules testing until we're done
753 0 : AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
754 :
755 0 : nsCOMPtr<nsIDOMElement> firstCell;
756 0 : nsCOMPtr<nsIDOMRange> range;
757 0 : rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
758 0 : NS_ENSURE_SUCCESS(rv, rv);
759 :
760 : int32_t rangeCount;
761 0 : rv = selection->GetRangeCount(&rangeCount);
762 0 : NS_ENSURE_SUCCESS(rv, rv);
763 :
764 0 : if (firstCell && rangeCount > 1) {
765 : // When > 1 selected cell,
766 : // ignore aNumber and use selected cells
767 0 : cell = firstCell;
768 :
769 : int32_t rowCount, colCount;
770 0 : rv = GetTableSize(table, &rowCount, &colCount);
771 0 : NS_ENSURE_SUCCESS(rv, rv);
772 :
773 : // Get indexes -- may be different than original cell
774 0 : rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
775 0 : NS_ENSURE_SUCCESS(rv, rv);
776 :
777 : // The setCaret object will call AutoSelectionSetterAfterTableEdit in its
778 : // destructor
779 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
780 : startColIndex, ePreviousColumn,
781 0 : false);
782 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
783 :
784 0 : bool checkToDeleteRow = true;
785 0 : bool checkToDeleteColumn = true;
786 0 : while (cell) {
787 0 : bool deleteRow = false;
788 0 : bool deleteCol = false;
789 :
790 0 : if (checkToDeleteRow) {
791 : // Optimize to delete an entire row
792 : // Clear so we don't repeat AllCellsInRowSelected within the same row
793 0 : checkToDeleteRow = false;
794 :
795 0 : deleteRow = AllCellsInRowSelected(table, startRowIndex, colCount);
796 0 : if (deleteRow) {
797 : // First, find the next cell in a different row
798 : // to continue after we delete this row
799 0 : int32_t nextRow = startRowIndex;
800 0 : while (nextRow == startRowIndex) {
801 0 : rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
802 0 : NS_ENSURE_SUCCESS(rv, rv);
803 0 : if (!cell) {
804 0 : break;
805 : }
806 0 : rv = GetCellIndexes(cell, &nextRow, &startColIndex);
807 0 : NS_ENSURE_SUCCESS(rv, rv);
808 : }
809 : // Delete entire row
810 0 : rv = DeleteRow(table, startRowIndex);
811 0 : NS_ENSURE_SUCCESS(rv, rv);
812 :
813 0 : if (cell) {
814 : // For the next cell: Subtract 1 for row we deleted
815 0 : startRowIndex = nextRow - 1;
816 : // Set true since we know we will look at a new row next
817 0 : checkToDeleteRow = true;
818 : }
819 : }
820 : }
821 0 : if (!deleteRow) {
822 0 : if (checkToDeleteColumn) {
823 : // Optimize to delete an entire column
824 : // Clear this so we don't repeat AllCellsInColSelected within the same Col
825 0 : checkToDeleteColumn = false;
826 :
827 0 : deleteCol = AllCellsInColumnSelected(table, startColIndex, colCount);
828 0 : if (deleteCol) {
829 : // First, find the next cell in a different column
830 : // to continue after we delete this column
831 0 : int32_t nextCol = startColIndex;
832 0 : while (nextCol == startColIndex) {
833 0 : rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
834 0 : NS_ENSURE_SUCCESS(rv, rv);
835 0 : if (!cell) {
836 0 : break;
837 : }
838 0 : rv = GetCellIndexes(cell, &startRowIndex, &nextCol);
839 0 : NS_ENSURE_SUCCESS(rv, rv);
840 : }
841 : // Delete entire Col
842 0 : rv = DeleteColumn(table, startColIndex);
843 0 : NS_ENSURE_SUCCESS(rv, rv);
844 0 : if (cell) {
845 : // For the next cell, subtract 1 for col. deleted
846 0 : startColIndex = nextCol - 1;
847 : // Set true since we know we will look at a new column next
848 0 : checkToDeleteColumn = true;
849 : }
850 : }
851 : }
852 0 : if (!deleteCol) {
853 : // First get the next cell to delete
854 0 : nsCOMPtr<nsIDOMElement> nextCell;
855 0 : rv = GetNextSelectedCell(getter_AddRefs(range),
856 0 : getter_AddRefs(nextCell));
857 0 : NS_ENSURE_SUCCESS(rv, rv);
858 :
859 : // Then delete the cell
860 0 : rv = DeleteNode(cell);
861 0 : NS_ENSURE_SUCCESS(rv, rv);
862 :
863 : // The next cell to delete
864 0 : cell = nextCell;
865 0 : if (cell) {
866 0 : rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
867 0 : NS_ENSURE_SUCCESS(rv, rv);
868 : }
869 : }
870 : }
871 : }
872 : } else {
873 0 : for (int32_t i = 0; i < aNumber; i++) {
874 0 : rv = GetCellContext(getter_AddRefs(selection),
875 0 : getter_AddRefs(table),
876 0 : getter_AddRefs(cell),
877 : nullptr, nullptr,
878 0 : &startRowIndex, &startColIndex);
879 0 : NS_ENSURE_SUCCESS(rv, rv);
880 : // Don't fail if no cell found
881 0 : NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
882 :
883 0 : if (GetNumberOfCellsInRow(table, startRowIndex) == 1) {
884 0 : nsCOMPtr<nsIDOMElement> parentRow;
885 0 : rv = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell,
886 0 : getter_AddRefs(parentRow));
887 0 : NS_ENSURE_SUCCESS(rv, rv);
888 0 : NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER);
889 :
890 : // We should delete the row instead,
891 : // but first check if its the only row left
892 : // so we can delete the entire table
893 : int32_t rowCount, colCount;
894 0 : rv = GetTableSize(table, &rowCount, &colCount);
895 0 : NS_ENSURE_SUCCESS(rv, rv);
896 :
897 0 : if (rowCount == 1) {
898 0 : return DeleteTable2(table, selection);
899 : }
900 :
901 : // We need to call DeleteTableRow to handle cells with rowspan
902 0 : rv = DeleteTableRow(1);
903 0 : NS_ENSURE_SUCCESS(rv, rv);
904 : } else {
905 : // More than 1 cell in the row
906 :
907 : // The setCaret object will call AutoSelectionSetterAfterTableEdit in its
908 : // destructor
909 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
910 : startColIndex, ePreviousColumn,
911 0 : false);
912 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
913 :
914 0 : rv = DeleteNode(cell);
915 : // If we fail, don't try to delete any more cells???
916 0 : NS_ENSURE_SUCCESS(rv, rv);
917 : }
918 : }
919 : }
920 0 : return NS_OK;
921 : }
922 :
923 : NS_IMETHODIMP
924 0 : HTMLEditor::DeleteTableCellContents()
925 : {
926 0 : RefPtr<Selection> selection;
927 0 : nsCOMPtr<nsIDOMElement> table;
928 0 : nsCOMPtr<nsIDOMElement> cell;
929 : int32_t startRowIndex, startColIndex;
930 0 : nsresult rv = GetCellContext(getter_AddRefs(selection),
931 0 : getter_AddRefs(table),
932 0 : getter_AddRefs(cell),
933 : nullptr, nullptr,
934 0 : &startRowIndex, &startColIndex);
935 0 : NS_ENSURE_SUCCESS(rv, rv);
936 : // Don't fail if no cell found
937 0 : NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
938 :
939 :
940 0 : AutoEditBatch beginBatching(this);
941 : // Prevent rules testing until we're done
942 0 : AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
943 : //Don't let Rules System change the selection
944 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
945 :
946 :
947 0 : nsCOMPtr<nsIDOMElement> firstCell;
948 0 : nsCOMPtr<nsIDOMRange> range;
949 0 : rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
950 0 : NS_ENSURE_SUCCESS(rv, rv);
951 :
952 :
953 0 : if (firstCell) {
954 0 : cell = firstCell;
955 0 : rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
956 0 : NS_ENSURE_SUCCESS(rv, rv);
957 : }
958 :
959 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
960 : startColIndex, ePreviousColumn,
961 0 : false);
962 :
963 0 : while (cell) {
964 0 : DeleteCellContents(cell);
965 0 : if (firstCell) {
966 : // We doing a selected cells, so do all of them
967 0 : rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
968 0 : NS_ENSURE_SUCCESS(rv, rv);
969 : } else {
970 0 : cell = nullptr;
971 : }
972 : }
973 0 : return NS_OK;
974 : }
975 :
976 : NS_IMETHODIMP
977 0 : HTMLEditor::DeleteCellContents(nsIDOMElement* aCell)
978 : {
979 0 : NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
980 :
981 : // Prevent rules testing until we're done
982 0 : AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
983 :
984 0 : nsCOMPtr<nsIDOMNode> child;
985 : bool hasChild;
986 0 : aCell->HasChildNodes(&hasChild);
987 :
988 0 : while (hasChild) {
989 0 : aCell->GetLastChild(getter_AddRefs(child));
990 0 : nsresult rv = DeleteNode(child);
991 0 : NS_ENSURE_SUCCESS(rv, rv);
992 0 : aCell->HasChildNodes(&hasChild);
993 : }
994 0 : return NS_OK;
995 : }
996 :
997 : NS_IMETHODIMP
998 0 : HTMLEditor::DeleteTableColumn(int32_t aNumber)
999 : {
1000 0 : RefPtr<Selection> selection;
1001 0 : nsCOMPtr<nsIDOMElement> table;
1002 0 : nsCOMPtr<nsIDOMElement> cell;
1003 : int32_t startRowIndex, startColIndex, rowCount, colCount;
1004 0 : nsresult rv = GetCellContext(getter_AddRefs(selection),
1005 0 : getter_AddRefs(table),
1006 0 : getter_AddRefs(cell),
1007 : nullptr, nullptr,
1008 0 : &startRowIndex, &startColIndex);
1009 0 : NS_ENSURE_SUCCESS(rv, rv);
1010 : // Don't fail if no cell found
1011 0 : NS_ENSURE_TRUE(table && cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
1012 :
1013 0 : rv = GetTableSize(table, &rowCount, &colCount);
1014 0 : NS_ENSURE_SUCCESS(rv, rv);
1015 :
1016 : // Shortcut the case of deleting all columns in table
1017 0 : if (!startColIndex && aNumber >= colCount) {
1018 0 : return DeleteTable2(table, selection);
1019 : }
1020 :
1021 : // Check for counts too high
1022 0 : aNumber = std::min(aNumber,(colCount-startColIndex));
1023 :
1024 0 : AutoEditBatch beginBatching(this);
1025 : // Prevent rules testing until we're done
1026 0 : AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
1027 :
1028 : // Test if deletion is controlled by selected cells
1029 0 : nsCOMPtr<nsIDOMElement> firstCell;
1030 0 : nsCOMPtr<nsIDOMRange> range;
1031 0 : rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
1032 0 : NS_ENSURE_SUCCESS(rv, rv);
1033 :
1034 : int32_t rangeCount;
1035 0 : rv = selection->GetRangeCount(&rangeCount);
1036 0 : NS_ENSURE_SUCCESS(rv, rv);
1037 :
1038 0 : if (firstCell && rangeCount > 1) {
1039 : // Fetch indexes again - may be different for selected cells
1040 0 : rv = GetCellIndexes(firstCell, &startRowIndex, &startColIndex);
1041 0 : NS_ENSURE_SUCCESS(rv, rv);
1042 : }
1043 : //We control selection resetting after the insert...
1044 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
1045 : startColIndex, ePreviousRow,
1046 0 : false);
1047 :
1048 0 : if (firstCell && rangeCount > 1) {
1049 : // Use selected cells to determine what rows to delete
1050 0 : cell = firstCell;
1051 :
1052 0 : while (cell) {
1053 0 : if (cell != firstCell) {
1054 0 : rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
1055 0 : NS_ENSURE_SUCCESS(rv, rv);
1056 : }
1057 : // Find the next cell in a different column
1058 : // to continue after we delete this column
1059 0 : int32_t nextCol = startColIndex;
1060 0 : while (nextCol == startColIndex) {
1061 0 : rv = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
1062 0 : NS_ENSURE_SUCCESS(rv, rv);
1063 0 : if (!cell) {
1064 0 : break;
1065 : }
1066 0 : rv = GetCellIndexes(cell, &startRowIndex, &nextCol);
1067 0 : NS_ENSURE_SUCCESS(rv, rv);
1068 : }
1069 0 : rv = DeleteColumn(table, startColIndex);
1070 0 : NS_ENSURE_SUCCESS(rv, rv);
1071 : }
1072 : } else {
1073 0 : for (int32_t i = 0; i < aNumber; i++) {
1074 0 : rv = DeleteColumn(table, startColIndex);
1075 0 : NS_ENSURE_SUCCESS(rv, rv);
1076 : }
1077 : }
1078 0 : return NS_OK;
1079 : }
1080 :
1081 : NS_IMETHODIMP
1082 0 : HTMLEditor::DeleteColumn(nsIDOMElement* aTable,
1083 : int32_t aColIndex)
1084 : {
1085 0 : NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
1086 :
1087 0 : nsCOMPtr<nsIDOMElement> cell;
1088 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
1089 : bool isSelected;
1090 0 : int32_t rowIndex = 0;
1091 :
1092 0 : do {
1093 : nsresult rv =
1094 0 : GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
1095 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
1096 0 : &actualRowSpan, &actualColSpan, &isSelected);
1097 0 : NS_ENSURE_SUCCESS(rv, rv);
1098 :
1099 0 : if (cell) {
1100 : // Find cells that don't start in column we are deleting
1101 0 : if (startColIndex < aColIndex || colSpan > 1 || !colSpan) {
1102 : // We have a cell spanning this location
1103 : // Decrease its colspan to keep table rectangular,
1104 : // but if colSpan=0, it will adjust automatically
1105 0 : if (colSpan > 0) {
1106 0 : NS_ASSERTION((colSpan > 1),"Bad COLSPAN in DeleteTableColumn");
1107 0 : SetColSpan(cell, colSpan-1);
1108 : }
1109 0 : if (startColIndex == aColIndex) {
1110 : // Cell is in column to be deleted, but must have colspan > 1,
1111 : // so delete contents of cell instead of cell itself
1112 : // (We must have reset colspan above)
1113 0 : DeleteCellContents(cell);
1114 : }
1115 : // To next cell in column
1116 0 : rowIndex += actualRowSpan;
1117 : } else {
1118 : // Delete the cell
1119 0 : if (GetNumberOfCellsInRow(aTable, rowIndex) == 1) {
1120 : // Only 1 cell in row - delete the row
1121 0 : nsCOMPtr<nsIDOMElement> parentRow;
1122 0 : rv = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell,
1123 0 : getter_AddRefs(parentRow));
1124 0 : NS_ENSURE_SUCCESS(rv, rv);
1125 0 : if (!parentRow) {
1126 0 : return NS_ERROR_NULL_POINTER;
1127 : }
1128 :
1129 : // But first check if its the only row left
1130 : // so we can delete the entire table
1131 : // (This should never happen but it's the safe thing to do)
1132 : int32_t rowCount, colCount;
1133 0 : rv = GetTableSize(aTable, &rowCount, &colCount);
1134 0 : NS_ENSURE_SUCCESS(rv, rv);
1135 :
1136 0 : if (rowCount == 1) {
1137 0 : RefPtr<Selection> selection = GetSelection();
1138 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1139 0 : return DeleteTable2(aTable, selection);
1140 : }
1141 :
1142 : // Delete the row by placing caret in cell we were to delete
1143 : // We need to call DeleteTableRow to handle cells with rowspan
1144 0 : rv = DeleteRow(aTable, startRowIndex);
1145 0 : NS_ENSURE_SUCCESS(rv, rv);
1146 :
1147 : // Note that we don't incremenet rowIndex
1148 : // since a row was deleted and "next"
1149 : // row now has current rowIndex
1150 : } else {
1151 : // A more "normal" deletion
1152 0 : rv = DeleteNode(cell);
1153 0 : NS_ENSURE_SUCCESS(rv, rv);
1154 :
1155 : //Skip over any rows spanned by this cell
1156 0 : rowIndex += actualRowSpan;
1157 : }
1158 : }
1159 : }
1160 : } while (cell);
1161 :
1162 0 : return NS_OK;
1163 : }
1164 :
1165 : NS_IMETHODIMP
1166 0 : HTMLEditor::DeleteTableRow(int32_t aNumber)
1167 : {
1168 0 : RefPtr<Selection> selection;
1169 0 : nsCOMPtr<nsIDOMElement> table;
1170 0 : nsCOMPtr<nsIDOMElement> cell;
1171 : int32_t startRowIndex, startColIndex;
1172 : int32_t rowCount, colCount;
1173 0 : nsresult rv = GetCellContext(getter_AddRefs(selection),
1174 0 : getter_AddRefs(table),
1175 0 : getter_AddRefs(cell),
1176 : nullptr, nullptr,
1177 0 : &startRowIndex, &startColIndex);
1178 0 : NS_ENSURE_SUCCESS(rv, rv);
1179 : // Don't fail if no cell found
1180 0 : NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
1181 :
1182 0 : rv = GetTableSize(table, &rowCount, &colCount);
1183 0 : NS_ENSURE_SUCCESS(rv, rv);
1184 :
1185 : // Shortcut the case of deleting all rows in table
1186 0 : if (!startRowIndex && aNumber >= rowCount) {
1187 0 : return DeleteTable2(table, selection);
1188 : }
1189 :
1190 0 : AutoEditBatch beginBatching(this);
1191 : // Prevent rules testing until we're done
1192 0 : AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
1193 :
1194 0 : nsCOMPtr<nsIDOMElement> firstCell;
1195 0 : nsCOMPtr<nsIDOMRange> range;
1196 0 : rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
1197 0 : NS_ENSURE_SUCCESS(rv, rv);
1198 :
1199 : int32_t rangeCount;
1200 0 : rv = selection->GetRangeCount(&rangeCount);
1201 0 : NS_ENSURE_SUCCESS(rv, rv);
1202 :
1203 0 : if (firstCell && rangeCount > 1) {
1204 : // Fetch indexes again - may be different for selected cells
1205 0 : rv = GetCellIndexes(firstCell, &startRowIndex, &startColIndex);
1206 0 : NS_ENSURE_SUCCESS(rv, rv);
1207 : }
1208 :
1209 : //We control selection resetting after the insert...
1210 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
1211 : startColIndex, ePreviousRow,
1212 0 : false);
1213 : // Don't change selection during deletions
1214 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
1215 :
1216 0 : if (firstCell && rangeCount > 1) {
1217 : // Use selected cells to determine what rows to delete
1218 0 : cell = firstCell;
1219 :
1220 0 : while (cell) {
1221 0 : if (cell != firstCell) {
1222 0 : rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
1223 0 : NS_ENSURE_SUCCESS(rv, rv);
1224 : }
1225 : // Find the next cell in a different row
1226 : // to continue after we delete this row
1227 0 : int32_t nextRow = startRowIndex;
1228 0 : while (nextRow == startRowIndex) {
1229 0 : rv = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
1230 0 : NS_ENSURE_SUCCESS(rv, rv);
1231 0 : if (!cell) break;
1232 0 : rv = GetCellIndexes(cell, &nextRow, &startColIndex);
1233 0 : NS_ENSURE_SUCCESS(rv, rv);
1234 : }
1235 : // Delete entire row
1236 0 : rv = DeleteRow(table, startRowIndex);
1237 0 : NS_ENSURE_SUCCESS(rv, rv);
1238 : }
1239 : } else {
1240 : // Check for counts too high
1241 0 : aNumber = std::min(aNumber,(rowCount-startRowIndex));
1242 0 : for (int32_t i = 0; i < aNumber; i++) {
1243 0 : rv = DeleteRow(table, startRowIndex);
1244 : // If failed in current row, try the next
1245 0 : if (NS_FAILED(rv)) {
1246 0 : startRowIndex++;
1247 : }
1248 :
1249 : // Check if there's a cell in the "next" row
1250 0 : rv = GetCellAt(table, startRowIndex, startColIndex, getter_AddRefs(cell));
1251 0 : NS_ENSURE_SUCCESS(rv, rv);
1252 0 : if (!cell) {
1253 0 : break;
1254 : }
1255 : }
1256 : }
1257 0 : return NS_OK;
1258 : }
1259 :
1260 : // Helper that doesn't batch or change the selection
1261 : NS_IMETHODIMP
1262 0 : HTMLEditor::DeleteRow(nsIDOMElement* aTable,
1263 : int32_t aRowIndex)
1264 : {
1265 0 : NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
1266 :
1267 0 : nsCOMPtr<nsIDOMElement> cell;
1268 0 : nsCOMPtr<nsIDOMElement> cellInDeleteRow;
1269 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
1270 : bool isSelected;
1271 0 : int32_t colIndex = 0;
1272 :
1273 : // Prevent rules testing until we're done
1274 0 : AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
1275 :
1276 : // The list of cells we will change rowspan in
1277 : // and the new rowspan values for each
1278 0 : nsTArray<nsCOMPtr<nsIDOMElement> > spanCellList;
1279 0 : nsTArray<int32_t> newSpanList;
1280 :
1281 : int32_t rowCount, colCount;
1282 0 : nsresult rv = GetTableSize(aTable, &rowCount, &colCount);
1283 0 : NS_ENSURE_SUCCESS(rv, rv);
1284 :
1285 : // Scan through cells in row to do rowspan adjustments
1286 : // Note that after we delete row, startRowIndex will point to the
1287 : // cells in the next row to be deleted
1288 0 : do {
1289 0 : if (aRowIndex >= rowCount || colIndex >= colCount) {
1290 : break;
1291 : }
1292 :
1293 0 : rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
1294 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
1295 0 : &actualRowSpan, &actualColSpan, &isSelected);
1296 : // We don't fail if we don't find a cell, so this must be real bad
1297 0 : if (NS_FAILED(rv)) {
1298 0 : return rv;
1299 : }
1300 :
1301 : // Compensate for cells that don't start or extend below the row we are deleting
1302 0 : if (cell) {
1303 0 : if (startRowIndex < aRowIndex) {
1304 : // Cell starts in row above us
1305 : // Decrease its rowspan to keep table rectangular
1306 : // but we don't need to do this if rowspan=0,
1307 : // since it will automatically adjust
1308 0 : if (rowSpan > 0) {
1309 : // Build list of cells to change rowspan
1310 : // We can't do it now since it upsets cell map,
1311 : // so we will do it after deleting the row
1312 0 : spanCellList.AppendElement(cell);
1313 0 : newSpanList.AppendElement(std::max((aRowIndex - startRowIndex), actualRowSpan-1));
1314 : }
1315 : } else {
1316 0 : if (rowSpan > 1) {
1317 : // Cell spans below row to delete, so we must insert new cells to
1318 : // keep rows below. Note that we test "rowSpan" so we don't do this
1319 : // if rowSpan = 0 (automatic readjustment).
1320 0 : int32_t aboveRowToInsertNewCellInto = aRowIndex - startRowIndex + 1;
1321 0 : int32_t numOfRawSpanRemainingBelow = actualRowSpan - 1;
1322 0 : rv = SplitCellIntoRows(aTable, startRowIndex, startColIndex,
1323 : aboveRowToInsertNewCellInto,
1324 0 : numOfRawSpanRemainingBelow, nullptr);
1325 0 : NS_ENSURE_SUCCESS(rv, rv);
1326 : }
1327 0 : if (!cellInDeleteRow) {
1328 0 : cellInDeleteRow = cell; // Reference cell to find row to delete
1329 : }
1330 : }
1331 : // Skip over other columns spanned by this cell
1332 0 : colIndex += actualColSpan;
1333 : }
1334 : } while (cell);
1335 :
1336 : // Things are messed up if we didn't find a cell in the row!
1337 0 : NS_ENSURE_TRUE(cellInDeleteRow, NS_ERROR_FAILURE);
1338 :
1339 : // Delete the entire row
1340 0 : nsCOMPtr<nsIDOMElement> parentRow;
1341 0 : rv = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow,
1342 0 : getter_AddRefs(parentRow));
1343 0 : NS_ENSURE_SUCCESS(rv, rv);
1344 :
1345 0 : if (parentRow) {
1346 0 : rv = DeleteNode(parentRow);
1347 0 : NS_ENSURE_SUCCESS(rv, rv);
1348 : }
1349 :
1350 : // Now we can set new rowspans for cells stored above
1351 0 : for (uint32_t i = 0, n = spanCellList.Length(); i < n; i++) {
1352 0 : nsIDOMElement *cellPtr = spanCellList[i];
1353 0 : if (cellPtr) {
1354 0 : rv = SetRowSpan(cellPtr, newSpanList[i]);
1355 0 : NS_ENSURE_SUCCESS(rv, rv);
1356 : }
1357 : }
1358 0 : return NS_OK;
1359 : }
1360 :
1361 :
1362 : NS_IMETHODIMP
1363 0 : HTMLEditor::SelectTable()
1364 : {
1365 0 : nsCOMPtr<nsIDOMElement> table;
1366 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr,
1367 0 : getter_AddRefs(table));
1368 0 : NS_ENSURE_SUCCESS(rv, rv);
1369 : // Don't fail if we didn't find a table
1370 0 : NS_ENSURE_TRUE(table, NS_OK);
1371 :
1372 0 : rv = ClearSelection();
1373 0 : if (NS_FAILED(rv)) {
1374 0 : return rv;
1375 : }
1376 0 : return AppendNodeToSelectionAsRange(table);
1377 : }
1378 :
1379 : NS_IMETHODIMP
1380 0 : HTMLEditor::SelectTableCell()
1381 : {
1382 0 : nsCOMPtr<nsIDOMElement> cell;
1383 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
1384 0 : getter_AddRefs(cell));
1385 0 : NS_ENSURE_SUCCESS(rv, rv);
1386 0 : NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
1387 :
1388 0 : rv = ClearSelection();
1389 0 : if (NS_FAILED(rv)) {
1390 0 : return rv;
1391 : }
1392 0 : return AppendNodeToSelectionAsRange(cell);
1393 : }
1394 :
1395 : NS_IMETHODIMP
1396 0 : HTMLEditor::SelectBlockOfCells(nsIDOMElement* aStartCell,
1397 : nsIDOMElement* aEndCell)
1398 : {
1399 0 : NS_ENSURE_TRUE(aStartCell && aEndCell, NS_ERROR_NULL_POINTER);
1400 :
1401 0 : RefPtr<Selection> selection = GetSelection();
1402 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1403 :
1404 0 : NS_NAMED_LITERAL_STRING(tableStr, "table");
1405 0 : nsCOMPtr<nsIDOMElement> table;
1406 0 : nsresult rv = GetElementOrParentByTagName(tableStr, aStartCell,
1407 0 : getter_AddRefs(table));
1408 0 : NS_ENSURE_SUCCESS(rv, rv);
1409 0 : NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
1410 :
1411 0 : nsCOMPtr<nsIDOMElement> endTable;
1412 0 : rv = GetElementOrParentByTagName(tableStr, aEndCell,
1413 0 : getter_AddRefs(endTable));
1414 0 : NS_ENSURE_SUCCESS(rv, rv);
1415 0 : NS_ENSURE_TRUE(endTable, NS_ERROR_FAILURE);
1416 :
1417 : // We can only select a block if within the same table,
1418 : // so do nothing if not within one table
1419 0 : if (table != endTable) {
1420 0 : return NS_OK;
1421 : }
1422 :
1423 : int32_t startRowIndex, startColIndex, endRowIndex, endColIndex;
1424 :
1425 : // Get starting and ending cells' location in the cellmap
1426 0 : rv = GetCellIndexes(aStartCell, &startRowIndex, &startColIndex);
1427 0 : if (NS_FAILED(rv)) {
1428 0 : return rv;
1429 : }
1430 :
1431 0 : rv = GetCellIndexes(aEndCell, &endRowIndex, &endColIndex);
1432 0 : if (NS_FAILED(rv)) {
1433 0 : return rv;
1434 : }
1435 :
1436 : // Suppress nsISelectionListener notification
1437 : // until all selection changes are finished
1438 0 : SelectionBatcher selectionBatcher(selection);
1439 :
1440 : // Examine all cell nodes in current selection and
1441 : // remove those outside the new block cell region
1442 0 : int32_t minColumn = std::min(startColIndex, endColIndex);
1443 0 : int32_t minRow = std::min(startRowIndex, endRowIndex);
1444 0 : int32_t maxColumn = std::max(startColIndex, endColIndex);
1445 0 : int32_t maxRow = std::max(startRowIndex, endRowIndex);
1446 :
1447 0 : nsCOMPtr<nsIDOMElement> cell;
1448 : int32_t currentRowIndex, currentColIndex;
1449 0 : nsCOMPtr<nsIDOMRange> range;
1450 0 : rv = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
1451 0 : NS_ENSURE_SUCCESS(rv, rv);
1452 0 : if (rv == NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND) {
1453 0 : return NS_OK;
1454 : }
1455 :
1456 0 : while (cell) {
1457 0 : rv = GetCellIndexes(cell, ¤tRowIndex, ¤tColIndex);
1458 0 : NS_ENSURE_SUCCESS(rv, rv);
1459 :
1460 0 : if (currentRowIndex < maxRow || currentRowIndex > maxRow ||
1461 0 : currentColIndex < maxColumn || currentColIndex > maxColumn) {
1462 0 : selection->RemoveRange(range);
1463 : // Since we've removed the range, decrement pointer to next range
1464 0 : mSelectedCellIndex--;
1465 : }
1466 0 : rv = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
1467 0 : NS_ENSURE_SUCCESS(rv, rv);
1468 : }
1469 :
1470 : int32_t rowSpan, colSpan, actualRowSpan, actualColSpan;
1471 : bool isSelected;
1472 0 : for (int32_t row = minRow; row <= maxRow; row++) {
1473 0 : for (int32_t col = minColumn; col <= maxColumn;
1474 0 : col += std::max(actualColSpan, 1)) {
1475 0 : rv = GetCellDataAt(table, row, col, getter_AddRefs(cell),
1476 : ¤tRowIndex, ¤tColIndex,
1477 : &rowSpan, &colSpan,
1478 0 : &actualRowSpan, &actualColSpan, &isSelected);
1479 0 : if (NS_FAILED(rv)) {
1480 0 : break;
1481 : }
1482 : // Skip cells that already selected or are spanned from previous locations
1483 0 : if (!isSelected && cell &&
1484 0 : row == currentRowIndex && col == currentColIndex) {
1485 0 : rv = AppendNodeToSelectionAsRange(cell);
1486 0 : if (NS_FAILED(rv)) {
1487 0 : break;
1488 : }
1489 : }
1490 : }
1491 : }
1492 : // NS_OK, otherwise, the last failure of GetCellDataAt() or
1493 : // AppendNodeToSelectionAsRange().
1494 0 : return rv;
1495 : }
1496 :
1497 : NS_IMETHODIMP
1498 0 : HTMLEditor::SelectAllTableCells()
1499 : {
1500 0 : nsCOMPtr<nsIDOMElement> cell;
1501 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
1502 0 : getter_AddRefs(cell));
1503 0 : NS_ENSURE_SUCCESS(rv, rv);
1504 :
1505 : // Don't fail if we didn't find a cell
1506 0 : NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
1507 :
1508 0 : nsCOMPtr<nsIDOMElement> startCell = cell;
1509 :
1510 : // Get parent table
1511 0 : nsCOMPtr<nsIDOMElement> table;
1512 0 : rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell,
1513 0 : getter_AddRefs(table));
1514 0 : NS_ENSURE_SUCCESS(rv, rv);
1515 0 : if (!table) {
1516 0 : return NS_ERROR_NULL_POINTER;
1517 : }
1518 :
1519 : int32_t rowCount, colCount;
1520 0 : rv = GetTableSize(table, &rowCount, &colCount);
1521 0 : NS_ENSURE_SUCCESS(rv, rv);
1522 :
1523 0 : RefPtr<Selection> selection = GetSelection();
1524 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1525 :
1526 : // Suppress nsISelectionListener notification
1527 : // until all selection changes are finished
1528 0 : SelectionBatcher selectionBatcher(selection);
1529 :
1530 : // It is now safe to clear the selection
1531 : // BE SURE TO RESET IT BEFORE LEAVING!
1532 0 : rv = ClearSelection();
1533 :
1534 : // Select all cells in the same column as current cell
1535 0 : bool cellSelected = false;
1536 : int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
1537 : bool isSelected;
1538 0 : for (int32_t row = 0; row < rowCount; row++) {
1539 0 : for (int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) {
1540 0 : rv = GetCellDataAt(table, row, col, getter_AddRefs(cell),
1541 : ¤tRowIndex, ¤tColIndex,
1542 : &rowSpan, &colSpan,
1543 0 : &actualRowSpan, &actualColSpan, &isSelected);
1544 0 : if (NS_FAILED(rv)) {
1545 0 : break;
1546 : }
1547 : // Skip cells that are spanned from previous rows or columns
1548 0 : if (cell && row == currentRowIndex && col == currentColIndex) {
1549 0 : rv = AppendNodeToSelectionAsRange(cell);
1550 0 : if (NS_FAILED(rv)) {
1551 0 : break;
1552 : }
1553 0 : cellSelected = true;
1554 : }
1555 : }
1556 : }
1557 : // Safety code to select starting cell if nothing else was selected
1558 0 : if (!cellSelected) {
1559 0 : return AppendNodeToSelectionAsRange(startCell);
1560 : }
1561 : // NS_OK, otherwise, the error of ClearSelection() when there is no column or
1562 : // the last failure of GetCellDataAt() or AppendNodeToSelectionAsRange().
1563 0 : return rv;
1564 : }
1565 :
1566 : NS_IMETHODIMP
1567 0 : HTMLEditor::SelectTableRow()
1568 : {
1569 0 : nsCOMPtr<nsIDOMElement> cell;
1570 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
1571 0 : getter_AddRefs(cell));
1572 0 : NS_ENSURE_SUCCESS(rv, rv);
1573 :
1574 : // Don't fail if we didn't find a cell
1575 0 : NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
1576 0 : nsCOMPtr<nsIDOMElement> startCell = cell;
1577 :
1578 : // Get table and location of cell:
1579 0 : RefPtr<Selection> selection;
1580 0 : nsCOMPtr<nsIDOMElement> table;
1581 : int32_t startRowIndex, startColIndex;
1582 :
1583 0 : rv = GetCellContext(getter_AddRefs(selection),
1584 0 : getter_AddRefs(table),
1585 0 : getter_AddRefs(cell),
1586 : nullptr, nullptr,
1587 0 : &startRowIndex, &startColIndex);
1588 0 : NS_ENSURE_SUCCESS(rv, rv);
1589 0 : NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
1590 :
1591 : int32_t rowCount, colCount;
1592 0 : rv = GetTableSize(table, &rowCount, &colCount);
1593 0 : NS_ENSURE_SUCCESS(rv, rv);
1594 :
1595 : //Note: At this point, we could get first and last cells in row,
1596 : // then call SelectBlockOfCells, but that would take just
1597 : // a little less code, so the following is more efficient
1598 :
1599 : // Suppress nsISelectionListener notification
1600 : // until all selection changes are finished
1601 0 : SelectionBatcher selectionBatcher(selection);
1602 :
1603 : // It is now safe to clear the selection
1604 : // BE SURE TO RESET IT BEFORE LEAVING!
1605 0 : rv = ClearSelection();
1606 :
1607 : // Select all cells in the same row as current cell
1608 0 : bool cellSelected = false;
1609 : int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
1610 : bool isSelected;
1611 0 : for (int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) {
1612 0 : rv = GetCellDataAt(table, startRowIndex, col, getter_AddRefs(cell),
1613 : ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan,
1614 0 : &actualRowSpan, &actualColSpan, &isSelected);
1615 0 : if (NS_FAILED(rv)) {
1616 0 : break;
1617 : }
1618 : // Skip cells that are spanned from previous rows or columns
1619 0 : if (cell && currentRowIndex == startRowIndex && currentColIndex == col) {
1620 0 : rv = AppendNodeToSelectionAsRange(cell);
1621 0 : if (NS_FAILED(rv)) {
1622 0 : break;
1623 : }
1624 0 : cellSelected = true;
1625 : }
1626 : }
1627 : // Safety code to select starting cell if nothing else was selected
1628 0 : if (!cellSelected) {
1629 0 : return AppendNodeToSelectionAsRange(startCell);
1630 : }
1631 : // NS_OK, otherwise, the error of ClearSelection() when there is no column or
1632 : // the last failure of GetCellDataAt() or AppendNodeToSelectionAsRange().
1633 0 : return rv;
1634 : }
1635 :
1636 : NS_IMETHODIMP
1637 0 : HTMLEditor::SelectTableColumn()
1638 : {
1639 0 : nsCOMPtr<nsIDOMElement> cell;
1640 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
1641 0 : getter_AddRefs(cell));
1642 0 : NS_ENSURE_SUCCESS(rv, rv);
1643 :
1644 : // Don't fail if we didn't find a cell
1645 0 : NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
1646 :
1647 0 : nsCOMPtr<nsIDOMElement> startCell = cell;
1648 :
1649 : // Get location of cell:
1650 0 : RefPtr<Selection> selection;
1651 0 : nsCOMPtr<nsIDOMElement> table;
1652 : int32_t startRowIndex, startColIndex;
1653 :
1654 0 : rv = GetCellContext(getter_AddRefs(selection),
1655 0 : getter_AddRefs(table),
1656 0 : getter_AddRefs(cell),
1657 : nullptr, nullptr,
1658 0 : &startRowIndex, &startColIndex);
1659 0 : NS_ENSURE_SUCCESS(rv, rv);
1660 0 : NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
1661 :
1662 : int32_t rowCount, colCount;
1663 0 : rv = GetTableSize(table, &rowCount, &colCount);
1664 0 : NS_ENSURE_SUCCESS(rv, rv);
1665 :
1666 : // Suppress nsISelectionListener notification
1667 : // until all selection changes are finished
1668 0 : SelectionBatcher selectionBatcher(selection);
1669 :
1670 : // It is now safe to clear the selection
1671 : // BE SURE TO RESET IT BEFORE LEAVING!
1672 0 : rv = ClearSelection();
1673 :
1674 : // Select all cells in the same column as current cell
1675 0 : bool cellSelected = false;
1676 : int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
1677 : bool isSelected;
1678 0 : for (int32_t row = 0; row < rowCount; row += std::max(actualRowSpan, 1)) {
1679 0 : rv = GetCellDataAt(table, row, startColIndex, getter_AddRefs(cell),
1680 : ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan,
1681 0 : &actualRowSpan, &actualColSpan, &isSelected);
1682 0 : if (NS_FAILED(rv)) {
1683 0 : break;
1684 : }
1685 : // Skip cells that are spanned from previous rows or columns
1686 0 : if (cell && currentRowIndex == row && currentColIndex == startColIndex) {
1687 0 : rv = AppendNodeToSelectionAsRange(cell);
1688 0 : if (NS_FAILED(rv)) {
1689 0 : break;
1690 : }
1691 0 : cellSelected = true;
1692 : }
1693 : }
1694 : // Safety code to select starting cell if nothing else was selected
1695 0 : if (!cellSelected) {
1696 0 : return AppendNodeToSelectionAsRange(startCell);
1697 : }
1698 : // NS_OK, otherwise, the error of ClearSelection() when there is no row or
1699 : // the last failure of GetCellDataAt() or AppendNodeToSelectionAsRange().
1700 0 : return rv;
1701 : }
1702 :
1703 : NS_IMETHODIMP
1704 0 : HTMLEditor::SplitTableCell()
1705 : {
1706 0 : nsCOMPtr<nsIDOMElement> table;
1707 0 : nsCOMPtr<nsIDOMElement> cell;
1708 : int32_t startRowIndex, startColIndex, actualRowSpan, actualColSpan;
1709 : // Get cell, table, etc. at selection anchor node
1710 0 : nsresult rv = GetCellContext(nullptr,
1711 0 : getter_AddRefs(table),
1712 0 : getter_AddRefs(cell),
1713 : nullptr, nullptr,
1714 0 : &startRowIndex, &startColIndex);
1715 0 : NS_ENSURE_SUCCESS(rv, rv);
1716 0 : if (!table || !cell) {
1717 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
1718 : }
1719 :
1720 : // We need rowspan and colspan data
1721 0 : rv = GetCellSpansAt(table, startRowIndex, startColIndex,
1722 0 : actualRowSpan, actualColSpan);
1723 0 : NS_ENSURE_SUCCESS(rv, rv);
1724 :
1725 : // Must have some span to split
1726 0 : if (actualRowSpan <= 1 && actualColSpan <= 1) {
1727 0 : return NS_OK;
1728 : }
1729 :
1730 0 : AutoEditBatch beginBatching(this);
1731 : // Prevent auto insertion of BR in new cell until we're done
1732 0 : AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
1733 :
1734 : // We reset selection
1735 : AutoSelectionSetterAfterTableEdit setCaret(this, table, startRowIndex,
1736 : startColIndex, ePreviousColumn,
1737 0 : false);
1738 : //...so suppress Rules System selection munging
1739 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
1740 :
1741 0 : nsCOMPtr<nsIDOMElement> newCell;
1742 0 : int32_t rowIndex = startRowIndex;
1743 : int32_t rowSpanBelow, colSpanAfter;
1744 :
1745 : // Split up cell row-wise first into rowspan=1 above, and the rest below,
1746 : // whittling away at the cell below until no more extra span
1747 0 : for (rowSpanBelow = actualRowSpan-1; rowSpanBelow >= 0; rowSpanBelow--) {
1748 : // We really split row-wise only if we had rowspan > 1
1749 0 : if (rowSpanBelow > 0) {
1750 0 : rv = SplitCellIntoRows(table, rowIndex, startColIndex, 1, rowSpanBelow,
1751 0 : getter_AddRefs(newCell));
1752 0 : NS_ENSURE_SUCCESS(rv, rv);
1753 0 : CopyCellBackgroundColor(newCell, cell);
1754 : }
1755 0 : int32_t colIndex = startColIndex;
1756 : // Now split the cell with rowspan = 1 into cells if it has colSpan > 1
1757 0 : for (colSpanAfter = actualColSpan-1; colSpanAfter > 0; colSpanAfter--) {
1758 0 : rv = SplitCellIntoColumns(table, rowIndex, colIndex, 1, colSpanAfter,
1759 0 : getter_AddRefs(newCell));
1760 0 : NS_ENSURE_SUCCESS(rv, rv);
1761 0 : CopyCellBackgroundColor(newCell, cell);
1762 0 : colIndex++;
1763 : }
1764 : // Point to the new cell and repeat
1765 0 : rowIndex++;
1766 : }
1767 0 : return NS_OK;
1768 : }
1769 :
1770 : nsresult
1771 0 : HTMLEditor::CopyCellBackgroundColor(nsIDOMElement* destCell,
1772 : nsIDOMElement* sourceCell)
1773 : {
1774 0 : NS_ENSURE_TRUE(destCell && sourceCell, NS_ERROR_NULL_POINTER);
1775 :
1776 : // Copy backgournd color to new cell
1777 0 : NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor");
1778 0 : nsAutoString color;
1779 : bool isSet;
1780 0 : nsresult rv = GetAttributeValue(sourceCell, bgcolor, color, &isSet);
1781 0 : if (NS_FAILED(rv)) {
1782 0 : return rv;
1783 : }
1784 0 : if (!isSet) {
1785 0 : return NS_OK;
1786 : }
1787 0 : return SetAttribute(destCell, bgcolor, color);
1788 : }
1789 :
1790 : NS_IMETHODIMP
1791 0 : HTMLEditor::SplitCellIntoColumns(nsIDOMElement* aTable,
1792 : int32_t aRowIndex,
1793 : int32_t aColIndex,
1794 : int32_t aColSpanLeft,
1795 : int32_t aColSpanRight,
1796 : nsIDOMElement** aNewCell)
1797 : {
1798 0 : NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
1799 0 : if (aNewCell) {
1800 0 : *aNewCell = nullptr;
1801 : }
1802 :
1803 0 : nsCOMPtr<nsIDOMElement> cell;
1804 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
1805 : bool isSelected;
1806 : nsresult rv =
1807 0 : GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell),
1808 : &startRowIndex, &startColIndex,
1809 : &rowSpan, &colSpan,
1810 0 : &actualRowSpan, &actualColSpan, &isSelected);
1811 0 : NS_ENSURE_SUCCESS(rv, rv);
1812 0 : NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER);
1813 :
1814 : // We can't split!
1815 0 : if (actualColSpan <= 1 || (aColSpanLeft + aColSpanRight) > actualColSpan) {
1816 0 : return NS_OK;
1817 : }
1818 :
1819 : // Reduce colspan of cell to split
1820 0 : rv = SetColSpan(cell, aColSpanLeft);
1821 0 : NS_ENSURE_SUCCESS(rv, rv);
1822 :
1823 : // Insert new cell after using the remaining span
1824 : // and always get the new cell so we can copy the background color;
1825 0 : nsCOMPtr<nsIDOMElement> newCell;
1826 0 : rv = InsertCell(cell, actualRowSpan, aColSpanRight, true, false,
1827 0 : getter_AddRefs(newCell));
1828 0 : NS_ENSURE_SUCCESS(rv, rv);
1829 0 : if (!newCell) {
1830 0 : return NS_OK;
1831 : }
1832 0 : if (aNewCell) {
1833 0 : NS_ADDREF(*aNewCell = newCell.get());
1834 : }
1835 0 : return CopyCellBackgroundColor(newCell, cell);
1836 : }
1837 :
1838 : NS_IMETHODIMP
1839 0 : HTMLEditor::SplitCellIntoRows(nsIDOMElement* aTable,
1840 : int32_t aRowIndex,
1841 : int32_t aColIndex,
1842 : int32_t aRowSpanAbove,
1843 : int32_t aRowSpanBelow,
1844 : nsIDOMElement** aNewCell)
1845 : {
1846 0 : NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
1847 0 : if (aNewCell) *aNewCell = nullptr;
1848 :
1849 0 : nsCOMPtr<nsIDOMElement> cell;
1850 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
1851 : bool isSelected;
1852 : nsresult rv =
1853 0 : GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell),
1854 : &startRowIndex, &startColIndex,
1855 : &rowSpan, &colSpan,
1856 0 : &actualRowSpan, &actualColSpan, &isSelected);
1857 0 : NS_ENSURE_SUCCESS(rv, rv);
1858 0 : NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER);
1859 :
1860 : // We can't split!
1861 0 : if (actualRowSpan <= 1 || (aRowSpanAbove + aRowSpanBelow) > actualRowSpan) {
1862 0 : return NS_OK;
1863 : }
1864 :
1865 : int32_t rowCount, colCount;
1866 0 : rv = GetTableSize(aTable, &rowCount, &colCount);
1867 0 : NS_ENSURE_SUCCESS(rv, rv);
1868 :
1869 0 : nsCOMPtr<nsIDOMElement> cell2;
1870 0 : nsCOMPtr<nsIDOMElement> lastCellFound;
1871 : int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2;
1872 : bool isSelected2;
1873 0 : int32_t colIndex = 0;
1874 0 : bool insertAfter = (startColIndex > 0);
1875 : // This is the row we will insert new cell into
1876 0 : int32_t rowBelowIndex = startRowIndex+aRowSpanAbove;
1877 :
1878 : // Find a cell to insert before or after
1879 : for (;;) {
1880 : // Search for a cell to insert before
1881 0 : rv = GetCellDataAt(aTable, rowBelowIndex,
1882 0 : colIndex, getter_AddRefs(cell2),
1883 : &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2,
1884 0 : &actualRowSpan2, &actualColSpan2, &isSelected2);
1885 : // If we fail here, it could be because row has bad rowspan values,
1886 : // such as all cells having rowspan > 1 (Call FixRowSpan first!)
1887 0 : if (NS_FAILED(rv) || !cell) {
1888 0 : return NS_ERROR_FAILURE;
1889 : }
1890 :
1891 : // Skip over cells spanned from above (like the one we are splitting!)
1892 0 : if (cell2 && startRowIndex2 == rowBelowIndex) {
1893 0 : if (!insertAfter) {
1894 : // Inserting before, so stop at first cell in row we want to insert
1895 : // into.
1896 0 : break;
1897 : }
1898 : // New cell isn't first in row,
1899 : // so stop after we find the cell just before new cell's column
1900 0 : if (startColIndex2 + actualColSpan2 == startColIndex) {
1901 0 : break;
1902 : }
1903 : // If cell found is AFTER desired new cell colum,
1904 : // we have multiple cells with rowspan > 1 that
1905 : // prevented us from finding a cell to insert after...
1906 0 : if (startColIndex2 > startColIndex) {
1907 : // ... so instead insert before the cell we found
1908 0 : insertAfter = false;
1909 0 : break;
1910 : }
1911 0 : lastCellFound = cell2;
1912 : }
1913 : // Skip to next available cellmap location
1914 0 : colIndex += std::max(actualColSpan2, 1);
1915 :
1916 : // Done when past end of total number of columns
1917 0 : if (colIndex > colCount) {
1918 0 : break;
1919 : }
1920 : }
1921 :
1922 0 : if (!cell2 && lastCellFound) {
1923 : // Edge case where we didn't find a cell to insert after
1924 : // or before because column(s) before desired column
1925 : // and all columns after it are spanned from above.
1926 : // We can insert after the last cell we found
1927 0 : cell2 = lastCellFound;
1928 0 : insertAfter = true; // Should always be true, but let's be sure
1929 : }
1930 :
1931 : // Reduce rowspan of cell to split
1932 0 : rv = SetRowSpan(cell, aRowSpanAbove);
1933 0 : NS_ENSURE_SUCCESS(rv, rv);
1934 :
1935 :
1936 : // Insert new cell after using the remaining span
1937 : // and always get the new cell so we can copy the background color;
1938 0 : nsCOMPtr<nsIDOMElement> newCell;
1939 0 : rv = InsertCell(cell2, aRowSpanBelow, actualColSpan, insertAfter, false,
1940 0 : getter_AddRefs(newCell));
1941 0 : NS_ENSURE_SUCCESS(rv, rv);
1942 0 : if (!newCell) {
1943 0 : return NS_OK;
1944 : }
1945 0 : if (aNewCell) {
1946 0 : NS_ADDREF(*aNewCell = newCell.get());
1947 : }
1948 0 : return CopyCellBackgroundColor(newCell, cell2);
1949 : }
1950 :
1951 : NS_IMETHODIMP
1952 0 : HTMLEditor::SwitchTableCellHeaderType(nsIDOMElement* aSourceCell,
1953 : nsIDOMElement** aNewCell)
1954 : {
1955 0 : nsCOMPtr<Element> sourceCell = do_QueryInterface(aSourceCell);
1956 0 : NS_ENSURE_TRUE(sourceCell, NS_ERROR_NULL_POINTER);
1957 :
1958 0 : AutoEditBatch beginBatching(this);
1959 : // Prevent auto insertion of BR in new cell created by ReplaceContainer
1960 0 : AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
1961 :
1962 : // Save current selection to restore when done
1963 : // This is needed so ReplaceContainer can monitor selection
1964 : // when replacing nodes
1965 0 : RefPtr<Selection> selection = GetSelection();
1966 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1967 0 : AutoSelectionRestorer selectionRestorer(selection, this);
1968 :
1969 : // Set to the opposite of current type
1970 0 : nsCOMPtr<nsIAtom> atom = EditorBase::GetTag(aSourceCell);
1971 0 : nsIAtom* newCellType = atom == nsGkAtoms::td ? nsGkAtoms::th : nsGkAtoms::td;
1972 :
1973 : // This creates new node, moves children, copies attributes (true)
1974 : // and manages the selection!
1975 0 : nsCOMPtr<Element> newNode = ReplaceContainer(sourceCell, newCellType,
1976 0 : nullptr, nullptr, EditorBase::eCloneAttributes);
1977 0 : NS_ENSURE_TRUE(newNode, NS_ERROR_FAILURE);
1978 :
1979 : // Return the new cell
1980 0 : if (aNewCell) {
1981 0 : nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode);
1982 0 : *aNewCell = newElement.get();
1983 0 : NS_ADDREF(*aNewCell);
1984 : }
1985 :
1986 0 : return NS_OK;
1987 : }
1988 :
1989 : NS_IMETHODIMP
1990 0 : HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents)
1991 : {
1992 0 : nsCOMPtr<nsIDOMElement> table;
1993 0 : nsCOMPtr<nsIDOMElement> targetCell;
1994 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
1995 : bool isSelected;
1996 0 : nsCOMPtr<nsIDOMElement> cell2;
1997 : int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2;
1998 : bool isSelected2;
1999 :
2000 : // Get cell, table, etc. at selection anchor node
2001 0 : nsresult rv = GetCellContext(nullptr,
2002 0 : getter_AddRefs(table),
2003 0 : getter_AddRefs(targetCell),
2004 : nullptr, nullptr,
2005 0 : &startRowIndex, &startColIndex);
2006 0 : NS_ENSURE_SUCCESS(rv, rv);
2007 0 : if (!table || !targetCell) {
2008 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
2009 : }
2010 :
2011 0 : AutoEditBatch beginBatching(this);
2012 : //Don't let Rules System change the selection
2013 0 : AutoTransactionsConserveSelection dontChangeSelection(this);
2014 :
2015 : // Note: We dont' use AutoSelectionSetterAfterTableEdit here so the selection
2016 : // is retained after joining. This leaves the target cell selected
2017 : // as well as the "non-contiguous" cells, so user can see what happened.
2018 :
2019 0 : nsCOMPtr<nsIDOMElement> firstCell;
2020 : int32_t firstRowIndex, firstColIndex;
2021 0 : rv = GetFirstSelectedCellInTable(&firstRowIndex, &firstColIndex,
2022 0 : getter_AddRefs(firstCell));
2023 0 : NS_ENSURE_SUCCESS(rv, rv);
2024 :
2025 0 : bool joinSelectedCells = false;
2026 0 : if (firstCell) {
2027 0 : nsCOMPtr<nsIDOMElement> secondCell;
2028 0 : rv = GetNextSelectedCell(nullptr, getter_AddRefs(secondCell));
2029 0 : NS_ENSURE_SUCCESS(rv, rv);
2030 :
2031 : // If only one cell is selected, join with cell to the right
2032 0 : joinSelectedCells = (secondCell != nullptr);
2033 : }
2034 :
2035 0 : if (joinSelectedCells) {
2036 : // We have selected cells: Join just contiguous cells
2037 : // and just merge contents if not contiguous
2038 :
2039 : int32_t rowCount, colCount;
2040 0 : rv = GetTableSize(table, &rowCount, &colCount);
2041 0 : NS_ENSURE_SUCCESS(rv, rv);
2042 :
2043 : // Get spans for cell we will merge into
2044 : int32_t firstRowSpan, firstColSpan;
2045 0 : rv = GetCellSpansAt(table, firstRowIndex, firstColIndex,
2046 0 : firstRowSpan, firstColSpan);
2047 0 : NS_ENSURE_SUCCESS(rv, rv);
2048 :
2049 : // This defines the last indexes along the "edges"
2050 : // of the contiguous block of cells, telling us
2051 : // that we can join adjacent cells to the block
2052 : // Start with same as the first values,
2053 : // then expand as we find adjacent selected cells
2054 0 : int32_t lastRowIndex = firstRowIndex;
2055 0 : int32_t lastColIndex = firstColIndex;
2056 : int32_t rowIndex, colIndex;
2057 :
2058 : // First pass: Determine boundaries of contiguous rectangular block
2059 : // that we will join into one cell,
2060 : // favoring adjacent cells in the same row
2061 0 : for (rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) {
2062 0 : int32_t currentRowCount = rowCount;
2063 : // Be sure each row doesn't have rowspan errors
2064 0 : rv = FixBadRowSpan(table, rowIndex, rowCount);
2065 0 : NS_ENSURE_SUCCESS(rv, rv);
2066 : // Adjust rowcount by number of rows we removed
2067 0 : lastRowIndex -= (currentRowCount-rowCount);
2068 :
2069 0 : bool cellFoundInRow = false;
2070 0 : bool lastRowIsSet = false;
2071 0 : int32_t lastColInRow = 0;
2072 0 : int32_t firstColInRow = firstColIndex;
2073 0 : for (colIndex = firstColIndex; colIndex < colCount;
2074 0 : colIndex += std::max(actualColSpan2, 1)) {
2075 0 : rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2),
2076 : &startRowIndex2, &startColIndex2,
2077 : &rowSpan2, &colSpan2,
2078 0 : &actualRowSpan2, &actualColSpan2, &isSelected2);
2079 0 : NS_ENSURE_SUCCESS(rv, rv);
2080 :
2081 0 : if (isSelected2) {
2082 0 : if (!cellFoundInRow) {
2083 : // We've just found the first selected cell in this row
2084 0 : firstColInRow = colIndex;
2085 : }
2086 0 : if (rowIndex > firstRowIndex && firstColInRow != firstColIndex) {
2087 : // We're in at least the second row,
2088 : // but left boundary is "ragged" (not the same as 1st row's start)
2089 : //Let's just end block on previous row
2090 : // and keep previous lastColIndex
2091 : //TODO: We could try to find the Maximum firstColInRow
2092 : // so our block can still extend down more rows?
2093 0 : lastRowIndex = std::max(0,rowIndex - 1);
2094 0 : lastRowIsSet = true;
2095 0 : break;
2096 : }
2097 : // Save max selected column in this row, including extra colspan
2098 0 : lastColInRow = colIndex + (actualColSpan2-1);
2099 0 : cellFoundInRow = true;
2100 0 : } else if (cellFoundInRow) {
2101 : // No cell or not selected, but at least one cell in row was found
2102 0 : if (rowIndex > (firstRowIndex + 1) && colIndex <= lastColIndex) {
2103 : // Cell is in a column less than current right border in
2104 : // the third or higher selected row, so stop block at the previous row
2105 0 : lastRowIndex = std::max(0,rowIndex - 1);
2106 0 : lastRowIsSet = true;
2107 : }
2108 : // We're done with this row
2109 0 : break;
2110 : }
2111 : } // End of column loop
2112 :
2113 : // Done with this row
2114 0 : if (cellFoundInRow) {
2115 0 : if (rowIndex == firstRowIndex) {
2116 : // First row always initializes the right boundary
2117 0 : lastColIndex = lastColInRow;
2118 : }
2119 :
2120 : // If we didn't determine last row above...
2121 0 : if (!lastRowIsSet) {
2122 0 : if (colIndex < lastColIndex) {
2123 : // (don't think we ever get here?)
2124 : // Cell is in a column less than current right boundary,
2125 : // so stop block at the previous row
2126 0 : lastRowIndex = std::max(0,rowIndex - 1);
2127 : } else {
2128 : // Go on to examine next row
2129 0 : lastRowIndex = rowIndex+1;
2130 : }
2131 : }
2132 : // Use the minimum col we found so far for right boundary
2133 0 : lastColIndex = std::min(lastColIndex, lastColInRow);
2134 : } else {
2135 : // No selected cells in this row -- stop at row above
2136 : // and leave last column at its previous value
2137 0 : lastRowIndex = std::max(0,rowIndex - 1);
2138 : }
2139 : }
2140 :
2141 : // The list of cells we will delete after joining
2142 0 : nsTArray<nsCOMPtr<nsIDOMElement> > deleteList;
2143 :
2144 : // 2nd pass: Do the joining and merging
2145 0 : for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
2146 0 : for (colIndex = 0; colIndex < colCount;
2147 0 : colIndex += std::max(actualColSpan2, 1)) {
2148 0 : rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2),
2149 : &startRowIndex2, &startColIndex2,
2150 : &rowSpan2, &colSpan2,
2151 0 : &actualRowSpan2, &actualColSpan2, &isSelected2);
2152 0 : NS_ENSURE_SUCCESS(rv, rv);
2153 :
2154 : // If this is 0, we are past last cell in row, so exit the loop
2155 0 : if (!actualColSpan2) {
2156 0 : break;
2157 : }
2158 :
2159 : // Merge only selected cells (skip cell we're merging into, of course)
2160 0 : if (isSelected2 && cell2 != firstCell) {
2161 0 : if (rowIndex >= firstRowIndex && rowIndex <= lastRowIndex &&
2162 0 : colIndex >= firstColIndex && colIndex <= lastColIndex) {
2163 : // We are within the join region
2164 : // Problem: It is very tricky to delete cells as we merge,
2165 : // since that will upset the cellmap
2166 : // Instead, build a list of cells to delete and do it later
2167 0 : NS_ASSERTION(startRowIndex2 == rowIndex, "JoinTableCells: StartRowIndex is in row above");
2168 :
2169 0 : if (actualColSpan2 > 1) {
2170 : //Check if cell "hangs" off the boundary because of colspan > 1
2171 : // Use split methods to chop off excess
2172 0 : int32_t extraColSpan = (startColIndex2 + actualColSpan2) - (lastColIndex+1);
2173 0 : if ( extraColSpan > 0) {
2174 0 : rv = SplitCellIntoColumns(table, startRowIndex2, startColIndex2,
2175 : actualColSpan2 - extraColSpan,
2176 0 : extraColSpan, nullptr);
2177 0 : NS_ENSURE_SUCCESS(rv, rv);
2178 : }
2179 : }
2180 :
2181 0 : rv = MergeCells(firstCell, cell2, false);
2182 0 : NS_ENSURE_SUCCESS(rv, rv);
2183 :
2184 : // Add cell to list to delete
2185 0 : deleteList.AppendElement(cell2.get());
2186 0 : } else if (aMergeNonContiguousContents) {
2187 : // Cell is outside join region -- just merge the contents
2188 0 : rv = MergeCells(firstCell, cell2, false);
2189 0 : NS_ENSURE_SUCCESS(rv, rv);
2190 : }
2191 : }
2192 : }
2193 : }
2194 :
2195 : // All cell contents are merged. Delete the empty cells we accumulated
2196 : // Prevent rules testing until we're done
2197 : AutoRules beginRulesSniffing(this, EditAction::deleteNode,
2198 0 : nsIEditor::eNext);
2199 :
2200 0 : for (uint32_t i = 0, n = deleteList.Length(); i < n; i++) {
2201 0 : nsIDOMElement *elementPtr = deleteList[i];
2202 0 : if (elementPtr) {
2203 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(elementPtr);
2204 0 : rv = DeleteNode(node);
2205 0 : NS_ENSURE_SUCCESS(rv, rv);
2206 : }
2207 : }
2208 : // Cleanup selection: remove ranges where cells were deleted
2209 0 : RefPtr<Selection> selection = GetSelection();
2210 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2211 :
2212 : int32_t rangeCount;
2213 0 : rv = selection->GetRangeCount(&rangeCount);
2214 0 : NS_ENSURE_SUCCESS(rv, rv);
2215 :
2216 0 : RefPtr<nsRange> range;
2217 0 : for (int32_t i = 0; i < rangeCount; i++) {
2218 0 : range = selection->GetRangeAt(i);
2219 0 : NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
2220 :
2221 0 : nsCOMPtr<nsIDOMElement> deletedCell;
2222 0 : GetCellFromRange(range, getter_AddRefs(deletedCell));
2223 0 : if (!deletedCell) {
2224 0 : selection->RemoveRange(range);
2225 0 : rangeCount--;
2226 0 : i--;
2227 : }
2228 : }
2229 :
2230 : // Set spans for the cell everthing merged into
2231 0 : rv = SetRowSpan(firstCell, lastRowIndex-firstRowIndex+1);
2232 0 : NS_ENSURE_SUCCESS(rv, rv);
2233 0 : rv = SetColSpan(firstCell, lastColIndex-firstColIndex+1);
2234 0 : NS_ENSURE_SUCCESS(rv, rv);
2235 :
2236 :
2237 : // Fixup disturbances in table layout
2238 0 : NormalizeTable(table);
2239 : } else {
2240 : // Joining with cell to the right -- get rowspan and colspan data of target cell
2241 0 : rv = GetCellDataAt(table, startRowIndex, startColIndex,
2242 0 : getter_AddRefs(targetCell),
2243 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
2244 0 : &actualRowSpan, &actualColSpan, &isSelected);
2245 0 : NS_ENSURE_SUCCESS(rv, rv);
2246 0 : NS_ENSURE_TRUE(targetCell, NS_ERROR_NULL_POINTER);
2247 :
2248 : // Get data for cell to the right
2249 0 : rv = GetCellDataAt(table, startRowIndex, startColIndex + actualColSpan,
2250 0 : getter_AddRefs(cell2),
2251 : &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2,
2252 0 : &actualRowSpan2, &actualColSpan2, &isSelected2);
2253 0 : NS_ENSURE_SUCCESS(rv, rv);
2254 0 : if (!cell2) {
2255 0 : return NS_OK; // Don't fail if there's no cell
2256 : }
2257 :
2258 : // sanity check
2259 0 : NS_ASSERTION((startRowIndex >= startRowIndex2),"JoinCells: startRowIndex < startRowIndex2");
2260 :
2261 : // Figure out span of merged cell starting from target's starting row
2262 : // to handle case of merged cell starting in a row above
2263 0 : int32_t spanAboveMergedCell = startRowIndex - startRowIndex2;
2264 0 : int32_t effectiveRowSpan2 = actualRowSpan2 - spanAboveMergedCell;
2265 :
2266 0 : if (effectiveRowSpan2 > actualRowSpan) {
2267 : // Cell to the right spans into row below target
2268 : // Split off portion below target cell's bottom-most row
2269 0 : rv = SplitCellIntoRows(table, startRowIndex2, startColIndex2,
2270 : spanAboveMergedCell+actualRowSpan,
2271 0 : effectiveRowSpan2-actualRowSpan, nullptr);
2272 0 : NS_ENSURE_SUCCESS(rv, rv);
2273 : }
2274 :
2275 : // Move contents from cell to the right
2276 : // Delete the cell now only if it starts in the same row
2277 : // and has enough row "height"
2278 0 : rv = MergeCells(targetCell, cell2,
2279 0 : (startRowIndex2 == startRowIndex) &&
2280 0 : (effectiveRowSpan2 >= actualRowSpan));
2281 0 : NS_ENSURE_SUCCESS(rv, rv);
2282 :
2283 0 : if (effectiveRowSpan2 < actualRowSpan) {
2284 : // Merged cell is "shorter"
2285 : // (there are cells(s) below it that are row-spanned by target cell)
2286 : // We could try splitting those cells, but that's REAL messy,
2287 : // so the safest thing to do is NOT really join the cells
2288 0 : return NS_OK;
2289 : }
2290 :
2291 0 : if (spanAboveMergedCell > 0) {
2292 : // Cell we merged started in a row above the target cell
2293 : // Reduce rowspan to give room where target cell will extend its colspan
2294 0 : rv = SetRowSpan(cell2, spanAboveMergedCell);
2295 0 : NS_ENSURE_SUCCESS(rv, rv);
2296 : }
2297 :
2298 : // Reset target cell's colspan to encompass cell to the right
2299 0 : rv = SetColSpan(targetCell, actualColSpan+actualColSpan2);
2300 0 : NS_ENSURE_SUCCESS(rv, rv);
2301 : }
2302 0 : return NS_OK;
2303 : }
2304 :
2305 : NS_IMETHODIMP
2306 0 : HTMLEditor::MergeCells(nsCOMPtr<nsIDOMElement> aTargetCell,
2307 : nsCOMPtr<nsIDOMElement> aCellToMerge,
2308 : bool aDeleteCellToMerge)
2309 : {
2310 0 : nsCOMPtr<dom::Element> targetCell = do_QueryInterface(aTargetCell);
2311 0 : nsCOMPtr<dom::Element> cellToMerge = do_QueryInterface(aCellToMerge);
2312 0 : NS_ENSURE_TRUE(targetCell && cellToMerge, NS_ERROR_NULL_POINTER);
2313 :
2314 : // Prevent rules testing until we're done
2315 0 : AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
2316 :
2317 : // Don't need to merge if cell is empty
2318 0 : if (!IsEmptyCell(cellToMerge)) {
2319 : // Get index of last child in target cell
2320 : // If we fail or don't have children,
2321 : // we insert at index 0
2322 0 : int32_t insertIndex = 0;
2323 :
2324 : // Start inserting just after last child
2325 0 : uint32_t len = targetCell->GetChildCount();
2326 0 : if (len == 1 && IsEmptyCell(targetCell)) {
2327 : // Delete the empty node
2328 0 : nsIContent* cellChild = targetCell->GetFirstChild();
2329 0 : nsresult rv = DeleteNode(cellChild->AsDOMNode());
2330 0 : NS_ENSURE_SUCCESS(rv, rv);
2331 0 : insertIndex = 0;
2332 : } else {
2333 0 : insertIndex = (int32_t)len;
2334 : }
2335 :
2336 : // Move the contents
2337 0 : while (cellToMerge->HasChildren()) {
2338 0 : nsCOMPtr<nsIDOMNode> cellChild = cellToMerge->GetLastChild()->AsDOMNode();
2339 0 : nsresult rv = DeleteNode(cellChild);
2340 0 : NS_ENSURE_SUCCESS(rv, rv);
2341 :
2342 0 : rv = InsertNode(cellChild, aTargetCell, insertIndex);
2343 0 : NS_ENSURE_SUCCESS(rv, rv);
2344 : }
2345 : }
2346 :
2347 : // Delete cells whose contents were moved
2348 0 : if (aDeleteCellToMerge) {
2349 0 : return DeleteNode(aCellToMerge);
2350 : }
2351 :
2352 0 : return NS_OK;
2353 : }
2354 :
2355 :
2356 : NS_IMETHODIMP
2357 0 : HTMLEditor::FixBadRowSpan(nsIDOMElement* aTable,
2358 : int32_t aRowIndex,
2359 : int32_t& aNewRowCount)
2360 : {
2361 0 : NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
2362 :
2363 : int32_t rowCount, colCount;
2364 0 : nsresult rv = GetTableSize(aTable, &rowCount, &colCount);
2365 0 : NS_ENSURE_SUCCESS(rv, rv);
2366 :
2367 0 : nsCOMPtr<nsIDOMElement>cell;
2368 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
2369 : bool isSelected;
2370 :
2371 0 : int32_t minRowSpan = -1;
2372 : int32_t colIndex;
2373 :
2374 0 : for (colIndex = 0; colIndex < colCount;
2375 0 : colIndex += std::max(actualColSpan, 1)) {
2376 0 : rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
2377 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
2378 0 : &actualRowSpan, &actualColSpan, &isSelected);
2379 : // NOTE: This is a *real* failure.
2380 : // GetCellDataAt passes if cell is missing from cellmap
2381 0 : if (NS_FAILED(rv)) {
2382 0 : return rv;
2383 : }
2384 0 : if (!cell) {
2385 0 : break;
2386 : }
2387 0 : if (rowSpan > 0 &&
2388 0 : startRowIndex == aRowIndex &&
2389 0 : (rowSpan < minRowSpan || minRowSpan == -1)) {
2390 0 : minRowSpan = rowSpan;
2391 : }
2392 0 : NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan");
2393 : }
2394 0 : if (minRowSpan > 1) {
2395 : // The amount to reduce everyone's rowspan
2396 : // so at least one cell has rowspan = 1
2397 0 : int32_t rowsReduced = minRowSpan - 1;
2398 0 : for (colIndex = 0; colIndex < colCount;
2399 0 : colIndex += std::max(actualColSpan, 1)) {
2400 0 : rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
2401 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
2402 0 : &actualRowSpan, &actualColSpan, &isSelected);
2403 0 : if (NS_FAILED(rv)) {
2404 0 : return rv;
2405 : }
2406 : // Fixup rowspans only for cells starting in current row
2407 0 : if (cell && rowSpan > 0 &&
2408 0 : startRowIndex == aRowIndex &&
2409 0 : startColIndex == colIndex ) {
2410 0 : rv = SetRowSpan(cell, rowSpan-rowsReduced);
2411 0 : if (NS_FAILED(rv)) {
2412 0 : return rv;
2413 : }
2414 : }
2415 0 : NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan");
2416 : }
2417 : }
2418 0 : return GetTableSize(aTable, &aNewRowCount, &colCount);
2419 : }
2420 :
2421 : NS_IMETHODIMP
2422 0 : HTMLEditor::FixBadColSpan(nsIDOMElement* aTable,
2423 : int32_t aColIndex,
2424 : int32_t& aNewColCount)
2425 : {
2426 0 : NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
2427 :
2428 : int32_t rowCount, colCount;
2429 0 : nsresult rv = GetTableSize(aTable, &rowCount, &colCount);
2430 0 : NS_ENSURE_SUCCESS(rv, rv);
2431 :
2432 0 : nsCOMPtr<nsIDOMElement> cell;
2433 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
2434 : bool isSelected;
2435 :
2436 0 : int32_t minColSpan = -1;
2437 : int32_t rowIndex;
2438 :
2439 0 : for (rowIndex = 0; rowIndex < rowCount;
2440 0 : rowIndex += std::max(actualRowSpan, 1)) {
2441 0 : rv = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
2442 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
2443 0 : &actualRowSpan, &actualColSpan, &isSelected);
2444 : // NOTE: This is a *real* failure.
2445 : // GetCellDataAt passes if cell is missing from cellmap
2446 0 : if (NS_FAILED(rv)) {
2447 0 : return rv;
2448 : }
2449 0 : if (!cell) {
2450 0 : break;
2451 : }
2452 0 : if (colSpan > 0 &&
2453 0 : startColIndex == aColIndex &&
2454 0 : (colSpan < minColSpan || minColSpan == -1)) {
2455 0 : minColSpan = colSpan;
2456 : }
2457 0 : NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan");
2458 : }
2459 0 : if (minColSpan > 1) {
2460 : // The amount to reduce everyone's colspan
2461 : // so at least one cell has colspan = 1
2462 0 : int32_t colsReduced = minColSpan - 1;
2463 0 : for (rowIndex = 0; rowIndex < rowCount;
2464 0 : rowIndex += std::max(actualRowSpan, 1)) {
2465 0 : rv = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
2466 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
2467 0 : &actualRowSpan, &actualColSpan, &isSelected);
2468 0 : if (NS_FAILED(rv)) {
2469 0 : return rv;
2470 : }
2471 : // Fixup colspans only for cells starting in current column
2472 0 : if (cell && colSpan > 0 &&
2473 0 : startColIndex == aColIndex &&
2474 0 : startRowIndex == rowIndex) {
2475 0 : rv = SetColSpan(cell, colSpan-colsReduced);
2476 0 : if (NS_FAILED(rv)) {
2477 0 : return rv;
2478 : }
2479 : }
2480 0 : NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan");
2481 : }
2482 : }
2483 0 : return GetTableSize(aTable, &rowCount, &aNewColCount);
2484 : }
2485 :
2486 : NS_IMETHODIMP
2487 0 : HTMLEditor::NormalizeTable(nsIDOMElement* aTable)
2488 : {
2489 0 : RefPtr<Selection> selection = GetSelection();
2490 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2491 :
2492 0 : nsCOMPtr<nsIDOMElement> table;
2493 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"),
2494 0 : aTable, getter_AddRefs(table));
2495 0 : NS_ENSURE_SUCCESS(rv, rv);
2496 : // Don't fail if we didn't find a table
2497 0 : NS_ENSURE_TRUE(table, NS_OK);
2498 :
2499 : int32_t rowCount, colCount, rowIndex, colIndex;
2500 0 : rv = GetTableSize(table, &rowCount, &colCount);
2501 0 : NS_ENSURE_SUCCESS(rv, rv);
2502 :
2503 : // Save current selection
2504 0 : AutoSelectionRestorer selectionRestorer(selection, this);
2505 :
2506 0 : AutoEditBatch beginBatching(this);
2507 : // Prevent auto insertion of BR in new cell until we're done
2508 0 : AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
2509 :
2510 0 : nsCOMPtr<nsIDOMElement> cell;
2511 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
2512 : bool isSelected;
2513 :
2514 : // Scan all cells in each row to detect bad rowspan values
2515 0 : for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
2516 0 : rv = FixBadRowSpan(table, rowIndex, rowCount);
2517 0 : NS_ENSURE_SUCCESS(rv, rv);
2518 : }
2519 : // and same for colspans
2520 0 : for (colIndex = 0; colIndex < colCount; colIndex++) {
2521 0 : rv = FixBadColSpan(table, colIndex, colCount);
2522 0 : NS_ENSURE_SUCCESS(rv, rv);
2523 : }
2524 :
2525 : // Fill in missing cellmap locations with empty cells
2526 0 : for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
2527 0 : nsCOMPtr<nsIDOMElement> previousCellInRow;
2528 0 : for (colIndex = 0; colIndex < colCount; colIndex++) {
2529 0 : rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell),
2530 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
2531 0 : &actualRowSpan, &actualColSpan, &isSelected);
2532 : // NOTE: This is a *real* failure.
2533 : // GetCellDataAt passes if cell is missing from cellmap
2534 0 : if (NS_FAILED(rv)) {
2535 0 : return rv;
2536 : }
2537 0 : if (!cell) {
2538 : //We are missing a cell at a cellmap location
2539 : #ifdef DEBUG
2540 : printf("NormalizeTable found missing cell at row=%d, col=%d\n",
2541 0 : rowIndex, colIndex);
2542 : #endif
2543 : // Add a cell after the previous Cell in the current row
2544 0 : if (!previousCellInRow) {
2545 : // We don't have any cells in this row -- We are really messed up!
2546 : #ifdef DEBUG
2547 : printf("NormalizeTable found no cells in row=%d, col=%d\n",
2548 0 : rowIndex, colIndex);
2549 : #endif
2550 0 : return NS_ERROR_FAILURE;
2551 : }
2552 :
2553 : // Insert a new cell after (true), and return the new cell to us
2554 0 : rv = InsertCell(previousCellInRow, 1, 1, true, false,
2555 0 : getter_AddRefs(cell));
2556 0 : NS_ENSURE_SUCCESS(rv, rv);
2557 :
2558 : // Set this so we use returned new "cell" to set previousCellInRow below
2559 0 : if (cell) {
2560 0 : startRowIndex = rowIndex;
2561 : }
2562 : }
2563 : // Save the last cell found in the same row we are scanning
2564 0 : if (startRowIndex == rowIndex) {
2565 0 : previousCellInRow = cell;
2566 : }
2567 : }
2568 : }
2569 0 : return NS_OK;
2570 : }
2571 :
2572 : NS_IMETHODIMP
2573 0 : HTMLEditor::GetCellIndexes(nsIDOMElement* aCell,
2574 : int32_t* aRowIndex,
2575 : int32_t* aColIndex)
2576 : {
2577 0 : NS_ENSURE_ARG_POINTER(aRowIndex);
2578 0 : *aColIndex=0; // initialize out params
2579 0 : NS_ENSURE_ARG_POINTER(aColIndex);
2580 0 : *aRowIndex=0;
2581 0 : if (!aCell) {
2582 : // Get the selected cell or the cell enclosing the selection anchor
2583 0 : nsCOMPtr<nsIDOMElement> cell;
2584 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr,
2585 0 : getter_AddRefs(cell));
2586 0 : if (NS_FAILED(rv) || !cell) {
2587 0 : return NS_ERROR_FAILURE;
2588 : }
2589 0 : aCell = cell;
2590 : }
2591 :
2592 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
2593 0 : NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
2594 :
2595 0 : nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aCell) );
2596 0 : NS_ENSURE_TRUE(nodeAsContent, NS_ERROR_FAILURE);
2597 : // frames are not ref counted, so don't use an nsCOMPtr
2598 0 : nsIFrame *layoutObject = nodeAsContent->GetPrimaryFrame();
2599 0 : NS_ENSURE_TRUE(layoutObject, NS_ERROR_FAILURE);
2600 :
2601 0 : nsITableCellLayout *cellLayoutObject = do_QueryFrame(layoutObject);
2602 0 : NS_ENSURE_TRUE(cellLayoutObject, NS_ERROR_FAILURE);
2603 0 : return cellLayoutObject->GetCellIndexes(*aRowIndex, *aColIndex);
2604 : }
2605 :
2606 : nsTableWrapperFrame*
2607 0 : HTMLEditor::GetTableFrame(nsIDOMElement* aTable)
2608 : {
2609 0 : NS_ENSURE_TRUE(aTable, nullptr);
2610 :
2611 0 : nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aTable) );
2612 0 : NS_ENSURE_TRUE(nodeAsContent, nullptr);
2613 0 : return do_QueryFrame(nodeAsContent->GetPrimaryFrame());
2614 : }
2615 :
2616 : //Return actual number of cells (a cell with colspan > 1 counts as just 1)
2617 : int32_t
2618 0 : HTMLEditor::GetNumberOfCellsInRow(nsIDOMElement* aTable,
2619 : int32_t rowIndex)
2620 : {
2621 0 : int32_t cellCount = 0;
2622 0 : nsCOMPtr<nsIDOMElement> cell;
2623 0 : int32_t colIndex = 0;
2624 0 : do {
2625 : int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
2626 : bool isSelected;
2627 : nsresult rv =
2628 0 : GetCellDataAt(aTable, rowIndex, colIndex, getter_AddRefs(cell),
2629 : &startRowIndex, &startColIndex, &rowSpan, &colSpan,
2630 0 : &actualRowSpan, &actualColSpan, &isSelected);
2631 0 : NS_ENSURE_SUCCESS(rv, 0);
2632 0 : if (cell) {
2633 : // Only count cells that start in row we are working with
2634 0 : if (startRowIndex == rowIndex) {
2635 0 : cellCount++;
2636 : }
2637 : //Next possible location for a cell
2638 0 : colIndex += actualColSpan;
2639 : } else {
2640 0 : colIndex++;
2641 : }
2642 : } while (cell);
2643 :
2644 0 : return cellCount;
2645 : }
2646 :
2647 : NS_IMETHODIMP
2648 0 : HTMLEditor::GetTableSize(nsIDOMElement* aTable,
2649 : int32_t* aRowCount,
2650 : int32_t* aColCount)
2651 : {
2652 0 : NS_ENSURE_ARG_POINTER(aRowCount);
2653 0 : NS_ENSURE_ARG_POINTER(aColCount);
2654 0 : *aRowCount = 0;
2655 0 : *aColCount = 0;
2656 0 : nsCOMPtr<nsIDOMElement> table;
2657 : // Get the selected talbe or the table enclosing the selection anchor
2658 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable,
2659 0 : getter_AddRefs(table));
2660 0 : NS_ENSURE_SUCCESS(rv, rv);
2661 0 : NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
2662 :
2663 0 : nsTableWrapperFrame* tableFrame = GetTableFrame(table.get());
2664 0 : NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);
2665 :
2666 0 : *aRowCount = tableFrame->GetRowCount();
2667 0 : *aColCount = tableFrame->GetColCount();
2668 :
2669 0 : return NS_OK;
2670 : }
2671 :
2672 : NS_IMETHODIMP
2673 0 : HTMLEditor::GetCellDataAt(nsIDOMElement* aTable,
2674 : int32_t aRowIndex,
2675 : int32_t aColIndex,
2676 : nsIDOMElement** aCell,
2677 : int32_t* aStartRowIndex,
2678 : int32_t* aStartColIndex,
2679 : int32_t* aRowSpan,
2680 : int32_t* aColSpan,
2681 : int32_t* aActualRowSpan,
2682 : int32_t* aActualColSpan,
2683 : bool* aIsSelected)
2684 : {
2685 0 : NS_ENSURE_ARG_POINTER(aStartRowIndex);
2686 0 : NS_ENSURE_ARG_POINTER(aStartColIndex);
2687 0 : NS_ENSURE_ARG_POINTER(aRowSpan);
2688 0 : NS_ENSURE_ARG_POINTER(aColSpan);
2689 0 : NS_ENSURE_ARG_POINTER(aActualRowSpan);
2690 0 : NS_ENSURE_ARG_POINTER(aActualColSpan);
2691 0 : NS_ENSURE_ARG_POINTER(aIsSelected);
2692 0 : NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
2693 :
2694 0 : *aStartRowIndex = 0;
2695 0 : *aStartColIndex = 0;
2696 0 : *aRowSpan = 0;
2697 0 : *aColSpan = 0;
2698 0 : *aActualRowSpan = 0;
2699 0 : *aActualColSpan = 0;
2700 0 : *aIsSelected = false;
2701 :
2702 0 : *aCell = nullptr;
2703 :
2704 0 : if (!aTable) {
2705 : // Get the selected table or the table enclosing the selection anchor
2706 0 : nsCOMPtr<nsIDOMElement> table;
2707 : nsresult rv =
2708 0 : GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr,
2709 0 : getter_AddRefs(table));
2710 0 : NS_ENSURE_SUCCESS(rv, rv);
2711 0 : if (!table) {
2712 0 : return NS_ERROR_FAILURE;
2713 : }
2714 0 : aTable = table;
2715 : }
2716 :
2717 0 : nsTableWrapperFrame* tableFrame = GetTableFrame(aTable);
2718 0 : NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);
2719 :
2720 : nsTableCellFrame* cellFrame =
2721 0 : tableFrame->GetCellFrameAt(aRowIndex, aColIndex);
2722 0 : if (!cellFrame) {
2723 0 : return NS_ERROR_FAILURE;
2724 : }
2725 :
2726 0 : *aIsSelected = cellFrame->IsSelected();
2727 0 : cellFrame->GetRowIndex(*aStartRowIndex);
2728 0 : cellFrame->GetColIndex(*aStartColIndex);
2729 0 : *aRowSpan = cellFrame->GetRowSpan();
2730 0 : *aColSpan = cellFrame->GetColSpan();
2731 0 : *aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
2732 0 : *aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
2733 0 : nsCOMPtr<nsIDOMElement> domCell = do_QueryInterface(cellFrame->GetContent());
2734 0 : domCell.forget(aCell);
2735 :
2736 0 : return NS_OK;
2737 : }
2738 :
2739 : // When all you want is the cell
2740 : NS_IMETHODIMP
2741 0 : HTMLEditor::GetCellAt(nsIDOMElement* aTable,
2742 : int32_t aRowIndex,
2743 : int32_t aColIndex,
2744 : nsIDOMElement** aCell)
2745 : {
2746 0 : NS_ENSURE_ARG_POINTER(aCell);
2747 0 : *aCell = nullptr;
2748 :
2749 0 : if (!aTable) {
2750 : // Get the selected table or the table enclosing the selection anchor
2751 0 : nsCOMPtr<nsIDOMElement> table;
2752 : nsresult rv =
2753 0 : GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr,
2754 0 : getter_AddRefs(table));
2755 0 : NS_ENSURE_SUCCESS(rv, rv);
2756 0 : NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
2757 0 : aTable = table;
2758 : }
2759 :
2760 0 : nsTableWrapperFrame* tableFrame = GetTableFrame(aTable);
2761 0 : if (!tableFrame) {
2762 0 : *aCell = nullptr;
2763 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
2764 : }
2765 :
2766 : nsCOMPtr<nsIDOMElement> domCell =
2767 0 : do_QueryInterface(tableFrame->GetCellAt(aRowIndex, aColIndex));
2768 0 : domCell.forget(aCell);
2769 :
2770 0 : return NS_OK;
2771 : }
2772 :
2773 : // When all you want are the rowspan and colspan (not exposed in nsITableEditor)
2774 : NS_IMETHODIMP
2775 0 : HTMLEditor::GetCellSpansAt(nsIDOMElement* aTable,
2776 : int32_t aRowIndex,
2777 : int32_t aColIndex,
2778 : int32_t& aActualRowSpan,
2779 : int32_t& aActualColSpan)
2780 : {
2781 0 : nsTableWrapperFrame* tableFrame = GetTableFrame(aTable);
2782 0 : if (!tableFrame) {
2783 0 : return NS_ERROR_FAILURE;
2784 : }
2785 0 : aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
2786 0 : aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
2787 :
2788 0 : return NS_OK;
2789 : }
2790 :
2791 : nsresult
2792 0 : HTMLEditor::GetCellContext(Selection** aSelection,
2793 : nsIDOMElement** aTable,
2794 : nsIDOMElement** aCell,
2795 : nsIDOMNode** aCellParent,
2796 : int32_t* aCellOffset,
2797 : int32_t* aRowIndex,
2798 : int32_t* aColIndex)
2799 : {
2800 : // Initialize return pointers
2801 0 : if (aSelection) {
2802 0 : *aSelection = nullptr;
2803 : }
2804 0 : if (aTable) {
2805 0 : *aTable = nullptr;
2806 : }
2807 0 : if (aCell) {
2808 0 : *aCell = nullptr;
2809 : }
2810 0 : if (aCellParent) {
2811 0 : *aCellParent = nullptr;
2812 : }
2813 0 : if (aCellOffset) {
2814 0 : *aCellOffset = 0;
2815 : }
2816 0 : if (aRowIndex) {
2817 0 : *aRowIndex = 0;
2818 : }
2819 0 : if (aColIndex) {
2820 0 : *aColIndex = 0;
2821 : }
2822 :
2823 0 : RefPtr<Selection> selection = GetSelection();
2824 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2825 :
2826 0 : if (aSelection) {
2827 0 : *aSelection = selection.get();
2828 0 : NS_ADDREF(*aSelection);
2829 : }
2830 0 : nsCOMPtr <nsIDOMElement> table;
2831 0 : nsCOMPtr <nsIDOMElement> cell;
2832 :
2833 : // Caller may supply the cell...
2834 0 : if (aCell && *aCell) {
2835 0 : cell = *aCell;
2836 : }
2837 :
2838 : // ...but if not supplied,
2839 : // get cell if it's the child of selection anchor node,
2840 : // or get the enclosing by a cell
2841 0 : if (!cell) {
2842 : // Find a selected or enclosing table element
2843 0 : nsCOMPtr<nsIDOMElement> cellOrTableElement;
2844 : int32_t selectedCount;
2845 0 : nsAutoString tagName;
2846 : nsresult rv =
2847 0 : GetSelectedOrParentTableElement(tagName, &selectedCount,
2848 0 : getter_AddRefs(cellOrTableElement));
2849 0 : NS_ENSURE_SUCCESS(rv, rv);
2850 0 : if (tagName.EqualsLiteral("table")) {
2851 : // We have a selected table, not a cell
2852 0 : if (aTable) {
2853 0 : *aTable = cellOrTableElement.get();
2854 0 : NS_ADDREF(*aTable);
2855 : }
2856 0 : return NS_OK;
2857 : }
2858 0 : if (!tagName.EqualsLiteral("td")) {
2859 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
2860 : }
2861 :
2862 : // We found a cell
2863 0 : cell = cellOrTableElement;
2864 : }
2865 0 : if (aCell) {
2866 0 : *aCell = cell.get();
2867 0 : NS_ADDREF(*aCell);
2868 : }
2869 :
2870 : // Get containing table
2871 0 : nsresult rv = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell,
2872 0 : getter_AddRefs(table));
2873 0 : NS_ENSURE_SUCCESS(rv, rv);
2874 : // Cell must be in a table, so fail if not found
2875 0 : NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
2876 0 : if (aTable) {
2877 0 : *aTable = table.get();
2878 0 : NS_ADDREF(*aTable);
2879 : }
2880 :
2881 : // Get the rest of the related data only if requested
2882 0 : if (aRowIndex || aColIndex) {
2883 : int32_t rowIndex, colIndex;
2884 : // Get current cell location so we can put caret back there when done
2885 0 : rv = GetCellIndexes(cell, &rowIndex, &colIndex);
2886 0 : if (NS_FAILED(rv)) {
2887 0 : return rv;
2888 : }
2889 0 : if (aRowIndex) {
2890 0 : *aRowIndex = rowIndex;
2891 : }
2892 0 : if (aColIndex) {
2893 0 : *aColIndex = colIndex;
2894 : }
2895 : }
2896 0 : if (aCellParent) {
2897 0 : nsCOMPtr <nsIDOMNode> cellParent;
2898 : // Get the immediate parent of the cell
2899 0 : rv = cell->GetParentNode(getter_AddRefs(cellParent));
2900 0 : NS_ENSURE_SUCCESS(rv, rv);
2901 : // Cell has to have a parent, so fail if not found
2902 0 : NS_ENSURE_TRUE(cellParent, NS_ERROR_FAILURE);
2903 :
2904 0 : *aCellParent = cellParent.get();
2905 0 : NS_ADDREF(*aCellParent);
2906 :
2907 0 : if (aCellOffset) {
2908 0 : *aCellOffset = GetChildOffset(cell, cellParent);
2909 : }
2910 : }
2911 :
2912 0 : return NS_OK;
2913 : }
2914 :
2915 : nsresult
2916 0 : HTMLEditor::GetCellFromRange(nsRange* aRange,
2917 : nsIDOMElement** aCell)
2918 : {
2919 : // Note: this might return a node that is outside of the range.
2920 : // Use carefully.
2921 0 : NS_ENSURE_TRUE(aRange && aCell, NS_ERROR_NULL_POINTER);
2922 :
2923 0 : *aCell = nullptr;
2924 :
2925 0 : nsCOMPtr<nsIDOMNode> startContainer;
2926 0 : nsresult rv = aRange->GetStartContainer(getter_AddRefs(startContainer));
2927 0 : NS_ENSURE_SUCCESS(rv, rv);
2928 0 : NS_ENSURE_TRUE(startContainer, NS_ERROR_FAILURE);
2929 :
2930 : int32_t startOffset;
2931 0 : rv = aRange->GetStartOffset(&startOffset);
2932 0 : NS_ENSURE_SUCCESS(rv, rv);
2933 :
2934 0 : nsCOMPtr<nsIDOMNode> childNode = GetChildAt(startContainer, startOffset);
2935 : // This means selection is probably at a text node (or end of doc?)
2936 0 : if (!childNode) {
2937 0 : return NS_ERROR_FAILURE;
2938 : }
2939 :
2940 0 : nsCOMPtr<nsIDOMNode> endContainer;
2941 0 : rv = aRange->GetEndContainer(getter_AddRefs(endContainer));
2942 0 : NS_ENSURE_SUCCESS(rv, rv);
2943 0 : NS_ENSURE_TRUE(startContainer, NS_ERROR_FAILURE);
2944 :
2945 : int32_t endOffset;
2946 0 : rv = aRange->GetEndOffset(&endOffset);
2947 0 : NS_ENSURE_SUCCESS(rv, rv);
2948 :
2949 : // If a cell is deleted, the range is collapse
2950 : // (startOffset == endOffset)
2951 : // so tell caller the cell wasn't found
2952 0 : if (startContainer == endContainer &&
2953 0 : endOffset == startOffset+1 &&
2954 0 : HTMLEditUtils::IsTableCell(childNode)) {
2955 : // Should we also test if frame is selected? (Use GetCellDataAt())
2956 : // (Let's not for now -- more efficient)
2957 0 : nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childNode);
2958 0 : *aCell = cellElement.get();
2959 0 : NS_ADDREF(*aCell);
2960 0 : return NS_OK;
2961 : }
2962 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
2963 : }
2964 :
2965 : NS_IMETHODIMP
2966 0 : HTMLEditor::GetFirstSelectedCell(nsIDOMRange** aRange,
2967 : nsIDOMElement** aCell)
2968 : {
2969 0 : NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
2970 0 : *aCell = nullptr;
2971 0 : if (aRange) {
2972 0 : *aRange = nullptr;
2973 : }
2974 :
2975 0 : RefPtr<Selection> selection = GetSelection();
2976 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2977 :
2978 0 : RefPtr<nsRange> range = selection->GetRangeAt(0);
2979 0 : NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
2980 :
2981 0 : mSelectedCellIndex = 0;
2982 :
2983 0 : nsresult rv = GetCellFromRange(range, aCell);
2984 : // Failure here probably means selection is in a text node,
2985 : // so there's no selected cell
2986 0 : if (NS_FAILED(rv)) {
2987 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
2988 : }
2989 : // No cell means range was collapsed (cell was deleted)
2990 0 : if (!*aCell) {
2991 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
2992 : }
2993 :
2994 0 : if (aRange) {
2995 0 : *aRange = range.get();
2996 0 : NS_ADDREF(*aRange);
2997 : }
2998 :
2999 : // Setup for next cell
3000 0 : mSelectedCellIndex = 1;
3001 :
3002 0 : return NS_OK;
3003 : }
3004 :
3005 : NS_IMETHODIMP
3006 0 : HTMLEditor::GetNextSelectedCell(nsIDOMRange** aRange,
3007 : nsIDOMElement** aCell)
3008 : {
3009 0 : NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
3010 0 : *aCell = nullptr;
3011 0 : if (aRange) {
3012 0 : *aRange = nullptr;
3013 : }
3014 :
3015 0 : RefPtr<Selection> selection = GetSelection();
3016 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
3017 :
3018 0 : int32_t rangeCount = selection->RangeCount();
3019 :
3020 : // Don't even try if index exceeds range count
3021 0 : if (mSelectedCellIndex >= rangeCount) {
3022 0 : return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
3023 : }
3024 :
3025 : // Scan through ranges to find next valid selected cell
3026 0 : RefPtr<nsRange> range;
3027 0 : for (; mSelectedCellIndex < rangeCount; mSelectedCellIndex++) {
3028 0 : range = selection->GetRangeAt(mSelectedCellIndex);
3029 0 : NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
3030 :
3031 0 : nsresult rv = GetCellFromRange(range, aCell);
3032 : // Failure here means the range doesn't contain a cell
3033 0 : NS_ENSURE_SUCCESS(rv, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
3034 :
3035 : // We found a selected cell
3036 0 : if (*aCell) {
3037 0 : break;
3038 : }
3039 :
3040 : // If we didn't find a cell, continue to next range in selection
3041 : }
3042 : // No cell means all remaining ranges were collapsed (cells were deleted)
3043 0 : NS_ENSURE_TRUE(*aCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
3044 :
3045 0 : if (aRange) {
3046 0 : *aRange = range.get();
3047 0 : NS_ADDREF(*aRange);
3048 : }
3049 :
3050 : // Setup for next cell
3051 0 : mSelectedCellIndex++;
3052 :
3053 0 : return NS_OK;
3054 : }
3055 :
3056 : NS_IMETHODIMP
3057 0 : HTMLEditor::GetFirstSelectedCellInTable(int32_t* aRowIndex,
3058 : int32_t* aColIndex,
3059 : nsIDOMElement** aCell)
3060 : {
3061 0 : NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
3062 0 : *aCell = nullptr;
3063 0 : if (aRowIndex) {
3064 0 : *aRowIndex = 0;
3065 : }
3066 0 : if (aColIndex) {
3067 0 : *aColIndex = 0;
3068 : }
3069 :
3070 0 : nsCOMPtr<nsIDOMElement> cell;
3071 0 : nsresult rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
3072 0 : NS_ENSURE_SUCCESS(rv, rv);
3073 0 : NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
3074 :
3075 0 : *aCell = cell.get();
3076 0 : NS_ADDREF(*aCell);
3077 :
3078 : // Also return the row and/or column if requested
3079 0 : if (aRowIndex || aColIndex) {
3080 : int32_t startRowIndex, startColIndex;
3081 0 : rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
3082 0 : if (NS_FAILED(rv)) {
3083 0 : return rv;
3084 : }
3085 :
3086 0 : if (aRowIndex) {
3087 0 : *aRowIndex = startRowIndex;
3088 : }
3089 0 : if (aColIndex) {
3090 0 : *aColIndex = startColIndex;
3091 : }
3092 : }
3093 :
3094 0 : return NS_OK;
3095 : }
3096 :
3097 : NS_IMETHODIMP
3098 0 : HTMLEditor::SetSelectionAfterTableEdit(nsIDOMElement* aTable,
3099 : int32_t aRow,
3100 : int32_t aCol,
3101 : int32_t aDirection,
3102 : bool aSelected)
3103 : {
3104 0 : NS_ENSURE_TRUE(aTable, NS_ERROR_NOT_INITIALIZED);
3105 :
3106 0 : RefPtr<Selection> selection = GetSelection();
3107 :
3108 0 : if (!selection) {
3109 0 : return NS_ERROR_FAILURE;
3110 : }
3111 :
3112 0 : nsCOMPtr<nsIDOMElement> cell;
3113 0 : bool done = false;
3114 0 : do {
3115 0 : nsresult rv = GetCellAt(aTable, aRow, aCol, getter_AddRefs(cell));
3116 0 : if (NS_FAILED(rv)) {
3117 0 : break;
3118 : }
3119 :
3120 0 : if (cell) {
3121 0 : if (aSelected) {
3122 : // Reselect the cell
3123 0 : return SelectElement(cell);
3124 : } else {
3125 : // Set the caret to deepest first child
3126 : // but don't go into nested tables
3127 : // TODO: Should we really be placing the caret at the END
3128 : // of the cell content?
3129 0 : nsCOMPtr<nsINode> cellNode = do_QueryInterface(cell);
3130 0 : if (cellNode) {
3131 0 : CollapseSelectionToDeepestNonTableFirstChild(selection, cellNode);
3132 : }
3133 0 : return NS_OK;
3134 : }
3135 : } else {
3136 : // Setup index to find another cell in the
3137 : // direction requested, but move in
3138 : // other direction if already at beginning of row or column
3139 0 : switch (aDirection) {
3140 : case ePreviousColumn:
3141 0 : if (!aCol) {
3142 0 : if (aRow > 0) {
3143 0 : aRow--;
3144 : } else {
3145 0 : done = true;
3146 : }
3147 : } else {
3148 0 : aCol--;
3149 : }
3150 0 : break;
3151 : case ePreviousRow:
3152 0 : if (!aRow) {
3153 0 : if (aCol > 0) {
3154 0 : aCol--;
3155 : } else {
3156 0 : done = true;
3157 : }
3158 : } else {
3159 0 : aRow--;
3160 : }
3161 0 : break;
3162 : default:
3163 0 : done = true;
3164 : }
3165 : }
3166 0 : } while (!done);
3167 :
3168 : // We didn't find a cell
3169 : // Set selection to just before the table
3170 0 : nsCOMPtr<nsIDOMNode> tableParent;
3171 0 : nsresult rv = aTable->GetParentNode(getter_AddRefs(tableParent));
3172 0 : if (NS_SUCCEEDED(rv) && tableParent) {
3173 0 : int32_t tableOffset = GetChildOffset(aTable, tableParent);
3174 0 : return selection->Collapse(tableParent, tableOffset);
3175 : }
3176 : // Last resort: Set selection to start of doc
3177 : // (it's very bad to not have a valid selection!)
3178 0 : return SetSelectionAtDocumentStart(selection);
3179 : }
3180 :
3181 : NS_IMETHODIMP
3182 0 : HTMLEditor::GetSelectedOrParentTableElement(nsAString& aTagName,
3183 : int32_t* aSelectedCount,
3184 : nsIDOMElement** aTableElement)
3185 : {
3186 0 : NS_ENSURE_ARG_POINTER(aTableElement);
3187 0 : NS_ENSURE_ARG_POINTER(aSelectedCount);
3188 0 : *aTableElement = nullptr;
3189 0 : aTagName.Truncate();
3190 0 : *aSelectedCount = 0;
3191 :
3192 0 : RefPtr<Selection> selection = GetSelection();
3193 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
3194 :
3195 : // Try to get the first selected cell
3196 0 : nsCOMPtr<nsIDOMElement> tableOrCellElement;
3197 0 : nsresult rv = GetFirstSelectedCell(nullptr,
3198 0 : getter_AddRefs(tableOrCellElement));
3199 0 : NS_ENSURE_SUCCESS(rv, rv);
3200 :
3201 0 : NS_NAMED_LITERAL_STRING(tdName, "td");
3202 :
3203 0 : if (tableOrCellElement) {
3204 : // Each cell is in its own selection range,
3205 : // so count signals multiple-cell selection
3206 0 : rv = selection->GetRangeCount(aSelectedCount);
3207 0 : NS_ENSURE_SUCCESS(rv, rv);
3208 0 : aTagName = tdName;
3209 : } else {
3210 0 : nsCOMPtr<nsIDOMNode> anchorNode;
3211 0 : rv = selection->GetAnchorNode(getter_AddRefs(anchorNode));
3212 0 : if (NS_FAILED(rv)) {
3213 0 : return rv;
3214 : }
3215 0 : NS_ENSURE_TRUE(anchorNode, NS_ERROR_FAILURE);
3216 :
3217 0 : nsCOMPtr<nsIDOMNode> selectedNode;
3218 :
3219 : // Get child of anchor node, if exists
3220 : bool hasChildren;
3221 0 : anchorNode->HasChildNodes(&hasChildren);
3222 :
3223 0 : if (hasChildren) {
3224 : int32_t anchorOffset;
3225 0 : rv = selection->GetAnchorOffset(&anchorOffset);
3226 0 : NS_ENSURE_SUCCESS(rv, rv);
3227 0 : selectedNode = GetChildAt(anchorNode, anchorOffset);
3228 0 : if (!selectedNode) {
3229 0 : selectedNode = anchorNode;
3230 : // If anchor doesn't have a child, we can't be selecting a table element,
3231 : // so don't do the following:
3232 : } else {
3233 0 : nsCOMPtr<nsIAtom> atom = EditorBase::GetTag(selectedNode);
3234 :
3235 0 : if (atom == nsGkAtoms::td) {
3236 0 : tableOrCellElement = do_QueryInterface(selectedNode);
3237 0 : aTagName = tdName;
3238 : // Each cell is in its own selection range,
3239 : // so count signals multiple-cell selection
3240 0 : rv = selection->GetRangeCount(aSelectedCount);
3241 0 : NS_ENSURE_SUCCESS(rv, rv);
3242 0 : } else if (atom == nsGkAtoms::table) {
3243 0 : tableOrCellElement = do_QueryInterface(selectedNode);
3244 0 : aTagName.AssignLiteral("table");
3245 0 : *aSelectedCount = 1;
3246 0 : } else if (atom == nsGkAtoms::tr) {
3247 0 : tableOrCellElement = do_QueryInterface(selectedNode);
3248 0 : aTagName.AssignLiteral("tr");
3249 0 : *aSelectedCount = 1;
3250 : }
3251 : }
3252 : }
3253 0 : if (!tableOrCellElement) {
3254 : // Didn't find a table element -- find a cell parent
3255 0 : rv = GetElementOrParentByTagName(tdName, anchorNode,
3256 0 : getter_AddRefs(tableOrCellElement));
3257 0 : if (NS_FAILED(rv)) {
3258 0 : return rv;
3259 : }
3260 0 : if (tableOrCellElement) {
3261 0 : aTagName = tdName;
3262 : }
3263 : }
3264 : }
3265 0 : if (tableOrCellElement) {
3266 0 : *aTableElement = tableOrCellElement.get();
3267 0 : NS_ADDREF(*aTableElement);
3268 : }
3269 0 : return NS_OK;
3270 : }
3271 :
3272 : NS_IMETHODIMP
3273 0 : HTMLEditor::GetSelectedCellsType(nsIDOMElement* aElement,
3274 : uint32_t* aSelectionType)
3275 : {
3276 0 : NS_ENSURE_ARG_POINTER(aSelectionType);
3277 0 : *aSelectionType = 0;
3278 :
3279 : // Be sure we have a table element
3280 : // (if aElement is null, this uses selection's anchor node)
3281 0 : nsCOMPtr<nsIDOMElement> table;
3282 :
3283 : nsresult rv =
3284 0 : GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aElement,
3285 0 : getter_AddRefs(table));
3286 0 : NS_ENSURE_SUCCESS(rv, rv);
3287 :
3288 : int32_t rowCount, colCount;
3289 0 : rv = GetTableSize(table, &rowCount, &colCount);
3290 0 : NS_ENSURE_SUCCESS(rv, rv);
3291 :
3292 : // Traverse all selected cells
3293 0 : nsCOMPtr<nsIDOMElement> selectedCell;
3294 0 : rv = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell));
3295 0 : NS_ENSURE_SUCCESS(rv, rv);
3296 0 : if (rv == NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND) {
3297 0 : return NS_OK;
3298 : }
3299 :
3300 : // We have at least one selected cell, so set return value
3301 0 : *aSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
3302 :
3303 : // Store indexes of each row/col to avoid duplication of searches
3304 0 : nsTArray<int32_t> indexArray;
3305 :
3306 0 : bool allCellsInRowAreSelected = false;
3307 0 : bool allCellsInColAreSelected = false;
3308 0 : while (NS_SUCCEEDED(rv) && selectedCell) {
3309 : // Get the cell's location in the cellmap
3310 : int32_t startRowIndex, startColIndex;
3311 0 : rv = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex);
3312 0 : if (NS_FAILED(rv)) {
3313 0 : return rv;
3314 : }
3315 :
3316 0 : if (!indexArray.Contains(startColIndex)) {
3317 0 : indexArray.AppendElement(startColIndex);
3318 0 : allCellsInRowAreSelected = AllCellsInRowSelected(table, startRowIndex, colCount);
3319 : // We're done as soon as we fail for any row
3320 0 : if (!allCellsInRowAreSelected) {
3321 0 : break;
3322 : }
3323 : }
3324 0 : rv = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
3325 : }
3326 :
3327 0 : if (allCellsInRowAreSelected) {
3328 0 : *aSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
3329 0 : return NS_OK;
3330 : }
3331 : // Test for columns
3332 :
3333 : // Empty the indexArray
3334 0 : indexArray.Clear();
3335 :
3336 : // Start at first cell again
3337 0 : rv = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell));
3338 0 : while (NS_SUCCEEDED(rv) && selectedCell) {
3339 : // Get the cell's location in the cellmap
3340 : int32_t startRowIndex, startColIndex;
3341 0 : rv = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex);
3342 0 : if (NS_FAILED(rv)) {
3343 0 : return rv;
3344 : }
3345 :
3346 0 : if (!indexArray.Contains(startRowIndex)) {
3347 0 : indexArray.AppendElement(startColIndex);
3348 0 : allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, rowCount);
3349 : // We're done as soon as we fail for any column
3350 0 : if (!allCellsInRowAreSelected) {
3351 0 : break;
3352 : }
3353 : }
3354 0 : rv = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
3355 : }
3356 0 : if (allCellsInColAreSelected) {
3357 0 : *aSelectionType = nsISelectionPrivate::TABLESELECTION_COLUMN;
3358 : }
3359 :
3360 0 : return NS_OK;
3361 : }
3362 :
3363 : bool
3364 0 : HTMLEditor::AllCellsInRowSelected(nsIDOMElement* aTable,
3365 : int32_t aRowIndex,
3366 : int32_t aNumberOfColumns)
3367 : {
3368 0 : NS_ENSURE_TRUE(aTable, false);
3369 :
3370 : int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
3371 : bool isSelected;
3372 :
3373 0 : for (int32_t col = 0; col < aNumberOfColumns;
3374 0 : col += std::max(actualColSpan, 1)) {
3375 0 : nsCOMPtr<nsIDOMElement> cell;
3376 0 : nsresult rv = GetCellDataAt(aTable, aRowIndex, col, getter_AddRefs(cell),
3377 : &curStartRowIndex, &curStartColIndex,
3378 : &rowSpan, &colSpan,
3379 0 : &actualRowSpan, &actualColSpan, &isSelected);
3380 :
3381 0 : NS_ENSURE_SUCCESS(rv, false);
3382 : // If no cell, we may have a "ragged" right edge,
3383 : // so return TRUE only if we already found a cell in the row
3384 0 : NS_ENSURE_TRUE(cell, (col > 0) ? true : false);
3385 :
3386 : // Return as soon as a non-selected cell is found
3387 0 : NS_ENSURE_TRUE(isSelected, false);
3388 :
3389 0 : NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in AllCellsInRowSelected");
3390 : }
3391 0 : return true;
3392 : }
3393 :
3394 : bool
3395 0 : HTMLEditor::AllCellsInColumnSelected(nsIDOMElement* aTable,
3396 : int32_t aColIndex,
3397 : int32_t aNumberOfRows)
3398 : {
3399 0 : NS_ENSURE_TRUE(aTable, false);
3400 :
3401 : int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
3402 : bool isSelected;
3403 :
3404 0 : for (int32_t row = 0; row < aNumberOfRows;
3405 0 : row += std::max(actualRowSpan, 1)) {
3406 0 : nsCOMPtr<nsIDOMElement> cell;
3407 0 : nsresult rv = GetCellDataAt(aTable, row, aColIndex, getter_AddRefs(cell),
3408 : &curStartRowIndex, &curStartColIndex,
3409 : &rowSpan, &colSpan,
3410 0 : &actualRowSpan, &actualColSpan, &isSelected);
3411 :
3412 0 : NS_ENSURE_SUCCESS(rv, false);
3413 : // If no cell, we must have a "ragged" right edge on the last column
3414 : // so return TRUE only if we already found a cell in the row
3415 0 : NS_ENSURE_TRUE(cell, (row > 0) ? true : false);
3416 :
3417 : // Return as soon as a non-selected cell is found
3418 0 : NS_ENSURE_TRUE(isSelected, false);
3419 : }
3420 0 : return true;
3421 : }
3422 :
3423 : bool
3424 0 : HTMLEditor::IsEmptyCell(dom::Element* aCell)
3425 : {
3426 0 : MOZ_ASSERT(aCell);
3427 :
3428 : // Check if target only contains empty text node or <br>
3429 0 : nsCOMPtr<nsINode> cellChild = aCell->GetFirstChild();
3430 0 : if (!cellChild) {
3431 0 : return false;
3432 : }
3433 :
3434 0 : nsCOMPtr<nsINode> nextChild = cellChild->GetNextSibling();
3435 0 : if (nextChild) {
3436 0 : return false;
3437 : }
3438 :
3439 : // We insert a single break into a cell by default
3440 : // to have some place to locate a cursor -- it is dispensable
3441 0 : if (cellChild->IsHTMLElement(nsGkAtoms::br)) {
3442 0 : return true;
3443 : }
3444 :
3445 : bool isEmpty;
3446 : // Or check if no real content
3447 0 : nsresult rv = IsEmptyNode(cellChild, &isEmpty, false, false);
3448 0 : NS_ENSURE_SUCCESS(rv, false);
3449 0 : return isEmpty;
3450 : }
3451 :
3452 : } // namespace mozilla
|