Bug 826657 part.2 Implement NOTIFY_IME_OF_MOUSE_BUTTON_EVENT in XP part r=smaug+ehsan
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 29 Aug 2014 19:08:43 +0900
changeset 202340 763a2f2ae2313bf415d7157c4bb7e79f48077d3e
parent 202339 44808b493a7ee4f8abc06bea4b765674e0aff782
child 202341 e0461ce2e137348f8507bc850d8373daee959baa
push id27397
push userryanvm@gmail.com
push dateFri, 29 Aug 2014 19:05:08 +0000
treeherdermozilla-central@11e4f1678eab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs826657
milestone34.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 826657 part.2 Implement NOTIFY_IME_OF_MOUSE_BUTTON_EVENT in XP part r=smaug+ehsan
dom/events/IMEContentObserver.cpp
dom/events/IMEContentObserver.h
dom/events/IMEStateManager.cpp
dom/events/IMEStateManager.h
editor/libeditor/nsEditorEventListener.cpp
editor/libeditor/nsEditorEventListener.h
editor/libeditor/nsHTMLEditorEventListener.cpp
mobile/android/base/GeckoEditable.java
widget/nsIWidget.h
xpcom/base/ErrorList.h
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -5,17 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContentEventHandler.h"
 #include "IMEContentObserver.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/IMEStateManager.h"
+#include "mozilla/MouseEvents.h"
 #include "mozilla/TextComposition.h"
+#include "mozilla/TextEvents.h"
 #include "mozilla/dom/Element.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
@@ -420,16 +422,86 @@ IMEContentObserver::Reflow(DOMHighResTim
 NS_IMETHODIMP
 IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
                                         DOMHighResTimeStamp aEnd)
 {
   MaybeNotifyIMEOfPositionChange();
   return NS_OK;
 }
 
+bool
+IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext,
+                                       WidgetMouseEvent* aMouseEvent)
+{
+  if (!mUpdatePreference.WantMouseButtonEventOnChar()) {
+    return false;
+  }
+  if (!aMouseEvent->mFlags.mIsTrusted ||
+      aMouseEvent->mFlags.mDefaultPrevented ||
+      !aMouseEvent->widget) {
+    return false;
+  }
+  // Now, we need to notify only mouse down and mouse up event.
+  switch (aMouseEvent->message) {
+    case NS_MOUSE_BUTTON_UP:
+    case NS_MOUSE_BUTTON_DOWN:
+      break;
+    default:
+      return false;
+  }
+  if (NS_WARN_IF(!mWidget)) {
+    return false;
+  }
+
+  WidgetQueryContentEvent charAtPt(true, NS_QUERY_CHARACTER_AT_POINT,
+                                   aMouseEvent->widget);
+  charAtPt.refPoint = aMouseEvent->refPoint;
+  ContentEventHandler handler(aPresContext);
+  handler.OnQueryCharacterAtPoint(&charAtPt);
+  if (NS_WARN_IF(!charAtPt.mSucceeded) ||
+      charAtPt.mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND) {
+    return false;
+  }
+
+  // The result character rect is relative to the top level widget.
+  // We should notify it with offset in the widget.
+  nsIWidget* topLevelWidget = mWidget->GetTopLevelWidget();
+  if (topLevelWidget && topLevelWidget != mWidget) {
+    charAtPt.mReply.mRect.MoveBy(
+      topLevelWidget->WidgetToScreenOffset() -
+        mWidget->WidgetToScreenOffset());
+  }
+  // The refPt is relative to its widget.
+  // We should notify it with offset in the widget.
+  if (aMouseEvent->widget != mWidget) {
+    charAtPt.refPoint += LayoutDeviceIntPoint::FromUntyped(
+      aMouseEvent->widget->WidgetToScreenOffset() -
+        mWidget->WidgetToScreenOffset());
+  }
+
+  IMENotification notification(NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
+  notification.mMouseButtonEventData.mEventMessage = aMouseEvent->message;
+  notification.mMouseButtonEventData.mOffset = charAtPt.mReply.mOffset;
+  notification.mMouseButtonEventData.mCursorPos.Set(
+    LayoutDeviceIntPoint::ToUntyped(charAtPt.refPoint));
+  notification.mMouseButtonEventData.mCharRect.Set(charAtPt.mReply.mRect);
+  notification.mMouseButtonEventData.mButton = aMouseEvent->button;
+  notification.mMouseButtonEventData.mButtons = aMouseEvent->buttons;
+  notification.mMouseButtonEventData.mModifiers = aMouseEvent->modifiers;
+
+  nsresult rv = mWidget->NotifyIME(notification);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
+  aMouseEvent->mFlags.mDefaultPrevented = consumed;
+  return consumed;
+}
+
 // Helper class, used for text change notification
 class TextChangeEvent : public nsRunnable
 {
 public:
   TextChangeEvent(IMEContentObserver* aDispatcher,
                   IMEContentObserver::TextChangeData& aData)
     : mDispatcher(aDispatcher)
     , mData(aData)
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -55,16 +55,19 @@ public:
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIREFLOWOBSERVER
 
   // nsIScrollObserver
   virtual void ScrollPositionChanged() MOZ_OVERRIDE;
 
+  bool OnMouseButtonEvent(nsPresContext* aPresContext,
+                          WidgetMouseEvent* aMouseEvent);
+
   void Init(nsIWidget* aWidget, nsPresContext* aPresContext,
             nsIContent* aContent);
   void Destroy();
   /**
    * IMEContentObserver is stored by EventStateManager during observing.
    * DisconnectFromEventStateManager() is called when EventStateManager stops
    * storing the instance.
    */
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "prlog.h"
 
 #include "mozilla/IMEStateManager.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventStates.h"
+#include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/HTMLFormElement.h"
 
 #include "HTMLInputElement.h"
 #include "IMEContentObserver.h"
@@ -467,16 +468,73 @@ IMEStateManager::OnInstalledMenuKeyboard
 
   InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
     aInstalling ? InputContextAction::MENU_GOT_PSEUDO_FOCUS :
                   InputContextAction::MENU_LOST_PSEUDO_FOCUS);
   OnChangeFocusInternal(sPresContext, sContent, action);
 }
 
 // static
+bool
+IMEStateManager::OnMouseButtonEventInEditor(nsPresContext* aPresContext,
+                                            nsIContent* aContent,
+                                            nsIDOMMouseEvent* aMouseEvent)
+{
+  PR_LOG(sISMLog, PR_LOG_ALWAYS,
+    ("ISM: IMEStateManager::OnMouseButtonEventInEditor(aPresContext=0x%p, "
+     "aContent=0x%p, aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
+     aPresContext, aContent, aMouseEvent, sPresContext, sContent));
+
+  if (sPresContext != aPresContext || sContent != aContent) {
+    PR_LOG(sISMLog, PR_LOG_DEBUG,
+      ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
+       "the mouse event isn't fired on the editor managed by ISM"));
+    return false;
+  }
+
+  if (!sActiveIMEContentObserver) {
+    PR_LOG(sISMLog, PR_LOG_DEBUG,
+      ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
+       "there is no active IMEContentObserver"));
+    return false;
+  }
+
+  if (!sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
+    PR_LOG(sISMLog, PR_LOG_DEBUG,
+      ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
+       "the active IMEContentObserver isn't managing the editor"));
+    return false;
+  }
+
+  WidgetMouseEvent* internalEvent =
+    aMouseEvent->GetInternalNSEvent()->AsMouseEvent();
+  if (NS_WARN_IF(!internalEvent)) {
+    PR_LOG(sISMLog, PR_LOG_DEBUG,
+      ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
+       "the internal event of aMouseEvent isn't WidgetMouseEvent"));
+    return false;
+  }
+
+  bool consumed =
+    sActiveIMEContentObserver->OnMouseButtonEvent(aPresContext, internalEvent);
+
+#ifdef PR_LOGGING
+  nsAutoString eventType;
+  aMouseEvent->GetType(eventType);
+  PR_LOG(sISMLog, PR_LOG_ALWAYS,
+    ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
+     "mouse event (type=%s, button=%d) is %s",
+     NS_ConvertUTF16toUTF8(eventType).get(), internalEvent->button,
+     consumed ? "consumed" : "not consumed"));
+#endif
+
+  return consumed;
+}
+
+// static
 void
 IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
                                  nsIContent* aContent,
                                  nsIDOMMouseEvent* aMouseEvent)
 {
   PR_LOG(sISMLog, PR_LOG_ALWAYS,
     ("ISM: IMEStateManager::OnClickInEditor(aPresContext=0x%p, aContent=0x%p, "
      "aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
--- a/dom/events/IMEStateManager.h
+++ b/dom/events/IMEStateManager.h
@@ -62,16 +62,23 @@ public:
                                            nsIContent** aRoot);
   // This method updates the current IME state.  However, if the enabled state
   // isn't changed by the new state, this method does nothing.
   // Note that this method changes the IME state of the active element in the
   // widget.  So, the caller must have focus.
   static void UpdateIMEState(const IMEState &aNewIMEState,
                              nsIContent* aContent);
 
+  // This method is called when user operates mouse button in focused editor
+  // and before the editor handles it.
+  // Returns true if IME consumes the event.  Otherwise, false.
+  static bool OnMouseButtonEventInEditor(nsPresContext* aPresContext,
+                                         nsIContent* aContent,
+                                         nsIDOMMouseEvent* aMouseEvent);
+
   // This method is called when user clicked in an editor.
   // aContent must be:
   //   If the editor is for <input> or <textarea>, the element.
   //   If the editor is for contenteditable, the active editinghost.
   //   If the editor is for designMode, nullptr.
   static void OnClickInEditor(nsPresContext* aPresContext,
                               nsIContent* aContent,
                               nsIDOMMouseEvent* aMouseEvent);
--- a/editor/libeditor/nsEditorEventListener.cpp
+++ b/editor/libeditor/nsEditorEventListener.cpp
@@ -90,16 +90,17 @@ DoCommandCallback(Command aCommand, void
     controller->DoCommand(commandStr);
   }
 }
 
 nsEditorEventListener::nsEditorEventListener()
   : mEditor(nullptr)
   , mCommitText(false)
   , mInTransaction(false)
+  , mMouseDownOrUpConsumedByIME(false)
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
   , mHaveBidiKeyboards(false)
   , mShouldSwitchTextDirection(false)
   , mSwitchToRTL(false)
 #endif
 {
 }
 
@@ -294,16 +295,52 @@ nsEditorEventListener::UninstallFromEdit
 already_AddRefed<nsIPresShell>
 nsEditorEventListener::GetPresShell()
 {
   NS_PRECONDITION(mEditor,
     "The caller must check whether this is connected to an editor");
   return mEditor->GetPresShell();
 }
 
+nsPresContext*
+nsEditorEventListener::GetPresContext()
+{
+  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+  return presShell ? presShell->GetPresContext() : nullptr;
+}
+
+nsIContent*
+nsEditorEventListener::GetFocusedRootContent()
+{
+  NS_ENSURE_TRUE(mEditor, nullptr);
+
+  nsCOMPtr<nsIContent> focusedContent = mEditor->GetFocusedContent();
+  if (!focusedContent) {
+    return nullptr;
+  }
+
+  nsIDocument* composedDoc = focusedContent->GetComposedDoc();
+  NS_ENSURE_TRUE(composedDoc, nullptr);
+
+  return composedDoc->HasFlag(NODE_IS_EDITABLE) ? nullptr : focusedContent;
+}
+
+bool
+nsEditorEventListener::EditorHasFocus()
+{
+  NS_PRECONDITION(mEditor,
+    "The caller must check whether this is connected to an editor");
+  nsCOMPtr<nsIContent> focusedContent = mEditor->GetFocusedContent();
+  if (!focusedContent) {
+    return false;
+  }
+  nsIDocument* composedDoc = focusedContent->GetComposedDoc();
+  return !!composedDoc;
+}
+
 /**
  *  nsISupports implementation
  */
 
 NS_IMPL_ISUPPORTS(nsEditorEventListener, nsIDOMEventListener)
 
 /**
  *  nsIDOMEventListener implementation
@@ -364,26 +401,57 @@ nsEditorEventListener::HandleEvent(nsIDO
     // keypress
     case NS_KEY_PRESS: {
       nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
       return KeyPress(keyEvent);
     }
     // mousedown
     case NS_MOUSE_BUTTON_DOWN: {
       nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
-      return MouseDown(mouseEvent);
+      NS_ENSURE_TRUE(mouseEvent, NS_OK);
+      // nsEditorEventListener may receive (1) all mousedown, mouseup and click
+      // events, (2) only mousedown event or (3) only mouseup event.
+      // mMouseDownOrUpConsumedByIME is used only for ignoring click event if
+      // preceding mousedown and/or mouseup event is consumed by IME.
+      // Therefore, even if case #2 or case #3 occurs,
+      // mMouseDownOrUpConsumedByIME is true here.  Therefore, we should always
+      // overwrite it here.
+      mMouseDownOrUpConsumedByIME = NotifyIMEOfMouseButtonEvent(mouseEvent);
+      return mMouseDownOrUpConsumedByIME ? NS_OK : MouseDown(mouseEvent);
     }
     // mouseup
     case NS_MOUSE_BUTTON_UP: {
       nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
-      return MouseUp(mouseEvent);
+      NS_ENSURE_TRUE(mouseEvent, NS_OK);
+      // See above comment in the NS_MOUSE_BUTTON_DOWN case, first.
+      // This code assumes that case #1 is occuring.  However, if case #3 may
+      // occurs after case #2 and the mousedown is consumed,
+      // mMouseDownOrUpConsumedByIME is true even though nsEditorEventListener
+      // has not received the preceding mousedown event of this mouseup event.
+      // So, mMouseDownOrUpConsumedByIME may be invalid here.  However,
+      // this is not a matter because mMouseDownOrUpConsumedByIME is referred
+      // only by NS_MOUSE_CLICK case but click event is fired only in case #1.
+      // So, before a click event is fired, mMouseDownOrUpConsumedByIME is
+      // always initialized in the NS_MOUSE_BUTTON_DOWN case if it's referred.
+      if (NotifyIMEOfMouseButtonEvent(mouseEvent)) {
+        mMouseDownOrUpConsumedByIME = true;
+      }
+      return mMouseDownOrUpConsumedByIME ? NS_OK : MouseUp(mouseEvent);
     }
     // click
     case NS_MOUSE_CLICK: {
       nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
+      NS_ENSURE_TRUE(mouseEvent, NS_OK);
+      // If the preceding mousedown event or mouseup event was consumed,
+      // editor shouldn't handle this click event.
+      if (mMouseDownOrUpConsumedByIME) {
+        mMouseDownOrUpConsumedByIME = false;
+        mouseEvent->PreventDefault();
+        return NS_OK;
+      }
       return MouseClick(mouseEvent);
     }
     // focus
     case NS_FOCUS_CONTENT:
       return Focus(aEvent);
     // blur
     case NS_BLUR_CONTENT:
       return Blur(aEvent);
@@ -587,37 +655,30 @@ nsEditorEventListener::KeyPress(nsIDOMKe
     aKeyEvent->PreventDefault();
   }
   return NS_OK;
 }
 
 nsresult
 nsEditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
 {
-  NS_ENSURE_TRUE(aMouseEvent, NS_OK);
-
   // nothing to do if editor isn't editable or clicked on out of the editor.
   if (mEditor->IsReadonly() || mEditor->IsDisabled() ||
       !mEditor->IsAcceptableInputEvent(aMouseEvent)) {
     return NS_OK;
   }
 
   // Notifies clicking on editor to IMEStateManager even when the event was
   // consumed.
-  nsCOMPtr<nsIContent> focusedContent = mEditor->GetFocusedContent();
-  if (focusedContent) {
-    nsIDocument* currentDoc = focusedContent->GetCurrentDoc();
-    nsCOMPtr<nsIPresShell> presShell = GetPresShell();
-    nsPresContext* presContext =
-      presShell ? presShell->GetPresContext() : nullptr;
-    if (presContext && currentDoc) {
-      IMEStateManager::OnClickInEditor(presContext,
-        currentDoc->HasFlag(NODE_IS_EDITABLE) ? nullptr : focusedContent,
-        aMouseEvent);
-    }
+  if (EditorHasFocus()) {
+    nsPresContext* presContext = GetPresContext();
+    if (presContext) {
+      IMEStateManager::OnClickInEditor(presContext, GetFocusedRootContent(),
+                                       aMouseEvent);
+     }
   }
 
   bool preventDefault;
   nsresult rv = aMouseEvent->GetDefaultPrevented(&preventDefault);
   if (NS_FAILED(rv) || preventDefault) {
     // We're done if 'preventdefault' is true (see for example bug 70698).
     return rv;
   }
@@ -689,21 +750,40 @@ nsEditorEventListener::HandleMiddleClick
   // again by the containing window:
   aMouseEvent->StopPropagation();
   aMouseEvent->PreventDefault();
 
   // We processed the event, whether drop/paste succeeded or not
   return NS_OK;
 }
 
+bool
+nsEditorEventListener::NotifyIMEOfMouseButtonEvent(
+                         nsIDOMMouseEvent* aMouseEvent)
+{
+  if (!EditorHasFocus()) {
+    return false;
+  }
+
+  bool defaultPrevented;
+  nsresult rv = aMouseEvent->GetDefaultPrevented(&defaultPrevented);
+  NS_ENSURE_SUCCESS(rv, false);
+  if (defaultPrevented) {
+    return false;
+  }
+  nsPresContext* presContext = GetPresContext();
+  NS_ENSURE_TRUE(presContext, false);
+  return IMEStateManager::OnMouseButtonEventInEditor(presContext,
+                                                     GetFocusedRootContent(),
+                                                     aMouseEvent);
+}
+
 nsresult
 nsEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
 {
-  NS_ENSURE_TRUE(aMouseEvent, NS_OK);
-
   mEditor->ForceCompositionEnd();
   return NS_OK;
 }
 
 nsresult
 nsEditorEventListener::HandleText(nsIDOMEvent* aTextEvent)
 {
   if (!mEditor->IsAcceptableInputEvent(aTextEvent)) {
--- a/editor/libeditor/nsEditorEventListener.h
+++ b/editor/libeditor/nsEditorEventListener.h
@@ -68,24 +68,30 @@ protected:
   nsresult DragOver(nsIDOMDragEvent* aDragEvent);
   nsresult DragExit(nsIDOMDragEvent* aDragEvent);
   nsresult Drop(nsIDOMDragEvent* aDragEvent);
   nsresult DragGesture(nsIDOMDragEvent* aDragEvent);
 
   bool CanDrop(nsIDOMDragEvent* aEvent);
   void CleanupDragDropCaret();
   already_AddRefed<nsIPresShell> GetPresShell();
+  nsPresContext* GetPresContext();
+  nsIContent* GetFocusedRootContent();
+  // Returns true if IME consumes the mouse event.
+  bool NotifyIMEOfMouseButtonEvent(nsIDOMMouseEvent* aMouseEvent);
+  bool EditorHasFocus();
   bool IsFileControlTextBox();
   bool ShouldHandleNativeKeyBindings(nsIDOMKeyEvent* aKeyEvent);
   nsresult HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent);
 
   nsEditor* mEditor; // weak
   nsRefPtr<nsCaret> mCaret;
   bool mCommitText;
   bool mInTransaction;
+  bool mMouseDownOrUpConsumedByIME;
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
   bool mHaveBidiKeyboards;
   bool mShouldSwitchTextDirection;
   bool mSwitchToRTL;
 #endif
 };
 
 #endif // nsEditorEventListener_h__
--- a/editor/libeditor/nsHTMLEditorEventListener.cpp
+++ b/editor/libeditor/nsHTMLEditorEventListener.cpp
@@ -47,18 +47,16 @@ nsHTMLEditorEventListener::GetHTMLEditor
 {
   // mEditor must be nsHTMLEditor or its subclass.
   return static_cast<nsHTMLEditor*>(mEditor);
 }
 
 nsresult
 nsHTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent)
 {
-  NS_ENSURE_TRUE(aMouseEvent, NS_OK);
-
   nsHTMLEditor* htmlEditor = GetHTMLEditor();
 
   nsCOMPtr<nsIDOMEventTarget> target;
   nsresult rv = aMouseEvent->GetTarget(getter_AddRefs(target));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
 
@@ -68,18 +66,16 @@ nsHTMLEditorEventListener::MouseUp(nsIDO
   htmlEditor->MouseUp(clientX, clientY, element);
 
   return nsEditorEventListener::MouseUp(aMouseEvent);
 }
 
 nsresult
 nsHTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
 {
-  NS_ENSURE_TRUE(aMouseEvent, NS_OK);
-
   nsHTMLEditor* htmlEditor = GetHTMLEditor();
 
   // Detect only "context menu" click
   // XXX This should be easier to do!
   // But eDOMEvents_contextmenu and NS_CONTEXTMENU is not exposed in any event
   // interface :-(
   int16_t buttonNumber;
   nsresult rv = aMouseEvent->GetButton(&buttonNumber);
@@ -199,18 +195,16 @@ nsHTMLEditorEventListener::MouseDown(nsI
   }
 
   return nsEditorEventListener::MouseDown(aMouseEvent);
 }
 
 nsresult
 nsHTMLEditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
 {
-  NS_ENSURE_TRUE(aMouseEvent, NS_OK);
-
   nsCOMPtr<nsIDOMEventTarget> target;
   nsresult rv = aMouseEvent->GetTarget(getter_AddRefs(target));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
 
   GetHTMLEditor()->DoInlineTableEditingAction(element);
 
--- a/mobile/android/base/GeckoEditable.java
+++ b/mobile/android/base/GeckoEditable.java
@@ -50,18 +50,18 @@ interface GeckoEditableClient {
 /* interface for the Editable to listen to the Gecko thread
    and also for the IC thread to listen to the Editable */
 interface GeckoEditableListener {
     // IME notification type for notifyIME(), corresponding to NotificationToIME enum in Gecko
     final int NOTIFY_IME_OPEN_VKB = -2;
     final int NOTIFY_IME_REPLY_EVENT = -1;
     final int NOTIFY_IME_OF_FOCUS = 1;
     final int NOTIFY_IME_OF_BLUR = 2;
-    final int NOTIFY_IME_TO_COMMIT_COMPOSITION = 7;
-    final int NOTIFY_IME_TO_CANCEL_COMPOSITION = 8;
+    final int NOTIFY_IME_TO_COMMIT_COMPOSITION = 8;
+    final int NOTIFY_IME_TO_CANCEL_COMPOSITION = 9;
     // IME enabled state for notifyIMEContext()
     final int IME_STATE_DISABLED = 0;
     final int IME_STATE_ENABLED = 1;
     final int IME_STATE_PASSWORD = 2;
     final int IME_STATE_PLUGIN = 3;
 
     void notifyIME(int type);
     void notifyIMEContext(int state, String typeHint,
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -221,16 +221,22 @@ struct nsIMEUpdatePreference {
   typedef uint8_t Notifications;
 
   enum MOZ_ENUM_TYPE(Notifications)
   {
     NOTIFY_NOTHING                       = 0,
     NOTIFY_SELECTION_CHANGE              = 1 << 0,
     NOTIFY_TEXT_CHANGE                   = 1 << 1,
     NOTIFY_POSITION_CHANGE               = 1 << 2,
+    // NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR is used when mouse button is pressed
+    // or released on a character in the focused editor.  The notification is
+    // notified to IME as a mouse event.  If it's consumed by IME, NotifyIME()
+    // returns NS_SUCCESS_EVENT_CONSUMED.  Otherwise, it returns NS_OK if it's
+    // handled without any error.
+    NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR    = 1 << 3,
     // Following values indicate when widget needs or doesn't need notification.
     NOTIFY_CHANGES_CAUSED_BY_COMPOSITION = 1 << 6,
     // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two
     //       or more compositions are possible.  E.g., Mac and Linux (GTK).
     NOTIFY_DURING_DEACTIVE               = 1 << 7,
     // Changes are notified in following conditions if the instance is
     // just constructed.  If some platforms don't need change notifications
     // in some of following conditions, the platform should remove following
@@ -269,16 +275,21 @@ struct nsIMEUpdatePreference {
     return !!(mWantUpdates & NOTIFY_POSITION_CHANGE);
   }
 
   bool WantChanges() const
   {
     return WantSelectionChange() || WantTextChange();
   }
 
+  bool WantMouseButtonEventOnChar() const
+  {
+    return !!(mWantUpdates & NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR);
+  }
+
   bool WantChangesCausedByComposition() const
   {
     return WantChanges() &&
              !!(mWantUpdates & NOTIFY_CHANGES_CAUSED_BY_COMPOSITION);
   }
 
   bool WantDuringDeactive() const
   {
@@ -500,16 +511,18 @@ enum IMEMessage MOZ_ENUM_TYPE(int8_t)
   // Selection in the focused editable content is changed
   NOTIFY_IME_OF_SELECTION_CHANGE,
   // Text in the focused editable content is changed
   NOTIFY_IME_OF_TEXT_CHANGE,
   // Composition string has been updated
   NOTIFY_IME_OF_COMPOSITION_UPDATE,
   // Position or size of focused element may be changed.
   NOTIFY_IME_OF_POSITION_CHANGE,
+  // Mouse button event is fired on a character in focused editor
+  NOTIFY_IME_OF_MOUSE_BUTTON_EVENT,
   // Request to commit current composition to IME
   // (some platforms may not support)
   REQUEST_TO_COMMIT_COMPOSITION,
   // Request to cancel current composition to IME
   // (some platforms may not support)
   REQUEST_TO_CANCEL_COMPOSITION
 };
 
@@ -523,16 +536,24 @@ struct IMENotification
         mSelectionChangeData.mCausedByComposition = false;
         break;
       case NOTIFY_IME_OF_TEXT_CHANGE:
         mTextChangeData.mStartOffset = 0;
         mTextChangeData.mOldEndOffset = 0;
         mTextChangeData.mNewEndOffset = 0;
         mTextChangeData.mCausedByComposition = false;
         break;
+      case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
+        mMouseButtonEventData.mEventMessage = 0;
+        mMouseButtonEventData.mOffset = UINT32_MAX;
+        mMouseButtonEventData.mCursorPos.Set(nsIntPoint(0, 0));
+        mMouseButtonEventData.mCharRect.Set(nsIntRect(0, 0, 0, 0));
+        mMouseButtonEventData.mButton = -1;
+        mMouseButtonEventData.mButtons = 0;
+        mMouseButtonEventData.mModifiers = 0;
       default:
         break;
     }
   }
 
   IMEMessage mMessage;
 
   union
@@ -560,16 +581,66 @@ struct IMENotification
       }
       bool IsInInt32Range() const
       {
         return mStartOffset <= INT32_MAX &&
                mOldEndOffset <= INT32_MAX &&
                mNewEndOffset <= INT32_MAX;
       }
     } mTextChangeData;
+
+    // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data
+    struct
+    {
+      // The value of WidgetEvent::message
+      uint32_t mEventMessage;
+      // Character offset from the start of the focused editor under the cursor
+      uint32_t mOffset;
+      // Cursor position in pixels relative to the widget
+      struct
+      {
+        int32_t mX;
+        int32_t mY;
+
+        void Set(const nsIntPoint& aPoint)
+        {
+          mX = aPoint.x;
+          mY = aPoint.y;
+        }
+        nsIntPoint AsIntPoint() const
+        {
+          return nsIntPoint(mX, mY);
+        }
+      } mCursorPos;
+      // Character rect in pixels under the cursor relative to the widget
+      struct
+      {
+        int32_t mX;
+        int32_t mY;
+        int32_t mWidth;
+        int32_t mHeight;
+
+        void Set(const nsIntRect& aRect)
+        {
+          mX = aRect.x;
+          mY = aRect.y;
+          mWidth = aRect.width;
+          mHeight = aRect.height;
+        }
+        nsIntRect AsIntRect() const
+        {
+          return nsIntRect(mX, mY, mWidth, mHeight);
+        }
+      } mCharRect;
+      // The value of WidgetMouseEventBase::button and buttons
+      int16_t mButton;
+      int16_t mButtons;
+      // The value of WidgetInputEvent::modifiers
+      Modifiers mModifiers;
+    } mMouseButtonEventData;
   };
 
   bool IsCausedByComposition() const
   {
     switch (mMessage) {
       case NOTIFY_IME_OF_SELECTION_CHANGE:
         return mSelectionChangeData.mCausedByComposition;
       case NOTIFY_IME_OF_TEXT_CHANGE:
@@ -1812,16 +1883,19 @@ public:
      *
      * If this is called with an empty string it forces a full reload of the
      * menu system.
      */
     virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) = 0;
 
     /**
      * Notify IME of the specified notification.
+     *
+     * @return If the notification is mouse button event and it's consumed by
+     *         IME, this returns NS_SUCCESS_EVENT_CONSUMED.
      */
     NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) = 0;
 
     /*
      * Notifies the input context changes.
      */
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction) = 0;
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -110,16 +110,26 @@
   ERROR(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY,                 FAILURE(7)),
 
   /* Font cmap is strangely structured - avoid this font! */
   ERROR(NS_ERROR_GFX_CMAP_MALFORMED,                      FAILURE(51)),
 #undef MODULE
 
 
   /* ======================================================================= */
+  /* 4:  NS_ERROR_MODULE_WIDGET */
+  /* ======================================================================= */
+#define MODULE  NS_ERROR_MODULE_WIDGET
+  /* Used by nsIWidget::NotifyIME(). Returned when the notification is handled
+   * and the notified event is consumed by IME. */
+  ERROR(NS_SUCCESS_EVENT_CONSUMED,                        SUCCESS(1)),
+#undef MODULE
+
+
+  /* ======================================================================= */
   /* 6: NS_ERROR_MODULE_NETWORK */
   /* ======================================================================= */
 #define MODULE NS_ERROR_MODULE_NETWORK
   /* General async request error codes:
    *
    * These error codes are commonly passed through callback methods to indicate
    * the status of some requested async request.
    *