Bug 1564993 - Create simulator actor and front, r=yzen,nchevobbe

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Maliha Islam 2019-08-29 04:55:45 +00:00
Родитель 2b2064b0e4
Коммит deacf348f4
11 изменённых файлов: 480 добавлений и 0 удалений

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

@ -16,7 +16,19 @@ loader.lazyRequireGetter(
"devtools/server/actors/accessibility/walker",
true
);
loader.lazyRequireGetter(
this,
"SimulatorActor",
"devtools/server/actors/accessibility/simulator",
true
);
loader.lazyRequireGetter(this, "events", "devtools/shared/event-emitter");
loader.lazyRequireGetter(
this,
"isWebRenderEnabled",
"devtools/server/actors/utils/accessibility",
true
);
const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";
@ -262,6 +274,29 @@ const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, {
return this.walker;
},
/**
* Get or create Simulator actor, managed by AccessibilityActor,
* only if webrender is enabled. Simulator applies color filters on an entire viewport.
* This needs to be done using webrender and not an SVG <feColorMatrix> since it is
* accelerated and scrolling with filter applied needs to be smooth (Bug1431466).
*
* @return {Object|null}
* SimulatorActor for the current tab.
*/
getSimulator() {
// TODO: Remove this check after Bug1570667
if (!isWebRenderEnabled(this.targetActor.window)) {
return null;
}
if (!this.simulator) {
this.simulator = new SimulatorActor(this.conn, this.targetActor);
this.manage(this.simulator);
}
return this.simulator;
},
/**
* Destroy accessibility service actor. This method also shutsdown
* accessibility service if possible.

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

@ -0,0 +1,195 @@
/* 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";
const {
accessibility: {
SIMULATION_TYPE: {
DEUTERANOMALY,
PROTANOMALY,
TRITANOMALY,
DEUTERANOPIA,
PROTANOPIA,
TRITANOPIA,
CONTRAST_LOSS,
},
},
} = require("devtools/shared/constants");
/**
* Constants used in accessibility actors.
*/
// Color blindness matrix values taken from https://github.com/eeejay/NoCoffee/blob/master/popup.js#L5-L10
// Contrast loss matrix values are for 50% contrast (see https://docs.rainmeter.net/tips/colormatrix-guide/,
// and https://stackoverflow.com/questions/23865511/contrast-with-color-matrix). The matrices are flattened
// 4x5 matrices, needed for docShell setColorMatrix method. i.e. A 4x5 matrix of the form:
// 1 2 3 4 5
// 6 7 8 9 10
// 11 12 13 14 15
// 16 17 18 19 20
// will be need to be set in docShell as:
// [1, 6, 11, 16, 2, 7, 12, 17, 3, 8, 13, 18, 4, 9, 14, 19, 5, 10, 15, 20]
const COLOR_TRANSFORMATION_MATRICES = {
NONE: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[PROTANOMALY]: [
0.817,
0.333,
0,
0,
0.183,
0.667,
0.125,
0,
0,
0,
0.875,
0,
0,
0,
0,
1,
0,
0,
0,
0,
],
[DEUTERANOMALY]: [
0.8,
0.258,
0,
0,
0.2,
0.742,
0.142,
0,
0,
0,
0.858,
0,
0,
0,
0,
1,
0,
0,
0,
0,
],
[TRITANOMALY]: [
0.967,
0,
0,
0,
0.033,
0.733,
0.183,
0,
0,
0.267,
0.817,
0,
0,
0,
0,
1,
0,
0,
0,
0,
],
[PROTANOPIA]: [
0.567,
0.558,
0,
0,
0.433,
0.442,
0.242,
0,
0,
0,
0.758,
0,
0,
0,
0,
1,
0,
0,
0,
0,
],
[DEUTERANOPIA]: [
0.625,
0.7,
0,
0,
0.375,
0.3,
0.3,
0,
0,
0,
0.7,
0,
0,
0,
0,
1,
0,
0,
0,
0,
],
[TRITANOPIA]: [
0.95,
0,
0,
0,
0.05,
0.433,
0.475,
0,
0,
0.567,
0.525,
0,
0,
0,
0,
1,
0,
0,
0,
0,
],
[CONTRAST_LOSS]: [
0.5,
0,
0,
0,
0,
0.5,
0,
0,
0,
0,
0.5,
0,
0,
0,
0,
0.5,
0.25,
0.25,
0.25,
0,
],
};
exports.simulation = {
COLOR_TRANSFORMATION_MATRICES,
};

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

@ -10,6 +10,8 @@ DevToolsModules(
'accessibility-parent.js',
'accessibility.js',
'accessible.js',
'constants.js',
'simulator.js',
'walker.js',
'worker.js',
)

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

@ -0,0 +1,78 @@
/* 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";
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { simulatorSpec } = require("devtools/shared/specs/accessibility");
const {
simulation: { COLOR_TRANSFORMATION_MATRICES },
} = require("./constants");
/**
* The SimulatorActor is responsible for setting color matrices
* based on the simulation type specified.
*/
const SimulatorActor = ActorClassWithSpec(simulatorSpec, {
initialize(conn, targetActor) {
Actor.prototype.initialize.call(this, conn);
this.targetActor = targetActor;
},
/**
* Simulates a type of visual impairment (i.e. color blindness or contrast loss).
*
* @param {Object} options
* Properties: {Array} types
* Contains the types of visual impairment(s) to be simulated.
* Set default color matrix if array is empty.
* @return {Boolean}
* True if matrix was successfully applied, false otherwise.
*/
simulate(options) {
if (options.types.length > 1) {
return false;
}
return this.setColorMatrix(
COLOR_TRANSFORMATION_MATRICES[
options.types.length === 1 ? options.types[0] : "NONE"
]
);
},
setColorMatrix(colorMatrix) {
if (!this.docShell) {
return false;
}
try {
this.docShell.setColorMatrix(colorMatrix);
} catch (error) {
return false;
}
return true;
},
/**
* Disables all simulations by setting the default color matrix.
*/
disable() {
this.simulate({ types: [] });
},
destroy() {
Actor.prototype.destroy.call(this);
this.disable();
this.targetActor = null;
},
get docShell() {
return this.targetActor && this.targetActor.docShell;
},
});
exports.SimulatorActor = SimulatorActor;

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

@ -83,6 +83,19 @@ function removeSheetForBackgroundCalculation(win) {
removeSheet(win, HIGHLIGHTER_STYLES_SHEET);
}
/**
* Helper function that determines if web render is enabled.
*
* @param {Window} win
* Window to be tested.
* @return {Boolean}
* True if web render is enabled, false otherwise.
*/
function isWebRenderEnabled(win) {
return win.windowUtils && win.windowUtils.layerManagerType === "WebRender";
}
exports.isDefunct = isDefunct;
exports.loadSheetForBackgroundCalculation = loadSheetForBackgroundCalculation;
exports.removeSheetForBackgroundCalculation = removeSheetForBackgroundCalculation;
exports.isWebRenderEnabled = isWebRenderEnabled;

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

@ -64,6 +64,7 @@ skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
[browser_accessibility_simple.js]
skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
[browser_accessibility_simulator.js]
[browser_accessibility_text_label_audit_frame.js]
skip-if =
(os == 'win' && processor == 'aarch64') || # bug 1533184

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

@ -4,6 +4,13 @@
"use strict";
loader.lazyRequireGetter(
this,
"isWebRenderEnabled",
"devtools/server/actors/utils/accessibility",
true
);
const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";
function checkAccessibilityState(accessibility, expected) {
@ -26,10 +33,19 @@ add_task(async function() {
ok(accessibility, "The AccessibilityFront was created");
ok(accessibility.getWalker, "The getWalker method exists");
ok(accessibility.getSimulator, "The getSimulator method exists");
let a11yWalker = await accessibility.getWalker();
ok(a11yWalker, "The AccessibleWalkerFront was returned");
const a11ySimulator = await accessibility.getSimulator();
const webRenderEnabled = isWebRenderEnabled(window);
is(
!!a11ySimulator,
webRenderEnabled,
`The SimulatorFront was${webRenderEnabled ? "" : " not"} returned.`
);
checkAccessibilityState(accessibility, {
enabled: false,
canBeDisabled: true,

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

@ -0,0 +1,96 @@
/* 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";
loader.lazyRequireGetter(
this,
"isWebRenderEnabled",
"devtools/server/actors/utils/accessibility",
true
);
const {
accessibility: {
SIMULATION_TYPE: { PROTANOPIA },
},
} = require("devtools/shared/constants");
const {
simulation: {
COLOR_TRANSFORMATION_MATRICES: {
PROTANOPIA: PROTANOPIA_MATRIX,
NONE: DEFAULT_MATRIX,
},
},
} = require("devtools/server/actors/accessibility/constants");
// Checks for the SimulatorActor
async function setup() {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.window.testColorMatrix = function(actual, expected) {
for (const idx in actual) {
is(
actual[idx].toFixed(3),
expected[idx].toFixed(3),
"Color matrix value is set correctly."
);
}
};
});
SimpleTest.registerCleanupFunction(async function() {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.window.testColorMatrix = null;
});
});
}
async function testSimulate(simulator, matrix, type = null) {
const matrixApplied = await simulator.simulate({ types: type ? [type] : [] });
ok(matrixApplied, "Simulation color matrix is successfully applied.");
await ContentTask.spawn(
gBrowser.selectedBrowser,
[type, matrix],
([simulationType, simulationMatrix]) => {
const { window } = content;
info(
`Test that color matrix is set to ${simulationType ||
"default"} simulation values.`
);
window.testColorMatrix(
window.docShell.getColorMatrix(),
simulationMatrix
);
}
);
}
add_task(async function() {
const { target, accessibility } = await initAccessibilityFrontForUrl(
MAIN_DOMAIN + "doc_accessibility.html"
);
const simulator = await accessibility.getSimulator();
if (!simulator) {
ok(!isWebRenderEnabled(window), "Web render is disabled.");
// If web render is disabled, we can't test the simulator actor, so return early
return;
}
await setup();
info("Test that protanopia is successfully simulated.");
await testSimulate(simulator, PROTANOPIA_MATRIX, PROTANOPIA);
info(
"Test that simulations are successfully removed by setting default color matrix."
);
await testSimulate(simulator, DEFAULT_MATRIX);
await target.destroy();
gBrowser.removeCurrentTab();
});

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

@ -85,8 +85,27 @@ const SCORES = {
WARNING: "WARNING",
};
// List of simulation types.
const SIMULATION_TYPE = {
// Low red color blindness
PROTANOMALY: "PROTANOMALY",
// Low green color blindness
DEUTERANOMALY: "DEUTERANOMALY",
// Low blue color blindness
TRITANOMALY: "TRITANOMALY",
// No red color blindness
PROTANOPIA: "PROTANOPIA",
// No green color blindness
DEUTERANOPIA: "DEUTERANOPIA",
// No blue color blindness
TRITANOPIA: "TRITANOPIA",
// Low contrast
CONTRAST_LOSS: "CONTRAST_LOSS",
};
exports.accessibility = {
AUDIT_TYPE,
ISSUE_TYPE,
SCORES,
SIMULATION_TYPE,
};

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

@ -12,6 +12,7 @@ const {
accessibleSpec,
accessibleWalkerSpec,
accessibilitySpec,
simulatorSpec,
} = require("devtools/shared/specs/accessibility");
const events = require("devtools/shared/event-emitter");
@ -217,9 +218,13 @@ class AccessibilityFront extends FrontClassWithSpec(accessibilitySpec) {
}
}
const SimulatorFront = FrontClassWithSpec(simulatorSpec);
exports.AccessibleFront = AccessibleFront;
registerFront(AccessibleFront);
exports.AccessibleWalkerFront = AccessibleWalkerFront;
registerFront(AccessibleWalkerFront);
exports.AccessibilityFront = AccessibilityFront;
registerFront(AccessibilityFront);
exports.SimulatorFront = SimulatorFront;
registerFront(SimulatorFront);

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

@ -206,6 +206,19 @@ const accessibleWalkerSpec = generateActorSpec({
},
});
const simulatorSpec = generateActorSpec({
typeName: "simulator",
methods: {
simulate: {
request: { options: Arg(0, "nullable:json") },
response: {
value: RetVal("boolean"),
},
},
},
});
const accessibilitySpec = generateActorSpec({
typeName: "accessibility",
@ -239,6 +252,12 @@ const accessibilitySpec = generateActorSpec({
walker: RetVal("accessiblewalker"),
},
},
getSimulator: {
request: {},
response: {
simulator: RetVal("nullable:simulator"),
},
},
enable: {
request: {},
response: {},
@ -253,3 +272,4 @@ const accessibilitySpec = generateActorSpec({
exports.accessibleSpec = accessibleSpec;
exports.accessibleWalkerSpec = accessibleWalkerSpec;
exports.accessibilitySpec = accessibilitySpec;
exports.simulatorSpec = simulatorSpec;