| 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..f3a5e97d3ff4ed3af01b56cb616358613344fd30
|
| --- /dev/null
|
| +++ b/chrome/browser/resources/image_loader/image_loader.js
|
| @@ -0,0 +1,305 @@
|
| +// Copyright 2013 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() {
|
| + /**
|
| + * Hash array of active requests.
|
| + * @type {Object}
|
| + * @private
|
| + */
|
| + 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);
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Returns the singleton instance.
|
| + * @return {ImageLoader} ImageLoader object.
|
| + */
|
| +ImageLoader.getInstance = function() {
|
| + if (!ImageLoader.instance_)
|
| + ImageLoader.instance_ = new ImageLoader();
|
| + return ImageLoader.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.
|
| + *
|
| + * @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.
|
| + *
|
| + * @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) {
|
| + /**
|
| + * @type {Object}
|
| + * @private
|
| + */
|
| + this.request_ = request;
|
| +
|
| + /**
|
| + * @type {function}
|
| + * @private
|
| + */
|
| + this.sendResponse_ = callback;
|
| +
|
| + /**
|
| + * Temporary image used to download images.
|
| + * @type {Image}
|
| + * @private
|
| + */
|
| + this.image_ = new Image();
|
| +
|
| + /**
|
| + * Used to download remote images using http:// or https:// protocols.
|
| + * @type {XMLHttpRequest}
|
| + * @private
|
| + */
|
| + this.xhr_ = new XMLHttpRequest();
|
| +
|
| + /**
|
| + * Temporary canvas used to resize and compress the image.
|
| + * @type {HTMLCanvasElement}
|
| + * @private
|
| + */
|
| + this.canvas_ = document.createElement('canvas');
|
| +
|
| + /**
|
| + * @type {CanvasRenderingContext2D}
|
| + * @private
|
| + */
|
| + 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.getInstance();
|
|
|