mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 11:55:49 +00:00
Do lazy evaluation of XBL fields. Bug 372769, r=mrbkap, sr=sicking
This commit is contained in:
parent
8929db3c74
commit
ffd52a5efc
@ -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,127 @@
|
||||
#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)
|
||||
{
|
||||
nsXBLPrototypeBinding* protoBinding =
|
||||
static_cast<nsXBLPrototypeBinding*>(::JS_GetPrivate(cx, obj));
|
||||
protoBinding->XBLDocumentInfo()->Release();
|
||||
|
||||
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);
|
||||
|
||||
nsXBLPrototypeBinding* protoBinding =
|
||||
static_cast<nsXBLPrototypeBinding*>(::JS_GetPrivate(cx, obj));
|
||||
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_NEW_RESOLVE | JSCLASS_NEW_RESOLVE_GETS_START;
|
||||
addProperty = delProperty = setProperty = getProperty = ::JS_PropertyStub;
|
||||
enumerate = ::JS_EnumerateStub;
|
||||
resolve = ::JS_ResolveStub;
|
||||
resolve = (JSResolveOp)XBLResolve;
|
||||
convert = ::JS_ConvertStub;
|
||||
finalize = XBLFinalize;
|
||||
}
|
||||
@ -1034,6 +1134,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 +1240,11 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
::JS_SetPrivate(cx, proto, aProtoBinding);
|
||||
|
||||
// Keep this proto binding alive while we're alive
|
||||
aProtoBinding->XBLDocumentInfo()->AddRef();
|
||||
|
||||
*aClassObject = (void*)proto;
|
||||
}
|
||||
else {
|
||||
@ -1155,63 +1261,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 +1393,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;
|
||||
|
@ -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,37 @@ nsXBLProtoImpl::Traverse(nsCycleCollectionTraversalCallback &cb) const
|
||||
}
|
||||
}
|
||||
|
||||
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,10 +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;
|
||||
|
||||
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,
|
||||
// all members before aBrokenMember are compiled, compilation of
|
||||
@ -93,6 +113,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;
|
||||
|
@ -822,7 +822,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);
|
||||
|
@ -50,6 +50,7 @@ _TEST_FILES = \
|
||||
test_bug296375.xul \
|
||||
test_bug366770.html \
|
||||
test_bug371724.xhtml \
|
||||
test_bug372769.xhtml \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
179
content/xbl/test/test_bug372769.xhtml
Normal file
179
content/xbl/test/test_bug372769.xhtml
Normal file
@ -0,0 +1,179 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=372769
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 372769</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<bindings xmlns="http://www.mozilla.org/xbl">
|
||||
<binding id="test1">
|
||||
<implementation>
|
||||
<field name="one">1</field>
|
||||
<field name="two">9</field>
|
||||
<field name="three">3</field>
|
||||
<field name="four">10</field>
|
||||
<field name="five">11</field>
|
||||
<field name="six">this.four = 4; 6;</field>
|
||||
<field name="seven">this.five = 5; 7;</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="test2">
|
||||
<implementation>
|
||||
<!-- Tests for recursive resolves -->
|
||||
<field name="eight">this.eight</field>
|
||||
<field name="nine">this.ten</field>
|
||||
<field name="ten">this.nine</field>
|
||||
<!-- Tests for non-DOM overrides -->
|
||||
<field name="eleven">11</field>
|
||||
<field name="twelve">12</field>
|
||||
<!-- Tests for DOM overrides -->
|
||||
<field name="parentNode">this.parentNode</field>
|
||||
<field name="ownerDocument">"ownerDocument override"</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="test3-ancestor">
|
||||
<implementation>
|
||||
<field name="thirteen">"13 ancestor"</field>
|
||||
<field name="fourteen">"14 ancestor"</field>
|
||||
<property name="fifteen" readonly="true" onget="return '15 ancestor'"/>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="test3" extends="#test3-ancestor">
|
||||
<implementation>
|
||||
<field name="thirteen">13</field>
|
||||
<field name="fifteen">15</field>
|
||||
<field name="sixteen">16</field>
|
||||
<field name="sixteen">"16 later"</field>
|
||||
<field name="seventeen">17</field>
|
||||
<field name="eighteen">"18 field"</field>
|
||||
<property name="eighteen" readonly="true" onget="return 18"/>
|
||||
<property name="nineteen" readonly="true" onget="return 19"/>
|
||||
<field name="nineteen">"19 field"</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="test4">
|
||||
<implementation>
|
||||
<field name="twenty">for (var i in this) ; 20;</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="test5">
|
||||
<implementation>
|
||||
<field name="twenty-one">for (var i in this) ; 21;</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=372769">Mozilla Bug 372769</a>
|
||||
<p id="display1" style="-moz-binding: url(#test1)"></p>
|
||||
<p id="display2" style="-moz-binding: url(#test2)"></p>
|
||||
<p id="display3" style="-moz-binding: url(#test3)"></p>
|
||||
<p id="display4" style="-moz-binding: url(#test4)"></p>
|
||||
<p id="display5" style="-moz-binding: url(#test5)"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for Bug 372769 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var d = $("display1");
|
||||
is(d.one, 1, "Should be able to read field");
|
||||
|
||||
d.two = 2;
|
||||
is(d.two, 2, "Should be able to write field");
|
||||
|
||||
is("three" in d, true, 'Should have a property named "three"');
|
||||
is(d.three, 3, "Should be 3");
|
||||
|
||||
is(d.four, 10, "Unexpected value so far");
|
||||
|
||||
// Save "five" for now
|
||||
|
||||
is(d.six, 6, "Should be 6");
|
||||
|
||||
is(d.four, 4, "Now should be 4");
|
||||
|
||||
d.four = 9;
|
||||
is(d.four, 9, "Just set it to 9");
|
||||
|
||||
var found = false;
|
||||
for (var prop in d) {
|
||||
if (prop == "seven") {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
is(found, true, "Enumeration is broken");
|
||||
|
||||
is(d.four, 9, "Shouldn't have rerun field six");
|
||||
is(d.five, 5, "Should have run field 7");
|
||||
is(d.seven, 7, "Should be 7")
|
||||
|
||||
d = $("display2");
|
||||
is(typeof(d.eight), "undefined", "Recursive resolve should bail out");
|
||||
is(typeof(d.nine), "undefined", "Recursive double resolve should bail out");
|
||||
is(typeof(d.ten), "undefined",
|
||||
"This recursive double resolve should bail out too");
|
||||
|
||||
// Get .eleven so it's resolved now
|
||||
is(d.eleven, 11, "Unexpected value for .eleven");
|
||||
var newProto = {};
|
||||
newProto.eleven = "Proto 11";
|
||||
newProto.twelve = "Proto 12";
|
||||
newProto.__proto__ = d.__proto__;
|
||||
d.__proto__ = newProto;
|
||||
is(d.eleven, 11, "Proto should not have affected this");
|
||||
is(d.twelve, "Proto 12", "Proto should have overridden 'twelve'");
|
||||
|
||||
is(d.parentNode, undefined, "We overrode this, yes we did");
|
||||
is(typeof(d.parentNode), "undefined", "This is a recursive resolve too");
|
||||
is(d.ownerDocument, "ownerDocument override",
|
||||
"Should have overridden ownerDocument");
|
||||
|
||||
d = $("display3");
|
||||
is(d.thirteen, 13, "descendant should win here");
|
||||
is(d.fourteen, "14 ancestor",
|
||||
"ancestor should win if descendant does nothing")
|
||||
is(d.fifteen, 15,
|
||||
"Field beats ancestor's property, since the latter lives on higher proto")
|
||||
is(d.sixteen, 16, "First field wins");
|
||||
is(d.__proto__.seventeen, undefined, "Shouldn't have this on proto");
|
||||
is(typeof(d.__proto__.seventeen), "undefined",
|
||||
"Really, should be undefined");
|
||||
is(d.seventeen, 17, "Should have this prop on the node itself, though");
|
||||
is(d.eighteen, 18, "Property beats field");
|
||||
is(d.nineteen, 19, "Property still beats field");
|
||||
|
||||
d = $("display4");
|
||||
is(d.twenty, 20, "Should be 20");
|
||||
|
||||
d = $("display5");
|
||||
found = false;
|
||||
for (var prop2 in d) {
|
||||
if (prop2 == "twenty-one") {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
is(found, true, "Enumeration is broken");
|
||||
is(d["twenty-one"], 21, "Should be 21");
|
||||
|
||||
});
|
||||
addLoadEvent(SimpleTest.finish);
|
||||
]]>
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user