Bug 1415994 - 7. Migrate existing gfx code to use LayerSession; r=snorp draft
authorJim Chen <nchen@mozilla.com>
Tue, 14 Nov 2017 18:18:35 -0500
changeset 697882 e018cd461c93e7a4461f1b46ebd880ba637b1bc7
parent 697881 2626743107245b7a3f928b2e4febba25c567b9a4
child 697883 61b5f0b4fccf8b9e8dd1906b84429120eacde635
push id89133
push userbmo:nchen@mozilla.com
push dateTue, 14 Nov 2017 23:19:10 +0000
reviewerssnorp
bugs1415994
milestone59.0a1
Bug 1415994 - 7. Migrate existing gfx code to use LayerSession; r=snorp Remove most of the Compositor and Surface management code in LayerView. And use LayerSession.Compositor in rest of the gfx code. MozReview-Commit-ID: 5E9pj3eGHlv
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -849,20 +849,16 @@ public abstract class GeckoApp extends G
                 inSampleSize = Math.round((float)height / idealHeight);
             } else {
                 inSampleSize = Math.round((float)width / idealWidth);
             }
         }
         return inSampleSize;
     }
 
-    public void requestRender() {
-        mLayerView.requestRender();
-    }
-
     @Override // GeckoSession.ContentListener
     public void onTitleChange(final GeckoSession session, final String title) {
     }
 
     @Override // GeckoSession.ContentListener
     public void onFullScreen(final GeckoSession session, final boolean fullScreen) {
         if (fullScreen) {
             SnackbarBuilder.builder(this)
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 
-import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
@@ -361,30 +360,23 @@ class GeckoInputConnection
         mCursorAnchorInfoBuilder.reset();
 
         // Calculate Gecko logical coords to screen coords
         final GeckoView view = getView();
         if (view == null) {
             return;
         }
 
-        int[] viewCoords = new int[2];
-        view.getLocationOnScreen(viewCoords);
-
-        DynamicToolbarAnimator animator = view.getDynamicToolbarAnimator();
-        float toolbarHeight = (float) animator.getCurrentToolbarHeight();
-
         Matrix matrix = view.getMatrixForLayerRectToViewRect();
         if (matrix == null) {
             if (DEBUG) {
                 Log.d(LOGTAG, "Cannot get Matrix to convert from Gecko coords to layer view coords");
             }
             return;
         }
-        matrix.postTranslate(viewCoords[0], viewCoords[1] + toolbarHeight);
         mCursorAnchorInfoBuilder.setMatrix(matrix);
 
         final Editable content = getEditable();
         if (content == null) {
             return;
         }
         int composingStart = getComposingSpanStart(content);
         int composingEnd = getComposingSpanEnd(content);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -44,17 +44,17 @@ public class DynamicToolbarAnimator {
         public Bitmap getBitmapOfToolbarChrome();
         public boolean isToolbarChromeVisible();
         public void toggleToolbarChrome(boolean aShow);
     }
 
     private final Set<PinReason> mPinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class));
 
     private final GeckoLayerClient mTarget;
-    private LayerView.Compositor mCompositor;
+    private LayerSession.Compositor mCompositor;
     private final List<MetricsListener> mListeners;
     private ToolbarChromeProxy mToolbarChromeProxy;
     private int mMaxToolbarHeight;
 
     public DynamicToolbarAnimator(GeckoLayerClient aTarget) {
         mTarget = aTarget;
         mListeners = new ArrayList<MetricsListener>();
     }
@@ -176,17 +176,17 @@ public class DynamicToolbarAnimator {
                 mCompositor.sendToolbarAnimatorMessage(LayerView.REQUEST_HIDE_TOOLBAR_IMMEDIATELY);
             }
             for (PinReason reason : PinReason.values()) {
                 mCompositor.setPinned(mPinFlags.contains(reason), reason.value);
             }
         }
     }
 
-    /* package-private */ void notifyCompositorCreated(LayerView.Compositor aCompositor) {
+    /* package-private */ void notifyCompositorCreated(LayerSession.Compositor aCompositor) {
         ThreadUtils.assertOnUiThread();
         mCompositor = aCompositor;
     }
 
     private boolean isCompositorReady() {
         return ((mCompositor != null) && (mCompositor.isReady()));
     }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -39,18 +39,16 @@ class GeckoLayerClient implements LayerV
      * 1) reading mViewportMetrics from any thread is fine without synchronization
      * 2) writing to mViewportMetrics requires synchronizing on the layer controller object
      * 3) whenever reading multiple fields from mViewportMetrics without synchronization (i.e. in
      *    case 1 above) you should always first grab a local copy of the reference, and then use
      *    that because mViewportMetrics might get reassigned in between reading the different
      *    fields. */
     private volatile ImmutableViewportMetrics mViewportMetrics;
 
-    private volatile boolean mGeckoIsReady;
-
     private final PanZoomController mPanZoomController;
     private final DynamicToolbarAnimator mToolbarAnimator;
     private final LayerView mView;
 
     /* This flag is true from the time that browser.js detects a first-paint is about to start,
      * to the time that we receive the first-paint composite notification from the compositor.
      * Note that there is a small race condition with this; if there are two paints that both
      * have the first-paint flag set, and the second paint happens concurrently with the
@@ -76,47 +74,18 @@ class GeckoLayerClient implements LayerV
         mView.setListener(this);
         mContentDocumentIsDisplayed = true;
     }
 
     public void setOverscrollHandler(final Overscroll listener) {
         mPanZoomController.setOverscrollHandler(listener);
     }
 
-    public void setGeckoReady(boolean ready) {
-        mGeckoIsReady = ready;
-    }
-
-    public boolean isGeckoReady() {
-        return mGeckoIsReady;
-    }
-
-    /** Attaches to root layer so that Gecko appears. */
-    @WrapForJNI(calledFrom = "gecko")
-    private void onGeckoReady() {
-        mGeckoIsReady = true;
-
-        sendResizeEventIfNecessary(true);
-
-        // Gecko being ready is one of the two conditions (along with having an available
-        // surface) that cause us to create the compositor. So here, now that we know gecko
-        // is ready, call updateCompositor() to see if we can actually do the creation.
-        // This needs to run on the UI thread so that the surface validity can't change on
-        // us while we're in the middle of creating the compositor.
-        mView.post(new Runnable() {
-            @Override
-            public void run() {
-                getView().updateCompositor();
-            }
-        });
-    }
-
     public void destroy() {
         mPanZoomController.destroy();
-        mGeckoIsReady = false;
     }
 
     public LayerView getView() {
         return mView;
     }
 
     public FloatSize getViewportSize() {
         return mViewportMetrics.getSize();
@@ -132,22 +101,17 @@ class GeckoLayerClient implements LayerV
      */
     boolean setViewportSize(int width, int height) {
         if (mViewportMetrics.viewportRectWidth == width &&
             mViewportMetrics.viewportRectHeight == height) {
             return false;
         }
         mViewportMetrics = mViewportMetrics.setViewportSize(width, height);
 
-        if (mGeckoIsReady) {
-            // here we send gecko a resize message. The code in browser.js is responsible for
-            // picking up on that resize event, modifying the viewport as necessary, and informing
-            // us of the new viewport.
-            sendResizeEventIfNecessary(true);
-
+        if (mView.isCompositorReady()) {
             // the following call also sends gecko a message, which will be processed after the resize
             // message above has updated the viewport. this message ensures that if we have just put
             // focus in a text field, we scroll the content so that the text field is in view.
             final boolean imeIsEnabled = mView.isIMEEnabled();
             if (imeIsEnabled && !mImeWasEnabledOnLastResize) {
                 // The IME just came up after not being up, so let's scroll
                 // to the focused input.
                 EventDispatcher.getInstance().dispatch("ScrollTo:FocusedInput", null);
@@ -160,38 +124,16 @@ class GeckoLayerClient implements LayerV
     PanZoomController getPanZoomController() {
         return mPanZoomController;
     }
 
     DynamicToolbarAnimator getDynamicToolbarAnimator() {
         return mToolbarAnimator;
     }
 
-    /* Informs Gecko that the screen size has changed. */
-    private void sendResizeEventIfNecessary(boolean force) {
-        IntSize newWindowSize = new IntSize(mViewportMetrics.viewportRectWidth,
-                                            mViewportMetrics.viewportRectHeight);
-
-        boolean windowSizeChanged = !mWindowSize.equals(newWindowSize);
-
-        if (!force && !windowSizeChanged) {
-            return;
-        }
-
-        mWindowSize = newWindowSize;
-
-        if (windowSizeChanged) {
-            Log.d(LOGTAG, "Window-size changed to " + mWindowSize);
-        }
-
-        if (mView != null) {
-            mView.notifySizeChanged(mWindowSize.width, mWindowSize.height);
-        }
-    }
-
     @WrapForJNI(calledFrom = "gecko")
     void contentDocumentChanged() {
         mContentDocumentIsDisplayed = false;
     }
 
     @WrapForJNI(calledFrom = "gecko")
     boolean isContentDocumentDisplayed() {
         return mContentDocumentIsDisplayed;
@@ -206,17 +148,17 @@ class GeckoLayerClient implements LayerV
     public void updateRootFrameMetrics(float scrollX, float scrollY, float zoom) {
         mViewportMetrics = mViewportMetrics.setViewportOrigin(scrollX, scrollY)
             .setZoomFactor(zoom);
 
         mToolbarAnimator.onMetricsChanged(mViewportMetrics);
         mContentDocumentIsDisplayed = true;
     }
 
-    class PointerInfo {
+    private static class PointerInfo {
         // We reserve one pointer ID for the mouse, so that tests don't have
         // to worry about tracking pointer IDs if they just want to test mouse
         // event synthesization. If somebody tries to use this ID for a
         // synthesized touch event we'll throw an exception.
         public static final int RESERVED_MOUSE_POINTER_ID = 100000;
 
         public int pointerId;
         public int source;
@@ -230,17 +172,17 @@ class GeckoLayerClient implements LayerV
             coords.orientation = orientation;
             coords.pressure = (float)pressure;
             coords.x = screenX;
             coords.y = screenY;
             return coords;
         }
     }
 
-    class SynthesizedEventState {
+    private static class SynthesizedEventState {
         public final ArrayList<PointerInfo> pointers;
         public long downTime;
 
         SynthesizedEventState() {
             pointers = new ArrayList<PointerInfo>();
         }
 
         int getPointerIndex(int pointerId) {
@@ -304,16 +246,21 @@ class GeckoLayerClient implements LayerV
     }
 
     private void synthesizeNativePointer(int source, int pointerId,
             int eventType, int screenX, int screenY, double pressure,
             int orientation)
     {
         Log.d(LOGTAG, "Synthesizing pointer from " + source + " id " + pointerId + " at " + screenX + ", " + screenY);
 
+        final int[] origin = new int[2];
+        mView.getLocationOnScreen(origin);
+        screenX -= origin[0];
+        screenY -= origin[1];
+
         if (mPointerState == null) {
             mPointerState = new SynthesizedEventState();
         }
 
         // Find the pointer if it already exists
         int pointerIndex = mPointerState.getPointerIndex(pointerId);
 
         // Event-specific handling
@@ -361,19 +308,16 @@ class GeckoLayerClient implements LayerV
                 }
                 break;
         }
 
         // Update the pointer with the new info
         PointerInfo info = mPointerState.pointers.get(pointerIndex);
         info.screenX = screenX;
         info.screenY = screenY;
-        if (mView != null) {
-            info.screenY += mView.getCurrentToolbarHeight();
-        }
         info.pressure = pressure;
         info.orientation = orientation;
 
         // Dispatch the event
         int action = 0;
         if (eventType == MotionEvent.ACTION_POINTER_DOWN ||
             eventType == MotionEvent.ACTION_POINTER_UP) {
             // for pointer-down and pointer-up events we need to add the
@@ -440,17 +384,17 @@ class GeckoLayerClient implements LayerV
         setViewportSize(viewportSize.width, viewportSize.height);
     }
 
     ImmutableViewportMetrics getViewportMetrics() {
         return mViewportMetrics;
     }
 
     Matrix getMatrixForLayerRectToViewRect() {
-        if (!mGeckoIsReady) {
+        if (!mView.isCompositorReady()) {
             return null;
         }
 
         ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
         PointF origin = viewportMetrics.getOrigin();
         float zoom = viewportMetrics.zoomFactor;
         ImmutableViewportMetrics geckoViewport = mViewportMetrics;
         PointF geckoOrigin = geckoViewport.getOrigin();
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
@@ -49,35 +49,25 @@ public class LayerView extends FrameLayo
 
     private static AccessibilityManager sAccessibilityManager;
 
     private GeckoLayerClient mLayerClient;
     private PanZoomController mPanZoomController;
     private DynamicToolbarAnimator mToolbarAnimator;
     private FullScreenState mFullScreenState;
 
-    private SurfaceView mSurfaceView;
-    private TextureView mTextureView;
-
     private Listener mListener;
 
     /* This should only be modified on the Java UI thread. */
     private final Overscroll mOverscroll;
 
-    private boolean mServerSurfaceValid;
-    private int mWidth, mHeight;
-
     private int mDefaultClearColor = Color.WHITE;
     /* package */ GetPixelsResult mGetPixelsResult;
     private final List<DrawListener> mDrawListeners;
 
-    /* This is written by the Gecko thread and the UI thread, and read by the UI thread. */
-    /* package */ volatile boolean mCompositorCreated;
-    /* package */ volatile boolean mCompositorControllerOpen;
-
     //
     // NOTE: These values are also defined in gfx/layers/ipc/UiCompositorControllerMessageTypes.h
     //       and must be kept in sync. Any new AnimatorMessageType added here must also be added there.
     //
     /* package */ final static int STATIC_TOOLBAR_NEEDS_UPDATE      = 0;  // Sent from compositor when the static toolbar wants to hide.
     /* package */ final static int STATIC_TOOLBAR_READY             = 1;  // Sent from compositor when the static toolbar image has been updated and is ready to animate.
     /* package */ final static int TOOLBAR_HIDDEN                   = 2;  // Sent to compositor when the real toolbar has been hidden.
     /* package */ final static int TOOLBAR_VISIBLE                  = 3;  // Sent to compositor when the real toolbar is visible.
@@ -85,127 +75,29 @@ public class LayerView extends FrameLayo
     /* package */ final static int FIRST_PAINT                      = 5;  // Sent from compositor after first paint
     /* package */ final static int REQUEST_SHOW_TOOLBAR_IMMEDIATELY = 6;  // Sent to compositor requesting toolbar be shown immediately
     /* package */ final static int REQUEST_SHOW_TOOLBAR_ANIMATED    = 7;  // Sent to compositor requesting toolbar be shown animated
     /* package */ final static int REQUEST_HIDE_TOOLBAR_IMMEDIATELY = 8;  // Sent to compositor requesting toolbar be hidden immediately
     /* package */ final static int REQUEST_HIDE_TOOLBAR_ANIMATED    = 9;  // Sent to compositor requesting toolbar be hidden animated
     /* package */ final static int LAYERS_UPDATED                   = 10; // Sent from compositor when a layer has been updated
     /* package */ final static int TOOLBAR_SNAPSHOT_FAILED          = 11; // Sent to compositor when the toolbar snapshot fails.
     /* package */ final static int COMPOSITOR_CONTROLLER_OPEN       = 20; // Special message sent from UiCompositorControllerChild once it is open
-    /* package */ final static int IS_COMPOSITOR_CONTROLLER_OPEN    = 21; // Special message sent from controller to query if the compositor controller is open
 
     private void postCompositorMessage(final int message) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 mCompositor.sendToolbarAnimatorMessage(message);
             }
         });
     }
 
-    @WrapForJNI(calledFrom = "ui")
     /* package */ boolean isCompositorReady() {
         ThreadUtils.assertOnUiThread();
-        return mCompositorCreated && mCompositorControllerOpen;
-    }
-
-    /* package */ class Compositor extends JNIObject {
-        public Compositor() {
-        }
-
-        /* package */ boolean isReady() {
-            return isCompositorReady();
-        }
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
-        @Override protected native void disposeNative();
-
-        // Gecko thread sets its Java instances; does not block UI thread.
-        @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
-        /* package */ native void attachToJava(GeckoLayerClient layerClient,
-                                               NativePanZoomController npzc);
-
-        @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
-        /* package */ native void onSizeChanged(int windowWidth, int windowHeight);
-
-        // Gecko thread creates compositor; blocks UI thread.
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "proxy")
-        /* package */ native void createCompositor(int width, int height, Object surface);
-
-        // Gecko thread pauses compositor; blocks UI thread.
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void syncPauseCompositor();
-
-        // UI thread resumes compositor and notifies Gecko thread; does not block UI thread.
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void syncResumeResizeCompositor(int width, int height, Object surface);
-
-        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
-        /* package */ native void syncInvalidateAndScheduleComposite();
-
-        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
-        /* package */ native void setMaxToolbarHeight(int height);
-
-        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
-        /* package */ native void setPinned(boolean pinned, int reason);
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void sendToolbarAnimatorMessage(int message);
-
-        @WrapForJNI(calledFrom = "ui")
-        /* package */ void recvToolbarAnimatorMessage(int message) {
-            handleToolbarAnimatorMessage(message);
-        }
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void setDefaultClearColor(int color);
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void requestScreenPixels();
-
-        @WrapForJNI(calledFrom = "ui")
-        /* package */ void recvScreenPixels(int width, int height, int[] pixels) {
-            if (mGetPixelsResult != null) {
-                mGetPixelsResult.onPixelsResult(width, height, IntBuffer.wrap(pixels));
-                mGetPixelsResult = null;
-            }
-        }
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void enableLayerUpdateNotifications(boolean enable);
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void sendToolbarPixelsToCompositor(final int width, final int height, final int[] pixels);
-
-        @WrapForJNI(calledFrom = "gecko")
-        private void reattach() {
-            ThreadUtils.postToUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    updateCompositor();
-                }
-            });
-        }
-
-        @WrapForJNI(calledFrom = "gecko")
-        private void destroy() {
-            // The nsWindow has been closed. First mark our compositor as destroyed.
-            LayerView.this.mCompositorCreated = false;
-            LayerView.this.mCompositorControllerOpen = false;
-
-            LayerView.this.mLayerClient.setGeckoReady(false);
-
-            // Then clear out any pending calls on the UI thread by disposing on the UI thread.
-            ThreadUtils.postToUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    LayerView.this.mDrawListeners.clear();
-                    disposeNative();
-                }
-            });
-        }
+        return mCompositor != null && mCompositor.isReady();
     }
 
     /* package */ void handleToolbarAnimatorMessage(int message) {
         switch (message) {
             case STATIC_TOOLBAR_NEEDS_UPDATE:
                 // Send updated toolbar image to compositor.
                 Bitmap bm = mToolbarAnimator.getBitmapOfToolbarChrome();
                 if (bm == null) {
@@ -238,62 +130,32 @@ public class LayerView extends FrameLayo
                 setSurfaceBackgroundColor(Color.TRANSPARENT);
                 break;
             case LAYERS_UPDATED:
                 for (DrawListener listener : mDrawListeners) {
                     listener.drawFinished();
                 }
                 break;
             case COMPOSITOR_CONTROLLER_OPEN:
-                // It is possible to get this message multiple times. Only act on it if we didn't know the compositor controller was open
-                if (mCompositorControllerOpen) {
-                    break;
-                }
-                mCompositorControllerOpen = true;
-                // updateCompositor makes a synchronous call to the compositor which will dead lock if called directly from here
                 ThreadUtils.postToUiThread(new Runnable() {
                     @Override
                     public void run() {
                         mCompositor.setDefaultClearColor(mDefaultClearColor);
                         mCompositor.enableLayerUpdateNotifications(!mDrawListeners.isEmpty());
                         mToolbarAnimator.updateCompositor();
-                        updateCompositor();
                     }
                 });
                 break;
             default:
                 Log.e(LOGTAG, "Unhandled Toolbar Animator Message: " + message);
                 break;
         }
     }
 
-    private final Compositor mCompositor = new Compositor();
-
-    public boolean shouldUseTextureView() {
-        // Disable TextureView support for now as it causes panning/zooming
-        // performance regressions (see bug 792259). Uncomment the code below
-        // once this bug is fixed.
-        return false;
-
-        /*
-        // we can only use TextureView on ICS or higher
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            Log.i(LOGTAG, "Not using TextureView: not on ICS+");
-            return false;
-        }
-
-        try {
-            // and then we can only use it if we have a hardware accelerated window
-            Method m = View.class.getMethod("isHardwareAccelerated", (Class[]) null);
-            return (Boolean) m.invoke(this);
-        } catch (Exception e) {
-            Log.i(LOGTAG, "Not using TextureView: caught exception checking for hw accel: " + e.toString());
-            return false;
-        } */
-    }
+    private LayerSession.Compositor mCompositor;
 
     public LayerView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         mFullScreenState = FullScreenState.NONE;
 
         mOverscroll = new OverscrollEdgeEffect(this);
         mDrawListeners = new ArrayList<DrawListener>();
@@ -306,20 +168,16 @@ public class LayerView extends FrameLayo
     public void initializeView() {
         mLayerClient = new GeckoLayerClient(this);
         if (mOverscroll != null) {
             mLayerClient.setOverscrollHandler(mOverscroll);
         }
 
         mPanZoomController = mLayerClient.getPanZoomController();
         mToolbarAnimator = mLayerClient.getDynamicToolbarAnimator();
-        mToolbarAnimator.notifyCompositorCreated(mCompositor);
-
-        setFocusable(true);
-        setFocusableInTouchMode(true);
     }
 
     /**
      * MotionEventHelper dragAsync() robocop tests can instruct
      * PanZoomController not to generate longpress events.
      * This call comes in from a thread other than the UI thread.
      * So dispatch to UI thread first to prevent assert in nsWindow.
      */
@@ -332,26 +190,16 @@ public class LayerView extends FrameLayo
         });
     }
 
     private static Point getEventRadius(MotionEvent event) {
         return new Point((int)event.getToolMajor() / 2,
                          (int)event.getToolMinor() / 2);
     }
 
-    public void showSurface() {
-        // Fix this if TextureView support is turned back on above
-        mSurfaceView.setVisibility(View.VISIBLE);
-    }
-
-    public void hideSurface() {
-        // Fix this if TextureView support is turned back on above
-        mSurfaceView.setVisibility(View.INVISIBLE);
-    }
-
     public void destroy() {
         if (mLayerClient != null) {
             mLayerClient.destroy();
         }
     }
 
     @Override
     public void dispatchDraw(final Canvas canvas) {
@@ -364,17 +212,17 @@ public class LayerView extends FrameLayo
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             requestFocus();
         }
 
-        if (!mLayerClient.isGeckoReady()) {
+        if (!isCompositorReady()) {
             // If gecko isn't loaded yet, don't try sending events to the
             // native code because it's just going to crash
             return true;
         }
         if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) {
             return true;
         }
         return false;
@@ -393,109 +241,58 @@ public class LayerView extends FrameLayo
     public boolean onHoverEvent(MotionEvent event) {
         // If we get a touchscreen hover event, and accessibility is not enabled,
         // don't send it to gecko.
         if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN &&
             !isAccessibilityEnabled()) {
             return false;
         }
 
-        if (!mLayerClient.isGeckoReady()) {
+        if (!isCompositorReady()) {
             // If gecko isn't loaded yet, don't try sending events to the
             // native code because it's just going to crash
             return true;
         } else if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
             return true;
         }
 
         return false;
     }
 
     @Override
     public boolean onGenericMotionEvent(MotionEvent event) {
         if (AndroidGamepadManager.handleMotionEvent(event)) {
             return true;
         }
-        if (!mLayerClient.isGeckoReady()) {
+        if (!isCompositorReady()) {
             // If gecko isn't loaded yet, don't try sending events to the
             // native code because it's just going to crash
             return true;
         }
         if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
             return true;
         }
         return false;
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        // We are adding descendants to this LayerView, but we don't want the
-        // descendants to affect the way LayerView retains its focus.
-        setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
-
-        // This check should not be done before the view is attached to a window
-        // as hardware acceleration will not be enabled at that point.
-        // We must create and add the SurfaceView instance before the view tree
-        // is fully created to avoid flickering (see bug 801477).
-        if (shouldUseTextureView()) {
-            mTextureView = new TextureView(getContext());
-            mTextureView.setSurfaceTextureListener(new SurfaceTextureListener());
-
-            // The background is set to this color when the LayerView is
-            // created, and it will be shown immediately at startup. Shortly
-            // after, the tab's background color will be used before any content
-            // is shown.
-            mTextureView.setBackgroundColor(Color.WHITE);
-            addView(mTextureView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-        } else {
-            // This will stop PropertyAnimator from creating a drawing cache (i.e. a bitmap)
-            // from a SurfaceView, which is just not possible (the bitmap will be transparent).
-            setWillNotCacheDrawing(false);
-
-            mSurfaceView = new LayerSurfaceView(getContext(), this);
-            mSurfaceView.setBackgroundColor(Color.WHITE);
-            addView(mSurfaceView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
-            SurfaceHolder holder = mSurfaceView.getHolder();
-            holder.addCallback(new SurfaceListener());
-        }
-
-        attachCompositor();
-    }
-
     // Don't expose GeckoLayerClient to things outside this package; only expose it as an Object
     GeckoLayerClient getLayerClient() { return mLayerClient; }
 
-    /* package */ boolean isGeckoReady() {
-        return mLayerClient.isGeckoReady();
-    }
-
     public PanZoomController getPanZoomController() { return mPanZoomController; }
     public DynamicToolbarAnimator getDynamicToolbarAnimator() { return mToolbarAnimator; }
 
     public ImmutableViewportMetrics getViewportMetrics() {
         return mLayerClient.getViewportMetrics();
     }
 
     public Matrix getMatrixForLayerRectToViewRect() {
         return mLayerClient.getMatrixForLayerRectToViewRect();
     }
 
     public void setSurfaceBackgroundColor(int newColor) {
-        if (mSurfaceView != null) {
-            mSurfaceView.setBackgroundColor(newColor);
-        }
-    }
-
-    public void requestRender() {
-        if (isCompositorReady()) {
-            mCompositor.syncInvalidateAndScheduleComposite();
-        }
     }
 
     public interface GetPixelsResult {
         public void onPixelsResult(int width, int height, IntBuffer pixels);
     }
 
     @RobocopTarget
     public void getPixels(final GetPixelsResult getPixelsResult) {
@@ -512,236 +309,68 @@ public class LayerView extends FrameLayo
         if (isCompositorReady()) {
             mGetPixelsResult = getPixelsResult;
             mCompositor.requestScreenPixels();
         } else {
             getPixelsResult.onPixelsResult(0, 0, null);
         }
     }
 
+    /* package */ void recvScreenPixels(int width, int height, int[] pixels) {
+        if (mGetPixelsResult != null) {
+            mGetPixelsResult.onPixelsResult(width, height, IntBuffer.wrap(pixels));
+            mGetPixelsResult = null;
+        }
+    }
+
     public void setListener(Listener listener) {
         mListener = listener;
     }
 
     Listener getListener() {
         return mListener;
     }
 
-    private void attachCompositor() {
+    protected void attachCompositor(final LayerSession session) {
+        mCompositor = session.mCompositor;
+        mCompositor.layerView = this;
+
+        mToolbarAnimator.notifyCompositorCreated(mCompositor);
+
         final NativePanZoomController npzc = (NativePanZoomController) mPanZoomController;
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             mCompositor.attachToJava(mLayerClient, npzc);
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mCompositor, "attachToJava",
                     GeckoLayerClient.class, mLayerClient,
                     NativePanZoomController.class, npzc);
         }
     }
 
     @WrapForJNI(calledFrom = "ui")
-    protected Object getCompositor() {
-        return mCompositor;
-    }
-
-    void serverSurfaceChanged(int newWidth, int newHeight) {
-        ThreadUtils.assertOnUiThread();
-
-        mWidth = newWidth;
-        mHeight = newHeight;
-        mServerSurfaceValid = true;
-
-        updateCompositor();
+    private Object getCompositor() {
+        return isCompositorReady() ? mCompositor : null;
     }
 
-    void updateCompositor() {
-        ThreadUtils.assertOnUiThread();
-
-        if (isCompositorReady()) {
-            // If the compositor has already been created, just resume it instead. We don't need
-            // to block here because if the surface is destroyed before the compositor grabs it,
-            // we can handle that gracefully (i.e. the compositor will remain paused).
-            if (!mServerSurfaceValid) {
-                return;
-            }
-            // Asking Gecko to resume the compositor takes too long (see
-            // https://bugzilla.mozilla.org/show_bug.cgi?id=735230#c23), so we
-            // resume the compositor directly. We still need to inform Gecko about
-            // the compositor resuming, so that Gecko knows that it can now draw.
-            // It is important to not notify Gecko until after the compositor has
-            // been resumed, otherwise Gecko may send updates that get dropped.
-            mCompositor.syncResumeResizeCompositor(mWidth, mHeight, getSurface());
-            return;
-        }
-
-        // Only try to create the compositor if we have a valid surface and gecko is up. When these
-        // two conditions are satisfied, we can be relatively sure that the compositor creation will
-        // happen without needing to block anywhere.
-        if (!mCompositorCreated && mServerSurfaceValid && getLayerClient().isGeckoReady()) {
-            mCompositorCreated = true;
-            mCompositor.createCompositor(mWidth, mHeight, getSurface());
-        }
-
-        if (mCompositorCreated && !mCompositorControllerOpen) {
-            mCompositor.sendToolbarAnimatorMessage(IS_COMPOSITOR_CONTROLLER_OPEN);
-        }
-    }
-
-    /* When using a SurfaceView (mSurfaceView != null), resizing happens in two
-     * phases. First, the LayerView changes size, then, often some frames later,
-     * the SurfaceView changes size. Because of this, we need to split the
-     * resize into two phases to avoid jittering.
-     *
-     * The first phase is the LayerView size change. mListener is notified so
-     * that a synchronous draw can be performed (otherwise a blank frame will
-     * appear).
-     *
-     * The second phase is the SurfaceView size change. At this point, the
-     * backing GL surface is resized and another synchronous draw is performed.
-     * Gecko is also sent the new window size, and this will likely cause an
-     * extra draw a few frames later, after it's re-rendered and caught up.
-     *
-     * In the case that there is no valid GL surface (for example, when
-     * resuming, or when coming back from the awesomescreen), or we're using a
-     * TextureView instead of a SurfaceView, the first phase is skipped.
-     */
-    private void onSizeChanged(int width, int height) {
-        if (!mServerSurfaceValid || mSurfaceView == null) {
-            surfaceChanged(width, height);
-            return;
-        }
-
-        if (isCompositorReady()) {
-            mCompositor.syncResumeResizeCompositor(width, height, getSurface());
-        }
-
-        if (mOverscroll != null) {
-            mOverscroll.setSize(width, height);
-        }
-    }
-
-    private void surfaceChanged(int width, int height) {
-        serverSurfaceChanged(width, height);
-
+    /* package */ void onSizeChanged(int width, int height) {
         if (mListener != null) {
             mListener.surfaceChanged();
         }
 
         if (mOverscroll != null) {
             mOverscroll.setSize(width, height);
         }
     }
 
-    void notifySizeChanged(int windowWidth, int windowHeight) {
-        mCompositor.onSizeChanged(windowWidth, windowHeight);
-    }
-
-    void serverSurfaceDestroyed() {
-        ThreadUtils.assertOnUiThread();
-
-        // We need to coordinate with Gecko when pausing composition, to ensure
-        // that Gecko never executes a draw event while the compositor is paused.
-        // This is sent synchronously to make sure that we don't attempt to use
-        // any outstanding Surfaces after we call this (such as from a
-        // serverSurfaceDestroyed notification), and to make sure that any in-flight
-        // Gecko draw events have been processed.  When this returns, composition is
-        // definitely paused -- it'll synchronize with the Gecko event loop, which
-        // in turn will synchronize with the compositor thread.
-        if (isCompositorReady()) {
-            mCompositor.syncPauseCompositor();
-        }
-
-        mServerSurfaceValid = false;
-    }
-
-    private void onDestroyed() {
-        serverSurfaceDestroyed();
-    }
-
-    public Object getNativeWindow() {
-        if (mSurfaceView != null)
-            return mSurfaceView.getHolder();
-
-        return mTextureView.getSurfaceTexture();
-    }
-
-    public Object getSurface() {
-      if (mSurfaceView != null) {
-        return mSurfaceView.getHolder().getSurface();
-      }
-      return null;
-    }
-
     public interface Listener {
         void surfaceChanged();
     }
 
-    private class SurfaceListener implements SurfaceHolder.Callback {
-        @Override
-        public void surfaceChanged(SurfaceHolder holder, int format, int width,
-                                                int height) {
-            onSizeChanged(width, height);
-        }
-
-        @Override
-        public void surfaceCreated(SurfaceHolder holder) {
-        }
-
-        @Override
-        public void surfaceDestroyed(SurfaceHolder holder) {
-            onDestroyed();
-        }
-    }
-
-    /* A subclass of SurfaceView to listen to layout changes, as
-     * View.OnLayoutChangeListener requires API level 11.
-     */
-    private class LayerSurfaceView extends SurfaceView {
-        private LayerView mParent;
-
-        public LayerSurfaceView(Context context, LayerView parent) {
-            super(context);
-            mParent = parent;
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-            super.onLayout(changed, left, top, right, bottom);
-            if (changed && mParent.mServerSurfaceValid) {
-                mParent.surfaceChanged(right - left, bottom - top);
-            }
-        }
-    }
-
-    private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
-        @Override
-        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
-            // We don't do this for surfaceCreated above because it is always followed by a surfaceChanged,
-            // but that is not the case here.
-            onSizeChanged(width, height);
-        }
-
-        @Override
-        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
-            onDestroyed();
-            return true; // allow Android to call release() on the SurfaceTexture, we are done drawing to it
-        }
-
-        @Override
-        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
-            onSizeChanged(width, height);
-        }
-
-        @Override
-        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
-
-        }
-    }
-
     @RobocopTarget
     public void addDrawListener(final DrawListener listener) {
         if (!ThreadUtils.isOnUiThread()) {
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     addDrawListener(listener);
                 }
@@ -770,16 +399,20 @@ public class LayerView extends FrameLayo
 
         boolean notEmpty = mDrawListeners.isEmpty();
         mDrawListeners.remove(listener);
         if (isCompositorReady() && notEmpty && mDrawListeners.isEmpty()) {
             mCompositor.enableLayerUpdateNotifications(false);
         }
     }
 
+    /* package */ void clearDrawListeners() {
+        mDrawListeners.clear();
+    }
+
     @RobocopTarget
     public static interface DrawListener {
         public void drawFinished();
     }
 
     public float getZoomFactor() {
         return getLayerClient().getViewportMetrics().zoomFactor;
     }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -181,17 +181,17 @@ class NativePanZoomController extends JN
             return handleMouseEvent(event);
         } else {
             return false;
         }
     }
 
     @Override @WrapForJNI(calledFrom = "ui") // PanZoomController
     public void destroy() {
-        if (mDestroyed || !mView.isGeckoReady()) {
+        if (mDestroyed || !mView.isCompositorReady()) {
             return;
         }
         mDestroyed = true;
         disposeNative();
     }
 
     @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority") @Override // JNIObject
     protected native void disposeNative();