1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/platform/text/locale_win.h"
32 
33 #include <limits>
34 #include <memory>
35 
36 #include "base/memory/ptr_util.h"
37 #include "base/stl_util.h"
38 #include "third_party/blink/renderer/platform/language.h"
39 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
40 #include "third_party/blink/renderer/platform/text/date_components.h"
41 #include "third_party/blink/renderer/platform/text/date_time_format.h"
42 #include "third_party/blink/renderer/platform/web_test_support.h"
43 #include "third_party/blink/renderer/platform/wtf/date_math.h"
44 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
45 #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
46 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
47 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
48 #include "ui/base/ui_base_features.h"
49 
50 namespace blink {
51 
ExtractLanguageCode(const String & locale)52 static String ExtractLanguageCode(const String& locale) {
53   size_t dash_position = locale.find('-');
54   if (dash_position == kNotFound)
55     return locale;
56   return locale.Left(dash_position);
57 }
58 
LCIDFromLocaleInternal(LCID user_default_lcid,const String & user_default_language_code,const String & locale)59 static LCID LCIDFromLocaleInternal(LCID user_default_lcid,
60                                    const String& user_default_language_code,
61                                    const String& locale) {
62   String locale_language_code = ExtractLanguageCode(locale);
63   if (DeprecatedEqualIgnoringCase(locale_language_code,
64                                   user_default_language_code))
65     return user_default_lcid;
66   if (locale.length() >= LOCALE_NAME_MAX_LENGTH)
67     return 0;
68   UChar buffer[LOCALE_NAME_MAX_LENGTH];
69   if (locale.Is8Bit())
70     StringImpl::CopyChars(buffer, locale.Characters8(), locale.length());
71   else
72     StringImpl::CopyChars(buffer, locale.Characters16(), locale.length());
73   buffer[locale.length()] = '\0';
74   return ::LocaleNameToLCID(buffer, 0);
75 }
76 
LCIDFromLocale(const String & locale,bool defaults_for_locale)77 static LCID LCIDFromLocale(const String& locale, bool defaults_for_locale) {
78   // According to MSDN, 9 is enough for LOCALE_SISO639LANGNAME.
79   const size_t kLanguageCodeBufferSize = 9;
80   WCHAR lowercase_language_code[kLanguageCodeBufferSize];
81   ::GetLocaleInfo(LOCALE_USER_DEFAULT,
82                   LOCALE_SISO639LANGNAME |
83                       (defaults_for_locale ? LOCALE_NOUSEROVERRIDE : 0),
84                   lowercase_language_code, kLanguageCodeBufferSize);
85   String user_default_language_code = String(lowercase_language_code);
86 
87   LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT,
88                                      user_default_language_code, locale);
89   if (!lcid)
90     lcid = LCIDFromLocaleInternal(
91         LOCALE_USER_DEFAULT, user_default_language_code, DefaultLanguage());
92   return lcid;
93 }
94 
Create(const String & locale)95 std::unique_ptr<Locale> Locale::Create(const String& locale) {
96   // Whether the default settings for the locale should be used, ignoring user
97   // overrides.
98   bool defaults_for_locale = WebTestSupport::IsRunningWebTest();
99   return LocaleWin::Create(LCIDFromLocale(locale, defaults_for_locale),
100                            defaults_for_locale);
101 }
102 
LocaleWin(LCID lcid,bool defaults_for_locale)103 inline LocaleWin::LocaleWin(LCID lcid, bool defaults_for_locale)
104     : lcid_(lcid),
105       did_initialize_number_data_(false),
106       defaults_for_locale_(defaults_for_locale) {
107   DWORD value = 0;
108   GetLocaleInfo(LOCALE_IFIRSTDAYOFWEEK |
109                     (defaults_for_locale ? LOCALE_NOUSEROVERRIDE : 0),
110                 value);
111   // 0:Monday, ..., 6:Sunday.
112   // We need 1 for Monday, 0 for Sunday.
113   first_day_of_week_ = (value + 1) % 7;
114 }
115 
Create(LCID lcid,bool defaults_for_locale)116 std::unique_ptr<LocaleWin> LocaleWin::Create(LCID lcid,
117                                              bool defaults_for_locale) {
118   return base::WrapUnique(new LocaleWin(lcid, defaults_for_locale));
119 }
120 
~LocaleWin()121 LocaleWin::~LocaleWin() {}
122 
GetLocaleInfoString(LCTYPE type)123 String LocaleWin::GetLocaleInfoString(LCTYPE type) {
124   int buffer_size_with_nul = ::GetLocaleInfo(
125       lcid_, type | (defaults_for_locale_ ? LOCALE_NOUSEROVERRIDE : 0), 0, 0);
126   if (buffer_size_with_nul <= 0)
127     return String();
128   StringBuffer<UChar> buffer(buffer_size_with_nul);
129   ::GetLocaleInfo(lcid_,
130                   type | (defaults_for_locale_ ? LOCALE_NOUSEROVERRIDE : 0),
131                   buffer.Characters(), buffer_size_with_nul);
132   buffer.Shrink(buffer_size_with_nul - 1);
133   return String::Adopt(buffer);
134 }
135 
GetLocaleInfo(LCTYPE type,DWORD & result)136 void LocaleWin::GetLocaleInfo(LCTYPE type, DWORD& result) {
137   ::GetLocaleInfo(lcid_, type | LOCALE_RETURN_NUMBER,
138                   reinterpret_cast<LPWSTR>(&result),
139                   sizeof(DWORD) / sizeof(TCHAR));
140 }
141 
EnsureShortMonthLabels()142 void LocaleWin::EnsureShortMonthLabels() {
143   if (!short_month_labels_.IsEmpty())
144     return;
145   const LCTYPE kTypes[12] = {
146       LOCALE_SABBREVMONTHNAME1,  LOCALE_SABBREVMONTHNAME2,
147       LOCALE_SABBREVMONTHNAME3,  LOCALE_SABBREVMONTHNAME4,
148       LOCALE_SABBREVMONTHNAME5,  LOCALE_SABBREVMONTHNAME6,
149       LOCALE_SABBREVMONTHNAME7,  LOCALE_SABBREVMONTHNAME8,
150       LOCALE_SABBREVMONTHNAME9,  LOCALE_SABBREVMONTHNAME10,
151       LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
152   };
153   short_month_labels_.ReserveCapacity(base::size(kTypes));
154   for (unsigned i = 0; i < base::size(kTypes); ++i) {
155     short_month_labels_.push_back(GetLocaleInfoString(kTypes[i]));
156     if (short_month_labels_.back().IsEmpty()) {
157       short_month_labels_.Shrink(0);
158       short_month_labels_.ReserveCapacity(base::size(WTF::kMonthName));
159       for (unsigned m = 0; m < base::size(WTF::kMonthName); ++m)
160         short_month_labels_.push_back(WTF::kMonthName[m]);
161       return;
162     }
163   }
164 }
165 
166 // -------------------------------- Tokenized date format
167 
CountContinuousLetters(const String & format,unsigned index)168 static unsigned CountContinuousLetters(const String& format, unsigned index) {
169   unsigned count = 1;
170   UChar reference = format[index];
171   while (index + 1 < format.length()) {
172     if (format[++index] != reference)
173       break;
174     ++count;
175   }
176   return count;
177 }
178 
CommitLiteralToken(StringBuilder & literal_buffer,StringBuilder & converted)179 static void CommitLiteralToken(StringBuilder& literal_buffer,
180                                StringBuilder& converted) {
181   if (literal_buffer.length() <= 0)
182     return;
183   DateTimeFormat::QuoteAndappend(literal_buffer.ToString(), converted);
184   literal_buffer.Clear();
185 }
186 
187 // This function converts Windows date/time pattern format [1][2] into LDML date
188 // format pattern [3].
189 //
190 // i.e.
191 //   We set h, H, m, s, d, dd, M, or y as is. They have same meaning in both of
192 //   Windows and LDML.
193 //   We need to convert the following patterns:
194 //     t -> a
195 //     tt -> a
196 //     ddd -> EEE
197 //     dddd -> EEEE
198 //     g -> G
199 //     gg -> ignore
200 //
201 // [1] http://msdn.microsoft.com/en-us/library/dd317787(v=vs.85).aspx
202 // [2] http://msdn.microsoft.com/en-us/library/dd318148(v=vs.85).aspx
203 // [3] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
ConvertWindowsDateTimeFormat(const String & format)204 static String ConvertWindowsDateTimeFormat(const String& format) {
205   StringBuilder converted;
206   StringBuilder literal_buffer;
207   bool in_quote = false;
208   bool last_quote_can_be_literal = false;
209   for (unsigned i = 0; i < format.length(); ++i) {
210     UChar ch = format[i];
211     if (in_quote) {
212       if (ch == '\'') {
213         in_quote = false;
214         DCHECK(i);
215         if (last_quote_can_be_literal && format[i - 1] == '\'') {
216           literal_buffer.Append('\'');
217           last_quote_can_be_literal = false;
218         } else {
219           last_quote_can_be_literal = true;
220         }
221       } else {
222         literal_buffer.Append(ch);
223       }
224       continue;
225     }
226 
227     if (ch == '\'') {
228       in_quote = true;
229       if (last_quote_can_be_literal && i > 0 && format[i - 1] == '\'') {
230         literal_buffer.Append(ch);
231         last_quote_can_be_literal = false;
232       } else {
233         last_quote_can_be_literal = true;
234       }
235     } else if (IsASCIIAlpha(ch)) {
236       CommitLiteralToken(literal_buffer, converted);
237       unsigned symbol_start = i;
238       unsigned count = CountContinuousLetters(format, i);
239       i += count - 1;
240       if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' ||
241           ch == 'y') {
242         converted.Append(format, symbol_start, count);
243       } else if (ch == 'd') {
244         if (count <= 2)
245           converted.Append(format, symbol_start, count);
246         else if (count == 3)
247           converted.Append("EEE");
248         else
249           converted.Append("EEEE");
250       } else if (ch == 'g') {
251         if (count == 1) {
252           converted.Append('G');
253         } else {
254           // gg means imperial era in Windows.
255           // Just ignore it.
256         }
257       } else if (ch == 't') {
258         converted.Append('a');
259       } else {
260         literal_buffer.Append(format, symbol_start, count);
261       }
262     } else {
263       literal_buffer.Append(ch);
264     }
265   }
266   CommitLiteralToken(literal_buffer, converted);
267   return converted.ToString();
268 }
269 
EnsureMonthLabels()270 void LocaleWin::EnsureMonthLabels() {
271   if (!month_labels_.IsEmpty())
272     return;
273   const LCTYPE kTypes[12] = {
274       LOCALE_SMONTHNAME1,  LOCALE_SMONTHNAME2,  LOCALE_SMONTHNAME3,
275       LOCALE_SMONTHNAME4,  LOCALE_SMONTHNAME5,  LOCALE_SMONTHNAME6,
276       LOCALE_SMONTHNAME7,  LOCALE_SMONTHNAME8,  LOCALE_SMONTHNAME9,
277       LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
278   };
279   month_labels_.ReserveCapacity(base::size(kTypes));
280   for (unsigned i = 0; i < base::size(kTypes); ++i) {
281     month_labels_.push_back(GetLocaleInfoString(kTypes[i]));
282     if (month_labels_.back().IsEmpty()) {
283       month_labels_.Shrink(0);
284       month_labels_.ReserveCapacity(base::size(WTF::kMonthFullName));
285       for (unsigned m = 0; m < base::size(WTF::kMonthFullName); ++m)
286         month_labels_.push_back(WTF::kMonthFullName[m]);
287       return;
288     }
289   }
290 }
291 
EnsureWeekDayShortLabels()292 void LocaleWin::EnsureWeekDayShortLabels() {
293   if (!week_day_short_labels_.IsEmpty())
294     return;
295   const LCTYPE kTypes[7] = {LOCALE_SABBREVDAYNAME7,  // Sunday
296                             LOCALE_SABBREVDAYNAME1,  // Monday
297                             LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
298                             LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
299                             LOCALE_SABBREVDAYNAME6};
300   const LCTYPE kTypesRefresh[7] = {
301       LOCALE_SSHORTESTDAYNAME7,  // Sunday
302       LOCALE_SSHORTESTDAYNAME1,  // Monday
303       LOCALE_SSHORTESTDAYNAME2, LOCALE_SSHORTESTDAYNAME3,
304       LOCALE_SSHORTESTDAYNAME4, LOCALE_SSHORTESTDAYNAME5,
305       LOCALE_SSHORTESTDAYNAME6};
306   week_day_short_labels_.ReserveCapacity(base::size(kTypes));
307   for (unsigned i = 0; i < base::size(kTypes); ++i) {
308     if (features::IsFormControlsRefreshEnabled()) {
309       week_day_short_labels_.push_back(GetLocaleInfoString(kTypesRefresh[i]));
310     } else {
311       week_day_short_labels_.push_back(GetLocaleInfoString(kTypes[i]));
312     }
313     if (week_day_short_labels_.back().IsEmpty()) {
314       week_day_short_labels_.Shrink(0);
315       week_day_short_labels_.ReserveCapacity(base::size(WTF::kWeekdayName));
316       for (unsigned w = 0; w < base::size(WTF::kWeekdayName); ++w) {
317         // weekdayName starts with Monday.
318         week_day_short_labels_.push_back(WTF::kWeekdayName[(w + 6) % 7]);
319       }
320       return;
321     }
322   }
323 }
324 
MonthLabels()325 const Vector<String>& LocaleWin::MonthLabels() {
326   EnsureMonthLabels();
327   return month_labels_;
328 }
329 
WeekDayShortLabels()330 const Vector<String>& LocaleWin::WeekDayShortLabels() {
331   EnsureWeekDayShortLabels();
332   return week_day_short_labels_;
333 }
334 
FirstDayOfWeek()335 unsigned LocaleWin::FirstDayOfWeek() {
336   return first_day_of_week_;
337 }
338 
IsRTL()339 bool LocaleWin::IsRTL() {
340   WTF::unicode::CharDirection dir =
341       WTF::unicode::Direction(MonthLabels()[0][0]);
342   return dir == WTF::unicode::kRightToLeft ||
343          dir == WTF::unicode::kRightToLeftArabic;
344 }
345 
DateFormat()346 String LocaleWin::DateFormat() {
347   if (date_format_.IsNull())
348     date_format_ =
349         ConvertWindowsDateTimeFormat(GetLocaleInfoString(LOCALE_SSHORTDATE));
350   return date_format_;
351 }
352 
DateFormat(const String & windows_format)353 String LocaleWin::DateFormat(const String& windows_format) {
354   return ConvertWindowsDateTimeFormat(windows_format);
355 }
356 
MonthFormat()357 String LocaleWin::MonthFormat() {
358   if (month_format_.IsNull())
359     month_format_ =
360         ConvertWindowsDateTimeFormat(GetLocaleInfoString(LOCALE_SYEARMONTH));
361   return month_format_;
362 }
363 
ShortMonthFormat()364 String LocaleWin::ShortMonthFormat() {
365   if (short_month_format_.IsNull())
366     short_month_format_ =
367         ConvertWindowsDateTimeFormat(GetLocaleInfoString(LOCALE_SYEARMONTH))
368             .Replace("MMMM", "MMM");
369   return short_month_format_;
370 }
371 
TimeFormat()372 String LocaleWin::TimeFormat() {
373   if (time_format_with_seconds_.IsNull())
374     time_format_with_seconds_ =
375         ConvertWindowsDateTimeFormat(GetLocaleInfoString(LOCALE_STIMEFORMAT));
376   return time_format_with_seconds_;
377 }
378 
ShortTimeFormat()379 String LocaleWin::ShortTimeFormat() {
380   if (!time_format_without_seconds_.IsNull())
381     return time_format_without_seconds_;
382   String format = GetLocaleInfoString(LOCALE_SSHORTTIME);
383   // Vista or older Windows doesn't support LOCALE_SSHORTTIME.
384   if (format.IsEmpty()) {
385     format = GetLocaleInfoString(LOCALE_STIMEFORMAT);
386     StringBuilder builder;
387     builder.Append(GetLocaleInfoString(LOCALE_STIME));
388     builder.Append("ss");
389     size_t pos = format.ReverseFind(builder.ToString());
390     if (pos != kNotFound)
391       format.Remove(pos, builder.length());
392   }
393   time_format_without_seconds_ = ConvertWindowsDateTimeFormat(format);
394   return time_format_without_seconds_;
395 }
396 
DateTimeFormatWithSeconds()397 String LocaleWin::DateTimeFormatWithSeconds() {
398   if (!date_time_format_with_seconds_.IsNull())
399     return date_time_format_with_seconds_;
400   StringBuilder builder;
401   builder.Append(DateFormat());
402   builder.Append(' ');
403   builder.Append(TimeFormat());
404   date_time_format_with_seconds_ = builder.ToString();
405   return date_time_format_with_seconds_;
406 }
407 
DateTimeFormatWithoutSeconds()408 String LocaleWin::DateTimeFormatWithoutSeconds() {
409   if (!date_time_format_without_seconds_.IsNull())
410     return date_time_format_without_seconds_;
411   StringBuilder builder;
412   builder.Append(DateFormat());
413   builder.Append(' ');
414   builder.Append(ShortTimeFormat());
415   date_time_format_without_seconds_ = builder.ToString();
416   return date_time_format_without_seconds_;
417 }
418 
ShortMonthLabels()419 const Vector<String>& LocaleWin::ShortMonthLabels() {
420   EnsureShortMonthLabels();
421   return short_month_labels_;
422 }
423 
StandAloneMonthLabels()424 const Vector<String>& LocaleWin::StandAloneMonthLabels() {
425   // Windows doesn't provide a way to get stand-alone month labels.
426   return MonthLabels();
427 }
428 
ShortStandAloneMonthLabels()429 const Vector<String>& LocaleWin::ShortStandAloneMonthLabels() {
430   // Windows doesn't provide a way to get stand-alone month labels.
431   return ShortMonthLabels();
432 }
433 
TimeAMPMLabels()434 const Vector<String>& LocaleWin::TimeAMPMLabels() {
435   if (time_ampm_labels_.IsEmpty()) {
436     time_ampm_labels_.push_back(GetLocaleInfoString(LOCALE_S1159));
437     time_ampm_labels_.push_back(GetLocaleInfoString(LOCALE_S2359));
438   }
439   return time_ampm_labels_;
440 }
441 
InitializeLocaleData()442 void LocaleWin::InitializeLocaleData() {
443   if (did_initialize_number_data_)
444     return;
445 
446   Vector<String, kDecimalSymbolsSize> symbols;
447   enum DigitSubstitution {
448     kDigitSubstitutionContext = 0,
449     kDigitSubstitution0to9 = 1,
450     kDigitSubstitutionNative = 2,
451   };
452   DWORD digit_substitution = kDigitSubstitution0to9;
453   GetLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digit_substitution);
454   if (digit_substitution == kDigitSubstitution0to9) {
455     symbols.push_back("0");
456     symbols.push_back("1");
457     symbols.push_back("2");
458     symbols.push_back("3");
459     symbols.push_back("4");
460     symbols.push_back("5");
461     symbols.push_back("6");
462     symbols.push_back("7");
463     symbols.push_back("8");
464     symbols.push_back("9");
465   } else {
466     String digits = GetLocaleInfoString(LOCALE_SNATIVEDIGITS);
467     DCHECK_GE(digits.length(), 10u);
468     for (unsigned i = 0; i < 10; ++i)
469       symbols.push_back(digits.Substring(i, 1));
470   }
471   DCHECK(symbols.size() == kDecimalSeparatorIndex);
472   symbols.push_back(GetLocaleInfoString(LOCALE_SDECIMAL));
473   DCHECK(symbols.size() == kGroupSeparatorIndex);
474   symbols.push_back(GetLocaleInfoString(LOCALE_STHOUSAND));
475   DCHECK(symbols.size() == kDecimalSymbolsSize);
476 
477   String negative_sign = GetLocaleInfoString(LOCALE_SNEGATIVESIGN);
478   enum NegativeFormat {
479     kNegativeFormatParenthesis = 0,
480     kNegativeFormatSignPrefix = 1,
481     kNegativeFormatSignSpacePrefix = 2,
482     kNegativeFormatSignSuffix = 3,
483     kNegativeFormatSpaceSignSuffix = 4,
484   };
485   DWORD negative_format = kNegativeFormatSignPrefix;
486   GetLocaleInfo(LOCALE_INEGNUMBER, negative_format);
487   String negative_prefix = g_empty_string;
488   String negative_suffix = g_empty_string;
489   switch (negative_format) {
490     case kNegativeFormatParenthesis:
491       negative_prefix = "(";
492       negative_suffix = ")";
493       break;
494     case kNegativeFormatSignSpacePrefix:
495       negative_prefix = negative_sign + " ";
496       break;
497     case kNegativeFormatSignSuffix:
498       negative_suffix = negative_sign;
499       break;
500     case kNegativeFormatSpaceSignSuffix:
501       negative_suffix = " " + negative_sign;
502       break;
503     case kNegativeFormatSignPrefix:  // Fall through.
504     default:
505       negative_prefix = negative_sign;
506       break;
507   }
508   did_initialize_number_data_ = true;
509   SetLocaleData(symbols, g_empty_string, g_empty_string, negative_prefix,
510                 negative_suffix);
511 }
512 }
513