| // 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->selected_index() != tab_index) { |
| // By default tabs are marked as visible. As only the selected tab is |
| // visible we need to explicitly tell non-selected tabs they are hidden. |
| // Without this call non-selected 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_); |
| scoped_refptr<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_); |
| scoped_refptr<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->SelectTabContentsAt(browser->tab_count() - 1, true); |
| return; |
| } |
| |
| DCHECK(browser); |
| DCHECK(browser->tab_count()); |
| browser->SelectTabContentsAt( |
| 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_SELECTED; |
| 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; |
| } |