OLD | NEW |
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 * A base class for metadata providers. Implementing in-memory caching. | 6 * A base class for metadata provider. Implementing in-memory caching. |
7 */ | 7 */ |
8 function MetadataCache() { | 8 function MetadataCache() { |
9 this.cache_ = {}; | 9 this.cache_ = {}; |
10 } | 10 } |
11 | 11 |
12 MetadataCache.prototype.isSupported = function(url) { | |
13 return true; | |
14 }; | |
15 | |
16 MetadataCache.prototype.doFetch = function(url) { | 12 MetadataCache.prototype.doFetch = function(url) { |
17 throw new Error('MetadataCache.doFetch not implemented'); | 13 throw new Error('MetadataCache.doFetch not implemented'); |
18 }; | 14 }; |
19 | 15 |
20 MetadataCache.prototype.fetch = function(url, callback) { | 16 MetadataCache.prototype.fetch = function(url, callback) { |
21 var cacheValue = this.cache_[url]; | 17 var cacheValue = this.cache_[url]; |
22 | 18 |
23 if (!cacheValue) { | 19 if (!cacheValue) { |
24 // This is the first time anyone's asked, go get it. | 20 // This is the first time anyone has asked, go get it. |
25 if (this.isSupported(url)) { | 21 this.cache_[url] = [callback]; |
26 this.cache_[url] = [callback]; | 22 if (!this.doFetch(url)) { |
27 this.doFetch(url); | |
28 } else { | |
29 // Fetch failed, return an empty map, no caching needed. | 23 // Fetch failed, return an empty map, no caching needed. |
| 24 delete this.cache_[url]; |
30 setTimeout(callback, 0, {}); | 25 setTimeout(callback, 0, {}); |
31 } | 26 } |
32 return; | 27 return; |
33 } | 28 } |
34 | 29 |
35 if (cacheValue instanceof Array) { | 30 if (cacheValue instanceof Array) { |
36 // Something is already pending, add to the list of observers. | 31 // Something is already pending, add to the list of observers. |
37 cacheValue.push(callback); | 32 cacheValue.push(callback); |
38 return; | 33 return; |
39 } | 34 } |
40 | 35 |
41 if (cacheValue instanceof Object) { | 36 if (cacheValue instanceof Object) { |
42 // We already know the answer, let the caller know in a fresh call stack. | 37 // We already know the answer, let the caller know in a fresh call stack. |
43 setTimeout(function() { callback(cacheValue) }, 0); | 38 setTimeout(callback, 0, cacheValue); |
44 return; | 39 return; |
45 } | 40 } |
46 | 41 |
47 console.error('Unexpected metadata cache value:', cacheValue); | 42 console.error('Unexpected metadata cache value:', cacheValue); |
48 }; | 43 }; |
49 | 44 |
50 MetadataCache.prototype.processResult = function(url, result) { | 45 MetadataCache.prototype.processResult = function(url, result) { |
51 console.log('metadata result:', result); | 46 console.log('metadata result:', result); |
52 | 47 |
53 var observers = this.cache_[url]; | 48 var observers = this.cache_[url]; |
(...skipping 14 matching lines...) Expand all Loading... |
68 MetadataCache.prototype.reset = function(url) { | 63 MetadataCache.prototype.reset = function(url) { |
69 if (this.cache_[url] instanceof Array) { | 64 if (this.cache_[url] instanceof Array) { |
70 console.error( | 65 console.error( |
71 'Abandoned ' + this.cache_[url].length + ' metadata subscribers', | 66 'Abandoned ' + this.cache_[url].length + ' metadata subscribers', |
72 url); | 67 url); |
73 } | 68 } |
74 delete this.cache_[url]; | 69 delete this.cache_[url]; |
75 }; | 70 }; |
76 | 71 |
77 /** | 72 /** |
78 * Create metadata provider for local files. | 73 * Base class for metadata fetchers. |
| 74 * |
| 75 * @param {function(string, Object)} resultCallback |
| 76 * @param {string} urlPrefix. |
79 */ | 77 */ |
80 function MetadataProvider() { | 78 function MetadataFetcher(resultCallback, urlPrefix) { |
81 MetadataCache.call(this); | 79 this.resultCallback_ = resultCallback; |
| 80 this.urlPrefix_ = urlPrefix; |
| 81 } |
| 82 |
| 83 MetadataFetcher.prototype.processResult = function(url, metadata) { |
| 84 this.resultCallback_(url, metadata); |
| 85 }; |
| 86 |
| 87 MetadataFetcher.prototype.isInitialized = function() { |
| 88 return true; |
| 89 }; |
| 90 |
| 91 MetadataFetcher.prototype.supports = function(url) { |
| 92 return url.indexOf(this.urlPrefix_) == 0; |
| 93 }; |
| 94 |
| 95 MetadataFetcher.prototype.doFetch = function(url) { |
| 96 console.error('MetadataFetcher.doFetch not implemented'); |
| 97 }; |
| 98 |
| 99 /** |
| 100 * Create metadata cache for local files. |
| 101 */ |
| 102 function LocalMetadataFetcher(resultCallback, rootUrl) { |
| 103 MetadataFetcher.apply(this, arguments); |
82 | 104 |
83 // Pass all URLs to the metadata reader until we have a correct filter. | 105 // Pass all URLs to the metadata reader until we have a correct filter. |
84 this.urlFilter = /.*/; | 106 this.urlFilter = /.*/; |
85 | 107 |
86 var path = document.location.pathname; | 108 var path = document.location.pathname; |
87 var workerPath = document.location.origin + | 109 var workerPath = document.location.origin + |
88 path.substring(0, path.lastIndexOf('/') + 1) + | 110 path.substring(0, path.lastIndexOf('/') + 1) + |
89 'js/metadata/metadata_dispatcher.js'; | 111 'js/metadata/metadata_dispatcher.js'; |
90 | 112 |
91 this.dispatcher_ = new Worker(workerPath); | 113 this.dispatcher_ = new Worker(workerPath); |
92 this.dispatcher_.onmessage = this.onMessage_.bind(this); | 114 this.dispatcher_.onmessage = this.onMessage_.bind(this); |
93 this.dispatcher_.postMessage({verb: 'init'}); | 115 this.dispatcher_.postMessage({verb: 'init'}); |
94 // Initialization is not complete until the Worker sends back the | 116 // Initialization is not complete until the Worker sends back the |
95 // 'initialized' message. See below. | 117 // 'initialized' message. See below. |
| 118 this.initialized_ = false; |
96 } | 119 } |
97 | 120 |
98 MetadataProvider.prototype = { __proto__: MetadataCache.prototype }; | 121 LocalMetadataFetcher.prototype = { __proto__: MetadataFetcher.prototype }; |
99 | 122 |
100 MetadataProvider.prototype.isSupported = function(url) { | 123 LocalMetadataFetcher.prototype.supports = function(url) { |
101 return url.match(this.urlFilter); | 124 return MetadataFetcher.prototype.supports.apply(this, arguments) && |
| 125 url.match(this.urlFilter); |
102 }; | 126 }; |
103 | 127 |
104 MetadataProvider.prototype.doFetch = function(url) { | 128 LocalMetadataFetcher.prototype.doFetch = function(url) { |
105 this.dispatcher_.postMessage({verb: 'request', arguments: [url]}); | 129 this.dispatcher_.postMessage({verb: 'request', arguments: [url]}); |
106 }; | 130 }; |
107 | 131 |
| 132 LocalMetadataFetcher.prototype.isInitialized = function() { |
| 133 return this.initialized_; |
| 134 }; |
| 135 |
108 /** | 136 /** |
109 * Dispatch a message from a metadata reader to the appropriate on* method. | 137 * Dispatch a message from a metadata reader to the appropriate on* method. |
110 */ | 138 */ |
111 MetadataProvider.prototype.onMessage_ = function(event) { | 139 LocalMetadataFetcher.prototype.onMessage_ = function(event) { |
112 var data = event.data; | 140 var data = event.data; |
113 | 141 |
114 var methodName = | 142 var methodName = |
115 'on' + data.verb.substr(0, 1).toUpperCase() + data.verb.substr(1) + '_'; | 143 'on' + data.verb.substr(0, 1).toUpperCase() + data.verb.substr(1) + '_'; |
116 | 144 |
117 if (!(methodName in this)) { | 145 if (!(methodName in this)) { |
118 console.log('Unknown message from metadata reader: ' + data.verb, data); | 146 console.log('Unknown message from metadata reader: ' + data.verb, data); |
119 return; | 147 return; |
120 } | 148 } |
121 | 149 |
122 this[methodName].apply(this, data.arguments); | 150 this[methodName].apply(this, data.arguments); |
123 }; | 151 }; |
124 | 152 |
125 /** | 153 /** |
126 * Handles the 'initialized' message from the metadata reader Worker. | 154 * Handles the 'initialized' message from the metadata reader Worker. |
127 */ | 155 */ |
128 MetadataProvider.prototype.onInitialized_ = function(regexp) { | 156 LocalMetadataFetcher.prototype.onInitialized_ = function(regexp) { |
129 this.urlFilter = regexp; | 157 this.urlFilter = regexp; |
| 158 |
| 159 // Tests can monitor for this state with |
| 160 // ExtensionTestMessageListener listener("worker-initialized"); |
| 161 // ASSERT_TRUE(listener.WaitUntilSatisfied()); |
| 162 // Automated tests need to wait for this, otherwise we crash in |
| 163 // browser_test cleanup because the worker process still has |
| 164 // URL requests in-flight. |
| 165 chrome.test.sendMessage('worker-initialized'); |
| 166 this.initialized_ = true; |
130 }; | 167 }; |
131 | 168 |
132 MetadataProvider.prototype.onResult_ = function(url, metadata) { | 169 LocalMetadataFetcher.prototype.onResult_ = function(url, metadata) { |
133 this.processResult(url, metadata); | 170 this.processResult(url, metadata); |
134 }; | 171 }; |
135 | 172 |
136 MetadataProvider.prototype.onError_ = function(url, step, error, metadata) { | 173 LocalMetadataFetcher.prototype.onError_ = function(url, step, error, metadata) { |
137 console.warn('metadata: ' + url + ': ' + step + ': ' + error); | 174 console.warn('metadata: ' + url + ': ' + step + ': ' + error); |
138 this.processResult(url, metadata || {}); | 175 this.processResult(url, metadata || {}); |
139 }; | 176 }; |
140 | 177 |
141 MetadataProvider.prototype.onLog_ = function(arglist) { | 178 LocalMetadataFetcher.prototype.onLog_ = function(arglist) { |
142 console.log.apply(console, ['metadata:'].concat(arglist)); | 179 console.log.apply(console, ['metadata:'].concat(arglist)); |
143 }; | 180 }; |
144 | 181 |
145 /** | 182 /** |
146 * Create a metadata provider for GData files. | 183 * Create a metadata fetcher for GData files. |
147 * | 184 * |
148 * Instead of reading the file contents it gets the metadata from GData API. | 185 * Instead of reading the file contents it gets the metadata from GData API. |
149 */ | 186 */ |
150 function GDataMetadataProvider() { | 187 function GDataMetadataFetcher(resultCallback, rootUrl) { |
151 MetadataCache.call(this); | 188 MetadataFetcher.apply(this, arguments); |
152 } | 189 } |
153 | 190 |
154 GDataMetadataProvider.prototype = { __proto__: MetadataCache.prototype }; | 191 GDataMetadataFetcher.prototype = { __proto__: MetadataFetcher.prototype }; |
155 | 192 |
156 GDataMetadataProvider.prototype.doFetch = function(url) { | 193 GDataMetadataFetcher.prototype.doFetch = function(url) { |
157 function convertMetadata(result) { | 194 function convertMetadata(result) { |
158 return { | 195 return { |
159 thumbnailURL: result.thumbnailUrl, | 196 thumbnailURL: result.thumbnailUrl, |
160 thumbnailOnly: true, // Temporary, unless we learn to fetch the content. | 197 contentURL: (result.contentUrl || '').replace(/\?.*$/gi, ''), |
161 fileSize: 0 // TODO(kaznacheev) Get the correct size from File API. | 198 fileSize: 0 // TODO(kaznacheev) Get the correct size from File API. |
162 } | 199 } |
163 } | 200 } |
164 | 201 |
165 chrome.fileBrowserPrivate.getGDataFileProperties([url], | 202 chrome.fileBrowserPrivate.getGDataFileProperties([url], |
166 function(results) { | 203 function(results) { |
167 this.processResult(url, convertMetadata(results[0])); | 204 this.processResult(url, convertMetadata(results[0])); |
168 }.bind(this)); | 205 }.bind(this)); |
169 }; | 206 }; |
| 207 |
| 208 /** |
| 209 * Create universal metadata provider. |
| 210 * |
| 211 * @param {string} rootUrl The file system root url. |
| 212 * @constructor |
| 213 */ |
| 214 function MetadataProvider(rootUrl) { |
| 215 MetadataCache.call(this); |
| 216 |
| 217 var resultCallback = this.processResult.bind(this); |
| 218 |
| 219 // TODO(kaznacheev): We use 'gdata' literal here because |
| 220 // DirectoryModel.GDATA_DIRECTORY definition might be not available. |
| 221 // Consider extracting this definition into some common js file. |
| 222 this.fetchers_ = [ |
| 223 new GDataMetadataFetcher(resultCallback, rootUrl + 'gdata' + '/'), |
| 224 new LocalMetadataFetcher(resultCallback, rootUrl) |
| 225 ]; |
| 226 } |
| 227 |
| 228 MetadataProvider.prototype = { __proto__: MetadataCache.prototype }; |
| 229 |
| 230 MetadataProvider.prototype.isInitialized = function() { |
| 231 for (var i = 0; i != this.fetchers_.length; i++) { |
| 232 if (!this.fetchers_[i].isInitialized()) |
| 233 return false; |
| 234 } |
| 235 return true; |
| 236 }; |
| 237 |
| 238 MetadataProvider.prototype.doFetch = function(url) { |
| 239 for (var i = 0; i != this.fetchers_.length; i++) { |
| 240 var fetcher = this.fetchers_[i]; |
| 241 if (fetcher.supports(url)) { |
| 242 fetcher.doFetch(url); |
| 243 return true; |
| 244 } |
| 245 } |
| 246 return false; |
| 247 }; |
OLD | NEW |