| // 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> |
| #endif |
| |
| #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" |
| #endif |
| |
| #if defined(OS_ANDROID) |
| #include "base/android/locale_utils.h" |
| #endif |
| |
| 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 |
| // 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<char>(isobuf[0]), |
| static_cast<char>(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 // 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; |
| } |
| |
| #endif |
| |
| 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(user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterIntegerPref(country_codes::kCountryIDAtInstall, |
| kCountryIDUnknown); |
| } |
| |
| #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]), |
| static_cast<char>(isobuf[1])); |
| } |
| |
| #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 |