Bug 1479039 - Implement java portion of focus path cache. r=snorp,yzen a=jcristau
authorEitan Isaacson <eitan@monotonous.org>
Mon, 12 Nov 2018 16:41:40 +0000
changeset 501350 44d90df54ca9c09561577f8ede75c24d78935337
parent 501349 f5d4bc87f0fd88b769b19da3e843e404d877dbc3
child 501351 857f5b5b5777836285ff87a9f0a321aa19da3300
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, yzen, jcristau
bugs1479039
milestone64.0
Bug 1479039 - Implement java portion of focus path cache. r=snorp,yzen a=jcristau Differential Revision: https://phabricator.services.mozilla.com/D11215
accessible/android/DocAccessibleWrap.cpp
accessible/android/Platform.cpp
accessible/android/SessionAccessibility.cpp
accessible/android/SessionAccessibility.h
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
--- a/accessible/android/DocAccessibleWrap.cpp
+++ b/accessible/android/DocAccessibleWrap.cpp
@@ -221,12 +221,18 @@ DocAccessibleWrap::CacheFocusPath(Access
                                         acc->CurValue(),
                                         acc->MinValue(),
                                         acc->MaxValue(),
                                         acc->Step(),
                                         attributes));
     }
 
     ipcDoc->SendBatch(eBatch_FocusPath, cacheData);
-  } else {
-    // XXX: Local codepath, next patch
+  } else if (SessionAccessibility* sessionAcc = SessionAccessibility::GetInstanceFor(this)) {
+    nsTArray<AccessibleWrap*> accessibles;
+    for (AccessibleWrap* acc = aAccessible; acc && acc != this->Parent();
+         acc = static_cast<AccessibleWrap*>(acc->Parent())) {
+      accessibles.AppendElement(acc);
+    }
+
+    sessionAcc->ReplaceFocusPathCache(accessibles);
   }
 }
\ No newline at end of file
--- a/accessible/android/Platform.cpp
+++ b/accessible/android/Platform.cpp
@@ -217,15 +217,15 @@ a11y::ProxyBatch(ProxyAccessible* aDocum
     accWraps.AppendElement(WrapperFor(aAccessibles.ElementAt(i)));
   }
 
   switch (aBatchType) {
     case DocAccessibleWrap::eBatch_Viewport:
       sessionAcc->ReplaceViewportCache(accWraps, aData);
       break;
     case DocAccessibleWrap::eBatch_FocusPath:
-      // XXX: Next patch
+      sessionAcc->ReplaceFocusPathCache(accWraps, aData);
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown batch type.");
       break;
   }
 }
--- a/accessible/android/SessionAccessibility.cpp
+++ b/accessible/android/SessionAccessibility.cpp
@@ -8,16 +8,17 @@
 #include "DocAccessibleParent.h"
 #include "nsThreadUtils.h"
 #include "AccessibilityEvent.h"
 #include "HyperTextAccessible.h"
 #include "JavaBuiltins.h"
 #include "RootAccessibleWrap.h"
 #include "nsAccessibilityService.h"
 #include "nsViewManager.h"
+#include "nsIPersistentProperties2.h"
 
 #ifdef DEBUG
 #include <android/log.h>
 #define AALOG(args...)                                                         \
   __android_log_print(ANDROID_LOG_INFO, "GeckoAccessibilityNative", ##args)
 #else
 #define AALOG(args...)                                                         \
   do {                                                                         \
@@ -328,16 +329,47 @@ void
 SessionAccessibility::ReplaceViewportCache(const nsTArray<AccessibleWrap*>& aAccessibles,
                                           const nsTArray<BatchData>& aData)
 {
   auto infos = jni::ObjectArray::New<java::GeckoBundle>(aAccessibles.Length());
   for (size_t i = 0; i < aAccessibles.Length(); i++) {
     AccessibleWrap* acc = aAccessibles.ElementAt(i);
     if (aData.Length() == aAccessibles.Length()) {
       const BatchData& data = aData.ElementAt(i);
-      infos->SetElement(i, acc->ToSmallBundle(data.State(), data.Bounds()));
+      auto bundle = acc->ToSmallBundle(data.State(), data.Bounds());
+      infos->SetElement(i, bundle);
     } else {
       infos->SetElement(i, acc->ToSmallBundle());
     }
   }
 
   mSessionAccessibility->ReplaceViewportCache(infos);
 }
+
+void
+SessionAccessibility::ReplaceFocusPathCache(const nsTArray<AccessibleWrap*>& aAccessibles,
+                                            const nsTArray<BatchData>& aData)
+{
+  auto infos = jni::ObjectArray::New<java::GeckoBundle>(aAccessibles.Length());
+  for (size_t i = 0; i < aAccessibles.Length(); i++) {
+    AccessibleWrap* acc = aAccessibles.ElementAt(i);
+    if (aData.Length() == aAccessibles.Length()) {
+      const BatchData& data = aData.ElementAt(i);
+      nsCOMPtr<nsIPersistentProperties> props =
+        AccessibleWrap::AttributeArrayToProperties(data.Attributes());
+      auto bundle = acc->ToBundle(data.State(),
+                                  data.Bounds(),
+                                  data.Name(),
+                                  data.TextValue(),
+                                  data.DOMNodeID(),
+                                  data.CurValue(),
+                                  data.MinValue(),
+                                  data.MaxValue(),
+                                  data.Step(),
+                                  props);
+      infos->SetElement(i, bundle);
+    } else {
+      infos->SetElement(i, acc->ToBundle());
+    }
+  }
+
+  mSessionAccessibility->ReplaceFocusPathCache(infos);
+}
--- a/accessible/android/SessionAccessibility.h
+++ b/accessible/android/SessionAccessibility.h
@@ -104,16 +104,19 @@ public:
   void SendClickedEvent(AccessibleWrap* aAccessible, bool aChecked);
   void SendWindowContentChangedEvent(AccessibleWrap* aAccessible);
   void SendWindowStateChangedEvent(AccessibleWrap* aAccessible);
 
   // Cache methods
   void ReplaceViewportCache(const nsTArray<AccessibleWrap*>& aAccessibles,
                            const nsTArray<BatchData>& aData = nsTArray<BatchData>());
 
+  void ReplaceFocusPathCache(const nsTArray<AccessibleWrap*>& aAccessibles,
+                             const nsTArray<BatchData>& aData = nsTArray<BatchData>());
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SessionAccessibility)
 
 private:
   ~SessionAccessibility() {}
 
   void SetAttached(bool aAttached, already_AddRefed<Runnable> aRunnable);
   RootAccessibleWrap* GetRoot();
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
@@ -28,16 +28,18 @@ import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.RangeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 
+import java.util.LinkedList;
+
 public class SessionAccessibility {
     private static final String LOGTAG = "GeckoAccessibility";
 
     // This is the number BrailleBack uses to start indexing routing keys.
     private static final int BRAILLE_CLICK_BASE_INDEX = -275000000;
     private static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
             "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
 
@@ -244,30 +246,38 @@ public class SessionAccessibility {
 
         private AccessibilityNodeInfo getNodeFromGecko(final int virtualViewId) {
             AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView, virtualViewId);
             populateNodeFromBundle(node, nativeProvider.getNodeInfo(virtualViewId), false);
             return node;
         }
 
         private boolean isNodeCached(final int virtualViewId) {
-            return mViewportCache.get(virtualViewId) != null;
+            return mViewportCache.get(virtualViewId) != null || mFocusPathCache.get(virtualViewId) != null;
         }
 
-        private AccessibilityNodeInfo getNodeFromCache(final int virtualViewId) {
-            GeckoBundle bundle = mViewportCache.get(virtualViewId);
-            if (bundle == null) {
-                Log.e(LOGTAG, "No node for " + virtualViewId + " cache size: " + mViewportCache.size());
-                return null;
+        private synchronized AccessibilityNodeInfo getNodeFromCache(final int virtualViewId) {
+            AccessibilityNodeInfo node = null;
+            for (SparseArray<GeckoBundle> cache : mCaches) {
+                GeckoBundle bundle = cache.get(virtualViewId);
+                if (bundle == null) {
+                    continue;
+                }
+
+                if (node == null) {
+                    node = AccessibilityNodeInfo.obtain(mView, virtualViewId);
+                }
+                populateNodeFromBundle(node, bundle, true);
             }
 
-            AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView, virtualViewId);
-            populateNodeFromBundle(node, bundle, true);
+            if (node == null) {
+                Log.e(LOGTAG, "No cached node for " + virtualViewId);
+            }
+
             return node;
-
         }
 
         private void populateNodeFromBundle(final AccessibilityNodeInfo node, final GeckoBundle nodeInfo, final boolean fromCache) {
             if (mView == null || nodeInfo == null) {
                 return;
             }
 
             boolean isRoot = nodeInfo.getInt("id") == View.NO_ID;
@@ -282,17 +292,20 @@ public class SessionAccessibility {
                 node.setParent(mView, nodeInfo.getInt("parentId", View.NO_ID));
             }
 
             final int flags = nodeInfo.getInt("flags");
 
             // The basics
             node.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
             node.setClassName(getClassName(nodeInfo.getInt("className")));
-            node.setText(nodeInfo.getString("text", ""));
+
+            if (nodeInfo.containsKey("text")) {
+                node.setText(nodeInfo.getString("text"));
+            }
 
             // Add actions
             node.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
             node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
             node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
             node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
             node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
             node.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
@@ -445,16 +458,20 @@ public class SessionAccessibility {
     private View mView;
     // 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;
     // Viewport cache
     final SparseArray<GeckoBundle> mViewportCache = new SparseArray<>();
+    // Focus cache
+    final SparseArray<GeckoBundle> mFocusPathCache = new SparseArray<>();
+    // List of caches in descending order from last updated.
+    LinkedList<SparseArray<GeckoBundle>> mCaches = new LinkedList<>();
 
     /* package */ SessionAccessibility(final GeckoSession session) {
         mSession = session;
         Settings.updateAccessibilitySettings();
     }
 
     /**
       * Get the View instance that delegates accessibility to this session.
@@ -671,17 +688,30 @@ public class SessionAccessibility {
                 @Override
                 public void run() {
                     sendEvent(eventType, sourceId, className, eventData);
                 }
             });
         }
 
         @WrapForJNI(calledFrom = "gecko")
-        private void replaceViewportCache(final GeckoBundle[] bundles) {
+        private synchronized void replaceViewportCache(final GeckoBundle[] bundles) {
             mViewportCache.clear();
             for (GeckoBundle bundle : bundles) {
                 if (bundle == null) { continue; }
                 mViewportCache.append(bundle.getInt("id"), bundle);
             }
+            mCaches.remove(mViewportCache);
+            mCaches.add(mViewportCache);
+        }
+
+        @WrapForJNI(calledFrom = "gecko")
+        private synchronized void replaceFocusPathCache(final GeckoBundle[] bundles) {
+            mFocusPathCache.clear();
+            for (GeckoBundle bundle : bundles) {
+                if (bundle == null) { continue; }
+                mFocusPathCache.append(bundle.getInt("id"), bundle);
+            }
+            mCaches.remove(mFocusPathCache);
+            mCaches.add(mFocusPathCache);
         }
     }
 }