// Copyright (c) 2011 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. #include "chrome/browser/sessions/session_restore.h" #include <algorithm> #include <list> #include <set> #include <vector> #include "base/callback.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/histogram.h" #include "base/stl_util-inl.h" #include "base/string_util.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_service.h" #include "chrome/browser/sessions/session_types.h" #include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_window.h" #include "content/browser/renderer_host/render_widget_host.h" #include "content/browser/renderer_host/render_widget_host_view.h" #include "content/browser/tab_contents/navigation_controller.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents_view.h" #include "content/common/notification_registrar.h" #include "content/common/notification_service.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/boot_times_loader.h" #include "chrome/browser/chromeos/network_state_notifier.h" #endif // Are we in the process of restoring? static bool restoring = false; namespace { // TabLoader ------------------------------------------------------------------ // Initial delay (see class decription for details). static const int kInitialDelayTimerMS = 100; // TabLoader is responsible for loading tabs after session restore creates // tabs. New tabs are loaded after the current tab finishes loading, or a delay // is reached (initially kInitialDelayTimerMS). If the delay is reached before // a tab finishes loading a new tab is loaded and the time of the delay // doubled. When all tabs are loading TabLoader deletes itself. // // This is not part of SessionRestoreImpl so that synchronous destruction // of SessionRestoreImpl doesn't have timing problems. class TabLoader : public NotificationObserver { public: explicit TabLoader(base::TimeTicks restore_started); ~TabLoader(); // Schedules a tab for loading. void ScheduleLoad(NavigationController* controller); // Notifies the loader that a tab has been scheduled for loading through // some other mechanism. void TabIsLoading(NavigationController* controller); // Invokes |LoadNextTab| to load a tab. // // This must be invoked once to start loading. void StartLoading(); private: typedef std::set<NavigationController*> TabsLoading; typedef std::list<NavigationController*> TabsToLoad; typedef std::set<RenderWidgetHost*> RenderWidgetHostSet; // Loads the next tab. If there are no more tabs to load this deletes itself, // otherwise |force_load_timer_| is restarted. void LoadNextTab(); // NotificationObserver method. Removes the specified tab and loads the next // tab. virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); // Removes the listeners from the specified tab and removes the tab from // the set of tabs to load and list of tabs we're waiting to get a load // from. void RemoveTab(NavigationController* tab); // Invoked from |force_load_timer_|. Doubles |force_load_delay_| and invokes // |LoadNextTab| to load the next tab void ForceLoadTimerFired(); // Returns the RenderWidgetHost associated with a tab if there is one, // NULL otherwise. static RenderWidgetHost* GetRenderWidgetHost(NavigationController* tab); // Register for necessary notificaitons on a tab navigation controller. void RegisterForNotifications(NavigationController* controller); // Called when a tab goes away or a load completes. void HandleTabClosedOrLoaded(NavigationController* controller); NotificationRegistrar registrar_; // Current delay before a new tab is loaded. See class description for // details. int64 force_load_delay_; // Has Load been invoked? bool loading_; // Have we recorded the times for a tab paint? bool got_first_paint_; // The set of tabs we've initiated loading on. This does NOT include the // selected tabs. TabsLoading tabs_loading_; // The tabs we need to load. TabsToLoad tabs_to_load_; // The renderers we have started loading into. RenderWidgetHostSet render_widget_hosts_loading_; // The renderers we have loaded and are waiting on to paint. RenderWidgetHostSet render_widget_hosts_to_paint_; // The number of tabs that have been restored. int tab_count_; base::OneShotTimer<TabLoader> force_load_timer_; // The time the restore process started. base::TimeTicks restore_started_; DISALLOW_COPY_AND_ASSIGN(TabLoader); }; TabLoader::TabLoader(base::TimeTicks restore_started) : force_load_delay_(kInitialDelayTimerMS), loading_(false), got_first_paint_(false), tab_count_(0), restore_started_(restore_started) { } TabLoader::~TabLoader() { DCHECK((got_first_paint_ || render_widget_hosts_to_paint_.empty()) && tabs_loading_.empty() && tabs_to_load_.empty()); } void TabLoader::ScheduleLoad(NavigationController* controller) { DCHECK(controller); DCHECK(find(tabs_to_load_.begin(), tabs_to_load_.end(), controller) == tabs_to_load_.end()); tabs_to_load_.push_back(controller); RegisterForNotifications(controller); } void TabLoader::TabIsLoading(NavigationController* controller) { DCHECK(controller); DCHECK(find(tabs_loading_.begin(), tabs_loading_.end(), controller) == tabs_loading_.end()); tabs_loading_.insert(controller); RenderWidgetHost* render_widget_host = GetRenderWidgetHost(controller); DCHECK(render_widget_host); render_widget_hosts_loading_.insert(render_widget_host); RegisterForNotifications(controller); } void TabLoader::StartLoading() { registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DID_PAINT, NotificationService::AllSources()); #if defined(OS_CHROMEOS) if (chromeos::NetworkStateNotifier::is_connected()) { loading_ = true; LoadNextTab(); } else { // Start listening to network state notification now. registrar_.Add(this, NotificationType::NETWORK_STATE_CHANGED, NotificationService::AllSources()); } #else loading_ = true; LoadNextTab(); #endif } void TabLoader::LoadNextTab() { if (!tabs_to_load_.empty()) { NavigationController* tab = tabs_to_load_.front(); DCHECK(tab); tabs_loading_.insert(tab); tabs_to_load_.pop_front(); tab->LoadIfNecessary(); if (tab->tab_contents()) { int tab_index; Browser* browser = Browser::GetBrowserForController(tab, &tab_index); if (browser && browser->active_index() != tab_index) { // By default tabs are marked as visible. As only the active tab is // visible we need to explicitly tell non-active tabs they are hidden. // Without this call non-active tabs are not marked as backgrounded. // // NOTE: We need to do this here rather than when the tab is added to // the Browser as at that time not everything has been created, so that // the call would do nothing. tab->tab_contents()->WasHidden(); } } } if (!tabs_to_load_.empty()) { force_load_timer_.Stop(); // Each time we load a tab we also set a timer to force us to start loading // the next tab if this one doesn't load quickly enough. force_load_timer_.Start( base::TimeDelta::FromMilliseconds(force_load_delay_), this, &TabLoader::ForceLoadTimerFired); } } void TabLoader::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type.value) { #if defined(OS_CHROMEOS) case NotificationType::NETWORK_STATE_CHANGED: { chromeos::NetworkStateDetails* state_details = Details<chromeos::NetworkStateDetails>(details).ptr(); switch (state_details->state()) { case chromeos::NetworkStateDetails::CONNECTED: if (!loading_) { loading_ = true; LoadNextTab(); } // Start loading break; case chromeos::NetworkStateDetails::CONNECTING: case chromeos::NetworkStateDetails::DISCONNECTED: // Disconnected while loading. Set loading_ false so // that it stops trying to load next tab. loading_ = false; break; default: NOTREACHED() << "Unknown nework state notification:" << state_details->state(); } break; } #endif case NotificationType::LOAD_START: { // Add this render_widget_host to the set of those we're waiting for // paints on. We want to only record stats for paints that occur after // a load has finished. NavigationController* tab = Source<NavigationController>(source).ptr(); RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); DCHECK(render_widget_host); render_widget_hosts_loading_.insert(render_widget_host); break; } case NotificationType::TAB_CONTENTS_DESTROYED: { TabContents* tab_contents = Source<TabContents>(source).ptr(); if (!got_first_paint_) { RenderWidgetHost* render_widget_host = GetRenderWidgetHost(&tab_contents->controller()); render_widget_hosts_loading_.erase(render_widget_host); } HandleTabClosedOrLoaded(&tab_contents->controller()); break; } case NotificationType::LOAD_STOP: { NavigationController* tab = Source<NavigationController>(source).ptr(); render_widget_hosts_to_paint_.insert(GetRenderWidgetHost(tab)); HandleTabClosedOrLoaded(tab); break; } case NotificationType::RENDER_WIDGET_HOST_DID_PAINT: { if (!got_first_paint_) { RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr(); if (render_widget_hosts_to_paint_.find(render_widget_host) != render_widget_hosts_to_paint_.end()) { // Got a paint for one of our renderers, so record time. got_first_paint_ = true; base::TimeDelta time_to_paint = base::TimeTicks::Now() - restore_started_; UMA_HISTOGRAM_CUSTOM_TIMES( "SessionRestore.FirstTabPainted", time_to_paint, base::TimeDelta::FromMilliseconds(10), base::TimeDelta::FromSeconds(100), 100); // Record a time for the number of tabs, to help track down // contention. std::string time_for_count = StringPrintf("SessionRestore.FirstTabPainted_%d", tab_count_); base::Histogram* counter_for_count = base::Histogram::FactoryTimeGet( time_for_count, base::TimeDelta::FromMilliseconds(10), base::TimeDelta::FromSeconds(100), 100, base::Histogram::kUmaTargetedHistogramFlag); counter_for_count->AddTime(time_to_paint); } else if (render_widget_hosts_loading_.find(render_widget_host) == render_widget_hosts_loading_.end()) { // If this is a host for a tab we're not loading some other tab // has rendered and there's no point tracking the time. This could // happen because the user opened a different tab or restored tabs // to an already existing browser and an existing tab painted. got_first_paint_ = true; } } break; } default: NOTREACHED() << "Unknown notification received:" << type.value; } // Delete ourselves when we're not waiting for any more notifications. if ((got_first_paint_ || render_widget_hosts_to_paint_.empty()) && tabs_loading_.empty() && tabs_to_load_.empty()) delete this; } void TabLoader::RemoveTab(NavigationController* tab) { registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, Source<TabContents>(tab->tab_contents())); registrar_.Remove(this, NotificationType::LOAD_STOP, Source<NavigationController>(tab)); registrar_.Remove(this, NotificationType::LOAD_START, Source<NavigationController>(tab)); TabsLoading::iterator i = tabs_loading_.find(tab); if (i != tabs_loading_.end()) tabs_loading_.erase(i); TabsToLoad::iterator j = find(tabs_to_load_.begin(), tabs_to_load_.end(), tab); if (j != tabs_to_load_.end()) tabs_to_load_.erase(j); } void TabLoader::ForceLoadTimerFired() { force_load_delay_ *= 2; LoadNextTab(); } RenderWidgetHost* TabLoader::GetRenderWidgetHost(NavigationController* tab) { TabContents* tab_contents = tab->tab_contents(); if (tab_contents) { RenderWidgetHostView* render_widget_host_view = tab_contents->GetRenderWidgetHostView(); if (render_widget_host_view) return render_widget_host_view->GetRenderWidgetHost(); } return NULL; } void TabLoader::RegisterForNotifications(NavigationController* controller) { registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, Source<TabContents>(controller->tab_contents())); registrar_.Add(this, NotificationType::LOAD_STOP, Source<NavigationController>(controller)); registrar_.Add(this, NotificationType::LOAD_START, Source<NavigationController>(controller)); ++tab_count_; } void TabLoader::HandleTabClosedOrLoaded(NavigationController* tab) { RemoveTab(tab); if (loading_) LoadNextTab(); if (tabs_loading_.empty() && tabs_to_load_.empty()) { base::TimeDelta time_to_load = base::TimeTicks::Now() - restore_started_; UMA_HISTOGRAM_CUSTOM_TIMES( "SessionRestore.AllTabsLoaded", time_to_load, base::TimeDelta::FromMilliseconds(10), base::TimeDelta::FromSeconds(100), 100); // Record a time for the number of tabs, to help track down contention. std::string time_for_count = StringPrintf("SessionRestore.AllTabsLoaded_%d", tab_count_); base::Histogram* counter_for_count = base::Histogram::FactoryTimeGet( time_for_count, base::TimeDelta::FromMilliseconds(10), base::TimeDelta::FromSeconds(100), 100, base::Histogram::kUmaTargetedHistogramFlag); counter_for_count->AddTime(time_to_load); } } // SessionRestoreImpl --------------------------------------------------------- // SessionRestoreImpl is responsible for fetching the set of tabs to create // from SessionService. SessionRestoreImpl deletes itself when done. class SessionRestoreImpl : public NotificationObserver { public: SessionRestoreImpl(Profile* profile, Browser* browser, bool synchronous, bool clobber_existing_window, bool always_create_tabbed_browser, const std::vector<GURL>& urls_to_open) : profile_(profile), browser_(browser), synchronous_(synchronous), clobber_existing_window_(clobber_existing_window), always_create_tabbed_browser_(always_create_tabbed_browser), urls_to_open_(urls_to_open), restore_started_(base::TimeTicks::Now()) { } Browser* Restore() { SessionService* session_service = profile_->GetSessionService(); DCHECK(session_service); SessionService::SessionCallback* callback = NewCallback(this, &SessionRestoreImpl::OnGotSession); session_service->GetLastSession(&request_consumer_, callback); if (synchronous_) { bool old_state = MessageLoop::current()->NestableTasksAllowed(); MessageLoop::current()->SetNestableTasksAllowed(true); MessageLoop::current()->Run(); MessageLoop::current()->SetNestableTasksAllowed(old_state); Browser* browser = ProcessSessionWindows(&windows_); delete this; return browser; } if (browser_) { registrar_.Add(this, NotificationType::BROWSER_CLOSED, Source<Browser>(browser_)); } return browser_; } // Restore window(s) from a foreign session. void RestoreForeignSession( std::vector<SessionWindow*>::const_iterator begin, std::vector<SessionWindow*>::const_iterator end) { StartTabCreation(); // Create a browser instance to put the restored tabs in. for (std::vector<SessionWindow*>::const_iterator i = begin; i != end; ++i) { Browser* browser = CreateRestoredBrowser( static_cast<Browser::Type>((*i)->type), (*i)->bounds, (*i)->is_maximized); // Restore and show the browser. const int initial_tab_count = browser->tab_count(); int selected_tab_index = (*i)->selected_tab_index; RestoreTabsToBrowser(*(*i), browser, selected_tab_index); ShowBrowser(browser, initial_tab_count, selected_tab_index); tab_loader_->TabIsLoading( &browser->GetSelectedTabContents()->controller()); NotifySessionServiceOfRestoredTabs(browser, initial_tab_count); } // Always create in a new window FinishedTabCreation(true, true); } // Restore a single tab from a foreign session. // Note: we currently restore the tab to the last active browser. void RestoreForeignTab(const SessionTab& tab) { StartTabCreation(); Browser* current_browser = browser_ ? browser_ : BrowserList::GetLastActive(); RestoreTab(tab, current_browser->tab_count(), current_browser, true); NotifySessionServiceOfRestoredTabs(current_browser, current_browser->tab_count()); FinishedTabCreation(true, true); } ~SessionRestoreImpl() { STLDeleteElements(&windows_); restoring = false; } virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type.value) { case NotificationType::BROWSER_CLOSED: delete this; return; default: NOTREACHED(); break; } } private: // Invoked when beginning to create new tabs. Resets the tab_loader_. void StartTabCreation() { tab_loader_.reset(new TabLoader(restore_started_)); } // Invoked when done with creating all the tabs/browsers. // // |created_tabbed_browser| indicates whether a tabbed browser was created, // or we used an existing tabbed browser. // // If successful, this begins loading tabs and deletes itself when all tabs // have been loaded. // // Returns the Browser that was created, if any. Browser* FinishedTabCreation(bool succeeded, bool created_tabbed_browser) { Browser* browser = NULL; if (!created_tabbed_browser && always_create_tabbed_browser_) { browser = Browser::Create(profile_); if (urls_to_open_.empty()) { // No tab browsers were created and no URLs were supplied on the command // line. Add an empty URL, which is treated as opening the users home // page. urls_to_open_.push_back(GURL()); } AppendURLsToBrowser(browser, urls_to_open_); browser->window()->Show(); } if (succeeded) { DCHECK(tab_loader_.get()); // TabLoader delets itself when done loading. tab_loader_.release()->StartLoading(); } if (!synchronous_) { // If we're not synchronous we need to delete ourself. // NOTE: we must use DeleteLater here as most likely we're in a callback // from the history service which doesn't deal well with deleting the // object it is notifying. MessageLoop::current()->DeleteSoon(FROM_HERE, this); } return browser; } void OnGotSession(SessionService::Handle handle, std::vector<SessionWindow*>* windows) { if (synchronous_) { // See comment above windows_ as to why we don't process immediately. windows_.swap(*windows); MessageLoop::current()->Quit(); return; } ProcessSessionWindows(windows); } Browser* ProcessSessionWindows(std::vector<SessionWindow*>* windows) { if (windows->empty()) { // Restore was unsuccessful. return FinishedTabCreation(false, false); } StartTabCreation(); Browser* current_browser = browser_ ? browser_ : BrowserList::GetLastActive(); // After the for loop this contains the last TABBED_BROWSER. Is null if no // tabbed browsers exist. Browser* last_browser = NULL; bool has_tabbed_browser = false; for (std::vector<SessionWindow*>::iterator i = windows->begin(); i != windows->end(); ++i) { Browser* browser = NULL; if (!has_tabbed_browser && (*i)->type == Browser::TYPE_NORMAL) has_tabbed_browser = true; if (i == windows->begin() && (*i)->type == Browser::TYPE_NORMAL && !clobber_existing_window_) { // If there is an open tabbed browser window, use it. Otherwise fall // through and create a new one. browser = current_browser; if (browser && (browser->type() != Browser::TYPE_NORMAL || browser->profile()->IsOffTheRecord())) { browser = NULL; } } if (!browser) { browser = CreateRestoredBrowser( static_cast<Browser::Type>((*i)->type), (*i)->bounds, (*i)->is_maximized); } if ((*i)->type == Browser::TYPE_NORMAL) last_browser = browser; const int initial_tab_count = browser->tab_count(); int selected_tab_index = (*i)->selected_tab_index; RestoreTabsToBrowser(*(*i), browser, selected_tab_index); ShowBrowser(browser, initial_tab_count, selected_tab_index); tab_loader_->TabIsLoading( &browser->GetSelectedTabContents()->controller()); NotifySessionServiceOfRestoredTabs(browser, initial_tab_count); } // If we're restoring a session as the result of a crash and the session // included at least one tabbed browser, then close the browser window // that was opened when the user clicked to restore the session. if (clobber_existing_window_ && current_browser && has_tabbed_browser && current_browser->type() == Browser::TYPE_NORMAL) { current_browser->CloseAllTabs(); } if (last_browser && !urls_to_open_.empty()) AppendURLsToBrowser(last_browser, urls_to_open_); // If last_browser is NULL and urls_to_open_ is non-empty, // FinishedTabCreation will create a new TabbedBrowser and add the urls to // it. Browser* finished_browser = FinishedTabCreation(true, has_tabbed_browser); if (finished_browser) last_browser = finished_browser; return last_browser; } void RestoreTabsToBrowser(const SessionWindow& window, Browser* browser, int selected_tab_index) { DCHECK(!window.tabs.empty()); for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin(); i != window.tabs.end(); ++i) { const SessionTab& tab = *(*i); const int tab_index = static_cast<int>(i - window.tabs.begin()); // Don't schedule a load for the selected tab, as ShowBrowser() will // already have done that. RestoreTab(tab, tab_index, browser, tab_index != selected_tab_index); } } void RestoreTab(const SessionTab& tab, const int tab_index, Browser* browser, bool schedule_load) { DCHECK(!tab.navigations.empty()); int selected_index = tab.current_navigation_index; selected_index = std::max( 0, std::min(selected_index, static_cast<int>(tab.navigations.size() - 1))); // Record an app launch, if applicable. GURL url = tab.navigations.at(tab.current_navigation_index).virtual_url(); if ( #if defined(OS_CHROMEOS) browser->profile()->GetExtensionService() && #endif browser->profile()->GetExtensionService()->IsInstalledApp(url)) { UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, extension_misc::APP_LAUNCH_SESSION_RESTORE, extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); } TabContents* tab_contents = browser->AddRestoredTab(tab.navigations, tab_index, selected_index, tab.extension_app_id, false, tab.pinned, true, NULL); if (schedule_load) tab_loader_->ScheduleLoad(&tab_contents->controller()); } Browser* CreateRestoredBrowser(Browser::Type type, gfx::Rect bounds, bool is_maximized) { Browser* browser = new Browser(type, profile_); browser->set_override_bounds(bounds); browser->set_maximized_state(is_maximized ? Browser::MAXIMIZED_STATE_MAXIMIZED : Browser::MAXIMIZED_STATE_UNMAXIMIZED); browser->InitBrowserWindow(); return browser; } void ShowBrowser(Browser* browser, int initial_tab_count, int selected_session_index) { if (browser_ == browser) { browser->ActivateTabAt(browser->tab_count() - 1, true); return; } DCHECK(browser); DCHECK(browser->tab_count()); browser->ActivateTabAt( std::min(initial_tab_count + std::max(0, selected_session_index), browser->tab_count() - 1), true); browser->window()->Show(); // TODO(jcampan): http://crbug.com/8123 we should not need to set the // initial focus explicitly. browser->GetSelectedTabContents()->view()->SetInitialFocus(); } // Appends the urls in |urls| to |browser|. void AppendURLsToBrowser(Browser* browser, const std::vector<GURL>& urls) { for (size_t i = 0; i < urls.size(); ++i) { int add_types = TabStripModel::ADD_FORCE_INDEX; if (i == 0) add_types |= TabStripModel::ADD_ACTIVE; int index = browser->GetIndexForInsertionDuringRestore(i); browser::NavigateParams params(browser, urls[i], PageTransition::START_PAGE); params.disposition = i == 0 ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; params.tabstrip_index = index; params.tabstrip_add_types = add_types; browser::Navigate(¶ms); } } // Invokes TabRestored on the SessionService for all tabs in browser after // initial_count. void NotifySessionServiceOfRestoredTabs(Browser* browser, int initial_count) { SessionService* session_service = profile_->GetSessionService(); for (int i = initial_count; i < browser->tab_count(); ++i) session_service->TabRestored(&browser->GetTabContentsAt(i)->controller(), browser->tabstrip_model()->IsTabPinned(i)); } // The profile to create the sessions for. Profile* profile_; // The first browser to restore to, may be null. Browser* browser_; // Whether or not restore is synchronous. const bool synchronous_; // See description in RestoreSession (in .h). const bool clobber_existing_window_; // If true and there is an error or there are no windows to restore, we // create a tabbed browser anyway. This is used on startup to make sure at // at least one window is created. const bool always_create_tabbed_browser_; // Set of URLs to open in addition to those restored from the session. std::vector<GURL> urls_to_open_; // Used to get the session. CancelableRequestConsumer request_consumer_; // Responsible for loading the tabs. scoped_ptr<TabLoader> tab_loader_; // When synchronous we run a nested message loop. To avoid creating windows // from the nested message loop (which can make exiting the nested message // loop take a while) we cache the SessionWindows here and create the actual // windows when the nested message loop exits. std::vector<SessionWindow*> windows_; NotificationRegistrar registrar_; // The time we started the restore. base::TimeTicks restore_started_; }; } // namespace // SessionRestore ------------------------------------------------------------- static Browser* Restore(Profile* profile, Browser* browser, bool synchronous, bool clobber_existing_window, bool always_create_tabbed_browser, const std::vector<GURL>& urls_to_open) { #if defined(OS_CHROMEOS) chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( "SessionRestoreStarted", false); #endif DCHECK(profile); // Always restore from the original profile (incognito profiles have no // session service). profile = profile->GetOriginalProfile(); if (!profile->GetSessionService()) { NOTREACHED(); return NULL; } restoring = true; profile->set_restored_last_session(true); // SessionRestoreImpl takes care of deleting itself when done. SessionRestoreImpl* restorer = new SessionRestoreImpl(profile, browser, synchronous, clobber_existing_window, always_create_tabbed_browser, urls_to_open); return restorer->Restore(); } // static void SessionRestore::RestoreSession(Profile* profile, Browser* browser, bool clobber_existing_window, bool always_create_tabbed_browser, const std::vector<GURL>& urls_to_open) { Restore(profile, browser, false, clobber_existing_window, always_create_tabbed_browser, urls_to_open); } // static void SessionRestore::RestoreForeignSessionWindows( Profile* profile, std::vector<SessionWindow*>::const_iterator begin, std::vector<SessionWindow*>::const_iterator end) { // Create a SessionRestore object to eventually restore the tabs. std::vector<GURL> gurls; SessionRestoreImpl restorer(profile, static_cast<Browser*>(NULL), true, false, true, gurls); restorer.RestoreForeignSession(begin, end); } // static void SessionRestore::RestoreForeignSessionTab(Profile* profile, const SessionTab& tab) { // Create a SessionRestore object to eventually restore the tabs. std::vector<GURL> gurls; SessionRestoreImpl restorer(profile, static_cast<Browser*>(NULL), true, false, true, gurls); restorer.RestoreForeignTab(tab); } // static Browser* SessionRestore::RestoreSessionSynchronously( Profile* profile, const std::vector<GURL>& urls_to_open) { return Restore(profile, NULL, true, false, true, urls_to_open); } // static bool SessionRestore::IsRestoring() { return restoring; }