Bug 727421 - Implement full screen support for Flash on Android r=blassey
☠☠ backed out by 76fa67b06379 ☠ ☠
authorJames Willcox <jwillcox@mozilla.com>
Wed, 30 May 2012 10:29:16 -0400
changeset 95295 814d564578d1852f2089136441fd46be6815bf3d
parent 95294 d047fae0339cb1753da66694c7e1955891a89048
child 95296 be6973c48222fda110e1d599deada3813eca6858
push id810
push usermihai.sucan@gmail.com
push dateThu, 31 May 2012 18:49:06 +0000
treeherderfx-team@183aed68ea36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey
bugs727421
milestone15.0a1
Bug 727421 - Implement full screen support for Flash on Android r=blassey
dom/plugins/base/Makefile.in
dom/plugins/base/android/ANPNativeWindow.cpp
dom/plugins/base/android/ANPSurface.cpp
dom/plugins/base/android/ANPWindow.cpp
dom/plugins/base/android/android_npapi.h
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mozglue/android/APKOpen.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJNI.cpp
--- a/dom/plugins/base/Makefile.in
+++ b/dom/plugins/base/Makefile.in
@@ -97,16 +97,17 @@ endif
 endif
 endif
 endif
 endif
 
 LOCAL_INCLUDES += \
   -DSK_BUILD_FOR_ANDROID_NDK \
   -I$(topsrcdir)/widget/android \
+  -I$(topsrcdir)/widget/xpwidgets \
   -I$(topsrcdir)/xpcom/base/ \
   -I$(topsrcdir)/gfx/skia/include/core \
   -I$(topsrcdir)/gfx/skia/include/config \
   $(MOZ_CAIRO_CFLAGS) \
   $(MOZ_PIXMAN_CFLAGS) \
   $(NULL)
 
 include $(topsrcdir)/dom/dom-config.mk
--- a/dom/plugins/base/android/ANPNativeWindow.cpp
+++ b/dom/plugins/base/android/ANPNativeWindow.cpp
@@ -20,17 +20,17 @@ using namespace mozilla;
 #define ASSIGN(obj, name)   (obj)->name = anp_native_window_##name
 
 static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
   nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
 
   return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
 }
 
-static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
+static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {  
   nsRefPtr<nsPluginInstanceOwner> owner;
   if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
     return NULL;
 
   ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent();
   owner->Invalidate();
 
   return window;
--- a/dom/plugins/base/android/ANPSurface.cpp
+++ b/dom/plugins/base/android/ANPSurface.cpp
@@ -138,31 +138,40 @@ static bool init() {
   if (!handle) {
     LOG("Failed to open libsurfaceflinger_client.so");
     return false;
   }
 
   gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb");
   gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv");
 
+
+  if (!gSurfaceFunctions.lock) {
+    // Stuff changed in 3.0/4.0
+    handle = dlopen("libgui.so", RTLD_LAZY);
+    gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE");
+    gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv");
+  }
+
   handle = dlopen("libui.so", RTLD_LAZY);
   if (!handle) {
     LOG("Failed to open libui.so");
     return false;
   }
 
   gSurfaceFunctions.regionConstructor = (void* (*)(void*))dlsym(handle, "_ZN7android6RegionC1Ev");
   gSurfaceFunctions.setRegion = (void (*)(void*, ARect const&))dlsym(handle, "_ZN7android6Region3setERKNS_4RectE");
 
   gSurfaceFunctions.initialized = (gSurfaceFunctions.lock && gSurfaceFunctions.unlockAndPost &&
                                    gSurfaceFunctions.regionConstructor && gSurfaceFunctions.setRegion);
   LOG("Initialized? %d\n", gSurfaceFunctions.initialized);
   return gSurfaceFunctions.initialized;
 }
 
+// FIXME: All of this should be changed to use the equivalent things in AndroidBridge, bug 758612
 static bool anp_surface_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) {
   if (!bitmap || !surfaceView) {
     return false;
   }
 
   void* surface = getSurface(env, surfaceView);
 
   if (!bitmap || !surface) {
--- a/dom/plugins/base/android/ANPWindow.cpp
+++ b/dom/plugins/base/android/ANPWindow.cpp
@@ -1,27 +1,36 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
+#include "base/basictypes.h"
 #include "assert.h"
 #include "ANPBase.h"
 #include <android/log.h>
 #include "AndroidBridge.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsWindow.h"
+#include "mozilla/dom/ScreenOrientation.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_window_##name
 
 using namespace mozilla;
 using namespace mozilla::widget;
+using namespace mozilla::dom;
+
+static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
+}
 
 void
 anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count)
 {
   NOT_IMPLEMENTED();
 }
 
 void
@@ -49,37 +58,45 @@ anp_window_showKeyboard(NPP instance, bo
   }
 
   window->SetInputContext(context, action);
 }
 
 void
 anp_window_requestFullScreen(NPP instance)
 {
-  NOT_IMPLEMENTED();
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  nsRefPtr<nsPluginInstanceOwner> owner;
+  if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) {
+    return;
+  }
+
+  owner->RequestFullScreen();
 }
 
 void
 anp_window_exitFullScreen(NPP instance)
 {
-  NOT_IMPLEMENTED();
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  nsRefPtr<nsPluginInstanceOwner> owner;
+  if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) {
+    return;
+  }
+
+  owner->ExitFullScreen();
 }
 
 void
 anp_window_requestCenterFitZoom(NPP instance)
 {
   NOT_IMPLEMENTED();
 }
 
-static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
-  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
-
-  return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
-}
-
 ANPRectI
 anp_window_visibleRect(NPP instance)
 {
   ANPRectI rect = { 0, 0, 0, 0 };
 
   nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
 
   nsRefPtr<nsPluginInstanceOwner> owner;
@@ -93,17 +110,39 @@ anp_window_visibleRect(NPP instance)
   rect.right = visibleRect.x + visibleRect.width;
   rect.bottom = visibleRect.y + visibleRect.height;
 
   return rect;
 }
 
 void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation)
 {
-  NOT_IMPLEMENTED();
+  short newOrientation;
+
+  // Convert to the ActivityInfo equivalent
+  switch (orientation) {
+    case kFixedLandscape_ANPScreenOrientation:
+      newOrientation = eScreenOrientation_LandscapePrimary;
+      break;
+    case kFixedPortrait_ANPScreenOrientation:
+      newOrientation = eScreenOrientation_PortraitPrimary;
+      break;
+    case kLandscape_ANPScreenOrientation:
+      newOrientation = eScreenOrientation_Landscape;
+      break;
+    case kPortrait_ANPScreenOrientation:
+      newOrientation = eScreenOrientation_Portrait;
+      break;
+    default:
+      newOrientation = eScreenOrientation_None;
+      break;
+  }
+
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+  pinst->SetFullScreenOrientation(newOrientation);
 }
 
 void InitWindowInterface(ANPWindowInterfaceV0 *i) {
   _assert(i->inSize == sizeof(*i));
   ASSIGN(i, setVisibleRects);
   ASSIGN(i, clearVisibleRects);
   ASSIGN(i, showKeyboard);
   ASSIGN(i, requestFullScreen);
--- a/dom/plugins/base/android/android_npapi.h
+++ b/dom/plugins/base/android/android_npapi.h
@@ -693,16 +693,38 @@ struct ANPWindowInterfaceV0 : ANPInterfa
 struct ANPWindowInterfaceV1 : ANPWindowInterfaceV0 {
     /** Returns a rectangle representing the visible area of the plugin on
         screen. The coordinates are relative to the size of the plugin in the
         document and therefore will never be negative or exceed the plugin's size.
      */
     ANPRectI (*visibleRect)(NPP instance);
 };
 
+enum ANPScreenOrientations {
+    /** No preference specified: let the system decide the best orientation.
+     */
+    kDefault_ANPScreenOrientation        = 0,
+    /** Would like to have the screen in a landscape orientation, but it will
+        not allow for 180 degree rotations.
+     */
+    kFixedLandscape_ANPScreenOrientation = 1,
+    /** Would like to have the screen in a portrait orientation, but it will
+        not allow for 180 degree rotations.
+     */
+    kFixedPortrait_ANPScreenOrientation  = 2,
+    /** Would like to have the screen in landscape orientation, but can use the
+        sensor to change which direction the screen is facing.
+     */
+    kLandscape_ANPScreenOrientation      = 3,
+    /** Would like to have the screen in portrait orientation, but can use the
+        sensor to change which direction the screen is facing.
+     */
+    kPortrait_ANPScreenOrientation       = 4
+};
+
 typedef int32_t ANPScreenOrientation;
 
 struct ANPWindowInterfaceV2 : ANPWindowInterfaceV1 {
     /** Called when the plugin wants to specify a particular screen orientation
         when entering into full screen mode. The orientation must be set prior
         to entering into full screen.  After entering full screen any subsequent
         changes will be updated the next time the plugin goes full screen.
      */
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1,13 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
+#ifdef MOZ_WIDGET_ANDROID
+// For ScreenOrientation.h
+#include "base/basictypes.h"
+#endif
+
 #include "prlog.h"
 #include "prmem.h"
 #include "nscore.h"
 #include "prenv.h"
 
 #include "nsNPAPIPluginInstance.h"
 #include "nsNPAPIPlugin.h"
 #include "nsNPAPIPluginStreamListener.h"
@@ -30,16 +35,17 @@
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "ANPBase.h"
 #include <android/log.h>
 #include "android_npapi.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/CondVar.h"
 #include "AndroidBridge.h"
+#include "mozilla/dom/ScreenOrientation.h"
 
 class PluginEventRunnable : public nsRunnable
 {
 public:
   PluginEventRunnable(nsNPAPIPluginInstance* instance, ANPEvent* event)
     : mInstance(instance), mEvent(*event), mCanceled(false) {}
 
   virtual nsresult Run() {
@@ -69,16 +75,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS0(nsNPAPIPlu
 
 nsNPAPIPluginInstance::nsNPAPIPluginInstance()
   :
     mDrawingModel(kDefaultDrawingModel),
 #ifdef MOZ_WIDGET_ANDROID
     mSurface(nsnull),
     mANPDrawingModel(0),
     mOnScreen(true),
+    mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary),
 #endif
     mRunning(NOT_STARTED),
     mWindowless(false),
     mTransparent(false),
     mCached(false),
     mUsesDOMForCursor(false),
     mInPluginInitCall(false),
     mPlugin(nsnull),
@@ -738,73 +745,39 @@ void nsNPAPIPluginInstance::MemoryPressu
 {
   PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::MemoryPressure this=%p\n",this));
   if (RUNNING != mRunning)
     return;
 
   SendLifecycleEvent(this, kFreeMemory_ANPLifecycleAction);
 }
 
+void nsNPAPIPluginInstance::NotifyFullScreen(bool aFullScreen)
+{
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::NotifyFullScreen this=%p\n",this));
+
+  if (RUNNING != mRunning)
+    return;
+
+  SendLifecycleEvent(this, aFullScreen ? kEnterFullScreen_ANPLifecycleAction : kExitFullScreen_ANPLifecycleAction);
+}
+
 void nsNPAPIPluginInstance::SetANPDrawingModel(PRUint32 aModel)
 {
   mANPDrawingModel = aModel;
 }
 
-class SurfaceGetter : public nsRunnable {
-public:
-  SurfaceGetter(nsNPAPIPluginInstance* aInstance, NPPluginFuncs* aPluginFunctions, NPP_t aNPP) : 
-    mInstance(aInstance), mPluginFunctions(aPluginFunctions), mNPP(aNPP) {
-  }
-  ~SurfaceGetter() {
-  }
-  nsresult Run() {
-    void* surface;
-    (*mPluginFunctions->getvalue)(&mNPP, kJavaSurface_ANPGetValue, &surface);
-    mInstance->SetJavaSurface(surface);
-    return NS_OK;
-  }
-  void RequestSurface() {
-    JNIEnv* env = GetJNIForThread();
-    if (!env)
-      return;
-
-    if (!mozilla::AndroidBridge::Bridge()) {
-      PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance null AndroidBridge"));
-      return;
-    }
-    mozilla::AndroidBridge::Bridge()->PostToJavaThread(env, this);
-  }
-private:
-  nsNPAPIPluginInstance* mInstance;
-  NPP_t mNPP;
-  NPPluginFuncs* mPluginFunctions;
-};
-
-
 void* nsNPAPIPluginInstance::GetJavaSurface()
 {
-  if (mANPDrawingModel != kSurface_ANPDrawingModel)
+  void* surface = nsnull; 
+  nsresult rv = GetValueFromPlugin(kJavaSurface_ANPGetValue, &surface);
+  if (NS_FAILED(rv))
     return nsnull;
-  
-  return mSurface;
-}
 
-void nsNPAPIPluginInstance::SetJavaSurface(void* aSurface)
-{
-  mSurface = aSurface;
-}
-
-void nsNPAPIPluginInstance::RequestJavaSurface()
-{
-  if (mSurfaceGetter.get())
-    return;
-
-  mSurfaceGetter = new SurfaceGetter(this, mPlugin->PluginFuncs(), mNPP);
-
-  ((SurfaceGetter*)mSurfaceGetter.get())->RequestSurface();
+  return surface;
 }
 
 void nsNPAPIPluginInstance::PostEvent(void* event)
 {
   PluginEventRunnable *r = new PluginEventRunnable(this, (ANPEvent*)event);
   mPostedEvents.AppendElement(nsRefPtr<PluginEventRunnable>(r));
 
   NS_DispatchToMainThread(r);
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -118,30 +118,33 @@ public:
 #ifdef XP_MACOSX
   void SetEventModel(NPEventModel aModel);
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
   void NotifyForeground(bool aForeground);
   void NotifyOnScreen(bool aOnScreen);
   void MemoryPressure();
+  void NotifyFullScreen(bool aFullScreen);
 
   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();
 
   void PostEvent(void* event);
+
+  // These are really mozilla::dom::ScreenOrientation, but it's
+  // difficult to include that here
+  PRUint32 FullScreenOrientation() { return mFullScreenOrientation; }
+  void SetFullScreenOrientation(PRUint32 orientation) { mFullScreenOrientation = orientation; }
 #endif
 
   nsresult NewStreamListener(const char* aURL, void* notifyData,
                              nsNPAPIPluginStreamListener** listener);
 
   nsNPAPIPluginInstance();
   virtual ~nsNPAPIPluginInstance();
 
@@ -216,16 +219,18 @@ protected:
 #ifdef MOZ_WIDGET_ANDROID
   PRUint32 mANPDrawingModel;
   nsCOMPtr<nsIRunnable> mSurfaceGetter;
 
   friend class PluginEventRunnable;
 
   nsTArray<nsCOMPtr<PluginEventRunnable>> mPostedEvents;
   void PopPostedEvent(PluginEventRunnable* r);
+
+  PRUint32 mFullScreenOrientation;
 #endif
 
   enum {
     NOT_STARTED,
     RUNNING,
     DESTROYING,
     DESTROYED
   } mRunning;
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -82,16 +82,20 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 #include <gdk/gdkx.h>
 #include <gtk/gtk.h>
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "ANPBase.h"
 #include "AndroidBridge.h"
 #include "AndroidMediaLayer.h"
+#include "nsWindow.h"
+
+static nsPluginInstanceOwner* sFullScreenInstance = nsnull;
+
 using namespace mozilla::dom;
 
 #include <android/log.h>
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #endif
 
 using namespace mozilla;
 
@@ -300,17 +304,19 @@ nsPluginInstanceOwner::nsPluginInstanceO
 #endif
   mUseAsyncRendering = false;
 #endif
 
   mWaitingForPaint = false;
 
 #ifdef MOZ_WIDGET_ANDROID
   mInverted = false;
+  mFullScreen = false;
   mLayer = nsnull;
+  mJavaView = nsnull;
 #endif
 }
 
 nsPluginInstanceOwner::~nsPluginInstanceOwner()
 {
   PRInt32 cnt;
 
   if (mWaitingForPaint) {
@@ -1720,51 +1726,100 @@ 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);
 }
 
-bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect)
+bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect /* = gfxRect(0, 0, 0, 0) */)
 {
-  void* javaSurface = mInstance->GetJavaSurface();
-  if (!javaSurface) {
-    mInstance->RequestJavaSurface();
-    return false;
+  if (!mJavaView) {
+    mJavaView = mInstance->GetJavaSurface();
+  
+    if (!mJavaView)
+      return false;
+
+    mJavaView = (void*)AndroidBridge::GetJNIEnv()->NewGlobalRef((jobject)mJavaView);
   }
 
   if (AndroidBridge::Bridge())
-    AndroidBridge::Bridge()->AddPluginView((jobject)javaSurface, aRect);
+    AndroidBridge::Bridge()->AddPluginView((jobject)mJavaView, aRect, mFullScreen, mInstance->FullScreenOrientation());
+
+  if (mFullScreen)
+    sFullScreenInstance = this;
 
   return true;
 }
 
 void nsPluginInstanceOwner::RemovePluginView()
 {
-  if (!mInstance)
-    return;
-
-  void* surface = mInstance->GetJavaSurface();
-  if (!surface)
+  if (!mInstance || !mJavaView)
     return;
 
   if (AndroidBridge::Bridge())
-    AndroidBridge::Bridge()->RemovePluginView((jobject)surface);
+    AndroidBridge::Bridge()->RemovePluginView((jobject)mJavaView, mFullScreen);
+
+  AndroidBridge::GetJNIEnv()->DeleteGlobalRef((jobject)mJavaView);
+  mJavaView = nsnull;
+
+  if (mFullScreen)
+    sFullScreenInstance = nsnull;
 }
 
 void nsPluginInstanceOwner::Invalidate() {
   NPRect rect;
   rect.left = rect.top = 0;
   rect.right = mPluginWindow->width;
   rect.bottom = mPluginWindow->height;
   InvalidateRect(&rect);
 }
 
+void nsPluginInstanceOwner::RequestFullScreen() {
+  if (mFullScreen)
+    return;
+
+  // Remove whatever view we currently have (if any, fullscreen or otherwise)
+  RemovePluginView();
+
+  mFullScreen = true;
+  AddPluginView();
+
+  mInstance->NotifyFullScreen(mFullScreen);
+}
+
+void nsPluginInstanceOwner::ExitFullScreen() {
+  if (!mFullScreen)
+    return;
+
+  RemovePluginView();
+
+  mFullScreen = false;
+  
+  PRInt32 model = mInstance->GetANPDrawingModel();
+
+  if (model == kSurface_ANPDrawingModel) {
+    // We need to invalidate the plugin rect so Paint() gets called above.
+    // This will cause the view to be re-added. Gross.
+    Invalidate();
+  }
+
+  mInstance->NotifyFullScreen(mFullScreen);
+}
+
+void nsPluginInstanceOwner::ExitFullScreen(jobject view) {
+  JNIEnv* env = AndroidBridge::GetJNIEnv();
+
+  if (env && sFullScreenInstance && sFullScreenInstance->mInstance &&
+      env->IsSameObject(view, (jobject)sFullScreenInstance->mInstance->GetJavaSurface())) {
+    sFullScreenInstance->ExitFullScreen();
+  } 
+}
+
 #endif
 
 nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent)
 {
 #ifdef MOZ_WIDGET_ANDROID
   {
     ANPEvent event;
     event.inSize = sizeof(ANPEvent);
@@ -2866,17 +2921,17 @@ GetOffsetRootContent(nsIFrame* aFrame)
 
   return offset;
 }
 
 void nsPluginInstanceOwner::Paint(gfxContext* aContext,
                                   const gfxRect& aFrameRect,
                                   const gfxRect& aDirtyRect)
 {
-  if (!mInstance || !mObjectFrame || !mPluginDocumentActiveState)
+  if (!mInstance || !mObjectFrame || !mPluginDocumentActiveState || mFullScreen)
     return;
 
   PRInt32 model = mInstance->GetANPDrawingModel();
 
   // Get the offset of the content relative to the page
   nsRect bounds = mObjectFrame->GetContentRectRelativeToSelf() + GetOffsetRootContent(mObjectFrame);
   nsIntRect intBounds = bounds.ToNearestPixels(mObjectFrame->PresContext()->AppUnitsPerDevPixel());
   gfxRect pluginRect(intBounds);
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -272,36 +272,45 @@ public:
     return mInverted;
   }
 
   mozilla::AndroidMediaLayer* Layer() {
     return mLayer;
   }
 
   void Invalidate();
+
+  void RequestFullScreen();
+  void ExitFullScreen();
+
+  // Called from AndroidJNI when we removed the fullscreen view.
+  static void ExitFullScreen(jobject view);
 #endif
   
 private:
   
   // return FALSE if LayerSurface dirty (newly created and don't have valid plugin content yet)
   bool IsUpToDate()
   {
     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);
 
-  bool AddPluginView(const gfxRect& aRect);
+  bool AddPluginView(const gfxRect& aRect = gfxRect(0, 0, 0, 0));
   void RemovePluginView();
 
   bool mInverted;
+  bool mFullScreen;
+
+  void* mJavaView;
 
   // For kOpenGL_ANPDrawingModel
   nsRefPtr<mozilla::AndroidMediaLayer> mLayer;
 #endif 
  
   nsPluginNativeWindow       *mPluginWindow;
   nsRefPtr<nsNPAPIPluginInstance> mInstance;
   nsObjectFrame              *mObjectFrame;
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -104,16 +104,18 @@ abstract public class GeckoApp
     public static FormAssistPopup mFormAssistPopup;
     public Favicons mFavicons;
 
     private static LayerController mLayerController;
     private static GeckoLayerClient mLayerClient;
     private AboutHomeContent mAboutHomeContent;
     private static AbsoluteLayout mPluginContainer;
 
+    private View mFullScreenPluginView;
+
     private int mRestoreMode = GeckoAppShell.RESTORE_NONE;
     private boolean mInitialized = false;
 
     static class ExtraMenuItem implements MenuItem.OnMenuItemClickListener {
         String label;
         String icon;
         int id;
         public boolean onMenuItemClick(MenuItem item) {
@@ -1345,42 +1347,95 @@ abstract public class GeckoApp
     }
 
     void handleWindowClose(final int tabId) {
         Tabs tabs = Tabs.getInstance();
         Tab tab = tabs.getTab(tabId);
         tabs.closeTab(tab);
     }
 
-    void addPluginView(final View view, final Rect rect) {
+    private void addFullScreenPluginView(View view, int orientation) {
+        if (mFullScreenPluginView != null) {
+            Log.w(LOGTAG, "Already have a fullscreen plugin view");
+            return;
+        }
+
+        setFullScreen(true);
+        mBrowserToolbar.hide();
+
+        if (orientation != GeckoScreenOrientationListener.eScreenOrientation_None)
+            GeckoScreenOrientationListener.getInstance().lockScreenOrientation(orientation);
+
+        view.setWillNotDraw(false);
+        if (view instanceof SurfaceView) {
+            ((SurfaceView) view).setZOrderOnTop(true);
+        }
+
+        mPluginContainer.addView(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        mFullScreenPluginView = view;
+    }
+
+    void addPluginView(final View view, final Rect rect, final boolean isFullScreen, final int orientation) {
         mMainHandler.post(new Runnable() { 
             public void run() {
                 Tabs tabs = Tabs.getInstance();
                 Tab tab = tabs.getSelectedTab();
 
+                if (isFullScreen) {
+                    addFullScreenPluginView(view, orientation);
+                    return;
+                }
+
                 PluginLayer layer = (PluginLayer) tab.getPluginLayer(view);
                 if (layer == null) {
                     layer = new PluginLayer(view, rect, mLayerController.getView().getRenderer().getMaxTextureSize());
                     tab.addPluginLayer(view, layer);
                 } else {
                     layer.reset(rect);
                     layer.setVisible(true);
                 }
 
                 mLayerController.getView().addLayer(layer);
             }
         });
     }
 
-    void removePluginView(final View view) {
+    private void removeFullScreenPluginView(View view) {
+        if (mFullScreenPluginView == null) {
+            Log.w(LOGTAG, "Don't have a fullscreen plugin view");
+            return;
+        }
+
+        if (mFullScreenPluginView != view) {
+            Log.w(LOGTAG, "Passed view is not the current full screen view");
+            return;
+        }
+
+        GeckoAppShell.onFullScreenPluginHidden(view);
+
+        GeckoScreenOrientationListener.getInstance().unlockScreenOrientation();
+
+        setFullScreen(false);
+        mBrowserToolbar.show();
+        
+        mPluginContainer.removeView(view);
+        mFullScreenPluginView = null;
+    }
+
+    void removePluginView(final View view, final boolean isFullScreen) {
         mMainHandler.post(new Runnable() { 
             public void run() {
                 Tabs tabs = Tabs.getInstance();
                 Tab tab = tabs.getSelectedTab();
 
+                if (isFullScreen) {
+                    removeFullScreenPluginView(view);
+                    return;
+                }
+
                 PluginLayer layer = (PluginLayer) tab.removePluginLayer(view);
                 if (layer != null) {
                     layer.destroy();
                 }
             }
         });
     }
 
@@ -2596,16 +2651,21 @@ abstract public class GeckoApp
 
     @Override
     public void onBackPressed() {
         if (mDoorHangerPopup.isShowing()) {
             mDoorHangerPopup.dismiss();
             return;
         }
 
+        if (mFullScreenPluginView != null) {
+            removePluginView(mFullScreenPluginView, true);
+            return;
+        }
+
         SiteIdentityPopup identityPopup = SiteIdentityPopup.getInstance();
         if (identityPopup.isShowing()) {
             identityPopup.dismiss();
             return;
         }
 
         if (mDOMFullScreen) {
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FullScreen:Exit", null));
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -202,16 +202,18 @@ public class GeckoAppShell
     public static native ByteBuffer allocateDirectBuffer(long size);
     public static native void freeDirectBuffer(ByteBuffer buf);
     public static native void scheduleComposite();
     public static native void schedulePauseComposition();
     public static native void scheduleResumeComposition(int width, int height);
 
     public static native SurfaceBits getSurfaceBits(Surface surface);
 
+    public static native void onFullScreenPluginHidden(View view);
+
     private static class GeckoMediaScannerClient implements MediaScannerConnectionClient {
         private String mFile = "";
         private String mMimeType = "";
         private MediaScannerConnection mScanner = null;
 
         public GeckoMediaScannerClient(Context aContext, String aFile, String aMimeType) {
             mFile = aFile;
             mMimeType = aMimeType;
@@ -1511,28 +1513,29 @@ public class GeckoAppShell
         }
         catch (Exception e) {
             return true;
         }
     }
 
     public static void addPluginView(View view,
                                      int x, int y,
-                                     int w, int h)
+                                     int w, int h,
+                                     boolean isFullScreen, int orientation)
 {
         ImmutableViewportMetrics pluginViewport;
 
-        Log.i(LOGTAG, "addPluginView:" + view + " @ x:" + x + " y:" + y + " w:" + w + " h:" + h);
+        Log.i(LOGTAG, "addPluginView:" + view + " @ x:" + x + " y:" + y + " w:" + w + " h:" + h + "fullscreen: " + isFullScreen + " orientation: " + orientation);
         
-        GeckoApp.mAppContext.addPluginView(view, new Rect(x, y, x + w, y + h));
+        GeckoApp.mAppContext.addPluginView(view, new Rect(x, y, x + w, y + h), isFullScreen, orientation);
     }
 
-    public static void removePluginView(View view) {
-        Log.i(LOGTAG, "removePluginView:" + view);
-        GeckoApp.mAppContext.removePluginView(view);
+    public static void removePluginView(View view, boolean isFullScreen) {
+        Log.i(LOGTAG, "removePluginView:" + view + " fullscreen: " + isFullScreen);
+        GeckoApp.mAppContext.removePluginView(view, isFullScreen);
     }
 
     public static Surface createSurface() {
         Log.i(LOGTAG, "createSurface");
         return GeckoApp.mAppContext.createSurface();
     }
 
     public static void showSurface(Surface surface,
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -314,16 +314,17 @@ SHELL_WRAPPER3(notifyGetSmsFailed, jint,
 SHELL_WRAPPER3(notifySmsDeleted, jboolean, jint, jlong)
 SHELL_WRAPPER3(notifySmsDeleteFailed, jint, jint, jlong)
 SHELL_WRAPPER2(notifyNoMessageInList, jint, jlong)
 SHELL_WRAPPER8(notifyListCreated, jint, jint, jstring, jstring, jstring, jlong, jint, jlong)
 SHELL_WRAPPER7(notifyGotNextMessage, jint, jstring, jstring, jstring, jlong, jint, jlong)
 SHELL_WRAPPER3(notifyReadingMessageListFailed, jint, jint, jlong)
 SHELL_WRAPPER2(notifyFilePickerResult, jstring, jlong)
 SHELL_WRAPPER1_WITH_RETURN(getSurfaceBits, jobject, jobject)
+SHELL_WRAPPER1(onFullScreenPluginHidden, jobject)
 
 static void * xul_handle = NULL;
 static void * sqlite_handle = NULL;
 static void * nss_handle = NULL;
 static void * nspr_handle = NULL;
 static void * plc_handle = NULL;
 static bool simple_linker_initialized = false;
 
@@ -731,16 +732,17 @@ loadGeckoLibs(const char *apkName)
   GETFUNC(notifySmsDeleted);
   GETFUNC(notifySmsDeleteFailed);
   GETFUNC(notifyNoMessageInList);
   GETFUNC(notifyListCreated);
   GETFUNC(notifyGotNextMessage);
   GETFUNC(notifyReadingMessageListFailed);
   GETFUNC(notifyFilePickerResult);
   GETFUNC(getSurfaceBits);
+  GETFUNC(onFullScreenPluginHidden);
 #undef GETFUNC
   sStartupTimeline = (uint64_t *)__wrap_dlsym(xul_handle, "_ZN7mozilla15StartupTimeline16sStartupTimelineE");
   gettimeofday(&t1, 0);
   struct rusage usage2;
   getrusage(RUSAGE_THREAD, &usage2);
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %ldms total, %ldms user, %ldms system, %ld faults",
                       (t1.tv_sec - t0.tv_sec)*1000 + (t1.tv_usec - t0.tv_usec)/1000, 
                       (usage2.ru_utime.tv_sec - usage1.ru_utime.tv_sec)*1000 + (usage2.ru_utime.tv_usec - usage1.ru_utime.tv_usec)/1000,
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -131,17 +131,17 @@ AndroidBridge::Init(JNIEnv *jEnv,
     jGetShowPasswordSetting = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getShowPasswordSetting", "()Z");
     jPostToJavaThread = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "postToJavaThread", "(Z)V");
     jInitCamera = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "initCamera", "(Ljava/lang/String;III)[I");
     jCloseCamera = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "closeCamera", "()V");
     jIsTablet = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isTablet", "()Z");
     jEnableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableBatteryNotifications", "()V");
     jDisableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableBatteryNotifications", "()V");
     jGetCurrentBatteryInformation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getCurrentBatteryInformation", "()[D");
-    jRemovePluginView = jEnv->GetStaticMethodID(jGeckoAppShellClass, "removePluginView", "(Landroid/view/View;)V");
+    jRemovePluginView = jEnv->GetStaticMethodID(jGeckoAppShellClass, "removePluginView", "(Landroid/view/View;Z)V");
     jNotifyPaintedRect = jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyPaintedRect", "(FFFF)V");
 
     jHandleGeckoMessage = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "handleGeckoMessage", "(Ljava/lang/String;)Ljava/lang/String;");
     jCheckUriVisited = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "checkUriVisited", "(Ljava/lang/String;)V");
     jMarkUriVisited = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "markUriVisited", "(Ljava/lang/String;)V");
 
     jNumberOfMessages = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getNumberOfMessagesForText", "(Ljava/lang/String;)I");
     jSendMessage = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "sendMessage", "(Ljava/lang/String;Ljava/lang/String;IJ)V");
@@ -180,17 +180,17 @@ AndroidBridge::Init(JNIEnv *jEnv,
     if (apiVersion <= 8 /* Froyo */)
         jSurfacePointerField = jEnv->GetFieldID(jSurfaceClass, "mSurface", "I");
     else /* not Froyo */
         jSurfacePointerField = jEnv->GetFieldID(jSurfaceClass, "mNativeSurface", "I");
 
 #ifdef MOZ_JAVA_COMPOSITOR
     jPumpMessageLoop = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "pumpMessageLoop", "()V");
 
-    jAddPluginView = jEnv->GetStaticMethodID(jGeckoAppShellClass, "addPluginView", "(Landroid/view/View;IIII)V");
+    jAddPluginView = jEnv->GetStaticMethodID(jGeckoAppShellClass, "addPluginView", "(Landroid/view/View;IIIIZI)V");
     jCreateSurface = jEnv->GetStaticMethodID(jGeckoAppShellClass, "createSurface", "()Landroid/view/Surface;");
     jShowSurface = jEnv->GetStaticMethodID(jGeckoAppShellClass, "showSurface", "(Landroid/view/Surface;IIIIZZ)V");
     jHideSurface = jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideSurface", "(Landroid/view/Surface;)V");
     jDestroySurface = jEnv->GetStaticMethodID(jGeckoAppShellClass, "destroySurface", "(Landroid/view/Surface;)V");
 
     jLayerView = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("org/mozilla/gecko/gfx/LayerView"));
 
     AndroidGLController::Init(jEnv);
@@ -2305,43 +2305,44 @@ NS_IMETHODIMP nsAndroidBridge::GetBrowse
 NS_IMETHODIMP nsAndroidBridge::SetBrowserApp(nsIAndroidBrowserApp *aBrowserApp)
 {
     if (nsAppShell::gAppShell)
         nsAppShell::gAppShell->SetBrowserApp(aBrowserApp);
     return NS_OK;
 }
 
 void
-AndroidBridge::AddPluginView(jobject view, const gfxRect& rect) {
+AndroidBridge::AddPluginView(jobject view, const gfxRect& rect, bool isFullScreen, int orientation) {
     JNIEnv *env = GetJNIEnv();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
 
 #if MOZ_JAVA_COMPOSITOR
     env->CallStaticVoidMethod(sBridge->mGeckoAppShellClass,
                               sBridge->jAddPluginView, view,
-                              (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
+                              (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height,
+                              isFullScreen, orientation);
 #else
     env->CallStaticVoidMethod(sBridge->mGeckoAppShellClass,
                               sBridge->jAddPluginView, view,
                               rect.x, rect.y, rect.width, rect.height);
 #endif
 }
 
 void
-AndroidBridge::RemovePluginView(jobject view)
+AndroidBridge::RemovePluginView(jobject view, bool isFullScreen)
 {
     JNIEnv *env = GetJNIEnv();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
-    env->CallStaticVoidMethod(mGeckoAppShellClass, jRemovePluginView, view);
+    env->CallStaticVoidMethod(mGeckoAppShellClass, jRemovePluginView, view, isFullScreen);
 }
 
 extern "C"
 __attribute__ ((visibility("default")))
 jobject JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size);
 
 
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -335,18 +335,18 @@ public:
     void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
                           nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY);
 
     jobject CreateSurface();
     void DestroySurface(jobject surface);
     void ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend);
     void HideSurface(jobject surface);
 
-    void AddPluginView(jobject view, const gfxRect& rect);
-    void RemovePluginView(jobject view);
+    void AddPluginView(jobject view, const gfxRect& rect, bool isFullScreen, int orientation);
+    void RemovePluginView(jobject view, bool isFullScreen);
 
     // These methods don't use a ScreenOrientation because it's an
     // 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();
     void EnableScreenOrientationNotifications();
     void DisableScreenOrientationNotifications();
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -32,16 +32,17 @@
 
 #include "mozilla/dom/sms/SmsMessage.h"
 #include "mozilla/dom/sms/Constants.h"
 #include "mozilla/dom/sms/Types.h"
 #include "mozilla/dom/sms/PSms.h"
 #include "mozilla/dom/sms/SmsParent.h"
 #include "nsISmsRequestManager.h"
 #include "nsISmsDatabaseService.h"
+#include "nsPluginInstanceOwner.h"
 
 using namespace mozilla;
 using namespace mozilla::dom::sms;
 
 /* Forward declare all the JNI methods as extern "C" */
 
 extern "C" {
 /*
@@ -969,11 +970,37 @@ Java_org_mozilla_gecko_GeckoAppShell_get
 
 cleanup:
     AndroidBridge::Bridge()->UnlockWindow(window);
     AndroidBridge::Bridge()->ReleaseNativeWindow(window);
 
     return surfaceBits;
 }
 
+NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden(JNIEnv* jenv, jclass, jobject view)
+{
+  class ExitFullScreenRunnable : public nsRunnable {
+    public:
+      ExitFullScreenRunnable(jobject view) : mView(view) {}
+
+      NS_IMETHODIMP Run() {
+        JNIEnv* env = AndroidBridge::GetJNIEnv();
+        if (!env) {
+          NS_WARNING("Failed to acquire JNI env, can't exit plugin fullscreen mode");
+          return NS_OK;
+        }
+
+        nsPluginInstanceOwner::ExitFullScreen(mView);
+        env->DeleteGlobalRef(mView);
+        return NS_OK;
+      }
+
+    private:
+      jobject mView;
+  };
+
+  nsCOMPtr<nsIRunnable> runnable = new ExitFullScreenRunnable(jenv->NewGlobalRef(view));
+  NS_DispatchToMainThread(runnable);
+}
 
 #endif
 }