1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set sw=4 ts=8 et 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"
11 #include "nsIDocument.h"
12 #include "nsIDOMFile.h"
13 #include "nsIDOMWindow.h"
15 #include "nsISimpleEnumerator.h"
16 #include "mozilla/unused.h"
17 #include "mozilla/dom/ContentParent.h"
18 #include "mozilla/dom/Element.h"
19 #include "mozilla/dom/TabParent.h"
20 #include "mozilla/dom/ipc/Blob.h"
22 using mozilla::unused
;
23 using namespace mozilla::dom
;
25 NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback
,
26 nsIFilePickerShownCallback
);
29 FilePickerParent::FilePickerShownCallback::Done(int16_t aResult
)
31 if (mFilePickerParent
) {
32 mFilePickerParent
->Done(aResult
);
38 FilePickerParent::FilePickerShownCallback::Destroy()
40 mFilePickerParent
= nullptr;
43 FilePickerParent::~FilePickerParent()
47 // Before sending a blob to the child, we need to get its size and modification
48 // date. Otherwise it will be sent as a "mystery blob" by
49 // GetOrCreateActorForBlob, which will cause problems for the child
50 // process. This runnable stat()s the file off the main thread.
52 // We run code in three places:
53 // 1. The main thread calls Dispatch() to start the runnable.
54 // 2. The stream transport thread stat()s the file in Run() and then dispatches
55 // the same runnable on the main thread.
56 // 3. The main thread sends the results over IPC.
57 FilePickerParent::FileSizeAndDateRunnable::FileSizeAndDateRunnable(FilePickerParent
*aFPParent
,
58 nsCOMArray
<nsIDOMFile
>& aDomfiles
)
59 : mFilePickerParent(aFPParent
)
61 mDomfiles
.SwapElements(aDomfiles
);
65 FilePickerParent::FileSizeAndDateRunnable::Dispatch()
67 MOZ_ASSERT(NS_IsMainThread());
69 mEventTarget
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
74 nsresult rv
= mEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
75 return NS_SUCCEEDED(rv
);
79 FilePickerParent::FileSizeAndDateRunnable::Run()
81 // If we're on the main thread, then that means we're done. Just send the
83 if (NS_IsMainThread()) {
84 if (mFilePickerParent
) {
85 mFilePickerParent
->SendFiles(mDomfiles
);
90 // We're not on the main thread, so do the stat().
91 for (unsigned i
= 0; i
< mDomfiles
.Length(); i
++) {
92 uint64_t size
, lastModified
;
93 mDomfiles
[i
]->GetSize(&size
);
94 mDomfiles
[i
]->GetMozLastModifiedDate(&lastModified
);
97 // Dispatch ourselves back on the main thread.
98 if (NS_FAILED(NS_DispatchToMainThread(this))) {
99 // It's hard to see how we can recover gracefully in this case. The child
100 // process is waiting for an IPC, but that can only happen on the main
108 FilePickerParent::FileSizeAndDateRunnable::Destroy()
110 mFilePickerParent
= nullptr;
114 FilePickerParent::SendFiles(const nsCOMArray
<nsIDOMFile
>& aDomfiles
)
116 nsIContentParent
* parent
= static_cast<TabParent
*>(Manager())->Manager();
117 InfallibleTArray
<PBlobParent
*> files
;
119 for (unsigned i
= 0; i
< aDomfiles
.Length(); i
++) {
120 BlobParent
* blob
= parent
->GetOrCreateActorForBlob(aDomfiles
[i
]);
122 files
.AppendElement(blob
);
127 infiles
.filesParent().SwapElements(files
);
128 unused
<< Send__delete__(this, infiles
, mResult
);
132 FilePickerParent::Done(int16_t aResult
)
136 if (mResult
!= nsIFilePicker::returnOK
) {
137 unused
<< Send__delete__(this, void_t(), mResult
);
141 nsCOMArray
<nsIDOMFile
> domfiles
;
142 if (mMode
== nsIFilePicker::modeOpenMultiple
) {
143 nsCOMPtr
<nsISimpleEnumerator
> iter
;
144 NS_ENSURE_SUCCESS_VOID(mFilePicker
->GetFiles(getter_AddRefs(iter
)));
146 nsCOMPtr
<nsISupports
> supports
;
148 while (NS_SUCCEEDED(iter
->HasMoreElements(&loop
)) && loop
) {
149 iter
->GetNext(getter_AddRefs(supports
));
151 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(supports
);
152 nsCOMPtr
<nsIDOMFile
> domfile
= DOMFile::CreateFromFile(file
);
153 domfiles
.AppendElement(domfile
);
157 nsCOMPtr
<nsIFile
> file
;
158 mFilePicker
->GetFile(getter_AddRefs(file
));
160 nsCOMPtr
<nsIDOMFile
> domfile
= DOMFile::CreateFromFile(file
);
161 domfiles
.AppendElement(domfile
);
165 MOZ_ASSERT(!mRunnable
);
166 mRunnable
= new FileSizeAndDateRunnable(this, domfiles
);
167 if (!mRunnable
->Dispatch()) {
168 unused
<< Send__delete__(this, void_t(), nsIFilePicker::returnCancel
);
173 FilePickerParent::CreateFilePicker()
175 mFilePicker
= do_CreateInstance("@mozilla.org/filepicker;1");
180 Element
* element
= static_cast<TabParent
*>(Manager())->GetOwnerElement();
185 nsCOMPtr
<nsIDOMWindow
> window
= do_QueryInterface(element
->OwnerDoc()->GetWindow());
190 return NS_SUCCEEDED(mFilePicker
->Init(window
, mTitle
, mMode
));
194 FilePickerParent::RecvOpen(const int16_t& aSelectedType
,
195 const bool& aAddToRecentDocs
,
196 const nsString
& aDefaultFile
,
197 const nsString
& aDefaultExtension
,
198 const InfallibleTArray
<nsString
>& aFilters
,
199 const InfallibleTArray
<nsString
>& aFilterNames
)
201 if (!CreateFilePicker()) {
202 unused
<< Send__delete__(this, void_t(), nsIFilePicker::returnCancel
);
206 mFilePicker
->SetAddToRecentDocs(aAddToRecentDocs
);
208 for (uint32_t i
= 0; i
< aFilters
.Length(); ++i
) {
209 mFilePicker
->AppendFilter(aFilterNames
[i
], aFilters
[i
]);
212 mFilePicker
->SetDefaultString(aDefaultFile
);
213 mFilePicker
->SetDefaultExtension(aDefaultExtension
);
214 mFilePicker
->SetFilterIndex(aSelectedType
);
216 mCallback
= new FilePickerShownCallback(this);
218 mFilePicker
->Open(mCallback
);
223 FilePickerParent::ActorDestroy(ActorDestroyReason aWhy
)
226 mCallback
->Destroy();
230 mRunnable
->Destroy();