Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | * No magic constructor behaviour, as is de rigeur for XPCOM. |
michael@0 | 8 | * If you must perform some initialization, and it could possibly fail (even |
michael@0 | 9 | * due to an out-of-memory condition), you should use an Init method, which |
michael@0 | 10 | * can convey failure appropriately (thrown exception in JS, |
michael@0 | 11 | * NS_FAILED(nsresult) return in C++). |
michael@0 | 12 | * |
michael@0 | 13 | * In JS, you can actually cheat, because a thrown exception will cause the |
michael@0 | 14 | * CreateInstance call to fail in turn, but not all languages are so lucky. |
michael@0 | 15 | * (Though ANSI C++ provides exceptions, they are verboten in Mozilla code |
michael@0 | 16 | * for portability reasons -- and even when you're building completely |
michael@0 | 17 | * platform-specific code, you can't throw across an XPCOM method boundary.) |
michael@0 | 18 | */ |
michael@0 | 19 | |
michael@0 | 20 | const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; |
michael@0 | 21 | |
michael@0 | 22 | // FIXME: improve this list of filters. |
michael@0 | 23 | const IMAGE_FILTERS = ['image/gif', 'image/jpeg', 'image/pjpeg', |
michael@0 | 24 | 'image/png', 'image/svg+xml', 'image/tiff', |
michael@0 | 25 | 'image/vnd.microsoft.icon']; |
michael@0 | 26 | const VIDEO_FILTERS = ['video/mpeg', 'video/mp4', 'video/ogg', |
michael@0 | 27 | 'video/quicktime', 'video/webm', 'video/x-matroska', |
michael@0 | 28 | 'video/x-ms-wmv', 'video/x-flv']; |
michael@0 | 29 | const AUDIO_FILTERS = ['audio/basic', 'audio/L24', 'audio/mp4', |
michael@0 | 30 | 'audio/mpeg', 'audio/ogg', 'audio/vorbis', |
michael@0 | 31 | 'audio/vnd.rn-realaudio', 'audio/vnd.wave', |
michael@0 | 32 | 'audio/webm']; |
michael@0 | 33 | |
michael@0 | 34 | Cu.import('resource://gre/modules/XPCOMUtils.jsm'); |
michael@0 | 35 | Cu.import("resource://gre/modules/osfile.jsm"); |
michael@0 | 36 | |
michael@0 | 37 | XPCOMUtils.defineLazyServiceGetter(this, 'cpmm', |
michael@0 | 38 | '@mozilla.org/childprocessmessagemanager;1', |
michael@0 | 39 | 'nsIMessageSender'); |
michael@0 | 40 | |
michael@0 | 41 | function FilePicker() { |
michael@0 | 42 | } |
michael@0 | 43 | |
michael@0 | 44 | FilePicker.prototype = { |
michael@0 | 45 | classID: Components.ID('{436ff8f9-0acc-4b11-8ec7-e293efba3141}'), |
michael@0 | 46 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker]), |
michael@0 | 47 | |
michael@0 | 48 | /* members */ |
michael@0 | 49 | |
michael@0 | 50 | mParent: undefined, |
michael@0 | 51 | mExtraProps: undefined, |
michael@0 | 52 | mFilterTypes: undefined, |
michael@0 | 53 | mFileEnumerator: undefined, |
michael@0 | 54 | mFilePickerShownCallback: undefined, |
michael@0 | 55 | |
michael@0 | 56 | /* methods */ |
michael@0 | 57 | |
michael@0 | 58 | init: function(parent, title, mode) { |
michael@0 | 59 | this.mParent = parent; |
michael@0 | 60 | this.mExtraProps = {}; |
michael@0 | 61 | this.mFilterTypes = []; |
michael@0 | 62 | this.mMode = mode; |
michael@0 | 63 | |
michael@0 | 64 | if (mode != Ci.nsIFilePicker.modeOpen && |
michael@0 | 65 | mode != Ci.nsIFilePicker.modeOpenMultiple) { |
michael@0 | 66 | throw Cr.NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 67 | } |
michael@0 | 68 | }, |
michael@0 | 69 | |
michael@0 | 70 | /* readonly attribute nsILocalFile file - not implemented; */ |
michael@0 | 71 | /* readonly attribute nsISimpleEnumerator files - not implemented; */ |
michael@0 | 72 | /* readonly attribute nsIURI fileURL - not implemented; */ |
michael@0 | 73 | |
michael@0 | 74 | get domfiles() { |
michael@0 | 75 | return this.mFilesEnumerator; |
michael@0 | 76 | }, |
michael@0 | 77 | |
michael@0 | 78 | get domfile() { |
michael@0 | 79 | return this.mFilesEnumerator ? this.mFilesEnumerator.mFiles[0] : null; |
michael@0 | 80 | }, |
michael@0 | 81 | |
michael@0 | 82 | get mode() { |
michael@0 | 83 | return this.mMode; |
michael@0 | 84 | }, |
michael@0 | 85 | |
michael@0 | 86 | appendFilters: function(filterMask) { |
michael@0 | 87 | // Ci.nsIFilePicker.filterHTML is not supported |
michael@0 | 88 | // Ci.nsIFilePicker.filterText is not supported |
michael@0 | 89 | |
michael@0 | 90 | if (filterMask & Ci.nsIFilePicker.filterImages) { |
michael@0 | 91 | this.mFilterTypes = this.mFilterTypes.concat(IMAGE_FILTERS); |
michael@0 | 92 | // This property is needed for the gallery app pick activity. |
michael@0 | 93 | this.mExtraProps['nocrop'] = true; |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | // Ci.nsIFilePicker.filterXML is not supported |
michael@0 | 97 | // Ci.nsIFilePicker.filterXUL is not supported |
michael@0 | 98 | // Ci.nsIFilePicker.filterApps is not supported |
michael@0 | 99 | // Ci.nsIFilePicker.filterAllowURLs is not supported |
michael@0 | 100 | |
michael@0 | 101 | if (filterMask & Ci.nsIFilePicker.filterVideo) { |
michael@0 | 102 | this.mFilterTypes = this.mFilterTypes.concat(VIDEO_FILTERS); |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | if (filterMask & Ci.nsIFilePicker.filterAudio) { |
michael@0 | 106 | this.mFilterTypes = this.mFilterTypes.concat(AUDIO_FILTERS); |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | if (filterMask & Ci.nsIFilePicker.filterAll) { |
michael@0 | 110 | // This property is needed for the gallery app pick activity. |
michael@0 | 111 | this.mExtraProps['nocrop'] = true; |
michael@0 | 112 | } |
michael@0 | 113 | }, |
michael@0 | 114 | |
michael@0 | 115 | appendFilter: function(title, extensions) { |
michael@0 | 116 | // pick activity doesn't support extensions |
michael@0 | 117 | }, |
michael@0 | 118 | |
michael@0 | 119 | open: function(aFilePickerShownCallback) { |
michael@0 | 120 | this.mFilePickerShownCallback = aFilePickerShownCallback; |
michael@0 | 121 | |
michael@0 | 122 | cpmm.addMessageListener('file-picked', this); |
michael@0 | 123 | |
michael@0 | 124 | let detail = {}; |
michael@0 | 125 | if (this.mFilterTypes) { |
michael@0 | 126 | detail.type = this.mFilterTypes; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | for (let prop in this.mExtraProps) { |
michael@0 | 130 | if (!(prop in detail)) { |
michael@0 | 131 | detail[prop] = this.mExtraProps[prop]; |
michael@0 | 132 | } |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | cpmm.sendAsyncMessage('file-picker', detail); |
michael@0 | 136 | }, |
michael@0 | 137 | |
michael@0 | 138 | fireSuccess: function(file) { |
michael@0 | 139 | this.mFilesEnumerator = { |
michael@0 | 140 | QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]), |
michael@0 | 141 | |
michael@0 | 142 | mFiles: [file], |
michael@0 | 143 | mIndex: 0, |
michael@0 | 144 | |
michael@0 | 145 | hasMoreElements: function() { |
michael@0 | 146 | return (this.mIndex < this.mFiles.length); |
michael@0 | 147 | }, |
michael@0 | 148 | |
michael@0 | 149 | getNext: function() { |
michael@0 | 150 | if (this.mIndex >= this.mFiles.length) { |
michael@0 | 151 | throw Components.results.NS_ERROR_FAILURE; |
michael@0 | 152 | } |
michael@0 | 153 | return this.mFiles[this.mIndex++]; |
michael@0 | 154 | } |
michael@0 | 155 | }; |
michael@0 | 156 | |
michael@0 | 157 | if (this.mFilePickerShownCallback) { |
michael@0 | 158 | this.mFilePickerShownCallback.done(Ci.nsIFilePicker.returnOK); |
michael@0 | 159 | this.mFilePickerShownCallback = null; |
michael@0 | 160 | } |
michael@0 | 161 | }, |
michael@0 | 162 | |
michael@0 | 163 | fireError: function() { |
michael@0 | 164 | if (this.mFilePickerShownCallback) { |
michael@0 | 165 | this.mFilePickerShownCallback.done(Ci.nsIFilePicker.returnCancel); |
michael@0 | 166 | this.mFilePickerShownCallback = null; |
michael@0 | 167 | } |
michael@0 | 168 | }, |
michael@0 | 169 | |
michael@0 | 170 | receiveMessage: function(message) { |
michael@0 | 171 | if (message.name !== 'file-picked') { |
michael@0 | 172 | return; |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | cpmm.removeMessageListener('file-picked', this); |
michael@0 | 176 | |
michael@0 | 177 | let data = message.data; |
michael@0 | 178 | if (!data.success || !data.result.blob) { |
michael@0 | 179 | this.fireError(); |
michael@0 | 180 | return; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | // The name to be shown can be part of the message, or can be taken from |
michael@0 | 184 | // the DOMFile (if the blob is a DOMFile). |
michael@0 | 185 | let name = data.result.name; |
michael@0 | 186 | if (!name && |
michael@0 | 187 | (data.result.blob instanceof this.mParent.File) && |
michael@0 | 188 | data.result.blob.name) { |
michael@0 | 189 | name = data.result.blob.name; |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | // Let's try to remove the full path and take just the filename. |
michael@0 | 193 | if (name) { |
michael@0 | 194 | let names = OS.Path.split(name); |
michael@0 | 195 | name = names.components[names.components.length - 1]; |
michael@0 | 196 | } |
michael@0 | 197 | |
michael@0 | 198 | // the fallback is a filename composed by 'blob' + extension. |
michael@0 | 199 | if (!name) { |
michael@0 | 200 | name = 'blob'; |
michael@0 | 201 | if (data.result.blob.type) { |
michael@0 | 202 | let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); |
michael@0 | 203 | let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, ''); |
michael@0 | 204 | if (mimeInfo) { |
michael@0 | 205 | name += '.' + mimeInfo.primaryExtension; |
michael@0 | 206 | } |
michael@0 | 207 | } |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | let file = new this.mParent.File(data.result.blob, |
michael@0 | 211 | { name: name, |
michael@0 | 212 | type: data.result.blob.type }); |
michael@0 | 213 | |
michael@0 | 214 | if (file) { |
michael@0 | 215 | this.fireSuccess(file); |
michael@0 | 216 | } else { |
michael@0 | 217 | this.fireError(); |
michael@0 | 218 | } |
michael@0 | 219 | } |
michael@0 | 220 | }; |
michael@0 | 221 | |
michael@0 | 222 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FilePicker]); |