Bug 1208944 - Part 2-a. Handle CompositionEvent on plugin. r=masayuki
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Tue, 29 Dec 2015 22:57:37 +0900
changeset 318399 5becd8aebc9ebfb0aea1f8948741b169d6e85497
parent 318398 8fb392fd3842e32d787f79446d90f4be0bf2a3f4
child 318400 4cd8ab65dd297c2730b7237690eebd5d6a66cf69
push id8863
push userpaolo.mozmail@amadzone.org
push dateSat, 02 Jan 2016 11:05:27 +0000
reviewersmasayuki
bugs1208944
milestone46.0a1
Bug 1208944 - Part 2-a. Handle CompositionEvent on plugin. r=masayuki
dom/events/TextComposition.cpp
dom/events/TextComposition.h
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
widget/TextRange.h
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContentEventHandler.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 #include "nsIEditor.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
+#include "nsPluginInstanceOwner.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/unused.h"
@@ -113,19 +114,32 @@ TextComposition::CloneAndDispatchAs(
   compositionEvent.mFlags.mIsSynthesizedForTests =
     aCompositionEvent->mFlags.mIsSynthesizedForTests;
 
   nsEventStatus dummyStatus = nsEventStatus_eConsumeNoDefault;
   nsEventStatus* status = aStatus ? aStatus : &dummyStatus;
   if (aMessage == eCompositionUpdate) {
     mLastData = compositionEvent.mData;
   }
+
+  DispatchEvent(&compositionEvent, status, aCallBack, aCompositionEvent);
+  return compositionEvent.mFlags;
+}
+
+void
+TextComposition::DispatchEvent(WidgetCompositionEvent* aDispatchEvent,
+                               nsEventStatus* aStatus,
+                               EventDispatchingCallback* aCallBack,
+                               const WidgetCompositionEvent *aOriginalEvent)
+{
+  nsPluginInstanceOwner::GeneratePluginEvent(aOriginalEvent,
+                                             aDispatchEvent);
+
   EventDispatcher::Dispatch(mNode, mPresContext,
-                            &compositionEvent, nullptr, status, aCallBack);
-  return compositionEvent.mFlags;
+                            aDispatchEvent, nullptr, aStatus, aCallBack);
 }
 
 void
 TextComposition::OnCompositionEventDiscarded(
                    WidgetCompositionEvent* aCompositionEvent)
 {
   // Note that this method is never called for synthesized events for emulating
   // commit or cancel composition.
@@ -336,18 +350,17 @@ TextComposition::DispatchCompositionEven
     // the limitation of mapping between event messages and DOM event types,
     // we cannot map multiple event messages to a DOM event type.
     if (dispatchDOMTextEvent &&
         aCompositionEvent->mMessage != eCompositionChange) {
       aCompositionEvent->mFlags =
         CloneAndDispatchAs(aCompositionEvent, eCompositionChange,
                            aStatus, aCallBack);
     } else {
-      EventDispatcher::Dispatch(mNode, mPresContext,
-                                aCompositionEvent, nullptr, aStatus, aCallBack);
+      DispatchEvent(aCompositionEvent, aStatus, aCallBack);
     }
   } else {
     *aStatus = nsEventStatus_eConsumeNoDefault;
   }
 
   if (!IsValidStateForComposition(aCompositionEvent->widget)) {
     return;
   }
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -289,16 +289,25 @@ private:
    * synchronously. The caller must ensure that it's safe to dispatch the event.
    */
   void DispatchCompositionEvent(WidgetCompositionEvent* aCompositionEvent,
                                 nsEventStatus* aStatus,
                                 EventDispatchingCallback* aCallBack,
                                 bool aIsSynthesized);
 
   /**
+   * Simply calling EventDispatcher::Dispatch() with plugin event.
+   * If dispatching event has no orginal clone, aOriginalEvent can be null.
+   */
+  void DispatchEvent(WidgetCompositionEvent* aDispatchEvent,
+                     nsEventStatus* aStatus,
+                     EventDispatchingCallback* aCallback,
+                     const WidgetCompositionEvent *aOriginalEvent = nullptr);
+
+  /**
    * HandleSelectionEvent() sends the selection event to ContentEventHandler
    * or dispatches it to the focused child process.
    */
   void HandleSelectionEvent(WidgetSelectionEvent* aSelectionEvent)
   {
     HandleSelectionEvent(mPresContext, mTabParent, aSelectionEvent);
   }
   static void HandleSelectionEvent(nsPresContext* aPresContext,
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1593,16 +1593,40 @@ nsresult nsPluginInstanceOwner::Dispatch
     if (mouseEvent->mMessage == eMouseUp) {
       mLastMouseDownButtonType = -1;
     }
   }
   return NS_OK;
 }
 
 nsresult
+nsPluginInstanceOwner::DispatchCompositionToPlugin(nsIDOMEvent* aEvent)
+{
+#ifdef XP_WIN
+  if (!mPluginWindow) {
+    // CompositionEvent isn't cancellable.  So it is unnecessary to call
+    // PreventDefaults() to consume event
+    return NS_OK;
+  }
+  WidgetCompositionEvent* compositionEvent =
+    aEvent->GetInternalNSEvent()->AsCompositionEvent();
+  if (NS_WARN_IF(!compositionEvent)) {
+      return NS_ERROR_INVALID_ARG;
+  }
+  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();
+  }
+#endif
+  return NS_OK;
+}
+
+nsresult
 nsPluginInstanceOwner::HandleEvent(nsIDOMEvent* aEvent)
 {
   NS_ASSERTION(mInstance, "Should have a valid plugin instance or not receive events.");
 
   nsAutoString eventType;
   aEvent->GetType(eventType);
 
 #ifdef XP_MACOSX
@@ -1646,16 +1670,21 @@ nsPluginInstanceOwner::HandleEvent(nsIDO
   }
   if (eventType.EqualsLiteral("keydown") ||
       eventType.EqualsLiteral("keyup")) {
     return DispatchKeyToPlugin(aEvent);
   }
   if (eventType.EqualsLiteral("keypress")) {
     return ProcessKeyPress(aEvent);
   }
+  if (eventType.EqualsLiteral("compositionstart") ||
+      eventType.EqualsLiteral("compositionend") ||
+      eventType.EqualsLiteral("text")) {
+    return DispatchCompositionToPlugin(aEvent);
+  }
 
   nsCOMPtr<nsIDOMDragEvent> dragEvent(do_QueryInterface(aEvent));
   if (dragEvent && mInstance) {
     WidgetEvent* ievent = aEvent->GetInternalNSEvent();
     if (ievent && ievent->mFlags.mIsTrusted &&
         ievent->mMessage != eDragEnter && ievent->mMessage != eDragOver) {
       aEvent->PreventDefault();
     }
@@ -2472,16 +2501,21 @@ nsPluginInstanceOwner::Destroy()
   content->RemoveEventListener(NS_LITERAL_STRING("drag"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragenter"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragover"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragleave"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragexit"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragstart"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("draggesture"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragend"), this, true);
+  content->RemoveSystemEventListener(NS_LITERAL_STRING("compositionstart"),
+                                     this, true);
+  content->RemoveSystemEventListener(NS_LITERAL_STRING("compositionend"),
+                                     this, true);
+  content->RemoveSystemEventListener(NS_LITERAL_STRING("text"), this, true);
 
 #if MOZ_WIDGET_ANDROID
   RemovePluginView();
 #endif
 
   if (mWidget) {
     if (mPluginWindow) {
       mPluginWindow->SetPluginWidget(nullptr);
@@ -2867,16 +2901,21 @@ nsresult nsPluginInstanceOwner::Init(nsI
   aContent->AddEventListener(NS_LITERAL_STRING("drag"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragenter"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragover"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragleave"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragexit"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("draggesture"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, true);
+  aContent->AddSystemEventListener(NS_LITERAL_STRING("compositionstart"),
+    this, true);
+  aContent->AddSystemEventListener(NS_LITERAL_STRING("compositionend"), this,
+    true);
+  aContent->AddSystemEventListener(NS_LITERAL_STRING("text"), this, true);
 
   return NS_OK;
 }
 
 void* nsPluginInstanceOwner::GetPluginPort()
 {
 //!!! Port must be released for windowless plugins on Windows, because it is HDC !!!
 
@@ -3369,16 +3408,61 @@ already_AddRefed<nsIURI> nsPluginInstanc
 {
   nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
   if (!content) {
     return nullptr;
   }
   return content->GetBaseURI();
 }
 
+// static
+void
+nsPluginInstanceOwner::GeneratePluginEvent(
+  const WidgetCompositionEvent* aSrcCompositionEvent,
+  WidgetCompositionEvent* aDistCompositionEvent)
+{
+#ifdef XP_WIN
+  NPEvent newEvent;
+  switch (aDistCompositionEvent->mMessage) {
+    case eCompositionChange: {
+      newEvent.event = WM_IME_COMPOSITION;
+      newEvent.wParam = 0;
+      if (aSrcCompositionEvent &&
+          (aSrcCompositionEvent->mMessage == eCompositionCommit ||
+           aSrcCompositionEvent->mMessage == eCompositionCommitAsIs)) {
+        newEvent.lParam = GCS_RESULTSTR;
+      } else {
+        newEvent.lParam = GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE;
+      }
+      TextRangeArray* ranges = aDistCompositionEvent->mRanges;
+      if (ranges && ranges->HasCaret()) {
+        newEvent.lParam |= GCS_CURSORPOS;
+      }
+      break;
+    }
+
+    case eCompositionStart:
+      newEvent.event = WM_IME_STARTCOMPOSITION;
+      newEvent.wParam = 0;
+      newEvent.lParam = 0;
+      break;
+
+    case eCompositionEnd:
+      newEvent.event = WM_IME_ENDCOMPOSITION;
+      newEvent.wParam = 0;
+      newEvent.lParam = 0;
+      break;
+
+    default:
+      return;
+  }
+  aDistCompositionEvent->mPluginEvent.Copy(newEvent);
+#endif
+}
+
 // nsPluginDOMContextMenuListener class implementation
 
 nsPluginDOMContextMenuListener::nsPluginDOMContextMenuListener(nsIContent* aContent)
 {
   aContent->AddEventListener(NS_LITERAL_STRING("contextmenu"), this, true);
 }
 
 nsPluginDOMContextMenuListener::~nsPluginDOMContextMenuListener()
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -113,16 +113,20 @@ public:
   
   nsresult Init(nsIContent* aContent);
   
   void* GetPluginPort();
   void ReleasePluginPort(void* pluginPort);
 
   nsEventStatus ProcessEvent(const mozilla::WidgetGUIEvent& anEvent);
 
+  static void GeneratePluginEvent(
+                const mozilla::WidgetCompositionEvent* aSrcCompositionEvent,
+                mozilla::WidgetCompositionEvent* aDistCompositionEvent);
+
 #if defined(XP_WIN)
   void SetWidgetWindowAsParent(HWND aWindowToAdopt);
   nsresult SetNetscapeWindowAsParent(HWND aWindowToAdopt);
 #endif
   
 #ifdef XP_MACOSX
   enum { ePluginPaintEnable, ePluginPaintDisable };
 
@@ -324,16 +328,17 @@ private:
   
   // pointer to wrapper for nsIDOMContextMenuListener
   RefPtr<nsPluginDOMContextMenuListener> mCXMenuListener;
   
   nsresult DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent);
   nsresult DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
                                  bool aAllowPropagate = false);
   nsresult DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent);
+  nsresult DispatchCompositionToPlugin(nsIDOMEvent* aEvent);
 
 #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);
--- a/widget/TextRange.h
+++ b/widget/TextRange.h
@@ -251,13 +251,23 @@ public:
   }
 
   void RemoveCharacter(uint32_t aOffset)
   {
     for (size_t i = 0, len = Length(); i < len; i++) {
       ElementAt(i).RemoveCharacter(aOffset);
     }
   }
+
+  bool HasCaret() const
+  {
+    for (const TextRange& range : *this) {
+      if (range.mRangeType == NS_TEXTRANGE_CARETPOSITION) {
+        return true;
+      }
+    }
+    return false;
+  }
 };
 
 } // namespace mozilla
 
 #endif // mozilla_TextRage_h_