Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/asmjscache/AsmJSCache.cpp
Line
Count
Source (jump to first uncovered line)
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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "AsmJSCache.h"
8
9
#include <stdio.h>
10
11
#include "js/RootingAPI.h"
12
#include "jsfriendapi.h"
13
#include "mozilla/Assertions.h"
14
#include "mozilla/CondVar.h"
15
#include "mozilla/CycleCollectedJSRuntime.h"
16
#include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
17
#include "mozilla/dom/asmjscache/PAsmJSCacheEntryParent.h"
18
#include "mozilla/dom/ContentChild.h"
19
#include "mozilla/dom/PermissionMessageUtils.h"
20
#include "mozilla/dom/quota/Client.h"
21
#include "mozilla/dom/quota/QuotaManager.h"
22
#include "mozilla/dom/quota/QuotaObject.h"
23
#include "mozilla/dom/quota/UsageInfo.h"
24
#include "mozilla/HashFunctions.h"
25
#include "mozilla/ipc/BackgroundChild.h"
26
#include "mozilla/ipc/BackgroundParent.h"
27
#include "mozilla/ipc/BackgroundUtils.h"
28
#include "mozilla/ipc/PBackgroundChild.h"
29
#include "mozilla/Unused.h"
30
#include "nsAutoPtr.h"
31
#include "nsAtom.h"
32
#include "nsIFile.h"
33
#include "nsIPrincipal.h"
34
#include "nsIRunnable.h"
35
#include "nsISimpleEnumerator.h"
36
#include "nsIThread.h"
37
#include "nsJSPrincipals.h"
38
#include "nsThreadUtils.h"
39
#include "nsXULAppAPI.h"
40
#include "prio.h"
41
#include "private/pprio.h"
42
#include "mozilla/Services.h"
43
44
#define ASMJSCACHE_METADATA_FILE_NAME "metadata"
45
#define ASMJSCACHE_ENTRY_FILE_NAME_BASE "module"
46
47
using mozilla::dom::quota::AssertIsOnIOThread;
48
using mozilla::dom::quota::DirectoryLock;
49
using mozilla::dom::quota::PersistenceType;
50
using mozilla::dom::quota::QuotaManager;
51
using mozilla::dom::quota::QuotaObject;
52
using mozilla::dom::quota::UsageInfo;
53
using mozilla::ipc::AssertIsOnBackgroundThread;
54
using mozilla::ipc::BackgroundChild;
55
using mozilla::ipc::IsOnBackgroundThread;
56
using mozilla::ipc::PBackgroundChild;
57
using mozilla::ipc::PrincipalInfo;
58
using mozilla::Unused;
59
using mozilla::HashString;
60
61
namespace mozilla {
62
63
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
64
65
namespace dom {
66
namespace asmjscache {
67
68
namespace {
69
70
class ParentRunnable;
71
72
// Anything smaller should compile fast enough that caching will just add
73
// overhead.
74
static const size_t sMinCachedModuleLength = 10000;
75
76
// The number of characters to hash into the Metadata::Entry::mFastHash.
77
static const unsigned sNumFastHashChars = 4096;
78
79
// Track all live parent actors.
80
typedef nsTArray<const ParentRunnable*> ParentActorArray;
81
StaticAutoPtr<ParentActorArray> sLiveParentActors;
82
83
nsresult
84
WriteMetadataFile(nsIFile* aMetadataFile, const Metadata& aMetadata)
85
0
{
86
0
  int32_t openFlags = PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE;
87
0
88
0
  JS::BuildIdCharVector buildId;
89
0
  bool ok = GetBuildId(&buildId);
90
0
  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
91
0
92
0
  ScopedPRFileDesc fd;
93
0
  nsresult rv = aMetadataFile->OpenNSPRFileDesc(openFlags, 0644, &fd.rwget());
94
0
  NS_ENSURE_SUCCESS(rv, rv);
95
0
96
0
  uint32_t length = buildId.length();
97
0
  int32_t bytesWritten = PR_Write(fd, &length, sizeof(length));
98
0
  NS_ENSURE_TRUE(bytesWritten == sizeof(length), NS_ERROR_UNEXPECTED);
99
0
100
0
  bytesWritten = PR_Write(fd, buildId.begin(), length);
101
0
  NS_ENSURE_TRUE(bytesWritten == int32_t(length), NS_ERROR_UNEXPECTED);
102
0
103
0
  bytesWritten = PR_Write(fd, &aMetadata, sizeof(aMetadata));
104
0
  NS_ENSURE_TRUE(bytesWritten == sizeof(aMetadata), NS_ERROR_UNEXPECTED);
105
0
106
0
  return NS_OK;
107
0
}
108
109
nsresult
110
ReadMetadataFile(nsIFile* aMetadataFile, Metadata& aMetadata)
111
0
{
112
0
  int32_t openFlags = PR_RDONLY;
113
0
114
0
  ScopedPRFileDesc fd;
115
0
  nsresult rv = aMetadataFile->OpenNSPRFileDesc(openFlags, 0644, &fd.rwget());
116
0
  NS_ENSURE_SUCCESS(rv, rv);
117
0
118
0
  // Read the buildid and check that it matches the current buildid
119
0
120
0
  JS::BuildIdCharVector currentBuildId;
121
0
  bool ok = GetBuildId(&currentBuildId);
122
0
  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
123
0
124
0
  uint32_t length;
125
0
  int32_t bytesRead = PR_Read(fd, &length, sizeof(length));
126
0
  NS_ENSURE_TRUE(bytesRead == sizeof(length), NS_ERROR_UNEXPECTED);
127
0
128
0
  NS_ENSURE_TRUE(currentBuildId.length() == length, NS_ERROR_UNEXPECTED);
129
0
130
0
  JS::BuildIdCharVector fileBuildId;
131
0
  ok = fileBuildId.resize(length);
132
0
  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
133
0
134
0
  bytesRead = PR_Read(fd, fileBuildId.begin(), length);
135
0
  NS_ENSURE_TRUE(bytesRead == int32_t(length), NS_ERROR_UNEXPECTED);
136
0
137
0
  for (uint32_t i = 0; i < length; i++) {
138
0
    if (currentBuildId[i] != fileBuildId[i]) {
139
0
      return NS_ERROR_FAILURE;
140
0
    }
141
0
  }
142
0
143
0
  // Read the Metadata struct
144
0
145
0
  bytesRead = PR_Read(fd, &aMetadata, sizeof(aMetadata));
146
0
  NS_ENSURE_TRUE(bytesRead == sizeof(aMetadata), NS_ERROR_UNEXPECTED);
147
0
148
0
  return NS_OK;
149
0
}
150
151
nsresult
152
GetCacheFile(nsIFile* aDirectory, unsigned aModuleIndex, nsIFile** aCacheFile)
153
0
{
154
0
  nsCOMPtr<nsIFile> cacheFile;
155
0
  nsresult rv = aDirectory->Clone(getter_AddRefs(cacheFile));
156
0
  NS_ENSURE_SUCCESS(rv, rv);
157
0
158
0
  nsString cacheFileName = NS_LITERAL_STRING(ASMJSCACHE_ENTRY_FILE_NAME_BASE);
159
0
  cacheFileName.AppendInt(aModuleIndex);
160
0
  rv = cacheFile->Append(cacheFileName);
161
0
  NS_ENSURE_SUCCESS(rv, rv);
162
0
163
0
  cacheFile.forget(aCacheFile);
164
0
  return NS_OK;
165
0
}
166
167
class AutoDecreaseUsageForOrigin
168
{
169
  const nsACString& mGroup;
170
  const nsACString& mOrigin;
171
172
public:
173
  uint64_t mFreed;
174
175
  AutoDecreaseUsageForOrigin(const nsACString& aGroup,
176
                             const nsACString& aOrigin)
177
178
  : mGroup(aGroup),
179
    mOrigin(aOrigin),
180
    mFreed(0)
181
0
  { }
182
183
  ~AutoDecreaseUsageForOrigin()
184
0
  {
185
0
    AssertIsOnIOThread();
186
0
187
0
    if (!mFreed) {
188
0
      return;
189
0
    }
190
0
191
0
    QuotaManager* qm = QuotaManager::Get();
192
0
    MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
193
0
194
0
    qm->DecreaseUsageForOrigin(quota::PERSISTENCE_TYPE_TEMPORARY,
195
0
                               mGroup, mOrigin, mFreed);
196
0
  }
197
};
198
199
static void
200
EvictEntries(nsIFile* aDirectory, const nsACString& aGroup,
201
             const nsACString& aOrigin, uint64_t aNumBytes,
202
             Metadata& aMetadata)
203
0
{
204
0
  AssertIsOnIOThread();
205
0
206
0
  AutoDecreaseUsageForOrigin usage(aGroup, aOrigin);
207
0
208
0
  for (int i = Metadata::kLastEntry; i >= 0 && usage.mFreed < aNumBytes; i--) {
209
0
    Metadata::Entry& entry = aMetadata.mEntries[i];
210
0
    unsigned moduleIndex = entry.mModuleIndex;
211
0
212
0
    nsCOMPtr<nsIFile> file;
213
0
    nsresult rv = GetCacheFile(aDirectory, moduleIndex, getter_AddRefs(file));
214
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
215
0
      return;
216
0
    }
217
0
218
0
    bool exists;
219
0
    rv = file->Exists(&exists);
220
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
221
0
      return;
222
0
    }
223
0
224
0
    if (exists) {
225
0
      int64_t fileSize;
226
0
      rv = file->GetFileSize(&fileSize);
227
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
228
0
        return;
229
0
      }
230
0
231
0
      rv = file->Remove(false);
232
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
233
0
        return;
234
0
      }
235
0
236
0
      usage.mFreed += fileSize;
237
0
    }
238
0
239
0
    entry.clear();
240
0
  }
241
0
}
242
243
/*******************************************************************************
244
 * Client
245
 ******************************************************************************/
246
247
class Client
248
  : public quota::Client
249
{
250
  static Client* sInstance;
251
252
  bool mShutdownRequested;
253
254
public:
255
  Client();
256
257
  static bool
258
  IsShuttingDownOnBackgroundThread()
259
0
  {
260
0
    AssertIsOnBackgroundThread();
261
0
262
0
    if (sInstance) {
263
0
      return sInstance->IsShuttingDown();
264
0
    }
265
0
266
0
    return QuotaManager::IsShuttingDown();
267
0
  }
268
269
  static bool
270
  IsShuttingDownOnNonBackgroundThread()
271
0
  {
272
0
    MOZ_ASSERT(!IsOnBackgroundThread());
273
0
274
0
    return QuotaManager::IsShuttingDown();
275
0
  }
276
277
  bool
278
  IsShuttingDown() const
279
0
  {
280
0
    AssertIsOnBackgroundThread();
281
0
282
0
    return mShutdownRequested;
283
0
  }
284
285
  NS_INLINE_DECL_REFCOUNTING(Client, override)
286
287
  Type
288
  GetType() override;
289
290
  nsresult
291
  InitOrigin(PersistenceType aPersistenceType,
292
             const nsACString& aGroup,
293
             const nsACString& aOrigin,
294
             const AtomicBool& aCanceled,
295
             UsageInfo* aUsageInfo) override;
296
297
  nsresult
298
  GetUsageForOrigin(PersistenceType aPersistenceType,
299
                    const nsACString& aGroup,
300
                    const nsACString& aOrigin,
301
                    const AtomicBool& aCanceled,
302
                    UsageInfo* aUsageInfo) override;
303
304
  void
305
  OnOriginClearCompleted(PersistenceType aPersistenceType,
306
                         const nsACString& aOrigin)
307
                         override;
308
309
  void
310
  ReleaseIOThreadObjects() override;
311
312
  void
313
  AbortOperations(const nsACString& aOrigin) override;
314
315
  void
316
  AbortOperationsForProcess(ContentParentId aContentParentId) override;
317
318
  void
319
  StartIdleMaintenance() override;
320
321
  void
322
  StopIdleMaintenance() override;
323
324
  void
325
  ShutdownWorkThreads() override;
326
327
private:
328
  ~Client() override;
329
};
330
331
// FileDescriptorHolder owns a file descriptor and its memory mapping.
332
// FileDescriptorHolder is derived by two runnable classes (that is,
333
// (Parent|Child)Runnable.
334
class FileDescriptorHolder : public Runnable
335
{
336
public:
337
  FileDescriptorHolder()
338
    : Runnable("dom::asmjscache::FileDescriptorHolder")
339
    , mQuotaObject(nullptr)
340
    , mFileSize(INT64_MIN)
341
    , mFileDesc(nullptr)
342
    , mFileMap(nullptr)
343
    , mMappedMemory(nullptr)
344
0
  { }
345
346
  ~FileDescriptorHolder() override
347
0
  {
348
0
    // These resources should have already been released by Finish().
349
0
    MOZ_ASSERT(!mQuotaObject);
350
0
    MOZ_ASSERT(!mMappedMemory);
351
0
    MOZ_ASSERT(!mFileMap);
352
0
    MOZ_ASSERT(!mFileDesc);
353
0
  }
354
355
  size_t
356
  FileSize() const
357
0
  {
358
0
    MOZ_ASSERT(mFileSize >= 0, "Accessing FileSize of unopened file");
359
0
    return mFileSize;
360
0
  }
361
362
  PRFileDesc*
363
  FileDesc() const
364
0
  {
365
0
    MOZ_ASSERT(mFileDesc, "Accessing FileDesc of unopened file");
366
0
    return mFileDesc;
367
0
  }
368
369
  bool
370
  MapMemory(OpenMode aOpenMode)
371
0
  {
372
0
    MOZ_ASSERT(!mFileMap, "Cannot call MapMemory twice");
373
0
374
0
    PRFileMapProtect mapFlags = aOpenMode == eOpenForRead ? PR_PROT_READONLY
375
0
                                                          : PR_PROT_READWRITE;
376
0
377
0
    mFileMap = PR_CreateFileMap(mFileDesc, mFileSize, mapFlags);
378
0
    NS_ENSURE_TRUE(mFileMap, false);
379
0
380
0
    mMappedMemory = PR_MemMap(mFileMap, 0, mFileSize);
381
0
    NS_ENSURE_TRUE(mMappedMemory, false);
382
0
383
0
    return true;
384
0
  }
385
386
  void*
387
  MappedMemory() const
388
0
  {
389
0
    MOZ_ASSERT(mMappedMemory, "Accessing MappedMemory of un-mapped file");
390
0
    return mMappedMemory;
391
0
  }
392
393
protected:
394
  // This method must be called before the directory lock is released (the lock
395
  // is protecting these resources). It is idempotent, so it is ok to call
396
  // multiple times (or before the file has been fully opened).
397
  void
398
  Finish()
399
0
  {
400
0
    if (mMappedMemory) {
401
0
      PR_MemUnmap(mMappedMemory, mFileSize);
402
0
      mMappedMemory = nullptr;
403
0
    }
404
0
    if (mFileMap) {
405
0
      PR_CloseFileMap(mFileMap);
406
0
      mFileMap = nullptr;
407
0
    }
408
0
    if (mFileDesc) {
409
0
      PR_Close(mFileDesc);
410
0
      mFileDesc = nullptr;
411
0
    }
412
0
413
0
    // Holding the QuotaObject alive until all the cache files are closed enables
414
0
    // assertions in QuotaManager that the cache entry isn't cleared while we
415
0
    // are working on it.
416
0
    mQuotaObject = nullptr;
417
0
  }
418
419
  RefPtr<QuotaObject> mQuotaObject;
420
  int64_t mFileSize;
421
  PRFileDesc* mFileDesc;
422
  PRFileMap* mFileMap;
423
  void* mMappedMemory;
424
};
425
426
// A runnable that implements a state machine required to open a cache entry.
427
// It executes in the parent for a cache access originating in the child.
428
// This runnable gets registered as an IPDL subprotocol actor so that it
429
// can communicate with the corresponding ChildRunnable.
430
class ParentRunnable final
431
  : public FileDescriptorHolder
432
  , public quota::OpenDirectoryListener
433
  , public PAsmJSCacheEntryParent
434
{
435
public:
436
  // We need to always declare refcounting because
437
  // OpenDirectoryListener has pure-virtual refcounting.
438
  NS_DECL_ISUPPORTS_INHERITED
439
  NS_DECL_NSIRUNNABLE
440
441
  ParentRunnable(const PrincipalInfo& aPrincipalInfo,
442
                 OpenMode aOpenMode,
443
                 const WriteParams& aWriteParams)
444
  : mOwningEventTarget(GetCurrentThreadEventTarget()),
445
    mPrincipalInfo(aPrincipalInfo),
446
    mOpenMode(aOpenMode),
447
    mWriteParams(aWriteParams),
448
    mOperationMayProceed(true),
449
    mModuleIndex(0),
450
    mState(eInitial),
451
    mResult(JS::AsmJSCache_InternalError),
452
    mActorDestroyed(false),
453
    mOpened(false)
454
0
  {
455
0
    MOZ_ASSERT(XRE_IsParentProcess());
456
0
    AssertIsOnOwningThread();
457
0
  }
458
459
private:
460
  ~ParentRunnable() override
461
0
  {
462
0
    MOZ_ASSERT(mState == eFinished);
463
0
    MOZ_ASSERT(!mDirectoryLock);
464
0
    MOZ_ASSERT(mActorDestroyed);
465
0
  }
466
467
#ifdef DEBUG
468
  bool
469
  IsOnOwningThread() const
470
  {
471
    MOZ_ASSERT(mOwningEventTarget);
472
473
    bool current;
474
    return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) && current;
475
  }
476
#endif
477
478
  void
479
  AssertIsOnOwningThread() const
480
0
  {
481
0
    MOZ_ASSERT(IsOnBackgroundThread());
482
0
    MOZ_ASSERT(IsOnOwningThread());
483
0
  }
484
485
  void
486
  AssertIsOnNonOwningThread() const
487
0
  {
488
0
    MOZ_ASSERT(!IsOnBackgroundThread());
489
0
    MOZ_ASSERT(!IsOnOwningThread());
490
0
  }
491
492
  bool
493
  IsActorDestroyed() const
494
0
  {
495
0
    AssertIsOnOwningThread();
496
0
497
0
    return mActorDestroyed;
498
0
  }
499
500
  // May be called on any thread, but you should call IsActorDestroyed() if
501
  // you know you're on the background thread because it is slightly faster.
502
  bool
503
  OperationMayProceed() const
504
0
  {
505
0
    return mOperationMayProceed;
506
0
  }
507
508
  // This method is called on the owning thread when the JS engine is finished
509
  // reading/writing the cache entry.
510
  void
511
  Close()
512
0
  {
513
0
    AssertIsOnOwningThread();
514
0
    MOZ_ASSERT(mState == eOpened);
515
0
    MOZ_ASSERT(mResult == JS::AsmJSCache_Success);
516
0
517
0
    mState = eFinished;
518
0
519
0
    MOZ_ASSERT(mOpened);
520
0
    mOpened = false;
521
0
522
0
    FinishOnOwningThread();
523
0
524
0
    if (!mActorDestroyed) {
525
0
      Unused << Send__delete__(this, mResult);
526
0
    }
527
0
  }
528
529
  // This method is called upon any failure that prevents the eventual opening
530
  // of the cache entry.
531
  void
532
  Fail()
533
0
  {
534
0
    AssertIsOnOwningThread();
535
0
    MOZ_ASSERT(mState != eFinished);
536
0
    MOZ_ASSERT(mResult != JS::AsmJSCache_Success);
537
0
538
0
    mState = eFinished;
539
0
540
0
    MOZ_ASSERT(!mOpened);
541
0
542
0
    FinishOnOwningThread();
543
0
544
0
    if (!mActorDestroyed) {
545
0
      Unused << Send__delete__(this, mResult);
546
0
    }
547
0
  }
548
549
  // The same as method above but is intended to be called off the owning
550
  // thread.
551
  void
552
  FailOnNonOwningThread()
553
0
  {
554
0
    AssertIsOnNonOwningThread();
555
0
    MOZ_ASSERT(mState != eOpened &&
556
0
               mState != eFailing &&
557
0
               mState != eFinished);
558
0
559
0
    mState = eFailing;
560
0
    MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
561
0
  }
562
563
  nsresult
564
  InitOnMainThread();
565
566
  void
567
  OpenDirectory();
568
569
  nsresult
570
  ReadMetadata();
571
572
  nsresult
573
  OpenCacheFileForWrite();
574
575
  nsresult
576
  OpenCacheFileForRead();
577
578
  void
579
  FinishOnOwningThread();
580
581
  void
582
  DispatchToIOThread()
583
0
  {
584
0
    AssertIsOnOwningThread();
585
0
586
0
    if (NS_WARN_IF(Client::IsShuttingDownOnBackgroundThread()) ||
587
0
        IsActorDestroyed()) {
588
0
      Fail();
589
0
      return;
590
0
    }
591
0
592
0
    QuotaManager* qm = QuotaManager::Get();
593
0
    MOZ_ASSERT(qm);
594
0
595
0
    nsresult rv = qm->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
596
0
    if (NS_FAILED(rv)) {
597
0
      Fail();
598
0
      return;
599
0
    }
600
0
  }
601
602
  // OpenDirectoryListener overrides.
603
  void
604
  DirectoryLockAcquired(DirectoryLock* aLock) override;
605
606
  void
607
  DirectoryLockFailed() override;
608
609
  // IPDL methods.
610
  void
611
  ActorDestroy(ActorDestroyReason why) override
612
0
  {
613
0
    AssertIsOnOwningThread();
614
0
    MOZ_ASSERT(!mActorDestroyed);
615
0
    MOZ_ASSERT(mOperationMayProceed);
616
0
617
0
    mActorDestroyed = true;
618
0
    mOperationMayProceed = false;
619
0
620
0
    // Assume ActorDestroy can happen at any time, so we can't probe the
621
0
    // current state since mState can be modified on any thread (only one
622
0
    // thread at a time based on the state machine).
623
0
    // However we can use mOpened which is only touched on the owning thread.
624
0
    // If mOpened is true, we can also modify mState since we are guaranteed
625
0
    // that there are no pending runnables which would probe mState to decide
626
0
    // what code needs to run (there shouldn't be any running runnables on
627
0
    // other threads either).
628
0
629
0
    if (mOpened) {
630
0
      Close();
631
0
632
0
      MOZ_ASSERT(mState == eFinished);
633
0
    }
634
0
635
0
    // We don't have to call Fail() if mOpened is not true since it means that
636
0
    // either nothing has been initialized yet, so nothing to cleanup or there
637
0
    // are pending runnables that will detect that the actor has been destroyed
638
0
    // and call Fail().
639
0
  }
640
641
  mozilla::ipc::IPCResult
642
  RecvSelectCacheFileToRead(const OpenMetadataForReadResponse& aResponse)
643
                            override
644
0
  {
645
0
    AssertIsOnOwningThread();
646
0
    MOZ_ASSERT(mState == eWaitingToOpenCacheFileForRead);
647
0
    MOZ_ASSERT(mOpenMode == eOpenForRead);
648
0
    MOZ_ASSERT(!mOpened);
649
0
650
0
    if (NS_WARN_IF(Client::IsShuttingDownOnBackgroundThread())) {
651
0
      Fail();
652
0
      return IPC_OK();
653
0
    }
654
0
655
0
    switch (aResponse.type()) {
656
0
      case OpenMetadataForReadResponse::TAsmJSCacheResult: {
657
0
        MOZ_ASSERT(aResponse.get_AsmJSCacheResult() != JS::AsmJSCache_Success);
658
0
659
0
        mResult = aResponse.get_AsmJSCacheResult();
660
0
661
0
        // This ParentRunnable can only be held alive by the IPDL. Fail()
662
0
        // clears that last reference. So we need to add a self reference here.
663
0
        RefPtr<ParentRunnable> kungFuDeathGrip = this;
664
0
665
0
        Fail();
666
0
667
0
        break;
668
0
      }
669
0
670
0
      case OpenMetadataForReadResponse::Tuint32_t:
671
0
        // A cache entry has been selected to open.
672
0
        mModuleIndex = aResponse.get_uint32_t();
673
0
674
0
        mState = eReadyToOpenCacheFileForRead;
675
0
676
0
        DispatchToIOThread();
677
0
678
0
        break;
679
0
680
0
      default:
681
0
        MOZ_CRASH("Should never get here!");
682
0
    }
683
0
684
0
    return IPC_OK();
685
0
  }
686
687
  mozilla::ipc::IPCResult
688
  RecvClose() override
689
0
  {
690
0
    AssertIsOnOwningThread();
691
0
    MOZ_ASSERT(mState == eOpened);
692
0
693
0
    // This ParentRunnable can only be held alive by the IPDL. Close() clears
694
0
    // that last reference. So we need to add a self reference here.
695
0
    RefPtr<ParentRunnable> kungFuDeathGrip = this;
696
0
697
0
    Close();
698
0
699
0
    MOZ_ASSERT(mState == eFinished);
700
0
701
0
    return IPC_OK();
702
0
  }
703
704
  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
705
  const PrincipalInfo mPrincipalInfo;
706
  const OpenMode mOpenMode;
707
  const WriteParams mWriteParams;
708
709
  // State initialized during eInitial:
710
  nsCString mSuffix;
711
  nsCString mGroup;
712
  nsCString mOrigin;
713
  RefPtr<DirectoryLock> mDirectoryLock;
714
715
  // State initialized during eReadyToReadMetadata
716
  nsCOMPtr<nsIFile> mDirectory;
717
  nsCOMPtr<nsIFile> mMetadataFile;
718
  Metadata mMetadata;
719
720
  Atomic<bool> mOperationMayProceed;
721
722
  // State initialized during eWaitingToOpenCacheFileForRead
723
  unsigned mModuleIndex;
724
725
  enum State {
726
    eInitial, // Just created, waiting to be dispatched to main thread
727
    eWaitingToFinishInit, // Waiting to finish initialization
728
    eWaitingToOpenDirectory, // Waiting to open directory
729
    eWaitingToOpenMetadata, // Waiting to be called back from OpenDirectory
730
    eReadyToReadMetadata, // Waiting to read the metadata file on the IO thread
731
    eSendingMetadataForRead, // Waiting to send OnOpenMetadataForRead
732
    eWaitingToOpenCacheFileForRead, // Waiting to hear back from child
733
    eReadyToOpenCacheFileForRead, // Waiting to open cache file for read
734
    eSendingCacheFile, // Waiting to send OnOpenCacheFile on the owning thread
735
    eOpened, // Finished calling OnOpenCacheFile, waiting to be closed
736
    eFailing, // Just failed, waiting to be dispatched to the owning thread
737
    eFinished, // Terminal state
738
  };
739
  State mState;
740
  JS::AsmJSCacheResult mResult;
741
742
  bool mActorDestroyed;
743
  bool mOpened;
744
};
745
746
nsresult
747
ParentRunnable::InitOnMainThread()
748
0
{
749
0
  MOZ_ASSERT(NS_IsMainThread());
750
0
  MOZ_ASSERT(mState == eInitial);
751
0
  MOZ_ASSERT(mPrincipalInfo.type() != PrincipalInfo::TNullPrincipalInfo);
752
0
753
0
  nsresult rv;
754
0
  nsCOMPtr<nsIPrincipal> principal =
755
0
    PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
756
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
757
0
    return rv;
758
0
  }
759
0
760
0
  rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
761
0
                                          &mOrigin);
762
0
  NS_ENSURE_SUCCESS(rv, rv);
763
0
764
0
  return NS_OK;
765
0
}
766
767
void
768
ParentRunnable::OpenDirectory()
769
0
{
770
0
  AssertIsOnOwningThread();
771
0
  MOZ_ASSERT(mState == eWaitingToFinishInit ||
772
0
             mState == eWaitingToOpenDirectory);
773
0
  MOZ_ASSERT(QuotaManager::Get());
774
0
775
0
  mState = eWaitingToOpenMetadata;
776
0
777
0
  // XXX The exclusive lock shouldn't be needed for read operations.
778
0
  QuotaManager::Get()->OpenDirectory(quota::PERSISTENCE_TYPE_TEMPORARY,
779
0
                                     mGroup,
780
0
                                     mOrigin,
781
0
                                     quota::Client::ASMJS,
782
0
                                     /* aExclusive */ true,
783
0
                                     this);
784
0
}
785
786
nsresult
787
ParentRunnable::ReadMetadata()
788
0
{
789
0
  AssertIsOnIOThread();
790
0
  MOZ_ASSERT(mState == eReadyToReadMetadata);
791
0
792
0
  QuotaManager* qm = QuotaManager::Get();
793
0
  MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
794
0
795
0
  nsresult rv =
796
0
    qm->EnsureOriginIsInitialized(quota::PERSISTENCE_TYPE_TEMPORARY, mSuffix,
797
0
                                  mGroup, mOrigin, getter_AddRefs(mDirectory));
798
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
799
0
    mResult = JS::AsmJSCache_StorageInitFailure;
800
0
    return rv;
801
0
  }
802
0
803
0
  rv = mDirectory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
804
0
  NS_ENSURE_SUCCESS(rv, rv);
805
0
806
0
  bool exists;
807
0
  rv = mDirectory->Exists(&exists);
808
0
  NS_ENSURE_SUCCESS(rv, rv);
809
0
810
0
  if (!exists) {
811
0
    rv = mDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
812
0
    NS_ENSURE_SUCCESS(rv, rv);
813
0
  } else {
814
0
    DebugOnly<bool> isDirectory;
815
0
    MOZ_ASSERT(NS_SUCCEEDED(mDirectory->IsDirectory(&isDirectory)));
816
0
    MOZ_ASSERT(isDirectory, "Should have caught this earlier!");
817
0
  }
818
0
819
0
  rv = mDirectory->Clone(getter_AddRefs(mMetadataFile));
820
0
  NS_ENSURE_SUCCESS(rv, rv);
821
0
822
0
  rv = mMetadataFile->Append(NS_LITERAL_STRING(ASMJSCACHE_METADATA_FILE_NAME));
823
0
  NS_ENSURE_SUCCESS(rv, rv);
824
0
825
0
  rv = mMetadataFile->Exists(&exists);
826
0
  NS_ENSURE_SUCCESS(rv, rv);
827
0
828
0
  if (exists && NS_FAILED(ReadMetadataFile(mMetadataFile, mMetadata))) {
829
0
    exists = false;
830
0
  }
831
0
832
0
  if (!exists) {
833
0
    // If we are reading, we can't possibly have a cache hit.
834
0
    if (mOpenMode == eOpenForRead) {
835
0
      return NS_ERROR_FILE_NOT_FOUND;
836
0
    }
837
0
838
0
    // Initialize Metadata with a valid empty state for the LRU cache.
839
0
    for (unsigned i = 0; i < Metadata::kNumEntries; i++) {
840
0
      Metadata::Entry& entry = mMetadata.mEntries[i];
841
0
      entry.mModuleIndex = i;
842
0
      entry.clear();
843
0
    }
844
0
  }
845
0
846
0
  return NS_OK;
847
0
}
848
849
nsresult
850
ParentRunnable::OpenCacheFileForWrite()
851
0
{
852
0
  AssertIsOnIOThread();
853
0
  MOZ_ASSERT(mState == eReadyToReadMetadata);
854
0
  MOZ_ASSERT(mOpenMode == eOpenForWrite);
855
0
856
0
  mFileSize = mWriteParams.mSize;
857
0
858
0
  // Kick out the oldest entry in the LRU queue in the metadata.
859
0
  mModuleIndex = mMetadata.mEntries[Metadata::kLastEntry].mModuleIndex;
860
0
861
0
  nsCOMPtr<nsIFile> file;
862
0
  nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file));
863
0
  NS_ENSURE_SUCCESS(rv, rv);
864
0
865
0
  QuotaManager* qm = QuotaManager::Get();
866
0
  MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
867
0
868
0
  // Create the QuotaObject before all file IO and keep it alive until caching
869
0
  // completes to get maximum assertion coverage in QuotaManager against
870
0
  // concurrent removal, etc.
871
0
  mQuotaObject = qm->GetQuotaObject(quota::PERSISTENCE_TYPE_TEMPORARY, mGroup,
872
0
                                    mOrigin, file);
873
0
  NS_ENSURE_STATE(mQuotaObject);
874
0
875
0
  if (!mQuotaObject->MaybeUpdateSize(mWriteParams.mSize,
876
0
                                     /* aTruncate */ false)) {
877
0
    // If the request fails, it might be because mOrigin is using too much
878
0
    // space (MaybeUpdateSize will not evict our own origin since it is
879
0
    // active). Try to make some space by evicting LRU entries until there is
880
0
    // enough space.
881
0
    EvictEntries(mDirectory, mGroup, mOrigin, mWriteParams.mSize, mMetadata);
882
0
    if (!mQuotaObject->MaybeUpdateSize(mWriteParams.mSize,
883
0
                                       /* aTruncate */ false)) {
884
0
      mResult = JS::AsmJSCache_QuotaExceeded;
885
0
      return NS_ERROR_FAILURE;
886
0
    }
887
0
  }
888
0
889
0
  int32_t openFlags = PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE;
890
0
  rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc);
891
0
  NS_ENSURE_SUCCESS(rv, rv);
892
0
893
0
  // Move the mModuleIndex's LRU entry to the recent end of the queue.
894
0
  PodMove(mMetadata.mEntries + 1, mMetadata.mEntries, Metadata::kLastEntry);
895
0
  Metadata::Entry& entry = mMetadata.mEntries[0];
896
0
  entry.mFastHash = mWriteParams.mFastHash;
897
0
  entry.mNumChars = mWriteParams.mNumChars;
898
0
  entry.mFullHash = mWriteParams.mFullHash;
899
0
  entry.mModuleIndex = mModuleIndex;
900
0
901
0
  rv = WriteMetadataFile(mMetadataFile, mMetadata);
902
0
  NS_ENSURE_SUCCESS(rv, rv);
903
0
904
0
  return NS_OK;
905
0
}
906
907
nsresult
908
ParentRunnable::OpenCacheFileForRead()
909
0
{
910
0
  AssertIsOnIOThread();
911
0
  MOZ_ASSERT(mState == eReadyToOpenCacheFileForRead);
912
0
  MOZ_ASSERT(mOpenMode == eOpenForRead);
913
0
914
0
  nsCOMPtr<nsIFile> file;
915
0
  nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file));
916
0
  NS_ENSURE_SUCCESS(rv, rv);
917
0
918
0
  QuotaManager* qm = QuotaManager::Get();
919
0
  MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
920
0
921
0
  // Even though it's not strictly necessary, create the QuotaObject before all
922
0
  // file IO and keep it alive until caching completes to get maximum assertion
923
0
  // coverage in QuotaManager against concurrent removal, etc.
924
0
  mQuotaObject = qm->GetQuotaObject(quota::PERSISTENCE_TYPE_TEMPORARY, mGroup,
925
0
                                    mOrigin, file);
926
0
  NS_ENSURE_STATE(mQuotaObject);
927
0
928
0
  rv = file->GetFileSize(&mFileSize);
929
0
  NS_ENSURE_SUCCESS(rv, rv);
930
0
931
0
  int32_t openFlags = PR_RDONLY | nsIFile::OS_READAHEAD;
932
0
  rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc);
933
0
  NS_ENSURE_SUCCESS(rv, rv);
934
0
935
0
  // Move the mModuleIndex's LRU entry to the recent end of the queue.
936
0
  unsigned lruIndex = 0;
937
0
  while (mMetadata.mEntries[lruIndex].mModuleIndex != mModuleIndex) {
938
0
    if (++lruIndex == Metadata::kNumEntries) {
939
0
      return NS_ERROR_UNEXPECTED;
940
0
    }
941
0
  }
942
0
  Metadata::Entry entry = mMetadata.mEntries[lruIndex];
943
0
  PodMove(mMetadata.mEntries + 1, mMetadata.mEntries, lruIndex);
944
0
  mMetadata.mEntries[0] = entry;
945
0
946
0
  rv = WriteMetadataFile(mMetadataFile, mMetadata);
947
0
  NS_ENSURE_SUCCESS(rv, rv);
948
0
949
0
  return NS_OK;
950
0
}
951
952
void
953
ParentRunnable::FinishOnOwningThread()
954
0
{
955
0
  AssertIsOnOwningThread();
956
0
957
0
  // Per FileDescriptorHolder::Finish()'s comment, call before
958
0
  // releasing the directory lock.
959
0
  FileDescriptorHolder::Finish();
960
0
961
0
  mDirectoryLock = nullptr;
962
0
963
0
  MOZ_ASSERT(sLiveParentActors);
964
0
  sLiveParentActors->RemoveElement(this);
965
0
966
0
  if (sLiveParentActors->IsEmpty()) {
967
0
    sLiveParentActors = nullptr;
968
0
  }
969
0
}
970
971
NS_IMETHODIMP
972
ParentRunnable::Run()
973
0
{
974
0
  nsresult rv;
975
0
976
0
  // All success/failure paths must eventually call Finish() to avoid leaving
977
0
  // the parser hanging.
978
0
  switch (mState) {
979
0
    case eInitial: {
980
0
      MOZ_ASSERT(NS_IsMainThread());
981
0
982
0
      if (NS_WARN_IF(Client::IsShuttingDownOnNonBackgroundThread()) ||
983
0
          !OperationMayProceed()) {
984
0
        FailOnNonOwningThread();
985
0
        return NS_OK;
986
0
      }
987
0
988
0
      rv = InitOnMainThread();
989
0
      if (NS_FAILED(rv)) {
990
0
        FailOnNonOwningThread();
991
0
        return NS_OK;
992
0
      }
993
0
994
0
      mState = eWaitingToFinishInit;
995
0
      MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
996
0
997
0
      return NS_OK;
998
0
    }
999
0
1000
0
    case eWaitingToFinishInit: {
1001
0
      AssertIsOnOwningThread();
1002
0
1003
0
      if (NS_WARN_IF(Client::IsShuttingDownOnBackgroundThread()) ||
1004
0
          IsActorDestroyed()) {
1005
0
        Fail();
1006
0
        return NS_OK;
1007
0
      }
1008
0
1009
0
      if (QuotaManager::Get()) {
1010
0
        OpenDirectory();
1011
0
        return NS_OK;
1012
0
      }
1013
0
1014
0
      mState = eWaitingToOpenDirectory;
1015
0
      QuotaManager::GetOrCreate(this);
1016
0
1017
0
      return NS_OK;
1018
0
    }
1019
0
1020
0
    case eWaitingToOpenDirectory: {
1021
0
      AssertIsOnOwningThread();
1022
0
1023
0
      if (NS_WARN_IF(Client::IsShuttingDownOnBackgroundThread()) ||
1024
0
          IsActorDestroyed()) {
1025
0
        Fail();
1026
0
        return NS_OK;
1027
0
      }
1028
0
1029
0
      if (NS_WARN_IF(!QuotaManager::Get())) {
1030
0
        Fail();
1031
0
        return NS_OK;
1032
0
      }
1033
0
1034
0
      OpenDirectory();
1035
0
      return NS_OK;
1036
0
    }
1037
0
1038
0
    case eReadyToReadMetadata: {
1039
0
      AssertIsOnIOThread();
1040
0
1041
0
      if (NS_WARN_IF(Client::IsShuttingDownOnNonBackgroundThread()) ||
1042
0
          !OperationMayProceed()) {
1043
0
        FailOnNonOwningThread();
1044
0
        return NS_OK;
1045
0
      }
1046
0
1047
0
      rv = ReadMetadata();
1048
0
      if (NS_FAILED(rv)) {
1049
0
        FailOnNonOwningThread();
1050
0
        return NS_OK;
1051
0
      }
1052
0
1053
0
      if (mOpenMode == eOpenForRead) {
1054
0
        mState = eSendingMetadataForRead;
1055
0
        MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
1056
0
1057
0
        return NS_OK;
1058
0
      }
1059
0
1060
0
      rv = OpenCacheFileForWrite();
1061
0
      if (NS_FAILED(rv)) {
1062
0
        FailOnNonOwningThread();
1063
0
        return NS_OK;
1064
0
      }
1065
0
1066
0
      mState = eSendingCacheFile;
1067
0
      MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
1068
0
      return NS_OK;
1069
0
    }
1070
0
1071
0
    case eSendingMetadataForRead: {
1072
0
      AssertIsOnOwningThread();
1073
0
      MOZ_ASSERT(mOpenMode == eOpenForRead);
1074
0
1075
0
      if (NS_WARN_IF(Client::IsShuttingDownOnBackgroundThread()) ||
1076
0
          IsActorDestroyed()) {
1077
0
        Fail();
1078
0
        return NS_OK;
1079
0
      }
1080
0
1081
0
      mState = eWaitingToOpenCacheFileForRead;
1082
0
1083
0
      // Metadata is now open.
1084
0
      if (!SendOnOpenMetadataForRead(mMetadata)) {
1085
0
        Fail();
1086
0
        return NS_OK;
1087
0
      }
1088
0
1089
0
      return NS_OK;
1090
0
    }
1091
0
1092
0
    case eReadyToOpenCacheFileForRead: {
1093
0
      AssertIsOnIOThread();
1094
0
      MOZ_ASSERT(mOpenMode == eOpenForRead);
1095
0
1096
0
      if (NS_WARN_IF(Client::IsShuttingDownOnNonBackgroundThread()) ||
1097
0
          !OperationMayProceed()) {
1098
0
        FailOnNonOwningThread();
1099
0
        return NS_OK;
1100
0
      }
1101
0
1102
0
      rv = OpenCacheFileForRead();
1103
0
      if (NS_FAILED(rv)) {
1104
0
        FailOnNonOwningThread();
1105
0
        return NS_OK;
1106
0
      }
1107
0
1108
0
      mState = eSendingCacheFile;
1109
0
      MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
1110
0
      return NS_OK;
1111
0
    }
1112
0
1113
0
    case eSendingCacheFile: {
1114
0
      AssertIsOnOwningThread();
1115
0
1116
0
      if (NS_WARN_IF(Client::IsShuttingDownOnBackgroundThread()) ||
1117
0
          IsActorDestroyed()) {
1118
0
        Fail();
1119
0
        return NS_OK;
1120
0
      }
1121
0
1122
0
      mState = eOpened;
1123
0
1124
0
      FileDescriptor::PlatformHandleType handle =
1125
0
        FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFileDesc));
1126
0
      if (!SendOnOpenCacheFile(mFileSize, FileDescriptor(handle))) {
1127
0
        Fail();
1128
0
        return NS_OK;
1129
0
      }
1130
0
1131
0
      // The entry is now open.
1132
0
      MOZ_ASSERT(!mOpened);
1133
0
      mOpened = true;
1134
0
1135
0
      mResult = JS::AsmJSCache_Success;
1136
0
1137
0
      return NS_OK;
1138
0
    }
1139
0
1140
0
    case eFailing: {
1141
0
      AssertIsOnOwningThread();
1142
0
1143
0
      Fail();
1144
0
1145
0
      return NS_OK;
1146
0
    }
1147
0
1148
0
    case eWaitingToOpenMetadata:
1149
0
    case eWaitingToOpenCacheFileForRead:
1150
0
    case eOpened:
1151
0
    case eFinished: {
1152
0
      MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Shouldn't Run() in this state");
1153
0
    }
1154
0
  }
1155
0
1156
0
  MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Corrupt state");
1157
0
  return NS_OK;
1158
0
}
1159
1160
void
1161
ParentRunnable::DirectoryLockAcquired(DirectoryLock* aLock)
1162
0
{
1163
0
  AssertIsOnOwningThread();
1164
0
  MOZ_ASSERT(mState == eWaitingToOpenMetadata);
1165
0
  MOZ_ASSERT(!mDirectoryLock);
1166
0
1167
0
  mDirectoryLock = aLock;
1168
0
1169
0
  mState = eReadyToReadMetadata;
1170
0
  DispatchToIOThread();
1171
0
}
1172
1173
void
1174
ParentRunnable::DirectoryLockFailed()
1175
0
{
1176
0
  AssertIsOnOwningThread();
1177
0
  MOZ_ASSERT(mState == eWaitingToOpenMetadata);
1178
0
  MOZ_ASSERT(!mDirectoryLock);
1179
0
1180
0
  Fail();
1181
0
}
1182
1183
NS_IMPL_ISUPPORTS_INHERITED0(ParentRunnable, FileDescriptorHolder)
1184
1185
bool
1186
FindHashMatch(const Metadata& aMetadata, const ReadParams& aReadParams,
1187
              unsigned* aModuleIndex)
1188
0
{
1189
0
  // Perform a fast hash of the first sNumFastHashChars chars. Each cache entry
1190
0
  // also stores an mFastHash of its first sNumFastHashChars so this gives us a
1191
0
  // fast way to probabilistically determine whether we have a cache hit. We
1192
0
  // still do a full hash of all the chars before returning the cache file to
1193
0
  // the engine to avoid penalizing the case where there are multiple cached
1194
0
  // asm.js modules where the first sNumFastHashChars are the same. The
1195
0
  // mFullHash of each cache entry can have a different mNumChars so the fast
1196
0
  // hash allows us to avoid performing up to Metadata::kNumEntries separate
1197
0
  // full hashes.
1198
0
  uint32_t numChars = aReadParams.mLimit - aReadParams.mBegin;
1199
0
  MOZ_ASSERT(numChars > sNumFastHashChars);
1200
0
  uint32_t fastHash = HashString(aReadParams.mBegin, sNumFastHashChars);
1201
0
1202
0
  for (auto entry : aMetadata.mEntries) {
1203
0
    // Compare the "fast hash" first to see whether it is worthwhile to
1204
0
    // hash all the chars.
1205
0
    if (entry.mFastHash != fastHash) {
1206
0
      continue;
1207
0
    }
1208
0
1209
0
    // Assuming we have enough characters, hash all the chars it would take
1210
0
    // to match this cache entry and compare to the cache entry. If we get a
1211
0
    // hit we'll still do a full source match later (in the JS engine), but
1212
0
    // the full hash match means this is probably the cache entry we want.
1213
0
    if (numChars < entry.mNumChars) {
1214
0
      continue;
1215
0
    }
1216
0
    uint32_t fullHash = HashString(aReadParams.mBegin, entry.mNumChars);
1217
0
    if (entry.mFullHash != fullHash) {
1218
0
      continue;
1219
0
    }
1220
0
1221
0
    *aModuleIndex = entry.mModuleIndex;
1222
0
    return true;
1223
0
  }
1224
0
1225
0
  return false;
1226
0
}
1227
1228
} // unnamed namespace
1229
1230
PAsmJSCacheEntryParent*
1231
AllocEntryParent(OpenMode aOpenMode,
1232
                 WriteParams aWriteParams,
1233
                 const PrincipalInfo& aPrincipalInfo)
1234
0
{
1235
0
  AssertIsOnBackgroundThread();
1236
0
1237
0
  if (NS_WARN_IF(Client::IsShuttingDownOnBackgroundThread())) {
1238
0
    return nullptr;
1239
0
  }
1240
0
1241
0
  if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
1242
0
    MOZ_ASSERT(false);
1243
0
    return nullptr;
1244
0
  }
1245
0
1246
0
  RefPtr<ParentRunnable> runnable =
1247
0
    new ParentRunnable(aPrincipalInfo, aOpenMode, aWriteParams);
1248
0
1249
0
  if (!sLiveParentActors) {
1250
0
    sLiveParentActors = new ParentActorArray();
1251
0
  }
1252
0
1253
0
  sLiveParentActors->AppendElement(runnable);
1254
0
1255
0
  nsresult rv = NS_DispatchToMainThread(runnable);
1256
0
  NS_ENSURE_SUCCESS(rv, nullptr);
1257
0
1258
0
  // Transfer ownership to IPDL.
1259
0
  return runnable.forget().take();
1260
0
}
1261
1262
void
1263
DeallocEntryParent(PAsmJSCacheEntryParent* aActor)
1264
0
{
1265
0
  // Transfer ownership back from IPDL.
1266
0
  RefPtr<ParentRunnable> op =
1267
0
    dont_AddRef(static_cast<ParentRunnable*>(aActor));
1268
0
}
1269
1270
namespace {
1271
1272
// A runnable that presents a single interface to the AsmJSCache ops which need
1273
// to wait until the file is open.
1274
class ChildRunnable final
1275
  : public FileDescriptorHolder
1276
  , public PAsmJSCacheEntryChild
1277
{
1278
  typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
1279
1280
public:
1281
  class AutoClose
1282
  {
1283
    ChildRunnable* mChildRunnable;
1284
1285
  public:
1286
    explicit AutoClose(ChildRunnable* aChildRunnable = nullptr)
1287
    : mChildRunnable(aChildRunnable)
1288
0
    { }
1289
1290
    void
1291
    Init(ChildRunnable* aChildRunnable)
1292
0
    {
1293
0
      MOZ_ASSERT(!mChildRunnable);
1294
0
      mChildRunnable = aChildRunnable;
1295
0
    }
1296
1297
    ChildRunnable*
1298
    operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
1299
0
    {
1300
0
      MOZ_ASSERT(mChildRunnable);
1301
0
      return mChildRunnable;
1302
0
    }
1303
1304
    void
1305
    Forget(ChildRunnable** aChildRunnable)
1306
0
    {
1307
0
      *aChildRunnable = mChildRunnable;
1308
0
      mChildRunnable = nullptr;
1309
0
    }
1310
1311
    ~AutoClose()
1312
0
    {
1313
0
      if (mChildRunnable) {
1314
0
        mChildRunnable->Close();
1315
0
      }
1316
0
    }
1317
  };
1318
1319
  NS_DECL_NSIRUNNABLE
1320
1321
  ChildRunnable(nsIPrincipal* aPrincipal,
1322
                OpenMode aOpenMode,
1323
                const WriteParams& aWriteParams,
1324
                ReadParams aReadParams)
1325
  : mPrincipal(aPrincipal),
1326
    mWriteParams(aWriteParams),
1327
    mReadParams(aReadParams),
1328
    mMutex("ChildRunnable::mMutex"),
1329
    mCondVar(mMutex, "ChildRunnable::mCondVar"),
1330
    mOpenMode(aOpenMode),
1331
    mState(eInitial),
1332
    mResult(JS::AsmJSCache_InternalError),
1333
    mActorDestroyed(false),
1334
    mWaiting(false),
1335
    mOpened(false)
1336
0
  {
1337
0
    MOZ_ASSERT(!NS_IsMainThread());
1338
0
  }
1339
1340
  JS::AsmJSCacheResult
1341
  BlockUntilOpen(AutoClose* aCloser)
1342
0
  {
1343
0
    MOZ_ASSERT(!mWaiting, "Can only call BlockUntilOpen once");
1344
0
    MOZ_ASSERT(!mOpened, "Can only call BlockUntilOpen once");
1345
0
1346
0
    mWaiting = true;
1347
0
1348
0
    nsresult rv = NS_DispatchToMainThread(this);
1349
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1350
0
      return JS::AsmJSCache_InternalError;
1351
0
    }
1352
0
1353
0
    {
1354
0
      MutexAutoLock lock(mMutex);
1355
0
      while (mWaiting) {
1356
0
        mCondVar.Wait();
1357
0
      }
1358
0
    }
1359
0
1360
0
    if (!mOpened) {
1361
0
      return mResult;
1362
0
    }
1363
0
1364
0
    // Now that we're open, we're guaranteed a Close() call. However, we are
1365
0
    // not guaranteed someone is holding an outstanding reference until the File
1366
0
    // is closed, so we do that ourselves and Release() in OnClose().
1367
0
    aCloser->Init(this);
1368
0
    AddRef();
1369
0
    return JS::AsmJSCache_Success;
1370
0
  }
1371
1372
  void Cleanup()
1373
0
  {
1374
#ifdef DEBUG
1375
    NoteActorDestroyed();
1376
#endif
1377
  }
1378
1379
private:
1380
  ~ChildRunnable() override
1381
0
  {
1382
0
    MOZ_ASSERT(!mWaiting, "Shouldn't be destroyed while thread is waiting");
1383
0
    MOZ_ASSERT(!mOpened);
1384
0
    MOZ_ASSERT(mState == eFinished);
1385
0
    MOZ_ASSERT(mActorDestroyed);
1386
0
  }
1387
1388
  // IPDL methods.
1389
  mozilla::ipc::IPCResult
1390
  RecvOnOpenMetadataForRead(const Metadata& aMetadata) override
1391
0
  {
1392
0
    MOZ_ASSERT(NS_IsMainThread());
1393
0
    MOZ_ASSERT(mState == eOpening);
1394
0
1395
0
    uint32_t moduleIndex;
1396
0
    bool ok;
1397
0
    if (FindHashMatch(aMetadata, mReadParams, &moduleIndex)) {
1398
0
      ok = SendSelectCacheFileToRead(moduleIndex);
1399
0
    } else {
1400
0
      ok = SendSelectCacheFileToRead(JS::AsmJSCache_InternalError);
1401
0
    }
1402
0
    if (!ok) {
1403
0
      return IPC_FAIL_NO_REASON(this);
1404
0
    }
1405
0
1406
0
    return IPC_OK();
1407
0
  }
1408
1409
  mozilla::ipc::IPCResult
1410
  RecvOnOpenCacheFile(const int64_t& aFileSize,
1411
                      const FileDescriptor& aFileDesc) override
1412
0
  {
1413
0
    MOZ_ASSERT(NS_IsMainThread());
1414
0
    MOZ_ASSERT(mState == eOpening);
1415
0
1416
0
    mFileSize = aFileSize;
1417
0
1418
0
    auto rawFD = aFileDesc.ClonePlatformHandle();
1419
0
    mFileDesc = PR_ImportFile(PROsfd(rawFD.release()));
1420
0
    if (!mFileDesc) {
1421
0
      return IPC_FAIL_NO_REASON(this);
1422
0
    }
1423
0
1424
0
    mState = eOpened;
1425
0
    Notify(JS::AsmJSCache_Success);
1426
0
    return IPC_OK();
1427
0
  }
1428
1429
  mozilla::ipc::IPCResult
1430
  Recv__delete__(const JS::AsmJSCacheResult& aResult) override
1431
0
  {
1432
0
    MOZ_ASSERT(NS_IsMainThread());
1433
0
    MOZ_ASSERT(mState == eOpening || mState == eFinishing);
1434
0
    MOZ_ASSERT_IF(mState == eOpening, aResult != JS::AsmJSCache_Success);
1435
0
    MOZ_ASSERT_IF(mState == eFinishing, aResult == JS::AsmJSCache_Success);
1436
0
1437
0
    if (mState == eOpening) {
1438
0
      Fail(aResult);
1439
0
    } else {
1440
0
      // Match the AddRef in BlockUntilOpen(). The IPDL still holds an
1441
0
      // outstanding ref which will keep 'this' alive until ActorDestroy()
1442
0
      // is executed.
1443
0
      Release();
1444
0
1445
0
      mState = eFinished;
1446
0
    }
1447
0
    return IPC_OK();
1448
0
  }
1449
1450
  void
1451
  ActorDestroy(ActorDestroyReason why) override
1452
0
  {
1453
0
    MOZ_ASSERT(NS_IsMainThread());
1454
0
    NoteActorDestroyed();
1455
0
  }
1456
1457
  void
1458
  Close()
1459
0
  {
1460
0
    MOZ_ASSERT(mState == eOpened);
1461
0
1462
0
    mState = eClosing;
1463
0
    NS_DispatchToMainThread(this);
1464
0
  }
1465
1466
  void
1467
  Fail(JS::AsmJSCacheResult aResult)
1468
0
  {
1469
0
    MOZ_ASSERT(NS_IsMainThread());
1470
0
    MOZ_ASSERT(mState == eInitial || mState == eOpening);
1471
0
    MOZ_ASSERT(aResult != JS::AsmJSCache_Success);
1472
0
1473
0
    mState = eFinished;
1474
0
1475
0
    FileDescriptorHolder::Finish();
1476
0
    Notify(aResult);
1477
0
  }
1478
1479
  void
1480
  Notify(JS::AsmJSCacheResult aResult)
1481
0
  {
1482
0
    MOZ_ASSERT(NS_IsMainThread());
1483
0
1484
0
    MutexAutoLock lock(mMutex);
1485
0
    MOZ_ASSERT(mWaiting);
1486
0
1487
0
    mWaiting = false;
1488
0
    mOpened = aResult == JS::AsmJSCache_Success;
1489
0
    mResult = aResult;
1490
0
    mCondVar.Notify();
1491
0
  }
1492
1493
  void NoteActorDestroyed()
1494
0
  {
1495
0
    mActorDestroyed = true;
1496
0
  }
1497
1498
  nsIPrincipal* const mPrincipal;
1499
  nsAutoPtr<PrincipalInfo> mPrincipalInfo;
1500
  WriteParams mWriteParams;
1501
  ReadParams mReadParams;
1502
  Mutex mMutex;
1503
  CondVar mCondVar;
1504
1505
  // Couple enums and bools together
1506
  const OpenMode mOpenMode;
1507
  enum State {
1508
    eInitial, // Just created, waiting to be dispatched to the main thread
1509
    eOpening, // Waiting for the parent process to respond
1510
    eOpened, // Parent process opened the entry and sent it back
1511
    eClosing, // Waiting to be dispatched to the main thread to Send__delete__
1512
    eFinishing, // Waiting for the parent process to close
1513
    eFinished // Terminal state
1514
  };
1515
  State mState;
1516
  JS::AsmJSCacheResult mResult;
1517
1518
  bool mActorDestroyed;
1519
  bool mWaiting;
1520
  bool mOpened;
1521
};
1522
1523
NS_IMETHODIMP
1524
ChildRunnable::Run()
1525
0
{
1526
0
  switch (mState) {
1527
0
    case eInitial: {
1528
0
      MOZ_ASSERT(NS_IsMainThread());
1529
0
1530
0
      if (mPrincipal->GetIsNullPrincipal()) {
1531
0
        NS_WARNING("AsmsJSCache not supported on null principal.");
1532
0
        Fail(JS::AsmJSCache_InternalError);
1533
0
        return NS_OK;
1534
0
      }
1535
0
1536
0
      nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
1537
0
      nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo);
1538
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1539
0
        Fail(JS::AsmJSCache_InternalError);
1540
0
        return NS_OK;
1541
0
      }
1542
0
1543
0
      mPrincipalInfo = std::move(principalInfo);
1544
0
1545
0
      PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();
1546
0
      if (NS_WARN_IF(!actor)) {
1547
0
        Fail(JS::AsmJSCache_InternalError);
1548
0
        return NS_OK;
1549
0
      }
1550
0
1551
0
      if (!actor->SendPAsmJSCacheEntryConstructor(this, mOpenMode, mWriteParams,
1552
0
                                                   *mPrincipalInfo)) {
1553
0
        // Unblock the parsing thread with a failure.
1554
0
1555
0
        Fail(JS::AsmJSCache_InternalError);
1556
0
        return NS_OK;
1557
0
      }
1558
0
1559
0
      // AddRef to keep this runnable alive until IPDL deallocates the
1560
0
      // subprotocol (DeallocEntryChild).
1561
0
      AddRef();
1562
0
1563
0
      mState = eOpening;
1564
0
      return NS_OK;
1565
0
    }
1566
0
1567
0
    case eClosing: {
1568
0
      MOZ_ASSERT(NS_IsMainThread());
1569
0
1570
0
      // Per FileDescriptorHolder::Finish()'s comment, call before
1571
0
      // releasing the directory lock (which happens in the parent upon receipt
1572
0
      // of the Close message).
1573
0
      FileDescriptorHolder::Finish();
1574
0
1575
0
      MOZ_ASSERT(mOpened);
1576
0
      mOpened = false;
1577
0
1578
0
      if (mActorDestroyed) {
1579
0
        // Match the AddRef in BlockUntilOpen(). The main thread event loop
1580
0
        // still holds an outstanding ref which will keep 'this' alive until
1581
0
        // returning to the event loop.
1582
0
        Release();
1583
0
1584
0
        mState = eFinished;
1585
0
      } else {
1586
0
        Unused << SendClose();
1587
0
1588
0
        mState = eFinishing;
1589
0
      }
1590
0
1591
0
      return NS_OK;
1592
0
    }
1593
0
1594
0
    case eOpening:
1595
0
    case eOpened:
1596
0
    case eFinishing:
1597
0
    case eFinished: {
1598
0
      MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Shouldn't Run() in this state");
1599
0
    }
1600
0
  }
1601
0
1602
0
  MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Corrupt state");
1603
0
  return NS_OK;
1604
0
}
1605
1606
} // unnamed namespace
1607
1608
void
1609
DeallocEntryChild(PAsmJSCacheEntryChild* aActor)
1610
0
{
1611
0
  // Match the AddRef before SendPAsmJSCacheEntryConstructor.
1612
0
  static_cast<ChildRunnable*>(aActor)->Release();
1613
0
}
1614
1615
namespace {
1616
1617
JS::AsmJSCacheResult
1618
OpenFile(nsIPrincipal* aPrincipal,
1619
         OpenMode aOpenMode,
1620
         WriteParams aWriteParams,
1621
         ReadParams aReadParams,
1622
         ChildRunnable::AutoClose* aChildRunnable)
1623
0
{
1624
0
  MOZ_ASSERT_IF(aOpenMode == eOpenForRead, aWriteParams.mSize == 0);
1625
0
  MOZ_ASSERT_IF(aOpenMode == eOpenForWrite, aReadParams.mBegin == nullptr);
1626
0
1627
0
  // There are three reasons we don't attempt caching from the main thread:
1628
0
  //  1. In the parent process: QuotaManager::WaitForOpenAllowed prevents
1629
0
  //     synchronous waiting on the main thread requiring a runnable to be
1630
0
  //     dispatched to the main thread.
1631
0
  //  2. In the child process: the IPDL PContent messages we need to
1632
0
  //     synchronously wait on are dispatched to the main thread.
1633
0
  //  3. While a cache lookup *should* be much faster than compilation, IO
1634
0
  //     operations can be unpredictably slow and we'd like to avoid the
1635
0
  //     occasional janks on the main thread.
1636
0
  // We could use a nested event loop to address 1 and 2, but we're potentially
1637
0
  // in the middle of running JS (eval()) and nested event loops can be
1638
0
  // semantically observable.
1639
0
  if (NS_IsMainThread()) {
1640
0
    return JS::AsmJSCache_SynchronousScript;
1641
0
  }
1642
0
1643
0
  // Check to see whether the principal reflects a private browsing session.
1644
0
  // Since AsmJSCache requires disk access at the moment, caching should be
1645
0
  // disabled in private browsing situations. Failing here will cause later
1646
0
  // read/write requests to also fail.
1647
0
  uint32_t pbId;
1648
0
  if (NS_WARN_IF(NS_FAILED(aPrincipal->GetPrivateBrowsingId(&pbId)))) {
1649
0
    return JS::AsmJSCache_InternalError;
1650
0
  }
1651
0
1652
0
  if (pbId > 0) {
1653
0
    return JS::AsmJSCache_Disabled_PrivateBrowsing;
1654
0
  }
1655
0
1656
0
  // We need to synchronously call into the parent to open the file and
1657
0
  // interact with the QuotaManager. The child can then map the file into its
1658
0
  // address space to perform I/O.
1659
0
  RefPtr<ChildRunnable> childRunnable =
1660
0
    new ChildRunnable(aPrincipal, aOpenMode, aWriteParams, aReadParams);
1661
0
1662
0
  JS::AsmJSCacheResult openResult =
1663
0
    childRunnable->BlockUntilOpen(aChildRunnable);
1664
0
  if (openResult != JS::AsmJSCache_Success) {
1665
0
    childRunnable->Cleanup();
1666
0
    return openResult;
1667
0
  }
1668
0
1669
0
  if (!childRunnable->MapMemory(aOpenMode)) {
1670
0
    return JS::AsmJSCache_InternalError;
1671
0
  }
1672
0
1673
0
  return JS::AsmJSCache_Success;
1674
0
}
1675
1676
} // namespace
1677
1678
typedef uint32_t AsmJSCookieType;
1679
static const uint32_t sAsmJSCookie = 0x600d600d;
1680
1681
bool
1682
OpenEntryForRead(nsIPrincipal* aPrincipal,
1683
                 const char16_t* aBegin,
1684
                 const char16_t* aLimit,
1685
                 size_t* aSize,
1686
                 const uint8_t** aMemory,
1687
                 intptr_t* aHandle)
1688
0
{
1689
0
  if (size_t(aLimit - aBegin) < sMinCachedModuleLength) {
1690
0
    return false;
1691
0
  }
1692
0
1693
0
  ReadParams readParams;
1694
0
  readParams.mBegin = aBegin;
1695
0
  readParams.mLimit = aLimit;
1696
0
1697
0
  ChildRunnable::AutoClose childRunnable;
1698
0
  WriteParams notAWrite;
1699
0
  JS::AsmJSCacheResult openResult =
1700
0
    OpenFile(aPrincipal, eOpenForRead, notAWrite, readParams, &childRunnable);
1701
0
  if (openResult != JS::AsmJSCache_Success) {
1702
0
    return false;
1703
0
  }
1704
0
1705
0
  // Although we trust that the stored cache files have not been arbitrarily
1706
0
  // corrupted, it is possible that a previous execution aborted in the middle
1707
0
  // of writing a cache file (crash, OOM-killer, etc). To protect against
1708
0
  // partially-written cache files, we use the following scheme:
1709
0
  //  - Allocate an extra word at the beginning of every cache file which
1710
0
  //    starts out 0 (OpenFile opens with PR_TRUNCATE).
1711
0
  //  - After the asm.js serialization is complete, PR_SyncMemMap to write
1712
0
  //    everything to disk and then store a non-zero value (sAsmJSCookie)
1713
0
  //    in the first word.
1714
0
  //  - When attempting to read a cache file, check whether the first word is
1715
0
  //    sAsmJSCookie.
1716
0
  if (childRunnable->FileSize() < sizeof(AsmJSCookieType) ||
1717
0
      *(AsmJSCookieType*)childRunnable->MappedMemory() != sAsmJSCookie) {
1718
0
    return false;
1719
0
  }
1720
0
1721
0
  *aSize = childRunnable->FileSize() - sizeof(AsmJSCookieType);
1722
0
  *aMemory = (uint8_t*) childRunnable->MappedMemory() + sizeof(AsmJSCookieType);
1723
0
1724
0
  // The caller guarnatees a call to CloseEntryForRead (on success or
1725
0
  // failure) at which point the file will be closed.
1726
0
  childRunnable.Forget(reinterpret_cast<ChildRunnable**>(aHandle));
1727
0
  return true;
1728
0
}
1729
1730
void
1731
CloseEntryForRead(size_t aSize,
1732
                  const uint8_t* aMemory,
1733
                  intptr_t aHandle)
1734
0
{
1735
0
  ChildRunnable::AutoClose childRunnable(
1736
0
    reinterpret_cast<ChildRunnable*>(aHandle));
1737
0
1738
0
  MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == childRunnable->FileSize());
1739
0
  MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) ==
1740
0
               childRunnable->MappedMemory());
1741
0
}
1742
1743
JS::AsmJSCacheResult
1744
OpenEntryForWrite(nsIPrincipal* aPrincipal,
1745
                  const char16_t* aBegin,
1746
                  const char16_t* aEnd,
1747
                  size_t aSize,
1748
                  uint8_t** aMemory,
1749
                  intptr_t* aHandle)
1750
0
{
1751
0
  if (size_t(aEnd - aBegin) < sMinCachedModuleLength) {
1752
0
    return JS::AsmJSCache_ModuleTooSmall;
1753
0
  }
1754
0
1755
0
  // Add extra space for the AsmJSCookieType (see OpenEntryForRead).
1756
0
  aSize += sizeof(AsmJSCookieType);
1757
0
1758
0
  static_assert(sNumFastHashChars < sMinCachedModuleLength, "HashString safe");
1759
0
1760
0
  WriteParams writeParams;
1761
0
  writeParams.mSize = aSize;
1762
0
  writeParams.mFastHash = HashString(aBegin, sNumFastHashChars);
1763
0
  writeParams.mNumChars = aEnd - aBegin;
1764
0
  writeParams.mFullHash = HashString(aBegin, writeParams.mNumChars);
1765
0
1766
0
  ChildRunnable::AutoClose childRunnable;
1767
0
  ReadParams notARead;
1768
0
  JS::AsmJSCacheResult openResult =
1769
0
    OpenFile(aPrincipal, eOpenForWrite, writeParams, notARead, &childRunnable);
1770
0
  if (openResult != JS::AsmJSCache_Success) {
1771
0
    return openResult;
1772
0
  }
1773
0
1774
0
  // Strip off the AsmJSCookieType from the buffer returned to the caller,
1775
0
  // which expects a buffer of aSize, not a buffer of sizeWithCookie starting
1776
0
  // with a cookie.
1777
0
  *aMemory = (uint8_t*) childRunnable->MappedMemory() + sizeof(AsmJSCookieType);
1778
0
1779
0
  // The caller guarnatees a call to CloseEntryForWrite (on success or
1780
0
  // failure) at which point the file will be closed
1781
0
  childRunnable.Forget(reinterpret_cast<ChildRunnable**>(aHandle));
1782
0
  return JS::AsmJSCache_Success;
1783
0
}
1784
1785
void
1786
CloseEntryForWrite(size_t aSize,
1787
                   uint8_t* aMemory,
1788
                   intptr_t aHandle)
1789
0
{
1790
0
  ChildRunnable::AutoClose childRunnable(
1791
0
    reinterpret_cast<ChildRunnable*>(aHandle));
1792
0
1793
0
  MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == childRunnable->FileSize());
1794
0
  MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) ==
1795
0
               childRunnable->MappedMemory());
1796
0
1797
0
  // Flush to disk before writing the cookie (see OpenEntryForRead).
1798
0
  if (PR_SyncMemMap(childRunnable->FileDesc(),
1799
0
                    childRunnable->MappedMemory(),
1800
0
                    childRunnable->FileSize()) == PR_SUCCESS) {
1801
0
    *(AsmJSCookieType*)childRunnable->MappedMemory() = sAsmJSCookie;
1802
0
  }
1803
0
}
1804
1805
/*******************************************************************************
1806
 * Client
1807
 ******************************************************************************/
1808
1809
Client* Client::sInstance = nullptr;
1810
1811
Client::Client()
1812
  : mShutdownRequested(false)
1813
0
{
1814
0
  AssertIsOnBackgroundThread();
1815
0
  MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
1816
0
1817
0
  sInstance = this;
1818
0
}
1819
1820
Client::~Client()
1821
0
{
1822
0
  AssertIsOnBackgroundThread();
1823
0
  MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
1824
0
1825
0
  sInstance = nullptr;
1826
0
}
1827
1828
Client::Type
1829
Client::GetType()
1830
0
{
1831
0
  return ASMJS;
1832
0
}
1833
1834
nsresult
1835
Client::InitOrigin(PersistenceType aPersistenceType,
1836
                   const nsACString& aGroup,
1837
                   const nsACString& aOrigin,
1838
                   const AtomicBool& aCanceled,
1839
                   UsageInfo* aUsageInfo)
1840
0
{
1841
0
  if (!aUsageInfo) {
1842
0
    return NS_OK;
1843
0
  }
1844
0
  return GetUsageForOrigin(aPersistenceType,
1845
0
                           aGroup,
1846
0
                           aOrigin,
1847
0
                           aCanceled,
1848
0
                           aUsageInfo);
1849
0
}
1850
1851
nsresult
1852
Client::GetUsageForOrigin(PersistenceType aPersistenceType,
1853
                          const nsACString& aGroup,
1854
                          const nsACString& aOrigin,
1855
                          const AtomicBool& aCanceled,
1856
                          UsageInfo* aUsageInfo)
1857
0
{
1858
0
  QuotaManager* qm = QuotaManager::Get();
1859
0
  MOZ_ASSERT(qm, "We were being called by the QuotaManager");
1860
0
1861
0
  nsCOMPtr<nsIFile> directory;
1862
0
  nsresult rv = qm->GetDirectoryForOrigin(aPersistenceType, aOrigin,
1863
0
                                          getter_AddRefs(directory));
1864
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1865
0
    return rv;
1866
0
  }
1867
0
1868
0
  MOZ_ASSERT(directory, "We're here because the origin directory exists");
1869
0
1870
0
  rv = directory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
1871
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1872
0
    return rv;
1873
0
  }
1874
0
1875
0
  DebugOnly<bool> exists;
1876
0
  MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
1877
0
1878
0
  nsCOMPtr<nsIDirectoryEnumerator> entries;
1879
0
  rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
1880
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1881
0
    return rv;
1882
0
  }
1883
0
1884
0
  nsCOMPtr<nsIFile> file;
1885
0
  while (NS_SUCCEEDED((rv = entries->GetNextFile(getter_AddRefs(file)))) &&
1886
0
         file && !aCanceled) {
1887
0
    int64_t fileSize;
1888
0
    rv = file->GetFileSize(&fileSize);
1889
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1890
0
      return rv;
1891
0
    }
1892
0
1893
0
    MOZ_ASSERT(fileSize >= 0, "Negative size?!");
1894
0
1895
0
    // Since the client is not explicitly storing files, append to database
1896
0
    // usage which represents implicit storage allocation.
1897
0
    aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
1898
0
  }
1899
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1900
0
    return rv;
1901
0
  }
1902
0
1903
0
  return NS_OK;
1904
0
}
1905
1906
void
1907
Client::OnOriginClearCompleted(PersistenceType aPersistenceType,
1908
                               const nsACString& aOrigin)
1909
0
{
1910
0
}
1911
1912
void
1913
Client::ReleaseIOThreadObjects()
1914
0
{
1915
0
}
1916
1917
void
1918
Client::AbortOperations(const nsACString& aOrigin)
1919
0
{
1920
0
}
1921
1922
void
1923
Client::AbortOperationsForProcess(ContentParentId aContentParentId)
1924
0
{
1925
0
}
1926
1927
void
1928
Client::StartIdleMaintenance()
1929
0
{
1930
0
}
1931
1932
void
1933
Client::StopIdleMaintenance()
1934
0
{
1935
0
}
1936
1937
void
1938
Client::ShutdownWorkThreads()
1939
0
{
1940
0
  AssertIsOnBackgroundThread();
1941
0
  MOZ_ASSERT(!mShutdownRequested);
1942
0
1943
0
  mShutdownRequested = true;
1944
0
1945
0
  if (sLiveParentActors) {
1946
0
    MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() {
1947
0
      return !sLiveParentActors;
1948
0
    }));
1949
0
  }
1950
0
}
1951
1952
quota::Client*
1953
CreateClient()
1954
0
{
1955
0
  return new Client();
1956
0
}
1957
1958
} // namespace asmjscache
1959
} // namespace dom
1960
} // namespace mozilla
1961
1962
namespace IPC {
1963
1964
using mozilla::dom::asmjscache::Metadata;
1965
using mozilla::dom::asmjscache::WriteParams;
1966
1967
void
1968
ParamTraits<Metadata>::Write(Message* aMsg, const paramType& aParam)
1969
0
{
1970
0
  for (auto entry : aParam.mEntries) {
1971
0
    WriteParam(aMsg, entry.mFastHash);
1972
0
    WriteParam(aMsg, entry.mNumChars);
1973
0
    WriteParam(aMsg, entry.mFullHash);
1974
0
    WriteParam(aMsg, entry.mModuleIndex);
1975
0
  }
1976
0
}
1977
1978
bool
1979
ParamTraits<Metadata>::Read(const Message* aMsg, PickleIterator* aIter,
1980
                            paramType* aResult)
1981
0
{
1982
0
  for (auto& entry : aResult->mEntries) {
1983
0
    if (!ReadParam(aMsg, aIter, &entry.mFastHash) ||
1984
0
        !ReadParam(aMsg, aIter, &entry.mNumChars) ||
1985
0
        !ReadParam(aMsg, aIter, &entry.mFullHash) ||
1986
0
        !ReadParam(aMsg, aIter, &entry.mModuleIndex))
1987
0
    {
1988
0
      return false;
1989
0
    }
1990
0
  }
1991
0
  return true;
1992
0
}
1993
1994
void
1995
ParamTraits<Metadata>::Log(const paramType& aParam, std::wstring* aLog)
1996
0
{
1997
0
  for (auto entry : aParam.mEntries) {
1998
0
    LogParam(entry.mFastHash, aLog);
1999
0
    LogParam(entry.mNumChars, aLog);
2000
0
    LogParam(entry.mFullHash, aLog);
2001
0
    LogParam(entry.mModuleIndex, aLog);
2002
0
  }
2003
0
}
2004
2005
void
2006
ParamTraits<WriteParams>::Write(Message* aMsg, const paramType& aParam)
2007
0
{
2008
0
  WriteParam(aMsg, aParam.mSize);
2009
0
  WriteParam(aMsg, aParam.mFastHash);
2010
0
  WriteParam(aMsg, aParam.mNumChars);
2011
0
  WriteParam(aMsg, aParam.mFullHash);
2012
0
}
2013
2014
bool
2015
ParamTraits<WriteParams>::Read(const Message* aMsg, PickleIterator* aIter,
2016
                               paramType* aResult)
2017
0
{
2018
0
  return ReadParam(aMsg, aIter, &aResult->mSize) &&
2019
0
         ReadParam(aMsg, aIter, &aResult->mFastHash) &&
2020
0
         ReadParam(aMsg, aIter, &aResult->mNumChars) &&
2021
0
         ReadParam(aMsg, aIter, &aResult->mFullHash);
2022
0
}
2023
2024
void
2025
ParamTraits<WriteParams>::Log(const paramType& aParam, std::wstring* aLog)
2026
0
{
2027
0
  LogParam(aParam.mSize, aLog);
2028
0
  LogParam(aParam.mFastHash, aLog);
2029
0
  LogParam(aParam.mNumChars, aLog);
2030
0
  LogParam(aParam.mFullHash, aLog);
2031
0
}
2032
2033
} // namespace IPC