tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FilePickerDelegate.sys.mjs (7136B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
      6 import { GeckoViewUtils } from "resource://gre/modules/GeckoViewUtils.sys.mjs";
      7 
      8 const lazy = {};
      9 
     10 ChromeUtils.defineESModuleGetters(lazy, {
     11  FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
     12  GeckoViewPrompter: "resource://gre/modules/GeckoViewPrompter.sys.mjs",
     13  NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
     14 });
     15 
     16 const { debug, warn } = GeckoViewUtils.initLogging("FilePickerDelegate");
     17 
     18 export class FilePickerDelegate {
     19  _filesInWebKitDirectory = [];
     20 
     21  /* ----------  nsIFilePicker  ---------- */
     22  init(aBrowsingContext, aTitle, aMode) {
     23    let mode;
     24    switch (aMode) {
     25      case Ci.nsIFilePicker.modeOpen:
     26        mode = "single";
     27        break;
     28      case Ci.nsIFilePicker.modeGetFolder:
     29        mode = "folder";
     30        break;
     31      case Ci.nsIFilePicker.modeOpenMultiple:
     32        mode = "multiple";
     33        break;
     34      default:
     35        throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
     36    }
     37    this._browsingContext = aBrowsingContext;
     38    this._prompt = new lazy.GeckoViewPrompter(aBrowsingContext);
     39    this._msg = {
     40      type: "file",
     41      title: aTitle,
     42      mode,
     43    };
     44    this._mode = aMode;
     45    this._mimeTypes = [];
     46    this._capture = 0;
     47  }
     48 
     49  get mode() {
     50    return this._mode;
     51  }
     52 
     53  appendRawFilter(aFilter) {
     54    this._mimeTypes.push(aFilter);
     55  }
     56 
     57  open(aFilePickerShownCallback) {
     58    if (!this._browsingContext.canOpenModalPicker) {
     59      // File pickers are not allowed to open, so we respond to the callback
     60      // with returnCancel.
     61      aFilePickerShownCallback.done(Ci.nsIFilePicker.returnCancel);
     62      return;
     63    }
     64 
     65    this._msg.mimeTypes = this._mimeTypes;
     66    this._msg.capture = this._capture;
     67    this._prompt.asyncShowPrompt(this._msg, result => {
     68      // OK: result
     69      // Cancel: !result
     70      if (!result || !result.files || !result.files.length) {
     71        aFilePickerShownCallback.done(Ci.nsIFilePicker.returnCancel);
     72      } else {
     73        this._resolveFilesInWebKitDirectory(result.filesInWebKitDirectory).then(
     74          () => {
     75            this._resolveFiles(result.files, aFilePickerShownCallback);
     76          }
     77        );
     78      }
     79    });
     80  }
     81 
     82  async _resolveFiles(aFiles, aCallback) {
     83    const fileData = [];
     84 
     85    try {
     86      for (const file of aFiles) {
     87        const domFileOrDir = await this._getDOMFileOrDir(file);
     88        fileData.push({
     89          file,
     90          domFileOrDir,
     91        });
     92      }
     93    } catch (ex) {
     94      warn`Error resolving files from file picker: ${ex}`;
     95      aCallback.done(Ci.nsIFilePicker.returnCancel);
     96      return;
     97    }
     98 
     99    this._fileData = fileData;
    100    aCallback.done(Ci.nsIFilePicker.returnOK);
    101  }
    102 
    103  async _resolveFilesInWebKitDirectory(files) {
    104    if (!files) {
    105      return;
    106    }
    107 
    108    const filesInWebKitDirectory = [];
    109 
    110    for (const info of files) {
    111      const { filePath, uri, relativePath, name, type, lastModified } = info;
    112      if (filePath) {
    113        const file = await (() => {
    114          if (this._prompt.domWin) {
    115            return this._prompt.domWin.File.createFromFileName(filePath, {
    116              type,
    117              lastModified,
    118            });
    119          }
    120          return File.createFromFileName(filePath, {
    121            type,
    122            lastModified,
    123          });
    124        })();
    125 
    126        file.setMozRelativePath(relativePath);
    127        filesInWebKitDirectory.push(file);
    128        continue;
    129      }
    130 
    131      // File path cannot be resolved, but we know content URI.
    132      const input = Cc[
    133        "@mozilla.org/network/android-content-input-stream;1"
    134      ].createInstance(Ci.nsIAndroidContentInputStream);
    135      input.init(Services.io.newURI(uri));
    136      const buffer = lazy.NetUtil.readInputStream(input);
    137      input.close();
    138 
    139      const file = (() => {
    140        if (this._prompt.domWin) {
    141          return new this._prompt.domWin.File([buffer], name, {
    142            type,
    143            lastModified,
    144          });
    145        }
    146        return new File([buffer], name, { type, lastModified });
    147      })();
    148      file.setMozRelativePath(relativePath);
    149      filesInWebKitDirectory.push(file);
    150    }
    151 
    152    this._filesInWebKitDirectory = filesInWebKitDirectory;
    153  }
    154 
    155  get file() {
    156    if (!this._fileData) {
    157      throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
    158    }
    159    const fileData = this._fileData[0];
    160    if (!fileData) {
    161      return null;
    162    }
    163    return new lazy.FileUtils.File(fileData.file);
    164  }
    165 
    166  get fileURL() {
    167    return Services.io.newFileURI(this.file);
    168  }
    169 
    170  *_getEnumerator(aDOMFile) {
    171    if (!this._fileData) {
    172      throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
    173    }
    174 
    175    for (const fileData of this._fileData) {
    176      if (aDOMFile) {
    177        yield fileData.domFileOrDir;
    178      }
    179      yield new lazy.FileUtils.File(fileData.file);
    180    }
    181  }
    182 
    183  get files() {
    184    return this._getEnumerator(/* aDOMFile */ false);
    185  }
    186 
    187  _getDOMFileOrDir(aPath) {
    188    if (this.mode == Ci.nsIFilePicker.modeGetFolder) {
    189      return this._getDOMDir(aPath);
    190    }
    191    return this._getDOMFile(aPath);
    192  }
    193 
    194  _getDOMDir(aPath) {
    195    if (this._prompt.domWin) {
    196      return new this._prompt.domWin.Directory(aPath);
    197    }
    198    return new Directory(aPath);
    199  }
    200 
    201  _getDOMFile(aPath) {
    202    if (this._prompt.domWin) {
    203      return this._prompt.domWin.File.createFromFileName(aPath);
    204    }
    205    return File.createFromFileName(aPath);
    206  }
    207 
    208  get domFileOrDirectory() {
    209    if (!this._fileData) {
    210      throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
    211    }
    212    return this._fileData[0]?.domFileOrDir;
    213  }
    214 
    215  get domFileOrDirectoryEnumerator() {
    216    return this._getEnumerator(/* aDOMFile */ true);
    217  }
    218 
    219  get defaultString() {
    220    return "";
    221  }
    222 
    223  set defaultString(aValue) {}
    224 
    225  get defaultExtension() {
    226    return "";
    227  }
    228 
    229  set defaultExtension(aValue) {}
    230 
    231  get filterIndex() {
    232    return 0;
    233  }
    234 
    235  set filterIndex(aValue) {}
    236 
    237  get displayDirectory() {
    238    return null;
    239  }
    240 
    241  set displayDirectory(aValue) {}
    242 
    243  get displaySpecialDirectory() {
    244    return "";
    245  }
    246 
    247  set displaySpecialDirectory(aValue) {}
    248 
    249  get addToRecentDocs() {
    250    return false;
    251  }
    252 
    253  set addToRecentDocs(aValue) {}
    254 
    255  get okButtonLabel() {
    256    return "";
    257  }
    258 
    259  set okButtonLabel(aValue) {}
    260 
    261  get capture() {
    262    return this._capture;
    263  }
    264 
    265  set capture(aValue) {
    266    this._capture = aValue;
    267  }
    268 
    269  *_getDOMFilesInWebKitDirectory() {
    270    if (
    271      this._mode != Ci.nsIFilePicker.modeGetFolder ||
    272      AppConstants.platform != "android"
    273    ) {
    274      throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
    275    }
    276 
    277    for (const file of this._filesInWebKitDirectory) {
    278      yield file;
    279    }
    280  }
    281 
    282  get domFilesInWebKitDirectory() {
    283    return this._getDOMFilesInWebKitDirectory();
    284  }
    285 }
    286 
    287 FilePickerDelegate.prototype.classID = Components.ID(
    288  "{e4565e36-f101-4bf5-950b-4be0887785a9}"
    289 );
    290 FilePickerDelegate.prototype.QueryInterface = ChromeUtils.generateQI([
    291  "nsIFilePicker",
    292 ]);