bug 812562 - click-to-play: reshow notification for blocklisted plugins r=jaws
[gecko.git] / startupcache / StartupCache.cpp
blob13a6982a40785b3fdfe23e2dab397565dcbf69e8
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "prio.h"
7 #include "prtypes.h"
8 #include "pldhash.h"
9 #include "nsXPCOMStrings.h"
10 #include "mozilla/scache/StartupCache.h"
12 #include "nsAutoPtr.h"
13 #include "nsClassHashtable.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsDirectoryServiceUtils.h"
16 #include "nsIClassInfo.h"
17 #include "nsIFile.h"
18 #include "nsIMemoryReporter.h"
19 #include "nsIObserver.h"
20 #include "nsIObserverService.h"
21 #include "nsIOutputStream.h"
22 #include "nsIStartupCache.h"
23 #include "nsIStorageStream.h"
24 #include "nsIStreamBufferAccess.h"
25 #include "nsIStringStream.h"
26 #include "nsISupports.h"
27 #include "nsITimer.h"
28 #include "nsIZipWriter.h"
29 #include "nsIZipReader.h"
30 #include "nsWeakReference.h"
31 #include "nsZipArchive.h"
32 #include "mozilla/Omnijar.h"
33 #include "prenv.h"
34 #include "mozilla/Telemetry.h"
35 #include "nsThreadUtils.h"
36 #include "nsXULAppAPI.h"
37 #include "nsIProtocolHandler.h"
39 #ifdef IS_BIG_ENDIAN
40 #define SC_ENDIAN "big"
41 #else
42 #define SC_ENDIAN "little"
43 #endif
45 #if PR_BYTES_PER_WORD == 4
46 #define SC_WORDSIZE "4"
47 #else
48 #define SC_WORDSIZE "8"
49 #endif
51 namespace mozilla {
52 namespace scache {
54 static int64_t
55 GetStartupCacheMappingSize()
57 mozilla::scache::StartupCache* sc = mozilla::scache::StartupCache::GetSingleton();
58 return sc ? sc->SizeOfMapping() : 0;
61 NS_MEMORY_REPORTER_IMPLEMENT(StartupCacheMapping,
62 "explicit/startup-cache/mapping",
63 KIND_NONHEAP,
64 nsIMemoryReporter::UNITS_BYTES,
65 GetStartupCacheMappingSize,
66 "Memory used to hold the mapping of the startup cache from file. This "
67 "memory is likely to be swapped out shortly after start-up.")
69 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StartupCacheDataMallocSizeOf, "startup-cache/data")
71 static int64_t
72 GetStartupCacheDataSize()
74 mozilla::scache::StartupCache* sc = mozilla::scache::StartupCache::GetSingleton();
75 return sc ? sc->HeapSizeOfIncludingThis(StartupCacheDataMallocSizeOf) : 0;
78 NS_MEMORY_REPORTER_IMPLEMENT(StartupCacheData,
79 "explicit/startup-cache/data",
80 KIND_HEAP,
81 nsIMemoryReporter::UNITS_BYTES,
82 GetStartupCacheDataSize,
83 "Memory used by the startup cache for things other than the file mapping.")
85 static const char sStartupCacheName[] = "startupCache." SC_WORDSIZE "." SC_ENDIAN;
86 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
88 StartupCache*
89 StartupCache::GetSingleton()
91 if (!gStartupCache) {
92 if (XRE_GetProcessType() != GeckoProcessType_Default) {
93 NS_WARNING("Startup cache is only available in the chrome process");
94 return nullptr;
97 StartupCache::InitSingleton();
100 return StartupCache::gStartupCache;
103 void
104 StartupCache::DeleteSingleton()
106 delete StartupCache::gStartupCache;
109 nsresult
110 StartupCache::InitSingleton()
112 nsresult rv;
113 StartupCache::gStartupCache = new StartupCache();
115 rv = StartupCache::gStartupCache->Init();
116 if (NS_FAILED(rv)) {
117 delete StartupCache::gStartupCache;
118 StartupCache::gStartupCache = nullptr;
120 return rv;
123 StartupCache* StartupCache::gStartupCache;
124 bool StartupCache::gShutdownInitiated;
125 bool StartupCache::gIgnoreDiskCache;
126 enum StartupCache::TelemetrifyAge StartupCache::gPostFlushAgeAction = StartupCache::IGNORE_AGE;
128 StartupCache::StartupCache()
129 : mArchive(NULL), mStartupWriteInitiated(false), mWriteThread(NULL),
130 mMappingMemoryReporter(nullptr), mDataMemoryReporter(nullptr) { }
132 StartupCache::~StartupCache()
134 if (mTimer) {
135 mTimer->Cancel();
138 // Generally, the in-memory table should be empty here,
139 // but an early shutdown means either mTimer didn't run
140 // or the write thread is still running.
141 WaitOnWriteThread();
142 WriteToDisk();
143 gStartupCache = nullptr;
144 (void)::NS_UnregisterMemoryReporter(mMappingMemoryReporter);
145 (void)::NS_UnregisterMemoryReporter(mDataMemoryReporter);
146 mMappingMemoryReporter = nullptr;
147 mDataMemoryReporter = nullptr;
150 nsresult
151 StartupCache::Init()
153 // workaround for bug 653936
154 nsCOMPtr<nsIProtocolHandler> jarInitializer(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar"));
156 nsresult rv;
157 mTable.Init();
158 #ifdef DEBUG
159 mWriteObjectMap.Init();
160 #endif
162 // This allows to override the startup cache filename
163 // which is useful from xpcshell, when there is no ProfLDS directory to keep cache in.
164 char *env = PR_GetEnv("MOZ_STARTUP_CACHE");
165 if (env) {
166 rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(mFile));
167 } else {
168 nsCOMPtr<nsIFile> file;
169 rv = NS_GetSpecialDirectory("ProfLDS",
170 getter_AddRefs(file));
171 if (NS_FAILED(rv)) {
172 // return silently, this will fail in mochitests's xpcshell process.
173 return rv;
176 rv = file->AppendNative(NS_LITERAL_CSTRING("startupCache"));
177 NS_ENSURE_SUCCESS(rv, rv);
179 // Try to create the directory if it's not there yet
180 rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
181 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
182 return rv;
184 rv = file->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName));
186 NS_ENSURE_SUCCESS(rv, rv);
188 mFile = do_QueryInterface(file);
191 NS_ENSURE_TRUE(mFile, NS_ERROR_UNEXPECTED);
193 mObserverService = do_GetService("@mozilla.org/observer-service;1");
195 if (!mObserverService) {
196 NS_WARNING("Could not get observerService.");
197 return NS_ERROR_UNEXPECTED;
200 mListener = new StartupCacheListener();
201 rv = mObserverService->AddObserver(mListener, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
202 false);
203 NS_ENSURE_SUCCESS(rv, rv);
204 rv = mObserverService->AddObserver(mListener, "startupcache-invalidate",
205 false);
206 NS_ENSURE_SUCCESS(rv, rv);
208 rv = LoadArchive(RECORD_AGE);
210 // Sometimes we don't have a cache yet, that's ok.
211 // If it's corrupted, just remove it and start over.
212 if (gIgnoreDiskCache || (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)) {
213 NS_WARNING("Failed to load startupcache file correctly, removing!");
214 InvalidateCache();
217 mMappingMemoryReporter = new NS_MEMORY_REPORTER_NAME(StartupCacheMapping);
218 mDataMemoryReporter = new NS_MEMORY_REPORTER_NAME(StartupCacheData);
219 (void)::NS_RegisterMemoryReporter(mMappingMemoryReporter);
220 (void)::NS_RegisterMemoryReporter(mDataMemoryReporter);
222 return NS_OK;
225 /**
226 * LoadArchive can be called from the main thread or while reloading cache on write thread.
228 nsresult
229 StartupCache::LoadArchive(enum TelemetrifyAge flag)
231 if (gIgnoreDiskCache)
232 return NS_ERROR_FAILURE;
234 bool exists;
235 mArchive = NULL;
236 nsresult rv = mFile->Exists(&exists);
237 if (NS_FAILED(rv) || !exists)
238 return NS_ERROR_FILE_NOT_FOUND;
240 mArchive = new nsZipArchive();
241 rv = mArchive->OpenArchive(mFile);
242 if (NS_FAILED(rv) || flag == IGNORE_AGE)
243 return rv;
245 nsCString comment;
246 if (!mArchive->GetComment(comment)) {
247 return rv;
250 const char *data;
251 size_t len = NS_CStringGetData(comment, &data);
252 PRTime creationStamp;
253 // We might not have a comment if the startup cache file was created
254 // before we started recording creation times in the comment.
255 if (len == sizeof(creationStamp)) {
256 memcpy(&creationStamp, data, len);
257 PRTime current = PR_Now();
258 int64_t diff = current - creationStamp;
260 // We can't use AccumulateTimeDelta here because we have no way of
261 // reifying a TimeStamp from creationStamp.
262 int64_t usec_per_hour = PR_USEC_PER_SEC * int64_t(3600);
263 int64_t hour_diff = (diff + usec_per_hour - 1) / usec_per_hour;
264 mozilla::Telemetry::Accumulate(Telemetry::STARTUP_CACHE_AGE_HOURS,
265 hour_diff);
268 return rv;
271 namespace {
273 nsresult
274 GetBufferFromZipArchive(nsZipArchive *zip, bool doCRC, const char* id,
275 char** outbuf, uint32_t* length)
277 if (!zip)
278 return NS_ERROR_NOT_AVAILABLE;
280 nsZipItemPtr<char> zipItem(zip, id, doCRC);
281 if (!zipItem)
282 return NS_ERROR_NOT_AVAILABLE;
284 *outbuf = zipItem.Forget();
285 *length = zipItem.Length();
286 return NS_OK;
289 } /* anonymous namespace */
291 // NOTE: this will not find a new entry until it has been written to disk!
292 // Consumer should take ownership of the resulting buffer.
293 nsresult
294 StartupCache::GetBuffer(const char* id, char** outbuf, uint32_t* length)
296 NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
297 WaitOnWriteThread();
298 if (!mStartupWriteInitiated) {
299 CacheEntry* entry;
300 nsDependentCString idStr(id);
301 mTable.Get(idStr, &entry);
302 if (entry) {
303 *outbuf = new char[entry->size];
304 memcpy(*outbuf, entry->data, entry->size);
305 *length = entry->size;
306 return NS_OK;
310 nsresult rv = GetBufferFromZipArchive(mArchive, true, id, outbuf, length);
311 if (NS_SUCCEEDED(rv))
312 return rv;
314 nsRefPtr<nsZipArchive> omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
315 // no need to checksum omnijarred entries
316 rv = GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
317 if (NS_SUCCEEDED(rv))
318 return rv;
320 omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
321 // no need to checksum omnijarred entries
322 return GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
325 // Makes a copy of the buffer, client retains ownership of inbuf.
326 nsresult
327 StartupCache::PutBuffer(const char* id, const char* inbuf, uint32_t len)
329 NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
330 WaitOnWriteThread();
331 if (StartupCache::gShutdownInitiated) {
332 return NS_ERROR_NOT_AVAILABLE;
335 nsAutoArrayPtr<char> data(new char[len]);
336 memcpy(data, inbuf, len);
338 nsDependentCString idStr(id);
339 // Cache it for now, we'll write all together later.
340 CacheEntry* entry;
342 #ifdef DEBUG
343 mTable.Get(idStr, &entry);
344 NS_ASSERTION(entry == nullptr, "Existing entry in StartupCache.");
346 if (mArchive) {
347 nsZipItem* zipItem = mArchive->GetItem(id);
348 NS_ASSERTION(zipItem == nullptr, "Existing entry in disk StartupCache.");
350 #endif
352 entry = new CacheEntry(data.forget(), len);
353 mTable.Put(idStr, entry);
354 return ResetStartupWriteTimer();
357 size_t
358 StartupCache::SizeOfMapping()
360 return mArchive ? mArchive->SizeOfMapping() : 0;
363 size_t
364 StartupCache::HeapSizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
366 // This function could measure more members, but they haven't been found by
367 // DMD to be significant. They can be added later if necessary.
368 return aMallocSizeOf(this) +
369 mTable.SizeOfExcludingThis(SizeOfEntryExcludingThis, aMallocSizeOf);
372 /* static */ size_t
373 StartupCache::SizeOfEntryExcludingThis(const nsACString& key, const nsAutoPtr<CacheEntry>& data,
374 nsMallocSizeOfFun mallocSizeOf, void *)
376 return data->SizeOfExcludingThis(mallocSizeOf);
379 struct CacheWriteHolder
381 nsCOMPtr<nsIZipWriter> writer;
382 nsCOMPtr<nsIStringInputStream> stream;
383 PRTime time;
386 PLDHashOperator
387 CacheCloseHelper(const nsACString& key, nsAutoPtr<CacheEntry>& data,
388 void* closure)
390 nsresult rv;
392 CacheWriteHolder* holder = (CacheWriteHolder*) closure;
393 nsIStringInputStream* stream = holder->stream;
394 nsIZipWriter* writer = holder->writer;
396 stream->ShareData(data->data, data->size);
398 #ifdef DEBUG
399 bool hasEntry;
400 rv = writer->HasEntry(key, &hasEntry);
401 NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == false,
402 "Existing entry in disk StartupCache.");
403 #endif
404 rv = writer->AddEntryStream(key, holder->time, true, stream, false);
406 if (NS_FAILED(rv)) {
407 NS_WARNING("cache entry deleted but not written to disk.");
409 return PL_DHASH_REMOVE;
413 /**
414 * WriteToDisk writes the cache out to disk. Callers of WriteToDisk need to call WaitOnWriteThread
415 * to make sure there isn't a write happening on another thread
417 void
418 StartupCache::WriteToDisk()
420 nsresult rv;
421 mStartupWriteInitiated = true;
423 if (!mTable.IsInitialized() || mTable.Count() == 0)
424 return;
426 nsCOMPtr<nsIZipWriter> zipW = do_CreateInstance("@mozilla.org/zipwriter;1");
427 if (!zipW)
428 return;
430 rv = zipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
431 if (NS_FAILED(rv)) {
432 NS_WARNING("could not open zipfile for write");
433 return;
436 // If we didn't have an mArchive member, that means that we failed to
437 // open the startup cache for reading. Therefore, we need to record
438 // the time of creation in a zipfile comment; this will be useful for
439 // Telemetry statistics.
440 PRTime now = PR_Now();
441 if (!mArchive) {
442 nsCString comment;
443 comment.Assign((char *)&now, sizeof(now));
444 zipW->SetComment(comment);
447 nsCOMPtr<nsIStringInputStream> stream
448 = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
449 if (NS_FAILED(rv)) {
450 NS_WARNING("Couldn't create string input stream.");
451 return;
454 CacheWriteHolder holder;
455 holder.stream = stream;
456 holder.writer = zipW;
457 holder.time = now;
459 mTable.Enumerate(CacheCloseHelper, &holder);
461 // Close the archive so Windows doesn't choke.
462 mArchive = NULL;
463 zipW->Close();
465 // We succesfully wrote the archive to disk; mark the disk file as trusted
466 gIgnoreDiskCache = false;
468 // Our reader's view of the archive is outdated now, reload it.
469 LoadArchive(gPostFlushAgeAction);
471 return;
474 void
475 StartupCache::InvalidateCache()
477 WaitOnWriteThread();
478 mTable.Clear();
479 mArchive = NULL;
480 nsresult rv = mFile->Remove(false);
481 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
482 rv != NS_ERROR_FILE_NOT_FOUND) {
483 gIgnoreDiskCache = true;
484 mozilla::Telemetry::Accumulate(Telemetry::STARTUP_CACHE_INVALID, true);
485 return;
487 gIgnoreDiskCache = false;
488 LoadArchive(gPostFlushAgeAction);
491 void
492 StartupCache::IgnoreDiskCache()
494 gIgnoreDiskCache = true;
495 if (gStartupCache)
496 gStartupCache->InvalidateCache();
500 * WaitOnWriteThread() is called from a main thread to wait for the worker
501 * thread to finish. However since the same code is used in the worker thread and
502 * main thread, the worker thread can also call WaitOnWriteThread() which is a no-op.
504 void
505 StartupCache::WaitOnWriteThread()
507 NS_ASSERTION(NS_IsMainThread(), "Startup cache should only wait for io thread on main thread");
508 if (!mWriteThread || mWriteThread == PR_GetCurrentThread())
509 return;
511 PR_JoinThread(mWriteThread);
512 mWriteThread = NULL;
515 void
516 StartupCache::ThreadedWrite(void *aClosure)
518 PR_SetCurrentThreadName("StartupCache");
519 gStartupCache->WriteToDisk();
523 * The write-thread is spawned on a timeout(which is reset with every write). This
524 * can avoid a slow shutdown. After writing out the cache, the zipreader is
525 * reloaded on the worker thread.
527 void
528 StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
530 gStartupCache->mWriteThread = PR_CreateThread(PR_USER_THREAD,
531 StartupCache::ThreadedWrite,
532 NULL,
533 PR_PRIORITY_NORMAL,
534 PR_LOCAL_THREAD,
535 PR_JOINABLE_THREAD,
539 // We don't want to refcount StartupCache, so we'll just
540 // hold a ref to this and pass it to observerService instead.
541 NS_IMPL_THREADSAFE_ISUPPORTS1(StartupCacheListener, nsIObserver)
543 nsresult
544 StartupCacheListener::Observe(nsISupports *subject, const char* topic, const PRUnichar* data)
546 StartupCache* sc = StartupCache::GetSingleton();
547 if (!sc)
548 return NS_OK;
550 if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
551 // Do not leave the thread running past xpcom shutdown
552 sc->WaitOnWriteThread();
553 StartupCache::gShutdownInitiated = true;
554 } else if (strcmp(topic, "startupcache-invalidate") == 0) {
555 sc->InvalidateCache();
557 return NS_OK;
560 nsresult
561 StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
562 nsIObjectOutputStream** aOutStream)
564 NS_ENSURE_ARG_POINTER(aStream);
565 #ifdef DEBUG
566 StartupCacheDebugOutputStream* stream
567 = new StartupCacheDebugOutputStream(aStream, &mWriteObjectMap);
568 NS_ADDREF(*aOutStream = stream);
569 #else
570 NS_ADDREF(*aOutStream = aStream);
571 #endif
573 return NS_OK;
576 nsresult
577 StartupCache::ResetStartupWriteTimer()
579 mStartupWriteInitiated = false;
580 nsresult rv;
581 if (!mTimer)
582 mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
583 else
584 rv = mTimer->Cancel();
585 NS_ENSURE_SUCCESS(rv, rv);
586 // Wait for 10 seconds, then write out the cache.
587 mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 60000,
588 nsITimer::TYPE_ONE_SHOT);
589 return NS_OK;
592 nsresult
593 StartupCache::RecordAgesAlways()
595 gPostFlushAgeAction = RECORD_AGE;
596 return NS_OK;
599 // StartupCacheDebugOutputStream implementation
600 #ifdef DEBUG
601 NS_IMPL_ISUPPORTS3(StartupCacheDebugOutputStream, nsIObjectOutputStream,
602 nsIBinaryOutputStream, nsIOutputStream)
604 bool
605 StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
607 nsresult rv;
609 nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
610 if (!classInfo) {
611 NS_ERROR("aObject must implement nsIClassInfo");
612 return false;
615 uint32_t flags;
616 rv = classInfo->GetFlags(&flags);
617 NS_ENSURE_SUCCESS(rv, false);
618 if (flags & nsIClassInfo::SINGLETON)
619 return true;
621 nsISupportsHashKey* key = mObjectMap->GetEntry(aObject);
622 if (key) {
623 NS_ERROR("non-singleton aObject is referenced multiple times in this"
624 "serialization, we don't support that.");
625 return false;
628 mObjectMap->PutEntry(aObject);
629 return true;
632 // nsIObjectOutputStream implementation
633 nsresult
634 StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
636 nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
638 NS_ASSERTION(rootObject.get() == aObject,
639 "bad call to WriteObject -- call WriteCompoundObject!");
640 bool check = CheckReferences(aObject);
641 NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
642 return mBinaryStream->WriteObject(aObject, aIsStrongRef);
645 nsresult
646 StartupCacheDebugOutputStream::WriteSingleRefObject(nsISupports* aObject)
648 nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
650 NS_ASSERTION(rootObject.get() == aObject,
651 "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
652 bool check = CheckReferences(aObject);
653 NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
654 return mBinaryStream->WriteSingleRefObject(aObject);
657 nsresult
658 StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports* aObject,
659 const nsIID& aIID,
660 bool aIsStrongRef)
662 nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
664 nsCOMPtr<nsISupports> roundtrip;
665 rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
666 NS_ASSERTION(roundtrip.get() == aObject,
667 "bad aggregation or multiple inheritance detected by call to "
668 "WriteCompoundObject!");
670 bool check = CheckReferences(aObject);
671 NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
672 return mBinaryStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
675 nsresult
676 StartupCacheDebugOutputStream::WriteID(nsID const& aID)
678 return mBinaryStream->WriteID(aID);
681 char*
682 StartupCacheDebugOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
684 return mBinaryStream->GetBuffer(aLength, aAlignMask);
687 void
688 StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
690 mBinaryStream->PutBuffer(aBuffer, aLength);
692 #endif //DEBUG
694 StartupCacheWrapper* StartupCacheWrapper::gStartupCacheWrapper = nullptr;
696 NS_IMPL_THREADSAFE_ISUPPORTS1(StartupCacheWrapper, nsIStartupCache)
698 StartupCacheWrapper* StartupCacheWrapper::GetSingleton()
700 if (!gStartupCacheWrapper)
701 gStartupCacheWrapper = new StartupCacheWrapper();
703 NS_ADDREF(gStartupCacheWrapper);
704 return gStartupCacheWrapper;
707 nsresult
708 StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, uint32_t* length)
710 StartupCache* sc = StartupCache::GetSingleton();
711 if (!sc) {
712 return NS_ERROR_NOT_INITIALIZED;
714 return sc->GetBuffer(id, outbuf, length);
717 nsresult
718 StartupCacheWrapper::PutBuffer(const char* id, const char* inbuf, uint32_t length)
720 StartupCache* sc = StartupCache::GetSingleton();
721 if (!sc) {
722 return NS_ERROR_NOT_INITIALIZED;
724 return sc->PutBuffer(id, inbuf, length);
727 nsresult
728 StartupCacheWrapper::InvalidateCache()
730 StartupCache* sc = StartupCache::GetSingleton();
731 if (!sc) {
732 return NS_ERROR_NOT_INITIALIZED;
734 sc->InvalidateCache();
735 return NS_OK;
738 nsresult
739 StartupCacheWrapper::IgnoreDiskCache()
741 StartupCache::IgnoreDiskCache();
742 return NS_OK;
745 nsresult
746 StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
747 nsIObjectOutputStream** outStream)
749 StartupCache* sc = StartupCache::GetSingleton();
750 if (!sc) {
751 return NS_ERROR_NOT_INITIALIZED;
753 return sc->GetDebugObjectOutputStream(stream, outStream);
756 nsresult
757 StartupCacheWrapper::StartupWriteComplete(bool *complete)
759 StartupCache* sc = StartupCache::GetSingleton();
760 if (!sc) {
761 return NS_ERROR_NOT_INITIALIZED;
763 sc->WaitOnWriteThread();
764 *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
765 return NS_OK;
768 nsresult
769 StartupCacheWrapper::ResetStartupWriteTimer()
771 StartupCache* sc = StartupCache::GetSingleton();
772 return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED;
775 nsresult
776 StartupCacheWrapper::GetObserver(nsIObserver** obv) {
777 StartupCache* sc = StartupCache::GetSingleton();
778 if (!sc) {
779 return NS_ERROR_NOT_INITIALIZED;
781 NS_ADDREF(*obv = sc->mListener);
782 return NS_OK;
785 nsresult
786 StartupCacheWrapper::RecordAgesAlways() {
787 StartupCache *sc = StartupCache::GetSingleton();
788 return sc ? sc->RecordAgesAlways() : NS_ERROR_NOT_INITIALIZED;
791 } // namespace scache
792 } // namespace mozilla