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 | |
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
-rw-r--r-- | chrome_frame/chrome_frame_activex_base.h | 17 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.cc | 9 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.h | 12 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_npapi_unittest.cc | 19 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_plugin.h | 5 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher.cc | 1 | ||||
-rw-r--r-- | chrome_frame/policy_settings.cc | 93 | ||||
-rw-r--r-- | chrome_frame/policy_settings.h | 19 | ||||
-rw-r--r-- | chrome_frame/simple_resource_loader.cc | 288 | ||||
-rw-r--r-- | chrome_frame/simple_resource_loader.h | 53 | ||||
-rw-r--r-- | chrome_frame/test/automation_client_mock.cc | 16 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_automation_mock.h | 14 | ||||
-rw-r--r-- | chrome_frame/test/policy_settings_unittest.cc | 35 | ||||
-rw-r--r-- | chrome_frame/test/proxy_factory_mock.cc | 10 | ||||
-rw-r--r-- | chrome_frame/test/simple_resource_loader_test.cc | 76 |
15 files changed, 521 insertions, 146 deletions
diff --git a/chrome_frame/chrome_frame_activex_base.h b/chrome_frame/chrome_frame_activex_base.h index 264621f..5f063c8 100644 --- a/chrome_frame/chrome_frame_activex_base.h +++ b/chrome_frame/chrome_frame_activex_base.h @@ -177,7 +177,8 @@ class ATL_NO_VTABLE ChromeFrameActivexBase : // NOLINT : ready_state_(READYSTATE_UNINITIALIZED), url_fetcher_(new UrlmonUrlRequestManager()), failed_to_fetch_in_place_frame_(false), - draw_sad_tab_(false) { + draw_sad_tab_(false), + prev_resource_instance_(NULL) { m_bWindowOnly = TRUE; url_fetcher_->set_container(static_cast<IDispatch*>(this)); } @@ -244,10 +245,16 @@ END_MSG_MAP() DECLARE_PROTECT_FINAL_CONSTRUCT() virtual void SetResourceModule() { + DCHECK(NULL == prev_resource_instance_); SimpleResourceLoader* loader_instance = SimpleResourceLoader::instance(); DCHECK(loader_instance); - HINSTANCE res_dll = loader_instance->GetResourceModuleHandle(); - _AtlBaseModule.SetResourceInstance(res_dll); + HMODULE res_dll = loader_instance->GetResourceModuleHandle(); + prev_resource_instance_ = _AtlBaseModule.SetResourceInstance(res_dll); + } + + virtual void ClearResourceModule() { + _AtlBaseModule.SetResourceInstance(prev_resource_instance_); + prev_resource_instance_ = NULL; } HRESULT FinalConstruct() { @@ -272,6 +279,8 @@ END_MSG_MAP() void FinalRelease() { Uninitialize(); + + ClearResourceModule(); } void ResetUrlRequestManager() { @@ -1243,6 +1252,8 @@ END_MSG_MAP() // Handle network requests when host network stack is used. Passed to the // automation client on initialization. scoped_ptr<UrlmonUrlRequestManager> url_fetcher_; + + HINSTANCE prev_resource_instance_; }; #endif // CHROME_FRAME_CHROME_FRAME_ACTIVEX_BASE_H_ diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc index 7c79287..7ee5950 100644 --- a/chrome_frame/chrome_frame_automation.cc +++ b/chrome_frame/chrome_frame_automation.cc @@ -4,6 +4,7 @@ #include "chrome_frame/chrome_frame_automation.h" +#include "app/app_switches.h" #include "base/callback.h" #include "base/command_line.h" #include "base/compiler_specific.h" @@ -26,6 +27,7 @@ #include "chrome_frame/chrome_launcher_utils.h" #include "chrome_frame/crash_reporting/crash_metrics.h" #include "chrome_frame/custom_sync_call_context.h" +#include "chrome_frame/simple_resource_loader.h" #include "chrome_frame/utils.h" #ifdef NDEBUG @@ -299,6 +301,9 @@ void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams* params, command_line->AppendSwitchPath(switches::kUserDataDir, params->profile_path()); + if (!params->language().empty()) + command_line->AppendSwitchNative(switches::kLang, params->language()); + std::wstring command_line_string(command_line->command_line_string()); // If there are any extra arguments, append them to the command line. if (!params->extra_arguments().empty()) { @@ -720,8 +725,8 @@ bool ChromeFrameAutomationClient::InitiateNavigation(const std::string& url, if (!chrome_launch_params_) { FilePath profile_path; chrome_launch_params_ = new ChromeFrameLaunchParams(parsed_url, - referrer_gurl, profile_path, L"", L"", false, false, - route_all_top_level_navigations_); + referrer_gurl, profile_path, L"", SimpleResourceLoader::GetLanguage(), + L"", false, false, route_all_top_level_navigations_); } else { chrome_launch_params_->set_referrer(referrer_gurl); chrome_launch_params_->set_url(parsed_url); diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h index 8500ee7..47b03c0 100644 --- a/chrome_frame/chrome_frame_automation.h +++ b/chrome_frame/chrome_frame_automation.h @@ -109,14 +109,15 @@ class ChromeFrameLaunchParams : // NOLINT ChromeFrameLaunchParams(const GURL& url, const GURL& referrer, const FilePath& profile_path, const std::wstring& profile_name, + const std::wstring& language, const std::wstring& extra_arguments, bool incognito, bool widget_mode, bool route_all_top_level_navigations) : launch_timeout_(kCommandExecutionTimeout), url_(url), referrer_(referrer), profile_path_(profile_path), - profile_name_(profile_name), extra_arguments_(extra_arguments), - version_check_(true), incognito_mode_(incognito), - is_widget_mode_(widget_mode), + profile_name_(profile_name), language_(language), + extra_arguments_(extra_arguments), version_check_(true), + incognito_mode_(incognito), is_widget_mode_(widget_mode), route_all_top_level_navigations_(route_all_top_level_navigations) { } @@ -155,6 +156,10 @@ class ChromeFrameLaunchParams : // NOLINT return profile_name_; } + const std::wstring& language() const { + return language_; + } + const std::wstring& extra_arguments() const { return extra_arguments_; } @@ -190,6 +195,7 @@ class ChromeFrameLaunchParams : // NOLINT GURL referrer_; FilePath profile_path_; std::wstring profile_name_; + std::wstring language_; std::wstring extra_arguments_; bool version_check_; bool incognito_mode_; diff --git a/chrome_frame/chrome_frame_npapi_unittest.cc b/chrome_frame/chrome_frame_npapi_unittest.cc index c0a0208..34194440 100644 --- a/chrome_frame/chrome_frame_npapi_unittest.cc +++ b/chrome_frame/chrome_frame_npapi_unittest.cc @@ -125,6 +125,7 @@ class TestNPAPIPrivilegedApi: public ::testing::Test { bool expect_privilege_check, bool is_privileged, const std::wstring& profile_name, + const std::wstring& language, const std::wstring& extra_args) { EXPECT_CALL(mock_api, GetLocation()) .WillOnce(Return(std::string("http://www.google.com"))); @@ -137,7 +138,7 @@ class TestNPAPIPrivilegedApi: public ::testing::Test { scoped_refptr<ChromeFrameLaunchParams> launch_params( new ChromeFrameLaunchParams(GURL(), GURL(), FilePath(), profile_name, - extra_args, is_incognito, true, false)); + language, extra_args, is_incognito, true, false)); EXPECT_CALL(*mock_automation, Initialize(_, LaunchParamEq(true, extra_args, is_incognito, true))) @@ -177,6 +178,7 @@ TEST_F(TestNPAPIPrivilegedApi, NoPrivilegeCheckWhenNoArguments) { false, // Fail if privilege check is invoked. false, kDefaultProfileName, + L"", // No specific language override. L""); // No extra args to initialize. // No arguments, no privilege requested. @@ -191,6 +193,7 @@ TEST_F(TestNPAPIPrivilegedApi, NoPrivilegeCheckWhenZeroArgument) { false, // Fail if privilege check is invoked. false, kDefaultProfileName, + L"", // No specific language override. L""); // No extra args to initialize. // Privileged mode explicitly zero. @@ -207,6 +210,7 @@ TEST_F(TestNPAPIPrivilegedApi, NotPrivilegedDoesNotAllowArgsOrProfile) { true, // Fail unless privilege check is invoked. false, // Not privileged. kDefaultProfileName, + L"", // No specific language override. L""); // No extra arguments allowed. char* argn[] = { @@ -227,9 +231,10 @@ TEST_F(TestNPAPIPrivilegedApi, NotPrivilegedDoesNotAllowArgsOrProfile) { TEST_F(TestNPAPIPrivilegedApi, PrivilegedAllowsArgsAndProfile) { SetupPrivilegeTest(false, // Not incognito. - true, // Fail unless privilege check is invoked. - true, // Privileged mode. + true, // Fail unless privilege check is invoked. + true, // Privileged mode. L"custom_profile_name", // Custom profile expected. + L"", // No specific language override. L"-bar=far"); // Extra arguments expected // With privileged mode we expect automation to be enabled. @@ -428,8 +433,9 @@ TEST_F(TestNPAPIPrivilegedProperty, // Attempt setting onprivatemessage when not privileged. SetupPrivilegeTest(false, // not incognito. true, // expect privilege check. - false, // not privileged. + false, // not privileged. kDefaultProfileName, + L"", // No specific language override. L""); char* on_private_message_str = "onprivatemessage()"; @@ -464,6 +470,7 @@ TEST_F(TestNPAPIPrivilegedProperty, true, // expect privilege check. true, // privileged. kDefaultProfileName, + L"", // No specific language override. L""); char* on_private_message_str = "onprivatemessage()"; @@ -499,8 +506,9 @@ TEST_F(TestNPAPIPrivilegedProperty, // Assigning to onprivatemessage when not privileged should fail. SetupPrivilegeTest(false, // not incognito. true, // expect privilege check. - false, // not privileged. + false, // not privileged. kDefaultProfileName, + L"", // No specific language override. L""); char* argn = "privileged_mode"; @@ -529,6 +537,7 @@ TEST_F(TestNPAPIPrivilegedProperty, true, // expect privilege check. true, // privileged. kDefaultProfileName, + L"", // No specific language override. L""); char* argn = "privileged_mode"; diff --git a/chrome_frame/chrome_frame_plugin.h b/chrome_frame/chrome_frame_plugin.h index 27e192e..9ea6ce9 100644 --- a/chrome_frame/chrome_frame_plugin.h +++ b/chrome_frame/chrome_frame_plugin.h @@ -75,8 +75,9 @@ END_MSG_MAP() // is Google Chrome Frame. FilePath actual_profile_name = profile_path.BaseName(); launch_params_ = new ChromeFrameLaunchParams(url, referrer, profile_path, - actual_profile_name.value(), extra_chrome_arguments, incognito_mode, - is_widget_mode, route_all_top_level_navigations); + actual_profile_name.value(), SimpleResourceLoader::GetLanguage(), + extra_chrome_arguments, incognito_mode, is_widget_mode, + route_all_top_level_navigations); return automation_client_->Initialize(this, launch_params_); } diff --git a/chrome_frame/chrome_launcher.cc b/chrome_frame/chrome_launcher.cc index f040d2b..a8ff329 100644 --- a/chrome_frame/chrome_launcher.cc +++ b/chrome_frame/chrome_launcher.cc @@ -23,6 +23,7 @@ const wchar_t* kAllowedSwitches[] = { L"disable-renderer-accessibility", L"enable-experimental-extension-apis", L"force-renderer-accessibility", + L"lang", L"no-default-browser-check", L"noerrdialogs", L"no-first-run", diff --git a/chrome_frame/policy_settings.cc b/chrome_frame/policy_settings.cc index 834d6cc..1a35d67 100644 --- a/chrome_frame/policy_settings.cc +++ b/chrome_frame/policy_settings.cc @@ -4,6 +4,8 @@ #include "chrome_frame/policy_settings.h" +#include <algorithm> + #include "base/logging.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" @@ -11,6 +13,18 @@ #include "chrome/common/policy_constants.h" #include "chrome_frame/utils.h" +namespace { + +// This array specifies the order in which registry keys are tested. Do not +// change this unless the decision is made product-wide (i.e., in Chrome's +// configuration policy provider). +const HKEY kRootKeys[] = { + HKEY_LOCAL_MACHINE, + HKEY_CURRENT_USER +}; + +} // namespace + PolicySettings::RendererForUrl PolicySettings::GetRendererForUrl( const wchar_t* url) { RendererForUrl renderer = default_renderer_; @@ -41,21 +55,24 @@ PolicySettings::RendererForUrl PolicySettings::GetRendererForContentType( return renderer; } -void PolicySettings::RefreshFromRegistry() { - default_renderer_ = RENDERER_NOT_SPECIFIED; - renderer_exclusion_list_.clear(); +// static +void PolicySettings::ReadUrlSettings( + RendererForUrl* default_renderer, + std::vector<std::wstring>* renderer_exclusion_list) { + DCHECK(default_renderer); + DCHECK(renderer_exclusion_list); + + *default_renderer = RENDERER_NOT_SPECIFIED; + renderer_exclusion_list->clear(); base::win::RegKey config_key; DWORD value = RENDERER_NOT_SPECIFIED; - HKEY root_key[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER }; std::wstring settings_value( ASCIIToWide(policy::key::kChromeFrameRendererSettings)); - for (int i = 0; i < arraysize(root_key); ++i) { - if (config_key.Open(root_key[i], policy::kRegistrySubKey, KEY_READ) && + for (int i = 0; i < arraysize(kRootKeys); ++i) { + if (config_key.Open(kRootKeys[i], policy::kRegistrySubKey, KEY_READ) && config_key.ReadValueDW(settings_value.c_str(), &value)) { break; - } else { - config_key.Close(); } } @@ -67,26 +84,72 @@ void PolicySettings::RefreshFromRegistry() { if (value != RENDER_IN_HOST && value != RENDER_IN_CHROME_FRAME) { DVLOG(1) << "default renderer not specified via policy"; } else { - default_renderer_ = static_cast<RendererForUrl>(value); - const char* exclusion_list_name = (default_renderer_ == RENDER_IN_HOST) ? + *default_renderer = static_cast<RendererForUrl>(value); + const char* exclusion_list_name = (*default_renderer == RENDER_IN_HOST) ? policy::key::kRenderInChromeFrameList : policy::key::kRenderInHostList; EnumerateKeyValues(config_key.Handle(), - ASCIIToWide(exclusion_list_name).c_str(), &renderer_exclusion_list_); + ASCIIToWide(exclusion_list_name).c_str(), renderer_exclusion_list); DVLOG(1) << "Default renderer as specified via policy: " - << default_renderer_ - << " exclusion list size: " << renderer_exclusion_list_.size(); + << *default_renderer + << " exclusion list size: " << renderer_exclusion_list->size(); } +} + +// static +void PolicySettings::ReadContentTypeSetting( + std::vector<std::wstring>* content_type_list) { + DCHECK(content_type_list); std::wstring sub_key(policy::kRegistrySubKey); sub_key += L"\\"; sub_key += ASCIIToWide(policy::key::kChromeFrameContentTypes); - for (int i = 0; i < arraysize(root_key) && content_type_list_.size() == 0; + content_type_list->clear(); + for (int i = 0; i < arraysize(kRootKeys) && content_type_list->size() == 0; ++i) { - EnumerateKeyValues(root_key[i], sub_key.c_str(), &content_type_list_); + EnumerateKeyValues(kRootKeys[i], sub_key.c_str(), content_type_list); } } +// static +void PolicySettings::ReadApplicationLocaleSetting( + std::wstring* application_locale) { + DCHECK(application_locale); + + application_locale->clear(); + base::win::RegKey config_key; + std::wstring application_locale_value( + ASCIIToWide(policy::key::kApplicationLocaleValue)); + for (int i = 0; i < arraysize(kRootKeys); ++i) { + if (config_key.Open(kRootKeys[i], policy::kRegistrySubKey, KEY_READ) && + config_key.ReadValue(application_locale_value.c_str(), + application_locale)) { + break; + } + } +} + +void PolicySettings::RefreshFromRegistry() { + RendererForUrl default_renderer; + std::vector<std::wstring> renderer_exclusion_list; + std::vector<std::wstring> content_type_list; + std::wstring application_locale; + + // Read the latest settings from the registry + ReadUrlSettings(&default_renderer, &renderer_exclusion_list); + ReadContentTypeSetting(&content_type_list); + ReadApplicationLocaleSetting(&application_locale); + + // Nofail swap in the new values. (Note: this is all that need be protected + // under a mutex if/when this becomes thread safe.) + using std::swap; + + swap(default_renderer_, default_renderer); + swap(renderer_exclusion_list_, renderer_exclusion_list); + swap(content_type_list_, content_type_list); + swap(application_locale_, application_locale); +} + diff --git a/chrome_frame/policy_settings.h b/chrome_frame/policy_settings.h index f3eacac..28182bd 100644 --- a/chrome_frame/policy_settings.h +++ b/chrome_frame/policy_settings.h @@ -8,6 +8,8 @@ #include <string> #include <vector> +#include "base/basictypes.h" + // A simple class that reads and caches policy settings for Chrome Frame. // TODO(tommi): Support refreshing when new settings are pushed. // TODO(tommi): Use Chrome's classes for this (and the notification service). @@ -34,6 +36,19 @@ class PolicySettings { RendererForUrl GetRendererForContentType(const wchar_t* content_type); + // Returns the policy-configured Chrome app locale, or an empty string if none + // is configured. + const std::wstring& ApplicationLocale() const { + return application_locale_; + } + + // Helper functions for reading settings from the registry + static void ReadUrlSettings(RendererForUrl* default_renderer, + std::vector<std::wstring>* renderer_exclusion_list); + static void ReadContentTypeSetting( + std::vector<std::wstring>* content_type_list); + static void ReadApplicationLocaleSetting(std::wstring* application_locale); + protected: // Protected for now since the class is not thread safe. void RefreshFromRegistry(); @@ -42,6 +57,10 @@ class PolicySettings { RendererForUrl default_renderer_; std::vector<std::wstring> renderer_exclusion_list_; std::vector<std::wstring> content_type_list_; + std::wstring application_locale_; + + private: + DISALLOW_COPY_AND_ASSIGN(PolicySettings); }; 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_; } diff --git a/chrome_frame/simple_resource_loader.h b/chrome_frame/simple_resource_loader.h index 45aca746..9f10934 100644 --- a/chrome_frame/simple_resource_loader.h +++ b/chrome_frame/simple_resource_loader.h @@ -12,6 +12,7 @@ #include <windows.h> #include <string> +#include <vector> #include "base/file_path.h" #include "base/gtest_prod_util.h" @@ -24,33 +25,50 @@ class SimpleResourceLoader { return Singleton<SimpleResourceLoader>::get(); } + // Returns the language tag for the active language. + static std::wstring GetLanguage(); + // Helper method to return the string resource identified by message_id // from the currently loaded locale dll. static std::wstring Get(int message_id); // Retrieves the HINSTANCE of the loaded module handle. May be NULL if a // resource DLL could not be loaded. - HINSTANCE GetResourceModuleHandle(); + HMODULE GetResourceModuleHandle(); + + // Retrieves the preferred languages for the current thread, adding them to + // |language_tags|. + static void GetPreferredLanguages(std::vector<std::wstring>* language_tags); + + // Retrieves the thread/process/user/system preferred languages on Vista+, + // adding them and their fallbacks to |language_tags|. Returns |false| if the + // platform does not support such (i.e., XP). + static bool GetThreadPreferredUILanguages( + std::vector<std::wstring>* language_tags); + + // Retrieves the system language and the region using ICU (used on XP). + static void GetICUSystemLanguage(std::wstring* language, + std::wstring* region); + + // Populates |locales_path| with the path to the "Locales" directory. + static void DetermineLocalesDirectory(FilePath* locales_path); + + // Returns false if |language_tag| is malformed. + static bool IsValidLanguageTag(const std::wstring& language_tag); private: SimpleResourceLoader(); ~SimpleResourceLoader(); - // Retrieves the system language and the region using ICU. - void GetSystemLocale(std::wstring* language, std::wstring* region); - - // Uses |locale| to build the resource DLL name and then looks for the named - // DLL in known locales paths. If it doesn't find it, it falls back to - // looking for an en-US.dll. + // Finds the most-preferred resource DLL for the laguages in |language_tags| + // in |locales_path|. // - // Returns true if a locale DLL can be found, false otherwise. - bool GetLocaleFilePath(const std::wstring& language, - const std::wstring& region, - FilePath* file_path); - - // Loads the locale dll at the given path. Returns a handle to the DLL or - // NULL on failure. - HINSTANCE LoadLocaleDll(const FilePath& dll_path); + // Returns true on success with a handle to the DLL that was loaded in + // |dll_handle| and its path in |file_path|. + static bool LoadLocaleDll(const std::vector<std::wstring>& language_tags, + const FilePath& locales_path, + HMODULE* dll_handle, + FilePath* file_path); // Returns the string resource identified by message_id from the currently // loaded locale dll. @@ -58,9 +76,10 @@ class SimpleResourceLoader { friend struct DefaultSingletonTraits<SimpleResourceLoader>; - FRIEND_TEST_ALL_PREFIXES(SimpleResourceLoaderTest, GetLocaleFilePath); + FRIEND_TEST_ALL_PREFIXES(SimpleResourceLoaderTest, LoadLocaleDll); - static HINSTANCE locale_dll_handle_; + std::wstring language_; + HINSTANCE locale_dll_handle_; }; #endif // CHROME_FRAME_SIMPLE_RESOURCE_LOADER_H_ diff --git a/chrome_frame/test/automation_client_mock.cc b/chrome_frame/test/automation_client_mock.cc index 7626c0b..a77b023 100644 --- a/chrome_frame/test/automation_client_mock.cc +++ b/chrome_frame/test/automation_client_mock.cc @@ -104,7 +104,7 @@ TEST(CFACWithChrome, CreateTooFast) { GURL empty; scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( - empty, empty, profile_path, profile_path.BaseName().value(), L"", + empty, empty, profile_path, profile_path.BaseName().value(), L"", L"", false, false, false)); clp->set_launch_timeout(timeout); clp->set_version_check(false); @@ -136,7 +136,7 @@ TEST(CFACWithChrome, CreateNotSoFast) { GURL empty; scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( - empty, empty, profile_path, profile_path.BaseName().value(), L"", + empty, empty, profile_path, profile_path.BaseName().value(), L"", L"", false, false, false)); clp->set_launch_timeout(timeout); clp->set_version_check(false); @@ -182,7 +182,7 @@ TEST(CFACWithChrome, NavigateOk) { GURL empty; scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( - empty, empty, profile_path, profile_path.BaseName().value(), L"", + empty, empty, profile_path, profile_path.BaseName().value(), L"", L"", false, false, false)); clp->set_launch_timeout(timeout); clp->set_version_check(false); @@ -228,7 +228,7 @@ TEST(CFACWithChrome, NavigateFailed) { GURL empty; scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( - empty, empty, profile_path, profile_path.BaseName().value(), L"", + empty, empty, profile_path, profile_path.BaseName().value(), L"", L"", false, false, false)); clp->set_launch_timeout(10000); clp->set_version_check(false); @@ -266,7 +266,7 @@ TEST_F(CFACMockTest, MockedCreateTabOk) { // Here we go! GURL empty; scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( - empty, empty, profile_path_, profile_path_.BaseName().value(), L"", + empty, empty, profile_path_, profile_path_.BaseName().value(), L"", L"", false, false, false)); clp->set_launch_timeout(timeout); clp->set_version_check(false); @@ -298,7 +298,7 @@ TEST_F(CFACMockTest, MockedCreateTabFailed) { // Here we go! GURL empty; scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( - empty, empty, profile_path_, profile_path_.BaseName().value(), L"", + empty, empty, profile_path_, profile_path_.BaseName().value(), L"", L"", false, false, false)); clp->set_launch_timeout(timeout_); clp->set_version_check(false); @@ -338,7 +338,7 @@ TEST_F(CFACMockTest, OnChannelError) { GURL empty; scoped_refptr<ChromeFrameLaunchParams> clp(new ChromeFrameLaunchParams( - empty, empty, profile_path_, profile_path_.BaseName().value(), L"", + empty, empty, profile_path_, profile_path_.BaseName().value(), L"", L"", false, false, false)); clp->set_launch_timeout(1); // Unneeded timeout, but can't be 0. clp->set_version_check(false); @@ -457,7 +457,7 @@ TEST_F(CFACMockTest, NavigateTwiceAfterInitToSameUrl) { scoped_refptr<ChromeFrameLaunchParams> launch_params( new ChromeFrameLaunchParams( GURL("http://www.nonexistent.com"), empty, profile_path_, - profile_path_.BaseName().value(), L"", false, false, false)); + profile_path_.BaseName().value(), L"", L"", false, false, false)); launch_params->set_launch_timeout(timeout); launch_params->set_version_check(false); EXPECT_TRUE(client_->Initialize(&cfd_, launch_params)); diff --git a/chrome_frame/test/chrome_frame_automation_mock.h b/chrome_frame/test/chrome_frame_automation_mock.h index 2d7a76e..0887f81 100644 --- a/chrome_frame/test/chrome_frame_automation_mock.h +++ b/chrome_frame/test/chrome_frame_automation_mock.h @@ -24,6 +24,7 @@ class AutomationMockDelegate AutomationMockDelegate(MessageLoop* caller_message_loop, int launch_timeout, bool perform_version_check, const std::wstring& profile_name, + const std::wstring& language, const std::wstring& extra_chrome_arguments, bool incognito, bool is_widget_mode) : caller_message_loop_(caller_message_loop), is_connected_(false), @@ -40,7 +41,8 @@ class AutomationMockDelegate GURL empty; scoped_refptr<ChromeFrameLaunchParams> clp( new ChromeFrameLaunchParams(empty, empty, profile_path, profile_name, - extra_chrome_arguments, incognito, is_widget_mode, false)); + language, extra_chrome_arguments, incognito, is_widget_mode, + false)); clp->set_launch_timeout(launch_timeout); clp->set_version_check(perform_version_check); automation_client_->Initialize(this, clp); @@ -145,7 +147,7 @@ class AutomationMockLaunch typedef AutomationMockDelegate<AutomationMockLaunch> Base; AutomationMockLaunch(MessageLoop* caller_message_loop, int launch_timeout) - : Base(caller_message_loop, launch_timeout, true, L"", L"", false, + : Base(caller_message_loop, launch_timeout, true, L"", L"", L"", false, false) { } virtual void OnAutomationServerReady() { @@ -163,7 +165,7 @@ class AutomationMockNavigate typedef AutomationMockDelegate<AutomationMockNavigate> Base; AutomationMockNavigate(MessageLoop* caller_message_loop, int launch_timeout) - : Base(caller_message_loop, launch_timeout, true, L"", L"", false, + : Base(caller_message_loop, launch_timeout, true, L"", L"", L"", false, false) { } virtual void OnLoad(int tab_handle, const GURL& url) { @@ -178,7 +180,8 @@ class AutomationMockPostMessage typedef AutomationMockDelegate<AutomationMockPostMessage> Base; AutomationMockPostMessage(MessageLoop* caller_message_loop, int launch_timeout) - : Base(caller_message_loop, launch_timeout, true, L"", L"", false, false), + : Base(caller_message_loop, launch_timeout, true, L"", L"", L"", false, + false), postmessage_result_(false) {} bool postmessage_result() const { return postmessage_result_; @@ -206,7 +209,8 @@ class AutomationMockHostNetworkRequestStart typedef AutomationMockDelegate<AutomationMockHostNetworkRequestStart> Base; AutomationMockHostNetworkRequestStart(MessageLoop* caller_message_loop, int launch_timeout) - : Base(caller_message_loop, launch_timeout, true, L"", L"", false, false), + : Base(caller_message_loop, launch_timeout, true, L"", L"", L"", false, + false), request_start_result_(false) { if (automation()) { automation()->set_use_chrome_network(false); diff --git a/chrome_frame/test/policy_settings_unittest.cc b/chrome_frame/test/policy_settings_unittest.cc index 52a3c55..55a0b32 100644 --- a/chrome_frame/test/policy_settings_unittest.cc +++ b/chrome_frame/test/policy_settings_unittest.cc @@ -25,6 +25,7 @@ void DeleteChromeFramePolicyEntries(HKEY root) { key.DeleteKey(ASCIIToWide(policy::key::kRenderInChromeFrameList).c_str()); key.DeleteKey(ASCIIToWide(policy::key::kRenderInHostList).c_str()); key.DeleteKey(ASCIIToWide(policy::key::kChromeFrameContentTypes).c_str()); + key.DeleteKey(ASCIIToWide(policy::key::kApplicationLocaleValue).c_str()); } } @@ -118,6 +119,17 @@ bool SetCFContentTypes(HKEY policy_root, const wchar_t* content_types[], return true; } +bool SetChromeApplicationLocale(HKEY policy_root, const wchar_t* locale) { + RegKey policy_key; + if (!InitializePolicyKey(policy_root, &policy_key)) + return false; + + std::wstring application_locale_value( + ASCIIToWide(policy::key::kApplicationLocaleValue)); + EXPECT_TRUE(policy_key.WriteValue(application_locale_value.c_str(), locale)); + return true; +} + } // end namespace TEST(PolicySettings, RendererForUrl) { @@ -215,3 +227,26 @@ TEST(PolicySettings, RendererForContentType) { } } +TEST(PolicySettings, ApplicationLocale) { + TempRegKeyOverride::DeleteAllTempKeys(); + + scoped_ptr<TempRegKeyOverride> hklm_pol( + new TempRegKeyOverride(HKEY_LOCAL_MACHINE, L"hklm_pol")); + scoped_ptr<TempRegKeyOverride> hkcu_pol( + new TempRegKeyOverride(HKEY_CURRENT_USER, L"hkcu_pol")); + + scoped_ptr<PolicySettings> settings(new PolicySettings()); + EXPECT_TRUE(settings->ApplicationLocale().empty()); + + static const wchar_t kTestApplicationLocale[] = L"fr-CA"; + + HKEY root[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER }; + for (int i = 0; i < arraysize(root); ++i) { + SetChromeApplicationLocale(root[i], kTestApplicationLocale); + settings.reset(new PolicySettings()); + EXPECT_EQ(std::wstring(kTestApplicationLocale), + settings->ApplicationLocale()); + + DeleteChromeFramePolicyEntries(root[i]); + } +} diff --git a/chrome_frame/test/proxy_factory_mock.cc b/chrome_frame/test/proxy_factory_mock.cc index 6e15104..effc4a0 100644 --- a/chrome_frame/test/proxy_factory_mock.cc +++ b/chrome_frame/test/proxy_factory_mock.cc @@ -21,7 +21,7 @@ TEST(ProxyFactoryTest, CreateDestroy) { FilePath profile_path; scoped_refptr<ChromeFrameLaunchParams> params( new ChromeFrameLaunchParams(empty, empty, profile_path, - L"Adam.N.Epilinter", L"", false, false, false)); + L"Adam.N.Epilinter", L"", L"", false, false, false)); params->set_launch_timeout(0); params->set_version_check(false); @@ -41,7 +41,7 @@ TEST(ProxyFactoryTest, CreateSameProfile) { FilePath profile_path; scoped_refptr<ChromeFrameLaunchParams> params( new ChromeFrameLaunchParams(empty, empty, profile_path, - L"Dr. Gratiano Forbeson", L"", false, false, false)); + L"Dr. Gratiano Forbeson", L"", L"", false, false, false)); params->set_launch_timeout(0); params->set_version_check(false); @@ -65,13 +65,13 @@ TEST(ProxyFactoryTest, CreateDifferentProfiles) { FilePath profile_path; scoped_refptr<ChromeFrameLaunchParams> params1( new ChromeFrameLaunchParams(empty, empty, profile_path, - L"Adam.N.Epilinter", L"", false, false, false)); + L"Adam.N.Epilinter", L"", L"", false, false, false)); params1->set_launch_timeout(0); params1->set_version_check(false); scoped_refptr<ChromeFrameLaunchParams> params2( new ChromeFrameLaunchParams(empty, empty, profile_path, - L"Dr. Gratiano Forbeson", L"", false, false, false)); + L"Dr. Gratiano Forbeson", L"", L"", false, false, false)); params2->set_launch_timeout(0); params2->set_version_check(false); @@ -95,7 +95,7 @@ TEST(ProxyFactoryTest, FastCreateDestroy) { FilePath profile_path; scoped_refptr<ChromeFrameLaunchParams> params( new ChromeFrameLaunchParams(empty, empty, profile_path, - L"Dr. Gratiano Forbeson", L"", false, false, false)); + L"Dr. Gratiano Forbeson", L"", L"", false, false, false)); params->set_launch_timeout(10000); params->set_version_check(false); diff --git a/chrome_frame/test/simple_resource_loader_test.cc b/chrome_frame/test/simple_resource_loader_test.cc index f67ef49..e120a6b 100644 --- a/chrome_frame/test/simple_resource_loader_test.cc +++ b/chrome_frame/test/simple_resource_loader_test.cc @@ -5,21 +5,83 @@ #include "chrome_frame/simple_resource_loader.h" #include "base/file_path.h" +#include "base/win/windows_version.h" #include "testing/gtest/include/gtest/gtest.h" -TEST(SimpleResourceLoaderTest, GetLocaleFilePath) { - SimpleResourceLoader loader; - +TEST(SimpleResourceLoaderTest, LoadLocaleDll) { + std::vector<std::wstring> language_tags; + FilePath locales_path; FilePath file_path; + HMODULE dll_handle = NULL; + + SimpleResourceLoader::DetermineLocalesDirectory(&locales_path); + // Test valid language-region string: - EXPECT_TRUE(loader.GetLocaleFilePath(L"en", L"GB", &file_path)); + language_tags.clear(); + language_tags.push_back(L"en-GB"); + language_tags.push_back(L"en"); + EXPECT_TRUE( + SimpleResourceLoader::LoadLocaleDll(language_tags, locales_path, + &dll_handle, &file_path)); + if (NULL != dll_handle) { + FreeLibrary(dll_handle); + dll_handle = NULL; + } EXPECT_TRUE(file_path.BaseName() == FilePath(L"en-GB.dll")); // Test valid language-region string for which we only have a language dll: - EXPECT_TRUE(loader.GetLocaleFilePath(L"fr", L"FR", &file_path)); + language_tags.clear(); + language_tags.push_back(L"fr-FR"); + language_tags.push_back(L"fr"); + EXPECT_TRUE( + SimpleResourceLoader::LoadLocaleDll(language_tags, locales_path, + &dll_handle, &file_path)); + if (NULL != dll_handle) { + FreeLibrary(dll_handle); + dll_handle = NULL; + } EXPECT_TRUE(file_path.BaseName() == FilePath(L"fr.dll")); - // Test invalid language-region string, make sure defaults to en-US.dll: - EXPECT_TRUE(loader.GetLocaleFilePath(L"xx", L"XX", &file_path)); + // Test invalid language-region string, make sure fallback works: + language_tags.clear(); + language_tags.push_back(L"xx-XX"); + language_tags.push_back(L"en-US"); + EXPECT_TRUE( + SimpleResourceLoader::LoadLocaleDll(language_tags, locales_path, + &dll_handle, &file_path)); + if (NULL != dll_handle) { + FreeLibrary(dll_handle); + dll_handle = NULL; + } EXPECT_TRUE(file_path.BaseName() == FilePath(L"en-US.dll")); } + +TEST(SimpleResourceLoaderTest, GetThreadPreferredUILanguages) { + std::vector<std::wstring> language_tags; + + if (base::win::GetVersion() >= base::win::VERSION_VISTA) { + EXPECT_TRUE( + SimpleResourceLoader::GetThreadPreferredUILanguages(&language_tags)); + // Did we find at least one language? + EXPECT_NE(static_cast<std::vector<std::wstring>::size_type>(0), + language_tags.size()); + } else { + EXPECT_FALSE( + SimpleResourceLoader::GetThreadPreferredUILanguages(&language_tags)); + } +} + +TEST(SimpleResourceLoaderTest, GetICUSystemLanguage) { + std::wstring language; + std::wstring region; + + SimpleResourceLoader::GetICUSystemLanguage(&language, ®ion); + EXPECT_NE(static_cast<std::wstring::size_type>(0), language.size()); +} + +TEST(SimpleResourceLoaderTest, InstanceTest) { + SimpleResourceLoader* loader = SimpleResourceLoader::instance(); + + ASSERT_TRUE(NULL != loader); + ASSERT_TRUE(NULL != loader->GetResourceModuleHandle()); +} |