Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2019 The Chromium Authors |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "content/browser/client_hints/client_hints.h" |
| 6 | |
| 7 | #include <algorithm> |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 8 | #include <optional> |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 9 | #include <string> |
| 10 | |
Ari Chivukula | 3bba0d0e | 2023-05-12 17:26:39 | [diff] [blame] | 11 | #include "base/check_is_test.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 12 | #include "base/command_line.h" |
Ali Beyad | 49055ed | 2021-10-11 15:33:33 | [diff] [blame] | 13 | #include "base/containers/contains.h" |
Ari Chivukula | 3bba0d0e | 2023-05-12 17:26:39 | [diff] [blame] | 14 | #include "base/dcheck_is_on.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 15 | #include "base/feature_list.h" |
| 16 | #include "base/metrics/field_trial_params.h" |
Ali Beyad | 8765eda | 2022-01-24 22:32:10 | [diff] [blame] | 17 | #include "base/metrics/histogram_functions.h" |
Peter Kasting | f5d6dd9 | 2020-08-17 18:53:30 | [diff] [blame] | 18 | #include "base/numerics/safe_conversions.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 19 | #include "base/rand_util.h" |
| 20 | #include "base/strings/string_number_conversions.h" |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 21 | #include "base/strings/string_util.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 22 | #include "base/time/time.h" |
| 23 | #include "build/build_config.h" |
Maks Orlovich | 85a6fd6 | 2020-05-05 23:02:14 | [diff] [blame] | 24 | #include "content/browser/devtools/devtools_instrumentation.h" |
Lingqi Chi | 59b6b42a | 2022-08-23 02:25:34 | [diff] [blame] | 25 | #include "content/browser/preloading/prerender/prerender_host.h" |
danakj | c027008 | 2020-09-15 22:28:00 | [diff] [blame] | 26 | #include "content/browser/renderer_host/frame_tree.h" |
| 27 | #include "content/browser/renderer_host/frame_tree_node.h" |
| 28 | #include "content/browser/renderer_host/navigator.h" |
| 29 | #include "content/browser/renderer_host/navigator_delegate.h" |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 30 | #include "content/browser/renderer_host/render_view_host_impl.h" |
Ali Beyad | 49055ed | 2021-10-11 15:33:33 | [diff] [blame] | 31 | #include "content/public/browser/browser_context.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 32 | #include "content/public/browser/browser_thread.h" |
Ali Beyad | 49055ed | 2021-10-11 15:33:33 | [diff] [blame] | 33 | #include "content/public/browser/client_hints_controller_delegate.h" |
Ari Chivukula | 116f0fc4 | 2022-04-13 23:26:18 | [diff] [blame] | 34 | #include "content/public/browser/content_browser_client.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 35 | #include "content/public/browser/host_zoom_map.h" |
Rakina Zata Amni | 347b7090 | 2020-07-22 10:49:04 | [diff] [blame] | 36 | #include "content/public/browser/web_contents.h" |
Clark DuVall | 04894d7f | 2020-04-07 23:54:21 | [diff] [blame] | 37 | #include "content/public/common/content_client.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 38 | #include "content/public/common/content_features.h" |
| 39 | #include "content/public/common/content_switches.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 40 | #include "net/base/url_util.h" |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 41 | #include "net/http/http_request_headers.h" |
| 42 | #include "net/http/http_response_headers.h" |
Aaron Tagliaboschi | 9f01b68 | 2020-05-05 21:03:17 | [diff] [blame] | 43 | #include "net/http/structured_headers.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 44 | #include "net/nqe/effective_connection_type.h" |
| 45 | #include "net/nqe/network_quality_estimator_params.h" |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 46 | #include "services/network/public/cpp/client_hints.h" |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 47 | #include "services/network/public/cpp/is_potentially_trustworthy.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 48 | #include "services/network/public/cpp/network_quality_tracker.h" |
Sandor Major | 23d05255 | 2025-02-26 20:28:59 | [diff] [blame] | 49 | #include "services/network/public/cpp/permissions_policy/client_hints_permissions_policy_mapping.h" |
Sandor Major | 07aa8b06 | 2025-02-28 17:24:35 | [diff] [blame] | 50 | #include "services/network/public/cpp/permissions_policy/permissions_policy.h" |
Sandor Major | 878f835 | 2025-02-18 20:16:02 | [diff] [blame] | 51 | #include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h" |
| 52 | #include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h" |
Ali Beyad | 77dba95 | 2021-08-25 20:52:58 | [diff] [blame] | 53 | #include "services/network/public/mojom/web_client_hints_types.mojom-shared.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 54 | #include "third_party/blink/public/common/client_hints/client_hints.h" |
Ali Beyad | a6b0fb6f | 2021-07-28 00:13:24 | [diff] [blame] | 55 | #include "third_party/blink/public/common/client_hints/enabled_client_hints.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 56 | #include "third_party/blink/public/common/device_memory/approximated_device_memory.h" |
Ali Beyad | a6b0fb6f | 2021-07-28 00:13:24 | [diff] [blame] | 57 | #include "third_party/blink/public/common/features.h" |
danakj | 938b37a6 | 2019-09-24 18:35:54 | [diff] [blame] | 58 | #include "third_party/blink/public/common/page/page_zoom.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 59 | #include "third_party/blink/public/common/user_agent/user_agent_metadata.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 60 | #include "ui/display/display.h" |
| 61 | #include "ui/display/screen.h" |
Ali Beyad | 49055ed | 2021-10-11 15:33:33 | [diff] [blame] | 62 | #include "url/origin.h" |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 63 | #include "url/url_constants.h" |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 64 | |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 65 | namespace content { |
| 66 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 67 | namespace { |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 68 | using ::network::mojom::WebClientHintsType; |
| 69 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 70 | uint8_t randomization_salt = 0; |
| 71 | |
| 72 | constexpr size_t kMaxRandomNumbers = 21; |
| 73 | |
| 74 | // Returns the randomization salt (weak and insecure) that should be used when |
| 75 | // adding noise to the network quality metrics. This is known only to the |
| 76 | // device, and is generated only once. This makes it possible to add the same |
| 77 | // amount of noise for a given origin. |
| 78 | uint8_t RandomizationSalt() { |
| 79 | if (randomization_salt == 0) |
| 80 | randomization_salt = base::RandInt(1, kMaxRandomNumbers); |
| 81 | DCHECK_LE(1, randomization_salt); |
| 82 | DCHECK_GE(kMaxRandomNumbers, randomization_salt); |
| 83 | return randomization_salt; |
| 84 | } |
| 85 | |
| 86 | double GetRandomMultiplier(const std::string& host) { |
| 87 | // The random number should be a function of the hostname to reduce |
| 88 | // cross-origin fingerprinting. The random number should also be a function |
| 89 | // of randomized salt which is known only to the device. This prevents |
| 90 | // origin from removing noise from the estimates. |
| 91 | unsigned hash = std::hash<std::string>{}(host) + RandomizationSalt(); |
| 92 | double random_multiplier = |
| 93 | 0.9 + static_cast<double>((hash % kMaxRandomNumbers)) * 0.01; |
| 94 | DCHECK_LE(0.90, random_multiplier); |
| 95 | DCHECK_GE(1.10, random_multiplier); |
| 96 | return random_multiplier; |
| 97 | } |
| 98 | |
| 99 | unsigned long RoundRtt(const std::string& host, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 100 | const std::optional<base::TimeDelta>& rtt) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 101 | if (!rtt.has_value()) { |
| 102 | // RTT is unavailable. So, return the fastest value. |
| 103 | return 0; |
| 104 | } |
| 105 | |
Peter Kasting | f5d6dd9 | 2020-08-17 18:53:30 | [diff] [blame] | 106 | // Limit the maximum reported value and the granularity to reduce |
| 107 | // fingerprinting. |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 108 | constexpr base::TimeDelta kMaxRtt = base::Seconds(3); |
| 109 | constexpr base::TimeDelta kGranularity = base::Milliseconds(50); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 110 | |
Peter Kasting | f5d6dd9 | 2020-08-17 18:53:30 | [diff] [blame] | 111 | const base::TimeDelta modified_rtt = |
| 112 | std::min(rtt.value() * GetRandomMultiplier(host), kMaxRtt); |
| 113 | DCHECK_GE(modified_rtt, base::TimeDelta()); |
Peter Kasting | fbdf495 | 2020-08-14 03:07:06 | [diff] [blame] | 114 | return modified_rtt.RoundToMultiple(kGranularity).InMilliseconds(); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | double RoundKbpsToMbps(const std::string& host, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 118 | const std::optional<int32_t>& downlink_kbps) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 119 | // Limit the size of the buckets and the maximum reported value to reduce |
| 120 | // fingerprinting. |
| 121 | static const size_t kGranularityKbps = 50; |
| 122 | static const double kMaxDownlinkKbps = 10.0 * 1000; |
| 123 | |
| 124 | // If downlink is unavailable, return the fastest value. |
| 125 | double randomized_downlink_kbps = downlink_kbps.value_or(kMaxDownlinkKbps); |
| 126 | randomized_downlink_kbps *= GetRandomMultiplier(host); |
| 127 | |
| 128 | randomized_downlink_kbps = |
| 129 | std::min(randomized_downlink_kbps, kMaxDownlinkKbps); |
| 130 | |
| 131 | DCHECK_LE(0, randomized_downlink_kbps); |
| 132 | DCHECK_GE(kMaxDownlinkKbps, randomized_downlink_kbps); |
| 133 | // Round down to the nearest kGranularityKbps kbps value. |
| 134 | double downlink_kbps_rounded = |
| 135 | std::round(randomized_downlink_kbps / kGranularityKbps) * |
| 136 | kGranularityKbps; |
| 137 | |
| 138 | // Convert from Kbps to Mbps. |
| 139 | return downlink_kbps_rounded / 1000; |
| 140 | } |
| 141 | |
| 142 | double GetDeviceScaleFactor() { |
| 143 | double device_scale_factor = 1.0; |
| 144 | if (display::Screen::GetScreen()) { |
| 145 | device_scale_factor = |
| 146 | display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor(); |
| 147 | } |
| 148 | DCHECK_LT(0.0, device_scale_factor); |
| 149 | return device_scale_factor; |
| 150 | } |
| 151 | |
| 152 | // Returns the zoom factor for a given |url|. |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 153 | double GetZoomFactor(BrowserContext* context, const GURL& url) { |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 154 | double zoom_level = HostZoomMap::GetDefaultForBrowserContext(context) |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 155 | ->GetZoomLevelForHostAndScheme( |
| 156 | url.scheme(), net::GetHostOrSpecFromURL(url)); |
| 157 | |
| 158 | if (zoom_level == 0.0) { |
| 159 | // Get default zoom level. |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 160 | zoom_level = HostZoomMap::GetDefaultForBrowserContext(context) |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 161 | ->GetDefaultZoomLevel(); |
| 162 | } |
| 163 | |
Stefan Zager | 036a35c | 2024-06-13 20:53:34 | [diff] [blame] | 164 | return blink::ZoomLevelToZoomFactor(zoom_level); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | // Returns a string corresponding to |value|. The returned string satisfies |
| 168 | // ABNF: 1*DIGIT [ "." 1*DIGIT ] |
| 169 | std::string DoubleToSpecCompliantString(double value) { |
| 170 | DCHECK_LE(0.0, value); |
| 171 | std::string result = base::NumberToString(value); |
| 172 | DCHECK(!result.empty()); |
| 173 | if (value >= 1.0) |
| 174 | return result; |
| 175 | |
| 176 | DCHECK_LE(0.0, value); |
| 177 | DCHECK_GT(1.0, value); |
| 178 | |
| 179 | // Check if there is at least one character before period. |
| 180 | if (result.at(0) != '.') |
| 181 | return result; |
| 182 | |
| 183 | // '.' is the first character in |result|. Prefix one digit before the |
| 184 | // period to make it spec compliant. |
| 185 | return "0" + result; |
| 186 | } |
| 187 | |
| 188 | // Return the effective connection type value overridden for web APIs. |
| 189 | // If no override value has been set, a null value is returned. |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 190 | std::optional<net::EffectiveConnectionType> |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 191 | GetWebHoldbackEffectiveConnectionType() { |
| 192 | if (!base::FeatureList::IsEnabled( |
| 193 | features::kNetworkQualityEstimatorWebHoldback)) { |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 194 | return std::nullopt; |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 195 | } |
| 196 | std::string effective_connection_type_param = |
| 197 | base::GetFieldTrialParamValueByFeature( |
| 198 | features::kNetworkQualityEstimatorWebHoldback, |
| 199 | "web_effective_connection_type_override"); |
| 200 | |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 201 | std::optional<net::EffectiveConnectionType> effective_connection_type = |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 202 | net::GetEffectiveConnectionTypeForName(effective_connection_type_param); |
| 203 | DCHECK(effective_connection_type_param.empty() || effective_connection_type); |
| 204 | |
| 205 | if (!effective_connection_type) |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 206 | return std::nullopt; |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 207 | DCHECK_NE(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, |
| 208 | effective_connection_type.value()); |
| 209 | return effective_connection_type; |
| 210 | } |
| 211 | |
| 212 | void SetHeaderToDouble(net::HttpRequestHeaders* headers, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 213 | WebClientHintsType client_hint_type, |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 214 | double value) { |
Ari Chivukula | 51baf9b | 2021-09-13 20:01:49 | [diff] [blame] | 215 | headers->SetHeader(network::GetClientHintToNameMap().at(client_hint_type), |
| 216 | DoubleToSpecCompliantString(value)); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 217 | } |
| 218 | |
| 219 | void SetHeaderToInt(net::HttpRequestHeaders* headers, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 220 | WebClientHintsType client_hint_type, |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 221 | double value) { |
Ari Chivukula | 51baf9b | 2021-09-13 20:01:49 | [diff] [blame] | 222 | headers->SetHeader(network::GetClientHintToNameMap().at(client_hint_type), |
| 223 | base::NumberToString(std::round(value))); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 224 | } |
| 225 | |
| 226 | void SetHeaderToString(net::HttpRequestHeaders* headers, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 227 | WebClientHintsType client_hint_type, |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 228 | const std::string& value) { |
Ari Chivukula | 51baf9b | 2021-09-13 20:01:49 | [diff] [blame] | 229 | headers->SetHeader(network::GetClientHintToNameMap().at(client_hint_type), |
| 230 | value); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 231 | } |
| 232 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 233 | void RemoveClientHintHeader(WebClientHintsType client_hint_type, |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 234 | net::HttpRequestHeaders* headers) { |
Ari Chivukula | 51baf9b | 2021-09-13 20:01:49 | [diff] [blame] | 235 | headers->RemoveHeader(network::GetClientHintToNameMap().at(client_hint_type)); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 236 | } |
| 237 | |
Ari Chivukula | 6424811 | 2021-10-07 06:00:56 | [diff] [blame] | 238 | void AddDeviceMemoryHeader(net::HttpRequestHeaders* headers, |
| 239 | bool use_deprecated_version = false) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 240 | DCHECK(headers); |
| 241 | blink::ApproximatedDeviceMemory::Initialize(); |
| 242 | const float device_memory = |
| 243 | blink::ApproximatedDeviceMemory::GetApproximatedDeviceMemory(); |
| 244 | DCHECK_LT(0.0, device_memory); |
Ari Chivukula | 6424811 | 2021-10-07 06:00:56 | [diff] [blame] | 245 | SetHeaderToDouble(headers, |
| 246 | use_deprecated_version |
| 247 | ? WebClientHintsType::kDeviceMemory_DEPRECATED |
| 248 | : WebClientHintsType::kDeviceMemory, |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 249 | device_memory); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 250 | } |
| 251 | |
| 252 | void AddDPRHeader(net::HttpRequestHeaders* headers, |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 253 | BrowserContext* context, |
Ari Chivukula | 6424811 | 2021-10-07 06:00:56 | [diff] [blame] | 254 | const GURL& url, |
| 255 | bool use_deprecated_version = false) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 256 | DCHECK(headers); |
| 257 | DCHECK(context); |
| 258 | double device_scale_factor = GetDeviceScaleFactor(); |
| 259 | double zoom_factor = GetZoomFactor(context, url); |
Ari Chivukula | 6424811 | 2021-10-07 06:00:56 | [diff] [blame] | 260 | SetHeaderToDouble(headers, |
| 261 | use_deprecated_version ? WebClientHintsType::kDpr_DEPRECATED |
| 262 | : WebClientHintsType::kDpr, |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 263 | device_scale_factor * zoom_factor); |
| 264 | } |
| 265 | |
Ari Chivukula | 116f0fc4 | 2022-04-13 23:26:18 | [diff] [blame] | 266 | void AddSaveDataHeader(net::HttpRequestHeaders* headers, |
| 267 | BrowserContext* context) { |
| 268 | DCHECK(headers); |
| 269 | DCHECK(context); |
| 270 | // Unlike other client hints, this one is only sent when it has a value. |
| 271 | if (GetContentClient()->browser()->IsDataSaverEnabled(context)) |
| 272 | SetHeaderToString(headers, WebClientHintsType::kSaveData, "on"); |
| 273 | } |
| 274 | |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 275 | RenderWidgetHostView* GetRenderWidgetHostViewFromFrameTreeNode( |
| 276 | FrameTreeNode* frame_tree_node) { |
| 277 | if (!frame_tree_node || !frame_tree_node->current_frame_host()) |
| 278 | return nullptr; |
| 279 | |
Max Curran | 0d344cfe | 2023-05-25 18:31:18 | [diff] [blame] | 280 | return frame_tree_node->current_frame_host()->GetMainFrame()->GetView(); |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 281 | } |
| 282 | |
| 283 | gfx::Size GetViewportSize(FrameTreeNode* frame_tree_node, |
| 284 | ClientHintsControllerDelegate* delegate) { |
Ari Chivukula | 3bba0d0e | 2023-05-12 17:26:39 | [diff] [blame] | 285 | #if DCHECK_IS_ON() |
| 286 | // In some tests we need to force an empty viewport size. |
| 287 | if (delegate->ShouldForceEmptyViewportSize()) { |
| 288 | CHECK_IS_TEST(); |
| 289 | return gfx::Size(); |
| 290 | } |
| 291 | #endif |
| 292 | |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 293 | // If possible, return the current viewport size. |
| 294 | RenderWidgetHostView* view = |
| 295 | GetRenderWidgetHostViewFromFrameTreeNode(frame_tree_node); |
| 296 | if (view) { |
| 297 | return view->GetVisibleViewportSize(); |
| 298 | } |
| 299 | |
| 300 | // Otherwise, use the cached viewport size if it is valid (both dimensions are |
| 301 | // greater than zero). |
| 302 | gfx::Size cached_viewport_size = |
| 303 | delegate->GetMostRecentMainFrameViewportSize(); |
| 304 | if (cached_viewport_size.width() > 0 && cached_viewport_size.height() > 0) { |
| 305 | return cached_viewport_size; |
| 306 | } |
| 307 | |
Ari Chivukula | 3bba0d0e | 2023-05-12 17:26:39 | [diff] [blame] | 308 | // We used to return the display size here as a last resort if above methods |
| 309 | // didn't work, but this was so inaccurate as to be useless. Short of trying |
| 310 | // to build a more extensive caching method or restructuring the calculation |
| 311 | // path to make the estimated size available here, we simply return 0. |
| 312 | // Further context can be found in crbug.com/1430903. |
Max Curran | f445edad | 2022-11-29 23:39:43 | [diff] [blame] | 313 | // viewport sizes already. |
Ari Chivukula | 3bba0d0e | 2023-05-12 17:26:39 | [diff] [blame] | 314 | return gfx::Size(); |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 315 | } |
| 316 | |
| 317 | gfx::Size GetScaledViewportSize(BrowserContext* context, |
| 318 | const GURL& url, |
| 319 | FrameTreeNode* frame_tree_node, |
| 320 | ClientHintsControllerDelegate* delegate) { |
| 321 | gfx::Size viewport_size = GetViewportSize(frame_tree_node, delegate); |
Findit | c5fc7bc6 | 2020-11-12 20:48:33 | [diff] [blame] | 322 | |
Xiaohan Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 323 | #if BUILDFLAG(IS_ANDROID) |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 324 | // On Android, the viewport is scaled so the width is 980. See |
| 325 | // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/viewportAndroid.css. |
Alison Gale | 81f4f2c7 | 2024-04-22 19:33:31 | [diff] [blame] | 326 | // TODO(crbug.com/40196453): Improve the usefulness of the viewport client |
| 327 | // hints for navigation requests. |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 328 | if (viewport_size.width() > 0) { |
| 329 | viewport_size = |
| 330 | ScaleToRoundedSize(viewport_size, 980.0 / viewport_size.width()); |
| 331 | } |
Mark Schillaci | 1363e4a | 2021-10-04 19:10:09 | [diff] [blame] | 332 | #endif |
| 333 | |
Max Curran | f445edad | 2022-11-29 23:39:43 | [diff] [blame] | 334 | double zoom_factor = GetZoomFactor(context, url); |
| 335 | if (zoom_factor > 0) { |
| 336 | viewport_size = ScaleToRoundedSize(viewport_size, 1.0 / zoom_factor); |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 337 | } |
| 338 | return viewport_size; |
| 339 | } |
| 340 | |
| 341 | void AddViewportWidthHeader(net::HttpRequestHeaders* headers, |
| 342 | BrowserContext* context, |
| 343 | const GURL& url, |
| 344 | FrameTreeNode* frame_tree_node, |
| 345 | ClientHintsControllerDelegate* delegate, |
| 346 | bool use_deprecated_version = false) { |
| 347 | DCHECK(headers); |
| 348 | DCHECK(context); |
| 349 | |
| 350 | gfx::Size viewport_size = |
| 351 | GetScaledViewportSize(context, url, frame_tree_node, delegate); |
| 352 | |
Ari Chivukula | 3bba0d0e | 2023-05-12 17:26:39 | [diff] [blame] | 353 | // The width cannot be less than 0, but if it is zero that means we could not |
| 354 | // determine the width and should omit the header. |
| 355 | DCHECK_LE(0, viewport_size.width()); |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 356 | if (viewport_size.width() > 0) { |
Ari Chivukula | 6424811 | 2021-10-07 06:00:56 | [diff] [blame] | 357 | SetHeaderToInt(headers, |
| 358 | use_deprecated_version |
| 359 | ? WebClientHintsType::kViewportWidth_DEPRECATED |
| 360 | : WebClientHintsType::kViewportWidth, |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 361 | viewport_size.width()); |
Avi Drissman | b5b854a8 | 2021-09-08 15:44:20 | [diff] [blame] | 362 | } |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 363 | } |
| 364 | |
Max Curran | 99cbec149 | 2021-08-16 19:54:26 | [diff] [blame] | 365 | void AddViewportHeightHeader(net::HttpRequestHeaders* headers, |
| 366 | BrowserContext* context, |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 367 | const GURL& url, |
| 368 | FrameTreeNode* frame_tree_node, |
| 369 | ClientHintsControllerDelegate* delegate) { |
Max Curran | 99cbec149 | 2021-08-16 19:54:26 | [diff] [blame] | 370 | DCHECK(headers); |
| 371 | DCHECK(context); |
| 372 | |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 373 | gfx::Size viewport_size = |
| 374 | GetScaledViewportSize(context, url, frame_tree_node, delegate); |
Max Curran | 99cbec149 | 2021-08-16 19:54:26 | [diff] [blame] | 375 | |
Ari Chivukula | 3bba0d0e | 2023-05-12 17:26:39 | [diff] [blame] | 376 | // The height cannot be less than 0, but if it is zero that means we could not |
| 377 | // determine the height and should omit the header. |
| 378 | DCHECK_LE(0, viewport_size.height()); |
| 379 | if (viewport_size.height() > 0) { |
| 380 | SetHeaderToInt(headers, network::mojom::WebClientHintsType::kViewportHeight, |
| 381 | viewport_size.height()); |
| 382 | } |
Max Curran | 99cbec149 | 2021-08-16 19:54:26 | [diff] [blame] | 383 | } |
| 384 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 385 | void AddRttHeader(net::HttpRequestHeaders* headers, |
| 386 | network::NetworkQualityTracker* network_quality_tracker, |
| 387 | const GURL& url) { |
| 388 | DCHECK(headers); |
| 389 | |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 390 | std::optional<net::EffectiveConnectionType> web_holdback_ect = |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 391 | GetWebHoldbackEffectiveConnectionType(); |
| 392 | |
| 393 | base::TimeDelta http_rtt; |
| 394 | if (web_holdback_ect.has_value()) { |
| 395 | http_rtt = net::NetworkQualityEstimatorParams::GetDefaultTypicalHttpRtt( |
| 396 | web_holdback_ect.value()); |
| 397 | } else if (network_quality_tracker) { |
| 398 | http_rtt = network_quality_tracker->GetHttpRTT(); |
| 399 | } else { |
| 400 | http_rtt = net::NetworkQualityEstimatorParams::GetDefaultTypicalHttpRtt( |
| 401 | net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN); |
| 402 | } |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 403 | SetHeaderToInt(headers, WebClientHintsType::kRtt_DEPRECATED, |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 404 | RoundRtt(url.host(), http_rtt)); |
| 405 | } |
| 406 | |
| 407 | void AddDownlinkHeader(net::HttpRequestHeaders* headers, |
| 408 | network::NetworkQualityTracker* network_quality_tracker, |
| 409 | const GURL& url) { |
| 410 | DCHECK(headers); |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 411 | std::optional<net::EffectiveConnectionType> web_holdback_ect = |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 412 | GetWebHoldbackEffectiveConnectionType(); |
| 413 | |
| 414 | int32_t downlink_throughput_kbps; |
| 415 | |
| 416 | if (web_holdback_ect.has_value()) { |
| 417 | downlink_throughput_kbps = |
| 418 | net::NetworkQualityEstimatorParams::GetDefaultTypicalDownlinkKbps( |
| 419 | web_holdback_ect.value()); |
| 420 | } else if (network_quality_tracker) { |
| 421 | downlink_throughput_kbps = |
| 422 | network_quality_tracker->GetDownstreamThroughputKbps(); |
| 423 | } else { |
| 424 | downlink_throughput_kbps = |
| 425 | net::NetworkQualityEstimatorParams::GetDefaultTypicalDownlinkKbps( |
| 426 | net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN); |
| 427 | } |
| 428 | |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 429 | SetHeaderToDouble(headers, WebClientHintsType::kDownlink_DEPRECATED, |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 430 | RoundKbpsToMbps(url.host(), downlink_throughput_kbps)); |
| 431 | } |
| 432 | |
| 433 | void AddEctHeader(net::HttpRequestHeaders* headers, |
| 434 | network::NetworkQualityTracker* network_quality_tracker, |
| 435 | const GURL& url) { |
| 436 | DCHECK(headers); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 437 | |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 438 | std::optional<net::EffectiveConnectionType> web_holdback_ect = |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 439 | GetWebHoldbackEffectiveConnectionType(); |
| 440 | |
| 441 | int effective_connection_type; |
| 442 | if (web_holdback_ect.has_value()) { |
| 443 | effective_connection_type = web_holdback_ect.value(); |
| 444 | } else if (network_quality_tracker) { |
| 445 | effective_connection_type = |
| 446 | static_cast<int>(network_quality_tracker->GetEffectiveConnectionType()); |
| 447 | } else { |
| 448 | effective_connection_type = |
| 449 | static_cast<int>(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN); |
| 450 | } |
| 451 | |
| 452 | SetHeaderToString( |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 453 | headers, WebClientHintsType::kEct_DEPRECATED, |
Ari Chivukula | 086d1726 | 2022-10-20 18:24:15 | [diff] [blame] | 454 | network::kWebEffectiveConnectionTypeMapping[effective_connection_type]); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 455 | } |
| 456 | |
François Beaufort | 1b51d06 | 2021-05-18 11:11:23 | [diff] [blame] | 457 | void AddPrefersColorSchemeHeader(net::HttpRequestHeaders* headers, |
| 458 | FrameTreeNode* frame_tree_node) { |
François Beaufort | 34e299e | 2021-05-26 05:54:33 | [diff] [blame] | 459 | if (!frame_tree_node) |
| 460 | return; |
François Beaufort | 1b51d06 | 2021-05-18 11:11:23 | [diff] [blame] | 461 | blink::mojom::PreferredColorScheme preferred_color_scheme = |
François Beaufort | a957a4e | 2022-08-09 09:14:31 | [diff] [blame] | 462 | WebContents::FromRenderFrameHost(frame_tree_node->current_frame_host()) |
| 463 | ->GetOrCreateWebPreferences() |
| 464 | .preferred_color_scheme; |
François Beaufort | 1b51d06 | 2021-05-18 11:11:23 | [diff] [blame] | 465 | bool is_dark_mode = |
| 466 | preferred_color_scheme == blink::mojom::PreferredColorScheme::kDark; |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 467 | SetHeaderToString(headers, WebClientHintsType::kPrefersColorScheme, |
François Beaufort | 378fffd | 2022-09-28 16:48:19 | [diff] [blame] | 468 | is_dark_mode ? network::kPrefersColorSchemeDark |
| 469 | : network::kPrefersColorSchemeLight); |
| 470 | } |
| 471 | |
| 472 | void AddPrefersReducedMotionHeader(net::HttpRequestHeaders* headers, |
| 473 | FrameTreeNode* frame_tree_node) { |
| 474 | if (!frame_tree_node) |
| 475 | return; |
| 476 | bool prefers_reduced_motion = |
| 477 | WebContents::FromRenderFrameHost(frame_tree_node->current_frame_host()) |
| 478 | ->GetOrCreateWebPreferences() |
| 479 | .prefers_reduced_motion; |
| 480 | SetHeaderToString(headers, WebClientHintsType::kPrefersReducedMotion, |
| 481 | prefers_reduced_motion |
| 482 | ? network::kPrefersReducedMotionReduce |
| 483 | : network::kPrefersReducedMotionNoPreference); |
François Beaufort | 1b51d06 | 2021-05-18 11:11:23 | [diff] [blame] | 484 | } |
| 485 | |
Luke Warlow | 8633929 | 2023-08-26 16:55:25 | [diff] [blame] | 486 | void AddPrefersReducedTransparencyHeader(net::HttpRequestHeaders* headers, |
| 487 | FrameTreeNode* frame_tree_node) { |
| 488 | if (!frame_tree_node) { |
| 489 | return; |
| 490 | } |
| 491 | bool prefers_reduced_transparency = |
| 492 | WebContents::FromRenderFrameHost(frame_tree_node->current_frame_host()) |
| 493 | ->GetOrCreateWebPreferences() |
| 494 | .prefers_reduced_transparency; |
| 495 | SetHeaderToString(headers, WebClientHintsType::kPrefersReducedTransparency, |
| 496 | prefers_reduced_transparency |
| 497 | ? network::kPrefersReducedTransparencyReduce |
| 498 | : network::kPrefersReducedTransparencyNoPreference); |
| 499 | } |
| 500 | |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 501 | bool IsValidURLForClientHints(const url::Origin& origin) { |
| 502 | return network::IsOriginPotentiallyTrustworthy(origin); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 503 | } |
| 504 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 505 | void AddUAHeader(net::HttpRequestHeaders* headers, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 506 | WebClientHintsType type, |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 507 | const std::string& value) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 508 | SetHeaderToString(headers, type, value); |
| 509 | } |
| 510 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 511 | // Creates a serialized string header value out of the input type, using |
| 512 | // structured headers as described in |
| 513 | // https://www.rfc-editor.org/rfc/rfc8941.html. |
| 514 | template <typename T> |
| 515 | const std::string SerializeHeaderString(const T& value) { |
Aaron Tagliaboschi | 9f01b68 | 2020-05-05 21:03:17 | [diff] [blame] | 516 | return net::structured_headers::SerializeItem( |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 517 | net::structured_headers::Item(value)) |
Aaron Tagliaboschi | 9f01b68 | 2020-05-05 21:03:17 | [diff] [blame] | 518 | .value_or(std::string()); |
Yoav Weiss | 1aeefa8 | 2020-03-06 12:02:17 | [diff] [blame] | 519 | } |
| 520 | |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 521 | // Captures the state used in applying client hints. |
| 522 | struct ClientHintsExtendedData { |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 523 | ClientHintsExtendedData(const url::Origin& origin, |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 524 | FrameTreeNode* frame_tree_node, |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 525 | ClientHintsControllerDelegate* delegate, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 526 | const std::optional<GURL>& maybe_request_url) |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 527 | : resource_origin(origin) { |
Ian Vollick | 25a9d03 | 2022-04-12 23:20:17 | [diff] [blame] | 528 | // If the current frame is the outermost main frame, the URL wasn't |
| 529 | // committed yet, so in order to get the main frame URL, we should use the |
| 530 | // provided URL instead. Otherwise, the current frame is a subframe and the |
| 531 | // outermost main frame URL was committed, so we can safely get it from it. |
| 532 | // Similarly, an in-navigation outermost main frame doesn't yet have a |
| 533 | // permissions policy. |
| 534 | is_outermost_main_frame = |
| 535 | !frame_tree_node || frame_tree_node->IsOutermostMainFrame(); |
| 536 | if (is_outermost_main_frame) { |
Liam Brady | cfb4410 | 2023-04-25 08:15:43 | [diff] [blame] | 537 | main_frame_origin = resource_origin; |
Ian Vollick | 25a9d03 | 2022-04-12 23:20:17 | [diff] [blame] | 538 | } else if (frame_tree_node->IsInFencedFrameTree()) { |
Alison Gale | 81f4f2c7 | 2024-04-22 19:33:31 | [diff] [blame] | 539 | // TODO(crbug.com/40263100) Add WPT tests and specify the behavior |
Adithya Srinivasan | 39c8191 | 2024-07-11 20:44:21 | [diff] [blame] | 540 | // of client hints delegation for subframes inside FencedFrames. |
Xiaochen Zhou | 86f2e71 | 2023-09-13 19:55:04 | [diff] [blame] | 541 | // Test cases should cover this 3 layers nested frames case, from top to |
| 542 | // bottom: |
| 543 | // 1. Fenced frame. |
| 544 | // 2. Urn iframe. |
| 545 | // 3. Iframe. |
| 546 | // `GetFencedFrameProperties()` called from the iframe returns the |
| 547 | // fenced frame properties from the urn iframe because it does a bottom |
| 548 | // up traversal. |
| 549 | // See crbug.com/1470634. |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 550 | const std::optional<FencedFrameProperties>& fenced_frame_properties = |
Liam Brady | 9543059 | 2023-05-03 18:28:34 | [diff] [blame] | 551 | frame_tree_node->GetFencedFrameProperties(); |
Sandor «Alex» Major | e9545a7 | 2025-01-31 20:40:46 | [diff] [blame] | 552 | base::span<const network::mojom::PermissionsPolicyFeature> permissions; |
Liam Brady | 9543059 | 2023-05-03 18:28:34 | [diff] [blame] | 553 | if (fenced_frame_properties) { |
Garrett Tanzer | 0698070f | 2023-12-12 19:48:20 | [diff] [blame] | 554 | permissions = fenced_frame_properties->effective_enabled_permissions(); |
Liam Brady | 9543059 | 2023-05-03 18:28:34 | [diff] [blame] | 555 | } |
Sandor Major | 07aa8b06 | 2025-02-28 17:24:35 | [diff] [blame] | 556 | permissions_policy = |
| 557 | network::PermissionsPolicy::CreateFixedForFencedFrame( |
| 558 | resource_origin, /*header_policy=*/{}, permissions); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 559 | } else { |
Liam Brady | cfb4410 | 2023-04-25 08:15:43 | [diff] [blame] | 560 | RenderFrameHostImpl* main_frame = |
| 561 | frame_tree_node->frame_tree().GetMainFrame(); |
| 562 | main_frame_origin = main_frame->GetLastCommittedOrigin(); |
Sandor Major | 07aa8b06 | 2025-02-28 17:24:35 | [diff] [blame] | 563 | permissions_policy = network::PermissionsPolicy::CopyStateFrom( |
Sandor Major | d1bbf775 | 2025-01-09 22:49:30 | [diff] [blame] | 564 | main_frame->GetPermissionsPolicy()); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 565 | } |
| 566 | |
Ali Beyad | 8765eda | 2022-01-24 22:32:10 | [diff] [blame] | 567 | const base::TimeTicks start_time = base::TimeTicks::Now(); |
Liam Brady | cfb4410 | 2023-04-25 08:15:43 | [diff] [blame] | 568 | delegate->GetAllowedClientHintsFromSource(main_frame_origin, &hints); |
Victor Tan | fcce6fa5 | 2023-01-19 23:16:05 | [diff] [blame] | 569 | const base::TimeTicks pref_read_time = base::TimeTicks::Now(); |
Ali Beyad | 49055ed | 2021-10-11 15:33:33 | [diff] [blame] | 570 | |
Lingqi Chi | 59b6b42a | 2022-08-23 02:25:34 | [diff] [blame] | 571 | // If this is a prerender tree, also capture prerender local setting. The |
| 572 | // setting was given by navigation requests on the prerendering page, and |
| 573 | // has not been used as a global setting. |
Hiroki Nakagawa | e2e4558d | 2023-06-07 07:54:48 | [diff] [blame] | 574 | if (frame_tree_node) { |
| 575 | if (auto* host = PrerenderHost::GetFromFrameTreeNodeIfPrerendering( |
| 576 | *frame_tree_node)) { |
Liam Brady | cfb4410 | 2023-04-25 08:15:43 | [diff] [blame] | 577 | host->GetAllowedClientHintsOnPage(main_frame_origin, &hints); |
Hiroki Nakagawa | e2e4558d | 2023-06-07 07:54:48 | [diff] [blame] | 578 | } |
Lingqi Chi | 59b6b42a | 2022-08-23 02:25:34 | [diff] [blame] | 579 | } |
Ali Beyad | 8765eda | 2022-01-24 22:32:10 | [diff] [blame] | 580 | |
| 581 | // Record the time spent getting the client hints. |
Victor Tan | fcce6fa5 | 2023-01-19 23:16:05 | [diff] [blame] | 582 | const base::TimeTicks end_time = base::TimeTicks::Now(); |
| 583 | base::UmaHistogramMicrosecondsTimes("ClientHints.FetchLatency_PrefRead", |
| 584 | pref_read_time - start_time); |
| 585 | base::UmaHistogramMicrosecondsTimes( |
Victor Tan | b37a4c8 | 2023-06-08 16:18:28 | [diff] [blame] | 586 | "ClientHints.FetchLatency_PrerenderHost", end_time - pref_read_time); |
Victor Tan | fcce6fa5 | 2023-01-19 23:16:05 | [diff] [blame] | 587 | base::UmaHistogramMicrosecondsTimes("ClientHints.FetchLatency_Total", |
| 588 | end_time - start_time); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 589 | } |
| 590 | |
Ali Beyad | a6b0fb6f | 2021-07-28 00:13:24 | [diff] [blame] | 591 | blink::EnabledClientHints hints; |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 592 | url::Origin resource_origin; |
Ian Vollick | 25a9d03 | 2022-04-12 23:20:17 | [diff] [blame] | 593 | bool is_outermost_main_frame = false; |
Liam Brady | cfb4410 | 2023-04-25 08:15:43 | [diff] [blame] | 594 | url::Origin main_frame_origin; |
Sandor Major | 07aa8b06 | 2025-02-28 17:24:35 | [diff] [blame] | 595 | std::unique_ptr<network::PermissionsPolicy> permissions_policy; |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 596 | }; |
| 597 | |
Ali Beyad | f312815 | 2022-01-21 20:36:36 | [diff] [blame] | 598 | bool IsClientHintEnabled(const ClientHintsExtendedData& data, |
| 599 | WebClientHintsType type) { |
Victor Tan | b37a4c8 | 2023-06-08 16:18:28 | [diff] [blame] | 600 | return blink::IsClientHintSentByDefault(type) || data.hints.IsEnabled(type); |
Ali Beyad | f312815 | 2022-01-21 20:36:36 | [diff] [blame] | 601 | } |
| 602 | |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 603 | bool IsClientHintAllowed(const ClientHintsExtendedData& data, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 604 | WebClientHintsType type) { |
Ian Vollick | 25a9d03 | 2022-04-12 23:20:17 | [diff] [blame] | 605 | if (data.is_outermost_main_frame) { |
Liam Brady | f519399 | 2023-04-14 14:49:53 | [diff] [blame] | 606 | return true; |
Ali Beyad | f312815 | 2022-01-21 20:36:36 | [diff] [blame] | 607 | } |
Victor Tan | b37a4c8 | 2023-06-08 16:18:28 | [diff] [blame] | 608 | return (data.permissions_policy->IsFeatureEnabledForOrigin( |
Sandor Major | 23d05255 | 2025-02-26 20:28:59 | [diff] [blame] | 609 | network::GetClientHintToPolicyFeatureMap().at(type), |
| 610 | data.resource_origin)); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 611 | } |
| 612 | |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 613 | bool ShouldAddClientHint(const ClientHintsExtendedData& data, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 614 | WebClientHintsType type) { |
Ali Beyad | f312815 | 2022-01-21 20:36:36 | [diff] [blame] | 615 | return IsClientHintEnabled(data, type) && IsClientHintAllowed(data, type); |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 616 | } |
| 617 | |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 618 | bool IsJavascriptEnabled(FrameTreeNode* frame_tree_node) { |
Rakina Zata Amni | 347b7090 | 2020-07-22 10:49:04 | [diff] [blame] | 619 | return WebContents::FromRenderFrameHost(frame_tree_node->current_frame_host()) |
| 620 | ->GetOrCreateWebPreferences() |
| 621 | .javascript_enabled; |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 622 | } |
| 623 | |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 624 | // This modifies `data.permissions_policy` to reflect any changes to client hint |
| 625 | // permissions which may have occurred via the named accept-ch meta tag. |
| 626 | // The permissions policy the browser side has for the frame was set in stone |
| 627 | // before HTML parsing began, so any updates must be sent via |
| 628 | // `container_policy`. |
Alison Gale | 53c77f6 | 2024-04-22 15:16:27 | [diff] [blame] | 629 | // TODO(crbug.com/40208054): Replace w/ generic HTML policy modification. |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 630 | void UpdateIFramePermissionsPolicyWithDelegationSupportForClientHints( |
| 631 | ClientHintsExtendedData& data, |
Sandor Major | 878f835 | 2025-02-18 20:16:02 | [diff] [blame] | 632 | const network::ParsedPermissionsPolicy& container_policy) { |
Ari Chivukula | 4ab383b | 2024-01-16 15:43:27 | [diff] [blame] | 633 | if (container_policy.empty()) { |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 634 | return; |
| 635 | } |
| 636 | |
| 637 | // For client hints specifically, we need to allow the container policy |
| 638 | // to overwrite the parent policy so that permissions policies set in HTML |
| 639 | // via an accept-ch meta tag can be respected. |
Sandor Major | 878f835 | 2025-02-18 20:16:02 | [diff] [blame] | 640 | network::ParsedPermissionsPolicy client_hints_container_policy; |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 641 | for (const auto& container_policy_item : container_policy) { |
Sandor Major | 23d05255 | 2025-02-26 20:28:59 | [diff] [blame] | 642 | const auto& it = network::GetPolicyFeatureToClientHintMap().find( |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 643 | container_policy_item.feature); |
Sandor Major | 23d05255 | 2025-02-26 20:28:59 | [diff] [blame] | 644 | if (it != network::GetPolicyFeatureToClientHintMap().end()) { |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 645 | client_hints_container_policy.push_back(container_policy_item); |
| 646 | |
| 647 | // We need to ensure `blink::EnabledClientHints` is updated where the |
| 648 | // main frame now has permission for the given client hints. |
Ari Chivukula | aa0c1196 | 2022-09-28 19:19:47 | [diff] [blame] | 649 | for (const auto& origin_with_possible_wildcards : |
| 650 | container_policy_item.allowed_origins) { |
| 651 | if (origin_with_possible_wildcards.DoesMatchOrigin( |
Liam Brady | cfb4410 | 2023-04-25 08:15:43 | [diff] [blame] | 652 | data.main_frame_origin)) { |
Ari Chivukula | aa0c1196 | 2022-09-28 19:19:47 | [diff] [blame] | 653 | for (const auto& hint : it->second) { |
| 654 | data.hints.SetIsEnabled(hint, /*should_send*/ true); |
| 655 | } |
| 656 | break; |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 657 | } |
| 658 | } |
| 659 | } |
| 660 | } |
Fergal Daly | a58cd787 | 2024-05-08 00:23:31 | [diff] [blame] | 661 | data.permissions_policy = |
| 662 | data.permissions_policy->WithClientHints(client_hints_container_policy); |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 663 | } |
| 664 | |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 665 | // Captures when UpdateNavigationRequestClientUaHeadersImpl() is being called. |
| 666 | enum class ClientUaHeaderCallType { |
| 667 | // The call is happening during creation of the NavigationRequest. |
| 668 | kDuringCreation, |
| 669 | |
| 670 | // The call is happening after creation of the NavigationRequest. |
| 671 | kAfterCreated, |
| 672 | }; |
| 673 | |
| 674 | // Implementation of UpdateNavigationRequestClientUaHeaders(). |
| 675 | void UpdateNavigationRequestClientUaHeadersImpl( |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 676 | ClientHintsControllerDelegate* delegate, |
| 677 | bool override_ua, |
| 678 | FrameTreeNode* frame_tree_node, |
| 679 | ClientUaHeaderCallType call_type, |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 680 | net::HttpRequestHeaders* headers, |
Sandor Major | 878f835 | 2025-02-18 20:16:02 | [diff] [blame] | 681 | const network::ParsedPermissionsPolicy& container_policy, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 682 | const std::optional<GURL>& request_url, |
Patrick Noland | fec8dba | 2022-12-20 20:25:47 | [diff] [blame] | 683 | const ClientHintsExtendedData& data) { |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 684 | std::optional<blink::UserAgentMetadata> ua_metadata; |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 685 | bool disable_due_to_custom_ua = false; |
| 686 | if (override_ua) { |
| 687 | NavigatorDelegate* nav_delegate = |
Ryan Sturm | 320fda4d | 2021-01-05 19:13:30 | [diff] [blame] | 688 | frame_tree_node ? frame_tree_node->navigator().GetDelegate() : nullptr; |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 689 | ua_metadata = |
Dave Tapuska | 94a6978a | 2024-11-22 14:37:29 | [diff] [blame] | 690 | nav_delegate |
| 691 | ? nav_delegate->GetUserAgentOverride(frame_tree_node->frame_tree()) |
| 692 | .ua_metadata_override |
| 693 | : std::nullopt; |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 694 | // If a custom UA override is set, but no value is provided for UA client |
| 695 | // hints, disable them. |
| 696 | disable_due_to_custom_ua = !ua_metadata.has_value(); |
| 697 | } |
| 698 | |
Ryan Sturm | 320fda4d | 2021-01-05 19:13:30 | [diff] [blame] | 699 | if (frame_tree_node && |
| 700 | devtools_instrumentation::ApplyUserAgentMetadataOverrides(frame_tree_node, |
Maks Orlovich | 85a6fd6 | 2020-05-05 23:02:14 | [diff] [blame] | 701 | &ua_metadata)) { |
| 702 | // Likewise, if devtools says to override client hints but provides no |
| 703 | // value, disable them. This overwrites previous decision from UI. |
| 704 | disable_due_to_custom_ua = !ua_metadata.has_value(); |
| 705 | } |
| 706 | |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 707 | if (!disable_due_to_custom_ua) { |
| 708 | if (!ua_metadata.has_value()) |
| 709 | ua_metadata = delegate->GetUserAgentMetadata(); |
| 710 | |
| 711 | // The `Sec-CH-UA` client hint is attached to all outgoing requests. This is |
| 712 | // (intentionally) different than other client hints. |
Yoav Weiss | 8627cac | 2020-05-15 00:43:37 | [diff] [blame] | 713 | // It's barred behind ShouldAddClientHints to make sure it's controlled by |
Charlie Hu | 5130d25e | 2021-03-05 21:53:39 | [diff] [blame] | 714 | // Permissions Policy. |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 715 | // |
| 716 | // https://wicg.github.io/client-hints-infrastructure/#abstract-opdef-append-client-hints-to-request |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 717 | if (ShouldAddClientHint(data, WebClientHintsType::kUA)) { |
| 718 | AddUAHeader(headers, WebClientHintsType::kUA, |
Victor Tan | 1eb643c3 | 2021-11-11 18:34:02 | [diff] [blame] | 719 | ua_metadata->SerializeBrandMajorVersionList()); |
Yoav Weiss | 8627cac | 2020-05-15 00:43:37 | [diff] [blame] | 720 | } |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 721 | // The `Sec-CH-UA-Mobile client hint was also deemed "low entropy" and can |
Yoav Weiss | 8627cac | 2020-05-15 00:43:37 | [diff] [blame] | 722 | // safely be sent with every request. Similarly to UA, ShouldAddClientHints |
Charlie Hu | 5130d25e | 2021-03-05 21:53:39 | [diff] [blame] | 723 | // makes sure it's controlled by Permissions Policy. |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 724 | if (ShouldAddClientHint(data, WebClientHintsType::kUAMobile)) { |
| 725 | AddUAHeader(headers, WebClientHintsType::kUAMobile, |
| 726 | SerializeHeaderString(ua_metadata->mobile)); |
Yoav Weiss | 8627cac | 2020-05-15 00:43:37 | [diff] [blame] | 727 | } |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 728 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 729 | if (ShouldAddClientHint(data, WebClientHintsType::kUAFullVersion)) { |
| 730 | AddUAHeader(headers, WebClientHintsType::kUAFullVersion, |
Aaron Tagliaboschi | 9f01b68 | 2020-05-05 21:03:17 | [diff] [blame] | 731 | SerializeHeaderString(ua_metadata->full_version)); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 732 | } |
| 733 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 734 | if (ShouldAddClientHint(data, WebClientHintsType::kUAArch)) { |
| 735 | AddUAHeader(headers, WebClientHintsType::kUAArch, |
Aaron Tagliaboschi | 9f01b68 | 2020-05-05 21:03:17 | [diff] [blame] | 736 | SerializeHeaderString(ua_metadata->architecture)); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 737 | } |
| 738 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 739 | if (ShouldAddClientHint(data, WebClientHintsType::kUAPlatform)) { |
| 740 | AddUAHeader(headers, WebClientHintsType::kUAPlatform, |
Aaron Tagliaboschi | 9f01b68 | 2020-05-05 21:03:17 | [diff] [blame] | 741 | SerializeHeaderString(ua_metadata->platform)); |
Yoav Weiss | fd1d19f | 2020-05-05 09:23:03 | [diff] [blame] | 742 | } |
| 743 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 744 | if (ShouldAddClientHint(data, WebClientHintsType::kUAPlatformVersion)) { |
| 745 | AddUAHeader(headers, WebClientHintsType::kUAPlatformVersion, |
Aaron Tagliaboschi | 9f01b68 | 2020-05-05 21:03:17 | [diff] [blame] | 746 | SerializeHeaderString(ua_metadata->platform_version)); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 747 | } |
| 748 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 749 | if (ShouldAddClientHint(data, WebClientHintsType::kUAModel)) { |
| 750 | AddUAHeader(headers, WebClientHintsType::kUAModel, |
Aaron Tagliaboschi | 9f01b68 | 2020-05-05 21:03:17 | [diff] [blame] | 751 | SerializeHeaderString(ua_metadata->model)); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 752 | } |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 753 | if (ShouldAddClientHint(data, WebClientHintsType::kUABitness)) { |
| 754 | AddUAHeader(headers, WebClientHintsType::kUABitness, |
Aaron Tagliaboschi | 1649e10 | 2021-06-18 23:15:07 | [diff] [blame] | 755 | SerializeHeaderString(ua_metadata->bitness)); |
| 756 | } |
Ali Beyad | b235f34 | 2022-01-31 18:50:22 | [diff] [blame] | 757 | if (ShouldAddClientHint(data, WebClientHintsType::kUAWoW64)) { |
| 758 | AddUAHeader(headers, WebClientHintsType::kUAWoW64, |
| 759 | SerializeHeaderString(ua_metadata->wow64)); |
| 760 | } |
Victor Tan | 1eb643c3 | 2021-11-11 18:34:02 | [diff] [blame] | 761 | if (ShouldAddClientHint(data, WebClientHintsType::kUAFullVersionList)) { |
| 762 | AddUAHeader(headers, WebClientHintsType::kUAFullVersionList, |
| 763 | ua_metadata->SerializeBrandFullVersionList()); |
| 764 | } |
Dustin J. Mitchell | 5909d3b | 2024-03-13 01:03:24 | [diff] [blame] | 765 | if (ShouldAddClientHint(data, WebClientHintsType::kUAFormFactors)) { |
| 766 | AddUAHeader(headers, WebClientHintsType::kUAFormFactors, |
| 767 | ua_metadata->SerializeFormFactors()); |
Dustin J. Mitchell | 434a18c | 2023-06-22 15:32:36 | [diff] [blame] | 768 | } |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 769 | } else if (call_type == ClientUaHeaderCallType::kAfterCreated) { |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 770 | RemoveClientHintHeader(WebClientHintsType::kUA, headers); |
| 771 | RemoveClientHintHeader(WebClientHintsType::kUAMobile, headers); |
| 772 | RemoveClientHintHeader(WebClientHintsType::kUAFullVersion, headers); |
| 773 | RemoveClientHintHeader(WebClientHintsType::kUAArch, headers); |
| 774 | RemoveClientHintHeader(WebClientHintsType::kUAPlatform, headers); |
| 775 | RemoveClientHintHeader(WebClientHintsType::kUAPlatformVersion, headers); |
| 776 | RemoveClientHintHeader(WebClientHintsType::kUAModel, headers); |
| 777 | RemoveClientHintHeader(WebClientHintsType::kUABitness, headers); |
Victor Tan | 1eb643c3 | 2021-11-11 18:34:02 | [diff] [blame] | 778 | RemoveClientHintHeader(WebClientHintsType::kUAFullVersionList, headers); |
Ali Beyad | b235f34 | 2022-01-31 18:50:22 | [diff] [blame] | 779 | RemoveClientHintHeader(WebClientHintsType::kUAWoW64, headers); |
Dustin J. Mitchell | 5909d3b | 2024-03-13 01:03:24 | [diff] [blame] | 780 | RemoveClientHintHeader(WebClientHintsType::kUAFormFactors, headers); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 781 | } |
Yoav Weiss | 8c9688d | 2019-11-07 17:21:04 | [diff] [blame] | 782 | } |
| 783 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 784 | } // namespace |
| 785 | |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 786 | bool ShouldAddClientHints(const url::Origin& origin, |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 787 | FrameTreeNode* frame_tree_node, |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 788 | ClientHintsControllerDelegate* delegate, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 789 | const std::optional<GURL> maybe_request_url) { |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 790 | url::Origin origin_to_check = |
| 791 | maybe_request_url ? url::Origin::Create(maybe_request_url.value()) |
| 792 | : origin; |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 793 | // Client hints should only be enabled when JavaScript is enabled. Platforms |
| 794 | // which enable/disable JavaScript on a per-origin basis should implement |
| 795 | // IsJavaScriptAllowed to check a given origin. Other platforms (Android |
| 796 | // WebView) enable/disable JavaScript on a per-View basis, using the |
| 797 | // WebPreferences setting. |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 798 | return IsValidURLForClientHints(origin_to_check) && |
Adithya Srinivasan | afe3e47c | 2022-04-21 22:54:02 | [diff] [blame] | 799 | delegate->IsJavaScriptAllowed( |
| 800 | origin_to_check.GetURL(), |
| 801 | frame_tree_node ? frame_tree_node->GetParentOrOuterDocument() |
| 802 | : nullptr) && |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 803 | (!frame_tree_node || IsJavascriptEnabled(frame_tree_node)); |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 804 | } |
| 805 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 806 | unsigned long RoundRttForTesting(const std::string& host, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 807 | const std::optional<base::TimeDelta>& rtt) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 808 | return RoundRtt(host, rtt); |
| 809 | } |
| 810 | |
| 811 | double RoundKbpsToMbpsForTesting(const std::string& host, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 812 | const std::optional<int32_t>& downlink_kbps) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 813 | return RoundKbpsToMbps(host, downlink_kbps); |
| 814 | } |
| 815 | |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 816 | void UpdateNavigationRequestClientUaHeaders( |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 817 | const url::Origin& origin, |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 818 | ClientHintsControllerDelegate* delegate, |
| 819 | bool override_ua, |
| 820 | FrameTreeNode* frame_tree_node, |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 821 | net::HttpRequestHeaders* headers, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 822 | const std::optional<GURL>& request_url) { |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 823 | DCHECK(frame_tree_node); |
Victor Tan | 7cb76819 | 2024-01-19 22:53:24 | [diff] [blame] | 824 | if (!ShouldAddClientHints(origin, frame_tree_node, delegate, request_url)) { |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 825 | return; |
| 826 | } |
| 827 | |
Patrick Noland | fec8dba | 2022-12-20 20:25:47 | [diff] [blame] | 828 | ClientHintsExtendedData data(origin, frame_tree_node, delegate, request_url); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 829 | UpdateNavigationRequestClientUaHeadersImpl( |
Patrick Noland | fec8dba | 2022-12-20 20:25:47 | [diff] [blame] | 830 | delegate, override_ua, frame_tree_node, |
| 831 | ClientUaHeaderCallType::kAfterCreated, headers, {}, request_url, data); |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 832 | } |
| 833 | |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 834 | namespace { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 835 | |
Aaron Tagliaboschi | eae7f4f2f | 2021-04-13 15:05:45 | [diff] [blame] | 836 | void AddRequestClientHintsHeaders( |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 837 | const url::Origin& origin, |
Aaron Tagliaboschi | eae7f4f2f | 2021-04-13 15:05:45 | [diff] [blame] | 838 | net::HttpRequestHeaders* headers, |
| 839 | BrowserContext* context, |
| 840 | ClientHintsControllerDelegate* delegate, |
| 841 | bool is_ua_override_on, |
| 842 | FrameTreeNode* frame_tree_node, |
Sandor Major | 878f835 | 2025-02-18 20:16:02 | [diff] [blame] | 843 | const network::ParsedPermissionsPolicy& container_policy, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 844 | const std::optional<GURL>& request_url) { |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 845 | ClientHintsExtendedData data(origin, frame_tree_node, delegate, request_url); |
Ari Chivukula | 6a8b32f | 2021-12-16 18:32:57 | [diff] [blame] | 846 | UpdateIFramePermissionsPolicyWithDelegationSupportForClientHints( |
| 847 | data, container_policy); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 848 | |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 849 | GURL url = origin.GetURL(); |
| 850 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 851 | // Add Headers |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 852 | if (ShouldAddClientHint(data, WebClientHintsType::kDeviceMemory_DEPRECATED)) { |
Ari Chivukula | 6424811 | 2021-10-07 06:00:56 | [diff] [blame] | 853 | AddDeviceMemoryHeader(headers, /*use_deprecated_version*/ true); |
| 854 | } |
| 855 | if (ShouldAddClientHint(data, WebClientHintsType::kDeviceMemory)) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 856 | AddDeviceMemoryHeader(headers); |
| 857 | } |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 858 | if (ShouldAddClientHint(data, WebClientHintsType::kDpr_DEPRECATED)) { |
Ari Chivukula | 6424811 | 2021-10-07 06:00:56 | [diff] [blame] | 859 | AddDPRHeader(headers, context, url, /*use_deprecated_version*/ true); |
| 860 | } |
| 861 | if (ShouldAddClientHint(data, WebClientHintsType::kDpr)) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 862 | AddDPRHeader(headers, context, url); |
| 863 | } |
Victor Tan | 1eb643c3 | 2021-11-11 18:34:02 | [diff] [blame] | 864 | if (ShouldAddClientHint(data, |
| 865 | WebClientHintsType::kViewportWidth_DEPRECATED)) { |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 866 | AddViewportWidthHeader(headers, context, url, frame_tree_node, delegate, |
Ari Chivukula | 6424811 | 2021-10-07 06:00:56 | [diff] [blame] | 867 | /*use_deprecated_version*/ true); |
| 868 | } |
| 869 | if (ShouldAddClientHint(data, WebClientHintsType::kViewportWidth)) { |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 870 | AddViewportWidthHeader(headers, context, url, frame_tree_node, delegate); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 871 | } |
Max Curran | 99cbec149 | 2021-08-16 19:54:26 | [diff] [blame] | 872 | if (ShouldAddClientHint( |
| 873 | data, network::mojom::WebClientHintsType::kViewportHeight)) { |
Max Curran | 2baf1f2 | 2022-08-26 22:01:07 | [diff] [blame] | 874 | AddViewportHeightHeader(headers, context, url, frame_tree_node, delegate); |
Max Curran | 99cbec149 | 2021-08-16 19:54:26 | [diff] [blame] | 875 | } |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 876 | network::NetworkQualityTracker* network_quality_tracker = |
| 877 | delegate->GetNetworkQualityTracker(); |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 878 | if (ShouldAddClientHint(data, WebClientHintsType::kRtt_DEPRECATED)) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 879 | AddRttHeader(headers, network_quality_tracker, url); |
| 880 | } |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 881 | if (ShouldAddClientHint(data, WebClientHintsType::kDownlink_DEPRECATED)) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 882 | AddDownlinkHeader(headers, network_quality_tracker, url); |
| 883 | } |
Ari Chivukula | 6692cc5 | 2021-09-09 09:30:11 | [diff] [blame] | 884 | if (ShouldAddClientHint(data, WebClientHintsType::kEct_DEPRECATED)) { |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 885 | AddEctHeader(headers, network_quality_tracker, url); |
| 886 | } |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 887 | |
Victor Tan | 7cb76819 | 2024-01-19 22:53:24 | [diff] [blame] | 888 | UpdateNavigationRequestClientUaHeadersImpl( |
| 889 | delegate, is_ua_override_on, frame_tree_node, |
| 890 | ClientUaHeaderCallType::kDuringCreation, headers, container_policy, |
| 891 | request_url, data); |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 892 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 893 | if (ShouldAddClientHint(data, WebClientHintsType::kPrefersColorScheme)) { |
François Beaufort | 1b51d06 | 2021-05-18 11:11:23 | [diff] [blame] | 894 | AddPrefersColorSchemeHeader(headers, frame_tree_node); |
| 895 | } |
| 896 | |
François Beaufort | 378fffd | 2022-09-28 16:48:19 | [diff] [blame] | 897 | if (ShouldAddClientHint(data, WebClientHintsType::kPrefersReducedMotion)) { |
| 898 | AddPrefersReducedMotionHeader(headers, frame_tree_node); |
| 899 | } |
| 900 | |
Luke Warlow | 8633929 | 2023-08-26 16:55:25 | [diff] [blame] | 901 | if (ShouldAddClientHint(data, |
| 902 | WebClientHintsType::kPrefersReducedTransparency)) { |
| 903 | AddPrefersReducedTransparencyHeader(headers, frame_tree_node); |
| 904 | } |
| 905 | |
Ari Chivukula | 116f0fc4 | 2022-04-13 23:26:18 | [diff] [blame] | 906 | if (ShouldAddClientHint(data, WebClientHintsType::kSaveData)) |
| 907 | AddSaveDataHeader(headers, context); |
| 908 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 909 | // Static assert that triggers if a new client hint header is added. If a |
| 910 | // new client hint header is added, the following assertion should be updated. |
| 911 | // If possible, logic should be added above so that the request headers for |
| 912 | // the newly added client hint can be added to the request. |
| 913 | static_assert( |
Luke Warlow | 8633929 | 2023-08-26 16:55:25 | [diff] [blame] | 914 | network::mojom::WebClientHintsType::kPrefersReducedTransparency == |
Max Curran | 99cbec149 | 2021-08-16 19:54:26 | [diff] [blame] | 915 | network::mojom::WebClientHintsType::kMaxValue, |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 916 | "Consider adding client hint request headers from the browser process"); |
| 917 | |
Alison Gale | 81f4f2c7 | 2024-04-22 19:33:31 | [diff] [blame] | 918 | // TODO(crbug.com/40526905): If the request is redirected, the client hint |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 919 | // headers stay attached to the redirected request. Consider removing/adding |
| 920 | // the client hints headers if the request is redirected with a change in |
| 921 | // scheme or a change in the origin. |
| 922 | } |
| 923 | |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 924 | } // namespace |
| 925 | |
| 926 | void AddPrefetchNavigationRequestClientHintsHeaders( |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 927 | const url::Origin& origin, |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 928 | net::HttpRequestHeaders* headers, |
| 929 | BrowserContext* context, |
| 930 | ClientHintsControllerDelegate* delegate, |
Kouhei Ueno | 27f86cb | 2025-02-06 09:03:36 | [diff] [blame] | 931 | bool is_ua_override_on) { |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 932 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 933 | DCHECK(context); |
| 934 | |
Kouhei Ueno | 27f86cb | 2025-02-06 09:03:36 | [diff] [blame] | 935 | if (!ShouldAddClientHints(origin, nullptr, delegate)) { |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 936 | return; |
| 937 | } |
| 938 | |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 939 | AddRequestClientHintsHeaders(origin, headers, context, delegate, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 940 | is_ua_override_on, nullptr, {}, std::nullopt); |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 941 | } |
| 942 | |
| 943 | void AddNavigationRequestClientHintsHeaders( |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 944 | const url::Origin& origin, |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 945 | net::HttpRequestHeaders* headers, |
| 946 | BrowserContext* context, |
| 947 | ClientHintsControllerDelegate* delegate, |
| 948 | bool is_ua_override_on, |
Aaron Tagliaboschi | eae7f4f2f | 2021-04-13 15:05:45 | [diff] [blame] | 949 | FrameTreeNode* frame_tree_node, |
Sandor Major | 878f835 | 2025-02-18 20:16:02 | [diff] [blame] | 950 | const network::ParsedPermissionsPolicy& container_policy, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 951 | const std::optional<GURL>& request_url) { |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 952 | DCHECK(frame_tree_node); |
| 953 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 954 | DCHECK(context); |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 955 | if (!ShouldAddClientHints(origin, frame_tree_node, delegate, request_url)) { |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 956 | return; |
| 957 | } |
| 958 | |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 959 | AddRequestClientHintsHeaders(origin, headers, context, delegate, |
Aaron Tagliaboschi | eae7f4f2f | 2021-04-13 15:05:45 | [diff] [blame] | 960 | is_ua_override_on, frame_tree_node, |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 961 | container_policy, request_url); |
Ryan Sturm | 6e961a3 | 2020-11-30 20:37:55 | [diff] [blame] | 962 | } |
| 963 | |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 964 | std::optional<std::vector<WebClientHintsType>> |
Ali Beyad | f690449a | 2021-07-13 07:49:53 | [diff] [blame] | 965 | ParseAndPersistAcceptCHForNavigation( |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 966 | const url::Origin& origin, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 967 | const network::mojom::ParsedHeadersPtr& parsed_headers, |
| 968 | const net::HttpResponseHeaders* response_headers, |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 969 | BrowserContext* context, |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 970 | ClientHintsControllerDelegate* delegate, |
| 971 | FrameTreeNode* frame_tree_node) { |
| 972 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 973 | DCHECK(context); |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 974 | DCHECK(parsed_headers); |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 975 | |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 976 | if (!parsed_headers->accept_ch) |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 977 | return std::nullopt; |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 978 | |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 979 | if (!IsValidURLForClientHints(origin)) |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 980 | return std::nullopt; |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 981 | |
| 982 | // Client hints should only be enabled when JavaScript is enabled. Platforms |
| 983 | // which enable/disable JavaScript on a per-origin basis should implement |
| 984 | // IsJavaScriptAllowed to check a given origin. Other platforms (Android |
| 985 | // WebView) enable/disable JavaScript on a per-View basis, using the |
| 986 | // WebPreferences setting. |
Adithya Srinivasan | afe3e47c | 2022-04-21 22:54:02 | [diff] [blame] | 987 | if (!delegate->IsJavaScriptAllowed( |
| 988 | origin.GetURL(), frame_tree_node->GetParentOrOuterDocument()) || |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 989 | !IsJavascriptEnabled(frame_tree_node)) { |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 990 | return std::nullopt; |
Scott Violet | 3cd8d36 | 2020-04-30 22:18:24 | [diff] [blame] | 991 | } |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 992 | |
Victor Tan | b37a4c8 | 2023-06-08 16:18:28 | [diff] [blame] | 993 | // Only the main frame should parse accept-CH. |
Ali Beyad | 49055ed | 2021-10-11 15:33:33 | [diff] [blame] | 994 | if (!frame_tree_node->IsMainFrame()) { |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 995 | return std::nullopt; |
Ali Beyad | 49055ed | 2021-10-11 15:33:33 | [diff] [blame] | 996 | } |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 997 | |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 998 | blink::EnabledClientHints enabled_hints; |
Victor Tan | b37a4c8 | 2023-06-08 16:18:28 | [diff] [blame] | 999 | for (const WebClientHintsType type : parsed_headers->accept_ch.value()) { |
| 1000 | enabled_hints.SetIsEnabled(type, true); |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 1001 | } |
| 1002 | const std::vector<WebClientHintsType> persisted_hints = |
| 1003 | enabled_hints.GetEnabledHints(); |
Lingqi Chi | 59b6b42a | 2022-08-23 02:25:34 | [diff] [blame] | 1004 | DCHECK(frame_tree_node); |
| 1005 | PersistAcceptCH(origin, *frame_tree_node, delegate, persisted_hints); |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 1006 | return persisted_hints; |
Maks Orlovich | c66745a | 2020-06-30 17:40:02 | [diff] [blame] | 1007 | } |
| 1008 | |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 1009 | void PersistAcceptCH(const url::Origin& origin, |
Lingqi Chi | 59b6b42a | 2022-08-23 02:25:34 | [diff] [blame] | 1010 | FrameTreeNode& frame_tree_node, |
Ali Beyad | 77dba95 | 2021-08-25 20:52:58 | [diff] [blame] | 1011 | ClientHintsControllerDelegate* delegate, |
Ari Chivukula | 93a1f4f | 2021-12-01 23:27:18 | [diff] [blame] | 1012 | const std::vector<WebClientHintsType>& hints) { |
Ali Beyad | 77dba95 | 2021-08-25 20:52:58 | [diff] [blame] | 1013 | DCHECK(delegate); |
Lingqi Chi | 59b6b42a | 2022-08-23 02:25:34 | [diff] [blame] | 1014 | |
Hiroki Nakagawa | e2e4558d | 2023-06-07 07:54:48 | [diff] [blame] | 1015 | // For prerendering headers, it should not persist the client header until |
| 1016 | // activation, considering user has not visited the page and allowed it to |
| 1017 | // change content setting yet. The client hints should apply to navigations |
| 1018 | // in the prerendering page, and propagate to the global setting upon user |
| 1019 | // navigation. |
| 1020 | if (auto* host = |
| 1021 | PrerenderHost::GetFromFrameTreeNodeIfPrerendering(frame_tree_node)) { |
| 1022 | host->OnAcceptClientHintChanged(origin, hints); |
Lingqi Chi | 59b6b42a | 2022-08-23 02:25:34 | [diff] [blame] | 1023 | return; |
| 1024 | } |
| 1025 | |
| 1026 | delegate->PersistClientHints( |
| 1027 | origin, frame_tree_node.GetParentOrOuterDocument(), hints); |
Ali Beyad | 77dba95 | 2021-08-25 20:52:58 | [diff] [blame] | 1028 | } |
| 1029 | |
Lei Zhang | 8076d9f | 2021-11-16 00:33:27 | [diff] [blame] | 1030 | std::vector<WebClientHintsType> LookupAcceptCHForCommit( |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 1031 | const url::Origin& origin, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 1032 | ClientHintsControllerDelegate* delegate, |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 1033 | FrameTreeNode* frame_tree_node, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 1034 | const std::optional<GURL>& request_url) { |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 1035 | std::vector<WebClientHintsType> result; |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 1036 | if (!ShouldAddClientHints(origin, frame_tree_node, delegate, request_url)) { |
Maks Orlovich | c66745a | 2020-06-30 17:40:02 | [diff] [blame] | 1037 | return result; |
| 1038 | } |
| 1039 | |
Aaron Tagliaboschi | d0cda1e | 2022-03-09 19:30:37 | [diff] [blame] | 1040 | const ClientHintsExtendedData data(origin, frame_tree_node, delegate, |
| 1041 | request_url); |
Victor Tan | b37a4c8 | 2023-06-08 16:18:28 | [diff] [blame] | 1042 | return data.hints.GetEnabledHints(); |
Maks Orlovich | bc5c59b | 2020-04-24 12:40:59 | [diff] [blame] | 1043 | } |
| 1044 | |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 1045 | bool AreCriticalHintsMissing( |
Aaron Tagliaboschi | 579c632e | 2022-02-23 17:45:12 | [diff] [blame] | 1046 | const url::Origin& origin, |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 1047 | FrameTreeNode* frame_tree_node, |
| 1048 | ClientHintsControllerDelegate* delegate, |
Ali Beyad | 7417fc2 | 2021-08-06 03:09:58 | [diff] [blame] | 1049 | const std::vector<WebClientHintsType>& critical_hints) { |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 1050 | ClientHintsExtendedData data(origin, frame_tree_node, delegate, std::nullopt); |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 1051 | |
Charlie Hu | 5130d25e | 2021-03-05 21:53:39 | [diff] [blame] | 1052 | // Note: these only check for per-hint origin/permissions policy settings, not |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 1053 | // origin-level or "browser-level" policies like disabiling JS or other |
| 1054 | // features. |
| 1055 | for (auto hint : critical_hints) { |
Ali Beyad | f312815 | 2022-01-21 20:36:36 | [diff] [blame] | 1056 | if (IsClientHintAllowed(data, hint) && !IsClientHintEnabled(data, hint)) { |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 1057 | return true; |
Ali Beyad | f312815 | 2022-01-21 20:36:36 | [diff] [blame] | 1058 | } |
Aaron Tagliaboschi | 41bed88e | 2020-10-20 16:29:42 | [diff] [blame] | 1059 | } |
| 1060 | |
| 1061 | return false; |
| 1062 | } |
| 1063 | |
Yoshisato Yanagisawa | b108f25 | 2025-07-24 23:49:04 | [diff] [blame] | 1064 | network::ResourceRequest::TrustedParams::EnabledClientHints |
| 1065 | GetEnabledClientHints(const url::Origin& origin, |
| 1066 | FrameTreeNode* frame_tree_node, |
| 1067 | ClientHintsControllerDelegate* delegate) { |
Yoshisato Yanagisawa | 35b1e363 | 2025-06-18 23:37:51 | [diff] [blame] | 1068 | ClientHintsExtendedData data(origin, frame_tree_node, delegate, std::nullopt); |
| 1069 | |
Yoshisato Yanagisawa | b108f25 | 2025-07-24 23:49:04 | [diff] [blame] | 1070 | network::ResourceRequest::TrustedParams::EnabledClientHints |
| 1071 | enabled_client_hints; |
| 1072 | enabled_client_hints.is_outermost_main_frame = data.is_outermost_main_frame; |
Yoshisato Yanagisawa | 35b1e363 | 2025-06-18 23:37:51 | [diff] [blame] | 1073 | const auto& client_hints_map = network::GetClientHintToNameMap(); |
| 1074 | // Note: these only check for per-hint origin/permissions policy settings, not |
| 1075 | // origin-level or "browser-level" policies like disabiling JS or other |
| 1076 | // features. |
| 1077 | for (const auto& [hint, _] : client_hints_map) { |
| 1078 | if (ShouldAddClientHint(data, hint)) { |
Yoshisato Yanagisawa | b108f25 | 2025-07-24 23:49:04 | [diff] [blame] | 1079 | enabled_client_hints.hints.push_back(hint); |
Yoshisato Yanagisawa | 35b1e363 | 2025-06-18 23:37:51 | [diff] [blame] | 1080 | } |
| 1081 | } |
Yoshisato Yanagisawa | b108f25 | 2025-07-24 23:49:04 | [diff] [blame] | 1082 | enabled_client_hints.origin = data.main_frame_origin; |
| 1083 | return enabled_client_hints; |
Yoshisato Yanagisawa | 35b1e363 | 2025-06-18 23:37:51 | [diff] [blame] | 1084 | } |
| 1085 | |
Yoav Weiss | 603be26 | 2019-03-04 13:14:57 | [diff] [blame] | 1086 | } // namespace content |