michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: /* michael@0: * No magic constructor behaviour, as is de rigeur for XPCOM. michael@0: * If you must perform some initialization, and it could possibly fail (even michael@0: * due to an out-of-memory condition), you should use an Init method, which michael@0: * can convey failure appropriately (thrown exception in JS, michael@0: * NS_FAILED(nsresult) return in C++). michael@0: * michael@0: * In JS, you can actually cheat, because a thrown exception will cause the michael@0: * CreateInstance call to fail in turn, but not all languages are so lucky. michael@0: * (Though ANSI C++ provides exceptions, they are verboten in Mozilla code michael@0: * for portability reasons -- and even when you're building completely michael@0: * platform-specific code, you can't throw across an XPCOM method boundary.) michael@0: */ michael@0: michael@0: const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; michael@0: michael@0: // FIXME: improve this list of filters. michael@0: const IMAGE_FILTERS = ['image/gif', 'image/jpeg', 'image/pjpeg', michael@0: 'image/png', 'image/svg+xml', 'image/tiff', michael@0: 'image/vnd.microsoft.icon']; michael@0: const VIDEO_FILTERS = ['video/mpeg', 'video/mp4', 'video/ogg', michael@0: 'video/quicktime', 'video/webm', 'video/x-matroska', michael@0: 'video/x-ms-wmv', 'video/x-flv']; michael@0: const AUDIO_FILTERS = ['audio/basic', 'audio/L24', 'audio/mp4', michael@0: 'audio/mpeg', 'audio/ogg', 'audio/vorbis', michael@0: 'audio/vnd.rn-realaudio', 'audio/vnd.wave', michael@0: 'audio/webm']; michael@0: michael@0: Cu.import('resource://gre/modules/XPCOMUtils.jsm'); michael@0: Cu.import("resource://gre/modules/osfile.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, 'cpmm', michael@0: '@mozilla.org/childprocessmessagemanager;1', michael@0: 'nsIMessageSender'); michael@0: michael@0: function FilePicker() { michael@0: } michael@0: michael@0: FilePicker.prototype = { michael@0: classID: Components.ID('{436ff8f9-0acc-4b11-8ec7-e293efba3141}'), michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker]), michael@0: michael@0: /* members */ michael@0: michael@0: mParent: undefined, michael@0: mExtraProps: undefined, michael@0: mFilterTypes: undefined, michael@0: mFileEnumerator: undefined, michael@0: mFilePickerShownCallback: undefined, michael@0: michael@0: /* methods */ michael@0: michael@0: init: function(parent, title, mode) { michael@0: this.mParent = parent; michael@0: this.mExtraProps = {}; michael@0: this.mFilterTypes = []; michael@0: this.mMode = mode; michael@0: michael@0: if (mode != Ci.nsIFilePicker.modeOpen && michael@0: mode != Ci.nsIFilePicker.modeOpenMultiple) { michael@0: throw Cr.NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: }, michael@0: michael@0: /* readonly attribute nsILocalFile file - not implemented; */ michael@0: /* readonly attribute nsISimpleEnumerator files - not implemented; */ michael@0: /* readonly attribute nsIURI fileURL - not implemented; */ michael@0: michael@0: get domfiles() { michael@0: return this.mFilesEnumerator; michael@0: }, michael@0: michael@0: get domfile() { michael@0: return this.mFilesEnumerator ? this.mFilesEnumerator.mFiles[0] : null; michael@0: }, michael@0: michael@0: get mode() { michael@0: return this.mMode; michael@0: }, michael@0: michael@0: appendFilters: function(filterMask) { michael@0: // Ci.nsIFilePicker.filterHTML is not supported michael@0: // Ci.nsIFilePicker.filterText is not supported michael@0: michael@0: if (filterMask & Ci.nsIFilePicker.filterImages) { michael@0: this.mFilterTypes = this.mFilterTypes.concat(IMAGE_FILTERS); michael@0: // This property is needed for the gallery app pick activity. michael@0: this.mExtraProps['nocrop'] = true; michael@0: } michael@0: michael@0: // Ci.nsIFilePicker.filterXML is not supported michael@0: // Ci.nsIFilePicker.filterXUL is not supported michael@0: // Ci.nsIFilePicker.filterApps is not supported michael@0: // Ci.nsIFilePicker.filterAllowURLs is not supported michael@0: michael@0: if (filterMask & Ci.nsIFilePicker.filterVideo) { michael@0: this.mFilterTypes = this.mFilterTypes.concat(VIDEO_FILTERS); michael@0: } michael@0: michael@0: if (filterMask & Ci.nsIFilePicker.filterAudio) { michael@0: this.mFilterTypes = this.mFilterTypes.concat(AUDIO_FILTERS); michael@0: } michael@0: michael@0: if (filterMask & Ci.nsIFilePicker.filterAll) { michael@0: // This property is needed for the gallery app pick activity. michael@0: this.mExtraProps['nocrop'] = true; michael@0: } michael@0: }, michael@0: michael@0: appendFilter: function(title, extensions) { michael@0: // pick activity doesn't support extensions michael@0: }, michael@0: michael@0: open: function(aFilePickerShownCallback) { michael@0: this.mFilePickerShownCallback = aFilePickerShownCallback; michael@0: michael@0: cpmm.addMessageListener('file-picked', this); michael@0: michael@0: let detail = {}; michael@0: if (this.mFilterTypes) { michael@0: detail.type = this.mFilterTypes; michael@0: } michael@0: michael@0: for (let prop in this.mExtraProps) { michael@0: if (!(prop in detail)) { michael@0: detail[prop] = this.mExtraProps[prop]; michael@0: } michael@0: } michael@0: michael@0: cpmm.sendAsyncMessage('file-picker', detail); michael@0: }, michael@0: michael@0: fireSuccess: function(file) { michael@0: this.mFilesEnumerator = { michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]), michael@0: michael@0: mFiles: [file], michael@0: mIndex: 0, michael@0: michael@0: hasMoreElements: function() { michael@0: return (this.mIndex < this.mFiles.length); michael@0: }, 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 this.mFiles[this.mIndex++]; michael@0: } michael@0: }; michael@0: michael@0: if (this.mFilePickerShownCallback) { michael@0: this.mFilePickerShownCallback.done(Ci.nsIFilePicker.returnOK); michael@0: this.mFilePickerShownCallback = null; michael@0: } michael@0: }, michael@0: michael@0: fireError: function() { michael@0: if (this.mFilePickerShownCallback) { michael@0: this.mFilePickerShownCallback.done(Ci.nsIFilePicker.returnCancel); michael@0: this.mFilePickerShownCallback = null; michael@0: } michael@0: }, michael@0: michael@0: receiveMessage: function(message) { michael@0: if (message.name !== 'file-picked') { michael@0: return; michael@0: } michael@0: michael@0: cpmm.removeMessageListener('file-picked', this); michael@0: michael@0: let data = message.data; michael@0: if (!data.success || !data.result.blob) { michael@0: this.fireError(); michael@0: return; michael@0: } michael@0: michael@0: // The name to be shown can be part of the message, or can be taken from michael@0: // the DOMFile (if the blob is a DOMFile). michael@0: let name = data.result.name; michael@0: if (!name && michael@0: (data.result.blob instanceof this.mParent.File) && michael@0: data.result.blob.name) { michael@0: name = data.result.blob.name; michael@0: } michael@0: michael@0: // Let's try to remove the full path and take just the filename. michael@0: if (name) { michael@0: let names = OS.Path.split(name); michael@0: name = names.components[names.components.length - 1]; michael@0: } michael@0: michael@0: // the fallback is a filename composed by 'blob' + extension. michael@0: if (!name) { michael@0: name = 'blob'; michael@0: if (data.result.blob.type) { michael@0: let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); michael@0: let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, ''); michael@0: if (mimeInfo) { michael@0: name += '.' + mimeInfo.primaryExtension; michael@0: } michael@0: } michael@0: } michael@0: michael@0: let file = new this.mParent.File(data.result.blob, michael@0: { name: name, michael@0: type: data.result.blob.type }); michael@0: michael@0: if (file) { michael@0: this.fireSuccess(file); michael@0: } else { michael@0: this.fireError(); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FilePicker]);