--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1873,16 +1873,25 @@ NS_NewDOMDocument(nsIDOMDocument** aInst
const nsAString& aQualifiedName,
nsIDOMDocumentType* aDoctype,
nsIURI* aDocumentURI,
nsIURI* aBaseURI,
nsIPrincipal* aPrincipal,
bool aLoadedAsData,
nsIScriptGlobalObject* aEventObject,
bool aSVGDocument);
+
+// This is used only for xbl documents created from the startup cache.
+// Non-cached documents are created in the same manner as xml documents.
+nsresult
+NS_NewXBLDocument(nsIDOMDocument** aInstancePtrResult,
+ nsIURI* aDocumentURI,
+ nsIURI* aBaseURI,
+ nsIPrincipal* aPrincipal);
+
nsresult
NS_NewPluginDocument(nsIDocument** aInstancePtrResult);
inline nsIDocument*
nsINode::GetOwnerDocument() const
{
nsIDocument* ownerDoc = OwnerDoc();
--- a/content/base/public/nsINameSpaceManager.h
+++ b/content/base/public/nsINameSpaceManager.h
@@ -42,30 +42,32 @@
#include "nsStringGlue.h"
class nsIAtom;
class nsString;
#define kNameSpaceID_Unknown -1
// 0 is special at C++, so use a static const PRInt32 for
// kNameSpaceID_None to keep if from being cast to pointers
+// Note that the XBL cache assumes (and asserts) that it can treat a
+// single-byte value higher than kNameSpaceID_LastBuiltin specially.
static const PRInt32 kNameSpaceID_None = 0;
#define kNameSpaceID_XMLNS 1 // not really a namespace, but it needs to play the game
#define kNameSpaceID_XML 2
#define kNameSpaceID_XHTML 3
#define kNameSpaceID_XLink 4
#define kNameSpaceID_XSLT 5
#define kNameSpaceID_XBL 6
#define kNameSpaceID_MathML 7
#define kNameSpaceID_RDF 8
#define kNameSpaceID_XUL 9
#define kNameSpaceID_SVG 10
#define kNameSpaceID_XMLEvents 11
#define kNameSpaceID_LastBuiltin 11 // last 'built-in' namespace
-
+
#define NS_NAMESPACEMANAGER_CONTRACTID "@mozilla.org/content/namespacemanager;1"
#define NS_INAMESPACEMANAGER_IID \
{ 0xd74e83e6, 0xf932, 0x4289, \
{ 0xac, 0x95, 0x9e, 0x10, 0x24, 0x30, 0x88, 0xd6 } }
/**
* The Name Space Manager tracks the association between a NameSpace
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -888,16 +888,20 @@ public:
void DoNotifyPossibleTitleChange();
nsExternalResourceMap& ExternalResourceMap()
{
return mExternalResourceMap;
}
void SetLoadedAsData(bool aLoadedAsData) { mLoadedAsData = aLoadedAsData; }
+ void SetLoadedAsInteractiveData(bool aLoadedAsInteractiveData)
+ {
+ mLoadedAsInteractiveData = aLoadedAsInteractiveData;
+ }
nsresult CloneDocHelper(nsDocument* clone) const;
void MaybeInitializeFinalizeFrameLoaders();
void MaybeEndOutermostXBLUpdate();
virtual void MaybePreLoadImage(nsIURI* uri,
--- a/content/xbl/src/nsXBLContentSink.cpp
+++ b/content/xbl/src/nsXBLContentSink.cpp
@@ -561,17 +561,19 @@ nsresult
nsXBLContentSink::ConstructBinding(PRUint32 aLineNumber)
{
nsCOMPtr<nsIContent> binding = GetCurrentContent();
nsAutoString id;
binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
NS_ConvertUTF16toUTF8 cid(id);
nsresult rv = NS_OK;
-
+
+ // Don't create a binding with no id. nsXBLPrototypeBinding::Read also
+ // performs this check.
if (!cid.IsEmpty()) {
mBinding = new nsXBLPrototypeBinding();
if (!mBinding)
return NS_ERROR_OUT_OF_MEMORY;
rv = mBinding->Init(cid, mDocInfo, binding, !mFoundFirstBinding);
if (NS_SUCCEEDED(rv) &&
NS_SUCCEEDED(mDocInfo->SetPrototypeBinding(cid, mBinding))) {
@@ -889,16 +891,18 @@ nsXBLContentSink::CreateElement(const PR
if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
#endif
return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
aLineNumber, aResult,
aAppendContent, aFromParser);
#ifdef MOZ_XUL
}
+ // Note that this needs to match the code in nsXBLPrototypeBinding::ReadContentNode.
+
*aAppendContent = true;
nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
if (!prototype)
return NS_ERROR_OUT_OF_MEMORY;
prototype->mNodeInfo = aNodeInfo;
// XXX - we need to do exactly what the XUL content-sink does (eg,
// look for 'type', 'version' etc attributes)
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -51,17 +51,23 @@
#include "nsIScriptError.h"
#include "nsIChromeRegistry.h"
#include "nsIPrincipal.h"
#include "nsIScriptSecurityManager.h"
#include "nsContentUtils.h"
#include "nsDOMJSUtils.h"
#include "mozilla/Services.h"
#include "xpcpublic.h"
-
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/scache/StartupCacheUtils.h"
+
+using namespace mozilla::scache;
+
+static const char kXBLCachePrefix[] = "xblcache";
+
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
// An XBLDocumentInfo object has a special context associated with it which we can use to pre-compile
// properties and methods of XBL bindings against.
class nsXBLDocGlobalObject : public nsIScriptGlobalObject,
public nsIScriptObjectPrincipal
{
public:
@@ -557,16 +563,148 @@ nsXBLDocumentInfo::SetPrototypeBinding(c
nsCStringKey key(flat.get());
NS_ENSURE_STATE(!mBindingTable->Get(&key));
mBindingTable->Put(&key, aBinding);
return NS_OK;
}
void
+nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString& aRef)
+{
+ if (mBindingTable) {
+ // Use a flat string to avoid making a copy.
+ const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
+ nsCStringKey key(flat);
+ mBindingTable->Remove(&key);
+ }
+}
+
+// Callback to enumerate over the bindings from this document and write them
+// out to the cache.
+bool
+WriteBinding(nsHashKey *aKey, void *aData, void* aClosure)
+{
+ nsXBLPrototypeBinding* binding = static_cast<nsXBLPrototypeBinding *>(aData);
+ binding->Write((nsIObjectOutputStream*)aClosure);
+
+ return kHashEnumerateNext;
+}
+
+// static
+nsresult
+nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo)
+{
+ *aDocInfo = nsnull;
+
+ nsCAutoString spec(kXBLCachePrefix);
+ nsresult rv = PathifyURI(aURI, spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ StartupCache* startupCache = StartupCache::GetSingleton();
+ NS_ENSURE_TRUE(startupCache, NS_ERROR_FAILURE);
+
+ nsAutoArrayPtr<char> buf;
+ PRUint32 len;
+ rv = startupCache->GetBuffer(spec.get(), getter_Transfers(buf), &len);
+ // GetBuffer will fail if the binding is not in the cache.
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIObjectInputStream> stream;
+ rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(stream));
+ NS_ENSURE_SUCCESS(rv, rv);
+ buf.forget();
+
+ // The file compatibility.ini stores the build id. This is checked in
+ // nsAppRunner.cpp and will delete the cache if a different build is
+ // present. However, we check that the version matches here to be safe.
+ PRUint32 version;
+ rv = stream->Read32(&version);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (version != XBLBinding_Serialize_Version) {
+ // The version that exists is different than expected, likely created with a
+ // different build, so invalidate the cache.
+ startupCache->InvalidateCache();
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal;
+ nsContentUtils::GetSecurityManager()->
+ GetSystemPrincipal(getter_AddRefs(principal));
+
+ nsCOMPtr<nsIDOMDocument> domdoc;
+ rv = NS_NewXBLDocument(getter_AddRefs(domdoc), aURI, nsnull, principal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
+ nsRefPtr<nsXBLDocumentInfo> docInfo = NS_NewXBLDocumentInfo(doc);
+
+ while (1) {
+ PRUint8 flags;
+ nsresult rv = stream->Read8(&flags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (flags == XBLBinding_Serialize_NoMoreBindings)
+ break;
+
+ nsXBLPrototypeBinding* binding = new nsXBLPrototypeBinding();
+ rv = binding->Read(stream, docInfo, doc, flags);
+ if (NS_FAILED(rv)) {
+ delete binding;
+ return rv;
+ }
+ }
+
+ docInfo.swap(*aDocInfo);
+ return NS_OK;
+}
+
+nsresult
+nsXBLDocumentInfo::WritePrototypeBindings()
+{
+ // Only write out bindings with the system principal
+ if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal()))
+ return NS_OK;
+
+ nsCAutoString spec(kXBLCachePrefix);
+ nsresult rv = PathifyURI(DocumentURI(), spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ StartupCache* startupCache = StartupCache::GetSingleton();
+ NS_ENSURE_TRUE(startupCache, rv);
+
+ nsCOMPtr<nsIObjectOutputStream> stream;
+ nsCOMPtr<nsIStorageStream> storageStream;
+ rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(stream),
+ getter_AddRefs(storageStream),
+ true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = stream->Write32(XBLBinding_Serialize_Version);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mBindingTable)
+ mBindingTable->Enumerate(WriteBinding, stream);
+
+ // write a end marker at the end
+ rv = stream->Write8(XBLBinding_Serialize_NoMoreBindings);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ stream->Close();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRUint32 len;
+ nsAutoArrayPtr<char> buf;
+ rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return startupCache->PutBuffer(spec.get(), buf, len);
+}
+
+void
nsXBLDocumentInfo::SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding)
{
mFirstBinding = aBinding;
}
bool FlushScopedSkinSheets(nsHashKey* aKey, void* aData, void* aClosure)
{
nsXBLPrototypeBinding* proto = (nsXBLPrototypeBinding*)aData;
--- a/content/xbl/src/nsXBLDocumentInfo.h
+++ b/content/xbl/src/nsXBLDocumentInfo.h
@@ -63,25 +63,32 @@ public:
bool GetScriptAccess() { return mScriptAccess; }
nsIURI* DocumentURI() { return mDocument->GetDocumentURI(); }
nsXBLPrototypeBinding* GetPrototypeBinding(const nsACString& aRef);
nsresult SetPrototypeBinding(const nsACString& aRef,
nsXBLPrototypeBinding* aBinding);
+ // This removes the binding without deleting it
+ void RemovePrototypeBinding(const nsACString& aRef);
+
+ nsresult WritePrototypeBindings();
+
void SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding);
void FlushSkinStylesheets();
bool IsChrome() { return mIsChrome; }
// nsIScriptGlobalObjectOwner methods
virtual nsIScriptGlobalObject* GetScriptGlobalObject();
+ static nsresult ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo);
+
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsXBLDocumentInfo,
nsIScriptGlobalObjectOwner)
private:
nsCOMPtr<nsIDocument> mDocument;
bool mScriptAccess;
bool mIsChrome;
// the binding table owns each nsXBLPrototypeBinding
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -42,16 +42,17 @@
#include "nsContentUtils.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptGlobalObjectOwner.h"
#include "nsIScriptContext.h"
#include "nsIXPConnect.h"
#include "nsIServiceManager.h"
#include "nsIDOMNode.h"
#include "nsXBLPrototypeBinding.h"
+#include "nsXBLProtoImplProperty.h"
// Checks that the version is not modified in a given scope.
class AutoVersionChecker
{
JSContext * const cx;
JSVersion versionBefore;
public:
@@ -295,16 +296,169 @@ nsXBLProtoImpl::DestroyMembers()
delete mMembers;
mMembers = nsnull;
mConstructor = nsnull;
mDestructor = nsnull;
}
nsresult
+nsXBLProtoImpl::Read(nsIScriptContext* aContext,
+ nsIObjectInputStream* aStream,
+ nsXBLPrototypeBinding* aBinding,
+ nsIScriptGlobalObject* aGlobal)
+{
+ // Set up a class object first so that deserialization is possible
+ JSContext *cx = static_cast<JSContext *>(aContext->GetNativeContext());
+ JSObject *global = aGlobal->GetGlobalJSObject();
+
+ void* classObject;
+ nsresult rv = aBinding->InitClass(mClassName, cx, global, global, &classObject);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(classObject, NS_ERROR_FAILURE);
+
+ mClassObject = (JSObject*) classObject;
+
+ nsXBLProtoImplField* previousField = nsnull;
+ nsXBLProtoImplMember* previousMember = nsnull;
+
+ do {
+ XBLBindingSerializeDetails type;
+ rv = aStream->Read8(&type);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (type == XBLBinding_Serialize_NoMoreItems)
+ break;
+
+ switch (type & XBLBinding_Serialize_Mask) {
+ case XBLBinding_Serialize_Field:
+ {
+ nsXBLProtoImplField* field =
+ new nsXBLProtoImplField(type & XBLBinding_Serialize_ReadOnly);
+ rv = field->Read(aContext, aStream);
+ if (NS_FAILED(rv)) {
+ delete field;
+ return rv;
+ }
+
+ if (previousField) {
+ previousField->SetNext(field);
+ }
+ else {
+ mFields = field;
+ }
+ previousField = field;
+
+ break;
+ }
+ case XBLBinding_Serialize_GetterProperty:
+ case XBLBinding_Serialize_SetterProperty:
+ case XBLBinding_Serialize_GetterSetterProperty:
+ {
+ nsAutoString name;
+ nsresult rv = aStream->ReadString(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsXBLProtoImplProperty* prop =
+ new nsXBLProtoImplProperty(name.get(), type & XBLBinding_Serialize_ReadOnly);
+ rv = prop->Read(aContext, aStream, type & XBLBinding_Serialize_Mask);
+ if (NS_FAILED(rv)) {
+ delete prop;
+ return rv;
+ }
+
+ previousMember = AddMember(prop, previousMember);
+ break;
+ }
+ case XBLBinding_Serialize_Method:
+ {
+ nsAutoString name;
+ rv = aStream->ReadString(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsXBLProtoImplMethod* method = new nsXBLProtoImplMethod(name.get());
+ rv = method->Read(aContext, aStream);
+ if (NS_FAILED(rv)) {
+ delete method;
+ return rv;
+ }
+
+ previousMember = AddMember(method, previousMember);
+ break;
+ }
+ case XBLBinding_Serialize_Constructor:
+ {
+ mConstructor = new nsXBLProtoImplAnonymousMethod();
+ rv = mConstructor->Read(aContext, aStream);
+ if (NS_FAILED(rv)) {
+ delete mConstructor;
+ mConstructor = nsnull;
+ return rv;
+ }
+
+ previousMember = AddMember(mConstructor, previousMember);
+ break;
+ }
+ case XBLBinding_Serialize_Destructor:
+ {
+ mDestructor = new nsXBLProtoImplAnonymousMethod();
+ rv = mDestructor->Read(aContext, aStream);
+ if (NS_FAILED(rv)) {
+ delete mDestructor;
+ mDestructor = nsnull;
+ return rv;
+ }
+
+ previousMember = AddMember(mDestructor, previousMember);
+ break;
+ }
+ default:
+ NS_ERROR("Unexpected binding member type");
+ break;
+ }
+ } while (1);
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLProtoImpl::Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream,
+ nsXBLPrototypeBinding* aBinding)
+{
+ nsresult rv;
+
+ if (!mClassObject) {
+ rv = CompilePrototypeMembers(aBinding);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = aStream->WriteStringZ(mClassName.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (nsXBLProtoImplField* curr = mFields; curr; curr = curr->GetNext()) {
+ rv = curr->Write(aContext, aStream);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) {
+ if (curr == mConstructor) {
+ rv = mConstructor->Write(aContext, aStream, XBLBinding_Serialize_Constructor);
+ }
+ else if (curr == mDestructor) {
+ rv = mDestructor->Write(aContext, aStream, XBLBinding_Serialize_Destructor);
+ }
+ else {
+ rv = curr->Write(aContext, aStream);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return aStream->Write8(XBLBinding_Serialize_NoMoreItems);
+}
+
+nsresult
NS_NewXBLProtoImpl(nsXBLPrototypeBinding* aBinding,
const PRUnichar* aClassName,
nsXBLProtoImpl** aResult)
{
nsXBLProtoImpl* impl = new nsXBLProtoImpl();
if (!impl)
return NS_ERROR_OUT_OF_MEMORY;
if (aClassName)
--- a/content/xbl/src/nsXBLProtoImpl.h
+++ b/content/xbl/src/nsXBLProtoImpl.h
@@ -100,17 +100,36 @@ public:
// Undefine all our fields from object |obj| (which should be a
// JSObject for a bound element).
void UndefineFields(JSContext* cx, JSObject* obj) const;
bool CompiledMembers() const {
return mClassObject != nsnull;
}
+ nsresult Read(nsIScriptContext* aContext,
+ nsIObjectInputStream* aStream,
+ nsXBLPrototypeBinding* aBinding,
+ nsIScriptGlobalObject* aGlobal);
+ nsresult Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream,
+ nsXBLPrototypeBinding* aBinding);
+
protected:
+ // used by Read to add each member
+ nsXBLProtoImplMember* AddMember(nsXBLProtoImplMember* aMember,
+ nsXBLProtoImplMember* aPreviousMember)
+ {
+ if (aPreviousMember)
+ aPreviousMember->SetNext(aMember);
+ else
+ mMembers = aMember;
+ return aMember;
+ }
+
void DestroyMembers();
public:
nsCString mClassName; // The name of the class.
protected:
void* mClassObject; // The class object for the binding. We'll use this to pre-compile properties
// and methods for the binding.
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -43,16 +43,18 @@
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "mozilla/FunctionTimer.h"
#include "nsXBLProtoImplField.h"
#include "nsIScriptContext.h"
#include "nsContentUtils.h"
#include "nsIURI.h"
+#include "nsXBLSerialize.h"
+#include "nsXBLPrototypeBinding.h"
nsXBLProtoImplField::nsXBLProtoImplField(const PRUnichar* aName, const PRUnichar* aReadOnly)
: mNext(nsnull),
mFieldText(nsnull),
mFieldTextLength(0),
mLineNumber(0)
{
MOZ_COUNT_CTOR(nsXBLProtoImplField);
@@ -61,16 +63,30 @@ nsXBLProtoImplField::nsXBLProtoImplField
mJSAttributes = JSPROP_ENUMERATE;
if (aReadOnly) {
nsAutoString readOnly; readOnly.Assign(aReadOnly);
if (readOnly.LowerCaseEqualsLiteral("true"))
mJSAttributes |= JSPROP_READONLY;
}
}
+
+nsXBLProtoImplField::nsXBLProtoImplField(bool aIsReadOnly)
+ : mNext(nsnull),
+ mFieldText(nsnull),
+ mFieldTextLength(0),
+ mLineNumber(0)
+{
+ MOZ_COUNT_CTOR(nsXBLProtoImplField);
+
+ mJSAttributes = JSPROP_ENUMERATE;
+ if (aIsReadOnly)
+ mJSAttributes |= JSPROP_READONLY;
+}
+
nsXBLProtoImplField::~nsXBLProtoImplField()
{
MOZ_COUNT_DTOR(nsXBLProtoImplField);
if (mFieldText)
nsMemory::Free(mFieldText);
NS_Free(mName);
NS_CONTENT_DELETE_LIST_MEMBER(nsXBLProtoImplField, this, mNext);
}
@@ -147,8 +163,50 @@ nsXBLProtoImplField::InstallField(nsIScr
name.Length(), result, nsnull, nsnull,
mJSAttributes)) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aDidInstall = true;
return NS_OK;
}
+
+nsresult
+nsXBLProtoImplField::Read(nsIScriptContext* aContext,
+ nsIObjectInputStream* aStream)
+{
+ nsAutoString name;
+ nsresult rv = aStream->ReadString(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mName = ToNewUnicode(name);
+
+ rv = aStream->Read32(&mLineNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString fieldText;
+ rv = aStream->ReadString(fieldText);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mFieldTextLength = fieldText.Length();
+ if (mFieldTextLength)
+ mFieldText = ToNewUnicode(fieldText);
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLProtoImplField::Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream)
+{
+ XBLBindingSerializeDetails type = XBLBinding_Serialize_Field;
+
+ if (mJSAttributes & JSPROP_READONLY) {
+ type |= XBLBinding_Serialize_ReadOnly;
+ }
+
+ nsresult rv = aStream->Write8(type);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->WriteWStringZ(mName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write32(mLineNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aStream->WriteWStringZ(mFieldText ? mFieldText : EmptyString().get());
+}
--- a/content/xbl/src/nsXBLProtoImplField.h
+++ b/content/xbl/src/nsXBLProtoImplField.h
@@ -47,32 +47,36 @@
#include "nsXBLProtoImplMember.h"
class nsIURI;
class nsXBLProtoImplField
{
public:
nsXBLProtoImplField(const PRUnichar* aName, const PRUnichar* aReadOnly);
+ nsXBLProtoImplField(const bool aIsReadOnly);
~nsXBLProtoImplField();
void AppendFieldText(const nsAString& aText);
void SetLineNumber(PRUint32 aLineNumber) {
mLineNumber = aLineNumber;
}
nsXBLProtoImplField* GetNext() const { return mNext; }
void SetNext(nsXBLProtoImplField* aNext) { mNext = aNext; }
nsresult InstallField(nsIScriptContext* aContext,
JSObject* aBoundNode,
nsIPrincipal* aPrincipal,
nsIURI* aBindingDocURI,
bool* aDidInstall) const;
+ nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream);
+ nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream);
+
const PRUnichar* GetName() const { return mName; }
protected:
nsXBLProtoImplField* mNext;
PRUnichar* mName;
PRUnichar* mFieldText;
PRUint32 mFieldTextLength;
PRUint32 mLineNumber;
--- a/content/xbl/src/nsXBLProtoImplMember.h
+++ b/content/xbl/src/nsXBLProtoImplMember.h
@@ -114,14 +114,20 @@ public:
void* aTargetClassObject,
const nsCString& aClassStr) = 0;
virtual nsresult CompileMember(nsIScriptContext* aContext,
const nsCString& aClassStr,
void* aClassObject)=0;
virtual void Trace(TraceCallback aCallback, void *aClosure) const = 0;
+ virtual nsresult Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream)
+ {
+ return NS_OK;
+ }
+
protected:
nsXBLProtoImplMember* mNext; // The members of an implementation are chained.
PRUnichar* mName; // The name of the field, method, or property.
};
#endif // nsXBLProtoImplMember_h__
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -46,16 +46,17 @@
#include "mozilla/FunctionTimer.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "nsXBLProtoImplMethod.h"
#include "nsIScriptContext.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsIXPConnect.h"
+#include "nsXBLPrototypeBinding.h"
nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) :
nsXBLProtoImplMember(aName),
mUncompiledMethod(BIT_UNCOMPILED)
{
MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
}
@@ -263,16 +264,51 @@ void
nsXBLProtoImplMethod::Trace(TraceCallback aCallback, void *aClosure) const
{
if (IsCompiled() && mJSMethodObject) {
aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject, "mJSMethodObject", aClosure);
}
}
nsresult
+nsXBLProtoImplMethod::Read(nsIScriptContext* aContext,
+ nsIObjectInputStream* aStream)
+{
+ void* methodCode;
+ PRUint32 lineNumber;
+ nsresult rv = XBL_DeserializeFunction(aContext, aStream, this,
+ &lineNumber, &methodCode);
+ mJSMethodObject = (JSObject *)methodCode;
+ if (NS_FAILED(rv)) {
+ SetUncompiledMethod(nsnull);
+ return rv;
+ }
+
+#ifdef DEBUG
+ mIsCompiled = true;
+#endif
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLProtoImplMethod::Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream)
+{
+ nsresult rv = aStream->Write8(XBLBinding_Serialize_Method);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->WriteWStringZ(mName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXXndeakin fix line number
+ return XBL_SerializeFunction(aContext, aStream, mJSMethodObject, 0);
+}
+
+nsresult
nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
{
NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
if (!mJSMethodObject) {
// Nothing to do here
return NS_OK;
}
@@ -343,8 +379,25 @@ nsXBLProtoImplAnonymousMethod::Execute(n
JS_ReportPendingException(cx);
if (saved)
JS_RestoreFrameChain(cx);
return NS_ERROR_FAILURE;
}
return NS_OK;
}
+
+nsresult
+nsXBLProtoImplAnonymousMethod::Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream,
+ XBLBindingSerializeDetails aType)
+{
+ if (mJSMethodObject) {
+ nsresult rv = aStream->Write8(aType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXXndeakin write out line number
+ rv = XBL_SerializeFunction(aContext, aStream, mJSMethodObject, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
--- a/content/xbl/src/nsXBLProtoImplMethod.h
+++ b/content/xbl/src/nsXBLProtoImplMethod.h
@@ -40,16 +40,17 @@
#define nsXBLProtoImplMethod_h__
#include "nsIAtom.h"
#include "nsString.h"
#include "jsapi.h"
#include "nsIContent.h"
#include "nsString.h"
#include "nsXBLProtoImplMember.h"
+#include "nsXBLSerialize.h"
struct nsXBLParameter {
nsXBLParameter* mNext;
char* mName;
nsXBLParameter(const nsAString& aName) {
MOZ_COUNT_CTOR(nsXBLParameter);
mName = ToNewCString(aName);
@@ -125,16 +126,19 @@ public:
void* aTargetClassObject,
const nsCString& aClassStr);
virtual nsresult CompileMember(nsIScriptContext* aContext,
const nsCString& aClassStr,
void* aClassObject);
virtual void Trace(TraceCallback aCallback, void *aClosure) const;
+ nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream);
+ virtual nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream);
+
bool IsCompiled() const
{
return !(mUncompiledMethod & BIT_UNCOMPILED);
}
void SetUncompiledMethod(nsXBLUncompiledMethod* aUncompiledMethod)
{
mUncompiledMethod = PRUptrdiff(aUncompiledMethod) | BIT_UNCOMPILED;
}
@@ -170,11 +174,15 @@ public:
// prototype implementation).
virtual nsresult InstallMember(nsIScriptContext* aContext,
nsIContent* aBoundElement,
void* aScriptObject,
void* aTargetClassObject,
const nsCString& aClassStr) {
return NS_OK;
}
+
+ nsresult Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream,
+ XBLBindingSerializeDetails aType);
};
#endif // nsXBLProtoImplMethod_h__
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp
+++ b/content/xbl/src/nsXBLProtoImplProperty.cpp
@@ -43,16 +43,18 @@
#include "nsIDocument.h"
#include "nsString.h"
#include "nsXBLProtoImplProperty.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "nsIScriptContext.h"
#include "nsIScriptGlobalObject.h"
#include "nsContentUtils.h"
+#include "nsXBLPrototypeBinding.h"
+#include "nsXBLSerialize.h"
nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
const PRUnichar* aGetter,
const PRUnichar* aSetter,
const PRUnichar* aReadOnly) :
nsXBLProtoImplMember(aName),
mGetterText(nsnull),
mSetterText(nsnull),
@@ -70,16 +72,32 @@ nsXBLProtoImplProperty::nsXBLProtoImplPr
}
if (aGetter)
AppendGetterText(nsDependentString(aGetter));
if (aSetter)
AppendSetterText(nsDependentString(aSetter));
}
+nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
+ const bool aIsReadOnly)
+ : nsXBLProtoImplMember(aName),
+ mGetterText(nsnull),
+ mSetterText(nsnull),
+ mJSAttributes(JSPROP_ENUMERATE)
+#ifdef DEBUG
+ , mIsCompiled(false)
+#endif
+{
+ MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
+
+ if (aIsReadOnly)
+ mJSAttributes |= JSPROP_READONLY;
+}
+
nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
{
MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
if (!(mJSAttributes & JSPROP_GETTER)) {
delete mGetterText;
}
@@ -331,8 +349,81 @@ nsXBLProtoImplProperty::Trace(TraceCallb
"mJSGetterObject", aClosure);
}
if (mJSAttributes & JSPROP_SETTER) {
aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSSetterObject,
"mJSSetterObject", aClosure);
}
}
+
+nsresult
+nsXBLProtoImplProperty::Read(nsIScriptContext* aContext,
+ nsIObjectInputStream* aStream,
+ XBLBindingSerializeDetails aType)
+{
+ nsresult rv;
+ PRUint32 lineNumber;
+ void* scriptObject;
+
+ if (aType == XBLBinding_Serialize_GetterProperty ||
+ aType == XBLBinding_Serialize_GetterSetterProperty) {
+ rv = XBL_DeserializeFunction(aContext, aStream, this, &lineNumber, &scriptObject);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mJSGetterObject = (JSObject *)scriptObject;
+ mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
+ }
+
+ if (aType == XBLBinding_Serialize_SetterProperty ||
+ aType == XBLBinding_Serialize_GetterSetterProperty) {
+ rv = XBL_DeserializeFunction(aContext, aStream, this, &lineNumber, &scriptObject);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mJSSetterObject = (JSObject *)scriptObject;
+ mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
+ }
+
+#ifdef DEBUG
+ mIsCompiled = true;
+#endif
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLProtoImplProperty::Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream)
+{
+ XBLBindingSerializeDetails type;
+
+ if (mJSAttributes & JSPROP_GETTER) {
+ type = mJSAttributes & JSPROP_SETTER ?
+ XBLBinding_Serialize_GetterSetterProperty :
+ XBLBinding_Serialize_GetterProperty;
+ }
+ else {
+ type = XBLBinding_Serialize_SetterProperty;
+ }
+
+ if (mJSAttributes & JSPROP_READONLY) {
+ type |= XBLBinding_Serialize_ReadOnly;
+ }
+
+ nsresult rv = aStream->Write8(type);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->WriteWStringZ(mName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mJSAttributes & JSPROP_GETTER) {
+ // XXXndeakin write out line number
+ rv = XBL_SerializeFunction(aContext, aStream, mJSGetterObject, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (mJSAttributes & JSPROP_SETTER) {
+ // XXXndeakin write out line number
+ rv = XBL_SerializeFunction(aContext, aStream, mJSSetterObject, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
--- a/content/xbl/src/nsXBLProtoImplProperty.h
+++ b/content/xbl/src/nsXBLProtoImplProperty.h
@@ -39,25 +39,28 @@
#ifndef nsXBLProtoImplProperty_h__
#define nsXBLProtoImplProperty_h__
#include "nsIAtom.h"
#include "nsString.h"
#include "jsapi.h"
#include "nsIContent.h"
#include "nsString.h"
+#include "nsXBLSerialize.h"
#include "nsXBLProtoImplMember.h"
class nsXBLProtoImplProperty: public nsXBLProtoImplMember
{
public:
nsXBLProtoImplProperty(const PRUnichar* aName,
const PRUnichar* aGetter,
const PRUnichar* aSetter,
const PRUnichar* aReadOnly);
+
+ nsXBLProtoImplProperty(const PRUnichar* aName, bool aIsReadOnly);
virtual ~nsXBLProtoImplProperty();
void AppendGetterText(const nsAString& aGetter);
void AppendSetterText(const nsAString& aSetter);
void SetGetterLineNumber(PRUint32 aLineNumber);
void SetSetterLineNumber(PRUint32 aLineNumber);
@@ -68,16 +71,22 @@ public:
void* aTargetClassObject,
const nsCString& aClassStr);
virtual nsresult CompileMember(nsIScriptContext* aContext,
const nsCString& aClassStr,
void* aClassObject);
virtual void Trace(TraceCallback aCallback, void *aClosure) const;
+ nsresult Read(nsIScriptContext* aContext,
+ nsIObjectInputStream* aStream,
+ XBLBindingSerializeDetails aType);
+ virtual nsresult Write(nsIScriptContext* aContext,
+ nsIObjectOutputStream* aStream);
+
protected:
union {
// The raw text for the getter (prior to compilation).
nsXBLTextWithLineNumber* mGetterText;
// The JS object for the getter (after compilation)
JSObject * mJSGetterObject;
};
--- a/content/xbl/src/nsXBLPrototypeBinding.cpp
+++ b/content/xbl/src/nsXBLPrototypeBinding.cpp
@@ -56,16 +56,17 @@
#include "nsIDocument.h"
#include "nsIXMLContentSink.h"
#include "nsContentCID.h"
#include "nsXMLDocument.h"
#include "nsXBLService.h"
#include "nsXBLBinding.h"
#include "nsXBLInsertionPoint.h"
#include "nsXBLPrototypeBinding.h"
+#include "nsXBLContentSink.h"
#include "nsFixedSizeAllocator.h"
#include "xptinfo.h"
#include "nsIInterfaceInfoManager.h"
#include "nsIDocumentObserver.h"
#include "nsGkAtoms.h"
#include "nsXBLProtoImpl.h"
#include "nsCRT.h"
#include "nsContentUtils.h"
@@ -74,18 +75,47 @@
#include "nsIScriptError.h"
#include "nsIStyleRuleProcessor.h"
#include "nsXBLResourceLoader.h"
#include "mozilla/dom/Element.h"
using namespace mozilla;
+#ifdef MOZ_XUL
+#include "nsXULElement.h"
+#endif
+
+using namespace mozilla::dom;
+
// Helper Classes =====================================================================
+// Internal helper class for managing our IID table.
+class nsIIDKey : public nsHashKey {
+ public:
+ nsIID mKey;
+
+ public:
+ nsIIDKey(REFNSIID key) : mKey(key) {}
+ ~nsIIDKey(void) {}
+
+ PRUint32 HashCode(void) const {
+ // Just use the 32-bit m0 field.
+ return mKey.m0;
+ }
+
+ bool Equals(const nsHashKey *aKey) const {
+ return mKey.Equals( ((nsIIDKey*) aKey)->mKey);
+ }
+
+ nsHashKey *Clone(void) const {
+ return new nsIIDKey(mKey);
+ }
+};
+
// nsXBLAttributeEntry and helpers. This class is used to efficiently handle
// attribute changes in anonymous content.
class nsXBLAttributeEntry {
public:
nsIAtom* GetSrcAttribute() { return mSrcAttribute; }
nsIAtom* GetDstAttribute() { return mDstAttribute; }
PRInt32 GetDstNameSpace() { return mDstNameSpace; }
@@ -277,17 +307,18 @@ nsXBLPrototypeBinding::nsXBLPrototypeBin
: mImplementation(nsnull),
mBaseBinding(nsnull),
mInheritStyle(true),
mCheckedBaseProto(false),
mKeyHandlersRegistered(false),
mResources(nsnull),
mAttributeTable(nsnull),
mInsertionPointTable(nsnull),
- mInterfaceTable(nsnull)
+ mInterfaceTable(nsnull),
+ mBaseNameSpaceID(kNameSpaceID_None)
{
MOZ_COUNT_CTOR(nsXBLPrototypeBinding);
gRefCnt++;
if (gRefCnt == 1) {
kAttrPool = new nsFixedSizeAllocator();
if (kAttrPool) {
kAttrPool->Init("XBL Attribute Entries", kAttrBucketSizes, kAttrNumBuckets, kAttrInitialSize);
@@ -314,17 +345,21 @@ nsXBLPrototypeBinding::Init(const nsACSt
if (aFirstBinding) {
rv = mBindingURI->Clone(getter_AddRefs(mAlternateBindingURI));
NS_ENSURE_SUCCESS(rv, rv);
}
mBindingURI->SetRef(aID);
mXBLDocInfoWeak = aInfo;
- SetBindingElement(aElement);
+ // aElement will be null when reading from the cache, but the element will
+ // still be set later.
+ if (aElement) {
+ SetBindingElement(aElement);
+ }
return NS_OK;
}
bool nsXBLPrototypeBinding::CompareBindingURI(nsIURI* aURI) const
{
bool equal = false;
mBindingURI->Equals(aURI, &equal);
if (!equal && mAlternateBindingURI) {
@@ -987,16 +1022,17 @@ bool SetAttrs(nsHashKey* aKey, void* aDa
nsIContent *realElement =
changeData->mProto->LocateInstance(changeData->mBoundElement, content,
changeData->mContent, element);
if (realElement) {
realElement->SetAttr(dstNs, dst, value, false);
+ // XXXndeakin shouldn't this be done in lieu of SetAttr?
if ((dst == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) ||
(realElement->NodeInfo()->Equals(nsGkAtoms::html,
kNameSpaceID_XUL) &&
dst == nsGkAtoms::value && !value.IsEmpty())) {
nsCOMPtr<nsIContent> textContent;
NS_NewTextNode(getter_AddRefs(textContent),
realElement->NodeInfo()->NodeInfoManager());
@@ -1069,32 +1105,66 @@ DeleteAttributeEntry(nsHashKey* aKey, vo
static bool
DeleteAttributeTable(nsHashKey* aKey, void* aData, void* aClosure)
{
delete static_cast<nsObjectHashtable*>(aData);
return true;
}
void
+nsXBLPrototypeBinding::EnsureAttributeTable()
+{
+ if (!mAttributeTable) {
+ mAttributeTable = new nsObjectHashtable(nsnull, nsnull,
+ DeleteAttributeTable,
+ nsnull, 4);
+ }
+}
+
+void
+nsXBLPrototypeBinding::AddToAttributeTable(PRInt32 aSourceNamespaceID, nsIAtom* aSourceTag,
+ PRInt32 aDestNamespaceID, nsIAtom* aDestTag,
+ nsIContent* aContent)
+{
+ nsPRUint32Key nskey(aSourceNamespaceID);
+ nsObjectHashtable* attributesNS =
+ static_cast<nsObjectHashtable*>(mAttributeTable->Get(&nskey));
+ if (!attributesNS) {
+ attributesNS = new nsObjectHashtable(nsnull, nsnull,
+ DeleteAttributeEntry,
+ nsnull, 4);
+ mAttributeTable->Put(&nskey, attributesNS);
+ }
+
+ nsXBLAttributeEntry* xblAttr =
+ nsXBLAttributeEntry::Create(aSourceTag, aDestTag, aDestNamespaceID, aContent);
+
+ nsISupportsKey key(aSourceTag);
+ nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry*>
+ (attributesNS->Get(&key));
+ if (!entry) {
+ attributesNS->Put(&key, xblAttr);
+ } else {
+ while (entry->GetNext())
+ entry = entry->GetNext();
+ entry->SetNext(xblAttr);
+ }
+}
+
+void
nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement)
{
// Don't add entries for <children> elements, since those will get
// removed from the DOM when we construct the insertion point table.
if (!aElement->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
nsAutoString inherits;
aElement->GetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, inherits);
if (!inherits.IsEmpty()) {
- if (!mAttributeTable) {
- mAttributeTable = new nsObjectHashtable(nsnull, nsnull,
- DeleteAttributeTable,
- nsnull, 4);
- if (!mAttributeTable)
- return;
- }
+ EnsureAttributeTable();
// The user specified at least one attribute.
char* str = ToNewCString(inherits);
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
char* token = nsCRT::strtok( str, ", ", &newStr );
@@ -1131,48 +1201,17 @@ nsXBLPrototypeBinding::ConstructAttribut
rv = nsContentUtils::SplitQName(aElement, tok, &atomNsID,
getter_AddRefs(atom));
if (NS_FAILED(rv))
return;
attribute = atom;
attributeNsID = atomNsID;
}
- nsPRUint32Key nskey(atomNsID);
- nsObjectHashtable* attributesNS =
- static_cast<nsObjectHashtable*>(mAttributeTable->Get(&nskey));
- if (!attributesNS) {
- attributesNS = new nsObjectHashtable(nsnull, nsnull,
- DeleteAttributeEntry,
- nsnull, 4);
- if (!attributesNS)
- return;
-
- mAttributeTable->Put(&nskey, attributesNS);
- }
-
- // Create an XBL attribute entry.
- nsXBLAttributeEntry* xblAttr =
- nsXBLAttributeEntry::Create(atom, attribute, attributeNsID, aElement);
-
- // Now we should see if some element within our anonymous
- // content is already observing this attribute.
- nsISupportsKey key(atom);
- nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry*>
- (attributesNS->Get(&key));
-
- if (!entry) {
- // Put it in the table.
- attributesNS->Put(&key, xblAttr);
- } else {
- while (entry->GetNext())
- entry = entry->GetNext();
-
- entry->SetNext(xblAttr);
- }
+ AddToAttributeTable(atomNsID, atom, attributeNsID, attribute, aElement);
// Now remove the inherits attribute from the element so that it doesn't
// show up on clones of the element. It is used
// by the template only, and we don't need it anymore.
// XXXdwh Don't do this for XUL elements, since it faults them into heavyweight
// elements. Should nuke from the prototype instead.
// aElement->UnsetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, false);
@@ -1239,17 +1278,17 @@ nsXBLPrototypeBinding::ConstructInsertio
char* token = nsCRT::strtok( str, "| ", &newStr );
while( token != NULL ) {
nsAutoString tok;
tok.AssignWithConversion(token);
// Build an atom out of this string.
nsCOMPtr<nsIAtom> atom = do_GetAtom(tok);
-
+
nsISupportsKey key(atom);
xblIns->AddRef();
mInsertionPointTable->Put(&key, xblIns);
token = nsCRT::strtok( newStr, "| ", &newStr );
}
nsMemory::Free(str);
@@ -1423,16 +1462,831 @@ nsXBLPrototypeBinding::CreateKeyHandlers
if (handler)
handler->AddProtoHandler(curr);
}
curr = curr->GetNextHandler();
}
}
+nsresult
+nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream,
+ nsXBLDocumentInfo* aDocInfo,
+ nsIDocument* aDocument,
+ PRUint8 aFlags)
+{
+ mInheritStyle = (aFlags & XBLBinding_Serialize_InheritStyle) ? true : false;
+
+ // nsXBLContentSink::ConstructBinding doesn't create a binding with an empty
+ // id, so we don't here either.
+ nsCAutoString id;
+ nsresult rv = aStream->ReadCString(id);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(!id.IsEmpty(), NS_ERROR_FAILURE);
+
+ nsCAutoString baseBindingURI;
+ rv = aStream->ReadCString(baseBindingURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCheckedBaseProto = true;
+
+ if (!baseBindingURI.IsEmpty()) {
+ rv = NS_NewURI(getter_AddRefs(mBaseBindingURI), baseBindingURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = ReadNamespace(aStream, mBaseNameSpaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString baseTag;
+ rv = aStream->ReadString(baseTag);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!baseTag.IsEmpty()) {
+ mBaseTag = do_GetAtom(baseTag);
+ }
+
+ aDocument->CreateElem(NS_LITERAL_STRING("binding"), nsnull, kNameSpaceID_XBL,
+ getter_AddRefs(mBinding));
+
+ nsCOMPtr<nsIContent> child;
+ rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(), getter_AddRefs(child));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ Element* rootElement = aDocument->GetRootElement();
+ if (rootElement)
+ rootElement->AppendChildTo(mBinding, false);
+
+ if (child) {
+ mBinding->AppendChildTo(child, false);
+ }
+
+ PRUint32 interfaceCount;
+ rv = aStream->Read32(&interfaceCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (interfaceCount > 0) {
+ NS_ASSERTION(!mInterfaceTable, "non-null mInterfaceTable");
+ mInterfaceTable = new nsSupportsHashtable(interfaceCount);
+ NS_ENSURE_TRUE(mInterfaceTable, NS_ERROR_OUT_OF_MEMORY);
+
+ for (; interfaceCount > 0; interfaceCount--) {
+ nsIID iid;
+ aStream->ReadID(&iid);
+ nsIIDKey key(iid);
+ mInterfaceTable->Put(&key, mBinding);
+ }
+ }
+
+ nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(do_QueryObject(aDocInfo));
+ nsIScriptGlobalObject* globalObject = globalOwner->GetScriptGlobalObject();
+ NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
+
+ nsIScriptContext *context = globalObject->GetContext();
+ NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
+
+ bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding;
+ rv = Init(id, aDocInfo, nsnull, isFirstBinding);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We need to set the prototype binding before reading the nsXBLProtoImpl,
+ // as it may be retrieved within.
+ rv = aDocInfo->SetPrototypeBinding(id, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCAutoString className;
+ rv = aStream->ReadCString(className);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!className.IsEmpty()) {
+ nsXBLProtoImpl* impl; // NS_NewXBLProtoImpl will set mImplementation for us
+ NS_NewXBLProtoImpl(this, NS_ConvertUTF8toUTF16(className).get(), &impl);
+
+ // This needs to happen after SetPrototypeBinding as calls are made to
+ // retrieve the mapped bindings from within here. However, if an error
+ // occurs, the mapping should be removed again so that we don't keep an
+ // invalid binding around.
+ rv = mImplementation->Read(context, aStream, this, globalObject);
+ if (NS_FAILED(rv)) {
+ aDocInfo->RemovePrototypeBinding(id);
+ return rv;
+ }
+ }
+
+ // Next read in the handlers.
+ nsXBLPrototypeHandler* previousHandler = nsnull;
+
+ do {
+ XBLBindingSerializeDetails type;
+ rv = aStream->Read8(&type);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (type == XBLBinding_Serialize_NoMoreItems)
+ break;
+
+ NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Handler,
+ "invalid handler type");
+
+ nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(this);
+ rv = handler->Read(context, aStream);
+ if (NS_FAILED(rv)) {
+ delete handler;
+ return rv;
+ }
+
+ if (previousHandler) {
+ previousHandler->SetNextHandler(handler);
+ }
+ else {
+ SetPrototypeHandlers(handler);
+ }
+ previousHandler = handler;
+ } while (1);
+
+ // Finally, read in the resources.
+ do {
+ XBLBindingSerializeDetails type;
+ rv = aStream->Read8(&type);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (type == XBLBinding_Serialize_NoMoreItems)
+ break;
+
+ NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Stylesheet ||
+ (type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Image, "invalid resource type");
+
+ nsAutoString src;
+ rv = aStream->ReadString(src);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ AddResource(type == XBLBinding_Serialize_Stylesheet ? nsGkAtoms::stylesheet :
+ nsGkAtoms::image, src);
+ } while (1);
+
+ if (isFirstBinding) {
+ aDocInfo->SetFirstPrototypeBinding(this);
+ }
+
+ return NS_OK;
+}
+
+static
+bool
+GatherInsertionPoints(nsHashKey *aKey, void *aData, void* aClosure)
+{
+ ArrayOfInsertionPointsByContent* insertionPointsByContent =
+ static_cast<ArrayOfInsertionPointsByContent *>(aClosure);
+
+ nsXBLInsertionPointEntry* entry = static_cast<nsXBLInsertionPointEntry *>(aData);
+
+ // Add a new blank array if it isn't already there.
+ nsAutoTArray<InsertionItem, 1>* list;
+ if (!insertionPointsByContent->Get(entry->GetInsertionParent(), &list)) {
+ list = new nsAutoTArray<InsertionItem, 1>;
+ insertionPointsByContent->Put(entry->GetInsertionParent(), list);
+ }
+
+ // Add the item to the array and ensure it stays sorted by insertion index.
+ nsIAtom* atom = static_cast<nsIAtom *>(
+ static_cast<nsISupportsKey *>(aKey)->GetValue());
+ InsertionItem newitem(entry->GetInsertionIndex(), atom, entry->GetDefaultContent());
+ list->InsertElementSorted(newitem);
+
+ return kHashEnumerateNext;
+}
+
+static
+bool
+WriteInterfaceID(nsHashKey *aKey, void *aData, void* aClosure)
+{
+ // We can just write out the ids. The cache will be invalidated when a
+ // different build is used, so we don't need to worry about ids changing.
+ nsID iid = ((nsIIDKey *)aKey)->mKey;
+ static_cast<nsIObjectOutputStream *>(aClosure)->WriteID(iid);
+ return kHashEnumerateNext;
+}
+
+nsresult
+nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream)
+{
+ // This writes out the binding. Note that mCheckedBaseProto,
+ // mKeyHandlersRegistered and mKeyHandlers are not serialized as they are
+ // computed on demand.
+
+ nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(do_QueryObject(mXBLDocInfoWeak));
+ nsIScriptGlobalObject* globalObject = globalOwner->GetScriptGlobalObject();
+ NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
+
+ nsIScriptContext *context = globalObject->GetContext();
+ NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
+
+ PRUint8 flags = mInheritStyle ? XBLBinding_Serialize_InheritStyle : 0;
+
+ // mAlternateBindingURI is only set on the first binding.
+ if (mAlternateBindingURI) {
+ flags |= XBLBinding_Serialize_IsFirstBinding;
+ }
+
+ nsresult rv = aStream->Write8(flags);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCAutoString id;
+ mBindingURI->GetRef(id);
+ rv = aStream->WriteStringZ(id.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // write out the extends and display attribute values
+ nsCAutoString extends;
+ ResolveBaseBinding();
+ if (mBaseBindingURI)
+ mBaseBindingURI->GetSpec(extends);
+
+ rv = aStream->WriteStringZ(extends.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = WriteNamespace(aStream, mBaseNameSpaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString baseTag;
+ if (mBaseTag) {
+ mBaseTag->ToString(baseTag);
+ }
+ rv = aStream->WriteWStringZ(baseTag.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The binding holds insertions points keyed by the tag that is going to be
+ // inserted, however, the cache would prefer to know insertion points keyed
+ // by where they are in the content hierarchy. GatherInsertionPoints is used
+ // to iterate over the insertion points and store them temporarily in this
+ // latter hashtable.
+ ArrayOfInsertionPointsByContent insertionPointsByContent;
+ insertionPointsByContent.Init();
+ if (mInsertionPointTable) {
+ mInsertionPointTable->Enumerate(GatherInsertionPoints, &insertionPointsByContent);
+ }
+
+ nsIContent* content = GetImmediateChild(nsGkAtoms::content);
+ if (content) {
+ rv = WriteContentNode(aStream, content, insertionPointsByContent);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ // Write a marker to indicate that there is no content.
+ rv = aStream->Write8(XBLBinding_Serialize_NoContent);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Enumerate and write out the implemented interfaces.
+ if (mInterfaceTable) {
+ rv = aStream->Write32(mInterfaceTable->Count());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInterfaceTable->Enumerate(WriteInterfaceID, aStream);
+ }
+ else {
+ rv = aStream->Write32(0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Write out the implementation details.
+ if (mImplementation) {
+ rv = mImplementation->Write(context, aStream, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ // Write out an empty classname. This indicates that the binding does not
+ // define an implementation.
+ rv = aStream->WriteWStringZ(EmptyString().get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Write out the handlers.
+ nsXBLPrototypeHandler* handler = mPrototypeHandler;
+ while (handler) {
+ rv = handler->Write(context, aStream);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ handler = handler->GetNextHandler();
+ }
+
+ aStream->Write8(XBLBinding_Serialize_NoMoreItems);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Write out the resources
+ if (mResources) {
+ rv = mResources->Write(aStream);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Write out an end mark at the end.
+ return aStream->Write8(XBLBinding_Serialize_NoMoreItems);
+}
+
+nsresult
+nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
+ nsIDocument* aDocument,
+ nsNodeInfoManager* aNim,
+ nsIContent** aContent)
+{
+ *aContent = nsnull;
+
+ PRInt32 namespaceID;
+ nsresult rv = ReadNamespace(aStream, namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // There is no content to read so just return.
+ if (namespaceID == XBLBinding_Serialize_NoContent)
+ return NS_OK;
+
+ nsCOMPtr<nsIContent> content;
+
+ // If this is a text type, just read the string and return.
+ if (namespaceID == XBLBinding_Serialize_TextNode ||
+ namespaceID == XBLBinding_Serialize_CDATANode ||
+ namespaceID == XBLBinding_Serialize_CommentNode) {
+ switch (namespaceID) {
+ case XBLBinding_Serialize_TextNode:
+ rv = NS_NewTextNode(getter_AddRefs(content), aNim);
+ break;
+ case XBLBinding_Serialize_CDATANode:
+ rv = NS_NewXMLCDATASection(getter_AddRefs(content), aNim);
+ break;
+ case XBLBinding_Serialize_CommentNode:
+ rv = NS_NewCommentNode(getter_AddRefs(content), aNim);
+ break;
+ default:
+ break;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString text;
+ rv = aStream->ReadString(text);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ content->SetText(text, false);
+ content.swap(*aContent);
+ return NS_OK;
+ }
+
+ // Otherwise, it's an element, so read its tag, attributes and children.
+ nsAutoString prefix, tag;
+ rv = aStream->ReadString(prefix);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAtom> prefixAtom;
+ if (!prefix.IsEmpty())
+ prefixAtom = do_GetAtom(prefix);
+
+ rv = aStream->ReadString(tag);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
+ nsCOMPtr<nsINodeInfo> nodeInfo =
+ aNim->GetNodeInfo(tagAtom, prefixAtom, namespaceID, nsIDOMNode::ELEMENT_NODE);
+
+ PRUint32 attrCount;
+ rv = aStream->Read32(&attrCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create XUL prototype elements, or regular elements for other namespaces.
+ // This needs to match the code in nsXBLContentSink::CreateElement.
+#ifdef MOZ_XUL
+ if (namespaceID == kNameSpaceID_XUL) {
+ nsIURI* documentURI = aDocument->GetDocumentURI();
+
+ nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
+ NS_ENSURE_TRUE(prototype, NS_ERROR_OUT_OF_MEMORY);
+
+ prototype->mNodeInfo = nodeInfo;
+ prototype->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
+
+ nsCOMPtr<Element> result;
+ nsresult rv =
+ nsXULElement::Create(prototype, aDocument, false, getter_AddRefs(result));
+ NS_ENSURE_SUCCESS(rv, rv);
+ content = result;
+
+ nsXULPrototypeAttribute* attrs = nsnull;
+ if (attrCount > 0) {
+ attrs = new nsXULPrototypeAttribute[attrCount];
+ }
+
+ prototype->mAttributes = attrs;
+ prototype->mNumAttributes = attrCount;
+
+ for (PRUint32 i = 0; i < attrCount; i++) {
+ rv = ReadNamespace(aStream, namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString prefix, name, val;
+ rv = aStream->ReadString(prefix);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->ReadString(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->ReadString(val);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
+ if (namespaceID == kNameSpaceID_None) {
+ attrs[i].mName.SetTo(nameAtom);
+ }
+ else {
+ nsCOMPtr<nsIAtom> prefixAtom;
+ if (!prefix.IsEmpty())
+ prefixAtom = do_GetAtom(prefix);
+
+ nsCOMPtr<nsINodeInfo> ni =
+ aNim->GetNodeInfo(nameAtom, prefixAtom,
+ namespaceID, nsIDOMNode::ATTRIBUTE_NODE);
+ attrs[i].mName.SetTo(ni);
+ }
+
+ rv = prototype->SetAttrAt(i, val, documentURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ else {
+#endif
+ nsCOMPtr<nsINodeInfo> ni = nodeInfo;
+ NS_NewElement(getter_AddRefs(content), nodeInfo->NamespaceID(),
+ ni.forget(), mozilla::dom::NOT_FROM_PARSER);
+
+ for (PRUint32 i = 0; i < attrCount; i++) {
+ rv = ReadNamespace(aStream, namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString prefix, name, val;
+ rv = aStream->ReadString(prefix);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->ReadString(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->ReadString(val);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAtom> prefixAtom;
+ if (!prefix.IsEmpty())
+ prefixAtom = do_GetAtom(prefix);
+
+ nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
+ content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
+ }
+
+#ifdef MOZ_XUL
+ }
+#endif
+
+ // Now read the attribute forwarding entries (xbl:inherits)
+
+ PRInt32 srcNamespaceID, destNamespaceID;
+ rv = ReadNamespace(aStream, srcNamespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ while (srcNamespaceID != XBLBinding_Serialize_NoMoreAttributes) {
+ nsAutoString srcAttribute, destAttribute;
+ rv = aStream->ReadString(srcAttribute);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = ReadNamespace(aStream, destNamespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->ReadString(destAttribute);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAtom> srcAtom = do_GetAtom(srcAttribute);
+ nsCOMPtr<nsIAtom> destAtom = do_GetAtom(destAttribute);
+
+ EnsureAttributeTable();
+ AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content);
+
+ rv = ReadNamespace(aStream, srcNamespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Next, read the insertion points.
+ PRUint32 insertionPointIndex;
+ rv = aStream->Read32(&insertionPointIndex);
+ NS_ENSURE_SUCCESS(rv, rv);
+ while (insertionPointIndex != XBLBinding_Serialize_NoMoreInsertionPoints) {
+ nsRefPtr<nsXBLInsertionPointEntry> xblIns =
+ nsXBLInsertionPointEntry::Create(content);
+ xblIns->SetInsertionIndex(insertionPointIndex);
+
+ // If the insertion point has default content, read it.
+ nsCOMPtr<nsIContent> defaultContent;
+ rv = ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(defaultContent));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (defaultContent) {
+ xblIns->SetDefaultContent(defaultContent);
+
+ rv = defaultContent->BindToTree(nsnull, content, nsnull, false);
+ if (NS_FAILED(rv)) {
+ defaultContent->UnbindFromTree();
+ return rv;
+ }
+ }
+
+ if (!mInsertionPointTable) {
+ mInsertionPointTable = new nsObjectHashtable(nsnull, nsnull,
+ DeleteInsertionPointEntry,
+ nsnull, 4);
+ }
+
+ // Now read in the tags that can be inserted at this point, which is
+ // specified by the syntax includes="menupopup|panel", and add them to
+ // the hash.
+ PRUint32 count;
+ rv = aStream->Read32(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (; count > 0; count --) {
+ aStream->ReadString(tag);
+ nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
+
+ nsISupportsKey key(tagAtom);
+ NS_ADDREF(xblIns.get());
+ mInsertionPointTable->Put(&key, xblIns);
+ }
+
+ rv = aStream->Read32(&insertionPointIndex);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Finally, read in the child nodes.
+ PRUint32 childCount;
+ rv = aStream->Read32(&childCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (PRUint32 i = 0; i < childCount; i++) {
+ nsCOMPtr<nsIContent> child;
+ ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child));
+
+ // Child may be null if this was a comment for example and can just be ignored.
+ if (child) {
+ content->AppendChildTo(child, false);
+ }
+ }
+
+ content.swap(*aContent);
+ return NS_OK;
+}
+
+// This structure holds information about a forwarded attribute that needs to be
+// written out. This is used because we need several fields passed within the
+// enumeration closure.
+struct WriteAttributeData
+{
+ nsXBLPrototypeBinding* binding;
+ nsIObjectOutputStream* stream;
+ nsIContent* content;
+ PRInt32 srcNamespace;
+
+ WriteAttributeData(nsXBLPrototypeBinding* aBinding,
+ nsIObjectOutputStream* aStream,
+ nsIContent* aContent)
+ : binding(aBinding), stream(aStream), content(aContent)
+ { }
+};
+
+static
+bool
+WriteAttribute(nsHashKey *aKey, void *aData, void* aClosure)
+{
+ WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
+ nsIObjectOutputStream* stream = data->stream;
+ const PRInt32 srcNamespace = data->srcNamespace;
+
+ nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry *>(aData);
+ do {
+ if (entry->GetElement() == data->content) {
+ data->binding->WriteNamespace(stream, srcNamespace);
+ stream->WriteWStringZ(nsDependentAtomString(entry->GetSrcAttribute()).get());
+ data->binding->WriteNamespace(stream, entry->GetDstNameSpace());
+ stream->WriteWStringZ(nsDependentAtomString(entry->GetDstAttribute()).get());
+ }
+
+ entry = entry->GetNext();
+ } while (entry);
+
+ return kHashEnumerateNext;
+}
+
+// WriteAttributeNS is the callback to enumerate over the attribute
+// forwarding entries. Since these are stored in a hash of hashes,
+// we need to iterate over the inner hashes, calling WriteAttribute
+// to do the actual work.
+static
+bool
+WriteAttributeNS(nsHashKey *aKey, void *aData, void* aClosure)
+{
+ WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
+ data->srcNamespace = static_cast<nsPRUint32Key *>(aKey)->GetValue();
+
+ nsObjectHashtable* attributes = static_cast<nsObjectHashtable*>(aData);
+ attributes->Enumerate(WriteAttribute, data);
+
+ return kHashEnumerateNext;
+}
+
+nsresult
+nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
+ nsIContent* aNode,
+ ArrayOfInsertionPointsByContent& aInsertionPointsByContent)
+{
+ nsresult rv;
+
+ if (!aNode->IsElement()) {
+ // Text is writen out as a single byte for the type, followed by the text.
+ PRUint8 type = XBLBinding_Serialize_NoContent;
+ switch (aNode->NodeType()) {
+ case nsIDOMNode::TEXT_NODE:
+ type = XBLBinding_Serialize_TextNode;
+ break;
+ case nsIDOMNode::CDATA_SECTION_NODE:
+ type = XBLBinding_Serialize_CDATANode;
+ break;
+ case nsIDOMNode::COMMENT_NODE:
+ type = XBLBinding_Serialize_CommentNode;
+ break;
+ default:
+ break;
+ }
+
+ rv = aStream->Write8(type);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString content;
+ aNode->GetText()->AppendTo(content);
+ return aStream->WriteWStringZ(content.get());
+ }
+
+ // Otherwise, this is an element.
+
+ // Write the namespace id followed by the tag name
+ rv = WriteNamespace(aStream, aNode->GetNameSpaceID());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString prefixStr;
+ aNode->NodeInfo()->GetPrefix(prefixStr);
+ rv = aStream->WriteWStringZ(prefixStr.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->WriteWStringZ(nsDependentAtomString(aNode->Tag()).get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Write attributes
+ PRUint32 count = aNode->GetAttrCount();
+ rv = aStream->Write32(count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRUint32 i;
+ for (i = 0; i < count; i++) {
+ // Write out the namespace id, the namespace prefix, the local tag name,
+ // and the value, in that order.
+
+ const nsAttrName* attr = aNode->GetAttrNameAt(i);
+
+ // XXXndeakin don't write out xbl:inherits?
+ PRInt32 namespaceID = attr->NamespaceID();
+ rv = WriteNamespace(aStream, namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString prefixStr;
+ nsIAtom* prefix = attr->GetPrefix();
+ if (prefix)
+ prefix->ToString(prefixStr);
+ rv = aStream->WriteWStringZ(prefixStr.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->WriteWStringZ(nsDependentAtomString(attr->LocalName()).get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString val;
+ aNode->GetAttr(attr->NamespaceID(), attr->LocalName(), val);
+ rv = aStream->WriteWStringZ(val.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Write out the attribute fowarding information
+ if (mAttributeTable) {
+ WriteAttributeData data(this, aStream, aNode);
+ mAttributeTable->Enumerate(WriteAttributeNS, &data);
+ }
+ rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Write out the insertion points.
+ nsAutoTArray<InsertionItem, 1>* list;
+ if (aInsertionPointsByContent.Get(aNode, &list)) {
+ PRUint32 lastInsertionIndex = 0xFFFFFFFF;
+
+ // Iterate over the insertion points and see if they match this point
+ // in the content tree by comparing insertion indices.
+ for (PRUint32 l = 0; l < list->Length(); l++) {
+ InsertionItem item = list->ElementAt(l);
+ // The list is sorted by insertionIndex so all items pertaining to
+ // this point will be in the list in order. We only need to write out the
+ // default content and the number of tags once for each index.
+ if (item.insertionIndex != lastInsertionIndex) {
+ lastInsertionIndex = item.insertionIndex;
+ aStream->Write32(item.insertionIndex);
+ // If the insertion point has default content, write that out, or
+ // write out XBLBinding_Serialize_NoContent if there isn't any.
+ if (item.defaultContent) {
+ rv = WriteContentNode(aStream, item.defaultContent,
+ aInsertionPointsByContent);
+ }
+ else {
+ rv = aStream->Write8(XBLBinding_Serialize_NoContent);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Determine how many insertion points share the same index, so that
+ // the total count can be written out.
+ PRUint32 icount = 1;
+ for (PRUint32 i = l + 1; i < list->Length(); i++) {
+ if (list->ElementAt(i).insertionIndex != lastInsertionIndex)
+ break;
+ icount++;
+ }
+
+ rv = aStream->Write32(icount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = aStream->WriteWStringZ(nsDependentAtomString(list->ElementAt(l).tag).get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ // Write out an end marker after the insertion points. If there weren't
+ // any, this will be all that is written out.
+ rv = aStream->Write32(XBLBinding_Serialize_NoMoreInsertionPoints);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Finally, write out the child nodes.
+ count = aNode->GetChildCount();
+ rv = aStream->Write32(count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (i = 0; i < count; i++) {
+ rv = WriteContentNode(aStream, aNode->GetChildAt(i), aInsertionPointsByContent);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream,
+ PRInt32& aNameSpaceID)
+{
+ PRUint8 namespaceID;
+ nsresult rv = aStream->Read8(&namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (namespaceID == XBLBinding_Serialize_CustomNamespace) {
+ nsAutoString namesp;
+ rv = aStream->ReadString(namesp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsContentUtils::NameSpaceManager()->RegisterNameSpace(namesp, aNameSpaceID);
+ }
+ else {
+ aNameSpaceID = namespaceID;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeBinding::WriteNamespace(nsIObjectOutputStream* aStream,
+ PRInt32 aNameSpaceID)
+{
+ // Namespaces are stored as a single byte id for well-known namespaces.
+ // This saves time and space as other namespaces aren't very common in
+ // XBL. If another namespace is used however, the namespace id will be
+ // XBLBinding_Serialize_CustomNamespace and the string namespace written
+ // out directly afterwards.
+ nsresult rv;
+
+ if (aNameSpaceID <= kNameSpaceID_LastBuiltin) {
+ rv = aStream->Write8((PRInt8)aNameSpaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ rv = aStream->Write8(XBLBinding_Serialize_CustomNamespace);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString namesp;
+ nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, namesp);
+ aStream->WriteWStringZ(namesp.get());
+ }
+
+ return NS_OK;
+}
+
+
bool CheckTagNameWhiteList(PRInt32 aNameSpaceID, nsIAtom *aTagName)
{
static nsIContent::AttrValuesArray kValidXULTagNames[] = {
&nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser,
&nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu,
&nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup,
&nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer,
&nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nsnull};
--- a/content/xbl/src/nsXBLPrototypeBinding.h
+++ b/content/xbl/src/nsXBLPrototypeBinding.h
@@ -42,30 +42,59 @@
#include "nsCOMPtr.h"
#include "nsXBLPrototypeResources.h"
#include "nsXBLPrototypeHandler.h"
#include "nsXBLProtoImplMethod.h"
#include "nsICSSLoaderObserver.h"
#include "nsWeakReference.h"
#include "nsIContent.h"
#include "nsHashtable.h"
+#include "nsClassHashtable.h"
#include "nsXBLDocumentInfo.h"
#include "nsCOMArray.h"
#include "nsXBLProtoImpl.h"
class nsIAtom;
class nsIDocument;
class nsIScriptContext;
class nsSupportsHashtable;
class nsIXBLService;
class nsFixedSizeAllocator;
class nsXBLProtoImplField;
class nsXBLBinding;
class nsCSSStyleSheet;
+// This structure represents an insertion point, and is used when writing out
+// insertion points. It contains comparison operators so that it can be stored
+// in an array sorted by index.
+struct InsertionItem {
+ PRUint32 insertionIndex;
+ nsIAtom* tag;
+ nsIContent* defaultContent;
+
+ InsertionItem(PRUint32 aInsertionIndex, nsIAtom* aTag, nsIContent* aDefaultContent)
+ : insertionIndex(aInsertionIndex), tag(aTag), defaultContent(aDefaultContent) { }
+
+ bool operator<(const InsertionItem& item) const
+ {
+ NS_ASSERTION(insertionIndex != item.insertionIndex || defaultContent == item.defaultContent,
+ "default content is different for same index");
+ return insertionIndex < item.insertionIndex;
+ }
+
+ bool operator==(const InsertionItem& item) const
+ {
+ NS_ASSERTION(insertionIndex != item.insertionIndex || defaultContent == item.defaultContent,
+ "default content is different for same index");
+ return insertionIndex == item.insertionIndex;
+ }
+};
+
+typedef nsClassHashtable<nsISupportsHashKey, nsAutoTArray<InsertionItem, 1> > ArrayOfInsertionPointsByContent;
+
// *********************************************************************/
// The XBLPrototypeBinding class
// Instances of this class are owned by the nsXBLDocumentInfo object returned
// by XBLDocumentInfo(). Consumers who want to refcount things should refcount
// that.
class nsXBLPrototypeBinding
{
@@ -186,16 +215,94 @@ public:
if (!mKeyHandlersRegistered) {
CreateKeyHandlers();
mKeyHandlersRegistered = true;
}
return &mKeyHandlers;
}
+ /**
+ * Read this binding from the stream aStream into the xbl document aDocument.
+ * aDocInfo should be the xbl document info for the binding document.
+ * aFlags can contain XBLBinding_Serialize_InheritStyle to indicate that
+ * mInheritStyle flag should be set, and XBLBinding_Serialize_IsFirstBinding
+ * to indicate the first binding in a document.
+ */
+ nsresult Read(nsIObjectInputStream* aStream,
+ nsXBLDocumentInfo* aDocInfo,
+ nsIDocument* aDocument,
+ PRUint8 aFlags);
+
+ /**
+ * Write this binding to the stream.
+ */
+ nsresult Write(nsIObjectOutputStream* aStream);
+
+ /**
+ * Read a content node from aStream and return it in aChild.
+ * aDocument and aNim are the document and node info manager for the document
+ * the child will be inserted into.
+ */
+ nsresult ReadContentNode(nsIObjectInputStream* aStream,
+ nsIDocument* aDocument,
+ nsNodeInfoManager* aNim,
+ nsIContent** aChild);
+
+ /**
+ * Write the content node aNode to aStream.
+ * aInsertionPointsByContent is a hash of the insertion points in the binding,
+ * keyed by where there are in the content hierarchy.
+ *
+ * This method is called recursively for each child descendant. For the topmost
+ * call, aNode must be an element.
+ *
+ * Text, CDATA and comment nodes are serialized as:
+ * the constant XBLBinding_Serialize_TextNode, XBLBinding_Serialize_CDATANode
+ * or XBLBinding_Serialize_CommentNode
+ * the text for the node
+ * Elements are serialized in the following format:
+ * node's namespace, written with WriteNamespace
+ * node's namespace prefix
+ * node's tag
+ * 32-bit attribute count
+ * table of attributes:
+ * attribute's namespace, written with WriteNamespace
+ * attribute's namespace prefix
+ * attribute's tag
+ * attribute's value
+ * attribute forwarding table:
+ * source namespace
+ * source attribute
+ * destination namespace
+ * destination attribute
+ * the constant XBLBinding_Serialize_NoMoreAttributes
+ * insertion points within this node:
+ * child index to insert within node
+ * default content serialized in the same manner or XBLBinding_Serialize_NoContent
+ * count of insertion points at that index
+ * that number of string tags (those in the <children>'s includes attribute)
+ * the constant XBLBinding_Serialize_NoMoreInsertionPoints
+ * 32-bit count of the number of child nodes
+ * each child node is serialized in the same manner in sequence
+ * the constant XBLBinding_Serialize_NoContent
+ */
+ nsresult WriteContentNode(nsIObjectOutputStream* aStream,
+ nsIContent* aNode,
+ ArrayOfInsertionPointsByContent& aInsertionPointsByContent);
+
+ /**
+ * Read or write a namespace id from or to aStream. If the namespace matches
+ * one of the built-in ones defined in nsINameSpaceManager.h, it will be written as
+ * a single byte with that value. Otherwise, XBLBinding_Serialize_CustomNamespace is
+ * written out, followed by a string written with writeWStringZ.
+ */
+ nsresult ReadNamespace(nsIObjectInputStream* aStream, PRInt32& aNameSpaceID);
+ nsresult WriteNamespace(nsIObjectOutputStream* aStream, PRInt32 aNameSpaceID);
+
public:
nsXBLPrototypeBinding();
~nsXBLPrototypeBinding();
// Init must be called after construction to initialize the prototype
// binding. It may well throw errors (eg on out-of-memory). Do not confuse
// this with the Initialize() method, which must be called after the
// binding's handlers, properties, etc are all set.
@@ -223,48 +330,30 @@ public:
* has the localname given by aTag and is in the XBL namespace.
*/
nsIContent* GetImmediateChild(nsIAtom* aTag);
nsIContent* LocateInstance(nsIContent* aBoundElt,
nsIContent* aTemplRoot,
nsIContent* aCopyRoot,
nsIContent* aTemplChild);
-protected:
+protected:
+ // Ensure that mAttributeTable has been created.
+ void EnsureAttributeTable();
+ // Ad an entry to the attribute table
+ void AddToAttributeTable(PRInt32 aSourceNamespaceID, nsIAtom* aSourceTag,
+ PRInt32 aDestNamespaceID, nsIAtom* aDestTag,
+ nsIContent* aContent);
void ConstructAttributeTable(nsIContent* aElement);
void ConstructInsertionTable(nsIContent* aElement);
void GetNestedChildren(nsIAtom* aTag, PRInt32 aNamespace,
nsIContent* aContent,
nsCOMArray<nsIContent> & aList);
void CreateKeyHandlers();
-protected:
- // Internal helper class for managing our IID table.
- class nsIIDKey : public nsHashKey {
- protected:
- nsIID mKey;
-
- public:
- nsIIDKey(REFNSIID key) : mKey(key) {}
- ~nsIIDKey(void) {}
-
- PRUint32 HashCode(void) const {
- // Just use the 32-bit m0 field.
- return mKey.m0;
- }
-
- bool Equals(const nsHashKey *aKey) const {
- return mKey.Equals( ((nsIIDKey*) aKey)->mKey);
- }
-
- nsHashKey *Clone(void) const {
- return new nsIIDKey(mKey);
- }
- };
-
// MEMBER VARIABLES
protected:
nsCOMPtr<nsIURI> mBindingURI;
nsCOMPtr<nsIURI> mAlternateBindingURI; // Alternate id-less URI that is only non-null on the first binding.
nsCOMPtr<nsIContent> mBinding; // Strong. We own a ref to our content element in the binding doc.
nsAutoPtr<nsXBLPrototypeHandler> mPrototypeHandler; // Strong. DocInfo owns us, and we own the handlers.
// the url of the base binding
@@ -293,9 +382,8 @@ protected:
PRInt32 mBaseNameSpaceID; // If we extend a tagname/namespace, then that information will
nsCOMPtr<nsIAtom> mBaseTag; // be stored in here.
nsCOMArray<nsXBLKeyEventHandler> mKeyHandlers;
};
#endif
-
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -74,16 +74,17 @@
#include "nsGUIEvent.h"
#include "nsIXPConnect.h"
#include "nsIDOMScriptObjectFactory.h"
#include "nsDOMCID.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "nsCRT.h"
#include "nsXBLEventHandler.h"
+#include "nsXBLSerialize.h"
#include "nsEventDispatcher.h"
#include "mozilla/Preferences.h"
using namespace mozilla;
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
@@ -118,41 +119,44 @@ nsXBLPrototypeHandler::nsXBLPrototypeHan
const PRUnichar* aAllowUntrusted,
nsXBLPrototypeBinding* aBinding,
PRUint32 aLineNumber)
: mHandlerText(nsnull),
mLineNumber(aLineNumber),
mNextHandler(nsnull),
mPrototypeBinding(aBinding)
{
- ++gRefCnt;
- if (gRefCnt == 1)
- // Get the primary accelerator key.
- InitAccessKeys();
+ Init();
ConstructPrototype(nsnull, aEvent, aPhase, aAction, aCommand, aKeyCode,
aCharCode, aModifiers, aButton, aClickCount,
aGroup, aPreventDefault, aAllowUntrusted);
}
nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement)
: mHandlerElement(nsnull),
mLineNumber(0),
mNextHandler(nsnull),
mPrototypeBinding(nsnull)
{
- ++gRefCnt;
- if (gRefCnt == 1)
- // Get the primary accelerator key.
- InitAccessKeys();
+ Init();
// Make sure our prototype is initialized.
ConstructPrototype(aHandlerElement);
}
+nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding)
+ : mHandlerText(nsnull),
+ mLineNumber(nsnull),
+ mNextHandler(nsnull),
+ mPrototypeBinding(aBinding)
+{
+ Init();
+}
+
nsXBLPrototypeHandler::~nsXBLPrototypeHandler()
{
--gRefCnt;
if (mType & NS_HANDLER_TYPE_XUL) {
NS_IF_RELEASE(mHandlerElement);
} else if (mHandlerText) {
nsMemory::Free(mHandlerText);
}
@@ -1025,8 +1029,72 @@ nsXBLPrototypeHandler::ModifiersMatchMas
if (mKeyMask & cControlMask) {
key ? key->GetCtrlKey(&keyPresent) : mouse->GetCtrlKey(&keyPresent);
if (keyPresent != ((mKeyMask & cControl) != 0))
return false;
}
return true;
}
+
+nsresult
+nsXBLPrototypeHandler::Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream)
+{
+ nsresult rv = aStream->Read8(&mPhase);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Read8(&mKeyMask);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Read8(&mType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Read8(&mMisc);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRUint32 detail;
+ rv = aStream->Read32(&detail);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mDetail = detail;
+
+ nsAutoString name;
+ rv = aStream->ReadString(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mEventName = do_GetAtom(name);
+
+ rv = aStream->Read32(&mLineNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString handlerText;
+ rv = aStream->ReadString(handlerText);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!handlerText.IsEmpty())
+ mHandlerText = ToNewUnicode(handlerText);
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeHandler::Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream)
+{
+ // Make sure we don't write out NS_HANDLER_TYPE_XUL types, as they are used
+ // for <keyset> elements.
+ if (mType & NS_HANDLER_TYPE_XUL)
+ return NS_OK;
+
+ XBLBindingSerializeDetails type = XBLBinding_Serialize_Handler;
+
+ nsresult rv = aStream->Write8(type);
+ rv = aStream->Write8(mPhase);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write8(mKeyMask);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write8(mType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write8(mMisc);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write32(mDetail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->WriteWStringZ(nsDependentAtomString(mEventName).get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->Write32(mLineNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return aStream->WriteWStringZ(mHandlerText ? mHandlerText : EmptyString().get());
+}
--- a/content/xbl/src/nsXBLPrototypeHandler.h
+++ b/content/xbl/src/nsXBLPrototypeHandler.h
@@ -83,16 +83,19 @@ public:
const PRUnichar* aPreventDefault,
const PRUnichar* aAllowUntrusted,
nsXBLPrototypeBinding* aBinding,
PRUint32 aLineNumber);
// This constructor is used only by XUL key handlers (e.g., <key>)
nsXBLPrototypeHandler(nsIContent* aKeyElement);
+ // This constructor is used for handlers loaded from the cache
+ nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding);
+
~nsXBLPrototypeHandler();
// if aCharCode is not zero, it is used instead of the charCode of aKeyEvent.
bool KeyEventMatched(nsIDOMKeyEvent* aKeyEvent,
PRUint32 aCharCode = 0,
bool aIgnoreShiftKey = false);
inline bool KeyEventMatched(nsIAtom* aEventType,
nsIDOMKeyEvent* aEvent,
@@ -152,20 +155,30 @@ public:
// This returns a valid value only if HasAllowUntrustedEventsAttr returns
// true.
bool AllowUntrustedEvents()
{
return (mType & NS_HANDLER_ALLOW_UNTRUSTED) != 0;
}
+ nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream);
+ nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream);
+
public:
static PRUint32 gRefCnt;
protected:
+ void Init() {
+ ++gRefCnt;
+ if (gRefCnt == 1)
+ // Get the primary accelerator key.
+ InitAccessKeys();
+ }
+
already_AddRefed<nsIController> GetController(nsIDOMEventTarget* aTarget);
inline PRInt32 GetMatchingKeyCode(const nsAString& aKeyName);
void ConstructPrototype(nsIContent* aKeyElement,
const PRUnichar* aEvent=nsnull, const PRUnichar* aPhase=nsnull,
const PRUnichar* aAction=nsnull, const PRUnichar* aCommand=nsnull,
const PRUnichar* aKeyCode=nsnull, const PRUnichar* aCharCode=nsnull,
const PRUnichar* aModifiers=nsnull, const PRUnichar* aButton=nsnull,
--- a/content/xbl/src/nsXBLPrototypeResources.cpp
+++ b/content/xbl/src/nsXBLPrototypeResources.cpp
@@ -134,8 +134,16 @@ nsXBLPrototypeResources::FlushSkinSheets
mStyleSheetList.AppendElement(newSheet);
}
mRuleProcessor = new nsCSSRuleProcessor(mStyleSheetList,
nsStyleSet::eDocSheet);
return NS_OK;
}
+
+nsresult
+nsXBLPrototypeResources::Write(nsIObjectOutputStream* aStream)
+{
+ if (mLoader)
+ return mLoader->Write(aStream);
+ return NS_OK;
+}
--- a/content/xbl/src/nsXBLPrototypeResources.h
+++ b/content/xbl/src/nsXBLPrototypeResources.h
@@ -58,16 +58,18 @@ class nsCSSStyleSheet;
class nsXBLPrototypeResources
{
public:
void LoadResources(bool* aResult);
void AddResource(nsIAtom* aResourceType, const nsAString& aSrc);
void AddResourceListener(nsIContent* aElement);
nsresult FlushSkinSheets();
+ nsresult Write(nsIObjectOutputStream* aStream);
+
nsXBLPrototypeResources(nsXBLPrototypeBinding* aBinding);
~nsXBLPrototypeResources();
// MEMBER VARIABLES
nsXBLResourceLoader* mLoader; // A loader object. Exists only long enough to load resources, and then it dies.
typedef nsTArray<nsRefPtr<nsCSSStyleSheet> > sheet_array_type;
sheet_array_type mStyleSheetList; // A list of loaded stylesheets for this binding.
--- a/content/xbl/src/nsXBLResourceLoader.cpp
+++ b/content/xbl/src/nsXBLResourceLoader.cpp
@@ -285,8 +285,30 @@ nsXBLResourceLoader::NotifyBoundElements
}
// Clear out the whole array.
mBoundElements.Clear();
// Delete ourselves.
NS_RELEASE(mResources->mLoader);
}
+
+nsresult
+nsXBLResourceLoader::Write(nsIObjectOutputStream* aStream)
+{
+ nsresult rv;
+
+ for (nsXBLResource* curr = mResourceList; curr; curr = curr->mNext) {
+ if (curr->mType == nsGkAtoms::image)
+ rv = aStream->Write8(XBLBinding_Serialize_Image);
+ else if (curr->mType == nsGkAtoms::stylesheet)
+ rv = aStream->Write8(XBLBinding_Serialize_Stylesheet);
+ else
+ continue;
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->WriteWStringZ(curr->mSrc.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
--- a/content/xbl/src/nsXBLResourceLoader.h
+++ b/content/xbl/src/nsXBLResourceLoader.h
@@ -86,16 +86,18 @@ public:
void AddResourceListener(nsIContent* aElement);
nsXBLResourceLoader(nsXBLPrototypeBinding* aBinding,
nsXBLPrototypeResources* aResources);
virtual ~nsXBLResourceLoader();
void NotifyBoundElements();
+ nsresult Write(nsIObjectOutputStream* aStream);
+
// MEMBER VARIABLES
nsXBLPrototypeBinding* mBinding; // A pointer back to our binding.
nsXBLPrototypeResources* mResources; // A pointer back to our resources
// information. May be null if the
// resources have already been
// destroyed.
nsXBLResource* mResourceList; // The list of resources we need to load.
--- a/content/xbl/src/nsXBLSerialize.cpp
+++ b/content/xbl/src/nsXBLSerialize.cpp
@@ -38,19 +38,21 @@
#include "nsXBLSerialize.h"
#include "nsDOMScriptObjectHolder.h"
#include "nsContentUtils.h"
#include "jsxdrapi.h"
nsresult
XBL_SerializeFunction(nsIScriptContext* aContext,
nsIObjectOutputStream* aStream,
- JSObject* aFunctionObject)
+ JSObject* aFunctionObject,
+ PRUint32 aLineNumber)
{
- nsresult rv = NS_OK;
+ nsresult rv = aStream->Write32(aLineNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
JSContext* cx = (JSContext*) aContext->GetNativeContext();
JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
if (!xdr)
return NS_ERROR_OUT_OF_MEMORY;
xdr->userdata = (void*) aStream;
jsval funval = OBJECT_TO_JSVAL(aFunctionObject);
--- a/content/xbl/src/nsXBLSerialize.h
+++ b/content/xbl/src/nsXBLSerialize.h
@@ -38,21 +38,83 @@
#ifndef nsXBLSerialize_h__
#define nsXBLSerialize_h__
#include "jsapi.h"
#include "nsIScriptContext.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
+#include "nsINameSpaceManager.h"
+
+typedef PRUint8 XBLBindingSerializeDetails;
+
+// A version number to ensure we don't load cached data in a different
+// file format.
+#define XBLBinding_Serialize_Version 0x00000001
+
+// Set for the first binding in a document
+#define XBLBinding_Serialize_IsFirstBinding 1
+
+// Set to indicate that nsXBLPrototypeBinding::mInheritStyle should be true
+#define XBLBinding_Serialize_InheritStyle 2
+
+// Appears at the end of the serialized data to indicate that no more bindings
+// are present for this document.
+#define XBLBinding_Serialize_NoMoreBindings 0x80
+
+// Implementation member types. The serialized value for each member contains one
+// of these values, combined with the read-only flag XBLBinding_Serialize_ReadOnly.
+// Use XBLBinding_Serialize_Mask to filter out the read-only flag and check for
+// just the member type.
+#define XBLBinding_Serialize_NoMoreItems 0 // appears at the end of the members list
+#define XBLBinding_Serialize_Field 1
+#define XBLBinding_Serialize_GetterProperty 2
+#define XBLBinding_Serialize_SetterProperty 3
+#define XBLBinding_Serialize_GetterSetterProperty 4
+#define XBLBinding_Serialize_Method 5
+#define XBLBinding_Serialize_Constructor 6
+#define XBLBinding_Serialize_Destructor 7
+#define XBLBinding_Serialize_Handler 8
+#define XBLBinding_Serialize_Image 9
+#define XBLBinding_Serialize_Stylesheet 10
+#define XBLBinding_Serialize_Mask 0x0F
+#define XBLBinding_Serialize_ReadOnly 0x80
+
+// Appears at the end of the list of insertion points to indicate that there
+// are no more.
+#define XBLBinding_Serialize_NoMoreInsertionPoints 0xFFFFFFFF
+
+// When serializing content nodes, a single-byte namespace id is written out
+// first. The special values below can appear in place of a namespace id.
+
+// Indicates that this is not one of the built-in namespaces defined in
+// nsINameSpaceManager.h. The string form will be serialized immediately
+// following.
+#define XBLBinding_Serialize_CustomNamespace 0xFE
+
+// Flags to indicate a non-element node. Otherwise, it is an element.
+#define XBLBinding_Serialize_TextNode 0xFB
+#define XBLBinding_Serialize_CDATANode 0xFC
+#define XBLBinding_Serialize_CommentNode 0xFD
+
+// Indicates that there is no content to serialize/deserialize
+#define XBLBinding_Serialize_NoContent 0xFF
+
+// Appears at the end of the forwarded attributes list to indicate that there
+// are no more attributes.
+#define XBLBinding_Serialize_NoMoreAttributes 0xFF
+
+PR_STATIC_ASSERT(XBLBinding_Serialize_CustomNamespace >= kNameSpaceID_LastBuiltin);
nsresult
XBL_SerializeFunction(nsIScriptContext* aContext,
nsIObjectOutputStream* aStream,
- JSObject* aFunctionObject);
+ JSObject* aFunctionObject,
+ PRUint32 aLineNumber);
nsresult
XBL_DeserializeFunction(nsIScriptContext* aContext,
nsIObjectInputStream* aStream,
void* aHolder,
PRUint32* aLineNumber,
void **aScriptObject);
--- a/content/xbl/src/nsXBLService.cpp
+++ b/content/xbl/src/nsXBLService.cpp
@@ -80,16 +80,17 @@
#include "nsContentErrors.h"
#include "nsIPresShell.h"
#include "nsIDocumentObserver.h"
#include "nsFrameManager.h"
#include "nsStyleContext.h"
#include "nsIScriptSecurityManager.h"
#include "nsIScriptError.h"
+#include "nsXBLSerialize.h"
#ifdef MOZ_XUL
#include "nsXULPrototypeCache.h"
#endif
#include "nsIDOMEventListener.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Element.h"
@@ -835,19 +836,16 @@ nsXBLService::GetBinding(nsIContent* aBo
nsresult rv = LoadBindingDocumentInfo(aBoundElement, boundDocument, aURI,
aOriginPrincipal,
false, getter_AddRefs(docInfo));
NS_ENSURE_SUCCESS(rv, rv);
if (!docInfo)
return NS_ERROR_FAILURE;
- // Get our doc info and determine our script access.
- nsCOMPtr<nsIDocument> doc = docInfo->GetDocument();
-
nsXBLPrototypeBinding* protoBinding = docInfo->GetPrototypeBinding(ref);
NS_WARN_IF_FALSE(protoBinding, "Unable to locate an XBL binding");
if (!protoBinding)
return NS_ERROR_FAILURE;
NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(protoBinding->BindingURI()),
NS_ERROR_OUT_OF_MEMORY);
@@ -1054,43 +1052,62 @@ nsXBLService::LoadBindingDocumentInfo(ns
// Create a new load observer.
if (!xblListener->HasRequest(aBindingURI, aBoundElement)) {
nsXBLBindingRequest* req = nsXBLBindingRequest::Create(mPool, aBindingURI, aBoundElement);
xblListener->AddRequest(req);
}
return NS_OK;
}
}
-
+
+#ifdef MOZ_XUL
+ // Next, look in the startup cache
+ bool useStartupCache = useXULCache && IsChromeOrResourceURI(documentURI);
+ if (!info && useStartupCache) {
+ rv = nsXBLDocumentInfo::ReadPrototypeBindings(documentURI, getter_AddRefs(info));
+ if (NS_SUCCEEDED(rv)) {
+ cache->PutXBLDocumentInfo(info);
+
+ if (bindingManager) {
+ // Cache it in our binding manager's document table.
+ bindingManager->PutXBLDocumentInfo(info);
+ }
+ }
+ }
+#endif
+
if (!info) {
// Finally, if all lines of defense fail, we go and fetch the binding
// document.
// Always load chrome synchronously
bool chrome;
if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
aForceSyncLoad = true;
nsCOMPtr<nsIDocument> document;
FetchBindingDocument(aBoundElement, aBoundDocument, documentURI,
aBindingURI, aForceSyncLoad, getter_AddRefs(document));
-
+
if (document) {
nsBindingManager *xblDocBindingManager = document->BindingManager();
info = xblDocBindingManager->GetXBLDocumentInfo(documentURI);
if (!info) {
NS_ERROR("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
return NS_ERROR_FAILURE;
}
xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
// If the doc is a chrome URI, then we put it into the XUL cache.
#ifdef MOZ_XUL
- if (useXULCache && IsChromeOrResourceURI(documentURI)) {
+ if (useStartupCache) {
cache->PutXBLDocumentInfo(info);
+
+ // now write the bindings into the startup cache
+ info->WritePrototypeBindings();
}
#endif
if (bindingManager) {
// Also put it in our binding manager's document table.
bindingManager->PutXBLDocumentInfo(info);
}
}
--- a/content/xml/document/src/nsXMLDocument.cpp
+++ b/content/xml/document/src/nsXMLDocument.cpp
@@ -214,16 +214,37 @@ NS_NewXMLDocument(nsIDocument** aInstanc
NS_RELEASE(doc);
}
*aInstancePtrResult = doc;
return rv;
}
+nsresult
+NS_NewXBLDocument(nsIDOMDocument** aInstancePtrResult,
+ nsIURI* aDocumentURI,
+ nsIURI* aBaseURI,
+ nsIPrincipal* aPrincipal)
+{
+ nsresult rv = NS_NewDOMDocument(aInstancePtrResult,
+ NS_LITERAL_STRING("http://www.mozilla.org/xbl"),
+ NS_LITERAL_STRING("bindings"), nsnull,
+ aDocumentURI, aBaseURI, aPrincipal, false,
+ nsnull, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> idoc = do_QueryInterface(*aInstancePtrResult);
+ nsDocument* doc = static_cast<nsDocument*>(idoc.get());
+ doc->SetLoadedAsInteractiveData(true);
+ doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
+
+ return NS_OK;
+}
+
// NOTE! nsDocument::operator new() zeroes out all members, so don't
// bother initializing members to 0.
nsXMLDocument::nsXMLDocument(const char* aContentType)
: nsDocument(aContentType),
mAsync(true)
{
--- a/startupcache/StartupCacheUtils.h
+++ b/startupcache/StartupCacheUtils.h
@@ -55,16 +55,19 @@ NewObjectInputStreamFromBuffer(char* buf
// non-singleton objects are written out multiple times during a serialization.
// This could cause them to be deserialized incorrectly (as multiple copies
// instead of references).
NS_EXPORT nsresult
NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
nsIStorageStream** stream,
bool wantDebugStream);
+// Creates a buffer for storing the stream into the cache. The buffer is
+// allocated with 'new []'. Typically, the caller would store the buffer in
+// an nsAutoArrayPtr<char> and then call nsIStartupCache::PutBuffer with it.
NS_EXPORT nsresult
NewBufferFromStorageStream(nsIStorageStream *storageStream,
char** buffer, PRUint32* len);
NS_EXPORT nsresult
PathifyURI(nsIURI *in, nsACString &out);
}
}