summaryrefslogtreecommitdiffstats
path: root/dom/file/FileBlobImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/file/FileBlobImpl.cpp294
1 files changed, 294 insertions, 0 deletions
diff --git a/dom/file/FileBlobImpl.cpp b/dom/file/FileBlobImpl.cpp
new file mode 100644
index 0000000000..af80476e1a
--- /dev/null
+++ b/dom/file/FileBlobImpl.cpp
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FileBlobImpl.h"
+#include "BaseBlobImpl.h"
+#include "mozilla/SlicedInputStream.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRunnable.h"
+#include "nsCExternalHandlerService.h"
+#include "nsIFile.h"
+#include "nsIFileStreams.h"
+#include "nsIMIMEService.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla::dom {
+
+FileBlobImpl::FileBlobImpl(nsIFile* aFile)
+ : mMutex("FileBlobImpl::mMutex"),
+ mFile(aFile),
+ mSerialNumber(BaseBlobImpl::NextSerialNumber()),
+ mStart(0),
+ mFileId(-1),
+ mIsFile(true),
+ mWholeFile(true) {
+ MOZ_ASSERT(mFile, "must have file");
+ MOZ_ASSERT(XRE_IsParentProcess());
+ // Lazily get the content type and size
+ mContentType.SetIsVoid(true);
+ mMozFullPath.SetIsVoid(true);
+ mFile->GetLeafName(mName);
+}
+
+FileBlobImpl::FileBlobImpl(const nsAString& aName,
+ const nsAString& aContentType, uint64_t aLength,
+ nsIFile* aFile)
+ : mMutex("FileBlobImpl::mMutex"),
+ mFile(aFile),
+ mContentType(aContentType),
+ mName(aName),
+ mSerialNumber(BaseBlobImpl::NextSerialNumber()),
+ mStart(0),
+ mFileId(-1),
+ mLength(Some(aLength)),
+ mIsFile(true),
+ mWholeFile(true) {
+ MOZ_ASSERT(mFile, "must have file");
+ MOZ_ASSERT(XRE_IsParentProcess());
+ mMozFullPath.SetIsVoid(true);
+}
+
+FileBlobImpl::FileBlobImpl(const nsAString& aName,
+ const nsAString& aContentType, uint64_t aLength,
+ nsIFile* aFile, int64_t aLastModificationDate)
+ : mMutex("FileBlobImpl::mMutex"),
+ mFile(aFile),
+ mContentType(aContentType),
+ mName(aName),
+ mSerialNumber(BaseBlobImpl::NextSerialNumber()),
+ mStart(0),
+ mFileId(-1),
+ mLength(Some(aLength)),
+ mLastModified(Some(aLastModificationDate)),
+ mIsFile(true),
+ mWholeFile(true) {
+ MOZ_ASSERT(mFile, "must have file");
+ MOZ_ASSERT(XRE_IsParentProcess());
+ mMozFullPath.SetIsVoid(true);
+}
+
+FileBlobImpl::FileBlobImpl(nsIFile* aFile, const nsAString& aName,
+ const nsAString& aContentType)
+ : mMutex("FileBlobImpl::mMutex"),
+ mFile(aFile),
+ mContentType(aContentType),
+ mName(aName),
+ mSerialNumber(BaseBlobImpl::NextSerialNumber()),
+ mStart(0),
+ mFileId(-1),
+ mIsFile(true),
+ mWholeFile(true) {
+ MOZ_ASSERT(mFile, "must have file");
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (aContentType.IsEmpty()) {
+ // Lazily get the content type and size
+ mContentType.SetIsVoid(true);
+ }
+
+ mMozFullPath.SetIsVoid(true);
+}
+
+FileBlobImpl::FileBlobImpl(const FileBlobImpl* aOther, uint64_t aStart,
+ uint64_t aLength, const nsAString& aContentType)
+ : mMutex("FileBlobImpl::mMutex"),
+ mFile(aOther->mFile),
+ mContentType(aContentType),
+ mSerialNumber(BaseBlobImpl::NextSerialNumber()),
+ mStart(aOther->mStart + aStart),
+ mFileId(-1),
+ mLength(Some(aLength)),
+ mIsFile(false),
+ mWholeFile(false) {
+ MOZ_ASSERT(mFile, "must have file");
+ MOZ_ASSERT(XRE_IsParentProcess());
+ mMozFullPath = aOther->mMozFullPath;
+}
+
+already_AddRefed<BlobImpl> FileBlobImpl::CreateSlice(
+ uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
+ ErrorResult& aRv) const {
+ RefPtr<FileBlobImpl> impl =
+ new FileBlobImpl(this, aStart, aLength, aContentType);
+ return impl.forget();
+}
+
+void FileBlobImpl::GetMozFullPathInternal(nsAString& aFilename,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(mIsFile, "Should only be called on files");
+
+ MutexAutoLock lock(mMutex);
+
+ if (!mMozFullPath.IsVoid()) {
+ aFilename = mMozFullPath;
+ return;
+ }
+
+ aRv = mFile->GetPath(aFilename);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ mMozFullPath = aFilename;
+}
+
+uint64_t FileBlobImpl::GetSize(ErrorResult& aRv) {
+ MutexAutoLock lock(mMutex);
+
+ if (mLength.isNothing()) {
+ MOZ_ASSERT(mWholeFile,
+ "Should only use lazy size when using the whole file");
+ int64_t fileSize;
+ aRv = mFile->GetFileSize(&fileSize);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return 0;
+ }
+
+ if (fileSize < 0) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return 0;
+ }
+
+ mLength.emplace(fileSize);
+ }
+
+ return mLength.value();
+}
+
+class FileBlobImpl::GetTypeRunnable final : public WorkerMainThreadRunnable {
+ public:
+ GetTypeRunnable(WorkerPrivate* aWorkerPrivate, FileBlobImpl* aBlobImpl,
+ const MutexAutoLock& aProofOfLock)
+ : WorkerMainThreadRunnable(aWorkerPrivate, "FileBlobImpl :: GetType"_ns),
+ mBlobImpl(aBlobImpl),
+ mProofOfLock(aProofOfLock) {
+ MOZ_ASSERT(aBlobImpl);
+ aWorkerPrivate->AssertIsOnWorkerThread();
+ }
+
+ bool MainThreadRun() override {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsAutoString type;
+ mBlobImpl->GetTypeInternal(type, mProofOfLock);
+ return true;
+ }
+
+ private:
+ ~GetTypeRunnable() override = default;
+
+ RefPtr<FileBlobImpl> mBlobImpl;
+ const MutexAutoLock& mProofOfLock;
+};
+
+void FileBlobImpl::GetType(nsAString& aType) {
+ MutexAutoLock lock(mMutex);
+ GetTypeInternal(aType, lock);
+}
+
+void FileBlobImpl::GetTypeInternal(nsAString& aType,
+ const MutexAutoLock& aProofOfLock) {
+ aType.Truncate();
+
+ if (mContentType.IsVoid()) {
+ MOZ_ASSERT(mWholeFile,
+ "Should only use lazy ContentType when using the whole file");
+
+ if (!NS_IsMainThread()) {
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ if (!workerPrivate) {
+ // I have no idea in which thread this method is called. We cannot
+ // return any valid value.
+ return;
+ }
+
+ RefPtr<GetTypeRunnable> runnable =
+ new GetTypeRunnable(workerPrivate, this, aProofOfLock);
+
+ ErrorResult rv;
+ runnable->Dispatch(Canceling, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return;
+ }
+ } else {
+ nsresult rv;
+ nsCOMPtr<nsIMIMEService> mimeService =
+ do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsAutoCString mimeType;
+ rv = mimeService->GetTypeFromFile(mFile, mimeType);
+ if (NS_FAILED(rv)) {
+ mimeType.Truncate();
+ }
+
+ AppendUTF8toUTF16(mimeType, mContentType);
+ mContentType.SetIsVoid(false);
+ }
+ }
+
+ aType = mContentType;
+}
+
+void FileBlobImpl::GetBlobImplType(nsAString& aBlobImplType) const {
+ aBlobImplType = u"FileBlobImpl"_ns;
+}
+
+int64_t FileBlobImpl::GetLastModified(ErrorResult& aRv) {
+ MOZ_ASSERT(mIsFile, "Should only be called on files");
+
+ MutexAutoLock lock(mMutex);
+
+ if (mLastModified.isNothing()) {
+ PRTime msecs;
+ aRv = mFile->GetLastModifiedTime(&msecs);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return 0;
+ }
+
+ mLastModified.emplace(int64_t(msecs));
+ }
+
+ return mLastModified.value();
+}
+
+const uint32_t sFileStreamFlags =
+ nsIFileInputStream::CLOSE_ON_EOF | nsIFileInputStream::REOPEN_ON_REWIND |
+ nsIFileInputStream::DEFER_OPEN | nsIFileInputStream::SHARE_DELETE;
+
+void FileBlobImpl::CreateInputStream(nsIInputStream** aStream,
+ ErrorResult& aRv) const {
+ nsCOMPtr<nsIInputStream> stream;
+ aRv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1,
+ sFileStreamFlags);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ if (mWholeFile) {
+ stream.forget(aStream);
+ return;
+ }
+
+ MOZ_ASSERT(mLength.isSome());
+
+ RefPtr<SlicedInputStream> slicedInputStream =
+ new SlicedInputStream(stream.forget(), mStart, mLength.value());
+ slicedInputStream.forget(aStream);
+}
+
+bool FileBlobImpl::IsDirectory() const {
+ bool isDirectory = false;
+ if (mFile) {
+ mFile->IsDirectory(&isDirectory);
+ }
+ return isDirectory;
+}
+
+} // namespace mozilla::dom