pjs/lib/libmocha/lm_event.c

592 строки
15 KiB
C

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* JS event class.
*/
#include "lm.h"
#include "lo_ele.h"
#include "pa_tags.h"
#include "xp.h"
#include "prbit.h"
#include "prtypes.h"
#include "mkhelp.h" /* For onhelp support */
#include "jsapi.h" /* For onhelp support */
static JSEventNames event_names[] = {
/* ordered by log2(event_bit) */
{"mousedown", "MouseDown"},
{"mouseup", "MouseUp"},
{"mouseover", "MouseOver"},
{"mouseout", "MouseOut"},
{"mousemove", "MouseMove"},
{"mousedrag", "MouseDrag"},
{"click", "Click"},
{"dblclick", "DblClick"},
{"keydown", "KeyDown"},
{"keyup", "KeyUp"},
{"keypress", "KeyPress"},
{"dragdrop", "DragDrop"},
{"focus", "Focus"},
{"blur", "Blur"},
{"select", "Select"},
{"change", "Change"},
{"reset", "Reset"},
{"submit", "Submit"},
{"scroll", "Scroll"},
{"load", "Load"},
{"unload", "Unload"},
{"xferdone", "XferDone"},
{"abort", "Abort"},
{"error", "Error"},
{"locate", "Locate"},
{"move", "Move"},
{"resize", "Resize"},
{"forward", "Forward"},
{"help", "Help"},
{"back", "Back"},
};
#define NUM_EVENTS (sizeof event_names / sizeof event_names[0])
static JSConstDoubleSpec event_constants[] = {
{EVENT_ALT_MASK , "ALT_MASK"},
{EVENT_CONTROL_MASK , "CONTROL_MASK"},
{EVENT_SHIFT_MASK , "SHIFT_MASK"},
{EVENT_META_MASK , "META_MASK"},
{EVENT_MOUSEDOWN , "MOUSEDOWN"},
{EVENT_MOUSEUP , "MOUSEUP"},
{EVENT_MOUSEOVER , "MOUSEOVER"},
{EVENT_MOUSEOUT , "MOUSEOUT"},
{EVENT_MOUSEMOVE , "MOUSEMOVE"},
{EVENT_MOUSEDRAG , "MOUSEDRAG"},
{EVENT_CLICK , "CLICK"},
{EVENT_DBLCLICK , "DBLCLICK"},
{EVENT_KEYDOWN , "KEYDOWN"},
{EVENT_KEYUP , "KEYUP"},
{EVENT_KEYPRESS , "KEYPRESS"},
{EVENT_DRAGDROP , "DRAGDROP"},
{EVENT_FOCUS , "FOCUS"},
{EVENT_BLUR , "BLUR"},
{EVENT_SELECT , "SELECT"},
{EVENT_CHANGE , "CHANGE"},
{EVENT_RESET , "RESET"},
{EVENT_SUBMIT , "SUBMIT"},
{EVENT_SCROLL , "SCROLL"},
{EVENT_LOAD , "LOAD"},
{EVENT_UNLOAD , "UNLOAD"},
{EVENT_XFER_DONE , "XFER_DONE"},
{EVENT_ABORT , "ABORT"},
{EVENT_ERROR , "ERROR"},
{EVENT_LOCATE , "LOCATE"},
{EVENT_MOVE , "MOVE"},
{EVENT_RESIZE , "RESIZE"},
{EVENT_FORWARD , "FORWARD"},
{EVENT_HELP , "HELP"},
{EVENT_BACK , "BACK"},
{0, 0}
};
const char *
lm_EventName(uint32 event_bit)
{
uint index = (uint)PR_CeilingLog2(event_bit);
if (index >= NUM_EVENTS)
return "unknown event";
return event_names[index].lowerName;
}
JSEventNames *
lm_GetEventNames(uint32 event_bit)
{
uint index = (uint)PR_CeilingLog2(event_bit);
if (index >= NUM_EVENTS)
return NULL;
return &event_names[index];
}
uint32
lm_FindEventInMWContext(MWContext *context)
{
int i;
XP_List *kids;
MWContext *kid;
uint32 events;
if (!context->grid_children)
return context->event_bit;
events = 0;
if ((kids = context->grid_children) != NULL) {
for (i = 1; ((kid = XP_ListGetObjectNum(kids, i)) != NULL); i++) {
events |= lm_FindEventInMWContext(kid);
}
}
return events;
}
XP_Bool
LM_EventCaptureCheck(MWContext * context, uint32 current_event) {
if (context->event_bit & current_event)
return TRUE;
if (context->grid_parent)
return LM_EventCaptureCheck(context->grid_parent, current_event);
return FALSE;
}
enum event_tinyid {
EVENT_TYPE = -1,
EVENT_X = -2,
EVENT_Y = -3,
EVENT_LAYERX = -4,
EVENT_LAYERY = -5,
EVENT_WHICH = -6,
EVENT_MODIFIERS = -7,
EVENT_DATA = -8,
EVENT_DOCX = -9,
EVENT_DOCY = -10,
EVENT_SCREENX = -11,
EVENT_SCREENY = -12,
EVENT_OBJECT = -13
};
static JSPropertySpec event_props[] = {
{"type" , EVENT_TYPE, JSPROP_ENUMERATE},
{"x" , EVENT_X, JSPROP_ENUMERATE},
{"y" , EVENT_Y, JSPROP_ENUMERATE},
{"width" , EVENT_X, JSPROP_ENUMERATE},
{"height" , EVENT_Y, JSPROP_ENUMERATE},
{"layerX" , EVENT_LAYERX, JSPROP_ENUMERATE},
{"layerY" , EVENT_LAYERY, JSPROP_ENUMERATE},
{"which" , EVENT_WHICH, JSPROP_ENUMERATE},
{"modifiers" , EVENT_MODIFIERS, JSPROP_ENUMERATE},
{"data" , EVENT_DATA, JSPROP_ENUMERATE},
{"pageX" , EVENT_DOCX, JSPROP_ENUMERATE},
{"pageY" , EVENT_DOCY, JSPROP_ENUMERATE},
{"screenX" , EVENT_SCREENX, JSPROP_ENUMERATE},
{"screenY" , EVENT_SCREENY, JSPROP_ENUMERATE},
{"target" , EVENT_OBJECT, JSPROP_ENUMERATE},
{0}
};
extern JSClass lm_event_class;
PR_STATIC_CALLBACK(JSBool)
event_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSEvent *event;
JSString *str;
char ** urlArray;
jsval urlVal;
JSObject *array;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
event = JS_GetInstancePrivate(cx, obj, &lm_event_class, NULL);
if (!event)
return JS_TRUE;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
/*
* You might think the following would make a real nice
* switch() statement. But if you make it into one be
* ready to battle the Win16 internal compiler error
* demons.
*/
if (JSVAL_TO_INT(id) == EVENT_TYPE) {
str = JS_NewStringCopyZ(cx, lm_EventName(event->type));
if (!str)
return JS_FALSE;
*vp = STRING_TO_JSVAL(str);
}
else if ((JSVAL_TO_INT(id) == EVENT_X) || (JSVAL_TO_INT(id) == EVENT_LAYERX)) {
* vp = INT_TO_JSVAL(event->x);
}
else if ((JSVAL_TO_INT(id) == EVENT_Y) || (JSVAL_TO_INT(id) == EVENT_LAYERY)){
* vp = INT_TO_JSVAL(event->y);
}
else if (JSVAL_TO_INT(id) == EVENT_DOCX) {
* vp = INT_TO_JSVAL(event->docx);
}
else if (JSVAL_TO_INT(id) == EVENT_DOCY) {
* vp = INT_TO_JSVAL(event->docy);
}
else if (JSVAL_TO_INT(id) == EVENT_SCREENX) {
* vp = INT_TO_JSVAL(event->screenx);
}
else if (JSVAL_TO_INT(id) == EVENT_SCREENY) {
* vp = INT_TO_JSVAL(event->screeny);
}
else if (JSVAL_TO_INT(id) == EVENT_WHICH) {
if (event->type == EVENT_HELP) {
/* For onHelp events, the which parameter holds the
URL of the help topic, as given in the pHelpInfo
field of the MWContext associated with this window. */
MWContext *context;
context = event->ce.context;
if (context->pHelpInfo) {
char *topicID;
JSString *topicStr;
topicID = ((HelpInfoStruct *) (context->pHelpInfo))->topicURL;
topicStr = JS_NewStringCopyZ(cx, topicID);
* vp = STRING_TO_JSVAL(topicStr);
}
else {
JSString *topicStr;
topicStr = JS_NewStringCopyZ(cx, "about:blank"); /* Do not localize */
* vp = STRING_TO_JSVAL(topicStr);
}
}
else {
* vp = INT_TO_JSVAL(event->which);
}
}
else if (JSVAL_TO_INT(id) == EVENT_MODIFIERS) {
* vp = INT_TO_JSVAL(event->modifiers);
}
else if (JSVAL_TO_INT(id) == EVENT_OBJECT) {
if (event->object)
* vp = OBJECT_TO_JSVAL(event->object);
}
else if (JSVAL_TO_INT(id) == EVENT_DATA) {
if (event->type == EVENT_DRAGDROP) {
if (lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_BROWSER_READ)) {
array = JS_NewArrayObject(cx, 0, NULL);
if (!array)
return JS_FALSE;
urlArray = (char**)event->data;
if (!urlArray)
return JS_TRUE;
for (slot=0; slot<(jsint)event->dataSize; slot++) {
str = JS_NewStringCopyZ(cx, urlArray[slot]);
if (!str)
return JS_FALSE;
urlVal = STRING_TO_JSVAL(str);
if (!JS_SetElement(cx, array, slot, &urlVal))
return JS_FALSE;
}
*vp = OBJECT_TO_JSVAL(array);
}
}
}
return JS_TRUE;
}
static JSBool
GetUint32(JSContext *cx, jsval v, uint32 *uip)
{
jsdouble d;
if (!JS_ValueToNumber(cx, v, &d))
return JS_FALSE;
*uip = (uint32)d;
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
event_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSEvent *event;
jsint slot, i;
JSString *str;
const char *name;
uint32 temp;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
event = JS_GetInstancePrivate(cx, obj, &lm_event_class, NULL);
if (!event)
return JS_TRUE;
slot = JSVAL_TO_INT(id);
if (slot==EVENT_TYPE) {
if (!JSVAL_IS_STRING(*vp) || !(str = JS_ValueToString(cx, *vp)))
return JS_FALSE;
name = JS_GetStringBytes(str);
for (i = 0; i < NUM_EVENTS; i++) {
if (!XP_STRCASECMP(event_names[i].lowerName, name))
event->type = PR_BIT(i);
}
}
else if ((slot==EVENT_X) || (slot==EVENT_LAYERX))
return JS_ValueToInt32(cx, *vp, &event->x);
else if ((slot==EVENT_Y) || (slot==EVENT_LAYERY))
return JS_ValueToInt32(cx, *vp, &event->y);
else if (slot==EVENT_DOCX)
return JS_ValueToInt32(cx, *vp, &event->docx);
else if (slot==EVENT_DOCY)
return JS_ValueToInt32(cx, *vp, &event->docy);
else if (slot==EVENT_SCREENX)
return JS_ValueToInt32(cx, *vp, &event->screenx);
else if (slot==EVENT_SCREENY)
return JS_ValueToInt32(cx, *vp, &event->screeny);
else if (slot==EVENT_WHICH)
return GetUint32(cx, *vp, &event->which);
else if (slot==EVENT_MODIFIERS)
GetUint32(cx, *vp, &temp);
event->modifiers = temp;
return JS_TRUE;
/* Win16 hack */
/*else if (slot==EVENT_DATA) {
GetUint32(cx, *vp, &temp);
event->data = temp;
return JS_TRUE;
} */
return JS_TRUE;
}
PR_STATIC_CALLBACK(void)
event_finalize(JSContext *cx, JSObject *obj)
{
JSEvent *event;
event = JS_GetPrivate(cx, obj);
if (!event)
return;
DROP_BACK_COUNT(event->decoder);
JS_free(cx, event);
}
JSClass lm_event_class = {
"Event", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, event_getProperty, event_setProperty,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, event_finalize
};
static JSBool
InitEventObject(JSContext *cx, JSObject *obj, JSEvent *pEvent)
{
MochaDecoder *decoder;
XP_ASSERT(JS_InstanceOf(cx, obj, &lm_event_class, NULL));
if (!JS_SetPrivate(cx, obj, pEvent)) {
JS_free(cx, pEvent);
return JS_FALSE;
}
decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
pEvent->decoder = HOLD_BACK_COUNT(decoder);
pEvent->saved = JS_TRUE;
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
lm_Event(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
JSEvent *pEvent;
if (!JS_InstanceOf(cx, obj, &lm_event_class, argv))
return JS_FALSE;
pEvent = JS_malloc(cx, sizeof(JSEvent));
if (!pEvent)
return JS_FALSE;
XP_BZERO(pEvent, sizeof(JSEvent));
/* Need to decide what arguments can be used */
if (!InitEventObject(cx, obj, pEvent))
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
static JSClass event_receiver_class = {
"EventReceiver", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
PR_STATIC_CALLBACK(JSBool)
EventReceiver(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
event_receiver_handle_event(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSObject *eventObj;
if (argc != 1)
return JS_TRUE;
if (!JSVAL_IS_OBJECT(argv[0]) &&
!JS_ConvertValue(cx, argv[0], JSTYPE_OBJECT, &argv[0]))
return JS_FALSE;
eventObj = JSVAL_TO_OBJECT(argv[0]);
if (!JS_InstanceOf(cx, eventObj, &lm_event_class, argv))
return JS_FALSE;
return lm_HandleEvent(cx, obj, eventObj, JSVAL_NULL, rval);
}
static JSFunctionSpec event_receiver_methods[] = {
{"handleEvent", event_receiver_handle_event, 1},
{0}
};
static JSClass event_capturer_class = {
"EventCapturer", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
PR_STATIC_CALLBACK(JSBool)
EventCapturer(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
event_capturer_route_event(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSObject *eventObj;
JSEvent *event;
if (argc != 1)
return JS_TRUE;
if (!JSVAL_IS_OBJECT(argv[0]) &&
!JS_ConvertValue(cx, argv[0], JSTYPE_OBJECT, &argv[0]))
return JS_FALSE;
eventObj = JSVAL_TO_OBJECT(argv[0]);
if (!(event = JS_GetInstancePrivate (cx, eventObj, &lm_event_class, argv)))
return JS_FALSE;
/* Routing objects to themselves causes infinite recursion.
* And that's a big no-no.
*/
if (event->object == obj)
return JS_TRUE;
if (!event->object || !event->decoder || !event->decoder->window_context)
return JS_TRUE;
return lm_FindEventHandler(event->decoder->window_context,
event->object, eventObj, JSVAL_NULL,
rval);
}
PR_STATIC_CALLBACK(JSBool)
event_capturer_capture_events(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
event_capturer_release_events(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
static JSFunctionSpec event_capturer_methods[] = {
{"routeEvent", event_capturer_route_event, 1},
{"captureEvents", event_capturer_capture_events, 1},
{"releaseEvents", event_capturer_release_events, 1},
{0}
};
JSBool
lm_InitEventClasses(MochaDecoder *decoder)
{
JSContext *cx;
JSObject *prototype, *ctor;
cx = decoder->js_context;
prototype = JS_InitClass(cx, decoder->window_object, NULL, &lm_event_class,
lm_Event, 0, event_props, NULL, NULL, NULL);
if (!prototype|| !(ctor = JS_GetConstructor(cx, prototype)))
return JS_FALSE;
if (!JS_DefineConstDoubles(cx, ctor, event_constants))
return JS_FALSE;
decoder->event_prototype = prototype;
prototype = JS_InitClass(cx, decoder->window_object, NULL,
&event_receiver_class, EventReceiver, 0,
NULL, event_receiver_methods, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->event_receiver_prototype = prototype;
prototype = JS_InitClass(cx, decoder->window_object,
decoder->event_receiver_prototype,
&event_capturer_class, EventCapturer, 0,
NULL, event_capturer_methods, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->event_capturer_prototype = prototype;
return JS_TRUE;
}
JSObject *
lm_NewEventObject(MochaDecoder * decoder, JSEvent *pEvent)
{
JSContext * cx;
JSObject *obj;
cx = decoder->js_context;
obj = JS_NewObject(cx, &lm_event_class, decoder->event_prototype, 0);
if (!obj)
return NULL;
if (!InitEventObject(cx, obj, pEvent))
return NULL;
return obj;
}