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