diff options
author | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-20 14:01:21 +0000 |
---|---|---|
committer | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-20 14:01:21 +0000 |
commit | 6ae3d4909757757f27940369e1bdc67eebd52b87 (patch) | |
tree | b95726d81214d6824646008e53d870d0ee8f2feb /chrome_frame/simple_resource_loader.cc | |
parent | b85e5120573c61b9bdd492fb903b334bf1119d8f (diff) | |
download | chromium_src-6ae3d4909757757f27940369e1bdc67eebd52b87.zip chromium_src-6ae3d4909757757f27940369e1bdc67eebd52b87.tar.gz chromium_src-6ae3d4909757757f27940369e1bdc67eebd52b87.tar.bz2 |
Various changes to make GCF nicer for the non-en-US world:
- Chrome Frame now uses MUI on Vista+ to detect IE's UX language.
- Chrome Frame now tells Chrome (via the --lang command-line option) what the current langauge is.
- Chrome Frame now respects Chrome's "ApplicationLocaleValue" group policy setting.
BUG=56435,59582
TEST=chrome_frame_unittests and chrome_frame_tests updated
Review URL: http://codereview.chromium.org/3757007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63208 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/simple_resource_loader.cc')
-rw-r--r-- | chrome_frame/simple_resource_loader.cc | 288 |
1 files changed, 214 insertions, 74 deletions
diff --git a/chrome_frame/simple_resource_loader.cc b/chrome_frame/simple_resource_loader.cc index e39087c..844afa7 100644 --- a/chrome_frame/simple_resource_loader.cc +++ b/chrome_frame/simple_resource_loader.cc @@ -4,40 +4,174 @@ #include "chrome_frame/simple_resource_loader.h" +#include <algorithm> + #include <atlbase.h> -#include <string> #include "base/base_paths.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/path_service.h" +#include "base/i18n/file_util_icu.h" #include "base/i18n/rtl.h" +#include "base/singleton.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/win/windows_version.h" +#include "chrome_frame/policy_settings.h" + +namespace { + const wchar_t kLocalesDirName[] = L"Locales"; -HINSTANCE SimpleResourceLoader::locale_dll_handle_; +bool IsInvalidTagCharacter(wchar_t tag_character) { + return !(L'-' == tag_character || + IsAsciiDigit(tag_character) || + IsAsciiAlpha(tag_character)); +} + +// A helper function object that performs a lower-case ASCII comparison between +// two strings. +class CompareInsensitiveASCII + : public std::unary_function<const std::wstring&, bool> { + public: + explicit CompareInsensitiveASCII(const std::wstring& value) + : value_lowered_(WideToASCII(value)) { + StringToLowerASCII(&value_lowered_); + } + bool operator()(const std::wstring& comparand) { + return LowerCaseEqualsASCII(comparand, value_lowered_.c_str()); + } + + private: + std::string value_lowered_; +}; + +// Returns true if the value was added. +bool PushBackIfAbsent( + const std::wstring& value, + std::vector<std::wstring>* collection) { + if (collection->end() == + std::find_if(collection->begin(), collection->end(), + CompareInsensitiveASCII(value))) { + collection->push_back(value); + return true; + } + return false; +} + +} // namespace -SimpleResourceLoader::SimpleResourceLoader() { +SimpleResourceLoader::SimpleResourceLoader() + : locale_dll_handle_(NULL) { // Find and load the resource DLL. + std::vector<std::wstring> language_tags; + + // First, try the locale dictated by policy and its fallback. + std::wstring application_locale = + Singleton<PolicySettings>()->ApplicationLocale(); + if (!application_locale.empty()) { + language_tags.push_back(application_locale); + std::wstring::size_type dash = application_locale.find(L'-'); + if (std::wstring::npos != dash) { + if (0 != dash) { + language_tags.push_back(application_locale.substr(0, dash)); + } else { + NOTREACHED() << "Group Policy application locale begins with a dash."; + } + } + } + + // Next, try the thread, process, user, system languages. + GetPreferredLanguages(&language_tags); + + // Finally, fall-back on "en-US" (which may already be present in the vector, + // but that's okay since we'll exit with success when the first is tried). + language_tags.push_back(L"en-US"); + + FilePath locales_path; + FilePath locale_dll_path; + + DetermineLocalesDirectory(&locales_path); + if (LoadLocaleDll(language_tags, locales_path, &locale_dll_handle_, + &locale_dll_path)) { + language_ = locale_dll_path.BaseName().RemoveExtension().value(); + } else { + NOTREACHED() << "Failed loading any resource dll (even \"en-US\")."; + } +} + +SimpleResourceLoader::~SimpleResourceLoader() { + locale_dll_handle_ = NULL; +} + +// static +void SimpleResourceLoader::GetPreferredLanguages( + std::vector<std::wstring>* language_tags) { + // The full set of preferred languages and their fallbacks are given priority. + GetThreadPreferredUILanguages(language_tags); + + // The above gives us nothing pre-Vista, so use ICU to get the system + // language and add it and its fallback to the end of the list if not present. std::wstring language; std::wstring region; - GetSystemLocale(&language, ®ion); - FilePath locale_dll_path; - if (GetLocaleFilePath(language, region, &locale_dll_path)) { - DCHECK(locale_dll_handle_ == NULL) << "Locale DLL is already loaded!"; - locale_dll_handle_ = LoadLocaleDll(locale_dll_path); - DCHECK(locale_dll_handle_ != NULL) << "Failed to load locale dll!"; + GetICUSystemLanguage(&language, ®ion); + if (!region.empty()) { + std::wstring combined; + combined.reserve(language.size() + 1 + region.size()); + combined.assign(language).append(L"-").append(region); + PushBackIfAbsent(combined, language_tags); } + PushBackIfAbsent(language, language_tags); } -SimpleResourceLoader::~SimpleResourceLoader() {} +// static +bool SimpleResourceLoader::GetThreadPreferredUILanguages( + std::vector<std::wstring>* language_tags) { + typedef BOOL (WINAPI* GetThreadPreferredUILanguages_Fn)( + DWORD, PULONG, PZZWSTR, PULONG); + HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); + DCHECK(kernel32) << "Failed finding kernel32.dll!"; + GetThreadPreferredUILanguages_Fn get_thread_preferred_ui_languages = + reinterpret_cast<GetThreadPreferredUILanguages_Fn>( + GetProcAddress(kernel32, "GetThreadPreferredUILanguages")); + bool have_mui = (NULL != get_thread_preferred_ui_languages); + if (have_mui) { + const DWORD kNameAndFallbackFlags = + MUI_LANGUAGE_NAME | MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK; + ULONG language_count = 0; + ULONG buffer_length = 0; + + if (get_thread_preferred_ui_languages( + kNameAndFallbackFlags, + &language_count, + NULL, + &buffer_length) && 0 != buffer_length) { + std::vector<wchar_t> language_names(buffer_length); + + if (get_thread_preferred_ui_languages( + kNameAndFallbackFlags, + &language_count, + &language_names[0], + &buffer_length)) { + std::vector<wchar_t>::const_iterator scan = language_names.begin(); + std::wstring language(&*scan); + while (!language.empty()) { + language_tags->push_back(language); + scan += language.size() + 1; + language.assign(&*scan); + } + } + } + } + return have_mui; +} -void SimpleResourceLoader::GetSystemLocale(std::wstring* language, - std::wstring* region) { +// static +void SimpleResourceLoader::GetICUSystemLanguage(std::wstring* language, + std::wstring* region) { DCHECK(language); DCHECK(region); @@ -45,89 +179,90 @@ void SimpleResourceLoader::GetSystemLocale(std::wstring* language, base::i18n::GetLanguageAndRegionFromOS(&icu_language, &icu_region); if (!icu_language.empty()) { *language = ASCIIToWide(icu_language); + } else { + language->clear(); } if (!icu_region.empty()) { *region = ASCIIToWide(icu_region); + } else { + region->clear(); } } -bool SimpleResourceLoader::GetLocaleFilePath(const std::wstring& language, - const std::wstring& region, - FilePath* file_path) { - DCHECK(file_path); +// static +void SimpleResourceLoader::DetermineLocalesDirectory(FilePath* locales_path) { + DCHECK(locales_path); FilePath module_path; PathService::Get(base::DIR_MODULE, &module_path); - FilePath locales_path = module_path.Append(kLocalesDirName); + *locales_path = module_path.Append(kLocalesDirName); // We may be residing in the "locales" directory's parent, or we might be // in a sibling directory. Move up one and look for Locales again in the // latter case. - if (!file_util::DirectoryExists(locales_path)) { - locales_path = module_path.DirName(); - locales_path = locales_path.Append(kLocalesDirName); + if (!file_util::DirectoryExists(*locales_path)) { + *locales_path = module_path.DirName(); + *locales_path = locales_path->Append(kLocalesDirName); } - bool found_dll = false; - if (file_util::DirectoryExists(locales_path)) { - std::wstring dll_name(language); - FilePath look_path; - - // First look for the [language]-[region].DLL. - if (!region.empty()) { - dll_name += L"-"; - dll_name += region; - dll_name += L".dll"; - - look_path = locales_path.Append(dll_name); - if (file_util::PathExists(look_path)) { - *file_path = look_path; - found_dll = true; - } - } - - // Next look for just [language].DLL. - if (!found_dll) { - dll_name = language; - dll_name += L".dll"; - look_path = locales_path.Append(dll_name); - if (file_util::PathExists(look_path)) { - *file_path = look_path; - found_dll = true; - } - } - - // Finally, try defaulting to en-US.dll. - if (!found_dll) { - look_path = locales_path.Append(L"en-US.dll"); - if (file_util::PathExists(look_path)) { - *file_path = look_path; - found_dll = true; - } - } - } else { - NOTREACHED() << "Could not locate locales DLL directory."; - } + // Don't make a second check to see if the dir is in the parent. We'll notice + // and log that in LoadLocaleDll when we actually try loading DLLs. +} - return found_dll; +// static +bool SimpleResourceLoader::IsValidLanguageTag( + const std::wstring& language_tag) { + // "[a-zA-Z]+(-[a-zA-Z0-9]+)*" is a simplification, but better than nothing. + // Rather than pick up the weight of a regex processor, just search for a + // character that isn't in the above set. This will at least weed out + // attempts at "../../EvilBinary". + return language_tag.end() == std::find_if(language_tag.begin(), + language_tag.end(), + &IsInvalidTagCharacter); } -HINSTANCE SimpleResourceLoader::LoadLocaleDll(const FilePath& dll_path) { - DWORD load_flags = 0; - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - load_flags = LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | - LOAD_LIBRARY_AS_IMAGE_RESOURCE; - } else { - load_flags = DONT_RESOLVE_DLL_REFERENCES; - } +// static +bool SimpleResourceLoader::LoadLocaleDll( + const std::vector<std::wstring>& language_tags, + const FilePath& locales_path, + HMODULE* dll_handle, + FilePath* file_path) { + DCHECK(file_path); // The dll should only have resources, not executable code. - HINSTANCE locale_dll_handle = LoadLibraryEx(dll_path.value().c_str(), NULL, + const DWORD load_flags = + (base::win::GetVersion() >= base::win::VERSION_VISTA ? + LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE : + DONT_RESOLVE_DLL_REFERENCES); + const std::wstring dll_suffix(L".dll"); + bool found_dll = false; + + for (std::vector<std::wstring>::const_iterator scan = language_tags.begin(), + end = language_tags.end(); + scan != end; + ++scan) { + if (!IsValidLanguageTag(*scan)) { + LOG(WARNING) << "Invalid language tag supplied while locating resources:" + " \"" << *scan << "\""; + continue; + } + FilePath look_path = locales_path.Append(*scan + dll_suffix); + HMODULE locale_dll_handle = LoadLibraryEx(look_path.value().c_str(), NULL, load_flags); - DCHECK(locale_dll_handle != NULL) << "unable to load generated resources: " - << GetLastError(); + if (NULL != locale_dll_handle) { + *dll_handle = locale_dll_handle; + *file_path = look_path; + found_dll = true; + break; + } + DPCHECK(ERROR_FILE_NOT_FOUND == GetLastError()) + << "Unable to load generated resources from " << look_path.value(); + } + + DCHECK(found_dll || file_util::DirectoryExists(locales_path)) + << "Could not locate locales DLL directory."; - return locale_dll_handle; + return found_dll; } std::wstring SimpleResourceLoader::GetLocalizedResource(int message_id) { @@ -154,11 +289,16 @@ std::wstring SimpleResourceLoader::GetLocalizedResource(int message_id) { } // static +std::wstring SimpleResourceLoader::GetLanguage() { + return SimpleResourceLoader::instance()->language_; +} + +// static std::wstring SimpleResourceLoader::Get(int message_id) { SimpleResourceLoader* loader = SimpleResourceLoader::instance(); return loader->GetLocalizedResource(message_id); } -HINSTANCE SimpleResourceLoader::GetResourceModuleHandle() { +HMODULE SimpleResourceLoader::GetResourceModuleHandle() { return locale_dll_handle_; } |