Bug 94199, part 4, serialize/deserialize xbl to startup cache, r=bz
authorNeil Deakin <neil@mozilla.com>
Thu, 03 Nov 2011 16:39:08 -0400
changeset 79721 b46ffd95bfd836109a7d9b8b504be14b2206271f
parent 79720 c38c653aed70eb1580d3981ef1c379fed6e2eeef
child 79722 797d8083efcac52490a8182017eea6ea52e528e7
push idunknown
push userunknown
push dateunknown
reviewersbz
bugs94199
milestone10.0a1
Bug 94199, part 4, serialize/deserialize xbl to startup cache, r=bz
content/base/public/nsIDocument.h
content/base/public/nsINameSpaceManager.h
content/base/src/nsDocument.h
content/xbl/src/nsXBLContentSink.cpp
content/xbl/src/nsXBLDocumentInfo.cpp
content/xbl/src/nsXBLDocumentInfo.h
content/xbl/src/nsXBLProtoImpl.cpp
content/xbl/src/nsXBLProtoImpl.h
content/xbl/src/nsXBLProtoImplField.cpp
content/xbl/src/nsXBLProtoImplField.h
content/xbl/src/nsXBLProtoImplMember.h
content/xbl/src/nsXBLProtoImplMethod.cpp
content/xbl/src/nsXBLProtoImplMethod.h
content/xbl/src/nsXBLProtoImplProperty.cpp
content/xbl/src/nsXBLProtoImplProperty.h
content/xbl/src/nsXBLPrototypeBinding.cpp
content/xbl/src/nsXBLPrototypeBinding.h
content/xbl/src/nsXBLPrototypeHandler.cpp
content/xbl/src/nsXBLPrototypeHandler.h
content/xbl/src/nsXBLPrototypeResources.cpp
content/xbl/src/nsXBLPrototypeResources.h
content/xbl/src/nsXBLResourceLoader.cpp
content/xbl/src/nsXBLResourceLoader.h
content/xbl/src/nsXBLSerialize.cpp
content/xbl/src/nsXBLSerialize.h
content/xbl/src/nsXBLService.cpp
content/xml/document/src/nsXMLDocument.cpp
startupcache/StartupCacheUtils.h
--- 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);
 }
 }