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