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/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
9 WrapPrivileged: "resource://testing-common/WrapPrivileged.sys.mjs",
12 const Cm = Components.manager;
14 const CONTRACT_ID = "@mozilla.org/filepicker;1";
16 if (import.meta.url.includes("specialpowers")) {
17 Cu.crashIfNotInAutomation();
20 var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
22 var newClassID = Services.uuid.generateUUID();
23 var newFactory = function (window) {
25 createInstance(aIID) {
26 return new MockFilePickerInstance(window).QueryInterface(aIID);
28 QueryInterface: ChromeUtils.generateQI(["nsIFactory"]),
32 export var MockFilePicker = {
33 returnOK: Ci.nsIFilePicker.returnOK,
34 returnCancel: Ci.nsIFilePicker.returnCancel,
35 returnReplace: Ci.nsIFilePicker.returnReplace,
37 filterAll: Ci.nsIFilePicker.filterAll,
38 filterHTML: Ci.nsIFilePicker.filterHTML,
39 filterText: Ci.nsIFilePicker.filterText,
40 filterImages: Ci.nsIFilePicker.filterImages,
41 filterXML: Ci.nsIFilePicker.filterXML,
42 filterXUL: Ci.nsIFilePicker.filterXUL,
43 filterApps: Ci.nsIFilePicker.filterApps,
44 filterAllowURLs: Ci.nsIFilePicker.filterAllowURLs,
45 filterAudio: Ci.nsIFilePicker.filterAudio,
46 filterVideo: Ci.nsIFilePicker.filterVideo,
55 this.factory = newFactory(window);
56 if (!registrar.isCIDRegistered(newClassID)) {
57 oldClassID = registrar.contractIDToCID(CONTRACT_ID);
58 registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory);
63 this.appendFilterCallback = null;
64 this.appendFiltersCallback = null;
65 this.displayDirectory = null;
66 this.displaySpecialDirectory = "";
70 this.returnValue = null;
71 this.showCallback = null;
72 this.afterOpenCallback = null;
78 var previousFactory = this.factory;
82 registrar.unregisterFactory(newClassID, previousFactory);
83 registrar.registerFactory(oldClassID, "", CONTRACT_ID, null);
87 internalFileData(obj) {
89 nsIFile: "nsIFile" in obj ? obj.nsIFile : null,
90 domFile: "domFile" in obj ? obj.domFile : null,
91 domDirectory: "domDirectory" in obj ? obj.domDirectory : null,
96 var file = lazy.FileUtils.getDir("TmpD", []);
97 file.append("testfile");
98 file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
99 let promise = this.window.File.createFromNsIFile(file)
104 // domFile can be null.
106 this.returnData = [this.internalFileData({ nsIFile: file, domFile })];
110 this.pendingPromises = [promise];
112 // We return a promise in order to support some existing mochitests.
117 var blob = new this.window.Blob([]);
118 var file = new this.window.File([blob], "helloworld.txt", {
121 this.returnData = [this.internalFileData({ domFile: file })];
122 this.pendingPromises = [];
125 useDirectory(aPath) {
126 var directory = new this.window.Directory(aPath);
127 var file = new lazy.FileUtils.File(aPath);
129 this.internalFileData({ domDirectory: directory, nsIFile: file }),
131 this.pendingPromises = [];
135 this.returnData = [];
136 this.pendingPromises = [];
138 for (let file of files) {
139 if (this.window.File.isInstance(file)) {
140 this.returnData.push(this.internalFileData({ domFile: file }));
142 let promise = this.window.File.createFromNsIFile(file, {
143 existenceCheck: false,
146 promise.then(domFile => {
147 this.returnData.push(
148 this.internalFileData({ nsIFile: file, domFile })
151 this.pendingPromises.push(promise);
157 if (this.returnData.length >= 1) {
158 return this.returnData[0].nsIFile;
164 function MockFilePickerInstance(window) {
165 this.window = window;
166 this.showCallback = null;
167 this.showCallbackWrapped = null;
169 MockFilePickerInstance.prototype = {
170 QueryInterface: ChromeUtils.generateQI(["nsIFilePicker"]),
171 init(aParent, aTitle, aMode) {
173 this.filterIndex = MockFilePicker.filterIndex;
174 this.parent = aParent;
176 appendFilter(aTitle, aFilter) {
177 if (typeof MockFilePicker.appendFilterCallback == "function") {
178 MockFilePicker.appendFilterCallback(this, aTitle, aFilter);
181 appendFilters(aFilterMask) {
182 if (typeof MockFilePicker.appendFiltersCallback == "function") {
183 MockFilePicker.appendFiltersCallback(this, aFilterMask);
187 defaultExtension: "",
190 displayDirectory: null,
191 displaySpecialDirectory: "",
193 if (MockFilePicker.returnData.length >= 1) {
194 return MockFilePicker.returnData[0].nsIFile;
200 // We don't support directories here.
201 get domFileOrDirectory() {
202 if (MockFilePicker.returnData.length < 1) {
206 if (MockFilePicker.returnData[0].domFile) {
207 return MockFilePicker.returnData[0].domFile;
210 if (MockFilePicker.returnData[0].domDirectory) {
211 return MockFilePicker.returnData[0].domDirectory;
218 MockFilePicker.returnData.length >= 1 &&
219 MockFilePicker.returnData[0].nsIFile
221 return Services.io.newFileURI(MockFilePicker.returnData[0].nsIFile);
227 for (let d of MockFilePicker.returnData) {
229 yield d.domFile || d.domDirectory;
230 } else if (d.nsIFile) {
233 throw Components.Exception("", Cr.NS_ERROR_FAILURE);
238 return this.getFiles(false);
240 get domFileOrDirectoryEnumerator() {
241 return this.getFiles(true);
243 open(aFilePickerShownCallback) {
244 MockFilePicker.showing = true;
245 Services.tm.dispatchToMainThread(() => {
246 // Maybe all the pending promises are already resolved, but we want to be sure.
247 Promise.all(MockFilePicker.pendingPromises)
250 return Ci.nsIFilePicker.returnOK;
253 return Ci.nsIFilePicker.returnCancel;
257 // Nothing else has to be done.
258 MockFilePicker.pendingPromises = [];
260 if (result == Ci.nsIFilePicker.returnCancel) {
264 MockFilePicker.displayDirectory = this.displayDirectory;
265 MockFilePicker.displaySpecialDirectory = this.displaySpecialDirectory;
266 MockFilePicker.shown = true;
267 if (typeof MockFilePicker.showCallback == "function") {
268 if (MockFilePicker.showCallback != this.showCallback) {
269 this.showCallback = MockFilePicker.showCallback;
270 if (Cu.isXrayWrapper(this.window)) {
271 this.showCallbackWrapped = lazy.WrapPrivileged.wrapCallback(
272 MockFilePicker.showCallback,
276 this.showCallbackWrapped = this.showCallback;
280 var returnValue = this.showCallbackWrapped(this);
281 if (typeof returnValue != "undefined") {
285 return Ci.nsIFilePicker.returnCancel;
289 return MockFilePicker.returnValue;
292 // Some additional result file can be set by the callback. Let's
293 // resolve the pending promises again.
294 return Promise.all(MockFilePicker.pendingPromises).then(
299 return Ci.nsIFilePicker.returnCancel;
304 MockFilePicker.pendingPromises = [];
306 if (aFilePickerShownCallback) {
307 aFilePickerShownCallback.done(result);
310 if (typeof MockFilePicker.afterOpenCallback == "function") {
311 Services.tm.dispatchToMainThread(() => {
312 MockFilePicker.afterOpenCallback(this);