blob: 9ca4772b70486dae09089e43a9ac3ca40a4e035f [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/public/common/client_hints/client_hints.h"
#include <utility>
#include <vector>
#include "base/feature_list.h"
#include "base/optional.h"
#include "base/stl_util.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "third_party/blink/public/common/feature_policy/feature_policy.h"
#include "third_party/blink/public/common/features.h"
#include "url/origin.h"
namespace blink {
const char* const kClientHintsHeaderMapping[] = {
"device-memory",
"dpr",
"width",
"viewport-width",
"rtt",
"downlink",
"ect",
"sec-ch-lang",
"sec-ch-ua",
"sec-ch-ua-arch",
"sec-ch-ua-platform",
"sec-ch-ua-model",
"sec-ch-ua-mobile",
"sec-ch-ua-full-version",
"sec-ch-ua-platform-version",
};
const unsigned kClientHintsNumberOfLegacyHints = 4;
const mojom::FeaturePolicyFeature kClientHintsFeaturePolicyMapping[] = {
// Legacy Hints that are sent cross-origin regardless of FeaturePolicy when
// kAllowClientHintsToThirdParty is enabled
mojom::FeaturePolicyFeature::kClientHintDeviceMemory,
mojom::FeaturePolicyFeature::kClientHintDPR,
mojom::FeaturePolicyFeature::kClientHintWidth,
mojom::FeaturePolicyFeature::kClientHintViewportWidth,
// End of legacy hints.
mojom::FeaturePolicyFeature::kClientHintRTT,
mojom::FeaturePolicyFeature::kClientHintDownlink,
mojom::FeaturePolicyFeature::kClientHintECT,
mojom::FeaturePolicyFeature::kClientHintLang,
mojom::FeaturePolicyFeature::kClientHintUA,
mojom::FeaturePolicyFeature::kClientHintUAArch,
mojom::FeaturePolicyFeature::kClientHintUAPlatform,
mojom::FeaturePolicyFeature::kClientHintUAModel,
mojom::FeaturePolicyFeature::kClientHintUAMobile,
mojom::FeaturePolicyFeature::kClientHintUAFullVersion,
mojom::FeaturePolicyFeature::kClientHintUAPlatformVersion,
};
const size_t kClientHintsMappingsCount = base::size(kClientHintsHeaderMapping);
static_assert(
base::size(kClientHintsHeaderMapping) ==
(static_cast<int>(network::mojom::WebClientHintsType::kMaxValue) + 1),
"Client Hint name table size must match network::mojom::WebClientHintsType "
"range");
static_assert(base::size(kClientHintsFeaturePolicyMapping) ==
kClientHintsMappingsCount,
"Client Hint table sizes must be identical between names and "
"feature policies");
const char* const kWebEffectiveConnectionTypeMapping[] = {
"4g" /* Unknown */, "4g" /* Offline */, "slow-2g" /* Slow 2G */,
"2g" /* 2G */, "3g" /* 3G */, "4g" /* 4G */
};
const size_t kWebEffectiveConnectionTypeMappingCount =
base::size(kWebEffectiveConnectionTypeMapping);
std::string SerializeLangClientHint(const std::string& raw_language_list) {
base::StringTokenizer t(raw_language_list, ",");
std::string result;
while (t.GetNext()) {
if (!result.empty())
result.append(", ");
result.append("\"");
result.append(t.token().c_str());
result.append("\"");
}
return result;
}
base::Optional<std::vector<network::mojom::WebClientHintsType>> FilterAcceptCH(
base::Optional<std::vector<network::mojom::WebClientHintsType>> in,
bool permit_lang_hints,
bool permit_ua_hints) {
if (!in.has_value())
return base::nullopt;
std::vector<network::mojom::WebClientHintsType> result;
for (network::mojom::WebClientHintsType hint : in.value()) {
// Some hints are supported only conditionally.
switch (hint) {
case network::mojom::WebClientHintsType::kLang:
if (permit_lang_hints)
result.push_back(hint);
break;
case network::mojom::WebClientHintsType::kUA:
case network::mojom::WebClientHintsType::kUAArch:
case network::mojom::WebClientHintsType::kUAPlatform:
case network::mojom::WebClientHintsType::kUAPlatformVersion:
case network::mojom::WebClientHintsType::kUAModel:
case network::mojom::WebClientHintsType::kUAMobile:
case network::mojom::WebClientHintsType::kUAFullVersion:
if (permit_ua_hints)
result.push_back(hint);
break;
default:
result.push_back(hint);
}
}
return base::make_optional(std::move(result));
}
bool IsClientHintSentByDefault(network::mojom::WebClientHintsType type) {
return (type == network::mojom::WebClientHintsType::kUA ||
type == network::mojom::WebClientHintsType::kUAMobile);
}
// Add a list of Client Hints headers to be removed to the output vector, based
// on FeaturePolicy and the url's origin.
void FindClientHintsToRemove(const FeaturePolicy* feature_policy,
const GURL& url,
std::vector<std::string>* removed_headers) {
DCHECK(removed_headers);
url::Origin origin = url::Origin::Create(url);
size_t startHint = 0;
if (base::FeatureList::IsEnabled(features::kAllowClientHintsToThirdParty)) {
// Do not remove any legacy Client Hints
startHint = kClientHintsNumberOfLegacyHints;
}
for (size_t i = startHint; i < blink::kClientHintsMappingsCount; ++i) {
// Remove the hint if any is true:
// * Feature policy is null (we're in a sync XHR case) and the hint is not
// sent by default.
// * Feature policy exists and doesn't allow for the hint.
if ((!feature_policy &&
!IsClientHintSentByDefault(
static_cast<network::mojom::WebClientHintsType>(i))) ||
(feature_policy &&
!feature_policy->IsFeatureEnabledForOrigin(
blink::kClientHintsFeaturePolicyMapping[i], origin))) {
removed_headers->push_back(blink::kClientHintsHeaderMapping[i]);
}
}
}
} // namespace blink