Bug 1502187 - Implement Java part of viewport tree caching. r=jchen a=jcristau
authorEitan Isaacson <eitan@monotonous.org>
Mon, 05 Nov 2018 22:35:35 +0000
changeset 501345 26a874969a1e6efe27fbdc617f791bfdf141730d
parent 501344 b3869ac5fad25da3c0d9913eabaf2f0679a1d6cf
child 501346 60b4eadd89fc24f010404fc3c542fa60b60e191a
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)
reviewersjchen, jcristau
bugs1502187
milestone64.0
Bug 1502187 - Implement Java part of viewport tree caching. r=jchen a=jcristau Depends on D9866 Differential Revision: https://phabricator.services.mozilla.com/D9867
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
@@ -14,18 +14,18 @@ import org.mozilla.gecko.util.GeckoBundl
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.mozglue.JNIObject;
 
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
-import android.text.InputType;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 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.AccessibilityNodeInfo.RangeInfo;
@@ -102,28 +102,32 @@ public class SessionAccessibility {
 
         Log.e(LOGTAG, "Index " + index + " our of CLASSNAME bounds.");
         return "android.view.View"; // Fallback class is View
     }
 
     /* package */ final class NodeProvider extends AccessibilityNodeProvider {
         @Override
         public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) {
-            AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView, virtualDescendantId);
+            AccessibilityNodeInfo node;
             if (mAttached) {
-                populateNodeFromBundle(node, nativeProvider.getNodeInfo(virtualDescendantId));
+                node = mSession.getSettings().getBoolean(GeckoSessionSettings.FULL_ACCESSIBILITY_TREE) ?
+                        getNodeFromGecko(virtualDescendantId) : getNodeFromCache(virtualDescendantId);
+                if (node != null) {
+                    node.setAccessibilityFocused(mAccessibilityFocusedNode == virtualDescendantId);
+                }
             } else {
+                node = AccessibilityNodeInfo.obtain(mView, virtualDescendantId);
                 if (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null) {
                     // When running junit tests we don't have a display
                     mView.onInitializeAccessibilityNodeInfo(node);
                 }
                 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) {
@@ -231,17 +235,40 @@ public class SessionAccessibility {
           if (focus == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY &&
               mAccessibilityFocusedNode != 0) {
             return createAccessibilityNodeInfo(mAccessibilityFocusedNode);
           }
 
           return super.findFocus(focus);
         }
 
-        private void populateNodeFromBundle(final AccessibilityNodeInfo node, final GeckoBundle nodeInfo) {
+        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;
+        }
+
+        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;
+            }
+
+            AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView, virtualViewId);
+            populateNodeFromBundle(node, bundle, true);
+            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;
             if (isRoot) {
                 if (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null) {
                     // When running junit tests we don't have a display
@@ -305,17 +332,20 @@ public class SessionAccessibility {
                 final Rect parentBounds = new Rect(b[0] - (int)origin[0], b[1] - (int)origin[1], b[2], b[3]);
                 node.setBoundsInParent(parentBounds);
             }
 
             // Children
             int[] children = nodeInfo.getIntArray("children");
             if (children != null) {
                 for (int childId : children) {
-                    node.addChild(mView, childId);
+                    if (!fromCache || isNodeCached(childId)) {
+                        // If this node is from cache, only populate with children that are cached as well.
+                        node.addChild(mView, childId);
+                    }
                 }
             }
 
             // SDK 18 and above
             if (Build.VERSION.SDK_INT >= 18) {
                 if ((flags & FLAG_EDITABLE) != 0) {
                     node.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
                     node.addAction(AccessibilityNodeInfo.ACTION_CUT);
@@ -411,16 +441,18 @@ public class SessionAccessibility {
     /* package */  final GeckoSession mSession;
     // This is the view that delegates accessibility to us. We also sends event through it.
     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<>();
 
     /* package */ SessionAccessibility(final GeckoSession session) {
         mSession = session;
         Settings.updateAccessibilitySettings();
     }
 
     /**
       * Get the View instance that delegates accessibility to this session.
@@ -638,11 +670,16 @@ public class SessionAccessibility {
                 public void run() {
                     sendEvent(eventType, sourceId, className, eventData);
                 }
             });
         }
 
         @WrapForJNI(calledFrom = "gecko")
         private void replaceViewportCache(final GeckoBundle[] bundles) {
+            mViewportCache.clear();
+            for (GeckoBundle bundle : bundles) {
+                if (bundle == null) { continue; }
+                mViewportCache.append(bundle.getInt("id"), bundle);
+            }
         }
     }
 }