| Index: chrome/browser/sessions/session_service_commands.cc
|
| diff --git a/chrome/browser/sessions/session_service_commands.cc b/chrome/browser/sessions/session_service_commands.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a95ef3aac2d313416ce1de4f74cbb3be55b3f19a
|
| --- /dev/null
|
| +++ b/chrome/browser/sessions/session_service_commands.cc
|
| @@ -0,0 +1,865 @@
|
| +// Copyright 2014 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_service_commands.h"
|
| +
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/pickle.h"
|
| +#include "chrome/browser/sessions/base_session_service_delegate.h"
|
| +#include "chrome/browser/sessions/session_backend.h"
|
| +#include "chrome/browser/sessions/session_command.h"
|
| +#include "chrome/browser/sessions/session_types.h"
|
| +#include "components/startup_metric_utils/startup_metric_utils.h"
|
| +
|
| +// Identifier for commands written to file.
|
| +static const SessionCommand::id_type kCommandSetTabWindow = 0;
|
| +// OBSOLETE Superseded by kCommandSetWindowBounds3.
|
| +// static const SessionCommand::id_type kCommandSetWindowBounds = 1;
|
| +static const SessionCommand::id_type kCommandSetTabIndexInWindow = 2;
|
| +static const SessionCommand::id_type
|
| + kCommandTabNavigationPathPrunedFromBack = 5;
|
| +static const SessionCommand::id_type kCommandUpdateTabNavigation = 6;
|
| +static const SessionCommand::id_type kCommandSetSelectedNavigationIndex = 7;
|
| +static const SessionCommand::id_type kCommandSetSelectedTabInIndex = 8;
|
| +static const SessionCommand::id_type kCommandSetWindowType = 9;
|
| +// OBSOLETE Superseded by kCommandSetWindowBounds3. Except for data migration.
|
| +// static const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
|
| +static const SessionCommand::id_type
|
| + kCommandTabNavigationPathPrunedFromFront = 11;
|
| +static const SessionCommand::id_type kCommandSetPinnedState = 12;
|
| +static const SessionCommand::id_type kCommandSetExtensionAppID = 13;
|
| +static const SessionCommand::id_type kCommandSetWindowBounds3 = 14;
|
| +static const SessionCommand::id_type kCommandSetWindowAppName = 15;
|
| +static const SessionCommand::id_type kCommandTabClosed = 16;
|
| +static const SessionCommand::id_type kCommandWindowClosed = 17;
|
| +static const SessionCommand::id_type kCommandSetTabUserAgentOverride = 18;
|
| +static const SessionCommand::id_type kCommandSessionStorageAssociated = 19;
|
| +static const SessionCommand::id_type kCommandSetActiveWindow = 20;
|
| +
|
| +// Every kWritesPerReset commands triggers recreating the file.
|
| +static const int kWritesPerReset = 250;
|
| +
|
| +namespace {
|
| +
|
| +// Various payload structures.
|
| +struct ClosedPayload {
|
| + SessionID::id_type id;
|
| + int64 close_time;
|
| +};
|
| +
|
| +struct WindowBoundsPayload2 {
|
| + SessionID::id_type window_id;
|
| + int32 x;
|
| + int32 y;
|
| + int32 w;
|
| + int32 h;
|
| + bool is_maximized;
|
| +};
|
| +
|
| +struct WindowBoundsPayload3 {
|
| + SessionID::id_type window_id;
|
| + int32 x;
|
| + int32 y;
|
| + int32 w;
|
| + int32 h;
|
| + int32 show_state;
|
| +};
|
| +
|
| +typedef SessionID::id_type ActiveWindowPayload;
|
| +
|
| +struct IDAndIndexPayload {
|
| + SessionID::id_type id;
|
| + int32 index;
|
| +};
|
| +
|
| +typedef IDAndIndexPayload TabIndexInWindowPayload;
|
| +
|
| +typedef IDAndIndexPayload TabNavigationPathPrunedFromBackPayload;
|
| +
|
| +typedef IDAndIndexPayload SelectedNavigationIndexPayload;
|
| +
|
| +typedef IDAndIndexPayload SelectedTabInIndexPayload;
|
| +
|
| +typedef IDAndIndexPayload WindowTypePayload;
|
| +
|
| +typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload;
|
| +
|
| +struct PinnedStatePayload {
|
| + SessionID::id_type tab_id;
|
| + bool pinned_state;
|
| +};
|
| +
|
| +// Persisted versions of ui::WindowShowState that are written to disk and can
|
| +// never change.
|
| +enum PersistedWindowShowState {
|
| + // SHOW_STATE_DEFAULT (0) never persisted.
|
| + PERSISTED_SHOW_STATE_NORMAL = 1,
|
| + PERSISTED_SHOW_STATE_MINIMIZED = 2,
|
| + PERSISTED_SHOW_STATE_MAXIMIZED = 3,
|
| + // SHOW_STATE_INACTIVE (4) never persisted.
|
| + PERSISTED_SHOW_STATE_FULLSCREEN = 5,
|
| + PERSISTED_SHOW_STATE_DETACHED_DEPRECATED = 6,
|
| + PERSISTED_SHOW_STATE_END = 6
|
| +};
|
| +
|
| +// Assert to ensure PersistedWindowShowState is updated if ui::WindowShowState
|
| +// is changed.
|
| +COMPILE_ASSERT(ui::SHOW_STATE_END ==
|
| + static_cast<ui::WindowShowState>(PERSISTED_SHOW_STATE_END),
|
| + persisted_show_state_mismatch);
|
| +
|
| +// Returns the show state to store to disk based |state|.
|
| +PersistedWindowShowState ShowStateToPersistedShowState(
|
| + ui::WindowShowState state) {
|
| + switch (state) {
|
| + case ui::SHOW_STATE_NORMAL:
|
| + return PERSISTED_SHOW_STATE_NORMAL;
|
| + case ui::SHOW_STATE_MINIMIZED:
|
| + return PERSISTED_SHOW_STATE_MINIMIZED;
|
| + case ui::SHOW_STATE_MAXIMIZED:
|
| + return PERSISTED_SHOW_STATE_MAXIMIZED;
|
| + case ui::SHOW_STATE_FULLSCREEN:
|
| + return PERSISTED_SHOW_STATE_FULLSCREEN;
|
| +
|
| + case ui::SHOW_STATE_DEFAULT:
|
| + case ui::SHOW_STATE_INACTIVE:
|
| + return PERSISTED_SHOW_STATE_NORMAL;
|
| +
|
| + case ui::SHOW_STATE_END:
|
| + break;
|
| + }
|
| + NOTREACHED();
|
| + return PERSISTED_SHOW_STATE_NORMAL;
|
| +}
|
| +
|
| +// Lints show state values when read back from persited disk.
|
| +ui::WindowShowState PersistedShowStateToShowState(int state) {
|
| + switch (state) {
|
| + case PERSISTED_SHOW_STATE_NORMAL:
|
| + return ui::SHOW_STATE_NORMAL;
|
| + case PERSISTED_SHOW_STATE_MINIMIZED:
|
| + return ui::SHOW_STATE_MINIMIZED;
|
| + case PERSISTED_SHOW_STATE_MAXIMIZED:
|
| + return ui::SHOW_STATE_MAXIMIZED;
|
| + case PERSISTED_SHOW_STATE_FULLSCREEN:
|
| + return ui::SHOW_STATE_FULLSCREEN;
|
| + case PERSISTED_SHOW_STATE_DETACHED_DEPRECATED:
|
| + return ui::SHOW_STATE_NORMAL;
|
| + }
|
| + NOTREACHED();
|
| + return ui::SHOW_STATE_NORMAL;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// SessionService -------------------------------------------------------------
|
| +
|
| +SessionServiceCommands::SessionServiceCommands(
|
| + BaseSessionService::SessionType type,
|
| + const base::FilePath& path,
|
| + scoped_ptr<BaseSessionServiceDelegate> delegate)
|
| + : BaseSessionService(type, path, delegate.Pass()),
|
| + weak_factory_(this) {
|
| +}
|
| +
|
| +SessionServiceCommands::~SessionServiceCommands() {
|
| + // The owner is responsible to save upon exit.
|
| +}
|
| +
|
| +base::CancelableTaskTracker::TaskId SessionServiceCommands::GetLastSession(
|
| + const SessionCallback& callback,
|
| + base::CancelableTaskTracker* tracker) {
|
| + // OnGotSessionServiceCommands maps the SessionServiceCommands to browser state, then run
|
| + // the callback.
|
| + return ScheduleGetLastSessionCommands(
|
| + base::Bind(&SessionServiceCommands::OnGotSessionServiceCommands,
|
| + weak_factory_.GetWeakPtr(), callback),
|
| + tracker);
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetSelectedTabInWindow(
|
| + const SessionID& window_id,
|
| + int index) {
|
| + SelectedTabInIndexPayload payload = { 0 };
|
| + payload.id = window_id.id();
|
| + payload.index = index;
|
| + SessionCommand* command = new SessionCommand(kCommandSetSelectedTabInIndex,
|
| + sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetTabWindow(
|
| + const SessionID& window_id,
|
| + const SessionID& tab_id) {
|
| + SessionID::id_type payload[] = { window_id.id(), tab_id.id() };
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandSetTabWindow, sizeof(payload));
|
| + memcpy(command->contents(), payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetWindowBounds(
|
| + const SessionID& window_id,
|
| + const gfx::Rect& bounds,
|
| + ui::WindowShowState show_state) {
|
| + WindowBoundsPayload3 payload = { 0 };
|
| + payload.window_id = window_id.id();
|
| + payload.x = bounds.x();
|
| + payload.y = bounds.y();
|
| + payload.w = bounds.width();
|
| + payload.h = bounds.height();
|
| + payload.show_state = ShowStateToPersistedShowState(show_state);
|
| + SessionCommand* command = new SessionCommand(kCommandSetWindowBounds3,
|
| + sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetTabIndexInWindow(
|
| + const SessionID& tab_id,
|
| + int new_index) {
|
| + TabIndexInWindowPayload payload = { 0 };
|
| + payload.id = tab_id.id();
|
| + payload.index = new_index;
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandSetTabIndexInWindow, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateTabClosed(
|
| + const SessionID::id_type tab_id) {
|
| + ClosedPayload payload;
|
| + // Because of what appears to be a compiler bug setting payload to {0} doesn't
|
| + // set the padding to 0, resulting in Purify reporting an UMR when we write
|
| + // the structure to disk. To avoid this we explicitly memset the struct.
|
| + memset(&payload, 0, sizeof(payload));
|
| + payload.id = tab_id;
|
| + payload.close_time = base::Time::Now().ToInternalValue();
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandTabClosed, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateWindowClosed(
|
| + const SessionID::id_type window_id) {
|
| + ClosedPayload payload;
|
| + // See comment in CreateTabClosedCommand as to why we do this.
|
| + memset(&payload, 0, sizeof(payload));
|
| + payload.id = window_id;
|
| + payload.close_time = base::Time::Now().ToInternalValue();
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandWindowClosed, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetSelectedNavigationIndex(
|
| + const SessionID& tab_id,
|
| + int index) {
|
| + SelectedNavigationIndexPayload payload = { 0 };
|
| + payload.id = tab_id.id();
|
| + payload.index = index;
|
| + SessionCommand* command = new SessionCommand(
|
| + kCommandSetSelectedNavigationIndex, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetWindowType(
|
| + const SessionID& window_id,
|
| + SessionWindow::WindowType type) {
|
| + WindowTypePayload payload = { 0 };
|
| + payload.id = window_id.id();
|
| + payload.index = static_cast<int32>(type);
|
| + SessionCommand* command = new SessionCommand(
|
| + kCommandSetWindowType, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreatePinnedState(
|
| + const SessionID& tab_id,
|
| + bool is_pinned) {
|
| + PinnedStatePayload payload = { 0 };
|
| + payload.tab_id = tab_id.id();
|
| + payload.pinned_state = is_pinned;
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandSetPinnedState, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSessionStorageAssociated(
|
| + const SessionID& tab_id,
|
| + const std::string& session_storage_persistent_id) {
|
| + Pickle pickle;
|
| + pickle.WriteInt(tab_id.id());
|
| + pickle.WriteString(session_storage_persistent_id);
|
| + return new SessionCommand(kCommandSessionStorageAssociated, pickle);
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetActiveWindow(
|
| + const SessionID& window_id) {
|
| + ActiveWindowPayload payload = 0;
|
| + payload = window_id.id();
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandSetActiveWindow, sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateTabNavigationPathPrunedFromBack(
|
| + const SessionID& tab_id,
|
| + int count) {
|
| + TabNavigationPathPrunedFromBackPayload payload = { 0 };
|
| + payload.id = tab_id.id();
|
| + payload.index = count;
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandTabNavigationPathPrunedFromBack,
|
| + sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateTabNavigationPathPrunedFromFront(
|
| + const SessionID& tab_id,
|
| + int count) {
|
| + TabNavigationPathPrunedFromFrontPayload payload = { 0 };
|
| + payload.id = tab_id.id();
|
| + payload.index = count;
|
| + SessionCommand* command =
|
| + new SessionCommand(kCommandTabNavigationPathPrunedFromFront,
|
| + sizeof(payload));
|
| + memcpy(command->contents(), &payload, sizeof(payload));
|
| + return command;
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateUpdateTabNavigation(
|
| + const SessionID& tab_id,
|
| + const sessions::SerializedNavigationEntry& navigation) {
|
| + // TODO(skuhne): Check why this command needs to be called from the base.
|
| + // thus: could eliminate the use of that class here.
|
| + return CreateUpdateTabNavigationCommand(
|
| + kCommandUpdateTabNavigation,
|
| + tab_id.id(),
|
| + navigation);
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetTabExtensionAppID(
|
| + const SessionID& tab_id,
|
| + const std::string& extension_id) {
|
| + return CreateSetTabExtensionAppIDCommand(
|
| + kCommandSetExtensionAppID,
|
| + tab_id.id(),
|
| + extension_id);
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetTabUserAgentOverride(
|
| + const SessionID& tab_id,
|
| + const std::string& user_agent_override) {
|
| + return CreateSetTabUserAgentOverrideCommand(
|
| + kCommandSetTabUserAgentOverride,
|
| + tab_id.id(),
|
| + user_agent_override);
|
| +}
|
| +
|
| +SessionCommand* SessionServiceCommands::CreateSetWindowAppName(
|
| + const SessionID& window_id,
|
| + const std::string& app_name) {
|
| + return CreateSetWindowAppNameCommand(
|
| + kCommandSetWindowAppName,
|
| + window_id.id(),
|
| + app_name);
|
| +}
|
| +
|
| +void SessionServiceCommands::ScheduleCommand(SessionCommand* command) {
|
| + DCHECK(command);
|
| + if (ReplacePendingCommand(command))
|
| + return;
|
| + BaseSessionService::ScheduleCommand(command);
|
| + // Don't schedule a reset on tab closed/window closed. Otherwise we may
|
| + // lose tabs/windows we want to restore from if we exit right after this.
|
| + if (commands_since_reset() >= kWritesPerReset &&
|
| + (command->id() != kCommandTabClosed &&
|
| + command->id() != kCommandWindowClosed)) {
|
| + ScheduleResetCommands();
|
| + }
|
| +}
|
| +
|
| +void SessionServiceCommands::OnGotSessionServiceCommands(
|
| + const SessionCallback& callback,
|
| + ScopedVector<SessionCommand> commands) {
|
| + ScopedVector<SessionWindow> valid_windows;
|
| + SessionID::id_type active_window_id = 0;
|
| +
|
| + RestoreSessionFromCommands(
|
| + commands.get(), &valid_windows.get(), &active_window_id);
|
| + callback.Run(valid_windows.Pass(), active_window_id);
|
| +}
|
| +
|
| +void SessionServiceCommands::RestoreSessionFromCommands(
|
| + const std::vector<SessionCommand*>& commands,
|
| + std::vector<SessionWindow*>* valid_windows,
|
| + SessionID::id_type* active_window_id) {
|
| + std::map<int, SessionTab*> tabs;
|
| + std::map<int, SessionWindow*> windows;
|
| +
|
| + VLOG(1) << "RestoreSessionFromCommands " << commands.size();
|
| + if (CreateTabsAndWindows(commands, &tabs, &windows, active_window_id)) {
|
| + AddTabsToWindows(&tabs, &windows);
|
| + SortTabsBasedOnVisualOrderAndPrune(&windows, valid_windows);
|
| + UpdateSelectedTabIndex(valid_windows);
|
| + }
|
| + STLDeleteValues(&tabs);
|
| + // Don't delete contents of windows, that is done by the caller as all
|
| + // valid windows are added to valid_windows.
|
| +}
|
| +
|
| +void SessionServiceCommands::UpdateSelectedTabIndex(
|
| + std::vector<SessionWindow*>* windows) {
|
| + for (std::vector<SessionWindow*>::const_iterator i = windows->begin();
|
| + i != windows->end(); ++i) {
|
| + // See note in SessionWindow as to why we do this.
|
| + int new_index = 0;
|
| + for (std::vector<SessionTab*>::const_iterator j = (*i)->tabs.begin();
|
| + j != (*i)->tabs.end(); ++j) {
|
| + if ((*j)->tab_visual_index == (*i)->selected_tab_index) {
|
| + new_index = static_cast<int>(j - (*i)->tabs.begin());
|
| + break;
|
| + }
|
| + }
|
| + (*i)->selected_tab_index = new_index;
|
| + }
|
| +}
|
| +
|
| +SessionWindow* SessionServiceCommands::GetWindow(SessionID::id_type window_id,
|
| + IdToSessionWindow* windows) {
|
| + std::map<int, SessionWindow*>::iterator i = windows->find(window_id);
|
| + if (i == windows->end()) {
|
| + SessionWindow* window = new SessionWindow();
|
| + window->window_id.set_id(window_id);
|
| + (*windows)[window_id] = window;
|
| + return window;
|
| + }
|
| + return i->second;
|
| +}
|
| +
|
| +SessionTab* SessionServiceCommands::GetTab(SessionID::id_type tab_id,
|
| + IdToSessionTab* tabs) {
|
| + DCHECK(tabs);
|
| + std::map<int, SessionTab*>::iterator i = tabs->find(tab_id);
|
| + if (i == tabs->end()) {
|
| + SessionTab* tab = new SessionTab();
|
| + tab->tab_id.set_id(tab_id);
|
| + (*tabs)[tab_id] = tab;
|
| + return tab;
|
| + }
|
| + return i->second;
|
| +}
|
| +
|
| +std::vector<sessions::SerializedNavigationEntry>::iterator
|
| + SessionServiceCommands::FindClosestNavigationWithIndex(
|
| + std::vector<sessions::SerializedNavigationEntry>* navigations,
|
| + int index) {
|
| + DCHECK(navigations);
|
| + for (std::vector<sessions::SerializedNavigationEntry>::iterator
|
| + i = navigations->begin(); i != navigations->end(); ++i) {
|
| + if (i->index() >= index)
|
| + return i;
|
| + }
|
| + return navigations->end();
|
| +}
|
| +
|
| +// Function used in sorting windows. Sorting is done based on window id. As
|
| +// window ids increment for each new window, this effectively sorts by creation
|
| +// time.
|
| +static bool WindowOrderSortFunction(const SessionWindow* w1,
|
| + const SessionWindow* w2) {
|
| + return w1->window_id.id() < w2->window_id.id();
|
| +}
|
| +
|
| +// Compares the two tabs based on visual index.
|
| +static bool TabVisualIndexSortFunction(const SessionTab* t1,
|
| + const SessionTab* t2) {
|
| + const int delta = t1->tab_visual_index - t2->tab_visual_index;
|
| + return delta == 0 ? (t1->tab_id.id() < t2->tab_id.id()) : (delta < 0);
|
| +}
|
| +
|
| +void SessionServiceCommands::SortTabsBasedOnVisualOrderAndPrune(
|
| + std::map<int, SessionWindow*>* windows,
|
| + std::vector<SessionWindow*>* valid_windows) {
|
| + std::map<int, SessionWindow*>::iterator i = windows->begin();
|
| + while (i != windows->end()) {
|
| + SessionWindow* window = i->second;
|
| + AppType app_type = window->app_name.empty() ? TYPE_NORMAL : TYPE_APP;
|
| + if (window->tabs.empty() || window->is_constrained ||
|
| + !ShouldTrackChangesOfWindowType(window->type, app_type)) {
|
| + delete window;
|
| + windows->erase(i++);
|
| + } else {
|
| + // Valid window; sort the tabs and add it to the list of valid windows.
|
| + std::sort(window->tabs.begin(), window->tabs.end(),
|
| + &TabVisualIndexSortFunction);
|
| + // Otherwise, add the window such that older windows appear first.
|
| + if (valid_windows->empty()) {
|
| + valid_windows->push_back(window);
|
| + } else {
|
| + valid_windows->insert(
|
| + std::upper_bound(valid_windows->begin(), valid_windows->end(),
|
| + window, &WindowOrderSortFunction),
|
| + window);
|
| + }
|
| + ++i;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void SessionServiceCommands::AddTabsToWindows(std::map<int, SessionTab*>* tabs,
|
| + std::map<int, SessionWindow*>* windows) {
|
| + VLOG(1) << "AddTabsToWindws";
|
| + VLOG(1) << "Tabs " << tabs->size() << ", windows " << windows->size();
|
| + std::map<int, SessionTab*>::iterator i = tabs->begin();
|
| + while (i != tabs->end()) {
|
| + SessionTab* tab = i->second;
|
| + if (tab->window_id.id() && !tab->navigations.empty()) {
|
| + SessionWindow* window = GetWindow(tab->window_id.id(), windows);
|
| + window->tabs.push_back(tab);
|
| + tabs->erase(i++);
|
| +
|
| + // See note in SessionTab as to why we do this.
|
| + std::vector<sessions::SerializedNavigationEntry>::iterator j =
|
| + FindClosestNavigationWithIndex(&(tab->navigations),
|
| + tab->current_navigation_index);
|
| + if (j == tab->navigations.end()) {
|
| + tab->current_navigation_index =
|
| + static_cast<int>(tab->navigations.size() - 1);
|
| + } else {
|
| + tab->current_navigation_index =
|
| + static_cast<int>(j - tab->navigations.begin());
|
| + }
|
| + } else {
|
| + // Never got a set tab index in window, or tabs are empty, nothing
|
| + // to do.
|
| + ++i;
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool SessionServiceCommands::CreateTabsAndWindows(
|
| + const std::vector<SessionCommand*>& data,
|
| + std::map<int, SessionTab*>* tabs,
|
| + std::map<int, SessionWindow*>* windows,
|
| + SessionID::id_type* active_window_id) {
|
| + // If the file is corrupt (command with wrong size, or unknown command), we
|
| + // still return true and attempt to restore what we we can.
|
| + VLOG(1) << "CreateTabsAndWindows";
|
| +
|
| + startup_metric_utils::ScopedSlowStartupUMA
|
| + scoped_timer("Startup.SlowStartupSessionServiceCreateTabsAndWindows");
|
| +
|
| + for (std::vector<SessionCommand*>::const_iterator i = data.begin();
|
| + i != data.end(); ++i) {
|
| + const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
|
| + const SessionCommand* command = *i;
|
| +
|
| + VLOG(1) << "Read command " << (int) command->id();
|
| + switch (command->id()) {
|
| + case kCommandSetTabWindow: {
|
| + SessionID::id_type payload[2];
|
| + if (!command->GetPayload(payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + GetTab(payload[1], tabs)->window_id.set_id(payload[0]);
|
| + break;
|
| + }
|
| +
|
| + // This is here for forward migration only. New data is saved with
|
| + // |kCommandSetWindowBounds3|.
|
| + case kCommandSetWindowBounds2: {
|
| + WindowBoundsPayload2 payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x,
|
| + payload.y,
|
| + payload.w,
|
| + payload.h);
|
| + GetWindow(payload.window_id, windows)->show_state =
|
| + payload.is_maximized ?
|
| + ui::SHOW_STATE_MAXIMIZED : ui::SHOW_STATE_NORMAL;
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetWindowBounds3: {
|
| + WindowBoundsPayload3 payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x,
|
| + payload.y,
|
| + payload.w,
|
| + payload.h);
|
| + GetWindow(payload.window_id, windows)->show_state =
|
| + PersistedShowStateToShowState(payload.show_state);
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetTabIndexInWindow: {
|
| + TabIndexInWindowPayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + GetTab(payload.id, tabs)->tab_visual_index = payload.index;
|
| + break;
|
| + }
|
| +
|
| + case kCommandTabClosed:
|
| + case kCommandWindowClosed: {
|
| + ClosedPayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + if (command->id() == kCommandTabClosed) {
|
| + delete GetTab(payload.id, tabs);
|
| + tabs->erase(payload.id);
|
| + } else {
|
| + delete GetWindow(payload.id, windows);
|
| + windows->erase(payload.id);
|
| + }
|
| + break;
|
| + }
|
| +
|
| + case kCommandTabNavigationPathPrunedFromBack: {
|
| + TabNavigationPathPrunedFromBackPayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + SessionTab* tab = GetTab(payload.id, tabs);
|
| + tab->navigations.erase(
|
| + FindClosestNavigationWithIndex(&(tab->navigations), payload.index),
|
| + tab->navigations.end());
|
| + break;
|
| + }
|
| +
|
| + case kCommandTabNavigationPathPrunedFromFront: {
|
| + TabNavigationPathPrunedFromFrontPayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload)) ||
|
| + payload.index <= 0) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + SessionTab* tab = GetTab(payload.id, tabs);
|
| +
|
| + // Update the selected navigation index.
|
| + tab->current_navigation_index =
|
| + std::max(-1, tab->current_navigation_index - payload.index);
|
| +
|
| + // And update the index of existing navigations.
|
| + for (std::vector<sessions::SerializedNavigationEntry>::iterator
|
| + i = tab->navigations.begin();
|
| + i != tab->navigations.end();) {
|
| + i->set_index(i->index() - payload.index);
|
| + if (i->index() < 0)
|
| + i = tab->navigations.erase(i);
|
| + else
|
| + ++i;
|
| + }
|
| + break;
|
| + }
|
| +
|
| + case kCommandUpdateTabNavigation: {
|
| + sessions::SerializedNavigationEntry navigation;
|
| + SessionID::id_type tab_id;
|
| + if (!RestoreUpdateTabNavigationCommand(
|
| + *command, &navigation, &tab_id)) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + SessionTab* tab = GetTab(tab_id, tabs);
|
| + std::vector<sessions::SerializedNavigationEntry>::iterator i =
|
| + FindClosestNavigationWithIndex(&(tab->navigations),
|
| + navigation.index());
|
| + if (i != tab->navigations.end() && i->index() == navigation.index())
|
| + *i = navigation;
|
| + else
|
| + tab->navigations.insert(i, navigation);
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetSelectedNavigationIndex: {
|
| + SelectedNavigationIndexPayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + GetTab(payload.id, tabs)->current_navigation_index = payload.index;
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetSelectedTabInIndex: {
|
| + SelectedTabInIndexPayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + GetWindow(payload.id, windows)->selected_tab_index = payload.index;
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetWindowType: {
|
| + WindowTypePayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + GetWindow(payload.id, windows)->is_constrained = false;
|
| + GetWindow(payload.id, windows)->type =
|
| + static_cast<SessionWindow::WindowType>(payload.index);
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetPinnedState: {
|
| + PinnedStatePayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + GetTab(payload.tab_id, tabs)->pinned = payload.pinned_state;
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetWindowAppName: {
|
| + SessionID::id_type window_id;
|
| + std::string app_name;
|
| + if (!RestoreSetWindowAppNameCommand(*command, &window_id, &app_name))
|
| + return true;
|
| +
|
| + GetWindow(window_id, windows)->app_name.swap(app_name);
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetExtensionAppID: {
|
| + SessionID::id_type tab_id;
|
| + std::string extension_app_id;
|
| + if (!RestoreSetTabExtensionAppIDCommand(
|
| + *command, &tab_id, &extension_app_id)) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| +
|
| + GetTab(tab_id, tabs)->extension_app_id.swap(extension_app_id);
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetTabUserAgentOverride: {
|
| + SessionID::id_type tab_id;
|
| + std::string user_agent_override;
|
| + if (!RestoreSetTabUserAgentOverrideCommand(
|
| + *command, &tab_id, &user_agent_override)) {
|
| + return true;
|
| + }
|
| +
|
| + GetTab(tab_id, tabs)->user_agent_override.swap(user_agent_override);
|
| + break;
|
| + }
|
| +
|
| + case kCommandSessionStorageAssociated: {
|
| + scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle());
|
| + SessionID::id_type command_tab_id;
|
| + std::string session_storage_persistent_id;
|
| + PickleIterator iter(*command_pickle.get());
|
| + if (!command_pickle->ReadInt(&iter, &command_tab_id) ||
|
| + !command_pickle->ReadString(&iter, &session_storage_persistent_id))
|
| + return true;
|
| + // Associate the session storage back.
|
| + GetTab(command_tab_id, tabs)->session_storage_persistent_id =
|
| + session_storage_persistent_id;
|
| + break;
|
| + }
|
| +
|
| + case kCommandSetActiveWindow: {
|
| + ActiveWindowPayload payload;
|
| + if (!command->GetPayload(&payload, sizeof(payload))) {
|
| + VLOG(1) << "Failed reading command " << command->id();
|
| + return true;
|
| + }
|
| + *active_window_id = payload;
|
| + break;
|
| + }
|
| +
|
| + default:
|
| + // TODO(skuhne): This might call back into a callback handler to extend
|
| + // the command set for specific implementations.
|
| + VLOG(1) << "Failed reading an unknown command " << command->id();
|
| + return true;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool SessionServiceCommands::ReplacePendingCommand(SessionCommand* command) {
|
| + // We optimize page navigations, which can happen quite frequently and
|
| + // are expensive. And activation is like Highlander, there can only be one!
|
| + if (command->id() != kCommandUpdateTabNavigation &&
|
| + command->id() != kCommandSetActiveWindow) {
|
| + return false;
|
| + }
|
| + for (std::vector<SessionCommand*>::reverse_iterator i =
|
| + pending_commands().rbegin(); i != pending_commands().rend(); ++i) {
|
| + SessionCommand* existing_command = *i;
|
| + if (command->id() == kCommandUpdateTabNavigation &&
|
| + existing_command->id() == kCommandUpdateTabNavigation) {
|
| + scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle());
|
| + PickleIterator iterator(*command_pickle);
|
| + SessionID::id_type command_tab_id;
|
| + int command_nav_index;
|
| + if (!command_pickle->ReadInt(&iterator, &command_tab_id) ||
|
| + !command_pickle->ReadInt(&iterator, &command_nav_index)) {
|
| + return false;
|
| + }
|
| + SessionID::id_type existing_tab_id;
|
| + int existing_nav_index;
|
| + {
|
| + // Creating a pickle like this means the Pickle references the data from
|
| + // the command. Make sure we delete the pickle before the command, else
|
| + // the pickle references deleted memory.
|
| + scoped_ptr<Pickle> existing_pickle(existing_command->PayloadAsPickle());
|
| + iterator = PickleIterator(*existing_pickle);
|
| + if (!existing_pickle->ReadInt(&iterator, &existing_tab_id) ||
|
| + !existing_pickle->ReadInt(&iterator, &existing_nav_index)) {
|
| + return false;
|
| + }
|
| + }
|
| + if (existing_tab_id == command_tab_id &&
|
| + existing_nav_index == command_nav_index) {
|
| + // existing_command is an update for the same tab/index pair. Replace
|
| + // it with the new one. We need to add to the end of the list just in
|
| + // case there is a prune command after the update command.
|
| + delete existing_command;
|
| + pending_commands().erase(i.base() - 1);
|
| + pending_commands().push_back(command);
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + if (command->id() == kCommandSetActiveWindow &&
|
| + existing_command->id() == kCommandSetActiveWindow) {
|
| + *i = command;
|
| + delete existing_command;
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
|
|