Coverage Report

Created: 2018-09-25 14:53

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