Bug 675865 backout new dragginf for selection handling code from Mozilla8 r=smaug (backed out patches are: bug 552707, bug 644621, bug 670058, bug 670508, bug 671319)
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 03 Aug 2011 12:39:02 +0900
changeset 73695 d32bc792fb5ed6842ed91935409c8679d7384b4c
parent 73694 bd2c7e8f102e63cab65ff4adac5d7df0a8e304e1
child 73696 3735fb1cd5ef45a6b2f41388856aa85c769dd17f
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewerssmaug
bugs675865, 552707, 644621, 670058, 670508, 671319
milestone8.0a1
Bug 675865 backout new dragginf for selection handling code from Mozilla8 r=smaug (backed out patches are: bug 552707, bug 644621, bug 670058, bug 670508, bug 671319)
layout/base/nsPresShell.cpp
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsFrameSelection.h
layout/generic/nsSelection.cpp
layout/generic/test/Makefile.in
layout/generic/test/test_bug670058.html
layout/generic/test/test_bug670508.html
layout/generic/test/test_bug671319.html
layout/generic/test/test_bug673315-1.html
layout/generic/test/test_selection_expanding.html
layout/generic/test/test_selection_scrolling.html
layout/generic/test/window_selection_scrolling.html
layout/xul/base/src/nsScrollBoxFrame.cpp
layout/xul/base/src/nsScrollbarButtonFrame.cpp
layout/xul/base/src/nsScrollbarButtonFrame.h
layout/xul/base/src/nsScrollbarFrame.cpp
layout/xul/base/src/nsScrollbarFrame.h
layout/xul/base/src/nsSliderFrame.cpp
layout/xul/base/src/nsSliderFrame.h
layout/xul/base/src/nsSplitterFrame.cpp
layout/xul/base/src/nsSplitterFrame.h
modules/libpref/src/init/all.js
testing/mochitest/tests/SimpleTest/EventUtils.js
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6193,24 +6193,16 @@ PresShell::Paint(nsIView*           aVie
   presContext->NotifyDidPaintForSubtree();
   return NS_OK;
 }
 
 // static
 void
 nsIPresShell::SetCapturingContent(nsIContent* aContent, PRUint8 aFlags)
 {
-  // If SetCapturingContent() is called during dragging mouse for selection,
-  // we should abort current transaction.
-  nsRefPtr<nsFrameSelection> fs =
-    nsFrameSelection::GetMouseDownFrameSelection();
-  if (fs) {
-    fs->AbortDragForSelection();
-  }
-
   NS_IF_RELEASE(gCaptureInfo.mContent);
 
   // only set capturing content if allowed or the CAPTURE_IGNOREALLOWED flag
   // is used
   if ((aFlags & CAPTURE_IGNOREALLOWED) || gCaptureInfo.mAllowed) {
     if (aContent) {
       NS_ADDREF(gCaptureInfo.mContent = aContent);
     }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -928,16 +928,28 @@ nsFrame::GetAdditionalChildListName(PRIn
 }
 
 nsFrameList
 nsFrame::GetChildList(nsIAtom* aListName) const
 {
   return nsFrameList::EmptyList();
 }
 
+static nsIFrame*
+GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
+{
+  nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
+  if (capturingContent) {
+    nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
+    return activeFrame ? activeFrame : aFrame;
+  }
+
+  return aFrame;
+}
+
 PRInt16
 nsFrame::DisplaySelection(nsPresContext* aPresContext, PRBool isOkToTurnOn)
 {
   PRInt16 selType = nsISelectionController::SELECTION_OFF;
 
   nsCOMPtr<nsISelectionController> selCon;
   nsresult result = GetSelectionController(aPresContext, getter_AddRefs(selCon));
   if (NS_SUCCEEDED(result) && selCon) {
@@ -2034,168 +2046,25 @@ nsFrame::IsSelectable(PRBool* aSelectabl
     *aSelectable = (selectStyle != NS_STYLE_USER_SELECT_NONE);
   if (aSelectStyle)
     *aSelectStyle = selectStyle;
   if (mState & NS_FRAME_GENERATED_CONTENT)
     *aSelectable = PR_FALSE;
   return NS_OK;
 }
 
-nsFrameSelection*
-nsFrame::GetFrameSelectionForSelectingByMouse()
-{
-  PRBool selectable;
-  PRUint8 selectStyle;
-  nsresult rv = IsSelectable(&selectable, &selectStyle);
-  NS_ENSURE_SUCCESS(rv, nsnull);
-  if (!selectable) {
-    return nsnull;
-  }
-
-  // When implementing NS_STYLE_USER_SELECT_ELEMENT,
-  // NS_STYLE_USER_SELECT_ELEMENTS and NS_STYLE_USER_SELECT_TOGGLE, need to
-  // change this logic
-  PRBool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT);
-
-  // XXX This is screwy; it really should use the selection frame, not the
-  // event frame
-  const nsFrameSelection* frameselection = 
-    (selectStyle == NS_STYLE_USER_SELECT_TEXT) ? GetConstFrameSelection() :
-      PresContext()->PresShell()->ConstFrameSelection();
-
-  return const_cast<nsFrameSelection*>(frameselection);
-}
-
-/**
- * GetContentToCaptureForSelection() returns a content which should capture
- * mouse events for aSelectionRoot.  E.g., the result is <input type="text">
- * if the aSelectionRoot is anonymous div element in the editor.
- */
-static nsIContent*
-GetContentToCaptureForSelection(nsIContent* aSelectionRoot)
-{
-  return aSelectionRoot->FindFirstNonNativeAnonymous();
-}
-
-/**
- * GetSelectionRootContentForCapturingContent() returns a selection root
- * content for the capturing content.  E.g., the result is anonymous div
- * element if aCapturingContent is a <input type="text">.
- */
-static nsIContent*
-GetSelectionRootContentForCapturingContent(nsIPresShell* aPresShell,
-                                           nsIContent* aCapturingContent)
-{
-  if (!aCapturingContent->HasIndependentSelection()) {
-    return aCapturingContent;
-  }
-  return aCapturingContent->GetSelectionRootContent(aPresShell);
-}
-
-/**
- * FindNearestScrollableFrameForSelection() returns the nearest ancestor
- * scrollable frame when user is dragging on aFrame.
- *
- * @param aFrame            A frame which the user is dragging on.
- * @param aSelectionRoot    When this is not NULL, the result is guaranteed that
- *                          the result belongs to the same selection root.
- *                          For example, when aFrame is in <input type="text">
- *                          but user is selecting outside of the <input>:
- *                            * If aSelectionRoot is NULL, this returns the
- *                              selection root frame of the <input>.
- *                            * Otherwise, e.g., aSelectionRoot is the root
- *                              element of the document, the result is the
- *                              nearest ancestor scrollable element of the
- *                              <input> element.
- *
- * @return                  The nearest ancestor scrollable frame for aFrame.
- *                          If it was not found, returns NULL.
- */
-static nsIScrollableFrame*
-FindNearestScrollableFrameForSelection(nsIFrame* aFrame,
-                                       nsIContent* aSelectionRoot = nsnull)
-{
-#ifdef DEBUG
-  nsFrameSelection* draggingFrameSelection =
-    nsFrameSelection::GetMouseDownFrameSelection();
-  NS_ASSERTION(!draggingFrameSelection ||
-               draggingFrameSelection == aFrame->GetConstFrameSelection(),
-               "aFrame must be in dragging nsFrameSelection");
-#endif
-  PRBool foundCapturingContent = PR_FALSE;
-  nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
-  // If the specified selection root content is capturing content,
-  // that might be different from the computed selection root.  Then, we should
-  // replace aSelectionRoot with its computed selection root.
-  if (aSelectionRoot && aSelectionRoot == capturingContent) {
-    nsIFrame* selectionRootFrame = aSelectionRoot->GetPrimaryFrame();
-    NS_ENSURE_TRUE(selectionRootFrame, nsnull);
-    nsIPresShell* ps = selectionRootFrame->PresContext()->PresShell();
-    aSelectionRoot = aSelectionRoot->GetSelectionRootContent(ps);
-  }
-  nsIScrollableFrame* lastScrollableFrame = nsnull;
-  for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
-    do {
-      nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
-      if (!scrollableFrame || !scrollableFrame->GetScrolledFrame()) {
-        break; // non-scrollable frame.
-      }
-
-      if (aSelectionRoot) {
-        // If aSelectionRoot isn't null, find a scrollable frame whose
-        // selection root is the same as aSelectionRoot.
-        nsIContent* content = frame->GetContent();
-        if (!content) {
-          break;
-        }
-        nsIPresShell* ps = frame->PresContext()->PresShell();
-        if (content->GetSelectionRootContent(ps) != aSelectionRoot) {
-          break;
-        }
-      }
-
-      lastScrollableFrame = scrollableFrame;
-
-      // If the scrollable frame has independent selection, we should return it
-      // even if it's not actually scrollable.
-      if (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) {
-        return scrollableFrame;
-      }
-
-      nsRect range = scrollableFrame->GetScrollRange();
-      if (range.width == 0 && range.height == 0) {
-        // The scrollable frame cannot scroll actually. We should look for
-        // another scrollable frame which can scrollable, however, if there is
-        // no such frame, we should return top most scrollable frame.
-        break;
-      }
-
-      return scrollableFrame;
-    } while (0);
-
-    if (frame->GetContent() == capturingContent) {
-      foundCapturingContent = PR_TRUE;
-    } else if (foundCapturingContent) {
-      break; // There is no scrollable frame in the capturing content
-    }
-  }
-  return lastScrollableFrame;
-}
-
 /**
   * Handles the Mouse Press Event for the frame
  */
 NS_IMETHODIMP
 nsFrame::HandlePress(nsPresContext* aPresContext, 
                      nsGUIEvent*     aEvent,
                      nsEventStatus*  aEventStatus)
 {
   NS_ENSURE_ARG_POINTER(aEventStatus);
-  NS_ASSERTION(aPresContext == PresContext(),
-               "HandlePress called with different presContext");
   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
     return NS_OK;
   }
 
   //We often get out of sync state issues with mousedown events that
   //get interrupted by alerts/dialogs.
   //Check with the ESM to see if we should process this one
   if (!aPresContext->EventStateManager()->EventStatusOK(aEvent)) 
@@ -2223,133 +2092,112 @@ nsFrame::HandlePress(nsPresContext* aPre
         // coordinate stuff is the fix for bug #55921
         if ((mRect - GetPosition()).Contains(
                nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this)))
           return NS_OK;
       }
     }
   }
 
-  nsFrameSelection* fs = GetFrameSelectionForSelectingByMouse();
-  if (!fs) {
-    return NS_OK; // maybe, select: none
-  }
-
-  // If the mouse is dragged outside the selection root's scrollable area
+  // check whether style allows selection
+  // if not, don't tell selection the mouse event even occurred.  
+  PRBool  selectable;
+  PRUint8 selectStyle;
+  rv = IsSelectable(&selectable, &selectStyle);
+  if (NS_FAILED(rv)) return rv;
+  
+  // check for select: none
+  if (!selectable)
+    return NS_OK;
+
+  // When implementing NS_STYLE_USER_SELECT_ELEMENT, NS_STYLE_USER_SELECT_ELEMENTS and
+  // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic
+  PRBool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT);
+
+  // If the mouse is dragged outside the nearest enclosing scrollable area
   // while making a selection, the area will be scrolled. To do this, capture
-  // the mouse on the selection root frame. However, in table selection mode,
-  // a nearest scrollable frame should be captured the mouse because each
-  // scrollable frame except the nearest one doesn't need to scroll during
-  // selection.
-
-  // If something else is already capturing the mouse, the current selection
-  // root must be the capturing content.  However, it might be outside of the
-  // this frame's selection root content.  Then, we should do nothing.
-  nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
-  PRBool captureMouse = !capturingContent;
-  NS_ASSERTION(mContent, "mContent must not be null");
-  nsIContent* selectionRootOfThisFrame =
-    mContent->GetSelectionRootContent(shell);
-  NS_ASSERTION(selectionRootOfThisFrame,
-               "mContent must have a selection root content");
-  if (capturingContent) {
-    nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
-    NS_ENSURE_TRUE(capturingFrame, NS_OK);
-    nsIPresShell* capturedPresShell =
-      capturingFrame->PresContext()->PresShell();
-    NS_ASSERTION(capturedPresShell,
-                 "The captured content must have a presShell");
-    nsIContent* selectionRootOfCapturedContent =
-      capturingContent->GetSelectionRootContent(capturedPresShell);
-    NS_ASSERTION(selectionRootOfCapturedContent,
-                 "The captured content must have a selection root content");
-    if (selectionRootOfThisFrame != selectionRootOfCapturedContent) {
-      return NS_OK;
-    }
-  } else {
-    nsIContent* contentToCaptureForSelection =
-      GetContentToCaptureForSelection(selectionRootOfThisFrame);
-    nsIPresShell::SetCapturingContent(contentToCaptureForSelection,
-                                      CAPTURE_IGNOREALLOWED);
-  }
-
-  if (fs->GetDisplaySelection() == nsISelectionController::SELECTION_OFF) {
+  // the mouse on the nearest scrollable frame. If there isn't a scrollable
+  // frame, or something else is already capturing the mouse, there's no
+  // reason to capture.
+  if (!nsIPresShell::GetCapturingContent()) {
+    nsIFrame* checkFrame = this;
+    nsIScrollableFrame *scrollFrame = nsnull;
+    while (checkFrame) {
+      scrollFrame = do_QueryFrame(checkFrame);
+      if (scrollFrame) {
+        nsIPresShell::SetCapturingContent(checkFrame->GetContent(), CAPTURE_IGNOREALLOWED);
+        break;
+      }
+      checkFrame = checkFrame->GetParent();
+    }
+  }
+
+  // XXX This is screwy; it really should use the selection frame, not the
+  // event frame
+  const nsFrameSelection* frameselection = nsnull;
+  if (useFrameSelection)
+    frameselection = GetConstFrameSelection();
+  else
+    frameselection = shell->ConstFrameSelection();
+
+  if (!frameselection || 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.
   PRBool control = me->isMeta;
 #else
   PRBool control = me->isControl;
 #endif
 
+  nsRefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
   if (me->clickCount >1 )
   {
     // These methods aren't const but can't actually delete anything,
     // so no need for nsWeakFrame.
-    fs->SetMouseDownState(PR_TRUE);
-    fs->SetMouseDoubleDown(PR_TRUE);
+    fc->SetMouseDownState(PR_TRUE);
+    fc->SetMouseDoubleDown(PR_TRUE);
     return HandleMultiplePress(aPresContext, aEvent, aEventStatus, control);
   }
 
   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(fs, shell, me, getter_AddRefs(parentContent),
-                                &contentOffset, &target);
+  rv = GetDataForTableSelection(frameselection, shell, me, getter_AddRefs(parentContent), &contentOffset, &target);
   if (NS_SUCCEEDED(rv) && parentContent)
   {
-    // In table selection mode, a nearest scrollable frame should capture the
-    // mouse events.
-    if (captureMouse) {
-      // NOTE: we must have set a content to capture already.  The content is
-      // selection root of this frame.  Therefore, when there is no scrollable
-      // frame, we don't need to reset the capturing content.
-      NS_ASSERTION(nsIPresShell::GetCapturingContent() != nsnull,
-                   "Someone must have captured mouse event already");
-      nsIScrollableFrame* scrollableFrame =
-        FindNearestScrollableFrameForSelection(this);
-      if (scrollableFrame) {
-        nsIFrame* frame = do_QueryFrame(scrollableFrame);
-        nsIContent* contentToCaptureForTableSelection =
-          GetContentToCaptureForSelection(frame->GetContent());
-        nsIPresShell::SetCapturingContent(contentToCaptureForTableSelection,
-                                          CAPTURE_IGNOREALLOWED);
-      }
-    }
-    fs->SetMouseDownState(PR_TRUE);
-    return fs->HandleTableSelection(parentContent, contentOffset, target, me);
-  }
-
-  fs->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);
 
   if (isSelected)
   {
     PRBool inSelection = PR_FALSE;
-    details =
-      fs->LookUpSelection(offsets.content, 0, offsets.EndOffset(), PR_FALSE);
+    details = frameselection->LookUpSelection(offsets.content, 0,
+        offsets.EndOffset(), PR_FALSE);
 
     //
     // If there are any details, check to see if the user clicked
     // within any selected region of the frame.
     //
 
     SelectionDetails *curDetail = details;
 
@@ -2371,45 +2219,45 @@ nsFrame::HandlePress(nsPresContext* aPre
       }
 
       SelectionDetails *nextDetail = curDetail->mNext;
       delete curDetail;
       curDetail = nextDetail;
     }
 
     if (inSelection) {
-      fs->SetMouseDownState(PR_FALSE);
-      fs->SetDelayedCaretData(me);
+      fc->SetMouseDownState(PR_FALSE);
+      fc->SetDelayedCaretData(me);
       return NS_OK;
     }
   }
 
-  fs->SetMouseDownState(PR_TRUE);
+  fc->SetMouseDownState(PR_TRUE);
 
   // Do not touch any nsFrame members after this point without adding
   // weakFrame checks.
-  rv = fs->HandleClick(offsets.content, offsets.StartOffset(),
+  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)
-    fs->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?
-    fs->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?
@@ -2551,411 +2399,233 @@ nsFrame::PeekBackwardAndForward(nsSelect
                                    nsFrameSelection::HINTLEFT);
   if (NS_FAILED(rv))
     return rv;
 
   // maintain selection
   return frameSelection->MaintainSelection(aAmountBack);
 }
 
-NS_IMETHODIMP
-nsFrame::HandleDrag(nsPresContext* aPresContext,
-                    nsGUIEvent*    aEvent,
-                    nsEventStatus* aEventStatus)
-{
-  nsFrame* target;
-  nsRefPtr<nsFrameSelection> fs =
-    FindDraggingFrameSelection(aPresContext->PresShell(), &target);
-  if (!fs || !target || IsSelectionOff()) {
-    return NS_OK; // not selecting now
-  }
-
-  NS_ASSERTION(target->PresContext()->PresShell() == fs->GetShell(),
-               "A different presShell received mouse move event during drag");
-
-  // Stop auto scrolling, first.
-  fs->StopAutoScrollTimer();
-
-  return target->ExpandSelectionByMouseMove(fs, fs->GetShell(),
-                                            static_cast<nsMouseEvent*>(aEvent),
-                                            aEventStatus);
-}
-
-static const char kPrefName_EdgeWidth[] =
-  "layout.selection.drag.autoscroll.edge_width";
-static const char kPrefName_EdgeScrollAmount[] =
-  "layout.selection.drag.autoscroll.edge_scroll_amount";
-
-nsresult
-nsFrame::ExpandSelectionByMouseMove(nsFrameSelection* aFrameSelection,
-                                    nsIPresShell* aPresShell,
-                                    nsMouseEvent* aEvent,
-                                    nsEventStatus* aEventStatus)
-{
-#ifdef DEBUG
-  nsFrameSelection* draggingFrameSelection =
-    nsFrameSelection::GetMouseDownFrameSelection();
-  nsFrameSelection* selectionFrameForSelectingByMouse =
-    GetFrameSelectionForSelectingByMouse();
-  NS_ASSERTION(draggingFrameSelection,
-               "dragging FrameSelection must not be NULL");
-  NS_ASSERTION(draggingFrameSelection == selectionFrameForSelectingByMouse,
-               "aFrameSelection must be handling current drag for selection");
-#endif
+NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext, 
+                                  nsGUIEvent*     aEvent,
+                                  nsEventStatus*  aEventStatus)
+{
+  PRBool  selectable;
+  PRUint8 selectStyle;
+  IsSelectable(&selectable, &selectStyle);
+  // XXX Do we really need to exclude non-selectable content here?
+  // GetContentOffsetsFromPoint can handle it just fine, although some
+  // other stuff might not like it.
+  if (!selectable)
+    return NS_OK;
+  if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
+    return NS_OK;
+  }
+  nsIPresShell *presShell = aPresContext->PresShell();
+
+  nsRefPtr<nsFrameSelection> frameselection = GetFrameSelection();
+  PRBool mouseDown = frameselection->GetMouseDownState();
+  if (!mouseDown)
+    return NS_OK;
+
+  frameselection->StopAutoScrollTimer();
 
   // Check if we are dragging in a table cell
   nsCOMPtr<nsIContent> parentContent;
   PRInt32 contentOffset;
   PRInt32 target;
-  nsresult rv = GetDataForTableSelection(aFrameSelection, aPresShell,
-                                         aEvent, getter_AddRefs(parentContent),
-                                         &contentOffset, &target);
-  PRBool handleTableSelection = NS_SUCCEEDED(rv) && parentContent;
+  nsMouseEvent *me = (nsMouseEvent *)aEvent;
+  nsresult result;
+  result = GetDataForTableSelection(frameselection, presShell, me,
+                                    getter_AddRefs(parentContent),
+                                    &contentOffset, &target);      
 
   nsWeakFrame weakThis = this;
-  if (handleTableSelection) {
-    aFrameSelection->HandleTableSelection(parentContent, contentOffset,
-                                          target, aEvent);
+  if (NS_SUCCEEDED(result) && parentContent) {
+    frameselection->HandleTableSelection(parentContent, contentOffset, target, me);
   } else {
     nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
-    aFrameSelection->HandleDrag(this, pt);
+    frameselection->HandleDrag(this, pt);
   }
 
   // The frameselection object notifies selection listeners synchronously above
   // which might have killed us.
   if (!weakThis.IsAlive()) {
     return NS_OK;
   }
 
-  nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
-  if (!capturingContent) {
-    return NS_OK;  // The capture was canceled.
-  }
-  nsIContent* selectionRoot =
-    GetSelectionRootContentForCapturingContent(aPresShell, capturingContent);
-
-  nsIScrollableFrame* scrollableFrame =
-    FindNearestScrollableFrameForSelection(this, selectionRoot);
-  // If a non-scrollable content captures by script and there is no scrollable
-  // frame between the selection root and this, we don't need to do anymore.
-  if (!scrollableFrame) {
-    return NS_OK;
-  }
-
-  const PRUint32 kAutoScrollTimerDelay = 30;
-
-  if (!handleTableSelection) {
-    nsIScrollableFrame* selectionRootScrollableFrame =
-      FindNearestScrollableFrameForSelection(selectionRoot->GetPrimaryFrame(),
-                                             selectionRoot);
-    while (scrollableFrame) {
-      nsPoint scrollTo;
-      if (IsOnScrollableFrameEdge(scrollableFrame, aEvent, scrollTo)) {
-        aFrameSelection->StartAutoScrollTimer(
-          scrollableFrame->GetScrolledFrame(), scrollTo, kAutoScrollTimerDelay);
-        return NS_OK;
-      }
-
-      nsIFrame* frame = do_QueryFrame(scrollableFrame);
-      scrollableFrame =
-        FindNearestScrollableFrameForSelection(frame->GetParent(),
-                                               selectionRoot);
-    }
-    scrollableFrame = selectionRootScrollableFrame;
-  }
-
-  if (!scrollableFrame) {
-    return NS_OK;
-  }
-
-  nsIFrame* scrolledFrame = scrollableFrame->GetScrolledFrame();
-  NS_ASSERTION(scrolledFrame,
-               "The found scrollable frame doesn't have scrolled frame");
-  nsPoint scrollTo =
-    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, scrolledFrame);
-
-  // We should set minimum scroll speed same as the on-edge scrolling speed.
-  // E.g., while mouse cursor is on the edge, scrolling speed is always same.
-  nsPoint currentScrollPos = scrollableFrame->GetScrollPosition();
-  nsRect visibleRectOfScrolledFrame = scrollableFrame->GetScrollPortRect();
-  visibleRectOfScrolledFrame.MoveTo(currentScrollPos);
-  if (visibleRectOfScrolledFrame.Contains(scrollTo)) {
-    return NS_OK; // scroll wouldn't happen actually
-  }
-  PRInt32 minAmountPixel =
-    NS_MAX(Preferences::GetInt(kPrefName_EdgeScrollAmount), 1);
-  nscoord minAmountApp = PresContext()->DevPixelsToAppUnits(minAmountPixel);
-  if (visibleRectOfScrolledFrame.x > scrollTo.x) {
-    scrollTo.x =
-      NS_MIN(visibleRectOfScrolledFrame.x - minAmountApp, scrollTo.x);
-  } else if (visibleRectOfScrolledFrame.XMost() < scrollTo.x) {
-    scrollTo.x =
-      NS_MAX(visibleRectOfScrolledFrame.XMost() + minAmountApp, scrollTo.x);
-  }
-  if (visibleRectOfScrolledFrame.y > scrollTo.y) {
-    scrollTo.y =
-      NS_MIN(visibleRectOfScrolledFrame.y - minAmountApp, scrollTo.y);
-  } else if (visibleRectOfScrolledFrame.YMost() < scrollTo.y) {
-    scrollTo.y =
-      NS_MAX(visibleRectOfScrolledFrame.YMost() + minAmountApp, scrollTo.y);
-  }
-
-  aFrameSelection->StartAutoScrollTimer(scrolledFrame, scrollTo,
-                                        kAutoScrollTimerDelay);
+  // get the nearest scrollframe
+  nsIFrame* checkFrame = this;
+  nsIScrollableFrame *scrollFrame = nsnull;
+  while (checkFrame) {
+    scrollFrame = do_QueryFrame(checkFrame);
+    if (scrollFrame) {
+      break;
+    }
+    checkFrame = checkFrame->GetParent();
+  }
+
+  if (scrollFrame) {
+    nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
+    if (capturingFrame) {
+      nsPoint pt =
+        nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, capturingFrame);
+      frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
+    }
+  }
 
   return NS_OK;
 }
 
-nsFrame*
-nsFrame::FindSelectableAncestor(nsIFrame* aFrame,
-                                nsFrameSelection* aFrameSelection)
-{
-  // If we're not selecting by mouse dragging, our ancestor must be selecting,
-  // so, we should handle it on the first selectable ancestor frame of the
-  // selecting document.
-  for (nsIFrame* frame = aFrame; frame;
-       frame = nsLayoutUtils::GetCrossDocParentFrame(frame)) {
-    PRBool selectable = PR_FALSE;
-    if (frame->GetConstFrameSelection() == aFrameSelection &&
-        NS_SUCCEEDED(frame->IsSelectable(&selectable, nsnull)) &&
-        selectable) {
-      nsFrame* result = do_QueryFrame(frame);
-      if (result) {
-        return result;
-      }
-    }
-  }
-  return nsnull;
-}
-
-nsFrameSelection*
-nsFrame::FindDraggingFrameSelection(nsIPresShell* aPresShell,
-                                    nsFrame** aEventTarget)
-{
-  *aEventTarget = nsnull;
-  nsFrameSelection* fs = nsFrameSelection::GetMouseDownFrameSelection();
-  if (!fs) {
-    return nsnull; // not dragging now
-  }
-  NS_ASSERTION(fs->GetMouseDownState(),
-    "Wrong nsFrameSelection was returned by GetMouseDownFrameSelection()");
-
-  nsIFrame* selectingFrame = this;
-
-  // If this frame is for capturing content and it has independent selection,
-  // the actual selection root element might be its child or descendant which
-  // is a native anonymous element.
-  if (mContent == nsIPresShell::GetCapturingContent()) {
-    nsIContent* selectionRoot =
-      GetSelectionRootContentForCapturingContent(aPresShell, mContent);
-    if (selectionRoot) {
-      nsIFrame* frame = selectionRoot->GetPrimaryFrame();
-      if (frame) {
-        selectingFrame = frame;
+/**
+ * This static method handles part of the nsFrame::HandleRelease in a way
+ * which doesn't rely on the nsFrame object to stay alive.
+ */
+static nsresult
+HandleFrameSelection(nsFrameSelection*         aFrameSelection,
+                     nsIFrame::ContentOffsets& aOffsets,
+                     PRBool                    aHandleTableSel,
+                     PRInt32                   aContentOffsetForTableSel,
+                     PRInt32                   aTargetForTableSel,
+                     nsIContent*               aParentContentForTableSel,
+                     nsGUIEvent*               aEvent,
+                     nsEventStatus*            aEventStatus)
+{
+  if (!aFrameSelection) {
+    return NS_OK;
+  }
+
+  nsresult rv = NS_OK;
+
+  if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
+    if (!aHandleTableSel) {
+      nsMouseEvent *me = aFrameSelection->GetDelayedCaretData();
+      if (!aOffsets.content || !me) {
+        return NS_ERROR_FAILURE;
       }
-    }
-  }
-  *aEventTarget = FindSelectableAncestor(selectingFrame, fs);
-  if (!*aEventTarget) {
-    *aEventTarget = this;
-  }
-  return fs;
-}
-
-PRBool
-nsFrame::IsOnScrollableFrameEdge(nsIScrollableFrame* aScrollableFrame,
-                                 nsGUIEvent* aEvent,
-                                 nsPoint &aScrollIntoView)
-{
-  nsIFrame* scrollableFrame = do_QueryFrame(aScrollableFrame);
-  nsPoint ptInScrollableFrame =
-    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, scrollableFrame);
-  nsRect scrollableFrameRect(scrollableFrame->GetRect());
-  scrollableFrameRect.MoveTo(0, 0);
-  if (!scrollableFrameRect.Contains(ptInScrollableFrame)) {
-    return PR_FALSE; // cursor is outside of the frame.
-  }
-  nsPoint scrollPosition = aScrollableFrame->GetScrollPosition();
-  nsRect scrollRange = aScrollableFrame->GetScrollRange();
-  nsRect scrollPort = aScrollableFrame->GetScrollPortRect();
-
-  nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
-  NS_ENSURE_TRUE(scrolledFrame, PR_FALSE);
-
-  aScrollIntoView =
-    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, scrolledFrame);
-
-  // The edge width (or height) is defined by pref, however, if the value
-  // is too thick for the frame, we should use 1/4 width (or height) of
-  // the frame.
-  nsPresContext* pc = PresContext();
-  PRInt32 edgePixel = Preferences::GetInt(kPrefName_EdgeWidth);
-  nscoord edgeApp = pc->DevPixelsToAppUnits(edgePixel);
-  nscoord onePixel = pc->DevPixelsToAppUnits(1);
-
-  nscoord edgeH = NS_MAX(onePixel, NS_MIN(edgeApp, scrollPort.width / 4));
-  nscoord edgeV = NS_MAX(onePixel, NS_MIN(edgeApp, scrollPort.height / 4));
-
-  // The scrolling mouse is defined by pref, however, if the amount is
-  // too big for the frame, we should use 1/2 width (or height) of the
-  // frame.
-  PRInt32 scrollAmountPixel =
-    NS_MAX(Preferences::GetInt(kPrefName_EdgeScrollAmount), 1);
-  nscoord scrollAmountApp = pc->DevPixelsToAppUnits(scrollAmountPixel);
-
-  nscoord scrollAmountH =
-    NS_MAX(onePixel, NS_MIN(scrollAmountApp, scrollPort.width / 2));
-  nscoord scrollAmountV =
-    NS_MAX(onePixel, NS_MIN(scrollAmountApp, scrollPort.height / 2));
-
-  PRBool isOnEdge = PR_FALSE;
-  if (ptInScrollableFrame.x < scrollPort.x + edgeH) {
-    if (scrollRange.x < scrollPosition.x) {
-      // Scroll to left.
-      aScrollIntoView.x = scrollPosition.x - scrollAmountH;
-      isOnEdge = PR_TRUE;
-    }
-  } else if (ptInScrollableFrame.x > scrollPort.x + scrollPort.width - edgeH) {
-    if (scrollRange.width > scrollPosition.x) {
-      // Scroll to right.
-      aScrollIntoView.x = scrollPosition.x + scrollPort.width + scrollAmountH;
-      isOnEdge = PR_TRUE;
-    }
-  }
-
-  if (ptInScrollableFrame.y < scrollPort.y + edgeV) {
-    if (scrollRange.y < scrollPosition.y) {
-      // Scroll to top.
-      aScrollIntoView.y = scrollPosition.y - scrollAmountV;
-      isOnEdge = PR_TRUE;
-    }
-  } else if (ptInScrollableFrame.y > scrollPort.y + scrollPort.height - edgeV) {
-    if (scrollRange.height > scrollPosition.y) {
-      // Scroll to bottom.
-      aScrollIntoView.y = scrollPosition.y + scrollPort.height + scrollAmountV;
-      isOnEdge = PR_TRUE;
-    }
-  }
-  return isOnEdge;
-}
-
-NS_IMETHODIMP
-nsFrame::HandleRelease(nsPresContext* aPresContext,
-                       nsGUIEvent* aEvent,
-                       nsEventStatus* aEventStatus)
-{
-  // NOTE: You don't need to release mouse capture here. It'll be done
-  // in PresShell automatically.  If you need to do it here, you must
-  // do it after nsFrame::EndSelectionChangeByMouse() because we need
-  // to call nsFrameSelection::SetMouseDownState(PR_FALSE) first.
-  // If we release mouse event capture first, it doesn't cause
-  // MOUSEUP_REASON selection change event.
-  nsFrame* targetFrame;
-  nsRefPtr<nsFrameSelection> fs =
-    FindDraggingFrameSelection(aPresContext->PresShell(), &targetFrame);
-  if (!fs) {
-    // If mouse button was pressed on selected text and released without
-    // mousemove event, there is no dragging frame selection.  At that time,
-    // we need to clean up the pressed state with the frame selection for this
-    // frame.
-    fs = GetFrameSelectionForSelectingByMouse();
-    if (!fs) {
-      return NS_OK; // maybe, select: none
-    }
-    targetFrame = FindSelectableAncestor(this, fs);
-    if (!targetFrame) {
-      // XXX At this time, can we just return?
-      targetFrame = this;
-    }
-  }
-  NS_ASSERTION(targetFrame, "targetFrame must be non-null");
-
-  nsMouseEvent* mouseEvent = static_cast<nsMouseEvent*>(aEvent);
-  return targetFrame->EndSelectionChangeByMouse(fs, mouseEvent, aEventStatus);
-}
-
-nsresult
-nsFrame::EndSelectionChangeByMouse(nsFrameSelection* aFrameSelection,
-                                   nsMouseEvent* aMouseEvent,
-                                   nsEventStatus* aEventStatus)
-{
-  PRBool wasMouseDown = aFrameSelection->GetMouseDownState();
-
-  // First, stop expanding selection if necessary
+
+      // We are doing this to simulate what we would have done on HandlePress.
+      // We didn't do it there to give the user an opportunity to drag
+      // the text, but since they didn't drag, we want to place the
+      // caret.
+      // However, we'll use the mouse position from the release, since:
+      //  * it's easier
+      //  * that's the normal click position to use (although really, in
+      //    the normal case, small movements that don't count as a drag
+      //    can do selection)
+      aFrameSelection->SetMouseDownState(PR_TRUE);
+
+      rv = aFrameSelection->HandleClick(aOffsets.content,
+                                        aOffsets.StartOffset(),
+                                        aOffsets.EndOffset(),
+                                        me->isShift, PR_FALSE,
+                                        aOffsets.associateWithNext);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+    } else if (aParentContentForTableSel) {
+      aFrameSelection->SetMouseDownState(PR_FALSE);
+      rv = aFrameSelection->HandleTableSelection(aParentContentForTableSel,
+                                                 aContentOffsetForTableSel,
+                                                 aTargetForTableSel,
+                                                 (nsMouseEvent *)aEvent);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+    }
+    aFrameSelection->SetDelayedCaretData(0);
+  }
+
   aFrameSelection->SetMouseDownState(PR_FALSE);
   aFrameSelection->StopAutoScrollTimer();
 
-  if (IsSelectionOff() || nsEventStatus_eConsumeNoDefault == *aEventStatus) {
-    return NS_OK;
-  }
-
-  // Check if the frameselection recorded the mouse going down.
-  // If not, the user must have clicked in a part of the selection.
-  // Place the caret before continuing!
-  nsresult rv = NS_OK;
-  nsMouseEvent* delayedEvent = aFrameSelection->GetDelayedCaretData();
-  if (!wasMouseDown && delayedEvent && delayedEvent->clickCount < 2) {
-    nsPoint pt =
-      nsLayoutUtils::GetEventCoordinatesRelativeTo(aMouseEvent, this);
-    ContentOffsets offsets = GetContentOffsetsFromPoint(pt);
-    NS_ENSURE_TRUE(offsets.content, NS_ERROR_FAILURE);
-
-    // We are doing this to simulate what we would have done on HandlePress.
-    // We didn't do it there to give the user an opportunity to drag
-    // the text, but since they didn't drag, we want to place the
-    // caret.
-    // However, we'll use the mouse position from the release, since:
-    //  * it's easier
-    //  * that's the normal click position to use (although really, in
-    //    the normal case, small movements that don't count as a drag
-    //    can do selection)
-    aFrameSelection->SetMouseDownState(PR_TRUE);
-
-    // XXX Do not call any methods of the current object after this point!!!
-    // The object is perhaps dead!
-    rv = aFrameSelection->HandleClick(offsets.content,
-                                      offsets.StartOffset(),
-                                      offsets.EndOffset(),
-                                      delayedEvent->isShift,
-                                      PR_FALSE,
-                                      offsets.associateWithNext);
-
-    aFrameSelection->SetMouseDownState(PR_FALSE);
-    aFrameSelection->SetDelayedCaretData(0);
-
-    NS_ENSURE_SUCCESS(rv, rv);
-    return NS_OK;
-  }
-
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext,
+                                     nsGUIEvent*    aEvent,
+                                     nsEventStatus* aEventStatus)
+{
+  nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
+
+  nsCOMPtr<nsIContent> captureContent = nsIPresShell::GetCapturingContent();
+
+  // We can unconditionally stop capturing because
+  // we should never be capturing when the mouse button is up
+  nsIPresShell::SetCapturingContent(nsnull, 0);
+
+  PRBool selectionOff =
+    (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF);
+
+  nsRefPtr<nsFrameSelection> frameselection;
+  ContentOffsets offsets;
   nsCOMPtr<nsIContent> parentContent;
   PRInt32 contentOffsetForTableSel = 0;
   PRInt32 targetForTableSel = 0;
-  GetDataForTableSelection(aFrameSelection, PresContext()->PresShell(),
-                           aMouseEvent, getter_AddRefs(parentContent),
-                           &contentOffsetForTableSel, &targetForTableSel);
-  if (parentContent) {
-    // XXX Do not call any methods of the current object after this point!!!
-    // The object is perhaps dead!
-    rv = aFrameSelection->HandleTableSelection(parentContent,
-                                               contentOffsetForTableSel,
-                                               targetForTableSel,
-                                               aMouseEvent);
-  }
-  aFrameSelection->SetDelayedCaretData(nsnull);
-
-  NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
-}
-
-PRBool
-nsFrame::IsSelectionOff()
-{
-  nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
-  NS_ENSURE_TRUE(fs, PR_TRUE);
-  return (fs->GetDisplaySelection() == nsISelectionController::SELECTION_OFF);
+  PRBool handleTableSelection = PR_TRUE;
+
+  if (!selectionOff) {
+    frameselection = GetFrameSelection();
+    if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
+      // Check if the frameselection recorded the mouse going down.
+      // If not, the user must have clicked in a part of the selection.
+      // Place the caret before continuing!
+
+      PRBool mouseDown = frameselection->GetMouseDownState();
+      nsMouseEvent *me = frameselection->GetDelayedCaretData();
+
+      if (!mouseDown && me && me->clickCount < 2) {
+        nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
+        offsets = GetContentOffsetsFromPoint(pt);
+        handleTableSelection = PR_FALSE;
+      } else {
+        GetDataForTableSelection(frameselection, PresContext()->PresShell(),
+                                 (nsMouseEvent *)aEvent,
+                                 getter_AddRefs(parentContent),
+                                 &contentOffsetForTableSel,
+                                 &targetForTableSel);
+      }
+    }
+  }
+
+  // We might be capturing in some other document and the event just happened to
+  // trickle down here. Make sure that document's frame selection is notified.
+  // Note, this may cause the current nsFrame object to be deleted, bug 336592.
+  nsRefPtr<nsFrameSelection> frameSelection;
+  if (activeFrame != this &&
+      static_cast<nsFrame*>(activeFrame)->DisplaySelection(activeFrame->PresContext())
+        != nsISelectionController::SELECTION_OFF) {
+      frameSelection = activeFrame->GetFrameSelection();
+  }
+
+  // Also check the selection of the capturing content which might be in a
+  // different document.
+  if (!frameSelection && captureContent) {
+    nsIDocument* doc = captureContent->GetCurrentDoc();
+    if (doc) {
+      nsIPresShell* capturingShell = doc->GetShell();
+      if (capturingShell && capturingShell != PresContext()->GetPresShell()) {
+        frameSelection = capturingShell->FrameSelection();
+      }
+    }
+  }
+
+  if (frameSelection) {
+    frameSelection->SetMouseDownState(PR_FALSE);
+    frameSelection->StopAutoScrollTimer();
+  }
+
+  // Do not call any methods of the current object after this point!!!
+  // The object is perhaps dead!
+
+  return selectionOff
+    ? NS_OK
+    : HandleFrameSelection(frameselection, offsets, handleTableSelection,
+                           contentOffsetForTableSel, targetForTableSel,
+                           parentContent, aEvent, aEventStatus);
 }
 
 struct NS_STACK_CLASS FrameContentRange {
   FrameContentRange(nsIContent* aContent, PRInt32 aStart, PRInt32 aEnd) :
     content(aContent), start(aStart), end(aEnd) { }
   nsCOMPtr<nsIContent> content;
   PRInt32 start;
   PRInt32 end;
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -594,63 +594,16 @@ protected:
   //   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(const nsFrameSelection *aFrameSelection,
                                       nsIPresShell *aPresShell, nsMouseEvent *aMouseEvent, 
                                       nsIContent **aParentContent, PRInt32 *aContentOffset, 
                                       PRInt32 *aTarget);
 
-  // Returns nsFrameSelection which is handling drag for selection.
-  // If it's not dragging for selection, this returns NULL.  Otherwise,
-  // this returns the handling nsFrameSelection and a frame which is ancestor
-  // and should handle mouse events for selection.
-  nsFrameSelection* FindDraggingFrameSelection(nsIPresShell* aPresShell,
-                                               nsFrame** aEventTarget);
-
-  // ExpandSelectionByMouseMove() will expand selection by aEvent.
-  // This should be called ONLY when the frame is mouse event target which is
-  // found by FindDraggingFrameSelection().  And aFrameSelection must be the
-  // drag handling nsFrameSelection.
-  nsresult ExpandSelectionByMouseMove(nsFrameSelection* aFrameSelection,
-                                      nsIPresShell* aPresShell,
-                                      nsMouseEvent* aEvent,
-                                      nsEventStatus* aEventStatus);
-
-  // EndSelectionChangeByMouse() will stop the drag for selection if during
-  // that.  And also cancel the selection if it's clicked on the selection
-  // range.
-  // This can be called on every frame.  However, if an nsFrameSelection is
-  // handling drag for selection, this is called on the mouse event target
-  // frame which is computed by FindDraggingFrameSelection().
-  // Otherwise, nearest ancestor selectable frame's should be called.
-  nsresult EndSelectionChangeByMouse(nsFrameSelection* aFrameSelection,
-                                     nsMouseEvent* aMouseEvent,
-                                     nsEventStatus* aEventStatus);
-
-  // IsOnScrollableFrameEdge() checks whether the aEvent is fired on the
-  // edge of aScrollableFrame and it can be scrolled to the direction of the
-  // edge.  If aEvent is fired on the edge and scrollable, this returns TRUE.
-  // Otherwise, FALSE.  When this returns TRUE, this computes aScrollIntoView.
-  PRBool IsOnScrollableFrameEdge(nsIScrollableFrame* aScrollableFrame,
-                                 nsGUIEvent* aEvent,
-                                 nsPoint &aScrollIntoView);
-
-  // FindSelectableAncestor() returns a frame which is the nearest selectable
-  // ancestor of aFrame.
-  static nsFrame* FindSelectableAncestor(nsIFrame* aFrame,
-                                         nsFrameSelection* aFrameSelection);
-
-  // Returns nsFrameSelection for selecting by mouse in the frame.
-  nsFrameSelection* GetFrameSelectionForSelectingByMouse();
-
-  // If selection is off, returns TRUE.  Otherwise, FALSE.
-  // This method always uses the frame's nsFrameSelection.
-  PRBool IsSelectionOff();
-
   // Fills aCursor with the appropriate information from ui
   static void FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
                                              nsIFrame::Cursor& aCursor);
   NS_IMETHOD DoLayout(nsBoxLayoutState& aBoxLayoutState);
 
 #ifdef DEBUG_LAYOUT
   virtual void GetBoxName(nsAutoString& aName);
 #endif
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -365,30 +365,16 @@ public:
 
   /** GetMouseDownState(PRBool *);
    *  gets the mouse state to aState for resons of drag state.
    * @param aState will hold the state of mousedown
    */
   PRBool GetMouseDownState() const { return mMouseDownState; }
 
   /**
-   * GetMouseDownedFrameSelection returns an instance which is handling
-   * mouse dragging.
-   */
-  static nsFrameSelection* GetMouseDownFrameSelection()
-  {
-    return sDraggingFrameSelection;
-  }
-
-  /**
-   * Call AbortDragForSelection() when we abort handling the drag as selecting.
-   */
-  void AbortDragForSelection();
-
-  /**
     if we are in table cell selection mode. aka ctrl click in table cell
    */
   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.
@@ -607,30 +593,25 @@ public:
    * 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.
    */
   nsresult MaintainSelection(nsSelectionAmount aAmount = eSelectNoAmount);
 
 
   nsFrameSelection();
-  virtual ~nsFrameSelection();
 
   void StartBatchChanges();
   void EndBatchChanges();
   /*unsafe*/
   nsresult DeleteFromDocument();
 
   nsIPresShell *GetShell()const  { return mShell; }
 
-  void DisconnectFromPresShell()
-  {
-    AbortDragForSelection();
-    mShell = nsnull;
-  }
+  void DisconnectFromPresShell() { StopAutoScrollTimer(); mShell = nsnull; }
 private:
   nsresult TakeFocus(nsIContent *aNewFocus,
                      PRUint32 aContentOffset,
                      PRUint32 aContentEndOffset,
                      HINT aHint,
                      PRBool aContinueSelection,
                      PRBool aMultipleSelection);
 
@@ -710,18 +691,16 @@ private:
   nsIContent* GetFirstCellNodeInRange(nsIRange *aRange) const;
   // Returns non-null table if in same table, null otherwise
   nsIContent* IsInSameTable(nsIContent *aContent1, nsIContent *aContent2) const;
   // Might return null
   nsIContent* GetParentTable(nsIContent *aCellNode) const;
   nsresult CreateAndAddRange(nsINode *aParentNode, PRInt32 aOffset);
   nsresult ClearNormalSelection();
 
-  static nsFrameSelection* sDraggingFrameSelection;
-
   nsCOMPtr<nsINode> mCellParent; //used to snap to table selection
   nsCOMPtr<nsIContent> mStartSelectedCell;
   nsCOMPtr<nsIContent> mEndSelectedCell;
   nsCOMPtr<nsIContent> mAppendStartSelectedCell;
   nsCOMPtr<nsIContent> mUnselectCellOnMouseUp;
   PRInt32  mSelectingTableCellMode;
   PRInt32  mSelectedCellIndex;
 
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -136,17 +136,17 @@ static nsINode* GetCellParent(nsINode *a
 
 #ifdef PRINT_RANGE
 static void printRange(nsIRange *aDomRange);
 #define DEBUG_OUT_RANGE(x)  printRange(x)
 #else
 #define DEBUG_OUT_RANGE(x)  
 #endif //MOZ_DEBUG
 
-nsFrameSelection* nsFrameSelection::sDraggingFrameSelection = nsnull;
+
 
 //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
 //#define DEBUG_NAVIGATION
 
 
 //#define DEBUG_TABLE_SELECTION 1
 
 struct CachedOffsetForFrame {
@@ -421,26 +421,25 @@ public:
 
   virtual ~nsAutoScrollTimer()
   {
    if (mTimer)
        mTimer->Cancel();
   }
 
   // aPoint is relative to aPresContext's root frame
-  nsresult Start(nsPresContext *aPresContext, nsIContent *aContent,
-                 nsPoint &aPoint)
+  nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
   {
     mPoint = aPoint;
 
     // Store the presentation context. The timer will be
     // stopped by the selection if the prescontext is destroyed.
     mPresContext = aPresContext;
 
-    mContent = aContent;
+    mContent = nsIPresShell::GetCapturingContent();
 
     if (!mTimer)
     {
       nsresult result;
       mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
 
       if (NS_FAILED(result))
         return result;
@@ -744,23 +743,16 @@ nsFrameSelection::nsFrameSelection()
   }
 
   mDisplaySelection = nsISelectionController::SELECTION_OFF;
 
   mDelayedMouseEventValid = PR_FALSE;
   mSelectionChangeReason = nsISelectionListener::NO_REASON;
 }
 
-nsFrameSelection::~nsFrameSelection()
-{
-  if (this == sDraggingFrameSelection) {
-    sDraggingFrameSelection = nsnull;
-  }
-}
-
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)
   PRInt32 i;
   for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
     tmp->mDomSelections[i] = nsnull;
   }
 
@@ -920,20 +912,51 @@ nsFrameSelection::ConstrainFrameAndPoint
 
   if (content)
   {
     nsIContent* contentRoot = content->GetSelectionRootContent(mShell);
     NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED);
 
     if (anchorRoot == contentRoot)
     {
-      // The anchor and aFrame's root are the same. There
-      // is no need to constrain, simply return aFrame.
-      *aRetFrame = aFrame;
-      return NS_OK;
+      // If the aFrame's content isn't the capturing content, it should be
+      // a descendant.  At this time, we can return simply.
+      nsIContent* capturedContent = nsIPresShell::GetCapturingContent();
+      if (capturedContent != content)
+      {
+        return NS_OK;
+      }
+
+      // Find the frame under the mouse cursor with the root frame.
+      // At this time, don't use the anchor's frame because it may not have
+      // fixed positioned frames.
+      nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame();
+      nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame);
+      nsIFrame* cursorFrame =
+        nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
+
+      // If the mouse cursor in on a frame which is descendant of same
+      // selection root, we can expand the selection to the frame.
+      if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell)
+      {
+        nsIContent* cursorContent = cursorFrame->GetContent();
+        NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE);
+        nsIContent* cursorContentRoot =
+          cursorContent->GetSelectionRootContent(mShell);
+        NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED);
+        if (cursorContentRoot == anchorRoot)
+        {
+          *aRetFrame = cursorFrame;
+          aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame);
+          return NS_OK;
+        }
+      }
+      // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse
+      // cursor is out of the window), we should use the frame of the anchor
+      // root.
     }
   }
 
   //
   // When we can't find a frame which is under the mouse cursor and has a same
   // selection root as the anchor node's, we should return the selection root
   // frame.
   //
@@ -1916,50 +1939,26 @@ nsFrameSelection::LookUpSelection(nsICon
 }
 
 void
 nsFrameSelection::SetMouseDownState(PRBool aState)
 {
   if (mMouseDownState == aState)
     return;
 
-  NS_ASSERTION((aState && !sDraggingFrameSelection) ||
-               (!aState && sDraggingFrameSelection),
-               "Unexpected state happened");
-
   mMouseDownState = aState;
-
-  if (mMouseDownState) {
-    if (sDraggingFrameSelection) {
-      sDraggingFrameSelection->AbortDragForSelection();
-    }
-    sDraggingFrameSelection = this;
-  } else {
-    if (sDraggingFrameSelection == this) {
-      sDraggingFrameSelection = nsnull;
-    }
+    
+  if (!mMouseDownState)
+  {
     mDragSelectingCells = PR_FALSE;
     PostReason(nsISelectionListener::MOUSEUP_REASON);
     NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); //notify that reason is mouse up please.
   }
 }
 
-void
-nsFrameSelection::AbortDragForSelection()
-{
-  if (sDraggingFrameSelection == this) {
-    sDraggingFrameSelection = nsnull;
-    mMouseDownState = PR_FALSE;
-    mDragSelectingCells = PR_FALSE;
-    PostReason(nsISelectionListener::NO_REASON);
-    NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
-  }
-  StopAutoScrollTimer();
-}
-
 nsISelection*
 nsFrameSelection::GetSelection(SelectionType aType) const
 {
   PRInt8 index = GetIndexFromSelectionType(aType);
   if (index < 0)
     return nsnull;
 
   return static_cast<nsISelection*>(mDomSelections[index]);
@@ -4713,18 +4712,17 @@ nsTypedSelection::DoAutoScroll(nsIFrame 
   //
   // Start the AutoScroll timer if necessary.
   //
 
   if (didScroll && mAutoScrollTimer)
   {
     nsPoint presContextPoint = globalPoint -
       presContext->PresShell()->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
-    mAutoScrollTimer->Start(presContext, aFrame->GetContent(),
-                            presContextPoint);
+    mAutoScrollTimer->Start(presContext, presContextPoint);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTypedSelection::GetEnumerator(nsIEnumerator **aIterator)
 {
--- a/layout/generic/test/Makefile.in
+++ b/layout/generic/test/Makefile.in
@@ -130,21 +130,15 @@ include $(topsrcdir)/config/rules.mk
 _CHROME_FILES = \
   test_bug632379.xul \
   test_bug508115.xul \
   test_bug469613.xul \
   test_bug469774.xul \
   test_backspace_delete.xul \
   test_bug514732-2.xul \
   file_bug514732_window.xul \
-  test_selection_scrolling.html \
-  window_selection_scrolling.html \
-  test_bug670058.html \
-  test_bug670508.html \
-  test_bug671319.html \
-  test_bug673315-1.html \
   $(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
deleted file mode 100644
--- a/layout/generic/test/test_bug670058.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE>
-<html>
-<head>
-<title>test for bug 670058</title>
-<script type="text/javascript"
-  src="chrome://mochikit/content/MochiKit/packed.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
-<link rel="stylesheet" type="text/css"
-  href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-</head>
-<body>
-<textarea id="textarea" style="width: 200px; height: 100px;"></textarea>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var textarea = document.getElementById('textarea');
-
-var x = 50;
-var y = 50;
-
-var timer = 0;
-
-function doTest() {
-  synthesizeMouse(textarea, x, y, { type: "mousedown" });
-
-  timer = setInterval(function()
-    {
-      if (textarea.style.display == 'block') {
-        synthesizeMouse(textarea, x++, y++, { type: "mousemove" });
-        textarea.style.display = '';
-      } else {
-        textarea.style.display = 'block';
-        synthesizeMouse(textarea, x++, y++, { type: "mousemove" });
-      }
-      if (x == 70) {
-        ok(true, "not crashed");
-        clearInterval(timer);
-        SimpleTest.finish();
-      }
-    }, 200);
-}
-
-SimpleTest.waitForFocus(doTest, window);
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/layout/generic/test/test_bug670508.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE>
-<html>
-<head>
-<title>test for bug 670508</title>
-<script type="text/javascript"
-  src="chrome://mochikit/content/MochiKit/packed.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
-<link rel="stylesheet" type="text/css"
-  href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-</head>
-<body>
-<table>
-  <tr>
-    <td id="td" contenteditable="true">
-      here is an editable table cell
-    </td>
-  </tr>
-</table>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-function doTest() {
-  synthesizeMouse(document.getElementById('td'), 5, 5, { ctrlKey: true });
-  ok(true, "not crashed");
-  SimpleTest.finish();
-}
-
-SimpleTest.waitForFocus(doTest, window);
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/layout/generic/test/test_bug671319.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<!DOCTYPE>
-<html>
-<head>
-<title>test for bug 671319</title>
-<script type="text/javascript"
-  src="chrome://mochikit/content/MochiKit/packed.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
-<link rel="stylesheet" type="text/css"
-  href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-</head>
-<body>
-<iframe id="iframe1"
-  src="data:text/html,&lt;input id='childInput' style='width: 100px; height: 20px;'&gt;"
-  style="margin: 10px; width: 150px; height: 40px;"></iframe><br>
-<iframe id="iframe2"
-  src="data:text/html,&lt;input id='childInput' style='width: 100px; height: 20px;'&gt;"
-  style="margin: 10px; width: 150px; height: 40px;"></iframe>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-function doTest() {
-  var iframe1 = document.getElementById("iframe1");
-  var childInput1 = iframe1.contentDocument.getElementById("childInput");
-  var childWindow1 = iframe1.contentWindow;
-
-  var iframe2 = document.getElementById("iframe2");
-  var childInput2 = iframe2.contentDocument.getElementById("childInput");
-  childInput2.onmousedown = function (aEvent) {
-    aEvent.preventDefault();
-  };
-  childInput2.onmousemove = function (aEvent) {
-    childInput2.setCapture();
-  };
-  var childWindow2 = iframe2.contentWindow;
-
-  synthesizeMouse(childInput1, 10, 10, { type: "mousedown", button: 0 }, childWindow1);
-  iframe1.contentDocument.releaseCapture();
-  synthesizeMouse(childInput2, 10, 10, { type: "mousedown", button: 0 }, childWindow2);
-  synthesizeMouse(childInput2, 20, 10, { type: "mousemove" }, childWindow2);
-
-  ok(true, "not crashed");
-
-  SimpleTest.finish();
-}
-
-SimpleTest.waitForFocus(doTest, window);
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/layout/generic/test/test_bug673315-1.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!DOCTYPE>
-<html>
-<head>
-<title>test for bug 673315 part.1</title>
-<script type="text/javascript"
-  src="chrome://mochikit/content/MochiKit/packed.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
-<link rel="stylesheet" type="text/css"
-  href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="p">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</p>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var gSelectionListener = {
-  reason: 0,
-  reset: function () { this.reason = 0; },
-  notifySelectionChanged: function (doc, sel, reason)
-  {
-    this.reason |= reason;
-  }
-}
-
-function doTest() {
-  var p = document.getElementById("p");
-
-  const nsISelectionPrivate = Components.interfaces.nsISelectionPrivate;
-  const nsISelectionListener = Components.interfaces.nsISelectionListener;
-  var selPrivate =
-    window.getSelection().QueryInterface(nsISelectionPrivate);
-  selPrivate.addSelectionListener(gSelectionListener);
-
-  gSelectionListener.reset();
-  synthesizeMouse(p, 10, 10, { type: "mousedown", button: 0 });
-  is(gSelectionListener.reason,
-     nsISelectionListener.DRAG_REASON | nsISelectionListener.MOUSEDOWN_REASON,
-     "the reason isn't drag and mosuedown after mousedown event");
-  gSelectionListener.reset();
-  synthesizeMouse(p, 10, 50, { type: "mousemove" });
-  is(gSelectionListener.reason,
-     nsISelectionListener.DRAG_REASON | nsISelectionListener.MOUSEDOWN_REASON,
-     "the reason isn't drag and mosuedown after mousemove event");
-  gSelectionListener.reset();
-  synthesizeMouse(p, 10, 50, { type: "mouseup", button: 0 });
-  is(gSelectionListener.reason,
-     nsISelectionListener.MOUSEUP_REASON,
-     "the reason isn't mosueup after mouseup event");
-
-  selPrivate.addSelectionListener(gSelectionListener);
-
-  SimpleTest.finish();
-}
-
-SimpleTest.waitForFocus(doTest, window);
-
-</script>
-</pre>
-</body>
-</html>
--- a/layout/generic/test/test_selection_expanding.html
+++ b/layout/generic/test/test_selection_expanding.html
@@ -179,31 +179,38 @@ function test()
     div1.style.overflow = kOverflows[i];
     div2.style.overflow = kOverflows[i];
     div3.style.overflow = kOverflows[i];
 
     // ***********************************************************
     // selection starting at div1
     synthesizeMouse(div1, 30, 5, { type: "mousedown" });
 
+    // XXX if we move the mouse cursor to another document, the
+    // nsFrameSelection::HandleDrag method is called on the another document's.
+
     // to iframe
     synthesizeMouse(iframe, 30, 5, { type: "mousemove" });
-    check(kTrue, kFalse, kFalse,
+    check(kTrue | kToDo, kFalse, kFalse,
           kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
           "div1-iframe, all boxes are overflow: " + kOverflows[i] + ";");
 
+    // XXX if the overflow is visible, synthesizeMouse with the input element
+    // or textarea element doesn't work fine.
+    var isVisibleTesting = kOverflows[i] == "visible";
+    var todoFlag = isVisibleTesting ? kToDo : 0;
     // to input
     synthesizeMouse(input, 30, 5, { type: "mousemove" });
-    check(kTrue, kTrue, kFalse,
+    check(kTrue | todoFlag, kTrue | todoFlag, kFalse,
           kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
          "div1-input, all boxes are overflow: " + kOverflows[i] + ";");
 
     // to textarea
     synthesizeMouse(textarea, 30, 5, { type: "mousemove" });
-    check(kTrue, kTrue, kTrue,
+    check(kTrue | todoFlag, kTrue | todoFlag, kTrue | todoFlag,
           kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
           "div1-textarea, all boxes are overflow: " + kOverflows[i] + ";");
 
     // to div2
     synthesizeMouse(div2, 30, 5, { type: "mousemove" });
     check(kTrue, kTrue, kFalse,
           kFalse, kFalse, kFalse, kFalse, kFalse, kFalse,
           "div1-div2, all boxes are overflow: " + kOverflows[i] + ";");
deleted file mode 100644
--- a/layout/generic/test/test_selection_scrolling.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE>
-<html>
-<head>
-<title>selection scrolling test</title>
-<script type="text/javascript"
-  src="chrome://mochikit/content/MochiKit/packed.js"></script>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-<link rel="stylesheet" type="text/css"
-  href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-
-</head>
-<body>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-window.open("window_selection_scrolling.html", "_blank", 
-            "top=100,left=100,width=500,height=500,scrollbars=yes");
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/layout/generic/test/window_selection_scrolling.html
+++ /dev/null
@@ -1,1537 +0,0 @@
-<!DOCTYPE>
-<html>
-<head>
-<title>selection scrolling test</title>
-<script type="text/javascript"
-  src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
-
-<style type="text/css">
-  /* this is opened in 500px x 500px window */
-  #container {
-    width: 700px;
-    height: 700px;
-  }
-  #fixed {
-    position: fixed;
-    left: 60px;
-    right: 60px;
-    top: 60px;
-    bottom: 60px;
-    overflow: auto;
-    border: blue 1px solid;
-  }
-  #static_overflow {
-    margin: 60px;
-    width: 200px;
-    height: 200px;
-    overflow: auto;
-    border: orange 1px solid;
-  }
-  #static_overflow_inner {
-    margin: 60px;
-    width: 300px;
-    height: 300px;
-    overflow: auto;
-    border: red 1px solid;
-  }
-  #iframe {
-    margin: 60px;
-    width: 300px;
-    height: 300px;
-  }
-  #iframe2 {
-    margin: 70px;
-    width: 150px;
-    height: 150px;
-  }
-  #textarea {
-    margin: 60px;
-    width: 100px;
-    height: 100px;
-  }
-  .overflow_contents {
-    width: 400px;
-    height: 400px;
-  }
-</style>
-
-</head>
-<body onunload="onUnload();">
-<div id="container">
-  <div id="fixed">
-    <div class="overflow_contents"></div>
-  </div>
-  <input id="input" value="">
-  <div id="static_overflow">
-    <div id="static_overflow_contents" class="overflow_contents">
-      <div id="static_overflow_inner">
-        <div class="overflow_contents"></div>
-      </div>
-      <iframe id="iframe"
-        src="data:text/html,<div style='height:400px; width:400px;'></div>"></iframe>
-    </div>
-  </div>
-  <textarea id="textarea">1
-2
-3
-4
-5
-6
-7
-8
-9
-10
-11
-12
-13
-14
-15</textarea>
-  <iframe id="iframe2"
-    src="data:text/html,<div style='height:400px; width:400px;'></div>"></iframe>
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var body = document.body;
-
-var div_container = document.getElementById("container");
-var div_fixed = document.getElementById("fixed");
-var div_subframe1 = document.getElementById("static_overflow");
-var div_subframe2 = document.getElementById("static_overflow_inner");
-var iframe = document.getElementById("iframe");
-var input = document.getElementById("input");
-var textarea = document.getElementById("textarea");
-var iframe2 = document.getElementById("iframe2");
-
-function getBodyOfIframe(aIframe)
-{
-  return aIframe.contentDocument.body;
-}
-
-function getScrollRightMost(aElement)
-{
-  return aElement.scrollWidth - aElement.clientWidth;
-}
-
-function getScrollBottomMost(aElement)
-{
-  return aElement.scrollHeight - aElement.clientHeight;
-}
-
-function getHeight(aElement)
-{
-  return aElement.getBoundingClientRect().height;
-}
-
-function getWidth(aElement)
-{
-  return aElement.getBoundingClientRect().width;
-}
-
-function ok(aCondition, aDesc)
-{
-  window.opener.SimpleTest.ok(aCondition, aDesc);
-}
-
-function is(aLeft, aRight, aDesc)
-{
-  window.opener.SimpleTest.is(aLeft, aRight, aDesc);
-}
-
-function isnot(aLeft, aRight, aDesc)
-{
-  window.opener.SimpleTest.isnot(aLeft, aRight, aDesc);
-}
-
-/**
- *  gTests
- *
- *    description:
- *      String, required.
- *      This is used for logs of tests.
- *
- *    prepare:
- *      Function, optional.
- *      This is called before test().  You can initialize something in this
- *      function.
- *
- *    test:
- *      Function, required.
- *      This is called after prepare() by timer.  If all of its jobs are done,
- *      return true.  Otherwise, e.g., some elements have never been ready yet,
- *      return false.  Then, the caller recall test() after some delay.
- *      If this returns false many times, the test will be marked as failed.
- *
- *    resetAllElementsBeforeTest:
- *      Boolean, required.
- *      If all elements should be reintialized, e.g., all elements should be
- *      hidden, all elements should be reset the scroll position, set true.
- *      Otherwise, false.  Typically, true is set by first test of consecutive
- *      similar tests.
- *
- *    testTarget:
- *      Function, required.
- *      The function must return an element which should be checked the
- *      scroll position at the end of the test.
- *
- *    expectedScrollTop:
- *      Function, required.
- *      This function must return expected Y position of the testTarget().
- *
- *    expectedScrollLeft:
- *      Function, required.
- *      This function must return expected X position of the testTarget().
- *
- *    scrollIsExpected:
- *      Boolean, required.
- *      This means whether the test() should cause the testTarget scrolling.
- *      If the target should be scrolled, set true.  Otherwise, set false.
- */
-var gTestingIndex = -1;
-var gTests = [
-  // Scroll the selection root (body) element
-  //   visible scrollable elements: body
-  {
-    description: "Selection root (body) element should be scrolled by moving mouse to right of body",
-    test: function () {
-      synthesizeMouse(body, 100, 100, { type: "mousedown" });
-      synthesizeMouse(body, 600, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return body; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(body); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "Selection root (body) element should be scrolled by moving mouse to left of body",
-    test: function () {
-      synthesizeMouse(body, 100, 100, { type: "mousedown" });
-      synthesizeMouse(body, -100, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return body; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "Selection root (body) element should be scrolled by moving mouse to bottom of body",
-    test: function () {
-      synthesizeMouse(body, 100, 100, { type: "mousedown" });
-      synthesizeMouse(body, 100, 600, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return body; },
-    expectedScrollTop: function () { return getScrollBottomMost(body); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "Selection root (body) element should be scrolled by moving mouse to top of body",
-    test: function () {
-      synthesizeMouse(body, 100, 100, { type: "mousedown" });
-      synthesizeMouse(body, 100, -100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return body; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll the selection root (body) element on its edge
-  //   visible scrollable elements: body
-  {
-    description: "Selection root (body) element should be scrolled by moving mouse to right edge of body",
-    test: function () {
-      synthesizeMouse(body, 100, 100, { type: "mousedown" });
-      synthesizeMouse(body, body.clientWidth - 10, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return body; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(body); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "Selection root (body) element should be scrolled by moving mouse to bottom edge of body",
-    test: function () {
-      synthesizeMouse(body, 100, 100, { type: "mousedown" });
-      synthesizeMouse(body, 100, body.clientHeight - 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return body; },
-    expectedScrollTop: function () { return getScrollBottomMost(body); },
-    expectedScrollLeft: function () { return getScrollRightMost(body); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "Selection root (body) element should be scrolled by moving mouse to top edge of body",
-    test: function () {
-      synthesizeMouse(body, 100, 100, { type: "mousedown" });
-      synthesizeMouse(body, 100, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return body; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(body); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "Selection root (body) element should be scrolled by moving mouse to left edge of body",
-    test: function () {
-      synthesizeMouse(body, 100, 100, { type: "mousedown" });
-      synthesizeMouse(body, 10, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return body; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll fixed div on its edge (from fixed div)
-  //   visible scrollable elements: body, div_fixed
-  {
-    description: "The fixed div element should be scrolled by moving mouse from inner of it to its right edge",
-    prepare: function () {
-      div_fixed.style.display = "block";
-    },
-    test: function () {
-      synthesizeMouse(div_fixed, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_fixed, div_fixed.clientWidth - 10, 50,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_fixed); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The fixed div element should be scrolled by moving mouse from inner of it to its left edge",
-    test: function () {
-      synthesizeMouse(div_fixed, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_fixed, 10, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The fixed div element should be scrolled by moving mouse from inner of it to its bottom edge",
-    test: function () {
-      synthesizeMouse(div_fixed, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_fixed, 50, div_fixed.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_fixed); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The fixed div element should be scrolled by moving mouse from inner of it to its top edge",
-    test: function () {
-      synthesizeMouse(div_fixed, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_fixed, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Don't scroll fixed div by the cursor moved outside (from fixed_div)
-  //   visible scrollable elements: body, div_fixed
-  {
-    description: "The fixed div element should NOT be scrolled by moving mouse from inner of it to right outside of it",
-    prepare: function () {
-      div_fixed.style.display = "block";
-      div_fixed.scrollTop = 30;
-      div_fixed.scrollLeft = 30;
-    },
-    test: function () {
-      synthesizeMouse(div_fixed, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_fixed, getWidth(div_fixed) + 10, 100,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The fixed div element should NOT be scrolled by moving mouse from inner of it to left outside of it",
-    test: function () {
-      synthesizeMouse(div_fixed, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_fixed, -10, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The fixed div element should NOT be scrolled by moving mouse from inner of it to below outside of it",
-    test: function () {
-      synthesizeMouse(div_fixed, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_fixed, 100, getHeight(div_fixed) + 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The fixed div element should NOT be scrolled by moving mouse from inner of it to upper outside of it",
-    test: function () {
-      synthesizeMouse(div_fixed, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_fixed, 100, -10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  // Scroll fixed div on its edge (from body)
-  //   visible scrollable elements: body, div_fixed
-  {
-    description: "The fixed div element should be scrolled by moving mouse from body to the right edge of the fixed div",
-    prepare: function () {
-      div_fixed.style.display = "block";
-    },
-    test: function () {
-      synthesizeMouse(div_fixed, -5, -5, { type: "mousedown" });
-      synthesizeMouse(div_fixed, div_fixed.clientWidth - 10, 50,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_fixed); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The fixed div element should be scrolled by moving mouse from body to the left edge of the fixed div",
-    test: function () {
-      synthesizeMouse(div_fixed, -5, -5, { type: "mousedown" });
-      synthesizeMouse(div_fixed, 10, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The fixed div element should be scrolled by moving mouse from body to the bottom edge of the fixed div",
-    test: function () {
-      synthesizeMouse(div_fixed, -5, -5, { type: "mousedown" });
-      synthesizeMouse(div_fixed, 50, div_fixed.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_fixed); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The fixed div element should be scrolled by moving mouse from body to the top edge of the fixed div",
-    test: function () {
-      synthesizeMouse(div_fixed, -5, -5, { type: "mousedown" });
-      synthesizeMouse(div_fixed, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_fixed; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll subframe1 on its edge (from subframe1)
-  //   visible scrollable elements: body, div_subframe1
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from inner of it to its right edge",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-    },
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 50,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_subframe1); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from inner of it to its left edge",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 10, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from inner of it to its bottom edge",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, div_subframe1.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_subframe1); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from inner of it to its top edge",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Don't scroll div_subframe1 by the cursor moved outside (from div_subframe1)
-  //   visible scrollable elements: body, div_subframe1
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled by moving mouse from inner of it to right outside of it",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-      div_subframe1.scrollTop = 30;
-      div_subframe1.scrollLeft = 30;
-    },
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, getWidth(div_subframe1) + 10, 100,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled by moving mouse from inner of it to left outside of it",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, -10, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled by moving mouse from inner of it to below outside of it",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 100, getHeight(div_subframe1) + 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled by moving mouse from inner of it to upper outside of it",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 100, -10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  // Scroll subframe1 on its edge (from body)
-  //   visible scrollable elements: body, div_subframe1
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from body to the right edge of the div element",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-    },
-    test: function () {
-      synthesizeMouse(div_subframe1, -5, -5, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 50,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_subframe1); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from body to the left edge of the div element",
-    test: function () {
-      synthesizeMouse(div_subframe1, -5, -5, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 10, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from body to the bottom edge of the div element",
-    test: function () {
-      synthesizeMouse(div_subframe1, -5, -5, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, div_subframe1.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_subframe1); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from body to the top edge of the div element",
-    test: function () {
-      synthesizeMouse(div_subframe1, -5, -5, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll subframe1 on its edge when container (non-scrollable element) is
-  // capturing mouse events
-  //   visible scrollable elements: body, div_subframe1
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from subframe1 to the right edge of the div element (container is capturing mouse events)",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-      div_subframe1.onmousedown = function () { div_container.setCapture(); }
-    },
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 50,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_subframe1); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from subframe1 to the left edge of the div element (container is capturing mouse events)",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 10, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from subframe1 to the bottom edge of the div element (container is capturing mouse events)",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, div_subframe1.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_subframe1); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse from subframe1 to the top edge of the div element (container is capturing mouse events)",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll subframe1 even if subframe2 is moved under the cursor
-  //   visible scrollable elements: body, div_subframe1, div_subframe2
-  {
-    description: "The scrollable div element (subframe1) should be scrolled even if another scrollable div element (subframe2) is moved under the cursor #1",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-      div_subframe2.style.display = "block";
-    },
-    test: function () {
-      synthesizeMouse(div_subframe1, 5, 5, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 50,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_subframe1); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled even if another scrollable div element (subframe2) is moved under the cursor #2",
-    test: function () {
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 5,
-                      { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 10, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled even if another scrollable div element (subframe2) is moved under the cursor #3",
-    test: function () {
-      synthesizeMouse(div_subframe1, 5, 5, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, div_subframe1.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_subframe1); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled even if another scrollable div element (subframe2) is moved under the cursor #4",
-    test: function () {
-      synthesizeMouse(div_subframe1, 5, div_subframe1.clientHeight - 10,
-                      { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll subframe1 even if iframe is moved under the cursor
-  //   visible scrollable elements: body, div_subframe1, iframe
-  {
-    description: "The scrollable div element (subframe1) should be scrolled even if iframe element is moved under the cursor #1",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-      iframe.style.display = "block";
-    },
-    test: function () {
-      synthesizeMouse(div_subframe1, 5, 5, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 50,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_subframe1); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled even if iframe element is moved under the cursor #2",
-    test: function () {
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 5,
-                      { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 10, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled even if iframe element is moved under the cursor #3",
-    test: function () {
-      synthesizeMouse(div_subframe1, 5, 5, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, div_subframe1.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_subframe1); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled even if iframe element is moved under the cursor #4",
-    test: function () {
-      synthesizeMouse(div_subframe1, 5, div_subframe1.clientHeight - 10,
-                      { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Don't scroll subframe1 when selecting in input
-  //   visible scrollable elements: body, input, div_subframe1
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled when dragging for selection is started in input element (cursor is moved to right edge)",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-      input.style.display = "inline";
-      div_subframe1.scrollTop = 30;
-      div_subframe1.scrollLeft = 30;
-    },
-    test: function () {
-      synthesizeMouse(input, 10, 10, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 50,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled when dragging for selection is started in input element (cursor is moved to left edge)",
-    test: function () {
-      synthesizeMouse(input, 10, 10, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 10, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled when dragging for selection is started in input element (cursor is moved to bottom edge)",
-    test: function () {
-      synthesizeMouse(input, 10, 10, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, div_subframe1.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled when dragging for selection is started in input element (cursor is moved to top edge)",
-    test: function () {
-      synthesizeMouse(input, 10, 10, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 30; },
-    scrollIsExpected: false,
-  },
-
-  // Don't scroll textarea when selecting body
-  //   visible scrollable elements: body, textarea
-  {
-    description: "The textarea element should NOT be scrolled when dragging for selection is started in body element (cursor is moved to bottom edge)",
-    prepare: function () {
-      textarea.style.display = "inline";
-      textarea.scrollTop = 30;
-    },
-    test: function () {
-      if (textarea.scrollTop != 30) {
-        textarea.scrollTop = 30;
-        return false;
-      }
-      synthesizeMouse(body, 50, 50, { type: "mousedown" });
-      synthesizeMouse(textarea, 50, textarea.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return textarea; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The textarea element should NOT be scrolled when dragging for selection is started in body element (cursor is moved to top edge)",
-    test: function () {
-      synthesizeMouse(body, 50, 50, { type: "mousedown" });
-      synthesizeMouse(textarea, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return textarea; },
-    expectedScrollTop: function () { return 30; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: false,
-  },
-
-  // Scroll the textarea element
-  //   visible scrollable elements: body, textarea
-  {
-    description: "The textarea element should be scrolled by moving mouse to below outside of it",
-    prepare: function () {
-      textarea.style.display = "inline";
-    },
-    test: function () {
-      synthesizeMouse(textarea, 50, 50, { type: "mousedown" });
-      synthesizeMouse(textarea, 50, getHeight(textarea) + 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return textarea; },
-    expectedScrollTop: function () { return getScrollBottomMost(textarea); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The textarea element should be scrolled by moving mouse to upper outside of it",
-    test: function () {
-      synthesizeMouse(textarea, 50, 50, { type: "mousedown" });
-      synthesizeMouse(textarea, 50, -50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return textarea; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll the textarea element on its edge
-  //   visible scrollable elements: body, textarea
-  {
-    description: "The textarea element should be scrolled by moving mouse to its bottom edge",
-    prepare: function () {
-      textarea.style.display = "inline";
-    },
-    test: function () {
-      synthesizeMouse(textarea, 50, 50, { type: "mousedown" });
-      synthesizeMouse(textarea, 50, textarea.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return textarea; },
-    expectedScrollTop: function () { return getScrollBottomMost(textarea); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The textarea element should be scrolled by moving mouse to its top edge",
-    test: function () {
-      synthesizeMouse(textarea, 50, 50, { type: "mousedown" });
-      synthesizeMouse(textarea, 50, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return textarea; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Don't scroll iframe when selecting parent's body
-  //   visible scrollable elements: body, iframe (child of body)
-  {
-    description: "The iframe element should NOT be scrolled when dragging for selection is started from its parent's body element (cursor is moved to the right edge of the iframe)",
-    prepare: function () {
-      iframe2.style.display = "inline";
-      getBodyOfIframe(iframe2).scrollTop = 50;
-      getBodyOfIframe(iframe2).scrollLeft = 50;
-    },
-    test: function () {
-      synthesizeMouse(body, 50, 50, { type: "mousedown" });
-      synthesizeMouse(iframe2, iframe2.clientWidth - 10, 100,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 50; },
-    expectedScrollLeft: function () { return 50; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The iframe element should NOT be scrolled when dragging for selection is started from its parent's body element (cursor is moved to the left edge of the iframe)",
-    test: function () {
-      synthesizeMouse(body, 50, 50, { type: "mousedown" });
-      synthesizeMouse(iframe2, 10, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 50; },
-    expectedScrollLeft: function () { return 50; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The iframe element should NOT be scrolled when dragging for selection is started from its parent's body element (cursor is moved to the bottom edge of the iframe)",
-    test: function () {
-      synthesizeMouse(body, 50, 50, { type: "mousedown" });
-      synthesizeMouse(iframe2, 100, iframe2.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 50; },
-    expectedScrollLeft: function () { return 50; },
-    scrollIsExpected: false,
-  },
-
-  {
-    description: "The iframe element should NOT be scrolled when dragging for selection is started from its parent's body element (cursor is moved to the top edge of the iframe)",
-    test: function () {
-      synthesizeMouse(body, 50, 50, { type: "mousedown" });
-      synthesizeMouse(iframe2, 100, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 50; },
-    expectedScrollLeft: function () { return 50; },
-    scrollIsExpected: false,
-  },
-
-  // Scroll the iframe element when cursor is outside of it
-  //   visible scrollable elements: body, iframe (child of body)
-  {
-    description: "The iframe element should be scrolled by moving mouse to right outside of it",
-    prepare: function () {
-      iframe2.style.display = "inline";
-    },
-    test: function () {
-      synthesizeMouse(iframe2, 50, 50, { type: "mousedown" });
-      synthesizeMouse(iframe2, getWidth(iframe2) + 50, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(this.testTarget()); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The iframe element should be scrolled by moving mouse to left outside of it",
-    test: function () {
-      synthesizeMouse(iframe2, 50, 50, { type: "mousedown" });
-      synthesizeMouse(iframe2, -50, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The iframe element should be scrolled by moving mouse to below outside of it",
-    test: function () {
-      synthesizeMouse(iframe2, 50, 50, { type: "mousedown" });
-      synthesizeMouse(iframe2, 50, getHeight(iframe2) + 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return getScrollBottomMost(this.testTarget()); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The iframe element should be scrolled by moving mouse to upper outside of it",
-    test: function () {
-      synthesizeMouse(iframe2, 50, 50, { type: "mousedown" });
-      synthesizeMouse(iframe2, 50, -50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll the iframe on its edge
-  //   visible scrollable elements: body, iframe (child of body)
-  {
-    description: "The iframe element should be scrolled by moving mouse to its right edge",
-    prepare: function () {
-      iframe2.style.display = "inline";
-    },
-    test: function () {
-      synthesizeMouse(iframe2, 100, 100, { type: "mousedown" });
-      synthesizeMouse(iframe2, getBodyOfIframe(iframe2).clientWidth - 10, 100,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(this.testTarget()); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The iframe element should be scrolled by moving mouse to its left edge",
-    test: function () {
-      synthesizeMouse(iframe2, 100, 100, { type: "mousedown" });
-      synthesizeMouse(iframe2, 10, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The iframe element should be scrolled by moving mouse to its bottom edge",
-    test: function () {
-      synthesizeMouse(iframe2, 100, 100, { type: "mousedown" });
-      synthesizeMouse(iframe2, 100, getBodyOfIframe(iframe2).clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return getScrollBottomMost(this.testTarget()); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The iframe element should be scrolled by moving mouse to its top edge",
-    test: function () {
-      synthesizeMouse(iframe2, 100, 100, { type: "mousedown" });
-      synthesizeMouse(iframe2, 100, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return getBodyOfIframe(iframe2); },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll the div_subframe1 when it captures mouse events and the cursor
-  // in outside of it.
-  //   visible scrollable elements: body, div_subframe1
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse to right outside of it when it captures mouse events",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-      div_subframe1.onmousedown = function () { div_subframe1.setCapture(); }
-    },
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, getWidth(div_subframe1) + 50, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_subframe1); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse to left outside of it when it captures mouse events",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, -50, 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse to below outside of it when it captures mouse events",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, getHeight(div_subframe1) + 50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_subframe1); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse to upper outside of it when it captures mouse events",
-    test: function () {
-      synthesizeMouse(div_subframe1, 50, 50, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 50, -50, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  // Scroll the div_subframe1 when it captures mouse events and the cursor is
-  // on its edge.
-  //   visible scrollable elements: body, div_subframe1
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse to its right edge when it captures mouse events",
-    prepare: function () {
-      div_subframe1.style.display = "block";
-      div_subframe1.onmousedown = function () { div_subframe1.setCapture(); }
-    },
-    test: function () {
-      synthesizeMouse(div_subframe1, 100, 100, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, div_subframe1.clientWidth - 10, 100,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: true,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return getScrollRightMost(div_subframe1); },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse to its left edge when it captures mouse events",
-    test: function () {
-      synthesizeMouse(div_subframe1, 100, 100, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 10, 100, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should be scrolled by moving mouse to its bottom edge when it captures mouse events",
-    test: function () {
-      synthesizeMouse(div_subframe1, 100, 100, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 100, div_subframe1.clientHeight - 10,
-                      { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return getScrollBottomMost(div_subframe1); },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-
-  {
-    description: "The scrollable div element (subframe1) should NOT be scrolled by moving mouse to its top edge when it captures mouse events",
-    test: function () {
-      synthesizeMouse(div_subframe1, 100, 100, { type: "mousedown" });
-      synthesizeMouse(div_subframe1, 100, 10, { type: "mousemove" });
-      return true;
-    },
-    resetAllElementsBeforeTest: false,
-    testTarget: function () { return div_subframe1; },
-    expectedScrollTop: function () { return 0; },
-    expectedScrollLeft: function () { return 0; },
-    scrollIsExpected: true,
-  },
-];
-
-var gTimer;
-const kTimeoutTimeForNonScrollingTest = 1000;
-
-function initScrollEventHandler(aHandler)
-{
-  document.onscroll = aHandler;
-  div_fixed.onscroll = aHandler;
-  div_subframe1.onscroll = aHandler;
-  div_subframe2.onscroll = aHandler;
-  iframe.contentDocument.onscroll = aHandler;
-  iframe2.contentDocument.onscroll = aHandler;
-  textarea.onscroll = aHandler;
-}
-
-function checkResult(aTimedOut)
-{
-  var test = gTests[gTestingIndex];
-  if (test.scrollIsExpected) {
-    ok(!aTimedOut, test.description + ": timed out");
-  } else {
-    ok(aTimedOut, test.description + ": not timed out");
-  }
-  is(test.testTarget().scrollTop, test.expectedScrollTop(),
-     test.description + ": unexpected vertial scroll position");
-  is(test.testTarget().scrollLeft, test.expectedScrollLeft(),
-     test.description + ": unexpected horizontal scroll position");
-  cleanupTest();
-}
-
-function cleanupTest()
-{
-  initScrollEventHandler(null);
-  synthesizeMouse(body, 10, 10, { type: "mouseup" });
-  runTests();
-}
-
-function onScroll(aEvent)
-{
-  function getScrollEventTarget(aScrollElement)
-  {
-    return aScrollElement == aScrollElement.ownerDocument.body ?
-             aScrollElement.ownerDocument : aScrollElement;
-  }
-
-  var test = gTests[gTestingIndex];
-  if (aEvent.target != getScrollEventTarget(test.testTarget())) {
-    is(aEvent.target, getScrollEventTarget(test.testTarget()),
-       test.description + ": Unexpected element was scrolled");
-    return;
-  }
-
-  clearTimeout(gTimer);
-  gTimer = 0;
-
-  var expectedScrollPosition =
-    (test.testTarget().scrollTop == test.expectedScrollTop() &&
-     test.testTarget().scrollLeft == test.expectedScrollLeft());
-
-  if (test.scrollIsExpected) {
-    if (expectedScrollPosition) {
-      // scroll is finished
-      checkResult(false);
-      return;
-    }
-
-    // if this test failed, it would be timeout.
-  } else {
-    if (expectedScrollPosition) {
-      // XXX If the unexpected scroll event is for initialization, we should
-      // ignore this event.
-      gTimer = setTimeout(checkResult, kTimeoutTimeForNonScrollingTest, true);
-      return;
-    }
-    checkResult(false);
-  }
-}
-
-function doTest(aTest)
-{
-  if (!aTest.test()) {
-    // retry until timeout
-    setTimeout(doTest, 0, aTest);
-    return;
-  }
-
-  initScrollEventHandler(onScroll);
-  if (aTest.scrollIsExpected) {
-    // if this test failed, it would be timeout.
-  } else {
-    gTimer = setTimeout(checkResult, kTimeoutTimeForNonScrollingTest, true);
-  }
-}
-
-function runTests()
-{
-  if (++gTestingIndex == gTests.length) {
-    window.close();
-    return;
-  }
-
-  disableNonTestMouseEvents(true);
-
-  try {
-    window.getSelection().removeAllRanges();
-  } catch (e) { }
-  try {
-    iframe.contentWindow.getSelection().removeAllRanges();
-  } catch (e) { }
-  try {
-    iframe2.contentWindow.getSelection().removeAllRanges();
-  } catch (e) { }
-
-  var test = gTests[gTestingIndex];
-
-  if (test.resetAllElementsBeforeTest) {
-    body.scrollTop = 0;
-    body.scrollLeft = 0;
-    input.style.position = "static";
-    input.style.display = "none";
-    div_fixed.scrollTop = 0;
-    div_fixed.scrollLeft = 0;
-    div_fixed.style.display = "none";
-    div_subframe1.onmousedown = null;
-    div_subframe1.scrollTop = 0;
-    div_subframe1.scrollLeft = 0;
-    div_subframe1.style.display = "none";
-    div_subframe2.scrollTop = 0;
-    div_subframe2.scrollLeft = 0;
-    div_subframe2.style.display = "none";
-    getBodyOfIframe(iframe).scrollTop = 0;
-    getBodyOfIframe(iframe).scrollLeft = 0;
-    iframe.style.display = "none";
-    getBodyOfIframe(iframe2).scrollTop = 0;
-    getBodyOfIframe(iframe2).scrollLeft = 0;
-    iframe2.style.display = "none";
-    textarea.scrollTop = 0;
-    textarea.scrollLeft = 0;
-    textarea.style.display = "none";
-  }
-
-  if (test.prepare) {
-    test.prepare();
-  }
-
-  setTimeout(doTest, 0, test);
-}
-
-function onUnload()
-{
-  is(gTestingIndex, gTests.length,
-     "Some tests are not tested");
-  disableNonTestMouseEvents(false);
-  window.opener.SimpleTest.finish();
-}
-
-window.opener.SimpleTest.waitForFocus(runTests, window);
-
-</script>
-</pre>
-</body>
-</html>
--- a/layout/xul/base/src/nsScrollBoxFrame.cpp
+++ b/layout/xul/base/src/nsScrollBoxFrame.cpp
@@ -164,17 +164,17 @@ nsAutoRepeatBoxFrame::HandlePress(nsPres
 NS_IMETHODIMP 
 nsAutoRepeatBoxFrame::HandleRelease(nsPresContext* aPresContext, 
                                     nsGUIEvent* aEvent,
                                     nsEventStatus* aEventStatus)
 {
   if (!IsActivatedOnHover()) {
     StopRepeat();
   }
-  return nsButtonBoxFrame::HandleRelease(aPresContext, aEvent, aEventStatus);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoRepeatBoxFrame::AttributeChanged(PRInt32 aNameSpaceID,
                                        nsIAtom* aAttribute,
                                        PRInt32 aModType)
 {
   if (aAttribute == nsGkAtoms::type) {
--- a/layout/xul/base/src/nsScrollbarButtonFrame.cpp
+++ b/layout/xul/base/src/nsScrollbarButtonFrame.cpp
@@ -77,20 +77,21 @@ nsScrollbarButtonFrame::HandleEvent(nsPr
 
   // If a web page calls event.preventDefault() we still want to
   // scroll when scroll arrow is clicked. See bug 511075.
   if (!mContent->IsInNativeAnonymousSubtree() &&
       nsEventStatus_eConsumeNoDefault == *aEventStatus) {
     return NS_OK;
   }
 
-  if (aEvent->message == NS_MOUSE_EXIT_SYNTH) {
-    Deactivate();
-  }
-
+  // XXX hack until handle release is actually called in nsframe.
+  if (aEvent->message == NS_MOUSE_EXIT_SYNTH ||
+      aEvent->message == NS_MOUSE_BUTTON_UP)
+     HandleRelease(aPresContext, aEvent, aEventStatus);
+  
   // if we didn't handle the press ourselves, pass it on to the superclass
   if (!HandleButtonPress(aPresContext, aEvent, aEventStatus))
     return nsButtonBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   return NS_OK;
 }
 
 
 PRBool
@@ -185,25 +186,20 @@ nsScrollbarButtonFrame::HandleButtonPres
   return PR_TRUE;
 }
 
 NS_IMETHODIMP 
 nsScrollbarButtonFrame::HandleRelease(nsPresContext* aPresContext, 
                                       nsGUIEvent*     aEvent,
                                       nsEventStatus*  aEventStatus)
 {
-  Deactivate();
-  return nsButtonBoxFrame::HandleRelease(aPresContext, aEvent, aEventStatus);
-}
-
-void
-nsScrollbarButtonFrame::Deactivate()
-{
+  // we're not active anymore
   mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, PR_TRUE);
   StopRepeat();
+  return NS_OK;
 }
 
 void nsScrollbarButtonFrame::Notify()
 {
   // Since this is only going to get called if we're scrolling a page length
   // or a line increment, we will always use smooth scrolling.
   DoButtonAction(PR_TRUE);
 }
--- a/layout/xul/base/src/nsScrollbarButtonFrame.h
+++ b/layout/xul/base/src/nsScrollbarButtonFrame.h
@@ -76,16 +76,20 @@ public:
                          nsGUIEvent *    aEvent,
                          nsEventStatus*  aEventStatus);
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  nsGUIEvent *    aEvent,
                                  nsEventStatus*  aEventStatus,
                                  PRBool aControlHeld)  { return NS_OK; }
 
+  NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
+                        nsGUIEvent *    aEvent,
+                        nsEventStatus*  aEventStatus) { return NS_OK; }
+
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            nsGUIEvent *    aEvent,
                            nsEventStatus*  aEventStatus);
 
 protected:
   virtual void MouseClicked(nsPresContext* aPresContext, nsGUIEvent* aEvent);
   void DoButtonAction(PRBool aSmoothScroll);
 
@@ -96,13 +100,11 @@ protected:
     nsRepeatService::GetInstance()->Stop(Notify, this);
   }
   void Notify();
   static void Notify(void* aData) {
     static_cast<nsScrollbarButtonFrame*>(aData)->Notify();
   }
   
   PRInt32 mIncrement;  
-
-  void Deactivate();
 };
 
 #endif
--- a/layout/xul/base/src/nsScrollbarFrame.cpp
+++ b/layout/xul/base/src/nsScrollbarFrame.cpp
@@ -149,16 +149,32 @@ NS_IMETHODIMP
 nsScrollbarFrame::HandleMultiplePress(nsPresContext* aPresContext, 
                                       nsGUIEvent*     aEvent,
                                       nsEventStatus*  aEventStatus,
                                       PRBool aControlHeld)
 {
   return NS_OK;
 }
 
+NS_IMETHODIMP 
+nsScrollbarFrame::HandleDrag(nsPresContext* aPresContext, 
+                              nsGUIEvent*     aEvent,
+                              nsEventStatus*  aEventStatus)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsScrollbarFrame::HandleRelease(nsPresContext* aPresContext, 
+                                 nsGUIEvent*     aEvent,
+                                 nsEventStatus*  aEventStatus)
+{
+  return NS_OK;
+}
+
 void
 nsScrollbarFrame::SetScrollbarMediatorContent(nsIContent* aMediator)
 {
   mScrollbarMediator = aMediator;
 }
 
 nsIScrollbarMediator*
 nsScrollbarFrame::GetScrollbarMediator()
--- a/layout/xul/base/src/nsScrollbarFrame.h
+++ b/layout/xul/base/src/nsScrollbarFrame.h
@@ -74,16 +74,24 @@ public:
                          nsGUIEvent *    aEvent,
                          nsEventStatus*  aEventStatus);
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  nsGUIEvent *    aEvent,
                                  nsEventStatus*  aEventStatus,
                                  PRBool aControlHeld);
 
+  NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
+                        nsGUIEvent *    aEvent,
+                        nsEventStatus*  aEventStatus);
+
+  NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
+                           nsGUIEvent *    aEvent,
+                           nsEventStatus*  aEventStatus);
+
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
--- a/layout/xul/base/src/nsSliderFrame.cpp
+++ b/layout/xul/base/src/nsSliderFrame.cpp
@@ -582,29 +582,18 @@ nsSliderFrame::HandleEvent(nsPresContext
 
     mDragStart = pos - mThumbStart;
   }
 
   // XXX hack until handle release is actually called in nsframe.
 //  if (aEvent->message == NS_MOUSE_EXIT_SYNTH || aEvent->message == NS_MOUSE_RIGHT_BUTTON_UP || aEvent->message == NS_MOUSE_LEFT_BUTTON_UP)
   //   HandleRelease(aPresContext, aEvent, aEventStatus);
 
-  if (aEvent->message == NS_MOUSE_EXIT_SYNTH && mChange) {
-    // XXX This is wrong behavior. We shouldn't stop handling dragging by
-    // mouseexit event.
-    // On Windows, can continue to change the value when mouse cursor is
-    // moved back to the slider button.
-    // On Linux (GTK), even if the mouse cursor existed from slider button,
-    // kept to change the value. (confirmed on Ubuntu 10.10)
-    // On Mac, like Windows, when mouse cursor is moved back to the button,
-    // restart to change the value.  However, Mac's slider can use the other
-    // direction button too.
-    HandleRelease(aPresContext, aEvent, aEventStatus);
-    nsIPresShell::SetCapturingContent(nsnull, 0);
-  }
+  if (aEvent->message == NS_MOUSE_EXIT_SYNTH && mChange)
+     HandleRelease(aPresContext, aEvent, aEventStatus);
 
   return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
 }
 
 // Helper function to collect the "scroll to click" metric. Beware of
 // caching this, users expect to be able to change the system preference
 // and see the browser change its behavior immediately.
 PRBool
@@ -1050,17 +1039,17 @@ nsSliderFrame::HandlePress(nsPresContext
 
 NS_IMETHODIMP
 nsSliderFrame::HandleRelease(nsPresContext* aPresContext,
                                  nsGUIEvent*     aEvent,
                                  nsEventStatus*  aEventStatus)
 {
   StopRepeat();
 
-  return nsBoxFrame::HandleRelease(aPresContext, aEvent, aEventStatus);
+  return NS_OK;
 }
 
 void
 nsSliderFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   // tell our mediator if we have one we are gone.
   if (mMediator) {
     mMediator->SetSlider(nsnull);
--- a/layout/xul/base/src/nsSliderFrame.h
+++ b/layout/xul/base/src/nsSliderFrame.h
@@ -143,16 +143,20 @@ public:
                          nsGUIEvent *    aEvent,
                          nsEventStatus*  aEventStatus);
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  nsGUIEvent *    aEvent,
                                  nsEventStatus*  aEventStatus,
                                  PRBool aControlHeld)  { return NS_OK; }
 
+  NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
+                        nsGUIEvent *    aEvent,
+                        nsEventStatus*  aEventStatus)  { return NS_OK; }
+
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            nsGUIEvent *    aEvent,
                            nsEventStatus*  aEventStatus);
 
 private:
 
   PRBool GetScrollToClick();
   nsIBox* GetScrollbar();
--- a/layout/xul/base/src/nsSplitterFrame.cpp
+++ b/layout/xul/base/src/nsSplitterFrame.cpp
@@ -376,16 +376,32 @@ nsSplitterFrame::HandleMultiplePress(nsP
                                      nsGUIEvent *    aEvent,
                                      nsEventStatus*  aEventStatus,
                                      PRBool aControlHeld)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSplitterFrame::HandleDrag(nsPresContext* aPresContext,
+                        nsGUIEvent *    aEvent,
+                        nsEventStatus*  aEventStatus)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSplitterFrame::HandleRelease(nsPresContext* aPresContext,
+                           nsGUIEvent *    aEvent,
+                           nsEventStatus*  aEventStatus)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                   const nsRect&           aDirtyRect,
                                   const nsDisplayListSet& aLists)
 {
   nsresult rv = nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
   
   // if the mouse is captured always return us as the frame.
--- a/layout/xul/base/src/nsSplitterFrame.h
+++ b/layout/xul/base/src/nsSplitterFrame.h
@@ -81,16 +81,24 @@ public:
                          nsGUIEvent *    aEvent,
                          nsEventStatus*  aEventStatus);
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  nsGUIEvent *    aEvent,
                                  nsEventStatus*  aEventStatus,
                                  PRBool aControlHeld);
 
+  NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
+                        nsGUIEvent *    aEvent,
+                        nsEventStatus*  aEventStatus);
+
+  NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
+                           nsGUIEvent *    aEvent,
+                           nsEventStatus*  aEventStatus);
+
   NS_IMETHOD HandleEvent(nsPresContext* aPresContext, 
                          nsGUIEvent* aEvent,
                          nsEventStatus* aEventStatus);
 
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists);
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1348,30 +1348,16 @@ pref("layout.word_select.stop_at_punctua
 // 1 = caret moves and blinks as when there is no selection; word
 //     delete deselects the selection and then deletes word (Windows default)
 // 2 = caret moves to selection edge and is not visible during selection; 
 //     word delete deletes the selection (Mac default)
 // 3 = caret moves and blinks as when there is no selection; word delete
 //     deletes the selection (Unix default)
 pref("layout.selection.caret_style", 0);
 
-// Prefs for auto scrolling by mouse drag.  When the mouse cursor is on edge of
-// scrollable frame which is a selection root or its descendant, the frame will
-// be scrolled.
-// |.edge_width| defines the edge width by device pixels.
-// |.edge_scroll_amount| defines the scrolling speed by device pixels.
-// The auto scroll implementation uses this value for scrolling-to computation.
-// When the mouse cursor is on the edge, it tries to scroll the frame to
-// this pixels away from the edge.
-// I.e., larger value makes faster scroll.
-// And also this value is used for the minimum scrolling speed when mouse cursor
-// is outside of the selection root element.
-pref("layout.selection.drag.autoscroll.edge_width", 32);
-pref("layout.selection.drag.autoscroll.edge_scroll_amount", 8);
-
 // pref to control whether or not to replace backslashes with Yen signs
 // in documents encoded in one of Japanese legacy encodings (EUC-JP, 
 // Shift_JIS, ISO-2022-JP)
 pref("layout.enable_japanese_specific_transform", false);
 
 // pref to force frames to be resizable
 pref("layout.frames.force_resizability", false);
 
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -215,23 +215,16 @@ function synthesizeMouse(aTarget, aOffse
     var clickCount = aEvent.clickCount || 1;
     var modifiers = _parseModifiers(aEvent);
 
     var rect = aTarget.getBoundingClientRect();
 
     var left = rect.left + aOffsetX;
     var top = rect.top + aOffsetY;
 
-    // body's bounding client rect depends its scroll position.
-    var body = aTarget.ownerDocument.body;
-    if (body == aTarget) {
-      left += aTarget.scrollLeft;
-      top += aTarget.scrollTop;
-    }
-
     if (aEvent.type) {
       utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
     }
     else {
       utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers);
       utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers);
     }
   }
@@ -284,35 +277,29 @@ function synthesizeMouseScroll(aTarget, 
     const kHasPixels = 0x08;
     const kIsMomentum = 0x40;
 
     var button = aEvent.button || 0;
     var modifiers = _parseModifiers(aEvent);
 
     var rect = aTarget.getBoundingClientRect();
 
-    var left = rect.left + aOffsetX;
-    var top = rect.top + aOffsetY;
-
-    // body's bounding client rect depends its scroll position.
-    if (aTarget.ownerDocument.body == aTarget) {
-      left += aTarget.scrollLeft;
-      top += aTarget.scrollTop;
-    }
+    var left = rect.left;
+    var top = rect.top;
 
     var type = aEvent.type || "DOMMouseScroll";
     var axis = aEvent.axis || "vertical";
     var scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical;
     if (aEvent.hasPixels) {
       scrollFlags |= kHasPixels;
     }
     if (aEvent.isMomentum) {
       scrollFlags |= kIsMomentum;
     }
-    utils.sendMouseScrollEvent(type, left, top, button,
+    utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button,
                                scrollFlags, aEvent.delta, modifiers);
   }
 }
 
 function _computeKeyCodeFromChar(aChar)
 {
   if (aChar.length != 1) {
     return 0;