Bug 703601 - Fix a bunch of lifecycle issues with Flash on Android

This commit is contained in:
James Willcox 2012-02-15 15:34:31 -05:00
parent 934c37d63f
commit eddd45d9b9
12 changed files with 199 additions and 55 deletions

View File

@ -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;

View File

@ -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
};

View File

@ -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;
}

View File

@ -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

View File

@ -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<mozilla::AndroidMediaLayer> mLayer;
#endif
nsPluginNativeWindow *mPluginWindow;

View File

@ -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();
}

View File

@ -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() {

View File

@ -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;
}

View File

@ -569,6 +569,7 @@ public:
VISITED = 21,
NETWORK_CHANGED = 22,
PROXIMITY_EVENT = 23,
ACTIVITY_RESUMING = 24,
dummy_java_enum_list_end
};

View File

@ -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<void*, SurfaceData*>::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<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 */

View File

@ -41,15 +41,17 @@
#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();
@ -68,8 +70,15 @@ public:
mInverted = aInverted;
}
bool IsVisible() {
return mVisible;
}
void SetVisible(bool aVisible);
private:
bool mInverted;
bool mVisible;
class SurfaceData {
public:

View File

@ -370,6 +370,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
}
case AndroidGeckoEvent::ACTIVITY_STOPPING: {
if (curEvent->Flags() > 0)
break;
nsCOMPtr<nsIObserverService> 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<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
@ -413,6 +424,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
}
case AndroidGeckoEvent::ACTIVITY_START: {
if (curEvent->Flags() > 0)
break;
nsCOMPtr<nsIObserverService> 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<nsIObserverService> obsServ =
mozilla::services::GetObserverService();
obsServ->NotifyObservers(nsnull, "application-foreground", nsnull);
}
break;
}
default:
nsWindow::OnGlobalAndroidEvent(curEvent);
}