diff options
author | Peter Varga <pvarga@inf.u-szeged.hu> | 2016-05-24 17:08:09 +0200 |
---|---|---|
committer | Peter Varga <pvarga@inf.u-szeged.hu> | 2016-07-13 21:12:03 +0000 |
commit | 89319455c36d48fe46a8501edd3c174c1de291fb (patch) | |
tree | 8ba7c33284a0b973090eb5de322ef466a859fbcf | |
parent | 9bb9076c50048913075f11692f784ebb959d63cf (diff) |
Fix changing locale of ResourceBundle on Linux
The ResourceBundle is not re-initialized for new Renderer Processes
because it is initialized only once in Zygote Process. This means runtime
locale changes do not affect new Renderer Processes. Zygote Process is
supported on Linux only thus other platforms don't need this fix.
With this change the locale of the ResourceBundle is reloaded when
new Renderer Process is started. For accessing the pak file of the locale
in the sandboxed environment the file descriptor is passed via the Chromium's
GlobalDescriptor solution.
Task-number: QTBUG-53000
Change-Id: I57e84078db9d0795d16d930aa1b3e93a6e86ec39
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
m--------- | src/3rdparty | 0 | ||||
-rw-r--r-- | src/core/content_browser_client_qt.cpp | 21 | ||||
-rw-r--r-- | src/core/content_browser_client_qt.h | 4 | ||||
-rw-r--r-- | src/core/content_main_delegate_qt.cpp | 13 | ||||
-rw-r--r-- | src/core/core_gyp_generator.pro | 1 | ||||
-rw-r--r-- | src/core/global_descriptors_qt.h | 10 | ||||
-rw-r--r-- | src/core/resource_bundle_qt.cpp | 68 | ||||
-rw-r--r-- | src/core/web_engine_library_info.cpp | 12 | ||||
-rw-r--r-- | tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp | 37 | ||||
-rw-r--r-- | tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp | 32 |
10 files changed, 195 insertions, 3 deletions
diff --git a/src/3rdparty b/src/3rdparty -Subproject 428ec4b558454d610b9cfbd6735a90157562831 +Subproject b5d5ca440f575fdc81ca95b9ba8c4c8f4ff5818 diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 8da5e2611..0c3acd37b 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -85,6 +85,11 @@ #include "web_engine_context.h" #include "web_engine_library_info.h" +#if defined(Q_OS_LINUX) +#include "global_descriptors_qt.h" +#include "ui/base/resource/resource_bundle.h" +#endif + #if defined(ENABLE_PLUGINS) #include "content/public/browser/browser_ppapi_host.h" #include "ppapi/host/ppapi_host.h" @@ -457,6 +462,22 @@ void ContentBrowserClientQt::AppendExtraCommandLineSwitches(base::CommandLine* c command_line->AppendSwitchASCII(switches::kLang, GetApplicationLocale()); } +#if defined(Q_OS_LINUX) +void ContentBrowserClientQt::GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::FileDescriptorInfo* mappings) +{ + const std::string &locale = GetApplicationLocale(); + const base::FilePath &locale_file_path = ui::ResourceBundle::GetSharedInstance().GetLocaleFilePath(locale, true); + if (locale_file_path.empty()) + return; + + // Open pak file of the current locale in the Browser process and pass its file descriptor to the sandboxed + // Renderer Process. FileDescriptorInfo is responsible for closing the file descriptor. + int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; + base::File locale_file = base::File(locale_file_path, flags); + mappings->Transfer(kWebEngineLocale, base::ScopedFD(locale_file.TakePlatformFile())); +} +#endif + #if defined(ENABLE_PLUGINS) void ContentBrowserClientQt::DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) { browser_host->GetPpapiHost()->AddHostFactoryFilter( diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h index 58924eb30..098631476 100644 --- a/src/core/content_browser_client_qt.h +++ b/src/core/content_browser_client_qt.h @@ -109,6 +109,10 @@ public: std::string GetAcceptLangs(content::BrowserContext* context) Q_DECL_OVERRIDE; virtual void AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id) Q_DECL_OVERRIDE; +#if defined(Q_OS_LINUX) + virtual void GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::FileDescriptorInfo* mappings) Q_DECL_OVERRIDE; +#endif + #if defined(ENABLE_PLUGINS) virtual void DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) Q_DECL_OVERRIDE; #endif diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp index a9c49ff2e..379c42468 100644 --- a/src/core/content_main_delegate_qt.cpp +++ b/src/core/content_main_delegate_qt.cpp @@ -59,7 +59,9 @@ #include "base/cpu.h" #endif -#include <QLocale> +#if defined(OS_LINUX) +#include "ui/base/ui_base_switches.h" +#endif namespace QtWebEngineCore { @@ -135,6 +137,15 @@ content::ContentBrowserClient *ContentMainDelegateQt::CreateContentBrowserClient content::ContentRendererClient *ContentMainDelegateQt::CreateContentRendererClient() { +#if defined(OS_LINUX) + base::CommandLine *parsedCommandLine = base::CommandLine::ForCurrentProcess(); + + if (parsedCommandLine->HasSwitch(switches::kLang)) { + const std::string &locale = parsedCommandLine->GetSwitchValueASCII(switches::kLang); + ui::ResourceBundle::GetSharedInstance().ReloadLocaleResources(locale); + } +#endif + return new ContentRendererClientQt; } diff --git a/src/core/core_gyp_generator.pro b/src/core/core_gyp_generator.pro index cdff4ed64..83de53d0d 100644 --- a/src/core/core_gyp_generator.pro +++ b/src/core/core_gyp_generator.pro @@ -141,6 +141,7 @@ HEADERS = \ file_picker_controller.h \ gl_context_qt.h \ gl_surface_qt.h \ + global_descriptors_qt.h \ javascript_dialog_controller_p.h \ javascript_dialog_controller.h \ javascript_dialog_manager_qt.h \ diff --git a/src/core/global_descriptors_qt.h b/src/core/global_descriptors_qt.h new file mode 100644 index 000000000..e9d490a2e --- /dev/null +++ b/src/core/global_descriptors_qt.h @@ -0,0 +1,10 @@ +#ifndef GLOBAL_DESCRIPTORS_QT_H +#define GLOBAL_DESCRIPTORS_QT_H + +#include "content/public/common/content_descriptors.h" + +enum { + kWebEngineLocale = kContentIPCDescriptorMax + 1, +}; + +#endif // GLOBAL_DESCRIPTORS_QT_H diff --git a/src/core/resource_bundle_qt.cpp b/src/core/resource_bundle_qt.cpp index ba25cc543..115dc56dc 100644 --- a/src/core/resource_bundle_qt.cpp +++ b/src/core/resource_bundle_qt.cpp @@ -38,10 +38,20 @@ ****************************************************************************/ #include "base/command_line.h" +#include "base/metrics/histogram.h" #include "content/public/common/content_switches.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/data_pack.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_switches.h" + #include "web_engine_library_info.h" +#if defined(OS_LINUX) +#include "base/posix/global_descriptors.h" +#include "global_descriptors_qt.h" +#endif + namespace ui { void ResourceBundle::LoadCommonResources() @@ -60,4 +70,62 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) return GetEmptyImage(); } +bool ResourceBundle::LocaleDataPakExists(const std::string& locale) +{ +#if defined(OS_LINUX) + base::CommandLine *parsed_command_line = base::CommandLine::ForCurrentProcess(); + std::string process_type = parsed_command_line->GetSwitchValueASCII(switches::kProcessType); + if (process_type == switches::kRendererProcess) { + // The Renderer Process is sandboxed thus only one locale is available in it. + // The particular one is passed by the --lang command line option. + if (!parsed_command_line->HasSwitch(switches::kLang) || parsed_command_line->GetSwitchValueASCII(switches::kLang) != locale) + return false; + + auto global_descriptors = base::GlobalDescriptors::GetInstance(); + return global_descriptors->MaybeGet(kWebEngineLocale) != -1; + } +#endif + + return !GetLocaleFilePath(locale, true).empty(); +} + +std::string ResourceBundle::LoadLocaleResources(const std::string& pref_locale) +{ + DCHECK(!locale_resources_data_.get()) << "locale.pak already loaded"; + + std::string app_locale = l10n_util::GetApplicationLocale(pref_locale); + +#if defined(OS_LINUX) + int locale_fd = base::GlobalDescriptors::GetInstance()->MaybeGet(kWebEngineLocale); + if (locale_fd > -1) { + scoped_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P)); + data_pack->LoadFromFile(base::File(locale_fd)); + locale_resources_data_.reset(data_pack.release()); + return app_locale; + } +#endif + + base::FilePath locale_file_path = GetOverriddenPakPath(); + if (locale_file_path.empty()) + locale_file_path = GetLocaleFilePath(app_locale, true); + + if (locale_file_path.empty()) { + // It's possible that there is no locale.pak. + LOG(WARNING) << "locale_file_path.empty() for locale " << app_locale; + return std::string(); + } + + scoped_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P)); + if (!data_pack->LoadFromPath(locale_file_path)) { + UMA_HISTOGRAM_ENUMERATION("ResourceBundle.LoadLocaleResourcesError", + logging::GetLastSystemErrorCode(), 16000); + LOG(ERROR) << "failed to load locale.pak"; + NOTREACHED(); + return std::string(); + } + + locale_resources_data_.reset(data_pack.release()); + return app_locale; +} + } // namespace ui diff --git a/src/core/web_engine_library_info.cpp b/src/core/web_engine_library_info.cpp index 2de3d39ff..a81a26d16 100644 --- a/src/core/web_engine_library_info.cpp +++ b/src/core/web_engine_library_info.cpp @@ -332,8 +332,16 @@ base::string16 WebEngineLibraryInfo::getApplicationName() std::string WebEngineLibraryInfo::getApplicationLocale() { base::CommandLine *parsedCommandLine = base::CommandLine::ForCurrentProcess(); - if (!parsedCommandLine->HasSwitch(switches::kLang)) - return QLocale().bcp47Name().toStdString(); + if (!parsedCommandLine->HasSwitch(switches::kLang)) { + const QString &locale = QLocale().bcp47Name(); + + // QLocale::bcp47Name returns "en" for American English locale. Chromium requires the "US" suffix + // to clarify the dialect and ignores the shorter version. + if (locale == "en") + return "en-US"; + + return locale.toStdString(); + } return parsedCommandLine->GetSwitchValueASCII(switches::kLang); } diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 1c5461fa7..d59fa92e9 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -76,6 +76,8 @@ private Q_SLOTS: void stopSettingFocusWhenDisabled_data(); void inputEventForwardingDisabledWhenActiveFocusOnPressDisabled(); + void changeLocale(); + private: inline QQuickWebEngineView *newWebEngineView(); inline QQuickWebEngineView *webEngineView() const; @@ -652,5 +654,40 @@ void tst_QQuickWebEngineView::inputEventForwardingDisabledWhenActiveFocusOnPress QTRY_COMPARE_WITH_TIMEOUT(view->hasActiveFocus(), true, 1000); } +void tst_QQuickWebEngineView::changeLocale() +{ + QUrl url("http://non.existent/"); + + QLocale::setDefault(QLocale("de")); + QQuickWebEngineView *viewDE = newWebEngineView(); + QSignalSpy titleSpyHU(viewDE, SIGNAL(titleChanged())); + + viewDE->setUrl(url); + QVERIFY(waitForLoadFailed(viewDE)); + QTRY_COMPARE(titleSpyHU.size(), 2); + QCOMPARE(viewDE->title(), QStringLiteral("Nicht verf\u00FCgbar: %1").arg(url.toString())); + + QLocale::setDefault(QLocale("en")); + QQuickWebEngineView *viewEN = newWebEngineView(); + QSignalSpy titleSpyEN(viewEN, SIGNAL(titleChanged())); + + viewEN->setUrl(url); + QVERIFY(waitForLoadFailed(viewEN)); + QTRY_COMPARE(titleSpyEN.size(), 2); + QCOMPARE(viewEN->title(), QStringLiteral("%1 is not available").arg(url.toString())); + + viewDE->setUrl(QUrl("about:blank")); + QVERIFY(waitForLoadSucceeded(viewDE)); + titleSpyHU.clear(); + + viewDE->setUrl(url); + QVERIFY(waitForLoadFailed(viewDE)); + QTRY_COMPARE(titleSpyHU.size(), 2); + QCOMPARE(viewDE->title(), QStringLiteral("Nicht verf\u00FCgbar: %1").arg(url.toString())); + + delete viewDE; + delete viewEN; +} + QTEST_MAIN(tst_QQuickWebEngineView) #include "tst_qquickwebengineview.moc" diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 52a696d66..e3ca30ef5 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -74,6 +74,8 @@ private Q_SLOTS: void doNotSendMouseKeyboardEventsWhenDisabled_data(); void stopSettingFocusWhenDisabled(); void stopSettingFocusWhenDisabled_data(); + + void changeLocale(); }; // This will be called before the first test function is executed. @@ -741,5 +743,35 @@ void tst_QWebEngineView::stopSettingFocusWhenDisabled_data() QTest::newRow("disabled view does not get focus") << false << false; } +void tst_QWebEngineView::changeLocale() +{ + QUrl url("http://non.existent/"); + + QLocale::setDefault(QLocale("de")); + QWebEngineView viewDE; + viewDE.setUrl(url); + + QVERIFY(waitForSignal(&viewDE, SIGNAL(titleChanged(QString)))); + QVERIFY(waitForSignal(&viewDE, SIGNAL(loadFinished(bool)))); + QCOMPARE(viewDE.title(), QStringLiteral("Nicht verf\u00FCgbar: %1").arg(url.toString())); + + QLocale::setDefault(QLocale("en")); + QWebEngineView viewEN; + viewEN.setUrl(url); + + QVERIFY(waitForSignal(&viewEN, SIGNAL(titleChanged(QString)))); + QVERIFY(waitForSignal(&viewEN, SIGNAL(loadFinished(bool)))); + QCOMPARE(viewEN.title(), QStringLiteral("%1 is not available").arg(url.toString())); + + viewDE.setUrl(QUrl("about:blank")); + QVERIFY(waitForSignal(&viewDE, SIGNAL(loadFinished(bool)))); + + viewDE.setUrl(url); + + QVERIFY(waitForSignal(&viewDE, SIGNAL(titleChanged(QString)))); + QVERIFY(waitForSignal(&viewDE, SIGNAL(loadFinished(bool)))); + QCOMPARE(viewDE.title(), QStringLiteral("Nicht verf\u00FCgbar: %1").arg(url.toString())); +} + QTEST_MAIN(tst_QWebEngineView) #include "tst_qwebengineview.moc" |