Index: chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java |
deleted file mode 100644 |
index 0fe5e71d28af3dd1fccc9495d07f05d117e16063..0000000000000000000000000000000000000000 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManager.java |
+++ /dev/null |
@@ -1,455 +0,0 @@ |
-// Copyright 2017 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-package org.chromium.chrome.browser.compositor; |
- |
-import android.content.Context; |
-import android.graphics.PixelFormat; |
-import android.graphics.drawable.Drawable; |
-import android.view.SurfaceHolder; |
-import android.view.SurfaceView; |
-import android.view.View; |
-import android.view.ViewGroup; |
-import android.widget.FrameLayout; |
- |
-/** |
- * Manage multiple SurfaceViews for the compositor, so that transitions between |
- * surfaces with and without an alpha channel can be visually smooth. |
- * |
- * This class allows a client to request a 'translucent' or 'opaque' surface, and we will signal via |
- * SurfaceHolder.Callback when it's ready. We guarantee that the client will receive surfaceCreated |
- * / surfaceDestroyed only for a surface that represents the most recently requested PixelFormat. |
- * |
- * Internally, we maintain two SurfaceViews, since calling setFormat() to change the PixelFormat |
- * results in a visual glitch as the surface is torn down. crbug.com/679902 |
- * |
- * The client has the responsibility to call doneWithUnownedSurface() at some point between when we |
- * call back its surfaceCreated, when it is safe for us to hide the SurfaceView with the wrong |
- * format. It is okay if it requests multiple surfaces without calling doneWithUnownedSurface. |
- * |
- * If the client requests the same format more than once in a row, it will still receive destroyed / |
- * created / changed messages for it, even though we won't tear it down. |
- * |
- * The full design doc is at https://goo.gl/aAmQzR . |
- */ |
-class CompositorSurfaceManager implements SurfaceHolder.Callback { |
- private static class SurfaceState { |
- public SurfaceView surfaceView; |
- |
- // Do we expect a surfaceCreated? |
- public boolean createPending; |
- |
- // Have we started destroying |surfaceView|, but haven't received surfaceDestroyed yet? |
- public boolean destroyPending; |
- |
- // Last PixelFormat that we received, or UNKNOWN if we don't know / don't want to cache it. |
- public int format; |
- |
- // Last known width, height for thsi surface. |
- public int width; |
- public int height; |
- |
- // Parent ViewGroup, or null. |
- private ViewGroup mParent; |
- |
- public SurfaceState(Context context, int format, SurfaceHolder.Callback callback) { |
- surfaceView = new SurfaceView(context); |
- surfaceView.setZOrderMediaOverlay(true); |
- surfaceView.setVisibility(View.VISIBLE); |
- surfaceHolder().setFormat(format); |
- surfaceHolder().addCallback(callback); |
- |
- // Set this to UNKNOWN until we get a format back. |
- this.format = PixelFormat.UNKNOWN; |
- } |
- |
- public SurfaceHolder surfaceHolder() { |
- return surfaceView.getHolder(); |
- } |
- |
- public boolean isValid() { |
- return surfaceHolder().getSurface().isValid(); |
- } |
- |
- // Attach to |parent|, such that isAttached() will be correct immediately. Otherwise, |
- // attaching and detaching can cause surfaceCreated / surfaceDestroyed callbacks without |
- // View.hasParent being up to date. |
- public void attachTo(ViewGroup parent, FrameLayout.LayoutParams lp) { |
- mParent = parent; |
- mParent.addView(surfaceView, lp); |
- } |
- |
- public void detachFromParent() { |
- final ViewGroup parent = mParent; |
- // Since removeView can call surfaceDestroyed before returning, be sure that isAttached |
- // will return false. |
- mParent = null; |
- parent.removeView(surfaceView); |
- } |
- |
- public boolean isAttached() { |
- return mParent != null; |
- } |
- } |
- |
- // SurfaceView with a translucent PixelFormat. |
- private final SurfaceState mTranslucent; |
- |
- // SurfaceView with an opaque PixelFormat. |
- private final SurfaceState mOpaque; |
- |
- // Surface that we last gave to the client with surfaceCreated. Cleared when we call |
- // surfaceDestroyed on |mClient|. Note that it's not necessary that Android has notified us |
- // the surface has been destroyed; we deliberately keep it around until the client tells us that |
- // it's okay to get rid of it. |
- private SurfaceState mOwnedByClient; |
- |
- // Surface that was most recently requested by the client. |
- private SurfaceState mRequestedByClient; |
- |
- // Client that we notify about surface change events. |
- private SurfaceHolder.Callback mClient; |
- |
- // View to which we'll attach the SurfaceView. |
- private final ViewGroup mParentView; |
- |
- public CompositorSurfaceManager(ViewGroup parentView, SurfaceHolder.Callback client) { |
- mParentView = parentView; |
- mClient = client; |
- |
- mTranslucent = new SurfaceState(parentView.getContext(), PixelFormat.TRANSLUCENT, this); |
- mOpaque = new SurfaceState(mParentView.getContext(), PixelFormat.OPAQUE, this); |
- } |
- |
- /** |
- * Turn off everything. |
- */ |
- public void shutDown() { |
- mTranslucent.surfaceHolder().removeCallback(this); |
- mOpaque.surfaceHolder().removeCallback(this); |
- } |
- |
- /** |
- * Called by the client to request a surface. Once called, we guarantee that the next call to |
- * surfaceCreated will match the most recent value of |format|. If the surface is already |
- * available for use, then we'll send synthetic callbacks as though it were destroyed and |
- * recreated. Note that |format| must be either OPAQUE or TRANSLUCENT. |
- */ |
- public void requestSurface(int format) { |
- mRequestedByClient = (format == PixelFormat.TRANSLUCENT) ? mTranslucent : mOpaque; |
- |
- // If destruction is pending, then we must wait for it to complete. When we're notified |
- // that it is destroyed, we'll re-start construction if the client still wants this surface. |
- // Note that we could send a surfaceDestroyed for the owned surface, if there is one, but we |
- // defer it until later so that the client can still use it until the new one is ready. |
- if (mRequestedByClient.destroyPending) return; |
- |
- // The requested surface isn't being torn down. |
- |
- // If the surface isn't attached yet, then attach it. Otherwise, we're still waiting for |
- // the surface to be created, or we've already received surfaceCreated for it. |
- if (!mRequestedByClient.isAttached()) { |
- attachSurfaceNow(mRequestedByClient); |
- assert mRequestedByClient.isAttached(); |
- return; |
- } |
- |
- // Surface is not pending destroy, and is attached. See if we need to send any synthetic |
- // callbacks to the client. If we're expecting a callback from Android, then we'll handle |
- // it when it arrives instead. |
- if (mRequestedByClient.createPending) return; |
- |
- // Surface is attached and no create is pending. Send a synthetic create. Note that, if |
- // Android destroyed the surface itself, then we'd have set |createPending| at that point. |
- // We don't check |isValid| here, since, technically, there could be a destroy in flight |
- // from Android. It's okay; we'll just notify the client at that point. Either way, we |
- // must tell the client that it now owns the surface. |
- |
- // Send a notification about any owned surface. Note that this can be |mRequestedByClient| |
- // which is fine. We'll send destroy / create for it. Also note that we don't actually |
- // start tear-down of the owned surface; the client notifies us via doneWithUnownedSurface |
- // when it is safe to do that. |
- disownClientSurface(mOwnedByClient); |
- |
- // The client now owns |mRequestedByClient|. Notify it that it's ready. |
- mOwnedByClient = mRequestedByClient; |
- mClient.surfaceCreated(mOwnedByClient.surfaceHolder()); |
- |
- // See if we're expecting a surfaceChanged. If not, then send a synthetic one. |
- if (mOwnedByClient.format != PixelFormat.UNKNOWN) { |
- mClient.surfaceChanged(mOwnedByClient.surfaceHolder(), mOwnedByClient.width, |
- mOwnedByClient.height, mOwnedByClient.format); |
- } |
- } |
- |
- /** |
- * Called to notify us that the client no longer needs the surface that it doesn't own. This |
- * tells us that we may destroy it. Note that it's okay if it never had an unowned surface. |
- */ |
- public void doneWithUnownedSurface() { |
- if (mOwnedByClient == null) return; |
- |
- SurfaceState unowned = (mOwnedByClient == mTranslucent) ? mOpaque : mTranslucent; |
- |
- if (mRequestedByClient == unowned) { |
- // Client is giving us back a surface that it's since requested but hasn't gotten yet. |
- // Do nothing. It will be notified when the new surface is ready, and it can call us |
- // again for the other surface, if it wants. |
- return; |
- } |
- |
- // Start destruction of this surface. To prevent recursive call-backs to the client, we |
- // post this for later. |
- detachSurfaceLater(unowned); |
- } |
- |
- /** |
- * Return the currently owned SurfaceHolder, if any. |
- */ |
- public SurfaceHolder getHolder() { |
- return mOwnedByClient != null ? mOwnedByClient.surfaceHolder() : null; |
- } |
- |
- /** |
- * Destroy and re-create the surface. Useful for a JB workaround needed by CompositorView. |
- */ |
- public void recreateSurfaceForJellyBean() { |
- // If they don't have a surface, then they'll get a new one anyway. |
- if (mOwnedByClient == null) return; |
- |
- // Notify the client that it no longer owns this surface, then destroy it. When destruction |
- // completes, we will recreate it automatically, since it will look like the client since |
- // re-requested it. That's why we send surfaceDestroyed here rather than letting our |
- // surfaceDestroyed do it when destruction completes. If we just started destruction while |
- // the client still owns the surface, then our surfaceDestroyed would assume that Android |
- // initiated the destruction, and wait for Android to recreate it. |
- |
- mParentView.post(new Runnable() { |
- @Override |
- public void run() { |
- if (mOwnedByClient == null) return; |
- SurfaceState owned = mOwnedByClient; |
- mClient.surfaceDestroyed(mOwnedByClient.surfaceHolder()); |
- mOwnedByClient = null; |
- detachSurfaceNow(owned); |
- } |
- }); |
- } |
- |
- @Override |
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
- SurfaceState state = getStateForHolder(holder); |
- assert state != null; |
- |
- // If this is the surface that the client currently cares about, then notify the client. |
- // Note that surfaceChanged is guaranteed to come only after surfaceCreated. Also, if the |
- // client has requested a different surface but hasn't gotten it yet, then skip this. |
- if (state == mOwnedByClient && state == mRequestedByClient) { |
- state.width = width; |
- state.height = height; |
- state.format = format; |
- mClient.surfaceChanged(holder, format, width, height); |
- } |
- } |
- |
- @Override |
- public void surfaceCreated(SurfaceHolder holder) { |
- SurfaceState state = getStateForHolder(holder); |
- assert state != null; |
- // Note that |createPending| might not be set, if Android destroyed and recreated this |
- // surface on its own. |
- |
- if (state != mRequestedByClient) { |
- // Surface is created, but it's not the one that's been requested most recently. Just |
- // destroy it again. |
- detachSurfaceLater(state); |
- return; |
- } |
- |
- // No create is pending. |
- state.createPending = false; |
- |
- // A surfaceChanged should arrive. |
- state.format = PixelFormat.UNKNOWN; |
- |
- // The client requested a surface, and it's now available. If the client owns a surface, |
- // then notify it that it doesn't. Note that the client can't own |state| at this point, |
- // since we would have removed ownership when we got surfaceDestroyed. It's okay if the |
- // client doesn't own either surface. |
- assert mOwnedByClient != state; |
- disownClientSurface(mOwnedByClient); |
- |
- // The client now owns this surface, so notify it. |
- mOwnedByClient = mRequestedByClient; |
- mClient.surfaceCreated(mOwnedByClient.surfaceHolder()); |
- } |
- |
- @Override |
- public void surfaceDestroyed(SurfaceHolder holder) { |
- SurfaceState state = getStateForHolder(holder); |
- assert state != null; |
- |
- // If no destroy is pending, then Android chose to destroy this surface and will, hopefully, |
- // re-create it at some point. Otherwise, a destroy is either posted or has already |
- // detached this SurfaceView. If it's already detached, then the destruction is complete |
- // and we can clear |destroyPending|. Otherwise, Android has destroyed this surface while |
- // our destroy was posted, and might even return it before it runs. When the post runs, it |
- // can sort that out based on whether the surface is valid or not. |
- if (!state.destroyPending) { |
- state.createPending = true; |
- } else if (!state.isAttached()) { |
- state.destroyPending = false; |
- } |
- |
- state.format = PixelFormat.UNKNOWN; |
- |
- // If the client owns this surface, then notify it synchronously that it no longer does. |
- // This can happen if Android destroys the surface on its own. It's also possible that |
- // we've detached it, if a destroy was pending. Either way, notify the client. |
- if (state == mOwnedByClient) { |
- disownClientSurface(mOwnedByClient); |
- |
- // Do not re-request the surface here. If android gives the surface back, then we'll |
- // re-signal the client about construction. |
- return; |
- } |
- |
- // The client doesn't own this surface, but might want it. |
- // If the client has requested this surface, then start construction on it. The client will |
- // be notified when it completes. This can happen if the client re-requests a surface after |
- // we start destruction on it from a previous request, for example. We post this for later, |
- // since we might be called while removing |state| from the view tree. In general, posting |
- // from here is good. |
- if (state == mRequestedByClient && !state.isAttached()) { |
- attachSurfaceLater(state); |
- } else if (state != mRequestedByClient && state.isAttached()) { |
- // This isn't the requested surface. If android destroyed it, then also unhook it so |
- // that it isn't recreated later. |
- detachSurfaceLater(state); |
- } |
- } |
- |
- /** |
- * Update the background drawable on all surfaces. |
- */ |
- public void setBackgroundDrawable(Drawable background) { |
- mTranslucent.surfaceView.setBackgroundDrawable(background); |
- mOpaque.surfaceView.setBackgroundDrawable(background); |
- } |
- |
- /** |
- * Set |willNotDraw| on all surfaces. |
- */ |
- public void setWillNotDraw(boolean willNotDraw) { |
- mTranslucent.surfaceView.setWillNotDraw(willNotDraw); |
- mOpaque.surfaceView.setWillNotDraw(willNotDraw); |
- } |
- |
- /** |
- * Return the SurfaceState for |holder|, or null if it isn't either. |
- */ |
- private SurfaceState getStateForHolder(SurfaceHolder holder) { |
- if (mTranslucent.surfaceHolder() == holder) return mTranslucent; |
- |
- if (mOpaque.surfaceHolder() == holder) return mOpaque; |
- |
- return null; |
- } |
- |
- /** |
- * Attach |state| to |mParentView| immedaitely. |
- */ |
- private void attachSurfaceNow(SurfaceState state) { |
- if (state.isAttached()) return; |
- |
- // If there is a destroy in-flight for this surface, then do nothing. |
- if (state.destroyPending) return; |
- |
- state.createPending = true; |
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( |
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); |
- state.attachTo(mParentView, lp); |
- mParentView.bringChildToFront(state.surfaceView); |
- mParentView.postInvalidateOnAnimation(); |
- } |
- |
- /** |
- * Post a Runnable to attach |state|. This is helpful, since one cannot directly interact with |
- * the View heirarchy during Surface callbacks. |
- */ |
- private void attachSurfaceLater(final SurfaceState state) { |
- // We shouldn't try to post construction if there's an in-flight destroy. |
- assert !state.destroyPending; |
- state.createPending = true; |
- |
- mParentView.post(new Runnable() { |
- @Override |
- public void run() { |
- attachSurfaceNow(state); |
- } |
- }); |
- } |
- |
- /** |
- * Cause the client to disown |state| if it currently owns it. This involves notifying it that |
- * the surface has been destroyed (recall that ownership involves getting created). It's okay |
- * if |state| is null or isn't owned by the client. |
- */ |
- private void disownClientSurface(SurfaceState state) { |
- if (mOwnedByClient != state || state == null) return; |
- |
- mClient.surfaceDestroyed(mOwnedByClient.surfaceHolder()); |
- mOwnedByClient = null; |
- } |
- |
- /** |
- * Detach |state| from |mParentView| immediately. |
- */ |
- private void detachSurfaceNow(SurfaceState state) { |
- // If we're called while we're not attached, then do nothing. This makes it easier for the |
- // client, since it doesn't have to keep track of whether the outgoing surface has been |
- // destroyed or not. The client will be notified (or has already) when the surface is |
- // destroyed, if it currently owns it. |
- if (state.isAttached()) { |
- // We are attached. If the surface is not valid, then Android has destroyed it for some |
- // other reason, and we should clean up. Otherwise, just wait for Android to finish. |
- final boolean valid = state.isValid(); |
- |
- // If the surface is valid, then we expect a callback to surfaceDestroyed eventually. |
- state.destroyPending = valid; |
- |
- // Note that this might call back surfaceDestroyed before returning! |
- state.detachFromParent(); |
- |
- // If the surface was valid before, then we expect a surfaceDestroyed callback, which |
- // might have arrived during removeView. Either way, that callback will finish cleanup |
- // of |state|. |
- if (valid) return; |
- } |
- |
- // The surface isn't attached, or was attached but wasn't currently valid. Either way, |
- // we're not going to get a destroy, so notify the client now if needed. |
- disownClientSurface(state); |
- |
- // If the client has since re-requested the surface, then start construction. |
- if (state == mRequestedByClient) attachSurfaceNow(mRequestedByClient); |
- } |
- |
- /** |
- * Post detachment of |state|. This is safe during Surface callbacks. |
- */ |
- private void detachSurfaceLater(final SurfaceState state) { |
- // If |state| is not attached, then do nothing. There might be a destroy pending from |
- // Android, but in any case leave it be. |
- if (!state.isAttached()) return; |
- |
- state.destroyPending = true; |
- mParentView.post(new Runnable() { |
- @Override |
- public void run() { |
- detachSurfaceNow(state); |
- } |
- }); |
- } |
-} |