chromium/ios/web/common/user_agent.mm

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/web/common/user_agent.h"

#import <UIKit/UIKit.h>

#import <stddef.h>
#import <stdint.h>
#import <sys/sysctl.h>
#import <string>

#import "base/strings/string_util.h"
#import "base/strings/stringprintf.h"
#import "base/strings/sys_string_conversions.h"
#import "base/system/sys_info.h"
#import "ios/web/common/features.h"

namespace {

// The Desktop OS version should always remain 10_15_7, to be consisent with
// Chrome, Firefox, and Safari: https://crbug.com/40167872
const char kDesktopUserAgentProductPlaceholder[] =
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
    "AppleWebKit/605.1.15 (KHTML, like Gecko) %s"
    "Version/11.1.1 "
    "Safari/605.1.15";

// UserAgentType description strings.
const char kUserAgentTypeAutomaticDescription[] = "AUTOMATIC";
const char kUserAgentTypeNoneDescription[] = "NONE";
const char kUserAgentTypeMobileDescription[] = "MOBILE";
const char kUserAgentTypeDesktopDescription[] = "DESKTOP";

std::string OSVersion() {
  int32_t os_major_version = 0;
  int32_t os_minor_version = 0;
  int32_t os_bugfix_version = 0;

  base::SysInfo::OperatingSystemVersionNumbers(
      &os_major_version, &os_minor_version, &os_bugfix_version);

  std::string os_version;

  if (base::FeatureList::IsEnabled(web::features::kUserAgentBugFixVersion)) {
    base::StringAppendF(&os_version, "%d_%d_%d", os_major_version,
                        os_minor_version, os_bugfix_version);
  } else {
    base::StringAppendF(&os_version, "%d_%d", os_major_version,
                        os_minor_version);
  }

  return os_version;
}

}  // namespace

namespace web {

std::string GetUserAgentTypeDescription(UserAgentType type) {
  switch (type) {
    case UserAgentType::AUTOMATIC:
      return std::string(kUserAgentTypeAutomaticDescription);
    case UserAgentType::NONE:
      return std::string(kUserAgentTypeNoneDescription);
    case UserAgentType::MOBILE:
      return std::string(kUserAgentTypeMobileDescription);
    case UserAgentType::DESKTOP:
      return std::string(kUserAgentTypeDesktopDescription);
  }
}

UserAgentType GetUserAgentTypeWithDescription(const std::string& description) {
  if (description == std::string(kUserAgentTypeMobileDescription))
    return UserAgentType::MOBILE;
  if (description == std::string(kUserAgentTypeDesktopDescription))
    return UserAgentType::DESKTOP;
  if (description == std::string(kUserAgentTypeAutomaticDescription))
    return UserAgentType::AUTOMATIC;
  return UserAgentType::NONE;
}

std::string BuildOSCpuInfo() {
  std::string os_cpu;
  // Remove the end of the platform name. For example "iPod touch" becomes
  // "iPod".
  std::string platform =
      base::SysNSStringToUTF8([[UIDevice currentDevice] model]);
  size_t position = platform.find_first_of(" ");
  if (position != std::string::npos)
    platform = platform.substr(0, position);

  base::StringAppendF(&os_cpu, "%s; CPU %s %s like Mac OS X", platform.c_str(),
                      (platform == "iPad") ? "OS" : "iPhone OS",
                      OSVersion().c_str());

  return os_cpu;
}

std::string BuildDesktopUserAgent(const std::string& desktop_product) {
  std::string product = desktop_product;
  if (!desktop_product.empty()) {
    // In case the product isn't empty, add a space after it.
    product = product + " ";
  }
  std::string user_agent;
  base::StringAppendF(&user_agent, kDesktopUserAgentProductPlaceholder,
                      product.c_str());
  return user_agent;
}

std::string BuildMobileUserAgent(const std::string& mobile_product) {
  std::string user_agent;
  base::StringAppendF(&user_agent,
                      "Mozilla/5.0 (%s) AppleWebKit/605.1.15"
                      " (KHTML, like Gecko) %s Mobile/15E148 Safari/604.1",
                      BuildOSCpuInfo().c_str(), mobile_product.c_str());

  return user_agent;
}

}  // namespace web