Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / file_manager / gallery / js / gallery_data_model.js
blobf3768ba98fa3888fcfb8225f27774b74e29d1403
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * Data model for gallery.
7  *
8  * @param {!MetadataModel} metadataModel
9  * @param {!EntryListWatcher=} opt_watcher Entry list watcher.
10  * @constructor
11  * @extends {cr.ui.ArrayDataModel}
12  */
13 function GalleryDataModel(metadataModel, opt_watcher) {
14   cr.ui.ArrayDataModel.call(this, []);
16   /**
17    * File system metadata.
18    * @private {!MetadataModel}
19    * @const
20    */
21   this.metadataModel_ = metadataModel;
23   /**
24    * Directory where the image is saved if the image is located in a read-only
25    * volume.
26    * @public {DirectoryEntry}
27    */
28   this.fallbackSaveDirectory = null;
30   // Start to watch file system entries.
31   var watcher = opt_watcher ? opt_watcher : new EntryListWatcher(this);
32   watcher.getEntry = function(item) { return item.getEntry(); };
34   this.addEventListener('splice', this.onSplice_.bind(this));
37 /**
38  * Maximum number of full size image cache.
39  * @type {number}
40  * @const
41  * @private
42  */
43 GalleryDataModel.MAX_FULL_IMAGE_CACHE_ = 3;
45 /**
46  * Maximum number of screen size image cache.
47  * @type {number}
48  * @const
49  * @private
50  */
51 GalleryDataModel.MAX_SCREEN_IMAGE_CACHE_ = 5;
53 GalleryDataModel.prototype = {
54   __proto__: cr.ui.ArrayDataModel.prototype
57 /**
58  * Saves new image.
59  *
60  * @param {!VolumeManagerWrapper} volumeManager Volume manager instance.
61  * @param {!Gallery.Item} item Original gallery item.
62  * @param {!HTMLCanvasElement} canvas Canvas containing new image.
63  * @param {boolean} overwrite Set true to overwrite original if it's possible.
64  * @return {!Promise} Promise to be fulfilled with when the operation completes.
65  */
66 GalleryDataModel.prototype.saveItem = function(
67     volumeManager, item, canvas, overwrite) {
68   var oldEntry = item.getEntry();
69   var oldLocationInfo = item.getLocationInfo();
70   var oldIsOriginal = item.isOriginal();
71   return new Promise(function(fulfill, reject) {
72     item.saveToFile(
73         volumeManager,
74         this.metadataModel_,
75         this.fallbackSaveDirectory,
76         canvas,
77         overwrite,
78         function(success) {
79           if (!success) {
80             reject('Failed to save the image.');
81             return;
82           }
84           // Current entry is updated.
85           // Dispatch an event.
86           var event = new Event('content');
87           event.item = item;
88           event.oldEntry = oldEntry;
89           event.thumbnailChanged = true;
90           this.dispatchEvent(event);
92           if (!util.isSameEntry(oldEntry, item.getEntry())) {
93             Promise.all([
94               this.metadataModel_.get(
95                   [oldEntry], Gallery.PREFETCH_PROPERTY_NAMES),
96               new ThumbnailModel(this.metadataModel_).get([oldEntry])
97             ]).then(function(itemLists) {
98               // New entry is added and the item now tracks it.
99               // Add another item for the old entry.
100               var anotherItem = new Gallery.Item(
101                   oldEntry,
102                   oldLocationInfo,
103                   itemLists[0][0],
104                   itemLists[1][0],
105                   oldIsOriginal);
106               // The item must be added behind the existing item so that it does
107               // not change the index of the existing item.
108               // TODO(hirono): Update the item index of the selection model
109               // correctly.
110               this.splice(this.indexOf(item) + 1, 0, anotherItem);
111             }.bind(this)).then(fulfill, reject);
112           } else {
113             fulfill();
114           }
115         }.bind(this));
116   }.bind(this));
120  * Evicts image caches in the items.
121  */
122 GalleryDataModel.prototype.evictCache = function() {
123   // Sort the item by the last accessed date.
124   var sorted = this.slice().sort(function(a, b) {
125     return b.getLastAccessedDate() - a.getLastAccessedDate();
126   });
128   // Evict caches.
129   var contentCacheCount = 0;
130   var screenCacheCount = 0;
131   for (var i = 0; i < sorted.length; i++) {
132     if (sorted[i].contentImage) {
133       if (++contentCacheCount > GalleryDataModel.MAX_FULL_IMAGE_CACHE_) {
134         if (sorted[i].contentImage.parentNode) {
135           console.error('The content image has a parent node.');
136         } else {
137           // Force to free the buffer of the canvas by assigning zero size.
138           sorted[i].contentImage.width = 0;
139           sorted[i].contentImage.height = 0;
140           sorted[i].contentImage = null;
141         }
142       }
143     }
144     if (sorted[i].screenImage) {
145       if (++screenCacheCount > GalleryDataModel.MAX_SCREEN_IMAGE_CACHE_) {
146         if (sorted[i].screenImage.parentNode) {
147           console.error('The screen image has a parent node.');
148         } else {
149           // Force to free the buffer of the canvas by assigning zero size.
150           sorted[i].screenImage.width = 0;
151           sorted[i].screenImage.height = 0;
152           sorted[i].screenImage = null;
153         }
154       }
155     }
156   }
160  * Handles entry delete.
161  * @param {!Event} event
162  * @private
163  */
164 GalleryDataModel.prototype.onSplice_ = function(event) {
165   if (!event.removed || !event.removed.length)
166     return;
167   var removedURLs = event.removed.map(function(item) {
168     return item.getEntry().toURL();
169   });
170   this.metadataModel_.notifyEntriesRemoved(removedURLs);