Index: ios/chrome/browser/sessions/session_service_ios.mm |
diff --git a/ios/chrome/browser/sessions/session_service_ios.mm b/ios/chrome/browser/sessions/session_service_ios.mm |
index cf89278083b6c51a96d8dc510a3b4a49d0aedd23..320d9998df359776aa866033681d6cbbfa139112 100644 |
--- a/ios/chrome/browser/sessions/session_service_ios.mm |
+++ b/ios/chrome/browser/sessions/session_service_ios.mm |
@@ -16,7 +16,6 @@ |
#include "base/strings/sys_string_conversions.h" |
#include "base/threading/sequenced_worker_pool.h" |
#include "base/threading/thread_restrictions.h" |
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
#import "ios/chrome/browser/sessions/session_window_ios.h" |
#import "ios/web/public/crw_navigation_item_storage.h" |
#import "ios/web/public/crw_session_certificate_policy_cache_storage.h" |
@@ -69,21 +68,25 @@ NSString* const kRootObjectKey = @"root"; // Key for the root object. |
@end |
-@interface SessionServiceIOS () { |
+@implementation SessionServiceIOS { |
// The SequencedTaskRunner on which File IO operations are performed. |
scoped_refptr<base::SequencedTaskRunner> _taskRunner; |
- // Maps save directories to the pending SessionWindow for the delayed |
- // save behavior. |
- NSMutableDictionary<NSString*, SessionWindowIOS*>* _pendingWindows; |
+ // Maps session path to the pending session window for the delayed save |
+ // behaviour. |
+ NSMutableDictionary<NSString*, SessionWindowIOS*>* _pendingSessionWindows; |
} |
-// Saves the session corresponding to |directory| on the background |
-// task runner |_taskRunner|. |
-- (void)performSaveToDirectoryInBackground:(NSString*)directory; |
-@end |
+#pragma mark - NSObject overrides |
+ |
+- (instancetype)init { |
+ base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool(); |
+ scoped_refptr<base::SequencedTaskRunner> taskRunner = |
+ pool->GetSequencedTaskRunner(pool->GetSequenceToken()); |
+ return [self initWithTaskRunner:taskRunner]; |
+} |
-@implementation SessionServiceIOS |
+#pragma mark - Public interface |
+ (SessionServiceIOS*)sharedService { |
static SessionServiceIOS* singleton = nil; |
@@ -93,124 +96,42 @@ NSString* const kRootObjectKey = @"root"; // Key for the root object. |
return singleton; |
} |
-- (instancetype)init { |
+- (instancetype)initWithTaskRunner: |
+ (const scoped_refptr<base::SequencedTaskRunner>&)taskRunner { |
+ DCHECK(taskRunner); |
self = [super init]; |
if (self) { |
- _pendingWindows = [NSMutableDictionary dictionary]; |
- base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool(); |
- _taskRunner = pool->GetSequencedTaskRunner(pool->GetSequenceToken()); |
+ _pendingSessionWindows = [NSMutableDictionary dictionary]; |
+ _taskRunner = taskRunner; |
} |
return self; |
} |
-// Returns the path of the session file. |
-- (NSString*)sessionFilePathForDirectory:(NSString*)directory { |
- return [directory stringByAppendingPathComponent:@"session.plist"]; |
-} |
- |
-// Do the work of saving on a background thread. Assumes |window| is threadsafe. |
-- (void)performSaveToDirectoryInBackground:(NSString*)directory { |
- DCHECK(directory); |
- DCHECK([_pendingWindows objectForKey:directory] != nil); |
- |
- // Put the window into a local var so it can be retained for the block, yet |
- // we can remove it from the dictionary to allow queuing another save. |
- SessionWindowIOS* localWindow = [_pendingWindows objectForKey:directory]; |
- [_pendingWindows removeObjectForKey:directory]; |
- |
- _taskRunner->PostTask( |
- FROM_HERE, base::MakeCriticalClosure(base::BindBlockArc(^{ |
- @try { |
- [self performSaveWindow:localWindow toDirectory:directory]; |
- } @catch (NSException* e) { |
- // Do nothing. |
- } |
- }))); |
-} |
- |
-// Saves a SessionWindowIOS in a given directory. In case the directory doesn't |
-// exists it will be automatically created. |
-- (void)performSaveWindow:(SessionWindowIOS*)window |
- toDirectory:(NSString*)directory { |
- base::ThreadRestrictions::AssertIOAllowed(); |
- NSFileManager* fileManager = [NSFileManager defaultManager]; |
- BOOL isDir; |
- if (![fileManager fileExistsAtPath:directory isDirectory:&isDir]) { |
- NSError* error = nil; |
- BOOL result = [fileManager createDirectoryAtPath:directory |
- withIntermediateDirectories:YES |
- attributes:nil |
- error:&error]; |
- DCHECK(result); |
- if (!result) { |
- DLOG(ERROR) << "Error creating destination directory: " |
- << base::SysNSStringToUTF8(directory) << ": " |
- << base::SysNSStringToUTF8([error description]); |
- return; |
- } |
- } else { |
- DCHECK(isDir); |
- if (!isDir) { |
- DLOG(ERROR) << "Error creating destination directory: " |
- << base::SysNSStringToUTF8(directory) << ": " |
- << "file exists and is not a directory."; |
- return; |
- } |
- } |
- |
- NSString* filename = [self sessionFilePathForDirectory:directory]; |
- if (filename) { |
- BOOL result = [NSKeyedArchiver archiveRootObject:window toFile:filename]; |
- DCHECK(result); |
- if (!result) { |
- DLOG(ERROR) << "Error writing session file to " << filename; |
- return; |
- } |
- |
- // Encrypt the session file (mostly for Incognito, but can't hurt to |
- // always do it). |
- NSError* error = nil; |
- BOOL success = [[NSFileManager defaultManager] |
- setAttributes:@{NSFileProtectionKey : NSFileProtectionComplete} |
- ofItemAtPath:filename |
- error:&error]; |
- if (!success) { |
- DLOG(ERROR) << "Error encrypting session file: " |
- << base::SysNSStringToUTF8(filename) << ": " |
- << base::SysNSStringToUTF8([error description]); |
- } |
- } |
-} |
- |
-- (void)saveWindow:(SessionWindowIOS*)window |
- forBrowserState:(ios::ChromeBrowserState*)browserState |
- immediately:(BOOL)immediately { |
- NSString* stashPath = |
- base::SysUTF8ToNSString(browserState->GetStatePath().value()); |
- BOOL hadPendingSession = [_pendingWindows objectForKey:stashPath] != nil; |
- [_pendingWindows setObject:window forKey:stashPath]; |
+- (void)saveSessionWindow:(SessionWindowIOS*)sessionWindow |
+ directory:(NSString*)directory |
+ immediately:(BOOL)immediately { |
+ NSString* sessionPath = [[self class] sessionPathForDirectory:directory]; |
+ BOOL hadPendingSession = |
+ [_pendingSessionWindows objectForKey:sessionPath] != nil; |
+ [_pendingSessionWindows setObject:sessionWindow forKey:sessionPath]; |
if (immediately) { |
[NSObject cancelPreviousPerformRequestsWithTarget:self]; |
- [self performSaveToDirectoryInBackground:stashPath]; |
+ [self performSaveToPathInBackground:sessionPath]; |
} else if (!hadPendingSession) { |
- // If there wasn't previously a delayed save pending for |stashPath|, |
+ // If there wasn't previously a delayed save pending for |sessionPath|, |
// enqueue one now. |
- [self performSelector:@selector(performSaveToDirectoryInBackground:) |
- withObject:stashPath |
+ [self performSelector:@selector(performSaveToPathInBackground:) |
+ withObject:sessionPath |
afterDelay:kSaveDelay]; |
} |
} |
-- (SessionWindowIOS*)loadWindowForBrowserState: |
- (ios::ChromeBrowserState*)browserState { |
- NSString* stashPath = |
- base::SysUTF8ToNSString(browserState->GetStatePath().value()); |
- SessionWindowIOS* window = |
- [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath]]; |
- return window; |
+- (SessionWindowIOS*)loadSessionWindowFromDirectory:(NSString*)directory { |
+ NSString* sessionPath = [[self class] sessionPathForDirectory:directory]; |
+ return [self loadSessionWindowFromPath:sessionPath]; |
} |
-- (SessionWindowIOS*)loadWindowFromPath:(NSString*)sessionPath { |
+- (SessionWindowIOS*)loadSessionWindowFromPath:(NSString*)sessionPath { |
@try { |
NSData* data = [NSData dataWithContentsOfFile:sessionPath]; |
if (!data) |
@@ -223,17 +144,15 @@ NSString* const kRootObjectKey = @"root"; // Key for the root object. |
[unarchiver cr_registerCompatibilityAliases]; |
return [unarchiver decodeObjectForKey:kRootObjectKey]; |
} @catch (NSException* exception) { |
- DLOG(ERROR) << "Error loading session file: " |
- << base::SysNSStringToUTF8(sessionPath) << ": " |
- << base::SysNSStringToUTF8([exception reason]); |
+ NOTREACHED() << "Error loading session file: " |
+ << base::SysNSStringToUTF8(sessionPath) << ": " |
+ << base::SysNSStringToUTF8([exception reason]); |
return nil; |
} |
} |
-// Deletes the file containing the commands for the last session in the given |
-// browserState directory. |
-- (void)deleteLastSession:(NSString*)directory { |
- NSString* sessionPath = [self sessionFilePathForDirectory:directory]; |
+- (void)deleteLastSessionFileInDirectory:(NSString*)directory { |
+ NSString* sessionPath = [[self class] sessionPathForDirectory:directory]; |
_taskRunner->PostTask( |
FROM_HERE, base::BindBlockArc(^{ |
base::ThreadRestrictions::AssertIOAllowed(); |
@@ -249,4 +168,80 @@ NSString* const kRootObjectKey = @"root"; // Key for the root object. |
})); |
} |
++ (NSString*)sessionPathForDirectory:(NSString*)directory { |
+ return [directory stringByAppendingPathComponent:@"session.plist"]; |
+} |
+ |
+#pragma mark - Private methods |
+ |
+// Do the work of saving on a background thread. |
+- (void)performSaveToPathInBackground:(NSString*)sessionPath { |
+ DCHECK(sessionPath); |
+ DCHECK([_pendingSessionWindows objectForKey:sessionPath] != nil); |
+ |
+ // Serialize to NSData on the main thread to avoid accessing potentially |
+ // non-threadsafe objects on a background thread. |
+ SessionWindowIOS* sessionWindow = |
+ [_pendingSessionWindows objectForKey:sessionPath]; |
+ [_pendingSessionWindows removeObjectForKey:sessionPath]; |
+ |
+ @try { |
+ NSData* sessionData = |
+ [NSKeyedArchiver archivedDataWithRootObject:sessionWindow]; |
+ _taskRunner->PostTask( |
+ FROM_HERE, base::MakeCriticalClosure(base::BindBlockArc(^{ |
+ [self performSaveSessionData:sessionData sessionPath:sessionPath]; |
+ }))); |
+ } @catch (NSException* exception) { |
+ NOTREACHED() << "Error serializing session for path: " |
+ << base::SysNSStringToUTF8(sessionPath) << ": " |
+ << base::SysNSStringToUTF8([exception description]); |
+ return; |
+ } |
+} |
+ |
+@end |
+ |
+@implementation SessionServiceIOS (SubClassing) |
+ |
+- (void)performSaveSessionData:(NSData*)sessionData |
+ sessionPath:(NSString*)sessionPath { |
+ base::ThreadRestrictions::AssertIOAllowed(); |
+ |
+ NSFileManager* fileManager = [NSFileManager defaultManager]; |
+ NSString* directory = [sessionPath stringByDeletingLastPathComponent]; |
+ |
+ NSError* error = nil; |
+ BOOL isDirectory = NO; |
+ if (![fileManager fileExistsAtPath:directory isDirectory:&isDirectory]) { |
+ isDirectory = YES; |
+ if (![fileManager createDirectoryAtPath:directory |
+ withIntermediateDirectories:YES |
+ attributes:nil |
+ error:&error]) { |
+ NOTREACHED() << "Error creating destination directory: " |
+ << base::SysNSStringToUTF8(directory) << ": " |
+ << base::SysNSStringToUTF8([error description]); |
+ return; |
+ } |
+ } |
+ |
+ if (!isDirectory) { |
+ NOTREACHED() << "Error creating destination directory: " |
+ << base::SysNSStringToUTF8(directory) << ": " |
+ << "file exists and is not a directory."; |
+ return; |
+ } |
+ |
+ NSDataWritingOptions options = |
+ NSDataWritingAtomic | NSDataWritingFileProtectionComplete; |
+ |
+ if (![sessionData writeToFile:sessionPath options:options error:&error]) { |
+ NOTREACHED() << "Error writing session file: " |
+ << base::SysNSStringToUTF8(sessionPath) << ": " |
+ << base::SysNSStringToUTF8([error description]); |
+ return; |
+ } |
+} |
+ |
@end |