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