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

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: Rebase. 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 sessionPath:(NSString*)sessionPath
108 return [directory stringByAppendingPathComponent:@"session.plist"]; 112 immediately:(BOOL)immediately {
109 } 113 BOOL hadPendingSession =
110 114 [_pendingSessionWindows objectForKey:sessionPath] != nil;
111 // Do the work of saving on a background thread. Assumes |window| is threadsafe. 115 [_pendingSessionWindows setObject:sessionWindow forKey:sessionPath];
112 - (void)performSaveToDirectoryInBackground:(NSString*)directory {
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) { 116 if (immediately) {
193 [NSObject cancelPreviousPerformRequestsWithTarget:self]; 117 [NSObject cancelPreviousPerformRequestsWithTarget:self];
194 [self performSaveToDirectoryInBackground:stashPath]; 118 [self performSaveToPathInBackground:sessionPath];
195 } else if (!hadPendingSession) { 119 } else if (!hadPendingSession) {
196 // If there wasn't previously a delayed save pending for |stashPath|, 120 // If there wasn't previously a delayed save pending for |sessionPath|,
197 // enqueue one now. 121 // enqueue one now.
198 [self performSelector:@selector(performSaveToDirectoryInBackground:) 122 [self performSelector:@selector(performSaveToPathInBackground:)
199 withObject:stashPath 123 withObject:sessionPath
200 afterDelay:kSaveDelay]; 124 afterDelay:kSaveDelay];
201 } 125 }
202 } 126 }
203 127
204 - (SessionWindowIOS*)loadWindowForBrowserState: 128 - (SessionWindowIOS*)loadSessionWindowFromPath:(NSString*)sessionPath {
205 (ios::ChromeBrowserState*)browserState {
206 NSString* stashPath =
207 base::SysUTF8ToNSString(browserState->GetStatePath().value());
208 SessionWindowIOS* window =
209 [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath]];
210 return window;
211 }
212
213 - (SessionWindowIOS*)loadWindowFromPath:(NSString*)sessionPath {
214 @try { 129 @try {
215 NSData* data = [NSData dataWithContentsOfFile:sessionPath]; 130 NSData* data = [NSData dataWithContentsOfFile:sessionPath];
216 if (!data) 131 if (!data)
217 return nil; 132 return nil;
218 133
219 NSKeyedUnarchiver* unarchiver = 134 NSKeyedUnarchiver* unarchiver =
220 [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 135 [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
221 136
222 // Register compatibility aliases to support legacy saved sessions. 137 // Register compatibility aliases to support legacy saved sessions.
223 [unarchiver cr_registerCompatibilityAliases]; 138 [unarchiver cr_registerCompatibilityAliases];
224 return [unarchiver decodeObjectForKey:kRootObjectKey]; 139 return [unarchiver decodeObjectForKey:kRootObjectKey];
225 } @catch (NSException* exception) { 140 } @catch (NSException* exception) {
226 DLOG(ERROR) << "Error loading session file: " 141 NOTREACHED() << "Error loading session file: "
227 << base::SysNSStringToUTF8(sessionPath) << ": " 142 << base::SysNSStringToUTF8(sessionPath) << ": "
228 << base::SysNSStringToUTF8([exception reason]); 143 << base::SysNSStringToUTF8([exception reason]);
229 return nil; 144 return nil;
230 } 145 }
231 } 146 }
232 147
233 // Deletes the file containing the commands for the last session in the given 148 - (void)deleteLastSessionAtPath:(NSString*)sessionPath {
234 // browserState directory.
235 - (void)deleteLastSession:(NSString*)directory {
236 NSString* sessionPath = [self sessionFilePathForDirectory:directory];
237 _taskRunner->PostTask( 149 _taskRunner->PostTask(
238 FROM_HERE, base::BindBlockArc(^{ 150 FROM_HERE, base::BindBlockArc(^{
239 base::ThreadRestrictions::AssertIOAllowed(); 151 base::ThreadRestrictions::AssertIOAllowed();
240 NSFileManager* fileManager = [NSFileManager defaultManager]; 152 NSFileManager* fileManager = [NSFileManager defaultManager];
241 if (![fileManager fileExistsAtPath:sessionPath]) 153 if (![fileManager fileExistsAtPath:sessionPath])
242 return; 154 return;
243 155
244 NSError* error = nil; 156 NSError* error = nil;
245 if (![fileManager removeItemAtPath:sessionPath error:nil]) 157 if (![fileManager removeItemAtPath:sessionPath error:nil])
246 CHECK(false) << "Unable to delete session file: " 158 CHECK(false) << "Unable to delete session file: "
247 << base::SysNSStringToUTF8(sessionPath) << ": " 159 << base::SysNSStringToUTF8(sessionPath) << ": "
248 << base::SysNSStringToUTF8([error description]); 160 << base::SysNSStringToUTF8([error description]);
249 })); 161 }));
250 } 162 }
251 163
164 - (NSString*)sessionPathForDirectory:(NSString*)directory {
165 return [directory stringByAppendingPathComponent:@"session.plist"];
166 }
167
168 #pragma mark - Private methods
169
170 // Do the work of saving on a background thread.
171 - (void)performSaveToPathInBackground:(NSString*)sessionPath {
172 DCHECK(sessionPath);
173 DCHECK([_pendingSessionWindows objectForKey:sessionPath] != nil);
174
175 // Serialize to NSData on the main thread to avoid accessing potentially
176 // non-threadsafe objects on a background thread.
177 SessionWindowIOS* sessionWindow =
178 [_pendingSessionWindows objectForKey:sessionPath];
179 [_pendingSessionWindows removeObjectForKey:sessionPath];
180
181 @try {
182 NSData* sessionData =
183 [NSKeyedArchiver archivedDataWithRootObject:sessionWindow];
184 _taskRunner->PostTask(
185 FROM_HERE, base::MakeCriticalClosure(base::BindBlockArc(^{
186 [self performSaveSessionData:sessionData sessionPath:sessionPath];
187 })));
188 } @catch (NSException* e) {
marq (ping after 24h) 2017/04/11 10:46:29 e -> exception
sdefresne 2017/04/11 11:49:41 Done.
189 NOTREACHED() << "Error serializing session for path: "
190 << base::SysNSStringToUTF8(sessionPath);
191 return;
192 }
193 }
194
195 - (void)performSaveSessionData:(NSData*)sessionData
196 sessionPath:(NSString*)sessionPath {
197 base::ThreadRestrictions::AssertIOAllowed();
198
199 NSFileManager* fileManager = [NSFileManager defaultManager];
200 NSString* directory = [sessionPath stringByDeletingLastPathComponent];
201
202 NSError* error = nil;
203 BOOL isDirectory = NO;
204 if (![fileManager fileExistsAtPath:directory isDirectory:&isDirectory]) {
205 isDirectory = YES;
206 if (![fileManager createDirectoryAtPath:directory
207 withIntermediateDirectories:YES
208 attributes:nil
209 error:&error]) {
210 NOTREACHED() << "Error creating destination directory: "
211 << base::SysNSStringToUTF8(directory) << ": "
212 << base::SysNSStringToUTF8([error description]);
213 return;
214 }
215 }
216
217 if (!isDirectory) {
218 NOTREACHED() << "Error creating destination directory: "
219 << base::SysNSStringToUTF8(directory) << ": "
220 << "file exists and is not a directory.";
221 return;
222 }
223
224 NSDataWritingOptions options =
225 NSDataWritingAtomic | NSDataWritingFileProtectionComplete;
226
227 if (![sessionData writeToFile:sessionPath options:options error:&error]) {
228 NOTREACHED() << "Error writing session file: "
229 << base::SysNSStringToUTF8(sessionPath) << ": "
230 << base::SysNSStringToUTF8([error description]);
231 return;
232 }
233 }
234
252 @end 235 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698