widget/android/NativeJSContainer.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "NativeJSContainer.h"
michael@0 7 #include "AndroidBridge.h"
michael@0 8 #include "mozilla/Vector.h"
michael@0 9 #include "prthread.h"
michael@0 10
michael@0 11 using namespace mozilla;
michael@0 12 using namespace mozilla::widget;
michael@0 13
michael@0 14 namespace mozilla {
michael@0 15 namespace widget {
michael@0 16
michael@0 17 class NativeJSContainer
michael@0 18 {
michael@0 19 public:
michael@0 20 static void InitJNI(JNIEnv* env) {
michael@0 21 if (jNativeJSContainer) {
michael@0 22 return;
michael@0 23 }
michael@0 24 jNativeJSContainer = AndroidBridge::GetClassGlobalRef(
michael@0 25 env, "org/mozilla/gecko/util/NativeJSContainer");
michael@0 26 MOZ_ASSERT(jNativeJSContainer);
michael@0 27 jContainerNativeObject = AndroidBridge::GetFieldID(
michael@0 28 env, jNativeJSContainer, "mNativeObject", "J");
michael@0 29 MOZ_ASSERT(jContainerNativeObject);
michael@0 30 jContainerConstructor = AndroidBridge::GetMethodID(
michael@0 31 env, jNativeJSContainer, "<init>", "(J)V");
michael@0 32 MOZ_ASSERT(jContainerConstructor);
michael@0 33
michael@0 34 jNativeJSObject = AndroidBridge::GetClassGlobalRef(
michael@0 35 env, "org/mozilla/gecko/util/NativeJSObject");
michael@0 36 MOZ_ASSERT(jNativeJSObject);
michael@0 37 jObjectContainer = AndroidBridge::GetFieldID(
michael@0 38 env, jNativeJSObject, "mContainer",
michael@0 39 "Lorg/mozilla/gecko/util/NativeJSContainer;");
michael@0 40 MOZ_ASSERT(jObjectContainer);
michael@0 41 jObjectIndex = AndroidBridge::GetFieldID(
michael@0 42 env, jNativeJSObject, "mObjectIndex", "I");
michael@0 43 MOZ_ASSERT(jObjectIndex);
michael@0 44 jObjectConstructor = AndroidBridge::GetMethodID(
michael@0 45 env, jNativeJSObject, "<init>",
michael@0 46 "(Lorg/mozilla/gecko/util/NativeJSContainer;I)V");
michael@0 47 MOZ_ASSERT(jContainerConstructor);
michael@0 48 }
michael@0 49
michael@0 50 static jobject CreateInstance(JNIEnv* env, JSContext* cx,
michael@0 51 JS::HandleObject object) {
michael@0 52 return CreateInstance(env, new NativeJSContainer(cx, object));
michael@0 53 }
michael@0 54
michael@0 55 static NativeJSContainer* FromInstance(JNIEnv* env, jobject instance) {
michael@0 56 MOZ_ASSERT(instance);
michael@0 57
michael@0 58 const jlong fieldValue =
michael@0 59 env->GetLongField(instance, jContainerNativeObject);
michael@0 60 NativeJSContainer* const nativeObject =
michael@0 61 reinterpret_cast<NativeJSContainer* const>(
michael@0 62 static_cast<uintptr_t>(fieldValue));
michael@0 63 if (!nativeObject) {
michael@0 64 AndroidBridge::ThrowException(env,
michael@0 65 "java/lang/NullPointerException",
michael@0 66 "Uninitialized NativeJSContainer");
michael@0 67 }
michael@0 68 return nativeObject;
michael@0 69 }
michael@0 70
michael@0 71 static void DisposeInstance(JNIEnv* env, jobject instance) {
michael@0 72 NativeJSContainer* const container = FromInstance(env, instance);
michael@0 73 if (container) {
michael@0 74 env->SetLongField(instance, jContainerNativeObject,
michael@0 75 static_cast<jlong>(reinterpret_cast<uintptr_t>(nullptr)));
michael@0 76 delete container;
michael@0 77 }
michael@0 78 }
michael@0 79
michael@0 80 static jobject CloneInstance(JNIEnv* env, jobject instance) {
michael@0 81 NativeJSContainer* const container = FromInstance(env, instance);
michael@0 82 if (!container || !container->EnsureObject(env)) {
michael@0 83 return nullptr;
michael@0 84 }
michael@0 85 JSContext* const cx = container->mThreadContext;
michael@0 86 JS::RootedObject object(cx, container->mJSObject);
michael@0 87 MOZ_ASSERT(object);
michael@0 88
michael@0 89 JSAutoStructuredCloneBuffer buffer;
michael@0 90 if (!buffer.write(cx, JS::RootedValue(cx, JS::ObjectValue(*object)))) {
michael@0 91 AndroidBridge::ThrowException(env,
michael@0 92 "java/lang/UnsupportedOperationException",
michael@0 93 "Cannot serialize object");
michael@0 94 return nullptr;
michael@0 95 }
michael@0 96 return CreateInstance(env, new NativeJSContainer(cx, Move(buffer)));
michael@0 97 }
michael@0 98
michael@0 99 static jobject GetInstanceFromObject(JNIEnv* env, jobject object) {
michael@0 100 MOZ_ASSERT(object);
michael@0 101
michael@0 102 const jobject instance = env->GetObjectField(object, jObjectContainer);
michael@0 103 MOZ_ASSERT(instance);
michael@0 104 return instance;
michael@0 105 }
michael@0 106
michael@0 107 static JSContext* GetContextFromObject(JNIEnv* env, jobject object) {
michael@0 108 MOZ_ASSERT(object);
michael@0 109 AutoLocalJNIFrame frame(env, 1);
michael@0 110
michael@0 111 NativeJSContainer* const container =
michael@0 112 FromInstance(env, GetInstanceFromObject(env, object));
michael@0 113 if (!container) {
michael@0 114 return nullptr;
michael@0 115 }
michael@0 116 return container->mThreadContext;
michael@0 117 }
michael@0 118
michael@0 119 static JSObject* GetObjectFromObject(JNIEnv* env, jobject object) {
michael@0 120 MOZ_ASSERT(object);
michael@0 121 AutoLocalJNIFrame frame(env, 1);
michael@0 122
michael@0 123 NativeJSContainer* const container =
michael@0 124 FromInstance(env, GetInstanceFromObject(env, object));
michael@0 125 if (!container ||
michael@0 126 !container->EnsureObject(env)) { // Do thread check
michael@0 127 return nullptr;
michael@0 128 }
michael@0 129 const jint index = env->GetIntField(object, jObjectIndex);
michael@0 130 if (index < 0) {
michael@0 131 // -1 for index field means it's the root object of the container
michael@0 132 return container->mJSObject;
michael@0 133 }
michael@0 134 return container->mRootedObjects[index];
michael@0 135 }
michael@0 136
michael@0 137 static jobject CreateObjectInstance(JNIEnv* env, jobject object,
michael@0 138 JS::HandleObject jsObject) {
michael@0 139 MOZ_ASSERT(object);
michael@0 140 MOZ_ASSERT(jsObject);
michael@0 141 AutoLocalJNIFrame frame(env, 2);
michael@0 142
michael@0 143 jobject instance = GetInstanceFromObject(env, object);
michael@0 144 NativeJSContainer* const container = FromInstance(env, instance);
michael@0 145 if (!container) {
michael@0 146 return nullptr;
michael@0 147 }
michael@0 148 size_t newIndex = container->mRootedObjects.length();
michael@0 149 if (!container->mRootedObjects.append(jsObject)) {
michael@0 150 AndroidBridge::ThrowException(env,
michael@0 151 "java/lang/OutOfMemoryError", "Cannot allocate object");
michael@0 152 return nullptr;
michael@0 153 }
michael@0 154 const jobject newObject =
michael@0 155 env->NewObject(jNativeJSObject, jObjectConstructor,
michael@0 156 instance, newIndex);
michael@0 157 AndroidBridge::HandleUncaughtException(env);
michael@0 158 MOZ_ASSERT(newObject);
michael@0 159 return frame.Pop(newObject);
michael@0 160 }
michael@0 161
michael@0 162 // Make sure we have a JSObject and deserialize if necessary/possible
michael@0 163 bool EnsureObject(JNIEnv* env) {
michael@0 164 if (mJSObject) {
michael@0 165 if (PR_GetCurrentThread() != mThread) {
michael@0 166 AndroidBridge::ThrowException(env,
michael@0 167 "java/lang/IllegalThreadStateException",
michael@0 168 "Using NativeJSObject off its thread");
michael@0 169 return false;
michael@0 170 }
michael@0 171 return true;
michael@0 172 }
michael@0 173 if (!SwitchContextToCurrentThread()) {
michael@0 174 AndroidBridge::ThrowException(env,
michael@0 175 "java/lang/IllegalThreadStateException",
michael@0 176 "Not available for this thread");
michael@0 177 return false;
michael@0 178 }
michael@0 179
michael@0 180 JS::RootedValue value(mThreadContext);
michael@0 181 MOZ_ASSERT(mBuffer.data());
michael@0 182 MOZ_ALWAYS_TRUE(mBuffer.read(mThreadContext, &value));
michael@0 183 if (value.isObject()) {
michael@0 184 mJSObject = &value.toObject();
michael@0 185 }
michael@0 186 if (!mJSObject) {
michael@0 187 AndroidBridge::ThrowException(env,
michael@0 188 "java/lang/IllegalStateException", "Cannot deserialize data");
michael@0 189 return false;
michael@0 190 }
michael@0 191 mBuffer.clear();
michael@0 192 return true;
michael@0 193 }
michael@0 194
michael@0 195 private:
michael@0 196 static jclass jNativeJSContainer;
michael@0 197 static jfieldID jContainerNativeObject;
michael@0 198 static jmethodID jContainerConstructor;
michael@0 199 static jclass jNativeJSObject;
michael@0 200 static jfieldID jObjectContainer;
michael@0 201 static jfieldID jObjectIndex;
michael@0 202 static jmethodID jObjectConstructor;
michael@0 203
michael@0 204 static jobject CreateInstance(JNIEnv* env,
michael@0 205 NativeJSContainer* nativeObject) {
michael@0 206 InitJNI(env);
michael@0 207 const jobject newObject =
michael@0 208 env->NewObject(jNativeJSContainer, jContainerConstructor,
michael@0 209 static_cast<jlong>(
michael@0 210 reinterpret_cast<uintptr_t>(nativeObject)));
michael@0 211 AndroidBridge::HandleUncaughtException(env);
michael@0 212 MOZ_ASSERT(newObject);
michael@0 213 return newObject;
michael@0 214 }
michael@0 215
michael@0 216 // Thread that the object is valid on
michael@0 217 PRThread* mThread;
michael@0 218 // Context that the object is valid in
michael@0 219 JSContext* mThreadContext;
michael@0 220 // Deserialized object, or nullptr if object is in serialized form
michael@0 221 JS::Heap<JSObject*> mJSObject;
michael@0 222 // Serialized object, or empty if object is in deserialized form
michael@0 223 JSAutoStructuredCloneBuffer mBuffer;
michael@0 224 // Objects derived from mJSObject
michael@0 225 Vector<JS::Heap<JSObject*>, 4> mRootedObjects;
michael@0 226
michael@0 227 // Create a new container containing the given deserialized object
michael@0 228 NativeJSContainer(JSContext* cx, JS::HandleObject object)
michael@0 229 : mThread(PR_GetCurrentThread())
michael@0 230 , mThreadContext(cx)
michael@0 231 , mJSObject(object)
michael@0 232 {
michael@0 233 }
michael@0 234
michael@0 235 // Create a new container containing the given serialized object
michael@0 236 NativeJSContainer(JSContext* cx, JSAutoStructuredCloneBuffer&& buffer)
michael@0 237 : mThread(PR_GetCurrentThread())
michael@0 238 , mThreadContext(cx)
michael@0 239 , mBuffer(Forward<JSAutoStructuredCloneBuffer>(buffer))
michael@0 240 {
michael@0 241 }
michael@0 242
michael@0 243 bool SwitchContextToCurrentThread() {
michael@0 244 PRThread* const currentThread = PR_GetCurrentThread();
michael@0 245 if (currentThread == mThread) {
michael@0 246 return true;
michael@0 247 }
michael@0 248 return false;
michael@0 249 }
michael@0 250 };
michael@0 251
michael@0 252 jclass NativeJSContainer::jNativeJSContainer = 0;
michael@0 253 jfieldID NativeJSContainer::jContainerNativeObject = 0;
michael@0 254 jmethodID NativeJSContainer::jContainerConstructor = 0;
michael@0 255 jclass NativeJSContainer::jNativeJSObject = 0;
michael@0 256 jfieldID NativeJSContainer::jObjectContainer = 0;
michael@0 257 jfieldID NativeJSContainer::jObjectIndex = 0;
michael@0 258 jmethodID NativeJSContainer::jObjectConstructor = 0;
michael@0 259
michael@0 260 jobject
michael@0 261 CreateNativeJSContainer(JNIEnv* env, JSContext* cx, JS::HandleObject object)
michael@0 262 {
michael@0 263 return NativeJSContainer::CreateInstance(env, cx, object);
michael@0 264 }
michael@0 265
michael@0 266 } // namespace widget
michael@0 267 } // namespace mozilla
michael@0 268
michael@0 269 namespace {
michael@0 270
michael@0 271 class JSJNIString
michael@0 272 {
michael@0 273 public:
michael@0 274 JSJNIString(JNIEnv* env, jstring str)
michael@0 275 : mEnv(env)
michael@0 276 , mJNIString(str)
michael@0 277 , mJSString(!str ? nullptr :
michael@0 278 reinterpret_cast<const jschar*>(env->GetStringChars(str, nullptr)))
michael@0 279 {
michael@0 280 }
michael@0 281 ~JSJNIString() {
michael@0 282 if (mJNIString) {
michael@0 283 mEnv->ReleaseStringChars(mJNIString,
michael@0 284 reinterpret_cast<const jchar*>(mJSString));
michael@0 285 }
michael@0 286 }
michael@0 287 operator const jschar*() const {
michael@0 288 return mJSString;
michael@0 289 }
michael@0 290 size_t Length() const {
michael@0 291 return static_cast<size_t>(mEnv->GetStringLength(mJNIString));
michael@0 292 }
michael@0 293 private:
michael@0 294 JNIEnv* const mEnv;
michael@0 295 const jstring mJNIString;
michael@0 296 const jschar* const mJSString;
michael@0 297 };
michael@0 298
michael@0 299 bool
michael@0 300 CheckJSCall(JNIEnv* env, bool result) {
michael@0 301 if (!result) {
michael@0 302 AndroidBridge::ThrowException(env,
michael@0 303 "java/lang/UnsupportedOperationException", "JSAPI call failed");
michael@0 304 }
michael@0 305 return result;
michael@0 306 }
michael@0 307
michael@0 308 bool
michael@0 309 CheckJNIArgument(JNIEnv* env, jobject arg) {
michael@0 310 if (!arg) {
michael@0 311 AndroidBridge::ThrowException(env,
michael@0 312 "java/lang/IllegalArgumentException", "Null argument");
michael@0 313 return false;
michael@0 314 }
michael@0 315 return true;
michael@0 316 }
michael@0 317
michael@0 318 bool
michael@0 319 AppendJSON(const jschar* buf, uint32_t len, void* data)
michael@0 320 {
michael@0 321 static_cast<nsAutoString*>(data)->Append(buf, len);
michael@0 322 return true;
michael@0 323 }
michael@0 324
michael@0 325 template <typename U, typename V,
michael@0 326 bool (JS::Value::*IsMethod)() const,
michael@0 327 V (JS::Value::*ToMethod)() const>
michael@0 328 struct PrimitiveProperty
michael@0 329 {
michael@0 330 typedef U Type; // JNI type
michael@0 331 typedef V NativeType; // JSAPI type
michael@0 332
michael@0 333 static bool InValue(JS::HandleValue val) {
michael@0 334 return (static_cast<const JS::Value&>(val).*IsMethod)();
michael@0 335 }
michael@0 336
michael@0 337 static Type FromValue(JNIEnv* env, jobject instance,
michael@0 338 JSContext* cx, JS::HandleValue val) {
michael@0 339 return static_cast<Type>(
michael@0 340 (static_cast<const JS::Value&>(val).*ToMethod)());
michael@0 341 }
michael@0 342 };
michael@0 343
michael@0 344 // Statically cast from bool to jboolean (unsigned char); it works
michael@0 345 // since false and JNI_FALSE have the same value (0), and true and
michael@0 346 // JNI_TRUE have the same value (1).
michael@0 347 typedef PrimitiveProperty<jboolean, bool,
michael@0 348 &JS::Value::isBoolean, &JS::Value::toBoolean> BooleanProperty;
michael@0 349
michael@0 350 typedef PrimitiveProperty<jdouble, double,
michael@0 351 &JS::Value::isNumber, &JS::Value::toNumber> DoubleProperty;
michael@0 352
michael@0 353 typedef PrimitiveProperty<jint, int32_t,
michael@0 354 &JS::Value::isInt32, &JS::Value::toInt32> IntProperty;
michael@0 355
michael@0 356 struct StringProperty
michael@0 357 {
michael@0 358 typedef jstring Type;
michael@0 359
michael@0 360 static bool InValue(JS::HandleValue val) {
michael@0 361 return val.isString();
michael@0 362 }
michael@0 363
michael@0 364 static Type FromValue(JNIEnv* env, jobject instance,
michael@0 365 JSContext* cx, JS::HandleValue val) {
michael@0 366 JS::RootedString str(cx, val.toString());
michael@0 367 size_t strLen = 0;
michael@0 368 const jschar* const strChars =
michael@0 369 JS_GetStringCharsAndLength(cx, str, &strLen);
michael@0 370 if (!CheckJSCall(env, !!strChars)) {
michael@0 371 return nullptr;
michael@0 372 }
michael@0 373 jstring ret = env->NewString(
michael@0 374 reinterpret_cast<const jchar*>(strChars), strLen);
michael@0 375 AndroidBridge::HandleUncaughtException(env);
michael@0 376 MOZ_ASSERT(ret);
michael@0 377 return ret;
michael@0 378 }
michael@0 379 };
michael@0 380
michael@0 381 struct ObjectProperty
michael@0 382 {
michael@0 383 typedef jobject Type;
michael@0 384
michael@0 385 static bool InValue(JS::HandleValue val) {
michael@0 386 return val.isObjectOrNull();
michael@0 387 }
michael@0 388
michael@0 389 static Type FromValue(JNIEnv* env, jobject instance,
michael@0 390 JSContext* cx, JS::HandleValue val) {
michael@0 391 if (val.isNull()) {
michael@0 392 return nullptr;
michael@0 393 }
michael@0 394 JS::RootedObject object(cx, &val.toObject());
michael@0 395 return NativeJSContainer::CreateObjectInstance(env, instance, object);
michael@0 396 }
michael@0 397 };
michael@0 398
michael@0 399 struct HasProperty
michael@0 400 {
michael@0 401 typedef jboolean Type;
michael@0 402
michael@0 403 static bool InValue(JS::HandleValue val) {
michael@0 404 return true;
michael@0 405 }
michael@0 406
michael@0 407 static Type FromValue(JNIEnv* env, jobject instance,
michael@0 408 JSContext* cx, JS::HandleValue val) {
michael@0 409 return JNI_TRUE;
michael@0 410 }
michael@0 411 };
michael@0 412
michael@0 413 MOZ_BEGIN_ENUM_CLASS(FallbackOption)
michael@0 414 THROW,
michael@0 415 RETURN,
michael@0 416 MOZ_END_ENUM_CLASS(FallbackOption)
michael@0 417
michael@0 418 template <class Property>
michael@0 419 typename Property::Type
michael@0 420 GetProperty(JNIEnv* env, jobject instance, jstring name,
michael@0 421 FallbackOption option = FallbackOption::THROW,
michael@0 422 typename Property::Type fallback = typename Property::Type()) {
michael@0 423 MOZ_ASSERT(env);
michael@0 424 MOZ_ASSERT(instance);
michael@0 425
michael@0 426 JSContext* const cx =
michael@0 427 NativeJSContainer::GetContextFromObject(env, instance);
michael@0 428 const JS::RootedObject object(cx,
michael@0 429 NativeJSContainer::GetObjectFromObject(env, instance));
michael@0 430 const JSJNIString strName(env, name);
michael@0 431 JS::RootedValue val(cx);
michael@0 432
michael@0 433 if (!object ||
michael@0 434 !CheckJNIArgument(env, name) ||
michael@0 435 !CheckJSCall(env,
michael@0 436 JS_GetUCProperty(cx, object, strName, strName.Length(), &val))) {
michael@0 437 return typename Property::Type();
michael@0 438 }
michael@0 439 if (val.isUndefined()) {
michael@0 440 if (option == FallbackOption::THROW) {
michael@0 441 AndroidBridge::ThrowException(env,
michael@0 442 "java/lang/IllegalArgumentException",
michael@0 443 "Property does not exist");
michael@0 444 }
michael@0 445 return fallback;
michael@0 446 }
michael@0 447 if (!Property::InValue(val)) {
michael@0 448 AndroidBridge::ThrowException(env,
michael@0 449 "java/lang/IllegalArgumentException",
michael@0 450 "Property type mismatch");
michael@0 451 return fallback;
michael@0 452 }
michael@0 453 return Property::FromValue(env, instance, cx, val);
michael@0 454 }
michael@0 455
michael@0 456 } // namespace
michael@0 457
michael@0 458 extern "C" {
michael@0 459
michael@0 460 NS_EXPORT void JNICALL
michael@0 461 Java_org_mozilla_gecko_util_NativeJSContainer_dispose(JNIEnv* env, jobject instance)
michael@0 462 {
michael@0 463 MOZ_ASSERT(env);
michael@0 464 NativeJSContainer::DisposeInstance(env, instance);
michael@0 465 }
michael@0 466
michael@0 467 NS_EXPORT jobject JNICALL
michael@0 468 Java_org_mozilla_gecko_util_NativeJSContainer_clone(JNIEnv* env, jobject instance)
michael@0 469 {
michael@0 470 MOZ_ASSERT(env);
michael@0 471 return NativeJSContainer::CloneInstance(env, instance);
michael@0 472 }
michael@0 473
michael@0 474 NS_EXPORT jboolean JNICALL
michael@0 475 Java_org_mozilla_gecko_util_NativeJSObject_getBoolean(JNIEnv* env, jobject instance, jstring name)
michael@0 476 {
michael@0 477 return GetProperty<BooleanProperty>(env, instance, name);
michael@0 478 }
michael@0 479
michael@0 480 NS_EXPORT jboolean JNICALL
michael@0 481 Java_org_mozilla_gecko_util_NativeJSObject_optBoolean(JNIEnv* env, jobject instance,
michael@0 482 jstring name, jboolean fallback)
michael@0 483 {
michael@0 484 return GetProperty<BooleanProperty>(env, instance, name, FallbackOption::RETURN, fallback);
michael@0 485 }
michael@0 486
michael@0 487 NS_EXPORT jdouble JNICALL
michael@0 488 Java_org_mozilla_gecko_util_NativeJSObject_getDouble(JNIEnv* env, jobject instance, jstring name)
michael@0 489 {
michael@0 490 return GetProperty<DoubleProperty>(env, instance, name);
michael@0 491 }
michael@0 492
michael@0 493 NS_EXPORT jdouble JNICALL
michael@0 494 Java_org_mozilla_gecko_util_NativeJSObject_optDouble(JNIEnv* env, jobject instance,
michael@0 495 jstring name, jdouble fallback)
michael@0 496 {
michael@0 497 return GetProperty<DoubleProperty>(env, instance, name, FallbackOption::RETURN, fallback);
michael@0 498 }
michael@0 499
michael@0 500 NS_EXPORT jint JNICALL
michael@0 501 Java_org_mozilla_gecko_util_NativeJSObject_getInt(JNIEnv* env, jobject instance, jstring name)
michael@0 502 {
michael@0 503 return GetProperty<IntProperty>(env, instance, name);
michael@0 504 }
michael@0 505
michael@0 506 NS_EXPORT jint JNICALL
michael@0 507 Java_org_mozilla_gecko_util_NativeJSObject_optInt(JNIEnv* env, jobject instance,
michael@0 508 jstring name, jint fallback)
michael@0 509 {
michael@0 510 return GetProperty<IntProperty>(env, instance, name, FallbackOption::RETURN, fallback);
michael@0 511 }
michael@0 512
michael@0 513 NS_EXPORT jobject JNICALL
michael@0 514 Java_org_mozilla_gecko_util_NativeJSObject_getObject(JNIEnv* env, jobject instance, jstring name)
michael@0 515 {
michael@0 516 return GetProperty<ObjectProperty>(env, instance, name);
michael@0 517 }
michael@0 518
michael@0 519 NS_EXPORT jobject JNICALL
michael@0 520 Java_org_mozilla_gecko_util_NativeJSObject_optObject(JNIEnv* env, jobject instance,
michael@0 521 jstring name, jobject fallback)
michael@0 522 {
michael@0 523 return GetProperty<ObjectProperty>(env, instance, name, FallbackOption::RETURN, fallback);
michael@0 524 }
michael@0 525
michael@0 526 NS_EXPORT jstring JNICALL
michael@0 527 Java_org_mozilla_gecko_util_NativeJSObject_getString(JNIEnv* env, jobject instance, jstring name)
michael@0 528 {
michael@0 529 return GetProperty<StringProperty>(env, instance, name);
michael@0 530 }
michael@0 531
michael@0 532 NS_EXPORT jstring JNICALL
michael@0 533 Java_org_mozilla_gecko_util_NativeJSObject_optString(JNIEnv* env, jobject instance,
michael@0 534 jstring name, jstring fallback)
michael@0 535 {
michael@0 536 return GetProperty<StringProperty>(env, instance, name, FallbackOption::RETURN, fallback);
michael@0 537 }
michael@0 538
michael@0 539 NS_EXPORT jboolean JNICALL
michael@0 540 Java_org_mozilla_gecko_util_NativeJSObject_has(JNIEnv* env, jobject instance, jstring name)
michael@0 541 {
michael@0 542 return GetProperty<HasProperty>(env, instance, name, FallbackOption::RETURN, JNI_FALSE);
michael@0 543 }
michael@0 544
michael@0 545 NS_EXPORT jstring JNICALL
michael@0 546 Java_org_mozilla_gecko_util_NativeJSObject_toString(JNIEnv* env, jobject instance)
michael@0 547 {
michael@0 548 MOZ_ASSERT(env);
michael@0 549 MOZ_ASSERT(instance);
michael@0 550
michael@0 551 JSContext* const cx = NativeJSContainer::GetContextFromObject(env, instance);
michael@0 552 const JS::RootedObject object(cx, NativeJSContainer::GetObjectFromObject(env, instance));
michael@0 553 JS::RootedValue value(cx, JS::ObjectValue(*object));
michael@0 554 nsAutoString json;
michael@0 555
michael@0 556 if (!object ||
michael@0 557 !CheckJSCall(env,
michael@0 558 JS_Stringify(cx, &value, JS::NullPtr(), JS::NullHandleValue, AppendJSON, &json))) {
michael@0 559 return nullptr;
michael@0 560 }
michael@0 561 jstring ret = env->NewString(reinterpret_cast<const jchar*>(json.get()), json.Length());
michael@0 562 AndroidBridge::HandleUncaughtException(env);
michael@0 563 MOZ_ASSERT(ret);
michael@0 564 return ret;
michael@0 565 }
michael@0 566
michael@0 567 } // extern "C"

mercurial