Commit 699c53eb authored by hirono's avatar hirono Committed by Commit bot

Gallery: Obtain metadata from MetadataCache when saving items.

Previously, when saving items, the new metadata was generated by
GalleryDataModel. But the generated metadata was broken and it contained old
thumbnail data that causes memory shortage.

The metadata for new items can be otained by using normal code path
(MetadataCache), so the CL removes special geneartion of metadata for new items
and let Gallery obtain the metadata by using MeatadataCache.

BUG=439898
TEST=GalleryJsTest.GalleryItemTest

Review URL: https://codereview.chromium.org/802283005

Cr-Commit-Position: refs/heads/master@{#310205}
parent 75ef78d7
......@@ -34,3 +34,8 @@ IN_PROC_BROWSER_TEST_F(GalleryJsTest, BackgroundTest) {
RunTest(base::FilePath(
FILE_PATH_LITERAL("background_unittest.html")));
}
IN_PROC_BROWSER_TEST_F(GalleryJsTest, GalleryItemTest) {
RunTest(base::FilePath(
FILE_PATH_LITERAL("gallery_item_unittest.html")));
}
......@@ -71,47 +71,27 @@ GalleryDataModel.prototype.saveItem = function(
var oldEntry = item.getEntry();
var oldMetadata = item.getMetadata();
var oldLocationInfo = item.getLocationInfo();
var metadataEncoder = ImageEncoder.encodeMetadata(
item.getMetadata(), canvas, 1 /* quality */);
var newMetadata = ContentProvider.ConvertContentMetadata(
metadataEncoder.getMetadata(),
MetadataCache.cloneMetadata(item.getMetadata()));
if (newMetadata.filesystem)
newMetadata.filesystem.modificationTime = new Date();
if (newMetadata.external)
newMetadata.external.present = true;
return new Promise(function(fulfill, reject) {
item.saveToFile(
volumeManager,
this.fallbackSaveDirectory,
overwrite,
canvas,
metadataEncoder,
function(success) {
if (!success) {
reject('Failed to save the image.');
return;
}
// The item's entry is updated to the latest entry. Update metadata.
item.setMetadata(newMetadata);
// Current entry is updated.
// Dispatch an event.
var event = new Event('content');
event.item = item;
event.oldEntry = oldEntry;
event.metadata = newMetadata;
event.metadata = item.getMetadata();
this.dispatchEvent(event);
if (util.isSameEntry(oldEntry, item.getEntry())) {
// Need an update of metdataCache.
this.metadataCache_.set(
item.getEntry(),
Gallery.METADATA_TYPE,
newMetadata);
} else {
if (!util.isSameEntry(oldEntry, item.getEntry())) {
// New entry is added and the item now tracks it.
// Add another item for the old entry.
var anotherItem = new Gallery.Item(
......
......@@ -111,14 +111,6 @@ Gallery.Item.prototype.getFetchedMedia = function() {
}.bind(this));
};
/**
* Sets the metadata.
* @param {!Object} metadata New metadata.
*/
Gallery.Item.prototype.setMetadata = function(metadata) {
this.metadata_ = Object.preventExtensions(metadata);
};
/**
* @return {string} File name.
*/
......@@ -231,30 +223,45 @@ Gallery.Item.prototype.createCopyName_ = function(dirEntry, callback) {
* Writes the new item content to either the existing or a new file.
*
* @param {!VolumeManager} volumeManager Volume manager instance.
* @param {string} fallbackDir Fallback directory in case the current directory
* is read only.
* @param {DirectoryEntry} fallbackDir Fallback directory in case the current
* directory is read only.
* @param {boolean} overwrite Whether to overwrite the image to the item or not.
* @param {!HTMLCanvasElement} canvas Source canvas.
* @param {!ImageEncoder.MetadataEncoder} metadataEncoder MetadataEncoder.
* @param {function(boolean)=} opt_callback Callback accepting true for success.
*/
Gallery.Item.prototype.saveToFile = function(
volumeManager, fallbackDir, overwrite, canvas, metadataEncoder,
opt_callback) {
volumeManager, fallbackDir, overwrite, canvas, opt_callback) {
ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime'));
var name = this.getFileName();
var onSuccess = function(entry, locationInfo) {
var onSuccess = function(entry) {
var locationInfo = volumeManager.getLocationInfo(entry);
if (!locationInfo) {
// Reuse old location info if it fails to obtain location info.
locationInfo = this.locationInfo_;
}
ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2);
ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime'));
this.entry_ = entry;
this.locationInfo_ = locationInfo;
this.metadataCache_.clear([this.entry_], 'fetchedMedia');
if (opt_callback)
opt_callback(true);
// Updates the metadata.
this.metadataCache_.clear([this.entry_], '*');
this.metadataCache_.getLatest(
[this.entry_],
Gallery.METADATA_TYPE,
function(metadataList) {
if (metadataList.length === 1) {
this.metadata_ = metadataList[0];
if (opt_callback)
opt_callback(true);
} else {
if (opt_callback)
opt_callback(false);
}
}.bind(this));
}.bind(this);
var onError = function(error) {
......@@ -266,15 +273,19 @@ Gallery.Item.prototype.saveToFile = function(
var doSave = function(newFile, fileEntry) {
fileEntry.createWriter(function(fileWriter) {
function writeContent() {
var writeContent = function() {
fileWriter.onwriteend = onSuccess.bind(null, fileEntry);
// TODO(hirono): Remove the quality 1 for thumbanils. The code path is
// no longer used.
var metadataEncoder = ImageEncoder.encodeMetadata(
this.metadata_, canvas, 1 /* quality */);
// Contrary to what one might think 1.0 is not a good default. Opening
// and saving an typical photo taken with consumer camera increases its
// file size by 50-100%. Experiments show that 0.9 is much better. It
// shrinks some photos a bit, keeps others about the same size, but does
// not visibly lower the quality.
fileWriter.write(ImageEncoder.getBlob(canvas, metadataEncoder, 0.9));
}
}.bind(this);
fileWriter.onerror = function(error) {
onError(error);
// Disable all callbacks on the first error.
......@@ -287,18 +298,12 @@ Gallery.Item.prototype.saveToFile = function(
fileWriter.onwriteend = writeContent;
fileWriter.truncate(0);
}
}, onError);
};
}.bind(this), onError);
}.bind(this);
var getFile = function(dir, newFile) {
dir.getFile(name, {create: newFile, exclusive: newFile},
function(fileEntry) {
var locationInfo = volumeManager.getLocationInfo(fileEntry);
// If the volume is gone, then abort the saving operation.
if (!locationInfo) {
onError('NotFound');
return;
}
doSave(newFile, fileEntry);
}.bind(this), onError);
}.bind(this);
......
<!DOCTYPE html>
<!-- Copyright 2015 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.
-->
<script>
// Define mock Gallery class to define Gallery.Item class.
var Gallery = function() {};
</script>
<!-- Should be loaded before volume_manager.js -->
<script src="../../file_manager/common/js/volume_manager_common.js"></script>
<!-- Others -->
<script src="../../../webui/resources/js/assert.js"></script>
<script src="../../../webui/resources/js/cr.js"></script>
<script src="../../../webui/resources/js/cr/event_target.js"></script>
<script src="../../../webui/resources/js/cr/ui/array_data_model.js"></script>
<script src="../../../webui/resources/js/load_time_data.js"></script>
<script src="../../file_manager/common/js/mock_entry.js"></script>
<script src="../../file_manager/common/js/unittest_util.js"></script>
<script src="../../file_manager/common/js/util.js"></script>
<script src="gallery_item.js"></script>
<script src="gallery_item_unittest.js"></script>
// Copyright 2015 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.
/**
* Mock of ImageUtil.
*/
var ImageUtil = {
getMetricName: function() {},
metrics: {
recordEnum: function() {},
recordInterval: function() {},
startInterval: function() {}
}
};
/**
* Mock of ImageEncoder
*/
var ImageEncoder = {
encodeMetadata: function() {},
getBlob: function() {}
};
/**
* Load time data.
*/
loadTimeData.data = {
DRIVE_DIRECTORY_LABEL: '',
DOWNLOADS_DIRECTORY_LABEL: ''
};
/**
* Tests for GalleryItem#saveToFile.
*/
function testSaveToFile(callback) {
var fileSystem = new MockFileSystem('volumeId');
fileSystem.populate(['/test.jpg']);
var entry = fileSystem.entries['/test.jpg'];
entry.createWriter = function(callback) {
callback({
write: function() {
Promise.resolve().then(function() {
this.onwriteend();
}.bind(this));
},
truncate: function() {
this.write();
}
});
};
var fetchedMediaCleared = false;
var metadataCache = {
getLatest: function(entries, type, callback) {
callback([{name: 'newMetadata'}]);
},
clear: function(entries, type) {
fetchedMediaCleared = true;
}
};
var item = new Gallery.Item(
entry,
{isReadOnly: false},
{name: 'oldMetadata'},
metadataCache,
/* original */ true);
assertEquals('oldMetadata', item.getMetadata().name);
assertFalse(fetchedMediaCleared);
reportPromise(
new Promise(item.saveToFile.bind(
item,
{getLocationInfo: function() { return {}; }},
null,
true,
document.createElement('canvas'))).then(function() {
assertEquals('newMetadata', item.getMetadata().name);
assertTrue(fetchedMediaCleared);
}), callback);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment