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

Unified 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 side-by-side diff with in-line comments
Download patch
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();
« 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