country_codes.cc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/country_codes/country_codes.h"
  5. #include "build/build_config.h"
  6. #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
  7. #include <locale.h>
  8. #endif
  9. #include "base/strings/string_util.h"
  10. #include "build/build_config.h"
  11. #include "components/prefs/pref_registry_simple.h"
  12. #include "components/prefs/pref_service.h"
  13. #if BUILDFLAG(IS_WIN)
  14. #include <windows.h>
  15. #undef IN // On Windows, windef.h defines this, which screws up "India" cases.
  16. #elif BUILDFLAG(IS_APPLE)
  17. #include "base/mac/scoped_cftyperef.h"
  18. #endif
  19. #if BUILDFLAG(IS_ANDROID)
  20. #include "base/android/locale_utils.h"
  21. #endif
  22. namespace country_codes {
  23. namespace {
  24. // TODO(hcarmona/johntlee): remove this function after confirming if it only
  25. // pertains to obsolete OSes.
  26. int CountryCharsToCountryIDWithUpdate(char c1, char c2) {
  27. // SPECIAL CASE: In 2003, Yugoslavia renamed itself to Serbia and Montenegro.
  28. // Serbia and Montenegro dissolved their union in June 2006. Yugoslavia was
  29. // ISO 'YU' and Serbia and Montenegro were ISO 'CS'. Serbia was subsequently
  30. // issued 'RS' and Montenegro 'ME'. Windows XP and Mac OS X Leopard still use
  31. // the value 'YU'. If we get a value of 'YU' or 'CS' we will map it to 'RS'.
  32. if ((c1 == 'Y' && c2 == 'U') || (c1 == 'C' && c2 == 'S')) {
  33. c1 = 'R';
  34. c2 = 'S';
  35. }
  36. // SPECIAL CASE: Timor-Leste changed from 'TP' to 'TL' in 2002. Windows XP
  37. // predates this; we therefore map this value.
  38. if (c1 == 'T' && c2 == 'P')
  39. c2 = 'L';
  40. return CountryCharsToCountryID(c1, c2);
  41. }
  42. #if BUILDFLAG(IS_WIN)
  43. // For reference, a list of GeoIDs can be found at
  44. // http://msdn.microsoft.com/en-us/library/dd374073.aspx .
  45. int GeoIDToCountryID(GEOID geo_id) {
  46. const int kISOBufferSize = 3; // Two plus one for the terminator.
  47. wchar_t isobuf[kISOBufferSize] = {};
  48. int retval = GetGeoInfo(geo_id, GEO_ISO2, isobuf, kISOBufferSize, 0);
  49. if (retval == kISOBufferSize && !(isobuf[0] == L'X' && isobuf[1] == L'X')) {
  50. return CountryCharsToCountryIDWithUpdate(static_cast<char>(isobuf[0]),
  51. static_cast<char>(isobuf[1]));
  52. }
  53. // Various locations have ISO codes that Windows does not return.
  54. switch (geo_id) {
  55. case 0x144: // Guernsey
  56. return CountryCharsToCountryID('G', 'G');
  57. case 0x148: // Jersey
  58. return CountryCharsToCountryID('J', 'E');
  59. case 0x3B16: // Isle of Man
  60. return CountryCharsToCountryID('I', 'M');
  61. // 'UM' (U.S. Minor Outlying Islands)
  62. case 0x7F: // Johnston Atoll
  63. case 0x102: // Wake Island
  64. case 0x131: // Baker Island
  65. case 0x146: // Howland Island
  66. case 0x147: // Jarvis Island
  67. case 0x149: // Kingman Reef
  68. case 0x152: // Palmyra Atoll
  69. case 0x52FA: // Midway Islands
  70. return CountryCharsToCountryID('U', 'M');
  71. // 'SH' (Saint Helena)
  72. case 0x12F: // Ascension Island
  73. case 0x15C: // Tristan da Cunha
  74. return CountryCharsToCountryID('S', 'H');
  75. // 'IO' (British Indian Ocean Territory)
  76. case 0x13A: // Diego Garcia
  77. return CountryCharsToCountryID('I', 'O');
  78. // Other cases where there is no ISO country code; we assign countries that
  79. // can serve as reasonable defaults.
  80. case 0x154: // Rota Island
  81. case 0x155: // Saipan
  82. case 0x15A: // Tinian Island
  83. return CountryCharsToCountryID('U', 'S');
  84. case 0x134: // Channel Islands
  85. return CountryCharsToCountryID('G', 'B');
  86. case 0x143: // Guantanamo Bay
  87. default:
  88. return kCountryIDUnknown;
  89. }
  90. }
  91. #endif // BUILDFLAG(IS_WIN)
  92. } // namespace
  93. const char kCountryIDAtInstall[] = "countryid_at_install";
  94. int CountryStringToCountryID(const std::string& country) {
  95. return (country.length() == 2)
  96. ? CountryCharsToCountryIDWithUpdate(country[0], country[1])
  97. : kCountryIDUnknown;
  98. }
  99. int GetCountryIDFromPrefs(PrefService* prefs) {
  100. if (!prefs)
  101. return GetCurrentCountryID();
  102. // Cache first run Country ID value in prefs, and use it afterwards. This
  103. // ensures that just because the user moves around, we won't automatically
  104. // make major changes to their available search providers, which would feel
  105. // surprising.
  106. if (!prefs->HasPrefPath(country_codes::kCountryIDAtInstall)) {
  107. prefs->SetInteger(country_codes::kCountryIDAtInstall,
  108. GetCurrentCountryID());
  109. }
  110. return prefs->GetInteger(country_codes::kCountryIDAtInstall);
  111. }
  112. void RegisterProfilePrefs(PrefRegistrySimple* registry) {
  113. registry->RegisterIntegerPref(country_codes::kCountryIDAtInstall,
  114. kCountryIDUnknown);
  115. }
  116. #if BUILDFLAG(IS_WIN)
  117. int GetCurrentCountryID() {
  118. return GeoIDToCountryID(GetUserGeoID(GEOCLASS_NATION));
  119. }
  120. #elif BUILDFLAG(IS_APPLE)
  121. int GetCurrentCountryID() {
  122. base::ScopedCFTypeRef<CFLocaleRef> locale(CFLocaleCopyCurrent());
  123. CFStringRef country =
  124. (CFStringRef)CFLocaleGetValue(locale.get(), kCFLocaleCountryCode);
  125. if (!country)
  126. return kCountryIDUnknown;
  127. UniChar isobuf[2];
  128. CFRange char_range = CFRangeMake(0, 2);
  129. CFStringGetCharacters(country, char_range, isobuf);
  130. return CountryCharsToCountryIDWithUpdate(static_cast<char>(isobuf[0]),
  131. static_cast<char>(isobuf[1]));
  132. }
  133. #elif BUILDFLAG(IS_ANDROID)
  134. int GetCurrentCountryID() {
  135. return CountryStringToCountryID(base::android::GetDefaultCountryCode());
  136. }
  137. #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
  138. int GetCurrentCountryID() {
  139. const char* locale = setlocale(LC_MESSAGES, nullptr);
  140. if (!locale)
  141. return kCountryIDUnknown;
  142. // The format of a locale name is:
  143. // language[_territory][.codeset][@modifier], where territory is an ISO 3166
  144. // country code, which is what we want.
  145. // First remove the language portion.
  146. std::string locale_str(locale);
  147. size_t territory_delim = locale_str.find('_');
  148. if (territory_delim == std::string::npos)
  149. return kCountryIDUnknown;
  150. locale_str.erase(0, territory_delim + 1);
  151. // Next remove any codeset/modifier portion and uppercase.
  152. return CountryStringToCountryID(
  153. base::ToUpperASCII(locale_str.substr(0, locale_str.find_first_of(".@"))));
  154. }
  155. #endif // OS_*
  156. } // namespace country_codes