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 "ScriptPreloader-inl.h"
8 #include "mozilla/URLPreloader.h"
9 #include "mozilla/loader/AutoMemMap.h"
10
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/ClearOnShutdown.h"
13 #include "mozilla/FileUtils.h"
14 #include "mozilla/IOBuffers.h"
15 #include "mozilla/Logging.h"
16 #include "mozilla/ScopeExit.h"
17 #include "mozilla/Services.h"
18 #include "mozilla/Unused.h"
19 #include "mozilla/Vector.h"
20
21 #include "MainThreadUtils.h"
22 #include "nsPrintfCString.h"
23 #include "nsDebug.h"
24 #include "nsIFile.h"
25 #include "nsIFileURL.h"
26 #include "nsNetUtil.h"
27 #include "nsPromiseFlatString.h"
28 #include "nsProxyRelease.h"
29 #include "nsThreadUtils.h"
30 #include "nsXULAppAPI.h"
31 #include "nsZipArchive.h"
32 #include "xpcpublic.h"
33
34 namespace mozilla {
35 namespace {
36 static LazyLogModule gURLLog("URLPreloader");
37
38 #define LOG(level, ...) MOZ_LOG(gURLLog, LogLevel::level, (__VA_ARGS__))
39
40 template <typename T>
StartsWith(const T & haystack,const T & needle)41 bool StartsWith(const T& haystack, const T& needle) {
42 return StringHead(haystack, needle.Length()) == needle;
43 }
44 } // anonymous namespace
45
46 using namespace mozilla::loader;
47
CollectReports(nsIHandleReportCallback * aHandleReport,nsISupports * aData,bool aAnonymize)48 nsresult URLPreloader::CollectReports(nsIHandleReportCallback* aHandleReport,
49 nsISupports* aData, bool aAnonymize) {
50 MOZ_COLLECT_REPORT("explicit/url-preloader/other", KIND_HEAP, UNITS_BYTES,
51 ShallowSizeOfIncludingThis(MallocSizeOf),
52 "Memory used by the URL preloader service itself.");
53
54 for (const auto& elem : mCachedURLs.Values()) {
55 nsAutoCString pathName;
56 pathName.Append(elem->mPath);
57 // The backslashes will automatically be replaced with slashes in
58 // about:memory, without splitting each path component into a separate
59 // branch in the memory report tree.
60 pathName.ReplaceChar('/', '\\');
61
62 nsPrintfCString path("explicit/url-preloader/cached-urls/%s/[%s]",
63 elem->TypeString(), pathName.get());
64
65 aHandleReport->Callback(
66 ""_ns, path, KIND_HEAP, UNITS_BYTES,
67 elem->SizeOfIncludingThis(MallocSizeOf),
68 nsLiteralCString("Memory used to hold cache data for files which "
69 "have been read or pre-loaded during this session."),
70 aData);
71 }
72
73 return NS_OK;
74 }
75
76 // static
Create(bool * aInitialized)77 already_AddRefed<URLPreloader> URLPreloader::Create(bool* aInitialized) {
78 // The static APIs like URLPreloader::Read work in the child process because
79 // they fall back to a synchronous read. The actual preloader must be
80 // explicitly initialized, and this should only be done in the parent.
81 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
82
83 RefPtr<URLPreloader> preloader = new URLPreloader();
84 if (preloader->InitInternal().isOk()) {
85 *aInitialized = true;
86 RegisterWeakMemoryReporter(preloader);
87 } else {
88 *aInitialized = false;
89 }
90
91 return preloader.forget();
92 }
93
GetSingleton()94 URLPreloader& URLPreloader::GetSingleton() {
95 if (!sSingleton) {
96 sSingleton = Create(&sInitialized);
97 ClearOnShutdown(&sSingleton);
98 }
99
100 return *sSingleton;
101 }
102
103 bool URLPreloader::sInitialized = false;
104
105 StaticRefPtr<URLPreloader> URLPreloader::sSingleton;
106
~URLPreloader()107 URLPreloader::~URLPreloader() {
108 if (sInitialized) {
109 UnregisterWeakMemoryReporter(this);
110 sInitialized = false;
111 }
112 }
113
InitInternal()114 Result<Ok, nsresult> URLPreloader::InitInternal() {
115 MOZ_RELEASE_ASSERT(NS_IsMainThread());
116
117 if (Omnijar::HasOmnijar(Omnijar::GRE)) {
118 MOZ_TRY(Omnijar::GetURIString(Omnijar::GRE, mGREPrefix));
119 }
120 if (Omnijar::HasOmnijar(Omnijar::APP)) {
121 MOZ_TRY(Omnijar::GetURIString(Omnijar::APP, mAppPrefix));
122 }
123
124 nsresult rv;
125 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
126 MOZ_TRY(rv);
127
128 nsCOMPtr<nsIProtocolHandler> ph;
129 MOZ_TRY(ios->GetProtocolHandler("resource", getter_AddRefs(ph)));
130
131 mResProto = do_QueryInterface(ph, &rv);
132 MOZ_TRY(rv);
133
134 mChromeReg = services::GetChromeRegistry();
135 if (!mChromeReg) {
136 return Err(NS_ERROR_UNEXPECTED);
137 }
138
139 MOZ_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD)));
140
141 return Ok();
142 }
143
ReInitialize()144 URLPreloader& URLPreloader::ReInitialize() {
145 MOZ_ASSERT(sSingleton);
146 sSingleton = nullptr;
147 sSingleton = Create(&sInitialized);
148 return *sSingleton;
149 }
150
GetCacheFile(const nsAString & suffix)151 Result<nsCOMPtr<nsIFile>, nsresult> URLPreloader::GetCacheFile(
152 const nsAString& suffix) {
153 if (!mProfD) {
154 return Err(NS_ERROR_NOT_INITIALIZED);
155 }
156
157 nsCOMPtr<nsIFile> cacheFile;
158 MOZ_TRY(mProfD->Clone(getter_AddRefs(cacheFile)));
159
160 MOZ_TRY(cacheFile->AppendNative("startupCache"_ns));
161 Unused << cacheFile->Create(nsIFile::DIRECTORY_TYPE, 0777);
162
163 MOZ_TRY(cacheFile->Append(u"urlCache"_ns + suffix));
164
165 return std::move(cacheFile);
166 }
167
168 static const uint8_t URL_MAGIC[] = "mozURLcachev002";
169
FindCacheFile()170 Result<nsCOMPtr<nsIFile>, nsresult> URLPreloader::FindCacheFile() {
171 nsCOMPtr<nsIFile> cacheFile;
172 MOZ_TRY_VAR(cacheFile, GetCacheFile(u".bin"_ns));
173
174 bool exists;
175 MOZ_TRY(cacheFile->Exists(&exists));
176 if (exists) {
177 MOZ_TRY(cacheFile->MoveTo(nullptr, u"urlCache-current.bin"_ns));
178 } else {
179 MOZ_TRY(cacheFile->SetLeafName(u"urlCache-current.bin"_ns));
180 MOZ_TRY(cacheFile->Exists(&exists));
181 if (!exists) {
182 return Err(NS_ERROR_FILE_NOT_FOUND);
183 }
184 }
185
186 return std::move(cacheFile);
187 }
188
WriteCache()189 Result<Ok, nsresult> URLPreloader::WriteCache() {
190 MOZ_ASSERT(!NS_IsMainThread());
191 MOZ_DIAGNOSTIC_ASSERT(mStartupFinished);
192
193 // The script preloader might call us a second time, if it has to re-write
194 // its cache after a cache flush. We don't care about cache flushes, since
195 // our cache doesn't store any file data, only paths. And we currently clear
196 // our cached file list after the first write, which means that a second
197 // write would (aside from breaking the invariant that we never touch
198 // mCachedURLs off-main-thread after the first write, and trigger a data
199 // race) mean we get no pre-loading on the next startup.
200 if (mCacheWritten) {
201 return Ok();
202 }
203 mCacheWritten = true;
204
205 LOG(Debug, "Writing cache...");
206
207 nsCOMPtr<nsIFile> cacheFile;
208 MOZ_TRY_VAR(cacheFile, GetCacheFile(u"-new.bin"_ns));
209
210 bool exists;
211 MOZ_TRY(cacheFile->Exists(&exists));
212 if (exists) {
213 MOZ_TRY(cacheFile->Remove(false));
214 }
215
216 {
217 AutoFDClose fd;
218 MOZ_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644,
219 &fd.rwget()));
220
221 nsTArray<URLEntry*> entries;
222 for (const auto& entry : mCachedURLs.Values()) {
223 if (entry->mReadTime) {
224 entries.AppendElement(entry.get());
225 }
226 }
227
228 entries.Sort(URLEntry::Comparator());
229
230 OutputBuffer buf;
231 for (auto entry : entries) {
232 entry->Code(buf);
233 }
234
235 uint8_t headerSize[4];
236 LittleEndian::writeUint32(headerSize, buf.cursor());
237
238 MOZ_TRY(Write(fd, URL_MAGIC, sizeof(URL_MAGIC)));
239 MOZ_TRY(Write(fd, headerSize, sizeof(headerSize)));
240 MOZ_TRY(Write(fd, buf.Get(), buf.cursor()));
241 }
242
243 MOZ_TRY(cacheFile->MoveTo(nullptr, u"urlCache.bin"_ns));
244
245 NS_DispatchToMainThread(
246 NewRunnableMethod("URLPreloader::Cleanup", this, &URLPreloader::Cleanup));
247
248 return Ok();
249 }
250
Cleanup()251 void URLPreloader::Cleanup() { mCachedURLs.Clear(); }
252
ReadCache(LinkedList<URLEntry> & pendingURLs)253 Result<Ok, nsresult> URLPreloader::ReadCache(
254 LinkedList<URLEntry>& pendingURLs) {
255 LOG(Debug, "Reading cache...");
256
257 nsCOMPtr<nsIFile> cacheFile;
258 MOZ_TRY_VAR(cacheFile, FindCacheFile());
259
260 AutoMemMap cache;
261 MOZ_TRY(cache.init(cacheFile));
262
263 auto size = cache.size();
264
265 uint32_t headerSize;
266 if (size < sizeof(URL_MAGIC) + sizeof(headerSize)) {
267 return Err(NS_ERROR_UNEXPECTED);
268 }
269
270 auto data = cache.get<uint8_t>();
271 auto end = data + size;
272
273 if (memcmp(URL_MAGIC, data.get(), sizeof(URL_MAGIC))) {
274 return Err(NS_ERROR_UNEXPECTED);
275 }
276 data += sizeof(URL_MAGIC);
277
278 headerSize = LittleEndian::readUint32(data.get());
279 data += sizeof(headerSize);
280
281 if (data + headerSize > end) {
282 return Err(NS_ERROR_UNEXPECTED);
283 }
284
285 {
286 mMonitor.AssertCurrentThreadOwns();
287
288 auto cleanup = MakeScopeExit([&]() {
289 while (auto* elem = pendingURLs.getFirst()) {
290 elem->remove();
291 }
292 mCachedURLs.Clear();
293 });
294
295 Range<uint8_t> header(data, data + headerSize);
296 data += headerSize;
297
298 InputBuffer buf(header);
299 while (!buf.finished()) {
300 CacheKey key(buf);
301
302 LOG(Debug, "Cached file: %s %s", key.TypeString(), key.mPath.get());
303
304 // Don't bother doing anything else if the key didn't load correctly.
305 // We're going to throw it out right away, and it is possible that this
306 // leads to pendingURLs getting into a weird state.
307 if (buf.error()) {
308 return Err(NS_ERROR_UNEXPECTED);
309 }
310
311 auto entry = mCachedURLs.GetOrInsertNew(key, key);
312 entry->mResultCode = NS_ERROR_NOT_INITIALIZED;
313
314 if (entry->isInList()) {
315 #ifdef NIGHTLY_BUILD
316 MOZ_DIAGNOSTIC_ASSERT(pendingURLs.contains(entry),
317 "Entry should be in pendingURLs");
318 MOZ_DIAGNOSTIC_ASSERT(key.mPath.Length() > 0,
319 "Path should be non-empty");
320 MOZ_DIAGNOSTIC_ASSERT(false, "Entry should be new and not in any list");
321 #endif
322 return Err(NS_ERROR_UNEXPECTED);
323 }
324
325 pendingURLs.insertBack(entry);
326 }
327
328 MOZ_RELEASE_ASSERT(!buf.error(),
329 "We should have already bailed on an error");
330
331 cleanup.release();
332 }
333
334 return Ok();
335 }
336
BackgroundReadFiles()337 void URLPreloader::BackgroundReadFiles() {
338 auto cleanup = MakeScopeExit([&]() {
339 auto lock = mReaderThread.Lock();
340 auto& readerThread = lock.ref();
341 NS_DispatchToMainThread(NewRunnableMethod(
342 "nsIThread::AsyncShutdown", readerThread, &nsIThread::AsyncShutdown));
343
344 readerThread = nullptr;
345 });
346
347 Vector<nsZipCursor> cursors;
348 LinkedList<URLEntry> pendingURLs;
349 {
350 MonitorAutoLock mal(mMonitor);
351
352 if (ReadCache(pendingURLs).isErr()) {
353 mReaderInitialized = true;
354 mal.NotifyAll();
355 return;
356 }
357
358 int numZipEntries = 0;
359 for (auto entry : pendingURLs) {
360 if (entry->mType != entry->TypeFile) {
361 numZipEntries++;
362 }
363 }
364 MOZ_RELEASE_ASSERT(cursors.reserve(numZipEntries));
365
366 // Initialize the zip cursors for all files in Omnijar while the monitor
367 // is locked. Omnijar is not threadsafe, so the caller of
368 // AutoBeginReading guard must ensure that no code accesses Omnijar
369 // until this segment is done. Once the cursors have been initialized,
370 // the actual reading and decompression can safely be done off-thread,
371 // as is the case for thread-retargeted jar: channels.
372 for (auto entry : pendingURLs) {
373 if (entry->mType == entry->TypeFile) {
374 continue;
375 }
376
377 RefPtr<nsZipArchive> zip = entry->Archive();
378 if (!zip) {
379 MOZ_CRASH_UNSAFE_PRINTF(
380 "Failed to get Omnijar %s archive for entry (path: \"%s\")",
381 entry->TypeString(), entry->mPath.get());
382 }
383
384 auto item = zip->GetItem(entry->mPath.get());
385 if (!item) {
386 entry->mResultCode = NS_ERROR_FILE_NOT_FOUND;
387 continue;
388 }
389
390 size_t size = item->RealSize();
391
392 entry->mData.SetLength(size);
393 auto data = entry->mData.BeginWriting();
394
395 cursors.infallibleEmplaceBack(item, zip, reinterpret_cast<uint8_t*>(data),
396 size, true);
397 }
398
399 mReaderInitialized = true;
400 mal.NotifyAll();
401 }
402
403 // Loop over the entries, read the file's contents, store them in the
404 // entry's mData pointer, and notify any waiting threads to check for
405 // completion.
406 uint32_t i = 0;
407 for (auto entry : pendingURLs) {
408 // If there is any other error code, the entry has already failed at
409 // this point, so don't bother trying to read it again.
410 if (entry->mResultCode != NS_ERROR_NOT_INITIALIZED) {
411 continue;
412 }
413
414 nsresult rv = NS_OK;
415
416 LOG(Debug, "Background reading %s file %s", entry->TypeString(),
417 entry->mPath.get());
418
419 if (entry->mType == entry->TypeFile) {
420 auto result = entry->Read();
421 if (result.isErr()) {
422 rv = result.unwrapErr();
423 }
424 } else {
425 auto& cursor = cursors[i++];
426
427 uint32_t len;
428 cursor.Copy(&len);
429 if (len != entry->mData.Length()) {
430 entry->mData.Truncate();
431 rv = NS_ERROR_FAILURE;
432 }
433 }
434
435 entry->mResultCode = rv;
436 mMonitor.NotifyAll();
437 }
438
439 // We're done reading pending entries, so clear the list.
440 pendingURLs.clear();
441 }
442
BeginBackgroundRead()443 void URLPreloader::BeginBackgroundRead() {
444 auto lock = mReaderThread.Lock();
445 auto& readerThread = lock.ref();
446 if (!readerThread && !mReaderInitialized && sInitialized) {
447 nsresult rv;
448 rv = NS_NewNamedThread("BGReadURLs", getter_AddRefs(readerThread));
449 if (NS_WARN_IF(NS_FAILED(rv))) {
450 return;
451 }
452
453 nsCOMPtr<nsIRunnable> runnable =
454 NewRunnableMethod("URLPreloader::BackgroundReadFiles", this,
455 &URLPreloader::BackgroundReadFiles);
456 rv = readerThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
457 if (NS_WARN_IF(NS_FAILED(rv))) {
458 // If we can't launch the task, just destroy the thread
459 readerThread = nullptr;
460 return;
461 }
462 }
463 }
464
ReadInternal(const CacheKey & key,ReadType readType)465 Result<nsCString, nsresult> URLPreloader::ReadInternal(const CacheKey& key,
466 ReadType readType) {
467 if (mStartupFinished || !mReaderInitialized) {
468 URLEntry entry(key);
469
470 return entry.Read();
471 }
472
473 auto entry = mCachedURLs.GetOrInsertNew(key, key);
474
475 entry->UpdateUsedTime();
476
477 return entry->ReadOrWait(readType);
478 }
479
ReadURIInternal(nsIURI * uri,ReadType readType)480 Result<nsCString, nsresult> URLPreloader::ReadURIInternal(nsIURI* uri,
481 ReadType readType) {
482 CacheKey key;
483 MOZ_TRY_VAR(key, ResolveURI(uri));
484
485 return ReadInternal(key, readType);
486 }
487
Read(const CacheKey & key,ReadType readType)488 /* static */ Result<nsCString, nsresult> URLPreloader::Read(const CacheKey& key,
489 ReadType readType) {
490 // If we're being called before the preloader has been initialized (i.e.,
491 // before the profile has been initialized), just fall back to a synchronous
492 // read. This happens when we're reading .ini and preference files that are
493 // needed to locate and initialize the profile.
494 if (!sInitialized) {
495 return URLEntry(key).Read();
496 }
497
498 return GetSingleton().ReadInternal(key, readType);
499 }
500
ReadURI(nsIURI * uri,ReadType readType)501 /* static */ Result<nsCString, nsresult> URLPreloader::ReadURI(
502 nsIURI* uri, ReadType readType) {
503 if (!sInitialized) {
504 return Err(NS_ERROR_NOT_INITIALIZED);
505 }
506
507 return GetSingleton().ReadURIInternal(uri, readType);
508 }
509
ReadFile(nsIFile * file,ReadType readType)510 /* static */ Result<nsCString, nsresult> URLPreloader::ReadFile(
511 nsIFile* file, ReadType readType) {
512 return Read(CacheKey(file), readType);
513 }
514
Read(FileLocation & location,ReadType readType)515 /* static */ Result<nsCString, nsresult> URLPreloader::Read(
516 FileLocation& location, ReadType readType) {
517 if (location.IsZip()) {
518 if (location.GetBaseZip()) {
519 nsCString path;
520 location.GetPath(path);
521 return ReadZip(location.GetBaseZip(), path);
522 }
523 return URLEntry::ReadLocation(location);
524 }
525
526 nsCOMPtr<nsIFile> file = location.GetBaseFile();
527 return ReadFile(file, readType);
528 }
529
ReadZip(nsZipArchive * zip,const nsACString & path,ReadType readType)530 /* static */ Result<nsCString, nsresult> URLPreloader::ReadZip(
531 nsZipArchive* zip, const nsACString& path, ReadType readType) {
532 // If the zip archive belongs to an Omnijar location, map it to a cache
533 // entry, and cache it as normal. Otherwise, simply read the entry
534 // synchronously, since other JAR archives are currently unsupported by the
535 // cache.
536 RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::GRE);
537 if (zip == reader) {
538 CacheKey key(CacheKey::TypeGREJar, path);
539 return Read(key, readType);
540 }
541
542 reader = Omnijar::GetReader(Omnijar::APP);
543 if (zip == reader) {
544 CacheKey key(CacheKey::TypeAppJar, path);
545 return Read(key, readType);
546 }
547
548 // Not an Omnijar archive, so just read it directly.
549 FileLocation location(zip, PromiseFlatCString(path).BeginReading());
550 return URLEntry::ReadLocation(location);
551 }
552
ResolveURI(nsIURI * uri)553 Result<URLPreloader::CacheKey, nsresult> URLPreloader::ResolveURI(nsIURI* uri) {
554 nsCString spec;
555 nsCString scheme;
556 MOZ_TRY(uri->GetSpec(spec));
557 MOZ_TRY(uri->GetScheme(scheme));
558
559 nsCOMPtr<nsIURI> resolved;
560
561 // If the URI is a resource: or chrome: URI, first resolve it to the
562 // underlying URI that it wraps.
563 if (scheme.EqualsLiteral("resource")) {
564 MOZ_TRY(mResProto->ResolveURI(uri, spec));
565 MOZ_TRY(NS_NewURI(getter_AddRefs(resolved), spec));
566 } else if (scheme.EqualsLiteral("chrome")) {
567 MOZ_TRY(mChromeReg->ConvertChromeURL(uri, getter_AddRefs(resolved)));
568 MOZ_TRY(resolved->GetSpec(spec));
569 } else {
570 resolved = uri;
571 }
572 MOZ_TRY(resolved->GetScheme(scheme));
573
574 // Try the GRE and App Omnijar prefixes.
575 if (mGREPrefix.Length() && StartsWith(spec, mGREPrefix)) {
576 return CacheKey(CacheKey::TypeGREJar, Substring(spec, mGREPrefix.Length()));
577 }
578
579 if (mAppPrefix.Length() && StartsWith(spec, mAppPrefix)) {
580 return CacheKey(CacheKey::TypeAppJar, Substring(spec, mAppPrefix.Length()));
581 }
582
583 // Try for a file URI.
584 if (scheme.EqualsLiteral("file")) {
585 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(resolved);
586 MOZ_ASSERT(fileURL);
587
588 nsCOMPtr<nsIFile> file;
589 MOZ_TRY(fileURL->GetFile(getter_AddRefs(file)));
590
591 nsString path;
592 MOZ_TRY(file->GetPath(path));
593
594 return CacheKey(CacheKey::TypeFile, NS_ConvertUTF16toUTF8(path));
595 }
596
597 // Not a file or Omnijar URI, so currently unsupported.
598 return Err(NS_ERROR_INVALID_ARG);
599 }
600
ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)601 size_t URLPreloader::ShallowSizeOfIncludingThis(
602 mozilla::MallocSizeOf mallocSizeOf) {
603 return (mallocSizeOf(this) +
604 mAppPrefix.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
605 mGREPrefix.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
606 mCachedURLs.ShallowSizeOfExcludingThis(mallocSizeOf));
607 }
608
ToFileLocation()609 Result<FileLocation, nsresult> URLPreloader::CacheKey::ToFileLocation() {
610 if (mType == TypeFile) {
611 nsCOMPtr<nsIFile> file;
612 MOZ_TRY(NS_NewLocalFile(NS_ConvertUTF8toUTF16(mPath), false,
613 getter_AddRefs(file)));
614 return FileLocation(file);
615 }
616
617 RefPtr<nsZipArchive> zip = Archive();
618 return FileLocation(zip, mPath.get());
619 }
620
Read()621 Result<nsCString, nsresult> URLPreloader::URLEntry::Read() {
622 FileLocation location;
623 MOZ_TRY_VAR(location, ToFileLocation());
624
625 MOZ_TRY_VAR(mData, ReadLocation(location));
626 return mData;
627 }
628
ReadLocation(FileLocation & location)629 /* static */ Result<nsCString, nsresult> URLPreloader::URLEntry::ReadLocation(
630 FileLocation& location) {
631 FileLocation::Data data;
632 MOZ_TRY(location.GetData(data));
633
634 uint32_t size;
635 MOZ_TRY(data.GetSize(&size));
636
637 nsCString result;
638 result.SetLength(size);
639 MOZ_TRY(data.Copy(result.BeginWriting(), size));
640
641 return std::move(result);
642 }
643
ReadOrWait(ReadType readType)644 Result<nsCString, nsresult> URLPreloader::URLEntry::ReadOrWait(
645 ReadType readType) {
646 auto now = TimeStamp::Now();
647 LOG(Info, "Reading %s\n", mPath.get());
648 auto cleanup = MakeScopeExit([&]() {
649 LOG(Info, "Read in %fms\n", (TimeStamp::Now() - now).ToMilliseconds());
650 });
651
652 if (mResultCode == NS_ERROR_NOT_INITIALIZED) {
653 MonitorAutoLock mal(GetSingleton().mMonitor);
654
655 while (mResultCode == NS_ERROR_NOT_INITIALIZED) {
656 mal.Wait();
657 }
658 }
659
660 if (mResultCode == NS_OK && mData.IsVoid()) {
661 LOG(Info, "Reading synchronously...\n");
662 return Read();
663 }
664
665 if (NS_FAILED(mResultCode)) {
666 return Err(mResultCode);
667 }
668
669 nsCString res = mData;
670
671 if (readType == Forget) {
672 mData.SetIsVoid(true);
673 }
674 return res;
675 }
676
CacheKey(InputBuffer & buffer)677 inline URLPreloader::CacheKey::CacheKey(InputBuffer& buffer) {
678 Code(buffer);
679 MOZ_DIAGNOSTIC_ASSERT(
680 mType == TypeAppJar || mType == TypeGREJar || mType == TypeFile,
681 "mType should be valid");
682 }
683
684 NS_IMPL_ISUPPORTS(URLPreloader, nsIMemoryReporter)
685
686 #undef LOG
687
688 } // namespace mozilla
689