Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(433)

Side by Side Diff: chrome/browser/resources/image_loader/image_loader.js

Issue 12304013: Introduce Image loader extension. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Simplified. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 /**
6 * Loads and resizes an image.
7 * @constructor
8 */
9 var ImageLoader = function() {
10 /**
11 * Hash array of active requests.
12 * @type {Object}
13 * @private
14 */
15 this.requests_ = {};
16
17 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) {
18 // TODO(mtomasz): Handle.
19 });
20
21 chrome.extension.onConnectExternal.addListener(function(port) {
22 if (ImageLoader.ALLOWED_CLIENTS.indexOf(port.sender.id) !== -1)
23 port.onMessage.addListener(function(request) {
24 this.onMessage_(port, request, port.postMessage.bind(port));
25 }.bind(this));
26 }.bind(this));
27 };
28
29 /**
30 * List of extensions allowed to perform image requests.
31 *
32 * @const
33 * @type {Array.<string>}
34 */
35 ImageLoader.ALLOWED_CLIENTS =
36 ['hhaomjibdihmijegdhdafkllkbggdgoj']; // File Manager's extension id.
37
38 /**
39 * Handles a request. Depending on type of the request, starts or stops
40 * an image task.
41 *
42 * @param {Port} port Connection port.
43 * @param {Object} request Request message as a hash array.
44 * @param {function} callback Callback to be called to return response.
45 * @private
46 */
47 ImageLoader.prototype.onMessage_ = function(port, request, callback) {
48 var requestId = port.sender.id + ':' + request.taskId;
49 if (request.cancel) {
50 // Cancel a task.
51 if (requestId in this.requests_) {
52 this.requests_[requestId].cancel();
53 delete this.requests_[requestId];
54 }
55 } else {
56 // Start a task.
57 this.requests_[requestId] =
58 new ImageLoader.Request(request, callback);
59 }
60 };
61
62 /**
63 * Returns the singleton instance.
64 * @return {ImageLoader} ImageLoader object.
65 */
66 ImageLoader.getInstance = function() {
67 if (!ImageLoader.instance_)
68 ImageLoader.instance_ = new ImageLoader();
69 return ImageLoader.instance_;
70 };
71
72 /**
73 * Calculates dimensions taking into account resize options, such as:
74 * - scale: for scaling,
75 * - maxWidth, maxHeight: for maximum dimensions,
76 * - width, height: for exact requested size.
77 * Returns the target size as hash array with width, height properties.
78 *
79 * @param {number} width Source width.
80 * @param {number} height Source height.
81 * @param {Object} options Resizing options as a hash array.
82 * @return {Object} Dimensions, eg. { width: 100, height: 50 }.
83 */
84 ImageLoader.resizeDimensions = function(width, height, options) {
85 var sourceWidth = width;
86 var sourceHeight = height;
87
88 var targetWidth = sourceWidth;
89 var targetHeight = sourceHeight;
90
91 if ('scale' in options) {
92 targetWidth = sourceWidth * options.scale;
93 targetHeight = sourceHeight * options.scale;
94 }
95
96 if (options.maxWidth &&
97 targetWidth > options.maxWidth) {
98 var scale = options.maxWidth / targetWidth;
99 targetWidth *= scale;
100 targetHeight *= scale;
101 }
102
103 if (options.maxHeight &&
104 targetHeight > options.maxHeight) {
105 var scale = options.maxHeight / targetHeight;
106 targetWidth *= scale;
107 targetHeight *= scale;
108 }
109
110 if (options.width)
111 targetWidth = options.width;
112
113 if (options.height)
114 targetHeight = options.height;
115
116 targetWidth = Math.round(targetWidth);
117 targetHeight = Math.round(targetHeight);
118
119 return { width: targetWidth, height: targetHeight };
120 };
121
122 /**
123 * Performs resizing of the source image into the target canvas.
124 *
125 * @param {HTMLCanvasElement|Image} source Source image or canvas.
126 * @param {HTMLCanvasElement} target Target canvas.
127 * @param {Object} options Resizing options as a hash array.
128 */
129 ImageLoader.resize = function(source, target, options) {
130 var targetDimensions = ImageLoader.resizeDimensions(
131 source.width, source.height, options);
132
133 target.width = targetDimensions.width;
134 target.height = targetDimensions.height;
135
136 var targetContext = target.getContext('2d');
137 targetContext.drawImage(source,
138 0, 0, source.width, source.height,
139 0, 0, target.width, target.height);
140 };
141
142 /**
143 * Creates and starts downloading and then resizing of the image. Finally,
144 * returns the image using the callback.
145 *
146 * @param {Object} request Request message as a hash array.
147 * @param {function} callback Callback used to send the response.
148 * @constructor
149 */
150 ImageLoader.Request = function(request, callback) {
151 /**
152 * @type {Object}
153 * @private
154 */
155 this.request_ = request;
156
157 /**
158 * @type {function}
159 * @private
160 */
161 this.sendResponse_ = callback;
162
163 /**
164 * Temporary image used to download images.
165 * @type {Image}
166 * @private
167 */
168 this.image_ = new Image();
169
170 /**
171 * Used to download remote images using http:// or https:// protocols.
172 * @type {XMLHttpRequest}
173 * @private
174 */
175 this.xhr_ = new XMLHttpRequest();
176
177 /**
178 * Temporary canvas used to resize and compress the image.
179 * @type {HTMLCanvasElement}
180 * @private
181 */
182 this.canvas_ = document.createElement('canvas');
183
184 /**
185 * @type {CanvasRenderingContext2D}
186 * @private
187 */
188 this.context_ = this.canvas_.getContext('2d');
189
190 this.downloadOriginal_();
191 };
192
193 /**
194 * Downloads an image directly or for remote resources using the XmlHttpRequest.
195 * @private
196 */
197 ImageLoader.Request.prototype.downloadOriginal_ = function() {
198 this.image_.onload = this.onImageLoad_.bind(this);
199 this.image_.onerror = this.onImageError_.bind(this);
200
201 if (window.harness || !this.request_.url.match(/^https?:/)) {
202 // Download directly.
203 this.image_.src = this.request_.url;
204 return;
205 }
206
207 // Download using an xhr request.
208 this.xhr_.responseType = 'blob';
209
210 this.xhr_.onerror = this.image_.onerror;
211 this.xhr_.onload = function() {
212 if (this.xhr_.status != 200) {
213 this.image_.onerror();
214 return;
215 }
216
217 // Process returnes data.
218 var reader = new FileReader();
219 reader.onerror = this.image_.onerror;
220 reader.onload = function(e) {
221 this.image_.src = e.target.result;
222 }.bind(this);
223
224 // Load the data to the image as a data url.
225 reader.readAsDataURL(this.xhr_.response);
226 }.bind(this);
227
228 // Perform a xhr request.
229 try {
230 this.xhr_.open('GET', this.request_.url, true);
231 this.xhr_.send();
232 } catch (e) {
233 this.image_.onerror();
234 }
235 };
236
237 /**
238 * Sends the resized image via the callback.
239 * @private
240 */
241 ImageLoader.Request.prototype.sendImage_ = function() {
242 // TODO(mtomasz): Keep format. Never compress using jpeg codec for lossless
243 // images such as png, gif.
244 var pngData = this.canvas_.toDataURL('image/png');
245 var jpegData = this.canvas_.toDataURL('image/jpeg', 0.9);
246 var imageData = pngData.length < jpegData.length * 2 ? pngData : jpegData;
247 this.sendResponse_({ status: 'success',
248 data: imageData,
249 taskId: this.request_.taskId });
250 };
251
252 /**
253 * Handler, when contents are loaded into the image element. Performs resizing
254 * and finalizes the request process.
255 *
256 * @private
257 */
258 ImageLoader.Request.prototype.onImageLoad_ = function() {
259 ImageLoader.resize(this.image_, this.canvas_, this.request_);
260 this.sendImage_();
261 this.cleanup_();
262 };
263
264 /**
265 * Handler, when loading of the image fails. Sends a failure response and
266 * finalizes the request process.
267 *
268 * @private
269 */
270 ImageLoader.Request.prototype.onImageError_ = function() {
271 this.sendResponse_({ status: 'error',
272 taskId: this.request_.taskId });
273 this.cleanup_();
274 };
275
276 /**
277 * Cancels the request.
278 */
279 ImageLoader.Request.prototype.cancel = function() {
280 this.cleanup_();
281 };
282
283 /**
284 * Cleans up memory used by this request.
285 * @private
286 */
287 ImageLoader.Request.prototype.cleanup_ = function() {
288 this.image_.onerror = function() {};
289 this.image_.onload = function() {};
290
291 // Transparent 1x1 pixel gif, to force garbage collecting.
292 this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAA' +
293 'ABAAEAAAICTAEAOw==';
294
295 this.xhr_.onerror = function() {};
296 this.xhr_.onload = function() {};
297 this.xhr_.abort();
298
299 // Dispose memory allocated by Canvas.
300 this.canvas_.width = 0;
301 this.canvas_.height = 0;
302 };
303
304 // Load the extension.
305 ImageLoader.getInstance();
OLDNEW
« no previous file with comments | « chrome/browser/resources/image_loader/client.js ('k') | chrome/browser/resources/image_loader/manifest.json » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698