Make the nsISerializable implementation of nsPrincipal actually work. This makes it possible to save principal objects to a stream and read them back. Bug 369566, r=dveditz+brendan, sr=jst, a=jst
authorbzbarsky@mit.edu
Mon, 17 Sep 2007 15:18:28 -0700
changeset 6004 5c4a24b11b871c519a72c4378f4225c886444cd8
parent 6003 21a52cfb4a7942d9d4b020f9b8c84a220160c5ec
child 6005 3259f85aa0ab0e97a992acfe437d9e73c0137d91
push idunknown
push userunknown
push dateunknown
reviewersdveditz, jst, jst
bugs369566
milestone1.9a8pre
Make the nsISerializable implementation of nsPrincipal actually work. This makes it possible to save principal objects to a stream and read them back. Bug 369566, r=dveditz+brendan, sr=jst, a=jst
caps/include/nsPrincipal.h
caps/src/nsPrincipal.cpp
content/xul/document/public/nsIXULPrototypeCache.h
--- a/caps/include/nsPrincipal.h
+++ b/caps/include/nsPrincipal.h
@@ -40,16 +40,18 @@
 #ifndef nsPrincipal_h__
 #define nsPrincipal_h__
 
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsVoidArray.h"
 #include "nsHashtable.h"
 #include "nsJSPrincipals.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
 
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
 
 class nsPrincipal : public nsIPrincipal
 {
 public:
   nsPrincipal();
@@ -95,17 +97,17 @@ public:
   void SetURI(nsIURI *aURI);
   nsresult SetCapability(const char *capability, void **annotation, 
                          AnnotationValue value);
 
   static const char sInvalid[];
 
 protected:
   nsJSPrincipals mJSPrincipals;
-  nsVoidArray mAnnotations;
+  nsTArray< nsAutoPtr<nsHashtable> > mAnnotations;
   nsHashtable mCapabilities;
   nsCString mPrefName;
   static PRInt32 sCapabilitiesOrdinal;
 
   // XXXcaa This is a semi-hack.  The best solution here is to keep
   // a reference to an interface here, except there is no interface
   // that we can use yet.
   struct Certificate
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -145,28 +145,18 @@ nsPrincipal::Init(const nsACString& aCer
     }
   }
 
   NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrincipal::Init() failed");
 
   return rv;
 }
 
-
-PR_STATIC_CALLBACK(PRBool)
-deleteElement(void* aElement, void *aData)
-{
-  nsHashtable *ht = (nsHashtable *) aElement;
-  delete ht;
-  return PR_TRUE;
-}
-
 nsPrincipal::~nsPrincipal(void)
 {
-  mAnnotations.EnumerateForwards(deleteElement, nsnull);
   SetSecurityPolicy(nsnull); 
 }
 
 NS_IMETHODIMP
 nsPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin)
 {
   NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitialized!");
 
@@ -481,33 +471,39 @@ nsPrincipal::RevertCapability(const char
   return NS_OK;
 }
 
 nsresult
 nsPrincipal::SetCapability(const char *capability, void **annotation,
                            AnnotationValue value)
 {
   if (*annotation == nsnull) {
-    *annotation = new nsHashtable(5);
-    if (!*annotation) {
+    nsHashtable* ht = new nsHashtable(5);
+
+    if (!ht) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
 
     // This object owns its annotations. Save them so we can release
     // them when we destroy this object.
-    mAnnotations.AppendElement(*annotation);
+    if (!mAnnotations.AppendElement(ht)) {
+      delete ht;
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    *annotation = ht;
   }
 
   const char *start = capability;
   for(;;) {
     const char *space = PL_strchr(start, ' ');
     int len = space ? space - start : strlen(start);
     nsCAutoString capString(start, len);
     nsCStringKey key(capString);
-    nsHashtable *ht = (nsHashtable *) *annotation;
+    nsHashtable *ht = static_cast<nsHashtable *>(*annotation);
     ht->Put(&key, (void *) value);
     if (!space) {
       break;
     }
 
     start = space + 1;
   }
 
@@ -668,17 +664,17 @@ nsPrincipal::InitFromPersistent(const ch
                                 const char* aGrantedList,
                                 const char* aDeniedList,
                                 nsISupports* aCert,
                                 PRBool aIsCert,
                                 PRBool aTrusted)
 {
   NS_PRECONDITION(mCapabilities.Count() == 0,
                   "mCapabilities was already initialized?");
-  NS_PRECONDITION(mAnnotations.Count() == 0,
+  NS_PRECONDITION(mAnnotations.Length() == 0,
                   "mAnnotations was already initialized?");
   NS_PRECONDITION(!mInitialized, "We were already initialized?");
 
   mInitialized = PR_TRUE;
 
   nsresult rv;
   if (aIsCert) {
     rv = SetCertificate(aToken, aSubjectName, aPrettyName, aCert);
@@ -906,97 +902,182 @@ FreeAnnotationEntry(nsIObjectInputStream
                     void* aData)
 {
   delete aKey;
 }
 
 NS_IMETHODIMP
 nsPrincipal::Read(nsIObjectInputStream* aStream)
 {
-  PRUint32 annotationCount;
-  nsresult rv = aStream->Read32(&annotationCount);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  for (PRInt32 i = 0, n = PRInt32(annotationCount); i < n; i++) {
-    nsHashtable *ht = new nsHashtable(aStream,
-                                      ReadAnnotationEntry,
-                                      FreeAnnotationEntry,
-                                      &rv);
-    if (!ht) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    if (NS_FAILED(rv)) {
-      delete ht;
-      return rv;
-    }
-
-    if (!mAnnotations.InsertElementAt(reinterpret_cast<void*>(ht), i)) {
-      delete ht;
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
   PRBool hasCapabilities;
-  rv = aStream->ReadBoolean(&hasCapabilities);
+  nsresult rv = aStream->ReadBoolean(&hasCapabilities);
   if (NS_SUCCEEDED(rv) && hasCapabilities) {
-    mCapabilities = nsHashtable(aStream,
-                                ReadAnnotationEntry,
-                                FreeAnnotationEntry,
-                                &rv);
+    // We want to use one of the nsHashtable constructors, but don't want to
+    // generally have mCapabilities be a pointer... and nsHashtable has no
+    // reasonable copy-constructor.  Placement-new to the rescue!
+    mCapabilities.~nsHashtable();
+    new (&mCapabilities) nsHashtable(aStream, ReadAnnotationEntry,
+                                     FreeAnnotationEntry, &rv);
   }
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   rv = NS_ReadOptionalCString(aStream, mPrefName);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  const char* ordinalBegin = PL_strpbrk(mPrefName.get(), "1234567890");
+  if (ordinalBegin) {
+    PRIntn n = atoi(ordinalBegin);
+    if (sCapabilitiesOrdinal <= n) {
+      sCapabilitiesOrdinal = n + 1;
+    }
+  }
+
+  PRBool haveCert;
+  rv = aStream->ReadBoolean(&haveCert);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCString fingerprint;
+  nsCString subjectName;
+  nsCString prettyName;
+  nsCOMPtr<nsISupports> cert;
+  if (haveCert) {
+    rv = NS_ReadOptionalCString(aStream, fingerprint);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    rv = NS_ReadOptionalCString(aStream, subjectName);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    rv = NS_ReadOptionalCString(aStream, prettyName);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    rv = aStream->ReadObject(PR_TRUE, getter_AddRefs(cert));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  nsCOMPtr<nsIURI> codebase;
+  rv = NS_ReadOptionalObject(aStream, PR_TRUE, getter_AddRefs(codebase));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = Init(fingerprint, subjectName, prettyName, cert, codebase);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> domain;
+  rv = NS_ReadOptionalObject(aStream, PR_TRUE, getter_AddRefs(domain));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  SetDomain(domain);
+
+  rv = aStream->Read8(&mTrusted);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   return NS_OK;
 }
 
 PR_STATIC_CALLBACK(nsresult)
 WriteScalarValue(nsIObjectOutputStream* aStream, void* aData)
 {
   PRUint32 value = NS_PTR_TO_INT32(aData);
 
   return aStream->Write32(value);
 }
 
 NS_IMETHODIMP
 nsPrincipal::Write(nsIObjectOutputStream* aStream)
 {
-  PRUint32 annotationCount = PRUint32(mAnnotations.Count());
-  nsresult rv = aStream->Write32(annotationCount);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  for (PRInt32 i = 0, n = PRInt32(annotationCount); i < n; i++) {
-    nsHashtable *ht = reinterpret_cast<nsHashtable *>(mAnnotations[i]);
-    rv = ht->Write(aStream, WriteScalarValue);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  }
-
+  NS_ENSURE_STATE(mCert || mCodebase);
+  
+  // mAnnotations is transient data associated to specific JS stack frames.  We
+  // don't want to serialize that.
+  
   PRBool hasCapabilities = (mCapabilities.Count() > 0);
-  rv = aStream->WriteBoolean(hasCapabilities);
+  nsresult rv = aStream->WriteBoolean(hasCapabilities);
   if (NS_SUCCEEDED(rv) && hasCapabilities) {
     rv = mCapabilities.Write(aStream, WriteScalarValue);
   }
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   rv = NS_WriteOptionalStringZ(aStream, mPrefName.get());
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  rv = aStream->WriteBoolean(mCert != nsnull);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (mCert) {
+    NS_ENSURE_STATE(mCert->cert);
+    
+    rv = NS_WriteOptionalStringZ(aStream, mCert->fingerprint.get());
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    
+    rv = NS_WriteOptionalStringZ(aStream, mCert->subjectName.get());
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    
+    rv = NS_WriteOptionalStringZ(aStream, mCert->prettyName.get());
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    rv = aStream->WriteCompoundObject(mCert->cert, NS_GET_IID(nsISupports),
+                                      PR_TRUE);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }    
+  }
+  
+  // mSecurityPolicy is an optimization; it'll get looked up again as needed.
+  // Don't bother saving and restoring it, esp. since it might change if
+  // preferences change.
+
+  rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI),
+                                      PR_TRUE);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = NS_WriteOptionalCompoundObject(aStream, mDomain, NS_GET_IID(nsIURI),
+                                      PR_TRUE);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // mOrigin is an optimization; don't bother serializing it.
+
+  rv = aStream->Write8(mTrusted);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // mCodebaseImmutable and mDomainImmutable will be recomputed based
+  // on the deserialized URIs in Read().
+
   return NS_OK;
 }
--- a/content/xul/document/public/nsIXULPrototypeCache.h
+++ b/content/xul/document/public/nsIXULPrototypeCache.h
@@ -77,15 +77,15 @@ NS_NewXULPrototypeCache(nsISupports* aOu
 
 
 const char XUL_FASTLOAD_FILE_BASENAME[] = "XUL";
 
 // Increase the subtractor when changing version, say when changing the
 // (opaque to XPCOM FastLoad code) format of XUL-specific XDR serializations.
 // See also JSXDR_BYTECODE_VERSION in jsxdrapi.h, which tracks incompatible JS
 // bytecode version changes.
-#define XUL_FASTLOAD_FILE_VERSION       (0xfeedbeef - 22)
+#define XUL_FASTLOAD_FILE_VERSION       (0xfeedbeef - 23)
 
 #define XUL_SERIALIZATION_BUFFER_SIZE   (64 * 1024)
 #define XUL_DESERIALIZATION_BUFFER_SIZE (8 * 1024)
 
 
 #endif // nsIXULPrototypeCache_h__