mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 833448 - add singleTap and doubleTap ability to marionette, r=mdas
This commit is contained in:
parent
cf02c90bbc
commit
217f5dc44c
@ -50,6 +50,12 @@ class HTMLElement(object):
|
||||
def click(self):
|
||||
return self.marionette._send_message('clickElement', 'ok', element=self.id)
|
||||
|
||||
def single_tap(self):
|
||||
return self.marionette._send_message('singleTap', 'ok', element=self.id)
|
||||
|
||||
def double_tap(self):
|
||||
return self.marionette._send_message('doubleTap', 'ok', element=self.id)
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self.marionette._send_message('getElementText', 'value', element=self.id)
|
||||
|
@ -0,0 +1,32 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
import time
|
||||
from marionette_test import MarionetteTestCase
|
||||
from marionette import HTMLElement
|
||||
from errors import MarionetteException
|
||||
|
||||
class testTouch(MarionetteTestCase):
|
||||
def test_touch(self):
|
||||
testTouch = self.marionette.absolute_url("testTouch.html")
|
||||
self.marionette.navigate(testTouch)
|
||||
button = self.marionette.find_element("id", "mozLink")
|
||||
button.single_tap()
|
||||
time.sleep(10)
|
||||
self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
|
||||
|
||||
def test_invisible(self):
|
||||
testTouch = self.marionette.absolute_url("testTouch.html")
|
||||
self.marionette.navigate(testTouch)
|
||||
ele = self.marionette.find_element("id", "testh2")
|
||||
self.assertRaises(MarionetteException, ele.single_tap)
|
||||
|
||||
def test_scrolling(self):
|
||||
testTouch = self.marionette.absolute_url("testTouch.html")
|
||||
self.marionette.navigate(testTouch)
|
||||
ele = self.marionette.find_element("id", "scroll")
|
||||
ele.single_tap()
|
||||
time.sleep(10)
|
||||
self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('scroll').innerHTML;"))
|
@ -44,6 +44,10 @@ b2g = false
|
||||
[test_timeouts.py]
|
||||
b2g = false
|
||||
|
||||
[test_touch.py]
|
||||
b2g = true
|
||||
browser = false
|
||||
|
||||
[test_simpletest_pass.js]
|
||||
[test_simpletest_sanity.py]
|
||||
[test_simpletest_chrome.js]
|
||||
|
282
testing/marionette/client/marionette/www/shim.js
Normal file
282
testing/marionette/client/marionette/www/shim.js
Normal file
@ -0,0 +1,282 @@
|
||||
/**
|
||||
* mouse_event_shim.js: generate mouse events from touch events.
|
||||
*
|
||||
* This library listens for touch events and generates mousedown, mousemove
|
||||
* mouseup, and click events to match them. It captures and dicards any
|
||||
* real mouse events (non-synthetic events with isTrusted true) that are
|
||||
* send by gecko so that there are not duplicates.
|
||||
*
|
||||
* This library does emit mouseover/mouseout and mouseenter/mouseleave
|
||||
* events. You can turn them off by setting MouseEventShim.trackMouseMoves to
|
||||
* false. This means that mousemove events will always have the same target
|
||||
* as the mousedown even that began the series. You can also call
|
||||
* MouseEventShim.setCapture() from a mousedown event handler to prevent
|
||||
* mouse tracking until the next mouseup event.
|
||||
*
|
||||
* This library does not support multi-touch but should be sufficient
|
||||
* to do drags based on mousedown/mousemove/mouseup events.
|
||||
*
|
||||
* This library does not emit dblclick events or contextmenu events
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
(function() {
|
||||
// Make sure we don't run more than once
|
||||
if (MouseEventShim)
|
||||
return;
|
||||
|
||||
// Bail if we're not on running on a platform that sends touch
|
||||
// events. We don't need the shim code for mouse events.
|
||||
try {
|
||||
document.createEvent('TouchEvent');
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
var starttouch; // The Touch object that we started with
|
||||
var target; // The element the touch is currently over
|
||||
var emitclick; // Will we be sending a click event after mouseup?
|
||||
|
||||
// Use capturing listeners to discard all mouse events from gecko
|
||||
window.addEventListener('mousedown', discardEvent, true);
|
||||
window.addEventListener('mouseup', discardEvent, true);
|
||||
window.addEventListener('mousemove', discardEvent, true);
|
||||
window.addEventListener('click', discardEvent, true);
|
||||
|
||||
function discardEvent(e) {
|
||||
if (e.isTrusted) {
|
||||
e.stopImmediatePropagation(); // so it goes no further
|
||||
if (e.type === 'click')
|
||||
e.preventDefault(); // so it doesn't trigger a change event
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for touch events that bubble up to the window.
|
||||
// If other code has called stopPropagation on the touch events
|
||||
// then we'll never see them. Also, we'll honor the defaultPrevented
|
||||
// state of the event and will not generate synthetic mouse events
|
||||
window.addEventListener('touchstart', handleTouchStart);
|
||||
window.addEventListener('touchmove', handleTouchMove);
|
||||
window.addEventListener('touchend', handleTouchEnd);
|
||||
window.addEventListener('touchcancel', handleTouchEnd); // Same as touchend
|
||||
|
||||
function handleTouchStart(e) {
|
||||
// If we're already handling a touch, ignore this one
|
||||
if (starttouch)
|
||||
return;
|
||||
|
||||
// Ignore any event that has already been prevented
|
||||
if (e.defaultPrevented)
|
||||
return;
|
||||
|
||||
// Sometimes an unknown gecko bug causes us to get a touchstart event
|
||||
// for an iframe target that we can't use because it is cross origin.
|
||||
// Don't start handling a touch in that case
|
||||
try {
|
||||
e.changedTouches[0].target.ownerDocument;
|
||||
}
|
||||
catch (e) {
|
||||
// Ignore the event if we can't see the properties of the target
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is more than one simultaneous touch, ignore all but the first
|
||||
starttouch = e.changedTouches[0];
|
||||
target = starttouch.target;
|
||||
emitclick = true;
|
||||
|
||||
// Move to the position of the touch
|
||||
emitEvent('mousemove', target, starttouch);
|
||||
|
||||
// Now send a synthetic mousedown
|
||||
var result = emitEvent('mousedown', target, starttouch);
|
||||
|
||||
// If the mousedown was prevented, pass that on to the touch event.
|
||||
// And remember not to send a click event
|
||||
if (!result) {
|
||||
e.preventDefault();
|
||||
emitclick = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchEnd(e) {
|
||||
if (!starttouch)
|
||||
return;
|
||||
|
||||
// End a MouseEventShim.setCapture() call
|
||||
if (MouseEventShim.capturing) {
|
||||
MouseEventShim.capturing = false;
|
||||
MouseEventShim.captureTarget = null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < e.changedTouches.length; i++) {
|
||||
var touch = e.changedTouches[i];
|
||||
// If the ended touch does not have the same id, skip it
|
||||
if (touch.identifier !== starttouch.identifier)
|
||||
continue;
|
||||
|
||||
emitEvent('mouseup', target, touch);
|
||||
|
||||
// If target is still the same element we started and the touch did not
|
||||
// move more than the threshold and if the user did not prevent
|
||||
// the mousedown, then send a click event, too.
|
||||
if (emitclick)
|
||||
emitEvent('click', starttouch.target, touch);
|
||||
|
||||
starttouch = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchMove(e) {
|
||||
if (!starttouch)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < e.changedTouches.length; i++) {
|
||||
var touch = e.changedTouches[i];
|
||||
// If the ended touch does not have the same id, skip it
|
||||
if (touch.identifier !== starttouch.identifier)
|
||||
continue;
|
||||
|
||||
// Don't send a mousemove if the touchmove was prevented
|
||||
if (e.defaultPrevented)
|
||||
return;
|
||||
|
||||
// See if we've moved too much to emit a click event
|
||||
var dx = Math.abs(touch.screenX - starttouch.screenX);
|
||||
var dy = Math.abs(touch.screenY - starttouch.screenY);
|
||||
if (dx > MouseEventShim.dragThresholdX ||
|
||||
dy > MouseEventShim.dragThresholdY) {
|
||||
emitclick = false;
|
||||
}
|
||||
|
||||
var tracking = MouseEventShim.trackMouseMoves &&
|
||||
!MouseEventShim.capturing;
|
||||
|
||||
if (tracking) {
|
||||
// If the touch point moves, then the element it is over
|
||||
// may have changed as well. Note that calling elementFromPoint()
|
||||
// forces a layout if one is needed.
|
||||
// XXX: how expensive is it to do this on each touchmove?
|
||||
// Can we listen for (non-standard) touchleave events instead?
|
||||
var oldtarget = target;
|
||||
var newtarget = document.elementFromPoint(touch.clientX, touch.clientY);
|
||||
if (newtarget === null) {
|
||||
// this can happen as the touch is moving off of the screen, e.g.
|
||||
newtarget = oldtarget;
|
||||
}
|
||||
if (newtarget !== oldtarget) {
|
||||
leave(oldtarget, newtarget, touch); // mouseout, mouseleave
|
||||
target = newtarget;
|
||||
}
|
||||
}
|
||||
else if (MouseEventShim.captureTarget) {
|
||||
target = MouseEventShim.captureTarget;
|
||||
}
|
||||
|
||||
emitEvent('mousemove', target, touch);
|
||||
|
||||
if (tracking && newtarget !== oldtarget) {
|
||||
enter(newtarget, oldtarget, touch); // mouseover, mouseenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if element a contains element b
|
||||
function contains(a, b) {
|
||||
return (a.compareDocumentPosition(b) & 16) !== 0;
|
||||
}
|
||||
|
||||
// A touch has left oldtarget and entered newtarget
|
||||
// Send out all the events that are required
|
||||
function leave(oldtarget, newtarget, touch) {
|
||||
emitEvent('mouseout', oldtarget, touch, newtarget);
|
||||
|
||||
// If the touch has actually left oldtarget (and has not just moved
|
||||
// into a child of oldtarget) send a mouseleave event. mouseleave
|
||||
// events don't bubble, so we have to repeat this up the hierarchy.
|
||||
for (var e = oldtarget; !contains(e, newtarget); e = e.parentNode) {
|
||||
emitEvent('mouseleave', e, touch, newtarget);
|
||||
}
|
||||
}
|
||||
|
||||
// A touch has entered newtarget from oldtarget
|
||||
// Send out all the events that are required.
|
||||
function enter(newtarget, oldtarget, touch) {
|
||||
emitEvent('mouseover', newtarget, touch, oldtarget);
|
||||
|
||||
// Emit non-bubbling mouseenter events if the touch actually entered
|
||||
// newtarget and wasn't already in some child of it
|
||||
for (var e = newtarget; !contains(e, oldtarget); e = e.parentNode) {
|
||||
emitEvent('mouseenter', e, touch, oldtarget);
|
||||
}
|
||||
}
|
||||
|
||||
function emitEvent(type, target, touch, relatedTarget) {
|
||||
var synthetic = document.createEvent('MouseEvents');
|
||||
var bubbles = (type !== 'mouseenter' && type !== 'mouseleave');
|
||||
var count =
|
||||
(type === 'mousedown' || type === 'mouseup' || type === 'click') ? 1 : 0;
|
||||
|
||||
synthetic.initMouseEvent(type,
|
||||
bubbles, // canBubble
|
||||
true, // cancelable
|
||||
window,
|
||||
count, // detail: click count
|
||||
touch.screenX,
|
||||
touch.screenY,
|
||||
touch.clientX,
|
||||
touch.clientY,
|
||||
false, // ctrlKey: we don't have one
|
||||
false, // altKey: we don't have one
|
||||
false, // shiftKey: we don't have one
|
||||
false, // metaKey: we don't have one
|
||||
0, // we're simulating the left button
|
||||
relatedTarget || null);
|
||||
|
||||
try {
|
||||
return target.dispatchEvent(synthetic);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Exception calling dispatchEvent', type, e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
var MouseEventShim = {
|
||||
// It is a known gecko bug that synthetic events have timestamps measured
|
||||
// in microseconds while regular events have timestamps measured in
|
||||
// milliseconds. This utility function returns a the timestamp converted
|
||||
// to milliseconds, if necessary.
|
||||
getEventTimestamp: function(e) {
|
||||
if (e.isTrusted) // XXX: Are real events always trusted?
|
||||
return e.timeStamp;
|
||||
else
|
||||
return e.timeStamp / 1000;
|
||||
},
|
||||
|
||||
// Set this to false if you don't care about mouseover/out events
|
||||
// and don't want the target of mousemove events to follow the touch
|
||||
trackMouseMoves: true,
|
||||
|
||||
// Call this function from a mousedown event handler if you want to guarantee
|
||||
// that the mousemove and mouseup events will go to the same element
|
||||
// as the mousedown even if they leave the bounds of the element. This is
|
||||
// like setting trackMouseMoves to false for just one drag. It is a
|
||||
// substitute for event.target.setCapture(true)
|
||||
setCapture: function(target) {
|
||||
this.capturing = true; // Will be set back to false on mouseup
|
||||
if (target)
|
||||
this.captureTarget = target;
|
||||
},
|
||||
|
||||
capturing: false,
|
||||
|
||||
// Keep these in sync with ui.dragThresholdX and ui.dragThresholdY prefs.
|
||||
// If a touch ever moves more than this many pixels from its starting point
|
||||
// then we will not synthesize a click event when the touch ends.
|
||||
dragThresholdX: 25,
|
||||
dragThresholdY: 25
|
||||
};
|
46
testing/marionette/client/marionette/www/testTouch.html
Normal file
46
testing/marionette/client/marionette/www/testTouch.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="shim.js">
|
||||
</script>
|
||||
<title>Marionette Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="testh1">Test Page</h1>
|
||||
<script type="text/javascript">
|
||||
window.ready = true;
|
||||
setTimeout(addDelayedElement, 1000);
|
||||
function addDelayedElement() {
|
||||
var newDiv = document.createElement("div");
|
||||
newDiv.id = "newDiv";
|
||||
var newContent = document.createTextNode("I am a newly created div!");
|
||||
newDiv.appendChild(newContent);
|
||||
document.body.appendChild(newDiv);
|
||||
}
|
||||
function clicked() {
|
||||
var link = document.getElementById("mozLink");
|
||||
link.innerHTML = "Clicked";
|
||||
}
|
||||
function clicked2() {
|
||||
var link2 = document.getElementById("scroll");
|
||||
link2.innerHTML = "Clicked";
|
||||
}
|
||||
</script>
|
||||
<button id="mozLink" type="button" onclick="clicked()" allowevents=true>Click Me!</button>
|
||||
<div id="testDiv">
|
||||
<a href="#" id="divLink" class="linkClass" onclick="clicked()">Div click me!</a>
|
||||
<a href="#" id="divLink2" class="linkClass" onclick="clicked()">Div click me!</a>
|
||||
</div>
|
||||
<input name="myInput" type="text" value="asdf"/>
|
||||
<input name="myCheckBox" type="checkbox" />
|
||||
<h2 id="testh2" style="visibility: hidden" class="linkClass">Hidden</h2>
|
||||
<h3 id="testh3">Voluntary Termination</h3>
|
||||
<br style="margin-bottom:600px;"/>
|
||||
<button id="scroll" type="button" onclick="clicked2()" allowevents=true>Click Me!</button>
|
||||
</body>
|
||||
</html>
|
@ -1259,6 +1259,42 @@ MarionetteDriverActor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Single Tap
|
||||
*
|
||||
* @param object aRequest
|
||||
'element' represents the ID of the element to single tap on
|
||||
*/
|
||||
singleTap: function MDA_singleTap(aRequest) {
|
||||
this.command_id = this.getCommandId();
|
||||
let serId = aRequest.element;
|
||||
if (this.context == "chrome") {
|
||||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("singleTap", {value: serId,
|
||||
command_id: this.command_id});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Double Tap
|
||||
*
|
||||
* @param object aRequest
|
||||
* 'element' represents the ID of the element to double tap on
|
||||
*/
|
||||
doubleTap: function MDA_doubleTap(aRequest) {
|
||||
this.command_id = this.getCommandId();
|
||||
let serId = aRequest.element;
|
||||
if (this.context == "chrome") {
|
||||
this.sendError("Not in Chrome", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
this.sendAsync("doubleTap", {value: serId,
|
||||
command_id: this.command_id});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Find an element using the indicated search strategy.
|
||||
*
|
||||
@ -2009,6 +2045,8 @@ MarionetteDriverActor.prototype.requestTypes = {
|
||||
"executeScript": MarionetteDriverActor.prototype.execute,
|
||||
"setScriptTimeout": MarionetteDriverActor.prototype.setScriptTimeout,
|
||||
"timeouts": MarionetteDriverActor.prototype.timeouts,
|
||||
"singleTap": MarionetteDriverActor.prototype.singleTap,
|
||||
"doubleTap": MarionetteDriverActor.prototype.doubleTap,
|
||||
"executeAsyncScript": MarionetteDriverActor.prototype.executeWithCallback,
|
||||
"executeJSScript": MarionetteDriverActor.prototype.executeJSScript,
|
||||
"setSearchTimeout": MarionetteDriverActor.prototype.setSearchTimeout,
|
||||
|
@ -54,6 +54,12 @@ let asyncTestTimeoutId;
|
||||
let originalOnError;
|
||||
//timer for doc changes
|
||||
let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
// Send move events about this often
|
||||
let EVENT_INTERVAL = 30; // milliseconds
|
||||
// The current array of all pending touches
|
||||
let touches = [];
|
||||
// For assigning unique ids to all touches
|
||||
let nextTouchId = 1000;
|
||||
|
||||
/**
|
||||
* Called when listener is first started up.
|
||||
@ -93,6 +99,8 @@ function startListeners() {
|
||||
addMessageListenerId("Marionette:executeScript", executeScript);
|
||||
addMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript);
|
||||
addMessageListenerId("Marionette:executeJSScript", executeJSScript);
|
||||
addMessageListenerId("Marionette:singleTap", singleTap);
|
||||
addMessageListenerId("Marionette:doubleTap", doubleTap);
|
||||
addMessageListenerId("Marionette:setSearchTimeout", setSearchTimeout);
|
||||
addMessageListenerId("Marionette:goUrl", goUrl);
|
||||
addMessageListenerId("Marionette:getUrl", getUrl);
|
||||
@ -180,6 +188,8 @@ function deleteSession(msg) {
|
||||
removeMessageListenerId("Marionette:executeScript", executeScript);
|
||||
removeMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript);
|
||||
removeMessageListenerId("Marionette:executeJSScript", executeJSScript);
|
||||
removeMessageListenerId("Marionette:singleTap", singleTap);
|
||||
removeMessageListenerId("Marionette:doubleTap", doubleTap);
|
||||
removeMessageListenerId("Marionette:setSearchTimeout", setSearchTimeout);
|
||||
removeMessageListenerId("Marionette:goUrl", goUrl);
|
||||
removeMessageListenerId("Marionette:getTitle", getTitle);
|
||||
@ -531,6 +541,254 @@ function executeWithCallback(msg, useFinish) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function creates a touch event given a touch type and a touch
|
||||
*/
|
||||
function emitTouchEvent(type, touch) {
|
||||
var target = touch.target;
|
||||
var doc = target.ownerDocument;
|
||||
var win = doc.defaultView;
|
||||
// Using domWindowUtils
|
||||
var domWindowUtils = curWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.screenX], [touch.screenY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function creates a touch and emit touch events
|
||||
* @param 'xt' and 'yt' are two-element array [from, to] and then is a callback that will be invoked after touchend event is sent
|
||||
*/
|
||||
function touch(target, duration, xt, yt, then) {
|
||||
var doc = target.ownerDocument;
|
||||
var win = doc.defaultView;
|
||||
var touchId = nextTouchId++;
|
||||
var x = xt;
|
||||
if (typeof xt !== 'function') {
|
||||
x = function(t) { return xt[0] + t / duration * (xt[1] - xt[0]); };
|
||||
}
|
||||
var y = yt;
|
||||
if (typeof yt !== 'function') {
|
||||
y = function(t) { return yt[0] + t / duration * (yt[1] - yt[0]); };
|
||||
}
|
||||
// viewport coordinates
|
||||
var clientX = Math.round(x(0)), clientY = Math.round(y(0));
|
||||
// document coordinates
|
||||
var pageX = clientX + win.pageXOffset,
|
||||
pageY = clientY + win.pageYOffset;
|
||||
// screen coordinates
|
||||
var screenX = clientX + win.mozInnerScreenX,
|
||||
screenY = clientY + win.mozInnerScreenY;
|
||||
// Remember the coordinates
|
||||
var lastX = clientX, lastY = clientY;
|
||||
// Create the touch object
|
||||
var touch = doc.createTouch(win, target, touchId,
|
||||
pageX, pageY,
|
||||
screenX, screenY,
|
||||
clientX, clientY);
|
||||
// Add this new touch to the list of touches
|
||||
touches.push(touch);
|
||||
// Send the start event
|
||||
emitTouchEvent('touchstart', touch);
|
||||
var startTime = Date.now();
|
||||
checkTimer.initWithCallback(nextEvent, EVENT_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
function nextEvent() {
|
||||
// Figure out if this is the last of the touchmove events
|
||||
var time = Date.now();
|
||||
var dt = time - startTime;
|
||||
var last = dt + EVENT_INTERVAL / 2 > duration;
|
||||
// Find our touch object in the touches[] array.
|
||||
// Note that its index may have changed since we pushed it
|
||||
var touchIndex = touches.indexOf(touch);
|
||||
// If this is the last move event, make sure we move all the way
|
||||
if (last)
|
||||
dt = duration;
|
||||
// New coordinates of the touch
|
||||
clientX = Math.round(x(dt));
|
||||
clientY = Math.round(y(dt));
|
||||
// If we've moved, send a move event
|
||||
if (clientX !== lastX || clientY !== lastY) { // If we moved
|
||||
lastX = clientX;
|
||||
lastY = clientY;
|
||||
pageX = clientX + win.pageXOffset;
|
||||
pageY = clientY + win.pageYOffset;
|
||||
screenX = clientX + win.mozInnerScreenX;
|
||||
screenY = clientY + win.mozInnerScreenY;
|
||||
// Since we moved, we've got to create a new Touch object
|
||||
// with the new coordinates
|
||||
touch = doc.createTouch(win, target, touchId,
|
||||
pageX, pageY,
|
||||
screenX, screenY,
|
||||
clientX, clientY);
|
||||
// Replace the old touch object with the new one
|
||||
touches[touchIndex] = touch;
|
||||
// And send the touchmove event
|
||||
emitTouchEvent('touchmove', touch);
|
||||
}
|
||||
// If that was the last move, send the touchend event
|
||||
// and call the callback
|
||||
if (last) {
|
||||
touches.splice(touchIndex, 1);
|
||||
emitTouchEvent('touchend', touch);
|
||||
if (then)
|
||||
checkTimer.initWithCallback(then, 0, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
// Otherwise, schedule the next event
|
||||
else {
|
||||
checkTimer.initWithCallback(nextEvent, EVENT_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function generates the coordinates of the element
|
||||
* @param 'x0', 'y0', 'x1', and 'y1' are the relative to the viewport.
|
||||
* If they are not specified, then the center of the target is used.
|
||||
*/
|
||||
function coordinates(target, x0, y0, x1, y1) {
|
||||
var coords = {};
|
||||
var box = target.getBoundingClientRect();
|
||||
var tx0 = typeof x0;
|
||||
var ty0 = typeof y0;
|
||||
var tx1 = typeof x1;
|
||||
var ty1 = typeof y1;
|
||||
function percent(s, x) {
|
||||
s = s.trim();
|
||||
var f = parseFloat(s);
|
||||
if (s[s.length - 1] === '%')
|
||||
f = f * x / 100;
|
||||
return f;
|
||||
}
|
||||
function relative(s, x) {
|
||||
var factor;
|
||||
if (s[0] === '+')
|
||||
factor = 1;
|
||||
else
|
||||
factor = -1;
|
||||
return factor * percent(s.substring(1), x);
|
||||
}
|
||||
if (tx0 === 'number')
|
||||
coords.x0 = box.left + x0;
|
||||
else if (tx0 === 'string')
|
||||
coords.x0 = box.left + percent(x0, box.width);
|
||||
//check tx1 point
|
||||
if (tx1 === 'number')
|
||||
coords.x1 = box.left + x1;
|
||||
else if (tx1 === 'string') {
|
||||
x1 = x1.trim();
|
||||
if (x1[0] === '+' || x1[0] === '-')
|
||||
coords.x1 = coords.x0 + relative(x1, box.width);
|
||||
else
|
||||
coords.x1 = box.left + percent(x1, box.width);
|
||||
}
|
||||
// check ty0
|
||||
if (ty0 === 'number')
|
||||
coords.y0 = box.top + y0;
|
||||
else if (ty0 === 'string')
|
||||
coords.y0 = box.top + percent(y0, box.height);
|
||||
//check ty1
|
||||
if (ty1 === 'number')
|
||||
coords.y1 = box.top + y1;
|
||||
else if (ty1 === 'string') {
|
||||
y1 = y1.trim();
|
||||
if (y1[0] === '+' || y1[0] === '-')
|
||||
coords.y1 = coords.y0 + relative(y1, box.height);
|
||||
else
|
||||
coords.y1 = box.top + percent(y1, box.height);
|
||||
}
|
||||
return coords;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns if the element is in viewport
|
||||
*/
|
||||
function elementInViewport(el) {
|
||||
var top = el.offsetTop;
|
||||
var left = el.offsetLeft;
|
||||
var width = el.offsetWidth;
|
||||
var height = el.offsetHeight;
|
||||
while(el.offsetParent) {
|
||||
el = el.offsetParent;
|
||||
top += el.offsetTop;
|
||||
left += el.offsetLeft;
|
||||
}
|
||||
return (top >= curWindow.pageYOffset &&
|
||||
left >= curWindow.pageXOffset &&
|
||||
(top + height) <= (curWindow.pageYOffset + curWindow.innerHeight) &&
|
||||
(left + width) <= (curWindow.pageXOffset + curWindow.innerWidth)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function throws the visibility of the element error
|
||||
*/
|
||||
function checkVisible(el, command_id) {
|
||||
//check if the element is visible
|
||||
let visible = utils.isElementDisplayed(el);
|
||||
if (!visible) {
|
||||
return false;
|
||||
}
|
||||
//check if scroll function exist. If so, call it.
|
||||
if (el.scrollIntoView) {
|
||||
el.scrollIntoView(true);
|
||||
}
|
||||
var scroll = elementInViewport(el);
|
||||
if (!scroll){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that perform a single tap
|
||||
*/
|
||||
function singleTap(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
let el;
|
||||
try {
|
||||
el = elementManager.getKnownElement(msg.json.value, curWindow);
|
||||
if (!checkVisible(el, command_id)) {
|
||||
sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
|
||||
return;
|
||||
}
|
||||
let x = '50%';
|
||||
let y = '50%';
|
||||
let c = coordinates(el, x, y);
|
||||
touch(el, 3000, [c.x0, c.x0], [c.y0, c.y0], null);
|
||||
sendOk(msg.json.command_id);
|
||||
}
|
||||
catch (e) {
|
||||
sendError(e.message, e.code, e.stack, msg.json.command_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that performs a double tap
|
||||
*/
|
||||
function doubleTap(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
let el;
|
||||
try {
|
||||
el = elementManager.getKnownElement(msg.json.value, curWindow);
|
||||
if (!checkVisible(el, command_id)) {
|
||||
sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
|
||||
return;
|
||||
}
|
||||
let x = '50%';
|
||||
let y = '50%';
|
||||
let c = coordinates(el, x, y);
|
||||
touch(el, 25, [c.x0, c.x0], [c.y0, c.y0], function() {
|
||||
// When the first tap is done, start a timer for interval ms
|
||||
checkTimer.initWithCallback(function() {
|
||||
//After interval ms, send the second tap
|
||||
touch(el, 25, [c.x0, c.x0], [c.y0, c.y0], null);
|
||||
}, 50, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
});
|
||||
sendOk(msg.json.command_id);
|
||||
}
|
||||
catch (e) {
|
||||
sendError(e.message, e.code, e.stack, msg.json.command_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to set the timeout period for element searching
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user