1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/plugins/base/nsJSNPRuntime.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2129 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 "base/basictypes.h" 1.10 + 1.11 +#include "jsfriendapi.h" 1.12 +#include "jswrapper.h" 1.13 + 1.14 +#include "nsIInterfaceRequestorUtils.h" 1.15 +#include "nsJSNPRuntime.h" 1.16 +#include "nsNPAPIPlugin.h" 1.17 +#include "nsNPAPIPluginInstance.h" 1.18 +#include "nsIScriptGlobalObject.h" 1.19 +#include "nsIScriptContext.h" 1.20 +#include "nsDOMJSUtils.h" 1.21 +#include "nsCxPusher.h" 1.22 +#include "nsIDocument.h" 1.23 +#include "nsIJSRuntimeService.h" 1.24 +#include "nsIXPConnect.h" 1.25 +#include "nsIDOMElement.h" 1.26 +#include "prmem.h" 1.27 +#include "nsIContent.h" 1.28 +#include "nsPluginInstanceOwner.h" 1.29 +#include "nsWrapperCacheInlines.h" 1.30 +#include "js/HashTable.h" 1.31 +#include "mozilla/HashFunctions.h" 1.32 + 1.33 + 1.34 +#define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class" 1.35 + 1.36 +using namespace mozilla::plugins::parent; 1.37 +using namespace mozilla; 1.38 + 1.39 +#include "mozilla/plugins/PluginScriptableObjectParent.h" 1.40 +using mozilla::plugins::PluginScriptableObjectParent; 1.41 +using mozilla::plugins::ParentNPObject; 1.42 + 1.43 +struct JSObjWrapperHasher : public js::DefaultHasher<nsJSObjWrapperKey> 1.44 +{ 1.45 + typedef nsJSObjWrapperKey Key; 1.46 + typedef Key Lookup; 1.47 + 1.48 + static uint32_t hash(const Lookup &l) { 1.49 + return HashGeneric(l.mJSObj, l.mNpp); 1.50 + } 1.51 + 1.52 + static void rekey(Key &k, const Key& newKey) { 1.53 + MOZ_ASSERT(k.mNpp == newKey.mNpp); 1.54 + k.mJSObj = newKey.mJSObj; 1.55 + } 1.56 +}; 1.57 + 1.58 +// Hash of JSObject wrappers that wraps JSObjects as NPObjects. There 1.59 +// will be one wrapper per JSObject per plugin instance, i.e. if two 1.60 +// plugins access the JSObject x, two wrappers for x will be 1.61 +// created. This is needed to be able to properly drop the wrappers 1.62 +// when a plugin is torn down in case there's a leak in the plugin (we 1.63 +// don't want to leak the world just because a plugin leaks an 1.64 +// NPObject). 1.65 +typedef js::HashMap<nsJSObjWrapperKey, 1.66 + nsJSObjWrapper*, 1.67 + JSObjWrapperHasher, 1.68 + js::SystemAllocPolicy> JSObjWrapperTable; 1.69 +static JSObjWrapperTable sJSObjWrappers; 1.70 + 1.71 +// Whether it's safe to iterate sJSObjWrappers. Set to true when sJSObjWrappers 1.72 +// has been initialized and is not currently being enumerated. 1.73 +static bool sJSObjWrappersAccessible = false; 1.74 + 1.75 +// Hash of NPObject wrappers that wrap NPObjects as JSObjects. 1.76 +static PLDHashTable sNPObjWrappers; 1.77 + 1.78 +// Global wrapper count. This includes JSObject wrappers *and* 1.79 +// NPObject wrappers. When this count goes to zero, there are no more 1.80 +// wrappers and we can kill off hash tables etc. 1.81 +static int32_t sWrapperCount; 1.82 + 1.83 +// The JSRuntime. Used to unroot JSObjects when no JSContext is 1.84 +// reachable. 1.85 +static JSRuntime *sJSRuntime; 1.86 + 1.87 +static nsTArray<NPObject*>* sDelayedReleases; 1.88 + 1.89 +namespace { 1.90 + 1.91 +inline bool 1.92 +NPObjectIsOutOfProcessProxy(NPObject *obj) 1.93 +{ 1.94 + return obj->_class == PluginScriptableObjectParent::GetClass(); 1.95 +} 1.96 + 1.97 +} // anonymous namespace 1.98 + 1.99 +// Helper class that reports any JS exceptions that were thrown while 1.100 +// the plugin executed JS. 1.101 + 1.102 +class AutoJSExceptionReporter 1.103 +{ 1.104 +public: 1.105 + AutoJSExceptionReporter(JSContext *cx) 1.106 + : mCx(cx) 1.107 + { 1.108 + } 1.109 + 1.110 + ~AutoJSExceptionReporter() 1.111 + { 1.112 + JS_ReportPendingException(mCx); 1.113 + } 1.114 + 1.115 +protected: 1.116 + JSContext *mCx; 1.117 +}; 1.118 + 1.119 + 1.120 +NPClass nsJSObjWrapper::sJSObjWrapperNPClass = 1.121 + { 1.122 + NP_CLASS_STRUCT_VERSION, 1.123 + nsJSObjWrapper::NP_Allocate, 1.124 + nsJSObjWrapper::NP_Deallocate, 1.125 + nsJSObjWrapper::NP_Invalidate, 1.126 + nsJSObjWrapper::NP_HasMethod, 1.127 + nsJSObjWrapper::NP_Invoke, 1.128 + nsJSObjWrapper::NP_InvokeDefault, 1.129 + nsJSObjWrapper::NP_HasProperty, 1.130 + nsJSObjWrapper::NP_GetProperty, 1.131 + nsJSObjWrapper::NP_SetProperty, 1.132 + nsJSObjWrapper::NP_RemoveProperty, 1.133 + nsJSObjWrapper::NP_Enumerate, 1.134 + nsJSObjWrapper::NP_Construct 1.135 + }; 1.136 + 1.137 +static bool 1.138 +NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp); 1.139 + 1.140 +static bool 1.141 +NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool *succeeded); 1.142 + 1.143 +static bool 1.144 +NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool strict, 1.145 + JS::MutableHandle<JS::Value> vp); 1.146 + 1.147 +static bool 1.148 +NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp); 1.149 + 1.150 +static bool 1.151 +NPObjWrapper_newEnumerate(JSContext *cx, JS::Handle<JSObject*> obj, JSIterateOp enum_op, 1.152 + JS::Value *statep, jsid *idp); 1.153 + 1.154 +static bool 1.155 +NPObjWrapper_NewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, 1.156 + JS::MutableHandle<JSObject*> objp); 1.157 + 1.158 +static bool 1.159 +NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp); 1.160 + 1.161 +static void 1.162 +NPObjWrapper_Finalize(JSFreeOp *fop, JSObject *obj); 1.163 + 1.164 +static bool 1.165 +NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp); 1.166 + 1.167 +static bool 1.168 +NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp); 1.169 + 1.170 +static bool 1.171 +CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj, 1.172 + JS::Handle<jsid> id, NPVariant* getPropertyResult, 1.173 + JS::MutableHandle<JS::Value> vp); 1.174 + 1.175 +const JSClass sNPObjectJSWrapperClass = 1.176 + { 1.177 + NPRUNTIME_JSCLASS_NAME, 1.178 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE, 1.179 + NPObjWrapper_AddProperty, 1.180 + NPObjWrapper_DelProperty, 1.181 + NPObjWrapper_GetProperty, 1.182 + NPObjWrapper_SetProperty, 1.183 + (JSEnumerateOp)NPObjWrapper_newEnumerate, 1.184 + (JSResolveOp)NPObjWrapper_NewResolve, 1.185 + NPObjWrapper_Convert, 1.186 + NPObjWrapper_Finalize, 1.187 + NPObjWrapper_Call, 1.188 + nullptr, /* hasInstance */ 1.189 + NPObjWrapper_Construct 1.190 + }; 1.191 + 1.192 +typedef struct NPObjectMemberPrivate { 1.193 + JS::Heap<JSObject *> npobjWrapper; 1.194 + JS::Heap<JS::Value> fieldValue; 1.195 + JS::Heap<jsid> methodName; 1.196 + NPP npp; 1.197 +} NPObjectMemberPrivate; 1.198 + 1.199 +static bool 1.200 +NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp); 1.201 + 1.202 +static void 1.203 +NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj); 1.204 + 1.205 +static bool 1.206 +NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp); 1.207 + 1.208 +static void 1.209 +NPObjectMember_Trace(JSTracer *trc, JSObject *obj); 1.210 + 1.211 +static const JSClass sNPObjectMemberClass = 1.212 + { 1.213 + "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, 1.214 + JS_PropertyStub, JS_DeletePropertyStub, 1.215 + JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, 1.216 + JS_ResolveStub, NPObjectMember_Convert, 1.217 + NPObjectMember_Finalize, NPObjectMember_Call, 1.218 + nullptr, nullptr, NPObjectMember_Trace 1.219 + }; 1.220 + 1.221 +static void 1.222 +OnWrapperDestroyed(); 1.223 + 1.224 +static void 1.225 +DelayedReleaseGCCallback(JSGCStatus status) 1.226 +{ 1.227 + if (JSGC_END == status) { 1.228 + // Take ownership of sDelayedReleases and null it out now. The 1.229 + // _releaseobject call below can reenter GC and double-free these objects. 1.230 + nsAutoPtr<nsTArray<NPObject*> > delayedReleases(sDelayedReleases); 1.231 + sDelayedReleases = nullptr; 1.232 + 1.233 + if (delayedReleases) { 1.234 + for (uint32_t i = 0; i < delayedReleases->Length(); ++i) { 1.235 + NPObject* obj = (*delayedReleases)[i]; 1.236 + if (obj) 1.237 + _releaseobject(obj); 1.238 + OnWrapperDestroyed(); 1.239 + } 1.240 + } 1.241 + } 1.242 +} 1.243 + 1.244 +static void 1.245 +OnWrapperCreated() 1.246 +{ 1.247 + if (sWrapperCount++ == 0) { 1.248 + static const char rtsvc_id[] = "@mozilla.org/js/xpc/RuntimeService;1"; 1.249 + nsCOMPtr<nsIJSRuntimeService> rtsvc(do_GetService(rtsvc_id)); 1.250 + if (!rtsvc) 1.251 + return; 1.252 + 1.253 + rtsvc->GetRuntime(&sJSRuntime); 1.254 + NS_ASSERTION(sJSRuntime != nullptr, "no JSRuntime?!"); 1.255 + 1.256 + // Register our GC callback to perform delayed destruction of finalized 1.257 + // NPObjects. Leave this callback around and don't ever unregister it. 1.258 + rtsvc->RegisterGCCallback(DelayedReleaseGCCallback); 1.259 + } 1.260 +} 1.261 + 1.262 +static void 1.263 +OnWrapperDestroyed() 1.264 +{ 1.265 + NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!"); 1.266 + 1.267 + if (--sWrapperCount == 0) { 1.268 + if (sJSObjWrappersAccessible) { 1.269 + MOZ_ASSERT(sJSObjWrappers.count() == 0); 1.270 + 1.271 + // No more wrappers, and our hash was initialized. Finish the 1.272 + // hash to prevent leaking it. 1.273 + sJSObjWrappers.finish(); 1.274 + sJSObjWrappersAccessible = false; 1.275 + } 1.276 + 1.277 + if (sNPObjWrappers.ops) { 1.278 + MOZ_ASSERT(sNPObjWrappers.entryCount == 0); 1.279 + 1.280 + // No more wrappers, and our hash was initialized. Finish the 1.281 + // hash to prevent leaking it. 1.282 + PL_DHashTableFinish(&sNPObjWrappers); 1.283 + 1.284 + sNPObjWrappers.ops = nullptr; 1.285 + } 1.286 + 1.287 + // No more need for this. 1.288 + sJSRuntime = nullptr; 1.289 + } 1.290 +} 1.291 + 1.292 +namespace mozilla { 1.293 +namespace plugins { 1.294 +namespace parent { 1.295 + 1.296 +JSContext * 1.297 +GetJSContext(NPP npp) 1.298 +{ 1.299 + NS_ENSURE_TRUE(npp, nullptr); 1.300 + 1.301 + nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata; 1.302 + NS_ENSURE_TRUE(inst, nullptr); 1.303 + 1.304 + nsRefPtr<nsPluginInstanceOwner> owner = inst->GetOwner(); 1.305 + NS_ENSURE_TRUE(owner, nullptr); 1.306 + 1.307 + nsCOMPtr<nsIDocument> doc; 1.308 + owner->GetDocument(getter_AddRefs(doc)); 1.309 + NS_ENSURE_TRUE(doc, nullptr); 1.310 + 1.311 + nsCOMPtr<nsISupports> documentContainer = doc->GetContainer(); 1.312 + nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(documentContainer)); 1.313 + NS_ENSURE_TRUE(sgo, nullptr); 1.314 + 1.315 + nsIScriptContext *scx = sgo->GetContext(); 1.316 + NS_ENSURE_TRUE(scx, nullptr); 1.317 + 1.318 + return scx->GetNativeContext(); 1.319 +} 1.320 + 1.321 +} 1.322 +} 1.323 +} 1.324 + 1.325 +static NPP 1.326 +LookupNPP(NPObject *npobj); 1.327 + 1.328 + 1.329 +static JS::Value 1.330 +NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant) 1.331 +{ 1.332 + switch (variant->type) { 1.333 + case NPVariantType_Void : 1.334 + return JSVAL_VOID; 1.335 + case NPVariantType_Null : 1.336 + return JSVAL_NULL; 1.337 + case NPVariantType_Bool : 1.338 + return BOOLEAN_TO_JSVAL(NPVARIANT_TO_BOOLEAN(*variant)); 1.339 + case NPVariantType_Int32 : 1.340 + { 1.341 + // Don't use INT_TO_JSVAL directly to prevent bugs when dealing 1.342 + // with ints larger than what fits in a integer JS::Value. 1.343 + return ::JS_NumberValue(NPVARIANT_TO_INT32(*variant)); 1.344 + } 1.345 + case NPVariantType_Double : 1.346 + { 1.347 + return ::JS_NumberValue(NPVARIANT_TO_DOUBLE(*variant)); 1.348 + } 1.349 + case NPVariantType_String : 1.350 + { 1.351 + const NPString *s = &NPVARIANT_TO_STRING(*variant); 1.352 + NS_ConvertUTF8toUTF16 utf16String(s->UTF8Characters, s->UTF8Length); 1.353 + 1.354 + JSString *str = 1.355 + ::JS_NewUCStringCopyN(cx, utf16String.get(), utf16String.Length()); 1.356 + 1.357 + if (str) { 1.358 + return STRING_TO_JSVAL(str); 1.359 + } 1.360 + 1.361 + break; 1.362 + } 1.363 + case NPVariantType_Object: 1.364 + { 1.365 + if (npp) { 1.366 + JSObject *obj = 1.367 + nsNPObjWrapper::GetNewOrUsed(npp, cx, NPVARIANT_TO_OBJECT(*variant)); 1.368 + 1.369 + if (obj) { 1.370 + return OBJECT_TO_JSVAL(obj); 1.371 + } 1.372 + } 1.373 + 1.374 + NS_ERROR("Error wrapping NPObject!"); 1.375 + 1.376 + break; 1.377 + } 1.378 + default: 1.379 + NS_ERROR("Unknown NPVariant type!"); 1.380 + } 1.381 + 1.382 + NS_ERROR("Unable to convert NPVariant to jsval!"); 1.383 + 1.384 + return JSVAL_VOID; 1.385 +} 1.386 + 1.387 +bool 1.388 +JSValToNPVariant(NPP npp, JSContext *cx, JS::Value val, NPVariant *variant) 1.389 +{ 1.390 + NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!"); 1.391 + 1.392 + if (JSVAL_IS_PRIMITIVE(val)) { 1.393 + if (val == JSVAL_VOID) { 1.394 + VOID_TO_NPVARIANT(*variant); 1.395 + } else if (JSVAL_IS_NULL(val)) { 1.396 + NULL_TO_NPVARIANT(*variant); 1.397 + } else if (JSVAL_IS_BOOLEAN(val)) { 1.398 + BOOLEAN_TO_NPVARIANT(JSVAL_TO_BOOLEAN(val), *variant); 1.399 + } else if (JSVAL_IS_INT(val)) { 1.400 + INT32_TO_NPVARIANT(JSVAL_TO_INT(val), *variant); 1.401 + } else if (JSVAL_IS_DOUBLE(val)) { 1.402 + double d = JSVAL_TO_DOUBLE(val); 1.403 + int i; 1.404 + if (JS_DoubleIsInt32(d, &i)) { 1.405 + INT32_TO_NPVARIANT(i, *variant); 1.406 + } else { 1.407 + DOUBLE_TO_NPVARIANT(d, *variant); 1.408 + } 1.409 + } else if (JSVAL_IS_STRING(val)) { 1.410 + JSString *jsstr = JSVAL_TO_STRING(val); 1.411 + size_t length; 1.412 + const jschar *chars = ::JS_GetStringCharsZAndLength(cx, jsstr, &length); 1.413 + if (!chars) { 1.414 + return false; 1.415 + } 1.416 + 1.417 + nsDependentString str(chars, length); 1.418 + 1.419 + uint32_t len; 1.420 + char *p = ToNewUTF8String(str, &len); 1.421 + 1.422 + if (!p) { 1.423 + return false; 1.424 + } 1.425 + 1.426 + STRINGN_TO_NPVARIANT(p, len, *variant); 1.427 + } else { 1.428 + NS_ERROR("Unknown primitive type!"); 1.429 + 1.430 + return false; 1.431 + } 1.432 + 1.433 + return true; 1.434 + } 1.435 + 1.436 + // The reflected plugin object may be in another compartment if the plugin 1.437 + // element has since been adopted into a new document. We don't bother 1.438 + // transplanting the plugin objects, and just do a unwrap with security 1.439 + // checks if we encounter one of them as an argument. If the unwrap fails, 1.440 + // we run with the original wrapped object, since sometimes there are 1.441 + // legitimate cases where a security wrapper ends up here (for example, 1.442 + // Location objects, which are _always_ behind security wrappers). 1.443 + JS::Rooted<JSObject*> obj(cx, val.toObjectOrNull()); 1.444 + obj = js::CheckedUnwrap(obj); 1.445 + if (!obj) { 1.446 + obj = JSVAL_TO_OBJECT(val); 1.447 + } 1.448 + 1.449 + NPObject *npobj = nsJSObjWrapper::GetNewOrUsed(npp, cx, obj); 1.450 + if (!npobj) { 1.451 + return false; 1.452 + } 1.453 + 1.454 + // Pass over ownership of npobj to *variant 1.455 + OBJECT_TO_NPVARIANT(npobj, *variant); 1.456 + 1.457 + return true; 1.458 +} 1.459 + 1.460 +static void 1.461 +ThrowJSException(JSContext *cx, const char *message) 1.462 +{ 1.463 + const char *ex = PeekException(); 1.464 + 1.465 + if (ex) { 1.466 + nsAutoString ucex; 1.467 + 1.468 + if (message) { 1.469 + AppendASCIItoUTF16(message, ucex); 1.470 + 1.471 + AppendASCIItoUTF16(" [plugin exception: ", ucex); 1.472 + } 1.473 + 1.474 + AppendUTF8toUTF16(ex, ucex); 1.475 + 1.476 + if (message) { 1.477 + AppendASCIItoUTF16("].", ucex); 1.478 + } 1.479 + 1.480 + JSString *str = ::JS_NewUCStringCopyN(cx, ucex.get(), ucex.Length()); 1.481 + 1.482 + if (str) { 1.483 + JS::Rooted<JS::Value> exn(cx, JS::StringValue(str)); 1.484 + ::JS_SetPendingException(cx, exn); 1.485 + } 1.486 + 1.487 + PopException(); 1.488 + } else { 1.489 + ::JS_ReportError(cx, message); 1.490 + } 1.491 +} 1.492 + 1.493 +static bool 1.494 +ReportExceptionIfPending(JSContext *cx) 1.495 +{ 1.496 + const char *ex = PeekException(); 1.497 + 1.498 + if (!ex) { 1.499 + return true; 1.500 + } 1.501 + 1.502 + ThrowJSException(cx, nullptr); 1.503 + 1.504 + return false; 1.505 +} 1.506 + 1.507 +nsJSObjWrapper::nsJSObjWrapper(NPP npp) 1.508 + : mJSObj(GetJSContext(npp), nullptr), mNpp(npp) 1.509 +{ 1.510 + MOZ_COUNT_CTOR(nsJSObjWrapper); 1.511 + OnWrapperCreated(); 1.512 +} 1.513 + 1.514 +nsJSObjWrapper::~nsJSObjWrapper() 1.515 +{ 1.516 + MOZ_COUNT_DTOR(nsJSObjWrapper); 1.517 + 1.518 + // Invalidate first, since it relies on sJSRuntime and sJSObjWrappers. 1.519 + NP_Invalidate(this); 1.520 + 1.521 + OnWrapperDestroyed(); 1.522 +} 1.523 + 1.524 +// static 1.525 +NPObject * 1.526 +nsJSObjWrapper::NP_Allocate(NPP npp, NPClass *aClass) 1.527 +{ 1.528 + NS_ASSERTION(aClass == &sJSObjWrapperNPClass, 1.529 + "Huh, wrong class passed to NP_Allocate()!!!"); 1.530 + 1.531 + return new nsJSObjWrapper(npp); 1.532 +} 1.533 + 1.534 +// static 1.535 +void 1.536 +nsJSObjWrapper::NP_Deallocate(NPObject *npobj) 1.537 +{ 1.538 + // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate(). 1.539 + delete (nsJSObjWrapper *)npobj; 1.540 +} 1.541 + 1.542 +// static 1.543 +void 1.544 +nsJSObjWrapper::NP_Invalidate(NPObject *npobj) 1.545 +{ 1.546 + nsJSObjWrapper *jsnpobj = (nsJSObjWrapper *)npobj; 1.547 + 1.548 + if (jsnpobj && jsnpobj->mJSObj) { 1.549 + 1.550 + if (sJSObjWrappersAccessible) { 1.551 + // Remove the wrapper from the hash 1.552 + nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp); 1.553 + JSObjWrapperTable::Ptr ptr = sJSObjWrappers.lookup(key); 1.554 + MOZ_ASSERT(ptr.found()); 1.555 + sJSObjWrappers.remove(ptr); 1.556 + } 1.557 + 1.558 + // Forget our reference to the JSObject. 1.559 + jsnpobj->mJSObj = nullptr; 1.560 + } 1.561 +} 1.562 + 1.563 +static bool 1.564 +GetProperty(JSContext *cx, JSObject *objArg, NPIdentifier npid, JS::MutableHandle<JS::Value> rval) 1.565 +{ 1.566 + NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid), 1.567 + "id must be either string or int!\n"); 1.568 + JS::Rooted<JSObject *> obj(cx, objArg); 1.569 + JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid)); 1.570 + return ::JS_GetPropertyById(cx, obj, id, rval); 1.571 +} 1.572 + 1.573 +// static 1.574 +bool 1.575 +nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id) 1.576 +{ 1.577 + NPP npp = NPPStack::Peek(); 1.578 + JSContext *cx = GetJSContext(npp); 1.579 + 1.580 + if (!cx) { 1.581 + return false; 1.582 + } 1.583 + 1.584 + if (!npobj) { 1.585 + ThrowJSException(cx, 1.586 + "Null npobj in nsJSObjWrapper::NP_HasMethod!"); 1.587 + 1.588 + return false; 1.589 + } 1.590 + 1.591 + nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; 1.592 + 1.593 + nsCxPusher pusher; 1.594 + pusher.Push(cx); 1.595 + JSAutoCompartment ac(cx, npjsobj->mJSObj); 1.596 + 1.597 + AutoJSExceptionReporter reporter(cx); 1.598 + 1.599 + JS::Rooted<JS::Value> v(cx); 1.600 + bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v); 1.601 + 1.602 + return ok && !JSVAL_IS_PRIMITIVE(v) && 1.603 + ::JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v)); 1.604 +} 1.605 + 1.606 +static bool 1.607 +doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args, 1.608 + uint32_t argCount, bool ctorCall, NPVariant *result) 1.609 +{ 1.610 + NPP npp = NPPStack::Peek(); 1.611 + JSContext *cx = GetJSContext(npp); 1.612 + 1.613 + if (!cx) { 1.614 + return false; 1.615 + } 1.616 + 1.617 + if (!npobj || !result) { 1.618 + ThrowJSException(cx, "Null npobj, or result in doInvoke!"); 1.619 + 1.620 + return false; 1.621 + } 1.622 + 1.623 + // Initialize *result 1.624 + VOID_TO_NPVARIANT(*result); 1.625 + 1.626 + nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; 1.627 + 1.628 + nsCxPusher pusher; 1.629 + pusher.Push(cx); 1.630 + JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj); 1.631 + JSAutoCompartment ac(cx, jsobj); 1.632 + JS::Rooted<JS::Value> fv(cx); 1.633 + 1.634 + AutoJSExceptionReporter reporter(cx); 1.635 + 1.636 + if (method != NPIdentifier_VOID) { 1.637 + if (!GetProperty(cx, jsobj, method, &fv) || 1.638 + ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) { 1.639 + return false; 1.640 + } 1.641 + } else { 1.642 + fv.setObject(*jsobj); 1.643 + } 1.644 + 1.645 + // Convert args 1.646 + JS::AutoValueVector jsargs(cx); 1.647 + if (!jsargs.reserve(argCount)) { 1.648 + ::JS_ReportOutOfMemory(cx); 1.649 + return false; 1.650 + } 1.651 + for (uint32_t i = 0; i < argCount; ++i) { 1.652 + jsargs.infallibleAppend(NPVariantToJSVal(npp, cx, args + i)); 1.653 + } 1.654 + 1.655 + JS::Rooted<JS::Value> v(cx); 1.656 + bool ok = false; 1.657 + 1.658 + if (ctorCall) { 1.659 + JSObject *newObj = 1.660 + ::JS_New(cx, jsobj, jsargs); 1.661 + 1.662 + if (newObj) { 1.663 + v.setObject(*newObj); 1.664 + ok = true; 1.665 + } 1.666 + } else { 1.667 + ok = ::JS_CallFunctionValue(cx, jsobj, fv, jsargs, &v); 1.668 + } 1.669 + 1.670 + if (ok) 1.671 + ok = JSValToNPVariant(npp, cx, v, result); 1.672 + 1.673 + return ok; 1.674 +} 1.675 + 1.676 +// static 1.677 +bool 1.678 +nsJSObjWrapper::NP_Invoke(NPObject *npobj, NPIdentifier method, 1.679 + const NPVariant *args, uint32_t argCount, 1.680 + NPVariant *result) 1.681 +{ 1.682 + if (method == NPIdentifier_VOID) { 1.683 + return false; 1.684 + } 1.685 + 1.686 + return doInvoke(npobj, method, args, argCount, false, result); 1.687 +} 1.688 + 1.689 +// static 1.690 +bool 1.691 +nsJSObjWrapper::NP_InvokeDefault(NPObject *npobj, const NPVariant *args, 1.692 + uint32_t argCount, NPVariant *result) 1.693 +{ 1.694 + return doInvoke(npobj, NPIdentifier_VOID, args, argCount, false, 1.695 + result); 1.696 +} 1.697 + 1.698 +// static 1.699 +bool 1.700 +nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier npid) 1.701 +{ 1.702 + NPP npp = NPPStack::Peek(); 1.703 + JSContext *cx = GetJSContext(npp); 1.704 + 1.705 + if (!cx) { 1.706 + return false; 1.707 + } 1.708 + 1.709 + if (!npobj) { 1.710 + ThrowJSException(cx, 1.711 + "Null npobj in nsJSObjWrapper::NP_HasProperty!"); 1.712 + 1.713 + return false; 1.714 + } 1.715 + 1.716 + nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; 1.717 + bool found, ok = false; 1.718 + 1.719 + nsCxPusher pusher; 1.720 + pusher.Push(cx); 1.721 + AutoJSExceptionReporter reporter(cx); 1.722 + JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj); 1.723 + JSAutoCompartment ac(cx, jsobj); 1.724 + 1.725 + NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid), 1.726 + "id must be either string or int!\n"); 1.727 + JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid)); 1.728 + ok = ::JS_HasPropertyById(cx, jsobj, id, &found); 1.729 + return ok && found; 1.730 +} 1.731 + 1.732 +// static 1.733 +bool 1.734 +nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier id, 1.735 + NPVariant *result) 1.736 +{ 1.737 + NPP npp = NPPStack::Peek(); 1.738 + JSContext *cx = GetJSContext(npp); 1.739 + 1.740 + if (!cx) { 1.741 + return false; 1.742 + } 1.743 + 1.744 + if (!npobj) { 1.745 + ThrowJSException(cx, 1.746 + "Null npobj in nsJSObjWrapper::NP_GetProperty!"); 1.747 + 1.748 + return false; 1.749 + } 1.750 + 1.751 + nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; 1.752 + 1.753 + nsCxPusher pusher; 1.754 + pusher.Push(cx); 1.755 + AutoJSExceptionReporter reporter(cx); 1.756 + JSAutoCompartment ac(cx, npjsobj->mJSObj); 1.757 + 1.758 + JS::Rooted<JS::Value> v(cx); 1.759 + return (GetProperty(cx, npjsobj->mJSObj, id, &v) && 1.760 + JSValToNPVariant(npp, cx, v, result)); 1.761 +} 1.762 + 1.763 +// static 1.764 +bool 1.765 +nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier npid, 1.766 + const NPVariant *value) 1.767 +{ 1.768 + NPP npp = NPPStack::Peek(); 1.769 + JSContext *cx = GetJSContext(npp); 1.770 + 1.771 + if (!cx) { 1.772 + return false; 1.773 + } 1.774 + 1.775 + if (!npobj) { 1.776 + ThrowJSException(cx, 1.777 + "Null npobj in nsJSObjWrapper::NP_SetProperty!"); 1.778 + 1.779 + return false; 1.780 + } 1.781 + 1.782 + nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; 1.783 + bool ok = false; 1.784 + 1.785 + nsCxPusher pusher; 1.786 + pusher.Push(cx); 1.787 + AutoJSExceptionReporter reporter(cx); 1.788 + JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj); 1.789 + JSAutoCompartment ac(cx, jsObj); 1.790 + 1.791 + JS::Rooted<JS::Value> v(cx, NPVariantToJSVal(npp, cx, value)); 1.792 + 1.793 + NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid), 1.794 + "id must be either string or int!\n"); 1.795 + JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid)); 1.796 + ok = ::JS_SetPropertyById(cx, jsObj, id, v); 1.797 + 1.798 + return ok; 1.799 +} 1.800 + 1.801 +// static 1.802 +bool 1.803 +nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier npid) 1.804 +{ 1.805 + NPP npp = NPPStack::Peek(); 1.806 + JSContext *cx = GetJSContext(npp); 1.807 + 1.808 + if (!cx) { 1.809 + return false; 1.810 + } 1.811 + 1.812 + if (!npobj) { 1.813 + ThrowJSException(cx, 1.814 + "Null npobj in nsJSObjWrapper::NP_RemoveProperty!"); 1.815 + 1.816 + return false; 1.817 + } 1.818 + 1.819 + nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; 1.820 + bool ok = false; 1.821 + 1.822 + nsCxPusher pusher; 1.823 + pusher.Push(cx); 1.824 + AutoJSExceptionReporter reporter(cx); 1.825 + bool deleted = false; 1.826 + JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj); 1.827 + JSAutoCompartment ac(cx, obj); 1.828 + 1.829 + NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid), 1.830 + "id must be either string or int!\n"); 1.831 + JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid)); 1.832 + ok = ::JS_DeletePropertyById2(cx, obj, id, &deleted); 1.833 + if (ok && deleted) { 1.834 + // FIXME: See bug 425823, we shouldn't need to do this, and once 1.835 + // that bug is fixed we can remove this code. 1.836 + 1.837 + bool hasProp; 1.838 + ok = ::JS_HasPropertyById(cx, obj, id, &hasProp); 1.839 + 1.840 + if (ok && hasProp) { 1.841 + // The property might have been deleted, but it got 1.842 + // re-resolved, so no, it's not really deleted. 1.843 + 1.844 + deleted = false; 1.845 + } 1.846 + } 1.847 + 1.848 + return ok && deleted; 1.849 +} 1.850 + 1.851 +//static 1.852 +bool 1.853 +nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray, 1.854 + uint32_t *count) 1.855 +{ 1.856 + NPP npp = NPPStack::Peek(); 1.857 + JSContext *cx = GetJSContext(npp); 1.858 + 1.859 + *idarray = 0; 1.860 + *count = 0; 1.861 + 1.862 + if (!cx) { 1.863 + return false; 1.864 + } 1.865 + 1.866 + if (!npobj) { 1.867 + ThrowJSException(cx, 1.868 + "Null npobj in nsJSObjWrapper::NP_Enumerate!"); 1.869 + 1.870 + return false; 1.871 + } 1.872 + 1.873 + nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj; 1.874 + 1.875 + nsCxPusher pusher; 1.876 + pusher.Push(cx); 1.877 + AutoJSExceptionReporter reporter(cx); 1.878 + JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj); 1.879 + JSAutoCompartment ac(cx, jsobj); 1.880 + 1.881 + JS::AutoIdArray ida(cx, JS_Enumerate(cx, jsobj)); 1.882 + if (!ida) { 1.883 + return false; 1.884 + } 1.885 + 1.886 + *count = ida.length(); 1.887 + *idarray = (NPIdentifier *)PR_Malloc(*count * sizeof(NPIdentifier)); 1.888 + if (!*idarray) { 1.889 + ThrowJSException(cx, "Memory allocation failed for NPIdentifier!"); 1.890 + return false; 1.891 + } 1.892 + 1.893 + for (uint32_t i = 0; i < *count; i++) { 1.894 + JS::Rooted<JS::Value> v(cx); 1.895 + if (!JS_IdToValue(cx, ida[i], &v)) { 1.896 + PR_Free(*idarray); 1.897 + return false; 1.898 + } 1.899 + 1.900 + NPIdentifier id; 1.901 + if (v.isString()) { 1.902 + JS::Rooted<JSString*> str(cx, v.toString()); 1.903 + str = JS_InternJSString(cx, str); 1.904 + if (!str) { 1.905 + PR_Free(*idarray); 1.906 + return false; 1.907 + } 1.908 + id = StringToNPIdentifier(cx, str); 1.909 + } else { 1.910 + NS_ASSERTION(JSVAL_IS_INT(v), 1.911 + "The element in ida must be either string or int!\n"); 1.912 + id = IntToNPIdentifier(JSVAL_TO_INT(v)); 1.913 + } 1.914 + 1.915 + (*idarray)[i] = id; 1.916 + } 1.917 + 1.918 + return true; 1.919 +} 1.920 + 1.921 +//static 1.922 +bool 1.923 +nsJSObjWrapper::NP_Construct(NPObject *npobj, const NPVariant *args, 1.924 + uint32_t argCount, NPVariant *result) 1.925 +{ 1.926 + return doInvoke(npobj, NPIdentifier_VOID, args, argCount, true, result); 1.927 +} 1.928 + 1.929 + 1.930 + 1.931 +/* 1.932 + * This function is called during minor GCs for each key in the sJSObjWrappers 1.933 + * table that has been moved. 1.934 + * 1.935 + * Note that the wrapper may be dead at this point, and even the table may have 1.936 + * been finalized if all wrappers have died. 1.937 + */ 1.938 +static void 1.939 +JSObjWrapperKeyMarkCallback(JSTracer *trc, JSObject *obj, void *data) { 1.940 + NPP npp = static_cast<NPP>(data); 1.941 + MOZ_ASSERT(sJSObjWrappersAccessible); 1.942 + if (!sJSObjWrappers.initialized()) 1.943 + return; 1.944 + 1.945 + JSObject *prior = obj; 1.946 + nsJSObjWrapperKey oldKey(prior, npp); 1.947 + JSObjWrapperTable::Ptr p = sJSObjWrappers.lookup(oldKey); 1.948 + if (!p) 1.949 + return; 1.950 + 1.951 + JS_CallObjectTracer(trc, &obj, "sJSObjWrappers key object"); 1.952 + nsJSObjWrapperKey newKey(obj, npp); 1.953 + sJSObjWrappers.rekeyIfMoved(oldKey, newKey); 1.954 +} 1.955 + 1.956 +// Look up or create an NPObject that wraps the JSObject obj. 1.957 + 1.958 +// static 1.959 +NPObject * 1.960 +nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JS::Handle<JSObject*> obj) 1.961 +{ 1.962 + if (!npp) { 1.963 + NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!"); 1.964 + 1.965 + return nullptr; 1.966 + } 1.967 + 1.968 + if (!cx) { 1.969 + cx = GetJSContext(npp); 1.970 + 1.971 + if (!cx) { 1.972 + NS_ERROR("Unable to find a JSContext in nsJSObjWrapper::GetNewOrUsed()!"); 1.973 + 1.974 + return nullptr; 1.975 + } 1.976 + } 1.977 + 1.978 + // No need to enter the right compartment here as we only get the 1.979 + // class and private from the JSObject, neither of which cares about 1.980 + // compartments. 1.981 + 1.982 + const JSClass *clazz = JS_GetClass(obj); 1.983 + 1.984 + if (clazz == &sNPObjectJSWrapperClass) { 1.985 + // obj is one of our own, its private data is the NPObject we're 1.986 + // looking for. 1.987 + 1.988 + NPObject *npobj = (NPObject *)::JS_GetPrivate(obj); 1.989 + 1.990 + // If the private is null, that means that the object has already been torn 1.991 + // down, possible because the owning plugin was destroyed (there can be 1.992 + // multiple plugins, so the fact that it was destroyed does not prevent one 1.993 + // of its dead JS objects from being passed to another plugin). There's not 1.994 + // much use in wrapping such a dead object, so we just return null, causing 1.995 + // us to throw. 1.996 + if (!npobj) 1.997 + return nullptr; 1.998 + 1.999 + if (LookupNPP(npobj) == npp) 1.1000 + return _retainobject(npobj); 1.1001 + } 1.1002 + 1.1003 + if (!sJSObjWrappers.initialized()) { 1.1004 + // No hash yet (or any more), initialize it. 1.1005 + if (!sJSObjWrappers.init(16)) { 1.1006 + NS_ERROR("Error initializing PLDHashTable!"); 1.1007 + 1.1008 + return nullptr; 1.1009 + } 1.1010 + sJSObjWrappersAccessible = true; 1.1011 + } 1.1012 + MOZ_ASSERT(sJSObjWrappersAccessible); 1.1013 + 1.1014 + JSObjWrapperTable::Ptr p = sJSObjWrappers.lookupForAdd(nsJSObjWrapperKey(obj, npp)); 1.1015 + if (p) { 1.1016 + MOZ_ASSERT(p->value()); 1.1017 + // Found a live nsJSObjWrapper, return it. 1.1018 + 1.1019 + return _retainobject(p->value()); 1.1020 + } 1.1021 + 1.1022 + // No existing nsJSObjWrapper, create one. 1.1023 + 1.1024 + nsJSObjWrapper *wrapper = 1.1025 + (nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass); 1.1026 + 1.1027 + if (!wrapper) { 1.1028 + // Out of memory, entry not yet added to table. 1.1029 + return nullptr; 1.1030 + } 1.1031 + 1.1032 + // Assign mJSObj, rooting the JSObject. Its lifetime is now tied to that of 1.1033 + // the NPObject. 1.1034 + wrapper->mJSObj = obj; 1.1035 + 1.1036 + nsJSObjWrapperKey key(obj, npp); 1.1037 + if (!sJSObjWrappers.putNew(key, wrapper)) { 1.1038 + // Out of memory, free the wrapper we created. 1.1039 + _releaseobject(wrapper); 1.1040 + return nullptr; 1.1041 + } 1.1042 + 1.1043 + // Add postbarrier for the hashtable key 1.1044 + JS_StoreObjectPostBarrierCallback(cx, JSObjWrapperKeyMarkCallback, obj, wrapper->mNpp); 1.1045 + 1.1046 + return wrapper; 1.1047 +} 1.1048 + 1.1049 +// Climb the prototype chain, unwrapping as necessary until we find an NP object 1.1050 +// wrapper. 1.1051 +// 1.1052 +// Because this function unwraps, its return value must be wrapped for the cx 1.1053 +// compartment for callers that plan to hold onto the result or do anything 1.1054 +// substantial with it. 1.1055 +static JSObject * 1.1056 +GetNPObjectWrapper(JSContext *cx, JSObject *aObj, bool wrapResult = true) 1.1057 +{ 1.1058 + JS::Rooted<JSObject*> obj(cx, aObj); 1.1059 + while (obj && (obj = js::CheckedUnwrap(obj))) { 1.1060 + if (JS_GetClass(obj) == &sNPObjectJSWrapperClass) { 1.1061 + if (wrapResult && !JS_WrapObject(cx, &obj)) { 1.1062 + return nullptr; 1.1063 + } 1.1064 + return obj; 1.1065 + } 1.1066 + if (!::JS_GetPrototype(cx, obj, &obj)) { 1.1067 + return nullptr; 1.1068 + } 1.1069 + } 1.1070 + return nullptr; 1.1071 +} 1.1072 + 1.1073 +static NPObject * 1.1074 +GetNPObject(JSContext *cx, JSObject *obj) 1.1075 +{ 1.1076 + obj = GetNPObjectWrapper(cx, obj, /* wrapResult = */ false); 1.1077 + if (!obj) { 1.1078 + return nullptr; 1.1079 + } 1.1080 + 1.1081 + return (NPObject *)::JS_GetPrivate(obj); 1.1082 +} 1.1083 + 1.1084 + 1.1085 +// Does not actually add a property because this is always followed by a 1.1086 +// SetProperty call. 1.1087 +static bool 1.1088 +NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) 1.1089 +{ 1.1090 + NPObject *npobj = GetNPObject(cx, obj); 1.1091 + 1.1092 + if (!npobj || !npobj->_class || !npobj->_class->hasProperty || 1.1093 + !npobj->_class->hasMethod) { 1.1094 + ThrowJSException(cx, "Bad NPObject as private data!"); 1.1095 + 1.1096 + return false; 1.1097 + } 1.1098 + 1.1099 + if (NPObjectIsOutOfProcessProxy(npobj)) { 1.1100 + return true; 1.1101 + } 1.1102 + 1.1103 + PluginDestructionGuard pdg(LookupNPP(npobj)); 1.1104 + 1.1105 + NPIdentifier identifier = JSIdToNPIdentifier(id); 1.1106 + bool hasProperty = npobj->_class->hasProperty(npobj, identifier); 1.1107 + if (!ReportExceptionIfPending(cx)) 1.1108 + return false; 1.1109 + 1.1110 + if (hasProperty) 1.1111 + return true; 1.1112 + 1.1113 + // We must permit methods here since JS_DefineUCFunction() will add 1.1114 + // the function as a property 1.1115 + bool hasMethod = npobj->_class->hasMethod(npobj, identifier); 1.1116 + if (!ReportExceptionIfPending(cx)) 1.1117 + return false; 1.1118 + 1.1119 + if (!hasMethod) { 1.1120 + ThrowJSException(cx, "Trying to add unsupported property on NPObject!"); 1.1121 + 1.1122 + return false; 1.1123 + } 1.1124 + 1.1125 + return true; 1.1126 +} 1.1127 + 1.1128 +static bool 1.1129 +NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool *succeeded) 1.1130 +{ 1.1131 + NPObject *npobj = GetNPObject(cx, obj); 1.1132 + 1.1133 + if (!npobj || !npobj->_class || !npobj->_class->hasProperty || 1.1134 + !npobj->_class->removeProperty) { 1.1135 + ThrowJSException(cx, "Bad NPObject as private data!"); 1.1136 + 1.1137 + return false; 1.1138 + } 1.1139 + 1.1140 + PluginDestructionGuard pdg(LookupNPP(npobj)); 1.1141 + 1.1142 + NPIdentifier identifier = JSIdToNPIdentifier(id); 1.1143 + 1.1144 + if (!NPObjectIsOutOfProcessProxy(npobj)) { 1.1145 + bool hasProperty = npobj->_class->hasProperty(npobj, identifier); 1.1146 + if (!ReportExceptionIfPending(cx)) 1.1147 + return false; 1.1148 + 1.1149 + if (!hasProperty) { 1.1150 + *succeeded = true; 1.1151 + return true; 1.1152 + } 1.1153 + } 1.1154 + 1.1155 + if (!npobj->_class->removeProperty(npobj, identifier)) 1.1156 + *succeeded = false; 1.1157 + 1.1158 + return ReportExceptionIfPending(cx); 1.1159 +} 1.1160 + 1.1161 +static bool 1.1162 +NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool strict, 1.1163 + JS::MutableHandle<JS::Value> vp) 1.1164 +{ 1.1165 + NPObject *npobj = GetNPObject(cx, obj); 1.1166 + 1.1167 + if (!npobj || !npobj->_class || !npobj->_class->hasProperty || 1.1168 + !npobj->_class->setProperty) { 1.1169 + ThrowJSException(cx, "Bad NPObject as private data!"); 1.1170 + 1.1171 + return false; 1.1172 + } 1.1173 + 1.1174 + // Find out what plugin (NPP) is the owner of the object we're 1.1175 + // manipulating, and make it own any JSObject wrappers created here. 1.1176 + NPP npp = LookupNPP(npobj); 1.1177 + 1.1178 + if (!npp) { 1.1179 + ThrowJSException(cx, "No NPP found for NPObject!"); 1.1180 + 1.1181 + return false; 1.1182 + } 1.1183 + 1.1184 + PluginDestructionGuard pdg(npp); 1.1185 + 1.1186 + NPIdentifier identifier = JSIdToNPIdentifier(id); 1.1187 + 1.1188 + if (!NPObjectIsOutOfProcessProxy(npobj)) { 1.1189 + bool hasProperty = npobj->_class->hasProperty(npobj, identifier); 1.1190 + if (!ReportExceptionIfPending(cx)) 1.1191 + return false; 1.1192 + 1.1193 + if (!hasProperty) { 1.1194 + ThrowJSException(cx, "Trying to set unsupported property on NPObject!"); 1.1195 + 1.1196 + return false; 1.1197 + } 1.1198 + } 1.1199 + 1.1200 + NPVariant npv; 1.1201 + if (!JSValToNPVariant(npp, cx, vp, &npv)) { 1.1202 + ThrowJSException(cx, "Error converting jsval to NPVariant!"); 1.1203 + 1.1204 + return false; 1.1205 + } 1.1206 + 1.1207 + bool ok = npobj->_class->setProperty(npobj, identifier, &npv); 1.1208 + _releasevariantvalue(&npv); // Release the variant 1.1209 + if (!ReportExceptionIfPending(cx)) 1.1210 + return false; 1.1211 + 1.1212 + if (!ok) { 1.1213 + ThrowJSException(cx, "Error setting property on NPObject!"); 1.1214 + 1.1215 + return false; 1.1216 + } 1.1217 + 1.1218 + return true; 1.1219 +} 1.1220 + 1.1221 +static bool 1.1222 +NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) 1.1223 +{ 1.1224 + NPObject *npobj = GetNPObject(cx, obj); 1.1225 + 1.1226 + if (!npobj || !npobj->_class || !npobj->_class->hasProperty || 1.1227 + !npobj->_class->hasMethod || !npobj->_class->getProperty) { 1.1228 + ThrowJSException(cx, "Bad NPObject as private data!"); 1.1229 + 1.1230 + return false; 1.1231 + } 1.1232 + 1.1233 + // Find out what plugin (NPP) is the owner of the object we're 1.1234 + // manipulating, and make it own any JSObject wrappers created here. 1.1235 + NPP npp = LookupNPP(npobj); 1.1236 + if (!npp) { 1.1237 + ThrowJSException(cx, "No NPP found for NPObject!"); 1.1238 + 1.1239 + return false; 1.1240 + } 1.1241 + 1.1242 + PluginDestructionGuard pdg(npp); 1.1243 + 1.1244 + bool hasProperty, hasMethod; 1.1245 + 1.1246 + NPVariant npv; 1.1247 + VOID_TO_NPVARIANT(npv); 1.1248 + 1.1249 + NPIdentifier identifier = JSIdToNPIdentifier(id); 1.1250 + 1.1251 + if (NPObjectIsOutOfProcessProxy(npobj)) { 1.1252 + PluginScriptableObjectParent* actor = 1.1253 + static_cast<ParentNPObject*>(npobj)->parent; 1.1254 + 1.1255 + // actor may be null if the plugin crashed. 1.1256 + if (!actor) 1.1257 + return false; 1.1258 + 1.1259 + bool success = actor->GetPropertyHelper(identifier, &hasProperty, 1.1260 + &hasMethod, &npv); 1.1261 + if (!ReportExceptionIfPending(cx)) { 1.1262 + if (success) 1.1263 + _releasevariantvalue(&npv); 1.1264 + return false; 1.1265 + } 1.1266 + 1.1267 + if (success) { 1.1268 + // We return NPObject Member class here to support ambiguous members. 1.1269 + if (hasProperty && hasMethod) 1.1270 + return CreateNPObjectMember(npp, cx, obj, npobj, id, &npv, vp); 1.1271 + 1.1272 + if (hasProperty) { 1.1273 + vp.set(NPVariantToJSVal(npp, cx, &npv)); 1.1274 + _releasevariantvalue(&npv); 1.1275 + 1.1276 + if (!ReportExceptionIfPending(cx)) 1.1277 + return false; 1.1278 + } 1.1279 + } 1.1280 + return true; 1.1281 + } 1.1282 + 1.1283 + hasProperty = npobj->_class->hasProperty(npobj, identifier); 1.1284 + if (!ReportExceptionIfPending(cx)) 1.1285 + return false; 1.1286 + 1.1287 + hasMethod = npobj->_class->hasMethod(npobj, identifier); 1.1288 + if (!ReportExceptionIfPending(cx)) 1.1289 + return false; 1.1290 + 1.1291 + // We return NPObject Member class here to support ambiguous members. 1.1292 + if (hasProperty && hasMethod) 1.1293 + return CreateNPObjectMember(npp, cx, obj, npobj, id, nullptr, vp); 1.1294 + 1.1295 + if (hasProperty) { 1.1296 + if (npobj->_class->getProperty(npobj, identifier, &npv)) 1.1297 + vp.set(NPVariantToJSVal(npp, cx, &npv)); 1.1298 + 1.1299 + _releasevariantvalue(&npv); 1.1300 + 1.1301 + if (!ReportExceptionIfPending(cx)) 1.1302 + return false; 1.1303 + } 1.1304 + 1.1305 + return true; 1.1306 +} 1.1307 + 1.1308 +static bool 1.1309 +CallNPMethodInternal(JSContext *cx, JS::Handle<JSObject*> obj, unsigned argc, 1.1310 + JS::Value *argv, JS::Value *rval, bool ctorCall) 1.1311 +{ 1.1312 + NPObject *npobj = GetNPObject(cx, obj); 1.1313 + 1.1314 + if (!npobj || !npobj->_class) { 1.1315 + ThrowJSException(cx, "Bad NPObject as private data!"); 1.1316 + 1.1317 + return false; 1.1318 + } 1.1319 + 1.1320 + // Find out what plugin (NPP) is the owner of the object we're 1.1321 + // manipulating, and make it own any JSObject wrappers created here. 1.1322 + NPP npp = LookupNPP(npobj); 1.1323 + 1.1324 + if (!npp) { 1.1325 + ThrowJSException(cx, "Error finding NPP for NPObject!"); 1.1326 + 1.1327 + return false; 1.1328 + } 1.1329 + 1.1330 + PluginDestructionGuard pdg(npp); 1.1331 + 1.1332 + NPVariant npargs_buf[8]; 1.1333 + NPVariant *npargs = npargs_buf; 1.1334 + 1.1335 + if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) { 1.1336 + // Our stack buffer isn't large enough to hold all arguments, 1.1337 + // malloc a buffer. 1.1338 + npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant)); 1.1339 + 1.1340 + if (!npargs) { 1.1341 + ThrowJSException(cx, "Out of memory!"); 1.1342 + 1.1343 + return false; 1.1344 + } 1.1345 + } 1.1346 + 1.1347 + // Convert arguments 1.1348 + uint32_t i; 1.1349 + for (i = 0; i < argc; ++i) { 1.1350 + if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) { 1.1351 + ThrowJSException(cx, "Error converting jsvals to NPVariants!"); 1.1352 + 1.1353 + if (npargs != npargs_buf) { 1.1354 + PR_Free(npargs); 1.1355 + } 1.1356 + 1.1357 + return false; 1.1358 + } 1.1359 + } 1.1360 + 1.1361 + NPVariant v; 1.1362 + VOID_TO_NPVARIANT(v); 1.1363 + 1.1364 + JSObject *funobj = JSVAL_TO_OBJECT(argv[-2]); 1.1365 + bool ok; 1.1366 + const char *msg = "Error calling method on NPObject!"; 1.1367 + 1.1368 + if (ctorCall) { 1.1369 + // construct a new NPObject based on the NPClass in npobj. Fail if 1.1370 + // no construct method is available. 1.1371 + 1.1372 + if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) && 1.1373 + npobj->_class->construct) { 1.1374 + ok = npobj->_class->construct(npobj, npargs, argc, &v); 1.1375 + } else { 1.1376 + ok = false; 1.1377 + 1.1378 + msg = "Attempt to construct object from class with no constructor."; 1.1379 + } 1.1380 + } else if (funobj != obj) { 1.1381 + // A obj.function() style call is made, get the method name from 1.1382 + // the function object. 1.1383 + 1.1384 + if (npobj->_class->invoke) { 1.1385 + JSFunction *fun = ::JS_GetObjectFunction(funobj); 1.1386 + JS::Rooted<JSString*> funId(cx, ::JS_GetFunctionId(fun)); 1.1387 + JSString *name = ::JS_InternJSString(cx, funId); 1.1388 + NPIdentifier id = StringToNPIdentifier(cx, name); 1.1389 + 1.1390 + ok = npobj->_class->invoke(npobj, id, npargs, argc, &v); 1.1391 + } else { 1.1392 + ok = false; 1.1393 + 1.1394 + msg = "Attempt to call a method on object with no invoke method."; 1.1395 + } 1.1396 + } else { 1.1397 + if (npobj->_class->invokeDefault) { 1.1398 + // obj is a callable object that is being called, no method name 1.1399 + // available then. Invoke the default method. 1.1400 + 1.1401 + ok = npobj->_class->invokeDefault(npobj, npargs, argc, &v); 1.1402 + } else { 1.1403 + ok = false; 1.1404 + 1.1405 + msg = "Attempt to call a default method on object with no " 1.1406 + "invokeDefault method."; 1.1407 + } 1.1408 + } 1.1409 + 1.1410 + // Release arguments. 1.1411 + for (i = 0; i < argc; ++i) { 1.1412 + _releasevariantvalue(npargs + i); 1.1413 + } 1.1414 + 1.1415 + if (npargs != npargs_buf) { 1.1416 + PR_Free(npargs); 1.1417 + } 1.1418 + 1.1419 + if (!ok) { 1.1420 + // ReportExceptionIfPending returns a return value, which is true 1.1421 + // if no exception was thrown. In that case, throw our own. 1.1422 + if (ReportExceptionIfPending(cx)) 1.1423 + ThrowJSException(cx, msg); 1.1424 + 1.1425 + return false; 1.1426 + } 1.1427 + 1.1428 + *rval = NPVariantToJSVal(npp, cx, &v); 1.1429 + 1.1430 + // *rval now owns the value, release our reference. 1.1431 + _releasevariantvalue(&v); 1.1432 + 1.1433 + return ReportExceptionIfPending(cx); 1.1434 +} 1.1435 + 1.1436 +static bool 1.1437 +CallNPMethod(JSContext *cx, unsigned argc, JS::Value *vp) 1.1438 +{ 1.1439 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1440 + JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp)); 1.1441 + if (!obj) 1.1442 + return false; 1.1443 + 1.1444 + return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false); 1.1445 +} 1.1446 + 1.1447 +struct NPObjectEnumerateState { 1.1448 + uint32_t index; 1.1449 + uint32_t length; 1.1450 + NPIdentifier *value; 1.1451 +}; 1.1452 + 1.1453 +static bool 1.1454 +NPObjWrapper_newEnumerate(JSContext *cx, JS::Handle<JSObject*> obj, JSIterateOp enum_op, 1.1455 + JS::Value *statep, jsid *idp) 1.1456 +{ 1.1457 + NPObject *npobj = GetNPObject(cx, obj); 1.1458 + NPIdentifier *enum_value; 1.1459 + uint32_t length; 1.1460 + NPObjectEnumerateState *state; 1.1461 + 1.1462 + if (!npobj || !npobj->_class) { 1.1463 + ThrowJSException(cx, "Bad NPObject as private data!"); 1.1464 + return false; 1.1465 + } 1.1466 + 1.1467 + PluginDestructionGuard pdg(LookupNPP(npobj)); 1.1468 + 1.1469 + NS_ASSERTION(statep, "Must have a statep to enumerate!"); 1.1470 + 1.1471 + switch(enum_op) { 1.1472 + case JSENUMERATE_INIT: 1.1473 + case JSENUMERATE_INIT_ALL: 1.1474 + state = new NPObjectEnumerateState(); 1.1475 + if (!state) { 1.1476 + ThrowJSException(cx, "Memory allocation failed for " 1.1477 + "NPObjectEnumerateState!"); 1.1478 + 1.1479 + return false; 1.1480 + } 1.1481 + 1.1482 + if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) || 1.1483 + !npobj->_class->enumerate) { 1.1484 + enum_value = 0; 1.1485 + length = 0; 1.1486 + } else if (!npobj->_class->enumerate(npobj, &enum_value, &length)) { 1.1487 + delete state; 1.1488 + 1.1489 + if (ReportExceptionIfPending(cx)) { 1.1490 + // ReportExceptionIfPending returns a return value, which is true 1.1491 + // if no exception was thrown. In that case, throw our own. 1.1492 + ThrowJSException(cx, "Error enumerating properties on scriptable " 1.1493 + "plugin object"); 1.1494 + } 1.1495 + 1.1496 + return false; 1.1497 + } 1.1498 + 1.1499 + state->value = enum_value; 1.1500 + state->length = length; 1.1501 + state->index = 0; 1.1502 + *statep = PRIVATE_TO_JSVAL(state); 1.1503 + if (idp) { 1.1504 + *idp = INT_TO_JSID(length); 1.1505 + } 1.1506 + 1.1507 + break; 1.1508 + 1.1509 + case JSENUMERATE_NEXT: 1.1510 + state = (NPObjectEnumerateState *)JSVAL_TO_PRIVATE(*statep); 1.1511 + enum_value = state->value; 1.1512 + length = state->length; 1.1513 + if (state->index != length) { 1.1514 + *idp = NPIdentifierToJSId(enum_value[state->index++]); 1.1515 + return true; 1.1516 + } 1.1517 + 1.1518 + // FALL THROUGH 1.1519 + 1.1520 + case JSENUMERATE_DESTROY: 1.1521 + state = (NPObjectEnumerateState *)JSVAL_TO_PRIVATE(*statep); 1.1522 + if (state->value) 1.1523 + PR_Free(state->value); 1.1524 + delete state; 1.1525 + *statep = JSVAL_NULL; 1.1526 + 1.1527 + break; 1.1528 + } 1.1529 + 1.1530 + return true; 1.1531 +} 1.1532 + 1.1533 +static bool 1.1534 +NPObjWrapper_NewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, 1.1535 + JS::MutableHandle<JSObject*> objp) 1.1536 +{ 1.1537 + NPObject *npobj = GetNPObject(cx, obj); 1.1538 + 1.1539 + if (!npobj || !npobj->_class || !npobj->_class->hasProperty || 1.1540 + !npobj->_class->hasMethod) { 1.1541 + ThrowJSException(cx, "Bad NPObject as private data!"); 1.1542 + 1.1543 + return false; 1.1544 + } 1.1545 + 1.1546 + PluginDestructionGuard pdg(LookupNPP(npobj)); 1.1547 + 1.1548 + NPIdentifier identifier = JSIdToNPIdentifier(id); 1.1549 + 1.1550 + bool hasProperty = npobj->_class->hasProperty(npobj, identifier); 1.1551 + if (!ReportExceptionIfPending(cx)) 1.1552 + return false; 1.1553 + 1.1554 + if (hasProperty) { 1.1555 + NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id), 1.1556 + "id must be either string or int!\n"); 1.1557 + if (!::JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, nullptr, 1.1558 + nullptr, JSPROP_ENUMERATE | JSPROP_SHARED)) { 1.1559 + return false; 1.1560 + } 1.1561 + 1.1562 + objp.set(obj); 1.1563 + 1.1564 + return true; 1.1565 + } 1.1566 + 1.1567 + bool hasMethod = npobj->_class->hasMethod(npobj, identifier); 1.1568 + if (!ReportExceptionIfPending(cx)) 1.1569 + return false; 1.1570 + 1.1571 + if (hasMethod) { 1.1572 + NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id), 1.1573 + "id must be either string or int!\n"); 1.1574 + 1.1575 + JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallNPMethod, 0, 1.1576 + JSPROP_ENUMERATE); 1.1577 + 1.1578 + objp.set(obj); 1.1579 + 1.1580 + return fnc != nullptr; 1.1581 + } 1.1582 + 1.1583 + // no property or method 1.1584 + return true; 1.1585 +} 1.1586 + 1.1587 +static bool 1.1588 +NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType hint, JS::MutableHandle<JS::Value> vp) 1.1589 +{ 1.1590 + MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID); 1.1591 + 1.1592 + // Plugins do not simply use JS_ConvertStub, and the default [[DefaultValue]] 1.1593 + // behavior, because that behavior involves calling toString or valueOf on 1.1594 + // objects which weren't designed to accommodate this. Usually this wouldn't 1.1595 + // be a problem, because the absence of either property, or the presence of 1.1596 + // either property with a value that isn't callable, will cause that property 1.1597 + // to simply be ignored. But there is a problem in one specific case: Java, 1.1598 + // specifically java.lang.Integer. The Integer class has static valueOf 1.1599 + // methods, none of which are nullary, so the JS-reflected method will behave 1.1600 + // poorly when called with no arguments. We work around this problem by 1.1601 + // giving plugins a [[DefaultValue]] which uses only toString and not valueOf. 1.1602 + 1.1603 + JS::Rooted<JS::Value> v(cx, JSVAL_VOID); 1.1604 + if (!JS_GetProperty(cx, obj, "toString", &v)) 1.1605 + return false; 1.1606 + if (!JSVAL_IS_PRIMITIVE(v) && JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(v))) { 1.1607 + if (!JS_CallFunctionValue(cx, obj, v, JS::HandleValueArray::empty(), vp)) 1.1608 + return false; 1.1609 + if (JSVAL_IS_PRIMITIVE(vp)) 1.1610 + return true; 1.1611 + } 1.1612 + 1.1613 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, 1.1614 + JS_GetClass(obj)->name, 1.1615 + hint == JSTYPE_VOID 1.1616 + ? "primitive type" 1.1617 + : hint == JSTYPE_NUMBER 1.1618 + ? "number" 1.1619 + : "string"); 1.1620 + return false; 1.1621 +} 1.1622 + 1.1623 +static void 1.1624 +NPObjWrapper_Finalize(JSFreeOp *fop, JSObject *obj) 1.1625 +{ 1.1626 + NPObject *npobj = (NPObject *)::JS_GetPrivate(obj); 1.1627 + if (npobj) { 1.1628 + if (sNPObjWrappers.ops) { 1.1629 + PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_REMOVE); 1.1630 + } 1.1631 + } 1.1632 + 1.1633 + if (!sDelayedReleases) 1.1634 + sDelayedReleases = new nsTArray<NPObject*>; 1.1635 + sDelayedReleases->AppendElement(npobj); 1.1636 +} 1.1637 + 1.1638 +static bool 1.1639 +NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp) 1.1640 +{ 1.1641 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1642 + JS::Rooted<JSObject*> obj(cx, &args.callee()); 1.1643 + return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false); 1.1644 +} 1.1645 + 1.1646 +static bool 1.1647 +NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp) 1.1648 +{ 1.1649 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1650 + JS::Rooted<JSObject*> obj(cx, &args.callee()); 1.1651 + return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, true); 1.1652 +} 1.1653 + 1.1654 +class NPObjWrapperHashEntry : public PLDHashEntryHdr 1.1655 +{ 1.1656 +public: 1.1657 + NPObject *mNPObj; // Must be the first member for the PLDHash stubs to work 1.1658 + JSObject *mJSObj; 1.1659 + NPP mNpp; 1.1660 +}; 1.1661 + 1.1662 + 1.1663 +// An NPObject is going away, make sure we null out the JS object's 1.1664 +// private data in case this is an NPObject that came from a plugin 1.1665 +// and it's destroyed prematurely. 1.1666 + 1.1667 +// static 1.1668 +void 1.1669 +nsNPObjWrapper::OnDestroy(NPObject *npobj) 1.1670 +{ 1.1671 + if (!npobj) { 1.1672 + return; 1.1673 + } 1.1674 + 1.1675 + if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) { 1.1676 + // npobj is one of our own, no private data to clean up here. 1.1677 + 1.1678 + return; 1.1679 + } 1.1680 + 1.1681 + if (!sNPObjWrappers.ops) { 1.1682 + // No hash yet (or any more), no used wrappers available. 1.1683 + 1.1684 + return; 1.1685 + } 1.1686 + 1.1687 + NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *> 1.1688 + (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP)); 1.1689 + 1.1690 + if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) { 1.1691 + // Found a live NPObject wrapper, null out its JSObjects' private 1.1692 + // data. 1.1693 + 1.1694 + ::JS_SetPrivate(entry->mJSObj, nullptr); 1.1695 + 1.1696 + // Remove the npobj from the hash now that it went away. 1.1697 + PL_DHashTableRawRemove(&sNPObjWrappers, entry); 1.1698 + 1.1699 + // The finalize hook will call OnWrapperDestroyed(). 1.1700 + } 1.1701 +} 1.1702 + 1.1703 +// Look up or create a JSObject that wraps the NPObject npobj. 1.1704 + 1.1705 +// static 1.1706 +JSObject * 1.1707 +nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj) 1.1708 +{ 1.1709 + if (!npobj) { 1.1710 + NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!"); 1.1711 + 1.1712 + return nullptr; 1.1713 + } 1.1714 + 1.1715 + if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) { 1.1716 + // npobj is one of our own, return its existing JSObject. 1.1717 + 1.1718 + JS::Rooted<JSObject*> obj(cx, ((nsJSObjWrapper *)npobj)->mJSObj); 1.1719 + if (!JS_WrapObject(cx, &obj)) { 1.1720 + return nullptr; 1.1721 + } 1.1722 + return obj; 1.1723 + } 1.1724 + 1.1725 + if (!npp) { 1.1726 + NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!"); 1.1727 + 1.1728 + return nullptr; 1.1729 + } 1.1730 + 1.1731 + if (!sNPObjWrappers.ops) { 1.1732 + // No hash yet (or any more), initialize it. 1.1733 + PL_DHashTableInit(&sNPObjWrappers, PL_DHashGetStubOps(), nullptr, 1.1734 + sizeof(NPObjWrapperHashEntry), 16); 1.1735 + } 1.1736 + 1.1737 + NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *> 1.1738 + (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD)); 1.1739 + 1.1740 + if (!entry) { 1.1741 + // Out of memory 1.1742 + JS_ReportOutOfMemory(cx); 1.1743 + 1.1744 + return nullptr; 1.1745 + } 1.1746 + 1.1747 + if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) { 1.1748 + // Found a live NPObject wrapper. It may not be in the same compartment 1.1749 + // as cx, so we need to wrap it before returning it. 1.1750 + JS::Rooted<JSObject*> obj(cx, entry->mJSObj); 1.1751 + if (!JS_WrapObject(cx, &obj)) { 1.1752 + return nullptr; 1.1753 + } 1.1754 + return obj; 1.1755 + } 1.1756 + 1.1757 + entry->mNPObj = npobj; 1.1758 + entry->mNpp = npp; 1.1759 + 1.1760 + uint32_t generation = sNPObjWrappers.generation; 1.1761 + 1.1762 + // No existing JSObject, create one. 1.1763 + 1.1764 + JS::Rooted<JSObject*> obj(cx, ::JS_NewObject(cx, &sNPObjectJSWrapperClass, JS::NullPtr(), 1.1765 + JS::NullPtr())); 1.1766 + 1.1767 + if (generation != sNPObjWrappers.generation) { 1.1768 + // Reload entry if the JS_NewObject call caused a GC and reallocated 1.1769 + // the table (see bug 445229). This is guaranteed to succeed. 1.1770 + 1.1771 + entry = static_cast<NPObjWrapperHashEntry *> 1.1772 + (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP)); 1.1773 + NS_ASSERTION(entry && PL_DHASH_ENTRY_IS_BUSY(entry), 1.1774 + "Hashtable didn't find what we just added?"); 1.1775 + } 1.1776 + 1.1777 + if (!obj) { 1.1778 + // OOM? Remove the stale entry from the hash. 1.1779 + 1.1780 + PL_DHashTableRawRemove(&sNPObjWrappers, entry); 1.1781 + 1.1782 + return nullptr; 1.1783 + } 1.1784 + 1.1785 + OnWrapperCreated(); 1.1786 + 1.1787 + entry->mJSObj = obj; 1.1788 + 1.1789 + ::JS_SetPrivate(obj, npobj); 1.1790 + 1.1791 + // The new JSObject now holds on to npobj 1.1792 + _retainobject(npobj); 1.1793 + 1.1794 + return obj; 1.1795 +} 1.1796 + 1.1797 + 1.1798 +// Struct for passing an NPP and a JSContext to 1.1799 +// NPObjWrapperPluginDestroyedCallback 1.1800 +struct NppAndCx 1.1801 +{ 1.1802 + NPP npp; 1.1803 + JSContext *cx; 1.1804 +}; 1.1805 + 1.1806 +static PLDHashOperator 1.1807 +NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.1808 + uint32_t number, void *arg) 1.1809 +{ 1.1810 + NPObjWrapperHashEntry *entry = (NPObjWrapperHashEntry *)hdr; 1.1811 + NppAndCx *nppcx = reinterpret_cast<NppAndCx *>(arg); 1.1812 + 1.1813 + if (entry->mNpp == nppcx->npp) { 1.1814 + // Prevent invalidate() and deallocate() from touching the hash 1.1815 + // we're enumerating. 1.1816 + const PLDHashTableOps *ops = table->ops; 1.1817 + table->ops = nullptr; 1.1818 + 1.1819 + NPObject *npobj = entry->mNPObj; 1.1820 + 1.1821 + if (npobj->_class && npobj->_class->invalidate) { 1.1822 + npobj->_class->invalidate(npobj); 1.1823 + } 1.1824 + 1.1825 +#ifdef NS_BUILD_REFCNT_LOGGING 1.1826 + { 1.1827 + int32_t refCnt = npobj->referenceCount; 1.1828 + while (refCnt) { 1.1829 + --refCnt; 1.1830 + NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject"); 1.1831 + } 1.1832 + } 1.1833 +#endif 1.1834 + 1.1835 + // Force deallocation of plugin objects since the plugin they came 1.1836 + // from is being torn down. 1.1837 + if (npobj->_class && npobj->_class->deallocate) { 1.1838 + npobj->_class->deallocate(npobj); 1.1839 + } else { 1.1840 + PR_Free(npobj); 1.1841 + } 1.1842 + 1.1843 + ::JS_SetPrivate(entry->mJSObj, nullptr); 1.1844 + 1.1845 + table->ops = ops; 1.1846 + 1.1847 + if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) { 1.1848 + OnWrapperDestroyed(); 1.1849 + } 1.1850 + 1.1851 + return PL_DHASH_REMOVE; 1.1852 + } 1.1853 + 1.1854 + return PL_DHASH_NEXT; 1.1855 +} 1.1856 + 1.1857 +// static 1.1858 +void 1.1859 +nsJSNPRuntime::OnPluginDestroy(NPP npp) 1.1860 +{ 1.1861 + if (sJSObjWrappersAccessible) { 1.1862 + 1.1863 + // Prevent modification of sJSObjWrappers table if we go reentrant. 1.1864 + sJSObjWrappersAccessible = false; 1.1865 + 1.1866 + for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) { 1.1867 + nsJSObjWrapper *npobj = e.front().value(); 1.1868 + MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass); 1.1869 + if (npobj->mNpp == npp) { 1.1870 + if (npobj->_class && npobj->_class->invalidate) { 1.1871 + npobj->_class->invalidate(npobj); 1.1872 + } 1.1873 + 1.1874 + _releaseobject(npobj); 1.1875 + 1.1876 + e.removeFront(); 1.1877 + } 1.1878 + } 1.1879 + 1.1880 + sJSObjWrappersAccessible = true; 1.1881 + } 1.1882 + 1.1883 + // Use the safe JSContext here as we're not always able to find the 1.1884 + // JSContext associated with the NPP any more. 1.1885 + AutoSafeJSContext cx; 1.1886 + if (sNPObjWrappers.ops) { 1.1887 + NppAndCx nppcx = { npp, cx }; 1.1888 + PL_DHashTableEnumerate(&sNPObjWrappers, 1.1889 + NPObjWrapperPluginDestroyedCallback, &nppcx); 1.1890 + } 1.1891 +} 1.1892 + 1.1893 + 1.1894 +// Find the NPP for a NPObject. 1.1895 +static NPP 1.1896 +LookupNPP(NPObject *npobj) 1.1897 +{ 1.1898 + if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) { 1.1899 + nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj); 1.1900 + return o->mNpp; 1.1901 + } 1.1902 + 1.1903 + NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *> 1.1904 + (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD)); 1.1905 + 1.1906 + if (PL_DHASH_ENTRY_IS_FREE(entry)) { 1.1907 + return nullptr; 1.1908 + } 1.1909 + 1.1910 + NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!"); 1.1911 + 1.1912 + return entry->mNpp; 1.1913 +} 1.1914 + 1.1915 +static bool 1.1916 +CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj, 1.1917 + JS::Handle<jsid> id, NPVariant* getPropertyResult, 1.1918 + JS::MutableHandle<JS::Value> vp) 1.1919 +{ 1.1920 + if (!npobj || !npobj->_class || !npobj->_class->getProperty || 1.1921 + !npobj->_class->invoke) { 1.1922 + ThrowJSException(cx, "Bad NPObject"); 1.1923 + 1.1924 + return false; 1.1925 + } 1.1926 + 1.1927 + NPObjectMemberPrivate *memberPrivate = 1.1928 + (NPObjectMemberPrivate *)PR_Malloc(sizeof(NPObjectMemberPrivate)); 1.1929 + if (!memberPrivate) 1.1930 + return false; 1.1931 + 1.1932 + // Make sure to clear all members in case something fails here 1.1933 + // during initialization. 1.1934 + memset(memberPrivate, 0, sizeof(NPObjectMemberPrivate)); 1.1935 + 1.1936 + JSObject *memobj = ::JS_NewObject(cx, &sNPObjectMemberClass, JS::NullPtr(), JS::NullPtr()); 1.1937 + if (!memobj) { 1.1938 + PR_Free(memberPrivate); 1.1939 + return false; 1.1940 + } 1.1941 + 1.1942 + vp.setObject(*memobj); 1.1943 + 1.1944 + ::JS_SetPrivate(memobj, (void *)memberPrivate); 1.1945 + 1.1946 + NPIdentifier identifier = JSIdToNPIdentifier(id); 1.1947 + 1.1948 + JS::Rooted<JS::Value> fieldValue(cx); 1.1949 + NPVariant npv; 1.1950 + 1.1951 + if (getPropertyResult) { 1.1952 + // Plugin has already handed us the value we want here. 1.1953 + npv = *getPropertyResult; 1.1954 + } 1.1955 + else { 1.1956 + VOID_TO_NPVARIANT(npv); 1.1957 + 1.1958 + NPBool hasProperty = npobj->_class->getProperty(npobj, identifier, 1.1959 + &npv); 1.1960 + if (!ReportExceptionIfPending(cx) || !hasProperty) { 1.1961 + return false; 1.1962 + } 1.1963 + } 1.1964 + 1.1965 + fieldValue = NPVariantToJSVal(npp, cx, &npv); 1.1966 + 1.1967 + // npobjWrapper is the JSObject through which we make sure we don't 1.1968 + // outlive the underlying NPObject, so make sure it points to the 1.1969 + // real JSObject wrapper for the NPObject. 1.1970 + obj = GetNPObjectWrapper(cx, obj); 1.1971 + 1.1972 + memberPrivate->npobjWrapper = obj; 1.1973 + 1.1974 + memberPrivate->fieldValue = fieldValue; 1.1975 + memberPrivate->methodName = id; 1.1976 + memberPrivate->npp = npp; 1.1977 + 1.1978 + return true; 1.1979 +} 1.1980 + 1.1981 +static bool 1.1982 +NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp) 1.1983 +{ 1.1984 + NPObjectMemberPrivate *memberPrivate = 1.1985 + (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj, 1.1986 + &sNPObjectMemberClass, 1.1987 + nullptr); 1.1988 + if (!memberPrivate) { 1.1989 + NS_ERROR("no Ambiguous Member Private data!"); 1.1990 + return false; 1.1991 + } 1.1992 + 1.1993 + switch (type) { 1.1994 + case JSTYPE_VOID: 1.1995 + case JSTYPE_STRING: 1.1996 + case JSTYPE_NUMBER: 1.1997 + vp.set(memberPrivate->fieldValue); 1.1998 + if (vp.isObject()) { 1.1999 + JS::Rooted<JSObject*> objVal(cx, &vp.toObject()); 1.2000 + return JS_DefaultValue(cx, objVal, type, vp); 1.2001 + } 1.2002 + return true; 1.2003 + case JSTYPE_BOOLEAN: 1.2004 + case JSTYPE_OBJECT: 1.2005 + vp.set(memberPrivate->fieldValue); 1.2006 + return true; 1.2007 + case JSTYPE_FUNCTION: 1.2008 + // Leave this to NPObjectMember_Call. 1.2009 + return true; 1.2010 + default: 1.2011 + NS_ERROR("illegal operation on JSObject prototype object"); 1.2012 + return false; 1.2013 + } 1.2014 +} 1.2015 + 1.2016 +static void 1.2017 +NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj) 1.2018 +{ 1.2019 + NPObjectMemberPrivate *memberPrivate; 1.2020 + 1.2021 + memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(obj); 1.2022 + if (!memberPrivate) 1.2023 + return; 1.2024 + 1.2025 + PR_Free(memberPrivate); 1.2026 +} 1.2027 + 1.2028 +static bool 1.2029 +NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp) 1.2030 +{ 1.2031 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.2032 + JS::Rooted<JSObject*> memobj(cx, &args.callee()); 1.2033 + NS_ENSURE_TRUE(memobj, false); 1.2034 + 1.2035 + NPObjectMemberPrivate *memberPrivate = 1.2036 + (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj, 1.2037 + &sNPObjectMemberClass, 1.2038 + &args); 1.2039 + if (!memberPrivate || !memberPrivate->npobjWrapper) 1.2040 + return false; 1.2041 + 1.2042 + NPObject *npobj = GetNPObject(cx, memberPrivate->npobjWrapper); 1.2043 + if (!npobj) { 1.2044 + ThrowJSException(cx, "Call on invalid member object"); 1.2045 + 1.2046 + return false; 1.2047 + } 1.2048 + 1.2049 + NPVariant npargs_buf[8]; 1.2050 + NPVariant *npargs = npargs_buf; 1.2051 + 1.2052 + if (args.length() > (sizeof(npargs_buf) / sizeof(NPVariant))) { 1.2053 + // Our stack buffer isn't large enough to hold all arguments, 1.2054 + // malloc a buffer. 1.2055 + npargs = (NPVariant *)PR_Malloc(args.length() * sizeof(NPVariant)); 1.2056 + 1.2057 + if (!npargs) { 1.2058 + ThrowJSException(cx, "Out of memory!"); 1.2059 + 1.2060 + return false; 1.2061 + } 1.2062 + } 1.2063 + 1.2064 + // Convert arguments 1.2065 + for (uint32_t i = 0; i < args.length(); ++i) { 1.2066 + if (!JSValToNPVariant(memberPrivate->npp, cx, args[i], npargs + i)) { 1.2067 + ThrowJSException(cx, "Error converting jsvals to NPVariants!"); 1.2068 + 1.2069 + if (npargs != npargs_buf) { 1.2070 + PR_Free(npargs); 1.2071 + } 1.2072 + 1.2073 + return false; 1.2074 + } 1.2075 + } 1.2076 + 1.2077 + 1.2078 + NPVariant npv; 1.2079 + bool ok = npobj->_class->invoke(npobj, 1.2080 + JSIdToNPIdentifier(memberPrivate->methodName), 1.2081 + npargs, args.length(), &npv); 1.2082 + 1.2083 + // Release arguments. 1.2084 + for (uint32_t i = 0; i < args.length(); ++i) { 1.2085 + _releasevariantvalue(npargs + i); 1.2086 + } 1.2087 + 1.2088 + if (npargs != npargs_buf) { 1.2089 + PR_Free(npargs); 1.2090 + } 1.2091 + 1.2092 + if (!ok) { 1.2093 + // ReportExceptionIfPending returns a return value, which is true 1.2094 + // if no exception was thrown. In that case, throw our own. 1.2095 + if (ReportExceptionIfPending(cx)) 1.2096 + ThrowJSException(cx, "Error calling method on NPObject!"); 1.2097 + 1.2098 + return false; 1.2099 + } 1.2100 + 1.2101 + args.rval().set(NPVariantToJSVal(memberPrivate->npp, cx, &npv)); 1.2102 + 1.2103 + // *vp now owns the value, release our reference. 1.2104 + _releasevariantvalue(&npv); 1.2105 + 1.2106 + return ReportExceptionIfPending(cx); 1.2107 +} 1.2108 + 1.2109 +static void 1.2110 +NPObjectMember_Trace(JSTracer *trc, JSObject *obj) 1.2111 +{ 1.2112 + NPObjectMemberPrivate *memberPrivate = 1.2113 + (NPObjectMemberPrivate *)::JS_GetPrivate(obj); 1.2114 + if (!memberPrivate) 1.2115 + return; 1.2116 + 1.2117 + // Our NPIdentifier is not always interned, so we must root it explicitly. 1.2118 + JS_CallHeapIdTracer(trc, &memberPrivate->methodName, "NPObjectMemberPrivate.methodName"); 1.2119 + 1.2120 + if (!JSVAL_IS_PRIMITIVE(memberPrivate->fieldValue)) { 1.2121 + JS_CallHeapValueTracer(trc, &memberPrivate->fieldValue, 1.2122 + "NPObject Member => fieldValue"); 1.2123 + } 1.2124 + 1.2125 + // There's no strong reference from our private data to the 1.2126 + // NPObject, so make sure to mark the NPObject wrapper to keep the 1.2127 + // NPObject alive as long as this NPObjectMember is alive. 1.2128 + if (memberPrivate->npobjWrapper) { 1.2129 + JS_CallHeapObjectTracer(trc, &memberPrivate->npobjWrapper, 1.2130 + "NPObject Member => npobjWrapper"); 1.2131 + } 1.2132 +}