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 math
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
|
@ -83,6 +84,9 @@ def web_socket_transfer_data(request):
|
|||
exec(code, namespace)
|
||||
# Run the function we just defined.
|
||||
ret = namespace["run"]()
|
||||
if isinstance(ret, float) and math.isnan(ret):
|
||||
# NaN can't be serialized by JSON.
|
||||
ret = None
|
||||
send("return", ret)
|
||||
except Exception:
|
||||
send("exception", traceback.format_exc())
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ addUiaTask(
|
|||
await setUpWaitForUiaPropEvent("ValueValue", "text");
|
||||
await runPython(`pattern.SetValue("after")`);
|
||||
await waitForUiaEvent();
|
||||
ok(true, "Got ValueValue prop change event on text");
|
||||
is(
|
||||
await runPython(`pattern.CurrentValue`),
|
||||
"after",
|
||||
|
|
@ -434,6 +435,7 @@ addUiaTask(
|
|||
await setUpWaitForUiaPropEvent("ValueValue", "ariaTextbox");
|
||||
await runPython(`pattern.SetValue("after")`);
|
||||
await waitForUiaEvent();
|
||||
ok(true, "Got ValueValue prop change event on ariaTextbox");
|
||||
is(
|
||||
await runPython(`pattern.CurrentValue`),
|
||||
"after",
|
||||
|
|
@ -443,3 +445,95 @@ addUiaTask(
|
|||
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);
|
||||
gotNewVal = true;
|
||||
break;
|
||||
case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
|
||||
property = UIA_RangeValueValuePropertyId;
|
||||
newVal.vt = VT_R8;
|
||||
uia->get_Value(&newVal.dblVal);
|
||||
gotNewVal = true;
|
||||
break;
|
||||
}
|
||||
if (property && ::UiaClientsAreListening()) {
|
||||
// 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);
|
||||
} else if (aIid == IID_IInvokeProvider) {
|
||||
*aInterface = static_cast<IInvokeProvider*>(this);
|
||||
} else if (aIid == IID_IRangeValueProvider) {
|
||||
*aInterface = static_cast<IRangeValueProvider*>(this);
|
||||
} else if (aIid == IID_IScrollItemProvider) {
|
||||
*aInterface = static_cast<IScrollItemProvider*>(this);
|
||||
} else if (aIid == IID_IToggleProvider) {
|
||||
|
|
@ -279,6 +287,12 @@ uiaRawElmProvider::GetPatternProvider(
|
|||
invoke.forget(aPatternProvider);
|
||||
}
|
||||
return S_OK;
|
||||
case UIA_RangeValuePatternId:
|
||||
if (acc->HasNumericValue()) {
|
||||
RefPtr<IValueProvider> value = this;
|
||||
value.forget(aPatternProvider);
|
||||
}
|
||||
return S_OK;
|
||||
case UIA_ScrollItemPatternId: {
|
||||
RefPtr<IScrollItemProvider> scroll = this;
|
||||
scroll.forget(aPatternProvider);
|
||||
|
|
@ -786,6 +800,99 @@ uiaRawElmProvider::get_IsReadOnly(__RPC__out BOOL* aRetVal) {
|
|||
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
|
||||
|
||||
bool uiaRawElmProvider::IsControl() {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ class uiaRawElmProvider : public IAccessibleEx,
|
|||
public IToggleProvider,
|
||||
public IExpandCollapseProvider,
|
||||
public IScrollItemProvider,
|
||||
public IValueProvider {
|
||||
public IValueProvider,
|
||||
public IRangeValueProvider {
|
||||
public:
|
||||
static constexpr enum ProviderOptions kProviderOptions =
|
||||
static_cast<enum ProviderOptions>(ProviderOptions_ServerSideProvider |
|
||||
|
|
@ -123,6 +124,27 @@ class uiaRawElmProvider : public IAccessibleEx,
|
|||
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsReadOnly(
|
||||
/* [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:
|
||||
Accessible* Acc() const;
|
||||
bool IsControl();
|
||||
|
|
|
|||
Loading…
Reference in a new issue