XFA - Support aria heading and table structure. (bug 1723421) (bug 1723425)
https://bugzilla.mozilla.org/show_bug.cgi?id=1723421 https://bugzilla.mozilla.org/show_bug.cgi?id=1723425
This commit is contained in:
parent
849bab973c
commit
a38d1122d8
@ -121,6 +121,8 @@ const MAX_EMPTY_PAGES = 3;
|
|||||||
// Default value to start with for the tabIndex property.
|
// Default value to start with for the tabIndex property.
|
||||||
const DEFAULT_TAB_INDEX = 5000;
|
const DEFAULT_TAB_INDEX = 5000;
|
||||||
|
|
||||||
|
const HEADING_PATTERN = /^H(\d+)$/;
|
||||||
|
|
||||||
function getBorderDims(node) {
|
function getBorderDims(node) {
|
||||||
if (!node || !node.border) {
|
if (!node || !node.border) {
|
||||||
return { w: 0, h: 0 };
|
return { w: 0, h: 0 };
|
||||||
@ -210,6 +212,40 @@ function setTabIndex(node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyAssist(obj, attributes) {
|
||||||
|
const assist = obj.assist;
|
||||||
|
if (assist) {
|
||||||
|
const assistTitle = assist[$toHTML]();
|
||||||
|
if (assistTitle) {
|
||||||
|
attributes.title = assistTitle;
|
||||||
|
}
|
||||||
|
const role = assist.role;
|
||||||
|
const match = role.match(HEADING_PATTERN);
|
||||||
|
if (match) {
|
||||||
|
const ariaRole = "heading";
|
||||||
|
const ariaLevel = match[1];
|
||||||
|
attributes.role = ariaRole;
|
||||||
|
attributes["aria-level"] = ariaLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// XXX: We could end up in a situation where the obj has a heading role and
|
||||||
|
// is also a table. For now prioritize the table role.
|
||||||
|
if (obj.layout === "table") {
|
||||||
|
attributes.role = "table";
|
||||||
|
} else if (obj.layout === "row") {
|
||||||
|
attributes.role = "row";
|
||||||
|
} else {
|
||||||
|
const parent = obj[$getParent]();
|
||||||
|
if (parent.layout === "row") {
|
||||||
|
if (parent.assist && parent.assist.role === "TH") {
|
||||||
|
attributes.role = "columnheader";
|
||||||
|
} else {
|
||||||
|
attributes.role = "cell";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function ariaLabel(obj) {
|
function ariaLabel(obj) {
|
||||||
if (!obj.assist) {
|
if (!obj.assist) {
|
||||||
return null;
|
return null;
|
||||||
@ -1849,10 +1885,7 @@ class Draw extends XFAObject {
|
|||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const assist = this.assist ? this.assist[$toHTML]() : null;
|
applyAssist(this, attributes);
|
||||||
if (assist) {
|
|
||||||
html.attributes.title = assist;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bbox = computeBbox(this, html, availableSpace);
|
const bbox = computeBbox(this, html, availableSpace);
|
||||||
|
|
||||||
@ -2475,10 +2508,7 @@ class ExclGroup extends XFAObject {
|
|||||||
children,
|
children,
|
||||||
};
|
};
|
||||||
|
|
||||||
const assist = this.assist ? this.assist[$toHTML]() : null;
|
applyAssist(this, attributes);
|
||||||
if (assist) {
|
|
||||||
html.attributes.title = assist;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this[$extra];
|
delete this[$extra];
|
||||||
|
|
||||||
@ -2816,10 +2846,7 @@ class Field extends XFAObject {
|
|||||||
children,
|
children,
|
||||||
};
|
};
|
||||||
|
|
||||||
const assist = this.assist ? this.assist[$toHTML]() : null;
|
applyAssist(this, attributes);
|
||||||
if (assist) {
|
|
||||||
html.attributes.title = assist;
|
|
||||||
}
|
|
||||||
|
|
||||||
const borderStyle = this.border ? this.border[$toStyle]() : null;
|
const borderStyle = this.border ? this.border[$toStyle]() : null;
|
||||||
const bbox = computeBbox(this, html, availableSpace);
|
const bbox = computeBbox(this, html, availableSpace);
|
||||||
@ -5105,10 +5132,7 @@ class Subform extends XFAObject {
|
|||||||
children,
|
children,
|
||||||
};
|
};
|
||||||
|
|
||||||
const assist = this.assist ? this.assist[$toHTML]() : null;
|
applyAssist(this, attributes);
|
||||||
if (assist) {
|
|
||||||
html.attributes.title = assist;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = HTMLResult.success(createWrapper(this, html), bbox);
|
const result = HTMLResult.success(createWrapper(this, html), bbox);
|
||||||
|
|
||||||
|
@ -17,15 +17,18 @@ import { isNodeJS } from "../../src/shared/is_node.js";
|
|||||||
import { XFAFactory } from "../../src/core/xfa/factory.js";
|
import { XFAFactory } from "../../src/core/xfa/factory.js";
|
||||||
|
|
||||||
describe("XFAFactory", function () {
|
describe("XFAFactory", function () {
|
||||||
function searchHtmlNode(root, name, value) {
|
function searchHtmlNode(root, name, value, byAttributes = false) {
|
||||||
if (root[name] === value) {
|
if (
|
||||||
|
(!byAttributes && root[name] === value) ||
|
||||||
|
(byAttributes && root.attributes && root.attributes[name] === value)
|
||||||
|
) {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
if (!root.children) {
|
if (!root.children) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (const child of root.children) {
|
for (const child of root.children) {
|
||||||
const node = searchHtmlNode(child, name, value);
|
const node = searchHtmlNode(child, name, value, byAttributes);
|
||||||
if (node) {
|
if (node) {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -177,6 +180,127 @@ describe("XFAFactory", function () {
|
|||||||
expect(field.attributes.alt).toEqual("alt text");
|
expect(field.attributes.alt).toEqual("alt text");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should have a aria heading role and level", function () {
|
||||||
|
const xml = `
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
|
||||||
|
<template xmlns="http://www.xfa.org/schema/xfa-template/3.3">
|
||||||
|
<subform name="root" mergeMode="matchTemplate">
|
||||||
|
<pageSet>
|
||||||
|
<pageArea>
|
||||||
|
<contentArea x="0pt" w="456pt" h="789pt"/>
|
||||||
|
<medium stock="default" short="456pt" long="789pt"/>
|
||||||
|
<draw name="BA-Logo" y="5.928mm" x="128.388mm" w="71.237mm" h="9.528mm">
|
||||||
|
<value><text>foo</text></value>
|
||||||
|
<assist role="H2"></assist>
|
||||||
|
</draw>
|
||||||
|
</pageArea>
|
||||||
|
</pageSet>
|
||||||
|
</subform>
|
||||||
|
</template>
|
||||||
|
<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
|
||||||
|
<xfa:data>
|
||||||
|
</xfa:data>
|
||||||
|
</xfa:datasets>
|
||||||
|
</xdp:xdp>
|
||||||
|
`;
|
||||||
|
const factory = new XFAFactory({ "xdp:xdp": xml });
|
||||||
|
|
||||||
|
expect(factory.numberPages).toEqual(1);
|
||||||
|
|
||||||
|
const pages = factory.getPages();
|
||||||
|
const page1 = pages.children[0];
|
||||||
|
const wrapper = page1.children[0];
|
||||||
|
const draw = wrapper.children[0];
|
||||||
|
|
||||||
|
expect(draw.attributes.role).toEqual("heading");
|
||||||
|
expect(draw.attributes["aria-level"]).toEqual("2");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have aria table role", function () {
|
||||||
|
const xml = `
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
|
||||||
|
<template xmlns="http://www.xfa.org/schema/xfa-template/3.3">
|
||||||
|
<subform name="root" mergeMode="matchTemplate">
|
||||||
|
<pageSet>
|
||||||
|
<pageArea>
|
||||||
|
<contentArea x="0pt" w="456pt" h="789pt"/>
|
||||||
|
<medium stock="default" short="456pt" long="789pt"/>
|
||||||
|
<font size="7pt" typeface="FooBar" baselineShift="2pt">
|
||||||
|
</font>
|
||||||
|
</pageArea>
|
||||||
|
</pageSet>
|
||||||
|
<subform name="table" mergeMode="matchTemplate" layout="table">
|
||||||
|
<subform layout="row" name="row1">
|
||||||
|
<assist role="TH"></assist>
|
||||||
|
<draw name="header1" y="5.928mm" x="128.388mm" w="71.237mm" h="9.528mm">
|
||||||
|
<value><text>Header Col 1</text></value>
|
||||||
|
</draw>
|
||||||
|
<draw name="header2" y="5.928mm" x="128.388mm" w="71.237mm" h="9.528mm">
|
||||||
|
<value><text>Header Col 2</text></value>
|
||||||
|
</draw>
|
||||||
|
</subform>
|
||||||
|
<subform layout="row" name="row2">
|
||||||
|
<draw name="cell1" y="5.928mm" x="128.388mm" w="71.237mm" h="9.528mm">
|
||||||
|
<value><text>Cell 1</text></value>
|
||||||
|
</draw>
|
||||||
|
<draw name="cell2" y="5.928mm" x="128.388mm" w="71.237mm" h="9.528mm">
|
||||||
|
<value><text>Cell 2</text></value>
|
||||||
|
</draw>
|
||||||
|
</subform>
|
||||||
|
</subform>
|
||||||
|
</subform>
|
||||||
|
</template>
|
||||||
|
<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
|
||||||
|
<xfa:data>
|
||||||
|
</xfa:data>
|
||||||
|
</xfa:datasets>
|
||||||
|
</xdp:xdp>
|
||||||
|
`;
|
||||||
|
const factory = new XFAFactory({ "xdp:xdp": xml });
|
||||||
|
factory.setFonts([]);
|
||||||
|
|
||||||
|
expect(factory.numberPages).toEqual(1);
|
||||||
|
|
||||||
|
const pages = factory.getPages();
|
||||||
|
const table = searchHtmlNode(
|
||||||
|
pages,
|
||||||
|
"xfaName",
|
||||||
|
"table",
|
||||||
|
/* byAttributes */ true
|
||||||
|
);
|
||||||
|
expect(table.attributes.role).toEqual("table");
|
||||||
|
const headerRow = searchHtmlNode(
|
||||||
|
pages,
|
||||||
|
"xfaName",
|
||||||
|
"row1",
|
||||||
|
/* byAttributes */ true
|
||||||
|
);
|
||||||
|
expect(headerRow.attributes.role).toEqual("row");
|
||||||
|
const headerCell = searchHtmlNode(
|
||||||
|
pages,
|
||||||
|
"xfaName",
|
||||||
|
"header2",
|
||||||
|
/* byAttributes */ true
|
||||||
|
);
|
||||||
|
expect(headerCell.attributes.role).toEqual("columnheader");
|
||||||
|
const row = searchHtmlNode(
|
||||||
|
pages,
|
||||||
|
"xfaName",
|
||||||
|
"row2",
|
||||||
|
/* byAttributes */ true
|
||||||
|
);
|
||||||
|
expect(row.attributes.role).toEqual("row");
|
||||||
|
const cell = searchHtmlNode(
|
||||||
|
pages,
|
||||||
|
"xfaName",
|
||||||
|
"cell2",
|
||||||
|
/* byAttributes */ true
|
||||||
|
);
|
||||||
|
expect(cell.attributes.role).toEqual("cell");
|
||||||
|
});
|
||||||
|
|
||||||
it("should have a maxLength property", function () {
|
it("should have a maxLength property", function () {
|
||||||
const xml = `
|
const xml = `
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user