blob: 8ef971053d3cf9118c501e08a70d9f560c24552f [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/loader/object_navigation_fallback_body_loader.h"
#include <string>
#include <utility>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "content/browser/loader/resource_timing_utils.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_frame_host_manager.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/system/data_pipe_drainer.h"
#include "net/http/http_response_info.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/cpp/record_ontransfersizeupdate_utils.h"
#include "services/network/public/mojom/early_hints.mojom.h"
#include "services/network/public/mojom/timing_allow_origin.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_util.h"
namespace content {
namespace {
std::string ExtractServerTimingValueIfNeeded(
const network::mojom::URLResponseHead& response_head) {
std::string value;
if (!response_head.timing_allow_passed)
return value;
// Note: the renderer will be responsible for parsing the actual server
// timing values.
response_head.headers->GetNormalizedHeader("Server-Timing", &value);
return value;
}
} // namespace
NAVIGATION_HANDLE_USER_DATA_KEY_IMPL(ObjectNavigationFallbackBodyLoader);
// static
void ObjectNavigationFallbackBodyLoader::CreateAndStart(
NavigationRequest& navigation_request,
const blink::mojom::CommonNavigationParams& common_params,
const blink::mojom::CommitNavigationParams& commit_params,
const network::mojom::URLResponseHead& response_head,
mojo::ScopedDataPipeConsumerHandle response_body,
network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
base::OnceClosure completion_closure) {
// This should only be called for HTTP errors.
DCHECK(response_head.headers);
RenderFrameHostImpl* render_frame_host =
navigation_request.frame_tree_node()->current_frame_host();
// A frame owned by <object> should always have a parent.
DCHECK(render_frame_host->GetParent());
// It's safe to snapshot the parent origin in the calculation here; if the
// parent frame navigates, `render_frame_host_` will be deleted, which
// triggers deletion of `this`, cancelling all remaining work.
blink::mojom::ResourceTimingInfoPtr timing_info =
GenerateResourceTimingForNavigation(
render_frame_host->GetParent()->GetLastCommittedOrigin(),
common_params, commit_params, response_head,
blink::mojom::RequestContextType::OBJECT);
std::string server_timing_value =
ExtractServerTimingValueIfNeeded(response_head);
CreateForNavigationHandle(
navigation_request, std::move(timing_info),
std::move(server_timing_value), std::move(response_body),
std::move(url_loader_client_endpoints), std::move(completion_closure));
}
ObjectNavigationFallbackBodyLoader::~ObjectNavigationFallbackBodyLoader() {}
ObjectNavigationFallbackBodyLoader::ObjectNavigationFallbackBodyLoader(
NavigationHandle& navigation_handle,
blink::mojom::ResourceTimingInfoPtr timing_info,
std::string server_timing_value,
mojo::ScopedDataPipeConsumerHandle response_body,
network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
base::OnceClosure completion_closure)
: navigation_request_(static_cast<NavigationRequest&>(navigation_handle)),
url_loader_(std::move(url_loader_client_endpoints->url_loader)),
url_loader_client_receiver_(
this,
std::move(url_loader_client_endpoints->url_loader_client)),
response_body_drainer_(
std::make_unique<mojo::DataPipeDrainer>(this,
std::move(response_body))),
timing_info_(std::move(timing_info)),
server_timing_value_(std::move(server_timing_value)),
completion_closure_(std::move(completion_closure)) {
// Unretained is safe; `url_loader_` is owned by `this` and will not dispatch
// callbacks after it is destroyed.
url_loader_client_receiver_.set_disconnect_handler(
base::BindOnce(&ObjectNavigationFallbackBodyLoader::BodyLoadFailed,
base::Unretained(this)));
}
void ObjectNavigationFallbackBodyLoader::BodyLoadFailed() {
// At this point, `this` is done and the associated NavigationRequest and
// `this` must be cleaned up, no matter what else happens. Running
// `completion_closure_` will delete the NavigationRequest, which will delete
// `this`.
base::ScopedClosureRunner cleanup(std::move(completion_closure_));
// The endpoint for the URL loader client was closed before the body load
// completed. This is considered failure, so trigger the fallback content, but
// without any timing info, since it can't be calculated.
navigation_request_->RenderFallbackContentForObjectTag();
}
void ObjectNavigationFallbackBodyLoader::OnReceiveEarlyHints(
network::mojom::EarlyHintsPtr) {
// Should have already happened.
NOTREACHED();
}
void ObjectNavigationFallbackBodyLoader::OnReceiveResponse(
network::mojom::URLResponseHeadPtr,
mojo::ScopedDataPipeConsumerHandle body,
absl::optional<mojo_base::BigBuffer> cached_metadata) {
// Should have already happened.
NOTREACHED();
}
void ObjectNavigationFallbackBodyLoader::OnReceiveRedirect(
const net::RedirectInfo&,
network::mojom::URLResponseHeadPtr) {
// Should have already happened.
NOTREACHED();
}
void ObjectNavigationFallbackBodyLoader::OnUploadProgress(
int64_t current_position,
int64_t total_size,
OnUploadProgressCallback) {
// Should have already happened.
NOTREACHED();
}
void ObjectNavigationFallbackBodyLoader::OnTransferSizeUpdated(
int32_t transfer_size_diff) {
// Not needed so implementation omitted.
network::RecordOnTransferSizeUpdatedUMA(
network::OnTransferSizeUpdatedFrom::kObjectNavigationFallbackBodyLoader);
}
void ObjectNavigationFallbackBodyLoader::OnComplete(
const network::URLLoaderCompletionStatus& status) {
response_body_drainer_.reset();
// At this point, `this` is done and the associated NavigationRequest and
// `this` must be cleaned up, no matter what else happens. Running
// `completion_closure_` will delete the NavigationRequest, which will delete
// `this`.
base::ScopedClosureRunner cleanup(std::move(completion_closure_));
timing_info_->response_end = status.completion_time;
timing_info_->encoded_body_size = status.encoded_body_length;
timing_info_->decoded_body_size = status.decoded_body_length;
RenderFrameHostManager* render_manager =
navigation_request_->frame_tree_node()->render_manager();
if (RenderFrameProxyHost* proxy = render_manager->GetProxyToParent()) {
if (proxy->is_render_frame_proxy_live()) {
proxy->GetAssociatedRemoteFrame()
->RenderFallbackContentWithResourceTiming(std::move(timing_info_),
server_timing_value_);
}
} else {
render_manager->current_frame_host()
->GetAssociatedLocalFrame()
->RenderFallbackContentWithResourceTiming(std::move(timing_info_),
server_timing_value_);
}
}
void ObjectNavigationFallbackBodyLoader::OnDataAvailable(const void* data,
size_t num_bytes) {}
void ObjectNavigationFallbackBodyLoader::OnDataComplete() {}
} // namespace content