зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset eb6428c5e590 (bug 1109282
) for webplatform test failures.
This commit is contained in:
Родитель
ce0524a7d7
Коммит
25783fb2c3
|
@ -8,7 +8,6 @@ from marionette import Marionette, HTMLElement, Actions, MultiActions
|
||||||
from marionette_test import MarionetteTestCase, MarionetteJSTestCase, CommonTestCase, expectedFailure, skip, SkipTest
|
from marionette_test import MarionetteTestCase, MarionetteJSTestCase, CommonTestCase, expectedFailure, skip, SkipTest
|
||||||
from errors import (
|
from errors import (
|
||||||
ElementNotVisibleException,
|
ElementNotVisibleException,
|
||||||
ElementNotAccessibleException,
|
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
FrameSendFailureError,
|
FrameSendFailureError,
|
||||||
FrameSendNotInitializedError,
|
FrameSendNotInitializedError,
|
||||||
|
|
|
@ -13,7 +13,6 @@ class ErrorCodes(object):
|
||||||
ELEMENT_NOT_VISIBLE = 11
|
ELEMENT_NOT_VISIBLE = 11
|
||||||
INVALID_ELEMENT_STATE = 12
|
INVALID_ELEMENT_STATE = 12
|
||||||
UNKNOWN_ERROR = 13
|
UNKNOWN_ERROR = 13
|
||||||
ELEMENT_NOT_ACCESSIBLE = 56
|
|
||||||
ELEMENT_IS_NOT_SELECTABLE = 15
|
ELEMENT_IS_NOT_SELECTABLE = 15
|
||||||
JAVASCRIPT_ERROR = 17
|
JAVASCRIPT_ERROR = 17
|
||||||
XPATH_LOOKUP_ERROR = 19
|
XPATH_LOOKUP_ERROR = 19
|
||||||
|
@ -114,9 +113,6 @@ class ElementNotVisibleException(MarionetteException):
|
||||||
super(ElementNotVisibleException, self).__init__(
|
super(ElementNotVisibleException, self).__init__(
|
||||||
message, status=status, cause=cause, stacktrace=stacktrace)
|
message, status=status, cause=cause, stacktrace=stacktrace)
|
||||||
|
|
||||||
class ElementNotAccessibleException(MarionetteException):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class NoSuchFrameException(MarionetteException):
|
class NoSuchFrameException(MarionetteException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -677,8 +677,6 @@ class Marionette(object):
|
||||||
raise errors.StaleElementException(message=message, status=status, stacktrace=stacktrace)
|
raise errors.StaleElementException(message=message, status=status, stacktrace=stacktrace)
|
||||||
elif status == errors.ErrorCodes.ELEMENT_NOT_VISIBLE:
|
elif status == errors.ErrorCodes.ELEMENT_NOT_VISIBLE:
|
||||||
raise errors.ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace)
|
raise errors.ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace)
|
||||||
elif status == errors.ErrorCodes.ELEMENT_NOT_ACCESSIBLE:
|
|
||||||
raise errors.ElementNotAccessibleException(message=message, status=status, stacktrace=stacktrace)
|
|
||||||
elif status == errors.ErrorCodes.INVALID_ELEMENT_STATE:
|
elif status == errors.ErrorCodes.INVALID_ELEMENT_STATE:
|
||||||
raise errors.InvalidElementStateException(message=message, status=status, stacktrace=stacktrace)
|
raise errors.InvalidElementStateException(message=message, status=status, stacktrace=stacktrace)
|
||||||
elif status == errors.ErrorCodes.UNKNOWN_ERROR:
|
elif status == errors.ErrorCodes.UNKNOWN_ERROR:
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
# 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_test import MarionetteTestCase
|
|
||||||
from errors import ElementNotAccessibleException
|
|
||||||
from errors import ElementNotVisibleException
|
|
||||||
|
|
||||||
|
|
||||||
class TestAccessibility(MarionetteTestCase):
|
|
||||||
|
|
||||||
# Elements that are accessible with and without the accessibliity API
|
|
||||||
valid_elementIDs = [
|
|
||||||
# Button1 is an accessible button with a valid accessible name
|
|
||||||
# computed from subtree
|
|
||||||
"button1",
|
|
||||||
# Button2 is an accessible button with a valid accessible name
|
|
||||||
# computed from aria-label
|
|
||||||
"button2"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Elements that are not accessible with the accessibility API
|
|
||||||
invalid_elementIDs = [
|
|
||||||
# Button3 does not have an accessible object
|
|
||||||
"button3",
|
|
||||||
# Button4 does not support any accessible actions
|
|
||||||
"button4",
|
|
||||||
# Button5 does not have a correct accessibility role and may not be
|
|
||||||
# manipulated via the accessibility API
|
|
||||||
"button5",
|
|
||||||
# Button6 is missing an accesible name
|
|
||||||
"button6",
|
|
||||||
# Button7 is not currently visible via the accessibility API and may
|
|
||||||
# not be manipulated by it
|
|
||||||
"button7",
|
|
||||||
# Button8 is not currently visible via the accessibility API and may
|
|
||||||
# not be manipulated by it (in hidden subtree)
|
|
||||||
"button8"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Elements that are either accessible to accessibility API or not accessible
|
|
||||||
# at all
|
|
||||||
falsy_elements = [
|
|
||||||
# Element is only visible to the accessibility API and may be
|
|
||||||
# manipulated by it
|
|
||||||
"button9",
|
|
||||||
# Element is not currently visible
|
|
||||||
"button10"
|
|
||||||
]
|
|
||||||
|
|
||||||
def run_element_test(self, ids, testFn):
|
|
||||||
for id in ids:
|
|
||||||
element = self.marionette.find_element("id", id)
|
|
||||||
testFn(element)
|
|
||||||
|
|
||||||
def setup_accessibility(self, raisesAccessibilityExceptions=True, navigate=True):
|
|
||||||
self.marionette.delete_session()
|
|
||||||
self.marionette.start_session(
|
|
||||||
{"raisesAccessibilityExceptions": raisesAccessibilityExceptions})
|
|
||||||
# Navigate to test_accessibility.html
|
|
||||||
if navigate:
|
|
||||||
test_accessibility = self.marionette.absolute_url("test_accessibility.html")
|
|
||||||
self.marionette.navigate(test_accessibility)
|
|
||||||
|
|
||||||
def test_valid_single_tap(self):
|
|
||||||
self.setup_accessibility()
|
|
||||||
# No exception should be raised
|
|
||||||
self.run_element_test(self.valid_elementIDs, lambda button: button.tap())
|
|
||||||
|
|
||||||
def test_invalid_single_tap(self):
|
|
||||||
self.setup_accessibility()
|
|
||||||
self.run_element_test(self.invalid_elementIDs,
|
|
||||||
lambda button: self.assertRaises(ElementNotAccessibleException,
|
|
||||||
button.tap))
|
|
||||||
self.run_element_test(self.falsy_elements,
|
|
||||||
lambda button: self.assertRaises(ElementNotAccessibleException,
|
|
||||||
button.tap))
|
|
||||||
|
|
||||||
def test_invalid_single_tap_no_exceptions(self):
|
|
||||||
self.setup_accessibility(False, True)
|
|
||||||
# No exception should be raised
|
|
||||||
self.run_element_test(self.invalid_elementIDs, lambda button: button.tap())
|
|
||||||
# Elements are invisible
|
|
||||||
self.run_element_test(self.falsy_elements,
|
|
||||||
lambda button: self.assertRaises(ElementNotVisibleException,
|
|
||||||
button.tap))
|
|
|
@ -16,8 +16,6 @@ skip = false
|
||||||
[test_session.py]
|
[test_session.py]
|
||||||
[test_capabilities.py]
|
[test_capabilities.py]
|
||||||
|
|
||||||
[test_accessibility.py]
|
|
||||||
|
|
||||||
[test_expectedfail.py]
|
[test_expectedfail.py]
|
||||||
expected = fail
|
expected = fail
|
||||||
[test_import_script.py]
|
[test_import_script.py]
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
<!-- 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>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<head>
|
|
||||||
<title>Marionette Test</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<button id="button1">button1</button>
|
|
||||||
<button id="button2" aria-label="button2"></button>
|
|
||||||
<span id="button3">I am a bad button with no accessible</span>
|
|
||||||
<h1 id="button4">I am a bad button that is actually a header</h1>
|
|
||||||
<h1 id="button5">
|
|
||||||
I am a bad button that is actually an actionable header with a listener
|
|
||||||
</h1>
|
|
||||||
<button id="button6"></button>
|
|
||||||
<button id="button7" aria-hidden="true">button7</button>
|
|
||||||
<div aria-hidden="true">
|
|
||||||
<button id="button8">button8</button>
|
|
||||||
</div>
|
|
||||||
<button id="button9" style="position:absolute;left:-100px;top:-455px;">
|
|
||||||
button9
|
|
||||||
</button>
|
|
||||||
<button id="button10" style="visibility:hidden;">
|
|
||||||
button10
|
|
||||||
</button>
|
|
||||||
<script>
|
|
||||||
document.getElementById('button5').addEventListener('click', function() {
|
|
||||||
// A pseudo button that has a listener but is missing button semantics.
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -12,7 +12,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = [
|
this.EXPORTED_SYMBOLS = [
|
||||||
"Accessibility",
|
|
||||||
"ElementManager",
|
"ElementManager",
|
||||||
"CLASS_NAME",
|
"CLASS_NAME",
|
||||||
"SELECTOR",
|
"SELECTOR",
|
||||||
|
@ -48,127 +47,6 @@ function ElementException(msg, num, stack) {
|
||||||
this.stack = stack;
|
this.stack = stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Accessibility = function Accessibility() {
|
|
||||||
// A flag indicating whether the accessibility issue should be logged or cause
|
|
||||||
// an exception. Default: log to stdout.
|
|
||||||
this.strict = false;
|
|
||||||
this.accessibleRetrieval = Components.classes[
|
|
||||||
'@mozilla.org/accessibleRetrieval;1'].getService(
|
|
||||||
Components.interfaces.nsIAccessibleRetrieval);
|
|
||||||
};
|
|
||||||
|
|
||||||
Accessibility.prototype = {
|
|
||||||
/**
|
|
||||||
* Accessible object roles that support some action
|
|
||||||
* @type Object
|
|
||||||
*/
|
|
||||||
actionableRoles: new Set([
|
|
||||||
'pushbutton',
|
|
||||||
'checkbutton',
|
|
||||||
'combobox',
|
|
||||||
'key',
|
|
||||||
'link',
|
|
||||||
'menuitem',
|
|
||||||
'check menu item',
|
|
||||||
'radio menu item',
|
|
||||||
'option',
|
|
||||||
'radiobutton',
|
|
||||||
'slider',
|
|
||||||
'spinbutton',
|
|
||||||
'pagetab',
|
|
||||||
'entry',
|
|
||||||
'outlineitem'
|
|
||||||
]),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an accessible object for a DOM element
|
|
||||||
* @param nsIDOMElement element
|
|
||||||
* @param Boolean mustHaveAccessible a flag indicating that the element must
|
|
||||||
* have an accessible object
|
|
||||||
* @return nsIAccessible object for the element
|
|
||||||
*/
|
|
||||||
getAccessibleObject(element, mustHaveAccessible = false) {
|
|
||||||
let acc = this.accessibleRetrieval.getAccessibleFor(element);
|
|
||||||
if (!acc && mustHaveAccessible) {
|
|
||||||
this.handleErrorMessage('Element does not have an accessible object');
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the accessible has a role that supports some action
|
|
||||||
* @param nsIAccessible object
|
|
||||||
* @return Boolean an indicator of role being actionable
|
|
||||||
*/
|
|
||||||
isActionableRole(accessible) {
|
|
||||||
return this.actionableRoles.has(
|
|
||||||
this.accessibleRetrieval.getStringRole(accessible.role));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an accessible has at least one action that it supports
|
|
||||||
* @param nsIAccessible object
|
|
||||||
* @return Boolean an indicator of supporting at least one accessible action
|
|
||||||
*/
|
|
||||||
hasActionCount(accessible) {
|
|
||||||
return accessible.actionCount > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an accessible has a valid name
|
|
||||||
* @param nsIAccessible object
|
|
||||||
* @return Boolean an indicator that the element has a non empty valid name
|
|
||||||
*/
|
|
||||||
hasValidName(accessible) {
|
|
||||||
return accessible.name && accessible.name.trim();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an accessible has a set hidden attribute
|
|
||||||
* @param nsIAccessible object
|
|
||||||
* @return Boolean an indicator that the element has a hidden accessible
|
|
||||||
* attribute set to true
|
|
||||||
*/
|
|
||||||
hasHiddenAttribute(accessible) {
|
|
||||||
let hidden;
|
|
||||||
try {
|
|
||||||
hidden = accessible.attributes.getStringProperty('hidden');
|
|
||||||
} finally {
|
|
||||||
// If the property is missing, exception will be thrown.
|
|
||||||
return hidden && hidden === 'true';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an accessible is hidden from the user of the accessibility API
|
|
||||||
* @param nsIAccessible object
|
|
||||||
* @return Boolean an indicator that the element is hidden from the user
|
|
||||||
*/
|
|
||||||
isHidden(accessible) {
|
|
||||||
while (accessible) {
|
|
||||||
if (this.hasHiddenAttribute(accessible)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
accessible = accessible.parent;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send an error message or log the error message in the log
|
|
||||||
* @param String message
|
|
||||||
*/
|
|
||||||
handleErrorMessage(message) {
|
|
||||||
if (!message) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.strict) {
|
|
||||||
throw new ElementException(message, 56, null);
|
|
||||||
}
|
|
||||||
dump(Date.now() + " Marionette: " + message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.ElementManager = function ElementManager(notSupported) {
|
this.ElementManager = function ElementManager(notSupported) {
|
||||||
this.seenItems = {};
|
this.seenItems = {};
|
||||||
this.timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
|
this.timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
|
||||||
|
|
|
@ -39,7 +39,6 @@ let listenerId = null; //unique ID of this listener
|
||||||
let curFrame = content;
|
let curFrame = content;
|
||||||
let previousFrame = null;
|
let previousFrame = null;
|
||||||
let elementManager = new ElementManager([]);
|
let elementManager = new ElementManager([]);
|
||||||
let accessibility = new Accessibility();
|
|
||||||
let importedScripts = null;
|
let importedScripts = null;
|
||||||
let inputSource = null;
|
let inputSource = null;
|
||||||
|
|
||||||
|
@ -211,7 +210,6 @@ function waitForReady() {
|
||||||
*/
|
*/
|
||||||
function newSession(msg) {
|
function newSession(msg) {
|
||||||
isB2G = msg.json.B2G;
|
isB2G = msg.json.B2G;
|
||||||
accessibility.strict = msg.json.raisesAccessibilityExceptions;
|
|
||||||
resetValues();
|
resetValues();
|
||||||
if (isB2G) {
|
if (isB2G) {
|
||||||
readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT);
|
readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||||
|
@ -947,15 +945,11 @@ function singleTap(msg) {
|
||||||
let command_id = msg.json.command_id;
|
let command_id = msg.json.command_id;
|
||||||
try {
|
try {
|
||||||
let el = elementManager.getKnownElement(msg.json.id, curFrame);
|
let el = elementManager.getKnownElement(msg.json.id, curFrame);
|
||||||
let acc = accessibility.getAccessibleObject(el, true);
|
|
||||||
// after this block, the element will be scrolled into view
|
// after this block, the element will be scrolled into view
|
||||||
let visible = checkVisible(el, msg.json.corx, msg.json.cory);
|
if (!checkVisible(el, msg.json.corx, msg.json.cory)) {
|
||||||
checkVisibleAccessibility(acc, visible);
|
sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
|
||||||
if (!visible) {
|
return;
|
||||||
sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
checkActionableAccessibility(acc);
|
|
||||||
if (!curFrame.document.createTouch) {
|
if (!curFrame.document.createTouch) {
|
||||||
mouseEventsOnly = true;
|
mouseEventsOnly = true;
|
||||||
}
|
}
|
||||||
|
@ -968,48 +962,6 @@ function singleTap(msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the element's visible state corresponds to its accessibility API
|
|
||||||
* visibility
|
|
||||||
* @param nsIAccessible object
|
|
||||||
* @param Boolean visible element's visibility state
|
|
||||||
*/
|
|
||||||
function checkVisibleAccessibility(accesible, visible) {
|
|
||||||
if (!accesible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hiddenAccessibility = accessibility.isHidden(accesible);
|
|
||||||
let message;
|
|
||||||
if (visible && hiddenAccessibility) {
|
|
||||||
message = 'Element is not currently visible via the accessibility API ' +
|
|
||||||
'and may not be manipulated by it';
|
|
||||||
} else if (!visible && !hiddenAccessibility) {
|
|
||||||
message = 'Element is currently only visible via the accessibility API ' +
|
|
||||||
'and can be manipulated by it';
|
|
||||||
}
|
|
||||||
accessibility.handleErrorMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if it is possible to activate an element with the accessibility API
|
|
||||||
* @param nsIAccessible object
|
|
||||||
*/
|
|
||||||
function checkActionableAccessibility(accesible) {
|
|
||||||
if (!accesible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let message;
|
|
||||||
if (!accessibility.hasActionCount(accesible)) {
|
|
||||||
message = 'Element does not support any accessible actions';
|
|
||||||
} else if (!accessibility.isActionableRole(accesible)) {
|
|
||||||
message = 'Element does not have a correct accessibility role ' +
|
|
||||||
'and may not be manipulated via the accessibility API';
|
|
||||||
} else if (!accessibility.hasValidName(accesible)) {
|
|
||||||
message = 'Element is missing an accesible name';
|
|
||||||
}
|
|
||||||
accessibility.handleErrorMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an element and a pair of coordinates, returns an array of the form
|
* Given an element and a pair of coordinates, returns an array of the form
|
||||||
* [ clientX, clientY, pageX, pageY, screenX, screenY ]
|
* [ clientX, clientY, pageX, pageY, screenX, screenY ]
|
||||||
|
|
|
@ -174,7 +174,6 @@ function MarionetteServerConnection(aPrefix, aTransport, aServer)
|
||||||
// Supported features
|
// Supported features
|
||||||
"handlesAlerts": false,
|
"handlesAlerts": false,
|
||||||
"nativeEvents": false,
|
"nativeEvents": false,
|
||||||
"raisesAccessibilityExceptions": false,
|
|
||||||
"rotatable": appName == "B2G",
|
"rotatable": appName == "B2G",
|
||||||
"secureSsl": false,
|
"secureSsl": false,
|
||||||
"takesElementScreenshot": true,
|
"takesElementScreenshot": true,
|
||||||
|
@ -2900,10 +2899,8 @@ MarionetteServerConnection.prototype = {
|
||||||
this.curBrowser.elementManager.seenItems[reg.id] = Cu.getWeakReference(listenerWindow);
|
this.curBrowser.elementManager.seenItems[reg.id] = Cu.getWeakReference(listenerWindow);
|
||||||
if (nullPrevious && (this.curBrowser.curFrameId != null)) {
|
if (nullPrevious && (this.curBrowser.curFrameId != null)) {
|
||||||
if (!this.sendAsync("newSession",
|
if (!this.sendAsync("newSession",
|
||||||
{ B2G: (appName == "B2G"),
|
{ B2G: (appName == "B2G") },
|
||||||
raisesAccessibilityExceptions:
|
this.newSessionCommandId)) {
|
||||||
this.sessionCapabilities.raisesAccessibilityExceptions },
|
|
||||||
this.newSessionCommandId)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.curBrowser.newSession) {
|
if (this.curBrowser.newSession) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче