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

Side by Side Diff: ios/chrome/browser/sessions/session_service_ios.mm

Issue 2802763002: [ios] Cleanup SessionServiceIOS implementation. (Closed)
Patch Set: Address comments. Created 3 years, 8 months 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
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/sessions/session_service.h" 5 #import "ios/chrome/browser/sessions/session_service_ios.h"
6 6
7 #import <UIKit/UIKit.h> 7 #import <UIKit/UIKit.h>
8 8
9 #include "base/critical_closure.h"
9 #include "base/files/file_path.h" 10 #include "base/files/file_path.h"
10 #include "base/location.h" 11 #include "base/location.h"
11 #include "base/logging.h" 12 #include "base/logging.h"
12 #include "base/mac/bind_objc_block.h" 13 #import "base/mac/bind_objc_block.h"
13 #include "base/mac/foundation_util.h"
14 #include "base/memory/ref_counted.h" 14 #include "base/memory/ref_counted.h"
15 #include "base/sequenced_task_runner.h" 15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/sys_string_conversions.h" 16 #include "base/strings/sys_string_conversions.h"
17 #include "base/synchronization/lock.h"
18 #include "base/threading/sequenced_worker_pool.h" 17 #include "base/threading/sequenced_worker_pool.h"
19 #include "base/threading/thread_restrictions.h" 18 #include "base/threading/thread_restrictions.h"
20 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" 19 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
21 #import "ios/chrome/browser/sessions/session_window_ios.h" 20 #import "ios/chrome/browser/sessions/session_window_ios.h"
22 #import "ios/web/public/crw_navigation_item_storage.h" 21 #import "ios/web/public/crw_navigation_item_storage.h"
23 #import "ios/web/public/crw_session_certificate_policy_cache_storage.h" 22 #import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
24 #import "ios/web/public/crw_session_storage.h" 23 #import "ios/web/public/crw_session_storage.h"
25 #include "ios/web/public/web_thread.h" 24 #include "ios/web/public/web_thread.h"
26 25
26 #if !defined(__has_feature) || !__has_feature(objc_arc)
27 #error "This file requires ARC support."
28 #endif
29
27 // When C++ exceptions are disabled, the C++ library defines |try| and 30 // When C++ exceptions are disabled, the C++ library defines |try| and
28 // |catch| so as to allow exception-expecting C++ code to build properly when 31 // |catch| so as to allow exception-expecting C++ code to build properly when
29 // language support for exceptions is not present. These macros interfere 32 // language support for exceptions is not present. These macros interfere
30 // with the use of |@try| and |@catch| in Objective-C files such as this one. 33 // with the use of |@try| and |@catch| in Objective-C files such as this one.
31 // Undefine these macros here, after everything has been #included, since 34 // Undefine these macros here, after everything has been #included, since
32 // there will be no C++ uses and only Objective-C uses from this point on. 35 // there will be no C++ uses and only Objective-C uses from this point on.
33 #undef try 36 #undef try
34 #undef catch 37 #undef catch
35 38
36 const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome. 39 namespace {
37 40 const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
38 @interface SessionWindowUnarchiver () 41 NSString* const kRootObjectKey = @"root"; // Key for the root object.
39
40 // Register compatibility aliases to support loading serialised sessions
41 // informations when the serialised classes are renamed.
42 + (void)registerCompatibilityAliases;
43
44 @end
45
46 @implementation SessionWindowUnarchiver
47
48 @synthesize browserState = _browserState;
49
50 - (instancetype)initForReadingWithData:(NSData*)data
51 browserState:(ios::ChromeBrowserState*)browserState {
52 if (self = [super initForReadingWithData:data]) {
53 _browserState = browserState;
54 }
55 return self;
56 } 42 }
57 43
58 - (instancetype)initForReadingWithData:(NSData*)data { 44 @implementation NSKeyedUnarchiver (CrLegacySessionCompatibility)
59 return [self initForReadingWithData:data browserState:nullptr];
60 }
61
62 + (void)initialize {
63 [super initialize];
64 [self registerCompatibilityAliases];
65 }
66 45
67 // When adding a new compatibility alias here, create a new crbug to track its 46 // When adding a new compatibility alias here, create a new crbug to track its
68 // removal and mark it with a release at least one year after the introduction 47 // removal and mark it with a release at least one year after the introduction
69 // of the alias. 48 // of the alias.
70 + (void)registerCompatibilityAliases { 49 - (void)cr_registerCompatibilityAliases {
71 // TODO(crbug.com/661633): those aliases where introduced between M57 and 50 // TODO(crbug.com/661633): those aliases where introduced between M57 and
72 // M58, so remove them after M67 has shipped to stable. 51 // M58, so remove them after M67 has shipped to stable.
73 [SessionWindowUnarchiver 52 [self setClass:[CRWSessionCertificatePolicyCacheStorage class]
74 setClass:[CRWSessionCertificatePolicyCacheStorage class]
75 forClassName:@"SessionCertificatePolicyManager"]; 53 forClassName:@"SessionCertificatePolicyManager"];
76 [SessionWindowUnarchiver setClass:[CRWSessionStorage class] 54 [self setClass:[CRWSessionStorage class] forClassName:@"SessionController"];
77 forClassName:@"SessionController"]; 55 [self setClass:[CRWSessionStorage class]
78 [SessionWindowUnarchiver setClass:[CRWSessionStorage class] 56 forClassName:@"CRWSessionController"];
79 forClassName:@"CRWSessionController"]; 57 [self setClass:[CRWNavigationItemStorage class] forClassName:@"SessionEntry"];
80 [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class] 58 [self setClass:[CRWNavigationItemStorage class]
81 forClassName:@"SessionEntry"]; 59 forClassName:@"CRWSessionEntry"];
82 [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class] 60 [self setClass:[SessionWindowIOS class] forClassName:@"SessionWindow"];
83 forClassName:@"CRWSessionEntry"];
84 [SessionWindowUnarchiver setClass:[SessionWindowIOS class]
85 forClassName:@"SessionWindow"];
86 61
87 // TODO(crbug.com/661633): this alias was introduced between M58 and M59, so 62 // TODO(crbug.com/661633): this alias was introduced between M58 and M59, so
88 // remove it after M68 has shipped to stable. 63 // remove it after M68 has shipped to stable.
89 [SessionWindowUnarchiver setClass:[CRWSessionStorage class] 64 [self setClass:[CRWSessionStorage class]
90 forClassName:@"CRWNavigationManagerStorage"]; 65 forClassName:@"CRWNavigationManagerStorage"];
91 [SessionWindowUnarchiver 66 [self setClass:[CRWSessionCertificatePolicyCacheStorage class]
92 setClass:[CRWSessionCertificatePolicyCacheStorage class]
93 forClassName:@"CRWSessionCertificatePolicyManager"]; 67 forClassName:@"CRWSessionCertificatePolicyManager"];
94 } 68 }
95 69
96 @end 70 @end
97 71
98 @interface SessionServiceIOS () { 72 @interface SessionServiceIOS () {
99 // The SequencedTaskRunner on which File IO operations are performed. 73 // The SequencedTaskRunner on which File IO operations are performed.
100 scoped_refptr<base::SequencedTaskRunner> _taskRunner; 74 scoped_refptr<base::SequencedTaskRunner> _taskRunner;
101 75
102 // Maps save directories to the pending SessionWindow for the delayed 76 // Maps save directories to the pending SessionWindow for the delayed
103 // save behavior. 77 // save behavior.
104 base::scoped_nsobject<NSMutableDictionary> _pendingWindows; 78 NSMutableDictionary<NSString*, SessionWindowIOS*>* _pendingWindows;
105 } 79 }
106 80
107 // Saves the session corresponding to |directory| on the background 81 // Saves the session corresponding to |directory| on the background
108 // task runner |_taskRunner|. 82 // task runner |_taskRunner|.
109 - (void)performSaveToDirectoryInBackground:(NSString*)directory; 83 - (void)performSaveToDirectoryInBackground:(NSString*)directory;
110 @end 84 @end
111 85
112 @implementation SessionServiceIOS 86 @implementation SessionServiceIOS
113 87
114 + (SessionServiceIOS*)sharedService { 88 + (SessionServiceIOS*)sharedService {
115 static SessionServiceIOS* singleton = nil; 89 static SessionServiceIOS* singleton = nil;
116 if (!singleton) { 90 if (!singleton) {
117 singleton = [[[self class] alloc] init]; 91 singleton = [[[self class] alloc] init];
118 } 92 }
119 return singleton; 93 return singleton;
120 } 94 }
121 95
122 - (instancetype)init { 96 - (instancetype)init {
123 self = [super init]; 97 self = [super init];
124 if (self) { 98 if (self) {
125 _pendingWindows.reset([[NSMutableDictionary alloc] init]); 99 _pendingWindows = [NSMutableDictionary dictionary];
126 auto* pool = web::WebThread::GetBlockingPool(); 100 base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool();
127 _taskRunner = pool->GetSequencedTaskRunner(pool->GetSequenceToken()); 101 _taskRunner = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
128 } 102 }
129 return self; 103 return self;
130 } 104 }
131 105
132 // Returns the path of the session file. 106 // Returns the path of the session file.
133 - (NSString*)sessionFilePathForDirectory:(NSString*)directory { 107 - (NSString*)sessionFilePathForDirectory:(NSString*)directory {
134 return [directory stringByAppendingPathComponent:@"session.plist"]; 108 return [directory stringByAppendingPathComponent:@"session.plist"];
135 } 109 }
136 110
137 // Do the work of saving on a background thread. Assumes |window| is threadsafe. 111 // Do the work of saving on a background thread. Assumes |window| is threadsafe.
138 - (void)performSaveToDirectoryInBackground:(NSString*)directory { 112 - (void)performSaveToDirectoryInBackground:(NSString*)directory {
139 DCHECK(directory); 113 DCHECK(directory);
140 DCHECK([_pendingWindows objectForKey:directory] != nil); 114 DCHECK([_pendingWindows objectForKey:directory] != nil);
141 UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication]
142 beginBackgroundTaskWithExpirationHandler:^{
143 }];
144 DCHECK(identifier != UIBackgroundTaskInvalid);
145 115
146 // Put the window into a local var so it can be retained for the block, yet 116 // Put the window into a local var so it can be retained for the block, yet
147 // we can remove it from the dictionary to allow queuing another save. 117 // we can remove it from the dictionary to allow queuing another save.
148 SessionWindowIOS* localWindow = 118 SessionWindowIOS* localWindow = [_pendingWindows objectForKey:directory];
149 [[_pendingWindows objectForKey:directory] retain];
150 [_pendingWindows removeObjectForKey:directory]; 119 [_pendingWindows removeObjectForKey:directory];
151 120
152 _taskRunner->PostTask( 121 _taskRunner->PostTask(
153 FROM_HERE, base::BindBlock(^{ 122 FROM_HERE, base::MakeCriticalClosure(base::BindBlockArc(^{
154 @try { 123 @try {
155 [self performSaveWindow:localWindow toDirectory:directory]; 124 [self performSaveWindow:localWindow toDirectory:directory];
156 } @catch (NSException* e) { 125 } @catch (NSException* e) {
157 // Do nothing. 126 // Do nothing.
158 } 127 }
159 [localWindow release]; 128 })));
160 [[UIApplication sharedApplication] endBackgroundTask:identifier];
161 }));
162 } 129 }
163 130
164 // Saves a SessionWindowIOS in a given directory. In case the directory doesn't 131 // Saves a SessionWindowIOS in a given directory. In case the directory doesn't
165 // exists it will be automatically created. 132 // exists it will be automatically created.
166 - (void)performSaveWindow:(SessionWindowIOS*)window 133 - (void)performSaveWindow:(SessionWindowIOS*)window
167 toDirectory:(NSString*)directory { 134 toDirectory:(NSString*)directory {
168 base::ThreadRestrictions::AssertIOAllowed(); 135 base::ThreadRestrictions::AssertIOAllowed();
169 NSFileManager* fileManager = [NSFileManager defaultManager]; 136 NSFileManager* fileManager = [NSFileManager defaultManager];
170 BOOL isDir; 137 BOOL isDir;
171 if (![fileManager fileExistsAtPath:directory isDirectory:&isDir]) { 138 if (![fileManager fileExistsAtPath:directory isDirectory:&isDir]) {
172 NSError* error = nil; 139 NSError* error = nil;
173 BOOL result = [fileManager createDirectoryAtPath:directory 140 BOOL result = [fileManager createDirectoryAtPath:directory
174 withIntermediateDirectories:YES 141 withIntermediateDirectories:YES
175 attributes:nil 142 attributes:nil
176 error:&error]; 143 error:&error];
177 DCHECK(result); 144 DCHECK(result);
178 if (!result) { 145 if (!result) {
179 DLOG(ERROR) << "Error creating destination dir: " 146 DLOG(ERROR) << "Error creating destination directory: "
147 << base::SysNSStringToUTF8(directory) << ": "
180 << base::SysNSStringToUTF8([error description]); 148 << base::SysNSStringToUTF8([error description]);
181 return; 149 return;
182 } 150 }
183 } else { 151 } else {
184 DCHECK(isDir); 152 DCHECK(isDir);
185 if (!isDir) { 153 if (!isDir) {
186 DLOG(ERROR) << "Destination Directory already exists and is a file"; 154 DLOG(ERROR) << "Error creating destination directory: "
155 << base::SysNSStringToUTF8(directory) << ": "
156 << "file exists and is not a directory.";
187 return; 157 return;
188 } 158 }
189 } 159 }
190 160
191 NSString* filename = [self sessionFilePathForDirectory:directory]; 161 NSString* filename = [self sessionFilePathForDirectory:directory];
192 if (filename) { 162 if (filename) {
193 BOOL result = [NSKeyedArchiver archiveRootObject:window toFile:filename]; 163 BOOL result = [NSKeyedArchiver archiveRootObject:window toFile:filename];
194 DCHECK(result); 164 DCHECK(result);
195 if (!result) 165 if (!result) {
196 DLOG(ERROR) << "Error writing session file to " << filename; 166 DLOG(ERROR) << "Error writing session file to " << filename;
167 return;
168 }
169
197 // Encrypt the session file (mostly for Incognito, but can't hurt to 170 // Encrypt the session file (mostly for Incognito, but can't hurt to
198 // always do it). 171 // always do it).
199 NSDictionary* attributeDict =
200 [NSDictionary dictionaryWithObject:NSFileProtectionComplete
201 forKey:NSFileProtectionKey];
202 NSError* error = nil; 172 NSError* error = nil;
203 BOOL success = [[NSFileManager defaultManager] setAttributes:attributeDict 173 BOOL success = [[NSFileManager defaultManager]
204 ofItemAtPath:filename 174 setAttributes:@{NSFileProtectionKey : NSFileProtectionComplete}
205 error:&error]; 175 ofItemAtPath:filename
176 error:&error];
206 if (!success) { 177 if (!success) {
207 DLOG(ERROR) << "Error encrypting session file" 178 DLOG(ERROR) << "Error encrypting session file: "
179 << base::SysNSStringToUTF8(filename) << ": "
208 << base::SysNSStringToUTF8([error description]); 180 << base::SysNSStringToUTF8([error description]);
209 } 181 }
210 } 182 }
211 } 183 }
212 184
213 - (void)saveWindow:(SessionWindowIOS*)window 185 - (void)saveWindow:(SessionWindowIOS*)window
214 forBrowserState:(ios::ChromeBrowserState*)browserState 186 forBrowserState:(ios::ChromeBrowserState*)browserState
215 immediately:(BOOL)immediately { 187 immediately:(BOOL)immediately {
216 NSString* stashPath = 188 NSString* stashPath =
217 base::SysUTF8ToNSString(browserState->GetStatePath().value()); 189 base::SysUTF8ToNSString(browserState->GetStatePath().value());
218 // If there's an existing session window for |stashPath|, clear it before it's 190 BOOL hadPendingSession = [_pendingWindows objectForKey:stashPath] != nil;
219 // replaced.
220 SessionWindowIOS* pendingSession = base::mac::ObjCCast<SessionWindowIOS>(
221 [_pendingWindows objectForKey:stashPath]);
222 [pendingSession clearSessions];
223 // Set |window| as the pending save for |stashPath|.
224 [_pendingWindows setObject:window forKey:stashPath]; 191 [_pendingWindows setObject:window forKey:stashPath];
225 if (immediately) { 192 if (immediately) {
226 [NSObject cancelPreviousPerformRequestsWithTarget:self]; 193 [NSObject cancelPreviousPerformRequestsWithTarget:self];
227 [self performSaveToDirectoryInBackground:stashPath]; 194 [self performSaveToDirectoryInBackground:stashPath];
228 } else if (!pendingSession) { 195 } else if (!hadPendingSession) {
229 // If there wasn't previously a delayed save pending for |stashPath|, 196 // If there wasn't previously a delayed save pending for |stashPath|,
230 // enqueue one now. 197 // enqueue one now.
231 [self performSelector:@selector(performSaveToDirectoryInBackground:) 198 [self performSelector:@selector(performSaveToDirectoryInBackground:)
232 withObject:stashPath 199 withObject:stashPath
233 afterDelay:kSaveDelay]; 200 afterDelay:kSaveDelay];
234 } 201 }
235 } 202 }
236 203
237 - (SessionWindowIOS*)loadWindowForBrowserState: 204 - (SessionWindowIOS*)loadWindowForBrowserState:
238 (ios::ChromeBrowserState*)browserState { 205 (ios::ChromeBrowserState*)browserState {
239 NSString* stashPath = 206 NSString* stashPath =
240 base::SysUTF8ToNSString(browserState->GetStatePath().value()); 207 base::SysUTF8ToNSString(browserState->GetStatePath().value());
241 SessionWindowIOS* window = 208 SessionWindowIOS* window =
242 [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath] 209 [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath]];
243 forBrowserState:browserState];
244 return window; 210 return window;
245 } 211 }
246 212
247 - (SessionWindowIOS*)loadWindowFromPath:(NSString*)path 213 - (SessionWindowIOS*)loadWindowFromPath:(NSString*)sessionPath {
248 forBrowserState:(ios::ChromeBrowserState*)browserState {
249 SessionWindowIOS* window = nil;
250 @try { 214 @try {
251 NSData* data = [NSData dataWithContentsOfFile:path]; 215 NSData* data = [NSData dataWithContentsOfFile:sessionPath];
252 if (data) { 216 if (!data)
253 base::scoped_nsobject<SessionWindowUnarchiver> unarchiver([ 217 return nil;
254 [SessionWindowUnarchiver alloc] initForReadingWithData:data 218
255 browserState:browserState]); 219 NSKeyedUnarchiver* unarchiver =
256 window = [[[unarchiver decodeObjectForKey:@"root"] retain] autorelease]; 220 [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
257 } 221
222 // Register compatibility aliases to support legacy saved sessions.
223 [unarchiver cr_registerCompatibilityAliases];
224 return [unarchiver decodeObjectForKey:kRootObjectKey];
258 } @catch (NSException* exception) { 225 } @catch (NSException* exception) {
259 DLOG(ERROR) << "Error loading session file."; 226 DLOG(ERROR) << "Error loading session file: "
227 << base::SysNSStringToUTF8(sessionPath) << ": "
228 << base::SysNSStringToUTF8([exception reason]);
229 return nil;
260 } 230 }
261 return window;
262 } 231 }
263 232
264 // Deletes the file containing the commands for the last session in the given 233 // Deletes the file containing the commands for the last session in the given
265 // browserState directory. 234 // browserState directory.
266 - (void)deleteLastSession:(NSString*)directory { 235 - (void)deleteLastSession:(NSString*)directory {
267 NSString* sessionFile = [self sessionFilePathForDirectory:directory]; 236 NSString* sessionPath = [self sessionFilePathForDirectory:directory];
268 _taskRunner->PostTask( 237 _taskRunner->PostTask(
269 FROM_HERE, base::BindBlock(^{ 238 FROM_HERE, base::BindBlockArc(^{
270 base::ThreadRestrictions::AssertIOAllowed(); 239 base::ThreadRestrictions::AssertIOAllowed();
271 NSFileManager* fileManager = [NSFileManager defaultManager]; 240 NSFileManager* fileManager = [NSFileManager defaultManager];
272 if (![fileManager fileExistsAtPath:sessionFile]) 241 if (![fileManager fileExistsAtPath:sessionPath])
273 return; 242 return;
274 if (![fileManager removeItemAtPath:sessionFile error:nil]) 243
275 CHECK(false) << "Unable to delete session file."; 244 NSError* error = nil;
245 if (![fileManager removeItemAtPath:sessionPath error:nil])
246 CHECK(false) << "Unable to delete session file: "
247 << base::SysNSStringToUTF8(sessionPath) << ": "
248 << base::SysNSStringToUTF8([error description]);
276 })); 249 }));
277 } 250 }
278 251
279 @end 252 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/sessions/session_service_ios.h ('k') | ios/chrome/browser/sessions/session_service_ios_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698