1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/android/NativeJSContainer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,567 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "NativeJSContainer.h" 1.10 +#include "AndroidBridge.h" 1.11 +#include "mozilla/Vector.h" 1.12 +#include "prthread.h" 1.13 + 1.14 +using namespace mozilla; 1.15 +using namespace mozilla::widget; 1.16 + 1.17 +namespace mozilla { 1.18 +namespace widget { 1.19 + 1.20 +class NativeJSContainer 1.21 +{ 1.22 +public: 1.23 + static void InitJNI(JNIEnv* env) { 1.24 + if (jNativeJSContainer) { 1.25 + return; 1.26 + } 1.27 + jNativeJSContainer = AndroidBridge::GetClassGlobalRef( 1.28 + env, "org/mozilla/gecko/util/NativeJSContainer"); 1.29 + MOZ_ASSERT(jNativeJSContainer); 1.30 + jContainerNativeObject = AndroidBridge::GetFieldID( 1.31 + env, jNativeJSContainer, "mNativeObject", "J"); 1.32 + MOZ_ASSERT(jContainerNativeObject); 1.33 + jContainerConstructor = AndroidBridge::GetMethodID( 1.34 + env, jNativeJSContainer, "<init>", "(J)V"); 1.35 + MOZ_ASSERT(jContainerConstructor); 1.36 + 1.37 + jNativeJSObject = AndroidBridge::GetClassGlobalRef( 1.38 + env, "org/mozilla/gecko/util/NativeJSObject"); 1.39 + MOZ_ASSERT(jNativeJSObject); 1.40 + jObjectContainer = AndroidBridge::GetFieldID( 1.41 + env, jNativeJSObject, "mContainer", 1.42 + "Lorg/mozilla/gecko/util/NativeJSContainer;"); 1.43 + MOZ_ASSERT(jObjectContainer); 1.44 + jObjectIndex = AndroidBridge::GetFieldID( 1.45 + env, jNativeJSObject, "mObjectIndex", "I"); 1.46 + MOZ_ASSERT(jObjectIndex); 1.47 + jObjectConstructor = AndroidBridge::GetMethodID( 1.48 + env, jNativeJSObject, "<init>", 1.49 + "(Lorg/mozilla/gecko/util/NativeJSContainer;I)V"); 1.50 + MOZ_ASSERT(jContainerConstructor); 1.51 + } 1.52 + 1.53 + static jobject CreateInstance(JNIEnv* env, JSContext* cx, 1.54 + JS::HandleObject object) { 1.55 + return CreateInstance(env, new NativeJSContainer(cx, object)); 1.56 + } 1.57 + 1.58 + static NativeJSContainer* FromInstance(JNIEnv* env, jobject instance) { 1.59 + MOZ_ASSERT(instance); 1.60 + 1.61 + const jlong fieldValue = 1.62 + env->GetLongField(instance, jContainerNativeObject); 1.63 + NativeJSContainer* const nativeObject = 1.64 + reinterpret_cast<NativeJSContainer* const>( 1.65 + static_cast<uintptr_t>(fieldValue)); 1.66 + if (!nativeObject) { 1.67 + AndroidBridge::ThrowException(env, 1.68 + "java/lang/NullPointerException", 1.69 + "Uninitialized NativeJSContainer"); 1.70 + } 1.71 + return nativeObject; 1.72 + } 1.73 + 1.74 + static void DisposeInstance(JNIEnv* env, jobject instance) { 1.75 + NativeJSContainer* const container = FromInstance(env, instance); 1.76 + if (container) { 1.77 + env->SetLongField(instance, jContainerNativeObject, 1.78 + static_cast<jlong>(reinterpret_cast<uintptr_t>(nullptr))); 1.79 + delete container; 1.80 + } 1.81 + } 1.82 + 1.83 + static jobject CloneInstance(JNIEnv* env, jobject instance) { 1.84 + NativeJSContainer* const container = FromInstance(env, instance); 1.85 + if (!container || !container->EnsureObject(env)) { 1.86 + return nullptr; 1.87 + } 1.88 + JSContext* const cx = container->mThreadContext; 1.89 + JS::RootedObject object(cx, container->mJSObject); 1.90 + MOZ_ASSERT(object); 1.91 + 1.92 + JSAutoStructuredCloneBuffer buffer; 1.93 + if (!buffer.write(cx, JS::RootedValue(cx, JS::ObjectValue(*object)))) { 1.94 + AndroidBridge::ThrowException(env, 1.95 + "java/lang/UnsupportedOperationException", 1.96 + "Cannot serialize object"); 1.97 + return nullptr; 1.98 + } 1.99 + return CreateInstance(env, new NativeJSContainer(cx, Move(buffer))); 1.100 + } 1.101 + 1.102 + static jobject GetInstanceFromObject(JNIEnv* env, jobject object) { 1.103 + MOZ_ASSERT(object); 1.104 + 1.105 + const jobject instance = env->GetObjectField(object, jObjectContainer); 1.106 + MOZ_ASSERT(instance); 1.107 + return instance; 1.108 + } 1.109 + 1.110 + static JSContext* GetContextFromObject(JNIEnv* env, jobject object) { 1.111 + MOZ_ASSERT(object); 1.112 + AutoLocalJNIFrame frame(env, 1); 1.113 + 1.114 + NativeJSContainer* const container = 1.115 + FromInstance(env, GetInstanceFromObject(env, object)); 1.116 + if (!container) { 1.117 + return nullptr; 1.118 + } 1.119 + return container->mThreadContext; 1.120 + } 1.121 + 1.122 + static JSObject* GetObjectFromObject(JNIEnv* env, jobject object) { 1.123 + MOZ_ASSERT(object); 1.124 + AutoLocalJNIFrame frame(env, 1); 1.125 + 1.126 + NativeJSContainer* const container = 1.127 + FromInstance(env, GetInstanceFromObject(env, object)); 1.128 + if (!container || 1.129 + !container->EnsureObject(env)) { // Do thread check 1.130 + return nullptr; 1.131 + } 1.132 + const jint index = env->GetIntField(object, jObjectIndex); 1.133 + if (index < 0) { 1.134 + // -1 for index field means it's the root object of the container 1.135 + return container->mJSObject; 1.136 + } 1.137 + return container->mRootedObjects[index]; 1.138 + } 1.139 + 1.140 + static jobject CreateObjectInstance(JNIEnv* env, jobject object, 1.141 + JS::HandleObject jsObject) { 1.142 + MOZ_ASSERT(object); 1.143 + MOZ_ASSERT(jsObject); 1.144 + AutoLocalJNIFrame frame(env, 2); 1.145 + 1.146 + jobject instance = GetInstanceFromObject(env, object); 1.147 + NativeJSContainer* const container = FromInstance(env, instance); 1.148 + if (!container) { 1.149 + return nullptr; 1.150 + } 1.151 + size_t newIndex = container->mRootedObjects.length(); 1.152 + if (!container->mRootedObjects.append(jsObject)) { 1.153 + AndroidBridge::ThrowException(env, 1.154 + "java/lang/OutOfMemoryError", "Cannot allocate object"); 1.155 + return nullptr; 1.156 + } 1.157 + const jobject newObject = 1.158 + env->NewObject(jNativeJSObject, jObjectConstructor, 1.159 + instance, newIndex); 1.160 + AndroidBridge::HandleUncaughtException(env); 1.161 + MOZ_ASSERT(newObject); 1.162 + return frame.Pop(newObject); 1.163 + } 1.164 + 1.165 + // Make sure we have a JSObject and deserialize if necessary/possible 1.166 + bool EnsureObject(JNIEnv* env) { 1.167 + if (mJSObject) { 1.168 + if (PR_GetCurrentThread() != mThread) { 1.169 + AndroidBridge::ThrowException(env, 1.170 + "java/lang/IllegalThreadStateException", 1.171 + "Using NativeJSObject off its thread"); 1.172 + return false; 1.173 + } 1.174 + return true; 1.175 + } 1.176 + if (!SwitchContextToCurrentThread()) { 1.177 + AndroidBridge::ThrowException(env, 1.178 + "java/lang/IllegalThreadStateException", 1.179 + "Not available for this thread"); 1.180 + return false; 1.181 + } 1.182 + 1.183 + JS::RootedValue value(mThreadContext); 1.184 + MOZ_ASSERT(mBuffer.data()); 1.185 + MOZ_ALWAYS_TRUE(mBuffer.read(mThreadContext, &value)); 1.186 + if (value.isObject()) { 1.187 + mJSObject = &value.toObject(); 1.188 + } 1.189 + if (!mJSObject) { 1.190 + AndroidBridge::ThrowException(env, 1.191 + "java/lang/IllegalStateException", "Cannot deserialize data"); 1.192 + return false; 1.193 + } 1.194 + mBuffer.clear(); 1.195 + return true; 1.196 + } 1.197 + 1.198 +private: 1.199 + static jclass jNativeJSContainer; 1.200 + static jfieldID jContainerNativeObject; 1.201 + static jmethodID jContainerConstructor; 1.202 + static jclass jNativeJSObject; 1.203 + static jfieldID jObjectContainer; 1.204 + static jfieldID jObjectIndex; 1.205 + static jmethodID jObjectConstructor; 1.206 + 1.207 + static jobject CreateInstance(JNIEnv* env, 1.208 + NativeJSContainer* nativeObject) { 1.209 + InitJNI(env); 1.210 + const jobject newObject = 1.211 + env->NewObject(jNativeJSContainer, jContainerConstructor, 1.212 + static_cast<jlong>( 1.213 + reinterpret_cast<uintptr_t>(nativeObject))); 1.214 + AndroidBridge::HandleUncaughtException(env); 1.215 + MOZ_ASSERT(newObject); 1.216 + return newObject; 1.217 + } 1.218 + 1.219 + // Thread that the object is valid on 1.220 + PRThread* mThread; 1.221 + // Context that the object is valid in 1.222 + JSContext* mThreadContext; 1.223 + // Deserialized object, or nullptr if object is in serialized form 1.224 + JS::Heap<JSObject*> mJSObject; 1.225 + // Serialized object, or empty if object is in deserialized form 1.226 + JSAutoStructuredCloneBuffer mBuffer; 1.227 + // Objects derived from mJSObject 1.228 + Vector<JS::Heap<JSObject*>, 4> mRootedObjects; 1.229 + 1.230 + // Create a new container containing the given deserialized object 1.231 + NativeJSContainer(JSContext* cx, JS::HandleObject object) 1.232 + : mThread(PR_GetCurrentThread()) 1.233 + , mThreadContext(cx) 1.234 + , mJSObject(object) 1.235 + { 1.236 + } 1.237 + 1.238 + // Create a new container containing the given serialized object 1.239 + NativeJSContainer(JSContext* cx, JSAutoStructuredCloneBuffer&& buffer) 1.240 + : mThread(PR_GetCurrentThread()) 1.241 + , mThreadContext(cx) 1.242 + , mBuffer(Forward<JSAutoStructuredCloneBuffer>(buffer)) 1.243 + { 1.244 + } 1.245 + 1.246 + bool SwitchContextToCurrentThread() { 1.247 + PRThread* const currentThread = PR_GetCurrentThread(); 1.248 + if (currentThread == mThread) { 1.249 + return true; 1.250 + } 1.251 + return false; 1.252 + } 1.253 +}; 1.254 + 1.255 +jclass NativeJSContainer::jNativeJSContainer = 0; 1.256 +jfieldID NativeJSContainer::jContainerNativeObject = 0; 1.257 +jmethodID NativeJSContainer::jContainerConstructor = 0; 1.258 +jclass NativeJSContainer::jNativeJSObject = 0; 1.259 +jfieldID NativeJSContainer::jObjectContainer = 0; 1.260 +jfieldID NativeJSContainer::jObjectIndex = 0; 1.261 +jmethodID NativeJSContainer::jObjectConstructor = 0; 1.262 + 1.263 +jobject 1.264 +CreateNativeJSContainer(JNIEnv* env, JSContext* cx, JS::HandleObject object) 1.265 +{ 1.266 + return NativeJSContainer::CreateInstance(env, cx, object); 1.267 +} 1.268 + 1.269 +} // namespace widget 1.270 +} // namespace mozilla 1.271 + 1.272 +namespace { 1.273 + 1.274 +class JSJNIString 1.275 +{ 1.276 +public: 1.277 + JSJNIString(JNIEnv* env, jstring str) 1.278 + : mEnv(env) 1.279 + , mJNIString(str) 1.280 + , mJSString(!str ? nullptr : 1.281 + reinterpret_cast<const jschar*>(env->GetStringChars(str, nullptr))) 1.282 + { 1.283 + } 1.284 + ~JSJNIString() { 1.285 + if (mJNIString) { 1.286 + mEnv->ReleaseStringChars(mJNIString, 1.287 + reinterpret_cast<const jchar*>(mJSString)); 1.288 + } 1.289 + } 1.290 + operator const jschar*() const { 1.291 + return mJSString; 1.292 + } 1.293 + size_t Length() const { 1.294 + return static_cast<size_t>(mEnv->GetStringLength(mJNIString)); 1.295 + } 1.296 +private: 1.297 + JNIEnv* const mEnv; 1.298 + const jstring mJNIString; 1.299 + const jschar* const mJSString; 1.300 +}; 1.301 + 1.302 +bool 1.303 +CheckJSCall(JNIEnv* env, bool result) { 1.304 + if (!result) { 1.305 + AndroidBridge::ThrowException(env, 1.306 + "java/lang/UnsupportedOperationException", "JSAPI call failed"); 1.307 + } 1.308 + return result; 1.309 +} 1.310 + 1.311 +bool 1.312 +CheckJNIArgument(JNIEnv* env, jobject arg) { 1.313 + if (!arg) { 1.314 + AndroidBridge::ThrowException(env, 1.315 + "java/lang/IllegalArgumentException", "Null argument"); 1.316 + return false; 1.317 + } 1.318 + return true; 1.319 +} 1.320 + 1.321 +bool 1.322 +AppendJSON(const jschar* buf, uint32_t len, void* data) 1.323 +{ 1.324 + static_cast<nsAutoString*>(data)->Append(buf, len); 1.325 + return true; 1.326 +} 1.327 + 1.328 +template <typename U, typename V, 1.329 + bool (JS::Value::*IsMethod)() const, 1.330 + V (JS::Value::*ToMethod)() const> 1.331 +struct PrimitiveProperty 1.332 +{ 1.333 + typedef U Type; // JNI type 1.334 + typedef V NativeType; // JSAPI type 1.335 + 1.336 + static bool InValue(JS::HandleValue val) { 1.337 + return (static_cast<const JS::Value&>(val).*IsMethod)(); 1.338 + } 1.339 + 1.340 + static Type FromValue(JNIEnv* env, jobject instance, 1.341 + JSContext* cx, JS::HandleValue val) { 1.342 + return static_cast<Type>( 1.343 + (static_cast<const JS::Value&>(val).*ToMethod)()); 1.344 + } 1.345 +}; 1.346 + 1.347 +// Statically cast from bool to jboolean (unsigned char); it works 1.348 +// since false and JNI_FALSE have the same value (0), and true and 1.349 +// JNI_TRUE have the same value (1). 1.350 +typedef PrimitiveProperty<jboolean, bool, 1.351 + &JS::Value::isBoolean, &JS::Value::toBoolean> BooleanProperty; 1.352 + 1.353 +typedef PrimitiveProperty<jdouble, double, 1.354 + &JS::Value::isNumber, &JS::Value::toNumber> DoubleProperty; 1.355 + 1.356 +typedef PrimitiveProperty<jint, int32_t, 1.357 + &JS::Value::isInt32, &JS::Value::toInt32> IntProperty; 1.358 + 1.359 +struct StringProperty 1.360 +{ 1.361 + typedef jstring Type; 1.362 + 1.363 + static bool InValue(JS::HandleValue val) { 1.364 + return val.isString(); 1.365 + } 1.366 + 1.367 + static Type FromValue(JNIEnv* env, jobject instance, 1.368 + JSContext* cx, JS::HandleValue val) { 1.369 + JS::RootedString str(cx, val.toString()); 1.370 + size_t strLen = 0; 1.371 + const jschar* const strChars = 1.372 + JS_GetStringCharsAndLength(cx, str, &strLen); 1.373 + if (!CheckJSCall(env, !!strChars)) { 1.374 + return nullptr; 1.375 + } 1.376 + jstring ret = env->NewString( 1.377 + reinterpret_cast<const jchar*>(strChars), strLen); 1.378 + AndroidBridge::HandleUncaughtException(env); 1.379 + MOZ_ASSERT(ret); 1.380 + return ret; 1.381 + } 1.382 +}; 1.383 + 1.384 +struct ObjectProperty 1.385 +{ 1.386 + typedef jobject Type; 1.387 + 1.388 + static bool InValue(JS::HandleValue val) { 1.389 + return val.isObjectOrNull(); 1.390 + } 1.391 + 1.392 + static Type FromValue(JNIEnv* env, jobject instance, 1.393 + JSContext* cx, JS::HandleValue val) { 1.394 + if (val.isNull()) { 1.395 + return nullptr; 1.396 + } 1.397 + JS::RootedObject object(cx, &val.toObject()); 1.398 + return NativeJSContainer::CreateObjectInstance(env, instance, object); 1.399 + } 1.400 +}; 1.401 + 1.402 +struct HasProperty 1.403 +{ 1.404 + typedef jboolean Type; 1.405 + 1.406 + static bool InValue(JS::HandleValue val) { 1.407 + return true; 1.408 + } 1.409 + 1.410 + static Type FromValue(JNIEnv* env, jobject instance, 1.411 + JSContext* cx, JS::HandleValue val) { 1.412 + return JNI_TRUE; 1.413 + } 1.414 +}; 1.415 + 1.416 +MOZ_BEGIN_ENUM_CLASS(FallbackOption) 1.417 + THROW, 1.418 + RETURN, 1.419 +MOZ_END_ENUM_CLASS(FallbackOption) 1.420 + 1.421 +template <class Property> 1.422 +typename Property::Type 1.423 +GetProperty(JNIEnv* env, jobject instance, jstring name, 1.424 + FallbackOption option = FallbackOption::THROW, 1.425 + typename Property::Type fallback = typename Property::Type()) { 1.426 + MOZ_ASSERT(env); 1.427 + MOZ_ASSERT(instance); 1.428 + 1.429 + JSContext* const cx = 1.430 + NativeJSContainer::GetContextFromObject(env, instance); 1.431 + const JS::RootedObject object(cx, 1.432 + NativeJSContainer::GetObjectFromObject(env, instance)); 1.433 + const JSJNIString strName(env, name); 1.434 + JS::RootedValue val(cx); 1.435 + 1.436 + if (!object || 1.437 + !CheckJNIArgument(env, name) || 1.438 + !CheckJSCall(env, 1.439 + JS_GetUCProperty(cx, object, strName, strName.Length(), &val))) { 1.440 + return typename Property::Type(); 1.441 + } 1.442 + if (val.isUndefined()) { 1.443 + if (option == FallbackOption::THROW) { 1.444 + AndroidBridge::ThrowException(env, 1.445 + "java/lang/IllegalArgumentException", 1.446 + "Property does not exist"); 1.447 + } 1.448 + return fallback; 1.449 + } 1.450 + if (!Property::InValue(val)) { 1.451 + AndroidBridge::ThrowException(env, 1.452 + "java/lang/IllegalArgumentException", 1.453 + "Property type mismatch"); 1.454 + return fallback; 1.455 + } 1.456 + return Property::FromValue(env, instance, cx, val); 1.457 +} 1.458 + 1.459 +} // namespace 1.460 + 1.461 +extern "C" { 1.462 + 1.463 +NS_EXPORT void JNICALL 1.464 +Java_org_mozilla_gecko_util_NativeJSContainer_dispose(JNIEnv* env, jobject instance) 1.465 +{ 1.466 + MOZ_ASSERT(env); 1.467 + NativeJSContainer::DisposeInstance(env, instance); 1.468 +} 1.469 + 1.470 +NS_EXPORT jobject JNICALL 1.471 +Java_org_mozilla_gecko_util_NativeJSContainer_clone(JNIEnv* env, jobject instance) 1.472 +{ 1.473 + MOZ_ASSERT(env); 1.474 + return NativeJSContainer::CloneInstance(env, instance); 1.475 +} 1.476 + 1.477 +NS_EXPORT jboolean JNICALL 1.478 +Java_org_mozilla_gecko_util_NativeJSObject_getBoolean(JNIEnv* env, jobject instance, jstring name) 1.479 +{ 1.480 + return GetProperty<BooleanProperty>(env, instance, name); 1.481 +} 1.482 + 1.483 +NS_EXPORT jboolean JNICALL 1.484 +Java_org_mozilla_gecko_util_NativeJSObject_optBoolean(JNIEnv* env, jobject instance, 1.485 + jstring name, jboolean fallback) 1.486 +{ 1.487 + return GetProperty<BooleanProperty>(env, instance, name, FallbackOption::RETURN, fallback); 1.488 +} 1.489 + 1.490 +NS_EXPORT jdouble JNICALL 1.491 +Java_org_mozilla_gecko_util_NativeJSObject_getDouble(JNIEnv* env, jobject instance, jstring name) 1.492 +{ 1.493 + return GetProperty<DoubleProperty>(env, instance, name); 1.494 +} 1.495 + 1.496 +NS_EXPORT jdouble JNICALL 1.497 +Java_org_mozilla_gecko_util_NativeJSObject_optDouble(JNIEnv* env, jobject instance, 1.498 + jstring name, jdouble fallback) 1.499 +{ 1.500 + return GetProperty<DoubleProperty>(env, instance, name, FallbackOption::RETURN, fallback); 1.501 +} 1.502 + 1.503 +NS_EXPORT jint JNICALL 1.504 +Java_org_mozilla_gecko_util_NativeJSObject_getInt(JNIEnv* env, jobject instance, jstring name) 1.505 +{ 1.506 + return GetProperty<IntProperty>(env, instance, name); 1.507 +} 1.508 + 1.509 +NS_EXPORT jint JNICALL 1.510 +Java_org_mozilla_gecko_util_NativeJSObject_optInt(JNIEnv* env, jobject instance, 1.511 + jstring name, jint fallback) 1.512 +{ 1.513 + return GetProperty<IntProperty>(env, instance, name, FallbackOption::RETURN, fallback); 1.514 +} 1.515 + 1.516 +NS_EXPORT jobject JNICALL 1.517 +Java_org_mozilla_gecko_util_NativeJSObject_getObject(JNIEnv* env, jobject instance, jstring name) 1.518 +{ 1.519 + return GetProperty<ObjectProperty>(env, instance, name); 1.520 +} 1.521 + 1.522 +NS_EXPORT jobject JNICALL 1.523 +Java_org_mozilla_gecko_util_NativeJSObject_optObject(JNIEnv* env, jobject instance, 1.524 + jstring name, jobject fallback) 1.525 +{ 1.526 + return GetProperty<ObjectProperty>(env, instance, name, FallbackOption::RETURN, fallback); 1.527 +} 1.528 + 1.529 +NS_EXPORT jstring JNICALL 1.530 +Java_org_mozilla_gecko_util_NativeJSObject_getString(JNIEnv* env, jobject instance, jstring name) 1.531 +{ 1.532 + return GetProperty<StringProperty>(env, instance, name); 1.533 +} 1.534 + 1.535 +NS_EXPORT jstring JNICALL 1.536 +Java_org_mozilla_gecko_util_NativeJSObject_optString(JNIEnv* env, jobject instance, 1.537 + jstring name, jstring fallback) 1.538 +{ 1.539 + return GetProperty<StringProperty>(env, instance, name, FallbackOption::RETURN, fallback); 1.540 +} 1.541 + 1.542 +NS_EXPORT jboolean JNICALL 1.543 +Java_org_mozilla_gecko_util_NativeJSObject_has(JNIEnv* env, jobject instance, jstring name) 1.544 +{ 1.545 + return GetProperty<HasProperty>(env, instance, name, FallbackOption::RETURN, JNI_FALSE); 1.546 +} 1.547 + 1.548 +NS_EXPORT jstring JNICALL 1.549 +Java_org_mozilla_gecko_util_NativeJSObject_toString(JNIEnv* env, jobject instance) 1.550 +{ 1.551 + MOZ_ASSERT(env); 1.552 + MOZ_ASSERT(instance); 1.553 + 1.554 + JSContext* const cx = NativeJSContainer::GetContextFromObject(env, instance); 1.555 + const JS::RootedObject object(cx, NativeJSContainer::GetObjectFromObject(env, instance)); 1.556 + JS::RootedValue value(cx, JS::ObjectValue(*object)); 1.557 + nsAutoString json; 1.558 + 1.559 + if (!object || 1.560 + !CheckJSCall(env, 1.561 + JS_Stringify(cx, &value, JS::NullPtr(), JS::NullHandleValue, AppendJSON, &json))) { 1.562 + return nullptr; 1.563 + } 1.564 + jstring ret = env->NewString(reinterpret_cast<const jchar*>(json.get()), json.Length()); 1.565 + AndroidBridge::HandleUncaughtException(env); 1.566 + MOZ_ASSERT(ret); 1.567 + return ret; 1.568 +} 1.569 + 1.570 +} // extern "C"