diff --git a/content/xbl/public/Makefile.in b/content/xbl/public/Makefile.in index 90d7e3a4324..6acdb572ed2 100644 --- a/content/xbl/public/Makefile.in +++ b/content/xbl/public/Makefile.in @@ -33,10 +33,9 @@ EXPORTS = \ nsIXBLBinding.h \ nsIXBLBindingAttachedHandler.h \ nsIXBLDocumentInfo.h \ - nsIXBLInsertionPoint.h \ + nsIXBLInsertionPoint.h \ nsIXBLPrototypeBinding.h \ nsIXBLPrototypeHandler.h \ - nsIXBLPrototypeProperty.h \ nsIXBLService.h \ $(NULL) diff --git a/content/xbl/public/makefile.win b/content/xbl/public/makefile.win index d2984f4fa9f..e69de29bb2d 100644 --- a/content/xbl/public/makefile.win +++ b/content/xbl/public/makefile.win @@ -1,38 +0,0 @@ -#!nmake -# -# The contents of this file are subject to the Netscape Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/NPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is Netscape -# Communications Corporation. Portions created by Netscape are -# Copyright (C) 1998 Netscape Communications Corporation. All -# Rights Reserved. -# -# Contributor(s): - -DEPTH=..\..\.. - -EXPORTS = \ - nsIBindingManager.h \ - nsIXBLBinding.h \ - nsIXBLBindingAttachedHandler.h \ - nsIXBLDocumentInfo.h \ - nsIXBLInsertionPoint.h \ - nsIXBLPrototypeBinding.h \ - nsIXBLPrototypeHandler.h \ - nsIXBLService.h \ - nsIXBLPrototypeProperty.h \ - $(NULL) - -MODULE=content - -include <$(DEPTH)\config\rules.mak> diff --git a/content/xbl/public/nsIXBLBinding.h b/content/xbl/public/nsIXBLBinding.h index 4f26f81deb1..4efae8ed333 100644 --- a/content/xbl/public/nsIXBLBinding.h +++ b/content/xbl/public/nsIXBLBinding.h @@ -82,7 +82,7 @@ public: NS_IMETHOD GenerateAnonymousContent() = 0; NS_IMETHOD InstallEventHandlers() = 0; - NS_IMETHOD InstallProperties() = 0; + NS_IMETHOD InstallImplementation() = 0; NS_IMETHOD HasStyleSheets(PRBool* aResolveStyle) = 0; diff --git a/content/xbl/public/nsIXBLPrototypeBinding.h b/content/xbl/public/nsIXBLPrototypeBinding.h index d4487e0ae04..1694a97ad2d 100644 --- a/content/xbl/public/nsIXBLPrototypeBinding.h +++ b/content/xbl/public/nsIXBLPrototypeBinding.h @@ -52,6 +52,7 @@ class nsIXBLBinding; class nsISupportsArray; class nsCString; class nsIScriptContext; +class nsXBLProtoImpl; // {34D700F5-C1A2-4408-A0B1-DD8F891DD1FE} #define NS_IXBLPROTOTYPEBINDING_IID \ @@ -82,12 +83,18 @@ public: NS_IMETHOD GetPrototypeHandlers(nsIXBLPrototypeHandler** aHandler)=0; NS_IMETHOD SetPrototypeHandlers(nsIXBLPrototypeHandler* aHandler)=0; - NS_IMETHOD GetPrototypeProperties(nsIXBLPrototypeProperty** aResult) = 0; - NS_IMETHOD SetProtoTypeProperties(nsIXBLPrototypeProperty* aResult) = 0; - NS_IMETHOD GetCompiledClassObject(const nsCString& aClassName, nsIScriptContext * aContext, void * aScriptObject, void ** aClassObject) = 0; - + NS_IMETHOD GetConstructor(nsIXBLPrototypeHandler** aResult)=0; + NS_IMETHOD SetConstructor(nsIXBLPrototypeHandler* aConstructor)=0; + NS_IMETHOD GetDestructor(nsIXBLPrototypeHandler** aResult)=0; + NS_IMETHOD SetDestructor(nsIXBLPrototypeHandler* aDestructor)=0; + NS_IMETHOD InitClass(const nsCString& aClassName, nsIScriptContext * aContext, void * aScriptObject, void ** aClassObject) = 0; + NS_IMETHOD SetImplementation(nsXBLProtoImpl* aImpl)=0; + NS_IMETHOD InstallImplementation(nsIContent* aBoundElement)=0; + + NS_IMETHOD ConstructInterfaceTable(const nsAReadableString& aImpls)=0; + NS_IMETHOD AttributeChanged(nsIAtom* aAttribute, PRInt32 aNameSpaceID, PRBool aRemoveFlag, nsIContent* aChangedElement, nsIContent* aAnonymousContent)=0; @@ -126,8 +133,6 @@ public: NS_IMETHOD AddResourceListener(nsIContent* aBoundElement)=0; - NS_IMETHOD GetConstructor(nsIXBLPrototypeHandler** aResult)=0; - NS_IMETHOD Initialize()=0; }; diff --git a/content/xbl/public/nsIXBLPrototypeHandler.h b/content/xbl/public/nsIXBLPrototypeHandler.h index 0937d90834c..a8846c5cc37 100644 --- a/content/xbl/public/nsIXBLPrototypeHandler.h +++ b/content/xbl/public/nsIXBLPrototypeHandler.h @@ -69,7 +69,7 @@ public: NS_IMETHOD KeyEventMatched(nsIAtom* aEventType, nsIDOMKeyEvent* aEvent, PRBool* aResult) = 0; NS_IMETHOD GetHandlerElement(nsIContent** aResult) = 0; - NS_IMETHOD SetHandlerText(const nsAReadableString& aText) = 0; + NS_IMETHOD AppendHandlerText(const nsAReadableString& aText) = 0; NS_IMETHOD GetPhase(PRUint8* aPhase) = 0; diff --git a/content/xbl/public/nsIXBLPrototypeProperty.h b/content/xbl/public/nsIXBLPrototypeProperty.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/content/xbl/src/Makefile.in b/content/xbl/src/Makefile.in index c633495ed01..aaa337199c7 100644 --- a/content/xbl/src/Makefile.in +++ b/content/xbl/src/Makefile.in @@ -62,7 +62,10 @@ CPPSRCS = \ nsXBLResourceLoader.cpp \ nsXBLDocumentInfo.cpp \ nsXBLContentSink.cpp \ - nsXBLPrototypeProperty.cpp \ + nsXBLProtoImplProperty.cpp \ + nsXBLProtoImplMethod.cpp \ + nsXBLProtoImplField.cpp \ + nsXBLProtoImpl.cpp \ nsXBLEventHandler.cpp \ nsXBLWindowHandler.cpp \ nsXBLWindowKeyHandler.cpp \ diff --git a/content/xbl/src/makefile.win b/content/xbl/src/makefile.win index 77715424b5c..e149c9ac239 100644 --- a/content/xbl/src/makefile.win +++ b/content/xbl/src/makefile.win @@ -76,7 +76,10 @@ CPPSRCS= \ nsXBLPrototypeHandler.cpp \ nsBindingManager.cpp \ nsXBLInsertionPoint.cpp \ - nsXBLPrototypeProperty.cpp \ + nsXBLProtoImplProperty.cpp \ + nsXBLProtoImplMethod.cpp \ + nsXBLProtoImplField.cpp \ + nsXBLProtoImpl.cpp \ $(NULL) CPP_OBJS= \ @@ -105,7 +108,10 @@ CPP_OBJS= \ .\$(OBJDIR)\nsXBLService.obj \ .\$(OBJDIR)\nsBindingManager.obj \ .\$(OBJDIR)\nsXBLInsertionPoint.obj \ - .\$(OBJDIR)\nsXBLPrototypeProperty.obj \ + .\$(OBJDIR)\nsXBLProtoImplProperty.obj \ + .\$(OBJDIR)\nsXBLProtoImplMethod.obj \ + .\$(OBJDIR)\nsXBLProtoImplField.obj \ + .\$(OBJDIR)\nsXBLProtoImpl.obj \ $(NULL) EXPORTS = \ diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index a274f326e2d..3c499bcf395 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -94,7 +94,6 @@ #include "nsIDOMAttr.h" #include "nsIDOMNamedNodeMap.h" -#include "nsIXBLPrototypeProperty.h" #include "nsXBLPrototypeHandler.h" #include "nsXBLKeyHandler.h" @@ -994,47 +993,17 @@ nsXBLBinding::InstallEventHandlers() } NS_IMETHODIMP -nsXBLBinding::InstallProperties() +nsXBLBinding::InstallImplementation() { // Always install the base class properties first, so that // derived classes can reference the base class properties. if (mNextBinding) - mNextBinding->InstallProperties(); + mNextBinding->InstallImplementation(); // iterate through each property in the prototype's list and install the property. - if (AllowScripts()) { - nsCOMPtr propertyChain; - mPrototypeBinding->GetPrototypeProperties(getter_AddRefs(propertyChain)); - - if (!propertyChain) return NS_OK; // kick out if our list is empty - - nsCOMPtr document; - mBoundElement->GetDocument(*getter_AddRefs(document)); - if (!document) return NS_OK; - - nsCOMPtr global; - document->GetScriptGlobalObject(getter_AddRefs(global)); - if (!global) return NS_OK; - - nsCOMPtr context; - nsresult rv = global->GetContext(getter_AddRefs(context)); - NS_ENSURE_SUCCESS(rv, rv); - - if (!context) return NS_OK; - - void * targetScriptObject = nsnull; - void * targetClassObject = nsnull; - rv = propertyChain->InitTargetObjects(context, mBoundElement, &targetScriptObject, &targetClassObject); - NS_ENSURE_SUCCESS(rv, rv); // kick out if we were unable to properly intialize our target objects - - nsCOMPtr curr = propertyChain; - do { - curr->InstallProperty(context, mBoundElement, targetScriptObject, targetClassObject); - curr->GetNextProperty(getter_AddRefs(propertyChain)); - curr = propertyChain; - } while (curr); - } + if (AllowScripts()) + mPrototypeBinding->InstallImplementation(mBoundElement); return NS_OK; } diff --git a/content/xbl/src/nsXBLBinding.h b/content/xbl/src/nsXBLBinding.h index 3b02a637ead..392dadaff3f 100644 --- a/content/xbl/src/nsXBLBinding.h +++ b/content/xbl/src/nsXBLBinding.h @@ -76,7 +76,7 @@ class nsXBLBinding: public nsIXBLBinding NS_IMETHOD GenerateAnonymousContent(); NS_IMETHOD InstallEventHandlers(); - NS_IMETHOD InstallProperties(); + NS_IMETHOD InstallImplementation(); NS_IMETHOD HasStyleSheets(PRBool* aResolveStyle); diff --git a/content/xbl/src/nsXBLContentSink.cpp b/content/xbl/src/nsXBLContentSink.cpp index de7c846c120..037eafe7ae4 100644 --- a/content/xbl/src/nsXBLContentSink.cpp +++ b/content/xbl/src/nsXBLContentSink.cpp @@ -51,6 +51,9 @@ #include "nsTextFragment.h" #include "nsXULElement.h" #include "nsXULAtoms.h" +#include "nsXBLProtoImplProperty.h" +#include "nsXBLProtoImplMethod.h" +#include "nsXBLProtoImplField.h" static NS_DEFINE_CID(kCSSParserCID, NS_CSSPARSER_CID); @@ -79,6 +82,11 @@ nsXBLContentSink::nsXBLContentSink() mSecondaryState = eXBL_None; mDocInfo = nsnull; mIsChromeOrResource = PR_FALSE; + mImplementation = nsnull; + mImplMember = nsnull; + mProperty = nsnull; + mMethod = nsnull; + mField = nsnull; } nsXBLContentSink::~nsXBLContentSink() @@ -137,13 +145,57 @@ nsXBLContentSink::OnOpenContainer(const nsIParserNode& aNode, PRInt32 aNameSpace mState = eXBL_InResources; ret = PR_FALSE; // The XML content sink should ignore all . } - else if (mState == eXBL_InResources && (aTagName == nsXBLAtoms::stylesheet || aTagName == nsXBLAtoms::image)) { - ConstructResource(aNode, aTagName); - ret = PR_FALSE; // The XML content sink should ignore all . + else if (mState == eXBL_InResources) { + if (aTagName == nsXBLAtoms::stylesheet || aTagName == nsXBLAtoms::image) + ConstructResource(aNode, aTagName); + ret = PR_FALSE; // The XML content sink should ignore everything within a block. } else if (aTagName == nsXBLAtoms::implementation) { - // mState = eXBL_InImplementation; - // ret = PR_FALSE; // The XML content sink should ignore the . + mState = eXBL_InImplementation; + ConstructImplementation(aNode); + ret = PR_FALSE; // The XML content sink should ignore the . + } + else if (mState == eXBL_InImplementation) { + if (aTagName == nsXBLAtoms::constructor) { + mSecondaryState = eXBL_InConstructor; + nsCOMPtr newHandler; + NS_NewXBLPrototypeHandler(nsnull, nsnull, nsnull, nsnull, + nsnull, nsnull, nsnull, nsnull, nsnull, + getter_AddRefs(newHandler)); + newHandler->SetEventName(nsXBLAtoms::constructor); + mBinding->SetConstructor(newHandler); + } + else if (aTagName == nsXBLAtoms::destructor) { + mSecondaryState = eXBL_InDestructor; + nsCOMPtr newHandler; + NS_NewXBLPrototypeHandler(nsnull, nsnull, nsnull, nsnull, + nsnull, nsnull, nsnull, nsnull, nsnull, + getter_AddRefs(newHandler)); + newHandler->SetEventName(nsXBLAtoms::destructor); + mBinding->SetDestructor(newHandler); + } + else if (aTagName == nsXBLAtoms::field) { + mSecondaryState = eXBL_InField; + ConstructField(aNode); + } + else if (aTagName == nsXBLAtoms::property) { + mSecondaryState = eXBL_InProperty; + ConstructProperty(aNode); + } + else if (aTagName == nsXBLAtoms::getter) + mSecondaryState = eXBL_InGetter; + else if (aTagName == nsXBLAtoms::setter) + mSecondaryState = eXBL_InSetter; + else if (aTagName == nsXBLAtoms::method) { + mSecondaryState = eXBL_InMethod; + ConstructMethod(aNode); + } + else if (aTagName == nsXBLAtoms::parameter) + ConstructParameter(aNode); + else if (aTagName == nsXBLAtoms::body) + mSecondaryState = eXBL_InBody; + + ret = PR_FALSE; // Ignore everything we encounter inside an block. } } @@ -188,6 +240,32 @@ nsXBLContentSink::CloseContainer(const nsIParserNode& aNode) mState = eXBL_InBinding; return NS_OK; } + else if (mState == eXBL_InImplementation) { + if (tagAtom == nsXBLAtoms::implementation) + mState = eXBL_InBinding; + else if (tagAtom == nsXBLAtoms::property) { + mSecondaryState = eXBL_None; + mProperty = nsnull; + } + else if (tagAtom == nsXBLAtoms::method) { + mSecondaryState = eXBL_None; + mMethod = nsnull; + } + else if (tagAtom == nsXBLAtoms::field) { + mSecondaryState = eXBL_None; + mField = nsnull; + } + else if (tagAtom == nsXBLAtoms::constructor || + tagAtom == nsXBLAtoms::destructor) + mSecondaryState = eXBL_None; + else if (tagAtom == nsXBLAtoms::getter || + tagAtom == nsXBLAtoms::setter) + mSecondaryState = eXBL_InProperty; + else if (tagAtom == nsXBLAtoms::parameter || + tagAtom == nsXBLAtoms::body) + mSecondaryState = eXBL_InMethod; + return NS_OK; + } nsresult rv = nsXMLContentSink::CloseContainer(aNode); if (NS_FAILED(rv)) @@ -243,21 +321,125 @@ nsXBLContentSink::AddLeaf(const nsIParserNode& aNode) // Get the text and add it to the event handler. switch (aNode.GetTokenType()) { case eToken_text: + case eToken_whitespace: + case eToken_newline: case eToken_cdatasection: - mHandler->SetHandlerText(aNode.GetText()); + mHandler->AppendHandlerText(aNode.GetText()); + break; case eToken_entity: { nsAutoString tmp; PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); if (unicode < 0) - mHandler->SetHandlerText(aNode.GetText()); + mHandler->AppendHandlerText(aNode.GetText()); else - mHandler->SetHandlerText(aNode.GetText()); + mHandler->AppendHandlerText(tmp); } } } return NS_OK; } + else if (mState == eXBL_InImplementation) { + if (mSecondaryState == eXBL_InConstructor || + mSecondaryState == eXBL_InDestructor) { + // Construct a handler for the constructor/destructor. + // XXXdwh This is just awful. These used to be handlers called + // BindingAttached and BindingDetached, and they're still implemented + // using handlers. At some point, we need to change these to just + // be special functions on the class instead. + nsCOMPtr handler; + if (mSecondaryState == eXBL_InConstructor) + mBinding->GetConstructor(getter_AddRefs(handler)); + else + mBinding->GetDestructor(getter_AddRefs(handler)); + + // Get the text and add it to the constructor/destructor. + switch (aNode.GetTokenType()) { + case eToken_text: + case eToken_whitespace: + case eToken_newline: + case eToken_cdatasection: + handler->AppendHandlerText(aNode.GetText()); + break; + case eToken_entity: + { + nsAutoString tmp; + PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); + if (unicode < 0) + handler->AppendHandlerText(aNode.GetText()); + else + handler->AppendHandlerText(tmp); + } + } + } + else if (mSecondaryState == eXBL_InGetter || + mSecondaryState == eXBL_InSetter) { + // Get the text and add it to the constructor/destructor. + PRBool getter = mSecondaryState == eXBL_InGetter; + switch (aNode.GetTokenType()) { + case eToken_text: + case eToken_whitespace: + case eToken_newline: + case eToken_cdatasection: + getter ? mProperty->AppendGetterText(aNode.GetText()) : + mProperty->AppendSetterText(aNode.GetText()); + break; + case eToken_entity: + { + nsAutoString tmp; + PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); + if (unicode < 0) + getter ? mProperty->AppendGetterText(aNode.GetText()) : + mProperty->AppendSetterText(aNode.GetText()); + else + getter ? mProperty->AppendGetterText(tmp) : + mProperty->AppendSetterText(tmp); + } + } + } + else if (mSecondaryState == eXBL_InBody) { + // Get the text and add it to the method + switch (aNode.GetTokenType()) { + case eToken_text: + case eToken_whitespace: + case eToken_newline: + case eToken_cdatasection: + mMethod->AppendBodyText(aNode.GetText()); + break; + case eToken_entity: + { + nsAutoString tmp; + PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); + if (unicode < 0) + mMethod->AppendBodyText(aNode.GetText()); + else + mMethod->AppendBodyText(tmp); + } + } + } + else if (mSecondaryState == eXBL_InField) { + // Get the text and add it to the method + switch (aNode.GetTokenType()) { + case eToken_text: + case eToken_whitespace: + case eToken_newline: + case eToken_cdatasection: + mField->AppendFieldText(aNode.GetText()); + break; + case eToken_entity: + { + nsAutoString tmp; + PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); + if (unicode < 0) + mField->AppendFieldText(aNode.GetText()); + else + mField->AppendFieldText(tmp); + } + } + } + + return NS_OK; + } return nsXMLContentSink::AddLeaf(aNode); } @@ -504,3 +686,184 @@ nsXBLContentSink::ConstructResource(const nsIParserNode& aNode, nsIAtom* aResour } } } + +void +nsXBLContentSink::ConstructImplementation(const nsIParserNode& aNode) +{ + mImplementation = nsnull; + mImplMember = nsnull; + + if (!mBinding) + return; + + nsAReadableString* name = nsnull; + + nsCOMPtr nameSpacePrefix, nameAtom; + PRInt32 ac = aNode.GetAttributeCount(); + for (PRInt32 i = 0; i < ac; i++) { + // Get upper-cased key + const nsAReadableString& key = aNode.GetKeyAt(i); + + SplitXMLName(key, getter_AddRefs(nameSpacePrefix), + getter_AddRefs(nameAtom)); + + if (nameSpacePrefix || nameAtom == nsLayoutAtoms::xmlnsNameSpace) + continue; + + // Is this attribute one of the ones we care about? + if (key.Equals(NS_LITERAL_STRING("name"))) + name = &(aNode.GetValueAt(i)); + else if (key.Equals(NS_LITERAL_STRING("implements"))) + mBinding->ConstructInterfaceTable(aNode.GetValueAt(i)); + } + + NS_NewXBLProtoImpl(mBinding, name, &mImplementation); +} + +void +nsXBLContentSink::ConstructField(const nsIParserNode& aNode) +{ + nsCOMPtr nameSpacePrefix, nameAtom; + PRInt32 ac = aNode.GetAttributeCount(); + + nsAReadableString* name = nsnull; + nsAReadableString* readonly = nsnull; + + for (PRInt32 i = 0; i < ac; i++) { + // Get upper-cased key + const nsAReadableString& key = aNode.GetKeyAt(i); + + SplitXMLName(key, getter_AddRefs(nameSpacePrefix), + getter_AddRefs(nameAtom)); + + if (nameSpacePrefix || nameAtom == nsLayoutAtoms::xmlnsNameSpace) + continue; + + // Is this attribute one of the ones we care about? + if (key.Equals(NS_LITERAL_STRING("name"))) + name = &(aNode.GetValueAt(i)); + else if (key.Equals(NS_LITERAL_STRING("readonly"))) + readonly = &(aNode.GetValueAt(i)); + } + + // All of our pointers are now filled in. Construct our field with all of these + // parameters. + mField = new nsXBLProtoImplField(name, readonly); + if (mField) { + // Add this member to our chain. + if (mImplMember) + mImplMember->SetNext(mField); // Already have a chain. Just append to the end. + else + mImplementation->SetMemberList(mField); // We're the first member in the chain. + + mImplMember = mField; // Adjust our pointer to point to the new last member in the chain. + } +} + +void +nsXBLContentSink::ConstructProperty(const nsIParserNode& aNode) +{ + nsCOMPtr nameSpacePrefix, nameAtom; + PRInt32 ac = aNode.GetAttributeCount(); + + nsAReadableString* name = nsnull; + nsAReadableString* readonly = nsnull; + nsAReadableString* onget = nsnull; + nsAReadableString* onset = nsnull; + + for (PRInt32 i = 0; i < ac; i++) { + // Get upper-cased key + const nsAReadableString& key = aNode.GetKeyAt(i); + + SplitXMLName(key, getter_AddRefs(nameSpacePrefix), + getter_AddRefs(nameAtom)); + + if (nameSpacePrefix || nameAtom == nsLayoutAtoms::xmlnsNameSpace) + continue; + + // Is this attribute one of the ones we care about? + if (key.Equals(NS_LITERAL_STRING("name"))) + name = &(aNode.GetValueAt(i)); + else if (key.Equals(NS_LITERAL_STRING("readonly"))) + readonly = &(aNode.GetValueAt(i)); + else if (key.Equals(NS_LITERAL_STRING("onget"))) + onget = &(aNode.GetValueAt(i)); + else if (key.Equals(NS_LITERAL_STRING("onset"))) + onset = &(aNode.GetValueAt(i)); + } + + // All of our pointers are now filled in. Construct our property with all of these + // parameters. + mProperty = new nsXBLProtoImplProperty(name, onget, onset, readonly); + if (mProperty) { + // Add this member to our chain. + if (mImplMember) + mImplMember->SetNext(mProperty); // Already have a chain. Just append to the end. + else + mImplementation->SetMemberList(mProperty); // We're the first member in the chain. + + mImplMember = mProperty; // Adjust our pointer to point to the new last member in the chain. + } +} + +void +nsXBLContentSink::ConstructMethod(const nsIParserNode& aNode) +{ + mMethod = nsnull; + + nsCOMPtr nameSpacePrefix, nameAtom; + PRInt32 ac = aNode.GetAttributeCount(); + + for (PRInt32 i = 0; i < ac; i++) { + // Get upper-cased key + const nsAReadableString& key = aNode.GetKeyAt(i); + + SplitXMLName(key, getter_AddRefs(nameSpacePrefix), + getter_AddRefs(nameAtom)); + + if (nameSpacePrefix || nameAtom == nsLayoutAtoms::xmlnsNameSpace) + continue; + + // Is this attribute one of the ones we care about? + if (key.Equals(NS_LITERAL_STRING("name"))) { + mMethod = new nsXBLProtoImplMethod(aNode.GetValueAt(i)); + break; + } + } + + if (mMethod) { + // Add this member to our chain. + if (mImplMember) + mImplMember->SetNext(mMethod); // Already have a chain. Just append to the end. + else + mImplementation->SetMemberList(mMethod); // We're the first member in the chain. + + mImplMember = mMethod; // Adjust our pointer to point to the new last member in the chain. + } +} + +void +nsXBLContentSink::ConstructParameter(const nsIParserNode& aNode) +{ + if (!mMethod) + return; + + nsCOMPtr nameSpacePrefix, nameAtom; + PRInt32 ac = aNode.GetAttributeCount(); + for (PRInt32 i = 0; i < ac; i++) { + // Get upper-cased key + const nsAReadableString& key = aNode.GetKeyAt(i); + + SplitXMLName(key, getter_AddRefs(nameSpacePrefix), + getter_AddRefs(nameAtom)); + + if (nameSpacePrefix || nameAtom == nsLayoutAtoms::xmlnsNameSpace) + continue; + + // Is this attribute one of the ones we care about? + if (key.Equals(NS_LITERAL_STRING("name"))) { + mMethod->AddParameter(aNode.GetValueAt(i)); + break; + } + } +} diff --git a/content/xbl/src/nsXBLContentSink.h b/content/xbl/src/nsXBLContentSink.h index 1d6933f018f..379d0b87496 100644 --- a/content/xbl/src/nsXBLContentSink.h +++ b/content/xbl/src/nsXBLContentSink.h @@ -43,6 +43,7 @@ #include "nsXBLDocumentInfo.h" #include "nsIXBLPrototypeBinding.h" #include "nsIXBLPrototypeHandler.h" +#include "nsXBLProtoImpl.h" #include "nsICSSParser.h" #include "nsLayoutCID.h" @@ -62,10 +63,16 @@ typedef enum { eXBL_InField, eXBL_InBody, eXBL_InGetter, - eXBL_InSetter + eXBL_InSetter, + eXBL_InConstructor, + eXBL_InDestructor } XBLSecondaryState; class nsXULPrototypeElement; +class nsXBLProtoImplMember; +class nsXBLProtoImplProperty; +class nsXBLProtoImplMethod; +class nsXBLProtoImplField; // The XBL content sink overrides the XML content sink to // builds its own lightweight data structures for the , @@ -100,6 +107,12 @@ protected: void ConstructBinding(); void ConstructHandler(const nsIParserNode& aNode); void ConstructResource(const nsIParserNode& aNode, nsIAtom* aResourceType); + void ConstructImplementation(const nsIParserNode& aNode); + void ConstructProperty(const nsIParserNode& aNode); + void ConstructMethod(const nsIParserNode& aNode); + void ConstructParameter(const nsIParserNode& aNode); + void ConstructField(const nsIParserNode& aNode); + nsresult AddAttributesToXULPrototype(const nsIParserNode& aNode, nsXULPrototypeElement* aElement); protected: @@ -112,6 +125,11 @@ protected: nsCOMPtr mBinding; nsCOMPtr mHandler; + nsXBLProtoImpl* mImplementation; + nsXBLProtoImplMember* mImplMember; + nsXBLProtoImplProperty* mProperty; + nsXBLProtoImplMethod* mMethod; + nsXBLProtoImplField* mField; }; diff --git a/content/xbl/src/nsXBLProtoImpl.cpp b/content/xbl/src/nsXBLProtoImpl.cpp new file mode 100644 index 00000000000..7e032e6bc34 --- /dev/null +++ b/content/xbl/src/nsXBLProtoImpl.cpp @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsXBLProtoImpl.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScriptGlobalObjectOwner.h" +#include "nsIScriptContext.h" +#include "nsIXPConnect.h" +#include "nsIServiceManager.h" +#include "nsIXBLDocumentInfo.h" + +nsresult +nsXBLProtoImpl::InstallImplementation(nsIXBLPrototypeBinding* aBinding, nsIContent* aBoundElement) +{ + // This function is called to install a concrete implementation on a bound element using + // 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) + return NS_OK; // Nothing to do, so let's not waste time. + + nsCOMPtr document; + aBoundElement->GetDocument(*getter_AddRefs(document)); + if (!document) return NS_OK; + + nsCOMPtr global; + document->GetScriptGlobalObject(getter_AddRefs(global)); + if (!global) return NS_OK; + + nsCOMPtr context; + nsresult rv = global->GetContext(getter_AddRefs(context)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!context) return NS_OK; + + // InitTarget objects gives us back the JS object that represents the bound element and the + // class object in the bound document that represents the concrete version of this implementation. + // This function also has the side effect of building up the prototype implementation if it has + // not been built already. + void * targetScriptObject = nsnull; + void * targetClassObject = nsnull; + rv = InitTargetObjects(aBinding, context, aBoundElement, &targetScriptObject, &targetClassObject); + NS_ENSURE_SUCCESS(rv, rv); // kick out if we were unable to properly intialize our target objects + + // Walk our member list and install each one in turn. + for (nsXBLProtoImplMember* curr = mMembers; + curr; + curr = curr->GetNext()) + curr->InstallMember(context, aBoundElement, targetScriptObject, targetClassObject); + return NS_OK; +} + +nsresult +nsXBLProtoImpl::InitTargetObjects(nsIXBLPrototypeBinding* aBinding, + nsIScriptContext* aContext, + nsIContent* aBoundElement, + void** aScriptObject, + void** aTargetClassObject) +{ + if (!mClassObject) + CompilePrototypeMembers(aBinding); // This is the first time we've ever installed this binding on an element. + // We need to go ahead and compile all methods and properties on a class + // in our prototype binding. + + if (!mClassObject) + return NS_OK; // This can be ok, if all we've got are fields (and no methods/properties). + + // Because our prototype implementation has a class, we need to build up a corresponding + // class for the concrete implementation in the bound document. + nsresult rv = NS_OK; + JSContext* jscontext = (JSContext*)aContext->GetNativeContext(); + JSObject* global = ::JS_GetGlobalObject(jscontext); + nsCOMPtr wrapper; + nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); + NS_ENSURE_SUCCESS(rv, rv); + rv = xpc->WrapNative(jscontext, global, aBoundElement, + NS_GET_IID(nsISupports), getter_AddRefs(wrapper)); + NS_ENSURE_SUCCESS(rv, rv); + JSObject * object = nsnull; + rv = wrapper->GetJSObject(&object); + NS_ENSURE_SUCCESS(rv, rv); + + // All of the above code was just obtaining the bound element's script object and its immediate + // concrete base class. We need to alter the object so that our concrete class is interposed + // between the object and its base class. We become the new base class of the object, and the + // object's old base class becomes the new class' base class. + *aScriptObject = object; + aBinding->InitClass(mClassName, aContext, (void *) object, aTargetClassObject); + + // Root ourselves in the document. + nsCOMPtr doc; + aBoundElement->GetDocument(*getter_AddRefs(doc)); + if (doc) { + nsCOMPtr nativeWrapper(do_QueryInterface(wrapper)); + if (nativeWrapper) + doc->AddReference(aBoundElement, nativeWrapper); + } + + return rv; +} + +nsresult +nsXBLProtoImpl::CompilePrototypeMembers(nsIXBLPrototypeBinding* aBinding) +{ + // We want to pre-compile our implementation's members against a "prototype context". Then when we actually + // bind the prototype to a real xbl instance, we'll clone the pre-compiled JS into the real instance's + // context. + nsCOMPtr docInfo; + aBinding->GetXBLDocumentInfo(nsnull, getter_AddRefs(docInfo)); + if (!docInfo) + return NS_ERROR_FAILURE; + + nsCOMPtr globalOwner(do_QueryInterface(docInfo)); + nsCOMPtr globalObject; + globalOwner->GetScriptGlobalObject(getter_AddRefs(globalObject)); + + nsCOMPtr context; + globalObject->GetContext(getter_AddRefs(context)); + + void* classObject; + JSObject* scopeObject = globalObject->GetGlobalJSObject(); + aBinding->InitClass(mClassName, context, scopeObject, &classObject); + mClassObject = (JSObject*) classObject; + if (!mClassObject) + return NS_ERROR_FAILURE; + + // Now that we have a class object installed, we walk our member list and compile each of our + // properties and methods in turn. + for (nsXBLProtoImplMember* curr = mMembers; + curr; + curr = curr->GetNext()) { + curr->CompileMember(context, mClassName, mClassObject); + } + return NS_OK; +} \ No newline at end of file diff --git a/content/xbl/src/nsXBLProtoImpl.h b/content/xbl/src/nsXBLProtoImpl.h new file mode 100644 index 00000000000..56232ed3a41 --- /dev/null +++ b/content/xbl/src/nsXBLProtoImpl.h @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXBLProtoImpl_h__ +#define nsXBLProtoImpl_h__ + +#include "nsMemory.h" +#include "nsIXBLPrototypeBinding.h" +#include "nsIXBLPrototypeHandler.h" +#include "nsXBLProtoImplMember.h" + +MOZ_DECL_CTOR_COUNTER(nsXBLProtoImpl); + +class nsXBLProtoImpl +{ +public: + nsXBLProtoImpl() + :mClassObject(nsnull) + { + MOZ_COUNT_CTOR(nsXBLProtoImpl); + mMembers = nsnull; + }; + ~nsXBLProtoImpl() + { + MOZ_COUNT_DTOR(nsXBLProtoImpl); + for (nsXBLProtoImplMember* curr = mMembers; curr; curr=curr->GetNext()) + curr->Destroy(mClassObject != nsnull); + delete mMembers; + }; + + nsresult InstallImplementation(nsIXBLPrototypeBinding* aBinding, nsIContent* aBoundElement); + nsresult InitTargetObjects(nsIXBLPrototypeBinding* aBinding, nsIScriptContext* aContext, + nsIContent* aBoundElement, + void** aScriptObject, void** aTargetClassObject); + nsresult CompilePrototypeMembers(nsIXBLPrototypeBinding* aBinding); + + void SetMemberList(nsXBLProtoImplMember* aMemberList) { delete mMembers; mMembers = aMemberList; }; + +public: + nsCString mClassName; // The name of the class. + void* mClassObject; // The class object for the binding. We'll use this to pre-compile properties + // and methods for the binding. + + nsXBLProtoImplMember* mMembers; // The members of an implementation are chained in this singly-linked list. + + nsCOMPtr mConstructor; // Our class constructor. + nsCOMPtr mDestructor; // Our class destructor. +}; + +static nsresult +NS_NewXBLProtoImpl(nsIXBLPrototypeBinding* aBinding, + const nsAReadableString* aClassName, + nsXBLProtoImpl** aResult) { + nsXBLProtoImpl* impl = new nsXBLProtoImpl(); + if (!impl) + return NS_ERROR_OUT_OF_MEMORY; + if (aClassName) + impl->mClassName.AssignWithConversion(*aClassName); + else + aBinding->GetBindingURI(impl->mClassName); + aBinding->SetImplementation(impl); + *aResult = impl; + return NS_OK; +} + +#endif // nsXBLProtoImpl_h__ diff --git a/content/xbl/src/nsXBLProtoImplField.cpp b/content/xbl/src/nsXBLProtoImplField.cpp new file mode 100644 index 00000000000..ccaa97044d0 --- /dev/null +++ b/content/xbl/src/nsXBLProtoImplField.cpp @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIAtom.h" +#include "nsString.h" +#include "jsapi.h" +#include "nsIContent.h" +#include "nsString.h" +#include "nsUnicharUtils.h" +#include "nsReadableUtils.h" +#include "nsXBLProtoImplField.h" +#include "nsIScriptContext.h" + +MOZ_DECL_CTOR_COUNTER(nsXBLProtoImplField); + +nsXBLProtoImplField::nsXBLProtoImplField(const nsAReadableString* aName, const nsAReadableString* aReadOnly) +:nsXBLProtoImplMember(aName) +{ + MOZ_COUNT_CTOR(nsXBLProtoImplField); + mJSAttributes = JSPROP_ENUMERATE; + if (aReadOnly) { + nsAutoString readOnly; readOnly.Assign(*aReadOnly); + if (readOnly.EqualsIgnoreCase("true")) + mJSAttributes |= JSPROP_READONLY; + } +} + +nsXBLProtoImplField::~nsXBLProtoImplField() +{ + MOZ_COUNT_DTOR(nsXBLProtoImplField); +} + +void +nsXBLProtoImplField::Destroy(PRBool aIsCompiled) +{ +} + +void +nsXBLProtoImplField::AppendFieldText(const nsAReadableString& aText) +{ + mFieldText += aText; +} + +nsresult +nsXBLProtoImplField::InstallMember(nsIScriptContext* aContext, nsIContent* aBoundElement, + void* aScriptObject, void* aTargetClassObject) +{ + if (mFieldText.IsEmpty()) + return NS_OK; // nothing to do. + + JSContext* cx = (JSContext*) aContext->GetNativeContext(); + JSObject * scriptObject = (JSObject *) aScriptObject; + NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen"); + if (!scriptObject) + return NS_ERROR_FAILURE; + + // compile the literal string + jsval result = nsnull; + PRBool undefined; + aContext->EvaluateStringWithValue(mFieldText, + scriptObject, + nsnull, nsnull, 0, nsnull, + (void*) &result, &undefined); + + if (!undefined) { + // Define the evaluated result as a JS property + nsDependentString name(mName); + ::JS_DefineUCProperty(cx, scriptObject, NS_REINTERPRET_CAST(const jschar*, mName), + name.Length(), result,nsnull, nsnull, mJSAttributes); + } + + return NS_OK; +} + +nsresult +nsXBLProtoImplField::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, + void* aClassObject) +{ + return NS_OK; +} diff --git a/content/xbl/src/nsXBLProtoImplField.h b/content/xbl/src/nsXBLProtoImplField.h new file mode 100644 index 00000000000..690f2c453d7 --- /dev/null +++ b/content/xbl/src/nsXBLProtoImplField.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXBLProtoImplField_h__ +#define nsXBLProtoImplField_h__ + +#include "nsIAtom.h" +#include "nsString.h" +#include "jsapi.h" +#include "nsIContent.h" +#include "nsString.h" +#include "nsXBLProtoImplMember.h" + +class nsXBLProtoImplField: public nsXBLProtoImplMember +{ +public: + nsXBLProtoImplField(const nsAReadableString* aName, const nsAReadableString* aReadOnly); + virtual ~nsXBLProtoImplField(); + virtual void Destroy(PRBool aIsCompiled); + + void AppendFieldText(const nsAReadableString& aText); + + virtual nsresult InstallMember(nsIScriptContext* aContext, nsIContent* aBoundElement, + void* aScriptObject, void* aTargetClassObject); + virtual nsresult CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, void* aClassObject); + +protected: + nsString mFieldText; + uintN mJSAttributes; +}; + +#endif // nsXBLProtoImplField_h__ diff --git a/content/xbl/src/nsXBLProtoImplMember.h b/content/xbl/src/nsXBLProtoImplMember.h new file mode 100644 index 00000000000..2333a824761 --- /dev/null +++ b/content/xbl/src/nsXBLProtoImplMember.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXBLProtoImplMember_h__ +#define nsXBLProtoImplMember_h__ + +#include "nsIAtom.h" +#include "nsString.h" +#include "jsapi.h" +#include "nsIContent.h" +#include "nsString.h" +#include "nsIJSRuntimeService.h" +#include "nsIServiceManager.h" +#include "nsReadableUtils.h" + +class nsIScriptContext; + +class nsXBLProtoImplMember +{ +public: + nsXBLProtoImplMember(const nsAReadableString* aName) :mNext(nsnull) { mName = ToNewUnicode(*aName); }; + virtual ~nsXBLProtoImplMember() { nsMemory::Free(mName); delete mNext; }; + virtual void Destroy(PRBool aIsCompiled)=0; + + nsXBLProtoImplMember* GetNext() { return mNext; }; + void SetNext(nsXBLProtoImplMember* aNext) { mNext = aNext; }; + + virtual nsresult InstallMember(nsIScriptContext* aContext, nsIContent* aBoundElement, + void* aScriptObject, void* aTargetClassObject)=0; + virtual nsresult CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, void* aClassObject)=0; + +protected: + nsXBLProtoImplMember* mNext; // The members of an implementation are chained. + PRUnichar* mName; // The name of the field, method, or property. +}; + +#endif // nsXBLProtoImplMember_h__ diff --git a/content/xbl/src/nsXBLProtoImplMethod.cpp b/content/xbl/src/nsXBLProtoImplMethod.cpp new file mode 100644 index 00000000000..464847e3cce --- /dev/null +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIAtom.h" +#include "nsString.h" +#include "jsapi.h" +#include "nsIContent.h" +#include "nsString.h" +#include "nsUnicharUtils.h" +#include "nsReadableUtils.h" +#include "nsXBLProtoImplMethod.h" +#include "nsIScriptContext.h" + +static nsIJSRuntimeService* gJSRuntimeService = nsnull; +static JSRuntime* gScriptRuntime = nsnull; +static PRInt32 gScriptRuntimeRefcnt = 0; + +static nsresult +AddJSGCRoot(void* aScriptObjectRef, const char* aName) +{ + if (++gScriptRuntimeRefcnt == 1 || !gScriptRuntime) { + CallGetService("@mozilla.org/js/xpc/RuntimeService;1", + &gJSRuntimeService); + if (! gJSRuntimeService) { + NS_NOTREACHED("couldn't add GC root"); + return NS_ERROR_FAILURE; + } + + gJSRuntimeService->GetRuntime(&gScriptRuntime); + if (! gScriptRuntime) { + NS_NOTREACHED("couldn't add GC root"); + return NS_ERROR_FAILURE; + } + } + + PRBool ok; + ok = ::JS_AddNamedRootRT(gScriptRuntime, aScriptObjectRef, aName); + if (! ok) { + NS_NOTREACHED("couldn't add GC root"); + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +static nsresult +RemoveJSGCRoot(void* aScriptObjectRef) +{ + if (!gScriptRuntime) { + NS_NOTREACHED("couldn't remove GC root"); + return NS_ERROR_FAILURE; + } + + ::JS_RemoveRootRT(gScriptRuntime, aScriptObjectRef); + + if (--gScriptRuntimeRefcnt == 0) { + NS_RELEASE(gJSRuntimeService); + gScriptRuntime = nsnull; + } + + return NS_OK; +} + +MOZ_DECL_CTOR_COUNTER(nsXBLProtoImplMethod); + +nsXBLProtoImplMethod::nsXBLProtoImplMethod(const nsAReadableString& aName) +:nsXBLProtoImplMember(&aName), + mUncompiledMethod(nsnull) +{ + MOZ_COUNT_CTOR(nsXBLProtoImplMethod); +} + +nsXBLProtoImplMethod::~nsXBLProtoImplMethod() +{ + MOZ_COUNT_DTOR(nsXBLProtoImplMethod); +} + +void +nsXBLProtoImplMethod::Destroy(PRBool aIsCompiled) +{ + if (aIsCompiled) { + if (mJSMethodObject) + RemoveJSGCRoot(&mJSMethodObject); + mJSMethodObject = nsnull; + } + else { + delete mUncompiledMethod; + mUncompiledMethod = nsnull; + } +} + +void +nsXBLProtoImplMethod::AppendBodyText(const nsAReadableString& aText) +{ + if (!mUncompiledMethod) { + mUncompiledMethod = new nsXBLUncompiledMethod(); + if (!mUncompiledMethod) + return; + } + + mUncompiledMethod->AppendBodyText(aText); +} + +void +nsXBLProtoImplMethod::AddParameter(const nsAReadableString& aText) +{ + if (!mUncompiledMethod) { + mUncompiledMethod = new nsXBLUncompiledMethod(); + if (!mUncompiledMethod) + return; + } + + mUncompiledMethod->AddParameter(aText); +} + +nsresult +nsXBLProtoImplMethod::InstallMember(nsIScriptContext* aContext, nsIContent* aBoundElement, + void* aScriptObject, void* aTargetClassObject) +{ + JSContext* cx = (JSContext*) aContext->GetNativeContext(); + JSObject * scriptObject = (JSObject *) aScriptObject; + NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen"); + if (!scriptObject) + return NS_ERROR_FAILURE; + + JSObject * targetClassObject = (JSObject *) aTargetClassObject; + JSObject * globalObject = ::JS_GetGlobalObject(cx); + + // now we want to reevaluate our property using aContext and the script object for this window... + if (mJSMethodObject && targetClassObject) { + nsDependentString name(mName); + JSObject * method = ::JS_CloneFunctionObject(cx, mJSMethodObject, globalObject); + ::JS_DefineUCProperty(cx, targetClassObject, NS_REINTERPRET_CAST(const jschar*, mName), + name.Length(), OBJECT_TO_JSVAL(method), + NULL, NULL, JSPROP_ENUMERATE); + } + return NS_OK; +} + +nsresult +nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, + void* aClassObject) +{ + if (!aClassObject) + return NS_OK; // Nothing to do. + + if (!mName) + return NS_ERROR_FAILURE; // Without a valid name, we can't install the member. + + // We have a method. + nsresult rv = NS_OK; + + // Allocate an array for our arguments. + PRInt32 paramCount = mUncompiledMethod->GetParameterCount(); + char** args = nsnull; + if (paramCount > 0) { + args = new char*[paramCount]; + if (!args) + return NS_ERROR_OUT_OF_MEMORY; + } + + // Add our parameters to our args array. + PRInt32 argPos = 0; + for (nsXBLParameter* curr = mUncompiledMethod->mParameters; + curr; + curr = curr->mNext) { + args[argPos] = curr->mName; + argPos++; + } + + // Now that we have a body and args, compile the function + // and then define it. + nsDependentString body(mUncompiledMethod->mBodyText); + if (!body.IsEmpty()) { + nsCAutoString cname; cname.AssignWithConversion(mName); + nsCAutoString functionUri(aClassStr); + functionUri += "."; + functionUri += cname; + functionUri += "()"; + + JSObject* methodObject = nsnull; + aContext->CompileFunction(aClassObject, + cname, + paramCount, + (const char**)args, + body, + functionUri.get(), + 0, + PR_FALSE, + (void **) &methodObject); + + // Destroy our uncompiled method and delete our arg list. + delete mUncompiledMethod; + delete [] args; + mJSMethodObject = methodObject; + + if (methodObject) { + // Root the compiled prototype script object. + JSContext* cx = NS_REINTERPRET_CAST(JSContext*, + aContext->GetNativeContext()); + if (!cx) return NS_ERROR_UNEXPECTED; + AddJSGCRoot(&mJSMethodObject, "nsXBLProtoImplMethod::mJSMethodObject"); + } + } + + return NS_OK; +} diff --git a/content/xbl/src/nsXBLProtoImplMethod.h b/content/xbl/src/nsXBLProtoImplMethod.h new file mode 100644 index 00000000000..11d3b0dadbc --- /dev/null +++ b/content/xbl/src/nsXBLProtoImplMethod.h @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXBLProtoImplMethod_h__ +#define nsXBLProtoImplMethod_h__ + +#include "nsIAtom.h" +#include "nsString.h" +#include "jsapi.h" +#include "nsIContent.h" +#include "nsString.h" +#include "nsXBLProtoImplMember.h" + +MOZ_DECL_CTOR_COUNTER(nsXBLParameter); + +struct nsXBLParameter { + nsXBLParameter* mNext; + char* mName; + + nsXBLParameter(const nsAReadableString& aName) { + MOZ_COUNT_CTOR(nsXBLParameter); + mName = ToNewCString(aName); + mNext = nsnull; + } + + ~nsXBLParameter() { + MOZ_COUNT_DTOR(nsXBLParameter); + nsMemory::Free(mName); + delete mNext; + } +}; + +MOZ_DECL_CTOR_COUNTER(nsXBLUncompiledMethod); + +struct nsXBLUncompiledMethod { + nsXBLParameter* mParameters; + nsXBLParameter* mLastParameter; + PRUnichar* mBodyText; + + nsXBLUncompiledMethod() { + MOZ_COUNT_CTOR(nsXBLUncompiledMethod); + mBodyText = nsnull; + mParameters = nsnull; + mLastParameter = nsnull; + } + + ~nsXBLUncompiledMethod() { + MOZ_COUNT_DTOR(nsXBLUncompiledMethod); + nsMemory::Free(mBodyText); + delete mParameters; + } + + PRInt32 GetParameterCount() { + PRInt32 result = 0; + for (nsXBLParameter* curr = mParameters; curr; curr=curr->mNext) + result++; + return result; + } + + void AppendBodyText(const nsAReadableString& aText) { + if (mBodyText) { + nsAutoString currText(mBodyText); + currText += aText; + nsMemory::Free(mBodyText); + mBodyText = ToNewUnicode(currText); + } + else + mBodyText = ToNewUnicode(aText); + } + + void AddParameter(const nsAReadableString& aText) { + nsXBLParameter* param = new nsXBLParameter(aText); + if (!param) + return; + if (!mParameters) + mParameters = param; + else + mLastParameter->mNext = param; + mLastParameter = param; + } +}; + +class nsXBLProtoImplMethod: public nsXBLProtoImplMember +{ +public: + nsXBLProtoImplMethod(const nsAReadableString& aName); + virtual ~nsXBLProtoImplMethod(); + virtual void Destroy(PRBool aIsCompiled); + + void AppendBodyText(const nsAReadableString& aBody); + void AddParameter(const nsAReadableString& aName); + + virtual nsresult InstallMember(nsIScriptContext* aContext, nsIContent* aBoundElement, + void* aScriptObject, void* aTargetClassObject); + virtual nsresult CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, void* aClassObject); + +protected: + union { + nsXBLUncompiledMethod* mUncompiledMethod; // An object that represents the method before being compiled. + JSObject * mJSMethodObject; // The JS object for the method (after compilation) + }; +}; + +#endif // nsXBLProtoImplMethod_h__ diff --git a/content/xbl/src/nsXBLProtoImplProperty.cpp b/content/xbl/src/nsXBLProtoImplProperty.cpp new file mode 100644 index 00000000000..9e03dae3a08 --- /dev/null +++ b/content/xbl/src/nsXBLProtoImplProperty.cpp @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIAtom.h" +#include "nsString.h" +#include "jsapi.h" +#include "nsIContent.h" +#include "nsString.h" +#include "nsXBLProtoImplProperty.h" +#include "nsUnicharUtils.h" +#include "nsReadableUtils.h" +#include "nsIScriptContext.h" + +static nsIJSRuntimeService* gJSRuntimeService = nsnull; +static JSRuntime* gScriptRuntime = nsnull; +static PRInt32 gScriptRuntimeRefcnt = 0; + +static nsresult +AddJSGCRoot(void* aScriptObjectRef, const char* aName) +{ + if (++gScriptRuntimeRefcnt == 1 || !gScriptRuntime) { + CallGetService("@mozilla.org/js/xpc/RuntimeService;1", + &gJSRuntimeService); + if (! gJSRuntimeService) { + NS_NOTREACHED("couldn't add GC root"); + return NS_ERROR_FAILURE; + } + + gJSRuntimeService->GetRuntime(&gScriptRuntime); + if (! gScriptRuntime) { + NS_NOTREACHED("couldn't add GC root"); + return NS_ERROR_FAILURE; + } + } + + PRBool ok; + ok = ::JS_AddNamedRootRT(gScriptRuntime, aScriptObjectRef, aName); + if (! ok) { + NS_NOTREACHED("couldn't add GC root"); + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +static nsresult +RemoveJSGCRoot(void* aScriptObjectRef) +{ + if (!gScriptRuntime) { + NS_NOTREACHED("couldn't remove GC root"); + return NS_ERROR_FAILURE; + } + + ::JS_RemoveRootRT(gScriptRuntime, aScriptObjectRef); + + if (--gScriptRuntimeRefcnt == 0) { + NS_RELEASE(gJSRuntimeService); + gScriptRuntime = nsnull; + } + + return NS_OK; +} + +MOZ_DECL_CTOR_COUNTER(nsXBLProtoImplProperty); + +nsXBLProtoImplProperty::nsXBLProtoImplProperty(const nsAReadableString* aName, + const nsAReadableString* aGetter, + const nsAReadableString* aSetter, + const nsAReadableString* aReadOnly) +:nsXBLProtoImplMember(aName), + mGetterText(nsnull), + mSetterText(nsnull) +{ + MOZ_COUNT_CTOR(nsXBLProtoImplProperty); + + mJSAttributes = JSPROP_ENUMERATE; + if (aReadOnly) { + nsAutoString readOnly; readOnly.Assign(*aReadOnly); + if (readOnly.EqualsIgnoreCase("true")) + mJSAttributes |= JSPROP_READONLY; + } + + if (aGetter) + AppendGetterText(*aGetter); + if (aSetter) + AppendSetterText(*aSetter); +} + +nsXBLProtoImplProperty::~nsXBLProtoImplProperty() +{ + MOZ_COUNT_DTOR(nsXBLProtoImplProperty); +} + +void +nsXBLProtoImplProperty::Destroy(PRBool aIsCompiled) +{ + if (aIsCompiled) { + if (mJSGetterObject) + RemoveJSGCRoot(&mJSGetterObject); + if (mJSSetterObject) + RemoveJSGCRoot(&mJSSetterObject); + mJSGetterObject = mJSSetterObject = nsnull; + } + else { + nsMemory::Free(mGetterText); + nsMemory::Free(mSetterText); + mGetterText = mSetterText = nsnull; + } +} + +void +nsXBLProtoImplProperty::AppendGetterText(const nsAReadableString& aText) +{ + if (mGetterText) { + nsAutoString currText(mGetterText); + currText += aText; + nsMemory::Free(mGetterText); + mGetterText = ToNewUnicode(currText); + } + else + mGetterText = ToNewUnicode(aText); +} + +void +nsXBLProtoImplProperty::AppendSetterText(const nsAReadableString& aText) +{ + if (mSetterText) { + nsAutoString currText(mSetterText); + currText += aText; + nsMemory::Free(mSetterText); + mSetterText = ToNewUnicode(currText); + } + else + mSetterText = ToNewUnicode(aText); +} + +const char* gPropertyArgs[] = { "val" }; + +nsresult +nsXBLProtoImplProperty::InstallMember(nsIScriptContext* aContext, nsIContent* aBoundElement, + void* aScriptObject, void* aTargetClassObject) +{ + JSContext* cx = (JSContext*) aContext->GetNativeContext(); + JSObject * scriptObject = (JSObject *) aScriptObject; + NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen"); + if (!scriptObject) + return NS_ERROR_FAILURE; + + JSObject * targetClassObject = (JSObject *) aTargetClassObject; + JSObject * globalObject = ::JS_GetGlobalObject(cx); + + // now we want to reevaluate our property using aContext and the script object for this window... + if ((mJSGetterObject || mJSSetterObject) && targetClassObject) { + JSObject * getter = nsnull; + if (mJSGetterObject) + getter = ::JS_CloneFunctionObject(cx, mJSGetterObject, globalObject); + + JSObject * setter = nsnull; + if (mJSSetterObject) + setter = ::JS_CloneFunctionObject(cx, mJSSetterObject, globalObject); + + nsDependentString name(mName); + ::JS_DefineUCProperty(cx, targetClassObject, NS_REINTERPRET_CAST(const jschar*, mName), + name.Length(), JSVAL_VOID, (JSPropertyOp) getter, + (JSPropertyOp) setter, mJSAttributes); + } + return NS_OK; +} + +nsresult +nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, + void* aClassObject) +{ + if (!aClassObject) + return NS_OK; // Nothing to do. + + if (!mName) + return NS_ERROR_FAILURE; // Without a valid name, we can't install the member. + + // We have a property. + nsresult rv = NS_OK; + + // Do we have a getter? + nsAutoString getter(mGetterText); + nsMemory::Free(mGetterText); + mGetterText = nsnull; + if (!getter.IsEmpty() && aClassObject) { + nsCAutoString functionUri; + functionUri.Assign(aClassStr); + functionUri += "."; + functionUri.AppendWithConversion(mName); + functionUri += " (getter)"; + rv = aContext->CompileFunction(aClassObject, + nsCAutoString("onget"), + 0, + nsnull, + getter, + functionUri.get(), + 0, + PR_FALSE, + (void **) &mJSGetterObject); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED; + if (mJSGetterObject) { + // Root the compiled prototype script object. + JSContext* cx = NS_REINTERPRET_CAST(JSContext*, + aContext->GetNativeContext()); + if (!cx) return NS_ERROR_UNEXPECTED; + rv = AddJSGCRoot(&mJSGetterObject, "nsXBLProtoImplProperty::mJSGetterObject"); + if (NS_FAILED(rv)) return rv; + } + } // if getter is not empty + + // Do we have a setter? + nsAutoString setter(mSetterText); + nsMemory::Free(mSetterText); + mSetterText = nsnull; + if (!setter.IsEmpty() && aClassObject) { + nsCAutoString functionUri (aClassStr); + functionUri += "."; + functionUri.AppendWithConversion(mName); + functionUri += " (setter)"; + rv = aContext->CompileFunction(aClassObject, + nsCAutoString("onset"), + 1, + gPropertyArgs, + setter, + functionUri.get(), + 0, + PR_FALSE, + (void **) &mJSSetterObject); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED; + if (mJSSetterObject) { + // Root the compiled prototype script object. + JSContext* cx = NS_REINTERPRET_CAST(JSContext*, + aContext->GetNativeContext()); + if (!cx) return NS_ERROR_UNEXPECTED; + rv = AddJSGCRoot(&mJSSetterObject, "nsXBLProtoImplProperty::mJSSetterObject"); + if (NS_FAILED(rv)) return rv; + } + } // if setter wasn't empty.... + + return rv; +} diff --git a/content/xbl/src/nsXBLProtoImplProperty.h b/content/xbl/src/nsXBLProtoImplProperty.h new file mode 100644 index 00000000000..8f57d250ee9 --- /dev/null +++ b/content/xbl/src/nsXBLProtoImplProperty.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David Hyatt (Original Author) + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXBLProtoImplProperty_h__ +#define nsXBLProtoImplProperty_h__ + +#include "nsIAtom.h" +#include "nsString.h" +#include "jsapi.h" +#include "nsIContent.h" +#include "nsString.h" +#include "nsXBLProtoImplMember.h" + +class nsXBLProtoImplProperty: public nsXBLProtoImplMember +{ +public: + nsXBLProtoImplProperty(const nsAReadableString* aName, + const nsAReadableString* aGetter, + const nsAReadableString* aSetter, + const nsAReadableString* aReadOnly); + + virtual ~nsXBLProtoImplProperty(); + virtual void Destroy(PRBool aIsCompiled); + + void AppendGetterText(const nsAReadableString& aGetter); + void AppendSetterText(const nsAReadableString& aSetter); + + virtual nsresult InstallMember(nsIScriptContext* aContext, nsIContent* aBoundElement, + void* aScriptObject, void* aTargetClassObject); + virtual nsresult CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr, void* aClassObject); + +protected: + union { + PRUnichar* mGetterText; // The raw text for the getter (prior to compilation). + JSObject * mJSGetterObject; // The JS object for the getter (after compilation) + }; + + union { + PRUnichar* mSetterText; // The raw text for the setter (prior to compilation). + JSObject * mJSSetterObject; // The JS object for the setter (after compilation) + }; + + uintN mJSAttributes; // A flag for all our JS properties (getter/setter/readonly/shared/enum) + PRBool mCompiled; // Whether or not we are compiled. +}; + +#endif // nsXBLProtoImplProperty_h__ diff --git a/content/xbl/src/nsXBLPrototypeBinding.cpp b/content/xbl/src/nsXBLPrototypeBinding.cpp index 3545c1e6482..cb83a024d01 100644 --- a/content/xbl/src/nsXBLPrototypeBinding.cpp +++ b/content/xbl/src/nsXBLPrototypeBinding.cpp @@ -76,6 +76,7 @@ #include "nsHTMLAtoms.h" #include "nsXULAtoms.h" #include "nsXBLAtoms.h" +#include "nsXBLProtoImpl.h" #include "nsIScriptContext.h" @@ -255,17 +256,18 @@ NS_IMPL_ISUPPORTS2(nsXBLPrototypeBinding, nsIXBLPrototypeBinding, nsISupportsWea // Constructors/Destructors nsXBLPrototypeBinding::nsXBLPrototypeBinding(const nsAReadableCString& aID, nsIXBLDocumentInfo* aInfo, nsIContent* aElement) -: mID(aID), - mInheritStyle(PR_TRUE), +: mInheritStyle(PR_TRUE), mHasBaseProto(PR_TRUE), mResources(nsnull), + mImplementation(nsnull), mAttributeTable(nsnull), mInsertionPointTable(nsnull), - mInterfaceTable(nsnull), - mClassObject(nsnull) + mInterfaceTable(nsnull) { NS_INIT_REFCNT(); + mID = ToNewCString(aID); + mXBLDocInfoWeak = getter_AddRefs(NS_GetWeakReference(aInfo)); gRefCnt++; @@ -284,15 +286,6 @@ nsXBLPrototypeBinding::nsXBLPrototypeBinding(const nsAReadableCString& aID, nsIX NS_IMETHODIMP nsXBLPrototypeBinding::Initialize() { - nsCOMPtr info(do_QueryReferent(mXBLDocInfoWeak)); - - PRBool allowScripts; - info->GetScriptAccess(&allowScripts); - if (allowScripts) { - BuildConstructorAndDestructor(); - ConstructProperties(); - } - nsCOMPtr content; GetImmediateChild(nsXBLAtoms::content, getter_AddRefs(content)); if (content) { @@ -300,20 +293,17 @@ nsXBLPrototypeBinding::Initialize() ConstructInsertionTable(content); } - nsCOMPtr impl; - GetImmediateChild(nsXBLAtoms::implementation, getter_AddRefs(impl)); - if (impl) - ConstructInterfaceTable(impl); - return NS_OK; } nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void) { + nsMemory::Free(mID); delete mResources; delete mAttributeTable; delete mInsertionPointTable; delete mInterfaceTable; + delete mImplementation; gRefCnt--; if (gRefCnt == 0) { delete kAttrPool; @@ -437,16 +427,16 @@ nsXBLPrototypeBinding::AddResource(nsIAtom* aResourceType, const nsAReadableStri NS_IMETHODIMP nsXBLPrototypeBinding::BindingAttached(nsIDOMEventReceiver* aReceiver) { - if (mConstructor) - return mConstructor->BindingAttached(aReceiver); + if (mImplementation && mImplementation->mConstructor) + return mImplementation->mConstructor->BindingAttached(aReceiver); return NS_OK; } NS_IMETHODIMP nsXBLPrototypeBinding::BindingDetached(nsIDOMEventReceiver* aReceiver) { - if (mDestructor) - return mDestructor->BindingDetached(aReceiver); + if (mImplementation && mImplementation->mDestructor) + return mImplementation->mDestructor->BindingDetached(aReceiver); return NS_OK; } @@ -500,17 +490,52 @@ nsXBLPrototypeBinding::SetPrototypeHandlers(nsIXBLPrototypeHandler* aHandler) } NS_IMETHODIMP -nsXBLPrototypeBinding::GetPrototypeProperties(nsIXBLPrototypeProperty ** aResult) +nsXBLPrototypeBinding::GetConstructor(nsIXBLPrototypeHandler** aResult) { - *aResult = mPrototypeProperty; - NS_IF_ADDREF(*aResult); + if (mImplementation && mImplementation->mConstructor) { + *aResult = mImplementation->mConstructor; + NS_ADDREF(*aResult); + } + else + *aResult = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsXBLPrototypeBinding::GetDestructor(nsIXBLPrototypeHandler** aResult) +{ + if (mImplementation && mImplementation->mDestructor) { + *aResult = mImplementation->mDestructor; + NS_ADDREF(*aResult); + } + else + *aResult = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsXBLPrototypeBinding::SetConstructor(nsIXBLPrototypeHandler* aHandler) +{ + if (!mImplementation) + return NS_ERROR_FAILURE; + mImplementation->mConstructor = aHandler; return NS_OK; } NS_IMETHODIMP -nsXBLPrototypeBinding::SetProtoTypeProperties(nsIXBLPrototypeProperty* aResult) +nsXBLPrototypeBinding::SetDestructor(nsIXBLPrototypeHandler* aHandler) { - mPrototypeProperty = aResult; + if (!mImplementation) + return NS_ERROR_FAILURE; + mImplementation->mDestructor = aHandler; + return NS_OK; +} + +NS_IMETHODIMP +nsXBLPrototypeBinding::InstallImplementation(nsIContent* aBoundElement) +{ + if (mImplementation) + return mImplementation->InstallImplementation(this, aBoundElement); return NS_OK; } @@ -829,30 +854,6 @@ nsXBLPrototypeBinding::GetImmediateChild(nsIAtom* aTag, nsIContent** aResult) return; } - -void -nsXBLPrototypeBinding::ConstructProperties() -{ - nsCOMPtr properties; - GetImmediateChild(nsXBLAtoms::implementation, getter_AddRefs(properties)); - if (properties && mBinding) { - nsXBLService::BuildPropertyChain(this, properties, getter_AddRefs(mPrototypeProperty)); - } -} - -NS_IMETHODIMP -nsXBLPrototypeBinding::GetCompiledClassObject(const nsCString& aClassName, nsIScriptContext * aContext, void * aScriptObject, void ** aClassObject) -{ - if (mClassObject) { - *aClassObject = mClassObject; - return NS_OK; - } - - InitClass(aClassName, aContext, aScriptObject, &mClassObject); - *aClassObject = mClassObject; - - return NS_OK; -} NS_IMETHODIMP nsXBLPrototypeBinding::InitClass(const nsCString& aClassName, nsIScriptContext * aContext, void * aScriptObject, void ** aClassObject) @@ -944,38 +945,6 @@ nsXBLPrototypeBinding::InitClass(const nsCString& aClassName, nsIScriptContext * return NS_OK; } -void -nsXBLPrototypeBinding::BuildConstructorAndDestructor() -{ - nsCOMPtr impl; - GetImmediateChild(nsXBLAtoms::implementation, getter_AddRefs(impl)); - if (impl) { - // Look for and . - PRInt32 count; - impl->ChildCount(count); - for (PRInt32 i = 0; i < count; i++) { - nsCOMPtr child; - impl->ChildAt(i, *getter_AddRefs(child)); - nsCOMPtr tag; - child->GetTag(*getter_AddRefs(tag)); - if (tag == nsXBLAtoms::constructor) { - nsAutoString value; - nsXBLBinding::GetTextData(child, value); - NS_NewXBLPrototypeHandler(nsnull, nsnull, &value, nsnull, nsnull, nsnull, nsnull, - nsnull, nsnull, getter_AddRefs(mConstructor)); - mConstructor->SetEventName(tag); - } - else if (tag == nsXBLAtoms::destructor) { - nsAutoString value; - nsXBLBinding::GetTextData(child, value); - NS_NewXBLPrototypeHandler(nsnull, nsnull, &value, nsnull, nsnull, nsnull, nsnull, - nsnull, nsnull, getter_AddRefs(mDestructor)); - mDestructor->SetEventName(tag); - } - } - } -} - void nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement, nsIContent* aTemplRoot, nsIContent* aCopyRoot, nsIContent* aTemplChild, nsIContent** aCopyResult) @@ -1365,24 +1334,22 @@ nsXBLPrototypeBinding::ConstructInsertionTable(nsIContent* aContent) } } -void -nsXBLPrototypeBinding::ConstructInterfaceTable(nsIContent* aElement) +NS_IMETHODIMP +nsXBLPrototypeBinding::ConstructInterfaceTable(const nsAReadableString& aImpls) { - nsAutoString impls; - aElement->GetAttr(kNameSpaceID_None, nsXBLAtoms::implements, impls); - if (!impls.IsEmpty()) { + if (!aImpls.IsEmpty()) { // Obtain the interface info manager that can tell us the IID // for a given interface name. nsCOMPtr infoManager = getter_AddRefs(XPTI_GetInterfaceInfoManager()); if (!infoManager) - return; + return NS_ERROR_FAILURE; // Create the table. if (!mInterfaceTable) mInterfaceTable = new nsSupportsHashtable(4); // The user specified at least one attribute. - char* str = ToNewCString(impls); + char* str = ToNewCString(aImpls); char* newStr; // XXX We should use a strtok function that tokenizes PRUnichars // so that we don't have to convert from Unicode to ASCII and then back @@ -1404,6 +1371,8 @@ nsXBLPrototypeBinding::ConstructInterfaceTable(nsIContent* aElement) nsMemory::Free(str); } + + return NS_OK; } void diff --git a/content/xbl/src/nsXBLPrototypeBinding.h b/content/xbl/src/nsXBLPrototypeBinding.h index 99fb56398d7..42923571549 100644 --- a/content/xbl/src/nsXBLPrototypeBinding.h +++ b/content/xbl/src/nsXBLPrototypeBinding.h @@ -40,7 +40,6 @@ #include "nsCOMPtr.h" #include "nsIXBLPrototypeBinding.h" #include "nsIXBLPrototypeHandler.h" -#include "nsIXBLPrototypeProperty.h" #include "nsXBLPrototypeResources.h" #include "nsICSSStyleSheet.h" #include "nsICSSLoaderObserver.h" @@ -82,12 +81,19 @@ class nsXBLPrototypeBinding: public nsIXBLPrototypeBinding, public nsSupportsWea NS_IMETHOD GetPrototypeHandlers(nsIXBLPrototypeHandler** aHandler); NS_IMETHOD SetPrototypeHandlers(nsIXBLPrototypeHandler* aHandler); - - NS_IMETHOD GetPrototypeProperties(nsIXBLPrototypeProperty** aResult); - NS_IMETHOD SetProtoTypeProperties(nsIXBLPrototypeProperty* aResult); - NS_IMETHOD GetCompiledClassObject(const nsCString& aClassName, nsIScriptContext * aContext, void * aScriptObject, void ** aClassObject); + + NS_IMETHOD GetConstructor(nsIXBLPrototypeHandler** aResult); + NS_IMETHOD SetConstructor(nsIXBLPrototypeHandler* aConstructor); + NS_IMETHOD GetDestructor(nsIXBLPrototypeHandler** aResult); + NS_IMETHOD SetDestructor(nsIXBLPrototypeHandler* aDestructor); + NS_IMETHOD InitClass(const nsCString& aClassName, nsIScriptContext * aContext, void * aScriptObject, void ** aClassObject); + NS_IMETHOD ConstructInterfaceTable(const nsAReadableString& aImpls); + + NS_IMETHOD SetImplementation(nsXBLProtoImpl* aImpl) { mImplementation = aImpl; return NS_OK; }; + NS_IMETHOD InstallImplementation(nsIContent* aBoundElement); + NS_IMETHOD AttributeChanged(nsIAtom* aAttribute, PRInt32 aNameSpaceID, PRBool aRemoveFlag, nsIContent* aChangedElement, nsIContent* aAnonymousContent); @@ -127,8 +133,6 @@ class nsXBLPrototypeBinding: public nsIXBLPrototypeBinding, public nsSupportsWea NS_IMETHOD AddResourceListener(nsIContent* aBoundElement); - NS_IMETHOD GetConstructor(nsIXBLPrototypeHandler** aResult) { *aResult = mConstructor; NS_IF_ADDREF(*aResult); return NS_OK; }; - NS_IMETHOD Initialize(); public: @@ -149,11 +153,8 @@ public: nsIContent* aTemplChild, nsIContent** aCopyResult); protected: - void BuildConstructorAndDestructor(); - void ConstructProperties(); void ConstructAttributeTable(nsIContent* aElement); void ConstructInsertionTable(nsIContent* aElement); - void ConstructInterfaceTable(nsIContent* aElement); void GetNestedChildren(nsIAtom* aTag, nsIContent* aContent, nsISupportsArray** aList); protected: @@ -182,14 +183,13 @@ protected: // MEMBER VARIABLES protected: - nsCString mID; + char* mID; nsCOMPtr mBinding; // Strong. We own a ref to our content element in the binding doc. nsCOMPtr mPrototypeHandler; // Strong. DocInfo owns us, and we own the handlers. - nsCOMPtr mConstructor; // Strong. Our constructor. - nsCOMPtr mDestructor; // Strong. Our destructor. - - nsCOMPtr mPrototypeProperty; + + nsXBLProtoImpl* mImplementation; // Our prototype implementation (includes methods, properties, fields, + // the constructor, and the destructor). nsCOMPtr mBaseBinding; // Strong. We own the base binding in our explicit inheritance chain. PRPackedBool mInheritStyle; @@ -209,6 +209,4 @@ protected: PRInt32 mBaseNameSpaceID; // If we extend a tagname/namespace, then that information will nsCOMPtr mBaseTag; // be stored in here. - - void * mClassObject; // the class object for the binding. We'll use this to pre-compile properties and methods for the binding. }; diff --git a/content/xbl/src/nsXBLPrototypeHandler.cpp b/content/xbl/src/nsXBLPrototypeHandler.cpp index fdc94fdc488..58f1febf9e8 100644 --- a/content/xbl/src/nsXBLPrototypeHandler.cpp +++ b/content/xbl/src/nsXBLPrototypeHandler.cpp @@ -146,11 +146,17 @@ nsXBLPrototypeHandler::GetHandlerElement(nsIContent** aResult) } NS_IMETHODIMP -nsXBLPrototypeHandler::SetHandlerText(const nsAReadableString& aText) +nsXBLPrototypeHandler::AppendHandlerText(const nsAReadableString& aText) { - if (mHandlerText) + if (mHandlerText) { + // Append our text to the existing text. + nsAutoString currText(mHandlerText); + currText += aText; nsMemory::Free(mHandlerText); - mHandlerText = ToNewUnicode(aText); + mHandlerText = ToNewUnicode(currText); + } + else + mHandlerText = ToNewUnicode(aText); return NS_OK; } diff --git a/content/xbl/src/nsXBLPrototypeHandler.h b/content/xbl/src/nsXBLPrototypeHandler.h index af5ea23e94d..288faa0cc79 100644 --- a/content/xbl/src/nsXBLPrototypeHandler.h +++ b/content/xbl/src/nsXBLPrototypeHandler.h @@ -81,7 +81,7 @@ public: NS_IMETHOD GetHandlerElement(nsIContent** aResult); - NS_IMETHOD SetHandlerText(const nsAReadableString& aText); + NS_IMETHOD AppendHandlerText(const nsAReadableString& aText); NS_IMETHOD GetPhase(PRUint8* aResult) { *aResult = mPhase; return NS_OK; }; diff --git a/content/xbl/src/nsXBLPrototypeProperty.cpp b/content/xbl/src/nsXBLPrototypeProperty.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/content/xbl/src/nsXBLPrototypeProperty.h b/content/xbl/src/nsXBLPrototypeProperty.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/content/xbl/src/nsXBLService.cpp b/content/xbl/src/nsXBLService.cpp index 75008ef80fa..91d26997df7 100644 --- a/content/xbl/src/nsXBLService.cpp +++ b/content/xbl/src/nsXBLService.cpp @@ -77,7 +77,6 @@ #include "nsXBLAtoms.h" #include "nsIXBLPrototypeHandler.h" -#include "nsIXBLPrototypeProperty.h" #include "nsIChromeRegistry.h" #include "nsIPref.h" @@ -308,7 +307,6 @@ nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsre if (NS_FAILED(rv) || NS_FAILED(aStatus)) { - nsCOMPtr aChannel = do_QueryInterface(request); if (aChannel) { @@ -685,7 +683,7 @@ nsXBLService::LoadBindings(nsIContent* aContent, const nsAReadableString& aURL, } while (nextBinding); // XXX Handle adjusting the prototype chain! We need to somehow indicate to - // InstallProperties that the whole chain should just be whacked and rebuilt. + // InstallImplementation that the whole chain should just be whacked and rebuilt. // We are becoming the new binding. bindingManager->SetBinding(aContent, newBinding); baseBinding->SetBaseBinding(binding); @@ -714,7 +712,7 @@ nsXBLService::LoadBindings(nsIContent* aContent, const nsAReadableString& aURL, newBinding->InstallEventHandlers(); // Set up our properties - newBinding->InstallProperties(); + newBinding->InstallImplementation(); // Figure out if we need to execute a constructor. newBinding->GetFirstBindingWithConstructor(aBinding); @@ -1345,40 +1343,6 @@ static void GetImmediateChild(nsIAtom* aTag, nsIContent* aParent, nsIContent** a } } -nsresult -nsXBLService::BuildPropertyChain(nsIXBLPrototypeBinding * aPrototypeBinding, nsIContent * aContent, nsIXBLPrototypeProperty ** aResult) -{ - nsCOMPtr firstProperty; - nsCOMPtr currProperty; - PRInt32 propertyCount = 0; - aContent->ChildCount(propertyCount); - - for (PRInt32 j = 0; j < propertyCount; j++) { - nsCOMPtr property; - aContent->ChildAt(j, *getter_AddRefs(property)); - - nsCOMPtr newProperty; - NS_NewXBLPrototypeProperty(aPrototypeBinding, getter_AddRefs(newProperty)); - - if (!newProperty) return NS_ERROR_FAILURE; - - newProperty->ConstructProperty(aContent, property); - - if (newProperty) { - if (currProperty) - currProperty->SetNextProperty(newProperty); - else - firstProperty = newProperty; - currProperty = newProperty; - } - } - - *aResult = firstProperty; - NS_IF_ADDREF(*aResult); - - return NS_OK; -} - // Creation Routine /////////////////////////////////////////////////////////////////////// nsresult NS_NewXBLService(nsIXBLService** aResult); diff --git a/content/xbl/src/nsXBLService.h b/content/xbl/src/nsXBLService.h index 4de87b37d9c..bebf2e423e5 100644 --- a/content/xbl/src/nsXBLService.h +++ b/content/xbl/src/nsXBLService.h @@ -52,7 +52,6 @@ class nsIXBLBinding; class nsIXBLDocumentInfo; class nsIXBLPrototypeHandler; class nsIXBLPrototypeBinding; -class nsIXBLPrototypeProperty; class nsINameSpaceManager; class nsIContent; class nsIDocument; @@ -115,10 +114,6 @@ public: NS_IMETHOD GetBindingInternal(nsIContent* aBoundElement, const nsCString& aURLStr, PRBool aPeekFlag, PRBool* aIsReady, nsIXBLBinding** aResult); - // This method walks a binding document and removes any text nodes - // that contain only whitespace. - static nsresult BuildPropertyChain(nsIXBLPrototypeBinding * aPrototypeBinding, nsIContent * aContent, nsIXBLPrototypeProperty ** aResult); - // MEMBER VARIABLES public: static nsIXULContentUtils* gXULUtils;