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" |
11 #include "base/files/file_path.h" | 11 #include "base/files/file_path.h" |
12 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
13 #include "base/location.h" | 13 #include "base/location.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/mac/bind_objc_block.h" | 15 #include "base/mac/bind_objc_block.h" |
16 #include "base/mac/scoped_cftyperef.h" | 16 #include "base/mac/scoped_cftyperef.h" |
17 #include "base/strings/sys_string_conversions.h" | 17 #include "base/strings/sys_string_conversions.h" |
18 #include "base/task_runner_util.h" | 18 #include "base/task_runner_util.h" |
19 #include "base/threading/thread_restrictions.h" | 19 #include "base/threading/thread_restrictions.h" |
20 #include "ios/chrome/browser/experimental_flags.h" | 20 #include "ios/chrome/browser/experimental_flags.h" |
21 #include "ios/chrome/browser/ui/ui_util.h" | 21 #include "ios/chrome/browser/ui/ui_util.h" |
22 #import "ios/chrome/browser/ui/uikit_ui_util.h" | 22 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
23 #include "ios/web/public/web_thread.h" | 23 #include "ios/web/public/web_thread.h" |
24 | 24 |
25 @interface SnapshotCache () | 25 @interface SnapshotCache () |
26 + (base::FilePath)imagePathForSessionID:(NSString*)sessionID; | 26 + (base::FilePath)imagePathForSessionID:(NSString*)sessionID; |
27 + (base::FilePath)greyImagePathForSessionID:(NSString*)sessionID; | 27 + (base::FilePath)greyImagePathForSessionID:(NSString*)sessionID; |
28 // Returns the directory where the thumbnails are saved. | 28 // Returns the directory where the thumbnails are saved. |
29 + (base::FilePath)cacheDirectory; | 29 + (base::FilePath)cacheDirectory; |
| 30 // Returns whether the in-memory cache (as opposed to the on-disk cache) is |
| 31 // enabled. |
| 32 - (BOOL)inMemoryCacheIsEnabled; |
| 33 // Returns whether the snapshots are cached in a LRU cache. |
| 34 - (BOOL)useLRUCache; |
30 // Returns the directory where the thumbnails were stored in M28 and earlier. | 35 // Returns the directory where the thumbnails were stored in M28 and earlier. |
31 - (base::FilePath)oldCacheDirectory; | 36 - (base::FilePath)oldCacheDirectory; |
32 // Remove all UIImages from |imageDictionary_|. | 37 // Remove all UIImages from |imageDictionary_|. |
33 - (void)handleEnterBackground; | 38 - (void)handleEnterBackground; |
34 // Remove all but adjacent UIImages from |imageDictionary_|. | 39 // Remove all but adjacent UIImages from |imageDictionary_|. |
35 - (void)handleLowMemory; | 40 - (void)handleLowMemory; |
36 // Restore adjacent UIImages to |imageDictionary_|. | 41 // Restore adjacent UIImages to |imageDictionary_|. |
37 - (void)handleBecomeActive; | 42 - (void)handleBecomeActive; |
38 // Clear most recent caller information. | 43 // Clear most recent caller information. |
39 - (void)clearGreySessionInfo; | 44 - (void)clearGreySessionInfo; |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 + (SnapshotCache*)sharedInstance { | 142 + (SnapshotCache*)sharedInstance { |
138 static SnapshotCache* instance = [[SnapshotCache alloc] init]; | 143 static SnapshotCache* instance = [[SnapshotCache alloc] init]; |
139 return instance; | 144 return instance; |
140 } | 145 } |
141 | 146 |
142 - (id)init { | 147 - (id)init { |
143 if ((self = [super init])) { | 148 if ((self = [super init])) { |
144 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 149 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
145 propertyReleaser_SnapshotCache_.Init(self, [SnapshotCache class]); | 150 propertyReleaser_SnapshotCache_.Init(self, [SnapshotCache class]); |
146 | 151 |
147 // Always use the LRUCache when the tab switcher is enabled. | 152 if ([self useLRUCache]) { |
148 if (experimental_flags::IsTabSwitcherEnabled() || | |
149 experimental_flags::IsLRUSnapshotCacheEnabled()) { | |
150 lruCache_.reset( | 153 lruCache_.reset( |
151 [[LRUCache alloc] initWithCacheSize:kLRUCacheMaxCapacity]); | 154 [[LRUCache alloc] initWithCacheSize:kLRUCacheMaxCapacity]); |
152 } else { | 155 } else { |
153 imageDictionary_.reset( | 156 imageDictionary_.reset( |
154 [[NSMutableDictionary alloc] initWithCapacity:kCacheInitialCapacity]); | 157 [[NSMutableDictionary alloc] initWithCapacity:kCacheInitialCapacity]); |
155 } | 158 } |
156 | 159 [[NSNotificationCenter defaultCenter] |
157 if (!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()) { | 160 addObserver:self |
158 [[NSNotificationCenter defaultCenter] | 161 selector:@selector(handleLowMemory) |
159 addObserver:self | 162 name:UIApplicationDidReceiveMemoryWarningNotification |
160 selector:@selector(handleLowMemory) | 163 object:nil]; |
161 name:UIApplicationDidReceiveMemoryWarningNotification | 164 [[NSNotificationCenter defaultCenter] |
162 object:nil]; | 165 addObserver:self |
163 [[NSNotificationCenter defaultCenter] | 166 selector:@selector(handleEnterBackground) |
164 addObserver:self | 167 name:UIApplicationDidEnterBackgroundNotification |
165 selector:@selector(handleEnterBackground) | 168 object:nil]; |
166 name:UIApplicationDidEnterBackgroundNotification | 169 [[NSNotificationCenter defaultCenter] |
167 object:nil]; | 170 addObserver:self |
168 [[NSNotificationCenter defaultCenter] | 171 selector:@selector(handleBecomeActive) |
169 addObserver:self | 172 name:UIApplicationDidBecomeActiveNotification |
170 selector:@selector(handleBecomeActive) | 173 object:nil]; |
171 name:UIApplicationDidBecomeActiveNotification | |
172 object:nil]; | |
173 } | |
174 } | 174 } |
175 return self; | 175 return self; |
176 } | 176 } |
177 | 177 |
178 - (void)dealloc { | 178 - (void)dealloc { |
179 if (!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()) { | 179 [[NSNotificationCenter defaultCenter] |
180 [[NSNotificationCenter defaultCenter] | 180 removeObserver:self |
181 removeObserver:self | 181 name:UIApplicationDidReceiveMemoryWarningNotification |
182 name:UIApplicationDidReceiveMemoryWarningNotification | 182 object:nil]; |
183 object:nil]; | 183 [[NSNotificationCenter defaultCenter] |
184 [[NSNotificationCenter defaultCenter] | 184 removeObserver:self |
185 removeObserver:self | 185 name:UIApplicationDidEnterBackgroundNotification |
186 name:UIApplicationDidEnterBackgroundNotification | 186 object:nil]; |
187 object:nil]; | 187 [[NSNotificationCenter defaultCenter] |
188 [[NSNotificationCenter defaultCenter] | 188 removeObserver:self |
189 removeObserver:self | 189 name:UIApplicationDidBecomeActiveNotification |
190 name:UIApplicationDidBecomeActiveNotification | 190 object:nil]; |
191 object:nil]; | |
192 } | |
193 [super dealloc]; | 191 [super dealloc]; |
194 } | 192 } |
195 | 193 |
196 + (CGFloat)snapshotScaleForDevice { | 194 + (CGFloat)snapshotScaleForDevice { |
197 // On handset, the color snapshot is used for the stack view, so the scale of | 195 // On handset, the color snapshot is used for the stack view, so the scale of |
198 // the snapshot images should match the scale of the device. | 196 // the snapshot images should match the scale of the device. |
199 // On tablet, the color snapshot is only used to generate the grey snapshot, | 197 // On tablet, the color snapshot is only used to generate the grey snapshot, |
200 // which does not have to be high quality, so use scale of 1.0 on all tablets. | 198 // which does not have to be high quality, so use scale of 1.0 on all tablets. |
201 if (IsIPadIdiom()) { | 199 if (IsIPadIdiom()) { |
202 return 1.0; | 200 return 1.0; |
203 } | 201 } |
204 // Cap snapshot resolution to 2x to reduce the amount of memory they use. | 202 // Cap snapshot resolution to 2x to reduce the amount of memory they use. |
205 return MIN([UIScreen mainScreen].scale, 2.0); | 203 return MIN([UIScreen mainScreen].scale, 2.0); |
206 } | 204 } |
207 | 205 |
208 - (void)retrieveImageForSessionID:(NSString*)sessionID | 206 - (void)retrieveImageForSessionID:(NSString*)sessionID |
209 callback:(void (^)(UIImage*))callback { | 207 callback:(void (^)(UIImage*))callback { |
210 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 208 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
211 DCHECK(sessionID); | 209 DCHECK(sessionID); |
212 | 210 |
213 // Cache on iPad is enabled only when the tab switcher is enabled. | 211 if (![self inMemoryCacheIsEnabled] && !callback) |
214 if ((IsIPadIdiom() && !experimental_flags::IsTabSwitcherEnabled()) && | |
215 !callback) | |
216 return; | 212 return; |
217 | 213 |
218 UIImage* img = nil; | 214 UIImage* img = nil; |
219 if (lruCache_) | 215 if (lruCache_) |
220 img = [lruCache_ objectForKey:sessionID]; | 216 img = [lruCache_ objectForKey:sessionID]; |
221 else | 217 else |
222 img = [imageDictionary_ objectForKey:sessionID]; | 218 img = [imageDictionary_ objectForKey:sessionID]; |
223 | 219 |
224 if (img) { | 220 if (img) { |
225 if (callback) | 221 if (callback) |
226 callback(img); | 222 callback(img); |
227 return; | 223 return; |
228 } | 224 } |
229 | 225 |
230 base::PostTaskAndReplyWithResult( | 226 base::PostTaskAndReplyWithResult( |
231 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING) | 227 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE_USER_BLOCKING) |
232 .get(), | 228 .get(), |
233 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() { | 229 FROM_HERE, base::BindBlock(^base::scoped_nsobject<UIImage>() { |
234 // Retrieve the image on a high priority thread. | 230 // Retrieve the image on a high priority thread. |
235 return base::scoped_nsobject<UIImage>([ReadImageFromDisk( | 231 return base::scoped_nsobject<UIImage>([ReadImageFromDisk( |
236 [SnapshotCache imagePathForSessionID:sessionID]) retain]); | 232 [SnapshotCache imagePathForSessionID:sessionID]) retain]); |
237 }), | 233 }), |
238 base::BindBlock(^(base::scoped_nsobject<UIImage> image) { | 234 base::BindBlock(^(base::scoped_nsobject<UIImage> image) { |
239 // Cache on iPad is enabled only when the tab switcher is enabled. | 235 if ([self inMemoryCacheIsEnabled] && image) { |
240 if ((!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()) && | |
241 image) { | |
242 if (lruCache_) | 236 if (lruCache_) |
243 [lruCache_ setObject:image forKey:sessionID]; | 237 [lruCache_ setObject:image forKey:sessionID]; |
244 else | 238 else |
245 [imageDictionary_ setObject:image forKey:sessionID]; | 239 [imageDictionary_ setObject:image forKey:sessionID]; |
246 } | 240 } |
247 if (callback) | 241 if (callback) |
248 callback(image); | 242 callback(image); |
249 })); | 243 })); |
250 } | 244 } |
251 | 245 |
252 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID { | 246 - (void)setImage:(UIImage*)img withSessionID:(NSString*)sessionID { |
253 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 247 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
254 if (!img || !sessionID) | 248 if (!img || !sessionID) |
255 return; | 249 return; |
256 | 250 |
257 // Cache on iPad is enabled only when the tab switcher is enabled. | 251 if ([self inMemoryCacheIsEnabled]) { |
258 if (!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()) { | |
259 if (lruCache_) | 252 if (lruCache_) |
260 [lruCache_ setObject:img forKey:sessionID]; | 253 [lruCache_ setObject:img forKey:sessionID]; |
261 else | 254 else |
262 [imageDictionary_ setObject:img forKey:sessionID]; | 255 [imageDictionary_ setObject:img forKey:sessionID]; |
263 } | 256 } |
264 // Save the image to disk. | 257 // Save the image to disk. |
265 web::WebThread::PostBlockingPoolSequencedTask( | 258 web::WebThread::PostBlockingPoolSequencedTask( |
266 kSequenceToken, FROM_HERE, | 259 kSequenceToken, FROM_HERE, |
267 base::BindBlock(^{ | 260 base::BindBlock(^{ |
268 base::scoped_nsobject<UIImage> image([img retain]); | 261 base::scoped_nsobject<UIImage> image([img retain]); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
379 return; | 372 return; |
380 backgroundingImageSessionId_.reset([sessionID copy]); | 373 backgroundingImageSessionId_.reset([sessionID copy]); |
381 if (lruCache_) { | 374 if (lruCache_) { |
382 backgroundingColorImage_.reset([[lruCache_ objectForKey:sessionID] retain]); | 375 backgroundingColorImage_.reset([[lruCache_ objectForKey:sessionID] retain]); |
383 } else { | 376 } else { |
384 backgroundingColorImage_.reset( | 377 backgroundingColorImage_.reset( |
385 [[imageDictionary_ objectForKey:sessionID] retain]); | 378 [[imageDictionary_ objectForKey:sessionID] retain]); |
386 } | 379 } |
387 } | 380 } |
388 | 381 |
| 382 - (BOOL)inMemoryCacheIsEnabled { |
| 383 // In-memory cache on iPad is enabled only when the tab switcher is enabled. |
| 384 return !IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled(); |
| 385 } |
| 386 |
| 387 - (BOOL)useLRUCache { |
| 388 // Always use the LRUCache when the tab switcher is enabled. |
| 389 return experimental_flags::IsTabSwitcherEnabled() || |
| 390 experimental_flags::IsLRUSnapshotCacheEnabled(); |
| 391 } |
| 392 |
389 - (void)handleLowMemory { | 393 - (void)handleLowMemory { |
390 DCHECK(!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()); | |
391 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 394 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
392 NSMutableDictionary* dictionary = | 395 NSMutableDictionary* dictionary = |
393 [[NSMutableDictionary alloc] initWithCapacity:2]; | 396 [[NSMutableDictionary alloc] initWithCapacity:2]; |
394 for (NSString* sessionID in pinnedIDs_) { | 397 for (NSString* sessionID in pinnedIDs_) { |
395 UIImage* image = nil; | 398 UIImage* image = nil; |
396 if (lruCache_) | 399 if (lruCache_) |
397 image = [lruCache_ objectForKey:sessionID]; | 400 image = [lruCache_ objectForKey:sessionID]; |
398 else | 401 else |
399 image = [imageDictionary_ objectForKey:sessionID]; | 402 image = [imageDictionary_ objectForKey:sessionID]; |
400 if (image) | 403 if (image) |
401 [dictionary setObject:image forKey:sessionID]; | 404 [dictionary setObject:image forKey:sessionID]; |
402 } | 405 } |
403 if (lruCache_) { | 406 if (lruCache_) { |
404 [lruCache_ removeAllObjects]; | 407 [lruCache_ removeAllObjects]; |
405 for (NSString* sessionID in pinnedIDs_) | 408 for (NSString* sessionID in pinnedIDs_) |
406 [lruCache_ setObject:dictionary[sessionID] forKey:sessionID]; | 409 [lruCache_ setObject:dictionary[sessionID] forKey:sessionID]; |
407 } else { | 410 } else { |
408 imageDictionary_.reset(dictionary); | 411 imageDictionary_.reset(dictionary); |
409 } | 412 } |
410 } | 413 } |
411 | 414 |
412 - (void)handleEnterBackground { | 415 - (void)handleEnterBackground { |
413 DCHECK(!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()); | |
414 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 416 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
415 [imageDictionary_ removeAllObjects]; | 417 [imageDictionary_ removeAllObjects]; |
416 [lruCache_ removeAllObjects]; | 418 [lruCache_ removeAllObjects]; |
417 } | 419 } |
418 | 420 |
419 - (void)handleBecomeActive { | 421 - (void)handleBecomeActive { |
420 DCHECK(!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()); | |
421 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 422 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
422 for (NSString* sessionID in pinnedIDs_) | 423 for (NSString* sessionID in pinnedIDs_) |
423 [self retrieveImageForSessionID:sessionID callback:nil]; | 424 [self retrieveImageForSessionID:sessionID callback:nil]; |
424 } | 425 } |
425 | 426 |
426 - (void)saveGreyImage:(UIImage*)greyImage forKey:(NSString*)sessionID { | 427 - (void)saveGreyImage:(UIImage*)greyImage forKey:(NSString*)sessionID { |
427 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 428 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
428 if (greyImage) | 429 if (greyImage) |
429 [greyImageDictionary_ setObject:greyImage forKey:sessionID]; | 430 [greyImageDictionary_ setObject:greyImage forKey:sessionID]; |
430 if ([sessionID isEqualToString:mostRecentGreySessionId_]) { | 431 if ([sessionID isEqualToString:mostRecentGreySessionId_]) { |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
561 web::WebThread::PostBlockingPoolTask( | 562 web::WebThread::PostBlockingPoolTask( |
562 FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath, | 563 FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath, |
563 greyImagePath, backgroundingColorImage_)); | 564 greyImagePath, backgroundingColorImage_)); |
564 } | 565 } |
565 | 566 |
566 @end | 567 @end |
567 | 568 |
568 @implementation SnapshotCache (TestingAdditions) | 569 @implementation SnapshotCache (TestingAdditions) |
569 | 570 |
570 - (BOOL)hasImageInMemory:(NSString*)sessionID { | 571 - (BOOL)hasImageInMemory:(NSString*)sessionID { |
571 if (experimental_flags::IsLRUSnapshotCacheEnabled()) | 572 if ([self useLRUCache]) |
572 return [lruCache_ objectForKey:sessionID] != nil; | 573 return [lruCache_ objectForKey:sessionID] != nil; |
573 else | 574 else |
574 return [imageDictionary_ objectForKey:sessionID] != nil; | 575 return [imageDictionary_ objectForKey:sessionID] != nil; |
575 } | 576 } |
| 577 |
576 - (BOOL)hasGreyImageInMemory:(NSString*)sessionID { | 578 - (BOOL)hasGreyImageInMemory:(NSString*)sessionID { |
577 return [greyImageDictionary_ objectForKey:sessionID] != nil; | 579 return [greyImageDictionary_ objectForKey:sessionID] != nil; |
578 } | 580 } |
579 | 581 |
580 - (NSUInteger)lruCacheMaxSize { | 582 - (NSUInteger)lruCacheMaxSize { |
581 return [lruCache_ maxCacheSize]; | 583 return [lruCache_ maxCacheSize]; |
582 } | 584 } |
583 | 585 |
584 @end | 586 @end |
OLD | NEW |