Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / renderer / resources / extensions / file_system_provider_custom_bindings.js
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.
4
5 // Custom binding for the fileSystemProvider API.
6
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;
13
14 /**
15  * Maximum size of the thumbnail in bytes.
16  * @type {number}
17  * @const
18  */
19 var METADATA_THUMBNAIL_SIZE_LIMIT = 32 * 1024 * 1024;
20
21 /**
22  * Regular expression to validate if the thumbnail URI is a valid data URI,
23  * taking into account allowed formats.
24  * @type {RegExp}
25  * @const
26  */
27 var METADATA_THUMBNAIL_FORMAT = new RegExp(
28     '^data:image/(png|jpeg|webp);', 'i');
29
30 /**
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.
34  */
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();
39   return result;
40 }
41
42 /**
43  * Verifies if the passed image URI is valid.
44  * @param {*} uri Image URI.
45  * @return {boolean} True if valid, valse otherwise.
46  */
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))
50     return false;
51
52   return METADATA_THUMBNAIL_FORMAT.test(uri);
53 }
54
55 /**
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.
60  */
61 function annotateMetadata(metadata) {
62   var result = {
63     isDirectory: metadata.isDirectory,
64     name: metadata.name,
65     size: metadata.size,
66     modificationTime: annotateDate(metadata.modificationTime)
67   };
68   if ('mimeType' in metadata)
69     result.mimeType = metadata.mimeType;
70   if ('thumbnail' in metadata)
71     result.thumbnail = metadata.thumbnail;
72   return result;
73 }
74
75 /**
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
79  *     arguments.
80  */
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);
87   };
88   var onErrorCallback = function(error) {
89     fileSystemProviderInternal.operationRequestedError(
90         options.fileSystemId, options.requestId, error,
91         Date.now() - executionStart);
92   }
93   dispatch([options, onSuccessCallback, onErrorCallback]);
94 }
95
96
97 binding.registerCustomHook(function(bindingsAPI) {
98   var apiFunctions = bindingsAPI.apiFunctions;
99
100   apiFunctions.setUpdateArgumentsPostValidate(
101     'mount',
102     function(options, successCallback, errorCallback) {
103       // Piggyback the error callback onto the success callback,
104       // so we can use the error callback later.
105       successCallback.errorCallback_ = errorCallback;
106       return [options, successCallback];
107     });
108
109   apiFunctions.setCustomCallback(
110     'mount',
111     function(name, request, response) {
112       var domError = null;
113       if (request.callback && response) {
114         // DOMError is present only if mount() failed.
115         if (response[0]) {
116           // Convert a Dictionary to a DOMError.
117           domError = GetDOMError(response[0].name, response[0].message);
118           response.length = 1;
119         }
120
121         var successCallback = request.callback;
122         var errorCallback = request.callback.errorCallback_;
123         delete request.callback;
124
125         if (domError)
126           errorCallback(domError);
127         else
128           successCallback();
129       }
130     });
131
132   apiFunctions.setUpdateArgumentsPostValidate(
133     'unmount',
134     function(options, successCallback, errorCallback) {
135       // Piggyback the error callback onto the success callback,
136       // so we can use the error callback later.
137       successCallback.errorCallback_ = errorCallback;
138       return [options, successCallback];
139     });
140
141   apiFunctions.setCustomCallback(
142     'unmount',
143     function(name, request, response) {
144       var domError = null;
145       if (request.callback) {
146         // DOMError is present only if mount() failed.
147         if (response && response[0]) {
148           // Convert a Dictionary to a DOMError.
149           domError = GetDOMError(response[0].name, response[0].message);
150           response.length = 1;
151         }
152
153         var successCallback = request.callback;
154         var errorCallback = request.callback.errorCallback_;
155         delete request.callback;
156
157         if (domError)
158           errorCallback(domError);
159         else
160           successCallback();
161       }
162     });
163 });
164
165 eventBindings.registerArgumentMassager(
166     'fileSystemProvider.onUnmountRequested',
167     massageArgumentsDefault);
168
169 eventBindings.registerArgumentMassager(
170     'fileSystemProvider.onGetMetadataRequested',
171     function(args, dispatch) {
172       var executionStart = Date.now();
173       var options = args[0];
174       var onSuccessCallback = function(metadata) {
175         var error;
176         // It is invalid to return a thumbnail when it's not requested. The
177         // restriction is added in order to avoid fetching the thumbnail while
178         // it's not needed.
179         if (!options.thumbnail && metadata.thumbnail)
180           error = 'Thumbnail data provided, but not requested.';
181
182         // Check the format and size. Note, that in the C++ layer, there is
183         // another sanity check to avoid passing any evil URL.
184         if ('thumbnail' in metadata && !verifyImageURI(metadata.thumbnail))
185           error = 'Thumbnail format invalid.';
186
187         if ('thumbnail' in metadata &&
188             metadata.thumbnail.length > METADATA_THUMBNAIL_SIZE_LIMIT) {
189           error = 'Thumbnail data too large.';
190         }
191
192         if (error) {
193           console.error(error);
194           fileSystemProviderInternal.operationRequestedError(
195               options.fileSystemId, options.requestId, 'FAILED',
196               Date.now() - executionStart);
197           return;
198         }
199
200         fileSystemProviderInternal.getMetadataRequestedSuccess(
201             options.fileSystemId,
202             options.requestId,
203             annotateMetadata(metadata),
204             Date.now() - executionStart);
205       };
206
207       var onErrorCallback = function(error) {
208         fileSystemProviderInternal.operationRequestedError(
209             options.fileSystemId, options.requestId, error,
210             Date.now() - executionStart);
211       }
212
213       dispatch([options, onSuccessCallback, onErrorCallback]);
214     });
215
216 eventBindings.registerArgumentMassager(
217     'fileSystemProvider.onReadDirectoryRequested',
218     function(args, dispatch) {
219       var executionStart = Date.now();
220       var options = args[0];
221       var onSuccessCallback = function(entries, hasNext) {
222         var annotatedEntries = entries.map(annotateMetadata);
223         // It is invalid to return a thumbnail when it's not requested.
224         var error;
225         annotatedEntries.forEach(function(metadata) {
226           if (metadata.thumbnail) {
227             var error =
228                 'Thumbnails must not be provided when reading a directory.';
229             return;
230           }
231         });
232
233         if (error) {
234           console.error(error);
235           fileSystemProviderInternal.operationRequestedError(
236               options.fileSystemId, options.requestId, 'FAILED',
237               Date.now() - executionStart);
238           return;
239         }
240
241         fileSystemProviderInternal.readDirectoryRequestedSuccess(
242             options.fileSystemId, options.requestId, annotatedEntries, hasNext,
243             Date.now() - executionStart);
244       };
245
246       var onErrorCallback = function(error) {
247         fileSystemProviderInternal.operationRequestedError(
248             options.fileSystemId, options.requestId, error,
249             Date.now() - executionStart);
250       }
251       dispatch([options, onSuccessCallback, onErrorCallback]);
252     });
253
254 eventBindings.registerArgumentMassager(
255     'fileSystemProvider.onOpenFileRequested',
256     massageArgumentsDefault);
257
258 eventBindings.registerArgumentMassager(
259     'fileSystemProvider.onCloseFileRequested',
260     massageArgumentsDefault);
261
262 eventBindings.registerArgumentMassager(
263     'fileSystemProvider.onReadFileRequested',
264     function(args, dispatch) {
265       var executionStart = Date.now();
266       var options = args[0];
267       var onSuccessCallback = function(data, hasNext) {
268         fileSystemProviderInternal.readFileRequestedSuccess(
269             options.fileSystemId, options.requestId, data, hasNext,
270             Date.now() - executionStart);
271       };
272       var onErrorCallback = function(error) {
273         fileSystemProviderInternal.operationRequestedError(
274             options.fileSystemId, options.requestId, error,
275             Date.now() - executionStart);
276       }
277       dispatch([options, onSuccessCallback, onErrorCallback]);
278     });
279
280 eventBindings.registerArgumentMassager(
281     'fileSystemProvider.onCreateDirectoryRequested',
282     massageArgumentsDefault);
283
284 eventBindings.registerArgumentMassager(
285     'fileSystemProvider.onDeleteEntryRequested',
286     massageArgumentsDefault);
287
288 eventBindings.registerArgumentMassager(
289     'fileSystemProvider.onCreateFileRequested',
290     massageArgumentsDefault);
291
292 eventBindings.registerArgumentMassager(
293     'fileSystemProvider.onCopyEntryRequested',
294     massageArgumentsDefault);
295
296 eventBindings.registerArgumentMassager(
297     'fileSystemProvider.onMoveEntryRequested',
298     massageArgumentsDefault);
299
300 eventBindings.registerArgumentMassager(
301     'fileSystemProvider.onTruncateRequested',
302     massageArgumentsDefault);
303
304 eventBindings.registerArgumentMassager(
305     'fileSystemProvider.onWriteFileRequested',
306     massageArgumentsDefault);
307
308 eventBindings.registerArgumentMassager(
309     'fileSystemProvider.onAbortRequested',
310     massageArgumentsDefault);
311
312 exports.binding = binding.generate();