Bug 1367692 - Make IMEHandler not restore default IMC unless legacy IMM-IME is active. r=m_kato

TIPs (and normal keyboard layouts) don't need IMC on focused window.  So, in most environment, it's not necessary to restore default IMC of focused window.

Therefore, this patch makes IMEHandler not restore default IMC unless legacy IMM-IME is active and disassociate IMC from focused window when IMM-IME isn't active.

However, this is risky change.  Therefore, the new behavior is disabled in default settings.  On the other hand, we need the new behavior only when MS-IME for Japanese is active on Win10.  Therefore, this patch adds a pref to enable/disable the hack and make it true in the default settings.

MozReview-Commit-ID: KAVxVT9CrsW
This commit is contained in:
Masayuki Nakano 2017-06-07 11:42:27 +09:00
Родитель 0d10a7c233
Коммит f6dd08693e
7 изменённых файлов: 129 добавлений и 29 удалений

Просмотреть файл

@ -3710,6 +3710,11 @@ pref("intl.tsf.enable", true);
// Support IMEs implemented with IMM in TSF mode. // Support IMEs implemented with IMM in TSF mode.
pref("intl.tsf.support_imm", true); pref("intl.tsf.support_imm", true);
// This is referred only when both "intl.tsf.enable" and "intl.tsf.support_imm"
// are true. When this is true, default IMC is associated with focused window
// only when active keyboard layout is a legacy IMM-IME.
pref("intl.tsf.associate_imc_only_when_imm_ime_is_active", false);
// Enables/Disables hack for specific TIP. // Enables/Disables hack for specific TIP.
// Whether creates native caret for ATOK or not. // Whether creates native caret for ATOK or not.
@ -3735,6 +3740,11 @@ pref("intl.tsf.hack.easy_changjei.do_not_return_no_layout_error", true);
// ITfContextView::GetTextExt() if the specified range is the first character // ITfContextView::GetTextExt() if the specified range is the first character
// of selected clause of composition string. // of selected clause of composition string.
pref("intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_first_char", true); pref("intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_first_char", true);
// Whether default IMC should be associated with focused window when MS-IME
// for Japanese on Win10 is active. MS-IME for Japanese on Win10 has a crash
// bug. While restoring default IMC when MS-IME for Japanese is active,
// it sometimes crashes after Creators Update. This pref avoid the crash.
pref("intl.tsf.hack.ms_japanese_ime.do_not_associate_imc_on_win10", true);
// Whether use previous character rect for the result of // Whether use previous character rect for the result of
// ITfContextView::GetTextExt() if the specified range is the caret of // ITfContextView::GetTextExt() if the specified range is the caret of
// composition string. // composition string.

Просмотреть файл

@ -180,9 +180,9 @@ IMEContext::IMEContext(HWND aWnd)
{ {
} }
IMEContext::IMEContext(nsWindow* aWindow) IMEContext::IMEContext(nsWindowBase* aWindowBase)
: mWnd(aWindow->GetWindowHandle()) : mWnd(aWindowBase->GetWindowHandle())
, mIMC(::ImmGetContext(aWindow->GetWindowHandle())) , mIMC(::ImmGetContext(aWindowBase->GetWindowHandle()))
{ {
} }
@ -195,9 +195,9 @@ IMEContext::Init(HWND aWnd)
} }
void void
IMEContext::Init(nsWindow* aWindow) IMEContext::Init(nsWindowBase* aWindowBase)
{ {
Init(aWindow->GetWindowHandle()); Init(aWindowBase->GetWindowHandle());
} }
void void

Просмотреть файл

@ -19,6 +19,7 @@
#include "npapi.h" #include "npapi.h"
class nsWindow; class nsWindow;
class nsWindowBase;
namespace mozilla { namespace mozilla {
namespace widget { namespace widget {
@ -35,7 +36,7 @@ public:
} }
explicit IMEContext(HWND aWnd); explicit IMEContext(HWND aWnd);
explicit IMEContext(nsWindow* aWindow); explicit IMEContext(nsWindowBase* aWindowBase);
~IMEContext() ~IMEContext()
{ {
@ -48,7 +49,7 @@ public:
} }
void Init(HWND aWnd); void Init(HWND aWnd);
void Init(nsWindow* aWindow); void Init(nsWindowBase* aWindowBase);
void Clear(); void Clear();
bool IsValid() const bool IsValid() const

Просмотреть файл

@ -3,26 +3,26 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define INPUTSCOPE_INIT_GUID
#define TEXTATTRS_INIT_GUID
#include "TSFTextStore.h"
#include <olectl.h> #include <olectl.h>
#include <algorithm> #include <algorithm>
#include "mozilla/Logging.h"
#include "nscore.h" #include "nscore.h"
#include "nsWindow.h" #include "nsWindow.h"
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
#include "WinIMEHandler.h"
#include "WinUtils.h" #include "WinUtils.h"
#include "mozilla/AutoRestore.h" #include "mozilla/AutoRestore.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/TextEventDispatcher.h" #include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEvents.h" #include "mozilla/TextEvents.h"
#include "mozilla/WindowsVersion.h" #include "mozilla/WindowsVersion.h"
#include "nsIXULRuntime.h" #include "nsIXULRuntime.h"
#define INPUTSCOPE_INIT_GUID
#define TEXTATTRS_INIT_GUID
#include "TSFTextStore.h"
namespace mozilla { namespace mozilla {
namespace widget { namespace widget {
@ -1037,7 +1037,7 @@ public:
return mActiveTIPKeyboardDescription; return mActiveTIPKeyboardDescription;
} }
static bool IsIMM_IME() static bool IsIMM_IMEActive()
{ {
if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) { if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
return IsIMM_IME(::GetKeyboardLayout(0)); return IsIMM_IME(::GetKeyboardLayout(0));
@ -1355,6 +1355,8 @@ TSFStaticSink::OnActivated(DWORD dwProfileType,
mIsIMM_IME = IsIMM_IME(hkl); mIsIMM_IME = IsIMM_IME(hkl);
GetTIPDescription(rclsid, mLangID, guidProfile, GetTIPDescription(rclsid, mLangID, guidProfile,
mActiveTIPKeyboardDescription); mActiveTIPKeyboardDescription);
// Notify IMEHandler of changing active keyboard layout.
IMEHandler::OnKeyboardLayoutChanged();
} }
MOZ_LOG(sTextStoreLog, LogLevel::Info, MOZ_LOG(sTextStoreLog, LogLevel::Info,
("0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08X), " ("0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08X), "
@ -6290,9 +6292,16 @@ TSFTextStore::ProcessMessage(nsWindowBase* aWindow,
// static // static
bool bool
TSFTextStore::IsIMM_IME() TSFTextStore::IsIMM_IMEActive()
{ {
return TSFStaticSink::IsIMM_IME(); return TSFStaticSink::IsIMM_IMEActive();
}
// static
bool
TSFTextStore::IsMSJapaneseIMEActive()
{
return TSFStaticSink::GetInstance()->IsMSJapaneseIMEActive();
} }
/******************************************************************/ /******************************************************************/

Просмотреть файл

@ -6,13 +6,16 @@
#ifndef TSFTextStore_h_ #ifndef TSFTextStore_h_
#define TSFTextStore_h_ #define TSFTextStore_h_
#include "mozilla/RefPtr.h"
#include "nsString.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIWidget.h" #include "nsIWidget.h"
#include "nsString.h"
#include "nsWindowBase.h" #include "nsWindowBase.h"
#include "WinUtils.h" #include "WinUtils.h"
#include "WritingModes.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h" #include "mozilla/StaticPtr.h"
#include "mozilla/TextEventDispatcher.h" #include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextRange.h" #include "mozilla/TextRange.h"
@ -235,7 +238,20 @@ public:
return (IsComposing() && sEnabledTextStore->mWidget == aWidget); return (IsComposing() && sEnabledTextStore->mWidget == aWidget);
} }
static bool IsIMM_IME(); static nsWindowBase* GetEnabledWindowBase()
{
return sEnabledTextStore ? sEnabledTextStore->mWidget.get() : nullptr;
}
/**
* Returns true if active keyboard layout is a legacy IMM-IME.
*/
static bool IsIMM_IMEActive();
/**
* Returns true if active TIP is MS-IME for Japanese.
*/
static bool IsMSJapaneseIMEActive();
#ifdef DEBUG #ifdef DEBUG
// Returns true when keyboard layout has IME (TIP). // Returns true when keyboard layout has IME (TIP).

Просмотреть файл

@ -7,6 +7,7 @@
#include "IMMHandler.h" #include "IMMHandler.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/WindowsVersion.h"
#include "nsWindowDefs.h" #include "nsWindowDefs.h"
#include "WinTextEventDispatcherListener.h" #include "WinTextEventDispatcherListener.h"
@ -47,6 +48,7 @@ bool IMEHandler::sPluginHasFocus = false;
#ifdef NS_ENABLE_TSF #ifdef NS_ENABLE_TSF
bool IMEHandler::sIsInTSFMode = false; bool IMEHandler::sIsInTSFMode = false;
bool IMEHandler::sIsIMMEnabled = true; bool IMEHandler::sIsIMMEnabled = true;
bool IMEHandler::sAssociateIMCOnlyWhenIMM_IMEActive = false;
decltype(SetInputScopes)* IMEHandler::sSetInputScopes = nullptr; decltype(SetInputScopes)* IMEHandler::sSetInputScopes = nullptr;
#endif // #ifdef NS_ENABLE_TSF #endif // #ifdef NS_ENABLE_TSF
@ -62,6 +64,10 @@ IMEHandler::Initialize()
sIsInTSFMode = TSFTextStore::IsInTSFMode(); sIsInTSFMode = TSFTextStore::IsInTSFMode();
sIsIMMEnabled = sIsIMMEnabled =
!sIsInTSFMode || Preferences::GetBool("intl.tsf.support_imm", true); !sIsInTSFMode || Preferences::GetBool("intl.tsf.support_imm", true);
sAssociateIMCOnlyWhenIMM_IMEActive =
sIsIMMEnabled &&
Preferences::GetBool("intl.tsf.associate_imc_only_when_imm_ime_is_active",
false);
if (!sIsInTSFMode) { if (!sIsInTSFMode) {
// When full TSFTextStore is not available, try to use SetInputScopes API // When full TSFTextStore is not available, try to use SetInputScopes API
// to enable at least InputScope. Use GET_MODULE_HANDLE_EX_FLAG_PIN to // to enable at least InputScope. Use GET_MODULE_HANDLE_EX_FLAG_PIN to
@ -173,7 +179,7 @@ IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage,
} }
// IME isn't implemented with IMM, IMMHandler shouldn't handle any // IME isn't implemented with IMM, IMMHandler shouldn't handle any
// messages. // messages.
if (!TSFTextStore::IsIMM_IME()) { if (!TSFTextStore::IsIMM_IMEActive()) {
return false; return false;
} }
} }
@ -188,8 +194,9 @@ IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage,
bool bool
IMEHandler::IsIMMActive() IMEHandler::IsIMMActive()
{ {
return TSFTextStore::IsIMM_IME(); return TSFTextStore::IsIMM_IMEActive();
} }
#endif // #ifdef NS_ENABLE_TSF #endif // #ifdef NS_ENABLE_TSF
// static // static
@ -408,6 +415,27 @@ IMEHandler::OnDestroyWindow(nsWindow* aWindow)
AssociateIMEContext(aWindow, true); AssociateIMEContext(aWindow, true);
} }
#ifdef NS_ENABLE_TSF
// static
bool
IMEHandler::NeedsToAssociateIMC()
{
if (sAssociateIMCOnlyWhenIMM_IMEActive) {
return TSFTextStore::IsIMM_IMEActive();
}
// Even if IMC should be associated with focused widget with non-IMM-IME,
// we need to avoid crash bug of MS-IME for Japanese on Win10. It crashes
// while we're associating default IME to a window when it's active.
static const bool sDoNotAssociateIMCWhenMSJapaneseIMEActiveOnWin10 =
IsWin10OrLater() &&
Preferences::GetBool(
"intl.tsf.hack.ms_japanese_ime.do_not_associate_imc_on_win10", true);
return !sDoNotAssociateIMCWhenMSJapaneseIMEActiveOnWin10 ||
!TSFTextStore::IsMSJapaneseIMEActive();
}
#endif // #ifdef NS_ENABLE_TSF
// static // static
void void
IMEHandler::SetInputContext(nsWindow* aWindow, IMEHandler::SetInputContext(nsWindow* aWindow,
@ -439,8 +467,8 @@ IMEHandler::SetInputContext(nsWindow* aWindow,
TSFTextStore::SetInputContext(aWindow, aInputContext, aAction); TSFTextStore::SetInputContext(aWindow, aInputContext, aAction);
if (IsTSFAvailable()) { if (IsTSFAvailable()) {
if (sIsIMMEnabled) { if (sIsIMMEnabled) {
// Associate IME context for IMM-IMEs. // Associate IMC with aWindow only when it's necessary.
AssociateIMEContext(aWindow, enable); AssociateIMEContext(aWindow, enable && NeedsToAssociateIMC());
} else if (oldInputContext.mIMEState.mEnabled == IMEState::PLUGIN) { } else if (oldInputContext.mIMEState.mEnabled == IMEState::PLUGIN) {
// Disassociate the IME context from the window when plugin loses focus // Disassociate the IME context from the window when plugin loses focus
// in pure TSF mode. // in pure TSF mode.
@ -468,15 +496,15 @@ IMEHandler::SetInputContext(nsWindow* aWindow,
// static // static
void void
IMEHandler::AssociateIMEContext(nsWindow* aWindow, bool aEnable) IMEHandler::AssociateIMEContext(nsWindowBase* aWindowBase, bool aEnable)
{ {
IMEContext context(aWindow); IMEContext context(aWindowBase);
if (aEnable) { if (aEnable) {
context.AssociateDefaultContext(); context.AssociateDefaultContext();
return; return;
} }
// Don't disassociate the context after the window is destroyed. // Don't disassociate the context after the window is destroyed.
if (aWindow->Destroyed()) { if (aWindowBase->Destroyed()) {
return; return;
} }
context.Disassociate(); context.Disassociate();
@ -524,6 +552,33 @@ IMEHandler::CurrentKeyboardLayoutHasIME()
} }
#endif // #ifdef DEBUG #endif // #ifdef DEBUG
// static
void
IMEHandler::OnKeyboardLayoutChanged()
{
if (!sIsIMMEnabled || !IsTSFAvailable()) {
return;
}
// If there is no TSFTextStore which has focus, i.e., no editor has focus,
// nothing to do here.
nsWindowBase* windowBase = TSFTextStore::GetEnabledWindowBase();
if (!windowBase) {
return;
}
// If IME isn't available, nothing to do here.
InputContext inputContext = windowBase->GetInputContext();
if (!WinUtils::IsIMEEnabled(inputContext)) {
return;
}
// Associate or Disassociate IMC if it's necessary.
// Note that this does nothing if the window has already associated with or
// disassociated from the window.
AssociateIMEContext(windowBase, NeedsToAssociateIMC());
}
// static // static
void void
IMEHandler::SetInputScopeForIMM32(nsWindow* aWindow, IMEHandler::SetInputScopeForIMM32(nsWindow* aWindow,

Просмотреть файл

@ -7,7 +7,7 @@
#define WinIMEHandler_h_ #define WinIMEHandler_h_
#include "nscore.h" #include "nscore.h"
#include "nsIWidget.h" #include "nsWindowBase.h"
#include "npapi.h" #include "npapi.h"
#include <windows.h> #include <windows.h>
#include <inputscope.h> #include <inputscope.h>
@ -100,9 +100,9 @@ public:
const InputContextAction& aAction); const InputContextAction& aAction);
/** /**
* Associate or disassociate IME context to/from the aWindow. * Associate or disassociate IME context to/from the aWindowBase.
*/ */
static void AssociateIMEContext(nsWindow* aWindow, bool aEnable); static void AssociateIMEContext(nsWindowBase* aWindowBase, bool aEnable);
/** /**
* Called when the window is created. * Called when the window is created.
@ -120,6 +120,13 @@ public:
static void DefaultProcOfPluginEvent(nsWindow* aWindow, static void DefaultProcOfPluginEvent(nsWindow* aWindow,
const NPEvent* aPluginEvent); const NPEvent* aPluginEvent);
#ifdef NS_ENABLE_TSF
/**
* This is called by TSFStaticSink when active IME is changed.
*/
static void OnKeyboardLayoutChanged();
#endif // #ifdef NS_ENABLE_TSF
#ifdef DEBUG #ifdef DEBUG
/** /**
* Returns true when current keyboard layout has IME. Otherwise, false. * Returns true when current keyboard layout has IME. Otherwise, false.
@ -142,6 +149,7 @@ private:
// If sIMMEnabled is false, any IME messages are not handled in TSF mode. // If sIMMEnabled is false, any IME messages are not handled in TSF mode.
// Additionally, IME context is always disassociated from focused window. // Additionally, IME context is always disassociated from focused window.
static bool sIsIMMEnabled; static bool sIsIMMEnabled;
static bool sAssociateIMCOnlyWhenIMM_IMEActive;
static bool IsTSFAvailable() { return (sIsInTSFMode && !sPluginHasFocus); } static bool IsTSFAvailable() { return (sIsInTSFMode && !sPluginHasFocus); }
static bool IsIMMActive(); static bool IsIMMActive();
@ -154,6 +162,7 @@ private:
static bool IsKeyboardPresentOnSlate(); static bool IsKeyboardPresentOnSlate();
static bool IsInTabletMode(); static bool IsInTabletMode();
static bool AutoInvokeOnScreenKeyboardInDesktopMode(); static bool AutoInvokeOnScreenKeyboardInDesktopMode();
static bool NeedsToAssociateIMC();
/** /**
* Show the Windows on-screen keyboard. Only allowed for * Show the Windows on-screen keyboard. Only allowed for