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