blob: 189e5c8562b0f96614839a72cc86e1fac1fe56c2 [file] [log] [blame]
// Copyright 2018 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 "components/country_codes/country_codes.h"
#if defined(OS_POSIX) && !defined(OS_MACOSX)
#include <locale.h>
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#if defined(OS_WIN)
#include <windows.h>
#undef IN // On Windows, windef.h defines this, which screws up "India" cases.
#elif defined(OS_MACOSX)
#include "base/mac/scoped_cftyperef.h"
#if defined(OS_ANDROID)
#include "base/android/locale_utils.h"
namespace country_codes {
namespace {
// TODO(scottchen): remove this function after confirming if it only pertains
// to obsolete OSes.
int CountryCharsToCountryIDWithUpdate(char c1, char c2) {
// SPECIAL CASE: In 2003, Yugoslavia renamed itself to Serbia and Montenegro.
// Serbia and Montenegro dissolved their union in June 2006. Yugoslavia was
// ISO 'YU' and Serbia and Montenegro were ISO 'CS'. Serbia was subsequently
// issued 'RS' and Montenegro 'ME'. Windows XP and Mac OS X Leopard still use
// the value 'YU'. If we get a value of 'YU' or 'CS' we will map it to 'RS'.
if ((c1 == 'Y' && c2 == 'U') || (c1 == 'C' && c2 == 'S')) {
c1 = 'R';
c2 = 'S';
// SPECIAL CASE: Timor-Leste changed from 'TP' to 'TL' in 2002. Windows XP
// predates this; we therefore map this value.
if (c1 == 'T' && c2 == 'P')
c2 = 'L';
return CountryCharsToCountryID(c1, c2);
#if defined(OS_WIN)
// For reference, a list of GeoIDs can be found at
// .
int GeoIDToCountryID(GEOID geo_id) {
const int kISOBufferSize = 3; // Two plus one for the terminator.
wchar_t isobuf[kISOBufferSize] = {};
int retval = GetGeoInfo(geo_id, GEO_ISO2, isobuf, kISOBufferSize, 0);
if (retval == kISOBufferSize && !(isobuf[0] == L'X' && isobuf[1] == L'X')) {
return CountryCharsToCountryIDWithUpdate(static_cast<char>(isobuf[0]),
// Various locations have ISO codes that Windows does not return.
switch (geo_id) {
case 0x144: // Guernsey
return CountryCharsToCountryID('G', 'G');
case 0x148: // Jersey
return CountryCharsToCountryID('J', 'E');
case 0x3B16: // Isle of Man
return CountryCharsToCountryID('I', 'M');
// 'UM' (U.S. Minor Outlying Islands)
case 0x7F: // Johnston Atoll
case 0x102: // Wake Island
case 0x131: // Baker Island
case 0x146: // Howland Island
case 0x147: // Jarvis Island
case 0x149: // Kingman Reef
case 0x152: // Palmyra Atoll
case 0x52FA: // Midway Islands
return CountryCharsToCountryID('U', 'M');
// 'SH' (Saint Helena)
case 0x12F: // Ascension Island
case 0x15C: // Tristan da Cunha
return CountryCharsToCountryID('S', 'H');
// 'IO' (British Indian Ocean Territory)
case 0x13A: // Diego Garcia
return CountryCharsToCountryID('I', 'O');
// Other cases where there is no ISO country code; we assign countries that
// can serve as reasonable defaults.
case 0x154: // Rota Island
case 0x155: // Saipan
case 0x15A: // Tinian Island
return CountryCharsToCountryID('U', 'S');
case 0x134: // Channel Islands
return CountryCharsToCountryID('G', 'B');
case 0x143: // Guantanamo Bay
return kCountryIDUnknown;
#endif // defined(OS_WIN)
} // namespace
const char kCountryIDAtInstall[] = "countryid_at_install";
#if !defined(OS_WIN) && !defined(OS_MACOSX)
int CountryStringToCountryID(const std::string& country) {
return (country.length() == 2)
? CountryCharsToCountryIDWithUpdate(country[0], country[1])
: kCountryIDUnknown;
int GetCountryIDFromPrefs(PrefService* prefs) {
if (!prefs)
return GetCurrentCountryID();
// Cache first run Country ID value in prefs, and use it afterwards. This
// ensures that just because the user moves around, we won't automatically
// make major changes to their available search providers, which would feel
// surprising.
if (!prefs->HasPrefPath(country_codes::kCountryIDAtInstall)) {
return prefs->GetInteger(country_codes::kCountryIDAtInstall);
void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
#if defined(OS_WIN)
int GetCurrentCountryID() {
return GeoIDToCountryID(GetUserGeoID(GEOCLASS_NATION));
#elif defined(OS_MACOSX)
int GetCurrentCountryID() {
base::ScopedCFTypeRef<CFLocaleRef> locale(CFLocaleCopyCurrent());
CFStringRef country =
(CFStringRef)CFLocaleGetValue(locale.get(), kCFLocaleCountryCode);
if (!country)
return kCountryIDUnknown;
UniChar isobuf[2];
CFRange char_range = CFRangeMake(0, 2);
CFStringGetCharacters(country, char_range, isobuf);
return CountryCharsToCountryIDWithUpdate(static_cast<char>(isobuf[0]),
#elif defined(OS_ANDROID)
int GetCurrentCountryID() {
return CountryStringToCountryID(base::android::GetDefaultCountryCode());
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
int GetCurrentCountryID() {
const char* locale = setlocale(LC_MESSAGES, nullptr);
if (!locale)
return kCountryIDUnknown;
// The format of a locale name is:
// language[_territory][.codeset][@modifier], where territory is an ISO 3166
// country code, which is what we want.
// First remove the language portion.
std::string locale_str(locale);
size_t territory_delim = locale_str.find('_');
if (territory_delim == std::string::npos)
return kCountryIDUnknown;
locale_str.erase(0, territory_delim + 1);
// Next remove any codeset/modifier portion and uppercase.
return CountryStringToCountryID(
base::ToUpperASCII(locale_str.substr(0, locale_str.find_first_of(".@"))));
#endif // OS_*
} // namespace country_codes