/* -*- Mode: C; tab-width: 4; 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. */ /* * Messages passing from the mozilla thread to the mocha thread */ #include "lm.h" #include "xp.h" #include "fe_proto.h" #include "proto.h" #include "net.h" #include "structs.h" #include "prmon.h" #include "prthread.h" #ifdef XP_MAC #include "prpriv.h" #else #include "private/prpriv.h" /* For PR_NewNamedMonitor */ #endif #include "layout.h" /* XXX for lo_ContextToCell and lo_FormData */ #include "libimg.h" #ifdef JAVA #include "java.h" #endif #include "pa_tags.h" #include "css.h" #include "pa_parse.h" #ifdef JAVA_OR_OJI #include "jsjava.h" #endif #include "intl_csi.h" /* #include "netcache.h" */ QueueStackElement * et_TopQueue = NULL; PRIVATE PRMonitor * lm_queue_monitor = NULL; PRIVATE JSBool lm_InterruptCurrentOp = JS_FALSE; #ifdef XP_WIN16 #define MOCHA_NORMAL_PRIORITY PR_PRIORITY_NORMAL extern PRThread *lm_InterpretThread; #endif /**********************************************************************/ #define MAKE_EAGER_INHERIT(e) \ if (et_TopQueue->inherit_parent && \ e->ce.handle_eagerly == JS_FALSE && \ e->ce.context->grid_parent) { \ e->ce.handle_eagerly = \ (XP_DOCID(e->ce.context->grid_parent) == \ et_TopQueue->doc_id); \ } #define MAKE_EAGER(e) if (et_TopQueue->doc_id == 0 && \ et_TopQueue->context == e->ce.context) { \ et_TopQueue->doc_id = XP_DOCID(e->ce.context); \ e->ce.handle_eagerly = JS_TRUE; \ } else { \ e->ce.handle_eagerly = \ (XP_DOCID(e->ce.context) == et_TopQueue->doc_id);\ } \ MAKE_EAGER_INHERIT(e) /**********************************************************************/ static void et_event_to_mocha(ETEvent * e) { JSBool canDoJS; if(e->context) { e->doc_id = XP_DOCID(e->context); canDoJS = LM_CanDoJS(e->context); } else { /* source of event must be timeout or someone else who can do mocha*/ canDoJS = JS_TRUE; } if (!lm_InterpretQueue || !canDoJS) { e->event.destructor((PREvent *) e); return; } /* * Decide which queue to put this event on. The et_TopQueue may * actually be the same as lm_InterpretQueue */ if (e->handle_eagerly) PR_PostEvent(et_TopQueue->queue, &e->event); else PR_PostEvent(lm_InterpretQueue, &e->event); #ifdef XP_WIN16 /* Raise the mocha thread priority, otherwise mocha may not get * time slices, because user's program java threads have higher * priority ( 15 ). If mocha is starving events are not transfered * to the mozilla-event-queue and Navigator stops reacting to * the input events */ PR_SetThreadPriority ( lm_InterpretThread, PR_PRIORITY_URGENT ); #endif if(lm_queue_monitor) { /* wake up the processing routine */ PR_EnterMonitor(lm_queue_monitor); PR_Notify(lm_queue_monitor); PR_ExitMonitor(lm_queue_monitor); } } /**********************************************************************/ /* By wrapping the following macros around an event handler callback, we can verify that the MWContext to which the event was directed hasn't gone away. As a side effect, the local variable 'decoder' is established within the macros: void foo_handler(JSEvent* e) { ET_BEGIN_EVENT_HANDLER(e); ... decoder ... ET_END_EVENT_HANDLER(e); } */ #define ET_BEGIN_EVENT_HANDLER(jsevent) \ { \ MWContext* _c = (jsevent)->ce.context; \ MochaDecoder* decoder; \ if (_c == NULL) { \ goto _quit; \ } \ decoder = LM_GetMochaDecoder(_c); \ if (decoder == NULL) { \ goto _quit; \ } \ #define ET_END_EVENT_HANDLER(e) \ _quit: \ if (decoder != NULL) { \ LM_PutMochaDecoder(decoder); \ } \ } \ /**********************************************************************/ /* * Trash our event when we get done with it */ PR_STATIC_CALLBACK(void) et_event_destructor(JSEvent * pEvent) { if (!pEvent->saved) XP_FREE(pEvent); } /* * Actual handler routine */ PR_STATIC_CALLBACK(void) et_event_handler(JSEvent * e) { LO_Element * lo_element = NULL; ETEventStatus status = EVENT_OK; jsval rval; ET_BEGIN_EVENT_HANDLER(e); LO_LockLayout(); /* verify that this document is still valid */ if(e->ce.doc_id != XP_DOCID(e->ce.context) && e->type != EVENT_UNLOAD) { status = EVENT_PANIC; LO_UnlockLayout(); goto done; } decoder->doc_id = e->ce.doc_id; /* find the element that caused this event */ lo_element = e->lo_element; /* figure out who to call */ switch(e->type) { case EVENT_SCROLL: case EVENT_DRAGDROP: case EVENT_MOVE: case EVENT_RESIZE: case EVENT_HELP: /* These are all window level events only, TRUE unless explicitly denied */ LO_UnlockLayout(); if (!(decoder->event_mask & e->type)) { decoder->event_mask |= e->type; if (lm_SendEvent(e->ce.context, decoder->window_object, e, &rval) && rval == JSVAL_FALSE) { status = EVENT_CANCEL; } decoder->event_mask &= ~e->type; } break; /*BEGIN Events occurring in the following cases must LO_UnlockLayout themselves. */ case EVENT_CLICK: case EVENT_MOUSEDOWN: case EVENT_MOUSEUP: case EVENT_DBLCLICK: /* TRUE unless explicitly denied */ if (lm_MouseInputEvent(e->ce.context, lo_element, e, &rval) && rval == JSVAL_FALSE) { status = EVENT_CANCEL; } /* * If this was the submit button we want to send a submit * event here */ break; case EVENT_KEYDOWN: case EVENT_KEYUP: case EVENT_KEYPRESS: /* TRUE unless explicitly denied */ if (lm_KeyInputEvent(e->ce.context, lo_element, e, &rval) && rval == JSVAL_FALSE) { status = EVENT_CANCEL; } break; case EVENT_BLUR: case EVENT_FOCUS: case EVENT_SELECT: case EVENT_MOUSEOUT: case EVENT_CHANGE: /* TRUE unless explicitly denied */ if (lm_InputEvent(e->ce.context, lo_element, e, &rval) && rval == JSVAL_FALSE) { status = EVENT_CANCEL; } break; case EVENT_MOUSEMOVE: /* There are two reasons this event could be here * 1. The script is capturing this event. * 2. The script has a handler defined for onmousemove and we * are assuming they intend the capure this event. Since the * time it takes to handle a mousedown event and start capturing * would cause lossage of mousemove messages we're sending all * mousemoves between mousedowns and ups so that if the scripts * starts capturing during the onmousedown handler, the mousemoves * we would have lost will be sitting on the JS queue. */ /* TRUE unless explicitly denied */ if (LM_EventCaptureCheck(e->ce.context, EVENT_MOUSEMOVE)) { if (lm_InputEvent(e->ce.context, lo_element, e, &rval) && rval == JSVAL_FALSE) { status = EVENT_CANCEL; } } else LO_UnlockLayout(); break; case EVENT_MOUSEOVER: /* FALSE unless explicitly set */ status = EVENT_CANCEL; if (lm_InputEvent(e->ce.context, lo_element, e, &rval) && rval == JSVAL_TRUE) { status = EVENT_OK; } break; case EVENT_SUBMIT: if(lm_SendOnSubmit(e->ce.context, e, lo_element)) status = EVENT_OK; else status = EVENT_CANCEL; /*LO_UnlockLayout called in form_event;*/ break; case EVENT_RESET: if(lm_SendOnReset(e->ce.context, e, lo_element)) status = EVENT_OK; else status = EVENT_CANCEL; /*LO_UnlockLayout called in form_event;*/ break; /*END asymmetric layout unlocking.*/ default: LO_UnlockLayout(); XP_TRACE(("Mocha thread: Got event %d that I didn't expect", e->type)); break; } /* clear the mask */ /* check again to make sure the document hasn't changed */ if(e->ce.doc_id != XP_DOCID(e->ce.context)) status = EVENT_PANIC; done: /* Post an event to call the front-end closuer */ if(e->fnClosure) ET_PostJsEventAck(e->ce.context, lo_element, e->type, e->fnClosure, e->whatever, status); #ifdef XPWIN16 /* Return to the normal mocha thread priority, after mocha thread * has transfered event to the mozilla-event-queue ( if necessary, * i.e. if e->fnClosure != NULL */ PR_SetThreadPriority ( lm_InterpretThread, MOCHA_NORMAL_PRIORITY ); #endif ET_END_EVENT_HANDLER(e); } /* * Tell the backend about a new event. * fnClosure should be allowed to be NULL */ JSBool ET_SendEvent(MWContext * pContext, LO_Element *pElement, JSEvent *pEvent, ETClosureFunc fnClosure, void * whatever) { /* make sure we are able to process Mocha events before bothering */ if (!LM_CanDoJS(pContext) || EDT_IS_EDITOR(pContext)) { ETEventStatus status = EVENT_OK; if (pEvent->type == EVENT_MOUSEOVER) status = EVENT_CANCEL; if (fnClosure) fnClosure(pContext, pElement, pEvent->type, whatever, status); XP_FREE(pEvent); return(JS_TRUE); } PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_event_handler, (PRDestroyEventProc)et_event_destructor); pEvent->ce.context = pContext; MAKE_EAGER_INHERIT(pEvent); if(pElement) pEvent->id = pElement->lo_any.ele_id; pEvent->lo_element = pElement; pEvent->fnClosure = fnClosure; pEvent->whatever = whatever; et_event_to_mocha(&pEvent->ce); return(JS_TRUE); } /**********************************************************************/ typedef struct { ETEvent ce; int32 type; ETVoidPtrFunc fnClosure; void * data; int32 layer_id; JSBool resize_reload; } LoadEvent; PR_STATIC_CALLBACK(void) et_load_event_handler(LoadEvent * e) { ETEventStatus status = EVENT_OK; ET_BEGIN_EVENT_HANDLER(e); LO_LockLayout(); /* verify that this document is still valid */ if(e->ce.doc_id != XP_DOCID(e->ce.context) && e->type != EVENT_UNLOAD) { status = EVENT_PANIC; LO_UnlockLayout(); goto done; } LO_UnlockLayout(); decoder->doc_id = e->ce.doc_id; /* figure out who to call */ if (e->layer_id == LO_DOCUMENT_LAYER_ID) { if (e->type == EVENT_LOAD) lm_ClearDecoderStream(decoder, JS_TRUE); lm_SendLoadEvent(e->ce.context, e->type, e->resize_reload); } else { if ((decoder->stream_owner == e->layer_id) && (e->type == EVENT_LOAD)) lm_ClearDecoderStream(decoder, JS_TRUE); /* * If the load event has already been sent, this is a layer whose * src has been dynamically changed. In that case, we want to fire * a load event irrespective of whether this context had been * resized. */ lm_SendLayerLoadEvent(e->ce.context, e->type, e->layer_id, decoder->load_event_sent ? JS_FALSE : e->resize_reload); } done: /* Post an event to call the front-end closure */ if(e->fnClosure) ET_moz_CallFunctionAsync(e->fnClosure, e->data); ET_END_EVENT_HANDLER(e); } PR_STATIC_CALLBACK(void) et_generic_destructor(void * event) { XP_FREE(event); } /* * Tell the backend about a new load event. */ void ET_SendLoadEvent(MWContext * pContext, int32 type, ETVoidPtrFunc fnClosure, NET_StreamClass *stream, int32 layer_id, Bool resize_reload) { LoadEvent * pEvent; /* * Make sure we are allowed to do mocha stuff in this context * before bothering to send the event */ if (!LM_CanDoJS(pContext)) { if(fnClosure) fnClosure(stream); return; } pEvent = (LoadEvent *) XP_NEW_ZAP(LoadEvent); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_load_event_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->type = type; pEvent->ce.context = pContext; MAKE_EAGER(pEvent); pEvent->layer_id = layer_id; pEvent->fnClosure = fnClosure; pEvent->data = stream; pEvent->resize_reload = (JSBool)resize_reload; /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ PR_STATIC_CALLBACK(void) et_setactiveform_handler(JSEvent * e) { ET_BEGIN_EVENT_HANDLER(e); /* verify that this document is still valid */ if(e->ce.doc_id != XP_DOCID(e->ce.context)) return; lm_SetActiveForm(e->ce.context, e->type); ET_END_EVENT_HANDLER(e); } /* */ void ET_SetActiveForm(MWContext * pContext, lo_FormData * form) { JSEvent * pEvent = (JSEvent *) XP_NEW_ZAP(JSEvent); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_setactiveform_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; MAKE_EAGER(pEvent); /* form can be NULL when there should be no active form */ if(form) pEvent->type = form->id; else pEvent->type = 0; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ PR_STATIC_CALLBACK(void) et_setactivelayer_handler(JSEvent * e) { ET_BEGIN_EVENT_HANDLER(e); /* verify that this document is still valid */ if(e->ce.doc_id != XP_DOCID(e->ce.context)) return; LM_SetActiveLayer(e->ce.context, e->layer_id); ET_END_EVENT_HANDLER(e); } /* */ void ET_SetActiveLayer(MWContext * pContext, int32 layer_id) { JSEvent * pEvent = (JSEvent *) XP_NEW_ZAP(JSEvent); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_setactivelayer_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; pEvent->layer_id = layer_id; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ /* * Mocha is about to process or is processing an event for the given * context. Verify we haven't been asked to interrupt it */ JSBool ET_ContinueProcessing(MWContext * context) { return (JSBool)(lm_InterruptCurrentOp == JS_FALSE); } /**********************************************************************/ static void et_RevokeEvents(MWContext * pContext) { QueueStackElement *qse; for (qse = et_TopQueue; qse; qse = qse->down) { PR_RevokeEvents(qse->queue, pContext); } for (qse = et_TopQueue->up; qse; qse = qse->up) { PR_RevokeEvents(qse->queue, pContext); } } void ET_InterruptContext(MWContext * pContext) { /* make sure the context can do mocha before bothering */ if (!lm_queue_monitor || !LM_CanDoJS(pContext)) return; /* need to lock the JS-thread from starting new events */ PR_EnterMonitor(lm_queue_monitor); /* Is our context currently running in mocha ? */ if (LM_JSLockGetContext() == pContext) { /* * if the owner of the JSLock is the context we are * interrupting set a flag so it will stop soon */ lm_InterruptCurrentOp = JS_TRUE; } /* clear events for this context off of the interpret queue */ et_RevokeEvents(pContext); /* need to unlock the JS-thread from starting new events */ PR_ExitMonitor(lm_queue_monitor); /* Interrupt the JS image context. */ if (pContext->mocha_context) ET_InterruptImgCX(pContext); } /**********************************************************************/ /* * Actual handler routine for image events */ PR_STATIC_CALLBACK(void) et_image_event_handler(JSEvent * e) { ETEventStatus status = EVENT_OK; LO_ImageStruct * image; JSObject * obj; ET_BEGIN_EVENT_HANDLER(e); LO_LockLayout(); /* verify that this document is still valid */ if(e->ce.doc_id != XP_DOCID(e->ce.context)) { status = EVENT_PANIC; LO_UnlockLayout(); goto done; } /* * Remember the ID of the document that spaned this call stack */ if(decoder) decoder->doc_id = e->ce.doc_id; /* XXX chouck - do we need to set a mask so we don't loop infinitely? */ /* find the element that caused this event */ if(e->id) image = LO_GetImageByIndex(e->ce.context, e->layer_id, e->id); else image = (LO_ImageStruct *) e->lo_element; if (!image || (image->type != LO_IMAGE)) { LO_UnlockLayout(); goto done; } obj = image->mocha_object; /* OK, we've gotten our pointer, let layout be happy again */ LO_UnlockLayout(); /* If we actually had an object send the event for it */ if(obj) lm_ProcessImageEvent(e->ce.context, obj, (LM_ImageEvent) e->type); /* clear the mask */ /* check again to make sure the document hasn't changed */ if(e->ce.doc_id != XP_DOCID(e->ce.context)) status = EVENT_PANIC; done: ET_END_EVENT_HANDLER(e); return; } void ET_SendImageEvent(MWContext * pContext, LO_ImageStruct *image_data, LM_ImageEvent event) { JSEvent * pEvent; if (!LM_CanDoJS(pContext)) return; pEvent = (JSEvent *) XP_NEW_ZAP(JSEvent); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_image_event_handler, (PRDestroyEventProc)et_event_destructor); pEvent->type = event; pEvent->ce.context = pContext; if(image_data) { pEvent->id = image_data->seq_num; pEvent->layer_id = image_data->layer_id; } pEvent->lo_element = (LO_Element *) image_data; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; int32 layer_id; void * lo_ele; void * pa_tag; ReflectedObject type; uint index; } Reflect_Event; /* * Make the appropriate LM_Reflect() call. Since we store a pointer * to the newly created JSObject as part of the layout object it * represents, we need to lock layout while doing this so layout * doesn't take our object away out from under us. * If any of the reflection routines take us back into the mozilla * thread we run the risk of deadlocking on the LO_LockLayout() * call. */ PR_STATIC_CALLBACK(void) et_reflect_handler(Reflect_Event * e) { ETEventStatus status = EVENT_OK; ET_BEGIN_EVENT_HANDLER(e); /* * Make sure the layout objects don't go away while we are * reflecting them */ LO_LockLayout(); /* verify that this document is still valid */ if(e->ce.doc_id != XP_DOCID(e->ce.context)) { status = EVENT_PANIC; goto done; } switch(e->type) { case LM_APPLETS: #if defined(JAVA) || defined(OJI) LM_ReflectApplet(e->ce.context, (LO_JavaAppStruct *) e->lo_ele, e->pa_tag, e->layer_id, e->index); #endif break; case LM_EMBEDS: #if defined(JAVA) || defined(OJI) LM_ReflectEmbed(e->ce.context, e->lo_ele, e->pa_tag, e->layer_id, e->index); #endif break; case LM_IMAGES: LM_ReflectImage(e->ce.context, e->lo_ele, e->pa_tag, e->layer_id, e->index); break; case LM_LINKS: LM_ReflectLink(e->ce.context, (LO_AnchorData *) e->lo_ele, e->pa_tag, e->layer_id, e->index); break; case LM_FORMS: LM_ReflectForm(e->ce.context, (lo_FormData *) e->lo_ele, e->pa_tag, e->layer_id, e->index); break; case LM_NAMEDANCHORS: LM_ReflectNamedAnchor(e->ce.context, (lo_NameList *) e->lo_ele, e->pa_tag, e->layer_id, e->index); break; case LM_FORMELEMENTS: XP_ASSERT(0); break; case LM_LAYERS: LM_ReflectLayer(e->ce.context, e->index, e->layer_id, e->pa_tag); break; #ifdef DOM case LM_SPANS: LM_ReflectSpan(e->ce.context, e->lo_ele, e->pa_tag, e->layer_id, e->index); break; case LM_TRANSCLUSIONS: LM_ReflectTransclusion(e->ce.context, e->lo_ele, e->layer_id, e->index); break; #endif default: XP_ASSERT(0); break; } done: /* * We are done with the reflection so unlock layout */ LO_UnlockLayout(); ET_END_EVENT_HANDLER(e); return; } /* * Free our memory */ PR_STATIC_CALLBACK(void) et_reflect_destructor(Reflect_Event * e) { /* we explictly duplicated our tag when we created this event * object so make sure to get rid of it now */ if (e->pa_tag) PA_FreeTag(e->pa_tag); XP_FREE(e); } /* * Reflect the given object. */ void ET_ReflectObject(MWContext * pContext, void * lo_ele, void * tag, int32 layer_id, uint index, ReflectedObject type) { /* create our event object */ Reflect_Event * pEvent = (Reflect_Event *) XP_NEW_ZAP(Reflect_Event); if(!pEvent) return; #ifdef JAVA /* before we can call java on the mocha thread we need * to initialize moja. this isn't safe to do with the * layout lock held, so we do it here. */ if (type == LM_APPLETS || type == LM_EMBEDS) ET_InitMoja(pContext); #endif /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_reflect_handler, (PRDestroyEventProc)et_reflect_destructor); /* fill in the non-PR fields we care about */ pEvent->type = type; pEvent->ce.context = pContext; pEvent->lo_ele = lo_ele; if(tag) pEvent->pa_tag = (void *) PA_CloneMDLTag(tag); else pEvent->pa_tag = NULL; pEvent->index = index; pEvent->layer_id = layer_id; MAKE_EAGER(pEvent); /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; int32 layer_id; void * pa_tag; uint element_index; uint form_index; } ReflectForm_Event; /* * Make the appropriate LM_Reflect() call. Since we store a pointer * to the newly created JSObject as part of the layout object it * represents, we need to lock layout while doing this so layout * doesn't take our object away out from under us. * If any of the reflection routines take us back into the mozilla * thread we run the risk of deadlocking on the LO_LockLayout() * call. */ PR_STATIC_CALLBACK(void) et_reflectElement_handler(ReflectForm_Event * e) { ET_BEGIN_EVENT_HANDLER(e); /* * Make sure the layout objects don't go away while we are * reflecting them */ LO_LockLayout(); if (!decoder) goto done; /* verify that this document is still valid */ if (e->ce.doc_id != XP_DOCID(e->ce.context)) goto done; /* reflect the form element */ LM_ReflectFormElement(e->ce.context, e->layer_id, e->form_index, e->element_index, e->pa_tag); done: LO_UnlockLayout(); ET_END_EVENT_HANDLER(e); } PR_STATIC_CALLBACK(void) et_reflectElement_destructor(ReflectForm_Event * e) { /* * We explictly duplicated our tag when we created this event * object so make sure to get rid of it now */ if(e->pa_tag) { PA_Tag * tag = (PA_Tag *) e->pa_tag; if(tag->data) PA_FREE(tag->data); PA_FREE(tag); } XP_FREE(e); } /* * Reflect a form element. This is enough of a special case that * its been pulled out of the generic reflect object */ void ET_ReflectFormElement(MWContext * pContext, void * form, LO_FormElementStruct * form_element, PA_Tag * tag) { /* create our event object */ ReflectForm_Event * pEvent; if (!form || !form_element) return; pEvent = (ReflectForm_Event *) XP_NEW_ZAP(ReflectForm_Event); if (!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_reflectElement_handler, (PRDestroyEventProc)et_reflectElement_destructor); pEvent->ce.context = pContext; if(tag) pEvent->pa_tag = (void *) PA_CloneMDLTag(tag); else pEvent->pa_tag = NULL; pEvent->element_index = form_element->element_index; pEvent->form_index = ((lo_FormData *)form)->id; pEvent->layer_id = form_element->layer_id; MAKE_EAGER(pEvent); et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; void * buffer; ETEvalStuff * stuff; ETEvalAckFunc fn; } EvalStruct; PR_STATIC_CALLBACK(void) et_evalbuffer_handler(EvalStruct * e) { JSContext * cx; JSPrincipals * principals = NULL; JSPrincipals * event_principals = NULL; jsval rv; JSBool ok; size_t result_len; char * result_str; char * wysiwyg_url; char * base_href; uint len, line_no; ET_BEGIN_EVENT_HANDLER(e); LO_LockLayout(); /* * If the current document is the same as the document that sent * the evaluate event we want to continue to evaluate and remember * the doc_id so we can see if the document changes out from under * us */ if(e->ce.doc_id != XP_DOCID(e->ce.context)) { LO_UnlockLayout(); goto done; } decoder->doc_id = e->ce.doc_id; LO_UnlockLayout(); len = e->stuff->len; line_no = e->stuff->line_no; lm_SetVersion(decoder, e->stuff->version); event_principals = e->stuff->principals; cx = decoder->js_context; if (event_principals) { /* First appearance on this thread. Create a root. */ JSPRINCIPALS_HOLD(cx, event_principals); } principals = lm_GetCompilationPrincipals(decoder, event_principals); if (principals) { JSPRINCIPALS_HOLD(cx, principals); ok = LM_EvaluateBuffer(decoder, e->buffer, len, line_no, e->stuff->scope_to, principals, e->stuff->unicode, &rv); } else { ok = JS_FALSE; } if (event_principals) { /* We're done with e->principals */ JSPRINCIPALS_DROP(cx, event_principals); } /* make sure the document hasn't changed out from under us */ if(e->ce.doc_id != XP_DOCID(e->ce.context)) goto done; if(!ok) { ET_PostEvalAck(e->ce.context, e->ce.doc_id, e->stuff->data, NULL, 0, NULL, NULL, FALSE, e->fn); goto done; } if (!e->stuff->want_result) rv = JSVAL_VOID; if (rv == JSVAL_VOID || !JS_ConvertValue(cx, rv, JSTYPE_STRING, &rv)) { ET_PostEvalAck(e->ce.context, e->ce.doc_id, e->stuff->data, NULL, 0, NULL, NULL, JS_TRUE, e->fn); goto done; } result_len = JS_GetStringLength(JSVAL_TO_STRING(rv)); result_str = JS_malloc(cx, result_len + 1); if (result_str) { /* XXXunicode or is this binary data going to imagelib ? */ XP_MEMCPY(result_str, JS_GetStringBytes(JSVAL_TO_STRING(rv)), result_len); result_str[result_len] = '\0'; } wysiwyg_url = lm_MakeWysiwygUrl(cx, decoder, decoder->active_layer_id, principals); base_href = LM_GetBaseHrefTag(cx, principals); ET_PostEvalAck(e->ce.context, e->ce.doc_id, e->stuff->data, result_str, result_len, wysiwyg_url, base_href, JS_TRUE, e->fn); done: if (principals) JSPRINCIPALS_DROP(cx, principals); ET_END_EVENT_HANDLER(e); } PR_STATIC_CALLBACK(void) et_evalbuffer_destructor(EvalStruct * e) { XP_FREEIF(e->stuff->scope_to); XP_FREE(e->buffer); XP_FREE(e->stuff); XP_FREE(e); } /* * This sucks a lot. The ET_EvaluateBuffer() API needed to change * but the security code is on a tagged release and can't be changed. */ void ET_EvaluateBuffer(MWContext * context, char * buffer, uint buflen, uint line_no, char * scope_to, JSBool want_result, ETEvalAckFunc fn, void * data, JSVersion ver, struct JSPrincipals * hi) { /* call ET_EvaluateScript(), please */ XP_ASSERT(0); } /* * Evaluate the given script. I'm sure this is going to need a * callback or compeletion routine */ void ET_EvaluateScript(MWContext * pContext, char * buffer, ETEvalStuff * stuff, ETEvalAckFunc fn) { EvalStruct * pEvent; int len; int16 charset; /* * make sure this context can do mocha, if not, don't bother * sending the event over, just call the closure function and * go home */ if (!LM_CanDoJS(pContext)) { fn(stuff->data, NULL, 0, NULL, NULL, JS_FALSE); XP_FREE(stuff); return; } /* create our event object */ pEvent = (EvalStruct *) XP_NEW_ZAP(EvalStruct); if (!pEvent) { XP_FREE(stuff); return; } /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_evalbuffer_handler, (PRDestroyEventProc)et_evalbuffer_destructor); pEvent->ce.context = pContext; MAKE_EAGER(pEvent); /* * We are going to make our own copy of the buffer in order * to be safe. If we are on an non-ascii page just do the * conversion to unicode here. Since the JS engine is all * unicode in 5.x maybe we should always just do the * transformation here. */ len = stuff->len; charset = INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(pContext)); if (charset == CS_DEFAULT || charset == CS_ASCII || charset == CS_LATIN1) { char * buf; buf = XP_ALLOC(sizeof(char)* (len + 1)); if (!buf) { XP_FREE(stuff); return; } strncpy(buf, buffer, len); buf[len] = '\0'; pEvent->buffer = buf; stuff->unicode = JS_FALSE; } else { uint32 unicodeLen; /* find out how many unicode characters we'll end up with */ unicodeLen = INTL_TextToUnicodeLen(charset, (unsigned char *) buffer, len); pEvent->buffer = XP_ALLOC(sizeof(INTL_Unicode) * unicodeLen); if (!pEvent->buffer) { XP_FREE(stuff); return; } /* do the conversion */ stuff->len = INTL_TextToUnicode(charset, (unsigned char *) buffer, len, pEvent->buffer, unicodeLen); stuff->unicode = JS_TRUE; } pEvent->stuff = stuff; pEvent->fn = fn; /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ PR_STATIC_CALLBACK(void) et_firetimeout_handler(MozillaEvent_Timeout * e) { /* ET_BEGIN_EVENT_HANDLER(e);*/ /* since we don't use the MWContext */ (e->fnCallback) ((void *)e); /* ET_END_EVENT_HANDLER(e);*/ } void ET_FireTimeoutCallBack(void * obj) { /* * our closure is actually the original event that we sent to * the mozilla thread to get this whole party started. * we own the freeing of this storage so macke sure we have a * valid destructor function */ MozillaEvent_Timeout * pEvent = (MozillaEvent_Timeout *) obj; /* reuse our event */ PR_InitEvent(&pEvent->ce.event, NULL, (PRHandleEventProc)et_firetimeout_handler, (PRDestroyEventProc)et_generic_destructor); /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; NET_StreamClass * stream; NET_StreamClass * old_stream; URL_Struct * url_struct; JSBool free_stream_on_close; } DecoderStreamStruct; PR_STATIC_CALLBACK(void) et_setstream_handler(DecoderStreamStruct * e) { ET_BEGIN_EVENT_HANDLER(e); /* This will hold a ref on e->url_struct from the context's decoder. */ LM_SetDecoderStream(e->ce.context, e->stream, e->url_struct, e->free_stream_on_close); /* Drop the reference held below when e was constructed. */ NET_DropURLStruct(e->url_struct); ET_END_EVENT_HANDLER(e); } /* */ void ET_SetDecoderStream(MWContext * pContext, NET_StreamClass *stream, URL_Struct *url_struct, JSBool free_stream_on_close) { /* create our event object */ DecoderStreamStruct * pEvent = XP_NEW_ZAP(DecoderStreamStruct); if(!pEvent) return; /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_setstream_handler, (PRDestroyEventProc)et_generic_destructor); /* fill in the non-PR fields we care about */ pEvent->ce.context = pContext; pEvent->stream = stream; pEvent->url_struct = url_struct; pEvent->free_stream_on_close = free_stream_on_close; /* we are holding a copy of the URL_struct across thread boundaries */ NET_HoldURLStruct(url_struct); /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; char *codebase; } StartSoftUpdateStruct; PR_STATIC_CALLBACK(void) et_startsoftupdate_handler(StartSoftUpdateStruct *e) { ET_BEGIN_EVENT_HANDLER(e); /* This decoder should have just been created to do the softupdate, * so it shouldn't have any principals yet. */ XP_ASSERT(decoder->principals == NULL); decoder->principals = LM_NewJSPrincipals(NULL, NULL, e->codebase); if (decoder->principals == NULL) return; JSPRINCIPALS_HOLD(decoder->js_context, decoder->principals); ET_END_EVENT_HANDLER(e); } PR_STATIC_CALLBACK(void) et_startsoftupdate_destructor(StartSoftUpdateStruct *e) { XP_FREE(e->codebase); XP_FREE(e); } /* */ void ET_StartSoftUpdate(MWContext *pContext, char *codebase) { /* create our event object */ StartSoftUpdateStruct *pEvent = XP_NEW_ZAP(StartSoftUpdateStruct); if (pEvent == NULL) return; /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_startsoftupdate_handler, (PRDestroyEventProc)et_startsoftupdate_destructor); /* fill in the non-PR fields we care about */ pEvent->ce.context = pContext; pEvent->codebase = codebase; /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ PR_STATIC_CALLBACK(void) et_clearstream_handler(DecoderStreamStruct * e) { ET_BEGIN_EVENT_HANDLER(e); lm_ClearDecoderStream(decoder, JS_TRUE); if (e->old_stream) XP_FREE(e->old_stream); ET_END_EVENT_HANDLER(e); } /* */ void ET_ClearDecoderStream(MWContext * pContext, NET_StreamClass * old_stream) { /* create our event object */ DecoderStreamStruct * pEvent = XP_NEW_ZAP(DecoderStreamStruct); if(!pEvent) return; /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_clearstream_handler, (PRDestroyEventProc)et_generic_destructor); /* fill in the non-PR fields we care about */ pEvent->ce.context = pContext; pEvent->old_stream = old_stream; /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; JSObject *layer_obj; } DestroyLayerStruct; PR_STATIC_CALLBACK(void) et_destroylayer_handler(DestroyLayerStruct * e) { ET_BEGIN_EVENT_HANDLER(e); lm_DestroyLayer(e->ce.context, e->layer_obj); ET_END_EVENT_HANDLER(e); } void ET_DestroyLayer(MWContext * pContext, JSObject *layer_obj) { DestroyLayerStruct * pEvent = XP_NEW_ZAP(DestroyLayerStruct); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_destroylayer_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; MAKE_EAGER(pEvent); pEvent->layer_obj = layer_obj; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; JSBool resize_reload; } ReleaseDocStruct; PR_STATIC_CALLBACK(void) et_releasedocument_handler(ReleaseDocStruct * e) { ET_BEGIN_EVENT_HANDLER(e); LM_ReleaseDocument(e->ce.context, e->resize_reload); ET_END_EVENT_HANDLER(e); } void ET_ReleaseDocument(MWContext * pContext, JSBool resize_reload) { /* create our event object */ ReleaseDocStruct * pEvent = XP_NEW_ZAP(ReleaseDocStruct); if(!pEvent) return; /* * give this event a NULL owner so it can't get revoked by an * errant intertupt */ PR_InitEvent(&pEvent->ce.event, NULL, (PRHandleEventProc)et_releasedocument_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; MAKE_EAGER(pEvent); pEvent->resize_reload = resize_reload; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; IL_GroupContext *img_cx; } InterruptImgCXEvent; PR_STATIC_CALLBACK(void) et_moz_interruptimgcx_func(void *data) { InterruptImgCXEvent *e = data; IL_InterruptContext(e->img_cx); } PR_STATIC_CALLBACK(void) et_interruptimgcx_handler(InterruptImgCXEvent *e) { ET_BEGIN_EVENT_HANDLER(e); e->img_cx = decoder->image_context; ET_moz_CallFunction(et_moz_interruptimgcx_func, e); ET_END_EVENT_HANDLER(e); } void ET_InterruptImgCX(MWContext * pContext) { InterruptImgCXEvent * pEvent = XP_NEW_ZAP(InterruptImgCXEvent); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_interruptimgcx_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; char * szUrl; } NestingUrlEvent; PR_STATIC_CALLBACK(void) et_nestingurl_handler(NestingUrlEvent * e) { JSNestingUrl * url; ET_BEGIN_EVENT_HANDLER(e); if (e->szUrl) { /* push a new url */ url = XP_NEW(JSNestingUrl); url->str = e->szUrl; url->next = decoder->nesting_url; decoder->nesting_url = url; e->szUrl = NULL; /* don't free below */ } else { /* pop an old url */ url = decoder->nesting_url; if (url) { decoder->nesting_url = url->next; XP_FREE(url->str); XP_FREE(url); } } ET_END_EVENT_HANDLER(e); } PR_STATIC_CALLBACK(void) et_nestingurl_destructor(NestingUrlEvent * e) { XP_FREEIF(e->szUrl); XP_FREE(e); } void ET_SetNestingUrl(MWContext * pContext, char * url) { NestingUrlEvent * pEvent = XP_NEW_ZAP(NestingUrlEvent); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_nestingurl_handler, (PRDestroyEventProc)et_nestingurl_destructor); pEvent->ce.context = pContext; MAKE_EAGER(pEvent); if(url) pEvent->szUrl = XP_STRDUP(url); else pEvent->szUrl = NULL; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; JSVersion version; } VersionEvent; PR_STATIC_CALLBACK(void) et_version_handler(VersionEvent * e) { ET_BEGIN_EVENT_HANDLER(e); JS_SetVersion(decoder->js_context, e->version); ET_END_EVENT_HANDLER(e); } void ET_SetVersion(MWContext * pContext, JSVersion version) { VersionEvent * pEvent = XP_NEW_ZAP(VersionEvent); if (pEvent == NULL) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_version_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; pEvent->version = version; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; void * data; ETVoidPtrFunc fn; History_entry * he; } RemoveWindowEvent; PR_STATIC_CALLBACK(void) et_moz_removewindow_epilog(void *data) { RemoveWindowEvent *e = data; /* Do this before calling e->fn, which nukes e->ce.context! */ if (e->he) SHIST_DropEntry(e->ce.context, e->he); /* Call the FE to destroy the context, finally. */ e->fn(e->data); } /* * Don't try to get the MochaDecoder in here with the * ET_BEGIN_EVENT_HANDLER() stuff since the decoder * already partially destroyed */ PR_STATIC_CALLBACK(void) et_removewindow_handler(RemoveWindowEvent * e) { LM_RemoveWindowContext(e->ce.context, e->he); /* remove any messages for this context that are waiting for us */ /* what prevents one from getting added while running this? A: Being in the monitor of lm_InterpretQueue, both when you do this, and when you deliver the events. -Warren */ /* XP_ASSERT(PR_InMonitor(lm_queue_monitor));*/ et_RevokeEvents(e->ce.context); ET_moz_CallFunction(et_moz_removewindow_epilog, e); } void ET_RemoveWindowContext(MWContext * pContext, ETVoidPtrFunc fn, void * data) { /* create our event object */ RemoveWindowEvent * pEvent; History_entry * he = NULL; /* * If mocha is disabled or this is a non-JS aware context don't * bother creating an event, just call the closure directly */ if (!LM_CanDoJS(pContext)) { ET_moz_CallFunctionAsync(fn, data); return; } /* * Allocate an event before possibly holding a history entry, so we can * return early without possibly having to drop. */ pEvent = XP_NEW_ZAP(RemoveWindowEvent); if (!pEvent) return; /* * Frames are special, because their contexts are destroyed and recreated * when they're reloaded, even when resizing. */ if (pContext->is_grid_cell) { lo_GridRec *grid = 0; lo_GridCellRec *rec = lo_ContextToCell(pContext, FALSE, &grid); if (rec && rec->hist_indx > 0) { he = (History_entry *)rec->hist_array[rec->hist_indx - 1].hist; SHIST_HoldEntry(he); } } PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_removewindow_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; pEvent->fn = fn; pEvent->data = data; pEvent->he = he; /* set the doc_id so that we don't try to reuse this context */ pContext->doc_id = -1; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; MochaDecoder * decoder; } PutDecoderEvent; PR_STATIC_CALLBACK(void) et_putdecoder_handler(PutDecoderEvent * e) { LM_PutMochaDecoder(e->decoder); } /* * The mozilla thread is, in general, not allowed into the world of * MochaDecoders, but one could have been stashed in a session * history object. If so, provide a way for the mozilla thread to * release the decoder when the history object goes away. */ void et_PutMochaDecoder(MWContext *pContext, MochaDecoder *decoder) { PutDecoderEvent * pEvent; pEvent = XP_NEW_ZAP(PutDecoderEvent); if (!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_putdecoder_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; pEvent->decoder = decoder; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; void * app; } SetPluginWindowEvent; PR_EXTERN(void) NPL_SetPluginWindow(void *); PR_STATIC_CALLBACK(void) et_SetPluginWindow_handler(SetPluginWindowEvent * e) { NPL_SetPluginWindow(e->app); } void ET_SetPluginWindow(MWContext *pContext, void* app) { SetPluginWindowEvent * pEvent; pEvent = XP_NEW_ZAP(SetPluginWindowEvent); if (!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_SetPluginWindow_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; pEvent->app = app; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; void * buf; int len; int status; char * content_type; Bool isUnicode; } StreamEvent; PR_STATIC_CALLBACK(void) et_streamcomplete_handler(StreamEvent * e) { jsval result; JSContext * cx; char *scope = NULL; JSNestingUrl * url; ET_BEGIN_EVENT_HANDLER(e); cx = decoder->js_context; if(e->content_type) { if(!strcasecomp(e->content_type, TEXT_JSSS)) { /* scope to document */ scope = lm_document_str; } else if(!strcasecomp(e->content_type, TEXT_CSS)) { /* convert to JS and scope to document */ char *new_buffer; int32 new_buffer_length; CSS_ConvertToJS(e->buf, e->len, &new_buffer, &new_buffer_length); XP_FREE(e->buf); e->buf = new_buffer; e->len = new_buffer_length; if(e->len) e->len--; /* hack: subtract one to remove final \n */ scope = lm_document_str; } } #ifdef JSDEBUGGER if( LM_GetJSDebugActive() ) LM_JamSourceIntoJSDebug( LM_GetSourceURL(decoder), e->buf, e->len, e->ce.context ); #endif /* JSDEBUGGER */ LM_EvaluateBuffer(decoder, e->buf, e->len, 1, scope, NULL, (JSBool) e->isUnicode, &result); url = decoder->nesting_url; if (decoder->stream && !url) { /* complete and remove the stream */ ET_moz_CallFunction( (ETVoidPtrFunc) decoder->stream->complete, (void *)decoder->stream); XP_DELETE(decoder->stream); decoder->stream = 0; decoder->stream_owner = LO_DOCUMENT_LAYER_ID; decoder->free_stream_on_close = JS_FALSE; } if (url) { decoder->nesting_url = url->next; XP_FREE(url->str); XP_FREE(url); ET_PostEvalAck(e->ce.context, XP_DOCID(e->ce.context), e->ce.context, NULL, 0, NULL, NULL, FALSE, lo_ScriptEvalExitFn); } ET_END_EVENT_HANDLER(e); } PR_STATIC_CALLBACK(void) et_streamcomplete_destructor(StreamEvent * e) { if(e->buf) XP_FREE(e->buf); XP_FREEIF(e->content_type); XP_FREE(e); } /* * A mocha stream from netlib has compeleted, eveluate the contents * and pass them up our stream. We will take ownership of the * buf argument and are responsible for freeing it */ void ET_MochaStreamComplete(MWContext * pContext, void * buf, int len, char *content_type, Bool isUnicode) { StreamEvent * pEvent; pEvent = XP_NEW_ZAP(StreamEvent); if(!pEvent) { XP_FREE(buf); return; } PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_streamcomplete_handler, (PRDestroyEventProc)et_streamcomplete_destructor); pEvent->ce.context = pContext; MAKE_EAGER(pEvent); pEvent->buf = buf; pEvent->len = len; pEvent->isUnicode = isUnicode; if (content_type) pEvent->content_type = XP_STRDUP(content_type); et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ PR_STATIC_CALLBACK(void) et_streamabort_handler(StreamEvent * e) { ET_BEGIN_EVENT_HANDLER(e); if (decoder->stream && !decoder->nesting_url) { ET_moz_Abort(decoder->stream->abort, decoder->stream, e->status); XP_DELETE(decoder->stream); decoder->stream = 0; decoder->free_stream_on_close = JS_FALSE; decoder->stream_owner = LO_DOCUMENT_LAYER_ID; } ET_END_EVENT_HANDLER(e); } /* * A mocha stream from netlib has aborted */ void ET_MochaStreamAbort(MWContext * context, int status) { StreamEvent * pEvent = XP_NEW_ZAP(StreamEvent); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, context, (PRHandleEventProc)et_streamabort_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = context; pEvent->status = status; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ PR_STATIC_CALLBACK(void) et_newlayerdoc_handler(JSEvent * e) { ET_BEGIN_EVENT_HANDLER(e); lm_NewLayerDocument(decoder, e->layer_id); ET_END_EVENT_HANDLER(e); } /* * A mocha stream from netlib has aborted */ void ET_NewLayerDocument(MWContext * context, int32 layer_id) { JSEvent * pEvent = XP_NEW_ZAP(JSEvent); if(!pEvent) return; /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, context, (PRHandleEventProc)et_newlayerdoc_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = context; pEvent->layer_id = layer_id; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; int32 layer_id; LO_BlockInitializeStruct *param; ETRestoreAckFunc fn; void *data; } RestoreStruct; PR_STATIC_CALLBACK(void) et_restorelayerstate_handler(RestoreStruct * e) { ET_BEGIN_EVENT_HANDLER(e); lm_RestoreLayerState(e->ce.context, e->layer_id, e->param); ET_PostRestoreAck(e->data, e->param, e->fn); ET_END_EVENT_HANDLER(e); } void ET_RestoreLayerState(MWContext *context, int32 layer_id, LO_BlockInitializeStruct *param, ETRestoreAckFunc fn, void *data) { RestoreStruct * pEvent = XP_NEW_ZAP(RestoreStruct); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, context, (PRHandleEventProc)et_restorelayerstate_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = context; pEvent->layer_id = layer_id; pEvent->param = param; pEvent->fn = fn; pEvent->data = data; MAKE_EAGER(pEvent); et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; PA_Block onload; PA_Block onunload; PA_Block onfocus; PA_Block onblur; PA_Block onhelp; PA_Block onmouseover; PA_Block onmouseout; PA_Block ondragdrop; PA_Block onmove; PA_Block onresize; PA_Block id; char *all; Bool bDelete; int newline_count; } ReflectWindowEvent; PR_STATIC_CALLBACK(void) et_reflectwindow_handler(ReflectWindowEvent * e) { JSObject *obj; ET_BEGIN_EVENT_HANDLER(e); obj = decoder->window_object; if (e->onload) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONLOAD, e->onload); } if (e->onunload) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONUNLOAD, e->onunload); } if (e->onfocus) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONFOCUS, e->onfocus); } if (e->onblur) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONBLUR, e->onblur); } if (e->onhelp) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONHELP, e->onhelp); } if (e->onmouseover) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONMOUSEOVER, e->onmouseover); } if (e->onmouseout) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONMOUSEOUT, e->onmouseout); } if (e->ondragdrop) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONDRAGDROP, e->ondragdrop); } if (e->onmove) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONMOVE, e->onmove); } if (e->onresize) { (void) lm_CompileEventHandler(decoder, e->id, (PA_Block) e->all, e->newline_count, obj, PARAM_ONRESIZE, e->onresize); } if (e->bDelete) { if(e->onload) XP_FREE(e->onload); if(e->onunload) XP_FREE(e->onunload); if(e->onfocus) XP_FREE(e->onfocus); if(e->onblur) XP_FREE(e->onblur); if(e->onhelp) XP_FREE(e->onhelp); if(e->onmouseover) XP_FREE(e->onmouseover); if(e->onmouseout) XP_FREE(e->onmouseout); if(e->ondragdrop) XP_FREE(e->ondragdrop); if(e->onmove) XP_FREE(e->onmove); if(e->onresize) XP_FREE(e->onresize); if(e->id) XP_FREE(e->id); if(e->all) XP_FREE(e->all); } ET_END_EVENT_HANDLER(e); } /* * Reflect window events */ void ET_ReflectWindow(MWContext * pContext, PA_Block onLoad, PA_Block onUnload, PA_Block onFocus, PA_Block onBlur, PA_Block onHelp, PA_Block onMouseOver, PA_Block onMouseOut, PA_Block onDragDrop, PA_Block onMove, PA_Block onResize, PA_Block id, char *all, Bool bDelete, int newline_count) { ReflectWindowEvent * pEvent = XP_NEW_ZAP(ReflectWindowEvent); if(!pEvent) return; PR_InitEvent(&pEvent->ce.event, pContext, (PRHandleEventProc)et_reflectwindow_handler, (PRDestroyEventProc)et_generic_destructor); pEvent->ce.context = pContext; MAKE_EAGER(pEvent); pEvent->onload = onLoad; pEvent->onunload = onUnload; pEvent->onfocus = onFocus; pEvent->onblur = onBlur; pEvent->onhelp = onHelp; pEvent->onmouseover = onMouseOver; pEvent->onmouseout = onMouseOut; pEvent->ondragdrop = onDragDrop; pEvent->onmove = onMove; pEvent->onresize = onResize; pEvent->id = id; pEvent->all = all; pEvent->bDelete = bDelete; pEvent->newline_count = newline_count; /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ #ifdef DEBUG PR_STATIC_CALLBACK(void) lm_dump_named_root(const char *name, void *rp, void *data) { XP_TRACE(("Leaked named root \"%s\" at 0x%x", name, rp)); } #endif PR_STATIC_CALLBACK(void) et_FinishMochaHandler(JSEvent * e) { MochaDecoder *decoder; decoder = lm_crippled_decoder; if (decoder) { LM_PutMochaDecoder(decoder); lm_crippled_decoder = 0; } #if defined(OJI) /* =-= sudu Ask scott about this. PR_PUBLIC_API(void) JSJ_DisconnectFromJavaVM(JSJavaVM *); */ #elif defined (JAVA) JSJ_Finish(); #endif #ifdef DEBUG JS_DumpNamedRoots(lm_runtime, lm_dump_named_root, NULL); #endif JS_Finish(lm_runtime); /* turn off the mocha thread here! */ } void ET_FinishMocha(void) { JSEvent * pEvent; /* * Annoyingly, the winfe might call us without event actually * initializing mocha (if an instance is already running) */ if (!lm_InterpretQueue) return; pEvent = XP_NEW_ZAP(JSEvent); if (!pEvent) return; PR_InitEvent(&pEvent->ce.event, NULL, (PRHandleEventProc)et_FinishMochaHandler, (PRDestroyEventProc)et_generic_destructor); et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; void * data; JSBool processed; } DocWriteAckEvent; PR_STATIC_CALLBACK(void) et_docwriteack_handler(DocWriteAckEvent * e) { e->processed = JS_TRUE; et_TopQueue->done = TRUE; et_TopQueue->retval = e->data; } PR_STATIC_CALLBACK(void) et_docwriteack_destructor(DocWriteAckEvent * e) { if (!e->processed) et_docwriteack_handler(e); XP_FREE(e); } void ET_DocWriteAck(MWContext * context, int status) { DocWriteAckEvent * pEvent; pEvent = XP_NEW_ZAP(DocWriteAckEvent); if (!pEvent) return; PR_InitEvent(&pEvent->ce.event, context, (PRHandleEventProc)et_docwriteack_handler, (PRDestroyEventProc)et_docwriteack_destructor); pEvent->ce.context = context; pEvent->ce.handle_eagerly = TRUE; pEvent->data = (void *)status; et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ void et_SubEventLoop(QueueStackElement * qse) { PREvent * pEvent; /* while there are events process them */ while (!qse->done) { /* can't be interrupted yet */ lm_InterruptCurrentOp = JS_FALSE; #ifdef OJI LM_LockJS(NULL); #else LM_LockJS(); #endif /* need to interlock the getting of an event with ET_Interrupt */ PR_EnterMonitor(lm_queue_monitor); pEvent = PR_GetEvent(qse->queue); /* if we got an event handle it else wait for something */ if(pEvent) { PR_ExitMonitor(lm_queue_monitor); LM_JSLockSetContext(((ETEvent *)pEvent)->context); PR_HandleEvent(pEvent); LM_UnlockJS(); #ifdef DEBUG /* make sure we don't have the layout lock */ while(!LO_VerifyUnlockedLayout()) { XP_ASSERT(0); LO_UnlockLayout(); } #endif } else { /* queue is empty, wait for something to show up */ LM_UnlockJS(); PR_Wait(lm_queue_monitor, PR_INTERVAL_NO_TIMEOUT); PR_ExitMonitor(lm_queue_monitor); } } } extern PRThread *lm_InterpretThread; extern PRMonitor *lm_owner_mon; /* * Sit around in the mocha thread waiting for events to show up */ void PR_CALLBACK lm_wait_for_events(void * pB) { XP_ASSERT(et_TopQueue); /* * In NSPR 2.0 this thread could get created and it could start * running before our parent is done initializing our state. * The mozilla thread will have done a PR_EnterMonitor() on * the lm_owner_mon before creating the mocha thread and will * not exit the monitor until all of the state is initialized. * So we are assured that if we can get the monitor here the * mozilla thread has released it and we are OK to run. */ PR_EnterMonitor(lm_owner_mon); PR_ExitMonitor(lm_owner_mon); /* set up the initial queue stack pointers */ et_TopQueue->queue = lm_InterpretQueue; /* create our monitor if it doesn't exist already */ if(lm_queue_monitor == NULL) { lm_queue_monitor = PR_NewNamedMonitor("lm-queue-monitor"); if(!lm_queue_monitor) return; } while (TRUE) { et_SubEventLoop(et_TopQueue); /* should never get here but just in case behave nicely */ XP_ASSERT(0); et_TopQueue->done = FALSE; } } /**********************************************************************/ typedef struct { ETEvent ce; char *name; ETBoolPtrFunc active_callback; ETVoidPtrFunc startup_callback; } RegComponentStruct; PR_STATIC_CALLBACK(void) et_registercomponent_handler(RegComponentStruct * e) { lm_RegisterComponent(e->name, e->active_callback, e->startup_callback); } PR_STATIC_CALLBACK(void) et_registercomponent_destructor(RegComponentStruct * e) { if (e->name) XP_FREE(e->name); XP_FREE(e); } void ET_RegisterComponent(char *name, void *active_callback, void *startup_callback) { /* create our event object */ RegComponentStruct * pEvent = XP_NEW_ZAP(RegComponentStruct); if(!pEvent) return; /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, NULL, (PRHandleEventProc)et_registercomponent_handler, (PRDestroyEventProc)et_registercomponent_destructor); /* fill in the non-PR fields we care about */ if (name) pEvent->name = XP_STRDUP(name); else pEvent->name = NULL; pEvent->active_callback = (ETBoolPtrFunc)active_callback; pEvent->startup_callback = (ETVoidPtrFunc)startup_callback; /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; char *comp; char *name; uint8 retType; void *setter; void *getter; } RegComponentPropStruct; PR_STATIC_CALLBACK(void) et_registercomponentprop_handler(RegComponentPropStruct * e) { lm_RegisterComponentProp(e->comp, e->name, e->retType, (ETCompPropSetterFunc)e->setter, (ETCompPropGetterFunc)e->getter); } PR_STATIC_CALLBACK(void) et_registercomponentprop_destructor(RegComponentPropStruct * e) { if (e->comp) XP_FREE(e->comp); if (e->name) XP_FREE(e->name); XP_FREE(e); } void ET_RegisterComponentProp(char *comp, char *name, uint8 retType, void *setter, void *getter) { /* create our event object */ RegComponentPropStruct * pEvent = XP_NEW_ZAP(RegComponentPropStruct); if(!pEvent) return; /* this won't work without a component and property name. */ if (!comp || !name) return; /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, NULL, (PRHandleEventProc)et_registercomponentprop_handler, (PRDestroyEventProc)et_registercomponentprop_destructor); /* fill in the non-PR fields we care about */ pEvent->comp = XP_STRDUP(comp); pEvent->name = XP_STRDUP(name); pEvent->retType = retType; pEvent->setter = setter; pEvent->getter = getter; /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); } /**********************************************************************/ typedef struct { ETEvent ce; char *comp; char *name; uint8 retType; void *method; int32 argc; } RegComponentMethodStruct; PR_STATIC_CALLBACK(void) et_registercomponentmethod_handler(RegComponentMethodStruct * e) { lm_RegisterComponentMethod(e->comp, e->name, e->retType, (ETCompMethodFunc)e->method, e->argc); } PR_STATIC_CALLBACK(void) et_registercomponentmethod_destructor(RegComponentMethodStruct * e) { if (e->comp) XP_FREE(e->comp); if (e->name) XP_FREE(e->name); XP_FREE(e); } void ET_RegisterComponentMethod(char *comp, char *name, uint8 retType, void *method, int32 argc) { /* create our event object */ RegComponentMethodStruct * pEvent = XP_NEW_ZAP(RegComponentMethodStruct); if(!pEvent) return; /* do a PR_InitEvent on the event structure */ PR_InitEvent(&pEvent->ce.event, NULL, (PRHandleEventProc)et_registercomponentmethod_handler, (PRDestroyEventProc)et_registercomponentmethod_destructor); /* fill in the non-PR fields we care about */ pEvent->comp = XP_STRDUP(comp); pEvent->name = XP_STRDUP(name); pEvent->retType = retType; pEvent->method = method; pEvent->argc = argc; /* add the event to the event queue */ et_event_to_mocha(&pEvent->ce); }