Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: ui/file_manager/zip_archiver/unpacker/js/app.js

Issue 2804453002: Move files from zip_archiver/unpacker/ to zip_archiver/. (Closed)
Patch Set: Move files from zip_archiver/unpacker/ to zip_archiver/. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium OS 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 'use strict';
6
7 /**
8 * The main namespace for the extension.
9 * @namespace
10 */
11 unpacker.app = {
12 /**
13 * The key used by chrome.storage.local to save and restore the volumes state.
14 * @const {string}
15 */
16 STORAGE_KEY: 'state',
17
18 /**
19 * The default id for the NaCl module.
20 * @const {string}
21 */
22 DEFAULT_MODULE_ID: 'nacl_module',
23
24 /**
25 * Time in milliseconds before the notification about mounting is shown.
26 * @const {number}
27 */
28 MOUNTING_NOTIFICATION_DELAY: 1000,
29
30 /**
31 * The default filename for .nmf file.
32 * This value must not be const because it is overwritten in tests.
33 * @type {string}
34 */
35 DEFAULT_MODULE_NMF: 'module.nmf',
36
37 /**
38 * The default MIME type for .nmf file.
39 * @const {string}
40 */
41 DEFAULT_MODULE_TYPE: 'application/x-pnacl',
42
43 /**
44 * Multiple volumes can be opened at the same time.
45 * @type {!Object<!unpacker.types.FileSystemId, !unpacker.Volume>}
46 */
47 volumes: {},
48
49 /**
50 * Each "Pack with" request from Files app creates a new compressor.
51 * Thus, multiple compressors can exist at the same time.
52 * @type {!Object<!unpacker.types.CompressorId, !unpacker.Compressor>}
53 */
54 compressors: {},
55
56 /**
57 * A map with promises of loading a volume's metadata from NaCl.
58 * Any call from fileSystemProvider API should work only on valid metadata.
59 * These promises ensure that the fileSystemProvider API calls wait for the
60 * metatada load.
61 * @type {!Object<!unpacker.types.FileSystemId, !Promise>}
62 */
63 volumeLoadedPromises: {},
64
65 /**
66 * A Promise used to postpone all calls to fileSystemProvider API after
67 * the NaCl module loads.
68 * @type {?Promise}
69 */
70 moduleLoadedPromise: null,
71
72 /**
73 * The NaCl module containing the logic for decompressing archives.
74 * @type {?Object}
75 */
76 naclModule: null,
77
78 /**
79 * The number of mount process in progress. NaCL module is unloaded if
80 * app.unpacker.volumes is empty and this counter is 0. This is incremented
81 * when a mounting process starts and decremented when it ends.
82 * @type {number}
83 */
84 mountProcessCounter: 0,
85
86 /**
87 * Function called on receiving a message from NaCl module. Registered by
88 * common.js.
89 * Process pack message by getting compressor and passing the message to it.
90 * @param {!Object} message The message received from NaCl module.
91 * @param {!unpacker.request.Operation} operation
92 * @private
93 */
94 handlePackMessage_: function(message, operation) {
95 var compressorId = message.data[unpacker.request.Key.COMPRESSOR_ID];
96 console.assert(compressorId, 'No NaCl compressor id.');
97
98 var compressor = unpacker.app.compressors[compressorId];
99 if (!compressor) {
100 console.error('No compressor for compressor id: ' + compressorId + '.');
101 return;
102 }
103 compressor.processMessage(message.data, operation);
104 },
105
106 /**
107 * Process unpack message by getting volume and passing the message to it.
108 * @param {!Object} message The message received from NaCl module.
109 * @param {!unpacker.request.Operation} operation
110 * @private
111 */
112 handleUnpackMessage_: function(message, operation) {
113 var fileSystemId = message.data[unpacker.request.Key.FILE_SYSTEM_ID];
114 console.assert(fileSystemId, 'No NaCl file system id.');
115
116 var requestId = message.data[unpacker.request.Key.REQUEST_ID];
117 console.assert(!!requestId, 'No NaCl request id.');
118
119 var volume = unpacker.app.volumes[fileSystemId];
120 if (!volume) {
121 // The volume is gone, which can happen.
122 console.info('No volume for: ' + fileSystemId + '.');
123 return;
124 }
125
126 volume.decompressor.processMessage(message.data, operation,
127 Number(requestId));
128 },
129
130 /**
131 * Function called on receiving a message from NaCl module. Registered by
132 * common.js.
133 * @param {!Object} message The message received from NaCl module.
134 * @private
135 */
136 handleMessage_: function(message) {
137 // Get mandatory fields in a message.
138 var operation = message.data[unpacker.request.Key.OPERATION];
139 console.assert(operation != undefined, // Operation can be 0.
140 'No NaCl operation: ' + operation + '.');
141
142 // Assign the message to either module.
143 if (unpacker.request.isPackRequest(operation))
144 unpacker.app.handlePackMessage_(message, operation);
145 else
146 unpacker.app.handleUnpackMessage_(message, operation);
147 },
148
149 /**
150 * Saves state in case of restarts, event page suspend, crashes, etc.
151 * @param {!Array<!unpacker.types.FileSystemId>} fileSystemIdsArray
152 * @private
153 */
154 saveState_: function(fileSystemIdsArray) {
155 chrome.storage.local.get([unpacker.app.STORAGE_KEY], function(result) {
156 if (!result[unpacker.app.STORAGE_KEY]) // First save state call.
157 result[unpacker.app.STORAGE_KEY] = {};
158
159 // Overwrite state only for the volumes that have their file system id
160 // present in the input array. Leave the rest of the volumes state
161 // untouched.
162 fileSystemIdsArray.forEach(function(fileSystemId) {
163 var entryId = chrome.fileSystem.retainEntry(
164 unpacker.app.volumes[fileSystemId].entry);
165 result[unpacker.app.STORAGE_KEY][fileSystemId] = {
166 entryId: entryId,
167 passphrase: unpacker.app.volumes[fileSystemId]
168 .decompressor.passphraseManager.rememberedPassphrase
169 };
170 });
171
172 chrome.storage.local.set(result);
173 });
174 },
175
176 /**
177 * Removes state from local storage for a single volume.
178 * @param {!unpacker.types.FileSystemId} fileSystemId
179 */
180 removeState_: function(fileSystemId) {
181 chrome.storage.local.get([unpacker.app.STORAGE_KEY], function(result) {
182 console.assert(result[unpacker.app.STORAGE_KEY] &&
183 result[unpacker.app.STORAGE_KEY][fileSystemId],
184 'Should call removeState_ only for file systems that ',
185 'have previously called saveState_.');
186
187 delete result[unpacker.app.STORAGE_KEY][fileSystemId];
188 chrome.storage.local.set(result);
189 });
190 },
191
192 /**
193 * Restores archive's entry and opened files for the passed file system id.
194 * @param {!unpacker.types.FileSystemId} fileSystemId
195 * @return {!Promise<!Object>} Promise fulfilled with the entry and list of
196 * opened files.
197 * @private
198 */
199 restoreVolumeState_: function(fileSystemId) {
200 return new Promise(function(fulfill, reject) {
201 chrome.storage.local.get([unpacker.app.STORAGE_KEY], function(result) {
202 if (!result[unpacker.app.STORAGE_KEY]) {
203 reject('FAILED');
204 return;
205 }
206
207 var volumeState = result[unpacker.app.STORAGE_KEY][fileSystemId];
208 if (!volumeState) {
209 console.error('No state for: ' + fileSystemId + '.');
210 reject('FAILED');
211 return;
212 }
213
214 chrome.fileSystem.restoreEntry(volumeState.entryId, function(entry) {
215 if (chrome.runtime.lastError) {
216 console.error('Restore entry error for <', fileSystemId, '>: ' +
217 chrome.runtime.lastError.message);
218 reject('FAILED');
219 return;
220 }
221 fulfill({
222 entry: entry,
223 passphrase: volumeState.passphrase
224 });
225 });
226 });
227 });
228 },
229
230 /**
231 * Creates a volume and loads its metadata from NaCl.
232 * @param {!unpacker.types.FileSystemId} fileSystemId
233 * @param {!Entry} entry The volume's archive entry.
234 * @param {!Object<!unpacker.types.RequestId,
235 * !unpacker.types.OpenFileRequestedOptions>}
236 * openedFiles Previously opened files before a suspend.
237 * @param {string} passphrase Previously used passphrase before a suspend.
238 * @return {!Promise} Promise fulfilled on success and rejected on failure.
239 * @private
240 */
241 loadVolume_: function(fileSystemId, entry, openedFiles, passphrase) {
242 return new Promise(function(fulfill, reject) {
243 entry.file(function(file) {
244 // File is a Blob object, so it's ok to construct the Decompressor
245 // directly with it.
246 var passphraseManager = new unpacker.PassphraseManager(passphrase);
247 console.assert(unpacker.app.naclModule,
248 'The NaCL module should have already been defined.');
249 var decompressor = new unpacker.Decompressor(
250 /** @type {!Object} */ (unpacker.app.naclModule),
251 fileSystemId, file, passphraseManager);
252 var volume = new unpacker.Volume(decompressor, entry);
253
254 var onLoadVolumeSuccess = function() {
255 if (Object.keys(openedFiles).length == 0) {
256 fulfill();
257 return;
258 }
259
260 // Restore opened files on NaCl side.
261 var openFilePromises = [];
262 for (var key in openedFiles) {
263 // 'key' is always a number but JS compiler complains that it is
264 // a string.
265 var openRequestId = Number(key);
266 var options =
267 /** @type {!unpacker.types.OpenFileRequestedOptions} */
268 (openedFiles[openRequestId]);
269 openFilePromises.push(new Promise(function(resolve, reject) {
270 volume.onOpenFileRequested(options, resolve, reject);
271 }));
272 }
273
274 Promise.all(openFilePromises).then(fulfill, reject);
275 };
276
277 unpacker.app.volumes[fileSystemId] = volume;
278 volume.initialize(onLoadVolumeSuccess, reject);
279 }, function(error) {
280 reject('FAILED');
281 });
282 });
283 },
284
285 /**
286 * Restores a volume mounted previously to a suspend / restart. In case of
287 * failure of the load promise for fileSystemId, the corresponding volume is
288 * forcely unmounted.
289 * @param {!unpacker.types.FileSystemId} fileSystemId
290 * @return {!Promise} A promise that restores state and loads volume.
291 * @private
292 */
293 restoreSingleVolume_: function(fileSystemId) {
294 // Load volume after restart / suspend page event.
295 return unpacker.app.restoreVolumeState_(fileSystemId)
296 .then(function(state) {
297 return new Promise(function(fulfill, reject) {
298 // Check if the file system is compatible with this version of the
299 // ZIP unpacker.
300 // TODO(mtomasz): Implement remounting instead of unmounting.
301 chrome.fileSystemProvider.get(fileSystemId, function(fileSystem) {
302 if (chrome.runtime.lastError) {
303 console.error(chrome.runtime.lastError.name);
304 reject('FAILED');
305 return;
306 }
307 if (!fileSystem || fileSystem.openedFilesLimit != 1) {
308 console.error('No compatible mounted file system found.');
309 reject('FAILED');
310 return;
311 }
312 fulfill({state: state, fileSystem: fileSystem});
313 });
314 });
315 })
316 .then(function(stateWithFileSystem) {
317 var openedFilesOptions = {};
318 stateWithFileSystem.fileSystem.openedFiles.forEach(function(
319 openedFile) {
320 openedFilesOptions[openedFile.openRequestId] = {
321 fileSystemId: fileSystemId,
322 requestId: openedFile.openRequestId,
323 mode: openedFile.mode,
324 filePath: openedFile.filePath
325 };
326 });
327 return unpacker.app.loadVolume_(
328 fileSystemId, stateWithFileSystem.state.entry, openedFilesOptions,
329 stateWithFileSystem.state.passphrase);
330 })
331 .catch(function(error) {
332 console.error(error.stack || error);
333 // Force unmount in case restore failed. All resources related to the
334 // volume will be cleanup from both memory and local storage.
335 // TODO(523195): Show a notification that the source file is gone.
336 return unpacker.app.unmountVolume(fileSystemId, true)
337 .then(function() { return Promise.reject('FAILED'); });
338 });
339 },
340
341 /**
342 * Ensures a volume is loaded by returning its corresponding loaded promise
343 * from unpacker.app.volumeLoadedPromises. In case there is no such promise,
344 * then this is a call after suspend / restart and a new volume loaded promise
345 * that restores state is returned.
346 * @param {!unpacker.types.FileSystemId} fileSystemId
347 * @return {!Promise} The loading volume promise.
348 * @private
349 */
350 ensureVolumeLoaded_: function(fileSystemId) {
351 // Increment the counter so that the NaCl module won't be unloaded until
352 // the mounting process ends.
353 unpacker.app.mountProcessCounter++;
354 // Create a promise to load the NaCL module.
355 if (!unpacker.app.moduleLoadedPromise)
356 unpacker.app.loadNaclModule(unpacker.app.DEFAULT_MODULE_NMF,
357 unpacker.app.DEFAULT_MODULE_TYPE);
358
359 return unpacker.app.moduleLoadedPromise.then(function() {
360 // In case there is no volume promise for fileSystemId then we
361 // received a call after restart / suspend as load promises are
362 // created on launched. In this case we will restore volume state
363 // from local storage and create a new load promise.
364 if (!unpacker.app.volumeLoadedPromises[fileSystemId]) {
365 unpacker.app.volumeLoadedPromises[fileSystemId] =
366 unpacker.app.restoreSingleVolume_(fileSystemId);
367 }
368
369 // Decrement the counter when the mounting process ends.
370 unpacker.app.volumeLoadedPromises[fileSystemId].then(function() {
371 unpacker.app.mountProcessCounter--;
372 }).catch(function(error) {
373 unpacker.app.mountProcessCounter--;
374 });
375
376 return unpacker.app.volumeLoadedPromises[fileSystemId];
377 });
378 },
379
380 /**
381 * @return {boolean} True if NaCl module is loaded.
382 */
383 naclModuleIsLoaded: function() { return !!unpacker.app.naclModule; },
384
385 /**
386 * Loads the NaCl module.
387 * @param {string} pathToConfigureFile Path to the module's configuration
388 * file, which should be a .nmf file.
389 * @param {string} mimeType The mime type for the NaCl executable.
390 * @param {string=} opt_moduleId The NaCl module id. Necessary for testing
391 * purposes.
392 */
393 loadNaclModule: function(pathToConfigureFile, mimeType, opt_moduleId) {
394 unpacker.app.moduleLoadedPromise = new Promise(function(fulfill) {
395 var moduleId =
396 opt_moduleId ? opt_moduleId : unpacker.app.DEFAULT_MODULE_ID;
397 var elementDiv = document.createElement('div');
398
399 // Promise fulfills only after NaCl module has been loaded.
400 elementDiv.addEventListener('load', function() {
401 // Since the first load of the NaCL module is slow, the module is loaded
402 // once in background.js in advance. If there is no mounted volume and
403 // ongoing mounting process, the module is just unloaded. This is the
404 // workaround for crbug.com/699930.
405 if (Object.keys(unpacker.app.volumes).length === 0 &&
406 unpacker.app.mountProcessCounter === 0) {
407 elementDiv.parentNode.removeChild(elementDiv);
408 // This is necessary for tests.
409 fulfill();
410 unpacker.app.moduleLoadedPromise = null;
411 return;
412 }
413 unpacker.app.naclModule = document.getElementById(moduleId);
414 fulfill();
415 }, true);
416
417 elementDiv.addEventListener('message', unpacker.app.handleMessage_, true);
418
419 var elementEmbed = document.createElement('embed');
420 elementEmbed.id = moduleId;
421 elementEmbed.style.width = 0;
422 elementEmbed.style.height = 0;
423 elementEmbed.src = pathToConfigureFile;
424 elementEmbed.type = mimeType;
425 elementDiv.appendChild(elementEmbed);
426
427 document.body.appendChild(elementDiv);
428 // Request the offsetTop property to force a relayout. As of Apr 10, 2014
429 // this is needed if the module is being loaded on a Chrome App's
430 // background page (see crbug.com/350445).
431 /** @suppress {suspiciousCode} */ elementEmbed.offsetTop;
432 });
433 },
434
435 /**
436 * Unloads the NaCl module.
437 */
438 unloadNaclModule: function() {
439 var naclModuleParentNode = unpacker.app.naclModule.parentNode;
440 naclModuleParentNode.parentNode.removeChild(naclModuleParentNode);
441 unpacker.app.naclModule = null;
442 unpacker.app.moduleLoadedPromise = null;
443 },
444
445 /**
446 * Cleans up the resources for a volume, except for the local storage. If
447 * necessary that can be done using unpacker.app.removeState_.
448 * @param {!unpacker.types.FileSystemId} fileSystemId
449 */
450 cleanupVolume: function(fileSystemId) {
451 delete unpacker.app.volumes[fileSystemId];
452 // Allow mount after clean.
453 delete unpacker.app.volumeLoadedPromises[fileSystemId];
454
455 if (Object.keys(unpacker.app.volumes).length === 0 &&
456 unpacker.app.mountProcessCounter === 0) {
457 unpacker.app.unloadNaclModule();
458 } else {
459 unpacker.app.naclModule.postMessage(
460 unpacker.request.createCloseVolumeRequest(fileSystemId));
461 }
462 },
463
464 /**
465 * Cleans up the resources for a compressor.
466 * @param {!unpacker.types.CompressorId} compressorId
467 * @param {boolean} hasError
468 */
469 cleanupCompressor: function(compressorId, hasError) {
470 var compressor = unpacker.app.compressors[compressorId];
471 if (!compressor) {
472 console.error('No compressor for: compressor id' + compressorId + '.');
473 return;
474 }
475
476 unpacker.app.mountProcessCounter--;
477 if (Object.keys(unpacker.app.volumes).length === 0 &&
478 unpacker.app.mountProcessCounter === 0) {
479 unpacker.app.unloadNaclModule();
480 } else {
481 // Request libarchive to abort any ongoing process and release resources.
482 // The argument indicates whether an error occurred or not.
483 if (hasError)
484 compressor.sendCloseArchiveRequest(hasError);
485 }
486
487 // Delete the archive file if it exists.
488 if (compressor.archiveFileEntry)
489 compressor.archiveFileEntry.remove();
490
491 delete unpacker.app.compressors[compressorId];
492 },
493
494 /**
495 * Unmounts a volume and removes any resources related to the volume from both
496 * the extension and the local storage state.
497 * @param {!unpacker.types.FileSystemId} fileSystemId
498 * @param {boolean=} opt_forceUnmount True if unmount should be forced even if
499 * volume might be in use, or is not restored yet.
500 * @return {!Promise} A promise that fulfills if volume is unmounted or
501 * rejects with ProviderError in case of any errors.
502 */
503 unmountVolume: function(fileSystemId, opt_forceUnmount) {
504 return new Promise(function(fulfill, reject) {
505 var volume = unpacker.app.volumes[fileSystemId];
506 console.assert(volume || opt_forceUnmount,
507 'Unmount that is not forced must not be called for ',
508 'volumes that are not restored.');
509
510 if (!opt_forceUnmount && volume.inUse()) {
511 reject('IN_USE');
512 return;
513 }
514
515 var options = {
516 fileSystemId: fileSystemId
517 };
518 chrome.fileSystemProvider.unmount(options, function() {
519 if (chrome.runtime.lastError) {
520 console.error('Unmount error: ' + chrome.runtime.lastError.message +
521 '.');
522 reject('FAILED');
523 return;
524 }
525
526 // In case of forced unmount volume can be undefined due to not being
527 // restored. An unmount that is not forced will be called only after
528 // restoring state. In the case of forced unmount when volume is not
529 // restored, we will not do a normal cleanup, but just remove the load
530 // volume promise to allow further mounts.
531 if (opt_forceUnmount)
532 delete unpacker.app.volumeLoadedPromises[fileSystemId];
533 else
534 unpacker.app.cleanupVolume(fileSystemId);
535
536 // Remove volume from local storage.
537 unpacker.app.removeState_(fileSystemId);
538 fulfill();
539 });
540 });
541 },
542
543 /**
544 * Handles an unmount request received from File System Provider API.
545 * @param {!unpacker.types.UnmountRequestedOptions} options
546 * @param {function()} onSuccess Callback to execute on success.
547 * @param {function(!ProviderError)} onError Callback to execute on error.
548 */
549 onUnmountRequested: function(options, onSuccess, onError) {
550 unpacker.app.ensureVolumeLoaded_(options.fileSystemId)
551 .then(function() {
552 return unpacker.app.unmountVolume(options.fileSystemId);
553 })
554 .then(onSuccess)
555 .catch(/** @type {function(*)} */ (onError));
556 },
557
558 /**
559 * Obtains metadata about a file system entry.
560 * @param {!unpacker.types.GetMetadataRequestedOptions} options
561 * @param {function(!EntryMetadata)} onSuccess Callback to execute on success.
562 * The parameter is the EntryMetadata obtained by this function.
563 * @param {function(!ProviderError)} onError Callback to execute on error.
564 */
565 onGetMetadataRequested: function(options, onSuccess, onError) {
566 unpacker.app.ensureVolumeLoaded_(options.fileSystemId)
567 .then(function() {
568 unpacker.app.volumes[options.fileSystemId].onGetMetadataRequested(
569 options, onSuccess, onError);
570 })
571 .catch(/** @type {function(*)} */ (onError));
572 },
573
574 /**
575 * Reads a directory entries.
576 * @param {!unpacker.types.ReadDirectoryRequestedOptions} options
577 * @param {function(!Array<!EntryMetadata>, boolean)} onSuccess Callback to
578 * execute on success. The first parameter is an array with directory
579 * entries. The second parameter is 'hasMore', and if it's set to true,
580 * then onSuccess must be called again with the next directory entries.
581 * @param {function(!ProviderError)} onError Callback to execute on error.
582 */
583 onReadDirectoryRequested: function(options, onSuccess, onError) {
584 unpacker.app.ensureVolumeLoaded_(options.fileSystemId)
585 .then(function() {
586 unpacker.app.volumes[options.fileSystemId].onReadDirectoryRequested(
587 options, onSuccess, onError);
588 })
589 .catch(/** @type {function(*)} */ (onError));
590 },
591
592 /**
593 * Opens a file for read or write.
594 * @param {!unpacker.types.OpenFileRequestedOptions} options
595 * @param {function()} onSuccess Callback to execute on success.
596 * @param {function(!ProviderError)} onError Callback to execute on error.
597 */
598 onOpenFileRequested: function(options, onSuccess, onError) {
599 unpacker.app.ensureVolumeLoaded_(options.fileSystemId)
600 .then(function() {
601 unpacker.app.volumes[options.fileSystemId].onOpenFileRequested(
602 options, onSuccess, onError);
603 })
604 .catch(/** @type {function(*)} */ (onError));
605 },
606
607 /**
608 * Closes a file identified by options.openRequestId.
609 * @param {!unpacker.types.CloseFileRequestedOptions} options
610 * @param {function()} onSuccess Callback to execute on success.
611 * @param {function(!ProviderError)} onError Callback to execute on error.
612 */
613 onCloseFileRequested: function(options, onSuccess, onError) {
614 unpacker.app.ensureVolumeLoaded_(options.fileSystemId)
615 .then(function() {
616 unpacker.app.volumes[options.fileSystemId].onCloseFileRequested(
617 options, onSuccess, onError);
618 })
619 .catch(/** @type {function(*)} */ (onError));
620 },
621
622 /**
623 * Reads the contents of a file identified by options.openRequestId.
624 * @param {!unpacker.types.ReadFileRequestedOptions} options
625 * @param {function(!ArrayBuffer, boolean)} onSuccess Callback to execute on
626 * success. The first parameter is the read data and the second parameter
627 * is 'hasMore'. If it's set to true, then onSuccess must be called again
628 * with the next data to read.
629 * @param {function(!ProviderError)} onError Callback to execute on error.
630 */
631 onReadFileRequested: function(options, onSuccess, onError) {
632 unpacker.app.ensureVolumeLoaded_(options.fileSystemId)
633 .then(function() {
634 unpacker.app.volumes[options.fileSystemId].onReadFileRequested(
635 options, onSuccess, onError);
636 })
637 .catch(/** @type {function(*)} */ (onError));
638 },
639
640 /**
641 * Creates a new compressor and compresses entries.
642 * @param {!Object} launchData
643 */
644 onLaunchedWithPack: function(launchData) {
645 unpacker.app.mountProcessCounter++;
646
647 // Create a promise to load the NaCL module.
648 if (!unpacker.app.moduleLoadedPromise) {
649 unpacker.app.loadNaclModule(unpacker.app.DEFAULT_MODULE_NMF,
650 unpacker.app.DEFAULT_MODULE_TYPE);
651 }
652
653 unpacker.app.moduleLoadedPromise
654 .then(function() {
655 var compressor = new unpacker.Compressor(
656 /** @type {!Object} */ (unpacker.app.naclModule),
657 launchData.items);
658
659 var compressorId = compressor.getCompressorId();
660
661 unpacker.app.compressors[compressorId] = compressor;
662
663 // TODO(takise): Error messages have not been prepared yet for timer
664 // and error processing.
665
666 // If packing takes significant amount of time, then show a
667 // notification about packing in progress.
668 // var deferredNotificationTimer = setTimeout(function() {
669 // chrome.notifications.create(compressorId.toString(), {
670 // type: 'basic',
671 // iconUrl: chrome.runtime.getManifest().icons[128],
672 // title: entry.name,
673 // message: chrome.i18n.getMessage('packingMessage'),
674 // }, function() {});
675 // }, unpacker.app.PACKING_NOTIFICATION_DELAY);
676
677 var onError = function(compressorId) {
678 // clearTimeout(deferredNotificationTimer);
679 // console.error('Packing error: ' + error.message + '.');
680 // chrome.notifications.create(compressorId.toString(), {
681 // type: 'basic',
682 // iconUrl: chrome.runtime.getManifest().icons[128],
683 // title: entry.name,
684 // message: chrome.i18n.getMessage('packingErrorMessage')
685 // }, function() {});
686 unpacker.app.cleanupCompressor(compressorId, true /* hasError */);
687 };
688
689 var onSuccess = function(compressorId) {
690 // clearTimeout(deferredNotificationTimer);
691 // chrome.notifications.clear(compressorId.toString(),
692 // function() {});
693 unpacker.app.cleanupCompressor(compressorId, false /* hasError */);
694 };
695
696 compressor.compress(onSuccess, onError);
697 });
698 },
699
700 /**
701 * Creates a volume for every opened file with the extension or mime type
702 * declared in the manifest file.
703 * @param {!Object} launchData
704 * @param {function(string)=} opt_onSuccess Callback to execute in case a
705 * volume was loaded successfully. Has one parameter, which is the file
706 * system id of the loaded volume. Can be called multiple times, depending
707 * on how many volumes must be loaded.
708 * @param {function(string)=} opt_onError Callback to execute in case of
709 * failure when loading a volume. Has one parameter, which is the file
710 * system id of the volume that failed to load. Can be called multiple
711 * times, depending on how many volumes must be loaded.
712 */
713 onLaunchedWithUnpack: function(launchData, opt_onSuccess, opt_onError) {
714 // Increment the counter that indicates the number of ongoing mouot process.
715 unpacker.app.mountProcessCounter++;
716
717 // Create a promise to load the NaCL module.
718 if (!unpacker.app.moduleLoadedPromise) {
719 unpacker.app.loadNaclModule(unpacker.app.DEFAULT_MODULE_NMF,
720 unpacker.app.DEFAULT_MODULE_TYPE);
721 }
722
723 unpacker.app.moduleLoadedPromise
724 .then(function() {
725 unpacker.app.mountProcessCounter--;
726 launchData.items.forEach(function(item) {
727 unpacker.app.mountProcessCounter++;
728 chrome.fileSystem.getDisplayPath(item.entry, function(
729 entry,
730 fileSystemId) {
731 // If loading takes significant amount of time, then show a
732 // notification about scanning in progress.
733 var deferredNotificationTimer = setTimeout(function() {
734 chrome.notifications.create(fileSystemId, {
735 type: 'basic',
736 iconUrl: chrome.runtime.getManifest().icons[128],
737 title: entry.name,
738 message: chrome.i18n.getMessage('mountingMessage'),
739 }, function() {});
740 }, unpacker.app.MOUNTING_NOTIFICATION_DELAY);
741
742 var onError = function(error, fileSystemId) {
743 clearTimeout(deferredNotificationTimer);
744 console.error('Mount error: ' + error.message + '.');
745 // Decrement the counter that indicates the number of ongoing
746 // mount process.
747 unpacker.app.mountProcessCounter--;
748 if (error.message === 'EXISTS') {
749 if (opt_onError)
750 opt_onError(fileSystemId);
751 return;
752 }
753 chrome.notifications.create(fileSystemId, {
754 type: 'basic',
755 iconUrl: chrome.runtime.getManifest().icons[128],
756 title: entry.name,
757 message: chrome.i18n.getMessage('otherErrorMessage')
758 }, function() {});
759 if (opt_onError)
760 opt_onError(fileSystemId);
761 // Cleanup volume resources in order to allow future attempts
762 // to mount the volume. The volume can't be cleaned up in
763 // case of 'EXIST' because we should not clean the other
764 // already mounted volume.
765 unpacker.app.cleanupVolume(fileSystemId);
766 };
767
768 var onSuccess = function(fileSystemId) {
769 clearTimeout(deferredNotificationTimer);
770 chrome.notifications.clear(fileSystemId, function() {});
771 // Decrement the counter that indicates the number of ongoing
772 // mount process.
773 unpacker.app.mountProcessCounter--;
774 if (opt_onSuccess)
775 opt_onSuccess(fileSystemId);
776 };
777
778 var loadPromise = unpacker.app.loadVolume_(
779 fileSystemId, entry, {}, '' /* passphrase */);
780 loadPromise.then(function() {
781 // Mount the volume and save its information in local storage
782 // in order to be able to recover the metadata in case of
783 // restarts, system crashes, etc.
784 chrome.fileSystemProvider.mount({
785 fileSystemId: fileSystemId,
786 displayName: entry.name,
787 openedFilesLimit: 1
788 },
789 function() {
790 if (chrome.runtime.lastError) {
791 onError(chrome.runtime.lastError, fileSystemId);
792 return;
793 }
794 // Save state so in case of restarts we are able to correctly
795 // get the archive's metadata.
796 unpacker.app.saveState_([fileSystemId]);
797 onSuccess(fileSystemId);
798 });
799 }).catch(function(error) {
800 onError(error.stack || error, fileSystemId);
801 return Promise.reject(error);
802 });
803
804 unpacker.app.volumeLoadedPromises[fileSystemId] = loadPromise;
805 }.bind(null, item.entry));
806 });
807 })
808 .catch(function(error) { console.error(error.stack || error); });
809 },
810
811 /**
812 * Fired when this extension is launched.
813 * Calls a module designated by launchData.id.
814 * Currently, Verbs API does not support "unpack" option. Thus, any launchData
815 * that does not have "pack" as id is regarded as unpack for now.
816 * @param {!Object} launchData
817 * @param {function(string)=} opt_onSuccess
818 * @param {function(string)=} opt_onError
819 */
820 onLaunched: function(launchData, opt_onSuccess, opt_onError) {
821 if (launchData.items == null) {
822 // The user tried to launch us directly.
823 console.log('Ignoring launch request w/out items field', {launchData});
824 return;
825 }
826
827 if (launchData.id === "pack")
828 unpacker.app.onLaunchedWithPack(launchData);
829 else
830 unpacker.app.onLaunchedWithUnpack(launchData, opt_onSuccess, opt_onError);
831 },
832
833 /**
834 * Saves the state before suspending the event page, so we can resume it
835 * once new events arrive.
836 */
837 onSuspend: function() {
838 unpacker.app.saveState_(Object.keys(unpacker.app.volumes));
839 }
840 };
OLDNEW
« no previous file with comments | « ui/file_manager/zip_archiver/unpacker/icons/icon96.png ('k') | ui/file_manager/zip_archiver/unpacker/js/background.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698