1eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Copyright 2013 The Chromium Authors. All rights reserved. 2eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// found in the LICENSE file. 4eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 5eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch'use strict'; 6eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Creates and starts downloading and then resizing of the image. Finally, 9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * returns the image using the callback. 10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {string} id Request ID. 12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {Cache} cache Cache object. 13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {Object} request Request message as a hash array. 14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function} callback Callback used to send the response. 15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @constructor 16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochfunction Request(id, cache, request, callback) { 18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {string} 20eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.id_ = id; 23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 24eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {Cache} 26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.cache_ = cache; 29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {Object} 32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.request_ = request; 35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {function} 38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.sendResponse_ = callback; 41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Temporary image used to download images. 44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {Image} 45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_ = new Image(); 48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * MIME type of the fetched image. 51eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {string} 52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.contentType_ = null; 55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 56eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Used to download remote images using http:// or https:// protocols. 584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @type {AuthorizedXHR} 59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.xhr_ = new AuthorizedXHR(); 62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Temporary canvas used to resize and compress the image. 65eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {HTMLCanvasElement} 66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 67eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 68eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.canvas_ = document.createElement('canvas'); 69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {CanvasRenderingContext2D} 72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.context_ = this.canvas_.getContext('2d'); 75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 77eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Callback to be called once downloading is finished. 78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @type {function()} 79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.downloadCallback_ = null; 82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Returns ID of the request. 86eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @return {string} Request ID. 87eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.getId = function() { 89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return this.id_; 90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Returns priority of the request. The higher priority, the faster it will 94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * be handled. The highest priority is 0. The default one is 2. 95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 96eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @return {number} Priority. 97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.getPriority = function() { 99eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return (this.request_.priority !== undefined) ? this.request_.priority : 2; 100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Tries to load the image from cache if exists and sends the response. 104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 105eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} onSuccess Success callback. 106eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} onFailure Failure callback. 107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.loadFromCacheAndProcess = function(onSuccess, onFailure) { 109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.loadFromCache_( 110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch function(data) { // Found in cache. 111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.sendImageData_(data); 112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch onSuccess(); 113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch }.bind(this), 114eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch onFailure); // Not found in cache. 115eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 117eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Tries to download the image, resizes and sends the response. 119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} callback Completion callback. 120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.downloadAndProcess = function(callback) { 122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (this.downloadCallback_) 123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch throw new Error('Downloading already started.'); 124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.downloadCallback_ = callback; 126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.downloadOriginal_(this.onImageLoad_.bind(this), 127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.onImageError_.bind(this)); 128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Fetches the image from the persistent cache. 132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} onSuccess Success callback. 134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} onFailure Failure callback. 135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.loadFromCache_ = function(onSuccess, onFailure) { 138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch var cacheKey = Cache.createKey(this.request_); 139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!this.request_.cache) { 141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Cache is disabled for this request; therefore, remove it from cache 142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // if existed. 143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.cache_.removeImage(cacheKey); 144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch onFailure(); 145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return; 146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!this.request_.timestamp) { 149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Persistent cache is available only when a timestamp is provided. 150eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch onFailure(); 151eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return; 152eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 153eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.cache_.loadImage(cacheKey, 155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.request_.timestamp, 156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch onSuccess, 157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch onFailure); 158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Saves the image to the persistent cache. 162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {string} data The image's data. 164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.saveToCache_ = function(data) { 167eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!this.request_.cache || !this.request_.timestamp) { 168eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Persistent cache is available only when a timestamp is provided. 169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return; 170eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch var cacheKey = Cache.createKey(this.request_); 173eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.cache_.saveImage(cacheKey, 174eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch data, 175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.request_.timestamp); 176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 177eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 178eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 179eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Downloads an image directly or for remote resources using the XmlHttpRequest. 180eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 181eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} onSuccess Success callback. 182eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} onFailure Failure callback. 183eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 184eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 185eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.downloadOriginal_ = function(onSuccess, onFailure) { 186eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_.onload = onSuccess; 187eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_.onerror = onFailure; 188eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 189eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Download data urls directly since they are not supported by XmlHttpRequest. 190eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch var dataUrlMatches = this.request_.url.match(/^data:([^,;]*)[,;]/); 191eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (dataUrlMatches) { 192eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_.src = this.request_.url; 193eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.contentType_ = dataUrlMatches[1]; 194eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return; 195eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 1974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Fetch the image via authorized XHR and parse it. 1984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var parseImage = function(contentType, blob) { 199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch var reader = new FileReader(); 2004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) reader.onerror = onFailure; 201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch reader.onload = function(e) { 202eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_.src = e.target.result; 203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch }.bind(this); 204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Load the data to the image as a data url. 2064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) reader.readAsDataURL(blob); 2074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) }.bind(this); 2084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Request raw data via XHR. 2104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.xhr_.load(this.request_.url, parseImage, onFailure); 2114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}; 2124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/** 2144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Creates a XmlHttpRequest wrapper with injected OAuth2 authentication headers. 2154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @constructor 2164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */ 2174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function AuthorizedXHR() { 2184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.xhr_ = null; 2194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.aborted_ = false; 2204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 2214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/** 2234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Aborts the current request (if running). 2244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */ 2254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)AuthorizedXHR.prototype.abort = function() { 2264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.aborted_ = true; 2274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (this.xhr_) 2284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.xhr_.abort(); 2294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}; 2304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/** 2324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Loads an image using a OAuth2 token. If it fails, then tries to retry with 2334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * a refreshed OAuth2 token. 2344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * 2354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {string} url URL to the resource to be fetched. 2364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {function(string, Blob}) onSuccess Success callback with the content 2374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * type and the fetched data. 2384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {function()} onFailure Failure callback. 2394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */ 2404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)AuthorizedXHR.prototype.load = function(url, onSuccess, onFailure) { 2414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) this.aborted_ = false; 2424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Do not call any callbacks when aborting. 2444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var onMaybeSuccess = function(contentType, response) { 2454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (!this.aborted_) 2464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) onSuccess(contentType, response); 2474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) }.bind(this); 2484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var onMaybeFailure = function(opt_code) { 2494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (!this.aborted_) 2504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) onFailure(); 2514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) }.bind(this); 2524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Fetches the access token and makes an authorized call. If refresh is true, 2544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // then forces refreshing the access token. 2554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var requestTokenAndCall = function(refresh, onInnerSuccess, onInnerFailure) { 2561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci chrome.fileManagerPrivate.requestAccessToken(refresh, function(token) { 2574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (this.aborted_) 2584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return; 2594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (!token) { 2604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) onInnerFailure(); 2614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return; 2624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 263f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) this.xhr_ = AuthorizedXHR.load_( 2644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) token, url, onInnerSuccess, onInnerFailure); 2654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) }.bind(this)); 2664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) }.bind(this); 2674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Refreshes the access token and retries the request. 2694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var maybeRetryCall = function(code) { 2704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (this.aborted_) 2714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return; 2724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) requestTokenAndCall(true, onMaybeSuccess, onMaybeFailure); 2734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) }.bind(this); 2744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 275f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Do not request a token for local resources, since it is not necessary. 27646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if (/^filesystem:/.test(url)) { 27746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // The query parameter is workaround for 27846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // crbug.com/379678, which force to obtain the latest contents of the image. 27946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) var noCacheUrl = url + '?nocache=' + Date.now(); 28046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) this.xhr_ = AuthorizedXHR.load_( 28146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) null, 28246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) noCacheUrl, 28346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) onMaybeSuccess, 28446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) onMaybeFailure); 285f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return; 286f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 287f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 2884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Make the request with reusing the current token. If it fails, then retry. 2894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) requestTokenAndCall(false, onMaybeSuccess, maybeRetryCall); 2904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}; 2914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 2924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/** 2934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Fetches data using authorized XmlHttpRequest with the provided OAuth2 token. 2944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * If the token is invalid, the request will fail. 2954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * 296f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * @param {?string} token OAuth2 token to be injected to the request. Null for 297f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * no token. 2984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {string} url URL to the resource to be fetched. 2994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {function(string, Blob}) onSuccess Success callback with the content 3004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * type and the fetched data. 3014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {function(number=)} onFailure Failure callback with the error code 3024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * if available. 303f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * @return {AuthorizedXHR} XHR instance. 3044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @private 3054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */ 306f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)AuthorizedXHR.load_ = function(token, url, onSuccess, onFailure) { 3074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var xhr = new XMLHttpRequest(); 3084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) xhr.responseType = 'blob'; 3094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 3104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) xhr.onreadystatechange = function() { 3114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (xhr.readyState != 4) 3124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return; 3134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (xhr.status != 200) { 3144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) onFailure(xhr.status); 3154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return; 3164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 3174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) var contentType = xhr.getResponseHeader('Content-Type'); 3184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) onSuccess(contentType, xhr.response); 319eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch }.bind(this); 320eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 321eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Perform a xhr request. 322eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch try { 3234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) xhr.open('GET', url, true); 324f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (token) 325f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) xhr.setRequestHeader('Authorization', 'Bearer ' + token); 3264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) xhr.send(); 327eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } catch (e) { 3284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) onFailure(); 329eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 330f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 331f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return xhr; 332eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 333eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 334eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 335eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Sends the resized image via the callback. If the image has been changed, 336eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * then packs the canvas contents, otherwise sends the raw image data. 337eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 338eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {boolean} imageChanged Whether the image has been changed. 339eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 340eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 341eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.sendImage_ = function(imageChanged) { 342eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch var imageData; 343eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!imageChanged) { 344eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // The image hasn't been processed, so the raw data can be directly 345eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // forwarded for speed (no need to encode the image again). 346eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch imageData = this.image_.src; 347eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } else { 348eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // The image has been resized or rotated, therefore the canvas has to be 349eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // encoded to get the correct compressed image data. 350eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch switch (this.contentType_) { 351eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch case 'image/gif': 352eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch case 'image/png': 353eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch case 'image/svg': 354eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch case 'image/bmp': 355eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch imageData = this.canvas_.toDataURL('image/png'); 356eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch break; 357eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch case 'image/jpeg': 358eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch default: 359eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch imageData = this.canvas_.toDataURL('image/jpeg', 0.9); 360eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 361eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 362eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 363eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Send and store in the persistent cache. 364eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.sendImageData_(imageData); 365eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.saveToCache_(imageData); 366eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 367eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 368eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 369eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Sends the resized image via the callback. 370eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {string} data Compressed image data. 371eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 372eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 373eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.sendImageData_ = function(data) { 3741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.sendResponse_( 3751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci {status: 'success', data: data, taskId: this.request_.taskId}); 376eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 377eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 378eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 379eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Handler, when contents are loaded into the image element. Performs resizing 380eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * and finalizes the request process. 381eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 382eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} callback Completion callback. 383eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 384eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 385eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.onImageLoad_ = function(callback) { 3867dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch // Perform processing if the url is not a data url, or if there are some 3877dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch // operations requested. 3887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (!this.request_.url.match(/^data/) || 3897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch ImageLoader.shouldProcess(this.image_.width, 390eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_.height, 391eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.request_)) { 392eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch ImageLoader.resize(this.image_, this.canvas_, this.request_); 393eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.sendImage_(true); // Image changed. 394eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } else { 395eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.sendImage_(false); // Image not changed. 396eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 397eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.cleanup_(); 398eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.downloadCallback_(); 399eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 400eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 401eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 402eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Handler, when loading of the image fails. Sends a failure response and 403eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * finalizes the request process. 404eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 405eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {function()} callback Completion callback. 406eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 407eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 408eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.onImageError_ = function(callback) { 4091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.sendResponse_( 4101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci {status: 'error', taskId: this.request_.taskId}); 411eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.cleanup_(); 412eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.downloadCallback_(); 413eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 414eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 415eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 416eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Cancels the request. 417eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 418eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.cancel = function() { 419eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.cleanup_(); 420eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 421eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // If downloading has started, then call the callback. 422eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (this.downloadCallback_) 423eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.downloadCallback_(); 424eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 425eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 426eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/** 427eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Cleans up memory used by this request. 428eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @private 429eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 430eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochRequest.prototype.cleanup_ = function() { 431eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_.onerror = function() {}; 432eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_.onload = function() {}; 433eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 434eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Transparent 1x1 pixel gif, to force garbage collecting. 435eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAA' + 436eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 'ABAAEAAAICTAEAOw=='; 437eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 438eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.xhr_.onload = function() {}; 439eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.xhr_.abort(); 440eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 441eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Dispose memory allocated by Canvas. 442eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.canvas_.width = 0; 443eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch this.canvas_.height = 0; 444eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}; 445