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 79700 b46ffd95bfd836109a7d9b8b504be14b2206271f
parent 79699 c38c653aed70eb1580d3981ef1c379fed6e2eeef
child 79701 797d8083efcac52490a8182017eea6ea52e528e7
push id3103
push userneil@mozilla.com
push dateThu, 03 Nov 2011 20:41:05 +0000
treeherdermozilla-inbound@797d8083efca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs94199
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);
 }
 }