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;
......
...@@ -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