blob: 3fc8de8b88f3dec5424a7ebceab8342fae61ffb9 [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 <string>
#include "base/check_op.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 "content/public/browser/browser_context.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_entry_restore_context.h"
#include "content/public/common/referrer.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/blink/public/common/page_state/page_state.h"
namespace sessions {
// static
SerializedNavigationEntry
ContentSerializedNavigationBuilder::FromNavigationEntry(
int index,
content::NavigationEntry* entry,
SerializationOptions serialization_options) {
SerializedNavigationEntry navigation;
navigation.index_ = index;
navigation.unique_id_ = entry->GetUniqueID();
navigation.referrer_url_ = entry->GetReferrer().url;
navigation.referrer_policy_ = static_cast<int>(entry->GetReferrer().policy);
navigation.virtual_url_ = entry->GetVirtualURL();
navigation.title_ = entry->GetTitle();
if (!(serialization_options & SerializationOptions::EXCLUDE_PAGE_STATE))
navigation.encoded_page_state_ = entry->GetPageState().ToEncodedData();
navigation.transition_type_ = entry->GetTransitionType();
navigation.has_post_data_ = entry->GetHasPostData();
navigation.post_id_ = entry->GetPostID();
navigation.original_request_url_ = entry->GetOriginalRequestURL();
navigation.is_overriding_user_agent_ = entry->GetIsOverridingUserAgent();
navigation.timestamp_ = entry->GetTimestamp();
navigation.is_restored_ = entry->IsRestored();
if (entry->GetFavicon().valid)
navigation.favicon_url_ = entry->GetFavicon().url;
navigation.http_status_code_ = entry->GetHttpStatusCode();
navigation.redirect_chain_ = entry->GetRedirectChain();
navigation.password_state_ = GetPasswordStateFromNavigation(entry);
navigation.task_id_ = NavigationTaskId::Get(entry)->id();
navigation.parent_task_id_ = NavigationTaskId::Get(entry)->parent_id();
navigation.root_task_id_ = NavigationTaskId::Get(entry)->root_id();
for (const auto& handler_entry :
ContentSerializedNavigationDriver::GetInstance()
->GetAllExtendedInfoHandlers()) {
ExtendedInfoHandler* handler = handler_entry.second.get();
DCHECK(handler);
std::string value = handler->GetExtendedInfo(entry);
if (!value.empty())
navigation.extended_info_map_[handler_entry.first] = value;
}
return navigation;
}
// static
std::unique_ptr<content::NavigationEntry>
ContentSerializedNavigationBuilder::ToNavigationEntry(
const SerializedNavigationEntry* navigation,
content::BrowserContext* browser_context,
content::NavigationEntryRestoreContext* restore_context) {
DCHECK(navigation);
DCHECK(browser_context);
DCHECK(restore_context);
// The initial values of the NavigationEntry are usually temporary - they will
// normally get clobbered by one of the SetPageState calls below.
//
// This means that things like |navigation->referrer_url| are ignored
// in favor of using the data stored in |navigation->encoded_page_state|.
//
// We still use the URL and referrer from |navigation| here for the temporary
// values, though, in case the PageState fails to decode and clobber them.
// This allows us to load the original URL and referrer even if other state is
// lost.
GURL temporary_url = navigation->virtual_url_;
content::Referrer temporary_referrer(
navigation->referrer_url(),
content::Referrer::ConvertToPolicy(navigation->referrer_policy()));
std::optional<url::Origin> temporary_initiator_origin;
std::optional<GURL> temporary_initiator_base_url;
std::unique_ptr<content::NavigationEntry> entry(
content::NavigationController::CreateNavigationEntry(
temporary_url, temporary_referrer, temporary_initiator_origin,
temporary_initiator_base_url,
// Use a transition type of reload so that we don't incorrectly
// increase the typed count.
ui::PAGE_TRANSITION_RELOAD, false,
// The extra headers are not sync'ed across sessions.
std::string(), browser_context,
nullptr /* blob_url_loader_factory */));
// In some cases the |encoded_page_state| might be empty - we
// need to gracefully handle such data when it is deserialized.
//
// One case is tests for "foreign" session restore entries, such as
// SessionRestoreTest.RestoreForeignTab. We hypothesise that old session
// restore entries might also contain an empty |encoded_page_state|.
if (navigation->encoded_page_state_.empty()) {
// Ensure that the deserialized/restored content::NavigationEntry (and
// the content::FrameNavigationEntry underneath) has a valid PageState.
entry->SetPageState(
blink::PageState::CreateFromURL(navigation->virtual_url_),
restore_context);
// The |navigation|-based referrer set below might be inconsistent with the
// referrer embedded inside the PageState set above. Nevertheless, to
// minimize changes to behavior of old session restore entries, we restore
// the deserialized referrer here.
//
// TODO(lukasza): Consider including the |deserialized_referrer| in the
// PageState set above + drop the SetReferrer call below. This will
// slightly change the legacy behavior, but will make PageState and
// Referrer consistent.
entry->SetReferrer(temporary_referrer);
} else {
// Note that PageState covers some of the values inside |navigation| (e.g.
// URL, Referrer). Calling SetPageState will clobber these values in
// content::NavigationEntry (and FrameNavigationEntry(s) below), as long as
// the PageState successfully decodes.
entry->SetPageState(blink::PageState::CreateFromEncodedData(
navigation->encoded_page_state_),
restore_context);
// In theory the referrer information in the PageState should exactly match
// the `navigation`-level data, but there are sometimes discrepancies in
// practice (e.g. see https://crbug.com/1362322).
//
// TODO(crbug.com/40871594): Reintroduce DCHECKs that verify
// consistency between `navigation->referrer_url()` and
// `entry->GetReferrer().url` (and between referrer policies restored in
// `entry` and remembered in `navigation`).
}
entry->SetTitle(navigation->title_);
entry->SetHasPostData(navigation->has_post_data_);
entry->SetPostID(navigation->post_id_);
entry->SetOriginalRequestURL(navigation->original_request_url_);
entry->SetIsOverridingUserAgent(navigation->is_overriding_user_agent_);
entry->SetTimestamp(navigation->timestamp_);
entry->SetHttpStatusCode(navigation->http_status_code_);
entry->SetRedirectChain(navigation->redirect_chain_);
entry->SetVirtualURL(navigation->virtual_url_);
sessions::NavigationTaskId* navigation_task_id =
sessions::NavigationTaskId::Get(entry.get());
navigation_task_id->set_id(navigation->task_id());
navigation_task_id->set_parent_id(navigation->parent_task_id());
navigation_task_id->set_root_id(navigation->root_task_id());
const ContentSerializedNavigationDriver::ExtendedInfoHandlerMap&
extended_info_handlers = ContentSerializedNavigationDriver::GetInstance()
->GetAllExtendedInfoHandlers();
for (const auto& extended_info_entry : navigation->extended_info_map_) {
const std::string& key = extended_info_entry.first;
if (!extended_info_handlers.count(key))
continue;
ExtendedInfoHandler* extended_info_handler =
extended_info_handlers.at(key).get();
DCHECK(extended_info_handler);
extended_info_handler->RestoreExtendedInfo(extended_info_entry.second,
entry.get());
}
// This field should have the default value.
DCHECK_EQ(SerializedNavigationEntry::STATE_INVALID,
navigation->blocked_state_);
return entry;
}
// static
std::vector<std::unique_ptr<content::NavigationEntry>>
ContentSerializedNavigationBuilder::ToNavigationEntries(
const std::vector<SerializedNavigationEntry>& navigations,
content::BrowserContext* browser_context) {
std::unique_ptr<content::NavigationEntryRestoreContext> restore_context =
content::NavigationEntryRestoreContext::Create();
std::vector<std::unique_ptr<content::NavigationEntry>> entries;
entries.reserve(navigations.size());
for (const auto& navigation : navigations) {
entries.push_back(
ToNavigationEntry(&navigation, browser_context, restore_context.get()));
}
return entries;
}
} // namespace sessions