From eddd45d9b9f4ce7023a874f0f7bdadd231c38e4c Mon Sep 17 00:00:00 2001 From: James Willcox Date: Wed, 15 Feb 2012 15:34:31 -0500 Subject: [PATCH] Bug 703601 - Fix a bunch of lifecycle issues with Flash on Android --- dom/plugins/base/nsNPAPIPluginInstance.cpp | 39 ++++++++++++ dom/plugins/base/nsNPAPIPluginInstance.h | 9 +++ dom/plugins/base/nsPluginHost.cpp | 22 +++++++ dom/plugins/base/nsPluginInstanceOwner.cpp | 74 ++++++++++------------ dom/plugins/base/nsPluginInstanceOwner.h | 4 +- mobile/android/base/GeckoApp.java | 12 ++-- mobile/android/base/GeckoEvent.java | 25 ++++++-- widget/android/AndroidJavaWrappers.cpp | 8 +++ widget/android/AndroidJavaWrappers.h | 1 + widget/android/AndroidMediaLayer.cpp | 24 ++++++- widget/android/AndroidMediaLayer.h | 11 +++- widget/android/nsAppShell.cpp | 25 ++++++++ 12 files changed, 199 insertions(+), 55 deletions(-) diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index 0f90ad6ded6f..e77d98cca147 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -90,6 +90,7 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance(nsNPAPIPlugin* plugin) #ifdef MOZ_WIDGET_ANDROID mSurface(nsnull), mANPDrawingModel(0), + mOnScreen(true), #endif mRunning(NOT_STARTED), mWindowless(false), @@ -724,6 +725,44 @@ void nsNPAPIPluginInstance::SetEventModel(NPEventModel 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; diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index ca13f71accfa..38094948a7a6 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -147,6 +147,14 @@ public: #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); @@ -282,6 +290,7 @@ private: bool mUsePluginLayersPref; #ifdef MOZ_WIDGET_ANDROID void* mSurface; + bool mOnScreen; #endif }; diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 94d2bee76f10..fa9cecdb5c85 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -375,6 +375,10 @@ nsPluginHost::nsPluginHost() 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 @@ -3381,6 +3385,24 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject, 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; } diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 05e39f1c8d99..b67de18fbcf2 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -339,9 +339,8 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mWaitingForPaint = false; #ifdef MOZ_WIDGET_ANDROID - mOnScreen = false; mInverted = false; - mLayer = new AndroidMediaLayer(); + mLayer = nsnull; #endif } @@ -393,10 +392,7 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() mPluginWindow = nsnull; #ifdef MOZ_WIDGET_ANDROID - if (mLayer) { - delete mLayer; - mLayer = nsnull; - } + RemovePluginView(); #endif if (mInstance) { @@ -1688,22 +1684,6 @@ void nsPluginInstanceOwner::SendSize(int width, int 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(); @@ -1754,14 +1734,12 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) aRect.height); #endif - SendOnScreenEvent(true); - return true; } void nsPluginInstanceOwner::RemovePluginView() { - if (!mInstance || !mObjectFrame | !mOnScreen) + if (!mInstance || !mObjectFrame) return; void* surface = mInstance->GetJavaSurface(); @@ -1779,7 +1757,6 @@ void nsPluginInstanceOwner::RemovePluginView() "removePluginView", "(Landroid/view/View;)V"); env->CallStaticVoidMethod(cls, method, surface); - SendOnScreenEvent(false); } void nsPluginInstanceOwner::Invalidate() { @@ -2759,6 +2736,14 @@ nsPluginInstanceOwner::Destroy() 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); @@ -2867,7 +2852,7 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, const gfxRect& aFrameRect, const gfxRect& aDirtyRect) { - if (!mInstance || !mObjectFrame) + if (!mInstance || !mObjectFrame || !mPluginDocumentActiveState) return; PRInt32 model = mInstance->GetANPDrawingModel(); @@ -2880,11 +2865,13 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, } 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; } @@ -3587,17 +3574,6 @@ void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow) } 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) @@ -3625,6 +3601,26 @@ 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 diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index b328650548b6..4e4a029c2484 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -328,16 +328,14 @@ private: 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 mLayer; #endif nsPluginNativeWindow *mPluginWindow; diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index a06f1cfead98..86ccca0fd293 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -1507,7 +1507,8 @@ abstract public class GeckoApp if (tab == null) return; - tab.removePluginLayer(surface); + Layer layer = tab.removePluginLayer(surface); + hidePluginLayer(layer); } public void showSurface(Surface surface, int x, int y, @@ -2087,7 +2088,7 @@ abstract public class GeckoApp 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. @@ -2107,7 +2108,8 @@ abstract public class GeckoApp { 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(); @@ -2156,7 +2158,7 @@ abstract public class GeckoApp // 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(); } @@ -2173,7 +2175,7 @@ abstract public class GeckoApp Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onStart"); Log.i(LOGTAG, "start"); - GeckoAppShell.sendEventToGecko(GeckoEvent.createStartEvent()); + GeckoAppShell.sendEventToGecko(GeckoEvent.createStartEvent(mOwnActivityDepth)); super.onStart(); } diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 30701e3b3784..eb622c7770d1 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -87,6 +87,7 @@ public class GeckoEvent { 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; @@ -139,16 +140,28 @@ public class GeckoEvent { 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() { diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 3fcadd2cf945..2051fe78c09f 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -547,6 +547,14 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj) break; } + case ACTIVITY_STOPPING: + case ACTIVITY_START: + case ACTIVITY_PAUSING: + case ACTIVITY_RESUMING: { + mFlags = jenv->GetIntField(jobj, jFlagsField); + break; + } + default: break; } diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index 751c5be6e031..de02c6a40dbd 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -569,6 +569,7 @@ public: VISITED = 21, NETWORK_CHANGED = 22, PROXIMITY_EVENT = 23, + ACTIVITY_RESUMING = 24, dummy_java_enum_list_end }; diff --git a/widget/android/AndroidMediaLayer.cpp b/widget/android/AndroidMediaLayer.cpp index 572dadcc46b0..d5691c3c29f5 100644 --- a/widget/android/AndroidMediaLayer.cpp +++ b/widget/android/AndroidMediaLayer.cpp @@ -45,7 +45,7 @@ namespace mozilla { AndroidMediaLayer::AndroidMediaLayer() - : mInverted(false) { + : mInverted(false), mVisible(true) { } AndroidMediaLayer::~AndroidMediaLayer() { @@ -132,6 +132,8 @@ void AndroidMediaLayer::SetNativeWindowDimensions(void* aWindow, const gfxRect& } void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) { + if (!mVisible) + return; std::map::iterator it; @@ -152,4 +154,24 @@ void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) { } } +void AndroidMediaLayer::SetVisible(bool aVisible) { + if (aVisible == mVisible) + return; + + mVisible = aVisible; + if (mVisible) + return; + + // Hide all surfaces + std::map::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 */ diff --git a/widget/android/AndroidMediaLayer.h b/widget/android/AndroidMediaLayer.h index eb402743fc17..a5b01e6e080d 100644 --- a/widget/android/AndroidMediaLayer.h +++ b/widget/android/AndroidMediaLayer.h @@ -41,15 +41,17 @@ #include #include #include "gfxRect.h" +#include "nsISupports.h" namespace mozilla { class AndroidMediaLayer { public: - AndroidMediaLayer(); virtual ~AndroidMediaLayer(); + + NS_INLINE_DECL_REFCOUNTING(AndroidMediaLayer) void* GetNativeWindowForContent(); @@ -68,8 +70,15 @@ public: mInverted = aInverted; } + bool IsVisible() { + return mVisible; + } + + void SetVisible(bool aVisible); + private: bool mInverted; + bool mVisible; class SurfaceData { public: diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 581c21b9272f..e7e570ef7eab 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -370,6 +370,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) } case AndroidGeckoEvent::ACTIVITY_STOPPING: { + if (curEvent->Flags() > 0) + break; + nsCOMPtr obsServ = mozilla::services::GetObserverService(); NS_NAMED_LITERAL_STRING(minimize, "heap-minimize"); @@ -395,6 +398,14 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) } case AndroidGeckoEvent::ACTIVITY_PAUSING: { + if (curEvent->Flags() == 0) { + // We aren't transferring to one of our own activities, so set + // background status + nsCOMPtr 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 @@ -413,6 +424,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) } case AndroidGeckoEvent::ACTIVITY_START: { + if (curEvent->Flags() > 0) + break; + nsCOMPtr obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nsnull, "application-foreground", nsnull); @@ -483,6 +497,17 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) 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 obsServ = + mozilla::services::GetObserverService(); + obsServ->NotifyObservers(nsnull, "application-foreground", nsnull); + } + break; + } + default: nsWindow::OnGlobalAndroidEvent(curEvent); }