Bug 1027631 - Send touch caret tap event. r=ehsan
--- 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.