1 /* 2 * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 5 #include "config.h" 6 7 #include "BridgeUtils.h" 8 9 #include <JavaScriptCore/CallFrame.h> 10 #include <JavaScriptCore/Identifier.h> 11 12 #include "Frame.h" 13 #include "JavaInstanceJSC.h" 14 #include "JavaArrayJSC.h" 15 #include "JavaRuntimeObject.h" 16 #include "JNIUtilityPrivate.h" 17 #include "JSDOMBinding.h" 18 #include "JSDOMGlobalObject.h" 19 #include "JSMainThreadExecState.h" 20 #include "JSNode.h" 21 #include "ScriptController.h" 22 #include "runtime_array.h" 23 #include "runtime_object.h" 24 #include "runtime_root.h" 25 #include <runtime/JSArray.h> 26 #include <runtime/JSLock.h> 27 #include <wtf/java/JavaRef.h> 28 #include <wtf/text/WTFString.h> 29 #include <APICast.h> 30 #include <API/OpaqueJSString.h> 31 #include <API/JSBase.h> 32 #include <API/JSStringRef.h> 33 34 #include "com_sun_webkit_dom_JSObject.h" 35 36 #if 1 37 #define FIND_CACHE_CLASS(ENV, SIG) \ 38 static JGClass cls((ENV)->FindClass(SIG)); return cls 39 #else 40 #define FIND_CACHE_CLASS(ENV, SIG) \ 41 static jclass cls = (jclass) (ENV)->NewGlobalRef((ENV)->FindClass(SIG)); \ 42 return cls 43 #endif 44 45 static jclass getJSObjectClass (JNIEnv *env) 46 { 47 FIND_CACHE_CLASS(env, "com/sun/webkit/dom/JSObject"); 48 } 49 50 static jclass getJSExceptionClass (JNIEnv *env) 51 { 52 FIND_CACHE_CLASS(env, "netscape/javascript/JSException"); 53 } 54 55 static jclass getNodeImplClass (JNIEnv *env) 56 { 57 FIND_CACHE_CLASS(env, "com/sun/webkit/dom/NodeImpl"); 58 } 59 60 static jclass getNumberClass (JNIEnv *env) 61 { 62 FIND_CACHE_CLASS(env, "java/lang/Number"); 63 } 64 65 static jclass getDoubleClass (JNIEnv *env) 66 { 67 FIND_CACHE_CLASS(env, "java/lang/Double"); 68 } 69 70 static jclass getIntegerClass (JNIEnv *env) 71 { 72 FIND_CACHE_CLASS(env, "java/lang/Integer"); 73 } 74 75 static jclass getBooleanClass (JNIEnv *env) 76 { 77 FIND_CACHE_CLASS(env, "java/lang/Boolean"); 78 } 79 80 static jclass getStringClass (JNIEnv *env) 81 { 82 FIND_CACHE_CLASS(env, "java/lang/String"); 83 } 84 85 static jclass getNullPointerExceptionClass (JNIEnv *env) 86 { 87 FIND_CACHE_CLASS(env, "java/lang/NullPointerException"); 88 } 89 90 static void throwNullPointerException (JNIEnv *env) 91 { 92 jclass clJSException = getNullPointerExceptionClass(env); 93 env->Throw((jthrowable) env->NewObject(clJSException, 94 env->GetMethodID(clJSException, "<init>", "()V"))); 95 } 96 97 namespace WebCore { 98 99 JSGlobalContextRef getGlobalContext(WebCore::ScriptController* scriptController) 100 { 101 return toGlobalRef(scriptController->globalObject(WebCore::mainThreadNormalWorld())->globalExec()); 102 } 103 104 JSStringRef asJSStringRef(JNIEnv *env, jstring str) 105 { 106 unsigned int slen = env->GetStringLength(str); 107 const jchar* schars = env->GetStringCritical(str, NULL); 108 JSStringRef name = JSStringCreateWithCharacters((const JSChar*) schars, slen); 109 env->ReleaseStringCritical(str, schars); 110 return name; 111 } 112 113 JSValueRef Java_Object_to_JSValue( 114 JNIEnv *env, 115 JSContextRef ctx, 116 JSC::Bindings::RootObject* rootObject, 117 jobject val, 118 jobject accessControlContext) 119 { 120 if (val == NULL) 121 return JSValueMakeNull(ctx); 122 JSC::ExecState* exec = toJS(ctx); 123 JSC::JSLockHolder lock(exec); 124 125 jclass clJSObject = getJSObjectClass(env); 126 if (env->IsInstanceOf(val, clJSObject)) { 127 static jfieldID fldPeer = env->GetFieldID(clJSObject, "peer", "J"); 128 static jfieldID fldPeerType = env->GetFieldID(clJSObject, "peer_type", "I"); 129 jlong peer = env->GetLongField(val, fldPeer); 130 jint peer_type = env->GetIntField(val, fldPeerType); 131 switch (peer_type) { 132 case com_sun_webkit_dom_JSObject_JS_CONTEXT_OBJECT: 133 return static_cast<JSObjectRef>(jlong_to_ptr(peer)); 134 case com_sun_webkit_dom_JSObject_JS_DOM_NODE_OBJECT: 135 case com_sun_webkit_dom_JSObject_JS_DOM_WINDOW_OBJECT: 136 { 137 JSDOMGlobalObject* globalObject = toJSDOMGlobalObject( 138 ((peer_type == com_sun_webkit_dom_JSObject_JS_DOM_WINDOW_OBJECT) 139 ? static_cast<DOMWindow*>(jlong_to_ptr(peer))->document() 140 : &static_cast<Node*>(jlong_to_ptr(peer))->document()), 141 exec); 142 return toRef(exec, 143 (peer_type == com_sun_webkit_dom_JSObject_JS_DOM_WINDOW_OBJECT) 144 ? WebCore::toJS(exec, globalObject, static_cast<DOMWindow*>(jlong_to_ptr(peer))) 145 : WebCore::toJS(exec, globalObject, static_cast<Node*>(jlong_to_ptr(peer)))); 146 } 147 } 148 } 149 jclass clString = getStringClass(env); 150 if (env->IsInstanceOf(val, clString)) { 151 JSStringRef value = asJSStringRef(env, (jstring) val); 152 JSValueRef jsvalue = JSValueMakeString(ctx, value); 153 JSStringRelease(value); 154 return jsvalue; 155 } 156 jclass clBoolean = getBooleanClass(env); 157 if (env->IsInstanceOf(val, clBoolean)) { 158 static jmethodID booleanValueMethod = env->GetMethodID(clBoolean, "booleanValue", "()Z"); 159 jboolean value = env->CallBooleanMethod(val, booleanValueMethod); 160 return JSValueMakeBoolean(ctx, value); 161 } 162 jclass clNumber = getNumberClass(env); 163 if (env->IsInstanceOf(val, clNumber)) { 164 static jmethodID doubleValueMethod = env->GetMethodID(clNumber, "doubleValue", "()D"); 165 jdouble value = env->CallDoubleMethod(val, doubleValueMethod); 166 return JSValueMakeNumber(ctx, value); 167 } 168 169 JLObject valClass(JSC::Bindings::callJNIMethod<jobject>(val, "getClass", "()Ljava/lang/Class;")); 170 if (JSC::Bindings::callJNIMethod<jboolean>(valClass, "isArray", "()Z")) { 171 JLString className((jstring)JSC::Bindings::callJNIMethod<jobject>(valClass, "getName", "()Ljava/lang/String;")); 172 const char* classNameC = JSC::Bindings::getCharactersFromJString(className); 173 JSC::JSValue arr = JSC::Bindings::JavaArray::convertJObjectToArray(exec, val, classNameC, rootObject, accessControlContext); 174 JSC::Bindings::releaseCharactersForJString(className, classNameC); 175 return toRef(exec, arr); 176 } 177 else { 178 // All other Java Object types including java.lang.Character will be wrapped inside JavaInstance. 179 PassRefPtr<JSC::Bindings::JavaInstance> jinstance = JSC::Bindings::JavaInstance::create(val, rootObject, accessControlContext); 180 return toRef(jinstance->createRuntimeObject(exec)); 181 } 182 } 183 184 jstring JSValue_to_Java_String(JSValueRef value, JNIEnv* env, JSContextRef ctx) 185 { 186 JSStringRef str = JSValueToStringCopy(ctx, value, NULL); 187 size_t slen = JSStringGetLength(str); 188 const JSChar* schars = JSStringGetCharactersPtr(str); 189 jstring result = env->NewString((const jchar*) schars, slen); 190 JSStringRelease(str); 191 return result; 192 } 193 194 jobject JSValue_to_Java_Object( 195 JSValueRef value, 196 JNIEnv*, 197 JSContextRef ctx, 198 JSC::Bindings::RootObject* rootObject) 199 { 200 JSC::ExecState* exec = toJS(ctx); 201 return convertValueToJValue(exec, rootObject, toJS(exec, value), 202 JSC::Bindings::JavaTypeObject, "java.lang.Object").l; 203 } 204 205 static void throwJavaException( 206 JNIEnv* env, 207 JSContextRef ctx, 208 JSValueRef exception, 209 JSC::Bindings::RootObject* rootObject) 210 { 211 jclass clJSObject = getJSObjectClass(env); 212 jobject jex = JSValue_to_Java_Object(exception, env, ctx, rootObject); 213 static jmethodID makeID = 214 env->GetStaticMethodID(clJSObject, "fwkMakeException", 215 "(Ljava/lang/Object;)Lnetscape/javascript/JSException;"); 216 217 env->Throw(JLocalRef<jthrowable>((jthrowable)env->CallStaticObjectMethod( 218 clJSObject, 219 makeID, 220 jex))); 221 } 222 223 jobject executeScript( 224 JNIEnv* env, 225 JSObjectRef object, 226 JSContextRef ctx, 227 JSC::Bindings::RootObject *rootObject, 228 jstring str) 229 { 230 if (str == NULL) { 231 throwNullPointerException(env); 232 return NULL; 233 } 234 JSStringRef script = asJSStringRef(env, str); 235 JSValueRef exception = 0; 236 JSValueRef value = JSEvaluateScript(ctx, script, object, NULL, 1, &exception); 237 JSStringRelease(script); 238 if (exception) { 239 throwJavaException(env, ctx, exception, rootObject); 240 return NULL; 241 } 242 return WebCore::JSValue_to_Java_Object(value, env, ctx, rootObject); 243 } 244 245 } 246 247 248 PassRefPtr<JSC::Bindings::RootObject> checkJSPeer( 249 jlong peer, 250 jint peer_type, 251 JSObjectRef &object, 252 JSContextRef &context) 253 { 254 JSC::Bindings::RootObject *rootObject = NULL; 255 switch (peer_type) { 256 case com_sun_webkit_dom_JSObject_JS_CONTEXT_OBJECT: 257 { 258 //based on [JavaJSObject] implementation 259 //[Source/WebCore/bridge/jni/jni_jsobject.mm] 260 object = static_cast<JSObjectRef>(jlong_to_ptr(peer)); 261 rootObject = JSC::Bindings::findProtectingRootObject(reinterpret_cast<JSC::JSObject*>(object)); 262 if (rootObject) { 263 context = toRef(rootObject->globalObject()->globalExec()); 264 } 265 } 266 break; 267 case com_sun_webkit_dom_JSObject_JS_DOM_NODE_OBJECT: 268 case com_sun_webkit_dom_JSObject_JS_DOM_WINDOW_OBJECT: 269 { 270 WebCore::Frame* frame = (peer_type == com_sun_webkit_dom_JSObject_JS_DOM_WINDOW_OBJECT) 271 ? static_cast<WebCore::DOMWindow*>(jlong_to_ptr(peer))->document()->frame() 272 : static_cast<WebCore::Node*>(jlong_to_ptr(peer))->document().frame(); 273 274 rootObject = frame->script().createRootObject(frame).leakRef(); 275 if (rootObject) { 276 context = WebCore::getGlobalContext(&frame->script()); 277 JSC::ExecState* exec = toJS(context); 278 JSC::JSLockHolder lock(exec); 279 280 object = const_cast<JSObjectRef>(toRef(exec, 281 (peer_type == com_sun_webkit_dom_JSObject_JS_DOM_WINDOW_OBJECT) 282 ? WebCore::toJS(exec, static_cast<WebCore::JSDOMGlobalObject *>(rootObject->globalObject()), static_cast<WebCore::DOMWindow*>(jlong_to_ptr(peer))) 283 : WebCore::toJS(exec, static_cast<WebCore::JSDOMGlobalObject *>(rootObject->globalObject()), static_cast<WebCore::Node*>(jlong_to_ptr(peer))))); 284 285 } 286 } 287 break; 288 }; 289 290 return rootObject; 291 } 292 293 294 295 extern "C" { 296 297 JNIEXPORT jobject JNICALL Java_com_sun_webkit_dom_JSObject_evalImpl 298 (JNIEnv *env, jclass, jlong peer, jint peer_type, jstring str) 299 { 300 if (str == NULL) { 301 throwNullPointerException(env); 302 return NULL; 303 } 304 JSObjectRef object; 305 JSContextRef ctx; 306 RefPtr<JSC::Bindings::RootObject> rootObject(checkJSPeer(peer, peer_type, object, ctx)); 307 308 return WebCore::executeScript(env, object, ctx, rootObject.get(), str); 309 } 310 311 JNIEXPORT jobject JNICALL Java_com_sun_webkit_dom_JSObject_getMemberImpl 312 (JNIEnv *env, jclass, jlong peer, jint peer_type, jstring str) 313 { 314 if (str == NULL) { 315 throwNullPointerException(env); 316 return NULL; 317 } 318 JSObjectRef object; 319 JSContextRef ctx; 320 RefPtr<JSC::Bindings::RootObject> rootObject(checkJSPeer(peer, peer_type, object, ctx)); 321 322 JSStringRef name = WebCore::asJSStringRef(env, str); 323 JSValueRef value = JSObjectGetProperty(ctx, object, name, NULL); 324 JSStringRelease(name); 325 return WebCore::JSValue_to_Java_Object(value, env, ctx, rootObject.get()); 326 } 327 328 JNIEXPORT void JNICALL Java_com_sun_webkit_dom_JSObject_setMemberImpl 329 (JNIEnv *env, jclass, jlong peer, jint peer_type, jstring str, jobject value, jobject accessControlContext) 330 { 331 if (str == NULL) { 332 throwNullPointerException(env); 333 return; 334 } 335 JSObjectRef object; 336 JSContextRef ctx; 337 RefPtr<JSC::Bindings::RootObject> rootObject(checkJSPeer(peer, peer_type, object, ctx)); 338 339 JSStringRef name = WebCore::asJSStringRef(env, str); 340 JSValueRef jsvalue = WebCore::Java_Object_to_JSValue(env, ctx, rootObject.get(), value, accessControlContext); 341 JSPropertyAttributes attributes = 0; 342 JSValueRef exception = 0; 343 JSObjectSetProperty(ctx, object, name, jsvalue, attributes, &exception); 344 JSStringRelease(name); 345 if (exception) 346 WebCore::throwJavaException(env, ctx, exception, rootObject.get()); 347 } 348 349 JNIEXPORT void JNICALL Java_com_sun_webkit_dom_JSObject_removeMemberImpl 350 (JNIEnv *env, jclass, jlong peer, jint peer_type, jstring str) 351 { 352 if (str == NULL) { 353 throwNullPointerException(env); 354 return; 355 } 356 JSObjectRef object; 357 JSContextRef ctx; 358 checkJSPeer(peer, peer_type, object, ctx); 359 360 JSStringRef name = WebCore::asJSStringRef(env, str); 361 JSObjectDeleteProperty(ctx, object, name, NULL); 362 JSStringRelease(name); 363 } 364 365 JNIEXPORT jobject JNICALL Java_com_sun_webkit_dom_JSObject_getSlotImpl 366 (JNIEnv *env, jclass, jlong peer, jint peer_type, jint index) 367 { 368 JSObjectRef object; 369 JSContextRef ctx; 370 RefPtr<JSC::Bindings::RootObject> rootObject(checkJSPeer(peer, peer_type, object, ctx)); 371 372 JSValueRef value = JSObjectGetPropertyAtIndex(ctx, object, index, NULL); 373 return WebCore::JSValue_to_Java_Object(value, env, ctx, rootObject.get()); 374 } 375 376 JNIEXPORT void JNICALL Java_com_sun_webkit_dom_JSObject_setSlotImpl 377 (JNIEnv *env, jclass, jlong peer, jint peer_type, jint index, jobject value, jobject accessControlContext) 378 { 379 JSObjectRef object; 380 JSContextRef ctx; 381 RefPtr<JSC::Bindings::RootObject> rootObject(checkJSPeer(peer, peer_type, object, ctx)); 382 383 JSValueRef jsvalue = WebCore::Java_Object_to_JSValue(env, ctx, rootObject.get(), value, accessControlContext); 384 JSObjectSetPropertyAtIndex(ctx, object, (unsigned) index, jsvalue, NULL); 385 } 386 387 JNIEXPORT jstring JNICALL Java_com_sun_webkit_dom_JSObject_toStringImpl 388 (JNIEnv *env, jclass, jlong peer, jint peer_type) 389 { 390 JSObjectRef object; 391 JSContextRef ctx; 392 checkJSPeer(peer, peer_type, object, ctx); 393 394 JSC::ExecState* exec = toJS(ctx); 395 396 return toJS(object)->toString(exec)->value(exec) 397 .toJavaString(env).releaseLocal(); 398 } 399 400 JNIEXPORT jobject JNICALL Java_com_sun_webkit_dom_JSObject_callImpl 401 (JNIEnv *env, jclass, jlong peer, jint peer_type, jstring methodName, jobjectArray args, jobject accessControlContext) 402 { 403 if (methodName == NULL || args == NULL) { 404 throwNullPointerException(env); 405 return NULL; 406 } 407 JSObjectRef object; 408 JSContextRef ctx; 409 RefPtr<JSC::Bindings::RootObject> rootObject(checkJSPeer(peer, peer_type, object, ctx)); 410 if (!rootObject || !ctx) { 411 env->ThrowNew(getJSExceptionClass(env), "Invalid function reference"); 412 return NULL; 413 } 414 415 JSStringRef name = WebCore::asJSStringRef(env, methodName); 416 JSValueRef member = JSObjectGetProperty(ctx, object, name, NULL); 417 JSStringRelease(name); 418 if (!JSValueIsObject(ctx, member)) 419 return JSC::Bindings::convertUndefinedToJObject(); 420 JSObjectRef function = JSValueToObject(ctx, member, NULL); 421 if (! JSObjectIsFunction(ctx, function)) 422 return JSC::Bindings::convertUndefinedToJObject(); 423 size_t argumentCount = env->GetArrayLength(args); 424 JSValueRef *arguments = new JSValueRef[argumentCount]; 425 for (int i = 0; i < argumentCount; i++) { 426 JLObject jarg(env->GetObjectArrayElement(args, i)); 427 arguments[i] = WebCore::Java_Object_to_JSValue(env, ctx, rootObject.get(), jarg, accessControlContext); 428 } 429 JSValueRef exception = 0; 430 JSValueRef result = JSObjectCallAsFunction(ctx, function, object, 431 argumentCount, arguments, 432 &exception); 433 delete[] arguments; 434 if (exception) { 435 WebCore::throwJavaException(env, ctx, exception, rootObject.get()); 436 return NULL; 437 } 438 return WebCore::JSValue_to_Java_Object(result, env, ctx, rootObject.get()); 439 } 440 441 JNIEXPORT void JNICALL Java_com_sun_webkit_dom_JSObject_unprotectImpl 442 (JNIEnv*, jclass, jlong peer, jint peer_type) 443 { 444 JSObjectRef object; 445 JSContextRef ctx; 446 RefPtr<JSC::Bindings::RootObject> rootObject(checkJSPeer(peer, peer_type, object, ctx)); 447 if (!rootObject || !peer || !ctx) { 448 return; 449 } 450 451 rootObject->gcUnprotect(toJS(object)); 452 } 453 454 }