From 9dcaa2e4e58eef5196e1b64767b1e1f6898e943c Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Mon, 28 Sep 2009 10:17:01 -0400 Subject: [PATCH] Bug 516615, when capturing, check if the target frame's content is a descendant of the capturing content, rather than using the frame tree, r=roc,sr=smaug --- layout/base/nsPresShell.cpp | 83 ++++++++++--------- layout/generic/nsFrameSetFrame.cpp | 7 ++ .../tests/widgets/test_mousecapture.xul | 24 +++++- 3 files changed, 73 insertions(+), 41 deletions(-) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 136675643ee5..3a5755947142 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6000,6 +6000,9 @@ PresShell::HandleEvent(nsIView *aView, } #endif + nsIContent* capturingContent = + NS_IS_MOUSE_EVENT(aEvent) ? GetCapturingContent() : nsnull; + nsCOMPtr retargetEventDoc; // key and IME events must be targeted at the presshell for the focused frame if (!sDontRetargetEvents) { @@ -6020,10 +6023,10 @@ PresShell::HandleEvent(nsIView *aView, retargetEventDoc = do_QueryInterface(piWindow->GetExtantDocument()); if (!retargetEventDoc) return NS_OK; - } else if (NS_IS_MOUSE_EVENT(aEvent) && GetCapturingContent()) { + } else if (capturingContent) { // if the mouse is being captured then retarget the mouse event at the // document that is being captured. - retargetEventDoc = gCaptureInfo.mContent->GetCurrentDoc(); + retargetEventDoc = capturingContent->GetCurrentDoc(); } if (retargetEventDoc) { @@ -6093,43 +6096,7 @@ PresShell::HandleEvent(nsIView *aView, return NS_OK; } - PRBool getDescendantPoint = PR_TRUE; nsIFrame* frame = static_cast(aView->GetClientData()); - - if (NS_IS_MOUSE_EVENT(aEvent) && GetCapturingContent()) { - // if a node is capturing the mouse, get the frame for the capturing - // content and use that instead. However, if the content has no parent, - // such as the root frame, get the parent canvas frame instead. This - // ensures that positioned frames are included when hit-testing. Note - // that a check was already done above to ensure that capturingContent - // is in this presshell. - nsIContent* capturingContent = gCaptureInfo.mContent; - frame = GetPrimaryFrameFor(capturingContent); - if (frame) { - getDescendantPoint = !gCaptureInfo.mRetargetToElement; - if (!capturingContent->GetParent()) { - frame = frame->GetParent(); - } - else { - // special case for as it needs to capture on the dropdown list, + // so get the frame for the dropdown list instead. + if (!captureRetarget && capturingContent->Tag() == nsGkAtoms::select && + capturingContent->IsNodeOfType(nsINode::eHTML)) { + nsIFrame* selectFrame = GetPrimaryFrameFor(capturingContent); + if (selectFrame) { + nsIFrame* childframe = selectFrame->GetChildList(nsGkAtoms::selectPopupList).FirstChild(); + if (childframe) { + frame = childframe; + } + } + } + } + + // Get the frame at the event point. However, don't do this if we're + // capturing and retargeting the event because the captured frame will + // be used instead below. nsIFrame* targetFrame = nsnull; - if (getDescendantPoint) { + if (!captureRetarget) { nsPoint eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame); { @@ -6197,6 +6184,24 @@ PresShell::HandleEvent(nsIView *aView, } } + // if a node is capturing the mouse, check if the event needs to be + // retargeted at the capturing content instead. This will be the case when + // capture retargeting is being used, no frame was found or the frame's + // content is not a descendant of the capturing content. + if (capturingContent && + (gCaptureInfo.mRetargetToElement || + !targetFrame || !targetFrame->GetContent() || + !nsContentUtils::ContentIsCrossDocDescendantOf(targetFrame->GetContent(), + capturingContent))) { + // A check was already done above to ensure that capturingContent is + // in this presshell, so GetPrimaryFrameFor can just be called directly. + nsIFrame* capturingFrame = GetPrimaryFrameFor(capturingContent); + if (capturingFrame) { + targetFrame = capturingFrame; + aView = targetFrame->GetClosestView(); + } + } + if (targetFrame) { PresShell* shell = static_cast(targetFrame->PresContext()->PresShell()); diff --git a/layout/generic/nsFrameSetFrame.cpp b/layout/generic/nsFrameSetFrame.cpp index e1cc965d8a25..03d85104357b 100644 --- a/layout/generic/nsFrameSetFrame.cpp +++ b/layout/generic/nsFrameSetFrame.cpp @@ -1458,6 +1458,13 @@ void nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext, nsGUIEvent* aEvent) { + // if the capture ended, reset the drag state + if (nsIPresShell::GetCapturingContent() != GetContent()) { + mDragger = nsnull; + gDragInProgress = PR_FALSE; + return; + } + PRInt32 change; // measured positive from left-to-right or top-to-bottom nsWeakFrame weakFrame(this); if (mDragger->mVertical) { diff --git a/toolkit/content/tests/widgets/test_mousecapture.xul b/toolkit/content/tests/widgets/test_mousecapture.xul index 539679eb416a..0a1005e032c9 100644 --- a/toolkit/content/tests/widgets/test_mousecapture.xul +++ b/toolkit/content/tests/widgets/test_mousecapture.xul @@ -124,9 +124,27 @@ function runTests() runCaptureTest(custom5); captureRetargetMode = false; + var custom6 = document.getElementById("custom6"); + synthesizeMouse(custom6, 2, 2, { type: "mousedown" }); + synthesizeMouseExpectEvent($("leftbox"), 2, 2, { type: "mousemove" }, + $("leftbox"), "mousemove", "setCapture only works on elements in documents"); + synthesizeMouse(custom6, 2, 2, { type: "mouseup" }); + var b = frames[0].document.getElementById("b"); runCaptureTest(b, selectionCallback); + frames[0].getSelection().collapseToStart(); + + var body = frames[0].document.body; + var fixed = frames[0].document.getElementById("fixed"); + function captureOnBody() { body.setCapture() } + body.addEventListener("mousedown", captureOnBody, true); + synthesizeMouse(body, 8, 8, { type: "mousedown" }, frames[0]); + body.removeEventListener("mousedown", captureOnBody, true); + synthesizeMouseExpectEvent(fixed, 2, 2, { type: "mousemove" }, + fixed, "mousemove", "setCapture on body retargets to root node", frames[0]); + synthesizeMouse(body, 8, 8, { type: "mouseup" }, frames[0]); + previousWidth = frames[1].frames[0].document.documentElement.clientWidth; originalWidth = previousWidth; runCaptureTest(frames[1].document.documentElement.lastChild, framesetCallback); @@ -220,16 +238,18 @@ SimpleTest.waitForFocus(runTests); +