Bug 371839. Simplify SetSelected signature and semantics, and reimplement it in nsTextFrame much more efficiently. r=bzbarsky
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 27 Jul 2009 10:05:41 +1200
changeset 30695 21085f6713149b1f9f616fd588601693bb2a6466
parent 30694 a0b42fa6b477091e87a45a071eeac65c5c31241d
child 30696 063330fb2f27fecafd9a532e479280973f1e1151
push idunknown
push userunknown
push dateunknown
reviewersbzbarsky
bugs371839
milestone1.9.2a1pre
Bug 371839. Simplify SetSelected signature and semantics, and reimplement it in nsTextFrame much more efficiently. r=bzbarsky
content/base/src/nsGenericDOMDataNode.h
layout/generic/nsFirstLetterFrame.cpp
layout/generic/nsFirstLetterFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsIFrame.h
layout/generic/nsSelection.cpp
layout/generic/nsTextFrame.h
layout/generic/nsTextFrameThebes.cpp
layout/svg/base/src/nsSVGGlyphFrame.cpp
layout/svg/base/src/nsSVGGlyphFrame.h
layout/tables/nsTableOuterFrame.cpp
layout/tables/nsTableOuterFrame.h
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -38,39 +38,43 @@
 /*
  * Base class for DOM Core's nsIDOMComment, nsIDOMDocumentType, nsIDOMText,
  * nsIDOMCDATASection, and nsIDOMProcessingInstruction nodes.
  */
 
 #ifndef nsGenericDOMDataNode_h___
 #define nsGenericDOMDataNode_h___
 
-// This bit is set to indicate that if the text node changes to
-// non-whitespace, we may need to create a frame for it. This bit must
-// not be set on nodes that already have a frame.
-#define NS_CREATE_FRAME_IF_NON_WHITESPACE (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET)
-
-// This bit is set to indicate that if the text node changes to
-// whitespace, we may need to reframe it (or its ancestors).
-#define NS_REFRAME_IF_WHITESPACE (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 1))
-
+#include "nsIContent.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOM3Text.h"
 #include "nsTextFragment.h"
 #include "nsDOMError.h"
 #include "nsIEventListenerManager.h"
 #include "nsGenericElement.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsContentUtils.h"
 
 #ifdef MOZ_SMIL
 #include "nsISMILAttr.h"
 #endif // MOZ_SMIL
 
+// This bit is set to indicate that if the text node changes to
+// non-whitespace, we may need to create a frame for it. This bit must
+// not be set on nodes that already have a frame.
+#define NS_CREATE_FRAME_IF_NON_WHITESPACE (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET)
+
+// This bit is set to indicate that if the text node changes to
+// whitespace, we may need to reframe it (or its ancestors).
+#define NS_REFRAME_IF_WHITESPACE (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 1))
+
+// This bit is set to indicate that the text may be part of a selection.
+#define NS_TEXT_IN_SELECTION (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 2))
+
 class nsIDOMAttr;
 class nsIDOMEventListener;
 class nsIDOMNodeList;
 class nsIFrame;
 class nsIDOMText;
 class nsINodeInfo;
 class nsURI;
 
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -106,31 +106,16 @@ nsFirstLetterFrame::SetInitialChildList(
   for (nsIFrame* frame = aChildList; frame; frame = frame->GetNextSibling()) {
     NS_ASSERTION(frame->GetParent() == this, "Unexpected parent");
     frameManager->ReParentStyleContext(frame);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsFirstLetterFrame::SetSelected(nsPresContext* aPresContext, nsIDOMRange *aRange,PRBool aSelected, nsSpread aSpread, SelectionType aType)
-{
-  if (aSelected && ParentDisablesSelection())
-    return NS_OK;
-  nsIFrame *child = GetFirstChild(nsnull);
-  while (child)
-  {
-    child->SetSelected(aPresContext, aRange, aSelected, aSpread, aType);
-    // don't worry about result. there are more frames to come
-    child = child->GetNextSibling();
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsFirstLetterFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset,
                                                   PRBool inHint,
                                                   PRInt32* outFrameContentOffset,
                                                   nsIFrame **outChildFrame)
 {
   nsIFrame *kid = mFrames.FirstChild();
   if (kid)
   {
--- a/layout/generic/nsFirstLetterFrame.h
+++ b/layout/generic/nsFirstLetterFrame.h
@@ -78,18 +78,16 @@ public:
                              PRBool aShrinkWrap);
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   virtual PRBool CanContinueTextRun() const;
 
-  NS_IMETHOD SetSelected(nsPresContext* aPresContext, nsIDOMRange *aRange,PRBool aSelected, nsSpread aSpread, SelectionType aType);
-
 //override of nsFrame method
   NS_IMETHOD GetChildFrameContainingOffset(PRInt32 inContentOffset,
                                            PRBool inHint,
                                            PRInt32* outFrameContentOffset,
                                            nsIFrame **outChildFrame);
 
   nscoord GetFirstLetterBaseline() const { return mBaseline; }
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4482,53 +4482,40 @@ nsFrame::DumpBaseRegressionData(nsPresCo
       IndentBy(out, aIndent);
       fprintf(out, "</child-list>\n");
     }
     list = GetAdditionalChildListName(listIndex++);
   } while (nsnull != list);
 }
 #endif
 
-/*this method may.. invalidate if the state was changed or if aForceRedraw is PR_TRUE
-  it will not update immediately.*/
-NS_IMETHODIMP
-nsFrame::SetSelected(nsPresContext* aPresContext, nsIDOMRange *aRange, PRBool aSelected, nsSpread aSpread, SelectionType aType)
-{
-/*
-  if (aSelected && ParentDisablesSelection())
-    return NS_OK;
-*/
-
-  if (aType == nsISelectionController::SELECTION_NORMAL) {
-    // check whether style allows selection
-    PRBool  selectable;
-    IsSelectable(&selectable, nsnull);
-    if (!selectable)
-      return NS_OK;
-  }
-
-/*
-  if (eSpreadDown == aSpread){
-    nsIFrame* kid = GetFirstChild(nsnull);
-    while (nsnull != kid) {
-      kid->SetSelected(nsnull,aSelected,aSpread);
-      kid = kid->GetNextSibling();
-    }
-  }
-*/
-  if ( aSelected ){
-    AddStateBits(NS_FRAME_SELECTED_CONTENT);
-  }
-  else
-    RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
-
-  // Repaint this frame subtree's entire area
-  InvalidateOverflowRect();
-
-  return NS_OK;
+void
+nsIFrame::SetSelected(PRBool aSelected, SelectionType aType)
+{
+  NS_ASSERTION(!GetPrevContinuation(),
+               "Should only be called on first in flow");
+  if (aType != nsISelectionController::SELECTION_NORMAL)
+    return;
+
+  // check whether style allows selection
+  PRBool selectable;
+  IsSelectable(&selectable, nsnull);
+  if (!selectable)
+    return;
+
+  for (nsIFrame* f = this; f; f = f->GetNextContinuation()) {
+    if (aSelected) {
+      AddStateBits(NS_FRAME_SELECTED_CONTENT);
+    } else {
+      RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
+    }
+
+    // Repaint this frame subtree's entire area
+    InvalidateOverflowRect();
+  }
 }
 
 NS_IMETHODIMP
 nsFrame::GetSelected(PRBool *aSelected) const
 {
   if (!aSelected )
     return NS_ERROR_NULL_POINTER;
   *aSelected = !!(mState & NS_FRAME_SELECTED_CONTENT);
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -232,17 +232,16 @@ public:
   virtual PRBool IsContainingBlock() const;
 #ifdef NS_DEBUG
   NS_IMETHOD  List(FILE* out, PRInt32 aIndent) const;
   NS_IMETHOD  GetFrameName(nsAString& aResult) const;
   NS_IMETHOD_(nsFrameState) GetDebugStateBits() const;
   NS_IMETHOD  DumpRegressionData(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent);
 #endif
 
-  NS_IMETHOD  SetSelected(nsPresContext* aPresContext, nsIDOMRange *aRange,PRBool aSelected, nsSpread aSpread, SelectionType aType);
   NS_IMETHOD  GetSelected(PRBool *aSelected) const;
   NS_IMETHOD  IsSelectable(PRBool* aIsSelectable, PRUint8* aSelectStyle) const;
 
   NS_IMETHOD  GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon);
 
   virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset);
   virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset);
   virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect,
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1882,29 +1882,30 @@ public:
    * Determine whether borders should not be painted on certain sides of the
    * frame.
    */
   virtual PRIntn GetSkipSides() const { return 0; }
 
   /** Selection related calls
    */
   /** 
-   *  Called to set the selection of the frame based on frame offsets.  you can FORCE the frame
-   *  to redraw event if aSelected == the frame selection with the last parameter.
-   *  data in struct may be changed when passed in.
-   *  @param aRange is the range that will dictate if the frames need to be redrawn null means the whole content needs to be redrawn
+   *  Called to set the selection status of the frame.
+   *  
+   *  This must be called on the primary frame, but all continuations
+   *  will be affected the same way.
+   *
+   *  This sets or clears NS_FRAME_SELECTED_CONTENT for each frame in the
+   *  continuation chain, if the frames are currently selectable.
+   *  The frames are unconditionally invalidated, if this selection type
+   *  is supported at all.
    *  @param aSelected is it selected?
-   *  @param aSpread should it spread the selection to flow elements around it? or go down to its children?
    *  @param aType the selection type of the selection that you are setting on the frame
    */
-  NS_IMETHOD  SetSelected(nsPresContext* aPresContext,
-                          nsIDOMRange*    aRange,
-                          PRBool          aSelected,
-                          nsSpread        aSpread,
-                          SelectionType   aType) = 0;
+  virtual void SetSelected(PRBool        aSelected,
+                           SelectionType aType);
 
   NS_IMETHOD  GetSelected(PRBool *aSelected) const = 0;
 
   /**
    *  called to discover where this frame, or a parent frame has user-select style
    *  applied, which affects that way that it is selected.
    *    
    *  @param aIsSelectable out param. Set to true if the frame can be selected
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -74,16 +74,17 @@
 #include "nsFrameTraversal.h"
 #include "nsILineIterator.h"
 #include "nsGkAtoms.h"
 #include "nsIFrameTraversal.h"
 #include "nsLayoutUtils.h"
 #include "nsLayoutCID.h"
 #include "nsBidiPresUtils.h"
 static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
+#include "nsTextFrame.h"
 
 #include "nsIDOMText.h"
 
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
 
 //included for desired x position;
 #include "nsPresContext.h"
@@ -296,17 +297,19 @@ private:
   private:
     nsTypedSelection *mTypedSelection;
     SelectionRegion   mRegion;
   };
 
   void setAnchorFocusRange(PRInt32 aIndex); // pass in index into mRanges;
                                             // negative value clears
                                             // mAnchorFocusRange
-  nsresult     selectFrames(nsPresContext* aPresContext, nsIContentIterator *aInnerIter, nsIContent *aContent, nsIPresShell *aPresShell, PRBool aFlags);
+  nsresult     SelectAllFramesForContent(nsIContentIterator *aInnerIter,
+                               nsIContent *aContent,
+                               PRBool aSelected);
   nsresult     selectFrames(nsPresContext* aPresContext, nsIRange *aRange, PRBool aSelect);
   nsresult     getTableCellLocationFromRange(nsIRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol);
   nsresult     addTableCellRange(nsIRange *aRange, PRBool *aDidAddRange, PRInt32 *aOutIndex);
 
   PRInt32 FindInsertionPoint(
       nsTArray<RangeData>* aElementArray,
       nsINode* aPointNode, PRInt32 aPointOffset,
       PRInt32 (*aComparator)(nsINode*,PRInt32,nsIRange*));
@@ -4172,41 +4175,36 @@ nsTypedSelection::GetPrimaryFrameForFocu
     GetFrameForNodeOffset(content, GetFocusOffset(),
                           hint, aOffsetUsed);
   if (!*aReturnFrame)
     return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
 
-
-
 //select all content children of aContent
 nsresult
-nsTypedSelection::selectFrames(nsPresContext* aPresContext,
-                               nsIContentIterator *aInnerIter,
-                               nsIContent *aContent,
-                               nsIPresShell *aPresShell,
-                               PRBool aFlags)
+nsTypedSelection::SelectAllFramesForContent(nsIContentIterator *aInnerIter,
+                                  nsIContent *aContent,
+                                  PRBool aSelected)
 {
   if (!mFrameSelection)
     return NS_OK;//nothing to do
   nsresult result;
   if (!aInnerIter)
     return NS_ERROR_NULL_POINTER;
   result = aInnerIter->Init(aContent);
   nsIFrame *frame;
   if (NS_SUCCEEDED(result))
   {
     // First select frame of content passed in
     frame = mFrameSelection->GetShell()->GetPrimaryFrameFor(aContent);
     if (frame)
     {
-      //NOTE: eSpreadDown is now IGNORED. Selected state is set only for given frame
-      frame->SetSelected(aPresContext, nsnull, aFlags, eSpreadDown, mType);
+      frame->SetSelected(aSelected, mType);
       if (mFrameSelection->GetTableCellSelection())
       {
         nsITableCellLayout *tcl = do_QueryFrame(frame);
         if (tcl)
         {
           return NS_OK;
         }
       }
@@ -4215,39 +4213,17 @@ nsTypedSelection::selectFrames(nsPresCon
     while (!aInnerIter->IsDone())
     {
       nsCOMPtr<nsIContent> innercontent =
         do_QueryInterface(aInnerIter->GetCurrentNode());
 
       frame = mFrameSelection->GetShell()->GetPrimaryFrameFor(innercontent);
       if (frame)
       {
-        //NOTE: eSpreadDown is now IGNORED. Selected state is set only
-        //for given frame
-
-        //spread from here to hit all frames in flow
-        frame->SetSelected(aPresContext, nsnull, aFlags, eSpreadDown, mType);
-        nsRect frameRect = frame->GetRect();
-
-        //if a rect is 0 height/width then try to notify next
-        //available in flow of selection status.
-        while (!frameRect.width || !frameRect.height)
-        {
-          //try to notify next in flow that its content is selected.
-          frame = frame->GetNextInFlow();
-          if (frame)
-          {
-            frameRect = frame->GetRect();
-            frame->SetSelected(aPresContext, nsnull, aFlags, eSpreadDown, mType);
-          }
-          else
-            break;
-        }
-        //if the frame is splittable and this frame is 0,0 then set
-        //the next in flow frame to be selected also
+        frame->SetSelected(aSelected, mType);
       }
 
       aInnerIter->Next();
     }
 
     return NS_OK;
   }
 
@@ -4287,46 +4263,63 @@ nsTypedSelection::selectFrames(nsPresCon
     // for each text node, call SetSelected on it:
     nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
 
     // we must call first one explicitly
     if (!content)
       return NS_ERROR_UNEXPECTED;
 
     nsIFrame *frame;
-    if (!content->IsNodeOfType(nsINode::eELEMENT))
+    if (content->IsNodeOfType(nsINode::eTEXT))
     {
       frame = mFrameSelection->GetShell()->GetPrimaryFrameFor(content);
-      if (frame)
-        frame->SetSelected(aPresContext, domRange, aFlags, eSpreadDown, mType);//spread from here to hit all frames in flow
+      // The frame could be an SVG text frame, in which case we'll ignore
+      // it.
+      if (frame && frame->GetType() == nsGkAtoms::textFrame)
+      {
+        nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
+        PRUint32 startOffset = aRange->StartOffset();
+        PRUint32 endOffset;
+        if (aRange->GetEndParent() == content) {
+          endOffset = aRange->EndOffset();
+        } else {
+          endOffset = content->GetText()->GetLength();
+        }
+        textFrame->SetSelectedRange(startOffset, endOffset, aFlags, mType);
+      }
     }
 
     iter->First();
 
     while (!iter->IsDone())
     {
       content = do_QueryInterface(iter->GetCurrentNode());
 
-      selectFrames(aPresContext, inneriter, content, presShell,aFlags);
+      SelectAllFramesForContent(inneriter, content, aFlags);
 
       iter->Next();
     }
 
     //we must now do the last one  if it is not the same as the first
     if (aRange->GetEndParent() != aRange->GetStartParent())
     {
       content = do_QueryInterface(aRange->GetEndParent(), &result);
       if (NS_FAILED(result) || !content)
         return result;
 
-      if (!content->IsNodeOfType(nsINode::eELEMENT))
+      if (content->IsNodeOfType(nsINode::eTEXT))
       {
         frame = mFrameSelection->GetShell()->GetPrimaryFrameFor(content);
-        if (frame)
-           frame->SetSelected(aPresContext, domRange, aFlags, eSpreadDown, mType);//spread from here to hit all frames in flow
+        // The frame could be an SVG text frame, in which case we'll
+        // ignore it.
+        if (frame && frame->GetType() == nsGkAtoms::textFrame)
+        {
+          nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
+          textFrame->SetSelectedRange(0, aRange->EndOffset(), aFlags, mType);
+        }
       }
     }
   }
   return result;
 }
 
 // nsTypedSelection::LookUpSelection
 //
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -150,22 +150,32 @@ public:
   NS_IMETHOD List(FILE* out, PRInt32 aIndent) const;
   NS_IMETHOD GetFrameName(nsAString& aResult) const;
   NS_IMETHOD_(nsFrameState) GetDebugStateBits() const ;
 #endif
   
   virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint);
   ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint &aPoint);
 
-  NS_IMETHOD SetSelected(nsPresContext* aPresContext,
-                         nsIDOMRange *aRange,
-                         PRBool aSelected,
-                         nsSpread aSpread,
-                         SelectionType aType);
-  
+  /**
+   * This is called only on the primary text frame. It indicates that
+   * the selection state of the given character range has changed.
+   * Text in the range is unconditionally invalidated
+   * (nsTypedSelection::Repaint depends on this).
+   * @param aSelected true if the selection has been added to the range,
+   * false otherwise
+   * @param aType the type of selection added or removed
+   */
+  virtual void SetSelected(PRBool        aSelected,
+                           SelectionType aType);
+  void SetSelectedRange(PRUint32 aStart,
+                        PRUint32 aEnd,
+                        PRBool aSelected,
+                        SelectionType aType);
+
   virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset);
   virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset);
   virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect,
                                 PRInt32* aOffset, PeekWordState* aState);
 
   NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval);
   
   // Update offsets to account for new length. This may clear mTextRun.
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -4964,130 +4964,108 @@ nsTextFrame::CombineSelectionUnderlineRe
                                             style, descentLimit);
     aRect.UnionRect(aRect, decorationArea);
   }
   DestroySelectionDetails(details);
 
   return !aRect.IsEmpty() && !givenRect.Contains(aRect);
 }
 
-//null range means the whole thing
-NS_IMETHODIMP
-nsTextFrame::SetSelected(nsPresContext* aPresContext,
-                         nsIDOMRange *aRange,
-                         PRBool aSelected,
-                         nsSpread aSpread,
+void
+nsTextFrame::SetSelected(PRBool        aSelected,
                          SelectionType aType)
 {
+  SetSelectedRange(0, mContent->GetText()->GetLength(), aSelected, aType);
+}
+
+void
+nsTextFrame::SetSelectedRange(PRUint32 aStart,
+                              PRUint32 aEnd,
+                              PRBool aSelected,
+                              SelectionType aType)
+{
+  NS_ASSERTION(!GetPrevContinuation(), "Should only be called for primary frame");
   DEBUG_VERIFY_NOT_DIRTY(mState);
-#if 0 //XXXrbs disable due to bug 310318
-  if (mState & NS_FRAME_IS_DIRTY)
-    return NS_ERROR_UNEXPECTED;
-#endif
-
+
+  // Selection is collapsed, which can't affect text frame rendering
+  if (aStart == aEnd)
+    return;
+
+  // XXXroc This is stupid, ParentDisablesSelection just returns true. Let's
+  // kill it.
   if (aSelected && ParentDisablesSelection())
-    return NS_OK;
+    return;
 
   if (aType == nsISelectionController::SELECTION_NORMAL) {
     // check whether style allows selection
     PRBool selectable;
     IsSelectable(&selectable, nsnull);
     if (!selectable)
-      return NS_OK;//do not continue no selection for this frame.
-  }
-
-  PRBool found = PR_FALSE;
-  if (aRange) {
-    //lets see if the range contains us, if so we must redraw!
-    nsCOMPtr<nsIDOMNode> endNode;
-    PRInt32 endOffset;
-    nsCOMPtr<nsIDOMNode> startNode;
-    PRInt32 startOffset;
-    aRange->GetEndContainer(getter_AddRefs(endNode));
-    aRange->GetEndOffset(&endOffset);
-    aRange->GetStartContainer(getter_AddRefs(startNode));
-    aRange->GetStartOffset(&startOffset);
-    nsCOMPtr<nsIDOMNode> thisNode = do_QueryInterface(GetContent());
-
-    if (thisNode == startNode)
-    {
-      if (GetContentEnd() >= startOffset)
-      {
-        found = PR_TRUE;
-        if (thisNode == endNode)
-        { //special case
-          if (endOffset == startOffset) //no need to redraw since drawing takes place with cursor
-            found = PR_FALSE;
-
-          if (mContentOffset > endOffset)
-            found = PR_FALSE;
-        }
-      }
+      return;
+  }
+
+  PRBool anySelected = PR_FALSE;
+
+  nsTextFrame* f = this;
+  while (f && f->GetContentEnd() <= aStart) {
+    if (f->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
+      anySelected = PR_TRUE;
     }
-    else if (thisNode == endNode)
-    {
-      if (mContentOffset < endOffset)
-        found = PR_TRUE;
-      else
-      {
-        found = PR_FALSE;
+    f = static_cast<nsTextFrame*>(f->GetNextContinuation());
+  }
+
+  nsPresContext* presContext = PresContext();
+  while (f && f->GetContentOffset() < aEnd) {
+    if (aSelected) {
+      f->AddStateBits(NS_FRAME_SELECTED_CONTENT);
+      anySelected = PR_TRUE;
+    } else { // we need to see if any other selection is available.
+      SelectionDetails *details = f->GetSelectionDetails();
+      if (details) {
+        anySelected = PR_TRUE;
+        DestroySelectionDetails(details);
+      } else {
+        f->RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
       }
     }
-    else
-    {
-      found = PR_TRUE;
-    }
-  }
-  else {
-    // null range means the whole thing
-    found = PR_TRUE;
-  }
-
-  if ( aSelected )
-    AddStateBits(NS_FRAME_SELECTED_CONTENT);
-  else
-  { //we need to see if any other selection is available.
-    SelectionDetails *details = GetSelectionDetails();
-    if (!details) {
-      RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
-    } else {
-      DestroySelectionDetails(details);
-    }
-  }
-  if (found) {
-    // If the selection state is changed in this content, we need to reflow
-    // to recompute the overflow area for underline of spellchecking or IME if
-    // their underline is thicker than normal decoration line.
+
+    // We may need to reflow to recompute the overflow area for
+    // spellchecking or IME underline if their underline is thicker than
+    // the normal decoration line.
     PRBool didHaveOverflowingSelection =
-      (mState & TEXT_SELECTION_UNDERLINE_OVERFLOWED) != 0;
+      (f->GetStateBits() & TEXT_SELECTION_UNDERLINE_OVERFLOWED) != 0;
     nsRect r(nsPoint(0, 0), GetSize());
     PRBool willHaveOverflowingSelection =
-      aSelected && CombineSelectionUnderlineRect(PresContext(), r);
+      aSelected && f->CombineSelectionUnderlineRect(presContext, r);
     if (didHaveOverflowingSelection || willHaveOverflowingSelection) {
-      PresContext()->PresShell()->FrameNeedsReflow(this,
-                                                   nsIPresShell::eStyleChange,
-                                                   NS_FRAME_IS_DIRTY);
+      presContext->PresShell()->FrameNeedsReflow(f,
+                                                 nsIPresShell::eStyleChange,
+                                                 NS_FRAME_IS_DIRTY);
     }
     // Selection might change anything. Invalidate the overflow area.
-    InvalidateOverflowRect();
-  }
-  if (aSpread == eSpreadDown)
-  {
-    nsIFrame* frame = GetPrevContinuation();
-    while(frame){
-      frame->SetSelected(aPresContext, aRange,aSelected,eSpreadNone, aType);
-      frame = frame->GetPrevContinuation();
+    f->InvalidateOverflowRect();
+
+    f = static_cast<nsTextFrame*>(f->GetNextContinuation());
+  }
+
+  // Scan remaining continuations to see if any are selected
+  while (f && !anySelected) {
+    if (f->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
+      anySelected = PR_TRUE;
     }
-    frame = GetNextContinuation();
-    while (frame){
-      frame->SetSelected(aPresContext, aRange,aSelected,eSpreadNone, aType);
-      frame = frame->GetNextContinuation();
-    }
-  }
-  return NS_OK;
+    f = static_cast<nsTextFrame*>(f->GetNextContinuation());
+  }
+
+  if (anySelected) {
+    mContent->SetFlags(NS_TEXT_IN_SELECTION);
+  } else {
+    // This is only legal because there is only one presentation for the
+    // content with a selection
+    mContent->UnsetFlags(NS_TEXT_IN_SELECTION);
+  }
 }
 
 NS_IMETHODIMP
 nsTextFrame::GetPointFromOffset(PRInt32 inOffset,
                                 nsPoint* outPoint)
 {
   if (!outPoint)
     return NS_ERROR_NULL_POINTER;
@@ -6416,17 +6394,28 @@ nsTextFrame::Reflow(nsPresContext*      
     NS_ASSERTION(numJustifiableCharacters <= charsFit,
                  "Bad justifiable character count");
     lineLayout.SetTextJustificationWeights(numJustifiableCharacters,
         charsFit - numJustifiableCharacters);
   }
 
   SetLength(contentLength);
 
-  Invalidate(nsRect(nsPoint(0, 0), GetSize()));
+  if (mContent->HasFlag(NS_TEXT_IN_SELECTION)) {
+    // XXXroc Watch out, this could be slow!!! Speed up GetSelectionDetails?
+    SelectionDetails* details = GetSelectionDetails();
+    if (details) {
+      AddStateBits(NS_FRAME_SELECTED_CONTENT);
+      DestroySelectionDetails(details);
+    } else {
+      RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
+    }
+  }
+
+  Invalidate(aMetrics.mOverflowArea);
 
 #ifdef NOISY_REFLOW
   ListTag(stdout);
   printf(": desiredSize=%d,%d(b=%d) status=%x\n",
          aMetrics.width, aMetrics.height, aMetrics.ascent,
          aStatus);
 #endif
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -224,45 +224,40 @@ nsSVGGlyphFrame::DidSetStyleContext(nsSt
   nsSVGGlyphFrameBase::DidSetStyleContext(aOldStyleContext);
 
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     ClearTextRun();
     NotifyGlyphMetricsChange();
   }
 }
 
-NS_IMETHODIMP
-nsSVGGlyphFrame::SetSelected(nsPresContext* aPresContext,
-                             nsIDOMRange*    aRange,
-                             PRBool          aSelected,
-                             nsSpread        aSpread,
-                             SelectionType   aType)
+void
+nsSVGGlyphFrame::SetSelected(PRBool        aSelected,
+                             SelectionType aType)
 {
 #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
   printf("nsSVGGlyphFrame(%p)::SetSelected()\n", this);
 #endif
-//  return nsSVGGlyphFrameBase::SetSelected(aPresContext, aRange, aSelected, aSpread, aType);
+
+  if (aType != nsISelectionController::SELECTION_NORMAL)
+    return;
 
-  if (aType == nsISelectionController::SELECTION_NORMAL) {
-    // check whether style allows selection
-    PRBool  selectable;
-    IsSelectable(&selectable, nsnull);
-    if (!selectable)
-      return NS_OK;
+  // check whether style allows selection
+  PRBool selectable;
+  IsSelectable(&selectable, nsnull);
+  if (!selectable)
+    return;
+
+  if (aSelected) {
+    AddStateBits(NS_FRAME_SELECTED_CONTENT);
+  } else {
+    RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
   }
 
-  if ( aSelected ){
-    mState |=  NS_FRAME_SELECTED_CONTENT;
-  }
-  else
-    mState &= ~NS_FRAME_SELECTED_CONTENT;
-
   nsSVGUtils::UpdateGraphic(this);
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSVGGlyphFrame::GetSelected(PRBool *aSelected) const
 {
   nsresult rv = nsSVGGlyphFrameBase::GetSelected(aSelected);
 #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
   printf("nsSVGGlyphFrame(%p)::GetSelected()=%d\n", this, *aSelected);
--- a/layout/svg/base/src/nsSVGGlyphFrame.h
+++ b/layout/svg/base/src/nsSVGGlyphFrame.h
@@ -77,21 +77,18 @@ public:
 
   // nsIFrame interface:
   NS_IMETHOD  CharacterDataChanged(nsPresContext*  aPresContext,
                                    nsIContent*     aChild,
                                    PRBool          aAppend);
 
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext);
 
-  NS_IMETHOD  SetSelected(nsPresContext* aPresContext,
-                          nsIDOMRange*    aRange,
-                          PRBool          aSelected,
-                          nsSpread        aSpread,
-                          SelectionType   aType);
+  virtual void SetSelected(PRBool        aSelected,
+                           SelectionType aType);
   NS_IMETHOD  GetSelected(PRBool *aSelected) const;
   NS_IMETHOD  IsSelectable(PRBool* aIsSelectable, PRUint8* aSelectStyle) const;
 
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 
   /**
--- a/layout/tables/nsTableOuterFrame.cpp
+++ b/layout/tables/nsTableOuterFrame.cpp
@@ -388,26 +388,24 @@ nsTableOuterFrame::BuildDisplayListForIn
   while (kid) {
     nsresult rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
     NS_ENSURE_SUCCESS(rv, rv);
     kid = kid->GetNextSibling();
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP nsTableOuterFrame::SetSelected(nsPresContext* aPresContext,
-                                             nsIDOMRange *aRange,
-                                             PRBool aSelected,
-                                             nsSpread aSpread,
-                                             SelectionType aType)
+void
+nsTableOuterFrame::SetSelected(PRBool        aSelected,
+                               SelectionType aType)
 {
-  nsresult result = nsFrame::SetSelected(aPresContext, aRange,aSelected, aSpread, aType);
-  if (NS_SUCCEEDED(result) && mInnerTableFrame)
-    return mInnerTableFrame->SetSelected(aPresContext, aRange,aSelected, aSpread, aType);
-  return result;
+  nsFrame::SetSelected(aSelected, aType);
+  if (mInnerTableFrame) {
+    mInnerTableFrame->SetSelected(aSelected, aType);
+  }
 }
 
 NS_IMETHODIMP 
 nsTableOuterFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
                                               nsIFrame**      aProviderFrame,
                                               PRBool*         aIsChild)
 {
   // The table outer frame and the (inner) table frame split the style
--- a/layout/tables/nsTableOuterFrame.h
+++ b/layout/tables/nsTableOuterFrame.h
@@ -160,21 +160,18 @@ public:
   virtual nsIAtom* GetType() const;
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const;
 #endif
 
   /** SetSelected needs to be overridden to talk to inner tableframe
    */
-  NS_IMETHOD SetSelected(nsPresContext* aPresContext,
-                         nsIDOMRange *aRange,
-                         PRBool aSelected,
-                         nsSpread aSpread,
-                         SelectionType aType);
+  void SetSelected(PRBool aSelected,
+                   SelectionType aType);
 
   NS_IMETHOD GetParentStyleContextFrame(nsPresContext* aPresContext,
                                         nsIFrame**      aProviderFrame,
                                         PRBool*         aIsChild);
 
   /*---------------- nsITableLayout methods ------------------------*/
 
   /** @see nsITableFrame::GetCellDataAt */