Bug 1067231 - Send touch caret tap event. r=ehsan
authorMorris Tseng <mtseng@mozilla.com>
Tue, 07 Oct 2014 02:28:00 -0400
changeset 209648 5d276cd3759b7a1abdba67badd95d986c8a47d23
parent 209647 52b5b862e170fb0231402eb5484f0dcbdb5659b9
child 209649 ea71dddb40aa31514c686c0de5c7616f40a0a64f
push id27619
push userkwierso@gmail.com
push dateThu, 09 Oct 2014 23:42:28 +0000
treeherdermozilla-central@c0a98d9b9a33 [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 DONTBUILD
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,
@@ -500,16 +501,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<Event> domEvent =
+    doc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), res);
+  if (res.Failed()) {
+    return;
+  }
+
+  CustomEvent* customEvent = static_cast<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.