Bug 506304. Support synthesized mouse events being sent to windowless plugins, support reporting mouse event coordinates from the test plugin, and add tests for mouse events. r=josh

--HG--
extra : rebase_source : 5c97644a6db8454f759bbeab7a1e06295aca8325
This commit is contained in:
Robert O'Callahan 2009-08-05 13:36:37 +12:00
parent 42fa43d1a1
commit a2a72eee58
9 changed files with 286 additions and 38 deletions

View File

@ -3329,10 +3329,14 @@ nsresult nsPluginInstanceOwner::EnsureCachedAttrParamArrays()
#ifdef XP_MACOSX
static void InitializeEventRecord(EventRecord* event)
static void InitializeEventRecord(EventRecord* event, Point* aMousePosition)
{
memset(event, 0, sizeof(EventRecord));
::GetGlobalMouse(&event->where);
if (aMousePosition) {
event->where = *aMousePosition;
} else {
::GetGlobalMouse(&event->where);
}
event->when = ::TickCount();
event->modifiers = ::GetCurrentEventKeyModifiers();
}
@ -3445,7 +3449,7 @@ nsresult nsPluginInstanceOwner::ScrollPositionWillChange(nsIScrollableView* aScr
nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
if (pluginWidget && NS_SUCCEEDED(pluginWidget->StartDrawPlugin())) {
EventRecord scrollEvent;
InitializeEventRecord(&scrollEvent);
InitializeEventRecord(&scrollEvent, nsnull);
scrollEvent.what = NPEventType_ScrollingBeginsEvent;
WindowRef window = FixUpPluginWindow(ePluginPaintDisable);
@ -3468,7 +3472,7 @@ nsresult nsPluginInstanceOwner::ScrollPositionDidChange(nsIScrollableView* aScro
nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
if (pluginWidget && NS_SUCCEEDED(pluginWidget->StartDrawPlugin())) {
EventRecord scrollEvent;
InitializeEventRecord(&scrollEvent);
InitializeEventRecord(&scrollEvent, nsnull);
scrollEvent.what = NPEventType_ScrollingEndsEvent;
WindowRef window = FixUpPluginWindow(ePluginPaintEnable);
@ -4079,12 +4083,30 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent)
EventRecord carbonEvent;
void* event = anEvent.nativeMsg;
if (!event || (static_cast<EventRecord*>(event)->what == nullEvent)) {
InitializeEventRecord(&carbonEvent);
if (anEvent.message == NS_FOCUS_CONTENT || anEvent.message == NS_BLUR_CONTENT) {
carbonEvent.what = (anEvent.message == NS_FOCUS_CONTENT) ?
NPEventType_GetFocusEvent : NPEventType_LoseFocusEvent;
}
event = &carbonEvent;
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner);
nsPresContext* presContext = mOwner->PresContext();
nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x),
presContext->AppUnitsToDevPixels(pt.y));
Point carbonPt = { ptPx.y + mPluginWindow->y, ptPx.x + mPluginWindow->x };
InitializeEventRecord(&carbonEvent, &carbonPt);
switch (anEvent.message) {
case NS_FOCUS_CONTENT:
case NS_BLUR_CONTENT:
carbonEvent.what = (anEvent.message == NS_FOCUS_CONTENT) ?
NPEventType_GetFocusEvent : NPEventType_LoseFocusEvent;
break;
case NS_MOUSE_MOVE:
carbonEvent.what = osEvt;
break;
case NS_MOUSE_BUTTON_DOWN:
carbonEvent.what = mouseDown;
break;
case NS_MOUSE_BUTTON_UP:
carbonEvent.what = mouseUp;
break;
}
}
if (anEvent.message == NS_FOCUS_CONTENT) {
@ -4120,8 +4142,48 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent)
// have no nativeMsg
nsPluginEvent pluginEvent;
if (anEvent.eventStructType == NS_MOUSE_EVENT) {
// XXX we could synthesize Windows mouse events here for our
// synthetic mouse events (i.e. !pPluginEvent)
if (!pPluginEvent) {
// XXX Should extend this list to synthesize events for more event
// types
pluginEvent.event = 0;
const nsMouseEvent* mouseEvent = static_cast<const nsMouseEvent*>(&anEvent);
switch (anEvent.message) {
case NS_MOUSE_MOVE:
pluginEvent.event = WM_MOUSEMOVE;
break;
case NS_MOUSE_BUTTON_DOWN: {
static const int downMsgs[] =
{ WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN };
pluginEvent.event = downMsgs[mouseEvent->button];
break;
}
case NS_MOUSE_BUTTON_UP: {
static const int upMsgs[] =
{ WM_LBUTTONUP, WM_MBUTTONUP, WM_RBUTTONUP };
pluginEvent.event = upMsgs[mouseEvent->button];
break;
}
case NS_MOUSE_DOUBLECLICK: {
static const int dblClickMsgs[] =
{ WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK, WM_RBUTTONDBLCLK };
pluginEvent.event = dblClickMsgs[mouseEvent->button];
break;
}
default:
break;
}
if (pluginEvent.event) {
pPluginEvent = &pluginEvent;
pluginEvent.wParam =
(::GetKeyState(VK_CONTROL) ? MK_CONTROL : 0) |
(::GetKeyState(VK_SHIFT) ? MK_SHIFT : 0) |
(::GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0) |
(::GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0) |
(::GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0) |
(::GetKeyState(VK_XBUTTON1) ? MK_XBUTTON1 : 0) |
(::GetKeyState(VK_XBUTTON2) ? MK_XBUTTON2 : 0);
}
}
if (pPluginEvent) {
// Make event coordinates relative to our enclosing widget,
// not the widget they were received on.
@ -4469,7 +4531,7 @@ void nsPluginInstanceOwner::Paint()
WindowRef window = FixUpPluginWindow(ePluginPaintEnable);
if (window) {
EventRecord updateEvent;
InitializeEventRecord(&updateEvent);
InitializeEventRecord(&updateEvent, nsnull);
updateEvent.what = updateEvt;
updateEvent.message = UInt32(window);
@ -4791,7 +4853,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::Notify(nsITimer* /* timer */)
WindowRef window = FixUpPluginWindow(ePluginPaintEnable);
if (window) {
EventRecord idleEvent;
InitializeEventRecord(&idleEvent);
InitializeEventRecord(&idleEvent, nsnull);
idleEvent.what = nullEvent;
// give a bogus 'where' field of our null event when hidden, so Flash

View File

@ -91,6 +91,7 @@ _TEST_FILES = \
test_plugin_clipping2.xhtml \
test_plugin_clipping_transformed.xhtml \
test_plugin_clipping_table.xhtml \
test_plugin_mouse_coords.html \
test_plugin_position.xhtml \
test_selection_underline.html \
$(NULL)

View File

@ -0,0 +1,59 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test delivering mouse events to plugins</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<style>
embed { width:200px; height:200px; display:block; }
iframe { border:none; }
</style>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: block">
<embed id="p1" type="application/x-test"
style="position:absolute; left:300px; top:10px;"></embed></div>
<iframe id="f1" style="position:absolute; left:0; top:250px;"
src="data:text/html,&lt;embed id='p2' type='application/x-test' style='position:absolute; left:10px; top:10px'&gt;"></iframe>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
function doTest() {
var p1 = document.getElementById("p1");
synthesizeMouse(p1, 15, 18, { type:"mousedown" });
is(p1.getLastMouseX(), 15, "p1 mouse down X");
is(p1.getLastMouseY(), 18, "p1 mouse down Y");
synthesizeMouse(p1, 15, 38, { type:"mousemove" });
is(p1.getLastMouseX(), 15, "p1 mouse move X");
is(p1.getLastMouseY(), 38, "p1 mouse move Y");
synthesizeMouse(p1, 15, 28, { type:"mouseup" });
is(p1.getLastMouseX(), 15, "p1 mouse up X");
is(p1.getLastMouseY(), 28, "p1 mouse up Y");
var f1 = document.getElementById("f1");
var p2 = f1.contentDocument.getElementById("p2");
synthesizeMouse(p2, 15, 18, { type:"mousedown" }, f1.contentWindow);
is(p2.getLastMouseX(), 15, "p2 mouse down X");
is(p2.getLastMouseY(), 18, "p2 mouse down Y");
synthesizeMouse(p2, 15, 38, { type:"mousemove" }, f1.contentWindow);
is(p2.getLastMouseX(), 15, "p2 mouse move X");
is(p2.getLastMouseY(), 38, "p2 mouse move Y");
synthesizeMouse(p2, 15, 28, { type:"mouseup" }, f1.contentWindow);
is(p2.getLastMouseX(), 15, "p2 mouse up X");
is(p2.getLastMouseY(), 28, "p2 mouse up Y");
SimpleTest.finish();
}
// Need to run 'doTest' after painting is unsuppressed, or we'll set clip
// regions to empty.
addLoadEvent(function() { setTimeout(doTest, 0); } );
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -102,6 +102,20 @@ getClipRegionRectCount(), this will throw an error. The coordinates are
the same as for getEdge. See getClipRegionRectCount() above for
notes on platform plugin limitations.
== Mouse events ==
The test plugin supports the following scriptable methods:
* getLastMouseX()
Returns the X coordinate of the last mouse event (move, button up, or
button down), relative to the left edge of the plugin, or -1 if no mouse
event has been received.
* getLastMouseX()
Returns the Y coordinate of the last mouse event (move, button up, or
button down), relative to the top edge of the plugin, or -1 if no mouse
event has been received.
== Instance lifecycle ==
The test plugin supports the following scriptable methods:

View File

@ -73,6 +73,8 @@ static bool startWatchingInstanceCount(NPObject* npobj, const NPVariant* args, u
static bool getInstanceCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool stopWatchingInstanceCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool unscheduleAllTimers(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool getLastMouseX(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool getLastMouseY(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static const NPUTF8* sPluginMethodIdentifierNames[] = {
"setUndefinedValueTest",
@ -88,6 +90,8 @@ static const NPUTF8* sPluginMethodIdentifierNames[] = {
"getInstanceCount",
"stopWatchingInstanceCount",
"unscheduleAllTimers",
"getLastMouseX",
"getLastMouseY",
};
static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)];
static const ScriptableFunction sPluginMethodFunctions[ARRAY_LENGTH(sPluginMethodIdentifierNames)] = {
@ -104,6 +108,8 @@ static const ScriptableFunction sPluginMethodFunctions[ARRAY_LENGTH(sPluginMetho
getInstanceCount,
stopWatchingInstanceCount,
unscheduleAllTimers,
getLastMouseX,
getLastMouseY,
};
static bool sIdentifiersInitialized = false;
@ -340,6 +346,7 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char*
}
instanceData->lastReportedPrivateModeState = false;
instanceData->lastMouseX = instanceData->lastMouseY = -1;
// do platform-specific initialization
NPError err = pluginInstanceInit(instanceData);
@ -876,3 +883,27 @@ unscheduleAllTimers(NPObject* npobj, const NPVariant* args, uint32_t argCount, N
return true;
}
static bool
getLastMouseX(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
{
if (argCount != 0)
return false;
NPP npp = static_cast<TestNPObject*>(npobj)->npp;
InstanceData* id = static_cast<InstanceData*>(npp->pdata);
INT32_TO_NPVARIANT(id->lastMouseX, *result);
return true;
}
static bool
getLastMouseY(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
{
if (argCount != 0)
return false;
NPP npp = static_cast<TestNPObject*>(npobj)->npp;
InstanceData* id = static_cast<InstanceData*>(npp->pdata);
INT32_TO_NPVARIANT(id->lastMouseY, *result);
return true;
}

View File

@ -64,6 +64,8 @@ typedef struct InstanceData {
bool hasWidget;
uint32_t timerID1;
uint32_t timerID2;
int32_t lastMouseX;
int32_t lastMouseY;
} InstanceData;
#endif // nptest_h_

View File

@ -214,6 +214,26 @@ ExposeWidget(GtkWidget* widget, GdkEventExpose* event,
return TRUE;
}
static gboolean
MotionEvent(GtkWidget* widget, GdkEventMotion* event,
gpointer user_data)
{
InstanceData* instanceData = static_cast<InstanceData*>(user_data);
instanceData->lastMouseX = event->x;
instanceData->lastMouseY = event->y;
return TRUE;
}
static gboolean
ButtonEvent(GtkWidget* widget, GdkEventButton* event,
gpointer user_data)
{
InstanceData* instanceData = static_cast<InstanceData*>(user_data);
instanceData->lastMouseX = event->x;
instanceData->lastMouseY = event->y;
return TRUE;
}
static gboolean
DeleteWidget(GtkWidget* widget, GdkEvent* event, gpointer user_data)
{
@ -256,9 +276,16 @@ pluginWidgetInit(InstanceData* instanceData, void* oldWindow)
GTK_WIDGET_SET_FLAGS (GTK_WIDGET(plug), GTK_CAN_FOCUS);
/* all the events that our widget wants to receive */
gtk_widget_add_events(plug, GDK_EXPOSURE_MASK);
gtk_widget_add_events(plug, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
g_signal_connect(G_OBJECT(plug), "expose-event", G_CALLBACK(ExposeWidget),
instanceData);
g_signal_connect(G_OBJECT(plug), "motion_notify_event", G_CALLBACK(MotionEvent),
instanceData);
g_signal_connect(G_OBJECT(plug), "button_press_event", G_CALLBACK(ButtonEvent),
instanceData);
g_signal_connect(G_OBJECT(plug), "button_release_event", G_CALLBACK(ButtonEvent),
instanceData);
g_signal_connect(G_OBJECT(plug), "delete-event", G_CALLBACK(DeleteWidget),
instanceData);
gtk_widget_show(plug);
@ -271,20 +298,38 @@ int16_t
pluginHandleEvent(InstanceData* instanceData, void* event)
{
#ifdef MOZ_X11
XEvent *nsEvent = (XEvent *)event;
XEvent* nsEvent = (XEvent*)event;
if (nsEvent->type != GraphicsExpose)
return 0;
switch (nsEvent->type) {
case GraphicsExpose: {
XGraphicsExposeEvent* expose = &nsEvent->xgraphicsexpose;
instanceData->window.window = (void*)(expose->drawable);
XGraphicsExposeEvent *expose = &nsEvent->xgraphicsexpose;
instanceData->window.window = (void*)(expose->drawable);
GdkNativeWindow nativeWinId =
reinterpret_cast<XID>(instanceData->window.window);
GdkDrawable* gdkWindow = GDK_DRAWABLE(gdk_window_foreign_new(nativeWinId));
pluginDrawWindow(instanceData, gdkWindow);
g_object_unref(gdkWindow);
GdkNativeWindow nativeWinId =
reinterpret_cast<XID>(instanceData->window.window);
GdkDrawable* gdkWindow = GDK_DRAWABLE(gdk_window_foreign_new(nativeWinId));
pluginDrawWindow(instanceData, gdkWindow);
g_object_unref(gdkWindow);
break;
}
case MotionNotify: {
XMotionEvent* motion = &nsEvent->xmotion;
instanceData->lastMouseX = motion->x;
instanceData->lastMouseY = motion->y;
break;
}
case ButtonPress:
case ButtonRelease: {
XButtonEvent* button = &nsEvent->xbutton;
instanceData->lastMouseX = button->x;
instanceData->lastMouseY = button->y;
break;
}
default:
break;
}
#endif
return 0;
}

View File

@ -237,11 +237,23 @@ int16_t
pluginHandleEvent(InstanceData* instanceData, void* event)
{
EventRecord* carbonEvent = (EventRecord*)event;
if (carbonEvent && (carbonEvent->what == updateEvt)) {
if (!carbonEvent)
return 0;
NPWindow* w = &instanceData->window;
switch (carbonEvent->what) {
case updateEvt:
pluginDraw(instanceData);
return 1;
case mouseDown:
case mouseUp:
case osEvt:
instanceData->lastMouseX = carbonEvent->where.h - w->x;
instanceData->lastMouseY = carbonEvent->where.v - w->y;
return 1;
default:
return 0;
}
return 0;
}
int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge)

View File

@ -35,6 +35,7 @@
#include "nptest_platform.h"
#include <windows.h>
#include <windowsx.h>
#pragma comment(lib, "msimg32.lib")
@ -367,22 +368,43 @@ pluginGetClipRegionRectEdge(InstanceData* instanceData,
/* windowless plugin events */
static bool
handleEventInternal(InstanceData* instanceData, NPEvent* pe)
{
switch ((UINT)pe->event) {
case WM_PAINT:
pluginDraw(instanceData);
return true;
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP: {
int x = instanceData->hasWidget ? 0 : instanceData->window.x;
int y = instanceData->hasWidget ? 0 : instanceData->window.y;
instanceData->lastMouseX = GET_X_LPARAM(pe->lParam) - x;
instanceData->lastMouseY = GET_Y_LPARAM(pe->lParam) - y;
return true;
}
default:
return false;
}
}
int16_t
pluginHandleEvent(InstanceData* instanceData, void* event)
{
NPEvent * pe = (NPEvent*) event;
NPEvent* pe = (NPEvent*)event;
if (pe == NULL || instanceData == NULL ||
instanceData->window.type != NPWindowTypeDrawable)
return 0;
switch((UINT)pe->event) {
case WM_PAINT:
pluginDraw(instanceData);
return 1;
}
return 0;
return handleEventInternal(instanceData, pe);
}
/* windowed plugin events */
@ -396,10 +418,10 @@ LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
if (!pInstance)
return 0;
if (uMsg == WM_PAINT) {
pluginDraw(pInstance);
NPEvent event = { uMsg, wParam, lParam };
if (handleEventInternal(pInstance, &event))
return 0;
}
if (uMsg == WM_CLOSE) {
ClearSubclass((HWND)pInstance->window.window);