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

Unified Diff: ui/file_manager/file_manager/background/js/import_history.js

Issue 1013893002: Update ImportHistory with support for reading history from multiple files. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Respond to review comments. Created 5 years, 9 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | ui/file_manager/file_manager/background/js/import_history_unittest.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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))
« no previous file with comments | « no previous file | ui/file_manager/file_manager/background/js/import_history_unittest.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698