Bug 1479037 - Introduce native event support 4/4. r=jchen,yzen?jamie

Depends on D6683

Differential Revision: https://phabricator.services.mozilla.com/D6684

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Eitan Isaacson 2018-10-09 17:21:01 +00:00
Родитель f3d88538aa
Коммит 8102b1e502
13 изменённых файлов: 679 добавлений и 109 удалений

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

@ -40,6 +40,118 @@ AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc)
//-----------------------------------------------------
AccessibleWrap::~AccessibleWrap() {}
nsresult
AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
{
nsresult rv = Accessible::HandleAccEvent(aEvent);
NS_ENSURE_SUCCESS(rv, rv);
if (IPCAccessibilityActive()) {
return NS_OK;
}
auto accessible = static_cast<AccessibleWrap*>(aEvent->GetAccessible());
NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE);
// The accessible can become defunct if we have an xpcom event listener
// which decides it would be fun to change the DOM and flush layout.
if (accessible->IsDefunct() || !accessible->IsBoundToParent()) {
return NS_OK;
}
if (DocAccessible* doc = accessible->Document()) {
if (!nsCoreUtils::IsContentDocument(doc->DocumentNode())) {
return NS_OK;
}
}
SessionAccessibility* sessionAcc =
SessionAccessibility::GetInstanceFor(accessible);
if (!sessionAcc) {
return NS_OK;
}
switch (aEvent->GetEventType()) {
case nsIAccessibleEvent::EVENT_FOCUS:
sessionAcc->SendFocusEvent(accessible);
break;
case nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED: {
AccVCChangeEvent* vcEvent = downcast_accEvent(aEvent);
auto newPosition = static_cast<AccessibleWrap*>(vcEvent->NewAccessible());
auto oldPosition = static_cast<AccessibleWrap*>(vcEvent->OldAccessible());
if (sessionAcc && newPosition) {
if (oldPosition != newPosition) {
if (vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT) {
sessionAcc->SendHoverEnterEvent(newPosition);
} else {
sessionAcc->SendAccessibilityFocusedEvent(newPosition);
}
}
if (vcEvent->BoundaryType() != nsIAccessiblePivot::NO_BOUNDARY) {
sessionAcc->SendTextTraversedEvent(
newPosition, vcEvent->NewStartOffset(), vcEvent->NewEndOffset());
}
}
break;
}
case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
AccCaretMoveEvent* event = downcast_accEvent(aEvent);
sessionAcc->SendTextSelectionChangedEvent(accessible,
event->GetCaretOffset());
break;
}
case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
case nsIAccessibleEvent::EVENT_TEXT_REMOVED: {
AccTextChangeEvent* event = downcast_accEvent(aEvent);
sessionAcc->SendTextChangedEvent(accessible,
event->ModifiedText(),
event->GetStartOffset(),
event->GetLength(),
event->IsTextInserted(),
event->IsFromUserInput());
break;
}
case nsIAccessibleEvent::EVENT_STATE_CHANGE: {
AccStateChangeEvent* event = downcast_accEvent(aEvent);
auto state = event->GetState();
if (state & states::CHECKED) {
sessionAcc->SendClickedEvent(accessible);
}
if (state & states::SELECTED) {
sessionAcc->SendSelectedEvent(accessible);
}
if (state & states::BUSY) {
sessionAcc->SendWindowStateChangedEvent(accessible);
}
break;
}
case nsIAccessibleEvent::EVENT_SCROLLING: {
AccScrollingEvent* event = downcast_accEvent(aEvent);
sessionAcc->SendScrollingEvent(accessible,
event->ScrollX(),
event->ScrollY(),
event->MaxScrollX(),
event->MaxScrollY());
break;
}
case nsIAccessibleEvent::EVENT_SHOW:
case nsIAccessibleEvent::EVENT_HIDE: {
AccMutationEvent* event = downcast_accEvent(aEvent);
auto parent = static_cast<AccessibleWrap*>(event->Parent());
sessionAcc->SendWindowContentChangedEvent(parent);
break;
}
default:
break;
}
return NS_OK;
}
void
AccessibleWrap::Shutdown()
{
@ -75,6 +187,24 @@ AccessibleWrap::SetTextContents(const nsAString& aText) {
}
}
void
AccessibleWrap::GetTextContents(nsAString& aText) {
// For now it is a simple wrapper for getting entire range of TextSubstring.
// In the future this may be smarter and retrieve a flattened string.
if (IsHyperText()) {
AsHyperText()->TextSubstring(0, -1, aText);
}
}
bool
AccessibleWrap::GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset) {
if (IsHyperText()) {
return AsHyperText()->SelectionBoundsAt(0, aStartOffset, aEndOffset);
}
return false;
}
mozilla::java::GeckoBundle::LocalRef
AccessibleWrap::CreateBundle(int32_t aParentID,
role aRole,

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

@ -20,12 +20,17 @@ public:
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
virtual ~AccessibleWrap();
virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
virtual void Shutdown() override;
int32_t VirtualViewID() const { return mID; }
virtual void SetTextContents(const nsAString& aText);
virtual void GetTextContents(nsAString& aText);
virtual bool GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset);
virtual mozilla::java::GeckoBundle::LocalRef ToBundle();
static const int32_t kNoID = -1;

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

@ -6,7 +6,10 @@
#include "Platform.h"
#include "ProxyAccessibleWrap.h"
#include "SessionAccessibility.h"
#include "mozilla/a11y/ProxyAccessible.h"
#include "nsIAccessibleEvent.h"
#include "nsIAccessiblePivot.h"
using namespace mozilla;
using namespace mozilla::a11y;
@ -54,33 +57,85 @@ a11y::ProxyDestroyed(ProxyAccessible* aProxy)
}
void
a11y::ProxyEvent(ProxyAccessible*, uint32_t)
a11y::ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType)
{
SessionAccessibility* sessionAcc =
SessionAccessibility::GetInstanceFor(aTarget);
if (!sessionAcc) {
return;
}
switch (aEventType) {
case nsIAccessibleEvent::EVENT_FOCUS:
sessionAcc->SendFocusEvent(WrapperFor(aTarget));
break;
}
}
void
a11y::ProxyStateChangeEvent(ProxyAccessible*, uint64_t, bool)
a11y::ProxyStateChangeEvent(ProxyAccessible* aTarget,
uint64_t aState,
bool aEnabled)
{
SessionAccessibility* sessionAcc =
SessionAccessibility::GetInstanceFor(aTarget);
if (!sessionAcc) {
return;
}
if (aState & states::CHECKED) {
sessionAcc->SendClickedEvent(WrapperFor(aTarget));
}
if (aState & states::SELECTED) {
sessionAcc->SendSelectedEvent(WrapperFor(aTarget));
}
if (aState & states::BUSY) {
sessionAcc->SendWindowStateChangedEvent(WrapperFor(aTarget));
}
}
void
a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
{
SessionAccessibility* sessionAcc =
SessionAccessibility::GetInstanceFor(aTarget);
if (sessionAcc) {
sessionAcc->SendTextSelectionChangedEvent(WrapperFor(aTarget), aOffset);
}
}
void
a11y::ProxyTextChangeEvent(ProxyAccessible*,
const nsString&,
int32_t,
uint32_t,
bool,
bool)
a11y::ProxyTextChangeEvent(ProxyAccessible* aTarget,
const nsString& aStr,
int32_t aStart,
uint32_t aLen,
bool aIsInsert,
bool aFromUser)
{
SessionAccessibility* sessionAcc =
SessionAccessibility::GetInstanceFor(aTarget);
if (sessionAcc) {
sessionAcc->SendTextChangedEvent(
WrapperFor(aTarget), aStr, aStart, aLen, aIsInsert, aFromUser);
}
}
void
a11y::ProxyShowHideEvent(ProxyAccessible*, ProxyAccessible*, bool, bool)
a11y::ProxyShowHideEvent(ProxyAccessible* aTarget,
ProxyAccessible* aParent,
bool aInsert,
bool aFromUser)
{
SessionAccessibility* sessionAcc =
SessionAccessibility::GetInstanceFor(aTarget);
if (sessionAcc) {
sessionAcc->SendWindowContentChangedEvent(WrapperFor(aParent));
}
}
void
@ -89,25 +144,57 @@ a11y::ProxySelectionEvent(ProxyAccessible*, ProxyAccessible*, uint32_t)
}
void
a11y::ProxyVirtualCursorChangeEvent(ProxyAccessible*,
ProxyAccessible*,
int32_t,
int32_t,
ProxyAccessible*,
int32_t,
int32_t,
int16_t,
int16_t,
bool)
a11y::ProxyVirtualCursorChangeEvent(ProxyAccessible* aTarget,
ProxyAccessible* aOldPosition,
int32_t aOldStartOffset,
int32_t aOldEndOffset,
ProxyAccessible* aNewPosition,
int32_t aNewStartOffset,
int32_t aNewEndOffset,
int16_t aReason,
int16_t aBoundaryType,
bool aFromUser)
{
if (!aNewPosition) {
return;
}
SessionAccessibility* sessionAcc =
SessionAccessibility::GetInstanceFor(aTarget);
if (!sessionAcc) {
return;
}
if (aOldPosition != aNewPosition) {
if (aReason == nsIAccessiblePivot::REASON_POINT) {
sessionAcc->SendHoverEnterEvent(WrapperFor(aNewPosition));
} else {
sessionAcc->SendAccessibilityFocusedEvent(WrapperFor(aNewPosition));
}
}
if (aBoundaryType != nsIAccessiblePivot::NO_BOUNDARY) {
sessionAcc->SendTextTraversedEvent(
WrapperFor(aNewPosition), aNewStartOffset, aNewEndOffset);
}
}
void
a11y::ProxyScrollingEvent(ProxyAccessible*,
uint32_t,
uint32_t,
uint32_t,
uint32_t,
uint32_t)
a11y::ProxyScrollingEvent(ProxyAccessible* aTarget,
uint32_t aEventType,
uint32_t aScrollX,
uint32_t aScrollY,
uint32_t aMaxScrollX,
uint32_t aMaxScrollY)
{
if (aEventType == nsIAccessibleEvent::EVENT_SCROLLING) {
SessionAccessibility* sessionAcc =
SessionAccessibility::GetInstanceFor(aTarget);
if (sessionAcc) {
sessionAcc->SendScrollingEvent(
WrapperFor(aTarget), aScrollX, aScrollY, aMaxScrollX, aMaxScrollY);
}
}
}

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

@ -66,6 +66,18 @@ ProxyAccessibleWrap::Attributes()
return attributes.forget();
}
uint32_t
ProxyAccessibleWrap::ChildCount() const
{
return Proxy()->ChildrenCount();
}
void
ProxyAccessibleWrap::ScrollTo(uint32_t aHow) const
{
Proxy()->ScrollTo(aHow);
}
// Other
void
@ -74,6 +86,22 @@ ProxyAccessibleWrap::SetTextContents(const nsAString& aText)
Proxy()->ReplaceText(PromiseFlatString(aText));
}
void
ProxyAccessibleWrap::GetTextContents(nsAString& aText)
{
nsAutoString text;
Proxy()->TextSubstring(0, -1, text);
aText.Assign(text);
}
bool
ProxyAccessibleWrap::GetSelectionBounds(int32_t* aStartOffset,
int32_t* aEndOffset)
{
nsAutoString unused;
return Proxy()->SelectionBoundsAt(0, unused, aStartOffset, aEndOffset);
}
mozilla::java::GeckoBundle::LocalRef
ProxyAccessibleWrap::ToBundle()
{

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

@ -32,10 +32,18 @@ public:
virtual already_AddRefed<nsIPersistentProperties> Attributes() override;
virtual uint32_t ChildCount() const override;
virtual void ScrollTo(uint32_t aHow) const override;
// AccessibleWrap
virtual void SetTextContents(const nsAString& aText) override;
virtual void GetTextContents(nsAString& aText) override;
virtual bool GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset) override;
virtual mozilla::java::GeckoBundle::LocalRef ToBundle() override;
};

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

@ -6,10 +6,12 @@
#include "SessionAccessibility.h"
#include "AndroidUiThread.h"
#include "nsThreadUtils.h"
#include "AccessibilityEvent.h"
#include "HyperTextAccessible.h"
#include "JavaBuiltins.h"
#include "RootAccessibleWrap.h"
#include "nsAccessibilityService.h"
#include "nsViewManager.h"
#ifdef DEBUG
#include <android/log.h>
@ -109,3 +111,209 @@ SessionAccessibility::SetText(int32_t aID, jni::String::Param aText)
acc->SetTextContents(aText->ToString());
}
}
SessionAccessibility*
SessionAccessibility::GetInstanceFor(ProxyAccessible* aAccessible)
{
Accessible* outerDoc = aAccessible->OuterDocOfRemoteBrowser();
if (!outerDoc) {
return nullptr;
}
return GetInstanceFor(outerDoc);
}
SessionAccessibility*
SessionAccessibility::GetInstanceFor(Accessible* aAccessible)
{
RootAccessible* rootAcc = aAccessible->RootAccessible();
nsIPresShell* shell = rootAcc->PresShell();
nsViewManager* vm = shell->GetViewManager();
if (!vm) {
return nullptr;
}
nsCOMPtr<nsIWidget> rootWidget;
vm->GetRootWidget(getter_AddRefs(rootWidget));
// `rootWidget` can be one of several types. Here we make sure it is an
// android nsWindow that implemented NS_NATIVE_WIDGET to return itself.
if (rootWidget &&
rootWidget->WindowType() == nsWindowType::eWindowType_toplevel &&
rootWidget->GetNativeData(NS_NATIVE_WIDGET) == rootWidget) {
return static_cast<nsWindow*>(rootWidget.get())->GetSessionAccessibility();
}
return nullptr;
}
void
SessionAccessibility::SendAccessibilityFocusedEvent(AccessibleWrap* aAccessible)
{
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_VIEW_ACCESSIBILITY_FOCUSED,
aAccessible->VirtualViewID(), nullptr, aAccessible->ToBundle());
aAccessible->ScrollTo(nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE);
}
void
SessionAccessibility::SendHoverEnterEvent(AccessibleWrap* aAccessible)
{
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_VIEW_HOVER_ENTER,
aAccessible->VirtualViewID(), nullptr, aAccessible->ToBundle());
}
void
SessionAccessibility::SendFocusEvent(AccessibleWrap* aAccessible)
{
// Suppress focus events from about:blank pages.
// This is important for tests.
if (aAccessible->IsDoc() && aAccessible->ChildCount() == 0) {
return;
}
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_VIEW_FOCUSED,
aAccessible->VirtualViewID(), nullptr, aAccessible->ToBundle());
}
void
SessionAccessibility::SendScrollingEvent(AccessibleWrap* aAccessible,
int32_t aScrollX,
int32_t aScrollY,
int32_t aMaxScrollX,
int32_t aMaxScrollY)
{
int32_t virtualViewId = aAccessible->VirtualViewID();
if (virtualViewId != AccessibleWrap::kNoID) {
// XXX: Support scrolling in subframes
return;
}
GECKOBUNDLE_START(eventInfo);
GECKOBUNDLE_PUT(eventInfo, "scrollX", java::sdk::Integer::ValueOf(aScrollX));
GECKOBUNDLE_PUT(eventInfo, "scrollY", java::sdk::Integer::ValueOf(aScrollY));
GECKOBUNDLE_PUT(eventInfo, "maxScrollX", java::sdk::Integer::ValueOf(aMaxScrollX));
GECKOBUNDLE_PUT(eventInfo, "maxScrollY", java::sdk::Integer::ValueOf(aMaxScrollY));
GECKOBUNDLE_FINISH(eventInfo);
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_VIEW_SCROLLED, virtualViewId,
eventInfo, aAccessible->ToBundle());
SendWindowContentChangedEvent(aAccessible);
}
void
SessionAccessibility::SendWindowContentChangedEvent(AccessibleWrap* aAccessible)
{
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_WINDOW_CONTENT_CHANGED,
aAccessible->VirtualViewID(), nullptr, aAccessible->ToBundle());
}
void
SessionAccessibility::SendWindowStateChangedEvent(AccessibleWrap* aAccessible)
{
// Suppress window state changed events from about:blank pages.
// This is important for tests.
if (aAccessible->IsDoc() && aAccessible->ChildCount() == 0) {
return;
}
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_WINDOW_STATE_CHANGED,
aAccessible->VirtualViewID(), nullptr, aAccessible->ToBundle());
}
void
SessionAccessibility::SendTextSelectionChangedEvent(AccessibleWrap* aAccessible,
int32_t aCaretOffset)
{
int32_t fromIndex = aCaretOffset;
int32_t startSel = -1;
int32_t endSel = -1;
if (aAccessible->GetSelectionBounds(&startSel, &endSel)) {
fromIndex = startSel == aCaretOffset ? endSel : startSel;
}
GECKOBUNDLE_START(eventInfo);
GECKOBUNDLE_PUT(eventInfo, "fromIndex", java::sdk::Integer::ValueOf(fromIndex));
GECKOBUNDLE_PUT(eventInfo, "toIndex", java::sdk::Integer::ValueOf(aCaretOffset));
GECKOBUNDLE_FINISH(eventInfo);
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_VIEW_TEXT_SELECTION_CHANGED,
aAccessible->VirtualViewID(), eventInfo, aAccessible->ToBundle());
}
void
SessionAccessibility::SendTextChangedEvent(AccessibleWrap* aAccessible,
const nsString& aStr,
int32_t aStart,
uint32_t aLen,
bool aIsInsert,
bool aFromUser)
{
if (!aFromUser) {
// Only dispatch text change events from users, for now.
return;
}
nsAutoString text;
aAccessible->GetTextContents(text);
nsAutoString beforeText(text);
if (aIsInsert) {
beforeText.Cut(aStart, aLen);
} else {
beforeText.Insert(aStr, aStart);
}
GECKOBUNDLE_START(eventInfo);
GECKOBUNDLE_PUT(eventInfo, "text", jni::StringParam(text));
GECKOBUNDLE_PUT(eventInfo, "beforeText", jni::StringParam(beforeText));
GECKOBUNDLE_PUT(eventInfo, "addedCount", java::sdk::Integer::ValueOf(aIsInsert ? aLen : 0));
GECKOBUNDLE_PUT(eventInfo, "removedCount", java::sdk::Integer::ValueOf(aIsInsert ? 0 : aLen));
GECKOBUNDLE_FINISH(eventInfo);
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_VIEW_TEXT_CHANGED,
aAccessible->VirtualViewID(), eventInfo, aAccessible->ToBundle());
}
void
SessionAccessibility::SendTextTraversedEvent(AccessibleWrap* aAccessible,
int32_t aStartOffset,
int32_t aEndOffset)
{
nsAutoString text;
aAccessible->GetTextContents(text);
GECKOBUNDLE_START(eventInfo);
GECKOBUNDLE_PUT(eventInfo, "text", jni::StringParam(text));
GECKOBUNDLE_PUT(eventInfo, "fromIndex", java::sdk::Integer::ValueOf(aStartOffset));
GECKOBUNDLE_PUT(eventInfo, "toIndex", java::sdk::Integer::ValueOf(aEndOffset));
GECKOBUNDLE_FINISH(eventInfo);
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::
TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
aAccessible->VirtualViewID(), eventInfo, aAccessible->ToBundle());
}
void
SessionAccessibility::SendClickedEvent(AccessibleWrap* aAccessible)
{
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_VIEW_CLICKED,
aAccessible->VirtualViewID(), nullptr, aAccessible->ToBundle());
}
void
SessionAccessibility::SendSelectedEvent(AccessibleWrap* aAccessible)
{
mSessionAccessibility->SendEvent(
java::sdk::AccessibilityEvent::TYPE_VIEW_SELECTED,
aAccessible->VirtualViewID(), nullptr, aAccessible->ToBundle());
}

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

@ -69,6 +69,8 @@ public:
}
static void Init();
static SessionAccessibility* GetInstanceFor(ProxyAccessible* aAccessible);
static SessionAccessibility* GetInstanceFor(Accessible* aAccessible);
// Native implementations
using Base::AttachNative;
@ -77,6 +79,31 @@ public:
void SetText(int32_t aID, jni::String::Param aText);
void StartNativeAccessibility();
// Event methods
void SendFocusEvent(AccessibleWrap* aAccessible);
void SendScrollingEvent(AccessibleWrap* aAccessible,
int32_t aScrollX,
int32_t aScrollY,
int32_t aMaxScrollX,
int32_t aMaxScrollY);
void SendAccessibilityFocusedEvent(AccessibleWrap* aAccessible);
void SendHoverEnterEvent(AccessibleWrap* aAccessible);
void SendTextSelectionChangedEvent(AccessibleWrap* aAccessible,
int32_t aCaretOffset);
void SendTextTraversedEvent(AccessibleWrap* aAccessible,
int32_t aStartOffset,
int32_t aEndOffset);
void SendTextChangedEvent(AccessibleWrap* aAccessible,
const nsString& aStr,
int32_t aStart,
uint32_t aLen,
bool aIsInsert,
bool aFromUser);
void SendSelectedEvent(AccessibleWrap* aAccessible);
void SendClickedEvent(AccessibleWrap* aAccessible);
void SendWindowContentChangedEvent(AccessibleWrap* aAccessible);
void SendWindowStateChangedEvent(AccessibleWrap* aAccessible);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SessionAccessibility)
private:

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

@ -555,7 +555,7 @@ public:
/**
* Scroll the accessible into view.
*/
void ScrollTo(uint32_t aHow) const;
virtual void ScrollTo(uint32_t aHow) const;
/**
* Scroll the accessible to the given point.

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

@ -19,6 +19,7 @@ const GECKOVIEW_MESSAGE = {
ACTIVATE: "GeckoView:AccessibilityActivate",
BY_GRANULARITY: "GeckoView:AccessibilityByGranularity",
CLIPBOARD: "GeckoView:AccessibilityClipboard",
CURSOR_TO_FOCUSED: "GeckoView:AccessibilityCursorToFocused",
EXPLORE_BY_TOUCH: "GeckoView:AccessibilityExploreByTouch",
LONG_PRESS: "GeckoView:AccessibilityLongPress",
NEXT: "GeckoView:AccessibilityNext",
@ -176,6 +177,9 @@ var AccessFu = {
case GECKOVIEW_MESSAGE.SCROLL_BACKWARD:
this.Input.androidScroll("backward");
break;
case GECKOVIEW_MESSAGE.CURSOR_TO_FOCUSED:
this.autoMove({ moveToFocused: true });
break;
case GECKOVIEW_MESSAGE.BY_GRANULARITY:
this.Input.moveByGranularity(data);
break;

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

@ -12,6 +12,7 @@ import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import android.support.test.filters.MediumTest
import android.support.test.InstrumentationRegistry
@ -33,7 +34,6 @@ import org.hamcrest.Matchers.*
import org.junit.Test
import org.junit.Before
import org.junit.After
import org.junit.Ignore
import org.junit.runner.RunWith
const val DISPLAY_WIDTH = 480
@ -97,6 +97,7 @@ class AccessibilityTest : BaseSessionTest() {
fun onTextChanged(event: AccessibilityEvent) { }
fun onTextTraversal(event: AccessibilityEvent) { }
fun onWinContentChanged(event: AccessibilityEvent) { }
fun onWinStateChanged(event: AccessibilityEvent) { }
}
@Before fun setup() {
@ -126,6 +127,7 @@ class AccessibilityTest : BaseSessionTest() {
AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED -> newDelegate.onTextChanged(event)
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY -> newDelegate.onTextTraversal(event)
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED -> newDelegate.onWinContentChanged(event)
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> newDelegate.onWinStateChanged(event)
else -> {}
}
return false
@ -140,11 +142,21 @@ class AccessibilityTest : BaseSessionTest() {
nodeInfos.forEach { node -> node.recycle() }
}
private fun waitForInitialFocus() {
private fun waitForInitialFocus(moveToFirstChild: Boolean = false) {
// XXX: Sometimes we get the window state change of the initial
// about:blank page loading. Need to figure out how to ignore that.
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onFocused(event: AccessibilityEvent) { }
@AssertCalled
override fun onWinStateChanged(event: AccessibilityEvent) { }
})
if (moveToFirstChild) {
provider.performAction(View.NO_ID,
AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, null)
}
}
@Test fun testRootNode() {
@ -154,7 +166,7 @@ class AccessibilityTest : BaseSessionTest() {
node.className.toString(), equalTo("android.webkit.WebView"))
}
@Ignore @Test fun testPageLoad() {
@Test fun testPageLoad() {
sessionRule.session.loadTestPath(INPUTS_PATH)
sessionRule.waitUntilCalled(object : EventDelegate {
@ -163,19 +175,18 @@ class AccessibilityTest : BaseSessionTest() {
})
}
@Ignore @Test fun testAccessibilityFocus() {
@Test fun testAccessibilityFocus() {
var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID
sessionRule.session.loadTestPath(INPUTS_PATH)
waitForInitialFocus()
provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
waitForInitialFocus(true)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onAccessibilityFocused(event: AccessibilityEvent) {
nodeId = getSourceId(event)
val node = createNodeInfo(nodeId)
assertThat("Label accessibility focused", node.className.toString(),
equalTo("android.view.View"))
assertThat("Text node should not be focusable", node.isFocusable, equalTo(false))
}
})
@ -188,12 +199,14 @@ class AccessibilityTest : BaseSessionTest() {
override fun onAccessibilityFocused(event: AccessibilityEvent) {
nodeId = getSourceId(event)
val node = createNodeInfo(nodeId)
assertThat("Editbox accessibility focused", node.className.toString(),
equalTo("android.widget.EditText"))
assertThat("Entry node should be focusable", node.isFocusable, equalTo(true))
}
})
}
@Ignore @Test fun testTextEntryNode() {
@Test fun testTextEntryNode() {
sessionRule.session.loadString("<input aria-label='Name' value='Tobias'>", "text/html")
waitForInitialFocus()
@ -201,7 +214,7 @@ class AccessibilityTest : BaseSessionTest() {
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onAccessibilityFocused(event: AccessibilityEvent) {
override fun onFocused(event: AccessibilityEvent) {
val nodeId = getSourceId(event)
val node = createNodeInfo(nodeId)
assertThat("Focused EditBox", node.className.toString(),
@ -275,7 +288,7 @@ class AccessibilityTest : BaseSessionTest() {
return arguments
}
@Ignore @Test fun testClipboard() {
@Test fun testClipboard() {
var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID;
sessionRule.session.loadString("<input value='hello cruel world' id='input'>", "text/html")
waitForInitialFocus()
@ -284,7 +297,7 @@ class AccessibilityTest : BaseSessionTest() {
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onAccessibilityFocused(event: AccessibilityEvent) {
override fun onFocused(event: AccessibilityEvent) {
nodeId = getSourceId(event)
val node = createNodeInfo(nodeId)
assertThat("Focused EditBox", node.className.toString(),
@ -326,13 +339,10 @@ class AccessibilityTest : BaseSessionTest() {
})
}
@Ignore @Test fun testMoveByCharacter() {
@Test fun testMoveByCharacter() {
var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID
sessionRule.session.loadTestPath(LOREM_IPSUM_HTML_PATH)
waitForInitialFocus()
provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
waitForInitialFocus(true)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
@ -359,13 +369,10 @@ class AccessibilityTest : BaseSessionTest() {
waitUntilTextTraversed(0, 1) // "L"
}
@Ignore @Test fun testMoveByWord() {
@Test fun testMoveByWord() {
var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID
sessionRule.session.loadTestPath(LOREM_IPSUM_HTML_PATH)
waitForInitialFocus()
provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
waitForInitialFocus(true)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
@ -392,10 +399,10 @@ class AccessibilityTest : BaseSessionTest() {
waitUntilTextTraversed(0, 5) // "Lorem"
}
@Ignore @Test fun testMoveByLine() {
@Test fun testMoveByLine() {
var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID
sessionRule.session.loadTestPath(LOREM_IPSUM_HTML_PATH)
waitForInitialFocus()
waitForInitialFocus(true)
provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
@ -425,12 +432,11 @@ class AccessibilityTest : BaseSessionTest() {
waitUntilTextTraversed(0, 18) // "Lorem ipsum dolor "
}
@Ignore @Test fun testCheckbox() {
@Test fun testCheckbox() {
var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID;
sessionRule.session.loadString("<label><input id='checkbox' type='checkbox'>many option</label>", "text/html")
waitForInitialFocus()
sessionRule.session.loadString("<label><input type='checkbox'>many option</label>", "text/html")
waitForInitialFocus(true)
mainSession.evaluateJS("$('#checkbox').focus()")
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onAccessibilityFocused(event: AccessibilityEvent) {
@ -440,7 +446,7 @@ class AccessibilityTest : BaseSessionTest() {
assertThat("Checkbox node is clickable", node.isClickable, equalTo(true))
assertThat("Checkbox node is focusable", node.isFocusable, equalTo(true))
assertThat("Checkbox node is not checked", node.isChecked, equalTo(false))
assertThat("Checkbox node has correct role", node.text.toString(), equalTo("many option check button"))
assertThat("Checkbox node has correct role", node.text.toString(), equalTo("many option"))
}
})
@ -451,16 +457,16 @@ class AccessibilityTest : BaseSessionTest() {
waitUntilClick(false)
}
@Ignore @Test fun testSelectable() {
@Test fun testSelectable() {
var nodeId = View.NO_ID
sessionRule.session.loadString(
"""<ul style="list-style-type: none;" role="listbox">
<li id="li" role="option" onclick="this.setAttribute('aria-selected',
this.getAttribute('aria-selected') == 'true' ? 'false' : 'true')">1</li>
<li role="option" aria-selected="false">2</li>
</ul>""","text/html")
waitForInitialFocus()
waitForInitialFocus(true)
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onAccessibilityFocused(event: AccessibilityEvent) {
@ -468,7 +474,7 @@ class AccessibilityTest : BaseSessionTest() {
var node = createNodeInfo(nodeId)
assertThat("Selectable node is clickable", node.isClickable, equalTo(true))
assertThat("Selectable node is not selected", node.isSelected, equalTo(false))
assertThat("Selectable node has correct role", node.text.toString(), equalTo("1 option list box"))
assertThat("Selectable node has correct text", node.text.toString(), equalTo("1"))
}
})
@ -492,7 +498,7 @@ class AccessibilityTest : BaseSessionTest() {
return screenRect.contains(nodeBounds)
}
@Ignore @Test fun testScroll() {
@Test fun testScroll() {
var nodeId = View.NO_ID
sessionRule.session.loadString(
"""<body style="margin: 0;">
@ -502,9 +508,11 @@ class AccessibilityTest : BaseSessionTest() {
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</body>""",
"text/html")
sessionRule.waitForPageStop()
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled
override fun onWinStateChanged(event: AccessibilityEvent) { }
@AssertCalled(count = 1)
override fun onFocused(event: AccessibilityEvent) {
nodeId = getSourceId(event)
@ -515,7 +523,7 @@ class AccessibilityTest : BaseSessionTest() {
}
})
provider.performAction(View.NO_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
provider.performAction(View.NO_ID, AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, null)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1, order = [1])
override fun onAccessibilityFocused(event: AccessibilityEvent) {
@ -531,25 +539,25 @@ class AccessibilityTest : BaseSessionTest() {
@AssertCalled(count = 1, order = [3])
override fun onWinContentChanged(event: AccessibilityEvent) {
nodeId = getSourceId(event)
assertThat("Focused node is onscreen", screenContainsNode(nodeId), equalTo(true))
}
})
SystemClock.sleep(100);
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1, order = [1])
override fun onScrolled(event: AccessibilityEvent) {
assertThat("View is scrolled to the end", event.scrollY, equalTo(event.maxScrollY))
assertThat("View is scrolled to the end", event.scrollY.toDouble(), closeTo(event.maxScrollY.toDouble(), 1.0))
}
@AssertCalled(count = 1, order = [2])
override fun onWinContentChanged(event: AccessibilityEvent) {
nodeId = getSourceId(event)
assertThat("Focused node is still onscreen", screenContainsNode(nodeId), equalTo(true))
}
})
SystemClock.sleep(100)
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1, order = [1])
@ -559,11 +567,11 @@ class AccessibilityTest : BaseSessionTest() {
@AssertCalled(count = 1, order = [2])
override fun onWinContentChanged(event: AccessibilityEvent) {
nodeId = getSourceId(event)
assertThat("Focused node is offscreen", screenContainsNode(nodeId), equalTo(false))
}
})
SystemClock.sleep(100)
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, null)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1, order = [1])
@ -574,12 +582,11 @@ class AccessibilityTest : BaseSessionTest() {
@AssertCalled(count = 1, order = [2])
override fun onScrolled(event: AccessibilityEvent) {
assertThat("View is scrolled to the end", event.scrollY, equalTo(event.maxScrollY))
assertThat("View is scrolled to the end", event.scrollY.toDouble(), closeTo(event.maxScrollY.toDouble(), 1.0))
}
@AssertCalled(count = 1, order = [3])
override fun onWinContentChanged(event: AccessibilityEvent) {
nodeId = getSourceId(event)
assertThat("Focused node is onscreen", screenContainsNode(nodeId), equalTo(true))
}
})
@ -588,17 +595,7 @@ class AccessibilityTest : BaseSessionTest() {
@Test fun autoFill() {
// Wait for the accessibility nodes to populate.
mainSession.loadTestPath(FORMS_HTML_PATH)
// sessionRule.waitUntilCalled(object : EventDelegate {
// // For the root document and the iframe document, each has a form group and
// // a group for inputs outside of forms, so the total count is 4.
// @AssertCalled(count = 4)
// override fun onWinContentChanged(event: AccessibilityEvent) {
// }
// })
// A quick but not reliable way to test the a11y tree. The next patch will have events
// to work with..
sessionRule.waitForPageStop()
waitForInitialFocus()
val autoFills = mapOf(
"#user1" to "bar", "#pass1" to "baz", "#user2" to "bar", "#pass2" to "baz") +
@ -668,12 +665,12 @@ class AccessibilityTest : BaseSessionTest() {
}
}
@Ignore @Test fun autoFill_navigation() {
@Test fun autoFill_navigation() {
fun countAutoFillNodes(cond: (AccessibilityNodeInfo) -> Boolean =
{ it.className == "android.widget.EditText" },
id: Int = View.NO_ID): Int {
val info = createNodeInfo(id)
return (if (cond(info)) 1 else 0) + (if (info.childCount > 0)
return (if (cond(info) && info.className != "android.webkit.WebView" ) 1 else 0) + (if (info.childCount > 0)
(0 until info.childCount).sumBy {
countAutoFillNodes(cond, info.getChildId(it))
} else 0)
@ -681,11 +678,8 @@ class AccessibilityTest : BaseSessionTest() {
// Wait for the accessibility nodes to populate.
mainSession.loadTestPath(FORMS_HTML_PATH)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 4)
override fun onWinContentChanged(event: AccessibilityEvent) {
}
})
waitForInitialFocus()
assertThat("Initial auto-fill count should match",
countAutoFillNodes(), equalTo(14))
assertThat("Password auto-fill count should match",
@ -693,17 +687,13 @@ class AccessibilityTest : BaseSessionTest() {
// Now wait for the nodes to clear.
mainSession.loadTestPath(HELLO_HTML_PATH)
mainSession.waitForPageStop()
waitForInitialFocus()
assertThat("Should not have auto-fill fields",
countAutoFillNodes(), equalTo(0))
// Now wait for the nodes to reappear.
mainSession.goBack()
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 4)
override fun onWinContentChanged(event: AccessibilityEvent) {
}
})
waitForInitialFocus()
assertThat("Should have auto-fill fields again",
countAutoFillNodes(), equalTo(14))
assertThat("Should not have focused field",
@ -732,10 +722,7 @@ class AccessibilityTest : BaseSessionTest() {
sessionRule.session.loadString(
"<label for='name'>Name:</label><input id='name' type='text' value='Julie'><button>Submit</button>",
"text/html")
// waitForInitialFocus()
// A quick but not reliable way to test the a11y tree. The next patch will have events
// to work with..
sessionRule.waitForPageStop()
waitForInitialFocus()
val rootNode = createNodeInfo(View.NO_ID)
assertThat("Document has 3 children", rootNode.childCount, equalTo(3))
@ -777,10 +764,7 @@ class AccessibilityTest : BaseSessionTest() {
|</ul>
""".trimMargin(),
"text/html")
// waitForInitialFocus()
// A quick but not reliable way to test the a11y tree. The next patch will have events
// to work with..
sessionRule.waitForPageStop()
waitForInitialFocus()
val rootNode = createNodeInfo(View.NO_ID)
assertThat("Document has 2 children", rootNode.childCount, equalTo(2))
@ -816,10 +800,7 @@ class AccessibilityTest : BaseSessionTest() {
|<input type="range" aria-label="Percent" min="0" max="1" step="0.01" value="0.83">
""".trimMargin(),
"text/html")
// waitForInitialFocus()
// A quick but not reliable way to test the a11y tree. The next patch will have events
// to work with..
sessionRule.waitForPageStop()
waitForInitialFocus()
val rootNode = createNodeInfo(View.NO_ID)
assertThat("Document has 3 children", rootNode.childCount, equalTo(3))

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

@ -11,6 +11,7 @@ import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.mozglue.JNIObject;
import android.content.Context;
@ -19,7 +20,6 @@ import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
@ -72,21 +72,36 @@ public class SessionAccessibility {
}
node.setClassName("android.webkit.WebView");
}
node.setAccessibilityFocused(mAccessibilityFocusedNode == virtualDescendantId);
return node;
}
@Override
public boolean performAction(final int virtualViewId, int action, Bundle arguments) {
final GeckoBundle data;
switch (action) {
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
final AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
event.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
event.setSource(mView, virtualViewId);
((ViewParent) mView).requestSendAccessibilityEvent(mView, event);
if (virtualViewId == View.NO_ID) {
sendEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, View.NO_ID, null, null);
} else {
final GeckoBundle nodeInfo = nativeProvider.getNodeInfo(virtualViewId);
final int flags = nodeInfo.getInt("flags");
if ((flags & FLAG_FOCUSED) != 0) {
mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityCursorToFocused", null);
} else {
sendEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, virtualViewId, null, nodeInfo);
}
}
return true;
case AccessibilityNodeInfo.ACTION_CLICK:
mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityActivate", null);
GeckoBundle nodeInfo = nativeProvider.getNodeInfo(virtualViewId);
final int flags = nodeInfo.getInt("flags");
if ((flags & (FLAG_SELECTABLE | FLAG_CHECKABLE)) == 0) {
sendEvent(AccessibilityEvent.TYPE_VIEW_CLICKED, virtualViewId, null, nodeInfo);
}
return true;
case AccessibilityNodeInfo.ACTION_LONG_CLICK:
mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityLongPress", null);
@ -166,6 +181,16 @@ public class SessionAccessibility {
return mView.performAccessibilityAction(action, arguments);
}
@Override
public AccessibilityNodeInfo findFocus(int focus) {
if (focus == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY &&
mAccessibilityFocusedNode != 0) {
return createAccessibilityNodeInfo(mAccessibilityFocusedNode);
}
return super.findFocus(focus);
}
private void populateNodeFromBundle(final AccessibilityNodeInfo node, final GeckoBundle nodeInfo) {
if (mView == null || nodeInfo == null) {
return;
@ -364,6 +389,8 @@ public class SessionAccessibility {
// The native portion of the node provider.
/* package */ final NativeProvider nativeProvider = new NativeProvider();
private boolean mAttached = false;
// The current node with accessibility focus
private int mAccessibilityFocusedNode = 0;
/* package */ SessionAccessibility(final GeckoSession session) {
mSession = session;
@ -455,6 +482,8 @@ public class SessionAccessibility {
PrefsHelper.addObserver(new String[]{ FORCE_ACCESSIBILITY_PREF }, prefHandler);
}
public static boolean isPlatformEnabled() { return sEnabled; }
public static boolean isEnabled() {
return sEnabled || sForceEnabled;
}
@ -515,6 +544,55 @@ public class SessionAccessibility {
return true;
}
/* package */ void sendEvent(final int eventType, final int sourceId, final GeckoBundle eventData, final GeckoBundle sourceInfo) {
ThreadUtils.assertOnUiThread();
if (mView == null) {
return;
}
if (!Settings.isPlatformEnabled() && (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null)) {
// Accessibility could be activated in Gecko via xpcom, for example when using a11y
// devtools. Here we assure that either Android a11y is *really* enabled, or no
// display is attached and we must be in a junit test.
return;
}
if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
mAccessibilityFocusedNode = sourceId;
}
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
event.setSource(mView, sourceId);
event.setEnabled(true);
if (sourceInfo != null) {
final int flags = sourceInfo.getInt("flags");
event.setClassName(sourceInfo.getString("className", "android.view.View"));
event.setChecked((flags & FLAG_CHECKED) != 0);
event.getText().add(sourceInfo.getString("text", ""));
}
if (eventData != null) {
if (eventData.containsKey("text")) {
event.getText().add(eventData.getString("text"));
}
event.setContentDescription(eventData.getString("description", ""));
event.setAddedCount(eventData.getInt("addedCount", -1));
event.setRemovedCount(eventData.getInt("removedCount", -1));
event.setFromIndex(eventData.getInt("fromIndex", -1));
event.setItemCount(eventData.getInt("itemCount", -1));
event.setCurrentItemIndex(eventData.getInt("currentItemIndex", -1));
event.setBeforeText(eventData.getString("beforeText", ""));
event.setToIndex(eventData.getInt("toIndex", -1));
event.setScrollX(eventData.getInt("scrollX", -1));
event.setScrollY(eventData.getInt("scrollY", -1));
event.setMaxScrollX(eventData.getInt("maxScrollX", -1));
event.setMaxScrollY(eventData.getInt("maxScrollY", -1));
}
((ViewParent) mView).requestSendAccessibilityEvent(mView, event);
}
/* package */ final class NativeProvider extends JNIObject {
@WrapForJNI(calledFrom = "ui")
private void setAttached(final boolean attached) {
@ -532,5 +610,15 @@ public class SessionAccessibility {
@WrapForJNI(dispatchTo = "gecko")
public native void setText(int id, String text);
@WrapForJNI(calledFrom = "gecko", stubName = "SendEvent")
private void sendEventNative(final int eventType, final int sourceId, final GeckoBundle eventData, final GeckoBundle sourceInfo) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
sendEvent(eventType, sourceId, eventData, sourceInfo);
}
});
}
}
}

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

@ -0,0 +1,3 @@
# We only use constants from KeyEvent
[android.view.accessibility.AccessibilityEvent = skip:true]
<field> = skip:false

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

@ -10,6 +10,7 @@ with Files("**"):
# List of stems to generate .cpp and .h files for. To add a stem, add it to
# this list and ensure that $(stem)-classes.txt exists in this directory.
generated = [
'AccessibilityEvent',
'AndroidBuild',
'AndroidRect',
'JavaBuiltins',