--- 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;
+}
+