Bug 387659, popup coordinates are not set within popupshowing event,r=eli,sr=bz

This commit is contained in:
enndeakin@sympatico.ca 2007-07-17 05:21:53 -07:00
parent 172d985b89
commit 8b63ae1bc7
4 changed files with 133 additions and 7 deletions

View File

@ -46,6 +46,7 @@
#include "nsIRollupListener.h"
#include "nsIMenuRollup.h"
#include "nsIDOMKeyListener.h"
#include "nsPoint.h"
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "nsITimer.h"
@ -359,8 +360,9 @@ public:
// This is used by the implementation of nsIDOMXULDocument::GetPopupRangeParent
// and nsIDOMXULDocument::GetPopupRangeOffset.
void GetMouseLocation(nsIDOMNode** aNode, PRInt32* aOffset);
// set the mouse event that was used to activate the next popup to be opened.
void SetMouseLocation(nsIDOMEvent* aEvent);
// set the mouse event that was used to activate the next popup, specified by
// aPopup, to be opened.
void SetMouseLocation(nsIDOMEvent* aEvent, nsIContent* aPopup);
/**
* Open a <menu> given its content node. If aSelectFirstItem is
@ -626,6 +628,7 @@ protected:
// range parent and offset set in SetMouseLocation
nsCOMPtr<nsIDOMNode> mRangeParent;
PRInt32 mRangeOffset;
nsPoint mCachedMousePoint;
// set to the currently active menu bar, if any
nsMenuBarFrame* mActiveMenuBar;

View File

@ -45,6 +45,7 @@
#include "nsIDOMDocument.h"
#include "nsIDOMNSEvent.h"
#include "nsIDOMNSUIEvent.h"
#include "nsIPrivateDOMEvent.h"
#include "nsEventDispatcher.h"
#include "nsCSSFrameConstructor.h"
#include "nsLayoutUtils.h"
@ -236,13 +237,40 @@ nsXULPopupManager::GetMouseLocation(nsIDOMNode** aNode, PRInt32* aOffset)
}
void
nsXULPopupManager::SetMouseLocation(nsIDOMEvent* aEvent)
nsXULPopupManager::SetMouseLocation(nsIDOMEvent* aEvent, nsIContent* aPopup)
{
mCachedMousePoint = nsPoint(0, 0);
nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(aEvent);
NS_ASSERTION(uiEvent, "Expected an nsIDOMNSUIEvent");
NS_ASSERTION(!aEvent || uiEvent, "Expected an nsIDOMNSUIEvent");
if (uiEvent) {
uiEvent->GetRangeParent(getter_AddRefs(mRangeParent));
uiEvent->GetRangeOffset(&mRangeOffset);
// get the event coordinates relative to the root frame of the document
// containing the popup.
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aEvent));
if (privateEvent) {
NS_ASSERTION(aPopup, "Expected a popup node");
nsEvent* event;
nsresult rv = privateEvent->GetInternalNSEvent(&event);
if (NS_SUCCEEDED(rv) && event) {
nsIDocument* doc = aPopup->GetCurrentDoc();
if (doc) {
nsIPresShell* presShell = doc->GetPrimaryShell();
if (presShell) {
nsPresContext* presContext = presShell->GetPresContext();
nsIFrame* rootFrame = presShell->GetRootFrame();
if (rootFrame && presContext) {
nsPoint pnt =
nsLayoutUtils::GetEventCoordinatesRelativeTo(event, rootFrame);
mCachedMousePoint = nsPoint(presContext->AppUnitsToDevPixels(pnt.x),
presContext->AppUnitsToDevPixels(pnt.y));
}
}
}
}
}
}
else {
mRangeParent = nsnull;
@ -293,6 +321,7 @@ nsXULPopupManager::ShowMenu(nsIContent *aMenu,
popupFrame->InitializePopup(aMenu, position, 0, 0, PR_TRUE);
if (aAsynchronous) {
SetMouseLocation(nsnull, nsnull);
nsCOMPtr<nsIRunnable> event =
new nsXULPopupShowingEvent(popupFrame->GetContent(), aMenu,
parentIsContextMenu, aSelectFirstItem);
@ -539,7 +568,7 @@ nsXULPopupManager::HidePopupCallback(nsIContent* aPopup,
// send the popuphidden event synchronously. This event has no default behaviour.
nsEventStatus status = nsEventStatus_eIgnore;
nsEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN);
nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN, nsnull, nsMouseEvent::eReal);
nsEventDispatcher::Dispatch(aPopup, aPopupFrame->PresContext(),
&event, nsnull, &status);
@ -678,8 +707,13 @@ nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup,
// is where those details would be retrieved. This removes the need for
// all the globals people keep adding to nsIDOMXULDocument.
nsEventStatus status = nsEventStatus_eIgnore;
nsEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING);
nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull, nsMouseEvent::eReal);
nsPoint pnt;
event.widget = presShell->GetRootFrame()->
GetClosestView()->GetNearestWidget(&pnt);
event.refPoint = mCachedMousePoint;
nsEventDispatcher::Dispatch(aPopup, aPresContext, &event, nsnull, &status);
mCachedMousePoint = nsPoint(0, 0);
// it is common to append content to the menu during the popupshowing event.
// Flush the notifications so that the frames are up to date before showing
@ -712,7 +746,7 @@ nsXULPopupManager::FirePopupHidingEvent(nsIContent* aPopup,
nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
nsEventStatus status = nsEventStatus_eIgnore;
nsEvent event(PR_TRUE, NS_XUL_POPUP_HIDING);
nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDING, nsnull, nsMouseEvent::eReal);
nsEventDispatcher::Dispatch(aPopup, aPresContext, &event, nsnull, &status);
// get frame again in case it went away

View File

@ -48,6 +48,7 @@ _TEST_FILES = test_bug360220.xul \
test_bug359754.xul \
test_bug365773.xul \
test_colorpicker_popup.xul \
test_popup_coords.xul \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,88 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
<window title="Popup Coordinate Tests"
onload="setTimeout(openThePopup, 0, 'outer');"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<title>Popup Tests</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<deck style="margin-top: 5px; padding-top: 5px;">
<label id="outer" popup="outerpopup" value="Popup"/>
</deck>
<panel id="outerpopup"
onpopupshowing="popupShowingEventOccured(event);"
onpopupshown="eventOccured(event); openThePopup('inner')"
onpopuphiding="eventOccured(event);"
onpopuphidden="eventOccured(event); SimpleTest.finish();">
<button id="item1" label="First"/>
<label id="inner" value="Second" popup="innerpopup"/>
<button id="item2" label="Third"/>
</panel>
<menupopup id="innerpopup"
onpopupshowing="popupShowingEventOccured(event);"
onpopupshown="eventOccured(event); event.target.hidePopup();"
onpopuphiding="eventOccured(event);"
onpopuphidden="eventOccured(event); document.getElementById('outerpopup').hidePopup();">
<menuitem id="inner1" label="Inner First"/>
<menuitem id="inner2" label="Inner Second"/>
</menupopup>
<script>
SimpleTest.waitForExplicitFinish();
function openThePopup(id)
{
if (id == "inner")
document.getElementById("item1").focus();
var trigger = document.getElementById(id);
synthesizeMouse(trigger, 4, 5, { });
}
function eventOccured(event)
{
var testname = event.type + " on " + event.target.id + " ";
ok(event instanceof MouseEvent, testname + "is a mouse event");
is(event.clientX, 0, testname + "clientX");
is(event.clientY, 0, testname + "clientY");
is(event.rangeParent, null, testname + "rangeParent");
is(event.rangeOffset, 0, testname + "rangeOffset");
}
function popupShowingEventOccured(event)
{
// the popupshowing event should have the event coordinates and
// range position filled in.
var testname = "popupshowing on " + event.target.id + " ";
ok(event instanceof MouseEvent, testname + "is a mouse event");
var trigger = document.getElementById(event.target.id == "outerpopup" ? "outer" : "inner");
var rect = trigger.getBoundingClientRect();
is(event.clientX, Math.round(rect.left) + 4, testname + "clientX");
is(event.clientY, Math.round(rect.top) + 5, testname + "clientY");
// rangeOffset should be just after the trigger element. As rangeOffset
// considers the zeroth position to be before the first element, the value
// should be one higher than its index within its parent.
is(event.rangeParent, trigger.parentNode, testname + "rangeParent");
is(event.rangeOffset, Array.indexOf(trigger.parentNode.childNodes, trigger) + 1, testname + "rangeOffset");
}
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display">
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</window>