Bug 1415994 - 7. Migrate existing gfx code to use LayerSession; r=snorp
authorJim Chen <nchen@mozilla.com>
Tue, 14 Nov 2017 18:18:35 -0500
changeset 443560 e90da05fbff4d1e741845f70b65dce2ba955b89a
parent 443559 f0e9749cfce5b15fa56acff72178901c2c3c36d8
child 443561 a4cef03e1c9e16e00b37faa0d2455b9b954f11c4
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1415994
milestone59.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 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();