Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "prio.h"
8 : #include "PLDHashTable.h"
9 : #include "mozilla/IOInterposer.h"
10 : #include "mozilla/MemoryReporting.h"
11 : #include "mozilla/scache/StartupCache.h"
12 :
13 : #include "nsAutoPtr.h"
14 : #include "nsClassHashtable.h"
15 : #include "nsComponentManagerUtils.h"
16 : #include "nsDirectoryServiceUtils.h"
17 : #include "nsIClassInfo.h"
18 : #include "nsIFile.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"
38 : #include "GeckoProfiler.h"
39 :
40 : #ifdef IS_BIG_ENDIAN
41 : #define SC_ENDIAN "big"
42 : #else
43 : #define SC_ENDIAN "little"
44 : #endif
45 :
46 : #if PR_BYTES_PER_WORD == 4
47 : #define SC_WORDSIZE "4"
48 : #else
49 : #define SC_WORDSIZE "8"
50 : #endif
51 :
52 : namespace mozilla {
53 : namespace scache {
54 :
55 0 : MOZ_DEFINE_MALLOC_SIZE_OF(StartupCacheMallocSizeOf)
56 :
57 : NS_IMETHODIMP
58 0 : StartupCache::CollectReports(nsIHandleReportCallback* aHandleReport,
59 : nsISupports* aData, bool aAnonymize)
60 : {
61 0 : MOZ_COLLECT_REPORT(
62 : "explicit/startup-cache/mapping", KIND_NONHEAP, UNITS_BYTES,
63 : SizeOfMapping(),
64 : "Memory used to hold the mapping of the startup cache from file. "
65 0 : "This memory is likely to be swapped out shortly after start-up.");
66 :
67 0 : MOZ_COLLECT_REPORT(
68 : "explicit/startup-cache/data", KIND_HEAP, UNITS_BYTES,
69 : HeapSizeOfIncludingThis(StartupCacheMallocSizeOf),
70 0 : "Memory used by the startup cache for things other than the file mapping.");
71 :
72 0 : return NS_OK;
73 : }
74 :
75 : #define STARTUP_CACHE_NAME "startupCache." SC_WORDSIZE "." SC_ENDIAN
76 :
77 : StartupCache*
78 380 : StartupCache::GetSingleton()
79 : {
80 380 : if (!gStartupCache) {
81 75 : if (!XRE_IsParentProcess()) {
82 74 : return nullptr;
83 : }
84 : #ifdef MOZ_DISABLE_STARTUPCACHE
85 : return nullptr;
86 : #else
87 1 : StartupCache::InitSingleton();
88 : #endif
89 : }
90 :
91 306 : return StartupCache::gStartupCache;
92 : }
93 :
94 : void
95 0 : StartupCache::DeleteSingleton()
96 : {
97 0 : StartupCache::gStartupCache = nullptr;
98 0 : }
99 :
100 : nsresult
101 1 : StartupCache::InitSingleton()
102 : {
103 : nsresult rv;
104 1 : StartupCache::gStartupCache = new StartupCache();
105 :
106 1 : rv = StartupCache::gStartupCache->Init();
107 1 : if (NS_FAILED(rv)) {
108 0 : StartupCache::gStartupCache = nullptr;
109 : }
110 1 : return rv;
111 : }
112 :
113 3 : StaticRefPtr<StartupCache> StartupCache::gStartupCache;
114 : bool StartupCache::gShutdownInitiated;
115 : bool StartupCache::gIgnoreDiskCache;
116 :
117 4 : NS_IMPL_ISUPPORTS(StartupCache, nsIMemoryReporter)
118 :
119 1 : StartupCache::StartupCache()
120 1 : : mArchive(nullptr), mStartupWriteInitiated(false), mWriteThread(nullptr)
121 1 : { }
122 :
123 0 : StartupCache::~StartupCache()
124 : {
125 0 : if (mTimer) {
126 0 : mTimer->Cancel();
127 : }
128 :
129 : // Generally, the in-memory table should be empty here,
130 : // but an early shutdown means either mTimer didn't run
131 : // or the write thread is still running.
132 0 : WaitOnWriteThread();
133 :
134 : // If we shutdown quickly timer wont have fired. Instead of writing
135 : // it on the main thread and block the shutdown we simply wont update
136 : // the startup cache. Always do this if the file doesn't exist since
137 : // we use it part of the package step.
138 0 : if (!mArchive) {
139 0 : WriteToDisk();
140 : }
141 :
142 0 : UnregisterWeakMemoryReporter(this);
143 0 : }
144 :
145 : nsresult
146 1 : StartupCache::Init()
147 : {
148 : // workaround for bug 653936
149 2 : nsCOMPtr<nsIProtocolHandler> jarInitializer(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar"));
150 :
151 : nsresult rv;
152 :
153 : // This allows to override the startup cache filename
154 : // which is useful from xpcshell, when there is no ProfLDS directory to keep cache in.
155 1 : char *env = PR_GetEnv("MOZ_STARTUP_CACHE");
156 1 : if (env && *env) {
157 0 : rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(mFile));
158 : } else {
159 2 : nsCOMPtr<nsIFile> file;
160 1 : rv = NS_GetSpecialDirectory("ProfLDS",
161 2 : getter_AddRefs(file));
162 1 : if (NS_FAILED(rv)) {
163 : // return silently, this will fail in mochitests's xpcshell process.
164 0 : return rv;
165 : }
166 :
167 2 : nsCOMPtr<nsIFile> profDir;
168 1 : NS_GetSpecialDirectory("ProfDS", getter_AddRefs(profDir));
169 1 : if (profDir) {
170 : bool same;
171 1 : if (NS_SUCCEEDED(profDir->Equals(file, &same)) && !same) {
172 : // We no longer store the startup cache in the main profile
173 : // directory, so we should cleanup the old one.
174 0 : if (NS_SUCCEEDED(
175 : profDir->AppendNative(NS_LITERAL_CSTRING("startupCache")))) {
176 0 : profDir->Remove(true);
177 : }
178 : }
179 : }
180 :
181 1 : rv = file->AppendNative(NS_LITERAL_CSTRING("startupCache"));
182 1 : NS_ENSURE_SUCCESS(rv, rv);
183 :
184 : // Try to create the directory if it's not there yet
185 1 : rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
186 1 : if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
187 0 : return rv;
188 :
189 1 : rv = file->AppendNative(NS_LITERAL_CSTRING(STARTUP_CACHE_NAME));
190 :
191 1 : NS_ENSURE_SUCCESS(rv, rv);
192 :
193 1 : mFile = do_QueryInterface(file);
194 : }
195 :
196 1 : NS_ENSURE_TRUE(mFile, NS_ERROR_UNEXPECTED);
197 :
198 1 : mObserverService = do_GetService("@mozilla.org/observer-service;1");
199 :
200 1 : if (!mObserverService) {
201 0 : NS_WARNING("Could not get observerService.");
202 0 : return NS_ERROR_UNEXPECTED;
203 : }
204 :
205 1 : mListener = new StartupCacheListener();
206 1 : rv = mObserverService->AddObserver(mListener, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
207 1 : false);
208 1 : NS_ENSURE_SUCCESS(rv, rv);
209 1 : rv = mObserverService->AddObserver(mListener, "startupcache-invalidate",
210 1 : false);
211 1 : NS_ENSURE_SUCCESS(rv, rv);
212 :
213 1 : rv = LoadArchive();
214 :
215 : // Sometimes we don't have a cache yet, that's ok.
216 : // If it's corrupted, just remove it and start over.
217 1 : if (gIgnoreDiskCache || (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)) {
218 0 : NS_WARNING("Failed to load startupcache file correctly, removing!");
219 0 : InvalidateCache();
220 : }
221 :
222 1 : RegisterWeakMemoryReporter(this);
223 :
224 1 : return NS_OK;
225 : }
226 :
227 : /**
228 : * LoadArchive can be called from the main thread or while reloading cache on write thread.
229 : */
230 : nsresult
231 1 : StartupCache::LoadArchive()
232 : {
233 1 : if (gIgnoreDiskCache)
234 0 : return NS_ERROR_FAILURE;
235 :
236 : bool exists;
237 1 : mArchive = nullptr;
238 1 : nsresult rv = mFile->Exists(&exists);
239 1 : if (NS_FAILED(rv) || !exists)
240 0 : return NS_ERROR_FILE_NOT_FOUND;
241 :
242 1 : mArchive = new nsZipArchive();
243 1 : rv = mArchive->OpenArchive(mFile);
244 1 : return rv;
245 : }
246 :
247 : namespace {
248 :
249 : nsresult
250 289 : GetBufferFromZipArchive(nsZipArchive *zip, bool doCRC, const char* id,
251 : UniquePtr<char[]>* outbuf, uint32_t* length)
252 : {
253 289 : if (!zip)
254 126 : return NS_ERROR_NOT_AVAILABLE;
255 :
256 326 : nsZipItemPtr<char> zipItem(zip, id, doCRC);
257 163 : if (!zipItem)
258 63 : return NS_ERROR_NOT_AVAILABLE;
259 :
260 100 : *outbuf = zipItem.Forget();
261 100 : *length = zipItem.Length();
262 100 : return NS_OK;
263 : }
264 :
265 : } /* anonymous namespace */
266 :
267 : // NOTE: this will not find a new entry until it has been written to disk!
268 : // Consumer should take ownership of the resulting buffer.
269 : nsresult
270 163 : StartupCache::GetBuffer(const char* id, UniquePtr<char[]>* outbuf, uint32_t* length)
271 : {
272 326 : AUTO_PROFILER_LABEL("StartupCache::GetBuffer", OTHER);
273 :
274 163 : NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
275 :
276 163 : WaitOnWriteThread();
277 163 : if (!mStartupWriteInitiated) {
278 : CacheEntry* entry;
279 326 : nsDependentCString idStr(id);
280 163 : mTable.Get(idStr, &entry);
281 163 : if (entry) {
282 0 : *outbuf = MakeUnique<char[]>(entry->size);
283 0 : memcpy(outbuf->get(), entry->data.get(), entry->size);
284 0 : *length = entry->size;
285 0 : return NS_OK;
286 : }
287 : }
288 :
289 163 : nsresult rv = GetBufferFromZipArchive(mArchive, true, id, outbuf, length);
290 163 : if (NS_SUCCEEDED(rv))
291 100 : return rv;
292 :
293 126 : RefPtr<nsZipArchive> omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
294 : // no need to checksum omnijarred entries
295 63 : rv = GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
296 63 : if (NS_SUCCEEDED(rv))
297 0 : return rv;
298 :
299 63 : omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
300 : // no need to checksum omnijarred entries
301 63 : return GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
302 : }
303 :
304 : // Makes a copy of the buffer, client retains ownership of inbuf.
305 : nsresult
306 64 : StartupCache::PutBuffer(const char* id, const char* inbuf, uint32_t len)
307 : {
308 64 : NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
309 64 : WaitOnWriteThread();
310 64 : if (StartupCache::gShutdownInitiated) {
311 0 : return NS_ERROR_NOT_AVAILABLE;
312 : }
313 :
314 128 : auto data = MakeUnique<char[]>(len);
315 64 : memcpy(data.get(), inbuf, len);
316 :
317 128 : nsCString idStr(id);
318 : // Cache it for now, we'll write all together later.
319 : CacheEntry* entry;
320 :
321 64 : if (mTable.Get(idStr)) {
322 0 : NS_WARNING("Existing entry in StartupCache.");
323 : // Double-caching is undesirable but not an error.
324 0 : return NS_OK;
325 : }
326 :
327 : #ifdef DEBUG
328 64 : if (mArchive) {
329 64 : nsZipItem* zipItem = mArchive->GetItem(id);
330 64 : NS_ASSERTION(zipItem == nullptr, "Existing entry in disk StartupCache.");
331 : }
332 : #endif
333 :
334 128 : entry = new CacheEntry(Move(data), len);
335 64 : mTable.Put(idStr, entry);
336 64 : mPendingWrites.AppendElement(idStr);
337 64 : return ResetStartupWriteTimer();
338 : }
339 :
340 : size_t
341 0 : StartupCache::SizeOfMapping()
342 : {
343 0 : return mArchive ? mArchive->SizeOfMapping() : 0;
344 : }
345 :
346 : size_t
347 0 : StartupCache::HeapSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
348 : {
349 : // This function could measure more members, but they haven't been found by
350 : // DMD to be significant. They can be added later if necessary.
351 :
352 0 : size_t n = aMallocSizeOf(this);
353 :
354 0 : n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
355 0 : for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
356 0 : n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
357 : }
358 :
359 0 : n += mPendingWrites.ShallowSizeOfExcludingThis(aMallocSizeOf);
360 :
361 0 : return n;
362 : }
363 :
364 0 : struct CacheWriteHolder
365 : {
366 : nsCOMPtr<nsIZipWriter> writer;
367 : nsCOMPtr<nsIStringInputStream> stream;
368 : PRTime time;
369 : };
370 :
371 : static void
372 0 : CacheCloseHelper(const nsACString& key, const CacheEntry* data,
373 : const CacheWriteHolder* holder)
374 : {
375 0 : MOZ_ASSERT(data); // assert key was found in mTable.
376 :
377 : nsresult rv;
378 0 : nsIStringInputStream* stream = holder->stream;
379 0 : nsIZipWriter* writer = holder->writer;
380 :
381 0 : stream->ShareData(data->data.get(), data->size);
382 :
383 : #ifdef DEBUG
384 : bool hasEntry;
385 0 : rv = writer->HasEntry(key, &hasEntry);
386 0 : NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == false,
387 : "Existing entry in disk StartupCache.");
388 : #endif
389 0 : rv = writer->AddEntryStream(key, holder->time, true, stream, false);
390 :
391 0 : if (NS_FAILED(rv)) {
392 0 : NS_WARNING("cache entry deleted but not written to disk.");
393 : }
394 0 : }
395 :
396 :
397 : /**
398 : * WriteToDisk writes the cache out to disk. Callers of WriteToDisk need to call WaitOnWriteThread
399 : * to make sure there isn't a write happening on another thread
400 : */
401 : void
402 0 : StartupCache::WriteToDisk()
403 : {
404 : nsresult rv;
405 0 : mStartupWriteInitiated = true;
406 :
407 0 : if (mTable.Count() == 0)
408 0 : return;
409 :
410 0 : nsCOMPtr<nsIZipWriter> zipW = do_CreateInstance("@mozilla.org/zipwriter;1");
411 0 : if (!zipW)
412 0 : return;
413 :
414 0 : rv = zipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
415 0 : if (NS_FAILED(rv)) {
416 0 : NS_WARNING("could not open zipfile for write");
417 0 : return;
418 : }
419 :
420 : // If we didn't have an mArchive member, that means that we failed to
421 : // open the startup cache for reading. Therefore, we need to record
422 : // the time of creation in a zipfile comment; this has been useful for
423 : // Telemetry statistics.
424 0 : PRTime now = PR_Now();
425 0 : if (!mArchive) {
426 0 : nsCString comment;
427 0 : comment.Assign((char *)&now, sizeof(now));
428 0 : zipW->SetComment(comment);
429 : }
430 :
431 : nsCOMPtr<nsIStringInputStream> stream
432 0 : = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
433 0 : if (NS_FAILED(rv)) {
434 0 : NS_WARNING("Couldn't create string input stream.");
435 0 : return;
436 : }
437 :
438 0 : CacheWriteHolder holder;
439 0 : holder.stream = stream;
440 0 : holder.writer = zipW;
441 0 : holder.time = now;
442 :
443 0 : for (auto& key : mPendingWrites) {
444 0 : CacheCloseHelper(key, mTable.Get(key), &holder);
445 : }
446 0 : mPendingWrites.Clear();
447 0 : mTable.Clear();
448 :
449 : // Close the archive so Windows doesn't choke.
450 0 : mArchive = nullptr;
451 0 : zipW->Close();
452 :
453 : // We succesfully wrote the archive to disk; mark the disk file as trusted
454 0 : gIgnoreDiskCache = false;
455 :
456 : // Our reader's view of the archive is outdated now, reload it.
457 0 : LoadArchive();
458 : }
459 :
460 : void
461 0 : StartupCache::InvalidateCache()
462 : {
463 0 : WaitOnWriteThread();
464 0 : mPendingWrites.Clear();
465 0 : mTable.Clear();
466 0 : mArchive = nullptr;
467 0 : nsresult rv = mFile->Remove(false);
468 0 : if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
469 : rv != NS_ERROR_FILE_NOT_FOUND) {
470 0 : gIgnoreDiskCache = true;
471 0 : return;
472 : }
473 0 : gIgnoreDiskCache = false;
474 0 : LoadArchive();
475 : }
476 :
477 : void
478 0 : StartupCache::IgnoreDiskCache()
479 : {
480 0 : gIgnoreDiskCache = true;
481 0 : if (gStartupCache)
482 0 : gStartupCache->InvalidateCache();
483 0 : }
484 :
485 : /*
486 : * WaitOnWriteThread() is called from a main thread to wait for the worker
487 : * thread to finish. However since the same code is used in the worker thread and
488 : * main thread, the worker thread can also call WaitOnWriteThread() which is a no-op.
489 : */
490 : void
491 227 : StartupCache::WaitOnWriteThread()
492 : {
493 227 : NS_ASSERTION(NS_IsMainThread(), "Startup cache should only wait for io thread on main thread");
494 227 : if (!mWriteThread || mWriteThread == PR_GetCurrentThread())
495 227 : return;
496 :
497 0 : PR_JoinThread(mWriteThread);
498 0 : mWriteThread = nullptr;
499 : }
500 :
501 : void
502 0 : StartupCache::ThreadedWrite(void *aClosure)
503 : {
504 0 : AutoProfilerRegisterThread registerThread("StartupCache");
505 0 : NS_SetCurrentThreadName("StartupCache");
506 0 : mozilla::IOInterposer::RegisterCurrentThread();
507 : /*
508 : * It is safe to use the pointer passed in aClosure to reference the
509 : * StartupCache object because the thread's lifetime is tightly coupled to
510 : * the lifetime of the StartupCache object; this thread is joined in the
511 : * StartupCache destructor, guaranteeing that this function runs if and only
512 : * if the StartupCache object is valid.
513 : */
514 0 : StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
515 0 : startupCacheObj->WriteToDisk();
516 0 : mozilla::IOInterposer::UnregisterCurrentThread();
517 0 : }
518 :
519 : /*
520 : * The write-thread is spawned on a timeout(which is reset with every write). This
521 : * can avoid a slow shutdown. After writing out the cache, the zipreader is
522 : * reloaded on the worker thread.
523 : */
524 : void
525 0 : StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
526 : {
527 : /*
528 : * It is safe to use the pointer passed in aClosure to reference the
529 : * StartupCache object because the timer's lifetime is tightly coupled to
530 : * the lifetime of the StartupCache object; this timer is canceled in the
531 : * StartupCache destructor, guaranteeing that this function runs if and only
532 : * if the StartupCache object is valid.
533 : */
534 0 : StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
535 0 : startupCacheObj->mWriteThread = PR_CreateThread(PR_USER_THREAD,
536 : StartupCache::ThreadedWrite,
537 : startupCacheObj,
538 : PR_PRIORITY_NORMAL,
539 : PR_GLOBAL_THREAD,
540 : PR_JOINABLE_THREAD,
541 : 0);
542 0 : }
543 :
544 : // We don't want to refcount StartupCache, so we'll just
545 : // hold a ref to this and pass it to observerService instead.
546 3 : NS_IMPL_ISUPPORTS(StartupCacheListener, nsIObserver)
547 :
548 : nsresult
549 0 : StartupCacheListener::Observe(nsISupports *subject, const char* topic, const char16_t* data)
550 : {
551 0 : StartupCache* sc = StartupCache::GetSingleton();
552 0 : if (!sc)
553 0 : return NS_OK;
554 :
555 0 : if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
556 : // Do not leave the thread running past xpcom shutdown
557 0 : sc->WaitOnWriteThread();
558 0 : StartupCache::gShutdownInitiated = true;
559 0 : } else if (strcmp(topic, "startupcache-invalidate") == 0) {
560 0 : sc->InvalidateCache();
561 : }
562 0 : return NS_OK;
563 : }
564 :
565 : nsresult
566 0 : StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
567 : nsIObjectOutputStream** aOutStream)
568 : {
569 0 : NS_ENSURE_ARG_POINTER(aStream);
570 : #ifdef DEBUG
571 0 : auto* stream = new StartupCacheDebugOutputStream(aStream, &mWriteObjectMap);
572 0 : NS_ADDREF(*aOutStream = stream);
573 : #else
574 : NS_ADDREF(*aOutStream = aStream);
575 : #endif
576 :
577 0 : return NS_OK;
578 : }
579 :
580 : nsresult
581 64 : StartupCache::ResetStartupWriteTimer()
582 : {
583 64 : mStartupWriteInitiated = false;
584 : nsresult rv;
585 64 : if (!mTimer)
586 1 : mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
587 : else
588 63 : rv = mTimer->Cancel();
589 64 : NS_ENSURE_SUCCESS(rv, rv);
590 : // Wait for 10 seconds, then write out the cache.
591 64 : mTimer->InitWithNamedFuncCallback(StartupCache::WriteTimeout, this, 60000,
592 : nsITimer::TYPE_ONE_SHOT,
593 64 : "StartupCache::WriteTimeout");
594 64 : return NS_OK;
595 : }
596 :
597 : bool
598 0 : StartupCache::StartupWriteComplete()
599 : {
600 0 : WaitOnWriteThread();
601 0 : return mStartupWriteInitiated && mTable.Count() == 0;
602 : }
603 :
604 : // StartupCacheDebugOutputStream implementation
605 : #ifdef DEBUG
606 0 : NS_IMPL_ISUPPORTS(StartupCacheDebugOutputStream, nsIObjectOutputStream,
607 : nsIBinaryOutputStream, nsIOutputStream)
608 :
609 : bool
610 0 : StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
611 : {
612 : nsresult rv;
613 :
614 0 : nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
615 0 : if (!classInfo) {
616 0 : NS_ERROR("aObject must implement nsIClassInfo");
617 0 : return false;
618 : }
619 :
620 : uint32_t flags;
621 0 : rv = classInfo->GetFlags(&flags);
622 0 : NS_ENSURE_SUCCESS(rv, false);
623 0 : if (flags & nsIClassInfo::SINGLETON)
624 0 : return true;
625 :
626 0 : nsISupportsHashKey* key = mObjectMap->GetEntry(aObject);
627 0 : if (key) {
628 0 : NS_ERROR("non-singleton aObject is referenced multiple times in this"
629 : "serialization, we don't support that.");
630 0 : return false;
631 : }
632 :
633 0 : mObjectMap->PutEntry(aObject);
634 0 : return true;
635 : }
636 :
637 : // nsIObjectOutputStream implementation
638 : nsresult
639 0 : StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
640 : {
641 0 : nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
642 :
643 0 : NS_ASSERTION(rootObject.get() == aObject,
644 : "bad call to WriteObject -- call WriteCompoundObject!");
645 0 : bool check = CheckReferences(aObject);
646 0 : NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
647 0 : return mBinaryStream->WriteObject(aObject, aIsStrongRef);
648 : }
649 :
650 : nsresult
651 0 : StartupCacheDebugOutputStream::WriteSingleRefObject(nsISupports* aObject)
652 : {
653 0 : nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
654 :
655 0 : NS_ASSERTION(rootObject.get() == aObject,
656 : "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
657 0 : bool check = CheckReferences(aObject);
658 0 : NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
659 0 : return mBinaryStream->WriteSingleRefObject(aObject);
660 : }
661 :
662 : nsresult
663 0 : StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports* aObject,
664 : const nsIID& aIID,
665 : bool aIsStrongRef)
666 : {
667 0 : nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
668 :
669 0 : nsCOMPtr<nsISupports> roundtrip;
670 0 : rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
671 0 : NS_ASSERTION(roundtrip.get() == aObject,
672 : "bad aggregation or multiple inheritance detected by call to "
673 : "WriteCompoundObject!");
674 :
675 0 : bool check = CheckReferences(aObject);
676 0 : NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
677 0 : return mBinaryStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
678 : }
679 :
680 : nsresult
681 0 : StartupCacheDebugOutputStream::WriteID(nsID const& aID)
682 : {
683 0 : return mBinaryStream->WriteID(aID);
684 : }
685 :
686 : char*
687 0 : StartupCacheDebugOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
688 : {
689 0 : return mBinaryStream->GetBuffer(aLength, aAlignMask);
690 : }
691 :
692 : void
693 0 : StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
694 : {
695 0 : mBinaryStream->PutBuffer(aBuffer, aLength);
696 0 : }
697 : #endif //DEBUG
698 :
699 : StartupCacheWrapper* StartupCacheWrapper::gStartupCacheWrapper = nullptr;
700 :
701 0 : NS_IMPL_ISUPPORTS(StartupCacheWrapper, nsIStartupCache)
702 :
703 0 : StartupCacheWrapper* StartupCacheWrapper::GetSingleton()
704 : {
705 0 : if (!gStartupCacheWrapper)
706 0 : gStartupCacheWrapper = new StartupCacheWrapper();
707 :
708 0 : NS_ADDREF(gStartupCacheWrapper);
709 0 : return gStartupCacheWrapper;
710 : }
711 :
712 : nsresult
713 0 : StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, uint32_t* length)
714 : {
715 0 : StartupCache* sc = StartupCache::GetSingleton();
716 0 : if (!sc) {
717 0 : return NS_ERROR_NOT_INITIALIZED;
718 : }
719 0 : UniquePtr<char[]> buf;
720 0 : nsresult rv = sc->GetBuffer(id, &buf, length);
721 0 : *outbuf = buf.release();
722 0 : return rv;
723 : }
724 :
725 : nsresult
726 0 : StartupCacheWrapper::PutBuffer(const char* id, const char* inbuf, uint32_t length)
727 : {
728 0 : StartupCache* sc = StartupCache::GetSingleton();
729 0 : if (!sc) {
730 0 : return NS_ERROR_NOT_INITIALIZED;
731 : }
732 0 : return sc->PutBuffer(id, inbuf, length);
733 : }
734 :
735 : nsresult
736 0 : StartupCacheWrapper::InvalidateCache()
737 : {
738 0 : StartupCache* sc = StartupCache::GetSingleton();
739 0 : if (!sc) {
740 0 : return NS_ERROR_NOT_INITIALIZED;
741 : }
742 0 : sc->InvalidateCache();
743 0 : return NS_OK;
744 : }
745 :
746 : nsresult
747 0 : StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
748 : nsIObjectOutputStream** outStream)
749 : {
750 0 : StartupCache* sc = StartupCache::GetSingleton();
751 0 : if (!sc) {
752 0 : return NS_ERROR_NOT_INITIALIZED;
753 : }
754 0 : return sc->GetDebugObjectOutputStream(stream, outStream);
755 : }
756 :
757 : nsresult
758 0 : StartupCacheWrapper::GetObserver(nsIObserver** obv) {
759 0 : StartupCache* sc = StartupCache::GetSingleton();
760 0 : if (!sc) {
761 0 : return NS_ERROR_NOT_INITIALIZED;
762 : }
763 0 : NS_ADDREF(*obv = sc->mListener);
764 0 : return NS_OK;
765 : }
766 :
767 : } // namespace scache
768 : } // namespace mozilla
|