Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / platform / text / LocaleWin.cpp
blob3a797cf6e9c5b93a9111f4fe88672722469359c8
1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
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.
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.
31 #include "config.h"
32 #include "platform/text/LocaleWin.h"
34 #include <limits>
35 #include "platform/DateComponents.h"
36 #include "platform/Language.h"
37 #include "platform/LayoutTestSupport.h"
38 #include "platform/text/DateTimeFormat.h"
39 #include "wtf/CurrentTime.h"
40 #include "wtf/DateMath.h"
41 #include "wtf/HashMap.h"
42 #include "wtf/OwnPtr.h"
43 #include "wtf/PassOwnPtr.h"
44 #include "wtf/text/StringBuffer.h"
45 #include "wtf/text/StringBuilder.h"
46 #include "wtf/text/StringHash.h"
48 namespace blink {
50 typedef LCID (WINAPI* LocaleNameToLCIDPtr)(LPCWSTR, DWORD);
51 typedef HashMap<String, LCID> NameToLCIDMap;
53 static String extractLanguageCode(const String& locale)
55 size_t dashPosition = locale.find('-');
56 if (dashPosition == kNotFound)
57 return locale;
58 return locale.left(dashPosition);
61 static String removeLastComponent(const String& name)
63 size_t lastSeparator = name.reverseFind('-');
64 if (lastSeparator == kNotFound)
65 return emptyString();
66 return name.left(lastSeparator);
69 static void ensureNameToLCIDMap(NameToLCIDMap& map)
71 if (!map.isEmpty())
72 return;
73 // http://www.microsoft.com/resources/msdn/goglobal/default.mspx
74 // We add only locales used in layout tests for now.
75 map.add("ar", 0x0001);
76 map.add("ar-eg", 0x0C01);
77 map.add("de", 0x0007);
78 map.add("de-de", 0x0407);
79 map.add("el", 0x0008);
80 map.add("el-gr", 0x0408);
81 map.add("en", 0x0009);
82 map.add("en-gb", 0x0809);
83 map.add("en-us", 0x0409);
84 map.add("fr", 0x000C);
85 map.add("fr-fr", 0x040C);
86 map.add("he", 0x000D);
87 map.add("he-il", 0x040D);
88 map.add("hi", 0x0039);
89 map.add("hi-in", 0x0439);
90 map.add("ja", 0x0011);
91 map.add("ja-jp", 0x0411);
92 map.add("ko", 0x0012);
93 map.add("ko-kr", 0x0412);
94 map.add("ru", 0x0019);
95 map.add("ru-ru", 0x0419);
96 map.add("zh-cn", 0x0804);
97 map.add("zh-tw", 0x0404);
100 // Fallback implementation of LocaleNameToLCID API. This is used for
101 // testing on Windows XP.
102 // FIXME: Remove this, ensureNameToLCIDMap, and removeLastComponent when we drop
103 // Windows XP support.
104 static LCID WINAPI convertLocaleNameToLCID(LPCWSTR name, DWORD)
106 if (!name || !name[0])
107 return LOCALE_USER_DEFAULT;
108 DEFINE_STATIC_LOCAL(NameToLCIDMap, map, ());
109 ensureNameToLCIDMap(map);
110 String localeName = String(name).replace('_', '-');
111 localeName = localeName.lower();
112 do {
113 NameToLCIDMap::const_iterator iterator = map.find(localeName);
114 if (iterator != map.end())
115 return iterator->value;
116 localeName = removeLastComponent(localeName);
117 } while (!localeName.isEmpty());
118 return LOCALE_USER_DEFAULT;
121 static LCID LCIDFromLocaleInternal(LCID userDefaultLCID, const String& userDefaultLanguageCode, LocaleNameToLCIDPtr localeNameToLCID, const String& locale)
123 String localeLanguageCode = extractLanguageCode(locale);
124 if (equalIgnoringCase(localeLanguageCode, userDefaultLanguageCode))
125 return userDefaultLCID;
126 return localeNameToLCID(locale.charactersWithNullTermination().data(), 0);
129 static LCID LCIDFromLocale(const String& locale, bool defaultsForLocale)
131 // LocaleNameToLCID() is available since Windows Vista.
132 LocaleNameToLCIDPtr localeNameToLCID = reinterpret_cast<LocaleNameToLCIDPtr>(::GetProcAddress(::GetModuleHandle(L"kernel32"), "LocaleNameToLCID"));
133 if (!localeNameToLCID)
134 localeNameToLCID = convertLocaleNameToLCID;
136 // According to MSDN, 9 is enough for LOCALE_SISO639LANGNAME.
137 const size_t languageCodeBufferSize = 9;
138 WCHAR lowercaseLanguageCode[languageCodeBufferSize];
139 ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME | (defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), lowercaseLanguageCode, languageCodeBufferSize);
140 String userDefaultLanguageCode = String(lowercaseLanguageCode);
142 LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, locale);
143 if (!lcid)
144 lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT, userDefaultLanguageCode, localeNameToLCID, defaultLanguage());
145 return lcid;
148 PassOwnPtr<Locale> Locale::create(const String& locale)
150 // Whether the default settings for the locale should be used, ignoring user overrides.
151 bool defaultsForLocale = LayoutTestSupport::isRunningLayoutTest();
152 return LocaleWin::create(LCIDFromLocale(locale, defaultsForLocale), defaultsForLocale);
155 inline LocaleWin::LocaleWin(LCID lcid, bool defaultsForLocale)
156 : m_lcid(lcid)
157 , m_didInitializeNumberData(false)
158 , m_defaultsForLocale(defaultsForLocale)
160 DWORD value = 0;
161 getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK | (defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), value);
162 // 0:Monday, ..., 6:Sunday.
163 // We need 1 for Monday, 0 for Sunday.
164 m_firstDayOfWeek = (value + 1) % 7;
167 PassOwnPtr<LocaleWin> LocaleWin::create(LCID lcid, bool defaultsForLocale)
169 return adoptPtr(new LocaleWin(lcid, defaultsForLocale));
172 LocaleWin::~LocaleWin()
176 String LocaleWin::getLocaleInfoString(LCTYPE type)
178 int bufferSizeWithNUL = ::GetLocaleInfo(m_lcid, type | (m_defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), 0, 0);
179 if (bufferSizeWithNUL <= 0)
180 return String();
181 StringBuffer<UChar> buffer(bufferSizeWithNUL);
182 ::GetLocaleInfo(m_lcid, type | (m_defaultsForLocale ? LOCALE_NOUSEROVERRIDE : 0), buffer.characters(), bufferSizeWithNUL);
183 buffer.shrink(bufferSizeWithNUL - 1);
184 return String::adopt(buffer);
187 void LocaleWin::getLocaleInfo(LCTYPE type, DWORD& result)
189 ::GetLocaleInfo(m_lcid, type | LOCALE_RETURN_NUMBER, reinterpret_cast<LPWSTR>(&result), sizeof(DWORD) / sizeof(TCHAR));
192 void LocaleWin::ensureShortMonthLabels()
194 if (!m_shortMonthLabels.isEmpty())
195 return;
196 const LCTYPE types[12] = {
197 LOCALE_SABBREVMONTHNAME1,
198 LOCALE_SABBREVMONTHNAME2,
199 LOCALE_SABBREVMONTHNAME3,
200 LOCALE_SABBREVMONTHNAME4,
201 LOCALE_SABBREVMONTHNAME5,
202 LOCALE_SABBREVMONTHNAME6,
203 LOCALE_SABBREVMONTHNAME7,
204 LOCALE_SABBREVMONTHNAME8,
205 LOCALE_SABBREVMONTHNAME9,
206 LOCALE_SABBREVMONTHNAME10,
207 LOCALE_SABBREVMONTHNAME11,
208 LOCALE_SABBREVMONTHNAME12,
210 m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
211 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
212 m_shortMonthLabels.append(getLocaleInfoString(types[i]));
213 if (m_shortMonthLabels.last().isEmpty()) {
214 m_shortMonthLabels.shrink(0);
215 m_shortMonthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthName));
216 for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthName); ++m)
217 m_shortMonthLabels.append(WTF::monthName[m]);
218 return;
223 // -------------------------------- Tokenized date format
225 static unsigned countContinuousLetters(const String& format, unsigned index)
227 unsigned count = 1;
228 UChar reference = format[index];
229 while (index + 1 < format.length()) {
230 if (format[++index] != reference)
231 break;
232 ++count;
234 return count;
237 static void commitLiteralToken(StringBuilder& literalBuffer, StringBuilder& converted)
239 if (literalBuffer.length() <= 0)
240 return;
241 DateTimeFormat::quoteAndAppendLiteral(literalBuffer.toString(), converted);
242 literalBuffer.clear();
245 // This function converts Windows date/time pattern format [1][2] into LDML date
246 // format pattern [3].
248 // i.e.
249 // We set h, H, m, s, d, dd, M, or y as is. They have same meaning in both of
250 // Windows and LDML.
251 // We need to convert the following patterns:
252 // t -> a
253 // tt -> a
254 // ddd -> EEE
255 // dddd -> EEEE
256 // g -> G
257 // gg -> ignore
259 // [1] http://msdn.microsoft.com/en-us/library/dd317787(v=vs.85).aspx
260 // [2] http://msdn.microsoft.com/en-us/library/dd318148(v=vs.85).aspx
261 // [3] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
262 static String convertWindowsDateTimeFormat(const String& format)
264 StringBuilder converted;
265 StringBuilder literalBuffer;
266 bool inQuote = false;
267 bool lastQuoteCanBeLiteral = false;
268 for (unsigned i = 0; i < format.length(); ++i) {
269 UChar ch = format[i];
270 if (inQuote) {
271 if (ch == '\'') {
272 inQuote = false;
273 ASSERT(i);
274 if (lastQuoteCanBeLiteral && format[i - 1] == '\'') {
275 literalBuffer.append('\'');
276 lastQuoteCanBeLiteral = false;
277 } else {
278 lastQuoteCanBeLiteral = true;
280 } else {
281 literalBuffer.append(ch);
283 continue;
286 if (ch == '\'') {
287 inQuote = true;
288 if (lastQuoteCanBeLiteral && i > 0 && format[i - 1] == '\'') {
289 literalBuffer.append(ch);
290 lastQuoteCanBeLiteral = false;
291 } else {
292 lastQuoteCanBeLiteral = true;
294 } else if (isASCIIAlpha(ch)) {
295 commitLiteralToken(literalBuffer, converted);
296 unsigned symbolStart = i;
297 unsigned count = countContinuousLetters(format, i);
298 i += count - 1;
299 if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' || ch == 'y') {
300 converted.append(format, symbolStart, count);
301 } else if (ch == 'd') {
302 if (count <= 2)
303 converted.append(format, symbolStart, count);
304 else if (count == 3)
305 converted.appendLiteral("EEE");
306 else
307 converted.appendLiteral("EEEE");
308 } else if (ch == 'g') {
309 if (count == 1) {
310 converted.append('G');
311 } else {
312 // gg means imperial era in Windows.
313 // Just ignore it.
315 } else if (ch == 't') {
316 converted.append('a');
317 } else {
318 literalBuffer.append(format, symbolStart, count);
320 } else {
321 literalBuffer.append(ch);
324 commitLiteralToken(literalBuffer, converted);
325 return converted.toString();
328 void LocaleWin::ensureMonthLabels()
330 if (!m_monthLabels.isEmpty())
331 return;
332 const LCTYPE types[12] = {
333 LOCALE_SMONTHNAME1,
334 LOCALE_SMONTHNAME2,
335 LOCALE_SMONTHNAME3,
336 LOCALE_SMONTHNAME4,
337 LOCALE_SMONTHNAME5,
338 LOCALE_SMONTHNAME6,
339 LOCALE_SMONTHNAME7,
340 LOCALE_SMONTHNAME8,
341 LOCALE_SMONTHNAME9,
342 LOCALE_SMONTHNAME10,
343 LOCALE_SMONTHNAME11,
344 LOCALE_SMONTHNAME12,
346 m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
347 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
348 m_monthLabels.append(getLocaleInfoString(types[i]));
349 if (m_monthLabels.last().isEmpty()) {
350 m_monthLabels.shrink(0);
351 m_monthLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::monthFullName));
352 for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::monthFullName); ++m)
353 m_monthLabels.append(WTF::monthFullName[m]);
354 return;
359 void LocaleWin::ensureWeekDayShortLabels()
361 if (!m_weekDayShortLabels.isEmpty())
362 return;
363 const LCTYPE types[7] = {
364 LOCALE_SABBREVDAYNAME7, // Sunday
365 LOCALE_SABBREVDAYNAME1, // Monday
366 LOCALE_SABBREVDAYNAME2,
367 LOCALE_SABBREVDAYNAME3,
368 LOCALE_SABBREVDAYNAME4,
369 LOCALE_SABBREVDAYNAME5,
370 LOCALE_SABBREVDAYNAME6
372 m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(types));
373 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(types); ++i) {
374 m_weekDayShortLabels.append(getLocaleInfoString(types[i]));
375 if (m_weekDayShortLabels.last().isEmpty()) {
376 m_weekDayShortLabels.shrink(0);
377 m_weekDayShortLabels.reserveCapacity(WTF_ARRAY_LENGTH(WTF::weekdayName));
378 for (unsigned w = 0; w < WTF_ARRAY_LENGTH(WTF::weekdayName); ++w) {
379 // weekdayName starts with Monday.
380 m_weekDayShortLabels.append(WTF::weekdayName[(w + 6) % 7]);
382 return;
387 const Vector<String>& LocaleWin::monthLabels()
389 ensureMonthLabels();
390 return m_monthLabels;
393 const Vector<String>& LocaleWin::weekDayShortLabels()
395 ensureWeekDayShortLabels();
396 return m_weekDayShortLabels;
399 unsigned LocaleWin::firstDayOfWeek()
401 return m_firstDayOfWeek;
404 bool LocaleWin::isRTL()
406 WTF::Unicode::Direction dir = WTF::Unicode::direction(monthLabels()[0][0]);
407 return dir == WTF::Unicode::RightToLeft || dir == WTF::Unicode::RightToLeftArabic;
410 String LocaleWin::dateFormat()
412 if (m_dateFormat.isNull())
413 m_dateFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SSHORTDATE));
414 return m_dateFormat;
417 String LocaleWin::dateFormat(const String& windowsFormat)
419 return convertWindowsDateTimeFormat(windowsFormat);
422 String LocaleWin::monthFormat()
424 if (m_monthFormat.isNull())
425 m_monthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH));
426 return m_monthFormat;
429 String LocaleWin::shortMonthFormat()
431 if (m_shortMonthFormat.isNull())
432 m_shortMonthFormat = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_SYEARMONTH)).replace("MMMM", "MMM");
433 return m_shortMonthFormat;
436 String LocaleWin::timeFormat()
438 if (m_timeFormatWithSeconds.isNull())
439 m_timeFormatWithSeconds = convertWindowsDateTimeFormat(getLocaleInfoString(LOCALE_STIMEFORMAT));
440 return m_timeFormatWithSeconds;
443 String LocaleWin::shortTimeFormat()
445 if (!m_timeFormatWithoutSeconds.isNull())
446 return m_timeFormatWithoutSeconds;
447 String format = getLocaleInfoString(LOCALE_SSHORTTIME);
448 // Vista or older Windows doesn't support LOCALE_SSHORTTIME.
449 if (format.isEmpty()) {
450 format = getLocaleInfoString(LOCALE_STIMEFORMAT);
451 StringBuilder builder;
452 builder.append(getLocaleInfoString(LOCALE_STIME));
453 builder.appendLiteral("ss");
454 size_t pos = format.reverseFind(builder.toString());
455 if (pos != kNotFound)
456 format.remove(pos, builder.length());
458 m_timeFormatWithoutSeconds = convertWindowsDateTimeFormat(format);
459 return m_timeFormatWithoutSeconds;
462 String LocaleWin::dateTimeFormatWithSeconds()
464 if (!m_dateTimeFormatWithSeconds.isNull())
465 return m_dateTimeFormatWithSeconds;
466 StringBuilder builder;
467 builder.append(dateFormat());
468 builder.append(' ');
469 builder.append(timeFormat());
470 m_dateTimeFormatWithSeconds = builder.toString();
471 return m_dateTimeFormatWithSeconds;
474 String LocaleWin::dateTimeFormatWithoutSeconds()
476 if (!m_dateTimeFormatWithoutSeconds.isNull())
477 return m_dateTimeFormatWithoutSeconds;
478 StringBuilder builder;
479 builder.append(dateFormat());
480 builder.append(' ');
481 builder.append(shortTimeFormat());
482 m_dateTimeFormatWithoutSeconds = builder.toString();
483 return m_dateTimeFormatWithoutSeconds;
486 const Vector<String>& LocaleWin::shortMonthLabels()
488 ensureShortMonthLabels();
489 return m_shortMonthLabels;
492 const Vector<String>& LocaleWin::standAloneMonthLabels()
494 // Windows doesn't provide a way to get stand-alone month labels.
495 return monthLabels();
498 const Vector<String>& LocaleWin::shortStandAloneMonthLabels()
500 // Windows doesn't provide a way to get stand-alone month labels.
501 return shortMonthLabels();
504 const Vector<String>& LocaleWin::timeAMPMLabels()
506 if (m_timeAMPMLabels.isEmpty()) {
507 m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S1159));
508 m_timeAMPMLabels.append(getLocaleInfoString(LOCALE_S2359));
510 return m_timeAMPMLabels;
513 void LocaleWin::initializeLocaleData()
515 if (m_didInitializeNumberData)
516 return;
518 Vector<String, DecimalSymbolsSize> symbols;
519 enum DigitSubstitution {
520 DigitSubstitutionContext = 0,
521 DigitSubstitution0to9 = 1,
522 DigitSubstitutionNative = 2,
524 DWORD digitSubstitution = DigitSubstitution0to9;
525 getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution);
526 if (digitSubstitution == DigitSubstitution0to9) {
527 symbols.append("0");
528 symbols.append("1");
529 symbols.append("2");
530 symbols.append("3");
531 symbols.append("4");
532 symbols.append("5");
533 symbols.append("6");
534 symbols.append("7");
535 symbols.append("8");
536 symbols.append("9");
537 } else {
538 String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS);
539 ASSERT(digits.length() >= 10);
540 for (unsigned i = 0; i < 10; ++i)
541 symbols.append(digits.substring(i, 1));
543 ASSERT(symbols.size() == DecimalSeparatorIndex);
544 symbols.append(getLocaleInfoString(LOCALE_SDECIMAL));
545 ASSERT(symbols.size() == GroupSeparatorIndex);
546 symbols.append(getLocaleInfoString(LOCALE_STHOUSAND));
547 ASSERT(symbols.size() == DecimalSymbolsSize);
549 String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN);
550 enum NegativeFormat {
551 NegativeFormatParenthesis = 0,
552 NegativeFormatSignPrefix = 1,
553 NegativeFormatSignSpacePrefix = 2,
554 NegativeFormatSignSuffix = 3,
555 NegativeFormatSpaceSignSuffix = 4,
557 DWORD negativeFormat = NegativeFormatSignPrefix;
558 getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat);
559 String negativePrefix = emptyString();
560 String negativeSuffix = emptyString();
561 switch (negativeFormat) {
562 case NegativeFormatParenthesis:
563 negativePrefix = "(";
564 negativeSuffix = ")";
565 break;
566 case NegativeFormatSignSpacePrefix:
567 negativePrefix = negativeSign + " ";
568 break;
569 case NegativeFormatSignSuffix:
570 negativeSuffix = negativeSign;
571 break;
572 case NegativeFormatSpaceSignSuffix:
573 negativeSuffix = " " + negativeSign;
574 break;
575 case NegativeFormatSignPrefix: // Fall through.
576 default:
577 negativePrefix = negativeSign;
578 break;
580 m_didInitializeNumberData = true;
581 setLocaleData(symbols, emptyString(), emptyString(), negativePrefix, negativeSuffix);