forked from mirrors/gecko-dev
Bug 1887785: Implement UIA RangeValue pattern. r=nlapre
Differential Revision: https://phabricator.services.mozilla.com/D207950
This commit is contained in:
parent
42042620f1
commit
56f7d50bd9
4 changed files with 228 additions and 1 deletions
|
|
@ -9,6 +9,7 @@ It is intended to be called from JS browser tests.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
@ -83,6 +84,9 @@ def web_socket_transfer_data(request):
|
||||||
exec(code, namespace)
|
exec(code, namespace)
|
||||||
# Run the function we just defined.
|
# Run the function we just defined.
|
||||||
ret = namespace["run"]()
|
ret = namespace["run"]()
|
||||||
|
if isinstance(ret, float) and math.isnan(ret):
|
||||||
|
# NaN can't be serialized by JSON.
|
||||||
|
ret = None
|
||||||
send("return", ret)
|
send("return", ret)
|
||||||
except Exception:
|
except Exception:
|
||||||
send("exception", traceback.format_exc())
|
send("exception", traceback.format_exc())
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,7 @@ addUiaTask(
|
||||||
await setUpWaitForUiaPropEvent("ValueValue", "text");
|
await setUpWaitForUiaPropEvent("ValueValue", "text");
|
||||||
await runPython(`pattern.SetValue("after")`);
|
await runPython(`pattern.SetValue("after")`);
|
||||||
await waitForUiaEvent();
|
await waitForUiaEvent();
|
||||||
|
ok(true, "Got ValueValue prop change event on text");
|
||||||
is(
|
is(
|
||||||
await runPython(`pattern.CurrentValue`),
|
await runPython(`pattern.CurrentValue`),
|
||||||
"after",
|
"after",
|
||||||
|
|
@ -434,6 +435,7 @@ addUiaTask(
|
||||||
await setUpWaitForUiaPropEvent("ValueValue", "ariaTextbox");
|
await setUpWaitForUiaPropEvent("ValueValue", "ariaTextbox");
|
||||||
await runPython(`pattern.SetValue("after")`);
|
await runPython(`pattern.SetValue("after")`);
|
||||||
await waitForUiaEvent();
|
await waitForUiaEvent();
|
||||||
|
ok(true, "Got ValueValue prop change event on ariaTextbox");
|
||||||
is(
|
is(
|
||||||
await runPython(`pattern.CurrentValue`),
|
await runPython(`pattern.CurrentValue`),
|
||||||
"after",
|
"after",
|
||||||
|
|
@ -443,3 +445,95 @@ addUiaTask(
|
||||||
await testPatternAbsent("button", "Value");
|
await testPatternAbsent("button", "Value");
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
async function testRangeValueProps(id, ro, val, min, max, small, large) {
|
||||||
|
await assignPyVarToUiaWithId(id);
|
||||||
|
await definePyVar("pattern", `getUiaPattern(${id}, "RangeValue")`);
|
||||||
|
ok(await runPython(`bool(pattern)`), `${id} has RangeValue pattern`);
|
||||||
|
is(
|
||||||
|
!!(await runPython(`pattern.CurrentIsReadOnly`)),
|
||||||
|
ro,
|
||||||
|
`${id} has IsReadOnly ${ro}`
|
||||||
|
);
|
||||||
|
is(await runPython(`pattern.CurrentValue`), val, `${id} has correct Value`);
|
||||||
|
is(
|
||||||
|
await runPython(`pattern.CurrentMinimum`),
|
||||||
|
min,
|
||||||
|
`${id} has correct Minimum`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
await runPython(`pattern.CurrentMaximum`),
|
||||||
|
max,
|
||||||
|
`${id} has correct Maximum`
|
||||||
|
);
|
||||||
|
// IA2 doesn't support small/large change, so the IA2 -> UIA proxy can't
|
||||||
|
// either.
|
||||||
|
if (gIsUiaEnabled) {
|
||||||
|
is(
|
||||||
|
await runPython(`pattern.CurrentSmallChange`),
|
||||||
|
small,
|
||||||
|
`${id} has correct SmallChange`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
await runPython(`pattern.CurrentLargeChange`),
|
||||||
|
large,
|
||||||
|
`${id} has correct LargeChange`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the RangeValue pattern.
|
||||||
|
*/
|
||||||
|
addUiaTask(
|
||||||
|
`
|
||||||
|
<input id="range" type="range">
|
||||||
|
<input id="rangeBig" type="range" max="1000">
|
||||||
|
<progress id="progress" value="0.5"></progress>
|
||||||
|
<input id="numberRo" type="number" min="0" max="10" value="5" readonly>
|
||||||
|
<div id="ariaSlider" role="slider">slider</div>
|
||||||
|
<button id="button">button</button>
|
||||||
|
`,
|
||||||
|
async function testRangeValue(browser) {
|
||||||
|
await definePyVar("doc", `getDocUia()`);
|
||||||
|
await testRangeValueProps("range", false, 50, 0, 100, 1, 10);
|
||||||
|
info("SetValue on range");
|
||||||
|
await setUpWaitForUiaPropEvent("RangeValueValue", "range");
|
||||||
|
await runPython(`pattern.SetValue(20)`);
|
||||||
|
await waitForUiaEvent();
|
||||||
|
ok(true, "Got RangeValueValue prop change event on range");
|
||||||
|
is(await runPython(`pattern.CurrentValue`), 20, "range has correct Value");
|
||||||
|
|
||||||
|
await testRangeValueProps("rangeBig", false, 500, 0, 1000, 1, 100);
|
||||||
|
|
||||||
|
// Gecko a11y doesn't expose progress bars as read only, but it probably
|
||||||
|
// should.
|
||||||
|
await testRangeValueProps("progress", false, 0.5, 0, 1, 0, 0.1);
|
||||||
|
info("Calling SetValue on progress");
|
||||||
|
await testPythonRaises(
|
||||||
|
`pattern.SetValue(0.6)`,
|
||||||
|
"SetValue on progress failed"
|
||||||
|
);
|
||||||
|
|
||||||
|
await testRangeValueProps("numberRo", true, 5, 0, 10, 1, 1);
|
||||||
|
info("Calling SetValue on numberRo");
|
||||||
|
await testPythonRaises(
|
||||||
|
`pattern.SetValue(6)`,
|
||||||
|
"SetValue on numberRo failed"
|
||||||
|
);
|
||||||
|
|
||||||
|
await testRangeValueProps("ariaSlider", false, 50, 0, 100, null, null);
|
||||||
|
info("Setting aria-valuenow on ariaSlider");
|
||||||
|
await setUpWaitForUiaPropEvent("RangeValueValue", "ariaSlider");
|
||||||
|
await invokeSetAttribute(browser, "ariaSlider", "aria-valuenow", "60");
|
||||||
|
await waitForUiaEvent();
|
||||||
|
ok(true, "Got RangeValueValue prop change event on ariaSlider");
|
||||||
|
is(
|
||||||
|
await runPython(`pattern.CurrentValue`),
|
||||||
|
60,
|
||||||
|
"ariaSlider has correct Value"
|
||||||
|
);
|
||||||
|
|
||||||
|
await testPatternAbsent("button", "RangeValue");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,12 @@ void uiaRawElmProvider::RaiseUiaEventForGeckoEvent(Accessible* aAcc,
|
||||||
uia->get_Value(&newVal.bstrVal);
|
uia->get_Value(&newVal.bstrVal);
|
||||||
gotNewVal = true;
|
gotNewVal = true;
|
||||||
break;
|
break;
|
||||||
|
case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
|
||||||
|
property = UIA_RangeValueValuePropertyId;
|
||||||
|
newVal.vt = VT_R8;
|
||||||
|
uia->get_Value(&newVal.dblVal);
|
||||||
|
gotNewVal = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (property && ::UiaClientsAreListening()) {
|
if (property && ::UiaClientsAreListening()) {
|
||||||
// We can't get the old value. Thankfully, clients don't seem to need it.
|
// We can't get the old value. Thankfully, clients don't seem to need it.
|
||||||
|
|
@ -162,6 +168,8 @@ uiaRawElmProvider::QueryInterface(REFIID aIid, void** aInterface) {
|
||||||
*aInterface = static_cast<IExpandCollapseProvider*>(this);
|
*aInterface = static_cast<IExpandCollapseProvider*>(this);
|
||||||
} else if (aIid == IID_IInvokeProvider) {
|
} else if (aIid == IID_IInvokeProvider) {
|
||||||
*aInterface = static_cast<IInvokeProvider*>(this);
|
*aInterface = static_cast<IInvokeProvider*>(this);
|
||||||
|
} else if (aIid == IID_IRangeValueProvider) {
|
||||||
|
*aInterface = static_cast<IRangeValueProvider*>(this);
|
||||||
} else if (aIid == IID_IScrollItemProvider) {
|
} else if (aIid == IID_IScrollItemProvider) {
|
||||||
*aInterface = static_cast<IScrollItemProvider*>(this);
|
*aInterface = static_cast<IScrollItemProvider*>(this);
|
||||||
} else if (aIid == IID_IToggleProvider) {
|
} else if (aIid == IID_IToggleProvider) {
|
||||||
|
|
@ -279,6 +287,12 @@ uiaRawElmProvider::GetPatternProvider(
|
||||||
invoke.forget(aPatternProvider);
|
invoke.forget(aPatternProvider);
|
||||||
}
|
}
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
case UIA_RangeValuePatternId:
|
||||||
|
if (acc->HasNumericValue()) {
|
||||||
|
RefPtr<IValueProvider> value = this;
|
||||||
|
value.forget(aPatternProvider);
|
||||||
|
}
|
||||||
|
return S_OK;
|
||||||
case UIA_ScrollItemPatternId: {
|
case UIA_ScrollItemPatternId: {
|
||||||
RefPtr<IScrollItemProvider> scroll = this;
|
RefPtr<IScrollItemProvider> scroll = this;
|
||||||
scroll.forget(aPatternProvider);
|
scroll.forget(aPatternProvider);
|
||||||
|
|
@ -786,6 +800,99 @@ uiaRawElmProvider::get_IsReadOnly(__RPC__out BOOL* aRetVal) {
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IRangeValueProvider methods
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
uiaRawElmProvider::SetValue(double aVal) {
|
||||||
|
Accessible* acc = Acc();
|
||||||
|
if (!acc) {
|
||||||
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
}
|
||||||
|
if (!acc->SetCurValue(aVal)) {
|
||||||
|
return UIA_E_INVALIDOPERATION;
|
||||||
|
}
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
uiaRawElmProvider::get_Value(__RPC__out double* aRetVal) {
|
||||||
|
if (!aRetVal) {
|
||||||
|
return E_INVALIDARG;
|
||||||
|
}
|
||||||
|
Accessible* acc = Acc();
|
||||||
|
if (!acc) {
|
||||||
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
}
|
||||||
|
*aRetVal = acc->CurValue();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
uiaRawElmProvider::get_Maximum(__RPC__out double* aRetVal) {
|
||||||
|
if (!aRetVal) {
|
||||||
|
return E_INVALIDARG;
|
||||||
|
}
|
||||||
|
Accessible* acc = Acc();
|
||||||
|
if (!acc) {
|
||||||
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
}
|
||||||
|
*aRetVal = acc->MaxValue();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
uiaRawElmProvider::get_Minimum(
|
||||||
|
/* [retval][out] */ __RPC__out double* aRetVal) {
|
||||||
|
if (!aRetVal) {
|
||||||
|
return E_INVALIDARG;
|
||||||
|
}
|
||||||
|
Accessible* acc = Acc();
|
||||||
|
if (!acc) {
|
||||||
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
}
|
||||||
|
*aRetVal = acc->MinValue();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
uiaRawElmProvider::get_LargeChange(
|
||||||
|
/* [retval][out] */ __RPC__out double* aRetVal) {
|
||||||
|
if (!aRetVal) {
|
||||||
|
return E_INVALIDARG;
|
||||||
|
}
|
||||||
|
Accessible* acc = Acc();
|
||||||
|
if (!acc) {
|
||||||
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
}
|
||||||
|
// We want the change that would occur if the user pressed page up or page
|
||||||
|
// down. For HTML input elements, this is 10% of the total range unless step
|
||||||
|
// is larger. See:
|
||||||
|
// https://searchfox.org/mozilla-central/rev/c7df16ffad1f12a19c81c16bce0b65e4a15304d0/dom/html/HTMLInputElement.cpp#3878
|
||||||
|
double step = acc->Step();
|
||||||
|
double min = acc->MinValue();
|
||||||
|
double max = acc->MaxValue();
|
||||||
|
if (std::isnan(step) || std::isnan(min) || std::isnan(max)) {
|
||||||
|
*aRetVal = UnspecifiedNaN<double>();
|
||||||
|
} else {
|
||||||
|
*aRetVal = std::max(step, (max - min) / 10);
|
||||||
|
}
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP
|
||||||
|
uiaRawElmProvider::get_SmallChange(
|
||||||
|
/* [retval][out] */ __RPC__out double* aRetVal) {
|
||||||
|
if (!aRetVal) {
|
||||||
|
return E_INVALIDARG;
|
||||||
|
}
|
||||||
|
Accessible* acc = Acc();
|
||||||
|
if (!acc) {
|
||||||
|
return CO_E_OBJNOTCONNECTED;
|
||||||
|
}
|
||||||
|
*aRetVal = acc->Step();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// Private methods
|
// Private methods
|
||||||
|
|
||||||
bool uiaRawElmProvider::IsControl() {
|
bool uiaRawElmProvider::IsControl() {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ class uiaRawElmProvider : public IAccessibleEx,
|
||||||
public IToggleProvider,
|
public IToggleProvider,
|
||||||
public IExpandCollapseProvider,
|
public IExpandCollapseProvider,
|
||||||
public IScrollItemProvider,
|
public IScrollItemProvider,
|
||||||
public IValueProvider {
|
public IValueProvider,
|
||||||
|
public IRangeValueProvider {
|
||||||
public:
|
public:
|
||||||
static constexpr enum ProviderOptions kProviderOptions =
|
static constexpr enum ProviderOptions kProviderOptions =
|
||||||
static_cast<enum ProviderOptions>(ProviderOptions_ServerSideProvider |
|
static_cast<enum ProviderOptions>(ProviderOptions_ServerSideProvider |
|
||||||
|
|
@ -123,6 +124,27 @@ class uiaRawElmProvider : public IAccessibleEx,
|
||||||
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsReadOnly(
|
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsReadOnly(
|
||||||
/* [retval][out] */ __RPC__out BOOL* pRetVal);
|
/* [retval][out] */ __RPC__out BOOL* pRetVal);
|
||||||
|
|
||||||
|
// IRangeValueProvider
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE SetValue(
|
||||||
|
/* [in] */ double aVal);
|
||||||
|
|
||||||
|
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Value(
|
||||||
|
/* [retval][out] */ __RPC__out double* aRetVal);
|
||||||
|
|
||||||
|
// get_IsReadOnly is shared with IValueProvider.
|
||||||
|
|
||||||
|
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Maximum(
|
||||||
|
/* [retval][out] */ __RPC__out double* aRetVal);
|
||||||
|
|
||||||
|
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Minimum(
|
||||||
|
/* [retval][out] */ __RPC__out double* aRetVal);
|
||||||
|
|
||||||
|
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_LargeChange(
|
||||||
|
/* [retval][out] */ __RPC__out double* aRetVal);
|
||||||
|
|
||||||
|
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_SmallChange(
|
||||||
|
/* [retval][out] */ __RPC__out double* aRetVal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Accessible* Acc() const;
|
Accessible* Acc() const;
|
||||||
bool IsControl();
|
bool IsControl();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue