| Index: ui/file_manager/file_manager/background/js/import_history.js
|
| diff --git a/ui/file_manager/file_manager/background/js/import_history.js b/ui/file_manager/file_manager/background/js/import_history.js
|
| index b0ec26ffc9e8990fbd6a03fc7ba7a91dc115ea34..650dc93494c38a166f35358f3c6205fe26f252c2 100644
|
| --- a/ui/file_manager/file_manager/background/js/import_history.js
|
| +++ b/ui/file_manager/file_manager/background/js/import_history.js
|
| @@ -13,7 +13,7 @@ var importer = importer || {};
|
| importer.ImportHistory = function() {};
|
|
|
| /**
|
| - * @return {!Promise.<!importer.ImportHistory>} Resolves when history
|
| + * @return {!Promise<!importer.ImportHistory>} Resolves when history
|
| * has been fully loaded.
|
| */
|
| importer.ImportHistory.prototype.whenReady;
|
| @@ -21,7 +21,7 @@ importer.ImportHistory.prototype.whenReady;
|
| /**
|
| * @param {!FileEntry} entry
|
| * @param {!importer.Destination} destination
|
| - * @return {!Promise.<boolean>} Resolves with true if the FileEntry
|
| + * @return {!Promise<boolean>} Resolves with true if the FileEntry
|
| * was previously copied to the device.
|
| */
|
| importer.ImportHistory.prototype.wasCopied;
|
| @@ -29,7 +29,7 @@ importer.ImportHistory.prototype.wasCopied;
|
| /**
|
| * @param {!FileEntry} entry
|
| * @param {!importer.Destination} destination
|
| - * @return {!Promise.<boolean>} Resolves with true if the FileEntry
|
| + * @return {!Promise<boolean>} Resolves with true if the FileEntry
|
| * was previously imported to the specified destination.
|
| */
|
| importer.ImportHistory.prototype.wasImported;
|
| @@ -44,20 +44,20 @@ importer.ImportHistory.prototype.markCopied;
|
| /**
|
| * List urls of all files that are marked as copied, but not marked as synced.
|
| * @param {!importer.Destination} destination
|
| - * @return {!Promise.<!Array.<string>>}
|
| + * @return {!Promise<!Array<string>>}
|
| */
|
| importer.ImportHistory.prototype.listUnimportedUrls;
|
|
|
| /**
|
| * @param {!FileEntry} entry
|
| * @param {!importer.Destination} destination
|
| - * @return {!Promise.<?>} Resolves when the operation is completed.
|
| + * @return {!Promise<?>} Resolves when the operation is completed.
|
| */
|
| importer.ImportHistory.prototype.markImported;
|
|
|
| /**
|
| * @param {string} destinationUrl
|
| - * @return {!Promise.<?>} Resolves when the operation is completed.
|
| + * @return {!Promise<?>} Resolves when the operation is completed.
|
| */
|
| importer.ImportHistory.prototype.markImportedByUrl;
|
|
|
| @@ -187,11 +187,11 @@ importer.Urls;
|
| * @implements {importer.ImportHistory}
|
| * @struct
|
| *
|
| - * @param {function(!FileEntry): !Promise.<string>} hashGenerator
|
| + * @param {function(!FileEntry): !Promise<string>} hashGenerator
|
| * @param {!importer.RecordStorage} storage
|
| */
|
| importer.PersistentImportHistory = function(hashGenerator, storage) {
|
| - /** @private {function(!FileEntry): !Promise.<string>} */
|
| + /** @private {function(!FileEntry): !Promise<string>} */
|
| this.createKey_ = hashGenerator;
|
|
|
| /** @private {!importer.RecordStorage} */
|
| @@ -216,11 +216,11 @@ importer.PersistentImportHistory = function(hashGenerator, storage) {
|
| * An in-memory representation of import history.
|
| * The first value is the "key" (as generated internally
|
| * from a file entry).
|
| - * @private {!Object.<string, !Array.<importer.Destination>>}
|
| + * @private {!Object.<string, !Array<importer.Destination>>}
|
| */
|
| this.importedEntries_ = {};
|
|
|
| - /** @private {!Array.<!importer.ImportHistory.Observer>} */
|
| + /** @private {!Array<!importer.ImportHistory.Observer>} */
|
| this.observers_ = [];
|
|
|
| /** @private {Promise.<!importer.PersistentImportHistory>} */
|
| @@ -231,13 +231,12 @@ importer.PersistentImportHistory = function(hashGenerator, storage) {
|
| * Reloads history from disk. Should be called when the file
|
| * is changed by an external source.
|
| *
|
| - * @return {!Promise.<!importer.PersistentImportHistory>} Resolves when
|
| + * @return {!Promise<!importer.PersistentImportHistory>} Resolves when
|
| * history has been refreshed.
|
| * @private
|
| */
|
| importer.PersistentImportHistory.prototype.load_ = function() {
|
| - return this.storage_.readAll()
|
| - .then(this.updateHistoryRecords_.bind(this))
|
| + return this.storage_.readAll(this.updateInMemoryRecord_.bind(this))
|
| .then(
|
| /**
|
| * @return {!importer.PersistentImportHistory}
|
| @@ -250,26 +249,16 @@ importer.PersistentImportHistory.prototype.load_ = function() {
|
| };
|
|
|
| /**
|
| - * @return {!Promise.<!importer.ImportHistory>}
|
| + * @return {!Promise<!importer.ImportHistory>}
|
| */
|
| importer.PersistentImportHistory.prototype.whenReady = function() {
|
| - return /** @type {!Promise.<!importer.ImportHistory>} */ (this.whenReady_);
|
| -};
|
| -
|
| -/**
|
| - * Adds a history entry to the in-memory history model.
|
| - * @param {!Array.<!Array.<*>>} records
|
| - * @private
|
| - */
|
| -importer.PersistentImportHistory.prototype.updateHistoryRecords_ =
|
| - function(records) {
|
| - records.forEach(this.updateInMemoryRecord_.bind(this));
|
| + return /** @type {!Promise<!importer.ImportHistory>} */ (this.whenReady_);
|
| };
|
|
|
| /**
|
| * Detects record type and expands records to appropriate arguments.
|
| *
|
| - * @param {!Array.<*>} record
|
| + * @param {!Array<*>} record
|
| * @this {importer.PersistentImportHistory}
|
| */
|
| importer.PersistentImportHistory.prototype.updateInMemoryRecord_ =
|
| @@ -401,7 +390,7 @@ importer.PersistentImportHistory.prototype.markCopied =
|
| .then(
|
| /**
|
| * @param {string} key
|
| - * @return {!Promise.<?>}
|
| + * @return {!Promise<?>}
|
| * @this {importer.ImportHistory}
|
| */
|
| function(key) {
|
| @@ -453,7 +442,7 @@ importer.PersistentImportHistory.prototype.markImported =
|
| .then(
|
| /**
|
| * @param {string} key
|
| - * @return {!Promise.<?>}
|
| + * @return {!Promise<?>}
|
| * @this {importer.ImportHistory}
|
| */
|
| function(key) {
|
| @@ -478,38 +467,44 @@ importer.PersistentImportHistory.prototype.markImportedByUrl =
|
| if (!!key) {
|
| var copyData = this.copiedEntries_[key];
|
|
|
| - // we could build an index of this as well, but it seems
|
| + // We could build an index of this as well, but it seems
|
| // unnecessary given the fact that there will almost always
|
| // be just one destination for a file (assumption).
|
| for (var destination in copyData) {
|
| if (copyData[destination].destinationUrl === deflatedUrl) {
|
| return this.storeRecord_([
|
| - importer.RecordType_.IMPORT,
|
| - key,
|
| - destination]).then(
|
| - function() {
|
| - var sourceUrl = importer.inflateAppUrl(
|
| - copyData[destination].sourceUrl);
|
| - // Here we try to create an Entry for the source URL.
|
| - // This will allow observers to update the UI if the
|
| - // source entry is in view.
|
| - util.urlToEntry(sourceUrl).then(
|
| - function(entry) {
|
| - if (entry.isFile) {
|
| - this.notifyObservers_(
|
| - importer.ImportHistory.State.IMPORTED,
|
| - /** @type {!FileEntry} */ (entry),
|
| - destination);
|
| - }
|
| - }.bind(this))
|
| - .catch(
|
| - function(error) {
|
| - console.log('Unable to lookup original entry for: ' +
|
| - sourceUrl);
|
| - return;
|
| - });
|
| - }.bind(this)
|
| - )
|
| + importer.RecordType_.IMPORT,
|
| + key,
|
| + destination])
|
| + .then(
|
| + /** @this {importer.PersistentImportHistory} */
|
| + function() {
|
| + var sourceUrl = importer.inflateAppUrl(
|
| + copyData[destination].sourceUrl);
|
| + // Here we try to create an Entry for the source URL.
|
| + // This will allow observers to update the UI if the
|
| + // source entry is in view.
|
| + util.urlToEntry(sourceUrl).then(
|
| + /**
|
| + * @param {Entry} entry
|
| + * @this {importer.PersistentImportHistory}
|
| + */
|
| + function(entry) {
|
| + if (entry.isFile) {
|
| + this.notifyObservers_(
|
| + importer.ImportHistory.State.IMPORTED,
|
| + /** @type {!FileEntry} */ (entry),
|
| + destination);
|
| + }
|
| + }.bind(this),
|
| + function() {
|
| + console.log(
|
| + 'Unable to find original entry for: ' + sourceUrl);
|
| + return;
|
| + })
|
| + .catch(importer.getLogger().catcher(
|
| + 'notify-listeners-on-import'));
|
| + }.bind(this))
|
| .catch(importer.getLogger().catcher('mark-imported-by-url'));
|
| }
|
| }
|
| @@ -561,9 +556,9 @@ importer.PersistentImportHistory.prototype.notifyObservers_ =
|
| };
|
|
|
| /**
|
| - * @param {!Array.<*>} record
|
| + * @param {!Array<*>} record
|
| *
|
| - * @return {!Promise.<?>} Resolves once the write has been completed.
|
| + * @return {!Promise<?>} Resolves once the write has been completed.
|
| * @private
|
| */
|
| importer.PersistentImportHistory.prototype.storeRecord_ = function(record) {
|
| @@ -573,7 +568,7 @@ importer.PersistentImportHistory.prototype.storeRecord_ = function(record) {
|
|
|
| /**
|
| * @param {string} key
|
| - * @return {!Array.<string>} The list of previously noted
|
| + * @return {!Array<string>} The list of previously noted
|
| * destinations, or an empty array, if none.
|
| * @private
|
| */
|
| @@ -596,7 +591,7 @@ importer.HistoryLoader = function() {};
|
| *
|
| * @see importer.SynchronizedHistoryLoader for an example.
|
| *
|
| - * @return {!Promise.<!importer.ImportHistory>} Resolves when history instance
|
| + * @return {!Promise<!importer.ImportHistory>} Resolves when history instance
|
| * is ready.
|
| */
|
| importer.HistoryLoader.prototype.getHistory;
|
| @@ -617,12 +612,18 @@ importer.HistoryLoader.prototype.addHistoryLoadedListener;
|
| * @implements {importer.HistoryLoader}
|
| * @struct
|
| *
|
| - * @param {!importer.SyncFileEntryProvider} fileProvider
|
| + * @param {function(): !Promise<!Array<!FileEntry>>} filesProvider
|
| */
|
| -importer.SynchronizedHistoryLoader = function(fileProvider) {
|
| -
|
| - /** @private {!importer.SyncFileEntryProvider} */
|
| - this.fileProvider_ = fileProvider;
|
| +importer.SynchronizedHistoryLoader = function(filesProvider) {
|
| + /**
|
| + * @return {!Promise<!Array<!FileEntry>>} History files. Will always
|
| + * have at least one file (the "primary file"). When other devices
|
| + * have been used for import, additional files may be present
|
| + * as well. In all cases the primary file will be used for write
|
| + * operations and all non-primary files are read-only.
|
| + * @private
|
| + */
|
| + this.getHistoryFiles_ = filesProvider;
|
|
|
| /** @private {boolean} */
|
| this.needsInitialization_ = true;
|
| @@ -635,16 +636,15 @@ importer.SynchronizedHistoryLoader = function(fileProvider) {
|
| importer.SynchronizedHistoryLoader.prototype.getHistory = function() {
|
| if (this.needsInitialization_) {
|
| this.needsInitialization_ = false;
|
| -
|
| - this.fileProvider_.getSyncFileEntry()
|
| + this.getHistoryFiles_()
|
| .then(
|
| /**
|
| - * @param {!FileEntry} fileEntry
|
| - * @return {!Promise.<!importer.ImportHistory>}
|
| + * @param {!Array<!FileEntry>} fileEntries
|
| + * @return {!Promise<!importer.ImportHistory>}
|
| * @this {importer.SynchronizedHistoryLoader}
|
| */
|
| - function(fileEntry) {
|
| - var storage = new importer.FileEntryRecordStorage(fileEntry);
|
| + function(fileEntries) {
|
| + var storage = new importer.FileBasedRecordStorage(fileEntries);
|
| var history = new importer.PersistentImportHistory(
|
| importer.createMetadataHashcode,
|
| storage);
|
| @@ -677,48 +677,55 @@ importer.RecordStorage = function() {};
|
| /**
|
| * Adds a new record.
|
| *
|
| - * @param {!Array.<*>} record
|
| - * @return {!Promise.<?>} Resolves when record is added.
|
| + * @param {!Array<*>} record
|
| + * @return {!Promise<?>} Resolves when record is added.
|
| */
|
| importer.RecordStorage.prototype.write;
|
|
|
| /**
|
| * Reads all records.
|
| *
|
| - * @return {!Promise.<!Array.<!Array.<*>>>}
|
| + * @param {function(!Array<*>)} recordCallback Callback called once
|
| + * for each record loaded.
|
| */
|
| importer.RecordStorage.prototype.readAll;
|
|
|
| /**
|
| * A {@code RecordStore} that persists data in a {@code FileEntry}.
|
| *
|
| - * @param {!FileEntry} fileEntry
|
| + * @param {!Array<!FileEntry>} fileEntries The first entry is the
|
| + * "primary" file for read-write, all other are read-only
|
| + * sources of data (presumably synced from other machines).
|
| *
|
| * @constructor
|
| * @implements {importer.RecordStorage}
|
| * @struct
|
| */
|
| -importer.FileEntryRecordStorage = function(fileEntry) {
|
| +importer.FileBasedRecordStorage = function(fileEntries) {
|
| + /** @private {!Array<!importer.PromisingFileEntry>} */
|
| + this.inputFiles_ = fileEntries.map(
|
| + importer.PromisingFileEntry.create);
|
| +
|
| /** @private {!importer.PromisingFileEntry} */
|
| - this.fileEntry_ = new importer.PromisingFileEntry(fileEntry);
|
| + this.outputFile_ = this.inputFiles_[0];
|
|
|
| /**
|
| - * Serializes all writes and reads.
|
| - * @private {!Promise.<?>}
|
| + * Serializes all writes and reads on the primary file.
|
| + * @private {!Promise<?>}
|
| * */
|
| this.latestOperation_ = Promise.resolve(null);
|
| };
|
|
|
| /** @override */
|
| -importer.FileEntryRecordStorage.prototype.write = function(record) {
|
| +importer.FileBasedRecordStorage.prototype.write = function(record) {
|
| return this.latestOperation_ = this.latestOperation_
|
| .then(
|
| /**
|
| * @param {?} ignore
|
| - * @this {importer.FileEntryRecordStorage}
|
| + * @this {importer.FileBasedRecordStorage}
|
| */
|
| function(ignore) {
|
| - return this.fileEntry_.createWriter();
|
| + return this.outputFile_.createWriter();
|
| }.bind(this))
|
| .then(this.writeRecord_.bind(this, record))
|
| .catch(importer.getLogger().catcher('file-record-store-write'));
|
| @@ -729,19 +736,20 @@ importer.FileEntryRecordStorage.prototype.write = function(record) {
|
| *
|
| * @param {!Object} record
|
| * @param {!FileWriter} writer
|
| - * @return {!Promise.<?>} Resolves when write is complete.
|
| + * @return {!Promise<?>} Resolves when write is complete.
|
| * @private
|
| */
|
| -importer.FileEntryRecordStorage.prototype.writeRecord_ =
|
| +importer.FileBasedRecordStorage.prototype.writeRecord_ =
|
| function(record, writer) {
|
| var blob = new Blob(
|
| [JSON.stringify(record) + ',\n'],
|
| {type: 'text/plain; charset=UTF-8'});
|
| +
|
| return new Promise(
|
| /**
|
| * @param {function()} resolve
|
| * @param {function()} reject
|
| - * @this {importer.FileEntryRecordStorage}
|
| + * @this {importer.FileBasedRecordStorage}
|
| */
|
| function(resolve, reject) {
|
| writer.onwriteend = resolve;
|
| @@ -753,33 +761,60 @@ importer.FileEntryRecordStorage.prototype.writeRecord_ =
|
| };
|
|
|
| /** @override */
|
| -importer.FileEntryRecordStorage.prototype.readAll = function() {
|
| +importer.FileBasedRecordStorage.prototype.readAll = function(recordCallback) {
|
| return this.latestOperation_ = this.latestOperation_
|
| .then(
|
| /**
|
| * @param {?} ignore
|
| - * @this {importer.FileEntryRecordStorage}
|
| + * @this {importer.FileBasedRecordStorage}
|
| */
|
| function(ignore) {
|
| - return this.fileEntry_.file();
|
| + var filePromises = this.inputFiles_.map(
|
| + /**
|
| + * @param {!importer.PromisingFileEntry} entry
|
| + * @this {importer.FileBasedRecordStorage}
|
| + */
|
| + function(entry) {
|
| + return entry.file();
|
| + });
|
| + return Promise.all(filePromises);
|
| }.bind(this))
|
| .then(
|
| - this.readFileAsText_.bind(this),
|
| + /**
|
| + * @return {!Array<!File>}
|
| + * @this {importer.FileBasedRecordStorage}
|
| + */
|
| + function(files) {
|
| + var contentPromises = files.map(
|
| + this.readFileAsText_.bind(this));
|
| + return Promise.all(contentPromises);
|
| + }.bind(this),
|
| /**
|
| * @return {string}
|
| - * @this {importer.FileEntryRecordStorage}
|
| + * @this {importer.FileBasedRecordStorage}
|
| */
|
| function() {
|
| - console.error('Unable to read from history file.');
|
| + console.error('Unable to read from one of history files.');
|
| return '';
|
| }.bind(this))
|
| .then(
|
| /**
|
| - * @param {string} fileContents
|
| - * @this {importer.FileEntryRecordStorage}
|
| + * @param {!Array<string>} fileContents
|
| + * @this {importer.FileBasedRecordStorage}
|
| */
|
| function(fileContents) {
|
| - return this.parse_(fileContents);
|
| + var parsePromises = fileContents.map(
|
| + this.parse_.bind(this));
|
| + return Promise.all(parsePromises);
|
| + }.bind(this))
|
| + .then(
|
| + /** @param {!Array<!Array<*>>} parsedContents */
|
| + function(parsedContents) {
|
| + parsedContents.forEach(
|
| + /** @param {!Array<!Array<*>>} recordSet */
|
| + function(recordSet) {
|
| + recordSet.forEach(recordCallback);
|
| + });
|
| }.bind(this))
|
| .catch(importer.getLogger().catcher('file-record-store-read-all'));
|
| };
|
| @@ -788,15 +823,15 @@ importer.FileEntryRecordStorage.prototype.readAll = function() {
|
| * Reads the entire entry as a single string value.
|
| *
|
| * @param {!File} file
|
| - * @return {!Promise.<string>}
|
| + * @return {!Promise<string>}
|
| * @private
|
| */
|
| -importer.FileEntryRecordStorage.prototype.readFileAsText_ = function(file) {
|
| +importer.FileBasedRecordStorage.prototype.readFileAsText_ = function(file) {
|
| return new Promise(
|
| /**
|
| * @param {function()} resolve
|
| * @param {function()} reject
|
| - * @this {importer.FileEntryRecordStorage}
|
| + * @this {importer.FileBasedRecordStorage}
|
| */
|
| function(resolve, reject) {
|
| var reader = new FileReader();
|
| @@ -811,24 +846,24 @@ importer.FileEntryRecordStorage.prototype.readFileAsText_ = function(file) {
|
| }.bind(this);
|
|
|
| reader.onerror = function(error) {
|
| - console.error(error);
|
| + console.error(error);
|
| reject(error);
|
| }.bind(this);
|
|
|
| reader.readAsText(file);
|
| }.bind(this))
|
| - .catch(importer.getLogger().catcher(
|
| - 'file-record-store-read-file-as-text'));
|
| + .catch(importer.getLogger().catcher(
|
| + 'file-record-store-read-file-as-text'));
|
| };
|
|
|
| /**
|
| * Parses the text.
|
| *
|
| * @param {string} text
|
| - * @return {!Array.<!Array.<*>>}
|
| + * @return {!Array<!Array<*>>}
|
| * @private
|
| */
|
| -importer.FileEntryRecordStorage.prototype.parse_ = function(text) {
|
| +importer.FileBasedRecordStorage.prototype.parse_ = function(text) {
|
| if (text.length === 0) {
|
| return [];
|
| } else {
|
| @@ -840,7 +875,7 @@ importer.FileEntryRecordStorage.prototype.parse_ = function(text) {
|
| // NOTE: JSON.parse is WAY faster than parsing this
|
| // ourselves in javascript.
|
| var json = '[' + text.substring(0, text.length - 2) + ']';
|
| - return /** @type {!Array.<!Array.<*>>} */ (JSON.parse(json));
|
| + return /** @type {!Array<!Array<*>>} */ (JSON.parse(json));
|
| }
|
| };
|
|
|
| @@ -883,7 +918,7 @@ importer.DriveSyncWatcher.UPDATE_DELAY_MS = 3500;
|
|
|
| /**
|
| * @param {!importer.Destination} destination
|
| - * @param {!Array.<string>} unimportedUrls
|
| + * @param {!Array<string>} unimportedUrls
|
| * @private
|
| */
|
| importer.DriveSyncWatcher.prototype.updateSyncStatus_ =
|
| @@ -968,7 +1003,7 @@ importer.DriveSyncWatcher.prototype.checkSyncStatus_ =
|
|
|
| /**
|
| * @param {string} url
|
| - * @return {!Promise.<boolean>} Resolves with true if the
|
| + * @return {!Promise<boolean>} Resolves with true if the
|
| * file has been synced to the named destination.
|
| * @private
|
| */
|
| @@ -983,7 +1018,7 @@ importer.DriveSyncWatcher.prototype.getSyncStatus_ =
|
| [url],
|
| ['dirty'],
|
| /**
|
| - * @param {!Array.<Object>} propertiesList
|
| + * @param {!Array<Object>} propertiesList
|
| * @this {importer.DriveSyncWatcher}
|
| */
|
| function(propertiesList) {
|
| @@ -1029,18 +1064,30 @@ importer.RuntimeHistoryLoader.prototype.getHistory = function() {
|
| .then(
|
| /**
|
| * @param {boolean} enabled
|
| - * @return {!Promise.<!importer.ImportHistory>}
|
| + * @return {!Promise<!importer.ImportHistory>}
|
| * @this {importer.RuntimeHistoryLoader}
|
| */
|
| function(enabled) {
|
| importer.getHistoryFilename().then(
|
| function(filename) {
|
| - var loader = enabled ?
|
| - new importer.SynchronizedHistoryLoader(
|
| - new importer.ChromeSyncFileEntryProvider(
|
| - filename)) :
|
| - new importer.DummyImportHistory(false);
|
| -
|
| + var loader;
|
| + if (enabled) {
|
| + // TODO(smckay): Replace this with code to load
|
| + // all history files from ChromeSyncFileSystem.
|
| + var historyFilesProvider = function() {
|
| + return importer.ChromeSyncFilesystem.
|
| + getOrCreateFileEntry(Promise.resolve(filename))
|
| + .then(
|
| + /** @param {!FileEntry} fileEntry */
|
| + function(fileEntry) {
|
| + return Promise.resolve([fileEntry]);
|
| + });
|
| + };
|
| + var loader = new importer.SynchronizedHistoryLoader(
|
| + historyFilesProvider);
|
| + } else {
|
| + var loader = new importer.DummyImportHistory(false);
|
| + }
|
| this.historyResolver_.resolve(loader.getHistory());
|
| }.bind(this));
|
| }.bind(this))
|
|
|