Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / modules / plugin / base / src / nsJSNPRuntime.cpp
blob54d2e9ea6a18203fee080867db945dbf32843f02
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
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/
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.
15 * The Original Code is mozilla.org code.
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.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsJSNPRuntime.h"
39 #include "nsNPAPIPlugin.h"
40 #include "nsNPAPIPluginInstance.h"
41 #include "nsIPluginInstancePeer2.h"
42 #include "nsPIPluginInstancePeer.h"
43 #include "nsIScriptGlobalObject.h"
44 #include "nsIScriptContext.h"
45 #include "nsDOMJSUtils.h"
46 #include "nsIDocument.h"
47 #include "nsIJSRuntimeService.h"
48 #include "nsIJSContextStack.h"
49 #include "nsIXPConnect.h"
50 #include "nsIDOMElement.h"
51 #include "prmem.h"
52 #include "nsIContent.h"
54 // FIXME(bug 332648): Give me a real API please!
55 #include "jscntxt.h"
57 // Hash of JSObject wrappers that wraps JSObjects as NPObjects. There
58 // will be one wrapper per JSObject per plugin instance, i.e. if two
59 // plugins access the JSObject x, two wrappers for x will be
60 // created. This is needed to be able to properly drop the wrappers
61 // when a plugin is torn down in case there's a leak in the plugin (we
62 // don't want to leak the world just because a plugin leaks an
63 // NPObject).
64 static PLDHashTable sJSObjWrappers;
66 // Hash of NPObject wrappers that wrap NPObjects as JSObjects.
67 static PLDHashTable sNPObjWrappers;
69 // Global wrapper count. This includes JSObject wrappers *and*
70 // NPObject wrappers. When this count goes to zero, there are no more
71 // wrappers and we can kill off hash tables etc.
72 static PRInt32 sWrapperCount;
74 // The JSRuntime. Used to unroot JSObjects when no JSContext is
75 // reachable.
76 static JSRuntime *sJSRuntime;
78 // The JS context stack, we use this to push a plugin's JSContext onto
79 // while executing JS on the context.
80 static nsIJSContextStack *sContextStack;
83 // Helper class that reports any JS exceptions that were thrown while
84 // the plugin executed JS.
86 class AutoJSExceptionReporter
88 public:
89 AutoJSExceptionReporter(JSContext *cx)
90 : mCx(cx)
94 ~AutoJSExceptionReporter()
96 JS_ReportPendingException(mCx);
99 protected:
100 JSContext *mCx;
104 NPClass nsJSObjWrapper::sJSObjWrapperNPClass =
106 NP_CLASS_STRUCT_VERSION,
107 nsJSObjWrapper::NP_Allocate,
108 nsJSObjWrapper::NP_Deallocate,
109 nsJSObjWrapper::NP_Invalidate,
110 nsJSObjWrapper::NP_HasMethod,
111 nsJSObjWrapper::NP_Invoke,
112 nsJSObjWrapper::NP_InvokeDefault,
113 nsJSObjWrapper::NP_HasProperty,
114 nsJSObjWrapper::NP_GetProperty,
115 nsJSObjWrapper::NP_SetProperty,
116 nsJSObjWrapper::NP_RemoveProperty,
117 nsJSObjWrapper::NP_Enumerate,
118 nsJSObjWrapper::NP_Construct
121 static JSBool
122 NPObjWrapper_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
124 static JSBool
125 NPObjWrapper_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
127 static JSBool
128 NPObjWrapper_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
130 static JSBool
131 NPObjWrapper_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
133 static JSBool
134 NPObjWrapper_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
135 jsval *statep, jsid *idp);
137 static JSBool
138 NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
139 JSObject **objp);
141 static JSBool
142 NPObjWrapper_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
144 static void
145 NPObjWrapper_Finalize(JSContext *cx, JSObject *obj);
147 static JSBool
148 NPObjWrapper_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
149 jsval *rval);
151 static JSBool
152 NPObjWrapper_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
153 jsval *rval);
155 static bool
156 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj,
157 NPObject *npobj, jsval id, jsval *vp);
159 static JSClass sNPObjectJSWrapperClass =
161 NPRUNTIME_JSCLASS_NAME,
162 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE,
163 NPObjWrapper_AddProperty, NPObjWrapper_DelProperty,
164 NPObjWrapper_GetProperty, NPObjWrapper_SetProperty,
165 (JSEnumerateOp)NPObjWrapper_newEnumerate,
166 (JSResolveOp)NPObjWrapper_NewResolve, NPObjWrapper_Convert,
167 NPObjWrapper_Finalize, nsnull, nsnull, NPObjWrapper_Call,
168 NPObjWrapper_Construct, nsnull, nsnull
171 typedef struct NPObjectMemberPrivate {
172 JSObject *npobjWrapper;
173 jsval fieldValue;
174 jsval methodName;
175 NPP npp;
176 } NPObjectMemberPrivate;
178 static JSBool
179 NPObjectMember_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
181 static void
182 NPObjectMember_Finalize(JSContext *cx, JSObject *obj);
184 static JSBool
185 NPObjectMember_Call(JSContext *cx, JSObject *obj, uintN argc,
186 jsval *argv, jsval *rval);
188 static uint32
189 NPObjectMember_Mark(JSContext *cx, JSObject *obj, void *arg);
191 static JSClass sNPObjectMemberClass =
193 "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE,
194 JS_PropertyStub, JS_PropertyStub,
195 JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,
196 JS_ResolveStub, NPObjectMember_Convert,
197 NPObjectMember_Finalize, nsnull, nsnull, NPObjectMember_Call,
198 nsnull, nsnull, nsnull, NPObjectMember_Mark, nsnull
201 static void
202 OnWrapperCreated()
204 if (sWrapperCount++ == 0) {
205 static const char rtsvc_id[] = "@mozilla.org/js/xpc/RuntimeService;1";
206 nsCOMPtr<nsIJSRuntimeService> rtsvc(do_GetService(rtsvc_id));
207 if (!rtsvc)
208 return;
210 rtsvc->GetRuntime(&sJSRuntime);
211 NS_ASSERTION(sJSRuntime != nsnull, "no JSRuntime?!");
213 CallGetService("@mozilla.org/js/xpc/ContextStack;1", &sContextStack);
217 static void
218 OnWrapperDestroyed()
220 NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
222 if (--sWrapperCount == 0) {
223 if (sJSObjWrappers.ops) {
224 NS_ASSERTION(sJSObjWrappers.entryCount == 0, "Uh, hash not empty?");
226 // No more wrappers, and our hash was initialized. Finish the
227 // hash to prevent leaking it.
228 PL_DHashTableFinish(&sJSObjWrappers);
230 sJSObjWrappers.ops = nsnull;
233 if (sNPObjWrappers.ops) {
234 NS_ASSERTION(sNPObjWrappers.entryCount == 0, "Uh, hash not empty?");
236 // No more wrappers, and our hash was initialized. Finish the
237 // hash to prevent leaking it.
238 PL_DHashTableFinish(&sNPObjWrappers);
240 sNPObjWrappers.ops = nsnull;
243 // No more need for this.
244 sJSRuntime = nsnull;
246 NS_IF_RELEASE(sContextStack);
250 struct AutoCXPusher
252 AutoCXPusher(JSContext *cx)
254 // Precondition explaining why we don't need to worry about errors
255 // in OnWrapperCreated.
256 NS_PRECONDITION(sWrapperCount > 0,
257 "must have live wrappers when using AutoCXPusher");
259 // Call OnWrapperCreated and OnWrapperDestroyed to ensure that the
260 // last OnWrapperDestroyed doesn't happen while we're on the stack
261 // and null out sContextStack.
262 OnWrapperCreated();
264 sContextStack->Push(cx);
267 ~AutoCXPusher()
269 JSContext *cx = nsnull;
270 sContextStack->Pop(&cx);
272 JSContext *currentCx = nsnull;
273 sContextStack->Peek(&currentCx);
275 if (!currentCx) {
276 // No JS is running, tell the context we're done executing
277 // script.
279 nsIScriptContext *scx = GetScriptContextFromJSContext(cx);
281 if (scx) {
282 scx->ScriptEvaluated(PR_TRUE);
286 OnWrapperDestroyed();
290 static JSContext *
291 GetJSContext(NPP npp)
293 NS_ENSURE_TRUE(npp, nsnull);
295 nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
296 NS_ENSURE_TRUE(inst, nsnull);
298 nsCOMPtr<nsPIPluginInstancePeer> pp(do_QueryInterface(inst->Peer()));
299 NS_ENSURE_TRUE(pp, nsnull);
301 nsCOMPtr<nsIPluginInstanceOwner> owner;
302 pp->GetOwner(getter_AddRefs(owner));
303 NS_ENSURE_TRUE(owner, nsnull);
305 nsCOMPtr<nsIDocument> doc;
306 owner->GetDocument(getter_AddRefs(doc));
307 NS_ENSURE_TRUE(doc, nsnull);
309 nsCOMPtr<nsISupports> documentContainer = doc->GetContainer();
310 nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(documentContainer));
311 NS_ENSURE_TRUE(sgo, nsnull);
313 nsIScriptContext *scx = sgo->GetContext();
314 NS_ENSURE_TRUE(scx, nsnull);
316 return (JSContext *)scx->GetNativeContext();
320 static NPP
321 LookupNPP(NPObject *npobj);
324 static jsval
325 NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant)
327 switch (variant->type) {
328 case NPVariantType_Void :
329 return JSVAL_VOID;
330 case NPVariantType_Null :
331 return JSVAL_NULL;
332 case NPVariantType_Bool :
333 return BOOLEAN_TO_JSVAL(NPVARIANT_TO_BOOLEAN(*variant));
334 case NPVariantType_Int32 :
336 // Don't use INT_TO_JSVAL directly to prevent bugs when dealing
337 // with ints larger than what fits in a integer jsval.
338 jsval val;
339 if (::JS_NewNumberValue(cx, NPVARIANT_TO_INT32(*variant), &val)) {
340 return val;
343 break;
345 case NPVariantType_Double :
347 jsval val;
348 if (::JS_NewNumberValue(cx, NPVARIANT_TO_DOUBLE(*variant), &val)) {
349 return val;
352 break;
354 case NPVariantType_String :
356 const NPString *s = &NPVARIANT_TO_STRING(*variant);
357 NS_ConvertUTF8toUTF16 utf16String(s->UTF8Characters, s->UTF8Length);
359 JSString *str =
360 ::JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar*>
361 (utf16String.get()),
362 utf16String.Length());
364 if (str) {
365 return STRING_TO_JSVAL(str);
368 break;
370 case NPVariantType_Object:
372 if (npp) {
373 JSObject *obj =
374 nsNPObjWrapper::GetNewOrUsed(npp, cx, NPVARIANT_TO_OBJECT(*variant));
376 if (obj) {
377 return OBJECT_TO_JSVAL(obj);
381 NS_ERROR("Error wrapping NPObject!");
383 break;
385 default:
386 NS_ERROR("Unknown NPVariant type!");
389 NS_ERROR("Unable to convert NPVariant to jsval!");
391 return JSVAL_VOID;
394 bool
395 JSValToNPVariant(NPP npp, JSContext *cx, jsval val, NPVariant *variant)
397 NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
399 if (JSVAL_IS_PRIMITIVE(val)) {
400 if (val == JSVAL_VOID) {
401 VOID_TO_NPVARIANT(*variant);
402 } else if (JSVAL_IS_NULL(val)) {
403 NULL_TO_NPVARIANT(*variant);
404 } else if (JSVAL_IS_BOOLEAN(val)) {
405 BOOLEAN_TO_NPVARIANT(JSVAL_TO_BOOLEAN(val), *variant);
406 } else if (JSVAL_IS_INT(val)) {
407 INT32_TO_NPVARIANT(JSVAL_TO_INT(val), *variant);
408 } else if (JSVAL_IS_DOUBLE(val)) {
409 DOUBLE_TO_NPVARIANT(*JSVAL_TO_DOUBLE(val), *variant);
410 } else if (JSVAL_IS_STRING(val)) {
411 JSString *jsstr = JSVAL_TO_STRING(val);
412 nsDependentString str((PRUnichar *)::JS_GetStringChars(jsstr),
413 ::JS_GetStringLength(jsstr));
415 PRUint32 len;
416 char *p = ToNewUTF8String(str, &len);
418 if (!p) {
419 return false;
422 STRINGN_TO_NPVARIANT(p, len, *variant);
423 } else {
424 NS_ERROR("Unknown primitive type!");
426 return false;
429 return true;
432 NPObject *npobj =
433 nsJSObjWrapper::GetNewOrUsed(npp, cx, JSVAL_TO_OBJECT(val));
434 if (!npobj) {
435 return false;
438 // Pass over ownership of npobj to *variant
439 OBJECT_TO_NPVARIANT(npobj, *variant);
441 return true;
444 static void
445 ThrowJSException(JSContext *cx, const char *message)
447 const char *ex = PeekException();
449 if (ex) {
450 nsAutoString ucex;
452 if (message) {
453 AppendASCIItoUTF16(message, ucex);
455 AppendASCIItoUTF16(" [plugin exception: ", ucex);
458 AppendUTF8toUTF16(ex, ucex);
460 if (message) {
461 AppendASCIItoUTF16("].", ucex);
464 JSString *str = ::JS_NewUCStringCopyN(cx, (jschar *)ucex.get(),
465 ucex.Length());
467 if (str) {
468 ::JS_SetPendingException(cx, STRING_TO_JSVAL(str));
471 PopException();
472 } else {
473 ::JS_ReportError(cx, message);
477 static JSBool
478 ReportExceptionIfPending(JSContext *cx)
480 const char *ex = PeekException();
482 if (!ex) {
483 return JS_TRUE;
486 ThrowJSException(cx, nsnull);
488 return JS_FALSE;
492 nsJSObjWrapper::nsJSObjWrapper(NPP npp)
493 : nsJSObjWrapperKey(nsnull, npp)
495 OnWrapperCreated();
498 nsJSObjWrapper::~nsJSObjWrapper()
500 // Invalidate first, since it relies on sJSRuntime and sJSObjWrappers.
501 NP_Invalidate(this);
503 OnWrapperDestroyed();
506 // static
507 NPObject *
508 nsJSObjWrapper::NP_Allocate(NPP npp, NPClass *aClass)
510 NS_ASSERTION(aClass == &sJSObjWrapperNPClass,
511 "Huh, wrong class passed to NP_Allocate()!!!");
513 return new nsJSObjWrapper(npp);
516 // static
517 void
518 nsJSObjWrapper::NP_Deallocate(NPObject *npobj)
520 // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate().
521 delete (nsJSObjWrapper *)npobj;
524 // static
525 void
526 nsJSObjWrapper::NP_Invalidate(NPObject *npobj)
528 nsJSObjWrapper *jsnpobj = (nsJSObjWrapper *)npobj;
530 if (jsnpobj && jsnpobj->mJSObj) {
531 // Unroot the object's JSObject
532 ::JS_RemoveRootRT(sJSRuntime, &jsnpobj->mJSObj);
534 if (sJSObjWrappers.ops) {
535 // Remove the wrapper from the hash
537 nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
538 PL_DHashTableOperate(&sJSObjWrappers, &key, PL_DHASH_REMOVE);
541 // Forget our reference to the JSObject.
542 jsnpobj->mJSObj = nsnull;
546 static JSBool
547 GetProperty(JSContext *cx, JSObject *obj, NPIdentifier identifier, jsval *rval)
549 jsval id = (jsval)identifier;
551 if (JSVAL_IS_STRING(id)) {
552 JSString *str = JSVAL_TO_STRING(id);
554 return ::JS_GetUCProperty(cx, obj, ::JS_GetStringChars(str),
555 ::JS_GetStringLength(str), rval);
558 NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
560 return ::JS_GetElement(cx, obj, JSVAL_TO_INT(id), rval);
563 // static
564 bool
565 nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier identifier)
567 NPP npp = NPPStack::Peek();
568 JSContext *cx = GetJSContext(npp);
570 if (!cx) {
571 NS_ERROR("Null cx in nsJSObjWrapper::NP_HasMethod!");
572 return PR_FALSE;
575 if (!npobj) {
576 ThrowJSException(cx,
577 "Null npobj in nsJSObjWrapper::NP_HasMethod!");
579 return PR_FALSE;
582 nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
584 AutoCXPusher pusher(cx);
585 JSAutoRequest ar(cx);
586 AutoJSExceptionReporter reporter(cx);
588 jsval v;
589 JSBool ok = GetProperty(cx, npjsobj->mJSObj, identifier, &v);
591 return ok && !JSVAL_IS_PRIMITIVE(v) &&
592 ::JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v));
595 static bool
596 doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args,
597 uint32_t argCount, PRBool ctorCall, NPVariant *result)
599 NPP npp = NPPStack::Peek();
600 JSContext *cx = GetJSContext(npp);
602 if (!cx) {
603 NS_ERROR("Null cx in doInvoke!");
604 return PR_FALSE;
607 if (!npobj || !result) {
608 ThrowJSException(cx, "Null npobj, or result in doInvoke!");
610 return PR_FALSE;
613 // Initialize *result
614 VOID_TO_NPVARIANT(*result);
616 nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
617 jsval fv;
619 AutoCXPusher pusher(cx);
620 JSAutoRequest ar(cx);
621 AutoJSExceptionReporter reporter(cx);
623 if ((jsval)method != JSVAL_VOID) {
624 if (!GetProperty(cx, npjsobj->mJSObj, method, &fv) ||
625 ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
626 return PR_FALSE;
628 } else {
629 fv = OBJECT_TO_JSVAL(npjsobj->mJSObj);
632 jsval jsargs_buf[8];
633 jsval *jsargs = jsargs_buf;
635 if (argCount > (sizeof(jsargs_buf) / sizeof(jsval))) {
636 // Our stack buffer isn't large enough to hold all arguments,
637 // malloc a buffer.
638 jsargs = (jsval *)PR_Malloc(argCount * sizeof(jsval));
639 if (!jsargs) {
640 ::JS_ReportOutOfMemory(cx);
642 return PR_FALSE;
646 JSTempValueRooter tvr;
647 JS_PUSH_TEMP_ROOT(cx, 0, jsargs, &tvr);
649 // Convert args
650 for (PRUint32 i = 0; i < argCount; ++i) {
651 jsargs[i] = NPVariantToJSVal(npp, cx, args + i);
652 ++tvr.count;
655 jsval v;
656 JSBool ok;
658 if (ctorCall) {
659 JSObject *global = ::JS_GetGlobalForObject(cx, npjsobj->mJSObj);
660 JSObject *newObj =
661 ::JS_ConstructObjectWithArguments(cx, JS_GET_CLASS(cx, npjsobj->mJSObj),
662 nsnull, global, argCount, jsargs);
664 if (newObj) {
665 v = OBJECT_TO_JSVAL(newObj);
666 ok = JS_TRUE;
667 } else {
668 ok = JS_FALSE;
670 } else {
671 ok = ::JS_CallFunctionValue(cx, npjsobj->mJSObj, fv, argCount, jsargs, &v);
674 JS_POP_TEMP_ROOT(cx, &tvr);
676 if (jsargs != jsargs_buf)
677 PR_Free(jsargs);
679 if (ok)
680 ok = JSValToNPVariant(npp, cx, v, result);
682 // return ok == JS_TRUE to quiet down compiler warning, even if
683 // return ok is what we really want.
684 return ok == JS_TRUE;
687 // static
688 bool
689 nsJSObjWrapper::NP_Invoke(NPObject *npobj, NPIdentifier method,
690 const NPVariant *args, uint32_t argCount,
691 NPVariant *result)
693 if ((jsval)method == JSVAL_VOID) {
694 return PR_FALSE;
697 return doInvoke(npobj, method, args, argCount, PR_FALSE, result);
700 // static
701 bool
702 nsJSObjWrapper::NP_InvokeDefault(NPObject *npobj, const NPVariant *args,
703 uint32_t argCount, NPVariant *result)
705 return doInvoke(npobj, (NPIdentifier)JSVAL_VOID, args, argCount, PR_FALSE,
706 result);
709 // static
710 bool
711 nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier identifier)
713 NPP npp = NPPStack::Peek();
714 JSContext *cx = GetJSContext(npp);
716 if (!cx) {
717 NS_ERROR("Null cx in nsJSObjWrapper::NP_HasProperty!");
718 return PR_FALSE;
721 if (!npobj) {
722 ThrowJSException(cx,
723 "Null npobj in nsJSObjWrapper::NP_HasProperty!");
725 return PR_FALSE;
728 nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
729 jsval id = (jsval)identifier;
730 JSBool found, ok = JS_FALSE;
732 AutoCXPusher pusher(cx);
733 JSAutoRequest ar(cx);
734 AutoJSExceptionReporter reporter(cx);
736 if (JSVAL_IS_STRING(id)) {
737 JSString *str = JSVAL_TO_STRING(id);
739 ok = ::JS_HasUCProperty(cx, npjsobj->mJSObj, ::JS_GetStringChars(str),
740 ::JS_GetStringLength(str), &found);
741 } else {
742 NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
744 ok = ::JS_HasElement(cx, npjsobj->mJSObj, JSVAL_TO_INT(id), &found);
747 return ok && found;
750 // static
751 bool
752 nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier identifier,
753 NPVariant *result)
755 NPP npp = NPPStack::Peek();
756 JSContext *cx = GetJSContext(npp);
758 if (!cx) {
759 NS_ERROR("Null cx in nsJSObjWrapper::NP_GetProperty!");
760 return PR_FALSE;
763 if (!npobj) {
764 ThrowJSException(cx,
765 "Null npobj in nsJSObjWrapper::NP_GetProperty!");
767 return PR_FALSE;
770 nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
772 AutoCXPusher pusher(cx);
773 JSAutoRequest ar(cx);
774 AutoJSExceptionReporter reporter(cx);
776 jsval v;
777 return (GetProperty(cx, npjsobj->mJSObj, identifier, &v) &&
778 JSValToNPVariant(npp, cx, v, result));
781 // static
782 bool
783 nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier identifier,
784 const NPVariant *value)
786 NPP npp = NPPStack::Peek();
787 JSContext *cx = GetJSContext(npp);
789 if (!cx) {
790 NS_ERROR("Null cx in nsJSObjWrapper::NP_SetProperty!");
791 return PR_FALSE;
794 if (!npobj) {
795 ThrowJSException(cx,
796 "Null npobj in nsJSObjWrapper::NP_SetProperty!");
798 return PR_FALSE;
801 nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
802 jsval id = (jsval)identifier;
803 JSBool ok = JS_FALSE;
805 AutoCXPusher pusher(cx);
806 JSAutoRequest ar(cx);
807 AutoJSExceptionReporter reporter(cx);
809 jsval v = NPVariantToJSVal(npp, cx, value);
810 JSAutoTempValueRooter tvr(cx, v);
812 if (JSVAL_IS_STRING(id)) {
813 JSString *str = JSVAL_TO_STRING(id);
815 ok = ::JS_SetUCProperty(cx, npjsobj->mJSObj, ::JS_GetStringChars(str),
816 ::JS_GetStringLength(str), &v);
817 } else {
818 NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
820 ok = ::JS_SetElement(cx, npjsobj->mJSObj, JSVAL_TO_INT(id), &v);
823 // return ok == JS_TRUE to quiet down compiler warning, even if
824 // return ok is what we really want.
825 return ok == JS_TRUE;
828 // static
829 bool
830 nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier identifier)
832 NPP npp = NPPStack::Peek();
833 JSContext *cx = GetJSContext(npp);
835 if (!cx) {
836 NS_ERROR("Null cx in nsJSObjWrapper::NP_RemoveProperty!");
837 return PR_FALSE;
840 if (!npobj) {
841 ThrowJSException(cx,
842 "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
844 return PR_FALSE;
847 nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
848 jsval id = (jsval)identifier;
849 JSBool ok = JS_FALSE;
851 AutoCXPusher pusher(cx);
852 JSAutoRequest ar(cx);
853 AutoJSExceptionReporter reporter(cx);
854 jsval deleted = JSVAL_FALSE;
856 if (JSVAL_IS_STRING(id)) {
857 JSString *str = JSVAL_TO_STRING(id);
859 ok = ::JS_DeleteUCProperty2(cx, npjsobj->mJSObj, ::JS_GetStringChars(str),
860 ::JS_GetStringLength(str), &deleted);
862 if (ok && deleted) {
863 // FIXME: See bug 425823, we shouldn't need to do this, and once
864 // that bug is fixed we can remove this code.
866 JSBool hasProp;
867 ok = ::JS_HasUCProperty(cx, npjsobj->mJSObj, ::JS_GetStringChars(str),
868 ::JS_GetStringLength(str), &hasProp);
870 if (ok && hasProp) {
871 // The property might have been deleted, but it got
872 // re-resolved, so no, it's not really deleted.
874 deleted = JSVAL_FALSE;
877 } else {
878 NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
880 ok = ::JS_DeleteElement2(cx, npjsobj->mJSObj, JSVAL_TO_INT(id), &deleted);
882 if (ok && deleted) {
883 // FIXME: See bug 425823, we shouldn't need to do this, and once
884 // that bug is fixed we can remove this code.
886 JSBool hasProp;
887 ok = ::JS_HasElement(cx, npjsobj->mJSObj, JSVAL_TO_INT(id), &hasProp);
889 if (ok && hasProp) {
890 // The property might have been deleted, but it got
891 // re-resolved, so no, it's not really deleted.
893 deleted = JSVAL_FALSE;
898 // return ok == JS_TRUE to quiet down compiler warning, even if
899 // return ok is what we really want.
900 return ok == JS_TRUE && deleted == JSVAL_TRUE;
903 //static
904 bool
905 nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **identifier,
906 uint32_t *count)
908 NPP npp = NPPStack::Peek();
909 JSContext *cx = GetJSContext(npp);
911 *identifier = 0;
912 *count = 0;
914 if (!cx) {
915 NS_ERROR("Null cx in nsJSObjWrapper::NP_Enumerate!");
916 return PR_FALSE;
919 if (!npobj) {
920 ThrowJSException(cx,
921 "Null npobj in nsJSObjWrapper::NP_Enumerate!");
923 return PR_FALSE;
926 nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
928 AutoCXPusher pusher(cx);
929 JSAutoRequest ar(cx);
930 AutoJSExceptionReporter reporter(cx);
932 JSIdArray *ida = ::JS_Enumerate(cx, npjsobj->mJSObj);
933 if (!ida) {
934 return PR_FALSE;
937 *count = ida->length;
938 *identifier = (NPIdentifier *)PR_Malloc(*count * sizeof(NPIdentifier));
939 if (!*identifier) {
940 ThrowJSException(cx, "Memory allocation failed for NPIdentifier!");
942 ::JS_DestroyIdArray(cx, ida);
944 return PR_FALSE;
947 for (PRUint32 i = 0; i < *count; i++) {
948 jsval v;
949 if (!::JS_IdToValue(cx, ida->vector[i], &v)) {
950 ::JS_DestroyIdArray(cx, ida);
951 PR_Free(*identifier);
952 return PR_FALSE;
955 if (JSVAL_IS_STRING(v)) {
956 JSString *str = JSVAL_TO_STRING(v);
958 if (!JS_InternUCStringN(cx, ::JS_GetStringChars(str),
959 ::JS_GetStringLength(str))) {
960 ::JS_DestroyIdArray(cx, ida);
961 PR_Free(*identifier);
963 return PR_FALSE;
965 } else {
966 NS_ASSERTION(JSVAL_IS_INT(v),
967 "The element in ida must be either string or int!\n");
970 (*identifier)[i] = (NPIdentifier)v;
973 ::JS_DestroyIdArray(cx, ida);
975 return PR_TRUE;
978 //static
979 bool
980 nsJSObjWrapper::NP_Construct(NPObject *npobj, const NPVariant *args,
981 uint32_t argCount, NPVariant *result)
983 return doInvoke(npobj, (NPIdentifier)JSVAL_VOID, args, argCount, PR_TRUE,
984 result);
988 class JSObjWrapperHashEntry : public PLDHashEntryHdr
990 public:
991 nsJSObjWrapper *mJSObjWrapper;
995 static PLDHashNumber
996 JSObjWrapperHash(PLDHashTable *table, const void *key)
998 const nsJSObjWrapperKey *e = static_cast<const nsJSObjWrapperKey *>(key);
1000 return (PLDHashNumber)((PRWord)e->mJSObj ^ (PRWord)e->mNpp) >> 2;
1003 static PRBool
1004 JSObjWrapperHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
1005 const void *key)
1007 const nsJSObjWrapperKey *objWrapperKey =
1008 static_cast<const nsJSObjWrapperKey *>(key);
1009 const JSObjWrapperHashEntry *e =
1010 static_cast<const JSObjWrapperHashEntry *>(entry);
1012 return (e->mJSObjWrapper->mJSObj == objWrapperKey->mJSObj &&
1013 e->mJSObjWrapper->mNpp == objWrapperKey->mNpp);
1017 // Look up or create an NPObject that wraps the JSObject obj.
1019 // static
1020 NPObject *
1021 nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JSObject *obj)
1023 if (!npp) {
1024 NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
1026 return nsnull;
1029 if (!cx) {
1030 cx = GetJSContext(npp);
1032 if (!cx) {
1033 NS_ERROR("Unable to find a JSContext in "
1034 "nsJSObjWrapper::GetNewOrUsed()!");
1036 return nsnull;
1040 JSClass *clazz = JS_GET_CLASS(cx, obj);
1042 if (clazz == &sNPObjectJSWrapperClass) {
1043 // obj is one of our own, its private data is the NPObject we're
1044 // looking for.
1046 NPObject *npobj = (NPObject *)::JS_GetPrivate(cx, obj);
1048 return _retainobject(npobj);
1051 if (!sJSObjWrappers.ops) {
1052 // No hash yet (or any more), initialize it.
1054 static PLDHashTableOps ops =
1056 PL_DHashAllocTable,
1057 PL_DHashFreeTable,
1058 JSObjWrapperHash,
1059 JSObjWrapperHashMatchEntry,
1060 PL_DHashMoveEntryStub,
1061 PL_DHashClearEntryStub,
1062 PL_DHashFinalizeStub
1065 if (!PL_DHashTableInit(&sJSObjWrappers, &ops, nsnull,
1066 sizeof(JSObjWrapperHashEntry), 16)) {
1067 NS_ERROR("Error initializing PLDHashTable!");
1069 return nsnull;
1073 nsJSObjWrapperKey key(obj, npp);
1075 JSObjWrapperHashEntry *entry = static_cast<JSObjWrapperHashEntry *>
1076 (PL_DHashTableOperate(&sJSObjWrappers, &key, PL_DHASH_ADD));
1078 if (!entry) {
1079 // Out of memory.
1080 return nsnull;
1083 if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObjWrapper) {
1084 // Found a live nsJSObjWrapper, return it.
1086 return _retainobject(entry->mJSObjWrapper);
1089 // No existing nsJSObjWrapper, create one.
1091 nsJSObjWrapper *wrapper =
1092 (nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass);
1094 if (!wrapper) {
1095 // OOM? Remove the stale entry from the hash.
1097 PL_DHashTableRawRemove(&sJSObjWrappers, entry);
1099 return nsnull;
1102 wrapper->mJSObj = obj;
1104 entry->mJSObjWrapper = wrapper;
1106 NS_ASSERTION(wrapper->mNpp == npp, "nsJSObjWrapper::mNpp not initialized!");
1108 JSAutoRequest ar(cx);
1110 // Root the JSObject, its lifetime is now tied to that of the
1111 // NPObject.
1112 if (!::JS_AddNamedRoot(cx, &wrapper->mJSObj, "nsJSObjWrapper::mJSObject")) {
1113 NS_ERROR("Failed to root JSObject!");
1115 _releaseobject(wrapper);
1117 PL_DHashTableRawRemove(&sJSObjWrappers, entry);
1119 return nsnull;
1122 return wrapper;
1125 static NPObject *
1126 GetNPObject(JSContext *cx, JSObject *obj)
1128 while (obj && JS_GET_CLASS(cx, obj) != &sNPObjectJSWrapperClass) {
1129 obj = ::JS_GetPrototype(cx, obj);
1132 if (!obj) {
1133 return nsnull;
1136 return (NPObject *)::JS_GetPrivate(cx, obj);
1139 static JSBool
1140 NPObjWrapper_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1142 NPObject *npobj = GetNPObject(cx, obj);
1144 if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1145 !npobj->_class->hasMethod) {
1146 ThrowJSException(cx, "Bad NPObject as private data!");
1148 return JS_FALSE;
1151 // We must permit methods here since JS_DefineUCFunction() will add
1152 // the function as a property
1153 if (!npobj->_class->hasProperty(npobj, (NPIdentifier)id) &&
1154 !npobj->_class->hasMethod(npobj, (NPIdentifier)id)) {
1155 ThrowJSException(cx, "Trying to add unsupported property on scriptable "
1156 "plugin object!");
1158 return JS_FALSE;
1161 return ReportExceptionIfPending(cx);
1164 static JSBool
1165 NPObjWrapper_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1167 NPObject *npobj = GetNPObject(cx, obj);
1169 if (!npobj || !npobj->_class || !npobj->_class->hasProperty) {
1170 ThrowJSException(cx, "Bad NPObject as private data!");
1172 return JS_FALSE;
1175 if (!npobj->_class->hasProperty(npobj, (NPIdentifier)id)) {
1176 ThrowJSException(cx, "Trying to remove unsupported property on scriptable "
1177 "plugin object!");
1179 return JS_FALSE;
1182 return ReportExceptionIfPending(cx);
1185 static JSBool
1186 NPObjWrapper_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1188 NPObject *npobj = GetNPObject(cx, obj);
1190 if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1191 !npobj->_class->setProperty) {
1192 ThrowJSException(cx, "Bad NPObject as private data!");
1194 return JS_FALSE;
1197 if (!npobj->_class->hasProperty(npobj, (NPIdentifier)id)) {
1198 ThrowJSException(cx, "Trying to set unsupported property on scriptable "
1199 "plugin object!");
1201 return JS_FALSE;
1204 // Find out what plugin (NPP) is the owner of the object we're
1205 // manipulating, and make it own any JSObject wrappers created here.
1206 NPP npp = LookupNPP(npobj);
1208 if (!npp) {
1209 ThrowJSException(cx, "No NPP found for NPObject!");
1211 return JS_FALSE;
1214 NPVariant npv;
1215 if (!JSValToNPVariant(npp, cx, *vp, &npv)) {
1216 ThrowJSException(cx, "Error converting jsval to NPVariant!");
1218 return JS_FALSE;
1221 JSBool ok = npobj->_class->setProperty(npobj, (NPIdentifier)id, &npv);
1223 // Release the variant
1224 _releasevariantvalue(&npv);
1226 if (!ok) {
1227 ThrowJSException(cx, "Error setting property on scriptable plugin "
1228 "object!");
1230 return JS_FALSE;
1233 return ReportExceptionIfPending(cx);
1236 static JSBool
1237 NPObjWrapper_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1239 NPObject *npobj = GetNPObject(cx, obj);
1241 if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1242 !npobj->_class->hasMethod || !npobj->_class->getProperty) {
1243 ThrowJSException(cx, "Bad NPObject as private data!");
1245 return JS_FALSE;
1248 PRBool hasProperty = npobj->_class->hasProperty(npobj, (NPIdentifier)id);
1249 PRBool hasMethod = npobj->_class->hasMethod(npobj, (NPIdentifier)id);
1250 NPP npp = nsnull;
1251 if (hasProperty) {
1252 // Find out what plugin (NPP) is the owner of the object we're
1253 // manipulating, and make it own any JSObject wrappers created
1254 // here.
1255 npp = LookupNPP(npobj);
1256 if (!npp) {
1257 ThrowJSException(cx, "No NPP found for NPObject!");
1259 return JS_FALSE;
1263 // To support ambiguous members, we return NPObject Member class here.
1264 if (hasProperty && hasMethod)
1265 return CreateNPObjectMember(npp, cx, obj, npobj, id, vp);
1267 if (hasProperty) {
1268 NPVariant npv;
1269 VOID_TO_NPVARIANT(npv);
1271 if (!npobj->_class->getProperty(npobj, (NPIdentifier)id, &npv)) {
1272 ThrowJSException(cx, "Error setting property on scriptable plugin "
1273 "object!");
1275 return JS_FALSE;
1278 *vp = NPVariantToJSVal(npp, cx, &npv);
1280 // *vp now owns the value, release our reference.
1281 _releasevariantvalue(&npv);
1283 return JS_TRUE;
1286 return ReportExceptionIfPending(cx);
1289 static JSBool
1290 CallNPMethodInternal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1291 jsval *rval, PRBool ctorCall)
1293 while (obj && JS_GET_CLASS(cx, obj) != &sNPObjectJSWrapperClass) {
1294 obj = ::JS_GetPrototype(cx, obj);
1297 if (!obj) {
1298 ThrowJSException(cx, "NPMethod called on non-NPObject wrapped JSObject!");
1300 return JS_FALSE;
1303 NPObject *npobj = (NPObject *)::JS_GetPrivate(cx, obj);
1305 if (!npobj || !npobj->_class || !npobj->_class->invoke) {
1306 ThrowJSException(cx, "Bad NPObject as private data!");
1308 return JS_FALSE;
1311 // Find out what plugin (NPP) is the owner of the object we're
1312 // manipulating, and make it own any JSObject wrappers created here.
1313 NPP npp = LookupNPP(npobj);
1315 if (!npp) {
1316 ThrowJSException(cx, "Error finding NPP for NPObject!");
1318 return JS_FALSE;
1321 NPVariant npargs_buf[8];
1322 NPVariant *npargs = npargs_buf;
1324 if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
1325 // Our stack buffer isn't large enough to hold all arguments,
1326 // malloc a buffer.
1327 npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
1329 if (!npargs) {
1330 ThrowJSException(cx, "Out of memory!");
1332 return JS_FALSE;
1336 // Convert arguments
1337 PRUint32 i;
1338 for (i = 0; i < argc; ++i) {
1339 if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) {
1340 ThrowJSException(cx, "Error converting jsvals to NPVariants!");
1342 if (npargs != npargs_buf) {
1343 PR_Free(npargs);
1346 return JS_FALSE;
1350 NPVariant v;
1351 VOID_TO_NPVARIANT(v);
1353 JSObject *funobj = JSVAL_TO_OBJECT(argv[-2]);
1354 JSBool ok;
1355 const char *msg = "Error calling method on NPObject!";
1357 if (ctorCall) {
1358 // construct a new NPObject based on the NPClass in npobj. Fail if
1359 // no construct method is available.
1361 if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) &&
1362 npobj->_class->construct) {
1363 ok = npobj->_class->construct(npobj, npargs, argc, &v);
1364 } else {
1365 ok = JS_FALSE;
1367 msg = "Attempt to construct object from class with no constructor.";
1369 } else if (funobj != obj) {
1370 // A obj.function() style call is made, get the method name from
1371 // the function object.
1373 if (npobj->_class->invoke) {
1374 JSFunction *fun = (JSFunction *)::JS_GetPrivate(cx, funobj);
1375 jsval method = STRING_TO_JSVAL(::JS_GetFunctionId(fun));
1377 ok = npobj->_class->invoke(npobj, (NPIdentifier)method, npargs, argc,
1378 &v);
1379 } else {
1380 ok = JS_FALSE;
1382 msg = "Attempt to call a method on object with no invoke method.";
1384 } else {
1385 if (npobj->_class->invokeDefault) {
1386 // obj is a callable object that is being called, no method name
1387 // available then. Invoke the default method.
1389 ok = npobj->_class->invokeDefault(npobj, npargs, argc, &v);
1390 } else {
1391 ok = JS_FALSE;
1393 msg = "Attempt to call a default method on object with no "
1394 "invokeDefault method.";
1398 // Release arguments.
1399 for (i = 0; i < argc; ++i) {
1400 _releasevariantvalue(npargs + i);
1403 if (npargs != npargs_buf) {
1404 PR_Free(npargs);
1407 if (!ok) {
1408 ThrowJSException(cx, msg);
1410 return JS_FALSE;
1413 *rval = NPVariantToJSVal(npp, cx, &v);
1415 // *rval now owns the value, release our reference.
1416 _releasevariantvalue(&v);
1418 return ReportExceptionIfPending(cx);
1421 static JSBool
1422 CallNPMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1423 jsval *rval)
1425 return CallNPMethodInternal(cx, obj, argc, argv, rval, PR_FALSE);
1428 struct NPObjectEnumerateState {
1429 PRUint32 index;
1430 PRUint32 length;
1431 NPIdentifier *value;
1434 static JSBool
1435 NPObjWrapper_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1436 jsval *statep, jsid *idp)
1438 NPObject *npobj = GetNPObject(cx, obj);
1439 NPIdentifier *enum_value;
1440 uint32_t length;
1441 NPObjectEnumerateState *state;
1443 if (!npobj || !npobj->_class) {
1444 ThrowJSException(cx, "Bad NPObject as private data!");
1445 return JS_FALSE;
1448 NS_ASSERTION(statep, "Must have a statep to enumerate!");
1450 switch(enum_op) {
1451 case JSENUMERATE_INIT:
1452 state = new NPObjectEnumerateState();
1453 if (!state) {
1454 ThrowJSException(cx, "Memory allocation failed for "
1455 "NPObjectEnumerateState!");
1457 return JS_FALSE;
1460 if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
1461 !npobj->_class->enumerate) {
1462 enum_value = 0;
1463 length = 0;
1464 } else if (!npobj->_class->enumerate(npobj, &enum_value, &length)) {
1465 ThrowJSException(cx, "Error enumerating properties on scriptable "
1466 "plugin object");
1467 delete state;
1469 return JS_FALSE;
1472 state->value = enum_value;
1473 state->length = length;
1474 state->index = 0;
1475 *statep = PRIVATE_TO_JSVAL(state);
1476 if (idp) {
1477 *idp = INT_TO_JSVAL(length);
1480 break;
1482 case JSENUMERATE_NEXT:
1483 state = (NPObjectEnumerateState *)JSVAL_TO_PRIVATE(*statep);
1484 enum_value = state->value;
1485 length = state->length;
1486 if (state->index != length) {
1487 return ::JS_ValueToId(cx, (jsval)enum_value[state->index++], idp);
1490 // FALL THROUGH
1492 case JSENUMERATE_DESTROY:
1493 state = (NPObjectEnumerateState *)JSVAL_TO_PRIVATE(*statep);
1494 if (state->value)
1495 PR_Free(state->value);
1496 delete state;
1497 *statep = JSVAL_NULL;
1499 break;
1502 return JS_TRUE;
1505 static JSBool
1506 NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1507 JSObject **objp)
1509 NPObject *npobj = GetNPObject(cx, obj);
1511 if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
1512 !npobj->_class->hasMethod) {
1513 ThrowJSException(cx, "Bad NPObject as private data!");
1515 return JS_FALSE;
1518 if (npobj->_class->hasProperty(npobj, (NPIdentifier)id)) {
1519 JSBool ok;
1521 if (JSVAL_IS_STRING(id)) {
1522 JSString *str = JSVAL_TO_STRING(id);
1524 ok = ::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str),
1525 ::JS_GetStringLength(str), JSVAL_VOID, nsnull,
1526 nsnull, JSPROP_ENUMERATE);
1527 } else {
1528 ok = ::JS_DefineElement(cx, obj, JSVAL_TO_INT(id), JSVAL_VOID, nsnull,
1529 nsnull, JSPROP_ENUMERATE);
1532 if (!ok) {
1533 return JS_FALSE;
1536 *objp = obj;
1537 } else if (npobj->_class->hasMethod(npobj, (NPIdentifier)id)) {
1538 JSString *str = nsnull;
1540 if (JSVAL_IS_STRING(id)) {
1541 str = JSVAL_TO_STRING(id);
1542 } else {
1543 NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
1545 str = ::JS_ValueToString(cx, id);
1547 if (!str) {
1548 // OOM. The JS engine throws exceptions for us in this case.
1550 return JS_FALSE;
1554 JSFunction *fnc =
1555 ::JS_DefineUCFunction(cx, obj, ::JS_GetStringChars(str),
1556 ::JS_GetStringLength(str), CallNPMethod, 0,
1557 JSPROP_ENUMERATE);
1559 *objp = obj;
1561 return fnc != nsnull;
1564 return ReportExceptionIfPending(cx);
1567 static JSBool
1568 NPObjWrapper_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1570 // The sole reason we implement this hook is to prevent the JS
1571 // engine from calling valueOf() on NPObject's. Some NPObject's may
1572 // actually implement a method named valueOf, but it's unlikely to
1573 // behave as the JS engine expects it to. IOW, this is an empty hook
1574 // that overrides what the default hook does.
1576 return JS_TRUE;
1579 static void
1580 NPObjWrapper_Finalize(JSContext *cx, JSObject *obj)
1582 NPObject *npobj = (NPObject *)::JS_GetPrivate(cx, obj);
1584 if (npobj) {
1585 if (sNPObjWrappers.ops) {
1586 PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_REMOVE);
1589 // Let go of our NPObject
1590 _releaseobject(npobj);
1593 OnWrapperDestroyed();
1596 static JSBool
1597 NPObjWrapper_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1598 jsval *rval)
1600 return CallNPMethodInternal(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval,
1601 PR_FALSE);
1604 static JSBool
1605 NPObjWrapper_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1606 jsval *rval)
1608 return CallNPMethodInternal(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval,
1609 PR_TRUE);
1612 class NPObjWrapperHashEntry : public PLDHashEntryHdr
1614 public:
1615 NPObject *mNPObj; // Must be the first member for the PLDHash stubs to work
1616 JSObject *mJSObj;
1617 NPP mNpp;
1621 // An NPObject is going away, make sure we null out the JS object's
1622 // private data in case this is an NPObject that came from a plugin
1623 // and it's destroyed prematurely.
1625 // static
1626 void
1627 nsNPObjWrapper::OnDestroy(NPObject *npobj)
1629 if (!npobj) {
1630 return;
1633 if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
1634 // npobj is one of our own, no private data to clean up here.
1636 return;
1639 if (!sNPObjWrappers.ops) {
1640 // No hash yet (or any more), no used wrappers available.
1642 return;
1645 NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
1646 (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
1648 if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
1649 // Found a live NPObject wrapper, null out its JSObjects' private
1650 // data.
1652 JSContext *cx = GetJSContext(entry->mNpp);
1654 if (cx) {
1655 ::JS_SetPrivate(cx, entry->mJSObj, nsnull);
1658 // Remove the npobj from the hash now that it went away.
1659 PL_DHashTableRawRemove(&sNPObjWrappers, entry);
1661 OnWrapperDestroyed();
1665 // Look up or create a JSObject that wraps the NPObject npobj.
1667 // static
1668 JSObject *
1669 nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj)
1671 if (!npobj) {
1672 NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!");
1674 return nsnull;
1677 if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
1678 // npobj is one of our own, return its existing JSObject.
1680 return ((nsJSObjWrapper *)npobj)->mJSObj;
1683 if (!npp) {
1684 NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
1686 return nsnull;
1689 if (!sNPObjWrappers.ops) {
1690 // No hash yet (or any more), initialize it.
1692 if (!PL_DHashTableInit(&sNPObjWrappers, PL_DHashGetStubOps(), nsnull,
1693 sizeof(NPObjWrapperHashEntry), 16)) {
1694 NS_ERROR("Error initializing PLDHashTable!");
1696 return nsnull;
1700 NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
1701 (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD));
1703 if (!entry) {
1704 // Out of memory
1705 JS_ReportOutOfMemory(cx);
1707 return nsnull;
1710 if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
1711 // Found a live NPObject wrapper, return it.
1712 return entry->mJSObj;
1715 entry->mNPObj = npobj;
1716 entry->mNpp = npp;
1718 JSAutoRequest ar(cx);
1720 PRUint32 generation = sNPObjWrappers.generation;
1722 // No existing JSObject, create one.
1724 JSObject *obj = ::JS_NewObject(cx, &sNPObjectJSWrapperClass, nsnull, nsnull);
1726 if (generation != sNPObjWrappers.generation) {
1727 // Reload entry if the JS_NewObject call caused a GC and reallocated
1728 // the table (see bug 445229). This is guaranteed to succeed.
1730 entry = static_cast<NPObjWrapperHashEntry *>
1731 (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
1732 NS_ASSERTION(entry && PL_DHASH_ENTRY_IS_BUSY(entry),
1733 "Hashtable didn't find what we just added?");
1736 if (!obj) {
1737 // OOM? Remove the stale entry from the hash.
1739 PL_DHashTableRawRemove(&sNPObjWrappers, entry);
1741 return nsnull;
1744 OnWrapperCreated();
1746 entry->mJSObj = obj;
1748 // JS_SetPrivate() never fails.
1749 ::JS_SetPrivate(cx, obj, npobj);
1751 // The new JSObject now holds on to npobj
1752 _retainobject(npobj);
1754 return obj;
1758 // PLDHashTable enumeration callbacks for destruction code.
1759 static PLDHashOperator
1760 JSObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
1761 PRUint32 number, void *arg)
1763 JSObjWrapperHashEntry *entry = (JSObjWrapperHashEntry *)hdr;
1765 nsJSObjWrapper *npobj = entry->mJSObjWrapper;
1767 if (npobj->mNpp == arg) {
1768 // Prevent invalidate() and _releaseobject() from touching the hash
1769 // we're enumerating.
1770 const PLDHashTableOps *ops = table->ops;
1771 table->ops = nsnull;
1773 if (npobj->_class && npobj->_class->invalidate) {
1774 npobj->_class->invalidate(npobj);
1777 _releaseobject(npobj);
1779 table->ops = ops;
1781 return PL_DHASH_REMOVE;
1784 return PL_DHASH_NEXT;
1787 // Struct for passing an NPP and a JSContext to
1788 // NPObjWrapperPluginDestroyedCallback
1789 struct NppAndCx
1791 NPP npp;
1792 JSContext *cx;
1795 static PLDHashOperator
1796 NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
1797 PRUint32 number, void *arg)
1799 NPObjWrapperHashEntry *entry = (NPObjWrapperHashEntry *)hdr;
1800 NppAndCx *nppcx = reinterpret_cast<NppAndCx *>(arg);
1802 if (entry->mNpp == nppcx->npp) {
1803 // Prevent invalidate() and deallocate() from touching the hash
1804 // we're enumerating.
1805 const PLDHashTableOps *ops = table->ops;
1806 table->ops = nsnull;
1808 NPObject *npobj = entry->mNPObj;
1810 if (npobj->_class && npobj->_class->invalidate) {
1811 npobj->_class->invalidate(npobj);
1814 // Force deallocation of plugin objects since the plugin they came
1815 // from is being torn down.
1816 if (npobj->_class && npobj->_class->deallocate) {
1817 npobj->_class->deallocate(npobj);
1818 } else {
1819 PR_Free(npobj);
1822 ::JS_SetPrivate(nppcx->cx, entry->mJSObj, nsnull);
1824 table->ops = ops;
1826 return PL_DHASH_REMOVE;
1829 return PL_DHASH_NEXT;
1832 // static
1833 void
1834 nsJSNPRuntime::OnPluginDestroy(NPP npp)
1836 if (sJSObjWrappers.ops) {
1837 PL_DHashTableEnumerate(&sJSObjWrappers,
1838 JSObjWrapperPluginDestroyedCallback, npp);
1841 // Use the safe JSContext here as we're not always able to find the
1842 // JSContext associated with the NPP any more.
1844 nsCOMPtr<nsIThreadJSContextStack> stack =
1845 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
1846 if (!stack) {
1847 NS_ERROR("No context stack available!");
1849 return;
1852 JSContext *cx;
1853 stack->GetSafeJSContext(&cx);
1854 if (!cx) {
1855 NS_ERROR("No safe JS context available!");
1857 return;
1860 JSAutoRequest ar(cx);
1862 if (sNPObjWrappers.ops) {
1863 NppAndCx nppcx = { npp, cx };
1864 PL_DHashTableEnumerate(&sNPObjWrappers,
1865 NPObjWrapperPluginDestroyedCallback, &nppcx);
1868 // If this plugin was scripted from a webpage, the plugin's
1869 // scriptable object will be on the DOM element's prototype
1870 // chain. Now that the plugin is being destroyed we need to pull the
1871 // plugin's scriptable object out of that prototype chain.
1872 if (!npp) {
1873 return;
1876 // Find the plugin instance so that we can (eventually) get to the
1877 // DOM element
1878 nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
1879 if (!inst) {
1880 return;
1883 nsCOMPtr<nsIPluginInstancePeer> pip;
1884 inst->GetPeer(getter_AddRefs(pip));
1885 nsCOMPtr<nsIPluginTagInfo2> pti2(do_QueryInterface(pip));
1886 if (!pti2) {
1887 return;
1890 nsCOMPtr<nsIDOMElement> element;
1891 pti2->GetDOMElement(getter_AddRefs(element));
1892 if (!element) {
1893 return;
1896 // Get the DOM element's JS object.
1897 nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
1898 if (!xpc) {
1899 return;
1902 // OK. Now we have to get our hands on the right scope object, since
1903 // GetWrappedNativeOfNativeObject doesn't call PreCreate and hence won't get
1904 // the right scope if we pass in something bogus. The right scope lives on
1905 // the script global of the element's document.
1906 // XXXbz we MUST have a better way of doing this... perhaps
1907 // GetWrappedNativeOfNativeObject _should_ call preCreate?
1908 nsCOMPtr<nsIContent> content(do_QueryInterface(element));
1909 if (!content) {
1910 return;
1913 nsIDocument* doc = content->GetOwnerDoc();
1914 if (!doc) {
1915 return;
1918 nsIScriptGlobalObject* sgo = doc->GetScriptGlobalObject();
1919 if (!sgo) {
1920 return;
1923 nsCOMPtr<nsISupports> supp(do_QueryInterface(element));
1924 nsCOMPtr<nsIXPConnectWrappedNative> holder;
1925 xpc->GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), supp,
1926 NS_GET_IID(nsISupports),
1927 getter_AddRefs(holder));
1928 if (!holder) {
1929 return;
1932 JSObject *obj, *proto;
1933 holder->GetJSObject(&obj);
1935 // Loop over the DOM element's JS object prototype chain and remove
1936 // all JS objects of the class sNPObjectJSWrapperClass (there should
1937 // be only one, but remove all instances found in case the page put
1938 // more than one of the plugin's scriptable objects on the prototype
1939 // chain).
1940 while (obj && (proto = ::JS_GetPrototype(cx, obj))) {
1941 if (JS_GET_CLASS(cx, proto) == &sNPObjectJSWrapperClass) {
1942 // We found an NPObject on the proto chain, get its prototype...
1943 proto = ::JS_GetPrototype(cx, proto);
1945 // ... and pull it out of the chain.
1946 ::JS_SetPrototype(cx, obj, proto);
1949 obj = proto;
1954 // Find the NPP for a NPObject.
1955 static NPP
1956 LookupNPP(NPObject *npobj)
1958 if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
1959 NS_ERROR("NPP requested for NPObject of class "
1960 "nsJSObjWrapper::sJSObjWrapperNPClass!\n");
1962 return nsnull;
1965 NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
1966 (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD));
1968 if (PL_DHASH_ENTRY_IS_FREE(entry)) {
1969 return nsnull;
1972 NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
1974 return entry->mNpp;
1977 bool
1978 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj,
1979 NPObject* npobj, jsval id, jsval *vp)
1981 NS_ENSURE_TRUE(vp, false);
1983 if (!npobj || !npobj->_class || !npobj->_class->getProperty ||
1984 !npobj->_class->invoke) {
1985 ThrowJSException(cx, "Bad NPObject");
1987 return false;
1990 NPObjectMemberPrivate *memberPrivate =
1991 (NPObjectMemberPrivate *)PR_Malloc(sizeof(NPObjectMemberPrivate));
1992 if (!memberPrivate)
1993 return false;
1995 // Make sure to clear all members in case something fails here
1996 // during initialization.
1997 memset(memberPrivate, 0, sizeof(NPObjectMemberPrivate));
1999 JSObject *memobj = ::JS_NewObject(cx, &sNPObjectMemberClass, nsnull, nsnull);
2000 if (!memobj) {
2001 PR_Free(memberPrivate);
2002 return false;
2005 *vp = OBJECT_TO_JSVAL(memobj);
2006 ::JS_AddRoot(cx, vp);
2008 ::JS_SetPrivate(cx, memobj, (void *)memberPrivate);
2010 jsval fieldValue;
2011 NPVariant npv;
2012 VOID_TO_NPVARIANT(npv);
2013 if (!npobj->_class->getProperty(npobj, (NPIdentifier)id, &npv)) {
2014 ::JS_RemoveRoot(cx, vp);
2015 return false;
2018 fieldValue = NPVariantToJSVal(npp, cx, &npv);
2020 // npobjWrapper is the JSObject through which we make sure we don't
2021 // outlive the underlying NPObject, so make sure it points to the
2022 // real JSObject wrapper for the NPObject.
2023 while (JS_GET_CLASS(cx, obj) != &sNPObjectJSWrapperClass) {
2024 obj = ::JS_GetPrototype(cx, obj);
2027 memberPrivate->npobjWrapper = obj;
2029 memberPrivate->fieldValue = fieldValue;
2030 memberPrivate->methodName = id;
2031 memberPrivate->npp = npp;
2033 ::JS_RemoveRoot(cx, vp);
2035 return true;
2038 static JSBool
2039 NPObjectMember_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
2041 NPObjectMemberPrivate *memberPrivate =
2042 (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
2043 &sNPObjectMemberClass,
2044 nsnull);
2045 NS_ASSERTION(memberPrivate, "no Ambiguous Member Private data!");
2047 switch (type) {
2048 case JSTYPE_VOID:
2049 case JSTYPE_STRING:
2050 case JSTYPE_NUMBER:
2051 case JSTYPE_BOOLEAN:
2052 case JSTYPE_OBJECT:
2053 *vp = memberPrivate->fieldValue;
2054 return JS_TRUE;
2055 case JSTYPE_FUNCTION:
2056 // Leave this to NPObjectMember_Call.
2057 return JS_TRUE;
2058 default:
2059 NS_ERROR("illegal operation on JSObject prototype object");
2060 return JS_FALSE;
2064 static void
2065 NPObjectMember_Finalize(JSContext *cx, JSObject *obj)
2067 NPObjectMemberPrivate *memberPrivate;
2069 memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(cx, obj);
2070 if (!memberPrivate)
2071 return;
2073 PR_Free(memberPrivate);
2076 static JSBool
2077 NPObjectMember_Call(JSContext *cx, JSObject *obj,
2078 uintN argc, jsval *argv, jsval *rval)
2080 JSObject *memobj = JSVAL_TO_OBJECT(argv[-2]);
2081 NS_ENSURE_TRUE(memobj, JS_FALSE);
2083 NPObjectMemberPrivate *memberPrivate =
2084 (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
2085 &sNPObjectMemberClass,
2086 argv);
2087 if (!memberPrivate || !memberPrivate->npobjWrapper)
2088 return JS_FALSE;
2090 NPObject *npobj = GetNPObject(cx, memberPrivate->npobjWrapper);
2091 if (!npobj) {
2092 ThrowJSException(cx, "Call on invalid member object");
2094 return JS_FALSE;
2097 NPVariant npargs_buf[8];
2098 NPVariant *npargs = npargs_buf;
2100 if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
2101 // Our stack buffer isn't large enough to hold all arguments,
2102 // malloc a buffer.
2103 npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
2105 if (!npargs) {
2106 ThrowJSException(cx, "Out of memory!");
2108 return JS_FALSE;
2112 // Convert arguments
2113 PRUint32 i;
2114 for (i = 0; i < argc; ++i) {
2115 if (!JSValToNPVariant(memberPrivate->npp, cx, argv[i], npargs + i)) {
2116 ThrowJSException(cx, "Error converting jsvals to NPVariants!");
2118 if (npargs != npargs_buf) {
2119 PR_Free(npargs);
2122 return JS_FALSE;
2126 NPVariant npv;
2127 JSBool ok;
2128 ok = npobj->_class->invoke(npobj, (NPIdentifier)memberPrivate->methodName,
2129 npargs, argc, &npv);
2131 // Release arguments.
2132 for (i = 0; i < argc; ++i) {
2133 _releasevariantvalue(npargs + i);
2136 if (npargs != npargs_buf) {
2137 PR_Free(npargs);
2140 if (!ok) {
2141 ThrowJSException(cx, "Error calling method on NPObject!");
2143 return JS_FALSE;
2146 *rval = NPVariantToJSVal(memberPrivate->npp, cx, &npv);
2148 // *rval now owns the value, release our reference.
2149 _releasevariantvalue(&npv);
2151 return ReportExceptionIfPending(cx);
2154 static uint32
2155 NPObjectMember_Mark(JSContext *cx, JSObject *obj, void *arg)
2157 NPObjectMemberPrivate *memberPrivate =
2158 (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
2159 &sNPObjectMemberClass,
2160 nsnull);
2161 if (!memberPrivate)
2162 return 0;
2164 if (!JSVAL_IS_PRIMITIVE(memberPrivate->fieldValue)) {
2165 ::JS_MarkGCThing(cx, JSVAL_TO_OBJECT(memberPrivate->fieldValue),
2166 "NPObject Member => fieldValue", arg);
2169 // There's no strong reference from our private data to the
2170 // NPObject, so make sure to mark the NPObject wrapper to keep the
2171 // NPObject alive as long as this NPObjectMember is alive.
2172 if (memberPrivate->npobjWrapper) {
2173 ::JS_MarkGCThing(cx, memberPrivate->npobjWrapper,
2174 "NPObject Member => npobjWrapper", arg);
2177 return 0;