diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/session_restore.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/session_restore.cc')
-rw-r--r-- | chrome/browser/session_restore.cc | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/chrome/browser/session_restore.cc b/chrome/browser/session_restore.cc new file mode 100644 index 0000000..57cc6d8 --- /dev/null +++ b/chrome/browser/session_restore.cc @@ -0,0 +1,456 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/session_restore.h" + +#include <vector> + +#include "base/scoped_ptr.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profile.h" +#include "chrome/common/notification_service.h" + +namespace { + +// TabLoader ------------------------------------------------------------------ + +// TabLoader is responsible for ensuring after session restore we have +// at least SessionRestore::num_tabs_to_load_ loading. As tabs finish loading +// new tabs are loaded. 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: + typedef std::list<NavigationController*> TabsToLoad; + + TabLoader(); + ~TabLoader(); + + // Adds a tab to load. + void AddTab(NavigationController* controller); + + // Loads the next batch of tabs until SessionRestore::num_tabs_to_load_ tabs + // are loading, or all tabs are loading. If there are no more tabs to load, + // this deletes the TabLoader. + // + // This must be invoked once to start loading. + void LoadTabs(); + + private: + // 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); + + // Adds the necessary listeners for the specified tab. + void AddListeners(NavigationController* controller); + + // Removes the necessary listeners for the specified tab. + void RemoveListeners(NavigationController* controller); + + // Has Load been invoked? + bool loading_; + + // The set of tabs we've initiated loading on. This does NOT include the + // selected tabs. + typedef std::set<NavigationController*> TabsLoading; + TabsLoading tabs_loading_; + + // The tabs we need to load. + TabsToLoad tabs_to_load_; +}; + +TabLoader::TabLoader() + : loading_(false) { +} + +TabLoader::~TabLoader() { + DCHECK(tabs_to_load_.empty() && tabs_loading_.empty()); +} + +void TabLoader::AddTab(NavigationController* controller) { + if (controller) { + DCHECK(find(tabs_to_load_.begin(), tabs_to_load_.end(), controller) == + tabs_to_load_.end()); + tabs_to_load_.push_back(controller); + AddListeners(controller); + } else { + // Should never get a NULL tab. + NOTREACHED(); + } +} + +void TabLoader::LoadTabs() { + loading_ = true; + while (!tabs_to_load_.empty() && + (SessionRestore::num_tabs_to_load_ == 0 || + tabs_loading_.size() < SessionRestore::num_tabs_to_load_)) { + NavigationController* tab = tabs_to_load_.front(); + tabs_loading_.insert(tab); + tabs_to_load_.pop_front(); + tab->LoadIfNecessary(); + if (tab && tab->active_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->active_contents()->WasHidden(); + } + } + } + + if (tabs_to_load_.empty()) { + for (TabsLoading::iterator i = tabs_loading_.begin(); + i != tabs_loading_.end(); ++i) { + RemoveListeners(*i); + } + tabs_loading_.clear(); + delete this; + } +} + +void TabLoader::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NOTIFY_TAB_CLOSED || type == NOTIFY_LOAD_STOP); + NavigationController* tab = Source<NavigationController>(source).ptr(); + RemoveTab(tab); + if (loading_) { + LoadTabs(); + // WARNING: if there are no more tabs to load, we have been deleted. + } +} + +void TabLoader::RemoveTab(NavigationController* tab) { + RemoveListeners(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::AddListeners(NavigationController* controller) { + NotificationService::current()->AddObserver( + this, NOTIFY_TAB_CLOSED, + Source<NavigationController>(controller)); + NotificationService::current()->AddObserver( + this, NOTIFY_LOAD_STOP, + Source<NavigationController>(controller)); +} + +void TabLoader::RemoveListeners(NavigationController* controller) { + NotificationService::current()->RemoveObserver( + this, NOTIFY_TAB_CLOSED, + Source<NavigationController>(controller)); + NotificationService::current()->RemoveObserver( + this, NOTIFY_LOAD_STOP, + Source<NavigationController>(controller)); +} + +// SessionRestoreImpl --------------------------------------------------------- + +// SessionRestoreImpl is responsible for fetching the set of tabs to create +// from SessionService. SessionRestoreImpl deletes itself when done. + +class SessionRestoreImpl { + public: + SessionRestoreImpl(Profile* profile, + bool use_saved_session, + int show_command, + bool synchronous, + bool clobber_existing_window, + bool always_create_tabbed_browser, + const std::vector<GURL>& urls_to_open) + : profile_(profile), + show_command_(show_command), + use_saved_session_(use_saved_session), + synchronous_(synchronous), + clobber_existing_window_(clobber_existing_window), + always_create_tabbed_browser_(always_create_tabbed_browser), + urls_to_open_(urls_to_open) { + } + + void SessionRestoreImpl::Restore() { + SessionService* session_service = profile_->GetSessionService(); + DCHECK(session_service); + SessionService::SavedSessionCallback* callback = + NewCallback(this, &SessionRestoreImpl::OnGotSession); + if (use_saved_session_) + session_service->GetSavedSession(&request_consumer_, callback); + else + session_service->GetLastSession(&request_consumer_, callback); + + if (synchronous_) { + MessageLoop::current()->Run(); + delete this; + return; + } + } + + ~SessionRestoreImpl() { + } + + private: + // 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. + void FinishedTabCreation(bool succeeded, bool created_tabbed_browser) { + if (!created_tabbed_browser && always_create_tabbed_browser_) { + Browser* browser = new Browser(gfx::Rect(), show_command_, profile_, + BrowserType::TABBED_BROWSER, + std::wstring()); + 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->Show(); + } + + if (synchronous_) + MessageLoop::current()->Quit(); + + if (succeeded) { + DCHECK(tab_loader_.get()); + // TabLoader delets itself when done loading. + tab_loader_.release()->LoadTabs(); + } + + 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); + } + } + + void OnGotSession(SessionService::Handle handle, + std::vector<SessionWindow*>* windows) { + if (windows->empty()) { + // Restore was unsuccessful. + FinishedTabCreation(false, false); + return; + } + + tab_loader_.reset(new TabLoader()); + + Browser* current_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 == BrowserType::TABBED_BROWSER) + has_tabbed_browser = true; + if (i == windows->begin() && (*i)->type == BrowserType::TABBED_BROWSER && + !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->GetType() != BrowserType::TABBED_BROWSER || + browser->profile()->IsOffTheRecord())) { + browser = NULL; + } + } + if (!browser) { + const int show_command = + (*i)->is_maximized ? SW_SHOWMAXIMIZED : show_command_; + browser = new Browser((*i)->bounds, show_command, profile_, (*i)->type, + std::wstring()); + } + if ((*i)->type == BrowserType::TABBED_BROWSER) + last_browser = browser; + const int initial_tab_count = browser->tab_count(); + RestoreTabsToBrowser(*(*i), browser); + ShowBrowser(browser, initial_tab_count, (*i)->selected_tab_index); + 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->GetType() == BrowserType::TABBED_BROWSER) { + 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. + FinishedTabCreation(true, has_tabbed_browser); + } + + void RestoreTabsToBrowser(const SessionWindow& window, Browser* browser) { + DCHECK(!window.tabs.empty()); + for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin(); + i != window.tabs.end(); ++i) { + const SessionTab& tab = *(*i); + 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))); + tab_loader_->AddTab( + browser->AddRestoredTab(tab.navigations, selected_index, false)); + } + } + + void ShowBrowser(Browser* browser, + int initial_tab_count, + int selected_session_index) { + 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->ShowAndFit(true); + } + + void AppendURLsToBrowser(Browser* browser, const std::vector<GURL>& urls) { + for (size_t i = 0; i < urls.size(); ++i) { + browser->AddTabWithURL(urls[i], PageTransition::START_PAGE, (i == 0), + NULL); + } + } + + // 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()); + } + + // The profile to create the sessions for. + Profile* profile_; + + // Used when creating windows. Passed to the window. + const int show_command_; + + // Whether we're restoring the saved session (true) or the last session + // (false). + const bool use_saved_session_; + + // 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_; +}; + +} // namespace + +// SessionRestore ------------------------------------------------------------- + +// static +size_t SessionRestore::num_tabs_to_load_ = 0; + +static void Restore(Profile* profile, + bool use_saved_session, + int show_command, + bool synchronous, + bool clobber_existing_window, + bool always_create_tabbed_browser, + const std::vector<GURL>& urls_to_open) { + DCHECK(profile); + if (!profile->GetSessionService()) + return; + // SessionRestoreImpl takes care of deleting itself when done. + SessionRestoreImpl* restorer = + new SessionRestoreImpl(profile, use_saved_session, show_command, + synchronous, clobber_existing_window, + always_create_tabbed_browser, + urls_to_open); + restorer->Restore(); +} + +// static +void SessionRestore::RestoreSession(Profile* profile, + bool use_saved_session, + bool clobber_existing_window, + bool always_create_tabbed_browser, + const std::vector<GURL>& urls_to_open) { + Restore(profile, use_saved_session, SW_SHOW, false, + clobber_existing_window, always_create_tabbed_browser, urls_to_open); +} + +// static +void SessionRestore::RestoreSessionSynchronously( + Profile* profile, + bool use_saved_session, + int show_command, + const std::vector<GURL>& urls_to_open) { + Restore(profile, use_saved_session, SW_SHOW, true, false, true, urls_to_open); +} |