no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / ipc / FilePickerParent.cpp
blobea65f79c236c114e69f1414fce002045a41bd0f0
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 "FilePickerParent.h"
8 #include "nsComponentManagerUtils.h"
9 #include "nsNetCID.h"
10 #include "mozilla/dom/Document.h"
11 #include "nsIFile.h"
12 #include "nsISimpleEnumerator.h"
13 #include "mozilla/Unused.h"
14 #include "mozilla/dom/BrowserParent.h"
15 #include "mozilla/dom/CanonicalBrowsingContext.h"
16 #include "mozilla/dom/ContentParent.h"
17 #include "mozilla/dom/Element.h"
18 #include "mozilla/dom/FileBlobImpl.h"
19 #include "mozilla/dom/FileSystemSecurity.h"
20 #include "mozilla/dom/IPCBlobUtils.h"
22 using mozilla::Unused;
23 using namespace mozilla::dom;
25 NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback,
26 nsIFilePickerShownCallback);
28 NS_IMETHODIMP
29 FilePickerParent::FilePickerShownCallback::Done(
30 nsIFilePicker::ResultCode aResult) {
31 if (mFilePickerParent) {
32 mFilePickerParent->Done(aResult);
34 return NS_OK;
37 void FilePickerParent::FilePickerShownCallback::Destroy() {
38 mFilePickerParent = nullptr;
41 FilePickerParent::~FilePickerParent() = default;
43 // We run code in three places:
44 // 1. The main thread calls Dispatch() to start the runnable.
45 // 2. The stream transport thread stat()s the file in Run() and then dispatches
46 // the same runnable on the main thread.
47 // 3. The main thread sends the results over IPC.
48 FilePickerParent::IORunnable::IORunnable(FilePickerParent* aFPParent,
49 nsTArray<nsCOMPtr<nsIFile>>&& aFiles,
50 bool aIsDirectory)
51 : mozilla::Runnable("dom::FilePickerParent::IORunnable"),
52 mFilePickerParent(aFPParent),
53 mFiles(std::move(aFiles)),
54 mIsDirectory(aIsDirectory) {
55 MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1);
58 bool FilePickerParent::IORunnable::Dispatch() {
59 MOZ_ASSERT(NS_IsMainThread());
61 mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
62 if (!mEventTarget) {
63 return false;
66 nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
67 return NS_SUCCEEDED(rv);
70 NS_IMETHODIMP
71 FilePickerParent::IORunnable::Run() {
72 // If we're on the main thread, then that means we're done. Just send the
73 // results.
74 if (NS_IsMainThread()) {
75 if (mFilePickerParent) {
76 mFilePickerParent->SendFilesOrDirectories(mResults);
78 return NS_OK;
81 // We're not on the main thread, so do the IO.
83 for (uint32_t i = 0; i < mFiles.Length(); ++i) {
84 if (mIsDirectory) {
85 nsAutoString path;
86 nsresult rv = mFiles[i]->GetPath(path);
87 if (NS_WARN_IF(NS_FAILED(rv))) {
88 continue;
91 BlobImplOrString* data = mResults.AppendElement();
92 data->mType = BlobImplOrString::eDirectoryPath;
93 data->mDirectoryPath = path;
94 continue;
97 RefPtr<BlobImpl> blobImpl = new FileBlobImpl(mFiles[i]);
99 ErrorResult error;
100 blobImpl->GetSize(error);
101 if (NS_WARN_IF(error.Failed())) {
102 error.SuppressException();
103 continue;
106 blobImpl->GetLastModified(error);
107 if (NS_WARN_IF(error.Failed())) {
108 error.SuppressException();
109 continue;
112 BlobImplOrString* data = mResults.AppendElement();
113 data->mType = BlobImplOrString::eBlobImpl;
114 data->mBlobImpl = blobImpl;
117 // Dispatch ourselves back on the main thread.
118 if (NS_FAILED(NS_DispatchToMainThread(this))) {
119 // It's hard to see how we can recover gracefully in this case. The child
120 // process is waiting for an IPC, but that can only happen on the main
121 // thread.
122 MOZ_CRASH();
125 return NS_OK;
128 void FilePickerParent::IORunnable::Destroy() { mFilePickerParent = nullptr; }
130 void FilePickerParent::SendFilesOrDirectories(
131 const nsTArray<BlobImplOrString>& aData) {
132 ContentParent* parent = BrowserParent::GetFrom(Manager())->Manager();
134 if (mMode == nsIFilePicker::modeGetFolder) {
135 MOZ_ASSERT(aData.Length() <= 1);
136 if (aData.IsEmpty()) {
137 Unused << Send__delete__(this, void_t(), mResult);
138 return;
141 MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath);
143 // Let's inform the security singleton about the given access of this tab on
144 // this directory path.
145 RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
146 fss->GrantAccessToContentProcess(parent->ChildID(),
147 aData[0].mDirectoryPath);
149 InputDirectory input;
150 input.directoryPath() = aData[0].mDirectoryPath;
151 Unused << Send__delete__(this, input, mResult);
152 return;
155 nsTArray<IPCBlob> ipcBlobs;
157 for (unsigned i = 0; i < aData.Length(); i++) {
158 IPCBlob ipcBlob;
160 MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
161 nsresult rv = IPCBlobUtils::Serialize(aData[i].mBlobImpl, ipcBlob);
162 if (NS_WARN_IF(NS_FAILED(rv))) {
163 break;
166 ipcBlobs.AppendElement(ipcBlob);
169 InputBlobs inblobs;
170 inblobs.blobs() = std::move(ipcBlobs);
172 Unused << Send__delete__(this, inblobs, mResult);
175 void FilePickerParent::Done(nsIFilePicker::ResultCode aResult) {
176 mResult = aResult;
178 if (mResult != nsIFilePicker::returnOK) {
179 Unused << Send__delete__(this, void_t(), mResult);
180 return;
183 nsTArray<nsCOMPtr<nsIFile>> files;
184 if (mMode == nsIFilePicker::modeOpenMultiple) {
185 nsCOMPtr<nsISimpleEnumerator> iter;
186 NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter)));
188 nsCOMPtr<nsISupports> supports;
189 bool loop = true;
190 while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
191 iter->GetNext(getter_AddRefs(supports));
192 if (supports) {
193 nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
194 MOZ_ASSERT(file);
195 files.AppendElement(file);
198 } else {
199 nsCOMPtr<nsIFile> file;
200 mFilePicker->GetFile(getter_AddRefs(file));
201 if (file) {
202 files.AppendElement(file);
206 if (files.IsEmpty()) {
207 Unused << Send__delete__(this, void_t(), mResult);
208 return;
211 MOZ_ASSERT(!mRunnable);
212 mRunnable = new IORunnable(this, std::move(files),
213 mMode == nsIFilePicker::modeGetFolder);
215 // Dispatch to background thread to do I/O:
216 if (!mRunnable->Dispatch()) {
217 Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
221 bool FilePickerParent::CreateFilePicker() {
222 if (!mBrowsingContext) {
223 return false;
226 mFilePicker = do_CreateInstance("@mozilla.org/filepicker;1");
228 if (!mFilePicker) {
229 return false;
232 return NS_SUCCEEDED(mFilePicker->Init(mBrowsingContext, mTitle, mMode));
235 mozilla::ipc::IPCResult FilePickerParent::RecvOpen(
236 const int16_t& aSelectedType, const bool& aAddToRecentDocs,
237 const nsString& aDefaultFile, const nsString& aDefaultExtension,
238 nsTArray<nsString>&& aFilters, nsTArray<nsString>&& aFilterNames,
239 nsTArray<nsString>&& aRawFilters, const nsString& aDisplayDirectory,
240 const nsString& aDisplaySpecialDirectory, const nsString& aOkButtonLabel,
241 const nsIFilePicker::CaptureTarget& aCapture) {
242 if (!CreateFilePicker()) {
243 Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
244 return IPC_OK();
247 mFilePicker->SetAddToRecentDocs(aAddToRecentDocs);
249 for (uint32_t i = 0; i < aFilters.Length(); ++i) {
250 mFilePicker->AppendFilter(aFilterNames[i], aFilters[i]);
253 for (uint32_t i = 0; i < aRawFilters.Length(); ++i) {
254 mFilePicker->AppendRawFilter(aRawFilters[i]);
257 mFilePicker->SetDefaultString(aDefaultFile);
258 mFilePicker->SetDefaultExtension(aDefaultExtension);
259 mFilePicker->SetFilterIndex(aSelectedType);
260 mFilePicker->SetOkButtonLabel(aOkButtonLabel);
261 mFilePicker->SetCapture(aCapture);
263 if (!aDisplayDirectory.IsEmpty()) {
264 nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
265 if (localFile) {
266 localFile->InitWithPath(aDisplayDirectory);
267 mFilePicker->SetDisplayDirectory(localFile);
269 } else if (!aDisplaySpecialDirectory.IsEmpty()) {
270 mFilePicker->SetDisplaySpecialDirectory(aDisplaySpecialDirectory);
273 MOZ_ASSERT(!mCallback);
274 mCallback = new FilePickerShownCallback(this);
276 mFilePicker->Open(mCallback);
277 return IPC_OK();
280 mozilla::ipc::IPCResult FilePickerParent::RecvClose() {
281 if (mFilePicker) {
282 mFilePicker->Close();
284 return IPC_OK();
287 void FilePickerParent::ActorDestroy(ActorDestroyReason aWhy) {
288 if (mCallback) {
289 mCallback->Destroy();
290 mCallback = nullptr;
292 if (mRunnable) {
293 mRunnable->Destroy();
294 mRunnable = nullptr;