| Index: ios/chrome/browser/sessions/session_service_ios.mm
|
| diff --git a/ios/chrome/browser/sessions/session_service.mm b/ios/chrome/browser/sessions/session_service_ios.mm
|
| similarity index 58%
|
| rename from ios/chrome/browser/sessions/session_service.mm
|
| rename to ios/chrome/browser/sessions/session_service_ios.mm
|
| index b1254ffd046ada214e9f28026db231616b46f1ff..cf89278083b6c51a96d8dc510a3b4a49d0aedd23 100644
|
| --- a/ios/chrome/browser/sessions/session_service.mm
|
| +++ b/ios/chrome/browser/sessions/session_service_ios.mm
|
| @@ -2,19 +2,18 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#import "ios/chrome/browser/sessions/session_service.h"
|
| +#import "ios/chrome/browser/sessions/session_service_ios.h"
|
|
|
| #import <UIKit/UIKit.h>
|
|
|
| +#include "base/critical_closure.h"
|
| #include "base/files/file_path.h"
|
| #include "base/location.h"
|
| #include "base/logging.h"
|
| -#include "base/mac/bind_objc_block.h"
|
| -#include "base/mac/foundation_util.h"
|
| +#import "base/mac/bind_objc_block.h"
|
| #include "base/memory/ref_counted.h"
|
| #include "base/sequenced_task_runner.h"
|
| #include "base/strings/sys_string_conversions.h"
|
| -#include "base/synchronization/lock.h"
|
| #include "base/threading/sequenced_worker_pool.h"
|
| #include "base/threading/thread_restrictions.h"
|
| #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
|
| @@ -24,6 +23,10 @@
|
| #import "ios/web/public/crw_session_storage.h"
|
| #include "ios/web/public/web_thread.h"
|
|
|
| +#if !defined(__has_feature) || !__has_feature(objc_arc)
|
| +#error "This file requires ARC support."
|
| +#endif
|
| +
|
| // When C++ exceptions are disabled, the C++ library defines |try| and
|
| // |catch| so as to allow exception-expecting C++ code to build properly when
|
| // language support for exceptions is not present. These macros interfere
|
| @@ -33,63 +36,34 @@
|
| #undef try
|
| #undef catch
|
|
|
| -const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
| -
|
| -@interface SessionWindowUnarchiver ()
|
| -
|
| -// Register compatibility aliases to support loading serialised sessions
|
| -// informations when the serialised classes are renamed.
|
| -+ (void)registerCompatibilityAliases;
|
| -
|
| -@end
|
| -
|
| -@implementation SessionWindowUnarchiver
|
| -
|
| -@synthesize browserState = _browserState;
|
| -
|
| -- (instancetype)initForReadingWithData:(NSData*)data
|
| - browserState:(ios::ChromeBrowserState*)browserState {
|
| - if (self = [super initForReadingWithData:data]) {
|
| - _browserState = browserState;
|
| - }
|
| - return self;
|
| +namespace {
|
| +const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
| +NSString* const kRootObjectKey = @"root"; // Key for the root object.
|
| }
|
|
|
| -- (instancetype)initForReadingWithData:(NSData*)data {
|
| - return [self initForReadingWithData:data browserState:nullptr];
|
| -}
|
| -
|
| -+ (void)initialize {
|
| - [super initialize];
|
| - [self registerCompatibilityAliases];
|
| -}
|
| +@implementation NSKeyedUnarchiver (CrLegacySessionCompatibility)
|
|
|
| // When adding a new compatibility alias here, create a new crbug to track its
|
| // removal and mark it with a release at least one year after the introduction
|
| // of the alias.
|
| -+ (void)registerCompatibilityAliases {
|
| +- (void)cr_registerCompatibilityAliases {
|
| // TODO(crbug.com/661633): those aliases where introduced between M57 and
|
| // M58, so remove them after M67 has shipped to stable.
|
| - [SessionWindowUnarchiver
|
| - setClass:[CRWSessionCertificatePolicyCacheStorage class]
|
| + [self setClass:[CRWSessionCertificatePolicyCacheStorage class]
|
| forClassName:@"SessionCertificatePolicyManager"];
|
| - [SessionWindowUnarchiver setClass:[CRWSessionStorage class]
|
| - forClassName:@"SessionController"];
|
| - [SessionWindowUnarchiver setClass:[CRWSessionStorage class]
|
| - forClassName:@"CRWSessionController"];
|
| - [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class]
|
| - forClassName:@"SessionEntry"];
|
| - [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class]
|
| - forClassName:@"CRWSessionEntry"];
|
| - [SessionWindowUnarchiver setClass:[SessionWindowIOS class]
|
| - forClassName:@"SessionWindow"];
|
| + [self setClass:[CRWSessionStorage class] forClassName:@"SessionController"];
|
| + [self setClass:[CRWSessionStorage class]
|
| + forClassName:@"CRWSessionController"];
|
| + [self setClass:[CRWNavigationItemStorage class] forClassName:@"SessionEntry"];
|
| + [self setClass:[CRWNavigationItemStorage class]
|
| + forClassName:@"CRWSessionEntry"];
|
| + [self setClass:[SessionWindowIOS class] forClassName:@"SessionWindow"];
|
|
|
| // TODO(crbug.com/661633): this alias was introduced between M58 and M59, so
|
| // remove it after M68 has shipped to stable.
|
| - [SessionWindowUnarchiver setClass:[CRWSessionStorage class]
|
| - forClassName:@"CRWNavigationManagerStorage"];
|
| - [SessionWindowUnarchiver
|
| - setClass:[CRWSessionCertificatePolicyCacheStorage class]
|
| + [self setClass:[CRWSessionStorage class]
|
| + forClassName:@"CRWNavigationManagerStorage"];
|
| + [self setClass:[CRWSessionCertificatePolicyCacheStorage class]
|
| forClassName:@"CRWSessionCertificatePolicyManager"];
|
| }
|
|
|
| @@ -101,7 +75,7 @@ const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
|
|
| // Maps save directories to the pending SessionWindow for the delayed
|
| // save behavior.
|
| - base::scoped_nsobject<NSMutableDictionary> _pendingWindows;
|
| + NSMutableDictionary<NSString*, SessionWindowIOS*>* _pendingWindows;
|
| }
|
|
|
| // Saves the session corresponding to |directory| on the background
|
| @@ -122,8 +96,8 @@ const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
| - (instancetype)init {
|
| self = [super init];
|
| if (self) {
|
| - _pendingWindows.reset([[NSMutableDictionary alloc] init]);
|
| - auto* pool = web::WebThread::GetBlockingPool();
|
| + _pendingWindows = [NSMutableDictionary dictionary];
|
| + base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool();
|
| _taskRunner = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
|
| }
|
| return self;
|
| @@ -138,27 +112,20 @@ const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
| - (void)performSaveToDirectoryInBackground:(NSString*)directory {
|
| DCHECK(directory);
|
| DCHECK([_pendingWindows objectForKey:directory] != nil);
|
| - UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication]
|
| - beginBackgroundTaskWithExpirationHandler:^{
|
| - }];
|
| - DCHECK(identifier != UIBackgroundTaskInvalid);
|
|
|
| // 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] retain];
|
| + SessionWindowIOS* localWindow = [_pendingWindows objectForKey:directory];
|
| [_pendingWindows removeObjectForKey:directory];
|
|
|
| _taskRunner->PostTask(
|
| - FROM_HERE, base::BindBlock(^{
|
| + FROM_HERE, base::MakeCriticalClosure(base::BindBlockArc(^{
|
| @try {
|
| [self performSaveWindow:localWindow toDirectory:directory];
|
| } @catch (NSException* e) {
|
| // Do nothing.
|
| }
|
| - [localWindow release];
|
| - [[UIApplication sharedApplication] endBackgroundTask:identifier];
|
| - }));
|
| + })));
|
| }
|
|
|
| // Saves a SessionWindowIOS in a given directory. In case the directory doesn't
|
| @@ -176,14 +143,17 @@ const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
| error:&error];
|
| DCHECK(result);
|
| if (!result) {
|
| - DLOG(ERROR) << "Error creating destination dir: "
|
| + DLOG(ERROR) << "Error creating destination directory: "
|
| + << base::SysNSStringToUTF8(directory) << ": "
|
| << base::SysNSStringToUTF8([error description]);
|
| return;
|
| }
|
| } else {
|
| DCHECK(isDir);
|
| if (!isDir) {
|
| - DLOG(ERROR) << "Destination Directory already exists and is a file";
|
| + DLOG(ERROR) << "Error creating destination directory: "
|
| + << base::SysNSStringToUTF8(directory) << ": "
|
| + << "file exists and is not a directory.";
|
| return;
|
| }
|
| }
|
| @@ -192,19 +162,21 @@ const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
| if (filename) {
|
| BOOL result = [NSKeyedArchiver archiveRootObject:window toFile:filename];
|
| DCHECK(result);
|
| - if (!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).
|
| - NSDictionary* attributeDict =
|
| - [NSDictionary dictionaryWithObject:NSFileProtectionComplete
|
| - forKey:NSFileProtectionKey];
|
| NSError* error = nil;
|
| - BOOL success = [[NSFileManager defaultManager] setAttributes:attributeDict
|
| - ofItemAtPath:filename
|
| - error:&error];
|
| + BOOL success = [[NSFileManager defaultManager]
|
| + setAttributes:@{NSFileProtectionKey : NSFileProtectionComplete}
|
| + ofItemAtPath:filename
|
| + error:&error];
|
| if (!success) {
|
| - DLOG(ERROR) << "Error encrypting session file"
|
| + DLOG(ERROR) << "Error encrypting session file: "
|
| + << base::SysNSStringToUTF8(filename) << ": "
|
| << base::SysNSStringToUTF8([error description]);
|
| }
|
| }
|
| @@ -215,17 +187,12 @@ const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
| immediately:(BOOL)immediately {
|
| NSString* stashPath =
|
| base::SysUTF8ToNSString(browserState->GetStatePath().value());
|
| - // If there's an existing session window for |stashPath|, clear it before it's
|
| - // replaced.
|
| - SessionWindowIOS* pendingSession = base::mac::ObjCCast<SessionWindowIOS>(
|
| - [_pendingWindows objectForKey:stashPath]);
|
| - [pendingSession clearSessions];
|
| - // Set |window| as the pending save for |stashPath|.
|
| + BOOL hadPendingSession = [_pendingWindows objectForKey:stashPath] != nil;
|
| [_pendingWindows setObject:window forKey:stashPath];
|
| if (immediately) {
|
| [NSObject cancelPreviousPerformRequestsWithTarget:self];
|
| [self performSaveToDirectoryInBackground:stashPath];
|
| - } else if (!pendingSession) {
|
| + } else if (!hadPendingSession) {
|
| // If there wasn't previously a delayed save pending for |stashPath|,
|
| // enqueue one now.
|
| [self performSelector:@selector(performSaveToDirectoryInBackground:)
|
| @@ -239,40 +206,46 @@ const NSTimeInterval kSaveDelay = 2.5; // Value taken from Desktop Chrome.
|
| NSString* stashPath =
|
| base::SysUTF8ToNSString(browserState->GetStatePath().value());
|
| SessionWindowIOS* window =
|
| - [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath]
|
| - forBrowserState:browserState];
|
| + [self loadWindowFromPath:[self sessionFilePathForDirectory:stashPath]];
|
| return window;
|
| }
|
|
|
| -- (SessionWindowIOS*)loadWindowFromPath:(NSString*)path
|
| - forBrowserState:(ios::ChromeBrowserState*)browserState {
|
| - SessionWindowIOS* window = nil;
|
| +- (SessionWindowIOS*)loadWindowFromPath:(NSString*)sessionPath {
|
| @try {
|
| - NSData* data = [NSData dataWithContentsOfFile:path];
|
| - if (data) {
|
| - base::scoped_nsobject<SessionWindowUnarchiver> unarchiver([
|
| - [SessionWindowUnarchiver alloc] initForReadingWithData:data
|
| - browserState:browserState]);
|
| - window = [[[unarchiver decodeObjectForKey:@"root"] retain] autorelease];
|
| - }
|
| + NSData* data = [NSData dataWithContentsOfFile:sessionPath];
|
| + if (!data)
|
| + return nil;
|
| +
|
| + NSKeyedUnarchiver* unarchiver =
|
| + [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
|
| +
|
| + // Register compatibility aliases to support legacy saved sessions.
|
| + [unarchiver cr_registerCompatibilityAliases];
|
| + return [unarchiver decodeObjectForKey:kRootObjectKey];
|
| } @catch (NSException* exception) {
|
| - DLOG(ERROR) << "Error loading session file.";
|
| + DLOG(ERROR) << "Error loading session file: "
|
| + << base::SysNSStringToUTF8(sessionPath) << ": "
|
| + << base::SysNSStringToUTF8([exception reason]);
|
| + return nil;
|
| }
|
| - return window;
|
| }
|
|
|
| // Deletes the file containing the commands for the last session in the given
|
| // browserState directory.
|
| - (void)deleteLastSession:(NSString*)directory {
|
| - NSString* sessionFile = [self sessionFilePathForDirectory:directory];
|
| + NSString* sessionPath = [self sessionFilePathForDirectory:directory];
|
| _taskRunner->PostTask(
|
| - FROM_HERE, base::BindBlock(^{
|
| + FROM_HERE, base::BindBlockArc(^{
|
| base::ThreadRestrictions::AssertIOAllowed();
|
| NSFileManager* fileManager = [NSFileManager defaultManager];
|
| - if (![fileManager fileExistsAtPath:sessionFile])
|
| + if (![fileManager fileExistsAtPath:sessionPath])
|
| return;
|
| - if (![fileManager removeItemAtPath:sessionFile error:nil])
|
| - CHECK(false) << "Unable to delete session file.";
|
| +
|
| + NSError* error = nil;
|
| + if (![fileManager removeItemAtPath:sessionPath error:nil])
|
| + CHECK(false) << "Unable to delete session file: "
|
| + << base::SysNSStringToUTF8(sessionPath) << ": "
|
| + << base::SysNSStringToUTF8([error description]);
|
| }));
|
| }
|
|
|
|
|