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