blob: 8d79d5ba1a04a4c8ec3bdf2f0e9171d2ffcb403b [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Watcher for entry lists.
* It watches entries and remove the item if the entry is removed from file
* system.
* @param {!cr.ui.ArrayDataModel} list
* @constructor
* @struct
*/
function EntryListWatcher(list) {
/**
* Watched list.
* @type {!cr.ui.ArrayDataModel}
* @const
* @private
*/
this.list_ = list;
/**
* Set of watched URL.
* Key is watched entry URL. Value is always true.
* @type {!Object<boolean>}
* @private
*/
this.watchers_ = {};
this.list_.addEventListener('splice', this.onSplice_.bind(this));
chrome.fileManagerPrivate.onDirectoryChanged.addListener(
this.onDirectoryChanged_.bind(this));
this.onSplice_(null);
}
/**
* Obtains entry from ArrayDataModel's item.
* @param {*} item Item in ArrayDataModel.
* @return {!FileEntry}
*/
EntryListWatcher.prototype.getEntry = function(item) {
return /** @type {!FileEntry} */ (item);
};
/**
* @param {Event} event
* @private
*/
EntryListWatcher.prototype.onSplice_ = function(event) {
// TODO(mtomasz, hirono): Remove operations on URLs as they won't work after
// switching to isolated entries.
// Mark all existing watchers as candidates to be removed.
var diff = {};
for (var url in this.watchers_) {
diff[url] = -1;
}
// Obtains the set of new watcher.
this.watchers_ = {};
for (var i = 0; i < this.list_.length; i++) {
var entry = this.getEntry(this.list_.item(i));
var parentURL = entry.toURL().replace(/[^\/]+\/?$/, '');
this.watchers_[parentURL] = true;
}
// Mark new watchers to be added, and existing watchers to be kept.
for (var url in this.watchers_) {
diff[url] = (diff[url] || 0) + 1;
}
// Check the number in diff.
// -1: watcher exists in the old set, but does not exists in the new set.
// 0: watcher exists in both sets.
// 1: watcher does not exists in the old set, but exists in the new set.
var reportError = function() {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.name);
}
};
for (var url in diff) {
switch (diff[url]) {
case 1:
window.webkitResolveLocalFileSystemURL(url, function(entry) {
reportError();
chrome.fileManagerPrivate.addFileWatch(entry, reportError);
});
break;
case -1:
window.webkitResolveLocalFileSystemURL(url, function(entry) {
reportError();
chrome.fileManagerPrivate.removeFileWatch(entry, reportError);
});
break;
case 0:
break;
default:
assertNotReached();
break;
}
}
};
/**
* @param {!chrome.fileManagerPrivate.FileWatchEvent} event
* @private
*/
EntryListWatcher.prototype.onDirectoryChanged_ = function(event) {
// Add '/' to the tail for checking if the each entry's URL is child URL of
// the URL or not by using entryURL.indexOf(thisUrl) === 0.
var url = event.entry.toURL().replace(/\/?$/, '/');
var promiseList = [];
var removedEntryURL = {};
for (var i = 0; i < this.list_.length; i++) {
var entry = this.getEntry(this.list_.item(i));
// Remove trailing '/' to prevent calling getMetadata in the case where the
// event.entry and the entry are same.
var entryURL = entry.toURL().replace(/\/$/, '');
if (entry.toURL().indexOf(url) !== 0) {
continue;
}
// Look for non-existing files by using getMetadata.
// getMetadata returns NOT_FOUND error for non-existing files.
promiseList.push(new Promise(entry.getMetadata.bind(entry)).catch(
function(url) {
removedEntryURL[url] = true;
}.bind(null, entry.toURL())));
}
Promise.all(promiseList).then(function() {
var i = 0;
while (i < this.list_.length) {
var url = this.getEntry(this.list_.item(i)).toURL();
if (removedEntryURL[url]) {
this.list_.splice(i, 1);
} else {
i++;
}
}
}.bind(this));
};