| 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 825ebe18d87d4baee84392407abf5b9173fc478b..6bfbab0ff8f1e0abd267049a3c218b3bb9d311dd 100644
|
| --- a/ui/file_manager/file_manager/background/js/import_history.js
|
| +++ b/ui/file_manager/file_manager/background/js/import_history.js
|
| @@ -16,6 +16,14 @@ importer.ImportHistory = function() {};
|
| * @param {!FileEntry} entry
|
| * @param {!importer.Destination} destination
|
| * @return {!Promise.<boolean>} Resolves with true if the FileEntry
|
| + * was previously copied to the device.
|
| + */
|
| +importer.ImportHistory.prototype.wasCopied;
|
| +
|
| +/**
|
| + * @param {!FileEntry} entry
|
| + * @param {!importer.Destination} destination
|
| + * @return {!Promise.<boolean>} Resolves with true if the FileEntry
|
| * was previously imported to the specified destination.
|
| */
|
| importer.ImportHistory.prototype.wasImported;
|
| @@ -23,6 +31,13 @@ importer.ImportHistory.prototype.wasImported;
|
| /**
|
| * @param {!FileEntry} entry
|
| * @param {!importer.Destination} destination
|
| + * @param {string} url
|
| + */
|
| +importer.ImportHistory.prototype.markCopied;
|
| +
|
| +/**
|
| + * @param {!FileEntry} entry
|
| + * @param {!importer.Destination} destination
|
| * @return {!Promise.<?>} Resolves when the operation is completed.
|
| */
|
| importer.ImportHistory.prototype.markImported;
|
| @@ -43,6 +58,7 @@ importer.ImportHistory.prototype.removeObserver;
|
|
|
| /** @enum {string} */
|
| importer.ImportHistory.State = {
|
| + 'COPIED': 'copied',
|
| 'IMPORTED': 'imported'
|
| };
|
|
|
| @@ -78,12 +94,24 @@ importer.DummyImportHistory.prototype.getHistory = function() {
|
| };
|
|
|
| /** @override */
|
| +importer.DummyImportHistory.prototype.wasCopied =
|
| + function(entry, destination) {
|
| + return Promise.resolve(this.answer_);
|
| +};
|
| +
|
| +/** @override */
|
| importer.DummyImportHistory.prototype.wasImported =
|
| function(entry, destination) {
|
| return Promise.resolve(this.answer_);
|
| };
|
|
|
| /** @override */
|
| +importer.DummyImportHistory.prototype.markCopied =
|
| + function(entry, destination, url) {
|
| + return Promise.resolve();
|
| +};
|
| +
|
| +/** @override */
|
| importer.DummyImportHistory.prototype.markImported =
|
| function(entry, destination) {
|
| return Promise.resolve();
|
| @@ -96,6 +124,14 @@ importer.DummyImportHistory.prototype.addObserver = function(observer) {};
|
| importer.DummyImportHistory.prototype.removeObserver = function(observer) {};
|
|
|
| /**
|
| + * @private @enum {number}
|
| + */
|
| +importer.RecordType_ = {
|
| + COPY: 0,
|
| + IMPORT: 1
|
| +};
|
| +
|
| +/**
|
| * An {@code ImportHistory} implementation that reads from and
|
| * writes to a storage object.
|
| *
|
| @@ -111,12 +147,20 @@ importer.PersistentImportHistory = function(storage) {
|
| this.storage_ = storage;
|
|
|
| /**
|
| + * An in-memory representation of local copy history.
|
| + * The first value is the "key" (as generated internally
|
| + * from a file entry).
|
| + * @private {!Object.<string, !Object.<!importer.Destination, string>>}
|
| + */
|
| + this.copiedEntries_ = {};
|
| +
|
| + /**
|
| * 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>>}
|
| */
|
| - this.entries_ = {};
|
| + this.importedEntries_ = {};
|
|
|
| /** @private {!Array.<!importer.ImportHistory.Observer>} */
|
| this.observers_ = [];
|
| @@ -135,11 +179,15 @@ importer.PersistentImportHistory = function(storage) {
|
| * @private
|
| */
|
| importer.PersistentImportHistory.prototype.refresh_ = function() {
|
| - var oldEntries = this.entries_;
|
| - this.entries_ = {};
|
| + var oldCopiedEntries = this.copiedEntries_;
|
| + var oldImportedEntries = this.importedEntries_;
|
| +
|
| + this.copiedEntries_ = {};
|
| + this.importedEntries_ = {};
|
| return this.storage_.readAll()
|
| .then(this.updateHistoryRecords_.bind(this))
|
| - .then(this.mergeEntries_.bind(this, oldEntries))
|
| + .then(this.mergeCopiedEntries_.bind(this, oldCopiedEntries))
|
| + .then(this.mergeImportedEntries_.bind(this, oldImportedEntries))
|
| .then(
|
| /**
|
| * @return {!importer.PersistentImportHistory}
|
| @@ -151,32 +199,58 @@ importer.PersistentImportHistory.prototype.refresh_ = function() {
|
| };
|
|
|
| /**
|
| - * Adds all entries not already present in history.
|
| + * Adds all copied entries into existing entries.
|
| + *
|
| + * @param {!Object.<string, !Object.<!importer.Destination, string>>} entries
|
| + * @return {!Promise.<?>} Resolves once all updates are completed.
|
| + * @private
|
| + */
|
| +importer.PersistentImportHistory.prototype.mergeCopiedEntries_ =
|
| + function(entries) {
|
| + var promises = [];
|
| + for (var key in entries) {
|
| + for (var destination in entries[key]) {
|
| + // This method is only called when data is reloaded from disk.
|
| + // In such a situation we defend against loss of in-memory data
|
| + // by copying it out of the way, reloading data from disk, then
|
| + // merging that data back into the freshly loaded data. For that
|
| + // reason we will write newly created entries back to disk.
|
| +
|
| + var path = entries[key][destination];
|
| + if (this.updateInMemoryRecord_(
|
| + importer.RecordType_.COPY, key, destination, path)) {
|
| + promises.push(this.storage_.write(
|
| + [importer.RecordType_.COPY, key, destination, path]));
|
| + }
|
| + }
|
| + }
|
| + return Promise.all(promises);
|
| +};
|
| +
|
| +/**
|
| + * Adds all imported entries into existing entries.
|
| *
|
| * @param {!Object.<string, !Array.<string>>} entries
|
| * @return {!Promise.<?>} Resolves once all updates are completed.
|
| * @private
|
| */
|
| -importer.PersistentImportHistory.prototype.mergeEntries_ = function(entries) {
|
| +importer.PersistentImportHistory.prototype.mergeImportedEntries_ =
|
| + function(entries) {
|
| var promises = [];
|
| - Object.keys(entries).forEach(
|
| - /**
|
| - * @param {string} key
|
| - * @this {importer.PersistentImportHistory}
|
| - */
|
| - function(key) {
|
| - entries[key].forEach(
|
| - /**
|
| - * @param {string} key
|
| - * @this {importer.PersistentImportHistory}
|
| - */
|
| - function(destination) {
|
| - if (this.getDestinations_(key).indexOf(destination) >= 0) {
|
| - this.updateHistoryRecord_(key, destination);
|
| - promises.push(this.storage_.write([key, destination]));
|
| - }
|
| + for (var key in entries) {
|
| + entries[key].forEach(
|
| + /**
|
| + * @param {string} key
|
| + * @this {importer.PersistentImportHistory}
|
| + */
|
| + function(destination) {
|
| + if (this.updateInMemoryRecord_(
|
| + importer.RecordType_.IMPORT, key, destination)) {
|
| + promises.push(this.storage_.write(
|
| + [importer.RecordType_.IMPORT, key, destination]));
|
| + }
|
| }.bind(this));
|
| - }.bind(this));
|
| + }
|
| return Promise.all(promises);
|
| };
|
|
|
| @@ -212,26 +286,66 @@ importer.PersistentImportHistory.prototype.updateHistoryRecords_ =
|
| * @this {importer.PersistentImportHistory}
|
| */
|
| function(record) {
|
| - this.updateHistoryRecord_(record[0], record[1]);
|
| + this.updateInMemoryRecord_(record[0], record[1], record[2], record[3]);
|
| }.bind(this));
|
| };
|
|
|
| /**
|
| * Adds a history entry to the in-memory history model.
|
| + * @param {!importer.RecordType_} type
|
| * @param {string} key
|
| - * @param {string} destination
|
| + * @param {!importer.Destination} destination
|
| + * @param {*=} opt_payload
|
| + * @return {boolean} True if a record was created.
|
| * @private
|
| */
|
| -importer.PersistentImportHistory.prototype.updateHistoryRecord_ =
|
| - function(key, destination) {
|
| - if (key in this.entries_) {
|
| - this.entries_[key].push(destination);
|
| - } else {
|
| - this.entries_[key] = [destination];
|
| +importer.PersistentImportHistory.prototype.updateInMemoryRecord_ =
|
| + function(type, key, destination, opt_payload) {
|
| +
|
| + switch (type) {
|
| + case importer.RecordType_.COPY:
|
| + if (!this.copiedEntries_.hasOwnProperty(key)) {
|
| + this.copiedEntries_[key] = {};
|
| + }
|
| + if (!this.copiedEntries_[key].hasOwnProperty(destination)) {
|
| + this.copiedEntries_[key][destination] = /** @type {string} */ (
|
| + opt_payload);
|
| + return true;
|
| + }
|
| + break;
|
| + case importer.RecordType_.IMPORT:
|
| + if (!this.importedEntries_.hasOwnProperty(key)) {
|
| + this.importedEntries_[key] = [destination];
|
| + }
|
| + if (this.importedEntries_[key].indexOf(destination) === -1) {
|
| + this.importedEntries_[key].push(destination);
|
| + return true;
|
| + }
|
| + break;
|
| + default:
|
| + console.log('Ignoring record with unrecognized type: ' + type);
|
| + return false;
|
| }
|
| };
|
|
|
| /** @override */
|
| +importer.PersistentImportHistory.prototype.wasCopied =
|
| + function(entry, destination) {
|
| + return this.whenReady_
|
| + .then(this.createKey_.bind(this, entry))
|
| + .then(
|
| + /**
|
| + * @param {string} key
|
| + * @return {boolean}
|
| + * @this {importer.PersistentImportHistory}
|
| + */
|
| + function(key) {
|
| + return key in this.copiedEntries_ &&
|
| + destination in this.copiedEntries_[key];
|
| + }.bind(this));
|
| +};
|
| +
|
| +/** @override */
|
| importer.PersistentImportHistory.prototype.wasImported =
|
| function(entry, destination) {
|
| return this.whenReady_
|
| @@ -239,7 +353,7 @@ importer.PersistentImportHistory.prototype.wasImported =
|
| .then(
|
| /**
|
| * @param {string} key
|
| - * @return {!Promise.<boolean>}
|
| + * @return {boolean}
|
| * @this {importer.PersistentImportHistory}
|
| */
|
| function(key) {
|
| @@ -248,6 +362,30 @@ importer.PersistentImportHistory.prototype.wasImported =
|
| };
|
|
|
| /** @override */
|
| +importer.PersistentImportHistory.prototype.markCopied =
|
| + function(entry, destination, url) {
|
| + return this.whenReady_
|
| + .then(this.createKey_.bind(this, entry))
|
| + .then(
|
| + /**
|
| + * @param {string} key
|
| + * @return {!Promise.<?>}
|
| + * @this {importer.ImportHistory}
|
| + */
|
| + function(key) {
|
| + return this.storeRecord_(
|
| + importer.RecordType_.COPY,
|
| + key,
|
| + destination,
|
| + url);
|
| + }.bind(this))
|
| + .then(this.notifyObservers_.bind(
|
| + this,
|
| + entry,
|
| + importer.ImportHistory.State.COPIED));
|
| +};
|
| +
|
| +/** @override */
|
| importer.PersistentImportHistory.prototype.markImported =
|
| function(entry, destination) {
|
| return this.whenReady_
|
| @@ -259,9 +397,15 @@ importer.PersistentImportHistory.prototype.markImported =
|
| * @this {importer.ImportHistory}
|
| */
|
| function(key) {
|
| - return this.addDestination_(destination, key);
|
| + return this.storeRecord_(
|
| + importer.RecordType_.IMPORT,
|
| + key,
|
| + destination);
|
| }.bind(this))
|
| - .then(this.notifyObservers_.bind(this, entry));
|
| + .then(this.notifyObservers_.bind(
|
| + this,
|
| + entry,
|
| + importer.ImportHistory.State.IMPORTED));
|
| };
|
|
|
| /** @override */
|
| @@ -283,29 +427,34 @@ importer.PersistentImportHistory.prototype.removeObserver =
|
|
|
| /**
|
| * @param {!FileEntry} subject
|
| + * @param {!importer.ImportHistory.State} newState
|
| * @private
|
| */
|
| importer.PersistentImportHistory.prototype.notifyObservers_ =
|
| - function(subject) {
|
| + function(subject, newState) {
|
| this.observers_.forEach(
|
| function(observer) {
|
| observer({
|
| - state: importer.ImportHistory.State.IMPORTED,
|
| + state: newState,
|
| entry: subject
|
| });
|
| }.bind(this));
|
| };
|
|
|
| /**
|
| - * @param {string} destination
|
| + * @param {!importer.RecordType_} type
|
| * @param {string} key
|
| + * @param {!importer.Destination} destination
|
| + * @param {*=} opt_payload
|
| * @return {!Promise.<?>} Resolves once the write has been completed.
|
| * @private
|
| */
|
| -importer.PersistentImportHistory.prototype.addDestination_ =
|
| - function(destination, key) {
|
| - this.updateHistoryRecord_(key, destination);
|
| - return this.storage_.write([key, destination]);
|
| +importer.PersistentImportHistory.prototype.storeRecord_ =
|
| + function(type, key, destination, opt_payload) {
|
| + this.updateInMemoryRecord_(type, key, destination, opt_payload);
|
| + return !!opt_payload ?
|
| + this.storage_.write([type, key, destination, opt_payload]) :
|
| + this.storage_.write([type, key, destination]);
|
| };
|
|
|
| /**
|
| @@ -315,7 +464,7 @@ importer.PersistentImportHistory.prototype.addDestination_ =
|
| * @private
|
| */
|
| importer.PersistentImportHistory.prototype.getDestinations_ = function(key) {
|
| - return key in this.entries_ ? this.entries_[key] : [];
|
| + return key in this.importedEntries_ ? this.importedEntries_[key] : [];
|
| };
|
|
|
| /**
|
| @@ -476,7 +625,7 @@ importer.ChromeSyncFileEntryProvider = function() {
|
| };
|
|
|
| /** @private @const {string} */
|
| -importer.ChromeSyncFileEntryProvider.FILE_NAME_ = 'import-history.data';
|
| +importer.ChromeSyncFileEntryProvider.FILE_NAME_ = 'import-history.log';
|
|
|
| /**
|
| * Wraps chrome.syncFileSystem.onFileStatusChanged
|
| @@ -678,6 +827,9 @@ importer.FileEntryRecordStorage.prototype.write = function(record) {
|
| */
|
| importer.FileEntryRecordStorage.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
|
| @@ -685,10 +837,6 @@ importer.FileEntryRecordStorage.prototype.writeRecord_ =
|
| * @this {importer.FileEntryRecordStorage}
|
| */
|
| function(resolve, reject) {
|
| - var blob = new Blob(
|
| - [JSON.stringify(record) + ',\n'],
|
| - {type: 'text/plain; charset=UTF-8'});
|
| -
|
| writer.onwriteend = resolve;
|
| writer.onerror = reject;
|
|
|
| @@ -759,31 +907,23 @@ importer.FileEntryRecordStorage.prototype.readFileAsText_ = function(file) {
|
| * Parses the text.
|
| *
|
| * @param {string} text
|
| - * @return {!Promise.<!Array.<!Array.<*>>>}
|
| + * @return {!Array.<!Array.<*>>}
|
| * @private
|
| */
|
| importer.FileEntryRecordStorage.prototype.parse_ = function(text) {
|
| - return new Promise(
|
| - /**
|
| - * @param {function()} resolve
|
| - * @param {function()} reject
|
| - * @this {importer.FileEntryRecordStorage}
|
| - */
|
| - function(resolve, reject) {
|
| - if (text.length === 0) {
|
| - resolve([]);
|
| - } else {
|
| - // Dress up the contents of the file like an array,
|
| - // so the JSON object can parse it using JSON.parse.
|
| - // That means we need to both:
|
| - // 1) Strip the trailing ',\n' from the last record
|
| - // 2) Surround the whole string in brackets.
|
| - // NOTE: JSON.parse is WAY faster than parsing this
|
| - // ourselves in javascript.
|
| - var json = '[' + text.substring(0, text.length - 2) + ']';
|
| - resolve(JSON.parse(json));
|
| - }
|
| - }.bind(this));
|
| + if (text.length === 0) {
|
| + return [];
|
| + } else {
|
| + // Dress up the contents of the file like an array,
|
| + // so the JSON object can parse it using JSON.parse.
|
| + // That means we need to both:
|
| + // 1) Strip the trailing ',\n' from the last record
|
| + // 2) Surround the whole string in brackets.
|
| + // 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));
|
| + }
|
| };
|
|
|
| /**
|
|
|