Bug 592943 - (3/3) switch nsXULPrototypeCache to use startupCache instead of fastload, Patch by Benedict Hsieh, r=jst

This commit is contained in:
Michael Wu 2010-08-12 12:42:36 -07:00
Родитель 248be115fa
Коммит 6926026b3e
11 изменённых файлов: 375 добавлений и 714 удалений

Просмотреть файл

@ -245,45 +245,11 @@ nsChromeProtocolHandler::NewChannel(nsIURI* aURI,
result->SetOwner(owner); result->SetOwner(owner);
} }
#ifdef MOZ_XUL // XXX Removed dependency-tracking code from here, because we're not
// Track FastLoad file dependencies. // tracking them anyways (with fastload we checked only in DEBUG
// // and with startupcache not at all), but this is where we would start
// This is harder than it ought to be! While an nsResChannel "is-a" // if we need to re-add.
// nsIFileChannel, an nsJARChannel is not. Once you unravel the jar: // See bug 531886, bug 533038.
// 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; *aResult = result;
NS_ADDREF(*aResult); NS_ADDREF(*aResult);

Просмотреть файл

@ -84,7 +84,6 @@
#include "nsIEventListenerManager.h" #include "nsIEventListenerManager.h"
#include "nsEventStateManager.h" #include "nsEventStateManager.h"
#include "nsFocusManager.h" #include "nsFocusManager.h"
#include "nsIFastLoadService.h"
#include "nsHTMLStyleSheet.h" #include "nsHTMLStyleSheet.h"
#include "nsINameSpaceManager.h" #include "nsINameSpaceManager.h"
#include "nsIObjectInputStream.h" #include "nsIObjectInputStream.h"
@ -2688,9 +2687,9 @@ nsXULPrototypeElement::Serialize(nsIObjectOutputStream* aStream,
if (script->mScriptObject.mObject) { if (script->mScriptObject.mObject) {
// This may return NS_OK without muxing script->mSrcURI's // This may return NS_OK without muxing script->mSrcURI's
// data into the FastLoad file, in the case where that // data into the cache file, in the case where that
// muxed document is already there (written by a prior // muxed document is already there (written by a prior
// session, or by an earlier FastLoad episode during this // session, or by an earlier cache episode during this
// session). // session).
rv |= script->SerializeOutOfLine(aStream, aGlobal); rv |= script->SerializeOutOfLine(aStream, aGlobal);
} }
@ -2820,8 +2819,8 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
// Oh dear. Something failed during the deserialization. // Oh dear. Something failed during the deserialization.
// We don't know what. But likely consequences of failed // We don't know what. But likely consequences of failed
// deserializations included calls to |AbortFastLoads| which // deserializations included calls to |AbortCaching| which
// shuts down the FastLoadService and closes our streams. // shuts down the cache and closes our streams.
// If that happens, next time through this loop, we die a messy // If that happens, next time through this loop, we die a messy
// death. So, let's just fail now, and propagate that failure // death. So, let's just fail now, and propagate that failure
// upward so that the ChromeProtocolHandler knows it can't use // upward so that the ChromeProtocolHandler knows it can't use
@ -2961,24 +2960,23 @@ nsresult
nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream, nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
nsIScriptGlobalObject* aGlobal) 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(); nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
if (!cache) if (!cache)
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
NS_ASSERTION(cache->IsEnabled(), NS_ASSERTION(cache->IsEnabled(),
"writing to the FastLoad file, but the XUL cache is off?"); "writing to the cache file, but the XUL cache is off?");
PRBool exists;
nsIFastLoadService* fastLoadService = cache->GetFastLoadService(); cache->HasData(mSrcURI, &exists);
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. /* return will be NS_OK from GetAsciiSpec.
* that makes no sense. * that makes no sense.
* nor does returning NS_OK from HasMuxedDocument. * nor does returning NS_OK from HasMuxedDocument.
@ -2987,34 +2985,15 @@ nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
if (exists) if (exists)
return NS_OK; return NS_OK;
// Allow callers to pass null for aStream, meaning nsCOMPtr<nsIObjectOutputStream> oos;
// "use the FastLoad service's default output stream." rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos));
// See nsXULDocument.cpp for one use of this. NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObjectOutputStream> objectOutput = aStream;
if (! objectOutput) { rv |= Serialize(oos, aGlobal, nsnull);
fastLoadService->GetOutputStream(getter_AddRefs(objectOutput)); rv |= cache->FinishOutputStream(mSrcURI);
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)) if (NS_FAILED(rv))
cache->AbortFastLoads(); cache->AbortCaching();
return rv; return rv;
} }
@ -3055,33 +3034,24 @@ nsresult
nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput, nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
nsIScriptGlobalObject* aGlobal) nsIScriptGlobalObject* aGlobal)
{ {
// Keep track of FastLoad failure via rv, so we can // Keep track of failure via rv, so we can
// AbortFastLoads if things look bad. // AbortCaching if things look bad.
nsresult rv = NS_OK; nsresult rv = NS_OK;
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); 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; nsCOMPtr<nsIObjectInputStream> objectInput = aInput;
if (! objectInput && fastLoadService) if (cache) {
fastLoadService->GetInputStream(getter_AddRefs(objectInput));
if (objectInput) {
PRBool useXULCache = PR_TRUE; PRBool useXULCache = PR_TRUE;
if (mSrcURI) { if (mSrcURI) {
// NB: we must check the XUL script cache early, to avoid // NB: we must check the XUL script cache early, to avoid
// multiple deserialization attempts for a given script, which // multiple deserialization attempts for a given script.
// would exhaust the multiplexed stream containing the singly // Note that nsXULDocument::LoadScript
// serialized script. Note that nsXULDocument::LoadScript
// checks the XUL script cache too, in order to handle the // checks the XUL script cache too, in order to handle the
// serialization case. // serialization case.
// //
// We need do this only for <script src='strres.js'> and the // We need do this only for <script src='strres.js'> and the
// like, i.e., out-of-line scripts that are included by several // like, i.e., out-of-line scripts that are included by several
// different XUL documents multiplexed in the FastLoad file. // different XUL documents stored in the cache file.
useXULCache = cache->IsEnabled(); useXULCache = cache->IsEnabled();
if (useXULCache) { if (useXULCache) {
@ -3105,47 +3075,21 @@ nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
} }
if (! mScriptObject.mObject) { if (! mScriptObject.mObject) {
nsCOMPtr<nsIURI> oldURI;
if (mSrcURI) { if (mSrcURI) {
nsCAutoString spec; rv = cache->GetInputStream(mSrcURI, getter_AddRefs(objectInput));
mSrcURI->GetAsciiSpec(spec); }
rv = fastLoadService->StartMuxedDocument(mSrcURI, spec.get(), // If !mSrcURI, we have an inline script. We shouldn't have
nsIFastLoadService::NS_FASTLOAD_READ); // to do anything else in that case, I think.
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 // We do reflect errors into rv, but our caller may want to
// ignore our return value, because mScriptObject will be null // ignore our return value, because mScriptObject will be null
// after any error, and that suffices to cause the script to // after any error, and that suffices to cause the script to
// be reloaded (from the src= URI, if any) and recompiled. // be reloaded (from the src= URI, if any) and recompiled.
// We're better off slow-loading than bailing out due to a // We're better off slow-loading than bailing out due to a
// FastLoad error. // error.
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv))
rv = Deserialize(objectInput, aGlobal, nsnull, nsnull); 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 (NS_SUCCEEDED(rv)) {
if (useXULCache && mSrcURI) { if (useXULCache && mSrcURI) {
PRBool isChrome = PR_FALSE; PRBool isChrome = PR_FALSE;
@ -3156,17 +3100,17 @@ nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
mScriptObject.mObject); mScriptObject.mObject);
} }
} }
cache->FinishInputStream(mSrcURI);
} else { } else {
// If mSrcURI is not in the FastLoad multiplex, // If mSrcURI is not in the cache,
// rv will be NS_ERROR_NOT_AVAILABLE and we'll try to // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
// update the FastLoad file to hold a serialization of // update the cache file to hold a serialization of
// this script, once it has finished loading. // this script, once it has finished loading.
if (rv != NS_ERROR_NOT_AVAILABLE) if (rv != NS_ERROR_NOT_AVAILABLE)
cache->AbortFastLoads(); cache->AbortCaching();
} }
} }
} }
return rv; return rv;
} }

Просмотреть файл

@ -65,9 +65,9 @@ public:
virtual PRBool IsCached(nsIURI* aURI) = 0; virtual PRBool IsCached(nsIURI* aURI) = 0;
/** /**
* Stop the FastLoad process abruptly, removing the FastLoad file. * Stop the caching process abruptly, removing the cache file.
*/ */
virtual void AbortFastLoads() = 0; virtual void AbortCaching() = 0;
}; };
NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULPrototypeCache, NS_IXULPROTOTYPECACHE_IID) NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULPrototypeCache, NS_IXULPROTOTYPECACHE_IID)

Просмотреть файл

@ -289,12 +289,11 @@ nsXULDocument::~nsXULDocument()
NS_IF_RELEASE(kNC_attribute); NS_IF_RELEASE(kNC_attribute);
NS_IF_RELEASE(kNC_value); NS_IF_RELEASE(kNC_value);
// Remove the current document here from the FastLoad table in // Remove the current document here from the table in
// case the document did not make it past StartLayout in // case the document did not make it past StartLayout in
// ResumeWalk. The FastLoad table must be clear of entries so // ResumeWalk.
// that the FastLoad file footer can be properly written.
if (mDocumentURI) if (mDocumentURI)
nsXULPrototypeCache::GetInstance()->RemoveFromFastLoadSet(mDocumentURI); nsXULPrototypeCache::GetInstance()->RemoveFromCacheSet(mDocumentURI);
} }
} }
@ -451,7 +450,7 @@ nsXULDocument::SetContentType(const nsAString& aContentType)
} }
// This is called when the master document begins loading, whether it's // This is called when the master document begins loading, whether it's
// fastloaded or not. // being cached or not.
nsresult nsresult
nsXULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, nsXULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
nsILoadGroup* aLoadGroup, nsILoadGroup* aLoadGroup,
@ -494,9 +493,9 @@ nsXULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
// to trigger the fail-safe parse-from-disk solution. Example failure cases // to trigger the fail-safe parse-from-disk solution. Example failure cases
// (for reference) include: // (for reference) include:
// //
// NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache, // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
// parse from disk // parse from disk
// other: the FastLoad cache file, XUL.mfl, could not be found, probably // other: the startup cache file could not be found, probably
// due to being accessed before a profile has been selected (e.g. // due to being accessed before a profile has been selected (e.g.
// loading chrome for the profile manager itself). This must be // loading chrome for the profile manager itself). This must be
// parsed from disk. // parsed from disk.

Просмотреть файл

@ -57,6 +57,8 @@
#include "nsIObjectInputStream.h" #include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h" #include "nsIObjectOutputStream.h"
#include "nsIObserverService.h" #include "nsIObserverService.h"
#include "nsIStringStream.h"
#include "nsIStorageStream.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsAppDirectoryServiceDefs.h" #include "nsAppDirectoryServiceDefs.h"
@ -64,13 +66,17 @@
#include "jsxdrapi.h" #include "jsxdrapi.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/scache/StartupCache.h"
#include "mozilla/scache/StartupCacheUtils.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::scache;
static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
static PRBool gDisableXULCache = PR_FALSE; // enabled by default static PRBool gDisableXULCache = PR_FALSE; // enabled by default
static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache"; static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache";
static const char kXULCacheInfoKey[] = "nsXULPrototypeCache.startupCache";
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -90,9 +96,7 @@ DisableXULCacheChangedCallback(const char* aPref, void* aClosure)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
StartupCache* nsXULPrototypeCache::gStartupCache = nsnull;
nsIFastLoadService* nsXULPrototypeCache::gFastLoadService = nsnull;
nsIFile* nsXULPrototypeCache::gFastLoadFile = nsnull;
nsXULPrototypeCache* nsXULPrototypeCache::sInstance = nsnull; nsXULPrototypeCache* nsXULPrototypeCache::sInstance = nsnull;
@ -104,9 +108,6 @@ nsXULPrototypeCache::nsXULPrototypeCache()
nsXULPrototypeCache::~nsXULPrototypeCache() nsXULPrototypeCache::~nsXULPrototypeCache()
{ {
FlushScripts(); FlushScripts();
NS_IF_RELEASE(gFastLoadService); // don't need ReleaseService nowadays!
NS_IF_RELEASE(gFastLoadFile);
} }
@ -129,8 +130,13 @@ NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult)
if (!(result->mPrototypeTable.Init() && if (!(result->mPrototypeTable.Init() &&
result->mStyleSheetTable.Init() && result->mStyleSheetTable.Init() &&
result->mScriptTable.Init() && result->mScriptTable.Init() &&
result->mXBLDocTable.Init() && result->mXBLDocTable.Init())) {
result->mFastLoadURITable.Init())) { return NS_ERROR_OUT_OF_MEMORY;
}
if (!(result->mCacheURITable.Init() &&
result->mInputStreamTable.Init() &&
result->mOutputStreamTable.Init())) {
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
@ -169,10 +175,10 @@ nsXULPrototypeCache::GetInstance()
return sInstance; return sInstance;
} }
/* static */ nsIFastLoadService* /* static */ StartupCache*
nsXULPrototypeCache::GetFastLoadService() nsXULPrototypeCache::GetStartupCache()
{ {
return gFastLoadService; return gStartupCache;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -189,7 +195,7 @@ nsXULPrototypeCache::Observe(nsISupports* aSubject,
Flush(); Flush();
} }
else if (!strcmp(aTopic, "startupcache-invalidate")) { else if (!strcmp(aTopic, "startupcache-invalidate")) {
AbortFastLoads(); AbortCaching();
} }
else { else {
NS_WARNING("Unexpected observer topic."); NS_WARNING("Unexpected observer topic.");
@ -201,42 +207,34 @@ nsXULPrototypeDocument*
nsXULPrototypeCache::GetPrototype(nsIURI* aURI) nsXULPrototypeCache::GetPrototype(nsIURI* aURI)
{ {
nsXULPrototypeDocument* protoDoc = mPrototypeTable.GetWeak(aURI); nsXULPrototypeDocument* protoDoc = mPrototypeTable.GetWeak(aURI);
if (protoDoc)
return protoDoc;
if (!protoDoc) { nsresult rv = BeginCaching(aURI);
// No prototype in XUL memory cache. Spin up FastLoad Service and if (NS_FAILED(rv))
// look in FastLoad file. return nsnull;
nsresult rv = StartFastLoad(aURI);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIObjectInputStream> objectInput;
gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
rv = StartFastLoadingURI(aURI, nsIFastLoadService::NS_FASTLOAD_READ); // No prototype in XUL memory cache. Spin up the cache Service.
if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIObjectInputStream> ois;
nsCOMPtr<nsIURI> oldURI; rv = GetInputStream(aURI, getter_AddRefs(ois));
gFastLoadService->SelectMuxedDocument(aURI, getter_AddRefs(oldURI)); if (NS_FAILED(rv))
return nsnull;
// Create a new prototype document.
nsRefPtr<nsXULPrototypeDocument> newProto; nsRefPtr<nsXULPrototypeDocument> newProto;
rv = NS_NewXULPrototypeDocument(getter_AddRefs(newProto)); rv = NS_NewXULPrototypeDocument(getter_AddRefs(newProto));
if (NS_FAILED(rv)) return nsnull; if (NS_FAILED(rv))
return nsnull;
rv = newProto->Read(objectInput);
if (NS_SUCCEEDED(rv)) { rv = newProto->Read(ois);
rv = PutPrototype(newProto); if (NS_SUCCEEDED(rv)) {
if (NS_FAILED(rv)) rv = PutPrototype(newProto);
newProto = nsnull; } else {
newProto = nsnull;
gFastLoadService->EndMuxedDocument(aURI);
} else {
newProto = nsnull;
}
RemoveFromFastLoadSet(aURI);
protoDoc = newProto;
}
}
} }
return protoDoc;
mInputStreamTable.Remove(aURI);
RemoveFromCacheSet(aURI);
return newProto;
} }
nsresult nsresult
@ -405,11 +403,10 @@ nsXULPrototypeCache::IsEnabled()
return !gDisableXULCache; return !gDisableXULCache;
} }
static PRBool gDisableXULFastLoad = PR_FALSE; // enabled by default static PRBool gDisableXULDiskCache = PR_FALSE; // enabled by default
static PRBool gChecksumXULFastLoadFile = PR_TRUE; // XXXbe too paranoid
void void
nsXULPrototypeCache::AbortFastLoads() nsXULPrototypeCache::AbortCaching()
{ {
#ifdef DEBUG_brendan #ifdef DEBUG_brendan
NS_BREAK(); NS_BREAK();
@ -419,282 +416,187 @@ nsXULPrototypeCache::AbortFastLoads()
// script, somehow. // script, somehow.
Flush(); Flush();
// Clear the FastLoad set // Clear the cache set
mFastLoadURITable.Clear(); mCacheURITable.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 void
nsXULPrototypeCache::RemoveFromFastLoadSet(nsIURI* aURI) nsXULPrototypeCache::RemoveFromCacheSet(nsIURI* aURI)
{ {
mFastLoadURITable.Remove(aURI); mCacheURITable.Remove(aURI);
} }
static const char kDisableXULFastLoadPref[] = "nglayout.debug.disable_xul_fastload";
static const char kChecksumXULFastLoadFilePref[] = "nglayout.debug.checksum_xul_fastload_file";
nsresult nsresult
nsXULPrototypeCache::WritePrototype(nsXULPrototypeDocument* aPrototypeDocument) nsXULPrototypeCache::WritePrototype(nsXULPrototypeDocument* aPrototypeDocument)
{ {
nsresult rv = NS_OK, rv2 = NS_OK; nsresult rv = NS_OK, rv2 = NS_OK;
// We're here before the FastLoad service has been initialized, probably because // We're here before the startupcache service has been initialized, probably because
// of the profile manager. Bail quietly, don't worry, we'll be back later. // of the profile manager. Bail quietly, don't worry, we'll be back later.
if (! gFastLoadService) if (!gStartupCache)
return NS_OK; 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(); nsCOMPtr<nsIURI> protoURI = aPrototypeDocument->GetURI();
// Remove this document from the FastLoad table. We use the table's // Remove this document from the cache table. We use the table's
// emptiness instead of a counter to decide when the FastLoad process // emptiness instead of a counter to decide when the caching process
// has completed. When complete, we can write footer details to the // has completed.
// FastLoad file. RemoveFromCacheSet(protoURI);
RemoveFromFastLoadSet(protoURI);
PRInt32 count = mFastLoadURITable.Count(); PRInt32 count = mCacheURITable.Count();
nsCOMPtr<nsIObjectOutputStream> oos;
if (objectOutput) { rv = GetOutputStream(protoURI, getter_AddRefs(oos));
rv = StartFastLoadingURI(protoURI, nsIFastLoadService::NS_FASTLOAD_WRITE); NS_ENSURE_SUCCESS(rv, rv);
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);
}
// 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();
if (NS_SUCCEEDED(rv) && gChecksumXULFastLoadFile) {
rv = gFastLoadService->CacheChecksum(gFastLoadFile,
objectOutput);
}
}
}
if (objectInput) {
// If this is the last of one or more XUL master documents loaded
// together at app startup, close the FastLoad service's singleton
// input stream now.
if (count == 0) {
gFastLoadService->SetInputStream(nsnull);
rv2 = objectInput->Close();
}
}
// If the list is empty now, the FastLoad process is done.
if (count == 0) {
NS_RELEASE(gFastLoadService);
NS_RELEASE(gFastLoadFile);
}
rv = aPrototypeDocument->Write(oos);
NS_ENSURE_SUCCESS(rv, rv);
FinishOutputStream(protoURI);
return NS_FAILED(rv) ? rv : rv2; 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 nsresult
nsXULPrototypeCache::StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags) nsXULPrototypeCache::FinishInputStream(nsIURI* uri) {
mInputStreamTable.Remove(uri);
return NS_OK;
}
nsresult
nsXULPrototypeCache::GetOutputStream(nsIURI* uri, nsIObjectOutputStream** stream)
{ {
nsresult rv; 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;
}
nsCAutoString urlspec; nsresult
rv = aURI->GetAsciiSpec(urlspec); nsXULPrototypeCache::FinishOutputStream(nsIURI* uri)
if (NS_FAILED(rv)) return rv; {
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);
// If StartMuxedDocument returns NS_ERROR_NOT_AVAILABLE, then nsCAutoString spec;
// we must be reading the file, and urlspec was not associated uri->GetPath(spec);
// with any multiplexed stream in it. The FastLoad service rv = gStartupCache->PutBuffer(spec.get(), buf, len);
// will therefore arrange to update the file, writing new data if (NS_SUCCEEDED(rv))
// at the end while old (available) data continues to be read mOutputStreamTable.Remove(uri);
// from the pre-existing part of the file.
return gFastLoadService->StartMuxedDocument(aURI, urlspec.get(), aDirectionFlags); return rv;
}
// We have data if we're in the middle of writing it or we already
// have it in the cache.
nsresult
nsXULPrototypeCache::HasData(nsIURI* uri, PRBool* exists)
{
if (mOutputStreamTable.Get(uri, nsnull)) {
*exists = PR_TRUE;
return NS_OK;
}
nsCAutoString spec;
uri->GetPath(spec);
nsAutoArrayPtr<char> buf;
PRUint32 len;
nsresult rv;
if (gStartupCache)
rv = gStartupCache->GetBuffer(spec.get(), getter_Transfers(buf),
&len);
else {
// We don't have everything we need to call BeginCaching and set up
// gStartupCache right now, but we just need to check the cache for
// this URI.
StartupCache* sc = StartupCache::GetSingleton();
if (!sc) {
*exists = PR_FALSE;
return NS_OK;
}
rv = sc->GetBuffer(spec.get(), getter_Transfers(buf), &len);
}
*exists = NS_SUCCEEDED(rv);
return NS_OK;
} }
static int static int
FastLoadPrefChangedCallback(const char* aPref, void* aClosure) CachePrefChangedCallback(const char* aPref, void* aClosure)
{ {
PRBool wasEnabled = !gDisableXULFastLoad; PRBool wasEnabled = !gDisableXULDiskCache;
gDisableXULFastLoad = gDisableXULDiskCache =
Preferences::GetBool(kDisableXULFastLoadPref, gDisableXULFastLoad); Preferences::GetBool(kDisableXULCachePref,
gDisableXULDiskCache);
if (wasEnabled && gDisableXULFastLoad) { if (wasEnabled && gDisableXULDiskCache) {
static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
nsCOMPtr<nsIXULPrototypeCache> cache = nsCOMPtr<nsIXULPrototypeCache> cache =
do_GetService(kXULPrototypeCacheCID); do_GetService(kXULPrototypeCacheCID);
if (cache) if (cache)
cache->AbortFastLoads(); cache->AbortCaching();
} }
gChecksumXULFastLoadFile =
Preferences::GetBool(kChecksumXULFastLoadFilePref,
gChecksumXULFastLoadFile);
return 0; 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 nsresult
nsXULPrototypeCache::StartFastLoad(nsIURI* aURI) nsXULPrototypeCache::BeginCaching(nsIURI* aURI)
{ {
nsresult rv; nsresult rv;
@ -703,47 +605,37 @@ nsXULPrototypeCache::StartFastLoad(nsIURI* aURI)
if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul"))) if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul")))
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
// Test gFastLoadFile to decide whether this is the first nsXULDocument // Test gStartupCache to decide whether this is the first nsXULDocument
// participating in FastLoad. If gFastLoadFile is non-null, this document // participating in the serialization. If gStartupCache is non-null, this document
// must not be first, but it can join the FastLoad process. Examples of // must not be first, but it can join the process. Examples of
// multiple master documents participating include hiddenWindow.xul and // multiple master documents participating include hiddenWindow.xul and
// navigator.xul on the Mac, and multiple-app-component (e.g., mailnews // navigator.xul on the Mac, and multiple-app-component (e.g., mailnews
// and browser) startup due to command-line arguments. // and browser) startup due to command-line arguments.
// //
// XXXbe we should attempt to update the FastLoad file after startup! if (gStartupCache) {
// mCacheURITable.Put(aURI, 1);
// 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; return NS_OK;
} }
// Use a local to refer to the service till we're sure we succeeded, then // Use a local to refer to the service till we're sure we succeeded, then
// commit to gFastLoadService. Same for gFastLoadFile, which is used to // commit to gStartupCache.
// delete the FastLoad file on abort. StartupCache* startupCache = StartupCache::GetSingleton();
nsCOMPtr<nsIFastLoadService> fastLoadService(do_GetFastLoadService()); if (!startupCache)
if (! fastLoadService)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
gDisableXULFastLoad = gDisableXULDiskCache =
Preferences::GetBool(kDisableXULFastLoadPref, gDisableXULFastLoad); Preferences::GetBool(kDisableXULCachePref, gDisableXULDiskCache);
gChecksumXULFastLoadFile =
Preferences::GetBool(kChecksumXULFastLoadFilePref, nsContentUtils::RegisterPrefCallback(kDisableXULCachePref,
gChecksumXULFastLoadFile); CachePrefChangedCallback,
nsContentUtils::RegisterPrefCallback(kDisableXULFastLoadPref,
FastLoadPrefChangedCallback,
nsnull);
nsContentUtils::RegisterPrefCallback(kChecksumXULFastLoadFilePref,
FastLoadPrefChangedCallback,
nsnull); nsnull);
if (gDisableXULFastLoad) if (gDisableXULDiskCache)
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
// Get the chrome directory to validate against the one stored in the // Get the chrome directory to validate against the one stored in the
// FastLoad file, or to store there if we're generating a new file. // cache file, or to store there if we're generating a new file.
nsCOMPtr<nsIFile> chromeDir; nsCOMPtr<nsIFile> chromeDir;
rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir)); rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir));
if (NS_FAILED(rv)) if (NS_FAILED(rv))
@ -753,134 +645,85 @@ nsXULPrototypeCache::StartFastLoad(nsIURI* aURI)
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return 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 // XXXbe we assume the first package's locale is the same as the locale of
// all subsequent packages of FastLoaded chrome URIs.... // all subsequent packages of cached chrome URIs....
nsCAutoString package; nsCAutoString package;
rv = aURI->GetHost(package); rv = aURI->GetHost(package);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
nsCOMPtr<nsIXULChromeRegistry> chromeReg
= do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv);
nsCAutoString locale; nsCAutoString locale;
rv = chromeReg->GetSelectedLocale(package, locale); rv = chromeReg->GetSelectedLocale(package, locale);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
// Try to read an existent FastLoad file. nsCAutoString fileChromePath, fileLocale;
PRBool exists = PR_FALSE;
if (NS_SUCCEEDED(file->Exists(&exists)) && exists) { nsAutoArrayPtr<char> buf;
nsCOMPtr<nsIObjectInputStream> objectInput; PRUint32 len, amtRead;
rv = fastLoadService->NewInputStream(file, getter_AddRefs(objectInput)); nsCOMPtr<nsIObjectInputStream> objectInput;
rv = startupCache->GetBuffer(kXULCacheInfoKey, getter_Transfers(buf),
&len);
if (NS_SUCCEEDED(rv))
rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(objectInput));
if (NS_SUCCEEDED(rv)) {
buf.forget();
rv = objectInput->ReadCString(fileLocale);
rv |= objectInput->ReadCString(fileChromePath);
if (NS_FAILED(rv) ||
(!fileChromePath.Equals(chromePath) ||
!fileLocale.Equals(locale))) {
// Our cache won't be valid in this case, we'll need to rewrite.
// XXX This blows away work that other consumers (like
// mozJSComponentLoader) have done, need more fine-grained control.
startupCache->InvalidateCache();
rv = NS_ERROR_UNEXPECTED;
}
} else if (rv != NS_ERROR_NOT_AVAILABLE)
// NS_ERROR_NOT_AVAILABLE is normal, usually if there's no cachefile.
return rv;
if (NS_FAILED(rv)) {
// Either the cache entry was invalid or it didn't exist, so write it now.
nsCOMPtr<nsIObjectOutputStream> objectOutput;
nsCOMPtr<nsIInputStream> inputStream;
nsCOMPtr<nsIStorageStream> storageStream;
rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput),
getter_AddRefs(storageStream),
false);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
if (NS_SUCCEEDED(rv)) { rv = objectOutput->WriteStringZ(locale.get());
// Get the XUL fastload file version number, which should be rv |= objectOutput->WriteStringZ(chromePath.get());
// decremented whenever the XUL-specific file format changes rv |= objectOutput->Close();
// (see public/nsIXULPrototypeCache.h for the #define). rv |= storageStream->NewInputStream(0, getter_AddRefs(inputStream));
PRUint32 xulFastLoadVersion, jsByteCodeVersion; }
rv = objectInput->Read32(&xulFastLoadVersion); if (NS_SUCCEEDED(rv))
rv |= objectInput->Read32(&jsByteCodeVersion); rv = inputStream->Available(&len);
if (NS_SUCCEEDED(rv)) {
if (xulFastLoadVersion != XUL_FASTLOAD_FILE_VERSION || if (NS_SUCCEEDED(rv)) {
jsByteCodeVersion != JSXDR_BYTECODE_VERSION) { buf = new char[len];
#ifdef DEBUG rv = inputStream->Read(buf, len, &amtRead);
printf((xulFastLoadVersion != XUL_FASTLOAD_FILE_VERSION) if (NS_SUCCEEDED(rv) && len == amtRead)
? "bad FastLoad file version\n" rv = startupCache->PutBuffer(kXULCacheInfoKey, buf, len);
: "bad JS bytecode version\n"); else {
#endif rv = NS_ERROR_UNEXPECTED;
rv = NS_ERROR_UNEXPECTED;
} else {
nsCAutoString fileChromePath, fileLocale;
rv = objectInput->ReadCString(fileChromePath);
rv |= objectInput->ReadCString(fileLocale);
if (NS_SUCCEEDED(rv) &&
(!fileChromePath.Equals(chromePath) ||
!fileLocale.Equals(locale))) {
rv = NS_ERROR_UNEXPECTED;
}
}
}
} }
} }
if (NS_SUCCEEDED(rv)) { // Failed again, just bail.
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;
}
}
// 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 (NS_FAILED(rv)) {
if (objectOutput) startupCache->InvalidateCache();
objectOutput->Close();
else
output->Close();
xio->mOutputStream = nsnull;
file->Remove(PR_FALSE);
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
fastLoadService->SetOutputStream(objectOutput);
} }
// Success! Insert this URI into the mFastLoadURITable // Success! Insert this URI into the mCacheURITable
// and commit locals to globals. // and commit locals to globals.
mFastLoadURITable.Put(aURI, 1); mCacheURITable.Put(aURI, 1);
NS_ADDREF(gFastLoadService = fastLoadService); gStartupCache = startupCache;
NS_ADDREF(gFastLoadFile = file);
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -52,8 +52,12 @@
#include "nsRefPtrHashtable.h" #include "nsRefPtrHashtable.h"
#include "nsURIHashKey.h" #include "nsURIHashKey.h"
#include "nsXULPrototypeDocument.h" #include "nsXULPrototypeDocument.h"
#include "nsIInputStream.h"
#include "nsIStorageStream.h"
#include "mozilla/scache/StartupCache.h"
using namespace mozilla::scache;
class nsIFastLoadService;
class nsCSSStyleSheet; class nsCSSStyleSheet;
struct CacheScriptEntry struct CacheScriptEntry
@ -68,7 +72,7 @@ struct CacheScriptEntry
* *
* The cache has two levels: * The cache has two levels:
* 1. In-memory hashtables * 1. In-memory hashtables
* 2. The on-disk fastload file. * 2. The on-disk cache file.
*/ */
class nsXULPrototypeCache : public nsIXULPrototypeCache, class nsXULPrototypeCache : public nsIXULPrototypeCache,
nsIObserver nsIObserver
@ -82,7 +86,7 @@ public:
virtual PRBool IsCached(nsIURI* aURI) { virtual PRBool IsCached(nsIURI* aURI) {
return GetPrototype(aURI) != nsnull; return GetPrototype(aURI) != nsnull;
} }
virtual void AbortFastLoads(); virtual void AbortCaching();
/** /**
@ -96,16 +100,6 @@ public:
*/ */
void Flush(); 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 // The following methods are used to put and retrive various items into and
// from the cache. // from the cache.
@ -135,9 +129,30 @@ public:
*/ */
nsresult PutStyleSheet(nsCSSStyleSheet* aStyleSheet); 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 nsXULPrototypeCache* GetInstance();
static nsIFastLoadService* GetFastLoadService();
static void ReleaseGlobals() static void ReleaseGlobals()
{ {
@ -162,16 +177,16 @@ protected:
nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo> mXBLDocTable; nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo> mXBLDocTable;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// FastLoad // StartupCache
// this is really a hash set, with a dummy data parameter // this is really a hash set, with a dummy data parameter
nsDataHashtable<nsURIHashKey,PRUint32> mFastLoadURITable; nsDataHashtable<nsURIHashKey,PRUint32> mCacheURITable;
static nsIFastLoadService* gFastLoadService; static StartupCache* gStartupCache;
static nsIFile* gFastLoadFile; nsInterfaceHashtable<nsURIHashKey, nsIStorageStream> mOutputStreamTable;
nsInterfaceHashtable<nsURIHashKey, nsIObjectInputStream> mInputStreamTable;
// Bootstrap FastLoad Service
nsresult StartFastLoad(nsIURI* aDocumentURI); // Bootstrap caching service
nsresult StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags); nsresult BeginCaching(nsIURI* aDocumentURI);
}; };
#endif // nsXULPrototypeCache_h__ #endif // nsXULPrototypeCache_h__

Просмотреть файл

@ -418,6 +418,13 @@ nsXULPrototypeDocument::Write(nsIObjectOutputStream* aStream)
rv |= aStream->WriteObject(mNodeInfoManager->DocumentPrincipal(), rv |= aStream->WriteObject(mNodeInfoManager->DocumentPrincipal(),
PR_TRUE); 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 // nsINodeInfo table
nsCOMArray<nsINodeInfo> nodeInfos; nsCOMArray<nsINodeInfo> nodeInfos;
if (mRoot) if (mRoot)

Просмотреть файл

@ -969,7 +969,8 @@ mozJSComponentLoader::WriteScript(StartupCache* cache, JSObject *scriptObj,
nsCOMPtr<nsIObjectOutputStream> oos; nsCOMPtr<nsIObjectOutputStream> oos;
nsCOMPtr<nsIStorageStream> storageStream; nsCOMPtr<nsIStorageStream> storageStream;
rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos), rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
getter_AddRefs(storageStream)); getter_AddRefs(storageStream),
true);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = WriteScriptToStream(cx, scriptObj, oos); rv = WriteScriptToStream(cx, scriptObj, oos);

Просмотреть файл

@ -27,7 +27,8 @@ NS_NewObjectInputStreamFromBuffer(char* buffer, PRUint32 len,
NS_EXPORT nsresult NS_EXPORT nsresult
NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream, NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
nsIStorageStream** stream) nsIStorageStream** stream,
PRBool wantDebugStream)
{ {
nsCOMPtr<nsIStorageStream> storageStream; nsCOMPtr<nsIStorageStream> storageStream;
@ -42,13 +43,17 @@ NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
objectOutput->SetOutputStream(outputStream); objectOutput->SetOutputStream(outputStream);
#ifdef DEBUG #ifdef DEBUG
// Wrap in debug stream to detect unsupported writes of if (wantDebugStream) {
// multiply-referenced non-singleton objects // Wrap in debug stream to detect unsupported writes of
StartupCache* sc = StartupCache::GetSingleton(); // multiply-referenced non-singleton objects
NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED); StartupCache* sc = StartupCache::GetSingleton();
nsCOMPtr<nsIObjectOutputStream> debugStream; NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED);
sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream)); nsCOMPtr<nsIObjectOutputStream> debugStream;
debugStream.forget(wrapperStream); sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream));
debugStream.forget(wrapperStream);
} else {
objectOutput.forget(wrapperStream);
}
#else #else
objectOutput.forget(wrapperStream); objectOutput.forget(wrapperStream);
#endif #endif

Просмотреть файл

@ -50,10 +50,15 @@ NS_NewObjectInputStreamFromBuffer(char* buffer, PRUint32 len,
nsIObjectInputStream** stream); nsIObjectInputStream** stream);
// We can't retrieve the wrapped stream from the objectOutputStream later, // We can't retrieve the wrapped stream from the objectOutputStream later,
// so we return it here. // so we return it here. We give callers in debug builds the option
// to wrap the outputstream in a debug stream, which will detect if
// non-singleton objects are written out multiple times during a serialization.
// This could cause them to be deserialized incorrectly (as multiple copies
// instead of references).
NS_EXPORT nsresult NS_EXPORT nsresult
NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream, NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
nsIStorageStream** stream); nsIStorageStream** stream,
PRBool wantDebugStream);
NS_EXPORT nsresult NS_EXPORT nsresult
NS_NewBufferFromStorageStream(nsIStorageStream *storageStream, NS_NewBufferFromStorageStream(nsIStorageStream *storageStream,

Просмотреть файл

@ -1,124 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Startup Cache.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benedict Hsieh <bhsieh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsStartupCacheUtils.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsIInputStream.h"
#include "nsIStorageStream.h"
#include "nsIStringStream.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
nsresult
NS_NewObjectInputStreamFromBuffer(char* buffer, int len,
nsIObjectInputStream** stream)
{
nsCOMPtr<nsIStringInputStream> stringStream
= do_CreateInstance("@mozilla.org/io/string-input-stream;1");
if (!stringStream)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIObjectInputStream> objectInput
= do_CreateInstance("@mozilla.org/binaryinputstream;1");
if (!objectInput)
return NS_ERROR_OUT_OF_MEMORY;
stringStream->AdoptData(buffer, len);
objectInput->SetInputStream(stringStream);
NS_ADDREF(*stream = objectInput);
return NS_OK;
}
// This is questionable API name and design, but we can't
// retrieve the wrapped stream from the objectOutputStream later...
nsresult
NS_NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
nsIStorageStream** stream)
{
nsCOMPtr<nsIStorageStream> storageStream;
nsresult rv = NS_NewStorageStream(256, (PRUint32)-1,
getter_AddRefs(storageStream));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObjectOutputStream> objectOutput
= do_CreateInstance("@mozilla.org/binaryoutputstream;1");
if (!objectOutput)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIOutputStream> outputStream
= do_QueryInterface(storageStream);
objectOutput->SetOutputStream(outputStream);
NS_ADDREF(*wrapperStream = objectOutput);
NS_ADDREF(*stream = storageStream);
return NS_OK;
}
nsresult
NS_NewBufferFromStorageStream(nsIStorageStream *storageStream,
char** buffer, int* len)
{
nsresult rv;
nsCOMPtr<nsIInputStream> inputStream;
rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 avail, read;
rv = inputStream->Available(&avail);
NS_ENSURE_SUCCESS(rv, rv);
char* temp = new char[avail];
if (!temp)
return NS_ERROR_OUT_OF_MEMORY;
rv = inputStream->Read(temp, avail, &read);
if (NS_SUCCEEDED(rv) && avail != read)
rv = NS_ERROR_UNEXPECTED;
if (NS_FAILED(rv)) {
delete temp;
return rv;
}
*len = avail;
*buffer = temp;
return NS_OK;
}