Bug 703601 - Fix a bunch of lifecycle issues with Flash on Android
authorJames Willcox <jwillcox@mozilla.com>
Wed, 15 Feb 2012 15:34:31 -0500
changeset 89803 7c004a416b2a00df22e8c4bab82b962b323f6e96
parent 89802 f086b7e2de8afa11bdbad8a1f17662db76fe2140
child 89804 28964353f814f13a8a7e0a573029e147ea2c51d1
push id783
push userlsblakk@mozilla.com
push dateTue, 24 Apr 2012 17:33:42 +0000
treeherdermozilla-beta@11faed19f136 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs703601
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 703601 - Fix a bunch of lifecycle issues with Flash on Android
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoEvent.java
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/AndroidMediaLayer.cpp
widget/android/AndroidMediaLayer.h
widget/android/nsAppShell.cpp
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -85,16 +85,17 @@ nsNPAPIPluginInstance::nsNPAPIPluginInst
     mDrawingModel(NPDrawingModelCoreGraphics),
 #else
     mDrawingModel(NPDrawingModelQuickDraw),
 #endif
 #endif
 #ifdef MOZ_WIDGET_ANDROID
     mSurface(nsnull),
     mANPDrawingModel(0),
+    mOnScreen(true),
 #endif
     mRunning(NOT_STARTED),
     mWindowless(false),
     mTransparent(false),
     mCached(false),
     mUsesDOMForCursor(false),
     mInPluginInitCall(false),
     mPlugin(plugin),
@@ -719,16 +720,54 @@ void nsNPAPIPluginInstance::SetEventMode
     return;
   }
 
   owner->SetEventModel(aModel);
 }
 #endif
 
 #if defined(MOZ_WIDGET_ANDROID)
+
+static void SendLifecycleEvent(nsNPAPIPluginInstance* aInstance, PRUint32 aAction)
+{
+  ANPEvent event;
+  event.inSize = sizeof(ANPEvent);
+  event.eventType = kLifecycle_ANPEventType;
+  event.data.lifecycle.action = aAction;
+  aInstance->HandleEvent(&event, nsnull);
+}
+
+void nsNPAPIPluginInstance::NotifyForeground(bool aForeground)
+{
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetForeground this=%p\n foreground=%d",this, aForeground));
+  if (RUNNING != mRunning)
+    return;
+
+  SendLifecycleEvent(this, aForeground ? kResume_ANPLifecycleAction : kPause_ANPLifecycleAction);
+}
+
+void nsNPAPIPluginInstance::NotifyOnScreen(bool aOnScreen)
+{
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetOnScreen this=%p\n onScreen=%d",this, aOnScreen));
+  if (RUNNING != mRunning || mOnScreen == aOnScreen)
+    return;
+
+  mOnScreen = aOnScreen;
+  SendLifecycleEvent(this, aOnScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction);
+}
+
+void nsNPAPIPluginInstance::MemoryPressure()
+{
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::MemoryPressure this=%p\n",this));
+  if (RUNNING != mRunning)
+    return;
+
+  SendLifecycleEvent(this, kFreeMemory_ANPLifecycleAction);
+}
+
 void nsNPAPIPluginInstance::SetANPDrawingModel(PRUint32 aModel)
 {
   mANPDrawingModel = aModel;
 }
 
 class SurfaceGetter : public nsRunnable {
 public:
   SurfaceGetter(nsNPAPIPluginInstance* aInstance, NPPluginFuncs* aPluginFunctions, NPP_t aNPP) : 
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -142,16 +142,24 @@ public:
   bool UsesDOMForCursor();
 
 #ifdef XP_MACOSX
   void SetDrawingModel(NPDrawingModel aModel);
   void SetEventModel(NPEventModel aModel);
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
+  void NotifyForeground(bool aForeground);
+  void NotifyOnScreen(bool aOnScreen);
+  void MemoryPressure();
+
+  bool IsOnScreen() {
+    return mOnScreen;
+  }
+
   PRUint32 GetANPDrawingModel() { return mANPDrawingModel; }
   void SetANPDrawingModel(PRUint32 aModel);
 
   // This stuff is for kSurface_ANPDrawingModel
   void* GetJavaSurface();
   void SetJavaSurface(void* aSurface);
   void RequestJavaSurface();
 #endif
@@ -277,12 +285,13 @@ private:
   // This is only valid when the plugin is actually stopped!
   mozilla::TimeStamp mStopTime;
 
   nsCOMPtr<nsIURI> mURI;
 
   bool mUsePluginLayersPref;
 #ifdef MOZ_WIDGET_ANDROID
   void* mSurface;
+  bool mOnScreen;
 #endif
 };
 
 #endif // nsNPAPIPluginInstance_h_
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -370,16 +370,20 @@ nsPluginHost::nsPluginHost()
     }
   }
 
   nsCOMPtr<nsIObserverService> obsService =
     mozilla::services::GetObserverService();
   if (obsService) {
     obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     obsService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false);
+#ifdef MOZ_WIDGET_ANDROID
+    obsService->AddObserver(this, "application-foreground", false);
+    obsService->AddObserver(this, "application-background", false);
+#endif
   }
 
 #ifdef PLUGIN_LOGGING
   nsPluginLogging::gNPNLog = PR_NewLogModule(NPN_LOG_NAME);
   nsPluginLogging::gNPPLog = PR_NewLogModule(NPP_LOG_NAME);
   nsPluginLogging::gPluginLog = PR_NewLogModule(PLUGIN_LOG_NAME);
 
   PR_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
@@ -3376,16 +3380,34 @@ NS_IMETHODIMP nsPluginHost::Observe(nsIS
     sInst->Release();
   }
   if (!nsCRT::strcmp(NS_PRIVATE_BROWSING_SWITCH_TOPIC, aTopic)) {
     // inform all active plugins of changed private mode state
     for (PRUint32 i = 0; i < mInstances.Length(); i++) {
       mInstances[i]->PrivateModeStateChanged();
     }
   }
+#ifdef MOZ_WIDGET_ANDROID
+  if (!nsCRT::strcmp("application-background", aTopic)) {
+    for(PRUint32 i = 0; i < mInstances.Length(); i++) {
+      mInstances[i]->NotifyForeground(false);
+    }
+  }
+  if (!nsCRT::strcmp("application-foreground", aTopic)) {
+    for(PRUint32 i = 0; i < mInstances.Length(); i++) {
+      if (mInstances[i]->IsOnScreen())
+        mInstances[i]->NotifyForeground(true);
+    }
+  }
+  if (!nsCRT::strcmp("memory-pressure", aTopic)) {
+    for(PRUint32 i = 0; i < mInstances.Length(); i++) {
+      mInstances[i]->MemoryPressure();
+    }
+  }
+#endif
   return NS_OK;
 }
 
 nsresult
 nsPluginHost::HandleBadPlugin(PRLibrary* aLibrary, nsNPAPIPluginInstance *aInstance)
 {
   // the |aLibrary| parameter is not needed anymore, after we added |aInstance| which
   // can also be used to look up the plugin name, but we cannot get rid of it because
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -334,19 +334,18 @@ nsPluginInstanceOwner::nsPluginInstanceO
   mEventModel = NPEventModelCocoa;
 #endif
   mUseAsyncRendering = false;
 #endif
 
   mWaitingForPaint = false;
 
 #ifdef MOZ_WIDGET_ANDROID
-  mOnScreen = false;
   mInverted = false;
-  mLayer = new AndroidMediaLayer();
+  mLayer = nsnull;
 #endif
 }
 
 nsPluginInstanceOwner::~nsPluginInstanceOwner()
 {
   PRInt32 cnt;
 
   if (mWaitingForPaint) {
@@ -388,20 +387,17 @@ nsPluginInstanceOwner::~nsPluginInstance
     NS_Free(mTagText);
     mTagText = nsnull;
   }
 
   PLUG_DeletePluginNativeWindow(mPluginWindow);
   mPluginWindow = nsnull;
 
 #ifdef MOZ_WIDGET_ANDROID
-  if (mLayer) {
-    delete mLayer;
-    mLayer = nsnull;
-  }
+  RemovePluginView();
 #endif
 
   if (mInstance) {
     mInstance->InvalidateOwner();
   }
 }
 
 NS_IMPL_ISUPPORTS3(nsPluginInstanceOwner,
@@ -1683,32 +1679,16 @@ void nsPluginInstanceOwner::SendSize(int
   event.eventType = kDraw_ANPEventType;
   event.data.draw.model = kOpenGL_ANPDrawingModel;
   event.data.draw.data.surfaceSize.width = width;
   event.data.draw.data.surfaceSize.height = height;
 
   mInstance->HandleEvent(&event, nsnull);
 }
 
-void nsPluginInstanceOwner::SendOnScreenEvent(bool onScreen)
-{
-  if (!mInstance)
-    return;
-
-  if ((onScreen && !mOnScreen) || (!onScreen && mOnScreen)) {
-    ANPEvent event;
-    event.inSize = sizeof(ANPEvent);
-    event.eventType = kLifecycle_ANPEventType;
-    event.data.lifecycle.action = onScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction;
-    mInstance->HandleEvent(&event, nsnull);
-
-    mOnScreen = onScreen;
-  }
-}
-
 bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect)
 {
   void* javaSurface = mInstance->GetJavaSurface();
   if (!javaSurface) {
     mInstance->RequestJavaSurface();
     return false;
   }
 
@@ -1749,24 +1729,22 @@ bool nsPluginInstanceOwner::AddPluginVie
                             method,
                             javaSurface,
                             aRect.x,
                             aRect.y,
                             aRect.width,
                             aRect.height);
 #endif
 
-  SendOnScreenEvent(true);
-
   return true;
 }
 
 void nsPluginInstanceOwner::RemovePluginView()
 {
-  if (!mInstance || !mObjectFrame | !mOnScreen)
+  if (!mInstance || !mObjectFrame)
     return;
 
   void* surface = mInstance->GetJavaSurface();
   if (!surface)
     return;
 
   JNIEnv* env = GetJNIForThread();
   if (!env)
@@ -1774,17 +1752,16 @@ void nsPluginInstanceOwner::RemovePlugin
 
   AndroidBridge::AutoLocalJNIFrame frame(env, 1);
 
   jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell");
   jmethodID method = env->GetStaticMethodID(cls,
                                             "removePluginView",
                                             "(Landroid/view/View;)V");
   env->CallStaticVoidMethod(cls, method, surface);
-  SendOnScreenEvent(false);
 }
 
 void nsPluginInstanceOwner::Invalidate() {
   NPRect rect;
   rect.left = rect.top = 0;
   rect.right = mPluginWindow->width;
   rect.bottom = mPluginWindow->height;
   InvalidateRect(&rect);
@@ -2754,16 +2731,24 @@ nsPluginInstanceOwner::Destroy()
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragexit"), this, true);
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragstart"), this, true);
   mContent->RemoveEventListener(NS_LITERAL_STRING("draggesture"), this, true);
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragend"), this, true);
 #if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
   mContent->RemoveEventListener(NS_LITERAL_STRING("text"), this, true);
 #endif
 
+#if MOZ_WIDGET_ANDROID
+  RemovePluginView();
+
+  if (mLayer)
+    mLayer->SetVisible(false);
+
+#endif
+
   if (mWidget) {
     if (mPluginWindow) {
       mPluginWindow->SetPluginWidget(nsnull);
     }
 
     nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
     if (pluginWidget) {
       pluginWidget->SetPluginInstanceOwner(nsnull);
@@ -2862,34 +2847,36 @@ void nsPluginInstanceOwner::Paint(const 
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 
 void nsPluginInstanceOwner::Paint(gfxContext* aContext,
                                   const gfxRect& aFrameRect,
                                   const gfxRect& aDirtyRect)
 {
-  if (!mInstance || !mObjectFrame)
+  if (!mInstance || !mObjectFrame || !mPluginDocumentActiveState)
     return;
 
   PRInt32 model = mInstance->GetANPDrawingModel();
 
   if (model == kSurface_ANPDrawingModel) {
     if (!AddPluginView(aFrameRect)) {
       Invalidate();
     }
     return;
   }
 
   if (model == kOpenGL_ANPDrawingModel) {
+    if (!mLayer)
+      mLayer = new AndroidMediaLayer();
+
     // FIXME: this is gross
     float zoomLevel = aFrameRect.width / (float)mPluginWindow->width;
     mLayer->UpdatePosition(aFrameRect, zoomLevel);
 
-    SendOnScreenEvent(true);
     SendSize((int)aFrameRect.width, (int)aFrameRect.height);
     return;
   }
 
   if (model != kBitmap_ANPDrawingModel)
     return;
 
 #ifdef ANP_BITMAP_DRAWING_MODEL
@@ -3582,27 +3569,16 @@ void nsPluginInstanceOwner::UpdateWindow
   mPluginWindow->clipRect.top = 0;
 
   if (mPluginWindowVisible && mPluginDocumentActiveState) {
     mPluginWindow->clipRect.right = mPluginWindow->width;
     mPluginWindow->clipRect.bottom = mPluginWindow->height;
   } else {
     mPluginWindow->clipRect.right = 0;
     mPluginWindow->clipRect.bottom = 0;
-#if 0 //MOZ_WIDGET_ANDROID
-    if (mInstance) {
-      PRInt32 model = mInstance->GetANPDrawingModel();
-
-      if (model == kSurface_ANPDrawingModel) {
-        RemovePluginView();
-      } else if (model == kOpenGL_ANPDrawingModel) {
-        HidePluginLayer();
-      }
-    }
-#endif
   }
 
   if (!aSetWindow)
     return;
 
   if (mPluginWindow->x               != oldWindow.x               ||
       mPluginWindow->y               != oldWindow.y               ||
       mPluginWindow->clipRect.left   != oldWindow.clipRect.left   ||
@@ -3620,16 +3596,36 @@ nsPluginInstanceOwner::UpdateWindowVisib
   UpdateWindowPositionAndClipRect(true);
 }
 
 void
 nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive)
 {
   mPluginDocumentActiveState = aIsActive;
   UpdateWindowPositionAndClipRect(true);
+
+#ifdef MOZ_WIDGET_ANDROID
+  if (mInstance) {
+    if (mLayer)
+      mLayer->SetVisible(mPluginDocumentActiveState);
+
+    if (!mPluginDocumentActiveState)
+      RemovePluginView();
+
+    mInstance->NotifyOnScreen(mPluginDocumentActiveState);
+
+    // This is, perhaps, incorrect. It is supposed to be sent
+    // when "the webview has paused or resumed". The side effect
+    // is that Flash video players pause or resume (if they were
+    // playing before) based on the value here. I personally think
+    // we want that on Android when switching to another tab, so
+    // that's why we call it here.
+    mInstance->NotifyForeground(mPluginDocumentActiveState);
+  }
+#endif
 }
 #endif // XP_MACOSX
 
 NS_IMETHODIMP
 nsPluginInstanceOwner::CallSetWindow()
 {
   if (mObjectFrame) {
     mObjectFrame->CallSetWindow(false);
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -323,26 +323,24 @@ private:
     nsIntSize size;
     return NS_SUCCEEDED(mInstance->GetImageSize(&size)) &&
     size == nsIntSize(mPluginWindow->width, mPluginWindow->height);
   }
   
   void FixUpURLS(const nsString &name, nsAString &value);
 #ifdef MOZ_WIDGET_ANDROID
   void SendSize(int width, int height);
-  void SendOnScreenEvent(bool onScreen);
 
   bool AddPluginView(const gfxRect& aRect);
   void RemovePluginView();
 
-  bool mOnScreen;
   bool mInverted;
 
   // For kOpenGL_ANPDrawingModel
-  mozilla::AndroidMediaLayer *mLayer;
+  nsRefPtr<mozilla::AndroidMediaLayer> mLayer;
 #endif 
  
   nsPluginNativeWindow       *mPluginWindow;
   nsRefPtr<nsNPAPIPluginInstance> mInstance;
   nsObjectFrame              *mObjectFrame;
   nsIContent                 *mContent; // WEAK, content owns us
   nsCString                   mDocumentBase;
   char                       *mTagText;
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1502,17 +1502,18 @@ abstract public class GeckoApp
     }
 
     public void destroySurface(Surface surface) {
         Tabs tabs = Tabs.getInstance();
         Tab tab = tabs.getSelectedTab();
         if (tab == null)
             return;
 
-        tab.removePluginLayer(surface);
+        Layer layer = tab.removePluginLayer(surface);
+        hidePluginLayer(layer);
     }
 
     public void showSurface(Surface surface, int x, int y,
                             int w, int h, boolean inverted, boolean blend,
                             String metadata) {
         Tabs tabs = Tabs.getInstance();
         Tab tab = tabs.getSelectedTab();
         if (tab == null)
@@ -2082,17 +2083,17 @@ abstract public class GeckoApp
     @Override
     public void onPause()
     {
         Log.i(LOGTAG, "pause");
 
         Runnable r = new SessionSnapshotRunnable(null);
         GeckoAppShell.getHandler().post(r);
 
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createPauseEvent());
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createPauseEvent(mOwnActivityDepth));
         // The user is navigating away from this activity, but nothing
         // has come to the foreground yet; for Gecko, we may want to
         // stop repainting, for example.
 
         // Whatever we do here should be fast, because we're blocking
         // the next activity from showing up until we finish.
 
         // onPause will be followed by either onResume or onStop.
@@ -2102,17 +2103,18 @@ abstract public class GeckoApp
         GeckoNetworkManager.getInstance().stop();
     }
 
     @Override
     public void onResume()
     {
         Log.i(LOGTAG, "resume");
         if (checkLaunchState(LaunchState.GeckoRunning))
-            GeckoAppShell.onResume();
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createResumeEvent(mOwnActivityDepth));
+
         // After an onPause, the activity is back in the foreground.
         // Undo whatever we did in onPause.
         super.onResume();
 
         /* We load the initial UI and wait until it is shown to the user
            to continue other initializations and loading about:home (if needed) */
         if (!mInitialized) {
             Bundle bundle = new Bundle();
@@ -2151,34 +2153,34 @@ abstract public class GeckoApp
         // without going through onDestroy.
         //
         // We might also get an onRestart after this; not sure what
         // that would mean for Gecko if we were to kill it here.
         // Instead, what we should do here is save prefs, session,
         // etc., and generally mark the profile as 'clean', and then
         // dirty it again if we get an onResume.
 
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createStoppingEvent());
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createStoppingEvent(mOwnActivityDepth));
         super.onStop();
     }
 
     @Override
     public void onRestart()
     {
         Log.i(LOGTAG, "restart");
         super.onRestart();
     }
 
     @Override
     public void onStart()
     {
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onStart");
 
         Log.i(LOGTAG, "start");
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createStartEvent());
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createStartEvent(mOwnActivityDepth));
         super.onStart();
     }
 
     @Override
     public void onDestroy()
     {
         Log.i(LOGTAG, "destroy");
 
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -82,16 +82,17 @@ public class GeckoEvent {
     private static final int SURFACE_DESTROYED = 14;
     private static final int GECKO_EVENT_SYNC = 15;
     private static final int ACTIVITY_START = 17;
     private static final int BROADCAST = 19;
     private static final int VIEWPORT = 20;
     private static final int VISITED = 21;
     private static final int NETWORK_CHANGED = 22;
     private static final int PROXIMITY_EVENT = 23;
+    private static final int ACTIVITY_RESUMING = 24;
 
     public static final int IME_COMPOSITION_END = 0;
     public static final int IME_COMPOSITION_BEGIN = 1;
     public static final int IME_SET_TEXT = 2;
     public static final int IME_GET_TEXT = 3;
     public static final int IME_DELETE_TEXT = 4;
     public static final int IME_SET_SELECTION = 5;
     public static final int IME_GET_SELECTION = 6;
@@ -134,26 +135,38 @@ public class GeckoEvent {
     public boolean mCanBeMetered;
 
     public int mNativeWindow;
 
     private GeckoEvent(int evType) {
         mType = evType;
     }
 
-    public static GeckoEvent createPauseEvent() {
-        return new GeckoEvent(ACTIVITY_PAUSING);
+    public static GeckoEvent createPauseEvent(int activityDepth) {
+        GeckoEvent event = new GeckoEvent(ACTIVITY_PAUSING);
+        event.mFlags = activityDepth > 0 ? 1 : 0;
+        return event;
     }
 
-    public static GeckoEvent createStoppingEvent() {
-        return new GeckoEvent(ACTIVITY_STOPPING);
+    public static GeckoEvent createResumeEvent(int activityDepth) {
+        GeckoEvent event = new GeckoEvent(ACTIVITY_RESUMING);
+        event.mFlags = activityDepth > 0 ? 1 : 0;
+        return event;
     }
 
-    public static GeckoEvent createStartEvent() {
-        return new GeckoEvent(ACTIVITY_START);
+    public static GeckoEvent createStoppingEvent(int activityDepth) {
+        GeckoEvent event = new GeckoEvent(ACTIVITY_STOPPING);
+        event.mFlags = activityDepth > 0 ? 1 : 0;
+        return event;
+    }
+
+    public static GeckoEvent createStartEvent(int activityDepth) {
+        GeckoEvent event = new GeckoEvent(ACTIVITY_START);
+        event.mFlags = activityDepth > 0 ? 1 : 0;
+        return event;
     }
 
     public static GeckoEvent createShutdownEvent() {
         return new GeckoEvent(ACTIVITY_SHUTDOWN);
     }
 
     public static GeckoEvent createSyncEvent() {
         return new GeckoEvent(GECKO_EVENT_SYNC);
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -542,16 +542,24 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jo
             break;
         }
 
         case PROXIMITY_EVENT: {
             mDistance = jenv->GetDoubleField(jobj, jDistanceField);
             break;
         }
 
+        case ACTIVITY_STOPPING:
+        case ACTIVITY_START:
+        case ACTIVITY_PAUSING:
+        case ACTIVITY_RESUMING: {
+            mFlags = jenv->GetIntField(jobj, jFlagsField);
+            break;
+        }
+
         default:
             break;
     }
 
 #ifndef DEBUG_ANDROID_EVENTS
     ALOG("AndroidGeckoEvent: %p : %d", (void*)jobj, mType);
 #endif
 }
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -564,16 +564,17 @@ public:
         GECKO_EVENT_SYNC = 15,
         FORCED_RESIZE = 16,
         ACTIVITY_START = 17,
         BROADCAST = 19,
         VIEWPORT = 20,
         VISITED = 21,
         NETWORK_CHANGED = 22,
         PROXIMITY_EVENT = 23,
+        ACTIVITY_RESUMING = 24,
         dummy_java_enum_list_end
     };
 
     enum {
         IME_COMPOSITION_END = 0,
         IME_COMPOSITION_BEGIN = 1,
         IME_SET_TEXT = 2,
         IME_GET_TEXT = 3,
--- a/widget/android/AndroidMediaLayer.cpp
+++ b/widget/android/AndroidMediaLayer.cpp
@@ -40,17 +40,17 @@
 #include "AndroidBridge.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AndroidMediaLayer" , ## args)
 
 
 namespace mozilla {
 
 AndroidMediaLayer::AndroidMediaLayer()
-  : mInverted(false) {
+  : mInverted(false), mVisible(true) {
 }
 
 AndroidMediaLayer::~AndroidMediaLayer() {
   if (mContentData.window) {
     AndroidBridge::Bridge()->ReleaseNativeWindow(mContentData.window);
     mContentData.window = NULL;
   }
 
@@ -127,16 +127,18 @@ void AndroidMediaLayer::SetNativeWindowD
   if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end())
     return;
 
   SurfaceData* data = mVideoSurfaces[aWindow];
   data->dimensions = aDimensions;
 }
 
 void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) {
+  if (!mVisible)
+    return;
 
   std::map<void*, SurfaceData*>::iterator it;
 
   if (EnsureContentSurface())
     AndroidBridge::Bridge()->ShowSurface(mContentData.surface, aRect, mInverted, true);
 
   for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) {
     SurfaceData* data = it->second;
@@ -147,9 +149,29 @@ void AndroidMediaLayer::UpdatePosition(c
     scaledDimensions.Scale(aZoomLevel);
 
     gfxRect videoRect(aRect.x + scaledDimensions.x, aRect.y + scaledDimensions.y,
                       scaledDimensions.width, scaledDimensions.height);
     AndroidBridge::Bridge()->ShowSurface(data->surface, videoRect, mInverted, false);
   }
 }
 
+void AndroidMediaLayer::SetVisible(bool aVisible) {
+  if (aVisible == mVisible)
+    return;
+
+  mVisible = aVisible;
+  if (mVisible)
+    return;
+
+  // Hide all surfaces
+  std::map<void*, SurfaceData*>::iterator it;
+
+  if (EnsureContentSurface())
+    AndroidBridge::Bridge()->HideSurface(mContentData.surface);
+
+  for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) {
+    SurfaceData* data = it->second;
+    AndroidBridge::Bridge()->HideSurface(data->surface);
+  }
+}
+
 } /* mozilla */
--- a/widget/android/AndroidMediaLayer.h
+++ b/widget/android/AndroidMediaLayer.h
@@ -36,25 +36,27 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef AndroidMediaLayer_h_
 #define AndroidMediaLayer_h_
 
 #include <map>
 #include <jni.h>
 #include "gfxRect.h"
+#include "nsISupports.h"
 
 namespace mozilla {
 
 class AndroidMediaLayer
 {
 public:
-
   AndroidMediaLayer();
   virtual ~AndroidMediaLayer();
+
+  NS_INLINE_DECL_REFCOUNTING(AndroidMediaLayer)
   
   void* GetNativeWindowForContent();
 
   void* RequestNativeWindowForVideo();
   void  ReleaseNativeWindowForVideo(void* aWindow);
 
   void SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions);
 
@@ -63,18 +65,25 @@ public:
   bool Inverted() {
     return mInverted;
   }
 
   void SetInverted(bool aInverted) {
     mInverted = aInverted;
   }
 
+  bool IsVisible() {
+    return mVisible;
+  }
+
+  void SetVisible(bool aVisible);
+
 private:
   bool mInverted;
+  bool mVisible;
 
   class SurfaceData {
     public:
       SurfaceData() :
         surface(NULL), window(NULL) {
       }
 
       SurfaceData(jobject aSurface, void* aWindow) :
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -365,16 +365,19 @@ nsAppShell::ProcessNextNativeEvent(bool 
         values.AppendElement(curEvent->Distance());
         
         hal::SensorData sdata(hal::SENSOR_PROXIMITY, PR_Now(), values);
         hal::NotifySensorChange(sdata);
         break;
     }
 
     case AndroidGeckoEvent::ACTIVITY_STOPPING: {
+        if (curEvent->Flags() > 0)
+            break;
+
         nsCOMPtr<nsIObserverService> obsServ =
             mozilla::services::GetObserverService();
         NS_NAMED_LITERAL_STRING(minimize, "heap-minimize");
         obsServ->NotifyObservers(nsnull, "memory-pressure", minimize.get());
         obsServ->NotifyObservers(nsnull, "application-background", nsnull);
 
         break;
     }
@@ -390,16 +393,24 @@ nsAppShell::ProcessNextNativeEvent(bool 
         obsServ->NotifyObservers(nsnull, "profile-before-change", context.get());
         nsCOMPtr<nsIAppStartup> appSvc = do_GetService("@mozilla.org/toolkit/app-startup;1");
         if (appSvc)
             appSvc->Quit(nsIAppStartup::eForceQuit);
         break;
     }
 
     case AndroidGeckoEvent::ACTIVITY_PAUSING: {
+        if (curEvent->Flags() == 0) {
+            // We aren't transferring to one of our own activities, so set
+            // background status
+            nsCOMPtr<nsIObserverService> obsServ =
+                mozilla::services::GetObserverService();
+            obsServ->NotifyObservers(nsnull, "application-background", nsnull);
+        }
+
         // We really want to send a notification like profile-before-change,
         // but profile-before-change ends up shutting some things down instead
         // of flushing data
         nsIPrefService* prefs = Preferences::GetService();
         if (prefs) {
             // reset the crash loop state
             nsCOMPtr<nsIPrefBranch> prefBranch;
             prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch));
@@ -408,16 +419,19 @@ nsAppShell::ProcessNextNativeEvent(bool 
 
             prefs->SavePrefFile(nsnull);
         }
 
         break;
     }
 
     case AndroidGeckoEvent::ACTIVITY_START: {
+        if (curEvent->Flags() > 0)
+            break;
+
         nsCOMPtr<nsIObserverService> obsServ =
             mozilla::services::GetObserverService();
         obsServ->NotifyObservers(nsnull, "application-foreground", nsnull);
 
         break;
     }
 
     case AndroidGeckoEvent::VIEWPORT:
@@ -478,16 +492,27 @@ nsAppShell::ProcessNextNativeEvent(bool 
     }
 
     case AndroidGeckoEvent::NETWORK_CHANGED: {
         hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->Bandwidth(),
                                                          curEvent->CanBeMetered()));
         break;
     }
 
+    case AndroidGeckoEvent::ACTIVITY_RESUMING: {
+        if (curEvent->Flags() == 0) {
+            // We didn't return from one of our own activities, so restore
+            // to foreground status
+            nsCOMPtr<nsIObserverService> obsServ =
+                mozilla::services::GetObserverService();
+            obsServ->NotifyObservers(nsnull, "application-foreground", nsnull);
+        }
+        break;
+    }
+
     default:
         nsWindow::OnGlobalAndroidEvent(curEvent);
     }
 
     EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type());
 
     return true;
 }