user_agent_utils.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/embedder_support/user_agent_utils.h"
  5. #include "base/command_line.h"
  6. #include "base/debug/stack_trace.h"
  7. #include "base/feature_list.h"
  8. #include "base/no_destructor.h"
  9. #include "base/strings/strcat.h"
  10. #include "base/strings/string_number_conversions.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/system/sys_info.h"
  13. #include "base/version.h"
  14. #include "build/branding_buildflags.h"
  15. #include "build/build_config.h"
  16. #include "components/embedder_support/pref_names.h"
  17. #include "components/embedder_support/switches.h"
  18. #include "components/policy/core/common/policy_pref_names.h"
  19. #include "components/prefs/pref_service.h"
  20. #include "components/version_info/version_info.h"
  21. #include "content/public/browser/web_contents.h"
  22. #include "content/public/common/content_features.h"
  23. #include "content/public/common/content_switches.h"
  24. #include "content/public/common/user_agent.h"
  25. #include "net/http/http_util.h"
  26. #include "third_party/blink/public/common/features.h"
  27. #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
  28. #if BUILDFLAG(IS_WIN)
  29. #include <windows.h>
  30. #include "base/win/registry.h"
  31. #include "base/win/windows_version.h"
  32. #endif // BUILDFLAG(IS_WIN)
  33. namespace embedder_support {
  34. namespace {
  35. constexpr char kVersion99[] = "99";
  36. #if BUILDFLAG(IS_WIN)
  37. // The registry key where the UniversalApiContract version value can be read
  38. // from.
  39. constexpr wchar_t kWindowsRuntimeWellKnownContractsRegKeyName[] =
  40. L"SOFTWARE\\Microsoft\\WindowsRuntime\\WellKnownContracts";
  41. // Name of the UniversalApiContract registry.
  42. constexpr wchar_t kUniversalApiContractName[] =
  43. L"Windows.Foundation.UniversalApiContract";
  44. // There's a chance that access to the registry key that contains the
  45. // UniversalApiContract Version will not be available in the future. After we
  46. // confirm that our Windows version is RS5 or greater, it is best to have the
  47. // default return value be the highest known version number at the time this
  48. // code is submitted. If the UniversalApiContract registry key is no longer
  49. // available, there will either be a new API introduced, or we will need
  50. // to rely on querying the IsApiContractPresentByMajor function used by
  51. // user_agent_utils_unittest.cc.
  52. const int kHighestKnownUniversalApiContractVersion = 14;
  53. int GetPreRS5UniversalApiContractVersion() {
  54. if (base::win::GetVersion() == base::win::Version::WIN10)
  55. return 1;
  56. if (base::win::GetVersion() == base::win::Version::WIN10_TH2)
  57. return 2;
  58. if (base::win::GetVersion() == base::win::Version::WIN10_RS1)
  59. return 3;
  60. if (base::win::GetVersion() == base::win::Version::WIN10_RS2)
  61. return 4;
  62. if (base::win::GetVersion() == base::win::Version::WIN10_RS3)
  63. return 5;
  64. if (base::win::GetVersion() == base::win::Version::WIN10_RS4)
  65. return 6;
  66. // The list above should account for all Windows versions prior to
  67. // RS5.
  68. NOTREACHED();
  69. return 0;
  70. }
  71. // Return the legacy windows platform version to match the spec description
  72. // https://wicg.github.io/ua-client-hints/#get-the-legacy-windows-version-number,
  73. // which is available for Windows versions between range WIN7 and WIN8_1.
  74. // Otherwise, returns 0.
  75. const std::string& GetLegacyWindowsPlatformVersion() {
  76. static const base::NoDestructor<std::string> legacy_windows_platform_version(
  77. [] {
  78. int major_version = 0;
  79. int minor_version = 0;
  80. switch (base::win::GetVersion()) {
  81. case base::win::Version::WIN7:
  82. minor_version = 1;
  83. break;
  84. case base::win::Version::WIN8:
  85. minor_version = 2;
  86. break;
  87. case base::win::Version::WIN8_1:
  88. minor_version = 3;
  89. break;
  90. default:
  91. minor_version = 0;
  92. break;
  93. }
  94. return base::StrCat({base::NumberToString(major_version), ".",
  95. base::NumberToString(minor_version), ".0"});
  96. }());
  97. return *legacy_windows_platform_version;
  98. }
  99. // Returns the UniversalApiContract version number, which is available for
  100. // Windows versions greater than RS5. Otherwise, returns 0.
  101. const std::string& GetUniversalApiContractVersion() {
  102. // Do not use this for runtime environment detection logic. This method should
  103. // only be used to help populate the Sec-CH-UA-Platform client hint. If
  104. // authoring code that depends on a minimum API contract version being
  105. // available, you should instead leverage the OS's IsApiContractPresentByMajor
  106. // method.
  107. static const base::NoDestructor<std::string> universal_api_contract_version(
  108. [] {
  109. int major_version = 0;
  110. int minor_version = 0;
  111. if (base::win::GetVersion() >= base::win::Version::WIN10) {
  112. if (base::win::GetVersion() <= base::win::Version::WIN10_RS4) {
  113. major_version = GetPreRS5UniversalApiContractVersion();
  114. } else {
  115. base::win::RegKey version_key(
  116. HKEY_LOCAL_MACHINE, kWindowsRuntimeWellKnownContractsRegKeyName,
  117. KEY_QUERY_VALUE | KEY_WOW64_64KEY);
  118. if (version_key.Valid()) {
  119. DWORD universal_api_contract_version = 0;
  120. LONG result = version_key.ReadValueDW(
  121. kUniversalApiContractName, &universal_api_contract_version);
  122. if (result == ERROR_SUCCESS) {
  123. major_version = HIWORD(universal_api_contract_version);
  124. minor_version = LOWORD(universal_api_contract_version);
  125. } else {
  126. major_version = kHighestKnownUniversalApiContractVersion;
  127. }
  128. } else {
  129. major_version = kHighestKnownUniversalApiContractVersion;
  130. }
  131. }
  132. }
  133. // The major version of the contract is stored in the HIWORD, while the
  134. // minor version is stored in the LOWORD.
  135. return base::StrCat({base::NumberToString(major_version), ".",
  136. base::NumberToString(minor_version), ".0"});
  137. }());
  138. return *universal_api_contract_version;
  139. }
  140. const std::string& GetWindowsPlatformVersion() {
  141. if (base::win::GetVersion() < base::win::Version::WIN10) {
  142. return GetLegacyWindowsPlatformVersion();
  143. }
  144. return GetUniversalApiContractVersion();
  145. }
  146. #endif // BUILDFLAG(IS_WIN)
  147. // Returns true if the user agent string should force the major version into
  148. // the minor position.
  149. // TODO(crbug.com/1290820): Remove this method along with policy.
  150. bool ShouldForceMajorVersionToMinorPosition(
  151. ForceMajorVersionToMinorPosition force_major_to_minor) {
  152. return (
  153. (force_major_to_minor !=
  154. ForceMajorVersionToMinorPosition::kForceDisabled &&
  155. base::FeatureList::IsEnabled(
  156. blink::features::kForceMajorVersionInMinorPositionInUserAgent)) ||
  157. force_major_to_minor == ForceMajorVersionToMinorPosition::kForceEnabled);
  158. }
  159. // Returns true if the user agent reduction should be forced (or prevented).
  160. // TODO(crbug.com/1330890): Remove this method along with policy.
  161. bool ShouldReduceUserAgentMinorVersion(
  162. UserAgentReductionEnterprisePolicyState user_agent_reduction) {
  163. return ((user_agent_reduction !=
  164. UserAgentReductionEnterprisePolicyState::kForceDisabled &&
  165. base::FeatureList::IsEnabled(
  166. blink::features::kReduceUserAgentMinorVersion)) ||
  167. user_agent_reduction ==
  168. UserAgentReductionEnterprisePolicyState::kForceEnabled);
  169. }
  170. #if !BUILDFLAG(IS_ANDROID)
  171. // Returns true if both kReduceUserAgentMinorVersionName and
  172. // kReduceUserAgentPlatformOsCpu are enabled. It makes
  173. // kReduceUserAgentPlatformOsCpu depend on kReduceUserAgentMinorVersionName.
  174. // It helps us avoid introducing individual enterprise policy controls for
  175. // reducing the user agent platform and oscpu.
  176. bool ShouldReduceUserAgentPlatformOsCpu(
  177. UserAgentReductionEnterprisePolicyState user_agent_reduction) {
  178. // For legacy windows, only reduce the user agent platform and oscpu when
  179. // kLegacyWindowsPlatform parameter set to true.
  180. #if BUILDFLAG(IS_WIN)
  181. if (base::win::GetVersion() < base::win::Version::WIN10) {
  182. return ShouldReduceUserAgentMinorVersion(user_agent_reduction) &&
  183. base::FeatureList::IsEnabled(
  184. blink::features::kReduceUserAgentPlatformOsCpu) &&
  185. blink::features::kLegacyWindowsPlatform.Get();
  186. }
  187. #endif
  188. return ShouldReduceUserAgentMinorVersion(user_agent_reduction) &&
  189. base::FeatureList::IsEnabled(
  190. blink::features::kReduceUserAgentPlatformOsCpu) &&
  191. blink::features::kAllExceptLegacyWindowsPlatform.Get();
  192. }
  193. #endif
  194. const std::string& GetMajorInMinorVersionNumber() {
  195. static const base::NoDestructor<std::string> version_number([] {
  196. base::Version version(version_info::GetVersionNumber());
  197. std::string version_str;
  198. const std::vector<uint32_t>& components = version.components();
  199. for (size_t i = 0; i < components.size(); ++i) {
  200. if (i > 0) {
  201. version_str.append(".");
  202. }
  203. if (i == 0) {
  204. // Hardcode major version to 99
  205. version_str.append(kVersion99);
  206. } else if (i == 1) {
  207. // Force major into minor version
  208. version_str.append(base::NumberToString(components[0]));
  209. } else {
  210. // build and patch stay the same
  211. version_str.append(base::NumberToString(components[i]));
  212. }
  213. }
  214. return version_str;
  215. }());
  216. return *version_number;
  217. }
  218. const std::string& GetReducedMajorInMinorVersionNumber() {
  219. static const base::NoDestructor<std::string> version_number([] {
  220. std::string version_str(kVersion99);
  221. version_str.append(".");
  222. version_str.append(version_info::GetMajorVersionNumber());
  223. version_str.append(".0.0");
  224. return version_str;
  225. }());
  226. return *version_number;
  227. }
  228. std::string GetVersionNumber(const UserAgentOptions& options) {
  229. // Force major version to 99.
  230. if (ShouldForceMajorVersionToMinorPosition(options.force_major_to_minor))
  231. return GetMajorInMinorVersionNumber();
  232. const std::string& version_str = version_info::GetVersionNumber();
  233. return version_str;
  234. }
  235. const blink::UserAgentBrandList GetUserAgentBrandList(
  236. const std::string& major_version,
  237. bool enable_updated_grease_by_policy,
  238. const std::string& full_version,
  239. blink::UserAgentBrandVersionType output_version_type) {
  240. int major_version_number;
  241. bool parse_result = base::StringToInt(major_version, &major_version_number);
  242. DCHECK(parse_result);
  243. absl::optional<std::string> brand;
  244. #if !BUILDFLAG(CHROMIUM_BRANDING)
  245. brand = version_info::GetProductName();
  246. #endif
  247. absl::optional<std::string> maybe_brand_override =
  248. base::GetFieldTrialParamValueByFeature(features::kGreaseUACH,
  249. "brand_override");
  250. absl::optional<std::string> maybe_version_override =
  251. base::GetFieldTrialParamValueByFeature(features::kGreaseUACH,
  252. "version_override");
  253. if (maybe_brand_override->empty())
  254. maybe_brand_override = absl::nullopt;
  255. if (maybe_version_override->empty())
  256. maybe_version_override = absl::nullopt;
  257. std::string brand_version =
  258. output_version_type == blink::UserAgentBrandVersionType::kFullVersion
  259. ? full_version
  260. : major_version;
  261. return GenerateBrandVersionList(major_version_number, brand, brand_version,
  262. maybe_brand_override, maybe_version_override,
  263. enable_updated_grease_by_policy,
  264. output_version_type);
  265. }
  266. const blink::UserAgentBrandList GetUserAgentBrandMajorVersionList(
  267. bool enable_updated_grease_by_policy) {
  268. return GetUserAgentBrandList(version_info::GetMajorVersionNumber(),
  269. enable_updated_grease_by_policy,
  270. version_info::GetVersionNumber(),
  271. blink::UserAgentBrandVersionType::kMajorVersion);
  272. }
  273. // TODO(crbug.com/1290820): Remove this method along with policy.
  274. blink::UserAgentBrandList GetMajorInMinorUserAgentBrandMajorVersionList(
  275. bool enable_updated_grease_by_policy) {
  276. return GetUserAgentBrandList(kVersion99, enable_updated_grease_by_policy,
  277. GetMajorInMinorVersionNumber(),
  278. blink::UserAgentBrandVersionType::kMajorVersion);
  279. }
  280. // TODO(crbug.com/1291612): Consolidate *FullVersionList() methods by using
  281. // GetVersionNumber()
  282. blink::UserAgentBrandList GetUserAgentBrandFullVersionList(
  283. bool enable_updated_grease_by_policy) {
  284. return GetUserAgentBrandList(version_info::GetMajorVersionNumber(),
  285. enable_updated_grease_by_policy,
  286. version_info::GetVersionNumber(),
  287. blink::UserAgentBrandVersionType::kFullVersion);
  288. }
  289. // TODO(crbug.com/1290820): Remove this method along with policy.
  290. blink::UserAgentBrandList GetMajorInMinorUserAgentBrandFullVersionList(
  291. bool enable_updated_grease_by_policy) {
  292. return GetUserAgentBrandList(kVersion99, enable_updated_grease_by_policy,
  293. GetMajorInMinorVersionNumber(),
  294. blink::UserAgentBrandVersionType::kFullVersion);
  295. }
  296. // Return UserAgentBrandList with the major version populated in the brand
  297. // `version` value.
  298. // TODO(crbug.com/1291612): Consolidate *MajorVersionList() methods by using
  299. // GetVersionNumber()
  300. blink::UserAgentBrandList GetBrandMajorVersionList(
  301. bool enable_updated_grease_by_policy,
  302. ForceMajorVersionToMinorPosition force_major_to_minor) {
  303. // Force major version to 99.
  304. if (ShouldForceMajorVersionToMinorPosition(force_major_to_minor))
  305. return GetMajorInMinorUserAgentBrandMajorVersionList(
  306. enable_updated_grease_by_policy);
  307. return GetUserAgentBrandMajorVersionList(enable_updated_grease_by_policy);
  308. }
  309. // Return UserAgentBrandList with the full version populated in the brand
  310. // `version` value.
  311. // TODO(crbug.com/1291612): Consolidate *FullVersionList() methods by using
  312. // GetVersionNumber()
  313. blink::UserAgentBrandList GetBrandFullVersionList(
  314. bool enable_updated_grease_by_policy,
  315. ForceMajorVersionToMinorPosition force_major_to_minor) {
  316. // Force major version to 99.
  317. if (ShouldForceMajorVersionToMinorPosition(force_major_to_minor))
  318. return GetMajorInMinorUserAgentBrandFullVersionList(
  319. enable_updated_grease_by_policy);
  320. return GetUserAgentBrandFullVersionList(enable_updated_grease_by_policy);
  321. }
  322. // Returns a string representing the major version number of the user agent
  323. // string for Chrome, potentially overridden by policy.
  324. std::string GetMajorVersionForUserAgentString(
  325. ForceMajorVersionToMinorPosition force_major_to_minor) {
  326. // Force major version to 99.
  327. if (ShouldForceMajorVersionToMinorPosition(force_major_to_minor))
  328. return kVersion99;
  329. return version_info::GetMajorVersionNumber();
  330. }
  331. } // namespace
  332. std::string GetProductAndVersion(
  333. ForceMajorVersionToMinorPosition force_major_to_minor,
  334. UserAgentReductionEnterprisePolicyState user_agent_reduction) {
  335. if (ShouldForceMajorVersionToMinorPosition(force_major_to_minor)) {
  336. // Force major version to 99 and major version to minor version position.
  337. if (ShouldReduceUserAgentMinorVersion(user_agent_reduction)) {
  338. return "Chrome/" + GetReducedMajorInMinorVersionNumber();
  339. } else {
  340. return "Chrome/" + GetMajorInMinorVersionNumber();
  341. }
  342. } else {
  343. if (ShouldReduceUserAgentMinorVersion(user_agent_reduction)) {
  344. return version_info::GetProductNameAndVersionForReducedUserAgent(
  345. blink::features::kUserAgentFrozenBuildVersion.Get().data());
  346. } else {
  347. return version_info::GetProductNameAndVersionForUserAgent();
  348. }
  349. }
  350. }
  351. // Internal function to handle return the full or "reduced" user agent string,
  352. // depending on the UserAgentReduction enterprise policy.
  353. std::string GetUserAgentInternal(
  354. ForceMajorVersionToMinorPosition force_major_to_minor,
  355. UserAgentReductionEnterprisePolicyState user_agent_reduction) {
  356. std::string product =
  357. GetProductAndVersion(force_major_to_minor, user_agent_reduction);
  358. #if BUILDFLAG(IS_ANDROID)
  359. if (base::CommandLine::ForCurrentProcess()->HasSwitch(
  360. switches::kUseMobileUserAgent))
  361. product += " Mobile";
  362. return content::BuildUserAgentFromProduct(product);
  363. #else
  364. // In User-Agent reduction phase 5, only apply the <unifiedPlatform> to
  365. // desktop UA strings.
  366. return ShouldReduceUserAgentPlatformOsCpu(user_agent_reduction)
  367. ? content::BuildUnifiedPlatformUserAgentFromProduct(product)
  368. : content::BuildUserAgentFromProduct(product);
  369. #endif
  370. }
  371. std::string GetUserAgent(
  372. ForceMajorVersionToMinorPosition force_major_to_minor,
  373. UserAgentReductionEnterprisePolicyState user_agent_reduction) {
  374. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  375. if (command_line->HasSwitch(kUserAgent)) {
  376. std::string ua = command_line->GetSwitchValueASCII(kUserAgent);
  377. if (net::HttpUtil::IsValidHeaderValue(ua))
  378. return ua;
  379. LOG(WARNING) << "Ignored invalid value for flag --" << kUserAgent;
  380. }
  381. if (base::FeatureList::IsEnabled(blink::features::kFullUserAgent))
  382. return GetFullUserAgent(force_major_to_minor);
  383. if (base::FeatureList::IsEnabled(blink::features::kReduceUserAgent))
  384. return GetReducedUserAgent(force_major_to_minor);
  385. return GetUserAgentInternal(force_major_to_minor, user_agent_reduction);
  386. }
  387. std::string GetReducedUserAgent(
  388. ForceMajorVersionToMinorPosition force_major_to_minor) {
  389. return content::GetReducedUserAgent(
  390. base::CommandLine::ForCurrentProcess()->HasSwitch(
  391. switches::kUseMobileUserAgent),
  392. GetMajorVersionForUserAgentString(force_major_to_minor));
  393. }
  394. std::string GetFullUserAgent(
  395. ForceMajorVersionToMinorPosition force_major_to_minor) {
  396. return GetUserAgentInternal(
  397. force_major_to_minor,
  398. UserAgentReductionEnterprisePolicyState::kForceDisabled);
  399. }
  400. // Generate a pseudo-random permutation of the following brand/version pairs:
  401. // 1. The base project (i.e. Chromium)
  402. // 2. The browser brand, if available
  403. // 3. A randomized string containing GREASE characters to ensure proper
  404. // header parsing, along with an arbitrarily low version to ensure proper
  405. // version checking.
  406. blink::UserAgentBrandList GenerateBrandVersionList(
  407. int seed,
  408. absl::optional<std::string> brand,
  409. const std::string& version,
  410. absl::optional<std::string> maybe_greasey_brand,
  411. absl::optional<std::string> maybe_greasey_version,
  412. bool enable_updated_grease_by_policy,
  413. blink::UserAgentBrandVersionType output_version_type) {
  414. DCHECK_GE(seed, 0);
  415. const int npermutations = 6; // 3!
  416. int permutation = seed % npermutations;
  417. // Pick a stable permutation seeded by major version number. any values here
  418. // and in order should be under three.
  419. const std::vector<std::vector<int>> orders{{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
  420. {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
  421. const std::vector<int> order = orders[permutation];
  422. DCHECK_EQ(6u, orders.size());
  423. DCHECK_EQ(3u, order.size());
  424. blink::UserAgentBrandVersion greasey_bv = GetGreasedUserAgentBrandVersion(
  425. order, seed, maybe_greasey_brand, maybe_greasey_version,
  426. enable_updated_grease_by_policy, output_version_type);
  427. blink::UserAgentBrandVersion chromium_bv = {"Chromium", version};
  428. blink::UserAgentBrandList greased_brand_version_list(3);
  429. if (brand) {
  430. blink::UserAgentBrandVersion brand_bv = {brand.value(), version};
  431. greased_brand_version_list[order[0]] = greasey_bv;
  432. greased_brand_version_list[order[1]] = chromium_bv;
  433. greased_brand_version_list[order[2]] = brand_bv;
  434. } else {
  435. greased_brand_version_list[seed % 2] = greasey_bv;
  436. greased_brand_version_list[(seed + 1) % 2] = chromium_bv;
  437. // If left, the last element would make a blank "" at the end of the header.
  438. greased_brand_version_list.pop_back();
  439. }
  440. return greased_brand_version_list;
  441. }
  442. // Process greased overridden brand version which is either major version or
  443. // full version, return the corresponding output version type.
  444. blink::UserAgentBrandVersion GetProcessedGreasedBrandVersion(
  445. const std::string& greasey_brand,
  446. const std::string& greasey_version,
  447. blink::UserAgentBrandVersionType output_version_type) {
  448. std::string greasey_major_version;
  449. std::string greasey_full_version;
  450. base::Version version(greasey_version);
  451. DCHECK(version.IsValid());
  452. // If the greased overridden version is a significant version type:
  453. // * Major version: set the major version as the overridden version
  454. // * Full version number: extending the version number with ".0.0.0"
  455. // If the overridden version is full version format:
  456. // * Major version: set the major version to match significant version format
  457. // * Full version: set the full version as the overridden version
  458. // https://wicg.github.io/ua-client-hints/#user-agent-full-version
  459. if (version.components().size() > 1) {
  460. greasey_major_version = base::NumberToString(version.components()[0]);
  461. greasey_full_version = greasey_version;
  462. } else {
  463. greasey_major_version = greasey_version;
  464. greasey_full_version = base::StrCat({greasey_version, ".0.0.0"});
  465. }
  466. blink::UserAgentBrandVersion output_greasey_bv = {
  467. greasey_brand,
  468. output_version_type == blink::UserAgentBrandVersionType::kFullVersion
  469. ? greasey_full_version
  470. : greasey_major_version};
  471. return output_greasey_bv;
  472. }
  473. blink::UserAgentBrandVersion GetGreasedUserAgentBrandVersion(
  474. std::vector<int> permuted_order,
  475. int seed,
  476. absl::optional<std::string> maybe_greasey_brand,
  477. absl::optional<std::string> maybe_greasey_version,
  478. bool enable_updated_grease_by_policy,
  479. blink::UserAgentBrandVersionType output_version_type) {
  480. std::string greasey_brand;
  481. std::string greasey_version;
  482. // The updated algorithm is enabled by default, but we maintain the ability
  483. // to opt out of it either via Finch (setting updated_algorithm to false) or
  484. // via an enterprise policy escape hatch.
  485. if (enable_updated_grease_by_policy &&
  486. base::GetFieldTrialParamByFeatureAsBool(features::kGreaseUACH,
  487. "updated_algorithm", true)) {
  488. const std::vector<std::string> greasey_chars = {
  489. " ", "(", ":", "-", ".", "/", ")", ";", "=", "?", "_"};
  490. const std::vector<std::string> greased_versions = {"8", "99", "24"};
  491. // See the spec:
  492. // https://wicg.github.io/ua-client-hints/#create-arbitrary-brands-section
  493. greasey_brand = base::StrCat(
  494. {"Not", greasey_chars[(seed) % greasey_chars.size()], "A",
  495. greasey_chars[(seed + 1) % greasey_chars.size()], "Brand"});
  496. greasey_version = greased_versions[seed % greased_versions.size()];
  497. return GetProcessedGreasedBrandVersion(
  498. maybe_greasey_brand.value_or(greasey_brand),
  499. maybe_greasey_version.value_or(greasey_version), output_version_type);
  500. } else {
  501. const std::vector<std::string> greasey_chars = {" ", " ", ";"};
  502. greasey_brand = base::StrCat({greasey_chars[permuted_order[0]], "Not",
  503. greasey_chars[permuted_order[1]], "A",
  504. greasey_chars[permuted_order[2]], "Brand"});
  505. greasey_version = "99";
  506. // The old algorithm is held constant; it does not respond to experiment
  507. // overrides.
  508. return GetProcessedGreasedBrandVersion(greasey_brand, greasey_version,
  509. output_version_type);
  510. }
  511. }
  512. std::string GetPlatformForUAMetadata() {
  513. #if BUILDFLAG(IS_MAC)
  514. // TODO(crbug.com/1103047): This can be removed/re-refactored once we use
  515. // "macOS" by default
  516. return "macOS";
  517. #elif BUILDFLAG(IS_CHROMEOS)
  518. // TODO(crbug.com/1334198): The branding change to remove the space caused a
  519. // regression that's solved here. Ideally, we would just use the new OS name
  520. // without the space here too, but that needs a launch plan.
  521. # if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  522. return "Chrome OS";
  523. # else
  524. return "Chromium OS";
  525. # endif
  526. #else
  527. return version_info::GetOSType();
  528. #endif
  529. }
  530. blink::UserAgentMetadata GetUserAgentMetadata() {
  531. return GetUserAgentMetadata(nullptr);
  532. }
  533. blink::UserAgentMetadata GetUserAgentMetadata(const PrefService* pref_service) {
  534. blink::UserAgentMetadata metadata;
  535. bool enable_updated_grease_by_policy = true;
  536. UserAgentOptions ua_options;
  537. if (pref_service) {
  538. if (pref_service->HasPrefPath(
  539. policy::policy_prefs::kUserAgentClientHintsGREASEUpdateEnabled))
  540. enable_updated_grease_by_policy = pref_service->GetBoolean(
  541. policy::policy_prefs::kUserAgentClientHintsGREASEUpdateEnabled);
  542. ua_options.force_major_to_minor = GetMajorToMinorFromPrefs(pref_service);
  543. }
  544. metadata.brand_version_list = GetBrandMajorVersionList(
  545. enable_updated_grease_by_policy, ua_options.force_major_to_minor);
  546. metadata.brand_full_version_list = GetBrandFullVersionList(
  547. enable_updated_grease_by_policy, ua_options.force_major_to_minor);
  548. metadata.full_version = GetVersionNumber(ua_options);
  549. metadata.platform = GetPlatformForUAMetadata();
  550. metadata.architecture = content::GetCpuArchitecture();
  551. metadata.model = content::BuildModelInfo();
  552. metadata.mobile = false;
  553. #if BUILDFLAG(IS_ANDROID)
  554. metadata.mobile = base::CommandLine::ForCurrentProcess()->HasSwitch(
  555. switches::kUseMobileUserAgent);
  556. #endif
  557. #if BUILDFLAG(IS_WIN)
  558. metadata.platform_version = GetWindowsPlatformVersion();
  559. #else
  560. int32_t major, minor, bugfix = 0;
  561. base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
  562. metadata.platform_version =
  563. base::StringPrintf("%d.%d.%d", major, minor, bugfix);
  564. #endif
  565. metadata.architecture = content::GetCpuArchitecture();
  566. metadata.bitness = content::GetCpuBitness();
  567. metadata.wow64 = content::IsWoW64();
  568. return metadata;
  569. }
  570. #if BUILDFLAG(IS_ANDROID)
  571. void SetDesktopUserAgentOverride(content::WebContents* web_contents,
  572. const blink::UserAgentMetadata& metadata,
  573. bool override_in_new_tabs) {
  574. const char kLinuxInfoStr[] = "X11; Linux x86_64";
  575. blink::UserAgentOverride spoofed_ua;
  576. spoofed_ua.ua_string_override = content::BuildUserAgentFromOSAndProduct(
  577. kLinuxInfoStr, GetProductAndVersion());
  578. spoofed_ua.ua_metadata_override = metadata;
  579. spoofed_ua.ua_metadata_override->platform = "Linux";
  580. spoofed_ua.ua_metadata_override->platform_version =
  581. std::string(); // match content::GetOSVersion(false) on Linux
  582. spoofed_ua.ua_metadata_override->model = std::string();
  583. spoofed_ua.ua_metadata_override->mobile = false;
  584. // Match the above "CpuInfo" string, which is also the most common Linux
  585. // CPU architecture and bitness.`
  586. spoofed_ua.ua_metadata_override->architecture = "x86";
  587. spoofed_ua.ua_metadata_override->bitness = "64";
  588. spoofed_ua.ua_metadata_override->wow64 = false;
  589. web_contents->SetUserAgentOverride(spoofed_ua, override_in_new_tabs);
  590. }
  591. #endif // BUILDFLAG(IS_ANDROID)
  592. #if BUILDFLAG(IS_WIN)
  593. int GetHighestKnownUniversalApiContractVersionForTesting() {
  594. return kHighestKnownUniversalApiContractVersion;
  595. }
  596. #endif // BUILDFLAG(IS_WIN)
  597. // TODO(crbug.com/1290820): Remove this function with policy.
  598. embedder_support::ForceMajorVersionToMinorPosition GetMajorToMinorFromPrefs(
  599. const PrefService* pref_service) {
  600. if (!pref_service->HasPrefPath(kForceMajorVersionToMinorPosition))
  601. return ForceMajorVersionToMinorPosition::kDefault;
  602. switch (pref_service->GetInteger(kForceMajorVersionToMinorPosition)) {
  603. case 1:
  604. return ForceMajorVersionToMinorPosition::kForceDisabled;
  605. case 2:
  606. return ForceMajorVersionToMinorPosition::kForceEnabled;
  607. case 0:
  608. default:
  609. return ForceMajorVersionToMinorPosition::kDefault;
  610. }
  611. }
  612. embedder_support::UserAgentReductionEnterprisePolicyState
  613. GetUserAgentReductionFromPrefs(const PrefService* pref_service) {
  614. if (!pref_service->HasPrefPath(kReduceUserAgentMinorVersion))
  615. return UserAgentReductionEnterprisePolicyState::kDefault;
  616. switch (pref_service->GetInteger(kReduceUserAgentMinorVersion)) {
  617. case 1:
  618. return UserAgentReductionEnterprisePolicyState::kForceDisabled;
  619. case 2:
  620. return UserAgentReductionEnterprisePolicyState::kForceEnabled;
  621. case 0:
  622. default:
  623. return UserAgentReductionEnterprisePolicyState::kDefault;
  624. }
  625. }
  626. } // namespace embedder_support