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

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

Issue 2802763002: [ios] Cleanup SessionServiceIOS implementation. (Closed)
Patch Set: 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 const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
37 40
38 @interface SessionWindowUnarchiver () 41 @implementation NSKeyedUnarchiver (CrLegacySessionCompatibility)
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 }
57
58 - (instancetype)initForReadingWithData:(NSData*)data {
59 return [self initForReadingWithData:data browserState:nullptr];
60 }
61
62 + (void)initialize {
63 [super initialize];
64 [self registerCompatibilityAliases];
65 }
66 42
67 // When adding a new compatibility alias here, create a new crbug to track its 43 // 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 44 // removal and mark it with a release at least one year after the introduction
69 // of the alias. 45 // of the alias.
70 + (void)registerCompatibilityAliases { 46 - (void)cr_registerCompatibilityAliases {
71 // TODO(crbug.com/661633): those aliases where introduced between M57 and 47 // TODO(crbug.com/661633): those aliases where introduced between M57 and
72 // M58, so remove them after M67 has shipped to stable. 48 // M58, so remove them after M67 has shipped to stable.
73 [SessionWindowUnarchiver 49 [self setClass:[CRWSessionCertificatePolicyCacheStorage class]
74 setClass:[CRWSessionCertificatePolicyCacheStorage class]
75 forClassName:@"SessionCertificatePolicyManager"]; 50 forClassName:@"SessionCertificatePolicyManager"];
76 [SessionWindowUnarchiver setClass:[CRWSessionStorage class] 51 [self setClass:[CRWSessionStorage class] forClassName:@"SessionController"];
77 forClassName:@"SessionController"]; 52 [self setClass:[CRWSessionStorage class]
78 [SessionWindowUnarchiver setClass:[CRWSessionStorage class] 53 forClassName:@"CRWSessionController"];
79 forClassName:@"CRWSessionController"]; 54 [self setClass:[CRWNavigationItemStorage class] forClassName:@"SessionEntry"];
80 [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class] 55 [self setClass:[CRWNavigationItemStorage class]
81 forClassName:@"SessionEntry"]; 56 forClassName:@"CRWSessionEntry"];
82 [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class] 57 [self setClass:[SessionWindowIOS class] forClassName:@"SessionWindow"];
83 forClassName:@"CRWSessionEntry"];
84 [SessionWindowUnarchiver setClass:[SessionWindowIOS class]
85 forClassName:@"SessionWindow"];
86 58
87 // TODO(crbug.com/661633): this alias was introduced between M58 and M59, so 59 // TODO(crbug.com/661633): this alias was introduced between M58 and M59, so
88 // remove it after M68 has shipped to stable. 60 // remove it after M68 has shipped to stable.
89 [SessionWindowUnarchiver setClass:[CRWSessionStorage class] 61 [self setClass:[CRWSessionStorage class]
90 forClassName:@"CRWNavigationManagerStorage"]; 62 forClassName:@"CRWNavigationManagerStorage"];
91 [SessionWindowUnarchiver 63 [self setClass:[CRWSessionCertificatePolicyCacheStorage class]
92 setClass:[CRWSessionCertificatePolicyCacheStorage class]
93 forClassName:@"CRWSessionCertificatePolicyManager"]; 64 forClassName:@"CRWSessionCertificatePolicyManager"];
94 } 65 }
95 66
96 @end 67 @end
97 68
98 @interface SessionServiceIOS () { 69 @interface SessionServiceIOS () {
99 // The SequencedTaskRunner on which File IO operations are performed. 70 // The SequencedTaskRunner on which File IO operations are performed.
100 scoped_refptr<base::SequencedTaskRunner> _taskRunner; 71 scoped_refptr<base::SequencedTaskRunner> _taskRunner;
101 72
102 // Maps save directories to the pending SessionWindow for the delayed 73 // Maps save directories to the pending SessionWindow for the delayed
103 // save behavior. 74 // save behavior.
104 base::scoped_nsobject<NSMutableDictionary> _pendingWindows; 75 NSMutableDictionary<NSString*, SessionWindowIOS*>* _pendingWindows;
105 } 76 }
106 77
107 // Saves the session corresponding to |directory| on the background 78 // Saves the session corresponding to |directory| on the background
108 // task runner |_taskRunner|. 79 // task runner |_taskRunner|.
109 - (void)performSaveToDirectoryInBackground:(NSString*)directory; 80 - (void)performSaveToDirectoryInBackground:(NSString*)directory;
110 @end 81 @end
111 82
112 @implementation SessionServiceIOS 83 @implementation SessionServiceIOS
113 84
114 + (SessionServiceIOS*)sharedService { 85 + (SessionServiceIOS*)sharedService {
115 static SessionServiceIOS* singleton = nil; 86 static SessionServiceIOS* singleton = nil;
116 if (!singleton) { 87 if (!singleton) {
117 singleton = [[[self class] alloc] init]; 88 singleton = [[[self class] alloc] init];
118 } 89 }
119 return singleton; 90 return singleton;
120 } 91 }
121 92
122 - (instancetype)init { 93 - (instancetype)init {
123 self = [super init]; 94 self = [super init];
124 if (self) { 95 if (self) {
125 _pendingWindows.reset([[NSMutableDictionary alloc] init]); 96 _pendingWindows = [NSMutableDictionary dictionary];
126 auto* pool = web::WebThread::GetBlockingPool(); 97 base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool();
127 _taskRunner = pool->GetSequencedTaskRunner(pool->GetSequenceToken()); 98 _taskRunner = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
128 } 99 }
129 return self; 100 return self;
130 } 101 }
131 102
132 // Returns the path of the session file. 103 // Returns the path of the session file.
133 - (NSString*)sessionFilePathForDirectory:(NSString*)directory { 104 - (NSString*)sessionFilePathForDirectory:(NSString*)directory {
134 return [directory stringByAppendingPathComponent:@"session.plist"]; 105 return [directory stringByAppendingPathComponent:@"session.plist"];
135 } 106 }
136 107
137 // Do the work of saving on a background thread. Assumes |window| is threadsafe. 108 // Do the work of saving on a background thread. Assumes |window| is threadsafe.
138 - (void)performSaveToDirectoryInBackground:(NSString*)directory { 109 - (void)performSaveToDirectoryInBackground:(NSString*)directory {
139 DCHECK(directory); 110 DCHECK(directory);
140 DCHECK([_pendingWindows objectForKey:directory] != nil); 111 DCHECK([_pendingWindows objectForKey:directory] != nil);
141 UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication]
marq (ping after 24h) 2017/04/06 11:58:16 Was this code just not doing anything useful? I'm
sdefresne 2017/04/06 14:57:01 This is replaced by CriticalClosure (look in scope
142 beginBackgroundTaskWithExpirationHandler:^{
143 }];
144 DCHECK(identifier != UIBackgroundTaskInvalid);
145 112
146 // Put the window into a local var so it can be retained for the block, yet 113 // 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. 114 // we can remove it from the dictionary to allow queuing another save.
148 SessionWindowIOS* localWindow = 115 SessionWindowIOS* localWindow = [_pendingWindows objectForKey:directory];
149 [[_pendingWindows objectForKey:directory] retain];
150 [_pendingWindows removeObjectForKey:directory]; 116 [_pendingWindows removeObjectForKey:directory];
151 117
152 _taskRunner->PostTask( 118 _taskRunner->PostTask(
153 FROM_HERE, base::BindBlock(^{ 119 FROM_HERE, base::MakeCriticalClosure(base::BindBlockArc(^{
154 @try { 120 @try {
155 [self performSaveWindow:localWindow toDirectory:directory]; 121 [self performSaveWindow:localWindow toDirectory:directory];
156 } @catch (NSException* e) { 122 } @catch (NSException* e) {
157 // Do nothing. 123 // Do nothing.
158 } 124 }
159 [localWindow release]; 125 })));
160 [[UIApplication sharedApplication] endBackgroundTask:identifier];
161 }));
162 } 126 }
163 127
164 // Saves a SessionWindowIOS in a given directory. In case the directory doesn't 128 // Saves a SessionWindowIOS in a given directory. In case the directory doesn't
165 // exists it will be automatically created. 129 // exists it will be automatically created.
166 - (void)performSaveWindow:(SessionWindowIOS*)window 130 - (void)performSaveWindow:(SessionWindowIOS*)window
167 toDirectory:(NSString*)directory { 131 toDirectory:(NSString*)directory {
168 base::ThreadRestrictions::AssertIOAllowed(); 132 base::ThreadRestrictions::AssertIOAllowed();
169 NSFileManager* fileManager = [NSFileManager defaultManager]; 133 NSFileManager* fileManager = [NSFileManager defaultManager];
170 BOOL isDir; 134 BOOL isDir;
171 if (![fileManager fileExistsAtPath:directory isDirectory:&isDir]) { 135 if (![fileManager fileExistsAtPath:directory isDirectory:&isDir]) {
172 NSError* error = nil; 136 NSError* error = nil;
173 BOOL result = [fileManager createDirectoryAtPath:directory 137 BOOL result = [fileManager createDirectoryAtPath:directory
174 withIntermediateDirectories:YES 138 withIntermediateDirectories:YES
175 attributes:nil 139 attributes:nil
176 error:&error]; 140 error:&error];
177 DCHECK(result); 141 DCHECK(result);
178 if (!result) { 142 if (!result) {
179 DLOG(ERROR) << "Error creating destination dir: " 143 DLOG(ERROR) << "Error creating destination directory: "
144 << base::SysNSStringToUTF8(directory) << ": "
180 << base::SysNSStringToUTF8([error description]); 145 << base::SysNSStringToUTF8([error description]);
181 return; 146 return;
182 } 147 }
183 } else { 148 } else {
184 DCHECK(isDir); 149 DCHECK(isDir);
185 if (!isDir) { 150 if (!isDir) {
186 DLOG(ERROR) << "Destination Directory already exists and is a file"; 151 DLOG(ERROR) << "Error creating destination directory: "
152 << base::SysNSStringToUTF8(directory) << ": "
153 << "file exists and is not a directory.";
187 return; 154 return;
188 } 155 }
189 } 156 }
190 157
191 NSString* filename = [self sessionFilePathForDirectory:directory]; 158 NSString* filename = [self sessionFilePathForDirectory:directory];
192 if (filename) { 159 if (filename) {
193 BOOL result = [NSKeyedArchiver archiveRootObject:window toFile:filename]; 160 BOOL result = [NSKeyedArchiver archiveRootObject:window toFile:filename];
194 DCHECK(result); 161 DCHECK(result);
195 if (!result) 162 if (!result) {
196 DLOG(ERROR) << "Error writing session file to " << filename; 163 DLOG(ERROR) << "Error writing session file to " << filename;
164 return;
165 }
166
197 // Encrypt the session file (mostly for Incognito, but can't hurt to 167 // Encrypt the session file (mostly for Incognito, but can't hurt to
198 // always do it). 168 // always do it).
199 NSDictionary* attributeDict = 169 NSDictionary* attributeDict =
marq (ping after 24h) 2017/04/06 11:58:16 Since you're here, make this NSDictionary<NSFile
sdefresne 2017/04/06 14:57:01 Inlined in the method call.
200 [NSDictionary dictionaryWithObject:NSFileProtectionComplete 170 [NSDictionary dictionaryWithObject:NSFileProtectionComplete
201 forKey:NSFileProtectionKey]; 171 forKey:NSFileProtectionKey];
202 NSError* error = nil; 172 NSError* error = nil;
203 BOOL success = [[NSFileManager defaultManager] setAttributes:attributeDict 173 BOOL success = [[NSFileManager defaultManager] setAttributes:attributeDict
204 ofItemAtPath:filename 174 ofItemAtPath:filename
205 error:&error]; 175 error:&error];
206 if (!success) { 176 if (!success) {
207 DLOG(ERROR) << "Error encrypting session file" 177 DLOG(ERROR) << "Error encrypting session file: "
178 << base::SysNSStringToUTF8(filename) << ": "
208 << base::SysNSStringToUTF8([error description]); 179 << base::SysNSStringToUTF8([error description]);
209 } 180 }
210 } 181 }
211 } 182 }
212 183
213 - (void)saveWindow:(SessionWindowIOS*)window 184 - (void)saveWindow:(SessionWindowIOS*)window
214 forBrowserState:(ios::ChromeBrowserState*)browserState 185 forBrowserState:(ios::ChromeBrowserState*)browserState
215 immediately:(BOOL)immediately { 186 immediately:(BOOL)immediately {
216 NSString* stashPath = 187 NSString* stashPath =
217 base::SysUTF8ToNSString(browserState->GetStatePath().value()); 188 base::SysUTF8ToNSString(browserState->GetStatePath().value());
218 // If there's an existing session window for |stashPath|, clear it before it's 189 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]; 190 [_pendingWindows setObject:window forKey:stashPath];
225 if (immediately) { 191 if (immediately) {
226 [NSObject cancelPreviousPerformRequestsWithTarget:self]; 192 [NSObject cancelPreviousPerformRequestsWithTarget:self];
227 [self performSaveToDirectoryInBackground:stashPath]; 193 [self performSaveToDirectoryInBackground:stashPath];
228 } else if (!pendingSession) { 194 } else if (!hadPendingSession) {
229 // If there wasn't previously a delayed save pending for |stashPath|, 195 // If there wasn't previously a delayed save pending for |stashPath|,
230 // enqueue one now. 196 // enqueue one now.
231 [self performSelector:@selector(performSaveToDirectoryInBackground:) 197 [self performSelector:@selector(performSaveToDirectoryInBackground:)
232 withObject:stashPath 198 withObject:stashPath
233 afterDelay:kSaveDelay]; 199 afterDelay:kSaveDelay];
234 } 200 }
235 } 201 }
236 202
237 - (SessionWindowIOS*)loadWindowForBrowserState: 203 - (SessionWindowIOS*)loadWindowForBrowserState:
238 (ios::ChromeBrowserState*)browserState { 204 (ios::ChromeBrowserState*)browserState {
239 NSString* stashPath = 205 NSString* stashPath =
240 base::SysUTF8ToNSString(browserState->GetStatePath().value()); 206 base::SysUTF8ToNSString(browserState->GetStatePath().value());
241 SessionWindowIOS* window = 207 SessionWindowIOS* window =
242 [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath] 208 [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath]];
243 forBrowserState:browserState];
244 return window; 209 return window;
245 } 210 }
246 211
247 - (SessionWindowIOS*)loadWindowFromPath:(NSString*)path 212 - (SessionWindowIOS*)loadWindowFromPath:(NSString*)sessionPath {
248 forBrowserState:(ios::ChromeBrowserState*)browserState {
249 SessionWindowIOS* window = nil;
250 @try { 213 @try {
251 NSData* data = [NSData dataWithContentsOfFile:path]; 214 NSData* data = [NSData dataWithContentsOfFile:sessionPath];
252 if (data) { 215 if (!data)
253 base::scoped_nsobject<SessionWindowUnarchiver> unarchiver([ 216 return nil;
254 [SessionWindowUnarchiver alloc] initForReadingWithData:data 217
255 browserState:browserState]); 218 NSKeyedUnarchiver* unarchiver =
256 window = [[[unarchiver decodeObjectForKey:@"root"] retain] autorelease]; 219 [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
257 } 220
221 // Register compatibility aliases to support legacy saved sessions.
222 [unarchiver cr_registerCompatibilityAliases];
223 return [unarchiver decodeObjectForKey:@"root"];
marq (ping after 24h) 2017/04/06 11:58:16 Extract @"root" into a constant?
sdefresne 2017/04/06 14:57:01 Done.
258 } @catch (NSException* exception) { 224 } @catch (NSException* exception) {
259 DLOG(ERROR) << "Error loading session file."; 225 DLOG(ERROR) << "Error loading session file: "
226 << base::SysNSStringToUTF8(sessionPath) << ": "
227 << base::SysNSStringToUTF8([exception reason]);
228 return nil;
260 } 229 }
261 return window;
262 } 230 }
263 231
264 // Deletes the file containing the commands for the last session in the given 232 // Deletes the file containing the commands for the last session in the given
265 // browserState directory. 233 // browserState directory.
266 - (void)deleteLastSession:(NSString*)directory { 234 - (void)deleteLastSession:(NSString*)directory {
267 NSString* sessionFile = [self sessionFilePathForDirectory:directory]; 235 NSString* sessionPath = [self sessionFilePathForDirectory:directory];
268 _taskRunner->PostTask( 236 _taskRunner->PostTask(
269 FROM_HERE, base::BindBlock(^{ 237 FROM_HERE, base::BindBlockArc(^{
marq (ping after 24h) 2017/04/06 11:58:16 Should this use criticalClosure()?
sdefresne 2017/04/06 14:57:01 Not sure. This is called from the startup code to
marq (ping after 24h) 2017/04/06 16:15:56 I think the answer to my question is "no", then. ;
270 base::ThreadRestrictions::AssertIOAllowed(); 238 base::ThreadRestrictions::AssertIOAllowed();
271 NSFileManager* fileManager = [NSFileManager defaultManager]; 239 NSFileManager* fileManager = [NSFileManager defaultManager];
272 if (![fileManager fileExistsAtPath:sessionFile]) 240 if (![fileManager fileExistsAtPath:sessionPath])
273 return; 241 return;
274 if (![fileManager removeItemAtPath:sessionFile error:nil]) 242
275 CHECK(false) << "Unable to delete session file."; 243 NSError* error = nil;
244 if (![fileManager removeItemAtPath:sessionPath error:nil])
245 CHECK(false) << "Unable to delete session file: "
246 << base::SysNSStringToUTF8(sessionPath) << ": "
247 << base::SysNSStringToUTF8([error description]);
276 })); 248 }));
277 } 249 }
278 250
279 @end 251 @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