blob: d9cd3f7c287adcc6c612d3616ee839f09aba9721 [file] [log] [blame]
John Abd-El-Malekec1fc69e2021-01-28 19:14:411// 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
5#include "components/embedder_support/user_agent_utils.h"
6
7#include "base/command_line.h"
Lei Zhangfcf71672021-05-14 16:28:208#include "base/no_destructor.h"
John Abd-El-Malekec1fc69e2021-01-28 19:14:419#include "base/strings/strcat.h"
Aaron Tagliaboschic42d0b612021-06-17 15:08:2310#include "base/strings/stringprintf.h"
11#include "base/system/sys_info.h"
John Abd-El-Malekec1fc69e2021-01-28 19:14:4112#include "build/branding_buildflags.h"
13#include "components/embedder_support/switches.h"
14#include "components/version_info/version_info.h"
15#include "content/public/browser/web_contents.h"
16#include "content/public/common/content_features.h"
17#include "content/public/common/content_switches.h"
18#include "content/public/common/user_agent.h"
19#include "net/http/http_util.h"
20#include "third_party/blink/public/common/features.h"
21#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
22
23namespace embedder_support {
24
25std::string GetProduct() {
26 return version_info::GetProductNameAndVersionForUserAgent();
27}
28
29std::string GetUserAgent() {
30 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
31 if (command_line->HasSwitch(kUserAgent)) {
32 std::string ua = command_line->GetSwitchValueASCII(kUserAgent);
33 if (net::HttpUtil::IsValidHeaderValue(ua))
34 return ua;
35 LOG(WARNING) << "Ignored invalid value for flag --" << kUserAgent;
36 }
37
Aaron Tagliaboschi2fa190652021-07-30 19:02:0638 if (base::FeatureList::IsEnabled(blink::features::kReduceUserAgent))
39 return GetReducedUserAgent();
John Abd-El-Malekec1fc69e2021-01-28 19:14:4140
41 std::string product = GetProduct();
42#if defined(OS_ANDROID)
43 if (command_line->HasSwitch(switches::kUseMobileUserAgent))
44 product += " Mobile";
45#endif
46 return content::BuildUserAgentFromProduct(product);
47}
48
Aaron Tagliaboschi2fa190652021-07-30 19:02:0649std::string GetReducedUserAgent() {
50 return content::GetReducedUserAgent(
51 base::CommandLine::ForCurrentProcess()->HasSwitch(
52 switches::kUseMobileUserAgent),
53 version_info::GetMajorVersionNumber());
54}
55
John Abd-El-Malekec1fc69e2021-01-28 19:14:4156// Generate a pseudo-random permutation of the following brand/version pairs:
57// 1. The base project (i.e. Chromium)
58// 2. The browser brand, if available
59// 3. A randomized string containing escaped characters to ensure proper
60// header parsing, along with an arbitrarily low version to ensure proper
61// version checking.
62blink::UserAgentBrandList GenerateBrandVersionList(
63 int seed,
Anton Bikineev1156b5f2021-05-15 22:35:3664 absl::optional<std::string> brand,
John Abd-El-Malekec1fc69e2021-01-28 19:14:4165 std::string major_version,
Anton Bikineev1156b5f2021-05-15 22:35:3666 absl::optional<std::string> maybe_greasey_brand) {
John Abd-El-Malekec1fc69e2021-01-28 19:14:4167 DCHECK_GE(seed, 0);
68 const int npermutations = 6; // 3!
69 int permutation = seed % npermutations;
70
71 // Pick a stable permutation seeded by major version number. any values here
72 // and in order should be under three.
73 const std::vector<std::vector<int>> orders{{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
74 {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
75 const std::vector<int> order = orders[permutation];
76 DCHECK_EQ(6u, orders.size());
77 DCHECK_EQ(3u, order.size());
78
79 // Previous values for indexes 0 and 1 were '\' and '"', temporarily removed
80 // because of compat issues
81 const std::vector<std::string> escaped_chars = {" ", " ", ";"};
82 std::string greasey_brand =
83 base::StrCat({escaped_chars[order[0]], "Not", escaped_chars[order[1]],
84 "A", escaped_chars[order[2]], "Brand"});
85
86 blink::UserAgentBrandVersion greasey_bv = {
87 maybe_greasey_brand.value_or(greasey_brand), "99"};
88 blink::UserAgentBrandVersion chromium_bv = {"Chromium", major_version};
89
90 blink::UserAgentBrandList greased_brand_version_list(3);
91
92 if (brand) {
93 blink::UserAgentBrandVersion brand_bv = {brand.value(), major_version};
94
95 greased_brand_version_list[order[0]] = greasey_bv;
96 greased_brand_version_list[order[1]] = chromium_bv;
97 greased_brand_version_list[order[2]] = brand_bv;
98 } else {
99 greased_brand_version_list[seed % 2] = greasey_bv;
100 greased_brand_version_list[(seed + 1) % 2] = chromium_bv;
101
102 // If left, the last element would make a blank "" at the end of the header.
103 greased_brand_version_list.pop_back();
104 }
105
106 return greased_brand_version_list;
107}
108
109const blink::UserAgentBrandList& GetBrandVersionList() {
110 static const base::NoDestructor<blink::UserAgentBrandList>
111 greased_brand_version_list([] {
112 int major_version_number;
113 std::string major_version = version_info::GetMajorVersionNumber();
114 base::StringToInt(major_version, &major_version_number);
Anton Bikineev1156b5f2021-05-15 22:35:36115 absl::optional<std::string> brand;
John Abd-El-Malekec1fc69e2021-01-28 19:14:41116#if !BUILDFLAG(CHROMIUM_BRANDING)
117 brand = version_info::GetProductName();
118#endif
Anton Bikineev1156b5f2021-05-15 22:35:36119 absl::optional<std::string> maybe_param_override =
John Abd-El-Malekec1fc69e2021-01-28 19:14:41120 base::GetFieldTrialParamValueByFeature(features::kGreaseUACH,
121 "brand_override");
122 if (maybe_param_override->empty())
Anton Bikineev1156b5f2021-05-15 22:35:36123 maybe_param_override = absl::nullopt;
John Abd-El-Malekec1fc69e2021-01-28 19:14:41124
125 return GenerateBrandVersionList(major_version_number, brand,
126 major_version, maybe_param_override);
127 }());
128 return *greased_brand_version_list;
129}
130
Aaron Tagliaboschie2e23a72021-01-29 15:42:02131// TODO(crbug.com/1103047): This can be removed/re-refactored once we use
132// "macOS" by default
133std::string GetPlatformForUAMetadata() {
134#if defined(OS_MAC)
135 return "macOS";
136#else
137 return version_info::GetOSType();
138#endif
139}
140
John Abd-El-Malekec1fc69e2021-01-28 19:14:41141blink::UserAgentMetadata GetUserAgentMetadata() {
142 blink::UserAgentMetadata metadata;
143
144 metadata.brand_version_list = GetBrandVersionList();
145 metadata.full_version = version_info::GetVersionNumber();
Aaron Tagliaboschie2e23a72021-01-29 15:42:02146 metadata.platform = GetPlatformForUAMetadata();
John Abd-El-Malekec1fc69e2021-01-28 19:14:41147 metadata.architecture = content::GetLowEntropyCpuArchitecture();
148 metadata.model = content::BuildModelInfo();
John Abd-El-Malekec1fc69e2021-01-28 19:14:41149 metadata.mobile = false;
150#if defined(OS_ANDROID)
151 metadata.mobile = base::CommandLine::ForCurrentProcess()->HasSwitch(
152 switches::kUseMobileUserAgent);
153#endif
154
Aaron Tagliaboschic42d0b612021-06-17 15:08:23155 int32_t major, minor, bugfix = 0;
156 base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
157 metadata.platform_version =
158 base::StringPrintf("%d.%d.%d", major, minor, bugfix);
Aaron Tagliaboschi1649e102021-06-18 23:15:07159 // These methods use the same information as the User-Agent string, but are
160 // "low entropy" in that they reduce the number of options for output to a
161 // set number. For more information, see the respective headers.
162 metadata.architecture = content::GetLowEntropyCpuArchitecture();
163 metadata.bitness = content::GetLowEntropyCpuBitness();
Aaron Tagliaboschic42d0b612021-06-17 15:08:23164
John Abd-El-Malekec1fc69e2021-01-28 19:14:41165 return metadata;
166}
167
168#if defined(OS_ANDROID)
169void SetDesktopUserAgentOverride(content::WebContents* web_contents,
Gang Wub14b3022021-03-25 22:53:48170 const blink::UserAgentMetadata& metadata,
171 bool override_in_new_tabs) {
John Abd-El-Malekec1fc69e2021-01-28 19:14:41172 const char kLinuxInfoStr[] = "X11; Linux x86_64";
173 std::string product = version_info::GetProductNameAndVersionForUserAgent();
174
175 blink::UserAgentOverride spoofed_ua;
176 spoofed_ua.ua_string_override =
177 content::BuildUserAgentFromOSAndProduct(kLinuxInfoStr, product);
178 spoofed_ua.ua_metadata_override = metadata;
179 spoofed_ua.ua_metadata_override->platform = "Linux";
180 spoofed_ua.ua_metadata_override->platform_version =
181 std::string(); // match content::GetOSVersion(false) on Linux
John Abd-El-Malekec1fc69e2021-01-28 19:14:41182 spoofed_ua.ua_metadata_override->model = std::string();
183 spoofed_ua.ua_metadata_override->mobile = false;
Aaron Tagliaboschi1649e102021-06-18 23:15:07184 // Match the above "CpuInfo" string, which is also the most common Linux
185 // CPU architecture and bitness.`
186 spoofed_ua.ua_metadata_override->architecture = "x86";
187 spoofed_ua.ua_metadata_override->bitness = "64";
John Abd-El-Malekec1fc69e2021-01-28 19:14:41188
Gang Wub14b3022021-03-25 22:53:48189 web_contents->SetUserAgentOverride(spoofed_ua, override_in_new_tabs);
John Abd-El-Malekec1fc69e2021-01-28 19:14:41190}
191#endif // OS_ANDROID
192
193} // namespace embedder_support