| 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 |