blob: 120a7e8a6c1510026da8dac604f65503af189389 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sessions/content/content_serialized_navigation_builder.h"
#include <memory>
#include <string>
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/sessions/content/content_record_password_state.h"
#include "components/sessions/content/content_serialized_navigation_driver.h"
#include "components/sessions/content/extended_info_handler.h"
#include "components/sessions/content/navigation_task_id.h"
#include "components/sessions/core/serialized_navigation_entry.h"
#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_entry_restore_context.h"
#include "content/public/common/referrer.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/page_state/page_state.h"
namespace sessions {
namespace {
const char kExtendedInfoKey1[] = "Key 1";
const char kExtendedInfoValue1[] = "Value 1";
const char kExtendedInfoKey2[] = "Key 2";
const char kExtendedInfoValue2[] = "Value 2";
struct TestData : public base::SupportsUserData::Data {
explicit TestData(const std::string& string) : string(string) {}
~TestData() override = default;
std::string string;
};
class TestExtendedInfoHandler : public ExtendedInfoHandler {
public:
explicit TestExtendedInfoHandler(const char* key) : key_(key) {}
TestExtendedInfoHandler(const TestExtendedInfoHandler&) = delete;
TestExtendedInfoHandler& operator=(const TestExtendedInfoHandler&) = delete;
~TestExtendedInfoHandler() override = default;
// ExtendedInfoHandler:
std::string GetExtendedInfo(content::NavigationEntry* entry) const override {
TestData* test_data = static_cast<TestData*>(entry->GetUserData(key_));
return test_data ? test_data->string : std::string();
}
void RestoreExtendedInfo(const std::string& info,
content::NavigationEntry* entry) override {
entry->SetUserData(key_, std::make_unique<TestData>(info));
}
private:
const char* key_;
};
std::unique_ptr<content::NavigationEntry> MakeNavigationEntryForTest() {
std::unique_ptr<content::NavigationEntry> navigation_entry(
content::NavigationEntry::Create());
navigation_entry->SetReferrer(content::Referrer(
GURL("http://www.referrer.com"),
static_cast<network::mojom::ReferrerPolicy>(test_data::kReferrerPolicy)));
navigation_entry->SetURL(GURL("http://www.url.com"));
navigation_entry->SetVirtualURL(GURL("http://www.virtual-url.com"));
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(
GURL("http://www.original-request.com"));
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 =
GURL("http://virtual-url.com/favicon.ico");
navigation_entry->SetHttpStatusCode(test_data::kHttpStatusCode);
std::vector<GURL> redirect_chain;
redirect_chain.emplace_back("http://go/redirect0");
redirect_chain.emplace_back("http://go/redirect1");
redirect_chain.push_back(navigation_entry->GetVirtualURL());
navigation_entry->SetRedirectChain(redirect_chain);
NavigationTaskId::Get(navigation_entry.get())->set_id(test_data::kTaskId);
NavigationTaskId::Get(navigation_entry.get())
->set_parent_id(test_data::kParentTaskId);
NavigationTaskId::Get(navigation_entry.get())
->set_root_id(test_data::kRootTaskId);
return navigation_entry;
}
void SetExtendedInfoForTest(content::NavigationEntry* entry) {
entry->SetUserData(kExtendedInfoKey1,
std::make_unique<TestData>(kExtendedInfoValue1));
entry->SetUserData(kExtendedInfoKey2,
std::make_unique<TestData>(kExtendedInfoValue2));
}
} // namespace
class ContentSerializedNavigationBuilderTest : public testing::Test {
public:
ContentSerializedNavigationBuilderTest() = default;
ContentSerializedNavigationBuilderTest(
const ContentSerializedNavigationBuilderTest&) = delete;
ContentSerializedNavigationBuilderTest& operator=(
const ContentSerializedNavigationBuilderTest&) = delete;
~ContentSerializedNavigationBuilderTest() override = default;
void SetUp() override {
ContentSerializedNavigationDriver* driver =
ContentSerializedNavigationDriver::GetInstance();
driver->RegisterExtendedInfoHandler(
kExtendedInfoKey1, base::WrapUnique<ExtendedInfoHandler>(
new TestExtendedInfoHandler(kExtendedInfoKey1)));
driver->RegisterExtendedInfoHandler(
kExtendedInfoKey2, base::WrapUnique<ExtendedInfoHandler>(
new TestExtendedInfoHandler(kExtendedInfoKey2)));
}
void TearDown() override {
ContentSerializedNavigationDriver* driver =
ContentSerializedNavigationDriver::GetInstance();
driver->extended_info_handler_map_.clear();
}
};
// Create a SerializedNavigationEntry from a NavigationEntry. All its fields
// should match the NavigationEntry's.
TEST_F(ContentSerializedNavigationBuilderTest, FromNavigationEntry) {
const std::unique_ptr<content::NavigationEntry> navigation_entry(
MakeNavigationEntryForTest());
SetExtendedInfoForTest(navigation_entry.get());
const SerializedNavigationEntry& navigation =
ContentSerializedNavigationBuilder::FromNavigationEntry(
test_data::kIndex, navigation_entry.get());
EXPECT_EQ(test_data::kIndex, navigation.index());
EXPECT_EQ(navigation_entry->GetUniqueID(), navigation.unique_id());
EXPECT_EQ(navigation_entry->GetReferrer().url, navigation.referrer_url());
EXPECT_EQ(navigation_entry->GetReferrer().policy,
static_cast<network::mojom::ReferrerPolicy>(
navigation.referrer_policy()));
EXPECT_EQ(navigation_entry->GetVirtualURL(), navigation.virtual_url());
EXPECT_EQ(navigation_entry->GetTitle(), navigation.title());
EXPECT_EQ(navigation_entry->GetPageState().ToEncodedData(),
navigation.encoded_page_state());
EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
navigation.transition_type(), navigation_entry->GetTransitionType()));
EXPECT_EQ(navigation_entry->GetHasPostData(), navigation.has_post_data());
EXPECT_EQ(navigation_entry->GetPostID(), navigation.post_id());
EXPECT_EQ(navigation_entry->GetOriginalRequestURL(),
navigation.original_request_url());
EXPECT_EQ(navigation_entry->GetIsOverridingUserAgent(),
navigation.is_overriding_user_agent());
EXPECT_EQ(navigation_entry->GetTimestamp(), navigation.timestamp());
EXPECT_EQ(navigation_entry->GetFavicon().url, navigation.favicon_url());
EXPECT_EQ(navigation_entry->GetHttpStatusCode(),
navigation.http_status_code());
EXPECT_EQ(navigation_entry->GetRedirectChain(), navigation.redirect_chain());
EXPECT_EQ(GetPasswordStateFromNavigation(navigation_entry.get()),
navigation.password_state());
ASSERT_EQ(2U, navigation.extended_info_map().size());
ASSERT_EQ(1U, navigation.extended_info_map().count(kExtendedInfoKey1));
EXPECT_EQ(kExtendedInfoValue1,
navigation.extended_info_map().at(kExtendedInfoKey1));
ASSERT_EQ(1U, navigation.extended_info_map().count(kExtendedInfoKey2));
EXPECT_EQ(kExtendedInfoValue2,
navigation.extended_info_map().at(kExtendedInfoKey2));
sessions::NavigationTaskId* navigation_task_id =
sessions::NavigationTaskId::Get(navigation_entry.get());
EXPECT_EQ(navigation_task_id->id(), navigation.task_id());
EXPECT_EQ(navigation_task_id->parent_id(), navigation.parent_task_id());
EXPECT_EQ(navigation_task_id->root_id(), navigation.root_task_id());
}
// Test effect of the navigation serialization options.
TEST_F(ContentSerializedNavigationBuilderTest,
FromNavigationEntrySerializationOptions) {
const std::unique_ptr<content::NavigationEntry> navigation_entry(
MakeNavigationEntryForTest());
const SerializedNavigationEntry& default_navigation =
ContentSerializedNavigationBuilder::FromNavigationEntry(
test_data::kIndex, navigation_entry.get(),
ContentSerializedNavigationBuilder::DEFAULT);
EXPECT_EQ(navigation_entry->GetPageState().ToEncodedData(),
default_navigation.encoded_page_state());
const SerializedNavigationEntry& excluded_page_state_navigation =
ContentSerializedNavigationBuilder::FromNavigationEntry(
test_data::kIndex, navigation_entry.get(),
ContentSerializedNavigationBuilder::EXCLUDE_PAGE_STATE);
EXPECT_TRUE(excluded_page_state_navigation.encoded_page_state().empty());
}
// Create a NavigationEntry, then create another one by converting to
// a SerializedNavigationEntry and back. The new one should match the old one
// except for fields that aren't preserved, which should be set to
// expected values.
TEST_F(ContentSerializedNavigationBuilderTest, ToNavigationEntry) {
content::BrowserTaskEnvironment test_environment;
content::TestBrowserContext browser_context;
const std::unique_ptr<content::NavigationEntry> old_navigation_entry(
MakeNavigationEntryForTest());
SetExtendedInfoForTest(old_navigation_entry.get());
const SerializedNavigationEntry& navigation =
ContentSerializedNavigationBuilder::FromNavigationEntry(
test_data::kIndex, old_navigation_entry.get());
std::unique_ptr<content::NavigationEntryRestoreContext> restore_context =
content::NavigationEntryRestoreContext::Create();
const std::unique_ptr<content::NavigationEntry> new_navigation_entry(
ContentSerializedNavigationBuilder::ToNavigationEntry(
&navigation, &browser_context, restore_context.get()));
EXPECT_EQ(old_navigation_entry->GetReferrer().url,
new_navigation_entry->GetReferrer().url);
EXPECT_EQ(old_navigation_entry->GetReferrer().policy,
new_navigation_entry->GetReferrer().policy);
EXPECT_EQ(old_navigation_entry->GetURL(), new_navigation_entry->GetURL());
EXPECT_EQ(old_navigation_entry->GetVirtualURL(),
new_navigation_entry->GetVirtualURL());
EXPECT_EQ(old_navigation_entry->GetTitle(), new_navigation_entry->GetTitle());
EXPECT_EQ(old_navigation_entry->GetPageState().ToEncodedData(),
new_navigation_entry->GetPageState().ToEncodedData());
EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
new_navigation_entry->GetTransitionType(), ui::PAGE_TRANSITION_RELOAD));
EXPECT_EQ(old_navigation_entry->GetHasPostData(),
new_navigation_entry->GetHasPostData());
EXPECT_EQ(old_navigation_entry->GetPostID(),
new_navigation_entry->GetPostID());
EXPECT_EQ(old_navigation_entry->GetOriginalRequestURL(),
new_navigation_entry->GetOriginalRequestURL());
EXPECT_EQ(old_navigation_entry->GetIsOverridingUserAgent(),
new_navigation_entry->GetIsOverridingUserAgent());
EXPECT_EQ(old_navigation_entry->GetHttpStatusCode(),
new_navigation_entry->GetHttpStatusCode());
EXPECT_EQ(old_navigation_entry->GetRedirectChain(),
new_navigation_entry->GetRedirectChain());
sessions::NavigationTaskId* old_navigation_task_id =
sessions::NavigationTaskId::Get(old_navigation_entry.get());
sessions::NavigationTaskId* new_navigation_task_id =
sessions::NavigationTaskId::Get(new_navigation_entry.get());
EXPECT_EQ(old_navigation_task_id->id(), new_navigation_task_id->id());
EXPECT_EQ(old_navigation_task_id->parent_id(),
new_navigation_task_id->parent_id());
EXPECT_EQ(old_navigation_task_id->root_id(),
new_navigation_task_id->root_id());
TestData* test_data = static_cast<TestData*>(
new_navigation_entry->GetUserData(kExtendedInfoKey1));
ASSERT_TRUE(test_data);
EXPECT_EQ(kExtendedInfoValue1, test_data->string);
test_data = static_cast<TestData*>(
new_navigation_entry->GetUserData(kExtendedInfoKey2));
ASSERT_TRUE(test_data);
EXPECT_EQ(kExtendedInfoValue2, test_data->string);
}
TEST_F(ContentSerializedNavigationBuilderTest, ToNavigationEntries) {
content::BrowserTaskEnvironment test_environment;
content::TestBrowserContext browser_context;
// Make a NavigationEntry and serialize it twice into a vector.
const std::unique_ptr<content::NavigationEntry> entry(
MakeNavigationEntryForTest());
std::vector<SerializedNavigationEntry> navigations;
navigations.push_back(
ContentSerializedNavigationBuilder::FromNavigationEntry(0, entry.get()));
navigations.push_back(
ContentSerializedNavigationBuilder::FromNavigationEntry(1, entry.get()));
// Deserialize them via ToNavigationEntries().
std::vector<std::unique_ptr<content::NavigationEntry>> restored_entries =
ContentSerializedNavigationBuilder::ToNavigationEntries(navigations,
&browser_context);
ASSERT_EQ(restored_entries.size(), 2ul);
ASSERT_EQ(entry->GetURL(), restored_entries[0]->GetURL());
ASSERT_EQ(entry->GetURL(), restored_entries[1]->GetURL());
// Because the state of the two SerializedNavigationEntries was identical
// (specifically, their item sequence numbers), the root
// FrameNavigationEntries for the two restored entries should be de-duplicated
// and shared. The url is stored on the root FrameNavigationEntry, so because
// they are shared, calling SetURL() on one of the restored entries should
// update the url on both.
GURL new_url("http://www.url.com#ToNavigationEntries");
restored_entries[0]->SetURL(new_url);
EXPECT_EQ(new_url, restored_entries[0]->GetURL());
EXPECT_EQ(new_url, restored_entries[1]->GetURL());
}
// Test that we can restore FrameNavigationEntries with matching item sequence
// numbers and unique names but different URLs, as these may be found in
// serialized sessions from builds prior to r894383.
// See https://crbug.com/1275257.
TEST_F(ContentSerializedNavigationBuilderTest, DeduplicateEntriesByURL) {
content::BrowserTaskEnvironment test_environment;
content::TestBrowserContext browser_context;
// Make a NavigationEntry and serialize it several times into a vector.
// Change the URL but not item sequence number for the last two entries, to
// simulate a replaceState that was serialized from a build prior to r894383.
const std::unique_ptr<content::NavigationEntry> entry(
MakeNavigationEntryForTest());
GURL url1("http://www.url.com/page1");
entry->SetURL(url1);
std::vector<SerializedNavigationEntry> navigations;
navigations.push_back(
ContentSerializedNavigationBuilder::FromNavigationEntry(0, entry.get()));
navigations.push_back(
ContentSerializedNavigationBuilder::FromNavigationEntry(1, entry.get()));
GURL url2("http://www.url.com/page2");
entry->SetURL(url2);
navigations.push_back(
ContentSerializedNavigationBuilder::FromNavigationEntry(2, entry.get()));
navigations.push_back(
ContentSerializedNavigationBuilder::FromNavigationEntry(3, entry.get()));
// Deserialize them via ToNavigationEntries().
std::vector<std::unique_ptr<content::NavigationEntry>> restored_entries =
ContentSerializedNavigationBuilder::ToNavigationEntries(navigations,
&browser_context);
ASSERT_EQ(restored_entries.size(), 4ul);
ASSERT_EQ(url1, restored_entries[0]->GetURL());
ASSERT_EQ(url1, restored_entries[1]->GetURL());
ASSERT_EQ(url2, restored_entries[2]->GetURL());
ASSERT_EQ(url2, restored_entries[3]->GetURL());
// The root FrameNavigationEntries for the first two restored entries should
// have been de-duplicated and shared, so changing the URL of one of them
// should change the other, but not the third and fourth entries (which had a
// different URL and thus aren't shared).
GURL url3("http://www.url.com/page3");
restored_entries[0]->SetURL(url3);
EXPECT_EQ(url3, restored_entries[0]->GetURL());
EXPECT_EQ(url3, restored_entries[1]->GetURL());
EXPECT_EQ(url2, restored_entries[2]->GetURL());
EXPECT_EQ(url2, restored_entries[3]->GetURL());
// The third and fourth entries should have a shared FrameNavigationEntry as
// well, such that updating one affects the other but not the first and second
// entries.
GURL url4("http://www.url.com/page4");
restored_entries[3]->SetURL(url4);
EXPECT_EQ(url3, restored_entries[0]->GetURL());
EXPECT_EQ(url3, restored_entries[1]->GetURL());
EXPECT_EQ(url4, restored_entries[2]->GetURL());
EXPECT_EQ(url4, restored_entries[3]->GetURL());
}
TEST_F(ContentSerializedNavigationBuilderTest, SetPasswordState) {
std::unique_ptr<content::NavigationEntry> entry(
content::NavigationEntry::Create());
EXPECT_EQ(SerializedNavigationEntry::PASSWORD_STATE_UNKNOWN,
GetPasswordStateFromNavigation(entry.get()));
SetPasswordStateInNavigation(SerializedNavigationEntry::NO_PASSWORD_FIELD,
entry.get());
EXPECT_EQ(SerializedNavigationEntry::NO_PASSWORD_FIELD,
GetPasswordStateFromNavigation(entry.get()));
}
} // namespace sessions