Bug 255378. Make the click-hold context menu code keep content references, not frame references, just as the drag gesture code does, so that it's robust to frame reconstruction during the click-hold time interval. r+sr=bzbarsky,a=asa

This commit is contained in:
roc+%cs.cmu.edu 2005-04-19 23:03:23 +00:00
parent 436d07b910
commit 3135638698
2 changed files with 82 additions and 88 deletions

View File

@ -193,12 +193,6 @@ nsEventStateManager::nsEventStateManager()
mTabbedThroughDocument(PR_FALSE),
mDOMEventLevel(0),
mAccessKeys(nsnull)
#ifdef CLICK_HOLD_CONTEXT_MENUS
,
mEventDownFrame(nsnull),
mEventDownWidget(nsnull),
mEventPresContext(nsnull)
#endif
{
++sESMInstanceCount;
}
@ -268,10 +262,7 @@ nsEventStateManager::Init()
nsEventStateManager::~nsEventStateManager()
{
#if CLICK_HOLD_CONTEXT_MENUS
if ( mClickHoldTimer ) {
mClickHoldTimer->Cancel();
mClickHoldTimer = nsnull;
}
KillClickHoldTimer();
#endif
--sESMInstanceCount;
@ -427,7 +418,7 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
switch (aEvent->message) {
case NS_MOUSE_LEFT_BUTTON_DOWN:
#ifndef XP_OS2
BeginTrackingDragGesture ( aPresContext, (nsGUIEvent*)aEvent, aTargetFrame );
BeginTrackingDragGesture ( aPresContext, (nsMouseEvent*)aEvent, aTargetFrame );
#endif
mLClickCount = ((nsMouseEvent*)aEvent)->clickCount;
SetClickCount(aPresContext, (nsMouseEvent*)aEvent, aStatus);
@ -439,7 +430,7 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
break;
case NS_MOUSE_RIGHT_BUTTON_DOWN:
#ifdef XP_OS2
BeginTrackingDragGesture ( aPresContext, (nsGUIEvent*)aEvent, aTargetFrame );
BeginTrackingDragGesture ( aPresContext, (nsMouseEvent*)aEvent, aTargetFrame );
#endif
mRClickCount = ((nsMouseEvent*)aEvent)->clickCount;
SetClickCount(aPresContext, (nsMouseEvent*)aEvent, aStatus);
@ -482,7 +473,7 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
// that ClearFrameRefs() has been called and it cleared out
// |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
// into UpdateCursor().
GenerateDragGesture(aPresContext, (nsGUIEvent*)aEvent);
GenerateDragGesture(aPresContext, (nsMouseEvent*)aEvent);
UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
GenerateMouseEnterExit((nsGUIEvent*)aEvent);
break;
@ -1151,14 +1142,6 @@ nsEventStateManager::CreateClickHoldTimer(nsPresContext* inPresContext,
mClickHoldTimer->InitWithFuncCallback(sClickHoldCallback, this,
kClickHoldDelay,
nsITimer::TYPE_ONE_SHOT);
mEventPoint = inMouseDownEvent->point;
mEventRefPoint = inMouseDownEvent->refPoint;
mEventDownFrame = inDownFrame;
mEventDownWidget = inMouseDownEvent->widget;
mEventPresContext = inPresContext;
} // CreateClickHoldTimer
@ -1174,12 +1157,7 @@ nsEventStateManager::KillClickHoldTimer()
mClickHoldTimer->Cancel();
mClickHoldTimer = nsnull;
}
mEventDownFrame = nsnull;
mEventDownWidget = nsnull;
mEventPresContext = nsnull;
} // KillTooltipTimer
}
//
@ -1216,7 +1194,7 @@ nsEventStateManager::sClickHoldCallback(nsITimer *aTimer, void* aESM)
void
nsEventStateManager::FireContextClick()
{
if ( !mEventDownWidget || !mEventPresContext )
if ( !mGestureDownContent )
return;
#if defined (XP_MAC) || defined(XP_MACOSX)
@ -1228,11 +1206,6 @@ nsEventStateManager::FireContextClick()
#endif
nsEventStatus status = nsEventStatus_eIgnore;
nsMouseEvent event(NS_CONTEXTMENU, mEventDownWidget);
event.clickCount = 1;
event.point = mEventPoint;
event.refPoint = mEventRefPoint;
event.internalAppFlags |= NS_APP_EVENT_FLAG_TRUSTED;
// Dispatch to the DOM. We have to fake out the ESM and tell it that the
// current target frame is actually where the mouseDown occurred, otherwise it
@ -1242,11 +1215,11 @@ nsEventStateManager::FireContextClick()
// event and it will get reset on the very next event to the correct frame).
mCurrentTarget = nsnull;
if ( mGestureDownContent ) {
mEventPresContext->GetPresShell()->GetPrimaryFrameFor(mGestureDownContent,
&mCurrentTarget);
mPresContext->GetPresShell()->GetPrimaryFrameFor(mGestureDownContent,
&mCurrentTarget);
if ( mCurrentTarget ) {
NS_ASSERTION(mEventPresContext == mCurrentTarget->GetPresContext(),
NS_ASSERTION(mPresContext == mCurrentTarget->GetPresContext(),
"a prescontext returned a primary frame that didn't belong to it?");
// before dispatching, check that we're not on something that
@ -1292,7 +1265,7 @@ nsEventStateManager::FireContextClick()
if (allowedToDispatch) {
// stop selection tracking, we're in control now
nsCOMPtr<nsIFrameSelection> frameSel;
GetSelection(mCurrentTarget, mEventPresContext, getter_AddRefs(frameSel));
GetSelection(mCurrentTarget, mPresContext, getter_AddRefs(frameSel));
if (frameSel) {
PRBool mouseDownState = PR_TRUE;
frameSel->GetMouseDownState(&mouseDownState);
@ -1300,8 +1273,12 @@ nsEventStateManager::FireContextClick()
frameSel->SetMouseDownState(PR_FALSE);
}
nsMouseEvent event(NS_CONTEXTMENU, mCurrentTarget->GetWindow());
event.clickCount = 1;
FillInEventFromGestureDown(&event, PR_TRUE);
// dispatch to DOM
mGestureDownContent->HandleDOMEvent(mEventPresContext, &event, nsnull,
mGestureDownContent->HandleDOMEvent(mPresContext, &event, nsnull,
NS_EVENT_FLAG_INIT, &status);
// We don't need to dispatch to frame handling because no frames
@ -1337,7 +1314,7 @@ nsEventStateManager::FireContextClick()
//
void
nsEventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext,
nsGUIEvent* inDownEvent,
nsMouseEvent* inDownEvent,
nsIFrame* inDownFrame)
{
// Note that |inDownEvent| could be either a mouse down event or a
@ -1350,6 +1327,11 @@ nsEventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext,
inDownFrame->GetContentForEvent(aPresContext, inDownEvent,
getter_AddRefs(mGestureDownContent));
mGestureDownShift = inDownEvent->isShift;
mGestureDownControl = inDownEvent->isControl;
mGestureDownAlt = inDownEvent->isAlt;
mGestureDownMeta = inDownEvent->isMeta;
#ifdef CLICK_HOLD_CONTEXT_MENUS
// fire off a timer to track click-hold
CreateClickHoldTimer ( aPresContext, inDownFrame, inDownEvent );
@ -1405,6 +1387,48 @@ nsEventStateManager::GetSelection(nsIFrame* inFrame,
} // GetSelection
void
nsEventStateManager::FillInEventFromGestureDown(nsMouseEvent* aEvent,
PRBool aIsTrusted)
{
NS_ASSERTION(aEvent->widget == mCurrentTarget->GetWindow(),
"Incorrect widget in event");
// Set the coordinates in the new event to the coordinates of
// the old event, adjusted for the fact that the widget might be
// different
nsRect tmpRect(0, 0, 1, 1);
aEvent->widget->WidgetToScreen(tmpRect, tmpRect);
aEvent->refPoint = mGestureDownPoint - tmpRect.TopLeft();
float pixelsToTwips;
pixelsToTwips = mPresContext->DeviceContext()->DevUnitsToTwips();
nsPoint refPointTwips(NSIntPixelsToTwips(aEvent->refPoint.x, pixelsToTwips),
NSIntPixelsToTwips(aEvent->refPoint.y, pixelsToTwips));
nsIView* widgetView = mCurrentTarget->GetClosestView();
nsPoint widgetToView;
#ifdef DEBUG
nsIWidget* theWidget =
#endif
widgetView->GetNearestWidget(&widgetToView);
NS_ASSERTION(theWidget == aEvent->widget, "Widget confusion!");
nsPoint widgetViewPoint = refPointTwips + widgetToView;
nsPoint targetToView;
nsIView* view;
mCurrentTarget->GetOffsetFromView(targetToView, &view);
aEvent->point = widgetViewPoint + widgetView->GetOffsetTo(view);
aEvent->isShift = mGestureDownShift;
aEvent->isControl = mGestureDownControl;
aEvent->isAlt = mGestureDownAlt;
aEvent->isMeta = mGestureDownMeta;
if (aIsTrusted) {
aEvent->internalAppFlags |= NS_APP_EVENT_FLAG_TRUSTED;
}
}
//
// GenerateDragGesture
@ -1422,7 +1446,7 @@ nsEventStateManager::GetSelection(nsIFrame* inFrame,
//
void
nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
nsGUIEvent *aEvent)
nsMouseEvent *aEvent)
{
NS_WARN_IF_FALSE(aPresContext, "This shouldn't happen.");
if ( IsTrackingDragGesture() ) {
@ -1459,12 +1483,6 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
pixelThresholdY = 5;
}
// figure out the delta in twips, since that is how it is in the event.
// Do we need to do this conversion every time?
// Will the pres context really change on us or can we cache it?
float pixelsToTwips;
pixelsToTwips = aPresContext->DeviceContext()->DevUnitsToTwips();
// fire drag gesture if mouse has moved enough
nsRect tmpRect;
aEvent->widget->WidgetToScreen(nsRect(aEvent->refPoint, nsSize(1, 1)),
@ -1486,32 +1504,8 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
// get the widget from the target frame
nsEventStatus status = nsEventStatus_eIgnore;
nsMouseEvent event(NS_DRAGDROP_GESTURE, mCurrentTarget->GetWindow());
// Set the coordinates in the new event to the coordinates of
// the old event, adjusted for the fact that the widget might be
// different
tmpRect = nsRect(0, 0, 1, 1);
event.widget->WidgetToScreen(tmpRect, tmpRect);
event.refPoint = mGestureDownPoint - tmpRect.TopLeft();
nsPoint refPointTwips(NSIntPixelsToTwips(event.refPoint.x, pixelsToTwips),
NSIntPixelsToTwips(event.refPoint.y, pixelsToTwips));
nsPoint targetToView;
nsIView* view = mCurrentTarget->GetClosestView(&targetToView);
nsPoint viewToWidget;
nsIWidget* widget = view->GetNearestWidget(&viewToWidget);
NS_ASSERTION(widget == event.widget, "Widget confusion");
event.point = refPointTwips - (targetToView + viewToWidget);
// ideally, we should get the modifiers from the original event too,
// but the drag code looks at modifiers at the end of the drag, so this
// is probably OK.
event.isShift = ((nsMouseEvent*)aEvent)->isShift;
event.isControl = ((nsMouseEvent*)aEvent)->isControl;
event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
event.internalAppFlags |=
aEvent->internalAppFlags & NS_APP_EVENT_FLAG_TRUSTED;
FillInEventFromGestureDown(&event,
(aEvent->internalAppFlags & NS_APP_EVENT_FLAG_TRUSTED) != 0);
// Dispatch to the DOM. By setting mCurrentTarget we are faking
// out the ESM and telling it that the current target frame is
@ -2285,13 +2279,6 @@ nsEventStateManager::ClearFrameRefs(nsIFrame* aFrame)
mLastMouseOverFrame = nsnull;
if (aFrame == mLastDragOverFrame)
mLastDragOverFrame = nsnull;
#if CLICK_HOLD_CONTEXT_MENUS
if (aFrame == mEventDownFrame) {
mEventDownFrame = nsnull;
mEventDownWidget = nsnull;
mEventPresContext = nsnull;
}
#endif
if (aFrame == mCurrentTarget) {
if (aFrame) {
mCurrentTargetContent = aFrame->GetContent();

View File

@ -242,10 +242,19 @@ protected:
// end mousewheel functions
// routines for the d&d gesture tracking state machine
void BeginTrackingDragGesture ( nsPresContext* aPresContext, nsGUIEvent* inDownEvent, nsIFrame* inDownFrame ) ;
void BeginTrackingDragGesture ( nsPresContext* aPresContext, nsMouseEvent* inDownEvent,
nsIFrame* inDownFrame ) ;
void StopTrackingDragGesture ( ) ;
void GenerateDragGesture ( nsPresContext* aPresContext, nsGUIEvent *aEvent ) ;
void GenerateDragGesture ( nsPresContext* aPresContext, nsMouseEvent *aEvent ) ;
PRBool IsTrackingDragGesture ( ) const { return mGestureDownContent != nsnull; }
/**
* Set the fields of aEvent to reflect the mouse position and modifier keys
* that were set when the user first pressed the mouse button (stored by
* BeginTrackingDragGesture). aEvent->widget must be
* mCurrentTarget->GetWindow().
* @param aIsTrusted if true, we set the trusted flag in the event
*/
void FillInEventFromGestureDown(nsMouseEvent* aEvent, PRBool aIsTrusted);
PRBool mSuppressFocusChange; // Used only for Ender text fields to suppress a focus firing on mouse down
@ -277,6 +286,11 @@ protected:
// member variables for the d&d gesture state machine
nsPoint mGestureDownPoint; // screen coordinates
nsCOMPtr<nsIContent> mGestureDownContent;
// State of keys when the original gesture-down happened
PRPackedBool mGestureDownShift;
PRPackedBool mGestureDownControl;
PRPackedBool mGestureDownAlt;
PRPackedBool mGestureDownMeta;
nsCOMPtr<nsIContent> mLastLeftMouseDownContent;
nsCOMPtr<nsIContent> mLastMiddleMouseDownContent;
@ -347,13 +361,6 @@ protected:
void FireContextClick ( ) ;
static void sClickHoldCallback ( nsITimer* aTimer, void* aESM ) ;
// stash a bunch of stuff because we're going through a timer and the event
// isn't guaranteed to be there later. We don't want to hold strong refs to
// things because we're alerted to when they are going away in ClearFrameRefs().
nsPoint mEventPoint, mEventRefPoint;
nsIFrame* mEventDownFrame;
nsIWidget* mEventDownWidget; // [WEAK]
nsPresContext* mEventPresContext; // [WEAK]
nsCOMPtr<nsITimer> mClickHoldTimer;
#endif