OLD | NEW |
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 Loading... |
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 |
OLD | NEW |