Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(267)

Side by Side Diff: ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.mm

Issue 2588733002: Upstream Chrome on iOS source code [9/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.h"
6
7 #include <unordered_map>
8
9 #import "base/ios/weak_nsobject.h"
10 #include "base/logging.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/synchronization/lock.h"
14 #import "ios/chrome/browser/tabs/tab.h"
15 #import "ios/chrome/browser/tabs/tab_model.h"
16 #import "ios/chrome/browser/ui/uikit_ui_util.h"
17 #include "ios/chrome/common/ios_app_bundle_id_prefix.h"
18 #include "ios/web/public/navigation_item.h"
19
20 namespace {
21 // The maximum amount of pixels the cache should hold.
22 NSUInteger kCacheMaxPixelCount = 2048 * 1536 * 4;
23 // Two floats that are different from less than |kMaxFloatDelta| are considered
24 // equals.
25 const CGFloat kMaxFloatDelta = 0.01;
26 } // namespace
27
28 @interface TabSwitcherCache ()
29 // Clears the cache. Called when a low memory warning was received.
30 - (void)lowMemoryWarningReceived;
31 // Returns a autoreleased resized image of |image|.
32 + (UIImage*)resizedImage:(UIImage*)image toSize:(CGSize)size;
33
34 @end
35
36 @implementation TabSwitcherCache {
37 base::scoped_nsobject<NSCache> _cache;
38 dispatch_queue_t _cacheQueue;
39 // The tab models.
40 TabModel* _mainTabModel; // weak
41 TabModel* _otrTabModel; // weak
42
43 // Lock protecting the pending requests map.
44 base::Lock _lock;
45 std::unordered_map<NSUInteger, PendingSnapshotRequest> _pendingRequests;
46 }
47
48 @synthesize mainTabModel = _mainTabModel;
49
50 - (instancetype)init {
51 self = [super init];
52 if (self) {
53 _cache.reset([[NSCache alloc] init]);
54 [_cache setTotalCostLimit:kCacheMaxPixelCount];
55 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
56 [nc addObserver:self
57 selector:@selector(lowMemoryWarningReceived)
58 name:UIApplicationDidReceiveMemoryWarningNotification
59 object:nil];
60 std::string queueName =
61 base::StringPrintf("%s.chrome.ios.TabSwitcherCacheQueue",
62 BUILDFLAG(IOS_APP_BUNDLE_ID_PREFIX));
63 _cacheQueue =
64 dispatch_queue_create(queueName.c_str(), DISPATCH_QUEUE_SERIAL);
65 }
66 return self;
67 }
68
69 - (void)dealloc {
70 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
71 [nc removeObserver:self
72 name:UIApplicationDidReceiveMemoryWarningNotification
73 object:nil];
74 dispatch_release(_cacheQueue);
75 [_mainTabModel removeObserver:self];
76 [_otrTabModel removeObserver:self];
77 [super dealloc];
78 }
79
80 - (PendingSnapshotRequest)requestSnapshotForTab:(Tab*)tab
81 withSize:(CGSize)size
82 completionBlock:
83 (SnapshotCompletionBlock)completionBlock {
84 DCHECK([NSThread isMainThread]);
85 DCHECK(tab);
86 DCHECK(completionBlock);
87 DCHECK(!CGSizeEqualToSize(size, CGSizeZero));
88 PendingSnapshotRequest currentRequest;
89 UIImage* snapshot = [_cache objectForKey:[self keyForTab:tab]];
90 if (snapshot) {
91 CGFloat tabContentAreaRatio = tab.snapshotContentArea.size.width /
92 tab.snapshotContentArea.size.height;
93 CGFloat cachedSnapshotRatio =
94 [snapshot size].width / [snapshot size].height;
95 // Check that the cached snapshot's ratio matches the content area ratio.
96 if (std::abs(tabContentAreaRatio - cachedSnapshotRatio) < kMaxFloatDelta &&
97 [snapshot size].width >= size.width) {
98 // Cache hit.
99 completionBlock(snapshot);
100 return currentRequest;
101 }
102 }
103
104 // Cache miss.
105 currentRequest = [self recordPendingRequestForTab:tab];
106 NSString* key = [self keyForTab:tab];
107 [tab retrieveSnapshot:^(UIImage* snapshot) {
108 PendingSnapshotRequest requestForSession = [self pendingRequestForTab:tab];
109 // Cancel this request if another one has replaced it for this sessionId.
110 if (currentRequest.requestId != requestForSession.requestId)
111 return;
112 dispatch_async(_cacheQueue, ^{
113 DCHECK(![NSThread isMainThread]);
114 UIImage* resizedSnapshot =
115 [TabSwitcherCache resizedImage:snapshot toSize:size];
116 if ([self storeImage:resizedSnapshot forKey:key request:currentRequest]) {
117 dispatch_async(dispatch_get_main_queue(), ^{
118 // Cancel this request if another one has replaced it for this
119 // sessionId.
120 PendingSnapshotRequest requestForSession =
121 [self pendingRequestForTab:tab];
122 if (currentRequest.requestId != requestForSession.requestId)
123 return;
124 completionBlock(resizedSnapshot);
125 [self removePendingSnapshotRequest:currentRequest];
126 });
127 }
128 });
129 }];
130 return currentRequest;
131 }
132
133 - (void)updateSnapshotForTab:(Tab*)tab
134 withImage:(UIImage*)image
135 size:(CGSize)size {
136 DCHECK([NSThread isMainThread]);
137 DCHECK(tab);
138 DCHECK(image);
139 PendingSnapshotRequest currentRequest = [self recordPendingRequestForTab:tab];
140 NSString* key = [self keyForTab:tab];
141
142 dispatch_async(_cacheQueue, ^{
143 DCHECK(![NSThread isMainThread]);
144 UIImage* resizedSnapshot =
145 [TabSwitcherCache resizedImage:image toSize:size];
146 [self storeImage:resizedSnapshot forKey:key request:currentRequest];
147 [self removePendingSnapshotRequest:currentRequest];
148 });
149 }
150
151 - (void)cancelPendingSnapshotRequest:(PendingSnapshotRequest)pendingRequest {
152 [self removePendingSnapshotRequest:pendingRequest];
153 }
154
155 #pragma mark - Private
156
157 - (NSString*)keyForTab:(Tab*)tab {
158 DCHECK([NSThread isMainThread]);
159 return [tab currentSessionID];
160 }
161
162 - (PendingSnapshotRequest)recordPendingRequestForTab:(Tab*)tab {
163 PendingSnapshotRequest pendingRequest;
164 pendingRequest.requestId = [[NSDate date] timeIntervalSince1970];
165 pendingRequest.sessionId = [[self keyForTab:tab] hash];
166 base::AutoLock guard(_lock);
167 _pendingRequests[pendingRequest.sessionId] = pendingRequest;
168 return pendingRequest;
169 }
170
171 - (PendingSnapshotRequest)pendingRequestForTab:(Tab*)tab {
172 DCHECK([NSThread isMainThread]);
173 PendingSnapshotRequest pendingRequest;
174 if (![tab webStateImpl])
175 return pendingRequest;
176 NSUInteger sessionId = [[self keyForTab:tab] hash];
177 base::AutoLock guard(_lock);
178 auto it = _pendingRequests.find(sessionId);
179 if (it != _pendingRequests.end())
180 pendingRequest = it->second;
181 return pendingRequest;
182 }
183
184 - (void)removePendingSnapshotRequest:(PendingSnapshotRequest)pendingRequest {
185 base::AutoLock guard(_lock);
186 auto itRequest = _pendingRequests.find(pendingRequest.sessionId);
187 if (itRequest != _pendingRequests.end() &&
188 pendingRequest.requestId == itRequest->second.requestId) {
189 _pendingRequests.erase(itRequest);
190 }
191 }
192
193 - (void)removePendingSnapshotRequestForTab:(Tab*)tab {
194 base::AutoLock guard(_lock);
195 auto itRequest = _pendingRequests.find([[self keyForTab:tab] hash]);
196 if (itRequest != _pendingRequests.end())
197 _pendingRequests.erase(itRequest);
198 }
199
200 - (BOOL)storeImage:(UIImage*)image
201 forKey:(NSString*)key
202 request:(PendingSnapshotRequest)request {
203 DCHECK(request.requestId != 0);
204 if (!image)
205 return NO;
206
207 {
208 base::AutoLock guard(_lock);
209 auto it = _pendingRequests.find(request.sessionId);
210 if (it == _pendingRequests.end())
211 return NO;
212
213 // Only write the image in cache if the request is still valid.
214 if (request.requestId != it->second.requestId)
215 return NO;
216 }
217
218 const CGFloat screenScale = [[UIScreen mainScreen] scale];
219 const NSUInteger cost =
220 image.size.width * screenScale * image.size.height * screenScale;
221 [_cache setObject:image forKey:key cost:cost];
222 return YES;
223 }
224
225 + (UIImage*)resizedImage:(UIImage*)image toSize:(CGSize)size {
226 DCHECK(image.scale == 1);
227 CGFloat screenScale = [[UIScreen mainScreen] scale];
228 CGSize pixelSize = size;
229 pixelSize.width *= screenScale;
230 pixelSize.height *= screenScale;
231 UIImage* resizedSnapshot =
232 ResizeImage(image, pixelSize, ProjectionMode::kAspectFillNoClipping, YES);
233 // Creates a new image with the correct |scale| attribute.
234 return [[[UIImage alloc] initWithCGImage:resizedSnapshot.CGImage
235 scale:screenScale
236 orientation:UIImageOrientationUp] autorelease];
237 }
238
239 - (void)lowMemoryWarningReceived {
240 [_cache removeAllObjects];
241 }
242
243 - (void)setMainTabModel:(TabModel*)mainTabModel
244 otrTabModel:(TabModel*)otrTabModel {
245 if (mainTabModel != _mainTabModel)
246 [self replaceOldTabModel:&_mainTabModel withTabModel:mainTabModel];
247 if (otrTabModel != _otrTabModel)
248 [self replaceOldTabModel:&_otrTabModel withTabModel:otrTabModel];
249 }
250
251 - (void)replaceOldTabModel:(TabModel**)oldTabModel
252 withTabModel:(TabModel*)newTabModel {
253 [*oldTabModel removeObserver:self];
254 *oldTabModel = newTabModel;
255 [newTabModel addObserver:self];
256 }
257
258 #pragma mark - TabModelObserver
259
260 - (void)tabModel:(TabModel*)model
261 didRemoveTab:(Tab*)tab
262 atIndex:(NSUInteger)index {
263 [self removePendingSnapshotRequestForTab:tab];
264 [_cache removeObjectForKey:[self keyForTab:tab]];
265 }
266
267 - (void)tabModel:(TabModel*)model
268 didChangeTabSnapshot:(Tab*)tab
269 withImage:image {
270 [self removePendingSnapshotRequestForTab:tab];
271 [_cache removeObjectForKey:[self keyForTab:tab]];
272 }
273
274 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.h ('k') | ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698