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