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