From dfbfb3d55e99ffa8d8656b3c7ee63279faff93de Mon Sep 17 00:00:00 2001 From: Sorah Fukumori Date: Thu, 23 Apr 2020 07:57:14 +0900 Subject: [PATCH] feat: respect language preferences on Win/macOS This commit fixes https://github.com/electron/electron/issues/18829 Previously the full preferences set to OS was not given to Chromium. Also, this commit improves fallback font selection for CJK text. Chromium uses browser languages to determine fallback fonts on Windows, especially kanji/han characters in CJK. For instance, when user sets 'en-US, ja-JP' to Accept-Language, while Chromium chooses Japanese font for kanji text, but Electron chooses Chinese font. This is because only the first language was given to Accept-Language on Electron. This patch is based on https://github.com/electron/electron/pull/15532 Co-authored-by: Nitish Sakhawalkar Co-authored-by: Kasumi Hanazuki --- filenames.gni | 4 + .../browser/api/electron_api_web_contents.cc | 20 ++++- shell/common/language_util.h | 26 +++++++ shell/common/language_util_linux.cc | 17 +++++ shell/common/language_util_mac.mm | 25 ++++++ shell/common/language_util_win.cc | 76 +++++++++++++++++++ spec-main/chromium-spec.ts | 3 +- 7 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 shell/common/language_util.h create mode 100644 shell/common/language_util_linux.cc create mode 100644 shell/common/language_util_mac.mm create mode 100644 shell/common/language_util_win.cc diff --git a/filenames.gni b/filenames.gni index ca3db4b5cddf7..01c32bd9a2533 100644 --- a/filenames.gni +++ b/filenames.gni @@ -545,6 +545,10 @@ filenames = { "shell/common/key_weak_map.h", "shell/common/keyboard_util.cc", "shell/common/keyboard_util.h", + "shell/common/language_util.h", + "shell/common/language_util_linux.cc", + "shell/common/language_util_mac.mm", + "shell/common/language_util_win.cc", "shell/common/mac/main_application_bundle.h", "shell/common/mac/main_application_bundle.mm", "shell/common/mouse_util.cc", diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index b65096d368ad3..6e1c7926c442c 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -89,6 +89,7 @@ #include "shell/common/gin_converters/value_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" +#include "shell/common/language_util.h" #include "shell/common/mouse_util.h" #include "shell/common/node_includes.h" #include "shell/common/options_switches.h" @@ -100,6 +101,7 @@ #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h" #include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h" +#include "third_party/blink/public/mojom/renderer_preferences.mojom.h" #include "ui/base/cursor/cursor.h" #include "ui/base/mojom/cursor_type.mojom-shared.h" #include "ui/display/screen.h" @@ -121,7 +123,6 @@ #endif #if defined(OS_LINUX) || defined(OS_WIN) -#include "third_party/blink/public/mojom/renderer_preferences.mojom.h" #include "ui/gfx/font_render_params.h" #endif @@ -535,7 +536,22 @@ void WebContents::InitWithSessionAndOptions( managed_web_contents()->GetView()->SetDelegate(this); auto* prefs = web_contents()->GetMutableRendererPrefs(); - prefs->accept_languages = g_browser_process->GetApplicationLocale(); + + // Collect preferred languages from OS and browser process. accept_languages + // effects HTTP header, navigator.languages, and CJK fallback font selection. + // + // Note that an application locale set to the browser process might be + // different with the one set to the preference list. + // (e.g. overridden with --lang) + std::string accept_languages = + g_browser_process->GetApplicationLocale() + ","; + for (auto const& language : electron::GetPreferredLanguages()) { + if (language == g_browser_process->GetApplicationLocale()) + continue; + accept_languages += language + ","; + } + accept_languages.pop_back(); + prefs->accept_languages = accept_languages; #if defined(OS_LINUX) || defined(OS_WIN) // Update font settings. diff --git a/shell/common/language_util.h b/shell/common/language_util.h new file mode 100644 index 0000000000000..3e35073c142cc --- /dev/null +++ b/shell/common/language_util.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_COMMON_LANGUAGE_UTIL_H_ +#define SHELL_COMMON_LANGUAGE_UTIL_H_ + +#include +#include + +#include "base/strings/string16.h" + +namespace electron { + +// Return a list of user preferred languages from OS. The list doesn't include +// overrides from command line arguments. +std::vector GetPreferredLanguages(); + +#if defined(OS_WIN) +bool GetPreferredLanguagesUsingGlobalization( + std::vector* languages); +#endif + +} // namespace electron + +#endif // SHELL_COMMON_LANGUAGE_UTIL_H_ diff --git a/shell/common/language_util_linux.cc b/shell/common/language_util_linux.cc new file mode 100644 index 0000000000000..76bc557741ba0 --- /dev/null +++ b/shell/common/language_util_linux.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#include "ui/base/l10n/l10n_util.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + // Return empty as there's no API to use. You may be able to use + // GetApplicationLocale() of a browser process. + return std::vector{}; +} + +} // namespace electron diff --git a/shell/common/language_util_mac.mm b/shell/common/language_util_mac.mm new file mode 100644 index 0000000000000..205d7087bf1f8 --- /dev/null +++ b/shell/common/language_util_mac.mm @@ -0,0 +1,25 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#import +#include +#include + +#include "base/strings/sys_string_conversions.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + __block std::vector languages; + [[NSLocale preferredLanguages] + enumerateObjectsUsingBlock:^(NSString* language, NSUInteger i, + BOOL* stop) { + languages.push_back(base::SysNSStringToUTF8(language)); + }]; + return languages; +} + +} // namespace electron diff --git a/shell/common/language_util_win.cc b/shell/common/language_util_win.cc new file mode 100644 index 0000000000000..d6717b31aec21 --- /dev/null +++ b/shell/common/language_util_win.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#include +#include +#include + +#include "base/strings/sys_string_conversions.h" +#include "base/win/core_winrt_util.h" +#include "base/win/i18n.h" +#include "base/win/win_util.h" +#include "base/win/windows_version.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + std::vector languages16; + + // Attempt to use API available on Windows 10 or later, which + // returns the full list of language preferences. + if (!GetPreferredLanguagesUsingGlobalization(&languages16)) { + base::win::i18n::GetThreadPreferredUILanguageList(&languages16); + } + + std::vector languages; + for (const auto& language : languages16) { + languages.push_back(base::SysWideToUTF8(language)); + } + return languages; +} + +bool GetPreferredLanguagesUsingGlobalization( + std::vector* languages) { + if (base::win::GetVersion() < base::win::Version::WIN10) + return false; + if (!base::win::ResolveCoreWinRTDelayload() || + !base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) + return false; + + base::win::ScopedHString guid = base::win::ScopedHString::Create( + RuntimeClass_Windows_System_UserProfile_GlobalizationPreferences); + Microsoft::WRL::ComPtr< + ABI::Windows::System::UserProfile::IGlobalizationPreferencesStatics> + prefs; + + HRESULT hr = + base::win::RoGetActivationFactory(guid.get(), IID_PPV_ARGS(&prefs)); + if (FAILED(hr)) + return false; + + ABI::Windows::Foundation::Collections::IVectorView* langs; + hr = prefs->get_Languages(&langs); + if (FAILED(hr)) + return false; + + unsigned size; + hr = langs->get_Size(&size); + if (FAILED(hr)) + return false; + + for (unsigned i = 0; i < size; ++i) { + HSTRING hstr; + hr = langs->GetAt(i, &hstr); + if (SUCCEEDED(hr)) { + base::WStringPiece str = base::win::ScopedHString(hstr).Get(); + languages->emplace_back(str.data(), str.size()); + } + } + + return true; +} + +} // namespace electron diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index 2a4320d393427..894f856baa6ce 100644 --- a/spec-main/chromium-spec.ts +++ b/spec-main/chromium-spec.ts @@ -319,7 +319,8 @@ describe('chromium features', () => { const w = new BrowserWindow({ show: false }); await w.loadURL('about:blank'); const languages = await w.webContents.executeJavaScript('navigator.languages'); - expect(languages).to.deep.equal([appLocale]); + expect(languages.length).to.be.greaterThan(0); + expect(languages).to.contain(appLocale); }); });