Bug 935821 - Part 1. Notify IME Compostion to widget. r=masayuki
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Thu, 07 Nov 2013 09:11:11 +0900
changeset 179774 2a228a62d0b8fc29fe448d770ecdd4368522e830
parent 179773 5c1168eb45037c26f9c435ddefb4da8709a24169
child 179775 8f09f17e27e9b83446e4a1d2a260f44025fde3de
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs935821
milestone29.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 935821 - Part 1. Notify IME Compostion to widget. r=masayuki
dom/events/TextComposition.cpp
dom/events/TextComposition.h
dom/events/nsIMEStateManager.cpp
dom/events/nsIMEStateManager.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
widget/nsIWidget.h
widget/xpwidgets/PuppetWidget.cpp
widget/xpwidgets/PuppetWidget.h
widget/xpwidgets/moz.build
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -21,26 +21,29 @@ namespace mozilla {
  * TextComposition
  ******************************************************************************/
 
 TextComposition::TextComposition(nsPresContext* aPresContext,
                                  nsINode* aNode,
                                  WidgetGUIEvent* aEvent) :
   mPresContext(aPresContext), mNode(aNode),
   mNativeContext(aEvent->widget->GetInputContext().mNativeIMEContext),
+  mCompositionStartOffset(0), mCompositionTargetOffset(0),
   mIsSynthesizedForTests(aEvent->mFlags.mIsSynthesizedForTests)
 {
 }
 
 TextComposition::TextComposition(const TextComposition& aOther)
 {
   mNativeContext = aOther.mNativeContext;
   mPresContext = aOther.mPresContext;
   mNode = aOther.mNode;
   mLastData = aOther.mLastData;
+  mCompositionStartOffset = aOther.mCompositionStartOffset;
+  mCompositionTargetOffset = aOther.mCompositionTargetOffset;
   mIsSynthesizedForTests = aOther.mIsSynthesizedForTests;
 }
 
 bool
 TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
 {
   return mNativeContext == aWidget->GetInputContext().mNativeIMEContext;
 }
@@ -51,16 +54,61 @@ TextComposition::DispatchEvent(WidgetGUI
                                nsDispatchingCallback* aCallBack)
 {
   if (aEvent->message == NS_COMPOSITION_UPDATE) {
     mLastData = aEvent->AsCompositionEvent()->data;
   }
 
   nsEventDispatcher::Dispatch(mNode, mPresContext,
                               aEvent, nullptr, aStatus, aCallBack);
+
+  // Notify composition update to widget if possible
+  NotityUpdateComposition(aEvent);
+}
+
+void
+TextComposition::NotityUpdateComposition(WidgetGUIEvent* aEvent)
+{
+  nsEventStatus status;
+
+  // When compositon start, notify the rect of first offset character.
+  // When not compositon start, notify the rect of selected composition
+  // string if text event.
+  if (aEvent->message == NS_COMPOSITION_START) {
+    nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
+    // Update composition start offset
+    WidgetQueryContentEvent selectedTextEvent(true,
+                                              NS_QUERY_SELECTED_TEXT,
+                                              widget);
+    widget->DispatchEvent(&selectedTextEvent, status);
+    if (selectedTextEvent.mSucceeded) {
+      mCompositionStartOffset = selectedTextEvent.mReply.mOffset;
+    } else {
+      // Unknown offset
+      NS_WARNING("Cannot get start offset of IME composition");
+      mCompositionStartOffset = 0;
+    }
+    mCompositionTargetOffset = mCompositionStartOffset;
+  } else if (aEvent->eventStructType != NS_TEXT_EVENT) {
+    return;
+  } else {
+    WidgetTextEvent* textEvent = aEvent->AsTextEvent();
+    mCompositionTargetOffset = mCompositionStartOffset;
+
+    for (uint32_t i = 0; i < textEvent->rangeCount; i++) {
+      TextRange& range = textEvent->rangeArray[i];
+      if (range.mRangeType == NS_TEXTRANGE_SELECTEDRAWTEXT ||
+          range.mRangeType == NS_TEXTRANGE_SELECTEDCONVERTEDTEXT) {
+        mCompositionTargetOffset += range.mStartOffset;
+        break;
+      }
+    }
+  }
+
+  NotifyIME(widget::NotificationToIME::NOTIFY_IME_OF_COMPOSITION_UPDATE);
 }
 
 void
 TextComposition::DispatchCompsotionEventRunnable(uint32_t aEventMessage,
                                                  const nsAString& aData)
 {
   nsContentUtils::AddScriptRunner(
     new CompositionEventDispatcher(mPresContext, mNode,
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_TextComposition_h
 #define mozilla_TextComposition_h
 
 #include "nsCOMPtr.h"
 #include "nsINode.h"
 #include "nsIWidget.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
+#include "nsPresContext.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 
 class nsDispatchingCallback;
 class nsIMEStateManager;
 class nsIWidget;
 
 namespace mozilla {
@@ -61,47 +62,63 @@ public:
   void SynthesizeCommit(bool aDiscard);
 
   /**
    * Send a notification to IME.  It depends on the IME or platform spec what
    * will occur (or not occur).
    */
   nsresult NotifyIME(widget::NotificationToIME aNotification);
 
+  /**
+   * the offset of first selected clause or start of of compositon
+   */
+  uint32_t OffsetOfTargetClause() const { return mCompositionTargetOffset; }
+
 private:
   // This class holds nsPresContext weak.  This instance shouldn't block
   // destroying it.  When the presContext is being destroyed, it's notified to
   // nsIMEStateManager::OnDestroyPresContext(), and then, it destroy
   // this instance.
   nsPresContext* mPresContext;
   nsCOMPtr<nsINode> mNode;
 
   // mNativeContext stores a opaque pointer.  This works as the "ID" for this
   // composition.  Don't access the instance, it may not be available.
   void* mNativeContext;
 
   // mLastData stores the data attribute of the latest composition event (except
   // the compositionstart event).
   nsString mLastData;
 
+  // Offset of the composition string from start of the editor
+  uint32_t mCompositionStartOffset;
+  // Offset of the selected clause of the composition string from start of the
+  // editor
+  uint32_t mCompositionTargetOffset;
+
   // See the comment for IsSynthesizedForTests().
   bool mIsSynthesizedForTests;
 
   // Hide the default constructor
   TextComposition() {}
 
   /**
    * DispatchEvent() dispatches the aEvent to the mContent synchronously.
    * The caller must ensure that it's safe to dispatch the event.
    */
   void DispatchEvent(WidgetGUIEvent* aEvent,
                      nsEventStatus* aStatus,
                      nsDispatchingCallback* aCallBack);
 
   /**
+   * Calculate composition offset then notify composition update to widget
+   */
+  void NotityUpdateComposition(WidgetGUIEvent* aEvent);
+
+  /**
    * CompositionEventDispatcher dispatches the specified composition (or text)
    * event.
    */
   class CompositionEventDispatcher : public nsRunnable
   {
   public:
     CompositionEventDispatcher(nsPresContext* aPresContext,
                                nsINode* aEventTarget,
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/nsIMEStateManager.cpp
@@ -593,16 +593,17 @@ nsIMEStateManager::NotifyIME(Notificatio
     composition = sTextCompositions->GetCompositionFor(aWidget);
   }
   if (!composition || !composition->IsSynthesizedForTests()) {
     switch (aNotification) {
       case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
         return aWidget->NotifyIME(aNotification);
       case REQUEST_TO_COMMIT_COMPOSITION:
       case REQUEST_TO_CANCEL_COMPOSITION:
+      case NOTIFY_IME_OF_COMPOSITION_UPDATE:
         return composition ? aWidget->NotifyIME(aNotification) : NS_OK;
       default:
         MOZ_CRASH("Unsupported notification");
     }
     MOZ_CRASH(
       "Failed to handle the notification for non-synthesized composition");
   }
 
@@ -1120,8 +1121,14 @@ nsIMEStateManager::GetFocusSelectionAndR
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent,
                "uninitialized text state observer");
   NS_ADDREF(*aSel = sTextStateObserver->mSel);
   NS_ADDREF(*aRoot = sTextStateObserver->mRootContent);
   return NS_OK;
 }
+
+TextComposition*
+nsIMEStateManager::GetTextComposition(nsIWidget* aWidget)
+{
+  return sTextCompositions->GetCompositionFor(aWidget);
+}
--- a/dom/events/nsIMEStateManager.h
+++ b/dom/events/nsIMEStateManager.h
@@ -15,16 +15,17 @@ class nsIDOMMouseEvent;
 class nsINode;
 class nsPIDOMWindow;
 class nsPresContext;
 class nsTextStateManager;
 class nsISelection;
 
 namespace mozilla {
 class TextCompositionArray;
+class TextComposition;
 } // namespace mozilla
 
 /*
  * IME state manager
  */
 
 class nsIMEStateManager
 {
@@ -90,16 +91,21 @@ public:
    */
   static void DispatchCompositionEvent(nsINode* aEventTargetNode,
                                        nsPresContext* aPresContext,
                                        mozilla::WidgetEvent* aEvent,
                                        nsEventStatus* aStatus,
                                        nsDispatchingCallback* aCallBack);
 
   /**
+   * Get TextComposition from widget.
+   */
+  static mozilla::TextComposition* GetTextComposition(nsIWidget* aWidget);
+
+  /**
    * Send a notification to IME.  It depends on the IME or platform spec what
    * will occur (or not occur).
    */
   static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
                             nsIWidget* aWidget);
   static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
                             nsPresContext* aPresContext);
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -127,16 +127,24 @@ parent:
      *  newEnd       New ending offset after insertion
      *
      *  for insertion, offset == end
      *  for deletion, offset == newEnd
      */
     NotifyIMETextChange(uint32_t offset, uint32_t end, uint32_t newEnd);
 
     /**
+     * Notifies chrome that there is a IME compostion rect updated
+     *
+     *  offset       The starting offset of this rect
+     *  rect         The rect of first character of selected IME composition
+     */
+    NotifyIMESelectedCompositionRect(uint32_t offset, nsIntRect rect);
+
+    /**
      * Notifies chrome that there has been a change in selection
      * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
      *
      *  seqno        Current seqno value on the content side
      *  anchor       Offset where the selection started
      *  focus        Offset where the caret is
      */
     NotifyIMESelection(uint32_t seqno, uint32_t anchor, uint32_t focus);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -988,16 +988,28 @@ TabParent::RecvNotifyIMETextChange(const
   if (!widget)
     return true;
 
   widget->NotifyIMEOfTextChange(aStart, aEnd, aNewEnd);
   return true;
 }
 
 bool
+TabParent::RecvNotifyIMESelectedCompositionRect(const uint32_t& aOffset,
+                                                const nsIntRect& aRect)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget) {
+    return true;
+  }
+  widget->NotifyIME(NOTIFY_IME_OF_COMPOSITION_UPDATE);
+  return true;
+}
+
+bool
 TabParent::RecvNotifyIMESelection(const uint32_t& aSeqno,
                                   const uint32_t& aAnchor,
                                   const uint32_t& aFocus)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return true;
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -139,16 +139,18 @@ public:
                                   const InfallibleTArray<CpowEntry>& aCpows,
                                   const IPC::Principal& aPrincipal);
     virtual bool RecvNotifyIMEFocus(const bool& aFocus,
                                     nsIMEUpdatePreference* aPreference,
                                     uint32_t* aSeqno);
     virtual bool RecvNotifyIMETextChange(const uint32_t& aStart,
                                          const uint32_t& aEnd,
                                          const uint32_t& aNewEnd);
+    virtual bool RecvNotifyIMESelectedCompositionRect(const uint32_t& aOffset,
+                                                      const nsIntRect& aRect);
     virtual bool RecvNotifyIMESelection(const uint32_t& aSeqno,
                                         const uint32_t& aAnchor,
                                         const uint32_t& aFocus);
     virtual bool RecvNotifyIMETextHint(const nsString& aText);
     virtual bool RecvEndIMEComposition(const bool& aCancel,
                                        nsString* aComposition);
     virtual bool RecvGetInputContext(int32_t* aIMEEnabled,
                                      int32_t* aIMEOpen,
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -445,17 +445,19 @@ enum NotificationToIME {
   NOTIFY_IME_OF_CURSOR_POS_CHANGED,
   // An editable content is getting focus
   NOTIFY_IME_OF_FOCUS,
   // An editable content is losing focus
   NOTIFY_IME_OF_BLUR,
   // Selection in the focused editable content is changed
   NOTIFY_IME_OF_SELECTION_CHANGE,
   REQUEST_TO_COMMIT_COMPOSITION,
-  REQUEST_TO_CANCEL_COMPOSITION
+  REQUEST_TO_CANCEL_COMPOSITION,
+  // Composition string has been updated
+  NOTIFY_IME_OF_COMPOSITION_UPDATE
 };
 
 } // namespace widget
 } // namespace mozilla
 
 /**
  * The base class for all the widgets. It provides the interface for
  * all basic and necessary functionality.
--- a/widget/xpwidgets/PuppetWidget.cpp
+++ b/widget/xpwidgets/PuppetWidget.cpp
@@ -14,16 +14,18 @@
 #endif
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/Hal.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "mozilla/TextEvents.h"
 #include "PuppetWidget.h"
 #include "nsIWidgetListener.h"
+#include "nsIMEStateManager.h"
+#include "TextComposition.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 
 static void
 InvalidateRegion(nsIWidget* aWidget, const nsIntRegion& aRegion)
@@ -383,16 +385,18 @@ PuppetWidget::NotifyIME(NotificationToIM
     case REQUEST_TO_CANCEL_COMPOSITION:
       return IMEEndComposition(true);
     case NOTIFY_IME_OF_FOCUS:
       return NotifyIMEOfFocusChange(true);
     case NOTIFY_IME_OF_BLUR:
       return NotifyIMEOfFocusChange(false);
     case NOTIFY_IME_OF_SELECTION_CHANGE:
       return NotifyIMEOfSelectionChange();
+    case NOTIFY_IME_OF_COMPOSITION_UPDATE:
+      return NotifyIMEOfUpdateComposition();
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
 NS_IMETHODIMP_(void)
 PuppetWidget::SetInputContext(const InputContext& aContext,
                               const InputContextAction& aAction)
@@ -472,16 +476,42 @@ PuppetWidget::NotifyIMEOfFocusChange(boo
       NotifyIMEOfSelectionChange(); // Update selection
     }
   } else {
     mIMELastBlurSeqno = chromeSeqno;
   }
   return NS_OK;
 }
 
+nsresult
+PuppetWidget::NotifyIMEOfUpdateComposition()
+{
+#ifndef MOZ_CROSS_PROCESS_IME
+  return NS_OK;
+#endif
+
+  NS_ENSURE_TRUE(mTabChild, NS_ERROR_FAILURE);
+
+  mozilla::TextComposition* textComposition =
+    nsIMEStateManager::GetTextComposition(this);
+  NS_ENSURE_TRUE(textComposition, NS_ERROR_FAILURE);
+
+  nsEventStatus status;
+  uint32_t offset = textComposition->OffsetOfTargetClause();
+  WidgetQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, this);
+  InitEvent(textRect, nullptr);
+  textRect.InitForQueryTextRect(offset, 1);
+  DispatchEvent(&textRect, status);
+  NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE);
+
+  mTabChild->SendNotifyIMESelectedCompositionRect(offset,
+                                                  textRect.mReply.mRect);
+  return NS_OK;
+}
+
 nsIMEUpdatePreference
 PuppetWidget::GetIMEUpdatePreference()
 {
   return mIMEPreference;
 }
 
 NS_IMETHODIMP
 PuppetWidget::NotifyIMEOfTextChange(uint32_t aStart,
--- a/widget/xpwidgets/PuppetWidget.h
+++ b/widget/xpwidgets/PuppetWidget.h
@@ -178,16 +178,17 @@ public:
 private:
   nsresult Paint();
 
   void SetChild(PuppetWidget* aChild);
 
   nsresult IMEEndComposition(bool aCancel);
   nsresult NotifyIMEOfFocusChange(bool aFocus);
   nsresult NotifyIMEOfSelectionChange();
+  nsresult NotifyIMEOfUpdateComposition();
 
   class PaintTask : public nsRunnable {
   public:
     NS_DECL_NSIRUNNABLE
     PaintTask(PuppetWidget* widget) : mWidget(widget) {}
     void Revoke() { mWidget = nullptr; }
   private:
     PuppetWidget* mWidget;
--- a/widget/xpwidgets/moz.build
+++ b/widget/xpwidgets/moz.build
@@ -71,16 +71,17 @@ if not CONFIG['MOZ_B2G']:
 MSVC_ENABLE_PGO = True
 
 LIBRARY_NAME = 'xpwidgets_s'
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '../shared',
+    '/dom/events',
     '/layout/base',
     '/layout/forms',
     '/layout/generic',
     '/layout/xul',
     '/view/src',
 ]
 
 widget_dir = CONFIG['MOZ_WIDGET_TOOLKIT']