зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1107706: Part 16: Fix rebase of action chains for chrome space
--HG-- extra : rebase_source : 82f95f1fd1e7eda093148bd4d0f7bdc1525cc8d2
This commit is contained in:
Родитель
2bff29ff8d
Коммит
53cbc0626e
|
@ -2,13 +2,15 @@
|
|||
* 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/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ActionChain"];
|
||||
|
||||
/**
|
||||
* Functionality for (single finger) action chains.
|
||||
*/
|
||||
this.ActionChain = function (utils, checkForInterrupted) {
|
||||
// For assigning unique ids to all touches
|
||||
this.ActionChain = function(utils, checkForInterrupted) {
|
||||
// for assigning unique ids to all touches
|
||||
this.nextTouchId = 1000;
|
||||
// Keep track of active Touches
|
||||
// keep track of active Touches
|
||||
this.touchIds = {};
|
||||
// last touch for each fingerId
|
||||
this.lastCoordinates = null;
|
||||
|
@ -17,9 +19,9 @@ this.ActionChain = function (utils, checkForInterrupted) {
|
|||
// whether to send mouse event
|
||||
this.mouseEventsOnly = false;
|
||||
this.checkTimer = Components.classes["@mozilla.org/timer;1"]
|
||||
.createInstance(Components.interfaces.nsITimer);
|
||||
.createInstance(Components.interfaces.nsITimer);
|
||||
|
||||
// Callbacks for command completion.
|
||||
// callbacks for command completion
|
||||
this.onSuccess = null;
|
||||
this.onError = null;
|
||||
if (typeof checkForInterrupted == "function") {
|
||||
|
@ -28,355 +30,463 @@ this.ActionChain = function (utils, checkForInterrupted) {
|
|||
this.checkForInterrupted = () => {};
|
||||
}
|
||||
|
||||
// Determines if we create touch events.
|
||||
// determines if we create touch events
|
||||
this.inputSource = null;
|
||||
|
||||
// Test utilities providing some event synthesis code.
|
||||
// test utilities providing some event synthesis code
|
||||
this.utils = utils;
|
||||
}
|
||||
};
|
||||
|
||||
ActionChain.prototype = {
|
||||
ActionChain.prototype.dispatchActions = function(
|
||||
args,
|
||||
touchId,
|
||||
frame,
|
||||
elementManager,
|
||||
callbacks,
|
||||
touchProvider) {
|
||||
// Some touch events code in the listener needs to do ipc, so we can't
|
||||
// share this code across chrome/content.
|
||||
if (touchProvider) {
|
||||
this.touchProvider = touchProvider;
|
||||
}
|
||||
|
||||
dispatchActions: function (args, touchId, frame, elementManager, callbacks,
|
||||
touchProvider) {
|
||||
// Some touch events code in the listener needs to do ipc, so we can't
|
||||
// share this code across chrome/content.
|
||||
if (touchProvider) {
|
||||
this.touchProvider = touchProvider;
|
||||
}
|
||||
this.elementManager = elementManager;
|
||||
let commandArray = elementManager.convertWrappedArguments(args, frame);
|
||||
this.onSuccess = callbacks.onSuccess;
|
||||
this.onError = callbacks.onError;
|
||||
this.frame = frame;
|
||||
|
||||
this.elementManager = elementManager;
|
||||
let commandArray = elementManager.convertWrappedArguments(args, frame);
|
||||
let {onSuccess, onError} = callbacks;
|
||||
this.onSuccess = onSuccess;
|
||||
this.onError = onError;
|
||||
this.frame = frame;
|
||||
if (touchId == null) {
|
||||
touchId = this.nextTouchId++;
|
||||
}
|
||||
|
||||
if (touchId == null) {
|
||||
touchId = this.nextTouchId++;
|
||||
}
|
||||
if (!frame.document.createTouch) {
|
||||
this.mouseEventsOnly = true;
|
||||
}
|
||||
|
||||
if (!frame.document.createTouch) {
|
||||
this.mouseEventsOnly = true;
|
||||
}
|
||||
let keyModifiers = {
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
metaKey: false
|
||||
};
|
||||
|
||||
let keyModifiers = {
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
metaKey: false
|
||||
};
|
||||
try {
|
||||
this.actions(commandArray, touchId, 0, keyModifiers);
|
||||
} catch (e) {
|
||||
this.onError(e);
|
||||
this.resetValues();
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
this.actions(commandArray, touchId, 0, keyModifiers);
|
||||
} catch (e) {
|
||||
this.onError(e.message, e.code, e.stack);
|
||||
this.resetValues();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* This function emit mouse event.
|
||||
*
|
||||
* @param {Document} doc
|
||||
* Current document.
|
||||
* @param {string} type
|
||||
* Type of event to dispatch.
|
||||
* @param {number} clickCount
|
||||
* Number of clicks, button notes the mouse button.
|
||||
* @param {number} elClientX
|
||||
* X coordinate of the mouse relative to the viewport.
|
||||
* @param {number} elClientY
|
||||
* Y coordinate of the mouse relative to the viewport.
|
||||
* @param {Object} modifiers
|
||||
* An object of modifier keys present.
|
||||
*/
|
||||
ActionChain.prototype.emitMouseEvent = function(
|
||||
doc,
|
||||
type,
|
||||
elClientX,
|
||||
elClientY,
|
||||
button,
|
||||
clickCount,
|
||||
modifiers) {
|
||||
if (!this.checkForInterrupted()) {
|
||||
let loggingInfo = "emitting Mouse event of type " + type +
|
||||
" at coordinates (" + elClientX + ", " + elClientY +
|
||||
") relative to the viewport\n" +
|
||||
" button: " + button + "\n" +
|
||||
" clickCount: " + clickCount + "\n";
|
||||
dump(Date.now() + " Marionette: " + loggingInfo);
|
||||
|
||||
/**
|
||||
* This function emit mouse event
|
||||
* @param: doc is the current document
|
||||
* type is the type of event to dispatch
|
||||
* clickCount is the number of clicks, button notes the mouse button
|
||||
* elClientX and elClientY are the coordinates of the mouse relative to the viewport
|
||||
* modifiers is an object of modifier keys present
|
||||
*/
|
||||
emitMouseEvent: function (doc, type, elClientX, elClientY, button, clickCount, modifiers) {
|
||||
if (!this.checkForInterrupted()) {
|
||||
let loggingInfo = "emitting Mouse event of type " + type +
|
||||
" at coordinates (" + elClientX + ", " + elClientY +
|
||||
") relative to the viewport\n" +
|
||||
" button: " + button + "\n" +
|
||||
" clickCount: " + clickCount + "\n";
|
||||
dump(Date.now() + " Marionette: " + loggingInfo);
|
||||
let win = doc.defaultView;
|
||||
let domUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
let win = doc.defaultView;
|
||||
let domUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
let mods;
|
||||
if (typeof modifiers != "undefined") {
|
||||
mods = this.utils._parseModifiers(modifiers);
|
||||
} else {
|
||||
mods = 0;
|
||||
}
|
||||
domUtils.sendMouseEvent(type, elClientX, elClientY, button || 0, clickCount || 1,
|
||||
mods, false, 0, this.inputSource);
|
||||
|
||||
let mods;
|
||||
if (typeof modifiers != "undefined") {
|
||||
mods = this.utils._parseModifiers(modifiers);
|
||||
} else {
|
||||
mods = 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset any persisted values after a command completes.
|
||||
*/
|
||||
resetValues: function () {
|
||||
this.onSuccess = null;
|
||||
this.onError = null;
|
||||
this.frame = null;
|
||||
this.elementManager = null;
|
||||
this.touchProvider = null;
|
||||
this.mouseEventsOnly = false;
|
||||
},
|
||||
domUtils.sendMouseEvent(
|
||||
type,
|
||||
elClientX,
|
||||
elClientY,
|
||||
button || 0,
|
||||
clickCount || 1,
|
||||
mods,
|
||||
false,
|
||||
0,
|
||||
this.inputSource);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to emit touch events for each finger. e.g. finger=[['press', id], ['wait', 5], ['release']]
|
||||
* touchId represents the finger id, i keeps track of the current action of the chain
|
||||
* keyModifiers is an object keeping track keyDown/keyUp pairs through an action chain.
|
||||
*/
|
||||
actions: function (chain, touchId, i, keyModifiers) {
|
||||
/**
|
||||
* Reset any persisted values after a command completes.
|
||||
*/
|
||||
ActionChain.prototype.resetValues = function() {
|
||||
this.onSuccess = null;
|
||||
this.onError = null;
|
||||
this.frame = null;
|
||||
this.elementManager = null;
|
||||
this.touchProvider = null;
|
||||
this.mouseEventsOnly = false;
|
||||
};
|
||||
|
||||
if (i == chain.length) {
|
||||
this.onSuccess({value: touchId});
|
||||
/**
|
||||
* Function to emit touch events for each finger. e.g.
|
||||
* finger=[['press', id], ['wait', 5], ['release']] touchId represents
|
||||
* the finger id, i keeps track of the current action of the chain
|
||||
* keyModifiers is an object keeping track keyDown/keyUp pairs through
|
||||
* an action chain.
|
||||
*/
|
||||
ActionChain.prototype.actions = function(chain, touchId, i, keyModifiers) {
|
||||
if (i == chain.length) {
|
||||
this.onSuccess({value: touchId});
|
||||
this.resetValues();
|
||||
return;
|
||||
}
|
||||
|
||||
let pack = chain[i];
|
||||
let command = pack[0];
|
||||
let el;
|
||||
let c;
|
||||
i++;
|
||||
|
||||
if (["press", "wait", "keyDown", "keyUp", "click"].indexOf(command) == -1) {
|
||||
// if mouseEventsOnly, then touchIds isn't used
|
||||
if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
|
||||
this.resetValues();
|
||||
return;
|
||||
throw new WebDriverError("Element has not been pressed");
|
||||
}
|
||||
}
|
||||
|
||||
let pack = chain[i];
|
||||
let command = pack[0];
|
||||
let el;
|
||||
let c;
|
||||
i++;
|
||||
|
||||
if (['press', 'wait', 'keyDown', 'keyUp', 'click'].indexOf(command) == -1) {
|
||||
// if mouseEventsOnly, then touchIds isn't used
|
||||
if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
|
||||
this.onError("Element has not been pressed", 500, null);
|
||||
this.resetValues();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(command) {
|
||||
case 'keyDown':
|
||||
switch(command) {
|
||||
case "keyDown":
|
||||
this.utils.sendKeyDown(pack[1], keyModifiers, this.frame);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
case 'keyUp':
|
||||
|
||||
case "keyUp":
|
||||
this.utils.sendKeyUp(pack[1], keyModifiers, this.frame);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
case 'click':
|
||||
|
||||
case "click":
|
||||
el = this.elementManager.getKnownElement(pack[1], this.frame);
|
||||
let button = pack[2];
|
||||
let clickCount = pack[3];
|
||||
c = this.coordinates(el, null, null);
|
||||
this.mouseTap(el.ownerDocument, c.x, c.y, button, clickCount,
|
||||
keyModifiers);
|
||||
this.mouseTap(el.ownerDocument, c.x, c.y, button, clickCount, keyModifiers);
|
||||
if (button == 2) {
|
||||
this.emitMouseEvent(el.ownerDocument, 'contextmenu', c.x, c.y,
|
||||
button, clickCount, keyModifiers);
|
||||
this.emitMouseEvent(el.ownerDocument, "contextmenu", c.x, c.y,
|
||||
button, clickCount, keyModifiers);
|
||||
}
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
case 'press':
|
||||
|
||||
case "press":
|
||||
if (this.lastCoordinates) {
|
||||
this.generateEvents('cancel', this.lastCoordinates[0], this.lastCoordinates[1],
|
||||
touchId, null, keyModifiers);
|
||||
this.onError("Invalid Command: press cannot follow an active touch event", 500, null);
|
||||
this.generateEvents(
|
||||
"cancel",
|
||||
this.lastCoordinates[0],
|
||||
this.lastCoordinates[1],
|
||||
touchId,
|
||||
null,
|
||||
keyModifiers);
|
||||
this.resetValues();
|
||||
return;
|
||||
throw new WebDriverError(
|
||||
"Invalid Command: press cannot follow an active touch event");
|
||||
}
|
||||
|
||||
// look ahead to check if we're scrolling. Needed for APZ touch dispatching.
|
||||
if ((i != chain.length) && (chain[i][0].indexOf('move') !== -1)) {
|
||||
this.scrolling = true;
|
||||
}
|
||||
el = this.elementManager.getKnownElement(pack[1], this.frame);
|
||||
c = this.coordinates(el, pack[2], pack[3]);
|
||||
touchId = this.generateEvents('press', c.x, c.y, null, el, keyModifiers);
|
||||
touchId = this.generateEvents("press", c.x, c.y, null, el, keyModifiers);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
case 'release':
|
||||
this.generateEvents('release', this.lastCoordinates[0], this.lastCoordinates[1],
|
||||
touchId, null, keyModifiers);
|
||||
|
||||
case "release":
|
||||
this.generateEvents(
|
||||
"release",
|
||||
this.lastCoordinates[0],
|
||||
this.lastCoordinates[1],
|
||||
touchId,
|
||||
null,
|
||||
keyModifiers);
|
||||
this.actions(chain, null, i, keyModifiers);
|
||||
this.scrolling = false;
|
||||
break;
|
||||
case 'move':
|
||||
|
||||
case "move":
|
||||
el = this.elementManager.getKnownElement(pack[1], this.frame);
|
||||
c = this.coordinates(el);
|
||||
this.generateEvents('move', c.x, c.y, touchId, null, keyModifiers);
|
||||
this.generateEvents("move", c.x, c.y, touchId, null, keyModifiers);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
case 'moveByOffset':
|
||||
this.generateEvents('move', this.lastCoordinates[0] + pack[1],
|
||||
this.lastCoordinates[1] + pack[2],
|
||||
touchId, null, keyModifiers);
|
||||
|
||||
case "moveByOffset":
|
||||
this.generateEvents(
|
||||
"move",
|
||||
this.lastCoordinates[0] + pack[1],
|
||||
this.lastCoordinates[1] + pack[2],
|
||||
touchId,
|
||||
null,
|
||||
keyModifiers);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
case 'wait':
|
||||
if (pack[1] != null ) {
|
||||
let time = pack[1]*1000;
|
||||
|
||||
case "wait":
|
||||
if (pack[1] != null) {
|
||||
let time = pack[1] * 1000;
|
||||
|
||||
// standard waiting time to fire contextmenu
|
||||
let standard = 750;
|
||||
try {
|
||||
standard = Services.prefs.getIntPref("ui.click_hold_context_menus.delay");
|
||||
}
|
||||
catch (e){}
|
||||
} catch (e) {}
|
||||
|
||||
if (time >= standard && this.isTap) {
|
||||
chain.splice(i, 0, ['longPress'], ['wait', (time-standard)/1000]);
|
||||
chain.splice(i, 0, ["longPress"], ["wait", (time - standard) / 1000]);
|
||||
time = standard;
|
||||
}
|
||||
this.checkTimer.initWithCallback(() => {
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
}, time, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
else {
|
||||
this.checkTimer.initWithCallback(
|
||||
() => this.actions(chain, touchId, i, keyModifiers),
|
||||
time, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
||||
} else {
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
}
|
||||
break;
|
||||
case 'cancel':
|
||||
this.generateEvents('cancel', this.lastCoordinates[0], this.lastCoordinates[1],
|
||||
touchId, null, keyModifiers);
|
||||
|
||||
case "cancel":
|
||||
this.generateEvents(
|
||||
"cancel",
|
||||
this.lastCoordinates[0],
|
||||
this.lastCoordinates[1],
|
||||
touchId,
|
||||
null,
|
||||
keyModifiers);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
this.scrolling = false;
|
||||
break;
|
||||
case 'longPress':
|
||||
this.generateEvents('contextmenu', this.lastCoordinates[0], this.lastCoordinates[1],
|
||||
touchId, null, keyModifiers);
|
||||
|
||||
case "longPress":
|
||||
this.generateEvents(
|
||||
"contextmenu",
|
||||
this.lastCoordinates[0],
|
||||
this.lastCoordinates[1],
|
||||
touchId,
|
||||
null,
|
||||
keyModifiers);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This function generates a pair of coordinates relative to the viewport given a
|
||||
* target element and coordinates relative to that element's top-left corner.
|
||||
* @param 'x', and 'y' are the relative to the target.
|
||||
* If they are not specified, then the center of the target is used.
|
||||
*/
|
||||
coordinates: function (target, x, y) {
|
||||
let box = target.getBoundingClientRect();
|
||||
if (x == null) {
|
||||
x = box.width / 2;
|
||||
}
|
||||
if (y == null) {
|
||||
y = box.height / 2;
|
||||
}
|
||||
let coords = {};
|
||||
coords.x = box.left + x;
|
||||
coords.y = box.top + y;
|
||||
return coords;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an element and a pair of coordinates, returns an array of the form
|
||||
* [ clientX, clientY, pageX, pageY, screenX, screenY ]
|
||||
*/
|
||||
getCoordinateInfo: function (el, corx, cory) {
|
||||
let win = el.ownerDocument.defaultView;
|
||||
return [ corx, // clientX
|
||||
cory, // clientY
|
||||
corx + win.pageXOffset, // pageX
|
||||
cory + win.pageYOffset, // pageY
|
||||
corx + win.mozInnerScreenX, // screenX
|
||||
cory + win.mozInnerScreenY // screenY
|
||||
];
|
||||
},
|
||||
/**
|
||||
* This function generates a pair of coordinates relative to the viewport given a
|
||||
* target element and coordinates relative to that element's top-left corner.
|
||||
*
|
||||
* @param {DOMElement} target
|
||||
* The target to calculate coordinates of.
|
||||
* @param {number} x
|
||||
* X coordinate relative to target. If unspecified, the centre of
|
||||
* the target is used.
|
||||
* @param {number} y
|
||||
* Y coordinate relative to target. If unspecified, the centre of
|
||||
* the target is used.
|
||||
*/
|
||||
ActionChain.prototype.coordinates = function(target, x, y) {
|
||||
let box = target.getBoundingClientRect();
|
||||
if (x == null) {
|
||||
x = box.width / 2;
|
||||
}
|
||||
if (y == null) {
|
||||
y = box.height / 2;
|
||||
}
|
||||
let coords = {};
|
||||
coords.x = box.left + x;
|
||||
coords.y = box.top + y;
|
||||
return coords;
|
||||
};
|
||||
|
||||
//x and y are coordinates relative to the viewport
|
||||
generateEvents: function (type, x, y, touchId, target, keyModifiers) {
|
||||
this.lastCoordinates = [x, y];
|
||||
let doc = this.frame.document;
|
||||
switch (type) {
|
||||
case 'tap':
|
||||
/**
|
||||
* Given an element and a pair of coordinates, returns an array of the
|
||||
* form [clientX, clientY, pageX, pageY, screenX, screenY].
|
||||
*/
|
||||
ActionChain.prototype.getCoordinateInfo = function(el, corx, cory) {
|
||||
let win = el.ownerDocument.defaultView;
|
||||
return [
|
||||
corx, // clientX
|
||||
cory, // clientY
|
||||
corx + win.pageXOffset, // pageX
|
||||
cory + win.pageYOffset, // pageY
|
||||
corx + win.mozInnerScreenX, // screenX
|
||||
cory + win.mozInnerScreenY // screenY
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} x
|
||||
* X coordinate of the location to generate the event that is relative
|
||||
* to the viewport.
|
||||
* @param {number} y
|
||||
* Y coordinate of the location to generate the event that is relative
|
||||
* to the viewport.
|
||||
*/
|
||||
ActionChain.prototype.generateEvents = function(
|
||||
type, x, y, touchId, target, keyModifiers) {
|
||||
this.lastCoordinates = [x, y];
|
||||
let doc = this.frame.document;
|
||||
|
||||
switch (type) {
|
||||
case "tap":
|
||||
if (this.mouseEventsOnly) {
|
||||
this.mouseTap(touch.target.ownerDocument, touch.clientX, touch.clientY,
|
||||
null, null, keyModifiers);
|
||||
this.mouseTap(
|
||||
touch.target.ownerDocument,
|
||||
touch.clientX,
|
||||
touch.clientY,
|
||||
null,
|
||||
null,
|
||||
keyModifiers);
|
||||
} else {
|
||||
touchId = this.nextTouchId++;
|
||||
let touch = this.touchProvider.createATouch(target, x, y, touchId);
|
||||
this.touchProvider.emitTouchEvent('touchstart', touch);
|
||||
this.touchProvider.emitTouchEvent('touchend', touch);
|
||||
this.mouseTap(touch.target.ownerDocument, touch.clientX, touch.clientY,
|
||||
null, null, keyModifiers);
|
||||
this.touchProvider.emitTouchEvent("touchstart", touch);
|
||||
this.touchProvider.emitTouchEvent("touchend", touch);
|
||||
this.mouseTap(
|
||||
touch.target.ownerDocument,
|
||||
touch.clientX,
|
||||
touch.clientY,
|
||||
null,
|
||||
null,
|
||||
keyModifiers);
|
||||
}
|
||||
this.lastCoordinates = null;
|
||||
break;
|
||||
case 'press':
|
||||
|
||||
case "press":
|
||||
this.isTap = true;
|
||||
if (this.mouseEventsOnly) {
|
||||
this.emitMouseEvent(doc, 'mousemove', x, y, null, null, keyModifiers);
|
||||
this.emitMouseEvent(doc, 'mousedown', x, y, null, null, keyModifiers);
|
||||
}
|
||||
else {
|
||||
this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers);
|
||||
this.emitMouseEvent(doc, "mousedown", x, y, null, null, keyModifiers);
|
||||
} else {
|
||||
touchId = this.nextTouchId++;
|
||||
let touch = this.touchProvider.createATouch(target, x, y, touchId);
|
||||
this.touchProvider.emitTouchEvent('touchstart', touch);
|
||||
this.touchProvider.emitTouchEvent("touchstart", touch);
|
||||
this.touchIds[touchId] = touch;
|
||||
return touchId;
|
||||
}
|
||||
break;
|
||||
case 'release':
|
||||
|
||||
case "release":
|
||||
if (this.mouseEventsOnly) {
|
||||
let [x, y] = this.lastCoordinates;
|
||||
this.emitMouseEvent(doc, 'mouseup', x, y,
|
||||
null, null, keyModifiers);
|
||||
}
|
||||
else {
|
||||
this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers);
|
||||
} else {
|
||||
let touch = this.touchIds[touchId];
|
||||
let [x, y] = this.lastCoordinates;
|
||||
|
||||
touch = this.touchProvider.createATouch(touch.target, x, y, touchId);
|
||||
this.touchProvider.emitTouchEvent('touchend', touch);
|
||||
this.touchProvider.emitTouchEvent("touchend", touch);
|
||||
|
||||
if (this.isTap) {
|
||||
this.mouseTap(touch.target.ownerDocument, touch.clientX, touch.clientY,
|
||||
null, null, keyModifiers);
|
||||
this.mouseTap(
|
||||
touch.target.ownerDocument,
|
||||
touch.clientX,
|
||||
touch.clientY,
|
||||
null,
|
||||
null,
|
||||
keyModifiers);
|
||||
}
|
||||
delete this.touchIds[touchId];
|
||||
}
|
||||
|
||||
this.isTap = false;
|
||||
this.lastCoordinates = null;
|
||||
break;
|
||||
case 'cancel':
|
||||
|
||||
case "cancel":
|
||||
this.isTap = false;
|
||||
if (this.mouseEventsOnly) {
|
||||
let [x, y] = this.lastCoordinates;
|
||||
this.emitMouseEvent(doc, 'mouseup', x, y,
|
||||
null, null, keyModifiers);
|
||||
}
|
||||
else {
|
||||
this.touchProvider.emitTouchEvent('touchcancel', this.touchIds[touchId]);
|
||||
this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers);
|
||||
} else {
|
||||
this.touchProvider.emitTouchEvent("touchcancel", this.touchIds[touchId]);
|
||||
delete this.touchIds[touchId];
|
||||
}
|
||||
this.lastCoordinates = null;
|
||||
break;
|
||||
case 'move':
|
||||
|
||||
case "move":
|
||||
this.isTap = false;
|
||||
if (this.mouseEventsOnly) {
|
||||
this.emitMouseEvent(doc, 'mousemove', x, y, null, null, keyModifiers);
|
||||
}
|
||||
else {
|
||||
let touch = this.touchProvider.createATouch(this.touchIds[touchId].target,
|
||||
x, y, touchId);
|
||||
this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers);
|
||||
} else {
|
||||
let touch = this.touchProvider.createATouch(
|
||||
this.touchIds[touchId].target, x, y, touchId);
|
||||
this.touchIds[touchId] = touch;
|
||||
this.touchProvider.emitTouchEvent('touchmove', touch);
|
||||
this.touchProvider.emitTouchEvent("touchmove", touch);
|
||||
}
|
||||
break;
|
||||
case 'contextmenu':
|
||||
|
||||
case "contextmenu":
|
||||
this.isTap = false;
|
||||
let event = this.frame.document.createEvent('MouseEvents');
|
||||
let event = this.frame.document.createEvent("MouseEvents");
|
||||
if (this.mouseEventsOnly) {
|
||||
target = doc.elementFromPoint(this.lastCoordinates[0], this.lastCoordinates[1]);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
target = this.touchIds[touchId].target;
|
||||
}
|
||||
let [ clientX, clientY,
|
||||
pageX, pageY,
|
||||
screenX, screenY ] = this.getCoordinateInfo(target, x, y);
|
||||
event.initMouseEvent('contextmenu', true, true,
|
||||
target.ownerDocument.defaultView, 1,
|
||||
screenX, screenY, clientX, clientY,
|
||||
false, false, false, false, 0, null);
|
||||
|
||||
let [clientX, clientY, pageX, pageY, screenX, screenY] =
|
||||
this.getCoordinateInfo(target, x, y);
|
||||
|
||||
event.initMouseEvent(
|
||||
"contextmenu",
|
||||
true,
|
||||
true,
|
||||
target.ownerDocument.defaultView,
|
||||
1,
|
||||
screenX,
|
||||
screenY,
|
||||
clientX,
|
||||
clientY,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
null);
|
||||
target.dispatchEvent(event);
|
||||
break;
|
||||
default:
|
||||
throw {message:"Unknown event type: " + type, code: 500, stack:null};
|
||||
}
|
||||
this.checkForInterrupted();
|
||||
},
|
||||
|
||||
mouseTap: function (doc, x, y, button, clickCount, keyModifiers) {
|
||||
this.emitMouseEvent(doc, 'mousemove', x, y, button, clickCount, keyModifiers);
|
||||
this.emitMouseEvent(doc, 'mousedown', x, y, button, clickCount, keyModifiers);
|
||||
this.emitMouseEvent(doc, 'mouseup', x, y, button, clickCount, keyModifiers);
|
||||
},
|
||||
}
|
||||
default:
|
||||
throw new WebDriverError("Unknown event type: " + type);
|
||||
}
|
||||
this.checkForInterrupted();
|
||||
};
|
||||
|
||||
ActionChain.prototype.mouseTap = function(doc, x, y, button, count, mod) {
|
||||
this.emitMouseEvent(doc, "mousemove", x, y, button, count, mod);
|
||||
this.emitMouseEvent(doc, "mousedown", x, y, button, count, mod);
|
||||
this.emitMouseEvent(doc, "mouseup", x, y, button, count, mod);
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ this.DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
|
|||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager");
|
||||
|
||||
Cu.import("chrome://marionette/content/actions.js");
|
||||
Cu.import("chrome://marionette/content/elements.js");
|
||||
Cu.import("chrome://marionette/content/emulator.js");
|
||||
Cu.import("chrome://marionette/content/error.js");
|
||||
|
@ -239,6 +240,7 @@ this.GeckoDriver = function(appName, device, emulator) {
|
|||
this.oopFrameId = null;
|
||||
this.observing = null;
|
||||
this._browserIds = new WeakMap();
|
||||
this.actions = new ActionChain(utils);
|
||||
|
||||
this.sessionCapabilities = {
|
||||
// Mandated capabilities
|
||||
|
@ -1879,14 +1881,29 @@ GeckoDriver.prototype.singleTap = function(cmd, resp) {
|
|||
* Last touch ID.
|
||||
*/
|
||||
GeckoDriver.prototype.actionChain = function(cmd, resp) {
|
||||
let {chain, nextId} = cmd.parameters;
|
||||
|
||||
switch (this.context) {
|
||||
case Context.CHROME:
|
||||
throw new WebDriverError("Command 'actionChain' is not available in chrome context");
|
||||
if (this.appName != "Firefox") {
|
||||
// be conservative until this has a use case and is established
|
||||
// to work as expected on b2g/fennec
|
||||
throw new WebDriverError(
|
||||
"Command 'actionChain' is not available in chrome context");
|
||||
}
|
||||
|
||||
let cbs = {};
|
||||
cbs.onSuccess = val => resp.value = val;
|
||||
cbs.onError = err => { throw err };
|
||||
|
||||
let win = this.getCurrentWindow();
|
||||
let elm = this.curBrowser.elementManager;
|
||||
this.actions.dispatchActions(chain, nextId, win, elm, cbs);
|
||||
break;
|
||||
|
||||
case Context.CONTENT:
|
||||
this.addFrameCloseListener("action chain");
|
||||
resp.value = yield this.listener.actionChain(
|
||||
{chain: cmd.parameters.chain, nextId: cmd.parameters.nextId});
|
||||
resp.value = yield this.listener.actionChain({chain: chain, nextId: nextId});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -969,8 +969,17 @@ function actionChain(msg) {
|
|||
touchProvider.createATouch = createATouch;
|
||||
touchProvider.emitTouchEvent = emitTouchEvent;
|
||||
|
||||
actions.dispatchActions(args, touchId, curFrame, elementManager, callbacks,
|
||||
touchProvider);
|
||||
try {
|
||||
actions.dispatchActions(
|
||||
args,
|
||||
touchId,
|
||||
curFrame,
|
||||
elementManager,
|
||||
callbacks,
|
||||
touchProvider);
|
||||
} catch (e) {
|
||||
sendError(e.message, e.code, e.stack, command_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче