Bug 592943 - (3/3) switch nsXULPrototypeCache to use startupCache instead of fastload, Patch by Benedict Hsieh, r=jst
☠☠ backed out by 20a9cf3dc3a7 ☠ ☠
authorMichael Wu <mwu@mozilla.com>
Thu, 12 Aug 2010 12:42:36 -0700
changeset 70532 eaa69ae330abb50bb8d68971a29d070228826dba
parent 70531 02a5505b965b143a1067a633889c5d1f70c97c06
child 70533 a3b5d768fa9615c283ac5765c031fe55cbc2786f
child 70543 20a9cf3dc3a7033ef19c3cd5154ceab42c4713f1
child 70703 b74bba0fa695af8fdb13f520d97dd5f61a1bf833
push id209
push userbzbarsky@mozilla.com
push dateTue, 05 Jul 2011 17:42:16 +0000
treeherdermozilla-aurora@cc6e30cce8af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst
bugs592943
milestone7.0a1
Bug 592943 - (3/3) switch nsXULPrototypeCache to use startupCache instead of fastload, Patch by Benedict Hsieh, r=jst
chrome/src/nsChromeProtocolHandler.cpp
content/xul/content/src/nsXULElement.cpp
content/xul/document/public/nsIXULPrototypeCache.h
content/xul/document/src/nsXULDocument.cpp
content/xul/document/src/nsXULPrototypeCache.cpp
content/xul/document/src/nsXULPrototypeCache.h
content/xul/document/src/nsXULPrototypeDocument.cpp
js/src/xpconnect/loader/mozJSComponentLoader.cpp
startupcache/StartupCacheUtils.cpp
startupcache/StartupCacheUtils.h
startupcache/nsStartupCacheUtils.cpp
--- a/chrome/src/nsChromeProtocolHandler.cpp
+++ b/chrome/src/nsChromeProtocolHandler.cpp
@@ -240,54 +240,20 @@ nsChromeProtocolHandler::NewChannel(nsIU
         nsCOMPtr<nsIPrincipal> principal;
         rv = securityManager->GetSystemPrincipal(getter_AddRefs(principal));
         if (NS_FAILED(rv)) return rv;
 
         nsCOMPtr<nsISupports> owner = do_QueryInterface(principal);
         result->SetOwner(owner);
     }
 
-#ifdef MOZ_XUL
-    // Track FastLoad file dependencies.
-    //
-    // This is harder than it ought to be!  While an nsResChannel "is-a"
-    // nsIFileChannel, an nsJARChannel is not.  Once you unravel the jar:
-    // URI, you may have a resource: URL -- but without a channel for it,
-    // you can't get the URI that it yields through substitution!
-    //
-    // XXXbe fix nsResChannel.cpp to move the substitution code into a new
-    //       nsResURL class?
-    nsCOMPtr<nsIFastLoadService> fastLoadServ(do_GetFastLoadService());
-    if (fastLoadServ) {
-        nsCOMPtr<nsIObjectOutputStream> objectOutput;
-        fastLoadServ->GetOutputStream(getter_AddRefs(objectOutput));
-        if (objectOutput) {
-            nsCOMPtr<nsIFile> file;
-
-            nsCOMPtr<nsIURI> uri;
-            result->GetURI(getter_AddRefs(uri));
-            uri = NS_GetInnermostURI(uri);
-
-            // Here we have a URL of the form resource:/chrome/A.jar
-            // or file:/some/path/to/A.jar.
-            nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(uri));
-            if (fileURL)
-                fileURL->GetFile(getter_AddRefs(file));
-
-            if (file) {
-                rv = fastLoadServ->AddDependency(file);
-                if (NS_FAILED(rv)) {
-                   nsCOMPtr<nsIXULPrototypeCache> cache
-                       (do_GetService(kXULPrototypeCacheCID));
-                   if (cache)
-                       cache->AbortFastLoads();
-                }
-            }
-        }
-    }
-#endif
+    // XXX Removed dependency-tracking code from here, because we're not
+    // tracking them anyways (with fastload we checked only in DEBUG
+    // and with startupcache not at all), but this is where we would start
+    // if we need to re-add.
+    // See bug 531886, bug 533038.
 
     *aResult = result;
     NS_ADDREF(*aResult);
     return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -79,17 +79,16 @@
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDOMElementCSSInlineStyle.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDocument.h"
 #include "nsIEventListenerManager.h"
 #include "nsEventStateManager.h"
 #include "nsFocusManager.h"
-#include "nsIFastLoadService.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsINameSpaceManager.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIRDFCompositeDataSource.h"
 #include "nsIRDFNode.h"
@@ -2683,19 +2682,19 @@ nsXULPrototypeElement::Serialize(nsIObje
                 rv |= script->Serialize(aStream, aGlobal, aNodeInfos);
             } else {
                 rv |= aStream->WriteCompoundObject(script->mSrcURI,
                                                    NS_GET_IID(nsIURI),
                                                    PR_TRUE);
 
                 if (script->mScriptObject.mObject) {
                     // This may return NS_OK without muxing script->mSrcURI's
-                    // data into the FastLoad file, in the case where that
+                    // data into the cache file, in the case where that
                     // muxed document is already there (written by a prior
-                    // session, or by an earlier FastLoad episode during this
+                    // session, or by an earlier cache episode during this
                     // session).
                     rv |= script->SerializeOutOfLine(aStream, aGlobal);
                 }
             }
             break;
         }
     }
 
@@ -2815,18 +2814,18 @@ nsXULPrototypeElement::Deserialize(nsIOb
                 NS_NOTREACHED("Unexpected child type!");
                 rv = NS_ERROR_UNEXPECTED;
             }
 
             mChildren.AppendElement(child);
 
             // Oh dear. Something failed during the deserialization.
             // We don't know what.  But likely consequences of failed
-            // deserializations included calls to |AbortFastLoads| which
-            // shuts down the FastLoadService and closes our streams.
+            // deserializations included calls to |AbortCaching| which
+            // shuts down the cache and closes our streams.
             // If that happens, next time through this loop, we die a messy
             // death. So, let's just fail now, and propagate that failure
             // upward so that the ChromeProtocolHandler knows it can't use
             // a cached chrome channel for this.
             if (NS_FAILED(rv))
                 return rv;
         }
     }
@@ -2956,70 +2955,50 @@ nsXULPrototypeScript::Serialize(nsIObjec
 
     return NS_OK;
 }
 
 nsresult
 nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
                                          nsIScriptGlobalObject* aGlobal)
 {
+    nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
+
+    PRBool isChrome = PR_FALSE;
+    if (NS_FAILED(mSrcURI->SchemeIs("chrome", &isChrome)) || !isChrome)
+       // Don't cache scripts that don't come from chrome uris.
+       return rv;
+
     nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
     if (!cache)
         return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ASSERTION(cache->IsEnabled(),
-                 "writing to the FastLoad file, but the XUL cache is off?");
-
-    nsIFastLoadService* fastLoadService = cache->GetFastLoadService();
-    if (!fastLoadService)
-        return NS_ERROR_NOT_AVAILABLE;
-
-    nsCAutoString urispec;
-    nsresult rv = mSrcURI->GetAsciiSpec(urispec);
-    if (NS_FAILED(rv))
-        return rv;
-
-    PRBool exists = PR_FALSE;
-    fastLoadService->HasMuxedDocument(urispec.get(), &exists);
+                 "writing to the cache file, but the XUL cache is off?");
+    PRBool exists;
+    cache->HasData(mSrcURI, &exists);
+    
+    
     /* return will be NS_OK from GetAsciiSpec.
      * that makes no sense.
      * nor does returning NS_OK from HasMuxedDocument.
      * XXX return something meaningful.
      */
     if (exists)
         return NS_OK;
 
-    // Allow callers to pass null for aStream, meaning
-    // "use the FastLoad service's default output stream."
-    // See nsXULDocument.cpp for one use of this.
-    nsCOMPtr<nsIObjectOutputStream> objectOutput = aStream;
-    if (! objectOutput) {
-        fastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
-        if (! objectOutput)
-            return NS_ERROR_NOT_AVAILABLE;
-    }
-
-    rv = fastLoadService->
-         StartMuxedDocument(mSrcURI, urispec.get(),
-                            nsIFastLoadService::NS_FASTLOAD_WRITE);
-    NS_ASSERTION(rv != NS_ERROR_NOT_AVAILABLE, "reading FastLoad?!");
-
-    nsCOMPtr<nsIURI> oldURI;
-    rv |= fastLoadService->SelectMuxedDocument(mSrcURI, getter_AddRefs(oldURI));
-    rv |= Serialize(objectOutput, aGlobal, nsnull);
-    rv |= fastLoadService->EndMuxedDocument(mSrcURI);
-
-    if (oldURI) {
-        nsCOMPtr<nsIURI> tempURI;
-        rv |= fastLoadService->
-              SelectMuxedDocument(oldURI, getter_AddRefs(tempURI));
-    }
+    nsCOMPtr<nsIObjectOutputStream> oos;
+    rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos));
+    NS_ENSURE_SUCCESS(rv, rv);
+    
+    rv |= Serialize(oos, aGlobal, nsnull);
+    rv |= cache->FinishOutputStream(mSrcURI);
 
     if (NS_FAILED(rv))
-        cache->AbortFastLoads();
+        cache->AbortCaching();
     return rv;
 }
 
 
 nsresult
 nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
                                   nsIScriptGlobalObject* aGlobal,
                                   nsIURI* aDocumentURI,
@@ -3050,43 +3029,34 @@ nsXULPrototypeScript::Deserialize(nsIObj
     return NS_OK;
 }
 
 
 nsresult
 nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
                                            nsIScriptGlobalObject* aGlobal)
 {
-    // Keep track of FastLoad failure via rv, so we can
-    // AbortFastLoads if things look bad.
+    // Keep track of failure via rv, so we can
+    // AbortCaching if things look bad.
     nsresult rv = NS_OK;
-
     nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
-    nsIFastLoadService* fastLoadService = cache->GetFastLoadService();
-
-    // Allow callers to pass null for aInput, meaning
-    // "use the FastLoad service's default input stream."
-    // See nsXULContentSink.cpp for one use of this.
+  
     nsCOMPtr<nsIObjectInputStream> objectInput = aInput;
-    if (! objectInput && fastLoadService)
-        fastLoadService->GetInputStream(getter_AddRefs(objectInput));
-
-    if (objectInput) {
+    if (cache) {
         PRBool useXULCache = PR_TRUE;
         if (mSrcURI) {
             // NB: we must check the XUL script cache early, to avoid
-            // multiple deserialization attempts for a given script, which
-            // would exhaust the multiplexed stream containing the singly
-            // serialized script.  Note that nsXULDocument::LoadScript
+            // multiple deserialization attempts for a given script.            
+            // Note that nsXULDocument::LoadScript
             // checks the XUL script cache too, in order to handle the
             // serialization case.
             //
             // We need do this only for <script src='strres.js'> and the
             // like, i.e., out-of-line scripts that are included by several
-            // different XUL documents multiplexed in the FastLoad file.
+            // different XUL documents stored in the cache file.
             useXULCache = cache->IsEnabled();
 
             if (useXULCache) {
                 PRUint32 newLangID = nsIProgrammingLanguage::UNKNOWN;
                 void *newScriptObject = cache->GetScript(mSrcURI, &newLangID);
                 if (newScriptObject) {
                     // Things may blow here if we simply change the script
                     // language - other code may already have pre-fetched the
@@ -3100,78 +3070,52 @@ nsXULPrototypeScript::DeserializeOutOfLi
                         return NS_ERROR_UNEXPECTED;
                     }
                     Set(newScriptObject);
                 }
             }
         }
 
         if (! mScriptObject.mObject) {
-            nsCOMPtr<nsIURI> oldURI;
-
             if (mSrcURI) {
-                nsCAutoString spec;
-                mSrcURI->GetAsciiSpec(spec);
-                rv = fastLoadService->StartMuxedDocument(mSrcURI, spec.get(),
-                                                         nsIFastLoadService::NS_FASTLOAD_READ);
-                if (NS_SUCCEEDED(rv))
-                    rv = fastLoadService->SelectMuxedDocument(mSrcURI, getter_AddRefs(oldURI));
-            } else {
-                // An inline script: check FastLoad multiplexing direction
-                // and skip Deserialize if we're not reading from a
-                // muxed stream to get inline objects that are contained in
-                // the current document.
-                PRInt32 direction;
-                fastLoadService->GetDirection(&direction);
-                if (direction != nsIFastLoadService::NS_FASTLOAD_READ)
-                    rv = NS_ERROR_NOT_AVAILABLE;
-            }
-
+                rv = cache->GetInputStream(mSrcURI, getter_AddRefs(objectInput));
+            } 
+            // If !mSrcURI, we have an inline script. We shouldn't have 
+            // to do anything else in that case, I think.
+ 
             // We do reflect errors into rv, but our caller may want to
             // ignore our return value, because mScriptObject will be null
             // after any error, and that suffices to cause the script to
             // be reloaded (from the src= URI, if any) and recompiled.
             // We're better off slow-loading than bailing out due to a
-            // FastLoad error.
+            // error.
             if (NS_SUCCEEDED(rv))
                 rv = Deserialize(objectInput, aGlobal, nsnull, nsnull);
 
-            if (NS_SUCCEEDED(rv) && mSrcURI) {
-                rv = fastLoadService->EndMuxedDocument(mSrcURI);
-
-                if (NS_SUCCEEDED(rv) && oldURI) {
-                    nsCOMPtr<nsIURI> tempURI;
-                    rv = fastLoadService->SelectMuxedDocument(oldURI, getter_AddRefs(tempURI));
-
-                    NS_ASSERTION(NS_SUCCEEDED(rv) && (!tempURI || tempURI == mSrcURI),
-                                 "not currently deserializing into the script we thought we were!");
-                }
-            }
-
             if (NS_SUCCEEDED(rv)) {
                 if (useXULCache && mSrcURI) {
                     PRBool isChrome = PR_FALSE;
                     mSrcURI->SchemeIs("chrome", &isChrome);
                     if (isChrome) {
                         cache->PutScript(mSrcURI,
                                          mScriptObject.mLangID,
                                          mScriptObject.mObject);
                     }
                 }
+                cache->FinishInputStream(mSrcURI);
             } else {
-                // If mSrcURI is not in the FastLoad multiplex,
+                // If mSrcURI is not in the cache,
                 // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
-                // update the FastLoad file to hold a serialization of
+                // update the cache file to hold a serialization of
                 // this script, once it has finished loading.
                 if (rv != NS_ERROR_NOT_AVAILABLE)
-                    cache->AbortFastLoads();
+                    cache->AbortCaching();
             }
         }
     }
-
     return rv;
 }
 
 nsresult
 nsXULPrototypeScript::Compile(const PRUnichar* aText,
                               PRInt32 aTextLength,
                               nsIURI* aURI,
                               PRUint32 aLineNo,
--- a/content/xul/document/public/nsIXULPrototypeCache.h
+++ b/content/xul/document/public/nsIXULPrototypeCache.h
@@ -60,19 +60,19 @@ public:
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXULPROTOTYPECACHE_IID)
 
     /**
      * Whether the XUL document at the specified URI is in the cache.
      */
     virtual PRBool IsCached(nsIURI* aURI) = 0;
 
     /**
-     * Stop the FastLoad process abruptly, removing the FastLoad file.
+     * Stop the caching process abruptly, removing the cache file.
      */
-    virtual void AbortFastLoads() = 0;
+    virtual void AbortCaching() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULPrototypeCache, NS_IXULPROTOTYPECACHE_IID)
 
 nsresult
 NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult);
 
 
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -284,22 +284,21 @@ nsXULDocument::~nsXULDocument()
 
     if (--gRefCnt == 0) {
         NS_IF_RELEASE(gRDFService);
 
         NS_IF_RELEASE(kNC_persist);
         NS_IF_RELEASE(kNC_attribute);
         NS_IF_RELEASE(kNC_value);
 
-        // Remove the current document here from the FastLoad table in
+        // Remove the current document here from the table in
         // case the document did not make it past StartLayout in
-        // ResumeWalk. The FastLoad table must be clear of entries so
-        // that the FastLoad file footer can be properly written.
+        // ResumeWalk. 
         if (mDocumentURI)
-            nsXULPrototypeCache::GetInstance()->RemoveFromFastLoadSet(mDocumentURI);
+            nsXULPrototypeCache::GetInstance()->RemoveFromCacheSet(mDocumentURI);
     }
 }
 
 nsresult
 NS_NewXULDocument(nsIXULDocument** result)
 {
     NS_PRECONDITION(result != nsnull, "null ptr");
     if (! result)
@@ -446,17 +445,17 @@ nsXULDocument::SetContentType(const nsAS
 {
     NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
                  "xul-documents always has content-type application/vnd.mozilla.xul+xml");
     // Don't do anything, xul always has the mimetype
     // application/vnd.mozilla.xul+xml
 }
 
 // This is called when the master document begins loading, whether it's
-// fastloaded or not.
+// being cached or not.
 nsresult
 nsXULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
                                  nsILoadGroup* aLoadGroup,
                                  nsISupports* aContainer,
                                  nsIStreamListener **aDocListener,
                                  PRBool aReset, nsIContentSink* aSink)
 {
     // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
@@ -489,19 +488,19 @@ nsXULDocument::StartDocumentLoad(const c
     // nsXULDocument::ResumeWalk
     // - Ben Goodger
     //
     // We don't abort on failure here because there are too many valid
     // cases that can return failure, and the null-ness of |proto| is enough
     // to trigger the fail-safe parse-from-disk solution. Example failure cases
     // (for reference) include:
     //
-    // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
+    // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
     //                         parse from disk
-    // other: the FastLoad cache file, XUL.mfl, could not be found, probably
+    // other: the startup cache file could not be found, probably
     //        due to being accessed before a profile has been selected (e.g.
     //        loading chrome for the profile manager itself). This must be
     //        parsed from disk.
 
     if (proto) {
         // If we're racing with another document to load proto, wait till the
         // load has finished loading before trying to add cloned style sheets.
         // nsXULDocument::EndLoad will call proto->NotifyLoadDone, which will
--- a/content/xul/document/src/nsXULPrototypeCache.cpp
+++ b/content/xul/document/src/nsXULPrototypeCache.cpp
@@ -52,30 +52,36 @@
 
 #include "nsIChromeRegistry.h"
 #include "nsIFastLoadService.h"
 #include "nsIFastLoadFileControl.h"
 #include "nsIFile.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObserverService.h"
+#include "nsIStringStream.h"
+#include "nsIStorageStream.h"
 
 #include "nsNetUtil.h"
 #include "nsAppDirectoryServiceDefs.h"
 
 #include "jsxdrapi.h"
 
 #include "mozilla/Preferences.h"
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/scache/StartupCacheUtils.h"
 
 using namespace mozilla;
+using namespace mozilla::scache;
 
 static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
 
 static PRBool gDisableXULCache = PR_FALSE; // enabled by default
 static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache";
+static const char kXULCacheInfoKey[] = "nsXULPrototypeCache.startupCache";
 
 //----------------------------------------------------------------------
 
 static int
 DisableXULCacheChangedCallback(const char* aPref, void* aClosure)
 {
     gDisableXULCache =
         Preferences::GetBool(kDisableXULCachePref, gDisableXULCache);
@@ -85,33 +91,28 @@ DisableXULCacheChangedCallback(const cha
     if (cache)
         cache->Flush();
 
     return 0;
 }
 
 //----------------------------------------------------------------------
 
-
-nsIFastLoadService*   nsXULPrototypeCache::gFastLoadService = nsnull;
-nsIFile*              nsXULPrototypeCache::gFastLoadFile = nsnull;
+StartupCache*   nsXULPrototypeCache::gStartupCache = nsnull;
 nsXULPrototypeCache*  nsXULPrototypeCache::sInstance = nsnull;
 
 
 nsXULPrototypeCache::nsXULPrototypeCache()
 {
 }
 
 
 nsXULPrototypeCache::~nsXULPrototypeCache()
 {
     FlushScripts();
-
-    NS_IF_RELEASE(gFastLoadService); // don't need ReleaseService nowadays!
-    NS_IF_RELEASE(gFastLoadFile);
 }
 
 
 NS_IMPL_THREADSAFE_ISUPPORTS2(nsXULPrototypeCache,
                               nsIXULPrototypeCache,
                               nsIObserver)
 
 
@@ -124,18 +125,23 @@ NS_NewXULPrototypeCache(nsISupports* aOu
 
     nsRefPtr<nsXULPrototypeCache> result = new nsXULPrototypeCache();
     if (! result)
         return NS_ERROR_OUT_OF_MEMORY;
 
     if (!(result->mPrototypeTable.Init() &&
           result->mStyleSheetTable.Init() &&
           result->mScriptTable.Init() &&
-          result->mXBLDocTable.Init() &&
-          result->mFastLoadURITable.Init())) {
+          result->mXBLDocTable.Init())) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    if (!(result->mCacheURITable.Init() &&
+          result->mInputStreamTable.Init() &&
+          result->mOutputStreamTable.Init())) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // XXX Ignore return values.
     gDisableXULCache =
         Preferences::GetBool(kDisableXULCachePref, gDisableXULCache);
     nsContentUtils::RegisterPrefCallback(kDisableXULCachePref,
                                          DisableXULCacheChangedCallback,
@@ -164,84 +170,76 @@ nsXULPrototypeCache::GetInstance()
 
         CallGetService(kXULPrototypeCacheCID, &cache);
 
         sInstance = static_cast<nsXULPrototypeCache*>(cache);
     }
     return sInstance;
 }
 
-/* static */ nsIFastLoadService*
-nsXULPrototypeCache::GetFastLoadService()
+/* static */ StartupCache*
+nsXULPrototypeCache::GetStartupCache()
 {
-    return gFastLoadService;
+    return gStartupCache;
 }
 
 //----------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsXULPrototypeCache::Observe(nsISupports* aSubject,
                              const char *aTopic,
                              const PRUnichar *aData)
 {
     if (!strcmp(aTopic, "chrome-flush-skin-caches")) {
         FlushSkinFiles();
     }
     else if (!strcmp(aTopic, "chrome-flush-caches")) {
         Flush();
     }
     else if (!strcmp(aTopic, "startupcache-invalidate")) {
-        AbortFastLoads();
+        AbortCaching();
     }
     else {
         NS_WARNING("Unexpected observer topic.");
     }
     return NS_OK;
 }
 
 nsXULPrototypeDocument*
 nsXULPrototypeCache::GetPrototype(nsIURI* aURI)
 {
     nsXULPrototypeDocument* protoDoc = mPrototypeTable.GetWeak(aURI);
+    if (protoDoc)
+        return protoDoc;
 
-    if (!protoDoc) {
-        // No prototype in XUL memory cache. Spin up FastLoad Service and
-        // look in FastLoad file.
-        nsresult rv = StartFastLoad(aURI);
-        if (NS_SUCCEEDED(rv)) {
-            nsCOMPtr<nsIObjectInputStream> objectInput;
-            gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
-
-            rv = StartFastLoadingURI(aURI, nsIFastLoadService::NS_FASTLOAD_READ);
-            if (NS_SUCCEEDED(rv)) {
-                nsCOMPtr<nsIURI> oldURI;
-                gFastLoadService->SelectMuxedDocument(aURI, getter_AddRefs(oldURI));
+    nsresult rv = BeginCaching(aURI);
+    if (NS_FAILED(rv))
+        return nsnull;
 
-                // Create a new prototype document.
-                nsRefPtr<nsXULPrototypeDocument> newProto;
-                rv = NS_NewXULPrototypeDocument(getter_AddRefs(newProto));
-                if (NS_FAILED(rv)) return nsnull;
-
-                rv = newProto->Read(objectInput);
-                if (NS_SUCCEEDED(rv)) {
-                    rv = PutPrototype(newProto);
-                    if (NS_FAILED(rv))
-                        newProto = nsnull;
-
-                    gFastLoadService->EndMuxedDocument(aURI);
-                } else {
-                    newProto = nsnull;
-                }
-
-                RemoveFromFastLoadSet(aURI);
-                protoDoc = newProto;
-            }
-        }
+    // No prototype in XUL memory cache. Spin up the cache Service.
+    nsCOMPtr<nsIObjectInputStream> ois;
+    rv = GetInputStream(aURI, getter_AddRefs(ois));
+    if (NS_FAILED(rv))
+        return nsnull;
+    
+    nsRefPtr<nsXULPrototypeDocument> newProto;
+    rv = NS_NewXULPrototypeDocument(getter_AddRefs(newProto));
+    if (NS_FAILED(rv))
+        return nsnull;
+    
+    rv = newProto->Read(ois);
+    if (NS_SUCCEEDED(rv)) {
+        rv = PutPrototype(newProto);
+    } else {
+        newProto = nsnull;
     }
-    return protoDoc;
+    
+    mInputStreamTable.Remove(aURI);
+    RemoveFromCacheSet(aURI);
+    return newProto;
 }
 
 nsresult
 nsXULPrototypeCache::PutPrototype(nsXULPrototypeDocument* aDocument)
 {
     nsCOMPtr<nsIURI> uri = aDocument->GetURI();
     // Put() releases any old value and addrefs the new one
     NS_ENSURE_TRUE(mPrototypeTable.Put(uri, aDocument), NS_ERROR_OUT_OF_MEMORY);
@@ -400,487 +398,332 @@ nsXULPrototypeCache::Flush()
 
 
 PRBool
 nsXULPrototypeCache::IsEnabled()
 {
     return !gDisableXULCache;
 }
 
-static PRBool gDisableXULFastLoad = PR_FALSE;           // enabled by default
-static PRBool gChecksumXULFastLoadFile = PR_TRUE;       // XXXbe too paranoid
+static PRBool gDisableXULDiskCache = PR_FALSE;           // enabled by default
 
 void
-nsXULPrototypeCache::AbortFastLoads()
+nsXULPrototypeCache::AbortCaching()
 {
 #ifdef DEBUG_brendan
     NS_BREAK();
 #endif
 
     // Flush the XUL cache for good measure, in case we cached a bogus/downrev
     // script, somehow.
     Flush();
 
-    // Clear the FastLoad set
-    mFastLoadURITable.Clear();
-
-    nsCOMPtr<nsIFastLoadService> fastLoadService = gFastLoadService;
-    nsCOMPtr<nsIFile> file = gFastLoadFile;
-
-    nsresult rv;
-
-    if (! fastLoadService) {
-        fastLoadService = do_GetFastLoadService();
-        if (! fastLoadService)
-            return;
-
-        rv = fastLoadService->NewFastLoadFile(XUL_FASTLOAD_FILE_BASENAME,
-                                              getter_AddRefs(file));
-        if (NS_FAILED(rv))
-            return;
-    }
-
-    // Fetch the current input (if FastLoad file existed) or output (if we're
-    // creating the FastLoad file during this app startup) stream.
-    nsCOMPtr<nsIObjectInputStream> objectInput;
-    nsCOMPtr<nsIObjectOutputStream> objectOutput;
-    fastLoadService->GetInputStream(getter_AddRefs(objectInput));
-    fastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
-
-    if (objectOutput) {
-        fastLoadService->SetOutputStream(nsnull);
-
-        if (NS_SUCCEEDED(objectOutput->Close()) && gChecksumXULFastLoadFile)
-            fastLoadService->CacheChecksum(file,
-                                           objectOutput);
-    }
-
-    if (objectInput) {
-        // If this is the last of one or more XUL master documents loaded
-        // together at app startup, close the FastLoad service's singleton
-        // input stream now.
-        fastLoadService->SetInputStream(nsnull);
-        objectInput->Close();
-    }
-
-    // Now rename or remove the file.
-    if (file) {
-#ifdef DEBUG
-        // Remove any existing Aborted.mfasl files generated in previous runs.
-        nsCOMPtr<nsIFile> existingAbortedFile;
-        file->Clone(getter_AddRefs(existingAbortedFile));
-        if (existingAbortedFile) {
-            existingAbortedFile->SetLeafName(NS_LITERAL_STRING("Aborted.mfasl"));
-            PRBool fileExists = PR_FALSE;
-            existingAbortedFile->Exists(&fileExists);
-            if (fileExists)
-                existingAbortedFile->Remove(PR_FALSE);
-        }
-        file->MoveToNative(nsnull, NS_LITERAL_CSTRING("Aborted.mfasl"));
-#else
-        rv = file->Remove(PR_FALSE);
-        if (NS_FAILED(rv))
-            NS_WARNING("Failed to remove fastload file, fastload data may be outdated");
-#endif
-    }
-
-    // If the list is empty now, the FastLoad process is done.
-    NS_IF_RELEASE(gFastLoadService);
-    NS_IF_RELEASE(gFastLoadFile);
+    // Clear the cache set
+    mCacheURITable.Clear();
 }
 
 
+static const char kDisableXULDiskCachePref[] = "nglayout.debug.disable_xul_fastload";
+
 void
-nsXULPrototypeCache::RemoveFromFastLoadSet(nsIURI* aURI)
+nsXULPrototypeCache::RemoveFromCacheSet(nsIURI* aURI)
 {
-    mFastLoadURITable.Remove(aURI);
+    mCacheURITable.Remove(aURI);
 }
 
-static const char kDisableXULFastLoadPref[] = "nglayout.debug.disable_xul_fastload";
-static const char kChecksumXULFastLoadFilePref[] = "nglayout.debug.checksum_xul_fastload_file";
-
 nsresult
 nsXULPrototypeCache::WritePrototype(nsXULPrototypeDocument* aPrototypeDocument)
 {
     nsresult rv = NS_OK, rv2 = NS_OK;
 
-    // We're here before the FastLoad service has been initialized, probably because
+    // We're here before the startupcache service has been initialized, probably because
     // of the profile manager. Bail quietly, don't worry, we'll be back later.
-    if (! gFastLoadService)
+    if (!gStartupCache)
         return NS_OK;
 
-    // Fetch the current input (if FastLoad file existed) or output (if we're
-    // creating the FastLoad file during this app startup) stream.
-    nsCOMPtr<nsIObjectInputStream> objectInput;
-    nsCOMPtr<nsIObjectOutputStream> objectOutput;
-    gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
-    gFastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
-
     nsCOMPtr<nsIURI> protoURI = aPrototypeDocument->GetURI();
 
-    // Remove this document from the FastLoad table. We use the table's
-    // emptiness instead of a counter to decide when the FastLoad process
-    // has completed. When complete, we can write footer details to the
-    // FastLoad file.
-    RemoveFromFastLoadSet(protoURI);
-
-    PRInt32 count = mFastLoadURITable.Count();
-
-    if (objectOutput) {
-        rv = StartFastLoadingURI(protoURI, nsIFastLoadService::NS_FASTLOAD_WRITE);
-        if (NS_SUCCEEDED(rv)) {
-            // Re-select the URL of the current prototype, as out-of-line script loads
-            // may have changed
-            nsCOMPtr<nsIURI> oldURI;
-            gFastLoadService->SelectMuxedDocument(protoURI, getter_AddRefs(oldURI));
-
-            aPrototypeDocument->Write(objectOutput);
-
-            gFastLoadService->EndMuxedDocument(protoURI);
-        }
+    // Remove this document from the cache table. We use the table's
+    // emptiness instead of a counter to decide when the caching process 
+    // has completed.
+    RemoveFromCacheSet(protoURI);
 
-        // If this is the last of one or more XUL master documents loaded
-        // together at app startup, close the FastLoad service's singleton
-        // output stream now.
-        //
-        // NB: we must close input after output, in case the output stream
-        // implementation needs to read from the input stream, to compute a
-        // FastLoad file checksum.  In that case, the implementation used
-        // nsIFastLoadFileIO to get the corresponding input stream for this
-        // output stream.
-        if (count == 0) {
-            gFastLoadService->SetOutputStream(nsnull);
-            rv = objectOutput->Close();
+    PRInt32 count = mCacheURITable.Count();
+    nsCOMPtr<nsIObjectOutputStream> oos;
+    rv = GetOutputStream(protoURI, getter_AddRefs(oos));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-            if (NS_SUCCEEDED(rv) && gChecksumXULFastLoadFile) {
-                rv = gFastLoadService->CacheChecksum(gFastLoadFile,
-                                                     objectOutput);
-            }
-        }
-    }
-
-    if (objectInput) {
-        // If this is the last of one or more XUL master documents loaded
-        // together at app startup, close the FastLoad service's singleton
-        // input stream now.
-        if (count == 0) {
-            gFastLoadService->SetInputStream(nsnull);
-            rv2 = objectInput->Close();
-        }
-    }
-
-    // If the list is empty now, the FastLoad process is done.
-    if (count == 0) {
-        NS_RELEASE(gFastLoadService);
-        NS_RELEASE(gFastLoadFile);
-    }
-
+    rv = aPrototypeDocument->Write(oos);
+    NS_ENSURE_SUCCESS(rv, rv);
+    FinishOutputStream(protoURI);
     return NS_FAILED(rv) ? rv : rv2;
 }
 
+nsresult
+nsXULPrototypeCache::GetInputStream(nsIURI* uri, nsIObjectInputStream** stream) 
+{
+    nsCAutoString spec;
+    uri->GetPath(spec);
+    
+    nsAutoArrayPtr<char> buf;
+    PRUint32 len;
+    nsCOMPtr<nsIObjectInputStream> ois;
+    if (!gStartupCache)
+        return NS_ERROR_NOT_AVAILABLE;
+    
+    nsresult rv = gStartupCache->GetBuffer(spec.get(), getter_Transfers(buf), 
+                                           &len);
+    if (NS_FAILED(rv)) 
+        return NS_ERROR_NOT_AVAILABLE;
+
+    rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
+    NS_ENSURE_SUCCESS(rv, rv);
+    buf.forget();
+
+    mInputStreamTable.Put(uri, ois);
+    
+    NS_ADDREF(*stream = ois);
+    return NS_OK;
+}
 
 nsresult
-nsXULPrototypeCache::StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags)
+nsXULPrototypeCache::FinishInputStream(nsIURI* uri) {
+    mInputStreamTable.Remove(uri);
+    return NS_OK;
+}
+
+nsresult
+nsXULPrototypeCache::GetOutputStream(nsIURI* uri, nsIObjectOutputStream** stream)
 {
     nsresult rv;
-
-    nsCAutoString urlspec;
-    rv = aURI->GetAsciiSpec(urlspec);
-    if (NS_FAILED(rv)) return rv;
+    nsCOMPtr<nsIObjectOutputStream> objectOutput;
+    nsCOMPtr<nsIStorageStream> storageStream;
+    PRBool found = mOutputStreamTable.Get(uri, getter_AddRefs(storageStream));
+    if (found) {
+        objectOutput = do_CreateInstance("mozilla.org/binaryoutputstream;1");
+        if (!objectOutput) return NS_ERROR_OUT_OF_MEMORY;
+        nsCOMPtr<nsIOutputStream> outputStream
+            = do_QueryInterface(storageStream);
+        objectOutput->SetOutputStream(outputStream);
+    } else {
+        rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput), 
+                                                    getter_AddRefs(storageStream),
+                                                    false);
+        NS_ENSURE_SUCCESS(rv, rv);
+        mOutputStreamTable.Put(uri, storageStream);
+    }
+    NS_ADDREF(*stream = objectOutput);
+    return NS_OK;
+}
 
-    // If StartMuxedDocument returns NS_ERROR_NOT_AVAILABLE, then
-    // we must be reading the file, and urlspec was not associated
-    // with any multiplexed stream in it.  The FastLoad service
-    // will therefore arrange to update the file, writing new data
-    // at the end while old (available) data continues to be read
-    // from the pre-existing part of the file.
-    return gFastLoadService->StartMuxedDocument(aURI, urlspec.get(), aDirectionFlags);
+nsresult
+nsXULPrototypeCache::FinishOutputStream(nsIURI* uri) 
+{
+    nsresult rv;
+    if (!gStartupCache)
+        return NS_ERROR_NOT_AVAILABLE;
+    
+    nsCOMPtr<nsIStorageStream> storageStream;
+    PRBool found = mOutputStreamTable.Get(uri, getter_AddRefs(storageStream));
+    if (!found)
+        return NS_ERROR_UNEXPECTED;
+    nsCOMPtr<nsIOutputStream> outputStream
+        = do_QueryInterface(storageStream);
+    outputStream->Close();
+    
+    nsAutoArrayPtr<char> buf;
+    PRUint32 len;
+    rv = NS_NewBufferFromStorageStream(storageStream, getter_Transfers(buf), 
+                                       &len);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCAutoString spec;
+    uri->GetPath(spec);
+    rv = gStartupCache->PutBuffer(spec.get(), buf, len);
+    if (NS_SUCCEEDED(rv))
+        mOutputStreamTable.Remove(uri);
+    
+    return rv;
+}
+
+// We have data if we're in the middle of writing it or we already
+// have it in the cache.
+nsresult
+nsXULPrototypeCache::HasData(nsIURI* uri, PRBool* exists)
+{
+    if (mOutputStreamTable.Get(uri, nsnull)) {
+        *exists = PR_TRUE;
+        return NS_OK;
+    }
+    nsCAutoString spec;
+    uri->GetPath(spec);
+    nsAutoArrayPtr<char> buf;
+    PRUint32 len;
+    nsresult rv;
+    if (gStartupCache)
+        rv = gStartupCache->GetBuffer(spec.get(), getter_Transfers(buf), 
+                                      &len);
+    else {
+        // We don't have everything we need to call BeginCaching and set up
+        // gStartupCache right now, but we just need to check the cache for 
+        // this URI.
+        StartupCache* sc = StartupCache::GetSingleton();
+        if (!sc) {
+            *exists = PR_FALSE;
+            return NS_OK;
+        }
+        rv = sc->GetBuffer(spec.get(), getter_Transfers(buf), &len);
+    }
+    *exists = NS_SUCCEEDED(rv);
+    return NS_OK;
 }
 
 static int
-FastLoadPrefChangedCallback(const char* aPref, void* aClosure)
+CachePrefChangedCallback(const char* aPref, void* aClosure)
 {
-    PRBool wasEnabled = !gDisableXULFastLoad;
-    gDisableXULFastLoad =
-        Preferences::GetBool(kDisableXULFastLoadPref, gDisableXULFastLoad);
+    PRBool wasEnabled = !gDisableXULDiskCache;
+    gDisableXULDiskCache =
+        Preferences::GetBool(kDisableXULCachePref,
+                             gDisableXULDiskCache);
 
-    if (wasEnabled && gDisableXULFastLoad) {
+    if (wasEnabled && gDisableXULDiskCache) {
         static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
         nsCOMPtr<nsIXULPrototypeCache> cache =
             do_GetService(kXULPrototypeCacheCID);
 
         if (cache)
-            cache->AbortFastLoads();
+            cache->AbortCaching();
     }
-
-    gChecksumXULFastLoadFile =
-        Preferences::GetBool(kChecksumXULFastLoadFilePref,
-                             gChecksumXULFastLoadFile);
-
     return 0;
 }
 
-
-class nsXULFastLoadFileIO : public nsIFastLoadFileIO
-{
-  public:
-    nsXULFastLoadFileIO(nsIFile* aFile)
-      : mFile(aFile), mTruncateOutputFile(true) {
-    }
-
-    virtual ~nsXULFastLoadFileIO() {
-    }
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIFASTLOADFILEIO
-
-    nsCOMPtr<nsIFile>         mFile;
-    nsCOMPtr<nsIInputStream>  mInputStream;
-    nsCOMPtr<nsIOutputStream> mOutputStream;
-    bool mTruncateOutputFile;
-};
-
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsXULFastLoadFileIO, nsIFastLoadFileIO)
-
-
-NS_IMETHODIMP
-nsXULFastLoadFileIO::GetInputStream(nsIInputStream** aResult)
-{
-    if (! mInputStream) {
-        nsresult rv;
-        nsCOMPtr<nsIInputStream> fileInput;
-        rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInput), mFile);
-        if (NS_FAILED(rv)) return rv;
-
-        rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream),
-                                       fileInput,
-                                       XUL_DESERIALIZATION_BUFFER_SIZE);
-        if (NS_FAILED(rv)) return rv;
-    }
-
-    NS_ADDREF(*aResult = mInputStream);
-    return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsXULFastLoadFileIO::GetOutputStream(nsIOutputStream** aResult)
-{
-    if (! mOutputStream) {
-        PRInt32 ioFlags = PR_WRONLY;
-        if (mTruncateOutputFile)
-            ioFlags |= PR_CREATE_FILE | PR_TRUNCATE;
-
-        nsresult rv;
-        nsCOMPtr<nsIOutputStream> fileOutput;
-        rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOutput), mFile,
-                                         ioFlags, 0644);
-        if (NS_FAILED(rv)) return rv;
-
-        rv = NS_NewBufferedOutputStream(getter_AddRefs(mOutputStream),
-                                        fileOutput,
-                                        XUL_SERIALIZATION_BUFFER_SIZE);
-        if (NS_FAILED(rv)) return rv;
-    }
-
-    NS_ADDREF(*aResult = mOutputStream);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsXULFastLoadFileIO::DisableTruncate()
-{
-    mTruncateOutputFile = false;
-    return NS_OK;
-}
-
 nsresult
-nsXULPrototypeCache::StartFastLoad(nsIURI* aURI)
+nsXULPrototypeCache::BeginCaching(nsIURI* aURI)
 {
     nsresult rv;
 
     nsCAutoString path;
     aURI->GetPath(path);
     if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul")))
         return NS_ERROR_NOT_AVAILABLE;
 
-    // Test gFastLoadFile to decide whether this is the first nsXULDocument
-    // participating in FastLoad.  If gFastLoadFile is non-null, this document
-    // must not be first, but it can join the FastLoad process.  Examples of
+    // Test gStartupCache to decide whether this is the first nsXULDocument
+    // participating in the serialization.  If gStartupCache is non-null, this document
+    // must not be first, but it can join the process.  Examples of
     // multiple master documents participating include hiddenWindow.xul and
     // navigator.xul on the Mac, and multiple-app-component (e.g., mailnews
     // and browser) startup due to command-line arguments.
     //
-    // XXXbe we should attempt to update the FastLoad file after startup!
-    //
-    // XXXbe we do not yet use nsFastLoadPtrs, but once we do, we must keep
-    // the FastLoad input stream open for the life of the app.
-    if (gFastLoadService && gFastLoadFile) {
-        mFastLoadURITable.Put(aURI, 1);
+    if (gStartupCache) {
+        mCacheURITable.Put(aURI, 1);
 
         return NS_OK;
     }
 
     // Use a local to refer to the service till we're sure we succeeded, then
-    // commit to gFastLoadService.  Same for gFastLoadFile, which is used to
-    // delete the FastLoad file on abort.
-    nsCOMPtr<nsIFastLoadService> fastLoadService(do_GetFastLoadService());
-    if (! fastLoadService)
+    // commit to gStartupCache.
+    StartupCache* startupCache = StartupCache::GetSingleton();
+    if (!startupCache)
         return NS_ERROR_FAILURE;
 
-    gDisableXULFastLoad =
-        Preferences::GetBool(kDisableXULFastLoadPref, gDisableXULFastLoad);
-    gChecksumXULFastLoadFile =
-        Preferences::GetBool(kChecksumXULFastLoadFilePref,
-                             gChecksumXULFastLoadFile);
-    nsContentUtils::RegisterPrefCallback(kDisableXULFastLoadPref,
-                                         FastLoadPrefChangedCallback,
-                                         nsnull);
-    nsContentUtils::RegisterPrefCallback(kChecksumXULFastLoadFilePref,
-                                         FastLoadPrefChangedCallback,
+    gDisableXULDiskCache =
+        Preferences::GetBool(kDisableXULCachePref, gDisableXULDiskCache);
+
+    nsContentUtils::RegisterPrefCallback(kDisableXULCachePref,
+                                         CachePrefChangedCallback,
                                          nsnull);
 
-    if (gDisableXULFastLoad)
+    if (gDisableXULDiskCache)
         return NS_ERROR_NOT_AVAILABLE;
 
     // Get the chrome directory to validate against the one stored in the
-    // FastLoad file, or to store there if we're generating a new file.
+    // cache file, or to store there if we're generating a new file.
     nsCOMPtr<nsIFile> chromeDir;
     rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir));
     if (NS_FAILED(rv))
         return rv;
     nsCAutoString chromePath;
     rv = chromeDir->GetNativePath(chromePath);
     if (NS_FAILED(rv))
         return rv;
 
-    nsCOMPtr<nsIFile> file;
-    rv = fastLoadService->NewFastLoadFile(XUL_FASTLOAD_FILE_BASENAME,
-                                          getter_AddRefs(file));
-    if (NS_FAILED(rv))
-        return rv;
-
-    // Give the FastLoad service an object by which it can get or create a
-    // file output stream given an input stream on the same file.
-    nsXULFastLoadFileIO* xio = new nsXULFastLoadFileIO(file);
-    nsCOMPtr<nsIFastLoadFileIO> io = static_cast<nsIFastLoadFileIO*>(xio);
-    if (! io)
-        return NS_ERROR_OUT_OF_MEMORY;
-    fastLoadService->SetFileIO(io);
-
-    nsCOMPtr<nsIXULChromeRegistry> chromeReg =
-        mozilla::services::GetXULChromeRegistryService();
-    if (!chromeReg)
-        return NS_ERROR_FAILURE;
-
     // XXXbe we assume the first package's locale is the same as the locale of
-    // all subsequent packages of FastLoaded chrome URIs....
+    // all subsequent packages of cached chrome URIs....
     nsCAutoString package;
     rv = aURI->GetHost(package);
     if (NS_FAILED(rv))
         return rv;
-
+    nsCOMPtr<nsIXULChromeRegistry> chromeReg
+        = do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv);
     nsCAutoString locale;
     rv = chromeReg->GetSelectedLocale(package, locale);
     if (NS_FAILED(rv))
         return rv;
 
-    // Try to read an existent FastLoad file.
-    PRBool exists = PR_FALSE;
-    if (NS_SUCCEEDED(file->Exists(&exists)) && exists) {
-        nsCOMPtr<nsIObjectInputStream> objectInput;
-        rv = fastLoadService->NewInputStream(file, getter_AddRefs(objectInput));
+    nsCAutoString fileChromePath, fileLocale;
+    
+    nsAutoArrayPtr<char> buf;
+    PRUint32 len, amtRead;
+    nsCOMPtr<nsIObjectInputStream> objectInput;
 
+    rv = startupCache->GetBuffer(kXULCacheInfoKey, getter_Transfers(buf), 
+                                 &len);
+    if (NS_SUCCEEDED(rv))
+        rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(objectInput));
+    
+    if (NS_SUCCEEDED(rv)) {
+        buf.forget();
+        rv = objectInput->ReadCString(fileLocale);
+        rv |= objectInput->ReadCString(fileChromePath);
+        if (NS_FAILED(rv) ||
+            (!fileChromePath.Equals(chromePath) ||
+             !fileLocale.Equals(locale))) {
+            // Our cache won't be valid in this case, we'll need to rewrite.
+            // XXX This blows away work that other consumers (like
+            // mozJSComponentLoader) have done, need more fine-grained control.
+            startupCache->InvalidateCache();
+            rv = NS_ERROR_UNEXPECTED;
+        }
+    } else if (rv != NS_ERROR_NOT_AVAILABLE)
+        // NS_ERROR_NOT_AVAILABLE is normal, usually if there's no cachefile.
+        return rv;
+
+    if (NS_FAILED(rv)) {
+        // Either the cache entry was invalid or it didn't exist, so write it now.
+        nsCOMPtr<nsIObjectOutputStream> objectOutput;
+        nsCOMPtr<nsIInputStream> inputStream;
+        nsCOMPtr<nsIStorageStream> storageStream;
+        rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput),
+                                                    getter_AddRefs(storageStream),
+                                                    false);
         if (NS_SUCCEEDED(rv)) {
-            if (NS_SUCCEEDED(rv)) {
-                // Get the XUL fastload file version number, which should be
-                // decremented whenever the XUL-specific file format changes
-                // (see public/nsIXULPrototypeCache.h for the #define).
-                PRUint32 xulFastLoadVersion, jsByteCodeVersion;
-                rv = objectInput->Read32(&xulFastLoadVersion);
-                rv |= objectInput->Read32(&jsByteCodeVersion);
-                if (NS_SUCCEEDED(rv)) {
-                    if (xulFastLoadVersion != XUL_FASTLOAD_FILE_VERSION ||
-                        jsByteCodeVersion != JSXDR_BYTECODE_VERSION) {
-#ifdef DEBUG
-                        printf((xulFastLoadVersion != XUL_FASTLOAD_FILE_VERSION)
-                               ? "bad FastLoad file version\n"
-                               : "bad JS bytecode version\n");
-#endif
-                        rv = NS_ERROR_UNEXPECTED;
-                    } else {
-                        nsCAutoString fileChromePath, fileLocale;
-
-                        rv = objectInput->ReadCString(fileChromePath);
-                        rv |= objectInput->ReadCString(fileLocale);
-                        if (NS_SUCCEEDED(rv) &&
-                            (!fileChromePath.Equals(chromePath) ||
-                             !fileLocale.Equals(locale))) {
-                            rv = NS_ERROR_UNEXPECTED;
-                        }
-                    }
-                }
+            rv = objectOutput->WriteStringZ(locale.get());
+            rv |= objectOutput->WriteStringZ(chromePath.get());
+            rv |= objectOutput->Close();
+            rv |= storageStream->NewInputStream(0, getter_AddRefs(inputStream));
+        }
+        if (NS_SUCCEEDED(rv))
+            rv = inputStream->Available(&len);
+        
+        if (NS_SUCCEEDED(rv)) {
+            buf = new char[len];
+            rv = inputStream->Read(buf, len, &amtRead);
+            if (NS_SUCCEEDED(rv) && len == amtRead)
+                rv = startupCache->PutBuffer(kXULCacheInfoKey, buf, len);
+            else {
+                rv = NS_ERROR_UNEXPECTED;
             }
         }
 
-        if (NS_SUCCEEDED(rv)) {
-            fastLoadService->SetInputStream(objectInput);
-        } else {
-            // NB: we must close before attempting to remove, for non-Unix OSes
-            // that can't do open-unlink.
-            if (objectInput)
-                objectInput->Close();
-            xio->mInputStream = nsnull;
-
-#ifdef DEBUG
-            file->MoveToNative(nsnull, NS_LITERAL_CSTRING("Invalid.mfasl"));
-#else
-            file->Remove(PR_FALSE);
-#endif
-            exists = PR_FALSE;
+        // Failed again, just bail.
+        if (NS_FAILED(rv)) {
+            startupCache->InvalidateCache();
+            return NS_ERROR_FAILURE;
         }
     }
 
-    // FastLoad file not found, or invalid: write a new one.
-    if (! exists) {
-        nsCOMPtr<nsIOutputStream> output;
-        rv = io->GetOutputStream(getter_AddRefs(output));
-        if (NS_FAILED(rv))
-            return rv;
-
-        nsCOMPtr<nsIObjectOutputStream> objectOutput;
-        rv = fastLoadService->NewOutputStream(output,
-                                              getter_AddRefs(objectOutput));
-        if (NS_SUCCEEDED(rv)) {
-            rv = objectOutput->Write32(XUL_FASTLOAD_FILE_VERSION);
-            rv |= objectOutput->Write32(JSXDR_BYTECODE_VERSION);
-            rv |= objectOutput->WriteStringZ(chromePath.get());
-            rv |= objectOutput->WriteStringZ(locale.get());
-        }
+    // Success!  Insert this URI into the mCacheURITable
+    // and commit locals to globals.
+    mCacheURITable.Put(aURI, 1);
 
-        // Remove here even though some errors above will lead to a FastLoad
-        // file invalidation.  Other errors (failure to note the dependency on
-        // installed-chrome.txt, e.g.) will not cause invalidation, and we may
-        // as well tidy up now.
-        if (NS_FAILED(rv)) {
-            if (objectOutput)
-                objectOutput->Close();
-            else
-                output->Close();
-            xio->mOutputStream = nsnull;
-
-            file->Remove(PR_FALSE);
-            return NS_ERROR_FAILURE;
-        }
-
-        fastLoadService->SetOutputStream(objectOutput);
-    }
-
-    // Success!  Insert this URI into the mFastLoadURITable
-    // and commit locals to globals.
-    mFastLoadURITable.Put(aURI, 1);
-
-    NS_ADDREF(gFastLoadService = fastLoadService);
-    NS_ADDREF(gFastLoadFile = file);
+    gStartupCache = startupCache;
     return NS_OK;
 }
-
--- a/content/xul/document/src/nsXULPrototypeCache.h
+++ b/content/xul/document/src/nsXULPrototypeCache.h
@@ -47,70 +47,64 @@
 #include "nsIObserver.h"
 #include "nsXBLDocumentInfo.h"
 #include "nsIXULPrototypeCache.h"
 #include "nsDataHashtable.h"
 #include "nsInterfaceHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsURIHashKey.h"
 #include "nsXULPrototypeDocument.h"
+#include "nsIInputStream.h"
+#include "nsIStorageStream.h"
+#include "mozilla/scache/StartupCache.h"
 
-class nsIFastLoadService;
+using namespace mozilla::scache;
+
 class nsCSSStyleSheet;
 
 struct CacheScriptEntry
 {
     PRUint32    mScriptTypeID; // the script language ID.
     void*       mScriptObject; // the script object.
 };
 
 /**
  * The XUL prototype cache can be used to store and retrieve shared data for
  * XUL documents, style sheets, XBL, and scripts.
  *
  * The cache has two levels:
  *  1. In-memory hashtables
- *  2. The on-disk fastload file.
+ *  2. The on-disk cache file.
  */
 class nsXULPrototypeCache : public nsIXULPrototypeCache,
                                    nsIObserver
 {
 public:
     // nsISupports
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 
     // nsIXULPrototypeCache
     virtual PRBool IsCached(nsIURI* aURI) {
         return GetPrototype(aURI) != nsnull;
     }
-    virtual void AbortFastLoads();
+    virtual void AbortCaching();
 
 
     /**
      * Whether the prototype cache is enabled.
      */
     PRBool IsEnabled();
 
     /**
      * Flush the cache; remove all XUL prototype documents, style
      * sheets, and scripts.
      */
     void Flush();
 
-    /**
-     * Remove a XUL document from the set of loading documents.
-     */
-    void RemoveFromFastLoadSet(nsIURI* aDocumentURI);
-
-    /**
-     * Write the XUL prototype document to fastload file. The proto must be
-     * fully loaded.
-     */
-    nsresult WritePrototype(nsXULPrototypeDocument* aPrototypeDocument);
 
     // The following methods are used to put and retrive various items into and
     // from the cache.
 
     nsXULPrototypeDocument* GetPrototype(nsIURI* aURI);
     nsresult PutPrototype(nsXULPrototypeDocument* aDocument);
 
     void* GetScript(nsIURI* aURI, PRUint32* langID);
@@ -130,19 +124,40 @@ public:
     }
 
     /**
      * Store a style sheet in the cache. The key, style sheet's URI is obtained
      * from the style sheet itself.
      */
     nsresult PutStyleSheet(nsCSSStyleSheet* aStyleSheet);
 
+    /**
+     * Remove a XUL document from the set of loading documents.
+     */
+    void RemoveFromCacheSet(nsIURI* aDocumentURI);
+
+    /**
+     * Write the XUL prototype document to a cache file. The proto must be
+     * fully loaded.
+     */
+    nsresult WritePrototype(nsXULPrototypeDocument* aPrototypeDocument);
+
+    /**
+     * This interface allows partial reads and writes from the buffers in the
+     * startupCache.
+     */
+    nsresult GetInputStream(nsIURI* aURI, nsIObjectInputStream** objectInput);
+    nsresult FinishInputStream(nsIURI* aURI);
+    nsresult GetOutputStream(nsIURI* aURI, nsIObjectOutputStream** objectOutput);
+    nsresult FinishOutputStream(nsIURI* aURI);
+    nsresult HasData(nsIURI* aURI, PRBool* exists);
+
+    static StartupCache* GetStartupCache();
 
     static nsXULPrototypeCache* GetInstance();
-    static nsIFastLoadService* GetFastLoadService();
 
     static void ReleaseGlobals()
     {
         NS_IF_RELEASE(sInstance);
     }
 
 protected:
     friend nsresult
@@ -157,21 +172,21 @@ protected:
     void FlushSkinFiles();
 
     nsRefPtrHashtable<nsURIHashKey,nsXULPrototypeDocument>  mPrototypeTable; // owns the prototypes
     nsRefPtrHashtable<nsURIHashKey,nsCSSStyleSheet>        mStyleSheetTable;
     nsDataHashtable<nsURIHashKey,CacheScriptEntry>         mScriptTable;
     nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo>  mXBLDocTable;
 
     ///////////////////////////////////////////////////////////////////////////
-    // FastLoad
+    // StartupCache
     // this is really a hash set, with a dummy data parameter
-    nsDataHashtable<nsURIHashKey,PRUint32> mFastLoadURITable;
+    nsDataHashtable<nsURIHashKey,PRUint32> mCacheURITable;
 
-    static nsIFastLoadService*    gFastLoadService;
-    static nsIFile*               gFastLoadFile;
-
-    // Bootstrap FastLoad Service
-    nsresult StartFastLoad(nsIURI* aDocumentURI);
-    nsresult StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags);
+    static StartupCache* gStartupCache;
+    nsInterfaceHashtable<nsURIHashKey, nsIStorageStream> mOutputStreamTable;
+    nsInterfaceHashtable<nsURIHashKey, nsIObjectInputStream> mInputStreamTable;
+ 
+    // Bootstrap caching service
+    nsresult BeginCaching(nsIURI* aDocumentURI);
 };
 
 #endif // nsXULPrototypeCache_h__
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -413,16 +413,23 @@ nsXULPrototypeDocument::Write(nsIObjectO
         rv |= aStream->WriteCompoundObject(mStyleSheetReferences[i],
                                            NS_GET_IID(nsIURI), PR_TRUE);
     }
 
     // nsIPrincipal mNodeInfoManager->mPrincipal
     rv |= aStream->WriteObject(mNodeInfoManager->DocumentPrincipal(),
                                PR_TRUE);
     
+#ifdef DEBUG
+    // XXX Worrisome if we're caching things without system principal.
+    if (nsContentUtils::IsSystemPrincipal(mNodeInfoManager->DocumentPrincipal())) {
+        NS_WARNING("Serializing document without system principal");
+    }
+#endif
+
     // nsINodeInfo table
     nsCOMArray<nsINodeInfo> nodeInfos;
     if (mRoot)
         rv |= GetNodeInfos(mRoot, nodeInfos);
 
     PRUint32 nodeInfoCount = nodeInfos.Count();
     rv |= aStream->Write32(nodeInfoCount);
     for (i = 0; i < nodeInfoCount; ++i) {
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -964,17 +964,18 @@ mozJSComponentLoader::WriteScript(Startu
     nsCAutoString spec;
     rv = PathifyURI(uri, spec);
     NS_ENSURE_SUCCESS(rv, rv);
 
     LOG(("Writing %s to startupcache\n", spec.get()));
     nsCOMPtr<nsIObjectOutputStream> oos;
     nsCOMPtr<nsIStorageStream> storageStream; 
     rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
-                                                getter_AddRefs(storageStream));
+                                                getter_AddRefs(storageStream),
+                                                true);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = WriteScriptToStream(cx, scriptObj, oos);
     oos->Close();
     NS_ENSURE_SUCCESS(rv, rv);
  
     nsAutoArrayPtr<char> buf;
     PRUint32 len;
--- a/startupcache/StartupCacheUtils.cpp
+++ b/startupcache/StartupCacheUtils.cpp
@@ -22,38 +22,43 @@ NS_NewObjectInputStreamFromBuffer(char* 
   objectInput->SetInputStream(stringStream);
   
   objectInput.forget(stream);
   return NS_OK;
 }
 
 NS_EXPORT nsresult
 NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
-                                       nsIStorageStream** stream)
+                                       nsIStorageStream** stream,
+				       PRBool wantDebugStream)
 {
   nsCOMPtr<nsIStorageStream> storageStream;
 
   nsresult rv = NS_NewStorageStream(256, PR_UINT32_MAX, getter_AddRefs(storageStream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIObjectOutputStream> objectOutput
     = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
   nsCOMPtr<nsIOutputStream> outputStream
     = do_QueryInterface(storageStream);
   
   objectOutput->SetOutputStream(outputStream);
   
 #ifdef DEBUG
-  // Wrap in debug stream to detect unsupported writes of 
-  // multiply-referenced non-singleton objects
-  StartupCache* sc = StartupCache::GetSingleton();
-  NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED);
-  nsCOMPtr<nsIObjectOutputStream> debugStream;
-  sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream));
-  debugStream.forget(wrapperStream);
+  if (wantDebugStream) {
+    // Wrap in debug stream to detect unsupported writes of 
+    // multiply-referenced non-singleton objects
+    StartupCache* sc = StartupCache::GetSingleton();
+    NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED);
+    nsCOMPtr<nsIObjectOutputStream> debugStream;
+    sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream));
+    debugStream.forget(wrapperStream);
+  } else {
+    objectOutput.forget(wrapperStream);
+  }
 #else
   objectOutput.forget(wrapperStream);
 #endif
   
   storageStream.forget(stream);
   return NS_OK;
 }
 
--- a/startupcache/StartupCacheUtils.h
+++ b/startupcache/StartupCacheUtils.h
@@ -45,19 +45,24 @@
 namespace mozilla {
 namespace scache {
 
 NS_EXPORT nsresult
 NS_NewObjectInputStreamFromBuffer(char* buffer, PRUint32 len, 
                                   nsIObjectInputStream** stream);
 
 // We can't retrieve the wrapped stream from the objectOutputStream later,
-// so we return it here.
+// so we return it here. We give callers in debug builds the option 
+// to wrap the outputstream in a debug stream, which will detect if
+// 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
 NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
-                                       nsIStorageStream** stream);
+                                       nsIStorageStream** stream,
+                                       PRBool wantDebugStream);
 
 NS_EXPORT nsresult
 NS_NewBufferFromStorageStream(nsIStorageStream *storageStream, 
                               char** buffer, PRUint32* len);
 }
 }
 #endif //nsStartupCacheUtils_h_
deleted file mode 100644
--- a/startupcache/nsStartupCacheUtils.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Startup Cache.
- *
- * The Initial Developer of the Original Code is
- * The Mozilla Foundation <http://www.mozilla.org/>.
- * Portions created by the Initial Developer are Copyright (C) 2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Benedict Hsieh <bhsieh@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsStartupCacheUtils.h"
-
-#include "nsCOMPtr.h"
-#include "nsComponentManagerUtils.h"
-#include "nsIInputStream.h"
-#include "nsIStorageStream.h"
-#include "nsIStringStream.h"
-#include "nsIObjectInputStream.h"
-#include "nsIObjectOutputStream.h"
-
-nsresult
-NS_NewObjectInputStreamFromBuffer(char* buffer, int len, 
-                                  nsIObjectInputStream** stream)
-{
-  nsCOMPtr<nsIStringInputStream> stringStream
-    = do_CreateInstance("@mozilla.org/io/string-input-stream;1");
-  if (!stringStream)
-    return NS_ERROR_OUT_OF_MEMORY;
-  nsCOMPtr<nsIObjectInputStream> objectInput 
-    = do_CreateInstance("@mozilla.org/binaryinputstream;1");
-  if (!objectInput)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  stringStream->AdoptData(buffer, len);
-  objectInput->SetInputStream(stringStream);
-
-  NS_ADDREF(*stream = objectInput);
-  return NS_OK;
-}
-
-// This is questionable API name and design, but we can't
-// retrieve the wrapped stream from the objectOutputStream later...
-nsresult
-NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
-                                       nsIStorageStream** stream)
-{
-  nsCOMPtr<nsIStorageStream> storageStream;
-  nsresult rv = NS_NewStorageStream(256, (PRUint32)-1, 
-                                    getter_AddRefs(storageStream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIObjectOutputStream> objectOutput
-    = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
-  if (!objectOutput)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  nsCOMPtr<nsIOutputStream> outputStream
-    = do_QueryInterface(storageStream);
-
-  objectOutput->SetOutputStream(outputStream);
-  NS_ADDREF(*wrapperStream = objectOutput);
-  NS_ADDREF(*stream = storageStream);
-  return NS_OK;
-}
-
-nsresult
-NS_NewBufferFromStorageStream(nsIStorageStream *storageStream, 
-                              char** buffer, int* len)
-{
-  nsresult rv;
-  nsCOMPtr<nsIInputStream> inputStream;
-  rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 avail, read;
-  rv = inputStream->Available(&avail);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  char* temp = new char[avail];
-  if (!temp)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  rv = inputStream->Read(temp, avail, &read);
-  if (NS_SUCCEEDED(rv) && avail != read)
-    rv = NS_ERROR_UNEXPECTED;
-
-  if (NS_FAILED(rv)) {
-    delete temp;
-    return rv;
-  }
-
-  *len = avail;
-  *buffer = temp;
-  return NS_OK;
-}
-