Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Unified Diff: ios/chrome/browser/crash_report/crash_restore_helper.mm

Issue 2585233003: Upstream Chrome on iOS source code [2/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | ios/chrome/browser/crash_report/crash_restore_helper_unittest.mm » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ios/chrome/browser/crash_report/crash_restore_helper.mm
diff --git a/ios/chrome/browser/crash_report/crash_restore_helper.mm b/ios/chrome/browser/crash_report/crash_restore_helper.mm
new file mode 100644
index 0000000000000000000000000000000000000000..ad2b05f963ad6bad12ca8240ae26bedf3f1a5739
--- /dev/null
+++ b/ios/chrome/browser/crash_report/crash_restore_helper.mm
@@ -0,0 +1,307 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/crash_report/crash_restore_helper.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+#include "components/infobars/core/infobar.h"
+#include "components/infobars/core/infobar_manager.h"
+#include "components/sessions/core/tab_restore_service.h"
+#include "components/sessions/ios/ios_live_tab.h"
+#include "components/strings/grit/components_chromium_strings.h"
+#include "components/strings/grit/components_google_chrome_strings.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/crash_report/breakpad_helper.h"
+#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
+#import "ios/chrome/browser/sessions/session_service.h"
+#import "ios/chrome/browser/sessions/session_window.h"
+#import "ios/chrome/browser/tabs/tab.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+#include "ios/chrome/grit/ios_theme_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+@protocol InfoBarManagerObserverBridgeProtocol
+- (void)infoBarRemoved:(infobars::InfoBar*)infobar;
+@end
+
+// Private methods.
+@interface CrashRestoreHelper ()<InfoBarManagerObserverBridgeProtocol>
+// Deletes the session file for the given browser state, optionally backing it
+// up beforehand to |backupFile| if it is not nil. This method returns YES in
+// case of success, NO otherwise.
+- (BOOL)deleteSessionForBrowserState:(ios::ChromeBrowserState*)browserState
+ backupFile:(NSString*)file;
+// Returns the path where the sessions for the main browser state are backed up.
+- (NSString*)sessionBackupPath;
+// Restores the sessions after a crash. It should only be called if
+// |moveAsideSessionInformation| was successful.
+- (BOOL)restoreSessionsAfterCrash;
+@end
+
+namespace {
+
+class InfoBarManagerObserverBridge : infobars::InfoBarManager::Observer {
+ public:
+ InfoBarManagerObserverBridge(
+ infobars::InfoBarManager* infoBarManager,
+ id<InfoBarManagerObserverBridgeProtocol> observer)
+ : infobars::InfoBarManager::Observer(),
+ manager_(infoBarManager),
+ observer_(observer) {
+ DCHECK(infoBarManager);
+ DCHECK(observer);
+ manager_->AddObserver(this);
+ }
+
+ ~InfoBarManagerObserverBridge() override {
+ if (manager_)
+ manager_->RemoveObserver(this);
+ }
+
+ void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override {
+ [observer_ infoBarRemoved:infobar];
+ }
+
+ void OnInfoBarReplaced(infobars::InfoBar* old_infobar,
+ infobars::InfoBar* new_infobar) override {
+ [observer_ infoBarRemoved:old_infobar];
+ }
+
+ void OnManagerShuttingDown(infobars::InfoBarManager* manager) override {
+ manager_->RemoveObserver(this);
+ manager_ = nullptr;
+ }
+
+ private:
+ infobars::InfoBarManager* manager_;
+ id<InfoBarManagerObserverBridgeProtocol> observer_;
+};
+
+// SessionCrashedInfoBarDelegate ----------------------------------------------
+
+// A delegate for the InfoBar shown when the previous session has crashed.
+class SessionCrashedInfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+ // Creates a session crashed infobar and adds it to |infobar_manager|.
+ static bool Create(infobars::InfoBarManager* infobar_manager,
+ CrashRestoreHelper* crash_restore_helper);
+
+ private:
+ SessionCrashedInfoBarDelegate(CrashRestoreHelper* crash_restore_helper);
+ ~SessionCrashedInfoBarDelegate() override;
+
+ // InfoBarDelegate:
+ InfoBarIdentifier GetIdentifier() const override;
+
+ // ConfirmInfoBarDelegate:
+ base::string16 GetMessageText() const override;
+ int GetButtons() const override;
+ base::string16 GetButtonLabel(InfoBarButton button) const override;
+ bool Accept() override;
+ int GetIconId() const override;
+
+ // The CrashRestoreHelper to restore sessions.
+ base::scoped_nsobject<CrashRestoreHelper> crash_restore_helper_;
+ // The TabModel to restore sessions to.
+ base::scoped_nsobject<TabModel> tab_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionCrashedInfoBarDelegate);
+};
+
+SessionCrashedInfoBarDelegate::SessionCrashedInfoBarDelegate(
+ CrashRestoreHelper* crash_restore_helper)
+ : crash_restore_helper_([crash_restore_helper retain]) {}
+
+SessionCrashedInfoBarDelegate::~SessionCrashedInfoBarDelegate() {}
+
+// static
+bool SessionCrashedInfoBarDelegate::Create(
+ infobars::InfoBarManager* infobar_manager,
+ CrashRestoreHelper* crash_restore_helper) {
+ DCHECK(infobar_manager);
+ std::unique_ptr<ConfirmInfoBarDelegate> delegate(
+ new SessionCrashedInfoBarDelegate(crash_restore_helper));
+ return !!infobar_manager->AddInfoBar(
+ infobar_manager->CreateConfirmInfoBar(std::move(delegate)));
+}
+
+infobars::InfoBarDelegate::InfoBarIdentifier
+SessionCrashedInfoBarDelegate::GetIdentifier() const {
+ return SESSION_CRASHED_INFOBAR_DELEGATE;
+}
+
+base::string16 SessionCrashedInfoBarDelegate::GetMessageText() const {
+ return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE);
+}
+
+int SessionCrashedInfoBarDelegate::GetButtons() const {
+ return BUTTON_OK;
+}
+
+base::string16 SessionCrashedInfoBarDelegate::GetButtonLabel(
+ InfoBarButton button) const {
+ DCHECK_EQ(BUTTON_OK, button);
+ return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON);
+}
+
+bool SessionCrashedInfoBarDelegate::Accept() {
+ // Accept should return NO if the infobar is going to be dismissed.
+ // Since |restoreSessionAfterCrash| returns YES if a single NTP tab is closed,
+ // which will dismiss the infobar, invert the bool.
+ return ![crash_restore_helper_ restoreSessionsAfterCrash];
+}
+
+int SessionCrashedInfoBarDelegate::GetIconId() const {
+ return IDR_IOS_INFOBAR_RESTORE_SESSION;
+}
+
+} // namespace
+
+@implementation CrashRestoreHelper {
+ ios::ChromeBrowserState* _browserState;
+ BOOL _needRestoration;
+ std::unique_ptr<InfoBarManagerObserverBridge> _infoBarBridge;
+ // The TabModel to restore sessions to.
+ base::scoped_nsobject<TabModel> _tabModel;
+
+ // Indicate that the session has been restored to tabs or to recently closed
+ // and should not be rerestored.
+ BOOL _sessionRestored;
+}
+
+- (id)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
+ if (self = [super init]) {
+ _browserState = browserState;
+ }
+ return self;
+}
+
+- (void)showRestoreIfNeeded:(TabModel*)tabModel {
+ if (!_needRestoration)
+ return;
+
+ // The last session didn't exit cleanly. Show an infobar to the user so
+ // that they can restore if they want. The delegate deletes itself when
+ // it is closed.
+ DCHECK([tabModel currentTab]);
+ infobars::InfoBarManager* infoBarManager =
+ [[tabModel currentTab] infoBarManager];
+ _tabModel.reset([tabModel retain]);
+ SessionCrashedInfoBarDelegate::Create(infoBarManager, self);
+ _infoBarBridge.reset(new InfoBarManagerObserverBridge(infoBarManager, self));
+}
+
+- (BOOL)deleteSessionForBrowserState:(ios::ChromeBrowserState*)browserState
+ backupFile:(NSString*)file {
+ SessionServiceIOS* sessionService = [SessionServiceIOS sharedService];
+ NSString* stashPath =
+ base::SysUTF8ToNSString(browserState->GetStatePath().value());
+ NSString* sessionPath =
+ [sessionService sessionFilePathForDirectory:stashPath];
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ if (![fileManager fileExistsAtPath:sessionPath])
+ return NO;
+ if (file) {
+ NSError* error = nil;
+ BOOL fileOperationSuccess =
+ [fileManager removeItemAtPath:file error:&error];
+ NSInteger errorCode = fileOperationSuccess ? 0 : [error code];
+ UMA_HISTOGRAM_SPARSE_SLOWLY("TabRestore.error_remove_backup_at_path",
+ errorCode);
+ if (!fileOperationSuccess && errorCode != NSFileNoSuchFileError) {
+ return NO;
+ }
+ fileOperationSuccess =
+ [fileManager moveItemAtPath:sessionPath toPath:file error:&error];
+ errorCode = fileOperationSuccess ? 0 : [error code];
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "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];
+ UMA_HISTOGRAM_SPARSE_SLOWLY("TabRestore.error_remove_session_at_path",
+ errorCode);
+ if (!fileOperationSuccess) {
+ return NO;
+ }
+ }
+ return YES;
+}
+
+- (NSString*)sessionBackupPath {
+ NSString* tmpDirectory = NSTemporaryDirectory();
+ return [tmpDirectory stringByAppendingPathComponent:@"session.bak"];
+}
+
+- (void)moveAsideSessionInformation {
+ // This may be the first time that the OTR browser state is being accessed, so
+ // ensure that the OTR ChromeBrowserState is created first.
+ ios::ChromeBrowserState* otrBrowserState =
+ _browserState->GetOffTheRecordChromeBrowserState();
+ [self deleteSessionForBrowserState:otrBrowserState backupFile:nil];
+ _needRestoration =
+ [self deleteSessionForBrowserState:_browserState
+ backupFile:[self sessionBackupPath]];
+}
+
+- (BOOL)restoreSessionsAfterCrash {
+ DCHECK(!_sessionRestored);
+ _sessionRestored = YES;
+ _infoBarBridge.reset();
+ SessionWindowIOS* sessionWindow = [[SessionServiceIOS sharedService]
+ loadWindowFromPath:[self sessionBackupPath]
+ forBrowserState:[_tabModel browserState]];
+ if (sessionWindow) {
+ breakpad_helper::WillStartCrashRestoration();
+ return [_tabModel restoreSessionWindow:sessionWindow];
+ }
+ return NO;
+}
+
+- (void)infoBarRemoved:(infobars::InfoBar*)infobar {
+ DCHECK(infobar->delegate());
+ if (_sessionRestored ||
+ infobar->delegate()->GetIdentifier() !=
+ infobars::InfoBarDelegate::SESSION_CRASHED_INFOBAR_DELEGATE) {
+ return;
+ }
+
+ // If the infobar is dismissed without restoring the tabs (either by closing
+ // it with the cross or after a navigation), all the entries will be added to
+ // the recently closed tabs.
+ _sessionRestored = YES;
+
+ SessionWindowIOS* window = [[SessionServiceIOS sharedService]
+ loadWindowFromPath:[self sessionBackupPath]
+ forBrowserState:[_tabModel browserState]];
+ DCHECK(window);
+ if (!window.unclaimedSessions)
+ return;
+ sessions::TabRestoreService* const tabRestoreService =
+ IOSChromeTabRestoreServiceFactory::GetForBrowserState(_browserState);
+ tabRestoreService->LoadTabsFromLastSession();
+
+ while (window.unclaimedSessions) {
+ // Add all tabs at the 0 position as the position is relative to an old
+ // tabModel.
+ std::unique_ptr<web::WebStateImpl> webState = [window nextSession];
+ tabRestoreService->CreateHistoricalTab(
+ sessions::IOSLiveTab::GetForWebState(webState.get()), 0);
+ }
+ return;
+}
+
+@end
« no previous file with comments | « no previous file | ios/chrome/browser/crash_report/crash_restore_helper_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698