Commit 5eef2621 authored by hirono's avatar hirono Committed by Commit bot

Files.app: Add NewMetadataProvider class.

The class will be used as the base class of all metadata provider.  The class has abstrct
getImpl method and sub-class of the class should inherit the function. The class
also contains MetadataProviderCache that is a sub-class of MetadataCacheSet, and
caches the result of getImpl method.

BUG=410766
TEST=FileManagerJsTest.NewMetadataProvider

Review URL: https://codereview.chromium.org/894283002

Cr-Commit-Position: refs/heads/master@{#314279}
parent c67ab01d
...@@ -134,3 +134,8 @@ IN_PROC_BROWSER_TEST_F(FileManagerJsTest, MetadataCacheSet) { ...@@ -134,3 +134,8 @@ IN_PROC_BROWSER_TEST_F(FileManagerJsTest, MetadataCacheSet) {
RunTest(base::FilePath(FILE_PATH_LITERAL( RunTest(base::FilePath(FILE_PATH_LITERAL(
"foreground/js/metadata/metadata_cache_set_unittest.html"))); "foreground/js/metadata/metadata_cache_set_unittest.html")));
} }
IN_PROC_BROWSER_TEST_F(FileManagerJsTest, NewMetadataProvider) {
RunTest(base::FilePath(FILE_PATH_LITERAL(
"foreground/js/metadata/new_metadata_provider_unittest.html")));
}
...@@ -88,6 +88,7 @@ ...@@ -88,6 +88,7 @@
'./metadata/metadata_cache.js', './metadata/metadata_cache.js',
'./metadata/metadata_cache_item.js', './metadata/metadata_cache_item.js',
'./metadata/metadata_cache_set.js', './metadata/metadata_cache_set.js',
'./metadata/new_metadata_provider.js',
'./metadata_update_controller.js', './metadata_update_controller.js',
'./naming_controller.js', './naming_controller.js',
'./navigation_list_model.js', './navigation_list_model.js',
......
// Copyright 2015 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.
/**
* TODO(hirono): Remove 'New' from the name after removing old MetadataProvider.
* @param {!MetadataProviderCache} cache
* @constructor
* @struct
* @template T
*/
function NewMetadataProvider(cache) {
/**
* @private {!MetadataProviderCache}
* @const
*/
this.cache_ = cache;
/**
* @private {!Array<!MetadataProviderCallbackRequest<T>>}
* @const
*/
this.callbackRequests_ = [];
}
/**
* Obtains the metadata for the request.
* Note: this must return all the properties requested by the argument.
* Otherwise Promise returned by NewMetadataProvider#get may not be fulfilled.
* @param {!Array<!MetadataRequest>} requests
* @return {!Promise<!Array<!T>>}
* @protected
*/
NewMetadataProvider.prototype.getImpl;
/**
* Obtains metadata for entries.
* @param {!Array<!FileEntry>} entries Entries.
* @param {!Array<string>} names Metadata property names to be obtained.
* @return {!Promise<!Array<!T>>}
*/
NewMetadataProvider.prototype.get = function(entries, names) {
// Check if the results are cached or not.
if (this.cache_.hasFreshCache(entries, names))
return Promise.resolve(this.getCache(entries, names));
// The LRU cache may be cached out when the callback is completed.
// To hold cached values, create snapshot of the cache for entries.
var requestId = this.cache_.generateRequestId();
var snapshot = this.cache_.createSnapshot(entries);
var requests = snapshot.createRequests(entries, names);
snapshot.startRequests(requestId, requests);
this.cache_.startRequests(requestId, requests);
// Register callback.
var promise = new Promise(function(fulfill, reject) {
this.callbackRequests_.push(new MetadataProviderCallbackRequest(
entries, names, snapshot, fulfill, reject));
}.bind(this));
// If the requests are not empty, call the requests.
if (requests.length) {
var requestedEntries = [];
for (var i = 0; i < requests.length; i++) {
requestedEntries.push(requests[i].entry);
}
this.getImpl(requests).then(function(list) {
// Store cache.
if (this.cache_.storeProperties(requestId, requestedEntries, list)) {
// TODO(hirono): Dispatch metadata change event here.
}
// Invoke callbacks.
var i = 0;
while (i < this.callbackRequests_.length) {
if (this.callbackRequests_[i].storeProperties(
requestId, requestedEntries, list)) {
// Callback was called.
this.callbackRequests_.splice(i, 1);
} else {
i++;
}
}
}.bind(this), function(error) {
// TODO(hirono): Handle rejection here and call rejection callback of
// MetadataProviderCallbackRequest.
console.error(error.stack);
});
}
return promise;
};
/**
* Obtains metadata cache for entries.
* @param {!Array<!FileEntry>} entries Entries.
* @param {!Array<string>} names Metadata property names to be obtained.
* @return {!Array<!T>}
*/
NewMetadataProvider.prototype.getCache = function(entries, names) {
return this.cache_.get(entries, names);
};
/**
* @param {!Array<!FileEntry>} entries
* @param {!Array<string>} names
* @param {!MetadataCacheSet} cache
* @param {function(!T):undefined} fulfill
* @param {function():undefined} reject
* @constructor
* @struct
* @template T
*/
function MetadataProviderCallbackRequest(
entries, names, cache, fulfill, reject) {
/**
* @private {!Array<!FileEntry>}
* @const
*/
this.entries_ = entries;
/**
* @private {!Array<string>}
* @const
*/
this.names_ = names;
/**
* @private {!MetadataCacheSet}
* @const
*/
this.cache_ = cache;
/**
* @private {function(!T):undefined}
* @const
*/
this.fulfill_ = fulfill;
/**
* @private {function():undefined}
* @const
*/
this.reject_ = reject;
}
/**
* Stores properties to snapshot cache of the callback request.
* If all the requested property are served, it invokes the callback.
* @param {number} requestId
* @param {!Array<!FileEntry>} entries
* @param {!Array<!Object>} objects
* @return {boolean} Whether the callback is invoked or not.
*/
MetadataProviderCallbackRequest.prototype.storeProperties = function(
requestId, entries, objects) {
this.cache_.storeProperties(requestId, entries, objects);
if (this.cache_.hasFreshCache(this.entries_, this.names_)) {
this.fulfill_(this.cache_.get(this.entries_, this.names_));
return true;
}
return false;
};
/**
* Helper wrapper for LRUCache.
* @constructor
* @extends {MetadataCacheSet}
* @struct
*/
function MetadataProviderCache() {
// TODO(hirono): Pass the correct maximum size of cache.
MetadataCacheSet.call(
this, new MetadataCacheSetStorageForLRUCache(new LRUCache(100)));
/**
* @private {number}
*/
this.requestIdCounter_ = 0;
}
MetadataProviderCache.prototype.__proto__ = MetadataCacheSet.prototype;
/**
* Generates a unique request ID every time when it is called.
* @return {number}
*/
MetadataProviderCache.prototype.generateRequestId = function() {
return this.requestIdCounter_++;
};
<!DOCTYPE html>
<!-- Copyright 2015 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.
-->
<!-- Base class -->
<script src="metadata_cache_set.js"></script>
<!-- Others -->
<script src="../../../common/js/lru_cache.js"></script>
<script src="../../../common/js/unittest_util.js"></script>
<script src="metadata_cache_item.js"></script>
<script src="new_metadata_provider.js"></script>
<script src="new_metadata_provider_unittest.js"></script>
// Copyright 2015 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.
function TestMetadataProvider(cache) {
NewMetadataProvider.call(this, cache);
this.requestCount = 0;
}
TestMetadataProvider.prototype.__proto__ = NewMetadataProvider.prototype;
TestMetadataProvider.prototype.getImpl = function(requests) {
this.requestCount++;
return Promise.resolve(requests.map(function(request) {
var entry = request.entry;
var names = request.names;
var result = {};
for (var i = 0; i < names.length; i++) {
result[names[i]] = entry.toURL() + ':' + names[i];
}
return result;
}));
};
var entryA = {
toURL: function() { return "filesystem://A"; }
};
var entryB = {
toURL: function() { return "filesystem://B"; }
};
function testNewMetadataProviderBasic(callback) {
var cache = new MetadataProviderCache();
var provider = new TestMetadataProvider(cache);
reportPromise(provider.get([entryA, entryB], ['property']).then(
function(results) {
assertEquals(1, provider.requestCount);
assertEquals('filesystem://A:property', results[0].property);
assertEquals('filesystem://B:property', results[1].property);
}), callback);
}
function testNewMetadataProviderRequestForCachedProperty(callback) {
var cache = new MetadataProviderCache();
var provider = new TestMetadataProvider(cache);
reportPromise(provider.get([entryA, entryB], ['property']).then(
function() {
// All the result should be cached here.
return provider.get([entryA, entryB], ['property']);
}).then(function(results) {
assertEquals(1, provider.requestCount);
assertEquals('filesystem://A:property', results[0].property);
assertEquals('filesystem://B:property', results[1].property);
}), callback);
}
function testNewMetadataProviderRequestForCachedAndNonCachedProperty(callback) {
var cache = new MetadataProviderCache();
var provider = new TestMetadataProvider(cache);
reportPromise(provider.get([entryA, entryB], ['propertyA']).then(
function() {
assertEquals(1, provider.requestCount);
// propertyB has not been cached here.
return provider.get([entryA, entryB], ['propertyA', 'propertyB']);
}).then(function(results) {
assertEquals(2, provider.requestCount);
assertEquals('filesystem://A:propertyA', results[0].propertyA);
assertEquals('filesystem://A:propertyB', results[0].propertyB);
assertEquals('filesystem://B:propertyA', results[1].propertyA);
assertEquals('filesystem://B:propertyB', results[1].propertyB);
}), callback);
}
function testNewMetadataProviderRequestForCachedAndNonCachedEntry(callback) {
var cache = new MetadataProviderCache();
var provider = new TestMetadataProvider(cache);
reportPromise(provider.get([entryA], ['property']).then(
function() {
assertEquals(1, provider.requestCount);
// entryB has not been cached here.
return provider.get([entryA, entryB], ['property']);
}).then(function(results) {
assertEquals(2, provider.requestCount);
assertEquals('filesystem://A:property', results[0].property);
assertEquals('filesystem://B:property', results[1].property);
}), callback);
}
function testNewMetadataProviderRequestBeforeCompletingPreviousRequest(
callback) {
var cache = new MetadataProviderCache();
var provider = new TestMetadataProvider(cache);
provider.get([entryA], ['property']);
assertEquals(1, provider.requestCount);
// The result of first call has not been fetched yet.
reportPromise(provider.get([entryA], ['property']).then(
function(results) {
assertEquals(1, provider.requestCount);
assertEquals('filesystem://A:property', results[0].property);
}), callback);
}
function testNewMetadataProviderGetCache(callback) {
var cache = new MetadataProviderCache();
var provider = new TestMetadataProvider(cache);
var promise = provider.get([entryA], ['property']);
var cache = provider.getCache([entryA], ['property']);
assertEquals(null, cache[0].property);
reportPromise(promise.then(function() {
var cache = provider.getCache([entryA], ['property']);
assertEquals(1, provider.requestCount);
assertEquals('filesystem://A:property', cache[0].property);
}), callback);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment