blob: eaf091dd623aabe69a8e02ce86b0b2364c3ea106 [file] [log] [blame]
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/browser/user_agent_string.h"
#include <vector>
#include "base/string_util.h"
#include "base/stringprintf.h"
#if defined(COBALT_ENABLE_LIB)
#include "cobalt/browser/lib/exported/user_agent.h"
#endif
#include "cobalt/script/javascript_engine.h"
#include "cobalt/version.h"
#include "cobalt_build_id.h" // NOLINT(build/include)
#include "starboard/log.h"
#include "starboard/string.h"
#include "starboard/system.h"
// Setup CobaltLib overrides for some user agent components.
#if defined(COBALT_ENABLE_LIB)
namespace {
// Max length including null terminator.
const size_t kUserAgentPlatformSuffixMaxLength = 128;
char g_user_agent_platform_suffix[kUserAgentPlatformSuffixMaxLength] = {0};
bool g_device_type_override_set = false;
SbSystemDeviceType g_device_type_override = kSbSystemDeviceTypeUnknown;
// Max length including null terminator.
const size_t kPropertyMaxLength = 512;
bool g_brand_name_override_set = false;
char g_brand_name_override[kPropertyMaxLength] = {0};
bool g_model_name_override_set = false;
char g_model_name_override[kPropertyMaxLength] = {0};
bool CopyStringAndTestIfSuccess(char* out_value, size_t value_length,
const char* from_value) {
if (strlen(from_value) + 1 > value_length) return false;
base::strlcpy(out_value, from_value, value_length);
return true;
}
} // namespace
// Allow host app to append a suffix to the reported platform name.
bool CbLibUserAgentSetPlatformNameSuffix(const char* suffix) {
if (!CopyStringAndTestIfSuccess(g_user_agent_platform_suffix,
kPropertyMaxLength, suffix)) {
// If the suffix is too large then nothing is appended to the platform.
g_user_agent_platform_suffix[0] = '\0';
return false;
}
return true;
}
bool CbLibUserAgentSetBrandNameOverride(const char* value) {
if (!value) {
g_brand_name_override_set = false;
return true;
}
if (CopyStringAndTestIfSuccess(g_brand_name_override, kPropertyMaxLength,
value)) {
g_brand_name_override_set = true;
return true;
}
// Failed to reset/set value.
return false;
}
bool CbLibUserAgentSetModelNameOverride(const char* value) {
if (!value) {
g_model_name_override_set = false;
return true;
}
if (CopyStringAndTestIfSuccess(g_model_name_override, kPropertyMaxLength,
value)) {
g_model_name_override_set = true;
return true;
}
// Failed to reset/set value.
return false;
}
bool CbLibUserAgentSetDeviceTypeOverride(SbSystemDeviceType device_type) {
switch (device_type) {
case kSbSystemDeviceTypeBlueRayDiskPlayer:
case kSbSystemDeviceTypeGameConsole:
case kSbSystemDeviceTypeOverTheTopBox:
case kSbSystemDeviceTypeSetTopBox:
case kSbSystemDeviceTypeTV:
case kSbSystemDeviceTypeDesktopPC:
case kSbSystemDeviceTypeAndroidTV:
case kSbSystemDeviceTypeUnknown:
g_device_type_override_set = true;
g_device_type_override = device_type;
return true;
default:
g_device_type_override_set = false;
return false;
}
}
#endif // defined(COBALT_ENABLE_LIB)
namespace cobalt {
namespace browser {
namespace {
#if SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION
const char kStarboardStabilitySuffix[] = "-Experimental";
#elif defined(SB_RELEASE_CANDIDATE_API_VERSION) && \
SB_API_VERSION >= SB_RELEASE_CANDIDATE_API_VERSION && \
SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
const char kStarboardStabilitySuffix[] = "-ReleaseCandidate";
#else
const char kStarboardStabilitySuffix[] = "";
#endif
struct SanitizeReplacements {
const char* replace_chars;
const char* replace_with;
} kSanitizeReplacements[] = {
{",", u8"\uFF0C"}, // fullwidth comma
{"_", u8"\u2E0F"}, // paragraphos
{"/", u8"\u2215"}, // division slash
{"(", u8"\uFF08"}, // fullwidth left paren
{")", u8"\uFF09"}, // fullwidth right paren
};
// Replace reserved characters with Unicode homoglyphs
std::string Sanitize(const std::string& str) {
std::string clean(str);
for (size_t i = 0; i < arraysize(kSanitizeReplacements); i++) {
const SanitizeReplacements* replacement = kSanitizeReplacements + i;
ReplaceChars(clean, replacement->replace_chars, replacement->replace_with,
&clean);
}
return clean;
}
std::string CreateDeviceTypeString(SbSystemDeviceType device_type) {
switch (device_type) {
case kSbSystemDeviceTypeBlueRayDiskPlayer:
return "BDP";
case kSbSystemDeviceTypeGameConsole:
return "GAME";
case kSbSystemDeviceTypeOverTheTopBox:
return "OTT";
case kSbSystemDeviceTypeSetTopBox:
return "STB";
case kSbSystemDeviceTypeTV:
return "TV";
case kSbSystemDeviceTypeAndroidTV:
return "ATV";
case kSbSystemDeviceTypeDesktopPC:
return "DESKTOP";
case kSbSystemDeviceTypeUnknown:
return "UNKNOWN";
default:
NOTREACHED();
return "";
}
}
std::string CreateConnectionTypeString(
const base::optional<SbSystemConnectionType>& connection_type) {
if (connection_type) {
switch (*connection_type) {
case kSbSystemConnectionTypeWired:
return "Wired";
case kSbSystemConnectionTypeWireless:
return "Wireless";
default:
NOTREACHED();
}
}
return "";
}
} // namespace
UserAgentPlatformInfo GetUserAgentPlatformInfoFromSystem() {
UserAgentPlatformInfo platform_info;
platform_info.starboard_version = base::StringPrintf(
"Starboard/%d%s", SB_API_VERSION, kStarboardStabilitySuffix);
const size_t kSystemPropertyMaxLength = 1024;
char value[kSystemPropertyMaxLength];
bool result;
result = SbSystemGetProperty(kSbSystemPropertyPlatformName, value,
kSystemPropertyMaxLength);
SB_DCHECK(result);
platform_info.os_name_and_version = value;
// Fill platform info if it is a hardware TV device.
SbSystemDeviceType device_type = SbSystemGetDeviceType();
// Network operator
result = SbSystemGetProperty(kSbSystemPropertyNetworkOperatorName, value,
kSystemPropertyMaxLength);
if (result) {
platform_info.network_operator = value;
}
platform_info.javascript_engine_version =
script::GetJavaScriptEngineNameAndVersion();
platform_info.cobalt_version = COBALT_VERSION;
platform_info.cobalt_build_version_number = COBALT_BUILD_VERSION_NUMBER;
#if defined(COBALT_BUILD_TYPE_DEBUG)
platform_info.build_configuration = "debug";
#elif defined(COBALT_BUILD_TYPE_DEVEL)
platform_info.build_configuration = "devel";
#elif defined(COBALT_BUILD_TYPE_QA)
platform_info.build_configuration = "qa";
#elif defined(COBALT_BUILD_TYPE_GOLD)
platform_info.build_configuration = "gold";
#else
#error Unknown build configuration.
#endif
#if SB_API_VERSION >= 5
result = SbSystemGetProperty(kSbSystemPropertyUserAgentAuxField, value,
kSystemPropertyMaxLength);
if (result) {
platform_info.aux_field = value;
}
#endif // SB_API_VERSION >= 5
// Device Type
platform_info.device_type = device_type;
// Chipset model number
result = SbSystemGetProperty(kSbSystemPropertyChipsetModelNumber, value,
kSystemPropertyMaxLength);
if (result) {
platform_info.chipset_model_number = value;
}
// Model year
result = SbSystemGetProperty(kSbSystemPropertyModelYear, value,
kSystemPropertyMaxLength);
if (result) {
platform_info.model_year = value;
}
// Firmware version
result = SbSystemGetProperty(kSbSystemPropertyFirmwareVersion, value,
kSystemPropertyMaxLength);
if (result) {
platform_info.firmware_version = value;
}
// Brand
result = SbSystemGetProperty(kSbSystemPropertyManufacturerName, value,
kSystemPropertyMaxLength);
if (result) {
platform_info.brand = value;
}
// Model name
result = SbSystemGetProperty(kSbSystemPropertyModelName, value,
kSystemPropertyMaxLength);
if (result) {
platform_info.model = value;
}
// Connection type
SbSystemConnectionType connection_type = SbSystemGetConnectionType();
if (connection_type != kSbSystemConnectionTypeUnknown) {
platform_info.connection_type = connection_type;
}
#if defined(COBALT_ENABLE_LIB)
if (g_user_agent_platform_suffix[0] != '\0') {
platform_info.os_name_and_version += "; ";
platform_info.os_name_and_version += g_user_agent_platform_suffix;
}
// Check for overrided values, to see if a client of CobaltLib has explicitly
// requested that some values be overridden.
if (g_device_type_override_set) {
platform_info.device_type = g_device_type_override;
}
if (g_brand_name_override_set) {
platform_info.brand = g_brand_name_override;
}
if (g_model_name_override_set) {
platform_info.model = g_model_name_override;
}
#endif // defined(COBALT_ENABLE_LIB)
return platform_info;
}
// This function is expected to be deterministic and non-dependent on global
// variables and state. If global state should be referenced, it should be done
// so during the creation of |platform_info| instead.
std::string CreateUserAgentString(const UserAgentPlatformInfo& platform_info) {
// Cobalt's user agent contains the following sections:
// Mozilla/5.0 (ChromiumStylePlatform)
// Cobalt/Version.BuildNumber-BuildConfiguration (unlike Gecko)
// JavaScript Engine Name/Version
// Starboard/APIVersion,
// Device/FirmwareVersion (Brand, Model, ConnectionType)
// Mozilla/5.0 (ChromiumStylePlatform)
std::string user_agent = base::StringPrintf(
"Mozilla/5.0 (%s)", platform_info.os_name_and_version.c_str());
// Cobalt/Version.BuildNumber-BuildConfiguration (unlike Gecko)
base::StringAppendF(&user_agent, " Cobalt/%s.%s-%s (unlike Gecko)",
platform_info.cobalt_version.c_str(),
platform_info.cobalt_build_version_number.c_str(),
platform_info.build_configuration.c_str());
// JavaScript Engine Name/Version
if (!platform_info.javascript_engine_version.empty()) {
base::StringAppendF(&user_agent, " %s",
platform_info.javascript_engine_version.c_str());
}
// Starboard/APIVersion,
if (!platform_info.starboard_version.empty()) {
base::StringAppendF(&user_agent, " %s",
platform_info.starboard_version.c_str());
}
// Device/FirmwareVersion (Brand, Model, ConnectionType)
base::StringAppendF(
&user_agent, ", %s_%s_%s_%s/%s (%s, %s, %s)",
Sanitize(platform_info.network_operator.value_or("")).c_str(),
CreateDeviceTypeString(platform_info.device_type).c_str(),
Sanitize(platform_info.chipset_model_number.value_or("")).c_str(),
Sanitize(platform_info.model_year.value_or("")).c_str(),
Sanitize(platform_info.firmware_version.value_or("")).c_str(),
Sanitize(platform_info.brand.value_or("")).c_str(),
Sanitize(platform_info.model.value_or("")).c_str(),
CreateConnectionTypeString(platform_info.connection_type).c_str());
if (!platform_info.aux_field.empty()) {
user_agent.append(" ");
user_agent.append(platform_info.aux_field);
}
return user_agent;
}
} // namespace browser
} // namespace cobalt