OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * Creates and starts downloading and then resizing of the image. Finally, | 8 * Creates and starts downloading and then resizing of the image. Finally, |
9 * returns the image using the callback. | 9 * returns the image using the callback. |
10 * | 10 * |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
48 | 48 |
49 /** | 49 /** |
50 * MIME type of the fetched image. | 50 * MIME type of the fetched image. |
51 * @type {string} | 51 * @type {string} |
52 * @private | 52 * @private |
53 */ | 53 */ |
54 this.contentType_ = null; | 54 this.contentType_ = null; |
55 | 55 |
56 /** | 56 /** |
57 * Used to download remote images using http:// or https:// protocols. | 57 * Used to download remote images using http:// or https:// protocols. |
58 * @type {XMLHttpRequest} | 58 * @type {AuthorizedXHR} |
59 * @private | 59 * @private |
60 */ | 60 */ |
61 this.xhr_ = new XMLHttpRequest(); | 61 this.xhr_ = new AuthorizedXHR(); |
62 | 62 |
63 /** | 63 /** |
64 * Temporary canvas used to resize and compress the image. | 64 * Temporary canvas used to resize and compress the image. |
65 * @type {HTMLCanvasElement} | 65 * @type {HTMLCanvasElement} |
66 * @private | 66 * @private |
67 */ | 67 */ |
68 this.canvas_ = document.createElement('canvas'); | 68 this.canvas_ = document.createElement('canvas'); |
69 | 69 |
70 /** | 70 /** |
71 * @type {CanvasRenderingContext2D} | 71 * @type {CanvasRenderingContext2D} |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
187 this.image_.onerror = onFailure; | 187 this.image_.onerror = onFailure; |
188 | 188 |
189 // Download data urls directly since they are not supported by XmlHttpRequest. | 189 // Download data urls directly since they are not supported by XmlHttpRequest. |
190 var dataUrlMatches = this.request_.url.match(/^data:([^,;]*)[,;]/); | 190 var dataUrlMatches = this.request_.url.match(/^data:([^,;]*)[,;]/); |
191 if (dataUrlMatches) { | 191 if (dataUrlMatches) { |
192 this.image_.src = this.request_.url; | 192 this.image_.src = this.request_.url; |
193 this.contentType_ = dataUrlMatches[1]; | 193 this.contentType_ = dataUrlMatches[1]; |
194 return; | 194 return; |
195 } | 195 } |
196 | 196 |
197 // Download using an xhr request. | 197 // Fetch the image via authorized XHR and parse it. |
198 this.xhr_.responseType = 'blob'; | 198 var parseImage = function(contentType, blob) { |
199 | |
200 this.xhr_.onerror = this.image_.onerror; | |
201 this.xhr_.onload = function() { | |
202 if (this.xhr_.status != 200) { | |
203 this.image_.onerror(); | |
204 return; | |
205 } | |
206 | |
207 // Process returned data, including the mime type. | |
208 this.contentType_ = this.xhr_.getResponseHeader('Content-Type'); | |
209 var reader = new FileReader(); | 199 var reader = new FileReader(); |
210 reader.onerror = this.image_.onerror; | 200 reader.onerror = onFailure; |
211 reader.onload = function(e) { | 201 reader.onload = function(e) { |
212 this.image_.src = e.target.result; | 202 this.image_.src = e.target.result; |
213 }.bind(this); | 203 }.bind(this); |
214 | 204 |
215 // Load the data to the image as a data url. | 205 // Load the data to the image as a data url. |
216 reader.readAsDataURL(this.xhr_.response); | 206 reader.readAsDataURL(blob); |
207 }.bind(this); | |
208 | |
209 // Request raw data via XHR. | |
210 this.xhr_.load(this.request_.url, parseImage, onFailure); | |
211 }; | |
212 | |
213 /** | |
214 * Creates a XmlHttpRequest wrapper with injected OAuth2 authentication headers. | |
215 * @constructor | |
216 */ | |
217 function AuthorizedXHR() { | |
218 this.xhr_ = null; | |
219 this.aborted_ = false; | |
220 } | |
221 | |
222 /** | |
223 * Aborts the current request (if running). | |
224 */ | |
225 AuthorizedXHR.prototype.abort = function() { | |
226 this.aborted_ = true; | |
227 if (this.xhr_) | |
228 this.xhr_.abort(); | |
229 }; | |
230 | |
231 /** | |
232 * Loads an image using a OAuth2 token. If it fails, then tries to retry with | |
233 * a refreshed OAuth2 token. | |
234 * | |
235 * @param {string} url URL to the resource to be fetched. | |
236 * @param {function(string, Blob}) onSuccess Success callback with the content | |
237 * type and the fetched data. | |
238 * @param {function()} onFailure Failure callback. | |
239 */ | |
240 AuthorizedXHR.prototype.load = function(url, onSuccess, onFailure) { | |
241 this.aborted_ = false; | |
242 | |
243 // Do not call any callbacks when aborting. | |
244 var onMaybeSuccess = function(contentType, response) { | |
245 if (!this.aborted_) | |
246 onSuccess(contentType, response); | |
247 }.bind(this); | |
248 var onMaybeFailure = function(opt_code) { | |
249 if (!this.aborted_) | |
250 onFailure(); | |
251 }.bind(this); | |
252 | |
253 // Fetches the access token and makes an authorized call. If refresh is true, | |
254 // then forces refreshing the access token. | |
255 var requestTokenAndCall = function(refresh, onInnerSuccess, onInnerFailure) { | |
256 chrome.fileBrowserPrivate.requestAccessToken(refresh, function(token) { | |
257 if (this.aborted_) | |
258 return; | |
259 if (!token) { | |
260 onInnerFailure(); | |
261 return; | |
262 } | |
263 this.xhr_ = AuthorizedXHR.loadWithToken_( | |
264 token, url, onInnerSuccess, onInnerFailure); | |
yoshiki
2013/10/01 08:51:18
nit: indent
| |
265 }.bind(this)); | |
266 }.bind(this); | |
267 | |
268 // Refreshes the access token and retries the request. | |
269 var maybeRetryCall = function(code) { | |
270 if (this.aborted_) | |
271 return; | |
272 requestTokenAndCall(true, onMaybeSuccess, onMaybeFailure); | |
273 }.bind(this); | |
274 | |
275 // Make the request with reusing the current token. If it fails, then retry. | |
276 requestTokenAndCall(false, onMaybeSuccess, maybeRetryCall); | |
277 }; | |
278 | |
279 /** | |
280 * Fetches data using authorized XmlHttpRequest with the provided OAuth2 token. | |
281 * If the token is invalid, the request will fail. | |
282 * | |
283 * @param {string} token OAuth2 token to be injected to the request. | |
284 * @param {string} url URL to the resource to be fetched. | |
285 * @param {function(string, Blob}) onSuccess Success callback with the content | |
286 * type and the fetched data. | |
287 * @param {function(number=)} onFailure Failure callback with the error code | |
288 * if available. | |
289 * @private | |
290 */ | |
291 AuthorizedXHR.loadWithToken_ = function(token, url, onSuccess, onFailure) { | |
292 var xhr = new XMLHttpRequest(); | |
293 xhr.responseType = 'blob'; | |
294 | |
295 xhr.onreadystatechange = function() { | |
296 if (xhr.readyState != 4) | |
297 return; | |
298 if (xhr.status != 200) { | |
299 onFailure(xhr.status); | |
300 return; | |
301 } | |
302 var contentType = xhr.getResponseHeader('Content-Type'); | |
303 onSuccess(contentType, xhr.response); | |
217 }.bind(this); | 304 }.bind(this); |
218 | 305 |
219 // Perform a xhr request. | 306 // Perform a xhr request. |
220 try { | 307 try { |
221 this.xhr_.open('GET', this.request_.url, true); | 308 xhr.open('GET', url, true); |
222 this.xhr_.send(); | 309 xhr.setRequestHeader('Authorization', 'Bearer ' + token); |
310 xhr.send(); | |
223 } catch (e) { | 311 } catch (e) { |
224 this.image_.onerror(); | 312 onFailure(); |
225 } | 313 } |
226 }; | 314 }; |
227 | 315 |
228 /** | 316 /** |
229 * Sends the resized image via the callback. If the image has been changed, | 317 * Sends the resized image via the callback. If the image has been changed, |
230 * then packs the canvas contents, otherwise sends the raw image data. | 318 * then packs the canvas contents, otherwise sends the raw image data. |
231 * | 319 * |
232 * @param {boolean} imageChanged Whether the image has been changed. | 320 * @param {boolean} imageChanged Whether the image has been changed. |
233 * @private | 321 * @private |
234 */ | 322 */ |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
323 * @private | 411 * @private |
324 */ | 412 */ |
325 Request.prototype.cleanup_ = function() { | 413 Request.prototype.cleanup_ = function() { |
326 this.image_.onerror = function() {}; | 414 this.image_.onerror = function() {}; |
327 this.image_.onload = function() {}; | 415 this.image_.onload = function() {}; |
328 | 416 |
329 // Transparent 1x1 pixel gif, to force garbage collecting. | 417 // Transparent 1x1 pixel gif, to force garbage collecting. |
330 this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAA' + | 418 this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAA' + |
331 'ABAAEAAAICTAEAOw=='; | 419 'ABAAEAAAICTAEAOw=='; |
332 | 420 |
333 this.xhr_.onerror = function() {}; | |
334 this.xhr_.onload = function() {}; | 421 this.xhr_.onload = function() {}; |
335 this.xhr_.abort(); | 422 this.xhr_.abort(); |
336 | 423 |
337 // Dispose memory allocated by Canvas. | 424 // Dispose memory allocated by Canvas. |
338 this.canvas_.width = 0; | 425 this.canvas_.width = 0; |
339 this.canvas_.height = 0; | 426 this.canvas_.height = 0; |
340 }; | 427 }; |
OLD | NEW |