зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1543575 - provide an option for running accessibility browser chrome e10s tests in fission process. r=Jamie
Differential Revision: https://phabricator.services.mozilla.com/D48549 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
85544e7033
Коммит
08b6f8ee1e
|
@ -5,6 +5,7 @@ support-files =
|
|||
*.jsm
|
||||
head.js
|
||||
shared-head.js
|
||||
fission_document_builder.sjs
|
||||
|
||||
[browser_shutdown_acc_reference.js]
|
||||
skip-if = (os == 'linux' && debug && bits == 64) #Bug 1421307
|
||||
|
|
|
@ -19,7 +19,7 @@ async function runTests(browser, accDoc) {
|
|||
|
||||
onFocus = waitForEvent(EVENT_FOCUS, "buttonInputDoc");
|
||||
let url = snippetToURL(`<input id="input" type="button" value="button">`, {
|
||||
id: "buttonInputDoc",
|
||||
contentDocBodyAttrs: { id: "buttonInputDoc" },
|
||||
});
|
||||
browser.loadURI(url, {
|
||||
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
|
|
|
@ -63,7 +63,9 @@ addAccessibleTask(
|
|||
`
|
||||
<button id="button">button</button>
|
||||
<iframe id="editabledoc"
|
||||
src="${snippetToURL("", { id: "body2", contentEditable: "true" })}">
|
||||
src="${snippetToURL("", {
|
||||
contentDocBodyAttrs: { id: "body2", contentEditable: "true" },
|
||||
})}">
|
||||
</iframe>
|
||||
<div id="alertdialog" style="display: none" tabindex="-1" role="alertdialog" aria-labelledby="title2" aria-describedby="desc2">
|
||||
<div id="title2">Blah blah</div>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// eslint-disable-next-line mozilla/use-chromeutils-import
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
// eslint-disable-next-line mozilla/reject-importGlobalProperties
|
||||
Cu.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
function loadHTMLFromFile(path) {
|
||||
// Load the HTML to return in the response from file.
|
||||
// Since it's relative to the cwd of the test runner, we start there and
|
||||
// append to get to the actual path of the file.
|
||||
const testHTMLFile =
|
||||
// eslint-disable-next-line mozilla/use-services
|
||||
Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("CurWorkD", Ci.nsIFile);
|
||||
const dirs = path.split("/");
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
testHTMLFile.append(dirs[i]);
|
||||
}
|
||||
|
||||
const testHTMLFileStream = Cc[
|
||||
"@mozilla.org/network/file-input-stream;1"
|
||||
].createInstance(Ci.nsIFileInputStream);
|
||||
testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
|
||||
const testHTML = NetUtil.readInputStreamToString(
|
||||
testHTMLFileStream,
|
||||
testHTMLFileStream.available()
|
||||
);
|
||||
|
||||
return testHTML;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function handleRequest(request, response) {
|
||||
const queryString = new URLSearchParams(request.queryString);
|
||||
const html = queryString.get("html");
|
||||
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
if (html) {
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.write(html);
|
||||
return;
|
||||
}
|
||||
|
||||
const path = queryString.get("file");
|
||||
const doc = loadHTMLFromFile(path);
|
||||
response.setHeader(
|
||||
"Content-Type",
|
||||
path.endsWith(".xhtml") ? "application/xhtml+xml" : "text/html",
|
||||
false
|
||||
);
|
||||
// This is a hack to set the correct id for the content document that is to be
|
||||
// loaded in the fission frame.
|
||||
response.write(doc.replace(`id="body"`, `id="fission-body"`));
|
||||
}
|
|
@ -11,13 +11,16 @@
|
|||
invokeSetStyle, getAccessibleDOMNodeID, getAccessibleTagName,
|
||||
addAccessibleTask, findAccessibleChildByID, isDefunct,
|
||||
CURRENT_CONTENT_DIR, loadScripts, loadContentScripts, snippetToURL,
|
||||
Cc, Cu, arrayFromChildren, forceGC, contentSpawnMutation */
|
||||
Cc, Cu, arrayFromChildren, forceGC, contentSpawnMutation,
|
||||
FISSION_IFRAME_ID, DEFAULT_FISSION_DOC_BODY_ID, invokeContentTask,
|
||||
matchContentDoc, currentContentDoc */
|
||||
|
||||
const CURRENT_FILE_DIR = "/browser/accessible/tests/browser/";
|
||||
|
||||
/**
|
||||
* Current browser test directory path used to load subscripts.
|
||||
*/
|
||||
const CURRENT_DIR =
|
||||
"chrome://mochitests/content/browser/accessible/tests/browser/";
|
||||
const CURRENT_DIR = `chrome://mochitests/content${CURRENT_FILE_DIR}`;
|
||||
/**
|
||||
* A11y mochitest directory where we find common files used in both browser and
|
||||
* plain tests.
|
||||
|
@ -27,11 +30,35 @@ const MOCHITESTS_DIR =
|
|||
/**
|
||||
* A base URL for test files used in content.
|
||||
*/
|
||||
const CURRENT_CONTENT_DIR =
|
||||
"http://example.com/browser/accessible/tests/browser/";
|
||||
const CURRENT_CONTENT_DIR = `http://example.com${CURRENT_FILE_DIR}`;
|
||||
|
||||
const LOADED_CONTENT_SCRIPTS = new Map();
|
||||
|
||||
const DEFAULT_CONTENT_DOC_BODY_ID = "body";
|
||||
const FISSION_IFRAME_ID = "fission-iframe";
|
||||
const DEFAULT_FISSION_DOC_BODY_ID = "fission-body";
|
||||
|
||||
let gIsFission = false;
|
||||
|
||||
function currentContentDoc() {
|
||||
return gIsFission ? DEFAULT_FISSION_DOC_BODY_ID : DEFAULT_CONTENT_DOC_BODY_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessible event match criteria based on the id of the current document
|
||||
* accessible in test.
|
||||
*
|
||||
* @param {nsIAccessibleEvent} event
|
||||
* Accessible event to be tested for a match.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* True if accessible event's accessible object ID matches current
|
||||
* document accessible ID.
|
||||
*/
|
||||
function matchContentDoc(event) {
|
||||
return getAccessibleDOMNodeID(event.accessible) === currentContentDoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to dump debug information.
|
||||
*/
|
||||
|
@ -98,7 +125,8 @@ function invokeSetAttribute(browser, id, attr, value) {
|
|||
} else {
|
||||
Logger.log(`Removing ${attr} attribute from node with id: ${id}`);
|
||||
}
|
||||
return SpecialPowers.spawn(
|
||||
|
||||
return invokeContentTask(
|
||||
browser,
|
||||
[id, attr, value],
|
||||
(contentId, contentAttr, contentValue) => {
|
||||
|
@ -114,7 +142,8 @@ function invokeSetAttribute(browser, id, attr, value) {
|
|||
|
||||
/**
|
||||
* Asynchronously set or remove content element's style (in content process if
|
||||
* e10s is enabled).
|
||||
* e10s is enabled, or in fission process if fission is enabled and a fission
|
||||
* frame is present).
|
||||
* @param {Object} browser current "tabbrowser" element
|
||||
* @param {String} id content element id
|
||||
* @param {String} aStyle style property name
|
||||
|
@ -128,11 +157,12 @@ function invokeSetStyle(browser, id, style, value) {
|
|||
} else {
|
||||
Logger.log(`Removing ${style} style from node with id: ${id}`);
|
||||
}
|
||||
return SpecialPowers.spawn(
|
||||
|
||||
return invokeContentTask(
|
||||
browser,
|
||||
[id, style, value],
|
||||
(contentId, contentStyle, contentValue) => {
|
||||
let elm = content.document.getElementById(contentId);
|
||||
const elm = content.document.getElementById(contentId);
|
||||
if (contentValue) {
|
||||
elm.style[contentStyle] = contentValue;
|
||||
} else {
|
||||
|
@ -144,22 +174,54 @@ function invokeSetStyle(browser, id, style, value) {
|
|||
|
||||
/**
|
||||
* Asynchronously set focus on a content element (in content process if e10s is
|
||||
* enabled).
|
||||
* enabled, or in fission process if fission is enabled and a fission frame is
|
||||
* present).
|
||||
* @param {Object} browser current "tabbrowser" element
|
||||
* @param {String} id content element id
|
||||
* @return {Promise} promise indicating that focus is set
|
||||
*/
|
||||
function invokeFocus(browser, id) {
|
||||
Logger.log(`Setting focus on a node with id: ${id}`);
|
||||
return SpecialPowers.spawn(browser, [id], contentId => {
|
||||
let elm = content.document.getElementById(contentId);
|
||||
|
||||
return invokeContentTask(browser, [id], contentId => {
|
||||
const elm = content.document.getElementById(contentId);
|
||||
if (elm.editor) {
|
||||
elm.selectionStart = elm.selectionEnd = elm.value.length;
|
||||
}
|
||||
|
||||
elm.focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously perform a task in content (in content process if e10s is
|
||||
* enabled, or in fission process if fission is enabled and a fission frame is
|
||||
* present).
|
||||
* @param {Object} browser current "tabbrowser" element
|
||||
* @param {Array} args arguments for the content task
|
||||
* @param {Function} task content task function
|
||||
*
|
||||
* @return {Promise} promise indicating that content task is complete
|
||||
*/
|
||||
function invokeContentTask(browser, args, task) {
|
||||
return SpecialPowers.spawn(
|
||||
browser,
|
||||
[FISSION_IFRAME_ID, task.toString(), ...args],
|
||||
(fissionFrameId, contentTask, ...contentArgs) => {
|
||||
// eslint-disable-next-line no-eval
|
||||
const runnableTask = eval(`
|
||||
(() => {
|
||||
return (${contentTask});
|
||||
})();`);
|
||||
const frame = content.document.getElementById(fissionFrameId);
|
||||
|
||||
return frame
|
||||
? SpecialPowers.spawn(frame, contentArgs, runnableTask)
|
||||
: runnableTask.call(this, ...contentArgs);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of scripts into the test
|
||||
* @param {Array} scripts a list of scripts to load
|
||||
|
@ -207,49 +269,87 @@ async function loadContentScripts(target, ...scripts) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an HTML snippet and returns an encoded URI for a full document
|
||||
* with the snippet.
|
||||
* @param {String} snippet a markup snippet.
|
||||
* @param {Object} bodyAttrs extra attributes to use in the body tag. Default is
|
||||
* { id: "body "}.
|
||||
* @return {String} a base64 encoded data url of the document container the
|
||||
* snippet.
|
||||
**/
|
||||
function snippetToURL(snippet, bodyAttrs = {}) {
|
||||
let attrs = Object.assign({}, { id: "body" }, bodyAttrs);
|
||||
let attrsString = Object.entries(attrs)
|
||||
function attrsToString(attrs) {
|
||||
return Object.entries(attrs)
|
||||
.map(([attr, value]) => `${attr}=${JSON.stringify(value)}`)
|
||||
.join(" ");
|
||||
let encodedDoc = encodeURIComponent(
|
||||
}
|
||||
|
||||
function wrapWithFissionIFrame(doc, options = {}) {
|
||||
const srcURL = new URL(`${CURRENT_CONTENT_DIR}fission_document_builder.sjs`);
|
||||
if (doc.endsWith("html")) {
|
||||
srcURL.searchParams.append("file", `${CURRENT_FILE_DIR}e10s/${doc}`);
|
||||
} else {
|
||||
const { fissionDocBodyAttrs = {} } = options;
|
||||
const attrs = {
|
||||
id: DEFAULT_FISSION_DOC_BODY_ID,
|
||||
...fissionDocBodyAttrs,
|
||||
};
|
||||
|
||||
srcURL.searchParams.append(
|
||||
"html",
|
||||
`<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Accessibility Fission Test</title>
|
||||
</head>
|
||||
<body ${attrsToString(attrs)}>${doc}</body>
|
||||
</html>`
|
||||
);
|
||||
}
|
||||
|
||||
return `<iframe id="${FISSION_IFRAME_ID}" src="${srcURL.href}"/>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an HTML snippet or HTML doc url and returns an encoded URI for a full
|
||||
* document with the snippet or the URL as a source for the IFRAME.
|
||||
* @param {String} doc
|
||||
* a markup snippet or url.
|
||||
* @param {Object} options
|
||||
* additonal options:
|
||||
* - {Boolean} fission
|
||||
* true if the content document should be wrapped inside a fission
|
||||
* iframe
|
||||
* - {Object} contentDocBodyAttrs
|
||||
* a set of attributes to be applied to a top level content document
|
||||
* body
|
||||
* - {Object} fissionDocBodyAttrs
|
||||
* a set of attributes to be applied to a fission content document body
|
||||
* @return {String}
|
||||
* a base64 encoded data url of the document container the snippet.
|
||||
**/
|
||||
function snippetToURL(doc, options = {}) {
|
||||
const { contentDocBodyAttrs = {} } = options;
|
||||
const attrs = {
|
||||
id: DEFAULT_CONTENT_DOC_BODY_ID,
|
||||
...contentDocBodyAttrs,
|
||||
};
|
||||
|
||||
if (options.fission) {
|
||||
doc = wrapWithFissionIFrame(doc, options);
|
||||
}
|
||||
|
||||
const encodedDoc = encodeURIComponent(
|
||||
`<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Accessibility Test</title>
|
||||
</head>
|
||||
<body ${attrsString}>${snippet}</body>
|
||||
<body ${attrsToString(attrs)}>${doc}</body>
|
||||
</html>`
|
||||
);
|
||||
|
||||
return `data:text/html;charset=utf-8,${encodedDoc}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around browser test add_task that triggers an accessible test task
|
||||
* as a new browser test task with given document, data URL or markup snippet.
|
||||
* @param {String} doc URL (relative to current directory) or
|
||||
* data URL or markup snippet that is used
|
||||
* to test content with
|
||||
* @param {Function|AsyncFunction} task a generator or a function with tests to
|
||||
* run
|
||||
*/
|
||||
function addAccessibleTask(doc, task) {
|
||||
add_task(async function() {
|
||||
function accessibleTask(doc, task, options = {}) {
|
||||
return async function() {
|
||||
let url;
|
||||
if (doc.includes("doc_")) {
|
||||
if (doc.endsWith("html") && !options.fission) {
|
||||
url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
|
||||
} else {
|
||||
url = snippetToURL(doc);
|
||||
url = snippetToURL(doc, options);
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
|
@ -260,7 +360,21 @@ function addAccessibleTask(doc, task) {
|
|||
}
|
||||
});
|
||||
|
||||
let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, "body");
|
||||
const onContentDocLoad = waitForEvent(
|
||||
EVENT_DOCUMENT_LOAD_COMPLETE,
|
||||
DEFAULT_CONTENT_DOC_BODY_ID
|
||||
);
|
||||
|
||||
let onFissionDocLoad;
|
||||
if (options.fission) {
|
||||
gIsFission = true;
|
||||
if (gFissionBrowser) {
|
||||
onFissionDocLoad = waitForEvent(
|
||||
EVENT_DOCUMENT_LOAD_COMPLETE,
|
||||
DEFAULT_FISSION_DOC_BODY_ID
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
|
@ -285,11 +399,54 @@ function addAccessibleTask(doc, task) {
|
|||
);
|
||||
Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
|
||||
|
||||
let event = await onDocLoad;
|
||||
await task(browser, event.accessible);
|
||||
const { accessible: docAccessible } = await onContentDocLoad;
|
||||
let fissionDocAccessible;
|
||||
if (options.fission) {
|
||||
fissionDocAccessible = gFissionBrowser
|
||||
? (await onFissionDocLoad).accessible
|
||||
: findAccessibleChildByID(docAccessible, FISSION_IFRAME_ID)
|
||||
.firstChild;
|
||||
}
|
||||
|
||||
await task(
|
||||
browser,
|
||||
fissionDocAccessible || docAccessible,
|
||||
fissionDocAccessible && docAccessible
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around browser test add_task that triggers an accessible test task
|
||||
* as a new browser test task with given document, data URL or markup snippet.
|
||||
* @param {String} doc
|
||||
* URL (relative to current directory) or data URL or markup snippet
|
||||
* that is used to test content with
|
||||
* @param {Function|AsyncFunction} task
|
||||
* a generator or a function with tests to run
|
||||
* @param {null|Object} options
|
||||
* Options for running accessibility test tasks:
|
||||
* - {Boolean} topLevel
|
||||
* Flag to run the test with content in the top level content process.
|
||||
* Default is true.
|
||||
* - {Boolean} iframe
|
||||
* Flag to run the test with content wrapped in an iframe. Default is
|
||||
* false.
|
||||
*/
|
||||
function addAccessibleTask(
|
||||
doc,
|
||||
task,
|
||||
{ topLevel = true, iframe = false } = {}
|
||||
) {
|
||||
if (topLevel) {
|
||||
add_task(accessibleTask(doc, task));
|
||||
}
|
||||
|
||||
if (iframe) {
|
||||
add_task(accessibleTask(doc, task, { fission: true }));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -401,20 +558,20 @@ async function contentSpawnMutation(browser, waitFor, func, args = []) {
|
|||
|
||||
// This stops the refreh driver from doing its regular ticks, and leaves
|
||||
// us in control.
|
||||
await SpecialPowers.spawn(browser, [], tick);
|
||||
await invokeContentTask(browser, [], tick);
|
||||
|
||||
// Perform the tree mutation.
|
||||
await SpecialPowers.spawn(browser, args, func);
|
||||
await invokeContentTask(browser, args, func);
|
||||
|
||||
// Do one tick to flush our queue (insertions, relocations, etc.)
|
||||
await SpecialPowers.spawn(browser, [], tick);
|
||||
await invokeContentTask(browser, [], tick);
|
||||
|
||||
let events = await onReorders;
|
||||
|
||||
unexpectedListener.stop();
|
||||
|
||||
// Go back to normal refresh driver ticks.
|
||||
await SpecialPowers.spawn(browser, [], function() {
|
||||
await invokeContentTask(browser, [], function() {
|
||||
content.windowUtils.restoreNormalRefresh();
|
||||
});
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче