mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1135846 - Expose marionette's actions code to chrome scope where applicable. r=dburns
This commit is contained in:
parent
1b6f431698
commit
63bae57fab
@ -32,72 +32,3 @@ class TestClick(MarionetteTestCase):
|
||||
|
||||
with self.assertRaises(ElementNotVisibleException):
|
||||
self.marionette.find_element(By.ID, 'child').click()
|
||||
|
||||
class TestClickAction(MarionetteTestCase):
|
||||
|
||||
def setUp(self):
|
||||
MarionetteTestCase.setUp(self)
|
||||
if self.marionette.session_capabilities['platformName'] == 'DARWIN':
|
||||
self.mod_key = Keys.META
|
||||
else:
|
||||
self.mod_key = Keys.CONTROL
|
||||
self.action = Actions(self.marionette)
|
||||
|
||||
def test_click_action(self):
|
||||
test_html = self.marionette.absolute_url("test.html")
|
||||
self.marionette.navigate(test_html)
|
||||
link = self.marionette.find_element(By.ID, "mozLink")
|
||||
self.action.click(link).perform()
|
||||
self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
|
||||
|
||||
def test_clicking_element_out_of_view_succeeds(self):
|
||||
# The action based click doesn't check for visibility.
|
||||
test_html = self.marionette.absolute_url('hidden.html')
|
||||
self.marionette.navigate(test_html)
|
||||
el = self.marionette.find_element(By.ID, 'child')
|
||||
self.action.click(el).perform()
|
||||
|
||||
def test_double_click_action(self):
|
||||
test_html = self.marionette.absolute_url("javascriptPage.html")
|
||||
self.marionette.navigate(test_html)
|
||||
el = self.marionette.find_element(By.ID, 'displayed')
|
||||
# The first click just brings the element into view so text selection
|
||||
# works as expected. (A different test page could be used to isolate
|
||||
# this element and make sure it's always in view)
|
||||
el.click()
|
||||
self.action.double_click(el).perform()
|
||||
el.send_keys(self.mod_key + 'c')
|
||||
rel = self.marionette.find_element("id", "keyReporter")
|
||||
rel.send_keys(self.mod_key + 'v')
|
||||
self.assertEqual(rel.get_attribute('value'), 'Displayed')
|
||||
|
||||
def test_context_click_action(self):
|
||||
test_html = self.marionette.absolute_url("javascriptPage.html")
|
||||
self.marionette.navigate(test_html)
|
||||
click_el = self.marionette.find_element(By.ID, 'resultContainer')
|
||||
|
||||
def context_menu_state():
|
||||
with self.marionette.using_context('chrome'):
|
||||
cm_el = self.marionette.find_element(By.ID, 'contentAreaContextMenu')
|
||||
return cm_el.get_attribute('state')
|
||||
|
||||
self.assertEqual('closed', context_menu_state())
|
||||
self.action.context_click(click_el).perform()
|
||||
self.wait_for_condition(lambda _: context_menu_state() == 'open')
|
||||
|
||||
with self.marionette.using_context('chrome'):
|
||||
(self.marionette.find_element(By.ID, 'main-window')
|
||||
.send_keys(Keys.ESCAPE))
|
||||
self.wait_for_condition(lambda _: context_menu_state() == 'closed')
|
||||
|
||||
def test_middle_click_action(self):
|
||||
test_html = self.marionette.absolute_url("clicks.html")
|
||||
self.marionette.navigate(test_html)
|
||||
|
||||
self.marionette.find_element(By.ID, "addbuttonlistener").click()
|
||||
|
||||
el = self.marionette.find_element(By.ID, "showbutton")
|
||||
self.action.middle_click(el).perform()
|
||||
|
||||
self.wait_for_condition(
|
||||
lambda _: el.get_attribute('innerHTML') == '1')
|
||||
|
@ -0,0 +1,117 @@
|
||||
# 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/.
|
||||
|
||||
from marionette import MarionetteTestCase
|
||||
from marionette_driver.marionette import Actions
|
||||
from marionette_driver.keys import Keys
|
||||
from marionette_driver.by import By
|
||||
|
||||
class TestMouseAction(MarionetteTestCase):
|
||||
|
||||
def setUp(self):
|
||||
MarionetteTestCase.setUp(self)
|
||||
if self.marionette.session_capabilities['platformName'] == 'DARWIN':
|
||||
self.mod_key = Keys.META
|
||||
else:
|
||||
self.mod_key = Keys.CONTROL
|
||||
self.action = Actions(self.marionette)
|
||||
|
||||
def test_click_action(self):
|
||||
test_html = self.marionette.absolute_url("test.html")
|
||||
self.marionette.navigate(test_html)
|
||||
link = self.marionette.find_element(By.ID, "mozLink")
|
||||
self.action.click(link).perform()
|
||||
self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
|
||||
|
||||
def test_clicking_element_out_of_view_succeeds(self):
|
||||
# The action based click doesn't check for visibility.
|
||||
test_html = self.marionette.absolute_url('hidden.html')
|
||||
self.marionette.navigate(test_html)
|
||||
el = self.marionette.find_element(By.ID, 'child')
|
||||
self.action.click(el).perform()
|
||||
|
||||
def test_double_click_action(self):
|
||||
test_html = self.marionette.absolute_url("javascriptPage.html")
|
||||
self.marionette.navigate(test_html)
|
||||
el = self.marionette.find_element(By.ID, 'displayed')
|
||||
# The first click just brings the element into view so text selection
|
||||
# works as expected. (A different test page could be used to isolate
|
||||
# this element and make sure it's always in view)
|
||||
el.click()
|
||||
self.action.double_click(el).perform()
|
||||
el.send_keys(self.mod_key + 'c')
|
||||
rel = self.marionette.find_element("id", "keyReporter")
|
||||
rel.send_keys(self.mod_key + 'v')
|
||||
self.assertEqual(rel.get_attribute('value'), 'Displayed')
|
||||
|
||||
def test_context_click_action(self):
|
||||
test_html = self.marionette.absolute_url("javascriptPage.html")
|
||||
self.marionette.navigate(test_html)
|
||||
click_el = self.marionette.find_element(By.ID, 'resultContainer')
|
||||
|
||||
def context_menu_state():
|
||||
with self.marionette.using_context('chrome'):
|
||||
cm_el = self.marionette.find_element(By.ID, 'contentAreaContextMenu')
|
||||
return cm_el.get_attribute('state')
|
||||
|
||||
self.assertEqual('closed', context_menu_state())
|
||||
self.action.context_click(click_el).perform()
|
||||
self.wait_for_condition(lambda _: context_menu_state() == 'open')
|
||||
|
||||
with self.marionette.using_context('chrome'):
|
||||
(self.marionette.find_element(By.ID, 'main-window')
|
||||
.send_keys(Keys.ESCAPE))
|
||||
self.wait_for_condition(lambda _: context_menu_state() == 'closed')
|
||||
|
||||
def test_middle_click_action(self):
|
||||
test_html = self.marionette.absolute_url("clicks.html")
|
||||
self.marionette.navigate(test_html)
|
||||
|
||||
self.marionette.find_element(By.ID, "addbuttonlistener").click()
|
||||
|
||||
el = self.marionette.find_element(By.ID, "showbutton")
|
||||
self.action.middle_click(el).perform()
|
||||
|
||||
self.wait_for_condition(
|
||||
lambda _: el.get_attribute('innerHTML') == '1')
|
||||
|
||||
def test_chrome_click(self):
|
||||
self.marionette.navigate("about:blank")
|
||||
data_uri = "data:text/html,<html></html>"
|
||||
with self.marionette.using_context('chrome'):
|
||||
urlbar = self.marionette.find_element(By.ID, "urlbar")
|
||||
urlbar.send_keys(data_uri)
|
||||
go_button = self.marionette.find_element(By.ID, "urlbar-go-button")
|
||||
self.action.click(go_button).perform()
|
||||
self.wait_for_condition(lambda mn: mn.get_url() == data_uri)
|
||||
|
||||
def test_chrome_double_click(self):
|
||||
self.marionette.navigate("about:blank")
|
||||
test_word = "quux"
|
||||
with self.marionette.using_context('chrome'):
|
||||
urlbar = self.marionette.find_element(By.ID, "urlbar")
|
||||
self.assertEqual(urlbar.get_attribute('value'), '')
|
||||
|
||||
urlbar.send_keys(test_word)
|
||||
self.assertEqual(urlbar.get_attribute('value'), test_word)
|
||||
(self.action.double_click(urlbar).perform()
|
||||
.key_down(self.mod_key)
|
||||
.key_down('x').perform())
|
||||
self.assertEqual(urlbar.get_attribute('value'), '')
|
||||
|
||||
def test_chrome_context_click_action(self):
|
||||
self.marionette.set_context('chrome')
|
||||
def context_menu_state():
|
||||
cm_el = self.marionette.find_element(By.ID, 'tabContextMenu')
|
||||
return cm_el.get_attribute('state')
|
||||
|
||||
currtab = self.marionette.execute_script("return gBrowser.selectedTab")
|
||||
self.assertEqual('closed', context_menu_state())
|
||||
self.action.context_click(currtab).perform()
|
||||
self.wait_for_condition(lambda _: context_menu_state() == 'open')
|
||||
|
||||
(self.marionette.find_element(By.ID, 'main-window')
|
||||
.send_keys(Keys.ESCAPE))
|
||||
|
||||
self.wait_for_condition(lambda _: context_menu_state() == 'closed')
|
@ -148,4 +148,6 @@ skip-if = os == "linux" # Bug 1085717
|
||||
[test_modal_dialogs.py]
|
||||
b2g = false
|
||||
[test_key_actions.py]
|
||||
[test_mouse_action.py]
|
||||
b2g = false
|
||||
[test_teardown_context_preserved.py]
|
||||
|
@ -9,6 +9,7 @@ marionette.jar:
|
||||
content/marionette-elements.js (marionette-elements.js)
|
||||
content/marionette-sendkeys.js (marionette-sendkeys.js)
|
||||
content/marionette-common.js (marionette-common.js)
|
||||
content/marionette-actions.js (marionette-actions.js)
|
||||
content/marionette-simpletest.js (marionette-simpletest.js)
|
||||
content/marionette-frame-manager.js (marionette-frame-manager.js)
|
||||
content/EventUtils.js (EventUtils.js)
|
||||
|
382
testing/marionette/marionette-actions.js
Normal file
382
testing/marionette/marionette-actions.js
Normal file
@ -0,0 +1,382 @@
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Functionality for (single finger) action chains.
|
||||
*/
|
||||
this.ActionChain = function (utils, checkForInterrupted) {
|
||||
// For assigning unique ids to all touches
|
||||
this.nextTouchId = 1000;
|
||||
// Keep track of active Touches
|
||||
this.touchIds = {};
|
||||
// last touch for each fingerId
|
||||
this.lastCoordinates = null;
|
||||
this.isTap = false;
|
||||
this.scrolling = false;
|
||||
// whether to send mouse event
|
||||
this.mouseEventsOnly = false;
|
||||
this.checkTimer = Components.classes["@mozilla.org/timer;1"]
|
||||
.createInstance(Components.interfaces.nsITimer);
|
||||
|
||||
// Callbacks for command completion.
|
||||
this.onSuccess = null;
|
||||
this.onError = null;
|
||||
if (typeof checkForInterrupted == "function") {
|
||||
this.checkForInterrupted = checkForInterrupted;
|
||||
} else {
|
||||
this.checkForInterrupted = () => {};
|
||||
}
|
||||
|
||||
// Determines if we create touch events.
|
||||
this.inputSource = null;
|
||||
|
||||
// Test utilities providing some event synthesis code.
|
||||
this.utils = utils;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (!frame.document.createTouch) {
|
||||
this.mouseEventsOnly = true;
|
||||
}
|
||||
|
||||
let keyModifiers = {
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
metaKey: false
|
||||
};
|
||||
|
||||
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: 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)
|
||||
.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);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
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.onError("Element has not been pressed", 500, null);
|
||||
this.resetValues();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(command) {
|
||||
case 'keyDown':
|
||||
this.utils.sendKeyDown(pack[1], keyModifiers, this.frame);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
case 'keyUp':
|
||||
this.utils.sendKeyUp(pack[1], keyModifiers, this.frame);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
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);
|
||||
if (button == 2) {
|
||||
this.emitMouseEvent(el.ownerDocument, 'contextmenu', c.x, c.y,
|
||||
button, clickCount, keyModifiers);
|
||||
}
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
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.resetValues();
|
||||
return;
|
||||
}
|
||||
// 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);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
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':
|
||||
el = this.elementManager.getKnownElement(pack[1], this.frame);
|
||||
c = this.coordinates(el);
|
||||
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);
|
||||
this.actions(chain, touchId, i, keyModifiers);
|
||||
break;
|
||||
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){}
|
||||
if (time >= standard && this.isTap) {
|
||||
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.actions(chain, touchId, i, keyModifiers);
|
||||
}
|
||||
break;
|
||||
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);
|
||||
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
|
||||
];
|
||||
},
|
||||
|
||||
//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':
|
||||
if (this.mouseEventsOnly) {
|
||||
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.lastCoordinates = null;
|
||||
break;
|
||||
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 {
|
||||
touchId = this.nextTouchId++;
|
||||
let touch = this.touchProvider.createATouch(target, x, y, touchId);
|
||||
this.touchProvider.emitTouchEvent('touchstart', touch);
|
||||
this.touchIds[touchId] = touch;
|
||||
return touchId;
|
||||
}
|
||||
break;
|
||||
case 'release':
|
||||
if (this.mouseEventsOnly) {
|
||||
let [x, y] = this.lastCoordinates;
|
||||
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);
|
||||
if (this.isTap) {
|
||||
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':
|
||||
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]);
|
||||
delete this.touchIds[touchId];
|
||||
}
|
||||
this.lastCoordinates = null;
|
||||
break;
|
||||
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.touchIds[touchId] = touch;
|
||||
this.touchProvider.emitTouchEvent('touchmove', touch);
|
||||
}
|
||||
break;
|
||||
case 'contextmenu':
|
||||
this.isTap = false;
|
||||
let event = this.frame.document.createEvent('MouseEvents');
|
||||
if (this.mouseEventsOnly) {
|
||||
target = doc.elementFromPoint(this.lastCoordinates[0], this.lastCoordinates[1]);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
},
|
||||
}
|
@ -13,6 +13,7 @@ let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-simpletest.js");
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-common.js");
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-actions.js");
|
||||
Cu.import("chrome://marionette/content/marionette-elements.js");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
@ -40,8 +41,8 @@ let curFrame = content;
|
||||
let previousFrame = null;
|
||||
let elementManager = new ElementManager([]);
|
||||
let accessibility = new Accessibility();
|
||||
let actions = new ActionChain(utils, checkForInterrupted);
|
||||
let importedScripts = null;
|
||||
let inputSource = null;
|
||||
|
||||
// The sandbox we execute test scripts in. Gets lazily created in
|
||||
// createExecuteContentSandbox().
|
||||
@ -68,17 +69,8 @@ let navTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
let onDOMContentLoaded;
|
||||
// Send move events about this often
|
||||
let EVENT_INTERVAL = 30; // milliseconds
|
||||
// For assigning unique ids to all touches
|
||||
let nextTouchId = 1000;
|
||||
//Keep track of active Touches
|
||||
let touchIds = {};
|
||||
// last touch for each fingerId
|
||||
let multiLast = {};
|
||||
let lastCoordinates = null;
|
||||
let isTap = false;
|
||||
let scrolling = false;
|
||||
// whether to send mouse event
|
||||
let mouseEventsOnly = false;
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
let logger = Log.repository.getLogger("Marionette");
|
||||
@ -124,7 +116,7 @@ function registerSelf() {
|
||||
|
||||
function emitTouchEventForIFrame(message) {
|
||||
message = message.json;
|
||||
let identifier = nextTouchId;
|
||||
let identifier = actions.nextTouchId;
|
||||
|
||||
let domWindowUtils = curFrame.
|
||||
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
@ -245,7 +237,7 @@ function newSession(msg) {
|
||||
// events being the result of a physical mouse action.
|
||||
// This is especially important for the touch event shim,
|
||||
// in order to prevent creating touch event for these fake mouse events.
|
||||
inputSource = Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH;
|
||||
actions.inputSource = Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH;
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,7 +318,7 @@ function deleteSession(msg) {
|
||||
// reset frame to the top-most frame
|
||||
curFrame = content;
|
||||
curFrame.focus();
|
||||
touchIds = {};
|
||||
actions.touchIds = {};
|
||||
}
|
||||
|
||||
/*
|
||||
@ -378,7 +370,7 @@ function sendError(message, status, trace, command_id) {
|
||||
function resetValues() {
|
||||
sandbox = null;
|
||||
curFrame = content;
|
||||
mouseEventsOnly = false;
|
||||
actions.mouseEventsOnly = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -404,6 +396,22 @@ function wasInterrupted() {
|
||||
return sendSyncMessage("MarionetteFrame:getInterruptedState", {})[0].value;
|
||||
}
|
||||
|
||||
function checkForInterrupted() {
|
||||
if (wasInterrupted()) {
|
||||
if (previousFrame) {
|
||||
//if previousFrame is set, then we're in a single process environment
|
||||
cuFrame = actions.frame = previousFrame;
|
||||
previousFrame = null;
|
||||
sandbox = null;
|
||||
}
|
||||
else {
|
||||
//else we're in OOP environment, so we'll switch to the original OOP frame
|
||||
sendSyncMessage("Marionette:switchToModalOrigin");
|
||||
}
|
||||
sendSyncMessage("Marionette:switchedToFrame", { restorePrevious: true });
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Marionette Methods
|
||||
*/
|
||||
@ -731,7 +739,7 @@ function emitTouchEvent(type, touch) {
|
||||
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIWebNavigation).
|
||||
QueryInterface(Components.interfaces.nsIDocShell);
|
||||
if (docShell.asyncPanZoomEnabled && scrolling) {
|
||||
if (docShell.asyncPanZoomEnabled && actions.scrolling) {
|
||||
// if we're in APZ and we're scrolling, we must use injectTouchEvent to dispatch our touchmove events
|
||||
let index = sendSyncMessage("MarionetteFrame:getCurrentFrameId");
|
||||
// only call emitTouchEventForIFrame if we're inside an iframe.
|
||||
@ -758,53 +766,6 @@ function emitTouchEvent(type, touch) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
function emitMouseEvent(doc, type, elClientX, elClientY, button, clickCount, modifiers) {
|
||||
if (!wasInterrupted()) {
|
||||
let loggingInfo = "emitting Mouse event of type " + type +
|
||||
" at coordinates (" + elClientX + ", " + elClientY +
|
||||
") relative to the viewport\n" +
|
||||
" button: " + button + "\n" +
|
||||
" clickCount: " + clickCount + "\n";
|
||||
dumpLog(loggingInfo);
|
||||
/*
|
||||
Disabled per bug 888303
|
||||
marionetteLogObj.log(loggingInfo, "TRACE");
|
||||
sendSyncMessage("Marionette:shareData",
|
||||
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
*/
|
||||
let win = doc.defaultView;
|
||||
let domUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
let mods;
|
||||
if (typeof modifiers != "undefined") {
|
||||
mods = utils._parseModifiers(modifiers);
|
||||
} else {
|
||||
mods = 0;
|
||||
}
|
||||
domUtils.sendMouseEvent(type, elClientX, elClientY, button || 0, clickCount || 1,
|
||||
mods, false, 0, inputSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that perform a mouse tap
|
||||
*/
|
||||
function mousetap(doc, x, y, keyModifiers) {
|
||||
emitMouseEvent(doc, 'mousemove', x, y, null, null, keyModifiers);
|
||||
emitMouseEvent(doc, 'mousedown', x, y, null, null, keyModifiers);
|
||||
emitMouseEvent(doc, 'mouseup', x, y, null, null, keyModifiers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -825,6 +786,7 @@ function coordinates(target, x, y) {
|
||||
return coords;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function returns true if the given coordinates are in the viewport.
|
||||
* @param 'x', and 'y' are the coordinates relative to the target.
|
||||
@ -876,113 +838,6 @@ function checkVisible(el, x, y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//x and y are coordinates relative to the viewport
|
||||
function generateEvents(type, x, y, touchId, target, keyModifiers) {
|
||||
lastCoordinates = [x, y];
|
||||
let doc = curFrame.document;
|
||||
switch (type) {
|
||||
case 'tap':
|
||||
if (mouseEventsOnly) {
|
||||
mousetap(target.ownerDocument, x, y);
|
||||
}
|
||||
else {
|
||||
let touchId = nextTouchId++;
|
||||
let touch = createATouch(target, x, y, touchId);
|
||||
emitTouchEvent('touchstart', touch);
|
||||
emitTouchEvent('touchend', touch);
|
||||
mousetap(target.ownerDocument, x, y);
|
||||
}
|
||||
lastCoordinates = null;
|
||||
break;
|
||||
case 'press':
|
||||
isTap = true;
|
||||
if (mouseEventsOnly) {
|
||||
emitMouseEvent(doc, 'mousemove', x, y, null, null, keyModifiers);
|
||||
emitMouseEvent(doc, 'mousedown', x, y, null, null, keyModifiers);
|
||||
}
|
||||
else {
|
||||
let touchId = nextTouchId++;
|
||||
let touch = createATouch(target, x, y, touchId);
|
||||
emitTouchEvent('touchstart', touch);
|
||||
touchIds[touchId] = touch;
|
||||
return touchId;
|
||||
}
|
||||
break;
|
||||
case 'release':
|
||||
if (mouseEventsOnly) {
|
||||
emitMouseEvent(doc, 'mouseup', lastCoordinates[0], lastCoordinates[1],
|
||||
null, null, keyModifiers);
|
||||
}
|
||||
else {
|
||||
let touch = touchIds[touchId];
|
||||
touch = createATouch(touch.target, lastCoordinates[0], lastCoordinates[1], touchId);
|
||||
emitTouchEvent('touchend', touch);
|
||||
if (isTap) {
|
||||
mousetap(touch.target.ownerDocument, touch.clientX, touch.clientY, keyModifiers);
|
||||
}
|
||||
delete touchIds[touchId];
|
||||
}
|
||||
isTap = false;
|
||||
lastCoordinates = null;
|
||||
break;
|
||||
case 'cancel':
|
||||
isTap = false;
|
||||
if (mouseEventsOnly) {
|
||||
emitMouseEvent(doc, 'mouseup', lastCoordinates[0], lastCoordinates[1],
|
||||
null, null, keyModifiers);
|
||||
}
|
||||
else {
|
||||
emitTouchEvent('touchcancel', touchIds[touchId]);
|
||||
delete touchIds[touchId];
|
||||
}
|
||||
lastCoordinates = null;
|
||||
break;
|
||||
case 'move':
|
||||
isTap = false;
|
||||
if (mouseEventsOnly) {
|
||||
emitMouseEvent(doc, 'mousemove', x, y, null, null, keyModifiers);
|
||||
}
|
||||
else {
|
||||
touch = createATouch(touchIds[touchId].target, x, y, touchId);
|
||||
touchIds[touchId] = touch;
|
||||
emitTouchEvent('touchmove', touch);
|
||||
}
|
||||
break;
|
||||
case 'contextmenu':
|
||||
isTap = false;
|
||||
let event = curFrame.document.createEvent('MouseEvents');
|
||||
if (mouseEventsOnly) {
|
||||
target = doc.elementFromPoint(lastCoordinates[0], lastCoordinates[1]);
|
||||
}
|
||||
else {
|
||||
target = touchIds[touchId].target;
|
||||
}
|
||||
let [ clientX, clientY,
|
||||
pageX, pageY,
|
||||
screenX, screenY ] = 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};
|
||||
}
|
||||
if (wasInterrupted()) {
|
||||
if (previousFrame) {
|
||||
//if previousFrame is set, then we're in a single process environment
|
||||
curFrame = previousFrame;
|
||||
previousFrame = null;
|
||||
sandbox = null;
|
||||
}
|
||||
else {
|
||||
//else we're in OOP environment, so we'll switch to the original OOP frame
|
||||
sendSyncMessage("Marionette:switchToModalOrigin");
|
||||
}
|
||||
sendSyncMessage("Marionette:switchedToFrame", { restorePrevious: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that perform a single tap
|
||||
@ -1001,10 +856,16 @@ function singleTap(msg) {
|
||||
}
|
||||
checkActionableAccessibility(acc);
|
||||
if (!curFrame.document.createTouch) {
|
||||
mouseEventsOnly = true;
|
||||
actions.mouseEventsOnly = true;
|
||||
}
|
||||
c = coordinates(el, msg.json.corx, msg.json.cory);
|
||||
generateEvents('tap', c.x, c.y, null, el);
|
||||
let c = coordinates(el, msg.json.corx, msg.json.cory);
|
||||
if (!actions.mouseEventsOnly) {
|
||||
let touchId = actions.nextTouchId++;
|
||||
let touch = createATouch(el, c.x, c.y, touchId);
|
||||
emitTouchEvent('touchstart', touch);
|
||||
emitTouchEvent('touchend', touch);
|
||||
}
|
||||
actions.mouseTap(el.ownerDocument, c.x, c.y);
|
||||
sendOk(msg.json.command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1070,20 +931,6 @@ function checkActionableAccessibility(accesible) {
|
||||
accessibility.handleErrorMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an element and a pair of coordinates, returns an array of the form
|
||||
* [ clientX, clientY, pageX, pageY, screenX, screenY ]
|
||||
*/
|
||||
function getCoordinateInfo(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
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create a touch based on the element
|
||||
@ -1093,138 +940,11 @@ function createATouch(el, corx, cory, touchId) {
|
||||
let doc = el.ownerDocument;
|
||||
let win = doc.defaultView;
|
||||
let [clientX, clientY, pageX, pageY, screenX, screenY] =
|
||||
getCoordinateInfo(el, corx, cory);
|
||||
actions.getCoordinateInfo(el, corx, cory);
|
||||
let atouch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY);
|
||||
return atouch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function actions(chain, touchId, command_id, i, keyModifiers) {
|
||||
if (typeof i === "undefined") {
|
||||
i = 0;
|
||||
}
|
||||
if (typeof keyModifiers === "undefined") {
|
||||
keyModifiers = {
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
metaKey: false
|
||||
};
|
||||
}
|
||||
if (i == chain.length) {
|
||||
sendResponse({value: touchId}, command_id);
|
||||
return;
|
||||
}
|
||||
let pack = chain[i];
|
||||
let command = pack[0];
|
||||
let el;
|
||||
let c;
|
||||
i++;
|
||||
if (['press', 'wait', 'keyDown', 'keyUp'].indexOf(command) == -1) {
|
||||
//if mouseEventsOnly, then touchIds isn't used
|
||||
if (!(touchId in touchIds) && !mouseEventsOnly) {
|
||||
sendError("Element has not been pressed", 500, null, command_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch(command) {
|
||||
case 'keyDown':
|
||||
utils.sendKeyDown(pack[1], keyModifiers, curFrame);
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
break;
|
||||
case 'keyUp':
|
||||
utils.sendKeyUp(pack[1], keyModifiers, curFrame);
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
break;
|
||||
case 'click':
|
||||
el = elementManager.getKnownElement(pack[1], curFrame);
|
||||
let button = pack[2];
|
||||
let clickCount = pack[3];
|
||||
c = coordinates(el, null, null);
|
||||
emitMouseEvent(el.ownerDocument, 'mousemove', c.x, c.y, button, clickCount,
|
||||
keyModifiers);
|
||||
emitMouseEvent(el.ownerDocument, 'mousedown', c.x, c.y, button, clickCount,
|
||||
keyModifiers);
|
||||
emitMouseEvent(el.ownerDocument, 'mouseup', c.x, c.y, button, clickCount,
|
||||
keyModifiers);
|
||||
if (button == 2) {
|
||||
emitMouseEvent(el.ownerDocument, 'contextmenu', c.x, c.y, button, clickCount,
|
||||
keyModifiers);
|
||||
}
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
break;
|
||||
case 'press':
|
||||
if (lastCoordinates) {
|
||||
generateEvents('cancel', lastCoordinates[0], lastCoordinates[1],
|
||||
touchId, null, keyModifiers);
|
||||
sendError("Invalid Command: press cannot follow an active touch event", 500, null, command_id);
|
||||
return;
|
||||
}
|
||||
// look ahead to check if we're scrolling. Needed for APZ touch dispatching.
|
||||
if ((i != chain.length) && (chain[i][0].indexOf('move') !== -1)) {
|
||||
scrolling = true;
|
||||
}
|
||||
el = elementManager.getKnownElement(pack[1], curFrame);
|
||||
c = coordinates(el, pack[2], pack[3]);
|
||||
touchId = generateEvents('press', c.x, c.y, null, el, keyModifiers);
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
break;
|
||||
case 'release':
|
||||
generateEvents('release', lastCoordinates[0], lastCoordinates[1],
|
||||
touchId, null, keyModifiers);
|
||||
actions(chain, null, command_id, i, keyModifiers);
|
||||
scrolling = false;
|
||||
break;
|
||||
case 'move':
|
||||
el = elementManager.getKnownElement(pack[1], curFrame);
|
||||
c = coordinates(el);
|
||||
generateEvents('move', c.x, c.y, touchId, null, keyModifiers);
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
break;
|
||||
case 'moveByOffset':
|
||||
generateEvents('move', lastCoordinates[0] + pack[1], lastCoordinates[1] + pack[2],
|
||||
touchId, null, keyModifiers);
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
break;
|
||||
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){}
|
||||
if (time >= standard && isTap) {
|
||||
chain.splice(i, 0, ['longPress'], ['wait', (time-standard)/1000]);
|
||||
time = standard;
|
||||
}
|
||||
checkTimer.initWithCallback(function() {
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
}, time, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
else {
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
}
|
||||
break;
|
||||
case 'cancel':
|
||||
generateEvents('cancel', lastCoordinates[0], lastCoordinates[1],
|
||||
touchId, null, keyModifiers);
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
scrolling = false;
|
||||
break;
|
||||
case 'longPress':
|
||||
generateEvents('contextmenu', lastCoordinates[0], lastCoordinates[1],
|
||||
touchId, null, keyModifiers);
|
||||
actions(chain, touchId, command_id, i, keyModifiers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to start action chain on one finger
|
||||
*/
|
||||
@ -1232,20 +952,21 @@ function actionChain(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
let args = msg.json.chain;
|
||||
let touchId = msg.json.nextId;
|
||||
try {
|
||||
let commandArray = elementManager.convertWrappedArguments(args, curFrame);
|
||||
// loop the action array [ ['press', id], ['move', id], ['release', id] ]
|
||||
if (touchId == null) {
|
||||
touchId = nextTouchId++;
|
||||
}
|
||||
if (!curFrame.document.createTouch) {
|
||||
mouseEventsOnly = true;
|
||||
}
|
||||
actions(commandArray, touchId, command_id);
|
||||
}
|
||||
catch (e) {
|
||||
sendError(e.message, e.code, e.stack, msg.json.command_id);
|
||||
}
|
||||
|
||||
let callbacks = {};
|
||||
callbacks.onSuccess = (value) => {
|
||||
sendResponse(value, command_id);
|
||||
};
|
||||
callbacks.onError = (message, code, trace) => {
|
||||
sendError(message, code, trace, msg.json.command_id);
|
||||
};
|
||||
|
||||
let touchProvider = {};
|
||||
touchProvider.createATouch = createATouch;
|
||||
touchProvider.emitTouchEvent = emitTouchEvent;
|
||||
|
||||
actions.dispatchActions(args, touchId, curFrame, elementManager, callbacks,
|
||||
touchProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,6 +17,7 @@ let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-simpletest.js");
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-common.js");
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-actions.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-frame-manager.js");
|
||||
Cu.import("chrome://marionette/content/marionette-elements.js");
|
||||
@ -190,6 +191,7 @@ function MarionetteServerConnection(aPrefix, aTransport, aServer)
|
||||
this.observing = null;
|
||||
this._browserIds = new WeakMap();
|
||||
this.quitFlags = null;
|
||||
this.actions = new ActionChain(utils);
|
||||
}
|
||||
|
||||
MarionetteServerConnection.prototype = {
|
||||
@ -1878,18 +1880,36 @@ MarionetteServerConnection.prototype = {
|
||||
* 'value' represents a nested array: inner array represents each event; outer array represents collection of events
|
||||
*/
|
||||
actionChain: function MDA_actionChain(aRequest) {
|
||||
this.command_id = this.getCommandId();
|
||||
let command_id = this.command_id = this.getCommandId();
|
||||
let chain = aRequest.parameters.chain;
|
||||
let nextId = aRequest.parameters.nextId;
|
||||
if (this.context == "chrome") {
|
||||
this.sendError("Command 'actionChain' is not available in chrome context", 500, null, this.command_id);
|
||||
}
|
||||
else {
|
||||
if (appName != 'Firefox') {
|
||||
// Be conservative until this has a use case and is established to work as
|
||||
// expected on b2g/fennec.
|
||||
this.sendError("Command 'actionChain' is not available in chrome context",
|
||||
500, null, this.command_id);
|
||||
}
|
||||
|
||||
let callbacks = {};
|
||||
callbacks.onSuccess = (value) => {
|
||||
this.sendResponse(value, command_id);
|
||||
};
|
||||
callbacks.onError = (message, code, trace) => {
|
||||
this.sendError(message, code, trace, command_id);
|
||||
};
|
||||
|
||||
let currWin = this.getCurrentWindow();
|
||||
let elementManager = this.curBrowser.elementManager;
|
||||
this.actions.dispatchActions(chain, nextId, currWin, elementManager, callbacks);
|
||||
} else {
|
||||
this.addFrameCloseListener("action chain");
|
||||
this.sendAsync("actionChain",
|
||||
{
|
||||
chain: aRequest.parameters.chain,
|
||||
nextId: aRequest.parameters.nextId
|
||||
chain: chain,
|
||||
nextId: nextId
|
||||
},
|
||||
this.command_id);
|
||||
command_id);
|
||||
}
|
||||
},
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user