Bug 1067231 - Send touch caret tap event. r=ehsan
authorMorris Tseng <mtseng@mozilla.com>
Sun, 12 Oct 2014 23:44:00 +0200
changeset 210105 ad33a3d9202c917f0ede00fb1669d577874ed856
parent 210104 6ee9fe0af6b84028ad67e9e5154d195a3bc4e029
child 210106 44eef0bd1b1df0f0399d5891363540c006ba9074
push id50339
push usercbook@mozilla.com
push dateMon, 13 Oct 2014 13:35:20 +0000
treeherdermozilla-inbound@44eef0bd1b1d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1067231
milestone35.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 1067231 - Send touch caret tap event. r=ehsan
b2g/chrome/content/shell.js
dom/browser-element/BrowserElementChildPreload.js
dom/browser-element/BrowserElementParent.jsm
layout/base/TouchCaret.cpp
layout/base/TouchCaret.h
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -328,16 +328,17 @@ var shell = {
     window.addEventListener('MozApplicationManifest', this);
     window.addEventListener('mozfullscreenchange', this);
     window.addEventListener('MozAfterPaint', this);
     window.addEventListener('sizemodechange', this);
     window.addEventListener('unload', this);
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.addEventListener('mozbrowserselectionchange', this, true);
     this.contentBrowser.addEventListener('mozbrowserscrollviewchange', this, true);
+    this.contentBrowser.addEventListener('mozbrowsertouchcarettap', this, true);
 
     CustomEventManager.init();
     WebappsHelper.init();
     UserAgentOverrides.init();
     IndexedDBPromptHelper.init();
     CaptivePortalLoginHelper.init();
 
     this.contentBrowser.src = homeURL;
@@ -356,16 +357,17 @@ var shell = {
     window.removeEventListener('keypress', this, true);
     window.removeEventListener('keyup', this, true);
     window.removeEventListener('MozApplicationManifest', this);
     window.removeEventListener('mozfullscreenchange', this);
     window.removeEventListener('sizemodechange', this);
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserselectionchange', this, true);
     this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
+    this.contentBrowser.removeEventListener('mozbrowsertouchcarettap', this, true);
     ppmm.removeMessageListener("content-handler", this);
 
     UserAgentOverrides.uninit();
     IndexedDBPromptHelper.uninit();
   },
 
   // If this key event actually represents a hardware button, filter it here
   // and send a mozChromeEvent with detail.type set to xxx-button-press or
@@ -503,16 +505,22 @@ var shell = {
         this.notifyContentStart();
        break;
       case 'mozbrowserscrollviewchange':
         this.sendChromeEvent({
           type: 'scrollviewchange',
           detail: evt.detail,
         });
         break;
+      case 'mozbrowsertouchcarettap':
+        this.sendChromeEvent({
+          type: 'touchcarettap',
+          detail: evt.detail,
+        });
+        break;
       case 'mozbrowserselectionchange':
         // The mozbrowserselectionchange event, may have crossed the chrome-content boundary.
         // This event always dispatch to shell.js. But the offset we got from this event is
         // based on tab's coordinate. So get the actual offsets between shell and evt.target.
         let elt = evt.target;
         let win = elt.ownerDocument.defaultView;
         let offsetX = win.mozInnerScreenX - window.mozInnerScreenX;
         let offsetY = win.mozInnerScreenY - window.mozInnerScreenY;
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -221,16 +221,22 @@ BrowserElementChild.prototype = {
                      /* useCapture = */ false,
                      /* wantsUntrusted = */ false);
 
     addEventListener('scrollviewchange',
                      this._ScrollViewChangeHandler.bind(this),
                      /* useCapture = */ false,
                      /* wantsUntrusted = */ false);
 
+    addEventListener('touchcarettap',
+                     this._touchCaretTapHandler.bind(this),
+                     /* useCapture = */ false,
+                     /* wantsUntrusted = */ false);
+
+
     // This listens to unload events from our message manager, but /not/ from
     // the |content| window.  That's because the window's unload event doesn't
     // bubble, and we're not using a capturing listener.  If we'd used
     // useCapture == true, we /would/ hear unload events from the window, which
     // is not what we want!
     addEventListener('unload',
                      this._unloadHandler.bind(this),
                      /* useCapture = */ false,
@@ -626,16 +632,21 @@ BrowserElementChild.prototype = {
 
     if (lang) {
       meta.lang = lang;
     }
 
     sendAsyncMsg('metachange', meta);
   },
 
+  _touchCaretTapHandler: function(e) {
+    e.stopPropagation();
+    sendAsyncMsg('touchcarettap');
+  },
+
   _ScrollViewChangeHandler: function(e) {
     e.stopPropagation();
     let detail = {
       state: e.state,
       scrollX: e.scrollX,
       scrollY: e.scrollY,
     };
     sendAsyncMsg('scrollviewchange', detail);
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -253,17 +253,18 @@ BrowserElementParent.prototype = {
       "got-can-go-forward": this._gotDOMRequestResult,
       "fullscreen-origin-change": this._remoteFullscreenOriginChange,
       "rollback-fullscreen": this._remoteFrameFullscreenReverted,
       "exit-fullscreen": this._exitFullscreen,
       "got-visible": this._gotDOMRequestResult,
       "visibilitychange": this._childVisibilityChange,
       "got-set-input-method-active": this._gotDOMRequestResult,
       "selectionchange": this._handleSelectionChange,
-      "scrollviewchange": this._handleScrollViewChange
+      "scrollviewchange": this._handleScrollViewChange,
+      "touchcarettap": this._handleTouchCaretTap
     };
 
     let mmSecuritySensitiveCalls = {
       "showmodalprompt": this._handleShowModalPrompt,
       "contextmenu": this._fireCtxMenuEvent,
       "securitychange": this._fireEventFromMsg,
       "locationchange": this._fireEventFromMsg,
       "iconchange": this._fireEventFromMsg,
@@ -501,16 +502,22 @@ BrowserElementParent.prototype = {
   },
 
   _handleScrollViewChange: function(data) {
     let evt = this._createEvent("scrollviewchange", data.json,
                                 /* cancelable = */ false);
     this._frameElement.dispatchEvent(evt);
   },
 
+  _handleTouchCaretTap: function(data) {
+    let evt = this._createEvent("touchcarettap", data.json,
+                                /* cancelable = */ false);
+    this._frameElement.dispatchEvent(evt);
+  },
+
   _createEvent: function(evtName, detail, cancelable) {
     // This will have to change if we ever want to send a CustomEvent with null
     // detail.  For now, it's OK.
     if (detail !== undefined && detail !== null) {
       detail = Cu.cloneInto(detail, this._window);
       return new this._window.CustomEvent('mozbrowser' + evtName,
                                           { bubbles: true,
                                             cancelable: cancelable,
--- a/layout/base/TouchCaret.cpp
+++ b/layout/base/TouchCaret.cpp
@@ -25,16 +25,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/BasicEvents.h"
 #include "nsIDOMWindow.h"
 #include "nsQueryContentEventResult.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsView.h"
 #include "nsDOMTokenList.h"
 #include "nsCaret.h"
+#include "mozilla/dom/CustomEvent.h"
 
 using namespace mozilla;
 
 // To enable all the TOUCHCARET_LOG print statements, change the 0 to 1 in the
 // following #define.
 #define ENABLE_TOUCHCARET_LOG 0
 
 #if ENABLE_TOUCHCARET_LOG
@@ -53,17 +54,18 @@ NS_IMPL_ISUPPORTS(TouchCaret, nsISelecti
 
 /*static*/ int32_t TouchCaret::sTouchCaretInflateSize = 0;
 /*static*/ int32_t TouchCaret::sTouchCaretExpirationTime = 0;
 
 TouchCaret::TouchCaret(nsIPresShell* aPresShell)
   : mState(TOUCHCARET_NONE),
     mActiveTouchId(-1),
     mCaretCenterToDownPointOffsetY(0),
-    mVisible(false)
+    mVisible(false),
+    mIsValidTap(false)
 {
   TOUCHCARET_LOG("Constructor, PresShell=%p", aPresShell);
   MOZ_ASSERT(NS_IsMainThread());
 
   static bool addedTouchCaretPref = false;
   if (!addedTouchCaretPref) {
     Preferences::AddIntVarCache(&sTouchCaretInflateSize,
                                 "touchcaret.inflatesize.threshold");
@@ -596,16 +598,17 @@ TouchCaret::HandleMouseMoveEvent(WidgetM
     case TOUCHCARET_MOUSEDRAG_ACTIVE:
       {
         nsPoint movePoint = GetEventPosition(aEvent);
         movePoint.y += mCaretCenterToDownPointOffsetY;
         nsRect contentBoundary = GetContentBoundary();
         movePoint = contentBoundary.ClampPoint(movePoint);
 
         MoveCaret(movePoint);
+        mIsValidTap = false;
         status = nsEventStatus_eConsumeNoDefault;
       }
       break;
 
     case TOUCHCARET_TOUCHDRAG_ACTIVE:
     case TOUCHCARET_TOUCHDRAG_INACTIVE:
       // Consume mouse event in touch sequence.
       status = nsEventStatus_eConsumeNoDefault;
@@ -633,16 +636,17 @@ TouchCaret::HandleTouchMoveEvent(WidgetT
     case TOUCHCARET_TOUCHDRAG_ACTIVE:
       {
         nsPoint movePoint = GetEventPosition(aEvent, mActiveTouchId);
         movePoint.y += mCaretCenterToDownPointOffsetY;
         nsRect contentBoundary = GetContentBoundary();
         movePoint = contentBoundary.ClampPoint(movePoint);
 
         MoveCaret(movePoint);
+        mIsValidTap = false;
         status = nsEventStatus_eConsumeNoDefault;
       }
       break;
 
     case TOUCHCARET_TOUCHDRAG_INACTIVE:
       // Consume NS_TOUCH_MOVE event in TOUCHCARET_TOUCHDRAG_INACTIVE state.
       status = nsEventStatus_eConsumeNoDefault;
       break;
@@ -844,16 +848,45 @@ TouchCaret::HandleTouchDownEvent(WidgetT
       mTouchesId.AppendElement(aEvent->touches[i]->mIdentifier);
     }
   }
 
   return status;
 }
 
 void
+TouchCaret::DispatchTapEvent()
+{
+  nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
+  if (!presShell) {
+    return;
+  }
+
+  nsCOMPtr<nsIDocument> doc = presShell->GetDocument();
+  if (!doc) {
+    return;
+  }
+
+  ErrorResult res;
+  nsRefPtr<dom::Event> domEvent =
+    doc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), res);
+  if (res.Failed()) {
+    return;
+  }
+
+  dom::CustomEvent* customEvent = static_cast<dom::CustomEvent*>(domEvent.get());
+  customEvent->InitCustomEvent(NS_LITERAL_STRING("touchcarettap"),
+                               true, false, nullptr);
+  customEvent->SetTrusted(true);
+  customEvent->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
+  bool ret;
+  doc->DispatchEvent(domEvent, &ret);
+}
+
+void
 TouchCaret::SetState(TouchCaretState aState)
 {
   TOUCHCARET_LOG("state changed from %d to %d", mState, aState);
   if (mState == TOUCHCARET_NONE) {
     MOZ_ASSERT(aState != TOUCHCARET_TOUCHDRAG_INACTIVE,
                "mState: NONE => TOUCHDRAG_INACTIVE isn't allowed!");
   }
 
@@ -874,10 +907,17 @@ TouchCaret::SetState(TouchCaretState aSt
                "TOUCHDRAG_INACTIVE allowed next state: NONE!");
   }
 
   mState = aState;
 
   if (mState == TOUCHCARET_NONE) {
     mActiveTouchId = -1;
     mCaretCenterToDownPointOffsetY = 0;
+    if (mIsValidTap) {
+      DispatchTapEvent();
+      mIsValidTap = false;
+    }
+  } else if (mState == TOUCHCARET_TOUCHDRAG_ACTIVE ||
+             mState == TOUCHCARET_MOUSEDRAG_ACTIVE) {
+    mIsValidTap = true;
   }
 }
--- a/layout/base/TouchCaret.h
+++ b/layout/base/TouchCaret.h
@@ -205,16 +205,21 @@ private:
   };
 
   /**
    * Do actual state transition and reset substates.
    */
   void SetState(TouchCaretState aState);
 
   /**
+   * Dispatch touch caret tap event to chrome.
+   */
+  void DispatchTapEvent();
+
+  /**
    * Current state we're dealing with.
    */
   TouchCaretState mState;
 
   /**
    * Array containing all active touch IDs. When a touch happens, it gets added
    * to this array, even if we choose not to handle it. When it ends, we remove
    * it. We need to maintain this array in order to detect the end of the
@@ -244,16 +249,18 @@ private:
   {
     return sTouchCaretExpirationTime;
   }
 
   nsWeakPtr mPresShell;
 
   // Touch caret visibility
   bool mVisible;
+  // Use for detecting single tap on touch caret.
+  bool mIsValidTap;
   // Touch caret timer
   nsCOMPtr<nsITimer> mTouchCaretExpirationTimer;
 
   // Preference
   static int32_t sTouchCaretInflateSize;
   static int32_t sTouchCaretExpirationTime;
 
   // The auto scroll timer's interval in miliseconds.