Chromium Code Reviews| Index: chrome/browser/resources/image_loader/image_loader.js |
| diff --git a/chrome/browser/resources/image_loader/image_loader.js b/chrome/browser/resources/image_loader/image_loader.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0ed73b7a6f497432e4a3d145b7345f1761dc834e |
| --- /dev/null |
| +++ b/chrome/browser/resources/image_loader/image_loader.js |
| @@ -0,0 +1,277 @@ |
| +// Copyright 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +/** |
| + * Loads and resizes an image. |
| + * @constructor |
| + */ |
| +var ImageLoader = function() { |
| + this.requests_ = {}; |
| + |
| + chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { |
| + // TODO(mtomasz): Handle. |
| + }); |
| + |
| + chrome.extension.onConnectExternal.addListener(function(port) { |
| + if (ImageLoader.ALLOWED_CLIENTS.indexOf(port.sender.id) !== -1) |
| + port.onMessage.addListener(function(request) { |
| + this.onMessage_(port, request, port.postMessage.bind(port)); |
| + }.bind(this)); |
| + }.bind(this)); |
| +}; |
| + |
| +/** |
| + * List of extensions allowed to perform image requests. |
| + * |
| + * @const |
| + * @type {Array.<string>} |
| + */ |
| +ImageLoader.ALLOWED_CLIENTS = |
| + ['hhaomjibdihmijegdhdafkllkbggdgoj']; // File Manager's extension id. |
| + |
| +/** |
| + * Handles a request. Depending on type of the request, starts or stops |
| + * an image task. |
| + * |
| + * @param {Port} port Connection port. |
| + * @param {Object} request Request message as a hash array. |
| + * @param {function} callback Callback to be called to return response. |
| + * @private |
| + */ |
| +ImageLoader.prototype.onMessage_ = function(port, request, callback) { |
| + var requestId = port.sender.id + ':' + request.taskId; |
| + if (request.cancel) { |
| + // Cancel a task. |
| + if (requestId in this.requests_) { |
| + this.requests_[requestId].cancel(); |
| + delete this.requests_[requestId]; |
| + } |
| + } else { |
| + // Start a task. |
| + this.requests_[requestId] = |
| + new ImageLoader.Request(request, callback); |
| + } |
| +}; |
| + |
| +/** |
| + * Loads the image loader. Static. |
| + */ |
| +ImageLoader.load = function() { |
|
James Hawkins
2013/02/20 04:31:06
Why is there a load method? Just have the client
mtomasz
2013/02/21 05:19:24
Client doesn't call it. Client communicates with I
James Hawkins
2013/02/21 23:20:36
Keep the code (more) simple and get rid of it.
mtomasz
2013/02/22 04:08:34
Done.
|
| + ImageLoader.getInstance(); |
| +}; |
| + |
| +/** |
| + * Returns the singleton instance. Static. |
|
James Hawkins
2013/02/20 04:31:06
nit: Remove 'static' here and elsewhere.
mtomasz
2013/02/21 05:19:24
Done.
|
| + * @return {ImageLoader} ImageLoader object. |
| + */ |
| +ImageLoader.getInstance = function() { |
| + if (!this.instance_) |
| + this.instance_ = new ImageLoader(); |
| + return this.instance_; |
| +}; |
| + |
| +/** |
| + * Calculates dimensions taking into account resize options, such as: |
| + * - scale: for scaling, |
| + * - maxWidth, maxHeight: for maximum dimensions, |
| + * - width, height: for exact requested size. |
| + * Returns the target size as hash array with width, height properties. |
| + * Static. |
| + * |
| + * @param {number} width Source width. |
| + * @param {number} height Source height. |
| + * @param {Object} options Resizing options as a hash array. |
| + * @return {Object} Dimensions, eg. { width: 100, height: 50 }. |
| + */ |
| +ImageLoader.resizeDimensions = function(width, height, options) { |
| + var sourceWidth = width; |
| + var sourceHeight = height; |
| + |
| + var targetWidth = sourceWidth; |
| + var targetHeight = sourceHeight; |
| + |
| + if ('scale' in options) { |
| + targetWidth = sourceWidth * options.scale; |
| + targetHeight = sourceHeight * options.scale; |
| + } |
| + |
| + if (options.maxWidth && |
| + targetWidth > options.maxWidth) { |
| + var scale = options.maxWidth / targetWidth; |
| + targetWidth *= scale; |
| + targetHeight *= scale; |
| + } |
| + |
| + if (options.maxHeight && |
| + targetHeight > options.maxHeight) { |
| + var scale = options.maxHeight / targetHeight; |
| + targetWidth *= scale; |
| + targetHeight *= scale; |
| + } |
| + |
| + if (options.width) |
| + targetWidth = options.width; |
| + |
| + if (options.height) |
| + targetHeight = options.height; |
| + |
| + targetWidth = Math.round(targetWidth); |
| + targetHeight = Math.round(targetHeight); |
| + |
| + return { width: targetWidth, height: targetHeight }; |
| +}; |
| + |
| +/** |
| + * Performs resizing of the source image into the target canvas. Static. |
| + * |
| + * @param {HTMLCanvasElement|Image} source Source image or canvas. |
| + * @param {HTMLCanvasElement} target Target canvas. |
| + * @param {Object} options Resizing options as a hash array. |
| + */ |
| +ImageLoader.resize = function(source, target, options) { |
| + var targetDimensions = ImageLoader.resizeDimensions( |
| + source.width, source.height, options); |
| + |
| + target.width = targetDimensions.width; |
| + target.height = targetDimensions.height; |
| + |
| + var targetContext = target.getContext('2d'); |
| + targetContext.drawImage(source, |
| + 0, 0, source.width, source.height, |
| + 0, 0, target.width, target.height); |
| +}; |
| + |
| +/** |
| + * Creates and starts downloading and then resizing of the image. Finally, |
| + * returns the image using the callback. |
| + * |
| + * @param {Object} request Request message as a hash array. |
| + * @param {function} callback Callback used to send the response. |
| + * @constructor |
| + */ |
| +ImageLoader.Request = function(request, callback) { |
| + this.request_ = request; |
| + this.sendResponse_ = callback; |
| + |
| + this.image_ = new Image(); |
| + this.xhr_ = new XMLHttpRequest(); |
| + this.canvas_ = document.createElement('canvas'); |
| + this.context_ = this.canvas_.getContext('2d'); |
| + |
| + this.downloadOriginal_(); |
| +}; |
| + |
| +/** |
| + * Downloads an image directly or for remote resources using the XmlHttpRequest. |
| + * @private |
| + */ |
| +ImageLoader.Request.prototype.downloadOriginal_ = function() { |
| + this.image_.onload = this.onImageLoad_.bind(this); |
| + this.image_.onerror = this.onImageError_.bind(this); |
| + |
| + if (window.harness || !this.request_.url.match(/^https?:/)) { |
| + // Download directly. |
| + this.image_.src = this.request_.url; |
| + return; |
| + } |
| + |
| + // Download using an xhr request. |
| + this.xhr_.responseType = 'blob'; |
| + |
| + this.xhr_.onerror = this.image_.onerror; |
| + this.xhr_.onload = function() { |
| + if (this.xhr_.status != 200) { |
| + this.image_.onerror(); |
| + return; |
| + } |
| + |
| + // Process returnes data. |
| + var reader = new FileReader(); |
| + reader.onerror = this.image_.onerror; |
| + reader.onload = function(e) { |
| + this.image_.src = e.target.result; |
| + }.bind(this); |
| + |
| + // Load the data to the image as a data url. |
| + reader.readAsDataURL(this.xhr_.response); |
| + }.bind(this); |
| + |
| + // Perform a xhr request. |
| + try { |
| + this.xhr_.open('GET', this.request_.url, true); |
| + this.xhr_.send(); |
| + } catch (e) { |
| + this.image_.onerror(); |
| + } |
| +}; |
| + |
| +/** |
| + * Sends the resized image via the callback. |
| + * @private |
| + */ |
| +ImageLoader.Request.prototype.sendImage_ = function() { |
| + // TODO(mtomasz): Keep format. Never compress using jpeg codec for lossless |
| + // images such as png, gif. |
| + var pngData = this.canvas_.toDataURL('image/png'); |
| + var jpegData = this.canvas_.toDataURL('image/jpeg', 0.9); |
| + var imageData = pngData.length < jpegData.length * 2 ? pngData : jpegData; |
| + this.sendResponse_({ status: 'success', |
| + data: imageData, |
| + taskId: this.request_.taskId }); |
| +}; |
| + |
| +/** |
| + * Handler, when contents are loaded into the image element. Performs resizing |
| + * and finalizes the request process. |
| + * |
| + * @private |
| + */ |
| +ImageLoader.Request.prototype.onImageLoad_ = function() { |
| + ImageLoader.resize(this.image_, this.canvas_, this.request_); |
| + this.sendImage_(); |
| + this.cleanup_(); |
| +}; |
| + |
| +/** |
| + * Handler, when loading of the image fails. Sends a failure response and |
| + * finalizes the request process. |
| + * |
| + * @private |
| + */ |
| +ImageLoader.Request.prototype.onImageError_ = function() { |
| + this.sendResponse_({ status: 'error', |
| + taskId: this.request_.taskId }); |
| + this.cleanup_(); |
| +}; |
| + |
| +/** |
| + * Cancels the request. |
| + */ |
| +ImageLoader.Request.prototype.cancel = function() { |
| + this.cleanup_(); |
| +}; |
| + |
| +/** |
| + * Cleans up memory used by this request. |
| + * @private |
| + */ |
| +ImageLoader.Request.prototype.cleanup_ = function() { |
| + this.image_.onerror = function() {}; |
| + this.image_.onload = function() {}; |
| + |
| + // Transparent 1x1 pixel gif, to force garbage collecting. |
| + this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAA' + |
| + 'ABAAEAAAICTAEAOw=='; |
| + |
| + this.xhr_.onerror = function() {}; |
| + this.xhr_.onload = function() {}; |
| + this.xhr_.abort(); |
| + |
| + // Dispose memory allocated by Canvas. |
| + this.canvas_.width = 0; |
| + this.canvas_.height = 0; |
| +}; |
| + |
| +// Load the extension. |
| +ImageLoader.load(); |