mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 23:02:20 +00:00
Bug 1881195: Implement IsControlElement and IsContentElement UIA properties. r=nlapre
Exactly what should be a control element on the web and what shouldn't isn't really documented anywhere. So, this is a little based on ideas from Chromium and a little based on my own understanding of how this should ideally work. This will definitely need to be tweaked as we further develop UIA. For now, the primary aim is to avoid the performance cliff caused by the UIA -> IA2 proxy's suboptimal implementation of IsControlElement. We map IsContentElement to IsControlElement, just as Chromium does. Differential Revision: https://phabricator.services.mozilla.com/D202923
This commit is contained in:
parent
53731fd1d3
commit
5ea5ad1b92
@ -252,6 +252,11 @@ class Accessible {
|
||||
*/
|
||||
virtual ENameValueFlag Name(nsString& aName) const = 0;
|
||||
|
||||
/*
|
||||
* Return true if the accessible name is empty.
|
||||
*/
|
||||
bool NameIsEmpty() const;
|
||||
|
||||
/*
|
||||
* Get the description of this accessible.
|
||||
*/
|
||||
@ -720,11 +725,6 @@ class Accessible {
|
||||
*/
|
||||
void ApplyImplicitState(uint64_t& aState) const;
|
||||
|
||||
/*
|
||||
* Return true if the accessible name is empty.
|
||||
*/
|
||||
bool NameIsEmpty() const;
|
||||
|
||||
private:
|
||||
static const uint8_t kTypeBits = 6;
|
||||
static const uint8_t kGenericTypesBits = 18;
|
||||
|
@ -9,3 +9,5 @@ support-files = ["head.js"]
|
||||
["browser_controlType.js"]
|
||||
|
||||
["browser_elementFromPoint.js"]
|
||||
|
||||
["browser_tree.js"]
|
||||
|
104
accessible/tests/browser/windows/uia/browser_tree.js
Normal file
104
accessible/tests/browser/windows/uia/browser_tree.js
Normal file
@ -0,0 +1,104 @@
|
||||
/* 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";
|
||||
|
||||
async function testIsControl(pyVar, isControl) {
|
||||
const result = await runPython(`bool(${pyVar}.CurrentIsControlElement)`);
|
||||
if (isControl) {
|
||||
ok(result, `${pyVar} is a control element`);
|
||||
} else {
|
||||
ok(!result, `${pyVar} isn't a control element`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a global Python variable and assign it to a given Python expression.
|
||||
*/
|
||||
function definePyVar(varName, expression) {
|
||||
return runPython(`
|
||||
global ${varName}
|
||||
${varName} = ${expression}
|
||||
`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UIA element with the given id and assign it to a global Python
|
||||
* variable using the id as the variable name.
|
||||
*/
|
||||
function assignPyVarToUiaWithId(id) {
|
||||
return definePyVar(id, `findUiaByDomId(doc, "${id}")`);
|
||||
}
|
||||
|
||||
addUiaTask(
|
||||
`
|
||||
<p id="p">paragraph</p>
|
||||
<div id="div">div</div>
|
||||
<!-- The spans are because the UIA -> IA2 proxy seems to remove a single text
|
||||
leaf child from even the raw tree.
|
||||
-->
|
||||
<a id="link" href="#">link<span> </span>></a>
|
||||
<h1 id="h1">h1<span> </span></h1>
|
||||
<h1 id="h1WithDiv"><div>h1 with div<span> </span></div></h1>
|
||||
<input id="range" type="range">
|
||||
<div onclick=";" id="clickable">clickable</div>
|
||||
<div id="editable" contenteditable>editable</div>
|
||||
<table id="table"><tr><th>th</th></tr></table>
|
||||
`,
|
||||
async function (browser, docAcc) {
|
||||
await definePyVar("doc", `getDocUia()`);
|
||||
await assignPyVarToUiaWithId("p");
|
||||
await testIsControl("p", false);
|
||||
await definePyVar(
|
||||
"pTextLeaf",
|
||||
`uiaClient.RawViewWalker.GetFirstChildElement(p)`
|
||||
);
|
||||
await testIsControl("pTextLeaf", true);
|
||||
await assignPyVarToUiaWithId("div");
|
||||
await testIsControl("div", false);
|
||||
await definePyVar(
|
||||
"divTextLeaf",
|
||||
`uiaClient.RawViewWalker.GetFirstChildElement(div)`
|
||||
);
|
||||
await testIsControl("divTextLeaf", true);
|
||||
await assignPyVarToUiaWithId("link");
|
||||
await testIsControl("link", true);
|
||||
await assignPyVarToUiaWithId("range");
|
||||
await testIsControl("range", true);
|
||||
await assignPyVarToUiaWithId("editable");
|
||||
await testIsControl("editable", true);
|
||||
await assignPyVarToUiaWithId("table");
|
||||
await testIsControl("table", true);
|
||||
if (!gIsUiaEnabled) {
|
||||
// The remaining tests are broken with the UIA -> IA2 proxy.
|
||||
return;
|
||||
}
|
||||
await definePyVar(
|
||||
"linkTextLeaf",
|
||||
`uiaClient.RawViewWalker.GetFirstChildElement(link)`
|
||||
);
|
||||
await testIsControl("linkTextLeaf", false);
|
||||
await assignPyVarToUiaWithId("h1");
|
||||
await testIsControl("h1", true);
|
||||
await definePyVar(
|
||||
"h1TextLeaf",
|
||||
`uiaClient.RawViewWalker.GetFirstChildElement(h1)`
|
||||
);
|
||||
await testIsControl("h1TextLeaf", false);
|
||||
await assignPyVarToUiaWithId("h1WithDiv");
|
||||
await testIsControl("h1WithDiv", true);
|
||||
// h1WithDiv's text leaf is its grandchild.
|
||||
await definePyVar(
|
||||
"h1WithDivTextLeaf",
|
||||
`uiaClient.RawViewWalker.GetFirstChildElement(
|
||||
uiaClient.RawViewWalker.GetFirstChildElement(
|
||||
h1WithDiv
|
||||
)
|
||||
)`
|
||||
);
|
||||
await testIsControl("h1WithDivTextLeaf", false);
|
||||
await assignPyVarToUiaWithId("clickable");
|
||||
await testIsControl("clickable", true);
|
||||
}
|
||||
);
|
@ -12,6 +12,7 @@
|
||||
#include "LocalAccessible-inl.h"
|
||||
#include "mozilla/a11y/RemoteAccessible.h"
|
||||
#include "MsaaAccessible.h"
|
||||
#include "nsTextEquivUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
@ -228,6 +229,12 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId,
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UIA_IsControlElementPropertyId:
|
||||
case UIA_IsContentElementPropertyId:
|
||||
aPropertyValue->vt = VT_BOOL;
|
||||
aPropertyValue->boolVal = IsControl() ? VARIANT_TRUE : VARIANT_FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
@ -242,3 +249,68 @@ uiaRawElmProvider::get_HostRawElementProvider(
|
||||
*aRawElmProvider = nullptr;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Private methods
|
||||
|
||||
bool uiaRawElmProvider::IsControl() {
|
||||
// UIA provides multiple views of the tree: raw, control and content. The
|
||||
// control and content views should only contain elements which a user cares
|
||||
// about when navigating.
|
||||
Accessible* acc = Acc();
|
||||
MOZ_ASSERT(acc);
|
||||
if (acc->IsTextLeaf()) {
|
||||
// If an ancestor control allows the name to be generated from content, do
|
||||
// not expose this text leaf as a control. Otherwise, the user will see the
|
||||
// text twice: once as the label of the control and once for the text leaf.
|
||||
for (Accessible* ancestor = acc->Parent(); ancestor && !ancestor->IsDoc();
|
||||
ancestor = ancestor->Parent()) {
|
||||
if (nsTextEquivUtils::HasNameRule(ancestor, eNameFromSubtreeRule)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (acc->HasNumericValue() || acc->ActionCount() > 0) {
|
||||
return true;
|
||||
}
|
||||
uint64_t state = acc->State();
|
||||
if (state & states::FOCUSABLE) {
|
||||
return true;
|
||||
}
|
||||
if (state & states::EDITABLE) {
|
||||
Accessible* parent = acc->Parent();
|
||||
if (parent && !(parent->State() & states::EDITABLE)) {
|
||||
// This is the root of a rich editable control.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't treat generic or text containers as controls unless they have a name
|
||||
// or description.
|
||||
switch (acc->Role()) {
|
||||
case roles::EMPHASIS:
|
||||
case roles::MARK:
|
||||
case roles::PARAGRAPH:
|
||||
case roles::SECTION:
|
||||
case roles::STRONG:
|
||||
case roles::SUBSCRIPT:
|
||||
case roles::SUPERSCRIPT:
|
||||
case roles::TEXT:
|
||||
case roles::TEXT_CONTAINER: {
|
||||
if (!acc->NameIsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
nsAutoString text;
|
||||
acc->Description(text);
|
||||
if (!text.IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ class uiaRawElmProvider : public IAccessibleEx,
|
||||
|
||||
private:
|
||||
Accessible* Acc();
|
||||
bool IsControl();
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
Loading…
Reference in New Issue
Block a user