diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/components/sessions | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (diff) | |
download | qtwebengine-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')
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 |