Bug 1906125 - [devtools] Implement memory panel odd background in CSS instead of...
[gecko.git] / testing / specialpowers / content / MockFilePicker.sys.mjs
blob0ce8c25d4e906b1a6234c657edc811dc2d410d33
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/. */
5 const lazy = {};
7 ChromeUtils.defineESModuleGetters(lazy, {
8   FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
9   WrapPrivileged: "resource://testing-common/WrapPrivileged.sys.mjs",
10 });
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);
21 var oldClassID;
22 var newClassID = Services.uuid.generateUUID();
23 var newFactory = function (window) {
24   return {
25     createInstance(aIID) {
26       return new MockFilePickerInstance(window).QueryInterface(aIID);
27     },
28     QueryInterface: ChromeUtils.generateQI(["nsIFactory"]),
29   };
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,
48   window: null,
49   pendingPromises: [],
51   init(browsingContext) {
52     this.window = browsingContext.window;
54     this.reset();
55     this.factory = newFactory(this.window);
56     if (!registrar.isCIDRegistered(newClassID)) {
57       oldClassID = registrar.contractIDToCID(CONTRACT_ID);
58       registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory);
59     }
60   },
62   reset() {
63     this.appendFilterCallback = null;
64     this.appendFiltersCallback = null;
65     this.displayDirectory = null;
66     this.displaySpecialDirectory = "";
67     this.filterIndex = 0;
68     this.mode = null;
69     this.returnData = [];
70     this.returnValue = null;
71     this.showCallback = null;
72     this.afterOpenCallback = null;
73     this.shown = false;
74     this.showing = false;
75   },
77   cleanup() {
78     var previousFactory = this.factory;
79     this.reset();
80     this.factory = null;
81     if (oldClassID) {
82       registrar.unregisterFactory(newClassID, previousFactory);
83       registrar.registerFactory(oldClassID, "", CONTRACT_ID, null);
84     }
85   },
87   internalFileData(obj) {
88     return {
89       nsIFile: "nsIFile" in obj ? obj.nsIFile : null,
90       domFile: "domFile" in obj ? obj.domFile : null,
91       domDirectory: "domDirectory" in obj ? obj.domDirectory : null,
92     };
93   },
95   useAnyFile() {
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)
100       .then(
101         domFile => domFile,
102         () => null
103       )
104       // domFile can be null.
105       .then(domFile => {
106         this.returnData = [this.internalFileData({ nsIFile: file, domFile })];
107       })
108       .then(() => file);
110     this.pendingPromises = [promise];
112     // We return a promise in order to support some existing mochitests.
113     return promise;
114   },
116   useBlobFile() {
117     var blob = new this.window.Blob([]);
118     var file = new this.window.File([blob], "helloworld.txt", {
119       type: "plain/text",
120     });
121     this.returnData = [this.internalFileData({ domFile: file })];
122     this.pendingPromises = [];
123   },
125   useDirectory(aPath) {
126     var directory = new this.window.Directory(aPath);
127     var file = new lazy.FileUtils.File(aPath);
128     this.returnData = [
129       this.internalFileData({ domDirectory: directory, nsIFile: file }),
130     ];
131     this.pendingPromises = [];
132   },
134   setFiles(files) {
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 }));
141       } else {
142         let promise = this.window.File.createFromNsIFile(file, {
143           existenceCheck: false,
144         });
146         promise.then(domFile => {
147           this.returnData.push(
148             this.internalFileData({ nsIFile: file, domFile })
149           );
150         });
151         this.pendingPromises.push(promise);
152       }
153     }
154   },
156   getNsIFile() {
157     if (this.returnData.length >= 1) {
158       return this.returnData[0].nsIFile;
159     }
160     return null;
161   },
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) {
172     this.mode = aMode;
173     this.filterIndex = MockFilePicker.filterIndex;
174     this.parent = aParent;
175   },
176   appendFilter(aTitle, aFilter) {
177     if (typeof MockFilePicker.appendFilterCallback == "function") {
178       MockFilePicker.appendFilterCallback(this, aTitle, aFilter);
179     }
180   },
181   appendFilters(aFilterMask) {
182     if (typeof MockFilePicker.appendFiltersCallback == "function") {
183       MockFilePicker.appendFiltersCallback(this, aFilterMask);
184     }
185   },
186   defaultString: "",
187   defaultExtension: "",
188   parent: null,
189   filterIndex: 0,
190   displayDirectory: null,
191   displaySpecialDirectory: "",
192   get file() {
193     if (MockFilePicker.returnData.length >= 1) {
194       return MockFilePicker.returnData[0].nsIFile;
195     }
197     return null;
198   },
200   // We don't support directories here.
201   get domFileOrDirectory() {
202     if (MockFilePicker.returnData.length < 1) {
203       return null;
204     }
206     if (MockFilePicker.returnData[0].domFile) {
207       return MockFilePicker.returnData[0].domFile;
208     }
210     if (MockFilePicker.returnData[0].domDirectory) {
211       return MockFilePicker.returnData[0].domDirectory;
212     }
214     return null;
215   },
216   get fileURL() {
217     if (
218       MockFilePicker.returnData.length >= 1 &&
219       MockFilePicker.returnData[0].nsIFile
220     ) {
221       return Services.io.newFileURI(MockFilePicker.returnData[0].nsIFile);
222     }
224     return null;
225   },
226   *getFiles(asDOM) {
227     for (let d of MockFilePicker.returnData) {
228       if (asDOM) {
229         yield d.domFile || d.domDirectory;
230       } else if (d.nsIFile) {
231         yield d.nsIFile;
232       } else {
233         throw Components.Exception("", Cr.NS_ERROR_FAILURE);
234       }
235     }
236   },
237   get files() {
238     return this.getFiles(false);
239   },
240   get domFileOrDirectoryEnumerator() {
241     return this.getFiles(true);
242   },
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)
248         .then(
249           () => {
250             return Ci.nsIFilePicker.returnOK;
251           },
252           () => {
253             return Ci.nsIFilePicker.returnCancel;
254           }
255         )
256         .then(result => {
257           // Nothing else has to be done.
258           MockFilePicker.pendingPromises = [];
260           if (result == Ci.nsIFilePicker.returnCancel) {
261             return result;
262           }
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,
273                   this.window
274                 );
275               } else {
276                 this.showCallbackWrapped = this.showCallback;
277               }
278             }
279             try {
280               var returnValue = this.showCallbackWrapped(this);
281               if (typeof returnValue != "undefined") {
282                 return returnValue;
283               }
284             } catch (ex) {
285               return Ci.nsIFilePicker.returnCancel;
286             }
287           }
289           return MockFilePicker.returnValue;
290         })
291         .then(result => {
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(
295             () => {
296               return result;
297             },
298             () => {
299               return Ci.nsIFilePicker.returnCancel;
300             }
301           );
302         })
303         .then(result => {
304           MockFilePicker.pendingPromises = [];
306           if (aFilePickerShownCallback) {
307             aFilePickerShownCallback.done(result);
308           }
310           if (typeof MockFilePicker.afterOpenCallback == "function") {
311             Services.tm.dispatchToMainThread(() => {
312               MockFilePicker.afterOpenCallback(this);
313             });
314           }
315         });
316     });
317   },