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 110604 ce3c78a357ab896e2e446e46c4628b35472e0c45
parent 110603 bc1f210a91f6b8f5975d8e9bf9ebc541d536e3a5
child 110605 b486c3782846bf28ff8b8b8e86d304317ec0537f
push id23700
push userryanvm@gmail.com
push dateThu, 18 Oct 2012 02:10:26 +0000
treeherdermozilla-central@5142bbd4da12 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, roc
bugs705057
milestone19.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 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