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(¤tBuildId);
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(¤t)) && 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
|