|
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 const Ci = Components.interfaces; |
|
6 const Cu = Components.utils; |
|
7 const Cc = Components.classes; |
|
8 |
|
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
10 Cu.import("resource://gre/modules/Services.jsm"); |
|
11 Cu.import("resource://gre/modules/FileUtils.jsm"); |
|
12 |
|
13 function FilePicker() { |
|
14 } |
|
15 |
|
16 FilePicker.prototype = { |
|
17 _mimeTypeFilter: 0, |
|
18 _extensionsFilter: "", |
|
19 _defaultString: "", |
|
20 _domWin: null, |
|
21 _defaultExtension: null, |
|
22 _displayDirectory: null, |
|
23 _filePath: null, |
|
24 _promptActive: false, |
|
25 _filterIndex: 0, |
|
26 _addToRecentDocs: false, |
|
27 |
|
28 init: function(aParent, aTitle, aMode) { |
|
29 this._domWin = aParent; |
|
30 this._mode = aMode; |
|
31 Services.obs.addObserver(this, "FilePicker:Result", false); |
|
32 |
|
33 let idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); |
|
34 this.guid = idService.generateUUID().toString(); |
|
35 |
|
36 if (aMode != Ci.nsIFilePicker.modeOpen && aMode != Ci.nsIFilePicker.modeOpenMultiple) |
|
37 throw Components.results.NS_ERROR_NOT_IMPLEMENTED; |
|
38 }, |
|
39 |
|
40 appendFilters: function(aFilterMask) { |
|
41 if (aFilterMask & Ci.nsIFilePicker.filterAudio) { |
|
42 this._mimeTypeFilter = "audio/*"; |
|
43 return; |
|
44 } |
|
45 |
|
46 if (aFilterMask & Ci.nsIFilePicker.filterImages) { |
|
47 this._mimeTypeFilter = "image/*"; |
|
48 return; |
|
49 } |
|
50 |
|
51 if (aFilterMask & Ci.nsIFilePicker.filterVideo) { |
|
52 this._mimeTypeFilter = "video/*"; |
|
53 return; |
|
54 } |
|
55 |
|
56 if (aFilterMask & Ci.nsIFilePicker.filterAll) { |
|
57 this._mimeTypeFilter = "*/*"; |
|
58 return; |
|
59 } |
|
60 |
|
61 /* From BaseFilePicker.cpp */ |
|
62 if (aFilterMask & Ci.nsIFilePicker.filterHTML) { |
|
63 this.appendFilter("*.html; *.htm; *.shtml; *.xhtml"); |
|
64 } |
|
65 if (aFilterMask & Ci.nsIFilePicker.filterText) { |
|
66 this.appendFilter("*.txt; *.text"); |
|
67 } |
|
68 |
|
69 if (aFilterMask & Ci.nsIFilePicker.filterXML) { |
|
70 this.appendFilter("*.xml"); |
|
71 } |
|
72 |
|
73 if (aFilterMask & Ci.nsIFilePicker.xulFilter) { |
|
74 this.appendFilter("*.xul"); |
|
75 } |
|
76 |
|
77 if (aFilterMask & Ci.nsIFilePicker.xulFilter) { |
|
78 this.appendFilter("..apps"); |
|
79 } |
|
80 }, |
|
81 |
|
82 appendFilter: function(title, filter) { |
|
83 if (this._extensionsFilter) |
|
84 this._extensionsFilter += ", "; |
|
85 this._extensionsFilter += filter; |
|
86 }, |
|
87 |
|
88 get defaultString() { |
|
89 return this._defaultString; |
|
90 }, |
|
91 |
|
92 set defaultString(defaultString) { |
|
93 this._defaultString = defaultString; |
|
94 }, |
|
95 |
|
96 get defaultExtension() { |
|
97 return this._defaultExtension; |
|
98 }, |
|
99 |
|
100 set defaultExtension(defaultExtension) { |
|
101 this._defaultExtension = defaultExtension; |
|
102 }, |
|
103 |
|
104 get filterIndex() { |
|
105 return this._filterIndex; |
|
106 }, |
|
107 |
|
108 set filterIndex(val) { |
|
109 this._filterIndex = val; |
|
110 }, |
|
111 |
|
112 get displayDirectory() { |
|
113 return this._displayDirectory; |
|
114 }, |
|
115 |
|
116 set displayDirectory(dir) { |
|
117 this._displayDirectory = dir; |
|
118 }, |
|
119 |
|
120 get file() { |
|
121 if (!this._filePath) { |
|
122 return null; |
|
123 } |
|
124 |
|
125 return new FileUtils.File(this._filePath); |
|
126 }, |
|
127 |
|
128 get fileURL() { |
|
129 let file = this.getFile(); |
|
130 return Services.io.newFileURI(file); |
|
131 }, |
|
132 |
|
133 get files() { |
|
134 return this.getEnumerator([this.file], function(file) { |
|
135 return file; |
|
136 }); |
|
137 }, |
|
138 |
|
139 get domfile() { |
|
140 let f = this.file; |
|
141 if (!f) { |
|
142 return null; |
|
143 } |
|
144 return File(f); |
|
145 }, |
|
146 |
|
147 get domfiles() { |
|
148 return this.getEnumerator([this.file], function(file) { |
|
149 return File(file); |
|
150 }); |
|
151 }, |
|
152 |
|
153 get addToRecentDocs() { |
|
154 return this._addToRecentDocs; |
|
155 }, |
|
156 |
|
157 set addToRecentDocs(val) { |
|
158 this._addToRecentDocs = val; |
|
159 }, |
|
160 |
|
161 get mode() { |
|
162 return this._mode; |
|
163 }, |
|
164 |
|
165 show: function() { |
|
166 if (this._domWin) { |
|
167 this.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog"); |
|
168 let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); |
|
169 winUtils.enterModalState(); |
|
170 } |
|
171 |
|
172 this._promptActive = true; |
|
173 this._sendMessage(); |
|
174 |
|
175 let thread = Services.tm.currentThread; |
|
176 while (this._promptActive) |
|
177 thread.processNextEvent(true); |
|
178 delete this._promptActive; |
|
179 |
|
180 if (this._domWin) { |
|
181 let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); |
|
182 winUtils.leaveModalState(); |
|
183 this.fireDialogEvent(this._domWin, "DOMModalDialogClosed"); |
|
184 } |
|
185 |
|
186 if (this._filePath) |
|
187 return Ci.nsIFilePicker.returnOK; |
|
188 |
|
189 return Ci.nsIFilePicker.returnCancel; |
|
190 }, |
|
191 |
|
192 open: function(callback) { |
|
193 this._callback = callback; |
|
194 this._sendMessage(); |
|
195 }, |
|
196 |
|
197 _sendMessage: function() { |
|
198 let msg = { |
|
199 type: "FilePicker:Show", |
|
200 guid: this.guid, |
|
201 }; |
|
202 |
|
203 // Knowing the window lets us destroy any temp files when the tab is closed |
|
204 // Other consumers of the file picker may have to either wait for Android |
|
205 // to clean up the temp dir (not guaranteed) or clean up after themselves. |
|
206 let win = Services.wm.getMostRecentWindow('navigator:browser'); |
|
207 let tab = win.BrowserApp.getTabForWindow(this._domWin.top) |
|
208 if (tab) { |
|
209 msg.tabId = tab.id; |
|
210 } |
|
211 |
|
212 if (!this._extensionsFilter && !this._mimeTypeFilter) { |
|
213 // If neither filters is set show anything we can. |
|
214 msg.mode = "mimeType"; |
|
215 msg.mimeType = "*/*"; |
|
216 } else if (this._extensionsFilter) { |
|
217 msg.mode = "extension"; |
|
218 msg.extensions = this._extensionsFilter; |
|
219 } else { |
|
220 msg.mode = "mimeType"; |
|
221 msg.mimeType = this._mimeTypeFilter; |
|
222 } |
|
223 |
|
224 this.sendMessageToJava(msg); |
|
225 }, |
|
226 |
|
227 sendMessageToJava: function(aMsg) { |
|
228 Services.androidBridge.handleGeckoMessage(aMsg); |
|
229 }, |
|
230 |
|
231 observe: function(aSubject, aTopic, aData) { |
|
232 let data = JSON.parse(aData); |
|
233 if (data.guid != this.guid) |
|
234 return; |
|
235 |
|
236 this._filePath = null; |
|
237 if (data.file) |
|
238 this._filePath = data.file; |
|
239 |
|
240 this._promptActive = false; |
|
241 |
|
242 if (this._callback) { |
|
243 this._callback.done(this._filePath ? Ci.nsIFilePicker.returnOK : Ci.nsIFilePicker.returnCancel); |
|
244 } |
|
245 delete this._callback; |
|
246 }, |
|
247 |
|
248 getEnumerator: function(files, mapFunction) { |
|
249 return { |
|
250 QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]), |
|
251 mFiles: files, |
|
252 mIndex: 0, |
|
253 hasMoreElements: function() { |
|
254 return (this.mIndex < this.mFiles.length); |
|
255 }, |
|
256 getNext: function() { |
|
257 if (this.mIndex >= this.mFiles.length) { |
|
258 throw Components.results.NS_ERROR_FAILURE; |
|
259 } |
|
260 return mapFunction(this.mFiles[this.mIndex++]); |
|
261 } |
|
262 }; |
|
263 }, |
|
264 |
|
265 fireDialogEvent: function(aDomWin, aEventName) { |
|
266 // accessing the document object can throw if this window no longer exists. See bug 789888. |
|
267 try { |
|
268 if (!aDomWin.document) |
|
269 return; |
|
270 let event = aDomWin.document.createEvent("Events"); |
|
271 event.initEvent(aEventName, true, true); |
|
272 let winUtils = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor) |
|
273 .getInterface(Ci.nsIDOMWindowUtils); |
|
274 winUtils.dispatchEventToChromeOnly(aDomWin, event); |
|
275 } catch(ex) { |
|
276 } |
|
277 }, |
|
278 |
|
279 classID: Components.ID("{18a4e042-7c7c-424b-a583-354e68553a7f}"), |
|
280 QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker, Ci.nsIObserver]) |
|
281 }; |
|
282 |
|
283 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FilePicker]); |