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
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.
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"
52 #include "nsIContent.h"
54 // FIXME(bug 332648): Give me a real API please!
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
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
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
89 AutoJSExceptionReporter(JSContext
*cx
)
94 ~AutoJSExceptionReporter()
96 JS_ReportPendingException(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
122 NPObjWrapper_AddProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
);
125 NPObjWrapper_DelProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
);
128 NPObjWrapper_SetProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
);
131 NPObjWrapper_GetProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
);
134 NPObjWrapper_newEnumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
135 jsval
*statep
, jsid
*idp
);
138 NPObjWrapper_NewResolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
142 NPObjWrapper_Convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
);
145 NPObjWrapper_Finalize(JSContext
*cx
, JSObject
*obj
);
148 NPObjWrapper_Call(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
152 NPObjWrapper_Construct(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
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
;
176 } NPObjectMemberPrivate
;
179 NPObjectMember_Convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
);
182 NPObjectMember_Finalize(JSContext
*cx
, JSObject
*obj
);
185 NPObjectMember_Call(JSContext
*cx
, JSObject
*obj
, uintN argc
,
186 jsval
*argv
, jsval
*rval
);
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
204 if (sWrapperCount
++ == 0) {
205 static const char rtsvc_id
[] = "@mozilla.org/js/xpc/RuntimeService;1";
206 nsCOMPtr
<nsIJSRuntimeService
> rtsvc(do_GetService(rtsvc_id
));
210 rtsvc
->GetRuntime(&sJSRuntime
);
211 NS_ASSERTION(sJSRuntime
!= nsnull
, "no JSRuntime?!");
213 CallGetService("@mozilla.org/js/xpc/ContextStack;1", &sContextStack
);
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.
246 NS_IF_RELEASE(sContextStack
);
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.
264 sContextStack
->Push(cx
);
269 JSContext
*cx
= nsnull
;
270 sContextStack
->Pop(&cx
);
272 JSContext
*currentCx
= nsnull
;
273 sContextStack
->Peek(¤tCx
);
276 // No JS is running, tell the context we're done executing
279 nsIScriptContext
*scx
= GetScriptContextFromJSContext(cx
);
282 scx
->ScriptEvaluated(PR_TRUE
);
286 OnWrapperDestroyed();
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();
321 LookupNPP(NPObject
*npobj
);
325 NPVariantToJSVal(NPP npp
, JSContext
*cx
, const NPVariant
*variant
)
327 switch (variant
->type
) {
328 case NPVariantType_Void
:
330 case NPVariantType_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.
339 if (::JS_NewNumberValue(cx
, NPVARIANT_TO_INT32(*variant
), &val
)) {
345 case NPVariantType_Double
:
348 if (::JS_NewNumberValue(cx
, NPVARIANT_TO_DOUBLE(*variant
), &val
)) {
354 case NPVariantType_String
:
356 const NPString
*s
= &NPVARIANT_TO_STRING(*variant
);
357 NS_ConvertUTF8toUTF16
utf16String(s
->UTF8Characters
, s
->UTF8Length
);
360 ::JS_NewUCStringCopyN(cx
, reinterpret_cast<const jschar
*>
362 utf16String
.Length());
365 return STRING_TO_JSVAL(str
);
370 case NPVariantType_Object
:
374 nsNPObjWrapper::GetNewOrUsed(npp
, cx
, NPVARIANT_TO_OBJECT(*variant
));
377 return OBJECT_TO_JSVAL(obj
);
381 NS_ERROR("Error wrapping NPObject!");
386 NS_ERROR("Unknown NPVariant type!");
389 NS_ERROR("Unable to convert NPVariant to jsval!");
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
));
416 char *p
= ToNewUTF8String(str
, &len
);
422 STRINGN_TO_NPVARIANT(p
, len
, *variant
);
424 NS_ERROR("Unknown primitive type!");
433 nsJSObjWrapper::GetNewOrUsed(npp
, cx
, JSVAL_TO_OBJECT(val
));
438 // Pass over ownership of npobj to *variant
439 OBJECT_TO_NPVARIANT(npobj
, *variant
);
445 ThrowJSException(JSContext
*cx
, const char *message
)
447 const char *ex
= PeekException();
453 AppendASCIItoUTF16(message
, ucex
);
455 AppendASCIItoUTF16(" [plugin exception: ", ucex
);
458 AppendUTF8toUTF16(ex
, ucex
);
461 AppendASCIItoUTF16("].", ucex
);
464 JSString
*str
= ::JS_NewUCStringCopyN(cx
, (jschar
*)ucex
.get(),
468 ::JS_SetPendingException(cx
, STRING_TO_JSVAL(str
));
473 ::JS_ReportError(cx
, message
);
478 ReportExceptionIfPending(JSContext
*cx
)
480 const char *ex
= PeekException();
486 ThrowJSException(cx
, nsnull
);
492 nsJSObjWrapper::nsJSObjWrapper(NPP npp
)
493 : nsJSObjWrapperKey(nsnull
, npp
)
498 nsJSObjWrapper::~nsJSObjWrapper()
500 // Invalidate first, since it relies on sJSRuntime and sJSObjWrappers.
503 OnWrapperDestroyed();
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
);
518 nsJSObjWrapper::NP_Deallocate(NPObject
*npobj
)
520 // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate().
521 delete (nsJSObjWrapper
*)npobj
;
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
;
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
);
565 nsJSObjWrapper::NP_HasMethod(NPObject
*npobj
, NPIdentifier identifier
)
567 NPP npp
= NPPStack::Peek();
568 JSContext
*cx
= GetJSContext(npp
);
571 NS_ERROR("Null cx in nsJSObjWrapper::NP_HasMethod!");
577 "Null npobj in nsJSObjWrapper::NP_HasMethod!");
582 nsJSObjWrapper
*npjsobj
= (nsJSObjWrapper
*)npobj
;
584 AutoCXPusher
pusher(cx
);
585 JSAutoRequest
ar(cx
);
586 AutoJSExceptionReporter
reporter(cx
);
589 JSBool ok
= GetProperty(cx
, npjsobj
->mJSObj
, identifier
, &v
);
591 return ok
&& !JSVAL_IS_PRIMITIVE(v
) &&
592 ::JS_ObjectIsFunction(cx
, JSVAL_TO_OBJECT(v
));
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
);
603 NS_ERROR("Null cx in doInvoke!");
607 if (!npobj
|| !result
) {
608 ThrowJSException(cx
, "Null npobj, or result in doInvoke!");
613 // Initialize *result
614 VOID_TO_NPVARIANT(*result
);
616 nsJSObjWrapper
*npjsobj
= (nsJSObjWrapper
*)npobj
;
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
) {
629 fv
= OBJECT_TO_JSVAL(npjsobj
->mJSObj
);
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,
638 jsargs
= (jsval
*)PR_Malloc(argCount
* sizeof(jsval
));
640 ::JS_ReportOutOfMemory(cx
);
646 JSTempValueRooter tvr
;
647 JS_PUSH_TEMP_ROOT(cx
, 0, jsargs
, &tvr
);
650 for (PRUint32 i
= 0; i
< argCount
; ++i
) {
651 jsargs
[i
] = NPVariantToJSVal(npp
, cx
, args
+ i
);
659 JSObject
*global
= ::JS_GetGlobalForObject(cx
, npjsobj
->mJSObj
);
661 ::JS_ConstructObjectWithArguments(cx
, JS_GET_CLASS(cx
, npjsobj
->mJSObj
),
662 nsnull
, global
, argCount
, jsargs
);
665 v
= OBJECT_TO_JSVAL(newObj
);
671 ok
= ::JS_CallFunctionValue(cx
, npjsobj
->mJSObj
, fv
, argCount
, jsargs
, &v
);
674 JS_POP_TEMP_ROOT(cx
, &tvr
);
676 if (jsargs
!= jsargs_buf
)
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
;
689 nsJSObjWrapper::NP_Invoke(NPObject
*npobj
, NPIdentifier method
,
690 const NPVariant
*args
, uint32_t argCount
,
693 if ((jsval
)method
== JSVAL_VOID
) {
697 return doInvoke(npobj
, method
, args
, argCount
, PR_FALSE
, result
);
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
,
711 nsJSObjWrapper::NP_HasProperty(NPObject
*npobj
, NPIdentifier identifier
)
713 NPP npp
= NPPStack::Peek();
714 JSContext
*cx
= GetJSContext(npp
);
717 NS_ERROR("Null cx in nsJSObjWrapper::NP_HasProperty!");
723 "Null npobj in nsJSObjWrapper::NP_HasProperty!");
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
);
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
);
752 nsJSObjWrapper::NP_GetProperty(NPObject
*npobj
, NPIdentifier identifier
,
755 NPP npp
= NPPStack::Peek();
756 JSContext
*cx
= GetJSContext(npp
);
759 NS_ERROR("Null cx in nsJSObjWrapper::NP_GetProperty!");
765 "Null npobj in nsJSObjWrapper::NP_GetProperty!");
770 nsJSObjWrapper
*npjsobj
= (nsJSObjWrapper
*)npobj
;
772 AutoCXPusher
pusher(cx
);
773 JSAutoRequest
ar(cx
);
774 AutoJSExceptionReporter
reporter(cx
);
777 return (GetProperty(cx
, npjsobj
->mJSObj
, identifier
, &v
) &&
778 JSValToNPVariant(npp
, cx
, v
, result
));
783 nsJSObjWrapper::NP_SetProperty(NPObject
*npobj
, NPIdentifier identifier
,
784 const NPVariant
*value
)
786 NPP npp
= NPPStack::Peek();
787 JSContext
*cx
= GetJSContext(npp
);
790 NS_ERROR("Null cx in nsJSObjWrapper::NP_SetProperty!");
796 "Null npobj in nsJSObjWrapper::NP_SetProperty!");
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
);
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
;
830 nsJSObjWrapper::NP_RemoveProperty(NPObject
*npobj
, NPIdentifier identifier
)
832 NPP npp
= NPPStack::Peek();
833 JSContext
*cx
= GetJSContext(npp
);
836 NS_ERROR("Null cx in nsJSObjWrapper::NP_RemoveProperty!");
842 "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
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
);
863 // FIXME: See bug 425823, we shouldn't need to do this, and once
864 // that bug is fixed we can remove this code.
867 ok
= ::JS_HasUCProperty(cx
, npjsobj
->mJSObj
, ::JS_GetStringChars(str
),
868 ::JS_GetStringLength(str
), &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
;
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
);
883 // FIXME: See bug 425823, we shouldn't need to do this, and once
884 // that bug is fixed we can remove this code.
887 ok
= ::JS_HasElement(cx
, npjsobj
->mJSObj
, JSVAL_TO_INT(id
), &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
;
905 nsJSObjWrapper::NP_Enumerate(NPObject
*npobj
, NPIdentifier
**identifier
,
908 NPP npp
= NPPStack::Peek();
909 JSContext
*cx
= GetJSContext(npp
);
915 NS_ERROR("Null cx in nsJSObjWrapper::NP_Enumerate!");
921 "Null npobj in nsJSObjWrapper::NP_Enumerate!");
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
);
937 *count
= ida
->length
;
938 *identifier
= (NPIdentifier
*)PR_Malloc(*count
* sizeof(NPIdentifier
));
940 ThrowJSException(cx
, "Memory allocation failed for NPIdentifier!");
942 ::JS_DestroyIdArray(cx
, ida
);
947 for (PRUint32 i
= 0; i
< *count
; i
++) {
949 if (!::JS_IdToValue(cx
, ida
->vector
[i
], &v
)) {
950 ::JS_DestroyIdArray(cx
, ida
);
951 PR_Free(*identifier
);
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
);
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
);
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
,
988 class JSObjWrapperHashEntry
: public PLDHashEntryHdr
991 nsJSObjWrapper
*mJSObjWrapper
;
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;
1004 JSObjWrapperHashMatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*entry
,
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.
1021 nsJSObjWrapper::GetNewOrUsed(NPP npp
, JSContext
*cx
, JSObject
*obj
)
1024 NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
1030 cx
= GetJSContext(npp
);
1033 NS_ERROR("Unable to find a JSContext in "
1034 "nsJSObjWrapper::GetNewOrUsed()!");
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
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
=
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!");
1073 nsJSObjWrapperKey
key(obj
, npp
);
1075 JSObjWrapperHashEntry
*entry
= static_cast<JSObjWrapperHashEntry
*>
1076 (PL_DHashTableOperate(&sJSObjWrappers
, &key
, PL_DHASH_ADD
));
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
);
1095 // OOM? Remove the stale entry from the hash.
1097 PL_DHashTableRawRemove(&sJSObjWrappers
, entry
);
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
1112 if (!::JS_AddNamedRoot(cx
, &wrapper
->mJSObj
, "nsJSObjWrapper::mJSObject")) {
1113 NS_ERROR("Failed to root JSObject!");
1115 _releaseobject(wrapper
);
1117 PL_DHashTableRawRemove(&sJSObjWrappers
, entry
);
1126 GetNPObject(JSContext
*cx
, JSObject
*obj
)
1128 while (obj
&& JS_GET_CLASS(cx
, obj
) != &sNPObjectJSWrapperClass
) {
1129 obj
= ::JS_GetPrototype(cx
, obj
);
1136 return (NPObject
*)::JS_GetPrivate(cx
, obj
);
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!");
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 "
1161 return ReportExceptionIfPending(cx
);
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!");
1175 if (!npobj
->_class
->hasProperty(npobj
, (NPIdentifier
)id
)) {
1176 ThrowJSException(cx
, "Trying to remove unsupported property on scriptable "
1182 return ReportExceptionIfPending(cx
);
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!");
1197 if (!npobj
->_class
->hasProperty(npobj
, (NPIdentifier
)id
)) {
1198 ThrowJSException(cx
, "Trying to set unsupported property on scriptable "
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
);
1209 ThrowJSException(cx
, "No NPP found for NPObject!");
1215 if (!JSValToNPVariant(npp
, cx
, *vp
, &npv
)) {
1216 ThrowJSException(cx
, "Error converting jsval to NPVariant!");
1221 JSBool ok
= npobj
->_class
->setProperty(npobj
, (NPIdentifier
)id
, &npv
);
1223 // Release the variant
1224 _releasevariantvalue(&npv
);
1227 ThrowJSException(cx
, "Error setting property on scriptable plugin "
1233 return ReportExceptionIfPending(cx
);
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!");
1248 PRBool hasProperty
= npobj
->_class
->hasProperty(npobj
, (NPIdentifier
)id
);
1249 PRBool hasMethod
= npobj
->_class
->hasMethod(npobj
, (NPIdentifier
)id
);
1252 // Find out what plugin (NPP) is the owner of the object we're
1253 // manipulating, and make it own any JSObject wrappers created
1255 npp
= LookupNPP(npobj
);
1257 ThrowJSException(cx
, "No NPP found for NPObject!");
1263 // To support ambiguous members, we return NPObject Member class here.
1264 if (hasProperty
&& hasMethod
)
1265 return CreateNPObjectMember(npp
, cx
, obj
, npobj
, id
, vp
);
1269 VOID_TO_NPVARIANT(npv
);
1271 if (!npobj
->_class
->getProperty(npobj
, (NPIdentifier
)id
, &npv
)) {
1272 ThrowJSException(cx
, "Error setting property on scriptable plugin "
1278 *vp
= NPVariantToJSVal(npp
, cx
, &npv
);
1280 // *vp now owns the value, release our reference.
1281 _releasevariantvalue(&npv
);
1286 return ReportExceptionIfPending(cx
);
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
);
1298 ThrowJSException(cx
, "NPMethod called on non-NPObject wrapped JSObject!");
1303 NPObject
*npobj
= (NPObject
*)::JS_GetPrivate(cx
, obj
);
1305 if (!npobj
|| !npobj
->_class
|| !npobj
->_class
->invoke
) {
1306 ThrowJSException(cx
, "Bad NPObject as private data!");
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
);
1316 ThrowJSException(cx
, "Error finding NPP for NPObject!");
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,
1327 npargs
= (NPVariant
*)PR_Malloc(argc
* sizeof(NPVariant
));
1330 ThrowJSException(cx
, "Out of memory!");
1336 // Convert arguments
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
) {
1351 VOID_TO_NPVARIANT(v
);
1353 JSObject
*funobj
= JSVAL_TO_OBJECT(argv
[-2]);
1355 const char *msg
= "Error calling method on NPObject!";
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
);
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
,
1382 msg
= "Attempt to call a method on object with no invoke method.";
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
);
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
) {
1408 ThrowJSException(cx
, msg
);
1413 *rval
= NPVariantToJSVal(npp
, cx
, &v
);
1415 // *rval now owns the value, release our reference.
1416 _releasevariantvalue(&v
);
1418 return ReportExceptionIfPending(cx
);
1422 CallNPMethod(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
1425 return CallNPMethodInternal(cx
, obj
, argc
, argv
, rval
, PR_FALSE
);
1428 struct NPObjectEnumerateState
{
1431 NPIdentifier
*value
;
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
;
1441 NPObjectEnumerateState
*state
;
1443 if (!npobj
|| !npobj
->_class
) {
1444 ThrowJSException(cx
, "Bad NPObject as private data!");
1448 NS_ASSERTION(statep
, "Must have a statep to enumerate!");
1451 case JSENUMERATE_INIT
:
1452 state
= new NPObjectEnumerateState();
1454 ThrowJSException(cx
, "Memory allocation failed for "
1455 "NPObjectEnumerateState!");
1460 if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj
->_class
) ||
1461 !npobj
->_class
->enumerate
) {
1464 } else if (!npobj
->_class
->enumerate(npobj
, &enum_value
, &length
)) {
1465 ThrowJSException(cx
, "Error enumerating properties on scriptable "
1472 state
->value
= enum_value
;
1473 state
->length
= length
;
1475 *statep
= PRIVATE_TO_JSVAL(state
);
1477 *idp
= INT_TO_JSVAL(length
);
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
);
1492 case JSENUMERATE_DESTROY
:
1493 state
= (NPObjectEnumerateState
*)JSVAL_TO_PRIVATE(*statep
);
1495 PR_Free(state
->value
);
1497 *statep
= JSVAL_NULL
;
1506 NPObjWrapper_NewResolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
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!");
1518 if (npobj
->_class
->hasProperty(npobj
, (NPIdentifier
)id
)) {
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
);
1528 ok
= ::JS_DefineElement(cx
, obj
, JSVAL_TO_INT(id
), JSVAL_VOID
, nsnull
,
1529 nsnull
, JSPROP_ENUMERATE
);
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
);
1543 NS_ASSERTION(JSVAL_IS_INT(id
), "id must be either string or int!\n");
1545 str
= ::JS_ValueToString(cx
, id
);
1548 // OOM. The JS engine throws exceptions for us in this case.
1555 ::JS_DefineUCFunction(cx
, obj
, ::JS_GetStringChars(str
),
1556 ::JS_GetStringLength(str
), CallNPMethod
, 0,
1561 return fnc
!= nsnull
;
1564 return ReportExceptionIfPending(cx
);
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.
1580 NPObjWrapper_Finalize(JSContext
*cx
, JSObject
*obj
)
1582 NPObject
*npobj
= (NPObject
*)::JS_GetPrivate(cx
, obj
);
1585 if (sNPObjWrappers
.ops
) {
1586 PL_DHashTableOperate(&sNPObjWrappers
, npobj
, PL_DHASH_REMOVE
);
1589 // Let go of our NPObject
1590 _releaseobject(npobj
);
1593 OnWrapperDestroyed();
1597 NPObjWrapper_Call(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
1600 return CallNPMethodInternal(cx
, JSVAL_TO_OBJECT(argv
[-2]), argc
, argv
, rval
,
1605 NPObjWrapper_Construct(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
1608 return CallNPMethodInternal(cx
, JSVAL_TO_OBJECT(argv
[-2]), argc
, argv
, rval
,
1612 class NPObjWrapperHashEntry
: public PLDHashEntryHdr
1615 NPObject
*mNPObj
; // Must be the first member for the PLDHash stubs to work
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.
1627 nsNPObjWrapper::OnDestroy(NPObject
*npobj
)
1633 if (npobj
->_class
== &nsJSObjWrapper::sJSObjWrapperNPClass
) {
1634 // npobj is one of our own, no private data to clean up here.
1639 if (!sNPObjWrappers
.ops
) {
1640 // No hash yet (or any more), no used wrappers available.
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
1652 JSContext
*cx
= GetJSContext(entry
->mNpp
);
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.
1669 nsNPObjWrapper::GetNewOrUsed(NPP npp
, JSContext
*cx
, NPObject
*npobj
)
1672 NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!");
1677 if (npobj
->_class
== &nsJSObjWrapper::sJSObjWrapperNPClass
) {
1678 // npobj is one of our own, return its existing JSObject.
1680 return ((nsJSObjWrapper
*)npobj
)->mJSObj
;
1684 NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
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!");
1700 NPObjWrapperHashEntry
*entry
= static_cast<NPObjWrapperHashEntry
*>
1701 (PL_DHashTableOperate(&sNPObjWrappers
, npobj
, PL_DHASH_ADD
));
1705 JS_ReportOutOfMemory(cx
);
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
;
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?");
1737 // OOM? Remove the stale entry from the hash.
1739 PL_DHashTableRawRemove(&sNPObjWrappers
, entry
);
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
);
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
);
1781 return PL_DHASH_REMOVE
;
1784 return PL_DHASH_NEXT
;
1787 // Struct for passing an NPP and a JSContext to
1788 // NPObjWrapperPluginDestroyedCallback
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
);
1822 ::JS_SetPrivate(nppcx
->cx
, entry
->mJSObj
, nsnull
);
1826 return PL_DHASH_REMOVE
;
1829 return PL_DHASH_NEXT
;
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");
1847 NS_ERROR("No context stack available!");
1853 stack
->GetSafeJSContext(&cx
);
1855 NS_ERROR("No safe JS context available!");
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.
1876 // Find the plugin instance so that we can (eventually) get to the
1878 nsNPAPIPluginInstance
*inst
= (nsNPAPIPluginInstance
*)npp
->ndata
;
1883 nsCOMPtr
<nsIPluginInstancePeer
> pip
;
1884 inst
->GetPeer(getter_AddRefs(pip
));
1885 nsCOMPtr
<nsIPluginTagInfo2
> pti2(do_QueryInterface(pip
));
1890 nsCOMPtr
<nsIDOMElement
> element
;
1891 pti2
->GetDOMElement(getter_AddRefs(element
));
1896 // Get the DOM element's JS object.
1897 nsCOMPtr
<nsIXPConnect
> xpc(do_GetService(nsIXPConnect::GetCID()));
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
));
1913 nsIDocument
* doc
= content
->GetOwnerDoc();
1918 nsIScriptGlobalObject
* sgo
= doc
->GetScriptGlobalObject();
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
));
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
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
);
1954 // Find the NPP for a NPObject.
1956 LookupNPP(NPObject
*npobj
)
1958 if (npobj
->_class
== &nsJSObjWrapper::sJSObjWrapperNPClass
) {
1959 NS_ERROR("NPP requested for NPObject of class "
1960 "nsJSObjWrapper::sJSObjWrapperNPClass!\n");
1965 NPObjWrapperHashEntry
*entry
= static_cast<NPObjWrapperHashEntry
*>
1966 (PL_DHashTableOperate(&sNPObjWrappers
, npobj
, PL_DHASH_ADD
));
1968 if (PL_DHASH_ENTRY_IS_FREE(entry
)) {
1972 NS_ASSERTION(entry
->mNpp
, "Live NPObject entry w/o an NPP!");
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");
1990 NPObjectMemberPrivate
*memberPrivate
=
1991 (NPObjectMemberPrivate
*)PR_Malloc(sizeof(NPObjectMemberPrivate
));
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
);
2001 PR_Free(memberPrivate
);
2005 *vp
= OBJECT_TO_JSVAL(memobj
);
2006 ::JS_AddRoot(cx
, vp
);
2008 ::JS_SetPrivate(cx
, memobj
, (void *)memberPrivate
);
2012 VOID_TO_NPVARIANT(npv
);
2013 if (!npobj
->_class
->getProperty(npobj
, (NPIdentifier
)id
, &npv
)) {
2014 ::JS_RemoveRoot(cx
, vp
);
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
);
2039 NPObjectMember_Convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
2041 NPObjectMemberPrivate
*memberPrivate
=
2042 (NPObjectMemberPrivate
*)::JS_GetInstancePrivate(cx
, obj
,
2043 &sNPObjectMemberClass
,
2045 NS_ASSERTION(memberPrivate
, "no Ambiguous Member Private data!");
2051 case JSTYPE_BOOLEAN
:
2053 *vp
= memberPrivate
->fieldValue
;
2055 case JSTYPE_FUNCTION
:
2056 // Leave this to NPObjectMember_Call.
2059 NS_ERROR("illegal operation on JSObject prototype object");
2065 NPObjectMember_Finalize(JSContext
*cx
, JSObject
*obj
)
2067 NPObjectMemberPrivate
*memberPrivate
;
2069 memberPrivate
= (NPObjectMemberPrivate
*)::JS_GetPrivate(cx
, obj
);
2073 PR_Free(memberPrivate
);
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
,
2087 if (!memberPrivate
|| !memberPrivate
->npobjWrapper
)
2090 NPObject
*npobj
= GetNPObject(cx
, memberPrivate
->npobjWrapper
);
2092 ThrowJSException(cx
, "Call on invalid member object");
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,
2103 npargs
= (NPVariant
*)PR_Malloc(argc
* sizeof(NPVariant
));
2106 ThrowJSException(cx
, "Out of memory!");
2112 // Convert arguments
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
) {
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
) {
2141 ThrowJSException(cx
, "Error calling method on NPObject!");
2146 *rval
= NPVariantToJSVal(memberPrivate
->npp
, cx
, &npv
);
2148 // *rval now owns the value, release our reference.
2149 _releasevariantvalue(&npv
);
2151 return ReportExceptionIfPending(cx
);
2155 NPObjectMember_Mark(JSContext
*cx
, JSObject
*obj
, void *arg
)
2157 NPObjectMemberPrivate
*memberPrivate
=
2158 (NPObjectMemberPrivate
*)::JS_GetInstancePrivate(cx
, obj
,
2159 &sNPObjectMemberClass
,
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
);