Bug 1732280 - Implement mappings for sub, sup elements and subscript, superscript roles, r=Jamie

This commit adds mappings for HTML sub and sup elements, as well as ARIA
role mappings for subscript, superscript roles, with the goal of properly
exposing this information to the accessibility tree. This commit also updates
text attribute code to account for the attributes implied by those roles.
Finally, this commit updates tests to verify that the role and attribute
information is working properly.

Differential Revision: https://phabricator.services.mozilla.com/D155523
This commit is contained in:
Nathan LaPre 2022-08-31 16:48:56 +00:00
Родитель 088c86b28a
Коммит 982410361d
12 изменённых файлов: 324 добавлений и 74 удалений

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

@ -1152,6 +1152,15 @@ static const nsRoleMapEntry sWAIRoleMaps[] = {
kGenericAccType, kGenericAccType,
kNoReqStates kNoReqStates
}, },
{ // subscript
nsGkAtoms::subscript,
roles::SUBSCRIPT,
kUseMapRole,
eNoValue,
eNoAction,
eNoLiveAttr,
kGenericAccType
},
{ // suggestion { // suggestion
nsGkAtoms::suggestion, nsGkAtoms::suggestion,
roles::SUGGESTION, roles::SUGGESTION,
@ -1161,6 +1170,15 @@ static const nsRoleMapEntry sWAIRoleMaps[] = {
eNoLiveAttr, eNoLiveAttr,
kGenericAccType, kGenericAccType,
}, },
{ // superscript
nsGkAtoms::superscript,
roles::SUPERSCRIPT,
kUseMapRole,
eNoValue,
eNoAction,
eNoLiveAttr,
kGenericAccType
},
{ // switch { // switch
nsGkAtoms::svgSwitch, nsGkAtoms::svgSwitch,
roles::SWITCH, roles::SWITCH,

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

@ -333,6 +333,8 @@ MARKUPMAP(
}, },
0) 0)
MARKUPMAP(sub, New_HyperText, roles::SUBSCRIPT)
MARKUPMAP( MARKUPMAP(
summary, summary,
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
@ -340,6 +342,8 @@ MARKUPMAP(
}, },
roles::SUMMARY) roles::SUMMARY)
MARKUPMAP(sup, New_HyperText, roles::SUPERSCRIPT)
MARKUPMAP( MARKUPMAP(
table, table,
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {

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

@ -19,7 +19,7 @@ namespace roles {
enum Role { enum Role {
/** /**
* Used when accessible hans't strong defined role. * Used when the accessible has no strongly-defined role.
*/ */
NOTHING = 0, NOTHING = 0,
@ -1083,7 +1083,21 @@ enum Role {
*/ */
METER = 185, METER = 185,
LAST_ROLE = METER /**
* Represents phrasing content that is presented with vertical alignment
* lower than the baseline and a smaller font size. For example, the "2" in
* the chemical formula H2O.
*/
SUBSCRIPT = 186,
/**
* Represents phrasing content that is presented with vertical alignment
* higher than the baseline and a smaller font size. For example, the
* exponent in a math expression.
*/
SUPERSCRIPT = 187,
LAST_ROLE = SUPERSCRIPT
}; };
} // namespace roles } // namespace roles

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

@ -1886,4 +1886,24 @@ ROLE(METER,
ROLE_SYSTEM_PROGRESSBAR, ROLE_SYSTEM_PROGRESSBAR,
java::SessionAccessibility::CLASSNAME_VIEW, java::SessionAccessibility::CLASSNAME_VIEW,
eNameFromValueRule) eNameFromValueRule)
ROLE(SUBSCRIPT,
"subscript",
ATK_ROLE_SUBSCRIPT,
NSAccessibilityGroupRole,
@"AXSubscriptStyleGroup",
ROLE_SYSTEM_GROUPING,
IA2_ROLE_TEXT_FRAME,
java::SessionAccessibility::CLASSNAME_VIEW,
eNoNameRule)
ROLE(SUPERSCRIPT,
"superscript",
ATK_ROLE_SUPERSCRIPT,
NSAccessibilityGroupRole,
@"AXSuperscriptStyleGroup",
ROLE_SYSTEM_GROUPING,
IA2_ROLE_TEXT_FRAME,
java::SessionAccessibility::CLASSNAME_VIEW,
eNoNameRule)
// clang-format on // clang-format on

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

@ -120,7 +120,7 @@ void TextAttrsMgr::GetAttributes(AccAttributes* aAttributes,
TextDecorTextAttr textDecorTextAttr(rootFrame, frame); TextDecorTextAttr textDecorTextAttr(rootFrame, frame);
// "text-position" text attribute // "text-position" text attribute
TextPosTextAttr textPosTextAttr(rootFrame, frame); TextPosTextAttr textPosTextAttr(rootFrame, frame, hyperTextElm, offsetNode);
TextAttr* attrArray[] = { TextAttr* attrArray[] = {
&langTextAttr, &invalidTextAttr, &bgColorTextAttr, &langTextAttr, &invalidTextAttr, &bgColorTextAttr,
@ -664,34 +664,67 @@ void TextAttrsMgr::TextDecorTextAttr::ExposeValue(
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::TextPosTextAttr::TextPosTextAttr(nsIFrame* aRootFrame, TextAttrsMgr::TextPosTextAttr::TextPosTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame) nsIFrame* aFrame,
: TTextAttr<TextPosValue>(!aFrame) { nsIContent* aRootElm,
mRootNativeValue = GetTextPosValue(aRootFrame); nsIContent* aElm)
mIsRootDefined = mRootNativeValue != eTextPosNone; : TTextAttr<Maybe<TextPosValue>>(!aFrame && !aElm), mRootElm(aRootElm) {
// Get the text-position values for the roots and children.
// If we find an ARIA text-position value on a DOM element - searching up
// from the supplied root DOM element - use the associated frame as the root
// frame. This ensures that we're using the proper root frame for comparison.
nsIFrame* ariaFrame = nullptr;
Maybe<TextPosValue> rootAria = GetAriaTextPosValue(aRootElm, ariaFrame);
if (rootAria && ariaFrame) {
aRootFrame = ariaFrame;
}
Maybe<TextPosValue> rootLayout = GetLayoutTextPosValue(aRootFrame);
Maybe<TextPosValue> childLayout;
Maybe<TextPosValue> childAria;
if (aFrame) { if (aFrame) {
mNativeValue = GetTextPosValue(aFrame); childLayout = GetLayoutTextPosValue(aFrame);
mIsDefined = mNativeValue != eTextPosNone; }
if (aElm) {
childAria = GetAriaTextPosValue(aElm);
}
// Aria values take precedence over layout values.
mIsRootDefined = rootAria || rootLayout;
mRootNativeValue = rootAria ? rootAria : rootLayout;
mIsDefined = childAria || childLayout;
mNativeValue = childAria ? childAria : childLayout;
// If there's no child text-position information from ARIA, and the child
// layout info is equivalent to the root layout info (i.e., it's inherited),
// then we should prefer the root information.
if (!childAria && childLayout == rootLayout) {
mIsDefined = false;
} }
} }
bool TextAttrsMgr::TextPosTextAttr::GetValueFor(LocalAccessible* aAccessible, bool TextAttrsMgr::TextPosTextAttr::GetValueFor(LocalAccessible* aAccessible,
TextPosValue* aValue) { Maybe<TextPosValue>* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent()); nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (elm) { if (elm) {
nsIFrame* frame = elm->GetPrimaryFrame(); nsIFrame* frame = elm->GetPrimaryFrame();
if (frame) { if (frame) {
*aValue = GetTextPosValue(frame); Maybe<TextPosValue> layoutValue = GetLayoutTextPosValue(frame);
return *aValue != eTextPosNone; Maybe<TextPosValue> ariaValue = GetAriaTextPosValue(elm);
*aValue = ariaValue ? ariaValue : layoutValue;
return aValue->isSome();
} }
} }
return false; return false;
} }
void TextAttrsMgr::TextPosTextAttr::ExposeValue(AccAttributes* aAttributes, void TextAttrsMgr::TextPosTextAttr::ExposeValue(
const TextPosValue& aValue) { AccAttributes* aAttributes, const Maybe<TextPosValue>& aValue) {
if (aValue.isNothing()) {
return;
}
RefPtr<nsAtom> atom = nullptr; RefPtr<nsAtom> atom = nullptr;
switch (aValue) { switch (*aValue) {
case eTextPosBaseline: case eTextPosBaseline:
atom = nsGkAtoms::baseline; atom = nsGkAtoms::baseline;
break; break;
@ -703,9 +736,6 @@ void TextAttrsMgr::TextPosTextAttr::ExposeValue(AccAttributes* aAttributes,
case eTextPosSuper: case eTextPosSuper:
atom = NS_Atomize("super"); atom = NS_Atomize("super");
break; break;
case eTextPosNone:
break;
} }
if (atom) { if (atom) {
@ -713,42 +743,75 @@ void TextAttrsMgr::TextPosTextAttr::ExposeValue(AccAttributes* aAttributes,
} }
} }
TextAttrsMgr::TextPosValue TextAttrsMgr::TextPosTextAttr::GetTextPosValue( Maybe<TextAttrsMgr::TextPosValue>
nsIFrame* aFrame) const { TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm) const {
nsIFrame* ariaFrame = nullptr;
return GetAriaTextPosValue(aElm, ariaFrame);
}
Maybe<TextAttrsMgr::TextPosValue>
TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm,
nsIFrame*& ariaFrame) const {
// Search for the superscript and subscript roles that imply text-position.
const nsIContent* elm = aElm;
do {
if (elm->IsElement()) {
const mozilla::dom::Element* domElm = elm->AsElement();
static const dom::Element::AttrValuesArray tokens[] = {
nsGkAtoms::subscript, nsGkAtoms::superscript, nullptr};
const int32_t valueIdx = domElm->FindAttrValueIn(
kNameSpaceID_None, nsGkAtoms::role, tokens, eCaseMatters);
ariaFrame = domElm->GetPrimaryFrame();
if (valueIdx == 0) {
return Some(eTextPosSub);
}
if (valueIdx == 1) {
return Some(eTextPosSuper);
}
}
} while ((elm = elm->GetParent()) && elm != mRootElm);
ariaFrame = nullptr;
return Nothing{};
}
Maybe<TextAttrsMgr::TextPosValue>
TextAttrsMgr::TextPosTextAttr::GetLayoutTextPosValue(nsIFrame* aFrame) const {
const auto& verticalAlign = aFrame->StyleDisplay()->mVerticalAlign; const auto& verticalAlign = aFrame->StyleDisplay()->mVerticalAlign;
if (verticalAlign.IsKeyword()) { if (verticalAlign.IsKeyword()) {
switch (verticalAlign.AsKeyword()) { switch (verticalAlign.AsKeyword()) {
case StyleVerticalAlignKeyword::Baseline: case StyleVerticalAlignKeyword::Baseline:
return eTextPosBaseline; return Some(eTextPosBaseline);
case StyleVerticalAlignKeyword::Sub: case StyleVerticalAlignKeyword::Sub:
return eTextPosSub; return Some(eTextPosSub);
case StyleVerticalAlignKeyword::Super: case StyleVerticalAlignKeyword::Super:
return eTextPosSuper; return Some(eTextPosSuper);
// No good guess for the rest, so do not expose value of text-position // No good guess for the rest, so do not expose value of text-position
// attribute. // attribute.
default: default:
return eTextPosNone; return Nothing{};
} }
} }
const auto& length = verticalAlign.AsLength(); const auto& length = verticalAlign.AsLength();
if (length.ConvertsToPercentage()) { if (length.ConvertsToPercentage()) {
float percentValue = length.ToPercentage(); const float percentValue = length.ToPercentage();
return percentValue > 0 return percentValue > 0 ? Some(eTextPosSuper)
? eTextPosSuper : (percentValue < 0 ? Some(eTextPosSub)
: (percentValue < 0 ? eTextPosSub : eTextPosBaseline); : Some(eTextPosBaseline));
} }
if (length.ConvertsToLength()) { if (length.ConvertsToLength()) {
nscoord coordValue = length.ToLength(); const nscoord coordValue = length.ToLength();
return coordValue > 0 ? eTextPosSuper return coordValue > 0
: (coordValue < 0 ? eTextPosSub : eTextPosBaseline); ? Some(eTextPosSuper)
: (coordValue < 0 ? Some(eTextPosSub) : Some(eTextPosBaseline));
} }
if (const nsIContent* content = aFrame->GetContent()) { if (const nsIContent* content = aFrame->GetContent()) {
if (content->IsHTMLElement(nsGkAtoms::sup)) return eTextPosSuper; if (content->IsHTMLElement(nsGkAtoms::sup)) return Some(eTextPosSuper);
if (content->IsHTMLElement(nsGkAtoms::sub)) return eTextPosSub; if (content->IsHTMLElement(nsGkAtoms::sub)) return Some(eTextPosSub);
} }
return eTextPosNone; return Nothing{};
} }

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

@ -407,27 +407,27 @@ class TextAttrsMgr {
* Class is used for the work with "text-position" text attribute. * Class is used for the work with "text-position" text attribute.
*/ */
enum TextPosValue { enum TextPosValue { eTextPosBaseline, eTextPosSub, eTextPosSuper };
eTextPosNone = 0,
eTextPosBaseline,
eTextPosSub,
eTextPosSuper
};
class TextPosTextAttr : public TTextAttr<TextPosValue> { class TextPosTextAttr : public TTextAttr<Maybe<TextPosValue>> {
public: public:
TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame); TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame,
nsIContent* aRootElm, nsIContent* aElm);
virtual ~TextPosTextAttr() {} virtual ~TextPosTextAttr() {}
protected: protected:
// TextAttr // TextAttr
virtual bool GetValueFor(LocalAccessible* aAccessible, virtual bool GetValueFor(LocalAccessible* aAccessible,
TextPosValue* aValue) override; Maybe<TextPosValue>* aValue) override;
virtual void ExposeValue(AccAttributes* aAttributes, virtual void ExposeValue(AccAttributes* aAttributes,
const TextPosValue& aValue) override; const Maybe<TextPosValue>& aValue) override;
private: private:
TextPosValue GetTextPosValue(nsIFrame* aFrame) const; Maybe<TextPosValue> GetAriaTextPosValue(nsIContent* aElm) const;
Maybe<TextPosValue> GetAriaTextPosValue(nsIContent* aElm,
nsIFrame*& ariaFrame) const;
Maybe<TextPosValue> GetLayoutTextPosValue(nsIFrame* aFrame) const;
nsIContent* mRootElm;
}; };
}; // TextAttrMgr }; // TextAttrMgr

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

@ -12,7 +12,7 @@
interface nsIAccessibleRole : nsISupports interface nsIAccessibleRole : nsISupports
{ {
/** /**
* Used when accessible hans't strong defined role. * Used when the accessible has no strongly-defined role.
*/ */
const unsigned long ROLE_NOTHING = 0; const unsigned long ROLE_NOTHING = 0;
@ -1077,4 +1077,18 @@ interface nsIAccessibleRole : nsISupports
*/ */
const unsigned long ROLE_METER = 185; const unsigned long ROLE_METER = 185;
/**
* Represents phrasing content that is presented with vertical alignment
* lower than the baseline and a smaller font size. For example, the "2" in
* the chemical formula H2O.
*/
const unsigned long ROLE_SUBSCRIPT = 186;
/**
* Represents phrasing content that is presented with vertical alignment
* higher than the baseline and a smaller font size. For example, the
* exponent in a math expression.
*/
const unsigned long ROLE_SUPERSCRIPT = 187;
}; };

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

@ -94,6 +94,8 @@ addAccessibleTask(
<hr id="hr" /> <hr id="hr" />
<ins id="insertion">Inserted text</ins> <ins id="insertion">Inserted text</ins>
<meter id="meter" min="0" max="100" value="24">meter text here</meter> <meter id="meter" min="0" max="100" value="24">meter text here</meter>
<sub id="sub">sub text here</sub>
<sup id="sup">sup text here</sup>
<!-- Some SVG stuff --> <!-- Some SVG stuff -->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg" <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg"
@ -211,6 +213,8 @@ addAccessibleTask(
null, null,
"level indicator" "level indicator"
); );
testRoleAndSubRole(accDoc, "sub", "AXGroup", "AXSubscriptStyleGroup");
testRoleAndSubRole(accDoc, "sup", "AXGroup", "AXSuperscriptStyleGroup");
// Some SVG stuff // Some SVG stuff
testRoleAndSubRole(accDoc, "svg", "AXImage"); testRoleAndSubRole(accDoc, "svg", "AXImage");

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

@ -1375,18 +1375,33 @@
}; };
testElm("strong_container", obj); testElm("strong_container", obj);
// ////////////////////////////////////////////////////////////////////////
// HTML:sub
obj = {
role: ROLE_SUBSCRIPT
};
testElm("sub", obj);
// ////////////////////////////////////////////////////////////////////////
// HTML:sup
obj = {
role: ROLE_SUPERSCRIPT
};
testElm("sup", obj);
// //////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////
// HTML:sub contained by paragraph // HTML:sub contained by paragraph
obj = { obj = {
role: ROLE_PARAGRAPH, role: ROLE_PARAGRAPH,
textAttrs: {
0: { },
6: { "text-position": "sub" },
},
children: [ children: [
{ role: ROLE_TEXT_LEAF }, // plain text { role: ROLE_TEXT_LEAF }, // plain text
{ role: ROLE_TEXT_LEAF }, // HTML:sub text {
role: ROLE_SUBSCRIPT, // HTML:sub
children: [
{ role: ROLE_TEXT_LEAF } // HTML:sub text
]
}
], ],
}; };
testElm("sub_container", obj); testElm("sub_container", obj);
@ -1396,13 +1411,14 @@
obj = { obj = {
role: ROLE_PARAGRAPH, role: ROLE_PARAGRAPH,
textAttrs: {
0: { },
6: { "text-position": "super" },
},
children: [ children: [
{ role: ROLE_TEXT_LEAF }, // plain text { role: ROLE_TEXT_LEAF }, // plain text
{ role: ROLE_TEXT_LEAF }, // HTML:sup text {
role: ROLE_SUPERSCRIPT, // HTML:sup
children: [
{ role: ROLE_TEXT_LEAF } // HTML:sup text
]
}
], ],
}; };
testElm("sup_container", obj); testElm("sup_container", obj);
@ -1961,6 +1977,8 @@
<span id="span"></span> <span id="span"></span>
<span id="span_explicit" title="explicit"></span> <span id="span_explicit" title="explicit"></span>
<p id="strong_container">normal<strong>strong</strong></p> <p id="strong_container">normal<strong>strong</strong></p>
<sub id="sub"></sub>
<sup id="sup"></sup>
<p id="sub_container">normal<sub>sub</sub></p> <p id="sub_container">normal<sub>sub</sub></p>
<p id="sup_container">normal<sup>sup</sup></p> <p id="sup_container">normal<sup>sup</sup></p>

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

@ -125,7 +125,9 @@ const ROLE_SLIDER = nsIAccessibleRole.ROLE_SLIDER;
const ROLE_SPINBUTTON = nsIAccessibleRole.ROLE_SPINBUTTON; const ROLE_SPINBUTTON = nsIAccessibleRole.ROLE_SPINBUTTON;
const ROLE_STATICTEXT = nsIAccessibleRole.ROLE_STATICTEXT; const ROLE_STATICTEXT = nsIAccessibleRole.ROLE_STATICTEXT;
const ROLE_STATUSBAR = nsIAccessibleRole.ROLE_STATUSBAR; const ROLE_STATUSBAR = nsIAccessibleRole.ROLE_STATUSBAR;
const ROLE_SUBSCRIPT = nsIAccessibleRole.ROLE_SUBSCRIPT;
const ROLE_SUGGESTION = nsIAccessibleRole.ROLE_SUGGESTION; const ROLE_SUGGESTION = nsIAccessibleRole.ROLE_SUGGESTION;
const ROLE_SUPERSCRIPT = nsIAccessibleRole.ROLE_SUPERSCRIPT;
const ROLE_SUMMARY = nsIAccessibleRole.ROLE_SUMMARY; const ROLE_SUMMARY = nsIAccessibleRole.ROLE_SUMMARY;
const ROLE_SWITCH = nsIAccessibleRole.ROLE_SWITCH; const ROLE_SWITCH = nsIAccessibleRole.ROLE_SWITCH;
const ROLE_TABLE = nsIAccessibleRole.ROLE_TABLE; const ROLE_TABLE = nsIAccessibleRole.ROLE_TABLE;

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

@ -146,8 +146,12 @@
testRole("aria_spinbutton_mixed", ROLE_SPINBUTTON); testRole("aria_spinbutton_mixed", ROLE_SPINBUTTON);
testRole("aria_status", ROLE_STATUSBAR); testRole("aria_status", ROLE_STATUSBAR);
testRole("aria_status_mixed", ROLE_STATUSBAR); testRole("aria_status_mixed", ROLE_STATUSBAR);
testRole("aria_subscript", ROLE_SUBSCRIPT);
testRole("aria_subscript_mixed", ROLE_SUBSCRIPT);
testRole("aria_suggestion", ROLE_SUGGESTION); testRole("aria_suggestion", ROLE_SUGGESTION);
testRole("aria_suggestion_mixed", ROLE_SUGGESTION); testRole("aria_suggestion_mixed", ROLE_SUGGESTION);
testRole("aria_superscript", ROLE_SUPERSCRIPT);
testRole("aria_superscript_mixed", ROLE_SUPERSCRIPT);
testRole("aria_switch", ROLE_SWITCH); testRole("aria_switch", ROLE_SWITCH);
testRole("aria_switch_mixed", ROLE_SWITCH); testRole("aria_switch_mixed", ROLE_SWITCH);
testRole("aria_tab", ROLE_PAGETAB); testRole("aria_tab", ROLE_PAGETAB);
@ -496,8 +500,12 @@
<span id="aria_spinbutton_mixed" role="sPINBUTTOn"></span> <span id="aria_spinbutton_mixed" role="sPINBUTTOn"></span>
<span id="aria_status" role="status"></span> <span id="aria_status" role="status"></span>
<span id="aria_status_mixed" role="sTATUs"></span> <span id="aria_status_mixed" role="sTATUs"></span>
<span id="aria_subscript" role="subscript"></span>
<span id="aria_subscript_mixed" role="sUBSCRIPt"></span>
<span id="aria_suggestion" role="suggestion"></span> <span id="aria_suggestion" role="suggestion"></span>
<span id="aria_suggestion_mixed" role="sUGGESTIOn"></span> <span id="aria_suggestion_mixed" role="sUGGESTIOn"></span>
<span id="aria_superscript" role="superscript"></span>
<span id="aria_superscript_mixed" role="sUPERSCRIPt"></span>
<span id="aria_switch" role="switch"></span> <span id="aria_switch" role="switch"></span>
<span id="aria_switch_mixed" role="sWITCh"></span> <span id="aria_switch_mixed" role="sWITCh"></span>
<span id="aria_tab" role="tab"></span> <span id="aria_tab" role="tab"></span>

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

@ -151,50 +151,50 @@
attrs = {}; attrs = {};
testTextAttrs(ID, 0, attrs, defAttrs, 0, 5); testTextAttrs(ID, 0, attrs, defAttrs, 0, 5);
attrs = { "text-position": "super", "font-size": "10pt" }; // Embedded object (sup) has no attributes but takes up one character.
testTextAttrs(ID, 5, attrs, defAttrs, 5, 13); testTextAttrs(ID, 5, {}, {}, 5, 6);
attrs = {}; attrs = {};
testTextAttrs(ID, 13, attrs, defAttrs, 13, 27); testTextAttrs(ID, 6, attrs, defAttrs, 6, 20);
attrs = { "text-position": "super" }; attrs = { "text-position": "super" };
testTextAttrs(ID, 27, attrs, defAttrs, 27, 35); testTextAttrs(ID, 20, attrs, defAttrs, 20, 28);
attrs = {}; attrs = {};
testTextAttrs(ID, 35, attrs, defAttrs, 35, 39); testTextAttrs(ID, 28, attrs, defAttrs, 28, 32);
attrs = { "text-position": "sub", "font-size": "10pt" }; // Embedded object (sub) has no attributes but takes up one character.
testTextAttrs(ID, 39, attrs, defAttrs, 39, 50); testTextAttrs(ID, 32, {}, {}, 32, 33);
attrs = {}; attrs = {};
testTextAttrs(ID, 50, attrs, defAttrs, 50, 55); testTextAttrs(ID, 33, attrs, defAttrs, 33, 38);
attrs = { "text-position": "sub" }; attrs = { "text-position": "sub" };
testTextAttrs(ID, 55, attrs, defAttrs, 55, 64); testTextAttrs(ID, 38, attrs, defAttrs, 38, 47);
attrs = {}; attrs = {};
testTextAttrs(ID, 64, attrs, defAttrs, 64, 69); testTextAttrs(ID, 47, attrs, defAttrs, 47, 52);
attrs = { "text-position": "super" }; attrs = { "text-position": "super" };
testTextAttrs(ID, 69, attrs, defAttrs, 69, 84); testTextAttrs(ID, 52, attrs, defAttrs, 52, 67);
attrs = {}; attrs = {};
testTextAttrs(ID, 84, attrs, defAttrs, 84, 89); testTextAttrs(ID, 67, attrs, defAttrs, 67, 72);
attrs = { "text-position": "sub" }; attrs = { "text-position": "sub" };
testTextAttrs(ID, 89, attrs, defAttrs, 89, 102); testTextAttrs(ID, 72, attrs, defAttrs, 72, 85);
attrs = {}; attrs = {};
testTextAttrs(ID, 102, attrs, defAttrs, 102, 107); testTextAttrs(ID, 85, attrs, defAttrs, 85, 90);
attrs = { "text-position": "super" }; attrs = { "text-position": "super" };
testTextAttrs(ID, 107, attrs, defAttrs, 107, 123); testTextAttrs(ID, 90, attrs, defAttrs, 90, 106);
attrs = {}; attrs = {};
testTextAttrs(ID, 123, attrs, defAttrs, 123, 128); testTextAttrs(ID, 106, attrs, defAttrs, 106, 111);
attrs = { "text-position": "sub" }; attrs = { "text-position": "sub" };
testTextAttrs(ID, 128, attrs, defAttrs, 128, 142); testTextAttrs(ID, 111, attrs, defAttrs, 111, 125);
// //////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////
// area7 // area7
@ -573,6 +573,75 @@
testDefaultTextAttrs(ID, defAttrs); testDefaultTextAttrs(ID, defAttrs);
testTextAttrs(ID, -1, {}, defAttrs, 0, 11); testTextAttrs(ID, -1, {}, defAttrs, 0, 11);
// ////////////////////////////////////////////////////////////////////////
// HTML sub tag offset test - verify attributes
ID = "sub_tag";
defAttrs = buildDefaultTextAttrs(ID, "10pt");
defAttrs["text-position"] = "sub";
testDefaultTextAttrs(ID, defAttrs);
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
// ////////////////////////////////////////////////////////////////////////
// HTML sup tag offset test - verify attributes
ID = "sup_tag";
defAttrs = buildDefaultTextAttrs(ID, "10pt");
defAttrs["text-position"] = "super";
testDefaultTextAttrs(ID, defAttrs);
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
// ////////////////////////////////////////////////////////////////////////
// ARIA subscript role - verify text-position attribute
ID = "subscript_role";
defAttrs = { "text-position": "sub" };
testDefaultTextAttrs(ID, defAttrs, true);
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
// ////////////////////////////////////////////////////////////////////////
// ARIA superscript role - verify text-position attribute
ID = "superscript_role";
defAttrs = { "text-position": "super" };
testDefaultTextAttrs(ID, defAttrs, true);
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
// ////////////////////////////////////////////////////////////////////////
// test text-position attributes in various situations
ID = "superscript_role_in_div";
defAttrs = { "text-position": "super" };
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
ID = "sub_within_superscript_role";
defAttrs = { "text-position": "sub" };
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
ID = "sup_within_subscript_role";
defAttrs = { "text-position": "super" };
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
ID = "sub_within_sup";
defAttrs = { "text-position": "sub" };
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
ID = "sup_within_sub";
defAttrs = { "text-position": "super" };
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
ID = "css_sub_within_superscript_role";
attrs = { "text-position": "sub" };
testTextAttrs(ID, 0, attrs, {}, 0, 11, true);
ID = "css_super_within_subscript_role";
attrs = { "text-position": "super" };
testTextAttrs(ID, 0, attrs, {}, 0, 11, true);
ID = "sub_with_superscript_role";
defAttrs = { "text-position": "super" };
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
ID = "sup_with_subscript_role";
defAttrs = { "text-position": "sub" };
testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true);
SimpleTest.finish(); SimpleTest.finish();
} }
@ -734,5 +803,21 @@
<p id="area20" style="font-size: 15pt;">offset test</p> <p id="area20" style="font-size: 15pt;">offset test</p>
<!-- subscript, superscript tests -->
<sub id="sub_tag">offset test</sub>
<sup id="sup_tag">offset test</sup>
<p id="subscript_role" role="subscript">offset test</p>
<p id="superscript_role" role="superscript">offset test</p>
<div><span id="superscript_role_in_div" role="superscript">offset test</span></div>
<p role="superscript"><sub id="sub_within_superscript_role">offset test</sub></p>
<p role="subscript"><sup id="sup_within_subscript_role">offset test</sup></p>
<sup><sub id="sub_within_sup">offset test</sub></sup>
<sub><sup id="sup_within_sub">offset test</sup></sub>
<p id="css_sub_within_superscript_role" role="superscript"><span style="vertical-align: sub">offset test</span></p>
<p id="css_super_within_subscript_role" role="subscript"><span style="vertical-align: super">offset test</span></p>
<sub id="sub_with_superscript_role" role="superscript">offset test</sub>
<sup id="sup_with_subscript_role" role="subscript">offset test</sup>
</body> </body>
</html> </html>