Bug 1330257 - 1. Refactor SessionAccessibility; r=eeejay
authorJim Chen <nchen@mozilla.com>
Tue, 21 Aug 2018 15:23:27 -0400
changeset 487733 c8789a613477be022d74179f8cc385b410fe315d
parent 487732 acfaacaca4cbe91eb413abb206d2187f304fad0d
child 487734 20a3dacdf9b898432d0eecd5340a74b34aeb5255
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerseeejay
bugs1330257
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1330257 - 1. Refactor SessionAccessibility; r=eeejay Move the AccessibilityNodeProvider implementation under SessionAccessibility, to reduce the indent of the code. Also make all methods in SessionAccessibility.Settings static to make the code easier to follow. Differential Revision: https://phabricator.services.mozilla.com/D3250
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
@@ -24,36 +24,195 @@ import android.view.View;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 
 public class SessionAccessibility {
     private static final String LOGTAG = "GeckoAccessibility";
-    // This is a special ID we use for nodes that are eent sources.
+    // This is a special ID we use for nodes that are event sources.
     // We expose it as a fragment and not an actual child of the View node.
     private static final int VIRTUAL_CONTENT_ID = -2;
 
     // This is the number BrailleBack uses to start indexing routing keys.
     private static final int BRAILLE_CLICK_BASE_INDEX = -275000000;
 
+    /* package */ final class NodeProvider extends AccessibilityNodeProvider {
+        @Override
+        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) {
+            AccessibilityNodeInfo info = (virtualDescendantId == VIRTUAL_CONTENT_ID && mVirtualContentNode != null) ?
+                                         AccessibilityNodeInfo.obtain(mVirtualContentNode) :
+                                         AccessibilityNodeInfo.obtain(mView, virtualDescendantId);
+
+            switch (virtualDescendantId) {
+            case View.NO_ID:
+                // This is the parent View node.
+                // We intentionally don't add VIRTUAL_CONTENT_ID
+                // as a child. It is a source for events,
+                // but not a member of the tree you
+                // can get to by traversing down.
+                if (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null) {
+                    // When running junit tests we don't have a display
+                    mView.onInitializeAccessibilityNodeInfo(info);
+                }
+                info.setClassName("android.webkit.WebView"); // TODO: WTF
+                if (Build.VERSION.SDK_INT >= 19) {
+                    Bundle bundle = info.getExtras();
+                    bundle.putCharSequence(
+                        "ACTION_ARGUMENT_HTML_ELEMENT_STRING_VALUES",
+                        "ARTICLE,BUTTON,CHECKBOX,COMBOBOX,CONTROL," +
+                        "FOCUSABLE,FRAME,GRAPHIC,H1,H2,H3,H4,H5,H6," +
+                        "HEADING,LANDMARK,LINK,LIST,LIST_ITEM,MAIN," +
+                        "MEDIA,RADIO,SECTION,TABLE,TEXT_FIELD," +
+                        "UNVISITED_LINK,VISITED_LINK");
+                }
+                info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
+                info.addChild(hostView, VIRTUAL_CONTENT_ID);
+                break;
+            default:
+                info.setParent(mView);
+                info.setSource(mView, virtualDescendantId);
+                info.setVisibleToUser(mView.isShown());
+                info.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
+                info.setEnabled(true);
+                info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
+                info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
+                info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
+                info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER |
+                                              AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD |
+                                              AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+                break;
+            }
+            return info;
+        }
+
+        @Override
+        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+            if (virtualViewId == View.NO_ID) {
+                return performRootAction(action, arguments);
+            }
+            return performContentAction(action, arguments);
+        }
+
+        private boolean performRootAction(int action, Bundle arguments) {
+            switch (action) {
+            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+                final GeckoBundle data = new GeckoBundle(1);
+                data.putBoolean("gainFocus", action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityViewFocused", data);
+                return true;
+            }
+
+            return mView.performAccessibilityAction(action, arguments);
+        }
+
+        @SuppressWarnings("fallthrough")
+        private boolean performContentAction(int action, Bundle arguments) {
+            final GeckoBundle data;
+            switch (action) {
+            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+                final AccessibilityEvent event = obtainEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, VIRTUAL_CONTENT_ID);
+                ((ViewParent) mView).requestSendAccessibilityEvent(mView, event);
+                return true;
+            case AccessibilityNodeInfo.ACTION_CLICK:
+                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityActivate", null);
+                return true;
+            case AccessibilityNodeInfo.ACTION_LONG_CLICK:
+                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityLongPress", null);
+                return true;
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityScrollForward", null);
+                return true;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityScrollBackward", null);
+                return true;
+            case AccessibilityNodeInfo.ACTION_SELECT:
+                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilitySelect", null);
+                return true;
+            case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
+                if (mLastItem) {
+                    return false;
+                }
+                // fall-through
+            case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
+                if (arguments != null) {
+                    data = new GeckoBundle(1);
+                    data.putString("rule", arguments.getString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING));
+                } else {
+                    data = null;
+                }
+                mSession.getEventDispatcher().dispatch(action == AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT ?
+                                                       "GeckoView:AccessibilityNext" : "GeckoView:AccessibilityPrevious", data);
+                return true;
+            case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+                // XXX: Self brailling gives this action with a bogus argument instead of an actual click action;
+                // the argument value is the BRAILLE_CLICK_BASE_INDEX - the index of the routing key that was hit.
+                // Other negative values are used by ChromeVox, but we don't support them.
+                // FAKE_GRANULARITY_READ_CURRENT = -1
+                // FAKE_GRANULARITY_READ_TITLE = -2
+                // FAKE_GRANULARITY_STOP_SPEECH = -3
+                // FAKE_GRANULARITY_CHANGE_SHIFTER = -4
+                int granularity = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
+                if (granularity <= BRAILLE_CLICK_BASE_INDEX) {
+                    int keyIndex = BRAILLE_CLICK_BASE_INDEX - granularity;
+                    data = new GeckoBundle(1);
+                    data.putInt("keyIndex", keyIndex);
+                    mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityActivate", data);
+                } else if (granularity > 0) {
+                    boolean extendSelection = arguments.getBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);
+                    data = new GeckoBundle(3);
+                    data.putString("direction", action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY ? "Next" : "Previous");
+                    data.putInt("granularity", granularity);
+                    data.putBoolean("select", extendSelection);
+                    mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityByGranularity", data);
+                }
+                return true;
+            case AccessibilityNodeInfo.ACTION_SET_SELECTION:
+                if (arguments == null) {
+                    return false;
+                }
+                int selectionStart = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT);
+                int selectionEnd = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT);
+                data = new GeckoBundle(2);
+                data.putInt("start", selectionStart);
+                data.putInt("end", selectionEnd);
+                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilitySetSelection", data);
+                return true;
+            case AccessibilityNodeInfo.ACTION_CUT:
+            case AccessibilityNodeInfo.ACTION_COPY:
+            case AccessibilityNodeInfo.ACTION_PASTE:
+                data = new GeckoBundle(1);
+                data.putInt("action", action);
+                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityClipboard", data);
+                return true;
+            }
+
+            return mView.performAccessibilityAction(action, arguments);
+        }
+    };
+
     // Gecko session we are proxying
     /* package */  final GeckoSession mSession;
     // This is the view that delegates accessibility to us. We also sends event through it.
     private View mView;
-    // Aave we reached the last item in content?
+    // Have we reached the last item in content?
     private boolean mLastItem;
     // Used to store the JSON message and populate the event later in the code path.
     private AccessibilityNodeInfo mVirtualContentNode;
 
     /* package */ SessionAccessibility(final GeckoSession session) {
         mSession = session;
 
-        Settings.getInstance().dispatch();
+        Settings.updateAccessibilitySettings();
 
         session.getEventDispatcher().registerUiThreadListener(new BundleEventListener() {
             @Override
             public void handleMessage(final String event, final GeckoBundle message,
                                       final EventCallback callback) {
                 sendAccessibilityEvent(message);
             }
         }, "GeckoView:AccessibilityEvent", null);
@@ -81,212 +240,42 @@ public class SessionAccessibility {
         mView = view;
         mLastItem = false;
 
         if (mView == null) {
             return;
         }
 
         mView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
-            private AccessibilityNodeProvider mAccessibilityNodeProvider;
+            private NodeProvider mProvider;
 
             @Override
             public AccessibilityNodeProvider getAccessibilityNodeProvider(final View hostView) {
-
-                if (mAccessibilityNodeProvider == null)
-                    mAccessibilityNodeProvider = new AccessibilityNodeProvider() {
-                    @Override
-                    public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) {
-                        assertAttachedView(hostView);
-
-                        AccessibilityNodeInfo info = (virtualDescendantId == VIRTUAL_CONTENT_ID && mVirtualContentNode != null) ?
-                                                     AccessibilityNodeInfo.obtain(mVirtualContentNode) :
-                                                     AccessibilityNodeInfo.obtain(mView, virtualDescendantId);
-
-                        switch (virtualDescendantId) {
-                        case View.NO_ID:
-                            // This is the parent View node.
-                            // We intentionally don't add VIRTUAL_CONTENT_ID
-                            // as a child. It is a source for events,
-                            // but not a member of the tree you
-                            // can get to by traversing down.
-                            if (mView.getDisplay() != null) {
-                                // When running junit tests we don't have a display
-                                onInitializeAccessibilityNodeInfo(mView, info);
-                            }
-                            info.setClassName("android.webkit.WebView"); // TODO: WTF
-                            if (Build.VERSION.SDK_INT >= 19) {
-                                Bundle bundle = info.getExtras();
-                                bundle.putCharSequence(
-                                    "ACTION_ARGUMENT_HTML_ELEMENT_STRING_VALUES",
-                                    "ARTICLE,BUTTON,CHECKBOX,COMBOBOX,CONTROL," +
-                                    "FOCUSABLE,FRAME,GRAPHIC,H1,H2,H3,H4,H5,H6," +
-                                    "HEADING,LANDMARK,LINK,LIST,LIST_ITEM,MAIN," +
-                                    "MEDIA,RADIO,SECTION,TABLE,TEXT_FIELD," +
-                                    "UNVISITED_LINK,VISITED_LINK");
-                            }
-                            info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
-                            info.addChild(hostView, VIRTUAL_CONTENT_ID);
-                            break;
-                        default:
-                            info.setParent(mView);
-                            info.setSource(mView, virtualDescendantId);
-                            info.setVisibleToUser(mView.isShown());
-                            info.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
-                            info.setEnabled(true);
-                            info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
-                            info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
-                            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-                            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
-                            info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
-                            info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
-                            info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER |
-                                                          AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD |
-                                                          AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
-                            break;
-                        }
-                        return info;
-                    }
-
-                    @Override
-                    public boolean performAction(int virtualViewId, int action, Bundle arguments) {
-                        assertAttachedView(hostView);
-
-                        if (virtualViewId == View.NO_ID) {
-                            return performRootAction(action, arguments);
-                        }
-                        return performContentAction(action, arguments);
-                    }
-
-                    private boolean performRootAction(int action, Bundle arguments) {
-                        switch (action) {
-                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
-                        case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
-                            final GeckoBundle data = new GeckoBundle(1);
-                            data.putBoolean("gainFocus", action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
-                            mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityViewFocused", data);
-                            return true;
-                        }
-
-                        return mView.performAccessibilityAction(action, arguments);
-                    }
-
-                    @SuppressWarnings("fallthrough")
-                    private boolean performContentAction(int action, Bundle arguments) {
-                        final GeckoBundle data;
-                        switch (action) {
-                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
-                            final AccessibilityEvent event = obtainEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, VIRTUAL_CONTENT_ID);
-                            ((ViewParent) mView).requestSendAccessibilityEvent(mView, event);
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_CLICK:
-                            mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityActivate", null);
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_LONG_CLICK:
-                            mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityLongPress", null);
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
-                            mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityScrollForward", null);
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
-                            mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityScrollBackward", null);
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_SELECT:
-                            mSession.getEventDispatcher().dispatch("GeckoView:AccessibilitySelect", null);
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
-                            if (mLastItem) {
-                                return false;
-                            }
-                            // fall-through
-                        case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
-                            if (arguments != null) {
-                                data = new GeckoBundle(1);
-                                data.putString("rule", arguments.getString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING));
-                            } else {
-                                data = null;
-                            }
-                            mSession.getEventDispatcher().dispatch(action == AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT ?
-                                                                   "GeckoView:AccessibilityNext" : "GeckoView:AccessibilityPrevious", data);
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
-                        case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
-                            // XXX: Self brailling gives this action with a bogus argument instead of an actual click action;
-                            // the argument value is the BRAILLE_CLICK_BASE_INDEX - the index of the routing key that was hit.
-                            // Other negative values are used by ChromeVox, but we don't support them.
-                            // FAKE_GRANULARITY_READ_CURRENT = -1
-                            // FAKE_GRANULARITY_READ_TITLE = -2
-                            // FAKE_GRANULARITY_STOP_SPEECH = -3
-                            // FAKE_GRANULARITY_CHANGE_SHIFTER = -4
-                            int granularity = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
-                            if (granularity <= BRAILLE_CLICK_BASE_INDEX) {
-                                int keyIndex = BRAILLE_CLICK_BASE_INDEX - granularity;
-                                data = new GeckoBundle(1);
-                                data.putInt("keyIndex", keyIndex);
-                                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityActivate", data);
-                            } else if (granularity > 0) {
-                                boolean extendSelection = arguments.getBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);
-                                data = new GeckoBundle(3);
-                                data.putString("direction", action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY ? "Next" : "Previous");
-                                data.putInt("granularity", granularity);
-                                data.putBoolean("select", extendSelection);
-                                mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityByGranularity", data);
-                            }
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_SET_SELECTION:
-                            if (arguments == null) {
-                                return false;
-                            }
-                            int selectionStart = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT);
-                            int selectionEnd = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT);
-                            data = new GeckoBundle(2);
-                            data.putInt("start", selectionStart);
-                            data.putInt("end", selectionEnd);
-                            mSession.getEventDispatcher().dispatch("GeckoView:AccessibilitySetSelection", data);
-                            return true;
-                        case AccessibilityNodeInfo.ACTION_CUT:
-                        case AccessibilityNodeInfo.ACTION_COPY:
-                        case AccessibilityNodeInfo.ACTION_PASTE:
-                            data = new GeckoBundle(1);
-                            data.putInt("action", action);
-                            mSession.getEventDispatcher().dispatch("GeckoView:AccessibilityClipboard", data);
-                            return true;
-                        }
-
-                        return mView.performAccessibilityAction(action, arguments);
-                    }
-
-                    private void assertAttachedView(final View view) {
-                        if (view != mView) {
-                            throw new AssertionError("delegate used with wrong view.");
-                        }
-                    }
-                };
-
-                return mAccessibilityNodeProvider;
+                if (hostView != mView) {
+                    return null;
+                }
+                if (mProvider == null) {
+                    mProvider = new NodeProvider();
+                }
+                return mProvider;
             }
-
         });
     }
 
     private static class Settings {
-        private static final Settings INSTANCE = new Settings();
         private static final String FORCE_ACCESSIBILITY_PREF = "accessibility.force_disabled";
 
-        private volatile boolean mEnabled;
-        /* package */ volatile boolean mForceEnabled;
+        private static volatile boolean sEnabled;
+        /* package */ static volatile boolean sForceEnabled;
 
-        public Settings() {
+        static {
             final Context context = GeckoAppShell.getApplicationContext();
             AccessibilityManager accessibilityManager =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
 
-            mEnabled = accessibilityManager.isEnabled() &&
-                       accessibilityManager.isTouchExplorationEnabled();
-
             accessibilityManager.addAccessibilityStateChangeListener(
             new AccessibilityManager.AccessibilityStateChangeListener() {
                 @Override
                 public void onAccessibilityStateChanged(boolean enabled) {
                     updateAccessibilitySettings();
                 }
             }
             );
@@ -301,43 +290,39 @@ public class SessionAccessibility {
                 }
                 );
             }
 
             PrefsHelper.PrefHandler prefHandler = new PrefsHelper.PrefHandlerBase() {
                 @Override
                 public void prefValue(String pref, int value) {
                     if (pref.equals(FORCE_ACCESSIBILITY_PREF)) {
-                        mForceEnabled = value < 0;
+                        sForceEnabled = value < 0;
                         dispatch();
                     }
                 }
             };
             PrefsHelper.addObserver(new String[]{ FORCE_ACCESSIBILITY_PREF }, prefHandler);
         }
 
-        public static Settings getInstance() {
-            return INSTANCE;
+        public static boolean isEnabled() {
+            return sEnabled || sForceEnabled;
         }
 
-        public static boolean isEnabled() {
-            return INSTANCE.mEnabled || INSTANCE.mForceEnabled;
-        }
-
-        private void updateAccessibilitySettings() {
+        public static void updateAccessibilitySettings() {
             final AccessibilityManager accessibilityManager = (AccessibilityManager)
                     GeckoAppShell.getApplicationContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
             mEnabled = accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled();
 
             dispatch();
         }
 
-        private void dispatch() {
+        /* package */ static void dispatch() {
             final GeckoBundle ret = new GeckoBundle(1);
-            ret.putBoolean("enabled", mEnabled || mForceEnabled);
+            ret.putBoolean("enabled", isTouchExplorationEnabled());
             // "GeckoView:AccessibilitySettings" is dispatched to the Gecko thread.
             EventDispatcher.getInstance().dispatch("GeckoView:AccessibilitySettings", ret);
             // "GeckoView:AccessibilityEnabled" is dispatched to the UI thread.
             EventDispatcher.getInstance().dispatch("GeckoView:AccessibilityEnabled", ret);
         }
     }
 
     private AccessibilityEvent obtainEvent(final int eventType, final int sourceId) {