/* -*- 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"
#include "java.h"
#include "pa_tags.h"
#include "css.h"
#include "pa_parse.h"
#include "jsjava.h"
#include "intl_csi.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:
#ifdef JAVA
	LM_ReflectApplet(e->ce.context, (LO_JavaAppStruct *) e->lo_ele, 
	                 e->pa_tag, e->layer_id, e->index);
#endif
	    break;
    case LM_EMBEDS:
#ifdef JAVA
	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;
    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);

}

/**********************************************************************/

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

#ifdef 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;

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




