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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FileBlobImpl.h"
8 #include "BaseBlobImpl.h"
9 #include "mozilla/SlicedInputStream.h"
10 #include "mozilla/dom/WorkerPrivate.h"
11 #include "mozilla/dom/WorkerRunnable.h"
12 #include "nsCExternalHandlerService.h"
14 #include "nsIFileStreams.h"
15 #include "nsIMIMEService.h"
16 #include "nsNetUtil.h"
17 #include "nsStreamUtils.h"
19 namespace mozilla::dom
{
21 FileBlobImpl::FileBlobImpl(nsIFile
* aFile
)
22 : mMutex("FileBlobImpl::mMutex"),
24 mSerialNumber(BaseBlobImpl::NextSerialNumber()),
29 MOZ_ASSERT(mFile
, "must have file");
30 MOZ_ASSERT(XRE_IsParentProcess());
31 // Lazily get the content type and size
32 mContentType
.SetIsVoid(true);
33 mMozFullPath
.SetIsVoid(true);
34 mFile
->GetLeafName(mName
);
37 FileBlobImpl::FileBlobImpl(const nsAString
& aName
,
38 const nsAString
& aContentType
, uint64_t aLength
,
40 : mMutex("FileBlobImpl::mMutex"),
42 mContentType(aContentType
),
44 mSerialNumber(BaseBlobImpl::NextSerialNumber()),
47 mLength(Some(aLength
)),
50 MOZ_ASSERT(mFile
, "must have file");
51 MOZ_ASSERT(XRE_IsParentProcess());
52 mMozFullPath
.SetIsVoid(true);
55 FileBlobImpl::FileBlobImpl(const nsAString
& aName
,
56 const nsAString
& aContentType
, uint64_t aLength
,
57 nsIFile
* aFile
, int64_t aLastModificationDate
)
58 : mMutex("FileBlobImpl::mMutex"),
60 mContentType(aContentType
),
62 mSerialNumber(BaseBlobImpl::NextSerialNumber()),
65 mLength(Some(aLength
)),
66 mLastModified(Some(aLastModificationDate
)),
69 MOZ_ASSERT(mFile
, "must have file");
70 MOZ_ASSERT(XRE_IsParentProcess());
71 mMozFullPath
.SetIsVoid(true);
74 FileBlobImpl::FileBlobImpl(nsIFile
* aFile
, const nsAString
& aName
,
75 const nsAString
& aContentType
)
76 : mMutex("FileBlobImpl::mMutex"),
78 mContentType(aContentType
),
80 mSerialNumber(BaseBlobImpl::NextSerialNumber()),
85 MOZ_ASSERT(mFile
, "must have file");
86 MOZ_ASSERT(XRE_IsParentProcess());
87 if (aContentType
.IsEmpty()) {
88 // Lazily get the content type and size
89 mContentType
.SetIsVoid(true);
92 mMozFullPath
.SetIsVoid(true);
95 FileBlobImpl::FileBlobImpl(const FileBlobImpl
* aOther
, uint64_t aStart
,
96 uint64_t aLength
, const nsAString
& aContentType
)
97 : mMutex("FileBlobImpl::mMutex"),
99 mContentType(aContentType
),
100 mSerialNumber(BaseBlobImpl::NextSerialNumber()),
101 mStart(aOther
->mStart
+ aStart
),
103 mLength(Some(aLength
)),
106 MOZ_ASSERT(mFile
, "must have file");
107 MOZ_ASSERT(XRE_IsParentProcess());
108 mMozFullPath
= aOther
->mMozFullPath
;
111 already_AddRefed
<BlobImpl
> FileBlobImpl::CreateSlice(
112 uint64_t aStart
, uint64_t aLength
, const nsAString
& aContentType
,
113 ErrorResult
& aRv
) const {
114 RefPtr
<FileBlobImpl
> impl
=
115 new FileBlobImpl(this, aStart
, aLength
, aContentType
);
116 return impl
.forget();
119 void FileBlobImpl::GetMozFullPathInternal(nsAString
& aFilename
,
121 MOZ_ASSERT(mIsFile
, "Should only be called on files");
123 MutexAutoLock
lock(mMutex
);
125 if (!mMozFullPath
.IsVoid()) {
126 aFilename
= mMozFullPath
;
130 aRv
= mFile
->GetPath(aFilename
);
131 if (NS_WARN_IF(aRv
.Failed())) {
135 mMozFullPath
= aFilename
;
138 uint64_t FileBlobImpl::GetSize(ErrorResult
& aRv
) {
139 MutexAutoLock
lock(mMutex
);
141 if (mLength
.isNothing()) {
142 MOZ_ASSERT(mWholeFile
,
143 "Should only use lazy size when using the whole file");
145 aRv
= mFile
->GetFileSize(&fileSize
);
146 if (NS_WARN_IF(aRv
.Failed())) {
151 aRv
.Throw(NS_ERROR_FAILURE
);
155 mLength
.emplace(fileSize
);
158 return mLength
.value();
161 class FileBlobImpl::GetTypeRunnable final
: public WorkerMainThreadRunnable
{
163 GetTypeRunnable(WorkerPrivate
* aWorkerPrivate
, FileBlobImpl
* aBlobImpl
,
164 const MutexAutoLock
& aProofOfLock
)
165 : WorkerMainThreadRunnable(aWorkerPrivate
, "FileBlobImpl :: GetType"_ns
),
166 mBlobImpl(aBlobImpl
),
167 mProofOfLock(aProofOfLock
) {
168 MOZ_ASSERT(aBlobImpl
);
169 aWorkerPrivate
->AssertIsOnWorkerThread();
172 bool MainThreadRun() override
{
173 MOZ_ASSERT(NS_IsMainThread());
176 mBlobImpl
->GetTypeInternal(type
, mProofOfLock
);
181 ~GetTypeRunnable() override
= default;
183 RefPtr
<FileBlobImpl
> mBlobImpl
;
184 const MutexAutoLock
& mProofOfLock
;
187 void FileBlobImpl::GetType(nsAString
& aType
) {
188 MutexAutoLock
lock(mMutex
);
189 GetTypeInternal(aType
, lock
);
192 void FileBlobImpl::GetTypeInternal(nsAString
& aType
,
193 const MutexAutoLock
& aProofOfLock
) {
196 if (mContentType
.IsVoid()) {
197 MOZ_ASSERT(mWholeFile
,
198 "Should only use lazy ContentType when using the whole file");
200 if (!NS_IsMainThread()) {
201 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
202 if (!workerPrivate
) {
203 // I have no idea in which thread this method is called. We cannot
204 // return any valid value.
208 RefPtr
<GetTypeRunnable
> runnable
=
209 new GetTypeRunnable(workerPrivate
, this, aProofOfLock
);
212 runnable
->Dispatch(Canceling
, rv
);
213 if (NS_WARN_IF(rv
.Failed())) {
214 rv
.SuppressException();
219 nsCOMPtr
<nsIMIMEService
> mimeService
=
220 do_GetService(NS_MIMESERVICE_CONTRACTID
, &rv
);
221 if (NS_WARN_IF(NS_FAILED(rv
))) {
225 nsAutoCString mimeType
;
226 rv
= mimeService
->GetTypeFromFile(mFile
, mimeType
);
231 AppendUTF8toUTF16(mimeType
, mContentType
);
232 mContentType
.SetIsVoid(false);
236 aType
= mContentType
;
239 void FileBlobImpl::GetBlobImplType(nsAString
& aBlobImplType
) const {
240 aBlobImplType
= u
"FileBlobImpl"_ns
;
243 int64_t FileBlobImpl::GetLastModified(ErrorResult
& aRv
) {
244 MOZ_ASSERT(mIsFile
, "Should only be called on files");
246 MutexAutoLock
lock(mMutex
);
248 if (mLastModified
.isNothing()) {
250 aRv
= mFile
->GetLastModifiedTime(&msecs
);
251 if (NS_WARN_IF(aRv
.Failed())) {
255 mLastModified
.emplace(int64_t(msecs
));
258 return mLastModified
.value();
261 const uint32_t sFileStreamFlags
=
262 nsIFileInputStream::CLOSE_ON_EOF
| nsIFileInputStream::REOPEN_ON_REWIND
|
263 nsIFileInputStream::DEFER_OPEN
| nsIFileInputStream::SHARE_DELETE
;
265 void FileBlobImpl::CreateInputStream(nsIInputStream
** aStream
,
266 ErrorResult
& aRv
) const {
267 nsCOMPtr
<nsIInputStream
> stream
;
268 aRv
= NS_NewLocalFileInputStream(getter_AddRefs(stream
), mFile
, -1, -1,
270 if (NS_WARN_IF(aRv
.Failed())) {
275 stream
.forget(aStream
);
279 MOZ_ASSERT(mLength
.isSome());
281 RefPtr
<SlicedInputStream
> slicedInputStream
=
282 new SlicedInputStream(stream
.forget(), mStart
, mLength
.value());
283 slicedInputStream
.forget(aStream
);
286 bool FileBlobImpl::IsDirectory() const {
287 bool isDirectory
= false;
289 mFile
->IsDirectory(&isDirectory
);
294 } // namespace mozilla::dom