Commit 64875586 authored by Mohammad Refaat's avatar Mohammad Refaat Committed by Commit Bot

Add Crash Restoration support for multi-window

After a crash info bar will be presented on all foreground scenes
if the user choose to restore all foreground windows will be restored.
if not all windows tabs for all windows which were opened on the last
run will be added to the recent closed tabs.

Bug: 1126311, 1114179
Change-Id: I80623012dc132e2702004d2dbc0bbcb410a7d91c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2415929
Commit-Queue: Mohammad Refaat <mrefaat@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810406}
parent d1dd2578
...@@ -482,8 +482,16 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData( ...@@ -482,8 +482,16 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
// browser state. // browser state.
BOOL needRestoration = NO; BOOL needRestoration = NO;
if (isPostCrashLaunch) { if (isPostCrashLaunch) {
needRestoration = [CrashRestoreHelper if (IsMultiwindowSupported()) {
moveAsideSessionInformationForBrowserState:chromeBrowserState]; NSSet<NSString*>* sessions =
[[PreviousSessionInfo sharedInstance] connectedSceneSessionsIDs];
needRestoration =
[CrashRestoreHelper moveAsideSessions:sessions
forBrowserState:chromeBrowserState];
} else {
needRestoration = [CrashRestoreHelper
moveAsideSessionInformationForBrowserState:chromeBrowserState];
}
} }
[[PreviousSessionInfo sharedInstance] resetConnectedSceneSessionIDs]; [[PreviousSessionInfo sharedInstance] resetConnectedSceneSessionIDs];
......
...@@ -75,6 +75,7 @@ source_set("crash_report_internal") { ...@@ -75,6 +75,7 @@ source_set("crash_report_internal") {
"//ios/chrome/browser/sessions:serialisation", "//ios/chrome/browser/sessions:serialisation",
"//ios/chrome/browser/sessions:session_service", "//ios/chrome/browser/sessions:session_service",
"//ios/chrome/browser/ui/infobars:feature_flags", "//ios/chrome/browser/ui/infobars:feature_flags",
"//ios/chrome/browser/ui/main:scene_state_header",
"//ios/chrome/browser/ui/util:multiwindow_util", "//ios/chrome/browser/ui/util:multiwindow_util",
"//ios/chrome/browser/web:tab_id_tab_helper", "//ios/chrome/browser/web:tab_id_tab_helper",
"//ios/chrome/browser/web_state_list", "//ios/chrome/browser/web_state_list",
...@@ -108,6 +109,7 @@ source_set("unit_tests") { ...@@ -108,6 +109,7 @@ source_set("unit_tests") {
"//ios/chrome/browser/metrics:previous_session_info", "//ios/chrome/browser/metrics:previous_session_info",
"//ios/chrome/browser/sessions:serialisation", "//ios/chrome/browser/sessions:serialisation",
"//ios/chrome/browser/sessions:session_service", "//ios/chrome/browser/sessions:session_service",
"//ios/chrome/browser/ui/util:multiwindow_util",
"//ios/chrome/browser/web_state_list:test_support", "//ios/chrome/browser/web_state_list:test_support",
"//ios/chrome/browser/web_state_list:web_state_list", "//ios/chrome/browser/web_state_list:web_state_list",
"//ios/chrome/test/ocmock", "//ios/chrome/test/ocmock",
......
...@@ -15,12 +15,21 @@ class ChromeBrowserState; ...@@ -15,12 +15,21 @@ class ChromeBrowserState;
- (instancetype)initWithBrowser:(Browser*)browser; - (instancetype)initWithBrowser:(Browser*)browser;
// Saves the session information stored on disk in temporary files and will // Saves the session information stored on disk for sessions with |sessionIDs|
// then delete those from their default location. This will ensure that the // in temporary files and will then delete those from their default location.
// user will then start from scratch, while allowing restoring their old // This will ensure that the user will then start from scratch, while allowing
// sessions. This method has to be called before the browser is created, or the // restoring their old sessions. This method has to be called before the browser
// session information will be overwritten. // is created, or the session information will be overwritten.
// Returns |YES| if the deletetion and backup was successful. // |sessionIDs| can be nil when multiple windows are not supported, and in that
// case only the default session will be moved.
// Returns |YES| if the at least one session deletion was successful.
+ (BOOL)moveAsideSessions:(NSSet<NSString*>*)sessionIDs
forBrowserState:(ChromeBrowserState*)browserState;
// Move the session information for Legacy non multiwindow supported OS.
// This method deletes the session from its default location, while
// allowing restoring it back later.
// Returns |YES| if the delettion and backup was successful.
+ (BOOL)moveAsideSessionInformationForBrowserState: + (BOOL)moveAsideSessionInformationForBrowserState:
(ChromeBrowserState*)browserState; (ChromeBrowserState*)browserState;
......
...@@ -27,12 +27,17 @@ ...@@ -27,12 +27,17 @@
#include "ios/chrome/browser/infobars/infobar_manager_impl.h" #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
#include "ios/chrome/browser/infobars/infobar_utils.h" #include "ios/chrome/browser/infobars/infobar_utils.h"
#import "ios/chrome/browser/main/browser.h" #import "ios/chrome/browser/main/browser.h"
#import "ios/chrome/browser/main/browser_list.h"
#import "ios/chrome/browser/main/browser_list_factory.h"
#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h" #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
#import "ios/chrome/browser/sessions/session_ios.h" #import "ios/chrome/browser/sessions/session_ios.h"
#import "ios/chrome/browser/sessions/session_restoration_browser_agent.h" #import "ios/chrome/browser/sessions/session_restoration_browser_agent.h"
#import "ios/chrome/browser/sessions/session_service_ios.h" #import "ios/chrome/browser/sessions/session_service_ios.h"
#import "ios/chrome/browser/sessions/session_window_ios.h" #import "ios/chrome/browser/sessions/session_window_ios.h"
#import "ios/chrome/browser/ui/infobars/infobar_feature.h" #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
#import "ios/chrome/browser/ui/main/scene_state.h"
#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
#import "ios/chrome/browser/ui/util/multi_window_support.h"
#include "ios/chrome/browser/web_state_list/web_state_list.h" #include "ios/chrome/browser/web_state_list/web_state_list.h"
#include "ios/chrome/grit/ios_theme_resources.h" #include "ios/chrome/grit/ios_theme_resources.h"
#import "ios/web/public/web_state.h" #import "ios/web/public/web_state.h"
...@@ -54,16 +59,32 @@ ...@@ -54,16 +59,32 @@
// case of success, NO otherwise. // case of success, NO otherwise.
+ (BOOL)deleteSessionForBrowserState:(ChromeBrowserState*)browserState + (BOOL)deleteSessionForBrowserState:(ChromeBrowserState*)browserState
backupFile:(NSString*)file; backupFile:(NSString*)file;
// Returns the path where the sessions for the main browser state are backed up.
+ (NSString*)sessionBackupPath; // Returns the path where the sessions with |sessionID| for the main browser
// state are backed up.
+ (NSString*)backupPathForSessionID:(NSString*)sessionID;
// Returns a list of IDs for all backed up sessions.
+ (NSArray<NSString*>*)backedupSessionIDs;
// Restores the sessions after a crash. It should only be called if // Restores the sessions after a crash. It should only be called if
// |moveAsideSessionInformation| for the browser state of the current browser // |moveAsideSessions:forBrowserState| for the browser state of the current
// was successful. // browser was successful.
- (BOOL)restoreSessionsAfterCrash; - (BOOL)restoreSessionsAfterCrash;
// The Browser instance associated with this crash restore helper.
@property(nonatomic) Browser* browser;
@end @end
namespace { namespace {
NSString* const kSessionBackupFileName =
@"session.bak"; // The session file name on disk.
NSString* const kSessionBackupDirectoryName =
@"Sessions"; // The name for directory which contains all session backup
// subdirectories for multiple sessions.
class InfoBarManagerObserverBridge : infobars::InfoBarManager::Observer { class InfoBarManagerObserverBridge : infobars::InfoBarManager::Observer {
public: public:
InfoBarManagerObserverBridge(infobars::InfoBarManager* infoBarManager, InfoBarManagerObserverBridge(infobars::InfoBarManager* infoBarManager,
...@@ -240,6 +261,72 @@ int SessionCrashedInfoBarDelegate::GetIconId() const { ...@@ -240,6 +261,72 @@ int SessionCrashedInfoBarDelegate::GetIconId() const {
_infoBarBridge.reset(new InfoBarManagerObserverBridge(infoBarManager, self)); _infoBarBridge.reset(new InfoBarManagerObserverBridge(infoBarManager, self));
} }
+ (BOOL)deleteSessions:(NSSet<NSString*>*)sessionIDs
forBrowserState:(ChromeBrowserState*)browserState
shouldBackup:(BOOL)shouldBackup {
BOOL partialSuccess = NO;
NSString* stashPath =
base::SysUTF8ToNSString(browserState->GetStatePath().value());
NSString* backupPath = nil;
for (NSString* sessionID in sessionIDs) {
NSString* sessionPath =
[SessionServiceIOS sessionPathForSessionID:sessionID
directory:stashPath];
if (shouldBackup)
backupPath = [self backupPathForSessionID:sessionID];
partialSuccess |= [[self class] deleteSessionFromPath:sessionPath
backupFile:backupPath];
}
return partialSuccess;
}
+ (BOOL)deleteSessionFromPath:(NSString*)sessionPath
backupFile:(NSString*)backupPath {
NSFileManager* fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:sessionPath])
return NO;
if (backupPath) {
NSError* error = nil;
BOOL fileOperationSuccess = [fileManager removeItemAtPath:backupPath
error:&error];
NSInteger errorCode = fileOperationSuccess ? 0 : [error code];
base::UmaHistogramSparse("TabRestore.error_remove_backup_at_path",
errorCode);
if (!fileOperationSuccess && errorCode != NSFileNoSuchFileError) {
return NO;
}
// Create the backup directory, if it doesn't exist.
NSString* directory = [backupPath stringByDeletingLastPathComponent];
[fileManager createDirectoryAtPath:directory
withIntermediateDirectories:YES
attributes:nil
error:&error];
fileOperationSuccess = [fileManager moveItemAtPath:sessionPath
toPath:backupPath
error:&error];
errorCode = fileOperationSuccess ? 0 : [error code];
base::UmaHistogramSparse("TabRestore.error_move_session_at_path_to_backup",
errorCode);
if (!fileOperationSuccess) {
return NO;
}
} else {
NSError* error;
BOOL fileOperationSuccess = [fileManager removeItemAtPath:sessionPath
error:&error];
NSInteger errorCode = fileOperationSuccess ? 0 : [error code];
base::UmaHistogramSparse("TabRestore.error_remove_session_at_path",
errorCode);
if (!fileOperationSuccess) {
return NO;
}
}
return YES;
}
+ (BOOL)deleteSessionForBrowserState:(ChromeBrowserState*)browserState + (BOOL)deleteSessionForBrowserState:(ChromeBrowserState*)browserState
backupFile:(NSString*)file { backupFile:(NSString*)file {
NSString* stashPath = NSString* stashPath =
...@@ -258,6 +345,7 @@ int SessionCrashedInfoBarDelegate::GetIconId() const { ...@@ -258,6 +345,7 @@ int SessionCrashedInfoBarDelegate::GetIconId() const {
if (!fileOperationSuccess && errorCode != NSFileNoSuchFileError) { if (!fileOperationSuccess && errorCode != NSFileNoSuchFileError) {
return NO; return NO;
} }
fileOperationSuccess = fileOperationSuccess =
[fileManager moveItemAtPath:sessionPath toPath:file error:&error]; [fileManager moveItemAtPath:sessionPath toPath:file error:&error];
errorCode = fileOperationSuccess ? 0 : [error code]; errorCode = fileOperationSuccess ? 0 : [error code];
...@@ -280,36 +368,122 @@ int SessionCrashedInfoBarDelegate::GetIconId() const { ...@@ -280,36 +368,122 @@ int SessionCrashedInfoBarDelegate::GetIconId() const {
return YES; return YES;
} }
+ (NSString*)sessionBackupPath { + (NSString*)backupPathForSessionID:(NSString*)sessionID {
NSString* tmpDirectory = NSTemporaryDirectory(); NSString* tmpDirectory = NSTemporaryDirectory();
return [tmpDirectory stringByAppendingPathComponent:@"session.bak"]; if (!sessionID || !sessionID.length)
return [tmpDirectory stringByAppendingPathComponent:kSessionBackupFileName];
return [NSString pathWithComponents:@[
tmpDirectory, kSessionBackupDirectoryName, sessionID, kSessionBackupFileName
]];
}
+ (NSString*)backupSessionsDirectoryPath {
NSString* tmpDirectory = NSTemporaryDirectory();
return
[tmpDirectory stringByAppendingPathComponent:kSessionBackupDirectoryName];
}
+ (NSArray<NSString*>*)backedupSessionPaths {
if (!IsMultiwindowSupported())
return @[ [[self class] backupPathForSessionID:nil] ];
NSString* sessionsDirectoryPath = [[self class] backupSessionsDirectoryPath];
NSArray<NSString*>* sessionsIDs = [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:sessionsDirectoryPath
error:nil];
NSMutableArray<NSString*>* sessionFilePaths =
[[NSMutableArray alloc] initWithCapacity:sessionsIDs.count];
for (NSString* sessionID in sessionsIDs) {
[sessionFilePaths
addObject:[[self class] backupPathForSessionID:sessionID]];
}
return sessionFilePaths;
}
+ (NSArray<NSString*>*)backedupSessionIDs {
if (!IsMultiwindowSupported())
return @[ @"" ];
NSString* sessionsDirectoryPath = [[self class] backupSessionsDirectoryPath];
return [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:sessionsDirectoryPath
error:nil];
} }
+ (BOOL)moveAsideSessionInformationForBrowserState: + (BOOL)moveAsideSessionInformationForBrowserState:
(ChromeBrowserState*)browserState { (ChromeBrowserState*)browserState {
DCHECK(!IsMultiwindowSupported());
[self deleteSessionForBrowserState:browserState
->GetOffTheRecordChromeBrowserState()
backupFile:nil];
return [self deleteSessionForBrowserState:browserState
backupFile:[self backupPathForSessionID:nil]];
}
+ (BOOL)moveAsideSessions:(NSSet<NSString*>*)sessionIDs
forBrowserState:(ChromeBrowserState*)browserState {
// This may be the first time that the OTR browser state is being accessed, so // This may be the first time that the OTR browser state is being accessed, so
// ensure that the OTR ChromeBrowserState is created first. // ensure that the OTR ChromeBrowserState is created first.
ChromeBrowserState* otrBrowserState = ChromeBrowserState* otrBrowserState =
browserState->GetOffTheRecordChromeBrowserState(); browserState->GetOffTheRecordChromeBrowserState();
[self deleteSessionForBrowserState:otrBrowserState backupFile:nil]; [self deleteSessions:sessionIDs
return [self deleteSessionForBrowserState:browserState forBrowserState:otrBrowserState
backupFile:[self sessionBackupPath]]; shouldBackup:NO];
return [self deleteSessions:sessionIDs
forBrowserState:browserState
shouldBackup:YES];
} }
- (BOOL)restoreSessionsAfterCrash { - (BOOL)restoreSessionsAfterCrash {
CrashRestoreHelper* strongSelf = self;
DCHECK(!_sessionRestored); DCHECK(!_sessionRestored);
_sessionRestored = YES; _sessionRestored = YES;
_infoBarBridge.reset(); _infoBarBridge.reset();
BrowserList* browserList = BrowserListFactory::GetForBrowserState(
SessionIOS* session = [[SessionServiceIOS sharedService] strongSelf.browser->GetBrowserState());
loadSessionFromPath:[[self class] sessionBackupPath]];
if (!session)
return NO;
DCHECK_EQ(session.sessionWindows.count, 1u);
breakpad_helper::WillStartCrashRestoration(); breakpad_helper::WillStartCrashRestoration();
return SessionRestorationBrowserAgent::FromBrowser(_browser) BOOL success = NO;
->RestoreSessionWindow(session.sessionWindows[0]); // First restore all conected sessions.
NSFileManager* fileManager = [NSFileManager defaultManager];
NSError* error = nil;
std::set<Browser*> regularBrowsers = browserList->AllRegularBrowsers();
for (Browser* browser : regularBrowsers) {
NSString* sessionID = SceneStateBrowserAgent::FromBrowser(browser)
->GetSceneState()
.sceneSessionID;
NSString* sessionPath =
[[strongSelf class] backupPathForSessionID:sessionID];
SessionIOS* session =
[[SessionServiceIOS sharedService] loadSessionFromPath:sessionPath];
if (!session)
continue;
success |= SessionRestorationBrowserAgent::FromBrowser(browser)
->RestoreSessionWindow(session.sessionWindows[0]);
// remove the backup directory for this session as it will not be moved
// back to its original browser state direcotry.
[fileManager
removeItemAtPath:[sessionPath stringByDeletingLastPathComponent]
error:&error];
}
// Now put non restored sessions files to its original location in the browser
// state directory.
Browser* anyBrowser = *regularBrowsers.begin();
NSString* stashPath = base::SysUTF8ToNSString(
anyBrowser->GetBrowserState()->GetStatePath().value());
NSArray<NSString*>* backedupSessionIDs =
[[strongSelf class] backedupSessionIDs];
for (NSString* sessionID in backedupSessionIDs) {
NSString* originalSessionPath =
[SessionServiceIOS sessionPathForSessionID:sessionID
directory:stashPath];
[fileManager
moveItemAtPath:[[strongSelf class] backupPathForSessionID:sessionID]
toPath:originalSessionPath
error:&error];
}
return success;
} }
- (void)infoBarRemoved:(infobars::InfoBar*)infobar { - (void)infoBarRemoved:(infobars::InfoBar*)infobar {
...@@ -325,25 +499,33 @@ int SessionCrashedInfoBarDelegate::GetIconId() const { ...@@ -325,25 +499,33 @@ int SessionCrashedInfoBarDelegate::GetIconId() const {
// the recently closed tabs. // the recently closed tabs.
_sessionRestored = YES; _sessionRestored = YES;
SessionIOS* session = [[SessionServiceIOS sharedService] NSArray<NSString*>* sessionsIDs = [[self class] backedupSessionIDs];
loadSessionFromPath:[[self class] sessionBackupPath]]; NSFileManager* fileManager = [NSFileManager defaultManager];
DCHECK_EQ(session.sessionWindows.count, 1u); NSError* error = nil;
for (NSString* sessionID in sessionsIDs) {
NSArray<CRWSessionStorage*>* sessions = session.sessionWindows[0].sessions; NSString* sessionPath = [[self class] backupPathForSessionID:sessionID];
if (!sessions.count) SessionIOS* session =
return; [[SessionServiceIOS sharedService] loadSessionFromPath:sessionPath];
sessions::TabRestoreService* const tabRestoreService = NSArray<CRWSessionStorage*>* sessions = session.sessionWindows[0].sessions;
IOSChromeTabRestoreServiceFactory::GetForBrowserState( if (!sessions.count)
_browser->GetBrowserState()); continue;
tabRestoreService->LoadTabsFromLastSession();
sessions::TabRestoreService* const tabRestoreService =
web::WebState::CreateParams params(_browser->GetBrowserState()); IOSChromeTabRestoreServiceFactory::GetForBrowserState(
for (CRWSessionStorage* session in sessions) { _browser->GetBrowserState());
auto live_tab = std::make_unique<sessions::RestoreIOSLiveTab>(session); tabRestoreService->LoadTabsFromLastSession();
// Add all tabs at the 0 position as the position is relative to an old
// tabModel. web::WebState::CreateParams params(_browser->GetBrowserState());
tabRestoreService->CreateHistoricalTab(live_tab.get(), 0); for (CRWSessionStorage* session in sessions) {
auto live_tab = std::make_unique<sessions::RestoreIOSLiveTab>(session);
// Add all tabs at the 0 position as the position is relative to an old
// tabModel.
tabRestoreService->CreateHistoricalTab(live_tab.get(), 0);
}
[fileManager
removeItemAtPath:[sessionPath stringByDeletingLastPathComponent]
error:&error];
} }
return; return;
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "ios/chrome/browser/crash_report/crash_restore_helper.h" #include "ios/chrome/browser/crash_report/crash_restore_helper.h"
#import "ios/chrome/browser/main/test_browser.h" #import "ios/chrome/browser/main/test_browser.h"
#import "ios/chrome/browser/sessions/session_service_ios.h" #import "ios/chrome/browser/sessions/session_service_ios.h"
#import "ios/chrome/browser/ui/util/multi_window_support.h"
#include "ios/web/public/test/web_task_environment.h" #include "ios/web/public/test/web_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -28,7 +29,7 @@ ...@@ -28,7 +29,7 @@
using testing::Return; using testing::Return;
@interface CrashRestoreHelper (Test) @interface CrashRestoreHelper (Test)
+ (NSString*)sessionBackupPath; + (NSString*)backupPathForSessionID:(NSString*)sessionID;
@end @end
namespace { namespace {
...@@ -45,6 +46,73 @@ class CrashRestoreHelperTest : public PlatformTest { ...@@ -45,6 +46,73 @@ class CrashRestoreHelperTest : public PlatformTest {
} }
protected: protected:
// Creates the session for |session_id|, if |session_id| is nil a session
// will be created in the default location.
// Returns |true| if the creation was successful.
bool CreateSession(NSString* session_id) {
NSFileManager* file_manager = [NSFileManager defaultManager];
ChromeBrowserState* browser_states[] = {
chrome_browser_state_.get(),
off_the_record_chrome_browser_state_,
};
NSString* backup_path =
[CrashRestoreHelper backupPathForSessionID:session_id];
[file_manager removeItemAtPath:backup_path error:nil];
NSData* data = [NSData dataWithBytes:"hello" length:5];
for (size_t index = 0; index < base::size(browser_states); ++index) {
NSString* state_path = base::SysUTF8ToNSString(
browser_states[index]->GetStatePath().value());
NSString* session_path =
[SessionServiceIOS sessionPathForSessionID:session_id
directory:state_path];
NSString* directory = [session_path stringByDeletingLastPathComponent];
if (![file_manager fileExistsAtPath:directory]) {
[file_manager createDirectoryAtPath:directory
withIntermediateDirectories:YES
attributes:nil
error:nil];
}
[file_manager createFileAtPath:session_path contents:data attributes:nil];
if (![file_manager fileExistsAtPath:session_path])
return false;
}
return true;
}
// Returns |true| if session for |session_id| was erased from its default
// location. if |session_id| is nil, the default session location is used.
bool IsSessionErased(NSString* session_id) {
NSFileManager* file_manager = [NSFileManager defaultManager];
ChromeBrowserState* browser_states[] = {
chrome_browser_state_.get(),
off_the_record_chrome_browser_state_,
};
for (size_t index = 0; index < base::size(browser_states); ++index) {
NSString* state_path = base::SysUTF8ToNSString(
browser_states[index]->GetStatePath().value());
NSString* session_path =
[SessionServiceIOS sessionPathForSessionID:session_id
directory:state_path];
if ([file_manager fileExistsAtPath:session_path])
return false;
}
return true;
}
// Returns |true| if the session with |session_id| was backed up correctly,
// and deletes the backup file. if |session_id| is nil, the default backup
// session location is used.
bool CheckAndDeleteSessionBackedUp(NSString* session_id) {
NSFileManager* file_manager = [NSFileManager defaultManager];
NSString* backup_path =
[CrashRestoreHelper backupPathForSessionID:session_id];
if (![file_manager fileExistsAtPath:backup_path])
return false;
[file_manager removeItemAtPath:backup_path error:nil];
return true;
}
web::WebTaskEnvironment task_environment_; web::WebTaskEnvironment task_environment_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_; std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
std::unique_ptr<TestBrowser> test_browser_; std::unique_ptr<TestBrowser> test_browser_;
...@@ -52,39 +120,33 @@ class CrashRestoreHelperTest : public PlatformTest { ...@@ -52,39 +120,33 @@ class CrashRestoreHelperTest : public PlatformTest {
CrashRestoreHelper* helper_; CrashRestoreHelper* helper_;
}; };
TEST_F(CrashRestoreHelperTest, MoveAsideTest) { // Tests that moving session work correctly when multiple windows are not
NSString* backup_path = [CrashRestoreHelper sessionBackupPath]; // supported.
NSFileManager* file_manager = [NSFileManager defaultManager]; TEST_F(CrashRestoreHelperTest, MoveAsideSingleSession) {
[file_manager removeItemAtPath:backup_path error:NULL]; // This test is only enabled when multi-window is disabled.
if (IsMultiwindowSupported())
NSData* data = [NSData dataWithBytes:"hello" length:5]; return;
ChromeBrowserState* browser_states[] = { ASSERT_TRUE(CreateSession(nil));
chrome_browser_state_.get(),
off_the_record_chrome_browser_state_,
};
for (size_t index = 0; index < base::size(browser_states); ++index) {
NSString* state_path =
base::SysUTF8ToNSString(browser_states[index]->GetStatePath().value());
NSString* session_path =
[SessionServiceIOS sessionPathForDirectory:state_path];
[file_manager createFileAtPath:session_path contents:data attributes:nil];
ASSERT_EQ(YES, [file_manager fileExistsAtPath:session_path]);
}
[CrashRestoreHelper [CrashRestoreHelper
moveAsideSessionInformationForBrowserState:chrome_browser_state_.get()]; moveAsideSessionInformationForBrowserState:chrome_browser_state_.get()];
EXPECT_TRUE(IsSessionErased(nil));
EXPECT_EQ(YES, CheckAndDeleteSessionBackedUp(nil));
}
for (size_t index = 0; index < base::size(browser_states); ++index) { // Tests that moving session work correctly when multiple windows are supported.
NSString* state_path = TEST_F(CrashRestoreHelperTest, MoveAsideMultipleSessions) {
base::SysUTF8ToNSString(browser_states[index]->GetStatePath().value()); NSSet<NSString*>* session_ids =
NSString* session_path = [NSSet setWithObjects:@"session_1", @"session_2", nil];
[SessionServiceIOS sessionPathForDirectory:state_path]; for (NSString* session_id in session_ids) {
EXPECT_EQ(NO, [file_manager fileExistsAtPath:session_path]); ASSERT_TRUE(CreateSession(session_id));
} }
EXPECT_EQ(YES, [file_manager fileExistsAtPath:backup_path]); [CrashRestoreHelper moveAsideSessions:session_ids
[file_manager removeItemAtPath:backup_path error:NULL]; forBrowserState:chrome_browser_state_.get()];
for (NSString* session_id in session_ids) {
EXPECT_TRUE(IsSessionErased(session_id));
EXPECT_EQ(YES, CheckAndDeleteSessionBackedUp(session_id));
}
} }
} // namespace } // namespace
...@@ -112,6 +112,11 @@ enum class DeviceBatteryState { ...@@ -112,6 +112,11 @@ enum class DeviceBatteryState {
// Reset to NO after resetSessionRestorationFlag call. // Reset to NO after resetSessionRestorationFlag call.
@property(nonatomic, readonly) BOOL terminatedDuringSessionRestoration; @property(nonatomic, readonly) BOOL terminatedDuringSessionRestoration;
// The list of the session IDs for all the connected scenes, used for crash
// restoration.
@property(nonatomic, readonly)
NSMutableSet<NSString*>* connectedSceneSessionsIDs;
// Singleton PreviousSessionInfo. During the lifetime of the app, the returned // Singleton PreviousSessionInfo. During the lifetime of the app, the returned
// object is the same, and describes the previous session, even after a new // object is the same, and describes the previous session, even after a new
// session has started (by calling beginRecordingCurrentSession). // session has started (by calling beginRecordingCurrentSession).
......
...@@ -116,10 +116,6 @@ NSString* const kPreviousSessionInfoConnectedSceneSessionIDs = ...@@ -116,10 +116,6 @@ NSString* const kPreviousSessionInfoConnectedSceneSessionIDs =
// Can be greater than one if multiple sessions are being restored in parallel. // Can be greater than one if multiple sessions are being restored in parallel.
@property(atomic, assign) int numberOfSessionsBeingRestored; @property(atomic, assign) int numberOfSessionsBeingRestored;
// The list of the session IDs for all the connected scenes, used for crash
// restoration.
@property(nonatomic, strong) NSMutableSet<NSString*>* connectedSceneSessionsIDs;
// Redefined to be read-write. // Redefined to be read-write.
@property(nonatomic, assign) NSInteger availableDeviceStorage; @property(nonatomic, assign) NSInteger availableDeviceStorage;
@property(nonatomic, assign) float deviceBatteryLevel; @property(nonatomic, assign) float deviceBatteryLevel;
...@@ -134,6 +130,7 @@ NSString* const kPreviousSessionInfoConnectedSceneSessionIDs = ...@@ -134,6 +130,7 @@ NSString* const kPreviousSessionInfoConnectedSceneSessionIDs =
@property(nonatomic, strong) NSString* OSVersion; @property(nonatomic, strong) NSString* OSVersion;
@property(nonatomic, strong) NSDate* sessionEndTime; @property(nonatomic, strong) NSDate* sessionEndTime;
@property(nonatomic, assign) BOOL terminatedDuringSessionRestoration; @property(nonatomic, assign) BOOL terminatedDuringSessionRestoration;
@property(nonatomic, strong) NSMutableSet<NSString*>* connectedSceneSessionsIDs;
@end @end
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
@class SessionIOS; @class SessionIOS;
@class SessionIOSFactory; @class SessionIOSFactory;
namespace session_constants {
NSString* const kSessionsDirectory = @"Sessions";
}
// A singleton service for saving the current session. Can either save on a // A singleton service for saving the current session. Can either save on a
// delay or immediately. Saving is always performed on a separate thread. // delay or immediately. Saving is always performed on a separate thread.
@interface SessionServiceIOS : NSObject @interface SessionServiceIOS : NSObject
...@@ -52,6 +56,10 @@ ...@@ -52,6 +56,10 @@
- (void)deleteSessions:(NSArray<NSString*>*)sessionIDs - (void)deleteSessions:(NSArray<NSString*>*)sessionIDs
fromBrowserStateDirectory:(NSString*)directory; fromBrowserStateDirectory:(NSString*)directory;
// Returns the path of the session with |sessionID| within a |directory|.
+ (NSString*)sessionPathForSessionID:(NSString*)sessionID
directory:(NSString*)directory;
// Returns the path of the session file for |directory|. // Returns the path of the session file for |directory|.
+ (NSString*)sessionPathForDirectory:(NSString*)directory; + (NSString*)sessionPathForDirectory:(NSString*)directory;
......
...@@ -48,6 +48,8 @@ NSString* const kRootObjectKey = @"root"; // Key for the root object. ...@@ -48,6 +48,8 @@ NSString* const kRootObjectKey = @"root"; // Key for the root object.
NSString* const kSessionDirectory = NSString* const kSessionDirectory =
@"Sessions"; // The directory name inside BrowserState directory which @"Sessions"; // The directory name inside BrowserState directory which
// contain all sessions directories. // contain all sessions directories.
NSString* const kSessionFileName =
@"session.plist"; // The session file name on disk.
} }
@implementation NSKeyedUnarchiver (CrLegacySessionCompatibility) @implementation NSKeyedUnarchiver (CrLegacySessionCompatibility)
...@@ -190,7 +192,16 @@ NSString* const kSessionDirectory = ...@@ -190,7 +192,16 @@ NSString* const kSessionDirectory =
} }
+ (NSString*)sessionPathForDirectory:(NSString*)directory { + (NSString*)sessionPathForDirectory:(NSString*)directory {
return [directory stringByAppendingPathComponent:@"session.plist"]; return [directory stringByAppendingPathComponent:kSessionFileName];
}
+ (NSString*)sessionPathForSessionID:(NSString*)sessionID
directory:(NSString*)directory {
if (!sessionID)
return [[self class] sessionPathForDirectory:directory];
return [NSString pathWithComponents:@[
directory, kSessionDirectory, sessionID, kSessionFileName
]];
} }
#pragma mark - Private methods #pragma mark - Private methods
......
...@@ -179,7 +179,7 @@ source_set("main") { ...@@ -179,7 +179,7 @@ source_set("main") {
"//ios/chrome/browser/ui/thumb_strip:feature_flags", "//ios/chrome/browser/ui/thumb_strip:feature_flags",
"//ios/chrome/browser/ui/translate:legacy_translate", "//ios/chrome/browser/ui/translate:legacy_translate",
"//ios/chrome/browser/ui/util:multiwindow_util", "//ios/chrome/browser/ui/util:multiwindow_util",
"//ios/chrome/browser/url_loading", "//ios/chrome/browser/url_loading:url_loading_params_header",
"//ios/chrome/browser/web", "//ios/chrome/browser/web",
"//ios/chrome/browser/web:tab_helper_delegates", "//ios/chrome/browser/web:tab_helper_delegates",
"//ios/chrome/browser/web:web_internal", "//ios/chrome/browser/web:web_internal",
......
...@@ -76,6 +76,9 @@ typedef NS_ENUM(NSUInteger, SceneActivationLevel) { ...@@ -76,6 +76,9 @@ typedef NS_ENUM(NSUInteger, SceneActivationLevel) {
@property(nonatomic, strong, readonly) id<BrowserInterfaceProvider> @property(nonatomic, strong, readonly) id<BrowserInterfaceProvider>
interfaceProvider; interfaceProvider;
// The persistent identifier for the scene session.
@property(nonatomic, readonly) NSString* sceneSessionID;
// True if First Run UI (terms of service & sync sign-in) is being presented // True if First Run UI (terms of service & sync sign-in) is being presented
// in a modal dialog. // in a modal dialog.
@property(nonatomic, assign) BOOL presentingFirstRunUI; @property(nonatomic, assign) BOOL presentingFirstRunUI;
......
...@@ -96,6 +96,14 @@ ...@@ -96,6 +96,14 @@
return _window; return _window;
} }
- (NSString*)sceneSessionID {
NSString* sessionID = nil;
if (@available(ios 13, *)) {
sessionID = _scene.session.persistentIdentifier;
}
return sessionID;
}
- (void)setActivationLevel:(SceneActivationLevel)newLevel { - (void)setActivationLevel:(SceneActivationLevel)newLevel {
if (_activationLevel == newLevel) { if (_activationLevel == newLevel) {
return; return;
......
...@@ -48,6 +48,16 @@ source_set("url_loading") { ...@@ -48,6 +48,16 @@ source_set("url_loading") {
] ]
} }
source_set("url_loading_params_header") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [ "url_loading_params.h" ]
deps = [
"//ios/chrome/browser/ui/commands",
"//ios/web/public",
"//ui/base",
]
}
source_set("test_support") { source_set("test_support") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
testonly = true testonly = true
......
...@@ -15,7 +15,7 @@ source_set("window_activities") { ...@@ -15,7 +15,7 @@ source_set("window_activities") {
":ios_move_tab_activity_type_buildflags", ":ios_move_tab_activity_type_buildflags",
"//base", "//base",
"//ios/chrome/browser:chrome_url_constants", "//ios/chrome/browser:chrome_url_constants",
"//ios/chrome/browser/url_loading", "//ios/chrome/browser/url_loading:url_loading_params_header",
"//ios/web/public/navigation", "//ios/web/public/navigation",
"//net", "//net",
"//url", "//url",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment