зеркало из https://github.com/mozilla/pjs.git
Relanding bug 372769 with some cycle collection changes to fix leaks. r=mrbkap, sr=sicking, with r=sicking on the leak fixes.
This commit is contained in:
Родитель
891792d05e
Коммит
8102578ca9
|
@ -111,6 +111,7 @@ LOCAL_INCLUDES = \
|
|||
-I$(srcdir)/../../xul/document/src \
|
||||
-I$(srcdir)/../../events/src \
|
||||
-I$(srcdir)/../../../layout/style \
|
||||
-I$(srcdir)/../../../dom/src/base \
|
||||
$(NULL)
|
||||
|
||||
DEFINES += -D_IMPL_NS_LAYOUT
|
||||
|
|
|
@ -102,27 +102,134 @@
|
|||
#include "prprf.h"
|
||||
#include "nsNodeUtils.h"
|
||||
|
||||
// Nasty hack. Maybe we could move some of the classinfo utility methods
|
||||
// (e.g. WrapNative and ThrowJSException) over to nsContentUtils?
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsJSUtils.h"
|
||||
|
||||
// Helper classes
|
||||
|
||||
/***********************************************************************/
|
||||
//
|
||||
// The JS class for XBLBinding
|
||||
//
|
||||
PR_STATIC_CALLBACK(void)
|
||||
JS_STATIC_DLL_CALLBACK(void)
|
||||
XBLFinalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
nsIXBLDocumentInfo* docInfo =
|
||||
static_cast<nsIXBLDocumentInfo*>(::JS_GetPrivate(cx, obj));
|
||||
NS_RELEASE(docInfo);
|
||||
|
||||
nsXBLJSClass* c = static_cast<nsXBLJSClass*>(::JS_GetClass(cx, obj));
|
||||
c->Drop();
|
||||
}
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSBool)
|
||||
XBLResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
JSObject **objp)
|
||||
{
|
||||
// Note: if we get here, that means that the implementation for some binding
|
||||
// was installed, which means that AllowScripts() tested true. Hence no need
|
||||
// to do checks like that here.
|
||||
|
||||
// Default to not resolving things.
|
||||
NS_ASSERTION(*objp, "Must have starting object");
|
||||
|
||||
JSObject* origObj = *objp;
|
||||
*objp = NULL;
|
||||
|
||||
if (!JSVAL_IS_STRING(id)) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
nsDependentJSString fieldName(id);
|
||||
|
||||
jsval slotVal;
|
||||
::JS_GetReservedSlot(cx, obj, 0, &slotVal);
|
||||
NS_ASSERTION(!JSVAL_IS_VOID(slotVal), "How did that happen?");
|
||||
|
||||
nsXBLPrototypeBinding* protoBinding =
|
||||
static_cast<nsXBLPrototypeBinding*>(JSVAL_TO_PRIVATE(slotVal));
|
||||
NS_ASSERTION(protoBinding, "Must have prototype binding!");
|
||||
|
||||
nsXBLProtoImplField* field = protoBinding->FindField(fieldName);
|
||||
if (!field) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// We have this field. Time to install it. Get our node.
|
||||
JSClass* nodeClass = ::JS_GetClass(cx, origObj);
|
||||
if (!nodeClass) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (~nodeClass->flags &
|
||||
(JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
|
||||
// Looks like whatever |origObj| is it's not our nsIContent. It might well
|
||||
// be the proto our binding installed, however, so just baul out quietly.
|
||||
// Do NOT throw an exception here.
|
||||
// We could make this stricter by checking the class maybe, but whatever
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper =
|
||||
do_QueryInterface(static_cast<nsISupports*>(::JS_GetPrivate(cx, origObj)));
|
||||
if (!xpcWrapper) {
|
||||
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_UNEXPECTED);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryWrappedNative(xpcWrapper);
|
||||
if (!content) {
|
||||
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_UNEXPECTED);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
// This mirrors code in nsXBLProtoImpl::InstallImplementation
|
||||
nsIDocument* doc = content->GetOwnerDoc();
|
||||
if (!doc) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
nsIScriptGlobalObject* global = doc->GetScriptGlobalObject();
|
||||
if (!global) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptContext> context = global->GetContext();
|
||||
if (!context) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Now we either resolve or fail
|
||||
*objp = origObj;
|
||||
nsresult rv = field->InstallField(context, origObj,
|
||||
protoBinding->DocURI());
|
||||
if (NS_FAILED(rv)) {
|
||||
if (!::JS_IsExceptionPending(cx)) {
|
||||
nsDOMClassInfo::ThrowJSException(cx, rv);
|
||||
}
|
||||
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
nsXBLJSClass::nsXBLJSClass(const nsAFlatCString& aClassName)
|
||||
{
|
||||
memset(this, 0, sizeof(nsXBLJSClass));
|
||||
next = prev = static_cast<JSCList*>(this);
|
||||
name = ToNewCString(aClassName);
|
||||
flags =
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
|
||||
JSCLASS_NEW_RESOLVE | JSCLASS_NEW_RESOLVE_GETS_START |
|
||||
// Our one reserved slot holds the relevant nsXBLPrototypeBinding
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1);
|
||||
addProperty = delProperty = setProperty = getProperty = ::JS_PropertyStub;
|
||||
enumerate = ::JS_EnumerateStub;
|
||||
resolve = ::JS_ResolveStub;
|
||||
resolve = (JSResolveOp)XBLResolve;
|
||||
convert = ::JS_ConvertStub;
|
||||
finalize = XBLFinalize;
|
||||
}
|
||||
|
@ -1034,6 +1141,7 @@ nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData)
|
|||
nsresult
|
||||
nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
|
||||
const nsAFlatCString& aClassName,
|
||||
nsXBLPrototypeBinding* aProtoBinding,
|
||||
void **aClassObject)
|
||||
{
|
||||
// First ensure our JS class is initialized.
|
||||
|
@ -1139,6 +1247,20 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Keep this proto binding alive while we're alive. Do this first so that
|
||||
// we can guarantee that in XBLFinalize this will be non-null.
|
||||
nsIXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
|
||||
::JS_SetPrivate(cx, proto, docInfo);
|
||||
NS_ADDREF(docInfo);
|
||||
|
||||
if (!::JS_SetReservedSlot(cx, proto, 0, PRIVATE_TO_JSVAL(aProtoBinding))) {
|
||||
(nsXBLService::gClassTable)->Remove(&key);
|
||||
|
||||
// |c| will get dropped when |proto| is finalized
|
||||
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
*aClassObject = (void*)proto;
|
||||
}
|
||||
else {
|
||||
|
@ -1155,63 +1277,6 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsXBLBinding::InitClass(const nsCString& aClassName,
|
||||
nsIScriptContext* aContext,
|
||||
nsIDocument* aDocument, void** aScriptObject,
|
||||
void** aClassObject)
|
||||
{
|
||||
*aClassObject = nsnull;
|
||||
*aScriptObject = nsnull;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// Obtain the bound element's current script object.
|
||||
JSContext* cx = (JSContext*)aContext->GetNativeContext();
|
||||
|
||||
nsIDocument *ownerDoc = mBoundElement->GetOwnerDoc();
|
||||
nsIScriptGlobalObject *sgo;
|
||||
|
||||
if (!ownerDoc || !(sgo = ownerDoc->GetScriptGlobalObject())) {
|
||||
NS_ERROR("Can't find global object for bound content!");
|
||||
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
|
||||
rv = nsContentUtils::XPConnect()->WrapNative(cx, sgo->GetGlobalJSObject(),
|
||||
mBoundElement,
|
||||
NS_GET_IID(nsISupports),
|
||||
getter_AddRefs(wrapper));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JSObject* object = nsnull;
|
||||
rv = wrapper->GetJSObject(&object);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aScriptObject = object;
|
||||
|
||||
// First ensure our JS class is initialized.
|
||||
|
||||
rv = DoInitJSClass(cx, sgo->GetGlobalJSObject(), object, aClassName,
|
||||
aClassObject);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Root mBoundElement so that it doesn't lose it's binding
|
||||
nsIDocument* doc = mBoundElement->GetOwnerDoc();
|
||||
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> native_wrapper =
|
||||
do_QueryInterface(wrapper);
|
||||
if (native_wrapper) {
|
||||
doc->AddReference(mBoundElement, native_wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsXBLBinding::AllowScripts()
|
||||
{
|
||||
|
@ -1344,6 +1409,20 @@ nsXBLBinding::GetFirstStyleBinding()
|
|||
return mNextBinding ? mNextBinding->GetFirstStyleBinding() : nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsXBLBinding::ResolveAllFields(JSContext *cx, JSObject *obj) const
|
||||
{
|
||||
if (!mPrototypeBinding->ResolveAllFields(cx, obj)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
if (mNextBinding) {
|
||||
return mNextBinding->ResolveAllFields(cx, obj);
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLBinding::MarkForDeath()
|
||||
{
|
||||
|
|
|
@ -132,6 +132,10 @@ public:
|
|||
nsXBLBinding* RootBinding();
|
||||
nsXBLBinding* GetFirstStyleBinding();
|
||||
|
||||
// Resolve all the fields for this binding and all ancestor bindings on the
|
||||
// object |obj|. False return means a JS exception was set.
|
||||
PRBool ResolveAllFields(JSContext *cx, JSObject *obj) const;
|
||||
|
||||
// Get the list of insertion points for aParent. The nsInsertionPointList
|
||||
// is owned by the binding, you should not delete it.
|
||||
nsresult GetInsertionPointsFor(nsIContent* aParent,
|
||||
|
@ -155,16 +159,11 @@ public:
|
|||
|
||||
static nsresult DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
|
||||
const nsAFlatCString& aClassName,
|
||||
nsXBLPrototypeBinding* aProtoBinding,
|
||||
void **aClassObject);
|
||||
|
||||
PRBool AllowScripts(); // XXX make const
|
||||
|
||||
// Internal member functions
|
||||
protected:
|
||||
nsresult InitClass(const nsCString& aClassName, nsIScriptContext* aContext,
|
||||
nsIDocument* aDocument, void** aScriptObject,
|
||||
void** aClassObject);
|
||||
|
||||
// MEMBER VARIABLES
|
||||
protected:
|
||||
nsAutoRefCnt mRefCnt;
|
||||
|
|
|
@ -83,12 +83,13 @@ nsXBLContentSink::nsXBLContentSink()
|
|||
: mState(eXBL_InDocument),
|
||||
mSecondaryState(eXBL_None),
|
||||
mDocInfo(nsnull),
|
||||
mFoundFirstBinding(PR_FALSE),
|
||||
mIsChromeOrResource(PR_FALSE),
|
||||
mFoundFirstBinding(PR_FALSE),
|
||||
mBinding(nsnull),
|
||||
mHandler(nsnull),
|
||||
mImplementation(nsnull),
|
||||
mImplMember(nsnull),
|
||||
mImplField(nsnull),
|
||||
mProperty(nsnull),
|
||||
mMethod(nsnull),
|
||||
mField(nsnull)
|
||||
|
@ -263,6 +264,18 @@ nsXBLContentSink::AddMember(nsXBLProtoImplMember* aMember)
|
|||
mImplMember = aMember; // Adjust our pointer to point to the new last member in the chain.
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLContentSink::AddField(nsXBLProtoImplField* aField)
|
||||
{
|
||||
// Add this field to our chain.
|
||||
if (mImplField)
|
||||
mImplField->SetNext(aField); // Already have a chain. Just append to the end.
|
||||
else
|
||||
mImplementation->SetFieldList(aField); // We're the first member in the chain.
|
||||
|
||||
mImplField = aField; // Adjust our pointer to point to the new last field in the chain.
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXBLContentSink::HandleStartElement(const PRUnichar *aName,
|
||||
const PRUnichar **aAtts,
|
||||
|
@ -703,7 +716,8 @@ nsXBLContentSink::ConstructImplementation(const PRUnichar **aAtts)
|
|||
{
|
||||
mImplementation = nsnull;
|
||||
mImplMember = nsnull;
|
||||
|
||||
mImplField = nsnull;
|
||||
|
||||
if (!mBinding)
|
||||
return;
|
||||
|
||||
|
@ -777,7 +791,7 @@ nsXBLContentSink::ConstructField(const PRUnichar **aAtts, PRUint32 aLineNumber)
|
|||
mField = new nsXBLProtoImplField(name, readonly);
|
||||
if (mField) {
|
||||
mField->SetLineNumber(aLineNumber);
|
||||
AddMember(mField);
|
||||
AddField(mField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,6 +157,7 @@ protected:
|
|||
nsresult ReportUnexpectedElement(nsIAtom* aElementName, PRUint32 aLineNumber);
|
||||
|
||||
void AddMember(nsXBLProtoImplMember* aMember);
|
||||
void AddField(nsXBLProtoImplField* aField);
|
||||
|
||||
XBLPrimaryState mState;
|
||||
XBLSecondaryState mSecondaryState;
|
||||
|
@ -168,6 +169,7 @@ protected:
|
|||
nsXBLPrototypeHandler* mHandler; // current handler, owned by its PrototypeBinding
|
||||
nsXBLProtoImpl* mImplementation;
|
||||
nsXBLProtoImplMember* mImplMember;
|
||||
nsXBLProtoImplField* mImplField;
|
||||
nsXBLProtoImplProperty* mProperty;
|
||||
nsXBLProtoImplMethod* mMethod;
|
||||
nsXBLProtoImplField* mField;
|
||||
|
|
|
@ -409,6 +409,8 @@ nsXBLDocGlobalObject::GetPrincipal()
|
|||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (!mGlobalObjectOwner) {
|
||||
// XXXbz this should really save the principal when
|
||||
// ClearGlobalObjectOwner() happens.
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
@ -442,8 +444,19 @@ TraverseProtos(nsHashKey *aKey, void *aData, void* aClosure)
|
|||
return kHashEnumerateNext;
|
||||
}
|
||||
|
||||
static PRIntn PR_CALLBACK
|
||||
UnlinkProtos(nsHashKey *aKey, void *aData, void* aClosure)
|
||||
{
|
||||
nsXBLPrototypeBinding *proto = static_cast<nsXBLPrototypeBinding*>(aData);
|
||||
proto->Unlink();
|
||||
return kHashEnumerateNext;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo)
|
||||
if (tmp->mBindingTable) {
|
||||
tmp->mBindingTable->Enumerate(UnlinkProtos, nsnull);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "nsIServiceManager.h"
|
||||
#include "nsIXBLDocumentInfo.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
|
||||
nsresult
|
||||
nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIContent* aBoundElement)
|
||||
|
@ -55,7 +56,7 @@ nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIConten
|
|||
// this prototype implementation as a guide. The prototype implementation is compiled lazily,
|
||||
// so for the first bound element that needs a concrete implementation, we also build the
|
||||
// prototype implementation.
|
||||
if (!mMembers) // Constructor and destructor also live in mMembers
|
||||
if (!mMembers && !mFields) // Constructor and destructor also live in mMembers
|
||||
return NS_OK; // Nothing to do, so let's not waste time.
|
||||
|
||||
// If the way this gets the script context changes, fix
|
||||
|
@ -214,6 +215,45 @@ nsXBLProtoImpl::Traverse(nsCycleCollectionTraversalCallback &cb) const
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLProtoImpl::Unlink()
|
||||
{
|
||||
if (mClassObject) {
|
||||
DestroyMembers(nsnull);
|
||||
}
|
||||
}
|
||||
|
||||
nsXBLProtoImplField*
|
||||
nsXBLProtoImpl::FindField(const nsString& aFieldName) const
|
||||
{
|
||||
for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) {
|
||||
if (aFieldName.Equals(f->GetName())) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsXBLProtoImpl::ResolveAllFields(JSContext *cx, JSObject *obj) const
|
||||
{
|
||||
for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) {
|
||||
// Using OBJ_LOOKUP_PROPERTY is a pain, since what we have is a
|
||||
// PRUnichar* for the property name. Let's just use the public API and
|
||||
// all.
|
||||
nsDependentString name(f->GetName());
|
||||
jsval dummy;
|
||||
if (!::JS_LookupUCProperty(cx, obj,
|
||||
reinterpret_cast<const jschar*>(name.get()),
|
||||
name.Length(), &dummy)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLProtoImpl::DestroyMembers(nsXBLProtoImplMember* aBrokenMember)
|
||||
{
|
||||
|
|
|
@ -42,9 +42,11 @@
|
|||
#include "nsMemory.h"
|
||||
#include "nsXBLPrototypeHandler.h"
|
||||
#include "nsXBLProtoImplMember.h"
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
#include "nsXBLProtoImplField.h"
|
||||
|
||||
class nsIXPConnectJSObjectHolder;
|
||||
class nsXBLPrototypeBinding;
|
||||
class nsXBLProtoImplAnonymousMethod;
|
||||
|
||||
class nsXBLProtoImpl
|
||||
{
|
||||
|
@ -52,6 +54,7 @@ public:
|
|||
nsXBLProtoImpl()
|
||||
: mClassObject(nsnull),
|
||||
mMembers(nsnull),
|
||||
mFields(nsnull),
|
||||
mConstructor(nsnull),
|
||||
mDestructor(nsnull)
|
||||
{
|
||||
|
@ -64,7 +67,8 @@ public:
|
|||
// clean them up automatically.
|
||||
for (nsXBLProtoImplMember* curr = mMembers; curr; curr=curr->GetNext())
|
||||
curr->Destroy(mClassObject != nsnull);
|
||||
delete mMembers;
|
||||
delete mMembers;
|
||||
delete mFields;
|
||||
}
|
||||
|
||||
nsresult InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIContent* aBoundElement);
|
||||
|
@ -74,9 +78,26 @@ public:
|
|||
void** aTargetClassObject);
|
||||
nsresult CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding);
|
||||
|
||||
void SetMemberList(nsXBLProtoImplMember* aMemberList) { delete mMembers; mMembers = aMemberList; }
|
||||
void SetMemberList(nsXBLProtoImplMember* aMemberList)
|
||||
{
|
||||
delete mMembers;
|
||||
mMembers = aMemberList;
|
||||
}
|
||||
|
||||
void SetFieldList(nsXBLProtoImplField* aFieldList)
|
||||
{
|
||||
delete mFields;
|
||||
mFields = aFieldList;
|
||||
}
|
||||
|
||||
void Traverse(nsCycleCollectionTraversalCallback &cb) const;
|
||||
void Unlink();
|
||||
|
||||
nsXBLProtoImplField* FindField(const nsString& aFieldName) const;
|
||||
|
||||
// Resolve all the fields for this implementation on the object |obj| False
|
||||
// return means a JS exception was set.
|
||||
PRBool ResolveAllFields(JSContext *cx, JSObject *obj) const;
|
||||
|
||||
protected:
|
||||
// Function to call if compilation of a member fails. When this is called,
|
||||
|
@ -93,6 +114,8 @@ protected:
|
|||
// and methods for the binding.
|
||||
|
||||
nsXBLProtoImplMember* mMembers; // The members of an implementation are chained in this singly-linked list.
|
||||
|
||||
nsXBLProtoImplField* mFields; // Our fields
|
||||
|
||||
public:
|
||||
nsXBLProtoImplAnonymousMethod* mConstructor; // Our class constructor.
|
||||
|
|
|
@ -46,14 +46,17 @@
|
|||
#include "nsXBLProtoImplField.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
nsXBLProtoImplField::nsXBLProtoImplField(const PRUnichar* aName, const PRUnichar* aReadOnly)
|
||||
: nsXBLProtoImplMember(aName),
|
||||
: mNext(nsnull),
|
||||
mFieldText(nsnull),
|
||||
mFieldTextLength(0),
|
||||
mLineNumber(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsXBLProtoImplField);
|
||||
mName = NS_strdup(aName); // XXXbz make more sense to use a stringbuffer?
|
||||
|
||||
mJSAttributes = JSPROP_ENUMERATE;
|
||||
if (aReadOnly) {
|
||||
nsAutoString readOnly; readOnly.Assign(*aReadOnly);
|
||||
|
@ -67,11 +70,8 @@ nsXBLProtoImplField::~nsXBLProtoImplField()
|
|||
MOZ_COUNT_DTOR(nsXBLProtoImplField);
|
||||
if (mFieldText)
|
||||
nsMemory::Free(mFieldText);
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLProtoImplField::Destroy(PRBool aIsCompiled)
|
||||
{
|
||||
NS_Free(mName);
|
||||
delete mNext;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -92,27 +92,15 @@ nsXBLProtoImplField::AppendFieldText(const nsAString& aText)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsXBLProtoImplField::InstallMember(nsIScriptContext* aContext,
|
||||
nsIContent* aBoundElement,
|
||||
void* aScriptObject,
|
||||
void* aTargetClassObject,
|
||||
const nsCString& aClassStr)
|
||||
nsXBLProtoImplField::InstallField(nsIScriptContext* aContext,
|
||||
JSObject* aBoundNode,
|
||||
nsIURI* aBindingDocURI) const
|
||||
{
|
||||
if (mFieldTextLength == 0)
|
||||
return NS_OK; // nothing to do.
|
||||
NS_PRECONDITION(aBoundNode,
|
||||
"uh-oh, bound node should NOT be null or bad things will "
|
||||
"happen");
|
||||
|
||||
JSContext* cx = (JSContext*) aContext->GetNativeContext();
|
||||
NS_ASSERTION(aScriptObject, "uh-oh, script Object should NOT be null or bad things will happen");
|
||||
if (!aScriptObject)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCAutoString bindingURI(aClassStr);
|
||||
PRInt32 hash = bindingURI.RFindChar('#');
|
||||
if (hash != kNotFound)
|
||||
bindingURI.Truncate(hash);
|
||||
|
||||
// compile the literal string
|
||||
jsval result = JSVAL_NULL;
|
||||
jsval result = JSVAL_VOID;
|
||||
|
||||
// EvaluateStringWithValue and JS_DefineUCProperty can both trigger GC, so
|
||||
// protect |result| here.
|
||||
|
@ -120,39 +108,40 @@ nsXBLProtoImplField::InstallMember(nsIScriptContext* aContext,
|
|||
nsAutoGCRoot root(&result, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
PRBool undefined;
|
||||
// XXX Need a URI here!
|
||||
nsCOMPtr<nsIScriptContext> context = aContext;
|
||||
rv = context->EvaluateStringWithValue(nsDependentString(mFieldText,
|
||||
mFieldTextLength),
|
||||
aScriptObject,
|
||||
nsnull, bindingURI.get(),
|
||||
mLineNumber, nsnull,
|
||||
(void*) &result, &undefined);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (!undefined) {
|
||||
// Define the evaluated result as a JS property
|
||||
nsDependentString name(mName);
|
||||
JSAutoRequest ar(cx);
|
||||
if (!::JS_DefineUCProperty(cx, static_cast<JSObject *>(aScriptObject),
|
||||
reinterpret_cast<const jschar*>(mName),
|
||||
name.Length(), result, nsnull, nsnull, mJSAttributes))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (mFieldTextLength != 0) {
|
||||
nsCAutoString uriSpec;
|
||||
aBindingDocURI->GetSpec(uriSpec);
|
||||
|
||||
// compile the literal string
|
||||
// XXX Could we produce a better principal here? Should be able
|
||||
// to, really!
|
||||
PRBool undefined;
|
||||
nsCOMPtr<nsIScriptContext> context = aContext;
|
||||
rv = context->EvaluateStringWithValue(nsDependentString(mFieldText,
|
||||
mFieldTextLength),
|
||||
aBoundNode,
|
||||
nsnull, uriSpec.get(),
|
||||
mLineNumber, nsnull,
|
||||
(void*) &result, &undefined);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (undefined) {
|
||||
result = JSVAL_VOID;
|
||||
}
|
||||
}
|
||||
|
||||
// Define the evaluated result as a JS property
|
||||
nsDependentString name(mName);
|
||||
JSContext* cx = (JSContext*) aContext->GetNativeContext();
|
||||
JSAutoRequest ar(cx);
|
||||
if (!::JS_DefineUCProperty(cx, aBoundNode,
|
||||
reinterpret_cast<const jschar*>(mName),
|
||||
name.Length(), result, nsnull, nsnull,
|
||||
mJSAttributes)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXBLProtoImplField::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr,
|
||||
void* aClassObject)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLProtoImplField::Traverse(nsCycleCollectionTraversalCallback &cb) const
|
||||
{
|
||||
}
|
||||
|
|
|
@ -46,30 +46,31 @@
|
|||
#include "nsString.h"
|
||||
#include "nsXBLProtoImplMember.h"
|
||||
|
||||
class nsXBLProtoImplField: public nsXBLProtoImplMember
|
||||
class nsIURI;
|
||||
|
||||
class nsXBLProtoImplField
|
||||
{
|
||||
public:
|
||||
nsXBLProtoImplField(const PRUnichar* aName, const PRUnichar* aReadOnly);
|
||||
virtual ~nsXBLProtoImplField();
|
||||
virtual void Destroy(PRBool aIsCompiled);
|
||||
~nsXBLProtoImplField();
|
||||
|
||||
void AppendFieldText(const nsAString& aText);
|
||||
void SetLineNumber(PRUint32 aLineNumber) {
|
||||
mLineNumber = aLineNumber;
|
||||
}
|
||||
|
||||
virtual nsresult InstallMember(nsIScriptContext* aContext,
|
||||
nsIContent* aBoundElement,
|
||||
void* aScriptObject,
|
||||
void* aTargetClassObject,
|
||||
const nsCString& aClassStr);
|
||||
virtual nsresult CompileMember(nsIScriptContext* aContext,
|
||||
const nsCString& aClassStr,
|
||||
void* aClassObject);
|
||||
nsXBLProtoImplField* GetNext() const { return mNext; }
|
||||
void SetNext(nsXBLProtoImplField* aNext) { mNext = aNext; }
|
||||
|
||||
virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const;
|
||||
nsresult InstallField(nsIScriptContext* aContext,
|
||||
JSObject* aBoundNode, nsIURI*
|
||||
aBindingDocURI) const;
|
||||
|
||||
const PRUnichar* GetName() const { return mName; }
|
||||
|
||||
protected:
|
||||
nsXBLProtoImplField* mNext;
|
||||
PRUnichar* mName;
|
||||
PRUnichar* mFieldText;
|
||||
PRUint32 mFieldTextLength;
|
||||
PRUint32 mLineNumber;
|
||||
|
|
|
@ -365,6 +365,25 @@ nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const
|
|||
mInterfaceTable->Enumerate(TraverseBinding, &cb);
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLPrototypeBinding::Unlink()
|
||||
{
|
||||
mBinding = nsnull;
|
||||
if (mImplementation)
|
||||
mImplementation->Unlink();
|
||||
if (mResources)
|
||||
NS_IF_RELEASE(mResources->mLoader);
|
||||
|
||||
// I'm not sure whether it would be safer to just nuke the tables or to
|
||||
// traverse them with unlinking functions... or whether we even need to
|
||||
// unlink them. I think we need to at least clean up mInsertionPointTable
|
||||
// becase it can hold strong refs to nodes in the binding document.
|
||||
delete mInsertionPointTable;
|
||||
mInsertionPointTable = nsnull;
|
||||
delete mInterfaceTable;
|
||||
mInterfaceTable = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLPrototypeBinding::Initialize()
|
||||
{
|
||||
|
@ -822,7 +841,7 @@ nsXBLPrototypeBinding::InitClass(const nsCString& aClassName,
|
|||
*aClassObject = nsnull;
|
||||
|
||||
return nsXBLBinding::DoInitJSClass(aContext, aGlobal, aScriptObject,
|
||||
aClassName, aClassObject);
|
||||
aClassName, this, aClassObject);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "nsHashtable.h"
|
||||
#include "nsIXBLDocumentInfo.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsXBLProtoImpl.h"
|
||||
|
||||
class nsIAtom;
|
||||
class nsIDocument;
|
||||
|
@ -58,7 +59,7 @@ class nsISupportsArray;
|
|||
class nsSupportsHashtable;
|
||||
class nsIXBLService;
|
||||
class nsFixedSizeAllocator;
|
||||
class nsXBLProtoImpl;
|
||||
class nsXBLProtoImplField;
|
||||
class nsXBLBinding;
|
||||
|
||||
// *********************************************************************/
|
||||
|
@ -94,6 +95,22 @@ public:
|
|||
nsXBLProtoImplAnonymousMethod* GetDestructor();
|
||||
nsresult SetDestructor(nsXBLProtoImplAnonymousMethod* aDestructor);
|
||||
|
||||
nsXBLProtoImplField* FindField(const nsString& aFieldName) const
|
||||
{
|
||||
return mImplementation ? mImplementation->FindField(aFieldName) : nsnull;
|
||||
}
|
||||
|
||||
// Resolve all the fields for this binding on the object |obj|.
|
||||
// False return means a JS exception was set.
|
||||
PRBool ResolveAllFields(JSContext* cx, JSObject* obj) const
|
||||
{
|
||||
return !mImplementation || mImplementation->ResolveAllFields(cx, obj);
|
||||
}
|
||||
|
||||
const nsCString& ClassName() const {
|
||||
return mImplementation ? mImplementation->mClassName : EmptyCString();
|
||||
}
|
||||
|
||||
nsresult InitClass(const nsCString& aClassName, JSContext * aContext,
|
||||
JSObject * aGlobal, JSObject * aScriptObject,
|
||||
void ** aClassObject);
|
||||
|
@ -172,6 +189,7 @@ public:
|
|||
nsIContent* aElement);
|
||||
|
||||
void Traverse(nsCycleCollectionTraversalCallback &cb) const;
|
||||
void Unlink();
|
||||
|
||||
// Static members
|
||||
static PRUint32 gRefCnt;
|
||||
|
|
|
@ -60,7 +60,9 @@
|
|||
#include "nsContentUtils.h"
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLResourceLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsXBLResourceLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLResourceLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mBoundElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLResourceLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mBoundElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
|
|
@ -50,6 +50,7 @@ _TEST_FILES = \
|
|||
test_bug296375.xul \
|
||||
test_bug366770.html \
|
||||
test_bug371724.xhtml \
|
||||
test_bug372769.xhtml \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
|
|
@ -477,7 +477,8 @@ static const char kDOMStringBundleURL[] =
|
|||
// possible.
|
||||
|
||||
#define ELEMENT_SCRIPTABLE_FLAGS \
|
||||
(NODE_SCRIPTABLE_FLAGS & ~nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY)
|
||||
((NODE_SCRIPTABLE_FLAGS & ~nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY) | \
|
||||
nsIXPCScriptable::WANT_ENUMERATE)
|
||||
|
||||
#define EXTERNAL_OBJ_SCRIPTABLE_FLAGS \
|
||||
(ELEMENT_SCRIPTABLE_FLAGS & ~nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY | \
|
||||
|
@ -6940,6 +6941,32 @@ nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsElementSH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, PRBool *_retval)
|
||||
{
|
||||
// Make sure to not call the superclass here!
|
||||
nsCOMPtr<nsIContent> content(do_QueryWrappedNative(wrapper));
|
||||
NS_ENSURE_TRUE(content, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsIDocument* doc = content->GetOwnerDoc();
|
||||
if (!doc) {
|
||||
// Nothing else to do here
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsXBLBinding* binding = doc->BindingManager()->GetBinding(content);
|
||||
if (!binding) {
|
||||
// Nothing else to do here
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*_retval = binding->ResolveAllFields(cx, obj);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// Generic array scriptable helper.
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -563,6 +563,8 @@ protected:
|
|||
public:
|
||||
NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj);
|
||||
NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, PRBool *_retval);
|
||||
|
||||
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче