1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/html/nsTableEditor.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3438 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <stdio.h> 1.10 + 1.11 +#include "mozilla/Assertions.h" 1.12 +#include "mozilla/dom/Selection.h" 1.13 +#include "mozilla/dom/Element.h" 1.14 +#include "nsAString.h" 1.15 +#include "nsAlgorithm.h" 1.16 +#include "nsCOMPtr.h" 1.17 +#include "nsDebug.h" 1.18 +#include "nsEditProperty.h" 1.19 +#include "nsEditor.h" 1.20 +#include "nsEditorUtils.h" 1.21 +#include "nsError.h" 1.22 +#include "nsGkAtoms.h" 1.23 +#include "nsHTMLEditUtils.h" 1.24 +#include "nsHTMLEditor.h" 1.25 +#include "nsIAtom.h" 1.26 +#include "nsIContent.h" 1.27 +#include "nsIDOMElement.h" 1.28 +#include "nsIDOMNode.h" 1.29 +#include "nsIDOMRange.h" 1.30 +#include "nsIEditor.h" 1.31 +#include "nsIFrame.h" 1.32 +#include "nsIHTMLEditor.h" 1.33 +#include "nsINode.h" 1.34 +#include "nsIPresShell.h" 1.35 +#include "nsISupportsUtils.h" 1.36 +#include "nsITableCellLayout.h" // For efficient access to table cell 1.37 +#include "nsITableEditor.h" 1.38 +#include "nsLiteralString.h" 1.39 +#include "nsQueryFrame.h" 1.40 +#include "nsString.h" 1.41 +#include "nsTArray.h" 1.42 +#include "nsTableCellFrame.h" 1.43 +#include "nsTableOuterFrame.h" 1.44 +#include "nscore.h" 1.45 +#include <algorithm> 1.46 + 1.47 +using namespace mozilla; 1.48 +using namespace mozilla::dom; 1.49 + 1.50 +/*************************************************************************** 1.51 + * stack based helper class for restoring selection after table edit 1.52 + */ 1.53 +class MOZ_STACK_CLASS nsSetSelectionAfterTableEdit 1.54 +{ 1.55 + private: 1.56 + nsCOMPtr<nsITableEditor> mEd; 1.57 + nsCOMPtr<nsIDOMElement> mTable; 1.58 + int32_t mCol, mRow, mDirection, mSelected; 1.59 + public: 1.60 + nsSetSelectionAfterTableEdit(nsITableEditor *aEd, nsIDOMElement* aTable, 1.61 + int32_t aRow, int32_t aCol, int32_t aDirection, 1.62 + bool aSelected) : 1.63 + mEd(do_QueryInterface(aEd)) 1.64 + { 1.65 + mTable = aTable; 1.66 + mRow = aRow; 1.67 + mCol = aCol; 1.68 + mDirection = aDirection; 1.69 + mSelected = aSelected; 1.70 + } 1.71 + 1.72 + ~nsSetSelectionAfterTableEdit() 1.73 + { 1.74 + if (mEd) 1.75 + mEd->SetSelectionAfterTableEdit(mTable, mRow, mCol, mDirection, mSelected); 1.76 + } 1.77 + // This is needed to abort the caret reset in the destructor 1.78 + // when one method yields control to another 1.79 + void CancelSetCaret() {mEd = nullptr; mTable = nullptr;} 1.80 +}; 1.81 + 1.82 +// Stack-class to turn on/off selection batching for table selection 1.83 +class MOZ_STACK_CLASS nsSelectionBatcherForTable 1.84 +{ 1.85 +private: 1.86 + nsCOMPtr<nsISelectionPrivate> mSelection; 1.87 +public: 1.88 + nsSelectionBatcherForTable(nsISelection *aSelection) 1.89 + { 1.90 + nsCOMPtr<nsISelection> sel(aSelection); 1.91 + mSelection = do_QueryInterface(sel); 1.92 + if (mSelection) mSelection->StartBatchChanges(); 1.93 + } 1.94 + virtual ~nsSelectionBatcherForTable() 1.95 + { 1.96 + if (mSelection) mSelection->EndBatchChanges(); 1.97 + } 1.98 +}; 1.99 + 1.100 +// Table Editing helper utilities (not exposed in IDL) 1.101 + 1.102 +NS_IMETHODIMP 1.103 +nsHTMLEditor::InsertCell(nsIDOMElement *aCell, int32_t aRowSpan, int32_t aColSpan, 1.104 + bool aAfter, bool aIsHeader, nsIDOMElement **aNewCell) 1.105 +{ 1.106 + NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); 1.107 + if (aNewCell) *aNewCell = nullptr; 1.108 + 1.109 + // And the parent and offsets needed to do an insert 1.110 + nsCOMPtr<nsIDOMNode> cellParent; 1.111 + nsresult res = aCell->GetParentNode(getter_AddRefs(cellParent)); 1.112 + NS_ENSURE_SUCCESS(res, res); 1.113 + NS_ENSURE_TRUE(cellParent, NS_ERROR_NULL_POINTER); 1.114 + 1.115 + int32_t cellOffset = GetChildOffset(aCell, cellParent); 1.116 + 1.117 + nsCOMPtr<nsIDOMElement> newCell; 1.118 + if (aIsHeader) 1.119 + res = CreateElementWithDefaults(NS_LITERAL_STRING("th"), getter_AddRefs(newCell)); 1.120 + else 1.121 + res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); 1.122 + 1.123 + if(NS_FAILED(res)) return res; 1.124 + if(!newCell) return NS_ERROR_FAILURE; 1.125 + 1.126 + //Optional: return new cell created 1.127 + if (aNewCell) 1.128 + { 1.129 + *aNewCell = newCell.get(); 1.130 + NS_ADDREF(*aNewCell); 1.131 + } 1.132 + 1.133 + if( aRowSpan > 1) 1.134 + { 1.135 + // Note: Do NOT use editor transaction for this 1.136 + nsAutoString newRowSpan; 1.137 + newRowSpan.AppendInt(aRowSpan, 10); 1.138 + newCell->SetAttribute(NS_LITERAL_STRING("rowspan"), newRowSpan); 1.139 + } 1.140 + if( aColSpan > 1) 1.141 + { 1.142 + // Note: Do NOT use editor transaction for this 1.143 + nsAutoString newColSpan; 1.144 + newColSpan.AppendInt(aColSpan, 10); 1.145 + newCell->SetAttribute(NS_LITERAL_STRING("colspan"), newColSpan); 1.146 + } 1.147 + if(aAfter) cellOffset++; 1.148 + 1.149 + //Don't let Rules System change the selection 1.150 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.151 + return InsertNode(newCell, cellParent, cellOffset); 1.152 +} 1.153 + 1.154 +NS_IMETHODIMP nsHTMLEditor::SetColSpan(nsIDOMElement *aCell, int32_t aColSpan) 1.155 +{ 1.156 + NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); 1.157 + nsAutoString newSpan; 1.158 + newSpan.AppendInt(aColSpan, 10); 1.159 + return SetAttribute(aCell, NS_LITERAL_STRING("colspan"), newSpan); 1.160 +} 1.161 + 1.162 +NS_IMETHODIMP nsHTMLEditor::SetRowSpan(nsIDOMElement *aCell, int32_t aRowSpan) 1.163 +{ 1.164 + NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); 1.165 + nsAutoString newSpan; 1.166 + newSpan.AppendInt(aRowSpan, 10); 1.167 + return SetAttribute(aCell, NS_LITERAL_STRING("rowspan"), newSpan); 1.168 +} 1.169 + 1.170 +/****************************************************************/ 1.171 + 1.172 +// Table Editing interface methods 1.173 + 1.174 +NS_IMETHODIMP 1.175 +nsHTMLEditor::InsertTableCell(int32_t aNumber, bool aAfter) 1.176 +{ 1.177 + nsCOMPtr<nsIDOMElement> table; 1.178 + nsCOMPtr<nsIDOMElement> curCell; 1.179 + nsCOMPtr<nsIDOMNode> cellParent; 1.180 + int32_t cellOffset, startRowIndex, startColIndex; 1.181 + nsresult res = GetCellContext(nullptr, 1.182 + getter_AddRefs(table), 1.183 + getter_AddRefs(curCell), 1.184 + getter_AddRefs(cellParent), &cellOffset, 1.185 + &startRowIndex, &startColIndex); 1.186 + NS_ENSURE_SUCCESS(res, res); 1.187 + // Don't fail if no cell found 1.188 + NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.189 + 1.190 + // Get more data for current cell in row we are inserting at (we need COLSPAN) 1.191 + int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.192 + bool isSelected; 1.193 + res = GetCellDataAt(table, startRowIndex, startColIndex, 1.194 + getter_AddRefs(curCell), 1.195 + &curStartRowIndex, &curStartColIndex, &rowSpan, &colSpan, 1.196 + &actualRowSpan, &actualColSpan, &isSelected); 1.197 + NS_ENSURE_SUCCESS(res, res); 1.198 + NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); 1.199 + int32_t newCellIndex = aAfter ? (startColIndex+colSpan) : startColIndex; 1.200 + //We control selection resetting after the insert... 1.201 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, newCellIndex, ePreviousColumn, false); 1.202 + //...so suppress Rules System selection munging 1.203 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.204 + 1.205 + int32_t i; 1.206 + for (i = 0; i < aNumber; i++) 1.207 + { 1.208 + nsCOMPtr<nsIDOMElement> newCell; 1.209 + res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); 1.210 + if (NS_SUCCEEDED(res) && newCell) 1.211 + { 1.212 + if (aAfter) cellOffset++; 1.213 + res = InsertNode(newCell, cellParent, cellOffset); 1.214 + if(NS_FAILED(res)) break; 1.215 + } 1.216 + } 1.217 + return res; 1.218 +} 1.219 + 1.220 + 1.221 +NS_IMETHODIMP 1.222 +nsHTMLEditor::GetFirstRow(nsIDOMElement* aTableElement, nsIDOMNode** aRowNode) 1.223 +{ 1.224 + NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); 1.225 + 1.226 + *aRowNode = nullptr; 1.227 + 1.228 + NS_ENSURE_TRUE(aTableElement, NS_ERROR_NULL_POINTER); 1.229 + 1.230 + nsCOMPtr<nsIDOMElement> tableElement; 1.231 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTableElement, getter_AddRefs(tableElement)); 1.232 + NS_ENSURE_SUCCESS(res, res); 1.233 + NS_ENSURE_TRUE(tableElement, NS_ERROR_NULL_POINTER); 1.234 + 1.235 + nsCOMPtr<nsIDOMNode> tableChild; 1.236 + res = tableElement->GetFirstChild(getter_AddRefs(tableChild)); 1.237 + NS_ENSURE_SUCCESS(res, res); 1.238 + 1.239 + while (tableChild) 1.240 + { 1.241 + nsCOMPtr<nsIContent> content = do_QueryInterface(tableChild); 1.242 + if (content) 1.243 + { 1.244 + nsIAtom *atom = content->Tag(); 1.245 + 1.246 + if (atom == nsEditProperty::tr) 1.247 + { 1.248 + // Found a row directly under <table> 1.249 + *aRowNode = tableChild; 1.250 + NS_ADDREF(*aRowNode); 1.251 + return NS_OK; 1.252 + } 1.253 + // Look for row in one of the row container elements 1.254 + if (atom == nsEditProperty::tbody || 1.255 + atom == nsEditProperty::thead || 1.256 + atom == nsEditProperty::tfoot) 1.257 + { 1.258 + nsCOMPtr<nsIDOMNode> rowNode; 1.259 + res = tableChild->GetFirstChild(getter_AddRefs(rowNode)); 1.260 + NS_ENSURE_SUCCESS(res, res); 1.261 + 1.262 + // We can encounter textnodes here -- must find a row 1.263 + while (rowNode && !nsHTMLEditUtils::IsTableRow(rowNode)) 1.264 + { 1.265 + nsCOMPtr<nsIDOMNode> nextNode; 1.266 + res = rowNode->GetNextSibling(getter_AddRefs(nextNode)); 1.267 + NS_ENSURE_SUCCESS(res, res); 1.268 + 1.269 + rowNode = nextNode; 1.270 + } 1.271 + if(rowNode) 1.272 + { 1.273 + *aRowNode = rowNode.get(); 1.274 + NS_ADDREF(*aRowNode); 1.275 + return NS_OK; 1.276 + } 1.277 + } 1.278 + } 1.279 + // Here if table child was a CAPTION or COLGROUP 1.280 + // or child of a row parent wasn't a row (bad HTML?), 1.281 + // or first child was a textnode 1.282 + // Look in next table child 1.283 + nsCOMPtr<nsIDOMNode> nextChild; 1.284 + res = tableChild->GetNextSibling(getter_AddRefs(nextChild)); 1.285 + NS_ENSURE_SUCCESS(res, res); 1.286 + 1.287 + tableChild = nextChild; 1.288 + }; 1.289 + // If here, row was not found 1.290 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.291 +} 1.292 + 1.293 +NS_IMETHODIMP 1.294 +nsHTMLEditor::GetNextRow(nsIDOMNode* aCurrentRowNode, nsIDOMNode **aRowNode) 1.295 +{ 1.296 + NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); 1.297 + 1.298 + *aRowNode = nullptr; 1.299 + 1.300 + NS_ENSURE_TRUE(aCurrentRowNode, NS_ERROR_NULL_POINTER); 1.301 + 1.302 + if (!nsHTMLEditUtils::IsTableRow(aCurrentRowNode)) 1.303 + return NS_ERROR_FAILURE; 1.304 + 1.305 + nsCOMPtr<nsIDOMNode> nextRow; 1.306 + nsresult res = aCurrentRowNode->GetNextSibling(getter_AddRefs(nextRow)); 1.307 + NS_ENSURE_SUCCESS(res, res); 1.308 + 1.309 + nsCOMPtr<nsIDOMNode> nextNode; 1.310 + 1.311 + // Skip over any textnodes here 1.312 + while (nextRow && !nsHTMLEditUtils::IsTableRow(nextRow)) 1.313 + { 1.314 + res = nextRow->GetNextSibling(getter_AddRefs(nextNode)); 1.315 + NS_ENSURE_SUCCESS(res, res); 1.316 + 1.317 + nextRow = nextNode; 1.318 + } 1.319 + if(nextRow) 1.320 + { 1.321 + *aRowNode = nextRow.get(); 1.322 + NS_ADDREF(*aRowNode); 1.323 + return NS_OK; 1.324 + } 1.325 + 1.326 + // No row found, search for rows in other table sections 1.327 + nsCOMPtr<nsIDOMNode> rowParent; 1.328 + res = aCurrentRowNode->GetParentNode(getter_AddRefs(rowParent)); 1.329 + NS_ENSURE_SUCCESS(res, res); 1.330 + NS_ENSURE_TRUE(rowParent, NS_ERROR_NULL_POINTER); 1.331 + 1.332 + nsCOMPtr<nsIDOMNode> parentSibling; 1.333 + res = rowParent->GetNextSibling(getter_AddRefs(parentSibling)); 1.334 + NS_ENSURE_SUCCESS(res, res); 1.335 + 1.336 + while (parentSibling) 1.337 + { 1.338 + res = parentSibling->GetFirstChild(getter_AddRefs(nextRow)); 1.339 + NS_ENSURE_SUCCESS(res, res); 1.340 + 1.341 + // We can encounter textnodes here -- must find a row 1.342 + while (nextRow && !nsHTMLEditUtils::IsTableRow(nextRow)) 1.343 + { 1.344 + res = nextRow->GetNextSibling(getter_AddRefs(nextNode)); 1.345 + NS_ENSURE_SUCCESS(res, res); 1.346 + 1.347 + nextRow = nextNode; 1.348 + } 1.349 + if(nextRow) 1.350 + { 1.351 + *aRowNode = nextRow.get(); 1.352 + NS_ADDREF(*aRowNode); 1.353 + return NS_OK; 1.354 + } 1.355 +#ifdef DEBUG_cmanske 1.356 + printf("GetNextRow: firstChild of row's parent's sibling is not a TR!\n"); 1.357 +#endif 1.358 + // We arrive here only if a table section has no children 1.359 + // or first child of section is not a row (bad HTML or more "_moz_text" nodes!) 1.360 + // So look for another section sibling 1.361 + res = parentSibling->GetNextSibling(getter_AddRefs(nextNode)); 1.362 + NS_ENSURE_SUCCESS(res, res); 1.363 + 1.364 + parentSibling = nextNode; 1.365 + } 1.366 + // If here, row was not found 1.367 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.368 +} 1.369 + 1.370 +NS_IMETHODIMP 1.371 +nsHTMLEditor::GetLastCellInRow(nsIDOMNode* aRowNode, nsIDOMNode** aCellNode) 1.372 +{ 1.373 + NS_ENSURE_TRUE(aCellNode, NS_ERROR_NULL_POINTER); 1.374 + 1.375 + *aCellNode = nullptr; 1.376 + 1.377 + NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER); 1.378 + 1.379 + nsCOMPtr<nsIDOMNode> rowChild; 1.380 + nsresult res = aRowNode->GetLastChild(getter_AddRefs(rowChild)); 1.381 + NS_ENSURE_SUCCESS(res, res); 1.382 + 1.383 + while (rowChild && !nsHTMLEditUtils::IsTableCell(rowChild)) 1.384 + { 1.385 + // Skip over textnodes 1.386 + nsCOMPtr<nsIDOMNode> previousChild; 1.387 + res = rowChild->GetPreviousSibling(getter_AddRefs(previousChild)); 1.388 + NS_ENSURE_SUCCESS(res, res); 1.389 + 1.390 + rowChild = previousChild; 1.391 + }; 1.392 + if (rowChild) 1.393 + { 1.394 + *aCellNode = rowChild.get(); 1.395 + NS_ADDREF(*aCellNode); 1.396 + return NS_OK; 1.397 + } 1.398 + // If here, cell was not found 1.399 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.400 +} 1.401 + 1.402 +NS_IMETHODIMP 1.403 +nsHTMLEditor::InsertTableColumn(int32_t aNumber, bool aAfter) 1.404 +{ 1.405 + nsCOMPtr<nsISelection> selection; 1.406 + nsCOMPtr<nsIDOMElement> table; 1.407 + nsCOMPtr<nsIDOMElement> curCell; 1.408 + int32_t startRowIndex, startColIndex; 1.409 + nsresult res = GetCellContext(getter_AddRefs(selection), 1.410 + getter_AddRefs(table), 1.411 + getter_AddRefs(curCell), 1.412 + nullptr, nullptr, 1.413 + &startRowIndex, &startColIndex); 1.414 + NS_ENSURE_SUCCESS(res, res); 1.415 + // Don't fail if no cell found 1.416 + NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.417 + 1.418 + // Get more data for current cell (we need ROWSPAN) 1.419 + int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.420 + bool isSelected; 1.421 + res = GetCellDataAt(table, startRowIndex, startColIndex, 1.422 + getter_AddRefs(curCell), 1.423 + &curStartRowIndex, &curStartColIndex, 1.424 + &rowSpan, &colSpan, 1.425 + &actualRowSpan, &actualColSpan, &isSelected); 1.426 + NS_ENSURE_SUCCESS(res, res); 1.427 + NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); 1.428 + 1.429 + nsAutoEditBatch beginBatching(this); 1.430 + // Prevent auto insertion of BR in new cell until we're done 1.431 + nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); 1.432 + 1.433 + // Use column after current cell if requested 1.434 + if (aAfter) 1.435 + { 1.436 + startColIndex += actualColSpan; 1.437 + //Detect when user is adding after a COLSPAN=0 case 1.438 + // Assume they want to stop the "0" behavior and 1.439 + // really add a new column. Thus we set the 1.440 + // colspan to its true value 1.441 + if (colSpan == 0) 1.442 + SetColSpan(curCell, actualColSpan); 1.443 + } 1.444 + 1.445 + int32_t rowCount, colCount, rowIndex; 1.446 + res = GetTableSize(table, &rowCount, &colCount); 1.447 + NS_ENSURE_SUCCESS(res, res); 1.448 + 1.449 + //We reset caret in destructor... 1.450 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); 1.451 + //.. so suppress Rules System selection munging 1.452 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.453 + 1.454 + // If we are inserting after all existing columns 1.455 + // Make sure table is "well formed" 1.456 + // before appending new column 1.457 + if (startColIndex >= colCount) 1.458 + NormalizeTable(table); 1.459 + 1.460 + nsCOMPtr<nsIDOMNode> rowNode; 1.461 + for ( rowIndex = 0; rowIndex < rowCount; rowIndex++) 1.462 + { 1.463 +#ifdef DEBUG_cmanske 1.464 + if (rowIndex == rowCount-1) 1.465 + printf(" ***InsertTableColumn: Inserting cell at last row: %d\n", rowIndex); 1.466 +#endif 1.467 + 1.468 + if (startColIndex < colCount) 1.469 + { 1.470 + // We are inserting before an existing column 1.471 + res = GetCellDataAt(table, rowIndex, startColIndex, 1.472 + getter_AddRefs(curCell), 1.473 + &curStartRowIndex, &curStartColIndex, 1.474 + &rowSpan, &colSpan, 1.475 + &actualRowSpan, &actualColSpan, &isSelected); 1.476 + NS_ENSURE_SUCCESS(res, res); 1.477 + 1.478 + // Don't fail entire process if we fail to find a cell 1.479 + // (may fail just in particular rows with < adequate cells per row) 1.480 + if (curCell) 1.481 + { 1.482 + if (curStartColIndex < startColIndex) 1.483 + { 1.484 + // We have a cell spanning this location 1.485 + // Simply increase its colspan to keep table rectangular 1.486 + // Note: we do nothing if colsSpan=0, 1.487 + // since it should automatically span the new column 1.488 + if (colSpan > 0) 1.489 + SetColSpan(curCell, colSpan+aNumber); 1.490 + } else { 1.491 + // Simply set selection to the current cell 1.492 + // so we can let InsertTableCell() do the work 1.493 + // Insert a new cell before current one 1.494 + selection->Collapse(curCell, 0); 1.495 + res = InsertTableCell(aNumber, false); 1.496 + } 1.497 + } 1.498 + } else { 1.499 + // Get current row and append new cells after last cell in row 1.500 + if(rowIndex == 0) 1.501 + res = GetFirstRow(table.get(), getter_AddRefs(rowNode)); 1.502 + else 1.503 + { 1.504 + nsCOMPtr<nsIDOMNode> nextRow; 1.505 + res = GetNextRow(rowNode.get(), getter_AddRefs(nextRow)); 1.506 + rowNode = nextRow; 1.507 + } 1.508 + NS_ENSURE_SUCCESS(res, res); 1.509 + 1.510 + if (rowNode) 1.511 + { 1.512 + nsCOMPtr<nsIDOMNode> lastCell; 1.513 + res = GetLastCellInRow(rowNode, getter_AddRefs(lastCell)); 1.514 + NS_ENSURE_SUCCESS(res, res); 1.515 + NS_ENSURE_TRUE(lastCell, NS_ERROR_FAILURE); 1.516 + 1.517 + curCell = do_QueryInterface(lastCell); 1.518 + if (curCell) 1.519 + { 1.520 + // Simply add same number of cells to each row 1.521 + // Although tempted to check cell indexes for curCell, 1.522 + // the effects of COLSPAN>1 in some cells makes this futile! 1.523 + // We must use NormalizeTable first to assure 1.524 + // that there are cells in each cellmap location 1.525 + selection->Collapse(curCell, 0); 1.526 + res = InsertTableCell(aNumber, true); 1.527 + } 1.528 + } 1.529 + } 1.530 + } 1.531 + return res; 1.532 +} 1.533 + 1.534 +NS_IMETHODIMP 1.535 +nsHTMLEditor::InsertTableRow(int32_t aNumber, bool aAfter) 1.536 +{ 1.537 + nsCOMPtr<nsISelection> selection; 1.538 + nsCOMPtr<nsIDOMElement> table; 1.539 + nsCOMPtr<nsIDOMElement> curCell; 1.540 + 1.541 + int32_t startRowIndex, startColIndex; 1.542 + nsresult res = GetCellContext(nullptr, 1.543 + getter_AddRefs(table), 1.544 + getter_AddRefs(curCell), 1.545 + nullptr, nullptr, 1.546 + &startRowIndex, &startColIndex); 1.547 + NS_ENSURE_SUCCESS(res, res); 1.548 + // Don't fail if no cell found 1.549 + NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.550 + 1.551 + // Get more data for current cell in row we are inserting at (we need COLSPAN) 1.552 + int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.553 + bool isSelected; 1.554 + res = GetCellDataAt(table, startRowIndex, startColIndex, 1.555 + getter_AddRefs(curCell), 1.556 + &curStartRowIndex, &curStartColIndex, 1.557 + &rowSpan, &colSpan, 1.558 + &actualRowSpan, &actualColSpan, &isSelected); 1.559 + NS_ENSURE_SUCCESS(res, res); 1.560 + NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); 1.561 + 1.562 + int32_t rowCount, colCount; 1.563 + res = GetTableSize(table, &rowCount, &colCount); 1.564 + NS_ENSURE_SUCCESS(res, res); 1.565 + 1.566 + nsAutoEditBatch beginBatching(this); 1.567 + // Prevent auto insertion of BR in new cell until we're done 1.568 + nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); 1.569 + 1.570 + if (aAfter) 1.571 + { 1.572 + // Use row after current cell 1.573 + startRowIndex += actualRowSpan; 1.574 + 1.575 + //Detect when user is adding after a ROWSPAN=0 case 1.576 + // Assume they want to stop the "0" behavior and 1.577 + // really add a new row. Thus we set the 1.578 + // rowspan to its true value 1.579 + if (rowSpan == 0) 1.580 + SetRowSpan(curCell, actualRowSpan); 1.581 + } 1.582 + 1.583 + //We control selection resetting after the insert... 1.584 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); 1.585 + //...so suppress Rules System selection munging 1.586 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.587 + 1.588 + nsCOMPtr<nsIDOMElement> cellForRowParent; 1.589 + int32_t cellsInRow = 0; 1.590 + if (startRowIndex < rowCount) 1.591 + { 1.592 + // We are inserting above an existing row 1.593 + // Get each cell in the insert row to adjust for COLSPAN effects while we 1.594 + // count how many cells are needed 1.595 + int32_t colIndex = 0; 1.596 + // This returns NS_TABLELAYOUT_CELL_NOT_FOUND when we run past end of row, 1.597 + // which passes the NS_SUCCEEDED macro 1.598 + while ( NS_OK == GetCellDataAt(table, startRowIndex, colIndex, 1.599 + getter_AddRefs(curCell), 1.600 + &curStartRowIndex, &curStartColIndex, 1.601 + &rowSpan, &colSpan, 1.602 + &actualRowSpan, &actualColSpan, 1.603 + &isSelected) ) 1.604 + { 1.605 + if (curCell) 1.606 + { 1.607 + if (curStartRowIndex < startRowIndex) 1.608 + { 1.609 + // We have a cell spanning this location 1.610 + // Simply increase its rowspan 1.611 + //Note that if rowSpan == 0, we do nothing, 1.612 + // since that cell should automatically extend into the new row 1.613 + if (rowSpan > 0) 1.614 + SetRowSpan(curCell, rowSpan+aNumber); 1.615 + } else { 1.616 + // We have a cell in the insert row 1.617 + 1.618 + // Count the number of cells we need to add to the new row 1.619 + cellsInRow += actualColSpan; 1.620 + 1.621 + // Save cell we will use below 1.622 + if (!cellForRowParent) 1.623 + cellForRowParent = curCell; 1.624 + } 1.625 + // Next cell in row 1.626 + colIndex += actualColSpan; 1.627 + } 1.628 + else 1.629 + colIndex++; 1.630 + } 1.631 + } else { 1.632 + // We are adding a new row after all others 1.633 + // If it weren't for colspan=0 effect, 1.634 + // we could simply use colCount for number of new cells... 1.635 + cellsInRow = colCount; 1.636 + 1.637 + // ...but we must compensate for all cells with rowSpan = 0 in the last row 1.638 + int32_t lastRow = rowCount-1; 1.639 + int32_t tempColIndex = 0; 1.640 + while ( NS_OK == GetCellDataAt(table, lastRow, tempColIndex, 1.641 + getter_AddRefs(curCell), 1.642 + &curStartRowIndex, &curStartColIndex, 1.643 + &rowSpan, &colSpan, 1.644 + &actualRowSpan, &actualColSpan, 1.645 + &isSelected) ) 1.646 + { 1.647 + if (rowSpan == 0) 1.648 + cellsInRow -= actualColSpan; 1.649 + 1.650 + tempColIndex += actualColSpan; 1.651 + 1.652 + // Save cell from the last row that we will use below 1.653 + if (!cellForRowParent && curStartRowIndex == lastRow) 1.654 + cellForRowParent = curCell; 1.655 + } 1.656 + } 1.657 + 1.658 + if (cellsInRow > 0) 1.659 + { 1.660 + // The row parent and offset where we will insert new row 1.661 + nsCOMPtr<nsIDOMNode> parentOfRow; 1.662 + int32_t newRowOffset; 1.663 + 1.664 + NS_NAMED_LITERAL_STRING(trStr, "tr"); 1.665 + if (cellForRowParent) 1.666 + { 1.667 + nsCOMPtr<nsIDOMElement> parentRow; 1.668 + res = GetElementOrParentByTagName(trStr, cellForRowParent, getter_AddRefs(parentRow)); 1.669 + NS_ENSURE_SUCCESS(res, res); 1.670 + NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER); 1.671 + 1.672 + parentRow->GetParentNode(getter_AddRefs(parentOfRow)); 1.673 + NS_ENSURE_TRUE(parentOfRow, NS_ERROR_NULL_POINTER); 1.674 + 1.675 + newRowOffset = GetChildOffset(parentRow, parentOfRow); 1.676 + 1.677 + // Adjust for when adding past the end 1.678 + if (aAfter && startRowIndex >= rowCount) 1.679 + newRowOffset++; 1.680 + } 1.681 + else 1.682 + return NS_ERROR_FAILURE; 1.683 + 1.684 + for (int32_t row = 0; row < aNumber; row++) 1.685 + { 1.686 + // Create a new row 1.687 + nsCOMPtr<nsIDOMElement> newRow; 1.688 + res = CreateElementWithDefaults(trStr, getter_AddRefs(newRow)); 1.689 + if (NS_SUCCEEDED(res)) 1.690 + { 1.691 + NS_ENSURE_TRUE(newRow, NS_ERROR_FAILURE); 1.692 + 1.693 + for (int32_t i = 0; i < cellsInRow; i++) 1.694 + { 1.695 + nsCOMPtr<nsIDOMElement> newCell; 1.696 + res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell)); 1.697 + NS_ENSURE_SUCCESS(res, res); 1.698 + NS_ENSURE_TRUE(newCell, NS_ERROR_FAILURE); 1.699 + 1.700 + // Don't use transaction system yet! (not until entire row is inserted) 1.701 + nsCOMPtr<nsIDOMNode>resultNode; 1.702 + res = newRow->AppendChild(newCell, getter_AddRefs(resultNode)); 1.703 + NS_ENSURE_SUCCESS(res, res); 1.704 + } 1.705 + // Use transaction system to insert the entire row+cells 1.706 + // (Note that rows are inserted at same childoffset each time) 1.707 + res = InsertNode(newRow, parentOfRow, newRowOffset); 1.708 + NS_ENSURE_SUCCESS(res, res); 1.709 + } 1.710 + } 1.711 + } 1.712 + return res; 1.713 +} 1.714 + 1.715 +// Editor helper only 1.716 +// XXX Code changed for bug 217717 and now we don't need aSelection param 1.717 +// TODO: Remove aSelection param 1.718 +NS_IMETHODIMP 1.719 +nsHTMLEditor::DeleteTable2(nsIDOMElement *aTable, nsISelection *aSelection) 1.720 +{ 1.721 + NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); 1.722 + 1.723 + // Select the table 1.724 + nsresult res = ClearSelection(); 1.725 + if (NS_SUCCEEDED(res)) 1.726 + res = AppendNodeToSelectionAsRange(aTable); 1.727 + NS_ENSURE_SUCCESS(res, res); 1.728 + 1.729 + return DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip); 1.730 +} 1.731 + 1.732 +NS_IMETHODIMP 1.733 +nsHTMLEditor::DeleteTable() 1.734 +{ 1.735 + nsCOMPtr<nsISelection> selection; 1.736 + nsCOMPtr<nsIDOMElement> table; 1.737 + nsresult res = GetCellContext(getter_AddRefs(selection), 1.738 + getter_AddRefs(table), 1.739 + nullptr, nullptr, nullptr, nullptr, nullptr); 1.740 + 1.741 + NS_ENSURE_SUCCESS(res, res); 1.742 + 1.743 + nsAutoEditBatch beginBatching(this); 1.744 + return DeleteTable2(table, selection); 1.745 +} 1.746 + 1.747 +NS_IMETHODIMP 1.748 +nsHTMLEditor::DeleteTableCell(int32_t aNumber) 1.749 +{ 1.750 + nsCOMPtr<nsISelection> selection; 1.751 + nsCOMPtr<nsIDOMElement> table; 1.752 + nsCOMPtr<nsIDOMElement> cell; 1.753 + int32_t startRowIndex, startColIndex; 1.754 + 1.755 + 1.756 + nsresult res = GetCellContext(getter_AddRefs(selection), 1.757 + getter_AddRefs(table), 1.758 + getter_AddRefs(cell), 1.759 + nullptr, nullptr, 1.760 + &startRowIndex, &startColIndex); 1.761 + 1.762 + NS_ENSURE_SUCCESS(res, res); 1.763 + // Don't fail if we didn't find a table or cell 1.764 + NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.765 + 1.766 + nsAutoEditBatch beginBatching(this); 1.767 + // Prevent rules testing until we're done 1.768 + nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); 1.769 + 1.770 + nsCOMPtr<nsIDOMElement> firstCell; 1.771 + nsCOMPtr<nsIDOMRange> range; 1.772 + res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); 1.773 + NS_ENSURE_SUCCESS(res, res); 1.774 + 1.775 + int32_t rangeCount; 1.776 + res = selection->GetRangeCount(&rangeCount); 1.777 + NS_ENSURE_SUCCESS(res, res); 1.778 + 1.779 + if (firstCell && rangeCount > 1) 1.780 + { 1.781 + // When > 1 selected cell, 1.782 + // ignore aNumber and use selected cells 1.783 + cell = firstCell; 1.784 + 1.785 + int32_t rowCount, colCount; 1.786 + res = GetTableSize(table, &rowCount, &colCount); 1.787 + NS_ENSURE_SUCCESS(res, res); 1.788 + 1.789 + // Get indexes -- may be different than original cell 1.790 + res = GetCellIndexes(cell, &startRowIndex, &startColIndex); 1.791 + NS_ENSURE_SUCCESS(res, res); 1.792 + 1.793 + // The setCaret object will call SetSelectionAfterTableEdit in its destructor 1.794 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); 1.795 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.796 + 1.797 + bool checkToDeleteRow = true; 1.798 + bool checkToDeleteColumn = true; 1.799 + while (cell) 1.800 + { 1.801 + bool deleteRow = false; 1.802 + bool deleteCol = false; 1.803 + 1.804 + if (checkToDeleteRow) 1.805 + { 1.806 + // Optimize to delete an entire row 1.807 + // Clear so we don't repeat AllCellsInRowSelected within the same row 1.808 + checkToDeleteRow = false; 1.809 + 1.810 + deleteRow = AllCellsInRowSelected(table, startRowIndex, colCount); 1.811 + if (deleteRow) 1.812 + { 1.813 + // First, find the next cell in a different row 1.814 + // to continue after we delete this row 1.815 + int32_t nextRow = startRowIndex; 1.816 + while (nextRow == startRowIndex) 1.817 + { 1.818 + res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); 1.819 + NS_ENSURE_SUCCESS(res, res); 1.820 + if (!cell) break; 1.821 + res = GetCellIndexes(cell, &nextRow, &startColIndex); 1.822 + NS_ENSURE_SUCCESS(res, res); 1.823 + } 1.824 + // Delete entire row 1.825 + res = DeleteRow(table, startRowIndex); 1.826 + NS_ENSURE_SUCCESS(res, res); 1.827 + 1.828 + if (cell) 1.829 + { 1.830 + // For the next cell: Subtract 1 for row we deleted 1.831 + startRowIndex = nextRow - 1; 1.832 + // Set true since we know we will look at a new row next 1.833 + checkToDeleteRow = true; 1.834 + } 1.835 + } 1.836 + } 1.837 + if (!deleteRow) 1.838 + { 1.839 + if (checkToDeleteColumn) 1.840 + { 1.841 + // Optimize to delete an entire column 1.842 + // Clear this so we don't repeat AllCellsInColSelected within the same Col 1.843 + checkToDeleteColumn = false; 1.844 + 1.845 + deleteCol = AllCellsInColumnSelected(table, startColIndex, colCount); 1.846 + if (deleteCol) 1.847 + { 1.848 + // First, find the next cell in a different column 1.849 + // to continue after we delete this column 1.850 + int32_t nextCol = startColIndex; 1.851 + while (nextCol == startColIndex) 1.852 + { 1.853 + res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); 1.854 + NS_ENSURE_SUCCESS(res, res); 1.855 + if (!cell) break; 1.856 + res = GetCellIndexes(cell, &startRowIndex, &nextCol); 1.857 + NS_ENSURE_SUCCESS(res, res); 1.858 + } 1.859 + // Delete entire Col 1.860 + res = DeleteColumn(table, startColIndex); 1.861 + NS_ENSURE_SUCCESS(res, res); 1.862 + if (cell) 1.863 + { 1.864 + // For the next cell, subtract 1 for col. deleted 1.865 + startColIndex = nextCol - 1; 1.866 + // Set true since we know we will look at a new column next 1.867 + checkToDeleteColumn = true; 1.868 + } 1.869 + } 1.870 + } 1.871 + if (!deleteCol) 1.872 + { 1.873 + // First get the next cell to delete 1.874 + nsCOMPtr<nsIDOMElement> nextCell; 1.875 + res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(nextCell)); 1.876 + NS_ENSURE_SUCCESS(res, res); 1.877 + 1.878 + // Then delete the cell 1.879 + res = DeleteNode(cell); 1.880 + NS_ENSURE_SUCCESS(res, res); 1.881 + 1.882 + // The next cell to delete 1.883 + cell = nextCell; 1.884 + if (cell) 1.885 + { 1.886 + res = GetCellIndexes(cell, &startRowIndex, &startColIndex); 1.887 + NS_ENSURE_SUCCESS(res, res); 1.888 + } 1.889 + } 1.890 + } 1.891 + } 1.892 + } 1.893 + else for (int32_t i = 0; i < aNumber; i++) 1.894 + { 1.895 + res = GetCellContext(getter_AddRefs(selection), 1.896 + getter_AddRefs(table), 1.897 + getter_AddRefs(cell), 1.898 + nullptr, nullptr, 1.899 + &startRowIndex, &startColIndex); 1.900 + NS_ENSURE_SUCCESS(res, res); 1.901 + // Don't fail if no cell found 1.902 + NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.903 + 1.904 + if (1 == GetNumberOfCellsInRow(table, startRowIndex)) 1.905 + { 1.906 + nsCOMPtr<nsIDOMElement> parentRow; 1.907 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell, getter_AddRefs(parentRow)); 1.908 + NS_ENSURE_SUCCESS(res, res); 1.909 + NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER); 1.910 + 1.911 + // We should delete the row instead, 1.912 + // but first check if its the only row left 1.913 + // so we can delete the entire table 1.914 + int32_t rowCount, colCount; 1.915 + res = GetTableSize(table, &rowCount, &colCount); 1.916 + NS_ENSURE_SUCCESS(res, res); 1.917 + 1.918 + if (rowCount == 1) 1.919 + return DeleteTable2(table, selection); 1.920 + 1.921 + // We need to call DeleteTableRow to handle cells with rowspan 1.922 + res = DeleteTableRow(1); 1.923 + NS_ENSURE_SUCCESS(res, res); 1.924 + } 1.925 + else 1.926 + { 1.927 + // More than 1 cell in the row 1.928 + 1.929 + // The setCaret object will call SetSelectionAfterTableEdit in its destructor 1.930 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); 1.931 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.932 + 1.933 + res = DeleteNode(cell); 1.934 + // If we fail, don't try to delete any more cells??? 1.935 + NS_ENSURE_SUCCESS(res, res); 1.936 + } 1.937 + } 1.938 + return NS_OK; 1.939 +} 1.940 + 1.941 +NS_IMETHODIMP 1.942 +nsHTMLEditor::DeleteTableCellContents() 1.943 +{ 1.944 + nsCOMPtr<nsISelection> selection; 1.945 + nsCOMPtr<nsIDOMElement> table; 1.946 + nsCOMPtr<nsIDOMElement> cell; 1.947 + int32_t startRowIndex, startColIndex; 1.948 + nsresult res; 1.949 + res = GetCellContext(getter_AddRefs(selection), 1.950 + getter_AddRefs(table), 1.951 + getter_AddRefs(cell), 1.952 + nullptr, nullptr, 1.953 + &startRowIndex, &startColIndex); 1.954 + NS_ENSURE_SUCCESS(res, res); 1.955 + // Don't fail if no cell found 1.956 + NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.957 + 1.958 + 1.959 + nsAutoEditBatch beginBatching(this); 1.960 + // Prevent rules testing until we're done 1.961 + nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); 1.962 + //Don't let Rules System change the selection 1.963 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.964 + 1.965 + 1.966 + nsCOMPtr<nsIDOMElement> firstCell; 1.967 + nsCOMPtr<nsIDOMRange> range; 1.968 + res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); 1.969 + NS_ENSURE_SUCCESS(res, res); 1.970 + 1.971 + 1.972 + if (firstCell) 1.973 + { 1.974 + cell = firstCell; 1.975 + res = GetCellIndexes(cell, &startRowIndex, &startColIndex); 1.976 + NS_ENSURE_SUCCESS(res, res); 1.977 + } 1.978 + 1.979 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); 1.980 + 1.981 + while (cell) 1.982 + { 1.983 + DeleteCellContents(cell); 1.984 + if (firstCell) 1.985 + { 1.986 + // We doing a selected cells, so do all of them 1.987 + res = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); 1.988 + NS_ENSURE_SUCCESS(res, res); 1.989 + } 1.990 + else 1.991 + cell = nullptr; 1.992 + } 1.993 + return NS_OK; 1.994 +} 1.995 + 1.996 +NS_IMETHODIMP 1.997 +nsHTMLEditor::DeleteCellContents(nsIDOMElement *aCell) 1.998 +{ 1.999 + NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); 1.1000 + 1.1001 + // Prevent rules testing until we're done 1.1002 + nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); 1.1003 + 1.1004 + nsCOMPtr<nsIDOMNode> child; 1.1005 + bool hasChild; 1.1006 + aCell->HasChildNodes(&hasChild); 1.1007 + 1.1008 + while (hasChild) 1.1009 + { 1.1010 + aCell->GetLastChild(getter_AddRefs(child)); 1.1011 + nsresult res = DeleteNode(child); 1.1012 + NS_ENSURE_SUCCESS(res, res); 1.1013 + aCell->HasChildNodes(&hasChild); 1.1014 + } 1.1015 + return NS_OK; 1.1016 +} 1.1017 + 1.1018 +NS_IMETHODIMP 1.1019 +nsHTMLEditor::DeleteTableColumn(int32_t aNumber) 1.1020 +{ 1.1021 + nsCOMPtr<nsISelection> selection; 1.1022 + nsCOMPtr<nsIDOMElement> table; 1.1023 + nsCOMPtr<nsIDOMElement> cell; 1.1024 + int32_t startRowIndex, startColIndex, rowCount, colCount; 1.1025 + nsresult res = GetCellContext(getter_AddRefs(selection), 1.1026 + getter_AddRefs(table), 1.1027 + getter_AddRefs(cell), 1.1028 + nullptr, nullptr, 1.1029 + &startRowIndex, &startColIndex); 1.1030 + NS_ENSURE_SUCCESS(res, res); 1.1031 + // Don't fail if no cell found 1.1032 + NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.1033 + 1.1034 + res = GetTableSize(table, &rowCount, &colCount); 1.1035 + NS_ENSURE_SUCCESS(res, res); 1.1036 + 1.1037 + // Shortcut the case of deleting all columns in table 1.1038 + if(startColIndex == 0 && aNumber >= colCount) 1.1039 + return DeleteTable2(table, selection); 1.1040 + 1.1041 + // Check for counts too high 1.1042 + aNumber = std::min(aNumber,(colCount-startColIndex)); 1.1043 + 1.1044 + nsAutoEditBatch beginBatching(this); 1.1045 + // Prevent rules testing until we're done 1.1046 + nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); 1.1047 + 1.1048 + // Test if deletion is controlled by selected cells 1.1049 + nsCOMPtr<nsIDOMElement> firstCell; 1.1050 + nsCOMPtr<nsIDOMRange> range; 1.1051 + res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); 1.1052 + NS_ENSURE_SUCCESS(res, res); 1.1053 + 1.1054 + int32_t rangeCount; 1.1055 + res = selection->GetRangeCount(&rangeCount); 1.1056 + NS_ENSURE_SUCCESS(res, res); 1.1057 + 1.1058 + if (firstCell && rangeCount > 1) 1.1059 + { 1.1060 + // Fetch indexes again - may be different for selected cells 1.1061 + res = GetCellIndexes(firstCell, &startRowIndex, &startColIndex); 1.1062 + NS_ENSURE_SUCCESS(res, res); 1.1063 + } 1.1064 + //We control selection resetting after the insert... 1.1065 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); 1.1066 + 1.1067 + if (firstCell && rangeCount > 1) 1.1068 + { 1.1069 + // Use selected cells to determine what rows to delete 1.1070 + cell = firstCell; 1.1071 + 1.1072 + while (cell) 1.1073 + { 1.1074 + if (cell != firstCell) 1.1075 + { 1.1076 + res = GetCellIndexes(cell, &startRowIndex, &startColIndex); 1.1077 + NS_ENSURE_SUCCESS(res, res); 1.1078 + } 1.1079 + // Find the next cell in a different column 1.1080 + // to continue after we delete this column 1.1081 + int32_t nextCol = startColIndex; 1.1082 + while (nextCol == startColIndex) 1.1083 + { 1.1084 + res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); 1.1085 + NS_ENSURE_SUCCESS(res, res); 1.1086 + if (!cell) break; 1.1087 + res = GetCellIndexes(cell, &startRowIndex, &nextCol); 1.1088 + NS_ENSURE_SUCCESS(res, res); 1.1089 + } 1.1090 + res = DeleteColumn(table, startColIndex); 1.1091 + NS_ENSURE_SUCCESS(res, res); 1.1092 + } 1.1093 + } 1.1094 + else for (int32_t i = 0; i < aNumber; i++) 1.1095 + { 1.1096 + res = DeleteColumn(table, startColIndex); 1.1097 + NS_ENSURE_SUCCESS(res, res); 1.1098 + } 1.1099 + return NS_OK; 1.1100 +} 1.1101 + 1.1102 +NS_IMETHODIMP 1.1103 +nsHTMLEditor::DeleteColumn(nsIDOMElement *aTable, int32_t aColIndex) 1.1104 +{ 1.1105 + NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); 1.1106 + 1.1107 + nsCOMPtr<nsIDOMElement> cell; 1.1108 + nsCOMPtr<nsIDOMElement> cellInDeleteCol; 1.1109 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.1110 + bool isSelected; 1.1111 + int32_t rowIndex = 0; 1.1112 + nsresult res = NS_OK; 1.1113 + 1.1114 + do { 1.1115 + res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), 1.1116 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.1117 + &actualRowSpan, &actualColSpan, &isSelected); 1.1118 + 1.1119 + NS_ENSURE_SUCCESS(res, res); 1.1120 + 1.1121 + if (cell) 1.1122 + { 1.1123 + // Find cells that don't start in column we are deleting 1.1124 + if (startColIndex < aColIndex || colSpan > 1 || colSpan == 0) 1.1125 + { 1.1126 + // We have a cell spanning this location 1.1127 + // Decrease its colspan to keep table rectangular, 1.1128 + // but if colSpan=0, it will adjust automatically 1.1129 + if (colSpan > 0) 1.1130 + { 1.1131 + NS_ASSERTION((colSpan > 1),"Bad COLSPAN in DeleteTableColumn"); 1.1132 + SetColSpan(cell, colSpan-1); 1.1133 + } 1.1134 + if (startColIndex == aColIndex) 1.1135 + { 1.1136 + // Cell is in column to be deleted, but must have colspan > 1, 1.1137 + // so delete contents of cell instead of cell itself 1.1138 + // (We must have reset colspan above) 1.1139 + DeleteCellContents(cell); 1.1140 + } 1.1141 + // To next cell in column 1.1142 + rowIndex += actualRowSpan; 1.1143 + } 1.1144 + else 1.1145 + { 1.1146 + // Delete the cell 1.1147 + if (1 == GetNumberOfCellsInRow(aTable, rowIndex)) 1.1148 + { 1.1149 + // Only 1 cell in row - delete the row 1.1150 + nsCOMPtr<nsIDOMElement> parentRow; 1.1151 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell, getter_AddRefs(parentRow)); 1.1152 + NS_ENSURE_SUCCESS(res, res); 1.1153 + if(!parentRow) return NS_ERROR_NULL_POINTER; 1.1154 + 1.1155 + // But first check if its the only row left 1.1156 + // so we can delete the entire table 1.1157 + // (This should never happen but it's the safe thing to do) 1.1158 + int32_t rowCount, colCount; 1.1159 + res = GetTableSize(aTable, &rowCount, &colCount); 1.1160 + NS_ENSURE_SUCCESS(res, res); 1.1161 + 1.1162 + if (rowCount == 1) 1.1163 + { 1.1164 + nsCOMPtr<nsISelection> selection; 1.1165 + res = GetSelection(getter_AddRefs(selection)); 1.1166 + NS_ENSURE_SUCCESS(res, res); 1.1167 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.1168 + return DeleteTable2(aTable, selection); 1.1169 + } 1.1170 + 1.1171 + // Delete the row by placing caret in cell we were to delete 1.1172 + // We need to call DeleteTableRow to handle cells with rowspan 1.1173 + res = DeleteRow(aTable, startRowIndex); 1.1174 + NS_ENSURE_SUCCESS(res, res); 1.1175 + 1.1176 + // Note that we don't incremenet rowIndex 1.1177 + // since a row was deleted and "next" 1.1178 + // row now has current rowIndex 1.1179 + } 1.1180 + else 1.1181 + { 1.1182 + // A more "normal" deletion 1.1183 + res = DeleteNode(cell); 1.1184 + NS_ENSURE_SUCCESS(res, res); 1.1185 + 1.1186 + //Skip over any rows spanned by this cell 1.1187 + rowIndex += actualRowSpan; 1.1188 + } 1.1189 + } 1.1190 + } 1.1191 + } while (cell); 1.1192 + 1.1193 + return NS_OK; 1.1194 +} 1.1195 + 1.1196 +NS_IMETHODIMP 1.1197 +nsHTMLEditor::DeleteTableRow(int32_t aNumber) 1.1198 +{ 1.1199 + nsCOMPtr<nsISelection> selection; 1.1200 + nsCOMPtr<nsIDOMElement> table; 1.1201 + nsCOMPtr<nsIDOMElement> cell; 1.1202 + int32_t startRowIndex, startColIndex; 1.1203 + int32_t rowCount, colCount; 1.1204 + nsresult res = GetCellContext(getter_AddRefs(selection), 1.1205 + getter_AddRefs(table), 1.1206 + getter_AddRefs(cell), 1.1207 + nullptr, nullptr, 1.1208 + &startRowIndex, &startColIndex); 1.1209 + NS_ENSURE_SUCCESS(res, res); 1.1210 + // Don't fail if no cell found 1.1211 + NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.1212 + 1.1213 + res = GetTableSize(table, &rowCount, &colCount); 1.1214 + NS_ENSURE_SUCCESS(res, res); 1.1215 + 1.1216 + // Shortcut the case of deleting all rows in table 1.1217 + if(startRowIndex == 0 && aNumber >= rowCount) 1.1218 + return DeleteTable2(table, selection); 1.1219 + 1.1220 + nsAutoEditBatch beginBatching(this); 1.1221 + // Prevent rules testing until we're done 1.1222 + nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); 1.1223 + 1.1224 + nsCOMPtr<nsIDOMElement> firstCell; 1.1225 + nsCOMPtr<nsIDOMRange> range; 1.1226 + res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell)); 1.1227 + NS_ENSURE_SUCCESS(res, res); 1.1228 + 1.1229 + int32_t rangeCount; 1.1230 + res = selection->GetRangeCount(&rangeCount); 1.1231 + NS_ENSURE_SUCCESS(res, res); 1.1232 + 1.1233 + if (firstCell && rangeCount > 1) 1.1234 + { 1.1235 + // Fetch indexes again - may be different for selected cells 1.1236 + res = GetCellIndexes(firstCell, &startRowIndex, &startColIndex); 1.1237 + NS_ENSURE_SUCCESS(res, res); 1.1238 + } 1.1239 + 1.1240 + //We control selection resetting after the insert... 1.1241 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false); 1.1242 + // Don't change selection during deletions 1.1243 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.1244 + 1.1245 + if (firstCell && rangeCount > 1) 1.1246 + { 1.1247 + // Use selected cells to determine what rows to delete 1.1248 + cell = firstCell; 1.1249 + 1.1250 + while (cell) 1.1251 + { 1.1252 + if (cell != firstCell) 1.1253 + { 1.1254 + res = GetCellIndexes(cell, &startRowIndex, &startColIndex); 1.1255 + NS_ENSURE_SUCCESS(res, res); 1.1256 + } 1.1257 + // Find the next cell in a different row 1.1258 + // to continue after we delete this row 1.1259 + int32_t nextRow = startRowIndex; 1.1260 + while (nextRow == startRowIndex) 1.1261 + { 1.1262 + res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); 1.1263 + NS_ENSURE_SUCCESS(res, res); 1.1264 + if (!cell) break; 1.1265 + res = GetCellIndexes(cell, &nextRow, &startColIndex); 1.1266 + NS_ENSURE_SUCCESS(res, res); 1.1267 + } 1.1268 + // Delete entire row 1.1269 + res = DeleteRow(table, startRowIndex); 1.1270 + NS_ENSURE_SUCCESS(res, res); 1.1271 + } 1.1272 + } 1.1273 + else 1.1274 + { 1.1275 + // Check for counts too high 1.1276 + aNumber = std::min(aNumber,(rowCount-startRowIndex)); 1.1277 + 1.1278 + for (int32_t i = 0; i < aNumber; i++) 1.1279 + { 1.1280 + res = DeleteRow(table, startRowIndex); 1.1281 + // If failed in current row, try the next 1.1282 + if (NS_FAILED(res)) 1.1283 + startRowIndex++; 1.1284 + 1.1285 + // Check if there's a cell in the "next" row 1.1286 + res = GetCellAt(table, startRowIndex, startColIndex, getter_AddRefs(cell)); 1.1287 + NS_ENSURE_SUCCESS(res, res); 1.1288 + if(!cell) 1.1289 + break; 1.1290 + } 1.1291 + } 1.1292 + return NS_OK; 1.1293 +} 1.1294 + 1.1295 +// Helper that doesn't batch or change the selection 1.1296 +NS_IMETHODIMP 1.1297 +nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, int32_t aRowIndex) 1.1298 +{ 1.1299 + NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); 1.1300 + 1.1301 + nsCOMPtr<nsIDOMElement> cell; 1.1302 + nsCOMPtr<nsIDOMElement> cellInDeleteRow; 1.1303 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.1304 + bool isSelected; 1.1305 + int32_t colIndex = 0; 1.1306 + nsresult res = NS_OK; 1.1307 + 1.1308 + // Prevent rules testing until we're done 1.1309 + nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); 1.1310 + 1.1311 + // The list of cells we will change rowspan in 1.1312 + // and the new rowspan values for each 1.1313 + nsTArray<nsCOMPtr<nsIDOMElement> > spanCellList; 1.1314 + nsTArray<int32_t> newSpanList; 1.1315 + 1.1316 + int32_t rowCount, colCount; 1.1317 + res = GetTableSize(aTable, &rowCount, &colCount); 1.1318 + NS_ENSURE_SUCCESS(res, res); 1.1319 + 1.1320 + // Scan through cells in row to do rowspan adjustments 1.1321 + // Note that after we delete row, startRowIndex will point to the 1.1322 + // cells in the next row to be deleted 1.1323 + do { 1.1324 + if (aRowIndex >= rowCount || colIndex >= colCount) 1.1325 + break; 1.1326 + 1.1327 + res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), 1.1328 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.1329 + &actualRowSpan, &actualColSpan, &isSelected); 1.1330 + 1.1331 + // We don't fail if we don't find a cell, so this must be real bad 1.1332 + if(NS_FAILED(res)) return res; 1.1333 + 1.1334 + // Compensate for cells that don't start or extend below the row we are deleting 1.1335 + if (cell) 1.1336 + { 1.1337 + if (startRowIndex < aRowIndex) 1.1338 + { 1.1339 + // Cell starts in row above us 1.1340 + // Decrease its rowspan to keep table rectangular 1.1341 + // but we don't need to do this if rowspan=0, 1.1342 + // since it will automatically adjust 1.1343 + if (rowSpan > 0) 1.1344 + { 1.1345 + // Build list of cells to change rowspan 1.1346 + // We can't do it now since it upsets cell map, 1.1347 + // so we will do it after deleting the row 1.1348 + spanCellList.AppendElement(cell); 1.1349 + newSpanList.AppendElement(std::max((aRowIndex - startRowIndex), actualRowSpan-1)); 1.1350 + } 1.1351 + } 1.1352 + else 1.1353 + { 1.1354 + if (rowSpan > 1) 1.1355 + { 1.1356 + //Cell spans below row to delete, 1.1357 + // so we must insert new cells to keep rows below even 1.1358 + // Note that we test "rowSpan" so we don't do this if rowSpan = 0 (automatic readjustment) 1.1359 + res = SplitCellIntoRows(aTable, startRowIndex, startColIndex, 1.1360 + aRowIndex - startRowIndex + 1, // The row above the row to insert new cell into 1.1361 + actualRowSpan - 1, nullptr); // Span remaining below 1.1362 + NS_ENSURE_SUCCESS(res, res); 1.1363 + } 1.1364 + if (!cellInDeleteRow) 1.1365 + cellInDeleteRow = cell; // Reference cell to find row to delete 1.1366 + } 1.1367 + // Skip over other columns spanned by this cell 1.1368 + colIndex += actualColSpan; 1.1369 + } 1.1370 + } while (cell); 1.1371 + 1.1372 + // Things are messed up if we didn't find a cell in the row! 1.1373 + NS_ENSURE_TRUE(cellInDeleteRow, NS_ERROR_FAILURE); 1.1374 + 1.1375 + // Delete the entire row 1.1376 + nsCOMPtr<nsIDOMElement> parentRow; 1.1377 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow, getter_AddRefs(parentRow)); 1.1378 + NS_ENSURE_SUCCESS(res, res); 1.1379 + 1.1380 + if (parentRow) 1.1381 + { 1.1382 + res = DeleteNode(parentRow); 1.1383 + NS_ENSURE_SUCCESS(res, res); 1.1384 + } 1.1385 + 1.1386 + // Now we can set new rowspans for cells stored above 1.1387 + for (uint32_t i = 0, n = spanCellList.Length(); i < n; i++) 1.1388 + { 1.1389 + nsIDOMElement *cellPtr = spanCellList[i]; 1.1390 + if (cellPtr) 1.1391 + { 1.1392 + res = SetRowSpan(cellPtr, newSpanList[i]); 1.1393 + NS_ENSURE_SUCCESS(res, res); 1.1394 + } 1.1395 + } 1.1396 + return NS_OK; 1.1397 +} 1.1398 + 1.1399 + 1.1400 +NS_IMETHODIMP 1.1401 +nsHTMLEditor::SelectTable() 1.1402 +{ 1.1403 + nsCOMPtr<nsIDOMElement> table; 1.1404 + nsresult res = NS_ERROR_FAILURE; 1.1405 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); 1.1406 + NS_ENSURE_SUCCESS(res, res); 1.1407 + // Don't fail if we didn't find a table 1.1408 + NS_ENSURE_TRUE(table, NS_OK); 1.1409 + 1.1410 + res = ClearSelection(); 1.1411 + if (NS_SUCCEEDED(res)) 1.1412 + res = AppendNodeToSelectionAsRange(table); 1.1413 + 1.1414 + return res; 1.1415 +} 1.1416 + 1.1417 +NS_IMETHODIMP 1.1418 +nsHTMLEditor::SelectTableCell() 1.1419 +{ 1.1420 + nsCOMPtr<nsIDOMElement> cell; 1.1421 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); 1.1422 + NS_ENSURE_SUCCESS(res, res); 1.1423 + NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.1424 + 1.1425 + res = ClearSelection(); 1.1426 + if (NS_SUCCEEDED(res)) 1.1427 + res = AppendNodeToSelectionAsRange(cell); 1.1428 + 1.1429 + return res; 1.1430 +} 1.1431 + 1.1432 +NS_IMETHODIMP 1.1433 +nsHTMLEditor::SelectBlockOfCells(nsIDOMElement *aStartCell, nsIDOMElement *aEndCell) 1.1434 +{ 1.1435 + NS_ENSURE_TRUE(aStartCell && aEndCell, NS_ERROR_NULL_POINTER); 1.1436 + 1.1437 + nsCOMPtr<nsISelection> selection; 1.1438 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.1439 + NS_ENSURE_SUCCESS(res, res); 1.1440 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.1441 + 1.1442 + NS_NAMED_LITERAL_STRING(tableStr, "table"); 1.1443 + nsCOMPtr<nsIDOMElement> table; 1.1444 + res = GetElementOrParentByTagName(tableStr, aStartCell, getter_AddRefs(table)); 1.1445 + NS_ENSURE_SUCCESS(res, res); 1.1446 + NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); 1.1447 + 1.1448 + nsCOMPtr<nsIDOMElement> endTable; 1.1449 + res = GetElementOrParentByTagName(tableStr, aEndCell, getter_AddRefs(endTable)); 1.1450 + NS_ENSURE_SUCCESS(res, res); 1.1451 + NS_ENSURE_TRUE(endTable, NS_ERROR_FAILURE); 1.1452 + 1.1453 + // We can only select a block if within the same table, 1.1454 + // so do nothing if not within one table 1.1455 + if (table != endTable) return NS_OK; 1.1456 + 1.1457 + int32_t startRowIndex, startColIndex, endRowIndex, endColIndex; 1.1458 + 1.1459 + // Get starting and ending cells' location in the cellmap 1.1460 + res = GetCellIndexes(aStartCell, &startRowIndex, &startColIndex); 1.1461 + if(NS_FAILED(res)) return res; 1.1462 + 1.1463 + res = GetCellIndexes(aEndCell, &endRowIndex, &endColIndex); 1.1464 + if(NS_FAILED(res)) return res; 1.1465 + 1.1466 + // Suppress nsISelectionListener notification 1.1467 + // until all selection changes are finished 1.1468 + nsSelectionBatcherForTable selectionBatcher(selection); 1.1469 + 1.1470 + // Examine all cell nodes in current selection and 1.1471 + // remove those outside the new block cell region 1.1472 + int32_t minColumn = std::min(startColIndex, endColIndex); 1.1473 + int32_t minRow = std::min(startRowIndex, endRowIndex); 1.1474 + int32_t maxColumn = std::max(startColIndex, endColIndex); 1.1475 + int32_t maxRow = std::max(startRowIndex, endRowIndex); 1.1476 + 1.1477 + nsCOMPtr<nsIDOMElement> cell; 1.1478 + int32_t currentRowIndex, currentColIndex; 1.1479 + nsCOMPtr<nsIDOMRange> range; 1.1480 + res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); 1.1481 + NS_ENSURE_SUCCESS(res, res); 1.1482 + if (res == NS_EDITOR_ELEMENT_NOT_FOUND) return NS_OK; 1.1483 + 1.1484 + while (cell) 1.1485 + { 1.1486 + res = GetCellIndexes(cell, ¤tRowIndex, ¤tColIndex); 1.1487 + NS_ENSURE_SUCCESS(res, res); 1.1488 + 1.1489 + if (currentRowIndex < maxRow || currentRowIndex > maxRow || 1.1490 + currentColIndex < maxColumn || currentColIndex > maxColumn) 1.1491 + { 1.1492 + selection->RemoveRange(range); 1.1493 + // Since we've removed the range, decrement pointer to next range 1.1494 + mSelectedCellIndex--; 1.1495 + } 1.1496 + res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); 1.1497 + NS_ENSURE_SUCCESS(res, res); 1.1498 + } 1.1499 + 1.1500 + int32_t rowSpan, colSpan, actualRowSpan, actualColSpan; 1.1501 + bool isSelected; 1.1502 + for (int32_t row = minRow; row <= maxRow; row++) 1.1503 + { 1.1504 + for(int32_t col = minColumn; col <= maxColumn; col += std::max(actualColSpan, 1)) 1.1505 + { 1.1506 + res = GetCellDataAt(table, row, col, getter_AddRefs(cell), 1.1507 + ¤tRowIndex, ¤tColIndex, 1.1508 + &rowSpan, &colSpan, 1.1509 + &actualRowSpan, &actualColSpan, &isSelected); 1.1510 + if (NS_FAILED(res)) break; 1.1511 + // Skip cells that already selected or are spanned from previous locations 1.1512 + if (!isSelected && cell && row == currentRowIndex && col == currentColIndex) 1.1513 + { 1.1514 + res = AppendNodeToSelectionAsRange(cell); 1.1515 + if (NS_FAILED(res)) break; 1.1516 + } 1.1517 + } 1.1518 + } 1.1519 + return res; 1.1520 +} 1.1521 + 1.1522 +NS_IMETHODIMP 1.1523 +nsHTMLEditor::SelectAllTableCells() 1.1524 +{ 1.1525 + nsCOMPtr<nsIDOMElement> cell; 1.1526 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); 1.1527 + NS_ENSURE_SUCCESS(res, res); 1.1528 + 1.1529 + // Don't fail if we didn't find a cell 1.1530 + NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.1531 + 1.1532 + nsCOMPtr<nsIDOMElement> startCell = cell; 1.1533 + 1.1534 + // Get parent table 1.1535 + nsCOMPtr<nsIDOMElement> table; 1.1536 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell, getter_AddRefs(table)); 1.1537 + NS_ENSURE_SUCCESS(res, res); 1.1538 + if(!table) return NS_ERROR_NULL_POINTER; 1.1539 + 1.1540 + int32_t rowCount, colCount; 1.1541 + res = GetTableSize(table, &rowCount, &colCount); 1.1542 + NS_ENSURE_SUCCESS(res, res); 1.1543 + 1.1544 + nsCOMPtr<nsISelection> selection; 1.1545 + res = GetSelection(getter_AddRefs(selection)); 1.1546 + NS_ENSURE_SUCCESS(res, res); 1.1547 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.1548 + 1.1549 + // Suppress nsISelectionListener notification 1.1550 + // until all selection changes are finished 1.1551 + nsSelectionBatcherForTable selectionBatcher(selection); 1.1552 + 1.1553 + // It is now safe to clear the selection 1.1554 + // BE SURE TO RESET IT BEFORE LEAVING! 1.1555 + res = ClearSelection(); 1.1556 + 1.1557 + // Select all cells in the same column as current cell 1.1558 + bool cellSelected = false; 1.1559 + int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; 1.1560 + bool isSelected; 1.1561 + for(int32_t row = 0; row < rowCount; row++) 1.1562 + { 1.1563 + for(int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) 1.1564 + { 1.1565 + res = GetCellDataAt(table, row, col, getter_AddRefs(cell), 1.1566 + ¤tRowIndex, ¤tColIndex, 1.1567 + &rowSpan, &colSpan, 1.1568 + &actualRowSpan, &actualColSpan, &isSelected); 1.1569 + if (NS_FAILED(res)) break; 1.1570 + // Skip cells that are spanned from previous rows or columns 1.1571 + if (cell && row == currentRowIndex && col == currentColIndex) 1.1572 + { 1.1573 + res = AppendNodeToSelectionAsRange(cell); 1.1574 + if (NS_FAILED(res)) break; 1.1575 + cellSelected = true; 1.1576 + } 1.1577 + } 1.1578 + } 1.1579 + // Safety code to select starting cell if nothing else was selected 1.1580 + if (!cellSelected) 1.1581 + { 1.1582 + return AppendNodeToSelectionAsRange(startCell); 1.1583 + } 1.1584 + return res; 1.1585 +} 1.1586 + 1.1587 +NS_IMETHODIMP 1.1588 +nsHTMLEditor::SelectTableRow() 1.1589 +{ 1.1590 + nsCOMPtr<nsIDOMElement> cell; 1.1591 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); 1.1592 + NS_ENSURE_SUCCESS(res, res); 1.1593 + 1.1594 + // Don't fail if we didn't find a cell 1.1595 + NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.1596 + nsCOMPtr<nsIDOMElement> startCell = cell; 1.1597 + 1.1598 + // Get table and location of cell: 1.1599 + nsCOMPtr<nsISelection> selection; 1.1600 + nsCOMPtr<nsIDOMElement> table; 1.1601 + int32_t startRowIndex, startColIndex; 1.1602 + 1.1603 + res = GetCellContext(getter_AddRefs(selection), 1.1604 + getter_AddRefs(table), 1.1605 + getter_AddRefs(cell), 1.1606 + nullptr, nullptr, 1.1607 + &startRowIndex, &startColIndex); 1.1608 + NS_ENSURE_SUCCESS(res, res); 1.1609 + NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); 1.1610 + 1.1611 + int32_t rowCount, colCount; 1.1612 + res = GetTableSize(table, &rowCount, &colCount); 1.1613 + NS_ENSURE_SUCCESS(res, res); 1.1614 + 1.1615 + //Note: At this point, we could get first and last cells in row, 1.1616 + // then call SelectBlockOfCells, but that would take just 1.1617 + // a little less code, so the following is more efficient 1.1618 + 1.1619 + // Suppress nsISelectionListener notification 1.1620 + // until all selection changes are finished 1.1621 + nsSelectionBatcherForTable selectionBatcher(selection); 1.1622 + 1.1623 + // It is now safe to clear the selection 1.1624 + // BE SURE TO RESET IT BEFORE LEAVING! 1.1625 + res = ClearSelection(); 1.1626 + 1.1627 + // Select all cells in the same row as current cell 1.1628 + bool cellSelected = false; 1.1629 + int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; 1.1630 + bool isSelected; 1.1631 + for(int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) 1.1632 + { 1.1633 + res = GetCellDataAt(table, startRowIndex, col, getter_AddRefs(cell), 1.1634 + ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, 1.1635 + &actualRowSpan, &actualColSpan, &isSelected); 1.1636 + if (NS_FAILED(res)) break; 1.1637 + // Skip cells that are spanned from previous rows or columns 1.1638 + if (cell && currentRowIndex == startRowIndex && currentColIndex == col) 1.1639 + { 1.1640 + res = AppendNodeToSelectionAsRange(cell); 1.1641 + if (NS_FAILED(res)) break; 1.1642 + cellSelected = true; 1.1643 + } 1.1644 + } 1.1645 + // Safety code to select starting cell if nothing else was selected 1.1646 + if (!cellSelected) 1.1647 + { 1.1648 + return AppendNodeToSelectionAsRange(startCell); 1.1649 + } 1.1650 + return res; 1.1651 +} 1.1652 + 1.1653 +NS_IMETHODIMP 1.1654 +nsHTMLEditor::SelectTableColumn() 1.1655 +{ 1.1656 + nsCOMPtr<nsIDOMElement> cell; 1.1657 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); 1.1658 + NS_ENSURE_SUCCESS(res, res); 1.1659 + 1.1660 + // Don't fail if we didn't find a cell 1.1661 + NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.1662 + 1.1663 + nsCOMPtr<nsIDOMElement> startCell = cell; 1.1664 + 1.1665 + // Get location of cell: 1.1666 + nsCOMPtr<nsISelection> selection; 1.1667 + nsCOMPtr<nsIDOMElement> table; 1.1668 + int32_t startRowIndex, startColIndex; 1.1669 + 1.1670 + res = GetCellContext(getter_AddRefs(selection), 1.1671 + getter_AddRefs(table), 1.1672 + getter_AddRefs(cell), 1.1673 + nullptr, nullptr, 1.1674 + &startRowIndex, &startColIndex); 1.1675 + NS_ENSURE_SUCCESS(res, res); 1.1676 + NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); 1.1677 + 1.1678 + int32_t rowCount, colCount; 1.1679 + res = GetTableSize(table, &rowCount, &colCount); 1.1680 + NS_ENSURE_SUCCESS(res, res); 1.1681 + 1.1682 + // Suppress nsISelectionListener notification 1.1683 + // until all selection changes are finished 1.1684 + nsSelectionBatcherForTable selectionBatcher(selection); 1.1685 + 1.1686 + // It is now safe to clear the selection 1.1687 + // BE SURE TO RESET IT BEFORE LEAVING! 1.1688 + res = ClearSelection(); 1.1689 + 1.1690 + // Select all cells in the same column as current cell 1.1691 + bool cellSelected = false; 1.1692 + int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; 1.1693 + bool isSelected; 1.1694 + for(int32_t row = 0; row < rowCount; row += std::max(actualRowSpan, 1)) 1.1695 + { 1.1696 + res = GetCellDataAt(table, row, startColIndex, getter_AddRefs(cell), 1.1697 + ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, 1.1698 + &actualRowSpan, &actualColSpan, &isSelected); 1.1699 + if (NS_FAILED(res)) break; 1.1700 + // Skip cells that are spanned from previous rows or columns 1.1701 + if (cell && currentRowIndex == row && currentColIndex == startColIndex) 1.1702 + { 1.1703 + res = AppendNodeToSelectionAsRange(cell); 1.1704 + if (NS_FAILED(res)) break; 1.1705 + cellSelected = true; 1.1706 + } 1.1707 + } 1.1708 + // Safety code to select starting cell if nothing else was selected 1.1709 + if (!cellSelected) 1.1710 + { 1.1711 + return AppendNodeToSelectionAsRange(startCell); 1.1712 + } 1.1713 + return res; 1.1714 +} 1.1715 + 1.1716 +NS_IMETHODIMP 1.1717 +nsHTMLEditor::SplitTableCell() 1.1718 +{ 1.1719 + nsCOMPtr<nsIDOMElement> table; 1.1720 + nsCOMPtr<nsIDOMElement> cell; 1.1721 + int32_t startRowIndex, startColIndex, actualRowSpan, actualColSpan; 1.1722 + // Get cell, table, etc. at selection anchor node 1.1723 + nsresult res = GetCellContext(nullptr, 1.1724 + getter_AddRefs(table), 1.1725 + getter_AddRefs(cell), 1.1726 + nullptr, nullptr, 1.1727 + &startRowIndex, &startColIndex); 1.1728 + NS_ENSURE_SUCCESS(res, res); 1.1729 + if(!table || !cell) return NS_EDITOR_ELEMENT_NOT_FOUND; 1.1730 + 1.1731 + // We need rowspan and colspan data 1.1732 + res = GetCellSpansAt(table, startRowIndex, startColIndex, actualRowSpan, actualColSpan); 1.1733 + NS_ENSURE_SUCCESS(res, res); 1.1734 + 1.1735 + // Must have some span to split 1.1736 + if (actualRowSpan <= 1 && actualColSpan <= 1) 1.1737 + return NS_OK; 1.1738 + 1.1739 + nsAutoEditBatch beginBatching(this); 1.1740 + // Prevent auto insertion of BR in new cell until we're done 1.1741 + nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); 1.1742 + 1.1743 + // We reset selection 1.1744 + nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false); 1.1745 + //...so suppress Rules System selection munging 1.1746 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.1747 + 1.1748 + nsCOMPtr<nsIDOMElement> newCell; 1.1749 + int32_t rowIndex = startRowIndex; 1.1750 + int32_t rowSpanBelow, colSpanAfter; 1.1751 + 1.1752 + // Split up cell row-wise first into rowspan=1 above, and the rest below, 1.1753 + // whittling away at the cell below until no more extra span 1.1754 + for (rowSpanBelow = actualRowSpan-1; rowSpanBelow >= 0; rowSpanBelow--) 1.1755 + { 1.1756 + // We really split row-wise only if we had rowspan > 1 1.1757 + if (rowSpanBelow > 0) 1.1758 + { 1.1759 + res = SplitCellIntoRows(table, rowIndex, startColIndex, 1, rowSpanBelow, getter_AddRefs(newCell)); 1.1760 + NS_ENSURE_SUCCESS(res, res); 1.1761 + CopyCellBackgroundColor(newCell, cell); 1.1762 + } 1.1763 + int32_t colIndex = startColIndex; 1.1764 + // Now split the cell with rowspan = 1 into cells if it has colSpan > 1 1.1765 + for (colSpanAfter = actualColSpan-1; colSpanAfter > 0; colSpanAfter--) 1.1766 + { 1.1767 + res = SplitCellIntoColumns(table, rowIndex, colIndex, 1, colSpanAfter, getter_AddRefs(newCell)); 1.1768 + NS_ENSURE_SUCCESS(res, res); 1.1769 + CopyCellBackgroundColor(newCell, cell); 1.1770 + colIndex++; 1.1771 + } 1.1772 + // Point to the new cell and repeat 1.1773 + rowIndex++; 1.1774 + } 1.1775 + return res; 1.1776 +} 1.1777 + 1.1778 +nsresult 1.1779 +nsHTMLEditor::CopyCellBackgroundColor(nsIDOMElement *destCell, nsIDOMElement *sourceCell) 1.1780 +{ 1.1781 + NS_ENSURE_TRUE(destCell && sourceCell, NS_ERROR_NULL_POINTER); 1.1782 + 1.1783 + // Copy backgournd color to new cell 1.1784 + NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor"); 1.1785 + nsAutoString color; 1.1786 + bool isSet; 1.1787 + nsresult res = GetAttributeValue(sourceCell, bgcolor, color, &isSet); 1.1788 + 1.1789 + if (NS_SUCCEEDED(res) && isSet) 1.1790 + res = SetAttribute(destCell, bgcolor, color); 1.1791 + 1.1792 + return res; 1.1793 +} 1.1794 + 1.1795 +NS_IMETHODIMP 1.1796 +nsHTMLEditor::SplitCellIntoColumns(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aColIndex, 1.1797 + int32_t aColSpanLeft, int32_t aColSpanRight, 1.1798 + nsIDOMElement **aNewCell) 1.1799 +{ 1.1800 + NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); 1.1801 + if (aNewCell) *aNewCell = nullptr; 1.1802 + 1.1803 + nsCOMPtr<nsIDOMElement> cell; 1.1804 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.1805 + bool isSelected; 1.1806 + nsresult res = GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell), 1.1807 + &startRowIndex, &startColIndex, 1.1808 + &rowSpan, &colSpan, 1.1809 + &actualRowSpan, &actualColSpan, &isSelected); 1.1810 + NS_ENSURE_SUCCESS(res, res); 1.1811 + NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER); 1.1812 + 1.1813 + // We can't split! 1.1814 + if (actualColSpan <= 1 || (aColSpanLeft + aColSpanRight) > actualColSpan) 1.1815 + return NS_OK; 1.1816 + 1.1817 + // Reduce colspan of cell to split 1.1818 + res = SetColSpan(cell, aColSpanLeft); 1.1819 + NS_ENSURE_SUCCESS(res, res); 1.1820 + 1.1821 + // Insert new cell after using the remaining span 1.1822 + // and always get the new cell so we can copy the background color; 1.1823 + nsCOMPtr<nsIDOMElement> newCell; 1.1824 + res = InsertCell(cell, actualRowSpan, aColSpanRight, true, false, getter_AddRefs(newCell)); 1.1825 + NS_ENSURE_SUCCESS(res, res); 1.1826 + if (newCell) 1.1827 + { 1.1828 + if (aNewCell) 1.1829 + { 1.1830 + *aNewCell = newCell.get(); 1.1831 + NS_ADDREF(*aNewCell); 1.1832 + } 1.1833 + res = CopyCellBackgroundColor(newCell, cell); 1.1834 + } 1.1835 + return res; 1.1836 +} 1.1837 + 1.1838 +NS_IMETHODIMP 1.1839 +nsHTMLEditor::SplitCellIntoRows(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aColIndex, 1.1840 + int32_t aRowSpanAbove, int32_t aRowSpanBelow, 1.1841 + nsIDOMElement **aNewCell) 1.1842 +{ 1.1843 + NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); 1.1844 + if (aNewCell) *aNewCell = nullptr; 1.1845 + 1.1846 + nsCOMPtr<nsIDOMElement> cell; 1.1847 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.1848 + bool isSelected; 1.1849 + nsresult res = GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell), 1.1850 + &startRowIndex, &startColIndex, 1.1851 + &rowSpan, &colSpan, 1.1852 + &actualRowSpan, &actualColSpan, &isSelected); 1.1853 + NS_ENSURE_SUCCESS(res, res); 1.1854 + NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER); 1.1855 + 1.1856 + // We can't split! 1.1857 + if (actualRowSpan <= 1 || (aRowSpanAbove + aRowSpanBelow) > actualRowSpan) 1.1858 + return NS_OK; 1.1859 + 1.1860 + int32_t rowCount, colCount; 1.1861 + res = GetTableSize(aTable, &rowCount, &colCount); 1.1862 + NS_ENSURE_SUCCESS(res, res); 1.1863 + 1.1864 + nsCOMPtr<nsIDOMElement> cell2; 1.1865 + nsCOMPtr<nsIDOMElement> lastCellFound; 1.1866 + int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2; 1.1867 + bool isSelected2; 1.1868 + int32_t colIndex = 0; 1.1869 + bool insertAfter = (startColIndex > 0); 1.1870 + // This is the row we will insert new cell into 1.1871 + int32_t rowBelowIndex = startRowIndex+aRowSpanAbove; 1.1872 + 1.1873 + // Find a cell to insert before or after 1.1874 + do 1.1875 + { 1.1876 + // Search for a cell to insert before 1.1877 + res = GetCellDataAt(aTable, rowBelowIndex, 1.1878 + colIndex, getter_AddRefs(cell2), 1.1879 + &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2, 1.1880 + &actualRowSpan2, &actualColSpan2, &isSelected2); 1.1881 + // If we fail here, it could be because row has bad rowspan values, 1.1882 + // such as all cells having rowspan > 1 (Call FixRowSpan first!) 1.1883 + if (NS_FAILED(res) || !cell) return NS_ERROR_FAILURE; 1.1884 + 1.1885 + // Skip over cells spanned from above (like the one we are splitting!) 1.1886 + if (cell2 && startRowIndex2 == rowBelowIndex) 1.1887 + { 1.1888 + if (insertAfter) 1.1889 + { 1.1890 + // New cell isn't first in row, 1.1891 + // so stop after we find the cell just before new cell's column 1.1892 + if ((startColIndex2 + actualColSpan2) == startColIndex) 1.1893 + break; 1.1894 + 1.1895 + // If cell found is AFTER desired new cell colum, 1.1896 + // we have multiple cells with rowspan > 1 that 1.1897 + // prevented us from finding a cell to insert after... 1.1898 + if (startColIndex2 > startColIndex) 1.1899 + { 1.1900 + // ... so instead insert before the cell we found 1.1901 + insertAfter = false; 1.1902 + break; 1.1903 + } 1.1904 + } 1.1905 + else 1.1906 + { 1.1907 + break; // Inserting before, so stop at first cell in row we want to insert into 1.1908 + } 1.1909 + lastCellFound = cell2; 1.1910 + } 1.1911 + // Skip to next available cellmap location 1.1912 + colIndex += std::max(actualColSpan2, 1); 1.1913 + 1.1914 + // Done when past end of total number of columns 1.1915 + if (colIndex > colCount) 1.1916 + break; 1.1917 + 1.1918 + } while(true); 1.1919 + 1.1920 + if (!cell2 && lastCellFound) 1.1921 + { 1.1922 + // Edge case where we didn't find a cell to insert after 1.1923 + // or before because column(s) before desired column 1.1924 + // and all columns after it are spanned from above. 1.1925 + // We can insert after the last cell we found 1.1926 + cell2 = lastCellFound; 1.1927 + insertAfter = true; // Should always be true, but let's be sure 1.1928 + } 1.1929 + 1.1930 + // Reduce rowspan of cell to split 1.1931 + res = SetRowSpan(cell, aRowSpanAbove); 1.1932 + NS_ENSURE_SUCCESS(res, res); 1.1933 + 1.1934 + 1.1935 + // Insert new cell after using the remaining span 1.1936 + // and always get the new cell so we can copy the background color; 1.1937 + nsCOMPtr<nsIDOMElement> newCell; 1.1938 + res = InsertCell(cell2, aRowSpanBelow, actualColSpan, insertAfter, false, getter_AddRefs(newCell)); 1.1939 + NS_ENSURE_SUCCESS(res, res); 1.1940 + if (newCell) 1.1941 + { 1.1942 + if (aNewCell) 1.1943 + { 1.1944 + *aNewCell = newCell.get(); 1.1945 + NS_ADDREF(*aNewCell); 1.1946 + } 1.1947 + res = CopyCellBackgroundColor(newCell, cell2); 1.1948 + } 1.1949 + return res; 1.1950 +} 1.1951 + 1.1952 +NS_IMETHODIMP 1.1953 +nsHTMLEditor::SwitchTableCellHeaderType(nsIDOMElement *aSourceCell, nsIDOMElement **aNewCell) 1.1954 +{ 1.1955 + NS_ENSURE_TRUE(aSourceCell, NS_ERROR_NULL_POINTER); 1.1956 + 1.1957 + nsAutoEditBatch beginBatching(this); 1.1958 + // Prevent auto insertion of BR in new cell created by ReplaceContainer 1.1959 + nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); 1.1960 + 1.1961 + nsCOMPtr<nsIDOMNode> newNode; 1.1962 + 1.1963 + // Save current selection to restore when done 1.1964 + // This is needed so ReplaceContainer can monitor selection 1.1965 + // when replacing nodes 1.1966 + nsRefPtr<Selection> selection = GetSelection(); 1.1967 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.1968 + nsAutoSelectionReset selectionResetter(selection, this); 1.1969 + 1.1970 + // Set to the opposite of current type 1.1971 + nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(aSourceCell); 1.1972 + nsString newCellType( (atom == nsEditProperty::td) ? NS_LITERAL_STRING("th") : NS_LITERAL_STRING("td")); 1.1973 + 1.1974 + // This creates new node, moves children, copies attributes (true) 1.1975 + // and manages the selection! 1.1976 + nsresult res = ReplaceContainer(aSourceCell, address_of(newNode), 1.1977 + newCellType, nullptr, nullptr, true); 1.1978 + NS_ENSURE_SUCCESS(res, res); 1.1979 + NS_ENSURE_TRUE(newNode, NS_ERROR_FAILURE); 1.1980 + 1.1981 + // Return the new cell 1.1982 + if (aNewCell) 1.1983 + { 1.1984 + nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode); 1.1985 + *aNewCell = newElement.get(); 1.1986 + NS_ADDREF(*aNewCell); 1.1987 + } 1.1988 + 1.1989 + return NS_OK; 1.1990 +} 1.1991 + 1.1992 +NS_IMETHODIMP 1.1993 +nsHTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) 1.1994 +{ 1.1995 + nsCOMPtr<nsIDOMElement> table; 1.1996 + nsCOMPtr<nsIDOMElement> targetCell; 1.1997 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.1998 + bool isSelected; 1.1999 + nsCOMPtr<nsIDOMElement> cell2; 1.2000 + int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2; 1.2001 + bool isSelected2; 1.2002 + 1.2003 + // Get cell, table, etc. at selection anchor node 1.2004 + nsresult res = GetCellContext(nullptr, 1.2005 + getter_AddRefs(table), 1.2006 + getter_AddRefs(targetCell), 1.2007 + nullptr, nullptr, 1.2008 + &startRowIndex, &startColIndex); 1.2009 + NS_ENSURE_SUCCESS(res, res); 1.2010 + if(!table || !targetCell) return NS_EDITOR_ELEMENT_NOT_FOUND; 1.2011 + 1.2012 + nsAutoEditBatch beginBatching(this); 1.2013 + //Don't let Rules System change the selection 1.2014 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.2015 + 1.2016 + // Note: We dont' use nsSetSelectionAfterTableEdit here so the selection 1.2017 + // is retained after joining. This leaves the target cell selected 1.2018 + // as well as the "non-contiguous" cells, so user can see what happened. 1.2019 + 1.2020 + nsCOMPtr<nsIDOMElement> firstCell; 1.2021 + int32_t firstRowIndex, firstColIndex; 1.2022 + res = GetFirstSelectedCellInTable(&firstRowIndex, &firstColIndex, getter_AddRefs(firstCell)); 1.2023 + NS_ENSURE_SUCCESS(res, res); 1.2024 + 1.2025 + bool joinSelectedCells = false; 1.2026 + if (firstCell) 1.2027 + { 1.2028 + nsCOMPtr<nsIDOMElement> secondCell; 1.2029 + res = GetNextSelectedCell(nullptr, getter_AddRefs(secondCell)); 1.2030 + NS_ENSURE_SUCCESS(res, res); 1.2031 + 1.2032 + // If only one cell is selected, join with cell to the right 1.2033 + joinSelectedCells = (secondCell != nullptr); 1.2034 + } 1.2035 + 1.2036 + if (joinSelectedCells) 1.2037 + { 1.2038 + // We have selected cells: Join just contiguous cells 1.2039 + // and just merge contents if not contiguous 1.2040 + 1.2041 + int32_t rowCount, colCount; 1.2042 + res = GetTableSize(table, &rowCount, &colCount); 1.2043 + NS_ENSURE_SUCCESS(res, res); 1.2044 + 1.2045 + // Get spans for cell we will merge into 1.2046 + int32_t firstRowSpan, firstColSpan; 1.2047 + res = GetCellSpansAt( table, firstRowIndex, firstColIndex, firstRowSpan, firstColSpan); 1.2048 + NS_ENSURE_SUCCESS(res, res); 1.2049 + 1.2050 + // This defines the last indexes along the "edges" 1.2051 + // of the contiguous block of cells, telling us 1.2052 + // that we can join adjacent cells to the block 1.2053 + // Start with same as the first values, 1.2054 + // then expand as we find adjacent selected cells 1.2055 + int32_t lastRowIndex = firstRowIndex; 1.2056 + int32_t lastColIndex = firstColIndex; 1.2057 + int32_t rowIndex, colIndex; 1.2058 + 1.2059 + // First pass: Determine boundaries of contiguous rectangular block 1.2060 + // that we will join into one cell, 1.2061 + // favoring adjacent cells in the same row 1.2062 + for (rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) 1.2063 + { 1.2064 + int32_t currentRowCount = rowCount; 1.2065 + // Be sure each row doesn't have rowspan errors 1.2066 + res = FixBadRowSpan(table, rowIndex, rowCount); 1.2067 + NS_ENSURE_SUCCESS(res, res); 1.2068 + // Adjust rowcount by number of rows we removed 1.2069 + lastRowIndex -= (currentRowCount-rowCount); 1.2070 + 1.2071 + bool cellFoundInRow = false; 1.2072 + bool lastRowIsSet = false; 1.2073 + int32_t lastColInRow = 0; 1.2074 + int32_t firstColInRow = firstColIndex; 1.2075 + for (colIndex = firstColIndex; colIndex < colCount; colIndex += std::max(actualColSpan2, 1)) 1.2076 + { 1.2077 + res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2), 1.2078 + &startRowIndex2, &startColIndex2, 1.2079 + &rowSpan2, &colSpan2, 1.2080 + &actualRowSpan2, &actualColSpan2, &isSelected2); 1.2081 + NS_ENSURE_SUCCESS(res, res); 1.2082 + 1.2083 + if (isSelected2) 1.2084 + { 1.2085 + if (!cellFoundInRow) 1.2086 + // We've just found the first selected cell in this row 1.2087 + firstColInRow = colIndex; 1.2088 + 1.2089 + if (rowIndex > firstRowIndex && firstColInRow != firstColIndex) 1.2090 + { 1.2091 + // We're in at least the second row, 1.2092 + // but left boundary is "ragged" (not the same as 1st row's start) 1.2093 + //Let's just end block on previous row 1.2094 + // and keep previous lastColIndex 1.2095 + //TODO: We could try to find the Maximum firstColInRow 1.2096 + // so our block can still extend down more rows? 1.2097 + lastRowIndex = std::max(0,rowIndex - 1); 1.2098 + lastRowIsSet = true; 1.2099 + break; 1.2100 + } 1.2101 + // Save max selected column in this row, including extra colspan 1.2102 + lastColInRow = colIndex + (actualColSpan2-1); 1.2103 + cellFoundInRow = true; 1.2104 + } 1.2105 + else if (cellFoundInRow) 1.2106 + { 1.2107 + // No cell or not selected, but at least one cell in row was found 1.2108 + 1.2109 + if (rowIndex > (firstRowIndex+1) && colIndex <= lastColIndex) 1.2110 + { 1.2111 + // Cell is in a column less than current right border in 1.2112 + // the third or higher selected row, so stop block at the previous row 1.2113 + lastRowIndex = std::max(0,rowIndex - 1); 1.2114 + lastRowIsSet = true; 1.2115 + } 1.2116 + // We're done with this row 1.2117 + break; 1.2118 + } 1.2119 + } // End of column loop 1.2120 + 1.2121 + // Done with this row 1.2122 + if (cellFoundInRow) 1.2123 + { 1.2124 + if (rowIndex == firstRowIndex) 1.2125 + { 1.2126 + // First row always initializes the right boundary 1.2127 + lastColIndex = lastColInRow; 1.2128 + } 1.2129 + 1.2130 + // If we didn't determine last row above... 1.2131 + if (!lastRowIsSet) 1.2132 + { 1.2133 + if (colIndex < lastColIndex) 1.2134 + { 1.2135 + // (don't think we ever get here?) 1.2136 + // Cell is in a column less than current right boundary, 1.2137 + // so stop block at the previous row 1.2138 + lastRowIndex = std::max(0,rowIndex - 1); 1.2139 + } 1.2140 + else 1.2141 + { 1.2142 + // Go on to examine next row 1.2143 + lastRowIndex = rowIndex+1; 1.2144 + } 1.2145 + } 1.2146 + // Use the minimum col we found so far for right boundary 1.2147 + lastColIndex = std::min(lastColIndex, lastColInRow); 1.2148 + } 1.2149 + else 1.2150 + { 1.2151 + // No selected cells in this row -- stop at row above 1.2152 + // and leave last column at its previous value 1.2153 + lastRowIndex = std::max(0,rowIndex - 1); 1.2154 + } 1.2155 + } 1.2156 + 1.2157 + // The list of cells we will delete after joining 1.2158 + nsTArray<nsCOMPtr<nsIDOMElement> > deleteList; 1.2159 + 1.2160 + // 2nd pass: Do the joining and merging 1.2161 + for (rowIndex = 0; rowIndex < rowCount; rowIndex++) 1.2162 + { 1.2163 + for (colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan2, 1)) 1.2164 + { 1.2165 + res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2), 1.2166 + &startRowIndex2, &startColIndex2, 1.2167 + &rowSpan2, &colSpan2, 1.2168 + &actualRowSpan2, &actualColSpan2, &isSelected2); 1.2169 + NS_ENSURE_SUCCESS(res, res); 1.2170 + 1.2171 + // If this is 0, we are past last cell in row, so exit the loop 1.2172 + if (actualColSpan2 == 0) 1.2173 + break; 1.2174 + 1.2175 + // Merge only selected cells (skip cell we're merging into, of course) 1.2176 + if (isSelected2 && cell2 != firstCell) 1.2177 + { 1.2178 + if (rowIndex >= firstRowIndex && rowIndex <= lastRowIndex && 1.2179 + colIndex >= firstColIndex && colIndex <= lastColIndex) 1.2180 + { 1.2181 + // We are within the join region 1.2182 + // Problem: It is very tricky to delete cells as we merge, 1.2183 + // since that will upset the cellmap 1.2184 + // Instead, build a list of cells to delete and do it later 1.2185 + NS_ASSERTION(startRowIndex2 == rowIndex, "JoinTableCells: StartRowIndex is in row above"); 1.2186 + 1.2187 + if (actualColSpan2 > 1) 1.2188 + { 1.2189 + //Check if cell "hangs" off the boundary because of colspan > 1 1.2190 + // Use split methods to chop off excess 1.2191 + int32_t extraColSpan = (startColIndex2 + actualColSpan2) - (lastColIndex+1); 1.2192 + if ( extraColSpan > 0) 1.2193 + { 1.2194 + res = SplitCellIntoColumns(table, startRowIndex2, startColIndex2, 1.2195 + actualColSpan2-extraColSpan, extraColSpan, nullptr); 1.2196 + NS_ENSURE_SUCCESS(res, res); 1.2197 + } 1.2198 + } 1.2199 + 1.2200 + res = MergeCells(firstCell, cell2, false); 1.2201 + NS_ENSURE_SUCCESS(res, res); 1.2202 + 1.2203 + // Add cell to list to delete 1.2204 + deleteList.AppendElement(cell2.get()); 1.2205 + } 1.2206 + else if (aMergeNonContiguousContents) 1.2207 + { 1.2208 + // Cell is outside join region -- just merge the contents 1.2209 + res = MergeCells(firstCell, cell2, false); 1.2210 + NS_ENSURE_SUCCESS(res, res); 1.2211 + } 1.2212 + } 1.2213 + } 1.2214 + } 1.2215 + 1.2216 + // All cell contents are merged. Delete the empty cells we accumulated 1.2217 + // Prevent rules testing until we're done 1.2218 + nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); 1.2219 + 1.2220 + for (uint32_t i = 0, n = deleteList.Length(); i < n; i++) 1.2221 + { 1.2222 + nsIDOMElement *elementPtr = deleteList[i]; 1.2223 + if (elementPtr) 1.2224 + { 1.2225 + nsCOMPtr<nsIDOMNode> node = do_QueryInterface(elementPtr); 1.2226 + res = DeleteNode(node); 1.2227 + NS_ENSURE_SUCCESS(res, res); 1.2228 + } 1.2229 + } 1.2230 + // Cleanup selection: remove ranges where cells were deleted 1.2231 + nsCOMPtr<nsISelection> selection; 1.2232 + res = GetSelection(getter_AddRefs(selection)); 1.2233 + NS_ENSURE_SUCCESS(res, res); 1.2234 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.2235 + 1.2236 + int32_t rangeCount; 1.2237 + res = selection->GetRangeCount(&rangeCount); 1.2238 + NS_ENSURE_SUCCESS(res, res); 1.2239 + 1.2240 + nsCOMPtr<nsIDOMRange> range; 1.2241 + int32_t i; 1.2242 + for (i = 0; i < rangeCount; i++) 1.2243 + { 1.2244 + res = selection->GetRangeAt(i, getter_AddRefs(range)); 1.2245 + NS_ENSURE_SUCCESS(res, res); 1.2246 + NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); 1.2247 + 1.2248 + nsCOMPtr<nsIDOMElement> deletedCell; 1.2249 + res = GetCellFromRange(range, getter_AddRefs(deletedCell)); 1.2250 + if (!deletedCell) 1.2251 + { 1.2252 + selection->RemoveRange(range); 1.2253 + rangeCount--; 1.2254 + i--; 1.2255 + } 1.2256 + } 1.2257 + 1.2258 + // Set spans for the cell everthing merged into 1.2259 + res = SetRowSpan(firstCell, lastRowIndex-firstRowIndex+1); 1.2260 + NS_ENSURE_SUCCESS(res, res); 1.2261 + res = SetColSpan(firstCell, lastColIndex-firstColIndex+1); 1.2262 + NS_ENSURE_SUCCESS(res, res); 1.2263 + 1.2264 + 1.2265 + // Fixup disturbances in table layout 1.2266 + NormalizeTable(table); 1.2267 + } 1.2268 + else 1.2269 + { 1.2270 + // Joining with cell to the right -- get rowspan and colspan data of target cell 1.2271 + res = GetCellDataAt(table, startRowIndex, startColIndex, getter_AddRefs(targetCell), 1.2272 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.2273 + &actualRowSpan, &actualColSpan, &isSelected); 1.2274 + NS_ENSURE_SUCCESS(res, res); 1.2275 + NS_ENSURE_TRUE(targetCell, NS_ERROR_NULL_POINTER); 1.2276 + 1.2277 + // Get data for cell to the right 1.2278 + res = GetCellDataAt(table, startRowIndex, startColIndex+actualColSpan, getter_AddRefs(cell2), 1.2279 + &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2, 1.2280 + &actualRowSpan2, &actualColSpan2, &isSelected2); 1.2281 + NS_ENSURE_SUCCESS(res, res); 1.2282 + if(!cell2) return NS_OK; // Don't fail if there's no cell 1.2283 + 1.2284 + // sanity check 1.2285 + NS_ASSERTION((startRowIndex >= startRowIndex2),"JoinCells: startRowIndex < startRowIndex2"); 1.2286 + 1.2287 + // Figure out span of merged cell starting from target's starting row 1.2288 + // to handle case of merged cell starting in a row above 1.2289 + int32_t spanAboveMergedCell = startRowIndex - startRowIndex2; 1.2290 + int32_t effectiveRowSpan2 = actualRowSpan2 - spanAboveMergedCell; 1.2291 + 1.2292 + if (effectiveRowSpan2 > actualRowSpan) 1.2293 + { 1.2294 + // Cell to the right spans into row below target 1.2295 + // Split off portion below target cell's bottom-most row 1.2296 + res = SplitCellIntoRows(table, startRowIndex2, startColIndex2, 1.2297 + spanAboveMergedCell+actualRowSpan, 1.2298 + effectiveRowSpan2-actualRowSpan, nullptr); 1.2299 + NS_ENSURE_SUCCESS(res, res); 1.2300 + } 1.2301 + 1.2302 + // Move contents from cell to the right 1.2303 + // Delete the cell now only if it starts in the same row 1.2304 + // and has enough row "height" 1.2305 + res = MergeCells(targetCell, cell2, 1.2306 + (startRowIndex2 == startRowIndex) && 1.2307 + (effectiveRowSpan2 >= actualRowSpan)); 1.2308 + NS_ENSURE_SUCCESS(res, res); 1.2309 + 1.2310 + if (effectiveRowSpan2 < actualRowSpan) 1.2311 + { 1.2312 + // Merged cell is "shorter" 1.2313 + // (there are cells(s) below it that are row-spanned by target cell) 1.2314 + // We could try splitting those cells, but that's REAL messy, 1.2315 + // so the safest thing to do is NOT really join the cells 1.2316 + return NS_OK; 1.2317 + } 1.2318 + 1.2319 + if( spanAboveMergedCell > 0 ) 1.2320 + { 1.2321 + // Cell we merged started in a row above the target cell 1.2322 + // Reduce rowspan to give room where target cell will extend its colspan 1.2323 + res = SetRowSpan(cell2, spanAboveMergedCell); 1.2324 + NS_ENSURE_SUCCESS(res, res); 1.2325 + } 1.2326 + 1.2327 + // Reset target cell's colspan to encompass cell to the right 1.2328 + res = SetColSpan(targetCell, actualColSpan+actualColSpan2); 1.2329 + NS_ENSURE_SUCCESS(res, res); 1.2330 + } 1.2331 + return res; 1.2332 +} 1.2333 + 1.2334 +NS_IMETHODIMP 1.2335 +nsHTMLEditor::MergeCells(nsCOMPtr<nsIDOMElement> aTargetCell, 1.2336 + nsCOMPtr<nsIDOMElement> aCellToMerge, 1.2337 + bool aDeleteCellToMerge) 1.2338 +{ 1.2339 + nsCOMPtr<dom::Element> targetCell = do_QueryInterface(aTargetCell); 1.2340 + nsCOMPtr<dom::Element> cellToMerge = do_QueryInterface(aCellToMerge); 1.2341 + NS_ENSURE_TRUE(targetCell && cellToMerge, NS_ERROR_NULL_POINTER); 1.2342 + 1.2343 + // Prevent rules testing until we're done 1.2344 + nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext); 1.2345 + 1.2346 + // Don't need to merge if cell is empty 1.2347 + if (!IsEmptyCell(cellToMerge)) { 1.2348 + // Get index of last child in target cell 1.2349 + // If we fail or don't have children, 1.2350 + // we insert at index 0 1.2351 + int32_t insertIndex = 0; 1.2352 + 1.2353 + // Start inserting just after last child 1.2354 + uint32_t len = targetCell->GetChildCount(); 1.2355 + if (len == 1 && IsEmptyCell(targetCell)) { 1.2356 + // Delete the empty node 1.2357 + nsIContent* cellChild = targetCell->GetFirstChild(); 1.2358 + nsresult res = DeleteNode(cellChild->AsDOMNode()); 1.2359 + NS_ENSURE_SUCCESS(res, res); 1.2360 + insertIndex = 0; 1.2361 + } else { 1.2362 + insertIndex = (int32_t)len; 1.2363 + } 1.2364 + 1.2365 + // Move the contents 1.2366 + while (cellToMerge->HasChildren()) { 1.2367 + nsCOMPtr<nsIDOMNode> cellChild = cellToMerge->GetLastChild()->AsDOMNode(); 1.2368 + nsresult res = DeleteNode(cellChild); 1.2369 + NS_ENSURE_SUCCESS(res, res); 1.2370 + 1.2371 + res = InsertNode(cellChild, aTargetCell, insertIndex); 1.2372 + NS_ENSURE_SUCCESS(res, res); 1.2373 + } 1.2374 + } 1.2375 + 1.2376 + // Delete cells whose contents were moved 1.2377 + if (aDeleteCellToMerge) 1.2378 + return DeleteNode(aCellToMerge); 1.2379 + 1.2380 + return NS_OK; 1.2381 +} 1.2382 + 1.2383 + 1.2384 +NS_IMETHODIMP 1.2385 +nsHTMLEditor::FixBadRowSpan(nsIDOMElement *aTable, int32_t aRowIndex, int32_t& aNewRowCount) 1.2386 +{ 1.2387 + NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); 1.2388 + 1.2389 + int32_t rowCount, colCount; 1.2390 + nsresult res = GetTableSize(aTable, &rowCount, &colCount); 1.2391 + NS_ENSURE_SUCCESS(res, res); 1.2392 + 1.2393 + nsCOMPtr<nsIDOMElement>cell; 1.2394 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.2395 + bool isSelected; 1.2396 + 1.2397 + int32_t minRowSpan = -1; 1.2398 + int32_t colIndex; 1.2399 + 1.2400 + for( colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan, 1)) 1.2401 + { 1.2402 + res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), 1.2403 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.2404 + &actualRowSpan, &actualColSpan, &isSelected); 1.2405 + // NOTE: This is a *real* failure. 1.2406 + // GetCellDataAt passes if cell is missing from cellmap 1.2407 + if(NS_FAILED(res)) return res; 1.2408 + if (!cell) break; 1.2409 + if(rowSpan > 0 && 1.2410 + startRowIndex == aRowIndex && 1.2411 + (rowSpan < minRowSpan || minRowSpan == -1)) 1.2412 + { 1.2413 + minRowSpan = rowSpan; 1.2414 + } 1.2415 + NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan"); 1.2416 + } 1.2417 + if(minRowSpan > 1) 1.2418 + { 1.2419 + // The amount to reduce everyone's rowspan 1.2420 + // so at least one cell has rowspan = 1 1.2421 + int32_t rowsReduced = minRowSpan - 1; 1.2422 + for(colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan, 1)) 1.2423 + { 1.2424 + res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), 1.2425 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.2426 + &actualRowSpan, &actualColSpan, &isSelected); 1.2427 + if(NS_FAILED(res)) return res; 1.2428 + // Fixup rowspans only for cells starting in current row 1.2429 + if(cell && rowSpan > 0 && 1.2430 + startRowIndex == aRowIndex && 1.2431 + startColIndex == colIndex ) 1.2432 + { 1.2433 + res = SetRowSpan(cell, rowSpan-rowsReduced); 1.2434 + if(NS_FAILED(res)) return res; 1.2435 + } 1.2436 + NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan"); 1.2437 + } 1.2438 + } 1.2439 + return GetTableSize(aTable, &aNewRowCount, &colCount); 1.2440 +} 1.2441 + 1.2442 +NS_IMETHODIMP 1.2443 +nsHTMLEditor::FixBadColSpan(nsIDOMElement *aTable, int32_t aColIndex, int32_t& aNewColCount) 1.2444 +{ 1.2445 + NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); 1.2446 + 1.2447 + int32_t rowCount, colCount; 1.2448 + nsresult res = GetTableSize(aTable, &rowCount, &colCount); 1.2449 + NS_ENSURE_SUCCESS(res, res); 1.2450 + 1.2451 + nsCOMPtr<nsIDOMElement> cell; 1.2452 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.2453 + bool isSelected; 1.2454 + 1.2455 + int32_t minColSpan = -1; 1.2456 + int32_t rowIndex; 1.2457 + 1.2458 + for( rowIndex = 0; rowIndex < rowCount; rowIndex += std::max(actualRowSpan, 1)) 1.2459 + { 1.2460 + res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), 1.2461 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.2462 + &actualRowSpan, &actualColSpan, &isSelected); 1.2463 + // NOTE: This is a *real* failure. 1.2464 + // GetCellDataAt passes if cell is missing from cellmap 1.2465 + if(NS_FAILED(res)) return res; 1.2466 + if (!cell) break; 1.2467 + if(colSpan > 0 && 1.2468 + startColIndex == aColIndex && 1.2469 + (colSpan < minColSpan || minColSpan == -1)) 1.2470 + { 1.2471 + minColSpan = colSpan; 1.2472 + } 1.2473 + NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan"); 1.2474 + } 1.2475 + if(minColSpan > 1) 1.2476 + { 1.2477 + // The amount to reduce everyone's colspan 1.2478 + // so at least one cell has colspan = 1 1.2479 + int32_t colsReduced = minColSpan - 1; 1.2480 + for(rowIndex = 0; rowIndex < rowCount; rowIndex += std::max(actualRowSpan, 1)) 1.2481 + { 1.2482 + res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), 1.2483 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.2484 + &actualRowSpan, &actualColSpan, &isSelected); 1.2485 + if(NS_FAILED(res)) return res; 1.2486 + // Fixup colspans only for cells starting in current column 1.2487 + if(cell && colSpan > 0 && 1.2488 + startColIndex == aColIndex && 1.2489 + startRowIndex == rowIndex ) 1.2490 + { 1.2491 + res = SetColSpan(cell, colSpan-colsReduced); 1.2492 + if(NS_FAILED(res)) return res; 1.2493 + } 1.2494 + NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan"); 1.2495 + } 1.2496 + } 1.2497 + return GetTableSize(aTable, &rowCount, &aNewColCount); 1.2498 +} 1.2499 + 1.2500 +NS_IMETHODIMP 1.2501 +nsHTMLEditor::NormalizeTable(nsIDOMElement *aTable) 1.2502 +{ 1.2503 + nsRefPtr<Selection> selection = GetSelection(); 1.2504 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.2505 + 1.2506 + nsCOMPtr<nsIDOMElement> table; 1.2507 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), 1.2508 + aTable, getter_AddRefs(table)); 1.2509 + NS_ENSURE_SUCCESS(res, res); 1.2510 + // Don't fail if we didn't find a table 1.2511 + NS_ENSURE_TRUE(table, NS_OK); 1.2512 + 1.2513 + int32_t rowCount, colCount, rowIndex, colIndex; 1.2514 + res = GetTableSize(table, &rowCount, &colCount); 1.2515 + NS_ENSURE_SUCCESS(res, res); 1.2516 + 1.2517 + // Save current selection 1.2518 + nsAutoSelectionReset selectionResetter(selection, this); 1.2519 + 1.2520 + nsAutoEditBatch beginBatching(this); 1.2521 + // Prevent auto insertion of BR in new cell until we're done 1.2522 + nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); 1.2523 + 1.2524 + nsCOMPtr<nsIDOMElement> cell; 1.2525 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.2526 + bool isSelected; 1.2527 + 1.2528 + // Scan all cells in each row to detect bad rowspan values 1.2529 + for(rowIndex = 0; rowIndex < rowCount; rowIndex++) 1.2530 + { 1.2531 + res = FixBadRowSpan(table, rowIndex, rowCount); 1.2532 + NS_ENSURE_SUCCESS(res, res); 1.2533 + } 1.2534 + // and same for colspans 1.2535 + for(colIndex = 0; colIndex < colCount; colIndex++) 1.2536 + { 1.2537 + res = FixBadColSpan(table, colIndex, colCount); 1.2538 + NS_ENSURE_SUCCESS(res, res); 1.2539 + } 1.2540 + 1.2541 + // Fill in missing cellmap locations with empty cells 1.2542 + for(rowIndex = 0; rowIndex < rowCount; rowIndex++) 1.2543 + { 1.2544 + nsCOMPtr<nsIDOMElement> previousCellInRow; 1.2545 + 1.2546 + for(colIndex = 0; colIndex < colCount; colIndex++) 1.2547 + { 1.2548 + res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell), 1.2549 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.2550 + &actualRowSpan, &actualColSpan, &isSelected); 1.2551 + // NOTE: This is a *real* failure. 1.2552 + // GetCellDataAt passes if cell is missing from cellmap 1.2553 + if(NS_FAILED(res)) return res; 1.2554 + if (!cell) 1.2555 + { 1.2556 + //We are missing a cell at a cellmap location 1.2557 +#ifdef DEBUG 1.2558 + printf("NormalizeTable found missing cell at row=%d, col=%d\n", rowIndex, colIndex); 1.2559 +#endif 1.2560 + // Add a cell after the previous Cell in the current row 1.2561 + if(previousCellInRow) 1.2562 + { 1.2563 + // Insert a new cell after (true), and return the new cell to us 1.2564 + res = InsertCell(previousCellInRow, 1, 1, true, false, getter_AddRefs(cell)); 1.2565 + NS_ENSURE_SUCCESS(res, res); 1.2566 + 1.2567 + // Set this so we use returned new "cell" to set previousCellInRow below 1.2568 + if(cell) 1.2569 + startRowIndex = rowIndex; 1.2570 + } else { 1.2571 + // We don't have any cells in this row -- We are really messed up! 1.2572 +#ifdef DEBUG 1.2573 + printf("NormalizeTable found no cells in row=%d, col=%d\n", rowIndex, colIndex); 1.2574 +#endif 1.2575 + return NS_ERROR_FAILURE; 1.2576 + } 1.2577 + } 1.2578 + // Save the last cell found in the same row we are scanning 1.2579 + if(startRowIndex == rowIndex) 1.2580 + { 1.2581 + previousCellInRow = cell; 1.2582 + } 1.2583 + } 1.2584 + } 1.2585 + return res; 1.2586 +} 1.2587 + 1.2588 +NS_IMETHODIMP 1.2589 +nsHTMLEditor::GetCellIndexes(nsIDOMElement *aCell, 1.2590 + int32_t *aRowIndex, int32_t *aColIndex) 1.2591 +{ 1.2592 + NS_ENSURE_ARG_POINTER(aRowIndex); 1.2593 + *aColIndex=0; // initialize out params 1.2594 + NS_ENSURE_ARG_POINTER(aColIndex); 1.2595 + *aRowIndex=0; 1.2596 + nsresult res=NS_ERROR_NOT_INITIALIZED; 1.2597 + if (!aCell) 1.2598 + { 1.2599 + // Get the selected cell or the cell enclosing the selection anchor 1.2600 + nsCOMPtr<nsIDOMElement> cell; 1.2601 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell)); 1.2602 + if (NS_SUCCEEDED(res) && cell) 1.2603 + aCell = cell; 1.2604 + else 1.2605 + return NS_ERROR_FAILURE; 1.2606 + } 1.2607 + 1.2608 + NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); 1.2609 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.2610 + NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); 1.2611 + 1.2612 + nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aCell) ); 1.2613 + NS_ENSURE_TRUE(nodeAsContent, NS_ERROR_FAILURE); 1.2614 + // frames are not ref counted, so don't use an nsCOMPtr 1.2615 + nsIFrame *layoutObject = nodeAsContent->GetPrimaryFrame(); 1.2616 + NS_ENSURE_TRUE(layoutObject, NS_ERROR_FAILURE); 1.2617 + 1.2618 + nsITableCellLayout *cellLayoutObject = do_QueryFrame(layoutObject); 1.2619 + NS_ENSURE_TRUE(cellLayoutObject, NS_ERROR_FAILURE); 1.2620 + return cellLayoutObject->GetCellIndexes(*aRowIndex, *aColIndex); 1.2621 +} 1.2622 + 1.2623 +nsTableOuterFrame* 1.2624 +nsHTMLEditor::GetTableFrame(nsIDOMElement* aTable) 1.2625 +{ 1.2626 + NS_ENSURE_TRUE(aTable, nullptr); 1.2627 + 1.2628 + nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aTable) ); 1.2629 + NS_ENSURE_TRUE(nodeAsContent, nullptr); 1.2630 + return do_QueryFrame(nodeAsContent->GetPrimaryFrame()); 1.2631 +} 1.2632 + 1.2633 +//Return actual number of cells (a cell with colspan > 1 counts as just 1) 1.2634 +int32_t nsHTMLEditor::GetNumberOfCellsInRow(nsIDOMElement* aTable, int32_t rowIndex) 1.2635 +{ 1.2636 + int32_t cellCount = 0; 1.2637 + nsCOMPtr<nsIDOMElement> cell; 1.2638 + int32_t colIndex = 0; 1.2639 + nsresult res; 1.2640 + do { 1.2641 + int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.2642 + bool isSelected; 1.2643 + res = GetCellDataAt(aTable, rowIndex, colIndex, getter_AddRefs(cell), 1.2644 + &startRowIndex, &startColIndex, &rowSpan, &colSpan, 1.2645 + &actualRowSpan, &actualColSpan, &isSelected); 1.2646 + NS_ENSURE_SUCCESS(res, 0); 1.2647 + if (cell) 1.2648 + { 1.2649 + // Only count cells that start in row we are working with 1.2650 + if (startRowIndex == rowIndex) 1.2651 + cellCount++; 1.2652 + 1.2653 + //Next possible location for a cell 1.2654 + colIndex += actualColSpan; 1.2655 + } 1.2656 + else 1.2657 + colIndex++; 1.2658 + 1.2659 + } while (cell); 1.2660 + 1.2661 + return cellCount; 1.2662 +} 1.2663 + 1.2664 +/* Not scriptable: For convenience in C++ 1.2665 + Use GetTableRowCount and GetTableColumnCount from JavaScript 1.2666 +*/ 1.2667 +NS_IMETHODIMP 1.2668 +nsHTMLEditor::GetTableSize(nsIDOMElement *aTable, 1.2669 + int32_t* aRowCount, int32_t* aColCount) 1.2670 +{ 1.2671 + NS_ENSURE_ARG_POINTER(aRowCount); 1.2672 + NS_ENSURE_ARG_POINTER(aColCount); 1.2673 + nsresult res; 1.2674 + *aRowCount = 0; 1.2675 + *aColCount = 0; 1.2676 + nsCOMPtr<nsIDOMElement> table; 1.2677 + // Get the selected talbe or the table enclosing the selection anchor 1.2678 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable, getter_AddRefs(table)); 1.2679 + NS_ENSURE_SUCCESS(res, res); 1.2680 + NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); 1.2681 + 1.2682 + nsTableOuterFrame* tableFrame = GetTableFrame(table.get()); 1.2683 + NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE); 1.2684 + 1.2685 + *aRowCount = tableFrame->GetRowCount(); 1.2686 + *aColCount = tableFrame->GetColCount(); 1.2687 + 1.2688 + return NS_OK; 1.2689 +} 1.2690 + 1.2691 +NS_IMETHODIMP 1.2692 +nsHTMLEditor::GetCellDataAt(nsIDOMElement* aTable, int32_t aRowIndex, 1.2693 + int32_t aColIndex, nsIDOMElement **aCell, 1.2694 + int32_t* aStartRowIndex, int32_t* aStartColIndex, 1.2695 + int32_t* aRowSpan, int32_t* aColSpan, 1.2696 + int32_t* aActualRowSpan, int32_t* aActualColSpan, 1.2697 + bool* aIsSelected) 1.2698 +{ 1.2699 + NS_ENSURE_ARG_POINTER(aStartRowIndex); 1.2700 + NS_ENSURE_ARG_POINTER(aStartColIndex); 1.2701 + NS_ENSURE_ARG_POINTER(aRowSpan); 1.2702 + NS_ENSURE_ARG_POINTER(aColSpan); 1.2703 + NS_ENSURE_ARG_POINTER(aActualRowSpan); 1.2704 + NS_ENSURE_ARG_POINTER(aActualColSpan); 1.2705 + NS_ENSURE_ARG_POINTER(aIsSelected); 1.2706 + NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); 1.2707 + 1.2708 + nsresult res=NS_ERROR_FAILURE; 1.2709 + *aStartRowIndex = 0; 1.2710 + *aStartColIndex = 0; 1.2711 + *aRowSpan = 0; 1.2712 + *aColSpan = 0; 1.2713 + *aActualRowSpan = 0; 1.2714 + *aActualColSpan = 0; 1.2715 + *aIsSelected = false; 1.2716 + 1.2717 + *aCell = nullptr; 1.2718 + 1.2719 + if (!aTable) 1.2720 + { 1.2721 + // Get the selected table or the table enclosing the selection anchor 1.2722 + nsCOMPtr<nsIDOMElement> table; 1.2723 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); 1.2724 + NS_ENSURE_SUCCESS(res, res); 1.2725 + if (table) 1.2726 + aTable = table; 1.2727 + else 1.2728 + return NS_ERROR_FAILURE; 1.2729 + } 1.2730 + 1.2731 + nsTableOuterFrame* tableFrame = GetTableFrame(aTable); 1.2732 + NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE); 1.2733 + 1.2734 + nsTableCellFrame* cellFrame = 1.2735 + tableFrame->GetCellFrameAt(aRowIndex, aColIndex); 1.2736 + if (!cellFrame) 1.2737 + return NS_ERROR_FAILURE; 1.2738 + 1.2739 + *aIsSelected = cellFrame->IsSelected(); 1.2740 + cellFrame->GetRowIndex(*aStartRowIndex); 1.2741 + cellFrame->GetColIndex(*aStartColIndex); 1.2742 + *aRowSpan = cellFrame->GetRowSpan(); 1.2743 + *aColSpan = cellFrame->GetColSpan(); 1.2744 + *aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex); 1.2745 + *aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex); 1.2746 + nsCOMPtr<nsIDOMElement> domCell = do_QueryInterface(cellFrame->GetContent()); 1.2747 + domCell.forget(aCell); 1.2748 + 1.2749 + return NS_OK; 1.2750 +} 1.2751 + 1.2752 +// When all you want is the cell 1.2753 +NS_IMETHODIMP 1.2754 +nsHTMLEditor::GetCellAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, nsIDOMElement **aCell) 1.2755 +{ 1.2756 + NS_ENSURE_ARG_POINTER(aCell); 1.2757 + *aCell = nullptr; 1.2758 + 1.2759 + if (!aTable) 1.2760 + { 1.2761 + // Get the selected table or the table enclosing the selection anchor 1.2762 + nsCOMPtr<nsIDOMElement> table; 1.2763 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table)); 1.2764 + NS_ENSURE_SUCCESS(res, res); 1.2765 + NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); 1.2766 + aTable = table; 1.2767 + } 1.2768 + 1.2769 + nsTableOuterFrame* tableFrame = GetTableFrame(aTable); 1.2770 + if (!tableFrame) 1.2771 + return NS_ERROR_FAILURE; 1.2772 + 1.2773 + nsCOMPtr<nsIDOMElement> domCell = 1.2774 + do_QueryInterface(tableFrame->GetCellAt(aRowIndex, aColIndex)); 1.2775 + domCell.forget(aCell); 1.2776 + 1.2777 + return NS_OK; 1.2778 +} 1.2779 + 1.2780 +// When all you want are the rowspan and colspan (not exposed in nsITableEditor) 1.2781 +NS_IMETHODIMP 1.2782 +nsHTMLEditor::GetCellSpansAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, 1.2783 + int32_t& aActualRowSpan, int32_t& aActualColSpan) 1.2784 +{ 1.2785 + nsTableOuterFrame* tableFrame = GetTableFrame(aTable); 1.2786 + if (!tableFrame) 1.2787 + return NS_ERROR_FAILURE; 1.2788 + 1.2789 + aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex); 1.2790 + aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex); 1.2791 + 1.2792 + return NS_OK; 1.2793 +} 1.2794 + 1.2795 +NS_IMETHODIMP 1.2796 +nsHTMLEditor::GetCellContext(nsISelection **aSelection, 1.2797 + nsIDOMElement **aTable, 1.2798 + nsIDOMElement **aCell, 1.2799 + nsIDOMNode **aCellParent, int32_t *aCellOffset, 1.2800 + int32_t *aRowIndex, int32_t *aColIndex) 1.2801 +{ 1.2802 + // Initialize return pointers 1.2803 + if (aSelection) *aSelection = nullptr; 1.2804 + if (aTable) *aTable = nullptr; 1.2805 + if (aCell) *aCell = nullptr; 1.2806 + if (aCellParent) *aCellParent = nullptr; 1.2807 + if (aCellOffset) *aCellOffset = 0; 1.2808 + if (aRowIndex) *aRowIndex = 0; 1.2809 + if (aColIndex) *aColIndex = 0; 1.2810 + 1.2811 + nsCOMPtr <nsISelection> selection; 1.2812 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.2813 + NS_ENSURE_SUCCESS(res, res); 1.2814 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.2815 + 1.2816 + if (aSelection) 1.2817 + { 1.2818 + *aSelection = selection.get(); 1.2819 + NS_ADDREF(*aSelection); 1.2820 + } 1.2821 + nsCOMPtr <nsIDOMElement> table; 1.2822 + nsCOMPtr <nsIDOMElement> cell; 1.2823 + 1.2824 + // Caller may supply the cell... 1.2825 + if (aCell && *aCell) 1.2826 + cell = *aCell; 1.2827 + 1.2828 + // ...but if not supplied, 1.2829 + // get cell if it's the child of selection anchor node, 1.2830 + // or get the enclosing by a cell 1.2831 + if (!cell) 1.2832 + { 1.2833 + // Find a selected or enclosing table element 1.2834 + nsCOMPtr<nsIDOMElement> cellOrTableElement; 1.2835 + int32_t selectedCount; 1.2836 + nsAutoString tagName; 1.2837 + res = GetSelectedOrParentTableElement(tagName, &selectedCount, 1.2838 + getter_AddRefs(cellOrTableElement)); 1.2839 + NS_ENSURE_SUCCESS(res, res); 1.2840 + if (tagName.EqualsLiteral("table")) 1.2841 + { 1.2842 + // We have a selected table, not a cell 1.2843 + if (aTable) 1.2844 + { 1.2845 + *aTable = cellOrTableElement.get(); 1.2846 + NS_ADDREF(*aTable); 1.2847 + } 1.2848 + return NS_OK; 1.2849 + } 1.2850 + if (!tagName.EqualsLiteral("td")) 1.2851 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.2852 + 1.2853 + // We found a cell 1.2854 + cell = cellOrTableElement; 1.2855 + } 1.2856 + if (aCell) 1.2857 + { 1.2858 + *aCell = cell.get(); 1.2859 + NS_ADDREF(*aCell); 1.2860 + } 1.2861 + 1.2862 + // Get containing table 1.2863 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell, getter_AddRefs(table)); 1.2864 + NS_ENSURE_SUCCESS(res, res); 1.2865 + // Cell must be in a table, so fail if not found 1.2866 + NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); 1.2867 + if (aTable) 1.2868 + { 1.2869 + *aTable = table.get(); 1.2870 + NS_ADDREF(*aTable); 1.2871 + } 1.2872 + 1.2873 + // Get the rest of the related data only if requested 1.2874 + if (aRowIndex || aColIndex) 1.2875 + { 1.2876 + int32_t rowIndex, colIndex; 1.2877 + // Get current cell location so we can put caret back there when done 1.2878 + res = GetCellIndexes(cell, &rowIndex, &colIndex); 1.2879 + if(NS_FAILED(res)) return res; 1.2880 + if (aRowIndex) *aRowIndex = rowIndex; 1.2881 + if (aColIndex) *aColIndex = colIndex; 1.2882 + } 1.2883 + if (aCellParent) 1.2884 + { 1.2885 + nsCOMPtr <nsIDOMNode> cellParent; 1.2886 + // Get the immediate parent of the cell 1.2887 + res = cell->GetParentNode(getter_AddRefs(cellParent)); 1.2888 + NS_ENSURE_SUCCESS(res, res); 1.2889 + // Cell has to have a parent, so fail if not found 1.2890 + NS_ENSURE_TRUE(cellParent, NS_ERROR_FAILURE); 1.2891 + 1.2892 + *aCellParent = cellParent.get(); 1.2893 + NS_ADDREF(*aCellParent); 1.2894 + 1.2895 + if (aCellOffset) { 1.2896 + *aCellOffset = GetChildOffset(cell, cellParent); 1.2897 + } 1.2898 + } 1.2899 + 1.2900 + return res; 1.2901 +} 1.2902 + 1.2903 +nsresult 1.2904 +nsHTMLEditor::GetCellFromRange(nsIDOMRange *aRange, nsIDOMElement **aCell) 1.2905 +{ 1.2906 + // Note: this might return a node that is outside of the range. 1.2907 + // Use carefully. 1.2908 + NS_ENSURE_TRUE(aRange && aCell, NS_ERROR_NULL_POINTER); 1.2909 + 1.2910 + *aCell = nullptr; 1.2911 + 1.2912 + nsCOMPtr<nsIDOMNode> startParent; 1.2913 + nsresult res = aRange->GetStartContainer(getter_AddRefs(startParent)); 1.2914 + NS_ENSURE_SUCCESS(res, res); 1.2915 + NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); 1.2916 + 1.2917 + int32_t startOffset; 1.2918 + res = aRange->GetStartOffset(&startOffset); 1.2919 + NS_ENSURE_SUCCESS(res, res); 1.2920 + 1.2921 + nsCOMPtr<nsIDOMNode> childNode = GetChildAt(startParent, startOffset); 1.2922 + // This means selection is probably at a text node (or end of doc?) 1.2923 + if (!childNode) { 1.2924 + return NS_ERROR_FAILURE; 1.2925 + } 1.2926 + 1.2927 + nsCOMPtr<nsIDOMNode> endParent; 1.2928 + res = aRange->GetEndContainer(getter_AddRefs(endParent)); 1.2929 + NS_ENSURE_SUCCESS(res, res); 1.2930 + NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); 1.2931 + 1.2932 + int32_t endOffset; 1.2933 + res = aRange->GetEndOffset(&endOffset); 1.2934 + NS_ENSURE_SUCCESS(res, res); 1.2935 + 1.2936 + // If a cell is deleted, the range is collapse 1.2937 + // (startOffset == endOffset) 1.2938 + // so tell caller the cell wasn't found 1.2939 + if (startParent == endParent && 1.2940 + endOffset == startOffset+1 && 1.2941 + nsHTMLEditUtils::IsTableCell(childNode)) 1.2942 + { 1.2943 + // Should we also test if frame is selected? (Use GetCellDataAt()) 1.2944 + // (Let's not for now -- more efficient) 1.2945 + nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childNode); 1.2946 + *aCell = cellElement.get(); 1.2947 + NS_ADDREF(*aCell); 1.2948 + return NS_OK; 1.2949 + } 1.2950 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.2951 +} 1.2952 + 1.2953 +NS_IMETHODIMP 1.2954 +nsHTMLEditor::GetFirstSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell) 1.2955 +{ 1.2956 + NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); 1.2957 + *aCell = nullptr; 1.2958 + if (aRange) *aRange = nullptr; 1.2959 + 1.2960 + nsCOMPtr<nsISelection> selection; 1.2961 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.2962 + NS_ENSURE_SUCCESS(res, res); 1.2963 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.2964 + 1.2965 + nsCOMPtr<nsIDOMRange> range; 1.2966 + res = selection->GetRangeAt(0, getter_AddRefs(range)); 1.2967 + NS_ENSURE_SUCCESS(res, res); 1.2968 + NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); 1.2969 + 1.2970 + mSelectedCellIndex = 0; 1.2971 + 1.2972 + res = GetCellFromRange(range, aCell); 1.2973 + // Failure here probably means selection is in a text node, 1.2974 + // so there's no selected cell 1.2975 + if (NS_FAILED(res)) { 1.2976 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.2977 + } 1.2978 + // No cell means range was collapsed (cell was deleted) 1.2979 + if (!*aCell) { 1.2980 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.2981 + } 1.2982 + 1.2983 + if (aRange) 1.2984 + { 1.2985 + *aRange = range.get(); 1.2986 + NS_ADDREF(*aRange); 1.2987 + } 1.2988 + 1.2989 + // Setup for next cell 1.2990 + mSelectedCellIndex = 1; 1.2991 + 1.2992 + return res; 1.2993 +} 1.2994 + 1.2995 +NS_IMETHODIMP 1.2996 +nsHTMLEditor::GetNextSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell) 1.2997 +{ 1.2998 + NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); 1.2999 + *aCell = nullptr; 1.3000 + if (aRange) *aRange = nullptr; 1.3001 + 1.3002 + nsCOMPtr<nsISelection> selection; 1.3003 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.3004 + NS_ENSURE_SUCCESS(res, res); 1.3005 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.3006 + 1.3007 + int32_t rangeCount; 1.3008 + res = selection->GetRangeCount(&rangeCount); 1.3009 + NS_ENSURE_SUCCESS(res, res); 1.3010 + 1.3011 + // Don't even try if index exceeds range count 1.3012 + if (mSelectedCellIndex >= rangeCount) 1.3013 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.3014 + 1.3015 + // Scan through ranges to find next valid selected cell 1.3016 + nsCOMPtr<nsIDOMRange> range; 1.3017 + for (; mSelectedCellIndex < rangeCount; mSelectedCellIndex++) 1.3018 + { 1.3019 + res = selection->GetRangeAt(mSelectedCellIndex, getter_AddRefs(range)); 1.3020 + NS_ENSURE_SUCCESS(res, res); 1.3021 + NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); 1.3022 + 1.3023 + res = GetCellFromRange(range, aCell); 1.3024 + // Failure here means the range doesn't contain a cell 1.3025 + NS_ENSURE_SUCCESS(res, NS_EDITOR_ELEMENT_NOT_FOUND); 1.3026 + 1.3027 + // We found a selected cell 1.3028 + if (*aCell) break; 1.3029 +#ifdef DEBUG_cmanske 1.3030 + else 1.3031 + printf("GetNextSelectedCell: Collapsed range found\n"); 1.3032 +#endif 1.3033 + 1.3034 + // If we didn't find a cell, continue to next range in selection 1.3035 + } 1.3036 + // No cell means all remaining ranges were collapsed (cells were deleted) 1.3037 + NS_ENSURE_TRUE(*aCell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.3038 + 1.3039 + if (aRange) 1.3040 + { 1.3041 + *aRange = range.get(); 1.3042 + NS_ADDREF(*aRange); 1.3043 + } 1.3044 + 1.3045 + // Setup for next cell 1.3046 + mSelectedCellIndex++; 1.3047 + 1.3048 + return res; 1.3049 +} 1.3050 + 1.3051 +NS_IMETHODIMP 1.3052 +nsHTMLEditor::GetFirstSelectedCellInTable(int32_t *aRowIndex, int32_t *aColIndex, nsIDOMElement **aCell) 1.3053 +{ 1.3054 + NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); 1.3055 + *aCell = nullptr; 1.3056 + if (aRowIndex) 1.3057 + *aRowIndex = 0; 1.3058 + if (aColIndex) 1.3059 + *aColIndex = 0; 1.3060 + 1.3061 + nsCOMPtr<nsIDOMElement> cell; 1.3062 + nsresult res = GetFirstSelectedCell(nullptr, getter_AddRefs(cell)); 1.3063 + NS_ENSURE_SUCCESS(res, res); 1.3064 + NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND); 1.3065 + 1.3066 + *aCell = cell.get(); 1.3067 + NS_ADDREF(*aCell); 1.3068 + 1.3069 + // Also return the row and/or column if requested 1.3070 + if (aRowIndex || aColIndex) 1.3071 + { 1.3072 + int32_t startRowIndex, startColIndex; 1.3073 + res = GetCellIndexes(cell, &startRowIndex, &startColIndex); 1.3074 + if(NS_FAILED(res)) return res; 1.3075 + 1.3076 + if (aRowIndex) 1.3077 + *aRowIndex = startRowIndex; 1.3078 + 1.3079 + if (aColIndex) 1.3080 + *aColIndex = startColIndex; 1.3081 + } 1.3082 + 1.3083 + return res; 1.3084 +} 1.3085 + 1.3086 +NS_IMETHODIMP 1.3087 +nsHTMLEditor::SetSelectionAfterTableEdit(nsIDOMElement* aTable, int32_t aRow, int32_t aCol, 1.3088 + int32_t aDirection, bool aSelected) 1.3089 +{ 1.3090 + NS_ENSURE_TRUE(aTable, NS_ERROR_NOT_INITIALIZED); 1.3091 + 1.3092 + nsCOMPtr<nsISelection>selection; 1.3093 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.3094 + NS_ENSURE_SUCCESS(res, res); 1.3095 + 1.3096 + if (!selection) 1.3097 + { 1.3098 +#ifdef DEBUG_cmanske 1.3099 + printf("Selection not found after table manipulation!\n"); 1.3100 +#endif 1.3101 + return NS_ERROR_FAILURE; 1.3102 + } 1.3103 + 1.3104 + nsCOMPtr<nsIDOMElement> cell; 1.3105 + bool done = false; 1.3106 + do { 1.3107 + res = GetCellAt(aTable, aRow, aCol, getter_AddRefs(cell)); 1.3108 + if (NS_SUCCEEDED(res)) 1.3109 + { 1.3110 + if (cell) 1.3111 + { 1.3112 + if (aSelected) 1.3113 + { 1.3114 + // Reselect the cell 1.3115 + return SelectElement(cell); 1.3116 + } 1.3117 + else 1.3118 + { 1.3119 + // Set the caret to deepest first child 1.3120 + // but don't go into nested tables 1.3121 + // TODO: Should we really be placing the caret at the END 1.3122 + // of the cell content? 1.3123 + return CollapseSelectionToDeepestNonTableFirstChild(selection, cell); 1.3124 + } 1.3125 + } else { 1.3126 + // Setup index to find another cell in the 1.3127 + // direction requested, but move in 1.3128 + // other direction if already at beginning of row or column 1.3129 + switch (aDirection) 1.3130 + { 1.3131 + case ePreviousColumn: 1.3132 + if (aCol == 0) 1.3133 + { 1.3134 + if (aRow > 0) 1.3135 + aRow--; 1.3136 + else 1.3137 + done = true; 1.3138 + } 1.3139 + else 1.3140 + aCol--; 1.3141 + break; 1.3142 + case ePreviousRow: 1.3143 + if (aRow == 0) 1.3144 + { 1.3145 + if (aCol > 0) 1.3146 + aCol--; 1.3147 + else 1.3148 + done = true; 1.3149 + } 1.3150 + else 1.3151 + aRow--; 1.3152 + break; 1.3153 + default: 1.3154 + done = true; 1.3155 + } 1.3156 + } 1.3157 + } 1.3158 + else 1.3159 + break; 1.3160 + } while (!done); 1.3161 + 1.3162 + // We didn't find a cell 1.3163 + // Set selection to just before the table 1.3164 + nsCOMPtr<nsIDOMNode> tableParent; 1.3165 + res = aTable->GetParentNode(getter_AddRefs(tableParent)); 1.3166 + if(NS_SUCCEEDED(res) && tableParent) 1.3167 + { 1.3168 + int32_t tableOffset = GetChildOffset(aTable, tableParent); 1.3169 + return selection->Collapse(tableParent, tableOffset); 1.3170 + } 1.3171 + // Last resort: Set selection to start of doc 1.3172 + // (it's very bad to not have a valid selection!) 1.3173 + return SetSelectionAtDocumentStart(selection); 1.3174 +} 1.3175 + 1.3176 +NS_IMETHODIMP 1.3177 +nsHTMLEditor::GetSelectedOrParentTableElement(nsAString& aTagName, 1.3178 + int32_t *aSelectedCount, 1.3179 + nsIDOMElement** aTableElement) 1.3180 +{ 1.3181 + NS_ENSURE_ARG_POINTER(aTableElement); 1.3182 + NS_ENSURE_ARG_POINTER(aSelectedCount); 1.3183 + *aTableElement = nullptr; 1.3184 + aTagName.Truncate(); 1.3185 + *aSelectedCount = 0; 1.3186 + 1.3187 + nsCOMPtr<nsISelection> selection; 1.3188 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.3189 + NS_ENSURE_SUCCESS(res, res); 1.3190 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.3191 + 1.3192 + // Try to get the first selected cell 1.3193 + nsCOMPtr<nsIDOMElement> tableOrCellElement; 1.3194 + res = GetFirstSelectedCell(nullptr, getter_AddRefs(tableOrCellElement)); 1.3195 + NS_ENSURE_SUCCESS(res, res); 1.3196 + 1.3197 + NS_NAMED_LITERAL_STRING(tdName, "td"); 1.3198 + 1.3199 + if (tableOrCellElement) 1.3200 + { 1.3201 + // Each cell is in its own selection range, 1.3202 + // so count signals multiple-cell selection 1.3203 + res = selection->GetRangeCount(aSelectedCount); 1.3204 + NS_ENSURE_SUCCESS(res, res); 1.3205 + aTagName = tdName; 1.3206 + } 1.3207 + else 1.3208 + { 1.3209 + nsCOMPtr<nsIDOMNode> anchorNode; 1.3210 + res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); 1.3211 + if(NS_FAILED(res)) return res; 1.3212 + NS_ENSURE_TRUE(anchorNode, NS_ERROR_FAILURE); 1.3213 + 1.3214 + nsCOMPtr<nsIDOMNode> selectedNode; 1.3215 + 1.3216 + // Get child of anchor node, if exists 1.3217 + bool hasChildren; 1.3218 + anchorNode->HasChildNodes(&hasChildren); 1.3219 + 1.3220 + if (hasChildren) 1.3221 + { 1.3222 + int32_t anchorOffset; 1.3223 + res = selection->GetAnchorOffset(&anchorOffset); 1.3224 + NS_ENSURE_SUCCESS(res, res); 1.3225 + selectedNode = GetChildAt(anchorNode, anchorOffset); 1.3226 + if (!selectedNode) 1.3227 + { 1.3228 + selectedNode = anchorNode; 1.3229 + // If anchor doesn't have a child, we can't be selecting a table element, 1.3230 + // so don't do the following: 1.3231 + } 1.3232 + else 1.3233 + { 1.3234 + nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(selectedNode); 1.3235 + 1.3236 + if (atom == nsEditProperty::td) 1.3237 + { 1.3238 + tableOrCellElement = do_QueryInterface(selectedNode); 1.3239 + aTagName = tdName; 1.3240 + // Each cell is in its own selection range, 1.3241 + // so count signals multiple-cell selection 1.3242 + res = selection->GetRangeCount(aSelectedCount); 1.3243 + NS_ENSURE_SUCCESS(res, res); 1.3244 + } 1.3245 + else if (atom == nsEditProperty::table) 1.3246 + { 1.3247 + tableOrCellElement = do_QueryInterface(selectedNode); 1.3248 + aTagName.AssignLiteral("table"); 1.3249 + *aSelectedCount = 1; 1.3250 + } 1.3251 + else if (atom == nsEditProperty::tr) 1.3252 + { 1.3253 + tableOrCellElement = do_QueryInterface(selectedNode); 1.3254 + aTagName.AssignLiteral("tr"); 1.3255 + *aSelectedCount = 1; 1.3256 + } 1.3257 + } 1.3258 + } 1.3259 + if (!tableOrCellElement) 1.3260 + { 1.3261 + // Didn't find a table element -- find a cell parent 1.3262 + res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableOrCellElement)); 1.3263 + if(NS_FAILED(res)) return res; 1.3264 + if (tableOrCellElement) 1.3265 + aTagName = tdName; 1.3266 + } 1.3267 + } 1.3268 + if (tableOrCellElement) 1.3269 + { 1.3270 + *aTableElement = tableOrCellElement.get(); 1.3271 + NS_ADDREF(*aTableElement); 1.3272 + } 1.3273 + return res; 1.3274 +} 1.3275 + 1.3276 +NS_IMETHODIMP 1.3277 +nsHTMLEditor::GetSelectedCellsType(nsIDOMElement *aElement, uint32_t *aSelectionType) 1.3278 +{ 1.3279 + NS_ENSURE_ARG_POINTER(aSelectionType); 1.3280 + *aSelectionType = 0; 1.3281 + 1.3282 + // Be sure we have a table element 1.3283 + // (if aElement is null, this uses selection's anchor node) 1.3284 + nsCOMPtr<nsIDOMElement> table; 1.3285 + 1.3286 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aElement, getter_AddRefs(table)); 1.3287 + NS_ENSURE_SUCCESS(res, res); 1.3288 + 1.3289 + int32_t rowCount, colCount; 1.3290 + res = GetTableSize(table, &rowCount, &colCount); 1.3291 + NS_ENSURE_SUCCESS(res, res); 1.3292 + 1.3293 + // Traverse all selected cells 1.3294 + nsCOMPtr<nsIDOMElement> selectedCell; 1.3295 + res = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell)); 1.3296 + NS_ENSURE_SUCCESS(res, res); 1.3297 + if (res == NS_EDITOR_ELEMENT_NOT_FOUND) return NS_OK; 1.3298 + 1.3299 + // We have at least one selected cell, so set return value 1.3300 + *aSelectionType = nsISelectionPrivate::TABLESELECTION_CELL; 1.3301 + 1.3302 + // Store indexes of each row/col to avoid duplication of searches 1.3303 + nsTArray<int32_t> indexArray; 1.3304 + 1.3305 + bool allCellsInRowAreSelected = false; 1.3306 + bool allCellsInColAreSelected = false; 1.3307 + while (NS_SUCCEEDED(res) && selectedCell) 1.3308 + { 1.3309 + // Get the cell's location in the cellmap 1.3310 + int32_t startRowIndex, startColIndex; 1.3311 + res = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex); 1.3312 + if(NS_FAILED(res)) return res; 1.3313 + 1.3314 + if (!indexArray.Contains(startColIndex)) 1.3315 + { 1.3316 + indexArray.AppendElement(startColIndex); 1.3317 + allCellsInRowAreSelected = AllCellsInRowSelected(table, startRowIndex, colCount); 1.3318 + // We're done as soon as we fail for any row 1.3319 + if (!allCellsInRowAreSelected) break; 1.3320 + } 1.3321 + res = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell)); 1.3322 + } 1.3323 + 1.3324 + if (allCellsInRowAreSelected) 1.3325 + { 1.3326 + *aSelectionType = nsISelectionPrivate::TABLESELECTION_ROW; 1.3327 + return NS_OK; 1.3328 + } 1.3329 + // Test for columns 1.3330 + 1.3331 + // Empty the indexArray 1.3332 + indexArray.Clear(); 1.3333 + 1.3334 + // Start at first cell again 1.3335 + res = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell)); 1.3336 + while (NS_SUCCEEDED(res) && selectedCell) 1.3337 + { 1.3338 + // Get the cell's location in the cellmap 1.3339 + int32_t startRowIndex, startColIndex; 1.3340 + res = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex); 1.3341 + if(NS_FAILED(res)) return res; 1.3342 + 1.3343 + if (!indexArray.Contains(startRowIndex)) 1.3344 + { 1.3345 + indexArray.AppendElement(startColIndex); 1.3346 + allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, rowCount); 1.3347 + // We're done as soon as we fail for any column 1.3348 + if (!allCellsInRowAreSelected) break; 1.3349 + } 1.3350 + res = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell)); 1.3351 + } 1.3352 + if (allCellsInColAreSelected) 1.3353 + *aSelectionType = nsISelectionPrivate::TABLESELECTION_COLUMN; 1.3354 + 1.3355 + return NS_OK; 1.3356 +} 1.3357 + 1.3358 +bool 1.3359 +nsHTMLEditor::AllCellsInRowSelected(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aNumberOfColumns) 1.3360 +{ 1.3361 + NS_ENSURE_TRUE(aTable, false); 1.3362 + 1.3363 + int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.3364 + bool isSelected; 1.3365 + 1.3366 + for( int32_t col = 0; col < aNumberOfColumns; col += std::max(actualColSpan, 1)) 1.3367 + { 1.3368 + nsCOMPtr<nsIDOMElement> cell; 1.3369 + nsresult res = GetCellDataAt(aTable, aRowIndex, col, getter_AddRefs(cell), 1.3370 + &curStartRowIndex, &curStartColIndex, 1.3371 + &rowSpan, &colSpan, 1.3372 + &actualRowSpan, &actualColSpan, &isSelected); 1.3373 + 1.3374 + NS_ENSURE_SUCCESS(res, false); 1.3375 + // If no cell, we may have a "ragged" right edge, 1.3376 + // so return TRUE only if we already found a cell in the row 1.3377 + NS_ENSURE_TRUE(cell, (col > 0) ? true : false); 1.3378 + 1.3379 + // Return as soon as a non-selected cell is found 1.3380 + NS_ENSURE_TRUE(isSelected, false); 1.3381 + 1.3382 + NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in AllCellsInRowSelected"); 1.3383 + } 1.3384 + return true; 1.3385 +} 1.3386 + 1.3387 +bool 1.3388 +nsHTMLEditor::AllCellsInColumnSelected(nsIDOMElement *aTable, int32_t aColIndex, int32_t aNumberOfRows) 1.3389 +{ 1.3390 + NS_ENSURE_TRUE(aTable, false); 1.3391 + 1.3392 + int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; 1.3393 + bool isSelected; 1.3394 + 1.3395 + for( int32_t row = 0; row < aNumberOfRows; row += std::max(actualRowSpan, 1)) 1.3396 + { 1.3397 + nsCOMPtr<nsIDOMElement> cell; 1.3398 + nsresult res = GetCellDataAt(aTable, row, aColIndex, getter_AddRefs(cell), 1.3399 + &curStartRowIndex, &curStartColIndex, 1.3400 + &rowSpan, &colSpan, 1.3401 + &actualRowSpan, &actualColSpan, &isSelected); 1.3402 + 1.3403 + NS_ENSURE_SUCCESS(res, false); 1.3404 + // If no cell, we must have a "ragged" right edge on the last column 1.3405 + // so return TRUE only if we already found a cell in the row 1.3406 + NS_ENSURE_TRUE(cell, (row > 0) ? true : false); 1.3407 + 1.3408 + // Return as soon as a non-selected cell is found 1.3409 + NS_ENSURE_TRUE(isSelected, false); 1.3410 + } 1.3411 + return true; 1.3412 +} 1.3413 + 1.3414 +bool 1.3415 +nsHTMLEditor::IsEmptyCell(dom::Element* aCell) 1.3416 +{ 1.3417 + MOZ_ASSERT(aCell); 1.3418 + 1.3419 + // Check if target only contains empty text node or <br> 1.3420 + nsCOMPtr<nsINode> cellChild = aCell->GetFirstChild(); 1.3421 + if (!cellChild) { 1.3422 + return false; 1.3423 + } 1.3424 + 1.3425 + nsCOMPtr<nsINode> nextChild = cellChild->GetNextSibling(); 1.3426 + if (nextChild) { 1.3427 + return false; 1.3428 + } 1.3429 + 1.3430 + // We insert a single break into a cell by default 1.3431 + // to have some place to locate a cursor -- it is dispensable 1.3432 + if (cellChild->IsElement() && cellChild->AsElement()->IsHTML(nsGkAtoms::br)) { 1.3433 + return true; 1.3434 + } 1.3435 + 1.3436 + bool isEmpty; 1.3437 + // Or check if no real content 1.3438 + nsresult rv = IsEmptyNode(cellChild, &isEmpty, false, false); 1.3439 + NS_ENSURE_SUCCESS(rv, false); 1.3440 + return isEmpty; 1.3441 +}