1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Josh Aas <josh@mozilla.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "base/basictypes.h"
40 :
41 : #include "jsapi.h"
42 : #include "jsfriendapi.h"
43 :
44 : #include "nsIInterfaceRequestorUtils.h"
45 : #include "nsJSNPRuntime.h"
46 : #include "nsNPAPIPlugin.h"
47 : #include "nsNPAPIPluginInstance.h"
48 : #include "nsIScriptGlobalObject.h"
49 : #include "nsIScriptContext.h"
50 : #include "nsDOMJSUtils.h"
51 : #include "nsIDocument.h"
52 : #include "nsIJSRuntimeService.h"
53 : #include "nsIJSContextStack.h"
54 : #include "nsIXPConnect.h"
55 : #include "nsIDOMElement.h"
56 : #include "prmem.h"
57 : #include "nsIContent.h"
58 : #include "nsIPluginInstanceOwner.h"
59 : #include "mozilla/HashFunctions.h"
60 :
61 : #define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class"
62 :
63 : using namespace mozilla::plugins::parent;
64 : using namespace mozilla;
65 :
66 : #include "mozilla/plugins/PluginScriptableObjectParent.h"
67 : using mozilla::plugins::PluginScriptableObjectParent;
68 : using mozilla::plugins::ParentNPObject;
69 :
70 : // Hash of JSObject wrappers that wraps JSObjects as NPObjects. There
71 : // will be one wrapper per JSObject per plugin instance, i.e. if two
72 : // plugins access the JSObject x, two wrappers for x will be
73 : // created. This is needed to be able to properly drop the wrappers
74 : // when a plugin is torn down in case there's a leak in the plugin (we
75 : // don't want to leak the world just because a plugin leaks an
76 : // NPObject).
77 : static PLDHashTable sJSObjWrappers;
78 :
79 : // Hash of NPObject wrappers that wrap NPObjects as JSObjects.
80 : static PLDHashTable sNPObjWrappers;
81 :
82 : // Global wrapper count. This includes JSObject wrappers *and*
83 : // NPObject wrappers. When this count goes to zero, there are no more
84 : // wrappers and we can kill off hash tables etc.
85 : static PRInt32 sWrapperCount;
86 :
87 : // The JSRuntime. Used to unroot JSObjects when no JSContext is
88 : // reachable.
89 : static JSRuntime *sJSRuntime;
90 :
91 : // The JS context stack, we use this to push a plugin's JSContext onto
92 : // while executing JS on the context.
93 : static nsIJSContextStack *sContextStack;
94 :
95 : static nsTArray<NPObject*>* sDelayedReleases;
96 :
97 : namespace {
98 :
99 : inline bool
100 0 : NPObjectIsOutOfProcessProxy(NPObject *obj)
101 : {
102 0 : return obj->_class == PluginScriptableObjectParent::GetClass();
103 : }
104 :
105 : } // anonymous namespace
106 :
107 : // Helper class that reports any JS exceptions that were thrown while
108 : // the plugin executed JS.
109 :
110 : class AutoJSExceptionReporter
111 : {
112 : public:
113 0 : AutoJSExceptionReporter(JSContext *cx)
114 0 : : mCx(cx)
115 : {
116 0 : }
117 :
118 0 : ~AutoJSExceptionReporter()
119 : {
120 0 : JS_ReportPendingException(mCx);
121 0 : }
122 :
123 : protected:
124 : JSContext *mCx;
125 : };
126 :
127 :
128 : NPClass nsJSObjWrapper::sJSObjWrapperNPClass =
129 : {
130 : NP_CLASS_STRUCT_VERSION,
131 : nsJSObjWrapper::NP_Allocate,
132 : nsJSObjWrapper::NP_Deallocate,
133 : nsJSObjWrapper::NP_Invalidate,
134 : nsJSObjWrapper::NP_HasMethod,
135 : nsJSObjWrapper::NP_Invoke,
136 : nsJSObjWrapper::NP_InvokeDefault,
137 : nsJSObjWrapper::NP_HasProperty,
138 : nsJSObjWrapper::NP_GetProperty,
139 : nsJSObjWrapper::NP_SetProperty,
140 : nsJSObjWrapper::NP_RemoveProperty,
141 : nsJSObjWrapper::NP_Enumerate,
142 : nsJSObjWrapper::NP_Construct
143 : };
144 :
145 : static JSBool
146 : NPObjWrapper_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
147 :
148 : static JSBool
149 : NPObjWrapper_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
150 :
151 : static JSBool
152 : NPObjWrapper_SetProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp);
153 :
154 : static JSBool
155 : NPObjWrapper_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
156 :
157 : static JSBool
158 : NPObjWrapper_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
159 : jsval *statep, jsid *idp);
160 :
161 : static JSBool
162 : NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
163 : JSObject **objp);
164 :
165 : static JSBool
166 : NPObjWrapper_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
167 :
168 : static void
169 : NPObjWrapper_Finalize(JSContext *cx, JSObject *obj);
170 :
171 : static JSBool
172 : NPObjWrapper_Call(JSContext *cx, unsigned argc, jsval *vp);
173 :
174 : static JSBool
175 : NPObjWrapper_Construct(JSContext *cx, unsigned argc, jsval *vp);
176 :
177 : static JSBool
178 : CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject *npobj,
179 : jsid id, NPVariant* getPropertyResult, jsval *vp);
180 :
181 : static JSClass sNPObjectJSWrapperClass =
182 : {
183 : NPRUNTIME_JSCLASS_NAME,
184 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE,
185 : NPObjWrapper_AddProperty, NPObjWrapper_DelProperty,
186 : NPObjWrapper_GetProperty, NPObjWrapper_SetProperty,
187 : (JSEnumerateOp)NPObjWrapper_newEnumerate,
188 : (JSResolveOp)NPObjWrapper_NewResolve, NPObjWrapper_Convert,
189 : NPObjWrapper_Finalize, nsnull, NPObjWrapper_Call,
190 : NPObjWrapper_Construct
191 : };
192 :
193 : typedef struct NPObjectMemberPrivate {
194 : JSObject *npobjWrapper;
195 : jsval fieldValue;
196 : NPIdentifier methodName;
197 : NPP npp;
198 : } NPObjectMemberPrivate;
199 :
200 : static JSBool
201 : NPObjectMember_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
202 :
203 : static void
204 : NPObjectMember_Finalize(JSContext *cx, JSObject *obj);
205 :
206 : static JSBool
207 : NPObjectMember_Call(JSContext *cx, unsigned argc, jsval *vp);
208 :
209 : static void
210 : NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
211 :
212 : static JSClass sNPObjectMemberClass =
213 : {
214 : "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
215 : JS_PropertyStub, JS_PropertyStub,
216 : JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub,
217 : JS_ResolveStub, NPObjectMember_Convert,
218 : NPObjectMember_Finalize, nsnull, NPObjectMember_Call,
219 : nsnull, nsnull, NPObjectMember_Trace
220 : };
221 :
222 : static void
223 : OnWrapperDestroyed();
224 :
225 : static void
226 0 : DelayedReleaseGCCallback(JSRuntime* rt, JSGCStatus status)
227 : {
228 0 : if (JSGC_END == status) {
229 : // Take ownership of sDelayedReleases and null it out now. The
230 : // _releaseobject call below can reenter GC and double-free these objects.
231 0 : nsAutoPtr<nsTArray<NPObject*> > delayedReleases(sDelayedReleases);
232 0 : sDelayedReleases = nsnull;
233 :
234 0 : if (delayedReleases) {
235 0 : for (PRUint32 i = 0; i < delayedReleases->Length(); ++i) {
236 0 : NPObject* obj = (*delayedReleases)[i];
237 0 : if (obj)
238 0 : _releaseobject(obj);
239 0 : OnWrapperDestroyed();
240 : }
241 : }
242 : }
243 0 : }
244 :
245 : static void
246 0 : OnWrapperCreated()
247 : {
248 0 : if (sWrapperCount++ == 0) {
249 : static const char rtsvc_id[] = "@mozilla.org/js/xpc/RuntimeService;1";
250 0 : nsCOMPtr<nsIJSRuntimeService> rtsvc(do_GetService(rtsvc_id));
251 0 : if (!rtsvc)
252 : return;
253 :
254 0 : rtsvc->GetRuntime(&sJSRuntime);
255 0 : NS_ASSERTION(sJSRuntime != nsnull, "no JSRuntime?!");
256 :
257 : // Register our GC callback to perform delayed destruction of finalized
258 : // NPObjects. Leave this callback around and don't ever unregister it.
259 0 : rtsvc->RegisterGCCallback(DelayedReleaseGCCallback);
260 :
261 0 : CallGetService("@mozilla.org/js/xpc/ContextStack;1", &sContextStack);
262 : }
263 : }
264 :
265 : static void
266 0 : OnWrapperDestroyed()
267 : {
268 0 : NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
269 :
270 0 : if (--sWrapperCount == 0) {
271 0 : if (sJSObjWrappers.ops) {
272 0 : NS_ASSERTION(sJSObjWrappers.entryCount == 0, "Uh, hash not empty?");
273 :
274 : // No more wrappers, and our hash was initialized. Finish the
275 : // hash to prevent leaking it.
276 0 : PL_DHashTableFinish(&sJSObjWrappers);
277 :
278 0 : sJSObjWrappers.ops = nsnull;
279 : }
280 :
281 0 : if (sNPObjWrappers.ops) {
282 0 : NS_ASSERTION(sNPObjWrappers.entryCount == 0, "Uh, hash not empty?");
283 :
284 : // No more wrappers, and our hash was initialized. Finish the
285 : // hash to prevent leaking it.
286 0 : PL_DHashTableFinish(&sNPObjWrappers);
287 :
288 0 : sNPObjWrappers.ops = nsnull;
289 : }
290 :
291 : // No more need for this.
292 0 : sJSRuntime = nsnull;
293 :
294 0 : NS_IF_RELEASE(sContextStack);
295 : }
296 0 : }
297 :
298 : struct AutoCXPusher
299 : {
300 0 : AutoCXPusher(JSContext *cx)
301 : {
302 : // Precondition explaining why we don't need to worry about errors
303 : // in OnWrapperCreated.
304 0 : NS_PRECONDITION(sWrapperCount > 0,
305 : "must have live wrappers when using AutoCXPusher");
306 :
307 : // Call OnWrapperCreated and OnWrapperDestroyed to ensure that the
308 : // last OnWrapperDestroyed doesn't happen while we're on the stack
309 : // and null out sContextStack.
310 0 : OnWrapperCreated();
311 :
312 0 : sContextStack->Push(cx);
313 0 : }
314 :
315 0 : ~AutoCXPusher()
316 : {
317 0 : JSContext *cx = nsnull;
318 0 : sContextStack->Pop(&cx);
319 :
320 0 : JSContext *currentCx = nsnull;
321 0 : sContextStack->Peek(¤tCx);
322 :
323 0 : if (!currentCx) {
324 : // No JS is running, tell the context we're done executing
325 : // script.
326 :
327 0 : nsIScriptContext *scx = GetScriptContextFromJSContext(cx);
328 :
329 0 : if (scx) {
330 0 : scx->ScriptEvaluated(true);
331 : }
332 : }
333 :
334 0 : OnWrapperDestroyed();
335 0 : }
336 : };
337 :
338 : namespace mozilla {
339 : namespace plugins {
340 : namespace parent {
341 :
342 : JSContext *
343 0 : GetJSContext(NPP npp)
344 : {
345 0 : NS_ENSURE_TRUE(npp, nsnull);
346 :
347 0 : nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
348 0 : NS_ENSURE_TRUE(inst, nsnull);
349 :
350 0 : nsCOMPtr<nsIPluginInstanceOwner> owner;
351 0 : inst->GetOwner(getter_AddRefs(owner));
352 0 : NS_ENSURE_TRUE(owner, nsnull);
353 :
354 0 : nsCOMPtr<nsIDocument> doc;
355 0 : owner->GetDocument(getter_AddRefs(doc));
356 0 : NS_ENSURE_TRUE(doc, nsnull);
357 :
358 0 : nsCOMPtr<nsISupports> documentContainer = doc->GetContainer();
359 0 : nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(documentContainer));
360 0 : NS_ENSURE_TRUE(sgo, nsnull);
361 :
362 0 : nsIScriptContext *scx = sgo->GetContext();
363 0 : NS_ENSURE_TRUE(scx, nsnull);
364 :
365 0 : return scx->GetNativeContext();
366 : }
367 :
368 : }
369 : }
370 : }
371 :
372 : static NPP
373 : LookupNPP(NPObject *npobj);
374 :
375 :
376 : static jsval
377 0 : NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant)
378 : {
379 0 : switch (variant->type) {
380 : case NPVariantType_Void :
381 0 : return JSVAL_VOID;
382 : case NPVariantType_Null :
383 0 : return JSVAL_NULL;
384 : case NPVariantType_Bool :
385 0 : return BOOLEAN_TO_JSVAL(NPVARIANT_TO_BOOLEAN(*variant));
386 : case NPVariantType_Int32 :
387 : {
388 : // Don't use INT_TO_JSVAL directly to prevent bugs when dealing
389 : // with ints larger than what fits in a integer jsval.
390 : jsval val;
391 0 : if (::JS_NewNumberValue(cx, NPVARIANT_TO_INT32(*variant), &val)) {
392 0 : return val;
393 : }
394 :
395 0 : break;
396 : }
397 : case NPVariantType_Double :
398 : {
399 : jsval val;
400 0 : if (::JS_NewNumberValue(cx, NPVARIANT_TO_DOUBLE(*variant), &val)) {
401 0 : return val;
402 : }
403 :
404 0 : break;
405 : }
406 : case NPVariantType_String :
407 : {
408 0 : const NPString *s = &NPVARIANT_TO_STRING(*variant);
409 0 : NS_ConvertUTF8toUTF16 utf16String(s->UTF8Characters, s->UTF8Length);
410 :
411 : JSString *str =
412 : ::JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar*>
413 0 : (utf16String.get()),
414 0 : utf16String.Length());
415 :
416 0 : if (str) {
417 0 : return STRING_TO_JSVAL(str);
418 : }
419 :
420 0 : break;
421 : }
422 : case NPVariantType_Object:
423 : {
424 0 : if (npp) {
425 : JSObject *obj =
426 0 : nsNPObjWrapper::GetNewOrUsed(npp, cx, NPVARIANT_TO_OBJECT(*variant));
427 :
428 0 : if (obj) {
429 0 : return OBJECT_TO_JSVAL(obj);
430 : }
431 : }
432 :
433 0 : NS_ERROR("Error wrapping NPObject!");
434 :
435 0 : break;
436 : }
437 : default:
438 0 : NS_ERROR("Unknown NPVariant type!");
439 : }
440 :
441 0 : NS_ERROR("Unable to convert NPVariant to jsval!");
442 :
443 0 : return JSVAL_VOID;
444 : }
445 :
446 : bool
447 0 : JSValToNPVariant(NPP npp, JSContext *cx, jsval val, NPVariant *variant)
448 : {
449 0 : NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
450 :
451 0 : if (JSVAL_IS_PRIMITIVE(val)) {
452 0 : if (val == JSVAL_VOID) {
453 0 : VOID_TO_NPVARIANT(*variant);
454 0 : } else if (JSVAL_IS_NULL(val)) {
455 0 : NULL_TO_NPVARIANT(*variant);
456 0 : } else if (JSVAL_IS_BOOLEAN(val)) {
457 0 : BOOLEAN_TO_NPVARIANT(JSVAL_TO_BOOLEAN(val), *variant);
458 0 : } else if (JSVAL_IS_INT(val)) {
459 0 : INT32_TO_NPVARIANT(JSVAL_TO_INT(val), *variant);
460 0 : } else if (JSVAL_IS_DOUBLE(val)) {
461 0 : double d = JSVAL_TO_DOUBLE(val);
462 : int i;
463 0 : if (JS_DoubleIsInt32(d, &i)) {
464 0 : INT32_TO_NPVARIANT(i, *variant);
465 : } else {
466 0 : DOUBLE_TO_NPVARIANT(d, *variant);
467 : }
468 0 : } else if (JSVAL_IS_STRING(val)) {
469 0 : JSString *jsstr = JSVAL_TO_STRING(val);
470 : size_t length;
471 0 : const jschar *chars = ::JS_GetStringCharsZAndLength(cx, jsstr, &length);
472 0 : if (!chars) {
473 0 : return false;
474 : }
475 :
476 0 : nsDependentString str(chars, length);
477 :
478 : PRUint32 len;
479 0 : char *p = ToNewUTF8String(str, &len);
480 :
481 0 : if (!p) {
482 0 : return false;
483 : }
484 :
485 0 : STRINGN_TO_NPVARIANT(p, len, *variant);
486 : } else {
487 0 : NS_ERROR("Unknown primitive type!");
488 :
489 0 : return false;
490 : }
491 :
492 0 : return true;
493 : }
494 :
495 : NPObject *npobj =
496 0 : nsJSObjWrapper::GetNewOrUsed(npp, cx, JSVAL_TO_OBJECT(val));
497 0 : if (!npobj) {
498 0 : return false;
499 : }
500 :
501 : // Pass over ownership of npobj to *variant
502 0 : OBJECT_TO_NPVARIANT(npobj, *variant);
503 :
504 0 : return true;
505 : }
506 :
507 : static void
508 0 : ThrowJSException(JSContext *cx, const char *message)
509 : {
510 0 : const char *ex = PeekException();
511 :
512 0 : if (ex) {
513 0 : nsAutoString ucex;
514 :
515 0 : if (message) {
516 0 : AppendASCIItoUTF16(message, ucex);
517 :
518 0 : AppendASCIItoUTF16(" [plugin exception: ", ucex);
519 : }
520 :
521 0 : AppendUTF8toUTF16(ex, ucex);
522 :
523 0 : if (message) {
524 0 : AppendASCIItoUTF16("].", ucex);
525 : }
526 :
527 0 : JSString *str = ::JS_NewUCStringCopyN(cx, (jschar *)ucex.get(),
528 0 : ucex.Length());
529 :
530 0 : if (str) {
531 0 : ::JS_SetPendingException(cx, STRING_TO_JSVAL(str));
532 : }
533 :
534 0 : PopException();
535 : } else {
536 0 : ::JS_ReportError(cx, message);
537 : }
538 0 : }
539 :
540 : static JSBool
541 0 : ReportExceptionIfPending(JSContext *cx)
542 : {
543 0 : const char *ex = PeekException();
544 :
545 0 : if (!ex) {
546 0 : return JS_TRUE;
547 : }
548 :
549 0 : ThrowJSException(cx, nsnull);
550 :
551 0 : return JS_FALSE;
552 : }
553 :
554 :
555 0 : nsJSObjWrapper::nsJSObjWrapper(NPP npp)
556 0 : : nsJSObjWrapperKey(nsnull, npp)
557 : {
558 0 : MOZ_COUNT_CTOR(nsJSObjWrapper);
559 0 : OnWrapperCreated();
560 0 : }
561 :
562 0 : nsJSObjWrapper::~nsJSObjWrapper()
563 : {
564 0 : MOZ_COUNT_DTOR(nsJSObjWrapper);
565 :
566 : // Invalidate first, since it relies on sJSRuntime and sJSObjWrappers.
567 0 : NP_Invalidate(this);
568 :
569 0 : OnWrapperDestroyed();
570 0 : }
571 :
572 : // static
573 : NPObject *
574 0 : nsJSObjWrapper::NP_Allocate(NPP npp, NPClass *aClass)
575 : {
576 0 : NS_ASSERTION(aClass == &sJSObjWrapperNPClass,
577 : "Huh, wrong class passed to NP_Allocate()!!!");
578 :
579 0 : return new nsJSObjWrapper(npp);
580 : }
581 :
582 : // static
583 : void
584 0 : nsJSObjWrapper::NP_Deallocate(NPObject *npobj)
585 : {
586 : // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate().
587 0 : delete (nsJSObjWrapper *)npobj;
588 0 : }
589 :
590 : // static
591 : void
592 0 : nsJSObjWrapper::NP_Invalidate(NPObject *npobj)
593 : {
594 0 : nsJSObjWrapper *jsnpobj = (nsJSObjWrapper *)npobj;
595 :
596 0 : if (jsnpobj && jsnpobj->mJSObj) {
597 : // Unroot the object's JSObject
598 0 : js_RemoveRoot(sJSRuntime, &jsnpobj->mJSObj);
599 :
600 0 : if (sJSObjWrappers.ops) {
601 : // Remove the wrapper from the hash
602 :
603 0 : nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
604 0 : PL_DHashTableOperate(&sJSObjWrappers, &key, PL_DHASH_REMOVE);
605 : }
606 :
607 : // Forget our reference to the JSObject.
608 0 : jsnpobj->mJSObj = nsnull;
609 : }
610 0 : }
611 :
612 : static JSBool
613 0 : GetProperty(JSContext *cx, JSObject *obj, NPIdentifier id, jsval *rval)
614 : {
615 0 : NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id),
616 : "id must be either string or int!\n");
617 0 : return ::JS_GetPropertyById(cx, obj, NPIdentifierToJSId(id), rval);
618 : }
619 :
620 : // static
621 : bool
622 0 : nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id)
623 : {
624 0 : NPP npp = NPPStack::Peek();
625 0 : JSContext *cx = GetJSContext(npp);
626 :
627 0 : if (!cx) {
628 0 : return false;
629 : }
630 :
631 0 : if (!npobj) {
632 : ThrowJSException(cx,
633 0 : "Null npobj in nsJSObjWrapper::NP_HasMethod!");
634 :
635 0 : return false;
636 : }
637 :
638 0 : nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
639 :
640 0 : AutoCXPusher pusher(cx);
641 0 : JSAutoRequest ar(cx);
642 0 : JSAutoEnterCompartment ac;
643 :
644 0 : if (!ac.enter(cx, npjsobj->mJSObj))
645 0 : return false;
646 :
647 0 : AutoJSExceptionReporter reporter(cx);
648 :
649 : jsval v;
650 0 : JSBool ok = GetProperty(cx, npjsobj->mJSObj, id, &v);
651 :
652 0 : return ok && !JSVAL_IS_PRIMITIVE(v) &&
653 0 : ::JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v));
654 : }
655 :
656 : static bool
657 0 : doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args,
658 : uint32_t argCount, bool ctorCall, NPVariant *result)
659 : {
660 0 : NPP npp = NPPStack::Peek();
661 0 : JSContext *cx = GetJSContext(npp);
662 :
663 0 : if (!cx) {
664 0 : return false;
665 : }
666 :
667 0 : if (!npobj || !result) {
668 0 : ThrowJSException(cx, "Null npobj, or result in doInvoke!");
669 :
670 0 : return false;
671 : }
672 :
673 : // Initialize *result
674 0 : VOID_TO_NPVARIANT(*result);
675 :
676 0 : nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
677 : jsval fv;
678 :
679 0 : AutoCXPusher pusher(cx);
680 0 : JSAutoRequest ar(cx);
681 0 : JSAutoEnterCompartment ac;
682 :
683 0 : if (!ac.enter(cx, npjsobj->mJSObj))
684 0 : return false;
685 :
686 0 : AutoJSExceptionReporter reporter(cx);
687 :
688 0 : if (method != NPIdentifier_VOID) {
689 0 : if (!GetProperty(cx, npjsobj->mJSObj, method, &fv) ||
690 0 : ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
691 0 : return false;
692 : }
693 : } else {
694 0 : fv = OBJECT_TO_JSVAL(npjsobj->mJSObj);
695 : }
696 :
697 : jsval jsargs_buf[8];
698 0 : jsval *jsargs = jsargs_buf;
699 :
700 0 : if (argCount > (sizeof(jsargs_buf) / sizeof(jsval))) {
701 : // Our stack buffer isn't large enough to hold all arguments,
702 : // malloc a buffer.
703 0 : jsargs = (jsval *)PR_Malloc(argCount * sizeof(jsval));
704 0 : if (!jsargs) {
705 0 : ::JS_ReportOutOfMemory(cx);
706 :
707 0 : return false;
708 : }
709 : }
710 :
711 : jsval v;
712 : JSBool ok;
713 :
714 : {
715 0 : JS::AutoArrayRooter tvr(cx, 0, jsargs);
716 :
717 : // Convert args
718 0 : for (PRUint32 i = 0; i < argCount; ++i) {
719 0 : jsargs[i] = NPVariantToJSVal(npp, cx, args + i);
720 0 : tvr.changeLength(i + 1);
721 : }
722 :
723 0 : if (ctorCall) {
724 : JSObject *newObj =
725 0 : ::JS_New(cx, npjsobj->mJSObj, argCount, jsargs);
726 :
727 0 : if (newObj) {
728 0 : v = OBJECT_TO_JSVAL(newObj);
729 0 : ok = JS_TRUE;
730 : } else {
731 0 : ok = JS_FALSE;
732 : }
733 : } else {
734 0 : ok = ::JS_CallFunctionValue(cx, npjsobj->mJSObj, fv, argCount, jsargs, &v);
735 : }
736 :
737 : }
738 :
739 0 : if (jsargs != jsargs_buf)
740 0 : PR_Free(jsargs);
741 :
742 0 : if (ok)
743 0 : ok = JSValToNPVariant(npp, cx, v, result);
744 :
745 : // return ok == JS_TRUE to quiet down compiler warning, even if
746 : // return ok is what we really want.
747 0 : return ok == JS_TRUE;
748 : }
749 :
750 : // static
751 : bool
752 0 : nsJSObjWrapper::NP_Invoke(NPObject *npobj, NPIdentifier method,
753 : const NPVariant *args, uint32_t argCount,
754 : NPVariant *result)
755 : {
756 0 : if (method == NPIdentifier_VOID) {
757 0 : return false;
758 : }
759 :
760 0 : return doInvoke(npobj, method, args, argCount, false, result);
761 : }
762 :
763 : // static
764 : bool
765 0 : nsJSObjWrapper::NP_InvokeDefault(NPObject *npobj, const NPVariant *args,
766 : uint32_t argCount, NPVariant *result)
767 : {
768 : return doInvoke(npobj, NPIdentifier_VOID, args, argCount, false,
769 0 : result);
770 : }
771 :
772 : // static
773 : bool
774 0 : nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier id)
775 : {
776 0 : NPP npp = NPPStack::Peek();
777 0 : JSContext *cx = GetJSContext(npp);
778 :
779 0 : if (!cx) {
780 0 : return false;
781 : }
782 :
783 0 : if (!npobj) {
784 : ThrowJSException(cx,
785 0 : "Null npobj in nsJSObjWrapper::NP_HasProperty!");
786 :
787 0 : return false;
788 : }
789 :
790 0 : nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
791 0 : JSBool found, ok = JS_FALSE;
792 :
793 0 : AutoCXPusher pusher(cx);
794 0 : JSAutoRequest ar(cx);
795 0 : AutoJSExceptionReporter reporter(cx);
796 0 : JSAutoEnterCompartment ac;
797 :
798 0 : if (!ac.enter(cx, npjsobj->mJSObj))
799 0 : return false;
800 :
801 0 : NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id),
802 : "id must be either string or int!\n");
803 0 : ok = ::JS_HasPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &found);
804 0 : return ok && found;
805 : }
806 :
807 : // static
808 : bool
809 0 : nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier id,
810 : NPVariant *result)
811 : {
812 0 : NPP npp = NPPStack::Peek();
813 0 : JSContext *cx = GetJSContext(npp);
814 :
815 0 : if (!cx) {
816 0 : return false;
817 : }
818 :
819 0 : if (!npobj) {
820 : ThrowJSException(cx,
821 0 : "Null npobj in nsJSObjWrapper::NP_GetProperty!");
822 :
823 0 : return false;
824 : }
825 :
826 0 : nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
827 :
828 0 : AutoCXPusher pusher(cx);
829 0 : JSAutoRequest ar(cx);
830 0 : AutoJSExceptionReporter reporter(cx);
831 0 : JSAutoEnterCompartment ac;
832 :
833 0 : if (!ac.enter(cx, npjsobj->mJSObj))
834 0 : return false;
835 :
836 : jsval v;
837 0 : return (GetProperty(cx, npjsobj->mJSObj, id, &v) &&
838 0 : JSValToNPVariant(npp, cx, v, result));
839 : }
840 :
841 : // static
842 : bool
843 0 : nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier id,
844 : const NPVariant *value)
845 : {
846 0 : NPP npp = NPPStack::Peek();
847 0 : JSContext *cx = GetJSContext(npp);
848 :
849 0 : if (!cx) {
850 0 : return false;
851 : }
852 :
853 0 : if (!npobj) {
854 : ThrowJSException(cx,
855 0 : "Null npobj in nsJSObjWrapper::NP_SetProperty!");
856 :
857 0 : return false;
858 : }
859 :
860 0 : nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
861 0 : JSBool ok = JS_FALSE;
862 :
863 0 : AutoCXPusher pusher(cx);
864 0 : JSAutoRequest ar(cx);
865 0 : AutoJSExceptionReporter reporter(cx);
866 0 : JSAutoEnterCompartment ac;
867 :
868 0 : if (!ac.enter(cx, npjsobj->mJSObj))
869 0 : return false;
870 :
871 0 : jsval v = NPVariantToJSVal(npp, cx, value);
872 0 : JS::AutoValueRooter tvr(cx, v);
873 :
874 0 : NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id),
875 : "id must be either string or int!\n");
876 0 : ok = ::JS_SetPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &v);
877 :
878 : // return ok == JS_TRUE to quiet down compiler warning, even if
879 : // return ok is what we really want.
880 0 : return ok == JS_TRUE;
881 : }
882 :
883 : // static
884 : bool
885 0 : nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier id)
886 : {
887 0 : NPP npp = NPPStack::Peek();
888 0 : JSContext *cx = GetJSContext(npp);
889 :
890 0 : if (!cx) {
891 0 : return false;
892 : }
893 :
894 0 : if (!npobj) {
895 : ThrowJSException(cx,
896 0 : "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
897 :
898 0 : return false;
899 : }
900 :
901 0 : nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
902 0 : JSBool ok = JS_FALSE;
903 :
904 0 : AutoCXPusher pusher(cx);
905 0 : JSAutoRequest ar(cx);
906 0 : AutoJSExceptionReporter reporter(cx);
907 0 : jsval deleted = JSVAL_FALSE;
908 0 : JSAutoEnterCompartment ac;
909 :
910 0 : if (!ac.enter(cx, npjsobj->mJSObj))
911 0 : return false;
912 :
913 0 : NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id),
914 : "id must be either string or int!\n");
915 0 : ok = ::JS_DeletePropertyById2(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &deleted);
916 0 : if (ok && deleted == JSVAL_TRUE) {
917 : // FIXME: See bug 425823, we shouldn't need to do this, and once
918 : // that bug is fixed we can remove this code.
919 :
920 : JSBool hasProp;
921 0 : ok = ::JS_HasPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &hasProp);
922 :
923 0 : if (ok && hasProp) {
924 : // The property might have been deleted, but it got
925 : // re-resolved, so no, it's not really deleted.
926 :
927 0 : deleted = JSVAL_FALSE;
928 : }
929 : }
930 :
931 : // return ok == JS_TRUE to quiet down compiler warning, even if
932 : // return ok is what we really want.
933 0 : return ok == JS_TRUE && deleted == JSVAL_TRUE;
934 : }
935 :
936 : //static
937 : bool
938 0 : nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray,
939 : uint32_t *count)
940 : {
941 0 : NPP npp = NPPStack::Peek();
942 0 : JSContext *cx = GetJSContext(npp);
943 :
944 0 : *idarray = 0;
945 0 : *count = 0;
946 :
947 0 : if (!cx) {
948 0 : return false;
949 : }
950 :
951 0 : if (!npobj) {
952 : ThrowJSException(cx,
953 0 : "Null npobj in nsJSObjWrapper::NP_Enumerate!");
954 :
955 0 : return false;
956 : }
957 :
958 0 : nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
959 :
960 0 : AutoCXPusher pusher(cx);
961 0 : JSAutoRequest ar(cx);
962 0 : AutoJSExceptionReporter reporter(cx);
963 0 : JSAutoEnterCompartment ac;
964 :
965 0 : if (!ac.enter(cx, npjsobj->mJSObj))
966 0 : return false;
967 :
968 0 : JS::AutoIdArray ida(cx, JS_Enumerate(cx, npjsobj->mJSObj));
969 0 : if (!ida) {
970 0 : return false;
971 : }
972 :
973 0 : *count = ida.length();
974 0 : *idarray = (NPIdentifier *)PR_Malloc(*count * sizeof(NPIdentifier));
975 0 : if (!*idarray) {
976 0 : ThrowJSException(cx, "Memory allocation failed for NPIdentifier!");
977 0 : return false;
978 : }
979 :
980 0 : for (PRUint32 i = 0; i < *count; i++) {
981 : jsval v;
982 0 : if (!JS_IdToValue(cx, ida[i], &v)) {
983 0 : PR_Free(*idarray);
984 0 : return false;
985 : }
986 :
987 : NPIdentifier id;
988 0 : if (JSVAL_IS_STRING(v)) {
989 0 : JSString *str = JS_InternJSString(cx, JSVAL_TO_STRING(v));
990 0 : if (!str) {
991 0 : PR_Free(*idarray);
992 0 : return false;
993 : }
994 0 : id = StringToNPIdentifier(cx, str);
995 : } else {
996 0 : NS_ASSERTION(JSVAL_IS_INT(v),
997 : "The element in ida must be either string or int!\n");
998 0 : id = IntToNPIdentifier(JSVAL_TO_INT(v));
999 : }
1000 :
1001 0 : (*idarray)[i] = id;
1002 : }
1003 :
1004 0 : return true;
1005 : }
1006 :
1007 : //static
1008 : bool
1009 0 : nsJSObjWrapper::NP_Construct(NPObject *npobj, const NPVariant *args,
1010 : uint32_t argCount, NPVariant *result)
1011 : {
1012 0 : return doInvoke(npobj, NPIdentifier_VOID, args, argCount, true, result);
1013 : }
1014 :
1015 :
1016 : class JSObjWrapperHashEntry : public PLDHashEntryHdr
1017 : {
1018 : public:
1019 : nsJSObjWrapper *mJSObjWrapper;
1020 : };
1021 :
1022 :
1023 : static PLDHashNumber
1024 0 : JSObjWrapperHash(PLDHashTable *table, const void *key)
1025 : {
1026 0 : const nsJSObjWrapperKey *e = static_cast<const nsJSObjWrapperKey *>(key);
1027 0 : return HashGeneric(e->mJSObj, e->mNpp);
1028 : }
1029 :
1030 : static bool
1031 0 : JSObjWrapperHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
1032 : const void *key)
1033 : {
1034 : const nsJSObjWrapperKey *objWrapperKey =
1035 0 : static_cast<const nsJSObjWrapperKey *>(key);
1036 : const JSObjWrapperHashEntry *e =
1037 0 : static_cast<const JSObjWrapperHashEntry *>(entry);
1038 :
1039 : return (e->mJSObjWrapper->mJSObj == objWrapperKey->mJSObj &&
1040 0 : e->mJSObjWrapper->mNpp == objWrapperKey->mNpp);
1041 : }
1042 :
1043 :
1044 : // Look up or create an NPObject that wraps the JSObject obj.
1045 :
1046 : // static
1047 : NPObject *
1048 0 : nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JSObject *obj)
1049 : {
1050 0 : if (!npp) {
1051 0 : NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
1052 :
1053 0 : return nsnull;
1054 : }
1055 :
1056 0 : if (!cx) {
1057 0 : cx = GetJSContext(npp);
1058 :
1059 0 : if (!cx) {
1060 0 : NS_ERROR("Unable to find a JSContext in nsJSObjWrapper::GetNewOrUsed()!");
1061 :
1062 0 : return nsnull;
1063 : }
1064 : }
1065 :
1066 : // No need to enter the right compartment here as we only get the
1067 : // class and private from the JSObject, neither of which cares about
1068 : // compartments.
1069 :
1070 0 : JSClass *clazz = JS_GetClass(obj);
1071 :
1072 0 : if (clazz == &sNPObjectJSWrapperClass) {
1073 : // obj is one of our own, its private data is the NPObject we're
1074 : // looking for.
1075 :
1076 0 : NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
1077 :
1078 0 : if (LookupNPP(npobj) == npp)
1079 0 : return _retainobject(npobj);
1080 : }
1081 :
1082 0 : if (!sJSObjWrappers.ops) {
1083 : // No hash yet (or any more), initialize it.
1084 :
1085 : static PLDHashTableOps ops =
1086 : {
1087 : PL_DHashAllocTable,
1088 : PL_DHashFreeTable,
1089 : JSObjWrapperHash,
1090 : JSObjWrapperHashMatchEntry,
1091 : PL_DHashMoveEntryStub,
1092 : PL_DHashClearEntryStub,
1093 : PL_DHashFinalizeStub
1094 : };
1095 :
1096 0 : if (!PL_DHashTableInit(&sJSObjWrappers, &ops, nsnull,
1097 0 : sizeof(JSObjWrapperHashEntry), 16)) {
1098 0 : NS_ERROR("Error initializing PLDHashTable!");
1099 :
1100 0 : return nsnull;
1101 : }
1102 : }
1103 :
1104 0 : nsJSObjWrapperKey key(obj, npp);
1105 :
1106 : JSObjWrapperHashEntry *entry = static_cast<JSObjWrapperHashEntry *>
1107 0 : (PL_DHashTableOperate(&sJSObjWrappers, &key, PL_DHASH_ADD));
1108 :
1109 0 : if (!entry) {
1110 : // Out of memory.
1111 0 : return nsnull;
1112 : }
1113 :
1114 0 : if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObjWrapper) {
1115 : // Found a live nsJSObjWrapper, return it.
1116 :
1117 0 : return _retainobject(entry->mJSObjWrapper);
1118 : }
1119 :
1120 : // No existing nsJSObjWrapper, create one.
1121 :
1122 : nsJSObjWrapper *wrapper =
1123 0 : (nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass);
1124 :
1125 0 : if (!wrapper) {
1126 : // OOM? Remove the stale entry from the hash.
1127 :
1128 0 : PL_DHashTableRawRemove(&sJSObjWrappers, entry);
1129 :
1130 0 : return nsnull;
1131 : }
1132 :
1133 0 : wrapper->mJSObj = obj;
1134 :
1135 0 : entry->mJSObjWrapper = wrapper;
1136 :
1137 0 : NS_ASSERTION(wrapper->mNpp == npp, "nsJSObjWrapper::mNpp not initialized!");
1138 :
1139 0 : JSAutoRequest ar(cx);
1140 :
1141 : // Root the JSObject, its lifetime is now tied to that of the
1142 : // NPObject.
1143 0 : if (!::JS_AddNamedObjectRoot(cx, &wrapper->mJSObj, "nsJSObjWrapper::mJSObject")) {
1144 0 : NS_ERROR("Failed to root JSObject!");
1145 :
1146 0 : _releaseobject(wrapper);
1147 :
1148 0 : PL_DHashTableRawRemove(&sJSObjWrappers, entry);
1149 :
1150 0 : return nsnull;
1151 : }
1152 :
1153 0 : return wrapper;
1154 : }
1155 :
1156 : static NPObject *
1157 0 : GetNPObject(JSObject *obj)
1158 : {
1159 0 : while (obj && JS_GetClass(obj) != &sNPObjectJSWrapperClass) {
1160 0 : obj = ::JS_GetPrototype(obj);
1161 : }
1162 :
1163 0 : if (!obj) {
1164 0 : return nsnull;
1165 : }
1166 :
1167 0 : return (NPObject *)::JS_GetPrivate(obj);
1168 : }
1169 :
1170 :
1171 : // Does not actually add a property because this is always followed by a
1172 : // SetProperty call.
1173 : static JSBool
1174 0 : NPObjWrapper_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1175 : {
1176 0 : NPObject *npobj = GetNPObject(obj);
1177 :
1178 0 : if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1179 0 : !npobj->_class->hasMethod) {
1180 0 : ThrowJSException(cx, "Bad NPObject as private data!");
1181 :
1182 0 : return JS_FALSE;
1183 : }
1184 :
1185 0 : if (NPObjectIsOutOfProcessProxy(npobj)) {
1186 0 : return JS_TRUE;
1187 : }
1188 :
1189 0 : PluginDestructionGuard pdg(LookupNPP(npobj));
1190 :
1191 0 : NPIdentifier identifier = JSIdToNPIdentifier(id);
1192 0 : JSBool hasProperty = npobj->_class->hasProperty(npobj, identifier);
1193 0 : if (!ReportExceptionIfPending(cx))
1194 0 : return JS_FALSE;
1195 :
1196 0 : if (hasProperty)
1197 0 : return JS_TRUE;
1198 :
1199 : // We must permit methods here since JS_DefineUCFunction() will add
1200 : // the function as a property
1201 0 : JSBool hasMethod = npobj->_class->hasMethod(npobj, identifier);
1202 0 : if (!ReportExceptionIfPending(cx))
1203 0 : return JS_FALSE;
1204 :
1205 0 : if (!hasMethod) {
1206 0 : ThrowJSException(cx, "Trying to add unsupported property on NPObject!");
1207 :
1208 0 : return JS_FALSE;
1209 : }
1210 :
1211 0 : return JS_TRUE;
1212 : }
1213 :
1214 : static JSBool
1215 0 : NPObjWrapper_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1216 : {
1217 0 : NPObject *npobj = GetNPObject(obj);
1218 :
1219 0 : if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1220 0 : !npobj->_class->removeProperty) {
1221 0 : ThrowJSException(cx, "Bad NPObject as private data!");
1222 :
1223 0 : return JS_FALSE;
1224 : }
1225 :
1226 0 : PluginDestructionGuard pdg(LookupNPP(npobj));
1227 :
1228 0 : NPIdentifier identifier = JSIdToNPIdentifier(id);
1229 :
1230 0 : if (!NPObjectIsOutOfProcessProxy(npobj)) {
1231 0 : JSBool hasProperty = npobj->_class->hasProperty(npobj, identifier);
1232 0 : if (!ReportExceptionIfPending(cx))
1233 0 : return JS_FALSE;
1234 :
1235 0 : if (!hasProperty)
1236 0 : return JS_TRUE;
1237 : }
1238 :
1239 0 : if (!npobj->_class->removeProperty(npobj, identifier))
1240 0 : *vp = JSVAL_FALSE;
1241 :
1242 0 : return ReportExceptionIfPending(cx);
1243 : }
1244 :
1245 : static JSBool
1246 0 : NPObjWrapper_SetProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
1247 : {
1248 0 : NPObject *npobj = GetNPObject(obj);
1249 :
1250 0 : if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1251 0 : !npobj->_class->setProperty) {
1252 0 : ThrowJSException(cx, "Bad NPObject as private data!");
1253 :
1254 0 : return JS_FALSE;
1255 : }
1256 :
1257 : // Find out what plugin (NPP) is the owner of the object we're
1258 : // manipulating, and make it own any JSObject wrappers created here.
1259 0 : NPP npp = LookupNPP(npobj);
1260 :
1261 0 : if (!npp) {
1262 0 : ThrowJSException(cx, "No NPP found for NPObject!");
1263 :
1264 0 : return JS_FALSE;
1265 : }
1266 :
1267 0 : PluginDestructionGuard pdg(npp);
1268 :
1269 0 : NPIdentifier identifier = JSIdToNPIdentifier(id);
1270 :
1271 0 : if (!NPObjectIsOutOfProcessProxy(npobj)) {
1272 0 : JSBool hasProperty = npobj->_class->hasProperty(npobj, identifier);
1273 0 : if (!ReportExceptionIfPending(cx))
1274 0 : return JS_FALSE;
1275 :
1276 0 : if (!hasProperty) {
1277 0 : ThrowJSException(cx, "Trying to set unsupported property on NPObject!");
1278 :
1279 0 : return JS_FALSE;
1280 : }
1281 : }
1282 :
1283 : NPVariant npv;
1284 0 : if (!JSValToNPVariant(npp, cx, *vp, &npv)) {
1285 0 : ThrowJSException(cx, "Error converting jsval to NPVariant!");
1286 :
1287 0 : return JS_FALSE;
1288 : }
1289 :
1290 0 : JSBool ok = npobj->_class->setProperty(npobj, identifier, &npv);
1291 0 : _releasevariantvalue(&npv); // Release the variant
1292 0 : if (!ReportExceptionIfPending(cx))
1293 0 : return JS_FALSE;
1294 :
1295 0 : if (!ok) {
1296 0 : ThrowJSException(cx, "Error setting property on NPObject!");
1297 :
1298 0 : return JS_FALSE;
1299 : }
1300 :
1301 0 : return JS_TRUE;
1302 : }
1303 :
1304 : static JSBool
1305 0 : NPObjWrapper_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1306 : {
1307 0 : NPObject *npobj = GetNPObject(obj);
1308 :
1309 0 : if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1310 0 : !npobj->_class->hasMethod || !npobj->_class->getProperty) {
1311 0 : ThrowJSException(cx, "Bad NPObject as private data!");
1312 :
1313 0 : return JS_FALSE;
1314 : }
1315 :
1316 : // Find out what plugin (NPP) is the owner of the object we're
1317 : // manipulating, and make it own any JSObject wrappers created here.
1318 0 : NPP npp = LookupNPP(npobj);
1319 0 : if (!npp) {
1320 0 : ThrowJSException(cx, "No NPP found for NPObject!");
1321 :
1322 0 : return JS_FALSE;
1323 : }
1324 :
1325 0 : PluginDestructionGuard pdg(npp);
1326 :
1327 : bool hasProperty, hasMethod;
1328 :
1329 : NPVariant npv;
1330 0 : VOID_TO_NPVARIANT(npv);
1331 :
1332 0 : NPIdentifier identifier = JSIdToNPIdentifier(id);
1333 :
1334 0 : if (NPObjectIsOutOfProcessProxy(npobj)) {
1335 : PluginScriptableObjectParent* actor =
1336 0 : static_cast<ParentNPObject*>(npobj)->parent;
1337 :
1338 : // actor may be null if the plugin crashed.
1339 0 : if (!actor)
1340 0 : return JS_FALSE;
1341 :
1342 : JSBool success = actor->GetPropertyHelper(identifier, &hasProperty,
1343 0 : &hasMethod, &npv);
1344 0 : if (!ReportExceptionIfPending(cx)) {
1345 0 : if (success)
1346 0 : _releasevariantvalue(&npv);
1347 0 : return JS_FALSE;
1348 : }
1349 :
1350 0 : if (success) {
1351 : // We return NPObject Member class here to support ambiguous members.
1352 0 : if (hasProperty && hasMethod)
1353 0 : return CreateNPObjectMember(npp, cx, obj, npobj, id, &npv, vp);
1354 :
1355 0 : if (hasProperty) {
1356 0 : *vp = NPVariantToJSVal(npp, cx, &npv);
1357 0 : _releasevariantvalue(&npv);
1358 :
1359 0 : if (!ReportExceptionIfPending(cx))
1360 0 : return JS_FALSE;
1361 : }
1362 : }
1363 0 : return JS_TRUE;
1364 : }
1365 :
1366 0 : hasProperty = npobj->_class->hasProperty(npobj, identifier);
1367 0 : if (!ReportExceptionIfPending(cx))
1368 0 : return JS_FALSE;
1369 :
1370 0 : hasMethod = npobj->_class->hasMethod(npobj, identifier);
1371 0 : if (!ReportExceptionIfPending(cx))
1372 0 : return JS_FALSE;
1373 :
1374 : // We return NPObject Member class here to support ambiguous members.
1375 0 : if (hasProperty && hasMethod)
1376 0 : return CreateNPObjectMember(npp, cx, obj, npobj, id, nsnull, vp);
1377 :
1378 0 : if (hasProperty) {
1379 0 : if (npobj->_class->getProperty(npobj, identifier, &npv))
1380 0 : *vp = NPVariantToJSVal(npp, cx, &npv);
1381 :
1382 0 : _releasevariantvalue(&npv);
1383 :
1384 0 : if (!ReportExceptionIfPending(cx))
1385 0 : return JS_FALSE;
1386 : }
1387 :
1388 0 : return JS_TRUE;
1389 : }
1390 :
1391 : static JSBool
1392 0 : CallNPMethodInternal(JSContext *cx, JSObject *obj, unsigned argc, jsval *argv,
1393 : jsval *rval, bool ctorCall)
1394 : {
1395 0 : while (obj && JS_GetClass(obj) != &sNPObjectJSWrapperClass) {
1396 0 : obj = ::JS_GetPrototype(obj);
1397 : }
1398 :
1399 0 : if (!obj) {
1400 0 : ThrowJSException(cx, "NPMethod called on non-NPObject wrapped JSObject!");
1401 :
1402 0 : return JS_FALSE;
1403 : }
1404 :
1405 0 : NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
1406 :
1407 0 : if (!npobj || !npobj->_class) {
1408 0 : ThrowJSException(cx, "Bad NPObject as private data!");
1409 :
1410 0 : return JS_FALSE;
1411 : }
1412 :
1413 : // Find out what plugin (NPP) is the owner of the object we're
1414 : // manipulating, and make it own any JSObject wrappers created here.
1415 0 : NPP npp = LookupNPP(npobj);
1416 :
1417 0 : if (!npp) {
1418 0 : ThrowJSException(cx, "Error finding NPP for NPObject!");
1419 :
1420 0 : return JS_FALSE;
1421 : }
1422 :
1423 0 : PluginDestructionGuard pdg(npp);
1424 :
1425 : NPVariant npargs_buf[8];
1426 0 : NPVariant *npargs = npargs_buf;
1427 :
1428 0 : if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
1429 : // Our stack buffer isn't large enough to hold all arguments,
1430 : // malloc a buffer.
1431 0 : npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
1432 :
1433 0 : if (!npargs) {
1434 0 : ThrowJSException(cx, "Out of memory!");
1435 :
1436 0 : return JS_FALSE;
1437 : }
1438 : }
1439 :
1440 : // Convert arguments
1441 : PRUint32 i;
1442 0 : for (i = 0; i < argc; ++i) {
1443 0 : if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) {
1444 0 : ThrowJSException(cx, "Error converting jsvals to NPVariants!");
1445 :
1446 0 : if (npargs != npargs_buf) {
1447 0 : PR_Free(npargs);
1448 : }
1449 :
1450 0 : return JS_FALSE;
1451 : }
1452 : }
1453 :
1454 : NPVariant v;
1455 0 : VOID_TO_NPVARIANT(v);
1456 :
1457 0 : JSObject *funobj = JSVAL_TO_OBJECT(argv[-2]);
1458 : JSBool ok;
1459 0 : const char *msg = "Error calling method on NPObject!";
1460 :
1461 0 : if (ctorCall) {
1462 : // construct a new NPObject based on the NPClass in npobj. Fail if
1463 : // no construct method is available.
1464 :
1465 0 : if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) &&
1466 : npobj->_class->construct) {
1467 0 : ok = npobj->_class->construct(npobj, npargs, argc, &v);
1468 : } else {
1469 0 : ok = JS_FALSE;
1470 :
1471 0 : msg = "Attempt to construct object from class with no constructor.";
1472 : }
1473 0 : } else if (funobj != obj) {
1474 : // A obj.function() style call is made, get the method name from
1475 : // the function object.
1476 :
1477 0 : if (npobj->_class->invoke) {
1478 0 : JSFunction *fun = ::JS_GetObjectFunction(funobj);
1479 0 : JSString *name = ::JS_InternJSString(cx, ::JS_GetFunctionId(fun));
1480 0 : NPIdentifier id = StringToNPIdentifier(cx, name);
1481 :
1482 0 : ok = npobj->_class->invoke(npobj, id, npargs, argc, &v);
1483 : } else {
1484 0 : ok = JS_FALSE;
1485 :
1486 0 : msg = "Attempt to call a method on object with no invoke method.";
1487 : }
1488 : } else {
1489 0 : if (npobj->_class->invokeDefault) {
1490 : // obj is a callable object that is being called, no method name
1491 : // available then. Invoke the default method.
1492 :
1493 0 : ok = npobj->_class->invokeDefault(npobj, npargs, argc, &v);
1494 : } else {
1495 0 : ok = JS_FALSE;
1496 :
1497 : msg = "Attempt to call a default method on object with no "
1498 0 : "invokeDefault method.";
1499 : }
1500 : }
1501 :
1502 : // Release arguments.
1503 0 : for (i = 0; i < argc; ++i) {
1504 0 : _releasevariantvalue(npargs + i);
1505 : }
1506 :
1507 0 : if (npargs != npargs_buf) {
1508 0 : PR_Free(npargs);
1509 : }
1510 :
1511 0 : if (!ok) {
1512 : // ReportExceptionIfPending returns a return value, which is JS_TRUE
1513 : // if no exception was thrown. In that case, throw our own.
1514 0 : if (ReportExceptionIfPending(cx))
1515 0 : ThrowJSException(cx, msg);
1516 :
1517 0 : return JS_FALSE;
1518 : }
1519 :
1520 0 : *rval = NPVariantToJSVal(npp, cx, &v);
1521 :
1522 : // *rval now owns the value, release our reference.
1523 0 : _releasevariantvalue(&v);
1524 :
1525 0 : return ReportExceptionIfPending(cx);
1526 : }
1527 :
1528 : static JSBool
1529 0 : CallNPMethod(JSContext *cx, unsigned argc, jsval *vp)
1530 : {
1531 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
1532 0 : if (!obj)
1533 0 : return JS_FALSE;
1534 :
1535 0 : return CallNPMethodInternal(cx, obj, argc, JS_ARGV(cx, vp), vp, false);
1536 : }
1537 :
1538 : struct NPObjectEnumerateState {
1539 : PRUint32 index;
1540 : PRUint32 length;
1541 : NPIdentifier *value;
1542 : };
1543 :
1544 : static JSBool
1545 0 : NPObjWrapper_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1546 : jsval *statep, jsid *idp)
1547 : {
1548 0 : NPObject *npobj = GetNPObject(obj);
1549 : NPIdentifier *enum_value;
1550 : uint32_t length;
1551 : NPObjectEnumerateState *state;
1552 :
1553 0 : if (!npobj || !npobj->_class) {
1554 0 : ThrowJSException(cx, "Bad NPObject as private data!");
1555 0 : return JS_FALSE;
1556 : }
1557 :
1558 0 : PluginDestructionGuard pdg(LookupNPP(npobj));
1559 :
1560 0 : NS_ASSERTION(statep, "Must have a statep to enumerate!");
1561 :
1562 0 : switch(enum_op) {
1563 : case JSENUMERATE_INIT:
1564 : case JSENUMERATE_INIT_ALL:
1565 0 : state = new NPObjectEnumerateState();
1566 0 : if (!state) {
1567 : ThrowJSException(cx, "Memory allocation failed for "
1568 0 : "NPObjectEnumerateState!");
1569 :
1570 0 : return JS_FALSE;
1571 : }
1572 :
1573 0 : if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
1574 0 : !npobj->_class->enumerate) {
1575 0 : enum_value = 0;
1576 0 : length = 0;
1577 0 : } else if (!npobj->_class->enumerate(npobj, &enum_value, &length)) {
1578 : delete state;
1579 :
1580 0 : if (ReportExceptionIfPending(cx)) {
1581 : // ReportExceptionIfPending returns a return value, which is JS_TRUE
1582 : // if no exception was thrown. In that case, throw our own.
1583 : ThrowJSException(cx, "Error enumerating properties on scriptable "
1584 0 : "plugin object");
1585 : }
1586 :
1587 0 : return JS_FALSE;
1588 : }
1589 :
1590 0 : state->value = enum_value;
1591 0 : state->length = length;
1592 0 : state->index = 0;
1593 0 : *statep = PRIVATE_TO_JSVAL(state);
1594 0 : if (idp) {
1595 0 : *idp = INT_TO_JSID(length);
1596 : }
1597 :
1598 0 : break;
1599 :
1600 : case JSENUMERATE_NEXT:
1601 0 : state = (NPObjectEnumerateState *)JSVAL_TO_PRIVATE(*statep);
1602 0 : enum_value = state->value;
1603 0 : length = state->length;
1604 0 : if (state->index != length) {
1605 0 : *idp = NPIdentifierToJSId(enum_value[state->index++]);
1606 0 : return JS_TRUE;
1607 : }
1608 :
1609 : // FALL THROUGH
1610 :
1611 : case JSENUMERATE_DESTROY:
1612 0 : state = (NPObjectEnumerateState *)JSVAL_TO_PRIVATE(*statep);
1613 0 : if (state->value)
1614 0 : PR_Free(state->value);
1615 : delete state;
1616 0 : *statep = JSVAL_NULL;
1617 :
1618 0 : break;
1619 : }
1620 :
1621 0 : return JS_TRUE;
1622 : }
1623 :
1624 : static JSBool
1625 0 : NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
1626 : JSObject **objp)
1627 : {
1628 0 : NPObject *npobj = GetNPObject(obj);
1629 :
1630 0 : if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1631 0 : !npobj->_class->hasMethod) {
1632 0 : ThrowJSException(cx, "Bad NPObject as private data!");
1633 :
1634 0 : return JS_FALSE;
1635 : }
1636 :
1637 0 : PluginDestructionGuard pdg(LookupNPP(npobj));
1638 :
1639 0 : NPIdentifier identifier = JSIdToNPIdentifier(id);
1640 :
1641 0 : bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
1642 0 : if (!ReportExceptionIfPending(cx))
1643 0 : return JS_FALSE;
1644 :
1645 0 : if (hasProperty) {
1646 0 : NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
1647 : "id must be either string or int!\n");
1648 0 : if (!::JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, nsnull,
1649 0 : nsnull, JSPROP_ENUMERATE)) {
1650 0 : return JS_FALSE;
1651 : }
1652 :
1653 0 : *objp = obj;
1654 :
1655 0 : return JS_TRUE;
1656 : }
1657 :
1658 0 : bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
1659 0 : if (!ReportExceptionIfPending(cx))
1660 0 : return JS_FALSE;
1661 :
1662 0 : if (hasMethod) {
1663 0 : NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
1664 : "id must be either string or int!\n");
1665 :
1666 : JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallNPMethod, 0,
1667 0 : JSPROP_ENUMERATE);
1668 :
1669 0 : *objp = obj;
1670 :
1671 0 : return fnc != nsnull;
1672 : }
1673 :
1674 : // no property or method
1675 0 : return JS_TRUE;
1676 : }
1677 :
1678 : static JSBool
1679 0 : NPObjWrapper_Convert(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1680 : {
1681 0 : JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
1682 :
1683 : // Plugins do not simply use JS_ConvertStub, and the default [[DefaultValue]]
1684 : // behavior, because that behavior involves calling toString or valueOf on
1685 : // objects which weren't designed to accommodate this. Usually this wouldn't
1686 : // be a problem, because the absence of either property, or the presence of
1687 : // either property with a value that isn't callable, will cause that property
1688 : // to simply be ignored. But there is a problem in one specific case: Java,
1689 : // specifically java.lang.Integer. The Integer class has static valueOf
1690 : // methods, none of which are nullary, so the JS-reflected method will behave
1691 : // poorly when called with no arguments. We work around this problem by
1692 : // giving plugins a [[DefaultValue]] which uses only toString and not valueOf.
1693 :
1694 0 : jsval v = JSVAL_VOID;
1695 0 : if (!JS_GetProperty(cx, obj, "toString", &v))
1696 0 : return false;
1697 0 : if (!JSVAL_IS_PRIMITIVE(v) && JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(v))) {
1698 0 : if (!JS_CallFunctionValue(cx, obj, v, 0, NULL, vp))
1699 0 : return false;
1700 0 : if (JSVAL_IS_PRIMITIVE(*vp))
1701 0 : return true;
1702 : }
1703 :
1704 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
1705 0 : JS_GetClass(obj)->name,
1706 : hint == JSTYPE_VOID
1707 : ? "primitive type"
1708 : : hint == JSTYPE_NUMBER
1709 : ? "number"
1710 0 : : "string");
1711 0 : return false;
1712 : }
1713 :
1714 : static void
1715 0 : NPObjWrapper_Finalize(JSContext *cx, JSObject *obj)
1716 : {
1717 0 : NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
1718 0 : if (npobj) {
1719 0 : if (sNPObjWrappers.ops) {
1720 0 : PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_REMOVE);
1721 : }
1722 : }
1723 :
1724 0 : if (!sDelayedReleases)
1725 0 : sDelayedReleases = new nsTArray<NPObject*>;
1726 0 : sDelayedReleases->AppendElement(npobj);
1727 0 : }
1728 :
1729 : static JSBool
1730 0 : NPObjWrapper_Call(JSContext *cx, unsigned argc, jsval *vp)
1731 : {
1732 : return CallNPMethodInternal(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), argc,
1733 0 : JS_ARGV(cx, vp), vp, false);
1734 : }
1735 :
1736 : static JSBool
1737 0 : NPObjWrapper_Construct(JSContext *cx, unsigned argc, jsval *vp)
1738 : {
1739 : return CallNPMethodInternal(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), argc,
1740 0 : JS_ARGV(cx, vp), vp, true);
1741 : }
1742 :
1743 : class NPObjWrapperHashEntry : public PLDHashEntryHdr
1744 : {
1745 : public:
1746 : NPObject *mNPObj; // Must be the first member for the PLDHash stubs to work
1747 : JSObject *mJSObj;
1748 : NPP mNpp;
1749 : };
1750 :
1751 :
1752 : // An NPObject is going away, make sure we null out the JS object's
1753 : // private data in case this is an NPObject that came from a plugin
1754 : // and it's destroyed prematurely.
1755 :
1756 : // static
1757 : void
1758 0 : nsNPObjWrapper::OnDestroy(NPObject *npobj)
1759 : {
1760 0 : if (!npobj) {
1761 0 : return;
1762 : }
1763 :
1764 0 : if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
1765 : // npobj is one of our own, no private data to clean up here.
1766 :
1767 0 : return;
1768 : }
1769 :
1770 0 : if (!sNPObjWrappers.ops) {
1771 : // No hash yet (or any more), no used wrappers available.
1772 :
1773 0 : return;
1774 : }
1775 :
1776 : NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
1777 0 : (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
1778 :
1779 0 : if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
1780 : // Found a live NPObject wrapper, null out its JSObjects' private
1781 : // data.
1782 :
1783 0 : ::JS_SetPrivate(entry->mJSObj, nsnull);
1784 :
1785 : // Remove the npobj from the hash now that it went away.
1786 0 : PL_DHashTableRawRemove(&sNPObjWrappers, entry);
1787 :
1788 0 : OnWrapperDestroyed();
1789 : }
1790 : }
1791 :
1792 : // Look up or create a JSObject that wraps the NPObject npobj.
1793 :
1794 : // static
1795 : JSObject *
1796 0 : nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj)
1797 : {
1798 0 : if (!npobj) {
1799 0 : NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!");
1800 :
1801 0 : return nsnull;
1802 : }
1803 :
1804 0 : if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
1805 : // npobj is one of our own, return its existing JSObject.
1806 :
1807 0 : return ((nsJSObjWrapper *)npobj)->mJSObj;
1808 : }
1809 :
1810 0 : if (!npp) {
1811 0 : NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
1812 :
1813 0 : return nsnull;
1814 : }
1815 :
1816 0 : if (!sNPObjWrappers.ops) {
1817 : // No hash yet (or any more), initialize it.
1818 :
1819 0 : if (!PL_DHashTableInit(&sNPObjWrappers, PL_DHashGetStubOps(), nsnull,
1820 0 : sizeof(NPObjWrapperHashEntry), 16)) {
1821 0 : NS_ERROR("Error initializing PLDHashTable!");
1822 :
1823 0 : return nsnull;
1824 : }
1825 : }
1826 :
1827 : NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
1828 0 : (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD));
1829 :
1830 0 : if (!entry) {
1831 : // Out of memory
1832 0 : JS_ReportOutOfMemory(cx);
1833 :
1834 0 : return nsnull;
1835 : }
1836 :
1837 0 : if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
1838 : // Found a live NPObject wrapper, return it.
1839 0 : return entry->mJSObj;
1840 : }
1841 :
1842 0 : entry->mNPObj = npobj;
1843 0 : entry->mNpp = npp;
1844 :
1845 0 : JSAutoRequest ar(cx);
1846 :
1847 0 : PRUint32 generation = sNPObjWrappers.generation;
1848 :
1849 : // No existing JSObject, create one.
1850 :
1851 0 : JSObject *obj = ::JS_NewObject(cx, &sNPObjectJSWrapperClass, nsnull, nsnull);
1852 :
1853 0 : if (generation != sNPObjWrappers.generation) {
1854 : // Reload entry if the JS_NewObject call caused a GC and reallocated
1855 : // the table (see bug 445229). This is guaranteed to succeed.
1856 :
1857 : entry = static_cast<NPObjWrapperHashEntry *>
1858 0 : (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
1859 0 : NS_ASSERTION(entry && PL_DHASH_ENTRY_IS_BUSY(entry),
1860 : "Hashtable didn't find what we just added?");
1861 : }
1862 :
1863 0 : if (!obj) {
1864 : // OOM? Remove the stale entry from the hash.
1865 :
1866 0 : PL_DHashTableRawRemove(&sNPObjWrappers, entry);
1867 :
1868 0 : return nsnull;
1869 : }
1870 :
1871 0 : OnWrapperCreated();
1872 :
1873 0 : entry->mJSObj = obj;
1874 :
1875 0 : ::JS_SetPrivate(obj, npobj);
1876 :
1877 : // The new JSObject now holds on to npobj
1878 0 : _retainobject(npobj);
1879 :
1880 0 : return obj;
1881 : }
1882 :
1883 :
1884 : // PLDHashTable enumeration callbacks for destruction code.
1885 : static PLDHashOperator
1886 0 : JSObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
1887 : PRUint32 number, void *arg)
1888 : {
1889 0 : JSObjWrapperHashEntry *entry = (JSObjWrapperHashEntry *)hdr;
1890 :
1891 0 : nsJSObjWrapper *npobj = entry->mJSObjWrapper;
1892 :
1893 0 : if (npobj->mNpp == arg) {
1894 : // Prevent invalidate() and _releaseobject() from touching the hash
1895 : // we're enumerating.
1896 0 : const PLDHashTableOps *ops = table->ops;
1897 0 : table->ops = nsnull;
1898 :
1899 0 : if (npobj->_class && npobj->_class->invalidate) {
1900 0 : npobj->_class->invalidate(npobj);
1901 : }
1902 :
1903 0 : _releaseobject(npobj);
1904 :
1905 0 : table->ops = ops;
1906 :
1907 0 : return PL_DHASH_REMOVE;
1908 : }
1909 :
1910 0 : return PL_DHASH_NEXT;
1911 : }
1912 :
1913 : // Struct for passing an NPP and a JSContext to
1914 : // NPObjWrapperPluginDestroyedCallback
1915 : struct NppAndCx
1916 : {
1917 : NPP npp;
1918 : JSContext *cx;
1919 : };
1920 :
1921 : static PLDHashOperator
1922 0 : NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
1923 : PRUint32 number, void *arg)
1924 : {
1925 0 : NPObjWrapperHashEntry *entry = (NPObjWrapperHashEntry *)hdr;
1926 0 : NppAndCx *nppcx = reinterpret_cast<NppAndCx *>(arg);
1927 :
1928 0 : if (entry->mNpp == nppcx->npp) {
1929 : // Prevent invalidate() and deallocate() from touching the hash
1930 : // we're enumerating.
1931 0 : const PLDHashTableOps *ops = table->ops;
1932 0 : table->ops = nsnull;
1933 :
1934 0 : NPObject *npobj = entry->mNPObj;
1935 :
1936 0 : if (npobj->_class && npobj->_class->invalidate) {
1937 0 : npobj->_class->invalidate(npobj);
1938 : }
1939 :
1940 : #ifdef NS_BUILD_REFCNT_LOGGING
1941 : {
1942 0 : int32_t refCnt = npobj->referenceCount;
1943 0 : while (refCnt) {
1944 0 : --refCnt;
1945 0 : NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
1946 : }
1947 : }
1948 : #endif
1949 :
1950 : // Force deallocation of plugin objects since the plugin they came
1951 : // from is being torn down.
1952 0 : if (npobj->_class && npobj->_class->deallocate) {
1953 0 : npobj->_class->deallocate(npobj);
1954 : } else {
1955 0 : PR_Free(npobj);
1956 : }
1957 :
1958 0 : ::JS_SetPrivate(entry->mJSObj, nsnull);
1959 :
1960 0 : table->ops = ops;
1961 :
1962 0 : return PL_DHASH_REMOVE;
1963 : }
1964 :
1965 0 : return PL_DHASH_NEXT;
1966 : }
1967 :
1968 : // static
1969 : void
1970 0 : nsJSNPRuntime::OnPluginDestroy(NPP npp)
1971 : {
1972 0 : if (sJSObjWrappers.ops) {
1973 : PL_DHashTableEnumerate(&sJSObjWrappers,
1974 0 : JSObjWrapperPluginDestroyedCallback, npp);
1975 : }
1976 :
1977 : // Use the safe JSContext here as we're not always able to find the
1978 : // JSContext associated with the NPP any more.
1979 :
1980 : nsCOMPtr<nsIThreadJSContextStack> stack =
1981 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1");
1982 0 : if (!stack) {
1983 0 : NS_ERROR("No context stack available!");
1984 :
1985 : return;
1986 : }
1987 :
1988 : JSContext *cx;
1989 0 : stack->GetSafeJSContext(&cx);
1990 0 : if (!cx) {
1991 0 : NS_ERROR("No safe JS context available!");
1992 :
1993 : return;
1994 : }
1995 :
1996 0 : JSAutoRequest ar(cx);
1997 :
1998 0 : if (sNPObjWrappers.ops) {
1999 0 : NppAndCx nppcx = { npp, cx };
2000 : PL_DHashTableEnumerate(&sNPObjWrappers,
2001 0 : NPObjWrapperPluginDestroyedCallback, &nppcx);
2002 : }
2003 :
2004 : // If this plugin was scripted from a webpage, the plugin's
2005 : // scriptable object will be on the DOM element's prototype
2006 : // chain. Now that the plugin is being destroyed we need to pull the
2007 : // plugin's scriptable object out of that prototype chain.
2008 0 : if (!npp) {
2009 : return;
2010 : }
2011 :
2012 : // Find the plugin instance so that we can (eventually) get to the
2013 : // DOM element
2014 0 : nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
2015 0 : if (!inst)
2016 : return;
2017 :
2018 0 : nsCOMPtr<nsIDOMElement> element;
2019 0 : inst->GetDOMElement(getter_AddRefs(element));
2020 0 : if (!element)
2021 : return;
2022 :
2023 : // Get the DOM element's JS object.
2024 0 : nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
2025 0 : if (!xpc)
2026 : return;
2027 :
2028 : // OK. Now we have to get our hands on the right scope object, since
2029 : // GetWrappedNativeOfNativeObject doesn't call PreCreate and hence won't get
2030 : // the right scope if we pass in something bogus. The right scope lives on
2031 : // the script global of the element's document.
2032 : // XXXbz we MUST have a better way of doing this... perhaps
2033 : // GetWrappedNativeOfNativeObject _should_ call preCreate?
2034 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(element));
2035 0 : if (!content) {
2036 : return;
2037 : }
2038 :
2039 0 : nsIDocument* doc = content->OwnerDoc();
2040 :
2041 0 : nsIScriptGlobalObject* sgo = doc->GetScriptGlobalObject();
2042 0 : if (!sgo) {
2043 : return;
2044 : }
2045 :
2046 0 : nsCOMPtr<nsIXPConnectWrappedNative> holder;
2047 0 : xpc->GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), content,
2048 : NS_GET_IID(nsISupports),
2049 0 : getter_AddRefs(holder));
2050 0 : if (!holder) {
2051 : return;
2052 : }
2053 :
2054 : JSObject *obj, *proto;
2055 0 : holder->GetJSObject(&obj);
2056 :
2057 0 : JSAutoEnterCompartment ac;
2058 :
2059 0 : if (obj && !ac.enter(cx, obj)) {
2060 : // Failure to enter compartment, nothing more we can do then.
2061 : return;
2062 : }
2063 :
2064 : // Loop over the DOM element's JS object prototype chain and remove
2065 : // all JS objects of the class sNPObjectJSWrapperClass (there should
2066 : // be only one, but remove all instances found in case the page put
2067 : // more than one of the plugin's scriptable objects on the prototype
2068 : // chain).
2069 0 : while (obj && (proto = ::JS_GetPrototype(obj))) {
2070 0 : if (JS_GetClass(proto) == &sNPObjectJSWrapperClass) {
2071 : // We found an NPObject on the proto chain, get its prototype...
2072 0 : proto = ::JS_GetPrototype(proto);
2073 :
2074 : // ... and pull it out of the chain.
2075 0 : ::JS_SetPrototype(cx, obj, proto);
2076 : }
2077 :
2078 0 : obj = proto;
2079 : }
2080 : }
2081 :
2082 :
2083 : // Find the NPP for a NPObject.
2084 : static NPP
2085 0 : LookupNPP(NPObject *npobj)
2086 : {
2087 0 : if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
2088 0 : nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
2089 0 : return o->mNpp;
2090 : }
2091 :
2092 : NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
2093 0 : (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD));
2094 :
2095 0 : if (PL_DHASH_ENTRY_IS_FREE(entry)) {
2096 0 : return nsnull;
2097 : }
2098 :
2099 0 : NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
2100 :
2101 0 : return entry->mNpp;
2102 : }
2103 :
2104 : JSBool
2105 0 : CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
2106 : jsid id, NPVariant* getPropertyResult, jsval *vp)
2107 : {
2108 0 : NS_ENSURE_TRUE(vp, JS_FALSE);
2109 :
2110 0 : if (!npobj || !npobj->_class || !npobj->_class->getProperty ||
2111 0 : !npobj->_class->invoke) {
2112 0 : ThrowJSException(cx, "Bad NPObject");
2113 :
2114 0 : return JS_FALSE;
2115 : }
2116 :
2117 : NPObjectMemberPrivate *memberPrivate =
2118 0 : (NPObjectMemberPrivate *)PR_Malloc(sizeof(NPObjectMemberPrivate));
2119 0 : if (!memberPrivate)
2120 0 : return JS_FALSE;
2121 :
2122 : // Make sure to clear all members in case something fails here
2123 : // during initialization.
2124 0 : memset(memberPrivate, 0, sizeof(NPObjectMemberPrivate));
2125 :
2126 0 : JSObject *memobj = ::JS_NewObject(cx, &sNPObjectMemberClass, nsnull, nsnull);
2127 0 : if (!memobj) {
2128 0 : PR_Free(memberPrivate);
2129 0 : return JS_FALSE;
2130 : }
2131 :
2132 0 : *vp = OBJECT_TO_JSVAL(memobj);
2133 0 : ::JS_AddValueRoot(cx, vp);
2134 :
2135 0 : ::JS_SetPrivate(memobj, (void *)memberPrivate);
2136 :
2137 0 : NPIdentifier identifier = JSIdToNPIdentifier(id);
2138 :
2139 : jsval fieldValue;
2140 : NPVariant npv;
2141 :
2142 0 : if (getPropertyResult) {
2143 : // Plugin has already handed us the value we want here.
2144 0 : npv = *getPropertyResult;
2145 : }
2146 : else {
2147 0 : VOID_TO_NPVARIANT(npv);
2148 :
2149 : NPBool hasProperty = npobj->_class->getProperty(npobj, identifier,
2150 0 : &npv);
2151 0 : if (!ReportExceptionIfPending(cx)) {
2152 0 : ::JS_RemoveValueRoot(cx, vp);
2153 0 : return JS_FALSE;
2154 : }
2155 :
2156 0 : if (!hasProperty) {
2157 0 : ::JS_RemoveValueRoot(cx, vp);
2158 0 : return JS_FALSE;
2159 : }
2160 : }
2161 :
2162 0 : fieldValue = NPVariantToJSVal(npp, cx, &npv);
2163 :
2164 : // npobjWrapper is the JSObject through which we make sure we don't
2165 : // outlive the underlying NPObject, so make sure it points to the
2166 : // real JSObject wrapper for the NPObject.
2167 0 : while (JS_GetClass(obj) != &sNPObjectJSWrapperClass) {
2168 0 : obj = ::JS_GetPrototype(obj);
2169 : }
2170 :
2171 0 : memberPrivate->npobjWrapper = obj;
2172 :
2173 0 : memberPrivate->fieldValue = fieldValue;
2174 0 : memberPrivate->methodName = identifier;
2175 0 : memberPrivate->npp = npp;
2176 :
2177 0 : ::JS_RemoveValueRoot(cx, vp);
2178 :
2179 0 : return JS_TRUE;
2180 : }
2181 :
2182 : static JSBool
2183 0 : NPObjectMember_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
2184 : {
2185 : NPObjectMemberPrivate *memberPrivate =
2186 : (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
2187 : &sNPObjectMemberClass,
2188 0 : nsnull);
2189 0 : if (!memberPrivate) {
2190 0 : NS_ERROR("no Ambiguous Member Private data!");
2191 0 : return JS_FALSE;
2192 : }
2193 :
2194 0 : switch (type) {
2195 : case JSTYPE_VOID:
2196 : case JSTYPE_STRING:
2197 : case JSTYPE_NUMBER:
2198 0 : *vp = memberPrivate->fieldValue;
2199 0 : if (!JSVAL_IS_PRIMITIVE(*vp)) {
2200 0 : return JS_DefaultValue(cx, JSVAL_TO_OBJECT(*vp), type, vp);
2201 : }
2202 0 : return JS_TRUE;
2203 : case JSTYPE_BOOLEAN:
2204 : case JSTYPE_OBJECT:
2205 0 : *vp = memberPrivate->fieldValue;
2206 0 : return JS_TRUE;
2207 : case JSTYPE_FUNCTION:
2208 : // Leave this to NPObjectMember_Call.
2209 0 : return JS_TRUE;
2210 : default:
2211 0 : NS_ERROR("illegal operation on JSObject prototype object");
2212 0 : return JS_FALSE;
2213 : }
2214 : }
2215 :
2216 : static void
2217 0 : NPObjectMember_Finalize(JSContext *cx, JSObject *obj)
2218 : {
2219 : NPObjectMemberPrivate *memberPrivate;
2220 :
2221 0 : memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
2222 0 : if (!memberPrivate)
2223 0 : return;
2224 :
2225 0 : PR_Free(memberPrivate);
2226 : }
2227 :
2228 : static JSBool
2229 0 : NPObjectMember_Call(JSContext *cx, unsigned argc, jsval *vp)
2230 : {
2231 0 : JSObject *memobj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
2232 0 : NS_ENSURE_TRUE(memobj, JS_FALSE);
2233 :
2234 : NPObjectMemberPrivate *memberPrivate =
2235 : (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
2236 : &sNPObjectMemberClass,
2237 0 : JS_ARGV(cx, vp));
2238 0 : if (!memberPrivate || !memberPrivate->npobjWrapper)
2239 0 : return JS_FALSE;
2240 :
2241 0 : NPObject *npobj = GetNPObject(memberPrivate->npobjWrapper);
2242 0 : if (!npobj) {
2243 0 : ThrowJSException(cx, "Call on invalid member object");
2244 :
2245 0 : return JS_FALSE;
2246 : }
2247 :
2248 : NPVariant npargs_buf[8];
2249 0 : NPVariant *npargs = npargs_buf;
2250 :
2251 0 : if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
2252 : // Our stack buffer isn't large enough to hold all arguments,
2253 : // malloc a buffer.
2254 0 : npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
2255 :
2256 0 : if (!npargs) {
2257 0 : ThrowJSException(cx, "Out of memory!");
2258 :
2259 0 : return JS_FALSE;
2260 : }
2261 : }
2262 :
2263 : // Convert arguments
2264 : PRUint32 i;
2265 0 : jsval *argv = JS_ARGV(cx, vp);
2266 0 : for (i = 0; i < argc; ++i) {
2267 0 : if (!JSValToNPVariant(memberPrivate->npp, cx, argv[i], npargs + i)) {
2268 0 : ThrowJSException(cx, "Error converting jsvals to NPVariants!");
2269 :
2270 0 : if (npargs != npargs_buf) {
2271 0 : PR_Free(npargs);
2272 : }
2273 :
2274 0 : return JS_FALSE;
2275 : }
2276 : }
2277 :
2278 :
2279 : NPVariant npv;
2280 : JSBool ok;
2281 : ok = npobj->_class->invoke(npobj, memberPrivate->methodName,
2282 0 : npargs, argc, &npv);
2283 :
2284 : // Release arguments.
2285 0 : for (i = 0; i < argc; ++i) {
2286 0 : _releasevariantvalue(npargs + i);
2287 : }
2288 :
2289 0 : if (npargs != npargs_buf) {
2290 0 : PR_Free(npargs);
2291 : }
2292 :
2293 0 : if (!ok) {
2294 : // ReportExceptionIfPending returns a return value, which is JS_TRUE
2295 : // if no exception was thrown. In that case, throw our own.
2296 0 : if (ReportExceptionIfPending(cx))
2297 0 : ThrowJSException(cx, "Error calling method on NPObject!");
2298 :
2299 0 : return JS_FALSE;
2300 : }
2301 :
2302 0 : JS_SET_RVAL(cx, vp, NPVariantToJSVal(memberPrivate->npp, cx, &npv));
2303 :
2304 : // *vp now owns the value, release our reference.
2305 0 : _releasevariantvalue(&npv);
2306 :
2307 0 : return ReportExceptionIfPending(cx);
2308 : }
2309 :
2310 : static void
2311 0 : NPObjectMember_Trace(JSTracer *trc, JSObject *obj)
2312 : {
2313 : NPObjectMemberPrivate *memberPrivate =
2314 0 : (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
2315 0 : if (!memberPrivate)
2316 0 : return;
2317 :
2318 : // Our NPIdentifier is not always interned, so we must root it explicitly.
2319 0 : jsid id = NPIdentifierToJSId(memberPrivate->methodName);
2320 0 : if (JSID_IS_STRING(id))
2321 0 : JS_CALL_STRING_TRACER(trc, JSID_TO_STRING(id), "NPObjectMemberPrivate.methodName");
2322 :
2323 0 : if (!JSVAL_IS_PRIMITIVE(memberPrivate->fieldValue)) {
2324 0 : JS_CALL_VALUE_TRACER(trc, memberPrivate->fieldValue,
2325 : "NPObject Member => fieldValue");
2326 : }
2327 :
2328 : // There's no strong reference from our private data to the
2329 : // NPObject, so make sure to mark the NPObject wrapper to keep the
2330 : // NPObject alive as long as this NPObjectMember is alive.
2331 0 : if (memberPrivate->npobjWrapper) {
2332 0 : JS_CALL_OBJECT_TRACER(trc, memberPrivate->npobjWrapper,
2333 : "NPObject Member => npobjWrapper");
2334 : }
2335 : }
|