From c28f13972fcce1b5c07185c86e3a930c1df53507 Mon Sep 17 00:00:00 2001
From: "cmanske%netscape.com" <cmanske%netscape.com>
Date: Wed, 31 May 2000 00:07:58 +0000
Subject: [PATCH] Added row/column selection in tables: Bug 26833. r=kin,
 a=beppe

---
 content/base/src/nsSelection.cpp | 529 +++++++++++++++++++------------
 layout/base/src/nsSelection.cpp  | 529 +++++++++++++++++++------------
 layout/generic/nsFrame.cpp       | 111 +++----
 layout/generic/nsFrame.h         |  11 +-
 layout/generic/nsSelection.cpp   | 529 +++++++++++++++++++------------
 layout/html/base/src/nsFrame.cpp | 111 +++----
 layout/html/base/src/nsFrame.h   |  11 +-
 7 files changed, 1121 insertions(+), 710 deletions(-)

diff --git a/content/base/src/nsSelection.cpp b/content/base/src/nsSelection.cpp
index debb2d88722e..69ed2dc33881 100644
--- a/content/base/src/nsSelection.cpp
+++ b/content/base/src/nsSelection.cpp
@@ -356,9 +356,12 @@ private:
   nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
   nsresult SelectCellElement(nsIDOMElement* aCellElement);
   nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
+  nsresult ClearNormalSelection();
 
   nsCOMPtr<nsIContent> mStartSelectedCell;
   nsCOMPtr<nsIContent> mEndSelectedCell;
+  nsCOMPtr<nsIContent> mAppendStartSelectedCell;
+  nsCOMPtr<nsIContent> mUnselectCellOnMouseUp;
   PRBool   mSelectingTableCells;
   PRUint32 mSelectingTableCellMode;
   PRInt32  mSelectedCellIndex;
@@ -1555,7 +1558,6 @@ nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset,
   // Don't take focus when dragging off of a table
   if (!mSelectingTableCells)
   {
-    mSelectingTableCellMode = 0;
     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
   }
   
@@ -1726,7 +1728,10 @@ nsSelection::SetMouseDownState(PRBool aState)
   mMouseDownState = aState;
   if (!mMouseDownState)
   {
-    // Mouse up kills table selection
+    // Mouse up kills dragging-table cell selection
+#ifdef DEBUG_cmanske
+printf("SetMouseDownState to FALSE - stopping cell selection\n");
+#endif
     mSelectingTableCells = PR_FALSE;
     mStartSelectedCell = nsnull;
     mEndSelectedCell = nsnull;
@@ -2040,6 +2045,13 @@ nsSelection::GetTableLayout(nsIContent *aTableContent)
   return tableLayoutObject;
 }
 
+nsresult
+nsSelection::ClearNormalSelection()
+{
+  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+  return mDomSelections[index]->ClearSelection();
+}
+
 nsresult
 nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRUint32 aTarget, nsMouseEvent *aMouseEvent)
 {
@@ -2054,13 +2066,13 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(aParentContent);
   if (!parentNode)  return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIContent> selectedContent;
-  nsresult result = aParentContent->ChildAt(aContentOffset, *getter_AddRefs(selectedContent));
+  nsCOMPtr<nsIContent> childContent;
+  nsresult result = aParentContent->ChildAt(aContentOffset, *getter_AddRefs(childContent));
   if (NS_FAILED(result)) return result;
-  if (!selectedContent)  return NS_ERROR_FAILURE;
+  if (!childContent)  return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedContent);
-  if (!selectedNode)  return NS_ERROR_FAILURE;
+  nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
+  if (!childNode)  return NS_ERROR_FAILURE;
 
   // When doing table selection, always set the direction to next
   //  so we can be sure that anchorNode's offset always points to the selected cell
@@ -2071,39 +2083,79 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   //  BeginBatchChanges() / EndBatchChanges()
   nsSelectionBatcher selectionBatcher(mDomSelections[index]);
 
+  PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
   if (mSelectingTableCells)
   {
     // We are drag-selecting
 
     if (aTarget != TABLESELECTION_TABLE)
     {
+      // If dragging in the same cell as last event, do nothing
+      if (mEndSelectedCell == childContent)
+        return NS_OK;
+
       // aTarget can be any "cell mode",
       //  so we can easily drag-select rows and columns 
-      // Once user gets the col or row hit area onclick,
-      //  they can drift into any cell to stay in col/row mode
-      //  (aTarget will be TABLESELECTION_CELL)
-
-      // If dragging in the same cell as last event, do nothing
-      if (mEndSelectedCell == selectedContent)
-        return NS_OK;
+      // Once we are in row or column mode,
+      //  we can drift into any cell to stay in that mode
+      //  even if aTarget = TABLESELECTION_CELL
 
       if (mSelectingTableCellMode == TABLESELECTION_ROW ||
           mSelectingTableCellMode == TABLESELECTION_COLUMN)
       {
-
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Dragged into a new column or row\n");
+        if (mEndSelectedCell)
+        {
+          // Also check if cell is in same row/col
+          result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
+          if (NS_FAILED(result)) return result;
+          result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+          if (NS_FAILED(result)) return result;
+        
+          if ((mSelectingTableCellMode == TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
+              (mSelectingTableCellMode == TABLESELECTION_COLUMN && startColIndex == curColIndex)) 
+            return NS_OK;
+        }
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Dragged into a new column or row\n");
 #endif
-        // Append anthor row or columns to the selection
-        return SelectRowOrColumn(selectedContent, mSelectingTableCellMode);
+        // Continue dragging row or column selection
+        return SelectRowOrColumn(childContent, mSelectingTableCellMode);
       }
       else if (mSelectingTableCellMode == TABLESELECTION_CELL)
       {
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Dragged into a new cell\n");
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Dragged into a new cell\n");
 #endif
+        // Clear this to be sure SelectBlockOfCells works correctly
+        mAppendStartSelectedCell = nsnull;
+        
+        // Trick for quick selection of rows and columns
+        // Hold down shift, then start selecting in one direction
+        // If next cell dragged into is in same row, select entire row,
+        //   if next cell is in same column, select entire column
+        if (mStartSelectedCell && aMouseEvent->isShift)
+        {
+          result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
+          if (NS_FAILED(result)) return result;
+          result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+          if (NS_FAILED(result)) return result;
+          
+          if (startRowIndex == curRowIndex || 
+              startColIndex == curColIndex)
+          {
+            // Force new selection block
+            mStartSelectedCell = nsnull;
+            mDomSelections[index]->ClearSelection();
+
+            mSelectingTableCellMode = 
+              (startRowIndex == curRowIndex) ? TABLESELECTION_ROW : TABLESELECTION_COLUMN;
+
+            return SelectRowOrColumn(childContent, mSelectingTableCellMode);
+          }
+        }
+        
         // Reselect block of cells to new end location
-        return SelectBlockOfCells(selectedContent);
+        return SelectBlockOfCells(childContent);
       }
     }
     // Do nothing if dragging in table, but outside a cell
@@ -2111,153 +2163,189 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   }
   else 
   {
-    // Not dragging  -- mouse event is a click
+    // Not dragging  -- mouse event is down or up
 
+    if (mMouseDownState)
+    {
 #ifdef DEBUG_cmanske
-//#ifdef DEBUG_TABLE
-printf("HandleTableSelection: CLICK event\n");
-printf("HandleTableSelection: Selecting Table\n");
-      {
-        nsIFrame  *frame = nsnull;
-        result = GetTracker()->GetPrimaryFrameFor(selectedContent, &frame);
-        if (frame)
-        {
-          nsRect rect1;
-          frame->GetRect(rect1);
-printf("Frame rect: x=%d, y=%d, right=%d, bottom=%d\n", rect1.x, rect1.y, rect1.XMost(), rect1.YMost());
-          nsRect rect2 = rect1;
-          mDomSelections[index]->GetFrameToRootViewOffset(frame, &rect2.x, &rect2.y);
-printf("Translated frame orgin: x=%d, y=%d\n", rect2.x, rect2.y);
-printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
-printf("*** Widget-relative (refPoint): x=%d, y=%d\n", aMouseEvent->refPoint.x, aMouseEvent->refPoint.y);
-        }
-/*
-  Useful frame methods:
-  GetRect(nsRect& aRect)
-  GetSize(nsSize& aSize)
-*/
-      }
-//#endif
+printf("HandleTableSelection: Mouse down event\n");
 #endif
-
-    if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
-    {
-      // Start drag-selecting mode so multiple rows/cols can be selected
-      mSelectingTableCells = PR_TRUE;
-      mSelectingTableCellMode = aTarget; //only shut off on mouse up.
+      // Clear cell we stored in mouse-down
+      mUnselectCellOnMouseUp = nsnull;
       
-      // Force new selection block
-      mStartSelectedCell = nsnull;
-      mDomSelections[index]->ClearSelection();
-      return SelectRowOrColumn(selectedContent, aTarget);
-    }
-    else if (aTarget == TABLESELECTION_TABLE)
-    {
-      //TODO: We currently select entire table when clicked between cells,
-      //  should we restrict to only around border?
-      //  *** How do we get location data for cell and click?
-      mSelectingTableCells = PR_FALSE;
-      mStartSelectedCell = nsnull;
-      mEndSelectedCell = nsnull;
-
-      // Select the table      
-      mDomSelections[index]->ClearSelection();
-      return CreateAndAddRange(parentNode, aContentOffset);
-    }
-
-    // We are already selecting cells
-    // Check if cell clicked on is already selected  
-    PRInt32 rangeCount;
-    result = mDomSelections[index]->GetRangeCount(&rangeCount);
-    if (NS_FAILED(result)) return result;
-
-    if (rangeCount > 0 && aMouseEvent->isShift && selectedContent != mStartSelectedCell)
-    {
-      // If Shift is down as well, do a block selection
-      return SelectBlockOfCells(selectedContent);
-    }
-    
-    nsCOMPtr<nsIDOMNode> previousCellParent;
-    nsCOMPtr<nsIDOMRange> range;
-    PRInt32 offset;
-    for( PRInt32 i = 0; i < rangeCount; i++)
-    {
-      result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
-      if (NS_FAILED(result)) return result;
-      if (!range) return NS_ERROR_NULL_POINTER;
-
-      nsCOMPtr<nsIDOMNode> parent;
-      result = range->GetStartParent(getter_AddRefs(parent));
-      if (NS_FAILED(result)) return result;
-      if (!parent) return NS_ERROR_NULL_POINTER;
-
-      range->GetStartOffset(&offset);
-      // Be sure previous selection is a table cell
-      nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
-      nsCOMPtr<nsIContent> childContent;
-      result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
-      if (NS_FAILED(result)) return result;
-      if (childContent && IsCell(childContent))
-          previousCellParent = parent;
-
-      // We're done if we didn't find parent of a previously-selected cell
-      if (!previousCellParent) break;
-        
-      if (previousCellParent == parentNode && offset == aContentOffset)
+      if (aTarget == TABLESELECTION_CELL)
       {
-        // Cell is already selected
-        if (rangeCount == 1)
+        PRBool isSelected = PR_FALSE;
+
+        // Check if we have other selected cells
+        nsCOMPtr<nsIDOMNode> previousCellNode;
+        GetFirstSelectedCellAndRange(getter_AddRefs(previousCellNode), nsnull);
+        if (previousCellNode)
         {
-#ifdef DEBUG_TABLE
-          printf("HandleTableSelection: Unselecting single selected cell\n");
-#endif
-          // This was the only cell selected.
-          // Collapse to "normal" selection inside the cell
-          mSelectingTableCells = PR_FALSE;
-          mStartSelectedCell = nsnull;
-          mEndSelectedCell = nsnull;
-          //TODO: We need a "Collapse to just before deepest child" routine
-          // Even better, should we collapse to just after the LAST deepest child
-          //  (i.e., at the end of the cell's contents)?
-          return mDomSelections[index]->Collapse(selectedNode, 0);
+          // We have at least 1 other selected cell
+
+          // Check if new cell is already selected
+          nsIFrame  *cellFrame = nsnull;
+          result = GetTracker()->GetPrimaryFrameFor(childContent, &cellFrame);
+          if (NS_FAILED(result)) return result;
+          if (!cellFrame) return NS_ERROR_NULL_POINTER;
+          result = cellFrame->GetSelected(&isSelected);
+          if (NS_FAILED(result)) return result;
         }
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Removing cell from multi-cell selection\n");
+        else
+        {
+          // No cells selected -- remove non-cell selection
+          mDomSelections[index]->ClearSelection();
+        }
+        mSelectingTableCells = PR_TRUE;    // Signal to start drag-cell-selection
+        mSelectingTableCellMode = aTarget;
+        // Set start for new drag-selection block (not appended)
+        mStartSelectedCell = childContent;
+        // The initial block end is same as the start
+        mEndSelectedCell = childContent;
+        
+        if (isSelected)
+        {
+          // Remember this cell to (possibly) unselect it on mouseup
+          mUnselectCellOnMouseUp = childContent;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
 #endif
-        //TODO: Should we try to reassign to a different existing cell?
-        //mStartSelectedCell = nsnull;
-        //mEndSelectedCell = nsnull;
-        // Other cells are selected:
-        // Deselect cell by removing its range from selection
-        return mDomSelections[index]->RemoveRange(range);
+        }
+        else
+        {
+          // Select an unselected cell
+          // but first remove existing selection if not in same table
+          nsCOMPtr<nsIContent> previousCellContent = do_QueryInterface(previousCellNode);
+          if (!IsInSameTable(previousCellContent, childContent, nsnull))
+            mDomSelections[index]->ClearSelection();
+
+          nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childContent);
+          return SelectCellElement(cellElement);
+        }
+
+        return NS_OK;
+      }
+      else if (aTarget == TABLESELECTION_TABLE)
+      {
+        //TODO: We currently select entire table when clicked between cells,
+        //  should we restrict to only around border?
+        //  *** How do we get location data for cell and click?
+        mSelectingTableCells = PR_FALSE;
+        mStartSelectedCell = nsnull;
+        mEndSelectedCell = nsnull;
+
+        // Remove existing selection and select the table
+        mDomSelections[index]->ClearSelection();
+        return CreateAndAddRange(parentNode, aContentOffset);
+      }
+      else if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
+      {
+        // Start drag-selecting mode so multiple rows/cols can be selected
+        // Note: Currently, nsFrame::GetDataForTableSelection
+        //       will never call us for row or column selection on mouse down
+        mSelectingTableCells = PR_TRUE;
+        mSelectingTableCellMode = aTarget;
+      
+        // Force new selection block
+        mStartSelectedCell = nsnull;
+        mDomSelections[index]->ClearSelection();
+        return SelectRowOrColumn(childContent, aTarget);
       }
     }
-    if (previousCellParent)
+    else
     {
-      // If new cell in a different table is selected, trigger clearing the  selection
-      nsCOMPtr<nsIContent> previousParentContent = do_QueryInterface(previousCellParent);
-      if (!IsInSameTable(selectedContent, previousParentContent, nsnull))
-        previousCellParent = nsnull;
-    }
-    if (!previousCellParent) 
-      // There was no cell selected in the same table - clear selection
-      mDomSelections[index]->ClearSelection();
-
-#ifdef DEBUG_TABLE
-    printf("HandleTableSelection: Adding new selected cell range\n");
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Mouse UP event\n");
 #endif
-    // Append a new selection range that surrounds the cell
-    // It would be nice to make the order of ranges 
-    //  map to the order of cells in table,
-    //  but AddRange doesn't allow that
-    result = CreateAndAddRange(parentNode, aContentOffset);
-    if (NS_SUCCEEDED(result))
-    {
-      mSelectingTableCells = PR_TRUE;
-      mSelectingTableCellMode = TABLESELECTION_CELL;//only shut off on mouse up
+      // First check if we are extending a block selection
+      PRInt32 rangeCount;
+      result = mDomSelections[index]->GetRangeCount(&rangeCount);
+      if (NS_FAILED(result)) return result;
 
-      mStartSelectedCell = selectedContent;
-      mEndSelectedCell = selectedContent;
+      if (rangeCount > 0 && aMouseEvent->isShift && 
+          mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
+      {
+        // If Shift is down as well, append a block selection
+        return SelectBlockOfCells(childContent);
+      }
+    
+      // Unselect a cell only if it wasn't
+      //  just selected on mousedown
+      if( childContent == mUnselectCellOnMouseUp)
+      {
+        // Scan ranges to find the cell to unselect (the selection range to remove)
+        nsCOMPtr<nsIDOMNode> previousCellParent;
+        nsCOMPtr<nsIDOMRange> range;
+        PRInt32 offset;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
+#endif
+        for( PRInt32 i = 0; i < rangeCount; i++)
+        {
+          result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
+          if (NS_FAILED(result)) return result;
+          if (!range) return NS_ERROR_NULL_POINTER;
+
+          nsCOMPtr<nsIDOMNode> parent;
+          result = range->GetStartParent(getter_AddRefs(parent));
+          if (NS_FAILED(result)) return result;
+          if (!parent) return NS_ERROR_NULL_POINTER;
+
+          range->GetStartOffset(&offset);
+          // Be sure previous selection is a table cell
+          nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
+          nsCOMPtr<nsIContent> childContent;
+          result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
+          if (NS_FAILED(result)) return result;
+          if (childContent && IsCell(childContent))
+            previousCellParent = parent;
+
+          // We're done if we didn't find parent of a previously-selected cell
+          if (!previousCellParent) break;
+        
+          if (previousCellParent == parentNode && offset == aContentOffset)
+          {
+            // Cell is already selected
+            if (rangeCount == 1)
+            {
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Unselecting single selected cell\n");
+#endif
+              // This was the only cell selected.
+              // Collapse to "normal" selection inside the cell
+              mSelectingTableCells = PR_FALSE;
+              mStartSelectedCell = nsnull;
+              mEndSelectedCell = nsnull;
+              mSelectingTableCellMode = 0;
+              //TODO: We need a "Collapse to just before deepest child" routine
+              // Even better, should we collapse to just after the LAST deepest child
+              //  (i.e., at the end of the cell's contents)?
+              return mDomSelections[index]->Collapse(childNode, 0);
+            }
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Removing cell from multi-cell selection\n");
+#endif
+            //TODO: Should we try to reassign to a different existing cell?
+            //mStartSelectedCell = nsnull;
+            //mEndSelectedCell = nsnull;
+            // Other cells are selected:
+            // Deselect cell by removing its range from selection
+            return mDomSelections[index]->RemoveRange(range);
+          }
+        }
+        mUnselectCellOnMouseUp = nsnull;
+        // Should we just return here?
+        // (we failed to unselect the cell)
+      }
+      // We have mouse up in a cell that was just selected on mouse down,
+      //  (and no drag or shift-extend action intervened)
+      //  Use it as the start of a block that 
+      //  we may append by using Shift+click in another cell
+      mAppendStartSelectedCell = childContent;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Setting mAppendStartSelectedCell for append block\n");
+#endif
     }
   }
   return result;
@@ -2269,57 +2357,88 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
   if (!aEndCell) return NS_ERROR_NULL_POINTER;
   mEndSelectedCell = aEndCell;
 
-  nsCOMPtr<nsIDOMNode> cellNode;
-  nsCOMPtr<nsIDOMRange> range;
-  nsresult result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
-  if (NS_FAILED(result)) return result;
-  if (!cellNode || !range) return NS_OK;
+  nsCOMPtr<nsIContent> startCell;
+  nsresult result = NS_OK;
 
-  mStartSelectedCell = do_QueryInterface(cellNode);
+  if (mAppendStartSelectedCell)
+  {
+    // We are appending a new block
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- using mAppendStartSelectedCell\n");
+#endif
+    startCell = mAppendStartSelectedCell;
+  }
+  else if (mStartSelectedCell)
+  {
+    startCell = mStartSelectedCell;
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- using mStartSelectedCell\n");
+#endif
+  }
+
+  if (!startCell)
+  {
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- NO START CELL!\n");
+#endif
+    return NS_OK;
+  }
+  // If new end cell is in a different table, do nothing
+  nsCOMPtr<nsIContent> table;
+  if (!IsInSameTable(startCell, aEndCell, getter_AddRefs(table)))
+    return NS_OK;
 
   // Get starting and ending cells' location in the cellmap
   PRInt32 startRowIndex, startColIndex, endRowIndex, endColIndex;
-  result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
+  result = GetCellIndexes(startCell, startRowIndex, startColIndex);
   if(NS_FAILED(result)) return result;
   result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
   if(NS_FAILED(result)) return result;
 
-  // Get parent table, and don't allow selection if start
-  //  and end are not in same table
-  nsCOMPtr<nsIContent> table;
-  if (!IsInSameTable(mStartSelectedCell, aEndCell, getter_AddRefs(table)))
-    return NS_OK;
-
   // Get TableLayout interface to access cell data based on cellmap location
   // frames are not ref counted, so don't use an nsCOMPtr
   nsITableLayout *tableLayoutObject = GetTableLayout(table);
   if (!tableLayoutObject) return NS_ERROR_FAILURE;
 
-  // Remove selected cells outside of new block limits
-
-  PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
-  PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
-  PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
-  PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
   PRInt32 curRowIndex, curColIndex;
 
-  // Examine all cell nodes, starting with first one found above
-  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
-  while (cellNode)
+  if (!mAppendStartSelectedCell)
   {
-    nsCOMPtr<nsIContent> cellContent = do_QueryInterface(cellNode);
-    result = GetCellIndexes(cellContent, curRowIndex, curColIndex);
+    // Not appending - remove selected cells outside of new block limits
+
+    PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+
+    nsCOMPtr<nsIDOMNode> cellNode;
+    nsCOMPtr<nsIDOMRange> range;
+    result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
     if (NS_FAILED(result)) return result;
 
-    if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
-        curColIndex < minColIndex || curColIndex > maxColIndex)
+    PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
+    PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
+    PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
+    PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
+
+    while (cellNode)
     {
-      mDomSelections[index]->RemoveRange(range);
-      // Since we've removed the range, decrement pointer to next range
-      mSelectedCellIndex--;
-    }    
-    result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
-    if (NS_FAILED(result)) return result;
+      nsCOMPtr<nsIContent> childContent = do_QueryInterface(cellNode);
+      result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+      if (NS_FAILED(result)) return result;
+
+#ifdef DEBUG_cmanske
+if (!range)
+printf("SelectBlockOfCells -- range is null\n");
+#endif
+      if (range &&
+          (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
+           curColIndex < minColIndex || curColIndex > maxColIndex))
+      {
+        mDomSelections[index]->RemoveRange(range);
+        // Since we've removed the range, decrement pointer to next range
+        mSelectedCellIndex--;
+      }    
+      result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
+      if (NS_FAILED(result)) return result;
+    }
   }
 
   nsCOMPtr<nsIDOMElement> cellElement;
@@ -2362,7 +2481,6 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
     else
       row--;
   };
-
   return result;
 }
 
@@ -2410,6 +2528,7 @@ nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
     if (NS_FAILED(result)) return result;
     if (cellElement)
     {
+      NS_ASSERTION(actualRowSpan > 0 && actualColSpan> 0, "SelectRowOrColumn: Bad rowspan or colspan\n");
       if (!firstCell)
         firstCell = cellElement;
 
@@ -2434,6 +2553,7 @@ nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
       // We are starting a new block, so select the first cell
       result = SelectCellElement(firstCell);
       if (NS_FAILED(result)) return result;
+      mStartSelectedCell = do_QueryInterface(firstCell);
     }
     nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
     return SelectBlockOfCells(lastCellContent);
@@ -2510,9 +2630,12 @@ nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode
 nsresult 
 nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
 {
-  if (!aCell || !aRange) return NS_ERROR_NULL_POINTER;
+  if (!aCell) return NS_ERROR_NULL_POINTER;
   *aCell = nsnull;
-  *aRange = nsnull;
+
+  // aRange is optional
+  if (aRange)
+    *aRange = nsnull;
 
   nsCOMPtr<nsIDOMRange> firstRange;
   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
@@ -2527,8 +2650,11 @@ nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRan
 
   *aCell = cellNode;
   NS_ADDREF(*aCell);
-  *aRange = firstRange;
-  NS_ADDREF(*aRange);
+  if (aRange)
+  {
+    *aRange = firstRange;
+    NS_ADDREF(*aRange);
+  }
 
   // Setup for next cell
   mSelectedCellIndex = 1;
@@ -2539,9 +2665,12 @@ nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRan
 nsresult
 nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
 {
-  if (!aCell || !aRange) return NS_ERROR_NULL_POINTER;
+  if (!aCell) return NS_ERROR_NULL_POINTER;
   *aCell = nsnull;
-  *aRange = nsnull;
+
+  // aRange is optional
+  if (aRange)
+    *aRange = nsnull;
 
   PRInt32 rangeCount;
   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
@@ -2571,8 +2700,11 @@ nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRang
 
   *aCell = cellNode;
   NS_ADDREF(*aCell);
-  *aRange = range;
-  NS_ADDREF(*aRange);
+  if (aRange)
+  {
+    *aRange = range;
+    NS_ADDREF(*aRange);
+  }
 
   // Setup for next cell
   mSelectedCellIndex++;
@@ -2657,9 +2789,10 @@ nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
   if (NS_FAILED(result)) return result;
   if (!parent) return NS_ERROR_FAILURE;
 
-  // Get child offset
   nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
   nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
+
+  // Get child offset
   PRInt32 offset;
   result = parentContent->IndexOf(cellContent, offset);
   if (NS_FAILED(result)) return result;
diff --git a/layout/base/src/nsSelection.cpp b/layout/base/src/nsSelection.cpp
index debb2d88722e..69ed2dc33881 100644
--- a/layout/base/src/nsSelection.cpp
+++ b/layout/base/src/nsSelection.cpp
@@ -356,9 +356,12 @@ private:
   nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
   nsresult SelectCellElement(nsIDOMElement* aCellElement);
   nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
+  nsresult ClearNormalSelection();
 
   nsCOMPtr<nsIContent> mStartSelectedCell;
   nsCOMPtr<nsIContent> mEndSelectedCell;
+  nsCOMPtr<nsIContent> mAppendStartSelectedCell;
+  nsCOMPtr<nsIContent> mUnselectCellOnMouseUp;
   PRBool   mSelectingTableCells;
   PRUint32 mSelectingTableCellMode;
   PRInt32  mSelectedCellIndex;
@@ -1555,7 +1558,6 @@ nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset,
   // Don't take focus when dragging off of a table
   if (!mSelectingTableCells)
   {
-    mSelectingTableCellMode = 0;
     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
   }
   
@@ -1726,7 +1728,10 @@ nsSelection::SetMouseDownState(PRBool aState)
   mMouseDownState = aState;
   if (!mMouseDownState)
   {
-    // Mouse up kills table selection
+    // Mouse up kills dragging-table cell selection
+#ifdef DEBUG_cmanske
+printf("SetMouseDownState to FALSE - stopping cell selection\n");
+#endif
     mSelectingTableCells = PR_FALSE;
     mStartSelectedCell = nsnull;
     mEndSelectedCell = nsnull;
@@ -2040,6 +2045,13 @@ nsSelection::GetTableLayout(nsIContent *aTableContent)
   return tableLayoutObject;
 }
 
+nsresult
+nsSelection::ClearNormalSelection()
+{
+  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+  return mDomSelections[index]->ClearSelection();
+}
+
 nsresult
 nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRUint32 aTarget, nsMouseEvent *aMouseEvent)
 {
@@ -2054,13 +2066,13 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(aParentContent);
   if (!parentNode)  return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIContent> selectedContent;
-  nsresult result = aParentContent->ChildAt(aContentOffset, *getter_AddRefs(selectedContent));
+  nsCOMPtr<nsIContent> childContent;
+  nsresult result = aParentContent->ChildAt(aContentOffset, *getter_AddRefs(childContent));
   if (NS_FAILED(result)) return result;
-  if (!selectedContent)  return NS_ERROR_FAILURE;
+  if (!childContent)  return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedContent);
-  if (!selectedNode)  return NS_ERROR_FAILURE;
+  nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
+  if (!childNode)  return NS_ERROR_FAILURE;
 
   // When doing table selection, always set the direction to next
   //  so we can be sure that anchorNode's offset always points to the selected cell
@@ -2071,39 +2083,79 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   //  BeginBatchChanges() / EndBatchChanges()
   nsSelectionBatcher selectionBatcher(mDomSelections[index]);
 
+  PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
   if (mSelectingTableCells)
   {
     // We are drag-selecting
 
     if (aTarget != TABLESELECTION_TABLE)
     {
+      // If dragging in the same cell as last event, do nothing
+      if (mEndSelectedCell == childContent)
+        return NS_OK;
+
       // aTarget can be any "cell mode",
       //  so we can easily drag-select rows and columns 
-      // Once user gets the col or row hit area onclick,
-      //  they can drift into any cell to stay in col/row mode
-      //  (aTarget will be TABLESELECTION_CELL)
-
-      // If dragging in the same cell as last event, do nothing
-      if (mEndSelectedCell == selectedContent)
-        return NS_OK;
+      // Once we are in row or column mode,
+      //  we can drift into any cell to stay in that mode
+      //  even if aTarget = TABLESELECTION_CELL
 
       if (mSelectingTableCellMode == TABLESELECTION_ROW ||
           mSelectingTableCellMode == TABLESELECTION_COLUMN)
       {
-
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Dragged into a new column or row\n");
+        if (mEndSelectedCell)
+        {
+          // Also check if cell is in same row/col
+          result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
+          if (NS_FAILED(result)) return result;
+          result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+          if (NS_FAILED(result)) return result;
+        
+          if ((mSelectingTableCellMode == TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
+              (mSelectingTableCellMode == TABLESELECTION_COLUMN && startColIndex == curColIndex)) 
+            return NS_OK;
+        }
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Dragged into a new column or row\n");
 #endif
-        // Append anthor row or columns to the selection
-        return SelectRowOrColumn(selectedContent, mSelectingTableCellMode);
+        // Continue dragging row or column selection
+        return SelectRowOrColumn(childContent, mSelectingTableCellMode);
       }
       else if (mSelectingTableCellMode == TABLESELECTION_CELL)
       {
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Dragged into a new cell\n");
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Dragged into a new cell\n");
 #endif
+        // Clear this to be sure SelectBlockOfCells works correctly
+        mAppendStartSelectedCell = nsnull;
+        
+        // Trick for quick selection of rows and columns
+        // Hold down shift, then start selecting in one direction
+        // If next cell dragged into is in same row, select entire row,
+        //   if next cell is in same column, select entire column
+        if (mStartSelectedCell && aMouseEvent->isShift)
+        {
+          result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
+          if (NS_FAILED(result)) return result;
+          result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+          if (NS_FAILED(result)) return result;
+          
+          if (startRowIndex == curRowIndex || 
+              startColIndex == curColIndex)
+          {
+            // Force new selection block
+            mStartSelectedCell = nsnull;
+            mDomSelections[index]->ClearSelection();
+
+            mSelectingTableCellMode = 
+              (startRowIndex == curRowIndex) ? TABLESELECTION_ROW : TABLESELECTION_COLUMN;
+
+            return SelectRowOrColumn(childContent, mSelectingTableCellMode);
+          }
+        }
+        
         // Reselect block of cells to new end location
-        return SelectBlockOfCells(selectedContent);
+        return SelectBlockOfCells(childContent);
       }
     }
     // Do nothing if dragging in table, but outside a cell
@@ -2111,153 +2163,189 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   }
   else 
   {
-    // Not dragging  -- mouse event is a click
+    // Not dragging  -- mouse event is down or up
 
+    if (mMouseDownState)
+    {
 #ifdef DEBUG_cmanske
-//#ifdef DEBUG_TABLE
-printf("HandleTableSelection: CLICK event\n");
-printf("HandleTableSelection: Selecting Table\n");
-      {
-        nsIFrame  *frame = nsnull;
-        result = GetTracker()->GetPrimaryFrameFor(selectedContent, &frame);
-        if (frame)
-        {
-          nsRect rect1;
-          frame->GetRect(rect1);
-printf("Frame rect: x=%d, y=%d, right=%d, bottom=%d\n", rect1.x, rect1.y, rect1.XMost(), rect1.YMost());
-          nsRect rect2 = rect1;
-          mDomSelections[index]->GetFrameToRootViewOffset(frame, &rect2.x, &rect2.y);
-printf("Translated frame orgin: x=%d, y=%d\n", rect2.x, rect2.y);
-printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
-printf("*** Widget-relative (refPoint): x=%d, y=%d\n", aMouseEvent->refPoint.x, aMouseEvent->refPoint.y);
-        }
-/*
-  Useful frame methods:
-  GetRect(nsRect& aRect)
-  GetSize(nsSize& aSize)
-*/
-      }
-//#endif
+printf("HandleTableSelection: Mouse down event\n");
 #endif
-
-    if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
-    {
-      // Start drag-selecting mode so multiple rows/cols can be selected
-      mSelectingTableCells = PR_TRUE;
-      mSelectingTableCellMode = aTarget; //only shut off on mouse up.
+      // Clear cell we stored in mouse-down
+      mUnselectCellOnMouseUp = nsnull;
       
-      // Force new selection block
-      mStartSelectedCell = nsnull;
-      mDomSelections[index]->ClearSelection();
-      return SelectRowOrColumn(selectedContent, aTarget);
-    }
-    else if (aTarget == TABLESELECTION_TABLE)
-    {
-      //TODO: We currently select entire table when clicked between cells,
-      //  should we restrict to only around border?
-      //  *** How do we get location data for cell and click?
-      mSelectingTableCells = PR_FALSE;
-      mStartSelectedCell = nsnull;
-      mEndSelectedCell = nsnull;
-
-      // Select the table      
-      mDomSelections[index]->ClearSelection();
-      return CreateAndAddRange(parentNode, aContentOffset);
-    }
-
-    // We are already selecting cells
-    // Check if cell clicked on is already selected  
-    PRInt32 rangeCount;
-    result = mDomSelections[index]->GetRangeCount(&rangeCount);
-    if (NS_FAILED(result)) return result;
-
-    if (rangeCount > 0 && aMouseEvent->isShift && selectedContent != mStartSelectedCell)
-    {
-      // If Shift is down as well, do a block selection
-      return SelectBlockOfCells(selectedContent);
-    }
-    
-    nsCOMPtr<nsIDOMNode> previousCellParent;
-    nsCOMPtr<nsIDOMRange> range;
-    PRInt32 offset;
-    for( PRInt32 i = 0; i < rangeCount; i++)
-    {
-      result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
-      if (NS_FAILED(result)) return result;
-      if (!range) return NS_ERROR_NULL_POINTER;
-
-      nsCOMPtr<nsIDOMNode> parent;
-      result = range->GetStartParent(getter_AddRefs(parent));
-      if (NS_FAILED(result)) return result;
-      if (!parent) return NS_ERROR_NULL_POINTER;
-
-      range->GetStartOffset(&offset);
-      // Be sure previous selection is a table cell
-      nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
-      nsCOMPtr<nsIContent> childContent;
-      result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
-      if (NS_FAILED(result)) return result;
-      if (childContent && IsCell(childContent))
-          previousCellParent = parent;
-
-      // We're done if we didn't find parent of a previously-selected cell
-      if (!previousCellParent) break;
-        
-      if (previousCellParent == parentNode && offset == aContentOffset)
+      if (aTarget == TABLESELECTION_CELL)
       {
-        // Cell is already selected
-        if (rangeCount == 1)
+        PRBool isSelected = PR_FALSE;
+
+        // Check if we have other selected cells
+        nsCOMPtr<nsIDOMNode> previousCellNode;
+        GetFirstSelectedCellAndRange(getter_AddRefs(previousCellNode), nsnull);
+        if (previousCellNode)
         {
-#ifdef DEBUG_TABLE
-          printf("HandleTableSelection: Unselecting single selected cell\n");
-#endif
-          // This was the only cell selected.
-          // Collapse to "normal" selection inside the cell
-          mSelectingTableCells = PR_FALSE;
-          mStartSelectedCell = nsnull;
-          mEndSelectedCell = nsnull;
-          //TODO: We need a "Collapse to just before deepest child" routine
-          // Even better, should we collapse to just after the LAST deepest child
-          //  (i.e., at the end of the cell's contents)?
-          return mDomSelections[index]->Collapse(selectedNode, 0);
+          // We have at least 1 other selected cell
+
+          // Check if new cell is already selected
+          nsIFrame  *cellFrame = nsnull;
+          result = GetTracker()->GetPrimaryFrameFor(childContent, &cellFrame);
+          if (NS_FAILED(result)) return result;
+          if (!cellFrame) return NS_ERROR_NULL_POINTER;
+          result = cellFrame->GetSelected(&isSelected);
+          if (NS_FAILED(result)) return result;
         }
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Removing cell from multi-cell selection\n");
+        else
+        {
+          // No cells selected -- remove non-cell selection
+          mDomSelections[index]->ClearSelection();
+        }
+        mSelectingTableCells = PR_TRUE;    // Signal to start drag-cell-selection
+        mSelectingTableCellMode = aTarget;
+        // Set start for new drag-selection block (not appended)
+        mStartSelectedCell = childContent;
+        // The initial block end is same as the start
+        mEndSelectedCell = childContent;
+        
+        if (isSelected)
+        {
+          // Remember this cell to (possibly) unselect it on mouseup
+          mUnselectCellOnMouseUp = childContent;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
 #endif
-        //TODO: Should we try to reassign to a different existing cell?
-        //mStartSelectedCell = nsnull;
-        //mEndSelectedCell = nsnull;
-        // Other cells are selected:
-        // Deselect cell by removing its range from selection
-        return mDomSelections[index]->RemoveRange(range);
+        }
+        else
+        {
+          // Select an unselected cell
+          // but first remove existing selection if not in same table
+          nsCOMPtr<nsIContent> previousCellContent = do_QueryInterface(previousCellNode);
+          if (!IsInSameTable(previousCellContent, childContent, nsnull))
+            mDomSelections[index]->ClearSelection();
+
+          nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childContent);
+          return SelectCellElement(cellElement);
+        }
+
+        return NS_OK;
+      }
+      else if (aTarget == TABLESELECTION_TABLE)
+      {
+        //TODO: We currently select entire table when clicked between cells,
+        //  should we restrict to only around border?
+        //  *** How do we get location data for cell and click?
+        mSelectingTableCells = PR_FALSE;
+        mStartSelectedCell = nsnull;
+        mEndSelectedCell = nsnull;
+
+        // Remove existing selection and select the table
+        mDomSelections[index]->ClearSelection();
+        return CreateAndAddRange(parentNode, aContentOffset);
+      }
+      else if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
+      {
+        // Start drag-selecting mode so multiple rows/cols can be selected
+        // Note: Currently, nsFrame::GetDataForTableSelection
+        //       will never call us for row or column selection on mouse down
+        mSelectingTableCells = PR_TRUE;
+        mSelectingTableCellMode = aTarget;
+      
+        // Force new selection block
+        mStartSelectedCell = nsnull;
+        mDomSelections[index]->ClearSelection();
+        return SelectRowOrColumn(childContent, aTarget);
       }
     }
-    if (previousCellParent)
+    else
     {
-      // If new cell in a different table is selected, trigger clearing the  selection
-      nsCOMPtr<nsIContent> previousParentContent = do_QueryInterface(previousCellParent);
-      if (!IsInSameTable(selectedContent, previousParentContent, nsnull))
-        previousCellParent = nsnull;
-    }
-    if (!previousCellParent) 
-      // There was no cell selected in the same table - clear selection
-      mDomSelections[index]->ClearSelection();
-
-#ifdef DEBUG_TABLE
-    printf("HandleTableSelection: Adding new selected cell range\n");
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Mouse UP event\n");
 #endif
-    // Append a new selection range that surrounds the cell
-    // It would be nice to make the order of ranges 
-    //  map to the order of cells in table,
-    //  but AddRange doesn't allow that
-    result = CreateAndAddRange(parentNode, aContentOffset);
-    if (NS_SUCCEEDED(result))
-    {
-      mSelectingTableCells = PR_TRUE;
-      mSelectingTableCellMode = TABLESELECTION_CELL;//only shut off on mouse up
+      // First check if we are extending a block selection
+      PRInt32 rangeCount;
+      result = mDomSelections[index]->GetRangeCount(&rangeCount);
+      if (NS_FAILED(result)) return result;
 
-      mStartSelectedCell = selectedContent;
-      mEndSelectedCell = selectedContent;
+      if (rangeCount > 0 && aMouseEvent->isShift && 
+          mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
+      {
+        // If Shift is down as well, append a block selection
+        return SelectBlockOfCells(childContent);
+      }
+    
+      // Unselect a cell only if it wasn't
+      //  just selected on mousedown
+      if( childContent == mUnselectCellOnMouseUp)
+      {
+        // Scan ranges to find the cell to unselect (the selection range to remove)
+        nsCOMPtr<nsIDOMNode> previousCellParent;
+        nsCOMPtr<nsIDOMRange> range;
+        PRInt32 offset;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
+#endif
+        for( PRInt32 i = 0; i < rangeCount; i++)
+        {
+          result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
+          if (NS_FAILED(result)) return result;
+          if (!range) return NS_ERROR_NULL_POINTER;
+
+          nsCOMPtr<nsIDOMNode> parent;
+          result = range->GetStartParent(getter_AddRefs(parent));
+          if (NS_FAILED(result)) return result;
+          if (!parent) return NS_ERROR_NULL_POINTER;
+
+          range->GetStartOffset(&offset);
+          // Be sure previous selection is a table cell
+          nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
+          nsCOMPtr<nsIContent> childContent;
+          result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
+          if (NS_FAILED(result)) return result;
+          if (childContent && IsCell(childContent))
+            previousCellParent = parent;
+
+          // We're done if we didn't find parent of a previously-selected cell
+          if (!previousCellParent) break;
+        
+          if (previousCellParent == parentNode && offset == aContentOffset)
+          {
+            // Cell is already selected
+            if (rangeCount == 1)
+            {
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Unselecting single selected cell\n");
+#endif
+              // This was the only cell selected.
+              // Collapse to "normal" selection inside the cell
+              mSelectingTableCells = PR_FALSE;
+              mStartSelectedCell = nsnull;
+              mEndSelectedCell = nsnull;
+              mSelectingTableCellMode = 0;
+              //TODO: We need a "Collapse to just before deepest child" routine
+              // Even better, should we collapse to just after the LAST deepest child
+              //  (i.e., at the end of the cell's contents)?
+              return mDomSelections[index]->Collapse(childNode, 0);
+            }
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Removing cell from multi-cell selection\n");
+#endif
+            //TODO: Should we try to reassign to a different existing cell?
+            //mStartSelectedCell = nsnull;
+            //mEndSelectedCell = nsnull;
+            // Other cells are selected:
+            // Deselect cell by removing its range from selection
+            return mDomSelections[index]->RemoveRange(range);
+          }
+        }
+        mUnselectCellOnMouseUp = nsnull;
+        // Should we just return here?
+        // (we failed to unselect the cell)
+      }
+      // We have mouse up in a cell that was just selected on mouse down,
+      //  (and no drag or shift-extend action intervened)
+      //  Use it as the start of a block that 
+      //  we may append by using Shift+click in another cell
+      mAppendStartSelectedCell = childContent;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Setting mAppendStartSelectedCell for append block\n");
+#endif
     }
   }
   return result;
@@ -2269,57 +2357,88 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
   if (!aEndCell) return NS_ERROR_NULL_POINTER;
   mEndSelectedCell = aEndCell;
 
-  nsCOMPtr<nsIDOMNode> cellNode;
-  nsCOMPtr<nsIDOMRange> range;
-  nsresult result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
-  if (NS_FAILED(result)) return result;
-  if (!cellNode || !range) return NS_OK;
+  nsCOMPtr<nsIContent> startCell;
+  nsresult result = NS_OK;
 
-  mStartSelectedCell = do_QueryInterface(cellNode);
+  if (mAppendStartSelectedCell)
+  {
+    // We are appending a new block
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- using mAppendStartSelectedCell\n");
+#endif
+    startCell = mAppendStartSelectedCell;
+  }
+  else if (mStartSelectedCell)
+  {
+    startCell = mStartSelectedCell;
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- using mStartSelectedCell\n");
+#endif
+  }
+
+  if (!startCell)
+  {
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- NO START CELL!\n");
+#endif
+    return NS_OK;
+  }
+  // If new end cell is in a different table, do nothing
+  nsCOMPtr<nsIContent> table;
+  if (!IsInSameTable(startCell, aEndCell, getter_AddRefs(table)))
+    return NS_OK;
 
   // Get starting and ending cells' location in the cellmap
   PRInt32 startRowIndex, startColIndex, endRowIndex, endColIndex;
-  result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
+  result = GetCellIndexes(startCell, startRowIndex, startColIndex);
   if(NS_FAILED(result)) return result;
   result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
   if(NS_FAILED(result)) return result;
 
-  // Get parent table, and don't allow selection if start
-  //  and end are not in same table
-  nsCOMPtr<nsIContent> table;
-  if (!IsInSameTable(mStartSelectedCell, aEndCell, getter_AddRefs(table)))
-    return NS_OK;
-
   // Get TableLayout interface to access cell data based on cellmap location
   // frames are not ref counted, so don't use an nsCOMPtr
   nsITableLayout *tableLayoutObject = GetTableLayout(table);
   if (!tableLayoutObject) return NS_ERROR_FAILURE;
 
-  // Remove selected cells outside of new block limits
-
-  PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
-  PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
-  PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
-  PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
   PRInt32 curRowIndex, curColIndex;
 
-  // Examine all cell nodes, starting with first one found above
-  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
-  while (cellNode)
+  if (!mAppendStartSelectedCell)
   {
-    nsCOMPtr<nsIContent> cellContent = do_QueryInterface(cellNode);
-    result = GetCellIndexes(cellContent, curRowIndex, curColIndex);
+    // Not appending - remove selected cells outside of new block limits
+
+    PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+
+    nsCOMPtr<nsIDOMNode> cellNode;
+    nsCOMPtr<nsIDOMRange> range;
+    result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
     if (NS_FAILED(result)) return result;
 
-    if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
-        curColIndex < minColIndex || curColIndex > maxColIndex)
+    PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
+    PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
+    PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
+    PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
+
+    while (cellNode)
     {
-      mDomSelections[index]->RemoveRange(range);
-      // Since we've removed the range, decrement pointer to next range
-      mSelectedCellIndex--;
-    }    
-    result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
-    if (NS_FAILED(result)) return result;
+      nsCOMPtr<nsIContent> childContent = do_QueryInterface(cellNode);
+      result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+      if (NS_FAILED(result)) return result;
+
+#ifdef DEBUG_cmanske
+if (!range)
+printf("SelectBlockOfCells -- range is null\n");
+#endif
+      if (range &&
+          (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
+           curColIndex < minColIndex || curColIndex > maxColIndex))
+      {
+        mDomSelections[index]->RemoveRange(range);
+        // Since we've removed the range, decrement pointer to next range
+        mSelectedCellIndex--;
+      }    
+      result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
+      if (NS_FAILED(result)) return result;
+    }
   }
 
   nsCOMPtr<nsIDOMElement> cellElement;
@@ -2362,7 +2481,6 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
     else
       row--;
   };
-
   return result;
 }
 
@@ -2410,6 +2528,7 @@ nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
     if (NS_FAILED(result)) return result;
     if (cellElement)
     {
+      NS_ASSERTION(actualRowSpan > 0 && actualColSpan> 0, "SelectRowOrColumn: Bad rowspan or colspan\n");
       if (!firstCell)
         firstCell = cellElement;
 
@@ -2434,6 +2553,7 @@ nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
       // We are starting a new block, so select the first cell
       result = SelectCellElement(firstCell);
       if (NS_FAILED(result)) return result;
+      mStartSelectedCell = do_QueryInterface(firstCell);
     }
     nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
     return SelectBlockOfCells(lastCellContent);
@@ -2510,9 +2630,12 @@ nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode
 nsresult 
 nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
 {
-  if (!aCell || !aRange) return NS_ERROR_NULL_POINTER;
+  if (!aCell) return NS_ERROR_NULL_POINTER;
   *aCell = nsnull;
-  *aRange = nsnull;
+
+  // aRange is optional
+  if (aRange)
+    *aRange = nsnull;
 
   nsCOMPtr<nsIDOMRange> firstRange;
   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
@@ -2527,8 +2650,11 @@ nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRan
 
   *aCell = cellNode;
   NS_ADDREF(*aCell);
-  *aRange = firstRange;
-  NS_ADDREF(*aRange);
+  if (aRange)
+  {
+    *aRange = firstRange;
+    NS_ADDREF(*aRange);
+  }
 
   // Setup for next cell
   mSelectedCellIndex = 1;
@@ -2539,9 +2665,12 @@ nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRan
 nsresult
 nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
 {
-  if (!aCell || !aRange) return NS_ERROR_NULL_POINTER;
+  if (!aCell) return NS_ERROR_NULL_POINTER;
   *aCell = nsnull;
-  *aRange = nsnull;
+
+  // aRange is optional
+  if (aRange)
+    *aRange = nsnull;
 
   PRInt32 rangeCount;
   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
@@ -2571,8 +2700,11 @@ nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRang
 
   *aCell = cellNode;
   NS_ADDREF(*aCell);
-  *aRange = range;
-  NS_ADDREF(*aRange);
+  if (aRange)
+  {
+    *aRange = range;
+    NS_ADDREF(*aRange);
+  }
 
   // Setup for next cell
   mSelectedCellIndex++;
@@ -2657,9 +2789,10 @@ nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
   if (NS_FAILED(result)) return result;
   if (!parent) return NS_ERROR_FAILURE;
 
-  // Get child offset
   nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
   nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
+
+  // Get child offset
   PRInt32 offset;
   result = parentContent->IndexOf(cellContent, offset);
   if (NS_FAILED(result)) return result;
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index 22f4e307e537..d734237a7393 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -804,10 +804,10 @@ nsFrame::HandleEvent(nsIPresContext* aPresContext,
 }
 
 NS_IMETHODIMP
-nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParentContent,
-                                  PRInt32 *aContentOffset, PRUint32 *aTarget)
+nsFrame::GetDataForTableSelection(nsIFrameSelection *aFrameSelection, nsMouseEvent *aMouseEvent, 
+                                  nsIContent **aParentContent, PRInt32 *aContentOffset, PRUint32 *aTarget)
 {
-  if (!aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
+  if (!aFrameSelection || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
     return NS_ERROR_NULL_POINTER;
 
   *aParentContent = nsnull;
@@ -816,22 +816,33 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
 
   // Test if special 'table selection' key is pressed
   PRBool doTableSelection;
-
+  
 #ifdef XP_MAC
   doTableSelection = aMouseEvent->isMeta;
 #else
   doTableSelection = aMouseEvent->isControl;
 #endif
 
-  if (!doTableSelection) return NS_OK;
+  if (!doTableSelection) 
+  {
+    // We allow table selection when just Shift is pressed
+    //  only if already in table/cell selection mode
+    if (aMouseEvent->isShift)
+      aFrameSelection->GetTableCellSelection(&doTableSelection);
+
+    if (!doTableSelection) 
+      return NS_OK;
+  }
 
   // Get the cell frame or table frame (or parent) of the current content node
   nsIFrame *frame = this;
   nsresult result = NS_OK;
   PRBool foundCell = PR_FALSE;
   PRBool foundTable = PR_FALSE;
-  PRBool selectColumn = PR_FALSE;
-  PRBool selectRow = PR_FALSE;
+  //We don't initiate row/col selection from here now,
+  //  but we may in future
+  //PRBool selectColumn = PR_FALSE;
+  //PRBool selectRow = PR_FALSE;
 
   while (frame && NS_SUCCEEDED(result))
   {
@@ -841,23 +852,8 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
     if (NS_SUCCEEDED(result) && cellElement)
     {
       foundCell = PR_TRUE;
-      PRInt32 colIndex, rowIndex;
-      result = cellElement->GetCellIndexes(rowIndex, colIndex);
-      // Give precedence to row over column
-      if (colIndex == 0)
-      {
-//printf(" * SelRow: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
-        //TODO: We need to test for proximity to top border
-        // For now, just go into row selection mode
-        selectRow = PR_TRUE;
-      }
-      else if (rowIndex == 0)
-      {
-//printf(" * SelCol: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
-        //TODO: We need to test for proximity to left border
-        // For now, just go into COLUMN selection mode
-        selectColumn = PR_TRUE;
-      }
+      //TODO: If we want to use proximity to top or left border
+      //      for row and column selection, this is the place to do it
       break;
     }
     else
@@ -903,11 +899,14 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
 
   *aContentOffset = offset;
 
+#if 0
   if (selectRow)
     *aTarget = TABLESELECTION_ROW;
   else if (selectColumn)
     *aTarget = TABLESELECTION_COLUMN;
-  else if (foundCell)
+  else 
+#endif
+  if (foundCell)
     *aTarget = TABLESELECTION_CELL;
   else if (foundTable)
     *aTarget = TABLESELECTION_TABLE;
@@ -1007,6 +1006,19 @@ nsFrame::HandlePress(nsIPresContext* aPresContext,
   if (NS_FAILED(rv))
     return rv;
 
+  // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
+  nsCOMPtr<nsIContent>parentContent;
+  PRInt32  contentOffset;
+  PRUint32 target;
+  rv = GetDataForTableSelection(frameselection, me, getter_AddRefs(parentContent), &contentOffset, &target);
+  if (NS_SUCCEEDED(rv) && parentContent)
+  {
+    rv = frameselection->SetMouseDownState( PR_TRUE );
+    if (NS_FAILED(rv)) return rv;
+  
+    return frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
+  }
+
   PRBool supportsDelay = PR_FALSE;
 
   frameselection->GetDelayCaretOverExistingSelection(&supportsDelay);
@@ -1071,19 +1083,7 @@ nsFrame::HandlePress(nsIPresContext* aPresContext,
   if (NS_FAILED(rv))
     return rv;
 
-  nsCOMPtr<nsIContent>parentContent;
-  PRInt32  contentOffset;
-  PRUint32 target;
-
-  rv = GetDataForTableSelection(me, getter_AddRefs(parentContent), &contentOffset, &target);
-
-  if (NS_SUCCEEDED(rv) && parentContent)
-    rv = frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
-  else
-    rv = frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent);
-
-  return rv;
-
+  return frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent);
 }
  
 /**
@@ -1269,7 +1269,7 @@ NS_IMETHODIMP nsFrame::HandleDrag(nsIPresContext* aPresContext,
     PRInt32 contentOffset;
     PRUint32 target;
     nsMouseEvent *me = (nsMouseEvent *)aEvent;
-    result = GetDataForTableSelection(me, getter_AddRefs(parentContent), &contentOffset, &target);
+    result = GetDataForTableSelection(frameselection, me, getter_AddRefs(parentContent), &contentOffset, &target);
     if (NS_SUCCEEDED(result) && parentContent)
       frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
     else
@@ -1342,31 +1342,34 @@ NS_IMETHODIMP nsFrame::HandleRelease(nsIPresContext* aPresContext,
 
     if (NS_SUCCEEDED(result) && !mouseDown && me && me->clickCount < 2)
     {
+      // We are doing this to simulate what we would have done on HandlePress
       result = frameselection->SetMouseDownState( PR_TRUE );
 
+      nsCOMPtr<nsIContent> content;
+      PRInt32 startOffset = 0, endOffset = 0;
+      PRBool  beginFrameContent = PR_FALSE;
+
+      result = GetContentAndOffsetsFromPoint(aPresContext, me->point, getter_AddRefs(content), startOffset, endOffset, beginFrameContent);
+      if (NS_FAILED(result)) return result;
+
+      result = frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent);
+      if (NS_FAILED(result)) return result;
+    }
+    else
+    {
+      me = (nsMouseEvent *)aEvent;
       nsCOMPtr<nsIContent>parentContent;
       PRInt32  contentOffset;
       PRUint32 target;
-
-      result = GetDataForTableSelection(me, getter_AddRefs(parentContent), &contentOffset, &target);
+      result = GetDataForTableSelection(frameselection, me, getter_AddRefs(parentContent), &contentOffset, &target);
 
       if (NS_SUCCEEDED(result) && parentContent)
-        result = frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
-      else
       {
-        nsCOMPtr<nsIContent> content;
-        PRInt32 startOffset = 0, endOffset = 0;
-        PRBool  beginFrameContent = PR_FALSE;
-
-        result = GetContentAndOffsetsFromPoint(aPresContext, me->point, getter_AddRefs(content), startOffset, endOffset, beginFrameContent);
-
-        if (NS_FAILED(result))
-          return result;
-
-        result = frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent);
+        frameselection->SetMouseDownState( PR_FALSE );
+        result = frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
+        if (NS_FAILED(result)) return result;
       }
     }
-
     result = frameselection->SetDelayedCaretData(0);
   }
 
diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h
index 58f7722d24a2..9acd2849826f 100644
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -33,6 +33,7 @@
 
 #include "nsIPresShell.h"
 #include "nsIReflowCommand.h"
+#include "nsIFrameSelection.h"
 
 /**
  * nsFrame logging constants. We redefine the nspr
@@ -418,13 +419,15 @@ protected:
   static void GetFirstLeaf(nsIPresContext* aPresContext, nsIFrame **aFrame);
 
   // Test if we are selecting a table object:
-  //  First test if Ctrl (Cmd on Mac) key is down during a mouse click or drag
-  //  If yes, get the parent content node and offset of the frame 
+  //  Most table/cell selection requires that Ctrl (Cmd on Mac) key is down 
+  //   during a mouse click or drag. Exception is using Shift+click when
+  //   already in "table/cell selection mode" to extend a block selection
+  //  Get the parent content node and offset of the frame 
   //   of the enclosing cell or table (if not inside a cell)
   //  aTarget tells us what table element to select (currently only cell and table supported)
   //  (enums for this are defined in nsIFrame.h)
-  NS_IMETHOD GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParentContent, 
-                                      PRInt32 *aContentOffset, PRUint32 *aTarget);
+  NS_IMETHOD GetDataForTableSelection(nsIFrameSelection *aFrameSelection, nsMouseEvent *aMouseEvent, 
+                                      nsIContent **aParentContent, PRInt32 *aContentOffset, PRUint32 *aTarget);
 
   static void XMLQuote(nsString& aString);
 
diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp
index debb2d88722e..69ed2dc33881 100644
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -356,9 +356,12 @@ private:
   nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
   nsresult SelectCellElement(nsIDOMElement* aCellElement);
   nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
+  nsresult ClearNormalSelection();
 
   nsCOMPtr<nsIContent> mStartSelectedCell;
   nsCOMPtr<nsIContent> mEndSelectedCell;
+  nsCOMPtr<nsIContent> mAppendStartSelectedCell;
+  nsCOMPtr<nsIContent> mUnselectCellOnMouseUp;
   PRBool   mSelectingTableCells;
   PRUint32 mSelectingTableCellMode;
   PRInt32  mSelectedCellIndex;
@@ -1555,7 +1558,6 @@ nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset,
   // Don't take focus when dragging off of a table
   if (!mSelectingTableCells)
   {
-    mSelectingTableCellMode = 0;
     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
   }
   
@@ -1726,7 +1728,10 @@ nsSelection::SetMouseDownState(PRBool aState)
   mMouseDownState = aState;
   if (!mMouseDownState)
   {
-    // Mouse up kills table selection
+    // Mouse up kills dragging-table cell selection
+#ifdef DEBUG_cmanske
+printf("SetMouseDownState to FALSE - stopping cell selection\n");
+#endif
     mSelectingTableCells = PR_FALSE;
     mStartSelectedCell = nsnull;
     mEndSelectedCell = nsnull;
@@ -2040,6 +2045,13 @@ nsSelection::GetTableLayout(nsIContent *aTableContent)
   return tableLayoutObject;
 }
 
+nsresult
+nsSelection::ClearNormalSelection()
+{
+  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+  return mDomSelections[index]->ClearSelection();
+}
+
 nsresult
 nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRUint32 aTarget, nsMouseEvent *aMouseEvent)
 {
@@ -2054,13 +2066,13 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(aParentContent);
   if (!parentNode)  return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIContent> selectedContent;
-  nsresult result = aParentContent->ChildAt(aContentOffset, *getter_AddRefs(selectedContent));
+  nsCOMPtr<nsIContent> childContent;
+  nsresult result = aParentContent->ChildAt(aContentOffset, *getter_AddRefs(childContent));
   if (NS_FAILED(result)) return result;
-  if (!selectedContent)  return NS_ERROR_FAILURE;
+  if (!childContent)  return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedContent);
-  if (!selectedNode)  return NS_ERROR_FAILURE;
+  nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
+  if (!childNode)  return NS_ERROR_FAILURE;
 
   // When doing table selection, always set the direction to next
   //  so we can be sure that anchorNode's offset always points to the selected cell
@@ -2071,39 +2083,79 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   //  BeginBatchChanges() / EndBatchChanges()
   nsSelectionBatcher selectionBatcher(mDomSelections[index]);
 
+  PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
   if (mSelectingTableCells)
   {
     // We are drag-selecting
 
     if (aTarget != TABLESELECTION_TABLE)
     {
+      // If dragging in the same cell as last event, do nothing
+      if (mEndSelectedCell == childContent)
+        return NS_OK;
+
       // aTarget can be any "cell mode",
       //  so we can easily drag-select rows and columns 
-      // Once user gets the col or row hit area onclick,
-      //  they can drift into any cell to stay in col/row mode
-      //  (aTarget will be TABLESELECTION_CELL)
-
-      // If dragging in the same cell as last event, do nothing
-      if (mEndSelectedCell == selectedContent)
-        return NS_OK;
+      // Once we are in row or column mode,
+      //  we can drift into any cell to stay in that mode
+      //  even if aTarget = TABLESELECTION_CELL
 
       if (mSelectingTableCellMode == TABLESELECTION_ROW ||
           mSelectingTableCellMode == TABLESELECTION_COLUMN)
       {
-
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Dragged into a new column or row\n");
+        if (mEndSelectedCell)
+        {
+          // Also check if cell is in same row/col
+          result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
+          if (NS_FAILED(result)) return result;
+          result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+          if (NS_FAILED(result)) return result;
+        
+          if ((mSelectingTableCellMode == TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
+              (mSelectingTableCellMode == TABLESELECTION_COLUMN && startColIndex == curColIndex)) 
+            return NS_OK;
+        }
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Dragged into a new column or row\n");
 #endif
-        // Append anthor row or columns to the selection
-        return SelectRowOrColumn(selectedContent, mSelectingTableCellMode);
+        // Continue dragging row or column selection
+        return SelectRowOrColumn(childContent, mSelectingTableCellMode);
       }
       else if (mSelectingTableCellMode == TABLESELECTION_CELL)
       {
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Dragged into a new cell\n");
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Dragged into a new cell\n");
 #endif
+        // Clear this to be sure SelectBlockOfCells works correctly
+        mAppendStartSelectedCell = nsnull;
+        
+        // Trick for quick selection of rows and columns
+        // Hold down shift, then start selecting in one direction
+        // If next cell dragged into is in same row, select entire row,
+        //   if next cell is in same column, select entire column
+        if (mStartSelectedCell && aMouseEvent->isShift)
+        {
+          result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
+          if (NS_FAILED(result)) return result;
+          result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+          if (NS_FAILED(result)) return result;
+          
+          if (startRowIndex == curRowIndex || 
+              startColIndex == curColIndex)
+          {
+            // Force new selection block
+            mStartSelectedCell = nsnull;
+            mDomSelections[index]->ClearSelection();
+
+            mSelectingTableCellMode = 
+              (startRowIndex == curRowIndex) ? TABLESELECTION_ROW : TABLESELECTION_COLUMN;
+
+            return SelectRowOrColumn(childContent, mSelectingTableCellMode);
+          }
+        }
+        
         // Reselect block of cells to new end location
-        return SelectBlockOfCells(selectedContent);
+        return SelectBlockOfCells(childContent);
       }
     }
     // Do nothing if dragging in table, but outside a cell
@@ -2111,153 +2163,189 @@ nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOf
   }
   else 
   {
-    // Not dragging  -- mouse event is a click
+    // Not dragging  -- mouse event is down or up
 
+    if (mMouseDownState)
+    {
 #ifdef DEBUG_cmanske
-//#ifdef DEBUG_TABLE
-printf("HandleTableSelection: CLICK event\n");
-printf("HandleTableSelection: Selecting Table\n");
-      {
-        nsIFrame  *frame = nsnull;
-        result = GetTracker()->GetPrimaryFrameFor(selectedContent, &frame);
-        if (frame)
-        {
-          nsRect rect1;
-          frame->GetRect(rect1);
-printf("Frame rect: x=%d, y=%d, right=%d, bottom=%d\n", rect1.x, rect1.y, rect1.XMost(), rect1.YMost());
-          nsRect rect2 = rect1;
-          mDomSelections[index]->GetFrameToRootViewOffset(frame, &rect2.x, &rect2.y);
-printf("Translated frame orgin: x=%d, y=%d\n", rect2.x, rect2.y);
-printf("Mouse was clicked at: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
-printf("*** Widget-relative (refPoint): x=%d, y=%d\n", aMouseEvent->refPoint.x, aMouseEvent->refPoint.y);
-        }
-/*
-  Useful frame methods:
-  GetRect(nsRect& aRect)
-  GetSize(nsSize& aSize)
-*/
-      }
-//#endif
+printf("HandleTableSelection: Mouse down event\n");
 #endif
-
-    if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
-    {
-      // Start drag-selecting mode so multiple rows/cols can be selected
-      mSelectingTableCells = PR_TRUE;
-      mSelectingTableCellMode = aTarget; //only shut off on mouse up.
+      // Clear cell we stored in mouse-down
+      mUnselectCellOnMouseUp = nsnull;
       
-      // Force new selection block
-      mStartSelectedCell = nsnull;
-      mDomSelections[index]->ClearSelection();
-      return SelectRowOrColumn(selectedContent, aTarget);
-    }
-    else if (aTarget == TABLESELECTION_TABLE)
-    {
-      //TODO: We currently select entire table when clicked between cells,
-      //  should we restrict to only around border?
-      //  *** How do we get location data for cell and click?
-      mSelectingTableCells = PR_FALSE;
-      mStartSelectedCell = nsnull;
-      mEndSelectedCell = nsnull;
-
-      // Select the table      
-      mDomSelections[index]->ClearSelection();
-      return CreateAndAddRange(parentNode, aContentOffset);
-    }
-
-    // We are already selecting cells
-    // Check if cell clicked on is already selected  
-    PRInt32 rangeCount;
-    result = mDomSelections[index]->GetRangeCount(&rangeCount);
-    if (NS_FAILED(result)) return result;
-
-    if (rangeCount > 0 && aMouseEvent->isShift && selectedContent != mStartSelectedCell)
-    {
-      // If Shift is down as well, do a block selection
-      return SelectBlockOfCells(selectedContent);
-    }
-    
-    nsCOMPtr<nsIDOMNode> previousCellParent;
-    nsCOMPtr<nsIDOMRange> range;
-    PRInt32 offset;
-    for( PRInt32 i = 0; i < rangeCount; i++)
-    {
-      result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
-      if (NS_FAILED(result)) return result;
-      if (!range) return NS_ERROR_NULL_POINTER;
-
-      nsCOMPtr<nsIDOMNode> parent;
-      result = range->GetStartParent(getter_AddRefs(parent));
-      if (NS_FAILED(result)) return result;
-      if (!parent) return NS_ERROR_NULL_POINTER;
-
-      range->GetStartOffset(&offset);
-      // Be sure previous selection is a table cell
-      nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
-      nsCOMPtr<nsIContent> childContent;
-      result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
-      if (NS_FAILED(result)) return result;
-      if (childContent && IsCell(childContent))
-          previousCellParent = parent;
-
-      // We're done if we didn't find parent of a previously-selected cell
-      if (!previousCellParent) break;
-        
-      if (previousCellParent == parentNode && offset == aContentOffset)
+      if (aTarget == TABLESELECTION_CELL)
       {
-        // Cell is already selected
-        if (rangeCount == 1)
+        PRBool isSelected = PR_FALSE;
+
+        // Check if we have other selected cells
+        nsCOMPtr<nsIDOMNode> previousCellNode;
+        GetFirstSelectedCellAndRange(getter_AddRefs(previousCellNode), nsnull);
+        if (previousCellNode)
         {
-#ifdef DEBUG_TABLE
-          printf("HandleTableSelection: Unselecting single selected cell\n");
-#endif
-          // This was the only cell selected.
-          // Collapse to "normal" selection inside the cell
-          mSelectingTableCells = PR_FALSE;
-          mStartSelectedCell = nsnull;
-          mEndSelectedCell = nsnull;
-          //TODO: We need a "Collapse to just before deepest child" routine
-          // Even better, should we collapse to just after the LAST deepest child
-          //  (i.e., at the end of the cell's contents)?
-          return mDomSelections[index]->Collapse(selectedNode, 0);
+          // We have at least 1 other selected cell
+
+          // Check if new cell is already selected
+          nsIFrame  *cellFrame = nsnull;
+          result = GetTracker()->GetPrimaryFrameFor(childContent, &cellFrame);
+          if (NS_FAILED(result)) return result;
+          if (!cellFrame) return NS_ERROR_NULL_POINTER;
+          result = cellFrame->GetSelected(&isSelected);
+          if (NS_FAILED(result)) return result;
         }
-#ifdef DEBUG_TABLE
-        printf("HandleTableSelection: Removing cell from multi-cell selection\n");
+        else
+        {
+          // No cells selected -- remove non-cell selection
+          mDomSelections[index]->ClearSelection();
+        }
+        mSelectingTableCells = PR_TRUE;    // Signal to start drag-cell-selection
+        mSelectingTableCellMode = aTarget;
+        // Set start for new drag-selection block (not appended)
+        mStartSelectedCell = childContent;
+        // The initial block end is same as the start
+        mEndSelectedCell = childContent;
+        
+        if (isSelected)
+        {
+          // Remember this cell to (possibly) unselect it on mouseup
+          mUnselectCellOnMouseUp = childContent;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
 #endif
-        //TODO: Should we try to reassign to a different existing cell?
-        //mStartSelectedCell = nsnull;
-        //mEndSelectedCell = nsnull;
-        // Other cells are selected:
-        // Deselect cell by removing its range from selection
-        return mDomSelections[index]->RemoveRange(range);
+        }
+        else
+        {
+          // Select an unselected cell
+          // but first remove existing selection if not in same table
+          nsCOMPtr<nsIContent> previousCellContent = do_QueryInterface(previousCellNode);
+          if (!IsInSameTable(previousCellContent, childContent, nsnull))
+            mDomSelections[index]->ClearSelection();
+
+          nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childContent);
+          return SelectCellElement(cellElement);
+        }
+
+        return NS_OK;
+      }
+      else if (aTarget == TABLESELECTION_TABLE)
+      {
+        //TODO: We currently select entire table when clicked between cells,
+        //  should we restrict to only around border?
+        //  *** How do we get location data for cell and click?
+        mSelectingTableCells = PR_FALSE;
+        mStartSelectedCell = nsnull;
+        mEndSelectedCell = nsnull;
+
+        // Remove existing selection and select the table
+        mDomSelections[index]->ClearSelection();
+        return CreateAndAddRange(parentNode, aContentOffset);
+      }
+      else if (aTarget == TABLESELECTION_ROW || aTarget == TABLESELECTION_COLUMN)
+      {
+        // Start drag-selecting mode so multiple rows/cols can be selected
+        // Note: Currently, nsFrame::GetDataForTableSelection
+        //       will never call us for row or column selection on mouse down
+        mSelectingTableCells = PR_TRUE;
+        mSelectingTableCellMode = aTarget;
+      
+        // Force new selection block
+        mStartSelectedCell = nsnull;
+        mDomSelections[index]->ClearSelection();
+        return SelectRowOrColumn(childContent, aTarget);
       }
     }
-    if (previousCellParent)
+    else
     {
-      // If new cell in a different table is selected, trigger clearing the  selection
-      nsCOMPtr<nsIContent> previousParentContent = do_QueryInterface(previousCellParent);
-      if (!IsInSameTable(selectedContent, previousParentContent, nsnull))
-        previousCellParent = nsnull;
-    }
-    if (!previousCellParent) 
-      // There was no cell selected in the same table - clear selection
-      mDomSelections[index]->ClearSelection();
-
-#ifdef DEBUG_TABLE
-    printf("HandleTableSelection: Adding new selected cell range\n");
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Mouse UP event\n");
 #endif
-    // Append a new selection range that surrounds the cell
-    // It would be nice to make the order of ranges 
-    //  map to the order of cells in table,
-    //  but AddRange doesn't allow that
-    result = CreateAndAddRange(parentNode, aContentOffset);
-    if (NS_SUCCEEDED(result))
-    {
-      mSelectingTableCells = PR_TRUE;
-      mSelectingTableCellMode = TABLESELECTION_CELL;//only shut off on mouse up
+      // First check if we are extending a block selection
+      PRInt32 rangeCount;
+      result = mDomSelections[index]->GetRangeCount(&rangeCount);
+      if (NS_FAILED(result)) return result;
 
-      mStartSelectedCell = selectedContent;
-      mEndSelectedCell = selectedContent;
+      if (rangeCount > 0 && aMouseEvent->isShift && 
+          mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
+      {
+        // If Shift is down as well, append a block selection
+        return SelectBlockOfCells(childContent);
+      }
+    
+      // Unselect a cell only if it wasn't
+      //  just selected on mousedown
+      if( childContent == mUnselectCellOnMouseUp)
+      {
+        // Scan ranges to find the cell to unselect (the selection range to remove)
+        nsCOMPtr<nsIDOMNode> previousCellParent;
+        nsCOMPtr<nsIDOMRange> range;
+        PRInt32 offset;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
+#endif
+        for( PRInt32 i = 0; i < rangeCount; i++)
+        {
+          result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
+          if (NS_FAILED(result)) return result;
+          if (!range) return NS_ERROR_NULL_POINTER;
+
+          nsCOMPtr<nsIDOMNode> parent;
+          result = range->GetStartParent(getter_AddRefs(parent));
+          if (NS_FAILED(result)) return result;
+          if (!parent) return NS_ERROR_NULL_POINTER;
+
+          range->GetStartOffset(&offset);
+          // Be sure previous selection is a table cell
+          nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
+          nsCOMPtr<nsIContent> childContent;
+          result = parentContent->ChildAt(offset, *getter_AddRefs(childContent));
+          if (NS_FAILED(result)) return result;
+          if (childContent && IsCell(childContent))
+            previousCellParent = parent;
+
+          // We're done if we didn't find parent of a previously-selected cell
+          if (!previousCellParent) break;
+        
+          if (previousCellParent == parentNode && offset == aContentOffset)
+          {
+            // Cell is already selected
+            if (rangeCount == 1)
+            {
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Unselecting single selected cell\n");
+#endif
+              // This was the only cell selected.
+              // Collapse to "normal" selection inside the cell
+              mSelectingTableCells = PR_FALSE;
+              mStartSelectedCell = nsnull;
+              mEndSelectedCell = nsnull;
+              mSelectingTableCellMode = 0;
+              //TODO: We need a "Collapse to just before deepest child" routine
+              // Even better, should we collapse to just after the LAST deepest child
+              //  (i.e., at the end of the cell's contents)?
+              return mDomSelections[index]->Collapse(childNode, 0);
+            }
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Removing cell from multi-cell selection\n");
+#endif
+            //TODO: Should we try to reassign to a different existing cell?
+            //mStartSelectedCell = nsnull;
+            //mEndSelectedCell = nsnull;
+            // Other cells are selected:
+            // Deselect cell by removing its range from selection
+            return mDomSelections[index]->RemoveRange(range);
+          }
+        }
+        mUnselectCellOnMouseUp = nsnull;
+        // Should we just return here?
+        // (we failed to unselect the cell)
+      }
+      // We have mouse up in a cell that was just selected on mouse down,
+      //  (and no drag or shift-extend action intervened)
+      //  Use it as the start of a block that 
+      //  we may append by using Shift+click in another cell
+      mAppendStartSelectedCell = childContent;
+#ifdef DEBUG_cmanske
+printf("HandleTableSelection: Setting mAppendStartSelectedCell for append block\n");
+#endif
     }
   }
   return result;
@@ -2269,57 +2357,88 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
   if (!aEndCell) return NS_ERROR_NULL_POINTER;
   mEndSelectedCell = aEndCell;
 
-  nsCOMPtr<nsIDOMNode> cellNode;
-  nsCOMPtr<nsIDOMRange> range;
-  nsresult result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
-  if (NS_FAILED(result)) return result;
-  if (!cellNode || !range) return NS_OK;
+  nsCOMPtr<nsIContent> startCell;
+  nsresult result = NS_OK;
 
-  mStartSelectedCell = do_QueryInterface(cellNode);
+  if (mAppendStartSelectedCell)
+  {
+    // We are appending a new block
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- using mAppendStartSelectedCell\n");
+#endif
+    startCell = mAppendStartSelectedCell;
+  }
+  else if (mStartSelectedCell)
+  {
+    startCell = mStartSelectedCell;
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- using mStartSelectedCell\n");
+#endif
+  }
+
+  if (!startCell)
+  {
+#ifdef DEBUG_cmanske
+printf("SelectBlockOfCells -- NO START CELL!\n");
+#endif
+    return NS_OK;
+  }
+  // If new end cell is in a different table, do nothing
+  nsCOMPtr<nsIContent> table;
+  if (!IsInSameTable(startCell, aEndCell, getter_AddRefs(table)))
+    return NS_OK;
 
   // Get starting and ending cells' location in the cellmap
   PRInt32 startRowIndex, startColIndex, endRowIndex, endColIndex;
-  result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
+  result = GetCellIndexes(startCell, startRowIndex, startColIndex);
   if(NS_FAILED(result)) return result;
   result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
   if(NS_FAILED(result)) return result;
 
-  // Get parent table, and don't allow selection if start
-  //  and end are not in same table
-  nsCOMPtr<nsIContent> table;
-  if (!IsInSameTable(mStartSelectedCell, aEndCell, getter_AddRefs(table)))
-    return NS_OK;
-
   // Get TableLayout interface to access cell data based on cellmap location
   // frames are not ref counted, so don't use an nsCOMPtr
   nsITableLayout *tableLayoutObject = GetTableLayout(table);
   if (!tableLayoutObject) return NS_ERROR_FAILURE;
 
-  // Remove selected cells outside of new block limits
-
-  PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
-  PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
-  PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
-  PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
   PRInt32 curRowIndex, curColIndex;
 
-  // Examine all cell nodes, starting with first one found above
-  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
-  while (cellNode)
+  if (!mAppendStartSelectedCell)
   {
-    nsCOMPtr<nsIContent> cellContent = do_QueryInterface(cellNode);
-    result = GetCellIndexes(cellContent, curRowIndex, curColIndex);
+    // Not appending - remove selected cells outside of new block limits
+
+    PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+
+    nsCOMPtr<nsIDOMNode> cellNode;
+    nsCOMPtr<nsIDOMRange> range;
+    result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
     if (NS_FAILED(result)) return result;
 
-    if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
-        curColIndex < minColIndex || curColIndex > maxColIndex)
+    PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
+    PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
+    PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
+    PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
+
+    while (cellNode)
     {
-      mDomSelections[index]->RemoveRange(range);
-      // Since we've removed the range, decrement pointer to next range
-      mSelectedCellIndex--;
-    }    
-    result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
-    if (NS_FAILED(result)) return result;
+      nsCOMPtr<nsIContent> childContent = do_QueryInterface(cellNode);
+      result = GetCellIndexes(childContent, curRowIndex, curColIndex);
+      if (NS_FAILED(result)) return result;
+
+#ifdef DEBUG_cmanske
+if (!range)
+printf("SelectBlockOfCells -- range is null\n");
+#endif
+      if (range &&
+          (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
+           curColIndex < minColIndex || curColIndex > maxColIndex))
+      {
+        mDomSelections[index]->RemoveRange(range);
+        // Since we've removed the range, decrement pointer to next range
+        mSelectedCellIndex--;
+      }    
+      result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
+      if (NS_FAILED(result)) return result;
+    }
   }
 
   nsCOMPtr<nsIDOMElement> cellElement;
@@ -2362,7 +2481,6 @@ nsSelection::SelectBlockOfCells(nsIContent *aEndCell)
     else
       row--;
   };
-
   return result;
 }
 
@@ -2410,6 +2528,7 @@ nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
     if (NS_FAILED(result)) return result;
     if (cellElement)
     {
+      NS_ASSERTION(actualRowSpan > 0 && actualColSpan> 0, "SelectRowOrColumn: Bad rowspan or colspan\n");
       if (!firstCell)
         firstCell = cellElement;
 
@@ -2434,6 +2553,7 @@ nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
       // We are starting a new block, so select the first cell
       result = SelectCellElement(firstCell);
       if (NS_FAILED(result)) return result;
+      mStartSelectedCell = do_QueryInterface(firstCell);
     }
     nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
     return SelectBlockOfCells(lastCellContent);
@@ -2510,9 +2630,12 @@ nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode
 nsresult 
 nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
 {
-  if (!aCell || !aRange) return NS_ERROR_NULL_POINTER;
+  if (!aCell) return NS_ERROR_NULL_POINTER;
   *aCell = nsnull;
-  *aRange = nsnull;
+
+  // aRange is optional
+  if (aRange)
+    *aRange = nsnull;
 
   nsCOMPtr<nsIDOMRange> firstRange;
   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
@@ -2527,8 +2650,11 @@ nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRan
 
   *aCell = cellNode;
   NS_ADDREF(*aCell);
-  *aRange = firstRange;
-  NS_ADDREF(*aRange);
+  if (aRange)
+  {
+    *aRange = firstRange;
+    NS_ADDREF(*aRange);
+  }
 
   // Setup for next cell
   mSelectedCellIndex = 1;
@@ -2539,9 +2665,12 @@ nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRan
 nsresult
 nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
 {
-  if (!aCell || !aRange) return NS_ERROR_NULL_POINTER;
+  if (!aCell) return NS_ERROR_NULL_POINTER;
   *aCell = nsnull;
-  *aRange = nsnull;
+
+  // aRange is optional
+  if (aRange)
+    *aRange = nsnull;
 
   PRInt32 rangeCount;
   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
@@ -2571,8 +2700,11 @@ nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRang
 
   *aCell = cellNode;
   NS_ADDREF(*aCell);
-  *aRange = range;
-  NS_ADDREF(*aRange);
+  if (aRange)
+  {
+    *aRange = range;
+    NS_ADDREF(*aRange);
+  }
 
   // Setup for next cell
   mSelectedCellIndex++;
@@ -2657,9 +2789,10 @@ nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
   if (NS_FAILED(result)) return result;
   if (!parent) return NS_ERROR_FAILURE;
 
-  // Get child offset
   nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
   nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
+
+  // Get child offset
   PRInt32 offset;
   result = parentContent->IndexOf(cellContent, offset);
   if (NS_FAILED(result)) return result;
diff --git a/layout/html/base/src/nsFrame.cpp b/layout/html/base/src/nsFrame.cpp
index 22f4e307e537..d734237a7393 100644
--- a/layout/html/base/src/nsFrame.cpp
+++ b/layout/html/base/src/nsFrame.cpp
@@ -804,10 +804,10 @@ nsFrame::HandleEvent(nsIPresContext* aPresContext,
 }
 
 NS_IMETHODIMP
-nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParentContent,
-                                  PRInt32 *aContentOffset, PRUint32 *aTarget)
+nsFrame::GetDataForTableSelection(nsIFrameSelection *aFrameSelection, nsMouseEvent *aMouseEvent, 
+                                  nsIContent **aParentContent, PRInt32 *aContentOffset, PRUint32 *aTarget)
 {
-  if (!aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
+  if (!aFrameSelection || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
     return NS_ERROR_NULL_POINTER;
 
   *aParentContent = nsnull;
@@ -816,22 +816,33 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
 
   // Test if special 'table selection' key is pressed
   PRBool doTableSelection;
-
+  
 #ifdef XP_MAC
   doTableSelection = aMouseEvent->isMeta;
 #else
   doTableSelection = aMouseEvent->isControl;
 #endif
 
-  if (!doTableSelection) return NS_OK;
+  if (!doTableSelection) 
+  {
+    // We allow table selection when just Shift is pressed
+    //  only if already in table/cell selection mode
+    if (aMouseEvent->isShift)
+      aFrameSelection->GetTableCellSelection(&doTableSelection);
+
+    if (!doTableSelection) 
+      return NS_OK;
+  }
 
   // Get the cell frame or table frame (or parent) of the current content node
   nsIFrame *frame = this;
   nsresult result = NS_OK;
   PRBool foundCell = PR_FALSE;
   PRBool foundTable = PR_FALSE;
-  PRBool selectColumn = PR_FALSE;
-  PRBool selectRow = PR_FALSE;
+  //We don't initiate row/col selection from here now,
+  //  but we may in future
+  //PRBool selectColumn = PR_FALSE;
+  //PRBool selectRow = PR_FALSE;
 
   while (frame && NS_SUCCEEDED(result))
   {
@@ -841,23 +852,8 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
     if (NS_SUCCEEDED(result) && cellElement)
     {
       foundCell = PR_TRUE;
-      PRInt32 colIndex, rowIndex;
-      result = cellElement->GetCellIndexes(rowIndex, colIndex);
-      // Give precedence to row over column
-      if (colIndex == 0)
-      {
-//printf(" * SelRow: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
-        //TODO: We need to test for proximity to top border
-        // For now, just go into row selection mode
-        selectRow = PR_TRUE;
-      }
-      else if (rowIndex == 0)
-      {
-//printf(" * SelCol: x=%d, y=%d\n", aMouseEvent->point.x, aMouseEvent->point.y);
-        //TODO: We need to test for proximity to left border
-        // For now, just go into COLUMN selection mode
-        selectColumn = PR_TRUE;
-      }
+      //TODO: If we want to use proximity to top or left border
+      //      for row and column selection, this is the place to do it
       break;
     }
     else
@@ -903,11 +899,14 @@ nsFrame::GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParen
 
   *aContentOffset = offset;
 
+#if 0
   if (selectRow)
     *aTarget = TABLESELECTION_ROW;
   else if (selectColumn)
     *aTarget = TABLESELECTION_COLUMN;
-  else if (foundCell)
+  else 
+#endif
+  if (foundCell)
     *aTarget = TABLESELECTION_CELL;
   else if (foundTable)
     *aTarget = TABLESELECTION_TABLE;
@@ -1007,6 +1006,19 @@ nsFrame::HandlePress(nsIPresContext* aPresContext,
   if (NS_FAILED(rv))
     return rv;
 
+  // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
+  nsCOMPtr<nsIContent>parentContent;
+  PRInt32  contentOffset;
+  PRUint32 target;
+  rv = GetDataForTableSelection(frameselection, me, getter_AddRefs(parentContent), &contentOffset, &target);
+  if (NS_SUCCEEDED(rv) && parentContent)
+  {
+    rv = frameselection->SetMouseDownState( PR_TRUE );
+    if (NS_FAILED(rv)) return rv;
+  
+    return frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
+  }
+
   PRBool supportsDelay = PR_FALSE;
 
   frameselection->GetDelayCaretOverExistingSelection(&supportsDelay);
@@ -1071,19 +1083,7 @@ nsFrame::HandlePress(nsIPresContext* aPresContext,
   if (NS_FAILED(rv))
     return rv;
 
-  nsCOMPtr<nsIContent>parentContent;
-  PRInt32  contentOffset;
-  PRUint32 target;
-
-  rv = GetDataForTableSelection(me, getter_AddRefs(parentContent), &contentOffset, &target);
-
-  if (NS_SUCCEEDED(rv) && parentContent)
-    rv = frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
-  else
-    rv = frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent);
-
-  return rv;
-
+  return frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent);
 }
  
 /**
@@ -1269,7 +1269,7 @@ NS_IMETHODIMP nsFrame::HandleDrag(nsIPresContext* aPresContext,
     PRInt32 contentOffset;
     PRUint32 target;
     nsMouseEvent *me = (nsMouseEvent *)aEvent;
-    result = GetDataForTableSelection(me, getter_AddRefs(parentContent), &contentOffset, &target);
+    result = GetDataForTableSelection(frameselection, me, getter_AddRefs(parentContent), &contentOffset, &target);
     if (NS_SUCCEEDED(result) && parentContent)
       frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
     else
@@ -1342,31 +1342,34 @@ NS_IMETHODIMP nsFrame::HandleRelease(nsIPresContext* aPresContext,
 
     if (NS_SUCCEEDED(result) && !mouseDown && me && me->clickCount < 2)
     {
+      // We are doing this to simulate what we would have done on HandlePress
       result = frameselection->SetMouseDownState( PR_TRUE );
 
+      nsCOMPtr<nsIContent> content;
+      PRInt32 startOffset = 0, endOffset = 0;
+      PRBool  beginFrameContent = PR_FALSE;
+
+      result = GetContentAndOffsetsFromPoint(aPresContext, me->point, getter_AddRefs(content), startOffset, endOffset, beginFrameContent);
+      if (NS_FAILED(result)) return result;
+
+      result = frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent);
+      if (NS_FAILED(result)) return result;
+    }
+    else
+    {
+      me = (nsMouseEvent *)aEvent;
       nsCOMPtr<nsIContent>parentContent;
       PRInt32  contentOffset;
       PRUint32 target;
-
-      result = GetDataForTableSelection(me, getter_AddRefs(parentContent), &contentOffset, &target);
+      result = GetDataForTableSelection(frameselection, me, getter_AddRefs(parentContent), &contentOffset, &target);
 
       if (NS_SUCCEEDED(result) && parentContent)
-        result = frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
-      else
       {
-        nsCOMPtr<nsIContent> content;
-        PRInt32 startOffset = 0, endOffset = 0;
-        PRBool  beginFrameContent = PR_FALSE;
-
-        result = GetContentAndOffsetsFromPoint(aPresContext, me->point, getter_AddRefs(content), startOffset, endOffset, beginFrameContent);
-
-        if (NS_FAILED(result))
-          return result;
-
-        result = frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent);
+        frameselection->SetMouseDownState( PR_FALSE );
+        result = frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
+        if (NS_FAILED(result)) return result;
       }
     }
-
     result = frameselection->SetDelayedCaretData(0);
   }
 
diff --git a/layout/html/base/src/nsFrame.h b/layout/html/base/src/nsFrame.h
index 58f7722d24a2..9acd2849826f 100644
--- a/layout/html/base/src/nsFrame.h
+++ b/layout/html/base/src/nsFrame.h
@@ -33,6 +33,7 @@
 
 #include "nsIPresShell.h"
 #include "nsIReflowCommand.h"
+#include "nsIFrameSelection.h"
 
 /**
  * nsFrame logging constants. We redefine the nspr
@@ -418,13 +419,15 @@ protected:
   static void GetFirstLeaf(nsIPresContext* aPresContext, nsIFrame **aFrame);
 
   // Test if we are selecting a table object:
-  //  First test if Ctrl (Cmd on Mac) key is down during a mouse click or drag
-  //  If yes, get the parent content node and offset of the frame 
+  //  Most table/cell selection requires that Ctrl (Cmd on Mac) key is down 
+  //   during a mouse click or drag. Exception is using Shift+click when
+  //   already in "table/cell selection mode" to extend a block selection
+  //  Get the parent content node and offset of the frame 
   //   of the enclosing cell or table (if not inside a cell)
   //  aTarget tells us what table element to select (currently only cell and table supported)
   //  (enums for this are defined in nsIFrame.h)
-  NS_IMETHOD GetDataForTableSelection(nsMouseEvent *aMouseEvent, nsIContent **aParentContent, 
-                                      PRInt32 *aContentOffset, PRUint32 *aTarget);
+  NS_IMETHOD GetDataForTableSelection(nsIFrameSelection *aFrameSelection, nsMouseEvent *aMouseEvent, 
+                                      nsIContent **aParentContent, PRInt32 *aContentOffset, PRUint32 *aTarget);
 
   static void XMLQuote(nsString& aString);