views:

553

answers:

2

I am confused about the ref count in npapi. Mostly, I don't know which method will increase ref count. Can anyone explain in detail about this? For the convenience, I listed most common used NPN_* functions here and my own understanding:

NPN_CreateObject: set ref count to 0

NPN_RetainObject: inc ref count

NPN_ReleaseObject: dec ref count

NPN_Evaluate: ?? (in case return an NPObject*)

NPN_GetValue: ?? (in case return an NPObject*)

NPN_SetValue: ?? (in case set to an NPObject*)

NPN_GetProperty: ?? (in case return an NPObject*)

NPN_SetProperty: ?? (in case set to an NPObject*)

NPN_RemoveProperty: ??

NPN_Enumerate: ??

NPN_Construct: ??

another thing: is npapi do nested release? (In case NPObject* with a property of NPObject*, release parent will decrease the ref count of child).

Thanks.

+2  A: 

First, to correct a misconception: NPN_CreateObject sets the refCount to 1, not to 0. Then, when you call NPN_RetainObject it increments the refcount, and NPN_ReleaseObject will decrement it. If ReleaseObject decrements the refcount to 0, it will also free it by calling the deallocate function from your NPClass (which should delete the NPObject after doing any needed cleanup)

see: https://developer.mozilla.org/En/NPClass

beyond that, a good general rule of thumb for any of the other NPClass functions is that any time you put an NPObject in a NPVariant, you need to call NPN_RetainObject. To remember this, simply remember that when you're done with an NPVariant (one that you have used and are done with, not one that was passed as a return value), you call NPN_ReleaseVariantValue, which will release the NPString data if it's a string or the NPObject if it's an object.

So from any of the other methods, if you are returning an NPObject, you need to call NPN_RetainObject before you stuff it into the NPVariant. Also, if you save a copy of the NPObject, you should call NPN_RetainObject on it and NPN_ReleaseObject when you're done saving it. It also bears mentioning that you should never call any NPN_ method from a thread other than the main thread.

Does that help?

Taxilian
thanks for the detailed answer. So, if I use NPN_GetValue, no need to release, but if I use NPN_Evaluate, I should release the variant.However, regarding the NPN_CreateObject, if it already set ref count to 1, why in the code we have to: if (this->npobj != NULL) retval = (NPObject *)this->npobj; else retval = (NPObject *)MyScriptableNPObject::NewObject(this->npp); NPN_RetainObject(retval);retain it again after it created and wouldn't this make ref count to 2 (The first call to getScriptableObject)?
liuliu
+3  A: 

There isn't room in comments to answer your question in the comment well, so I'll put it in another answer.

Any time your code gets an NPObject from a NPObject function (one of those you mentioned above), you should Release that NPObject when you're done with it. (that could be immediately, or you could save it for awhile and release it when your object gets destroyed). The same holds true with a NPVariant. It does not hold true with the arguments passed into your Invoke function, but the return value you set will get released by the browser when it's done.

When you call NPN_GetValue and get an NPObject from there, that also has to be released. This means that when the browser calls NPP_GetValue, it will release your NPObject when it's done. If you want to create a new NPObject every time the browser calls NPP_GetValue to get your NPObject, then you don't have to call NPN_RetainObject on it; the assumption in the NPAPI example is that you are saving a copy of your NPObject so that it doesn't get deleted until your plugin object gets deleted.

Since the browser will call Release for every time that it calls NPP_GetValue to get your NPObject, you need to make sure that the refcount is incremented before you return it. The reason you don't have to call it twice in the case that you're going to keep it is that NPN_CreateObject does an implicit Retain before it returns your object.

I'll try to post a more detailed explanation with examples on http://colonelpanic.net later today. Does that clear it up?

Taxilian
thanks. I think I finally get it. Correct me if I am wrong. For example: if you get an object A by NPN_Evaluate, and then set it to another object B through NPN_SetProperty(npp, B, identifier, A), you still have to and can NPN_ReleaseVariableValue(A) immediately (you destroy what you created). But you don't have to worry that B will get seg fault when it tried to access property A because browser inc ref count of A in NPN_SetProperty. Final word, that is the way browser implemented nested release.
liuliu
I'm not sure if I fully understand what you're saying. If you get an object A that is an NPVariant, then yes, you need to release it, but not until you're done with it. Once you release, you must assume that it is no longer accessible to you. And yes, if it is implemented properly, B should have incremented the reference count during SetProperty if it holds onto a reference. Any time you get a reference, retain; any time you're done with one, release.I wrote up a blog post on the subject with a little more detail: http://colonelpanic.net/2009/12/memory-management-in-npapi/
Taxilian