Bug 1143575. Android's screenshotting code should invalidate the LayerManagerComposite to ensure composition will actually happen. r=nical
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 12 Jun 2015 03:20:04 +1200
changeset 251666 3527e6013de9a9b04cd9f5e57c1cafafeaf9670e
parent 251665 b745c996a71d8c56d12e04bdcaa7a508c3940a5c
child 251667 6ecf73959121e4c63ca7c945159484cdc0edfcae
push idunknown
push userunknown
push dateunknown
reviewersnical
bugs1143575
milestone42.0a1
Bug 1143575. Android's screenshotting code should invalidate the LayerManagerComposite to ensure composition will actually happen. r=nical There is some ambiguity about whether ScheduleComposite will necessarily trigger a composite all the way to nsWindow::DrawWindowUnderlay. Android robocop tests assume it will, because they rely on DrawWindowOverlay being called so they can take a screenshot and make progress, but this is a very fragile assumption. They also rely on the entire window being painted, which is also a fragile assumption. This patch improves the situation by explicitly invalidating the current window area when Android Java code needs to trigger a composite. This avoids regressions from future patches in this series which make composition bail out when there is nothing invalid. The resulting setup is still a bit fragile for my taste but I'm not sure what the ideal solution would be.
dom/plugins/base/nsNPAPIPluginInstance.cpp
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
mobile/android/base/GeckoAppShell.java
mobile/android/base/PresentationMediaPlayerManager.java
mobile/android/base/gfx/GeckoLayerClient.java
mozglue/android/jni-stubs.inc
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJNI.cpp
widget/android/nsWindow.cpp
widget/android/nsWindow.h
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -973,17 +973,17 @@ already_AddRefed<AndroidSurfaceTexture> 
   nsCOMPtr<nsIRunnable> frameCallback = NS_NewRunnableMethod(this, &nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable);
   surface->SetFrameAvailableCallback(frameCallback);
   return surface.forget();
 }
 
 void nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable()
 {
   if (mRunning == RUNNING && mOwner)
-    AndroidBridge::Bridge()->ScheduleComposite();
+    AndroidBridge::Bridge()->InvalidateAndScheduleComposite();
 }
 
 void* nsNPAPIPluginInstance::AcquireContentWindow()
 {
   if (!mContentSurface) {
     mContentSurface = CreateSurfaceTexture();
 
     if (!mContentSurface)
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -840,16 +840,25 @@ bool
 CompositorParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion)
 {
   if (mLayerManager) {
     mLayerManager->AddInvalidRegion(aRegion);
   }
   return true;
 }
 
+void
+CompositorParent::Invalidate()
+{
+  if (mLayerManager && mLayerManager->GetRoot()) {
+    mLayerManager->AddInvalidRegion(
+        mLayerManager->GetRoot()->GetVisibleRegion().GetBounds());
+  }
+}
+
 bool
 CompositorParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex)
 {
   if (mLayerManager) {
     *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize);
   } else {
     *aOutStartIndex = 0;
   }
@@ -894,16 +903,24 @@ void
 CompositorParent::ScheduleRenderOnCompositorThread()
 {
   CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::ScheduleComposition);
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(FROM_HERE, renderTask);
 }
 
 void
+CompositorParent::InvalidateOnCompositorThread()
+{
+  CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::Invalidate);
+  MOZ_ASSERT(CompositorLoop());
+  CompositorLoop()->PostTask(FROM_HERE, renderTask);
+}
+
+void
 CompositorParent::PauseComposition()
 {
   MOZ_ASSERT(IsInCompositorThread(),
              "PauseComposition() can only be called on the compositor thread");
 
   MonitorAutoLock lock(mPauseCompositionMonitor);
 
   if (!mPaused) {
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -292,16 +292,17 @@ public:
 
   void NotifyChildCreated(const uint64_t& aChild);
 
   void AsyncRender();
 
   // Can be called from any thread
   void ScheduleRenderOnCompositorThread();
   void SchedulePauseOnCompositorThread();
+  void InvalidateOnCompositorThread();
   /**
    * Returns true if a surface was obtained and the resume succeeded; false
    * otherwise.
    */
   bool ScheduleResumeOnCompositorThread();
   bool ScheduleResumeOnCompositorThread(int width, int height);
 
   virtual void ScheduleComposition();
@@ -446,16 +447,17 @@ protected:
   void SetEGLSurfaceSize(int width, int height);
 
   void InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints);
   void PauseComposition();
   void ResumeComposition();
   void ResumeCompositionAndResize(int width, int height);
   void ForceComposition();
   void CancelCurrentCompositeTask();
+  void Invalidate();
 
   /**
    * Add a compositor to the global compositor map.
    */
   static void AddCompositor(CompositorParent* compositor, uint64_t* id);
   /**
    * Remove a compositor from the global compositor map.
    */
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -276,17 +276,17 @@ public class GeckoAppShell
     public static void notifyUriVisited(String uri) {
         sendEventToGecko(GeckoEvent.createVisitedEvent(uri));
     }
 
     public static native void processNextNativeEvent(boolean mayWait);
 
     public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime);
 
-    public static native void scheduleComposite();
+    public static native void invalidateAndScheduleComposite();
 
     // Resuming the compositor is a synchronous request, so be
     // careful of possible deadlock. Resuming the compositor will also cause
     // a composition, so there is no need to schedule a composition after
     // resuming.
     public static native void scheduleResumeComposition(int width, int height);
 
     public static native float computeRenderIntegrity();
--- a/mobile/android/base/PresentationMediaPlayerManager.java
+++ b/mobile/android/base/PresentationMediaPlayerManager.java
@@ -95,23 +95,23 @@ public class PresentationMediaPlayerMana
         }
     }
 
     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();
+            GeckoAppShell.invalidateAndScheduleComposite();
         }
 
         @Override
         public void surfaceCreated(SurfaceHolder holder) {
             GeckoAppShell.addPresentationSurface(holder.getSurface());
-            GeckoAppShell.scheduleComposite();
+            GeckoAppShell.invalidateAndScheduleComposite();
         }
 
         @Override
         public void surfaceDestroyed(SurfaceHolder holder) {
             GeckoAppShell.removePresentationSurface(holder.getSurface());
         }
     }
 }
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -774,17 +774,17 @@ class GeckoLayerClient implements LayerV
             adjustViewport(displayPort);
         }
     }
 
     /** Implementation of LayerView.Listener */
     @Override
     public void renderRequested() {
         try {
-            GeckoAppShell.scheduleComposite();
+            GeckoAppShell.invalidateAndScheduleComposite();
         } catch (UnsupportedOperationException uoe) {
             // In some very rare cases this gets called before libxul is loaded,
             // so catch and ignore the exception that will throw. See bug 837821
             Log.d(LOGTAG, "Dropping renderRequested call before libxul load.");
         }
     }
 
     /** Implementation of LayerView.Listener */
--- a/mozglue/android/jni-stubs.inc
+++ b/mozglue/android/jni-stubs.inc
@@ -205,31 +205,31 @@ Java_org_mozilla_gecko_GeckoAppShell_not
 #endif
 
 #ifdef JNI_BINDINGS
   xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange", &f_Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange);
 #endif
 
 #ifdef JNI_STUBS
 
-typedef void (*Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite_t)(JNIEnv *, jclass);
-static Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite_t f_Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite;
+typedef void (*Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite_t)(JNIEnv *, jclass);
+static Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite_t f_Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite;
 extern "C" NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(JNIEnv * arg0, jclass arg1) {
-    if (!f_Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite) {
+Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite(JNIEnv * arg0, jclass arg1) {
+    if (!f_Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite) {
         arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
                        "JNI Function called before it was loaded");
         return ;
     }
-     f_Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(arg0, arg1);
+     f_Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite(arg0, arg1);
 }
 #endif
 
 #ifdef JNI_BINDINGS
-  xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite", &f_Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite);
+  xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite", &f_Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite);
 #endif
 
 #ifdef JNI_STUBS
 
 typedef void (*Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition_t)(JNIEnv *, jclass, jint, jint);
 static Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition_t f_Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition;
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition(JNIEnv * arg0, jclass arg1, jint arg2, jint arg3) {
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1646,19 +1646,19 @@ AndroidBridge::GetScreenOrientation()
 
     if (!orientation)
         return dom::eScreenOrientation_None;
 
     return static_cast<dom::ScreenOrientation>(orientation);
 }
 
 void
-AndroidBridge::ScheduleComposite()
+AndroidBridge::InvalidateAndScheduleComposite()
 {
-    nsWindow::ScheduleComposite();
+    nsWindow::InvalidateAndScheduleComposite();
 }
 
 nsresult
 AndroidBridge::GetProxyForURI(const nsACString & aSpec,
                               const nsACString & aScheme,
                               const nsACString & aHost,
                               const int32_t      aPort,
                               nsACString & aResult)
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -303,17 +303,17 @@ public:
     // enum and that would require including the header which requires
     // include IPC headers which requires including basictypes.h which
     // requires a lot of changes...
     uint32_t GetScreenOrientation();
 
     int GetAPIVersion() { return mAPIVersion; }
     bool IsHoneycomb() { return mAPIVersion >= 11 && mAPIVersion <= 13; }
 
-    void ScheduleComposite();
+    void InvalidateAndScheduleComposite();
 
     nsresult GetProxyForURI(const nsACString & aSpec,
                             const nsACString & aScheme,
                             const nsACString & aHost,
                             const int32_t      aPort,
                             nsACString & aResult);
 
     bool PumpMessageLoop();
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -660,19 +660,19 @@ Java_org_mozilla_gecko_GeckoSmsManager_n
     nsCOMPtr<nsIRunnable> runnable =
       new NotifyReadListFailedRunnable(aError, aRequestId);
     NS_DispatchToMainThread(runnable);
 }
 
 #endif  // MOZ_WEBSMS_BACKEND
 
 NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(JNIEnv*, jclass)
+Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite(JNIEnv*, jclass)
 {
-    nsWindow::ScheduleComposite();
+    nsWindow::InvalidateAndScheduleComposite();
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition(JNIEnv*, jclass, jint width, jint height)
 {
     nsWindow::ScheduleResumeComposition(width, height);
 }
 
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2406,19 +2406,20 @@ nsWindow::SetCompositor(mozilla::layers:
                         mozilla::layers::CompositorChild* aCompositorChild)
 {
     sLayerManager = aLayerManager;
     sCompositorParent = aCompositorParent;
     sCompositorChild = aCompositorChild;
 }
 
 void
-nsWindow::ScheduleComposite()
+nsWindow::InvalidateAndScheduleComposite()
 {
     if (sCompositorParent) {
+        sCompositorParent->InvalidateOnCompositorThread();
         sCompositorParent->ScheduleRenderOnCompositorThread();
     }
 }
 
 bool
 nsWindow::IsCompositionPaused()
 {
     return sCompositorPaused;
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -150,17 +150,17 @@ public:
     virtual void DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect);
 
     virtual mozilla::layers::CompositorParent* NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight) override;
 
     static void SetCompositor(mozilla::layers::LayerManager* aLayerManager,
                               mozilla::layers::CompositorParent* aCompositorParent,
                               mozilla::layers::CompositorChild* aCompositorChild);
     static bool IsCompositionPaused();
-    static void ScheduleComposite();
+    static void InvalidateAndScheduleComposite();
     static void SchedulePauseComposition();
     static void ScheduleResumeComposition();
     static void ScheduleResumeComposition(int width, int height);
     static void ForceIsFirstPaint();
     static float ComputeRenderIntegrity();
     static mozilla::layers::APZCTreeManager* GetAPZCTreeManager();
     /* RootLayerTreeId() can only be called when GetAPZCTreeManager() returns non-null */
     static uint64_t RootLayerTreeId();