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

Unified Diff: chrome/browser/sessions/in_memory_tab_restore_service.cc

Issue 10989027: Split TabRestoreService into InMemoryTRS and PersistentTRS (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Split TRS into InMemoryTRS and PersistentTRS Created 8 years, 2 months 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
Index: chrome/browser/sessions/in_memory_tab_restore_service.cc
diff --git a/chrome/browser/sessions/in_memory_tab_restore_service.cc b/chrome/browser/sessions/in_memory_tab_restore_service.cc
new file mode 100644
index 0000000000000000000000000000000000000000..dc71e5eee7d1eff835f4c4f16401e0bfbc6a5c9b
--- /dev/null
+++ b/chrome/browser/sessions/in_memory_tab_restore_service.cc
@@ -0,0 +1,585 @@
+// Copyright (c) 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.
+
+#include "chrome/browser/sessions/in_memory_tab_restore_service.h"
+
+#include <algorithm>
+#include <iterator>
+#include <map>
+
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_command.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/session_service_factory.h"
+#include "chrome/browser/sessions/session_types.h"
+#include "chrome/browser/sessions/tab_restore_service_delegate.h"
+#include "chrome/browser/sessions/tab_restore_service_observer.h"
+#include "chrome/browser/ui/tab_contents/tab_contents.h"
+#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/session_storage_namespace.h"
+
+using content::NavigationController;
+using content::NavigationEntry;
+using content::WebContents;
+
+namespace {
+
+void RecordAppLaunch(Profile* profile, const TabRestoreService::Tab& tab) {
+#if !defined(OS_ANDROID)
+ GURL url = tab.navigations.at(tab.current_navigation_index).virtual_url();
+ DCHECK(profile->GetExtensionService());
+ if (!profile->GetExtensionService()->IsInstalledApp(url))
+ return;
+
+ AppLauncherHandler::RecordAppLaunchType(
+ extension_misc::APP_LAUNCH_NTP_RECENTLY_CLOSED);
+#endif // !defined(OS_ANDROID)
+}
+
+} // namespace
+
+InMemoryTabRestoreService::InMemoryTabRestoreService(
+ Profile* profile,
+ TabRestoreService::TimeFactory* time_factory)
+ : profile_(profile),
+ restoring_(false),
+ time_factory_(time_factory) {
+}
+
+InMemoryTabRestoreService::~InMemoryTabRestoreService() {
+ FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
+ TabRestoreServiceDestroyed(this));
+ STLDeleteElements(&entries_);
+ time_factory_ = NULL;
+}
+
+void InMemoryTabRestoreService::AddObserver(
+ TabRestoreServiceObserver* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void InMemoryTabRestoreService::RemoveObserver(
+ TabRestoreServiceObserver* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void InMemoryTabRestoreService::CreateHistoricalTab(
+ content::WebContents* contents,
+ int index) {
+ if (restoring_)
+ return;
+
+ TabRestoreServiceDelegate* delegate =
+ TabRestoreServiceDelegate::FindDelegateForWebContents(contents);
+ if (closing_delegates_.find(delegate) != closing_delegates_.end())
+ return;
+
+ scoped_ptr<Tab> local_tab(new Tab());
+ PopulateTab(local_tab.get(), index, delegate, &contents->GetController());
+ if (local_tab->navigations.empty())
+ return;
+
+ AddEntry(local_tab.release(), true, true);
+}
+
+void InMemoryTabRestoreService::BrowserClosing(
+ TabRestoreServiceDelegate* delegate) {
+ closing_delegates_.insert(delegate);
+
+ scoped_ptr<Window> window(new Window());
+ window->selected_tab_index = delegate->GetSelectedIndex();
+ window->timestamp = TimeNow();
+ window->app_name = delegate->GetAppName();
+
+ // Don't use std::vector::resize() because it will push copies of an empty tab
+ // into the vector, which will give all tabs in a window the same ID.
+ for (int i = 0; i < delegate->GetTabCount(); ++i) {
+ window->tabs.push_back(Tab());
+ }
+ size_t entry_index = 0;
+ for (int tab_index = 0; tab_index < delegate->GetTabCount(); ++tab_index) {
+ PopulateTab(&(window->tabs[entry_index]),
+ tab_index,
+ delegate,
+ &delegate->GetWebContentsAt(tab_index)->GetController());
+ if (window->tabs[entry_index].navigations.empty()) {
+ window->tabs.erase(window->tabs.begin() + entry_index);
+ } else {
+ window->tabs[entry_index].browser_id = delegate->GetSessionID().id();
+ entry_index++;
+ }
+ }
+ if (window->tabs.size() == 1 && window->app_name.empty()) {
+ // Short-circuit creating a Window if only 1 tab was present. This fixes
+ // http://crbug.com/56744. Copy the Tab because it's owned by an object on
+ // the stack.
+ AddEntry(new Tab(window->tabs[0]), true, true);
+ } else if (!window->tabs.empty()) {
+ window->selected_tab_index =
+ std::min(static_cast<int>(window->tabs.size() - 1),
+ window->selected_tab_index);
+ AddEntry(window.release(), true, true);
+ }
+}
+
+void InMemoryTabRestoreService::BrowserClosed(
+ TabRestoreServiceDelegate* delegate) {
+ closing_delegates_.erase(delegate);
+}
+
+void InMemoryTabRestoreService::ClearEntries() {
+ OnClearEntries();
+ STLDeleteElements(&entries_);
+ NotifyTabsChanged();
+}
+
+const TabRestoreService::Entries& InMemoryTabRestoreService::entries() const {
+ return entries_;
+}
+
+void InMemoryTabRestoreService::RestoreMostRecentEntry(
+ TabRestoreServiceDelegate* delegate) {
+ if (entries_.empty())
+ return;
+
+ RestoreEntryById(delegate, entries_.front()->id, UNKNOWN);
+}
+
+TabRestoreService::Tab* InMemoryTabRestoreService::RemoveTabEntryById(
+ SessionID::id_type id) {
+ Entries::iterator i = GetEntryIteratorById(id);
+ if (i == entries_.end())
+ return NULL;
+
+ Entry* entry = *i;
+ if (entry->type != TAB)
+ return NULL;
+
+ Tab* tab = static_cast<Tab*>(entry);
+ entries_.erase(i);
+ return tab;
+}
+
+void InMemoryTabRestoreService::RestoreEntryById(
+ TabRestoreServiceDelegate* delegate,
+ SessionID::id_type id,
+ WindowOpenDisposition disposition) {
+ Entries::iterator entry_iterator = GetEntryIteratorById(id);
+ if (entry_iterator == entries_.end())
+ // Don't hoark here, we allow an invalid id.
+ return;
+
+ OnRestoreEntryById(id, entry_iterator);
+ restoring_ = true;
+ Entry* entry = *entry_iterator;
+
+ // If the entry's ID does not match the ID that is being restored, then the
+ // entry is a window from which a single tab will be restored.
+ bool restoring_tab_in_window = entry->id != id;
+
+ if (!restoring_tab_in_window) {
+ entries_.erase(entry_iterator);
+ entry_iterator = entries_.end();
+ }
+
+ // |delegate| will be NULL in cases where one isn't already available (eg,
+ // when invoked on Mac OS X with no windows open). In this case, create a
+ // new browser into which we restore the tabs.
+ if (entry->type == TAB) {
+ Tab* tab = static_cast<Tab*>(entry);
+ delegate = RestoreTab(*tab, delegate, disposition);
+ delegate->ShowBrowserWindow();
+ } else if (entry->type == WINDOW) {
+ TabRestoreServiceDelegate* current_delegate = delegate;
+ Window* window = static_cast<Window*>(entry);
+
+ // When restoring a window, either the entire window can be restored, or a
+ // single tab within it. If the entry's ID matches the one to restore, then
+ // the entire window will be restored.
+ if (!restoring_tab_in_window) {
+ delegate = TabRestoreServiceDelegate::Create(profile_, window->app_name);
+ for (size_t tab_i = 0; tab_i < window->tabs.size(); ++tab_i) {
+ const Tab& tab = window->tabs[tab_i];
+ WebContents* restored_tab =
+ delegate->AddRestoredTab(tab.navigations, delegate->GetTabCount(),
+ tab.current_navigation_index,
+ tab.extension_app_id,
+ static_cast<int>(tab_i) ==
+ window->selected_tab_index,
+ tab.pinned, tab.from_last_session,
+ tab.session_storage_namespace,
+ tab.user_agent_override);
+ if (restored_tab) {
+ restored_tab->GetController().LoadIfNecessary();
+ RecordAppLaunch(profile_, tab);
+ }
+ }
+ // All the window's tabs had the same former browser_id.
+ if (window->tabs[0].has_browser()) {
+ UpdateTabBrowserIDs(window->tabs[0].browser_id,
+ delegate->GetSessionID().id());
+ }
+ } else {
+ // Restore a single tab from the window. Find the tab that matches the ID
+ // in the window and restore it.
+ for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
+ tab_i != window->tabs.end(); ++tab_i) {
+ const Tab& tab = *tab_i;
+ if (tab.id == id) {
+ delegate = RestoreTab(tab, delegate, disposition);
+ window->tabs.erase(tab_i);
+ // If restoring the tab leaves the window with nothing else, delete it
+ // as well.
+ if (!window->tabs.size()) {
+ entries_.erase(entry_iterator);
+ delete entry;
+ } else {
+ // Update the browser ID of the rest of the tabs in the window so if
+ // any one is restored, it goes into the same window as the tab
+ // being restored now.
+ UpdateTabBrowserIDs(tab.browser_id,
+ delegate->GetSessionID().id());
+ for (std::vector<Tab>::iterator tab_j = window->tabs.begin();
+ tab_j != window->tabs.end(); ++tab_j) {
+ (*tab_j).browser_id = delegate->GetSessionID().id();
+ }
+ }
+ break;
+ }
+ }
+ }
+ delegate->ShowBrowserWindow();
+
+ if (disposition == CURRENT_TAB && current_delegate &&
+ current_delegate->GetActiveWebContents()) {
+ current_delegate->CloseTab();
+ }
+ } else {
+ NOTREACHED();
+ }
+
+ if (!restoring_tab_in_window) {
+ delete entry;
+ }
+
+ restoring_ = false;
+ NotifyTabsChanged();
+}
+
+void InMemoryTabRestoreService::LoadTabsFromLastSession() {
+ NOTIMPLEMENTED();
+}
+
+bool InMemoryTabRestoreService::IsLoaded() const {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void InMemoryTabRestoreService::Shutdown() {
+ NOTIMPLEMENTED();
+}
+
+void InMemoryTabRestoreService::PopulateTab(
+ Tab* tab,
+ int index,
+ TabRestoreServiceDelegate* delegate,
+ NavigationController* controller) {
+ const int pending_index = controller->GetPendingEntryIndex();
+ int entry_count = controller->GetEntryCount();
+ if (entry_count == 0 && pending_index == 0)
+ entry_count++;
+ tab->navigations.resize(static_cast<int>(entry_count));
+ for (int i = 0; i < entry_count; ++i) {
+ NavigationEntry* entry = (i == pending_index) ?
+ controller->GetPendingEntry() : controller->GetEntryAtIndex(i);
+ tab->navigations[i] =
+ TabNavigation::FromNavigationEntry(i, *entry);
+ }
+ tab->timestamp = TimeNow();
+ tab->current_navigation_index = controller->GetCurrentEntryIndex();
+ if (tab->current_navigation_index == -1 && entry_count > 0)
+ tab->current_navigation_index = 0;
+ tab->tabstrip_index = index;
+
+ TabContents* tab_contents =
+ TabContents::FromWebContents(controller->GetWebContents());
+ // tab_contents is NULL in some browser tests.
+ if (tab_contents) {
+ const extensions::Extension* extension =
+ extensions::TabHelper::FromWebContents(controller->GetWebContents())->
+ extension_app();
+ if (extension)
+ tab->extension_app_id = extension->id();
+ }
+
+ tab->user_agent_override =
+ controller->GetWebContents()->GetUserAgentOverride();
+
+ // TODO(ajwong): This does not correctly handle storage for isolated apps.
+ tab->session_storage_namespace =
+ controller->GetDefaultSessionStorageNamespace();
+
+ // Delegate may be NULL during unit tests.
+ if (delegate) {
+ tab->browser_id = delegate->GetSessionID().id();
+ tab->pinned = delegate->IsTabPinned(tab->tabstrip_index);
+ }
+}
+
+void InMemoryTabRestoreService::AddEntry(Entry* entry,
+ bool notify,
+ bool to_front) {
+ if (!FilterEntry(entry) || (entries_.size() >= kMaxEntries && !to_front)) {
+ delete entry;
+ return;
+ }
+
+ if (to_front)
+ entries_.push_front(entry);
+ else
+ entries_.push_back(entry);
+
+ PruneEntries();
+
+ if (notify)
+ NotifyTabsChanged();
+
+ OnAddEntry();
+}
+
+void InMemoryTabRestoreService::PruneEntries() {
+ Entries new_entries;
+
+ for (TabRestoreService::Entries::const_iterator iter = entries_.begin();
+ iter != entries_.end(); ++iter) {
+ TabRestoreService::Entry* entry = *iter;
+
+ if (FilterEntry(entry) &&
+ new_entries.size() < kMaxEntries) {
+ new_entries.push_back(entry);
+ } else {
+ delete entry;
+ }
+ }
+
+ entries_ = new_entries;
+}
+
+void InMemoryTabRestoreService::NotifyTabsChanged() {
+ FOR_EACH_OBSERVER(TabRestoreServiceObserver, observer_list_,
+ TabRestoreServiceChanged(this));
+}
+
+TabRestoreService::Entries::iterator
+InMemoryTabRestoreService::GetEntryIteratorById(SessionID::id_type id) {
+ for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
+ if ((*i)->id == id)
+ return i;
+
+ // For Window entries, see if the ID matches a tab. If so, report the window
+ // as the Entry.
+ if ((*i)->type == WINDOW) {
+ std::vector<Tab>& tabs = static_cast<Window*>(*i)->tabs;
+ for (std::vector<Tab>::iterator j = tabs.begin();
+ j != tabs.end(); ++j) {
+ if ((*j).id == id) {
+ return i;
+ }
+ }
+ }
+ }
+ return entries_.end();
+}
+
+bool InMemoryTabRestoreService::ConvertSessionWindowToWindow(
+ SessionWindow* session_window,
+ Window* window) {
+ for (size_t i = 0; i < session_window->tabs.size(); ++i) {
+ if (!session_window->tabs[i]->navigations.empty()) {
+ window->tabs.resize(window->tabs.size() + 1);
+ Tab& tab = window->tabs.back();
+ tab.pinned = session_window->tabs[i]->pinned;
+ tab.navigations.swap(session_window->tabs[i]->navigations);
+ tab.current_navigation_index =
+ session_window->tabs[i]->current_navigation_index;
+ tab.extension_app_id = session_window->tabs[i]->extension_app_id;
+ tab.timestamp = base::Time();
+ }
+ }
+ if (window->tabs.empty())
+ return false;
+
+ window->selected_tab_index =
+ std::min(session_window->selected_tab_index,
+ static_cast<int>(window->tabs.size() - 1));
+ window->timestamp = base::Time();
+ return true;
+}
+
+// static
+bool InMemoryTabRestoreService::ValidateEntry(Entry* entry) {
+ if (entry->type == TAB)
+ return ValidateTab(static_cast<Tab*>(entry));
+
+ if (entry->type == WINDOW)
+ return ValidateWindow(static_cast<Window*>(entry));
+
+ NOTREACHED();
+ return false;
+}
+
+TabRestoreServiceDelegate* InMemoryTabRestoreService::RestoreTab(
+ const Tab& tab,
+ TabRestoreServiceDelegate* delegate,
+ WindowOpenDisposition disposition) {
+ if (disposition == CURRENT_TAB && delegate) {
+ delegate->ReplaceRestoredTab(tab.navigations,
+ tab.current_navigation_index,
+ tab.from_last_session,
+ tab.extension_app_id,
+ tab.session_storage_namespace,
+ tab.user_agent_override);
+ } else {
+ // We only respsect the tab's original browser if there's no disposition.
+ if (disposition == UNKNOWN && tab.has_browser())
+ delegate = TabRestoreServiceDelegate::FindDelegateWithID(tab.browser_id);
+
+ int tab_index = -1;
+
+ // |delegate| will be NULL in cases where one isn't already available (eg,
+ // when invoked on Mac OS X with no windows open). In this case, create a
+ // new browser into which we restore the tabs.
+ if (delegate && disposition != NEW_WINDOW) {
+ tab_index = tab.tabstrip_index;
+ } else {
+ delegate = TabRestoreServiceDelegate::Create(profile_, std::string());
+ if (tab.has_browser())
+ UpdateTabBrowserIDs(tab.browser_id, delegate->GetSessionID().id());
+ }
+
+ // Place the tab at the end if the tab index is no longer valid or
+ // we were passed a specific disposition.
+ if (tab_index < 0 || tab_index > delegate->GetTabCount() ||
+ disposition != UNKNOWN) {
+ tab_index = delegate->GetTabCount();
+ }
+
+ WebContents* web_contents = delegate->AddRestoredTab(
+ tab.navigations,
+ tab_index,
+ tab.current_navigation_index,
+ tab.extension_app_id,
+ disposition != NEW_BACKGROUND_TAB,
+ tab.pinned,
+ tab.from_last_session,
+ tab.session_storage_namespace,
+ tab.user_agent_override);
+ web_contents->GetController().LoadIfNecessary();
+ }
+ RecordAppLaunch(profile_, tab);
+ return delegate;
+}
+
+
+bool InMemoryTabRestoreService::ValidateTab(Tab* tab) {
+ if (tab->navigations.empty())
+ return false;
+
+ tab->current_navigation_index =
+ std::max(0, std::min(tab->current_navigation_index,
+ static_cast<int>(tab->navigations.size()) - 1));
+
+ return true;
+}
+
+bool InMemoryTabRestoreService::ValidateWindow(Window* window) {
+ window->selected_tab_index =
+ std::max(0, std::min(window->selected_tab_index,
+ static_cast<int>(window->tabs.size() - 1)));
+
+ int i = 0;
+ for (std::vector<Tab>::iterator tab_i = window->tabs.begin();
+ tab_i != window->tabs.end();) {
+ if (!ValidateTab(&(*tab_i))) {
+ tab_i = window->tabs.erase(tab_i);
+ if (i < window->selected_tab_index)
+ window->selected_tab_index--;
+ else if (i == window->selected_tab_index)
+ window->selected_tab_index = 0;
+ } else {
+ ++tab_i;
+ ++i;
+ }
+ }
+
+ if (window->tabs.empty())
+ return false;
+
+ return true;
+}
+
+bool InMemoryTabRestoreService::IsTabInteresting(const Tab* tab) {
+ if (tab->navigations.empty())
+ return false;
+
+ if (tab->navigations.size() > 1)
+ return true;
+
+ return tab->pinned ||
+ tab->navigations.at(0).virtual_url() !=
+ GURL(chrome::kChromeUINewTabURL);
+}
+
+bool InMemoryTabRestoreService::IsWindowInteresting(const Window* window) {
+ if (window->tabs.empty())
+ return false;
+
+ if (window->tabs.size() > 1)
+ return true;
+
+ return IsTabInteresting(&window->tabs[0]);
+}
+
+bool InMemoryTabRestoreService::FilterEntry(Entry* entry) {
+ if (!ValidateEntry(entry))
+ return false;
+
+ if (entry->type == TAB)
+ return IsTabInteresting(static_cast<Tab*>(entry));
+ else if (entry->type == WINDOW)
+ return IsWindowInteresting(static_cast<Window*>(entry));
+
+ NOTREACHED();
+ return false;
+}
+
+void InMemoryTabRestoreService::UpdateTabBrowserIDs(SessionID::id_type old_id,
+ SessionID::id_type new_id) {
+ for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
+ Entry* entry = *i;
+ if (entry->type == TAB) {
+ Tab* tab = static_cast<Tab*>(entry);
+ if (tab->browser_id == old_id)
+ tab->browser_id = new_id;
+ }
+ }
+}
+
+void InMemoryTabRestoreService::DeleteLastSession() {
+ NOTIMPLEMENTED();
+}
+
+base::Time InMemoryTabRestoreService::TimeNow() const {
+ return time_factory_ ? time_factory_->TimeNow() : base::Time::Now();
+}

Powered by Google App Engine
This is Rietveld 408576698