| Index: chrome/browser/sessions/persistent_tab_restore_service.cc
|
| diff --git a/chrome/browser/sessions/persistent_tab_restore_service.cc b/chrome/browser/sessions/persistent_tab_restore_service.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ca9fd2b8b091da1f3d6eaf6fa49f7f3aecc9fc0c
|
| --- /dev/null
|
| +++ b/chrome/browser/sessions/persistent_tab_restore_service.cc
|
| @@ -0,0 +1,723 @@
|
| +// 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/persistent_tab_restore_service.h"
|
| +
|
| +#include <cstring> // memcpy
|
| +#include <vector>
|
| +
|
| +#include "base/basictypes.h"
|
| +#include "base/bind.h"
|
| +#include "base/file_path.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/scoped_vector.h"
|
| +#include "base/stl_util.h"
|
| +#include "base/time.h"
|
| +#include "chrome/browser/sessions/session_command.h"
|
| +#include "content/public/browser/session_storage_namespace.h"
|
| +
|
| +namespace {
|
| +
|
| +// Only written if the tab is pinned.
|
| +typedef bool PinnedStatePayload;
|
| +
|
| +typedef int32 RestoredEntryPayload;
|
| +
|
| +// Payload used for the start of a tab close. This is the old struct that is
|
| +// used for backwards compat when it comes to reading the session files.
|
| +struct SelectedNavigationInTabPayload {
|
| + SessionID::id_type id;
|
| + int32 index;
|
| +};
|
| +
|
| +// Payload used for the start of a window close. This is the old struct that is
|
| +// used for backwards compat when it comes to reading the session files. This
|
| +// struct must be POD, because we memset the contents.
|
| +struct WindowPayload {
|
| + SessionID::id_type window_id;
|
| + int32 selected_tab_index;
|
| + int32 num_tabs;
|
| +};
|
| +
|
| +// Payload used for the start of a window close. This struct must be POD,
|
| +// because we memset the contents.
|
| +struct WindowPayload2 : WindowPayload {
|
| + int64 timestamp;
|
| +};
|
| +
|
| +// Payload used for the start of a tab close.
|
| +struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload {
|
| + int64 timestamp;
|
| +};
|
| +
|
| +// Used to indicate what has loaded.
|
| +enum LoadState {
|
| + // Indicates we haven't loaded anything.
|
| + NOT_LOADED = 1 << 0,
|
| +
|
| + // Indicates we've asked for the last sessions and tabs but haven't gotten
|
| + // the result back yet.
|
| + LOADING = 1 << 2,
|
| +
|
| + // Indicates we finished loading the last tabs (but not necessarily the
|
| + // last session).
|
| + LOADED_LAST_TABS = 1 << 3,
|
| +
|
| + // Indicates we finished loading the last session (but not necessarily the
|
| + // last tabs).
|
| + LOADED_LAST_SESSION = 1 << 4
|
| +};
|
| +
|
| +// Identifier for commands written to file.
|
| +// The ordering in the file is as follows:
|
| +// . When the user closes a tab a command of type
|
| +// kCommandSelectedNavigationInTab is written identifying the tab and
|
| +// the selected index, then a kCommandPinnedState command if the tab was
|
| +// pinned and kCommandSetExtensionAppID if the tab has an app id and
|
| +// the user agent override if it was using one. This is
|
| +// followed by any number of kCommandUpdateTabNavigation commands (1 per
|
| +// navigation entry).
|
| +// . When the user closes a window a kCommandSelectedNavigationInTab command
|
| +// is written out and followed by n tab closed sequences (as previoulsy
|
| +// described).
|
| +// . When the user restores an entry a command of type kCommandRestoredEntry
|
| +// is written.
|
| +const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
|
| +const SessionCommand::id_type kCommandRestoredEntry = 2;
|
| +const SessionCommand::id_type kCommandWindow = 3;
|
| +const SessionCommand::id_type kCommandSelectedNavigationInTab = 4;
|
| +const SessionCommand::id_type kCommandPinnedState = 5;
|
| +const SessionCommand::id_type kCommandSetExtensionAppID = 6;
|
| +const SessionCommand::id_type kCommandSetWindowAppName = 7;
|
| +const SessionCommand::id_type kCommandSetTabUserAgentOverride = 8;
|
| +
|
| +// Number of entries (not commands) before we clobber the file and write
|
| +// everything.
|
| +const int kEntriesPerReset = 40;
|
| +
|
| +SessionCommand* CreateRestoredEntryCommand(SessionID::id_type entry_id) {
|
| + RestoredEntryPayload payload = entry_id;
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandRestoredEntry, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* CreateSelectedNavigationInTabCommand(SessionID::id_type tab_id,
|
| + int32 index,
|
| + base::Time timestamp) {
|
| + SelectedNavigationInTabPayload2 payload;
|
| + payload.id = tab_id;
|
| + payload.index = index;
|
| + payload.timestamp = timestamp.ToInternalValue();
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* CreateWindowCommand(SessionID::id_type id,
|
| + int selected_tab_index,
|
| + int num_tabs,
|
| + base::Time timestamp) {
|
| + WindowPayload2 payload;
|
| + // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of
|
| + // uninitialized memory in the struct.
|
| + memset(&payload, 0, sizeof(payload));
|
| + payload.window_id = id;
|
| + payload.selected_tab_index = selected_tab_index;
|
| + payload.num_tabs = num_tabs;
|
| + payload.timestamp = timestamp.ToInternalValue();
|
| +
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandWindow, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +
|
| +} // namespace
|
| +
|
| +PersistentTabRestoreService::PersistentTabRestoreService(
|
| + Profile* profile,
|
| + TimeFactory* time_factory)
|
| + : BaseSessionService(BaseSessionService::TAB_RESTORE, profile,
|
| + FilePath()),
|
| + InMemoryTabRestoreService(profile, time_factory),
|
| + entries_to_write_(0),
|
| + entries_written_(0),
|
| + load_state_(NOT_LOADED) {
|
| +}
|
| +
|
| +PersistentTabRestoreService::~PersistentTabRestoreService() {
|
| + STLDeleteElements(&staging_entries_);
|
| +}
|
| +
|
| +void PersistentTabRestoreService::Save() {
|
| + int to_write_count = std::min(entries_to_write_,
|
| + static_cast<int>(entries().size()));
|
| + entries_to_write_ = 0;
|
| + if (entries_written_ + to_write_count > kEntriesPerReset) {
|
| + to_write_count = entries().size();
|
| + set_pending_reset(true);
|
| + }
|
| + if (to_write_count) {
|
| + // Write the to_write_count most recently added entries out. The most
|
| + // recently added entry is at the front, so we use a reverse iterator to
|
| + // write in the order the entries were added.
|
| + Entries::const_reverse_iterator i = entries().rbegin();
|
| + DCHECK(static_cast<size_t>(to_write_count) <= entries().size());
|
| + std::advance(i, entries().size() - static_cast<int>(to_write_count));
|
| + for (; i != entries().rend(); ++i) {
|
| + Entry* entry = *i;
|
| + if (entry->type == TAB) {
|
| + Tab* tab = static_cast<Tab*>(entry);
|
| + int selected_index = GetSelectedNavigationIndexToPersist(*tab);
|
| + if (selected_index != -1)
|
| + ScheduleCommandsForTab(*tab, selected_index);
|
| + } else {
|
| + ScheduleCommandsForWindow(*static_cast<Window*>(entry));
|
| + }
|
| + entries_written_++;
|
| + }
|
| + }
|
| + if (pending_reset())
|
| + entries_written_ = 0;
|
| + BaseSessionService::Save();
|
| +}
|
| +
|
| +bool PersistentTabRestoreService::IsLoaded() const {
|
| + return !(load_state_ & (NOT_LOADED | LOADING));
|
| +}
|
| +
|
| +void PersistentTabRestoreService::Shutdown() {
|
| + if (backend())
|
| + Save();
|
| +}
|
| +
|
| +void PersistentTabRestoreService::OnClearEntries() {
|
| + const Entries& entries = TabRestoreService::entries();
|
| + // Mark all the tabs as closed so that we don't attempt to restore them.
|
| + for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i)
|
| + ScheduleCommand(CreateRestoredEntryCommand((*i)->id));
|
| +
|
| + entries_to_write_ = 0;
|
| +
|
| + // Schedule a pending reset so that we nuke the file on next write.
|
| + set_pending_reset(true);
|
| +
|
| + // Schedule a command, otherwise if there are no pending commands Save does
|
| + // nothing.
|
| + ScheduleCommand(CreateRestoredEntryCommand(1));
|
| +}
|
| +
|
| +void PersistentTabRestoreService::OnRestoreEntryById(
|
| + SessionID::id_type id,
|
| + Entries::const_iterator entry_iterator) {
|
| + const Entries& entries = TabRestoreService::entries();
|
| + size_t index = 0;
|
| + for (Entries::const_iterator j = entries.begin();
|
| + j != entry_iterator && j != entries.end();
|
| + ++j, ++index) {}
|
| + if (static_cast<int>(index) < entries_to_write_)
|
| + entries_to_write_--;
|
| +
|
| + ScheduleCommand(CreateRestoredEntryCommand(id));
|
| +}
|
| +
|
| +void PersistentTabRestoreService::OnAddEntry() {
|
| + // Start the save timer, when it fires we'll generate the commands.
|
| + StartSaveTimer();
|
| + entries_to_write_++;
|
| +}
|
| +
|
| +int PersistentTabRestoreService::GetSelectedNavigationIndexToPersist(
|
| + const Tab& tab) {
|
| + const std::vector<TabNavigation>& navigations = tab.navigations;
|
| + int selected_index = tab.current_navigation_index;
|
| + int max_index = static_cast<int>(navigations.size());
|
| +
|
| + // Find the first navigation to persist. We won't persist the selected
|
| + // navigation if ShouldTrackEntry returns false.
|
| + while (selected_index >= 0 &&
|
| + !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
|
| + selected_index--;
|
| + }
|
| +
|
| + if (selected_index != -1)
|
| + return selected_index;
|
| +
|
| + // Couldn't find a navigation to persist going back, go forward.
|
| + selected_index = tab.current_navigation_index + 1;
|
| + while (selected_index < max_index &&
|
| + !ShouldTrackEntry(navigations[selected_index].virtual_url())) {
|
| + selected_index++;
|
| + }
|
| +
|
| + return (selected_index == max_index) ? -1 : selected_index;
|
| +}
|
| +
|
| +void PersistentTabRestoreService::ScheduleCommandsForTab(const Tab& tab,
|
| + int selected_index) {
|
| + const std::vector<TabNavigation>& navigations = tab.navigations;
|
| + int max_index = static_cast<int>(navigations.size());
|
| +
|
| + // Determine the first navigation we'll persist.
|
| + int valid_count_before_selected = 0;
|
| + int first_index_to_persist = selected_index;
|
| + for (int i = selected_index - 1; i >= 0 &&
|
| + valid_count_before_selected < max_persist_navigation_count; --i) {
|
| + if (ShouldTrackEntry(navigations[i].virtual_url())) {
|
| + first_index_to_persist = i;
|
| + valid_count_before_selected++;
|
| + }
|
| + }
|
| +
|
| + // Write the command that identifies the selected tab.
|
| + ScheduleCommand(
|
| + CreateSelectedNavigationInTabCommand(tab.id,
|
| + valid_count_before_selected,
|
| + tab.timestamp));
|
| +
|
| + if (tab.pinned) {
|
| + PinnedStatePayload payload = true;
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandPinnedState, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + ScheduleCommand(command);
|
| + }
|
| +
|
| + if (!tab.extension_app_id.empty()) {
|
| + ScheduleCommand(
|
| + CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID, tab.id,
|
| + tab.extension_app_id));
|
| + }
|
| +
|
| + if (!tab.user_agent_override.empty()) {
|
| + ScheduleCommand(
|
| + CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
|
| + tab.id, tab.user_agent_override));
|
| + }
|
| +
|
| + // Then write the navigations.
|
| + for (int i = first_index_to_persist, wrote_count = 0;
|
| + i < max_index && wrote_count < 2 * max_persist_navigation_count; ++i) {
|
| + if (ShouldTrackEntry(navigations[i].virtual_url())) {
|
| + ScheduleCommand(
|
| + CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id,
|
| + navigations[i]));
|
| + }
|
| + }
|
| +}
|
| +
|
| +void PersistentTabRestoreService::ScheduleCommandsForWindow(
|
| + const Window& window) {
|
| + DCHECK(!window.tabs.empty());
|
| + int selected_tab = window.selected_tab_index;
|
| + int valid_tab_count = 0;
|
| + int real_selected_tab = selected_tab;
|
| + for (size_t i = 0; i < window.tabs.size(); ++i) {
|
| + if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) {
|
| + valid_tab_count++;
|
| + } else if (static_cast<int>(i) < selected_tab) {
|
| + real_selected_tab--;
|
| + }
|
| + }
|
| + if (valid_tab_count == 0)
|
| + return; // No tabs to persist.
|
| +
|
| + ScheduleCommand(
|
| + CreateWindowCommand(window.id,
|
| + std::min(real_selected_tab, valid_tab_count - 1),
|
| + valid_tab_count,
|
| + window.timestamp));
|
| +
|
| + if (!window.app_name.empty()) {
|
| + ScheduleCommand(
|
| + CreateSetWindowAppNameCommand(kCommandSetWindowAppName,
|
| + window.id,
|
| + window.app_name));
|
| + }
|
| +
|
| + for (size_t i = 0; i < window.tabs.size(); ++i) {
|
| + int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]);
|
| + if (selected_index != -1)
|
| + ScheduleCommandsForTab(window.tabs[i], selected_index);
|
| + }
|
| +}
|
| +
|
| +void PersistentTabRestoreService::OnGotPreviousSession(
|
| + Handle handle,
|
| + std::vector<SessionWindow*>* windows,
|
| + SessionID::id_type ignored_active_window) {
|
| + std::vector<Entry*> entries;
|
| + CreateEntriesFromWindows(windows, &entries);
|
| + // Previous session tabs go first.
|
| + staging_entries_.insert(staging_entries_.begin(), entries.begin(),
|
| + entries.end());
|
| + load_state_ |= LOADED_LAST_SESSION;
|
| + LoadStateChanged();
|
| +}
|
| +
|
| +void PersistentTabRestoreService::OnGotLastSessionCommands(
|
| + Handle handle,
|
| + scoped_refptr<InternalGetCommandsRequest> request) {
|
| + std::vector<Entry*> entries;
|
| + CreateEntriesFromCommands(request, &entries);
|
| + // Closed tabs always go to the end.
|
| + staging_entries_.insert(staging_entries_.end(), entries.begin(),
|
| + entries.end());
|
| + load_state_ |= LOADED_LAST_TABS;
|
| + LoadStateChanged();
|
| +}
|
| +
|
| +void PersistentTabRestoreService::CreateEntriesFromCommands(
|
| + scoped_refptr<InternalGetCommandsRequest> request,
|
| + std::vector<Entry*>* loaded_entries) {
|
| + if (request->canceled() || entries().size() == kMaxEntries)
|
| + return;
|
| +
|
| + std::vector<SessionCommand*>& commands = request->commands;
|
| + // Iterate through the commands populating entries and id_to_entry.
|
| + ScopedVector<Entry> entries;
|
| + IDToEntry id_to_entry;
|
| + // If non-null we're processing the navigations of this tab.
|
| + Tab* current_tab = NULL;
|
| + // If non-null we're processing the tabs of this window.
|
| + Window* current_window = NULL;
|
| + // If > 0, we've gotten a window command but not all the tabs yet.
|
| + int pending_window_tabs = 0;
|
| + for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
|
| + i != commands.end(); ++i) {
|
| + const SessionCommand& command = *(*i);
|
| + switch (command.id()) {
|
| + case kCommandRestoredEntry: {
|
| + if (pending_window_tabs > 0) {
|
| + // Should never receive a restored command while waiting for all the
|
| + // tabs in a window.
|
| + return;
|
| + }
|
| +
|
| + current_tab = NULL;
|
| + current_window = NULL;
|
| +
|
| + RestoredEntryPayload payload;
|
| + if (!command.GetPayload(&payload, sizeof(payload)))
|
| + return;
|
| + RemoveEntryByID(payload, &id_to_entry, &(entries.get()));
|
| + break;
|
| + }
|
| +
|
| + case kCommandWindow: {
|
| + WindowPayload2 payload;
|
| + if (pending_window_tabs > 0) {
|
| + // Should never receive a window command while waiting for all the
|
| + // tabs in a window.
|
| + return;
|
| + }
|
| +
|
| + // Try the new payload first
|
| + if (!command.GetPayload(&payload, sizeof(payload))) {
|
| + // then the old payload
|
| + WindowPayload old_payload;
|
| + if (!command.GetPayload(&old_payload, sizeof(old_payload)))
|
| + return;
|
| +
|
| + // Copy the old payload data to the new payload.
|
| + payload.window_id = old_payload.window_id;
|
| + payload.selected_tab_index = old_payload.selected_tab_index;
|
| + payload.num_tabs = old_payload.num_tabs;
|
| + // Since we don't have a time use time 0 which is used to mark as an
|
| + // unknown timestamp.
|
| + payload.timestamp = 0;
|
| + }
|
| +
|
| + pending_window_tabs = payload.num_tabs;
|
| + if (pending_window_tabs <= 0) {
|
| + // Should always have at least 1 tab. Likely indicates corruption.
|
| + return;
|
| + }
|
| +
|
| + RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get()));
|
| +
|
| + current_window = new Window();
|
| + current_window->selected_tab_index = payload.selected_tab_index;
|
| + current_window->timestamp =
|
| + base::Time::FromInternalValue(payload.timestamp);
|
| + entries.push_back(current_window);
|
| + id_to_entry[payload.window_id] = current_window;
|
| + break;
|
| + }
|
| +
|
| + case kCommandSelectedNavigationInTab: {
|
| + SelectedNavigationInTabPayload2 payload;
|
| + if (!command.GetPayload(&payload, sizeof(payload))) {
|
| + SelectedNavigationInTabPayload old_payload;
|
| + if (!command.GetPayload(&old_payload, sizeof(old_payload)))
|
| + return;
|
| + payload.id = old_payload.id;
|
| + payload.index = old_payload.index;
|
| + // Since we don't have a time use time 0 which is used to mark as an
|
| + // unknown timestamp.
|
| + payload.timestamp = 0;
|
| + }
|
| +
|
| + if (pending_window_tabs > 0) {
|
| + if (!current_window) {
|
| + // We should have created a window already.
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| + current_window->tabs.resize(current_window->tabs.size() + 1);
|
| + current_tab = &(current_window->tabs.back());
|
| + if (--pending_window_tabs == 0)
|
| + current_window = NULL;
|
| + } else {
|
| + RemoveEntryByID(payload.id, &id_to_entry, &(entries.get()));
|
| + current_tab = new Tab();
|
| + id_to_entry[payload.id] = current_tab;
|
| + current_tab->timestamp =
|
| + base::Time::FromInternalValue(payload.timestamp);
|
| + entries.push_back(current_tab);
|
| + }
|
| + current_tab->current_navigation_index = payload.index;
|
| + break;
|
| + }
|
| +
|
| + case kCommandUpdateTabNavigation: {
|
| + if (!current_tab) {
|
| + // Should be in a tab when we get this.
|
| + return;
|
| + }
|
| + current_tab->navigations.resize(current_tab->navigations.size() + 1);
|
| + SessionID::id_type tab_id;
|
| + if (!RestoreUpdateTabNavigationCommand(
|
| + command, ¤t_tab->navigations.back(), &tab_id)) {
|
| + return;
|
| + }
|
| + break;
|
| + }
|
| +
|
| + case kCommandPinnedState: {
|
| + if (!current_tab) {
|
| + // Should be in a tab when we get this.
|
| + return;
|
| + }
|
| + // NOTE: payload doesn't matter. kCommandPinnedState is only written if
|
| + // tab is pinned.
|
| + current_tab->pinned = true;
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetWindowAppName: {
|
| + if (!current_window) {
|
| + // We should have created a window already.
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + SessionID::id_type window_id;
|
| + std::string app_name;
|
| + if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name))
|
| + return;
|
| +
|
| + current_window->app_name.swap(app_name);
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetExtensionAppID: {
|
| + if (!current_tab) {
|
| + // Should be in a tab when we get this.
|
| + return;
|
| + }
|
| + SessionID::id_type tab_id;
|
| + std::string extension_app_id;
|
| + if (!RestoreSetTabExtensionAppIDCommand(command, &tab_id,
|
| + &extension_app_id)) {
|
| + return;
|
| + }
|
| + current_tab->extension_app_id.swap(extension_app_id);
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetTabUserAgentOverride: {
|
| + if (!current_tab) {
|
| + // Should be in a tab when we get this.
|
| + return;
|
| + }
|
| + SessionID::id_type tab_id;
|
| + std::string user_agent_override;
|
| + if (!RestoreSetTabUserAgentOverrideCommand(command, &tab_id,
|
| + &user_agent_override)) {
|
| + return;
|
| + }
|
| + current_tab->user_agent_override.swap(user_agent_override);
|
| + break;
|
| + }
|
| +
|
| + default:
|
| + // Unknown type, usually indicates corruption of file. Ignore it.
|
| + return;
|
| + }
|
| + }
|
| +
|
| + // If there was corruption some of the entries won't be valid.
|
| + ValidateAndDeleteEmptyEntries(&(entries.get()));
|
| +
|
| + loaded_entries->swap(entries.get());
|
| +}
|
| +
|
| +void PersistentTabRestoreService::CreateEntriesFromWindows(
|
| + std::vector<SessionWindow*>* windows,
|
| + std::vector<Entry*>* entries) {
|
| + for (size_t i = 0; i < windows->size(); ++i) {
|
| + scoped_ptr<Window> window(new Window());
|
| + if (ConvertSessionWindowToWindow((*windows)[i], window.get()))
|
| + entries->push_back(window.release());
|
| + }
|
| +}
|
| +
|
| +void PersistentTabRestoreService::RemoveEntryByID(
|
| + SessionID::id_type id,
|
| + IDToEntry* id_to_entry,
|
| + std::vector<TabRestoreService::Entry*>* entries) {
|
| + // Look for the entry in the map. If it is present, erase it from both
|
| + // collections and return.
|
| + IDToEntry::iterator i = id_to_entry->find(id);
|
| + if (i != id_to_entry->end()) {
|
| + entries->erase(std::find(entries->begin(), entries->end(), i->second));
|
| + delete i->second;
|
| + id_to_entry->erase(i);
|
| + return;
|
| + }
|
| +
|
| + // Otherwise, loop over all items in the map and see if any of the Windows
|
| + // have Tabs with the |id|.
|
| + for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end();
|
| + ++i) {
|
| + if (i->second->type == TabRestoreService::WINDOW) {
|
| + TabRestoreService::Window* window =
|
| + static_cast<TabRestoreService::Window*>(i->second);
|
| + std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin();
|
| + for ( ; j != window->tabs.end(); ++j) {
|
| + // If the ID matches one of this window's tabs, remove it from the list.
|
| + if ((*j).id == id) {
|
| + window->tabs.erase(j);
|
| + return;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +// static
|
| +void PersistentTabRestoreService::ValidateAndDeleteEmptyEntries(
|
| + std::vector<Entry*>* entries) {
|
| + std::vector<Entry*> valid_entries;
|
| + std::vector<Entry*> invalid_entries;
|
| +
|
| + // Iterate from the back so that we keep the most recently closed entries.
|
| + for (std::vector<Entry*>::reverse_iterator i = entries->rbegin();
|
| + i != entries->rend(); ++i) {
|
| + if (ValidateEntry(*i))
|
| + valid_entries.push_back(*i);
|
| + else
|
| + invalid_entries.push_back(*i);
|
| + }
|
| + // NOTE: at this point the entries are ordered with newest at the front.
|
| + entries->swap(valid_entries);
|
| +
|
| + // Delete the remaining entries.
|
| + STLDeleteElements(&invalid_entries);
|
| +}
|
| +
|
| +void PersistentTabRestoreService::LoadTabsFromLastSession() {
|
| + if (load_state_ != NOT_LOADED || entries().size() == kMaxEntries)
|
| + return;
|
| +
|
| +#if !defined(ENABLE_SESSION_SERVICE)
|
| + // If sessions are not stored in the SessionService, default to
|
| + // |LOADED_LAST_SESSION| state.
|
| + load_state_ = LOADING | LOADED_LAST_SESSION;
|
| +#else
|
| + load_state_ = LOADING;
|
| +
|
| + SessionService* session_service =
|
| + SessionServiceFactory::GetForProfile(profile_);
|
| + Profile::ExitType exit_type = profile()->GetLastSessionExitType();
|
| + if (!profile()->restored_last_session() && session_service &&
|
| + (exit_type == Profile::EXIT_CRASHED ||
|
| + exit_type == Profile::EXIT_SESSION_ENDED)) {
|
| + // The previous session crashed and wasn't restored, or was a forced
|
| + // shutdown. Both of which won't have notified us of the browser close so
|
| + // that we need to load the windows from session service (which will have
|
| + // saved them).
|
| + session_service->GetLastSession(
|
| + &crash_consumer_,
|
| + base::Bind(&PersistentTabRestoreService::OnGotPreviousSession,
|
| + base::Unretained(this)));
|
| + } else {
|
| + load_state_ |= LOADED_LAST_SESSION;
|
| + }
|
| +#endif
|
| +
|
| + // Request the tabs closed in the last session. If the last session crashed,
|
| + // this won't contain the tabs/window that were open at the point of the
|
| + // crash (the call to GetLastSession above requests those).
|
| + ScheduleGetLastSessionCommands(
|
| + new InternalGetCommandsRequest(
|
| + base::Bind(&PersistentTabRestoreService::OnGotLastSessionCommands,
|
| + base::Unretained(this))),
|
| + &load_consumer_);
|
| +}
|
| +
|
| +void PersistentTabRestoreService::LoadStateChanged() {
|
| + if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) !=
|
| + (LOADED_LAST_TABS | LOADED_LAST_SESSION)) {
|
| + // Still waiting on previous session or previous tabs.
|
| + return;
|
| + }
|
| +
|
| + // We're done loading.
|
| + load_state_ ^= LOADING;
|
| +
|
| + const size_t entries_size = entries().size();
|
| + if (staging_entries_.empty() || entries_size >= kMaxEntries) {
|
| + STLDeleteElements(&staging_entries_);
|
| + return;
|
| + }
|
| +
|
| + if (staging_entries_.size() + entries_size > kMaxEntries) {
|
| + // If we add all the staged entries we'll end up with more than
|
| + // kMaxEntries. Delete entries such that we only end up with
|
| + // at most kMaxEntries.
|
| + int surplus = kMaxEntries - entries_size;
|
| + CHECK_LE(0, surplus);
|
| + CHECK_GE(static_cast<int>(staging_entries_.size()), surplus);
|
| + STLDeleteContainerPointers(
|
| + staging_entries_.begin() + (kMaxEntries - entries_size),
|
| + staging_entries_.end());
|
| + staging_entries_.erase(
|
| + staging_entries_.begin() + (kMaxEntries - entries_size),
|
| + staging_entries_.end());
|
| + }
|
| +
|
| + // And add them.
|
| + for (size_t i = 0; i < staging_entries_.size(); ++i) {
|
| + staging_entries_[i]->from_last_session = true;
|
| + AddEntry(staging_entries_[i], false, false);
|
| + }
|
| +
|
| + // AddEntry takes ownership of the entry, need to clear out entries so that
|
| + // it doesn't delete them.
|
| + staging_entries_.clear();
|
| +
|
| + // Make it so we rewrite all the tabs. We need to do this otherwise we won't
|
| + // correctly write out the entries when Save is invoked (Save starts from
|
| + // the front, not the end and we just added the entries to the end).
|
| + entries_to_write_ = staging_entries_.size();
|
| +
|
| + PruneEntries();
|
| + NotifyTabsChanged();
|
| +}
|
|
|