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

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

Issue 2810743002: [ios] Refactor SessionServiceIOS to remove dependency on BrowserState. (Closed)
Patch Set: Fix a typo in SessionServiceIOS initializer's comment. 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_ios.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/critical_closure.h"
10 #include "base/files/file_path.h" 10 #include "base/files/file_path.h"
11 #include "base/location.h" 11 #include "base/location.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #import "base/mac/bind_objc_block.h" 13 #import "base/mac/bind_objc_block.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/threading/sequenced_worker_pool.h" 17 #include "base/threading/sequenced_worker_pool.h"
18 #include "base/threading/thread_restrictions.h" 18 #include "base/threading/thread_restrictions.h"
19 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
20 #import "ios/chrome/browser/sessions/session_window_ios.h" 19 #import "ios/chrome/browser/sessions/session_window_ios.h"
21 #import "ios/web/public/crw_navigation_item_storage.h" 20 #import "ios/web/public/crw_navigation_item_storage.h"
22 #import "ios/web/public/crw_session_certificate_policy_cache_storage.h" 21 #import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
23 #import "ios/web/public/crw_session_storage.h" 22 #import "ios/web/public/crw_session_storage.h"
24 #include "ios/web/public/web_thread.h" 23 #include "ios/web/public/web_thread.h"
25 24
26 #if !defined(__has_feature) || !__has_feature(objc_arc) 25 #if !defined(__has_feature) || !__has_feature(objc_arc)
27 #error "This file requires ARC support." 26 #error "This file requires ARC support."
28 #endif 27 #endif
29 28
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 // TODO(crbug.com/661633): this alias was introduced between M58 and M59, so 61 // TODO(crbug.com/661633): this alias was introduced between M58 and M59, so
63 // remove it after M68 has shipped to stable. 62 // remove it after M68 has shipped to stable.
64 [self setClass:[CRWSessionStorage class] 63 [self setClass:[CRWSessionStorage class]
65 forClassName:@"CRWNavigationManagerStorage"]; 64 forClassName:@"CRWNavigationManagerStorage"];
66 [self setClass:[CRWSessionCertificatePolicyCacheStorage class] 65 [self setClass:[CRWSessionCertificatePolicyCacheStorage class]
67 forClassName:@"CRWSessionCertificatePolicyManager"]; 66 forClassName:@"CRWSessionCertificatePolicyManager"];
68 } 67 }
69 68
70 @end 69 @end
71 70
72 @interface SessionServiceIOS () { 71 @implementation SessionServiceIOS {
73 // The SequencedTaskRunner on which File IO operations are performed. 72 // The SequencedTaskRunner on which File IO operations are performed.
74 scoped_refptr<base::SequencedTaskRunner> _taskRunner; 73 scoped_refptr<base::SequencedTaskRunner> _taskRunner;
75 74
76 // Maps save directories to the pending SessionWindow for the delayed 75 // Maps session path to the pending session window for the delayed save
77 // save behavior. 76 // behaviour.
78 NSMutableDictionary<NSString*, SessionWindowIOS*>* _pendingWindows; 77 NSMutableDictionary<NSString*, SessionWindowIOS*>* _pendingSessionWindows;
79 } 78 }
80 79
81 // Saves the session corresponding to |directory| on the background 80 #pragma mark - NSObject overrides
82 // task runner |_taskRunner|.
83 - (void)performSaveToDirectoryInBackground:(NSString*)directory;
84 @end
85 81
86 @implementation SessionServiceIOS 82 - (instancetype)init {
83 base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool();
84 scoped_refptr<base::SequencedTaskRunner> taskRunner =
85 pool->GetSequencedTaskRunner(pool->GetSequenceToken());
86 return [self initWithTaskRunner:taskRunner];
87 }
88
89 #pragma mark - Public interface
87 90
88 + (SessionServiceIOS*)sharedService { 91 + (SessionServiceIOS*)sharedService {
89 static SessionServiceIOS* singleton = nil; 92 static SessionServiceIOS* singleton = nil;
90 if (!singleton) { 93 if (!singleton) {
91 singleton = [[[self class] alloc] init]; 94 singleton = [[[self class] alloc] init];
92 } 95 }
93 return singleton; 96 return singleton;
94 } 97 }
95 98
96 - (instancetype)init { 99 - (instancetype)initWithTaskRunner:
100 (const scoped_refptr<base::SequencedTaskRunner>&)taskRunner {
101 DCHECK(taskRunner);
97 self = [super init]; 102 self = [super init];
98 if (self) { 103 if (self) {
99 _pendingWindows = [NSMutableDictionary dictionary]; 104 _pendingSessionWindows = [NSMutableDictionary dictionary];
100 base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool(); 105 _taskRunner = taskRunner;
101 _taskRunner = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
102 } 106 }
103 return self; 107 return self;
104 } 108 }
105 109
106 // Returns the path of the session file. 110 - (void)saveSessionWindow:(SessionWindowIOS*)sessionWindow
107 - (NSString*)sessionFilePathForDirectory:(NSString*)directory { 111 directory:(NSString*)directory
108 return [directory stringByAppendingPathComponent:@"session.plist"]; 112 immediately:(BOOL)immediately {
109 } 113 NSString* sessionPath = [[self class] sessionPathForDirectory:directory];
110 114 BOOL hadPendingSession =
111 // Do the work of saving on a background thread. Assumes |window| is threadsafe. 115 [_pendingSessionWindows objectForKey:sessionPath] != nil;
112 - (void)performSaveToDirectoryInBackground:(NSString*)directory { 116 [_pendingSessionWindows setObject:sessionWindow forKey:sessionPath];
113 DCHECK(directory);
114 DCHECK([_pendingWindows objectForKey:directory] != nil);
115
116 // Put the window into a local var so it can be retained for the block, yet
117 // we can remove it from the dictionary to allow queuing another save.
118 SessionWindowIOS* localWindow = [_pendingWindows objectForKey:directory];
119 [_pendingWindows removeObjectForKey:directory];
120
121 _taskRunner->PostTask(
122 FROM_HERE, base::MakeCriticalClosure(base::BindBlockArc(^{
123 @try {
124 [self performSaveWindow:localWindow toDirectory:directory];
125 } @catch (NSException* e) {
126 // Do nothing.
127 }
128 })));
129 }
130
131 // Saves a SessionWindowIOS in a given directory. In case the directory doesn't
132 // exists it will be automatically created.
133 - (void)performSaveWindow:(SessionWindowIOS*)window
134 toDirectory:(NSString*)directory {
135 base::ThreadRestrictions::AssertIOAllowed();
136 NSFileManager* fileManager = [NSFileManager defaultManager];
137 BOOL isDir;
138 if (![fileManager fileExistsAtPath:directory isDirectory:&isDir]) {
139 NSError* error = nil;
140 BOOL result = [fileManager createDirectoryAtPath:directory
141 withIntermediateDirectories:YES
142 attributes:nil
143 error:&error];
144 DCHECK(result);
145 if (!result) {
146 DLOG(ERROR) << "Error creating destination directory: "
147 << base::SysNSStringToUTF8(directory) << ": "
148 << base::SysNSStringToUTF8([error description]);
149 return;
150 }
151 } else {
152 DCHECK(isDir);
153 if (!isDir) {
154 DLOG(ERROR) << "Error creating destination directory: "
155 << base::SysNSStringToUTF8(directory) << ": "
156 << "file exists and is not a directory.";
157 return;
158 }
159 }
160
161 NSString* filename = [self sessionFilePathForDirectory:directory];
162 if (filename) {
163 BOOL result = [NSKeyedArchiver archiveRootObject:window toFile:filename];
164 DCHECK(result);
165 if (!result) {
166 DLOG(ERROR) << "Error writing session file to " << filename;
167 return;
168 }
169
170 // Encrypt the session file (mostly for Incognito, but can't hurt to
171 // always do it).
172 NSError* error = nil;
173 BOOL success = [[NSFileManager defaultManager]
174 setAttributes:@{NSFileProtectionKey : NSFileProtectionComplete}
175 ofItemAtPath:filename
176 error:&error];
177 if (!success) {
178 DLOG(ERROR) << "Error encrypting session file: "
179 << base::SysNSStringToUTF8(filename) << ": "
180 << base::SysNSStringToUTF8([error description]);
181 }
182 }
183 }
184
185 - (void)saveWindow:(SessionWindowIOS*)window
186 forBrowserState:(ios::ChromeBrowserState*)browserState
187 immediately:(BOOL)immediately {
188 NSString* stashPath =
189 base::SysUTF8ToNSString(browserState->GetStatePath().value());
190 BOOL hadPendingSession = [_pendingWindows objectForKey:stashPath] != nil;
191 [_pendingWindows setObject:window forKey:stashPath];
192 if (immediately) { 117 if (immediately) {
193 [NSObject cancelPreviousPerformRequestsWithTarget:self]; 118 [NSObject cancelPreviousPerformRequestsWithTarget:self];
194 [self performSaveToDirectoryInBackground:stashPath]; 119 [self performSaveToPathInBackground:sessionPath];
195 } else if (!hadPendingSession) { 120 } else if (!hadPendingSession) {
196 // If there wasn't previously a delayed save pending for |stashPath|, 121 // If there wasn't previously a delayed save pending for |sessionPath|,
197 // enqueue one now. 122 // enqueue one now.
198 [self performSelector:@selector(performSaveToDirectoryInBackground:) 123 [self performSelector:@selector(performSaveToPathInBackground:)
199 withObject:stashPath 124 withObject:sessionPath
200 afterDelay:kSaveDelay]; 125 afterDelay:kSaveDelay];
201 } 126 }
202 } 127 }
203 128
204 - (SessionWindowIOS*)loadWindowForBrowserState: 129 - (SessionWindowIOS*)loadSessionWindowFromDirectory:(NSString*)directory {
205 (ios::ChromeBrowserState*)browserState { 130 NSString* sessionPath = [[self class] sessionPathForDirectory:directory];
206 NSString* stashPath = 131 return [self loadSessionWindowFromPath:sessionPath];
207 base::SysUTF8ToNSString(browserState->GetStatePath().value());
208 SessionWindowIOS* window =
209 [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath]];
210 return window;
211 } 132 }
212 133
213 - (SessionWindowIOS*)loadWindowFromPath:(NSString*)sessionPath { 134 - (SessionWindowIOS*)loadSessionWindowFromPath:(NSString*)sessionPath {
214 @try { 135 @try {
215 NSData* data = [NSData dataWithContentsOfFile:sessionPath]; 136 NSData* data = [NSData dataWithContentsOfFile:sessionPath];
216 if (!data) 137 if (!data)
217 return nil; 138 return nil;
218 139
219 NSKeyedUnarchiver* unarchiver = 140 NSKeyedUnarchiver* unarchiver =
220 [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 141 [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
221 142
222 // Register compatibility aliases to support legacy saved sessions. 143 // Register compatibility aliases to support legacy saved sessions.
223 [unarchiver cr_registerCompatibilityAliases]; 144 [unarchiver cr_registerCompatibilityAliases];
224 return [unarchiver decodeObjectForKey:kRootObjectKey]; 145 return [unarchiver decodeObjectForKey:kRootObjectKey];
225 } @catch (NSException* exception) { 146 } @catch (NSException* exception) {
226 DLOG(ERROR) << "Error loading session file: " 147 NOTREACHED() << "Error loading session file: "
227 << base::SysNSStringToUTF8(sessionPath) << ": " 148 << base::SysNSStringToUTF8(sessionPath) << ": "
228 << base::SysNSStringToUTF8([exception reason]); 149 << base::SysNSStringToUTF8([exception reason]);
229 return nil; 150 return nil;
230 } 151 }
231 } 152 }
232 153
233 // Deletes the file containing the commands for the last session in the given 154 - (void)deleteLastSessionFileInDirectory:(NSString*)directory {
234 // browserState directory. 155 NSString* sessionPath = [[self class] sessionPathForDirectory:directory];
235 - (void)deleteLastSession:(NSString*)directory {
236 NSString* sessionPath = [self sessionFilePathForDirectory:directory];
237 _taskRunner->PostTask( 156 _taskRunner->PostTask(
238 FROM_HERE, base::BindBlockArc(^{ 157 FROM_HERE, base::BindBlockArc(^{
239 base::ThreadRestrictions::AssertIOAllowed(); 158 base::ThreadRestrictions::AssertIOAllowed();
240 NSFileManager* fileManager = [NSFileManager defaultManager]; 159 NSFileManager* fileManager = [NSFileManager defaultManager];
241 if (![fileManager fileExistsAtPath:sessionPath]) 160 if (![fileManager fileExistsAtPath:sessionPath])
242 return; 161 return;
243 162
244 NSError* error = nil; 163 NSError* error = nil;
245 if (![fileManager removeItemAtPath:sessionPath error:nil]) 164 if (![fileManager removeItemAtPath:sessionPath error:nil])
246 CHECK(false) << "Unable to delete session file: " 165 CHECK(false) << "Unable to delete session file: "
247 << base::SysNSStringToUTF8(sessionPath) << ": " 166 << base::SysNSStringToUTF8(sessionPath) << ": "
248 << base::SysNSStringToUTF8([error description]); 167 << base::SysNSStringToUTF8([error description]);
249 })); 168 }));
250 } 169 }
251 170
171 + (NSString*)sessionPathForDirectory:(NSString*)directory {
172 return [directory stringByAppendingPathComponent:@"session.plist"];
173 }
174
175 #pragma mark - Private methods
176
177 // Do the work of saving on a background thread.
178 - (void)performSaveToPathInBackground:(NSString*)sessionPath {
179 DCHECK(sessionPath);
180 DCHECK([_pendingSessionWindows objectForKey:sessionPath] != nil);
181
182 // Serialize to NSData on the main thread to avoid accessing potentially
183 // non-threadsafe objects on a background thread.
184 SessionWindowIOS* sessionWindow =
185 [_pendingSessionWindows objectForKey:sessionPath];
186 [_pendingSessionWindows removeObjectForKey:sessionPath];
187
188 @try {
189 NSData* sessionData =
190 [NSKeyedArchiver archivedDataWithRootObject:sessionWindow];
191 _taskRunner->PostTask(
192 FROM_HERE, base::MakeCriticalClosure(base::BindBlockArc(^{
193 [self performSaveSessionData:sessionData sessionPath:sessionPath];
194 })));
195 } @catch (NSException* exception) {
196 NOTREACHED() << "Error serializing session for path: "
197 << base::SysNSStringToUTF8(sessionPath) << ": "
198 << base::SysNSStringToUTF8([exception description]);
199 return;
200 }
201 }
202
252 @end 203 @end
204
205 @implementation SessionServiceIOS (SubClassing)
206
207 - (void)performSaveSessionData:(NSData*)sessionData
208 sessionPath:(NSString*)sessionPath {
209 base::ThreadRestrictions::AssertIOAllowed();
210
211 NSFileManager* fileManager = [NSFileManager defaultManager];
212 NSString* directory = [sessionPath stringByDeletingLastPathComponent];
213
214 NSError* error = nil;
215 BOOL isDirectory = NO;
216 if (![fileManager fileExistsAtPath:directory isDirectory:&isDirectory]) {
217 isDirectory = YES;
218 if (![fileManager createDirectoryAtPath:directory
219 withIntermediateDirectories:YES
220 attributes:nil
221 error:&error]) {
222 NOTREACHED() << "Error creating destination directory: "
223 << base::SysNSStringToUTF8(directory) << ": "
224 << base::SysNSStringToUTF8([error description]);
225 return;
226 }
227 }
228
229 if (!isDirectory) {
230 NOTREACHED() << "Error creating destination directory: "
231 << base::SysNSStringToUTF8(directory) << ": "
232 << "file exists and is not a directory.";
233 return;
234 }
235
236 NSDataWritingOptions options =
237 NSDataWritingAtomic | NSDataWritingFileProtectionComplete;
238
239 if (![sessionData writeToFile:sessionPath options:options error:&error]) {
240 NOTREACHED() << "Error writing session file: "
241 << base::SysNSStringToUTF8(sessionPath) << ": "
242 << base::SysNSStringToUTF8([error description]);
243 return;
244 }
245 }
246
247 @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