michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: const Cc = Components.classes; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/FileUtils.jsm"); michael@0: michael@0: function FilePicker() { michael@0: } michael@0: michael@0: FilePicker.prototype = { michael@0: _mimeTypeFilter: 0, michael@0: _extensionsFilter: "", michael@0: _defaultString: "", michael@0: _domWin: null, michael@0: _defaultExtension: null, michael@0: _displayDirectory: null, michael@0: _filePath: null, michael@0: _promptActive: false, michael@0: _filterIndex: 0, michael@0: _addToRecentDocs: false, michael@0: michael@0: init: function(aParent, aTitle, aMode) { michael@0: this._domWin = aParent; michael@0: this._mode = aMode; michael@0: Services.obs.addObserver(this, "FilePicker:Result", false); michael@0: michael@0: let idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); michael@0: this.guid = idService.generateUUID().toString(); michael@0: michael@0: if (aMode != Ci.nsIFilePicker.modeOpen && aMode != Ci.nsIFilePicker.modeOpenMultiple) michael@0: throw Components.results.NS_ERROR_NOT_IMPLEMENTED; michael@0: }, michael@0: michael@0: appendFilters: function(aFilterMask) { michael@0: if (aFilterMask & Ci.nsIFilePicker.filterAudio) { michael@0: this._mimeTypeFilter = "audio/*"; michael@0: return; michael@0: } michael@0: michael@0: if (aFilterMask & Ci.nsIFilePicker.filterImages) { michael@0: this._mimeTypeFilter = "image/*"; michael@0: return; michael@0: } michael@0: michael@0: if (aFilterMask & Ci.nsIFilePicker.filterVideo) { michael@0: this._mimeTypeFilter = "video/*"; michael@0: return; michael@0: } michael@0: michael@0: if (aFilterMask & Ci.nsIFilePicker.filterAll) { michael@0: this._mimeTypeFilter = "*/*"; michael@0: return; michael@0: } michael@0: michael@0: /* From BaseFilePicker.cpp */ michael@0: if (aFilterMask & Ci.nsIFilePicker.filterHTML) { michael@0: this.appendFilter("*.html; *.htm; *.shtml; *.xhtml"); michael@0: } michael@0: if (aFilterMask & Ci.nsIFilePicker.filterText) { michael@0: this.appendFilter("*.txt; *.text"); michael@0: } michael@0: michael@0: if (aFilterMask & Ci.nsIFilePicker.filterXML) { michael@0: this.appendFilter("*.xml"); michael@0: } michael@0: michael@0: if (aFilterMask & Ci.nsIFilePicker.xulFilter) { michael@0: this.appendFilter("*.xul"); michael@0: } michael@0: michael@0: if (aFilterMask & Ci.nsIFilePicker.xulFilter) { michael@0: this.appendFilter("..apps"); michael@0: } michael@0: }, michael@0: michael@0: appendFilter: function(title, filter) { michael@0: if (this._extensionsFilter) michael@0: this._extensionsFilter += ", "; michael@0: this._extensionsFilter += filter; michael@0: }, michael@0: michael@0: get defaultString() { michael@0: return this._defaultString; michael@0: }, michael@0: michael@0: set defaultString(defaultString) { michael@0: this._defaultString = defaultString; michael@0: }, michael@0: michael@0: get defaultExtension() { michael@0: return this._defaultExtension; michael@0: }, michael@0: michael@0: set defaultExtension(defaultExtension) { michael@0: this._defaultExtension = defaultExtension; michael@0: }, michael@0: michael@0: get filterIndex() { michael@0: return this._filterIndex; michael@0: }, michael@0: michael@0: set filterIndex(val) { michael@0: this._filterIndex = val; michael@0: }, michael@0: michael@0: get displayDirectory() { michael@0: return this._displayDirectory; michael@0: }, michael@0: michael@0: set displayDirectory(dir) { michael@0: this._displayDirectory = dir; michael@0: }, michael@0: michael@0: get file() { michael@0: if (!this._filePath) { michael@0: return null; michael@0: } michael@0: michael@0: return new FileUtils.File(this._filePath); michael@0: }, michael@0: michael@0: get fileURL() { michael@0: let file = this.getFile(); michael@0: return Services.io.newFileURI(file); michael@0: }, michael@0: michael@0: get files() { michael@0: return this.getEnumerator([this.file], function(file) { michael@0: return file; michael@0: }); michael@0: }, michael@0: michael@0: get domfile() { michael@0: let f = this.file; michael@0: if (!f) { michael@0: return null; michael@0: } michael@0: return File(f); michael@0: }, michael@0: michael@0: get domfiles() { michael@0: return this.getEnumerator([this.file], function(file) { michael@0: return File(file); michael@0: }); michael@0: }, michael@0: michael@0: get addToRecentDocs() { michael@0: return this._addToRecentDocs; michael@0: }, michael@0: michael@0: set addToRecentDocs(val) { michael@0: this._addToRecentDocs = val; michael@0: }, michael@0: michael@0: get mode() { michael@0: return this._mode; michael@0: }, michael@0: michael@0: show: function() { michael@0: if (this._domWin) { michael@0: this.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog"); michael@0: let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); michael@0: winUtils.enterModalState(); michael@0: } michael@0: michael@0: this._promptActive = true; michael@0: this._sendMessage(); michael@0: michael@0: let thread = Services.tm.currentThread; michael@0: while (this._promptActive) michael@0: thread.processNextEvent(true); michael@0: delete this._promptActive; michael@0: michael@0: if (this._domWin) { michael@0: let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); michael@0: winUtils.leaveModalState(); michael@0: this.fireDialogEvent(this._domWin, "DOMModalDialogClosed"); michael@0: } michael@0: michael@0: if (this._filePath) michael@0: return Ci.nsIFilePicker.returnOK; michael@0: michael@0: return Ci.nsIFilePicker.returnCancel; michael@0: }, michael@0: michael@0: open: function(callback) { michael@0: this._callback = callback; michael@0: this._sendMessage(); michael@0: }, michael@0: michael@0: _sendMessage: function() { michael@0: let msg = { michael@0: type: "FilePicker:Show", michael@0: guid: this.guid, michael@0: }; michael@0: michael@0: // Knowing the window lets us destroy any temp files when the tab is closed michael@0: // Other consumers of the file picker may have to either wait for Android michael@0: // to clean up the temp dir (not guaranteed) or clean up after themselves. michael@0: let win = Services.wm.getMostRecentWindow('navigator:browser'); michael@0: let tab = win.BrowserApp.getTabForWindow(this._domWin.top) michael@0: if (tab) { michael@0: msg.tabId = tab.id; michael@0: } michael@0: michael@0: if (!this._extensionsFilter && !this._mimeTypeFilter) { michael@0: // If neither filters is set show anything we can. michael@0: msg.mode = "mimeType"; michael@0: msg.mimeType = "*/*"; michael@0: } else if (this._extensionsFilter) { michael@0: msg.mode = "extension"; michael@0: msg.extensions = this._extensionsFilter; michael@0: } else { michael@0: msg.mode = "mimeType"; michael@0: msg.mimeType = this._mimeTypeFilter; michael@0: } michael@0: michael@0: this.sendMessageToJava(msg); michael@0: }, michael@0: michael@0: sendMessageToJava: function(aMsg) { michael@0: Services.androidBridge.handleGeckoMessage(aMsg); michael@0: }, michael@0: michael@0: observe: function(aSubject, aTopic, aData) { michael@0: let data = JSON.parse(aData); michael@0: if (data.guid != this.guid) michael@0: return; michael@0: michael@0: this._filePath = null; michael@0: if (data.file) michael@0: this._filePath = data.file; michael@0: michael@0: this._promptActive = false; michael@0: michael@0: if (this._callback) { michael@0: this._callback.done(this._filePath ? Ci.nsIFilePicker.returnOK : Ci.nsIFilePicker.returnCancel); michael@0: } michael@0: delete this._callback; michael@0: }, michael@0: michael@0: getEnumerator: function(files, mapFunction) { michael@0: return { michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]), michael@0: mFiles: files, michael@0: mIndex: 0, michael@0: hasMoreElements: function() { michael@0: return (this.mIndex < this.mFiles.length); michael@0: }, michael@0: getNext: function() { michael@0: if (this.mIndex >= this.mFiles.length) { michael@0: throw Components.results.NS_ERROR_FAILURE; michael@0: } michael@0: return mapFunction(this.mFiles[this.mIndex++]); michael@0: } michael@0: }; michael@0: }, michael@0: michael@0: fireDialogEvent: function(aDomWin, aEventName) { michael@0: // accessing the document object can throw if this window no longer exists. See bug 789888. michael@0: try { michael@0: if (!aDomWin.document) michael@0: return; michael@0: let event = aDomWin.document.createEvent("Events"); michael@0: event.initEvent(aEventName, true, true); michael@0: let winUtils = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils); michael@0: winUtils.dispatchEventToChromeOnly(aDomWin, event); michael@0: } catch(ex) { michael@0: } michael@0: }, michael@0: michael@0: classID: Components.ID("{18a4e042-7c7c-424b-a583-354e68553a7f}"), michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker, Ci.nsIObserver]) michael@0: }; michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FilePicker]);