Bug 705057 part.4 Emulate the behavior of nsIWidget::ResetInputState() and nsIWidget::CancelIMEComposition() if the composition is synthesized r=smaug+roc, sr=roc
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 26 Sep 2012 14:47:51 +0900
changeset 110736 ce3c78a357ab896e2e446e46c4628b35472e0c45
parent 110735 bc1f210a91f6b8f5975d8e9bf9ebc541d536e3a5
child 110737 b486c3782846bf28ff8b8b8e86d304317ec0537f
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewerssmaug, roc
bugs705057
milestone19.0a1
Bug 705057 part.4 Emulate the behavior of nsIWidget::ResetInputState() and nsIWidget::CancelIMEComposition() if the composition is synthesized r=smaug+roc, sr=roc
content/events/src/TextComposition.cpp
content/events/src/TextComposition.h
content/events/src/nsIMEStateManager.cpp
dom/base/nsDOMWindowUtils.cpp
widget/nsIWidget.h
widget/windows/TaskbarPreview.cpp
widget/xpwidgets/nsBaseWidget.cpp
widget/xpwidgets/nsBaseWidget.h
--- a/content/events/src/TextComposition.cpp
+++ b/content/events/src/TextComposition.cpp
@@ -21,26 +21,29 @@ namespace mozilla {
  ******************************************************************************/
 
 TextComposition::TextComposition(nsPresContext* aPresContext,
                                  nsINode* aNode,
                                  nsGUIEvent* aEvent) :
   mPresContext(aPresContext), mNode(aNode),
   // temporarily, we should assume that one native IME context is per native
   // widget.
-  mNativeContext(aEvent->widget)
+  mNativeContext(aEvent->widget),
+  mIsSynthesizedForTests(
+    (aEvent->flags & NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT) != 0)
 {
 }
 
 TextComposition::TextComposition(const TextComposition& aOther)
 {
   mNativeContext = aOther.mNativeContext;
   mPresContext = aOther.mPresContext;
   mNode = aOther.mNode;
   mLastData = aOther.mLastData;
+  mIsSynthesizedForTests = aOther.mIsSynthesizedForTests;
 }
 
 bool
 TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
 {
   // temporarily, we should assume that one native IME context is per one
   // native widget.
   return mNativeContext == static_cast<void*>(aWidget);
--- a/content/events/src/TextComposition.h
+++ b/content/events/src/TextComposition.h
@@ -43,16 +43,19 @@ public:
   {
     // WARNING: mPresContext may be destroying, so, be careful if you touch it.
   }
 
   nsPresContext* GetPresContext() const { return mPresContext; }
   nsINode* GetEventTargetNode() const { return mNode; }
   // The latest CompositionEvent.data value except compositionstart event.
   const nsString& GetLastData() const { return mLastData; }
+  // Returns true if the composition is started with synthesized event which
+  // came from nsDOMWindowUtils.
+  bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; }
 
   bool MatchesNativeContext(nsIWidget* aWidget) const;
   bool MatchesEventTarget(nsPresContext* aPresContext,
                           nsINode* aNode) const;
 
   /**
    * SynthesizeCommit() dispatches compositionupdate, text and compositionend
    * events for emulating commit on the content.
@@ -78,16 +81,19 @@ private:
   // 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;
 
+  // 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(nsGUIEvent* aEvent,
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -503,26 +503,93 @@ nsIMEStateManager::NotifyIME(Notificatio
                              nsIWidget* aWidget)
 {
   NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
 
   TextComposition* composition = nullptr;
   if (sTextCompositions) {
     composition = sTextCompositions->GetCompositionFor(aWidget);
   }
+  if (!composition || !composition->IsSynthesizedForTests()) {
+    switch (aNotification) {
+      case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
+        return aWidget->ResetInputState();
+      case REQUEST_TO_COMMIT_COMPOSITION:
+        return composition ? aWidget->ResetInputState() : NS_OK;
+      case REQUEST_TO_CANCEL_COMPOSITION:
+        return composition ? aWidget->CancelIMEComposition() : NS_OK;
+      default:
+        MOZ_NOT_REACHED("Unsupported notification");
+        return NS_ERROR_INVALID_ARG;
+    }
+    MOZ_NOT_REACHED(
+      "Failed to handle the notification for non-synthesized composition");
+  }
+
+  // If the composition is synthesized events for automated tests, we should
+  // dispatch composition events for emulating the native composition behavior.
+  // NOTE: The dispatched events are discarded if it's not safe to run script.
   switch (aNotification) {
-    case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
-      return aWidget->ResetInputState();
-    case REQUEST_TO_COMMIT_COMPOSITION:
-      return composition ? aWidget->ResetInputState() : NS_OK;
-    case REQUEST_TO_CANCEL_COMPOSITION:
-      return composition ? aWidget->CancelIMEComposition() : NS_OK;
+    case REQUEST_TO_COMMIT_COMPOSITION: {
+      nsCOMPtr<nsIWidget> widget(aWidget);
+      TextComposition backup = *composition;
+
+      nsEventStatus status = nsEventStatus_eIgnore;
+      if (!backup.GetLastData().IsEmpty()) {
+        nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
+        textEvent.theText = backup.GetLastData();
+        textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&textEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+      }
+
+      status = nsEventStatus_eIgnore;
+      nsCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
+      endEvent.data = backup.GetLastData();
+      endEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+      widget->DispatchEvent(&endEvent, status);
+
+      return NS_OK;
+    }
+    case REQUEST_TO_CANCEL_COMPOSITION: {
+      nsCOMPtr<nsIWidget> widget(aWidget);
+      TextComposition backup = *composition;
+
+      nsEventStatus status = nsEventStatus_eIgnore;
+      if (!backup.GetLastData().IsEmpty()) {
+        nsCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
+        updateEvent.data = backup.GetLastData();
+        updateEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&updateEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+
+        status = nsEventStatus_eIgnore;
+        nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
+        textEvent.theText = backup.GetLastData();
+        textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&textEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+      }
+
+      status = nsEventStatus_eIgnore;
+      nsCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
+      endEvent.data = backup.GetLastData();
+      endEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+      widget->DispatchEvent(&endEvent, status);
+
+      return NS_OK;
+    }
     default:
-      MOZ_NOT_REACHED("Unsupported notification");
-      return NS_ERROR_INVALID_ARG;
+      return NS_OK;
   }
 }
 
 // static
 nsresult
 nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
                              nsPresContext* aPresContext)
 {
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1593,16 +1593,18 @@ nsDOMWindowUtils::SendCompositionEvent(c
   }
 
   nsCompositionEvent compositionEvent(true, msg, widget);
   InitEvent(compositionEvent);
   if (msg != NS_COMPOSITION_START) {
     compositionEvent.data = aData;
   }
 
+  compositionEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+
   nsEventStatus status;
   nsresult rv = widget->DispatchEvent(&compositionEvent, status);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 static void
@@ -1670,16 +1672,18 @@ nsDOMWindowUtils::SendTextEvent(const ns
     textRanges.AppendElement(range);
   }
 
   textEvent.theText = aCompositionString;
 
   textEvent.rangeCount = textRanges.Length();
   textEvent.rangeArray = textRanges.Elements();
 
+  textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+
   nsEventStatus status;
   nsresult rv = widget->DispatchEvent(&textEvent, status);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -404,16 +404,17 @@ class nsIWidget : public nsISupports {
       { }
     };
 
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWIDGET_IID)
 
     nsIWidget()
       : mLastChild(nullptr)
       , mPrevSibling(nullptr)
+      , mOnDestroyCalled(false)
     {}
 
         
     /**
      * Create and initialize a widget. 
      *
      * All the arguments can be NULL in which case a top level window
      * with size 0 is created. The event callback function has to be
@@ -506,16 +507,22 @@ class nsIWidget : public nsISupports {
 
     /**
      * Close and destroy the internal native window. 
      * This method does not delete the widget.
      */
 
     NS_IMETHOD Destroy(void) = 0;
 
+    /**
+     * Destroyed() returns true if Destroy() has been called already.
+     * Otherwise, false.
+     */
+    bool Destroyed() const { return mOnDestroyCalled; }
+
 
     /**
      * Reparent a widget
      *
      * Change the widget's parent. Null parents are allowed.
      *
      * @param     aNewParent   new parent 
      */
@@ -1649,13 +1656,15 @@ protected:
     // the first element of the list, and each element holds a strong
     // ref to the next element in the list.  The prevsibling and
     // lastchild pointers are weak, which is fine as long as they are
     // maintained properly.
     nsCOMPtr<nsIWidget> mFirstChild;
     nsIWidget* mLastChild;
     nsCOMPtr<nsIWidget> mNextSibling;
     nsIWidget* mPrevSibling;
+    // When Destroy() is called, the sub class should set this true.
+    bool mOnDestroyCalled;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIWidget, NS_IWIDGET_IID)
 
 #endif // nsIWidget_h__
--- a/widget/windows/TaskbarPreview.cpp
+++ b/widget/windows/TaskbarPreview.cpp
@@ -246,17 +246,17 @@ TaskbarPreview::Disable() {
 
   return NS_OK;
 }
 
 bool
 TaskbarPreview::IsWindowAvailable() const {
   if (mWnd) {
     nsWindow* win = WinUtils::GetNSWindowPtr(mWnd);
-    if(win && !win->HasDestroyStarted()) {
+    if(win && !win->Destroyed()) {
       return true;
     }
   }
   return false;
 }
 
 void
 TaskbarPreview::DetachFromNSWindow() {
--- a/widget/xpwidgets/nsBaseWidget.cpp
+++ b/widget/xpwidgets/nsBaseWidget.cpp
@@ -85,17 +85,16 @@ nsAutoRollup::~nsAutoRollup()
 
 nsBaseWidget::nsBaseWidget()
 : mWidgetListener(nullptr)
 , mAttachedWidgetListener(nullptr)
 , mContext(nullptr)
 , mCursor(eCursor_standard)
 , mWindowType(eWindowType_child)
 , mBorderStyle(eBorderStyle_none)
-, mOnDestroyCalled(false)
 , mUseAcceleratedRendering(false)
 , mForceLayersAcceleration(false)
 , mTemporarilyUseBasicLayerManager(false)
 , mUseAttachedEvents(false)
 , mContextInitialized(false)
 , mBounds(0,0,0,0)
 , mOriginalBounds(nullptr)
 , mClipRectCount(0)
--- a/widget/xpwidgets/nsBaseWidget.h
+++ b/widget/xpwidgets/nsBaseWidget.h
@@ -223,23 +223,16 @@ public:
   public:
     AutoUseBasicLayerManager(nsBaseWidget* aWidget);
     ~AutoUseBasicLayerManager();
   private:
     nsBaseWidget* mWidget;
   };
   friend class AutoUseBasicLayerManager;
 
-  bool HasDestroyStarted() const 
-  {
-    return mOnDestroyCalled;
-  }
-
-  bool                    Destroyed() { return mOnDestroyCalled; }
-
   nsWindowType            GetWindowType() { return mWindowType; }
 
   virtual bool            UseOffMainThreadCompositing();
 protected:
 
   virtual void            ResolveIconName(const nsAString &aIconName,
                                           const nsAString &aIconSuffix,
                                           nsIFile **aResult);
@@ -336,17 +329,16 @@ protected:
   nsRefPtr<LayerManager> mBasicLayerManager;
   nsRefPtr<CompositorChild> mCompositorChild;
   nsRefPtr<CompositorParent> mCompositorParent;
   nscolor           mBackground;
   nscolor           mForeground;
   nsCursor          mCursor;
   nsWindowType      mWindowType;
   nsBorderStyle     mBorderStyle;
-  bool              mOnDestroyCalled;
   bool              mUseAcceleratedRendering;
   bool              mForceLayersAcceleration;
   bool              mTemporarilyUseBasicLayerManager;
   bool              mUseAttachedEvents;
   bool              mContextInitialized;
   nsIntRect         mBounds;
   nsIntRect*        mOriginalBounds;
   // When this pointer is null, the widget is not clipped