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

Side by Side Diff: ios/chrome/browser/sessions/session_service.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
(Empty)
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
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/sessions/session_service.h"
6
7 #import <UIKit/UIKit.h>
8
9 #include "base/files/file_path.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/mac/bind_objc_block.h"
13 #include "base/mac/foundation_util.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/synchronization/lock.h"
18 #include "base/threading/sequenced_worker_pool.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
21 #import "ios/chrome/browser/sessions/session_window_ios.h"
22 #import "ios/web/public/crw_navigation_item_storage.h"
23 #import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
24 #import "ios/web/public/crw_session_storage.h"
25 #include "ios/web/public/web_thread.h"
26
27 // 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
29 // 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.
31 // 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.
33 #undef try
34 #undef catch
35
36 const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
37
38 @interface SessionWindowUnarchiver ()
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
67 // 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
69 // of the alias.
70 + (void)registerCompatibilityAliases {
71 // TODO(crbug.com/661633): those aliases where introduced between M57 and
72 // M58, so remove them after M67 has shipped to stable.
73 [SessionWindowUnarchiver
74 setClass:[CRWSessionCertificatePolicyCacheStorage class]
75 forClassName:@"SessionCertificatePolicyManager"];
76 [SessionWindowUnarchiver setClass:[CRWSessionStorage class]
77 forClassName:@"SessionController"];
78 [SessionWindowUnarchiver setClass:[CRWSessionStorage class]
79 forClassName:@"CRWSessionController"];
80 [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class]
81 forClassName:@"SessionEntry"];
82 [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class]
83 forClassName:@"CRWSessionEntry"];
84 [SessionWindowUnarchiver setClass:[SessionWindowIOS class]
85 forClassName:@"SessionWindow"];
86
87 // TODO(crbug.com/661633): this alias was introduced between M58 and M59, so
88 // remove it after M68 has shipped to stable.
89 [SessionWindowUnarchiver setClass:[CRWSessionStorage class]
90 forClassName:@"CRWNavigationManagerStorage"];
91 [SessionWindowUnarchiver
92 setClass:[CRWSessionCertificatePolicyCacheStorage class]
93 forClassName:@"CRWSessionCertificatePolicyManager"];
94 }
95
96 @end
97
98 @interface SessionServiceIOS () {
99 // The SequencedTaskRunner on which File IO operations are performed.
100 scoped_refptr<base::SequencedTaskRunner> _taskRunner;
101
102 // Maps save directories to the pending SessionWindow for the delayed
103 // save behavior.
104 base::scoped_nsobject<NSMutableDictionary> _pendingWindows;
105 }
106
107 // Saves the session corresponding to |directory| on the background
108 // task runner |_taskRunner|.
109 - (void)performSaveToDirectoryInBackground:(NSString*)directory;
110 @end
111
112 @implementation SessionServiceIOS
113
114 + (SessionServiceIOS*)sharedService {
115 static SessionServiceIOS* singleton = nil;
116 if (!singleton) {
117 singleton = [[[self class] alloc] init];
118 }
119 return singleton;
120 }
121
122 - (instancetype)init {
123 self = [super init];
124 if (self) {
125 _pendingWindows.reset([[NSMutableDictionary alloc] init]);
126 auto* pool = web::WebThread::GetBlockingPool();
127 _taskRunner = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
128 }
129 return self;
130 }
131
132 // Returns the path of the session file.
133 - (NSString*)sessionFilePathForDirectory:(NSString*)directory {
134 return [directory stringByAppendingPathComponent:@"session.plist"];
135 }
136
137 // Do the work of saving on a background thread. Assumes |window| is threadsafe.
138 - (void)performSaveToDirectoryInBackground:(NSString*)directory {
139 DCHECK(directory);
140 DCHECK([_pendingWindows objectForKey:directory] != nil);
141 UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication]
142 beginBackgroundTaskWithExpirationHandler:^{
143 }];
144 DCHECK(identifier != UIBackgroundTaskInvalid);
145
146 // 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.
148 SessionWindowIOS* localWindow =
149 [[_pendingWindows objectForKey:directory] retain];
150 [_pendingWindows removeObjectForKey:directory];
151
152 _taskRunner->PostTask(
153 FROM_HERE, base::BindBlock(^{
154 @try {
155 [self performSaveWindow:localWindow toDirectory:directory];
156 } @catch (NSException* e) {
157 // Do nothing.
158 }
159 [localWindow release];
160 [[UIApplication sharedApplication] endBackgroundTask:identifier];
161 }));
162 }
163
164 // Saves a SessionWindowIOS in a given directory. In case the directory doesn't
165 // exists it will be automatically created.
166 - (void)performSaveWindow:(SessionWindowIOS*)window
167 toDirectory:(NSString*)directory {
168 base::ThreadRestrictions::AssertIOAllowed();
169 NSFileManager* fileManager = [NSFileManager defaultManager];
170 BOOL isDir;
171 if (![fileManager fileExistsAtPath:directory isDirectory:&isDir]) {
172 NSError* error = nil;
173 BOOL result = [fileManager createDirectoryAtPath:directory
174 withIntermediateDirectories:YES
175 attributes:nil
176 error:&error];
177 DCHECK(result);
178 if (!result) {
179 DLOG(ERROR) << "Error creating destination dir: "
180 << base::SysNSStringToUTF8([error description]);
181 return;
182 }
183 } else {
184 DCHECK(isDir);
185 if (!isDir) {
186 DLOG(ERROR) << "Destination Directory already exists and is a file";
187 return;
188 }
189 }
190
191 NSString* filename = [self sessionFilePathForDirectory:directory];
192 if (filename) {
193 BOOL result = [NSKeyedArchiver archiveRootObject:window toFile:filename];
194 DCHECK(result);
195 if (!result)
196 DLOG(ERROR) << "Error writing session file to " << filename;
197 // Encrypt the session file (mostly for Incognito, but can't hurt to
198 // always do it).
199 NSDictionary* attributeDict =
200 [NSDictionary dictionaryWithObject:NSFileProtectionComplete
201 forKey:NSFileProtectionKey];
202 NSError* error = nil;
203 BOOL success = [[NSFileManager defaultManager] setAttributes:attributeDict
204 ofItemAtPath:filename
205 error:&error];
206 if (!success) {
207 DLOG(ERROR) << "Error encrypting session file"
208 << base::SysNSStringToUTF8([error description]);
209 }
210 }
211 }
212
213 - (void)saveWindow:(SessionWindowIOS*)window
214 forBrowserState:(ios::ChromeBrowserState*)browserState
215 immediately:(BOOL)immediately {
216 NSString* stashPath =
217 base::SysUTF8ToNSString(browserState->GetStatePath().value());
218 // If there's an existing session window for |stashPath|, clear it before it's
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];
225 if (immediately) {
226 [NSObject cancelPreviousPerformRequestsWithTarget:self];
227 [self performSaveToDirectoryInBackground:stashPath];
228 } else if (!pendingSession) {
229 // If there wasn't previously a delayed save pending for |stashPath|,
230 // enqueue one now.
231 [self performSelector:@selector(performSaveToDirectoryInBackground:)
232 withObject:stashPath
233 afterDelay:kSaveDelay];
234 }
235 }
236
237 - (SessionWindowIOS*)loadWindowForBrowserState:
238 (ios::ChromeBrowserState*)browserState {
239 NSString* stashPath =
240 base::SysUTF8ToNSString(browserState->GetStatePath().value());
241 SessionWindowIOS* window =
242 [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath]
243 forBrowserState:browserState];
244 return window;
245 }
246
247 - (SessionWindowIOS*)loadWindowFromPath:(NSString*)path
248 forBrowserState:(ios::ChromeBrowserState*)browserState {
249 SessionWindowIOS* window = nil;
250 @try {
251 NSData* data = [NSData dataWithContentsOfFile:path];
252 if (data) {
253 base::scoped_nsobject<SessionWindowUnarchiver> unarchiver([
254 [SessionWindowUnarchiver alloc] initForReadingWithData:data
255 browserState:browserState]);
256 window = [[[unarchiver decodeObjectForKey:@"root"] retain] autorelease];
257 }
258 } @catch (NSException* exception) {
259 DLOG(ERROR) << "Error loading session file.";
260 }
261 return window;
262 }
263
264 // Deletes the file containing the commands for the last session in the given
265 // browserState directory.
266 - (void)deleteLastSession:(NSString*)directory {
267 NSString* sessionFile = [self sessionFilePathForDirectory:directory];
268 _taskRunner->PostTask(
269 FROM_HERE, base::BindBlock(^{
270 base::ThreadRestrictions::AssertIOAllowed();
271 NSFileManager* fileManager = [NSFileManager defaultManager];
272 if (![fileManager fileExistsAtPath:sessionFile])
273 return;
274 if (![fileManager removeItemAtPath:sessionFile error:nil])
275 CHECK(false) << "Unable to delete session file.";
276 }));
277 }
278
279 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/sessions/session_service.h ('k') | ios/chrome/browser/sessions/session_service_ios.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698