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

Unified Diff: components/sessions/core/persistent_tab_restore_service.cc

Issue 2868983003: Ensure History > Recent Tabs restore preserves window disposition. (Closed)
Patch Set: Remove NOTREACHED(). Created 3 years, 4 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
« no previous file with comments | « components/sessions/core/live_tab_context.h ('k') | components/sessions/core/tab_restore_service.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: components/sessions/core/persistent_tab_restore_service.cc
diff --git a/components/sessions/core/persistent_tab_restore_service.cc b/components/sessions/core/persistent_tab_restore_service.cc
index 3a2f3fc7e7153a66b895ea051bc1ded49db0ff30..c70d39d51ac389d02c08a2137a423ea44a5d4db0 100644
--- a/components/sessions/core/persistent_tab_restore_service.cc
+++ b/components/sessions/core/persistent_tab_restore_service.cc
@@ -45,15 +45,16 @@ struct SelectedNavigationInTabPayload {
// 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 {
+struct WindowPayloadObsolete {
SessionID::id_type window_id;
int32_t selected_tab_index;
int32_t num_tabs;
};
-// Payload used for the start of a window close. This struct must be POD,
-// because we memset the contents.
-struct WindowPayload2 : WindowPayload {
+// Payload used for the start of a window close. This struct must be POD,
+// because we memset the contents. This is an older version of the struct that
+// is used for backwards compat when it comes to reading the session files.
+struct WindowPayloadObsolete2 : WindowPayloadObsolete {
int64_t timestamp;
};
@@ -96,12 +97,13 @@ enum LoadState {
// is written.
const SessionCommand::id_type kCommandUpdateTabNavigation = 1;
const SessionCommand::id_type kCommandRestoredEntry = 2;
-const SessionCommand::id_type kCommandWindow = 3;
+const SessionCommand::id_type kCommandWindowDeprecated = 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;
+const SessionCommand::id_type kCommandWindow = 9;
// Number of entries (not commands) before we clobber the file and write
// everything.
@@ -135,6 +137,202 @@ void RemoveEntryByID(
}
}
+// An enum that corresponds to ui::WindowShowStates. This needs to be kept in
+// sync with that enum. Moreover, the integer values corresponding to each show
+// state need to be stable in this enum (which is not necessarily true about the
+// ui::WindowShowStates enum).
+enum SerializedWindowShowState : int {
+ kSerializedShowStateInvalid = -1,
+ kSerializedShowStateDefault = 0,
+ kSerializedShowStateNormal = 1,
+ kSerializedShowStateMinimized = 2,
+ kSerializedShowStateMaximized = 3,
+ kSerializedShowStateInactive = 4,
+ kSerializedShowStateFullscreen = 5,
+};
+
+// Converts a window show state to an integer. This function needs to be kept
+// up to date with the SerializedWindowShowState enum.
+int SerializeWindowShowState(ui::WindowShowState show_state) {
+ switch (show_state) {
+ case ui::SHOW_STATE_DEFAULT:
+ return kSerializedShowStateDefault;
+ case ui::SHOW_STATE_NORMAL:
+ return kSerializedShowStateNormal;
+ case ui::SHOW_STATE_MINIMIZED:
+ return kSerializedShowStateMinimized;
+ case ui::SHOW_STATE_MAXIMIZED:
+ return kSerializedShowStateMaximized;
+ case ui::SHOW_STATE_INACTIVE:
+ return kSerializedShowStateInactive;
+ case ui::SHOW_STATE_FULLSCREEN:
+ return kSerializedShowStateFullscreen;
+ case ui::SHOW_STATE_END:
+ // This should never happen.
+ NOTREACHED();
+ }
+ return kSerializedShowStateInvalid;
+}
+
+// Converts an integer to a window show state. Returns true on success, false
+// otherwise. This function needs to be kept up to date with the
+// SerializedWindowShowState enum.
+bool DeserializeWindowShowState(int show_state_int,
+ ui::WindowShowState* show_state) {
+ switch (static_cast<SerializedWindowShowState>(show_state_int)) {
+ case kSerializedShowStateDefault:
+ *show_state = ui::SHOW_STATE_DEFAULT;
+ return true;
+ case kSerializedShowStateNormal:
+ *show_state = ui::SHOW_STATE_NORMAL;
+ return true;
+ case kSerializedShowStateMinimized:
+ *show_state = ui::SHOW_STATE_MINIMIZED;
+ return true;
+ case kSerializedShowStateMaximized:
+ *show_state = ui::SHOW_STATE_MAXIMIZED;
+ return true;
+ case kSerializedShowStateInactive:
+ *show_state = ui::SHOW_STATE_INACTIVE;
+ return true;
+ case kSerializedShowStateFullscreen:
+ *show_state = ui::SHOW_STATE_FULLSCREEN;
+ return true;
+ case kSerializedShowStateInvalid:
+ default:
+ // Ignore unknown values. This could happen if the data is corrupt.
+ break;
+ }
+ return false;
+}
+
+// Superset of WindowPayloadObsolete/WindowPayloadObsolete2 and the other fields
+// that can appear in the Pickle version of a Window command. This is used as a
+// convenient destination for parsing the various fields in a WindowCommand.
+struct WindowCommandFields {
+ // Fields in WindowPayloadObsolete/WindowPayloadObsolete2/Pickle:
+ int window_id = 0;
+ int selected_tab_index = 0;
+ int num_tabs = 0;
+
+ // Fields in WindowPayloadObsolete2/Pickle:
+ int64_t timestamp = 0;
+
+ // Fields in Pickle:
+ // Completely zeroed position/dimensions indicates that defaults should be
+ // used.
+ int window_x = 0;
+ int window_y = 0;
+ int window_width = 0;
+ int window_height = 0;
+ int window_show_state = 0;
+ std::string workspace;
+};
+
+std::unique_ptr<sessions::TabRestoreService::Window>
+CreateWindowEntryFromCommand(const SessionCommand* command,
+ SessionID::id_type* window_id,
+ int32_t* num_tabs) {
+ WindowCommandFields fields;
+ ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
+
+ if (command->id() == kCommandWindow) {
+ std::unique_ptr<base::Pickle> pickle(command->PayloadAsPickle());
+ if (!pickle.get())
+ return nullptr;
+
+ base::PickleIterator it(*pickle);
+ WindowCommandFields parsed_fields;
+
+ // The first version of the pickle contains all of the following fields, so
+ // they should all successfully parse if the command is in fact a pickle.
+ if (!it.ReadInt(&parsed_fields.window_id) ||
+ !it.ReadInt(&parsed_fields.selected_tab_index) ||
+ !it.ReadInt(&parsed_fields.num_tabs) ||
+ !it.ReadInt64(&parsed_fields.timestamp) ||
+ !it.ReadInt(&parsed_fields.window_x) ||
+ !it.ReadInt(&parsed_fields.window_y) ||
+ !it.ReadInt(&parsed_fields.window_width) ||
+ !it.ReadInt(&parsed_fields.window_height) ||
+ !it.ReadInt(&parsed_fields.window_show_state) ||
+ !it.ReadString(&parsed_fields.workspace)) {
+ return nullptr;
+ }
+
+ // Validate the parameters. If the entire pickles parses but any of the
+ // validation fails assume corruption.
+ if (parsed_fields.window_width < 0 || parsed_fields.window_height < 0)
+ return nullptr;
+
+ // Deserialize the show state, validating it at the same time.
+ if (!DeserializeWindowShowState(parsed_fields.window_show_state,
+ &show_state)) {
+ return nullptr;
+ }
+
+ // New fields added to the pickle in later versions would be parsed and
+ // validated here.
+
+ // Copy the parsed data.
+ fields = parsed_fields;
+ } else if (command->id() == kCommandWindowDeprecated) {
+ // Old window commands can be in either of 2 formats. Try the newest first.
+ // These have distinct sizes so are easily distinguished.
+ bool parsed = false;
+
+ // Try to parse the command as a WindowPayloadObsolete2.
+ WindowPayloadObsolete2 payload2;
+ if (command->GetPayload(&payload2, sizeof(payload2))) {
+ fields.window_id = payload2.window_id;
+ fields.selected_tab_index = payload2.selected_tab_index;
+ fields.num_tabs = payload2.num_tabs;
+ fields.timestamp = payload2.timestamp;
+ parsed = true;
+ }
+
+ // Finally, try the oldest WindowPayloadObsolete type.
+ if (!parsed) {
+ WindowPayloadObsolete payload;
+ if (command->GetPayload(&payload, sizeof(payload))) {
+ fields.window_id = payload.window_id;
+ fields.selected_tab_index = payload.selected_tab_index;
+ fields.num_tabs = payload.num_tabs;
+ parsed = true;
+ }
+ }
+
+ // Fail if the old command wasn't able to be parsed in either of the
+ // deprecated formats.
+ if (!parsed)
+ return nullptr;
+ } else {
+ // This should never be called with anything other than a known window
+ // command ID.
+ NOTREACHED();
+ }
+
+ // Create the Window entry.
+ std::unique_ptr<sessions::TabRestoreService::Window> window =
+ base::MakeUnique<sessions::TabRestoreService::Window>();
+ window->selected_tab_index = fields.selected_tab_index;
+ window->timestamp = base::Time::FromInternalValue(fields.timestamp);
+ *window_id = static_cast<SessionID::id_type>(fields.window_id);
+ *num_tabs = fields.num_tabs;
+
+ // Set the bounds, show state and workspace if valid ones have been provided.
+ if (!(fields.window_x == 0 && fields.window_y == 0 &&
+ fields.window_width == 0 && fields.window_height == 0)) {
+ window->bounds.SetRect(fields.window_x, fields.window_y,
+ fields.window_width, fields.window_height);
+ // |show_state| was converted from window->show_state earlier during
+ // validation.
+ window->show_state = show_state;
+ window->workspace = std::move(fields.workspace);
+ }
+
+ return window;
+}
+
} // namespace
// PersistentTabRestoreService::Delegate ---------------------------------------
@@ -186,9 +384,12 @@ class PersistentTabRestoreService::Delegate
// Creates a window close command.
static std::unique_ptr<SessionCommand> CreateWindowCommand(
- SessionID::id_type id,
+ SessionID::id_type window_id,
int selected_tab_index,
int num_tabs,
+ const gfx::Rect& bounds,
+ ui::WindowShowState show_state,
+ const std::string& workspace,
base::Time timestamp);
// Creates a tab close command.
@@ -429,7 +630,8 @@ void PersistentTabRestoreService::Delegate::ScheduleCommandsForWindow(
base_session_service_->ScheduleCommand(CreateWindowCommand(
window.id, std::min(real_selected_tab, valid_tab_count - 1),
- valid_tab_count, window.timestamp));
+ valid_tab_count, window.bounds, window.show_state, window.workspace,
+ window.timestamp));
if (!window.app_name.empty()) {
base_session_service_->ScheduleCommand(CreateSetWindowAppNameCommand(
@@ -498,22 +700,38 @@ void PersistentTabRestoreService::Delegate::ScheduleCommandsForTab(
// static
std::unique_ptr<SessionCommand>
PersistentTabRestoreService::Delegate::CreateWindowCommand(
- SessionID::id_type id,
+ SessionID::id_type window_id,
int selected_tab_index,
int num_tabs,
+ const gfx::Rect& bounds,
+ ui::WindowShowState show_state,
+ const std::string& workspace,
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();
+ static_assert(sizeof(SessionID::id_type) == sizeof(int),
+ "SessionID::id_type has changed size.");
+
+ // Use a pickle to handle marshaling as this command contains variable-length
+ // content.
+ base::Pickle pickle;
+ pickle.WriteInt(static_cast<int>(window_id));
+ pickle.WriteInt(selected_tab_index);
+ pickle.WriteInt(num_tabs);
+ pickle.WriteInt64(timestamp.ToInternalValue());
+ pickle.WriteInt(bounds.x());
+ pickle.WriteInt(bounds.y());
+ pickle.WriteInt(bounds.width());
+ pickle.WriteInt(bounds.height());
+ pickle.WriteInt(SerializeWindowShowState(show_state));
+
+ // Enforce a maximum length on workspace names. A common size is 32 bytes for
+ // GUIDs.
+ if (workspace.size() <= 128)
+ pickle.WriteString(workspace);
+ else
+ pickle.WriteString(std::string());
std::unique_ptr<SessionCommand> command(
- new SessionCommand(kCommandWindow, sizeof(payload)));
- memcpy(command->contents(), &payload, sizeof(payload));
+ new SessionCommand(kCommandWindow, pickle));
return command;
}
@@ -618,43 +836,29 @@ void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
break;
}
+ case kCommandWindowDeprecated:
case kCommandWindow: {
- WindowPayload2 payload;
- if (pending_window_tabs > 0) {
- // Should never receive a window command while waiting for all the
- // tabs in a window.
+ // Should never receive a window command while waiting for all the
+ // tabs in a window.
+ if (pending_window_tabs > 0)
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.
+ // Try to parse the command, and silently skip if it fails.
+ int32_t num_tabs = 0;
+ SessionID::id_type window_id = 0;
+ std::unique_ptr<Window> window =
+ CreateWindowEntryFromCommand(&command, &window_id, &num_tabs);
+ if (!window)
return;
- }
- RemoveEntryByID(payload.window_id, &entries);
+ // Should always have at least 1 tab. Likely indicates corruption.
+ pending_window_tabs = num_tabs;
+ if (pending_window_tabs <= 0)
+ return;
- entries.push_back(base::MakeUnique<Window>());
- current_window = static_cast<Window*>(entries.back().get());
- current_window->selected_tab_index = payload.selected_tab_index;
- current_window->timestamp =
- base::Time::FromInternalValue(payload.timestamp);
+ RemoveEntryByID(window_id, &entries);
+ current_window = window.get();
+ entries.push_back(std::move(window));
break;
}
@@ -774,7 +978,6 @@ void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
// If there was corruption some of the entries won't be valid.
ValidateAndDeleteEmptyEntries(&entries);
-
loaded_entries->swap(entries);
}
@@ -827,6 +1030,9 @@ bool PersistentTabRestoreService::Delegate::ConvertSessionWindowToWindow(
std::min(session_window->selected_tab_index,
static_cast<int>(window->tabs.size() - 1));
window->timestamp = base::Time();
+ window->bounds = session_window->bounds;
+ window->show_state = session_window->show_state;
+ window->workspace = session_window->workspace;
return true;
}
« no previous file with comments | « components/sessions/core/live_tab_context.h ('k') | components/sessions/core/tab_restore_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698