/* -*- 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; }