1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/asmjscache/AsmJSCache.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1943 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "AsmJSCache.h" 1.11 + 1.12 +#include <stdio.h> 1.13 + 1.14 +#include "js/RootingAPI.h" 1.15 +#include "jsfriendapi.h" 1.16 +#include "mozilla/Assertions.h" 1.17 +#include "mozilla/CondVar.h" 1.18 +#include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h" 1.19 +#include "mozilla/dom/asmjscache/PAsmJSCacheEntryParent.h" 1.20 +#include "mozilla/dom/ContentChild.h" 1.21 +#include "mozilla/dom/PermissionMessageUtils.h" 1.22 +#include "mozilla/dom/quota/Client.h" 1.23 +#include "mozilla/dom/quota/OriginOrPatternString.h" 1.24 +#include "mozilla/dom/quota/QuotaManager.h" 1.25 +#include "mozilla/dom/quota/QuotaObject.h" 1.26 +#include "mozilla/dom/quota/UsageInfo.h" 1.27 +#include "mozilla/HashFunctions.h" 1.28 +#include "mozilla/unused.h" 1.29 +#include "nsIAtom.h" 1.30 +#include "nsIFile.h" 1.31 +#include "nsIPermissionManager.h" 1.32 +#include "nsIPrincipal.h" 1.33 +#include "nsIRunnable.h" 1.34 +#include "nsISimpleEnumerator.h" 1.35 +#include "nsIThread.h" 1.36 +#include "nsIXULAppInfo.h" 1.37 +#include "nsJSPrincipals.h" 1.38 +#include "nsThreadUtils.h" 1.39 +#include "nsXULAppAPI.h" 1.40 +#include "prio.h" 1.41 +#include "private/pprio.h" 1.42 + 1.43 +#define ASMJSCACHE_METADATA_FILE_NAME "metadata" 1.44 +#define ASMJSCACHE_ENTRY_FILE_NAME_BASE "module" 1.45 + 1.46 +using mozilla::dom::quota::AssertIsOnIOThread; 1.47 +using mozilla::dom::quota::OriginOrPatternString; 1.48 +using mozilla::dom::quota::PersistenceType; 1.49 +using mozilla::dom::quota::QuotaManager; 1.50 +using mozilla::dom::quota::QuotaObject; 1.51 +using mozilla::dom::quota::UsageInfo; 1.52 +using mozilla::unused; 1.53 +using mozilla::HashString; 1.54 + 1.55 +namespace mozilla { 1.56 + 1.57 +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close); 1.58 + 1.59 +namespace dom { 1.60 +namespace asmjscache { 1.61 + 1.62 +namespace { 1.63 + 1.64 +bool 1.65 +IsMainProcess() 1.66 +{ 1.67 + return XRE_GetProcessType() == GeckoProcessType_Default; 1.68 +} 1.69 + 1.70 +// Anything smaller should compile fast enough that caching will just add 1.71 +// overhead. 1.72 +static const size_t sMinCachedModuleLength = 10000; 1.73 + 1.74 +// The number of characters to hash into the Metadata::Entry::mFastHash. 1.75 +static const unsigned sNumFastHashChars = 4096; 1.76 + 1.77 +nsresult 1.78 +WriteMetadataFile(nsIFile* aMetadataFile, const Metadata& aMetadata) 1.79 +{ 1.80 + int32_t openFlags = PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE; 1.81 + 1.82 + JS::BuildIdCharVector buildId; 1.83 + bool ok = GetBuildId(&buildId); 1.84 + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); 1.85 + 1.86 + ScopedPRFileDesc fd; 1.87 + nsresult rv = aMetadataFile->OpenNSPRFileDesc(openFlags, 0644, &fd.rwget()); 1.88 + NS_ENSURE_SUCCESS(rv, rv); 1.89 + 1.90 + uint32_t length = buildId.length(); 1.91 + int32_t bytesWritten = PR_Write(fd, &length, sizeof(length)); 1.92 + NS_ENSURE_TRUE(bytesWritten == sizeof(length), NS_ERROR_UNEXPECTED); 1.93 + 1.94 + bytesWritten = PR_Write(fd, buildId.begin(), length); 1.95 + NS_ENSURE_TRUE(bytesWritten == int32_t(length), NS_ERROR_UNEXPECTED); 1.96 + 1.97 + bytesWritten = PR_Write(fd, &aMetadata, sizeof(aMetadata)); 1.98 + NS_ENSURE_TRUE(bytesWritten == sizeof(aMetadata), NS_ERROR_UNEXPECTED); 1.99 + 1.100 + return NS_OK; 1.101 +} 1.102 + 1.103 +nsresult 1.104 +ReadMetadataFile(nsIFile* aMetadataFile, Metadata& aMetadata) 1.105 +{ 1.106 + int32_t openFlags = PR_RDONLY; 1.107 + 1.108 + ScopedPRFileDesc fd; 1.109 + nsresult rv = aMetadataFile->OpenNSPRFileDesc(openFlags, 0644, &fd.rwget()); 1.110 + NS_ENSURE_SUCCESS(rv, rv); 1.111 + 1.112 + // Read the buildid and check that it matches the current buildid 1.113 + 1.114 + JS::BuildIdCharVector currentBuildId; 1.115 + bool ok = GetBuildId(¤tBuildId); 1.116 + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); 1.117 + 1.118 + uint32_t length; 1.119 + int32_t bytesRead = PR_Read(fd, &length, sizeof(length)); 1.120 + NS_ENSURE_TRUE(bytesRead == sizeof(length), NS_ERROR_UNEXPECTED); 1.121 + 1.122 + NS_ENSURE_TRUE(currentBuildId.length() == length, NS_ERROR_UNEXPECTED); 1.123 + 1.124 + JS::BuildIdCharVector fileBuildId; 1.125 + ok = fileBuildId.resize(length); 1.126 + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); 1.127 + 1.128 + bytesRead = PR_Read(fd, fileBuildId.begin(), length); 1.129 + NS_ENSURE_TRUE(bytesRead == int32_t(length), NS_ERROR_UNEXPECTED); 1.130 + 1.131 + for (uint32_t i = 0; i < length; i++) { 1.132 + if (currentBuildId[i] != fileBuildId[i]) { 1.133 + return NS_ERROR_FAILURE; 1.134 + } 1.135 + } 1.136 + 1.137 + // Read the Metadata struct 1.138 + 1.139 + bytesRead = PR_Read(fd, &aMetadata, sizeof(aMetadata)); 1.140 + NS_ENSURE_TRUE(bytesRead == sizeof(aMetadata), NS_ERROR_UNEXPECTED); 1.141 + 1.142 + return NS_OK; 1.143 +} 1.144 + 1.145 +nsresult 1.146 +GetCacheFile(nsIFile* aDirectory, unsigned aModuleIndex, nsIFile** aCacheFile) 1.147 +{ 1.148 + nsCOMPtr<nsIFile> cacheFile; 1.149 + nsresult rv = aDirectory->Clone(getter_AddRefs(cacheFile)); 1.150 + NS_ENSURE_SUCCESS(rv, rv); 1.151 + 1.152 + nsString cacheFileName = NS_LITERAL_STRING(ASMJSCACHE_ENTRY_FILE_NAME_BASE); 1.153 + cacheFileName.AppendInt(aModuleIndex); 1.154 + rv = cacheFile->Append(cacheFileName); 1.155 + NS_ENSURE_SUCCESS(rv, rv); 1.156 + 1.157 + cacheFile.forget(aCacheFile); 1.158 + return NS_OK; 1.159 +} 1.160 + 1.161 +class AutoDecreaseUsageForOrigin 1.162 +{ 1.163 + const nsACString& mGroup; 1.164 + const nsACString& mOrigin; 1.165 + 1.166 +public: 1.167 + uint64_t mFreed; 1.168 + 1.169 + AutoDecreaseUsageForOrigin(const nsACString& aGroup, 1.170 + const nsACString& aOrigin) 1.171 + 1.172 + : mGroup(aGroup), 1.173 + mOrigin(aOrigin), 1.174 + mFreed(0) 1.175 + { } 1.176 + 1.177 + ~AutoDecreaseUsageForOrigin() 1.178 + { 1.179 + AssertIsOnIOThread(); 1.180 + 1.181 + if (!mFreed) { 1.182 + return; 1.183 + } 1.184 + 1.185 + QuotaManager* qm = QuotaManager::Get(); 1.186 + MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread"); 1.187 + 1.188 + qm->DecreaseUsageForOrigin(quota::PERSISTENCE_TYPE_TEMPORARY, 1.189 + mGroup, mOrigin, mFreed); 1.190 + } 1.191 +}; 1.192 + 1.193 +static void 1.194 +EvictEntries(nsIFile* aDirectory, const nsACString& aGroup, 1.195 + const nsACString& aOrigin, uint64_t aNumBytes, 1.196 + Metadata& aMetadata) 1.197 +{ 1.198 + AssertIsOnIOThread(); 1.199 + 1.200 + AutoDecreaseUsageForOrigin usage(aGroup, aOrigin); 1.201 + 1.202 + for (int i = Metadata::kLastEntry; i >= 0 && usage.mFreed < aNumBytes; i--) { 1.203 + Metadata::Entry& entry = aMetadata.mEntries[i]; 1.204 + unsigned moduleIndex = entry.mModuleIndex; 1.205 + 1.206 + nsCOMPtr<nsIFile> file; 1.207 + nsresult rv = GetCacheFile(aDirectory, moduleIndex, getter_AddRefs(file)); 1.208 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.209 + return; 1.210 + } 1.211 + 1.212 + bool exists; 1.213 + rv = file->Exists(&exists); 1.214 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.215 + return; 1.216 + } 1.217 + 1.218 + if (exists) { 1.219 + int64_t fileSize; 1.220 + rv = file->GetFileSize(&fileSize); 1.221 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.222 + return; 1.223 + } 1.224 + 1.225 + rv = file->Remove(false); 1.226 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.227 + return; 1.228 + } 1.229 + 1.230 + usage.mFreed += fileSize; 1.231 + } 1.232 + 1.233 + entry.clear(); 1.234 + } 1.235 +} 1.236 + 1.237 +// FileDescriptorHolder owns a file descriptor and its memory mapping. 1.238 +// FileDescriptorHolder is derived by all three runnable classes (that is, 1.239 +// (Single|Parent|Child)ProcessRunnable. To avoid awkward workarouds, 1.240 +// FileDescriptorHolder is derived virtually by File and MainProcessRunnable for 1.241 +// the benefit of SingleProcessRunnable, which derives both. Since File and 1.242 +// MainProcessRunnable both need to be runnables, FileDescriptorHolder also 1.243 +// derives nsRunnable. 1.244 +class FileDescriptorHolder : public nsRunnable 1.245 +{ 1.246 +public: 1.247 + FileDescriptorHolder() 1.248 + : mQuotaObject(nullptr), 1.249 + mFileSize(INT64_MIN), 1.250 + mFileDesc(nullptr), 1.251 + mFileMap(nullptr), 1.252 + mMappedMemory(nullptr) 1.253 + { } 1.254 + 1.255 + ~FileDescriptorHolder() 1.256 + { 1.257 + // These resources should have already been released by Finish(). 1.258 + MOZ_ASSERT(!mQuotaObject); 1.259 + MOZ_ASSERT(!mMappedMemory); 1.260 + MOZ_ASSERT(!mFileMap); 1.261 + MOZ_ASSERT(!mFileDesc); 1.262 + } 1.263 + 1.264 + size_t 1.265 + FileSize() const 1.266 + { 1.267 + MOZ_ASSERT(mFileSize >= 0, "Accessing FileSize of unopened file"); 1.268 + return mFileSize; 1.269 + } 1.270 + 1.271 + PRFileDesc* 1.272 + FileDesc() const 1.273 + { 1.274 + MOZ_ASSERT(mFileDesc, "Accessing FileDesc of unopened file"); 1.275 + return mFileDesc; 1.276 + } 1.277 + 1.278 + bool 1.279 + MapMemory(OpenMode aOpenMode) 1.280 + { 1.281 + MOZ_ASSERT(!mFileMap, "Cannot call MapMemory twice"); 1.282 + 1.283 + PRFileMapProtect mapFlags = aOpenMode == eOpenForRead ? PR_PROT_READONLY 1.284 + : PR_PROT_READWRITE; 1.285 + 1.286 + mFileMap = PR_CreateFileMap(mFileDesc, mFileSize, mapFlags); 1.287 + NS_ENSURE_TRUE(mFileMap, false); 1.288 + 1.289 + mMappedMemory = PR_MemMap(mFileMap, 0, mFileSize); 1.290 + NS_ENSURE_TRUE(mMappedMemory, false); 1.291 + 1.292 + return true; 1.293 + } 1.294 + 1.295 + void* 1.296 + MappedMemory() const 1.297 + { 1.298 + MOZ_ASSERT(mMappedMemory, "Accessing MappedMemory of un-mapped file"); 1.299 + return mMappedMemory; 1.300 + } 1.301 + 1.302 +protected: 1.303 + // This method must be called before AllowNextSynchronizedOp (which releases 1.304 + // the lock protecting these resources). It is idempotent, so it is ok to call 1.305 + // multiple times (or before the file has been fully opened). 1.306 + void 1.307 + Finish() 1.308 + { 1.309 + if (mMappedMemory) { 1.310 + PR_MemUnmap(mMappedMemory, mFileSize); 1.311 + mMappedMemory = nullptr; 1.312 + } 1.313 + if (mFileMap) { 1.314 + PR_CloseFileMap(mFileMap); 1.315 + mFileMap = nullptr; 1.316 + } 1.317 + if (mFileDesc) { 1.318 + PR_Close(mFileDesc); 1.319 + mFileDesc = nullptr; 1.320 + } 1.321 + 1.322 + // Holding the QuotaObject alive until all the cache files are closed enables 1.323 + // assertions in QuotaManager that the cache entry isn't cleared while we 1.324 + // are working on it. 1.325 + mQuotaObject = nullptr; 1.326 + } 1.327 + 1.328 + nsRefPtr<QuotaObject> mQuotaObject; 1.329 + int64_t mFileSize; 1.330 + PRFileDesc* mFileDesc; 1.331 + PRFileMap* mFileMap; 1.332 + void* mMappedMemory; 1.333 +}; 1.334 + 1.335 +// File is a base class shared by (Single|Client)ProcessEntryRunnable that 1.336 +// presents a single interface to the AsmJSCache ops which need to wait until 1.337 +// the file is open, regardless of whether we are executing in the main process 1.338 +// or not. 1.339 +class File : public virtual FileDescriptorHolder 1.340 +{ 1.341 +public: 1.342 + class AutoClose 1.343 + { 1.344 + File* mFile; 1.345 + 1.346 + public: 1.347 + explicit AutoClose(File* aFile = nullptr) 1.348 + : mFile(aFile) 1.349 + { } 1.350 + 1.351 + void 1.352 + Init(File* aFile) 1.353 + { 1.354 + MOZ_ASSERT(!mFile); 1.355 + mFile = aFile; 1.356 + } 1.357 + 1.358 + File* 1.359 + operator->() const 1.360 + { 1.361 + MOZ_ASSERT(mFile); 1.362 + return mFile; 1.363 + } 1.364 + 1.365 + void 1.366 + Forget(File** aFile) 1.367 + { 1.368 + *aFile = mFile; 1.369 + mFile = nullptr; 1.370 + } 1.371 + 1.372 + ~AutoClose() 1.373 + { 1.374 + if (mFile) { 1.375 + mFile->Close(); 1.376 + } 1.377 + } 1.378 + }; 1.379 + 1.380 + bool 1.381 + BlockUntilOpen(AutoClose* aCloser) 1.382 + { 1.383 + MOZ_ASSERT(!mWaiting, "Can only call BlockUntilOpen once"); 1.384 + MOZ_ASSERT(!mOpened, "Can only call BlockUntilOpen once"); 1.385 + 1.386 + mWaiting = true; 1.387 + 1.388 + nsresult rv = NS_DispatchToMainThread(this); 1.389 + NS_ENSURE_SUCCESS(rv, false); 1.390 + 1.391 + { 1.392 + MutexAutoLock lock(mMutex); 1.393 + while (mWaiting) { 1.394 + mCondVar.Wait(); 1.395 + } 1.396 + } 1.397 + 1.398 + if (!mOpened) { 1.399 + return false; 1.400 + } 1.401 + 1.402 + // Now that we're open, we're guarnateed a Close() call. However, we are 1.403 + // not guarnateed someone is holding an outstanding reference until the File 1.404 + // is closed, so we do that ourselves and Release() in OnClose(). 1.405 + aCloser->Init(this); 1.406 + AddRef(); 1.407 + return true; 1.408 + } 1.409 + 1.410 + // This method must be called if BlockUntilOpen returns 'true'. AutoClose 1.411 + // mostly takes care of this. A derived class that implements Close() must 1.412 + // guarnatee that OnClose() is called (eventually). 1.413 + virtual void 1.414 + Close() = 0; 1.415 + 1.416 +protected: 1.417 + File() 1.418 + : mMutex("File::mMutex"), 1.419 + mCondVar(mMutex, "File::mCondVar"), 1.420 + mWaiting(false), 1.421 + mOpened(false) 1.422 + { } 1.423 + 1.424 + ~File() 1.425 + { 1.426 + MOZ_ASSERT(!mWaiting, "Shouldn't be destroyed while thread is waiting"); 1.427 + MOZ_ASSERT(!mOpened, "OnClose() should have been called"); 1.428 + } 1.429 + 1.430 + void 1.431 + OnOpen() 1.432 + { 1.433 + Notify(true); 1.434 + } 1.435 + 1.436 + void 1.437 + OnFailure() 1.438 + { 1.439 + FileDescriptorHolder::Finish(); 1.440 + 1.441 + Notify(false); 1.442 + } 1.443 + 1.444 + void 1.445 + OnClose() 1.446 + { 1.447 + FileDescriptorHolder::Finish(); 1.448 + 1.449 + MOZ_ASSERT(mOpened); 1.450 + mOpened = false; 1.451 + 1.452 + // Match the AddRef in BlockUntilOpen(). The main thread event loop still 1.453 + // holds an outstanding ref which will keep 'this' alive until returning to 1.454 + // the event loop. 1.455 + Release(); 1.456 + } 1.457 + 1.458 +private: 1.459 + void 1.460 + Notify(bool aSuccess) 1.461 + { 1.462 + MOZ_ASSERT(NS_IsMainThread()); 1.463 + 1.464 + MutexAutoLock lock(mMutex); 1.465 + MOZ_ASSERT(mWaiting); 1.466 + 1.467 + mWaiting = false; 1.468 + mOpened = aSuccess; 1.469 + mCondVar.Notify(); 1.470 + } 1.471 + 1.472 + Mutex mMutex; 1.473 + CondVar mCondVar; 1.474 + bool mWaiting; 1.475 + bool mOpened; 1.476 +}; 1.477 + 1.478 +// MainProcessRunnable is a base class shared by (Single|Parent)ProcessRunnable 1.479 +// that factors out the runnable state machine required to open a cache entry 1.480 +// that runs in the main process. 1.481 +class MainProcessRunnable : public virtual FileDescriptorHolder 1.482 +{ 1.483 +public: 1.484 + NS_DECL_NSIRUNNABLE 1.485 + 1.486 + // MainProcessRunnable runnable assumes that the derived class ensures 1.487 + // (through ref-counting or preconditions) that aPrincipal is kept alive for 1.488 + // the lifetime of the MainProcessRunnable. 1.489 + MainProcessRunnable(nsIPrincipal* aPrincipal, 1.490 + OpenMode aOpenMode, 1.491 + WriteParams aWriteParams) 1.492 + : mPrincipal(aPrincipal), 1.493 + mOpenMode(aOpenMode), 1.494 + mWriteParams(aWriteParams), 1.495 + mNeedAllowNextSynchronizedOp(false), 1.496 + mPersistence(quota::PERSISTENCE_TYPE_INVALID), 1.497 + mState(eInitial) 1.498 + { 1.499 + MOZ_ASSERT(IsMainProcess()); 1.500 + } 1.501 + 1.502 + virtual ~MainProcessRunnable() 1.503 + { 1.504 + MOZ_ASSERT(mState == eFinished); 1.505 + MOZ_ASSERT(!mNeedAllowNextSynchronizedOp); 1.506 + } 1.507 + 1.508 +protected: 1.509 + // This method is called by the derived class on the main thread when a 1.510 + // cache entry has been selected to open. 1.511 + void 1.512 + OpenForRead(unsigned aModuleIndex) 1.513 + { 1.514 + MOZ_ASSERT(NS_IsMainThread()); 1.515 + MOZ_ASSERT(mState == eWaitingToOpenCacheFileForRead); 1.516 + MOZ_ASSERT(mOpenMode == eOpenForRead); 1.517 + 1.518 + mModuleIndex = aModuleIndex; 1.519 + mState = eReadyToOpenCacheFileForRead; 1.520 + DispatchToIOThread(); 1.521 + } 1.522 + 1.523 + // This method is called by the derived class on the main thread when no cache 1.524 + // entry was found to open. If we just tried a lookup in persistent storage 1.525 + // then we might still get a hit in temporary storage (for an asm.js module 1.526 + // that wasn't compiled at install-time). 1.527 + void 1.528 + CacheMiss() 1.529 + { 1.530 + MOZ_ASSERT(NS_IsMainThread()); 1.531 + MOZ_ASSERT(mState == eFailedToReadMetadata || 1.532 + mState == eWaitingToOpenCacheFileForRead); 1.533 + MOZ_ASSERT(mOpenMode == eOpenForRead); 1.534 + 1.535 + if (mPersistence == quota::PERSISTENCE_TYPE_TEMPORARY) { 1.536 + Fail(); 1.537 + return; 1.538 + } 1.539 + 1.540 + // Try again with a clean slate. InitOnMainThread will see that mPersistence 1.541 + // is initialized and switch to temporary storage. 1.542 + MOZ_ASSERT(mPersistence == quota::PERSISTENCE_TYPE_PERSISTENT); 1.543 + FinishOnMainThread(); 1.544 + mState = eInitial; 1.545 + NS_DispatchToMainThread(this); 1.546 + } 1.547 + 1.548 + // This method is called by the derived class (either on the JS compilation 1.549 + // thread or the main thread) when the JS engine is finished reading/writing 1.550 + // the cache entry. 1.551 + void 1.552 + Close() 1.553 + { 1.554 + MOZ_ASSERT(mState == eOpened); 1.555 + mState = eClosing; 1.556 + NS_DispatchToMainThread(this); 1.557 + } 1.558 + 1.559 + // This method is called both internally and by derived classes upon any 1.560 + // failure that prevents the eventual opening of the cache entry. 1.561 + void 1.562 + Fail() 1.563 + { 1.564 + MOZ_ASSERT(mState != eOpened && 1.565 + mState != eClosing && 1.566 + mState != eFailing && 1.567 + mState != eFinished); 1.568 + 1.569 + mState = eFailing; 1.570 + NS_DispatchToMainThread(this); 1.571 + } 1.572 + 1.573 + // Called by MainProcessRunnable on the main thread after metadata is open: 1.574 + virtual void 1.575 + OnOpenMetadataForRead(const Metadata& aMetadata) = 0; 1.576 + 1.577 + // Called by MainProcessRunnable on the main thread after the entry is open: 1.578 + virtual void 1.579 + OnOpenCacheFile() = 0; 1.580 + 1.581 + // This method may be overridden, but it must be called from the overrider. 1.582 + // Called by MainProcessRunnable on the main thread after a call to Fail(): 1.583 + virtual void 1.584 + OnFailure() 1.585 + { 1.586 + FinishOnMainThread(); 1.587 + } 1.588 + 1.589 + // This method may be overridden, but it must be called from the overrider. 1.590 + // Called by MainProcessRunnable on the main thread after a call to Close(): 1.591 + virtual void 1.592 + OnClose() 1.593 + { 1.594 + FinishOnMainThread(); 1.595 + } 1.596 + 1.597 +private: 1.598 + nsresult 1.599 + InitOnMainThread(); 1.600 + 1.601 + nsresult 1.602 + ReadMetadata(); 1.603 + 1.604 + nsresult 1.605 + OpenCacheFileForWrite(); 1.606 + 1.607 + nsresult 1.608 + OpenCacheFileForRead(); 1.609 + 1.610 + void 1.611 + FinishOnMainThread(); 1.612 + 1.613 + void 1.614 + DispatchToIOThread() 1.615 + { 1.616 + // If shutdown just started, the QuotaManager may have been deleted. 1.617 + QuotaManager* qm = QuotaManager::Get(); 1.618 + if (!qm) { 1.619 + Fail(); 1.620 + return; 1.621 + } 1.622 + 1.623 + nsresult rv = qm->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); 1.624 + if (NS_FAILED(rv)) { 1.625 + Fail(); 1.626 + return; 1.627 + } 1.628 + } 1.629 + 1.630 + nsIPrincipal* const mPrincipal; 1.631 + const OpenMode mOpenMode; 1.632 + const WriteParams mWriteParams; 1.633 + 1.634 + // State initialized during eInitial: 1.635 + bool mNeedAllowNextSynchronizedOp; 1.636 + quota::PersistenceType mPersistence; 1.637 + nsCString mGroup; 1.638 + nsCString mOrigin; 1.639 + nsCString mStorageId; 1.640 + 1.641 + // State initialized during eReadyToReadMetadata 1.642 + nsCOMPtr<nsIFile> mDirectory; 1.643 + nsCOMPtr<nsIFile> mMetadataFile; 1.644 + Metadata mMetadata; 1.645 + 1.646 + // State initialized during eWaitingToOpenCacheFileForRead 1.647 + unsigned mModuleIndex; 1.648 + 1.649 + enum State { 1.650 + eInitial, // Just created, waiting to be dispatched to main thread 1.651 + eWaitingToOpenMetadata, // Waiting to be called back from WaitForOpenAllowed 1.652 + eReadyToReadMetadata, // Waiting to read the metadata file on the IO thread 1.653 + eFailedToReadMetadata, // Waiting to be dispatched to main thread after fail 1.654 + eSendingMetadataForRead, // Waiting to send OnOpenMetadataForRead 1.655 + eWaitingToOpenCacheFileForRead, // Waiting to hear back from child 1.656 + eReadyToOpenCacheFileForRead, // Waiting to open cache file for read 1.657 + eSendingCacheFile, // Waiting to send OnOpenCacheFile on the main thread 1.658 + eOpened, // Finished calling OnOpen, waiting to be closed 1.659 + eClosing, // Waiting to be dispatched to main thread again 1.660 + eFailing, // Just failed, waiting to be dispatched to the main thread 1.661 + eFinished, // Terminal state 1.662 + }; 1.663 + State mState; 1.664 +}; 1.665 + 1.666 +nsresult 1.667 +MainProcessRunnable::InitOnMainThread() 1.668 +{ 1.669 + MOZ_ASSERT(NS_IsMainThread()); 1.670 + MOZ_ASSERT(mState == eInitial); 1.671 + 1.672 + QuotaManager* qm = QuotaManager::GetOrCreate(); 1.673 + NS_ENSURE_STATE(qm); 1.674 + 1.675 + nsresult rv = QuotaManager::GetInfoFromPrincipal(mPrincipal, &mGroup, 1.676 + &mOrigin, nullptr, nullptr); 1.677 + NS_ENSURE_SUCCESS(rv, rv); 1.678 + 1.679 + bool isApp = mPrincipal->GetAppStatus() != 1.680 + nsIPrincipal::APP_STATUS_NOT_INSTALLED; 1.681 + 1.682 + if (mOpenMode == eOpenForWrite) { 1.683 + MOZ_ASSERT(mPersistence == quota::PERSISTENCE_TYPE_INVALID); 1.684 + if (mWriteParams.mInstalled) { 1.685 + // If we are performing install-time caching of an app, we'd like to store 1.686 + // the cache entry in persistent storage so the entry is never evicted, 1.687 + // but we need to verify that the app has unlimited storage permissions 1.688 + // first. Unlimited storage permissions justify us in skipping all quota 1.689 + // checks when storing the cache entry and avoids all the issues around 1.690 + // the persistent quota prompt. 1.691 + MOZ_ASSERT(isApp); 1.692 + 1.693 + nsCOMPtr<nsIPermissionManager> pm = 1.694 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.695 + NS_ENSURE_TRUE(pm, NS_ERROR_UNEXPECTED); 1.696 + 1.697 + uint32_t permission; 1.698 + rv = pm->TestPermissionFromPrincipal(mPrincipal, 1.699 + PERMISSION_STORAGE_UNLIMITED, 1.700 + &permission); 1.701 + NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); 1.702 + 1.703 + // If app doens't have the unlimited storage permission, we can still 1.704 + // cache in temporary for a likely good first-run experience. 1.705 + mPersistence = permission == nsIPermissionManager::ALLOW_ACTION 1.706 + ? quota::PERSISTENCE_TYPE_PERSISTENT 1.707 + : quota::PERSISTENCE_TYPE_TEMPORARY; 1.708 + } else { 1.709 + mPersistence = quota::PERSISTENCE_TYPE_TEMPORARY; 1.710 + } 1.711 + } else { 1.712 + // For the reasons described above, apps may have cache entries in both 1.713 + // persistent and temporary storage. At lookup time we don't know how and 1.714 + // where the given script was cached, so start the search in persistent 1.715 + // storage and, if that fails, search in temporary storage. (Non-apps can 1.716 + // only be stored in temporary storage.) 1.717 + if (mPersistence == quota::PERSISTENCE_TYPE_INVALID) { 1.718 + mPersistence = isApp ? quota::PERSISTENCE_TYPE_PERSISTENT 1.719 + : quota::PERSISTENCE_TYPE_TEMPORARY; 1.720 + } else { 1.721 + MOZ_ASSERT(isApp); 1.722 + MOZ_ASSERT(mPersistence == quota::PERSISTENCE_TYPE_PERSISTENT); 1.723 + mPersistence = quota::PERSISTENCE_TYPE_TEMPORARY; 1.724 + } 1.725 + } 1.726 + 1.727 + QuotaManager::GetStorageId(mPersistence, mOrigin, quota::Client::ASMJS, 1.728 + NS_LITERAL_STRING("asmjs"), mStorageId); 1.729 + 1.730 + return NS_OK; 1.731 +} 1.732 + 1.733 +nsresult 1.734 +MainProcessRunnable::ReadMetadata() 1.735 +{ 1.736 + AssertIsOnIOThread(); 1.737 + MOZ_ASSERT(mState == eReadyToReadMetadata); 1.738 + 1.739 + QuotaManager* qm = QuotaManager::Get(); 1.740 + MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread"); 1.741 + 1.742 + // Only track quota for temporary storage. For persistent storage, we've 1.743 + // already checked that we have unlimited-storage permissions. 1.744 + bool trackQuota = mPersistence == quota::PERSISTENCE_TYPE_TEMPORARY; 1.745 + 1.746 + nsresult rv = qm->EnsureOriginIsInitialized(mPersistence, mGroup, mOrigin, 1.747 + trackQuota, 1.748 + getter_AddRefs(mDirectory)); 1.749 + NS_ENSURE_SUCCESS(rv, rv); 1.750 + 1.751 + rv = mDirectory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME)); 1.752 + NS_ENSURE_SUCCESS(rv, rv); 1.753 + 1.754 + bool exists; 1.755 + rv = mDirectory->Exists(&exists); 1.756 + NS_ENSURE_SUCCESS(rv, rv); 1.757 + 1.758 + if (!exists) { 1.759 + rv = mDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); 1.760 + NS_ENSURE_SUCCESS(rv, rv); 1.761 + } else { 1.762 + DebugOnly<bool> isDirectory; 1.763 + MOZ_ASSERT(NS_SUCCEEDED(mDirectory->IsDirectory(&isDirectory))); 1.764 + MOZ_ASSERT(isDirectory, "Should have caught this earlier!"); 1.765 + } 1.766 + 1.767 + rv = mDirectory->Clone(getter_AddRefs(mMetadataFile)); 1.768 + NS_ENSURE_SUCCESS(rv, rv); 1.769 + 1.770 + rv = mMetadataFile->Append(NS_LITERAL_STRING(ASMJSCACHE_METADATA_FILE_NAME)); 1.771 + NS_ENSURE_SUCCESS(rv, rv); 1.772 + 1.773 + rv = mMetadataFile->Exists(&exists); 1.774 + NS_ENSURE_SUCCESS(rv, rv); 1.775 + 1.776 + if (exists && NS_FAILED(ReadMetadataFile(mMetadataFile, mMetadata))) { 1.777 + exists = false; 1.778 + } 1.779 + 1.780 + if (!exists) { 1.781 + // If we are reading, we can't possibly have a cache hit. 1.782 + if (mOpenMode == eOpenForRead) { 1.783 + return NS_ERROR_FILE_NOT_FOUND; 1.784 + } 1.785 + 1.786 + // Initialize Metadata with a valid empty state for the LRU cache. 1.787 + for (unsigned i = 0; i < Metadata::kNumEntries; i++) { 1.788 + Metadata::Entry& entry = mMetadata.mEntries[i]; 1.789 + entry.mModuleIndex = i; 1.790 + entry.clear(); 1.791 + } 1.792 + } 1.793 + 1.794 + return NS_OK; 1.795 +} 1.796 + 1.797 +nsresult 1.798 +MainProcessRunnable::OpenCacheFileForWrite() 1.799 +{ 1.800 + AssertIsOnIOThread(); 1.801 + MOZ_ASSERT(mState == eReadyToReadMetadata); 1.802 + MOZ_ASSERT(mOpenMode == eOpenForWrite); 1.803 + 1.804 + mFileSize = mWriteParams.mSize; 1.805 + 1.806 + // Kick out the oldest entry in the LRU queue in the metadata. 1.807 + mModuleIndex = mMetadata.mEntries[Metadata::kLastEntry].mModuleIndex; 1.808 + 1.809 + nsCOMPtr<nsIFile> file; 1.810 + nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file)); 1.811 + NS_ENSURE_SUCCESS(rv, rv); 1.812 + 1.813 + QuotaManager* qm = QuotaManager::Get(); 1.814 + MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread"); 1.815 + 1.816 + // If we are allocating in temporary storage, ask the QuotaManager if we're 1.817 + // within the quota. If we are allocating in persistent storage, we've already 1.818 + // checked that we have the unlimited-storage permission, so there is nothing 1.819 + // to check. 1.820 + if (mPersistence == quota::PERSISTENCE_TYPE_TEMPORARY) { 1.821 + // Create the QuotaObject before all file IO and keep it alive until caching 1.822 + // completes to get maximum assertion coverage in QuotaManager against 1.823 + // concurrent removal, etc. 1.824 + mQuotaObject = qm->GetQuotaObject(mPersistence, mGroup, mOrigin, file); 1.825 + NS_ENSURE_STATE(mQuotaObject); 1.826 + 1.827 + if (!mQuotaObject->MaybeAllocateMoreSpace(0, mWriteParams.mSize)) { 1.828 + // If the request fails, it might be because mOrigin is using too much 1.829 + // space (MaybeAllocateMoreSpace will not evict our own origin since it is 1.830 + // active). Try to make some space by evicting LRU entries until there is 1.831 + // enough space. 1.832 + EvictEntries(mDirectory, mGroup, mOrigin, mWriteParams.mSize, mMetadata); 1.833 + if (!mQuotaObject->MaybeAllocateMoreSpace(0, mWriteParams.mSize)) { 1.834 + return NS_ERROR_FAILURE; 1.835 + } 1.836 + } 1.837 + } 1.838 + 1.839 + int32_t openFlags = PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE; 1.840 + rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc); 1.841 + NS_ENSURE_SUCCESS(rv, rv); 1.842 + 1.843 + // Move the mModuleIndex's LRU entry to the recent end of the queue. 1.844 + PodMove(mMetadata.mEntries + 1, mMetadata.mEntries, Metadata::kLastEntry); 1.845 + Metadata::Entry& entry = mMetadata.mEntries[0]; 1.846 + entry.mFastHash = mWriteParams.mFastHash; 1.847 + entry.mNumChars = mWriteParams.mNumChars; 1.848 + entry.mFullHash = mWriteParams.mFullHash; 1.849 + entry.mModuleIndex = mModuleIndex; 1.850 + 1.851 + rv = WriteMetadataFile(mMetadataFile, mMetadata); 1.852 + NS_ENSURE_SUCCESS(rv, rv); 1.853 + 1.854 + return NS_OK; 1.855 +} 1.856 + 1.857 +nsresult 1.858 +MainProcessRunnable::OpenCacheFileForRead() 1.859 +{ 1.860 + AssertIsOnIOThread(); 1.861 + MOZ_ASSERT(mState == eReadyToOpenCacheFileForRead); 1.862 + MOZ_ASSERT(mOpenMode == eOpenForRead); 1.863 + 1.864 + nsCOMPtr<nsIFile> file; 1.865 + nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file)); 1.866 + NS_ENSURE_SUCCESS(rv, rv); 1.867 + 1.868 + QuotaManager* qm = QuotaManager::Get(); 1.869 + MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread"); 1.870 + 1.871 + if (mPersistence == quota::PERSISTENCE_TYPE_TEMPORARY) { 1.872 + // Even though it's not strictly necessary, create the QuotaObject before 1.873 + // all file IO and keep it alive until caching completes to get maximum 1.874 + // assertion coverage in QuotaManager against concurrent removal, etc. 1.875 + mQuotaObject = qm->GetQuotaObject(mPersistence, mGroup, mOrigin, file); 1.876 + NS_ENSURE_STATE(mQuotaObject); 1.877 + } 1.878 + 1.879 + rv = file->GetFileSize(&mFileSize); 1.880 + NS_ENSURE_SUCCESS(rv, rv); 1.881 + 1.882 + int32_t openFlags = PR_RDONLY | nsIFile::OS_READAHEAD; 1.883 + rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc); 1.884 + NS_ENSURE_SUCCESS(rv, rv); 1.885 + 1.886 + // Move the mModuleIndex's LRU entry to the recent end of the queue. 1.887 + unsigned lruIndex = 0; 1.888 + while (mMetadata.mEntries[lruIndex].mModuleIndex != mModuleIndex) { 1.889 + if (++lruIndex == Metadata::kNumEntries) { 1.890 + return NS_ERROR_UNEXPECTED; 1.891 + } 1.892 + } 1.893 + Metadata::Entry entry = mMetadata.mEntries[lruIndex]; 1.894 + PodMove(mMetadata.mEntries + 1, mMetadata.mEntries, lruIndex); 1.895 + mMetadata.mEntries[0] = entry; 1.896 + 1.897 + rv = WriteMetadataFile(mMetadataFile, mMetadata); 1.898 + NS_ENSURE_SUCCESS(rv, rv); 1.899 + 1.900 + return NS_OK; 1.901 +} 1.902 + 1.903 +void 1.904 +MainProcessRunnable::FinishOnMainThread() 1.905 +{ 1.906 + MOZ_ASSERT(NS_IsMainThread()); 1.907 + 1.908 + // Per FileDescriptorHolder::Finish()'s comment, call before 1.909 + // AllowNextSynchronizedOp. 1.910 + FileDescriptorHolder::Finish(); 1.911 + 1.912 + if (mNeedAllowNextSynchronizedOp) { 1.913 + mNeedAllowNextSynchronizedOp = false; 1.914 + QuotaManager* qm = QuotaManager::Get(); 1.915 + if (qm) { 1.916 + qm->AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mOrigin), 1.917 + Nullable<PersistenceType>(mPersistence), 1.918 + mStorageId); 1.919 + } 1.920 + } 1.921 +} 1.922 + 1.923 +NS_IMETHODIMP 1.924 +MainProcessRunnable::Run() 1.925 +{ 1.926 + nsresult rv; 1.927 + 1.928 + // All success/failure paths must eventually call Finish() to avoid leaving 1.929 + // the parser hanging. 1.930 + switch (mState) { 1.931 + case eInitial: { 1.932 + MOZ_ASSERT(NS_IsMainThread()); 1.933 + 1.934 + rv = InitOnMainThread(); 1.935 + if (NS_FAILED(rv)) { 1.936 + Fail(); 1.937 + return NS_OK; 1.938 + } 1.939 + 1.940 + mState = eWaitingToOpenMetadata; 1.941 + rv = QuotaManager::Get()->WaitForOpenAllowed( 1.942 + OriginOrPatternString::FromOrigin(mOrigin), 1.943 + Nullable<PersistenceType>(mPersistence), 1.944 + mStorageId, this); 1.945 + if (NS_FAILED(rv)) { 1.946 + Fail(); 1.947 + return NS_OK; 1.948 + } 1.949 + 1.950 + mNeedAllowNextSynchronizedOp = true; 1.951 + return NS_OK; 1.952 + } 1.953 + 1.954 + case eWaitingToOpenMetadata: { 1.955 + MOZ_ASSERT(NS_IsMainThread()); 1.956 + 1.957 + mState = eReadyToReadMetadata; 1.958 + DispatchToIOThread(); 1.959 + return NS_OK; 1.960 + } 1.961 + 1.962 + case eReadyToReadMetadata: { 1.963 + AssertIsOnIOThread(); 1.964 + 1.965 + rv = ReadMetadata(); 1.966 + if (NS_FAILED(rv)) { 1.967 + mState = eFailedToReadMetadata; 1.968 + NS_DispatchToMainThread(this); 1.969 + return NS_OK; 1.970 + } 1.971 + 1.972 + if (mOpenMode == eOpenForRead) { 1.973 + mState = eSendingMetadataForRead; 1.974 + NS_DispatchToMainThread(this); 1.975 + return NS_OK; 1.976 + } 1.977 + 1.978 + rv = OpenCacheFileForWrite(); 1.979 + if (NS_FAILED(rv)) { 1.980 + Fail(); 1.981 + return NS_OK; 1.982 + } 1.983 + 1.984 + mState = eSendingCacheFile; 1.985 + NS_DispatchToMainThread(this); 1.986 + return NS_OK; 1.987 + } 1.988 + 1.989 + case eFailedToReadMetadata: { 1.990 + MOZ_ASSERT(NS_IsMainThread()); 1.991 + 1.992 + CacheMiss(); 1.993 + return NS_OK; 1.994 + } 1.995 + 1.996 + case eSendingMetadataForRead: { 1.997 + MOZ_ASSERT(NS_IsMainThread()); 1.998 + MOZ_ASSERT(mOpenMode == eOpenForRead); 1.999 + 1.1000 + mState = eWaitingToOpenCacheFileForRead; 1.1001 + OnOpenMetadataForRead(mMetadata); 1.1002 + return NS_OK; 1.1003 + } 1.1004 + 1.1005 + case eReadyToOpenCacheFileForRead: { 1.1006 + AssertIsOnIOThread(); 1.1007 + MOZ_ASSERT(mOpenMode == eOpenForRead); 1.1008 + 1.1009 + rv = OpenCacheFileForRead(); 1.1010 + if (NS_FAILED(rv)) { 1.1011 + Fail(); 1.1012 + return NS_OK; 1.1013 + } 1.1014 + 1.1015 + mState = eSendingCacheFile; 1.1016 + NS_DispatchToMainThread(this); 1.1017 + return NS_OK; 1.1018 + } 1.1019 + 1.1020 + case eSendingCacheFile: { 1.1021 + MOZ_ASSERT(NS_IsMainThread()); 1.1022 + 1.1023 + mState = eOpened; 1.1024 + OnOpenCacheFile(); 1.1025 + return NS_OK; 1.1026 + } 1.1027 + 1.1028 + case eFailing: { 1.1029 + MOZ_ASSERT(NS_IsMainThread()); 1.1030 + 1.1031 + mState = eFinished; 1.1032 + OnFailure(); 1.1033 + return NS_OK; 1.1034 + } 1.1035 + 1.1036 + case eClosing: { 1.1037 + MOZ_ASSERT(NS_IsMainThread()); 1.1038 + 1.1039 + mState = eFinished; 1.1040 + OnClose(); 1.1041 + return NS_OK; 1.1042 + } 1.1043 + 1.1044 + case eWaitingToOpenCacheFileForRead: 1.1045 + case eOpened: 1.1046 + case eFinished: { 1.1047 + MOZ_ASSUME_UNREACHABLE("Shouldn't Run() in this state"); 1.1048 + } 1.1049 + } 1.1050 + 1.1051 + MOZ_ASSUME_UNREACHABLE("Corrupt state"); 1.1052 + return NS_OK; 1.1053 +} 1.1054 + 1.1055 +bool 1.1056 +FindHashMatch(const Metadata& aMetadata, const ReadParams& aReadParams, 1.1057 + unsigned* aModuleIndex) 1.1058 +{ 1.1059 + // Perform a fast hash of the first sNumFastHashChars chars. Each cache entry 1.1060 + // also stores an mFastHash of its first sNumFastHashChars so this gives us a 1.1061 + // fast way to probabilistically determine whether we have a cache hit. We 1.1062 + // still do a full hash of all the chars before returning the cache file to 1.1063 + // the engine to avoid penalizing the case where there are multiple cached 1.1064 + // asm.js modules where the first sNumFastHashChars are the same. The 1.1065 + // mFullHash of each cache entry can have a different mNumChars so the fast 1.1066 + // hash allows us to avoid performing up to Metadata::kNumEntries separate 1.1067 + // full hashes. 1.1068 + uint32_t numChars = aReadParams.mLimit - aReadParams.mBegin; 1.1069 + MOZ_ASSERT(numChars > sNumFastHashChars); 1.1070 + uint32_t fastHash = HashString(aReadParams.mBegin, sNumFastHashChars); 1.1071 + 1.1072 + for (unsigned i = 0; i < Metadata::kNumEntries ; i++) { 1.1073 + // Compare the "fast hash" first to see whether it is worthwhile to 1.1074 + // hash all the chars. 1.1075 + Metadata::Entry entry = aMetadata.mEntries[i]; 1.1076 + if (entry.mFastHash != fastHash) { 1.1077 + continue; 1.1078 + } 1.1079 + 1.1080 + // Assuming we have enough characters, hash all the chars it would take 1.1081 + // to match this cache entry and compare to the cache entry. If we get a 1.1082 + // hit we'll still do a full source match later (in the JS engine), but 1.1083 + // the full hash match means this is probably the cache entry we want. 1.1084 + if (numChars < entry.mNumChars) { 1.1085 + continue; 1.1086 + } 1.1087 + uint32_t fullHash = HashString(aReadParams.mBegin, entry.mNumChars); 1.1088 + if (entry.mFullHash != fullHash) { 1.1089 + continue; 1.1090 + } 1.1091 + 1.1092 + *aModuleIndex = entry.mModuleIndex; 1.1093 + return true; 1.1094 + } 1.1095 + 1.1096 + return false; 1.1097 +} 1.1098 + 1.1099 +// A runnable that executes for a cache access originating in the main process. 1.1100 +class SingleProcessRunnable MOZ_FINAL : public File, 1.1101 + private MainProcessRunnable 1.1102 +{ 1.1103 +public: 1.1104 + // In the single-process case, the calling JS compilation thread holds the 1.1105 + // nsIPrincipal alive indirectly (via the global object -> compartment -> 1.1106 + // principal) so we don't have to ref-count it here. This is fortunate since 1.1107 + // we are off the main thread and nsIPrincipals can only be ref-counted on 1.1108 + // the main thread. 1.1109 + SingleProcessRunnable(nsIPrincipal* aPrincipal, 1.1110 + OpenMode aOpenMode, 1.1111 + WriteParams aWriteParams, 1.1112 + ReadParams aReadParams) 1.1113 + : MainProcessRunnable(aPrincipal, aOpenMode, aWriteParams), 1.1114 + mReadParams(aReadParams) 1.1115 + { 1.1116 + MOZ_ASSERT(IsMainProcess()); 1.1117 + MOZ_ASSERT(!NS_IsMainThread()); 1.1118 + MOZ_COUNT_CTOR(SingleProcessRunnable); 1.1119 + } 1.1120 + 1.1121 + ~SingleProcessRunnable() 1.1122 + { 1.1123 + MOZ_COUNT_DTOR(SingleProcessRunnable); 1.1124 + } 1.1125 + 1.1126 +private: 1.1127 + void 1.1128 + OnOpenMetadataForRead(const Metadata& aMetadata) MOZ_OVERRIDE 1.1129 + { 1.1130 + uint32_t moduleIndex; 1.1131 + if (FindHashMatch(aMetadata, mReadParams, &moduleIndex)) { 1.1132 + MainProcessRunnable::OpenForRead(moduleIndex); 1.1133 + } else { 1.1134 + MainProcessRunnable::CacheMiss(); 1.1135 + } 1.1136 + } 1.1137 + 1.1138 + void 1.1139 + OnOpenCacheFile() MOZ_OVERRIDE 1.1140 + { 1.1141 + File::OnOpen(); 1.1142 + } 1.1143 + 1.1144 + void 1.1145 + Close() MOZ_OVERRIDE MOZ_FINAL 1.1146 + { 1.1147 + MainProcessRunnable::Close(); 1.1148 + } 1.1149 + 1.1150 + void 1.1151 + OnFailure() MOZ_OVERRIDE 1.1152 + { 1.1153 + MainProcessRunnable::OnFailure(); 1.1154 + File::OnFailure(); 1.1155 + } 1.1156 + 1.1157 + void 1.1158 + OnClose() MOZ_OVERRIDE MOZ_FINAL 1.1159 + { 1.1160 + MainProcessRunnable::OnClose(); 1.1161 + File::OnClose(); 1.1162 + } 1.1163 + 1.1164 + // Avoid MSVC 'dominance' warning by having clear Run() override. 1.1165 + NS_IMETHODIMP 1.1166 + Run() MOZ_OVERRIDE 1.1167 + { 1.1168 + return MainProcessRunnable::Run(); 1.1169 + } 1.1170 + 1.1171 + ReadParams mReadParams; 1.1172 +}; 1.1173 + 1.1174 +// A runnable that executes in a parent process for a cache access originating 1.1175 +// in the content process. This runnable gets registered as an IPDL subprotocol 1.1176 +// actor so that it can communicate with the corresponding ChildProcessRunnable. 1.1177 +class ParentProcessRunnable MOZ_FINAL : public PAsmJSCacheEntryParent, 1.1178 + public MainProcessRunnable 1.1179 +{ 1.1180 +public: 1.1181 + // The given principal comes from an IPC::Principal which will be dec-refed 1.1182 + // at the end of the message, so we must ref-count it here. Fortunately, we 1.1183 + // are on the main thread (where PContent messages are delivered). 1.1184 + ParentProcessRunnable(nsIPrincipal* aPrincipal, 1.1185 + OpenMode aOpenMode, 1.1186 + WriteParams aWriteParams) 1.1187 + : MainProcessRunnable(aPrincipal, aOpenMode, aWriteParams), 1.1188 + mPrincipalHolder(aPrincipal), 1.1189 + mActorDestroyed(false), 1.1190 + mOpened(false), 1.1191 + mFinished(false) 1.1192 + { 1.1193 + MOZ_ASSERT(IsMainProcess()); 1.1194 + MOZ_ASSERT(NS_IsMainThread()); 1.1195 + MOZ_COUNT_CTOR(ParentProcessRunnable); 1.1196 + } 1.1197 + 1.1198 +private: 1.1199 + ~ParentProcessRunnable() 1.1200 + { 1.1201 + MOZ_ASSERT(!mPrincipalHolder, "Should have already been released"); 1.1202 + MOZ_ASSERT(mActorDestroyed); 1.1203 + MOZ_ASSERT(mFinished); 1.1204 + MOZ_COUNT_DTOR(ParentProcessRunnable); 1.1205 + } 1.1206 + 1.1207 + bool 1.1208 + Recv__delete__() MOZ_OVERRIDE 1.1209 + { 1.1210 + MOZ_ASSERT(!mFinished); 1.1211 + mFinished = true; 1.1212 + 1.1213 + if (mOpened) { 1.1214 + MainProcessRunnable::Close(); 1.1215 + } else { 1.1216 + MainProcessRunnable::Fail(); 1.1217 + } 1.1218 + 1.1219 + return true; 1.1220 + } 1.1221 + 1.1222 + void 1.1223 + ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE 1.1224 + { 1.1225 + MOZ_ASSERT(!mActorDestroyed); 1.1226 + mActorDestroyed = true; 1.1227 + 1.1228 + // Assume ActorDestroy can happen at any time, so probe the current state to 1.1229 + // determine what needs to happen. 1.1230 + 1.1231 + if (mFinished) { 1.1232 + return; 1.1233 + } 1.1234 + 1.1235 + mFinished = true; 1.1236 + 1.1237 + if (mOpened) { 1.1238 + MainProcessRunnable::Close(); 1.1239 + } else { 1.1240 + MainProcessRunnable::Fail(); 1.1241 + } 1.1242 + } 1.1243 + 1.1244 + void 1.1245 + OnOpenMetadataForRead(const Metadata& aMetadata) MOZ_OVERRIDE 1.1246 + { 1.1247 + MOZ_ASSERT(NS_IsMainThread()); 1.1248 + 1.1249 + if (!SendOnOpenMetadataForRead(aMetadata)) { 1.1250 + unused << Send__delete__(this); 1.1251 + } 1.1252 + } 1.1253 + 1.1254 + bool 1.1255 + RecvSelectCacheFileToRead(const uint32_t& aModuleIndex) MOZ_OVERRIDE 1.1256 + { 1.1257 + MainProcessRunnable::OpenForRead(aModuleIndex); 1.1258 + return true; 1.1259 + } 1.1260 + 1.1261 + bool 1.1262 + RecvCacheMiss() MOZ_OVERRIDE 1.1263 + { 1.1264 + MainProcessRunnable::CacheMiss(); 1.1265 + return true; 1.1266 + } 1.1267 + 1.1268 + void 1.1269 + OnOpenCacheFile() MOZ_OVERRIDE 1.1270 + { 1.1271 + MOZ_ASSERT(NS_IsMainThread()); 1.1272 + 1.1273 + MOZ_ASSERT(!mOpened); 1.1274 + mOpened = true; 1.1275 + 1.1276 + FileDescriptor::PlatformHandleType handle = 1.1277 + FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFileDesc)); 1.1278 + if (!SendOnOpenCacheFile(mFileSize, handle)) { 1.1279 + unused << Send__delete__(this); 1.1280 + } 1.1281 + } 1.1282 + 1.1283 + void 1.1284 + OnClose() MOZ_OVERRIDE MOZ_FINAL 1.1285 + { 1.1286 + MOZ_ASSERT(NS_IsMainThread()); 1.1287 + MOZ_ASSERT(mOpened); 1.1288 + 1.1289 + mFinished = true; 1.1290 + 1.1291 + MainProcessRunnable::OnClose(); 1.1292 + 1.1293 + MOZ_ASSERT(mActorDestroyed); 1.1294 + 1.1295 + mPrincipalHolder = nullptr; 1.1296 + } 1.1297 + 1.1298 + void 1.1299 + OnFailure() MOZ_OVERRIDE 1.1300 + { 1.1301 + MOZ_ASSERT(NS_IsMainThread()); 1.1302 + MOZ_ASSERT(!mOpened); 1.1303 + 1.1304 + mFinished = true; 1.1305 + 1.1306 + MainProcessRunnable::OnFailure(); 1.1307 + 1.1308 + if (!mActorDestroyed) { 1.1309 + unused << Send__delete__(this); 1.1310 + } 1.1311 + 1.1312 + mPrincipalHolder = nullptr; 1.1313 + } 1.1314 + 1.1315 + nsCOMPtr<nsIPrincipal> mPrincipalHolder; 1.1316 + bool mActorDestroyed; 1.1317 + bool mOpened; 1.1318 + bool mFinished; 1.1319 +}; 1.1320 + 1.1321 +} // unnamed namespace 1.1322 + 1.1323 +PAsmJSCacheEntryParent* 1.1324 +AllocEntryParent(OpenMode aOpenMode, 1.1325 + WriteParams aWriteParams, 1.1326 + nsIPrincipal* aPrincipal) 1.1327 +{ 1.1328 + ParentProcessRunnable* runnable = 1.1329 + new ParentProcessRunnable(aPrincipal, aOpenMode, aWriteParams); 1.1330 + 1.1331 + // AddRef to keep the runnable alive until DeallocEntryParent. 1.1332 + runnable->AddRef(); 1.1333 + 1.1334 + nsresult rv = NS_DispatchToMainThread(runnable); 1.1335 + NS_ENSURE_SUCCESS(rv, nullptr); 1.1336 + 1.1337 + return runnable; 1.1338 +} 1.1339 + 1.1340 +void 1.1341 +DeallocEntryParent(PAsmJSCacheEntryParent* aActor) 1.1342 +{ 1.1343 + // Match the AddRef in AllocEntryParent. 1.1344 + static_cast<ParentProcessRunnable*>(aActor)->Release(); 1.1345 +} 1.1346 + 1.1347 +namespace { 1.1348 + 1.1349 +class ChildProcessRunnable MOZ_FINAL : public File, 1.1350 + public PAsmJSCacheEntryChild 1.1351 +{ 1.1352 +public: 1.1353 + NS_DECL_NSIRUNNABLE 1.1354 + 1.1355 + // In the single-process case, the calling JS compilation thread holds the 1.1356 + // nsIPrincipal alive indirectly (via the global object -> compartment -> 1.1357 + // principal) so we don't have to ref-count it here. This is fortunate since 1.1358 + // we are off the main thread and nsIPrincipals can only be ref-counted on 1.1359 + // the main thread. 1.1360 + ChildProcessRunnable(nsIPrincipal* aPrincipal, 1.1361 + OpenMode aOpenMode, 1.1362 + WriteParams aWriteParams, 1.1363 + ReadParams aReadParams) 1.1364 + : mPrincipal(aPrincipal), 1.1365 + mOpenMode(aOpenMode), 1.1366 + mWriteParams(aWriteParams), 1.1367 + mReadParams(aReadParams), 1.1368 + mActorDestroyed(false), 1.1369 + mState(eInitial) 1.1370 + { 1.1371 + MOZ_ASSERT(!IsMainProcess()); 1.1372 + MOZ_ASSERT(!NS_IsMainThread()); 1.1373 + MOZ_COUNT_CTOR(ChildProcessRunnable); 1.1374 + } 1.1375 + 1.1376 + ~ChildProcessRunnable() 1.1377 + { 1.1378 + MOZ_ASSERT(mState == eFinished); 1.1379 + MOZ_ASSERT(mActorDestroyed); 1.1380 + MOZ_COUNT_DTOR(ChildProcessRunnable); 1.1381 + } 1.1382 + 1.1383 +private: 1.1384 + bool 1.1385 + RecvOnOpenMetadataForRead(const Metadata& aMetadata) MOZ_OVERRIDE 1.1386 + { 1.1387 + MOZ_ASSERT(NS_IsMainThread()); 1.1388 + MOZ_ASSERT(mState == eOpening); 1.1389 + 1.1390 + uint32_t moduleIndex; 1.1391 + if (FindHashMatch(aMetadata, mReadParams, &moduleIndex)) { 1.1392 + return SendSelectCacheFileToRead(moduleIndex); 1.1393 + } 1.1394 + 1.1395 + return SendCacheMiss(); 1.1396 + } 1.1397 + 1.1398 + bool 1.1399 + RecvOnOpenCacheFile(const int64_t& aFileSize, 1.1400 + const FileDescriptor& aFileDesc) MOZ_OVERRIDE 1.1401 + { 1.1402 + MOZ_ASSERT(NS_IsMainThread()); 1.1403 + MOZ_ASSERT(mState == eOpening); 1.1404 + 1.1405 + mFileSize = aFileSize; 1.1406 + 1.1407 + mFileDesc = PR_ImportFile(PROsfd(aFileDesc.PlatformHandle())); 1.1408 + if (!mFileDesc) { 1.1409 + return false; 1.1410 + } 1.1411 + 1.1412 + mState = eOpened; 1.1413 + File::OnOpen(); 1.1414 + return true; 1.1415 + } 1.1416 + 1.1417 + bool 1.1418 + Recv__delete__() MOZ_OVERRIDE 1.1419 + { 1.1420 + MOZ_ASSERT(NS_IsMainThread()); 1.1421 + MOZ_ASSERT(mState == eOpening); 1.1422 + 1.1423 + Fail(); 1.1424 + return true; 1.1425 + } 1.1426 + 1.1427 + void 1.1428 + ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE 1.1429 + { 1.1430 + MOZ_ASSERT(NS_IsMainThread()); 1.1431 + mActorDestroyed = true; 1.1432 + } 1.1433 + 1.1434 + void 1.1435 + Close() MOZ_OVERRIDE MOZ_FINAL 1.1436 + { 1.1437 + MOZ_ASSERT(mState == eOpened); 1.1438 + 1.1439 + mState = eClosing; 1.1440 + NS_DispatchToMainThread(this); 1.1441 + } 1.1442 + 1.1443 +private: 1.1444 + void 1.1445 + Fail() 1.1446 + { 1.1447 + MOZ_ASSERT(NS_IsMainThread()); 1.1448 + MOZ_ASSERT(mState == eInitial || mState == eOpening); 1.1449 + 1.1450 + mState = eFinished; 1.1451 + File::OnFailure(); 1.1452 + } 1.1453 + 1.1454 + nsIPrincipal* const mPrincipal; 1.1455 + const OpenMode mOpenMode; 1.1456 + WriteParams mWriteParams; 1.1457 + ReadParams mReadParams; 1.1458 + bool mActorDestroyed; 1.1459 + 1.1460 + enum State { 1.1461 + eInitial, // Just created, waiting to dispatched to the main thread 1.1462 + eOpening, // Waiting for the parent process to respond 1.1463 + eOpened, // Parent process opened the entry and sent it back 1.1464 + eClosing, // Waiting to be dispatched to the main thread to Send__delete__ 1.1465 + eFinished // Terminal state 1.1466 + }; 1.1467 + State mState; 1.1468 +}; 1.1469 + 1.1470 +NS_IMETHODIMP 1.1471 +ChildProcessRunnable::Run() 1.1472 +{ 1.1473 + switch (mState) { 1.1474 + case eInitial: { 1.1475 + MOZ_ASSERT(NS_IsMainThread()); 1.1476 + 1.1477 + // AddRef to keep this runnable alive until IPDL deallocates the 1.1478 + // subprotocol (DeallocEntryChild). 1.1479 + AddRef(); 1.1480 + 1.1481 + if (!ContentChild::GetSingleton()->SendPAsmJSCacheEntryConstructor( 1.1482 + this, mOpenMode, mWriteParams, IPC::Principal(mPrincipal))) 1.1483 + { 1.1484 + // On failure, undo the AddRef (since DeallocEntryChild will not be 1.1485 + // called) and unblock the parsing thread with a failure. The main 1.1486 + // thread event loop still holds an outstanding ref which will keep 1.1487 + // 'this' alive until returning to the event loop. 1.1488 + Release(); 1.1489 + 1.1490 + Fail(); 1.1491 + return NS_OK; 1.1492 + } 1.1493 + 1.1494 + mState = eOpening; 1.1495 + return NS_OK; 1.1496 + } 1.1497 + 1.1498 + case eClosing: { 1.1499 + MOZ_ASSERT(NS_IsMainThread()); 1.1500 + 1.1501 + // Per FileDescriptorHolder::Finish()'s comment, call before 1.1502 + // AllowNextSynchronizedOp (which happens in the parent upon receipt of 1.1503 + // the Send__delete__ message). 1.1504 + File::OnClose(); 1.1505 + 1.1506 + if (!mActorDestroyed) { 1.1507 + unused << Send__delete__(this); 1.1508 + } 1.1509 + 1.1510 + mState = eFinished; 1.1511 + return NS_OK; 1.1512 + } 1.1513 + 1.1514 + case eOpening: 1.1515 + case eOpened: 1.1516 + case eFinished: { 1.1517 + MOZ_ASSUME_UNREACHABLE("Shouldn't Run() in this state"); 1.1518 + } 1.1519 + } 1.1520 + 1.1521 + MOZ_ASSUME_UNREACHABLE("Corrupt state"); 1.1522 + return NS_OK; 1.1523 +} 1.1524 + 1.1525 +} // unnamed namespace 1.1526 + 1.1527 +void 1.1528 +DeallocEntryChild(PAsmJSCacheEntryChild* aActor) 1.1529 +{ 1.1530 + // Match the AddRef before SendPAsmJSCacheEntryConstructor. 1.1531 + static_cast<ChildProcessRunnable*>(aActor)->Release(); 1.1532 +} 1.1533 + 1.1534 +namespace { 1.1535 + 1.1536 +bool 1.1537 +OpenFile(nsIPrincipal* aPrincipal, 1.1538 + OpenMode aOpenMode, 1.1539 + WriteParams aWriteParams, 1.1540 + ReadParams aReadParams, 1.1541 + File::AutoClose* aFile) 1.1542 +{ 1.1543 + MOZ_ASSERT_IF(aOpenMode == eOpenForRead, aWriteParams.mSize == 0); 1.1544 + MOZ_ASSERT_IF(aOpenMode == eOpenForWrite, aReadParams.mBegin == nullptr); 1.1545 + 1.1546 + // There are three reasons we don't attempt caching from the main thread: 1.1547 + // 1. In the parent process: QuotaManager::WaitForOpenAllowed prevents 1.1548 + // synchronous waiting on the main thread requiring a runnable to be 1.1549 + // dispatched to the main thread. 1.1550 + // 2. In the child process: the IPDL PContent messages we need to 1.1551 + // synchronously wait on are dispatched to the main thread. 1.1552 + // 3. While a cache lookup *should* be much faster than compilation, IO 1.1553 + // operations can be unpredictably slow and we'd like to avoid the 1.1554 + // occasional janks on the main thread. 1.1555 + // We could use a nested event loop to address 1 and 2, but we're potentially 1.1556 + // in the middle of running JS (eval()) and nested event loops can be 1.1557 + // semantically observable. 1.1558 + if (NS_IsMainThread()) { 1.1559 + return false; 1.1560 + } 1.1561 + 1.1562 + // If we are in a child process, we need to synchronously call into the 1.1563 + // parent process to open the file and interact with the QuotaManager. The 1.1564 + // child can then map the file into its address space to perform I/O. 1.1565 + nsRefPtr<File> file; 1.1566 + if (IsMainProcess()) { 1.1567 + file = new SingleProcessRunnable(aPrincipal, aOpenMode, aWriteParams, 1.1568 + aReadParams); 1.1569 + } else { 1.1570 + file = new ChildProcessRunnable(aPrincipal, aOpenMode, aWriteParams, 1.1571 + aReadParams); 1.1572 + } 1.1573 + 1.1574 + if (!file->BlockUntilOpen(aFile)) { 1.1575 + return false; 1.1576 + } 1.1577 + 1.1578 + return file->MapMemory(aOpenMode); 1.1579 +} 1.1580 + 1.1581 +} // anonymous namespace 1.1582 + 1.1583 +typedef uint32_t AsmJSCookieType; 1.1584 +static const uint32_t sAsmJSCookie = 0x600d600d; 1.1585 + 1.1586 +bool 1.1587 +OpenEntryForRead(nsIPrincipal* aPrincipal, 1.1588 + const jschar* aBegin, 1.1589 + const jschar* aLimit, 1.1590 + size_t* aSize, 1.1591 + const uint8_t** aMemory, 1.1592 + intptr_t* aFile) 1.1593 +{ 1.1594 + if (size_t(aLimit - aBegin) < sMinCachedModuleLength) { 1.1595 + return false; 1.1596 + } 1.1597 + 1.1598 + ReadParams readParams; 1.1599 + readParams.mBegin = aBegin; 1.1600 + readParams.mLimit = aLimit; 1.1601 + 1.1602 + File::AutoClose file; 1.1603 + WriteParams notAWrite; 1.1604 + if (!OpenFile(aPrincipal, eOpenForRead, notAWrite, readParams, &file)) { 1.1605 + return false; 1.1606 + } 1.1607 + 1.1608 + // Although we trust that the stored cache files have not been arbitrarily 1.1609 + // corrupted, it is possible that a previous execution aborted in the middle 1.1610 + // of writing a cache file (crash, OOM-killer, etc). To protect against 1.1611 + // partially-written cache files, we use the following scheme: 1.1612 + // - Allocate an extra word at the beginning of every cache file which 1.1613 + // starts out 0 (OpenFile opens with PR_TRUNCATE). 1.1614 + // - After the asm.js serialization is complete, PR_SyncMemMap to write 1.1615 + // everything to disk and then store a non-zero value (sAsmJSCookie) 1.1616 + // in the first word. 1.1617 + // - When attempting to read a cache file, check whether the first word is 1.1618 + // sAsmJSCookie. 1.1619 + if (file->FileSize() < sizeof(AsmJSCookieType) || 1.1620 + *(AsmJSCookieType*)file->MappedMemory() != sAsmJSCookie) { 1.1621 + return false; 1.1622 + } 1.1623 + 1.1624 + *aSize = file->FileSize() - sizeof(AsmJSCookieType); 1.1625 + *aMemory = (uint8_t*) file->MappedMemory() + sizeof(AsmJSCookieType); 1.1626 + 1.1627 + // The caller guarnatees a call to CloseEntryForRead (on success or 1.1628 + // failure) at which point the file will be closed. 1.1629 + file.Forget(reinterpret_cast<File**>(aFile)); 1.1630 + return true; 1.1631 +} 1.1632 + 1.1633 +void 1.1634 +CloseEntryForRead(JS::Handle<JSObject*> global, 1.1635 + size_t aSize, 1.1636 + const uint8_t* aMemory, 1.1637 + intptr_t aFile) 1.1638 +{ 1.1639 + File::AutoClose file(reinterpret_cast<File*>(aFile)); 1.1640 + 1.1641 + MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == file->FileSize()); 1.1642 + MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) == file->MappedMemory()); 1.1643 +} 1.1644 + 1.1645 +bool 1.1646 +OpenEntryForWrite(nsIPrincipal* aPrincipal, 1.1647 + bool aInstalled, 1.1648 + const jschar* aBegin, 1.1649 + const jschar* aEnd, 1.1650 + size_t aSize, 1.1651 + uint8_t** aMemory, 1.1652 + intptr_t* aFile) 1.1653 +{ 1.1654 + if (size_t(aEnd - aBegin) < sMinCachedModuleLength) { 1.1655 + return false; 1.1656 + } 1.1657 + 1.1658 + // Add extra space for the AsmJSCookieType (see OpenEntryForRead). 1.1659 + aSize += sizeof(AsmJSCookieType); 1.1660 + 1.1661 + static_assert(sNumFastHashChars < sMinCachedModuleLength, "HashString safe"); 1.1662 + 1.1663 + WriteParams writeParams; 1.1664 + writeParams.mInstalled = aInstalled; 1.1665 + writeParams.mSize = aSize; 1.1666 + writeParams.mFastHash = HashString(aBegin, sNumFastHashChars); 1.1667 + writeParams.mNumChars = aEnd - aBegin; 1.1668 + writeParams.mFullHash = HashString(aBegin, writeParams.mNumChars); 1.1669 + 1.1670 + File::AutoClose file; 1.1671 + ReadParams notARead; 1.1672 + if (!OpenFile(aPrincipal, eOpenForWrite, writeParams, notARead, &file)) { 1.1673 + return false; 1.1674 + } 1.1675 + 1.1676 + // Strip off the AsmJSCookieType from the buffer returned to the caller, 1.1677 + // which expects a buffer of aSize, not a buffer of sizeWithCookie starting 1.1678 + // with a cookie. 1.1679 + *aMemory = (uint8_t*) file->MappedMemory() + sizeof(AsmJSCookieType); 1.1680 + 1.1681 + // The caller guarnatees a call to CloseEntryForWrite (on success or 1.1682 + // failure) at which point the file will be closed 1.1683 + file.Forget(reinterpret_cast<File**>(aFile)); 1.1684 + return true; 1.1685 +} 1.1686 + 1.1687 +void 1.1688 +CloseEntryForWrite(JS::Handle<JSObject*> global, 1.1689 + size_t aSize, 1.1690 + uint8_t* aMemory, 1.1691 + intptr_t aFile) 1.1692 +{ 1.1693 + File::AutoClose file(reinterpret_cast<File*>(aFile)); 1.1694 + 1.1695 + MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == file->FileSize()); 1.1696 + MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) == file->MappedMemory()); 1.1697 + 1.1698 + // Flush to disk before writing the cookie (see OpenEntryForRead). 1.1699 + if (PR_SyncMemMap(file->FileDesc(), 1.1700 + file->MappedMemory(), 1.1701 + file->FileSize()) == PR_SUCCESS) { 1.1702 + *(AsmJSCookieType*)file->MappedMemory() = sAsmJSCookie; 1.1703 + } 1.1704 +} 1.1705 + 1.1706 +bool 1.1707 +GetBuildId(JS::BuildIdCharVector* aBuildID) 1.1708 +{ 1.1709 + nsCOMPtr<nsIXULAppInfo> info = do_GetService("@mozilla.org/xre/app-info;1"); 1.1710 + if (!info) { 1.1711 + return false; 1.1712 + } 1.1713 + 1.1714 + nsCString buildID; 1.1715 + nsresult rv = info->GetPlatformBuildID(buildID); 1.1716 + NS_ENSURE_SUCCESS(rv, false); 1.1717 + 1.1718 + if (!aBuildID->resize(buildID.Length())) { 1.1719 + return false; 1.1720 + } 1.1721 + 1.1722 + for (size_t i = 0; i < buildID.Length(); i++) { 1.1723 + (*aBuildID)[i] = buildID[i]; 1.1724 + } 1.1725 + 1.1726 + return true; 1.1727 +} 1.1728 + 1.1729 +class Client : public quota::Client 1.1730 +{ 1.1731 +public: 1.1732 + NS_IMETHOD_(MozExternalRefCountType) 1.1733 + AddRef() MOZ_OVERRIDE; 1.1734 + 1.1735 + NS_IMETHOD_(MozExternalRefCountType) 1.1736 + Release() MOZ_OVERRIDE; 1.1737 + 1.1738 + virtual Type 1.1739 + GetType() MOZ_OVERRIDE 1.1740 + { 1.1741 + return ASMJS; 1.1742 + } 1.1743 + 1.1744 + virtual nsresult 1.1745 + InitOrigin(PersistenceType aPersistenceType, 1.1746 + const nsACString& aGroup, 1.1747 + const nsACString& aOrigin, 1.1748 + UsageInfo* aUsageInfo) MOZ_OVERRIDE 1.1749 + { 1.1750 + if (!aUsageInfo) { 1.1751 + return NS_OK; 1.1752 + } 1.1753 + return GetUsageForOrigin(aPersistenceType, aGroup, aOrigin, aUsageInfo); 1.1754 + } 1.1755 + 1.1756 + virtual nsresult 1.1757 + GetUsageForOrigin(PersistenceType aPersistenceType, 1.1758 + const nsACString& aGroup, 1.1759 + const nsACString& aOrigin, 1.1760 + UsageInfo* aUsageInfo) MOZ_OVERRIDE 1.1761 + { 1.1762 + QuotaManager* qm = QuotaManager::Get(); 1.1763 + MOZ_ASSERT(qm, "We were being called by the QuotaManager"); 1.1764 + 1.1765 + nsCOMPtr<nsIFile> directory; 1.1766 + nsresult rv = qm->GetDirectoryForOrigin(aPersistenceType, aOrigin, 1.1767 + getter_AddRefs(directory)); 1.1768 + NS_ENSURE_SUCCESS(rv, rv); 1.1769 + MOZ_ASSERT(directory, "We're here because the origin directory exists"); 1.1770 + 1.1771 + rv = directory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME)); 1.1772 + NS_ENSURE_SUCCESS(rv, rv); 1.1773 + 1.1774 + DebugOnly<bool> exists; 1.1775 + MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists); 1.1776 + 1.1777 + nsCOMPtr<nsISimpleEnumerator> entries; 1.1778 + rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); 1.1779 + NS_ENSURE_SUCCESS(rv, rv); 1.1780 + 1.1781 + bool hasMore; 1.1782 + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && 1.1783 + hasMore && !aUsageInfo->Canceled()) { 1.1784 + nsCOMPtr<nsISupports> entry; 1.1785 + rv = entries->GetNext(getter_AddRefs(entry)); 1.1786 + NS_ENSURE_SUCCESS(rv, rv); 1.1787 + 1.1788 + nsCOMPtr<nsIFile> file = do_QueryInterface(entry); 1.1789 + NS_ENSURE_TRUE(file, NS_NOINTERFACE); 1.1790 + 1.1791 + int64_t fileSize; 1.1792 + rv = file->GetFileSize(&fileSize); 1.1793 + NS_ENSURE_SUCCESS(rv, rv); 1.1794 + 1.1795 + MOZ_ASSERT(fileSize >= 0, "Negative size?!"); 1.1796 + 1.1797 + // Since the client is not explicitly storing files, append to database 1.1798 + // usage which represents implicit storage allocation. 1.1799 + aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); 1.1800 + } 1.1801 + NS_ENSURE_SUCCESS(rv, rv); 1.1802 + 1.1803 + return NS_OK; 1.1804 + } 1.1805 + 1.1806 + virtual void 1.1807 + OnOriginClearCompleted(PersistenceType aPersistenceType, 1.1808 + const OriginOrPatternString& aOriginOrPattern) 1.1809 + MOZ_OVERRIDE 1.1810 + { } 1.1811 + 1.1812 + virtual void 1.1813 + ReleaseIOThreadObjects() MOZ_OVERRIDE 1.1814 + { } 1.1815 + 1.1816 + virtual bool 1.1817 + IsFileServiceUtilized() MOZ_OVERRIDE 1.1818 + { 1.1819 + return false; 1.1820 + } 1.1821 + 1.1822 + virtual bool 1.1823 + IsTransactionServiceActivated() MOZ_OVERRIDE 1.1824 + { 1.1825 + return false; 1.1826 + } 1.1827 + 1.1828 + virtual void 1.1829 + WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages, 1.1830 + nsIRunnable* aCallback) MOZ_OVERRIDE 1.1831 + { 1.1832 + MOZ_ASSUME_UNREACHABLE("There are no storages"); 1.1833 + } 1.1834 + 1.1835 + virtual void 1.1836 + AbortTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE 1.1837 + { 1.1838 + MOZ_ASSUME_UNREACHABLE("There are no storages"); 1.1839 + } 1.1840 + 1.1841 + virtual bool 1.1842 + HasTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE 1.1843 + { 1.1844 + return false; 1.1845 + } 1.1846 + 1.1847 + virtual void 1.1848 + ShutdownTransactionService() MOZ_OVERRIDE 1.1849 + { } 1.1850 + 1.1851 +private: 1.1852 + nsAutoRefCnt mRefCnt; 1.1853 + NS_DECL_OWNINGTHREAD 1.1854 +}; 1.1855 + 1.1856 +NS_IMPL_ADDREF(asmjscache::Client) 1.1857 +NS_IMPL_RELEASE(asmjscache::Client) 1.1858 + 1.1859 +quota::Client* 1.1860 +CreateClient() 1.1861 +{ 1.1862 + return new Client(); 1.1863 +} 1.1864 + 1.1865 +} // namespace asmjscache 1.1866 +} // namespace dom 1.1867 +} // namespace mozilla 1.1868 + 1.1869 +namespace IPC { 1.1870 + 1.1871 +using mozilla::dom::asmjscache::Metadata; 1.1872 +using mozilla::dom::asmjscache::WriteParams; 1.1873 + 1.1874 +void 1.1875 +ParamTraits<Metadata>::Write(Message* aMsg, const paramType& aParam) 1.1876 +{ 1.1877 + for (unsigned i = 0; i < Metadata::kNumEntries; i++) { 1.1878 + const Metadata::Entry& entry = aParam.mEntries[i]; 1.1879 + WriteParam(aMsg, entry.mFastHash); 1.1880 + WriteParam(aMsg, entry.mNumChars); 1.1881 + WriteParam(aMsg, entry.mFullHash); 1.1882 + WriteParam(aMsg, entry.mModuleIndex); 1.1883 + } 1.1884 +} 1.1885 + 1.1886 +bool 1.1887 +ParamTraits<Metadata>::Read(const Message* aMsg, void** aIter, 1.1888 + paramType* aResult) 1.1889 +{ 1.1890 + for (unsigned i = 0; i < Metadata::kNumEntries; i++) { 1.1891 + Metadata::Entry& entry = aResult->mEntries[i]; 1.1892 + if (!ReadParam(aMsg, aIter, &entry.mFastHash) || 1.1893 + !ReadParam(aMsg, aIter, &entry.mNumChars) || 1.1894 + !ReadParam(aMsg, aIter, &entry.mFullHash) || 1.1895 + !ReadParam(aMsg, aIter, &entry.mModuleIndex)) 1.1896 + { 1.1897 + return false; 1.1898 + } 1.1899 + } 1.1900 + return true; 1.1901 +} 1.1902 + 1.1903 +void 1.1904 +ParamTraits<Metadata>::Log(const paramType& aParam, std::wstring* aLog) 1.1905 +{ 1.1906 + for (unsigned i = 0; i < Metadata::kNumEntries; i++) { 1.1907 + const Metadata::Entry& entry = aParam.mEntries[i]; 1.1908 + LogParam(entry.mFastHash, aLog); 1.1909 + LogParam(entry.mNumChars, aLog); 1.1910 + LogParam(entry.mFullHash, aLog); 1.1911 + LogParam(entry.mModuleIndex, aLog); 1.1912 + } 1.1913 +} 1.1914 + 1.1915 +void 1.1916 +ParamTraits<WriteParams>::Write(Message* aMsg, const paramType& aParam) 1.1917 +{ 1.1918 + WriteParam(aMsg, aParam.mSize); 1.1919 + WriteParam(aMsg, aParam.mFastHash); 1.1920 + WriteParam(aMsg, aParam.mNumChars); 1.1921 + WriteParam(aMsg, aParam.mFullHash); 1.1922 + WriteParam(aMsg, aParam.mInstalled); 1.1923 +} 1.1924 + 1.1925 +bool 1.1926 +ParamTraits<WriteParams>::Read(const Message* aMsg, void** aIter, 1.1927 + paramType* aResult) 1.1928 +{ 1.1929 + return ReadParam(aMsg, aIter, &aResult->mSize) && 1.1930 + ReadParam(aMsg, aIter, &aResult->mFastHash) && 1.1931 + ReadParam(aMsg, aIter, &aResult->mNumChars) && 1.1932 + ReadParam(aMsg, aIter, &aResult->mFullHash) && 1.1933 + ReadParam(aMsg, aIter, &aResult->mInstalled); 1.1934 +} 1.1935 + 1.1936 +void 1.1937 +ParamTraits<WriteParams>::Log(const paramType& aParam, std::wstring* aLog) 1.1938 +{ 1.1939 + LogParam(aParam.mSize, aLog); 1.1940 + LogParam(aParam.mFastHash, aLog); 1.1941 + LogParam(aParam.mNumChars, aLog); 1.1942 + LogParam(aParam.mFullHash, aLog); 1.1943 + LogParam(aParam.mInstalled, aLog); 1.1944 +} 1.1945 + 1.1946 +} // namespace IPC