Bug 1456294 - Hook ImmAssociateContextEx. r=masayuki, a=IanN CLOSED TREE DONTBUILD SEAMONKEY_2_49_ESR_RELBRANCH
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Thu, 17 May 2018 14:47:55 +0900
branchSEAMONKEY_2_49_ESR_RELBRANCH
changeset 357525 9a01f768f67ede0c0e87f7d109ed7d493db9f013
parent 357524 24913c4cc9684dc88c1418f01c024644c642ebf0
child 357526 b8f3bfa08918b8ad605696f81e359b2f183ae443
push id7834
push userfrgrahl@gmx.net
push dateSun, 13 Jan 2019 12:17:02 +0000
treeherdermozilla-esr52@6e4ad8a8f2e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, IanN
bugs1456294
milestone52.9.1
Bug 1456294 - Hook ImmAssociateContextEx. r=masayuki, a=IanN CLOSED TREE DONTBUILD mozilla-esr52 SEAMONKEY_2_49_ESR_RELBRANCH
dom/ipc/PBrowser.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
dom/plugins/ipc/PPluginInstance.ipdl
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginInstanceParent.h
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/nsIWidget.h
widget/windows/WinUtils.cpp
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -322,16 +322,21 @@ parent:
     nested(inside_cpow) async SetPluginFocused(bool aFocused);
 
     /**
      * Set IME candidate window by windowless plugin if plugin has focus.
      */
     async SetCandidateWindowForPlugin(CandidateWindowPosition aPosition);
 
     /**
+     * Enable or Disable IME by windowless plugin if plugin has focus.
+     */
+    async EnableIMEForPlugin(bool aEnable);
+
+    /**
      * Notifies the parent process of native key event data received in a
      * plugin process directly.
      *
      * aKeyEventData    The native key event data.  The actual type copied into
      *                  NativeEventData depending on the caller.  Please check
      *                  PluginInstanceChild.
      */
     nested(inside_cpow) async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2165,30 +2165,41 @@ TabParent::RecvSetPluginFocused(const bo
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return true;
   }
   widget->SetPluginFocused((bool&)aFocused);
   return true;
 }
 
- bool
+bool
 TabParent::RecvSetCandidateWindowForPlugin(
              const CandidateWindowPosition& aPosition)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return true;
   }
 
   widget->SetCandidateWindowForPlugin(aPosition);
   return true;
 }
 
 bool
+TabParent::RecvEnableIMEForPlugin(const bool& aEnable)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget) {
+    return true;
+  }
+  widget->EnableIMEForPlugin(aEnable);
+  return true;
+}
+
+bool
 TabParent::RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return true;
   }
 
   widget->DefaultProcOfPluginEvent(aEvent);
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -248,16 +248,18 @@ public:
                      const int32_t& aPanelX,
                      const int32_t& aPanelY,
                      nsString* aCommitted) override;
 
   virtual bool RecvSetPluginFocused(const bool& aFocused) override;
 
   virtual bool RecvSetCandidateWindowForPlugin(
                  const widget::CandidateWindowPosition& aPosition) override;
+  virtual bool
+  RecvEnableIMEForPlugin(const bool& aEnable) override;
 
   virtual bool
   RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent) override;
 
   virtual bool RecvGetInputContext(int32_t* aIMEEnabled,
                                    int32_t* aIMEOpen) override;
 
   virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -972,16 +972,31 @@ nsPluginInstanceOwner::RequestCommitOrCa
   if (aCommitted) {
     widget->NotifyIME(widget::REQUEST_TO_COMMIT_COMPOSITION);
   } else {
     widget->NotifyIME(widget::REQUEST_TO_CANCEL_COMPOSITION);
   }
   return true;
 }
 
+bool
+nsPluginInstanceOwner::EnableIME(bool aEnable)
+{
+  nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+  if (!widget) {
+    widget = GetRootWidgetForPluginFrame(mPluginFrame);
+    if (NS_WARN_IF(!widget)) {
+      return false;
+    }
+  }
+
+  widget->EnableIMEForPlugin(aEnable);
+  return true;
+}
+
 #endif // #ifdef XP_WIN
 
 void
 nsPluginInstanceOwner::HandledWindowedPluginKeyEvent(
                          const NativeEventData& aKeyEventData,
                          bool aIsConsumed)
 {
   if (NS_WARN_IF(!mInstance)) {
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -273,16 +273,17 @@ public:
   void NotifyHostCreateWidget();
   void NotifyDestroyPending();
 
   bool GetCompositionString(uint32_t aIndex, nsTArray<uint8_t>* aString,
                             int32_t* aLength);
   bool SetCandidateWindow(
            const mozilla::widget::CandidateWindowPosition& aPosition);
   bool RequestCommitOrCancel(bool aCommitted);
+  bool EnableIME(bool aEnable);
 
   // See nsIKeyEventInPluginCallback
   virtual void HandledWindowedPluginKeyEvent(
                  const mozilla::NativeEventData& aKeyEventData,
                  bool aIsConsumed) override;
 
   /**
    * OnWindowedPluginKeyEvent() is called when the plugin process receives
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -277,16 +277,21 @@ parent:
   sync GetCompositionString(uint32_t aType)
                             returns (uint8_t[] aDist, int32_t aLength);
   // Set candidate window position.
   //
   // @param aPosition  position information of candidate window
   async SetCandidateWindow(CandidateWindowPosition aPosition);
   async RequestCommitOrCancel(bool aCommitted);
 
+  // Control IME can enable or disable
+  //
+  // @param aEanble  whether IME is enabled or disabled
+  async EnableIME(bool aEnable);
+
   // Notifies the parent process of a plugin instance receiving key event
   // directly.
   //
   // @param aKeyEventData       The native key event which will be sent to
   //                            plugin from native event handler.
   async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData);
 
 both:
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -71,32 +71,27 @@ typedef BOOL (WINAPI *User32TrackPopupMe
                                             int y,
                                             int nReserved,
                                             HWND hWnd,
                                             CONST RECT *prcRect);
 static WindowsDllInterceptor sUser32Intercept;
 static HWND sWinlessPopupSurrogateHWND = nullptr;
 static User32TrackPopupMenu sUser32TrackPopupMenuStub = nullptr;
 
-typedef HIMC (WINAPI *Imm32ImmGetContext)(HWND hWND);
-typedef BOOL (WINAPI *Imm32ImmReleaseContext)(HWND hWND, HIMC hIMC);
-typedef LONG (WINAPI *Imm32ImmGetCompositionString)(HIMC hIMC,
-                                                    DWORD dwIndex,
-                                                    LPVOID lpBuf,
-                                                    DWORD dwBufLen);
-typedef BOOL (WINAPI *Imm32ImmSetCandidateWindow)(HIMC hIMC,
-                                                  LPCANDIDATEFORM lpCandidate);
-typedef BOOL (WINAPI *Imm32ImmNotifyIME)(HIMC hIMC, DWORD dwAction,
-                                        DWORD dwIndex, DWORD dwValue);
 static WindowsDllInterceptor sImm32Intercept;
-static Imm32ImmGetContext sImm32ImmGetContextStub = nullptr;
-static Imm32ImmReleaseContext sImm32ImmReleaseContextStub = nullptr;
-static Imm32ImmGetCompositionString sImm32ImmGetCompositionStringStub = nullptr;
-static Imm32ImmSetCandidateWindow sImm32ImmSetCandidateWindowStub = nullptr;
-static Imm32ImmNotifyIME sImm32ImmNotifyIME = nullptr;
+static decltype(ImmGetContext)* sImm32ImmGetContextStub = nullptr;
+static decltype(ImmReleaseContext)* sImm32ImmReleaseContextStub = nullptr;
+static decltype(ImmGetCompositionStringW)* sImm32ImmGetCompositionStringStub =
+                                             nullptr;
+static decltype(ImmSetCandidateWindow)* sImm32ImmSetCandidateWindowStub =
+                                          nullptr;
+static decltype(ImmNotifyIME)* sImm32ImmNotifyIME = nullptr;
+static decltype(ImmAssociateContextEx)* sImm32ImmAssociateContextExStub =
+                                          nullptr;
+
 static PluginInstanceChild* sCurrentPluginInstance = nullptr;
 static const HIMC sHookIMC = (const HIMC)0xefefefef;
 
 using mozilla::gfx::SharedDIB;
 
 // Flash WM_USER message delay time for PostDelayedTask. Borrowed
 // from Chromium's web plugin delegate src. See 'flash msg throttling
 // helpers' section for details.
@@ -186,16 +181,17 @@ PluginInstanceChild::PluginInstanceChild
     , mSurfaceType(gfxSurfaceType::Max)
     , mPendingPluginCall(false)
     , mDoAlphaExtraction(false)
     , mHasPainted(false)
     , mSurfaceDifferenceRect(0,0,0,0)
     , mDestroyed(false)
 #ifdef XP_WIN
     , mLastKeyEventConsumed(false)
+    , mLastEnableIMEState(true)
 #endif // #ifdef XP_WIN
     , mStackDepth(0)
 {
     memset(&mWindow, 0, sizeof(mWindow));
     mWindow.type = NPWindowTypeWindow;
     mData.ndata = (void*) this;
     mData.pdata = nullptr;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
@@ -220,18 +216,18 @@ PluginInstanceChild::PluginInstanceChild
 PluginInstanceChild::~PluginInstanceChild()
 {
 #if defined(OS_WIN)
     NS_ASSERTION(!mPluginWindowHWND, "Destroying PluginInstanceChild without NPP_Destroy?");
     if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
         ClearUnityHooks();
     }
     // In the event that we registered for audio device changes, stop.
-    PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
-    if (chromeInstance) {
+    PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+    if (chromeInstance) {
       NPError rv = chromeInstance->PluginRequiresAudioDeviceChanges(this, false);
     }
 #endif
 #if defined(MOZ_WIDGET_COCOA)
     if (mShColorSpace) {
         ::CGColorSpaceRelease(mShColorSpace);
     }
     if (mShContext) {
@@ -692,18 +688,18 @@ PluginInstanceChild::NPN_SetValue(NPPVar
 #ifdef XP_WIN
     case NPPVpluginRequiresAudioDeviceChanges: {
       // Many other NPN_SetValue variables are forwarded to our
       // PluginInstanceParent, which runs on a content process.  We
       // instead forward this message to the PluginModuleParent, which runs
       // on the chrome process.  This is because our audio
       // API calls should run the chrome proc, not content.
       NPError rv = NPERR_GENERIC_ERROR;
-      PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
-      if (chromeInstance) {
+      PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+      if (chromeInstance) {
         rv = chromeInstance->PluginRequiresAudioDeviceChanges(this,
                                               (NPBool)(intptr_t)aValue);
       }
       return rv;
     }
 #endif
 
     default:
@@ -2422,16 +2418,40 @@ PluginInstanceChild::ImmNotifyIME(HIMC a
         (aIndex != CPS_COMPLETE && aIndex != CPS_CANCEL)) {
         return FALSE;
     }
 
     sCurrentPluginInstance->SendRequestCommitOrCancel(aAction == CPS_COMPLETE);
     return TRUE;
 }
 
+// static
+BOOL
+PluginInstanceChild::ImmAssociateContextExProc(HWND hWND, HIMC hImc,
+                                               DWORD dwFlags)
+{
+    PluginInstanceChild* self = sCurrentPluginInstance;
+    if (!self) {
+        // If ImmAssociateContextEx calls unexpected window message,
+        // we can use child instance object from window property if available.
+        self = reinterpret_cast<PluginInstanceChild*>(
+                   GetProp(hWND, kFlashThrottleProperty));
+        NS_WARNING_ASSERTION(self, "Cannot find PluginInstanceChild");
+    }
+
+    // HIMC is always nullptr on Flash's windowless
+    if (!hImc && self) {
+        // Store the last IME state since Flash may call ImmAssociateContextEx
+        // before taking focus.
+        self->mLastEnableIMEState = !!(dwFlags & IACE_DEFAULT);
+        self->SendEnableIME(self->mLastEnableIMEState);
+    }
+    return sImm32ImmAssociateContextExStub(hWND, hImc, dwFlags);
+}
+
 void
 PluginInstanceChild::InitImm32Hook()
 {
     if (!(GetQuirks() & QUIRK_WINLESS_HOOK_IME)) {
         return;
     }
 
     if (sImm32ImmGetContextStub) {
@@ -2456,16 +2476,20 @@ PluginInstanceChild::InitImm32Hook()
     sImm32Intercept.AddHook(
         "ImmSetCandidateWindow",
         reinterpret_cast<intptr_t>(ImmSetCandidateWindowProc),
         (void**)&sImm32ImmSetCandidateWindowStub);
     sImm32Intercept.AddHook(
         "ImmNotifyIME",
         reinterpret_cast<intptr_t>(ImmNotifyIME),
         (void**)&sImm32ImmNotifyIME);
+    sImm32Intercept.AddHook(
+        "ImmAssociateContextEx",
+        reinterpret_cast<intptr_t>(ImmAssociateContextExProc),
+        (void**)&sImm32ImmAssociateContextExStub);
 }
 
 void
 PluginInstanceChild::DestroyWinlessPopupSurrogate()
 {
     if (mWinlessPopupSurrogateHWND)
         DestroyWindow(mWinlessPopupSurrogateHWND);
     mWinlessPopupSurrogateHWND = nullptr;
@@ -2494,31 +2518,53 @@ PluginInstanceChild::WinlessHandleEvent(
       // A little trick scrounged from chromium's code - set the focus
       // to our surrogate parent so keyboard nav events go to the menu. 
       focusHwnd = SetFocus(mWinlessPopupSurrogateHWND);
     }
 
     AutoRestore<PluginInstanceChild *> pluginInstance(sCurrentPluginInstance);
     if (event.event == WM_IME_STARTCOMPOSITION ||
         event.event == WM_IME_COMPOSITION ||
+        event.event == WM_LBUTTONDOWN ||
         event.event == WM_KILLFOCUS) {
       sCurrentPluginInstance = this;
     }
 
     MessageLoop* loop = MessageLoop::current();
     AutoRestore<bool> modalLoop(loop->os_modal_loop());
 
     handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
 
     sWinlessPopupSurrogateHWND = nullptr;
 
     if (IsWindow(focusHwnd)) {
       SetFocus(focusHwnd);
     }
 
+    // This is hack of Flash's behaviour.
+    //
+    // When moving focus from chrome to plugin by mouse click, Gecko sends
+    // mouse message (such as WM_LBUTTONDOWN etc) at first, then sends
+    // WM_SETFOCUS. But Flash will call ImmAssociateContextEx on WM_LBUTTONDOWN
+    // even if it doesn't receive WM_SETFOCUS.
+    //
+    // In this situation, after sending mouse message, content process will be
+    // activated and set input context with PLUGIN.  So after activating
+    // content process, we have to set current IME state again.
+
+    if (event.event == WM_SETFOCUS) {
+        // When focus is changed from chrome process to plugin process,
+        // Flash may call ImmAssociateContextEx before receiving WM_SETFOCUS.
+        SendEnableIME(mLastEnableIMEState);
+    } else if (event.event == WM_KILLFOCUS) {
+        // Flash always calls ImmAssociateContextEx by taking focus.
+        // Although this flag doesn't have to be reset, I add it for safety.
+        mLastEnableIMEState = true;
+    }
+
     return handled;
 }
 
 /* flash msg throttling helpers */
 
 // Flash has the unfortunate habit of flooding dispatch loops with custom
 // windowing events they use for timing. We throttle these by dropping the
 // delivery priority below any other event, including pending ipc io
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -366,16 +366,18 @@ private:
     static HIMC WINAPI ImmGetContextProc(HWND aWND);
     static BOOL WINAPI ImmReleaseContextProc(HWND aWND, HIMC aIMC);
     static LONG WINAPI ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex,
                                                    LPVOID aBuf, DWORD aLen);
     static BOOL WINAPI ImmSetCandidateWindowProc(HIMC hIMC,
                                                  LPCANDIDATEFORM plCandidate);
     static BOOL WINAPI ImmNotifyIME(HIMC aIMC, DWORD aAction, DWORD aIndex,
                                     DWORD aValue);
+    static BOOL WINAPI ImmAssociateContextExProc(HWND hWnd, HIMC aIMC,
+                                                 DWORD dwFlags);
 
     class FlashThrottleAsyncMsg : public ChildAsyncCall
     {
       public:
         FlashThrottleAsyncMsg();
         FlashThrottleAsyncMsg(PluginInstanceChild* aInst, 
                               HWND aWnd, UINT aMsg,
                               WPARAM aWParam, LPARAM aLParam,
@@ -676,16 +678,20 @@ private:
     bool mDestroyed;
 
 #ifdef XP_WIN
     // WM_*CHAR messages are never consumed by chrome process's widget.
     // So, if preceding keydown or keyup event is consumed by reserved
     // shortcut key in the chrome process, we shouldn't send the following
     // WM_*CHAR messages to the plugin.
     bool mLastKeyEventConsumed;
+
+    // Store the last IME state by ImmAssociateContextEx.  This will reset by
+    // WM_KILLFOCUS;
+    bool mLastEnableIMEState;
 #endif // #ifdef XP_WIN
 
     // While IME in the process has composition, this is set to true.
     // Otherwise, false.
     static bool sIsIMEComposing;
 
     // A counter is incremented by AutoStackHelper to indicate that there is an
     // active plugin call which should be preventing shutdown.
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -2450,16 +2450,30 @@ PluginInstanceParent::RecvRequestCommitO
     nsPluginInstanceOwner* owner = GetOwner();
     if (owner) {
         owner->RequestCommitOrCancel(aCommitted);
     }
 #endif
     return true;
 }
 
+bool
+PluginInstanceParent::RecvEnableIME(const bool& aEnable)
+{
+#if defined(OS_WIN)
+    nsPluginInstanceOwner* owner = GetOwner();
+    if (owner) {
+        owner->EnableIME(aEnable);
+    }
+#else
+    MOZ_CRASH("Not reachable");
+#endif
+    return true;
+}
+
 nsresult
 PluginInstanceParent::HandledWindowedPluginKeyEvent(
                         const NativeEventData& aKeyEventData,
                         bool aIsConsumed)
 {
     if (NS_WARN_IF(!SendHandledWindowedPluginKeyEvent(aKeyEventData,
                                                       aIsConsumed))) {
         return NS_ERROR_FAILURE;
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -352,16 +352,17 @@ public:
     RecvGetCompositionString(const uint32_t& aIndex,
                              nsTArray<uint8_t>* aBuffer,
                              int32_t* aLength) override;
     virtual bool
     RecvSetCandidateWindow(
         const mozilla::widget::CandidateWindowPosition& aPosition) override;
     virtual bool
     RecvRequestCommitOrCancel(const bool& aCommitted) override;
+    virtual bool RecvEnableIME(const bool& aEnable) override;
 
     // for reserved shortcut key handling with windowed plugin on Windows
     nsresult HandledWindowedPluginKeyEvent(
       const mozilla::NativeEventData& aKeyEventData,
       bool aIsConsumed);
     virtual bool
     RecvOnWindowedPluginKeyEvent(
       const mozilla::NativeEventData& aKeyEventData) override;
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -708,16 +708,25 @@ PuppetWidget::SetPluginFocused(bool& aFo
 void
 PuppetWidget::DefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent)
 {
   if (!mTabChild) {
     return;
   }
   mTabChild->SendDefaultProcOfPluginEvent(aEvent);
 }
+ 
+// When this widget caches input context and currently managed by
+// IMEStateManager, the cache is valid.
+bool
+PuppetWidget::HaveValidInputContextCache() const
+{
+  return (mInputContext.mIMEState.mEnabled != IMEState::UNKNOWN &&
+          IMEStateManager::GetWidgetForActiveInputContext() == this);
+}
 
 NS_IMETHODIMP_(void)
 PuppetWidget::SetInputContext(const InputContext& aContext,
                               const InputContextAction& aAction)
 {
   mInputContext = aContext;
   // Any widget instances cannot cache IME open state because IME open state
   // can be changed by user but native IME may not notify us of changing the
@@ -746,21 +755,19 @@ PuppetWidget::GetInputContext()
 {
 #ifndef MOZ_CROSS_PROCESS_IME
   return InputContext();
 #endif
 
   // XXX Currently, we don't support retrieving IME open state from child
   //     process.
 
-  // When this widget caches input context and currently managed by
-  // IMEStateManager, the cache is valid.  Only in this case, we can
-  // avoid to use synchronous IPC.
-  if (mInputContext.mIMEState.mEnabled != IMEState::UNKNOWN &&
-      IMEStateManager::GetWidgetForActiveInputContext() == this) {
+  // If the cache of input context is valid, we can avoid to use synchronous
+  // IPC.
+  if (HaveValidInputContextCache()) {
     return mInputContext;
   }
 
   NS_WARNING("PuppetWidget::GetInputContext() needs to retrieve it with IPC");
 
   // Don't cache InputContext here because this process isn't managing IME
   // state of the chrome widget.  So, we cannot modify mInputContext when
   // chrome widget is set to new context.
@@ -1480,16 +1487,35 @@ PuppetWidget::SetCandidateWindowForPlugi
   if (!mTabChild) {
     return;
   }
 
   mTabChild->SendSetCandidateWindowForPlugin(aPosition);
 }
 
 void
+PuppetWidget::EnableIMEForPlugin(bool aEnable)
+{
+  if (!mTabChild) {
+    return;
+  }
+
+  // If current IME state isn't plugin, we ignore this call.
+  if (NS_WARN_IF(HaveValidInputContextCache() &&
+                 mInputContext.mIMEState.mEnabled != IMEState::UNKNOWN &&
+                 mInputContext.mIMEState.mEnabled != IMEState::PLUGIN)) {
+    return;
+  }
+
+  // We don't have valid state in cache or state is plugin, so delegate to
+  // chrome process.
+  mTabChild->SendEnableIMEForPlugin(aEnable);
+}
+
+void
 PuppetWidget::ZoomToRect(const uint32_t& aPresShellId,
                          const FrameMetrics::ViewID& aViewId,
                          const CSSRect& aRect,
                          const uint32_t& aFlags)
 {
   if (!mTabChild) {
     return;
   }
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -260,16 +260,17 @@ public:
                                             nsIObserver* aObserver) override;
   virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver) override;
   virtual uint32_t GetMaxTouchPoints() const override;
 
   virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override;
 
   virtual void SetCandidateWindowForPlugin(
                  const CandidateWindowPosition& aPosition) override;
+  virtual void EnableIMEForPlugin(bool aEnable) override;
 
   virtual void ZoomToRect(const uint32_t& aPresShellId,
                           const FrameMetrics::ViewID& aViewId,
                           const CSSRect& aRect,
                           const uint32_t& aFlags) override;
 
   virtual bool HasPendingInputEvent() override;
 
@@ -306,16 +307,20 @@ private:
   bool CacheCompositionRects(uint32_t& aStartOffset,
                              nsTArray<LayoutDeviceIntRect>& aRectArray,
                              uint32_t& aTargetCauseOffset);
   bool GetCaretRect(LayoutDeviceIntRect& aCaretRect, uint32_t aCaretOffset);
   uint32_t GetCaretOffset();
 
   nsIWidgetListener* GetCurrentWidgetListener();
 
+  // When this widget caches input context and currently managed by
+  // IMEStateManager, the cache is valid.
+  bool HaveValidInputContextCache() const;
+
   class PaintTask : public Runnable {
   public:
     NS_DECL_NSIRUNNABLE
     explicit PaintTask(PuppetWidget* widget) : mWidget(widget) {}
     void Revoke() { mWidget = nullptr; }
   private:
     PuppetWidget* mWidget;
   };
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1783,16 +1783,23 @@ public:
 
     /**
      * Handle default action when PluginEvent isn't handled
      */
     virtual void DefaultProcOfPluginEvent(
                    const mozilla::WidgetPluginEvent& aEvent) = 0;
 
     /*
+     * Enable or Disable IME by windowless plugin.
+     */
+    virtual void EnableIMEForPlugin(bool aEnable)
+    {
+    }
+
+    /*
      * Notifies the input context changes.
      */
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction) = 0;
 
     /*
      * Get current input context.
      */
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -1828,17 +1828,24 @@ WinUtils::ToIntRect(const RECT& aRect)
                    aRect.right - aRect.left,
                    aRect.bottom - aRect.top);
 }
 
 /* static */
 bool
 WinUtils::IsIMEEnabled(const InputContext& aInputContext)
 {
-  return IsIMEEnabled(aInputContext.mIMEState.mEnabled);
+  if (!IsIMEEnabled(aInputContext.mIMEState.mEnabled)) {
+    return false;
+  }
+  if (aInputContext.mIMEState.mEnabled == IMEState::PLUGIN &&
+      aInputContext.mHTMLInputType.EqualsLiteral("password")) {
+    return false;
+  }
+  return true;
 }
 
 /* static */
 bool
 WinUtils::IsIMEEnabled(IMEState::Enabled aIMEState)
 {
   return (aIMEState == IMEState::ENABLED ||
           aIMEState == IMEState::PLUGIN);
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -8066,16 +8066,33 @@ nsWindow::DefaultProcOfPluginEvent(const
 
   // For WM_IME_*COMPOSITION
   IMEHandler::DefaultProcOfPluginEvent(this, pPluginEvent);
 
   CallWindowProcW(GetPrevWindowProc(), mWnd, pPluginEvent->event,
                   pPluginEvent->wParam, pPluginEvent->lParam);
 }
 
+void
+nsWindow::EnableIMEForPlugin(bool aEnable)
+{
+  // Current IME state isn't plugin, ignore this call
+  if (NS_WARN_IF(mInputContext.mIMEState.mEnabled != IMEState::PLUGIN)) {
+    return;
+  }
+
+  InputContext inputContext = GetInputContext();
+  if (aEnable) {
+    inputContext.mHTMLInputType.AssignLiteral("text");
+  } else {
+    inputContext.mHTMLInputType.AssignLiteral("password");
+  }
+  SetInputContext(inputContext, InputContextAction());
+}
+
 nsresult
 nsWindow::OnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
                                    nsIKeyEventInPluginCallback* aCallback)
 {
   if (NS_WARN_IF(!mWnd)) {
     return NS_OK;
   }
   const WinNativeKeyEventData* eventData =
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -305,16 +305,17 @@ public:
 
   const IMEContext& DefaultIMC() const { return mDefaultIMC; }
 
   virtual void SetCandidateWindowForPlugin(
                  const mozilla::widget::CandidateWindowPosition&
                    aPosition) override;
   virtual void DefaultProcOfPluginEvent(
                  const mozilla::WidgetPluginEvent& aEvent) override;
+  virtual void EnableIMEForPlugin(bool aEnable) override;
   virtual nsresult OnWindowedPluginKeyEvent(
                      const mozilla::NativeEventData& aKeyEventData,
                      nsIKeyEventInPluginCallback* aCallback) override;
 
   void GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData) override;
   bool IsTouchWindow() const { return mTouchWindow; }
 
 protected: