bug 755070 - Scrolling causes after paint notifications which causes screenshotting which causes checkerboarding, screenshot only on idle r=kats
authorBrad Lassey <blassey@mozilla.com>
Mon, 04 Jun 2012 11:56:30 -0400
changeset 95749 12809c23e838dd1e332ddda38a003a7f2d1f0e2b
parent 95748 fc94f4339da76d758d62aa447eb7b420bd1caaba
child 95750 6cf625ab6acde3b172a53c3fb7055d003cda18e1
push idunknown
push userunknown
push dateunknown
reviewerskats
bugs755070
milestone15.0a1
bug 755070 - Scrolling causes after paint notifications which causes screenshotting which causes checkerboarding, screenshot only on idle r=kats
ipc/chromium/src/base/message_loop.cc
ipc/chromium/src/base/message_loop.h
widget/android/nsAppShell.cpp
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -251,16 +251,24 @@ void MessageLoop::PostNonNestableTask(
   PostTask_Helper(from_here, task, 0, false);
 }
 
 void MessageLoop::PostNonNestableDelayedTask(
     const tracked_objects::Location& from_here, Task* task, int delay_ms) {
   PostTask_Helper(from_here, task, delay_ms, false);
 }
 
+void MessageLoop::PostIdleTask(
+    const tracked_objects::Location& from_here, Task* task) {
+  DCHECK(current() == this);
+  task->SetBirthPlace(from_here);
+  PendingTask pending_task(task, false);
+  deferred_non_nestable_work_queue_.push(pending_task);
+}
+
 // Possibly called on a background thread!
 void MessageLoop::PostTask_Helper(
     const tracked_objects::Location& from_here, Task* task, int delay_ms,
     bool nestable) {
   task->SetBirthPlace(from_here);
 
   PendingTask pending_task(task, nestable);
 
--- a/ipc/chromium/src/base/message_loop.h
+++ b/ipc/chromium/src/base/message_loop.h
@@ -116,16 +116,20 @@ public:
       const tracked_objects::Location& from_here, Task* task, int delay_ms);
 
   void PostNonNestableTask(
       const tracked_objects::Location& from_here, Task* task);
 
   void PostNonNestableDelayedTask(
       const tracked_objects::Location& from_here, Task* task, int delay_ms);
 
+  // PostIdleTask is not thread safe and should be called on this thread
+  void PostIdleTask(
+      const tracked_objects::Location& from_here, Task* task);
+
   // A variant on PostTask that deletes the given object.  This is useful
   // if the object needs to live until the next run of the MessageLoop (for
   // example, deleting a RenderProcessHost from within an IPC callback is not
   // good).
   //
   // NOTE: This method may be called on any thread.  The object will be deleted
   // on the thread that executes MessageLoop::Run().  If this is not the same
   // as the thread that calls PostDelayedTask(FROM_HERE, ), then T MUST inherit
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Make sure the order of included headers
 #include "base/basictypes.h"
 #include "nspr/prtypes.h"
+#include "base/message_loop.h"
+#include "base/task.h"
 
 #include "mozilla/Hal.h"
 #include "nsAppShell.h"
 #include "nsWindow.h"
 #include "nsThreadUtils.h"
 #include "nsICommandLineRunner.h"
 #include "nsIObserverService.h"
 #include "nsIAppStartup.h"
@@ -58,16 +60,44 @@ PRLogModuleInfo *gWidgetLog = nsnull;
 
 nsIGeolocationUpdate *gLocationCallback = nsnull;
 nsAutoPtr<mozilla::AndroidGeckoEvent> gLastSizeChange;
 
 nsAppShell *nsAppShell::gAppShell = nsnull;
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver)
 
+class ScreenshotRunnable : public nsRunnable {
+public:
+    ScreenshotRunnable(nsIAndroidBrowserApp* aBrowserApp, int aTabId, nsTArray<nsIntPoint>& aPoints, int aToken):
+        mBrowserApp(aBrowserApp), mTabId(aTabId), mPoints(aPoints), mToken(aToken) {}
+
+    virtual nsresult Run() {
+        nsCOMPtr<nsIDOMWindow> domWindow;
+        nsCOMPtr<nsIBrowserTab> tab;
+        mBrowserApp->GetBrowserTab(mTabId, getter_AddRefs(tab));
+        if (!tab)
+            return NS_OK;
+
+        tab->GetWindow(getter_AddRefs(domWindow));
+        if (!domWindow)
+            return NS_OK;
+
+        float scale = 1.0;
+        NS_ASSERTION(mPoints.Length() == 4, "Screenshot event does not have enough coordinates");
+
+        AndroidBridge::Bridge()->TakeScreenshot(domWindow, mPoints[0].x, mPoints[0].y, mPoints[1].x, mPoints[1].y, mPoints[3].x, mPoints[3].y, mTabId, scale, mToken);
+        return NS_OK;
+    }
+private:
+    nsCOMPtr<nsIAndroidBrowserApp> mBrowserApp;
+    nsTArray<nsIntPoint> mPoints;
+    int mTabId, mToken;
+};
+
 class AfterPaintListener : public nsIDOMEventListener {
   public:
     NS_DECL_ISUPPORTS
 
     void Register(nsIDOMWindow* window) {
         if (mEventTarget)
             Unregister();
         nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
@@ -420,31 +450,22 @@ nsAppShell::ProcessNextNativeEvent(bool 
         if (!mBrowserApp)
             break;
 
         AndroidBridge* bridge = AndroidBridge::Bridge();
         if (!bridge)
             break;
 
         PRInt32 token = curEvent->Flags();
-
-        nsCOMPtr<nsIDOMWindow> domWindow;
-        nsCOMPtr<nsIBrowserTab> tab;
-        mBrowserApp->GetBrowserTab(curEvent->MetaState(), getter_AddRefs(tab));
-        if (!tab)
-            break;
-
-        tab->GetWindow(getter_AddRefs(domWindow));
-        if (!domWindow)
-            break;
-
-        float scale = 1.0;
+        PRInt32 tabId = curEvent->MetaState();
         nsTArray<nsIntPoint> points = curEvent->Points();
-        NS_ASSERTION(points.Length() == 4, "Screenshot event does not have enough coordinates");
-        bridge->TakeScreenshot(domWindow, points[0].x, points[0].y, points[1].x, points[1].y, points[3].x, points[3].y, curEvent->MetaState(), scale, curEvent->Flags());
+        nsCOMPtr<ScreenshotRunnable> sr = 
+            new ScreenshotRunnable(mBrowserApp, tabId, points, token);
+        MessageLoop::current()->PostIdleTask(
+            FROM_HERE, NewRunnableMethod(sr.get(), &ScreenshotRunnable::Run));
         break;
     }
 
     case AndroidGeckoEvent::VIEWPORT:
     case AndroidGeckoEvent::BROADCAST: {
 
         if (curEvent->Characters().Length() == 0)
             break;