LCOV - code coverage report
Current view: top level - dom/asmjscache - AsmJSCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 752 0.1 %
Date: 2017-07-14 16:53:18 Functions: 1 95 1.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13