1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* ***** BEGIN LICENSE BLOCK *****
4
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 1.1 (the "License"); you may not use this file except in compliance with
8
* the License. You may obtain a copy of the License at
9
* http://www.mozilla.org/MPL/
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
16
* The Original Code is Mozilla Communicator client code, released
19
* The Initial Developer of the Original Code is
20
* Netscape Communications Corporation.
21
* Portions created by the Initial Developer are Copyright (C) 1998
22
* the Initial Developer. All Rights Reserved.
26
* Alternatively, the contents of this file may be used under the terms of
27
* either of the GNU General Public License Version 2 or later (the "GPL"),
28
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29
* in which case the provisions of the GPL or the LGPL are applicable instead
30
* of those above. If you wish to allow use of your version of this file only
31
* under the terms of either the GPL or the LGPL, and not to allow others to
32
* use your version of this file under the terms of the MPL, indicate your
33
* decision by deleting the provisions above and replace them with the notice
34
* and other provisions required by the GPL or the LGPL. If you do not delete
35
* the provisions above, a recipient may use your version of this file under
36
* the terms of any one of the MPL, the GPL or the LGPL.
38
* ***** END LICENSE BLOCK ***** */
40
* This file is part of the Java-vendor-neutral implementation of LiveConnect
42
* It contains the implementation providing nsIFactory XP-COM interface.
53
#include "jsj_private.h"
56
#include "jscntxt.h" /* For js_ReportErrorAgain().*/
58
#include "netscape_javascript_JSObject.h" /* javah-generated headers */
59
#include "nsISecurityContext.h"
60
#include "nsIServiceManager.h"
61
#include "nsIJSContextStack.h"
65
/* A captured JavaScript error, created when JS_ReportError() is called while
66
running JavaScript code that is itself called from Java. */
67
struct CapturedJSError {
69
JSErrorReport report; /* Line # of error, etc. */
70
jthrowable java_exception; /* Java exception, error, or null */
71
CapturedJSError * next; /* Next oldest captured JS error */
75
#include "nsCLiveconnect.h"
77
#include "jsinterp.h" // XXX private API so we can auto-push a JSStackFrame
78
#include "nsIScriptSecurityManager.h"
79
#include "nsIPrincipal.h"
80
#include "nsNetUtil.h"
81
#include "nsISecurityContext.h"
84
/***************************************************************************/
85
// A class to put on the stack to manage JS contexts when we are entering JS.
86
// This pushes and pops the given context
87
// with the nsThreadJSContextStack service as this object goes into and out
88
// of scope. It is optimized to not push/pop the cx if it is already on top
89
// of the stack. We need to push the JSContext when we enter JS because the
90
// JS security manager looks on the context stack for permissions information.
92
class AutoPushJSContext
95
AutoPushJSContext(nsISupports* aSecuritySupports,
100
nsresult ResultOfPush() { return mPushResult; };
103
nsCOMPtr<nsIJSContextStack> mContextStack;
106
nsresult mPushResult;
109
AutoPushJSContext::AutoPushJSContext(nsISupports* aSecuritySupports,
111
: mContext(cx), mPushResult(NS_OK)
113
mContextStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
117
JSContext* currentCX;
118
if(NS_SUCCEEDED(mContextStack->Peek(¤tCX)))
120
// Is the current context already on the stack?
122
mContextStack = nsnull;
125
mContextStack->Push(cx);
126
// Leave the reference to the mContextStack to
127
// indicate that we need to pop it in our dtor.
132
nsCOMPtr<nsIScriptSecurityManager> secMan(
133
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &mPushResult));
135
if (NS_FAILED(mPushResult))
138
nsCOMPtr<nsIPrincipal> principal;
139
mPushResult = secMan->GetPrincipalFromContext(cx, getter_AddRefs(principal));
141
if (NS_FAILED(mPushResult))
143
JS_ReportError(cx, "failed to get a principal");
147
// See if JavaScript is enabled for the current window
148
PRBool jsEnabled = PR_FALSE;
149
mPushResult = secMan->CanExecuteScripts(cx, principal, &jsEnabled);
151
mPushResult = NS_ERROR_FAILURE;
153
memset(&mFrame, 0, sizeof(mFrame));
155
if (NS_SUCCEEDED(mPushResult))
157
// See if there are any scripts on the stack.
158
// If not, we need to add a dummy frame with a principal.
160
PRBool hasScript = PR_FALSE;
161
JSStackFrame* tempFP = cx->fp;
169
tempFP = tempFP->down;
174
JSPrincipals* jsprinc;
175
principal->GetJSPrincipals(cx, &jsprinc);
177
mFrame.script = JS_CompileScriptForPrincipals(cx, JS_GetGlobalObject(cx),
178
jsprinc, "", 0, "", 1);
179
JSPRINCIPALS_DROP(cx, jsprinc);
183
mFrame.down = cx->fp;
187
mPushResult = NS_ERROR_OUT_OF_MEMORY;
192
AutoPushJSContext::~AutoPushJSContext()
195
mContextStack->Pop(nsnull);
198
mContext->fp = mFrame.down;
203
////////////////////////////////////////////////////////////////////////////
204
// from nsISupports and AggregatedQueryInterface:
206
// Thes macro expands to the aggregated query interface scheme.
208
NS_IMPL_AGGREGATED(nsCLiveconnect)
211
nsCLiveconnect::AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr)
213
NS_ENSURE_ARG_POINTER(aInstancePtr);
215
if (aIID.Equals(NS_GET_IID(nsISupports))) {
216
*aInstancePtr = GetInner();
218
else if (aIID.Equals(NS_GET_IID(nsILiveconnect))) {
219
*aInstancePtr = NS_STATIC_CAST(nsILiveconnect*, this);
222
*aInstancePtr = nsnull;
223
return NS_NOINTERFACE;
225
NS_ADDREF((nsISupports*) *aInstancePtr);
231
////////////////////////////////////////////////////////////////////////////
232
// from nsILiveconnect:
235
* get member of a Native JSObject for a given name.
237
* @param jEnv - JNIEnv on which the call is being made.
238
* @param obj - A Native JS Object.
239
* @param name - Name of a member.
240
* @param pjobj - return parameter as a java object representing
241
* the member. If it is a basic data type it is converted to
242
* a corresponding java type. If it is a NJSObject, then it is
243
* wrapped up as java wrapper netscape.javascript.JSObject.
246
nsCLiveconnect::GetMember(JNIEnv *jEnv, jsobject obj, const jchar *name, jsize length, void* principalsArray[],
247
int numPrincipals, nsISupports *securitySupports, jobject *pjobj)
249
if(jEnv == NULL || obj == 0)
251
return NS_ERROR_FAILURE;
254
JSJavaThreadState *jsj_env = NULL;
255
JSObjectHandle *handle = (JSObjectHandle*)obj;
256
JSObject *js_obj = handle->js_obj;
257
JSContext *cx = NULL;
258
jobject member = NULL;
261
JSBool dummy_bool = PR_FALSE;
262
JSErrorReporter saved_state = NULL;
264
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports);
266
return NS_ERROR_FAILURE;
268
AutoPushJSContext autopush(securitySupports, cx);
269
if (NS_FAILED(autopush.ResultOfPush()))
273
JS_ReportError(cx, "illegal null member name");
278
if (!JS_GetUCProperty(cx, js_obj, name, length, &js_val))
281
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
282
&dummy_cost, &member, &dummy_bool);
285
if (!jsj_exit_js(cx, jsj_env, saved_state))
286
return NS_ERROR_FAILURE;
295
* get member of a Native JSObject for a given index.
297
* @param jEnv - JNIEnv on which the call is being made.
298
* @param obj - A Native JS Object.
299
* @param index - Index of a member.
300
* @param pjobj - return parameter as a java object representing
304
nsCLiveconnect::GetSlot(JNIEnv *jEnv, jsobject obj, jint slot, void* principalsArray[],
305
int numPrincipals, nsISupports *securitySupports, jobject *pjobj)
307
if(jEnv == NULL || obj == 0)
309
return NS_ERROR_FAILURE;
312
JSJavaThreadState *jsj_env = NULL;
313
JSObjectHandle *handle = (JSObjectHandle*)obj;
314
JSObject *js_obj = handle->js_obj;
315
JSContext *cx = NULL;
316
jobject member = NULL;
319
JSBool dummy_bool = PR_FALSE;
320
JSErrorReporter saved_state = NULL;
322
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports);
324
return NS_ERROR_FAILURE;
326
AutoPushJSContext autopush(securitySupports, cx);
327
if (NS_FAILED(autopush.ResultOfPush()))
330
// =-= sudu: check to see if slot can be passed in as is.
331
// Should it be converted to a jsint?
332
if (!JS_GetElement(cx, js_obj, slot, &js_val))
334
if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
335
&dummy_cost, &member, &dummy_bool))
339
if (!jsj_exit_js(cx, jsj_env, saved_state))
340
return NS_ERROR_FAILURE;
347
* set member of a Native JSObject for a given name.
349
* @param jEnv - JNIEnv on which the call is being made.
350
* @param obj - A Native JS Object.
351
* @param name - Name of a member.
352
* @param jobj - Value to set. If this is a basic data type, it is converted
353
* using standard JNI calls but if it is a wrapper to a JSObject
354
* then a internal mapping is consulted to convert to a NJSObject.
357
nsCLiveconnect::SetMember(JNIEnv *jEnv, jsobject obj, const jchar *name, jsize length, jobject java_obj, void* principalsArray[],
358
int numPrincipals, nsISupports *securitySupports)
360
if(jEnv == NULL || obj == 0)
362
return NS_ERROR_FAILURE;
365
JSJavaThreadState *jsj_env = NULL;
366
JSObjectHandle *handle = (JSObjectHandle*)obj;
367
JSObject *js_obj = handle->js_obj;
368
JSContext *cx = NULL;
370
JSErrorReporter saved_state = NULL;
372
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports);
374
return NS_ERROR_FAILURE;
376
AutoPushJSContext autopush(securitySupports, cx);
377
if (NS_FAILED(autopush.ResultOfPush()))
381
JS_ReportError(cx, "illegal null member name");
385
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
388
JS_SetUCProperty(cx, js_obj, name, length, &js_val);
391
jsj_exit_js(cx, jsj_env, saved_state);
397
* set member of a Native JSObject for a given index.
399
* @param jEnv - JNIEnv on which the call is being made.
400
* @param obj - A Native JS Object.
401
* @param index - Index of a member.
402
* @param jobj - Value to set. If this is a basic data type, it is converted
403
* using standard JNI calls but if it is a wrapper to a JSObject
404
* then a internal mapping is consulted to convert to a NJSObject.
407
nsCLiveconnect::SetSlot(JNIEnv *jEnv, jsobject obj, jint slot, jobject java_obj, void* principalsArray[],
408
int numPrincipals, nsISupports *securitySupports)
410
if(jEnv == NULL || obj == 0)
412
return NS_ERROR_FAILURE;
415
JSJavaThreadState *jsj_env = NULL;
416
JSObjectHandle *handle = (JSObjectHandle*)obj;
417
JSObject *js_obj = handle->js_obj;
418
JSContext *cx = NULL;
420
JSErrorReporter saved_state = NULL;
422
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports);
424
return NS_ERROR_FAILURE;
426
AutoPushJSContext autopush(securitySupports, cx);
427
if (NS_FAILED(autopush.ResultOfPush()))
430
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
432
JS_SetElement(cx, js_obj, slot, &js_val);
435
jsj_exit_js(cx, jsj_env, saved_state);
441
* remove member of a Native JSObject for a given name.
443
* @param jEnv - JNIEnv on which the call is being made.
444
* @param obj - A Native JS Object.
445
* @param name - Name of a member.
448
nsCLiveconnect::RemoveMember(JNIEnv *jEnv, jsobject obj, const jchar *name, jsize length, void* principalsArray[],
449
int numPrincipals, nsISupports *securitySupports)
451
if(jEnv == NULL || obj == 0)
453
return NS_ERROR_FAILURE;
456
JSJavaThreadState *jsj_env = NULL;
457
JSObjectHandle *handle = (JSObjectHandle*)obj;
458
JSObject *js_obj = handle->js_obj;
459
JSContext *cx = NULL;
461
JSErrorReporter saved_state = NULL;
463
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports);
465
return NS_ERROR_FAILURE;
467
AutoPushJSContext autopush(securitySupports, cx);
468
if (NS_FAILED(autopush.ResultOfPush()))
472
JS_ReportError(cx, "illegal null member name");
476
JS_DeleteUCProperty2(cx, js_obj, name, length, &js_val);
479
jsj_exit_js(cx, jsj_env, saved_state);
485
* call a method of Native JSObject.
487
* @param jEnv - JNIEnv on which the call is being made.
488
* @param obj - A Native JS Object.
489
* @param name - Name of a method.
490
* @param jobjArr - Array of jobjects representing parameters of method being caled.
491
* @param pjobj - return value.
494
nsCLiveconnect::Call(JNIEnv *jEnv, jsobject obj, const jchar *name, jsize length, jobjectArray java_args, void* principalsArray[],
495
int numPrincipals, nsISupports *securitySupports, jobject *pjobj)
497
if(jEnv == NULL || obj == 0)
499
return NS_ERROR_FAILURE;
506
JSJavaThreadState *jsj_env = NULL;
507
JSObjectHandle *handle = (JSObjectHandle*)obj;
508
JSObject *js_obj = handle->js_obj;
509
JSContext *cx = NULL;
511
jsval function_val = 0;
513
JSBool dummy_bool = PR_FALSE;
514
JSErrorReporter saved_state = NULL;
515
jobject result = NULL;
517
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports);
519
return NS_ERROR_FAILURE;
522
AutoPushJSContext autopush(securitySupports, cx);
523
if (NS_FAILED(autopush.ResultOfPush()))
527
JS_ReportError(cx, "illegal null JavaScript function name");
531
/* Allocate space for JS arguments */
532
argc = java_args ? jEnv->GetArrayLength(java_args) : 0;
534
argv = (jsval*)JS_malloc(cx, argc * sizeof(jsval));
541
/* Convert arguments from Java to JS values */
542
for (arg_num = 0; arg_num < argc; arg_num++) {
543
jobject arg = jEnv->GetObjectArrayElement(java_args, arg_num);
545
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, arg, &argv[arg_num]))
547
JS_AddRoot(cx, &argv[arg_num]);
550
if (!JS_GetUCProperty(cx, js_obj, name, length, &function_val))
553
if (!JS_CallFunctionValue(cx, js_obj, function_val, argc, argv, &js_val))
556
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
557
&dummy_cost, &result, &dummy_bool);
561
for (i = 0; i < arg_num; i++)
562
JS_RemoveRoot(cx, &argv[i]);
567
if (!jsj_exit_js(cx, jsj_env, saved_state))
568
return NS_ERROR_FAILURE;
576
nsCLiveconnect::Eval(JNIEnv *jEnv, jsobject obj, const jchar *script, jsize length, void* principalsArray[],
577
int numPrincipals, nsISupports *securitySupports, jobject *pjobj)
579
if(jEnv == NULL || obj == 0)
581
return NS_ERROR_FAILURE;
584
JSJavaThreadState *jsj_env = NULL;
585
JSObjectHandle *handle = (JSObjectHandle*)obj;
586
JSObject *js_obj = handle->js_obj;
587
JSContext *cx = NULL;
590
JSBool dummy_bool = PR_FALSE;
591
JSErrorReporter saved_state = NULL;
592
jobject result = NULL;
593
const char *codebase = NULL;
594
JSPrincipals *principals = NULL;
595
JSBool eval_succeeded = PR_FALSE;
597
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports);
599
return NS_ERROR_FAILURE;
602
AutoPushJSContext autopush(securitySupports, cx);
603
if (NS_FAILED(autopush.ResultOfPush()))
607
JS_ReportError(cx, "illegal null string eval argument");
611
/* Set up security stuff */
612
if (JSJ_callbacks && JSJ_callbacks->get_JSPrincipals_from_java_caller)
613
principals = JSJ_callbacks->get_JSPrincipals_from_java_caller(jEnv, cx, principalsArray, numPrincipals, securitySupports);
614
codebase = principals ? principals->codebase : NULL;
616
/* Have the JS engine evaluate the unicode string */
617
eval_succeeded = JS_EvaluateUCScriptForPrincipals(cx, js_obj, principals,
619
codebase, 0, &js_val);
624
/* Convert result to a subclass of java.lang.Object */
625
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
626
&dummy_cost, &result, &dummy_bool);
630
JSPRINCIPALS_DROP(cx, principals);
631
if (!jsj_exit_js(cx, jsj_env, saved_state))
632
return NS_ERROR_FAILURE;
640
* Get the window object for a plugin instance.
642
* @param jEnv - JNIEnv on which the call is being made.
643
* @param pJavaObject - Either a jobject or a pointer to a plugin instance
644
* representing the java object.
645
* @param pjobj - return value. This is a native js object
646
* representing the window object of a frame
647
* in which a applet/bean resides.
650
nsCLiveconnect::GetWindow(JNIEnv *jEnv, void *pJavaObject, void* principalsArray[],
651
int numPrincipals, nsISupports *securitySupports, jsobject *pobj)
653
if(jEnv == NULL || JSJ_callbacks == NULL)
655
return NS_ERROR_FAILURE;
658
// associate this Java client with this LiveConnect connection.
659
mJavaClient = pJavaObject;
661
char *err_msg = NULL;
662
JSContext *cx = NULL;
663
JSObject *js_obj = NULL;
664
JSErrorReporter saved_state = NULL;
665
JSJavaThreadState *jsj_env = NULL;
666
JSObjectHandle *handle = NULL;
668
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports);
670
return NS_ERROR_FAILURE;
673
AutoPushJSContext autopush(securitySupports, cx);
674
if (NS_FAILED(autopush.ResultOfPush()))
677
js_obj = JSJ_callbacks->map_java_object_to_js_object(jEnv, mJavaClient, &err_msg);
680
JS_ReportError(cx, err_msg);
685
#ifdef PRESERVE_JSOBJECT_IDENTITY
686
*pobj = (jint)js_obj;
688
/* FIXME: to handle PRESERVE_JSOBJECT_IDENTITY case, this needs to
689
just return a raw JSObject reference. FIXME !!! */
690
/* Create a tiny stub object to act as the GC root that points to the
691
JSObject from its netscape.javascript.JSObject counterpart. */
692
handle = (JSObjectHandle*)JS_malloc(cx, sizeof(JSObjectHandle));
695
handle->js_obj = js_obj;
696
handle->rt = JS_GetRuntime(cx);
698
*pobj = (jsobject)NS_PTR_TO_INT32(handle);
699
/* FIXME: what if the window is explicitly disposed of, how do we
703
if (!jsj_exit_js(cx, jsj_env, saved_state))
704
return NS_ERROR_FAILURE;
710
* Get the window object for a plugin instance.
712
* @param jEnv - JNIEnv on which the call is being made.
713
* @param obj - A Native JS Object.
716
nsCLiveconnect::FinalizeJSObject(JNIEnv *jEnv, jsobject obj)
718
if(jEnv == NULL || obj == 0)
720
return NS_ERROR_FAILURE;
723
JSObjectHandle *handle = (JSObjectHandle *)obj;
726
return NS_ERROR_NULL_POINTER;
727
JS_RemoveRootRT(handle->rt, &handle->js_obj);
734
nsCLiveconnect::ToString(JNIEnv *jEnv, jsobject obj, jstring *pjstring)
736
if(jEnv == NULL || obj == 0)
738
return NS_ERROR_FAILURE;
741
JSJavaThreadState *jsj_env = NULL;
742
JSObjectHandle *handle = (JSObjectHandle*)obj;
743
JSObject *js_obj = handle->js_obj;
744
JSContext *cx = NULL;
745
JSErrorReporter saved_state = NULL;
746
jstring result = NULL;
747
JSString *jsstr = NULL;
749
jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, NULL, 0, NULL );
751
return NS_ERROR_FAILURE;
754
AutoPushJSContext autopush(nsnull, cx);
755
if (NS_FAILED(autopush.ResultOfPush()))
756
return NS_ERROR_FAILURE;
758
jsstr = JS_ValueToString(cx, OBJECT_TO_JSVAL(js_obj));
760
result = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr);
762
result = jEnv->NewStringUTF("*JavaObject*");
764
if (!jsj_exit_js(cx, jsj_env, saved_state))
765
return NS_ERROR_FAILURE;
771
////////////////////////////////////////////////////////////////////////////
772
// from nsCLiveconnect:
774
nsCLiveconnect::nsCLiveconnect(nsISupports *aOuter)
777
NS_INIT_AGGREGATED(aOuter);
778
#ifdef PRESERVE_JSOBJECT_IDENTITY
779
jsj_init_js_obj_reflections_table();
783
nsCLiveconnect::~nsCLiveconnect()