Commit 0267a42c authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

Set overlay mode opacity hint properly

The compositor does not use an alpha channel on its surface when it does
not need to, for power efficiency reasons. AndroidOverlays require the
compositor's surface to not be opaque, in order to draw a transparent
quad, through which the AO surface (and video) can be seen. We are
calling this transparency mode 'overlay mode'.

This CL adds the necessary plumbing to let WebContents embedders know
when to enter overlay mode. It also adds overlay mode support to
content shell and Chrome.

Bug: 710186
Change-Id: Ib873b553e7cda8edafc535ed730c18d3e8d44b2a
Reviewed-on: https://chromium-review.googlesource.com/567773
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: default avatarCharlie Reis <creis@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Reviewed-by: default avatarBo Liu <boliu@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#491307}
parent 74b2febb
...@@ -1634,6 +1634,15 @@ public abstract class ChromeActivity extends AsyncInitializationActivity ...@@ -1634,6 +1634,15 @@ public abstract class ChromeActivity extends AsyncInitializationActivity
return mFullscreenManager; return mFullscreenManager;
} }
/**
* Sets the overlay mode.
* Overlay mode means that we are currently using AndroidOverlays to display video, and
* that the compositor's surface should support alpha and not be marked as opaque.
*/
public void setOverlayMode(boolean useOverlayMode) {
mCompositorViewHolder.setOverlayMode(useOverlayMode);
}
/** /**
* @return The content offset provider, may be null. * @return The content offset provider, may be null.
*/ */
......
...@@ -73,7 +73,6 @@ import java.util.List; ...@@ -73,7 +73,6 @@ import java.util.List;
public class CompositorViewHolder extends FrameLayout public class CompositorViewHolder extends FrameLayout
implements ContentOffsetProvider, LayoutManagerHost, LayoutRenderHost, Invalidator.Host, implements ContentOffsetProvider, LayoutManagerHost, LayoutRenderHost, Invalidator.Host,
FullscreenListener { FullscreenListener {
private boolean mIsKeyboardShowing; private boolean mIsKeyboardShowing;
private final Invalidator mInvalidator = new Invalidator(); private final Invalidator mInvalidator = new Invalidator();
...@@ -481,6 +480,15 @@ public class CompositorViewHolder extends FrameLayout ...@@ -481,6 +480,15 @@ public class CompositorViewHolder extends FrameLayout
} }
} }
/**
* Sets the overlay mode.
*/
public void setOverlayMode(boolean useOverlayMode) {
if (mCompositorView != null) {
mCompositorView.setOverlayVideoMode(useOverlayMode);
}
}
private void setContentViewMotionEventOffsets(MotionEvent e, boolean canClear) { private void setContentViewMotionEventOffsets(MotionEvent e, boolean canClear) {
// TODO(dtrainor): Factor this out to LayoutDriver. // TODO(dtrainor): Factor this out to LayoutDriver.
if (e == null || mTabVisible == null) return; if (e == null || mTabVisible == null) return;
......
...@@ -506,6 +506,11 @@ public class TabWebContentsDelegateAndroid extends WebContentsDelegateAndroid { ...@@ -506,6 +506,11 @@ public class TabWebContentsDelegateAndroid extends WebContentsDelegateAndroid {
if (tab != null) nativeNotifyStopped(tab.getWebContents()); if (tab != null) nativeNotifyStopped(tab.getWebContents());
} }
@CalledByNative
private void setOverlayMode(boolean useOverlayMode) {
mTab.getActivity().setOverlayMode(useOverlayMode);
}
@Override @Override
public ContentVideoViewEmbedder getContentVideoViewEmbedder() { public ContentVideoViewEmbedder getContentVideoViewEmbedder() {
return new ActivityContentVideoViewEmbedder(mTab.getActivity()) { return new ActivityContentVideoViewEmbedder(mTab.getActivity()) {
......
...@@ -290,6 +290,15 @@ bool TabWebContentsDelegateAndroid::CheckMediaAccessPermission( ...@@ -290,6 +290,15 @@ bool TabWebContentsDelegateAndroid::CheckMediaAccessPermission(
->CheckMediaAccessPermission(web_contents, security_origin, type); ->CheckMediaAccessPermission(web_contents, security_origin, type);
} }
void TabWebContentsDelegateAndroid::SetOverlayMode(bool use_overlay_mode) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
if (obj.is_null())
return;
Java_TabWebContentsDelegateAndroid_setOverlayMode(env, obj, use_overlay_mode);
}
bool TabWebContentsDelegateAndroid::RequestPpapiBrokerPermission( bool TabWebContentsDelegateAndroid::RequestPpapiBrokerPermission(
WebContents* web_contents, WebContents* web_contents,
const GURL& url, const GURL& url,
......
...@@ -65,6 +65,7 @@ class TabWebContentsDelegateAndroid ...@@ -65,6 +65,7 @@ class TabWebContentsDelegateAndroid
bool CheckMediaAccessPermission(content::WebContents* web_contents, bool CheckMediaAccessPermission(content::WebContents* web_contents,
const GURL& security_origin, const GURL& security_origin,
content::MediaStreamType type) override; content::MediaStreamType type) override;
void SetOverlayMode(bool use_overlay_mode) override;
bool RequestPpapiBrokerPermission( bool RequestPpapiBrokerPermission(
content::WebContents* web_contents, content::WebContents* web_contents,
const GURL& url, const GURL& url,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "content/browser/android/dialog_overlay_impl.h" #include "content/browser/android/dialog_overlay_impl.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "gpu/ipc/common/gpu_surface_tracker.h" #include "gpu/ipc/common/gpu_surface_tracker.h"
#include "jni/DialogOverlayImpl_jni.h" #include "jni/DialogOverlayImpl_jni.h"
#include "ui/android/window_android.h" #include "ui/android/window_android.h"
...@@ -67,6 +68,12 @@ DialogOverlayImpl::DialogOverlayImpl(const JavaParamRef<jobject>& obj, ...@@ -67,6 +68,12 @@ DialogOverlayImpl::DialogOverlayImpl(const JavaParamRef<jobject>& obj,
void DialogOverlayImpl::CompleteInit(JNIEnv* env, void DialogOverlayImpl::CompleteInit(JNIEnv* env,
const JavaParamRef<jobject>& obj) { const JavaParamRef<jobject>& obj) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Note: It's ok to call SetOverlayMode() directly here, because there can be
// at most one overlay alive at the time. This logic needs to be updated if
// ever AndroidOverlayProviderImpl.MAX_OVERLAYS > 1.
web_contents()->GetDelegate()->SetOverlayMode(true);
// Send the initial token, if there is one. The observer will notify us about // Send the initial token, if there is one. The observer will notify us about
// changes only. // changes only.
if (ui::WindowAndroid* window = cvc_->GetWindowAndroid()) { if (ui::WindowAndroid* window = cvc_->GetWindowAndroid()) {
...@@ -120,6 +127,11 @@ void DialogOverlayImpl::UnregisterForTokensIfNeeded() { ...@@ -120,6 +127,11 @@ void DialogOverlayImpl::UnregisterForTokensIfNeeded() {
if (!cvc_) if (!cvc_)
return; return;
// We clear overlay mode here rather than in Destroy(), because we may have
// been called via a WebContentsDestroyed() event, and this might be the last
// opportunity we have to access web_contents().
web_contents()->GetDelegate()->SetOverlayMode(false);
cvc_->RemoveObserver(this); cvc_->RemoveObserver(this);
cvc_ = nullptr; cvc_ = nullptr;
rfhi_ = nullptr; rfhi_ = nullptr;
......
...@@ -81,6 +81,13 @@ public class ContentViewRenderView extends FrameLayout { ...@@ -81,6 +81,13 @@ public class ContentViewRenderView extends FrameLayout {
assert mNativeContentViewRenderView != 0; assert mNativeContentViewRenderView != 0;
nativeSurfaceCreated(mNativeContentViewRenderView); nativeSurfaceCreated(mNativeContentViewRenderView);
// On pre-M Android, layers start in the hidden state until a relayout happens.
// There is a bug that manifests itself when entering overlay mode on pre-M devices,
// where a relayout never happens. This bug is out of Chromium's control, but can be
// worked around by forcibly re-setting the visibility of the surface view.
// Otherwise, the screen stays black, and some tests fail.
mSurfaceView.setVisibility(mSurfaceView.getVisibility());
onReadyToRender(); onReadyToRender();
} }
......
...@@ -28,6 +28,9 @@ public class AndroidOverlayProviderImpl implements AndroidOverlayProvider { ...@@ -28,6 +28,9 @@ public class AndroidOverlayProviderImpl implements AndroidOverlayProvider {
private static final String TAG = "AndroidOverlayProvider"; private static final String TAG = "AndroidOverlayProvider";
// Maximum number of concurrent overlays that we allow. // Maximum number of concurrent overlays that we allow.
// Note: DialogOverlayImpl::CompleteInit() calls WebContentsDelegate::SetOverlayMode() directly,
// because there can only be one overlay alive at a time. If we were to support multiple
// concurrent overlays, we need to revisit this logic.
private static final int MAX_OVERLAYS = 1; private static final int MAX_OVERLAYS = 1;
// We maintain a thread with a Looper for the AndroidOverlays to use, since Dialog requires one. // We maintain a thread with a Looper for the AndroidOverlays to use, since Dialog requires one.
...@@ -57,6 +60,9 @@ public class AndroidOverlayProviderImpl implements AndroidOverlayProvider { ...@@ -57,6 +60,9 @@ public class AndroidOverlayProviderImpl implements AndroidOverlayProvider {
AndroidOverlayConfig config) { AndroidOverlayConfig config) {
ThreadUtils.assertOnUiThread(); ThreadUtils.assertOnUiThread();
// If this is no longer true, we need to update DialogOverlayImpl::CompleteInit().
assert MAX_OVERLAYS == 1;
// Limit the number of concurrent surfaces. // Limit the number of concurrent surfaces.
if (mNumOverlays >= MAX_OVERLAYS) { if (mNumOverlays >= MAX_OVERLAYS) {
client.onDestroyed(); client.onDestroyed();
......
...@@ -26,6 +26,9 @@ public class DialogOverlayImplTest extends DialogOverlayImplTestBase { ...@@ -26,6 +26,9 @@ public class DialogOverlayImplTest extends DialogOverlayImplTestBase {
@SmallTest @SmallTest
@Feature({"AndroidOverlay"}) @Feature({"AndroidOverlay"})
public void testCreateDestroyOverlay() { public void testCreateDestroyOverlay() {
Assert.assertFalse(getClient().hasReceivedOverlayModeChange());
Assert.assertFalse(getClient().isUsingOverlayMode());
final DialogOverlayImpl overlay = createOverlay(0, 0, 10, 10); final DialogOverlayImpl overlay = createOverlay(0, 0, 10, 10);
// We should get a new overlay with a valid surface key. // We should get a new overlay with a valid surface key.
...@@ -33,6 +36,9 @@ public class DialogOverlayImplTest extends DialogOverlayImplTestBase { ...@@ -33,6 +36,9 @@ public class DialogOverlayImplTest extends DialogOverlayImplTestBase {
Assert.assertEquals(Client.SURFACE_READY, event.which); Assert.assertEquals(Client.SURFACE_READY, event.which);
Assert.assertTrue(event.surfaceKey > 0); Assert.assertTrue(event.surfaceKey > 0);
Assert.assertTrue(getClient().hasReceivedOverlayModeChange());
Assert.assertTrue(getClient().isUsingOverlayMode());
// Close the overlay, and make sure that the provider is notified. // Close the overlay, and make sure that the provider is notified.
// Note that we should not get a 'destroyed' message when we close it. // Note that we should not get a 'destroyed' message when we close it.
ThreadUtils.runOnUiThreadBlocking(new Runnable() { ThreadUtils.runOnUiThreadBlocking(new Runnable() {
...@@ -42,6 +48,7 @@ public class DialogOverlayImplTest extends DialogOverlayImplTestBase { ...@@ -42,6 +48,7 @@ public class DialogOverlayImplTest extends DialogOverlayImplTestBase {
} }
}); });
Assert.assertEquals(Client.RELEASED, getClient().nextEvent().which); Assert.assertEquals(Client.RELEASED, getClient().nextEvent().which);
Assert.assertFalse(getClient().isUsingOverlayMode());
} }
@SmallTest @SmallTest
......
...@@ -9,6 +9,7 @@ import android.os.HandlerThread; ...@@ -9,6 +9,7 @@ import android.os.HandlerThread;
import org.junit.Assert; import org.junit.Assert;
import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.content.browser.framehost.RenderFrameHostImpl; import org.chromium.content.browser.framehost.RenderFrameHostImpl;
import org.chromium.content_shell_apk.ContentShellTestBase; import org.chromium.content_shell_apk.ContentShellTestBase;
...@@ -75,6 +76,9 @@ public abstract class DialogOverlayImplTestBase extends ContentShellTestBase { ...@@ -75,6 +76,9 @@ public abstract class DialogOverlayImplTestBase extends ContentShellTestBase {
public long surfaceKey; public long surfaceKey;
} }
private boolean mHasReceivedOverlayModeChange = false;
private boolean mUseOverlayMode = false;
private ArrayBlockingQueue<Event> mPending; private ArrayBlockingQueue<Event> mPending;
public Client() { public Client() {
...@@ -101,6 +105,19 @@ public abstract class DialogOverlayImplTestBase extends ContentShellTestBase { ...@@ -101,6 +105,19 @@ public abstract class DialogOverlayImplTestBase extends ContentShellTestBase {
mPending.add(new Event(CONNECTION_ERROR, exception)); mPending.add(new Event(CONNECTION_ERROR, exception));
} }
public void onOverlayModeChanged(boolean useOverlayMode) {
mHasReceivedOverlayModeChange = true;
mUseOverlayMode = useOverlayMode;
}
public boolean hasReceivedOverlayModeChange() {
return mHasReceivedOverlayModeChange;
}
public boolean isUsingOverlayMode() {
return mUseOverlayMode;
}
// This isn't part of the overlay client. It's called by the overlay to indicate that it // This isn't part of the overlay client. It's called by the overlay to indicate that it
// has been released by the client, but it's routed to us anyway. It's on the Browser UI // has been released by the client, but it's routed to us anyway. It's on the Browser UI
// thread, and it's convenient for us to keep track of it here. // thread, and it's convenient for us to keep track of it here.
...@@ -176,6 +193,16 @@ public abstract class DialogOverlayImplTestBase extends ContentShellTestBase { ...@@ -176,6 +193,16 @@ public abstract class DialogOverlayImplTestBase extends ContentShellTestBase {
mClient.notifyReleased(); mClient.notifyReleased();
} }
}; };
Callback<Boolean> overlayModeChanged = new Callback<Boolean>() {
@Override
public void onResult(Boolean useOverlayMode) {
mClient.onOverlayModeChanged(useOverlayMode);
}
};
getActivityForTestCommon().getActiveShell().setOverayModeChangedCallbackForTesting(
overlayModeChanged);
} }
// Create an overlay with the given parameters and return it. // Create an overlay with the given parameters and return it.
......
...@@ -217,6 +217,8 @@ WebContentsDelegate::GetContentVideoViewEmbedder() { ...@@ -217,6 +217,8 @@ WebContentsDelegate::GetContentVideoViewEmbedder() {
bool WebContentsDelegate::ShouldBlockMediaRequest(const GURL& url) { bool WebContentsDelegate::ShouldBlockMediaRequest(const GURL& url) {
return false; return false;
} }
void WebContentsDelegate::SetOverlayMode(bool use_overlay_mode) {}
#endif #endif
bool WebContentsDelegate::RequestPpapiBrokerPermission( bool WebContentsDelegate::RequestPpapiBrokerPermission(
......
...@@ -491,6 +491,12 @@ class CONTENT_EXPORT WebContentsDelegate { ...@@ -491,6 +491,12 @@ class CONTENT_EXPORT WebContentsDelegate {
// Returns true if the given media should be blocked to load. // Returns true if the given media should be blocked to load.
virtual bool ShouldBlockMediaRequest(const GURL& url); virtual bool ShouldBlockMediaRequest(const GURL& url);
// Tells the delegate to enter overlay mode.
// Overlay mode means that we are currently using AndroidOverlays to display
// video, and that the compositor's surface should support alpha and not be
// marked as opaque. See media/base/android/android_overlay.h.
virtual void SetOverlayMode(bool use_overlay_mode);
#endif #endif
// Requests permission to access the PPAPI broker. The delegate should return // Requests permission to access the PPAPI broker. The delegate should return
......
...@@ -24,6 +24,7 @@ import android.widget.LinearLayout; ...@@ -24,6 +24,7 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import org.chromium.base.Callback;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.content.browser.ActivityContentVideoViewEmbedder; import org.chromium.content.browser.ActivityContentVideoViewEmbedder;
...@@ -70,6 +71,8 @@ public class Shell extends LinearLayout { ...@@ -70,6 +71,8 @@ public class Shell extends LinearLayout {
private boolean mLoading; private boolean mLoading;
private boolean mIsFullscreen; private boolean mIsFullscreen;
private Callback<Boolean> mOverlayModeChangedCallbackForTesting;
/** /**
* Constructor for inflating via XML. * Constructor for inflating via XML.
*/ */
...@@ -365,6 +368,18 @@ public class Shell extends LinearLayout { ...@@ -365,6 +368,18 @@ public class Shell extends LinearLayout {
}; };
} }
@CalledByNative
public void setOverlayMode(boolean useOverlayMode) {
mContentViewRenderView.setOverlayVideoMode(useOverlayMode);
if (mOverlayModeChangedCallbackForTesting != null) {
mOverlayModeChangedCallbackForTesting.onResult(useOverlayMode);
}
}
public void setOverayModeChangedCallbackForTesting(Callback<Boolean> callback) {
mOverlayModeChangedCallbackForTesting = callback;
}
/** /**
* Enable/Disable navigation(Prev/Next) button if navigation is allowed/disallowed * Enable/Disable navigation(Prev/Next) button if navigation is allowed/disallowed
* in respective direction. * in respective direction.
......
...@@ -88,15 +88,6 @@ public class ShellManager extends FrameLayout { ...@@ -88,15 +88,6 @@ public class ShellManager extends FrameLayout {
if (previousShell != null) previousShell.close(); if (previousShell != null) previousShell.close();
} }
/**
* Enter or leave overlay video mode.
* @param enabled Whether overlay mode is enabled.
*/
public void setOverlayVideoMode(boolean enabled) {
if (mContentViewRenderView == null) return;
mContentViewRenderView.setOverlayVideoMode(enabled);
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
@CalledByNative @CalledByNative
private Object createShell(long nativeShellPtr) { private Object createShell(long nativeShellPtr) {
......
...@@ -132,6 +132,7 @@ class Shell : public WebContentsDelegate, ...@@ -132,6 +132,7 @@ class Shell : public WebContentsDelegate,
void LoadProgressChanged(WebContents* source, double progress) override; void LoadProgressChanged(WebContents* source, double progress) override;
base::android::ScopedJavaLocalRef<jobject> base::android::ScopedJavaLocalRef<jobject>
GetContentVideoViewEmbedder() override; GetContentVideoViewEmbedder() override;
void SetOverlayMode(bool use_overlay_mode) override;
#endif #endif
void EnterFullscreenModeForTab(WebContents* web_contents, void EnterFullscreenModeForTab(WebContents* web_contents,
const GURL& origin) override; const GURL& origin) override;
......
...@@ -82,6 +82,11 @@ ScopedJavaLocalRef<jobject> Shell::GetContentVideoViewEmbedder() { ...@@ -82,6 +82,11 @@ ScopedJavaLocalRef<jobject> Shell::GetContentVideoViewEmbedder() {
return Java_Shell_getContentVideoViewEmbedder(env, java_object_); return Java_Shell_getContentVideoViewEmbedder(env, java_object_);
} }
void Shell::SetOverlayMode(bool use_overlay_mode) {
JNIEnv* env = base::android::AttachCurrentThread();
return Java_Shell_setOverlayMode(env, java_object_, use_overlay_mode);
}
void Shell::PlatformToggleFullscreenModeForTab(WebContents* web_contents, void Shell::PlatformToggleFullscreenModeForTab(WebContents* web_contents,
bool enter_fullscreen) { bool enter_fullscreen) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment