1
0
Fork 0
firefox/mobile/shared/components/geckoview/FilePickerDelegate.sys.mjs
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

289 lines
6.8 KiB
JavaScript

/* 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/. */
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { GeckoViewUtils } from "resource://gre/modules/GeckoViewUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
GeckoViewPrompter: "resource://gre/modules/GeckoViewPrompter.sys.mjs",
NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
});
const { debug, warn } = GeckoViewUtils.initLogging("FilePickerDelegate");
export class FilePickerDelegate {
_filesInWebKitDirectory = [];
/* ---------- nsIFilePicker ---------- */
init(aBrowsingContext, aTitle, aMode) {
let mode;
switch (aMode) {
case Ci.nsIFilePicker.modeOpen:
mode = "single";
break;
case Ci.nsIFilePicker.modeGetFolder:
mode = "folder";
break;
case Ci.nsIFilePicker.modeOpenMultiple:
mode = "multiple";
break;
default:
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
}
this._browsingContext = aBrowsingContext;
this._prompt = new lazy.GeckoViewPrompter(aBrowsingContext);
this._msg = {
type: "file",
title: aTitle,
mode,
};
this._mode = aMode;
this._mimeTypes = [];
this._capture = 0;
}
get mode() {
return this._mode;
}
appendRawFilter(aFilter) {
this._mimeTypes.push(aFilter);
}
open(aFilePickerShownCallback) {
if (!this._browsingContext.canOpenModalPicker) {
// File pickers are not allowed to open, so we respond to the callback
// with returnCancel.
aFilePickerShownCallback.done(Ci.nsIFilePicker.returnCancel);
return;
}
this._msg.mimeTypes = this._mimeTypes;
this._msg.capture = this._capture;
this._prompt.asyncShowPrompt(this._msg, result => {
// OK: result
// Cancel: !result
if (!result || !result.files || !result.files.length) {
aFilePickerShownCallback.done(Ci.nsIFilePicker.returnCancel);
} else {
this._resolveFilesInWebKitDirectory(result.filesInWebKitDirectory).then(
() => {
this._resolveFiles(result.files, aFilePickerShownCallback);
}
);
}
});
}
async _resolveFiles(aFiles, aCallback) {
const fileData = [];
try {
for (const file of aFiles) {
const domFileOrDir = await this._getDOMFileOrDir(file);
fileData.push({
file,
domFileOrDir,
});
}
} catch (ex) {
warn`Error resolving files from file picker: ${ex}`;
aCallback.done(Ci.nsIFilePicker.returnCancel);
return;
}
this._fileData = fileData;
aCallback.done(Ci.nsIFilePicker.returnOK);
}
async _resolveFilesInWebKitDirectory(files) {
if (!files) {
return;
}
const filesInWebKitDirectory = [];
for (const info of files) {
const { filePath, uri, name, type, lastModified } = info;
if (filePath) {
const file = (() => {
if (this._prompt.domWin) {
return this._prompt.domWin.File.createFromFileName(filePath, {
type,
lastModified,
});
}
return File.createFromFileName(filePath, {
type,
lastModified,
});
})();
filesInWebKitDirectory.push(await file);
continue;
}
// File path cannot be resolved, but we know content URI.
const input = Cc[
"@mozilla.org/network/android-content-input-stream;1"
].createInstance(Ci.nsIAndroidContentInputStream);
input.init(Services.io.newURI(uri));
const buffer = lazy.NetUtil.readInputStream(input);
const file = (() => {
if (this._prompt.domWin) {
return new this._prompt.domWin.File([buffer], name, {
type,
lastModified,
});
}
return new File([buffer], name, { type, lastModified });
})();
filesInWebKitDirectory.push(file);
}
this._filesInWebKitDirectory = filesInWebKitDirectory;
}
get file() {
if (!this._fileData) {
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
}
const fileData = this._fileData[0];
if (!fileData) {
return null;
}
return new lazy.FileUtils.File(fileData.file);
}
get fileURL() {
return Services.io.newFileURI(this.file);
}
*_getEnumerator(aDOMFile) {
if (!this._fileData) {
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
}
for (const fileData of this._fileData) {
if (aDOMFile) {
yield fileData.domFileOrDir;
}
yield new lazy.FileUtils.File(fileData.file);
}
}
get files() {
return this._getEnumerator(/* aDOMFile */ false);
}
_getDOMFileOrDir(aPath) {
if (this.mode == Ci.nsIFilePicker.modeGetFolder) {
return this._getDOMDir(aPath);
}
return this._getDOMFile(aPath);
}
_getDOMDir(aPath) {
if (this._prompt.domWin) {
return new this._prompt.domWin.Directory(aPath);
}
return new Directory(aPath);
}
_getDOMFile(aPath) {
if (this._prompt.domWin) {
return this._prompt.domWin.File.createFromFileName(aPath);
}
return File.createFromFileName(aPath);
}
get domFileOrDirectory() {
if (!this._fileData) {
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
}
return this._fileData[0]?.domFileOrDir;
}
get domFileOrDirectoryEnumerator() {
return this._getEnumerator(/* aDOMFile */ true);
}
get defaultString() {
return "";
}
set defaultString(aValue) {}
get defaultExtension() {
return "";
}
set defaultExtension(aValue) {}
get filterIndex() {
return 0;
}
set filterIndex(aValue) {}
get displayDirectory() {
return null;
}
set displayDirectory(aValue) {}
get displaySpecialDirectory() {
return "";
}
set displaySpecialDirectory(aValue) {}
get addToRecentDocs() {
return false;
}
set addToRecentDocs(aValue) {}
get okButtonLabel() {
return "";
}
set okButtonLabel(aValue) {}
get capture() {
return this._capture;
}
set capture(aValue) {
this._capture = aValue;
}
*_getDOMFilesInWebKitDirectory() {
if (
this._mode != Ci.nsIFilePicker.modeGetFolder ||
AppConstants.platform != "android"
) {
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
}
for (const file of this._filesInWebKitDirectory) {
yield file;
}
}
get domFilesInWebKitDirectory() {
return this._getDOMFilesInWebKitDirectory();
}
}
FilePickerDelegate.prototype.classID = Components.ID(
"{e4565e36-f101-4bf5-950b-4be0887785a9}"
);
FilePickerDelegate.prototype.QueryInterface = ChromeUtils.generateQI([
"nsIFilePicker",
]);