Bug 1130935 part.6 Selection change notification should have selection range and writing mode information r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 15 May 2015 10:18:08 +0900
changeset 243956 7097594f13d4ed7bda8479fc97934019c771ad92
parent 243955 c72c0cec7d618206ec7e0216c1ca8f14bbe5148f
child 243957 82939d59771ad5bdfc607e4d9536b7659c1bc038
push id59807
push usermasayuki@d-toybox.com
push dateFri, 15 May 2015 01:18:23 +0000
treeherdermozilla-inbound@c6c67158efef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1130935
milestone41.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 1130935 part.6 Selection change notification should have selection range and writing mode information r=smaug
dom/events/IMEContentObserver.cpp
dom/events/IMEContentObserver.h
dom/ipc/TabParent.cpp
layout/generic/WritingModes.h
widget/PuppetWidget.cpp
widget/nsBaseWidget.cpp
widget/nsGUIEventIPC.h
widget/nsIWidget.h
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -27,16 +27,17 @@
 #include "nsIPresShell.h"
 #include "nsISelectionController.h"
 #include "nsISelectionPrivate.h"
 #include "nsISupports.h"
 #include "nsIWidget.h"
 #include "nsPresContext.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
+#include "WritingModes.h"
 
 namespace mozilla {
 
 using namespace widget;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
@@ -242,16 +243,22 @@ IMEContentObserver::UnregisterObservers(
   }
 
   if (mUpdatePreference.WantPositionChanged() && mDocShell) {
     mDocShell->RemoveWeakScrollObserver(this);
     mDocShell->RemoveWeakReflowObserver(this);
   }
 }
 
+nsPresContext*
+IMEContentObserver::GetPresContext() const
+{
+  return mESM ? mESM->GetPresContext() : nullptr;
+}
+
 void
 IMEContentObserver::Destroy()
 {
   // WARNING: When you change this method, you have to check Unlink() too.
 
   UnregisterObservers(false);
 
   mEditor = nullptr;
@@ -326,22 +333,49 @@ public:
     : mDispatcher(aDispatcher)
     , mCausedByComposition(aCausedByComposition)
   {
     MOZ_ASSERT(mDispatcher);
   }
 
   NS_IMETHOD Run()
   {
-    if (mDispatcher->GetWidget()) {
-      IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
-      notification.mSelectionChangeData.mCausedByComposition =
-         mCausedByComposition;
-      mDispatcher->GetWidget()->NotifyIME(notification);
+    nsCOMPtr<nsIWidget> widget = mDispatcher->GetWidget();
+    nsPresContext* presContext = mDispatcher->GetPresContext();
+    if (!widget || !presContext) {
+      return NS_OK;
+    }
+
+    // XXX Cannot we cache some information for reducing the cost to compute
+    //     selection offset and writing mode?
+    WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, widget);
+    ContentEventHandler handler(presContext);
+    handler.OnQuerySelectedText(&selection);
+    if (NS_WARN_IF(!selection.mSucceeded)) {
+      return NS_OK;
     }
+
+    // The widget might be destroyed during querying the content since it
+    // causes flushing layout.
+    widget = mDispatcher->GetWidget();
+    if (!widget || NS_WARN_IF(widget->Destroyed())) {
+      return NS_OK;
+    }
+
+    IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
+    notification.mSelectionChangeData.mOffset =
+      selection.mReply.mOffset;
+    notification.mSelectionChangeData.mLength =
+      selection.mReply.mString.Length();
+    notification.mSelectionChangeData.SetWritingMode(
+                                        selection.GetWritingMode());
+    notification.mSelectionChangeData.mReversed = selection.mReply.mReversed;
+    notification.mSelectionChangeData.mCausedByComposition =
+      mCausedByComposition;
+    widget->NotifyIME(notification);
     return NS_OK;
   }
 
 private:
   nsRefPtr<IMEContentObserver> mDispatcher;
   bool mCausedByComposition;
 };
 
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -74,16 +74,17 @@ public:
   void DisconnectFromEventStateManager();
   bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent);
   bool IsEditorHandlingEventForComposition() const;
   bool KeepAliveDuringDeactive() const
   {
     return mUpdatePreference.WantDuringDeactive();
   }
   nsIWidget* GetWidget() const { return mWidget; }
+  nsPresContext* GetPresContext() const;
   nsresult GetSelectionAndRoot(nsISelection** aSelection,
                                nsIContent** aRoot) const;
 
   struct TextChangeData
   {
     // mStartOffset is the start offset of modified or removed text in
     // original content and inserted text in new content.
     uint32_t mStartOffset;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1889,16 +1889,25 @@ TabParent::RecvNotifyIMESelection(const 
     mIMESelectionFocus = aFocus;
     mWritingMode = aWritingMode;
     const nsIMEUpdatePreference updatePreference =
       widget->GetIMEUpdatePreference();
     if (updatePreference.WantSelectionChange() &&
         (updatePreference.WantChangesCausedByComposition() ||
          !aCausedByComposition)) {
       IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
+      notification.mSelectionChangeData.mOffset =
+        std::min(mIMESelectionAnchor, mIMESelectionFocus);
+      notification.mSelectionChangeData.mLength =
+        mIMESelectionAnchor > mIMESelectionFocus ?
+          mIMESelectionAnchor - mIMESelectionFocus :
+          mIMESelectionFocus - mIMESelectionAnchor;
+      notification.mSelectionChangeData.mReversed =
+        mIMESelectionFocus < mIMESelectionAnchor;
+      notification.mSelectionChangeData.SetWritingMode(mWritingMode);
       notification.mSelectionChangeData.mCausedByComposition =
         aCausedByComposition;
       widget->NotifyIME(notification);
     }
   }
   return true;
 }
 
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -25,16 +25,20 @@
 // such methods should only be used by other methods that have already checked
 // the writing modes.)
 
 #define CHECK_WRITING_MODE(param) \
    NS_ASSERTION(param == GetWritingMode(), "writing-mode mismatch")
 
 namespace mozilla {
 
+namespace widget {
+struct IMENotification;
+} // namespace widget
+
 // Physical axis constants.
 enum PhysicalAxis {
   eAxisVertical      = 0x0,
   eAxisHorizontal    = 0x1
 };
 
 inline bool IsInline(LogicalSide aSide) { return aSide & 0x2; }
 inline bool IsBlock(LogicalSide aSide) { return !IsInline(aSide); }
@@ -470,16 +474,20 @@ public:
 
 private:
   friend class LogicalPoint;
   friend class LogicalSize;
   friend class LogicalMargin;
   friend class LogicalRect;
 
   friend struct IPC::ParamTraits<WritingMode>;
+  // IMENotification cannot store this class directly since this has some
+  // constructors.  Therefore, it stores mWritingMode and recreate the
+  // instance from it.
+  friend struct widget::IMENotification;
 
   /**
    * Return a WritingMode representing an unknown value.
    */
   static inline WritingMode Unknown()
   {
     return WritingMode(eUnknownWritingMode);
   }
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -881,29 +881,22 @@ PuppetWidget::NotifyIMEOfSelectionChange
 
 #ifndef MOZ_CROSS_PROCESS_IME
   return NS_OK;
 #endif
 
   if (!mTabChild)
     return NS_ERROR_FAILURE;
 
-  nsEventStatus status;
-  WidgetQueryContentEvent queryEvent(true, NS_QUERY_SELECTED_TEXT, this);
-  InitEvent(queryEvent, nullptr);
-  DispatchEvent(&queryEvent, status);
-
-  if (queryEvent.mSucceeded) {
-    mTabChild->SendNotifyIMESelection(
-      mIMELastReceivedSeqno,
-      queryEvent.GetSelectionStart(),
-      queryEvent.GetSelectionEnd(),
-      queryEvent.GetWritingMode(),
-      aIMENotification.mSelectionChangeData.mCausedByComposition);
-  }
+  mTabChild->SendNotifyIMESelection(
+    mIMELastReceivedSeqno,
+    aIMENotification.mSelectionChangeData.StartOffset(),
+    aIMENotification.mSelectionChangeData.EndOffset(),
+    aIMENotification.mSelectionChangeData.GetWritingMode(),
+    aIMENotification.mSelectionChangeData.mCausedByComposition);
   return NS_OK;
 }
 
 nsresult
 PuppetWidget::NotifyIMEOfMouseButtonEvent(
                 const IMENotification& aIMENotification)
 {
   if (!mTabChild) {
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -49,16 +49,17 @@
 #include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/ChromeProcessController.h"
 #include "mozilla/layers/InputAPZContext.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/dom/TabParent.h"
 #include "nsRefPtrHashtable.h"
 #include "TouchEvents.h"
+#include "WritingModes.h"
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
 #ifdef DEBUG
 #include "nsIObserver.h"
 
 static void debug_RegisterPrefCallbacks();
@@ -93,16 +94,35 @@ bool            gDisableNativeTheme     
 #define TOUCH_INJECT_PUMP_TIMER_MSEC 50
 #define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500
 int32_t nsIWidget::sPointerIdCounter = 0;
 
 // Some statics from nsIWidget.h
 /*static*/ uint64_t AutoObserverNotifier::sObserverId = 0;
 /*static*/ nsDataHashtable<nsUint64HashKey, nsCOMPtr<nsIObserver>> AutoObserverNotifier::sSavedObservers;
 
+namespace mozilla {
+namespace widget {
+
+void
+IMENotification::SelectionChangeData::SetWritingMode(
+                                        const WritingMode& aWritingMode)
+{
+  mWritingMode = aWritingMode.mWritingMode;
+}
+
+WritingMode
+IMENotification::SelectionChangeData::GetWritingMode() const
+{
+  return WritingMode(mWritingMode);
+}
+
+} // namespace widget
+} // namespace mozilla
+
 nsAutoRollup::nsAutoRollup()
 {
   // remember if mLastRollup was null, and only clear it upon destruction
   // if so. This prevents recursive usage of nsAutoRollup from clearing
   // mLastRollup when it shouldn't.
   wasClear = !nsBaseWidget::mLastRollup;
 }
 
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -670,16 +670,20 @@ struct ParamTraits<mozilla::widget::IMEN
   typedef mozilla::widget::IMENotification paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg,
       static_cast<mozilla::widget::IMEMessageType>(aParam.mMessage));
     switch (aParam.mMessage) {
       case mozilla::widget::NOTIFY_IME_OF_SELECTION_CHANGE:
+        WriteParam(aMsg, aParam.mSelectionChangeData.mOffset);
+        WriteParam(aMsg, aParam.mSelectionChangeData.mLength);
+        WriteParam(aMsg, aParam.mSelectionChangeData.mWritingMode);
+        WriteParam(aMsg, aParam.mSelectionChangeData.mReversed);
         WriteParam(aMsg, aParam.mSelectionChangeData.mCausedByComposition);
         return;
       case mozilla::widget::NOTIFY_IME_OF_TEXT_CHANGE:
         WriteParam(aMsg, aParam.mTextChangeData.mStartOffset);
         WriteParam(aMsg, aParam.mTextChangeData.mOldEndOffset);
         WriteParam(aMsg, aParam.mTextChangeData.mNewEndOffset);
         WriteParam(aMsg, aParam.mTextChangeData.mCausedByComposition);
         return;
@@ -706,16 +710,24 @@ struct ParamTraits<mozilla::widget::IMEN
     mozilla::widget::IMEMessageType IMEMessage = 0;
     if (!ReadParam(aMsg, aIter, &IMEMessage)) {
       return false;
     }
     aResult->mMessage = static_cast<mozilla::widget::IMEMessage>(IMEMessage);
     switch (aResult->mMessage) {
       case mozilla::widget::NOTIFY_IME_OF_SELECTION_CHANGE:
         return ReadParam(aMsg, aIter,
+                         &aResult->mSelectionChangeData.mOffset) &&
+               ReadParam(aMsg, aIter,
+                         &aResult->mSelectionChangeData.mLength) &&
+               ReadParam(aMsg, aIter,
+                         &aResult->mSelectionChangeData.mWritingMode) &&
+               ReadParam(aMsg, aIter,
+                         &aResult->mSelectionChangeData.mReversed) &&
+               ReadParam(aMsg, aIter,
                          &aResult->mSelectionChangeData.mCausedByComposition);
       case mozilla::widget::NOTIFY_IME_OF_TEXT_CHANGE:
         return ReadParam(aMsg, aIter,
                          &aResult->mTextChangeData.mStartOffset) &&
                ReadParam(aMsg, aIter,
                          &aResult->mTextChangeData.mOldEndOffset) &&
                ReadParam(aMsg, aIter,
                          &aResult->mTextChangeData.mNewEndOffset) &&
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -33,16 +33,17 @@ class   imgIContainer;
 class   nsIContent;
 class   ViewWrapper;
 class   nsIWidgetListener;
 class   nsIntRegion;
 class   nsIScreen;
 
 namespace mozilla {
 class CompositorVsyncDispatcher;
+class WritingMode;
 namespace dom {
 class TabChild;
 }
 namespace plugins {
 class PluginWidgetChild;
 }
 namespace layers {
 class Composer2D;
@@ -590,16 +591,20 @@ struct IMENotification
     : mMessage(static_cast<IMEMessage>(-1))
   {}
 
   MOZ_IMPLICIT IMENotification(IMEMessage aMessage)
     : mMessage(aMessage)
   {
     switch (aMessage) {
       case NOTIFY_IME_OF_SELECTION_CHANGE:
+        mSelectionChangeData.mOffset = UINT32_MAX;
+        mSelectionChangeData.mLength = 0;
+        mSelectionChangeData.mWritingMode = 0;
+        mSelectionChangeData.mReversed = false;
         mSelectionChangeData.mCausedByComposition = false;
         break;
       case NOTIFY_IME_OF_TEXT_CHANGE:
         mTextChangeData.mStartOffset = 0;
         mTextChangeData.mOldEndOffset = 0;
         mTextChangeData.mNewEndOffset = 0;
         mTextChangeData.mCausedByComposition = false;
         break;
@@ -613,23 +618,50 @@ struct IMENotification
         mMouseButtonEventData.mModifiers = 0;
       default:
         break;
     }
   }
 
   IMEMessage mMessage;
 
+  // NOTIFY_IME_OF_SELECTION_CHANGE specific data
+  struct SelectionChangeData
+  {
+    // Selection range.
+    uint32_t mOffset;
+    uint32_t mLength;
+
+    // Writing mode at the selection.
+    uint8_t mWritingMode;
+
+    bool mReversed;
+    bool mCausedByComposition;
+
+    void SetWritingMode(const WritingMode& aWritingMode);
+    WritingMode GetWritingMode() const;
+
+    uint32_t StartOffset() const
+    {
+      return mOffset + (mReversed ? mLength : 0);
+    }
+    uint32_t EndOffset() const
+    {
+      return mOffset + (mReversed ? 0 : mLength);
+    }
+    bool IsInInt32Range() const
+    {
+      return mOffset + mLength <= INT32_MAX;
+    }
+  };
+
   union
   {
     // NOTIFY_IME_OF_SELECTION_CHANGE specific data
-    struct
-    {
-      bool mCausedByComposition;
-    } mSelectionChangeData;
+    SelectionChangeData mSelectionChangeData;
 
     // NOTIFY_IME_OF_TEXT_CHANGE specific data
     struct
     {
       uint32_t mStartOffset;
       uint32_t mOldEndOffset;
       uint32_t mNewEndOffset;