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); |
| 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 |