| 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) { |
| 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 |