blob: 1de12aae6fb8d30fd01d3508ac23faa0cc23c7a7 [file] [log] [blame]
// 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 "components/sessions/core/session_service_commands.h"
#include <stdint.h>
#include <string.h>
#include <utility>
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/pickle.h"
#include "components/sessions/core/base_session_service_commands.h"
#include "components/sessions/core/base_session_service_delegate.h"
#include "components/sessions/core/session_command.h"
#include "components/sessions/core/session_types.h"
namespace sessions {
// 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;
static const SessionCommand::id_type kCommandLastActiveTime = 21;
// OBSOLETE Superseded by kCommandSetWindowWorkspace2.
// static const SessionCommand::id_type kCommandSetWindowWorkspace = 22;
static const SessionCommand::id_type kCommandSetWindowWorkspace2 = 23;
namespace {
// Various payload structures.
struct ClosedPayload {
SessionID::id_type id;
int64_t close_time;
};
struct WindowBoundsPayload2 {
SessionID::id_type window_id;
int32_t x;
int32_t y;
int32_t w;
int32_t h;
bool is_maximized;
};
struct WindowBoundsPayload3 {
SessionID::id_type window_id;
int32_t x;
int32_t y;
int32_t w;
int32_t h;
int32_t show_state;
};
using ActiveWindowPayload = SessionID::id_type;
struct IDAndIndexPayload {
SessionID::id_type id;
int32_t index;
};
using TabIndexInWindowPayload = IDAndIndexPayload;
using TabNavigationPathPrunedFromBackPayload = IDAndIndexPayload;
using SelectedNavigationIndexPayload = IDAndIndexPayload;
using SelectedTabInIndexPayload = IDAndIndexPayload;
using WindowTypePayload = IDAndIndexPayload;
using TabNavigationPathPrunedFromFrontPayload = IDAndIndexPayload;
struct PinnedStatePayload {
SessionID::id_type tab_id;
bool pinned_state;
};
struct LastActiveTimePayload {
SessionID::id_type tab_id;
int64_t last_active_time;
};
// 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_DOCKED_DEPRECATED = 7,
PERSISTED_SHOW_STATE_END = 8,
};
using IdToSessionTab = std::map<SessionID, std::unique_ptr<SessionTab>>;
using IdToSessionWindow = std::map<SessionID, std::unique_ptr<SessionWindow>>;
// Assert to ensure PersistedWindowShowState is updated if ui::WindowShowState
// is changed.
static_assert(ui::SHOW_STATE_END ==
(static_cast<ui::WindowShowState>(PERSISTED_SHOW_STATE_END) -
2),
"SHOW_STATE_END must equal PERSISTED_SHOW_STATE_END minus the "
"deprecated entries");
// 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:
case PERSISTED_SHOW_STATE_DOCKED_DEPRECATED:
return ui::SHOW_STATE_NORMAL;
}
NOTREACHED();
return ui::SHOW_STATE_NORMAL;
}
// Iterates through the vector updating the selected_tab_index of each
// SessionWindow based on the actual tabs that were restored.
void UpdateSelectedTabIndex(
std::vector<std::unique_ptr<SessionWindow>>* windows) {
for (auto& window : *windows) {
// See note in SessionWindow as to why we do this.
int new_index = 0;
for (auto j = window->tabs.begin(); j != window->tabs.end(); ++j) {
if ((*j)->tab_visual_index == window->selected_tab_index) {
new_index = static_cast<int>(j - window->tabs.begin());
break;
}
}
window->selected_tab_index = new_index;
}
}
// Returns the window in windows with the specified id. If a window does
// not exist, one is created.
SessionWindow* GetWindow(SessionID window_id, IdToSessionWindow* windows) {
auto i = windows->find(window_id);
if (i == windows->end()) {
SessionWindow* window = new SessionWindow();
window->window_id = window_id;
(*windows)[window_id] = base::WrapUnique(window);
return window;
}
return i->second.get();
}
// Returns the tab with the specified id in tabs. If a tab does not exist,
// it is created.
SessionTab* GetTab(SessionID tab_id, IdToSessionTab* tabs) {
DCHECK(tabs);
auto i = tabs->find(tab_id);
if (i == tabs->end()) {
SessionTab* tab = new SessionTab();
tab->tab_id = tab_id;
(*tabs)[tab_id] = base::WrapUnique(tab);
return tab;
}
return i->second.get();
}
// Returns an iterator into navigations pointing to the navigation whose
// index matches |index|. If no navigation index matches |index|, the first
// navigation with an index > |index| is returned.
//
// This assumes the navigations are ordered by index in ascending order.
std::vector<sessions::SerializedNavigationEntry>::iterator
FindClosestNavigationWithIndex(
std::vector<sessions::SerializedNavigationEntry>* navigations,
int index) {
DCHECK(navigations);
for (auto 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 std::unique_ptr<SessionWindow>& w1,
const std::unique_ptr<SessionWindow>& w2) {
return w1->window_id.id() < w2->window_id.id();
}
// Compares the two tabs based on visual index.
static bool TabVisualIndexSortFunction(const std::unique_ptr<SessionTab>& t1,
const std::unique_ptr<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);
}
// Does the following:
// . Deletes and removes any windows with no tabs. NOTE: constrained windows
// that have been dragged out are of type browser. As such, this preserves any
// dragged out constrained windows (aka popups that have been dragged out).
// . Sorts the tabs in windows with valid tabs based on the tabs;
// visual order, and adds the valid windows to |valid_windows|.
void SortTabsBasedOnVisualOrderAndClear(
IdToSessionWindow* windows,
std::vector<std::unique_ptr<SessionWindow>>* valid_windows) {
for (auto& window_pair : *windows) {
std::unique_ptr<SessionWindow> window = std::move(window_pair.second);
if (window->tabs.empty() || window->is_constrained) {
continue;
} else {
// Valid window; sort the tabs and add it to the list of valid windows.
std::sort(window->tabs.begin(), window->tabs.end(),
&TabVisualIndexSortFunction);
// Add the window such that older windows appear first.
if (valid_windows->empty()) {
valid_windows->push_back(std::move(window));
} else {
valid_windows->insert(
std::upper_bound(valid_windows->begin(), valid_windows->end(),
window, &WindowOrderSortFunction),
std::move(window));
}
}
}
// There are no more pointers left in |window|, just empty husks from the
// move, so clear it out.
windows->clear();
}
// Adds tabs to their parent window based on the tab's window_id. This
// ignores tabs with no navigations.
void AddTabsToWindows(IdToSessionTab* tabs, IdToSessionWindow* windows) {
DVLOG(1) << "AddTabsToWindows";
DVLOG(1) << "Tabs " << tabs->size() << ", windows " << windows->size();
for (auto& tab_pair : *tabs) {
std::unique_ptr<SessionTab> tab = std::move(tab_pair.second);
if (!tab->window_id.id() || tab->navigations.empty())
continue;
SessionTab* tab_ptr = tab.get();
SessionWindow* window = GetWindow(tab_ptr->window_id, windows);
window->tabs.push_back(std::move(tab));
// See note in SessionTab as to why we do this.
auto j = FindClosestNavigationWithIndex(&tab_ptr->navigations,
tab_ptr->current_navigation_index);
if (j == tab_ptr->navigations.end()) {
tab_ptr->current_navigation_index =
static_cast<int>(tab_ptr->navigations.size() - 1);
} else {
tab_ptr->current_navigation_index =
static_cast<int>(j - tab_ptr->navigations.begin());
}
}
// There are no more pointers left in |tabs|, just empty husks from the
// move, so clear it out.
tabs->clear();
}
// Creates tabs and windows from the commands specified in |data|. The created
// tabs and windows are added to |tabs| and |windows| respectively, with the
// id of the active window set in |active_window_id|. It is up to the caller
// to delete the tabs and windows added to |tabs| and |windows|.
//
// This does NOT add any created SessionTabs to SessionWindow.tabs, that is
// done by AddTabsToWindows.
bool CreateTabsAndWindows(
const std::vector<std::unique_ptr<SessionCommand>>& data,
IdToSessionTab* tabs,
IdToSessionWindow* windows,
SessionID* 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.
DVLOG(1) << "CreateTabsAndWindows";
for (const auto& command_ptr : data) {
const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
const SessionCommand* command = command_ptr.get();
DVLOG(1) << "Read command " << (int) command->id();
switch (command->id()) {
case kCommandSetTabWindow: {
SessionID::id_type payload[2];
if (!command->GetPayload(payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionID window_id = SessionID::FromSerializedValue(payload[0]);
SessionID tab_id = SessionID::FromSerializedValue(payload[1]);
GetTab(tab_id, tabs)->window_id = window_id;
break;
}
// This is here for forward migration only. New data is saved with
// |kCommandSetWindowBounds3|.
case kCommandSetWindowBounds2: {
WindowBoundsPayload2 payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionID window_id = SessionID::FromSerializedValue(payload.window_id);
GetWindow(window_id, windows)
->bounds.SetRect(payload.x, payload.y, payload.w, payload.h);
GetWindow(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))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionID window_id = SessionID::FromSerializedValue(payload.window_id);
GetWindow(window_id, windows)
->bounds.SetRect(payload.x, payload.y, payload.w, payload.h);
GetWindow(window_id, windows)->show_state =
PersistedShowStateToShowState(payload.show_state);
break;
}
case kCommandSetTabIndexInWindow: {
TabIndexInWindowPayload payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionID tab_id = SessionID::FromSerializedValue(payload.id);
GetTab(tab_id, tabs)->tab_visual_index = payload.index;
break;
}
case kCommandTabClosed:
case kCommandWindowClosed: {
ClosedPayload payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
if (command->id() == kCommandTabClosed)
tabs->erase(SessionID::FromSerializedValue(payload.id));
else
windows->erase(SessionID::FromSerializedValue(payload.id));
break;
}
case kCommandTabNavigationPathPrunedFromBack: {
TabNavigationPathPrunedFromBackPayload payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionTab* tab =
GetTab(SessionID::FromSerializedValue(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) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionTab* tab =
GetTab(SessionID::FromSerializedValue(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 (auto 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 tab_id = SessionID::InvalidValue();
if (!RestoreUpdateTabNavigationCommand(*command,
&navigation,
&tab_id)) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionTab* tab = GetTab(tab_id, tabs);
auto 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))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
GetTab(SessionID::FromSerializedValue(payload.id), tabs)
->current_navigation_index = payload.index;
break;
}
case kCommandSetSelectedTabInIndex: {
SelectedTabInIndexPayload payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
GetWindow(SessionID::FromSerializedValue(payload.id), windows)
->selected_tab_index = payload.index;
break;
}
case kCommandSetWindowType: {
WindowTypePayload payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionID window_id = SessionID::FromSerializedValue(payload.id);
GetWindow(window_id, windows)->is_constrained = false;
GetWindow(window_id, windows)->type =
static_cast<SessionWindow::WindowType>(payload.index);
break;
}
case kCommandSetPinnedState: {
PinnedStatePayload payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
GetTab(SessionID::FromSerializedValue(payload.tab_id), tabs)->pinned =
payload.pinned_state;
break;
}
case kCommandSetWindowAppName: {
SessionID window_id = SessionID::InvalidValue();
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 tab_id = SessionID::InvalidValue();
std::string extension_app_id;
if (!RestoreSetTabExtensionAppIDCommand(*command,
&tab_id,
&extension_app_id)) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
GetTab(tab_id, tabs)->extension_app_id.swap(extension_app_id);
break;
}
case kCommandSetTabUserAgentOverride: {
SessionID tab_id = SessionID::InvalidValue();
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: {
std::unique_ptr<base::Pickle> command_pickle(
command->PayloadAsPickle());
SessionID::id_type command_tab_id;
std::string session_storage_persistent_id;
base::PickleIterator iter(*command_pickle);
if (!iter.ReadInt(&command_tab_id) ||
!iter.ReadString(&session_storage_persistent_id))
return true;
// Associate the session storage back.
GetTab(SessionID::FromSerializedValue(command_tab_id), tabs)
->session_storage_persistent_id = session_storage_persistent_id;
break;
}
case kCommandSetActiveWindow: {
ActiveWindowPayload payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
*active_window_id = SessionID::FromSerializedValue(payload);
break;
}
case kCommandLastActiveTime: {
LastActiveTimePayload payload;
if (!command->GetPayload(&payload, sizeof(payload))) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
SessionTab* tab =
GetTab(SessionID::FromSerializedValue(payload.tab_id), tabs);
tab->last_active_time =
base::TimeTicks::FromInternalValue(payload.last_active_time);
break;
}
case kCommandSetWindowWorkspace2: {
std::unique_ptr<base::Pickle> pickle(command->PayloadAsPickle());
base::PickleIterator it(*pickle);
SessionID::id_type window_id = -1;
std::string workspace;
if (!it.ReadInt(&window_id) || !it.ReadString(&workspace)) {
DVLOG(1) << "Failed reading command " << command->id();
return true;
}
GetWindow(SessionID::FromSerializedValue(window_id), windows)
->workspace = workspace;
break;
}
default:
// TODO(skuhne): This might call back into a callback handler to extend
// the command set for specific implementations.
DVLOG(1) << "Failed reading an unknown command " << command->id();
return true;
}
}
return true;
}
} // namespace
std::unique_ptr<SessionCommand> CreateSetSelectedTabInWindowCommand(
const SessionID& window_id,
int index) {
SelectedTabInIndexPayload payload = { 0 };
payload.id = window_id.id();
payload.index = index;
std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
kCommandSetSelectedTabInIndex, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateSetTabWindowCommand(
const SessionID& window_id,
const SessionID& tab_id) {
SessionID::id_type payload[] = { window_id.id(), tab_id.id() };
std::unique_ptr<SessionCommand> command =
std::make_unique<SessionCommand>(kCommandSetTabWindow, sizeof(payload));
memcpy(command->contents(), payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateSetWindowBoundsCommand(
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);
std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
kCommandSetWindowBounds3, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateSetTabIndexInWindowCommand(
const SessionID& tab_id,
int new_index) {
TabIndexInWindowPayload payload = { 0 };
payload.id = tab_id.id();
payload.index = new_index;
std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
kCommandSetTabIndexInWindow, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateTabClosedCommand(const SessionID 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.id();
payload.close_time = base::Time::Now().ToInternalValue();
std::unique_ptr<SessionCommand> command =
std::make_unique<SessionCommand>(kCommandTabClosed, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateWindowClosedCommand(
const SessionID window_id) {
ClosedPayload payload;
// See comment in CreateTabClosedCommand as to why we do this.
memset(&payload, 0, sizeof(payload));
payload.id = window_id.id();
payload.close_time = base::Time::Now().ToInternalValue();
std::unique_ptr<SessionCommand> command =
std::make_unique<SessionCommand>(kCommandWindowClosed, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateSetSelectedNavigationIndexCommand(
const SessionID& tab_id,
int index) {
SelectedNavigationIndexPayload payload = { 0 };
payload.id = tab_id.id();
payload.index = index;
std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
kCommandSetSelectedNavigationIndex, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateSetWindowTypeCommand(
const SessionID& window_id,
SessionWindow::WindowType type) {
WindowTypePayload payload = { 0 };
payload.id = window_id.id();
payload.index = static_cast<int32_t>(type);
std::unique_ptr<SessionCommand> command =
std::make_unique<SessionCommand>(kCommandSetWindowType, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreatePinnedStateCommand(
const SessionID& tab_id,
bool is_pinned) {
PinnedStatePayload payload = { 0 };
payload.tab_id = tab_id.id();
payload.pinned_state = is_pinned;
std::unique_ptr<SessionCommand> command =
std::make_unique<SessionCommand>(kCommandSetPinnedState, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateSessionStorageAssociatedCommand(
const SessionID& tab_id,
const std::string& session_storage_persistent_id) {
base::Pickle pickle;
pickle.WriteInt(tab_id.id());
pickle.WriteString(session_storage_persistent_id);
return std::unique_ptr<SessionCommand>(std::make_unique<SessionCommand>(
kCommandSessionStorageAssociated, pickle));
}
std::unique_ptr<SessionCommand> CreateSetActiveWindowCommand(
const SessionID& window_id) {
ActiveWindowPayload payload = 0;
payload = window_id.id();
std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
kCommandSetActiveWindow, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateLastActiveTimeCommand(
const SessionID& tab_id,
base::TimeTicks last_active_time) {
LastActiveTimePayload payload = {0};
payload.tab_id = tab_id.id();
payload.last_active_time = last_active_time.ToInternalValue();
std::unique_ptr<SessionCommand> command =
std::make_unique<SessionCommand>(kCommandLastActiveTime, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateSetWindowWorkspaceCommand(
const SessionID& window_id,
const std::string& workspace) {
base::Pickle pickle;
pickle.WriteInt(window_id.id());
pickle.WriteString(workspace);
std::unique_ptr<SessionCommand> command =
std::make_unique<SessionCommand>(kCommandSetWindowWorkspace2, pickle);
return command;
}
std::unique_ptr<SessionCommand> CreateTabNavigationPathPrunedFromBackCommand(
const SessionID& tab_id,
int count) {
TabNavigationPathPrunedFromBackPayload payload = { 0 };
payload.id = tab_id.id();
payload.index = count;
std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
kCommandTabNavigationPathPrunedFromBack, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateTabNavigationPathPrunedFromFrontCommand(
const SessionID& tab_id,
int count) {
TabNavigationPathPrunedFromFrontPayload payload = { 0 };
payload.id = tab_id.id();
payload.index = count;
std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
kCommandTabNavigationPathPrunedFromFront, sizeof(payload));
memcpy(command->contents(), &payload, sizeof(payload));
return command;
}
std::unique_ptr<SessionCommand> CreateUpdateTabNavigationCommand(
const SessionID& tab_id,
const sessions::SerializedNavigationEntry& navigation) {
return CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab_id,
navigation);
}
std::unique_ptr<SessionCommand> CreateSetTabExtensionAppIDCommand(
const SessionID& tab_id,
const std::string& extension_id) {
return CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID, tab_id,
extension_id);
}
std::unique_ptr<SessionCommand> CreateSetTabUserAgentOverrideCommand(
const SessionID& tab_id,
const std::string& user_agent_override) {
return CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
tab_id, user_agent_override);
}
std::unique_ptr<SessionCommand> CreateSetWindowAppNameCommand(
const SessionID& window_id,
const std::string& app_name) {
return CreateSetWindowAppNameCommand(kCommandSetWindowAppName, window_id,
app_name);
}
bool ReplacePendingCommand(BaseSessionService* base_session_service,
std::unique_ptr<SessionCommand>* command) {
// We optimize page navigations, which can happen quite frequently and
// is expensive. And activation is like Highlander, there can only be one!
if ((*command)->id() != kCommandUpdateTabNavigation &&
(*command)->id() != kCommandSetActiveWindow) {
return false;
}
for (auto i = base_session_service->pending_commands().rbegin();
i != base_session_service->pending_commands().rend(); ++i) {
SessionCommand* existing_command = i->get();
if ((*command)->id() == kCommandUpdateTabNavigation &&
existing_command->id() == kCommandUpdateTabNavigation) {
std::unique_ptr<base::Pickle> command_pickle(
(*command)->PayloadAsPickle());
base::PickleIterator iterator(*command_pickle);
SessionID::id_type command_tab_id;
int command_nav_index;
if (!iterator.ReadInt(&command_tab_id) ||
!iterator.ReadInt(&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.
std::unique_ptr<base::Pickle> existing_pickle(
existing_command->PayloadAsPickle());
iterator = base::PickleIterator(*existing_pickle);
if (!iterator.ReadInt(&existing_tab_id) ||
!iterator.ReadInt(&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.
base_session_service->EraseCommand((i.base() - 1)->get());
base_session_service->AppendRebuildCommand(std::move(*command));
return true;
}
return false;
}
if ((*command)->id() == kCommandSetActiveWindow &&
existing_command->id() == kCommandSetActiveWindow) {
base_session_service->SwapCommand(existing_command,
(std::move(*command)));
return true;
}
}
return false;
}
bool IsClosingCommand(SessionCommand* command) {
return command->id() == kCommandTabClosed ||
command->id() == kCommandWindowClosed;
}
void RestoreSessionFromCommands(
const std::vector<std::unique_ptr<SessionCommand>>& commands,
std::vector<std::unique_ptr<SessionWindow>>* valid_windows,
SessionID* active_window_id) {
IdToSessionTab tabs;
IdToSessionWindow windows;
DVLOG(1) << "RestoreSessionFromCommands " << commands.size();
if (CreateTabsAndWindows(commands, &tabs, &windows, active_window_id)) {
AddTabsToWindows(&tabs, &windows);
SortTabsBasedOnVisualOrderAndClear(&windows, valid_windows);
UpdateSelectedTabIndex(valid_windows);
}
// AddTabsToWindows should have processed all the tabs.
DCHECK_EQ(0u, tabs.size());
// SortTabsBasedOnVisualOrderAndClear should have processed all the windows.
DCHECK_EQ(0u, windows.size());
}
} // namespace sessions