Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
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
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "EncodedBufferCache.h"
8 : #include "prio.h"
9 : #include "nsAnonymousTemporaryFile.h"
10 : #include "mozilla/Monitor.h"
11 : #include "mozilla/dom/ContentChild.h"
12 : #include "mozilla/dom/File.h"
13 : #include "nsThreadUtils.h"
14 : #include "nsXULAppAPI.h"
15 :
16 : namespace mozilla {
17 :
18 : void
19 0 : EncodedBufferCache::AppendBuffer(nsTArray<uint8_t> & aBuf)
20 : {
21 0 : MOZ_ASSERT(!NS_IsMainThread());
22 :
23 0 : MutexAutoLock lock(mMutex);
24 0 : mDataSize += aBuf.Length();
25 :
26 0 : mEncodedBuffers.AppendElement()->SwapElements(aBuf);
27 :
28 0 : if (!mTempFileEnabled && mDataSize > mMaxMemoryStorage) {
29 : nsresult rv;
30 0 : PRFileDesc* tempFD = nullptr;
31 : {
32 : // Release the mMutex because of the sync dispatch to the main thread.
33 0 : MutexAutoUnlock unlock(mMutex);
34 0 : if (XRE_IsParentProcess()) {
35 : // In case we are in the parent process, do a synchronous I/O here to open a
36 : // temporary file.
37 0 : rv = NS_OpenAnonymousTemporaryFile(&tempFD);
38 : } else {
39 : // In case we are in the child process, we don't have access to open a file
40 : // directly due to sandbox restrictions, so we need to ask the parent process
41 : // to do that for us. In order to initiate the IPC, we need to first go to
42 : // the main thread. This is done by dispatching a runnable to the main thread.
43 : // From there, we start an asynchronous IPC, and we block the current thread
44 : // using a monitor while this async work is in progress. When we receive the
45 : // resulting file descriptor from the parent process, we notify the monitor
46 : // and unblock the current thread and continue.
47 : typedef dom::ContentChild::AnonymousTemporaryFileCallback
48 : AnonymousTemporaryFileCallback;
49 0 : bool done = false;
50 0 : Monitor monitor("EncodeBufferCache::AppendBuffer");
51 0 : RefPtr<dom::ContentChild> cc = dom::ContentChild::GetSingleton();
52 : nsCOMPtr<nsIRunnable> runnable =
53 0 : NewRunnableMethod<AnonymousTemporaryFileCallback>(
54 : "dom::ContentChild::AsyncOpenAnonymousTemporaryFile",
55 : cc,
56 : &dom::ContentChild::AsyncOpenAnonymousTemporaryFile,
57 0 : [&](PRFileDesc* aFile) {
58 0 : rv = aFile ? NS_OK : NS_ERROR_FAILURE;
59 0 : tempFD = aFile;
60 0 : MonitorAutoLock lock(monitor);
61 0 : done = true;
62 0 : lock.Notify();
63 0 : });
64 0 : MonitorAutoLock lock(monitor);
65 0 : rv = NS_DispatchToMainThread(runnable);
66 0 : if (NS_SUCCEEDED(rv)) {
67 0 : while (!done) {
68 0 : lock.Wait();
69 : }
70 : }
71 : }
72 : }
73 0 : if (!NS_FAILED(rv)) {
74 : // Check the mDataSize again since we release the mMutex before.
75 0 : if (mDataSize > mMaxMemoryStorage) {
76 0 : mFD = tempFD;
77 0 : mTempFileEnabled = true;
78 : } else {
79 : // Close the tempFD because the data had been taken during the
80 : // MutexAutoUnlock.
81 0 : PR_Close(tempFD);
82 : }
83 : }
84 : }
85 :
86 0 : if (mTempFileEnabled) {
87 : // has created temporary file, write buffer in it
88 0 : for (uint32_t i = 0; i < mEncodedBuffers.Length(); i++) {
89 0 : int32_t amount = PR_Write(mFD, mEncodedBuffers.ElementAt(i).Elements(), mEncodedBuffers.ElementAt(i).Length());
90 0 : if (amount < 0 || size_t(amount) < mEncodedBuffers.ElementAt(i).Length()) {
91 0 : NS_WARNING("Failed to write media cache block!");
92 : }
93 : }
94 0 : mEncodedBuffers.Clear();
95 : }
96 :
97 0 : }
98 :
99 : already_AddRefed<dom::Blob>
100 0 : EncodedBufferCache::ExtractBlob(nsISupports* aParent,
101 : const nsAString &aContentType)
102 : {
103 0 : MutexAutoLock lock(mMutex);
104 0 : RefPtr<dom::Blob> blob;
105 0 : if (mTempFileEnabled) {
106 : // generate new temporary file to write
107 0 : blob = dom::Blob::CreateTemporaryBlob(aParent, mFD, 0, mDataSize,
108 0 : aContentType);
109 : // fallback to memory blob
110 0 : mTempFileEnabled = false;
111 0 : mDataSize = 0;
112 0 : mFD = nullptr;
113 : } else {
114 0 : void* blobData = malloc(mDataSize);
115 0 : NS_ASSERTION(blobData, "out of memory!!");
116 :
117 0 : if (blobData) {
118 0 : for (uint32_t i = 0, offset = 0; i < mEncodedBuffers.Length(); i++) {
119 0 : memcpy((uint8_t*)blobData + offset, mEncodedBuffers.ElementAt(i).Elements(),
120 0 : mEncodedBuffers.ElementAt(i).Length());
121 0 : offset += mEncodedBuffers.ElementAt(i).Length();
122 : }
123 0 : blob = dom::Blob::CreateMemoryBlob(aParent, blobData, mDataSize,
124 0 : aContentType);
125 0 : mEncodedBuffers.Clear();
126 : } else
127 0 : return nullptr;
128 : }
129 0 : mDataSize = 0;
130 0 : return blob.forget();
131 : }
132 :
133 9 : } // namespace mozilla
|