Skip to content
Permalink
Browse files

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
  • Loading branch information...
yukawa committed Jan 10, 2016
1 parent 0989ef2 commit a2c1d40e5244e10c5afe808b92daf205cec76fdf
@@ -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)) {
}
@@ -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
@@ -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.
@@ -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
@@ -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);
@@ -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) {}
@@ -55,6 +55,7 @@ struct InputState {
};

struct InputBehavior {
bool initialized;
bool disabled;
bool prefer_kana_input;
bool use_mode_indicator;
@@ -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;
}
@@ -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_;
}
@@ -63,6 +63,7 @@ class TipPrivateContext {
DWORD text_layout_sink_cookie);
~TipPrivateContext();

void EnsureInitialized();
client::ClientInterface *GetClient();
SurrogatePairObserver *GetSurrogatePairObserver();
TipUiElementManager *GetUiElementManager();
@@ -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.

0 comments on commit a2c1d40

Please sign in to comment.
You can’t perform that action at this time.