Merge from Chromium at DEPS revision r207203

This commit was generated by merge_to_master.py.

Change-Id: I5fbb6854d092096c4d39edc2865a48be1b53c418
diff --git a/chrome/browser/resources/file_manager/js/image_editor/image_util.js b/chrome/browser/resources/file_manager/js/image_editor/image_util.js
index 33c527d..7ae773a 100644
--- a/chrome/browser/resources/file_manager/js/image_editor/image_util.js
+++ b/chrome/browser/resources/file_manager/js/image_editor/image_util.js
@@ -403,11 +403,15 @@
  * 2. Copies pixels from HTMLImageElement to HTMLCanvasElement. This is done
  *    stripe-by-stripe to avoid freezing up the UI. The transform is taken into
  *    account.
+ *
  * @param {HTMLDocument} document Owner document.
+ * @param {MetadataCache=} opt_metadataCache Metadata cache. Required for
+ *     caching. If not passed, caching will be disabled.
  * @constructor
  */
-ImageUtil.ImageLoader = function(document) {
+ImageUtil.ImageLoader = function(document, opt_metadataCache) {
   this.document_ = document;
+  this.metadataCache_ = opt_metadataCache || null;
   this.image_ = new Image();
   this.generation_ = 0;
 };
@@ -427,6 +431,10 @@
 };
 
 /**
+ * Loads an image.
+ * TODO(mtomasz): Simplify, or even get rid of this class and merge with the
+ * ThumbnaiLoader class.
+ *
  * @param {string} url Image URL.
  * @param {function(function(object))} transformFetcher function to get
  *     the image transform (which we need for the image orientation).
@@ -462,9 +470,10 @@
     tmpCallback(emptyCanvas, opt_error);
   }.bind(this);
 
-  var loadImage = function() {
+  var loadImage = function(opt_metadata) {
     ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime'));
     this.timeout_ = null;
+
     this.image_.onload = function(e) {
       this.image_.onerror = null;
       this.image_.onload = null;
@@ -474,30 +483,32 @@
       }
       transformFetcher(url, onTransform.bind(this, e.target));
     }.bind(this);
-    // errorCallback has an optional error argument, which in case of general
-    // error should not be specified
+
+    // The error callback has an optional error argument, which in case of a
+    // general error should not be specified
     this.image_.onerror = onError.bind(this, 'IMAGE_ERROR');
-    this.taskId_ = util.loadImage(this.image_, url, {priority: 1});
+
+    // Extract the last modification date to determine if the cached image
+    // is outdated.
+    var modificationTime = opt_metadata &&
+                           opt_metadata.modificationTime &&
+                           opt_metadata.modificationTime.getTime();
+
+    this.taskId_ = util.loadImage(this.image_, url, {
+      cache: true,
+      timestamp: modificationTime,
+      priority: 1
+    });
   }.bind(this);
 
-  // The clients of this class sometimes request the same url repeatedly.
-  // The onload fires only if the src is different from the previous value.
-  // To work around that we reset the src temporarily to an 1x1 pixel.
-  // Load an empty 1x1 pixel image first.
-  var resetImage = function(callback) {
-    this.image_.onload = callback;
-    this.image_.onerror = onError.bind(this, 'IMAGE_ERROR');
-    this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAA' +
-        'AAABAAEAAAICTAEAOw==';
-  }.bind(this);
-
-  // Loads the image. If already loaded, then forces reloads.
-  var startLoad = function() {
-    if (this.image_.src == url)
-      resetImage(loadImage);
+  // Loads the image. If already loaded, then forces a reload.
+  var startLoad = this.resetImage_.bind(this, function() {
+    // Fetch metadata to detect last modification time for the caching purpose.
+    if (this.metadataCache_)
+      this.metadataCache_.get(url, 'filesystem', loadImage);
     else
       loadImage();
-  }.bind(this);
+  }.bind(this), onError);
 
   if (opt_delay) {
     this.timeout_ = setTimeout(startLoad, opt_delay);
@@ -507,6 +518,36 @@
 };
 
 /**
+ * Resets the image by forcing the garbage collection and clearing the src
+ * attribute.
+ *
+ * @param {function()} onSuccess Success callback.
+ * @param {function(opt_string)} onError Failure callback with an optional
+ *     error identifier.
+ * @private
+ */
+ImageUtil.ImageLoader.prototype.resetImage_ = function(onSuccess, onError) {
+  var clearSrc = function() {
+    this.image_.onload = onSuccess;
+    this.image_.onerror = onSuccess;
+    this.image_.src = '';
+  }.bind(this);
+
+  var emptyImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAA' +
+      'AAABAAEAAAICTAEAOw==';
+
+  if (this.image_.src != emptyImage) {
+    // Load an empty image, then clear src.
+    this.image_.onload = clearSrc;
+    this.image_.onerror = onError.bind(this, 'IMAGE_ERROR');
+    this.image_.src = emptyImage;
+  } else {
+    // Empty image already loaded, so clear src immediately.
+    clearSrc();
+  }
+};
+
+/**
  * @return {boolean} True if an image is loading.
  */
 ImageUtil.ImageLoader.prototype.isBusy = function() {