summaryrefslogtreecommitdiff
path: root/chromium/components/sessions
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/components/sessions
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
downloadqtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/sessions')
-rw-r--r--chromium/components/sessions/BUILD.gn50
-rw-r--r--chromium/components/sessions/DEPS1
-rw-r--r--chromium/components/sessions/content/DEPS3
-rw-r--r--chromium/components/sessions/content/content_live_tab.cc3
-rw-r--r--chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc44
-rw-r--r--chromium/components/sessions/content/session_tab_helper.cc130
-rw-r--r--chromium/components/sessions/content/session_tab_helper.h91
-rw-r--r--chromium/components/sessions/content/session_tab_helper_delegate.h56
-rw-r--r--chromium/components/sessions/core/DEPS1
-rw-r--r--chromium/components/sessions/core/base_session_service.cc178
-rw-r--r--chromium/components/sessions/core/base_session_service.h139
-rw-r--r--chromium/components/sessions/core/base_session_service_commands.cc2
-rw-r--r--chromium/components/sessions/core/base_session_service_delegate.h29
-rw-r--r--chromium/components/sessions/core/base_session_service_test_helper.cc37
-rw-r--r--chromium/components/sessions/core/command_storage_backend.cc473
-rw-r--r--chromium/components/sessions/core/command_storage_backend.h159
-rw-r--r--chromium/components/sessions/core/command_storage_backend_unittest.cc290
-rw-r--r--chromium/components/sessions/core/command_storage_manager.cc212
-rw-r--r--chromium/components/sessions/core/command_storage_manager.h164
-rw-r--r--chromium/components/sessions/core/command_storage_manager_delegate.h37
-rw-r--r--chromium/components/sessions/core/command_storage_manager_test_helper.cc55
-rw-r--r--chromium/components/sessions/core/command_storage_manager_test_helper.h (renamed from chromium/components/sessions/core/base_session_service_test_helper.h)26
-rw-r--r--chromium/components/sessions/core/live_tab_context.h30
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry.cc2
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc49
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry_test_helper.h19
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry_unittest.cc6
-rw-r--r--chromium/components/sessions/core/session_backend.cc385
-rw-r--r--chromium/components/sessions/core/session_backend.h149
-rw-r--r--chromium/components/sessions/core/session_command.cc8
-rw-r--r--chromium/components/sessions/core/session_command.h4
-rw-r--r--chromium/components/sessions/core/session_constants.cc10
-rw-r--r--chromium/components/sessions/core/session_constants.h13
-rw-r--r--chromium/components/sessions/core/session_service_commands.cc129
-rw-r--r--chromium/components/sessions/core/session_service_commands.h21
-rw-r--r--chromium/components/sessions/core/session_types.cc5
-rw-r--r--chromium/components/sessions/core/session_types.h26
-rw-r--r--chromium/components/sessions/core/snapshotting_command_storage_backend.cc90
-rw-r--r--chromium/components/sessions/core/snapshotting_command_storage_backend.h78
-rw-r--r--chromium/components/sessions/core/snapshotting_command_storage_manager.cc70
-rw-r--r--chromium/components/sessions/core/snapshotting_command_storage_manager.h63
-rw-r--r--chromium/components/sessions/core/snapshotting_session_backend_unittest.cc (renamed from chromium/components/sessions/core/session_backend_unittest.cc)124
-rw-r--r--chromium/components/sessions/core/tab_restore_service.h8
-rw-r--r--chromium/components/sessions/core/tab_restore_service_client.h6
-rw-r--r--chromium/components/sessions/core/tab_restore_service_helper.cc25
-rw-r--r--chromium/components/sessions/core/tab_restore_service_impl.cc109
-rw-r--r--chromium/components/sessions/ios/ios_live_tab.h45
-rw-r--r--chromium/components/sessions/ios/ios_live_tab.mm53
-rw-r--r--chromium/components/sessions/ios/ios_restore_live_tab.h5
-rw-r--r--chromium/components/sessions/ios/ios_restore_live_tab.mm8
-rw-r--r--chromium/components/sessions/ios/ios_serialized_navigation_builder.mm4
-rw-r--r--chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm12
-rw-r--r--chromium/components/sessions/ios/ios_serialized_navigation_driver_unittest.cc14
-rw-r--r--chromium/components/sessions/ios/ios_webstate_live_tab.h61
-rw-r--r--chromium/components/sessions/ios/ios_webstate_live_tab.mm73
55 files changed, 2582 insertions, 1302 deletions
diff --git a/chromium/components/sessions/BUILD.gn b/chromium/components/sessions/BUILD.gn
index 9f787b6a27f..429232cde57 100644
--- a/chromium/components/sessions/BUILD.gn
+++ b/chromium/components/sessions/BUILD.gn
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//extensions/buildflags/buildflags.gni")
+
if (is_android) {
import("//build/config/android/config.gni")
}
@@ -26,6 +28,9 @@ if (!is_ios) {
"content/extended_info_handler.h",
"content/navigation_task_id.cc",
"content/navigation_task_id.h",
+ "content/session_tab_helper.cc",
+ "content/session_tab_helper.h",
+ "content/session_tab_helper_delegate.h",
]
configs += [ ":implementation" ]
@@ -39,12 +44,18 @@ if (!is_ios) {
"//base",
"//base/third_party/dynamic_annotations",
"//content/public/common",
+ "//extensions/buildflags",
"//ui/base",
"//url",
]
+
+ if (enable_extensions) {
+ deps += [ "//extensions/common" ]
+ }
}
} else {
source_set("sessions") {
+ configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"ios/ios_live_tab.h",
"ios/ios_live_tab.mm",
@@ -54,6 +65,8 @@ if (!is_ios) {
"ios/ios_serialized_navigation_builder.mm",
"ios/ios_serialized_navigation_driver.cc",
"ios/ios_serialized_navigation_driver.h",
+ "ios/ios_webstate_live_tab.h",
+ "ios/ios_webstate_live_tab.mm",
]
public_deps = [
@@ -74,19 +87,19 @@ source_set("shared") {
visibility = [ ":*" ]
sources = [
- "core/base_session_service.cc",
- "core/base_session_service.h",
"core/base_session_service_commands.cc",
"core/base_session_service_commands.h",
- "core/base_session_service_delegate.h",
+ "core/command_storage_backend.cc",
+ "core/command_storage_backend.h",
+ "core/command_storage_manager.cc",
+ "core/command_storage_manager.h",
+ "core/command_storage_manager_delegate.h",
"core/live_tab.cc",
"core/live_tab.h",
"core/live_tab_context.h",
"core/serialized_navigation_driver.h",
"core/serialized_navigation_entry.cc",
"core/serialized_navigation_entry.h",
- "core/session_backend.cc",
- "core/session_backend.h",
"core/session_command.cc",
"core/session_command.h",
"core/session_constants.cc",
@@ -99,6 +112,10 @@ source_set("shared") {
"core/session_service_commands.h",
"core/session_types.cc",
"core/session_types.h",
+ "core/snapshotting_command_storage_backend.cc",
+ "core/snapshotting_command_storage_backend.h",
+ "core/snapshotting_command_storage_manager.cc",
+ "core/snapshotting_command_storage_manager.h",
"core/tab_restore_service.cc",
"core/tab_restore_service.h",
"core/tab_restore_service_client.cc",
@@ -122,7 +139,9 @@ source_set("shared") {
"//components/history/core/common",
"//components/keyed_service/core",
"//components/prefs",
+ "//components/tab_groups",
"//components/variations",
+ "//crypto",
"//skia",
"//ui/base",
"//ui/gfx",
@@ -137,9 +156,7 @@ static_library("test_support") {
"core/serialized_navigation_entry_test_helper.h",
]
- public_deps = [
- ":sessions",
- ]
+ public_deps = [ ":sessions" ]
deps = [
"//base",
"//skia",
@@ -148,19 +165,15 @@ static_library("test_support") {
"//url",
]
- if (!is_android && !is_ios) {
- sources += [
- "core/base_session_service_test_helper.cc",
- "core/base_session_service_test_helper.h",
- ]
- }
-
if (!is_ios) {
sources += [
"content/content_test_helper.cc",
"content/content_test_helper.h",
+ "core/command_storage_manager_test_helper.cc",
+ "core/command_storage_manager_test_helper.h",
]
deps += [
+ "//base/test:test_support",
"//content/public/browser",
"//content/public/common",
]
@@ -173,9 +186,10 @@ source_set("unit_tests") {
}
testonly = true
sources = [
+ "core/command_storage_backend_unittest.cc",
"core/serialized_navigation_entry_unittest.cc",
- "core/session_backend_unittest.cc",
"core/session_id_generator_unittest.cc",
+ "core/snapshotting_session_backend_unittest.cc",
"ios/ios_serialized_navigation_builder_unittest.mm",
"ios/ios_serialized_navigation_driver_unittest.cc",
]
@@ -188,9 +202,7 @@ source_set("unit_tests") {
]
}
- public_deps = [
- ":sessions",
- ]
+ public_deps = [ ":sessions" ]
deps = [
":test_support",
diff --git a/chromium/components/sessions/DEPS b/chromium/components/sessions/DEPS
index 2d083923531..2c9ec090d3f 100644
--- a/chromium/components/sessions/DEPS
+++ b/chromium/components/sessions/DEPS
@@ -1,6 +1,7 @@
include_rules = [
"+components/history/core/common",
"+components/variations",
+ "+crypto",
"+ui/base",
"+ui/gfx",
]
diff --git a/chromium/components/sessions/content/DEPS b/chromium/components/sessions/content/DEPS
index 97c7fc8542e..ce5cab76a69 100644
--- a/chromium/components/sessions/content/DEPS
+++ b/chromium/components/sessions/content/DEPS
@@ -2,6 +2,9 @@ include_rules = [
"+content/public/browser",
"+content/public/common",
"+content/public/test",
+ "+extensions/buildflags",
+ "+extensions/common",
"+third_party/blink/public/platform",
+ "+third_party/blink/public/common/user_agent/user_agent_metadata.h",
"+services/network/public/mojom/referrer_policy.mojom.h",
]
diff --git a/chromium/components/sessions/content/content_live_tab.cc b/chromium/components/sessions/content/content_live_tab.cc
index 07f3d1f5f4f..26c42aa1dcd 100644
--- a/chromium/components/sessions/content/content_live_tab.cc
+++ b/chromium/components/sessions/content/content_live_tab.cc
@@ -8,6 +8,7 @@
#include "base/memory/ptr_util.h"
#include "components/sessions/content/content_platform_specific_tab_data.h"
+#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
namespace {
const char kContentLiveTabWebContentsUserDataKey[] = "content_live_tab";
@@ -65,7 +66,7 @@ ContentLiveTab::GetPlatformSpecificTabData() {
}
const std::string& ContentLiveTab::GetUserAgentOverride() {
- return web_contents()->GetUserAgentOverride();
+ return web_contents()->GetUserAgentOverride().ua_string_override;
}
} // namespace sessions
diff --git a/chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc b/chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc
index 398eea3aefe..a8d2d284b25 100644
--- a/chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc
+++ b/chromium/components/sessions/content/content_serialized_navigation_builder_unittest.cc
@@ -68,26 +68,26 @@ std::unique_ptr<content::NavigationEntry> MakeNavigationEntryForTest() {
std::unique_ptr<content::NavigationEntry> navigation_entry(
content::NavigationEntry::Create());
navigation_entry->SetReferrer(content::Referrer(
- test_data::kReferrerURL,
+ test_data::ReferrerUrl(),
static_cast<network::mojom::ReferrerPolicy>(test_data::kReferrerPolicy)));
- navigation_entry->SetURL(test_data::kURL);
- navigation_entry->SetVirtualURL(test_data::kVirtualURL);
+ navigation_entry->SetURL(test_data::Url());
+ navigation_entry->SetVirtualURL(test_data::VirtualUrl());
navigation_entry->SetTitle(test_data::kTitle);
navigation_entry->SetTransitionType(test_data::kTransitionType);
navigation_entry->SetHasPostData(test_data::kHasPostData);
navigation_entry->SetPostID(test_data::kPostID);
- navigation_entry->SetOriginalRequestURL(test_data::kOriginalRequestURL);
+ navigation_entry->SetOriginalRequestURL(test_data::OriginalRequestUrl());
navigation_entry->SetIsOverridingUserAgent(test_data::kIsOverridingUserAgent);
navigation_entry->SetTimestamp(test_data::kTimestamp);
SetPasswordStateInNavigation(test_data::kPasswordState,
navigation_entry.get());
navigation_entry->GetFavicon().valid = true;
- navigation_entry->GetFavicon().url = test_data::kFaviconURL;
+ navigation_entry->GetFavicon().url = test_data::FaviconUrl();
navigation_entry->SetHttpStatusCode(test_data::kHttpStatusCode);
std::vector<GURL> redirect_chain;
- redirect_chain.push_back(test_data::kRedirectURL0);
- redirect_chain.push_back(test_data::kRedirectURL1);
- redirect_chain.push_back(test_data::kVirtualURL);
+ redirect_chain.push_back(test_data::RedirectUrl0());
+ redirect_chain.push_back(test_data::RedirectUrl1());
+ redirect_chain.push_back(test_data::VirtualUrl());
navigation_entry->SetRedirectChain(redirect_chain);
NavigationTaskId::Get(navigation_entry.get())->set_id(test_data::kTaskId);
NavigationTaskId::Get(navigation_entry.get())
@@ -148,9 +148,9 @@ TEST_F(ContentSerializedNavigationBuilderTest, FromNavigationEntry) {
EXPECT_EQ(test_data::kIndex, navigation.index());
EXPECT_EQ(navigation_entry->GetUniqueID(), navigation.unique_id());
- EXPECT_EQ(test_data::kReferrerURL, navigation.referrer_url());
+ EXPECT_EQ(test_data::ReferrerUrl(), navigation.referrer_url());
EXPECT_EQ(test_data::kReferrerPolicy, navigation.referrer_policy());
- EXPECT_EQ(test_data::kVirtualURL, navigation.virtual_url());
+ EXPECT_EQ(test_data::VirtualUrl(), navigation.virtual_url());
EXPECT_EQ(test_data::kTitle, navigation.title());
EXPECT_EQ(navigation_entry->GetPageState().ToEncodedData(),
navigation.encoded_page_state());
@@ -158,16 +158,16 @@ TEST_F(ContentSerializedNavigationBuilderTest, FromNavigationEntry) {
navigation.transition_type(), test_data::kTransitionType));
EXPECT_EQ(test_data::kHasPostData, navigation.has_post_data());
EXPECT_EQ(test_data::kPostID, navigation.post_id());
- EXPECT_EQ(test_data::kOriginalRequestURL, navigation.original_request_url());
+ EXPECT_EQ(test_data::OriginalRequestUrl(), navigation.original_request_url());
EXPECT_EQ(test_data::kIsOverridingUserAgent,
navigation.is_overriding_user_agent());
EXPECT_EQ(test_data::kTimestamp, navigation.timestamp());
- EXPECT_EQ(test_data::kFaviconURL, navigation.favicon_url());
+ EXPECT_EQ(test_data::FaviconUrl(), navigation.favicon_url());
EXPECT_EQ(test_data::kHttpStatusCode, navigation.http_status_code());
ASSERT_EQ(3U, navigation.redirect_chain().size());
- EXPECT_EQ(test_data::kRedirectURL0, navigation.redirect_chain()[0]);
- EXPECT_EQ(test_data::kRedirectURL1, navigation.redirect_chain()[1]);
- EXPECT_EQ(test_data::kVirtualURL, navigation.redirect_chain()[2]);
+ EXPECT_EQ(test_data::RedirectUrl0(), navigation.redirect_chain()[0]);
+ EXPECT_EQ(test_data::RedirectUrl1(), navigation.redirect_chain()[1]);
+ EXPECT_EQ(test_data::VirtualUrl(), navigation.redirect_chain()[2]);
EXPECT_EQ(test_data::kPasswordState, navigation.password_state());
ASSERT_EQ(2U, navigation.extended_info_map().size());
@@ -224,11 +224,11 @@ TEST_F(ContentSerializedNavigationBuilderTest, ToNavigationEntry) {
ContentSerializedNavigationBuilder::ToNavigationEntry(&navigation,
&browser_context));
- EXPECT_EQ(test_data::kReferrerURL, new_navigation_entry->GetReferrer().url);
+ EXPECT_EQ(test_data::ReferrerUrl(), new_navigation_entry->GetReferrer().url);
EXPECT_EQ(test_data::kReferrerPolicy,
static_cast<int>(new_navigation_entry->GetReferrer().policy));
- EXPECT_EQ(test_data::kURL, new_navigation_entry->GetURL());
- EXPECT_EQ(test_data::kVirtualURL, new_navigation_entry->GetVirtualURL());
+ EXPECT_EQ(test_data::Url(), new_navigation_entry->GetURL());
+ EXPECT_EQ(test_data::VirtualUrl(), new_navigation_entry->GetVirtualURL());
EXPECT_EQ(test_data::kTitle, new_navigation_entry->GetTitle());
EXPECT_EQ(old_navigation_entry->GetPageState().ToEncodedData(),
new_navigation_entry->GetPageState().ToEncodedData());
@@ -236,18 +236,18 @@ TEST_F(ContentSerializedNavigationBuilderTest, ToNavigationEntry) {
new_navigation_entry->GetTransitionType(), ui::PAGE_TRANSITION_RELOAD));
EXPECT_EQ(test_data::kHasPostData, new_navigation_entry->GetHasPostData());
EXPECT_EQ(test_data::kPostID, new_navigation_entry->GetPostID());
- EXPECT_EQ(test_data::kOriginalRequestURL,
+ EXPECT_EQ(test_data::OriginalRequestUrl(),
new_navigation_entry->GetOriginalRequestURL());
EXPECT_EQ(test_data::kIsOverridingUserAgent,
new_navigation_entry->GetIsOverridingUserAgent());
EXPECT_EQ(test_data::kHttpStatusCode,
new_navigation_entry->GetHttpStatusCode());
ASSERT_EQ(3U, new_navigation_entry->GetRedirectChain().size());
- EXPECT_EQ(test_data::kRedirectURL0,
+ EXPECT_EQ(test_data::RedirectUrl0(),
new_navigation_entry->GetRedirectChain()[0]);
- EXPECT_EQ(test_data::kRedirectURL1,
+ EXPECT_EQ(test_data::RedirectUrl1(),
new_navigation_entry->GetRedirectChain()[1]);
- EXPECT_EQ(test_data::kVirtualURL,
+ EXPECT_EQ(test_data::VirtualUrl(),
new_navigation_entry->GetRedirectChain()[2]);
sessions::NavigationTaskId* new_navigation_task_id =
sessions::NavigationTaskId::Get(new_navigation_entry.get());
diff --git a/chromium/components/sessions/content/session_tab_helper.cc b/chromium/components/sessions/content/session_tab_helper.cc
new file mode 100644
index 00000000000..f5cf214812f
--- /dev/null
+++ b/chromium/components/sessions/content/session_tab_helper.cc
@@ -0,0 +1,130 @@
+// 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 "components/sessions/content/session_tab_helper.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/sessions/content/content_serialized_navigation_builder.h"
+#include "components/sessions/content/session_tab_helper_delegate.h"
+#include "components/sessions/core/serialized_navigation_entry.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/buildflags/buildflags.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/common/extension_messages.h"
+#endif
+
+namespace sessions {
+
+SessionTabHelper::SessionTabHelper(content::WebContents* contents,
+ DelegateLookup lookup)
+ : content::WebContentsObserver(contents),
+ delegate_lookup_(std::move(lookup)),
+ session_id_(SessionID::NewUnique()),
+ window_id_(SessionID::InvalidValue()) {}
+
+SessionTabHelper::~SessionTabHelper() = default;
+
+void SessionTabHelper::CreateForWebContents(content::WebContents* contents,
+ DelegateLookup lookup) {
+ DCHECK(contents);
+ if (!FromWebContents(contents)) {
+ contents->SetUserData(UserDataKey(), base::WrapUnique(new SessionTabHelper(
+ contents, std::move(lookup))));
+ }
+}
+
+void SessionTabHelper::SetWindowID(const SessionID& id) {
+ window_id_ = id;
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ // Extension code in the renderer holds the ID of the window that hosts it.
+ // Notify it that the window ID changed.
+ web_contents()->SendToAllFrames(
+ new ExtensionMsg_UpdateBrowserWindowId(MSG_ROUTING_NONE, id.id()));
+#endif
+}
+
+// static
+SessionID SessionTabHelper::IdForTab(const content::WebContents* tab) {
+ const SessionTabHelper* session_tab_helper =
+ tab ? SessionTabHelper::FromWebContents(tab) : nullptr;
+ return session_tab_helper ? session_tab_helper->session_id()
+ : SessionID::InvalidValue();
+}
+
+// static
+SessionID SessionTabHelper::IdForWindowContainingTab(
+ const content::WebContents* tab) {
+ const SessionTabHelper* session_tab_helper =
+ tab ? SessionTabHelper::FromWebContents(tab) : nullptr;
+ return session_tab_helper ? session_tab_helper->window_id()
+ : SessionID::InvalidValue();
+}
+
+void SessionTabHelper::UserAgentOverrideSet(
+ const blink::UserAgentOverride& ua_override) {
+ // TODO(https://crbug.com/1061917): handle |ua_override.ua_metadata_override|.
+ SessionTabHelperDelegate* delegate = GetDelegate();
+ if (delegate) {
+ delegate->SetTabUserAgentOverride(window_id(), session_id(),
+ ua_override.ua_string_override);
+ }
+}
+
+void SessionTabHelper::NavigationEntryCommitted(
+ const content::LoadCommittedDetails& load_details) {
+ SessionTabHelperDelegate* delegate = GetDelegate();
+ if (!delegate)
+ return;
+
+ int current_entry_index =
+ web_contents()->GetController().GetCurrentEntryIndex();
+ delegate->SetSelectedNavigationIndex(window_id(), session_id(),
+ current_entry_index);
+ const SerializedNavigationEntry navigation =
+ ContentSerializedNavigationBuilder::FromNavigationEntry(
+ current_entry_index,
+ web_contents()->GetController().GetEntryAtIndex(current_entry_index));
+ delegate->UpdateTabNavigation(window_id(), session_id(), navigation);
+}
+
+void SessionTabHelper::NavigationListPruned(
+ const content::PrunedDetails& pruned_details) {
+ SessionTabHelperDelegate* delegate = GetDelegate();
+ if (!delegate)
+ return;
+
+ delegate->TabNavigationPathPruned(window_id(), session_id(),
+ pruned_details.index, pruned_details.count);
+}
+
+void SessionTabHelper::NavigationEntriesDeleted() {
+ SessionTabHelperDelegate* delegate = GetDelegate();
+ if (!delegate)
+ return;
+
+ delegate->TabNavigationPathEntriesDeleted(window_id(), session_id());
+}
+
+void SessionTabHelper::NavigationEntryChanged(
+ const content::EntryChangedDetails& change_details) {
+ SessionTabHelperDelegate* delegate = GetDelegate();
+ if (!delegate)
+ return;
+
+ const SerializedNavigationEntry navigation =
+ ContentSerializedNavigationBuilder::FromNavigationEntry(
+ change_details.index, change_details.changed_entry);
+ delegate->UpdateTabNavigation(window_id(), session_id(), navigation);
+}
+
+SessionTabHelperDelegate* SessionTabHelper::GetDelegate() {
+ return delegate_lookup_ ? delegate_lookup_.Run(web_contents()) : nullptr;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(SessionTabHelper)
+
+} // namespace sessions
diff --git a/chromium/components/sessions/content/session_tab_helper.h b/chromium/components/sessions/content/session_tab_helper.h
new file mode 100644
index 00000000000..ddba15a693a
--- /dev/null
+++ b/chromium/components/sessions/content/session_tab_helper.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 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.
+
+#ifndef COMPONENTS_SESSIONS_CONTENT_SESSION_TAB_HELPER_H_
+#define COMPONENTS_SESSIONS_CONTENT_SESSION_TAB_HELPER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/sessions/core/session_id.h"
+#include "components/sessions/core/sessions_export.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace sessions {
+class SessionTabHelperDelegate;
+
+// This class keeps the extension API's windowID up to date with the current
+// window of the tab and observes navigation events.
+class SESSIONS_EXPORT SessionTabHelper
+ : public content::WebContentsObserver,
+ public content::WebContentsUserData<SessionTabHelper> {
+ public:
+ using DelegateLookup =
+ base::RepeatingCallback<SessionTabHelperDelegate*(content::WebContents*)>;
+
+ ~SessionTabHelper() override;
+
+ static void CreateForWebContents(content::WebContents* contents,
+ DelegateLookup lookup);
+
+ // Returns the identifier used by session restore for this tab.
+ const SessionID& session_id() const { return session_id_; }
+
+ // Identifier of the window the tab is in.
+ void SetWindowID(const SessionID& id);
+ const SessionID& window_id() const { return window_id_; }
+
+ // If the specified WebContents has a SessionTabHelper (probably because it
+ // was used as the contents of a tab), returns a tab id. This value is
+ // immutable for a given tab. It will be unique across Chrome within the
+ // current session, but may be re-used across sessions. Returns
+ // SessionID::InvalidValue() for a null WebContents or if the WebContents has
+ // no SessionTabHelper.
+ static SessionID IdForTab(const content::WebContents* tab);
+
+ // If the specified WebContents has a SessionTabHelper (probably because it
+ // was used as the contents of a tab), and has ever been attached to a Browser
+ // window, returns Browser::session_id().id() for that Browser. If the tab is
+ // being dragged between Browser windows, returns the old window's id value.
+ // If the WebContents has a SessionTabHelper but has never been attached to a
+ // Browser window, returns an id value that is different from that of any
+ // Browser. Returns SessionID::InvalidValue() for a null WebContents or if the
+ // WebContents has no SessionTabHelper.
+ static SessionID IdForWindowContainingTab(const content::WebContents* tab);
+
+ // content::WebContentsObserver:
+ void UserAgentOverrideSet(
+ const blink::UserAgentOverride& ua_override) override;
+ void NavigationEntryCommitted(
+ const content::LoadCommittedDetails& load_details) override;
+ void NavigationListPruned(
+ const content::PrunedDetails& pruned_details) override;
+ void NavigationEntriesDeleted() override;
+ void NavigationEntryChanged(
+ const content::EntryChangedDetails& change_details) override;
+
+ private:
+ friend class content::WebContentsUserData<SessionTabHelper>;
+ SessionTabHelper(content::WebContents* contents, DelegateLookup lookup);
+
+ sessions::SessionTabHelperDelegate* GetDelegate();
+
+ DelegateLookup delegate_lookup_;
+
+ // Unique identifier of the tab for session restore. This id is only unique
+ // within the current session, and is not guaranteed to be unique across
+ // sessions.
+ const SessionID session_id_;
+
+ // Unique identifier of the window the tab is in.
+ SessionID window_id_;
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+ DISALLOW_COPY_AND_ASSIGN(SessionTabHelper);
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_CONTENT_SESSION_TAB_HELPER_H_
diff --git a/chromium/components/sessions/content/session_tab_helper_delegate.h b/chromium/components/sessions/content/session_tab_helper_delegate.h
new file mode 100644
index 00000000000..e3453e4fc7c
--- /dev/null
+++ b/chromium/components/sessions/content/session_tab_helper_delegate.h
@@ -0,0 +1,56 @@
+// Copyright 2020 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.
+
+#ifndef COMPONENTS_SESSIONS_CONTENT_SESSION_TAB_HELPER_DELEGATE_H_
+#define COMPONENTS_SESSIONS_CONTENT_SESSION_TAB_HELPER_DELEGATE_H_
+
+#include <string>
+
+#include "components/sessions/core/sessions_export.h"
+
+class SessionID;
+
+namespace sessions {
+
+class SerializedNavigationEntry;
+
+// Defines the interface used by SessionTabHelper to record changes to
+// navigation entries so that they can restored at a later date.
+class SESSIONS_EXPORT SessionTabHelperDelegate {
+ public:
+ // Sets the user agent override of the specified tab.
+ virtual void SetTabUserAgentOverride(
+ const SessionID& window_id,
+ const SessionID& tab_id,
+ const std::string& user_agent_override) = 0;
+
+ // Sets the index of the selected entry in the navigation controller for the
+ // specified tab.
+ virtual void SetSelectedNavigationIndex(const SessionID& window_id,
+ const SessionID& tab_id,
+ int index) = 0;
+
+ // Updates the navigation entry for the specified tab.
+ virtual void UpdateTabNavigation(
+ const SessionID& window_id,
+ const SessionID& tab_id,
+ const SerializedNavigationEntry& navigation) = 0;
+
+ // Invoked when the NavigationController has removed entries from the list.
+ // |index| gives the the starting index from which entries were deleted.
+ // |count| gives the number of entries that were removed.
+ virtual void TabNavigationPathPruned(const SessionID& window_id,
+ const SessionID& tab_id,
+ int index,
+ int count) = 0;
+
+ // Invoked when the NavigationController has deleted entries because of a
+ // history deletion.
+ virtual void TabNavigationPathEntriesDeleted(const SessionID& window_id,
+ const SessionID& tab_id) = 0;
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_CONTENT_SESSION_TAB_HELPER_DELEGATE_H_
diff --git a/chromium/components/sessions/core/DEPS b/chromium/components/sessions/core/DEPS
index 6c0a29aa7f7..256c8189af9 100644
--- a/chromium/components/sessions/core/DEPS
+++ b/chromium/components/sessions/core/DEPS
@@ -1,6 +1,7 @@
include_rules = [
"+components/keyed_service/core",
"+components/prefs",
+ "+components/tab_groups",
# SkColor is referenced in a struct in session_types.h
"+third_party/skia/include/core/SkColor.h",
diff --git a/chromium/components/sessions/core/base_session_service.cc b/chromium/components/sessions/core/base_session_service.cc
deleted file mode 100644
index bbf50c6fd71..00000000000
--- a/chromium/components/sessions/core/base_session_service.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 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 "components/sessions/core/base_session_service.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/sequenced_task_runner.h"
-#include "base/task/post_task.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/sessions/core/base_session_service_delegate.h"
-#include "components/sessions/core/session_backend.h"
-
-// BaseSessionService ---------------------------------------------------------
-
-namespace sessions {
-namespace {
-
-// Helper used by ScheduleGetLastSessionCommands. It runs callback on TaskRunner
-// thread if it's not canceled.
-void RunIfNotCanceled(
- const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
- const BaseSessionService::GetCommandsCallback& callback,
- std::vector<std::unique_ptr<SessionCommand>> commands) {
- if (is_canceled.Run())
- return;
- callback.Run(std::move(commands));
-}
-
-void PostOrRunInternalGetCommandsCallback(
- base::TaskRunner* task_runner,
- const BaseSessionService::GetCommandsCallback& callback,
- std::vector<std::unique_ptr<SessionCommand>> commands) {
- if (task_runner->RunsTasksInCurrentSequence()) {
- callback.Run(std::move(commands));
- } else {
- task_runner->PostTask(FROM_HERE,
- base::BindOnce(callback, std::move(commands)));
- }
-}
-
-} // namespace
-
-// Delay between when a command is received, and when we save it to the
-// backend.
-static const int kSaveDelayMS = 2500;
-
-BaseSessionService::BaseSessionService(SessionType type,
- const base::FilePath& path,
- BaseSessionServiceDelegate* delegate)
- : pending_reset_(false),
- commands_since_reset_(0),
- delegate_(delegate),
- backend_task_runner_(base::CreateSequencedTaskRunner(
- {base::ThreadPool(), base::MayBlock(),
- base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {
- backend_ = new SessionBackend(type, path);
- DCHECK(backend_);
-}
-
-BaseSessionService::~BaseSessionService() {}
-
-void BaseSessionService::MoveCurrentSessionToLastSession() {
- Save();
- RunTaskOnBackendThread(
- FROM_HERE,
- base::BindOnce(&SessionBackend::MoveCurrentSessionToLastSession,
- backend_));
-}
-
-void BaseSessionService::DeleteLastSession() {
- RunTaskOnBackendThread(
- FROM_HERE, base::BindOnce(&SessionBackend::DeleteLastSession, backend_));
-}
-
-void BaseSessionService::ScheduleCommand(
- std::unique_ptr<SessionCommand> command) {
- DCHECK(command);
- commands_since_reset_++;
- pending_commands_.push_back(std::move(command));
- StartSaveTimer();
-}
-
-void BaseSessionService::AppendRebuildCommand(
- std::unique_ptr<SessionCommand> command) {
- DCHECK(command);
- pending_commands_.push_back(std::move(command));
-}
-
-void BaseSessionService::EraseCommand(SessionCommand* old_command) {
- auto it = std::find_if(
- pending_commands_.begin(), pending_commands_.end(),
- [old_command](const std::unique_ptr<SessionCommand>& command_ptr) {
- return command_ptr.get() == old_command;
- });
- CHECK(it != pending_commands_.end());
- pending_commands_.erase(it);
-}
-
-void BaseSessionService::SwapCommand(
- SessionCommand* old_command,
- std::unique_ptr<SessionCommand> new_command) {
- auto it = std::find_if(
- pending_commands_.begin(), pending_commands_.end(),
- [old_command](const std::unique_ptr<SessionCommand>& command_ptr) {
- return command_ptr.get() == old_command;
- });
- CHECK(it != pending_commands_.end());
- *it = std::move(new_command);
-}
-
-void BaseSessionService::ClearPendingCommands() {
- pending_commands_.clear();
-}
-
-void BaseSessionService::StartSaveTimer() {
- // Don't start a timer when testing.
- if (delegate_->ShouldUseDelayedSave() &&
- base::ThreadTaskRunnerHandle::IsSet() && !weak_factory_.HasWeakPtrs()) {
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&BaseSessionService::Save, weak_factory_.GetWeakPtr()),
- base::TimeDelta::FromMilliseconds(kSaveDelayMS));
- }
-}
-
-void BaseSessionService::Save() {
- // Inform the delegate that we will save the commands now, giving it the
- // opportunity to append more commands.
- delegate_->OnWillSaveCommands();
-
- if (pending_commands_.empty())
- return;
-
- // We create a new vector which will receive all elements from the
- // current commands. This will also clear the current list.
- RunTaskOnBackendThread(
- FROM_HERE, base::BindOnce(&SessionBackend::AppendCommands, backend_,
- std::move(pending_commands_), pending_reset_));
-
- if (pending_reset_) {
- commands_since_reset_ = 0;
- pending_reset_ = false;
- }
-}
-
-base::CancelableTaskTracker::TaskId
-BaseSessionService::ScheduleGetLastSessionCommands(
- const GetCommandsCallback& callback,
- base::CancelableTaskTracker* tracker) {
- base::CancelableTaskTracker::IsCanceledCallback is_canceled;
- base::CancelableTaskTracker::TaskId id =
- tracker->NewTrackedTaskId(&is_canceled);
-
- GetCommandsCallback run_if_not_canceled =
- base::Bind(&RunIfNotCanceled, is_canceled, callback);
-
- GetCommandsCallback callback_runner =
- base::Bind(&PostOrRunInternalGetCommandsCallback,
- base::RetainedRef(base::ThreadTaskRunnerHandle::Get()),
- run_if_not_canceled);
-
- RunTaskOnBackendThread(
- FROM_HERE, base::BindOnce(&SessionBackend::ReadLastSessionCommands,
- backend_, is_canceled, callback_runner));
- return id;
-}
-
-void BaseSessionService::RunTaskOnBackendThread(const base::Location& from_here,
- base::OnceClosure task) {
- backend_task_runner_->PostNonNestableTask(from_here, std::move(task));
-}
-
-} // namespace sessions
diff --git a/chromium/components/sessions/core/base_session_service.h b/chromium/components/sessions/core/base_session_service.h
deleted file mode 100644
index 9e63212036e..00000000000
--- a/chromium/components/sessions/core/base_session_service.h
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 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.
-
-#ifndef COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_H_
-#define COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "components/sessions/core/sessions_export.h"
-
-namespace base {
-class SequencedTaskRunner;
-}
-
-namespace sessions {
-class BaseSessionServiceDelegate;
-class SessionCommand;
-class SessionBackend;
-
-// BaseSessionService is the super class of both tab restore service and
-// session service. It contains commonality needed by both, in particular
-// it manages a set of SessionCommands that are periodically sent to a
-// SessionBackend.
-class SESSIONS_EXPORT BaseSessionService {
- public:
- // Identifies the type of session service this is. This is used by the
- // backend to determine the name of the files.
- enum SessionType {
- SESSION_RESTORE,
- TAB_RESTORE
- };
-
- typedef base::Callback<void(std::vector<std::unique_ptr<SessionCommand>>)>
- GetCommandsCallback;
-
- // Creates a new BaseSessionService. After creation you need to invoke
- // Init. |delegate| will remain owned by the creator and it is guaranteed
- // that its lifetime surpasses this class.
- // |type| gives the type of session service, |path| the path to save files to.
- BaseSessionService(SessionType type,
- const base::FilePath& path,
- BaseSessionServiceDelegate* delegate);
- ~BaseSessionService();
-
- // Moves the current session to the last session.
- void MoveCurrentSessionToLastSession();
-
- // Deletes the last session.
- void DeleteLastSession();
-
- // Returns the set of commands which were scheduled to be written. Once
- // committed to the backend, the commands are removed from here.
- const std::vector<std::unique_ptr<SessionCommand>>& pending_commands() {
- return pending_commands_;
- }
-
- // Whether the next save resets the file before writing to it.
- void set_pending_reset(bool value) { pending_reset_ = value; }
- bool pending_reset() const { return pending_reset_; }
-
- // Returns the number of commands sent down since the last reset.
- int commands_since_reset() const { return commands_since_reset_; }
-
- // Schedules a command. This adds |command| to pending_commands_ and
- // invokes StartSaveTimer to start a timer that invokes Save at a later
- // time.
- void ScheduleCommand(std::unique_ptr<SessionCommand> command);
-
- // Appends a command as part of a general rebuild. This will neither count
- // against a rebuild, nor will it trigger a save of commands.
- void AppendRebuildCommand(std::unique_ptr<SessionCommand> command);
-
- // Erase the |old_command| from the list of commands.
- // The passed command will automatically be deleted.
- void EraseCommand(SessionCommand* old_command);
-
- // Swap a |new_command| into the list of queued commands at the location of
- // the |old_command|. The |old_command| will be automatically deleted in the
- // process.
- void SwapCommand(SessionCommand* old_command,
- std::unique_ptr<SessionCommand> new_command);
-
- // Clears all commands from the list.
- void ClearPendingCommands();
-
- // Starts the timer that invokes Save (if timer isn't already running).
- void StartSaveTimer();
-
- // Passes all pending commands to the backend for saving.
- void Save();
-
- // Uses the backend to load the last session commands from disc. |callback|
- // gets called once the data has arrived.
- base::CancelableTaskTracker::TaskId ScheduleGetLastSessionCommands(
- const GetCommandsCallback& callback,
- base::CancelableTaskTracker* tracker);
-
- private:
- friend class BaseSessionServiceTestHelper;
-
- // This posts the task to the TaskRunner.
- void RunTaskOnBackendThread(const base::Location& from_here,
- base::OnceClosure task);
-
- // The backend object which reads and saves commands.
- scoped_refptr<SessionBackend> backend_;
-
- // Commands we need to send over to the backend.
- std::vector<std::unique_ptr<SessionCommand>> pending_commands_;
-
- // Whether the backend file should be recreated the next time we send
- // over the commands.
- bool pending_reset_;
-
- // The number of commands sent to the backend before doing a reset.
- int commands_since_reset_;
-
- BaseSessionServiceDelegate* delegate_;
-
- // TaskRunner all backend tasks are run on. This is a SequencedTaskRunner as
- // all tasks *must* be processed in the order they are scheduled.
- scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
-
- // Used to invoke Save.
- base::WeakPtrFactory<BaseSessionService> weak_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(BaseSessionService);
-};
-
-} // namespace sessions
-
-#endif // COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_H_
diff --git a/chromium/components/sessions/core/base_session_service_commands.cc b/chromium/components/sessions/core/base_session_service_commands.cc
index 42b1e9d7549..cd3bf654a53 100644
--- a/chromium/components/sessions/core/base_session_service_commands.cc
+++ b/chromium/components/sessions/core/base_session_service_commands.cc
@@ -7,7 +7,7 @@
#include <stddef.h>
#include "base/pickle.h"
-#include "components/sessions/core/session_backend.h"
+#include "components/sessions/core/command_storage_backend.h"
#include "components/sessions/core/session_types.h"
namespace sessions {
diff --git a/chromium/components/sessions/core/base_session_service_delegate.h b/chromium/components/sessions/core/base_session_service_delegate.h
deleted file mode 100644
index 1a2c245611c..00000000000
--- a/chromium/components/sessions/core/base_session_service_delegate.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_DELEGATE_H_
-#define COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_DELEGATE_H_
-
-namespace sessions {
-
-// The BaseSessionServiceDelegate decouples the BaseSessionService from
-// chrome/content dependencies.
-class BaseSessionServiceDelegate {
- public:
- BaseSessionServiceDelegate() {}
-
- // Returns true if save operations can be performed as a delayed task - which
- // is usually only used by unit tests.
- virtual bool ShouldUseDelayedSave() = 0;
-
- // Called when commands are about to be written to disc.
- virtual void OnWillSaveCommands() {}
-
- protected:
- virtual ~BaseSessionServiceDelegate() {}
-};
-
-} // namespace sessions
-
-#endif // COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_DELEGATE_H_
diff --git a/chromium/components/sessions/core/base_session_service_test_helper.cc b/chromium/components/sessions/core/base_session_service_test_helper.cc
deleted file mode 100644
index 5d06a68d816..00000000000
--- a/chromium/components/sessions/core/base_session_service_test_helper.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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/base_session_service_test_helper.h"
-
-#include "components/sessions/core/base_session_service.h"
-#include "components/sessions/core/session_backend.h"
-
-namespace sessions {
-
-BaseSessionServiceTestHelper::BaseSessionServiceTestHelper(
- BaseSessionService* base_session_service)
- : base_session_service_(base_session_service) {
- CHECK(base_session_service);
-}
-
-BaseSessionServiceTestHelper::~BaseSessionServiceTestHelper() {
-}
-
-void BaseSessionServiceTestHelper::RunTaskOnBackendThread(
- const base::Location& from_here,
- const base::Closure& task) {
- base_session_service_->RunTaskOnBackendThread(from_here, task);
-}
-
-bool BaseSessionServiceTestHelper::ProcessedAnyCommands() {
- return base_session_service_->backend_->inited() ||
- !base_session_service_->pending_commands().empty();
-}
-
-bool BaseSessionServiceTestHelper::ReadLastSessionCommands(
- std::vector<std::unique_ptr<SessionCommand>>* commands) {
- return base_session_service_->backend_->ReadLastSessionCommandsImpl(commands);
-}
-
-} // namespace sessions
diff --git a/chromium/components/sessions/core/command_storage_backend.cc b/chromium/components/sessions/core/command_storage_backend.cc
new file mode 100644
index 00000000000..466d4ba422f
--- /dev/null
+++ b/chromium/components/sessions/core/command_storage_backend.cc
@@ -0,0 +1,473 @@
+// Copyright 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 "components/sessions/core/command_storage_backend.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "build/build_config.h"
+#include "crypto/aead.h"
+
+namespace sessions {
+
+namespace {
+
+// File version number.
+constexpr int32_t kFileCurrentVersion = 1;
+constexpr int32_t kEncryptedFileCurrentVersion = 2;
+
+// The signature at the beginning of the file = SSNS (Sessions).
+constexpr int32_t kFileSignature = 0x53534E53;
+
+// Length (in bytes) of the nonce (used when encrypting).
+constexpr int kNonceLength = 12;
+
+// The file header is the first bytes written to the file,
+// and is used to identify the file as one written by us.
+struct FileHeader {
+ int32_t signature;
+ int32_t version;
+};
+
+// SessionFileReader ----------------------------------------------------------
+
+// SessionFileReader is responsible for reading the set of SessionCommands that
+// describe a Session back from a file. SessionFileRead does minimal error
+// checking on the file (pretty much only that the header is valid).
+
+class SessionFileReader {
+ public:
+ typedef sessions::SessionCommand::id_type id_type;
+ typedef sessions::SessionCommand::size_type size_type;
+
+ SessionFileReader(const base::FilePath& path,
+ const std::vector<uint8_t>& crypto_key)
+ : buffer_(CommandStorageBackend::kFileReadBufferSize, 0),
+ crypto_key_(crypto_key) {
+ if (!crypto_key.empty()) {
+ aead_ = std::make_unique<crypto::Aead>(crypto::Aead::AES_256_GCM);
+ aead_->Init(base::make_span(crypto_key_));
+ }
+ file_ = std::make_unique<base::File>(
+ path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ }
+ // Reads the contents of the file specified in the constructor, returning
+ // true on success, and filling up |commands| with commands.
+ bool Read(std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
+
+ private:
+ // Reads a single command, returning it. A return value of null indicates
+ // either there are no commands, or there was an error. Use errored_ to
+ // distinguish the two. If null is returned, and there is no error, it means
+ // the end of file was successfully reached.
+ std::unique_ptr<sessions::SessionCommand> ReadCommand();
+
+ // Decrypts a previously encrypted command. Returns the new command on
+ // success.
+ std::unique_ptr<sessions::SessionCommand> CreateCommandFromEncrypted(
+ const char* data,
+ size_type length);
+
+ // Creates a command from the previously written value.
+ std::unique_ptr<sessions::SessionCommand> CreateCommand(const char* data,
+ size_type length);
+
+ // Shifts the unused portion of buffer_ to the beginning and fills the
+ // remaining portion with data from the file. Returns false if the buffer
+ // couldn't be filled. A return value of false only signals an error if
+ // errored_ is set to true.
+ bool FillBuffer();
+
+ // Whether an error condition has been detected (
+ bool errored_ = false;
+
+ // As we read from the file, data goes here.
+ std::string buffer_;
+
+ const std::vector<uint8_t> crypto_key_;
+
+ std::unique_ptr<crypto::Aead> aead_;
+
+ // The file.
+ std::unique_ptr<base::File> file_;
+
+ // Position in buffer_ of the data.
+ size_t buffer_position_ = 0;
+
+ // Number of available bytes; relative to buffer_position_.
+ size_t available_count_ = 0;
+
+ // Count of the number of commands encountered.
+ int command_counter_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionFileReader);
+};
+
+bool SessionFileReader::Read(
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
+ if (!file_->IsValid())
+ return false;
+ FileHeader header;
+ int read_count;
+ read_count =
+ file_->ReadAtCurrentPos(reinterpret_cast<char*>(&header), sizeof(header));
+ if (read_count != sizeof(header) || header.signature != kFileSignature) {
+ const bool encrypt = aead_.get() != nullptr;
+ if ((encrypt && header.version != kEncryptedFileCurrentVersion) ||
+ (!encrypt && header.version != kFileCurrentVersion)) {
+ return false;
+ }
+ }
+
+ std::vector<std::unique_ptr<sessions::SessionCommand>> read_commands;
+ for (std::unique_ptr<sessions::SessionCommand> command = ReadCommand();
+ command && !errored_; command = ReadCommand())
+ read_commands.push_back(std::move(command));
+ if (!errored_)
+ read_commands.swap(*commands);
+ return !errored_;
+}
+
+std::unique_ptr<sessions::SessionCommand> SessionFileReader::ReadCommand() {
+ // Make sure there is enough in the buffer for the size of the next command.
+ if (available_count_ < sizeof(size_type)) {
+ if (!FillBuffer())
+ return nullptr;
+ if (available_count_ < sizeof(size_type)) {
+ VLOG(1) << "SessionFileReader::ReadCommand, file incomplete";
+ // Still couldn't read a valid size for the command, assume write was
+ // incomplete and return null.
+ return nullptr;
+ }
+ }
+ // Get the size of the command.
+ size_type command_size;
+ memcpy(&command_size, &(buffer_[buffer_position_]), sizeof(command_size));
+ buffer_position_ += sizeof(command_size);
+ available_count_ -= sizeof(command_size);
+
+ if (command_size == 0) {
+ VLOG(1) << "SessionFileReader::ReadCommand, empty command";
+ // Empty command. Shouldn't happen if write was successful, fail.
+ return nullptr;
+ }
+
+ // Make sure buffer has the complete contents of the command.
+ if (command_size > available_count_) {
+ if (command_size > buffer_.size())
+ buffer_.resize((command_size / 1024 + 1) * 1024, 0);
+ if (!FillBuffer() || command_size > available_count_) {
+ // Again, assume the file was ok, and just the last chunk was lost.
+ VLOG(1) << "SessionFileReader::ReadCommand, last chunk lost";
+ return nullptr;
+ }
+ }
+ std::unique_ptr<SessionCommand> command;
+ if (aead_) {
+ command = CreateCommandFromEncrypted(buffer_.c_str() + buffer_position_,
+ command_size);
+ } else {
+ command = CreateCommand(buffer_.c_str() + buffer_position_, command_size);
+ }
+ ++command_counter_;
+ buffer_position_ += command_size;
+ available_count_ -= command_size;
+ return command;
+}
+
+std::unique_ptr<sessions::SessionCommand>
+SessionFileReader::CreateCommandFromEncrypted(const char* data,
+ size_type length) {
+ // This means the nonce overflowed and we're reusing a nonce.
+ // CommandStorageBackend should never write enough commands to trigger this,
+ // so assume we should stop.
+ if (command_counter_ < 0)
+ return nullptr;
+
+ char nonce[kNonceLength];
+ memset(nonce, 0, kNonceLength);
+ memcpy(nonce, &command_counter_, sizeof(command_counter_));
+ std::string plain_text;
+ if (!aead_->Open(base::StringPiece(data, length),
+ base::StringPiece(nonce, kNonceLength), base::StringPiece(),
+ &plain_text)) {
+ DVLOG(1) << "SessionFileReader::ReadCommand, decryption failed";
+ return nullptr;
+ }
+ if (plain_text.size() < sizeof(id_type)) {
+ DVLOG(1) << "SessionFileReader::ReadCommand, size too small";
+ return nullptr;
+ }
+ return CreateCommand(plain_text.c_str(), plain_text.size());
+}
+
+std::unique_ptr<sessions::SessionCommand> SessionFileReader::CreateCommand(
+ const char* data,
+ size_type length) {
+ // Callers should have checked the size.
+ DCHECK_GE(length, sizeof(id_type));
+ const id_type command_id = data[0];
+ // NOTE: |length| includes the size of the id, which is not part of the
+ // contents of the SessionCommand.
+ std::unique_ptr<sessions::SessionCommand> command =
+ std::make_unique<sessions::SessionCommand>(command_id,
+ length - sizeof(id_type));
+ if (length > sizeof(id_type)) {
+ memcpy(command->contents(), &(data[sizeof(id_type)]),
+ length - sizeof(id_type));
+ }
+ return command;
+}
+
+bool SessionFileReader::FillBuffer() {
+ if (available_count_ > 0 && buffer_position_ > 0) {
+ // Shift buffer to beginning.
+ memmove(&(buffer_[0]), &(buffer_[buffer_position_]), available_count_);
+ }
+ buffer_position_ = 0;
+ DCHECK(buffer_position_ + available_count_ < buffer_.size());
+ int to_read = static_cast<int>(buffer_.size() - available_count_);
+ int read_count =
+ file_->ReadAtCurrentPos(&(buffer_[available_count_]), to_read);
+ if (read_count < 0) {
+ errored_ = true;
+ return false;
+ }
+ if (read_count == 0)
+ return false;
+ available_count_ += read_count;
+ return true;
+}
+
+} // namespace
+
+// CommandStorageBackend
+// -------------------------------------------------------------
+
+// static
+const int CommandStorageBackend::kFileReadBufferSize = 1024;
+
+// static
+const SessionCommand::size_type
+ CommandStorageBackend::kEncryptionOverheadInBytes = 16;
+
+CommandStorageBackend::CommandStorageBackend(
+ scoped_refptr<base::SequencedTaskRunner> owning_task_runner,
+ const base::FilePath& path)
+ : RefCountedDeleteOnSequence(owning_task_runner), path_(path) {}
+
+void CommandStorageBackend::AppendCommands(
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands,
+ bool truncate,
+ const std::vector<uint8_t>& crypto_key) {
+ InitIfNecessary();
+
+ if (truncate) {
+ const bool was_encrypted = IsEncrypted();
+ const bool encrypt = !crypto_key.empty();
+ if (was_encrypted != encrypt) {
+ // The header is different when encrypting, so the file needs to be
+ // recreated.
+ CloseFile();
+ }
+ if (encrypt) {
+ aead_ = std::make_unique<crypto::Aead>(crypto::Aead::AES_256_GCM);
+ crypto_key_ = crypto_key;
+ aead_->Init(base::make_span(crypto_key_));
+ } else {
+ aead_.reset();
+ }
+ } else {
+ // |crypto_key| is only used when |truncate| is true.
+ DCHECK(crypto_key.empty());
+ }
+
+ // Make sure and check |file_|, if opening the file failed |file_| will be
+ // null.
+ if (truncate || !file_ || !file_->IsValid())
+ TruncateFile();
+
+ // Check |file_| again as TruncateFile() may fail.
+ if (file_ && file_->IsValid() &&
+ !AppendCommandsToFile(file_.get(), commands)) {
+ file_.reset();
+ }
+}
+
+void CommandStorageBackend::ReadCurrentSessionCommands(
+ const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
+ const std::vector<uint8_t>& crypto_key,
+ GetCommandsCallback callback) {
+ if (is_canceled.Run())
+ return;
+
+ InitIfNecessary();
+
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
+ ReadCommandsFromFile(path_, crypto_key, &commands);
+ std::move(callback).Run(std::move(commands));
+}
+
+bool CommandStorageBackend::AppendCommandsToFile(
+ base::File* file,
+ const std::vector<std::unique_ptr<sessions::SessionCommand>>& commands) {
+ for (auto& command : commands) {
+ if (IsEncrypted()) {
+ if (!AppendEncryptedCommandToFile(file, *(command.get())))
+ return false;
+ } else if (!AppendCommandToFile(file, *(command.get()))) {
+ return false;
+ }
+ commands_written_++;
+ }
+#if defined(OS_CHROMEOS)
+ file->Flush();
+#endif
+ return true;
+}
+
+CommandStorageBackend::~CommandStorageBackend() = default;
+
+void CommandStorageBackend::InitIfNecessary() {
+ if (inited_)
+ return;
+
+ inited_ = true;
+ base::CreateDirectory(path_.DirName());
+ DoInit();
+}
+
+bool CommandStorageBackend::ReadCommandsFromFile(
+ const base::FilePath& path,
+ const std::vector<uint8_t>& crypto_key,
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
+ SessionFileReader file_reader(path, crypto_key);
+ return file_reader.Read(commands);
+}
+
+void CommandStorageBackend::CloseFile() {
+ file_.reset();
+}
+
+void CommandStorageBackend::TruncateFile() {
+ DCHECK(inited_);
+ if (file_) {
+ // File is already open, truncate it. We truncate instead of closing and
+ // reopening to avoid the possibility of scanners locking the file out
+ // from under us once we close it. If truncation fails, we'll try to
+ // recreate.
+ const int header_size = static_cast<int>(sizeof(FileHeader));
+ if (file_->Seek(base::File::FROM_BEGIN, header_size) != header_size ||
+ !file_->SetLength(header_size))
+ file_.reset();
+ }
+ if (!file_)
+ file_ = OpenAndWriteHeader(path_);
+ commands_written_ = 0;
+}
+
+std::unique_ptr<base::File> CommandStorageBackend::OpenAndWriteHeader(
+ const base::FilePath& path) {
+ DCHECK(!path.empty());
+ std::unique_ptr<base::File> file = std::make_unique<base::File>(
+ path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
+ base::File::FLAG_EXCLUSIVE_WRITE |
+ base::File::FLAG_EXCLUSIVE_READ);
+ if (!file->IsValid())
+ return nullptr;
+ FileHeader header;
+ header.signature = kFileSignature;
+ header.version =
+ IsEncrypted() ? kEncryptedFileCurrentVersion : kFileCurrentVersion;
+ if (file->WriteAtCurrentPos(reinterpret_cast<char*>(&header),
+ sizeof(header)) != sizeof(header)) {
+ return nullptr;
+ }
+ commands_written_ = 0;
+ return file;
+}
+
+bool CommandStorageBackend::AppendCommandToFile(
+ base::File* file,
+ const sessions::SessionCommand& command) {
+ const size_type total_size = command.GetSerializedSize();
+ if (file->WriteAtCurrentPos(reinterpret_cast<const char*>(&total_size),
+ sizeof(total_size)) != sizeof(total_size)) {
+ DVLOG(1) << "error writing";
+ return false;
+ }
+ id_type command_id = command.id();
+ if (file->WriteAtCurrentPos(reinterpret_cast<char*>(&command_id),
+ sizeof(command_id)) != sizeof(command_id)) {
+ DVLOG(1) << "error writing";
+ return false;
+ }
+
+ const size_type content_size = total_size - sizeof(id_type);
+ if (content_size == 0)
+ return true;
+
+ if (file->WriteAtCurrentPos(reinterpret_cast<const char*>(command.contents()),
+ content_size) != content_size) {
+ DVLOG(1) << "error writing";
+ return false;
+ }
+ return true;
+}
+
+bool CommandStorageBackend::AppendEncryptedCommandToFile(
+ base::File* file,
+ const sessions::SessionCommand& command) {
+ // This means the nonce overflowed and we're reusing a nonce. This class
+ // should never write enough commands to trigger this, so assume we should
+ // stop.
+ if (commands_written_ < 0)
+ return false;
+ DCHECK(IsEncrypted());
+ char nonce[kNonceLength];
+ memset(nonce, 0, kNonceLength);
+ memcpy(nonce, &commands_written_, sizeof(commands_written_));
+
+ // Encryption adds overhead, resulting in a slight reduction in the available
+ // space for each command. Chop any contents beyond the available size.
+ const size_type command_size = std::min(
+ command.size(),
+ static_cast<size_type>(std::numeric_limits<size_type>::max() -
+ sizeof(id_type) - kEncryptionOverheadInBytes));
+ std::vector<char> command_and_id(command_size + sizeof(id_type));
+ const id_type command_id = command.id();
+ memcpy(&command_and_id.front(), reinterpret_cast<const char*>(&command_id),
+ sizeof(id_type));
+ memcpy(&(command_and_id.front()) + sizeof(id_type), command.contents(),
+ command_size);
+
+ std::string cipher_text;
+ aead_->Seal(base::StringPiece(&command_and_id.front(), command_and_id.size()),
+ base::StringPiece(nonce, kNonceLength), base::StringPiece(),
+ &cipher_text);
+ DCHECK_LE(cipher_text.size(), std::numeric_limits<size_type>::max());
+ const size_type command_and_id_size =
+ static_cast<size_type>(cipher_text.size());
+
+ int wrote = file->WriteAtCurrentPos(
+ reinterpret_cast<const char*>(&command_and_id_size),
+ sizeof(command_and_id_size));
+ if (wrote != sizeof(command_and_id_size)) {
+ DVLOG(1) << "error writing";
+ return false;
+ }
+ wrote = file->WriteAtCurrentPos(cipher_text.c_str(), cipher_text.size());
+ if (wrote != static_cast<int>(cipher_text.size())) {
+ DVLOG(1) << "error writing";
+ return false;
+ }
+ return true;
+}
+
+} // namespace sessions
diff --git a/chromium/components/sessions/core/command_storage_backend.h b/chromium/components/sessions/core/command_storage_backend.h
new file mode 100644
index 00000000000..8fb526fb137
--- /dev/null
+++ b/chromium/components/sessions/core/command_storage_backend.h
@@ -0,0 +1,159 @@
+// Copyright 2011 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.
+
+#ifndef COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_BACKEND_H_
+#define COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_BACKEND_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/sessions/core/session_command.h"
+#include "components/sessions/core/sessions_export.h"
+
+namespace base {
+class File;
+}
+
+namespace crypto {
+class Aead;
+}
+
+namespace sessions {
+
+// CommandStorageBackend is the backend used by CommandStorageManager. It writes
+// SessionCommands to disk with the ability to read back at a later date.
+// CommandStorageBackend does not interpret the commands in anyway, it simply
+// reads/writes them.
+class SESSIONS_EXPORT CommandStorageBackend
+ : public base::RefCountedDeleteOnSequence<CommandStorageBackend> {
+ public:
+ using id_type = SessionCommand::id_type;
+ using size_type = SessionCommand::size_type;
+ using GetCommandsCallback =
+ base::OnceCallback<void(std::vector<std::unique_ptr<SessionCommand>>)>;
+
+ // Initial size of the buffer used in reading the file. This is exposed
+ // for testing.
+ static const int kFileReadBufferSize;
+
+ // Number of bytes encryption adds.
+ static const size_type kEncryptionOverheadInBytes;
+
+ // Creates a CommandStorageBackend. This method is invoked on the MAIN thread,
+ // and does no IO. The real work is done from Init, which is invoked on
+ // a background task runer.
+ //
+ // |path| is the path the file is written to.
+ CommandStorageBackend(
+ scoped_refptr<base::SequencedTaskRunner> owning_task_runner,
+ const base::FilePath& path);
+
+ base::SequencedTaskRunner* owning_task_runner() {
+ return base::RefCountedDeleteOnSequence<
+ CommandStorageBackend>::owning_task_runner();
+ }
+
+ // Appends the specified commands to the current file. If |truncate| is true
+ // the file is truncated. If |truncate| is true and |crypto_key| is non-empty,
+ // then all commands are encrypted using the supplied key.
+ void AppendCommands(
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands,
+ bool truncate,
+ const std::vector<uint8_t>& crypto_key = std::vector<uint8_t>());
+
+ // Reads the commands that make up the current session. If |crypto_key|
+ // is non-empty, it is used to decrypt the file.
+ void ReadCurrentSessionCommands(
+ const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
+ const std::vector<uint8_t>& crypto_key,
+ GetCommandsCallback callback);
+
+ bool inited() const { return inited_; }
+
+ protected:
+ virtual ~CommandStorageBackend();
+
+ // Performs initialization on the background task run, calling DoInit() if
+ // necessary.
+ void InitIfNecessary();
+
+ // Called the first time InitIfNecessary() is called.
+ virtual void DoInit() {}
+
+ const base::FilePath& path() const { return path_; }
+
+ // Reads the commands from the specified file. If |crypto_key| is non-empty,
+ // it is used to decrypt the file. On success, the read commands are added to
+ // |commands|.
+ bool ReadCommandsFromFile(
+ const base::FilePath& path,
+ const std::vector<uint8_t>& crypto_key,
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
+
+ // Closes the file. The next time AppendCommands() is called the file will
+ // implicitly be reopened.
+ void CloseFile();
+
+ // If current_session_file_ is open, it is truncated so that it is essentially
+ // empty (only contains the header). If current_session_file_ isn't open, it
+ // is is opened and the header is written to it. After this
+ // current_session_file_ contains no commands.
+ // NOTE: current_session_file_ may be null if the file couldn't be opened or
+ // the header couldn't be written.
+ void TruncateFile();
+
+ private:
+ friend class base::RefCountedDeleteOnSequence<CommandStorageBackend>;
+ friend class base::DeleteHelper<CommandStorageBackend>;
+
+ // Opens the current file and writes the header. On success a handle to
+ // the file is returned.
+ std::unique_ptr<base::File> OpenAndWriteHeader(const base::FilePath& path);
+
+ // Appends the specified commands to the specified file.
+ bool AppendCommandsToFile(
+ base::File* file,
+ const std::vector<std::unique_ptr<sessions::SessionCommand>>& commands);
+
+ // Writes |command| to |file|. Returns true on success.
+ bool AppendCommandToFile(base::File* file,
+ const sessions::SessionCommand& command);
+
+ // Encrypts |command| and writes it to |file|. Returns true on success.
+ // The contents of the command and id are encrypted together. This is
+ // preceded by the length of the command.
+ bool AppendEncryptedCommandToFile(base::File* file,
+ const sessions::SessionCommand& command);
+
+ // Returns true if commands are encrypted.
+ bool IsEncrypted() const { return !crypto_key_.empty(); }
+
+ // Path commands are saved to.
+ const base::FilePath path_;
+
+ // This may be null, created as necessary.
+ std::unique_ptr<base::File> file_;
+
+ // Whether DoInit() was called. DoInit() is called on the background task
+ // runner.
+ bool inited_ = false;
+
+ std::vector<uint8_t> crypto_key_;
+ std::unique_ptr<crypto::Aead> aead_;
+
+ // Incremented every time a command is written.
+ int commands_written_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(CommandStorageBackend);
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_BACKEND_H_
diff --git a/chromium/components/sessions/core/command_storage_backend_unittest.cc b/chromium/components/sessions/core/command_storage_backend_unittest.cc
new file mode 100644
index 00000000000..bafb0d5c666
--- /dev/null
+++ b/chromium/components/sessions/core/command_storage_backend_unittest.cc
@@ -0,0 +1,290 @@
+// Copyright 2011 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/command_storage_backend.h"
+
+#include <stddef.h>
+#include <limits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "components/sessions/core/command_storage_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::MakeRefCounted;
+
+using size_type = sessions::SessionCommand::size_type;
+namespace sessions {
+namespace {
+
+using SessionCommands = std::vector<std::unique_ptr<sessions::SessionCommand>>;
+
+struct TestData {
+ sessions::SessionCommand::id_type command_id;
+ std::string data;
+};
+
+std::unique_ptr<sessions::SessionCommand> CreateCommandFromData(
+ const TestData& data) {
+ std::unique_ptr<sessions::SessionCommand> command =
+ std::make_unique<sessions::SessionCommand>(
+ data.command_id,
+ static_cast<sessions::SessionCommand::size_type>(data.data.size()));
+ if (!data.data.empty())
+ memcpy(command->contents(), data.data.c_str(), data.data.size());
+ return command;
+}
+
+bool IsCanceled() {
+ return false;
+}
+
+} // namespace
+
+class CommandStorageBackendTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ path_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Session"));
+ }
+
+ void AssertCommandEqualsData(const TestData& data,
+ sessions::SessionCommand* command) {
+ EXPECT_EQ(data.command_id, command->id());
+ EXPECT_EQ(data.data.size(), command->size());
+ EXPECT_TRUE(
+ memcmp(command->contents(), data.data.c_str(), command->size()) == 0);
+ }
+
+ scoped_refptr<CommandStorageBackend> CreateBackend() {
+ return MakeRefCounted<CommandStorageBackend>(
+ task_environment_.GetMainThreadTaskRunner(), path_);
+ }
+
+ void ReadCurrentSessionCommands(
+ CommandStorageBackend* backend,
+ const std::vector<uint8_t>& crypto_key,
+ std::vector<std::unique_ptr<SessionCommand>>* commands) {
+ backend->ReadCurrentSessionCommands(
+ base::BindRepeating(&IsCanceled), crypto_key,
+ base::BindLambdaForTesting(
+ [&commands](std::vector<std::unique_ptr<SessionCommand>> result) {
+ *commands = std::move(result);
+ }));
+ }
+
+ base::test::TaskEnvironment task_environment_;
+ base::FilePath path_;
+ base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(CommandStorageBackendTest, SimpleReadWriteEncrypted) {
+ std::vector<uint8_t> key = CommandStorageManager::CreateCryptoKey();
+ scoped_refptr<CommandStorageBackend> backend = CreateBackend();
+ struct TestData data = {1, "a"};
+ SessionCommands commands;
+ commands.push_back(CreateCommandFromData(data));
+ backend->AppendCommands(std::move(commands), true, key);
+ ASSERT_TRUE(commands.empty());
+
+ // Read it back in.
+ backend = nullptr;
+ backend = CreateBackend();
+ ReadCurrentSessionCommands(backend.get(), key, &commands);
+
+ ASSERT_EQ(1U, commands.size());
+ AssertCommandEqualsData(data, commands[0].get());
+
+ // Repeat, but with the wrong key.
+ backend = nullptr;
+ ++(key[0]);
+ backend = CreateBackend();
+ ReadCurrentSessionCommands(backend.get(), key, &commands);
+ EXPECT_TRUE(commands.empty());
+}
+
+TEST_F(CommandStorageBackendTest, RandomDataEncrypted) {
+ struct TestData data[] = {
+ {1, "a"},
+ {2, "ab"},
+ {3, "abc"},
+ {4, "abcd"},
+ {5, "abcde"},
+ {6, "abcdef"},
+ {7, "abcdefg"},
+ {8, "abcdefgh"},
+ {9, "abcdefghi"},
+ {10, "abcdefghij"},
+ {11, "abcdefghijk"},
+ {12, "abcdefghijkl"},
+ {13, "abcdefghijklm"},
+ };
+
+ const std::vector<uint8_t> key = CommandStorageManager::CreateCryptoKey();
+ for (size_t i = 0; i < base::size(data); ++i) {
+ scoped_refptr<CommandStorageBackend> backend = CreateBackend();
+ SessionCommands commands;
+ if (i != 0) {
+ // Read previous data.
+ ReadCurrentSessionCommands(backend.get(), key, &commands);
+ ASSERT_EQ(i, commands.size());
+ for (auto j = commands.begin(); j != commands.end(); ++j)
+ AssertCommandEqualsData(data[j - commands.begin()], j->get());
+
+ backend->AppendCommands(std::move(commands), true, key);
+ }
+ commands.push_back(CreateCommandFromData(data[i]));
+ backend->AppendCommands(std::move(commands), i == 0,
+ i == 0 ? key : std::vector<uint8_t>());
+ }
+}
+
+TEST_F(CommandStorageBackendTest, BigDataEncrypted) {
+ struct TestData data[] = {
+ {1, "a"},
+ {2, "ab"},
+ };
+
+ const std::vector<uint8_t> key = CommandStorageManager::CreateCryptoKey();
+ scoped_refptr<CommandStorageBackend> backend = CreateBackend();
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
+
+ commands.push_back(CreateCommandFromData(data[0]));
+ const sessions::SessionCommand::size_type big_size =
+ CommandStorageBackend::kFileReadBufferSize + 100;
+ const sessions::SessionCommand::id_type big_id = 50;
+ std::unique_ptr<sessions::SessionCommand> big_command =
+ std::make_unique<sessions::SessionCommand>(big_id, big_size);
+ reinterpret_cast<char*>(big_command->contents())[0] = 'a';
+ reinterpret_cast<char*>(big_command->contents())[big_size - 1] = 'z';
+ commands.push_back(std::move(big_command));
+ commands.push_back(CreateCommandFromData(data[1]));
+ backend->AppendCommands(std::move(commands), true, key);
+
+ backend = nullptr;
+ backend = CreateBackend();
+
+ ReadCurrentSessionCommands(backend.get(), key, &commands);
+ ASSERT_EQ(3U, commands.size());
+ AssertCommandEqualsData(data[0], commands[0].get());
+ AssertCommandEqualsData(data[1], commands[2].get());
+
+ EXPECT_EQ(big_id, commands[1]->id());
+ ASSERT_EQ(big_size, commands[1]->size());
+ EXPECT_EQ('a', reinterpret_cast<char*>(commands[1]->contents())[0]);
+ EXPECT_EQ('z',
+ reinterpret_cast<char*>(commands[1]->contents())[big_size - 1]);
+}
+
+TEST_F(CommandStorageBackendTest, EmptyCommandEncrypted) {
+ TestData empty_command;
+ empty_command.command_id = 1;
+ std::vector<uint8_t> key = CommandStorageManager::CreateCryptoKey();
+ scoped_refptr<CommandStorageBackend> backend = CreateBackend();
+ SessionCommands empty_commands;
+ empty_commands.push_back(CreateCommandFromData(empty_command));
+ std::vector<uint8_t> key2 = key;
+ ++(key2[0]);
+ backend->AppendCommands(std::move(empty_commands), true, key2);
+
+ backend = nullptr;
+ backend = CreateBackend();
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
+ ReadCurrentSessionCommands(backend.get(), key2, &commands);
+ ASSERT_EQ(1U, commands.size());
+ AssertCommandEqualsData(empty_command, commands[0].get());
+}
+
+// Writes a command, appends another command with reset to true, then reads
+// making sure we only get back the second command.
+TEST_F(CommandStorageBackendTest, TruncateEncrypted) {
+ std::vector<uint8_t> key = CommandStorageManager::CreateCryptoKey();
+ scoped_refptr<CommandStorageBackend> backend = CreateBackend();
+ struct TestData first_data = {1, "a"};
+ SessionCommands commands;
+ commands.push_back(CreateCommandFromData(first_data));
+ backend->AppendCommands(std::move(commands), true, key);
+
+ // Write another command, this time resetting the file when appending.
+ struct TestData second_data = {2, "b"};
+ commands.push_back(CreateCommandFromData(second_data));
+ std::vector<uint8_t> key2 = key;
+ ++(key2[0]);
+ backend->AppendCommands(std::move(commands), true, key2);
+
+ // Read it back in.
+ backend = nullptr;
+ backend = CreateBackend();
+ ReadCurrentSessionCommands(backend.get(), key2, &commands);
+
+ // And make sure we get back the expected data.
+ ASSERT_EQ(1U, commands.size());
+ AssertCommandEqualsData(second_data, commands[0].get());
+}
+
+std::unique_ptr<SessionCommand> CreateCommandWithMaxSize() {
+ const size_type max_size_value = std::numeric_limits<size_type>::max();
+ std::unique_ptr<SessionCommand> command =
+ std::make_unique<SessionCommand>(11, max_size_value);
+ for (int i = 0; i <= max_size_value; ++i)
+ (command->contents())[i] = i;
+ return command;
+}
+
+TEST_F(CommandStorageBackendTest, MaxSizeTypeEncrypted) {
+ std::vector<uint8_t> key = CommandStorageManager::CreateCryptoKey();
+ scoped_refptr<CommandStorageBackend> backend = CreateBackend();
+
+ SessionCommands commands;
+ commands.push_back(CreateCommandWithMaxSize());
+ backend->AppendCommands(std::move(commands), true, key);
+
+ // Read it back in.
+ backend = nullptr;
+ backend = CreateBackend();
+ ReadCurrentSessionCommands(backend.get(), key, &commands);
+
+ // Encryption restricts the main size, and results in truncation.
+ ASSERT_EQ(1U, commands.size());
+ auto expected_command = CreateCommandWithMaxSize();
+ EXPECT_EQ(expected_command->id(), (commands[0])->id());
+ const size_type expected_size =
+ expected_command->size() -
+ CommandStorageBackend::kEncryptionOverheadInBytes -
+ sizeof(SessionCommand::id_type);
+ ASSERT_EQ(expected_size, (commands[0])->size());
+ EXPECT_TRUE(memcmp(commands[0]->contents(), expected_command->contents(),
+ expected_size) == 0);
+}
+
+TEST_F(CommandStorageBackendTest, MaxSizeType) {
+ scoped_refptr<CommandStorageBackend> backend = CreateBackend();
+
+ SessionCommands commands;
+ commands.push_back(CreateCommandWithMaxSize());
+ backend->AppendCommands(std::move(commands), true);
+
+ // Read it back in.
+ backend = nullptr;
+ backend = CreateBackend();
+ ReadCurrentSessionCommands(backend.get(), std::vector<uint8_t>(), &commands);
+
+ ASSERT_EQ(1U, commands.size());
+ auto expected_command = CreateCommandWithMaxSize();
+ EXPECT_EQ(expected_command->id(), (commands[0])->id());
+ const size_type expected_size =
+ expected_command->size() - sizeof(SessionCommand::id_type);
+ ASSERT_EQ(expected_size, (commands[0])->size());
+ EXPECT_TRUE(memcmp(commands[0]->contents(), expected_command->contents(),
+ expected_size) == 0);
+}
+
+} // namespace sessions
diff --git a/chromium/components/sessions/core/command_storage_manager.cc b/chromium/components/sessions/core/command_storage_manager.cc
new file mode 100644
index 00000000000..7e2d6579385
--- /dev/null
+++ b/chromium/components/sessions/core/command_storage_manager.cc
@@ -0,0 +1,212 @@
+// Copyright 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 "components/sessions/core/command_storage_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/sessions/core/command_storage_backend.h"
+#include "components/sessions/core/command_storage_manager_delegate.h"
+#include "crypto/random.h"
+
+namespace sessions {
+namespace {
+
+// Helper used by ScheduleGetLastSessionCommands. It runs callback on TaskRunner
+// thread if it's not canceled.
+void RunIfNotCanceled(
+ const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
+ CommandStorageManager::GetCommandsCallback callback,
+ std::vector<std::unique_ptr<SessionCommand>> commands) {
+ if (is_canceled.Run())
+ return;
+ std::move(callback).Run(std::move(commands));
+}
+
+void PostOrRunInternalGetCommandsCallback(
+ base::SequencedTaskRunner* task_runner,
+ CommandStorageManager::GetCommandsCallback callback,
+ std::vector<std::unique_ptr<SessionCommand>> commands) {
+ if (task_runner->RunsTasksInCurrentSequence()) {
+ std::move(callback).Run(std::move(commands));
+ } else {
+ task_runner->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), std::move(commands)));
+ }
+}
+
+} // namespace
+
+// Delay between when a command is received, and when we save it to the
+// backend.
+constexpr base::TimeDelta kSaveDelay = base::TimeDelta::FromMilliseconds(2500);
+
+CommandStorageManager::CommandStorageManager(
+ const base::FilePath& path,
+ CommandStorageManagerDelegate* delegate,
+ bool enable_crypto)
+ : CommandStorageManager(base::MakeRefCounted<CommandStorageBackend>(
+ CreateDefaultBackendTaskRunner(),
+ path),
+ delegate) {
+ use_crypto_ = enable_crypto;
+}
+
+CommandStorageManager::~CommandStorageManager() = default;
+
+// static
+std::vector<uint8_t> CommandStorageManager::CreateCryptoKey() {
+ std::vector<uint8_t> key(32);
+ crypto::RandBytes(&(key.front()), key.size());
+ return key;
+}
+
+void CommandStorageManager::ScheduleCommand(
+ std::unique_ptr<SessionCommand> command) {
+ DCHECK(command);
+ commands_since_reset_++;
+ pending_commands_.push_back(std::move(command));
+ StartSaveTimer();
+}
+
+void CommandStorageManager::AppendRebuildCommand(
+ std::unique_ptr<SessionCommand> command) {
+ std::vector<std::unique_ptr<SessionCommand>> commands;
+ commands.push_back(std::move(command));
+ AppendRebuildCommands(std::move(commands));
+}
+
+void CommandStorageManager::AppendRebuildCommands(
+ std::vector<std::unique_ptr<SessionCommand>> commands) {
+ pending_commands_.insert(pending_commands_.end(),
+ std::make_move_iterator(commands.begin()),
+ std::make_move_iterator(commands.end()));
+}
+
+void CommandStorageManager::EraseCommand(SessionCommand* old_command) {
+ auto it = std::find_if(
+ pending_commands_.begin(), pending_commands_.end(),
+ [old_command](const std::unique_ptr<SessionCommand>& command_ptr) {
+ return command_ptr.get() == old_command;
+ });
+ CHECK(it != pending_commands_.end());
+ pending_commands_.erase(it);
+}
+
+void CommandStorageManager::SwapCommand(
+ SessionCommand* old_command,
+ std::unique_ptr<SessionCommand> new_command) {
+ auto it = std::find_if(
+ pending_commands_.begin(), pending_commands_.end(),
+ [old_command](const std::unique_ptr<SessionCommand>& command_ptr) {
+ return command_ptr.get() == old_command;
+ });
+ CHECK(it != pending_commands_.end());
+ *it = std::move(new_command);
+}
+
+void CommandStorageManager::ClearPendingCommands() {
+ pending_commands_.clear();
+}
+
+void CommandStorageManager::StartSaveTimer() {
+ // Don't start a timer when testing.
+ if (delegate_->ShouldUseDelayedSave() &&
+ base::ThreadTaskRunnerHandle::IsSet() && !HasPendingSave()) {
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CommandStorageManager::Save,
+ weak_factory_for_timer_.GetWeakPtr()),
+ kSaveDelay);
+ }
+}
+
+void CommandStorageManager::Save() {
+ // Inform the delegate that we will save the commands now, giving it the
+ // opportunity to append more commands.
+ delegate_->OnWillSaveCommands();
+
+ if (pending_commands_.empty())
+ return;
+
+ std::vector<uint8_t> crypto_key;
+ if (use_crypto_ && pending_reset_) {
+ crypto_key = CreateCryptoKey();
+ delegate_->OnGeneratedNewCryptoKey(crypto_key);
+ }
+ backend_task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ base::BindOnce(&CommandStorageBackend::AppendCommands, backend_,
+ std::move(pending_commands_), pending_reset_, crypto_key));
+
+ if (pending_reset_) {
+ commands_since_reset_ = 0;
+ pending_reset_ = false;
+ }
+}
+
+bool CommandStorageManager::HasPendingSave() const {
+ return weak_factory_for_timer_.HasWeakPtrs();
+}
+
+base::CancelableTaskTracker::TaskId
+CommandStorageManager::ScheduleGetCurrentSessionCommands(
+ GetCommandsCallback callback,
+ const std::vector<uint8_t>& decryption_key,
+ base::CancelableTaskTracker* tracker) {
+ base::CancelableTaskTracker::IsCanceledCallback is_canceled;
+ GetCommandsCallback backend_callback;
+ const base::CancelableTaskTracker::TaskId id = CreateCallbackForGetCommands(
+ tracker, std::move(callback), &is_canceled, &backend_callback);
+
+ backend_task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ base::BindOnce(&CommandStorageBackend::ReadCurrentSessionCommands,
+ backend_.get(), is_canceled, decryption_key,
+ std::move(backend_callback)));
+ return id;
+}
+
+CommandStorageManager::CommandStorageManager(
+ scoped_refptr<CommandStorageBackend> backend,
+ CommandStorageManagerDelegate* delegate)
+ : backend_(std::move(backend)),
+ delegate_(delegate),
+ backend_task_runner_(backend_->owning_task_runner()) {}
+
+// static
+scoped_refptr<base::SequencedTaskRunner>
+CommandStorageManager::CreateDefaultBackendTaskRunner() {
+ return base::ThreadPool::CreateSequencedTaskRunner(
+ {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
+}
+
+base::CancelableTaskTracker::TaskId
+CommandStorageManager::CreateCallbackForGetCommands(
+ base::CancelableTaskTracker* tracker,
+ GetCommandsCallback callback,
+ base::CancelableTaskTracker::IsCanceledCallback* is_canceled,
+ GetCommandsCallback* backend_callback) {
+ const base::CancelableTaskTracker::TaskId id =
+ tracker->NewTrackedTaskId(is_canceled);
+
+ GetCommandsCallback run_if_not_canceled =
+ base::BindOnce(&RunIfNotCanceled, *is_canceled, std::move(callback));
+
+ *backend_callback =
+ base::BindOnce(&PostOrRunInternalGetCommandsCallback,
+ base::RetainedRef(base::ThreadTaskRunnerHandle::Get()),
+ std::move(run_if_not_canceled));
+ return id;
+}
+
+} // namespace sessions
diff --git a/chromium/components/sessions/core/command_storage_manager.h b/chromium/components/sessions/core/command_storage_manager.h
new file mode 100644
index 00000000000..1c31364740b
--- /dev/null
+++ b/chromium/components/sessions/core/command_storage_manager.h
@@ -0,0 +1,164 @@
+// Copyright 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.
+
+#ifndef COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_H_
+#define COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/sessions/core/sessions_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace sessions {
+class CommandStorageManagerDelegate;
+class SessionCommand;
+class CommandStorageBackend;
+
+// CommandStorageManager is responsible for reading/writing SessionCommands
+// to disk. SessionCommands are used to save and restore the state of the
+// browser. CommandStorageManager runs on the main thread and uses
+// CommandStorageBackend (which runs on a background task runner) for the actual
+// reading/writing. In hopes of minimizing IO, SessionCommands are queued up
+// and processed after a delay.
+class SESSIONS_EXPORT CommandStorageManager {
+ public:
+ using GetCommandsCallback =
+ base::OnceCallback<void(std::vector<std::unique_ptr<SessionCommand>>)>;
+
+ // Creates a new CommandStorageManager. After creation you need to invoke
+ // Init. |delegate| will remain owned by the creator and it is guaranteed
+ // that its lifetime surpasses this class. |path| is the path to save files
+ // to. If |enable_crypto| is true, the contents of the file are encrypted.
+ CommandStorageManager(const base::FilePath& path,
+ CommandStorageManagerDelegate* delegate,
+ bool enable_crypto = false);
+ virtual ~CommandStorageManager();
+
+ // Helper to generate a new key.
+ static std::vector<uint8_t> CreateCryptoKey();
+
+ // Returns the set of commands which were scheduled to be written. Once
+ // committed to the backend, the commands are removed from here.
+ const std::vector<std::unique_ptr<SessionCommand>>& pending_commands() {
+ return pending_commands_;
+ }
+
+ // Whether the next save resets the file before writing to it.
+ void set_pending_reset(bool value) { pending_reset_ = value; }
+ bool pending_reset() const { return pending_reset_; }
+
+ // Returns the number of commands sent down since the last reset.
+ int commands_since_reset() const { return commands_since_reset_; }
+
+ // Schedules a command. This adds |command| to pending_commands_ and
+ // invokes StartSaveTimer to start a timer that invokes Save at a later
+ // time.
+ void ScheduleCommand(std::unique_ptr<SessionCommand> command);
+
+ // Appends a command as part of a general rebuild. This will neither count
+ // against a rebuild, nor will it trigger a save of commands.
+ void AppendRebuildCommand(std::unique_ptr<SessionCommand> command);
+ void AppendRebuildCommands(
+ std::vector<std::unique_ptr<SessionCommand>> commands);
+
+ // Erase the |old_command| from the list of commands.
+ // The passed command will automatically be deleted.
+ void EraseCommand(SessionCommand* old_command);
+
+ // Swap a |new_command| into the list of queued commands at the location of
+ // the |old_command|. The |old_command| will be automatically deleted in the
+ // process.
+ void SwapCommand(SessionCommand* old_command,
+ std::unique_ptr<SessionCommand> new_command);
+
+ // Clears all commands from the list.
+ void ClearPendingCommands();
+
+ // Starts the timer that invokes Save (if timer isn't already running).
+ void StartSaveTimer();
+
+ // Passes all pending commands to the backend for saving.
+ void Save();
+
+ // Returns true if StartSaveTimer() has been called, but a save has not yet
+ // occurred.
+ bool HasPendingSave() const;
+
+ // Requests the commands for the current session. If |decryption_key| is
+ // non-empty it is used to decrypt the contents of the file.
+ base::CancelableTaskTracker::TaskId ScheduleGetCurrentSessionCommands(
+ GetCommandsCallback callback,
+ const std::vector<uint8_t>& decryption_key,
+ base::CancelableTaskTracker* tracker);
+
+ protected:
+ // Provided for subclasses.
+ CommandStorageManager(scoped_refptr<CommandStorageBackend> backend,
+ CommandStorageManagerDelegate* delegate);
+
+ // Creates a SequencedTaskRunner suitable for the backend.
+ static scoped_refptr<base::SequencedTaskRunner>
+ CreateDefaultBackendTaskRunner();
+
+ scoped_refptr<base::SequencedTaskRunner> backend_task_runner() {
+ return backend_task_runner_;
+ }
+
+ CommandStorageBackend* backend() { return backend_.get(); }
+
+ // Creates the necessary callbacks/taskid for using CancelableTaskTracker
+ // with a request for the backend to fetch session commands.
+ base::CancelableTaskTracker::TaskId CreateCallbackForGetCommands(
+ base::CancelableTaskTracker* tracker,
+ GetCommandsCallback callback,
+ base::CancelableTaskTracker::IsCanceledCallback* is_canceled,
+ GetCommandsCallback* backend_callback);
+
+ private:
+ friend class CommandStorageManagerTestHelper;
+
+ // The backend object which reads and saves commands.
+ scoped_refptr<CommandStorageBackend> backend_;
+
+ // If true, all commands are encrypted.
+ bool use_crypto_ = false;
+
+ // Commands we need to send over to the backend.
+ std::vector<std::unique_ptr<SessionCommand>> pending_commands_;
+
+ // Whether the backend file should be recreated the next time we send
+ // over the commands.
+ bool pending_reset_ = false;
+
+ // The number of commands sent to the backend before doing a reset.
+ int commands_since_reset_ = 0;
+
+ CommandStorageManagerDelegate* delegate_;
+
+ // TaskRunner all backend tasks are run on. This is a SequencedTaskRunner as
+ // all tasks *must* be processed in the order they are scheduled.
+ scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
+
+ // Used solely for saving after a delay, and not to be used for any other
+ // purposes.
+ base::WeakPtrFactory<CommandStorageManager> weak_factory_for_timer_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(CommandStorageManager);
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_H_
diff --git a/chromium/components/sessions/core/command_storage_manager_delegate.h b/chromium/components/sessions/core/command_storage_manager_delegate.h
new file mode 100644
index 00000000000..a641a2e8e61
--- /dev/null
+++ b/chromium/components/sessions/core/command_storage_manager_delegate.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_DELEGATE_H_
+#define COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_DELEGATE_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+namespace sessions {
+
+// The CommandStorageManagerDelegate decouples the CommandStorageManager from
+// chrome/content dependencies.
+class CommandStorageManagerDelegate {
+ public:
+ CommandStorageManagerDelegate() {}
+
+ // Returns true if save operations can be performed as a delayed task - which
+ // is usually only used by unit tests.
+ virtual bool ShouldUseDelayedSave() = 0;
+
+ // Called when commands are about to be written to disc.
+ virtual void OnWillSaveCommands() {}
+
+ // Called when a new crypto key has been generated. This is only called if
+ // CommandStorageManager was configured to enable encryption.
+ virtual void OnGeneratedNewCryptoKey(const std::vector<uint8_t>& key) {}
+
+ protected:
+ virtual ~CommandStorageManagerDelegate() {}
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_DELEGATE_H_
diff --git a/chromium/components/sessions/core/command_storage_manager_test_helper.cc b/chromium/components/sessions/core/command_storage_manager_test_helper.cc
new file mode 100644
index 00000000000..3b225fe80b4
--- /dev/null
+++ b/chromium/components/sessions/core/command_storage_manager_test_helper.cc
@@ -0,0 +1,55 @@
+// 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/command_storage_manager_test_helper.h"
+
+#include "base/bind.h"
+#include "base/test/bind_test_util.h"
+#include "components/sessions/core/command_storage_backend.h"
+#include "components/sessions/core/command_storage_manager.h"
+#include "components/sessions/core/snapshotting_command_storage_backend.h"
+
+namespace sessions {
+namespace {
+bool IsCanceled() {
+ return false;
+}
+} // namespace
+
+CommandStorageManagerTestHelper::CommandStorageManagerTestHelper(
+ CommandStorageManager* command_storage_manager)
+ : command_storage_manager_(command_storage_manager) {
+ CHECK(command_storage_manager);
+}
+
+void CommandStorageManagerTestHelper::RunTaskOnBackendThread(
+ const base::Location& from_here,
+ base::OnceClosure task) {
+ command_storage_manager_->backend_task_runner_->PostNonNestableTask(
+ from_here, std::move(task));
+}
+
+bool CommandStorageManagerTestHelper::ProcessedAnyCommands() {
+ return command_storage_manager_->backend_->inited() ||
+ !command_storage_manager_->pending_commands().empty();
+}
+
+void CommandStorageManagerTestHelper::ReadLastSessionCommands(
+ std::vector<std::unique_ptr<SessionCommand>>* commands) {
+ static_cast<SnapshottingCommandStorageBackend*>(
+ command_storage_manager_->backend_.get())
+ ->ReadLastSessionCommands(
+ base::BindRepeating(&IsCanceled),
+ base::BindLambdaForTesting(
+ [&commands](std::vector<std::unique_ptr<SessionCommand>> result) {
+ *commands = std::move(result);
+ }));
+}
+
+scoped_refptr<base::SequencedTaskRunner>
+CommandStorageManagerTestHelper::GetBackendTaskRunner() {
+ return command_storage_manager_->backend_task_runner_;
+}
+
+} // namespace sessions
diff --git a/chromium/components/sessions/core/base_session_service_test_helper.h b/chromium/components/sessions/core/command_storage_manager_test_helper.h
index 4e281f832b6..577ceb362f9 100644
--- a/chromium/components/sessions/core/base_session_service_test_helper.h
+++ b/chromium/components/sessions/core/command_storage_manager_test_helper.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_TEST_HELPER_H_
-#define COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_TEST_HELPER_H_
+#ifndef COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_TEST_HELPER_H_
+#define COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_TEST_HELPER_H_
#include <memory>
#include <vector>
@@ -14,32 +14,34 @@
namespace sessions {
class SessionCommand;
-class BaseSessionService;
+class CommandStorageManager;
-class BaseSessionServiceTestHelper {
+class CommandStorageManagerTestHelper {
public:
- explicit BaseSessionServiceTestHelper(
- BaseSessionService* base_session_service_);
- ~BaseSessionServiceTestHelper();
+ explicit CommandStorageManagerTestHelper(
+ CommandStorageManager* command_storage_manager_);
+ ~CommandStorageManagerTestHelper() = default;
// This posts the task to the SequencedWorkerPool, or run immediately
// if the SequencedWorkerPool has been shutdown.
void RunTaskOnBackendThread(const base::Location& from_here,
- const base::Closure& task);
+ base::OnceClosure task);
// Returns true if any commands got processed yet - saved or queued.
bool ProcessedAnyCommands();
// Read the last session commands directly from file.
- bool ReadLastSessionCommands(
+ void ReadLastSessionCommands(
std::vector<std::unique_ptr<SessionCommand>>* commands);
+ scoped_refptr<base::SequencedTaskRunner> GetBackendTaskRunner();
+
private:
- BaseSessionService* base_session_service_;
+ CommandStorageManager* command_storage_manager_;
- DISALLOW_COPY_AND_ASSIGN(BaseSessionServiceTestHelper);
+ DISALLOW_COPY_AND_ASSIGN(CommandStorageManagerTestHelper);
};
} // namespace sessions
-#endif // COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_TEST_HELPER_H_
+#endif // COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_TEST_HELPER_H_
diff --git a/chromium/components/sessions/core/live_tab_context.h b/chromium/components/sessions/core/live_tab_context.h
index d29dcc850be..f14affecf60 100644
--- a/chromium/components/sessions/core/live_tab_context.h
+++ b/chromium/components/sessions/core/live_tab_context.h
@@ -13,13 +13,11 @@
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/session_types.h"
#include "components/sessions/core/sessions_export.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/ui_base_types.h"
-namespace base {
-class Token;
-}
-
namespace gfx {
class Rect;
}
@@ -35,8 +33,6 @@ class PlatformSpecificTabData;
// is backed by an instance of the Browser class.
class SESSIONS_EXPORT LiveTabContext {
public:
- using TabGroupMetadata = sessions::TabGroupMetadata;
-
// TODO(blundell): Rename.
virtual void ShowBrowserWindow() = 0;
virtual SessionID GetSessionID() const = 0;
@@ -46,10 +42,17 @@ class SESSIONS_EXPORT LiveTabContext {
virtual LiveTab* GetLiveTabAt(int index) const = 0;
virtual LiveTab* GetActiveLiveTab() const = 0;
virtual bool IsTabPinned(int index) const = 0;
- virtual base::Optional<base::Token> GetTabGroupForTab(int index) const = 0;
+ virtual base::Optional<tab_groups::TabGroupId> GetTabGroupForTab(
+ int index) const = 0;
// Should not be called for |group| unless GetTabGroupForTab() returned
// |group|.
- virtual TabGroupMetadata GetTabGroupMetadata(base::Token group) const = 0;
+ virtual const tab_groups::TabGroupVisualData* GetVisualDataForGroup(
+ const tab_groups::TabGroupId& group) const = 0;
+ // Update |group|'s metadata. Should only be called for |group| if a tab has
+ // been restored in |group| via AddRestoredTab() or ReplaceRestoredTab().
+ virtual void SetVisualDataForGroup(
+ const tab_groups::TabGroupId& group,
+ const tab_groups::TabGroupVisualData& visual_data) = 0;
virtual const gfx::Rect GetRestoredBounds() const = 0;
virtual ui::WindowShowState GetRestoredState() const = 0;
virtual std::string GetWorkspace() const = 0;
@@ -62,8 +65,8 @@ class SESSIONS_EXPORT LiveTabContext {
int tab_index,
int selected_navigation,
const std::string& extension_app_id,
- base::Optional<base::Token> group,
- const TabGroupMetadata* group_metadata,
+ base::Optional<tab_groups::TabGroupId> group,
+ const tab_groups::TabGroupVisualData& group_visual_data,
bool select,
bool pin,
bool from_last_session,
@@ -75,7 +78,7 @@ class SESSIONS_EXPORT LiveTabContext {
// platform-specific data).
virtual LiveTab* ReplaceRestoredTab(
const std::vector<SerializedNavigationEntry>& navigations,
- base::Optional<base::Token> group,
+ base::Optional<tab_groups::TabGroupId> group,
int selected_navigation,
bool from_last_session,
const std::string& extension_app_id,
@@ -83,11 +86,6 @@ class SESSIONS_EXPORT LiveTabContext {
const std::string& user_agent_override) = 0;
virtual void CloseTab() = 0;
- // Update |group|'s metadata. Should only be called for |group| if a tab has
- // been restored in |group| via AddRestoredTab() or ReplaceRestoredTab().
- virtual void SetTabGroupMetadata(base::Token group,
- TabGroupMetadata metadata) = 0;
-
protected:
virtual ~LiveTabContext() {}
};
diff --git a/chromium/components/sessions/core/serialized_navigation_entry.cc b/chromium/components/sessions/core/serialized_navigation_entry.cc
index 2c8b41c9756..03bc935cf58 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry.cc
+++ b/chromium/components/sessions/core/serialized_navigation_entry.cc
@@ -161,7 +161,7 @@ void SerializedNavigationEntry::WriteToPickle(int max_size,
pickle->WriteInt(referrer_policy_);
pickle->WriteInt(extended_info_map_.size());
- for (const auto entry : extended_info_map_) {
+ for (const auto& entry : extended_info_map_) {
WriteStringToPickle(pickle, &bytes_written, max_size, entry.first);
WriteStringToPickle(pickle, &bytes_written, max_size, entry.second);
}
diff --git a/chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc b/chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc
index 8bb74fde0fe..96ee56d895e 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc
+++ b/chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc
@@ -16,10 +16,7 @@ namespace test_data {
const int kIndex = 3;
const int kUniqueID = 50;
-const GURL kReferrerURL = GURL("http://www.referrer.com");
const int kReferrerPolicy = 0;
-const GURL kURL = GURL("http://www.url.com");
-const GURL kVirtualURL = GURL("http://www.virtual-url.com");
const base::string16 kTitle = base::ASCIIToUTF16("title");
const std::string kEncodedPageState = "page state";
const ui::PageTransition kTransitionType =
@@ -29,15 +26,10 @@ const ui::PageTransition kTransitionType =
ui::PAGE_TRANSITION_CLIENT_REDIRECT);
const bool kHasPostData = true;
const int64_t kPostID = 100;
-const GURL kOriginalRequestURL = GURL("http://www.original-request.com");
const bool kIsOverridingUserAgent = true;
const base::Time kTimestamp =
base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(100);
-const GURL kFaviconURL = GURL("http://virtual-url.com/favicon.ico");
const int kHttpStatusCode = 404;
-const GURL kRedirectURL0 = GURL("http://go/redirect0");
-const GURL kRedirectURL1 = GURL("http://go/redirect1");
-const GURL kOtherURL = GURL("http://other.com");
const SerializedNavigationEntry::PasswordState kPasswordState =
SerializedNavigationEntry::HAS_PASSWORD_FIELD;
const std::string kExtendedInfoKey1 = "key 1";
@@ -49,6 +41,33 @@ const int64_t kParentTaskId = 1;
const int64_t kRootTaskId = 0;
const std::vector<int64_t> kChildrenTaskIds{3, 4, 5};
+// TODO(https://crbug.com/1042727): Fix test GURL scoping and remove this getter
+// function.
+GURL ReferrerUrl() {
+ return GURL("http://www.referrer.com");
+}
+GURL Url() {
+ return GURL("http://www.url.com");
+}
+GURL VirtualUrl() {
+ return GURL("http://www.virtual-url.com");
+}
+GURL OriginalRequestUrl() {
+ return GURL("http://www.original-request.com");
+}
+GURL FaviconUrl() {
+ return GURL("http://virtual-url.com/favicon.ico");
+}
+GURL RedirectUrl0() {
+ return GURL("http://go/redirect0");
+}
+GURL RedirectUrl1() {
+ return GURL("http://go/redirect1");
+}
+GURL OtherUrl() {
+ return GURL("http://other.com");
+}
+
} // namespace test_data
// static
@@ -74,18 +93,18 @@ SerializedNavigationEntryTestHelper::CreateNavigationForTest() {
SerializedNavigationEntry navigation;
navigation.index_ = test_data::kIndex;
navigation.unique_id_ = test_data::kUniqueID;
- navigation.referrer_url_ = test_data::kReferrerURL;
+ navigation.referrer_url_ = test_data::ReferrerUrl();
navigation.referrer_policy_ = test_data::kReferrerPolicy;
- navigation.virtual_url_ = test_data::kVirtualURL;
+ navigation.virtual_url_ = test_data::VirtualUrl();
navigation.title_ = test_data::kTitle;
navigation.encoded_page_state_ = test_data::kEncodedPageState;
navigation.transition_type_ = test_data::kTransitionType;
navigation.has_post_data_ = test_data::kHasPostData;
navigation.post_id_ = test_data::kPostID;
- navigation.original_request_url_ = test_data::kOriginalRequestURL;
+ navigation.original_request_url_ = test_data::OriginalRequestUrl();
navigation.is_overriding_user_agent_ = test_data::kIsOverridingUserAgent;
navigation.timestamp_ = test_data::kTimestamp;
- navigation.favicon_url_ = test_data::kFaviconURL;
+ navigation.favicon_url_ = test_data::FaviconUrl();
navigation.http_status_code_ = test_data::kHttpStatusCode;
navigation.password_state_ = test_data::kPasswordState;
@@ -94,9 +113,9 @@ SerializedNavigationEntryTestHelper::CreateNavigationForTest() {
navigation.extended_info_map_[test_data::kExtendedInfoKey2] =
test_data::kExtendedInfoValue2;
- navigation.redirect_chain_.push_back(test_data::kRedirectURL0);
- navigation.redirect_chain_.push_back(test_data::kRedirectURL1);
- navigation.redirect_chain_.push_back(test_data::kVirtualURL);
+ navigation.redirect_chain_.push_back(test_data::RedirectUrl0());
+ navigation.redirect_chain_.push_back(test_data::RedirectUrl1());
+ navigation.redirect_chain_.push_back(test_data::VirtualUrl());
navigation.task_id_ = test_data::kTaskId;
navigation.parent_task_id_ = test_data::kParentTaskId;
navigation.root_task_id_ = test_data::kRootTaskId;
diff --git a/chromium/components/sessions/core/serialized_navigation_entry_test_helper.h b/chromium/components/sessions/core/serialized_navigation_entry_test_helper.h
index 9e8af07b6df..85ba2b549d7 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry_test_helper.h
+++ b/chromium/components/sessions/core/serialized_navigation_entry_test_helper.h
@@ -27,23 +27,15 @@ namespace test_data {
extern const int kIndex;
extern const int kUniqueID;
-extern const GURL kReferrerURL;
extern const int kReferrerPolicy;
-extern const GURL kURL;
-extern const GURL kVirtualURL;
extern const base::string16 kTitle;
extern const std::string kEncodedPageState;
extern const ui::PageTransition kTransitionType;
extern const bool kHasPostData;
extern const int64_t kPostID;
-extern const GURL kOriginalRequestURL;
extern const bool kIsOverridingUserAgent;
extern const base::Time kTimestamp;
-extern const GURL kFaviconURL;
extern const int kHttpStatusCode;
-extern const GURL kRedirectURL0;
-extern const GURL kRedirectURL1;
-extern const GURL kOtherURL;
extern const SerializedNavigationEntry::PasswordState kPasswordState;
extern const std::string kExtendedInfoKey1;
extern const std::string kExtendedInfoKey2;
@@ -54,6 +46,17 @@ extern const int64_t kRootTaskId;
extern const int64_t kTaskId;
extern const std::vector<int64_t> kChildrenTaskIds;
+// TODO(https://crbug.com/1042727): Fix test GURL scoping and remove this getter
+// function.
+GURL ReferrerUrl();
+GURL Url();
+GURL VirtualUrl();
+GURL OriginalRequestUrl();
+GURL FaviconUrl();
+GURL RedirectUrl0();
+GURL RedirectUrl1();
+GURL OtherUrl();
+
} // namespace test_data
// Set of test functions to manipulate a SerializedNavigationEntry.
diff --git a/chromium/components/sessions/core/serialized_navigation_entry_unittest.cc b/chromium/components/sessions/core/serialized_navigation_entry_unittest.cc
index b4803fce8a0..1ec6d1c7188 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry_unittest.cc
+++ b/chromium/components/sessions/core/serialized_navigation_entry_unittest.cc
@@ -65,14 +65,14 @@ TEST(SerializedNavigationEntryTest, Pickle) {
// Fields that are written to the pickle.
EXPECT_EQ(test_data::kIndex, new_navigation.index());
- EXPECT_EQ(test_data::kReferrerURL, new_navigation.referrer_url());
+ EXPECT_EQ(test_data::ReferrerUrl(), new_navigation.referrer_url());
EXPECT_EQ(test_data::kReferrerPolicy, new_navigation.referrer_policy());
- EXPECT_EQ(test_data::kVirtualURL, new_navigation.virtual_url());
+ EXPECT_EQ(test_data::VirtualUrl(), new_navigation.virtual_url());
EXPECT_EQ(test_data::kTitle, new_navigation.title());
EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
new_navigation.transition_type(), test_data::kTransitionType));
EXPECT_EQ(test_data::kHasPostData, new_navigation.has_post_data());
- EXPECT_EQ(test_data::kOriginalRequestURL,
+ EXPECT_EQ(test_data::OriginalRequestUrl(),
new_navigation.original_request_url());
EXPECT_EQ(test_data::kIsOverridingUserAgent,
new_navigation.is_overriding_user_agent());
diff --git a/chromium/components/sessions/core/session_backend.cc b/chromium/components/sessions/core/session_backend.cc
deleted file mode 100644
index 41936d7230c..00000000000
--- a/chromium/components/sessions/core/session_backend.cc
+++ /dev/null
@@ -1,385 +0,0 @@
-// Copyright 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 "components/sessions/core/session_backend.h"
-
-#include <stdint.h>
-#include <limits>
-#include <utility>
-
-#include "base/files/file.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/threading/thread_restrictions.h"
-#include "build/build_config.h"
-
-using base::TimeTicks;
-
-namespace sessions {
-
-// File version number.
-static const int32_t kFileCurrentVersion = 1;
-
-// The signature at the beginning of the file = SSNS (Sessions).
-static const int32_t kFileSignature = 0x53534E53;
-
-namespace {
-
-// The file header is the first bytes written to the file,
-// and is used to identify the file as one written by us.
-struct FileHeader {
- int32_t signature;
- int32_t version;
-};
-
-// SessionFileReader ----------------------------------------------------------
-
-// SessionFileReader is responsible for reading the set of SessionCommands that
-// describe a Session back from a file. SessionFileRead does minimal error
-// checking on the file (pretty much only that the header is valid).
-
-class SessionFileReader {
- public:
- typedef sessions::SessionCommand::id_type id_type;
- typedef sessions::SessionCommand::size_type size_type;
-
- explicit SessionFileReader(const base::FilePath& path)
- : errored_(false),
- buffer_(SessionBackend::kFileReadBufferSize, 0),
- buffer_position_(0),
- available_count_(0) {
- file_.reset(new base::File(
- path, base::File::FLAG_OPEN | base::File::FLAG_READ));
- }
- // Reads the contents of the file specified in the constructor, returning
- // true on success, and filling up |commands| with commands.
- bool Read(std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
-
- private:
- // Reads a single command, returning it. A return value of NULL indicates
- // either there are no commands, or there was an error. Use errored_ to
- // distinguish the two. If NULL is returned, and there is no error, it means
- // the end of file was successfully reached.
- std::unique_ptr<sessions::SessionCommand> ReadCommand();
-
- // Shifts the unused portion of buffer_ to the beginning and fills the
- // remaining portion with data from the file. Returns false if the buffer
- // couldn't be filled. A return value of false only signals an error if
- // errored_ is set to true.
- bool FillBuffer();
-
- // Whether an error condition has been detected (
- bool errored_;
-
- // As we read from the file, data goes here.
- std::string buffer_;
-
- // The file.
- std::unique_ptr<base::File> file_;
-
- // Position in buffer_ of the data.
- size_t buffer_position_;
-
- // Number of available bytes; relative to buffer_position_.
- size_t available_count_;
-
- DISALLOW_COPY_AND_ASSIGN(SessionFileReader);
-};
-
-bool SessionFileReader::Read(
- std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
- if (!file_->IsValid())
- return false;
- FileHeader header;
- int read_count;
- read_count = file_->ReadAtCurrentPos(reinterpret_cast<char*>(&header),
- sizeof(header));
- if (read_count != sizeof(header) || header.signature != kFileSignature ||
- header.version != kFileCurrentVersion)
- return false;
-
- std::vector<std::unique_ptr<sessions::SessionCommand>> read_commands;
- for (std::unique_ptr<sessions::SessionCommand> command = ReadCommand();
- command && !errored_; command = ReadCommand())
- read_commands.push_back(std::move(command));
- if (!errored_)
- read_commands.swap(*commands);
- return !errored_;
-}
-
-std::unique_ptr<sessions::SessionCommand> SessionFileReader::ReadCommand() {
- // Make sure there is enough in the buffer for the size of the next command.
- if (available_count_ < sizeof(size_type)) {
- if (!FillBuffer())
- return nullptr;
- if (available_count_ < sizeof(size_type)) {
- VLOG(1) << "SessionFileReader::ReadCommand, file incomplete";
- // Still couldn't read a valid size for the command, assume write was
- // incomplete and return NULL.
- return nullptr;
- }
- }
- // Get the size of the command.
- size_type command_size;
- memcpy(&command_size, &(buffer_[buffer_position_]), sizeof(command_size));
- buffer_position_ += sizeof(command_size);
- available_count_ -= sizeof(command_size);
-
- if (command_size == 0) {
- VLOG(1) << "SessionFileReader::ReadCommand, empty command";
- // Empty command. Shouldn't happen if write was successful, fail.
- return nullptr;
- }
-
- // Make sure buffer has the complete contents of the command.
- if (command_size > available_count_) {
- if (command_size > buffer_.size())
- buffer_.resize((command_size / 1024 + 1) * 1024, 0);
- if (!FillBuffer() || command_size > available_count_) {
- // Again, assume the file was ok, and just the last chunk was lost.
- VLOG(1) << "SessionFileReader::ReadCommand, last chunk lost";
- return nullptr;
- }
- }
- const id_type command_id = buffer_[buffer_position_];
- // NOTE: command_size includes the size of the id, which is not part of
- // the contents of the SessionCommand.
- std::unique_ptr<sessions::SessionCommand> command =
- std::make_unique<sessions::SessionCommand>(
- command_id, command_size - sizeof(id_type));
- if (command_size > sizeof(id_type)) {
- memcpy(command->contents(),
- &(buffer_[buffer_position_ + sizeof(id_type)]),
- command_size - sizeof(id_type));
- }
- buffer_position_ += command_size;
- available_count_ -= command_size;
- return command;
-}
-
-bool SessionFileReader::FillBuffer() {
- if (available_count_ > 0 && buffer_position_ > 0) {
- // Shift buffer to beginning.
- memmove(&(buffer_[0]), &(buffer_[buffer_position_]), available_count_);
- }
- buffer_position_ = 0;
- DCHECK(buffer_position_ + available_count_ < buffer_.size());
- int to_read = static_cast<int>(buffer_.size() - available_count_);
- int read_count = file_->ReadAtCurrentPos(&(buffer_[available_count_]),
- to_read);
- if (read_count < 0) {
- errored_ = true;
- return false;
- }
- if (read_count == 0)
- return false;
- available_count_ += read_count;
- return true;
-}
-
-} // namespace
-
-// SessionBackend -------------------------------------------------------------
-
-// File names (current and previous) for a type of TAB.
-static const char* kCurrentTabSessionFileName = "Current Tabs";
-static const char* kLastTabSessionFileName = "Last Tabs";
-
-// File names (current and previous) for a type of SESSION.
-static const char* kCurrentSessionFileName = "Current Session";
-static const char* kLastSessionFileName = "Last Session";
-
-// static
-const int SessionBackend::kFileReadBufferSize = 1024;
-
-SessionBackend::SessionBackend(sessions::BaseSessionService::SessionType type,
- const base::FilePath& path_to_dir)
- : type_(type),
- path_to_dir_(path_to_dir),
- last_session_valid_(false),
- inited_(false),
- empty_file_(true) {
- // NOTE: this is invoked on the main thread, don't do file access here.
-}
-
-void SessionBackend::Init() {
- if (inited_)
- return;
-
- inited_ = true;
-
- // Create the directory for session info.
- base::CreateDirectory(path_to_dir_);
-
- MoveCurrentSessionToLastSession();
-}
-
-void SessionBackend::AppendCommands(
- std::vector<std::unique_ptr<sessions::SessionCommand>> commands,
- bool reset_first) {
- Init();
- // Make sure and check current_session_file_, if opening the file failed
- // current_session_file_ will be NULL.
- if ((reset_first && !empty_file_) || !current_session_file_ ||
- !current_session_file_->IsValid()) {
- ResetFile();
- }
- // Need to check current_session_file_ again, ResetFile may fail.
- if (current_session_file_.get() && current_session_file_->IsValid() &&
- !AppendCommandsToFile(current_session_file_.get(), commands)) {
- current_session_file_.reset(nullptr);
- }
- empty_file_ = false;
-}
-
-void SessionBackend::ReadLastSessionCommands(
- const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
- const sessions::BaseSessionService::GetCommandsCallback& callback) {
- if (is_canceled.Run())
- return;
-
- Init();
-
- std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
- ReadLastSessionCommandsImpl(&commands);
- callback.Run(std::move(commands));
-}
-
-bool SessionBackend::ReadLastSessionCommandsImpl(
- std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
- Init();
- SessionFileReader file_reader(GetLastSessionPath());
- return file_reader.Read(commands);
-}
-
-void SessionBackend::DeleteLastSession() {
- Init();
- base::DeleteFile(GetLastSessionPath(), false);
-}
-
-void SessionBackend::MoveCurrentSessionToLastSession() {
- Init();
- current_session_file_.reset(nullptr);
-
- const base::FilePath current_session_path = GetCurrentSessionPath();
- const base::FilePath last_session_path = GetLastSessionPath();
- if (base::PathExists(last_session_path))
- base::DeleteFile(last_session_path, false);
- if (base::PathExists(current_session_path))
- last_session_valid_ = base::Move(current_session_path, last_session_path);
-
- if (base::PathExists(current_session_path))
- base::DeleteFile(current_session_path, false);
-
- // Create and open the file for the current session.
- ResetFile();
-}
-
-bool SessionBackend::ReadCurrentSessionCommandsImpl(
- std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
- Init();
- SessionFileReader file_reader(GetCurrentSessionPath());
- return file_reader.Read(commands);
-}
-
-bool SessionBackend::AppendCommandsToFile(
- base::File* file,
- const std::vector<std::unique_ptr<sessions::SessionCommand>>& commands) {
- for (auto i = commands.begin(); i != commands.end(); ++i) {
- int wrote;
- const size_type content_size = static_cast<size_type>((*i)->size());
- const size_type total_size = content_size + sizeof(id_type);
- wrote = file->WriteAtCurrentPos(reinterpret_cast<const char*>(&total_size),
- sizeof(total_size));
- if (wrote != sizeof(total_size)) {
- NOTREACHED() << "error writing";
- return false;
- }
- id_type command_id = (*i)->id();
- wrote = file->WriteAtCurrentPos(reinterpret_cast<char*>(&command_id),
- sizeof(command_id));
- if (wrote != sizeof(command_id)) {
- NOTREACHED() << "error writing";
- return false;
- }
- if (content_size > 0) {
- wrote = file->WriteAtCurrentPos(reinterpret_cast<char*>((*i)->contents()),
- content_size);
- if (wrote != content_size) {
- NOTREACHED() << "error writing";
- return false;
- }
- }
- }
-#if defined(OS_CHROMEOS)
- file->Flush();
-#endif
- return true;
-}
-
-SessionBackend::~SessionBackend() {
- if (current_session_file_) {
- // Destructor performs file IO because file is open in sync mode.
- // crbug.com/112512.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- current_session_file_.reset();
- }
-}
-
-void SessionBackend::ResetFile() {
- DCHECK(inited_);
- if (current_session_file_) {
- // File is already open, truncate it. We truncate instead of closing and
- // reopening to avoid the possibility of scanners locking the file out
- // from under us once we close it. If truncation fails, we'll try to
- // recreate.
- const int header_size = static_cast<int>(sizeof(FileHeader));
- if (current_session_file_->Seek(
- base::File::FROM_BEGIN, header_size) != header_size ||
- !current_session_file_->SetLength(header_size))
- current_session_file_.reset(nullptr);
- }
- if (!current_session_file_)
- current_session_file_.reset(OpenAndWriteHeader(GetCurrentSessionPath()));
- empty_file_ = true;
-}
-
-base::File* SessionBackend::OpenAndWriteHeader(const base::FilePath& path) {
- DCHECK(!path.empty());
- std::unique_ptr<base::File> file(new base::File(
- path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
- base::File::FLAG_EXCLUSIVE_WRITE |
- base::File::FLAG_EXCLUSIVE_READ));
- if (!file->IsValid())
- return nullptr;
- FileHeader header;
- header.signature = kFileSignature;
- header.version = kFileCurrentVersion;
- int wrote = file->WriteAtCurrentPos(reinterpret_cast<char*>(&header),
- sizeof(header));
- if (wrote != sizeof(header))
- return nullptr;
- return file.release();
-}
-
-base::FilePath SessionBackend::GetLastSessionPath() {
- base::FilePath path = path_to_dir_;
- if (type_ == sessions::BaseSessionService::TAB_RESTORE)
- path = path.AppendASCII(kLastTabSessionFileName);
- else
- path = path.AppendASCII(kLastSessionFileName);
- return path;
-}
-
-base::FilePath SessionBackend::GetCurrentSessionPath() {
- base::FilePath path = path_to_dir_;
- if (type_ == sessions::BaseSessionService::TAB_RESTORE)
- path = path.AppendASCII(kCurrentTabSessionFileName);
- else
- path = path.AppendASCII(kCurrentSessionFileName);
- return path;
-}
-
-} // namespace sessions
diff --git a/chromium/components/sessions/core/session_backend.h b/chromium/components/sessions/core/session_backend.h
deleted file mode 100644
index 765ae5f08d7..00000000000
--- a/chromium/components/sessions/core/session_backend.h
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2011 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.
-
-#ifndef COMPONENTS_SESSIONS_CORE_SESSION_BACKEND_H_
-#define COMPONENTS_SESSIONS_CORE_SESSION_BACKEND_H_
-
-#include <stddef.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "components/sessions/core/base_session_service.h"
-#include "components/sessions/core/session_command.h"
-#include "components/sessions/core/sessions_export.h"
-
-namespace base {
-class File;
-}
-
-namespace sessions {
-// SessionBackend -------------------------------------------------------------
-
-// SessionBackend is the backend used by BaseSessionService. It is responsible
-// for maintaining two files:
-// . The current file, which is the file commands passed to AppendCommands
-// get written to.
-// . The last file. When created the current file is moved to the last
-// file.
-//
-// Each file contains an arbitrary set of commands supplied from
-// BaseSessionService. A command consists of a unique id and a stream of bytes.
-// SessionBackend does not use the id in anyway, that is used by
-// BaseSessionService.
-class SESSIONS_EXPORT SessionBackend
- : public base::RefCountedThreadSafe<SessionBackend> {
- public:
- typedef sessions::SessionCommand::id_type id_type;
- typedef sessions::SessionCommand::size_type size_type;
-
- // Initial size of the buffer used in reading the file. This is exposed
- // for testing.
- static const int kFileReadBufferSize;
-
- // Creates a SessionBackend. This method is invoked on the MAIN thread,
- // and does no IO. The real work is done from Init, which is invoked on
- // the file thread.
- //
- // |path_to_dir| gives the path the files are written two, and |type|
- // indicates which service is using this backend. |type| is used to determine
- // the name of the files to use as well as for logging.
- SessionBackend(sessions::BaseSessionService::SessionType type,
- const base::FilePath& path_to_dir);
-
- // Moves the current file to the last file, and recreates the current file.
- //
- // NOTE: this is invoked before every command, and does nothing if we've
- // already Init'ed.
- void Init();
- bool inited() const { return inited_; }
-
- // Appends the specified commands to the current file. If reset_first is
- // true the the current file is recreated.
- void AppendCommands(
- std::vector<std::unique_ptr<sessions::SessionCommand>> commands,
- bool reset_first);
-
- // Invoked from the service to read the commands that make up the last
- // session, invokes ReadLastSessionCommandsImpl to do the work.
- void ReadLastSessionCommands(
- const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
- const sessions::BaseSessionService::GetCommandsCallback& callback);
-
- // Reads the commands from the last file.
- //
- // On success, the read commands are added to commands.
- bool ReadLastSessionCommandsImpl(
- std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
-
- // Deletes the file containing the commands for the last session.
- void DeleteLastSession();
-
- // Moves the current session to the last and resets the current. This is
- // called during startup and if the user launchs the app and no tabbed
- // browsers are running.
- void MoveCurrentSessionToLastSession();
-
- // Reads the commands from the current file.
- //
- // On success, the read commands are added to commands. It is up to the
- // caller to delete the commands.
- bool ReadCurrentSessionCommandsImpl(
- std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
-
- private:
- friend class base::RefCountedThreadSafe<SessionBackend>;
-
- ~SessionBackend();
-
- // If current_session_file_ is open, it is truncated so that it is essentially
- // empty (only contains the header). If current_session_file_ isn't open, it
- // is is opened and the header is written to it. After this
- // current_session_file_ contains no commands.
- // NOTE: current_session_file_ may be NULL if the file couldn't be opened or
- // the header couldn't be written.
- void ResetFile();
-
- // Opens the current file and writes the header. On success a handle to
- // the file is returned.
- base::File* OpenAndWriteHeader(const base::FilePath& path);
-
- // Appends the specified commands to the specified file.
- bool AppendCommandsToFile(
- base::File* file,
- const std::vector<std::unique_ptr<sessions::SessionCommand>>& commands);
-
- const sessions::BaseSessionService::SessionType type_;
-
- // Returns the path to the last file.
- base::FilePath GetLastSessionPath();
-
- // Returns the path to the current file.
- base::FilePath GetCurrentSessionPath();
-
- // Directory files are relative to.
- const base::FilePath path_to_dir_;
-
- // Whether the previous target file is valid.
- bool last_session_valid_;
-
- // Handle to the target file.
- std::unique_ptr<base::File> current_session_file_;
-
- // Whether we've inited. Remember, the constructor is run on the
- // Main thread, all others on the IO thread, hence lazy initialization.
- bool inited_;
-
- // If true, the file is empty (no commands have been added to it).
- bool empty_file_;
-
- DISALLOW_COPY_AND_ASSIGN(SessionBackend);
-};
-
-} // namespace sessions
-
-#endif // COMPONENTS_SESSIONS_CORE_SESSION_BACKEND_H_
diff --git a/chromium/components/sessions/core/session_command.cc b/chromium/components/sessions/core/session_command.cc
index 706233229be..2de11d95439 100644
--- a/chromium/components/sessions/core/session_command.cc
+++ b/chromium/components/sessions/core/session_command.cc
@@ -21,6 +21,14 @@ SessionCommand::SessionCommand(id_type id, const base::Pickle& pickle)
memcpy(contents(), pickle.data(), pickle.size());
}
+SessionCommand::size_type SessionCommand::GetSerializedSize() const {
+ const size_type additional_overhead = sizeof(id_type);
+ return std::min(size(),
+ static_cast<size_type>(std::numeric_limits<size_type>::max() -
+ additional_overhead)) +
+ additional_overhead;
+}
+
bool SessionCommand::GetPayload(void* dest, size_t count) const {
if (size() != count)
return false;
diff --git a/chromium/components/sessions/core/session_command.h b/chromium/components/sessions/core/session_command.h
index e5f766369ae..69a4bbb1e7a 100644
--- a/chromium/components/sessions/core/session_command.h
+++ b/chromium/components/sessions/core/session_command.h
@@ -57,6 +57,10 @@ class SESSIONS_EXPORT SessionCommand {
// Size of data.
size_type size() const { return static_cast<size_type>(contents_.size()); }
+ // The serialized format has overhead (the serialized format includes the
+ // id). This returns the size to use when serializing.
+ size_type GetSerializedSize() const;
+
// Convenience for extracting the data to a target. Returns false if
// count is not equal to the size of data this command contains.
bool GetPayload(void* dest, size_t count) const;
diff --git a/chromium/components/sessions/core/session_constants.cc b/chromium/components/sessions/core/session_constants.cc
index f1a26c79177..52eb6816978 100644
--- a/chromium/components/sessions/core/session_constants.cc
+++ b/chromium/components/sessions/core/session_constants.cc
@@ -6,6 +6,16 @@
namespace sessions {
+const base::FilePath::StringPieceType kCurrentTabSessionFileName =
+ FILE_PATH_LITERAL("Current Tabs");
+const base::FilePath::StringPieceType kLastTabSessionFileName =
+ FILE_PATH_LITERAL("Last Tabs");
+
+const base::FilePath::StringPieceType kCurrentSessionFileName =
+ FILE_PATH_LITERAL("Current Session");
+const base::FilePath::StringPieceType kLastSessionFileName =
+ FILE_PATH_LITERAL("Last Session");
+
const int gMaxPersistNavigationCount = 6;
} // namespace sessions
diff --git a/chromium/components/sessions/core/session_constants.h b/chromium/components/sessions/core/session_constants.h
index a81c4d3bcbf..5932a12a3d8 100644
--- a/chromium/components/sessions/core/session_constants.h
+++ b/chromium/components/sessions/core/session_constants.h
@@ -5,10 +5,23 @@
#ifndef COMPONENTS_SESSIONS_CORE_SESSION_CONSTANTS_H_
#define COMPONENTS_SESSIONS_CORE_SESSION_CONSTANTS_H_
+#include "base/files/file_path.h"
#include "components/sessions/core/sessions_export.h"
namespace sessions {
+// File names (current and previous) for a type of TAB.
+extern const base::FilePath::StringPieceType SESSIONS_EXPORT
+ kCurrentTabSessionFileName;
+extern const base::FilePath::StringPieceType SESSIONS_EXPORT
+ kLastTabSessionFileName;
+
+// File names (current and previous) for a type of SESSION.
+extern const base::FilePath::StringPieceType SESSIONS_EXPORT
+ kCurrentSessionFileName;
+extern const base::FilePath::StringPieceType SESSIONS_EXPORT
+ kLastSessionFileName;
+
// The maximum number of navigation entries in each direction to persist.
extern const int SESSIONS_EXPORT gMaxPersistNavigationCount;
diff --git a/chromium/components/sessions/core/session_service_commands.cc b/chromium/components/sessions/core/session_service_commands.cc
index 9eaee1a40e2..f7c5bf3cb24 100644
--- a/chromium/components/sessions/core/session_service_commands.cc
+++ b/chromium/components/sessions/core/session_service_commands.cc
@@ -11,13 +11,17 @@
#include <vector>
#include "base/containers/flat_set.h"
+#include "base/guid.h"
#include "base/memory/ptr_util.h"
#include "base/pickle.h"
#include "base/token.h"
#include "components/sessions/core/base_session_service_commands.h"
-#include "components/sessions/core/base_session_service_delegate.h"
+#include "components/sessions/core/command_storage_manager_delegate.h"
#include "components/sessions/core/session_command.h"
#include "components/sessions/core/session_types.h"
+#include "components/tab_groups/tab_group_color.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
namespace sessions {
@@ -60,6 +64,8 @@ static const SessionCommand::id_type kCommandSetWindowWorkspace2 = 23;
static const SessionCommand::id_type kCommandTabNavigationPathPruned = 24;
static const SessionCommand::id_type kCommandSetTabGroup = 25;
static const SessionCommand::id_type kCommandSetTabGroupMetadata = 26;
+static const SessionCommand::id_type kCommandSetTabGroupMetadata2 = 27;
+static const SessionCommand::id_type kCommandSetTabGuid = 28;
namespace {
@@ -218,8 +224,8 @@ void UpdateSelectedTabIndex(
using IdToSessionTab = std::map<SessionID, std::unique_ptr<SessionTab>>;
using IdToSessionWindow = std::map<SessionID, std::unique_ptr<SessionWindow>>;
-using TokenToSessionTabGroup =
- std::map<base::Token, std::unique_ptr<SessionTabGroup>>;
+using GroupIdToSessionTabGroup =
+ std::map<tab_groups::TabGroupId, std::unique_ptr<SessionTabGroup>>;
// Returns the window in windows with the specified id. If a window does
// not exist, one is created.
@@ -248,12 +254,12 @@ SessionTab* GetTab(SessionID tab_id, IdToSessionTab* tabs) {
return i->second.get();
}
-SessionTabGroup* GetTabGroup(base::Token group_id,
- TokenToSessionTabGroup* groups) {
+SessionTabGroup* GetTabGroup(tab_groups::TabGroupId group_id,
+ GroupIdToSessionTabGroup* groups) {
DCHECK(groups);
// For |group_id|, insert a corresponding group entry or get the existing one.
auto result = groups->emplace(group_id, nullptr);
- TokenToSessionTabGroup::iterator it = result.first;
+ GroupIdToSessionTabGroup::iterator it = result.first;
if (result.second)
it->second = std::make_unique<SessionTabGroup>(group_id);
return it->second.get();
@@ -328,7 +334,7 @@ void SortTabsBasedOnVisualOrderAndClear(
// Adds tabs to their parent window based on the tab's window_id. This
// ignores tabs with no navigations.
void AddTabsToWindows(IdToSessionTab* tabs,
- TokenToSessionTabGroup* tab_groups,
+ GroupIdToSessionTabGroup* tab_groups,
IdToSessionWindow* windows) {
DVLOG(1) << "AddTabsToWindows";
DVLOG(1) << "Tabs " << tabs->size() << ", groups " << tab_groups->size()
@@ -364,14 +370,14 @@ void AddTabsToWindows(IdToSessionTab* tabs,
for (auto& window_pair : *windows) {
SessionWindow* window = window_pair.second.get();
- base::flat_set<base::Token> groups_in_current_window;
+ base::flat_set<tab_groups::TabGroupId> groups_in_current_window;
for (const auto& tab : window->tabs) {
if (tab->group.has_value())
groups_in_current_window.insert(tab->group.value());
}
// Move corresponding SessionTabGroup entries into SessionWindow.
- for (const base::Token& group_id : groups_in_current_window) {
+ for (const tab_groups::TabGroupId& group_id : groups_in_current_window) {
auto it = tab_groups->find(group_id);
if (it == tab_groups->end()) {
window->tab_groups.push_back(
@@ -424,7 +430,7 @@ void ProcessTabNavigationPathPrunedCommand(
bool CreateTabsAndWindows(
const std::vector<std::unique_ptr<SessionCommand>>& data,
IdToSessionTab* tabs,
- TokenToSessionTabGroup* tab_groups,
+ GroupIdToSessionTabGroup* tab_groups,
IdToSessionWindow* windows,
SessionID* active_window_id) {
// If the file is corrupt (command with wrong size, or unknown command), we
@@ -618,25 +624,50 @@ bool CreateTabsAndWindows(
const base::Token token(payload.maybe_group.id_high,
payload.maybe_group.id_low);
session_tab->group =
- payload.has_group ? base::make_optional(token) : base::nullopt;
+ payload.has_group ? base::make_optional(
+ tab_groups::TabGroupId::FromRawToken(token))
+ : base::nullopt;
break;
}
- case kCommandSetTabGroupMetadata: {
+ case kCommandSetTabGroupMetadata:
+ case kCommandSetTabGroupMetadata2: {
std::unique_ptr<base::Pickle> pickle = command->PayloadAsPickle();
base::PickleIterator iter(*pickle);
- base::Optional<base::Token> group_id = ReadTokenFromPickle(&iter);
- if (!group_id.has_value())
+ base::Optional<base::Token> group_token = ReadTokenFromPickle(&iter);
+ if (!group_token.has_value())
return true;
- SessionTabGroup* group = GetTabGroup(group_id.value(), tab_groups);
+ SessionTabGroup* group = GetTabGroup(
+ tab_groups::TabGroupId::FromRawToken(group_token.value()),
+ tab_groups);
- if (!iter.ReadString16(&group->metadata.title))
+ base::string16 title;
+ if (!iter.ReadString16(&title))
return true;
- if (!iter.ReadUInt32(&group->metadata.color))
- return true;
+ if (command->id() == kCommandSetTabGroupMetadata) {
+ SkColor color;
+ if (!iter.ReadUInt32(&color))
+ return true;
+
+ // crrev.com/c/1968039 changes the color of a tab group from a SkColor
+ // to a TabGroupColorId. Here we ignore the old SkColor and assign the
+ // default TabGroupColorId because the fallback is acceptable while
+ // the tab groups feature isn't yet launched. Once it is,
+ // kCommandSetTabGroupMetadata will be deprecated in favor of
+ // kCommandSetTabGroupMetadata2, which properly restores
+ // TabGroupColorIds.
+ group->visual_data = tab_groups::TabGroupVisualData(
+ title, tab_groups::TabGroupColorId::kGrey);
+ } else {
+ uint32_t color_int;
+ if (!iter.ReadUInt32(&color_int))
+ return true;
+
+ group->visual_data = tab_groups::TabGroupVisualData(title, color_int);
+ }
break;
}
@@ -741,9 +772,21 @@ bool CreateTabsAndWindows(
break;
}
+ case kCommandSetTabGuid: {
+ std::unique_ptr<base::Pickle> pickle(command->PayloadAsPickle());
+ base::PickleIterator it(*pickle);
+ SessionID::id_type tab_id = -1;
+ std::string guid;
+ if (!it.ReadInt(&tab_id) || !it.ReadString(&guid) ||
+ !base::IsValidGUID(guid)) {
+ DVLOG(1) << "Failed reading command " << command->id();
+ return true;
+ }
+ GetTab(SessionID::FromSerializedValue(tab_id), tabs)->guid = guid;
+ 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;
}
@@ -843,27 +886,26 @@ std::unique_ptr<SessionCommand> CreateSetWindowTypeCommand(
std::unique_ptr<SessionCommand> CreateTabGroupCommand(
const SessionID& tab_id,
- base::Optional<base::Token> group) {
+ base::Optional<tab_groups::TabGroupId> group) {
TabGroupPayload payload = {0};
payload.tab_id = tab_id.id();
if (group.has_value()) {
- DCHECK(!group.value().is_zero());
- payload.maybe_group.id_high = group.value().high();
- payload.maybe_group.id_low = group.value().low();
+ DCHECK(!group.value().token().is_zero());
+ payload.maybe_group.id_high = group.value().token().high();
+ payload.maybe_group.id_low = group.value().token().low();
payload.has_group = true;
}
return CreateSessionCommandForPayload(kCommandSetTabGroup, payload);
}
std::unique_ptr<SessionCommand> CreateTabGroupMetadataUpdateCommand(
- const base::Token& group,
- const base::string16& title,
- SkColor color) {
+ const tab_groups::TabGroupId group,
+ const tab_groups::TabGroupVisualData* visual_data) {
base::Pickle pickle;
- WriteTokenToPickle(&pickle, group);
- pickle.WriteString16(title);
- pickle.WriteUInt32(color);
- return std::make_unique<SessionCommand>(kCommandSetTabGroupMetadata, pickle);
+ WriteTokenToPickle(&pickle, group.token());
+ pickle.WriteString16(visual_data->title());
+ pickle.WriteUInt32(static_cast<int>(visual_data->color()));
+ return std::make_unique<SessionCommand>(kCommandSetTabGroupMetadata2, pickle);
}
std::unique_ptr<SessionCommand> CreatePinnedStateCommand(
@@ -950,7 +992,16 @@ std::unique_ptr<SessionCommand> CreateSetWindowAppNameCommand(
app_name);
}
-bool ReplacePendingCommand(BaseSessionService* base_session_service,
+std::unique_ptr<SessionCommand> CreateSetTabGuidCommand(
+ const SessionID& tab_id,
+ const std::string& guid) {
+ base::Pickle pickle;
+ pickle.WriteInt(tab_id.id());
+ pickle.WriteString(guid);
+ return std::make_unique<SessionCommand>(kCommandSetTabGuid, pickle);
+}
+
+bool ReplacePendingCommand(CommandStorageManager* command_storage_manager,
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!
@@ -958,8 +1009,8 @@ bool ReplacePendingCommand(BaseSessionService* base_session_service,
(*command)->id() != kCommandSetActiveWindow) {
return false;
}
- for (auto i = base_session_service->pending_commands().rbegin();
- i != base_session_service->pending_commands().rend(); ++i) {
+ for (auto i = command_storage_manager->pending_commands().rbegin();
+ i != command_storage_manager->pending_commands().rend(); ++i) {
SessionCommand* existing_command = i->get();
if ((*command)->id() == kCommandUpdateTabNavigation &&
existing_command->id() == kCommandUpdateTabNavigation) {
@@ -991,16 +1042,16 @@ bool ReplacePendingCommand(BaseSessionService* base_session_service,
// 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));
+ command_storage_manager->EraseCommand((i.base() - 1)->get());
+ command_storage_manager->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)));
+ command_storage_manager->SwapCommand(existing_command,
+ (std::move(*command)));
return true;
}
}
@@ -1017,7 +1068,7 @@ void RestoreSessionFromCommands(
std::vector<std::unique_ptr<SessionWindow>>* valid_windows,
SessionID* active_window_id) {
IdToSessionTab tabs;
- TokenToSessionTabGroup tab_groups;
+ GroupIdToSessionTabGroup tab_groups;
IdToSessionWindow windows;
DVLOG(1) << "RestoreSessionFromCommands " << commands.size();
diff --git a/chromium/components/sessions/core/session_service_commands.h b/chromium/components/sessions/core/session_service_commands.h
index 5715d6e5558..815f2266cb2 100644
--- a/chromium/components/sessions/core/session_service_commands.h
+++ b/chromium/components/sessions/core/session_service_commands.h
@@ -14,9 +14,11 @@
#include "base/optional.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/token.h"
-#include "components/sessions/core/base_session_service.h"
+#include "components/sessions/core/command_storage_manager.h"
#include "components/sessions/core/session_types.h"
#include "components/sessions/core/sessions_export.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
#include "ui/base/ui_base_types.h"
namespace sessions {
@@ -25,7 +27,6 @@ class SessionCommand;
// The following functions create sequentialized change commands which are
// used to reconstruct the current/previous session state.
-// It is up to the caller to delete the returned SessionCommand* object.
SESSIONS_EXPORT std::unique_ptr<SessionCommand>
CreateSetSelectedTabInWindowCommand(const SessionID& window_id, int index);
SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateSetTabWindowCommand(
@@ -48,11 +49,11 @@ SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateSetWindowTypeCommand(
SessionWindow::WindowType type);
SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateTabGroupCommand(
const SessionID& tab_id,
- base::Optional<base::Token> group);
+ base::Optional<tab_groups::TabGroupId> group);
SESSIONS_EXPORT std::unique_ptr<SessionCommand>
-CreateTabGroupMetadataUpdateCommand(const base::Token& group,
- const base::string16& title,
- SkColor color);
+CreateTabGroupMetadataUpdateCommand(
+ const tab_groups::TabGroupId group,
+ const tab_groups::TabGroupVisualData* visual_data);
SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreatePinnedStateCommand(
const SessionID& tab_id,
bool is_pinned);
@@ -87,12 +88,16 @@ SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateSetWindowWorkspaceCommand(
const SessionID& window_id,
const std::string& workspace);
-// Searches for a pending command using |base_session_service| that can be
+SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateSetTabGuidCommand(
+ const SessionID& tab_id,
+ const std::string& guid);
+
+// Searches for a pending command using |command_storage_manager| that can be
// replaced with |command|. If one is found, pending command is removed, the
// command is added to the pending commands (taken ownership) and true is
// returned.
SESSIONS_EXPORT bool ReplacePendingCommand(
- BaseSessionService* base_session_service,
+ CommandStorageManager* command_storage_manager,
std::unique_ptr<SessionCommand>* command);
// Returns true if provided |command| either closes a window or a tab.
diff --git a/chromium/components/sessions/core/session_types.cc b/chromium/components/sessions/core/session_types.cc
index d0da8757478..13b1ea29665 100644
--- a/chromium/components/sessions/core/session_types.cc
+++ b/chromium/components/sessions/core/session_types.cc
@@ -7,6 +7,7 @@
#include <stddef.h>
#include "components/sessions/core/session_command.h"
+#include "components/tab_groups/tab_group_id.h"
namespace sessions {
@@ -22,9 +23,9 @@ SessionTab::SessionTab()
SessionTab::~SessionTab() {
}
-// SessionTab -----------------------------------------------------------------
+// SessionTabGroup -------------------------------------------------------------
-SessionTabGroup::SessionTabGroup(base::Token group_id) : group_id(group_id) {}
+SessionTabGroup::SessionTabGroup(const tab_groups::TabGroupId& id) : id(id) {}
SessionTabGroup::~SessionTabGroup() {}
diff --git a/chromium/components/sessions/core/session_types.h b/chromium/components/sessions/core/session_types.h
index 3522e912039..671a55387c8 100644
--- a/chromium/components/sessions/core/session_types.h
+++ b/chromium/components/sessions/core/session_types.h
@@ -18,6 +18,8 @@
#include "components/sessions/core/serialized_navigation_entry.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/sessions_export.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
#include "components/variations/variations_associated_data.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/ui_base_types.h"
@@ -70,7 +72,7 @@ struct SESSIONS_EXPORT SessionTab {
int current_navigation_index;
// The tab's group ID, if any.
- base::Optional<base::Token> group;
+ base::Optional<tab_groups::TabGroupId> group;
// True if the tab is pinned.
bool pinned;
@@ -95,35 +97,28 @@ struct SESSIONS_EXPORT SessionTab {
// For reassociating sessionStorage.
std::string session_storage_persistent_id;
+ // guid associated with the tab, may be empty.
+ std::string guid;
+
private:
DISALLOW_COPY_AND_ASSIGN(SessionTab);
};
-// Visual parameters of a tab group. This is shared between the session
-// service and the tab restore service.
-struct SESSIONS_EXPORT TabGroupMetadata {
- // A human-readable title for the group.
- base::string16 title;
-
- // An accent color used when displaying the group.
- SkColor color = gfx::kPlaceholderColor;
-};
-
// SessionTabGroup -----------------------------------------------------------
// Describes a tab group referenced by some SessionTab entry in its group
// field. By default, this is initialized with placeholder values that are
// visually obvious.
struct SESSIONS_EXPORT SessionTabGroup {
- explicit SessionTabGroup(base::Token group);
+ explicit SessionTabGroup(const tab_groups::TabGroupId& id);
~SessionTabGroup();
// Uniquely identifies this group. Initialized to zero and must be set be
// user. Unlike SessionID this should be globally unique, even across
// different sessions.
- base::Token group_id;
+ tab_groups::TabGroupId id;
- TabGroupMetadata metadata;
+ tab_groups::TabGroupVisualData visual_data;
private:
DISALLOW_COPY_AND_ASSIGN(SessionTabGroup);
@@ -142,7 +137,8 @@ struct SESSIONS_EXPORT SessionWindow {
TYPE_NORMAL = 0,
TYPE_POPUP = 1,
TYPE_APP = 2,
- TYPE_DEVTOOLS = 3
+ TYPE_DEVTOOLS = 3,
+ TYPE_APP_POPUP = 4,
};
// Identifier of the window.
diff --git a/chromium/components/sessions/core/snapshotting_command_storage_backend.cc b/chromium/components/sessions/core/snapshotting_command_storage_backend.cc
new file mode 100644
index 00000000000..8fb7dbdf2bd
--- /dev/null
+++ b/chromium/components/sessions/core/snapshotting_command_storage_backend.cc
@@ -0,0 +1,90 @@
+// Copyright 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 "components/sessions/core/snapshotting_command_storage_backend.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "components/sessions/core/session_constants.h"
+
+namespace sessions {
+namespace {
+
+base::FilePath GetCurrentFilePath(
+ SnapshottingCommandStorageManager::SessionType type,
+ const base::FilePath& base_path) {
+ base::FilePath path = base_path;
+ if (type == SnapshottingCommandStorageManager::TAB_RESTORE)
+ path = path.Append(kCurrentTabSessionFileName);
+ else
+ path = path.Append(kCurrentSessionFileName);
+ return path;
+}
+
+base::FilePath GetLastFilePath(
+ SnapshottingCommandStorageManager::SessionType type,
+ const base::FilePath& base_path) {
+ base::FilePath path = base_path;
+ if (type == SnapshottingCommandStorageManager::TAB_RESTORE)
+ path = path.Append(kLastTabSessionFileName);
+ else
+ path = path.Append(kLastSessionFileName);
+ return path;
+}
+
+} // namespace
+
+SnapshottingCommandStorageBackend::SnapshottingCommandStorageBackend(
+ scoped_refptr<base::SequencedTaskRunner> owning_task_runner,
+ SnapshottingCommandStorageManager::SessionType type,
+ const base::FilePath& path_to_dir)
+ : CommandStorageBackend(std::move(owning_task_runner),
+ GetCurrentFilePath(type, path_to_dir)),
+ last_file_path_(GetLastFilePath(type, path_to_dir)) {
+ // NOTE: this is invoked on the main thread, don't do file access here.
+}
+
+void SnapshottingCommandStorageBackend::ReadLastSessionCommands(
+ const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
+ GetCommandsCallback callback) {
+ if (is_canceled.Run())
+ return;
+
+ InitIfNecessary();
+
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
+ ReadCommandsFromFile(last_file_path_, std::vector<uint8_t>(), &commands);
+ std::move(callback).Run(std::move(commands));
+}
+
+void SnapshottingCommandStorageBackend::DeleteLastSession() {
+ InitIfNecessary();
+ base::DeleteFile(last_file_path_, false);
+}
+
+void SnapshottingCommandStorageBackend::MoveCurrentSessionToLastSession() {
+ InitIfNecessary();
+ CloseFile();
+
+ if (base::PathExists(last_file_path_))
+ base::DeleteFile(last_file_path_, false);
+ if (base::PathExists(path()))
+ last_session_valid_ = base::Move(path(), last_file_path_);
+
+ if (base::PathExists(path()))
+ base::DeleteFile(path(), false);
+
+ // Create and open the file for the current session.
+ TruncateFile();
+}
+
+void SnapshottingCommandStorageBackend::DoInit() {
+ MoveCurrentSessionToLastSession();
+}
+
+SnapshottingCommandStorageBackend::~SnapshottingCommandStorageBackend() =
+ default;
+
+} // namespace sessions
diff --git a/chromium/components/sessions/core/snapshotting_command_storage_backend.h b/chromium/components/sessions/core/snapshotting_command_storage_backend.h
new file mode 100644
index 00000000000..27f27811927
--- /dev/null
+++ b/chromium/components/sessions/core/snapshotting_command_storage_backend.h
@@ -0,0 +1,78 @@
+// Copyright 2020 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.
+
+#ifndef COMPONENTS_SESSIONS_CORE_SNAPSHOTTING_COMMAND_STORAGE_BACKEND_H_
+#define COMPONENTS_SESSIONS_CORE_SNAPSHOTTING_COMMAND_STORAGE_BACKEND_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/task/cancelable_task_tracker.h"
+#include "components/sessions/core/command_storage_backend.h"
+#include "components/sessions/core/sessions_export.h"
+#include "components/sessions/core/snapshotting_command_storage_manager.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace sessions {
+
+// Adds the ability to snapshot the current session file. The snapshot is
+// referred to as 'last'
+class SESSIONS_EXPORT SnapshottingCommandStorageBackend
+ : public CommandStorageBackend {
+ public:
+ // Creates a CommandStorageBackend. This method is invoked on the MAIN thread,
+ // and does no IO. The real work is done from Init, which is invoked on
+ // a background task runer.
+ //
+ // |path_to_dir| gives the path the files are written two, and |type|
+ // indicates which service is using this backend. |type| is used to determine
+ // the name of the files to use as well as for logging.
+ SnapshottingCommandStorageBackend(
+ scoped_refptr<base::SequencedTaskRunner> owning_task_runner,
+ SnapshottingCommandStorageManager::SessionType type,
+ const base::FilePath& path_to_dir);
+
+ SnapshottingCommandStorageBackend(const SnapshottingCommandStorageBackend&) =
+ delete;
+ SnapshottingCommandStorageBackend& operator=(
+ const SnapshottingCommandStorageBackend&) = delete;
+
+ // Runs |callback| with commands from the last session file.
+ void ReadLastSessionCommands(
+ const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
+ GetCommandsCallback callback);
+
+ // Deletes the file containing the commands for the last session.
+ void DeleteLastSession();
+
+ // Moves the current session file to the last session file. This is typically
+ // called during startup or if the user launches the app and no tabbed
+ // browsers are running.
+ void MoveCurrentSessionToLastSession();
+
+ protected:
+ void DoInit() override;
+
+ private:
+ friend class base::RefCountedDeleteOnSequence<
+ SnapshottingCommandStorageBackend>;
+ friend class base::DeleteHelper<SnapshottingCommandStorageBackend>;
+
+ ~SnapshottingCommandStorageBackend() override;
+
+ // Directory files are relative to.
+ const base::FilePath last_file_path_;
+
+ // Whether the previous target file is valid.
+ bool last_session_valid_ = false;
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_CORE_SNAPSHOTTING_COMMAND_STORAGE_BACKEND_H_
diff --git a/chromium/components/sessions/core/snapshotting_command_storage_manager.cc b/chromium/components/sessions/core/snapshotting_command_storage_manager.cc
new file mode 100644
index 00000000000..8857b9800b0
--- /dev/null
+++ b/chromium/components/sessions/core/snapshotting_command_storage_manager.cc
@@ -0,0 +1,70 @@
+// Copyright 2020 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/snapshotting_command_storage_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "components/sessions/core/snapshotting_command_storage_backend.h"
+
+namespace sessions {
+
+SnapshottingCommandStorageManager::SnapshottingCommandStorageManager(
+ SessionType type,
+ const base::FilePath& path,
+ CommandStorageManagerDelegate* delegate)
+ : CommandStorageManager(
+ base::MakeRefCounted<SnapshottingCommandStorageBackend>(
+ CreateDefaultBackendTaskRunner(),
+ type,
+ path),
+ delegate) {}
+
+SnapshottingCommandStorageManager::~SnapshottingCommandStorageManager() =
+ default;
+
+void SnapshottingCommandStorageManager::MoveCurrentSessionToLastSession() {
+ Save();
+ backend_task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ base::BindOnce(
+ &SnapshottingCommandStorageBackend::MoveCurrentSessionToLastSession,
+ GetSnapshottingBackend()));
+}
+
+void SnapshottingCommandStorageManager::DeleteLastSession() {
+ backend_task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ base::BindOnce(&SnapshottingCommandStorageBackend::DeleteLastSession,
+ GetSnapshottingBackend()));
+}
+
+base::CancelableTaskTracker::TaskId
+SnapshottingCommandStorageManager::ScheduleGetLastSessionCommands(
+ GetCommandsCallback callback,
+ base::CancelableTaskTracker* tracker) {
+ base::CancelableTaskTracker::IsCanceledCallback is_canceled;
+ GetCommandsCallback backend_callback;
+ const base::CancelableTaskTracker::TaskId id = CreateCallbackForGetCommands(
+ tracker, std::move(callback), &is_canceled, &backend_callback);
+
+ backend_task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ base::BindOnce(
+ &SnapshottingCommandStorageBackend::ReadLastSessionCommands,
+ GetSnapshottingBackend(), is_canceled, std::move(backend_callback)));
+ return id;
+}
+
+SnapshottingCommandStorageBackend*
+SnapshottingCommandStorageManager::GetSnapshottingBackend() {
+ return static_cast<SnapshottingCommandStorageBackend*>(backend());
+}
+
+} // namespace sessions
diff --git a/chromium/components/sessions/core/snapshotting_command_storage_manager.h b/chromium/components/sessions/core/snapshotting_command_storage_manager.h
new file mode 100644
index 00000000000..bd795dee50c
--- /dev/null
+++ b/chromium/components/sessions/core/snapshotting_command_storage_manager.h
@@ -0,0 +1,63 @@
+// Copyright 2020 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.
+
+#ifndef COMPONENTS_SESSIONS_CORE_SNAPSHOTTING_COMMAND_STORAGE_MANAGER_H_
+#define COMPONENTS_SESSIONS_CORE_SNAPSHOTTING_COMMAND_STORAGE_MANAGER_H_
+
+#include <memory>
+
+#include "components/sessions/core/command_storage_manager.h"
+#include "components/sessions/core/sessions_export.h"
+
+namespace base {
+class CancelableTaskTracker;
+class FilePath;
+} // namespace base
+
+namespace sessions {
+class CommandStorageManagerDelegate;
+class SnapshottingCommandStorageBackend;
+
+// Adds snapshotting to CommandStorageManager. In this context a snapshot refers
+// to a copy of the current session file (in code, the snapshot is referred to
+// as the last session file). A snapshot is made at startup, but may also occur
+// at other points. For example, when Chrome transitions from no tabbed browsers
+// to having tabbed browsers a snapshot is made.
+class SESSIONS_EXPORT SnapshottingCommandStorageManager
+ : public CommandStorageManager {
+ public:
+ // Identifies the type of session service this is. This is used by the
+ // backend to determine the name of the files.
+ enum SessionType { SESSION_RESTORE, TAB_RESTORE };
+
+ SnapshottingCommandStorageManager(SessionType type,
+ const base::FilePath& path,
+ CommandStorageManagerDelegate* delegate);
+
+ SnapshottingCommandStorageManager(const SnapshottingCommandStorageManager&) =
+ delete;
+ SnapshottingCommandStorageManager& operator=(
+ const SnapshottingCommandStorageManager&) = delete;
+
+ ~SnapshottingCommandStorageManager() override;
+
+ // Moves the current session to the last session.
+ void MoveCurrentSessionToLastSession();
+
+ // Deletes the last session.
+ void DeleteLastSession();
+
+ // Uses the backend to load the last session commands from disc. |callback|
+ // gets called once the data has arrived.
+ base::CancelableTaskTracker::TaskId ScheduleGetLastSessionCommands(
+ GetCommandsCallback callback,
+ base::CancelableTaskTracker* tracker);
+
+ private:
+ SnapshottingCommandStorageBackend* GetSnapshottingBackend();
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_CORE_SNAPSHOTTING_COMMAND_STORAGE_MANAGER_H_
diff --git a/chromium/components/sessions/core/session_backend_unittest.cc b/chromium/components/sessions/core/snapshotting_session_backend_unittest.cc
index 9fc9ec48fe3..8c1673cb62d 100644
--- a/chromium/components/sessions/core/session_backend_unittest.cc
+++ b/chromium/components/sessions/core/snapshotting_session_backend_unittest.cc
@@ -2,21 +2,27 @@
// 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_backend.h"
+#include "components/sessions/core/snapshotting_command_storage_backend.h"
#include <stddef.h>
#include <utility>
+#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_refptr.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::MakeRefCounted;
+
namespace sessions {
namespace {
-typedef std::vector<std::unique_ptr<sessions::SessionCommand>> SessionCommands;
+using SessionCommands = std::vector<std::unique_ptr<sessions::SessionCommand>>;
struct TestData {
sessions::SessionCommand::id_type command_id;
@@ -34,9 +40,13 @@ std::unique_ptr<sessions::SessionCommand> CreateCommandFromData(
return command;
}
+bool IsCanceled() {
+ return false;
+}
+
} // namespace
-class SessionBackendTest : public testing::Test {
+class SnapshottingCommandStorageBackendTest : public testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -52,15 +62,32 @@ class SessionBackendTest : public testing::Test {
memcmp(command->contents(), data.data.c_str(), command->size()) == 0);
}
+ scoped_refptr<SnapshottingCommandStorageBackend> CreateBackend() {
+ return MakeRefCounted<SnapshottingCommandStorageBackend>(
+ task_environment_.GetMainThreadTaskRunner(),
+ sessions::SnapshottingCommandStorageManager::SESSION_RESTORE, path_);
+ }
+
+ void ReadLastSessionCommands(
+ SnapshottingCommandStorageBackend* backend,
+ std::vector<std::unique_ptr<SessionCommand>>* commands) {
+ backend->ReadLastSessionCommands(
+ base::BindRepeating(&IsCanceled),
+ base::BindLambdaForTesting(
+ [&commands](std::vector<std::unique_ptr<SessionCommand>> result) {
+ *commands = std::move(result);
+ }));
+ }
+
+ base::test::TaskEnvironment task_environment_;
// Path used in testing.
base::FilePath path_;
base::ScopedTempDir temp_dir_;
};
-TEST_F(SessionBackendTest, SimpleReadWrite) {
- scoped_refptr<SessionBackend> backend(
- new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE, path_));
- struct TestData data = { 1, "a" };
+TEST_F(SnapshottingCommandStorageBackendTest, SimpleReadWrite) {
+ scoped_refptr<SnapshottingCommandStorageBackend> backend = CreateBackend();
+ struct TestData data = {1, "a"};
SessionCommands commands;
commands.push_back(CreateCommandFromData(data));
backend->AppendCommands(std::move(commands), false);
@@ -68,9 +95,8 @@ TEST_F(SessionBackendTest, SimpleReadWrite) {
// Read it back in.
backend = nullptr;
- backend = new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE,
- path_);
- backend->ReadLastSessionCommandsImpl(&commands);
+ backend = CreateBackend();
+ ReadLastSessionCommands(backend.get(), &commands);
ASSERT_EQ(1U, commands.size());
AssertCommandEqualsData(data, commands[0].get());
@@ -78,43 +104,40 @@ TEST_F(SessionBackendTest, SimpleReadWrite) {
commands.clear();
backend = nullptr;
- backend = new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE,
- path_);
- backend->ReadLastSessionCommandsImpl(&commands);
+ backend = CreateBackend();
+ ReadLastSessionCommands(backend.get(), &commands);
ASSERT_EQ(0U, commands.size());
// Make sure we can delete.
backend->DeleteLastSession();
- backend->ReadLastSessionCommandsImpl(&commands);
+ ReadLastSessionCommands(backend.get(), &commands);
ASSERT_EQ(0U, commands.size());
}
-TEST_F(SessionBackendTest, RandomData) {
+TEST_F(SnapshottingCommandStorageBackendTest, RandomData) {
struct TestData data[] = {
- { 1, "a" },
- { 2, "ab" },
- { 3, "abc" },
- { 4, "abcd" },
- { 5, "abcde" },
- { 6, "abcdef" },
- { 7, "abcdefg" },
- { 8, "abcdefgh" },
- { 9, "abcdefghi" },
- { 10, "abcdefghij" },
- { 11, "abcdefghijk" },
- { 12, "abcdefghijkl" },
- { 13, "abcdefghijklm" },
+ {1, "a"},
+ {2, "ab"},
+ {3, "abc"},
+ {4, "abcd"},
+ {5, "abcde"},
+ {6, "abcdef"},
+ {7, "abcdefg"},
+ {8, "abcdefgh"},
+ {9, "abcdefghi"},
+ {10, "abcdefghij"},
+ {11, "abcdefghijk"},
+ {12, "abcdefghijkl"},
+ {13, "abcdefghijklm"},
};
for (size_t i = 0; i < base::size(data); ++i) {
- scoped_refptr<SessionBackend> backend(
- new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE,
- path_));
+ scoped_refptr<SnapshottingCommandStorageBackend> backend = CreateBackend();
SessionCommands commands;
if (i != 0) {
// Read previous data.
- backend->ReadLastSessionCommandsImpl(&commands);
+ ReadLastSessionCommands(backend.get(), &commands);
ASSERT_EQ(i, commands.size());
for (auto j = commands.begin(); j != commands.end(); ++j)
AssertCommandEqualsData(data[j - commands.begin()], j->get());
@@ -126,19 +149,18 @@ TEST_F(SessionBackendTest, RandomData) {
}
}
-TEST_F(SessionBackendTest, BigData) {
+TEST_F(SnapshottingCommandStorageBackendTest, BigData) {
struct TestData data[] = {
- { 1, "a" },
- { 2, "ab" },
+ {1, "a"},
+ {2, "ab"},
};
- scoped_refptr<SessionBackend> backend(
- new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE, path_));
+ scoped_refptr<SnapshottingCommandStorageBackend> backend = CreateBackend();
std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
commands.push_back(CreateCommandFromData(data[0]));
const sessions::SessionCommand::size_type big_size =
- SessionBackend::kFileReadBufferSize + 100;
+ SnapshottingCommandStorageBackend::kFileReadBufferSize + 100;
const sessions::SessionCommand::id_type big_id = 50;
std::unique_ptr<sessions::SessionCommand> big_command =
std::make_unique<sessions::SessionCommand>(big_id, big_size);
@@ -149,10 +171,9 @@ TEST_F(SessionBackendTest, BigData) {
backend->AppendCommands(std::move(commands), false);
backend = nullptr;
- backend = new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE,
- path_);
+ backend = CreateBackend();
- backend->ReadLastSessionCommandsImpl(&commands);
+ ReadLastSessionCommands(backend.get(), &commands);
ASSERT_EQ(3U, commands.size());
AssertCommandEqualsData(data[0], commands[0].get());
AssertCommandEqualsData(data[1], commands[2].get());
@@ -165,18 +186,17 @@ TEST_F(SessionBackendTest, BigData) {
commands.clear();
}
-TEST_F(SessionBackendTest, EmptyCommand) {
+TEST_F(SnapshottingCommandStorageBackendTest, EmptyCommand) {
TestData empty_command;
empty_command.command_id = 1;
- scoped_refptr<SessionBackend> backend(
- new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE, path_));
+ scoped_refptr<SnapshottingCommandStorageBackend> backend = CreateBackend();
SessionCommands empty_commands;
empty_commands.push_back(CreateCommandFromData(empty_command));
backend->AppendCommands(std::move(empty_commands), true);
backend->MoveCurrentSessionToLastSession();
SessionCommands commands;
- backend->ReadLastSessionCommandsImpl(&commands);
+ ReadLastSessionCommands(backend.get(), &commands);
ASSERT_EQ(1U, commands.size());
AssertCommandEqualsData(empty_command, commands[0].get());
commands.clear();
@@ -184,24 +204,22 @@ TEST_F(SessionBackendTest, EmptyCommand) {
// Writes a command, appends another command with reset to true, then reads
// making sure we only get back the second command.
-TEST_F(SessionBackendTest, Truncate) {
- scoped_refptr<SessionBackend> backend(
- new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE, path_));
- struct TestData first_data = { 1, "a" };
+TEST_F(SnapshottingCommandStorageBackendTest, Truncate) {
+ scoped_refptr<SnapshottingCommandStorageBackend> backend = CreateBackend();
+ struct TestData first_data = {1, "a"};
SessionCommands commands;
commands.push_back(CreateCommandFromData(first_data));
backend->AppendCommands(std::move(commands), false);
// Write another command, this time resetting the file when appending.
- struct TestData second_data = { 2, "b" };
+ struct TestData second_data = {2, "b"};
commands.push_back(CreateCommandFromData(second_data));
backend->AppendCommands(std::move(commands), true);
// Read it back in.
backend = nullptr;
- backend = new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE,
- path_);
- backend->ReadLastSessionCommandsImpl(&commands);
+ backend = CreateBackend();
+ ReadLastSessionCommands(backend.get(), &commands);
// And make sure we get back the expected data.
ASSERT_EQ(1U, commands.size());
diff --git a/chromium/components/sessions/core/tab_restore_service.h b/chromium/components/sessions/core/tab_restore_service.h
index cdb3f8b5f22..ec5cc7ad371 100644
--- a/chromium/components/sessions/core/tab_restore_service.h
+++ b/chromium/components/sessions/core/tab_restore_service.h
@@ -20,6 +20,8 @@
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/session_types.h"
#include "components/sessions/core/sessions_export.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
#include "ui/base/ui_base_types.h"
#include "ui/base/window_open_disposition.h"
#include "ui/gfx/geometry/rect.h"
@@ -120,10 +122,10 @@ class SESSIONS_EXPORT TabRestoreService : public KeyedService {
std::string user_agent_override;
// The group the tab belonged to, if any.
- base::Optional<base::Token> group;
+ base::Optional<tab_groups::TabGroupId> group;
// The group metadata for the tab, if any.
- base::Optional<TabGroupMetadata> group_metadata;
+ base::Optional<tab_groups::TabGroupVisualData> group_visual_data;
};
// Represents a previously open window.
@@ -140,7 +142,7 @@ class SESSIONS_EXPORT TabRestoreService : public KeyedService {
std::vector<std::unique_ptr<Tab>> tabs;
// Tab group data.
- std::map<base::Token, TabGroupMetadata> tab_groups;
+ std::map<tab_groups::TabGroupId, tab_groups::TabGroupVisualData> tab_groups;
// Index of the selected tab.
int selected_tab_index = -1;
diff --git a/chromium/components/sessions/core/tab_restore_service_client.h b/chromium/components/sessions/core/tab_restore_service_client.h
index f9388fe99b2..a3beea6227e 100644
--- a/chromium/components/sessions/core/tab_restore_service_client.h
+++ b/chromium/components/sessions/core/tab_restore_service_client.h
@@ -32,8 +32,8 @@ struct SessionWindow;
// Callback from TabRestoreServiceClient::GetLastSession.
// The second parameter is the id of the window that was last active.
using GetLastSessionCallback =
- base::Callback<void(std::vector<std::unique_ptr<SessionWindow>>,
- SessionID)>;
+ base::OnceCallback<void(std::vector<std::unique_ptr<SessionWindow>>,
+ SessionID)>;
// A client interface that needs to be supplied to the tab restore service by
// the embedder.
@@ -78,7 +78,7 @@ class SESSIONS_EXPORT TabRestoreServiceClient {
// Fetches the contents of the last session, notifying the callback when
// done. If the callback is supplied an empty vector of SessionWindows
// it means the session could not be restored.
- virtual void GetLastSession(const GetLastSessionCallback& callback,
+ virtual void GetLastSession(GetLastSessionCallback callback,
base::CancelableTaskTracker* tracker) = 0;
// Called when a tab is restored. |url| is the URL that the tab is currently
diff --git a/chromium/components/sessions/core/tab_restore_service_helper.cc b/chromium/components/sessions/core/tab_restore_service_helper.cc
index e83b5a52cda..3caebfcdb55 100644
--- a/chromium/components/sessions/core/tab_restore_service_helper.cc
+++ b/chromium/components/sessions/core/tab_restore_service_helper.cc
@@ -28,6 +28,8 @@
#include "components/sessions/core/session_types.h"
#include "components/sessions/core/tab_restore_service_client.h"
#include "components/sessions/core/tab_restore_service_observer.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
namespace sessions {
@@ -115,7 +117,7 @@ void TabRestoreServiceHelper::BrowserClosing(LiveTabContext* context) {
window->show_state = context->GetRestoredState();
window->workspace = context->GetWorkspace();
- base::flat_set<base::Token> seen_groups;
+ base::flat_set<tab_groups::TabGroupId> seen_groups;
for (int tab_index = 0; tab_index < context->GetTabCount(); ++tab_index) {
auto tab = std::make_unique<Tab>();
PopulateTab(tab.get(), tab_index, context,
@@ -128,9 +130,10 @@ void TabRestoreServiceHelper::BrowserClosing(LiveTabContext* context) {
}
}
- for (const base::Token& group : seen_groups) {
- TabGroupMetadata metadata = context->GetTabGroupMetadata(group);
- window->tab_groups.emplace(group, std::move(metadata));
+ for (const tab_groups::TabGroupId& group : seen_groups) {
+ const tab_groups::TabGroupVisualData* visual_data =
+ context->GetVisualDataForGroup(group);
+ window->tab_groups.emplace(group, std::move(*visual_data));
}
if (window->tabs.size() == 1 && window->app_name.empty()) {
@@ -318,7 +321,7 @@ std::vector<LiveTab*> TabRestoreServiceHelper::RestoreEntryById(
LiveTab* restored_tab = context->AddRestoredTab(
tab.navigations, context->GetTabCount(),
tab.current_navigation_index, tab.extension_app_id, tab.group,
- base::OptionalOrNullptr(tab.group_metadata),
+ tab.group_visual_data.value_or(tab_groups::TabGroupVisualData()),
static_cast<int>(tab_i) == window.selected_tab_index, tab.pinned,
tab.from_last_session, tab.platform_data.get(),
tab.user_agent_override);
@@ -330,7 +333,7 @@ std::vector<LiveTab*> TabRestoreServiceHelper::RestoreEntryById(
}
for (const auto& tab_group : window.tab_groups) {
- context->SetTabGroupMetadata(tab_group.first, tab_group.second);
+ context->SetVisualDataForGroup(tab_group.first, tab_group.second);
}
// All the window's tabs had the same former browser_id.
@@ -556,10 +559,12 @@ void TabRestoreServiceHelper::PopulateTab(Tab* tab,
tab->browser_id = context->GetSessionID().id();
tab->pinned = context->IsTabPinned(tab->tabstrip_index);
tab->group = context->GetTabGroupForTab(tab->tabstrip_index);
- tab->group_metadata =
+ tab->group_visual_data =
tab->group.has_value()
- ? base::Optional<TabGroupMetadata>{context->GetTabGroupMetadata(
- tab->group.value())}
+ ? base::Optional<
+ tab_groups::TabGroupVisualData>{*context
+ ->GetVisualDataForGroup(
+ tab->group.value())}
: base::nullopt;
}
}
@@ -606,7 +611,7 @@ LiveTabContext* TabRestoreServiceHelper::RestoreTab(
restored_tab = context->AddRestoredTab(
tab.navigations, tab_index, tab.current_navigation_index,
tab.extension_app_id, tab.group,
- base::OptionalOrNullptr(tab.group_metadata),
+ tab.group_visual_data.value_or(tab_groups::TabGroupVisualData()),
disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB, tab.pinned,
tab.from_last_session, tab.platform_data.get(),
tab.user_agent_override);
diff --git a/chromium/components/sessions/core/tab_restore_service_impl.cc b/chromium/components/sessions/core/tab_restore_service_impl.cc
index 3f948ce9631..6b81da763bd 100644
--- a/chromium/components/sessions/core/tab_restore_service_impl.cc
+++ b/chromium/components/sessions/core/tab_restore_service_impl.cc
@@ -21,11 +21,14 @@
#include "base/time/time.h"
#include "components/history/core/common/pref_names.h"
#include "components/prefs/pref_service.h"
-#include "components/sessions/core/base_session_service.h"
#include "components/sessions/core/base_session_service_commands.h"
-#include "components/sessions/core/base_session_service_delegate.h"
+#include "components/sessions/core/command_storage_manager_delegate.h"
#include "components/sessions/core/session_command.h"
#include "components/sessions/core/session_constants.h"
+#include "components/sessions/core/snapshotting_command_storage_manager.h"
+#include "components/tab_groups/tab_group_color.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
namespace sessions {
@@ -342,17 +345,18 @@ CreateWindowEntryFromCommand(const SessionCommand* command,
// ---------------------------------------
// This restore service persistence delegate will create and own a
-// BaseSessionService and implement the required BaseSessionServiceDelegate to
-// handle all the persistence of the tab restore service implementation.
+// CommandStorageManager and implement the required
+// CommandStorageManagerDelegate to handle all the persistence of the tab
+// restore service implementation.
class TabRestoreServiceImpl::PersistenceDelegate
- : public BaseSessionServiceDelegate,
+ : public CommandStorageManagerDelegate,
public TabRestoreServiceHelper::Observer {
public:
explicit PersistenceDelegate(TabRestoreServiceClient* client);
~PersistenceDelegate() override;
- // BaseSessionServiceDelegate:
+ // CommandStorageManagerDelegate:
bool ShouldUseDelayedSave() override;
void OnWillSaveCommands() override;
@@ -430,9 +434,9 @@ class TabRestoreServiceImpl::PersistenceDelegate
static void ValidateAndDeleteEmptyEntries(
std::vector<std::unique_ptr<Entry>>* entries);
- // Callback from BaseSessionService when we've received the windows from the
- // previous session. This creates and add entries to |staging_entries_| and
- // invokes LoadStateChanged. |ignored_active_window| is ignored because we
+ // Callback from CommandStorageManager when we've received the windows from
+ // the previous session. This creates and add entries to |staging_entries_|
+ // and invokes LoadStateChanged. |ignored_active_window| is ignored because we
// don't need to restore activation.
void OnGotPreviousSession(std::vector<std::unique_ptr<SessionWindow>> windows,
SessionID ignored_active_window);
@@ -451,7 +455,7 @@ class TabRestoreServiceImpl::PersistenceDelegate
// The associated client.
TabRestoreServiceClient* client_;
- std::unique_ptr<BaseSessionService> base_session_service_;
+ std::unique_ptr<SnapshottingCommandStorageManager> command_storage_manager_;
TabRestoreServiceHelper* tab_restore_service_helper_;
@@ -478,10 +482,11 @@ class TabRestoreServiceImpl::PersistenceDelegate
TabRestoreServiceImpl::PersistenceDelegate::PersistenceDelegate(
TabRestoreServiceClient* client)
: client_(client),
- base_session_service_(
- new BaseSessionService(BaseSessionService::TAB_RESTORE,
- client_->GetPathToSaveTo(),
- this)),
+ command_storage_manager_(
+ std::make_unique<SnapshottingCommandStorageManager>(
+ SnapshottingCommandStorageManager::TAB_RESTORE,
+ client_->GetPathToSaveTo(),
+ this)),
tab_restore_service_helper_(nullptr),
entries_to_write_(0),
entries_written_(0),
@@ -500,7 +505,7 @@ void TabRestoreServiceImpl::PersistenceDelegate::OnWillSaveCommands() {
entries_to_write_ = 0;
if (entries_written_ + to_write_count > kEntriesPerReset) {
to_write_count = entries.size();
- base_session_service_->set_pending_reset(true);
+ command_storage_manager_->set_pending_reset(true);
}
if (to_write_count) {
// Write the to_write_count most recently added entries out. The most
@@ -526,7 +531,7 @@ void TabRestoreServiceImpl::PersistenceDelegate::OnWillSaveCommands() {
entries_written_++;
}
}
- if (base_session_service_->pending_reset())
+ if (command_storage_manager_->pending_reset())
entries_written_ = 0;
}
@@ -534,16 +539,16 @@ void TabRestoreServiceImpl::PersistenceDelegate::OnClearEntries() {
// Mark all the tabs as closed so that we don't attempt to restore them.
const Entries& entries = tab_restore_service_helper_->entries();
for (auto i = entries.begin(); i != entries.end(); ++i)
- base_session_service_->ScheduleCommand(
+ command_storage_manager_->ScheduleCommand(
CreateRestoredEntryCommand((*i)->id));
entries_to_write_ = 0;
// Schedule a pending reset so that we nuke the file on next write.
- base_session_service_->set_pending_reset(true);
+ command_storage_manager_->set_pending_reset(true);
// Schedule a command, otherwise if there are no pending commands Save does
// nothing.
- base_session_service_->ScheduleCommand(
+ command_storage_manager_->ScheduleCommand(
CreateRestoredEntryCommand(SessionID::InvalidValue()));
}
@@ -552,10 +557,10 @@ void TabRestoreServiceImpl::PersistenceDelegate::OnNavigationEntriesDeleted() {
entries_to_write_ = tab_restore_service_helper_->entries().size();
// Schedule a pending reset so that we nuke the file on next write.
- base_session_service_->set_pending_reset(true);
+ command_storage_manager_->set_pending_reset(true);
// Schedule a command, otherwise if there are no pending commands Save does
// nothing.
- base_session_service_->ScheduleCommand(
+ command_storage_manager_->ScheduleCommand(
CreateRestoredEntryCommand(SessionID::InvalidValue()));
}
@@ -570,12 +575,12 @@ void TabRestoreServiceImpl::PersistenceDelegate::OnRestoreEntryById(
if (static_cast<int>(index) < entries_to_write_)
entries_to_write_--;
- base_session_service_->ScheduleCommand(CreateRestoredEntryCommand(id));
+ command_storage_manager_->ScheduleCommand(CreateRestoredEntryCommand(id));
}
void TabRestoreServiceImpl::PersistenceDelegate::OnAddEntry() {
// Start the save timer, when it fires we'll generate the commands.
- base_session_service_->StartSaveTimer();
+ command_storage_manager_->StartSaveTimer();
entries_to_write_++;
}
@@ -604,14 +609,14 @@ void TabRestoreServiceImpl::PersistenceDelegate::LoadTabsFromLastSession() {
// 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).
- base_session_service_->ScheduleGetLastSessionCommands(
- base::BindRepeating(&PersistenceDelegate::OnGotLastSessionCommands,
- base::Unretained(this)),
+ command_storage_manager_->ScheduleGetLastSessionCommands(
+ base::BindOnce(&PersistenceDelegate::OnGotLastSessionCommands,
+ base::Unretained(this)),
&cancelable_task_tracker_);
}
void TabRestoreServiceImpl::PersistenceDelegate::DeleteLastSession() {
- base_session_service_->DeleteLastSession();
+ command_storage_manager_->DeleteLastSession();
}
bool TabRestoreServiceImpl::PersistenceDelegate::IsLoaded() const {
@@ -630,7 +635,7 @@ void TabRestoreServiceImpl::PersistenceDelegate::CreateEntriesFromWindows(
}
void TabRestoreServiceImpl::PersistenceDelegate::Shutdown() {
- base_session_service_->Save();
+ command_storage_manager_->Save();
}
void TabRestoreServiceImpl::PersistenceDelegate::ScheduleCommandsForWindow(
@@ -649,13 +654,13 @@ void TabRestoreServiceImpl::PersistenceDelegate::ScheduleCommandsForWindow(
if (valid_tab_count == 0)
return; // No tabs to persist.
- base_session_service_->ScheduleCommand(CreateWindowCommand(
+ command_storage_manager_->ScheduleCommand(CreateWindowCommand(
window.id, std::min(real_selected_tab, valid_tab_count - 1),
valid_tab_count, window.bounds, window.show_state, window.workspace,
window.timestamp));
if (!window.app_name.empty()) {
- base_session_service_->ScheduleCommand(CreateSetWindowAppNameCommand(
+ command_storage_manager_->ScheduleCommand(CreateSetWindowAppNameCommand(
kCommandSetWindowAppName, window.id, window.app_name));
}
@@ -685,44 +690,48 @@ void TabRestoreServiceImpl::PersistenceDelegate::ScheduleCommandsForTab(
}
// Write the command that identifies the selected tab.
- base_session_service_->ScheduleCommand(CreateSelectedNavigationInTabCommand(
- tab.id, valid_count_before_selected, tab.timestamp));
+ command_storage_manager_->ScheduleCommand(
+ CreateSelectedNavigationInTabCommand(tab.id, valid_count_before_selected,
+ tab.timestamp));
if (tab.pinned) {
PinnedStatePayload payload = true;
std::unique_ptr<SessionCommand> command(
new SessionCommand(kCommandPinnedState, sizeof(payload)));
memcpy(command->contents(), &payload, sizeof(payload));
- base_session_service_->ScheduleCommand(std::move(command));
+ command_storage_manager_->ScheduleCommand(std::move(command));
}
if (tab.group.has_value()) {
base::Pickle pickle;
- WriteTokenToPickle(&pickle, tab.group.value());
- const TabGroupMetadata* metadata = &tab.group_metadata.value();
- pickle.WriteString16(metadata->title);
- pickle.WriteUInt32(metadata->color);
+ WriteTokenToPickle(&pickle, tab.group.value().token());
+ const tab_groups::TabGroupVisualData* visual_data =
+ &tab.group_visual_data.value();
+ pickle.WriteString16(visual_data->title());
+ pickle.WriteUInt32(static_cast<int>(visual_data->color()));
std::unique_ptr<SessionCommand> command(
new SessionCommand(kCommandGroup, pickle));
- base_session_service_->ScheduleCommand(std::move(command));
+ command_storage_manager_->ScheduleCommand(std::move(command));
}
if (!tab.extension_app_id.empty()) {
- base_session_service_->ScheduleCommand(CreateSetTabExtensionAppIDCommand(
+ command_storage_manager_->ScheduleCommand(CreateSetTabExtensionAppIDCommand(
kCommandSetExtensionAppID, tab.id, tab.extension_app_id));
}
if (!tab.user_agent_override.empty()) {
- base_session_service_->ScheduleCommand(CreateSetTabUserAgentOverrideCommand(
- kCommandSetTabUserAgentOverride, tab.id, tab.user_agent_override));
+ command_storage_manager_->ScheduleCommand(
+ CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
+ tab.id, tab.user_agent_override));
}
// Then write the navigations.
for (int i = first_index_to_persist, wrote_count = 0;
wrote_count < 2 * gMaxPersistNavigationCount && i < max_index; ++i) {
if (client_->ShouldTrackURLForRestore(navigations[i].virtual_url())) {
- base_session_service_->ScheduleCommand(CreateUpdateTabNavigationCommand(
- kCommandUpdateTabNavigation, tab.id, navigations[i]));
+ command_storage_manager_->ScheduleCommand(
+ CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id,
+ navigations[i]));
}
}
}
@@ -961,17 +970,21 @@ void TabRestoreServiceImpl::PersistenceDelegate::CreateEntriesFromCommands(
}
std::unique_ptr<base::Pickle> pickle(command.PayloadAsPickle());
base::PickleIterator iter(*pickle);
- base::Optional<base::Token> group_id = ReadTokenFromPickle(&iter);
+ base::Optional<base::Token> group_token = ReadTokenFromPickle(&iter);
base::string16 title;
- SkColor color;
+ uint32_t color_int;
if (!iter.ReadString16(&title)) {
break;
}
- if (!iter.ReadUInt32(&color)) {
+ if (!iter.ReadUInt32(&color_int)) {
break;
}
- current_tab->group = group_id.value();
- current_tab->group_metadata = TabGroupMetadata{title, color};
+
+ current_tab->group =
+ tab_groups::TabGroupId::FromRawToken(group_token.value());
+
+ current_tab->group_visual_data =
+ tab_groups::TabGroupVisualData(title, color_int);
break;
}
diff --git a/chromium/components/sessions/ios/ios_live_tab.h b/chromium/components/sessions/ios/ios_live_tab.h
index 4d50f120a3c..3dda6cf9994 100644
--- a/chromium/components/sessions/ios/ios_live_tab.h
+++ b/chromium/components/sessions/ios/ios_live_tab.h
@@ -5,56 +5,17 @@
#ifndef COMPONENTS_SESSIONS_IOS_IOS_LIVE_TAB_H_
#define COMPONENTS_SESSIONS_IOS_IOS_LIVE_TAB_H_
-#include "base/macros.h"
-#include "base/supports_user_data.h"
#include "components/sessions/core/live_tab.h"
-#include "components/sessions/ios/ios_serialized_navigation_builder.h"
#import "ios/web/public/web_state.h"
-namespace web {
-class NavigationManager;
-}
-
namespace sessions {
-// An implementation of LiveTab that is backed by web::WebState for use
-// on //ios/web-based platforms.
-class SESSIONS_EXPORT IOSLiveTab : public LiveTab,
- public base::SupportsUserData::Data {
+class SESSIONS_EXPORT IOSLiveTab : public LiveTab {
public:
~IOSLiveTab() override;
- // Returns the IOSLiveTab associated with |web_state|, creating it if
- // it has not already been created.
- static IOSLiveTab* GetForWebState(web::WebState* web_state);
-
- // LiveTab:
- bool IsInitialBlankNavigation() override;
- int GetCurrentEntryIndex() override;
- int GetPendingEntryIndex() override;
- sessions::SerializedNavigationEntry GetEntryAtIndex(int index) override;
- sessions::SerializedNavigationEntry GetPendingEntry() override;
- int GetEntryCount() override;
- const std::string& GetUserAgentOverride() override;
-
- web::WebState* web_state() { return web_state_; }
- const web::WebState* web_state() const { return web_state_; }
-
- private:
- friend class base::SupportsUserData;
-
- explicit IOSLiveTab(web::WebState* web_state);
-
- web::NavigationManager* navigation_manager() {
- return web_state_->GetNavigationManager();
- }
-
- web::WebState* web_state_;
-
- // Needed to return an empty string in GetUserAgentOverride().
- std::string user_agent_override_;
-
- DISALLOW_COPY_AND_ASSIGN(IOSLiveTab);
+ // The backing WebState of the live tab, or nullptr is it does not exist.
+ virtual const web::WebState* GetWebState() const = 0;
};
} // namespace sessions
diff --git a/chromium/components/sessions/ios/ios_live_tab.mm b/chromium/components/sessions/ios/ios_live_tab.mm
index d3bdbc6f479..001119cabc9 100644
--- a/chromium/components/sessions/ios/ios_live_tab.mm
+++ b/chromium/components/sessions/ios/ios_live_tab.mm
@@ -3,59 +3,14 @@
// found in the LICENSE file.
#include "components/sessions/ios/ios_live_tab.h"
-#include "base/memory/ptr_util.h"
-#include "ios/web/public/navigation/navigation_manager.h"
-namespace {
-const char kIOSLiveTabWebStateUserDataKey[] = "ios_live_tab";
-}
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
-namespace sessions {
-
-// static
-IOSLiveTab* IOSLiveTab::GetForWebState(web::WebState* web_state) {
- if (!web_state->GetUserData(kIOSLiveTabWebStateUserDataKey)) {
- web_state->SetUserData(kIOSLiveTabWebStateUserDataKey,
- base::WrapUnique(new IOSLiveTab(web_state)));
- }
-
- return static_cast<IOSLiveTab*>(
- web_state->GetUserData(kIOSLiveTabWebStateUserDataKey));
-}
-IOSLiveTab::IOSLiveTab(web::WebState* web_state) : web_state_(web_state) {}
+namespace sessions {
IOSLiveTab::~IOSLiveTab() {}
-bool IOSLiveTab::IsInitialBlankNavigation() {
- return navigation_manager()->GetItemCount() == 0;
-}
-
-int IOSLiveTab::GetCurrentEntryIndex() {
- return navigation_manager()->GetLastCommittedItemIndex();
-}
-
-int IOSLiveTab::GetPendingEntryIndex() {
- return navigation_manager()->GetPendingItemIndex();
-}
-
-sessions::SerializedNavigationEntry IOSLiveTab::GetEntryAtIndex(int index) {
- return sessions::IOSSerializedNavigationBuilder::FromNavigationItem(
- index, *navigation_manager()->GetItemAtIndex(index));
-}
-
-sessions::SerializedNavigationEntry IOSLiveTab::GetPendingEntry() {
- return sessions::IOSSerializedNavigationBuilder::FromNavigationItem(
- GetPendingEntryIndex(), *navigation_manager()->GetPendingItem());
-}
-
-int IOSLiveTab::GetEntryCount() {
- return navigation_manager()->GetItemCount();
-}
-
-const std::string& IOSLiveTab::GetUserAgentOverride() {
- // Dynamic user agent overrides are not supported on iOS.
- return user_agent_override_;
-}
-
} // namespace sessions
diff --git a/chromium/components/sessions/ios/ios_restore_live_tab.h b/chromium/components/sessions/ios/ios_restore_live_tab.h
index e68c2e65090..dd8f47875a3 100644
--- a/chromium/components/sessions/ios/ios_restore_live_tab.h
+++ b/chromium/components/sessions/ios/ios_restore_live_tab.h
@@ -7,7 +7,7 @@
#include "base/macros.h"
#include "base/supports_user_data.h"
-#include "components/sessions/core/live_tab.h"
+#include "components/sessions/ios/ios_live_tab.h"
@class CRWSessionStorage;
@@ -15,7 +15,7 @@ namespace sessions {
// An implementation of LiveTab that is backed by web::CRWSessionStorage for use
// when restoring tabs from a crashed session.
-class SESSIONS_EXPORT RestoreIOSLiveTab : public LiveTab {
+class SESSIONS_EXPORT RestoreIOSLiveTab : public IOSLiveTab {
public:
explicit RestoreIOSLiveTab(CRWSessionStorage* session);
~RestoreIOSLiveTab() override;
@@ -30,6 +30,7 @@ class SESSIONS_EXPORT RestoreIOSLiveTab : public LiveTab {
sessions::SerializedNavigationEntry GetPendingEntry() override;
int GetEntryCount() override;
const std::string& GetUserAgentOverride() override;
+ const web::WebState* GetWebState() const override;
private:
CRWSessionStorage* session_;
diff --git a/chromium/components/sessions/ios/ios_restore_live_tab.mm b/chromium/components/sessions/ios/ios_restore_live_tab.mm
index f26fa5559ea..3cc33eca3f1 100644
--- a/chromium/components/sessions/ios/ios_restore_live_tab.mm
+++ b/chromium/components/sessions/ios/ios_restore_live_tab.mm
@@ -8,6 +8,10 @@
#include "ios/web/public/session/crw_navigation_item_storage.h"
#include "ios/web/public/session/crw_session_storage.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
namespace sessions {
RestoreIOSLiveTab::RestoreIOSLiveTab(CRWSessionStorage* session)
@@ -48,4 +52,8 @@ const std::string& RestoreIOSLiveTab::GetUserAgentOverride() {
return user_agent_override_;
}
+const web::WebState* RestoreIOSLiveTab::GetWebState() const {
+ return nullptr;
+}
+
} // namespace sessions
diff --git a/chromium/components/sessions/ios/ios_serialized_navigation_builder.mm b/chromium/components/sessions/ios/ios_serialized_navigation_builder.mm
index 096bf40501b..5bd2936e388 100644
--- a/chromium/components/sessions/ios/ios_serialized_navigation_builder.mm
+++ b/chromium/components/sessions/ios/ios_serialized_navigation_builder.mm
@@ -10,6 +10,10 @@
#include "ios/web/public/navigation/referrer.h"
#include "ios/web/public/session/crw_navigation_item_storage.h"
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
namespace sessions {
// static
diff --git a/chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm b/chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm
index 4dafcb95e22..5a6a11551d5 100644
--- a/chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm
+++ b/chromium/components/sessions/ios/ios_serialized_navigation_builder_unittest.mm
@@ -27,14 +27,14 @@ std::unique_ptr<web::NavigationItem> MakeNavigationItemForTest() {
std::unique_ptr<web::NavigationItem> navigation_item(
web::NavigationItem::Create());
navigation_item->SetReferrer(web::Referrer(
- test_data::kReferrerURL,
+ test_data::ReferrerUrl(),
static_cast<web::ReferrerPolicy>(test_data::kReferrerPolicy)));
- navigation_item->SetURL(test_data::kVirtualURL);
+ navigation_item->SetURL(test_data::VirtualUrl());
navigation_item->SetTitle(test_data::kTitle);
navigation_item->SetTransitionType(test_data::kTransitionType);
navigation_item->SetTimestamp(test_data::kTimestamp);
navigation_item->GetFavicon().valid = true;
- navigation_item->GetFavicon().url = test_data::kFaviconURL;
+ navigation_item->GetFavicon().url = test_data::FaviconUrl();
return navigation_item;
}
@@ -53,14 +53,14 @@ TEST_F(IOSSerializedNavigationBuilderTest, FromNavigationItem) {
EXPECT_EQ(test_data::kIndex, navigation.index());
EXPECT_EQ(navigation_item->GetUniqueID(), navigation.unique_id());
- EXPECT_EQ(test_data::kReferrerURL, navigation.referrer_url());
+ EXPECT_EQ(test_data::ReferrerUrl(), navigation.referrer_url());
EXPECT_EQ(test_data::kReferrerPolicy, navigation.referrer_policy());
- EXPECT_EQ(test_data::kVirtualURL, navigation.virtual_url());
+ EXPECT_EQ(test_data::VirtualUrl(), navigation.virtual_url());
EXPECT_EQ(test_data::kTitle, navigation.title());
EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
navigation.transition_type(), test_data::kTransitionType));
EXPECT_EQ(test_data::kTimestamp, navigation.timestamp());
- EXPECT_EQ(test_data::kFaviconURL, navigation.favicon_url());
+ EXPECT_EQ(test_data::FaviconUrl(), navigation.favicon_url());
// The following fields should be left at their default values.
SerializedNavigationEntry default_navigation;
diff --git a/chromium/components/sessions/ios/ios_serialized_navigation_driver_unittest.cc b/chromium/components/sessions/ios/ios_serialized_navigation_driver_unittest.cc
index 4ece988240e..530c3f33bb2 100644
--- a/chromium/components/sessions/ios/ios_serialized_navigation_driver_unittest.cc
+++ b/chromium/components/sessions/ios/ios_serialized_navigation_driver_unittest.cc
@@ -39,20 +39,20 @@ TEST(IOSSerializedNavigationDriverTest, SanitizeWithReferrerPolicyAlways) {
driver->Sanitize(&navigation);
EXPECT_EQ(test_data::kIndex, navigation.index());
EXPECT_EQ(test_data::kUniqueID, navigation.unique_id());
- EXPECT_EQ(test_data::kReferrerURL, navigation.referrer_url());
+ EXPECT_EQ(test_data::ReferrerUrl(), navigation.referrer_url());
EXPECT_EQ(web::ReferrerPolicyAlways, navigation.referrer_policy());
- EXPECT_EQ(test_data::kVirtualURL, navigation.virtual_url());
+ EXPECT_EQ(test_data::VirtualUrl(), navigation.virtual_url());
EXPECT_EQ(test_data::kTitle, navigation.title());
EXPECT_EQ(test_data::kEncodedPageState, navigation.encoded_page_state());
EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
navigation.transition_type(), test_data::kTransitionType));
EXPECT_EQ(test_data::kHasPostData, navigation.has_post_data());
EXPECT_EQ(test_data::kPostID, navigation.post_id());
- EXPECT_EQ(test_data::kOriginalRequestURL, navigation.original_request_url());
+ EXPECT_EQ(test_data::OriginalRequestUrl(), navigation.original_request_url());
EXPECT_EQ(test_data::kIsOverridingUserAgent,
navigation.is_overriding_user_agent());
EXPECT_EQ(test_data::kTimestamp, navigation.timestamp());
- EXPECT_EQ(test_data::kFaviconURL, navigation.favicon_url());
+ EXPECT_EQ(test_data::FaviconUrl(), navigation.favicon_url());
EXPECT_EQ(test_data::kHttpStatusCode, navigation.http_status_code());
}
@@ -71,18 +71,18 @@ TEST(IOSSerializedNavigationDriverTest, SanitizeWithReferrerPolicyNever) {
// Fields that should remain untouched.
EXPECT_EQ(test_data::kIndex, navigation.index());
EXPECT_EQ(test_data::kUniqueID, navigation.unique_id());
- EXPECT_EQ(test_data::kVirtualURL, navigation.virtual_url());
+ EXPECT_EQ(test_data::VirtualUrl(), navigation.virtual_url());
EXPECT_EQ(test_data::kTitle, navigation.title());
EXPECT_EQ(test_data::kEncodedPageState, navigation.encoded_page_state());
EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
navigation.transition_type(), test_data::kTransitionType));
EXPECT_EQ(test_data::kHasPostData, navigation.has_post_data());
EXPECT_EQ(test_data::kPostID, navigation.post_id());
- EXPECT_EQ(test_data::kOriginalRequestURL, navigation.original_request_url());
+ EXPECT_EQ(test_data::OriginalRequestUrl(), navigation.original_request_url());
EXPECT_EQ(test_data::kIsOverridingUserAgent,
navigation.is_overriding_user_agent());
EXPECT_EQ(test_data::kTimestamp, navigation.timestamp());
- EXPECT_EQ(test_data::kFaviconURL, navigation.favicon_url());
+ EXPECT_EQ(test_data::FaviconUrl(), navigation.favicon_url());
EXPECT_EQ(test_data::kHttpStatusCode, navigation.http_status_code());
// Fields that were sanitized.
diff --git a/chromium/components/sessions/ios/ios_webstate_live_tab.h b/chromium/components/sessions/ios/ios_webstate_live_tab.h
new file mode 100644
index 00000000000..0dc921e41eb
--- /dev/null
+++ b/chromium/components/sessions/ios/ios_webstate_live_tab.h
@@ -0,0 +1,61 @@
+// Copyright 2015 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.
+
+#ifndef COMPONENTS_SESSIONS_IOS_IOS_WEBSTATE_LIVE_TAB_H_
+#define COMPONENTS_SESSIONS_IOS_IOS_WEBSTATE_LIVE_TAB_H_
+
+#include "base/macros.h"
+#include "base/supports_user_data.h"
+#include "components/sessions/ios/ios_live_tab.h"
+#include "components/sessions/ios/ios_serialized_navigation_builder.h"
+#import "ios/web/public/web_state.h"
+
+namespace web {
+class NavigationManager;
+}
+
+namespace sessions {
+
+// An implementation of LiveTab that is backed by web::WebState for use
+// on //ios/web-based platforms.
+class SESSIONS_EXPORT IOSWebStateLiveTab : public IOSLiveTab,
+ public base::SupportsUserData::Data {
+ public:
+ ~IOSWebStateLiveTab() override;
+
+ // Returns the IOSLiveTab associated with |web_state|, creating it if
+ // it has not already been created.
+ static IOSWebStateLiveTab* GetForWebState(web::WebState* web_state);
+
+ // LiveTab:
+ bool IsInitialBlankNavigation() override;
+ int GetCurrentEntryIndex() override;
+ int GetPendingEntryIndex() override;
+ sessions::SerializedNavigationEntry GetEntryAtIndex(int index) override;
+ sessions::SerializedNavigationEntry GetPendingEntry() override;
+ int GetEntryCount() override;
+ const std::string& GetUserAgentOverride() override;
+
+ const web::WebState* GetWebState() const override;
+
+ private:
+ friend class base::SupportsUserData;
+
+ explicit IOSWebStateLiveTab(web::WebState* web_state);
+
+ web::NavigationManager* navigation_manager() {
+ return web_state_->GetNavigationManager();
+ }
+
+ web::WebState* web_state_;
+
+ // Needed to return an empty string in GetUserAgentOverride().
+ std::string user_agent_override_;
+
+ DISALLOW_COPY_AND_ASSIGN(IOSWebStateLiveTab);
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_IOS_IOS_WEBSTATE_LIVE_TAB_H_
diff --git a/chromium/components/sessions/ios/ios_webstate_live_tab.mm b/chromium/components/sessions/ios/ios_webstate_live_tab.mm
new file mode 100644
index 00000000000..48c9935f036
--- /dev/null
+++ b/chromium/components/sessions/ios/ios_webstate_live_tab.mm
@@ -0,0 +1,73 @@
+// Copyright 2015 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/ios/ios_webstate_live_tab.h"
+
+#include "base/memory/ptr_util.h"
+#include "ios/web/public/navigation/navigation_manager.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+const char kIOSWebStateLiveTabWebStateUserDataKey[] = "ios_live_tab";
+}
+
+namespace sessions {
+
+// static
+IOSWebStateLiveTab* IOSWebStateLiveTab::GetForWebState(
+ web::WebState* web_state) {
+ if (!web_state->GetUserData(kIOSWebStateLiveTabWebStateUserDataKey)) {
+ web_state->SetUserData(kIOSWebStateLiveTabWebStateUserDataKey,
+ base::WrapUnique(new IOSWebStateLiveTab(web_state)));
+ }
+
+ return static_cast<IOSWebStateLiveTab*>(
+ web_state->GetUserData(kIOSWebStateLiveTabWebStateUserDataKey));
+}
+
+IOSWebStateLiveTab::IOSWebStateLiveTab(web::WebState* web_state)
+ : web_state_(web_state) {}
+
+IOSWebStateLiveTab::~IOSWebStateLiveTab() {}
+
+bool IOSWebStateLiveTab::IsInitialBlankNavigation() {
+ return navigation_manager()->GetItemCount() == 0;
+}
+
+int IOSWebStateLiveTab::GetCurrentEntryIndex() {
+ return navigation_manager()->GetLastCommittedItemIndex();
+}
+
+int IOSWebStateLiveTab::GetPendingEntryIndex() {
+ return navigation_manager()->GetPendingItemIndex();
+}
+
+sessions::SerializedNavigationEntry IOSWebStateLiveTab::GetEntryAtIndex(
+ int index) {
+ return sessions::IOSSerializedNavigationBuilder::FromNavigationItem(
+ index, *navigation_manager()->GetItemAtIndex(index));
+}
+
+sessions::SerializedNavigationEntry IOSWebStateLiveTab::GetPendingEntry() {
+ return sessions::IOSSerializedNavigationBuilder::FromNavigationItem(
+ GetPendingEntryIndex(), *navigation_manager()->GetPendingItem());
+}
+
+int IOSWebStateLiveTab::GetEntryCount() {
+ return navigation_manager()->GetItemCount();
+}
+
+const std::string& IOSWebStateLiveTab::GetUserAgentOverride() {
+ // Dynamic user agent overrides are not supported on iOS.
+ return user_agent_override_;
+}
+
+const web::WebState* IOSWebStateLiveTab::GetWebState() const {
+ return web_state_;
+}
+
+} // namespace sessions