Bug 1674428 - Part 2. Add method to collect files that is selected WebKitDirectory. r=dom-core,sefeng
When using Android 13 or later, there is no permission to enumerate directories if on user storage. So `GetFilesHelper` and etc won't work on Android. We have to use content resolver APIs to enumerate files in directory. So I have to add a method to collect files in FilePicker and return DOM Files. Differential Revision: https://phabricator.services.mozilla.com/D228761
This commit is contained in:
@@ -576,6 +576,36 @@ HTMLInputElement::nsFilePickerShownCallback::Done(
|
||||
|
||||
if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
|
||||
mInput->HasAttr(nsGkAtoms::webkitdirectory)) {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// Android 13 or later cannot enumerate files into user directory due to
|
||||
// no permission. So we store file list into file picker.
|
||||
FallibleTArray<RefPtr<BlobImpl>> filesInWebKitDirectory;
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
if (NS_SUCCEEDED(
|
||||
mFilePicker->GetDomFilesInWebKitDirectory(getter_AddRefs(iter))) &&
|
||||
iter) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
|
||||
bool loop = true;
|
||||
while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
|
||||
iter->GetNext(getter_AddRefs(supports));
|
||||
if (supports) {
|
||||
RefPtr<BlobImpl> file = static_cast<File*>(supports.get())->Impl();
|
||||
MOZ_ASSERT(file);
|
||||
if (!filesInWebKitDirectory.AppendElement(file, fallible)) {
|
||||
return nsresult::NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!filesInWebKitDirectory.IsEmpty()) {
|
||||
dispatchChangeEventCallback->Callback(NS_OK, filesInWebKitDirectory);
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
ErrorResult error;
|
||||
GetFilesHelper* helper = mInput->GetOrCreateGetFilesHelper(true, error);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
|
||||
@@ -45,12 +45,13 @@ FilePickerParent::~FilePickerParent() = default;
|
||||
// 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)
|
||||
FilePickerParent::IORunnable::IORunnable(
|
||||
FilePickerParent* aFPParent, nsTArray<nsCOMPtr<nsIFile>>&& aFiles,
|
||||
nsTArray<RefPtr<BlobImpl>>&& aFilesInWebKitDirectory, bool aIsDirectory)
|
||||
: mozilla::Runnable("dom::FilePickerParent::IORunnable"),
|
||||
mFilePickerParent(aFPParent),
|
||||
mFiles(std::move(aFiles)),
|
||||
mFilesInWebKitDirectory(std::move(aFilesInWebKitDirectory)),
|
||||
mIsDirectory(aIsDirectory) {
|
||||
MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1);
|
||||
}
|
||||
@@ -73,7 +74,8 @@ FilePickerParent::IORunnable::Run() {
|
||||
// results.
|
||||
if (NS_IsMainThread()) {
|
||||
if (mFilePickerParent) {
|
||||
mFilePickerParent->SendFilesOrDirectories(mResults);
|
||||
mFilePickerParent->SendFilesOrDirectories(mResults,
|
||||
mFilesInWebKitDirectory);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -128,7 +130,8 @@ FilePickerParent::IORunnable::Run() {
|
||||
void FilePickerParent::IORunnable::Destroy() { mFilePickerParent = nullptr; }
|
||||
|
||||
void FilePickerParent::SendFilesOrDirectories(
|
||||
const nsTArray<BlobImplOrString>& aData) {
|
||||
const nsTArray<BlobImplOrString>& aData,
|
||||
const nsTArray<RefPtr<BlobImpl>>& aFilesInWebKitDirectory) {
|
||||
ContentParent* parent = BrowserParent::GetFrom(Manager())->Manager();
|
||||
|
||||
if (mMode == nsIFilePicker::modeGetFolder) {
|
||||
@@ -146,8 +149,20 @@ void FilePickerParent::SendFilesOrDirectories(
|
||||
fss->GrantAccessToContentProcess(parent->ChildID(),
|
||||
aData[0].mDirectoryPath);
|
||||
|
||||
nsTArray<IPCBlob> ipcBlobs;
|
||||
for (const auto& blob : aFilesInWebKitDirectory) {
|
||||
IPCBlob ipcBlob;
|
||||
|
||||
nsresult rv = IPCBlobUtils::Serialize(blob, ipcBlob);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
break;
|
||||
}
|
||||
ipcBlobs.AppendElement(ipcBlob);
|
||||
}
|
||||
|
||||
InputDirectory input;
|
||||
input.directoryPath() = aData[0].mDirectoryPath;
|
||||
input.blobsInWebKitDirectory() = std::move(ipcBlobs);
|
||||
Unused << Send__delete__(this, input, mResult);
|
||||
return;
|
||||
}
|
||||
@@ -208,9 +223,32 @@ void FilePickerParent::Done(nsIFilePicker::ResultCode aResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<BlobImpl>> blobsInWebKitDirectory;
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (mMode == nsIFilePicker::modeGetFolder) {
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
if (NS_SUCCEEDED(
|
||||
mFilePicker->GetDomFilesInWebKitDirectory(getter_AddRefs(iter)))) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
|
||||
bool loop = true;
|
||||
while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
|
||||
iter->GetNext(getter_AddRefs(supports));
|
||||
if (supports) {
|
||||
RefPtr<BlobImpl> file = static_cast<File*>(supports.get())->Impl();
|
||||
MOZ_ASSERT(file);
|
||||
blobsInWebKitDirectory.AppendElement(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(!mRunnable);
|
||||
mRunnable = new IORunnable(this, std::move(files),
|
||||
mMode == nsIFilePicker::modeGetFolder);
|
||||
mRunnable =
|
||||
new IORunnable(this, std::move(files), std::move(blobsInWebKitDirectory),
|
||||
mMode == nsIFilePicker::modeGetFolder);
|
||||
|
||||
// Dispatch to background thread to do I/O:
|
||||
if (!mRunnable->Dispatch()) {
|
||||
|
||||
@@ -43,7 +43,9 @@ class FilePickerParent : public PFilePickerParent {
|
||||
enum { eBlobImpl, eDirectoryPath } mType;
|
||||
};
|
||||
|
||||
void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData);
|
||||
void SendFilesOrDirectories(
|
||||
const nsTArray<BlobImplOrString>& aData,
|
||||
const nsTArray<RefPtr<BlobImpl>>& aFilesInWebKitDirectory);
|
||||
|
||||
mozilla::ipc::IPCResult RecvOpen(
|
||||
const int16_t& aSelectedType, const bool& aAddToRecentDocs,
|
||||
@@ -79,11 +81,14 @@ class FilePickerParent : public PFilePickerParent {
|
||||
nsTArray<nsCOMPtr<nsIFile>> mFiles;
|
||||
nsTArray<BlobImplOrString> mResults;
|
||||
nsCOMPtr<nsIEventTarget> mEventTarget;
|
||||
nsTArray<RefPtr<BlobImpl>> mFilesInWebKitDirectory;
|
||||
bool mIsDirectory;
|
||||
|
||||
public:
|
||||
IORunnable(FilePickerParent* aFPParent,
|
||||
nsTArray<nsCOMPtr<nsIFile>>&& aFiles, bool aIsDirectory);
|
||||
nsTArray<nsCOMPtr<nsIFile>>&& aFiles,
|
||||
nsTArray<RefPtr<BlobImpl>>&& aFilesInWebKitDirectory,
|
||||
bool aIsDirectory);
|
||||
|
||||
bool Dispatch();
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
@@ -18,7 +18,10 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct InputBlobs { IPCBlob[] blobs; };
|
||||
struct InputDirectory { nsString directoryPath; };
|
||||
struct InputDirectory {
|
||||
nsString directoryPath;
|
||||
IPCBlob[] blobsInWebKitDirectory;
|
||||
};
|
||||
union MaybeInputData
|
||||
{
|
||||
InputBlobs;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* 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/. */
|
||||
|
||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
@@ -70,6 +72,7 @@ export var MockFilePicker = {
|
||||
this.mode = null;
|
||||
this.returnData = [];
|
||||
this.returnValue = null;
|
||||
this.returnDataForWebKitDirs = [];
|
||||
this.showCallback = null;
|
||||
this.afterOpenCallback = null;
|
||||
this.shown = false;
|
||||
@@ -132,6 +135,22 @@ export var MockFilePicker = {
|
||||
this.internalFileData({ domDirectory: directory, nsIFile: file }),
|
||||
];
|
||||
this.pendingPromises = [];
|
||||
this.returnDataForWebKitDirs = [];
|
||||
|
||||
if (AppConstants.platform === "android") {
|
||||
for (const filename of ["/foo.txt", "/subdir/bar.txt"]) {
|
||||
let fileInDir = lazy.FileUtils.File(aPath + filename);
|
||||
this.window.File.createFromNsIFile(file, {
|
||||
existenceCheck: false,
|
||||
}).then(domFile => {
|
||||
this.returnDataForWebKitDirs.push(
|
||||
this.internalFileData({ nsIFile: fileInDir, domFile })
|
||||
);
|
||||
});
|
||||
// promise might be added into pendinngPromise, but it causes
|
||||
// InvalidStateError.
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setFiles(files) {
|
||||
@@ -243,6 +262,18 @@ MockFilePickerInstance.prototype = {
|
||||
get domFileOrDirectoryEnumerator() {
|
||||
return this.getFiles(true);
|
||||
},
|
||||
*getDomFilesInWebKitDirectory() {
|
||||
for (let d of MockFilePicker.returnDataForWebKitDirs) {
|
||||
yield d.domFile;
|
||||
}
|
||||
},
|
||||
get domFilesInWebKitDirectory() {
|
||||
if (AppConstants.platform !== "android") {
|
||||
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
return this.getDomFilesInWebKitDirectory();
|
||||
},
|
||||
open(aFilePickerShownCallback) {
|
||||
MockFilePicker.showing = true;
|
||||
Services.tm.dispatchToMainThread(() => {
|
||||
|
||||
@@ -472,3 +472,8 @@ nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(
|
||||
retIter.forget(aValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseFilePicker::GetDomFilesInWebKitDirectory(nsISimpleEnumerator** aValue) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ class nsBaseFilePicker : public nsIFilePicker {
|
||||
NS_IMETHOD GetDomFileOrDirectory(nsISupports** aValue) override;
|
||||
NS_IMETHOD GetDomFileOrDirectoryEnumerator(
|
||||
nsISimpleEnumerator** aValue) override;
|
||||
NS_IMETHOD GetDomFilesInWebKitDirectory(
|
||||
nsISimpleEnumerator** aValue) override;
|
||||
|
||||
protected:
|
||||
virtual ~nsBaseFilePicker();
|
||||
|
||||
@@ -182,6 +182,25 @@ mozilla::ipc::IPCResult nsFilePickerProxy::Recv__delete__(
|
||||
|
||||
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
|
||||
element->SetAsDirectory() = directory;
|
||||
|
||||
const nsTArray<IPCBlob>& blobs =
|
||||
aData.get_InputDirectory().blobsInWebKitDirectory();
|
||||
for (const auto& blob : blobs) {
|
||||
RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blob);
|
||||
NS_ENSURE_TRUE(blobImpl, IPC_OK());
|
||||
|
||||
if (!blobImpl->IsFile()) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<File> file = File::Create(inner->AsGlobal(), blobImpl);
|
||||
if (NS_WARN_IF(!file)) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
OwningFileOrDirectory* element = mFilesInWebKitDirectory.AppendElement();
|
||||
element->SetAsFile() = file;
|
||||
}
|
||||
}
|
||||
|
||||
if (mCallback) {
|
||||
@@ -283,3 +302,16 @@ nsresult nsFilePickerProxy::ResolveSpecialDirectory(
|
||||
// even meaningful here, so we just accept anything.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFilePickerProxy::GetDomFilesInWebKitDirectory(
|
||||
nsISimpleEnumerator** aDomfiles) {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
RefPtr<SimpleEnumerator> enumerator =
|
||||
new SimpleEnumerator(mFilesInWebKitDirectory);
|
||||
enumerator.forget(aDomfiles);
|
||||
return NS_OK;
|
||||
#else
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ class nsFilePickerProxy : public nsBaseFilePicker,
|
||||
NS_IMETHOD GetDomFileOrDirectory(nsISupports** aValue) override;
|
||||
NS_IMETHOD GetDomFileOrDirectoryEnumerator(
|
||||
nsISimpleEnumerator** aValue) override;
|
||||
NS_IMETHOD GetDomFilesInWebKitDirectory(
|
||||
nsISimpleEnumerator** aValue) override;
|
||||
|
||||
NS_IMETHOD Open(nsIFilePickerShownCallback* aCallback) override;
|
||||
|
||||
@@ -70,6 +72,7 @@ class nsFilePickerProxy : public nsBaseFilePicker,
|
||||
|
||||
nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
|
||||
nsCOMPtr<nsIFilePickerShownCallback> mCallback;
|
||||
nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesInWebKitDirectory;
|
||||
|
||||
int16_t mSelectedType;
|
||||
nsString mFile;
|
||||
|
||||
@@ -229,6 +229,13 @@ interface nsIFilePicker : nsISupports
|
||||
* Not used by Firefox Desktop.
|
||||
*/
|
||||
attribute nsIFilePicker_CaptureTarget capture;
|
||||
|
||||
/**
|
||||
* Get the enumerator for the DOM files that is selected by directory picker
|
||||
*
|
||||
* @return Returns the files/directories
|
||||
*/
|
||||
readonly attribute nsISimpleEnumerator domFilesInWebKitDirectory;
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(0d79adad-b244-49A5-9997-2a8cad93fc44)]
|
||||
|
||||
Reference in New Issue
Block a user