diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index 11c1329e6f75..91a31e723dbb 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -446,6 +446,9 @@ GLContextEGL::ReleaseSurface() { if (mOwnsContext) { mozilla::gl::DestroySurface(mSurface); } + if (mSurface == mSurfaceOverride) { + mSurfaceOverride = EGL_NO_SURFACE; + } mSurface = EGL_NO_SURFACE; } @@ -813,6 +816,41 @@ GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget) return glContext.forget(); } +#if defined(ANDROID) +EGLSurface +GLContextProviderEGL::CreateEGLSurface(void* aWindow) +{ + if (!sEGLLibrary.EnsureInitialized()) { + MOZ_CRASH("Failed to load EGL library!\n"); + } + + EGLConfig config; + if (!CreateConfig(&config)) { + MOZ_CRASH("Failed to create EGLConfig!\n"); + } + + MOZ_ASSERT(aWindow); + + EGLSurface surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, aWindow, 0); + + if (surface == EGL_NO_SURFACE) { + MOZ_CRASH("Failed to create EGLSurface!\n"); + } + + return surface; +} + +void +GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface) +{ + if (!sEGLLibrary.EnsureInitialized()) { + MOZ_CRASH("Failed to load EGL library!\n"); + } + + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); +} +#endif // defined(ANDROID) + already_AddRefed GLContextEGL::CreateEGLPBufferOffscreenContext(const gfxIntSize& size) { diff --git a/gfx/gl/GLContextProviderImpl.h b/gfx/gl/GLContextProviderImpl.h index 9e87170c8237..fa4d253fe227 100644 --- a/gfx/gl/GLContextProviderImpl.h +++ b/gfx/gl/GLContextProviderImpl.h @@ -10,6 +10,9 @@ #ifndef GL_CONTEXT_PROVIDER_NAME #error GL_CONTEXT_PROVIDER_NAME not defined #endif +#if defined(ANDROID) +typedef void* EGLSurface; +#endif // defined(ANDROID) class GL_CONTEXT_PROVIDER_NAME { @@ -76,6 +79,11 @@ public: static already_AddRefed CreateWrappingExisting(void* aContext, void* aSurface); +#if defined(ANDROID) + static EGLSurface CreateEGLSurface(void* aWindow); + static void DestroyEGLSurface(EGLSurface surface); +#endif // defined(ANDROID) + /** * Get a pointer to the global context, creating it if it doesn't exist. */ diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp index 54b7c66eb107..3ea7102c47ec 100644 --- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -54,6 +54,11 @@ #include "nsRegion.h" // for nsIntRegion, etc #ifdef MOZ_WIDGET_ANDROID #include +#include "AndroidBridge.h" +#include "opengl/CompositorOGL.h" +#include "GLContextEGL.h" +#include "GLContextProvider.h" +#include "ScopedGLHelpers.h" #endif #include "GeckoProfiler.h" #include "TextRenderer.h" // for TextRenderer @@ -306,6 +311,9 @@ LayerManagerComposite::EndTransaction(DrawPaintedLayerCallback aCallback, ApplyOcclusionCulling(mRoot, opaque); Render(); +#ifdef MOZ_WIDGET_ANDROID + RenderToPresentationSurface(); +#endif mGeometryChanged = false; } else { // Modified layer tree @@ -768,6 +776,175 @@ LayerManagerComposite::Render() RecordFrame(); } +#ifdef MOZ_WIDGET_ANDROID +class ScopedCompositorProjMatrix { +public: + ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix): + mCompositor(aCompositor), + mOriginalProjMatrix(mCompositor->GetProjMatrix()) + { + mCompositor->SetProjMatrix(aProjMatrix); + } + + ~ScopedCompositorProjMatrix() + { + mCompositor->SetProjMatrix(mOriginalProjMatrix); + } +private: + CompositorOGL* const mCompositor; + const Matrix4x4 mOriginalProjMatrix; +}; + +class ScopedCompostitorSurfaceSize { +public: + ScopedCompostitorSurfaceSize(CompositorOGL* aCompositor, const gfx::IntSize& aSize) : + mCompositor(aCompositor), + mOriginalSize(mCompositor->GetDestinationSurfaceSize()) + { + mCompositor->SetDestinationSurfaceSize(aSize); + } + ~ScopedCompostitorSurfaceSize() + { + mCompositor->SetDestinationSurfaceSize(mOriginalSize); + } +private: + CompositorOGL* const mCompositor; + const gfx::IntSize mOriginalSize; +}; + +class ScopedCompositorRenderOffset { +public: + ScopedCompositorRenderOffset(CompositorOGL* aCompositor, const ScreenPoint& aOffset) : + mCompositor(aCompositor), + mOriginalOffset(mCompositor->GetScreenRenderOffset()) + { + mCompositor->SetScreenRenderOffset(aOffset); + } + ~ScopedCompositorRenderOffset() + { + mCompositor->SetScreenRenderOffset(mOriginalOffset); + } +private: + CompositorOGL* const mCompositor; + const ScreenPoint mOriginalOffset; +}; + +class ScopedContextSurfaceOverride { +public: + ScopedContextSurfaceOverride(GLContextEGL* aContext, void* aSurface) : + mContext(aContext) + { + MOZ_ASSERT(aSurface); + mContext->SetEGLSurfaceOverride(aSurface); + mContext->MakeCurrent(true); + } + ~ScopedContextSurfaceOverride() + { + mContext->SetEGLSurfaceOverride(EGL_NO_SURFACE); + mContext->MakeCurrent(true); + } +private: + GLContextEGL* const mContext; +}; + +void +LayerManagerComposite::RenderToPresentationSurface() +{ + if (!AndroidBridge::Bridge()) { + return; + } + + void* window = AndroidBridge::Bridge()->GetPresentationWindow(); + + if (!window) { + return; + } + + EGLSurface surface = AndroidBridge::Bridge()->GetPresentationSurface(); + + if (!surface) { + //create surface; + surface = GLContextProviderEGL::CreateEGLSurface(window); + if (!surface) { + return; + } + + AndroidBridge::Bridge()->SetPresentationSurface(surface); + } + + CompositorOGL* compositor = static_cast(mCompositor.get()); + GLContext* gl = compositor->gl(); + GLContextEGL* egl = GLContextEGL::Cast(gl); + + if (!egl) { + return; + } + + const IntSize windowSize = AndroidBridge::Bridge()->GetNativeWindowSize(window); + + if ((windowSize.width <= 0) || (windowSize.height <= 0)) { + return; + } + + const int actualWidth = windowSize.width; + const int actualHeight = windowSize.height; + + const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize(); + + const int pageWidth = originalSize.width; + const int pageHeight = originalSize.height; + + float scale = 1.0; + + if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) { + const float scaleWidth = (float)actualWidth / (float)pageWidth; + const float scaleHeight = (float)actualHeight / (float)pageHeight; + scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight; + } + + const gfx::IntSize actualSize(actualWidth, actualHeight); + ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize); + + const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0); + ScopedCompositorRenderOffset overrideRenderOffset(compositor, offset); + ScopedContextSurfaceOverride overrideSurface(egl, surface); + + nsIntRegion invalid; + Rect bounds(0.0f, 0.0f, scale * pageWidth, (float)actualHeight); + Rect rect, actualBounds; + + mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds); + + // Override the projection matrix since the presentation frame buffer + // is probably not the same size as the device frame buffer. The override + // projection matrix also scales the content to fit into the presentation + // frame buffer. + Matrix viewMatrix; + viewMatrix.PreTranslate(-1.0, 1.0); + viewMatrix.PreScale((2.0f * scale) / (float)actualWidth, (2.0f * scale) / (float)actualHeight); + viewMatrix.PreScale(1.0f, -1.0f); + viewMatrix.PreTranslate((int)((float)offset.x / scale), offset.y); + + Matrix4x4 projMatrix = Matrix4x4::From2D(viewMatrix); + + ScopedCompositorProjMatrix overrideProjMatrix(compositor, projMatrix); + + // The Java side of Fennec sets a scissor rect that accounts for + // chrome such as the URL bar. Override that so that the entire frame buffer + // is cleared. + ScopedScissorRect screen(egl, 0, 0, actualWidth, actualHeight); + egl->fClearColor(0.0, 0.0, 0.0, 0.0); + egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT); + + const nsIntRect clipRect = nsIntRect(0, 0, (int)(scale * pageWidth), actualHeight); + RootLayer()->Prepare(RenderTargetPixel::FromUntyped(clipRect)); + RootLayer()->RenderLayer(clipRect); + + mCompositor->EndFrame(); + mCompositor->SetFBAcquireFence(mRoot); +} +#endif + static void SubtractTransformedRegion(nsIntRegion& aRegion, const nsIntRegion& aRegionToSubtract, diff --git a/gfx/layers/composite/LayerManagerComposite.h b/gfx/layers/composite/LayerManagerComposite.h index 0e6552099dc2..bda1380c18c1 100644 --- a/gfx/layers/composite/LayerManagerComposite.h +++ b/gfx/layers/composite/LayerManagerComposite.h @@ -276,6 +276,9 @@ private: * Render the current layer tree to the active target. */ void Render(); +#ifdef MOZ_WIDGET_ANDROID + void RenderToPresentationSurface(); +#endif /** * Render debug overlays such as the FPS/FrameCounter above the frame. diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 23876f0bc706..0eff856fa047 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -799,6 +799,22 @@ CompositorParent::SchedulePauseOnCompositorThread() lock.Wait(); } +bool +CompositorParent::ScheduleResumeOnCompositorThread() +{ + MonitorAutoLock lock(mResumeCompositionMonitor); + + CancelableTask *resumeTask = + NewRunnableMethod(this, &CompositorParent::ResumeComposition); + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(FROM_HERE, resumeTask); + + // Wait until the resume has actually been processed by the compositor thread + lock.Wait(); + + return !mPaused; +} + bool CompositorParent::ScheduleResumeOnCompositorThread(int width, int height) { diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 0011a479cc5a..586a77d23278 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -223,6 +223,7 @@ public: * Returns true if a surface was obtained and the resume succeeded; false * otherwise. */ + bool ScheduleResumeOnCompositorThread(); bool ScheduleResumeOnCompositorThread(int width, int height); virtual void ScheduleComposition(); diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h index ff82fa5956c0..94f7205ded7a 100644 --- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -304,6 +304,19 @@ public: const gfx::Matrix4x4& GetProjMatrix() const { return mProjMatrix; } + + void SetProjMatrix(const gfx::Matrix4x4& aProjMatrix) { + mProjMatrix = aProjMatrix; + } + + const gfx::IntSize GetDestinationSurfaceSize() const { + return gfx::IntSize (mSurfaceSize.width, mSurfaceSize.height); + } + + const ScreenPoint& GetScreenRenderOffset() const { + return mRenderOffset; + } + private: virtual gfx::IntSize GetWidgetSize() const override { diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 5a9916bddf44..4b5c675ae979 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -294,6 +294,9 @@ public class GeckoAppShell public static native SurfaceBits getSurfaceBits(Surface surface); + public static native void addPresentationSurface(Surface surface); + public static native void removePresentationSurface(Surface surface); + public static native void onFullScreenPluginHidden(View view); public static class CreateShortcutFaviconLoadedListener implements OnFaviconLoadedListener { diff --git a/mobile/android/base/MediaPlayerManager.java b/mobile/android/base/MediaPlayerManager.java index 68f943b7aabb..91ba5fc61479 100644 --- a/mobile/android/base/MediaPlayerManager.java +++ b/mobile/android/base/MediaPlayerManager.java @@ -5,6 +5,9 @@ package org.mozilla.gecko; +import android.app.Presentation; +import android.content.Context; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.media.MediaControlIntent; @@ -12,6 +15,13 @@ import android.support.v7.media.MediaRouteSelector; import android.support.v7.media.MediaRouter; import android.support.v7.media.MediaRouter.RouteInfo; import android.util.Log; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowManager; import com.google.android.gms.cast.CastMediaControlIntent; @@ -61,6 +71,7 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener private MediaRouter mediaRouter = null; private final Map displays = new HashMap(); + private GeckoPresentation presentation = null; @Override public void onCreate(Bundle savedInstanceState) { @@ -135,19 +146,23 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener displays.remove(route.getId()); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent( "MediaPlayer:Removed", route.getId())); + updatePresentation(); } @SuppressWarnings("unused") public void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo route) { + updatePresentation(); } // These methods aren't used by the support version Media Router @SuppressWarnings("unused") public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) { + updatePresentation(); } @Override public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { + updatePresentation(); } @Override @@ -159,6 +174,7 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener debug("onRouteAdded: route=" + route); final GeckoMediaPlayer display = getMediaPlayerForRoute(route); saveAndNotifyOfDisplay("MediaPlayer:Added", route, display); + updatePresentation(); } @Override @@ -166,6 +182,7 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener debug("onRouteChanged: route=" + route); final GeckoMediaPlayer display = displays.get(route.getId()); saveAndNotifyOfDisplay("MediaPlayer:Changed", route, display); + updatePresentation(); } private void saveAndNotifyOfDisplay(final String eventName, @@ -221,4 +238,86 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener .build(); mediaRouter.addCallback(selectorBuilder, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); } + + @Override + public void onStop() { + super.onStop(); + if (presentation != null) { + presentation.dismiss(); + presentation = null; + } + } + + private void updatePresentation() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + return; + } + + if (mediaRouter == null) { + return; + } + + MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute(); + Display display = route != null ? route.getPresentationDisplay() : null; + + if (display != null) { + if ((presentation != null) && (presentation.getDisplay() != display)) { + presentation.dismiss(); + presentation = null; + } + + if (presentation == null) { + presentation = new GeckoPresentation(getActivity(), display); + + try { + presentation.show(); + } catch (WindowManager.InvalidDisplayException ex) { + Log.w(LOGTAG, "Couldn't show presentation! Display was removed in " + + "the meantime.", ex); + presentation = null; + } + } + } else if (presentation != null) { + presentation.dismiss(); + presentation = null; + } + } + + private static class SurfaceListener implements SurfaceHolder.Callback { + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + // Surface changed so force a composite + GeckoAppShell.scheduleComposite(); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + GeckoAppShell.addPresentationSurface(holder.getSurface()); + GeckoAppShell.scheduleComposite(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + GeckoAppShell.removePresentationSurface(holder.getSurface()); + } + } + + private final static class GeckoPresentation extends Presentation { + private SurfaceView mView; + public GeckoPresentation(Context context, Display display) { + super(context, display); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mView = new SurfaceView(getContext()); + setContentView(mView, new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + mView.getHolder().addCallback(new SurfaceListener()); + } + } } diff --git a/mozglue/android/jni-stubs.inc b/mozglue/android/jni-stubs.inc index 92fb10fa904c..6d2726366b6b 100644 --- a/mozglue/android/jni-stubs.inc +++ b/mozglue/android/jni-stubs.inc @@ -305,6 +305,44 @@ Java_org_mozilla_gecko_GeckoAppShell_getSurfaceBits(JNIEnv * arg0, jclass arg1, #ifdef JNI_STUBS +typedef void (*Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface_t)(JNIEnv *, jclass, jobject); +static Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface_t f_Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface; +extern "C" NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface(JNIEnv * arg0, jclass arg1, jobject arg2) { + if (!f_Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface) { + arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), + "JNI Function called before it was loaded"); + return ; + } + f_Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface(arg0, arg1, arg2); +} +#endif + +#ifdef JNI_BINDINGS + xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface", &f_Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface); +#endif + +#ifdef JNI_STUBS + +typedef void (*Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface_t)(JNIEnv *, jclass, jobject); +static Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface_t f_Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface; +extern "C" NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface(JNIEnv * arg0, jclass arg1, jobject arg2) { + if (!f_Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface) { + arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), + "JNI Function called before it was loaded"); + return ; + } + f_Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface(arg0, arg1, arg2); +} +#endif + +#ifdef JNI_BINDINGS + xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface", &f_Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface); +#endif + +#ifdef JNI_STUBS + typedef void (*Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden_t)(JNIEnv *, jclass, jobject); static Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden_t f_Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden; extern "C" NS_EXPORT void JNICALL diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index f68f469224b1..c2d1b5223932 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -44,6 +44,7 @@ #include "MediaCodec.h" #include "SurfaceTexture.h" +#include "GLContextProvider.h" using namespace mozilla; using namespace mozilla::gfx; @@ -821,6 +822,8 @@ AndroidBridge::OpenGraphicsLibraries() ANativeWindow_setBuffersGeometry = (int (*)(void*, int, int, int)) dlsym(handle, "ANativeWindow_setBuffersGeometry"); ANativeWindow_lock = (int (*)(void*, void*, void*)) dlsym(handle, "ANativeWindow_lock"); ANativeWindow_unlockAndPost = (int (*)(void*))dlsym(handle, "ANativeWindow_unlockAndPost"); + ANativeWindow_getWidth = (int (*)(void*))dlsym(handle, "ANativeWindow_getWidth"); + ANativeWindow_getHeight = (int (*)(void*))dlsym(handle, "ANativeWindow_getHeight"); // This is only available in Honeycomb and ICS. It was removed in Jelly Bean ANativeWindow_fromSurfaceTexture = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurfaceTexture"); @@ -1275,6 +1278,16 @@ AndroidBridge::ReleaseNativeWindow(void *window) // have nothing to do here. We should probably ref it. } +IntSize +AndroidBridge::GetNativeWindowSize(void* window) +{ + if (!window || !ANativeWindow_getWidth || !ANativeWindow_getHeight) { + return IntSize(0, 0); + } + + return IntSize(ANativeWindow_getWidth(window), ANativeWindow_getHeight(window)); +} + void* AndroidBridge::AcquireNativeWindowFromSurfaceTexture(JNIEnv* aEnv, jobject aSurfaceTexture) { @@ -1508,7 +1521,9 @@ void AndroidBridge::SyncFrameMetrics(const ParentLayerPoint& aScrollOffset, floa } AndroidBridge::AndroidBridge() - : mLayerClient(nullptr) + : mLayerClient(nullptr), + mPresentationWindow(nullptr), + mPresentationSurface(nullptr) { } @@ -2036,6 +2051,51 @@ AndroidBridge::RunDelayedUiThreadTasks() return -1; } +void* +AndroidBridge::GetPresentationWindow() +{ + return mPresentationWindow; +} + +void +AndroidBridge::SetPresentationWindow(void* aPresentationWindow) +{ + if (mPresentationWindow) { + const bool wasAlreadyPaused = nsWindow::IsCompositionPaused(); + if (!wasAlreadyPaused) { + nsWindow::SchedulePauseComposition(); + } + + mPresentationWindow = aPresentationWindow; + if (mPresentationSurface) { + // destroy the egl surface! + // The compositor is paused so it should be okay to destroy + // the surface here. + mozilla::gl::GLContextProvider::DestroyEGLSurface(mPresentationSurface); + mPresentationSurface = nullptr; + } + + if (!wasAlreadyPaused) { + nsWindow::ScheduleResumeComposition(); + } + } + else { + mPresentationWindow = aPresentationWindow; + } +} + +EGLSurface +AndroidBridge::GetPresentationSurface() +{ + return mPresentationSurface; +} + +void +AndroidBridge::SetPresentationSurface(EGLSurface aPresentationSurface) +{ + mPresentationSurface = aPresentationSurface; +} + Object::LocalRef AndroidBridge::ChannelCreate(Object::Param stream) { JNIEnv* const env = GetJNIForThread(); auto rv = Object::LocalRef::Adopt(env, env->CallStaticObjectMethod( diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index c792d21731d4..8e1aaf625d4b 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -21,6 +21,7 @@ #include "nsIMIMEInfo.h" #include "nsColor.h" #include "gfxRect.h" +#include "mozilla/gfx/Point.h" #include "nsIAndroidBridge.h" #include "nsIMobileMessageCallback.h" @@ -264,6 +265,7 @@ public: void *AcquireNativeWindow(JNIEnv* aEnv, jobject aSurface); void ReleaseNativeWindow(void *window); + mozilla::gfx::IntSize GetNativeWindowSize(void* window); void *AcquireNativeWindowFromSurfaceTexture(JNIEnv* aEnv, jobject aSurface); void ReleaseNativeWindowForSurfaceTexture(void *window); @@ -426,6 +428,8 @@ protected: int (* ANativeWindow_lock)(void *window, void *outBuffer, void *inOutDirtyBounds); int (* ANativeWindow_unlockAndPost)(void *window); + int (* ANativeWindow_getWidth)(void * window); + int (* ANativeWindow_getHeight)(void * window); int (* Surface_lock)(void* surface, void* surfaceInfo, void* region, bool block); int (* Surface_unlockAndPost)(void* surface); @@ -439,6 +443,15 @@ private: public: void PostTaskToUiThread(Task* aTask, int aDelayMs); int64_t RunDelayedUiThreadTasks(); + + void* GetPresentationWindow(); + void SetPresentationWindow(void* aPresentationWindow); + + EGLSurface GetPresentationSurface(); + void SetPresentationSurface(EGLSurface aPresentationSurface); +private: + void* mPresentationWindow; + EGLSurface mPresentationSurface; }; class AutoJNIClass { diff --git a/widget/android/AndroidJNI.cpp b/widget/android/AndroidJNI.cpp index dbd177df453b..3200c8c79429 100644 --- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -811,6 +811,27 @@ cleanup: return surfaceBits; } +NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_addPresentationSurface(JNIEnv* jenv, jclass, jobject surface) +{ + if (surface != NULL) { + void* window = AndroidBridge::Bridge()->AcquireNativeWindow(jenv, surface); + if (window) { + AndroidBridge::Bridge()->SetPresentationWindow(window); + } + } +} + +NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_removePresentationSurface(JNIEnv* jenv, jclass, jobject surface) +{ + void* window = AndroidBridge::Bridge()->GetPresentationWindow(); + if (window) { + AndroidBridge::Bridge()->SetPresentationWindow(nullptr); + AndroidBridge::Bridge()->ReleaseNativeWindow(window); + } +} + NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden(JNIEnv* jenv, jclass, jobject view) { diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 028b7e55f40b..1a432c8c150b 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -2454,6 +2454,29 @@ nsWindow::ScheduleComposite() } } +bool +nsWindow::IsCompositionPaused() +{ + return sCompositorPaused; +} + +void +nsWindow::SchedulePauseComposition() +{ + if (sCompositorParent) { + sCompositorParent->SchedulePauseOnCompositorThread(); + sCompositorPaused = true; + } +} + +void +nsWindow::ScheduleResumeComposition() +{ + if (sCompositorParent && sCompositorParent->ScheduleResumeOnCompositorThread()) { + sCompositorPaused = false; + } +} + void nsWindow::ScheduleResumeComposition(int width, int height) { diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index d3915089f937..648abca0dc9d 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -156,7 +156,10 @@ public: static void SetCompositor(mozilla::layers::LayerManager* aLayerManager, mozilla::layers::CompositorParent* aCompositorParent, mozilla::layers::CompositorChild* aCompositorChild); + static bool IsCompositionPaused(); static void ScheduleComposite(); + static void SchedulePauseComposition(); + static void ScheduleResumeComposition(); static void ScheduleResumeComposition(int width, int height); static void ForceIsFirstPaint(); static float ComputeRenderIntegrity();