blob: 34e8e1a7870aab6acb73dd5ece39a9a9c0d8d9f4 [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.
#ifndef CONTENT_BROWSER_LOADER_NAVIGATION_EARLY_HINTS_MANAGER_H_
#define CONTENT_BROWSER_LOADER_NAVIGATION_EARLY_HINTS_MANAGER_H_
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/isolation_info.h"
#include "net/base/request_priority.h"
#include "net/url_request/referrer_policy.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/mojom/early_hints.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
namespace blink {
class ThrottlingURLLoader;
} // namespace blink
namespace network {
namespace mojom {
class NetworkContext;
} // namespace mojom
struct ResourceRequest;
class SharedURLLoaderFactory;
} // namespace network
namespace content {
class BrowserContext;
class StoragePartition;
// Contains parameters to create NavigationEarlyHintsManager.
struct CONTENT_EXPORT NavigationEarlyHintsManagerParams {
NavigationEarlyHintsManagerParams(
const url::Origin& origin,
net::IsolationInfo isolation_info,
mojo::Remote<network::mojom::URLLoaderFactory> loader_factory);
~NavigationEarlyHintsManagerParams();
NavigationEarlyHintsManagerParams(NavigationEarlyHintsManagerParams&&);
NavigationEarlyHintsManagerParams& operator=(
NavigationEarlyHintsManagerParams&&);
NavigationEarlyHintsManagerParams(const NavigationEarlyHintsManagerParams&) =
delete;
NavigationEarlyHintsManagerParams& operator=(
const NavigationEarlyHintsManagerParams&) = delete;
url::Origin origin;
net::IsolationInfo isolation_info;
mojo::Remote<network::mojom::URLLoaderFactory> loader_factory;
};
// Handles 103 Early Hints responses for navigation. Responsible for resource
// hints in Early Hints responses. Created when the first 103 response is
// received and owned by NavigationURLLoaderImpl until the final response to the
// navigation request is received. NavigationURLLoaderImpl transfers the
// ownership of this instance to RenderFrameHostImpl via NavigationRequest when
// the navigation is committed so that this can outlive NavigationURLLoaderImpl
// until inflight preloads finish.
class CONTENT_EXPORT NavigationEarlyHintsManager {
public:
// Contains results of a preload request.
struct CONTENT_EXPORT PreloadedResource {
PreloadedResource();
~PreloadedResource();
PreloadedResource(const PreloadedResource&);
PreloadedResource& operator=(const PreloadedResource&);
// Completion error code. Set only when network request is completed.
absl::optional<int> error_code;
// Optional CORS error details.
absl::optional<network::CorsErrorStatus> cors_error_status;
// True when the preload was canceled. When true, the response was already
// in the disk cache.
bool was_canceled = false;
};
using PreloadedResources = base::flat_map<GURL, PreloadedResource>;
NavigationEarlyHintsManager(BrowserContext& browser_context,
StoragePartition& storage_partition,
int frame_tree_node_id,
NavigationEarlyHintsManagerParams params);
~NavigationEarlyHintsManager();
NavigationEarlyHintsManager(const NavigationEarlyHintsManager&) = delete;
NavigationEarlyHintsManager& operator=(const NavigationEarlyHintsManager&) =
delete;
NavigationEarlyHintsManager(NavigationEarlyHintsManager&&) = delete;
NavigationEarlyHintsManager& operator=(NavigationEarlyHintsManager&&) =
delete;
// Handles an Early Hints response. Can be called multiple times during a
// navigation. When `early_hints` contains a preload Link header, starts
// preloading it if preloading hasn't started for the same URL.
void HandleEarlyHints(network::mojom::EarlyHintsPtr early_hints,
const network::ResourceRequest& request_for_navigation);
// True when at least one preload or preconnect Link header was received via
// Early Hints responses for main frame navigation.
bool WasResourceHintsReceived() const;
std::vector<GURL> TakePreloadedResourceURLs();
// True when there are at least one inflight preloads.
bool HasInflightPreloads() const;
void WaitForPreloadsFinishedForTesting(
base::OnceCallback<void(PreloadedResources)> callback);
void SetNetworkContextForTesting(
network::mojom::NetworkContext* network_context);
private:
FRIEND_TEST_ALL_PREFIXES(NavigationEarlyHintsManagerTest, PreloadPriority);
class PreloadURLLoaderClient;
struct PreconnectEntry;
network::mojom::NetworkContext* GetNetworkContext();
void MaybePreconnect(const network::mojom::LinkHeaderPtr& link);
void MaybePreloadHintedResource(
const network::mojom::LinkHeaderPtr& link,
const network::ResourceRequest& request_for_navigation,
const std::vector<network::mojom::ContentSecurityPolicyPtr>&
content_security_policies,
net::ReferrerPolicy referrer_policy);
// Determines whether resource hints like preload and preconnect should be
// handled or not.
bool ShouldHandleResourceHints(const network::mojom::LinkHeaderPtr& link);
void OnPreloadComplete(const GURL& url, const PreloadedResource& result);
static net::RequestPriority CalculateRequestPriority(
const network::mojom::LinkHeaderPtr& link);
const raw_ref<BrowserContext> browser_context_;
const raw_ref<StoragePartition> storage_partition_;
const int frame_tree_node_id_;
mojo::Remote<network::mojom::URLLoaderFactory> loader_factory_;
// This needs to be declared last because it holds a pointer on
// `loader_factory`, and thus needs to be destroyed before factory gets
// destroyed.
scoped_refptr<network::SharedURLLoaderFactory> shared_loader_factory_;
const url::Origin origin_;
const net::IsolationInfo isolation_info_;
base::flat_set<PreconnectEntry> preconnect_entries_;
struct InflightPreload {
InflightPreload(std::unique_ptr<blink::ThrottlingURLLoader> loader,
std::unique_ptr<PreloadURLLoaderClient> client);
~InflightPreload();
InflightPreload(const InflightPreload&) = delete;
InflightPreload& operator=(const InflightPreload&) = delete;
InflightPreload(InflightPreload&&) = delete;
InflightPreload& operator=(InflightPreload&&) = delete;
// `loader` holds a raw_ptr on `client`, so it needs to be declared last to
// avoid holding a dangling reference to `client` at destruction.
std::unique_ptr<PreloadURLLoaderClient> client;
std::unique_ptr<blink::ThrottlingURLLoader> loader;
};
// Using flat_map because the number of preloads are expected to be small.
// Early Hints preloads should be requested for critical subresources such as
// style sheets and fonts.
base::flat_map<GURL, std::unique_ptr<InflightPreload>> inflight_preloads_;
PreloadedResources preloaded_resources_;
std::vector<GURL> preloaded_urls_;
// Set to true when HandleEarlyHints() is called for the first time. Used to
// ignore following responses.
bool was_first_early_hints_received_ = false;
// Set to true when preload or preconnect Link headers are received. Used for
// metrics recording.
bool was_resource_hints_received_ = false;
base::OnceCallback<void(PreloadedResources)>
preloads_completion_callback_for_testing_;
raw_ptr<network::mojom::NetworkContext> network_context_for_testing_ =
nullptr;
};
} // namespace content
#endif // CONTENT_BROWSER_LOADER_NAVIGATION_EARLY_HINTS_MANAGER_H_