OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Shared cloud importer namespace | 5 // Shared cloud importer namespace |
6 var importer = importer || {}; | 6 var importer = importer || {}; |
7 | 7 |
8 /** @enum {string} */ | 8 /** @enum {string} */ |
9 importer.ScanEvent = { | 9 importer.ScanEvent = { |
10 FINALIZED: 'finalized', | 10 FINALIZED: 'finalized', |
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
360 }; | 360 }; |
361 | 361 |
362 /** | 362 /** |
363 * @return {!Promise.<!Object>} | 363 * @return {!Promise.<!Object>} |
364 */ | 364 */ |
365 importer.PromisingFileEntry.prototype.getMetadata = function() { | 365 importer.PromisingFileEntry.prototype.getMetadata = function() { |
366 return new Promise(this.fileEntry_.getMetadata.bind(this.fileEntry_)); | 366 return new Promise(this.fileEntry_.getMetadata.bind(this.fileEntry_)); |
367 }; | 367 }; |
368 | 368 |
369 /** | 369 /** |
| 370 * This prefix is stripped from URL used in import history. It is stripped |
| 371 * to same on disk space, parsing time, and runtime memory. |
| 372 * @private @const {string} |
| 373 */ |
| 374 importer.APP_URL_PREFIX_ = |
| 375 'filesystem:chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/external'; |
| 376 |
| 377 /** |
| 378 * Strips non-unique information from the URL. The resulting |
| 379 * value can be reconstituted using {@code importer.inflateAppUrl}. |
| 380 * |
| 381 * @param {string} url |
| 382 * @return {string} |
| 383 */ |
| 384 importer.deflateAppUrl = function(url) { |
| 385 if (url.substring(0, importer.APP_URL_PREFIX_.length) === |
| 386 importer.APP_URL_PREFIX_) { |
| 387 return '$' + url.substring(importer.APP_URL_PREFIX_.length); |
| 388 } |
| 389 |
| 390 return url; |
| 391 }; |
| 392 |
| 393 /** |
| 394 * Reconstitutes a url previous deflated by {@code deflateAppUrl}. |
| 395 * Returns the original string if it can't be inflated. |
| 396 * |
| 397 * @param {string} deflated |
| 398 * @return {string} |
| 399 */ |
| 400 importer.inflateAppUrl = function(deflated) { |
| 401 if (deflated.substring(0, 1) === '$') { |
| 402 return importer.APP_URL_PREFIX_ + deflated.substring(1); |
| 403 } |
| 404 return deflated; |
| 405 }; |
| 406 |
| 407 /** |
370 * @param {!FileEntry} fileEntry | 408 * @param {!FileEntry} fileEntry |
371 * @return {!Promise.<string>} Resolves with a "hashcode" consisting of | 409 * @return {!Promise.<string>} Resolves with a "hashcode" consisting of |
372 * just the last modified time and the file size. | 410 * just the last modified time and the file size. |
373 */ | 411 */ |
374 importer.createMetadataHashcode = function(fileEntry) { | 412 importer.createMetadataHashcode = function(fileEntry) { |
375 var entry = new importer.PromisingFileEntry(fileEntry); | 413 var entry = new importer.PromisingFileEntry(fileEntry); |
376 return new Promise( | 414 return new Promise( |
377 /** | 415 /** |
378 * @param {function()} resolve | 416 * @param {function()} resolve |
379 * @param {function()} reject | 417 * @param {function()} reject |
380 * @this {importer.PersistentImportHistory} | 418 * @this {importer.PersistentImportHistory} |
381 */ | 419 */ |
382 function(resolve, reject) { | 420 function(resolve, reject) { |
383 entry.getMetadata() | 421 entry.getMetadata() |
384 .then( | 422 .then( |
385 /** | 423 /** |
386 * @param {!Object} metadata | 424 * @param {!Object} metadata |
387 * @return {!Promise.<string>} | 425 * @return {!Promise.<string>} |
388 * @this {importer.PersistentImportHistory} | 426 * @this {importer.PersistentImportHistory} |
389 */ | 427 */ |
390 function(metadata) { | 428 function(metadata) { |
391 if (!('modificationTime' in metadata)) { | 429 if (!('modificationTime' in metadata)) { |
392 reject('File entry missing "modificationTime" field.'); | 430 reject('File entry missing "modificationTime" field.'); |
393 } else if (!('size' in metadata)) { | 431 } else if (!('size' in metadata)) { |
394 reject('File entry missing "size" field.'); | 432 reject('File entry missing "size" field.'); |
395 } else { | 433 } else { |
396 resolve(metadata.modificationTime + '_' + metadata.size); | 434 var secondsSinceEpoch = |
| 435 importer.toSecondsFromEpoch(metadata.modificationTime); |
| 436 resolve(secondsSinceEpoch + '_' + metadata.size); |
397 } | 437 } |
398 }.bind(this)); | 438 }.bind(this)); |
399 }.bind(this)); | 439 }.bind(this)) |
| 440 .catch(importer.getLogger().catcher('importer-common-create-hashcode')); |
400 }; | 441 }; |
401 | 442 |
402 /** | 443 /** |
| 444 * @param {string} date A date string in the form |
| 445 * expected by Date.parse. |
| 446 * @return {string} The number of seconds from epoch to the date...as a string. |
| 447 */ |
| 448 importer.toSecondsFromEpoch = function(date) { |
| 449 // Since we're parsing a value that only has |
| 450 // precision to the second, our last three digits |
| 451 // will always be 000. We strip them and end up |
| 452 // with seconds. |
| 453 var milliseconds = String(Date.parse(date)); |
| 454 return milliseconds.substring(0, milliseconds.length - 3); |
| 455 }; |
| 456 |
| 457 /** |
403 * Factory interface for creating/accessing synced {@code FileEntry} | 458 * Factory interface for creating/accessing synced {@code FileEntry} |
404 * instances and listening to sync events on those files. | 459 * instances and listening to sync events on those files. |
405 * | 460 * |
406 * @interface | 461 * @interface |
407 */ | 462 */ |
408 importer.SyncFileEntryProvider = function() {}; | 463 importer.SyncFileEntryProvider = function() {}; |
409 | 464 |
410 /** | 465 /** |
411 * Adds a listener to be notified when the the FileEntry owned/managed | |
412 * by this class is updated via sync. | |
413 * | |
414 * @param {function()} syncListener | |
415 */ | |
416 importer.SyncFileEntryProvider.prototype.addSyncListener; | |
417 | |
418 /** | |
419 * Provides accsess to the sync FileEntry owned/managed by this class. | 466 * Provides accsess to the sync FileEntry owned/managed by this class. |
420 * | 467 * |
421 * @return {!Promise.<!FileEntry>} | 468 * @return {!Promise.<!FileEntry>} |
422 */ | 469 */ |
423 importer.SyncFileEntryProvider.prototype.getSyncFileEntry; | 470 importer.SyncFileEntryProvider.prototype.getSyncFileEntry; |
424 | 471 |
425 /** | 472 /** |
426 * Factory for synchronized files based on chrome.syncFileSystem. | 473 * Factory for synchronized files based on chrome.syncFileSystem. |
427 * | 474 * |
428 * @constructor | 475 * @constructor |
429 * @implements {importer.SyncFileEntryProvider} | 476 * @implements {importer.SyncFileEntryProvider} |
430 * @struct | 477 * @struct |
431 * | 478 * |
432 * @param {string} fileName | 479 * @param {string} fileName |
433 */ | 480 */ |
434 importer.ChromeSyncFileEntryProvider = function(fileName) { | 481 importer.ChromeSyncFileEntryProvider = function(fileName) { |
435 | 482 |
436 /** @private {string} */ | 483 /** @private {string} */ |
437 this.fileName_ = fileName; | 484 this.fileName_ = fileName; |
438 | 485 |
439 /** @private {!Array.<function()>} */ | 486 /** @private {!Array.<function()>} */ |
440 this.syncListeners_ = []; | 487 this.syncListeners_ = []; |
441 | 488 |
442 /** @private {Promise.<!FileEntry>} */ | 489 /** @private {Promise.<!FileEntry>} */ |
443 this.fileEntryPromise_ = null; | 490 this.fileEntryPromise_ = null; |
444 | |
445 this.monitorSyncEvents_(); | |
446 }; | 491 }; |
447 | 492 |
448 /** | 493 /** |
449 * Returns a sync FileEntry. Convenience method for class that just want | 494 * Returns a sync FileEntry. Convenience method for class that just want |
450 * a file, but don't need to monitor changes. | 495 * a file, but don't need to monitor changes. |
451 * @param {!Promise.<string>} fileNamePromise | 496 * @param {!Promise.<string>} fileNamePromise |
452 * @return {!Promise.<!FileEntry>} | 497 * @return {!Promise.<!FileEntry>} |
453 */ | 498 */ |
454 importer.ChromeSyncFileEntryProvider.getFileEntry = | 499 importer.ChromeSyncFileEntryProvider.getFileEntry = |
455 function(fileNamePromise) { | 500 function(fileNamePromise) { |
456 return fileNamePromise.then( | 501 return fileNamePromise.then( |
457 function(fileName) { | 502 function(fileName) { |
458 return new importer.ChromeSyncFileEntryProvider(fileName) | 503 return new importer.ChromeSyncFileEntryProvider(fileName) |
459 .getSyncFileEntry(); | 504 .getSyncFileEntry(); |
460 }); | 505 }); |
461 }; | 506 }; |
462 | 507 |
463 /** | |
464 * Wraps chrome.syncFileSystem.onFileStatusChanged | |
465 * so that we can report to our listeners when our file has changed. | |
466 * @private | |
467 */ | |
468 importer.ChromeSyncFileEntryProvider.prototype.monitorSyncEvents_ = | |
469 function() { | |
470 chrome.syncFileSystem.onFileStatusChanged.addListener( | |
471 this.handleSyncEvent_.bind(this)); | |
472 }; | |
473 | |
474 /** @override */ | |
475 importer.ChromeSyncFileEntryProvider.prototype.addSyncListener = | |
476 function(listener) { | |
477 if (this.syncListeners_.indexOf(listener) === -1) { | |
478 this.syncListeners_.push(listener); | |
479 } | |
480 }; | |
481 | |
482 /** @override */ | 508 /** @override */ |
483 importer.ChromeSyncFileEntryProvider.prototype.getSyncFileEntry = function() { | 509 importer.ChromeSyncFileEntryProvider.prototype.getSyncFileEntry = function() { |
484 if (this.fileEntryPromise_) { | 510 if (this.fileEntryPromise_) { |
485 return /** @type {!Promise.<!FileEntry>} */ (this.fileEntryPromise_); | 511 return /** @type {!Promise.<!FileEntry>} */ (this.fileEntryPromise_); |
486 }; | 512 }; |
487 | 513 |
488 this.fileEntryPromise_ = this.getFileSystem_() | 514 this.fileEntryPromise_ = this.getFileSystem_() |
489 .then( | 515 .then( |
490 /** | 516 /** |
491 * @param {!FileSystem} fileSystem | 517 * @param {!FileSystem} fileSystem |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
582 function(fileEntry) { | 608 function(fileEntry) { |
583 if (event['fileEntry'].fullPath !== fileEntry.fullPath) { | 609 if (event['fileEntry'].fullPath !== fileEntry.fullPath) { |
584 return; | 610 return; |
585 } | 611 } |
586 | 612 |
587 if (event.direction && event.direction !== 'remote_to_local') { | 613 if (event.direction && event.direction !== 'remote_to_local') { |
588 return; | 614 return; |
589 } | 615 } |
590 | 616 |
591 if (event.action && event.action !== 'updated') { | 617 if (event.action && event.action !== 'updated') { |
592 console.error( | 618 console.warn( |
593 'Unexpected sync event action for sync file: ' + event.action); | 619 'Unusual sync event action for sync file: ' + event.action); |
594 return; | 620 return; |
595 } | 621 } |
596 | 622 |
597 this.syncListeners_.forEach( | 623 this.syncListeners_.forEach( |
598 /** | 624 /** |
599 * @param {function()} listener | 625 * @param {function()} listener |
600 * @this {importer.ChromeSyncFileEntryProvider} | 626 * @this {importer.ChromeSyncFileEntryProvider} |
601 */ | 627 */ |
602 function(listener) { | 628 function(listener) { |
603 // Notify by way of a promise so that it is fully asynchronous | 629 // Notify by way of a promise so that it is fully asynchronous |
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
928 }); | 954 }); |
929 }; | 955 }; |
930 | 956 |
931 /** @private @const {!importer.ChromeLocalStorage} */ | 957 /** @private @const {!importer.ChromeLocalStorage} */ |
932 importer.ChromeLocalStorage.INSTANCE_ = new importer.ChromeLocalStorage(); | 958 importer.ChromeLocalStorage.INSTANCE_ = new importer.ChromeLocalStorage(); |
933 | 959 |
934 /** @return {!importer.ChromeLocalStorage} */ | 960 /** @return {!importer.ChromeLocalStorage} */ |
935 importer.ChromeLocalStorage.getInstance = function() { | 961 importer.ChromeLocalStorage.getInstance = function() { |
936 return importer.ChromeLocalStorage.INSTANCE_; | 962 return importer.ChromeLocalStorage.INSTANCE_; |
937 }; | 963 }; |
OLD | NEW |