Bug 866736 Use SetInputScopes to support InputScope on IMM32 r=masayuki
authorYohei Yukawa <yukawa@google.com>
Tue, 30 Apr 2013 00:41:45 +0900
changeset 130420 40437cd7395bf12bab86634513e6e245c6030911
parent 130419 de9d4e1505a9322a4ec739fa58dc62f35bdc114f
child 130421 3d939baa396b3275931cf17f7149db4b0ebdad49
push id24618
push userryanvm@gmail.com
push dateWed, 01 May 2013 14:49:43 +0000
treeherdermozilla-central@4ff1e574e509 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs866736
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 866736 Use SetInputScopes to support InputScope on IMM32 r=masayuki
widget/windows/WinIMEHandler.cpp
widget/windows/WinIMEHandler.h
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -18,25 +18,37 @@ namespace widget {
 
 /******************************************************************************
  * IMEHandler
  ******************************************************************************/
 
 #ifdef NS_ENABLE_TSF
 bool IMEHandler::sIsInTSFMode = false;
 bool IMEHandler::sPluginHasFocus = false;
+IMEHandler::SetInputScopesFunc IMEHandler::sSetInputScopes = nullptr;
 #endif // #ifdef NS_ENABLE_TSF
 
 // static
 void
 IMEHandler::Initialize()
 {
 #ifdef NS_ENABLE_TSF
   nsTextStore::Initialize();
   sIsInTSFMode = nsTextStore::IsInTSFMode();
+  if (!sIsInTSFMode) {
+    // When full nsTextStore is not available, try to use SetInputScopes API
+    // to enable at least InputScope. Use GET_MODULE_HANDLE_EX_FLAG_PIN to
+    // ensure that msctf.dll will not be unloaded.
+    HMODULE module = nullptr;
+    if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"msctf.dll",
+                           &module)) {
+      sSetInputScopes = reinterpret_cast<SetInputScopesFunc>(
+        GetProcAddress(module, "SetInputScopes"));
+    }
+  }
 #endif // #ifdef NS_ENABLE_TSF
 
   nsIMM32Handler::Initialize();
 }
 
 // static
 void
 IMEHandler::Terminate()
@@ -240,18 +252,25 @@ IMEHandler::GetOpenState(nsWindow* aWind
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   return IMEContext.GetOpenState();
 }
 
 // static
 void
 IMEHandler::OnDestroyWindow(nsWindow* aWindow)
 {
+#ifdef NS_ENABLE_TSF
   // We need to do nothing here for TSF. Just restore the default context
   // if it's been disassociated.
+  if (!sIsInTSFMode) {
+    // MSDN says we need to set IS_DEFAULT to avoid memory leak when we use
+    // SetInputScopes API. Use an empty string to do this.
+    SetInputScopeForIMM32(aWindow, EmptyString());
+  }
+#endif // #ifdef NS_ENABLE_TSF
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   IMEContext.AssociateDefaultContext();
 }
 
 // static
 void
 IMEHandler::SetInputContext(nsWindow* aWindow,
                             InputContext& aInputContext,
@@ -277,16 +296,19 @@ IMEHandler::SetInputContext(nsWindow* aW
     nsTextStore::SetInputContext(aWindow, aInputContext, aAction);
     if (IsTSFAvailable()) {
       aInputContext.mNativeIMEContext = nsTextStore::GetTextStore();
       if (adjustOpenState) {
         nsTextStore::SetIMEOpenState(open);
       }
       return;
     }
+  } else {
+    // Set at least InputScope even when TextStore is not available.
+    SetInputScopeForIMM32(aWindow, aInputContext.mHTMLInputType);
   }
 #endif // #ifdef NS_ENABLE_TSF
 
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   if (enable) {
     IMEContext.AssociateDefaultContext();
     if (!aInputContext.mNativeIMEContext) {
       aInputContext.mNativeIMEContext = static_cast<void*>(IMEContext.get());
@@ -380,10 +402,73 @@ IMEHandler::IsDoingKakuteiUndo(HWND aWnd
          startCompositionMsg.lParam == 0x0 &&
          compositionMsg.wParam == 0x0 &&
          compositionMsg.lParam == 0x1BF &&
          charMsg.wParam == VK_BACK && charMsg.lParam == 0x1 &&
          startCompositionMsg.time <= compositionMsg.time &&
          compositionMsg.time <= charMsg.time;
 }
 
+// static
+void
+IMEHandler::SetInputScopeForIMM32(nsWindow* aWindow,
+                                  const nsAString& aHTMLInputType)
+{
+  if (sIsInTSFMode || !sSetInputScopes || aWindow->Destroyed()) {
+    return;
+  }
+  UINT arraySize = 0;
+  const InputScope* scopes = nullptr;
+  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
+  if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) {
+    static const InputScope inputScopes[] = { IS_DEFAULT };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("url")) {
+    static const InputScope inputScopes[] = { IS_URL };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("search")) {
+    static const InputScope inputScopes[] = { IS_SEARCH };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("email")) {
+    static const InputScope inputScopes[] = { IS_EMAIL_SMTPEMAILADDRESS };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("password")) {
+    static const InputScope inputScopes[] = { IS_PASSWORD };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("datetime") ||
+             aHTMLInputType.EqualsLiteral("datetime-local")) {
+    static const InputScope inputScopes[] = {
+      IS_DATE_FULLDATE, IS_TIME_FULLTIME };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("date") ||
+             aHTMLInputType.EqualsLiteral("month") ||
+             aHTMLInputType.EqualsLiteral("week")) {
+    static const InputScope inputScopes[] = { IS_DATE_FULLDATE };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("time")) {
+    static const InputScope inputScopes[] = { IS_TIME_FULLTIME };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("tel")) {
+    static const InputScope inputScopes[] = {
+      IS_TELEPHONE_FULLTELEPHONENUMBER, IS_TELEPHONE_LOCALNUMBER };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  } else if (aHTMLInputType.EqualsLiteral("number")) {
+    static const InputScope inputScopes[] = { IS_NUMBER };
+    scopes = &inputScopes[0];
+    arraySize = ArrayLength(inputScopes);
+  }
+  if (scopes && arraySize > 0) {
+    sSetInputScopes(aWindow->GetWindowHandle(), scopes, arraySize, nullptr, 0,
+                    nullptr, nullptr);
+  }
+}
+
 } // namespace widget
 } // namespace mozilla
--- a/widget/windows/WinIMEHandler.h
+++ b/widget/windows/WinIMEHandler.h
@@ -5,16 +5,17 @@
 
 #ifndef WinIMEHandler_h_
 #define WinIMEHandler_h_
 
 #include "nscore.h"
 #include "nsEvent.h"
 #include "nsIWidget.h"
 #include <windows.h>
+#include <inputscope.h>
 
 #define NS_WM_IMEFIRST WM_IME_SETCONTEXT
 #define NS_WM_IMELAST  WM_IME_KEYUP
 
 class nsWindow;
 
 namespace mozilla {
 namespace widget {
@@ -122,16 +123,26 @@ public:
   /**
    * Returns true when current keyboard layout has IME.  Otherwise, false.
    */
   static bool CurrentKeyboardLayoutHasIME();
 #endif // #ifdef DEBUG
 
 private:
 #ifdef NS_ENABLE_TSF
+  typedef HRESULT (WINAPI *SetInputScopesFunc)(HWND aindowHandle,
+                                               const InputScope *inputScopes,
+                                               UINT numInputScopes,
+                                               wchar_t **phrase_list,
+                                               UINT numPhraseList,
+                                               wchar_t *regExp,
+                                               wchar_t *srgs);
+  static SetInputScopesFunc sSetInputScopes;
+  static void SetInputScopeForIMM32(nsWindow* aWindow,
+                                    const nsAString& aHTMLInputType);
   static bool sIsInTSFMode;
   static bool sPluginHasFocus;
 
   static bool IsTSFAvailable() { return (sIsInTSFMode && !sPluginHasFocus); }
 #endif // #ifdef NS_ENABLE_TSF
 };
 
 } // namespace widget