0
Files
android_webview
apps
ash
base
build
build_overrides
buildtools
cc
chrome
chromecast
chromeos
codelabs
components
about_ui
access_code_cast
account_id
account_manager_core
aggregation_service
allocation_recorder
android_autofill
android_system_error_page
apdu
app_constants
app_restore
arc
arc_strings_grdp
assist_ranker
attribution_reporting
autofill
autofill_payments_strings_grdp
autofill_strings_grdp
back_forward_cache
background_fetch
background_sync
background_task_scheduler
base32
blocked_content
blocklist
bookmark_bar_strings_grdp
bookmark_component_strings_grdp
bookmarks
breadcrumbs
browser_sync
browser_ui
browsing_data
browsing_data_strings_grdp
browsing_topics
captive_portal
cast
cast_receiver
cast_streaming
cbor
cdm
certificate_matching
certificate_transparency
chrome_cleaner
chromeos_camera
client_hints
browser
common
COMMON_METADATA
DEPS
DIR_METADATA
OWNERS
README.md
client_update_protocol
cloud_devices
commerce
commerce_strings_grdp
component_updater
components_chromium_strings_grd
components_google_chrome_strings_grd
components_strings_grd
consent_auditor
constrained_window
content_capture
content_creation
content_creation_strings_grdp
content_relationship_verification
content_settings
contextual_search
continuous_search
cookie_config
country_codes
crash
crash_strings_grdp
cronet
crx_file
custom_handlers
database_utils
dbus
desks_storage
device_event_log
device_reauth
device_signals
devtools
digital_goods
discardable_memory
dom_distiller
dom_distiller_strings_grdp
domain_reliability
download
drive
embedder_support
encrypted_messages
endpoint_fetcher
enterprise
enterprise_strings_grdp
error_page
error_page_strings_grdp
exo
external_intents
external_intents_strings_grdp
externalauth
favicon
favicon_base
feature_engagement
feed
feedback
file_access
filename_generation
find_in_page
flags_strings_grdp
flags_ui
fuchsia_component_support
fuchsia_legacymetrics
fullscreen_control
fullscreen_control_strings_grdp
gcm_driver
global_media_controls
global_media_controls_strings_grdp
google
grpc_support
guest_os
guest_view
gwp_asan
handoff
headless
heap_profiling
heavy_ad_intervention
heavy_ad_intervention_strings_grdp
history
history_clusters
history_clusters_strings_grdp
history_strings_grdp
image_fetcher
infobars
installedapp
invalidation
javascript_dialogs
javascript_dialogs_strings_grdp
js_injection
keep_alive_registry
keyed_service
language
lens
leveldb_proto
link_header_util
live_caption
live_caption_strings_grdp
local_state
location
login
lookalikes
management
management_ios_strings_grdp
management_strings_grdp
media_control
media_message_center
media_message_center_strings_grdp
media_router
memory_pressure
memory_system
messages
metal_util
metrics
metrics_services_manager
minidump_uploader
mirroring
ml
module_installer
nacl
named_mojo_ipc_server
navigation_interception
navigation_metrics
net_log
neterror
network_hints
network_session_configurator
network_time
new_or_sad_tab_strings_grdp
no_state_prefetch
ntp_snippets
ntp_tiles
offline_items_collection
offline_pages
omnibox
omnibox_pedal_ui_strings_grdp
omnibox_strings_grdp
on_load_script_injector
onc
open_from_clipboard
openscreen_platform
optimization_guide
origin_trials
os_crypt
ownership
page_image_annotation
page_image_service
page_info
page_info_strings_grdp
page_load_metrics
paint_preview
password_manager
password_manager_strings_grdp
payments
payments_strings_grdp
pdf
pdf_strings_grdp
performance_manager
permissions
permissions_strings_grdp
plugins
policy
policy_strings_grdp
power_bookmarks
power_metrics
power_monitor
power_scheduler
pref_registry
prefs
previous_session_info
print_media_strings_grdp
printing
printing_component_strings_grdp
privacy_sandbox
privacy_sandbox_strings_grdp
profile_metrics
proxy_config
pwg_encoder
qr_code_generator
query_parser
query_tiles
quirks
reading_list
reduce_accept_language
remote_cocoa
renderer_context_menu
reporting
reset_password_strings_grdp
resources
rlz
safe_browsing
safe_search_api
safety_check
saved_tab_groups
scheduling_metrics
schema_org
search
search_engines
search_provider_logos
security_interstitials
security_interstitials_strings_grdp
security_state
segmentation_platform
send_tab_to_self
send_tab_to_self_strings_grdp
services
session_manager
session_proto_db
sessions
shared_highlighting
signin
site_engagement
site_isolation
site_settings_strings_grdp
sms_strings_grdp
soda
speech
spellcheck
sqlite_proto
ssl_errors
ssl_errors_strings_grdp
stability_report
startup_metric_utils
storage_monitor
strictmode
strings
stylus_handwriting
subresource_filter
subresource_filter_strings_grdp
supervised_user
supervised_user_strings_grdp
sync
sync_bookmarks
sync_device_info
sync_preferences
sync_sessions
sync_ui_strings_grdp
sync_user_events
system_media_controls
tab_groups
tab_groups_strings_grdp
test
thin_webview
tracing
translate
translate_strings_grdp
trusted_vault
ui_devtools
ui_metrics
ukm
undo
undo_strings_grdp
unexportable_keys
unified_consent
update_client
upload_list
url_formatter
url_matcher
url_pattern_index
url_rewrite
user_actions_ui
user_education
user_education_strings_grdp
user_manager
user_notes
user_prefs
value_store
variations
vector_icons
version_info
version_ui
version_ui_strings_grdp
visitedlink
viz
web_app_resources
web_cache
web_modal
web_package
web_resource
webapk
webapps
webauthn
webcrypto
webdata
webdata_services
webrtc
webrtc_logging
webxr
webxr_strings_grdp
wifi
winhttp
zoom
zucchini
.eslintrc.js
BUILD.gn
DEPS
OWNERS
PRESUBMIT.py
README.md
android_system_error_page_strings.grdp
arc_strings.grdp
autofill_payments_strings.grdp
autofill_strings.grdp
blocked_content_strings.grdp
bookmark_bar_strings.grdp
bookmark_component_strings.grdp
browsing_data_strings.grdp
commerce_strings.grdp
components_chromium_strings.grd
components_google_chrome_strings.grd
components_locale_settings.grd
components_settings_strings.grdp
components_strings.grd
content_creation_strings.grdp
crash_strings.grdp
dialog_strings.grdp
dom_distiller_strings.grdp
enterprise_strings.grdp
error_page_strings.grdp
external_intents_strings.grdp
find_in_page_strings.grdp
flags_strings.grdp
fullscreen_control_strings.grdp
global_media_controls_strings.grdp
heavy_ad_intervention_strings.grdp
history_clusters_strings.grdp
history_strings.grdp
javascript_dialogs_strings.grdp
live_caption_strings.grdp
login_dialog_strings.grdp
management_ios_strings.grdp
management_strings.grdp
media_message_center_strings.grdp
new_or_sad_tab_strings.grdp
omnibox_pedal_ui_strings.grdp
omnibox_strings.grdp
page_info_strings.grdp
paint_preview_strings.grdp
password_manager_strings.grdp
payments_strings.grdp
pdf_strings.grdp
permissions_strings.grdp
policy_strings.grdp
print_media_strings.grdp
printing_component_strings.grdp
privacy_sandbox_strings.grdp
protocol_handler_strings.grdp
reset_password_strings.grdp
security_interstitials_strings.grdp
send_tab_to_self_strings.grdp
site_settings_strings.grdp
sms_strings.grdp
ssl_errors_strings.grdp
subresource_filter_strings.grdp
supervised_user_strings.grdp
sync_ui_strings.grdp
tab_groups_strings.grdp
translate_strings.grdp
undo_strings.grdp
user_education_strings.grdp
version_ui_strings.grdp
webapps_strings.grdp
webxr_strings.grdp
content
courgette
crypto
dbus
device
docs
extensions
fuchsia_web
gin
google_apis
google_update
gpu
headless
infra
ios
ipc
media
mojo
native_client_sdk
net
pdf
ppapi
printing
remoting
rlz
sandbox
services
skia
sql
storage
styleguide
testing
third_party
tools
ui
url
weblayer
.clang-format
.clang-tidy
.eslintrc.js
.git-blame-ignore-revs
.gitattributes
.gitignore
.gn
.mailmap
.rustfmt.toml
.vpython3
.yapfignore
ATL_OWNERS
AUTHORS
BUILD.gn
CODE_OF_CONDUCT.md
DEPS
DIR_METADATA
LICENSE
LICENSE.chromium_os
OWNERS
PRESUBMIT.py
PRESUBMIT_test.py
PRESUBMIT_test_mocks.py
README.md
WATCHLISTS
codereview.settings
src/components/client_hints
Matt Menke 8cd62b5d80 Remove Value::DictItems(), replacing it with Value::GetDict().
Value::DictItems() is deprecated. This CL removes it, and replaces all
remaining calls with Value::GetDict(), which works without any more
extensive changes.

A couple callers were calling PrefService::GetValue() to get the value
they were calling DictItems() on. Replaces those few calls with
PrefService::GetDict(), which gets a Value::Dict directly (CHECKing if
it's not a dict), removing the need for a Value::GetDict() call.

Bug: 1187001
Change-Id: I0637b57bb5ad0dc002835466c760ba64633867a4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4377522
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Reviewed-by: Ali Juma <ajuma@chromium.org>
Auto-Submit: Matt Menke <mmenke@chromium.org>
Reviewed-by: Luke Halliwell <halliwell@chromium.org>
Reviewed-by: Colin Blundell <blundell@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Jack Shira <jackshira@google.com>
Commit-Queue: Matt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1123641}
2023-03-29 16:30:51 +00:00
..

Client Hints

This README will serve as a reference for the Chromium implementation of HTTP Client Hints, the HTML and Fetch integrations, and the Critical-CH response header as defined in the Client Hints Reliability draft.

The code can be found in the following directories:

[TOC]

What Client Hints are (and how they work)

HTTP Client Hints are request headers that can be optionally sent to origins that signal they want extra information via a response header (Accept-CH). When an origin sends an Accept-CH header with a (comma separated) list of client hint headers it would like to receive (on a secure top-level navigation request) those preferences are stored by the browser. Every subsequent request to an origin will contain those extra client hint request headers, as described in the HTTP Client Hints specification. This cache is cleared when session cookies are cleared, or when a user clears site data or cookies for a given origin.

Sub-resource delegation

Every document created with that origin contains those preferences as a “client hint set” and uses that set alongside other settings to decide what client hints to delegate to sub-resource requests associated with that document.

When requests are initiated from a document, the client hints are filtered through Permission Policies, which allows origins to control what features are used by what 3rd parties in a document. By default, the feature policies for client hints (except Sec-CH-UA and Sec-CH-UA-Mobile) are set to “self,” which means that hints are only delegated to the same origin as the (top-level) document. The permission can also be a list of origins or an asterisk * for all origins (Sec-CH-UA and Sec-CH-UA-Mobile are considered “low-entropy” and safe to send to all origins, thus their defaults are *). Permissions can also be set in HTML for iframes in the same format through the allow attribute.

Note: All client hints (top-level and subresource) are gated on JavaScript being enabled in Chrome. While not explicitly stated, it fits into the requirement to only reveal information visible through JavaScript interfaces.

Client Hint Reliability

There are two situations where a request could have hints that are different from what the origin wanted:

  1. The origin and browsers client hints preferences are out of sync (either because the site has not been visited or because the sites preferences have changed since the last time), OR
  2. The browser does not wish to send the requested client hint (e.g. it goes against user preferences or because of some browser controlled privacy mechanism)

As HTTP Client Hints are defined, theres no way to know which is the case. Two mechanisms were defined in the Client Hints Reliability proposal:

  • an HTTP-header-based retry to ensure critical Client Hints are reliably available ("Critical-CH")
  • a connection-level optimization to avoid the performance hit of a retry in most cases ("ACCEPT_CH")

An explainer for both can be found here

Critical-CH retry mechanism

The Critical-CH response header is a signal from the origin that the hints listed are important to have on the very first request (i.e. make a meaningful change to the request). The algorithm is fairly straightforward:

  1. Determine what hints would be sent with the newly-received Accept-CH header (including user preferences and local policy)
  2. Find the difference between those hints and the hints that were sent with the initial request
  3. If any hints are present in the Critical-CH list that were not sent but would have been:
    1. retry the request with the new client hint set.

ACCEPT_CH HTTP2/3 frame and ALPS TLS extension

The ALPS ("Application-Layer Protocol Settings") TLS extension allows applications to add application-layer protocol settings to the TLS handshake, notably before the "first" round trip of the application protocol itself.

For the purposes of Client-Hints, this means that an "ACCEPT_CH" HTTP2/3 frame can be added to the handshake, which sets a connection-level addition to the "Accept-CH" cache, meaning the browser can see these settings and reach appropriately before sending the request to a server.

The full explanation is outside of the scope of this document and can be found in the reliability explainer linked above.

Implementation

Accept-CH cache

Client Hint preferences are stored in the preferences service as a content setting (ContentSettingsType::CLIENT_HINTS), keyed to the origin. This storage is accessed through the content::ClientHintsControllerDelegate interface, with the principle implementation being client_hints::ClientHints in //components (to share across multiple platforms). The delegate is accessible in the browser process as a property of the content::BrowserContext (in //chrome land, this is implemented as the Profile and “Off The Record” Profile. An important note is that there is an “incognito profile” that gets its own client hints storage).

This storage is marked as content_settings::SessionModel::Durable. This means that the client hint settings are read in from disk on browser start up and loaded into memory. Practically, this means that the client hint settings persist until the user clears site data or cookies for the origin.

The code for reading from and writing to the client hint preferences in content is in /content/browser/client_hints/client_hints.cc

The preferences are read on the construction of a ClientHintsExtendedData object, which then will use the FrameTreeNode (which is where the object gets first party origin and permission policy information) and client hints preferences to calculate what hints should be sent for a given request.

The preferences are written in ParseAndPersistAcceptCHForNavigation, which is also where various settings (secure context, JS permissions, feature flags set) are checked before sending the information to the controller delegate.

Client Hints Infrastructure

The client hints set is passed into the document on commit from NavigationRequest::CommitNavigation to the document and is used in FrameFetchContext::AddClientHintsIfNecessary, where all of the relevant client hint information gets filled into the headers to be sent.

Critical-CH response header

The Critical-CH retry mechanism is implemented as content::CriticalClientHintsThrottle and all of the important logic is in WillProcessResponse. When a retry situation is found (and the redirected_ flag isnt set) the header is stored, the new hints are stored as normal and the request is “restarted” (i.e. the response is changed to an internal redirect to the same location, which is also what DevTools sees).

ACCEPT_CH restart

The ACCEPT_CH restart mechanism is implemented with the navigation stack. The mojo interface network::mojom::AcceptCHFrameObserver is implemented by content::NavigationURLLoaderImpl and the resulting pipe is passed to the URLLoaderFactory and so on through the network::ResourceRequest and related mojo interface.

On the network side, when a TLS socket is selected (either created or re-used from the socket pool) that contains an ACCEPT_CH ALPS frame, it's checked against the headers in the request in ComputeAcceptCHFrame, which removes any tokens from the ACCEPT_CH frame that are already present in the request as headers and checks if the result is empty.

If the result is not empty, it's passed through the AcceptCHFrameObserver mojo pipe (back to content::NavigationURLLoaderImpl) to the browser, and if new headers are added the and navigation is restarted.

Testing

Incognito mode

Any storage related to an incognito mode profile is cleared when the last incognito tab is closed, including client hint preferences.

--initial-client-hint-storage Command Line Switch

A command line flag is available for testing to pre-populate the Accept-CH cache. It takes a json dictionary, where each key is an origin and each value is a string in the same format as the Accept-CH response header.

Each new profile will include these pre-populated preferences except for "Off The Record" profiles (e.g. guest profiles and incognito profiles).

Note: Don't forget to escape quotes if your shell needs it.

An example use might be:

out/default/chrome --initial-client-hint-storage="{\"https://a.test\":\"Sec-CH-UA-Full-Version\", \"https://b.test\":\"Sec-CH-UA-Model\"}"

Adding a new hint

Theres two main steps to adding a hint to Chromium: adding the token, and populating the value when appropriate

Adding a new client hint token

The canonical enum for client hint tokens is network::mojom::WebClientHintsType. Any new token should be added to the end of that list. Along with that:

NOTE: Its very important that the order of these arrays remain in sync.

There should also be a new feature policy created:

The header should also be added to the cors safe_names list in /services/network/public/cpp/cors/cors.cc and update its test.

TODO(crbug.com/1176808): There should be UseCounters measuring usage, but there are not currently.

Populating the client hint

Client Hints are populated in BaseFetchContext::AddClientHintsIfNecessary. If you need frame-based information, this should be added to ClientHintsImageInfo, which is populated in FrameFetchContext::AddClientHintsIfNecessary

Web platform tests

Devtools Backend

Devtools Frontend

Devtools frontend source code is in a different branch devtools/devtools-frontend.