1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Custom binding for the fileSystemProvider API.
7 var binding
= require('binding').Binding
.create('fileSystemProvider');
8 var fileSystemProviderInternal
=
9 require('binding').Binding
.create('fileSystemProviderInternal').generate();
10 var eventBindings
= require('event_bindings');
11 var fileSystemNatives
= requireNative('file_system_natives');
12 var GetDOMError
= fileSystemNatives
.GetDOMError
;
15 * Maximum size of the thumbnail in bytes.
19 var METADATA_THUMBNAIL_SIZE_LIMIT
= 32 * 1024 * 1024;
22 * Regular expression to validate if the thumbnail URI is a valid data URI,
23 * taking into account allowed formats.
27 var METADATA_THUMBNAIL_FORMAT
= new RegExp(
28 '^data:image/(png|jpeg|webp);', 'i');
31 * Annotates a date with its serialized value.
32 * @param {Date} date Input date.
33 * @return {Date} Date with an extra <code>value</code> attribute.
35 function annotateDate(date
) {
36 // Copy in case the input date is frozen.
37 var result
= new Date(date
.getTime());
38 result
.value
= result
.toString();
43 * Verifies if the passed image URI is valid.
44 * @param {*} uri Image URI.
45 * @return {boolean} True if valid, valse otherwise.
47 function verifyImageURI(uri
) {
48 // The URI is specified by a user, so the type may be incorrect.
49 if (typeof uri
!= 'string' && !(uri
instanceof String
))
52 return METADATA_THUMBNAIL_FORMAT
.test(uri
);
56 * Annotates an entry metadata by serializing its modifiedTime value.
57 * @param {EntryMetadata} metadata Input metadata.
58 * @return {EntryMetadata} metadata Annotated metadata, which can be passed
59 * back to the C++ layer.
61 function annotateMetadata(metadata
) {
63 isDirectory
: metadata
.isDirectory
,
66 modificationTime
: annotateDate(metadata
.modificationTime
)
68 if ('mimeType' in metadata
)
69 result
.mimeType
= metadata
.mimeType
;
70 if ('thumbnail' in metadata
)
71 result
.thumbnail
= metadata
.thumbnail
;
76 * Massages arguments of an event raised by the File System Provider API.
77 * @param {Array<*>} args Input arguments.
78 * @param {function(Array<*>)} dispatch Closure to be called with massaged
81 function massageArgumentsDefault(args
, dispatch
) {
82 var executionStart
= Date
.now();
83 var options
= args
[0];
84 var onSuccessCallback = function(hasNext
) {
85 fileSystemProviderInternal
.operationRequestedSuccess(
86 options
.fileSystemId
, options
.requestId
, Date
.now() - executionStart
);
88 var onErrorCallback = function(error
) {
89 fileSystemProviderInternal
.operationRequestedError(
90 options
.fileSystemId
, options
.requestId
, error
,
91 Date
.now() - executionStart
);
93 dispatch([options
, onSuccessCallback
, onErrorCallback
]);
96 eventBindings
.registerArgumentMassager(
97 'fileSystemProvider.onUnmountRequested',
98 massageArgumentsDefault
);
100 eventBindings
.registerArgumentMassager(
101 'fileSystemProvider.onGetMetadataRequested',
102 function(args
, dispatch
) {
103 var executionStart
= Date
.now();
104 var options
= args
[0];
105 var onSuccessCallback = function(metadata
) {
107 // It is invalid to return a thumbnail when it's not requested. The
108 // restriction is added in order to avoid fetching the thumbnail while
110 if (!options
.thumbnail
&& metadata
.thumbnail
)
111 error
= 'Thumbnail data provided, but not requested.';
113 // Check the format and size. Note, that in the C++ layer, there is
114 // another sanity check to avoid passing any evil URL.
115 if ('thumbnail' in metadata
&& !verifyImageURI(metadata
.thumbnail
))
116 error
= 'Thumbnail format invalid.';
118 if ('thumbnail' in metadata
&&
119 metadata
.thumbnail
.length
> METADATA_THUMBNAIL_SIZE_LIMIT
) {
120 error
= 'Thumbnail data too large.';
124 console
.error(error
);
125 fileSystemProviderInternal
.operationRequestedError(
126 options
.fileSystemId
, options
.requestId
, 'FAILED',
127 Date
.now() - executionStart
);
131 fileSystemProviderInternal
.getMetadataRequestedSuccess(
132 options
.fileSystemId
,
134 annotateMetadata(metadata
),
135 Date
.now() - executionStart
);
138 var onErrorCallback = function(error
) {
139 fileSystemProviderInternal
.operationRequestedError(
140 options
.fileSystemId
, options
.requestId
, error
,
141 Date
.now() - executionStart
);
144 dispatch([options
, onSuccessCallback
, onErrorCallback
]);
147 eventBindings
.registerArgumentMassager(
148 'fileSystemProvider.onGetActionsRequested',
149 function(args
, dispatch
) {
150 var executionStart
= Date
.now();
151 var options
= args
[0];
152 var onSuccessCallback = function(actions
) {
153 fileSystemProviderInternal
.getActionsRequestedSuccess(
154 options
.fileSystemId
,
157 Date
.now() - executionStart
);
160 var onErrorCallback = function(error
) {
161 fileSystemProviderInternal
.operationRequestedError(
162 options
.fileSystemId
, options
.requestId
, error
,
163 Date
.now() - executionStart
);
166 dispatch([options
, onSuccessCallback
, onErrorCallback
]);
169 eventBindings
.registerArgumentMassager(
170 'fileSystemProvider.onReadDirectoryRequested',
171 function(args
, dispatch
) {
172 var executionStart
= Date
.now();
173 var options
= args
[0];
174 var onSuccessCallback = function(entries
, hasNext
) {
175 var annotatedEntries
= entries
.map(annotateMetadata
);
176 // It is invalid to return a thumbnail when it's not requested.
178 annotatedEntries
.forEach(function(metadata
) {
179 if (metadata
.thumbnail
) {
181 'Thumbnails must not be provided when reading a directory.';
187 console
.error(error
);
188 fileSystemProviderInternal
.operationRequestedError(
189 options
.fileSystemId
, options
.requestId
, 'FAILED',
190 Date
.now() - executionStart
);
194 fileSystemProviderInternal
.readDirectoryRequestedSuccess(
195 options
.fileSystemId
, options
.requestId
, annotatedEntries
, hasNext
,
196 Date
.now() - executionStart
);
199 var onErrorCallback = function(error
) {
200 fileSystemProviderInternal
.operationRequestedError(
201 options
.fileSystemId
, options
.requestId
, error
,
202 Date
.now() - executionStart
);
204 dispatch([options
, onSuccessCallback
, onErrorCallback
]);
207 eventBindings
.registerArgumentMassager(
208 'fileSystemProvider.onOpenFileRequested',
209 massageArgumentsDefault
);
211 eventBindings
.registerArgumentMassager(
212 'fileSystemProvider.onCloseFileRequested',
213 massageArgumentsDefault
);
215 eventBindings
.registerArgumentMassager(
216 'fileSystemProvider.onReadFileRequested',
217 function(args
, dispatch
) {
218 var executionStart
= Date
.now();
219 var options
= args
[0];
220 var onSuccessCallback = function(data
, hasNext
) {
221 fileSystemProviderInternal
.readFileRequestedSuccess(
222 options
.fileSystemId
, options
.requestId
, data
, hasNext
,
223 Date
.now() - executionStart
);
225 var onErrorCallback = function(error
) {
226 fileSystemProviderInternal
.operationRequestedError(
227 options
.fileSystemId
, options
.requestId
, error
,
228 Date
.now() - executionStart
);
230 dispatch([options
, onSuccessCallback
, onErrorCallback
]);
233 eventBindings
.registerArgumentMassager(
234 'fileSystemProvider.onCreateDirectoryRequested',
235 massageArgumentsDefault
);
237 eventBindings
.registerArgumentMassager(
238 'fileSystemProvider.onDeleteEntryRequested',
239 massageArgumentsDefault
);
241 eventBindings
.registerArgumentMassager(
242 'fileSystemProvider.onCreateFileRequested',
243 massageArgumentsDefault
);
245 eventBindings
.registerArgumentMassager(
246 'fileSystemProvider.onCopyEntryRequested',
247 massageArgumentsDefault
);
249 eventBindings
.registerArgumentMassager(
250 'fileSystemProvider.onMoveEntryRequested',
251 massageArgumentsDefault
);
253 eventBindings
.registerArgumentMassager(
254 'fileSystemProvider.onTruncateRequested',
255 massageArgumentsDefault
);
257 eventBindings
.registerArgumentMassager(
258 'fileSystemProvider.onWriteFileRequested',
259 massageArgumentsDefault
);
261 eventBindings
.registerArgumentMassager(
262 'fileSystemProvider.onAbortRequested',
263 massageArgumentsDefault
);
265 eventBindings
.registerArgumentMassager(
266 'fileSystemProvider.onObserveDirectoryRequested',
267 massageArgumentsDefault
);
269 eventBindings
.registerArgumentMassager(
270 'fileSystemProvider.onUnobserveEntryRequested',
271 massageArgumentsDefault
);
273 eventBindings
.registerArgumentMassager(
274 'fileSystemProvider.onAddWatcherRequested',
275 massageArgumentsDefault
);
277 eventBindings
.registerArgumentMassager(
278 'fileSystemProvider.onRemoveWatcherRequested',
279 massageArgumentsDefault
);
281 eventBindings
.registerArgumentMassager(
282 'fileSystemProvider.onConfigureRequested',
283 massageArgumentsDefault
);
285 eventBindings
.registerArgumentMassager(
286 'fileSystemProvider.onExecuteActionRequested',
287 massageArgumentsDefault
);
289 eventBindings
.registerArgumentMassager(
290 'fileSystemProvider.onMountRequested',
291 function(args
, dispatch
) {
292 var onSuccessCallback = function() {
293 // TODO(mtomasz): To be implemented.
295 var onErrorCallback = function(error
) {
296 // TODO(mtomasz): To be implemented.
298 dispatch([onSuccessCallback
, onErrorCallback
]);
301 exports
.binding
= binding
.generate();