mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
2104 lines
58 KiB
C
2104 lines
58 KiB
C
/* -*- 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.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
/*
|
|
* 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: "
|
|
: "<FONT SIZE=4>\\\n<B>%s Error:</B> ",
|
|
js_language_name);
|
|
|
|
if (!report) {
|
|
last = PR_sprintf_append(last,
|
|
java_errors
|
|
? "%s\\\n"
|
|
: "<B>%s</B>\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"
|
|
: "<B>line %u:</B>",
|
|
report->lineno);
|
|
}
|
|
last = PR_sprintf_append(last,
|
|
java_errors
|
|
? " %s."
|
|
: "<BR><BR>%s.</FONT><PRE><FONT SIZE=4>",
|
|
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,
|
|
"</FONT>"
|
|
"<FONT SIZE=4 COLOR=#ff2020>");
|
|
}
|
|
last = PR_sprintf_append(last, (*t == '<') ? "<" : "%c", *t);
|
|
}
|
|
t++;
|
|
}
|
|
if (java_errors) {
|
|
last = PR_sprintf_append(last, "\\\n");
|
|
}
|
|
else {
|
|
last = PR_sprintf_append(last, "</FONT><FONT SIZE=4>\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 ? "^" : "<B>^</B>");
|
|
}
|
|
}
|
|
if (!java_errors)
|
|
last = PR_sprintf_append(last, "\n</FONT></PRE>");
|
|
}
|
|
|
|
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 <SCRIPT SRC="URL"> URLs. */
|
|
if (decoder->nesting_url)
|
|
return JS_TRUE;
|
|
|
|
/* Update the origin URL associated with the document in decoder. */
|
|
if (!decoder->writing_input) {
|
|
switch (NET_URL_Type(url_struct->address)) {
|
|
case ABOUT_TYPE_URL:
|
|
case MOCHA_TYPE_URL:
|
|
return JS_TRUE;
|
|
}
|
|
origin_url = url_struct->address;
|
|
} else {
|
|
origin_url = lm_GetSubjectOriginURL(cx);
|
|
if (!origin_url)
|
|
return JS_FALSE;
|
|
}
|
|
|
|
container = lm_GetActiveContainer(decoder);
|
|
if (container == NULL)
|
|
return JS_FALSE;
|
|
principals = lm_GetInnermostPrincipals(cx, container, NULL);
|
|
if (principals == NULL)
|
|
return JS_FALSE;
|
|
if (XP_STRCMP(principals->codebase, origin_url) == 0) {
|
|
/* XXXbe Don't want to invalidate self-modifying doc's cert principals
|
|
pessimisticly (for fear of a written unsigned <script> tag).
|
|
Rather, let LM_RegisterPrincipals handle that case only when
|
|
it arises. Note that overwrite of a closed doc drops any old
|
|
principals via LM_ReleaseDocument/lm_FreeWindowContent.
|
|
lm_InvalidateCertPrincipals(decoder, principals);
|
|
*/
|
|
return JS_TRUE;
|
|
}
|
|
principals = LM_NewJSPrincipals(NULL, NULL, origin_url);
|
|
if (principals == NULL)
|
|
return JS_FALSE;
|
|
lm_SetContainerPrincipals(cx, container, principals);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
NET_StreamClass *
|
|
lm_ClearDecoderStream(MochaDecoder *decoder, JSBool fromDiscard)
|
|
{
|
|
NET_StreamClass *stream;
|
|
URL_Struct *url_struct;
|
|
|
|
stream = decoder->stream;
|
|
url_struct = decoder->url_struct;
|
|
decoder->stream = 0;
|
|
decoder->stream_owner = LO_DOCUMENT_LAYER_ID;
|
|
decoder->url_struct = 0;
|
|
if (decoder->writing_input) {
|
|
decoder->writing_input = JS_FALSE;
|
|
if (stream) {
|
|
/*
|
|
* Complete the stream before freeing the URL struct to which it
|
|
* may hold a private pointer.
|
|
*/
|
|
if (!fromDiscard) {
|
|
if (decoder->window_context &&
|
|
XP_DOCID(decoder->window_context) != -1)
|
|
ET_moz_CallFunction( (ETVoidPtrFunc) stream->complete, (void *)stream);
|
|
if (decoder->free_stream_on_close)
|
|
XP_DELETE(stream);
|
|
}
|
|
stream = NULL;
|
|
}
|
|
if (url_struct) {
|
|
/* we should no longer be setting this */
|
|
XP_ASSERT(url_struct->pre_exit_fn == NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* whether we were writing or not we were holding a reference
|
|
* to this url_struct. Let go of it now
|
|
*/
|
|
if (url_struct)
|
|
NET_DropURLStruct(url_struct);
|
|
decoder->free_stream_on_close = JS_FALSE;
|
|
return(stream);
|
|
}
|
|
|
|
#ifndef DOM
|
|
/* XXX believe that this is dead code, can I remove it? */
|
|
PRIVATE void
|
|
LM_ClearContextStream(MWContext *context)
|
|
{
|
|
MochaDecoder *decoder;
|
|
|
|
if (!context->mocha_context) {
|
|
/* Don't impose cost on non-JS contexts. */
|
|
return;
|
|
}
|
|
decoder = LM_GetMochaDecoder(context);
|
|
if (!decoder) {
|
|
/* XXX how can this happen? If lm_InitWindow fails or if the user
|
|
turns off JS in the middle of a load. Return false! */
|
|
return;
|
|
}
|
|
lm_ClearDecoderStream(decoder, JS_FALSE);
|
|
LM_PutMochaDecoder(decoder);
|
|
}
|
|
#endif
|
|
|
|
JSBool
|
|
lm_SaveParamString(JSContext *cx, PA_Block *bp, const char *str)
|
|
{
|
|
if (*bp)
|
|
PA_FREE(*bp);
|
|
|
|
*bp = (PA_Block) strdup(str);
|
|
if (!*bp) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSObject *
|
|
lm_GetOuterObject(MochaDecoder * decoder)
|
|
{
|
|
lo_FormData *form_data;
|
|
JSObject * rv = NULL;
|
|
|
|
if (decoder->active_form_id) {
|
|
LO_LockLayout();
|
|
form_data =
|
|
LO_GetFormDataByID(decoder->window_context,
|
|
decoder->active_layer_id,
|
|
decoder->active_form_id);
|
|
if (form_data)
|
|
rv = form_data->mocha_object;
|
|
|
|
LO_UnlockLayout();
|
|
}
|
|
else
|
|
rv = lm_GetDocumentFromLayerId(decoder, decoder->active_layer_id);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Add an object to an array
|
|
*/
|
|
JSBool
|
|
lm_AddObjectToArray(JSContext * cx, JSObject * array_obj,
|
|
const char * name, jsint index, JSObject * obj)
|
|
{
|
|
JSObjectArray *array;
|
|
|
|
array = JS_GetPrivate(cx, array_obj);
|
|
if (!array)
|
|
return JS_TRUE;
|
|
|
|
if (name) {
|
|
if (!JS_DefineProperty(cx, array_obj, name, OBJECT_TO_JSVAL(obj),
|
|
NULL, NULL,
|
|
JSPROP_ENUMERATE | JSPROP_READONLY)) {
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
if (!JS_DefineElement(cx, array_obj, (jsint) index,
|
|
OBJECT_TO_JSVAL(obj),
|
|
NULL, NULL,
|
|
JSPROP_ENUMERATE | JSPROP_READONLY)) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (index >= array->length)
|
|
array->length = index + 1;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
void
|
|
lm_SetVersion(MochaDecoder *decoder, JSVersion version) {
|
|
if (version == JSVERSION_UNKNOWN) {
|
|
version = (decoder->firstVersion == JSVERSION_UNKNOWN)
|
|
? JSVERSION_DEFAULT
|
|
: decoder->firstVersion;
|
|
}
|
|
if (decoder->firstVersion == JSVERSION_UNKNOWN) {
|
|
decoder->firstVersion = version;
|
|
}
|
|
JS_SetVersion(decoder->js_context, version);
|
|
}
|
|
|
|
/*
|
|
* Convert a locally encoded string into a 16-bit unicode string to pass
|
|
* to the JS runtime. Allow cx to be NULL
|
|
*/
|
|
|
|
char *
|
|
lm_StrToEncoding(JSContext * cx, uint16 charset, JSString * str)
|
|
{
|
|
INTL_Unicode * src = JS_GetStringChars(str);
|
|
uint32 srclen = JS_GetStringLength(str);
|
|
uint32 destlen;
|
|
char * dest;
|
|
|
|
if (!str)
|
|
return NULL;
|
|
|
|
destlen = INTL_UnicodeToStrLen(charset, src, srclen);
|
|
dest = XP_ALLOC(destlen);
|
|
if (!dest) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return NULL;
|
|
}
|
|
|
|
INTL_UnicodeToStr(charset, src, srclen, (unsigned char *) dest, destlen);
|
|
return dest;
|
|
|
|
}
|
|
|
|
JSString *
|
|
lm_EncodingToStr(JSContext * cx, uint16 charset, char * bytes)
|
|
{
|
|
uint32 srclen, destlen;
|
|
INTL_Unicode * unicode = NULL;
|
|
|
|
/* return NULL or empty string? */
|
|
if (!bytes)
|
|
return JS_NewStringCopyZ(cx, NULL);
|
|
|
|
srclen = XP_STRLEN(bytes);
|
|
|
|
/* find out how many unicode characters we'll end up with */
|
|
destlen = INTL_StrToUnicodeLen(charset, (unsigned char *) bytes);
|
|
unicode = XP_ALLOC(sizeof(INTL_Unicode) * destlen);
|
|
if (!unicode)
|
|
return NULL;
|
|
|
|
/* do the conversion */
|
|
destlen = INTL_StrToUnicode(charset, (unsigned char *) bytes,
|
|
unicode, destlen);
|
|
return JS_NewUCString(cx, (jschar *) unicode, destlen);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert a locally encoded string into a 16-bit unicode string to pass
|
|
* to the JS runtime. Allow cx to be NULL
|
|
*/
|
|
char *
|
|
lm_StrToLocalEncoding(MWContext * context, JSString * str)
|
|
{
|
|
#ifdef UNICODE
|
|
uint16 charset;
|
|
charset = INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(context));
|
|
XP_ASSERT(charset != CS_UNKNOWN);
|
|
if (charset == CS_DEFAULT || charset == CS_UNKNOWN)
|
|
charset = CS_LATIN1;
|
|
return lm_StrToEncoding(context->mocha_context, charset, str);
|
|
|
|
#else
|
|
return strdup(JS_GetStringBytes(str));
|
|
#endif
|
|
|
|
}
|
|
|
|
JSString *
|
|
lm_LocalEncodingToStr(MWContext * context, char * bytes)
|
|
{
|
|
uint16 charset;
|
|
|
|
#ifdef UNICODE
|
|
charset = INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(context));
|
|
XP_ASSERT(charset != CS_UNKNOWN);
|
|
if (charset == CS_DEFAULT || charset == CS_UNKNOWN)
|
|
charset = CS_LATIN1;
|
|
|
|
return lm_EncodingToStr(context->mocha_context, charset, bytes);
|
|
|
|
#else
|
|
return JS_NewStringCopyZ(context->mocha_context, bytes);
|
|
#endif
|
|
|
|
}
|