widget/windows/WinIMEHandler.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "WinIMEHandler.h"
     8 #include "mozilla/Preferences.h"
     9 #include "nsIMM32Handler.h"
    10 #include "nsWindowDefs.h"
    12 #ifdef NS_ENABLE_TSF
    13 #include "nsTextStore.h"
    14 #endif // #ifdef NS_ENABLE_TSF
    16 #include "nsWindow.h"
    17 #include "WinUtils.h"
    19 namespace mozilla {
    20 namespace widget {
    22 /******************************************************************************
    23  * IMEHandler
    24  ******************************************************************************/
    26 #ifdef NS_ENABLE_TSF
    27 bool IMEHandler::sIsInTSFMode = false;
    28 bool IMEHandler::sIsIMMEnabled = true;
    29 bool IMEHandler::sPluginHasFocus = false;
    30 decltype(SetInputScopes)* IMEHandler::sSetInputScopes = nullptr;
    31 #endif // #ifdef NS_ENABLE_TSF
    33 // static
    34 void
    35 IMEHandler::Initialize()
    36 {
    37 #ifdef NS_ENABLE_TSF
    38   nsTextStore::Initialize();
    39   sIsInTSFMode = nsTextStore::IsInTSFMode();
    40   sIsIMMEnabled =
    41     !sIsInTSFMode || Preferences::GetBool("intl.tsf.support_imm", true);
    42   if (!sIsInTSFMode) {
    43     // When full nsTextStore is not available, try to use SetInputScopes API
    44     // to enable at least InputScope. Use GET_MODULE_HANDLE_EX_FLAG_PIN to
    45     // ensure that msctf.dll will not be unloaded.
    46     HMODULE module = nullptr;
    47     if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"msctf.dll",
    48                            &module)) {
    49       sSetInputScopes = reinterpret_cast<decltype(SetInputScopes)*>(
    50         GetProcAddress(module, "SetInputScopes"));
    51     }
    52   }
    53 #endif // #ifdef NS_ENABLE_TSF
    55   nsIMM32Handler::Initialize();
    56 }
    58 // static
    59 void
    60 IMEHandler::Terminate()
    61 {
    62 #ifdef NS_ENABLE_TSF
    63   if (sIsInTSFMode) {
    64     nsTextStore::Terminate();
    65     sIsInTSFMode = false;
    66   }
    67 #endif // #ifdef NS_ENABLE_TSF
    69   nsIMM32Handler::Terminate();
    70 }
    72 // static
    73 void*
    74 IMEHandler::GetNativeData(uint32_t aDataType)
    75 {
    76 #ifdef NS_ENABLE_TSF
    77   void* result = nsTextStore::GetNativeData(aDataType);
    78   if (!result || !(*(static_cast<void**>(result)))) {
    79     return nullptr;
    80   }
    81   // XXX During the TSF module test, sIsInTSFMode must be true.  After that,
    82   //     the value should be restored but currently, there is no way for that.
    83   //     When the TSF test is enabled again, we need to fix this.  Perhaps,
    84   //     sending a message can fix this.
    85   sIsInTSFMode = true;
    86   return result;
    87 #else // #ifdef NS_ENABLE_TSF
    88   return nullptr;
    89 #endif // #ifdef NS_ENABLE_TSF #else
    90 }
    92 // static
    93 bool
    94 IMEHandler::ProcessRawKeyMessage(const MSG& aMsg)
    95 {
    96 #ifdef NS_ENABLE_TSF
    97   if (IsTSFAvailable()) {
    98     return nsTextStore::ProcessRawKeyMessage(aMsg);
    99   }
   100 #endif // #ifdef NS_ENABLE_TSF
   101   return false; // noting to do in IMM mode.
   102 }
   104 // static
   105 bool
   106 IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage,
   107                            WPARAM& aWParam, LPARAM& aLParam,
   108                            MSGResult& aResult)
   109 {
   110 #ifdef NS_ENABLE_TSF
   111   if (IsTSFAvailable()) {
   112     nsTextStore::ProcessMessage(aWindow, aMessage, aWParam, aLParam, aResult);
   113     if (aResult.mConsumed) {
   114       return true;
   115     }
   116     // If we don't support IMM in TSF mode, we don't use nsIMM32Handler.
   117     if (!sIsIMMEnabled) {
   118       return false;
   119     }
   120     // IME isn't implemented with IMM, nsIMM32Handler shouldn't handle any
   121     // messages.
   122     if (!nsTextStore::IsIMM_IME()) {
   123       return false;
   124     }
   125   }
   126 #endif // #ifdef NS_ENABLE_TSF
   128   return nsIMM32Handler::ProcessMessage(aWindow, aMessage, aWParam, aLParam,
   129                                         aResult);
   130 }
   132 // static
   133 bool
   134 IMEHandler::IsComposing()
   135 {
   136 #ifdef NS_ENABLE_TSF
   137   if (IsTSFAvailable()) {
   138     return nsTextStore::IsComposing();
   139   }
   140 #endif // #ifdef NS_ENABLE_TSF
   142   return nsIMM32Handler::IsComposing();
   143 }
   145 // static
   146 bool
   147 IMEHandler::IsComposingOn(nsWindow* aWindow)
   148 {
   149 #ifdef NS_ENABLE_TSF
   150   if (IsTSFAvailable()) {
   151     return nsTextStore::IsComposingOn(aWindow);
   152   }
   153 #endif // #ifdef NS_ENABLE_TSF
   155   return nsIMM32Handler::IsComposingOn(aWindow);
   156 }
   158 // static
   159 nsresult
   160 IMEHandler::NotifyIME(nsWindow* aWindow,
   161                       const IMENotification& aIMENotification)
   162 {
   163 #ifdef NS_ENABLE_TSF
   164   if (IsTSFAvailable()) {
   165     switch (aIMENotification.mMessage) {
   166       case NOTIFY_IME_OF_SELECTION_CHANGE:
   167         return nsTextStore::OnSelectionChange();
   168       case NOTIFY_IME_OF_TEXT_CHANGE:
   169         return nsTextStore::OnTextChange(aIMENotification);
   170       case NOTIFY_IME_OF_FOCUS:
   171         return nsTextStore::OnFocusChange(true, aWindow,
   172                  aWindow->GetInputContext().mIMEState.mEnabled);
   173       case NOTIFY_IME_OF_BLUR:
   174         return nsTextStore::OnFocusChange(false, aWindow,
   175                  aWindow->GetInputContext().mIMEState.mEnabled);
   176       case REQUEST_TO_COMMIT_COMPOSITION:
   177         if (nsTextStore::IsComposingOn(aWindow)) {
   178           nsTextStore::CommitComposition(false);
   179         }
   180         return NS_OK;
   181       case REQUEST_TO_CANCEL_COMPOSITION:
   182         if (nsTextStore::IsComposingOn(aWindow)) {
   183           nsTextStore::CommitComposition(true);
   184         }
   185         return NS_OK;
   186       case NOTIFY_IME_OF_POSITION_CHANGE:
   187         return nsTextStore::OnLayoutChange();
   188       default:
   189         return NS_ERROR_NOT_IMPLEMENTED;
   190     }
   191   }
   192 #endif //NS_ENABLE_TSF
   194   switch (aIMENotification.mMessage) {
   195     case REQUEST_TO_COMMIT_COMPOSITION:
   196       nsIMM32Handler::CommitComposition(aWindow);
   197       return NS_OK;
   198     case REQUEST_TO_CANCEL_COMPOSITION:
   199       nsIMM32Handler::CancelComposition(aWindow);
   200       return NS_OK;
   201     case NOTIFY_IME_OF_POSITION_CHANGE:
   202     case NOTIFY_IME_OF_COMPOSITION_UPDATE:
   203       nsIMM32Handler::OnUpdateComposition(aWindow);
   204       return NS_OK;
   205 #ifdef NS_ENABLE_TSF
   206     case NOTIFY_IME_OF_BLUR:
   207       // If a plugin gets focus while TSF has focus, we need to notify TSF of
   208       // the blur.
   209       if (nsTextStore::ThinksHavingFocus()) {
   210         return nsTextStore::OnFocusChange(false, aWindow,
   211                  aWindow->GetInputContext().mIMEState.mEnabled);
   212       }
   213       return NS_ERROR_NOT_IMPLEMENTED;
   214 #endif //NS_ENABLE_TSF
   215     default:
   216       return NS_ERROR_NOT_IMPLEMENTED;
   217   }
   218 }
   220 // static
   221 nsIMEUpdatePreference
   222 IMEHandler::GetUpdatePreference()
   223 {
   224 #ifdef NS_ENABLE_TSF
   225   if (IsTSFAvailable()) {
   226     return nsTextStore::GetIMEUpdatePreference();
   227   }
   228 #endif //NS_ENABLE_TSF
   230   return nsIMM32Handler::GetIMEUpdatePreference();
   231 }
   233 // static
   234 bool
   235 IMEHandler::GetOpenState(nsWindow* aWindow)
   236 {
   237 #ifdef NS_ENABLE_TSF
   238   if (IsTSFAvailable()) {
   239     return nsTextStore::GetIMEOpenState();
   240   }
   241 #endif //NS_ENABLE_TSF
   243   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   244   return IMEContext.GetOpenState();
   245 }
   247 // static
   248 void
   249 IMEHandler::OnDestroyWindow(nsWindow* aWindow)
   250 {
   251 #ifdef NS_ENABLE_TSF
   252   // We need to do nothing here for TSF. Just restore the default context
   253   // if it's been disassociated.
   254   if (!sIsInTSFMode) {
   255     // MSDN says we need to set IS_DEFAULT to avoid memory leak when we use
   256     // SetInputScopes API. Use an empty string to do this.
   257     SetInputScopeForIMM32(aWindow, EmptyString());
   258   }
   259 #endif // #ifdef NS_ENABLE_TSF
   260   AssociateIMEContext(aWindow, true);
   261 }
   263 // static
   264 void
   265 IMEHandler::SetInputContext(nsWindow* aWindow,
   266                             InputContext& aInputContext,
   267                             const InputContextAction& aAction)
   268 {
   269   // FYI: If there is no composition, this call will do nothing.
   270   NotifyIME(aWindow, IMENotification(REQUEST_TO_COMMIT_COMPOSITION));
   272   const InputContext& oldInputContext = aWindow->GetInputContext();
   274   // Assume that SetInputContext() is called only when aWindow has focus.
   275   sPluginHasFocus = (aInputContext.mIMEState.mEnabled == IMEState::PLUGIN);
   277   bool enable = WinUtils::IsIMEEnabled(aInputContext);
   278   bool adjustOpenState = (enable &&
   279     aInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE);
   280   bool open = (adjustOpenState &&
   281     aInputContext.mIMEState.mOpen == IMEState::OPEN);
   283   aInputContext.mNativeIMEContext = nullptr;
   285 #ifdef NS_ENABLE_TSF
   286   // Note that even while a plugin has focus, we need to notify TSF of that.
   287   if (sIsInTSFMode) {
   288     nsTextStore::SetInputContext(aWindow, aInputContext, aAction);
   289     if (IsTSFAvailable()) {
   290       aInputContext.mNativeIMEContext = nsTextStore::GetTextStore();
   291       if (sIsIMMEnabled) {
   292         // Associate IME context for IMM-IMEs.
   293         AssociateIMEContext(aWindow, enable);
   294       } else if (oldInputContext.mIMEState.mEnabled == IMEState::PLUGIN) {
   295         // Disassociate the IME context from the window when plugin loses focus
   296         // in pure TSF mode.
   297         AssociateIMEContext(aWindow, false);
   298       }
   299       if (adjustOpenState) {
   300         nsTextStore::SetIMEOpenState(open);
   301       }
   302       return;
   303     }
   304   } else {
   305     // Set at least InputScope even when TextStore is not available.
   306     SetInputScopeForIMM32(aWindow, aInputContext.mHTMLInputType);
   307   }
   308 #endif // #ifdef NS_ENABLE_TSF
   310   AssociateIMEContext(aWindow, enable);
   312   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   313   if (adjustOpenState) {
   314     IMEContext.SetOpenState(open);
   315   }
   317   if (aInputContext.mNativeIMEContext) {
   318     return;
   319   }
   321   // The old InputContext must store the default IMC or old TextStore.
   322   // When IME context is disassociated from the window, use it.
   323   aInputContext.mNativeIMEContext = enable ?
   324     static_cast<void*>(IMEContext.get()) : oldInputContext.mNativeIMEContext;
   325 }
   327 // static
   328 void
   329 IMEHandler::AssociateIMEContext(nsWindow* aWindow, bool aEnable)
   330 {
   331   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   332   if (aEnable) {
   333     IMEContext.AssociateDefaultContext();
   334     return;
   335   }
   336   // Don't disassociate the context after the window is destroyed.
   337   if (aWindow->Destroyed()) {
   338     return;
   339   }
   340   IMEContext.Disassociate();
   341 }
   343 // static
   344 void
   345 IMEHandler::InitInputContext(nsWindow* aWindow, InputContext& aInputContext)
   346 {
   347   // For a11y, the default enabled state should be 'enabled'.
   348   aInputContext.mIMEState.mEnabled = IMEState::ENABLED;
   350 #ifdef NS_ENABLE_TSF
   351   if (sIsInTSFMode) {
   352     nsTextStore::SetInputContext(aWindow, aInputContext,
   353       InputContextAction(InputContextAction::CAUSE_UNKNOWN,
   354                          InputContextAction::GOT_FOCUS));
   355     aInputContext.mNativeIMEContext = nsTextStore::GetTextStore();
   356     MOZ_ASSERT(aInputContext.mNativeIMEContext);
   357     // IME context isn't necessary in pure TSF mode.
   358     if (!sIsIMMEnabled) {
   359       AssociateIMEContext(aWindow, false);
   360     }
   361     return;
   362   }
   363 #endif // #ifdef NS_ENABLE_TSF
   365   // NOTE: mNativeIMEContext may be null if IMM module isn't installed.
   366   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   367   aInputContext.mNativeIMEContext = static_cast<void*>(IMEContext.get());
   368   MOZ_ASSERT(aInputContext.mNativeIMEContext || !CurrentKeyboardLayoutHasIME());
   369   // If no IME context is available, we should set the widget's pointer since
   370   // nullptr indicates there is only one context per process on the platform.
   371   if (!aInputContext.mNativeIMEContext) {
   372     aInputContext.mNativeIMEContext = static_cast<void*>(aWindow);
   373   }
   374 }
   376 #ifdef DEBUG
   377 // static
   378 bool
   379 IMEHandler::CurrentKeyboardLayoutHasIME()
   380 {
   381 #ifdef NS_ENABLE_TSF
   382   if (sIsInTSFMode) {
   383     return nsTextStore::CurrentKeyboardLayoutHasIME();
   384   }
   385 #endif // #ifdef NS_ENABLE_TSF
   387   return nsIMM32Handler::IsIMEAvailable();
   388 }
   389 #endif // #ifdef DEBUG
   391 // static
   392 void
   393 IMEHandler::SetInputScopeForIMM32(nsWindow* aWindow,
   394                                   const nsAString& aHTMLInputType)
   395 {
   396   if (sIsInTSFMode || !sSetInputScopes || aWindow->Destroyed()) {
   397     return;
   398   }
   399   UINT arraySize = 0;
   400   const InputScope* scopes = nullptr;
   401   // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
   402   if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) {
   403     static const InputScope inputScopes[] = { IS_DEFAULT };
   404     scopes = &inputScopes[0];
   405     arraySize = ArrayLength(inputScopes);
   406   } else if (aHTMLInputType.EqualsLiteral("url")) {
   407     static const InputScope inputScopes[] = { IS_URL };
   408     scopes = &inputScopes[0];
   409     arraySize = ArrayLength(inputScopes);
   410   } else if (aHTMLInputType.EqualsLiteral("search")) {
   411     static const InputScope inputScopes[] = { IS_SEARCH };
   412     scopes = &inputScopes[0];
   413     arraySize = ArrayLength(inputScopes);
   414   } else if (aHTMLInputType.EqualsLiteral("email")) {
   415     static const InputScope inputScopes[] = { IS_EMAIL_SMTPEMAILADDRESS };
   416     scopes = &inputScopes[0];
   417     arraySize = ArrayLength(inputScopes);
   418   } else if (aHTMLInputType.EqualsLiteral("password")) {
   419     static const InputScope inputScopes[] = { IS_PASSWORD };
   420     scopes = &inputScopes[0];
   421     arraySize = ArrayLength(inputScopes);
   422   } else if (aHTMLInputType.EqualsLiteral("datetime") ||
   423              aHTMLInputType.EqualsLiteral("datetime-local")) {
   424     static const InputScope inputScopes[] = {
   425       IS_DATE_FULLDATE, IS_TIME_FULLTIME };
   426     scopes = &inputScopes[0];
   427     arraySize = ArrayLength(inputScopes);
   428   } else if (aHTMLInputType.EqualsLiteral("date") ||
   429              aHTMLInputType.EqualsLiteral("month") ||
   430              aHTMLInputType.EqualsLiteral("week")) {
   431     static const InputScope inputScopes[] = { IS_DATE_FULLDATE };
   432     scopes = &inputScopes[0];
   433     arraySize = ArrayLength(inputScopes);
   434   } else if (aHTMLInputType.EqualsLiteral("time")) {
   435     static const InputScope inputScopes[] = { IS_TIME_FULLTIME };
   436     scopes = &inputScopes[0];
   437     arraySize = ArrayLength(inputScopes);
   438   } else if (aHTMLInputType.EqualsLiteral("tel")) {
   439     static const InputScope inputScopes[] = {
   440       IS_TELEPHONE_FULLTELEPHONENUMBER, IS_TELEPHONE_LOCALNUMBER };
   441     scopes = &inputScopes[0];
   442     arraySize = ArrayLength(inputScopes);
   443   } else if (aHTMLInputType.EqualsLiteral("number")) {
   444     static const InputScope inputScopes[] = { IS_NUMBER };
   445     scopes = &inputScopes[0];
   446     arraySize = ArrayLength(inputScopes);
   447   }
   448   if (scopes && arraySize > 0) {
   449     sSetInputScopes(aWindow->GetWindowHandle(), scopes, arraySize, nullptr, 0,
   450                     nullptr, nullptr);
   451   }
   452 }
   454 } // namespace widget
   455 } // namespace mozilla

mercurial