// 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" #include "build/build_config.h" #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) #include #endif #include "base/strings/string_util.h" #include "build/build_config.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #if BUILDFLAG(IS_WIN) #include #undef IN // On Windows, windef.h defines this, which screws up "India" cases. #elif BUILDFLAG(IS_APPLE) #include "base/mac/scoped_cftyperef.h" #endif #if BUILDFLAG(IS_ANDROID) #include "base/android/locale_utils.h" #endif namespace country_codes { namespace { // TODO(hcarmona/johntlee): 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 BUILDFLAG(IS_WIN) // For reference, a list of GeoIDs can be found at // http://msdn.microsoft.com/en-us/library/dd374073.aspx . 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(isobuf[0]), static_cast(isobuf[1])); } // 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 default: return kCountryIDUnknown; } } #endif // BUILDFLAG(IS_WIN) } // namespace const char kCountryIDAtInstall[] = "countryid_at_install"; 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)) { prefs->SetInteger(country_codes::kCountryIDAtInstall, GetCurrentCountryID()); } return prefs->GetInteger(country_codes::kCountryIDAtInstall); } void RegisterProfilePrefs(PrefRegistrySimple* registry) { registry->RegisterIntegerPref(country_codes::kCountryIDAtInstall, kCountryIDUnknown); } #if BUILDFLAG(IS_WIN) int GetCurrentCountryID() { return GeoIDToCountryID(GetUserGeoID(GEOCLASS_NATION)); } #elif BUILDFLAG(IS_APPLE) int GetCurrentCountryID() { base::ScopedCFTypeRef 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(isobuf[0]), static_cast(isobuf[1])); } #elif BUILDFLAG(IS_ANDROID) int GetCurrentCountryID() { return CountryStringToCountryID(base::android::GetDefaultCountryCode()); } #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_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