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

Side by Side Diff: chrome/browser/resources/file_manager/js/metadata/metadata_cache.js

Issue 10384155: [filemanager] Content metadata moved to the cache. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Bug Created 8 years, 7 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * MetadataCache is a map from url to an object containing properties. 6 * MetadataCache is a map from url to an object containing properties.
7 * Properties are divided by types, and all properties of one type are accessed 7 * Properties are divided by types, and all properties of one type are accessed
8 * at once. 8 * at once.
9 * Some of the properties: 9 * Some of the properties:
10 * { 10 * {
11 * filesystem: size, modificationTime, icon 11 * filesystem: size, modificationTime
12 * internal: presence 12 * internal: presence
13 * gdata: pinned, present, hosted, editUrl, contentUrl, availableOffline 13 * gdata: pinned, present, hosted, editUrl, contentUrl, availableOffline
14 * streaming: url
15 *
16 * Following are not fetched for non-present gdata files.
17 * media: artist, album, title, width, height, imageTransform, etc.
14 * thumbnail: url, transform 18 * thumbnail: url, transform
15 * media: artist, album, title 19 *
20 * Following are always fetched from content, and so force the downloading
21 * of remote gdata files. One should use this for required content metadata,
22 * i.e. image orientation.
23 * fetchedMedia: width, height, etc.
16 * } 24 * }
17 * 25 *
18 * Typical usages: 26 * Typical usages:
19 * { 27 * {
20 * cache.get([entry1, entry2], 'gdata', function(gdata) { 28 * cache.get([entry1, entry2], 'gdata|filesystem', function(metadata) {
21 * if (gdata[0].pinned && gdata[1].pinned) alert("They are both pinned!"); 29 * if (metadata[0].gdata.pinned && metadata[1].filesystem.size == 0)
30 * alert("Pinned and empty!");
22 * }); 31 * });
23 * 32 *
24 * cache.set(entry, 'internal', {presence: 'deleted'}); 33 * cache.set(entry, 'internal', {presence: 'deleted'});
25 * 34 *
26 * cache.clear([fileUrl1, fileUrl2], 'filesystem'); 35 * cache.clear([fileUrl1, fileUrl2], 'filesystem');
27 * 36 *
28 * // Getting fresh value. 37 * // Getting fresh value.
29 * cache.clear(entry, 'thumbnail'); 38 * cache.clear(entry, 'thumbnail');
30 * cache.get(entry, 'thumbnail', function(thumbnail) { 39 * cache.get(entry, 'thumbnail', function(thumbnail) {
31 * img.src = thumbnail.url; 40 * img.src = thumbnail.url;
32 * }); 41 * });
42 *
43 * var cached = cache.getCached(entry, 'filesystem');
44 * var size = (cached && cached.size) || UNKNOWN_SIZE;
33 * } 45 * }
34 * 46 *
35 * TODO(dgozman): eviction.
36 * @constructor 47 * @constructor
37 */ 48 */
38 function MetadataCache() { 49 function MetadataCache() {
39 /** 50 /**
40 * Map from urls to entries. Entry contains |properties| - an hierarchical 51 * Map from urls to entries. Entry contains |properties| - an hierarchical
41 * object of values, and an object for each metadata provider: 52 * object of values, and an object for each metadata provider:
42 * <prodiver-id>: { time, callbacks } 53 * <prodiver-id>: { time, callbacks }
43 * @private 54 * @private
44 */ 55 */
45 this.cache_ = {}; 56 this.cache_ = {};
46 57
47 /** 58 /**
48 * List of metadata providers. 59 * List of metadata providers.
49 * @private 60 * @private
50 */ 61 */
51 this.providers_ = []; 62 this.providers_ = [];
52 63
53 /** 64 /**
54 * List of observers added. Each one is an object with fields: 65 * List of observers added. Each one is an object with fields:
55 * re - regexp of urls; 66 * re - regexp of urls;
56 * type - metadata type; 67 * type - metadata type;
57 * callback - the callback. 68 * callback - the callback.
58 * TODO(dgozman): pass entries to observer if present. 69 * TODO(dgozman): pass entries to observer if present.
59 * @private 70 * @private
60 */ 71 */
61 this.observers_ = []; 72 this.observers_ = [];
62 this.observerId_ = 0; 73 this.observerId_ = 0;
63 74
64 this.batchCount_ = 0; 75 this.batchCount_ = 0;
76 this.totalCount_ = 0;
77
78 /**
79 * Time of first get query of the current batch. Items updated later than this
80 * will not be evicted.
81 * @private
82 */
83 this.lastBatchStart_ = new Date();
65 } 84 }
66 85
67 /** 86 /**
68 * Observer type: it will be notified if the url changed is exactlt the same 87 * Observer type: it will be notified if the url changed is exactlt the same
69 * as the url passed. 88 * as the url passed.
70 */ 89 */
71 MetadataCache.EXACT = 0; 90 MetadataCache.EXACT = 0;
72 91
73 /** 92 /**
74 * Observer type: it will be notified if the url changed is an immediate child 93 * Observer type: it will be notified if the url changed is an immediate child
75 * of the url passed. 94 * of the url passed.
76 */ 95 */
77 MetadataCache.CHILDREN = 1; 96 MetadataCache.CHILDREN = 1;
78 97
79 /** 98 /**
80 * Observer type: it will be notified if the url changed is any descendant 99 * Observer type: it will be notified if the url changed is any descendant
81 * of the url passed. 100 * of the url passed.
82 */ 101 */
83 MetadataCache.DESCENDANTS = 2; 102 MetadataCache.DESCENDANTS = 2;
84 103
85 /** 104 /**
105 * Minimum number of items in cache to start eviction.
106 */
107 MetadataCache.EVICTION_NUMBER = 1000;
108
109 /**
86 * @return {MetadataCache!} The cache with all providers. 110 * @return {MetadataCache!} The cache with all providers.
87 */ 111 */
88 MetadataCache.createFull = function() { 112 MetadataCache.createFull = function() {
89 var cache = new MetadataCache(); 113 var cache = new MetadataCache();
90 cache.providers_.push(new FilesystemProvider()); 114 cache.providers_.push(new FilesystemProvider());
91 cache.providers_.push(new GDataProvider()); 115 cache.providers_.push(new GDataProvider());
116 cache.providers_.push(new ContentProvider());
92 return cache; 117 return cache;
93 }; 118 };
94 119
95 /** 120 /**
121 * @return {boolean} Whether all providers are ready.
122 */
123 MetadataCache.prototype.isInitialized = function() {
124 for (var index = 0; index < this.providers_.length; index++) {
125 if (!this.providers_[index].isInitialized()) return false;
126 }
127 return true;
128 };
129
130 /**
96 * Fetches the metadata, puts it in the cache, and passes to callback. 131 * Fetches the metadata, puts it in the cache, and passes to callback.
97 * If required metadata is already in the cache, does not fetch it again. 132 * If required metadata is already in the cache, does not fetch it again.
98 * @param {string|Entry|Array.<string|Entry>} items The list of entries or 133 * @param {string|Entry|Array.<string|Entry>} items The list of entries or
99 * file urls. May be just a single item. 134 * file urls. May be just a single item.
100 * @param {string} type The metadata type. 135 * @param {string} type The metadata type.
101 * @param {Function(Object)} callback The metadata is passed to callback. 136 * @param {Function(Object)} callback The metadata is passed to callback.
102 */ 137 */
103 MetadataCache.prototype.get = function(items, type, callback) { 138 MetadataCache.prototype.get = function(items, type, callback) {
104 if (!(items instanceof Array)) { 139 if (!(items instanceof Array)) {
105 this.getOne(items, type, callback); 140 this.getOne(items, type, callback);
(...skipping 24 matching lines...) Expand all
130 } 165 }
131 }; 166 };
132 167
133 /** 168 /**
134 * Fetches the metadata for one Entry/FileUrl. See comments to |get|. 169 * Fetches the metadata for one Entry/FileUrl. See comments to |get|.
135 * @param {Entry|string} item The entry or url. 170 * @param {Entry|string} item The entry or url.
136 * @param {string} type Metadata type. 171 * @param {string} type Metadata type.
137 * @param {Function(Object)} callback The callback. 172 * @param {Function(Object)} callback The callback.
138 */ 173 */
139 MetadataCache.prototype.getOne = function(item, type, callback) { 174 MetadataCache.prototype.getOne = function(item, type, callback) {
175 if (type.indexOf('|') != -1) {
176 var types = type.split('|');
177 var result = {};
178 var typesLeft = types.length;
179
180 function onOneType(requestedType, metadata) {
181 result[requestedType] = metadata;
182 typesLeft--;
183 if (typesLeft == 0) callback(result);
184 }
185
186 for (var index = 0; index < types.length; index++) {
187 this.getOne(item, types[index], onOneType.bind(null, types[index]));
188 }
189 return;
190 }
191
140 var url = this.itemToUrl_(item); 192 var url = this.itemToUrl_(item);
141 193
142 // Passing entry to fetchers may save one round-trip to APIs. 194 // Passing entry to fetchers may save one round-trip to APIs.
143 var fsEntry = item === url ? null : item; 195 var fsEntry = item === url ? null : item;
144 callback = callback || function() {}; 196 callback = callback || function() {};
145 197
146 if (!(url in this.cache_)) 198 if (!(url in this.cache_)) {
147 this.cache_[url] = this.createEmptyEntry_(); 199 this.cache_[url] = this.createEmptyEntry_();
200 this.totalCount_++;
201 }
148 202
149 var entry = this.cache_[url]; 203 var entry = this.cache_[url];
150 204
151 if (type in entry.properties) { 205 if (type in entry.properties) {
152 callback(entry.properties[type]); 206 callback(entry.properties[type]);
153 return; 207 return;
154 } 208 }
155 209
156 this.startBatchUpdates(); 210 this.startBatchUpdates();
157 var providers = this.providers_.slice(); 211 var providers = this.providers_.slice();
158 var currentProvider; 212 var currentProvider;
159 var self = this; 213 var self = this;
160 214
161 function onFetched() { 215 function onFetched() {
162 if (type in entry.properties) { 216 if (type in entry.properties) {
163 self.endBatchUpdates(); 217 self.endBatchUpdates();
164 // Got properties from provider. 218 // Got properties from provider.
165 callback(entry.properties[type]); 219 callback(entry.properties[type]);
166 } else { 220 } else {
167 tryNextProvider(); 221 tryNextProvider();
168 } 222 }
169 } 223 }
170 224
171 function onProviderProperties(properties) { 225 function onProviderProperties(properties) {
172 var id = currentProvider.getId(); 226 var id = currentProvider.getId();
173 var fetchedCallbacks = entry[id].callbacks; 227 var fetchedCallbacks = entry[id].callbacks;
174 delete entry[id].callbacks; 228 delete entry[id].callbacks;
175 entry[id].time = new Date(); 229 entry.time = new Date();
176 self.mergeProperties_(url, properties); 230 self.mergeProperties_(url, properties);
177 231
178 for (var index = 0; index < fetchedCallbacks.length; index++) { 232 for (var index = 0; index < fetchedCallbacks.length; index++) {
179 fetchedCallbacks[index](); 233 fetchedCallbacks[index]();
180 } 234 }
181 } 235 }
182 236
183 function queryProvider() { 237 function queryProvider() {
184 var id = currentProvider.getId(); 238 var id = currentProvider.getId();
185 if ('callbacks' in entry[id]) { 239 if ('callbacks' in entry[id]) {
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 * @param {string} type The metadata type. 295 * @param {string} type The metadata type.
242 * @param {Array.<Object>} values List of corresponding metadata values. 296 * @param {Array.<Object>} values List of corresponding metadata values.
243 */ 297 */
244 MetadataCache.prototype.set = function(items, type, values) { 298 MetadataCache.prototype.set = function(items, type, values) {
245 if (!(items instanceof Array)) 299 if (!(items instanceof Array))
246 items = [items]; 300 items = [items];
247 301
248 this.startBatchUpdates(); 302 this.startBatchUpdates();
249 for (var index = 0; index < items.length; index++) { 303 for (var index = 0; index < items.length; index++) {
250 var url = this.itemToUrl_(items[index]); 304 var url = this.itemToUrl_(items[index]);
251 if (!(url in this.cache_)) 305 if (!(url in this.cache_)) {
252 this.cache_[url] = this.createEmptyEntry_(); 306 this.cache_[url] = this.createEmptyEntry_();
307 this.totalCount_++;
308 }
253 this.cache_[url].properties[type] = values[index]; 309 this.cache_[url].properties[type] = values[index];
254 this.notifyObservers_(url, type); 310 this.notifyObservers_(url, type);
255 } 311 }
256 this.endBatchUpdates(); 312 this.endBatchUpdates();
257 }; 313 };
258 314
259 /** 315 /**
260 * Clears the cached metadata values. 316 * Clears the cached metadata values.
261 * @param {string|Entry|Array.<string|Entry>} items The list of entries or 317 * @param {string|Entry|Array.<string|Entry>} items The list of entries or
262 * file urls. May be just a single item. 318 * file urls. May be just a single item.
263 * @param {string} type The metadata type. 319 * @param {string} type The metadata type.
264 */ 320 */
265 MetadataCache.prototype.clear = function(items, type) { 321 MetadataCache.prototype.clear = function(items, type) {
266 if (!(items instanceof Array)) 322 if (!(items instanceof Array))
267 items = [items]; 323 items = [items];
268 324
325 var types = type.split('|');
326
269 for (var index = 0; index < items.length; index++) { 327 for (var index = 0; index < items.length; index++) {
270 var url = this.itemToUrl_(items[index]); 328 var url = this.itemToUrl_(items[index]);
271 if (url in this.cache_) 329 if (url in this.cache_) {
272 delete this.cache_[url].properties[type]; 330 for (var j = 0; j < types.length; j++) {
331 var type = types[j];
332 delete this.cache_[url].properties[type];
333 }
334 }
273 } 335 }
274 }; 336 };
275 337
276 /** 338 /**
277 * Adds an observer, which will be notified when metadata changes. 339 * Adds an observer, which will be notified when metadata changes.
278 * @param {string|Entry} item The root item to look at. 340 * @param {string|Entry} item The root item to look at.
279 * @param {number} relation This defines, which items will trigger the observer. 341 * @param {number} relation This defines, which items will trigger the observer.
280 * See comments to |MetadataCache.EXACT| and others. 342 * See comments to |MetadataCache.EXACT| and others.
281 * @param {string} type The metadata type. 343 * @param {string} type The metadata type.
282 * @param {Function(Array.<string>, Array.<Object>)} observer List of file urls 344 * @param {Function(Array.<string>, Array.<Object>)} observer List of file urls
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 } 377 }
316 } 378 }
317 return false; 379 return false;
318 }; 380 };
319 381
320 /** 382 /**
321 * Start batch updates. 383 * Start batch updates.
322 */ 384 */
323 MetadataCache.prototype.startBatchUpdates = function() { 385 MetadataCache.prototype.startBatchUpdates = function() {
324 this.batchCount_++; 386 this.batchCount_++;
387 if (this.batchCount_ == 1)
388 this.lastBatchStart_ = new Date();
325 }; 389 };
326 390
327 /** 391 /**
328 * End batch updates. Notifies observers if all nested updates are finished. 392 * End batch updates. Notifies observers if all nested updates are finished.
329 */ 393 */
330 MetadataCache.prototype.endBatchUpdates = function() { 394 MetadataCache.prototype.endBatchUpdates = function() {
331 this.batchCount_--; 395 this.batchCount_--;
332 if (this.batchCount_ != 0) return; 396 if (this.batchCount_ != 0) return;
397 if (this.totalCount_ > MetadataCache.EVICTION_NUMBER)
398 this.evict_();
333 for (var index = 0; index < this.observers_.length; index++) { 399 for (var index = 0; index < this.observers_.length; index++) {
334 var observer = this.observers_[index]; 400 var observer = this.observers_[index];
335 var urls = []; 401 var urls = [];
336 var properties = []; 402 var properties = [];
337 for (var url in observer.pending) { 403 for (var url in observer.pending) {
338 if (observer.pending.hasOwnProperty(url) && url in this.cache_) { 404 if (observer.pending.hasOwnProperty(url) && url in this.cache_) {
339 urls.push(url); 405 urls.push(url);
340 properties.push(this.cache_[url].properties[observer.type] || null); 406 properties.push(this.cache_[url].properties[observer.type] || null);
341 } 407 }
342 } 408 }
(...skipping 18 matching lines...) Expand all
361 // Observer expects array of urls and array of properties. 427 // Observer expects array of urls and array of properties.
362 observer.callback([url], [this.cache_[url].properties[type] || null]); 428 observer.callback([url], [this.cache_[url].properties[type] || null]);
363 } else { 429 } else {
364 observer.pending[url] = true; 430 observer.pending[url] = true;
365 } 431 }
366 } 432 }
367 } 433 }
368 }; 434 };
369 435
370 /** 436 /**
437 * Removes the oldest items from the cache.
438 * This method never removes the items from last batch.
439 * @private
440 */
441 MetadataCache.prototype.evict_ = function() {
442 var toRemove = [];
443
444 // We leave only a half of items, so we will not call evict_ soon again.
445 var desiredCount = Math.round(MetadataCache.EVICTION_NUMBER / 2);
446 var removeCount = this.totalCount_ - desiredCount;
447 for (var url in this.cache_) {
448 if (this.cache_.hasOwnProperty(url) &&
449 this.cache_[url].time < this.lastBatchStart_) {
450 toRemove.push(url);
451 }
452 }
453
454 toRemove.sort(function(a, b) {
455 var aTime = this.cache_[a].time;
456 var bTime = this.cache_[b].time;
457 return aTime < bTime ? -1 : aTime > bTime ? 1 : 0;
458 });
459
460 removeCount = Math.min(removeCount, toRemove.length);
461 this.totalCount_ -= removeCount;
462 for (var index = 0; index < removeCount; index++) {
463 delete this.cache_[toRemove[index]];
464 }
465 };
466
467 /**
371 * Converts Entry or file url to url. 468 * Converts Entry or file url to url.
372 * @param {string|Entry} item Item to convert. 469 * @param {string|Entry} item Item to convert.
373 * @return {string} File url. 470 * @return {string} File url.
374 * @private 471 * @private
375 */ 472 */
376 MetadataCache.prototype.itemToUrl_ = function(item) { 473 MetadataCache.prototype.itemToUrl_ = function(item) {
377 if (typeof(item) == 'string') 474 if (typeof(item) == 'string')
378 return item; 475 return item;
379 else 476 else
380 return item.toURL(); 477 return item.toURL();
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
430 * @return {boolean} Whether this provider provides this metadata. 527 * @return {boolean} Whether this provider provides this metadata.
431 */ 528 */
432 MetadataProvider2.prototype.providesType = function(type) { return false; }; 529 MetadataProvider2.prototype.providesType = function(type) { return false; };
433 530
434 /** 531 /**
435 * @return {string} Unique provider id. 532 * @return {string} Unique provider id.
436 */ 533 */
437 MetadataProvider2.prototype.getId = function() { return ''; }; 534 MetadataProvider2.prototype.getId = function() { return ''; };
438 535
439 /** 536 /**
537 * @return {boolean} Whether provider is ready.
538 */
539 MetadataProvider2.prototype.isInitialized = function() { return true; };
540
541 /**
440 * Fetches the metadata. It's suggested to return all the metadata this provider 542 * Fetches the metadata. It's suggested to return all the metadata this provider
441 * can fetch at once. 543 * can fetch at once.
442 * @param {string} url File url. 544 * @param {string} url File url.
443 * @param {string} type Requested metadata type. 545 * @param {string} type Requested metadata type.
444 * @param {Function(Object)} callback Callback expects a map from metadata type 546 * @param {Function(Object)} callback Callback expects a map from metadata type
445 * to metadata value. 547 * to metadata value.
446 * @param {Entry=} opt_entry The file entry if present. 548 * @param {Entry=} opt_entry The file entry if present.
447 */ 549 */
448 MetadataProvider2.prototype.fetch = function(url, type, callback, opt_entry) { 550 MetadataProvider2.prototype.fetch = function(url, type, callback, opt_entry) {
449 throw new Error('Default metadata provider cannot fetch.'); 551 throw new Error('Default metadata provider cannot fetch.');
450 }; 552 };
451 553
452 554
453 /** 555 /**
454 * Provider of filesystem metadata. 556 * Provider of filesystem metadata.
455 * This provider returns the following objects: 557 * This provider returns the following objects:
456 * filesystem: { 558 * filesystem: { size, modificationTime }
457 * size;
458 * modificationTime;
459 * icon - string describing icon type;
460 * }
461 * @constructor 559 * @constructor
462 */ 560 */
463 function FilesystemProvider() { 561 function FilesystemProvider() {
464 MetadataProvider2.call(this, 'filesystem'); 562 MetadataProvider2.call(this);
465 } 563 }
466 564
467 FilesystemProvider.prototype = { 565 FilesystemProvider.prototype = {
468 __proto__: MetadataProvider2.prototype 566 __proto__: MetadataProvider2.prototype
469 }; 567 };
470 568
471 /** 569 /**
472 * @param {string} url The url. 570 * @param {string} url The url.
473 * @return {boolean} Whether this provider supports the url. 571 * @return {boolean} Whether this provider supports the url.
474 */ 572 */
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
519 onEntry(opt_entry); 617 onEntry(opt_entry);
520 else 618 else
521 webkitResolveLocalFileSystemURL(url, onEntry, onError); 619 webkitResolveLocalFileSystemURL(url, onEntry, onError);
522 }; 620 };
523 621
524 /** 622 /**
525 * Provider of gdata metadata. 623 * Provider of gdata metadata.
526 * This provider returns the following objects: 624 * This provider returns the following objects:
527 * gdata: { pinned, hosted, present, dirty, editUrl, contentUrl } 625 * gdata: { pinned, hosted, present, dirty, editUrl, contentUrl }
528 * thumbnail: { url, transform } 626 * thumbnail: { url, transform }
627 * streaming: { url }
529 * @constructor 628 * @constructor
530 */ 629 */
531 function GDataProvider() { 630 function GDataProvider() {
532 MetadataProvider2.call(this, 'gdata'); 631 MetadataProvider2.call(this);
533 632
534 // We batch metadata fetches into single API call. 633 // We batch metadata fetches into single API call.
535 this.urls_ = []; 634 this.urls_ = [];
536 this.callbacks_ = []; 635 this.callbacks_ = [];
537 this.scheduled_ = false; 636 this.scheduled_ = false;
538 637
539 this.callApiBound_ = this.callApi_.bind(this); 638 this.callApiBound_ = this.callApi_.bind(this);
540 } 639 }
541 640
542 GDataProvider.prototype = { 641 GDataProvider.prototype = {
(...skipping 12 matching lines...) Expand all
555 */ 654 */
556 GDataProvider.prototype.supportsUrl = function(url) { 655 GDataProvider.prototype.supportsUrl = function(url) {
557 return GDataProvider.URL_PATTERN.test(url); 656 return GDataProvider.URL_PATTERN.test(url);
558 }; 657 };
559 658
560 /** 659 /**
561 * @param {string} type The metadata type. 660 * @param {string} type The metadata type.
562 * @return {boolean} Whether this provider provides this metadata. 661 * @return {boolean} Whether this provider provides this metadata.
563 */ 662 */
564 GDataProvider.prototype.providesType = function(type) { 663 GDataProvider.prototype.providesType = function(type) {
565 return type == 'gdata' || type == 'thumbnail'; 664 return type == 'gdata' || type == 'thumbnail' ||
665 type == 'streaming' || type == 'media';
566 }; 666 };
567 667
568 /** 668 /**
569 * @return {string} Unique provider id. 669 * @return {string} Unique provider id.
570 */ 670 */
571 GDataProvider.prototype.getId = function() { return 'gdata'; }; 671 GDataProvider.prototype.getId = function() { return 'gdata'; };
572 672
573 /** 673 /**
574 * Fetches the metadata. 674 * Fetches the metadata.
575 * @param {string} url File url. 675 * @param {string} url File url.
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
641 result.gdata = { 741 result.gdata = {
642 present: data.isPresent, 742 present: data.isPresent,
643 pinned: data.isPinned, 743 pinned: data.isPinned,
644 hosted: data.isHosted, 744 hosted: data.isHosted,
645 dirty: data.isDirty, 745 dirty: data.isDirty,
646 availableOffline: GDataProvider.isAvailableOffline(data), 746 availableOffline: GDataProvider.isAvailableOffline(data),
647 availableWhenMetered: GDataProvider.isAvailableWhenMetered(data), 747 availableWhenMetered: GDataProvider.isAvailableWhenMetered(data),
648 contentUrl: (data.contentUrl || '').replace(/\?.*$/gi, ''), 748 contentUrl: (data.contentUrl || '').replace(/\?.*$/gi, ''),
649 editUrl: data.editUrl || '' 749 editUrl: data.editUrl || ''
650 }; 750 };
751
752 if (!data.isPresent) {
753 // Block the local fetch for gdata files, which require downloading.
754 result.thumbnail = { url: '', transform: null };
755 result.media = {};
756 }
757
651 if ('thumbnailUrl' in data) { 758 if ('thumbnailUrl' in data) {
652 result.thumbnail = { 759 result.thumbnail = {
653 url: data.thumbnailUrl, 760 url: data.thumbnailUrl,
654 transform: '' 761 transform: null
655 }; 762 };
656 } 763 }
764 if (data.isPresent && ('contentUrl' in data)) {
765 result.streaming = {
766 url: data.contentUrl.replace(/\?.*$/gi, '')
767 };
768 }
657 return result; 769 return result;
658 }; 770 };
771
772
773 /**
774 * Provider of content metadata.
775 * This provider returns the following objects:
776 * thumbnail: { url, transform }
777 * media: { artist, album, title, width, height, imageTransform, etc. }
778 * fetchedMedia: { same fields here }
779 * @constructor
780 */
781 function ContentProvider() {
782 MetadataProvider2.call(this);
783
784 // Pass all URLs to the metadata reader until we have a correct filter.
785 this.urlFilter_ = /.*/;
786
787 var path = document.location.pathname;
788 var workerPath = document.location.origin +
789 path.substring(0, path.lastIndexOf('/') + 1) +
790 'js/metadata/metadata_dispatcher.js';
791
792 this.dispatcher_ = new Worker(workerPath);
793 this.dispatcher_.onmessage = this.onMessage_.bind(this);
794 this.dispatcher_.postMessage({verb: 'init'});
795
796 // Initialization is not complete until the Worker sends back the
797 // 'initialized' message. See below.
798 this.initialized_ = false;
799
800 // Map from url to callback.
801 // Note that simultaneous requests for same url are handled in MetadataCache.
802 this.callbacks_ = {};
803 }
804
805 ContentProvider.prototype = {
806 __proto__: MetadataProvider2.prototype
807 };
808
809 /**
810 * @param {string} url The url.
811 * @return {boolean} Whether this provider supports the url.
812 */
813 ContentProvider.prototype.supportsUrl = function(url) {
814 return url.match(this.urlFilter_);
815 };
816
817 /**
818 * @param {string} type The metadata type.
819 * @return {boolean} Whether this provider provides this metadata.
820 */
821 ContentProvider.prototype.providesType = function(type) {
822 return type == 'thumbnail' || type == 'fetchedMedia' || type == 'media';
823 };
824
825 /**
826 * @return {string} Unique provider id.
827 */
828 ContentProvider.prototype.getId = function() { return 'content'; };
829
830 /**
831 * Fetches the metadata.
832 * @param {string} url File url.
833 * @param {string} type Requested metadata type.
834 * @param {Function(Object)} callback Callback expects a map from metadata type
835 * to metadata value.
836 * @param {Entry=} opt_entry The file entry if present.
837 */
838 ContentProvider.prototype.fetch = function(url, type, callback, opt_entry) {
839 if (opt_entry && opt_entry.isDirectory) {
840 callback({});
841 return;
842 }
843 this.callbacks_[url] = callback;
844 this.dispatcher_.postMessage({verb: 'request', arguments: [url]});
845 };
846
847 /**
848 * Dispatch a message from a metadata reader to the appropriate on* method.
849 * @param {Object} event The event.
850 * @private
851 */
852 ContentProvider.prototype.onMessage_ = function(event) {
853 var data = event.data;
854
855 var methodName =
856 'on' + data.verb.substr(0, 1).toUpperCase() + data.verb.substr(1) + '_';
857
858 if (!(methodName in this)) {
859 console.log('Unknown message from metadata reader: ' + data.verb, data);
860 return;
861 }
862
863 this[methodName].apply(this, data.arguments);
864 };
865
866 /**
867 * @return {boolean} Whether provider is ready.
868 */
869 ContentProvider.prototype.isInitialized = function() {
870 return this.initialized_;
871 };
872
873 /**
874 * Handles the 'initialized' message from the metadata reader Worker.
875 * @param {Object} regexp Regexp of supported urls.
876 * @private
877 */
878 ContentProvider.prototype.onInitialized_ = function(regexp) {
879 this.urlFilter_ = regexp;
880
881 // Tests can monitor for this state with
882 // ExtensionTestMessageListener listener("worker-initialized");
883 // ASSERT_TRUE(listener.WaitUntilSatisfied());
884 // Automated tests need to wait for this, otherwise we crash in
885 // browser_test cleanup because the worker process still has
886 // URL requests in-flight.
887 chrome.test.sendMessage('worker-initialized');
888 this.initialized_ = true;
889 };
890
891 /**
892 * Handles the 'result' message from the worker.
893 * @param {string} url File url.
894 * @param {Object} metadata The metadata.
895 * @private
896 */
897 ContentProvider.prototype.onResult_ = function(url, metadata) {
898 var callback = this.callbacks_[url];
899 delete this.callbacks_[url];
900
901 var result = {};
902
903 if ('thumbnailURL' in metadata) {
904 metadata.thumbnailTransform = metadata.thumbnailTransform || null;
905 result.thumbnail = {
906 url: metadata.thumbnailURL,
907 transform: metadata.thumbnailTransform
908 };
909 delete metadata.thumbnailURL;
910 delete metadata.thumbnailTransform;
911 }
912
913 for (var key in metadata) {
914 if (metadata.hasOwnProperty(key)) {
915 if (!('media' in result)) result.media = {};
916 result.media[key] = metadata[key];
917 }
918 }
919
920 if ('media' in result) {
921 result.fetchedMedia = result.media;
922 }
923
924 callback(result);
925 };
926
927 /**
928 * Handles the 'error' message from the worker.
929 * @param {string} url File url.
930 * @param {string} step Step failed.
931 * @param {string} error Error description.
932 * @param {Object?} metadata The metadata, if available.
933 * @private
934 */
935 ContentProvider.prototype.onError_ = function(url, step, error, metadata) {
936 console.warn('metadata: ' + url + ': ' + step + ': ' + error);
937 metadata = metadata || {};
938 // Prevent asking for thumbnail again.
939 metadata.thumbnailURL = '';
940 this.onResult_(url, metadata);
941 };
942
943 /**
944 * Handles the 'log' message from the worker.
945 * @param {Array.<*>} arglist Log arguments.
946 * @private
947 */
948 ContentProvider.prototype.onLog_ = function(arglist) {
949 console.log.apply(console, ['metadata:'].concat(arglist));
950 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698