Merge backout of Bug 592943 due to extensions bustage
authorMichael Wu <mwu@mozilla.com>
Sat, 28 May 2011 00:16:11 -0700
changeset 70980 c8bc6089f86dcafd8b07b4ebf16746e54961be28
parent 70978 e3f62813c742407cce199be2d313302b9fd2c127 (current diff)
parent 70979 20a9cf3dc3a7033ef19c3cd5154ceab42c4713f1 (diff)
child 70981 f13a2020b5b938bf001195abb472eb3be3822e56
push id45
push userffxbld
push dateThu, 22 Sep 2011 17:29:26 +0000
treeherdermozilla-release@b3273da80b44 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs592943
milestone7.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
Merge backout of Bug 592943 due to extensions bustage
startupcache/StartupCacheUtils.cpp
--- a/chrome/src/nsChromeProtocolHandler.cpp
+++ b/chrome/src/nsChromeProtocolHandler.cpp
@@ -240,20 +240,54 @@ 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);
     }
 
-    // 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.
+#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
 
     *aResult = result;
     NS_ADDREF(*aResult);
     return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -79,16 +79,17 @@
 #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"
@@ -2682,19 +2683,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 cache file, in the case where that
+                    // data into the FastLoad file, in the case where that
                     // muxed document is already there (written by a prior
-                    // session, or by an earlier cache episode during this
+                    // session, or by an earlier FastLoad episode during this
                     // session).
                     rv |= script->SerializeOutOfLine(aStream, aGlobal);
                 }
             }
             break;
         }
     }
 
@@ -2814,18 +2815,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 |AbortCaching| which
-            // shuts down the cache and closes our streams.
+            // deserializations included calls to |AbortFastLoads| which
+            // shuts down the FastLoadService 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;
         }
     }
@@ -2955,50 +2956,70 @@ 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 cache file, but the XUL cache is off?");
-    PRBool exists;
-    cache->HasData(mSrcURI, &exists);
-    
-    
+                 "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);
     /* 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;
 
-    nsCOMPtr<nsIObjectOutputStream> oos;
-    rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos));
-    NS_ENSURE_SUCCESS(rv, rv);
-    
-    rv |= Serialize(oos, aGlobal, nsnull);
-    rv |= cache->FinishOutputStream(mSrcURI);
+    // 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));
+    }
 
     if (NS_FAILED(rv))
-        cache->AbortCaching();
+        cache->AbortFastLoads();
     return rv;
 }
 
 
 nsresult
 nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
                                   nsIScriptGlobalObject* aGlobal,
                                   nsIURI* aDocumentURI,
@@ -3029,34 +3050,43 @@ nsXULPrototypeScript::Deserialize(nsIObj
     return NS_OK;
 }
 
 
 nsresult
 nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
                                            nsIScriptGlobalObject* aGlobal)
 {
-    // Keep track of failure via rv, so we can
-    // AbortCaching if things look bad.
+    // Keep track of FastLoad failure via rv, so we can
+    // AbortFastLoads 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 (cache) {
+    if (! objectInput && fastLoadService)
+        fastLoadService->GetInputStream(getter_AddRefs(objectInput));
+
+    if (objectInput) {
         PRBool useXULCache = PR_TRUE;
         if (mSrcURI) {
             // NB: we must check the XUL script cache early, to avoid
-            // multiple deserialization attempts for a given script.            
-            // Note that nsXULDocument::LoadScript
+            // multiple deserialization attempts for a given script, which
+            // would exhaust the multiplexed stream containing the singly
+            // serialized 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 stored in the cache file.
+            // different XUL documents multiplexed in the FastLoad 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
@@ -3070,52 +3100,78 @@ nsXULPrototypeScript::DeserializeOutOfLi
                         return NS_ERROR_UNEXPECTED;
                     }
                     Set(newScriptObject);
                 }
             }
         }
 
         if (! mScriptObject.mObject) {
+            nsCOMPtr<nsIURI> oldURI;
+
             if (mSrcURI) {
-                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.
- 
+                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;
+            }
+
             // 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
-            // error.
+            // FastLoad 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 cache,
+                // If mSrcURI is not in the FastLoad multiplex,
                 // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
-                // update the cache file to hold a serialization of
+                // update the FastLoad file to hold a serialization of
                 // this script, once it has finished loading.
                 if (rv != NS_ERROR_NOT_AVAILABLE)
-                    cache->AbortCaching();
+                    cache->AbortFastLoads();
             }
         }
     }
+
     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 caching process abruptly, removing the cache file.
+     * Stop the FastLoad process abruptly, removing the FastLoad file.
      */
-    virtual void AbortCaching() = 0;
+    virtual void AbortFastLoads() = 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,21 +284,22 @@ 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 table in
+        // Remove the current document here from the FastLoad table in
         // case the document did not make it past StartLayout in
-        // ResumeWalk. 
+        // ResumeWalk. The FastLoad table must be clear of entries so
+        // that the FastLoad file footer can be properly written.
         if (mDocumentURI)
-            nsXULPrototypeCache::GetInstance()->RemoveFromCacheSet(mDocumentURI);
+            nsXULPrototypeCache::GetInstance()->RemoveFromFastLoadSet(mDocumentURI);
     }
 }
 
 nsresult
 NS_NewXULDocument(nsIXULDocument** result)
 {
     NS_PRECONDITION(result != nsnull, "null ptr");
     if (! result)
@@ -445,17 +446,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
-// being cached or not.
+// fastloaded 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
@@ -488,19 +489,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 startup cache,
+    // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
     //                         parse from disk
-    // other: the startup cache file could not be found, probably
+    // other: the FastLoad cache file, XUL.mfl, 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,36 +52,30 @@
 
 #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);
@@ -91,28 +85,33 @@ DisableXULCacheChangedCallback(const cha
     if (cache)
         cache->Flush();
 
     return 0;
 }
 
 //----------------------------------------------------------------------
 
-StartupCache*   nsXULPrototypeCache::gStartupCache = nsnull;
+
+nsIFastLoadService*   nsXULPrototypeCache::gFastLoadService = nsnull;
+nsIFile*              nsXULPrototypeCache::gFastLoadFile = 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)
 
 
@@ -125,23 +124,18 @@ 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())) {
-        return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    if (!(result->mCacheURITable.Init() &&
-          result->mInputStreamTable.Init() &&
-          result->mOutputStreamTable.Init())) {
+          result->mXBLDocTable.Init() &&
+          result->mFastLoadURITable.Init())) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // XXX Ignore return values.
     gDisableXULCache =
         Preferences::GetBool(kDisableXULCachePref, gDisableXULCache);
     nsContentUtils::RegisterPrefCallback(kDisableXULCachePref,
                                          DisableXULCacheChangedCallback,
@@ -170,76 +164,84 @@ nsXULPrototypeCache::GetInstance()
 
         CallGetService(kXULPrototypeCacheCID, &cache);
 
         sInstance = static_cast<nsXULPrototypeCache*>(cache);
     }
     return sInstance;
 }
 
-/* static */ StartupCache*
-nsXULPrototypeCache::GetStartupCache()
+/* static */ nsIFastLoadService*
+nsXULPrototypeCache::GetFastLoadService()
 {
-    return gStartupCache;
+    return gFastLoadService;
 }
 
 //----------------------------------------------------------------------
 
 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")) {
-        AbortCaching();
+        AbortFastLoads();
     }
     else {
         NS_WARNING("Unexpected observer topic.");
     }
     return NS_OK;
 }
 
 nsXULPrototypeDocument*
 nsXULPrototypeCache::GetPrototype(nsIURI* aURI)
 {
     nsXULPrototypeDocument* protoDoc = mPrototypeTable.GetWeak(aURI);
-    if (protoDoc)
-        return protoDoc;
 
-    nsresult rv = BeginCaching(aURI);
-    if (NS_FAILED(rv))
-        return nsnull;
+    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));
 
-    // 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;
+                // 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;
+            }
+        }
     }
-    
-    mInputStreamTable.Remove(aURI);
-    RemoveFromCacheSet(aURI);
-    return newProto;
+    return protoDoc;
 }
 
 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);
@@ -398,332 +400,487 @@ nsXULPrototypeCache::Flush()
 
 
 PRBool
 nsXULPrototypeCache::IsEnabled()
 {
     return !gDisableXULCache;
 }
 
-static PRBool gDisableXULDiskCache = PR_FALSE;           // enabled by default
+static PRBool gDisableXULFastLoad = PR_FALSE;           // enabled by default
+static PRBool gChecksumXULFastLoadFile = PR_TRUE;       // XXXbe too paranoid
 
 void
-nsXULPrototypeCache::AbortCaching()
+nsXULPrototypeCache::AbortFastLoads()
 {
 #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 cache set
-    mCacheURITable.Clear();
+    // 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);
 }
 
 
-static const char kDisableXULDiskCachePref[] = "nglayout.debug.disable_xul_fastload";
+void
+nsXULPrototypeCache::RemoveFromFastLoadSet(nsIURI* aURI)
+{
+    mFastLoadURITable.Remove(aURI);
+}
 
-void
-nsXULPrototypeCache::RemoveFromCacheSet(nsIURI* 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 startupcache service has been initialized, probably because
+    // We're here before the FastLoad service has been initialized, probably because
     // of the profile manager. Bail quietly, don't worry, we'll be back later.
-    if (!gStartupCache)
+    if (! gFastLoadService)
         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 cache table. We use the table's
-    // emptiness instead of a counter to decide when the caching process 
-    // has completed.
-    RemoveFromCacheSet(protoURI);
+    // 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);
+        }
 
-    PRInt32 count = mCacheURITable.Count();
-    nsCOMPtr<nsIObjectOutputStream> oos;
-    rv = GetOutputStream(protoURI, getter_AddRefs(oos));
-    NS_ENSURE_SUCCESS(rv, rv);
+        // 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();
 
-    rv = aPrototypeDocument->Write(oos);
-    NS_ENSURE_SUCCESS(rv, rv);
-    FinishOutputStream(protoURI);
+            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);
+    }
+
     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::FinishInputStream(nsIURI* uri) {
-    mInputStreamTable.Remove(uri);
-    return NS_OK;
-}
-
-nsresult
-nsXULPrototypeCache::GetOutputStream(nsIURI* uri, nsIObjectOutputStream** stream)
-{
-    nsresult 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;
-}
 
 nsresult
-nsXULPrototypeCache::FinishOutputStream(nsIURI* uri) 
+nsXULPrototypeCache::StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags)
 {
     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;
-}
+    nsCAutoString urlspec;
+    rv = aURI->GetAsciiSpec(urlspec);
+    if (NS_FAILED(rv)) 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;
+    // 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);
 }
 
 static int
-CachePrefChangedCallback(const char* aPref, void* aClosure)
+FastLoadPrefChangedCallback(const char* aPref, void* aClosure)
 {
-    PRBool wasEnabled = !gDisableXULDiskCache;
-    gDisableXULDiskCache =
-        Preferences::GetBool(kDisableXULCachePref,
-                             gDisableXULDiskCache);
+    PRBool wasEnabled = !gDisableXULFastLoad;
+    gDisableXULFastLoad =
+        Preferences::GetBool(kDisableXULFastLoadPref, gDisableXULFastLoad);
 
-    if (wasEnabled && gDisableXULDiskCache) {
+    if (wasEnabled && gDisableXULFastLoad) {
         static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
         nsCOMPtr<nsIXULPrototypeCache> cache =
             do_GetService(kXULPrototypeCacheCID);
 
         if (cache)
-            cache->AbortCaching();
+            cache->AbortFastLoads();
     }
+
+    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::BeginCaching(nsIURI* aURI)
+nsXULPrototypeCache::StartFastLoad(nsIURI* aURI)
 {
     nsresult rv;
 
     nsCAutoString path;
     aURI->GetPath(path);
     if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul")))
         return NS_ERROR_NOT_AVAILABLE;
 
-    // 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
+    // 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
     // 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.
     //
-    if (gStartupCache) {
-        mCacheURITable.Put(aURI, 1);
+    // 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);
 
         return NS_OK;
     }
 
     // Use a local to refer to the service till we're sure we succeeded, then
-    // commit to gStartupCache.
-    StartupCache* startupCache = StartupCache::GetSingleton();
-    if (!startupCache)
+    // commit to gFastLoadService.  Same for gFastLoadFile, which is used to
+    // delete the FastLoad file on abort.
+    nsCOMPtr<nsIFastLoadService> fastLoadService(do_GetFastLoadService());
+    if (! fastLoadService)
         return NS_ERROR_FAILURE;
 
-    gDisableXULDiskCache =
-        Preferences::GetBool(kDisableXULCachePref, gDisableXULDiskCache);
-
-    nsContentUtils::RegisterPrefCallback(kDisableXULCachePref,
-                                         CachePrefChangedCallback,
+    gDisableXULFastLoad =
+        Preferences::GetBool(kDisableXULFastLoadPref, gDisableXULFastLoad);
+    gChecksumXULFastLoadFile =
+        Preferences::GetBool(kChecksumXULFastLoadFilePref,
+                             gChecksumXULFastLoadFile);
+    nsContentUtils::RegisterPrefCallback(kDisableXULFastLoadPref,
+                                         FastLoadPrefChangedCallback,
+                                         nsnull);
+    nsContentUtils::RegisterPrefCallback(kChecksumXULFastLoadFilePref,
+                                         FastLoadPrefChangedCallback,
                                          nsnull);
 
-    if (gDisableXULDiskCache)
+    if (gDisableXULFastLoad)
         return NS_ERROR_NOT_AVAILABLE;
 
     // Get the chrome directory to validate against the one stored in the
-    // cache file, or to store there if we're generating a new file.
+    // FastLoad 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 cached chrome URIs....
+    // all subsequent packages of FastLoaded 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;
 
-    nsCAutoString fileChromePath, fileLocale;
-    
-    nsAutoArrayPtr<char> buf;
-    PRUint32 len, amtRead;
-    nsCOMPtr<nsIObjectInputStream> objectInput;
+    // 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));
 
-    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_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;
 
-    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)) {
-            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;
+                        rv = objectInput->ReadCString(fileChromePath);
+                        rv |= objectInput->ReadCString(fileLocale);
+                        if (NS_SUCCEEDED(rv) &&
+                            (!fileChromePath.Equals(chromePath) ||
+                             !fileLocale.Equals(locale))) {
+                            rv = NS_ERROR_UNEXPECTED;
+                        }
+                    }
+                }
             }
         }
 
-        // Failed again, just bail.
-        if (NS_FAILED(rv)) {
-            startupCache->InvalidateCache();
-            return NS_ERROR_FAILURE;
+        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;
         }
     }
 
-    // Success!  Insert this URI into the mCacheURITable
+    // 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());
+        }
+
+        // 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.
-    mCacheURITable.Put(aURI, 1);
+    mFastLoadURITable.Put(aURI, 1);
 
-    gStartupCache = startupCache;
+    NS_ADDREF(gFastLoadService = fastLoadService);
+    NS_ADDREF(gFastLoadFile = file);
     return NS_OK;
 }
+
--- a/content/xul/document/src/nsXULPrototypeCache.h
+++ b/content/xul/document/src/nsXULPrototypeCache.h
@@ -47,64 +47,70 @@
 #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"
 
-using namespace mozilla::scache;
-
+class nsIFastLoadService;
 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 cache file.
+ *  2. The on-disk fastload 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 AbortCaching();
+    virtual void AbortFastLoads();
 
 
     /**
      * 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);
@@ -124,40 +130,19 @@ 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
@@ -172,21 +157,21 @@ protected:
     void FlushSkinFiles();
 
     nsRefPtrHashtable<nsURIHashKey,nsXULPrototypeDocument>  mPrototypeTable; // owns the prototypes
     nsRefPtrHashtable<nsURIHashKey,nsCSSStyleSheet>        mStyleSheetTable;
     nsDataHashtable<nsURIHashKey,CacheScriptEntry>         mScriptTable;
     nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo>  mXBLDocTable;
 
     ///////////////////////////////////////////////////////////////////////////
-    // StartupCache
+    // FastLoad
     // this is really a hash set, with a dummy data parameter
-    nsDataHashtable<nsURIHashKey,PRUint32> mCacheURITable;
+    nsDataHashtable<nsURIHashKey,PRUint32> mFastLoadURITable;
 
-    static StartupCache* gStartupCache;
-    nsInterfaceHashtable<nsURIHashKey, nsIStorageStream> mOutputStreamTable;
-    nsInterfaceHashtable<nsURIHashKey, nsIObjectInputStream> mInputStreamTable;
- 
-    // Bootstrap caching service
-    nsresult BeginCaching(nsIURI* aDocumentURI);
+    static nsIFastLoadService*    gFastLoadService;
+    static nsIFile*               gFastLoadFile;
+
+    // Bootstrap FastLoad Service
+    nsresult StartFastLoad(nsIURI* aDocumentURI);
+    nsresult StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags);
 };
 
 #endif // nsXULPrototypeCache_h__
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -413,23 +413,16 @@ 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,18 +964,17 @@ 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),
-                                                true);
+                                                getter_AddRefs(storageStream));
     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,43 +22,38 @@ NS_NewObjectInputStreamFromBuffer(char* 
   objectInput->SetInputStream(stringStream);
   
   objectInput.forget(stream);
   return NS_OK;
 }
 
 NS_EXPORT nsresult
 NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
-                                       nsIStorageStream** stream,
-				       PRBool wantDebugStream)
+                                       nsIStorageStream** stream)
 {
   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
-  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);
-  }
+  // 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);
 #endif
   
   storageStream.forget(stream);
   return NS_OK;
 }
 
--- a/startupcache/StartupCacheUtils.h
+++ b/startupcache/StartupCacheUtils.h
@@ -45,24 +45,19 @@
 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. 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).
+// so we return it here.
 NS_EXPORT nsresult
 NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
-                                       nsIStorageStream** stream,
-                                       PRBool wantDebugStream);
+                                       nsIStorageStream** stream);
 
 NS_EXPORT nsresult
 NS_NewBufferFromStorageStream(nsIStorageStream *storageStream, 
                               char** buffer, PRUint32* len);
 }
 }
 #endif //nsStartupCacheUtils_h_
new file mode 100644
--- /dev/null
+++ b/startupcache/nsStartupCacheUtils.cpp
@@ -0,0 +1,124 @@
+/* -*-  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;
+}
+