From a2c1d40e5244e10c5afe808b92daf205cec76fdf Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Sun, 10 Jan 2016 00:16:14 -0800 Subject: [PATCH] Introduce a workaround for sandboxed applications. In short, Mozc cannot establish IPC connections from sandboxed applications until mozc_server is launched from any other non-sandboxed applications because: 1. Mozc client cannot launch mozc_server from sandboxed applications. 2. mozc::client::Client does give up further connection when it fails to launch mozc_server. 3. Mozc client cannot directly read the config file from sandboxed applications. Hence it asks mozc_server to send back the current config data. If the client cannot establish connection to mozc_server, mozc client assumes that no short-cut key is assigned to enable IME. To make matters worse, on Windows 10 the search box on Windows 10 is implemented by SearchUI.exe, which is a sandboxed application but is launched at an early starge of the log-in process. Because of that, Mozc client that was loaded into SearchUI.exe will end up with "give-up" mode before mozc_server is launched from Mozc client running in other non-sandboxed applications. Unfortunately this is a kind of design issue of Mozc IPC implementation. In particular, fixing the 1st limitation in the above list requires non-trivial redesign. To mitigate the problem in SearchUI.exe, we slightly modify the 2nd limitation in the above list. With this CL, if Mozc client is running in a sandboxed application, we call mozc::client::Client::Reset() every time when the UI thread gains focus. Hence there is still a chance that user cannot use Mozc in SearchUI.exe just after logging in, but it is supposed to not be persistent anymore. Closes #351. BUG=#351 TEST=manually done REF_BUG=24793812 REF_CL=105726484 --- src/base/win_util.cc | 24 +++++++ src/base/win_util.h | 3 + src/mozc_version_template.txt | 2 +- src/win32/base/config_snapshot.cc | 93 +++++++++++++--------------- src/win32/base/config_snapshot.h | 2 +- src/win32/base/input_state.cc | 3 +- src/win32/base/input_state.h | 1 + src/win32/ime/ime_private_context.cc | 18 +++--- src/win32/tip/tip_private_context.cc | 35 +++++++---- src/win32/tip/tip_private_context.h | 1 + src/win32/tip/tip_text_service.cc | 11 ++++ 11 files changed, 119 insertions(+), 74 deletions(-) diff --git a/src/base/win_util.cc b/src/base/win_util.cc index 2cf66fe41..6f716ada8 100644 --- a/src/base/win_util.cc +++ b/src/base/win_util.cc @@ -65,6 +65,24 @@ bool EqualLuid(const LUID &L1, const LUID &L2) { return (L1.LowPart == L2.LowPart && L1.HighPart == L2.HighPart); } +bool IsProcessSandboxedImpl() { + bool is_restricted = false; + if (!WinUtil::IsProcessRestricted(::GetCurrentProcess(), &is_restricted)) { + return true; + } + if (is_restricted) { + return true; + } + + bool in_appcontainer = false; + if (!WinUtil::IsProcessInAppContainer(::GetCurrentProcess(), + &in_appcontainer)) { + return true; + } + + return in_appcontainer; +} + } // namespace HMODULE WinUtil::LoadSystemLibrary(const wstring &base_filename) { @@ -603,6 +621,12 @@ bool WinUtil::IsPerUserInputSettingsEnabled() { return !is_thread_local; } +bool WinUtil::IsProcessSandboxed() { + // Thread safety is not required. + static bool sandboxed = IsProcessSandboxedImpl(); + return sandboxed; +} + ScopedCOMInitializer::ScopedCOMInitializer() : hr_(::CoInitialize(nullptr)) { } diff --git a/src/base/win_util.h b/src/base/win_util.h index 1c2b22fb8..b846c5cbc 100644 --- a/src/base/win_util.h +++ b/src/base/win_util.h @@ -150,6 +150,9 @@ class WinUtil { // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724947.aspx static bool IsPerUserInputSettingsEnabled(); + // Returns true if the current process is restricted or in AppContainer. + static bool IsProcessSandboxed(); + private: // Compares |lhs| with |rhs| by CompareStringOrdinal and returns the result // in |are_equal|. If |ignore_case| is true, this function uses system diff --git a/src/mozc_version_template.txt b/src/mozc_version_template.txt index 47e39243e..062eb289d 100644 --- a/src/mozc_version_template.txt +++ b/src/mozc_version_template.txt @@ -1,6 +1,6 @@ MAJOR=2 MINOR=17 -BUILD=2309 +BUILD=2310 REVISION=102 # NACL_DICTIONARY_VERSION is the target version of the system dictionary to be # downloaded by NaCl Mozc. diff --git a/src/win32/base/config_snapshot.cc b/src/win32/base/config_snapshot.cc index b152ce94d..ea201b78f 100644 --- a/src/win32/base/config_snapshot.cc +++ b/src/win32/base/config_snapshot.cc @@ -53,50 +53,30 @@ struct StaticConfigSnapshot { KeyInformation direct_mode_keys[kMaxDirectModeKeys]; }; -bool IsProcessSandboxedImpl() { - bool is_restricted = false; - if (!WinUtil::IsProcessRestricted(::GetCurrentProcess(), &is_restricted)) { - return true; +bool GetConfigSnapshotForSandboxedProcess(ClientInterface *client, + Config *config) { + if (client == nullptr) { + return false; } - if (is_restricted) { - return true; + // config1.db is likely to be inaccessible from sandboxed processes. + // So retrieve the config data from the server process. + // CAVEATS: ConfigHandler::GetConfig always returns true even when it fails + // to load the config file due to the sandbox. b/10449414 + if (!client->CheckVersionOrRestartServer()) { + return false; } - - bool in_appcontainer = false; - if (!WinUtil::IsProcessInAppContainer(::GetCurrentProcess(), - &in_appcontainer)) { - return true; + if (!client->GetConfig(config)) { + return false; } - - return in_appcontainer; -} - -bool IsProcessSandboxed() { - // Thread safety is not required. - static bool sandboxed = IsProcessSandboxedImpl(); - return sandboxed; + return true; } -StaticConfigSnapshot GetConfigSnapshotImpl(ClientInterface *client) { - StaticConfigSnapshot snapshot = {}; - +StaticConfigSnapshot GetConfigSnapshotForNonSandboxedProcess() { Config config; - if (IsProcessSandboxed()) { - // config1.db is likely to be inaccessible from sandboxed processes. - // So retrieve the config data from the server process. - // CAVEATS: ConfigHandler::GetConfig always returns true even when it fails - // to load the config file due to the sandbox. b/10449414 - if (!client->CheckVersionOrRestartServer()) { - return snapshot; - } - if (!client->GetConfig(&config)) { - return snapshot; - } - } else { - // config1.db should be readable in this case. - config.CopyFrom(config::ConfigHandler::GetConfig()); - } + // config1.db should be readable in this case. + config.CopyFrom(config::ConfigHandler::GetConfig()); + StaticConfigSnapshot snapshot = {}; snapshot.use_kana_input = (config.preedit_method() == Config::KANA); snapshot.use_keyboard_to_change_preedit_method = config.use_keyboard_to_change_preedit_method(); @@ -122,24 +102,35 @@ ConfigSnapshot::Info::Info() use_mode_indicator(false) {} // static -ConfigSnapshot::Info ConfigSnapshot::Get(client::ClientInterface *client) { - if (client == nullptr) { - return ConfigSnapshot::Info(); +bool ConfigSnapshot::Get(client::ClientInterface *client, Info *info) { + // A temporary workaround for b/24793812. Always reload config if the + // process is sandboxed. + // TODO(yukawa): Cache the result once it succeeds. + if (WinUtil::IsProcessSandboxed()) { + Config config; + if (!GetConfigSnapshotForSandboxedProcess(client, &config)) { + return false; + } + info->use_kana_input = (config.preedit_method() == Config::KANA); + info->use_keyboard_to_change_preedit_method = + config.use_keyboard_to_change_preedit_method(); + info->use_mode_indicator = config.use_mode_indicator(); + info->direct_mode_keys = KeyInfoUtil::ExtractSortedDirectModeKeys(config); + return true; } // Note: Thread-safety is not required. - static auto static_snapshot = GetConfigSnapshotImpl(client); - - ConfigSnapshot::Info snapshot; - snapshot.use_kana_input = static_snapshot.use_kana_input; - snapshot.use_keyboard_to_change_preedit_method = - static_snapshot.use_keyboard_to_change_preedit_method; - snapshot.use_mode_indicator = static_snapshot.use_mode_indicator; - snapshot.direct_mode_keys.resize(static_snapshot.num_direct_mode_keys); - for (size_t i = 0; i < static_snapshot.num_direct_mode_keys; ++i) { - snapshot.direct_mode_keys[i] = static_snapshot.direct_mode_keys[i]; + const static StaticConfigSnapshot cached_snapshot = + GetConfigSnapshotForNonSandboxedProcess(); + info->use_kana_input = cached_snapshot.use_kana_input; + info->use_keyboard_to_change_preedit_method = + cached_snapshot.use_keyboard_to_change_preedit_method; + info->use_mode_indicator = cached_snapshot.use_mode_indicator; + info->direct_mode_keys.resize(cached_snapshot.num_direct_mode_keys); + for (size_t i = 0; i < cached_snapshot.num_direct_mode_keys; ++i) { + info->direct_mode_keys[i] = cached_snapshot.direct_mode_keys[i]; } - return snapshot; + return true; } } // namespace win32 diff --git a/src/win32/base/config_snapshot.h b/src/win32/base/config_snapshot.h index c150d1d1d..99e580a2a 100644 --- a/src/win32/base/config_snapshot.h +++ b/src/win32/base/config_snapshot.h @@ -52,7 +52,7 @@ class ConfigSnapshot { Info(); }; - static Info Get(client::ClientInterface *client); + static bool Get(client::ClientInterface *client, Info *info); private: DISALLOW_IMPLICIT_CONSTRUCTORS(ConfigSnapshot); diff --git a/src/win32/base/input_state.cc b/src/win32/base/input_state.cc index 7842978b0..2812b5f19 100644 --- a/src/win32/base/input_state.cc +++ b/src/win32/base/input_state.cc @@ -39,7 +39,8 @@ InputState::InputState() } InputBehavior::InputBehavior() - : disabled(false), + : initialized(false), + disabled(false), prefer_kana_input(false), use_mode_indicator(false), use_romaji_key_to_toggle_input_style(false) {} diff --git a/src/win32/base/input_state.h b/src/win32/base/input_state.h index cb479b1ef..517ff7d6b 100644 --- a/src/win32/base/input_state.h +++ b/src/win32/base/input_state.h @@ -55,6 +55,7 @@ struct InputState { }; struct InputBehavior { + bool initialized; bool disabled; bool prefer_kana_input; bool use_mode_indicator; diff --git a/src/win32/ime/ime_private_context.cc b/src/win32/ime/ime_private_context.cc index 34872e2fd..a97167604 100644 --- a/src/win32/ime/ime_private_context.cc +++ b/src/win32/ime/ime_private_context.cc @@ -165,14 +165,16 @@ bool PrivateContextUtil::EnsurePrivateContextIsInitialized( } // Try to reflect the current config to the IME behavior. - const auto &snapshot = - ConfigSnapshot::Get(private_context_allocator->client); - auto *behavior = private_context_allocator->ime_behavior; - behavior->prefer_kana_input = snapshot.use_kana_input; - behavior->use_romaji_key_to_toggle_input_style = - snapshot.use_keyboard_to_change_preedit_method; - behavior->use_mode_indicator = snapshot.use_mode_indicator; - behavior->direct_mode_keys = snapshot.direct_mode_keys; + ConfigSnapshot::Info snapshot; + if (ConfigSnapshot::Get(private_context_allocator->client, &snapshot)) { + auto *behavior = private_context_allocator->ime_behavior; + behavior->prefer_kana_input = snapshot.use_kana_input; + behavior->use_romaji_key_to_toggle_input_style = + snapshot.use_keyboard_to_change_preedit_method; + behavior->use_mode_indicator = snapshot.use_mode_indicator; + behavior->direct_mode_keys = snapshot.direct_mode_keys; + behavior->initialized = true; + } return true; } diff --git a/src/win32/tip/tip_private_context.cc b/src/win32/tip/tip_private_context.cc index b09529abc..ef22bf6a0 100644 --- a/src/win32/tip/tip_private_context.cc +++ b/src/win32/tip/tip_private_context.cc @@ -79,18 +79,7 @@ TipPrivateContext::TipPrivateContext(DWORD text_edit_sink_cookie, DWORD text_layout_sink_cookie) : state_(new InternalState(text_edit_sink_cookie, text_layout_sink_cookie)) { - Capability capability; - capability.set_text_deletion(Capability::DELETE_PRECEDING_TEXT); - state_->client_->set_client_capability(capability); - - // Try to reflect the current config to the IME behavior. - const auto &snapshot = ConfigSnapshot::Get(state_->client_.get()); - auto *behavior = &state_->input_behavior_; - behavior->prefer_kana_input = snapshot.use_kana_input; - behavior->use_romaji_key_to_toggle_input_style = - snapshot.use_keyboard_to_change_preedit_method; - behavior->use_mode_indicator = snapshot.use_mode_indicator; - behavior->direct_mode_keys = snapshot.direct_mode_keys; + EnsureInitialized(); } TipPrivateContext::~TipPrivateContext() {} @@ -99,6 +88,28 @@ ClientInterface *TipPrivateContext::GetClient() { return state_->client_.get(); } +void TipPrivateContext::EnsureInitialized() { + if (!state_->input_behavior_.initialized) { + state_->client_->Reset(); + + Capability capability; + capability.set_text_deletion(Capability::DELETE_PRECEDING_TEXT); + state_->client_->set_client_capability(capability); + } + + // Try to reflect the current config to the IME behavior. + ConfigSnapshot::Info snapshot; + if (ConfigSnapshot::Get(state_->client_.get(), &snapshot)) { + auto *behavior = &state_->input_behavior_; + behavior->prefer_kana_input = snapshot.use_kana_input; + behavior->use_romaji_key_to_toggle_input_style = + snapshot.use_keyboard_to_change_preedit_method; + behavior->use_mode_indicator = snapshot.use_mode_indicator; + behavior->direct_mode_keys = snapshot.direct_mode_keys; + behavior->initialized = true; + } +} + SurrogatePairObserver *TipPrivateContext::GetSurrogatePairObserver() { return &state_->surrogate_pair_observer_; } diff --git a/src/win32/tip/tip_private_context.h b/src/win32/tip/tip_private_context.h index cd01f2985..e7f03bf62 100644 --- a/src/win32/tip/tip_private_context.h +++ b/src/win32/tip/tip_private_context.h @@ -63,6 +63,7 @@ class TipPrivateContext { DWORD text_layout_sink_cookie); ~TipPrivateContext(); + void EnsureInitialized(); client::ClientInterface *GetClient(); SurrogatePairObserver *GetSurrogatePairObserver(); TipUiElementManager *GetUiElementManager(); diff --git a/src/win32/tip/tip_text_service.cc b/src/win32/tip/tip_text_service.cc index 23f5a13ad..d1ca2a97d 100644 --- a/src/win32/tip/tip_text_service.cc +++ b/src/win32/tip/tip_text_service.cc @@ -887,6 +887,17 @@ class TipTextServiceImpl // ITfThreadFocusSink virtual HRESULT STDMETHODCALLTYPE OnSetThreadFocus() { EnsureKanaLockUnlocked(); + + // A temporary workaround for b/24793812. When previous atempt to + // establish conection failed, retry again as if this was the first attempt. + // TODO(yukawa): We should give up if this fails a number of times. + if (WinUtil::IsProcessSandboxed()) { + auto *private_context = GetFocusedPrivateContext(); + if (private_context != nullptr) { + private_context->EnsureInitialized(); + } + } + // While ITfThreadMgrEventSink::OnSetFocus notifies the logical focus inside // the application, ITfThreadFocusSink notifies the OS-level keyboard focus // events. In both cases, Mozc's UI visibility should be updated.