Milán Major

netscape-revival

1 branch
Code

sun-java/netscape/net/javactxt.c

/* -*- Mode: C; tab-width: 4; -*- */
/*
 * @(#)javactxt.c	1.2 95/06/14 Warren Harris
 *
 * Copyright (c) 1995 Netscape Communications Corporation. All Rights Reserved.
 *
 */

#include "nsn.h"
#include "prmem.h"
#include "prmon.h"
#include "prlog.h"
#include "prtime.h"
#include "prdump.h"

PR_LOG_DEFINE(JavaContext);

/******************************************************************************/
/*
** Here's what's going on here: We need a context to invoke NET_GetURL,
** but if we use the one available to us relative to the current thread
** (found via nsn_GetCurrentContext) we may deadlock or crash. This is
** because in some cases NET_GetURL does its work on the current thread
** rather than the mozilla thread causing the two to contend for the same
** dpy.
**
** To get around this, we make a dummy MWContext object (called a
** JavaContext) and set its fe callback functions to deliver events
** (defined in java.h) back to the mozilla thread to get the work
** done. We do this for things like GraphProgress, etc.
*/

typedef struct MozillaEvent_GraphProgressInit {
    MWContextEvent	ce;
    URL_Struct*		URL_s;
    int32			content_length;
} MozillaEvent_GraphProgressInit;

PR_STATIC_CALLBACK(void)
nsn_HandleEvent_GraphProgressInit(MozillaEvent_GraphProgressInit* e)
{
    e->ce.context->numberOfSimultaneousShowers++;
    PR_LOG(JavaContext, debug,
		   ("$$$ MOZILLAEVENT_GRAPHPROGRESSINIT: numberOfSimultaneousShowers = %d",
			e->ce.context->numberOfSimultaneousShowers));
    if (e->ce.context->numberOfSimultaneousShowers == 1) {
		e->ce.context->timeOfFirstMeteorShower = PR_Now();
    }
}

PRIVATE void
nsn_GraphProgressInit(MWContext  *context, 
		      URL_Struct *URL_s, 
		      int32       content_length)
{
    MozillaEvent_GraphProgressInit* event;
    MWContext* realContext;

    /* Don't look at the realContext until we've entered the monitor */
    PR_EnterMonitor(mozilla_event_queue->monitor);

    PR_ASSERT(context->type == MWContextJava);

    if (context->javaContextStreamData == NULL
		|| (realContext = context->javaContextStreamData->realContext) == NULL)
		goto done;
	
    event = PR_NEW(MozillaEvent_GraphProgressInit);
	if (event == NULL) return;
    PR_InitEvent(&event->ce.event,
				 (PRHandleEventProc)nsn_HandleEvent_GraphProgressInit,
				 (PRDestroyEventProc)free);
    event->ce.context = realContext;
    event->URL_s = /* URL_s */ NULL;
    event->content_length = content_length;
    PR_PostEvent(mozilla_event_queue, &event->ce.event);
    
  done:
    PR_ExitMonitor(mozilla_event_queue->monitor);
}

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

typedef struct MozillaEvent_GraphProgressDestroy {
    MWContextEvent	ce;
    URL_Struct*		URL_s;
    int32			content_length;
    int32			total_bytes_read;
} MozillaEvent_GraphProgressDestroy;

PR_STATIC_CALLBACK(void)
nsn_HandleEvent_GraphProgressDestroy(MozillaEvent_GraphProgressDestroy* e)
{
    /*
    ** e->URL_s is ALWAYS NULL.  This is because the URL was created by a 
    ** different thread. So by the time the event is processed, the URL may have
    ** been destroyed.
    */
    e->ce.context->numberOfSimultaneousShowers--;
    PR_LOG(JavaContext, debug,
		   ("$$$ MOZILLAEVENT_GRAPHPROGRESSDESTROY: numberOfSimultaneousShowers = %d",
			e->ce.context->numberOfSimultaneousShowers));
    PR_ASSERT(e->ce.context->numberOfSimultaneousShowers >= 0);
    if (e->ce.context->numberOfSimultaneousShowers == 0 && e->ce.context->displayingMeteors) {
		e->ce.context->displayingMeteors = PR_FALSE;
		PR_LOG(JavaContext, debug, ("GraphProgressDestroy"));
		FE_GraphProgressDestroy(e->ce.context, /* e->URL_s */ NULL,
								e->content_length,
								e->total_bytes_read);
		/*
		** And it seems that if we don't call FE_EnableClicking about
		** now, those meteors never will stop tumbling.
		*/
		FE_EnableClicking(e->ce.context);
    }
}

PRIVATE void
nsn_GraphProgressDestroy(MWContext  *context, 
			 URL_Struct *URL_s, 
			 int32       content_length,
			 int32       total_bytes_read)
{
    MozillaEvent_GraphProgressDestroy* event;
    MWContext* realContext;

    /* Don't look at the realContext until we've entered the monitor */
    PR_EnterMonitor(mozilla_event_queue->monitor);

    PR_ASSERT(context->type == MWContextJava);

    if (context->javaContextStreamData == NULL
		|| (realContext = context->javaContextStreamData->realContext) == NULL)
		goto done;

    event = PR_NEW(MozillaEvent_GraphProgressDestroy);
	if (event == NULL) return;
    PR_InitEvent(&event->ce.event, 
				 (PRHandleEventProc)nsn_HandleEvent_GraphProgressDestroy,
				 (PRDestroyEventProc)free); 
    event->ce.context = realContext;
    event->URL_s = /* URL_s */ NULL;
    event->content_length = content_length;
    event->total_bytes_read = total_bytes_read;
    PR_PostEvent(mozilla_event_queue, &event->ce.event);
  done:
    PR_ExitMonitor(mozilla_event_queue->monitor);
}

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

typedef struct MozillaEvent_GraphProgress {
    MWContextEvent	ce;
    URL_Struct*		URL_s;
    int32			bytes_received;
    int32       	bytes_since_last_time;
    int32       	content_length;
} MozillaEvent_GraphProgress;

PR_STATIC_CALLBACK(void)
nsn_HandleEvent_GraphProgress(MozillaEvent_GraphProgress* e)
{
    /*
    ** I think I figured this out. For some reason, lots of
    ** subsystems called from netlib (mail, news, ftp, etc.)  will
    ** call FE_SetProgressBarPercent -- but not http, and this is
    ** the main one we care about for java's netlib stubs. So we'll
    ** just fake it out by calculating our own percentage and
    ** setting that. Why FE_GraphProgress just doen't work this way
    ** to begin with will remain a complete and total mystery.
    **
    ** e->URL_s is ALWAYS NULL.  This is because the URL was created by a 
    ** different thread. So by the time the event is processed, the URL may have
    ** been destroyed.
    */
    int32 percent;
    if (e->content_length == 0) return;	/* it happens */
    percent = e->bytes_received * 100 / e->content_length;
    PR_LOG(JavaContext, debug, ("$$$ MOZILLAEVENT_GRAPHPROGRESS: percent = %d", percent));
    if (!e->ce.context->displayingMeteors) {
		/*
		** Heuristics... ohmygod. I'm only looking at the 10-70%
		** case because I'm trying to filter off initial transients
		** and the case where we're almost done anyway.
		*/
		if (percent > 10 && percent < 70) {
			int64 now = PR_Now();
			int64 delta, tmp, dpct, eta, waitTime;
				
			LL_SUB(delta, now, e->ce.context->timeOfFirstMeteorShower);
			LL_I2L(tmp, 100);
			LL_MUL(dpct, delta, tmp);	/* dpct = delta*100 */
			LL_I2L(tmp, percent);
			LL_DIV(eta, dpct, tmp);	/* eta = dpct/percent */
			LL_I2L(waitTime, 1000000L);	/* 1 sec */
#ifdef DEBUG_GRAPH
			{
				char buf[1024];
				size_t buflen = 1024;
				size_t count = 0;
				count += PR_snprintf(buf[count], buflen-count,
									 "GraphProgress: count=%d, %%=%d, delta=", 
									 e->ce.context->numberOfSimultaneousShowers, percent);
				count += PR_PrintMicrosecondsToBuf(buf[count], buflen-count, delta);
				count += PR_snprintf(buf[count], buflen-count, ", eta=");
				count += PR_PrintMicrosecondsToBuf(buf[count], buflen-count, eta);
				PR_LOG(JavaContext, debug, (buf));
			}
#endif /* DEBUG_GRAPH */
			if (LL_CMP(eta, >, waitTime)) {
				PR_LOG(JavaContext, debug, ("GraphProgressInit"));
				FE_GraphProgressInit(e->ce.context, /* e->URL_s */ NULL, e->content_length);
				e->ce.context->displayingMeteors = PR_TRUE;
			}
		}
    }
    else {
		FE_SetProgressBarPercent(e->ce.context, percent);
		FE_GraphProgress(e->ce.context, /* e->URL_s */ NULL, e->bytes_received,
						 e->bytes_since_last_time, e->content_length);
    }
}

PRIVATE void
nsn_GraphProgress(MWContext  *context, 
		  URL_Struct *URL_s, 
		  int32       bytes_received,
		  int32       bytes_since_last_time,
		  int32       content_length)
{
    MozillaEvent_GraphProgress* event;
    MWContext* realContext;

    /* Don't look at the realContext until we've entered the monitor */
    PR_EnterMonitor(mozilla_event_queue->monitor);

    PR_ASSERT(context->type == MWContextJava);

    if (context->javaContextStreamData == NULL
		|| (realContext = context->javaContextStreamData->realContext) == NULL)
		goto done;

    event = PR_NEW(MozillaEvent_GraphProgress);
	if (event == NULL) return;
    PR_InitEvent(&event->ce.event,
				 (PRHandleEventProc)nsn_HandleEvent_GraphProgress,
				 (PRDestroyEventProc)free); 
    event->ce.context = realContext;
    event->URL_s = /* URL_s */ NULL;
    event->bytes_received = bytes_received;
    event->bytes_since_last_time = bytes_since_last_time;
    event->content_length = content_length;
    PR_PostEvent(mozilla_event_queue, &event->ce.event);
  done:
    PR_ExitMonitor(mozilla_event_queue->monitor);
}

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

PRIVATE void
nsn_Progress(MWContext *context, const char* msg)
{
    MWContext* realContext;

    /* Don't look at the realContext until we've entered the monitor */
    PR_EnterMonitor(mozilla_event_queue->monitor);

    PR_ASSERT(context->type == MWContextJava);

    if (context->javaContextStreamData == NULL
	|| (realContext = context->javaContextStreamData->realContext) == NULL)
	goto done;

    LJ_PostShowStatus(realContext, (char*)msg);
  done:
    PR_ExitMonitor(mozilla_event_queue->monitor);
}

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

typedef struct MozillaEvent_SetProgressBarPercent {
    MWContextEvent	ce;
    int32			percent;
} MozillaEvent_SetProgressBarPercent;

PR_STATIC_CALLBACK(void)
nsn_HandleEvent_SetProgressBarPercent(MozillaEvent_SetProgressBarPercent* e)
{
    FE_SetProgressBarPercent(e->ce.context, e->percent);
}

PRIVATE void
nsn_SetProgressBarPercent(MWContext *context, int32 percent)
{
    MozillaEvent_SetProgressBarPercent* event;
    MWContext* realContext;

    /* Don't look at the realContext until we've entered the monitor */
    PR_EnterMonitor(mozilla_event_queue->monitor);

    PR_ASSERT(context->type == MWContextJava);

    if (context->javaContextStreamData == NULL
	|| (realContext = context->javaContextStreamData->realContext) == NULL)
	goto done;

    event = PR_NEW(MozillaEvent_SetProgressBarPercent);
	if (event == NULL) return;
    PR_InitEvent(&event->ce.event,
				 (PRHandleEventProc)nsn_HandleEvent_SetProgressBarPercent,
				 (PRDestroyEventProc)free); 
    event->ce.context = realContext;
    event->percent = percent;
    PR_PostEvent(mozilla_event_queue, &event->ce.event);
  done:
    PR_ExitMonitor(mozilla_event_queue->monitor);
}

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

PR_STATIC_CALLBACK(void)
nsn_AllConnectionsComplete(MWContext* context)
{
    nsn_JavaContext_destroy(context);
    DECR(streamCount);
}

/*
** And the rest of them are no-ops. Define them with Michael's fancy
** mk_cx_fn.h header:
*/

PRIVATE int
nsn_noop(int x, ...)
{
    return 0;
}

#define MAKE_FE_TYPES_PREFIX(func)	func##_t
#define MAKE_FE_FUNCS_TYPES
#include "java.h"
#include "mk_cx_fn.h"
#undef MAKE_FE_FUNCS_TYPES

#define nsn_CreateNewDocWindow		(CreateNewDocWindow_t)nsn_noop
#define nsn_LayoutNewDocument		(LayoutNewDocument_t)nsn_noop
#define nsn_SetDocTitle			(SetDocTitle_t)nsn_noop
#define nsn_FinishedLayout		(FinishedLayout_t)nsn_noop
#define nsn_TranslateISOText		(TranslateISOText_t)nsn_noop
#define nsn_GetTextInfo			(GetTextInfo_t)nsn_noop
#define nsn_GetImageInfo		(GetImageInfo_t)nsn_noop
#define nsn_GetEmbedSize		(GetEmbedSize_t)nsn_noop
#define nsn_GetJavaAppSize		(GetJavaAppSize_t)nsn_noop
#define nsn_GetFormElementInfo		(GetFormElementInfo_t)nsn_noop
#define nsn_GetFormElementValue		(GetFormElementValue_t)nsn_noop
#define nsn_ResetFormElement		(ResetFormElement_t)nsn_noop
#define nsn_SetFormElementToggle	(SetFormElementToggle_t)nsn_noop
#define nsn_FreeFormElement		(FreeFormElement_t)nsn_noop
#define nsn_FreeImageElement		(FreeImageElement_t)nsn_noop
#define nsn_FreeEmbedElement		(FreeEmbedElement_t)nsn_noop
#define nsn_FreeJavaAppElement		(FreeJavaAppElement_t)nsn_noop
#define nsn_HideJavaAppElement		(HideJavaAppElement_t)nsn_noop
#define nsn_FreeEdgeElement		(FreeEdgeElement_t)nsn_noop
#define nsn_FormTextIsSubmit		(FormTextIsSubmit_t)nsn_noop
#define nsn_DisplaySubtext		(DisplaySubtext_t)nsn_noop
#define nsn_DisplayText			(DisplayText_t)nsn_noop
#define nsn_DisplayImage		(DisplayImage_t)nsn_noop
#define nsn_DisplayEmbed		(DisplayEmbed_t)nsn_noop
#define nsn_DisplayJavaApp		(DisplayJavaApp_t)nsn_noop
#define nsn_DisplaySubImage		(DisplaySubImage_t)nsn_noop
#define nsn_DisplayEdge			(DisplayEdge_t)nsn_noop
#define nsn_DisplayTable		(DisplayTable_t)nsn_noop
#define nsn_DisplayCell			(DisplayCell_t)nsn_noop
#define nsn_DisplaySubDoc		(DisplaySubDoc_t)nsn_noop
#define nsn_DisplayLineFeed		(DisplayLineFeed_t)nsn_noop
#define nsn_DisplayHR			(DisplayHR_t)nsn_noop
#define nsn_DisplayBullet		(DisplayBullet_t)nsn_noop
#define nsn_DisplayFormElement		(DisplayFormElement_t)nsn_noop
#define nsn_ClearView			(ClearView_t)nsn_noop
#define nsn_SetDocDimension		(SetDocDimension_t)nsn_noop
#define nsn_SetDocPosition		(SetDocPosition_t)nsn_noop
#define nsn_GetDocPosition		(GetDocPosition_t)nsn_noop
#define nsn_BeginPreSection		(BeginPreSection_t)nsn_noop
#define nsn_EndPreSection		(EndPreSection_t)nsn_noop
#if 0
#define nsn_SetProgressBarPercent	(SetProgressBarPercent_t)nsn_noop
#endif
#define nsn_SetBackgroundColor		(SetBackgroundColor_t)nsn_noop
#if 0	/* above */
#define nsn_Progress			(Progress_t)nsn_noop
#endif
#define nsn_Alert			(Alert_t)nsn_noop
#define nsn_SetCallNetlibAllTheTime	(SetCallNetlibAllTheTime_t)nsn_noop
#define nsn_ClearCallNetlibAllTheTime	(ClearCallNetlibAllTheTime_t)nsn_noop
#if 0	/* above */
#define nsn_GraphProgressInit		(GraphProgressInit_t)nsn_noop
#define nsn_GraphProgressDestroy	(GraphProgressDestroy_t)nsn_noop
#define nsn_GraphProgress		(GraphProgress_t)nsn_noop
#endif
#define nsn_UseFancyFTP			(UseFancyFTP_t)nsn_noop
#define nsn_UseFancyNewsgroupListing	(UseFancyNewsgroupListing_t)nsn_noop
#define nsn_FileSortMethod		(FileSortMethod_t)nsn_noop
#define nsn_ShowAllNewsArticles		(ShowAllNewsArticles_t)nsn_noop
#define nsn_Confirm			(Confirm_t)nsn_noop
#define nsn_Prompt			(Prompt_t)nsn_noop
#define nsn_PromptUsernameAndPassword	(PromptUsernameAndPassword_t)nsn_noop
#define nsn_PromptPassword		(PromptPassword_t)nsn_noop
#define nsn_EnableClicking		(EnableClicking_t)nsn_noop
#define nsn_ImageSize			(ImageSize_t)nsn_noop
#define nsn_ImageData			(ImageData_t)nsn_noop
#define nsn_ImageIcon			(ImageIcon_t)nsn_noop
#define nsn_ImageOnScreen		(ImageOnScreen_t)nsn_noop
#define nsn_SetColormap			(SetColormap_t)nsn_noop
#if 0	/* above */
#define nsn_AllConnectionsComplete	(AllConnectionsComplete_t)nsn_noop
#endif

/* Just reuse the same set of context functions: */
ContextFuncs nsn_JavaContextFuncs;

#define JCX_LOCK() PR_CEnterMonitor(&nsn_JavaContextFuncs)
#define JCX_UNLOCK() PR_CExitMonitor(&nsn_JavaContextFuncs)

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

MWContext*
nsn_JavaContext_make(URL_Struct* url, nsn_JavaStreamData* javaData)
{
    MWContext* context;
    static PRBool funcsInitialized = PR_FALSE;

    if (!funcsInitialized) {
	/* Initialize our statically allocated context funcs table: */
	#define MAKE_FE_FUNCS_PREFIX(f) nsn_##f
	#define MAKE_FE_FUNCS_ASSIGN nsn_JavaContextFuncs.
	#include "mk_cx_fn.h"

	funcsInitialized = PR_TRUE;
    }

    context = PR_NEW(MWContext);
    if (!context) return NULL;
    XP_MEMSET(context, 0, sizeof(MWContext));
    context->type = MWContextJava;
    context->prInfo = NULL;
    context->prSetup = NULL;
    context->funcs = &nsn_JavaContextFuncs;

    context->convertPixX = 1;	/* XXX needed? */
    context->convertPixY = 1;	/* XXX needed? */

    context->is_grid_cell = FALSE;

    context->javaContextStreamData = javaData;

    PR_INIT_CLIST(&context->javaContexts);
    /*
    ** Link us into the javaContexts so that if the realContext goes
    ** away, we can cope (see structs.h and lj_embed.c). The realContext
    ** might be null if we were not in an AppletThreadGroup
    ** (e.g. AWT-Motif).
    */
    if (javaData->realContext != NULL) {
	JCX_LOCK();
	PR_APPEND_LINK(&context->javaContexts, &javaData->realContext->javaContexts);
	JCX_UNLOCK();
    }

    INCR(contextCount);
    return context;
}

void
nsn_JavaContext_destroy(MWContext* context)
{
    
    PR_EnterMonitor(mozilla_event_queue->monitor);
    JCX_LOCK();
    PR_ASSERT(context->type == MWContextJava);
    PR_REMOVE_AND_INIT_LINK(&context->javaContexts);
    if( context->javaContextStreamData ) {
        context->javaContextStreamData->javaContext = NULL;
    }
    JCX_UNLOCK();
    NSN_FREE(context);
    DECR(contextCount);
    PR_ExitMonitor(mozilla_event_queue->monitor);
}

/*
** Break the links between the real window contexts and
** dummy contexts created for java's network connections. This
** prevents subsequent Progress events from happening on this context
** that's going away.
*/
void nsn_JavaContext_breakLinkage(MWContext *realContext)
{
    JCX_LOCK();
	PR_ASSERT(realContext->type != MWContextJava);
    if (realContext->javaContexts.next == NULL) {
	/*
	** Defensive: in case we get here before the history code has
	** initialized the linkage. This can happen on certain kinds
	** of contexts (e.g. a ShowDocument window).
	*/
    } else {
	while (!PR_CLIST_IS_EMPTY(&realContext->javaContexts)) {
	    MWContext* javaContext = 
		MWCONTEXT_PTR(PR_LIST_HEAD(&realContext->javaContexts));
	    PR_REMOVE_AND_INIT_LINK(&javaContext->javaContexts);
	    JCX_UNLOCK();
	    nsn_CloseJavaConnection(javaContext);
	    JCX_LOCK();
	}
    }
    JCX_UNLOCK();
}

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