b2g/components/FilePicker.js

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

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]);

mercurial