Bug 1598026 - connect to the OOP frame server when children of the remote iframe are queried. r=jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D66156

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Yura Zenevich 2020-04-08 23:33:19 +00:00
Родитель 969e685d94
Коммит cd3662346d
8 изменённых файлов: 243 добавлений и 15 удалений

Просмотреть файл

@ -32,5 +32,6 @@ skip-if = (os == 'linux' && debug && bits == 64) # Bug 1511247
[browser_accessibility_tree_audit_toolbar.js]
[browser_accessibility_tree_audit.js]
[browser_accessibility_tree_contrast.js]
[browser_accessibility_tree_nagivation_oop.js]
[browser_accessibility_tree_nagivation.js]
[browser_accessibility_tree.js]

Просмотреть файл

@ -66,18 +66,18 @@ const tests = [
{
desc: "Select an accessible object.",
setup: async env => {
await selectAccessibleForNode(env, "body");
await selectAccessibleForNode(env, "h1");
},
expected: {
tree: [
{
role: "document",
name: `"Accessibility Panel Test"`,
selected: true,
},
{
role: "heading",
name: `"Top level header"`,
selected: true,
},
{
role: "text leaf",

Просмотреть файл

@ -0,0 +1,140 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URI = `<h1>Top level header</h1><p>This is a paragraph.</p>`;
/**
* Test data has the format of:
* {
* desc {String} description for better logging
* setup {Function} An optional setup that needs to be performed before
* the state of the tree and the sidebar can be checked.
* expected {JSON} An expected states for the tree and the sidebar.
* }
*/
const tests = [
{
desc: "Test the initial accessibility tree and sidebar states.",
expected: {
tree: [
{
role: "document",
name: `""text label`,
badges: ["text label"],
},
],
sidebar: {
name: "",
role: "document",
actions: [],
value: "",
description: "",
keyboardShortcut: "",
childCount: 1,
indexInParent: 0,
states: ["readonly", "focusable", "opaque", "enabled", "sensitive"],
},
},
},
{
desc: "Expand first tree node.",
setup: ({ doc }) => toggleRow(doc, 0),
expected: {
tree: [
{
role: "document",
name: `""text label`,
badges: ["text label"],
},
{
role: "internal frame",
name: `"Accessibility Panel Test (OOP)"`,
},
],
},
},
{
desc: "Expand second tree node. Display OOP document.",
setup: ({ doc }) => toggleRow(doc, 1),
expected: {
tree: [
{
role: "document",
name: `""text label`,
badges: ["text label"],
},
{
role: "internal frame",
name: `"Accessibility Panel Test (OOP)"`,
},
{
role: "document",
name: `"Accessibility Panel Test (OOP)"`,
},
],
sidebar: {
name: "Accessibility Panel Test (OOP)",
role: "internal frame",
actions: [],
value: "",
description: "",
keyboardShortcut: "",
childCount: 1,
indexInParent: 0,
states: ["focusable", "opaque", "enabled", "sensitive"],
},
},
},
{
desc: "Expand third tree node. Display OOP frame content.",
setup: ({ doc }) => toggleRow(doc, 2),
expected: {
tree: [
{
role: "document",
name: `""text label`,
badges: ["text label"],
},
{
role: "internal frame",
name: `"Accessibility Panel Test (OOP)"`,
},
{
role: "document",
name: `"Accessibility Panel Test (OOP)"`,
},
{
role: "heading",
name: `"Top level header"`,
},
{
role: "paragraph",
name: `""`,
},
],
sidebar: {
name: "Accessibility Panel Test (OOP)",
role: "document",
actions: [],
value: "",
description: "",
keyboardShortcut: "",
childCount: 2,
indexInParent: 0,
states: ["readonly", "focusable", "opaque", "enabled", "sensitive"],
},
},
},
];
/**
* Check navigation within the tree.
*/
addA11yPanelTestsTask(
tests,
TEST_URI,
"Test Accessibility panel tree navigation with OOP frame.",
{ remoteIframe: true }
);

Просмотреть файл

@ -752,16 +752,32 @@ async function runA11yPanelTests(tests, env) {
/**
* Build a valid URL from an HTML snippet.
* @param {String} uri HTML snippet
* @param {String} uri HTML snippet
* @param {Object} options options for the test
* @return {String} built URL
*/
function buildURL(uri) {
function buildURL(uri, options = {}) {
if (options.remoteIframe) {
const srcURL = new URL(`http://example.net/document-builder.sjs`);
srcURL.searchParams.append(
"html",
`<html>
<head>
<meta charset="utf-8"/>
<title>Accessibility Panel Test (OOP)</title>
</head>
<body>${uri}</body>
</html>`
);
uri = `<iframe title="Accessibility Panel Test (OOP)" src="${srcURL.href}"/>`;
}
return `data:text/html;charset=UTF-8,${encodeURIComponent(uri)}`;
}
/**
* Add a test task based on the test structure and a test URL.
* @param {JSON} tests test data that has the format of:
* @param {JSON} tests test data that has the format of:
* {
* desc {String} description for better logging
* setup {Function} An optional setup that needs to be
@ -770,11 +786,12 @@ function buildURL(uri) {
* expected {JSON} An expected states for the tree and
* the sidebar
* }
* @param {String} uri test URL
* @param {String} msg a message that is printed for the test
* @param {String} uri test URL
* @param {String} msg a message that is printed for the test
* @param {Object} options options for the test
*/
function addA11yPanelTestsTask(tests, uri, msg) {
addA11YPanelTask(msg, uri, env => runA11yPanelTests(tests, env));
function addA11yPanelTestsTask(tests, uri, msg, options) {
addA11YPanelTask(msg, uri, env => runA11yPanelTests(tests, env), options);
}
/**
@ -783,11 +800,12 @@ function addA11yPanelTestsTask(tests, uri, msg) {
* @param {String} msg a message that is printed for the test
* @param {String} uri test URL
* @param {Function} task task function containing the tests.
* @param {Object} options options for the test
*/
function addA11YPanelTask(msg, uri, task) {
function addA11YPanelTask(msg, uri, task, options = {}) {
add_task(async function a11YPanelTask() {
info(msg);
const env = await addTestTab(buildURL(uri));
const env = await addTestTab(buildURL(uri, options));
await task(env);
await disableAccessibilityInspector(env);
});

Просмотреть файл

@ -16,6 +16,11 @@ const {
simulatorSpec,
} = require("devtools/shared/specs/accessibility");
const events = require("devtools/shared/event-emitter");
const Services = require("Services");
const BROWSER_TOOLBOX_FISSION_ENABLED = Services.prefs.getBoolPref(
"devtools.browsertoolbox.fission",
false
);
class AccessibleFront extends FrontClassWithSpec(accessibleSpec) {
constructor(client, targetFront, parentFront) {
@ -38,6 +43,10 @@ class AccessibleFront extends FrontClassWithSpec(accessibleSpec) {
return this.getParent();
}
get remoteFrame() {
return BROWSER_TOOLBOX_FISSION_ENABLED && this._form.remoteFrame;
}
get role() {
return this._form.role;
}
@ -159,6 +168,34 @@ class AccessibleFront extends FrontClassWithSpec(accessibleSpec) {
Object.assign(this._form, properties);
});
}
async children() {
if (!this.remoteFrame) {
return super.children();
}
const { walker: domWalkerFront } = await this.targetFront.getFront(
"inspector"
);
const node = await domWalkerFront.getNodeFromActor(this.actorID, [
"rawAccessible",
"DOMNode",
]);
// We are using DOM inspector/walker API here because we want to keep both
// the accessiblity tree and the DOM tree in sync. This is necessary for
// several features that the accessibility panel provides such as inspecting
// a corresponding DOM node or any other functionality that requires DOM
// node ancestries to be resolved all the way up to the top level document.
const {
nodes: [documentNodeFront],
} = await domWalkerFront.children(node);
const accessibilityFront = await documentNodeFront.targetFront.getFront(
"accessibility"
);
await accessibilityFront.bootstrap();
return accessibilityFront.accessibleWalkerFront.children();
}
}
class AccessibleWalkerFront extends FrontClassWithSpec(accessibleWalkerSpec) {

Просмотреть файл

@ -48,6 +48,12 @@ loader.lazyRequireGetter(
"devtools/server/actors/highlighters/utils/accessibility",
true
);
loader.lazyRequireGetter(
this,
"isRemoteFrame",
"devtools/shared/layout/utils",
true
);
const RELATIONS_TO_IGNORE = new Set([
Ci.nsIAccessibleRelation.RELATION_CONTAINING_APPLICATION,
@ -240,6 +246,12 @@ const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
if (this.isDefunct) {
return 0;
}
// In case of a remote frame declare at least one child (the #document
// element) so that they can be expanded.
if (this.remoteFrame) {
return 1;
}
return this.rawAccessible.childCount;
},
@ -413,11 +425,23 @@ const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
return relationObjects;
},
get remoteFrame() {
if (this.isDefunct) {
return false;
}
return (
this.rawAccessible.role === Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME &&
isRemoteFrame(this.rawAccessible.DOMNode)
);
},
form() {
return {
actor: this.actorID,
role: this.role,
name: this.name,
remoteFrame: this.remoteFrame,
childCount: this.childCount,
checks: this._lastAudit,
};

Просмотреть файл

@ -484,9 +484,17 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
*/
getAccessibleFor(domNode) {
// We need to make sure that the document is loaded processed by a11y first.
return this.getDocument().then(() =>
this.addRef(this.getRawAccessibleFor(domNode.rawNode))
);
return this.getDocument().then(() => {
const rawAccessible = this.getRawAccessibleFor(domNode.rawNode);
// Not all DOM nodes have corresponding accessible objects. It's usually
// the case where there is no semantics or relevance to the accessibility
// client.
if (!rawAccessible) {
return null;
}
return this.addRef(rawAccessible);
});
},
/**

Просмотреть файл

@ -169,7 +169,7 @@ const accessibleWalkerSpec = generateActorSpec({
getAccessibleFor: {
request: { node: Arg(0, "domnode") },
response: {
accessible: RetVal("accessible"),
accessible: RetVal("nullable:accessible"),
},
},
getAncestry: {