/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*
* JS in the Navigator top-level stuff.
*
* Brendan Eich, 9/8/95
*/
#include "lm.h"
#ifdef DOM
#include "lm_dom.h"
#endif
#include "xp.h"
#include "net.h"
#include "structs.h"
#include "layout.h" /* for lo_FormData */
#include "pa_tags.h" /* included via -I../libparse */
#include "prmem.h"
#include "prthread.h"
#include "prmon.h"
#ifdef XP_MAC
#include "pprthred.h" /* for PR_CreateThreadGCAble */
#else
#include "private/pprthred.h"
#endif
#if defined (JAVA)
#include "jri.h"
#include "jriext.h"
#include "java.h"
#elif defined (OJI)
/*
#include "jni.h"
#include "np2.h"
#include "jsjava.h"
*/
#include "jvmmgr.h"
#endif
#include "prefapi.h"
#include "libi18n.h"
#include "intl_csi.h"
#define UNICODE /* remove after working everywhere */
extern PRThread *mozilla_thread;
char js_language_name[] = "JavaScript";
char js_content_type[] = APPLICATION_JAVASCRIPT;
char lm_argc_err_str[] = "incorrect number of arguments";
char lm_onLoad_str[] = PARAM_ONLOAD;
char lm_onUnload_str[] = PARAM_ONUNLOAD;
char lm_onAbort_str[] = PARAM_ONABORT;
char lm_onError_str[] = PARAM_ONERROR;
char lm_onScroll_str[] = PARAM_ONSCROLL;
char lm_onFocus_str[] = PARAM_ONFOCUS;
char lm_onBlur_str[] = PARAM_ONBLUR;
char lm_onSelect_str[] = PARAM_ONSELECT;
char lm_onChange_str[] = PARAM_ONCHANGE;
char lm_onReset_str[] = PARAM_ONRESET;
char lm_onSubmit_str[] = PARAM_ONSUBMIT;
char lm_onClick_str[] = PARAM_ONCLICK;
char lm_onMouseDown_str[] = PARAM_ONMOUSEDOWN;
char lm_onMouseOver_str[] = PARAM_ONMOUSEOVER;
char lm_onMouseOut_str[] = PARAM_ONMOUSEOUT;
char lm_onMouseUp_str[] = PARAM_ONMOUSEUP;
char lm_onLocate_str[] = PARAM_ONLOCATE;
char lm_onHelp_str[] = PARAM_ONHELP;
char lm_focus_str[] = "focus";
char lm_blur_str[] = "blur";
char lm_select_str[] = "select";
char lm_click_str[] = "click";
char lm_scroll_str[] = "scroll";
char lm_enable_str[] = "enable";
char lm_disable_str[] = "disable";
char lm_toString_str[] = "toString";
char lm_length_str[] = "length";
char lm_document_str[] = "document";
char lm_forms_str[] = "forms";
char lm_links_str[] = "links";
char lm_anchors_str[] = "anchors";
char lm_plugins_str[] = "plugins";
char lm_applets_str[] = "applets";
char lm_embeds_str[] = "embeds";
char lm_images_str[] = "images";
char lm_layers_str[] = "layers";
char lm_builtins_str[] = "trees";
char lm_location_str[] = "location";
char lm_navigator_str[] = "navigator";
char lm_netcaster_str[] = "netcaster";
char lm_components_str[] = "components";
char lm_parentLayer_str[] = "parentLayer";
char lm_opener_str[] = "opener";
char lm_closed_str[] = "closed";
char lm_assign_str[] = "assign";
char lm_reload_str[] = "reload";
char lm_replace_str[] = "replace";
char lm_event_str[] = "event";
char lm_methodPrefix_str[] = "#method";
char lm_methodArgc_str[] = "#margc";
char lm_methodArgv_str[] = "#margv";
char lm_getPrefix_str[] = "#get_";
char lm_setPrefix_str[] = "#set_";
char lm_typePrefix_str[] = "#type_";
const char *lm_event_argv[] = {lm_event_str};
JSRuntime *lm_runtime;
static uint32 lm_max_gc_bytes = 4L * 1024L * 1024L; /* XXX use a pref */
#ifdef OJI
JNIEnv *lm_JSEnv; /* Java env for lm_InterpretThread */
#elif defined(JAVA)
JRIEnv *lm_JSEnv; /* Java env for lm_InterpretThread */
#endif /* ! (OJI || JAVA) */
#ifdef JAVA
extern LJ_JSJ_Init(void);
#endif
static JSClass lm_dummy_class = {
"Dummy", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
JSObject *lm_DummyObject;
MochaDecoder *crippled_decoder;
JSContext *crippled_context; /* exported to jsStubs.c */
MochaDecoder *LM_GetCrippledDecoder()
{
JS_SetContextThread(crippled_decoder->js_context);
return crippled_decoder;
}
void LM_SetCrippledDecoder(MochaDecoder *md)
{
crippled_decoder = md;
if(crippled_decoder) {
crippled_context = crippled_decoder->js_context;
}
}
JSContext *LM_GetCrippledContext()
{
JS_SetContextThread(crippled_decoder->js_context);
return crippled_context;
}
PR_STATIC_CALLBACK(JSBool)
lm_alert(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
MochaDecoder *decoder;
MWContext *context;
JSString *arg;
decoder = JS_GetInstancePrivate(cx, obj, &lm_window_class, argv);
if (!decoder)
return JS_TRUE;
if (!(arg = JS_ValueToString(cx, argv[0])))
return JS_FALSE;
context = decoder->window_context;
if (context) {
char * message;
message = lm_StrToLocalEncoding(context, arg);
ET_PostMessageBox(context, message, JS_FALSE);
XP_FREEIF(message);
}
return JS_TRUE;
}
#ifdef DEBUG
#include "jscntxt.h"
PR_STATIC_CALLBACK(JSBool)
lm_tracing(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSBool bval;
if (argc == 0) {
*rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
return JS_TRUE;
}
if (JSVAL_IS_INT(argv[0])) {
if (JSVAL_TO_INT(argv[0]) != 0)
bval = JS_TRUE;
} else if (JSVAL_IS_BOOLEAN(argv[0])) {
bval = JSVAL_TO_BOOLEAN(argv[0]);
} else {
return JS_TRUE; /* XXX should be error */
}
if (cx->tracefp)
fclose(cx->tracefp);
cx->tracefp = bval ? fopen("/dev/tty", "w") : 0;
return JS_TRUE;
}
#endif /* DEBUG */
static JSFunctionSpec lm_global_functions[] = {
{"alert", lm_alert, 0},
#ifdef DEBUG
{"tracing", lm_tracing, 0},
#endif
{0}
};
/*
* If we don't get passed an object, assume we are supposed to use
* the main window object
*/
JSBool
lm_CompileEventHandler(MochaDecoder * decoder, PA_Block id, PA_Block all,
int newline_count, JSObject *object,
const char *name, PA_Block block)
{
JSPrincipals *principals, *registered;
JSContext *cx;
char *body, *p;
JSBool ok;
JSString *unicode;
cx = decoder->js_context;
if (object == NULL)
object = decoder->window_object;
principals = lm_GetCompilationPrincipals(decoder, NULL);
if (principals == NULL)
return JS_FALSE;
JSPRINCIPALS_HOLD(cx, principals);
body = (char *) block;
/* Find block in all and subtract newlines in and after block
from newline_count */
if (all) {
/*
* XXX - We really should fix it so that "all" is always
* nonnull.
*/
for (p=(char *) all; *p; p++) {
if (*p == '\r' || *p == '\n') {
p = XP_STRSTR((char *) all, body);
if (p == NULL)
break;
/*
* XXX - doesn't handle case where there are two
* handlers with the same text
*/
while (*p) {
switch (*p) {
case '\r':
if (p[1] == '\n')
p++;
/* fall through */
case '\n':
newline_count--;
break;
default:
break;
}
p++;
}
break;
}
}
}
registered = LM_RegisterPrincipals(decoder, principals,
(char *) id, (char *) all);
unicode = lm_LocalEncodingToStr(decoder->window_context, body);
JS_LockGCThing(cx, unicode);
ok = (JSBool)(registered &&
JS_CompileUCFunctionForPrincipals(cx, object,
registered,
name, 1, lm_event_argv,
JS_GetStringChars(unicode), JS_GetStringLength(unicode),
LM_GetSourceURL(decoder),
newline_count + 1)
!= NULL);
JSPRINCIPALS_DROP(cx, principals);
JS_UnlockGCThing(cx, unicode);
return ok;
}
#define INTERRUPT_BRANCH_COUNT_MASK 255
#define MAYBE_GC_BRANCH_COUNT_MASK 4095
#ifdef XP_UNIX
static uint32 lm_handling_event;
#endif
JSBool PR_CALLBACK
lm_BranchCallback(JSContext *cx, JSScript *script)
{
static uint32 count = 0;
/*
* If we have been running for a while yield and see if anyone
* else is waiting for a time slice or is trying to stop us.
* At a much less frequent interval, force a GC to catch any garbage
* created by long-running or long-resident scripts.
*/
if ((++count & INTERRUPT_BRANCH_COUNT_MASK) == 0) {
MochaDecoder *decoder;
MWContext *context;
PR_Sleep(PR_INTERVAL_NO_WAIT);
/* check to see if we've been stopped */
decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
context = decoder->window_context;
if (!ET_ContinueProcessing(context)) {
#ifdef DEBUG_chouck
XP_TRACE(("Interrupted in branch callback"));
#endif
return JS_FALSE;
}
/* if not stopped, go look for garbage */
if ((count & MAYBE_GC_BRANCH_COUNT_MASK) == 0) {
#ifdef XP_UNIX
/* X lacks an idle loop from which to do this opportunistically. */
if (lm_handling_event && context)
ET_moz_CallFunction((ETVoidPtrFunc)FE_UpdateStopState, context);
#endif /* XP_UNIX */
JS_MaybeGC(cx);
}
}
return JS_TRUE;
}
#define ERROR_REPORT_LIMIT 10
static JSBool
lm_lookup_event_handler(JSContext *cx, JSObject *obj, uint32 type, jsval *fvp);
void PR_CALLBACK
lm_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
JSObject *obj;
MochaDecoder *decoder;
jsval fval, rval, argv[3];
JSString *str;
JSBool ok;
char *last;
int i, j, k, n;
const char *s, *t;
#ifdef JAVA
JSBool java_errors = JS_TRUE;
#else
JSBool java_errors = JS_FALSE;
#endif /* JAVA */
MWContext *context;
#ifdef XP_WIN16
java_errors = JS_FALSE;
return;
#endif
obj = JS_GetGlobalObject(cx);
decoder = JS_GetPrivate(cx, obj);
if (!decoder)
return;
context = decoder->window_context;
if (context && context->type == MWContextPrint)
return;
JS_SetErrorReporter(cx, NULL);
if (lm_lookup_event_handler(cx, obj, EVENT_ERROR, &fval)) {
if (fval == JSVAL_NULL) {
/* User has set onerror to null, so cancel this report. */
goto out;
}
if (JS_TypeOfValue(cx, fval) == JSTYPE_FUNCTION) {
str = JS_NewStringCopyZ(cx, message);
if (!str)
goto out;
argv[0] = STRING_TO_JSVAL(str);
str = JS_NewStringCopyZ(cx, report ? report->filename : "");
if (!str)
goto out;
argv[1] = STRING_TO_JSVAL(str);
argv[2] = INT_TO_JSVAL(report ? report->lineno : 0);
#ifdef XP_UNIX
lm_handling_event++;
#endif
ok = JS_CallFunctionValue(cx, obj, fval, 3, argv, &rval);
#ifdef XP_UNIX
lm_handling_event--;
#endif
if (!ok) {
/* Error during onerror, message is probably free now. */
goto out;
}
if (rval == JSVAL_TRUE) {
/* True return value means the function reported this error. */
goto out;
}
}
}
decoder->error_count++;
if (decoder->error_count >= ERROR_REPORT_LIMIT) {
if (decoder->error_count == ERROR_REPORT_LIMIT) {
last = PR_smprintf("too many %s errors", js_language_name);
if (last) {
ET_PostMessageBox(decoder->window_context, last, JS_FALSE);
XP_FREE(last);
}
}
goto out;
}
last = PR_sprintf_append(NULL,
java_errors
? "java.lang.System.err.println(\"%s Error: "
: "\\\n%s Error: ",
js_language_name);
if (!report) {
last = PR_sprintf_append(last,
java_errors
? "%s\\\n"
: "%s\n",
message);
}
else {
if (report->filename)
last = PR_sprintf_append(last, "%s, ", report->filename);
if (report->lineno) {
last = PR_sprintf_append(last,
java_errors
? "line %u:\\\n"
: "line %u:",
report->lineno);
}
last = PR_sprintf_append(last,
java_errors
? " %s."
: "
%s.
", message); if (report->linebuf) { if (java_errors) last = PR_sprintf_append(last, "\\\n"); for (s = t = report->linebuf; *s != '\0'; s = t) { for (; t != report->tokenptr && *t != '<' && *t != '\0'; t++) ; last = PR_sprintf_append(last, "%.*s", t - s, s); if (*t == '\0') break; if (java_errors) { last = PR_sprintf_append(last, (*t == '\"') ? "\\\"" : "%c", *t); } else { if (t == report->tokenptr) { last = PR_sprintf_append(last, "" ""); } last = PR_sprintf_append(last, (*t == '<') ? "<" : "%c", *t); } t++; } if (java_errors) { last = PR_sprintf_append(last, "\\\n"); } else { last = PR_sprintf_append(last, "\n"); n = report->tokenptr - report->linebuf; for (i = j = 0; i < n; i++) { if (report->linebuf[i] == '\t') { for (k = (j + 8) & ~7; j < k; j++) last = PR_sprintf_append(last, "."); continue; } last = PR_sprintf_append(last, "."); j++; } last = PR_sprintf_append(last, java_errors ? "^" : "^"); } } if (!java_errors) last = PR_sprintf_append(last, "\n"); } if (java_errors) last = PR_sprintf_append(last, "\");"); if (last) { if (java_errors) { JSContext *crc = LM_GetCrippledContext(); JS_EvaluateScript(crc, JS_GetGlobalObject(crc), last, strlen(last), NULL, 0, &rval); } else { if (context) ET_MakeHTMLAlert(context, last); } XP_FREE(last); } out: JS_SetErrorReporter(cx, lm_ErrorReporter); } static MochaDecoder * lm_new_decoder(JSRuntime *rt, JSClass *clasp) { JSContext *cx; MochaDecoder *decoder; JSObject *obj; decoder = XP_NEW_ZAP(MochaDecoder); if (!decoder) return NULL; /* XXX begin common */ cx = JS_NewContext(rt, LM_STACK_SIZE); if (!cx) { XP_DELETE(decoder); return NULL; } JS_BeginRequest(cx); JS_SetGCCallback(cx, LM_ShouldRunGC); decoder->forw_count = 1; decoder->js_context = cx; JS_SetBranchCallback(cx, lm_BranchCallback); JS_SetErrorReporter(cx, lm_ErrorReporter); obj = JS_NewObject(cx, clasp, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, decoder)) { JS_EndRequest(cx); JS_DestroyContext(cx); XP_DELETE(decoder); return NULL; } /* If this decoder ever needs to hold anonymous images, then call lm_NewImageContext with a NULL context and use IL_SetDisplayMode to set the context later. */ decoder->image_context = 0; decoder->window_context = 0; decoder->window_object = obj; JS_AddNamedRoot(cx, &decoder->window_object, "window_object"); JS_SetGlobalObject(cx, obj); /* XXX end common */ JS_DefineFunctions(cx, obj, lm_global_functions); JS_EndRequest(cx); return decoder; } /* * Enable or disable local JS decoding. */ static XP_Bool lm_enabled = TRUE; static XP_Bool lm_enabledMailNews = TRUE; static XP_Bool lm_enabledSigning = TRUE; static XP_Bool lm_enabledCrossOrigin = TRUE; static XP_Bool lm_enabledUnsignedExecution = TRUE; /* * Is this window enbled to do JS? */ JSBool LM_CanDoJS(MWContext *context) { PRBool forceJSEnabled; if (context) { forceJSEnabled = (JSBool)context->forceJSEnabled; } else { forceJSEnabled = PR_FALSE; } /* No JS for Editor unless forced on * (e.g., for Composer Plugins, which uses JS to access preferences) */ if (!forceJSEnabled && (!LM_GetMochaEnabled() || EDT_IS_EDITOR(context))) { return JS_FALSE; } switch (context->type) { case MWContextBrowser: case MWContextDialog: case MWContextPane: /* DSR101097 - OS/2 must follow NT w/ this or hangs printing large pages w/ JS */ #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_OS2) /* only alow JS to run in windows print mode * don't know about the safty of other platforms yet */ case MWContextPrint: #endif /* XP_WIN */ return JS_TRUE; case MWContextMail: case MWContextNews: case MWContextMailMsg: case MWContextNewsMsg: case MWContextMessageComposition: return (JSBool) lm_enabledMailNews; default: return JS_FALSE; } } /* * Is JS globally enabled? */ JSBool LM_GetMochaEnabled(void) { return (JSBool) lm_enabled; } /* * Is cross-origin access enabled? */ JSBool lm_GetCrossOriginEnabled() { return (JSBool) lm_enabledCrossOrigin; } /* * Is execution of unsigned scripts enabled? */ JSBool lm_GetUnsignedExecutionEnabled() { return (JSBool) lm_enabledUnsignedExecution; } static char lm_jsEnabled[] = "javascript.enabled"; static char lm_jsEnabledMN[] = "javascript.allow.mailnews"; static char lm_jsEnabledSigning[] = "javascript.allow.signing"; static char lm_jsEnabledCrossOrigin[] = "javascript.allow.crossOrigin"; static JSBool mochaInited = JS_FALSE; static char lm_jsEnabledUnsignedExecution[] = "javascript.allow.unsignedExecution"; PR_STATIC_CALLBACK(int) lm_PrefChangedFunc(const char *pref, void *data) { PREF_GetBoolPref(lm_jsEnabled, &lm_enabled); PREF_GetBoolPref(lm_jsEnabledMN, &lm_enabledMailNews); PREF_GetBoolPref(lm_jsEnabledSigning, &lm_enabledSigning); PREF_GetBoolPref(lm_jsEnabledCrossOrigin, &lm_enabledCrossOrigin); PREF_GetBoolPref(lm_jsEnabledUnsignedExecution, &lm_enabledUnsignedExecution); /* * If we started up w/ JS turned off we will have not bothered * the separate thread and event queues, do it now */ /* MLM - Use the boolean mochaInited instead of checking for the thread. */ if (!mochaInited && lm_enabled) LM_InitMocha(); return PREF_NOERROR; } /* has mocha been inited? MLM */ JSBool lm_inited(void) { return mochaInited; } /* * create the mocha thread, event queues, and stream converters */ static void lm_ReallyInitMocha(void) { /* register callback in case pref changes while we're running */ PREF_RegisterCallback(lm_jsEnabled, lm_PrefChangedFunc, NULL); PREF_RegisterCallback(lm_jsEnabledMN, lm_PrefChangedFunc, NULL); PREF_RegisterCallback(lm_jsEnabledSigning, lm_PrefChangedFunc, NULL); PREF_RegisterCallback(lm_jsEnabledCrossOrigin, lm_PrefChangedFunc, NULL); PREF_RegisterCallback(lm_jsEnabledUnsignedExecution, lm_PrefChangedFunc, NULL); if ( mochaInited ) { return; } lm_runtime = JS_Init(lm_max_gc_bytes); if (!lm_runtime) return; mochaInited = JS_TRUE; /* Initialize a crippled decoder/context for use by Java. */ crippled_decoder = lm_new_decoder(lm_runtime, &lm_dummy_class); crippled_context = crippled_decoder->js_context; /* Initialize a dummy object used for unreflectable applets and embeds. */ lm_DummyObject = crippled_decoder->window_object; /* Associate a JS netlib "converter" with our mime type. */ /* Cool, we are still in the mozilla thread at this point so we don't have to make this call into an event passage */ NET_RegisterContentTypeConverter(js_content_type, FO_PRESENT, 0, NET_CreateMochaConverter); NET_RegisterContentTypeConverter(TEXT_CSS, FO_PRESENT, 0, NET_CreateMochaConverter); NET_RegisterContentTypeConverter(TEXT_JSSS, FO_PRESENT, 0, NET_CreateMochaConverter); /* Associate a JS netlib "converter" with our CSS too. */ NET_RegisterContentTypeConverter(TEXT_CSS, FO_PRESENT, 0, NET_CreateMochaConverter); /* Make sure the mozilla event queue is around */ XP_ASSERT(mozilla_event_queue != NULL); /* MLM - init the first window group and therefore the first JS thread */ lm_InitWindowGroups(); /* AutoInstall trigger functions for JS config object */ lm_DefineTriggers(); #ifdef JSDEBUGGER lm_InitJSDebug(lm_runtime); #endif /* beard: move this to end, to ensure complete initialization before initing LiveConnect. */ #if defined(OJI) if (JVM_MaybeStartupLiveConnect()) JSJ_InitJSContext(LM_GetCrippledContext(), JS_GetGlobalObject(LM_GetCrippledContext()), NULL); #elif defined(JAVA) LJ_JSJ_Init(); /* * Get liveconnect functions defined for the crippled context * so we can pass error messages to the JavaConsole */ JSJ_InitContext(crippled_context, JS_GetGlobalObject(crippled_context)); #endif return; } void LM_ForceJSEnabled(MWContext *cx) { lm_ReallyInitMocha(); cx->forceJSEnabled = PR_TRUE; return; } /* XXX return boolean to propagate errors. */ void LM_InitMocha(void) { /* register callback incase pref changes while we're running */ PREF_RegisterCallback(lm_jsEnabled, lm_PrefChangedFunc, NULL); PREF_RegisterCallback(lm_jsEnabledMN, lm_PrefChangedFunc, NULL); PREF_RegisterCallback(lm_jsEnabledSigning, lm_PrefChangedFunc, NULL); PREF_RegisterCallback(lm_jsEnabledCrossOrigin, lm_PrefChangedFunc, NULL); PREF_RegisterCallback(lm_jsEnabledUnsignedExecution, lm_PrefChangedFunc, NULL); /* get our enabled-ness states */ PREF_GetBoolPref(lm_jsEnabled, &lm_enabled); PREF_GetBoolPref(lm_jsEnabledMN, &lm_enabledMailNews); PREF_GetBoolPref(lm_jsEnabledSigning, &lm_enabledSigning); PREF_GetBoolPref(lm_jsEnabledCrossOrigin, &lm_enabledCrossOrigin); PREF_GetBoolPref(lm_jsEnabledUnsignedExecution, &lm_enabledUnsignedExecution); /* XXXMLM - there used to be a race condition where et_TopQueue was being * initialized here because someone was putting events on it * before lm_ReallyInitMocha() had been called. Is this still * an issue? We don't have the default window group yet here. */ if (!lm_enabled) return; lm_ReallyInitMocha(); } static int lm_moja_initialized = LM_MOJA_UNINITIALIZED; PRLogModuleInfo* Moja; void MojaLogModuleInit() { Moja = PR_NewLogModule("Moja"); } /* returns FALSE if already done or TRUE for success */ int LM_InitMoja() { LMWindowGroup *grp; /* XXX assert mozilla thread */ /* this stuff should only be done once. since it is always * called on the moz thread we can do it the easy way */ if (lm_moja_initialized != LM_MOJA_UNINITIALIZED) return lm_moja_initialized; /* BONEHEAD - does this stuff need to be cross-thread, or will one do? */ grp = LM_GetDefaultWindowGroup(NULL); #if defined(OJI) { nsJVMStatus status = JVM_GetJVMStatus(); if (status != nsJVMStatus_Running) { lm_moja_initialized = LM_MOJA_JAVA_FAILED; return lm_moja_initialized; } lm_moja_initialized = LM_MOJA_OK; } #elif defined(JAVA) /* initialize the java env associated with the mocha thread */ lm_JSEnv = LJ_EnsureJavaEnv(grp->thread); if (lm_JSEnv == NULL) { lm_moja_initialized = LM_MOJA_JAVA_FAILED; return lm_moja_initialized; } lm_moja_initialized = LM_MOJA_OK; #else lm_moja_initialized = LM_MOJA_JAVA_FAILED; #endif MojaLogModuleInit(); return lm_moja_initialized; } /* returns FALSE if already done or TRUE for success */ int LM_IsMojaInitialized() { return lm_moja_initialized; } void LM_FinishMoja() { /* XXX write me */ } /* BONEHEAD - defaults to the default thread group. Hmm. */ PRBool PR_CALLBACK LM_HandOffJSLock(PRThread * oldOwner, PRThread *newOwner) { /* XXX note this doesn't worry about the lm_owner_count, * assumes it's pushed/popped properly */ LMWindowGroup *grp; PRBool didHandOff = PR_FALSE; grp = LM_GetDefaultWindowGroup(NULL); PR_EnterMonitor(grp->owner_monitor); if (grp->owner == oldOwner) { grp->owner = newOwner; didHandOff = PR_TRUE; } PR_Notify(grp->owner_monitor); PR_ExitMonitor(grp->owner_monitor); return didHandOff; } /* * push the mozilla event loop until the jslock is obtained */ static void LM_WaitForJSLock(LMWindowGroup *grp) { PREventQueue *q = mozilla_event_queue; PRMonitor *mon = PR_GetEventQueueMonitor(q); PRBool hadLayoutLock; XP_ASSERT(PR_CurrentThread() == mozilla_thread); hadLayoutLock = (JSBool)(!LO_VerifyUnlockedLayout()); if (hadLayoutLock) LO_UnlockLayout(); while (!(grp->mozGotLock)) { PR_EnterMonitor(mon); if (!PR_EventAvailable(q)) PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); PR_ExitMonitor(mon); PR_ProcessPendingEvents(q); } /* XXXMLM - We need a JSContext to set the thread on! */ /* In current code, though, modules/applet is no * longer active. Does this mean Mozilla isn't * going to try to get the lock anymore? */ if (hadLayoutLock) LO_LockLayout(); } /* * Wait until we get the JSLock for a given MWContext */ JSBool PR_CALLBACK LM_LockJS(MWContext *mwc, char **errp) { LMWindowGroup *grp = lm_MWContextToGroup(mwc); if(grp == NULL) { grp = LM_GetDefaultWindowGroup(mwc); LM_AddContextToGroup(grp, mwc); } return LM_LockJSByGroup(grp, errp); } JSBool PR_CALLBACK LM_LockJSByGroup(LMWindowGroup *grp, char **errp) { PRThread *t = PR_CurrentThread(); XP_ASSERT(grp->owner_monitor != NULL); PR_EnterMonitor(grp->owner_monitor); while (grp->owner != t) { if (grp->owner == NULL) { grp->owner = t; break; } if (PR_CurrentThread() == mozilla_thread) { /* blocking here could deadlock, because the owner * of the js thread may make synchronous event calls * to the moz thread through et_moz.c or others. */ /* while we wait, run another event loop */ grp->mozWantsLock = PR_TRUE; grp->mozGotLock = PR_FALSE; PR_ExitMonitor(grp->owner_monitor); LM_WaitForJSLock(grp); PR_EnterMonitor(grp->owner_monitor); XP_ASSERT(grp->owner == mozilla_thread); grp->mozWantsLock = PR_FALSE; grp->mozGotLock = PR_FALSE; } else { PR_Wait(grp->owner_monitor, PR_INTERVAL_NO_TIMEOUT); } } grp->current_count++; PR_ExitMonitor(grp->owner_monitor); return( JS_TRUE ); } /* * Try to get the JSLock but just return JS_FALSE if we can't * get it, don't wait since we could deadlock */ JSBool PR_CALLBACK LM_AttemptLockJS(MWContext *mwc, JSLockReleaseFunc fn, void * data) { PRThread *t = PR_CurrentThread(); LMWindowGroup *grp = lm_MWContextToGroup(mwc); /* * If javascript is disabled this might have never been * created. In that case its never possible to get the * js lock. */ /* MLM - changing to use mochaInited */ if (!mochaInited) return JS_FALSE; if(grp == NULL) { grp = LM_GetDefaultWindowGroup(mwc); LM_AddContextToGroup(grp, mwc); } PR_EnterMonitor(grp->owner_monitor); if (grp->owner == NULL || grp->owner == t) { grp->owner = t; grp->current_count++; PR_ExitMonitor(grp->owner_monitor); return JS_TRUE; } if (fn) { /* XXX - Only one waitor function allowed at a time */ lm_lock_waiter ** p; lm_lock_waiter * waiter = XP_NEW_ZAP(lm_lock_waiter); if (!waiter) { PR_ExitMonitor(grp->owner_monitor); return JS_FALSE; } waiter->fn = fn; waiter->data = data; /* double indirection! */ for (p = &grp->waiting_list; *p; p = &(*p)->next) ; *p = waiter; } PR_ExitMonitor(grp->owner_monitor); return JS_FALSE; } JSBool PR_CALLBACK LM_ClearAttemptLockJS(MWContext *mwc, JSLockReleaseFunc fn, void * data) { LMWindowGroup *grp = lm_MWContextToGroup(mwc); lm_lock_waiter ** p; lm_lock_waiter * waiter; XP_ASSERT(grp != NULL); for (p = &grp->waiting_list; (waiter = *p) != NULL; p = &waiter->next) { if (waiter->fn == fn && waiter->data == data) { *p = waiter->next; XP_FREE(waiter); return JS_TRUE; } } return JS_FALSE; } PR_STATIC_CALLBACK(void) lm_MozGotJSLock(void *data) { /* does nothing - signalling is done when the lock * is transferred over. the event that calls this * function is only sent to wake up the nested event * loop */ } /* * Release the JSLock */ void PR_CALLBACK LM_UnlockJS(MWContext *mwc) { LMWindowGroup *grp = lm_MWContextToGroup(mwc); XP_ASSERT(grp != NULL); LM_UnlockJSByGroup(grp); } void PR_CALLBACK LM_UnlockJSByGroup(LMWindowGroup *grp) { XP_ASSERT(PR_CurrentThread() == grp->owner); PR_EnterMonitor(grp->owner_monitor); XP_ASSERT(grp->current_count > 0); if (--grp->current_count <= 0) { grp->current_count = 0; grp->owner = NULL; grp->current_context = NULL; /* was anyone waiting for us to release the JSLock? */ /* moz gets priority, and we hand the lock off immediately */ if (grp->mozWantsLock) { grp->owner = mozilla_thread; grp->current_count = 0; grp->mozWantsLock = PR_FALSE; grp->mozGotLock = PR_TRUE; ET_moz_CallFunctionAsync(lm_MozGotJSLock, NULL); } else if (grp->waiting_list) { lm_lock_waiter * waiter = grp->waiting_list; grp->waiting_list = waiter->next; ET_moz_CallFunctionAsync(waiter->fn, waiter->data); XP_FREE(waiter); } PR_Notify(grp->owner_monitor); } PR_ExitMonitor(grp->owner_monitor); } /* * keep track of the current context that owns the JS lock in * case we get an interrupt event and need to decide whether * to interrupt the current operation or not */ void LM_JSLockSetContext(MWContext * context) { LMWindowGroup *grp = lm_MWContextToGroup(context); if(grp == NULL) { grp = LM_GetDefaultWindowGroup(context); LM_AddContextToGroup(grp, context); } XP_ASSERT(grp->owner_monitor != NULL); PR_EnterMonitor(grp->owner_monitor); XP_ASSERT(grp->owner == PR_CurrentThread()); grp->current_context = context; PR_ExitMonitor(grp->owner_monitor); } MWContext * LM_JSLockGetContext(MWContext *mwc) { LMWindowGroup *grp = lm_MWContextToGroup(mwc); XP_ASSERT(grp != NULL); return grp->current_context; } MochaDecoder * LM_GetMochaDecoder(MWContext *context) { JSContext *cx; MochaDecoder *decoder; if (!LM_CanDoJS(context)) return NULL; /* Get the context's JS decoder, creating one if necessary. */ cx = context->mocha_context; if (cx) { JS_SetContextThread(cx); /* XXXMLM - are we already in the request here? */ JS_BeginRequest(cx); decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx)); JS_EndRequest(cx); } else { decoder = lm_NewWindow(context); if (!decoder) return NULL; cx = decoder->js_context; } if (!decoder->document) { JS_BeginRequest(cx); if(!lm_InitWindowContent(decoder)) { JS_EndRequest(cx); return NULL; } JS_EndRequest(cx); } /* The decoder has at least one forward ref from context. */ XP_ASSERT(decoder->forw_count > 0); decoder->forw_count++; return decoder; } void LM_PutMochaDecoder(MochaDecoder *decoder) { JSContext * cx; XP_ASSERT(decoder->forw_count > 0); if (--decoder->forw_count <= 0) { decoder->forw_count = 0; if (decoder->window_context) decoder->window_context->mocha_context = NULL; #define CLEAR(obj) obj = 0 /* Clear all object prototype refs. */ CLEAR(decoder->anchor_prototype); CLEAR(decoder->bar_prototype); CLEAR(decoder->document_prototype); CLEAR(decoder->event_prototype); CLEAR(decoder->event_capturer_prototype); CLEAR(decoder->event_receiver_prototype); CLEAR(decoder->form_prototype); CLEAR(decoder->image_prototype); CLEAR(decoder->input_prototype); CLEAR(decoder->layer_prototype); CLEAR(decoder->option_prototype); CLEAR(decoder->rect_prototype); CLEAR(decoder->url_prototype); /* Clear window sub-object refs. */ if (decoder->document) lm_CleanUpDocumentRoots(decoder, decoder->document); CLEAR(decoder->document); CLEAR(decoder->history); CLEAR(decoder->location); CLEAR(decoder->navigator); CLEAR(decoder->components); CLEAR(decoder->crypto); CLEAR(decoder->screen); #ifdef NAV_HARDWARE CLEAR(decoder->hardware); #endif CLEAR(decoder->environment); CLEAR(decoder->pkcs11); CLEAR(decoder->background_update); /* Clear ad-hoc GC roots. */ CLEAR(decoder->event_receiver); CLEAR(decoder->opener); /* Clear the root of this window's object tree. */ CLEAR(decoder->window_object); #undef CLEAR /* Don't forget to clear GC roots in the context. */ cx = decoder->js_context; JS_ClearRegExpRoots(cx); /* Reset these in case a backward reference to decoder lingers. */ decoder->firstVersion = JSVERSION_UNKNOWN; decoder->principals = NULL; /* Hold an extra back ref to keep cx and decoder alive. */ HOLD_BACK_COUNT(decoder); JS_SetGlobalObject(cx, NULL); JS_GC(cx); /* * If the finalizer ran, and there are no back refs from private * data of objects held by other windows' properties, then this drop * will destroy the decoder. Otherwise it must live on until its * runaway kids are all finalized. */ DROP_BACK_COUNT(decoder); } } JSBool LM_IsActive(MWContext *context) { JSContext *cx = context->mocha_context; JSBool ans; if (!cx) return JS_FALSE; /* No need to lock here. MLM */ ans = JS_IsRunning(cx); return (JSBool)(ans || (context->js_timeouts_pending > 0)); } const char * LM_GetSourceURL(MochaDecoder *decoder) { JSContext *cx; const char *originURL; if (decoder->nesting_url) return decoder->nesting_url->str; if (decoder->source_url) return decoder->source_url; cx = decoder->js_context; originURL = lm_GetObjectOriginURL(cx, decoder->window_object); if (!originURL) return NULL; decoder->source_url = JS_strdup(cx, originURL); return decoder->source_url; } static const char AUTOINSTALL_PREFIX[] = "autoinstall:"; static size_t AUTOINSTALL_PREFIX_LENGTH = sizeof AUTOINSTALL_PREFIX - 1; static JSObject * lm_get_scope_from_string(MochaDecoder *decoder, JSContext *cx, const char *source_url, uint lineno, char *scope_to, JSPrincipals *principals) { jsval result; JSBool ok; JSObject *scope; if (!scope_to) return NULL; /* * Hack for ASD -- create a new object with environment from scope_to. * If we ever change autoinstall:, change the define MOCHA_CONTEXT_PREFIX * in softupdt.c XXXbe how about a single definition in a .h file? */ if (!XP_STRNCMP(scope_to, AUTOINSTALL_PREFIX, AUTOINSTALL_PREFIX_LENGTH)) { return lm_NewSoftupObject(cx, decoder, scope_to + AUTOINSTALL_PREFIX_LENGTH); } if (decoder->active_layer_id == LO_DOCUMENT_LAYER_ID) { scope = decoder->window_object; } else { LO_LockLayout(); scope = LO_GetLayerMochaObjectFromId(decoder->window_context, decoder->active_layer_id); LO_UnlockLayout(); } /* pass the scope as a script to return the scope object */ ok = JS_EvaluateScriptForPrincipals(cx, scope, principals, scope_to, strlen(scope_to), source_url, lineno, &result); if (ok) { XP_ASSERT(JSVAL_IS_OBJECT(result)); if (JSVAL_IS_OBJECT(result)) return JSVAL_TO_OBJECT(result); } return NULL; } JSBool LM_EvaluateBuffer(MochaDecoder *decoder, void *base, size_t length, uint lineno, char *scope_to, JSPrincipals *principals, JSBool unicode, jsval *result) { JSContext *cx; const char *source_url; JSBool ok; JSObject *scope; /* XXX if lineno == 0, do something smart */ source_url = LM_GetSourceURL(decoder); if (!source_url) return JS_FALSE; cx = decoder->js_context; principals = LM_RegisterPrincipals(decoder, principals, NULL, base); if (!principals) return JS_FALSE; scope = lm_get_scope_from_string(decoder, cx, source_url, lineno, scope_to, principals); if (!scope) { if (decoder->active_layer_id == LO_DOCUMENT_LAYER_ID) { scope = decoder->window_object; } else { const char *scope_source_url; LO_LockLayout(); scope = LO_GetLayerMochaObjectFromId(decoder->window_context, decoder->active_layer_id); LO_UnlockLayout(); if (!scope) return JS_FALSE; scope_source_url = lm_GetLayerOriginURL(cx, scope); if (scope_source_url) source_url = scope_source_url; } } if (unicode) ok = JS_EvaluateUCScriptForPrincipals(cx, scope, principals, base, length, source_url, lineno, result); else ok = JS_EvaluateScriptForPrincipals(cx, scope, principals, base, length, source_url, lineno, result); return ok; } char * LM_EvaluateAttribute(MWContext *context, char *expr, uint lineno) { char *bytes; MochaDecoder *decoder; jsval result; JSContext *cx; JSPrincipals *principals; bytes = 0; if (!expr) return bytes; decoder = LM_GetMochaDecoder(context); if (!decoder) return bytes; /* * Since this called directly from the mozilla thread, we know that * it's safe to sample the doc_id. In fact, we have to do that, * otherwise the doc_id check could fail for any entities processed. */ decoder->doc_id = XP_DOCID(context); cx = decoder->js_context; /* Make sure the correct thread ID is set on the JS context */ JS_SetContextThread(cx); /* Since we're on the Mozilla thread, we haven't begun a request yet. */ JS_BeginRequest(cx); if (!JS_AddRoot(cx, &result)) { /* XXX chouck - can we do a lockGCThing here */ JS_EndRequest(cx); LM_PutMochaDecoder(decoder); return bytes; } principals = lm_GetCompilationPrincipals(decoder, NULL); if (principals) { JSPRINCIPALS_HOLD(cx, principals); if (LM_EvaluateBuffer(decoder, expr, XP_STRLEN(expr), lineno, NULL, principals, JS_FALSE, &result)) { bytes = lm_StrToLocalEncoding(context, JS_ValueToString(cx, result)); } JSPRINCIPALS_DROP(cx, principals); } JS_RemoveRoot(cx, &result); JS_EndRequest(cx); LM_PutMochaDecoder(decoder); return bytes; } static JSObject* lm_get_layer_parent(JSContext *cx, JSObject *obj) { JSObject *save = obj; jsval val; if (!JS_InstanceOf(cx, obj, &lm_layer_class, NULL)) return NULL; while (obj) { if (JS_InstanceOf(cx, obj, &lm_document_class, NULL)) { return obj; } obj = JS_GetParent(cx, obj); } /* Should only get here if the layer's parent has been severed * Don't want to use this method in general because it could * cause a security violation on the layer container check. */ if (!JS_GetProperty(cx, save, lm_parentLayer_str, &val)) return NULL; if (val != JSVAL_NULL && JSVAL_IS_OBJECT(val)) { obj = JSVAL_TO_OBJECT(val); if (!JS_GetProperty(cx, obj, lm_document_str, &val)) return NULL; if (val != JSVAL_NULL && JSVAL_IS_OBJECT(val)) return JSVAL_TO_OBJECT(val); } return NULL; } static JSBool lm_lookup_event_handler(JSContext *cx, JSObject *obj, uint32 type, jsval *fvp) { JSEventNames *names; char name[32]; JSBool ok; names = lm_GetEventNames(type); if (!names) { *fvp = JSVAL_VOID; return JS_TRUE; } PR_snprintf(name, sizeof name, "on%s", names->lowerName); ok = JS_LookupProperty(cx, obj, name, fvp); if (ok && JSVAL_IS_VOID(*fvp)) { PR_snprintf(name, sizeof name, "on%s", names->mixedName); ok = JS_LookupProperty(cx, obj, name, fvp); } return ok; } JSBool lm_SendEvent(MWContext *context, JSObject *obj, JSEvent *event, jsval *result) { JSContext *cx; JSObject *eventObj; MochaDecoder *decoder; JSBool ok = JS_TRUE; jsval funval, argv[1]; MWContext *mwcx; uint32 event_capture_bit = 0; decoder = LM_GetMochaDecoder(context); if (!decoder) return JS_FALSE; cx = decoder->js_context; if (!event->object) event->object = obj; event_capture_bit |= context->event_bit; if (context->is_grid_cell) { mwcx = context; while (mwcx->grid_parent) { mwcx = mwcx->grid_parent; event_capture_bit |= mwcx->event_bit; } } ok = lm_lookup_event_handler(cx, obj, event->type, &funval); if (!ok || (JS_TypeOfValue(cx, funval) != JSTYPE_FUNCTION && !(event_capture_bit & event->type))) { goto out; } eventObj = lm_NewEventObject(decoder, event); if (!eventObj) { ok = JS_FALSE; goto out; } argv[0] = OBJECT_TO_JSVAL(eventObj); ok = lm_FindEventHandler(context, obj, eventObj, funval, result); #ifdef DOM /* when firing the onUnload event, destroy the node tree afterwards */ if (event->type == EVENT_UNLOAD) { /* XXX should we run GC before, to clean up reflections? */ lm_DestroyDocumentNodes(context); } #endif out: LM_PutMochaDecoder(decoder); return ok; } JSBool lm_FindEventHandler(MWContext *context, JSObject *obj, JSObject *eventObj, jsval funval, jsval *result) { MochaDecoder *decoder; JSContext *cx; JSBool ok=JS_FALSE, is_window; JSEventCapturer *js_cap; JSEvent *event; MWContext *mwcx; uint32 event_capture_bit = 0; if (!obj) return JS_TRUE; decoder = LM_GetMochaDecoder(context); if (!decoder) return JS_FALSE; cx = decoder->js_context; /* Fun, fun. Time for the reverse architecture event capturing. * First, let's see if we're capturing at all. */ event_capture_bit |= context->event_bit; if (context->is_grid_cell) { mwcx = context; while (mwcx->grid_parent) { mwcx = mwcx->grid_parent; event_capture_bit |= mwcx->event_bit; } } if (!(event = JS_GetInstancePrivate(cx, eventObj, &lm_event_class, NULL))) { LM_PutMochaDecoder(decoder); return JS_FALSE; } if (!(event_capture_bit & event->type)) { /*No capturing going on. Just call the function.*/ LM_PutMochaDecoder(decoder); return lm_HandleEvent(cx, obj, eventObj, funval, result); } event->event_handled = JS_FALSE; /*Somebody wants it. Let's go! Time for recursion!*/ is_window = JS_InstanceOf(cx, obj, &lm_window_class, NULL); if (!is_window || context->grid_parent) { if (is_window && context->is_grid_cell) { if (context->grid_parent->mocha_context) { ok = lm_FindEventHandler(context->grid_parent, JS_GetGlobalObject(context->grid_parent->mocha_context), eventObj, 0, result); } else { ok = JS_TRUE; } } else if (JS_InstanceOf(cx, obj, &lm_layer_class, NULL)) { ok = lm_FindEventHandler(context, lm_get_layer_parent(cx, obj), eventObj, 0, result); } else { ok = lm_FindEventHandler(context, JS_GetParent(cx, obj), eventObj, 0, result); } } if (!event->event_handled) { /*We unfortunately have to check versus different type here.*/ if (JS_InstanceOf(cx, obj, &lm_window_class, NULL)) { if (decoder->event_bit & event->type && !(decoder->event_mask & event->type)) { decoder->event_mask |= event->type; ok = lm_HandleEvent(cx, obj, eventObj, funval, result); decoder->event_mask &= ~event->type; LM_PutMochaDecoder(decoder); event->event_handled = ok; return ok; } } if (JS_InstanceOf(cx, obj, &lm_document_class, NULL) || JS_InstanceOf(cx, obj, &lm_layer_class, NULL)) { js_cap = JS_GetPrivate(cx, obj); if (js_cap && js_cap->event_bit & event->type && !(js_cap->base.event_mask & event->type)) { XP_ASSERT(cx == event->decoder->js_context); js_cap->base.event_mask |= event->type; ok = lm_HandleEvent(cx, obj, eventObj, funval, result); js_cap->base.event_mask &= ~event->type; LM_PutMochaDecoder(decoder); event->event_handled = ok; return ok; } } /* If we get this far we should be back at the original object and it * needs to have its eventhandler called. */ if (obj == event->object) { ok = lm_HandleEvent(cx, obj, eventObj, funval, result); } } LM_PutMochaDecoder(decoder); return ok; } JSBool lm_HandleEvent(JSContext *cx, JSObject *obj, JSObject *eventObj, jsval funval, jsval *result) { JSEvent *event; JSBool ok = JS_TRUE; jsval argv[1]; char name[32]; JSFunction *fun; if (JS_TypeOfValue(cx, funval) != JSTYPE_FUNCTION) { event = JS_GetPrivate(cx, eventObj); if (!event) { ok = JS_FALSE; goto out; } PR_snprintf(name, sizeof name, "on%s", lm_EventName(event->type)); if (!JS_LookupProperty(cx, obj, name, result)) { ok = JS_FALSE; goto out; } ok = lm_lookup_event_handler(cx, obj, event->type, &funval); if (!ok || JS_TypeOfValue(cx, funval) != JSTYPE_FUNCTION) goto out; fun = JS_ValueToFunction(cx, funval); if (fun == NULL) { ok = JS_FALSE; goto out; } if (!lm_CanCaptureEvent(cx, fun, event)) { ok = JS_FALSE; goto out; } } argv[0] = OBJECT_TO_JSVAL(eventObj); #ifdef XP_UNIX lm_handling_event++; #endif ok = JS_CallFunctionValue(cx, obj, funval, 1, argv, result); #ifdef XP_UNIX lm_handling_event--; #endif out: return ok; } /* * Wrapper for the common case of decoder's stream being set by code running * on decoder's JS context. */ JSBool LM_SetDecoderStream(MWContext * context, NET_StreamClass *stream, URL_Struct *url_struct, JSBool free_stream_on_close) { MochaDecoder *decoder = LM_GetMochaDecoder(context); JSBool rv; if (!decoder) return (JS_FALSE); rv = lm_SetInputStream(decoder->js_context, decoder, stream, url_struct, free_stream_on_close); LM_PutMochaDecoder(decoder); return(rv); } void lm_SetActiveForm(MWContext * context, int32 id) { MochaDecoder *decoder = LM_GetMochaDecoder(context); if (!decoder) return; decoder->active_form_id = id; LM_PutMochaDecoder(decoder); } void LM_SetActiveLayer(MWContext * context, int32 layer_id) { MochaDecoder *decoder = LM_GetMochaDecoder(context); if (!decoder) return; decoder->active_layer_id = layer_id; LM_PutMochaDecoder(decoder); } int32 LM_GetActiveLayer(MWContext * context) { int32 layer_id; MochaDecoder *decoder = LM_GetMochaDecoder(context); if (!decoder) return LO_DOCUMENT_LAYER_ID; layer_id = decoder->active_layer_id; LM_PutMochaDecoder(decoder); return layer_id; } JSBool lm_SetInputStream(JSContext *cx, MochaDecoder *decoder, NET_StreamClass *stream, URL_Struct *url_struct, JSBool free_stream_on_close) { const char *origin_url; JSObject *container; JSPrincipals *principals; /* * If the stream is NULL don't clobber the old value */ if (stream) { decoder->stream = stream; decoder->free_stream_on_close = free_stream_on_close; } /* * If we're still in the process of loading the main parser stream * (i.e. the window's load event hasn't been sent), the window is the * owner of the stream. */ if (!decoder->load_event_sent) decoder->stream_owner = LO_DOCUMENT_LAYER_ID; decoder->url_struct = NET_HoldURLStruct(url_struct); /* Don't update origin for