Bug 368760, r=mats, sr=roc, a=blocking1.9+
authorOlli.Pettay@helsinki.fi
Thu, 22 Nov 2007 12:47:33 -0800
changeset 8295 302f9953ee6d34c1f0eb79df476b5686003253fa
parent 8294 6692f7acce55219019224813f604d072b2cf6011
child 8296 af1ffbeb95fdbbb51facbd01ca5a2e9e75ffdcc8
push idunknown
push userunknown
push dateunknown
reviewersmats, roc, blocking1.9
bugs368760
milestone1.9b2pre
Bug 368760, r=mats, sr=roc, a=blocking1.9+
content/events/src/nsEventStateManager.cpp
layout/base/nsCaret.cpp
layout/base/nsCaret.h
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/forms/nsTextControlFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsFrameSelection.h
layout/generic/nsIFrame.h
layout/generic/nsSelection.cpp
layout/generic/nsTextFrameThebes.cpp
layout/mathml/base/src/nsMathMLmoFrame.cpp
layout/tables/nsTableCellFrame.cpp
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1204,17 +1204,18 @@ nsEventStateManager::PreHandleEvent(nsPr
             if (focusedElement) {
               nsCOMPtr<nsIContent> focusContent = do_QueryInterface(focusedElement);
               focusContent->SetFocus(context);
             } else {
               nsIMEStateManager::OnChangeFocus(context, nsnull);
             }
 
             // disable selection mousedown state on activation
-            shell->FrameSelection()->SetMouseDownState(PR_FALSE);
+            nsCOMPtr<nsFrameSelection> frameSelection = shell->FrameSelection();
+            frameSelection->SetMouseDownState(PR_FALSE);
           }
         }
       }
 
       if (focusController) {
         // Make sure the focus controller is up-to-date, since restoring
         // focus memory may have caused focus to go elsewhere.
 
@@ -1708,17 +1709,18 @@ nsEventStateManager::FireContextClick()
                            targetWidget,
                            nsMouseEvent::eReal);
         event.clickCount = 1;
         FillInEventFromGestureDown(&event);
         
         // stop selection tracking, we're in control now
         if (mCurrentTarget)
         {
-          nsFrameSelection* frameSel = mCurrentTarget->GetFrameSelection();
+          nsCOMPtr<nsFrameSelection> frameSel =
+            mCurrentTarget->GetFrameSelection();
         
           if (frameSel && frameSel->GetMouseDownState()) {
             // note that this can cause selection changed events to fire if we're in
             // a text field, which will null out mCurrentTarget
             frameSel->SetMouseDownState(PR_FALSE);
           }
         }
 
@@ -1844,17 +1846,17 @@ nsEventStateManager::GenerateDragGesture
       StopTrackingDragGesture();
       return;
     }
 
     // Check if selection is tracking drag gestures, if so
     // don't interfere!
     if (mCurrentTarget)
     {
-      nsFrameSelection* frameSel = mCurrentTarget->GetFrameSelection();
+      nsCOMPtr<nsFrameSelection> frameSel = mCurrentTarget->GetFrameSelection();
       if (frameSel && frameSel->GetMouseDownState()) {
         StopTrackingDragGesture();
         return;
       }
     }
 
     static PRInt32 pixelThresholdX = 0;
     static PRInt32 pixelThresholdY = 0;
@@ -2381,17 +2383,18 @@ nsEventStateManager::PostHandleEvent(nsP
         if (viewMan) {
           nsIView* grabbingView = nsnull;
           viewMan->GetMouseEventGrabber(grabbingView);
           if (grabbingView == aView) {
             PRBool result;
             viewMan->GrabMouseEvents(nsnull, result);
           }
         }
-        shell->FrameSelection()->SetMouseDownState(PR_FALSE);
+        nsCOMPtr<nsFrameSelection> frameSelection = shell->FrameSelection();
+        frameSelection->SetMouseDownState(PR_FALSE);
       }
     }
     break;
   case NS_MOUSE_SCROLL:
     if (nsEventStatus_eConsumeNoDefault != *aStatus) {
 
       // Build the preference keys, based on the event properties.
       nsMouseScrollEvent *msEvent = (nsMouseScrollEvent*) aEvent;
@@ -4940,17 +4943,17 @@ nsEventStateManager::GetDocSelectionLoca
 
   NS_ASSERTION(mPresContext, "mPresContent is null!!");
   EnsureDocument(mPresContext);
   if (!mDocument)
     return rv;
   nsIPresShell *shell;
   shell = mPresContext->GetPresShell();
 
-  nsFrameSelection *frameSelection = nsnull;
+  nsCOMPtr<nsFrameSelection> frameSelection;
   if (shell)
     frameSelection = shell->FrameSelection();
 
   nsCOMPtr<nsISelection> domSelection;
   if (frameSelection) {
     domSelection = frameSelection->
       GetSelection(nsISelectionController::SELECTION_NORMAL);
   }
@@ -5291,17 +5294,18 @@ nsEventStateManager::MoveCaretToFocus()
                             &selectionFrame, &selectionOffset);
 
     nsIPresShell *shell = mPresContext->GetPresShell();
     if (shell) {
       // rangeDoc is a document interface we can create a range with
       nsCOMPtr<nsIDOMDocumentRange> rangeDoc(do_QueryInterface(mDocument));
 
       if (rangeDoc) {
-        nsCOMPtr<nsISelection> domSelection = shell->FrameSelection()->
+        nsCOMPtr<nsFrameSelection> frameSelection = shell->FrameSelection();
+        nsCOMPtr<nsISelection> domSelection = frameSelection->
           GetSelection(nsISelectionController::SELECTION_NORMAL);
         if (domSelection) {
           nsCOMPtr<nsIDOMNode> currentFocusNode(do_QueryInterface(mCurrentFocus));
           // First clear the selection
           domSelection->RemoveAllRanges();
           if (currentFocusNode) {
             nsCOMPtr<nsIDOMRange> newRange;
             nsresult rv = rangeDoc->CreateRange(getter_AddRefs(newRange));
@@ -5350,25 +5354,25 @@ nsresult
 nsEventStateManager::SetContentCaretVisible(nsIPresShell* aPresShell,
                                             nsIContent *aFocusedContent,
                                             PRBool aVisible)
 {
   // When browsing with caret, make sure caret is visible after new focus
   nsCOMPtr<nsICaret> caret;
   aPresShell->GetCaret(getter_AddRefs(caret));
 
-  nsFrameSelection* frameSelection = nsnull;
+  nsCOMPtr<nsFrameSelection> frameSelection;
   if (aFocusedContent) {
     nsIFrame *focusFrame = aPresShell->GetPrimaryFrameFor(aFocusedContent);
     
     if (focusFrame)
       frameSelection = focusFrame->GetFrameSelection();
   }
 
-  nsFrameSelection *docFrameSelection = aPresShell->FrameSelection();
+  nsCOMPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
 
   if (docFrameSelection && caret &&
      (frameSelection == docFrameSelection || !aFocusedContent)) {
     nsISelection* domSelection = docFrameSelection->
       GetSelection(nsISelectionController::SELECTION_NORMAL);
     if (domSelection) {
       // First, tell the caret which selection to use
       caret->SetCaretDOMSelection(domSelection);
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -294,17 +294,17 @@ NS_IMETHODIMP nsCaret::GetCaretCoordinat
   nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode);
   if (!contentNode)
     return NS_ERROR_FAILURE;
 
   // find the frame that contains the content node that has focus
   nsIFrame*       theFrame = nsnull;
   PRInt32         theFrameOffset = 0;
 
-  nsFrameSelection* frameSelection = GetFrameSelection();
+  nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
   if (!frameSelection)
     return NS_ERROR_FAILURE;
   PRUint8 bidiLevel = frameSelection->GetCaretBidiLevel();
   
   err = GetCaretFrameForNodeOffset(contentNode, focusOffset,
                                    frameSelection->GetHint(), bidiLevel,
                                    &theFrame, &theFrameOffset);
   if (NS_FAILED(err) || !theFrame)
@@ -374,17 +374,17 @@ NS_IMETHODIMP nsCaret::SetVisibilityDuri
   return NS_OK;
 }
 
 NS_IMETHODIMP nsCaret::DrawAtPosition(nsIDOMNode* aNode, PRInt32 aOffset)
 {
   NS_ENSURE_ARG(aNode);
 
   PRUint8 bidiLevel;
-  nsFrameSelection* frameSelection = GetFrameSelection();
+  nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
   if (!frameSelection)
     return NS_ERROR_FAILURE;
   bidiLevel = frameSelection->GetCaretBidiLevel();
 
   // DrawAtPosition is used by consumers who want us to stay drawn where they
   // tell us. Setting mBlinkRate to 0 tells us to not set a timer to erase
   // ourselves, our consumer will take care of that.
   mBlinkRate = 0;
@@ -586,17 +586,17 @@ nsCaret::DrawAtPositionWithHint(nsIDOMNo
     // save stuff so we can figure out what frame we're in later.
     mLastContent = contentNode;
     mLastContentOffset = aOffset;
     mLastHint = aFrameHint;
     mLastBidiLevel = aBidiLevel;
 
     // If there has been a reflow, set the caret Bidi level to the level of the current frame
     if (aBidiLevel & BIDI_LEVEL_UNDEFINED) {
-      nsFrameSelection* frameSelection = GetFrameSelection();
+      nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
       if (!frameSelection)
         return PR_FALSE;
       frameSelection->SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame));
     }
 
     // Only update the caret's rect when we're not currently drawn.
     rv = UpdateCaretRects(theFrame, theFrameOffset);
     if (NS_FAILED(rv))
@@ -682,17 +682,17 @@ nsCaret::GetCaretFrameForNodeOffset(nsIC
                                     PRInt32*                aReturnOffset)
 {
 
   //get frame selection and find out what frame to use...
   nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
   if (!presShell)
     return NS_ERROR_FAILURE;
 
-  nsFrameSelection* frameSelection = GetFrameSelection();
+  nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
   if (!frameSelection)
     return NS_ERROR_FAILURE;
 
   nsIFrame* theFrame = nsnull;
   PRInt32   theFrameOffset = 0;
 
   theFrame = frameSelection->GetFrameForNodeOffset(aContentNode, aOffset,
                                                    aFrameHint, &theFrameOffset);
@@ -1004,17 +1004,17 @@ void nsCaret::DrawCaret(PRBool aInvalida
     // get the node and offset, which is where we want the caret to draw
     domSelection->GetFocusNode(getter_AddRefs(node));
     if (!node)
       return;
     
     if (NS_FAILED(domSelection->GetFocusOffset(&offset)))
       return;
 
-    nsFrameSelection* frameSelection = GetFrameSelection();
+    nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
     if (!frameSelection)
       return;
     bidiLevel = frameSelection->GetCaretBidiLevel();
   }
   else
   {
     if (!mLastContent)
     {
@@ -1198,22 +1198,24 @@ void nsCaret::CaretBlinkCallback(nsITime
   nsCaret   *theCaret = reinterpret_cast<nsCaret*>(aClosure);
   if (!theCaret) return;
   
   theCaret->DrawCaret(PR_TRUE);
 }
 
 
 //-----------------------------------------------------------------------------
-nsFrameSelection* nsCaret::GetFrameSelection() {
+already_AddRefed<nsFrameSelection>
+nsCaret::GetFrameSelection()
+{
   nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryReferent(mDomSelectionWeak));
   if (!privateSelection)
     return nsnull;
-  nsCOMPtr<nsFrameSelection> frameSelection;
-  privateSelection->GetFrameSelection(getter_AddRefs(frameSelection));
+  nsFrameSelection* frameSelection = nsnull;
+  privateSelection->GetFrameSelection(&frameSelection);
   return frameSelection;
 }
 
 void
 nsCaret::SetIgnoreUserModify(PRBool aIgnoreUserModify)
 {
   mIgnoreUserModify = aIgnoreUserModify;
 }
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -150,17 +150,17 @@ class nsCaret : public nsICaret,
 #ifdef IBMBIDI
       return mHookRect;
 #else
       return nsRect();
 #endif
     }
     void          ToggleDrawnStatus() { mDrawn = !mDrawn; }
 
-    nsFrameSelection* GetFrameSelection();
+    already_AddRefed<nsFrameSelection> GetFrameSelection();
 
 protected:
 
     nsWeakPtr             mPresShell;
     nsWeakPtr             mDomSelectionWeak;
 
     nsCOMPtr<nsITimer>              mBlinkTimer;
     nsCOMPtr<nsIRenderingContext>   mRendContext;
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -97,20 +97,20 @@ template<class E> class nsCOMArray;
 class nsWeakFrame;
 class nsIScrollableFrame;
 class gfxASurface;
 class gfxContext;
 
 typedef short SelectionType;
 typedef PRUint32 nsFrameState;
 
-// 4BE324F2-FB22-47CD-A653-19C70EE55E3F
+// ff2bdd39-75ed-4392-92b8-8b650c4db574
 #define NS_IPRESSHELL_IID \
-{ 0x4BE324F2, 0xFB22, 0x47CD, \
-  { 0xA6, 0x53, 0x19, 0xC7, 0x0E, 0xE5, 0x5E, 0x3F } }
+{ 0xff2bdd39, 0x75ed, 0x4392, \
+  { 0x92, 0xb8, 0x8b, 0x65, 0x0c, 0x4d, 0xb5, 0x74 } }
 
 // Constants for ScrollContentIntoView() function
 #define NS_PRESSHELL_SCROLL_TOP      0
 #define NS_PRESSHELL_SCROLL_BOTTOM   100
 #define NS_PRESSHELL_SCROLL_LEFT     0
 #define NS_PRESSHELL_SCROLL_RIGHT    100
 #define NS_PRESSHELL_SCROLL_CENTER   50
 #define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -247,17 +247,23 @@ public:
    */
   NS_IMETHOD SetPreferenceStyleRules(PRBool aForceReflow) = 0;
 
   /**
    * FrameSelection will return the Frame based selection API.
    * You cannot go back and forth anymore with QI between nsIDOM sel and
    * nsIFrame sel.
    */
-  nsFrameSelection* FrameSelection() { return mSelection; }
+  already_AddRefed<nsFrameSelection> FrameSelection();
+
+  /**
+   * ConstFrameSelection returns an object which methods are safe to use for
+   * example in nsIFrame code.
+   */
+  const nsFrameSelection* ConstFrameSelection() { return mSelection; }
 
   // Make shell be a document observer.  If called after Destroy() has
   // been called on the shell, this will be ignored.
   NS_IMETHOD BeginObservingDocument() = 0;
 
   // Make shell stop being a document observer
   NS_IMETHOD EndObservingDocument() = 0;
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1347,16 +1347,23 @@ nsIPresShell::RemoveWeakFrame(nsWeakFram
   while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
     nextWeak = nextWeak->GetPreviousWeakFrame();
   }
   if (nextWeak) {
     nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
   }
 }
 
+already_AddRefed<nsFrameSelection>
+nsIPresShell::FrameSelection()
+{
+  NS_IF_ADDREF(mSelection);
+  return mSelection;
+}
+
 //----------------------------------------------------------------------
 
 nsresult
 NS_NewPresShell(nsIPresShell** aInstancePtrResult)
 {
   NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
   if (nsnull == aInstancePtrResult) {
     return NS_ERROR_NULL_POINTER;
@@ -1407,17 +1414,20 @@ PresShell::~PresShell()
  
   delete mStyleSet;
   delete mFrameConstructor;
 
   mCurrentEventContent = nsnull;
 
   NS_IF_RELEASE(mPresContext);
   NS_IF_RELEASE(mDocument);
-  NS_IF_RELEASE(mSelection);
+  if (mSelection) {
+    mSelection->DisconnectFromPresShell();
+    NS_RELEASE(mSelection);
+  }
 }
 
 /**
  * Initialize the presentation shell. Create view manager and style
  * manager.
  */
 NS_IMETHODIMP
 PresShell::Init(nsIDocument* aDocument,
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -2711,19 +2711,20 @@ nsTextControlFrame::SetValue(const nsASt
       mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
       if (domSel)
       {
         selPriv = do_QueryInterface(domSel);
         if (selPriv)
           selPriv->StartBatchChanges();
       }
 
+      nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon;
       mSelCon->SelectAll();
       nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(editor);
-      if (!plaintextEditor) {
+      if (!plaintextEditor || !weakFrame.IsAlive()) {
         NS_WARNING("Somehow not a plaintext editor?");
         if (pushed) {
           JSContext* cx;
           stack->Pop(&cx);
           NS_ASSERTION(!cx, "Unexpected JSContext popped!");
         }
         return NS_ERROR_FAILURE;
       }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -811,17 +811,17 @@ nsFrame::DisplaySelectionOverlay(nsDispl
 
   PRInt16 displaySelection;
   nsresult rv = shell->GetSelectionFlags(&displaySelection);
   if (NS_FAILED(rv))
     return rv;
   if (!(displaySelection & aContentType))
     return NS_OK;
 
-  nsFrameSelection* frameSelection = GetFrameSelection();
+  const nsFrameSelection* frameSelection = GetConstFrameSelection();
   PRInt16 selectionValue = frameSelection->GetDisplaySelection();
 
   if (selectionValue <= nsISelectionController::SELECTION_HIDDEN)
     return NS_OK; // selection is hidden or off
 
   nsIContent *newContent = mContent->GetParent();
 
   //check to see if we are anonymous content
@@ -1511,17 +1511,17 @@ nsFrame::HandleEvent(nsPresContext* aPre
     } else if (aEvent->message == NS_MOUSE_BUTTON_UP) {
       HandleRelease(aPresContext, aEvent, aEventStatus);
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsFrame::GetDataForTableSelection(nsFrameSelection *aFrameSelection,
+nsFrame::GetDataForTableSelection(const nsFrameSelection *aFrameSelection,
                                   nsIPresShell *aPresShell, nsMouseEvent *aMouseEvent, 
                                   nsIContent **aParentContent, PRInt32 *aContentOffset, PRInt32 *aTarget)
 {
   if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
     return NS_ERROR_NULL_POINTER;
 
   *aParentContent = nsnull;
   *aContentOffset = 0;
@@ -1770,57 +1770,59 @@ nsFrame::HandlePress(nsPresContext* aPre
   // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic
   PRBool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT);
 
   if (!IsMouseCaptured(aPresContext))
     CaptureMouse(aPresContext, PR_TRUE);
 
   // XXX This is screwy; it really should use the selection frame, not the
   // event frame
-  nsFrameSelection* frameselection;
+  const nsFrameSelection* frameselection = nsnull;
   if (useFrameSelection)
-    frameselection = GetFrameSelection();
+    frameselection = GetConstFrameSelection();
   else
-    frameselection = shell->FrameSelection();
+    frameselection = shell->ConstFrameSelection();
 
   if (frameselection->GetDisplaySelection() == nsISelectionController::SELECTION_OFF)
     return NS_OK;//nothing to do we cannot affect selection from here
 
   nsMouseEvent *me = (nsMouseEvent *)aEvent;
 
 #ifdef XP_MACOSX
   if (me->isControl)
     return NS_OK;//short ciruit. hard coded for mac due to time restraints.
 #endif
-    
+  nsCOMPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
   if (me->clickCount >1 )
   {
-    frameselection->SetMouseDownState(PR_TRUE);
-    frameselection->SetMouseDoubleDown(PR_TRUE);
+    // These methods aren't const but can't actually delete anything,
+    // so no need for nsWeakFrame.
+    fc->SetMouseDownState(PR_TRUE);
+    fc->SetMouseDoubleDown(PR_TRUE);
     return HandleMultiplePress(aPresContext, aEvent, aEventStatus);
   }
 
   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
   ContentOffsets offsets = GetContentOffsetsFromPoint(pt);
 
   if (!offsets.content)
     return NS_ERROR_FAILURE;
 
   // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
   nsCOMPtr<nsIContent>parentContent;
   PRInt32  contentOffset;
   PRInt32 target;
   rv = GetDataForTableSelection(frameselection, shell, me, getter_AddRefs(parentContent), &contentOffset, &target);
   if (NS_SUCCEEDED(rv) && parentContent)
   {
-    frameselection->SetMouseDownState(PR_TRUE);
-    return frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
-  }
-
-  frameselection->SetDelayedCaretData(0);
+    fc->SetMouseDownState(PR_TRUE);
+    return fc->HandleTableSelection(parentContent, contentOffset, target, me);
+  }
+
+  fc->SetDelayedCaretData(0);
 
   // Check if any part of this frame is selected, and if the
   // user clicked inside the selected region. If so, we delay
   // starting a new selection since the user may be trying to
   // drag the selected region to some other app.
 
   SelectionDetails *details = 0;
   PRBool isSelected = ((GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT);
@@ -1847,55 +1849,57 @@ nsFrame::HandlePress(nsPresContext* aPre
         // the caret later on when the mouse is released. We ignore
         // the spellcheck selection.
         //
         if (curDetail->mType != nsISelectionController::SELECTION_SPELLCHECK &&
             curDetail->mStart <= offsets.StartOffset() &&
             offsets.EndOffset() <= curDetail->mEnd)
         {
           delete details;
-          frameselection->SetMouseDownState(PR_FALSE);
-          frameselection->SetDelayedCaretData(me);
+          fc->SetMouseDownState(PR_FALSE);
+          fc->SetDelayedCaretData(me);
           return NS_OK;
         }
 
         curDetail = curDetail->mNext;
       }
 
       delete details;
     }
   }
 
-  frameselection->SetMouseDownState(PR_TRUE);
+  fc->SetMouseDownState(PR_TRUE);
 
 #ifdef XP_MACOSX
   PRBool control = me->isMeta;
 #else
   PRBool control = me->isControl;
 #endif
 
-  rv = frameselection->HandleClick(offsets.content, offsets.StartOffset(),
-                                   offsets.EndOffset(), me->isShift, control,
-                                   offsets.associateWithNext);
+  // Do not touch any nsFrame members after this point without adding
+  // weakFrame checks.
+  rv = fc->HandleClick(offsets.content, offsets.StartOffset(),
+                       offsets.EndOffset(), me->isShift, control,
+                       offsets.associateWithNext);
 
   if (NS_FAILED(rv))
     return rv;
 
   if (offsets.offset != offsets.secondaryOffset)
-    frameselection->MaintainSelection();
+    fc->MaintainSelection();
 
   if (isEditor && !me->isShift &&
       (offsets.EndOffset() - offsets.StartOffset()) == 1)
   {
     // A single node is selected and we aren't extending an existing
     // selection, which means the user clicked directly on an object (either
     // -moz-user-select: all or a non-text node without children).
     // Therefore, disable selection extension during mouse moves.
     // XXX This is a bit hacky; shouldn't editor be able to deal with this?
-    frameselection->SetMouseDownState(PR_FALSE);
+    fc->SetMouseDownState(PR_FALSE);
   }
 
   return rv;
 }
 
 /**
   * Multiple Mouse Press -- line or paragraph selection -- for the frame.
   * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
@@ -1940,17 +1944,19 @@ nsFrame::HandleMultiplePress(nsPresConte
 
   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
   ContentOffsets offsets = GetContentOffsetsFromPoint(pt);
   if (!offsets.content) return NS_ERROR_FAILURE;
 
   nsIFrame* theFrame;
   PRInt32 offset;
   // Maybe make this a static helper?
-  theFrame = PresContext()->GetPresShell()->FrameSelection()->
+  const nsFrameSelection* frameSelection =
+    PresContext()->GetPresShell()->ConstFrameSelection();
+  theFrame = frameSelection->
     GetFrameForNodeOffset(offsets.content, offsets.offset,
                           nsFrameSelection::HINT(offsets.associateWithNext),
                           &offset);
   if (!theFrame)
     return NS_ERROR_FAILURE;
 
   nsFrame* frame = static_cast<nsFrame*>(theFrame);
 
@@ -3967,28 +3973,38 @@ nsFrame::GetSelectionController(nsPresCo
       return NS_OK;
     }
     frame = frame->GetParent();
   }
 
   return CallQueryInterface(aPresContext->GetPresShell(), aSelCon);
 }
 
-nsFrameSelection* nsIFrame::GetFrameSelection()
+already_AddRefed<nsFrameSelection>
+nsIFrame::GetFrameSelection()
+{
+  nsFrameSelection* fs =
+    const_cast<nsFrameSelection*>(GetConstFrameSelection());
+  NS_IF_ADDREF(fs);
+  return fs;
+}
+
+const nsFrameSelection*
+nsIFrame::GetConstFrameSelection()
 {
   nsIFrame *frame = this;
   while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
     nsITextControlFrame *tcf;
     if (NS_SUCCEEDED(frame->QueryInterface(NS_GET_IID(nsITextControlFrame),(void**)&tcf))) {
       return tcf->GetOwnedFrameSelection();
     }
     frame = frame->GetParent();
   }
 
-  return PresContext()->PresShell()->FrameSelection();
+  return PresContext()->PresShell()->ConstFrameSelection();
 }
 
 #ifdef NS_DEBUG
 NS_IMETHODIMP
 nsFrame::DumpRegressionData(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent, PRBool aIncludeStyleData)
 {
   IndentBy(out, aIndent);
   fprintf(out, "<frame va=\"%ld\" type=\"", PRUptrdiff(this));
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -574,17 +574,17 @@ protected:
   // Test if we are selecting a table object:
   //  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(nsFrameSelection *aFrameSelection,
+  NS_IMETHOD GetDataForTableSelection(const nsFrameSelection *aFrameSelection,
                                       nsIPresShell *aPresShell, nsMouseEvent *aMouseEvent, 
                                       nsIContent **aParentContent, PRInt32 *aContentOffset, 
                                       PRInt32 *aTarget);
 
   virtual PRBool ParentDisablesSelection() const;
 
   // Fills aCursor with the appropriate information from ui
   static void FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -43,20 +43,20 @@
 #include "nsISelectionController.h"
 #include "nsIScrollableViewProvider.h"
 #include "nsITableLayout.h"
 #include "nsITableCellLayout.h"
 #include "nsIDOMElement.h"
 #include "nsGUIEvent.h"
 
 // IID for the nsFrameSelection interface
-// 6c2c1a4c-47ec-42be-a790-00417bf4c241
+// d78edc5a-28d0-48f0-8abb-1597b1591556
 #define NS_FRAME_SELECTION_IID      \
-{ 0x6c2c1a4c, 0x47ec, 0x42be, \
-  { 0xa7, 0x90, 0x00, 0x41, 0x7b, 0xf4, 0xc2, 0x41 } }
+{ 0xd78edc5a, 0x28d0, 0x48f0, \
+  { 0x8a, 0xbb, 0x15, 0x97, 0xb1, 0x59, 0x15, 0x56 } }
 
 #ifdef IBMBIDI // Constant for Set/Get CaretBidiLevel
 #define BIDI_LEVEL_UNDEFINED 0x80
 #endif
 
 //----------------------------------------------------------------------
 
 // Selection interface
@@ -189,16 +189,22 @@ struct nsPrevNextBidiLevels
   nsIFrame* mFrameAfter;
   PRUint8 mLevelBefore;
   PRUint8 mLevelAfter;
 };
 
 class nsTypedSelection;
 class nsIScrollableView;
 
+/**
+ * Methods which are marked with *unsafe* should be handled with special care.
+ * They may cause nsFrameSelection to be deleted, if strong pointer isn't used,
+ * or they may cause other objects to be deleted.
+ */
+
 class nsFrameSelection : public nsISupports {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_FRAME_SELECTION_IID)
   enum HINT { HINTLEFT = 0, HINTRIGHT = 1};  //end of this line or beginning of next
   /*interfaces for addref and release and queryinterface*/
   
   NS_DECL_ISUPPORTS
 
@@ -216,17 +222,17 @@ public:
   void SetScrollableViewProvider(nsIScrollableViewProvider* aProvider)
   {
     mScrollableViewProvider = aProvider;
   }
 
   /**
    * GetScrollableView returns the current scroll view.
    */
-  nsIScrollableView* GetScrollableView()
+  nsIScrollableView* GetScrollableView() const
   {
     return mScrollableViewProvider
       ? mScrollableViewProvider->GetScrollableView()
       : nsnull;
   }
 
   /** HandleClick will take the focus to the new frame at the new offset and 
    *  will either extend the selection from the old anchor, or replace the old anchor.
@@ -236,42 +242,45 @@ public:
    *  @param aContentOffsetEnd is the content offset of the parent aNewFocus and is specified different
    *                           when you need to select to and include both start and end points
    *  @param aContinueSelection is the flag that tells the selection to keep the old anchor point or not.
    *  @param aMultipleSelection will tell the frame selector to replace /or not the old selection. 
    *         cannot coexist with aContinueSelection
    *  @param aHint will tell the selection which direction geometrically to actually show the caret on. 
    *         1 = end of this line 0 = beginning of this line
    */
+  /*unsafe*/
   nsresult HandleClick(nsIContent *aNewFocus,
                        PRUint32 aContentOffset,
                        PRUint32 aContentEndOffset,
                        PRBool aContinueSelection,
                        PRBool aMultipleSelection,
                        PRBool aHint);
 
   /** HandleDrag extends the selection to contain the frame closest to aPoint.
    *  @param aPresContext is the context to use when figuring out what frame contains the point.
    *  @param aFrame is the parent of all frames to use when searching for the closest frame to the point.
    *  @param aPoint is relative to aFrame
    */
+  /*unsafe*/
   void HandleDrag(nsIFrame *aFrame, nsPoint aPoint);
 
   /** HandleTableSelection will set selection to a table, cell, etc
    *   depending on information contained in aFlags
    *  @param aParentContent is the paretent of either a table or cell that user clicked or dragged the mouse in
    *  @param aContentOffset is the offset of the table or cell
    *  @param aTarget indicates what to select (defined in nsISelectionPrivate.idl/nsISelectionPrivate.h):
    *    TABLESELECTION_CELL      We should select a cell (content points to the cell)
    *    TABLESELECTION_ROW       We should select a row (content points to any cell in row)
    *    TABLESELECTION_COLUMN    We should select a row (content points to any cell in column)
    *    TABLESELECTION_TABLE     We should select a table (content points to the table)
    *    TABLESELECTION_ALLCELLS  We should select all cells (content points to any cell in table)
    *  @param aMouseEvent         passed in so we can get where event occurred and what keys are pressed
    */
+  /*unsafe*/
   nsresult HandleTableSelection(nsIContent *aParentContent,
                                 PRInt32 aContentOffset,
                                 PRInt32 aTarget,
                                 nsMouseEvent *aMouseEvent);
 
   /** StartAutoScrollTimer is responsible for scrolling views so that aPoint is always
    *  visible, and for selecting any frame that contains aPoint. The timer will also reset
    *  itself to fire again if we have not scrolled to the end of the document.
@@ -294,150 +303,158 @@ public:
    * @param aContentOffset is the starting content boundary
    * @param aContentLength is the length of the content piece asking
    * @param aReturnDetails linkedlist of return values for the selection. 
    * @param aSlowCheck will check using slow method with no shortcuts
    */
   SelectionDetails* LookUpSelection(nsIContent *aContent,
                                     PRInt32 aContentOffset,
                                     PRInt32 aContentLength,
-                                    PRBool aSlowCheck);
+                                    PRBool aSlowCheck) const;
 
   /** SetMouseDownState(PRBool);
    *  sets the mouse state to aState for resons of drag state.
    * @param aState is the new state of mousedown
    */
+  /*unsafe*/
   void SetMouseDownState(PRBool aState);
 
   /** GetMouseDownState(PRBool *);
    *  gets the mouse state to aState for resons of drag state.
    * @param aState will hold the state of mousedown
    */
-  PRBool GetMouseDownState() { return mMouseDownState; }
+  PRBool GetMouseDownState() const { return mMouseDownState; }
 
   /**
     if we are in table cell selection mode. aka ctrl click in table cell
    */
-  PRBool GetTableCellSelection() { return mSelectingTableCellMode != 0; }
-  void ClearTableCellSelection(){ mSelectingTableCellMode = 0; }
+  PRBool GetTableCellSelection() const { return mSelectingTableCellMode != 0; }
+  void ClearTableCellSelection() { mSelectingTableCellMode = 0; }
 
   /** GetSelection
    * no query interface for selection. must use this method now.
    * @param aSelectionType enum value defined in nsISelection for the seleciton you want.
    */
-  nsISelection* GetSelection(SelectionType aType);
+  nsISelection* GetSelection(SelectionType aType) const;
 
   /**
    * ScrollSelectionIntoView scrolls a region of the selection,
    * so that it is visible in the scrolled view.
    *
    * @param aType the selection to scroll into view.
    * @param aRegion the region inside the selection to scroll into view.
    * @param aIsSynchronous when PR_TRUE, scrolls the selection into view
    * at some point after the method returns.request which is processed
    */
   nsresult ScrollSelectionIntoView(SelectionType aType,
                                    SelectionRegion aRegion,
-                                   PRBool aIsSynchronous);
+                                   PRBool aIsSynchronous) const;
 
   /** RepaintSelection repaints the selected frames that are inside the selection
    *  specified by aSelectionType.
    * @param aSelectionType enum value defined in nsISelection for the seleciton you want.
    */
-  nsresult RepaintSelection(SelectionType aType);
+  nsresult RepaintSelection(SelectionType aType) const;
 
   /** GetFrameForNodeOffset given a node and its child offset, return the nsIFrame and
    *  the offset into that frame. 
    * @param aNode input parameter for the node to look at
    * @param aOffset offset into above node.
    * @param aReturnOffset will contain offset into frame.
    */
   nsIFrame* GetFrameForNodeOffset(nsIContent *aNode,
                                   PRInt32     aOffset,
                                   HINT        aHint,
-                                  PRInt32    *aReturnOffset);
+                                  PRInt32    *aReturnOffset) const;
 
   /**
    * Scrolling then moving caret placement code in common to text areas and 
    * content areas should be located in the implementer
    * This method will accept the following parameters and perform the scroll 
    * and caret movement.  It remains for the caller to call the final 
    * ScrollCaretIntoView if that called wants to be sure the caret is always
    * visible.
    *
    * @param aForward if PR_TRUE, scroll forward if not scroll backward
    * @param aExtend  if PR_TRUE, extend selection to the new point
    * @param aScrollableView the view that needs the scrolling
    */
+  /*unsafe*/
   void CommonPageMove(PRBool aForward,
                       PRBool aExtend,
                       nsIScrollableView *aScrollableView);
 
   void SetHint(HINT aHintRight) { mHint = aHintRight; }
-  HINT GetHint() { return mHint; }
+  HINT GetHint() const { return mHint; }
   
 #ifdef IBMBIDI
   /** SetCaretBidiLevel sets the caret bidi level
    *  @param aLevel the caret bidi level
    *  This method is virtual since it gets called from outside of layout.
    */
   virtual void SetCaretBidiLevel (PRUint8 aLevel);
   /** GetCaretBidiLevel gets the caret bidi level
    *  This method is virtual since it gets called from outside of layout.
    */
-  virtual PRUint8 GetCaretBidiLevel();
+  virtual PRUint8 GetCaretBidiLevel() const;
   /** UndefineCaretBidiLevel sets the caret bidi level to "undefined"
    *  This method is virtual since it gets called from outside of layout.
    */
   virtual void UndefineCaretBidiLevel();
 #endif
 
   /** CharacterMove will generally be called from the nsiselectioncontroller implementations.
    *  the effect being the selection will move one character left or right.
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
+  /*unsafe*/
   nsresult CharacterMove(PRBool aForward, PRBool aExtend);
 
   /** WordMove will generally be called from the nsiselectioncontroller implementations.
    *  the effect being the selection will move one word left or right.
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
+  /*unsafe*/
   nsresult WordMove(PRBool aForward, PRBool aExtend);
 
   /** WordExtendForDelete extends the selection backward or forward (logically) to the
    *  next word boundary, so that the selected word can be deleted.
    * @param aForward select forward in document.
    */
+  /*unsafe*/
   nsresult WordExtendForDelete(PRBool aForward);
   
   /** LineMove will generally be called from the nsiselectioncontroller implementations.
    *  the effect being the selection will move one line up or down.
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
+  /*unsafe*/
   nsresult LineMove(PRBool aForward, PRBool aExtend);
 
   /** IntraLineMove will generally be called from the nsiselectioncontroller implementations.
    *  the effect being the selection will move to beginning or end of line
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
+  /*unsafe*/
   nsresult IntraLineMove(PRBool aForward, PRBool aExtend); 
 
   /** Select All will generally be called from the nsiselectioncontroller implementations.
    *  it will select the whole doc
    */
+  /*unsafe*/
   nsresult SelectAll();
 
   /** Sets/Gets The display selection enum.
    */
   void SetDisplaySelection(PRInt16 aState) { mDisplaySelection = aState; }
-  PRInt16 GetDisplaySelection() { return mDisplaySelection; }
+  PRInt16 GetDisplaySelection() const { return mDisplaySelection; }
 
   /** This method can be used to store the data received during a MouseDown
    *  event so that we can place the caret during the MouseUp event.
    * @aMouseEvent the event received by the selection MouseDown
    *  handling method. A NULL value can be use to tell this method
    *  that any data is storing is no longer valid.
    */
   void SetDelayedCaretData(nsMouseEvent *aMouseEvent);
@@ -450,31 +467,32 @@ public:
    */
   nsMouseEvent* GetDelayedCaretData();
 
   /** Get the content node that limits the selection
    *  When searching up a nodes for parents, as in a text edit field
    *    in an browser page, we must stop at this node else we reach into the 
    *    parent page, which is very bad!
    */
-  nsIContent* GetLimiter() { return mLimiter; }
+  nsIContent* GetLimiter() const { return mLimiter; }
 
-  nsIContent* GetAncestorLimiter() { return mAncestorLimiter; }
+  nsIContent* GetAncestorLimiter() const { return mAncestorLimiter; }
+  /*unsafe*/
   void SetAncestorLimiter(nsIContent *aLimiter);
 
   /** This will tell the frame selection that a double click has been pressed 
    *  so it can track abort future drags if inside the same selection
    *  @aDoubleDown has the double click down happened
    */
   void SetMouseDoubleDown(PRBool aDoubleDown) { mMouseDoubleDownState = aDoubleDown; }
   
   /** This will return whether the double down flag was set.
    *  @return whether the double down flag was set
    */
-  PRBool GetMouseDoubleDown() { return mMouseDoubleDownState; }
+  PRBool GetMouseDoubleDown() const { return mMouseDoubleDownState; }
 
   /** GetPrevNextBidiLevels will return the frames and associated Bidi levels of the characters
    *   logically before and after a (collapsed) selection.
    *  @param aNode is the node containing the selection
    *  @param aContentOffset is the offset of the selection in the node
    *  @param aJumpLines If PR_TRUE, look across line boundaries.
    *                    If PR_FALSE, behave as if there were base-level frames at line edges.  
    *
@@ -484,31 +502,31 @@ public:
    *   Bidi level equal to the paragraph embedding level.
    *  In these cases the before frame and after frame respectively will be 
    *   nsnull.
    *
    *  This method is virtual since it gets called from outside of layout. 
    */
   virtual nsPrevNextBidiLevels GetPrevNextBidiLevels(nsIContent *aNode,
                                                      PRUint32 aContentOffset,
-                                                     PRBool aJumpLines);
+                                                     PRBool aJumpLines) const;
 
   /** GetFrameFromLevel will scan in a given direction
    *   until it finds a frame with a Bidi level less than or equal to a given level.
    *   It will return the last frame before this.
    *  @param aPresContext is the context to use
    *  @param aFrameIn is the frame to start from
    *  @param aDirection is the direction to scan
    *  @param aBidiLevel is the level to search for
    *  @param aFrameOut will hold the frame returned
    */
   nsresult GetFrameFromLevel(nsIFrame *aFrameIn,
                              nsDirection aDirection,
                              PRUint8 aBidiLevel,
-                             nsIFrame **aFrameOut);
+                             nsIFrame **aFrameOut) const;
 
   /**
    * MaintainSelection will track the current selection as being "sticky".
    * Dragging or extending selection will never allow for a subset
    * (or the whole) of the maintained selection to become unselected.
    * Primary use: double click selecting then dragging on second click
    * @param aAmount the initial amount of text selected (word, line or paragraph).
    *                For "line", use eSelectBeginLine.
@@ -516,37 +534,39 @@ public:
   nsresult MaintainSelection(nsSelectionAmount aAmount = eSelectNoAmount);
 
 
   nsFrameSelection();
   virtual ~nsFrameSelection();
 
   void StartBatchChanges();
   void EndBatchChanges();
+  /*unsafe*/
   nsresult DeleteFromDocument();
 
-  nsIPresShell *GetShell() {return mShell;}
+  nsIPresShell *GetShell()const  { return mShell; }
 
+  void DisconnectFromPresShell() { mShell = nsnull; }
 private:
   nsresult TakeFocus(nsIContent *aNewFocus,
                      PRUint32 aContentOffset,
                      PRUint32 aContentEndOffset, 
                      PRBool aContinueSelection,
                      PRBool aMultipleSelection);
 
   void BidiLevelFromMove(nsIPresShell* aPresShell,
                          nsIContent *aNode,
                          PRUint32 aContentOffset,
                          PRUint32 aKeycode,
                          HINT aHint);
   void BidiLevelFromClick(nsIContent *aNewFocus, PRUint32 aContentOffset);
   nsPrevNextBidiLevels GetPrevNextBidiLevels(nsIContent *aNode,
                                              PRUint32 aContentOffset,
                                              HINT aHint,
-                                             PRBool aJumpLines);
+                                             PRBool aJumpLines) const;
 #ifdef VISUALSELECTION
   NS_IMETHOD VisualSelectFrames(nsIFrame* aCurrentFrame,
                                 nsPeekOffsetStruct aPos);
   NS_IMETHOD VisualSequence(nsIFrame* aSelectFrame,
                             nsIFrame* aCurrentFrame,
                             nsPeekOffsetStruct* aPos,
                             PRBool* aNeedVisualSelection);
   NS_IMETHOD SelectToEdge(nsIFrame *aFrame,
@@ -586,39 +606,44 @@ private:
 
   nsresult     FetchDesiredX(nscoord &aDesiredX); //the x position requested by the Key Handling for up down
   void         InvalidateDesiredX(); //do not listen to mDesiredX you must get another.
   void         SetDesiredX(nscoord aX); //set the mDesiredX
 
   nsresult     GetRootForContentSubtree(nsIContent *aContent, nsIContent **aParent);
   nsresult     ConstrainFrameAndPointToAnchorSubtree(nsIFrame *aFrame, nsPoint& aPoint, nsIFrame **aRetFrame, nsPoint& aRetPoint);
 
-  PRUint32     GetBatching(){return mBatching;}
-  PRBool       GetNotifyFrames(){return mNotifyFrames;}
+  PRUint32     GetBatching() const {return mBatching; }
+  PRBool       GetNotifyFrames() const { return mNotifyFrames; }
   void         SetDirty(PRBool aDirty=PR_TRUE){if (mBatching) mChangesDuringBatching = aDirty;}
 
+  // nsFrameSelection may get deleted when calling this,
+  // so remember to use nsCOMPtr when needed.
   nsresult     NotifySelectionListeners(SelectionType aType);     // add parameters to say collapsed etc?
 
   nsTypedSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
 
   // Table selection support.
   // Interfaces that let us get info based on cellmap locations
-  nsITableLayout* GetTableLayout(nsIContent *aTableContent);
-  nsITableCellLayout* GetCellLayout(nsIContent *aCellContent);
+  nsITableLayout* GetTableLayout(nsIContent *aTableContent) const;
+  nsITableCellLayout* GetCellLayout(nsIContent *aCellContent) const;
 
   nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
   nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
   nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
 
   nsresult GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
   nsresult GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
-  nsresult GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode);
+  nsresult GetFirstCellNodeInRange(nsIDOMRange *aRange,
+                                   nsIDOMNode **aCellNode) const;
   // aTableNode may be null if table isn't needed to be returned
-  PRBool   IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTableNode);
-  nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
+  PRBool   IsInSameTable(nsIContent *aContent1, nsIContent *aContent2,
+                         nsIContent **aTableNode) const;
+  nsresult GetParentTable(nsIContent *aCellNode,
+                          nsIContent **aTableNode) const;
   nsresult SelectCellElement(nsIDOMElement* aCellElement);
   nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
   nsresult ClearNormalSelection();
 
   nsCOMPtr<nsIDOMNode> mCellParent; //used to snap to table selection
   nsCOMPtr<nsIContent> mStartSelectedCell;
   nsCOMPtr<nsIContent> mEndSelectedCell;
   nsCOMPtr<nsIContent> mAppendStartSelectedCell;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -99,20 +99,20 @@ struct nsPeekOffsetStruct;
 struct nsPoint;
 struct nsRect;
 struct nsSize;
 struct nsMargin;
 
 typedef class nsIFrame nsIBox;
 
 // IID for the nsIFrame interface 
-// 39681dd7-5db6-4e38-b84c-5d9a163c987a
+// 95f75c0a-de85-437a-a195-0304df3f62ce
 #define NS_IFRAME_IID \
-{ 0x39681dd7, 0x5db6, 0x4e38, \
-  { 0xb8, 0x4c, 0x5d, 0x9a, 0x16, 0x3c, 0x98, 0x7a } }
+{ 0x95f75c0a, 0xde85, 0x437a, \
+  { 0xa1, 0x95, 0x03, 0x04, 0xdf, 0x3f, 0x62, 0xce } }
 
 /**
  * Indication of how the frame can be split. This is used when doing runaround
  * of floats, and when pulling up child frames from a next-in-flow.
  *
  * The choices are splittable, not splittable at all, and splittable in
  * a non-rectangular fashion. This last type only applies to block-level
  * elements, and indicates whether splitting can be used when doing runaround.
@@ -1721,19 +1721,25 @@ public:
   /** 
    *  Called to retrieve the SelectionController associated with the frame.
    *  @param aSelCon will contain the selection controller associated with
    *  the frame.
    */
   NS_IMETHOD  GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) = 0;
 
   /**
-   *  Call to get nsFrameSelection for this frame; does not addref
+   *  Call to get nsFrameSelection for this frame.
    */
-  nsFrameSelection* GetFrameSelection();
+  already_AddRefed<nsFrameSelection> GetFrameSelection();
+
+  /**
+   * GetConstFrameSelection returns an object which methods are safe to use for
+   * example in nsIFrame code.
+   */
+  const nsFrameSelection* GetConstFrameSelection();
 
   /** EndSelection related calls
    */
 
   /**
    *  Call to turn on/off mouseCapture at the view level. Needed by the ESM so
    *  it must be in the public interface.
    *  @param aPresContext presContext associated with the frame
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -107,20 +107,16 @@ static NS_DEFINE_CID(kFrameTraversalCID,
 #include "nsAutoCopyListener.h"
 #include "nsCopySupport.h"
 #include "nsIClipboard.h"
 
 #ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
 #endif // IBMBIDI
 
-#define STATUS_CHECK_RETURN_MACRO() {if (!mShell) return NS_ERROR_FAILURE;}
-
-
-
 //#define DEBUG_TABLE 1
 
 static NS_DEFINE_IID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
 static NS_DEFINE_IID(kCSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
 
 #undef OLD_SELECTION
 #undef OLD_TABLE_SELECTION
 
@@ -183,16 +179,23 @@ struct RangeData
 {
   RangeData(nsIDOMRange* aRange, PRInt32 aEndIndex) :
     mRange(aRange), mEndIndex(aEndIndex) {}
 
   nsCOMPtr<nsIDOMRange> mRange;
   PRInt32 mEndIndex; // index into mRangeEndings of this item
 };
 
+// Note, the ownership of nsTypedSelection depends on which way the object is
+// created. When nsFrameSelection has created nsTypedSelection,
+// addreffing/releasing nsTypedSelection object is aggregated to
+// nsFrameSelection. Otherwise normal addref/release is used.
+// This ensures that nsFrameSelection is never deleted before its
+// nsTypedSelections.
+
 class nsTypedSelection : public nsISelection2,
                          public nsISelectionPrivate,
                          public nsSupportsWeakReference
 {
 public:
   nsTypedSelection();
   nsTypedSelection(nsFrameSelection *aList);
   virtual ~nsTypedSelection();
@@ -274,18 +277,16 @@ private:
   nsresult     GetViewAncestorOffset(nsIView *aView, nsIView *aAncestorView, nscoord *aXOffset, nscoord *aYOffset);
 
 public:
   SelectionType GetType(){return mType;}
   void          SetType(SelectionType aType){mType = aType;}
 
   nsresult     NotifySelectionListeners();
 
-  void DetachFromPresentation();
-
 private:
   friend class nsSelectionIterator;
 
   class ScrollSelectionIntoViewEvent;
   friend class ScrollSelectionIntoViewEvent;
 
   class ScrollSelectionIntoViewEvent : public nsRunnable {
   public:
@@ -550,22 +551,24 @@ public:
       // that was destroyed.
       if (!capturingFrame) {
         NS_WARNING("Frame destroyed or set to display:none before scroll timer fired.");
         return NS_OK;
       }
 
       nsIView* captureView = capturingFrame->GetMouseCapturer();
     
-      nsIFrame* viewFrame = static_cast<nsIFrame*>(captureView->GetClientData());
-      NS_ASSERTION(viewFrame, "View must have a client frame");
+      nsWeakFrame viewFrame = static_cast<nsIFrame*>(captureView->GetClientData());
+      NS_ASSERTION(viewFrame.GetFrame(), "View must have a client frame");
       
       mFrameSelection->HandleDrag(viewFrame, mPoint);
 
-      mSelection->DoAutoScrollView(mPresContext, captureView, mPoint, PR_TRUE);
+      mSelection->DoAutoScrollView(mPresContext,
+                                   viewFrame.IsAlive() ? captureView : nsnull,
+                                   mPoint, PR_TRUE);
     }
     return NS_OK;
   }
 private:
   nsFrameSelection *mFrameSelection;
   nsTypedSelection *mSelection;
   nsPresContext *mPresContext;
   nsPoint mPoint;
@@ -819,17 +822,16 @@ nsFrameSelection::nsFrameSelection()
   PRInt32 i;
   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
     mDomSelections[i] = nsnull;
   }
   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
     mDomSelections[i] = new nsTypedSelection(this);
     if (!mDomSelections[i])
       return;
-    NS_ADDREF(mDomSelections[i]);
     mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
   }
   mBatching = 0;
   mChangesDuringBatching = PR_FALSE;
   mNotifyFrames = PR_TRUE;
   mLimiter = nsnull; //no default limiter.
   mAncestorLimiter = nsnull;
   
@@ -864,18 +866,17 @@ nsFrameSelection::nsFrameSelection()
 }
 
 
 nsFrameSelection::~nsFrameSelection()
 {
   PRInt32 i;
   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
     if (mDomSelections[i]) {
-      mDomSelections[i]->DetachFromPresentation();
-      NS_RELEASE(mDomSelections[i]);
+      delete mDomSelections[i];
     }
   }
 }
 
 
 NS_IMPL_ISUPPORTS1(nsFrameSelection, nsFrameSelection)
 
 
@@ -1073,16 +1074,17 @@ nsFrameSelection::ConstrainFrameAndPoint
 
   //
   // aFrame's root does not match the anchor's root, or there is no
   // content associated with aFrame. Just return the primary frame
   // for the anchor's root. We'll let GetContentAndOffsetsFromPoint()
   // find the closest frame aPoint.
   //
 
+  NS_ENSURE_STATE(mShell);
   *aRetFrame = mShell->GetPrimaryFrameFor(anchorRoot);
 
   if (! *aRetFrame)
     return NS_ERROR_FAILURE;
 
   //
   // Now make sure that aRetPoint is converted to the same coordinate
   // system used by aRetFrame.
@@ -1104,17 +1106,17 @@ nsFrameSelection::SetCaretBidiLevel(PRUi
   
   nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
   if (bidiKeyboard && !afterInsert)
     bidiKeyboard->SetLangFromBidiLevel(aLevel);
   return;
 }
 
 PRUint8
-nsFrameSelection::GetCaretBidiLevel()
+nsFrameSelection::GetCaretBidiLevel() const
 {
   return mCaretBidiLevel;
 }
 
 void
 nsFrameSelection::UndefineCaretBidiLevel()
 {
   mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED;
@@ -1213,30 +1215,25 @@ nsFrameSelection::Init(nsIPresShell *aSh
   mCaretMovementStyle = nsContentUtils::GetIntPref("bidi.edit.caret_movement_style", 2);
 }
 
 nsresult
 nsFrameSelection::MoveCaret(PRUint32          aKeycode,
                             PRBool            aContinueSelection,
                             nsSelectionAmount aAmount)
 {
-  {
-    // Make sure that if our presshell gets Destroy() called when we
-    // flush we don't die.
-    nsRefPtr<nsFrameSelection> kungFuDeathGrip(this);
-
-    // Flush out layout, since we need it to be up to date to do caret
-    // positioning.
-    mShell->FlushPendingNotifications(Flush_Layout);
-
-    if (!mShell) {
-      return NS_OK;
-    }
-  }
-    
+  NS_ENSURE_STATE(mShell);
+  // Flush out layout, since we need it to be up to date to do caret
+  // positioning.
+  mShell->FlushPendingNotifications(Flush_Layout);
+
+  if (!mShell) {
+    return NS_OK;
+  }
+
   nsPresContext *context = mShell->GetPresContext();
   if (!context)
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIDOMNode> weakNodeUsed;
   PRInt32 offsetused = 0;
 
   PRBool isCollapsed;
@@ -1940,26 +1937,26 @@ nsFrameSelection::VisualSelectFrames(nsI
   NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
   return NS_OK;
 }
 #endif // VISUALSELECTION
 
 nsPrevNextBidiLevels
 nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
                                         PRUint32    aContentOffset,
-                                        PRBool      aJumpLines)
+                                        PRBool      aJumpLines) const
 {
   return GetPrevNextBidiLevels(aNode, aContentOffset, mHint, aJumpLines);
 }
 
 nsPrevNextBidiLevels
 nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
                                         PRUint32    aContentOffset,
                                         HINT        aHint,
-                                        PRBool      aJumpLines)
+                                        PRBool      aJumpLines) const
 {
   // Get the level of the frames on each side
   nsIFrame    *currentFrame;
   PRInt32     currentOffset;
   PRInt32     frameStart, frameEnd;
   nsDirection direction;
   
   nsPrevNextBidiLevels levels;
@@ -2019,18 +2016,19 @@ nsFrameSelection::GetPrevNextBidiLevels(
 
   return levels;
 }
 
 nsresult
 nsFrameSelection::GetFrameFromLevel(nsIFrame    *aFrameIn,
                                     nsDirection  aDirection,
                                     PRUint8      aBidiLevel,
-                                    nsIFrame   **aFrameOut)
-{
+                                    nsIFrame   **aFrameOut) const
+{
+  NS_ENSURE_STATE(mShell);
   PRUint8 foundLevel = 0;
   nsIFrame *foundFrame = aFrameIn;
 
   nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
   nsresult result;
   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
   if (NS_FAILED(result))
       return result;
@@ -2263,17 +2261,17 @@ nsFrameSelection::HandleClick(nsIContent
   }
   
   return NS_OK;
 }
 
 void
 nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint)
 {
-  if (!aFrame)
+  if (!aFrame || !mShell)
     return;
 
   nsresult result;
   nsIFrame *newFrame = 0;
   nsPoint   newPoint;
 
   result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame, newPoint);
   if (NS_FAILED(result))
@@ -2346,16 +2344,17 @@ nsFrameSelection::HandleDrag(nsIFrame *a
                 PR_TRUE, PR_FALSE, offsets.associateWithNext);
 }
 
 nsresult
 nsFrameSelection::StartAutoScrollTimer(nsIView  *aView,
                                        nsPoint   aPoint,
                                        PRUint32  aDelay)
 {
+  NS_ENSURE_STATE(mShell);
   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
   return mDomSelections[index]->StartAutoScrollTimer(mShell->GetPresContext(),
                                                      aView, aPoint, aDelay);
 }
 
 void
 nsFrameSelection::StopAutoScrollTimer()
 {
@@ -2371,17 +2370,17 @@ nsFrameSelection::TakeFocus(nsIContent *
                             PRUint32    aContentOffset,
                             PRUint32    aContentEndOffset,
                             PRBool      aContinueSelection,
                             PRBool      aMultipleSelection)
 {
   if (!aNewFocus)
     return NS_ERROR_NULL_POINTER;
 
-  STATUS_CHECK_RETURN_MACRO();
+  NS_ENSURE_STATE(mShell);
 
   if (!IsValidSelectionPoint(this,aNewFocus))
     return NS_ERROR_FAILURE;
 
   // Clear all table selection data
   mSelectingTableCellMode = 0;
   mDragSelectingCells = PR_FALSE;
   mStartSelectedCell = nsnull;
@@ -2423,16 +2422,17 @@ nsFrameSelection::TakeFocus(nsIContent *
     if (aContentEndOffset != aContentOffset)
       mDomSelections[index]->Extend(domNode,aContentEndOffset);
 
     //find out if we are inside a table. if so, find out which one and which cell
     //once we do that, the next time we get a takefocus, check the parent tree. 
     //if we are no longer inside same table ,cell then switch to table selection mode.
     // BUT only do this in an editor
 
+    NS_ENSURE_STATE(mShell);
     PRInt16 displaySelection;
     nsresult result = mShell->GetSelectionFlags(&displaySelection);
     if (NS_FAILED(result))
       return result;
 
     // Editor has DISPLAY_ALL selection type
     if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
     {
@@ -2497,18 +2497,20 @@ printf(" * TakeFocus - moving into new c
   if (GetBatching())
     return NS_OK;
   return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
 }
 
 
 
 SelectionDetails*
-nsFrameSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset,
-                                  PRInt32 aContentLength, PRBool aSlowCheck)
+nsFrameSelection::LookUpSelection(nsIContent *aContent,
+                                  PRInt32 aContentOffset,
+                                  PRInt32 aContentLength,
+                                  PRBool aSlowCheck) const
 {
   if (!aContent || !mShell)
     return nsnull;
 
   SelectionDetails* details = nsnull;
 
   for (PRInt32 j = 0; j < nsISelectionController::NUM_SELECTIONTYPES; j++) {
     if (mDomSelections[j]) {
@@ -2531,59 +2533,60 @@ nsFrameSelection::SetMouseDownState(PRBo
   if (!mMouseDownState)
   {
     PostReason(nsISelectionListener::MOUSEUP_REASON);
     NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); //notify that reason is mouse up please.
   }
 }
 
 nsISelection*
-nsFrameSelection::GetSelection(SelectionType aType)
+nsFrameSelection::GetSelection(SelectionType aType) const
 {
   PRInt8 index = GetIndexFromSelectionType(aType);
   if (index < 0)
     return nsnull;
 
   return static_cast<nsISelection*>(mDomSelections[index]);
 }
 
 nsresult
 nsFrameSelection::ScrollSelectionIntoView(SelectionType   aType,
                                           SelectionRegion aRegion,
-                                          PRBool          aIsSynchronous)
+                                          PRBool          aIsSynchronous) const
 {
   PRInt8 index = GetIndexFromSelectionType(aType);
   if (index < 0)
     return NS_ERROR_INVALID_ARG;
 
   if (!mDomSelections[index])
     return NS_ERROR_NULL_POINTER;
 
   return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous,
                                                PR_FALSE);
 }
 
 nsresult
-nsFrameSelection::RepaintSelection(SelectionType aType)
+nsFrameSelection::RepaintSelection(SelectionType aType) const
 {
   PRInt8 index = GetIndexFromSelectionType(aType);
   if (index < 0)
     return NS_ERROR_INVALID_ARG;
   if (!mDomSelections[index])
     return NS_ERROR_NULL_POINTER;
+  NS_ENSURE_STATE(mShell);
   return mDomSelections[index]->Repaint(mShell->GetPresContext());
 }
  
 nsIFrame*
 nsFrameSelection::GetFrameForNodeOffset(nsIContent *aNode,
                                         PRInt32     aOffset,
                                         HINT        aHint,
-                                        PRInt32    *aReturnOffset)
-{
-  if (!aNode || !aReturnOffset)
+                                        PRInt32    *aReturnOffset) const
+{
+  if (!aNode || !aReturnOffset || !mShell)
     return nsnull;
 
   if (aOffset < 0)
     return nsnull;
 
   *aReturnOffset = aOffset;
 
   nsCOMPtr<nsIContent> theNode = aNode;
@@ -2813,16 +2816,17 @@ nsFrameSelection::SelectAll()
   {
     rootContent = mLimiter;//addrefit
   }
   else if (mAncestorLimiter) {
     rootContent = mAncestorLimiter;
   }
   else
   {
+    NS_ENSURE_STATE(mShell);
     nsIDocument *doc = mShell->GetDocument();
     if (!doc)
       return NS_ERROR_FAILURE;
     rootContent = doc->GetRootContent();
     if (!rootContent)
       return NS_ERROR_FAILURE;
   }
   PRInt32 numChildren = rootContent->GetChildCount();
@@ -2866,32 +2870,34 @@ nsFrameSelection::NotifySelectionListene
 static PRBool IsCell(nsIContent *aContent)
 {
   return ((aContent->Tag() == nsGkAtoms::td ||
            aContent->Tag() == nsGkAtoms::th) &&
           aContent->IsNodeOfType(nsINode::eHTML));
 }
 
 nsITableCellLayout* 
-nsFrameSelection::GetCellLayout(nsIContent *aCellContent)
-{
+nsFrameSelection::GetCellLayout(nsIContent *aCellContent) const
+{
+  NS_ENSURE_TRUE(mShell, nsnull);
   // Get frame for cell
   nsIFrame *cellFrame = mShell->GetPrimaryFrameFor(aCellContent);
   if (!cellFrame)
     return nsnull;
 
   nsITableCellLayout *cellLayoutObject = nsnull;
   CallQueryInterface(cellFrame, &cellLayoutObject);
 
   return cellLayoutObject;
 }
 
 nsITableLayout* 
-nsFrameSelection::GetTableLayout(nsIContent *aTableContent)
-{
+nsFrameSelection::GetTableLayout(nsIContent *aTableContent) const
+{
+  NS_ENSURE_TRUE(mShell, nsnull);
   // Get frame for table
   nsIFrame *tableFrame = mShell->GetPrimaryFrameFor(aTableContent);
   if (!tableFrame)
     return nsnull;
 
   nsITableLayout *tableLayoutObject = nsnull;
   CallQueryInterface(tableFrame, &tableLayoutObject);
 
@@ -3041,16 +3047,17 @@ printf("HandleTableSelection: Mouse down
         // Check if we have other selected cells
         nsCOMPtr<nsIDOMNode> previousCellNode;
         GetFirstSelectedCellAndRange(getter_AddRefs(previousCellNode), nsnull);
         if (previousCellNode)
         {
           // We have at least 1 other selected cell
 
           // Check if new cell is already selected
+          NS_ENSURE_STATE(mShell);
           nsIFrame  *cellFrame = mShell->GetPrimaryFrameFor(childContent);
           if (!cellFrame) return NS_ERROR_NULL_POINTER;
           result = cellFrame->GetSelected(&isSelected);
           if (NS_FAILED(result)) return result;
         }
         else
         {
           // No cells selected -- remove non-cell selection
@@ -3248,20 +3255,18 @@ nsFrameSelection::SelectBlockOfCells(nsI
 
   // Get starting and ending cells' location in the cellmap
   PRInt32 startRowIndex, startColIndex, endRowIndex, endColIndex;
   result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
   if(NS_FAILED(result)) return result;
   result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
   if(NS_FAILED(result)) return result;
 
-  // 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;
+  // Check that |table| is a table.
+  if (!GetTableLayout(table)) return NS_ERROR_FAILURE;
 
   PRInt32 curRowIndex, curColIndex;
 
   if (mDragSelectingCells)
   {
     // Drag selecting: remove selected cells outside of new block limits
 
     PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
@@ -3306,16 +3311,21 @@ printf("SelectBlockOfCells -- range is n
   // Note that we select block in the direction of user's mouse dragging,
   //  which means start cell may be after the end cell in either row or column
   PRInt32 row = startRowIndex;
   while(PR_TRUE)
   {
     PRInt32 col = startColIndex;
     while(PR_TRUE)
     {
+      // 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;
+
       result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
                                                 curRowIndex, curColIndex, rowSpan, colSpan, 
                                                 actualRowSpan, actualColSpan, isSelected);
       if (NS_FAILED(result)) return result;
 
       NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
 
       // Skip cells that are spanned from previous locations or are already selected
@@ -3457,17 +3467,17 @@ nsFrameSelection::SelectRowOrColumn(nsIC
   while (cellElement);
 #endif
 
   return NS_OK;
 }
 
 nsresult 
 nsFrameSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange,
-                                          nsIDOMNode **aCellNode)
+                                          nsIDOMNode **aCellNode) const
 {
   if (!aRange || !aCellNode) return NS_ERROR_NULL_POINTER;
 
   *aCellNode = nsnull;
 
   nsCOMPtr<nsIDOMNode> startParent;
   nsresult result = aRange->GetStartContainer(getter_AddRefs(startParent));
   if (NS_FAILED(result))
@@ -3597,17 +3607,17 @@ nsFrameSelection::GetCellIndexes(nsICont
   nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
   if (!cellLayoutObject)  return NS_ERROR_FAILURE;
   return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
 }
 
 PRBool 
 nsFrameSelection::IsInSameTable(nsIContent  *aContent1,
                                 nsIContent  *aContent2,
-                                nsIContent **aTable)
+                                nsIContent **aTable) const
 {
   if (!aContent1 || !aContent2) return PR_FALSE;
   
   // aTable is optional:
   if(aTable) *aTable = nsnull;
   
   nsCOMPtr<nsIContent> tableNode1;
   nsCOMPtr<nsIContent> tableNode2;
@@ -3626,17 +3636,17 @@ nsFrameSelection::IsInSameTable(nsIConte
       NS_ADDREF(*aTable);
     }
     return PR_TRUE;;
   }
   return PR_FALSE;
 }
 
 nsresult
-nsFrameSelection::GetParentTable(nsIContent *aCell, nsIContent **aTable)
+nsFrameSelection::GetParentTable(nsIContent *aCell, nsIContent **aTable) const
 {
   if (!aCell || !aTable) {
     return NS_ERROR_NULL_POINTER;
   }
 
   for (nsIContent* parent = aCell->GetParent(); parent;
        parent = parent->GetParent()) {
     if (parent->Tag() == nsGkAtoms::table &&
@@ -3987,20 +3997,16 @@ nsTypedSelection::nsTypedSelection()
   mDirection = eDirNext;
   mCachedOffsetForFrame = nsnull;
 }
 
 
 
 nsTypedSelection::~nsTypedSelection()
 {
-  DetachFromPresentation();
-}
-  
-void nsTypedSelection::DetachFromPresentation() {
   setAnchorFocusRange(-1);
 
   if (mAutoScrollTimer) {
     mAutoScrollTimer->Stop();
     mAutoScrollTimer = nsnull;
   }
 
   mScrollEvent.Revoke();
@@ -4020,19 +4026,46 @@ NS_INTERFACE_MAP_BEGIN(nsTypedSelection)
   NS_INTERFACE_MAP_ENTRY(nsISelection2)
   NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(Selection)
 NS_INTERFACE_MAP_END
 
 
-NS_IMPL_ADDREF(nsTypedSelection)
-NS_IMPL_RELEASE(nsTypedSelection)
-
+NS_IMETHODIMP_(nsrefcnt)
+nsTypedSelection::AddRef()
+{
+  if (mFrameSelection) {
+    return mFrameSelection->AddRef();
+  }
+  NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
+  NS_ASSERT_OWNINGTHREAD(nsTypedSelection);
+  ++mRefCnt;
+  NS_LOG_ADDREF(this, mRefCnt, "nsTypedSelection", sizeof(*this));
+  return mRefCnt;
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsTypedSelection::Release()
+{
+  if (mFrameSelection) {
+    return mFrameSelection->Release();
+  }
+  NS_PRECONDITION(0 != mRefCnt, "dup release");
+  NS_ASSERT_OWNINGTHREAD(nsTypedSelection);
+  --mRefCnt;
+  NS_LOG_RELEASE(this, mRefCnt, "nsTypedSelection");
+  if (mRefCnt == 0) {
+    mRefCnt = 1; /* stabilize */
+    NS_DELETEXPCOM(this);
+    return 0;
+  }
+  return mRefCnt;
+}
 
 NS_IMETHODIMP
 nsTypedSelection::SetPresShell(nsIPresShell *aPresShell)
 {
   mPresShellWeak = do_GetWeakReference(aPresShell);
   return NS_OK;
 }
 
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -3480,37 +3480,38 @@ GetGeneratedContentOwner(nsIFrame* aFram
     aFrame = aFrame->GetParent();
   }
   return aFrame;
 }
 
 SelectionDetails*
 nsTextFrame::GetSelectionDetails()
 {
+  const nsFrameSelection* frameSelection = GetConstFrameSelection();
   if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
     SelectionDetails* details =
-      GetFrameSelection()->LookUpSelection(mContent, GetContentOffset(), 
-                                           GetContentLength(), PR_FALSE);
+      frameSelection->LookUpSelection(mContent, GetContentOffset(),
+                                      GetContentLength(), PR_FALSE);
     SelectionDetails* sd;
     for (sd = details; sd; sd = sd->mNext) {
       sd->mStart += mContentOffset;
       sd->mEnd += mContentOffset;
     }
     return details;
   }
 
   // Check if the beginning or end of the element is selected, depending on
   // whether we're :before content or :after content.
   PRBool isBefore;
   nsIFrame* owner = GetGeneratedContentOwner(this, &isBefore);
   if (!owner || !owner->GetContent())
     return nsnull;
 
   SelectionDetails* details =
-    GetFrameSelection()->LookUpSelection(owner->GetContent(),
+    frameSelection->LookUpSelection(owner->GetContent(),
         isBefore ? 0 : owner->GetContent()->GetChildCount(), 0, PR_FALSE);
   SelectionDetails* sd;
   for (sd = details; sd; sd = sd->mNext) {
     // The entire text is selected!
     sd->mStart = GetContentOffset();
     sd->mEnd = GetContentEnd();
   }
   return details;
--- a/layout/mathml/base/src/nsMathMLmoFrame.cpp
+++ b/layout/mathml/base/src/nsMathMLmoFrame.cpp
@@ -93,18 +93,19 @@ nsMathMLmoFrame::IsFrameInSelection(nsIF
   if (!aFrame)
     return PR_FALSE;
 
   PRBool isSelected = PR_FALSE;
   aFrame->GetSelected(&isSelected);
   if (!isSelected)
     return PR_FALSE;
 
-  SelectionDetails* details = aFrame->GetFrameSelection()->
-    LookUpSelection(aFrame->GetContent(), 0, 1, PR_TRUE);
+  const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
+  SelectionDetails* details =
+    frameSelection->LookUpSelection(aFrame->GetContent(), 0, 1, PR_TRUE);
 
   if (!details)
     return PR_FALSE;
 
   while (details) {
     SelectionDetails* next = details->mNext;
     delete details;
     details = next;
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -291,17 +291,18 @@ nsTableCellFrame::DecorateForSelection(n
                                        nsPoint aPt)
 {
   NS_ASSERTION(GetStateBits() & NS_FRAME_SELECTED_CONTENT,
                "Should only be called for selected cells");
   PRInt16 displaySelection;
   nsPresContext* presContext = PresContext();
   displaySelection = DisplaySelection(presContext);
   if (displaySelection) {
-    nsFrameSelection *frameSelection = presContext->PresShell()->FrameSelection();
+    nsCOMPtr<nsFrameSelection> frameSelection =
+      presContext->PresShell()->FrameSelection();
 
     if (frameSelection->GetTableCellSelection()) {
       nscolor       bordercolor;
       if (displaySelection == nsISelectionController::SELECTION_DISABLED) {
         bordercolor = NS_RGB(176,176,176);// disabled color
       }
       else {
         presContext->LookAndFeel()->
@@ -487,17 +488,19 @@ nsTableCellFrame::SetSelected(nsPresCont
   }
   //return nsFrame::SetSelected(aRange,aSelected,eSpreadNone);
 #endif
   // Must call base class to set mSelected state and trigger repaint of frame
   // Note that in current version, aRange and aSpread are ignored,
   //   only this frame is considered
   nsFrame::SetSelected(aPresContext, aRange, aSelected, aSpread);
 
-  if (aPresContext->PresShell()->FrameSelection()->GetTableCellSelection()) {
+  nsCOMPtr<nsFrameSelection> frameSelection =
+    aPresContext->PresShell()->FrameSelection();
+  if (frameSelection->GetTableCellSelection()) {
     // Selection can affect content, border and outline
     Invalidate(GetOverflowRect(), PR_FALSE);
   }
   return NS_OK;
 }
 
 PRIntn
 nsTableCellFrame::GetSkipSides() const