diff --git a/dom/plugins/base/Makefile.in b/dom/plugins/base/Makefile.in index 1b67bfff57c5..d91f793ed684 100644 --- a/dom/plugins/base/Makefile.in +++ b/dom/plugins/base/Makefile.in @@ -138,6 +138,7 @@ endif LOCAL_INCLUDES += \ -DSK_BUILD_FOR_ANDROID_NDK \ + -I$(topsrcdir)/widget/android \ -I$(topsrcdir)/xpcom/base/ \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ diff --git a/dom/plugins/base/android/ANPAudio.cpp b/dom/plugins/base/android/ANPAudio.cpp index 003616842680..c3398de9b8d8 100644 --- a/dom/plugins/base/android/ANPAudio.cpp +++ b/dom/plugins/base/android/ANPAudio.cpp @@ -306,7 +306,6 @@ anp_audio_start(ANPAudioTrack* s) if (s->keepGoing) { // we are already playing. Ignore. - LOG("anp_audio_start called twice!"); return; } @@ -359,7 +358,14 @@ anp_audio_isStopped(ANPAudioTrack* s) return s->isStopped; } -void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) { +uint32_t +anp_audio_trackLatency(ANPAudioTrack* s) { + // Bug 721835 + NOT_IMPLEMENTED(); + return 1; +} + +void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, newTrack); ASSIGN(i, deleteTrack); @@ -368,3 +374,14 @@ void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) { ASSIGN(i, stop); ASSIGN(i, isStopped); } + +void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, newTrack); + ASSIGN(i, deleteTrack); + ASSIGN(i, start); + ASSIGN(i, pause); + ASSIGN(i, stop); + ASSIGN(i, isStopped); + ASSIGN(i, trackLatency); +} diff --git a/dom/plugins/base/android/ANPBase.h b/dom/plugins/base/android/ANPBase.h index 729de6a5a342..1553e698ff60 100644 --- a/dom/plugins/base/android/ANPBase.h +++ b/dom/plugins/base/android/ANPBase.h @@ -36,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#include "android_npapi.h" #include +#include "android_npapi.h" #include "nsAutoPtr.h" #include "nsISupportsImpl.h" @@ -53,7 +53,8 @@ "!!!!!!!!!!!!!! %s not implemented %s, %d", \ __PRETTY_FUNCTION__, __FILE__, __LINE__); \ -void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i); +void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i); +void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1* i); void InitBitmapInterface(ANPBitmapInterfaceV0 *i); void InitCanvasInterface(ANPCanvasInterfaceV0 *i); void InitEventInterface(ANPEventInterfaceV0 *i); @@ -63,5 +64,13 @@ void InitPaintInterface(ANPPaintInterfaceV0 *i); void InitPathInterface(ANPPathInterfaceV0 *i); void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i); void InitSystemInterface(ANPSystemInterfaceV0 *i); +void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i); +void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i); void InitTypeFaceInterface(ANPTypefaceInterfaceV0 *i); void InitWindowInterface(ANPWindowInterfaceV0 *i); +void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i); +void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i); +void InitVideoInterfaceV0(ANPVideoInterfaceV0 *i); +void InitVideoInterfaceV1(ANPVideoInterfaceV1 *i); +void InitOpenGLInterface(ANPOpenGLInterfaceV0 *i); +void InitNativeWindowInterface(ANPNativeWindowInterfaceV0 *i); diff --git a/dom/plugins/base/android/ANPNativeWindow.cpp b/dom/plugins/base/android/ANPNativeWindow.cpp new file mode 100644 index 000000000000..4359688a859d --- /dev/null +++ b/dom/plugins/base/android/ANPNativeWindow.cpp @@ -0,0 +1,85 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// must include config.h first for webkit to fiddle with new/delete +#include +#include "AndroidBridge.h" +#include "AndroidMediaLayer.h" +#include "ANPBase.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" +#include "nsNPAPIPluginInstance.h" +#include "gfxRect.h" + +using namespace mozilla; +using namespace mozilla; + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_native_window_##name + +static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return NULL; + } + + + ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent(); + owner->Invalidate(); + + return window; +} + +static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return; + } + + owner->Layer()->SetInverted(isContentInverted); +} + + +void InitNativeWindowInterface(ANPNativeWindowInterfaceV0* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, invertPluginContent); +} diff --git a/dom/plugins/base/android/ANPOpenGL.cpp b/dom/plugins/base/android/ANPOpenGL.cpp new file mode 100644 index 000000000000..1c55fff40a65 --- /dev/null +++ b/dom/plugins/base/android/ANPOpenGL.cpp @@ -0,0 +1,65 @@ +/* The Original Code is Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include "AndroidBridge.h" +#include "ANPBase.h" +#include "GLContextProvider.h" +#include "nsNPAPIPluginInstance.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_opengl_##name + +using namespace mozilla; +using namespace mozilla::gl; + +static ANPEGLContext anp_opengl_acquireContext(NPP inst) { + // Bug 687267 + NOT_IMPLEMENTED(); + return NULL; +} + +static ANPTextureInfo anp_opengl_lockTexture(NPP instance) { + ANPTextureInfo info = { 0, 0, 0, 0 }; + NOT_IMPLEMENTED(); + return info; +} + +static void anp_opengl_releaseTexture(NPP instance, const ANPTextureInfo* info) { + NOT_IMPLEMENTED(); +} + +static void anp_opengl_invertPluginContent(NPP instance, bool isContentInverted) { + NOT_IMPLEMENTED(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitOpenGLInterface(ANPOpenGLInterfaceV0* i) { + ASSIGN(i, acquireContext); + ASSIGN(i, lockTexture); + ASSIGN(i, releaseTexture); + ASSIGN(i, invertPluginContent); +} diff --git a/dom/plugins/base/android/ANPSystem.cpp b/dom/plugins/base/android/ANPSystem.cpp index 0fcd1530e330..876f9940ed4e 100644 --- a/dom/plugins/base/android/ANPSystem.cpp +++ b/dom/plugins/base/android/ANPSystem.cpp @@ -62,6 +62,12 @@ anp_system_getApplicationDataDirectory() return dir; } +const char* +anp_system_getApplicationDataDirectory(NPP instance) +{ + return anp_system_getApplicationDataDirectory(); +} + jclass anp_system_loadJavaClass(NPP instance, const char* className) { LOG("%s", __PRETTY_FUNCTION__); @@ -88,8 +94,27 @@ jclass anp_system_loadJavaClass(NPP instance, const char* className) return reinterpret_cast(obj); } +void anp_system_setPowerState(NPP instance, ANPPowerState powerState) +{ + NOT_IMPLEMENTED(); +} + void InitSystemInterface(ANPSystemInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, getApplicationDataDirectory); ASSIGN(i, loadJavaClass); } + +void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, getApplicationDataDirectory); + ASSIGN(i, loadJavaClass); + ASSIGN(i, setPowerState); +} + +void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, getApplicationDataDirectory); + ASSIGN(i, loadJavaClass); + ASSIGN(i, setPowerState); +} diff --git a/dom/plugins/base/android/ANPVideo.cpp b/dom/plugins/base/android/ANPVideo.cpp new file mode 100644 index 000000000000..6b75425a046c --- /dev/null +++ b/dom/plugins/base/android/ANPVideo.cpp @@ -0,0 +1,109 @@ +/* The Original Code is Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include "ANPBase.h" +#include "AndroidMediaLayer.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" +#include "nsNPAPIPluginInstance.h" +#include "gfxRect.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_video_##name + +using namespace mozilla; + +static AndroidMediaLayer* GetLayerForInstance(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return NULL; + } + + return owner->Layer(); +} + +static void Invalidate(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) + return; + + owner->Invalidate(); +} + +static ANPNativeWindow anp_video_acquireNativeWindow(NPP instance) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return NULL; + + return layer->RequestNativeWindowForVideo(); +} + +static void anp_video_setWindowDimensions(NPP instance, const ANPNativeWindow window, + const ANPRectF* dimensions) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return; + + gfxRect rect(dimensions->left, dimensions->top, + dimensions->right - dimensions->left, + dimensions->bottom - dimensions->top); + + layer->SetNativeWindowDimensions(window, rect); + Invalidate(instance); +} + + +static void anp_video_releaseNativeWindow(NPP instance, ANPNativeWindow window) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return; + + layer->ReleaseNativeWindowForVideo(window); + Invalidate(instance); +} + +static void anp_video_setFramerateCallback(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc callback) { + // Bug 722682 + NOT_IMPLEMENTED(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitVideoInterfaceV0(ANPVideoInterfaceV0* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, setWindowDimensions); + ASSIGN(i, releaseNativeWindow); +} + +void InitVideoInterfaceV1(ANPVideoInterfaceV1* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, setWindowDimensions); + ASSIGN(i, releaseNativeWindow); + ASSIGN(i, setFramerateCallback); +} diff --git a/dom/plugins/base/android/ANPWindow.cpp b/dom/plugins/base/android/ANPWindow.cpp index c26790054b2c..75b643df386c 100644 --- a/dom/plugins/base/android/ANPWindow.cpp +++ b/dom/plugins/base/android/ANPWindow.cpp @@ -39,10 +39,16 @@ #include "assert.h" #include "ANPBase.h" #include +#include "AndroidBridge.h" +#include "nsNPAPIPluginInstance.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #define ASSIGN(obj, name) (obj)->name = anp_window_##name +using namespace mozilla; + void anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count) { @@ -79,6 +85,32 @@ anp_window_requestCenterFitZoom(NPP instance) NOT_IMPLEMENTED(); } +ANPRectI +anp_window_visibleRect(NPP instance) +{ + ANPRectI rect = { 0, 0, 0, 0 }; + + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return rect; + } + + nsIntRect visibleRect = owner->GetVisibleRect(); + rect.left = visibleRect.x; + rect.top = visibleRect.y; + rect.right = visibleRect.x + visibleRect.width; + rect.bottom = visibleRect.y + visibleRect.height; + + return rect; +} + +void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation) +{ + NOT_IMPLEMENTED(); +} + void InitWindowInterface(ANPWindowInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, setVisibleRects); @@ -89,3 +121,26 @@ void InitWindowInterface(ANPWindowInterfaceV0 *i) { ASSIGN(i, requestCenterFitZoom); } +void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, setVisibleRects); + ASSIGN(i, clearVisibleRects); + ASSIGN(i, showKeyboard); + ASSIGN(i, requestFullScreen); + ASSIGN(i, exitFullScreen); + ASSIGN(i, requestCenterFitZoom); + ASSIGN(i, visibleRect); +} + +void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, setVisibleRects); + ASSIGN(i, clearVisibleRects); + ASSIGN(i, showKeyboard); + ASSIGN(i, requestFullScreen); + ASSIGN(i, exitFullScreen); + ASSIGN(i, requestCenterFitZoom); + ASSIGN(i, visibleRect); + ASSIGN(i, requestFullScreenOrientation); +} + diff --git a/dom/plugins/base/android/Makefile.in b/dom/plugins/base/android/Makefile.in index 1b987e428de3..5f78039dc0fd 100644 --- a/dom/plugins/base/android/Makefile.in +++ b/dom/plugins/base/android/Makefile.in @@ -63,12 +63,17 @@ CPPSRCS += ANPAudio.cpp \ ANPWindow.cpp \ ANPBitmap.cpp \ ANPLog.cpp \ + ANPNativeWindow.cpp \ ANPSurface.cpp \ + ANPVideo.cpp \ + ANPOpenGL.cpp \ $(NULL) LOCAL_INCLUDES += \ + -I$(topsrcdir)/widget/android \ -I$(topsrcdir)/dom/plugins/base \ -I$(topsrcdir)/dom/plugins/base/android/include \ + -I$(topsrcdir)/gfx/gl \ $(MOZ_CAIRO_CFLAGS) \ $(NULL) diff --git a/dom/plugins/base/android/android_npapi.h b/dom/plugins/base/android/android_npapi.h index 27b0c0c9fab3..e903e2af0d37 100644 --- a/dom/plugins/base/android/android_npapi.h +++ b/dom/plugins/base/android/android_npapi.h @@ -36,8 +36,9 @@ #define android_npapi_H #include -#include "npapi.h" #include +#include "npapi.h" +#include "GLDefs.h" /////////////////////////////////////////////////////////////////////////////// // General types @@ -120,6 +121,16 @@ typedef uint32_t ANPMatrixFlag; #define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010) #define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011) +#define kAudioTrackInterfaceV1_ANPGetValue ((NPNVariable)1012) +#define kOpenGLInterfaceV0_ANPGetValue ((NPNVariable)1013) +#define kWindowInterfaceV1_ANPGetValue ((NPNVariable)1014) +#define kVideoInterfaceV0_ANPGetValue ((NPNVariable)1015) +#define kSystemInterfaceV1_ANPGetValue ((NPNVariable)1016) +#define kSystemInterfaceV2_ANPGetValue ((NPNVariable)1017) +#define kWindowInterfaceV2_ANPGetValue ((NPNVariable)1018) +#define kNativeWindowInterfaceV0_ANPGetValue ((NPNVariable)1019) +#define kVideoInterfaceV1_ANPGetValue ((NPNVariable)1020) + /** queries for the drawing models supported on this device. NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits) @@ -180,6 +191,7 @@ enum ANPDrawingModels { surface object. */ kSurface_ANPDrawingModel = 1 << 1, + kOpenGL_ANPDrawingModel = 1 << 2, }; typedef int32_t ANPDrawingModel; @@ -678,6 +690,25 @@ struct ANPWindowInterfaceV0 : ANPInterface { void (*requestCenterFitZoom)(NPP instance); }; +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); +}; + +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. + */ + void (*requestFullScreenOrientation)(NPP instance, ANPScreenOrientation orientation); +}; + /////////////////////////////////////////////////////////////////////////////// enum ANPSampleFormats { @@ -762,6 +793,12 @@ struct ANPAudioTrackInterfaceV0 : ANPInterface { bool (*isStopped)(ANPAudioTrack*); }; +struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 { + /** Returns the track's latency in milliseconds. */ + uint32_t (*trackLatency)(ANPAudioTrack*); +}; + + /////////////////////////////////////////////////////////////////////////////// // DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent @@ -922,12 +959,16 @@ struct ANPEvent { // use based on the value in model union { ANPBitmap bitmap; + struct { + int32_t width; + int32_t height; + } surfaceSize; } data; } draw; - int32_t other[8]; } data; }; + struct ANPEventInterfaceV0 : ANPInterface { /** Post a copy of the specified event to the plugin. The event will be delivered to the plugin in its main thread (the thread that receives @@ -976,4 +1017,117 @@ typedef uint32_t uint32; typedef int16_t int16; typedef uint16_t uint16; +/** + * TODO should we not use EGL and GL data types for ABI safety? + */ +struct ANPTextureInfo { + GLuint textureId; + uint32_t width; + uint32_t height; + GLenum internalFormat; +}; + +typedef void* ANPEGLContext; + +struct ANPOpenGLInterfaceV0 : ANPInterface { + ANPEGLContext (*acquireContext)(NPP instance); + + ANPTextureInfo (*lockTexture)(NPP instance); + + void (*releaseTexture)(NPP instance, const ANPTextureInfo*); + + /** + * Invert the contents of the plugin on the y-axis. + * default is to not be inverted (i.e. use OpenGL coordinates) + */ + void (*invertPluginContent)(NPP instance, bool isContentInverted); +}; + +enum ANPPowerStates { + kDefault_ANPPowerState = 0, + kScreenOn_ANPPowerState = 1 +}; +typedef int32_t ANPPowerState; + +struct ANPSystemInterfaceV1 : ANPSystemInterfaceV0 { + void (*setPowerState)(NPP instance, ANPPowerState powerState); +}; + +struct ANPSystemInterfaceV2 : ANPInterface { + /** Return the path name for the current Application's plugin data directory, + or NULL if not supported. This directory will change depending on whether + or not the plugin is found within an incognito tab. + */ + const char* (*getApplicationDataDirectory)(NPP instance); + + // redeclaration of existing features + jclass (*loadJavaClass)(NPP instance, const char* className); + void (*setPowerState)(NPP instance, ANPPowerState powerState); +}; + +typedef void* ANPNativeWindow; + +struct ANPVideoInterfaceV0 : ANPInterface { + + /** + * Constructs a new native window to be used for rendering video content. + * + * Subsequent calls will produce new windows, but may also return NULL after + * n attempts if the browser has reached it's limit. Further, if the browser + * is unable to acquire the window quickly it may also return NULL in order + * to not prevent the plugin from executing. A subsequent call will then + * return the window if it is avaiable. + * + * NOTE: The hardware may fail if you try to decode more than the allowable + * number of videos supported on that device. + */ + ANPNativeWindow (*acquireNativeWindow)(NPP instance); + + /** + * Sets the rectangle that specifies where the video content is to be drawn. + * The dimensions are in document space. Further, if the rect is NULL the + * browser will not attempt to draw the window, therefore do not set the + * dimensions until you queue the first buffer in the window. + */ + void (*setWindowDimensions)(NPP instance, const ANPNativeWindow window, const ANPRectF* dimensions); + + /** + */ + void (*releaseNativeWindow)(NPP instance, ANPNativeWindow window); +}; + +/** Called to notify the plugin that a video frame has been composited by the +* browser for display. This will be called in a separate thread and as such +* you cannot call releaseNativeWindow from the callback. +* +* The timestamp is in nanoseconds, and is monotonically increasing. +*/ +typedef void (*ANPVideoFrameCallbackProc)(ANPNativeWindow* window, int64_t timestamp); + +struct ANPVideoInterfaceV1 : ANPVideoInterfaceV0 { + /** Set a callback to be notified when an ANPNativeWindow is composited by + * the browser. + */ + void (*setFramerateCallback)(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc); +}; + +struct ANPNativeWindowInterfaceV0 : ANPInterface { + /** + * Constructs a new native window to be used for rendering plugin content. + * + * Subsequent calls will return the original constructed window. Further, if + * the browser is unable to acquire the window quickly it may return NULL in + * order to not block the plugin indefinitely. A subsequent call will then + * return the window if it is available. + */ + ANPNativeWindow (*acquireNativeWindow)(NPP instance); + + /** + * Invert the contents of the plugin on the y-axis. + * default is to not be inverted (e.g. use OpenGL coordinates) + */ + void (*invertPluginContent)(NPP instance, bool isContentInverted); +}; + + #endif diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 2d70d7f2fee3..109430b8ce85 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -132,9 +132,10 @@ using mozilla::plugins::PluginModuleParent; #endif #ifdef MOZ_WIDGET_ANDROID +#include +#include "android_npapi.h" #include "ANPBase.h" #include "AndroidBridge.h" -#include #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #endif @@ -2348,7 +2349,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) case kAudioTrackInterfaceV0_ANPGetValue: { LOG("get audio interface"); ANPAudioTrackInterfaceV0 *i = (ANPAudioTrackInterfaceV0 *) result; - InitAudioTrackInterface(i); + InitAudioTrackInterfaceV0(i); return NPERR_NO_ERROR; } @@ -2363,7 +2364,6 @@ _getvalue(NPP npp, NPNVariable variable, void *result) LOG("get system interface"); ANPSystemInterfaceV0* i = reinterpret_cast(result); InitSystemInterface(i); - LOG("done system interface"); return NPERR_NO_ERROR; } @@ -2395,6 +2395,71 @@ _getvalue(NPP npp, NPNVariable variable, void *result) *i = reinterpret_cast(ret); return NPERR_NO_ERROR; } + + case kAudioTrackInterfaceV1_ANPGetValue: { + LOG("get audio interface v1"); + ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result; + InitAudioTrackInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kNativeWindowInterfaceV0_ANPGetValue: { + LOG("get native window interface v0"); + ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result; + InitNativeWindowInterface(i); + return NPERR_NO_ERROR; + } + + case kOpenGLInterfaceV0_ANPGetValue: { + LOG("get openGL interface"); + ANPOpenGLInterfaceV0 *i = (ANPOpenGLInterfaceV0*) result; + InitOpenGLInterface(i); + return NPERR_NO_ERROR; + } + + case kWindowInterfaceV1_ANPGetValue: { + LOG("get Window interface V1"); + ANPWindowInterfaceV1 *i = (ANPWindowInterfaceV1 *) result; + InitWindowInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kWindowInterfaceV2_ANPGetValue: { + LOG("get Window interface V2"); + ANPWindowInterfaceV2 *i = (ANPWindowInterfaceV2 *) result; + InitWindowInterfaceV2(i); + return NPERR_NO_ERROR; + } + + case kVideoInterfaceV0_ANPGetValue: { + LOG("get video interface"); + ANPVideoInterfaceV0 *i = (ANPVideoInterfaceV0*) result; + InitVideoInterfaceV0(i); + return NPERR_NO_ERROR; + } + + case kVideoInterfaceV1_ANPGetValue: { + LOG("get video interface"); + ANPVideoInterfaceV1 *i = (ANPVideoInterfaceV1*) result; + InitVideoInterfaceV1(i); + return NPERR_NO_ERROR; + } + + + case kSystemInterfaceV1_ANPGetValue: { + LOG("get system interface v1"); + ANPSystemInterfaceV1* i = reinterpret_cast(result); + InitSystemInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kSystemInterfaceV2_ANPGetValue: { + LOG("get system interface v2"); + ANPSystemInterfaceV2* i = reinterpret_cast(result); + InitSystemInterfaceV2(i); + return NPERR_NO_ERROR; + } + #endif // we no longer hand out any XPCOM objects diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index ef4f8246ef8e..cf6e47d2ee68 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -152,6 +152,8 @@ public: #ifdef MOZ_WIDGET_ANDROID PRUint32 GetANPDrawingModel() { return mANPDrawingModel; } void SetANPDrawingModel(PRUint32 aModel); + + // This stuff is for kSurface_ANPDrawingModel void* GetJavaSurface(); void SetJavaSurface(void* aSurface); void RequestJavaSurface(); diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 5247a46d7667..90c5057338af 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -120,6 +120,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #include "ANPBase.h" #include "android_npapi.h" #include "AndroidBridge.h" +#include "AndroidMediaLayer.h" using namespace mozilla::dom; #include @@ -323,8 +324,9 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mWaitingForPaint = false; #ifdef MOZ_WIDGET_ANDROID - mPluginViewAdded = false; - mLastPluginRect = gfxRect(0, 0, 0, 0); + mOnScreen = false; + mInverted = false; + mLayer = new AndroidMediaLayer(); #endif } @@ -380,6 +382,13 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() mPluginWindow = nsnull; } +#ifdef MOZ_WIDGET_ANDROID + if (mLayer) { + delete mLayer; + mLayer = nsnull; + } +#endif + if (mInstance) { mInstance->InvalidateOwner(); } @@ -1658,6 +1667,43 @@ void nsPluginInstanceOwner::ScrollPositionDidChange(nscoord aX, nscoord aY) } #ifdef MOZ_WIDGET_ANDROID + +void nsPluginInstanceOwner::SendSize(int width, int height) +{ + if (!mInstance) + return; + + PRInt32 model = mInstance->GetANPDrawingModel(); + + if (model != kOpenGL_ANPDrawingModel) + return; + + ANPEvent event; + event.inSize = sizeof(ANPEvent); + 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(); @@ -1666,11 +1712,6 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) return false; } - if (aRect.IsEqualEdges(mLastPluginRect)) { - // Already added and in position, no work to do - return true; - } - JNIEnv* env = GetJNIForThread(); if (!env) return false; @@ -1713,26 +1754,16 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) aRect.height); #endif - if (!mPluginViewAdded) { - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = kOnScreen_ANPLifecycleAction; - mInstance->HandleEvent(&event, nsnull); - - mPluginViewAdded = true; - } + SendOnScreenEvent(true); return true; } void nsPluginInstanceOwner::RemovePluginView() { - if (!mInstance || !mObjectFrame | !mPluginViewAdded) + if (!mInstance || !mObjectFrame | !mOnScreen) return; - mPluginViewAdded = false; - void* surface = mInstance->GetJavaSurface(); if (!surface) return; @@ -1748,13 +1779,17 @@ void nsPluginInstanceOwner::RemovePluginView() "removePluginView", "(Landroid/view/View;)V"); env->CallStaticVoidMethod(cls, method, surface); - - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = kOffScreen_ANPLifecycleAction; - mInstance->HandleEvent(&event, nsnull); + SendOnScreenEvent(false); } + +void nsPluginInstanceOwner::Invalidate() { + NPRect rect; + rect.left = rect.top = 0; + rect.right = mPluginWindow->width; + rect.bottom = mPluginWindow->height; + InvalidateRect(&rect); +} + #endif nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent) @@ -2779,7 +2814,15 @@ nsPluginInstanceOwner::PrepareToStop(bool aDelayedStop) #endif #ifdef MOZ_WIDGET_ANDROID - RemovePluginView(); + + PRInt32 model = mInstance->GetANPDrawingModel(); + if (model == kSurface_ANPDrawingModel) { + RemovePluginView(); + } else if (model == kOpenGL_ANPDrawingModel && mLayer) { + delete mLayer; + mLayer = nsnull; + } + #endif // Unregister scroll position listeners @@ -2891,15 +2934,21 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, if (model == kSurface_ANPDrawingModel) { if (!AddPluginView(aFrameRect)) { - NPRect rect; - rect.left = rect.top = 0; - rect.right = aFrameRect.width; - rect.bottom = aFrameRect.height; - InvalidateRect(&rect); + Invalidate(); } return; } + if (model == kOpenGL_ANPDrawingModel) { + // 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; @@ -3590,8 +3639,16 @@ void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow) } else { mPluginWindow->clipRect.right = 0; mPluginWindow->clipRect.bottom = 0; -#ifdef MOZ_WIDGET_ANDROID - RemovePluginView(); +#if 0 //MOZ_WIDGET_ANDROID + if (mInstance) { + PRInt32 model = mInstance->GetANPDrawingModel(); + + if (model == kSurface_ANPDrawingModel) { + RemovePluginView(); + } else if (model == kOpenGL_ANPDrawingModel) { + HidePluginLayer(); + } + } #endif } diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index a6acb483f741..a92f35e3ddbc 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -94,6 +94,12 @@ class gfxXlibSurface; #include #endif +#ifdef MOZ_WIDGET_ANDROID +namespace mozilla { + class AndroidMediaLayer; +} +#endif + // X.h defines KeyPress #ifdef KeyPress #undef KeyPress @@ -297,6 +303,26 @@ public: void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect); bool UseAsyncRendering(); + +#ifdef ANDROID + nsIntRect GetVisibleRect() { + return nsIntRect(0, 0, mPluginWindow->width, mPluginWindow->height); + } + + void SetInverted(bool aInverted) { + mInverted = aInverted; + } + + bool Inverted() { + return mInverted; + } + + mozilla::AndroidMediaLayer* Layer() { + return mLayer; + } + + void Invalidate(); +#endif private: @@ -310,10 +336,17 @@ private: void FixUpURLS(const nsString &name, nsAString &value); #ifdef ANDROID + void SendSize(int width, int height); + void SendOnScreenEvent(bool onScreen); + bool AddPluginView(const gfxRect& aRect); void RemovePluginView(); - bool mPluginViewAdded; - gfxRect mLastPluginRect; + + bool mOnScreen; + bool mInverted; + + // For kOpenGL_ANPDrawingModel + mozilla::AndroidMediaLayer *mLayer; #endif nsPluginNativeWindow *mPluginWindow; diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 102637134917..25c9616adc35 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1695,7 +1695,6 @@ nsObjectFrame::PaintPlugin(nsDisplayListBuilder* aBuilder, // The matrix includes the frame's position, so we need to transform // from 0,0 to get the correct coordinates. frameGfxRect.MoveTo(0, 0); - matrix2d.NudgeToIntegers(); mInstanceOwner->Paint(ctx, matrix2d.Transform(frameGfxRect), dirtyGfxRect); return; diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index b35fa3478191..9e1efe4dc144 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -44,10 +44,12 @@ import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient; import org.mozilla.gecko.gfx.IntSize; +import org.mozilla.gecko.gfx.Layer; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.PlaceholderLayerClient; import org.mozilla.gecko.gfx.RectUtils; +import org.mozilla.gecko.gfx.SurfaceTextureLayer; import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.Tab.HistoryEntry; @@ -217,8 +219,9 @@ abstract public class GeckoApp public ArrayList mPackageInfoCache = new ArrayList(); String[] getPluginDirectories() { - // we don't support Honeycomb and later - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + // we don't support Honeycomb + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && + Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) return new String[0]; Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start of getPluginDirectories"); @@ -749,7 +752,7 @@ abstract public class GeckoApp mLayerController.setWaitForTouchListeners(false); if (tab != null) - hidePluginViews(tab); + hidePlugins(tab, true); } } }); @@ -1374,8 +1377,6 @@ abstract public class GeckoApp mMainHandler.post(new Runnable() { public void run() { PluginLayoutParams lp; - JSONObject viewportObject; - ViewportMetrics pluginViewport; Tabs tabs = Tabs.getInstance(); Tab tab = tabs.getSelectedTab(); @@ -1384,9 +1385,10 @@ abstract public class GeckoApp return; ViewportMetrics targetViewport = mLayerController.getViewportMetrics(); + ViewportMetrics pluginViewport; try { - viewportObject = new JSONObject(metadata); + JSONObject viewportObject = new JSONObject(metadata); pluginViewport = new ViewportMetrics(viewportObject); } catch (JSONException e) { Log.e(LOGTAG, "Bad viewport metadata: ", e); @@ -1441,28 +1443,128 @@ abstract public class GeckoApp }); } - public void hidePluginViews() { + public Surface createSurface() { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return null; + + SurfaceTextureLayer layer = SurfaceTextureLayer.create(); + if (layer == null) + return null; + + Surface surface = layer.getSurface(); + tab.addPluginLayer(surface, layer); + return surface; + } + + public void destroySurface(Surface surface) { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return; + + tab.removePluginLayer(surface); + } + + 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) + return; + + ViewportMetrics metrics; + try { + metrics = new ViewportMetrics(new JSONObject(metadata)); + } catch (JSONException e) { + Log.e(LOGTAG, "Bad viewport metadata: ", e); + return; + } + + PointF origin = metrics.getDisplayportOrigin(); + x = x + (int)origin.x; + y = y + (int)origin.y; + + LayerView layerView = mLayerController.getView(); + SurfaceTextureLayer layer = (SurfaceTextureLayer)tab.getPluginLayer(surface); + if (layer == null) + return; + + layer.update(new Point(x, y), new IntSize(w, h), metrics.getZoomFactor(), inverted, blend); + layerView.addLayer(layer); + + // FIXME: shouldn't be necessary, layer will request + // one when it gets first frame + layerView.requestRender(); + } + + private void hidePluginLayer(Layer layer) { + LayerView layerView = mLayerController.getView(); + layerView.removeLayer(layer); + layerView.requestRender(); + } + + private void showPluginLayer(Layer layer) { + LayerView layerView = mLayerController.getView(); + layerView.addLayer(layer); + layerView.requestRender(); + } + + public void hideSurface(Surface surface) { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return; + + Layer layer = tab.getPluginLayer(surface); + if (layer == null) + return; + + hidePluginLayer(layer); + } + + public void requestRender() { + mLayerController.getView().requestRender(); + } + + public void hidePlugins(boolean hideLayers) { Tabs tabs = Tabs.getInstance(); Tab tab = tabs.getSelectedTab(); if (tab == null) return; - hidePluginViews(tab); + hidePlugins(tab, hideLayers); } - public void hidePluginViews(Tab tab) { + public void hidePlugins(Tab tab, boolean hideLayers) { for (View view : tab.getPluginViews()) { view.setVisibility(View.GONE); } + + if (hideLayers) { + for (Layer layer : tab.getPluginLayers()) { + hidePluginLayer(layer); + } + + requestRender(); + } } - public void showPluginViews() { + public void showPlugins() { repositionPluginViews(true); } - public void showPluginViews(Tab tab) { + public void showPlugins(Tab tab) { repositionPluginViews(tab, true); + + for (Layer layer : tab.getPluginLayers()) { + showPluginLayer(layer); + } + + requestRender(); } public void repositionPluginViews(boolean setVisible) { @@ -2619,8 +2721,10 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams } public void reset(int aX, int aY, int aWidth, int aHeight, ViewportMetrics aViewport) { - x = mOriginalX = aX; - y = mOriginalY = aY; + PointF origin = aViewport.getDisplayportOrigin(); + + x = mOriginalX = aX + (int)origin.x; + y = mOriginalY = aY + (int)origin.y; width = mOriginalWidth = aWidth; height = mOriginalHeight = aHeight; mOriginalViewport = aViewport; @@ -2651,4 +2755,8 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams reposition(offset, viewport.getZoomFactor()); } + + public float getLastResolution() { + return mLastResolution; + } } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 56e7bbb77210..630cf8953cfd 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -226,6 +226,7 @@ public class GeckoAppShell try { sHandler = lt.mHandlerQueue.take(); } catch (InterruptedException ie) {} + } return sHandler; } @@ -567,7 +568,7 @@ public class GeckoAppShell while (sGeckoPendingAcks.getCount() != 0) { try { sGeckoPendingAcks.await(); - } catch (InterruptedException e) {} + } catch(InterruptedException e) {} } sGeckoPendingAcks = null; } @@ -1391,10 +1392,40 @@ public class GeckoAppShell } public static void removePluginView(View view) { - Log.i(LOGTAG, "remove view:" + view); + Log.i(LOGTAG, "removePluginView:" + view); GeckoApp.mAppContext.removePluginView(view); } + public static Surface createSurface() { + Log.i(LOGTAG, "createSurface"); + return GeckoApp.mAppContext.createSurface(); + } + + public static void showSurface(Surface surface, + int x, int y, + int w, int h, + boolean inverted, + boolean blend, + String metadata) + { + Log.i(LOGTAG, "showSurface:" + surface + " @ x:" + x + " y:" + y + " w:" + w + " h:" + h + " inverted: " + inverted + " blend: " + blend + " metadata: " + metadata); + try { + GeckoApp.mAppContext.showSurface(surface, x, y, w, h, inverted, blend, metadata); + } catch (Exception e) { + Log.i(LOGTAG, "Error in showSurface:", e); + } + } + + public static void hideSurface(Surface surface) { + Log.i(LOGTAG, "hideSurface:" + surface); + GeckoApp.mAppContext.hideSurface(surface); + } + + public static void destroySurface(Surface surface) { + Log.i(LOGTAG, "destroySurface:" + surface); + GeckoApp.mAppContext.destroySurface(surface); + } + public static Class loadPluginClass(String className, String libName) { Log.i(LOGTAG, "in loadPluginClass... attempting to access className, then libName....."); Log.i(LOGTAG, "className: " + className); diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 81999c349111..62b2971b50f4 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -124,7 +124,9 @@ FENNEC_JAVA_FILES = \ gfx/RectUtils.java \ gfx/ScrollbarLayer.java \ gfx/SingleTileLayer.java \ + gfx/SurfaceTextureLayer.java \ gfx/TextLayer.java \ + gfx/TextureGenerator.java \ gfx/TextureReaper.java \ gfx/TileLayer.java \ gfx/ViewportMetrics.java \ diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 8c35b25de93d..21672a08d2ea 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -44,13 +44,16 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.util.DisplayMetrics; import android.util.Log; +import android.view.Surface; import android.view.View; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.gfx.Layer; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -83,6 +86,7 @@ public final class Tab { private String mContentType; private boolean mHasTouchListeners; private ArrayList mPluginViews; + private HashMap mPluginLayers; private boolean mHasLoaded; public static final class HistoryEntry { @@ -117,6 +121,7 @@ public final class Tab { mDocumentURI = ""; mContentType = ""; mPluginViews = new ArrayList(); + mPluginLayers = new HashMap(); mHasLoaded = false; } @@ -568,4 +573,20 @@ public final class Tab { public View[] getPluginViews() { return mPluginViews.toArray(new View[mPluginViews.size()]); } + + public void addPluginLayer(Surface surface, Layer layer) { + mPluginLayers.put(surface, layer); + } + + public Layer getPluginLayer(Surface surface) { + return mPluginLayers.get(surface); + } + + public Collection getPluginLayers() { + return mPluginLayers.values(); + } + + public Layer removePluginLayer(Surface surface) { + return mPluginLayers.remove(surface); + } } diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 6e466b9912d4..e22986d49afa 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -136,7 +136,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mBrowserToolbar.setShadowVisibility(!(tab.getURL().startsWith("about:"))); if (oldTab != null) - GeckoApp.mAppContext.hidePluginViews(oldTab); + GeckoApp.mAppContext.hidePlugins(oldTab, true); } } }); @@ -200,7 +200,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mAppContext.onTabsChanged(closedTab); GeckoApp.mBrowserToolbar.updateTabs(Tabs.getInstance().getCount()); GeckoApp.mDoorHangerPopup.updatePopup(); - GeckoApp.mAppContext.hidePluginViews(closedTab); + GeckoApp.mAppContext.hidePlugins(closedTab, true); } }); diff --git a/mobile/android/base/gfx/Layer.java b/mobile/android/base/gfx/Layer.java index 4c3328667542..46b81b7587f0 100644 --- a/mobile/android/base/gfx/Layer.java +++ b/mobile/android/base/gfx/Layer.java @@ -49,12 +49,13 @@ import org.mozilla.gecko.FloatUtils; public abstract class Layer { private final ReentrantLock mTransactionLock; private boolean mInTransaction; - private Point mOrigin; private Point mNewOrigin; - private float mResolution; private float mNewResolution; private LayerView mView; + protected Point mOrigin; + protected float mResolution; + public Layer() { mTransactionLock = new ReentrantLock(); mOrigin = new Point(0, 0); diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index 573e1884b838..7704e436ee23 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -46,6 +46,7 @@ import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.NinePatchTileLayer; import org.mozilla.gecko.gfx.SingleTileLayer; import org.mozilla.gecko.gfx.TextureReaper; +import org.mozilla.gecko.gfx.TextureGenerator; import org.mozilla.gecko.gfx.TextLayer; import org.mozilla.gecko.gfx.TileLayer; import android.content.Context; @@ -62,6 +63,7 @@ import android.view.WindowManager; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import java.nio.IntBuffer; +import java.util.ArrayList; /** * The layer renderer implements the rendering logic for a layer view. @@ -90,6 +92,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private RenderContext mLastPageContext; private int mMaxTextureSize; + private ArrayList mExtraLayers = new ArrayList(); + // Dropped frames display private int[] mFrameTimings; private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames; @@ -152,12 +156,34 @@ public class LayerRenderer implements GLSurfaceView.Renderer { int maxTextureSizeResult[] = new int[1]; gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0); mMaxTextureSize = maxTextureSizeResult[0]; + + TextureGenerator.get().fill(); } public int getMaxTextureSize() { return mMaxTextureSize; } + public void addLayer(Layer layer) { + LayerController controller = mView.getController(); + + synchronized (controller) { + if (mExtraLayers.contains(layer)) { + mExtraLayers.remove(layer); + } + + mExtraLayers.add(layer); + } + } + + public void removeLayer(Layer layer) { + LayerController controller = mView.getController(); + + synchronized (controller) { + mExtraLayers.remove(layer); + } + } + /** * Called whenever a new frame is about to be drawn. */ @@ -165,6 +191,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { long frameStartTime = SystemClock.uptimeMillis(); TextureReaper.get().reap(gl); + TextureGenerator.get().fill(); LayerController controller = mView.getController(); RenderContext screenContext = createScreenContext(); @@ -198,6 +225,9 @@ public class LayerRenderer implements GLSurfaceView.Renderer { updated &= mVertScrollLayer.update(gl, pageContext); updated &= mHorizScrollLayer.update(gl, pageContext); + for (Layer layer : mExtraLayers) + updated &= layer.update(gl, pageContext); + /* Draw the background. */ mBackgroundLayer.draw(screenContext); @@ -222,6 +252,10 @@ public class LayerRenderer implements GLSurfaceView.Renderer { gl.glDisable(GL10.GL_SCISSOR_TEST); + /* Draw any extra layers that were added (likely plugins) */ + for (Layer layer : mExtraLayers) + layer.draw(pageContext); + /* Draw the vertical scrollbar. */ IntSize screenSize = new IntSize(controller.getViewportSize()); if (pageRect.height() > screenSize.height) diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java index 7781c1b97599..0e782daea9e6 100644 --- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -224,6 +224,14 @@ public class LayerView extends GLSurfaceView } } + public void addLayer(Layer layer) { + mRenderer.addLayer(layer); + } + + public void removeLayer(Layer layer) { + mRenderer.removeLayer(layer); + } + /** * Returns the time elapsed between the first call of requestRender() after * the last call of getRenderTime(), in nanoseconds. diff --git a/mobile/android/base/gfx/SurfaceTextureLayer.java b/mobile/android/base/gfx/SurfaceTextureLayer.java new file mode 100644 index 000000000000..8f4799416dbe --- /dev/null +++ b/mobile/android/base/gfx/SurfaceTextureLayer.java @@ -0,0 +1,259 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009-2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.gecko.gfx; + +import org.mozilla.gecko.GeckoApp; + +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.SurfaceTexture; +import android.opengl.GLES11; +import android.opengl.GLES11Ext; +import android.opengl.Matrix; +import android.util.Log; +import android.view.Surface; +import javax.microedition.khronos.opengles.GL10; +import javax.microedition.khronos.opengles.GL11Ext; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import android.hardware.Camera; + +public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrameAvailableListener { + private static final String LOGTAG = "SurfaceTextureLayer"; + private static final int LOCAL_GL_TEXTURE_EXTERNAL_OES = 0x00008d65; // This is only defined in API level 15 for some reason (Android 4.0.3) + + private final SurfaceTexture mSurfaceTexture; + private final Surface mSurface; + private int mTextureId; + private boolean mHaveFrame; + + private IntSize mSize; + private IntSize mNewSize; + + private boolean mInverted; + private boolean mNewInverted; + private boolean mBlend; + private boolean mNewBlend; + + private FloatBuffer textureBuffer; + private FloatBuffer textureBufferInverted; + + public SurfaceTextureLayer(int textureId) { + mTextureId = textureId; + mHaveFrame = true; + mInverted = false; + + mSurfaceTexture = new SurfaceTexture(mTextureId); + mSurfaceTexture.setOnFrameAvailableListener(this); + + mSurface = new Surface(mSurfaceTexture); + + float textureMap[] = { + 0.0f, 1.0f, // top left + 0.0f, 0.0f, // bottom left + 1.0f, 1.0f, // top right + 1.0f, 0.0f, // bottom right + }; + + textureBuffer = createBuffer(textureMap); + + float textureMapInverted[] = { + 0.0f, 0.0f, // bottom left + 0.0f, 1.0f, // top left + 1.0f, 0.0f, // bottom right + 1.0f, 1.0f, // top right + }; + + textureBufferInverted = createBuffer(textureMapInverted); + } + + public static SurfaceTextureLayer create() { + int textureId = TextureGenerator.get().take(); + if (textureId == 0) + return null; + + return new SurfaceTextureLayer(textureId); + } + + // For SurfaceTexture.OnFrameAvailableListener + public void onFrameAvailable(SurfaceTexture texture) { + // FIXME: for some reason this doesn't get called + mHaveFrame = true; + GeckoApp.mAppContext.requestRender(); + } + + private FloatBuffer createBuffer(float[] input) { + // a float has 4 bytes so we allocate for each coordinate 4 bytes + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(input.length * 4); + byteBuffer.order(ByteOrder.nativeOrder()); + + FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); + floatBuffer.put(input); + floatBuffer.position(0); + + return floatBuffer; + } + + public void update(Point origin, IntSize size, float resolution, boolean inverted, boolean blend) { + beginTransaction(null); + + setOrigin(origin); + setResolution(resolution); + + mNewSize = size; + mNewInverted = inverted; + mNewBlend = blend; + + endTransaction(); + } + + @Override + public IntSize getSize() { return mSize; } + + @Override + protected void finalize() throws Throwable { + if (mSurfaceTexture != null) + mSurfaceTexture.release(); + + if (mTextureId > 0) + TextureReaper.get().add(mTextureId); + } + + @Override + protected boolean performUpdates(GL10 gl, RenderContext context) { + super.performUpdates(gl, context); + + if (mNewSize != null) { + mSize = mNewSize; + mNewSize = null; + } + + mInverted = mNewInverted; + mBlend = mNewBlend; + + gl.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + gl.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); + mSurfaceTexture.updateTexImage(); + gl.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + + // FIXME: we should return true and rely on onFrameAvailable, but + // that isn't working for some reason + return false; + } + + private float mapToGLCoords(float input, float viewport, boolean flip) { + if (flip) input = viewport - input; + return ((input / viewport) * 2.0f) - 1.0f; + } + + @Override + public void draw(RenderContext context) { + + // Enable GL_TEXTURE_EXTERNAL_OES and bind our texture + GLES11.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + GLES11.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); + + // Enable vertex and texture coordinate buffers + GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY); + GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + // Load whatever texture transform the SurfaceMatrix needs + float[] matrix = new float[16]; + mSurfaceTexture.getTransformMatrix(matrix); + GLES11.glMatrixMode(GLES11.GL_TEXTURE); + GLES11.glLoadMatrixf(matrix, 0); + + // Figure out vertices to put the texture in the right spot on the screen + IntSize size = getSize(); + RectF bounds = getBounds(context, new FloatSize(size)); + RectF viewport = context.viewport; + bounds.offset(-viewport.left, -viewport.top); + + float vertices[] = new float[8]; + + // Bottom left + vertices[0] = mapToGLCoords(bounds.left, viewport.width(), false); + vertices[1] = mapToGLCoords(bounds.bottom, viewport.height(), true); + + // Top left + vertices[2] = mapToGLCoords(bounds.left, viewport.width(), false); + vertices[3] = mapToGLCoords(bounds.top, viewport.height(), true); + + // Bottom right + vertices[4] = mapToGLCoords(bounds.right, viewport.width(), false); + vertices[5] = mapToGLCoords(bounds.bottom, viewport.height(), true); + + // Top right + vertices[6] = mapToGLCoords(bounds.right, viewport.width(), false); + vertices[7] = mapToGLCoords(bounds.top, viewport.height(), true); + + // Set texture and vertex buffers + GLES11.glVertexPointer(2, GL10.GL_FLOAT, 0, createBuffer(vertices)); + GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mInverted ? textureBufferInverted : textureBuffer); + + if (mBlend) { + GLES11.glEnable(GL10.GL_BLEND); + GLES11.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); + } + + // Draw the vertices as triangle strip + GLES11.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 2); + + // Clean up + GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY); + GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + GLES11.glLoadIdentity(); + + if (mBlend) { + GLES11.glDisable(GL10.GL_BLEND); + } + } + + public SurfaceTexture getSurfaceTexture() { + return mSurfaceTexture; + } + + public Surface getSurface() { + return mSurface; + } +} + diff --git a/mobile/android/base/gfx/TextureGenerator.java b/mobile/android/base/gfx/TextureGenerator.java new file mode 100644 index 000000000000..4392c55e1ae7 --- /dev/null +++ b/mobile/android/base/gfx/TextureGenerator.java @@ -0,0 +1,73 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009-2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.gecko.gfx; + +import android.opengl.GLES10; +import java.util.Stack; + +public class TextureGenerator { + private static final int MIN_TEXTURES = 5; + + private static TextureGenerator sSharedInstance; + private Stack mTextureIds; + + private TextureGenerator() { mTextureIds = new Stack(); } + + public static TextureGenerator get() { + if (sSharedInstance == null) + sSharedInstance = new TextureGenerator(); + return sSharedInstance; + } + + public synchronized int take() { + if (mTextureIds.empty()) + return 0; + + return (int)mTextureIds.pop(); + } + + public synchronized void fill() { + int[] textures = new int[1]; + while (mTextureIds.size() < MIN_TEXTURES) { + GLES10.glGenTextures(1, textures, 0); + mTextureIds.push(textures[0]); + } + } +} + + diff --git a/mobile/android/base/gfx/TextureReaper.java b/mobile/android/base/gfx/TextureReaper.java index 0527ae73811d..047406234b44 100644 --- a/mobile/android/base/gfx/TextureReaper.java +++ b/mobile/android/base/gfx/TextureReaper.java @@ -55,7 +55,11 @@ public class TextureReaper { public void add(int[] textureIDs) { for (int textureID : textureIDs) - mDeadTextureIDs.add(textureID); + add(textureID); + } + + public void add(int textureID) { + mDeadTextureIDs.add(textureID); } public void reap(GL10 gl) { diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index 505fa2a11d20..b4fc39abf753 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -293,6 +293,7 @@ public class PanZoomController return false; } cancelTouch(); + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); // fall through case PANNING_HOLD_LOCKED: GeckoApp.mAppContext.mAutoCompletePopup.hide(); @@ -474,6 +475,8 @@ public class PanZoomController stopAnimationTimer(); } + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); + mAnimationTimer = new Timer("Animation Timer"); mAnimationRunnable = runnable; mAnimationTimer.scheduleAtFixedRate(new TimerTask() { @@ -492,6 +495,8 @@ public class PanZoomController mAnimationRunnable.terminate(); mAnimationRunnable = null; } + + GeckoApp.mAppContext.showPlugins(); } private float getVelocity() { @@ -581,7 +586,6 @@ public class PanZoomController finishBounce(); finishAnimation(); mState = PanZoomState.NOTHING; - GeckoApp.mAppContext.showPluginViews(); } /* Performs one frame of a bounce animation. */ @@ -661,6 +665,7 @@ public class PanZoomController stopAnimationTimer(); // Force a viewport synchronisation + GeckoApp.mAppContext.showPlugins(); mController.setForceRedraw(); mController.notifyLayerClientOfGeometryChange(); } @@ -741,7 +746,7 @@ public class PanZoomController mState = PanZoomState.PINCHING; mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); - GeckoApp.mAppContext.hidePluginViews(); + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers, only views */); GeckoApp.mAppContext.mAutoCompletePopup.hide(); cancelTouch(); @@ -806,9 +811,9 @@ public class PanZoomController startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime()); // Force a viewport synchronisation + GeckoApp.mAppContext.showPlugins(); mController.setForceRedraw(); mController.notifyLayerClientOfGeometryChange(); - GeckoApp.mAppContext.showPluginViews(); } public boolean getRedrawHint() { @@ -862,7 +867,6 @@ public class PanZoomController } private boolean animatedZoomTo(RectF zoomToRect) { - GeckoApp.mAppContext.hidePluginViews(); GeckoApp.mAppContext.mAutoCompletePopup.hide(); mState = PanZoomState.ANIMATED_ZOOM; diff --git a/other-licenses/skia-npapi/Makefile.in b/other-licenses/skia-npapi/Makefile.in index c067c426077e..ba85042ea3cc 100644 --- a/other-licenses/skia-npapi/Makefile.in +++ b/other-licenses/skia-npapi/Makefile.in @@ -56,6 +56,7 @@ LOCAL_INCLUDES += \ -I$(topsrcdir)/dom/plugins/base/android \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ + -I$(topsrcdir)/gfx/gl \ $(NULL) diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 2e33871497f4..d9b1c75af395 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -1709,9 +1709,9 @@ AndroidBridge::ReleaseNativeWindow(void *window) } bool -AndroidBridge::SetNativeWindowFormat(void *window, int format) +AndroidBridge::SetNativeWindowFormat(void *window, int width, int height, int format) { - return ANativeWindow_setBuffersGeometry(window, 0, 0, format) == 0; + return ANativeWindow_setBuffersGeometry(window, width, height, format) == 0; } bool @@ -1857,3 +1857,85 @@ extern "C" { return jEnv; } } + +jobject +AndroidBridge::CreateSurface() +{ +#ifndef MOZ_JAVA_COMPOSITOR + return NULL; +#else + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "createSurface", + "()Landroid/view/Surface;"); + + jobject surface = env->CallStaticObjectMethod(cls, method); + if (surface) + env->NewGlobalRef(surface); + + return surface; +#endif +} + +void +AndroidBridge::DestroySurface(jobject surface) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "destroySurface", + "(Landroid/view/Surface;)V"); + env->CallStaticVoidMethod(cls, method, surface); + env->DeleteGlobalRef(surface); +#endif +} + +void +AndroidBridge::ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame; + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + nsAutoString metadata; + nsCOMPtr metadataProvider = GetDrawMetadataProvider(); + metadataProvider->GetDrawMetadata(metadata); + + jstring jMetadata = env->NewString(nsPromiseFlatString(metadata).get(), metadata.Length()); + + jmethodID method = env->GetStaticMethodID(cls, + "showSurface", + "(Landroid/view/Surface;IIIIZZLjava/lang/String;)V"); + + env->CallStaticVoidMethod(cls, method, surface, + (int)aRect.x, (int)aRect.y, + (int)aRect.width, (int)aRect.height, + aInverted, aBlend, jMetadata); +#endif +} + +void +AndroidBridge::HideSurface(jobject surface) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "hideSurface", + "(Landroid/view/Surface;)V"); + env->CallStaticVoidMethod(cls, method, surface); +#endif +} diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 388fccf8e4f0..9d0d2f164d5b 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -54,6 +54,7 @@ #include "nsIMutableArray.h" #include "nsIMIMEInfo.h" #include "nsColor.h" +#include "gfxRect.h" #include "nsIAndroidBridge.h" @@ -341,7 +342,7 @@ public: void *AcquireNativeWindow(jobject surface); void ReleaseNativeWindow(void *window); - bool SetNativeWindowFormat(void *window, int format); + bool SetNativeWindowFormat(void *window, int width, int height, int format); bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride); bool UnlockWindow(void *window); @@ -378,6 +379,11 @@ public: void EnableNetworkNotifications(); void DisableNetworkNotifications(); + jobject CreateSurface(); + void DestroySurface(jobject surface); + void ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend); + void HideSurface(jobject surface); + protected: static AndroidBridge *sBridge; diff --git a/widget/android/AndroidMediaLayer.cpp b/widget/android/AndroidMediaLayer.cpp new file mode 100644 index 000000000000..572dadcc46b0 --- /dev/null +++ b/widget/android/AndroidMediaLayer.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include "AndroidMediaLayer.h" +#include "AndroidBridge.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidMediaLayer" , ## args) + + +namespace mozilla { + +AndroidMediaLayer::AndroidMediaLayer() + : mInverted(false) { +} + +AndroidMediaLayer::~AndroidMediaLayer() { + if (mContentData.window) { + AndroidBridge::Bridge()->ReleaseNativeWindow(mContentData.window); + mContentData.window = NULL; + } + + if (mContentData.surface) { + AndroidBridge::Bridge()->DestroySurface(mContentData.surface); + mContentData.surface = NULL; + } + + std::map::iterator it; + + for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { + SurfaceData* data = it->second; + + AndroidBridge::Bridge()->ReleaseNativeWindow(data->window); + AndroidBridge::Bridge()->DestroySurface(data->surface); + delete data; + } + + mVideoSurfaces.clear(); +} + +bool AndroidMediaLayer::EnsureContentSurface() { + if (!mContentData.surface) { + mContentData.surface = AndroidBridge::Bridge()->CreateSurface(); + if (mContentData.surface) { + mContentData.window = AndroidBridge::Bridge()->AcquireNativeWindow(mContentData.surface); + AndroidBridge::Bridge()->SetNativeWindowFormat(mContentData.window, 0, 0, AndroidBridge::WINDOW_FORMAT_RGBA_8888); + } + } + + return mContentData.surface && mContentData.window; +} + +void* AndroidMediaLayer::GetNativeWindowForContent() { + if (!EnsureContentSurface()) + return NULL; + + return mContentData.window; +} + +void* AndroidMediaLayer::RequestNativeWindowForVideo() { + jobject surface = AndroidBridge::Bridge()->CreateSurface(); + if (surface) { + void* window = AndroidBridge::Bridge()->AcquireNativeWindow(surface); + if (window) { + AndroidBridge::Bridge()->SetNativeWindowFormat(window, 0, 0, AndroidBridge::WINDOW_FORMAT_RGBA_8888); + mVideoSurfaces[window] = new SurfaceData(surface, window); + return window; + } else { + LOG("Failed to create native window from surface"); + + // Cleanup + AndroidBridge::Bridge()->DestroySurface(surface); + } + } + + return NULL; +} + +void AndroidMediaLayer::ReleaseNativeWindowForVideo(void* aWindow) { + if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end()) + return; + + SurfaceData* data = mVideoSurfaces[aWindow]; + + AndroidBridge::Bridge()->ReleaseNativeWindow(data->window); + AndroidBridge::Bridge()->DestroySurface(data->surface); + + mVideoSurfaces.erase(aWindow); + delete data; +} + +void AndroidMediaLayer::SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions) { + if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end()) + return; + + SurfaceData* data = mVideoSurfaces[aWindow]; + data->dimensions = aDimensions; +} + +void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) { + + std::map::iterator it; + + if (EnsureContentSurface()) + AndroidBridge::Bridge()->ShowSurface(mContentData.surface, aRect, mInverted, true); + + for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { + SurfaceData* data = it->second; + + // The video window dimension we get is not adjusted by zoom factor (unlike the + // content window). Fix it up here. + gfxRect scaledDimensions = data->dimensions; + 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); + } +} + +} /* mozilla */ diff --git a/widget/android/AndroidMediaLayer.h b/widget/android/AndroidMediaLayer.h new file mode 100644 index 000000000000..eb402743fc17 --- /dev/null +++ b/widget/android/AndroidMediaLayer.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef AndroidMediaLayer_h_ +#define AndroidMediaLayer_h_ + +#include +#include +#include "gfxRect.h" + +namespace mozilla { + +class AndroidMediaLayer +{ +public: + + AndroidMediaLayer(); + virtual ~AndroidMediaLayer(); + + void* GetNativeWindowForContent(); + + void* RequestNativeWindowForVideo(); + void ReleaseNativeWindowForVideo(void* aWindow); + + void SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions); + + void UpdatePosition(const gfxRect& aRect, float aZoomLevel); + + bool Inverted() { + return mInverted; + } + + void SetInverted(bool aInverted) { + mInverted = aInverted; + } + +private: + bool mInverted; + + class SurfaceData { + public: + SurfaceData() : + surface(NULL), window(NULL) { + } + + SurfaceData(jobject aSurface, void* aWindow) : + surface(aSurface), window(aWindow) { + } + + jobject surface; + void* window; + gfxRect dimensions; + }; + + bool EnsureContentSurface(); + + SurfaceData mContentData; + std::map mVideoSurfaces; +}; + +} /* mozilla */ +#endif /* AndroidMediaLayer_h_ */ diff --git a/widget/android/Makefile.in b/widget/android/Makefile.in index 7413f0e57cc9..236837b8e200 100644 --- a/widget/android/Makefile.in +++ b/widget/android/Makefile.in @@ -68,6 +68,7 @@ CPPSRCS = \ AndroidDirectTexture.cpp \ AndroidGraphicBuffer.cpp \ AndroidJNI.cpp \ + AndroidMediaLayer.cpp \ nsWindow.cpp \ nsLookAndFeel.cpp \ nsScreenManagerAndroid.cpp \ diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 57606d898733..55a9ce3b7850 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1019,7 +1019,7 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) if (surface) { sNativeWindow = AndroidBridge::Bridge()->AcquireNativeWindow(surface); if (sNativeWindow) { - AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, AndroidBridge::WINDOW_FORMAT_RGB_565); + AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, 0, 0, AndroidBridge::WINDOW_FORMAT_RGB_565); } } }