// 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, bool* exists) { if (mOutputStreamTable.Get(uri, nullptr)) { *exists = true; return NS_OK; } nsAutoCString spec(kXULCachePrefix); nsresult rv = PathifyURI(uri, spec); if (NS_FAILED(rv)) { *exists = false; return NS_OK; } nsAutoArrayPtr<char> buf; uint32_t len; StartupCache* sc = StartupCache::GetSingleton(); if (sc) rv = sc->GetBuffer(spec.get(), getter_Transfers(buf), &len); else { *exists = false; return NS_OK; } *exists = NS_SUCCEEDED(rv); return NS_OK; }
// 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, bool* exists) { if (mOutputStreamTable.Get(uri, nsnull)) { *exists = true; return NS_OK; } nsCAutoString spec(kXULCachePrefix); nsresult rv = PathifyURI(uri, spec); if (NS_FAILED(rv)) { *exists = false; return NS_OK; } nsAutoArrayPtr<char> buf; PRUint32 len; 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 = false; return NS_OK; } rv = sc->GetBuffer(spec.get(), getter_Transfers(buf), &len); } *exists = NS_SUCCEEDED(rv); return NS_OK; }
nsresult nsXULPrototypeCache::GetInputStream(nsIURI* uri, nsIObjectInputStream** stream) { nsAutoCString spec(kXULCachePrefix); nsresult rv = PathifyURI(uri, spec); if (NS_FAILED(rv)) return NS_ERROR_NOT_AVAILABLE; nsAutoArrayPtr<char> buf; uint32_t len; nsCOMPtr<nsIObjectInputStream> ois; StartupCache* sc = StartupCache::GetSingleton(); if (!sc) return NS_ERROR_NOT_AVAILABLE; rv = sc->GetBuffer(spec.get(), getter_Transfers(buf), &len); if (NS_FAILED(rv)) return NS_ERROR_NOT_AVAILABLE; rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois)); NS_ENSURE_SUCCESS(rv, rv); buf.forget(); mInputStreamTable.Put(uri, ois); ois.forget(stream); return NS_OK; }
nsresult StartupCacheWrapper::PutBuffer(const char* id, const char* inbuf, uint32_t length) { StartupCache* sc = StartupCache::GetSingleton(); if (!sc) { return NS_ERROR_NOT_INITIALIZED; } return sc->PutBuffer(id, inbuf, length); }
nsresult StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, PRUint32* length) { StartupCache* sc = StartupCache::GetSingleton(); if (!sc) { return NS_ERROR_NOT_INITIALIZED; } return sc->GetBuffer(id, outbuf, length); }
nsresult StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream, nsIObjectOutputStream** outStream) { StartupCache* sc = StartupCache::GetSingleton(); if (!sc) { return NS_ERROR_NOT_INITIALIZED; } return sc->GetDebugObjectOutputStream(stream, outStream); }
nsresult StartupCacheWrapper::InvalidateCache() { StartupCache* sc = StartupCache::GetSingleton(); if (!sc) { return NS_ERROR_NOT_INITIALIZED; } sc->InvalidateCache(); return NS_OK; }
nsresult StartupCacheWrapper::StartupWriteComplete(bool *complete) { StartupCache* sc = StartupCache::GetSingleton(); if (!sc) { return NS_ERROR_NOT_INITIALIZED; } sc->WaitOnWriteThread(); *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0; return NS_OK; }
nsresult StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, uint32_t* length) { StartupCache* sc = StartupCache::GetSingleton(); if (!sc) { return NS_ERROR_NOT_INITIALIZED; } UniquePtr<char[]> buf; nsresult rv = sc->GetBuffer(id, &buf, length); *outbuf = buf.release(); return rv; }
void StartupCache::ThreadedWrite(void *aClosure) { PR_SetCurrentThreadName("StartupCache"); /* * It is safe to use the pointer passed in aClosure to reference the * StartupCache object because the thread's lifetime is tightly coupled to * the lifetime of the StartupCache object; this thread is joined in the * StartupCache destructor, guaranteeing that this function runs if and only * if the StartupCache object is valid. */ StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure); startupCacheObj->WriteToDisk(); }
nsresult StartupCacheListener::Observe(nsISupports *subject, const char* topic, const char16_t* data) { StartupCache* sc = StartupCache::GetSingleton(); if (!sc) return NS_OK; if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { // Do not leave the thread running past xpcom shutdown sc->WaitOnWriteThread(); StartupCache::gShutdownInitiated = true; } else if (strcmp(topic, "startupcache-invalidate") == 0) { sc->InvalidateCache(); } return NS_OK; }
nsresult nsXULPrototypeCache::FinishOutputStream(nsIURI* uri) { nsresult rv; StartupCache* sc = StartupCache::GetSingleton(); if (!sc) return NS_ERROR_NOT_AVAILABLE; nsCOMPtr<nsIStorageStream> storageStream; bool found = mOutputStreamTable.Get(uri, getter_AddRefs(storageStream)); if (!found) return NS_ERROR_UNEXPECTED; nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(storageStream); outputStream->Close(); nsAutoArrayPtr<char> buf; uint32_t len; rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len); NS_ENSURE_SUCCESS(rv, rv); if (!mCacheURITable.GetEntry(uri)) { nsAutoCString spec(kXULCachePrefix); rv = PathifyURI(uri, spec); if (NS_FAILED(rv)) return NS_ERROR_NOT_AVAILABLE; rv = sc->PutBuffer(spec.get(), buf, len); if (NS_SUCCEEDED(rv)) { mOutputStreamTable.Remove(uri); mCacheURITable.RemoveEntry(uri); } } return rv; }
NS_EXPORT nsresult NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream, nsIStorageStream** stream, bool 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 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; }
nsresult StartupCacheWrapper::RecordAgesAlways() { StartupCache *sc = StartupCache::GetSingleton(); return sc ? sc->RecordAgesAlways() : NS_ERROR_NOT_INITIALIZED; }
nsresult nsXULPrototypeCache::BeginCaching(nsIURI* aURI) { nsresult rv, tmp; nsAutoCString path; aURI->GetPath(path); if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul"))) return NS_ERROR_NOT_AVAILABLE; StartupCache* startupCache = StartupCache::GetSingleton(); if (!startupCache) return NS_ERROR_FAILURE; if (gDisableXULCache) return NS_ERROR_NOT_AVAILABLE; // Get the chrome directory to validate against the one stored in the // cache file, or to store there if we're generating a new file. nsCOMPtr<nsIFile> chromeDir; rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir)); if (NS_FAILED(rv)) return rv; nsAutoCString chromePath; rv = chromeDir->GetNativePath(chromePath); if (NS_FAILED(rv)) return rv; // XXXbe we assume the first package's locale is the same as the locale of // all subsequent packages of cached chrome URIs.... nsAutoCString package; rv = aURI->GetHost(package); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIXULChromeRegistry> chromeReg = do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv); nsAutoCString locale; rv = chromeReg->GetSelectedLocale(package, locale); if (NS_FAILED(rv)) return rv; nsAutoCString fileChromePath, fileLocale; UniquePtr<char[]> buf; uint32_t len, amtRead; nsCOMPtr<nsIObjectInputStream> objectInput; rv = startupCache->GetBuffer(kXULCacheInfoKey, &buf, &len); if (NS_SUCCEEDED(rv)) rv = NewObjectInputStreamFromBuffer(Move(buf), len, getter_AddRefs(objectInput)); if (NS_SUCCEEDED(rv)) { rv = objectInput->ReadCString(fileLocale); tmp = objectInput->ReadCString(fileChromePath); if (NS_FAILED(tmp)) { rv = tmp; } 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(); mStartupCacheURITable.Clear(); 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 = NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput), getter_AddRefs(storageStream), false); if (NS_SUCCEEDED(rv)) { rv = objectOutput->WriteStringZ(locale.get()); tmp = objectOutput->WriteStringZ(chromePath.get()); if (NS_FAILED(tmp)) { rv = tmp; } tmp = objectOutput->Close(); if (NS_FAILED(tmp)) { rv = tmp; } tmp = storageStream->NewInputStream(0, getter_AddRefs(inputStream)); if (NS_FAILED(tmp)) { rv = tmp; } } if (NS_SUCCEEDED(rv)) { uint64_t len64; rv = inputStream->Available(&len64); if (NS_SUCCEEDED(rv)) { if (len64 <= UINT32_MAX) len = (uint32_t)len64; else rv = NS_ERROR_FILE_TOO_BIG; } } if (NS_SUCCEEDED(rv)) { buf = MakeUnique<char[]>(len); rv = inputStream->Read(buf.get(), len, &amtRead); if (NS_SUCCEEDED(rv) && len == amtRead) rv = startupCache->PutBuffer(kXULCacheInfoKey, buf.get(), len); else { rv = NS_ERROR_UNEXPECTED; } } // Failed again, just bail. if (NS_FAILED(rv)) { startupCache->InvalidateCache(); mStartupCacheURITable.Clear(); return NS_ERROR_FAILURE; } } return NS_OK; }
nsresult StartupCacheWrapper::ResetStartupWriteTimer() { StartupCache* sc = StartupCache::GetSingleton(); return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED; }
nsresult nsXULPrototypeCache::BeginCaching(nsIURI* aURI) { nsresult rv; nsCAutoString path; aURI->GetPath(path); if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul"))) return NS_ERROR_NOT_AVAILABLE; // Test gStartupCache to decide whether this is the first nsXULDocument // participating in the serialization. If gStartupCache is non-null, this document // must not be first, but it can join the process. Examples of // multiple master documents participating include hiddenWindow.xul and // navigator.xul on the Mac, and multiple-app-component (e.g., mailnews // and browser) startup due to command-line arguments. // if (gStartupCache) { mCacheURITable.Put(aURI, 1); return NS_OK; } // Use a local to refer to the service till we're sure we succeeded, then // commit to gStartupCache. StartupCache* startupCache = StartupCache::GetSingleton(); if (!startupCache) return NS_ERROR_FAILURE; gDisableXULDiskCache = Preferences::GetBool(kDisableXULCachePref, gDisableXULDiskCache); Preferences::RegisterCallback(CachePrefChangedCallback, kDisableXULCachePref); if (gDisableXULDiskCache) return NS_ERROR_NOT_AVAILABLE; // Get the chrome directory to validate against the one stored in the // cache file, or to store there if we're generating a new file. 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; // XXXbe we assume the first package's locale is the same as the locale of // 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; 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 = 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 = NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput), getter_AddRefs(storageStream), false); if (NS_SUCCEEDED(rv)) { rv = objectOutput->WriteStringZ(locale.get()); rv |= objectOutput->WriteStringZ(chromePath.get()); rv |= objectOutput->Close(); rv |= storageStream->NewInputStream(0, getter_AddRefs(inputStream)); } if (NS_SUCCEEDED(rv)) rv = inputStream->Available(&len); if (NS_SUCCEEDED(rv)) { buf = new char[len]; rv = inputStream->Read(buf, len, &amtRead); if (NS_SUCCEEDED(rv) && len == amtRead) rv = startupCache->PutBuffer(kXULCacheInfoKey, buf, len); else { rv = NS_ERROR_UNEXPECTED; } } // Failed again, just bail. if (NS_FAILED(rv)) { startupCache->InvalidateCache(); return NS_ERROR_FAILURE; } } // Success! Insert this URI into the mCacheURITable // and commit locals to globals. mCacheURITable.Put(aURI, 1); gStartupCache = startupCache; return NS_OK; }