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