OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/chrome/browser/snapshots/snapshot_cache.h" | 5 #import "ios/chrome/browser/snapshots/snapshot_cache.h" |
6 | 6 |
7 #import <UIKit/UIKit.h> | 7 #import <UIKit/UIKit.h> |
8 | 8 |
9 #include "base/critical_closure.h" | 9 #include "base/critical_closure.h" |
10 #include "base/files/file_enumerator.h" | 10 #include "base/files/file_enumerator.h" |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
133 + (SnapshotCache*)sharedInstance { | 133 + (SnapshotCache*)sharedInstance { |
134 static SnapshotCache* instance = [[SnapshotCache alloc] init]; | 134 static SnapshotCache* instance = [[SnapshotCache alloc] init]; |
135 return instance; | 135 return instance; |
136 } | 136 } |
137 | 137 |
138 - (id)init { | 138 - (id)init { |
139 if ((self = [super init])) { | 139 if ((self = [super init])) { |
140 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 140 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
141 propertyReleaser_SnapshotCache_.Init(self, [SnapshotCache class]); | 141 propertyReleaser_SnapshotCache_.Init(self, [SnapshotCache class]); |
142 | 142 |
143 // TODO(andybons): In the case where the cache grows, it is expensive. | 143 if (!IsIPadIdiom()) { |
144 // Make sure this doesn't suck when there are more than ten tabs. | 144 // TODO(jbbegue): In the case where the cache grows, it is expensive. |
145 imageDictionary_.reset( | 145 // Make sure this doesn't suck when there are more than ten tabs. |
146 [[NSMutableDictionary alloc] initWithCapacity:kCacheInitialCapacity]); | 146 imageDictionary_.reset( |
147 [[NSNotificationCenter defaultCenter] | 147 [[NSMutableDictionary alloc] initWithCapacity:kCacheInitialCapacity]); |
148 addObserver:self | 148 [[NSNotificationCenter defaultCenter] |
149 selector:@selector(handleLowMemory) | 149 addObserver:self |
150 name:UIApplicationDidReceiveMemoryWarningNotification | 150 selector:@selector(handleLowMemory) |
151 object:nil]; | 151 name:UIApplicationDidReceiveMemoryWarningNotification |
152 [[NSNotificationCenter defaultCenter] | 152 object:nil]; |
153 addObserver:self | 153 [[NSNotificationCenter defaultCenter] |
154 selector:@selector(handleEnterBackground) | 154 addObserver:self |
155 name:UIApplicationDidEnterBackgroundNotification | 155 selector:@selector(handleEnterBackground) |
156 object:nil]; | 156 name:UIApplicationDidEnterBackgroundNotification |
157 [[NSNotificationCenter defaultCenter] | 157 object:nil]; |
158 addObserver:self | 158 [[NSNotificationCenter defaultCenter] |
159 selector:@selector(handleBecomeActive) | 159 addObserver:self |
160 name:UIApplicationDidBecomeActiveNotification | 160 selector:@selector(handleBecomeActive) |
161 object:nil]; | 161 name:UIApplicationDidBecomeActiveNotification |
| 162 object:nil]; |
| 163 } |
162 } | 164 } |
163 return self; | 165 return self; |
164 } | 166 } |
165 | 167 |
166 - (void)dealloc { | 168 - (void)dealloc { |
167 [[NSNotificationCenter defaultCenter] | 169 if (!IsIPadIdiom()) { |
168 removeObserver:self | 170 [[NSNotificationCenter defaultCenter] |
169 name:UIApplicationDidReceiveMemoryWarningNotification | 171 removeObserver:self |
170 object:nil]; | 172 name:UIApplicationDidReceiveMemoryWarningNotification |
171 [[NSNotificationCenter defaultCenter] | 173 object:nil]; |
172 removeObserver:self | 174 [[NSNotificationCenter defaultCenter] |
173 name:UIApplicationDidEnterBackgroundNotification | 175 removeObserver:self |
174 object:nil]; | 176 name:UIApplicationDidEnterBackgroundNotification |
175 [[NSNotificationCenter defaultCenter] | 177 object:nil]; |
176 removeObserver:self | 178 [[NSNotificationCenter defaultCenter] |
177 name:UIApplicationDidBecomeActiveNotification | 179 removeObserver:self |
178 object:nil]; | 180 name:UIApplicationDidBecomeActiveNotification |
| 181 object:nil]; |
| 182 } |
179 [super dealloc]; | 183 [super dealloc]; |
180 } | 184 } |
181 | 185 |
182 + (CGFloat)snapshotScaleForDevice { | 186 + (CGFloat)snapshotScaleForDevice { |
183 // On handset, the color snapshot is used for the stack view, so the scale of | 187 // On handset, the color snapshot is used for the stack view, so the scale of |
184 // the snapshot images should match the scale of the device. | 188 // the snapshot images should match the scale of the device. |
185 // On tablet, the color snapshot is only used to generate the grey snapshot, | 189 // On tablet, the color snapshot is only used to generate the grey snapshot, |
186 // which does not have to be high quality, so use scale of 1.0 on all tablets. | 190 // which does not have to be high quality, so use scale of 1.0 on all tablets. |
187 if (IsIPadIdiom()) { | 191 if (IsIPadIdiom()) { |
188 return 1.0; | 192 return 1.0; |
189 } | 193 } |
190 // Cap snapshot resolution to 2x to reduce the amount of memory they use. | 194 // Cap snapshot resolution to 2x to reduce the amount of memory they use. |
191 return MIN([UIScreen mainScreen].scale, 2.0); | 195 return MIN([UIScreen mainScreen].scale, 2.0); |
192 } | 196 } |
193 | 197 |
194 - (void)retrieveImageForSessionID:(NSString*)sessionID | 198 - (void)retrieveImageForSessionID:(NSString*)sessionID |
195 callback:(void (^)(UIImage*))callback { | 199 callback:(void (^)(UIImage*))callback { |
196 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 200 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
197 DCHECK(sessionID); | 201 DCHECK(sessionID); |
| 202 // iPad does not cache images, so if there is no callback we can avoid an |
| 203 // expensive read from storage. |
| 204 if (IsIPadIdiom() && !callback) |
| 205 return; |
| 206 |
198 UIImage* img = [imageDictionary_ objectForKey:sessionID]; | 207 UIImage* img = [imageDictionary_ objectForKey:sessionID]; |
199 if (img) { | 208 if (img) { |
200 if (callback) | 209 if (callback) |
201 callback(img); | 210 callback(img); |
202 return; | 211 return; |
203 } | 212 } |
204 | 213 |
205 base::PostTaskAndReplyWithResult( | 214 base::PostTaskAndReplyWithResult( |
206 web::WebThread::GetMessageLoopProxyForThread( | 215 web::WebThread::GetMessageLoopProxyForThread( |
207 web::WebThread::FILE_USER_BLOCKING).get(), | 216 web::WebThread::FILE_USER_BLOCKING).get(), |
208 FROM_HERE, | 217 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() { |
209 base::BindBlock(^base::scoped_nsobject<UIImage>() { | |
210 // Retrieve the image on a high priority thread. | 218 // Retrieve the image on a high priority thread. |
211 return base::scoped_nsobject<UIImage>([ReadImageFromDisk( | 219 return base::scoped_nsobject<UIImage>([ReadImageFromDisk( |
212 [SnapshotCache imagePathForSessionID:sessionID]) retain]); | 220 [SnapshotCache imagePathForSessionID:sessionID]) retain]); |
213 }), | 221 }), |
214 base::BindBlock(^(base::scoped_nsobject<UIImage> image) { | 222 base::BindBlock(^(base::scoped_nsobject<UIImage> image) { |
215 if (image) | 223 // The iPad tab switcher is currently using its own memory cache so the |
| 224 // image is not stored in memory here if running on iPad. |
| 225 // The same logic is used on image writes (code below). |
| 226 if (!IsIPadIdiom() && image) |
216 [imageDictionary_ setObject:image forKey:sessionID]; | 227 [imageDictionary_ setObject:image forKey:sessionID]; |
217 if (callback) | 228 if (callback) |
218 callback(image); | 229 callback(image); |
219 })); | 230 })); |
220 } | 231 } |
221 | 232 |
222 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID { | 233 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID { |
223 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 234 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
224 if (!img || !sessionID) | 235 if (!img || !sessionID) |
225 return; | 236 return; |
226 | 237 |
227 // Color snapshots are not used on tablets, so don't keep them in memory. | 238 // The iPad tab switcher is currently using its own memory cache so the image |
| 239 // is not stored in memory here if running on iPad. |
| 240 // The same logic is used on image reads (code above). |
228 if (!IsIPadIdiom()) { | 241 if (!IsIPadIdiom()) { |
229 [imageDictionary_ setObject:img forKey:sessionID]; | 242 [imageDictionary_ setObject:img forKey:sessionID]; |
230 } | 243 } |
231 // Save the image to disk. | 244 // Save the image to disk. |
232 web::WebThread::PostBlockingPoolSequencedTask( | 245 web::WebThread::PostBlockingPoolSequencedTask( |
233 kSequenceToken, FROM_HERE, | 246 kSequenceToken, FROM_HERE, |
234 base::BindBlock(^{ | 247 base::BindBlock(^{ |
235 base::scoped_nsobject<UIImage> image([img retain]); | 248 base::scoped_nsobject<UIImage> image([img retain]); |
236 WriteImageToDisk(image, | 249 WriteImageToDisk(image, |
237 [SnapshotCache imagePathForSessionID:sessionID]); | 250 [SnapshotCache imagePathForSessionID:sessionID]); |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 - (void)willBeSavedGreyWhenBackgrounding:(NSString*)sessionID { | 352 - (void)willBeSavedGreyWhenBackgrounding:(NSString*)sessionID { |
340 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 353 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
341 if (!sessionID) | 354 if (!sessionID) |
342 return; | 355 return; |
343 backgroundingImageSessionId_.reset([sessionID copy]); | 356 backgroundingImageSessionId_.reset([sessionID copy]); |
344 backgroundingColorImage_.reset( | 357 backgroundingColorImage_.reset( |
345 [[imageDictionary_ objectForKey:sessionID] retain]); | 358 [[imageDictionary_ objectForKey:sessionID] retain]); |
346 } | 359 } |
347 | 360 |
348 - (void)handleLowMemory { | 361 - (void)handleLowMemory { |
| 362 DCHECK(!IsIPadIdiom()); |
349 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 363 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
350 NSMutableDictionary* dictionary = | 364 NSMutableDictionary* dictionary = |
351 [[NSMutableDictionary alloc] initWithCapacity:2]; | 365 [[NSMutableDictionary alloc] initWithCapacity:2]; |
352 for (NSString* sessionID in pinnedIDs_) { | 366 for (NSString* sessionID in pinnedIDs_) { |
353 UIImage* image = [imageDictionary_ objectForKey:sessionID]; | 367 UIImage* image = [imageDictionary_ objectForKey:sessionID]; |
354 if (image) | 368 if (image) |
355 [dictionary setObject:image forKey:sessionID]; | 369 [dictionary setObject:image forKey:sessionID]; |
356 } | 370 } |
357 imageDictionary_.reset(dictionary); | 371 imageDictionary_.reset(dictionary); |
358 } | 372 } |
359 | 373 |
360 - (void)handleEnterBackground { | 374 - (void)handleEnterBackground { |
| 375 DCHECK(!IsIPadIdiom()); |
361 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 376 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
362 [imageDictionary_ removeAllObjects]; | 377 [imageDictionary_ removeAllObjects]; |
363 } | 378 } |
364 | 379 |
365 - (void)handleBecomeActive { | 380 - (void)handleBecomeActive { |
| 381 DCHECK(!IsIPadIdiom()); |
366 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 382 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
367 for (NSString* sessionID in pinnedIDs_) | 383 for (NSString* sessionID in pinnedIDs_) |
368 [self retrieveImageForSessionID:sessionID callback:nil]; | 384 [self retrieveImageForSessionID:sessionID callback:nil]; |
369 } | 385 } |
370 | 386 |
371 - (void)saveGreyImage:(UIImage*)greyImage forKey:(NSString*)sessionID { | 387 - (void)saveGreyImage:(UIImage*)greyImage forKey:(NSString*)sessionID { |
372 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 388 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
373 if (greyImage) | 389 if (greyImage) |
374 [greyImageDictionary_ setObject:greyImage forKey:sessionID]; | 390 [greyImageDictionary_ setObject:greyImage forKey:sessionID]; |
375 if ([sessionID isEqualToString:mostRecentGreySessionId_]) { | 391 if ([sessionID isEqualToString:mostRecentGreySessionId_]) { |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
499 backgroundingImageSessionId_.reset(); | 515 backgroundingImageSessionId_.reset(); |
500 } | 516 } |
501 } | 517 } |
502 | 518 |
503 web::WebThread::PostBlockingPoolTask( | 519 web::WebThread::PostBlockingPoolTask( |
504 FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath, | 520 FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath, |
505 greyImagePath, backgroundingColorImage_)); | 521 greyImagePath, backgroundingColorImage_)); |
506 } | 522 } |
507 | 523 |
508 @end | 524 @end |
OLD | NEW |