diff options
29 files changed, 359 insertions, 231 deletions
diff --git a/base/base.gyp b/base/base.gyp index 868e20e..da5d018 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -37,6 +37,8 @@ 'i18n/break_iterator.h', 'i18n/char_iterator.cc', 'i18n/char_iterator.h', + 'i18n/case_conversion.cc', + 'i18n/case_conversion.h', 'i18n/file_util_icu.cc', 'i18n/file_util_icu.h', 'i18n/icu_encoding_detection.cc', @@ -126,6 +128,7 @@ 'id_map_unittest.cc', 'i18n/break_iterator_unittest.cc', 'i18n/char_iterator_unittest.cc', + 'i18n/case_conversion_unittest.cc', 'i18n/file_util_icu_unittest.cc', 'i18n/icu_string_conversions_unittest.cc', 'i18n/rtl_unittest.cc', diff --git a/base/i18n/case_conversion.cc b/base/i18n/case_conversion.cc new file mode 100644 index 0000000..2dedade --- /dev/null +++ b/base/i18n/case_conversion.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2011 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 "base/i18n/case_conversion.h" + +#include "base/utf_string_conversions.h" +#include "unicode/unistr.h" + +namespace base { +namespace i18n { + +string16 ToLower(const string16& string) { + icu::UnicodeString unicode_string(string.c_str(), string.size()); + unicode_string.toLower(); + return string16(unicode_string.getBuffer(), unicode_string.length()); +} + +std::wstring WideToLower(const std::wstring& string) { + return UTF16ToWide(ToLower(WideToUTF16(string))); +} + +string16 ToUpper(const string16& string) { + icu::UnicodeString unicode_string(string.c_str(), string.size()); + unicode_string.toUpper(); + return string16(unicode_string.getBuffer(), unicode_string.length()); +} + +std::wstring WideToUpper(const std::wstring& string) { + return UTF16ToWide(ToUpper(WideToUTF16(string))); +} + +} // namespace i18n +} // namespace base diff --git a/base/i18n/case_conversion.h b/base/i18n/case_conversion.h new file mode 100644 index 0000000..d834ede --- /dev/null +++ b/base/i18n/case_conversion.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 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. + +#ifndef BASE_I18N_CASE_CONVERSION_ +#define BASE_I18N_CASE_CONVERSION_ +#pragma once + +#include <string> + +#include "base/string16.h" + +namespace base { +namespace i18n { + +// Returns the lower case equivalent of string. Uses ICU's default locale. +string16 ToLower(const string16& string); +std::wstring WideToLower(const std::wstring& string); + +// Returns the upper case equivalent of string. Uses ICU's default locale. +string16 ToUpper(const string16& string); +std::wstring WideToUpper(const std::wstring& string); + +} // namespace i18n +} // namespace base + +#endif // BASE_I18N_CASE_CONVERSION_ diff --git a/base/i18n/case_conversion_unittest.cc b/base/i18n/case_conversion_unittest.cc new file mode 100644 index 0000000..87a349e --- /dev/null +++ b/base/i18n/case_conversion_unittest.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2011 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 "base/i18n/case_conversion.h" +#include "base/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Test upper and lower case string conversion. +TEST(CaseConversionTest, UpperLower) { + string16 mixed(ASCIIToUTF16("Text with UPPer & lowER casE.")); + const string16 expected_lower(ASCIIToUTF16("text with upper & lower case.")); + const string16 expected_upper(ASCIIToUTF16("TEXT WITH UPPER & LOWER CASE.")); + + string16 result = base::i18n::ToLower(mixed); + EXPECT_EQ(expected_lower, result); + + result = base::i18n::ToUpper(mixed); + EXPECT_EQ(expected_upper, result); +} + +// Test upper and lower case string conversion. +TEST(CaseConversionTest, WideUpperLower) { + std::wstring mixed(L"Text with UPPer & lowER casE."); + const std::wstring expected_lower(L"text with upper & lower case."); + const std::wstring expected_upper(L"TEXT WITH UPPER & LOWER CASE."); + + std::wstring result = base::i18n::WideToLower(mixed); + EXPECT_EQ(expected_lower, result); + + result = base::i18n::WideToUpper(mixed); + EXPECT_EQ(expected_upper, result); +} + +// TODO(jshin): More tests are needed, especially with non-ASCII characters. + +} // namespace diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc index fd40f47..235341c 100644 --- a/chrome/browser/autocomplete/search_provider.cc +++ b/chrome/browser/autocomplete/search_provider.cc @@ -8,6 +8,7 @@ #include <cmath> #include "base/callback.h" +#include "base/i18n/case_conversion.h" #include "base/i18n/icu_string_conversions.h" #include "base/message_loop.h" #include "base/string16.h" @@ -836,7 +837,7 @@ void SearchProvider::AddMatchToMap(const string16& query_string, // NOTE: Keep this ToLower() call in sync with url_database.cc. const std::pair<MatchMap::iterator, bool> i = map->insert( std::pair<string16, AutocompleteMatch>( - l10n_util::ToLower(query_string), match)); + base::i18n::ToLower(query_string), match)); // NOTE: We purposefully do a direct relevance comparison here instead of // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added // first" rather than "items alphabetically first" when the scores are equal. diff --git a/chrome/browser/bookmarks/bookmark_index.cc b/chrome/browser/bookmarks/bookmark_index.cc index c247cb5..875140f 100644 --- a/chrome/browser/bookmarks/bookmark_index.cc +++ b/chrome/browser/bookmarks/bookmark_index.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -8,6 +8,7 @@ #include <iterator> #include <list> +#include "base/i18n/case_conversion.h" #include "base/string16.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_utils.h" @@ -242,8 +243,9 @@ std::vector<string16> BookmarkIndex::ExtractQueryWords(const string16& query) { if (query.empty()) return std::vector<string16>(); QueryParser parser; - // TODO: use ICU normalization. - parser.ExtractQueryWords(l10n_util::ToLower(query), &terms); + // TODO(brettw): use ICU normalization: + // http://userguide.icu-project.org/transforms/normalization + parser.ExtractQueryWords(base::i18n::ToLower(query), &terms); return terms; } diff --git a/chrome/browser/bookmarks/bookmark_utils.cc b/chrome/browser/bookmarks/bookmark_utils.cc index 6090ea1..d5846a6 100644 --- a/chrome/browser/bookmarks/bookmark_utils.cc +++ b/chrome/browser/bookmarks/bookmark_utils.cc @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/file_path.h" +#include "base/i18n/case_conversion.h" #include "base/string16.h" #include "base/string_number_conversions.h" #include "base/time.h" @@ -194,10 +195,10 @@ bool DoesBookmarkContainWords(const BookmarkNode* node, const std::string& languages) { return DoesBookmarkTextContainWords( - l10n_util::ToLower(node->GetTitle()), words) || + base::i18n::ToLower(node->GetTitle()), words) || DoesBookmarkTextContainWords( - l10n_util::ToLower(UTF8ToUTF16(node->GetURL().spec())), words) || - DoesBookmarkTextContainWords(l10n_util::ToLower( + base::i18n::ToLower(UTF8ToUTF16(node->GetURL().spec())), words) || + DoesBookmarkTextContainWords(base::i18n::ToLower( net::FormatUrl(node->GetURL(), languages, net::kFormatUrlOmitNothing, UnescapeRule::NORMAL, NULL, NULL, NULL)), words); } @@ -519,7 +520,7 @@ void GetBookmarksContainingText(BookmarkModel* model, std::vector<const BookmarkNode*>* nodes) { std::vector<string16> words; QueryParser parser; - parser.ExtractQueryWords(l10n_util::ToLower(text), &words); + parser.ExtractQueryWords(base::i18n::ToLower(text), &words); if (words.empty()) return; @@ -539,7 +540,7 @@ bool DoesBookmarkContainText(const BookmarkNode* node, const std::string& languages) { std::vector<string16> words; QueryParser parser; - parser.ExtractQueryWords(l10n_util::ToLower(text), &words); + parser.ExtractQueryWords(base::i18n::ToLower(text), &words); if (words.empty()) return false; diff --git a/chrome/browser/download/download_item.cc b/chrome/browser/download/download_item.cc index 54165a9..ffbd4b2a 100644 --- a/chrome/browser/download/download_item.cc +++ b/chrome/browser/download/download_item.cc @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/file_util.h" #include "base/format_macros.h" +#include "base/i18n/case_conversion.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/stringprintf.h" @@ -538,9 +539,9 @@ bool DownloadItem::MatchesQuery(const string16& query) const { if (query.empty()) return true; - DCHECK_EQ(query, l10n_util::ToLower(query)); + DCHECK_EQ(query, base::i18n::ToLower(query)); - string16 url_raw(l10n_util::ToLower(UTF8ToUTF16(url().spec()))); + string16 url_raw(base::i18n::ToLower(UTF8ToUTF16(url().spec()))); if (url_raw.find(query) != string16::npos) return true; @@ -551,11 +552,11 @@ bool DownloadItem::MatchesQuery(const string16& query) const { // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD" PrefService* prefs = download_manager_->profile()->GetPrefs(); std::string languages(prefs->GetString(prefs::kAcceptLanguages)); - string16 url_formatted(l10n_util::ToLower(net::FormatUrl(url(), languages))); + string16 url_formatted(base::i18n::ToLower(net::FormatUrl(url(), languages))); if (url_formatted.find(query) != string16::npos) return true; - string16 path(l10n_util::ToLower(full_path().LossyDisplayName())); + string16 path(base::i18n::ToLower(full_path().LossyDisplayName())); // This shouldn't just do a substring match; it is wrong for Unicode // due to normalization and we have a fancier search-query system // used elsewhere. diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc index f7e05f7..6ef4a2b 100644 --- a/chrome/browser/download/download_manager.cc +++ b/chrome/browser/download/download_manager.cc @@ -6,6 +6,7 @@ #include "base/callback.h" #include "base/file_util.h" +#include "base/i18n/case_conversion.h" #include "base/logging.h" #include "base/path_service.h" #include "base/rand_util.h" @@ -191,7 +192,7 @@ void DownloadManager::SearchDownloads(const string16& query, std::vector<DownloadItem*>* result) { DCHECK(result); - string16 query_lower(l10n_util::ToLower(query)); + string16 query_lower(base::i18n::ToLower(query)); for (DownloadMap::iterator it = history_downloads_.begin(); it != history_downloads_.end(); ++it) { diff --git a/chrome/browser/enumerate_modules_model_win.cc b/chrome/browser/enumerate_modules_model_win.cc index 69315e9..78ea9d1 100644 --- a/chrome/browser/enumerate_modules_model_win.cc +++ b/chrome/browser/enumerate_modules_model_win.cc @@ -11,6 +11,7 @@ #include "base/environment.h" #include "base/file_path.h" #include "base/file_version_info_win.h" +#include "base/i18n/case_conversion.h" #include "base/metrics/histogram.h" #include "base/string_number_conversions.h" #include "base/string_util.h" @@ -274,7 +275,7 @@ void ModuleEnumerator::NormalizeModule(Module* module) { if (!ConvertToLongPath(path, &module->location)) module->location = path; - module->location = l10n_util::ToLower(module->location); + module->location = base::i18n::ToLower(module->location); // Location contains the filename, so the last slash is where the path // ends. @@ -586,8 +587,8 @@ void ModuleEnumerator::PreparePathMappings() { std::string path; if (environment->GetVar(WideToASCII(*variable).c_str(), &path)) { path_mapping_.push_back( - std::make_pair(l10n_util::ToLower(UTF8ToWide(path)) + L"\\", - L"%" + l10n_util::ToLower(*variable) + L"%")); + std::make_pair(base::i18n::WideToLower(UTF8ToWide(path)) + L"\\", + L"%" + base::i18n::ToLower(*variable) + L"%")); } } } diff --git a/chrome/browser/history/in_memory_url_index.cc b/chrome/browser/history/in_memory_url_index.cc index 2fc67ec..f2c2bf3 100644 --- a/chrome/browser/history/in_memory_url_index.cc +++ b/chrome/browser/history/in_memory_url_index.cc @@ -12,6 +12,7 @@ #include "base/file_util.h" #include "base/i18n/break_iterator.h" +#include "base/i18n/case_conversion.h" #include "base/metrics/histogram.h" #include "base/string_util.h" #include "base/time.h" @@ -180,7 +181,7 @@ bool InMemoryURLIndex::IndexRow(const URLRow& row) { history_info_map_[history_id] = new_row; // Split URL into individual, unique words then add in the title words. - url = l10n_util::ToLower(url); + url = base::i18n::ToLower(url); String16Set url_words = WordSetFromString16(url); String16Set title_words = WordSetFromString16(row.title()); String16Set words; @@ -353,7 +354,7 @@ ScoredHistoryMatches InMemoryURLIndex::HistoryItemsForTerms( String16Vector lower_terms; for (String16Vector::const_iterator term_iter = terms.begin(); term_iter != terms.end(); ++term_iter) - lower_terms.push_back(l10n_util::ToLower(*term_iter)); + lower_terms.push_back(base::i18n::ToLower(*term_iter)); String16Vector::value_type all_terms(JoinString(lower_terms, ' ')); HistoryIDSet history_id_set = HistoryIDSetFromWords(all_terms); @@ -477,7 +478,7 @@ InMemoryURLIndex::String16Set InMemoryURLIndex::WordSetFromString16( String16Set word_set; for (String16Vector::const_iterator iter = words.begin(); iter != words.end(); ++iter) - word_set.insert(l10n_util::ToLower(*iter)); + word_set.insert(base::i18n::ToLower(*iter)); return word_set; } @@ -710,8 +711,8 @@ ScoredHistoryMatch InMemoryURLIndex::ScoredMatchForURL( // Figure out where each search term appears in the URL and/or page title // so that we can score as well as provide autocomplete highlighting. - string16 url = l10n_util::ToLower(UTF8ToUTF16(gurl.spec())); - string16 title = l10n_util::ToLower(row.title()); + string16 url = base::i18n::ToLower(UTF8ToUTF16(gurl.spec())); + string16 title = base::i18n::ToLower(row.title()); int term_num = 0; for (String16Vector::const_iterator iter = terms.begin(); iter != terms.end(); ++iter, ++term_num) { diff --git a/chrome/browser/history/query_parser.cc b/chrome/browser/history/query_parser.cc index d2957f3..628879b 100644 --- a/chrome/browser/history/query_parser.cc +++ b/chrome/browser/history/query_parser.cc @@ -9,6 +9,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/i18n/break_iterator.h" +#include "base/i18n/case_conversion.h" #include "base/logging.h" #include "base/stl_util-inl.h" #include "base/string_util.h" @@ -313,7 +314,7 @@ int QueryParser::ParseQuery(const string16& query, string16* sqlite_query) { void QueryParser::ParseQuery(const string16& query, std::vector<QueryNode*>* nodes) { QueryNodeList root; - if (ParseQueryImpl(l10n_util::ToLower(query), &root)) + if (ParseQueryImpl(base::i18n::ToLower(query), &root)) nodes->swap(*root.children()); } @@ -333,7 +334,7 @@ bool QueryParser::DoesQueryMatch(const string16& text, return false; std::vector<QueryWord> query_words; - string16 lower_text = l10n_util::ToLower(text); + string16 lower_text = base::i18n::ToLower(text); ExtractQueryWords(lower_text, &query_words); if (query_words.empty()) diff --git a/chrome/browser/history/url_database.cc b/chrome/browser/history/url_database.cc index 78ca0c5..cc9bf9e 100644 --- a/chrome/browser/history/url_database.cc +++ b/chrome/browser/history/url_database.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -10,6 +10,7 @@ #include <vector> #include "app/sql/statement.h" +#include "base/i18n/case_conversion.h" #include "base/utf_string_conversions.h" #include "chrome/common/url_constants.h" #include "googleurl/src/gurl.h" @@ -426,7 +427,7 @@ bool URLDatabase::SetKeywordSearchTermsForURL(URLID url_id, statement.BindInt64(0, keyword_id); statement.BindInt64(1, url_id); - statement.BindString16(2, l10n_util::ToLower(term)); + statement.BindString16(2, base::i18n::ToLower(term)); statement.BindString16(3, term); return statement.Run(); } @@ -485,7 +486,7 @@ void URLDatabase::GetMostRecentKeywordSearchTerms( return; // NOTE: Keep this ToLower() call in sync with search_provider.cc. - string16 lower_prefix = l10n_util::ToLower(prefix); + string16 lower_prefix = base::i18n::ToLower(prefix); // This magic gives us a prefix search. string16 next_prefix = lower_prefix; next_prefix[next_prefix.size() - 1] = diff --git a/chrome/browser/instant/instant_loader.cc b/chrome/browser/instant/instant_loader.cc index a1920a0..ca99bea 100644 --- a/chrome/browser/instant/instant_loader.cc +++ b/chrome/browser/instant/instant_loader.cc @@ -10,6 +10,7 @@ #include <vector> #include "base/command_line.h" +#include "base/i18n/case_conversion.h" #include "base/string_number_conversions.h" #include "base/timer.h" #include "base/utf_string_conversions.h" @@ -685,9 +686,9 @@ bool InstantLoader::Update(TabContentsWrapper* tab_contents, host->Send(new ViewMsg_SearchBoxChange( host->routing_id(), user_text_, verbatim, text_length, text_length)); - string16 complete_suggested_text_lower = l10n_util::ToLower( + string16 complete_suggested_text_lower = base::i18n::ToLower( complete_suggested_text_); - string16 user_text_lower = l10n_util::ToLower(user_text_); + string16 user_text_lower = base::i18n::ToLower(user_text_); if (!verbatim && complete_suggested_text_lower.size() > user_text_lower.size() && !complete_suggested_text_lower.compare(0, user_text_lower.size(), @@ -841,8 +842,8 @@ void InstantLoader::SetCompleteSuggestedText( return; } - string16 user_text_lower = l10n_util::ToLower(user_text_); - string16 complete_suggested_text_lower = l10n_util::ToLower( + string16 user_text_lower = base::i18n::ToLower(user_text_); + string16 complete_suggested_text_lower = base::i18n::ToLower( complete_suggested_text); last_suggestion_.clear(); if (user_text_lower.compare(0, user_text_lower.size(), diff --git a/chrome/browser/search_engines/template_url.cc b/chrome/browser/search_engines/template_url.cc index 5952fdb3..1d276aa6 100644 --- a/chrome/browser/search_engines/template_url.cc +++ b/chrome/browser/search_engines/template_url.cc @@ -4,6 +4,7 @@ #include "chrome/browser/search_engines/template_url.h" +#include "base/i18n/case_conversion.h" #include "base/i18n/icu_string_conversions.h" #include "base/i18n/rtl.h" #include "base/logging.h" @@ -628,7 +629,7 @@ void TemplateURL::SetInstantURL(const std::string& url, void TemplateURL::set_keyword(const string16& keyword) { // Case sensitive keyword matching is confusing. As such, we force all // keywords to be lower case. - keyword_ = l10n_util::ToLower(keyword); + keyword_ = base::i18n::ToLower(keyword); autogenerate_keyword_ = false; } diff --git a/chrome/browser/search_engines/template_url_model.cc b/chrome/browser/search_engines/template_url_model.cc index 5e3d234..a2def18 100644 --- a/chrome/browser/search_engines/template_url_model.cc +++ b/chrome/browser/search_engines/template_url_model.cc @@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/environment.h" +#include "base/i18n/case_conversion.h" #include "base/stl_util-inl.h" #include "base/string_number_conversions.h" #include "base/string_split.h" @@ -153,7 +154,7 @@ string16 TemplateURLModel::GenerateKeyword(const GURL& url, // static string16 TemplateURLModel::CleanUserInputKeyword(const string16& keyword) { // Remove the scheme. - string16 result(l10n_util::ToLower(keyword)); + string16 result(base::i18n::ToLower(keyword)); url_parse::Component scheme_component; if (url_parse::ExtractScheme(UTF16ToUTF8(keyword).c_str(), static_cast<int>(keyword.length()), diff --git a/chrome/browser/ui/gtk/edit_search_engine_dialog.cc b/chrome/browser/ui/gtk/edit_search_engine_dialog.cc index a3e4940..24863f6 100644 --- a/chrome/browser/ui/gtk/edit_search_engine_dialog.cc +++ b/chrome/browser/ui/gtk/edit_search_engine_dialog.cc @@ -6,6 +6,7 @@ #include <gtk/gtk.h> +#include "base/i18n/case_conversion.h" #include "base/i18n/rtl.h" #include "base/message_loop.h" #include "base/utf_string_conversions.h" @@ -33,7 +34,7 @@ std::string GetDisplayURL(const TemplateURL& turl) { void LowercaseInsertTextHandler(GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data) { string16 original_text = UTF8ToUTF16(text); - string16 lower_text = l10n_util::ToLower(original_text); + string16 lower_text = base::i18n::ToLower(original_text); if (lower_text != original_text) { std::string result = UTF16ToUTF8(lower_text); // Prevent ourselves getting called recursively about our own edit. diff --git a/chrome/browser/ui/views/shell_dialogs_win.cc b/chrome/browser/ui/views/shell_dialogs_win.cc index 5eba0de4..691f050 100644 --- a/chrome/browser/ui/views/shell_dialogs_win.cc +++ b/chrome/browser/ui/views/shell_dialogs_win.cc @@ -13,6 +13,7 @@ #include "base/file_path.h" #include "base/file_util.h" +#include "base/i18n/case_conversion.h" #include "base/message_loop.h" #include "base/string_split.h" #include "base/threading/thread.h" @@ -143,7 +144,7 @@ std::wstring FormatFilterForExtensions( // the we create a description "QQQ File (.qqq)"). include_all_files = true; desc = l10n_util::GetStringFUTF16(IDS_APP_SAVEAS_EXTENSION_FORMAT, - l10n_util::ToUpper(ext_name), + base::i18n::WideToUpper(ext_name), ext_name); } if (desc.empty()) diff --git a/chrome/browser/webdata/autofill_table.cc b/chrome/browser/webdata/autofill_table.cc index 75a9503..8e5242a 100644 --- a/chrome/browser/webdata/autofill_table.cc +++ b/chrome/browser/webdata/autofill_table.cc @@ -12,6 +12,7 @@ #include <vector> #include "app/sql/statement.h" +#include "base/i18n/case_conversion.h" #include "base/logging.h" #include "base/string_number_conversions.h" #include "base/time.h" @@ -429,7 +430,7 @@ bool AutofillTable::GetFormValuesForElementName(const string16& name, s.BindString16(0, name); s.BindInt(1, limit); } else { - string16 prefix_lower = l10n_util::ToLower(prefix); + string16 prefix_lower = base::i18n::ToLower(prefix); string16 next_prefix = prefix_lower; next_prefix[next_prefix.length() - 1]++; @@ -625,7 +626,7 @@ bool AutofillTable::InsertFormElement(const FormField& element, s.BindString16(0, element.name); s.BindString16(1, element.value); - s.BindString16(2, l10n_util::ToLower(element.value)); + s.BindString16(2, base::i18n::ToLower(element.value)); if (!s.Run()) { NOTREACHED(); @@ -824,7 +825,7 @@ bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) { s.BindString16(0, entry.key().name()); s.BindString16(1, entry.key().value()); - s.BindString16(2, l10n_util::ToLower(entry.key().value())); + s.BindString16(2, base::i18n::ToLower(entry.key().value())); s.BindInt(3, entry.timestamps().size()); if (!s.Run()) { diff --git a/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc b/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc index 251cc75..73db09b 100644 --- a/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc +++ b/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc @@ -8,6 +8,7 @@ #include <map> #include "base/compiler_specific.h" +#include "base/i18n/case_conversion.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/metrics/histogram.h" @@ -199,7 +200,7 @@ void PhishingTermFeatureExtractor::ExtractFeaturesWithTimeout() { } void PhishingTermFeatureExtractor::HandleWord(const string16& word) { - std::string word_lower = UTF16ToUTF8(l10n_util::ToLower(word)); + std::string word_lower = UTF16ToUTF8(base::i18n::ToLower(word)); std::string word_hash = crypto::SHA256HashString(word_lower); // Quick out if the word is not part of any term, which is the common case. diff --git a/net/ftp/ftp_directory_listing_parser_unittest.cc b/net/ftp/ftp_directory_listing_parser_unittest.cc index 6664a89..a0a58d2 100644 --- a/net/ftp/ftp_directory_listing_parser_unittest.cc +++ b/net/ftp/ftp_directory_listing_parser_unittest.cc @@ -19,51 +19,11 @@ namespace net { namespace { -TEST(FtpDirectoryListingBufferTest, Parse) { - const char* test_files[] = { - "dir-listing-ls-1", - "dir-listing-ls-1-utf8", - "dir-listing-ls-2", - "dir-listing-ls-3", - "dir-listing-ls-4", - "dir-listing-ls-5", - "dir-listing-ls-6", - "dir-listing-ls-7", - "dir-listing-ls-8", - "dir-listing-ls-9", - "dir-listing-ls-10", - "dir-listing-ls-11", - "dir-listing-ls-12", - "dir-listing-ls-13", - "dir-listing-ls-14", - "dir-listing-ls-15", - "dir-listing-ls-16", - "dir-listing-ls-17", - "dir-listing-ls-18", - "dir-listing-ls-19", - "dir-listing-ls-20", // TODO(phajdan.jr): should use windows-1251 encoding. - "dir-listing-ls-21", // TODO(phajdan.jr): should use windows-1251 encoding. - "dir-listing-ls-22", // TODO(phajdan.jr): should use windows-1251 encoding. - "dir-listing-ls-23", - "dir-listing-ls-24", - - // Tests for Russian listings. The only difference between those - // files is character encoding: - "dir-listing-ls-25", // UTF-8 - "dir-listing-ls-26", // KOI8-R - "dir-listing-ls-27", // windows-1251 - - "dir-listing-netware-1", - "dir-listing-netware-2", - "dir-listing-vms-1", - "dir-listing-vms-2", - "dir-listing-vms-3", - "dir-listing-vms-4", - "dir-listing-vms-5", - "dir-listing-windows-1", - "dir-listing-windows-2", - }; +class FtpDirectoryListingParserTest + : public testing::TestWithParam<const char*> { +}; +TEST_P(FtpDirectoryListingParserTest, Parse) { FilePath test_dir; PathService::Get(base::DIR_SOURCE_ROOT, &test_dir); test_dir = test_dir.AppendASCII("net"); @@ -79,71 +39,116 @@ TEST(FtpDirectoryListingBufferTest, Parse) { base::Time mock_current_time( base::Time::FromLocalExploded(mock_current_time_exploded)); - for (size_t i = 0; i < arraysize(test_files); i++) { - SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i, test_files[i])); - - std::string test_listing; - EXPECT_TRUE(file_util::ReadFileToString(test_dir.AppendASCII(test_files[i]), - &test_listing)); - - std::vector<FtpDirectoryListingEntry> entries; - EXPECT_EQ(OK, ParseFtpDirectoryListing(test_listing, - mock_current_time, - &entries)); - - std::string expected_listing; - ASSERT_TRUE(file_util::ReadFileToString( - test_dir.AppendASCII(std::string(test_files[i]) + ".expected"), - &expected_listing)); - - std::vector<std::string> lines; - StringTokenizer tokenizer(expected_listing, "\r\n"); - while (tokenizer.GetNext()) - lines.push_back(tokenizer.token()); - - ASSERT_EQ(8 * entries.size(), lines.size()); - - for (size_t i = 0; i < lines.size() / 8; i++) { - std::string type(lines[8 * i]); - std::string name(lines[8 * i + 1]); - int64 size; - base::StringToInt64(lines[8 * i + 2], &size); - - SCOPED_TRACE(base::StringPrintf("Filename: %s", name.c_str())); - - int year, month, day_of_month, hour, minute; - base::StringToInt(lines[8 * i + 3], &year); - base::StringToInt(lines[8 * i + 4], &month); - base::StringToInt(lines[8 * i + 5], &day_of_month); - base::StringToInt(lines[8 * i + 6], &hour); - base::StringToInt(lines[8 * i + 7], &minute); - - const FtpDirectoryListingEntry& entry = entries[i]; - - if (type == "d") { - EXPECT_EQ(FtpDirectoryListingEntry::DIRECTORY, entry.type); - } else if (type == "-") { - EXPECT_EQ(FtpDirectoryListingEntry::FILE, entry.type); - } else if (type == "l") { - EXPECT_EQ(FtpDirectoryListingEntry::SYMLINK, entry.type); - } else { - ADD_FAILURE() << "invalid gold test data: " << type; - } - - EXPECT_EQ(UTF8ToUTF16(name), entry.name); - EXPECT_EQ(size, entry.size); - - base::Time::Exploded time_exploded; - entry.last_modified.LocalExplode(&time_exploded); - EXPECT_EQ(year, time_exploded.year); - EXPECT_EQ(month, time_exploded.month); - EXPECT_EQ(day_of_month, time_exploded.day_of_month); - EXPECT_EQ(hour, time_exploded.hour); - EXPECT_EQ(minute, time_exploded.minute); + SCOPED_TRACE(base::StringPrintf("Test case: %s", GetParam())); + + std::string test_listing; + EXPECT_TRUE(file_util::ReadFileToString(test_dir.AppendASCII(GetParam()), + &test_listing)); + + std::vector<FtpDirectoryListingEntry> entries; + EXPECT_EQ(OK, ParseFtpDirectoryListing(test_listing, + mock_current_time, + &entries)); + + std::string expected_listing; + ASSERT_TRUE(file_util::ReadFileToString( + test_dir.AppendASCII(std::string(GetParam()) + ".expected"), + &expected_listing)); + + std::vector<std::string> lines; + StringTokenizer tokenizer(expected_listing, "\r\n"); + while (tokenizer.GetNext()) + lines.push_back(tokenizer.token()); + + ASSERT_EQ(8 * entries.size(), lines.size()); + + for (size_t i = 0; i < lines.size() / 8; i++) { + std::string type(lines[8 * i]); + std::string name(lines[8 * i + 1]); + int64 size; + base::StringToInt64(lines[8 * i + 2], &size); + + SCOPED_TRACE(base::StringPrintf("Filename: %s", name.c_str())); + + int year, month, day_of_month, hour, minute; + base::StringToInt(lines[8 * i + 3], &year); + base::StringToInt(lines[8 * i + 4], &month); + base::StringToInt(lines[8 * i + 5], &day_of_month); + base::StringToInt(lines[8 * i + 6], &hour); + base::StringToInt(lines[8 * i + 7], &minute); + + const FtpDirectoryListingEntry& entry = entries[i]; + + if (type == "d") { + EXPECT_EQ(FtpDirectoryListingEntry::DIRECTORY, entry.type); + } else if (type == "-") { + EXPECT_EQ(FtpDirectoryListingEntry::FILE, entry.type); + } else if (type == "l") { + EXPECT_EQ(FtpDirectoryListingEntry::SYMLINK, entry.type); + } else { + ADD_FAILURE() << "invalid gold test data: " << type; } + + EXPECT_EQ(UTF8ToUTF16(name), entry.name); + EXPECT_EQ(size, entry.size); + + base::Time::Exploded time_exploded; + entry.last_modified.LocalExplode(&time_exploded); + EXPECT_EQ(year, time_exploded.year); + EXPECT_EQ(month, time_exploded.month); + EXPECT_EQ(day_of_month, time_exploded.day_of_month); + EXPECT_EQ(hour, time_exploded.hour); + EXPECT_EQ(minute, time_exploded.minute); } } +const char* kTestFiles[] = { + "dir-listing-ls-1", + "dir-listing-ls-1-utf8", + "dir-listing-ls-2", + "dir-listing-ls-3", + "dir-listing-ls-4", + "dir-listing-ls-5", + "dir-listing-ls-6", + "dir-listing-ls-7", + "dir-listing-ls-8", + "dir-listing-ls-9", + "dir-listing-ls-10", + "dir-listing-ls-11", + "dir-listing-ls-12", + "dir-listing-ls-13", + "dir-listing-ls-14", + "dir-listing-ls-15", + "dir-listing-ls-16", + "dir-listing-ls-17", + "dir-listing-ls-18", + "dir-listing-ls-19", + "dir-listing-ls-20", // TODO(phajdan.jr): should use windows-1251 encoding. + "dir-listing-ls-21", // TODO(phajdan.jr): should use windows-1251 encoding. + "dir-listing-ls-22", // TODO(phajdan.jr): should use windows-1251 encoding. + "dir-listing-ls-23", + "dir-listing-ls-24", + + // Tests for Russian listings. The only difference between those + // files is character encoding: + "dir-listing-ls-25", // UTF-8 + "dir-listing-ls-26", // KOI8-R + "dir-listing-ls-27", // windows-1251 + + "dir-listing-netware-1", + "dir-listing-netware-2", + "dir-listing-vms-1", + "dir-listing-vms-2", + "dir-listing-vms-3", + "dir-listing-vms-4", + "dir-listing-vms-5", + "dir-listing-windows-1", + "dir-listing-windows-2", +}; + +INSTANTIATE_TEST_CASE_P(, FtpDirectoryListingParserTest, + testing::ValuesIn(kTestFiles)); + } // namespace } // namespace net diff --git a/net/ftp/ftp_util.cc b/net/ftp/ftp_util.cc index 441c666..0f384bd 100644 --- a/net/ftp/ftp_util.cc +++ b/net/ftp/ftp_util.cc @@ -4,10 +4,13 @@ #include "net/ftp/ftp_util.h" +#include <map> #include <vector> +#include "base/i18n/case_conversion.h" #include "base/i18n/char_iterator.h" #include "base/logging.h" +#include "base/memory/singleton.h" #include "base/string_number_conversions.h" #include "base/string_tokenizer.h" #include "base/string_util.h" @@ -111,47 +114,82 @@ std::string FtpUtil::VMSPathToUnix(const std::string& vms_path) { return result; } -// static -bool FtpUtil::AbbreviatedMonthToNumber(const string16& text, int* number) { - icu::UnicodeString unicode_text(text.data(), text.size()); - - int32_t locales_count; - const icu::Locale* locales = - icu::DateFormat::getAvailableLocales(locales_count); - - // Some FTP servers localize the date listings. To guess the locale, - // we loop over all available ones. - for (int32_t locale = 0; locale < locales_count; locale++) { - UErrorCode status(U_ZERO_ERROR); - - icu::DateFormatSymbols format_symbols(locales[locale], status); - - // If we cannot get format symbols for some locale, it's not a fatal error. - // Just try another one. - if (U_FAILURE(status)) - continue; - - int32_t months_count; - const icu::UnicodeString* months = - format_symbols.getShortMonths(months_count); - - // Loop over all abbreviated month names in given locale. - // An alternative solution (to parse |text| in given locale) is more - // lenient, and may accept more than we want even with setLenient(false). - for (int32_t month = 0; month < months_count; month++) { - // Compare (case-insensitive), but just first three characters. Sometimes - // ICU returns longer strings (for example for Russian locale), and in FTP - // listings they are abbreviated to just three characters. - // Note: ICU may also return strings shorter than three characters, - // and those also should be accepted. - if (months[month].caseCompare(0, 3, unicode_text, 0) == 0) { - *number = month + 1; - return true; +namespace { + +// Lazy-initialized map of abbreviated month names. +class AbbreviatedMonthsMap { + public: + static AbbreviatedMonthsMap* GetInstance() { + return Singleton<AbbreviatedMonthsMap>::get(); + } + + // Converts abbreviated month name |text| to its number (in range 1-12). + // On success returns true and puts the number in |number|. + bool GetMonthNumber(const string16& text, int* number) { + // Ignore the case of the month names. The simplest way to handle that + // is to make everything lowercase. + string16 text_lower(base::i18n::ToLower(text)); + + if (map_.find(text_lower) == map_.end()) + return false; + + *number = map_[text_lower]; + return true; + } + + private: + friend struct DefaultSingletonTraits<AbbreviatedMonthsMap>; + + // Constructor, initializes the map based on ICU data. It is much faster + // to do that just once. + AbbreviatedMonthsMap() { + int32_t locales_count; + const icu::Locale* locales = + icu::DateFormat::getAvailableLocales(locales_count); + + for (int32_t locale = 0; locale < locales_count; locale++) { + UErrorCode status(U_ZERO_ERROR); + + icu::DateFormatSymbols format_symbols(locales[locale], status); + + // If we cannot get format symbols for some locale, it's not a fatal + // error. Just try another one. + if (U_FAILURE(status)) + continue; + + int32_t months_count; + const icu::UnicodeString* months = + format_symbols.getShortMonths(months_count); + + for (int32_t month = 0; month < months_count; month++) { + string16 month_name(months[month].getBuffer(), + static_cast<size_t>(months[month].length())); + + // Ignore the case of the month names. The simplest way to handle that + // is to make everything lowercase. + month_name = base::i18n::ToLower(month_name); + + map_[month_name] = month + 1; + + // Sometimes ICU returns longer strings, but in FTP listings a shorter + // abbreviation is used (for example for the Russian locale). Make sure + // we always have a map entry for a three-letter abbreviation. + map_[month_name.substr(0, 3)] = month + 1; } } } - return false; + // Maps lowercase month names to numbers in range 1-12. + std::map<string16, int> map_; + + DISALLOW_COPY_AND_ASSIGN(AbbreviatedMonthsMap); +}; + +} // namespace + +// static +bool FtpUtil::AbbreviatedMonthToNumber(const string16& text, int* number) { + return AbbreviatedMonthsMap::GetInstance()->GetMonthNumber(text, number); } // static diff --git a/tools/valgrind/gtest_exclude/net_unittests.gtest-tsan.txt b/tools/valgrind/gtest_exclude/net_unittests.gtest-tsan.txt index dd0be86..506186c 100644 --- a/tools/valgrind/gtest_exclude/net_unittests.gtest-tsan.txt +++ b/tools/valgrind/gtest_exclude/net_unittests.gtest-tsan.txt @@ -22,6 +22,3 @@ DirectoryListerTest.BigDirRecursiveTest # Crashes silently, see http://crbug.com/76911 URLRequestTest.FileTest - -# This test does not finish in our time-out period. http://crbug.com/79022 -FtpDirectoryListingBufferTest.Parse diff --git a/ui/base/l10n/l10n_util.cc b/ui/base/l10n/l10n_util.cc index 139e8f1..e39cfbe 100644 --- a/ui/base/l10n/l10n_util.cc +++ b/ui/base/l10n/l10n_util.cc @@ -779,26 +779,6 @@ string16 TruncateString(const string16& string, size_t length) { return string.substr(0, index) + kElideString; } -string16 ToLower(const string16& string) { - icu::UnicodeString lower_u_str( - icu::UnicodeString(FALSE, string.c_str(), string.size()).toLower( - icu::Locale::getDefault())); - string16 result; - lower_u_str.extract(0, lower_u_str.length(), - WriteInto(&result, lower_u_str.length() + 1)); - return result; -} - -string16 ToUpper(const string16& string) { - icu::UnicodeString upper_u_str( - icu::UnicodeString(FALSE, string.c_str(), string.size()).toUpper( - icu::Locale::getDefault())); - string16 result; - upper_u_str.extract(0, upper_u_str.length(), - WriteInto(&result, upper_u_str.length() + 1)); - return result; -} - // Compares the character data stored in two different string16 strings by // specified Collator instance. UCollationResult CompareString16WithCollator(const icu::Collator* collator, diff --git a/ui/base/l10n/l10n_util.h b/ui/base/l10n/l10n_util.h index d0418c0..1478ae2 100644 --- a/ui/base/l10n/l10n_util.h +++ b/ui/base/l10n/l10n_util.h @@ -131,12 +131,6 @@ string16 GetStringFUTF16Int(int message_id, int64 a); // less. string16 TruncateString(const string16& string, size_t length); -// Returns the lower case equivalent of string. -string16 ToLower(const string16& string); - -// Returns the upper case equivalent of string. -string16 ToUpper(const string16& string); - // In place sorting of string16 strings using collation rules for |locale|. void SortStrings16(const std::string& locale, std::vector<string16>* strings); diff --git a/ui/base/l10n/l10n_util_unittest.cc b/ui/base/l10n/l10n_util_unittest.cc index 90aaeff..8b24181 100644 --- a/ui/base/l10n/l10n_util_unittest.cc +++ b/ui/base/l10n/l10n_util_unittest.cc @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "base/environment.h" #include "base/file_util.h" +#include "base/i18n/case_conversion.h" #include "base/path_service.h" #include "base/stl_util-inl.h" #include "base/string_util.h" @@ -303,19 +304,6 @@ TEST_F(L10nUtilTest, SortStringsUsingFunction) { STLDeleteElements(&strings); } -// Test upper and lower case string conversion. -TEST_F(L10nUtilTest, UpperLower) { - string16 mixed(ASCIIToUTF16("Text with UPPer & lowER casE.")); - const string16 expected_lower(ASCIIToUTF16("text with upper & lower case.")); - const string16 expected_upper(ASCIIToUTF16("TEXT WITH UPPER & LOWER CASE.")); - - string16 result = l10n_util::ToLower(mixed); - EXPECT_EQ(expected_lower, result); - - result = l10n_util::ToUpper(mixed); - EXPECT_EQ(expected_upper, result); -} - TEST_F(L10nUtilTest, LocaleDisplayName) { // TODO(jungshik): Make this test more extensive. // Test zh-CN and zh-TW are treated as zh-Hans and zh-Hant. @@ -336,12 +324,12 @@ TEST_F(L10nUtilTest, LocaleDisplayName) { char16 buf_with_null[length_with_null] = { 0, 'a', 0, 'b' }; string16 string16_with_null(buf_with_null, length_with_null); - string16 upper_with_null = l10n_util::ToUpper(string16_with_null); + string16 upper_with_null = base::i18n::ToUpper(string16_with_null); ASSERT_EQ(length_with_null, upper_with_null.size()); EXPECT_TRUE(upper_with_null[0] == 0 && upper_with_null[1] == 'A' && upper_with_null[2] == 0 && upper_with_null[3] == 'B'); - string16 lower_with_null = l10n_util::ToLower(upper_with_null); + string16 lower_with_null = base::i18n::ToLower(upper_with_null); ASSERT_EQ(length_with_null, upper_with_null.size()); EXPECT_TRUE(lower_with_null[0] == 0 && lower_with_null[1] == 'a' && lower_with_null[2] == 0 && lower_with_null[3] == 'b'); diff --git a/views/controls/menu/menu_controller.cc b/views/controls/menu/menu_controller.cc index 5703d69..5a0ed2d 100644 --- a/views/controls/menu/menu_controller.cc +++ b/views/controls/menu/menu_controller.cc @@ -4,6 +4,7 @@ #include "views/controls/menu/menu_controller.h" +#include "base/i18n/case_conversion.h" #include "base/i18n/rtl.h" #include "base/time.h" #include "base/utf_string_conversions.h" @@ -61,8 +62,7 @@ bool TitleMatchesMnemonic(MenuItemView* menu, wchar_t key) { if (menu->GetMnemonic()) return false; - std::wstring lower_title = UTF16ToWide( - l10n_util::ToLower(WideToUTF16(menu->GetTitle()))); + std::wstring lower_title = base::i18n::WideToLower(menu->GetTitle()); return !lower_title.empty() && lower_title[0] == key; } @@ -1661,7 +1661,7 @@ bool MenuController::AcceptOrSelect(MenuItemView* parent, bool MenuController::SelectByChar(wchar_t character) { wchar_t char_array[1] = { character }; - wchar_t key = UTF16ToWide(l10n_util::ToLower(WideToUTF16(char_array)))[0]; + wchar_t key = base::i18n::WideToLower(char_array)[0]; MenuItemView* item = pending_state_.item; if (!item->HasSubmenu() || !item->GetSubmenu()->IsShowing()) item = item->GetParentMenuItem(); diff --git a/views/controls/menu/menu_item_view.cc b/views/controls/menu/menu_item_view.cc index 4af6da7..8c2858a8 100644 --- a/views/controls/menu/menu_item_view.cc +++ b/views/controls/menu/menu_item_view.cc @@ -4,6 +4,7 @@ #include "views/controls/menu/menu_item_view.h" +#include "base/i18n/case_conversion.h" #include "base/utf_string_conversions.h" #include "grit/app_strings.h" #include "ui/base/accessibility/accessible_view_state.h" @@ -393,7 +394,11 @@ wchar_t MenuItemView::GetMnemonic() { if (index != std::wstring::npos) { if (index + 1 != title.size() && title[index + 1] != '&') { wchar_t char_array[1] = { title[index + 1] }; - return UTF16ToWide(l10n_util::ToLower(WideToUTF16(char_array)))[0]; + // TODO(jshin): What about Turkish locale? See http://crbug.com/81719. + // If the mnemonic is capital I and the UI language is Turkish, + // lowercasing it results in 'small dotless i', which is different + // from a 'dotted i'. Similar issues may exist for az and lt locales. + return base::i18n::WideToLower(char_array)[0]; } index++; } diff --git a/views/controls/textfield/native_textfield_win.cc b/views/controls/textfield/native_textfield_win.cc index 9c4d196..5811cf9 100644 --- a/views/controls/textfield/native_textfield_win.cc +++ b/views/controls/textfield/native_textfield_win.cc @@ -6,6 +6,7 @@ #include <algorithm> +#include "base/i18n/case_conversion.h" #include "base/i18n/rtl.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" @@ -193,7 +194,7 @@ void NativeTextfieldWin::UpdateText() { // sure both RTL and LTR strings are displayed properly. base::i18n::AdjustStringForLocaleDirection(&text); if (textfield_->style() & Textfield::STYLE_LOWERCASE) - text = l10n_util::ToLower(text); + text = base::i18n::WideToLower(text); SetWindowText(text.c_str()); UpdateAccessibleValue(text); } @@ -906,7 +907,7 @@ void NativeTextfieldWin::OnPaste() { if (!clipboard_str.empty()) { std::wstring collapsed(CollapseWhitespace(clipboard_str, false)); if (textfield_->style() & Textfield::STYLE_LOWERCASE) - collapsed = l10n_util::ToLower(collapsed); + collapsed = base::i18n::WideToLower(collapsed); // Force a Paste operation to trigger ContentsChanged, even if identical // contents are pasted into the text box. See http://crbug.com/79002 ReplaceSel(L"", false); |