editor/libeditor/html/nsTableEditor.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include <stdio.h>
     8 #include "mozilla/Assertions.h"
     9 #include "mozilla/dom/Selection.h"
    10 #include "mozilla/dom/Element.h"
    11 #include "nsAString.h"
    12 #include "nsAlgorithm.h"
    13 #include "nsCOMPtr.h"
    14 #include "nsDebug.h"
    15 #include "nsEditProperty.h"
    16 #include "nsEditor.h"
    17 #include "nsEditorUtils.h"
    18 #include "nsError.h"
    19 #include "nsGkAtoms.h"
    20 #include "nsHTMLEditUtils.h"
    21 #include "nsHTMLEditor.h"
    22 #include "nsIAtom.h"
    23 #include "nsIContent.h"
    24 #include "nsIDOMElement.h"
    25 #include "nsIDOMNode.h"
    26 #include "nsIDOMRange.h"
    27 #include "nsIEditor.h"
    28 #include "nsIFrame.h"
    29 #include "nsIHTMLEditor.h"
    30 #include "nsINode.h"
    31 #include "nsIPresShell.h"
    32 #include "nsISupportsUtils.h"
    33 #include "nsITableCellLayout.h" // For efficient access to table cell
    34 #include "nsITableEditor.h"
    35 #include "nsLiteralString.h"
    36 #include "nsQueryFrame.h"
    37 #include "nsString.h"
    38 #include "nsTArray.h"
    39 #include "nsTableCellFrame.h"
    40 #include "nsTableOuterFrame.h"
    41 #include "nscore.h"
    42 #include <algorithm>
    44 using namespace mozilla;
    45 using namespace mozilla::dom;
    47 /***************************************************************************
    48  * stack based helper class for restoring selection after table edit
    49  */
    50 class MOZ_STACK_CLASS nsSetSelectionAfterTableEdit
    51 {
    52   private:
    53     nsCOMPtr<nsITableEditor> mEd;
    54     nsCOMPtr<nsIDOMElement> mTable;
    55     int32_t mCol, mRow, mDirection, mSelected;
    56   public:
    57     nsSetSelectionAfterTableEdit(nsITableEditor *aEd, nsIDOMElement* aTable, 
    58                                  int32_t aRow, int32_t aCol, int32_t aDirection, 
    59                                  bool aSelected) : 
    60         mEd(do_QueryInterface(aEd))
    61     { 
    62       mTable = aTable; 
    63       mRow = aRow; 
    64       mCol = aCol; 
    65       mDirection = aDirection;
    66       mSelected = aSelected;
    67     } 
    69     ~nsSetSelectionAfterTableEdit() 
    70     { 
    71       if (mEd)
    72         mEd->SetSelectionAfterTableEdit(mTable, mRow, mCol, mDirection, mSelected);
    73     }
    74     // This is needed to abort the caret reset in the destructor
    75     //  when one method yields control to another
    76     void CancelSetCaret() {mEd = nullptr; mTable = nullptr;}
    77 };
    79 // Stack-class to turn on/off selection batching for table selection
    80 class MOZ_STACK_CLASS nsSelectionBatcherForTable
    81 {
    82 private:
    83   nsCOMPtr<nsISelectionPrivate> mSelection;
    84 public:
    85   nsSelectionBatcherForTable(nsISelection *aSelection)
    86   {
    87     nsCOMPtr<nsISelection> sel(aSelection);
    88     mSelection = do_QueryInterface(sel);
    89     if (mSelection)  mSelection->StartBatchChanges();
    90   }
    91   virtual ~nsSelectionBatcherForTable() 
    92   { 
    93     if (mSelection) mSelection->EndBatchChanges();
    94   }
    95 };
    97 // Table Editing helper utilities (not exposed in IDL)
    99 NS_IMETHODIMP
   100 nsHTMLEditor::InsertCell(nsIDOMElement *aCell, int32_t aRowSpan, int32_t aColSpan, 
   101                          bool aAfter, bool aIsHeader, nsIDOMElement **aNewCell)
   102 {
   103   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
   104   if (aNewCell) *aNewCell = nullptr;
   106   // And the parent and offsets needed to do an insert
   107   nsCOMPtr<nsIDOMNode> cellParent;
   108   nsresult res = aCell->GetParentNode(getter_AddRefs(cellParent));
   109   NS_ENSURE_SUCCESS(res, res);
   110   NS_ENSURE_TRUE(cellParent, NS_ERROR_NULL_POINTER);
   112   int32_t cellOffset = GetChildOffset(aCell, cellParent);
   114   nsCOMPtr<nsIDOMElement> newCell;
   115   if (aIsHeader)
   116     res = CreateElementWithDefaults(NS_LITERAL_STRING("th"), getter_AddRefs(newCell));
   117   else
   118     res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell));
   120   if(NS_FAILED(res)) return res;
   121   if(!newCell) return NS_ERROR_FAILURE;
   123   //Optional: return new cell created
   124   if (aNewCell)
   125   {
   126     *aNewCell = newCell.get();
   127     NS_ADDREF(*aNewCell);
   128   }
   130   if( aRowSpan > 1)
   131   {
   132     // Note: Do NOT use editor transaction for this
   133     nsAutoString newRowSpan;
   134     newRowSpan.AppendInt(aRowSpan, 10);
   135     newCell->SetAttribute(NS_LITERAL_STRING("rowspan"), newRowSpan);
   136   }
   137   if( aColSpan > 1)
   138   {
   139     // Note: Do NOT use editor transaction for this
   140     nsAutoString newColSpan;
   141     newColSpan.AppendInt(aColSpan, 10);
   142     newCell->SetAttribute(NS_LITERAL_STRING("colspan"), newColSpan);
   143   }
   144   if(aAfter) cellOffset++;
   146   //Don't let Rules System change the selection
   147   nsAutoTxnsConserveSelection dontChangeSelection(this);
   148   return InsertNode(newCell, cellParent, cellOffset);
   149 }
   151 NS_IMETHODIMP nsHTMLEditor::SetColSpan(nsIDOMElement *aCell, int32_t aColSpan)
   152 {
   153   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
   154   nsAutoString newSpan;
   155   newSpan.AppendInt(aColSpan, 10);
   156   return SetAttribute(aCell, NS_LITERAL_STRING("colspan"), newSpan);
   157 }
   159 NS_IMETHODIMP nsHTMLEditor::SetRowSpan(nsIDOMElement *aCell, int32_t aRowSpan)
   160 {
   161   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
   162   nsAutoString newSpan;
   163   newSpan.AppendInt(aRowSpan, 10);
   164   return SetAttribute(aCell, NS_LITERAL_STRING("rowspan"), newSpan);
   165 }
   167 /****************************************************************/
   169 // Table Editing interface methods
   171 NS_IMETHODIMP
   172 nsHTMLEditor::InsertTableCell(int32_t aNumber, bool aAfter)
   173 {
   174   nsCOMPtr<nsIDOMElement> table;
   175   nsCOMPtr<nsIDOMElement> curCell;
   176   nsCOMPtr<nsIDOMNode> cellParent;
   177   int32_t cellOffset, startRowIndex, startColIndex;
   178   nsresult res = GetCellContext(nullptr,
   179                                 getter_AddRefs(table), 
   180                                 getter_AddRefs(curCell), 
   181                                 getter_AddRefs(cellParent), &cellOffset,
   182                                 &startRowIndex, &startColIndex);
   183   NS_ENSURE_SUCCESS(res, res);
   184   // Don't fail if no cell found
   185   NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND);
   187   // Get more data for current cell in row we are inserting at (we need COLSPAN)
   188   int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
   189   bool    isSelected;
   190   res = GetCellDataAt(table, startRowIndex, startColIndex,
   191                       getter_AddRefs(curCell),
   192                       &curStartRowIndex, &curStartColIndex, &rowSpan, &colSpan,
   193                       &actualRowSpan, &actualColSpan, &isSelected);
   194   NS_ENSURE_SUCCESS(res, res);
   195   NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
   196   int32_t newCellIndex = aAfter ? (startColIndex+colSpan) : startColIndex;
   197   //We control selection resetting after the insert...
   198   nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, newCellIndex, ePreviousColumn, false);
   199   //...so suppress Rules System selection munging
   200   nsAutoTxnsConserveSelection dontChangeSelection(this);
   202   int32_t i;
   203   for (i = 0; i < aNumber; i++)
   204   {
   205     nsCOMPtr<nsIDOMElement> newCell;
   206     res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell));
   207     if (NS_SUCCEEDED(res) && newCell)
   208     {
   209       if (aAfter) cellOffset++;
   210       res = InsertNode(newCell, cellParent, cellOffset);
   211       if(NS_FAILED(res)) break;
   212     }
   213   }
   214   return res;
   215 }
   218 NS_IMETHODIMP 
   219 nsHTMLEditor::GetFirstRow(nsIDOMElement* aTableElement, nsIDOMNode** aRowNode)
   220 {
   221   NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
   223   *aRowNode = nullptr;  
   225   NS_ENSURE_TRUE(aTableElement, NS_ERROR_NULL_POINTER);
   227   nsCOMPtr<nsIDOMElement> tableElement;
   228   nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTableElement, getter_AddRefs(tableElement));
   229   NS_ENSURE_SUCCESS(res, res);
   230   NS_ENSURE_TRUE(tableElement, NS_ERROR_NULL_POINTER);
   232   nsCOMPtr<nsIDOMNode> tableChild;
   233   res = tableElement->GetFirstChild(getter_AddRefs(tableChild));
   234   NS_ENSURE_SUCCESS(res, res);
   236   while (tableChild)
   237   {
   238     nsCOMPtr<nsIContent> content = do_QueryInterface(tableChild);
   239     if (content)
   240     {
   241       nsIAtom *atom = content->Tag();
   243       if (atom == nsEditProperty::tr)
   244       {
   245         // Found a row directly under <table>
   246         *aRowNode = tableChild;
   247         NS_ADDREF(*aRowNode);
   248         return NS_OK;
   249       }
   250       // Look for row in one of the row container elements      
   251       if (atom == nsEditProperty::tbody ||
   252           atom == nsEditProperty::thead ||
   253           atom == nsEditProperty::tfoot)
   254       {
   255         nsCOMPtr<nsIDOMNode> rowNode;
   256         res = tableChild->GetFirstChild(getter_AddRefs(rowNode));
   257         NS_ENSURE_SUCCESS(res, res);
   259         // We can encounter textnodes here -- must find a row
   260         while (rowNode && !nsHTMLEditUtils::IsTableRow(rowNode))
   261         {
   262           nsCOMPtr<nsIDOMNode> nextNode;
   263           res = rowNode->GetNextSibling(getter_AddRefs(nextNode));
   264           NS_ENSURE_SUCCESS(res, res);
   266           rowNode = nextNode;
   267         }
   268         if(rowNode)
   269         {
   270           *aRowNode = rowNode.get();
   271           NS_ADDREF(*aRowNode);
   272           return NS_OK;
   273         }
   274       }
   275     }
   276     // Here if table child was a CAPTION or COLGROUP
   277     //  or child of a row parent wasn't a row (bad HTML?),
   278     //  or first child was a textnode
   279     // Look in next table child
   280     nsCOMPtr<nsIDOMNode> nextChild;
   281     res = tableChild->GetNextSibling(getter_AddRefs(nextChild));
   282     NS_ENSURE_SUCCESS(res, res);
   284     tableChild = nextChild;
   285   };
   286   // If here, row was not found
   287   return NS_EDITOR_ELEMENT_NOT_FOUND;
   288 }
   290 NS_IMETHODIMP 
   291 nsHTMLEditor::GetNextRow(nsIDOMNode* aCurrentRowNode, nsIDOMNode **aRowNode)
   292 {
   293   NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
   295   *aRowNode = nullptr;  
   297   NS_ENSURE_TRUE(aCurrentRowNode, NS_ERROR_NULL_POINTER);
   299   if (!nsHTMLEditUtils::IsTableRow(aCurrentRowNode))
   300     return NS_ERROR_FAILURE;
   302   nsCOMPtr<nsIDOMNode> nextRow;
   303   nsresult res = aCurrentRowNode->GetNextSibling(getter_AddRefs(nextRow));
   304   NS_ENSURE_SUCCESS(res, res);
   306   nsCOMPtr<nsIDOMNode> nextNode;
   308   // Skip over any textnodes here
   309   while (nextRow && !nsHTMLEditUtils::IsTableRow(nextRow))
   310   {
   311     res = nextRow->GetNextSibling(getter_AddRefs(nextNode));
   312     NS_ENSURE_SUCCESS(res, res);
   314     nextRow = nextNode;
   315   }
   316   if(nextRow)
   317   {
   318     *aRowNode = nextRow.get();
   319     NS_ADDREF(*aRowNode);
   320     return NS_OK;
   321   }
   323   // No row found, search for rows in other table sections
   324   nsCOMPtr<nsIDOMNode> rowParent;
   325   res = aCurrentRowNode->GetParentNode(getter_AddRefs(rowParent));
   326   NS_ENSURE_SUCCESS(res, res);
   327   NS_ENSURE_TRUE(rowParent, NS_ERROR_NULL_POINTER);
   329   nsCOMPtr<nsIDOMNode> parentSibling;
   330   res = rowParent->GetNextSibling(getter_AddRefs(parentSibling));
   331   NS_ENSURE_SUCCESS(res, res);
   333   while (parentSibling)
   334   {
   335     res = parentSibling->GetFirstChild(getter_AddRefs(nextRow));
   336     NS_ENSURE_SUCCESS(res, res);
   338     // We can encounter textnodes here -- must find a row
   339     while (nextRow && !nsHTMLEditUtils::IsTableRow(nextRow))
   340     {
   341       res = nextRow->GetNextSibling(getter_AddRefs(nextNode));
   342       NS_ENSURE_SUCCESS(res, res);
   344       nextRow = nextNode;
   345     }
   346     if(nextRow)
   347     {
   348       *aRowNode = nextRow.get();
   349       NS_ADDREF(*aRowNode);
   350       return NS_OK;
   351     }
   352 #ifdef DEBUG_cmanske
   353     printf("GetNextRow: firstChild of row's parent's sibling is not a TR!\n");
   354 #endif
   355     // We arrive here only if a table section has no children 
   356     //  or first child of section is not a row (bad HTML or more "_moz_text" nodes!)
   357     // So look for another section sibling
   358     res = parentSibling->GetNextSibling(getter_AddRefs(nextNode));
   359     NS_ENSURE_SUCCESS(res, res);
   361     parentSibling = nextNode;
   362   }
   363   // If here, row was not found
   364   return NS_EDITOR_ELEMENT_NOT_FOUND;
   365 }
   367 NS_IMETHODIMP 
   368 nsHTMLEditor::GetLastCellInRow(nsIDOMNode* aRowNode, nsIDOMNode** aCellNode)
   369 {
   370   NS_ENSURE_TRUE(aCellNode, NS_ERROR_NULL_POINTER);
   372   *aCellNode = nullptr;
   374   NS_ENSURE_TRUE(aRowNode, NS_ERROR_NULL_POINTER);
   376   nsCOMPtr<nsIDOMNode> rowChild;
   377   nsresult res = aRowNode->GetLastChild(getter_AddRefs(rowChild));
   378   NS_ENSURE_SUCCESS(res, res);
   380   while (rowChild && !nsHTMLEditUtils::IsTableCell(rowChild))
   381   {
   382     // Skip over textnodes
   383     nsCOMPtr<nsIDOMNode> previousChild;
   384     res = rowChild->GetPreviousSibling(getter_AddRefs(previousChild));
   385     NS_ENSURE_SUCCESS(res, res);
   387     rowChild = previousChild;
   388   };
   389   if (rowChild)
   390   {
   391     *aCellNode = rowChild.get();
   392     NS_ADDREF(*aCellNode);
   393     return NS_OK;
   394   }
   395   // If here, cell was not found
   396   return NS_EDITOR_ELEMENT_NOT_FOUND;
   397 }
   399 NS_IMETHODIMP
   400 nsHTMLEditor::InsertTableColumn(int32_t aNumber, bool aAfter)
   401 {
   402   nsCOMPtr<nsISelection> selection;
   403   nsCOMPtr<nsIDOMElement> table;
   404   nsCOMPtr<nsIDOMElement> curCell;
   405   int32_t startRowIndex, startColIndex;
   406   nsresult res = GetCellContext(getter_AddRefs(selection),
   407                                 getter_AddRefs(table), 
   408                                 getter_AddRefs(curCell), 
   409                                 nullptr, nullptr,
   410                                 &startRowIndex, &startColIndex);
   411   NS_ENSURE_SUCCESS(res, res);
   412   // Don't fail if no cell found
   413   NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND);
   415   // Get more data for current cell (we need ROWSPAN)
   416   int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
   417   bool    isSelected;
   418   res = GetCellDataAt(table, startRowIndex, startColIndex,
   419                       getter_AddRefs(curCell),
   420                       &curStartRowIndex, &curStartColIndex,
   421                       &rowSpan, &colSpan, 
   422                       &actualRowSpan, &actualColSpan, &isSelected);
   423   NS_ENSURE_SUCCESS(res, res);
   424   NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
   426   nsAutoEditBatch beginBatching(this);
   427   // Prevent auto insertion of BR in new cell until we're done
   428   nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
   430   // Use column after current cell if requested
   431   if (aAfter)
   432   {
   433     startColIndex += actualColSpan;
   434     //Detect when user is adding after a COLSPAN=0 case
   435     // Assume they want to stop the "0" behavior and
   436     // really add a new column. Thus we set the 
   437     // colspan to its true value
   438     if (colSpan == 0)
   439       SetColSpan(curCell, actualColSpan);
   440   }
   442   int32_t rowCount, colCount, rowIndex;
   443   res = GetTableSize(table, &rowCount, &colCount);
   444   NS_ENSURE_SUCCESS(res, res);
   446   //We reset caret in destructor...
   447   nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false);
   448   //.. so suppress Rules System selection munging
   449   nsAutoTxnsConserveSelection dontChangeSelection(this);
   451   // If we are inserting after all existing columns
   452   // Make sure table is "well formed"
   453   //  before appending new column
   454   if (startColIndex >= colCount)
   455     NormalizeTable(table);
   457   nsCOMPtr<nsIDOMNode> rowNode;
   458   for ( rowIndex = 0; rowIndex < rowCount; rowIndex++)
   459   {
   460 #ifdef DEBUG_cmanske
   461     if (rowIndex == rowCount-1)
   462       printf(" ***InsertTableColumn: Inserting cell at last row: %d\n", rowIndex);
   463 #endif
   465     if (startColIndex < colCount)
   466     {
   467       // We are inserting before an existing column
   468       res = GetCellDataAt(table, rowIndex, startColIndex,
   469                           getter_AddRefs(curCell),
   470                           &curStartRowIndex, &curStartColIndex,
   471                           &rowSpan, &colSpan, 
   472                           &actualRowSpan, &actualColSpan, &isSelected);
   473       NS_ENSURE_SUCCESS(res, res);
   475       // Don't fail entire process if we fail to find a cell
   476       //  (may fail just in particular rows with < adequate cells per row)
   477       if (curCell)
   478       {
   479         if (curStartColIndex < startColIndex)
   480         {
   481           // We have a cell spanning this location
   482           // Simply increase its colspan to keep table rectangular
   483           // Note: we do nothing if colsSpan=0,
   484           //  since it should automatically span the new column
   485           if (colSpan > 0)
   486             SetColSpan(curCell, colSpan+aNumber);
   487         } else {
   488           // Simply set selection to the current cell 
   489           //  so we can let InsertTableCell() do the work
   490           // Insert a new cell before current one
   491           selection->Collapse(curCell, 0);
   492           res = InsertTableCell(aNumber, false);
   493         }
   494       }
   495     } else {
   496       // Get current row and append new cells after last cell in row
   497       if(rowIndex == 0)
   498         res = GetFirstRow(table.get(), getter_AddRefs(rowNode));
   499       else
   500       {
   501         nsCOMPtr<nsIDOMNode> nextRow;
   502         res = GetNextRow(rowNode.get(), getter_AddRefs(nextRow));
   503         rowNode = nextRow;
   504       }
   505       NS_ENSURE_SUCCESS(res, res);
   507       if (rowNode)
   508       {
   509         nsCOMPtr<nsIDOMNode> lastCell;
   510         res = GetLastCellInRow(rowNode, getter_AddRefs(lastCell));
   511         NS_ENSURE_SUCCESS(res, res);
   512         NS_ENSURE_TRUE(lastCell, NS_ERROR_FAILURE);
   514         curCell = do_QueryInterface(lastCell);
   515         if (curCell)
   516         {
   517           // Simply add same number of cells to each row
   518           // Although tempted to check cell indexes for curCell,
   519           //  the effects of COLSPAN>1 in some cells makes this futile!
   520           // We must use NormalizeTable first to assure
   521           //  that there are cells in each cellmap location
   522           selection->Collapse(curCell, 0);
   523           res = InsertTableCell(aNumber, true);
   524         }
   525       }
   526     }
   527   }
   528   return res;
   529 }
   531 NS_IMETHODIMP
   532 nsHTMLEditor::InsertTableRow(int32_t aNumber, bool aAfter)
   533 {
   534   nsCOMPtr<nsISelection> selection;
   535   nsCOMPtr<nsIDOMElement> table;
   536   nsCOMPtr<nsIDOMElement> curCell;
   538   int32_t startRowIndex, startColIndex;
   539   nsresult res = GetCellContext(nullptr,
   540                                 getter_AddRefs(table), 
   541                                 getter_AddRefs(curCell), 
   542                                 nullptr, nullptr, 
   543                                 &startRowIndex, &startColIndex);
   544   NS_ENSURE_SUCCESS(res, res);
   545   // Don't fail if no cell found
   546   NS_ENSURE_TRUE(curCell, NS_EDITOR_ELEMENT_NOT_FOUND);
   548   // Get more data for current cell in row we are inserting at (we need COLSPAN)
   549   int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
   550   bool    isSelected;
   551   res = GetCellDataAt(table, startRowIndex, startColIndex,
   552                       getter_AddRefs(curCell),
   553                       &curStartRowIndex, &curStartColIndex,
   554                       &rowSpan, &colSpan, 
   555                       &actualRowSpan, &actualColSpan, &isSelected);
   556   NS_ENSURE_SUCCESS(res, res);
   557   NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
   559   int32_t rowCount, colCount;
   560   res = GetTableSize(table, &rowCount, &colCount);
   561   NS_ENSURE_SUCCESS(res, res);
   563   nsAutoEditBatch beginBatching(this);
   564   // Prevent auto insertion of BR in new cell until we're done
   565   nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
   567   if (aAfter)
   568   {
   569     // Use row after current cell
   570     startRowIndex += actualRowSpan;
   572     //Detect when user is adding after a ROWSPAN=0 case
   573     // Assume they want to stop the "0" behavior and
   574     // really add a new row. Thus we set the 
   575     // rowspan to its true value
   576     if (rowSpan == 0)
   577       SetRowSpan(curCell, actualRowSpan);
   578   }
   580   //We control selection resetting after the insert...
   581   nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false);
   582   //...so suppress Rules System selection munging
   583   nsAutoTxnsConserveSelection dontChangeSelection(this);
   585   nsCOMPtr<nsIDOMElement> cellForRowParent;
   586   int32_t cellsInRow = 0;
   587   if (startRowIndex < rowCount)
   588   {
   589     // We are inserting above an existing row
   590     // Get each cell in the insert row to adjust for COLSPAN effects while we
   591     //   count how many cells are needed
   592     int32_t colIndex = 0;
   593     // This returns NS_TABLELAYOUT_CELL_NOT_FOUND when we run past end of row,
   594     //   which passes the NS_SUCCEEDED macro
   595     while ( NS_OK == GetCellDataAt(table, startRowIndex, colIndex,
   596                                    getter_AddRefs(curCell), 
   597                                    &curStartRowIndex, &curStartColIndex,
   598                                    &rowSpan, &colSpan, 
   599                                    &actualRowSpan, &actualColSpan,
   600                                    &isSelected) )
   601     {
   602       if (curCell)
   603       {
   604         if (curStartRowIndex < startRowIndex)
   605         {
   606           // We have a cell spanning this location
   607           // Simply increase its rowspan
   608           //Note that if rowSpan == 0, we do nothing,
   609           //  since that cell should automatically extend into the new row
   610           if (rowSpan > 0)
   611             SetRowSpan(curCell, rowSpan+aNumber);
   612         } else {
   613           // We have a cell in the insert row
   615           // Count the number of cells we need to add to the new row
   616           cellsInRow += actualColSpan;
   618           // Save cell we will use below
   619           if (!cellForRowParent)
   620             cellForRowParent = curCell;
   621         }
   622         // Next cell in row
   623         colIndex += actualColSpan;
   624       }
   625       else
   626         colIndex++;
   627     }
   628   } else {
   629     // We are adding a new row after all others
   630     // If it weren't for colspan=0 effect, 
   631     // we could simply use colCount for number of new cells...
   632     cellsInRow = colCount;
   634     // ...but we must compensate for all cells with rowSpan = 0 in the last row
   635     int32_t lastRow = rowCount-1;
   636     int32_t tempColIndex = 0;
   637     while ( NS_OK == GetCellDataAt(table, lastRow, tempColIndex,
   638                                    getter_AddRefs(curCell), 
   639                                    &curStartRowIndex, &curStartColIndex,
   640                                    &rowSpan, &colSpan, 
   641                                    &actualRowSpan, &actualColSpan,
   642                                    &isSelected) )
   643     {
   644       if (rowSpan == 0)
   645         cellsInRow -= actualColSpan;
   647       tempColIndex += actualColSpan;
   649       // Save cell from the last row that we will use below
   650       if (!cellForRowParent && curStartRowIndex == lastRow)
   651         cellForRowParent = curCell;
   652     }
   653   }
   655   if (cellsInRow > 0)
   656   {
   657     // The row parent and offset where we will insert new row
   658     nsCOMPtr<nsIDOMNode> parentOfRow;
   659     int32_t newRowOffset;
   661     NS_NAMED_LITERAL_STRING(trStr, "tr");
   662     if (cellForRowParent)
   663     {
   664       nsCOMPtr<nsIDOMElement> parentRow;
   665       res = GetElementOrParentByTagName(trStr, cellForRowParent, getter_AddRefs(parentRow));
   666       NS_ENSURE_SUCCESS(res, res);
   667       NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER);
   669       parentRow->GetParentNode(getter_AddRefs(parentOfRow));
   670       NS_ENSURE_TRUE(parentOfRow, NS_ERROR_NULL_POINTER);
   672       newRowOffset = GetChildOffset(parentRow, parentOfRow);
   674       // Adjust for when adding past the end 
   675       if (aAfter && startRowIndex >= rowCount)
   676         newRowOffset++;
   677     }
   678     else
   679       return NS_ERROR_FAILURE;
   681     for (int32_t row = 0; row < aNumber; row++)
   682     {
   683       // Create a new row
   684       nsCOMPtr<nsIDOMElement> newRow;
   685       res = CreateElementWithDefaults(trStr, getter_AddRefs(newRow));
   686       if (NS_SUCCEEDED(res))
   687       {
   688         NS_ENSURE_TRUE(newRow, NS_ERROR_FAILURE);
   690         for (int32_t i = 0; i < cellsInRow; i++)
   691         {
   692           nsCOMPtr<nsIDOMElement> newCell;
   693           res = CreateElementWithDefaults(NS_LITERAL_STRING("td"), getter_AddRefs(newCell));
   694           NS_ENSURE_SUCCESS(res, res);
   695           NS_ENSURE_TRUE(newCell, NS_ERROR_FAILURE);
   697           // Don't use transaction system yet! (not until entire row is inserted)
   698           nsCOMPtr<nsIDOMNode>resultNode;
   699           res = newRow->AppendChild(newCell, getter_AddRefs(resultNode));
   700           NS_ENSURE_SUCCESS(res, res);
   701         }
   702         // Use transaction system to insert the entire row+cells
   703         // (Note that rows are inserted at same childoffset each time)
   704         res = InsertNode(newRow, parentOfRow, newRowOffset);
   705         NS_ENSURE_SUCCESS(res, res);
   706       }
   707     }
   708   }
   709   return res;
   710 }
   712 // Editor helper only
   713 // XXX Code changed for bug 217717 and now we don't need aSelection param
   714 //     TODO: Remove aSelection param
   715 NS_IMETHODIMP
   716 nsHTMLEditor::DeleteTable2(nsIDOMElement *aTable, nsISelection *aSelection)
   717 {
   718   NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
   720   // Select the table
   721   nsresult res = ClearSelection();
   722   if (NS_SUCCEEDED(res))
   723     res = AppendNodeToSelectionAsRange(aTable);
   724   NS_ENSURE_SUCCESS(res, res);
   726   return DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
   727 }
   729 NS_IMETHODIMP
   730 nsHTMLEditor::DeleteTable()
   731 {
   732   nsCOMPtr<nsISelection> selection;
   733   nsCOMPtr<nsIDOMElement> table;
   734   nsresult res = GetCellContext(getter_AddRefs(selection),
   735                                 getter_AddRefs(table), 
   736                                 nullptr, nullptr, nullptr, nullptr, nullptr);
   738   NS_ENSURE_SUCCESS(res, res);
   740   nsAutoEditBatch beginBatching(this);
   741   return DeleteTable2(table, selection);
   742 }
   744 NS_IMETHODIMP
   745 nsHTMLEditor::DeleteTableCell(int32_t aNumber)
   746 {
   747   nsCOMPtr<nsISelection> selection;
   748   nsCOMPtr<nsIDOMElement> table;
   749   nsCOMPtr<nsIDOMElement> cell;
   750   int32_t startRowIndex, startColIndex;
   753   nsresult res = GetCellContext(getter_AddRefs(selection),
   754                          getter_AddRefs(table), 
   755                          getter_AddRefs(cell), 
   756                          nullptr, nullptr,
   757                          &startRowIndex, &startColIndex);
   759   NS_ENSURE_SUCCESS(res, res);
   760   // Don't fail if we didn't find a table or cell
   761   NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND);
   763   nsAutoEditBatch beginBatching(this);
   764   // Prevent rules testing until we're done
   765   nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
   767   nsCOMPtr<nsIDOMElement> firstCell;
   768   nsCOMPtr<nsIDOMRange> range;
   769   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
   770   NS_ENSURE_SUCCESS(res, res);
   772   int32_t rangeCount;
   773   res = selection->GetRangeCount(&rangeCount);
   774   NS_ENSURE_SUCCESS(res, res);
   776   if (firstCell && rangeCount > 1)
   777   {
   778     // When > 1 selected cell,
   779     //  ignore aNumber and use selected cells
   780     cell = firstCell;
   782     int32_t rowCount, colCount;
   783     res = GetTableSize(table, &rowCount, &colCount);
   784     NS_ENSURE_SUCCESS(res, res);
   786     // Get indexes -- may be different than original cell
   787     res = GetCellIndexes(cell, &startRowIndex, &startColIndex);
   788     NS_ENSURE_SUCCESS(res, res);
   790     // The setCaret object will call SetSelectionAfterTableEdit in its destructor
   791     nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false);
   792     nsAutoTxnsConserveSelection dontChangeSelection(this);
   794     bool    checkToDeleteRow = true;
   795     bool    checkToDeleteColumn = true;
   796     while (cell)
   797     {
   798       bool deleteRow = false;
   799       bool deleteCol = false;
   801       if (checkToDeleteRow)
   802       {
   803         // Optimize to delete an entire row
   804         // Clear so we don't repeat AllCellsInRowSelected within the same row
   805         checkToDeleteRow = false;
   807         deleteRow = AllCellsInRowSelected(table, startRowIndex, colCount);
   808         if (deleteRow)
   809         {
   810           // First, find the next cell in a different row
   811           //   to continue after we delete this row
   812           int32_t nextRow = startRowIndex;
   813           while (nextRow == startRowIndex)
   814           {
   815             res = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
   816             NS_ENSURE_SUCCESS(res, res);
   817             if (!cell) break;
   818             res = GetCellIndexes(cell, &nextRow, &startColIndex);
   819             NS_ENSURE_SUCCESS(res, res);
   820           }
   821           // Delete entire row
   822           res = DeleteRow(table, startRowIndex);          
   823           NS_ENSURE_SUCCESS(res, res);
   825           if (cell)
   826           {
   827             // For the next cell: Subtract 1 for row we deleted
   828             startRowIndex = nextRow - 1;
   829             // Set true since we know we will look at a new row next
   830             checkToDeleteRow = true;
   831           }
   832         }
   833       }
   834       if (!deleteRow)
   835       {
   836         if (checkToDeleteColumn)
   837         {
   838           // Optimize to delete an entire column
   839           // Clear this so we don't repeat AllCellsInColSelected within the same Col
   840           checkToDeleteColumn = false;
   842           deleteCol = AllCellsInColumnSelected(table, startColIndex, colCount);
   843           if (deleteCol)
   844           {
   845             // First, find the next cell in a different column
   846             //   to continue after we delete this column
   847             int32_t nextCol = startColIndex;
   848             while (nextCol == startColIndex)
   849             {
   850               res = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
   851               NS_ENSURE_SUCCESS(res, res);
   852               if (!cell) break;
   853               res = GetCellIndexes(cell, &startRowIndex, &nextCol);
   854               NS_ENSURE_SUCCESS(res, res);
   855             }
   856             // Delete entire Col
   857             res = DeleteColumn(table, startColIndex);          
   858             NS_ENSURE_SUCCESS(res, res);
   859             if (cell) 
   860             {
   861               // For the next cell, subtract 1 for col. deleted
   862               startColIndex = nextCol - 1;
   863               // Set true since we know we will look at a new column next
   864               checkToDeleteColumn = true;
   865             }
   866           }
   867         }
   868         if (!deleteCol)
   869         {
   870           // First get the next cell to delete
   871           nsCOMPtr<nsIDOMElement> nextCell;
   872           res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(nextCell));
   873           NS_ENSURE_SUCCESS(res, res);
   875           // Then delete the cell
   876           res = DeleteNode(cell);
   877           NS_ENSURE_SUCCESS(res, res);
   879           // The next cell to delete
   880           cell = nextCell;
   881           if (cell)
   882           {
   883             res = GetCellIndexes(cell, &startRowIndex, &startColIndex);
   884             NS_ENSURE_SUCCESS(res, res);
   885           }
   886         }
   887       }
   888     }
   889   }
   890   else for (int32_t i = 0; i < aNumber; i++)
   891   {
   892     res = GetCellContext(getter_AddRefs(selection),
   893                          getter_AddRefs(table), 
   894                          getter_AddRefs(cell), 
   895                          nullptr, nullptr,
   896                          &startRowIndex, &startColIndex);
   897     NS_ENSURE_SUCCESS(res, res);
   898     // Don't fail if no cell found
   899     NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
   901     if (1 == GetNumberOfCellsInRow(table, startRowIndex))
   902     {
   903       nsCOMPtr<nsIDOMElement> parentRow;
   904       res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell, getter_AddRefs(parentRow));
   905       NS_ENSURE_SUCCESS(res, res);
   906       NS_ENSURE_TRUE(parentRow, NS_ERROR_NULL_POINTER);
   908       // We should delete the row instead,
   909       //  but first check if its the only row left
   910       //  so we can delete the entire table
   911       int32_t rowCount, colCount;
   912       res = GetTableSize(table, &rowCount, &colCount);
   913       NS_ENSURE_SUCCESS(res, res);
   915       if (rowCount == 1)
   916         return DeleteTable2(table, selection);
   918       // We need to call DeleteTableRow to handle cells with rowspan 
   919       res = DeleteTableRow(1);
   920       NS_ENSURE_SUCCESS(res, res);
   921     } 
   922     else
   923     {
   924       // More than 1 cell in the row
   926       // The setCaret object will call SetSelectionAfterTableEdit in its destructor
   927       nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false);
   928       nsAutoTxnsConserveSelection dontChangeSelection(this);
   930       res = DeleteNode(cell);
   931       // If we fail, don't try to delete any more cells???
   932       NS_ENSURE_SUCCESS(res, res);
   933     }
   934   }
   935   return NS_OK;
   936 }
   938 NS_IMETHODIMP
   939 nsHTMLEditor::DeleteTableCellContents()
   940 {
   941   nsCOMPtr<nsISelection> selection;
   942   nsCOMPtr<nsIDOMElement> table;
   943   nsCOMPtr<nsIDOMElement> cell;
   944   int32_t startRowIndex, startColIndex;
   945   nsresult res;
   946   res = GetCellContext(getter_AddRefs(selection),
   947                        getter_AddRefs(table), 
   948                        getter_AddRefs(cell), 
   949                        nullptr, nullptr,
   950                        &startRowIndex, &startColIndex);
   951   NS_ENSURE_SUCCESS(res, res);
   952   // Don't fail if no cell found
   953   NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
   956   nsAutoEditBatch beginBatching(this);
   957   // Prevent rules testing until we're done
   958   nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
   959   //Don't let Rules System change the selection
   960   nsAutoTxnsConserveSelection dontChangeSelection(this);
   963   nsCOMPtr<nsIDOMElement> firstCell;
   964   nsCOMPtr<nsIDOMRange> range;
   965   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
   966   NS_ENSURE_SUCCESS(res, res);
   969   if (firstCell)
   970   {
   971     cell = firstCell;
   972     res = GetCellIndexes(cell, &startRowIndex, &startColIndex);
   973     NS_ENSURE_SUCCESS(res, res);
   974   }
   976   nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false);
   978   while (cell)
   979   {
   980     DeleteCellContents(cell);
   981     if (firstCell)
   982     {
   983       // We doing a selected cells, so do all of them
   984       res = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
   985       NS_ENSURE_SUCCESS(res, res);
   986     }
   987     else
   988       cell = nullptr;
   989   }
   990   return NS_OK;
   991 }
   993 NS_IMETHODIMP
   994 nsHTMLEditor::DeleteCellContents(nsIDOMElement *aCell)
   995 {
   996   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
   998   // Prevent rules testing until we're done
   999   nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
  1001   nsCOMPtr<nsIDOMNode> child;
  1002   bool hasChild;
  1003   aCell->HasChildNodes(&hasChild);
  1005   while (hasChild)
  1007     aCell->GetLastChild(getter_AddRefs(child));
  1008     nsresult res = DeleteNode(child);
  1009     NS_ENSURE_SUCCESS(res, res);
  1010     aCell->HasChildNodes(&hasChild);
  1012   return NS_OK;
  1015 NS_IMETHODIMP
  1016 nsHTMLEditor::DeleteTableColumn(int32_t aNumber)
  1018   nsCOMPtr<nsISelection> selection;
  1019   nsCOMPtr<nsIDOMElement> table;
  1020   nsCOMPtr<nsIDOMElement> cell;
  1021   int32_t startRowIndex, startColIndex, rowCount, colCount;
  1022   nsresult res = GetCellContext(getter_AddRefs(selection),
  1023                                 getter_AddRefs(table), 
  1024                                 getter_AddRefs(cell), 
  1025                                 nullptr, nullptr,
  1026                                 &startRowIndex, &startColIndex);
  1027   NS_ENSURE_SUCCESS(res, res);
  1028   // Don't fail if no cell found
  1029   NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND);
  1031   res = GetTableSize(table, &rowCount, &colCount);
  1032   NS_ENSURE_SUCCESS(res, res);
  1034   // Shortcut the case of deleting all columns in table
  1035   if(startColIndex == 0 && aNumber >= colCount)
  1036     return DeleteTable2(table, selection);
  1038   // Check for counts too high
  1039   aNumber = std::min(aNumber,(colCount-startColIndex));
  1041   nsAutoEditBatch beginBatching(this);
  1042   // Prevent rules testing until we're done
  1043   nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
  1045   // Test if deletion is controlled by selected cells
  1046   nsCOMPtr<nsIDOMElement> firstCell;
  1047   nsCOMPtr<nsIDOMRange> range;
  1048   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
  1049   NS_ENSURE_SUCCESS(res, res);
  1051   int32_t rangeCount;
  1052   res = selection->GetRangeCount(&rangeCount);
  1053   NS_ENSURE_SUCCESS(res, res);
  1055   if (firstCell && rangeCount > 1)
  1057     // Fetch indexes again - may be different for selected cells
  1058     res = GetCellIndexes(firstCell, &startRowIndex, &startColIndex);
  1059     NS_ENSURE_SUCCESS(res, res);
  1061   //We control selection resetting after the insert...
  1062   nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false);
  1064   if (firstCell && rangeCount > 1)
  1066     // Use selected cells to determine what rows to delete
  1067     cell = firstCell;
  1069     while (cell)
  1071       if (cell != firstCell)
  1073         res = GetCellIndexes(cell, &startRowIndex, &startColIndex);
  1074         NS_ENSURE_SUCCESS(res, res);
  1076       // Find the next cell in a different column
  1077       // to continue after we delete this column
  1078       int32_t nextCol = startColIndex;
  1079       while (nextCol == startColIndex)
  1081         res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
  1082         NS_ENSURE_SUCCESS(res, res);
  1083         if (!cell) break;
  1084         res = GetCellIndexes(cell, &startRowIndex, &nextCol);
  1085         NS_ENSURE_SUCCESS(res, res);
  1087       res = DeleteColumn(table, startColIndex);          
  1088       NS_ENSURE_SUCCESS(res, res);
  1091   else for (int32_t i = 0; i < aNumber; i++)
  1093     res = DeleteColumn(table, startColIndex);
  1094     NS_ENSURE_SUCCESS(res, res);
  1096   return NS_OK;
  1099 NS_IMETHODIMP
  1100 nsHTMLEditor::DeleteColumn(nsIDOMElement *aTable, int32_t aColIndex)
  1102   NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
  1104   nsCOMPtr<nsIDOMElement> cell;
  1105   nsCOMPtr<nsIDOMElement> cellInDeleteCol;
  1106   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  1107   bool    isSelected;
  1108   int32_t rowIndex = 0;
  1109   nsresult res = NS_OK;
  1111   do {
  1112     res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
  1113                         &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  1114                         &actualRowSpan, &actualColSpan, &isSelected);
  1116     NS_ENSURE_SUCCESS(res, res);
  1118     if (cell)
  1120       // Find cells that don't start in column we are deleting
  1121       if (startColIndex < aColIndex || colSpan > 1 || colSpan == 0)
  1123         // We have a cell spanning this location
  1124         // Decrease its colspan to keep table rectangular,
  1125         // but if colSpan=0, it will adjust automatically
  1126         if (colSpan > 0)
  1128           NS_ASSERTION((colSpan > 1),"Bad COLSPAN in DeleteTableColumn");
  1129           SetColSpan(cell, colSpan-1);
  1131         if (startColIndex == aColIndex)
  1133           // Cell is in column to be deleted, but must have colspan > 1,
  1134           // so delete contents of cell instead of cell itself
  1135           // (We must have reset colspan above)
  1136           DeleteCellContents(cell);
  1138         // To next cell in column
  1139         rowIndex += actualRowSpan;
  1141       else 
  1143         // Delete the cell
  1144         if (1 == GetNumberOfCellsInRow(aTable, rowIndex))
  1146           // Only 1 cell in row - delete the row
  1147           nsCOMPtr<nsIDOMElement> parentRow;
  1148           res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cell, getter_AddRefs(parentRow));
  1149           NS_ENSURE_SUCCESS(res, res);
  1150           if(!parentRow) return NS_ERROR_NULL_POINTER;
  1152           //  But first check if its the only row left
  1153           //  so we can delete the entire table
  1154           //  (This should never happen but it's the safe thing to do)
  1155           int32_t rowCount, colCount;
  1156           res = GetTableSize(aTable, &rowCount, &colCount);
  1157           NS_ENSURE_SUCCESS(res, res);
  1159           if (rowCount == 1)
  1161             nsCOMPtr<nsISelection> selection;
  1162             res = GetSelection(getter_AddRefs(selection));
  1163             NS_ENSURE_SUCCESS(res, res);
  1164             NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1165             return DeleteTable2(aTable, selection);
  1168           // Delete the row by placing caret in cell we were to delete
  1169           // We need to call DeleteTableRow to handle cells with rowspan 
  1170           res = DeleteRow(aTable, startRowIndex);
  1171           NS_ENSURE_SUCCESS(res, res);
  1173           // Note that we don't incremenet rowIndex
  1174           // since a row was deleted and "next" 
  1175           // row now has current rowIndex
  1177         else 
  1179           // A more "normal" deletion
  1180           res = DeleteNode(cell);
  1181           NS_ENSURE_SUCCESS(res, res);
  1183           //Skip over any rows spanned by this cell
  1184           rowIndex += actualRowSpan;
  1188   } while (cell);    
  1190   return NS_OK;
  1193 NS_IMETHODIMP
  1194 nsHTMLEditor::DeleteTableRow(int32_t aNumber)
  1196   nsCOMPtr<nsISelection> selection;
  1197   nsCOMPtr<nsIDOMElement> table;
  1198   nsCOMPtr<nsIDOMElement> cell;
  1199   int32_t startRowIndex, startColIndex;
  1200   int32_t rowCount, colCount;
  1201   nsresult res =  GetCellContext(getter_AddRefs(selection),
  1202                                  getter_AddRefs(table), 
  1203                                  getter_AddRefs(cell), 
  1204                                  nullptr, nullptr,
  1205                                  &startRowIndex, &startColIndex);
  1206   NS_ENSURE_SUCCESS(res, res);
  1207   // Don't fail if no cell found
  1208   NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
  1210   res = GetTableSize(table, &rowCount, &colCount);
  1211   NS_ENSURE_SUCCESS(res, res);
  1213   // Shortcut the case of deleting all rows in table
  1214   if(startRowIndex == 0 && aNumber >= rowCount)
  1215     return DeleteTable2(table, selection);
  1217   nsAutoEditBatch beginBatching(this);
  1218   // Prevent rules testing until we're done
  1219   nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
  1221   nsCOMPtr<nsIDOMElement> firstCell;
  1222   nsCOMPtr<nsIDOMRange> range;
  1223   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
  1224   NS_ENSURE_SUCCESS(res, res);
  1226   int32_t rangeCount;
  1227   res = selection->GetRangeCount(&rangeCount);
  1228   NS_ENSURE_SUCCESS(res, res);
  1230   if (firstCell && rangeCount > 1)
  1232     // Fetch indexes again - may be different for selected cells
  1233     res = GetCellIndexes(firstCell, &startRowIndex, &startColIndex);
  1234     NS_ENSURE_SUCCESS(res, res);
  1237   //We control selection resetting after the insert...
  1238   nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousRow, false);
  1239   // Don't change selection during deletions
  1240   nsAutoTxnsConserveSelection dontChangeSelection(this);
  1242   if (firstCell && rangeCount > 1)
  1244     // Use selected cells to determine what rows to delete
  1245     cell = firstCell;
  1247     while (cell)
  1249       if (cell != firstCell)
  1251         res = GetCellIndexes(cell, &startRowIndex, &startColIndex);
  1252         NS_ENSURE_SUCCESS(res, res);
  1254       // Find the next cell in a different row
  1255       // to continue after we delete this row
  1256       int32_t nextRow = startRowIndex;
  1257       while (nextRow == startRowIndex)
  1259         res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
  1260         NS_ENSURE_SUCCESS(res, res);
  1261         if (!cell) break;
  1262         res = GetCellIndexes(cell, &nextRow, &startColIndex);
  1263         NS_ENSURE_SUCCESS(res, res);
  1265       // Delete entire row
  1266       res = DeleteRow(table, startRowIndex);          
  1267       NS_ENSURE_SUCCESS(res, res);
  1270   else
  1272     // Check for counts too high
  1273     aNumber = std::min(aNumber,(rowCount-startRowIndex));
  1275     for (int32_t i = 0; i < aNumber; i++)
  1277       res = DeleteRow(table, startRowIndex);
  1278       // If failed in current row, try the next
  1279       if (NS_FAILED(res))
  1280         startRowIndex++;
  1282       // Check if there's a cell in the "next" row
  1283       res = GetCellAt(table, startRowIndex, startColIndex, getter_AddRefs(cell));
  1284       NS_ENSURE_SUCCESS(res, res);
  1285       if(!cell)
  1286         break;
  1289   return NS_OK;
  1292 // Helper that doesn't batch or change the selection
  1293 NS_IMETHODIMP
  1294 nsHTMLEditor::DeleteRow(nsIDOMElement *aTable, int32_t aRowIndex)
  1296   NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
  1298   nsCOMPtr<nsIDOMElement> cell;
  1299   nsCOMPtr<nsIDOMElement> cellInDeleteRow;
  1300   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  1301   bool    isSelected;
  1302   int32_t colIndex = 0;
  1303   nsresult res = NS_OK;
  1305   // Prevent rules testing until we're done
  1306   nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
  1308   // The list of cells we will change rowspan in
  1309   //  and the new rowspan values for each
  1310   nsTArray<nsCOMPtr<nsIDOMElement> > spanCellList;
  1311   nsTArray<int32_t> newSpanList;
  1313   int32_t rowCount, colCount;
  1314   res = GetTableSize(aTable, &rowCount, &colCount);
  1315   NS_ENSURE_SUCCESS(res, res);
  1317   // Scan through cells in row to do rowspan adjustments
  1318   // Note that after we delete row, startRowIndex will point to the
  1319   //   cells in the next row to be deleted
  1320   do {
  1321     if (aRowIndex >= rowCount || colIndex >= colCount)
  1322       break;
  1324     res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
  1325                         &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  1326                         &actualRowSpan, &actualColSpan, &isSelected);
  1328     // We don't fail if we don't find a cell, so this must be real bad
  1329     if(NS_FAILED(res)) return res;
  1331     // Compensate for cells that don't start or extend below the row we are deleting
  1332     if (cell)
  1334       if (startRowIndex < aRowIndex)
  1336         // Cell starts in row above us
  1337         // Decrease its rowspan to keep table rectangular
  1338         //  but we don't need to do this if rowspan=0,
  1339         //  since it will automatically adjust
  1340         if (rowSpan > 0)
  1342           // Build list of cells to change rowspan
  1343           // We can't do it now since it upsets cell map,
  1344           //  so we will do it after deleting the row
  1345           spanCellList.AppendElement(cell);
  1346           newSpanList.AppendElement(std::max((aRowIndex - startRowIndex), actualRowSpan-1));
  1349       else 
  1351         if (rowSpan > 1)
  1353           //Cell spans below row to delete,
  1354           //  so we must insert new cells to keep rows below even
  1355           // Note that we test "rowSpan" so we don't do this if rowSpan = 0 (automatic readjustment)
  1356           res = SplitCellIntoRows(aTable, startRowIndex, startColIndex,
  1357                                   aRowIndex - startRowIndex + 1, // The row above the row to insert new cell into
  1358                                   actualRowSpan - 1, nullptr);    // Span remaining below
  1359           NS_ENSURE_SUCCESS(res, res);
  1361         if (!cellInDeleteRow)
  1362           cellInDeleteRow = cell; // Reference cell to find row to delete
  1364       // Skip over other columns spanned by this cell
  1365       colIndex += actualColSpan;
  1367   } while (cell);
  1369   // Things are messed up if we didn't find a cell in the row!
  1370   NS_ENSURE_TRUE(cellInDeleteRow, NS_ERROR_FAILURE);
  1372   // Delete the entire row
  1373   nsCOMPtr<nsIDOMElement> parentRow;
  1374   res = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow, getter_AddRefs(parentRow));
  1375   NS_ENSURE_SUCCESS(res, res);
  1377   if (parentRow)
  1379     res = DeleteNode(parentRow);
  1380     NS_ENSURE_SUCCESS(res, res);
  1383   // Now we can set new rowspans for cells stored above  
  1384   for (uint32_t i = 0, n = spanCellList.Length(); i < n; i++)
  1386     nsIDOMElement *cellPtr = spanCellList[i];
  1387     if (cellPtr)
  1389       res = SetRowSpan(cellPtr, newSpanList[i]);
  1390       NS_ENSURE_SUCCESS(res, res);
  1393   return NS_OK;
  1397 NS_IMETHODIMP 
  1398 nsHTMLEditor::SelectTable()
  1400   nsCOMPtr<nsIDOMElement> table;
  1401   nsresult res = NS_ERROR_FAILURE;
  1402   res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table));
  1403   NS_ENSURE_SUCCESS(res, res);
  1404   // Don't fail if we didn't find a table
  1405   NS_ENSURE_TRUE(table, NS_OK);
  1407   res = ClearSelection();
  1408   if (NS_SUCCEEDED(res))
  1409     res = AppendNodeToSelectionAsRange(table);
  1411   return res;
  1414 NS_IMETHODIMP 
  1415 nsHTMLEditor::SelectTableCell()
  1417   nsCOMPtr<nsIDOMElement> cell;
  1418   nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell));
  1419   NS_ENSURE_SUCCESS(res, res);
  1420   NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
  1422   res = ClearSelection();
  1423   if (NS_SUCCEEDED(res))
  1424     res = AppendNodeToSelectionAsRange(cell);
  1426   return res;
  1429 NS_IMETHODIMP 
  1430 nsHTMLEditor::SelectBlockOfCells(nsIDOMElement *aStartCell, nsIDOMElement *aEndCell)
  1432   NS_ENSURE_TRUE(aStartCell && aEndCell, NS_ERROR_NULL_POINTER);
  1434   nsCOMPtr<nsISelection> selection;
  1435   nsresult res = GetSelection(getter_AddRefs(selection));
  1436   NS_ENSURE_SUCCESS(res, res);
  1437   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1439   NS_NAMED_LITERAL_STRING(tableStr, "table");
  1440   nsCOMPtr<nsIDOMElement> table;
  1441   res = GetElementOrParentByTagName(tableStr, aStartCell, getter_AddRefs(table));
  1442   NS_ENSURE_SUCCESS(res, res);
  1443   NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
  1445   nsCOMPtr<nsIDOMElement> endTable;
  1446   res = GetElementOrParentByTagName(tableStr, aEndCell, getter_AddRefs(endTable));
  1447   NS_ENSURE_SUCCESS(res, res);
  1448   NS_ENSURE_TRUE(endTable, NS_ERROR_FAILURE);
  1450   // We can only select a block if within the same table,
  1451   //  so do nothing if not within one table
  1452   if (table != endTable) return NS_OK;
  1454   int32_t startRowIndex, startColIndex, endRowIndex, endColIndex;
  1456   // Get starting and ending cells' location in the cellmap
  1457   res = GetCellIndexes(aStartCell, &startRowIndex, &startColIndex);
  1458   if(NS_FAILED(res)) return res;
  1460   res = GetCellIndexes(aEndCell, &endRowIndex, &endColIndex);
  1461   if(NS_FAILED(res)) return res;
  1463   // Suppress nsISelectionListener notification
  1464   //  until all selection changes are finished
  1465   nsSelectionBatcherForTable selectionBatcher(selection);
  1467   // Examine all cell nodes in current selection and 
  1468   //  remove those outside the new block cell region
  1469   int32_t minColumn = std::min(startColIndex, endColIndex);
  1470   int32_t minRow    = std::min(startRowIndex, endRowIndex);
  1471   int32_t maxColumn   = std::max(startColIndex, endColIndex);
  1472   int32_t maxRow      = std::max(startRowIndex, endRowIndex);
  1474   nsCOMPtr<nsIDOMElement> cell;
  1475   int32_t currentRowIndex, currentColIndex;
  1476   nsCOMPtr<nsIDOMRange> range;
  1477   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
  1478   NS_ENSURE_SUCCESS(res, res);
  1479   if (res == NS_EDITOR_ELEMENT_NOT_FOUND) return NS_OK;
  1481   while (cell)
  1483     res = GetCellIndexes(cell, &currentRowIndex, &currentColIndex);
  1484     NS_ENSURE_SUCCESS(res, res);
  1486     if (currentRowIndex < maxRow || currentRowIndex > maxRow || 
  1487         currentColIndex < maxColumn || currentColIndex > maxColumn)
  1489       selection->RemoveRange(range);
  1490       // Since we've removed the range, decrement pointer to next range
  1491       mSelectedCellIndex--;
  1493     res = GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
  1494     NS_ENSURE_SUCCESS(res, res);
  1497   int32_t rowSpan, colSpan, actualRowSpan, actualColSpan;
  1498   bool    isSelected;
  1499   for (int32_t row = minRow; row <= maxRow; row++)
  1501     for(int32_t col = minColumn; col <= maxColumn; col += std::max(actualColSpan, 1))
  1503       res = GetCellDataAt(table, row, col, getter_AddRefs(cell),
  1504                           &currentRowIndex, &currentColIndex,
  1505                           &rowSpan, &colSpan, 
  1506                           &actualRowSpan, &actualColSpan, &isSelected);
  1507       if (NS_FAILED(res)) break;
  1508       // Skip cells that already selected or are spanned from previous locations
  1509       if (!isSelected && cell && row == currentRowIndex && col == currentColIndex)
  1511         res = AppendNodeToSelectionAsRange(cell);
  1512         if (NS_FAILED(res)) break;
  1516   return res;
  1519 NS_IMETHODIMP 
  1520 nsHTMLEditor::SelectAllTableCells()
  1522   nsCOMPtr<nsIDOMElement> cell;
  1523   nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell));
  1524   NS_ENSURE_SUCCESS(res, res);
  1526   // Don't fail if we didn't find a cell
  1527   NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
  1529   nsCOMPtr<nsIDOMElement> startCell = cell;
  1531   // Get parent table
  1532   nsCOMPtr<nsIDOMElement> table;
  1533   res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell, getter_AddRefs(table));
  1534   NS_ENSURE_SUCCESS(res, res);
  1535   if(!table) return NS_ERROR_NULL_POINTER;
  1537   int32_t rowCount, colCount;
  1538   res = GetTableSize(table, &rowCount, &colCount);
  1539   NS_ENSURE_SUCCESS(res, res);
  1541   nsCOMPtr<nsISelection> selection;
  1542   res = GetSelection(getter_AddRefs(selection));
  1543   NS_ENSURE_SUCCESS(res, res);
  1544   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1546   // Suppress nsISelectionListener notification
  1547   //  until all selection changes are finished
  1548   nsSelectionBatcherForTable selectionBatcher(selection);
  1550   // It is now safe to clear the selection
  1551   // BE SURE TO RESET IT BEFORE LEAVING!
  1552   res = ClearSelection();
  1554   // Select all cells in the same column as current cell
  1555   bool cellSelected = false;
  1556   int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
  1557   bool    isSelected;
  1558   for(int32_t row = 0; row < rowCount; row++)
  1560     for(int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1))
  1562       res = GetCellDataAt(table, row, col, getter_AddRefs(cell),
  1563                           &currentRowIndex, &currentColIndex,
  1564                           &rowSpan, &colSpan, 
  1565                           &actualRowSpan, &actualColSpan, &isSelected);
  1566       if (NS_FAILED(res)) break;
  1567       // Skip cells that are spanned from previous rows or columns
  1568       if (cell && row == currentRowIndex && col == currentColIndex)
  1570         res =  AppendNodeToSelectionAsRange(cell);
  1571         if (NS_FAILED(res)) break;
  1572         cellSelected = true;
  1576   // Safety code to select starting cell if nothing else was selected
  1577   if (!cellSelected)
  1579     return AppendNodeToSelectionAsRange(startCell);
  1581   return res;
  1584 NS_IMETHODIMP 
  1585 nsHTMLEditor::SelectTableRow()
  1587   nsCOMPtr<nsIDOMElement> cell;
  1588   nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell));
  1589   NS_ENSURE_SUCCESS(res, res);
  1591   // Don't fail if we didn't find a cell
  1592   NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
  1593   nsCOMPtr<nsIDOMElement> startCell = cell;
  1595   // Get table and location of cell:
  1596   nsCOMPtr<nsISelection> selection;
  1597   nsCOMPtr<nsIDOMElement> table;
  1598   int32_t startRowIndex, startColIndex;
  1600   res = GetCellContext(getter_AddRefs(selection),
  1601                        getter_AddRefs(table), 
  1602                        getter_AddRefs(cell),
  1603                        nullptr, nullptr,
  1604                        &startRowIndex, &startColIndex);
  1605   NS_ENSURE_SUCCESS(res, res);
  1606   NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
  1608   int32_t rowCount, colCount;
  1609   res = GetTableSize(table, &rowCount, &colCount);
  1610   NS_ENSURE_SUCCESS(res, res);
  1612   //Note: At this point, we could get first and last cells in row,
  1613   //  then call SelectBlockOfCells, but that would take just
  1614   //  a little less code, so the following is more efficient
  1616   // Suppress nsISelectionListener notification
  1617   //  until all selection changes are finished
  1618   nsSelectionBatcherForTable selectionBatcher(selection);
  1620   // It is now safe to clear the selection
  1621   // BE SURE TO RESET IT BEFORE LEAVING!
  1622   res = ClearSelection();
  1624   // Select all cells in the same row as current cell
  1625   bool cellSelected = false;
  1626   int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
  1627   bool    isSelected;
  1628   for(int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1))
  1630     res = GetCellDataAt(table, startRowIndex, col, getter_AddRefs(cell),
  1631                         &currentRowIndex, &currentColIndex, &rowSpan, &colSpan, 
  1632                         &actualRowSpan, &actualColSpan, &isSelected);
  1633     if (NS_FAILED(res)) break;
  1634     // Skip cells that are spanned from previous rows or columns
  1635     if (cell && currentRowIndex == startRowIndex && currentColIndex == col)
  1637       res = AppendNodeToSelectionAsRange(cell);
  1638       if (NS_FAILED(res)) break;
  1639       cellSelected = true;
  1642   // Safety code to select starting cell if nothing else was selected
  1643   if (!cellSelected)
  1645     return AppendNodeToSelectionAsRange(startCell);
  1647   return res;
  1650 NS_IMETHODIMP 
  1651 nsHTMLEditor::SelectTableColumn()
  1653   nsCOMPtr<nsIDOMElement> cell;
  1654   nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell));
  1655   NS_ENSURE_SUCCESS(res, res);
  1657   // Don't fail if we didn't find a cell
  1658   NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
  1660   nsCOMPtr<nsIDOMElement> startCell = cell;
  1662   // Get location of cell:
  1663   nsCOMPtr<nsISelection> selection;
  1664   nsCOMPtr<nsIDOMElement> table;
  1665   int32_t startRowIndex, startColIndex;
  1667   res = GetCellContext(getter_AddRefs(selection),
  1668                        getter_AddRefs(table), 
  1669                        getter_AddRefs(cell),
  1670                        nullptr, nullptr,
  1671                        &startRowIndex, &startColIndex);
  1672   NS_ENSURE_SUCCESS(res, res);
  1673   NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
  1675   int32_t rowCount, colCount;
  1676   res = GetTableSize(table, &rowCount, &colCount);
  1677   NS_ENSURE_SUCCESS(res, res);
  1679   // Suppress nsISelectionListener notification
  1680   //  until all selection changes are finished
  1681   nsSelectionBatcherForTable selectionBatcher(selection);
  1683   // It is now safe to clear the selection
  1684   // BE SURE TO RESET IT BEFORE LEAVING!
  1685   res = ClearSelection();
  1687   // Select all cells in the same column as current cell
  1688   bool cellSelected = false;
  1689   int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex;
  1690   bool    isSelected;
  1691   for(int32_t row = 0; row < rowCount; row += std::max(actualRowSpan, 1))
  1693     res = GetCellDataAt(table, row, startColIndex, getter_AddRefs(cell),
  1694                         &currentRowIndex, &currentColIndex, &rowSpan, &colSpan, 
  1695                         &actualRowSpan, &actualColSpan, &isSelected);
  1696     if (NS_FAILED(res)) break;
  1697     // Skip cells that are spanned from previous rows or columns
  1698     if (cell && currentRowIndex == row && currentColIndex == startColIndex)
  1700       res = AppendNodeToSelectionAsRange(cell);
  1701       if (NS_FAILED(res)) break;
  1702       cellSelected = true;
  1705   // Safety code to select starting cell if nothing else was selected
  1706   if (!cellSelected)
  1708     return AppendNodeToSelectionAsRange(startCell);
  1710   return res;
  1713 NS_IMETHODIMP 
  1714 nsHTMLEditor::SplitTableCell()
  1716   nsCOMPtr<nsIDOMElement> table;
  1717   nsCOMPtr<nsIDOMElement> cell;
  1718   int32_t startRowIndex, startColIndex, actualRowSpan, actualColSpan;
  1719   // Get cell, table, etc. at selection anchor node
  1720   nsresult res = GetCellContext(nullptr,
  1721                                 getter_AddRefs(table), 
  1722                                 getter_AddRefs(cell),
  1723                                 nullptr, nullptr,
  1724                                 &startRowIndex, &startColIndex);
  1725   NS_ENSURE_SUCCESS(res, res);
  1726   if(!table || !cell) return NS_EDITOR_ELEMENT_NOT_FOUND;
  1728   // We need rowspan and colspan data
  1729   res = GetCellSpansAt(table, startRowIndex, startColIndex, actualRowSpan, actualColSpan);
  1730   NS_ENSURE_SUCCESS(res, res);
  1732   // Must have some span to split
  1733   if (actualRowSpan <= 1 && actualColSpan <= 1)
  1734     return NS_OK;
  1736   nsAutoEditBatch beginBatching(this);
  1737   // Prevent auto insertion of BR in new cell until we're done
  1738   nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
  1740   // We reset selection  
  1741   nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false);
  1742   //...so suppress Rules System selection munging
  1743   nsAutoTxnsConserveSelection dontChangeSelection(this);
  1745   nsCOMPtr<nsIDOMElement> newCell;
  1746   int32_t rowIndex = startRowIndex;
  1747   int32_t rowSpanBelow, colSpanAfter;
  1749   // Split up cell row-wise first into rowspan=1 above, and the rest below,
  1750   //  whittling away at the cell below until no more extra span
  1751   for (rowSpanBelow = actualRowSpan-1; rowSpanBelow >= 0; rowSpanBelow--)
  1753     // We really split row-wise only if we had rowspan > 1
  1754     if (rowSpanBelow > 0)
  1756       res = SplitCellIntoRows(table, rowIndex, startColIndex, 1, rowSpanBelow, getter_AddRefs(newCell));
  1757       NS_ENSURE_SUCCESS(res, res);
  1758       CopyCellBackgroundColor(newCell, cell);
  1760     int32_t colIndex = startColIndex;
  1761     // Now split the cell with rowspan = 1 into cells if it has colSpan > 1
  1762     for (colSpanAfter = actualColSpan-1; colSpanAfter > 0; colSpanAfter--)
  1764       res = SplitCellIntoColumns(table, rowIndex, colIndex, 1, colSpanAfter, getter_AddRefs(newCell));
  1765       NS_ENSURE_SUCCESS(res, res);
  1766       CopyCellBackgroundColor(newCell, cell);
  1767       colIndex++;
  1769     // Point to the new cell and repeat
  1770     rowIndex++;
  1772   return res;
  1775 nsresult
  1776 nsHTMLEditor::CopyCellBackgroundColor(nsIDOMElement *destCell, nsIDOMElement *sourceCell)
  1778   NS_ENSURE_TRUE(destCell && sourceCell, NS_ERROR_NULL_POINTER);
  1780   // Copy backgournd color to new cell
  1781   NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor");
  1782   nsAutoString color;
  1783   bool isSet;
  1784   nsresult res = GetAttributeValue(sourceCell, bgcolor, color, &isSet);
  1786   if (NS_SUCCEEDED(res) && isSet)
  1787     res = SetAttribute(destCell, bgcolor, color);
  1789   return res;
  1792 NS_IMETHODIMP 
  1793 nsHTMLEditor::SplitCellIntoColumns(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aColIndex,
  1794                                    int32_t aColSpanLeft, int32_t aColSpanRight,
  1795                                    nsIDOMElement **aNewCell)
  1797   NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
  1798   if (aNewCell) *aNewCell = nullptr;
  1800   nsCOMPtr<nsIDOMElement> cell;
  1801   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  1802   bool    isSelected;
  1803   nsresult res = GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell),
  1804                                &startRowIndex, &startColIndex,
  1805                                &rowSpan, &colSpan, 
  1806                                &actualRowSpan, &actualColSpan, &isSelected);
  1807   NS_ENSURE_SUCCESS(res, res);
  1808   NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER);
  1810   // We can't split!
  1811   if (actualColSpan <= 1 || (aColSpanLeft + aColSpanRight) > actualColSpan)
  1812     return NS_OK;
  1814   // Reduce colspan of cell to split
  1815   res = SetColSpan(cell, aColSpanLeft);
  1816   NS_ENSURE_SUCCESS(res, res);
  1818   // Insert new cell after using the remaining span
  1819   //  and always get the new cell so we can copy the background color;
  1820   nsCOMPtr<nsIDOMElement> newCell;
  1821   res = InsertCell(cell, actualRowSpan, aColSpanRight, true, false, getter_AddRefs(newCell));
  1822   NS_ENSURE_SUCCESS(res, res);
  1823   if (newCell)
  1825     if (aNewCell)
  1827       *aNewCell = newCell.get();
  1828       NS_ADDREF(*aNewCell);
  1830     res = CopyCellBackgroundColor(newCell, cell);
  1832   return res;
  1835 NS_IMETHODIMP
  1836 nsHTMLEditor::SplitCellIntoRows(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aColIndex,
  1837                                 int32_t aRowSpanAbove, int32_t aRowSpanBelow, 
  1838                                 nsIDOMElement **aNewCell)
  1840   NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
  1841   if (aNewCell) *aNewCell = nullptr;
  1843   nsCOMPtr<nsIDOMElement> cell;
  1844   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  1845   bool    isSelected;
  1846   nsresult res = GetCellDataAt(aTable, aRowIndex, aColIndex, getter_AddRefs(cell),
  1847                                &startRowIndex, &startColIndex,
  1848                                &rowSpan, &colSpan, 
  1849                                &actualRowSpan, &actualColSpan, &isSelected);
  1850   NS_ENSURE_SUCCESS(res, res);
  1851   NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER);
  1853   // We can't split!
  1854   if (actualRowSpan <= 1 || (aRowSpanAbove + aRowSpanBelow) > actualRowSpan)
  1855     return NS_OK;
  1857   int32_t rowCount, colCount;
  1858   res = GetTableSize(aTable, &rowCount, &colCount);
  1859   NS_ENSURE_SUCCESS(res, res);
  1861   nsCOMPtr<nsIDOMElement> cell2;
  1862   nsCOMPtr<nsIDOMElement> lastCellFound;
  1863   int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2;
  1864   bool    isSelected2;
  1865   int32_t colIndex = 0;
  1866   bool insertAfter = (startColIndex > 0);
  1867   // This is the row we will insert new cell into
  1868   int32_t rowBelowIndex = startRowIndex+aRowSpanAbove;
  1870   // Find a cell to insert before or after
  1871   do 
  1873     // Search for a cell to insert before
  1874     res = GetCellDataAt(aTable, rowBelowIndex, 
  1875                         colIndex, getter_AddRefs(cell2),
  1876                         &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2, 
  1877                         &actualRowSpan2, &actualColSpan2, &isSelected2);
  1878     // If we fail here, it could be because row has bad rowspan values,
  1879     //   such as all cells having rowspan > 1 (Call FixRowSpan first!)
  1880     if (NS_FAILED(res) || !cell) return NS_ERROR_FAILURE;
  1882     // Skip over cells spanned from above (like the one we are splitting!)
  1883     if (cell2 && startRowIndex2 == rowBelowIndex)
  1885       if (insertAfter)
  1887         // New cell isn't first in row,
  1888         // so stop after we find the cell just before new cell's column
  1889         if ((startColIndex2 + actualColSpan2) == startColIndex)
  1890           break;
  1892         // If cell found is AFTER desired new cell colum,
  1893         //  we have multiple cells with rowspan > 1 that
  1894         //  prevented us from finding a cell to insert after...
  1895         if (startColIndex2 > startColIndex)
  1897           // ... so instead insert before the cell we found
  1898           insertAfter = false;
  1899           break;
  1902       else
  1904         break; // Inserting before, so stop at first cell in row we want to insert into
  1906       lastCellFound = cell2;
  1908     // Skip to next available cellmap location
  1909     colIndex += std::max(actualColSpan2, 1);
  1911     // Done when past end of total number of columns
  1912     if (colIndex > colCount)
  1913         break;
  1915   } while(true);
  1917   if (!cell2 && lastCellFound)
  1919     // Edge case where we didn't find a cell to insert after
  1920     //  or before because column(s) before desired column 
  1921     //  and all columns after it are spanned from above. 
  1922     //  We can insert after the last cell we found 
  1923     cell2 = lastCellFound;
  1924     insertAfter = true; // Should always be true, but let's be sure
  1927   // Reduce rowspan of cell to split
  1928   res = SetRowSpan(cell, aRowSpanAbove);
  1929   NS_ENSURE_SUCCESS(res, res);
  1932   // Insert new cell after using the remaining span
  1933   //  and always get the new cell so we can copy the background color;
  1934   nsCOMPtr<nsIDOMElement> newCell;
  1935   res = InsertCell(cell2, aRowSpanBelow, actualColSpan, insertAfter, false, getter_AddRefs(newCell));
  1936   NS_ENSURE_SUCCESS(res, res);
  1937   if (newCell)
  1939     if (aNewCell)
  1941       *aNewCell = newCell.get();
  1942       NS_ADDREF(*aNewCell);
  1944     res = CopyCellBackgroundColor(newCell, cell2);
  1946   return res;
  1949 NS_IMETHODIMP
  1950 nsHTMLEditor::SwitchTableCellHeaderType(nsIDOMElement *aSourceCell, nsIDOMElement **aNewCell)
  1952   NS_ENSURE_TRUE(aSourceCell, NS_ERROR_NULL_POINTER);
  1954   nsAutoEditBatch beginBatching(this);
  1955   // Prevent auto insertion of BR in new cell created by ReplaceContainer
  1956   nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
  1958   nsCOMPtr<nsIDOMNode> newNode;
  1960   // Save current selection to restore when done
  1961   // This is needed so ReplaceContainer can monitor selection
  1962   //  when replacing nodes
  1963   nsRefPtr<Selection> selection = GetSelection();
  1964   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  1965   nsAutoSelectionReset selectionResetter(selection, this);
  1967   // Set to the opposite of current type
  1968   nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(aSourceCell);
  1969   nsString newCellType( (atom == nsEditProperty::td) ? NS_LITERAL_STRING("th") : NS_LITERAL_STRING("td"));
  1971   // This creates new node, moves children, copies attributes (true)
  1972   //   and manages the selection!
  1973   nsresult res = ReplaceContainer(aSourceCell, address_of(newNode),
  1974                                   newCellType, nullptr, nullptr, true);
  1975   NS_ENSURE_SUCCESS(res, res);
  1976   NS_ENSURE_TRUE(newNode, NS_ERROR_FAILURE);
  1978   // Return the new cell
  1979   if (aNewCell)
  1981     nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode);
  1982     *aNewCell = newElement.get();
  1983     NS_ADDREF(*aNewCell);
  1986   return NS_OK;
  1989 NS_IMETHODIMP 
  1990 nsHTMLEditor::JoinTableCells(bool aMergeNonContiguousContents)
  1992   nsCOMPtr<nsIDOMElement> table;
  1993   nsCOMPtr<nsIDOMElement> targetCell;
  1994   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  1995   bool    isSelected;
  1996   nsCOMPtr<nsIDOMElement> cell2;
  1997   int32_t startRowIndex2, startColIndex2, rowSpan2, colSpan2, actualRowSpan2, actualColSpan2;
  1998   bool    isSelected2;
  2000   // Get cell, table, etc. at selection anchor node
  2001   nsresult res = GetCellContext(nullptr,
  2002                                 getter_AddRefs(table), 
  2003                                 getter_AddRefs(targetCell),
  2004                                 nullptr, nullptr,
  2005                                 &startRowIndex, &startColIndex);
  2006   NS_ENSURE_SUCCESS(res, res);
  2007   if(!table || !targetCell) return NS_EDITOR_ELEMENT_NOT_FOUND;
  2009   nsAutoEditBatch beginBatching(this);
  2010   //Don't let Rules System change the selection
  2011   nsAutoTxnsConserveSelection dontChangeSelection(this);
  2013   // Note: We dont' use nsSetSelectionAfterTableEdit here so the selection
  2014   //  is retained after joining. This leaves the target cell selected
  2015   //  as well as the "non-contiguous" cells, so user can see what happened.
  2017   nsCOMPtr<nsIDOMElement> firstCell;
  2018   int32_t firstRowIndex, firstColIndex;
  2019   res = GetFirstSelectedCellInTable(&firstRowIndex, &firstColIndex, getter_AddRefs(firstCell));
  2020   NS_ENSURE_SUCCESS(res, res);
  2022   bool joinSelectedCells = false;
  2023   if (firstCell)
  2025     nsCOMPtr<nsIDOMElement> secondCell;
  2026     res = GetNextSelectedCell(nullptr, getter_AddRefs(secondCell));
  2027     NS_ENSURE_SUCCESS(res, res);
  2029     // If only one cell is selected, join with cell to the right
  2030     joinSelectedCells = (secondCell != nullptr);
  2033   if (joinSelectedCells)
  2035     // We have selected cells: Join just contiguous cells
  2036     //  and just merge contents if not contiguous
  2038     int32_t rowCount, colCount;
  2039     res = GetTableSize(table, &rowCount, &colCount);
  2040     NS_ENSURE_SUCCESS(res, res);
  2042     // Get spans for cell we will merge into
  2043     int32_t firstRowSpan, firstColSpan;
  2044     res = GetCellSpansAt( table, firstRowIndex, firstColIndex, firstRowSpan, firstColSpan);
  2045     NS_ENSURE_SUCCESS(res, res);
  2047     // This defines the last indexes along the "edges"
  2048     //  of the contiguous block of cells, telling us
  2049     //  that we can join adjacent cells to the block
  2050     // Start with same as the first values,
  2051     //  then expand as we find adjacent selected cells
  2052     int32_t lastRowIndex = firstRowIndex;
  2053     int32_t lastColIndex = firstColIndex;
  2054     int32_t rowIndex, colIndex;
  2056     // First pass: Determine boundaries of contiguous rectangular block 
  2057     //  that we will join into one cell,
  2058     //  favoring adjacent cells in the same row
  2059     for (rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++)
  2061       int32_t currentRowCount = rowCount;
  2062       // Be sure each row doesn't have rowspan errors
  2063       res = FixBadRowSpan(table, rowIndex, rowCount);
  2064       NS_ENSURE_SUCCESS(res, res);
  2065       // Adjust rowcount by number of rows we removed
  2066       lastRowIndex -= (currentRowCount-rowCount);
  2068       bool cellFoundInRow = false;
  2069       bool lastRowIsSet = false;
  2070       int32_t lastColInRow = 0;
  2071       int32_t firstColInRow = firstColIndex;
  2072       for (colIndex = firstColIndex; colIndex < colCount; colIndex += std::max(actualColSpan2, 1))
  2074         res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2),
  2075                             &startRowIndex2, &startColIndex2,
  2076                             &rowSpan2, &colSpan2, 
  2077                             &actualRowSpan2, &actualColSpan2, &isSelected2);
  2078         NS_ENSURE_SUCCESS(res, res);
  2080         if (isSelected2)
  2082           if (!cellFoundInRow)
  2083             // We've just found the first selected cell in this row
  2084             firstColInRow = colIndex;
  2086           if (rowIndex > firstRowIndex && firstColInRow != firstColIndex)
  2088             // We're in at least the second row,
  2089             // but left boundary is "ragged" (not the same as 1st row's start)
  2090             //Let's just end block on previous row
  2091             // and keep previous lastColIndex
  2092             //TODO: We could try to find the Maximum firstColInRow
  2093             //      so our block can still extend down more rows?
  2094             lastRowIndex = std::max(0,rowIndex - 1);
  2095             lastRowIsSet = true;
  2096             break;
  2098           // Save max selected column in this row, including extra colspan
  2099           lastColInRow = colIndex + (actualColSpan2-1);
  2100           cellFoundInRow = true;
  2102         else if (cellFoundInRow)
  2104           // No cell or not selected, but at least one cell in row was found
  2106           if (rowIndex > (firstRowIndex+1) && colIndex <= lastColIndex)
  2108             // Cell is in a column less than current right border in 
  2109             //  the third or higher selected row, so stop block at the previous row
  2110             lastRowIndex = std::max(0,rowIndex - 1);
  2111             lastRowIsSet = true;
  2113           // We're done with this row
  2114           break;
  2116       } // End of column loop
  2118       // Done with this row 
  2119       if (cellFoundInRow) 
  2121         if (rowIndex == firstRowIndex)
  2123           // First row always initializes the right boundary
  2124           lastColIndex = lastColInRow;
  2127         // If we didn't determine last row above...
  2128         if (!lastRowIsSet)
  2130           if (colIndex < lastColIndex)
  2132             // (don't think we ever get here?)
  2133             // Cell is in a column less than current right boundary,
  2134             //  so stop block at the previous row
  2135             lastRowIndex = std::max(0,rowIndex - 1);
  2137           else
  2139             // Go on to examine next row
  2140             lastRowIndex = rowIndex+1;
  2143         // Use the minimum col we found so far for right boundary
  2144         lastColIndex = std::min(lastColIndex, lastColInRow);
  2146       else
  2148         // No selected cells in this row -- stop at row above
  2149         //  and leave last column at its previous value
  2150         lastRowIndex = std::max(0,rowIndex - 1);
  2154     // The list of cells we will delete after joining
  2155     nsTArray<nsCOMPtr<nsIDOMElement> > deleteList;
  2157     // 2nd pass: Do the joining and merging
  2158     for (rowIndex = 0; rowIndex < rowCount; rowIndex++)
  2160       for (colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan2, 1))
  2162         res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2),
  2163                             &startRowIndex2, &startColIndex2,
  2164                             &rowSpan2, &colSpan2, 
  2165                             &actualRowSpan2, &actualColSpan2, &isSelected2);
  2166         NS_ENSURE_SUCCESS(res, res);
  2168         // If this is 0, we are past last cell in row, so exit the loop
  2169         if (actualColSpan2 == 0)
  2170           break;
  2172         // Merge only selected cells (skip cell we're merging into, of course)
  2173         if (isSelected2 && cell2 != firstCell)
  2175           if (rowIndex >= firstRowIndex && rowIndex <= lastRowIndex && 
  2176               colIndex >= firstColIndex && colIndex <= lastColIndex)
  2178             // We are within the join region
  2179             // Problem: It is very tricky to delete cells as we merge,
  2180             //  since that will upset the cellmap
  2181             //  Instead, build a list of cells to delete and do it later
  2182             NS_ASSERTION(startRowIndex2 == rowIndex, "JoinTableCells: StartRowIndex is in row above");
  2184             if (actualColSpan2 > 1)
  2186               //Check if cell "hangs" off the boundary because of colspan > 1
  2187               //  Use split methods to chop off excess
  2188               int32_t extraColSpan = (startColIndex2 + actualColSpan2) - (lastColIndex+1);
  2189               if ( extraColSpan > 0)
  2191                 res = SplitCellIntoColumns(table, startRowIndex2, startColIndex2, 
  2192                                            actualColSpan2-extraColSpan, extraColSpan, nullptr);
  2193                 NS_ENSURE_SUCCESS(res, res);
  2197             res = MergeCells(firstCell, cell2, false);
  2198             NS_ENSURE_SUCCESS(res, res);
  2200             // Add cell to list to delete
  2201             deleteList.AppendElement(cell2.get());
  2203           else if (aMergeNonContiguousContents)
  2205             // Cell is outside join region -- just merge the contents
  2206             res = MergeCells(firstCell, cell2, false);
  2207             NS_ENSURE_SUCCESS(res, res);
  2213     // All cell contents are merged. Delete the empty cells we accumulated
  2214     // Prevent rules testing until we're done
  2215     nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
  2217     for (uint32_t i = 0, n = deleteList.Length(); i < n; i++)
  2219       nsIDOMElement *elementPtr = deleteList[i];
  2220       if (elementPtr)
  2222         nsCOMPtr<nsIDOMNode> node = do_QueryInterface(elementPtr);
  2223         res = DeleteNode(node);
  2224         NS_ENSURE_SUCCESS(res, res);
  2227     // Cleanup selection: remove ranges where cells were deleted
  2228     nsCOMPtr<nsISelection> selection;
  2229     res = GetSelection(getter_AddRefs(selection));
  2230     NS_ENSURE_SUCCESS(res, res);
  2231     NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  2233     int32_t rangeCount;
  2234     res = selection->GetRangeCount(&rangeCount);
  2235     NS_ENSURE_SUCCESS(res, res);
  2237     nsCOMPtr<nsIDOMRange> range;
  2238     int32_t i;
  2239     for (i = 0; i < rangeCount; i++)
  2241       res = selection->GetRangeAt(i, getter_AddRefs(range));
  2242       NS_ENSURE_SUCCESS(res, res);
  2243       NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
  2245       nsCOMPtr<nsIDOMElement> deletedCell;
  2246       res = GetCellFromRange(range, getter_AddRefs(deletedCell));
  2247       if (!deletedCell)
  2249         selection->RemoveRange(range);
  2250         rangeCount--;
  2251         i--;
  2255     // Set spans for the cell everthing merged into
  2256     res = SetRowSpan(firstCell, lastRowIndex-firstRowIndex+1);
  2257     NS_ENSURE_SUCCESS(res, res);
  2258     res = SetColSpan(firstCell, lastColIndex-firstColIndex+1);
  2259     NS_ENSURE_SUCCESS(res, res);
  2262     // Fixup disturbances in table layout
  2263     NormalizeTable(table);
  2265   else
  2267     // Joining with cell to the right -- get rowspan and colspan data of target cell
  2268     res = GetCellDataAt(table, startRowIndex, startColIndex, getter_AddRefs(targetCell),
  2269                         &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  2270                         &actualRowSpan, &actualColSpan, &isSelected);
  2271     NS_ENSURE_SUCCESS(res, res);
  2272     NS_ENSURE_TRUE(targetCell, NS_ERROR_NULL_POINTER);
  2274     // Get data for cell to the right
  2275     res = GetCellDataAt(table, startRowIndex, startColIndex+actualColSpan, getter_AddRefs(cell2),
  2276                         &startRowIndex2, &startColIndex2, &rowSpan2, &colSpan2, 
  2277                         &actualRowSpan2, &actualColSpan2, &isSelected2);
  2278     NS_ENSURE_SUCCESS(res, res);
  2279     if(!cell2) return NS_OK; // Don't fail if there's no cell
  2281     // sanity check
  2282     NS_ASSERTION((startRowIndex >= startRowIndex2),"JoinCells: startRowIndex < startRowIndex2");
  2284     // Figure out span of merged cell starting from target's starting row
  2285     // to handle case of merged cell starting in a row above
  2286     int32_t spanAboveMergedCell = startRowIndex - startRowIndex2;
  2287     int32_t effectiveRowSpan2 = actualRowSpan2 - spanAboveMergedCell;
  2289     if (effectiveRowSpan2 > actualRowSpan)
  2291       // Cell to the right spans into row below target
  2292       // Split off portion below target cell's bottom-most row
  2293       res = SplitCellIntoRows(table, startRowIndex2, startColIndex2,
  2294                               spanAboveMergedCell+actualRowSpan, 
  2295                               effectiveRowSpan2-actualRowSpan, nullptr);
  2296       NS_ENSURE_SUCCESS(res, res);
  2299     // Move contents from cell to the right
  2300     // Delete the cell now only if it starts in the same row
  2301     //   and has enough row "height"
  2302     res = MergeCells(targetCell, cell2, 
  2303                      (startRowIndex2 == startRowIndex) && 
  2304                      (effectiveRowSpan2 >= actualRowSpan));
  2305     NS_ENSURE_SUCCESS(res, res);
  2307     if (effectiveRowSpan2 < actualRowSpan)
  2309       // Merged cell is "shorter" 
  2310       // (there are cells(s) below it that are row-spanned by target cell)
  2311       // We could try splitting those cells, but that's REAL messy,
  2312       //  so the safest thing to do is NOT really join the cells
  2313       return NS_OK;
  2316     if( spanAboveMergedCell > 0 )
  2318       // Cell we merged started in a row above the target cell
  2319       // Reduce rowspan to give room where target cell will extend its colspan
  2320       res = SetRowSpan(cell2, spanAboveMergedCell);
  2321       NS_ENSURE_SUCCESS(res, res);
  2324     // Reset target cell's colspan to encompass cell to the right
  2325     res = SetColSpan(targetCell, actualColSpan+actualColSpan2);
  2326     NS_ENSURE_SUCCESS(res, res);
  2328   return res;
  2331 NS_IMETHODIMP 
  2332 nsHTMLEditor::MergeCells(nsCOMPtr<nsIDOMElement> aTargetCell, 
  2333                          nsCOMPtr<nsIDOMElement> aCellToMerge,
  2334                          bool aDeleteCellToMerge)
  2336   nsCOMPtr<dom::Element> targetCell = do_QueryInterface(aTargetCell);
  2337   nsCOMPtr<dom::Element> cellToMerge = do_QueryInterface(aCellToMerge);
  2338   NS_ENSURE_TRUE(targetCell && cellToMerge, NS_ERROR_NULL_POINTER);
  2340   // Prevent rules testing until we're done
  2341   nsAutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
  2343   // Don't need to merge if cell is empty
  2344   if (!IsEmptyCell(cellToMerge)) {
  2345     // Get index of last child in target cell
  2346     // If we fail or don't have children, 
  2347     //  we insert at index 0
  2348     int32_t insertIndex = 0;
  2350     // Start inserting just after last child
  2351     uint32_t len = targetCell->GetChildCount();
  2352     if (len == 1 && IsEmptyCell(targetCell)) {
  2353       // Delete the empty node
  2354       nsIContent* cellChild = targetCell->GetFirstChild();
  2355       nsresult res = DeleteNode(cellChild->AsDOMNode());
  2356       NS_ENSURE_SUCCESS(res, res);
  2357       insertIndex = 0;
  2358     } else {
  2359       insertIndex = (int32_t)len;
  2362     // Move the contents
  2363     while (cellToMerge->HasChildren()) {
  2364       nsCOMPtr<nsIDOMNode> cellChild = cellToMerge->GetLastChild()->AsDOMNode();
  2365       nsresult res = DeleteNode(cellChild);
  2366       NS_ENSURE_SUCCESS(res, res);
  2368       res = InsertNode(cellChild, aTargetCell, insertIndex);
  2369       NS_ENSURE_SUCCESS(res, res);
  2373   // Delete cells whose contents were moved
  2374   if (aDeleteCellToMerge)
  2375     return DeleteNode(aCellToMerge);
  2377   return NS_OK;
  2381 NS_IMETHODIMP 
  2382 nsHTMLEditor::FixBadRowSpan(nsIDOMElement *aTable, int32_t aRowIndex, int32_t& aNewRowCount)
  2384   NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
  2386   int32_t rowCount, colCount;
  2387   nsresult res = GetTableSize(aTable, &rowCount, &colCount);
  2388   NS_ENSURE_SUCCESS(res, res);
  2390   nsCOMPtr<nsIDOMElement>cell;
  2391   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  2392   bool    isSelected;
  2394   int32_t minRowSpan = -1;
  2395   int32_t colIndex;
  2397   for( colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan, 1))
  2399     res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
  2400                         &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  2401                         &actualRowSpan, &actualColSpan, &isSelected);
  2402     // NOTE: This is a *real* failure. 
  2403     // GetCellDataAt passes if cell is missing from cellmap
  2404     if(NS_FAILED(res)) return res;
  2405     if (!cell) break;
  2406     if(rowSpan > 0 && 
  2407        startRowIndex == aRowIndex &&
  2408        (rowSpan < minRowSpan || minRowSpan == -1))
  2410       minRowSpan = rowSpan;
  2412     NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan");
  2414   if(minRowSpan > 1)
  2416     // The amount to reduce everyone's rowspan
  2417     // so at least one cell has rowspan = 1
  2418     int32_t rowsReduced = minRowSpan - 1;
  2419     for(colIndex = 0; colIndex < colCount; colIndex += std::max(actualColSpan, 1))
  2421       res = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell),
  2422                           &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  2423                           &actualRowSpan, &actualColSpan, &isSelected);
  2424       if(NS_FAILED(res)) return res;
  2425       // Fixup rowspans only for cells starting in current row
  2426       if(cell && rowSpan > 0 &&
  2427          startRowIndex == aRowIndex && 
  2428          startColIndex ==  colIndex )
  2430         res = SetRowSpan(cell, rowSpan-rowsReduced);
  2431         if(NS_FAILED(res)) return res;
  2433       NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan");
  2436   return GetTableSize(aTable, &aNewRowCount, &colCount);
  2439 NS_IMETHODIMP 
  2440 nsHTMLEditor::FixBadColSpan(nsIDOMElement *aTable, int32_t aColIndex, int32_t& aNewColCount)
  2442   NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER);
  2444   int32_t rowCount, colCount;
  2445   nsresult res = GetTableSize(aTable, &rowCount, &colCount);
  2446   NS_ENSURE_SUCCESS(res, res);
  2448   nsCOMPtr<nsIDOMElement> cell;
  2449   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  2450   bool    isSelected;
  2452   int32_t minColSpan = -1;
  2453   int32_t rowIndex;
  2455   for( rowIndex = 0; rowIndex < rowCount; rowIndex += std::max(actualRowSpan, 1))
  2457     res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
  2458                         &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  2459                         &actualRowSpan, &actualColSpan, &isSelected);
  2460     // NOTE: This is a *real* failure. 
  2461     // GetCellDataAt passes if cell is missing from cellmap
  2462     if(NS_FAILED(res)) return res;
  2463     if (!cell) break;
  2464     if(colSpan > 0 && 
  2465        startColIndex == aColIndex &&
  2466        (colSpan < minColSpan || minColSpan == -1))
  2468       minColSpan = colSpan;
  2470     NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan");
  2472   if(minColSpan > 1)
  2474     // The amount to reduce everyone's colspan
  2475     // so at least one cell has colspan = 1
  2476     int32_t colsReduced = minColSpan - 1;
  2477     for(rowIndex = 0; rowIndex < rowCount; rowIndex += std::max(actualRowSpan, 1))
  2479       res = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell),
  2480                           &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  2481                           &actualRowSpan, &actualColSpan, &isSelected);
  2482       if(NS_FAILED(res)) return res;
  2483       // Fixup colspans only for cells starting in current column
  2484       if(cell && colSpan > 0 &&
  2485          startColIndex == aColIndex && 
  2486          startRowIndex ==  rowIndex )
  2488         res = SetColSpan(cell, colSpan-colsReduced);
  2489         if(NS_FAILED(res)) return res;
  2491       NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan");
  2494   return GetTableSize(aTable, &rowCount, &aNewColCount);
  2497 NS_IMETHODIMP 
  2498 nsHTMLEditor::NormalizeTable(nsIDOMElement *aTable)
  2500   nsRefPtr<Selection> selection = GetSelection();
  2501   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  2503   nsCOMPtr<nsIDOMElement> table;
  2504   nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"),
  2505                                              aTable, getter_AddRefs(table));
  2506   NS_ENSURE_SUCCESS(res, res);
  2507   // Don't fail if we didn't find a table
  2508   NS_ENSURE_TRUE(table, NS_OK);
  2510   int32_t rowCount, colCount, rowIndex, colIndex;
  2511   res = GetTableSize(table, &rowCount, &colCount);
  2512   NS_ENSURE_SUCCESS(res, res);
  2514   // Save current selection
  2515   nsAutoSelectionReset selectionResetter(selection, this);
  2517   nsAutoEditBatch beginBatching(this);
  2518   // Prevent auto insertion of BR in new cell until we're done
  2519   nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
  2521   nsCOMPtr<nsIDOMElement> cell;
  2522   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  2523   bool    isSelected;
  2525   // Scan all cells in each row to detect bad rowspan values
  2526   for(rowIndex = 0; rowIndex < rowCount; rowIndex++)
  2528     res = FixBadRowSpan(table, rowIndex, rowCount);
  2529     NS_ENSURE_SUCCESS(res, res);
  2531   // and same for colspans
  2532   for(colIndex = 0; colIndex < colCount; colIndex++)
  2534     res = FixBadColSpan(table, colIndex, colCount);
  2535     NS_ENSURE_SUCCESS(res, res);
  2538   // Fill in missing cellmap locations with empty cells
  2539   for(rowIndex = 0; rowIndex < rowCount; rowIndex++)
  2541     nsCOMPtr<nsIDOMElement> previousCellInRow;
  2543     for(colIndex = 0; colIndex < colCount; colIndex++)
  2545       res = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell),
  2546                           &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  2547                           &actualRowSpan, &actualColSpan, &isSelected);
  2548       // NOTE: This is a *real* failure. 
  2549       // GetCellDataAt passes if cell is missing from cellmap
  2550       if(NS_FAILED(res)) return res;
  2551       if (!cell)
  2553         //We are missing a cell at a cellmap location
  2554 #ifdef DEBUG
  2555         printf("NormalizeTable found missing cell at row=%d, col=%d\n", rowIndex, colIndex);
  2556 #endif
  2557         // Add a cell after the previous Cell in the current row
  2558         if(previousCellInRow)
  2560           // Insert a new cell after (true), and return the new cell to us
  2561           res = InsertCell(previousCellInRow, 1, 1, true, false, getter_AddRefs(cell));
  2562           NS_ENSURE_SUCCESS(res, res);
  2564           // Set this so we use returned new "cell" to set previousCellInRow below
  2565           if(cell)
  2566             startRowIndex = rowIndex;   
  2567         } else {
  2568           // We don't have any cells in this row -- We are really messed up!
  2569 #ifdef DEBUG
  2570           printf("NormalizeTable found no cells in row=%d, col=%d\n", rowIndex, colIndex);
  2571 #endif
  2572           return NS_ERROR_FAILURE;
  2575       // Save the last cell found in the same row we are scanning
  2576       if(startRowIndex == rowIndex)
  2578         previousCellInRow = cell;
  2582   return res;
  2585 NS_IMETHODIMP 
  2586 nsHTMLEditor::GetCellIndexes(nsIDOMElement *aCell,
  2587                              int32_t *aRowIndex, int32_t *aColIndex)
  2589   NS_ENSURE_ARG_POINTER(aRowIndex);
  2590   *aColIndex=0; // initialize out params
  2591   NS_ENSURE_ARG_POINTER(aColIndex);
  2592   *aRowIndex=0;
  2593   nsresult res=NS_ERROR_NOT_INITIALIZED;
  2594   if (!aCell)
  2596     // Get the selected cell or the cell enclosing the selection anchor
  2597     nsCOMPtr<nsIDOMElement> cell;
  2598     res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cell));
  2599     if (NS_SUCCEEDED(res) && cell)
  2600       aCell = cell;
  2601     else
  2602       return NS_ERROR_FAILURE;
  2605   NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
  2606   nsCOMPtr<nsIPresShell> ps = GetPresShell();
  2607   NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
  2609   nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aCell) );
  2610   NS_ENSURE_TRUE(nodeAsContent, NS_ERROR_FAILURE);
  2611   // frames are not ref counted, so don't use an nsCOMPtr
  2612   nsIFrame *layoutObject = nodeAsContent->GetPrimaryFrame();
  2613   NS_ENSURE_TRUE(layoutObject, NS_ERROR_FAILURE);
  2615   nsITableCellLayout *cellLayoutObject = do_QueryFrame(layoutObject);
  2616   NS_ENSURE_TRUE(cellLayoutObject, NS_ERROR_FAILURE);
  2617   return cellLayoutObject->GetCellIndexes(*aRowIndex, *aColIndex);
  2620 nsTableOuterFrame*
  2621 nsHTMLEditor::GetTableFrame(nsIDOMElement* aTable)
  2623   NS_ENSURE_TRUE(aTable, nullptr);
  2625   nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aTable) );
  2626   NS_ENSURE_TRUE(nodeAsContent, nullptr);
  2627   return do_QueryFrame(nodeAsContent->GetPrimaryFrame());
  2630 //Return actual number of cells (a cell with colspan > 1 counts as just 1)
  2631 int32_t nsHTMLEditor::GetNumberOfCellsInRow(nsIDOMElement* aTable, int32_t rowIndex)
  2633   int32_t cellCount = 0;
  2634   nsCOMPtr<nsIDOMElement> cell;
  2635   int32_t colIndex = 0;
  2636   nsresult res;
  2637   do {
  2638     int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  2639     bool    isSelected;
  2640     res = GetCellDataAt(aTable, rowIndex, colIndex, getter_AddRefs(cell),
  2641                         &startRowIndex, &startColIndex, &rowSpan, &colSpan, 
  2642                         &actualRowSpan, &actualColSpan, &isSelected);
  2643     NS_ENSURE_SUCCESS(res, 0);
  2644     if (cell)
  2646       // Only count cells that start in row we are working with
  2647       if (startRowIndex == rowIndex)
  2648         cellCount++;
  2650       //Next possible location for a cell
  2651       colIndex += actualColSpan;
  2653     else
  2654       colIndex++;
  2656   } while (cell);
  2658   return cellCount;
  2661 /* Not scriptable: For convenience in C++ 
  2662    Use GetTableRowCount and GetTableColumnCount from JavaScript
  2663 */
  2664 NS_IMETHODIMP
  2665 nsHTMLEditor::GetTableSize(nsIDOMElement *aTable,
  2666                            int32_t* aRowCount, int32_t* aColCount)
  2668   NS_ENSURE_ARG_POINTER(aRowCount);
  2669   NS_ENSURE_ARG_POINTER(aColCount);
  2670   nsresult res;
  2671   *aRowCount = 0;
  2672   *aColCount = 0;
  2673   nsCOMPtr<nsIDOMElement> table;
  2674   // Get the selected talbe or the table enclosing the selection anchor
  2675   res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aTable, getter_AddRefs(table));
  2676   NS_ENSURE_SUCCESS(res, res);
  2677   NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
  2679   nsTableOuterFrame* tableFrame = GetTableFrame(table.get());
  2680   NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);
  2682   *aRowCount = tableFrame->GetRowCount();
  2683   *aColCount = tableFrame->GetColCount();
  2685   return NS_OK;
  2688 NS_IMETHODIMP 
  2689 nsHTMLEditor::GetCellDataAt(nsIDOMElement* aTable, int32_t aRowIndex,
  2690                             int32_t aColIndex, nsIDOMElement **aCell, 
  2691                             int32_t* aStartRowIndex, int32_t* aStartColIndex, 
  2692                             int32_t* aRowSpan, int32_t* aColSpan, 
  2693                             int32_t* aActualRowSpan, int32_t* aActualColSpan, 
  2694                             bool* aIsSelected)
  2696   NS_ENSURE_ARG_POINTER(aStartRowIndex);
  2697   NS_ENSURE_ARG_POINTER(aStartColIndex);
  2698   NS_ENSURE_ARG_POINTER(aRowSpan);
  2699   NS_ENSURE_ARG_POINTER(aColSpan);
  2700   NS_ENSURE_ARG_POINTER(aActualRowSpan);
  2701   NS_ENSURE_ARG_POINTER(aActualColSpan);
  2702   NS_ENSURE_ARG_POINTER(aIsSelected);
  2703   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
  2705   nsresult res=NS_ERROR_FAILURE;
  2706   *aStartRowIndex = 0;
  2707   *aStartColIndex = 0;
  2708   *aRowSpan = 0;
  2709   *aColSpan = 0;
  2710   *aActualRowSpan = 0;
  2711   *aActualColSpan = 0;
  2712   *aIsSelected = false;
  2714   *aCell = nullptr;
  2716   if (!aTable)
  2718     // Get the selected table or the table enclosing the selection anchor
  2719     nsCOMPtr<nsIDOMElement> table;
  2720     res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table));
  2721     NS_ENSURE_SUCCESS(res, res);
  2722     if (table)
  2723       aTable = table;
  2724     else
  2725       return NS_ERROR_FAILURE;
  2728   nsTableOuterFrame* tableFrame = GetTableFrame(aTable);
  2729   NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);
  2731   nsTableCellFrame* cellFrame =
  2732     tableFrame->GetCellFrameAt(aRowIndex, aColIndex);
  2733   if (!cellFrame)
  2734     return NS_ERROR_FAILURE;
  2736   *aIsSelected = cellFrame->IsSelected();
  2737   cellFrame->GetRowIndex(*aStartRowIndex);
  2738   cellFrame->GetColIndex(*aStartColIndex);
  2739   *aRowSpan = cellFrame->GetRowSpan();
  2740   *aColSpan = cellFrame->GetColSpan();
  2741   *aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
  2742   *aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
  2743   nsCOMPtr<nsIDOMElement> domCell = do_QueryInterface(cellFrame->GetContent());
  2744   domCell.forget(aCell);
  2746   return NS_OK;
  2749 // When all you want is the cell
  2750 NS_IMETHODIMP 
  2751 nsHTMLEditor::GetCellAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, nsIDOMElement **aCell)
  2753   NS_ENSURE_ARG_POINTER(aCell);
  2754   *aCell = nullptr;
  2756   if (!aTable)
  2758     // Get the selected table or the table enclosing the selection anchor
  2759     nsCOMPtr<nsIDOMElement> table;
  2760     nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), nullptr, getter_AddRefs(table));
  2761     NS_ENSURE_SUCCESS(res, res);
  2762     NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
  2763     aTable = table;
  2766   nsTableOuterFrame* tableFrame = GetTableFrame(aTable);
  2767   if (!tableFrame)
  2768     return NS_ERROR_FAILURE;
  2770   nsCOMPtr<nsIDOMElement> domCell =
  2771     do_QueryInterface(tableFrame->GetCellAt(aRowIndex, aColIndex));
  2772   domCell.forget(aCell);
  2774   return NS_OK;
  2777 // When all you want are the rowspan and colspan (not exposed in nsITableEditor)
  2778 NS_IMETHODIMP
  2779 nsHTMLEditor::GetCellSpansAt(nsIDOMElement* aTable, int32_t aRowIndex, int32_t aColIndex, 
  2780                              int32_t& aActualRowSpan, int32_t& aActualColSpan)
  2782   nsTableOuterFrame* tableFrame = GetTableFrame(aTable);
  2783   if (!tableFrame)
  2784     return NS_ERROR_FAILURE;
  2786   aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
  2787   aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
  2789   return NS_OK;
  2792 NS_IMETHODIMP
  2793 nsHTMLEditor::GetCellContext(nsISelection **aSelection,
  2794                              nsIDOMElement   **aTable,
  2795                              nsIDOMElement   **aCell,
  2796                              nsIDOMNode      **aCellParent, int32_t *aCellOffset,
  2797                              int32_t *aRowIndex, int32_t *aColIndex)
  2799   // Initialize return pointers
  2800   if (aSelection) *aSelection = nullptr;
  2801   if (aTable) *aTable = nullptr;
  2802   if (aCell) *aCell = nullptr;
  2803   if (aCellParent) *aCellParent = nullptr;
  2804   if (aCellOffset) *aCellOffset = 0;
  2805   if (aRowIndex) *aRowIndex = 0;
  2806   if (aColIndex) *aColIndex = 0;
  2808   nsCOMPtr <nsISelection> selection;
  2809   nsresult res = GetSelection(getter_AddRefs(selection));
  2810   NS_ENSURE_SUCCESS(res, res);
  2811   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  2813   if (aSelection)
  2815     *aSelection = selection.get();
  2816     NS_ADDREF(*aSelection);
  2818   nsCOMPtr <nsIDOMElement> table;
  2819   nsCOMPtr <nsIDOMElement> cell;
  2821   // Caller may supply the cell...
  2822   if (aCell && *aCell)
  2823     cell = *aCell;
  2825   // ...but if not supplied,
  2826   //    get cell if it's the child of selection anchor node,
  2827   //    or get the enclosing by a cell
  2828   if (!cell)
  2830     // Find a selected or enclosing table element
  2831     nsCOMPtr<nsIDOMElement> cellOrTableElement;
  2832     int32_t selectedCount;
  2833     nsAutoString tagName;
  2834     res = GetSelectedOrParentTableElement(tagName, &selectedCount,
  2835                                           getter_AddRefs(cellOrTableElement));
  2836     NS_ENSURE_SUCCESS(res, res);
  2837     if (tagName.EqualsLiteral("table"))
  2839       // We have a selected table, not a cell
  2840       if (aTable)
  2842         *aTable = cellOrTableElement.get();
  2843         NS_ADDREF(*aTable);
  2845       return NS_OK;
  2847     if (!tagName.EqualsLiteral("td"))
  2848       return NS_EDITOR_ELEMENT_NOT_FOUND;
  2850     // We found a cell
  2851     cell = cellOrTableElement;
  2853   if (aCell)
  2855     *aCell = cell.get();
  2856     NS_ADDREF(*aCell);
  2859   // Get containing table
  2860   res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), cell, getter_AddRefs(table));
  2861   NS_ENSURE_SUCCESS(res, res);
  2862   // Cell must be in a table, so fail if not found
  2863   NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
  2864   if (aTable)
  2866     *aTable = table.get();
  2867     NS_ADDREF(*aTable);
  2870   // Get the rest of the related data only if requested
  2871   if (aRowIndex || aColIndex)
  2873     int32_t rowIndex, colIndex;
  2874     // Get current cell location so we can put caret back there when done
  2875     res = GetCellIndexes(cell, &rowIndex, &colIndex);
  2876     if(NS_FAILED(res)) return res;
  2877     if (aRowIndex) *aRowIndex = rowIndex;
  2878     if (aColIndex) *aColIndex = colIndex;
  2880   if (aCellParent)
  2882     nsCOMPtr <nsIDOMNode> cellParent;
  2883     // Get the immediate parent of the cell
  2884     res = cell->GetParentNode(getter_AddRefs(cellParent));
  2885     NS_ENSURE_SUCCESS(res, res);
  2886     // Cell has to have a parent, so fail if not found
  2887     NS_ENSURE_TRUE(cellParent, NS_ERROR_FAILURE);
  2889     *aCellParent = cellParent.get();
  2890     NS_ADDREF(*aCellParent);
  2892     if (aCellOffset) {
  2893       *aCellOffset = GetChildOffset(cell, cellParent);
  2897   return res;
  2900 nsresult 
  2901 nsHTMLEditor::GetCellFromRange(nsIDOMRange *aRange, nsIDOMElement **aCell)
  2903   // Note: this might return a node that is outside of the range.
  2904   // Use carefully.
  2905   NS_ENSURE_TRUE(aRange && aCell, NS_ERROR_NULL_POINTER);
  2907   *aCell = nullptr;
  2909   nsCOMPtr<nsIDOMNode> startParent;
  2910   nsresult res = aRange->GetStartContainer(getter_AddRefs(startParent));
  2911   NS_ENSURE_SUCCESS(res, res);
  2912   NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
  2914   int32_t startOffset;
  2915   res = aRange->GetStartOffset(&startOffset);
  2916   NS_ENSURE_SUCCESS(res, res);
  2918   nsCOMPtr<nsIDOMNode> childNode = GetChildAt(startParent, startOffset);
  2919   // This means selection is probably at a text node (or end of doc?)
  2920   if (!childNode) {
  2921     return NS_ERROR_FAILURE;
  2924   nsCOMPtr<nsIDOMNode> endParent;
  2925   res = aRange->GetEndContainer(getter_AddRefs(endParent));
  2926   NS_ENSURE_SUCCESS(res, res);
  2927   NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
  2929   int32_t endOffset;
  2930   res = aRange->GetEndOffset(&endOffset);
  2931   NS_ENSURE_SUCCESS(res, res);
  2933   // If a cell is deleted, the range is collapse
  2934   //   (startOffset == endOffset)
  2935   //   so tell caller the cell wasn't found
  2936   if (startParent == endParent && 
  2937       endOffset == startOffset+1 &&
  2938       nsHTMLEditUtils::IsTableCell(childNode))
  2940     // Should we also test if frame is selected? (Use GetCellDataAt())
  2941     // (Let's not for now -- more efficient)
  2942     nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childNode);
  2943     *aCell = cellElement.get();
  2944     NS_ADDREF(*aCell);
  2945     return NS_OK;
  2947   return NS_EDITOR_ELEMENT_NOT_FOUND;
  2950 NS_IMETHODIMP 
  2951 nsHTMLEditor::GetFirstSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell)
  2953   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
  2954   *aCell = nullptr;
  2955   if (aRange) *aRange = nullptr;
  2957   nsCOMPtr<nsISelection> selection;
  2958   nsresult res = GetSelection(getter_AddRefs(selection));
  2959   NS_ENSURE_SUCCESS(res, res);
  2960   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  2962   nsCOMPtr<nsIDOMRange> range;
  2963   res = selection->GetRangeAt(0, getter_AddRefs(range));
  2964   NS_ENSURE_SUCCESS(res, res);
  2965   NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
  2967   mSelectedCellIndex = 0;
  2969   res = GetCellFromRange(range, aCell);
  2970   // Failure here probably means selection is in a text node,
  2971   //  so there's no selected cell
  2972   if (NS_FAILED(res)) {
  2973     return NS_EDITOR_ELEMENT_NOT_FOUND;
  2975   // No cell means range was collapsed (cell was deleted)
  2976   if (!*aCell) {
  2977     return NS_EDITOR_ELEMENT_NOT_FOUND;
  2980   if (aRange)
  2982     *aRange = range.get();
  2983     NS_ADDREF(*aRange);
  2986   // Setup for next cell
  2987   mSelectedCellIndex = 1;
  2989   return res;  
  2992 NS_IMETHODIMP
  2993 nsHTMLEditor::GetNextSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell)
  2995   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
  2996   *aCell = nullptr;
  2997   if (aRange) *aRange = nullptr;
  2999   nsCOMPtr<nsISelection> selection;
  3000   nsresult res = GetSelection(getter_AddRefs(selection));
  3001   NS_ENSURE_SUCCESS(res, res);
  3002   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  3004   int32_t rangeCount;
  3005   res = selection->GetRangeCount(&rangeCount);
  3006   NS_ENSURE_SUCCESS(res, res);
  3008   // Don't even try if index exceeds range count
  3009   if (mSelectedCellIndex >= rangeCount) 
  3010     return NS_EDITOR_ELEMENT_NOT_FOUND;
  3012   // Scan through ranges to find next valid selected cell
  3013   nsCOMPtr<nsIDOMRange> range;
  3014   for (; mSelectedCellIndex < rangeCount; mSelectedCellIndex++)
  3016     res = selection->GetRangeAt(mSelectedCellIndex, getter_AddRefs(range));
  3017     NS_ENSURE_SUCCESS(res, res);
  3018     NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
  3020     res = GetCellFromRange(range, aCell);
  3021     // Failure here means the range doesn't contain a cell
  3022     NS_ENSURE_SUCCESS(res, NS_EDITOR_ELEMENT_NOT_FOUND);
  3024     // We found a selected cell
  3025     if (*aCell) break;
  3026 #ifdef DEBUG_cmanske
  3027     else
  3028       printf("GetNextSelectedCell: Collapsed range found\n");
  3029 #endif
  3031     // If we didn't find a cell, continue to next range in selection
  3033   // No cell means all remaining ranges were collapsed (cells were deleted)
  3034   NS_ENSURE_TRUE(*aCell, NS_EDITOR_ELEMENT_NOT_FOUND);
  3036   if (aRange)
  3038     *aRange = range.get();
  3039     NS_ADDREF(*aRange);
  3042   // Setup for next cell
  3043   mSelectedCellIndex++;
  3045   return res;  
  3048 NS_IMETHODIMP 
  3049 nsHTMLEditor::GetFirstSelectedCellInTable(int32_t *aRowIndex, int32_t *aColIndex, nsIDOMElement **aCell)
  3051   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
  3052   *aCell = nullptr;
  3053   if (aRowIndex)
  3054     *aRowIndex = 0;
  3055   if (aColIndex)
  3056     *aColIndex = 0;
  3058   nsCOMPtr<nsIDOMElement> cell;
  3059   nsresult res = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
  3060   NS_ENSURE_SUCCESS(res, res);
  3061   NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
  3063   *aCell = cell.get();
  3064   NS_ADDREF(*aCell);
  3066   // Also return the row and/or column if requested
  3067   if (aRowIndex || aColIndex)
  3069     int32_t startRowIndex, startColIndex;
  3070     res = GetCellIndexes(cell, &startRowIndex, &startColIndex);
  3071     if(NS_FAILED(res)) return res;
  3073     if (aRowIndex)
  3074       *aRowIndex = startRowIndex;
  3076     if (aColIndex)
  3077       *aColIndex = startColIndex;
  3080   return res;
  3083 NS_IMETHODIMP
  3084 nsHTMLEditor::SetSelectionAfterTableEdit(nsIDOMElement* aTable, int32_t aRow, int32_t aCol, 
  3085                                      int32_t aDirection, bool aSelected)
  3087   NS_ENSURE_TRUE(aTable, NS_ERROR_NOT_INITIALIZED);
  3089   nsCOMPtr<nsISelection>selection;
  3090   nsresult res = GetSelection(getter_AddRefs(selection));
  3091   NS_ENSURE_SUCCESS(res, res);
  3093   if (!selection)
  3095 #ifdef DEBUG_cmanske
  3096     printf("Selection not found after table manipulation!\n");
  3097 #endif
  3098     return NS_ERROR_FAILURE;
  3101   nsCOMPtr<nsIDOMElement> cell;
  3102   bool done = false;
  3103   do {
  3104     res = GetCellAt(aTable, aRow, aCol, getter_AddRefs(cell));
  3105     if (NS_SUCCEEDED(res))
  3107       if (cell)
  3109         if (aSelected)
  3111           // Reselect the cell
  3112           return SelectElement(cell);
  3114         else
  3116           // Set the caret to deepest first child
  3117           //   but don't go into nested tables
  3118           // TODO: Should we really be placing the caret at the END
  3119           //  of the cell content?
  3120           return CollapseSelectionToDeepestNonTableFirstChild(selection, cell);
  3122       } else {
  3123         // Setup index to find another cell in the 
  3124         //   direction requested, but move in
  3125         //   other direction if already at beginning of row or column
  3126         switch (aDirection)
  3128           case ePreviousColumn:
  3129             if (aCol == 0)
  3131               if (aRow > 0)
  3132                 aRow--;
  3133               else
  3134                 done = true;
  3136             else
  3137               aCol--;
  3138             break;
  3139           case ePreviousRow:
  3140             if (aRow == 0)
  3142               if (aCol > 0)
  3143                 aCol--;
  3144               else
  3145                 done = true;
  3147             else
  3148               aRow--;
  3149             break;
  3150           default:
  3151             done = true;
  3155     else
  3156       break;
  3157   } while (!done);
  3159   // We didn't find a cell
  3160   // Set selection to just before the table
  3161   nsCOMPtr<nsIDOMNode> tableParent;
  3162   res = aTable->GetParentNode(getter_AddRefs(tableParent));
  3163   if(NS_SUCCEEDED(res) && tableParent)
  3165     int32_t tableOffset = GetChildOffset(aTable, tableParent);
  3166     return selection->Collapse(tableParent, tableOffset);
  3168   // Last resort: Set selection to start of doc
  3169   // (it's very bad to not have a valid selection!)
  3170   return SetSelectionAtDocumentStart(selection);
  3173 NS_IMETHODIMP 
  3174 nsHTMLEditor::GetSelectedOrParentTableElement(nsAString& aTagName,
  3175                                               int32_t *aSelectedCount,
  3176                                               nsIDOMElement** aTableElement)
  3178   NS_ENSURE_ARG_POINTER(aTableElement);
  3179   NS_ENSURE_ARG_POINTER(aSelectedCount);
  3180   *aTableElement = nullptr;
  3181   aTagName.Truncate();
  3182   *aSelectedCount = 0;
  3184   nsCOMPtr<nsISelection> selection;
  3185   nsresult res = GetSelection(getter_AddRefs(selection));
  3186   NS_ENSURE_SUCCESS(res, res);
  3187   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  3189   // Try to get the first selected cell
  3190   nsCOMPtr<nsIDOMElement> tableOrCellElement;
  3191   res = GetFirstSelectedCell(nullptr, getter_AddRefs(tableOrCellElement));
  3192   NS_ENSURE_SUCCESS(res, res);
  3194   NS_NAMED_LITERAL_STRING(tdName, "td");
  3196   if (tableOrCellElement)
  3198       // Each cell is in its own selection range,
  3199       //  so count signals multiple-cell selection
  3200       res = selection->GetRangeCount(aSelectedCount);
  3201       NS_ENSURE_SUCCESS(res, res);
  3202       aTagName = tdName;
  3204   else
  3206     nsCOMPtr<nsIDOMNode> anchorNode;
  3207     res = selection->GetAnchorNode(getter_AddRefs(anchorNode));
  3208     if(NS_FAILED(res)) return res;
  3209     NS_ENSURE_TRUE(anchorNode, NS_ERROR_FAILURE);
  3211     nsCOMPtr<nsIDOMNode> selectedNode;
  3213     // Get child of anchor node, if exists
  3214     bool hasChildren;
  3215     anchorNode->HasChildNodes(&hasChildren);
  3217     if (hasChildren)
  3219       int32_t anchorOffset;
  3220       res = selection->GetAnchorOffset(&anchorOffset);
  3221       NS_ENSURE_SUCCESS(res, res);
  3222       selectedNode = GetChildAt(anchorNode, anchorOffset);
  3223       if (!selectedNode)
  3225         selectedNode = anchorNode;
  3226         // If anchor doesn't have a child, we can't be selecting a table element,
  3227         //  so don't do the following:
  3229       else
  3231         nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(selectedNode);
  3233         if (atom == nsEditProperty::td)
  3235           tableOrCellElement = do_QueryInterface(selectedNode);
  3236           aTagName = tdName;
  3237           // Each cell is in its own selection range,
  3238           //  so count signals multiple-cell selection
  3239           res = selection->GetRangeCount(aSelectedCount);
  3240           NS_ENSURE_SUCCESS(res, res);
  3242         else if (atom == nsEditProperty::table)
  3244           tableOrCellElement = do_QueryInterface(selectedNode);
  3245           aTagName.AssignLiteral("table");
  3246           *aSelectedCount = 1;
  3248         else if (atom == nsEditProperty::tr)
  3250           tableOrCellElement = do_QueryInterface(selectedNode);
  3251           aTagName.AssignLiteral("tr");
  3252           *aSelectedCount = 1;
  3256     if (!tableOrCellElement)
  3258       // Didn't find a table element -- find a cell parent
  3259       res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableOrCellElement));
  3260       if(NS_FAILED(res)) return res;
  3261       if (tableOrCellElement)
  3262         aTagName = tdName;
  3265   if (tableOrCellElement)
  3267     *aTableElement = tableOrCellElement.get();
  3268     NS_ADDREF(*aTableElement);
  3270   return res;
  3273 NS_IMETHODIMP 
  3274 nsHTMLEditor::GetSelectedCellsType(nsIDOMElement *aElement, uint32_t *aSelectionType)
  3276   NS_ENSURE_ARG_POINTER(aSelectionType);
  3277   *aSelectionType = 0;
  3279   // Be sure we have a table element 
  3280   //  (if aElement is null, this uses selection's anchor node)
  3281   nsCOMPtr<nsIDOMElement> table;
  3283   nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("table"), aElement, getter_AddRefs(table));
  3284   NS_ENSURE_SUCCESS(res, res);
  3286   int32_t rowCount, colCount;
  3287   res = GetTableSize(table, &rowCount, &colCount);
  3288   NS_ENSURE_SUCCESS(res, res);
  3290   // Traverse all selected cells 
  3291   nsCOMPtr<nsIDOMElement> selectedCell;
  3292   res = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell));
  3293   NS_ENSURE_SUCCESS(res, res);
  3294   if (res == NS_EDITOR_ELEMENT_NOT_FOUND) return NS_OK;
  3296   // We have at least one selected cell, so set return value
  3297   *aSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
  3299   // Store indexes of each row/col to avoid duplication of searches
  3300   nsTArray<int32_t> indexArray;
  3302   bool allCellsInRowAreSelected = false;
  3303   bool allCellsInColAreSelected = false;
  3304   while (NS_SUCCEEDED(res) && selectedCell)
  3306     // Get the cell's location in the cellmap
  3307     int32_t startRowIndex, startColIndex;
  3308     res = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex);
  3309     if(NS_FAILED(res)) return res;
  3311     if (!indexArray.Contains(startColIndex))
  3313       indexArray.AppendElement(startColIndex);
  3314       allCellsInRowAreSelected = AllCellsInRowSelected(table, startRowIndex, colCount);
  3315       // We're done as soon as we fail for any row
  3316       if (!allCellsInRowAreSelected) break;
  3318     res = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
  3321   if (allCellsInRowAreSelected)
  3323     *aSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
  3324     return NS_OK;
  3326   // Test for columns
  3328   // Empty the indexArray
  3329   indexArray.Clear();
  3331   // Start at first cell again
  3332   res = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell));
  3333   while (NS_SUCCEEDED(res) && selectedCell)
  3335     // Get the cell's location in the cellmap
  3336     int32_t startRowIndex, startColIndex;
  3337     res = GetCellIndexes(selectedCell, &startRowIndex, &startColIndex);
  3338     if(NS_FAILED(res)) return res;
  3340     if (!indexArray.Contains(startRowIndex))
  3342       indexArray.AppendElement(startColIndex);
  3343       allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, rowCount);
  3344       // We're done as soon as we fail for any column
  3345       if (!allCellsInRowAreSelected) break;
  3347     res = GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
  3349   if (allCellsInColAreSelected)
  3350     *aSelectionType = nsISelectionPrivate::TABLESELECTION_COLUMN;
  3352   return NS_OK;
  3355 bool 
  3356 nsHTMLEditor::AllCellsInRowSelected(nsIDOMElement *aTable, int32_t aRowIndex, int32_t aNumberOfColumns)
  3358   NS_ENSURE_TRUE(aTable, false);
  3360   int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  3361   bool    isSelected;
  3363   for( int32_t col = 0; col < aNumberOfColumns; col += std::max(actualColSpan, 1))
  3365     nsCOMPtr<nsIDOMElement> cell;    
  3366     nsresult res = GetCellDataAt(aTable, aRowIndex, col, getter_AddRefs(cell),
  3367                                  &curStartRowIndex, &curStartColIndex,
  3368                                  &rowSpan, &colSpan,
  3369                                  &actualRowSpan, &actualColSpan, &isSelected);
  3371     NS_ENSURE_SUCCESS(res, false);
  3372     // If no cell, we may have a "ragged" right edge,
  3373     //   so return TRUE only if we already found a cell in the row
  3374     NS_ENSURE_TRUE(cell, (col > 0) ? true : false);
  3376     // Return as soon as a non-selected cell is found
  3377     NS_ENSURE_TRUE(isSelected, false);
  3379     NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in AllCellsInRowSelected");
  3381   return true;
  3384 bool 
  3385 nsHTMLEditor::AllCellsInColumnSelected(nsIDOMElement *aTable, int32_t aColIndex, int32_t aNumberOfRows)
  3387   NS_ENSURE_TRUE(aTable, false);
  3389   int32_t curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
  3390   bool    isSelected;
  3392   for( int32_t row = 0; row < aNumberOfRows; row += std::max(actualRowSpan, 1))
  3394     nsCOMPtr<nsIDOMElement> cell;    
  3395     nsresult res = GetCellDataAt(aTable, row, aColIndex, getter_AddRefs(cell),
  3396                                  &curStartRowIndex, &curStartColIndex,
  3397                                  &rowSpan, &colSpan,
  3398                                  &actualRowSpan, &actualColSpan, &isSelected);
  3400     NS_ENSURE_SUCCESS(res, false);
  3401     // If no cell, we must have a "ragged" right edge on the last column
  3402     //   so return TRUE only if we already found a cell in the row
  3403     NS_ENSURE_TRUE(cell, (row > 0) ? true : false);
  3405     // Return as soon as a non-selected cell is found
  3406     NS_ENSURE_TRUE(isSelected, false);
  3408   return true;
  3411 bool 
  3412 nsHTMLEditor::IsEmptyCell(dom::Element* aCell)
  3414   MOZ_ASSERT(aCell);
  3416   // Check if target only contains empty text node or <br>
  3417   nsCOMPtr<nsINode> cellChild = aCell->GetFirstChild();
  3418   if (!cellChild) {
  3419     return false;
  3422   nsCOMPtr<nsINode> nextChild = cellChild->GetNextSibling();
  3423   if (nextChild) {
  3424     return false;
  3427   // We insert a single break into a cell by default
  3428   //   to have some place to locate a cursor -- it is dispensable
  3429   if (cellChild->IsElement() && cellChild->AsElement()->IsHTML(nsGkAtoms::br)) {
  3430     return true;
  3433   bool isEmpty;
  3434   // Or check if no real content
  3435   nsresult rv = IsEmptyNode(cellChild, &isEmpty, false, false);
  3436   NS_ENSURE_SUCCESS(rv, false);
  3437   return isEmpty;

mercurial