diff options
Diffstat (limited to 'dom/ipc/FilePickerParent.cpp')
-rw-r--r-- | dom/ipc/FilePickerParent.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/dom/ipc/FilePickerParent.cpp b/dom/ipc/FilePickerParent.cpp new file mode 100644 index 0000000000..08f2a8fb6c --- /dev/null +++ b/dom/ipc/FilePickerParent.cpp @@ -0,0 +1,293 @@ +/* -*- 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 "FilePickerParent.h" +#include "nsComponentManagerUtils.h" +#include "nsNetCID.h" +#include "mozilla/dom/Document.h" +#include "nsIFile.h" +#include "nsISimpleEnumerator.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/FileBlobImpl.h" +#include "mozilla/dom/FileSystemSecurity.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/IPCBlobUtils.h" + +using mozilla::Unused; +using namespace mozilla::dom; + +NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback, + nsIFilePickerShownCallback); + +NS_IMETHODIMP +FilePickerParent::FilePickerShownCallback::Done( + nsIFilePicker::ResultCode aResult) { + if (mFilePickerParent) { + mFilePickerParent->Done(aResult); + } + return NS_OK; +} + +void FilePickerParent::FilePickerShownCallback::Destroy() { + mFilePickerParent = nullptr; +} + +FilePickerParent::~FilePickerParent() = default; + +// We run code in three places: +// 1. The main thread calls Dispatch() to start the runnable. +// 2. The stream transport thread stat()s the file in Run() and then dispatches +// the same runnable on the main thread. +// 3. The main thread sends the results over IPC. +FilePickerParent::IORunnable::IORunnable(FilePickerParent* aFPParent, + nsTArray<nsCOMPtr<nsIFile>>&& aFiles, + bool aIsDirectory) + : mozilla::Runnable("dom::FilePickerParent::IORunnable"), + mFilePickerParent(aFPParent), + mFiles(std::move(aFiles)), + mIsDirectory(aIsDirectory) { + MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1); +} + +bool FilePickerParent::IORunnable::Dispatch() { + MOZ_ASSERT(NS_IsMainThread()); + + mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + if (!mEventTarget) { + return false; + } + + nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); + return NS_SUCCEEDED(rv); +} + +NS_IMETHODIMP +FilePickerParent::IORunnable::Run() { + // If we're on the main thread, then that means we're done. Just send the + // results. + if (NS_IsMainThread()) { + if (mFilePickerParent) { + mFilePickerParent->SendFilesOrDirectories(mResults); + } + return NS_OK; + } + + // We're not on the main thread, so do the IO. + + for (uint32_t i = 0; i < mFiles.Length(); ++i) { + if (mIsDirectory) { + nsAutoString path; + nsresult rv = mFiles[i]->GetPath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + BlobImplOrString* data = mResults.AppendElement(); + data->mType = BlobImplOrString::eDirectoryPath; + data->mDirectoryPath = path; + continue; + } + + RefPtr<BlobImpl> blobImpl = new FileBlobImpl(mFiles[i]); + + ErrorResult error; + blobImpl->GetSize(error); + if (NS_WARN_IF(error.Failed())) { + error.SuppressException(); + continue; + } + + blobImpl->GetLastModified(error); + if (NS_WARN_IF(error.Failed())) { + error.SuppressException(); + continue; + } + + BlobImplOrString* data = mResults.AppendElement(); + data->mType = BlobImplOrString::eBlobImpl; + data->mBlobImpl = blobImpl; + } + + // Dispatch ourselves back on the main thread. + if (NS_FAILED(NS_DispatchToMainThread(this))) { + // It's hard to see how we can recover gracefully in this case. The child + // process is waiting for an IPC, but that can only happen on the main + // thread. + MOZ_CRASH(); + } + + return NS_OK; +} + +void FilePickerParent::IORunnable::Destroy() { mFilePickerParent = nullptr; } + +void FilePickerParent::SendFilesOrDirectories( + const nsTArray<BlobImplOrString>& aData) { + ContentParent* parent = BrowserParent::GetFrom(Manager())->Manager(); + + if (mMode == nsIFilePicker::modeGetFolder) { + MOZ_ASSERT(aData.Length() <= 1); + if (aData.IsEmpty()) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + + MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath); + + // Let's inform the security singleton about the given access of this tab on + // this directory path. + RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate(); + fss->GrantAccessToContentProcess(parent->ChildID(), + aData[0].mDirectoryPath); + + InputDirectory input; + input.directoryPath() = aData[0].mDirectoryPath; + Unused << Send__delete__(this, input, mResult); + return; + } + + nsTArray<IPCBlob> ipcBlobs; + + for (unsigned i = 0; i < aData.Length(); i++) { + IPCBlob ipcBlob; + + MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl); + nsresult rv = IPCBlobUtils::Serialize(aData[i].mBlobImpl, ipcBlob); + if (NS_WARN_IF(NS_FAILED(rv))) { + break; + } + + ipcBlobs.AppendElement(ipcBlob); + } + + InputBlobs inblobs; + inblobs.blobs() = std::move(ipcBlobs); + + Unused << Send__delete__(this, inblobs, mResult); +} + +void FilePickerParent::Done(nsIFilePicker::ResultCode aResult) { + mResult = aResult; + + if (mResult != nsIFilePicker::returnOK) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + + nsTArray<nsCOMPtr<nsIFile>> files; + if (mMode == nsIFilePicker::modeOpenMultiple) { + nsCOMPtr<nsISimpleEnumerator> iter; + NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter))); + + nsCOMPtr<nsISupports> supports; + bool loop = true; + while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) { + iter->GetNext(getter_AddRefs(supports)); + if (supports) { + nsCOMPtr<nsIFile> file = do_QueryInterface(supports); + MOZ_ASSERT(file); + files.AppendElement(file); + } + } + } else { + nsCOMPtr<nsIFile> file; + mFilePicker->GetFile(getter_AddRefs(file)); + if (file) { + files.AppendElement(file); + } + } + + if (files.IsEmpty()) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + + MOZ_ASSERT(!mRunnable); + mRunnable = new IORunnable(this, std::move(files), + mMode == nsIFilePicker::modeGetFolder); + + // Dispatch to background thread to do I/O: + if (!mRunnable->Dispatch()) { + Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); + } +} + +bool FilePickerParent::CreateFilePicker() { + mFilePicker = do_CreateInstance("@mozilla.org/filepicker;1"); + if (!mFilePicker) { + return false; + } + + Element* element = BrowserParent::GetFrom(Manager())->GetOwnerElement(); + if (!element) { + return false; + } + + nsCOMPtr<mozIDOMWindowProxy> window = element->OwnerDoc()->GetWindow(); + if (!window) { + return false; + } + + return NS_SUCCEEDED(mFilePicker->Init(window, mTitle, mMode)); +} + +mozilla::ipc::IPCResult FilePickerParent::RecvOpen( + const int16_t& aSelectedType, const bool& aAddToRecentDocs, + const nsString& aDefaultFile, const nsString& aDefaultExtension, + nsTArray<nsString>&& aFilters, nsTArray<nsString>&& aFilterNames, + nsTArray<nsString>&& aRawFilters, const nsString& aDisplayDirectory, + const nsString& aDisplaySpecialDirectory, const nsString& aOkButtonLabel, + const nsIFilePicker::CaptureTarget& aCapture) { + if (!CreateFilePicker()) { + Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); + return IPC_OK(); + } + + mFilePicker->SetAddToRecentDocs(aAddToRecentDocs); + + for (uint32_t i = 0; i < aFilters.Length(); ++i) { + mFilePicker->AppendFilter(aFilterNames[i], aFilters[i]); + } + + for (uint32_t i = 0; i < aRawFilters.Length(); ++i) { + mFilePicker->AppendRawFilter(aRawFilters[i]); + } + + mFilePicker->SetDefaultString(aDefaultFile); + mFilePicker->SetDefaultExtension(aDefaultExtension); + mFilePicker->SetFilterIndex(aSelectedType); + mFilePicker->SetOkButtonLabel(aOkButtonLabel); + mFilePicker->SetCapture(aCapture); + + if (!aDisplayDirectory.IsEmpty()) { + nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); + if (localFile) { + localFile->InitWithPath(aDisplayDirectory); + mFilePicker->SetDisplayDirectory(localFile); + } + } else if (!aDisplaySpecialDirectory.IsEmpty()) { + mFilePicker->SetDisplaySpecialDirectory(aDisplaySpecialDirectory); + } + + MOZ_ASSERT(!mCallback); + mCallback = new FilePickerShownCallback(this); + + mFilePicker->Open(mCallback); + return IPC_OK(); +} + +void FilePickerParent::ActorDestroy(ActorDestroyReason aWhy) { + if (mCallback) { + mCallback->Destroy(); + mCallback = nullptr; + } + if (mRunnable) { + mRunnable->Destroy(); + mRunnable = nullptr; + } +} |