Bug 1208944 - Part 10-b. Call DefaultProc When CompositionEvent isn't handled correctly by plugin. r=masayuki
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Tue, 29 Dec 2015 22:57:38 +0900
changeset 313283 d0565d59cf0ad7a7ad8836e98e121f2ce4a16172
parent 313282 e43204b34ed1e09f324b2999166eed8cfd10e683
child 313284 7f199f2e1b1bbd2f034aa77a65e480235970b96a
push id5703
push userraliiev@mozilla.com
push dateMon, 07 Mar 2016 14:18:41 +0000
treeherdermozilla-beta@31e373ad5b5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1208944
milestone46.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 1208944 - Part 10-b. Call DefaultProc When CompositionEvent isn't handled correctly by plugin. r=masayuki
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
widget/windows/IMMHandler.cpp
widget/windows/IMMHandler.h
widget/windows/WinIMEHandler.cpp
widget/windows/WinIMEHandler.h
widget/windows/nsWindow.cpp
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -57,16 +57,17 @@ using mozilla::DefaultXDisplay;
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/TabChild.h"
 #include "nsFrameSelection.h"
 #include "PuppetWidget.h"
 #include "nsPIWindowRoot.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/TextComposition.h"
+#include "mozilla/AutoRestore.h"
 
 #include "nsContentCID.h"
 #include "nsWidgetsCID.h"
 static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID);
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 #ifdef XP_WIN
 #include <wtypes.h>
@@ -363,16 +364,21 @@ nsPluginInstanceOwner::nsPluginInstanceO
 #endif
 
   mWaitingForPaint = false;
 
 #ifdef MOZ_WIDGET_ANDROID
   mFullScreen = false;
   mJavaView = nullptr;
 #endif
+
+#ifdef XP_WIN
+  mGotCompositionData = false;
+  mSentStartComposition = false;
+#endif
 }
 
 nsPluginInstanceOwner::~nsPluginInstanceOwner()
 {
   if (mWaitingForPaint) {
     nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
     if (content) {
       // We don't care when the event is dispatched as long as it's "soon",
@@ -794,16 +800,19 @@ nsPluginInstanceOwner::SetNetscapeWindow
   return NS_OK;
 }
 
 bool
 nsPluginInstanceOwner::GetCompositionString(uint32_t aType,
                                             nsTArray<uint8_t>* aDist,
                                             int32_t* aLength)
 {
+  // Mark pkugin calls ImmGetCompositionStringW correctly
+  mGotCompositionData = true;
+
   RefPtr<TextComposition> composition = GetTextComposition();
   if (NS_WARN_IF(!composition)) {
     return false;
   }
 
   switch(aType) {
     case GCS_COMPSTR: {
       if (!composition->IsComposing()) {
@@ -1770,16 +1779,38 @@ nsresult nsPluginInstanceOwner::Dispatch
     if (mouseEvent->mMessage == eMouseUp) {
       mLastMouseDownButtonType = -1;
     }
   }
   return NS_OK;
 }
 
 #ifdef XP_WIN
+void
+nsPluginInstanceOwner::CallDefaultProc(const WidgetGUIEvent* aEvent)
+{
+  nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+  if (!widget) {
+    widget = GetRootWidgetForPluginFrame(mPluginFrame);
+    if (NS_WARN_IF(!widget)) {
+      return;
+    }
+  }
+
+  const NPEvent* npEvent =
+    static_cast<const NPEvent*>(aEvent->mPluginEvent);
+  if (NS_WARN_IF(!npEvent)) {
+    return;
+  }
+
+  WidgetPluginEvent pluginEvent(true, ePluginInputEvent, widget);
+  pluginEvent.mPluginEvent.Copy(*npEvent);
+  widget->DefaultProcOfPluginEvent(pluginEvent);
+}
+
 already_AddRefed<TextComposition>
 nsPluginInstanceOwner::GetTextComposition()
 {
   if (NS_WARN_IF(!mPluginFrame)) {
     return nullptr;
   }
 
   nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
@@ -1819,23 +1850,89 @@ nsPluginInstanceOwner::DispatchCompositi
     RefPtr<TextComposition> composition = GetTextComposition();
     if (NS_WARN_IF(!composition)) {
       return NS_ERROR_FAILURE;
     }
     TextComposition::CompositionChangeEventHandlingMarker
       compositionChangeEventHandlingMarker(composition, compositionEvent);
   }
 
-  nsEventStatus rv = ProcessEvent(*compositionEvent);
-  // XXX This isn't e10s aware.
-  // If the event isn't consumed, we cannot post result to chrome process.
-  if (nsEventStatus_eConsumeNoDefault == rv) {
-    aEvent->StopImmediatePropagation();
+  // Protected mode Flash returns noDefault by NPP_HandleEvent, but
+  // composition information into plugin is invalid because plugin's bug.
+  // So if plugin doesn't get composition data by WM_IME_COMPOSITION, we
+  // recongnize it isn't handled
+  AutoRestore<bool> restore(mGotCompositionData);
+  mGotCompositionData = false;
+
+  nsEventStatus status = ProcessEvent(*compositionEvent);
+  aEvent->StopImmediatePropagation();
+
+  // Composition event isn't handled by plugin, so we have to call default proc.
+  const NPEvent* pPluginEvent =
+    static_cast<const NPEvent*>(compositionEvent->mPluginEvent);
+  if (NS_WARN_IF(!pPluginEvent)) {
+    return NS_OK;
+  }
+
+  if (pPluginEvent->event == WM_IME_STARTCOMPOSITION) {
+    // Flash's protected mode lies that composition event is handled, but it
+    // cannot do it well.  So even if handled, we should post this message when
+    // no IMM API calls during WM_IME_COMPOSITION.
+    if (nsEventStatus_eConsumeNoDefault != status)  {
+      CallDefaultProc(compositionEvent);
+      mSentStartComposition = true;
+    } else {
+      mSentStartComposition = false;
+    }
+    return NS_OK;
+  }
+
+  if (pPluginEvent->event == WM_IME_ENDCOMPOSITION) {
+    // Always post WM_END_COMPOSITION to default proc. Because Flash may lie
+    // that it doesn't handle composition well, but event is handled.
+    // Even if posting this message, default proc do nothing if unnecessary.
+    CallDefaultProc(compositionEvent);
+    return NS_OK;
   }
-#endif
+
+  if (pPluginEvent->event == WM_IME_COMPOSITION && !mGotCompositionData) {
+    nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+    if (!widget) {
+      widget = GetRootWidgetForPluginFrame(mPluginFrame);
+    }
+
+    if (pPluginEvent->lParam & GCS_RESULTSTR) {
+      // GCS_RESULTSTR's default proc will generate WM_CHAR. So emulate it.
+      for (size_t i = 0; i < compositionEvent->mData.Length(); i++) {
+        WidgetPluginEvent charEvent(true, ePluginInputEvent, widget);
+        NPEvent event;
+        event.event = WM_CHAR;
+        event.wParam = compositionEvent->mData[i];
+        event.lParam = 0;
+        charEvent.mPluginEvent.Copy(event);
+        ProcessEvent(charEvent);
+      }
+      return NS_OK;
+    }
+    if (!mSentStartComposition) {
+      // We post WM_IME_COMPOSITION to default proc, but
+      // WM_IME_STARTCOMPOSITION isn't post yet.  We should post it at first.
+      WidgetPluginEvent event(true, ePluginInputEvent, widget);
+      NPEvent npevent;
+      npevent.event = WM_IME_STARTCOMPOSITION;
+      npevent.wParam = 0;
+      npevent.lParam = 0;
+      event.mPluginEvent.Copy(npevent);
+      CallDefaultProc(&event);
+      mSentStartComposition = true;
+    }
+
+    CallDefaultProc(compositionEvent);
+  }
+#endif // #ifdef XP_WIN
   return NS_OK;
 }
 
 nsresult
 nsPluginInstanceOwner::HandleEvent(nsIDOMEvent* aEvent)
 {
   NS_ASSERTION(mInstance, "Should have a valid plugin instance or not receive events.");
 
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -284,16 +284,19 @@ private:
 
   bool mFullScreen;
   void* mJavaView;
 #endif 
 
 #if defined(XP_WIN)
   nsIWidget* GetContainingWidgetIfOffset();
   already_AddRefed<mozilla::TextComposition> GetTextComposition();
+
+  bool mGotCompositionData;
+  bool mSentStartComposition;
 #endif
  
   nsPluginNativeWindow       *mPluginWindow;
   RefPtr<nsNPAPIPluginInstance> mInstance;
   nsPluginFrame              *mPluginFrame;
   nsWeakPtr                   mContent; // WEAK, content owns us
   nsCString                   mDocumentBase;
   bool                        mWidgetCreationComplete;
@@ -337,16 +340,20 @@ private:
   RefPtr<nsPluginDOMContextMenuListener> mCXMenuListener;
   
   nsresult DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent);
   nsresult DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
                                  bool aAllowPropagate = false);
   nsresult DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent);
   nsresult DispatchCompositionToPlugin(nsIDOMEvent* aEvent);
 
+#ifdef XP_WIN
+  void CallDefaultProc(const mozilla::WidgetGUIEvent* aEvent);
+#endif
+
 #ifdef XP_MACOSX
   static NPBool ConvertPointPuppet(PuppetWidget *widget, nsPluginFrame* pluginFrame,
                             double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
                             double *destX, double *destY, NPCoordinateSpace destSpace);
   static NPBool ConvertPointNoPuppet(nsIWidget *widget, nsPluginFrame* pluginFrame,
                             double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
                             double *destX, double *destY, NPCoordinateSpace destSpace);
   void PerformDelayedBlurs();
--- a/widget/windows/IMMHandler.cpp
+++ b/widget/windows/IMMHandler.cpp
@@ -1083,91 +1083,77 @@ IMMHandler::OnChar(nsWindow* aWindow,
   aResult.mConsumed = true;
   return aResult.mConsumed;
 }
 
 /****************************************************************************
  * message handlers for plug-in
  ****************************************************************************/
 
-bool
+void
 IMMHandler::OnIMEStartCompositionOnPlugin(nsWindow* aWindow,
                                           WPARAM wParam,
-                                          LPARAM lParam,
-                                          MSGResult& aResult)
+                                          LPARAM lParam)
 {
   MOZ_LOG(gIMMLog, LogLevel::Info,
     ("IMM: OnIMEStartCompositionOnPlugin, hWnd=%08x, mIsComposingOnPlugin=%s",
      aWindow->GetWindowHandle(), GetBoolName(mIsComposingOnPlugin)));
   mIsComposingOnPlugin = true;
   mComposingWindow = aWindow;
   IMEContext context(aWindow);
   SetIMERelatedWindowsPosOnPlugin(aWindow, context);
   // On widnowless plugin, we should assume that the focused editor is always
   // in horizontal writing mode.
   AdjustCompositionFont(aWindow, context, WritingMode());
-  aResult.mConsumed =
-    aWindow->DispatchPluginEvent(WM_IME_STARTCOMPOSITION, wParam, lParam,
-                                 false);
-  return true;
 }
 
-bool
+void
 IMMHandler::OnIMECompositionOnPlugin(nsWindow* aWindow,
                                      WPARAM wParam,
-                                     LPARAM lParam,
-                                     MSGResult& aResult)
+                                     LPARAM lParam)
 {
   MOZ_LOG(gIMMLog, LogLevel::Info,
     ("IMM: OnIMECompositionOnPlugin, hWnd=%08x, lParam=%08x, "
      "mIsComposingOnPlugin=%s, GCS_RESULTSTR=%s, GCS_COMPSTR=%s, "
      "GCS_COMPATTR=%s, GCS_COMPCLAUSE=%s, GCS_CURSORPOS=%s",
      aWindow->GetWindowHandle(), lParam, GetBoolName(mIsComposingOnPlugin),
      GetBoolName(lParam & GCS_RESULTSTR), GetBoolName(lParam & GCS_COMPSTR),
      GetBoolName(lParam & GCS_COMPATTR), GetBoolName(lParam & GCS_COMPCLAUSE),
      GetBoolName(lParam & GCS_CURSORPOS)));
   // We should end composition if there is a committed string.
   if (IS_COMMITTING_LPARAM(lParam)) {
     mIsComposingOnPlugin = false;
     mComposingWindow = nullptr;
+    return;
   }
   // Continue composition if there is still a string being composed.
   if (IS_COMPOSING_LPARAM(lParam)) {
     mIsComposingOnPlugin = true;
     mComposingWindow = aWindow;
     IMEContext context(aWindow);
     SetIMERelatedWindowsPosOnPlugin(aWindow, context);
   }
-  aResult.mConsumed =
-    aWindow->DispatchPluginEvent(WM_IME_COMPOSITION, wParam, lParam, true);
-  return true;
 }
 
-bool
+void
 IMMHandler::OnIMEEndCompositionOnPlugin(nsWindow* aWindow,
                                         WPARAM wParam,
-                                        LPARAM lParam,
-                                        MSGResult& aResult)
+                                        LPARAM lParam)
 {
   MOZ_LOG(gIMMLog, LogLevel::Info,
     ("IMM: OnIMEEndCompositionOnPlugin, hWnd=%08x, mIsComposingOnPlugin=%s",
      aWindow->GetWindowHandle(), GetBoolName(mIsComposingOnPlugin)));
 
   mIsComposingOnPlugin = false;
   mComposingWindow = nullptr;
 
   if (mNativeCaretIsCreated) {
     ::DestroyCaret();
     mNativeCaretIsCreated = false;
   }
-
-  aResult.mConsumed =
-    aWindow->DispatchPluginEvent(WM_IME_ENDCOMPOSITION, wParam, lParam,
-                                 false);
-  return true;
 }
 
 bool
 IMMHandler::OnIMECharOnPlugin(nsWindow* aWindow,
                               WPARAM wParam,
                               LPARAM lParam,
                               MSGResult& aResult)
 {
@@ -1226,16 +1212,22 @@ IMMHandler::OnIMESetContextOnPlugin(nsWi
 }
 
 bool
 IMMHandler::OnCharOnPlugin(nsWindow* aWindow,
                            WPARAM wParam,
                            LPARAM lParam,
                            MSGResult& aResult)
 {
+  NS_WARNING("OnCharOnPlugin");
+  if (mIsComposing) {
+    aWindow->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
+    return true;
+  }
+
   // We should never consume char message on windowless plugin.
   aResult.mConsumed = false;
   if (IsIMECharRecordsEmpty()) {
     return false;
   }
 
   WPARAM recWParam;
   LPARAM recLParam;
@@ -2752,16 +2744,43 @@ IMMHandler::OnKeyDownEvent(nsWindow* aWi
 // static
 void
 IMMHandler::SetCandidateWindow(nsWindow* aWindow, CANDIDATEFORM* aForm)
 {
   IMEContext context(aWindow);
   ImmSetCandidateWindow(context.get(), aForm);
 }
 
+// staitc
+void
+IMMHandler::DefaultProcOfPluginEvent(nsWindow* aWindow, const NPEvent* aEvent)
+{
+  switch (aEvent->event) {
+    case WM_IME_STARTCOMPOSITION:
+      EnsureHandlerInstance();
+      gIMMHandler->OnIMEStartCompositionOnPlugin(aWindow, aEvent->wParam,
+                                                 aEvent->lParam);
+      break;
+
+    case WM_IME_COMPOSITION:
+      if (gIMMHandler) {
+        gIMMHandler->OnIMECompositionOnPlugin(aWindow, aEvent->wParam,
+                                              aEvent->lParam);
+      }
+      break;
+
+    case WM_IME_ENDCOMPOSITION:
+      if (gIMMHandler) {
+        gIMMHandler->OnIMEEndCompositionOnPlugin(aWindow, aEvent->wParam,
+                                                 aEvent->lParam);
+      }
+      break;
+  }
+}
+
 /******************************************************************************
  * IMMHandler::Selection
  ******************************************************************************/
 
 bool
 IMMHandler::Selection::IsValid() const
 {
   if (!mIsValid || NS_WARN_IF(mOffset == UINT32_MAX)) {
--- a/widget/windows/IMMHandler.h
+++ b/widget/windows/IMMHandler.h
@@ -10,16 +10,17 @@
 #include <windows.h>
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsIWidget.h"
 #include "mozilla/EventForwards.h"
 #include "nsRect.h"
 #include "WritingModes.h"
+#include "npapi.h"
 
 class nsWindow;
 
 namespace mozilla {
 namespace widget {
 
 struct MSGResult;
 
@@ -146,16 +147,18 @@ public:
 
   static nsIMEUpdatePreference GetIMEUpdatePreference();
 
   // Returns NS_SUCCESS_EVENT_CONSUMED if the mouse button event is consumed by
   // IME.  Otherwise, NS_OK.
   static nsresult OnMouseButtonEvent(nsWindow* aWindow,
                                      const IMENotification& aIMENotification);
   static void SetCandidateWindow(nsWindow* aWindow, CANDIDATEFORM* aForm);
+  static void DefaultProcOfPluginEvent(nsWindow* aWindow,
+                                       const NPEvent* aEvent);
 
 protected:
   static void EnsureHandlerInstance();
 
   static bool IsComposingOnOurEditor();
   static bool IsComposingOnPlugin();
   static bool IsComposingWindow(nsWindow* aWindow);
 
@@ -187,26 +190,25 @@ protected:
   ~IMMHandler();
 
   // On*() methods return true if the caller of message handler shouldn't do
   // anything anymore.  Otherwise, false.
   static bool OnKeyDownEvent(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                              MSGResult& aResult);
 
   bool OnIMEStartComposition(nsWindow* aWindow, MSGResult& aResult);
-  bool OnIMEStartCompositionOnPlugin(nsWindow* aWindow,
-                                     WPARAM wParam, LPARAM lParam,
-                                     MSGResult& aResult);
+  void OnIMEStartCompositionOnPlugin(nsWindow* aWindow,
+                                     WPARAM wParam, LPARAM lParam);
   bool OnIMEComposition(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                         MSGResult& aResult);
-  bool OnIMECompositionOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
-                                MSGResult& aResult);
+  void OnIMECompositionOnPlugin(nsWindow* aWindow, WPARAM wParam,
+                                LPARAM lParam);
   bool OnIMEEndComposition(nsWindow* aWindow, MSGResult& aResult);
-  bool OnIMEEndCompositionOnPlugin(nsWindow* aWindow, WPARAM wParam,
-                                   LPARAM lParam, MSGResult& aResult);
+  void OnIMEEndCompositionOnPlugin(nsWindow* aWindow, WPARAM wParam,
+                                   LPARAM lParam);
   bool OnIMERequest(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                     MSGResult& aResult);
   bool OnIMECharOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                          MSGResult& aResult);
   bool OnChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
               MSGResult& aResult);
   bool OnCharOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                       MSGResult& aResult);
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -954,10 +954,21 @@ IMEHandler::SetCandidateWindow(nsWindow*
 {
   if (!sPluginHasFocus) {
     return;
   }
 
   IMMHandler::SetCandidateWindow(aWindow, aForm);
 }
 
+// static
+void
+IMEHandler::DefaultProcOfPluginEvent(nsWindow* aWindow,
+                                     const NPEvent* aPluginEvent)
+{
+  if (!sPluginHasFocus) {
+    return;
+  }
+  IMMHandler::DefaultProcOfPluginEvent(aWindow, aPluginEvent);
+}
+
 } // namespace widget
 } // namespace mozilla
--- a/widget/windows/WinIMEHandler.h
+++ b/widget/windows/WinIMEHandler.h
@@ -3,16 +3,17 @@
  * 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/. */
 
 #ifndef WinIMEHandler_h_
 #define WinIMEHandler_h_
 
 #include "nscore.h"
 #include "nsIWidget.h"
+#include "npapi.h"
 #include <windows.h>
 #include <inputscope.h>
 
 #define NS_WM_IMEFIRST WM_IME_SETCONTEXT
 #define NS_WM_IMELAST  WM_IME_KEYUP
 
 class nsWindow;
 
@@ -103,16 +104,22 @@ public:
    */
   static void InitInputContext(nsWindow* aWindow, InputContext& aInputContext);
 
   /*
    * For windowless plugin helper.
    */
   static void SetCandidateWindow(nsWindow* aWindow, CANDIDATEFORM* aForm);
 
+  /*
+   * For WM_IME_*COMPOSITION messages and e10s with windowless plugin
+   */
+  static void DefaultProcOfPluginEvent(nsWindow* aWindow,
+                                       const NPEvent* aPluginEvent);
+
 #ifdef DEBUG
   /**
    * Returns true when current keyboard layout has IME.  Otherwise, false.
    */
   static bool CurrentKeyboardLayoutHasIME();
 #endif // #ifdef DEBUG
 
 private:
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -7772,16 +7772,19 @@ nsWindow::DefaultProcOfPluginEvent(const
   if (NS_WARN_IF(!pPluginEvent)) {
     return;
   }
 
   if (!mWnd) {
     return;
   }
 
+  // For WM_IME_*COMPOSITION
+  IMEHandler::DefaultProcOfPluginEvent(this, pPluginEvent);
+
   CallWindowProcW(GetPrevWindowProc(), mWnd, pPluginEvent->event,
                   pPluginEvent->wParam, pPluginEvent->lParam);
 }
 
 /**************************************************************
  **************************************************************
  **
  ** BLOCK: ChildWindow impl.