gecko/dom/plugins/PluginInstanceChild.cpp

2987 lines
94 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: sw=4 ts=4 et :
* ***** 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 Plugin App.
*
* The Initial Developer of the Original Code is
* Chris Jones <jones.chris.g@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jim Mathies <jmathies@mozilla.com>
*
* 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 "PluginInstanceChild.h"
#include "PluginModuleChild.h"
#include "BrowserStreamChild.h"
#include "PluginStreamChild.h"
#include "StreamNotifyChild.h"
#include "PluginProcessChild.h"
#include "gfxASurface.h"
#include "gfxContext.h"
#ifdef MOZ_X11
#include "gfxXlibSurface.h"
#endif
#ifdef XP_WIN
#include "mozilla/gfx/SharedDIBSurface.h"
using mozilla::gfx::SharedDIBSurface;
#endif
#include "gfxSharedImageSurface.h"
#include "gfxUtils.h"
#include "gfxAlphaRecovery.h"
#include "mozilla/ipc/SyncChannel.h"
using mozilla::ipc::ProcessChild;
using namespace mozilla::plugins;
#ifdef MOZ_WIDGET_GTK2
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <gdk/gdk.h>
#include "gtk2xtbin.h"
#elif defined(MOZ_WIDGET_QT)
#include <QX11Info>
#elif defined(OS_WIN)
#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E
#endif
#include "nsWindowsDllInterceptor.h"
typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu,
UINT uFlags,
int x,
int y,
int nReserved,
HWND hWnd,
CONST RECT *prcRect);
static WindowsDllInterceptor sUser32Intercept;
static HWND sWinlessPopupSurrogateHWND = NULL;
static User32TrackPopupMenu sUser32TrackPopupMenuStub = NULL;
using mozilla::gfx::SharedDIB;
#include <windows.h>
#include <windowsx.h>
// Flash WM_USER message delay time for PostDelayedTask. Borrowed
// from Chromium's web plugin delegate src. See 'flash msg throttling
// helpers' section for details.
const int kFlashWMUSERMessageThrottleDelayMs = 5;
static const TCHAR kPluginIgnoreSubclassProperty[] = TEXT("PluginIgnoreSubclassProperty");
#elif defined(XP_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
#endif // defined(XP_MACOSX)
template<>
struct RunnableMethodTraits<PluginInstanceChild>
{
static void RetainCallee(PluginInstanceChild* obj) { }
static void ReleaseCallee(PluginInstanceChild* obj) { }
};
PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
const nsCString& aMimeType)
: mPluginIface(aPluginIface)
, mQuirks(0)
, mCachedWindowActor(nsnull)
, mCachedElementActor(nsnull)
#if defined(OS_WIN)
, mPluginWindowHWND(0)
, mPluginWndProc(0)
, mPluginParentHWND(0)
, mCachedWinlessPluginHWND(0)
, mWinlessPopupSurrogateHWND(0)
, mWinlessThrottleOldWndProc(0)
, mWinlessHiddenMsgHWND(0)
#endif // OS_WIN
, mAsyncCallMutex("PluginInstanceChild::mAsyncCallMutex")
#if defined(OS_MACOSX)
#if defined(__i386__)
, mEventModel(NPEventModelCarbon)
#endif
, mShColorSpace(nsnull)
, mShContext(nsnull)
, mDrawingModel(NPDrawingModelCoreGraphics)
, mCurrentEvent(nsnull)
#endif
, mLayersRendering(false)
, mAccumulatedInvalidRect(0,0,0,0)
, mIsTransparent(false)
, mSurfaceType(gfxASurface::SurfaceTypeMax)
, mPendingForcePaint(false)
, mCurrentInvalidateTask(nsnull)
, mPendingPluginCall(false)
, mDoAlphaExtraction(false)
, mSurfaceDifferenceRect(0,0,0,0)
#ifdef MOZ_X11
, mFlash10Quirks(false)
#endif
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
, mMaemoImageRendering(PR_FALSE)
#endif
{
memset(&mWindow, 0, sizeof(mWindow));
mData.ndata = (void*) this;
mData.pdata = nsnull;
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
mWindow.ws_info = &mWsInfo;
memset(&mWsInfo, 0, sizeof(mWsInfo));
mWsInfo.display = DefaultXDisplay();
#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
#if defined(OS_WIN)
memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
#endif // OS_WIN
InitQuirksModes(aMimeType);
#if defined(OS_WIN)
InitPopupMenuHook();
#endif // OS_WIN
#ifdef MOZ_X11
const char *description = NULL;
mPluginIface->getvalue(GetNPP(), NPPVpluginDescriptionString,
&description);
if (description) {
NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10.");
mFlash10Quirks = StringBeginsWith(nsDependentCString(description), flash10Head);
}
#endif
}
PluginInstanceChild::~PluginInstanceChild()
{
#if defined(OS_WIN)
DestroyPluginWindow();
#endif
#if defined(OS_MACOSX)
if (mShColorSpace) {
::CGColorSpaceRelease(mShColorSpace);
}
if (mShContext) {
::CGContextRelease(mShContext);
}
#endif
}
void
PluginInstanceChild::InitQuirksModes(const nsCString& aMimeType)
{
// application/x-silverlight
// application/x-silverlight-2
NS_NAMED_LITERAL_CSTRING(silverlight, "application/x-silverlight");
if (FindInReadable(silverlight, aMimeType)) {
mQuirks |= QUIRK_SILVERLIGHT_DEFAULT_TRANSPARENT;
#ifdef OS_WIN
mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
#endif
}
#ifdef OS_WIN
// application/x-shockwave-flash
NS_NAMED_LITERAL_CSTRING(flash, "application/x-shockwave-flash");
if (FindInReadable(flash, aMimeType)) {
mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
mQuirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS;
mQuirks |= QUIRK_FLASH_HOOK_SETLONGPTR;
}
#endif
}
NPError
PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
NPObject** aObject)
{
PluginScriptableObjectChild* actor;
NPError result = NPERR_NO_ERROR;
switch (aValue) {
case NPNVWindowNPObject:
if (!(actor = mCachedWindowActor)) {
PPluginScriptableObjectChild* actorProtocol;
CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result);
if (result == NPERR_NO_ERROR) {
actor = mCachedWindowActor =
static_cast<PluginScriptableObjectChild*>(actorProtocol);
NS_ASSERTION(actor, "Null actor!");
PluginModuleChild::sBrowserFuncs.retainobject(
actor->GetObject(false));
}
}
break;
case NPNVPluginElementNPObject:
if (!(actor = mCachedElementActor)) {
PPluginScriptableObjectChild* actorProtocol;
CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol,
&result);
if (result == NPERR_NO_ERROR) {
actor = mCachedElementActor =
static_cast<PluginScriptableObjectChild*>(actorProtocol);
NS_ASSERTION(actor, "Null actor!");
PluginModuleChild::sBrowserFuncs.retainobject(
actor->GetObject(false));
}
}
break;
default:
NS_NOTREACHED("Don't know what to do with this value type!");
}
#ifdef DEBUG
{
NPError currentResult;
PPluginScriptableObjectChild* currentActor;
switch (aValue) {
case NPNVWindowNPObject:
CallNPN_GetValue_NPNVWindowNPObject(&currentActor,
&currentResult);
break;
case NPNVPluginElementNPObject:
CallNPN_GetValue_NPNVPluginElementNPObject(&currentActor,
&currentResult);
break;
default:
NS_NOTREACHED("Don't know what to do with this value type!");
}
// Make sure that the current actor returned by the parent matches our
// cached actor!
NS_ASSERTION(static_cast<PluginScriptableObjectChild*>(currentActor) ==
actor, "Cached actor is out of date!");
NS_ASSERTION(currentResult == result, "Results don't match?!");
}
#endif
if (result != NPERR_NO_ERROR) {
return result;
}
NPObject* object = actor->GetObject(false);
NS_ASSERTION(object, "Null object?!");
*aObject = PluginModuleChild::sBrowserFuncs.retainobject(object);
return NPERR_NO_ERROR;
}
NPError
PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
void* aValue)
{
PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar));
AssertPluginThread();
switch(aVar) {
case NPNVSupportsWindowless:
#if defined(OS_LINUX) || defined(OS_WIN)
*((NPBool*)aValue) = true;
#else
*((NPBool*)aValue) = false;
#endif
return NPERR_NO_ERROR;
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
case NPNVSupportsWindowlessLocal: {
#ifdef MOZ_WIDGET_QT
const char *graphicsSystem = PR_GetEnv("MOZ_QT_GRAPHICSSYSTEM");
// we should set local rendering to false in order to render X-Plugin
// there is no possibility to change it later on maemo5 platform
mMaemoImageRendering = (!(graphicsSystem && !strcmp(graphicsSystem, "native")));
#endif
*((NPBool*)aValue) = mMaemoImageRendering;
return NPERR_NO_ERROR;
}
#endif
#if defined(OS_LINUX)
case NPNVSupportsXEmbedBool:
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
case NPNVToolkit:
*((NPNToolkitType*)aValue) = NPNVGtk2;
return NPERR_NO_ERROR;
#elif defined(OS_WIN)
case NPNVToolkit:
return NPERR_GENERIC_ERROR;
#endif
case NPNVjavascriptEnabledBool: {
bool v = false;
NPError result;
if (!CallNPN_GetValue_NPNVjavascriptEnabledBool(&v, &result)) {
return NPERR_GENERIC_ERROR;
}
*static_cast<NPBool*>(aValue) = v;
return result;
}
case NPNVisOfflineBool: {
bool v = false;
NPError result;
if (!CallNPN_GetValue_NPNVisOfflineBool(&v, &result)) {
return NPERR_GENERIC_ERROR;
}
*static_cast<NPBool*>(aValue) = v;
return result;
}
case NPNVprivateModeBool: {
bool v = false;
NPError result;
if (!CallNPN_GetValue_NPNVprivateModeBool(&v, &result)) {
return NPERR_GENERIC_ERROR;
}
*static_cast<NPBool*>(aValue) = v;
return result;
}
case NPNVWindowNPObject: // Intentional fall-through
case NPNVPluginElementNPObject: {
NPObject* object;
NPError result = InternalGetNPObjectForValue(aVar, &object);
if (result == NPERR_NO_ERROR) {
*((NPObject**)aValue) = object;
}
return result;
}
case NPNVnetscapeWindow: {
#ifdef XP_WIN
if (mWindow.type == NPWindowTypeDrawable) {
if (mCachedWinlessPluginHWND) {
*static_cast<HWND*>(aValue) = mCachedWinlessPluginHWND;
return NPERR_NO_ERROR;
}
NPError result;
if (!CallNPN_GetValue_NPNVnetscapeWindow(&mCachedWinlessPluginHWND, &result)) {
return NPERR_GENERIC_ERROR;
}
*static_cast<HWND*>(aValue) = mCachedWinlessPluginHWND;
return result;
}
else {
*static_cast<HWND*>(aValue) = mPluginWindowHWND;
return NPERR_NO_ERROR;
}
#elif defined(MOZ_X11)
NPError result;
CallNPN_GetValue_NPNVnetscapeWindow(static_cast<XID*>(aValue), &result);
return result;
#else
return NPERR_GENERIC_ERROR;
#endif
}
#ifdef XP_MACOSX
case NPNVsupportsCoreGraphicsBool: {
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsCoreAnimationBool: {
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsInvalidatingCoreAnimationBool: {
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsCocoaBool: {
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
#ifndef NP_NO_QUICKDRAW
case NPNVsupportsQuickDrawBool: {
*((NPBool*)aValue) = false;
return NPERR_NO_ERROR;
}
#endif /* NP_NO_QUICKDRAW */
#endif /* XP_MACOSX */
default:
PR_LOG(gPluginLog, PR_LOG_WARNING,
("In PluginInstanceChild::NPN_GetValue: Unhandled NPNVariable %i (%s)",
(int) aVar, NPNVariableToString(aVar)));
return NPERR_GENERIC_ERROR;
}
}
NPError
PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
{
PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s (aVar=%i, aValue=%p)",
FULLFUNCTION, (int) aVar, aValue));
AssertPluginThread();
switch (aVar) {
case NPPVpluginWindowBool: {
NPError rv;
bool windowed = (NPBool) (intptr_t) aValue;
if (!CallNPN_SetValue_NPPVpluginWindow(windowed, &rv))
return NPERR_GENERIC_ERROR;
return rv;
}
case NPPVpluginTransparentBool: {
NPError rv;
mIsTransparent = (!!aValue);
if (!CallNPN_SetValue_NPPVpluginTransparent(mIsTransparent, &rv))
return NPERR_GENERIC_ERROR;
return rv;
}
#ifdef XP_MACOSX
case NPPVpluginDrawingModel: {
NPError rv;
int drawingModel = (int16) (intptr_t) aValue;
if (!CallNPN_SetValue_NPPVpluginDrawingModel(drawingModel, &rv))
return NPERR_GENERIC_ERROR;
mDrawingModel = drawingModel;
return rv;
}
case NPPVpluginEventModel: {
NPError rv;
int eventModel = (int16) (intptr_t) aValue;
if (!CallNPN_SetValue_NPPVpluginEventModel(eventModel, &rv))
return NPERR_GENERIC_ERROR;
#if defined(__i386__)
mEventModel = static_cast<NPEventModel>(eventModel);
#endif
return rv;
}
#endif
default:
PR_LOG(gPluginLog, PR_LOG_WARNING,
("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i (%s)",
(int) aVar, NPPVariableToString(aVar)));
return NPERR_GENERIC_ERROR;
}
}
bool
PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(
bool* needs, NPError* rv)
{
AssertPluginThread();
#ifdef MOZ_X11
// The documentation on the types for many variables in NP(N|P)_GetValue
// is vague. Often boolean values are NPBool (1 byte), but
// https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins
// treats NPPVpluginNeedsXEmbed as PRBool (int), and
// on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|.
// thus we can't use NPBool for needsXEmbed, or the three bytes above
// it on the stack would get clobbered. so protect with the larger PRBool.
PRBool needsXEmbed = 0;
if (!mPluginIface->getvalue) {
*rv = NPERR_GENERIC_ERROR;
}
else {
*rv = mPluginIface->getvalue(GetNPP(), NPPVpluginNeedsXEmbed,
&needsXEmbed);
}
*needs = needsXEmbed;
return true;
#else
NS_RUNTIMEABORT("shouldn't be called on non-X11 platforms");
return false; // not reached
#endif
}
bool
PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginScriptableNPObject(
PPluginScriptableObjectChild** aValue,
NPError* aResult)
{
AssertPluginThread();
NPObject* object = nsnull;
NPError result = NPERR_GENERIC_ERROR;
if (mPluginIface->getvalue) {
result = mPluginIface->getvalue(GetNPP(), NPPVpluginScriptableNPObject,
&object);
}
if (result == NPERR_NO_ERROR && object) {
PluginScriptableObjectChild* actor = GetActorForNPObject(object);
// If we get an actor then it has retained. Otherwise we don't need it
// any longer.
PluginModuleChild::sBrowserFuncs.releaseobject(object);
if (actor) {
*aValue = actor;
*aResult = NPERR_NO_ERROR;
return true;
}
NS_ERROR("Failed to get actor!");
result = NPERR_GENERIC_ERROR;
}
else {
result = NPERR_GENERIC_ERROR;
}
*aValue = nsnull;
*aResult = result;
return true;
}
bool
PluginInstanceChild::AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value,
NPError* result)
{
if (!mPluginIface->setvalue) {
*result = NPERR_GENERIC_ERROR;
return true;
}
NPBool v = value;
*result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v);
return true;
}
bool
PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
int16_t* handled)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
#if defined(MOZ_X11) && defined(DEBUG)
if (GraphicsExpose == event.event.type)
PLUGIN_LOG_DEBUG((" received drawable 0x%lx\n",
event.event.xgraphicsexpose.drawable));
#endif
#ifdef XP_MACOSX
// Mac OS X does not define an NPEvent structure. It defines more specific types.
NPCocoaEvent evcopy = event.event;
// Make sure we reset mCurrentEvent in case of an exception
AutoRestore<const NPCocoaEvent*> savePreviousEvent(mCurrentEvent);
// Track the current event for NPN_PopUpContextMenu.
mCurrentEvent = &event.event;
#else
// Make a copy since we may modify values.
NPEvent evcopy = event.event;
#endif
#ifdef OS_WIN
// FIXME/bug 567645: temporarily drop the "dummy event" on the floor
if (WM_NULL == evcopy.event)
return true;
// Painting for win32. SharedSurfacePaint handles everything.
if (mWindow.type == NPWindowTypeDrawable) {
if (evcopy.event == WM_PAINT) {
*handled = SharedSurfacePaint(evcopy);
return true;
}
else if (DoublePassRenderingEvent() == evcopy.event) {
// We'll render to mSharedSurfaceDib first, then render to a cached bitmap
// we store locally. The two passes are for alpha extraction, so the second
// pass must be to a flat white surface in order for things to work.
mAlphaExtract.doublePass = RENDER_BACK_ONE;
*handled = true;
return true;
}
}
*handled = WinlessHandleEvent(evcopy);
return true;
#endif
if (!mPluginIface->event)
*handled = false;
else
*handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
#ifdef XP_MACOSX
// Release any reference counted objects created in the child process.
if (evcopy.type == NPCocoaEventKeyDown ||
evcopy.type == NPCocoaEventKeyUp) {
::CFRelease((CFStringRef)evcopy.data.key.characters);
::CFRelease((CFStringRef)evcopy.data.key.charactersIgnoringModifiers);
}
else if (evcopy.type == NPCocoaEventTextInput) {
::CFRelease((CFStringRef)evcopy.data.text.text);
}
#endif
#ifdef MOZ_X11
if (GraphicsExpose == event.event.type) {
// Make sure the X server completes the drawing before the parent
// draws on top and destroys the Drawable.
//
// XSync() waits for the X server to complete. Really this child
// process does not need to wait; the parent is the process that needs
// to wait. A possibly-slightly-better alternative would be to send
// an X event to the parent that the parent would wait for.
XSync(mWsInfo.display, False);
}
#endif
return true;
}
#ifdef XP_MACOSX
bool
PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
Shmem& mem,
int16_t* handled,
Shmem* rtnmem)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
PaintTracker pt;
NPCocoaEvent evcopy = event.event;
if (evcopy.type == NPCocoaEventDrawRect) {
if (!mShColorSpace) {
mShColorSpace = CreateSystemColorSpace();
if (!mShColorSpace) {
PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
*handled = false;
*rtnmem = mem;
return true;
}
}
if (!mShContext) {
void* cgContextByte = mem.get<char>();
mShContext = ::CGBitmapContextCreate(cgContextByte,
mWindow.width, mWindow.height, 8,
mWindow.width * 4, mShColorSpace,
kCGImageAlphaPremultipliedFirst |
kCGBitmapByteOrder32Host);
if (!mShContext) {
PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
*handled = false;
*rtnmem = mem;
return true;
}
}
CGRect clearRect = ::CGRectMake(0, 0, mWindow.width, mWindow.height);
::CGContextClearRect(mShContext, clearRect);
evcopy.data.draw.context = mShContext;
} else {
PLUGIN_LOG_DEBUG(("Invalid event type for AnswerNNP_HandleEvent_Shmem."));
*handled = false;
*rtnmem = mem;
return true;
}
if (!mPluginIface->event) {
*handled = false;
} else {
::CGContextSaveGState(evcopy.data.draw.context);
*handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
::CGContextRestoreGState(evcopy.data.draw.context);
}
*rtnmem = mem;
return true;
}
#else
bool
PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
Shmem& mem,
int16_t* handled,
Shmem* rtnmem)
{
NS_RUNTIMEABORT("not reached.");
*rtnmem = mem;
return true;
}
#endif
#ifdef XP_MACOSX
bool
PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
const uint32_t &surfaceid,
int16_t* handled)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
PaintTracker pt;
NPCocoaEvent evcopy = event.event;
nsIOSurface* surf = nsIOSurface::LookupSurface(surfaceid);
if (!surf) {
NS_ERROR("Invalid IOSurface.");
*handled = false;
return false;
}
if (evcopy.type == NPCocoaEventDrawRect) {
mCARenderer.AttachIOSurface(surf);
if (!mCARenderer.isInit()) {
void *caLayer = nsnull;
NPError result = mPluginIface->getvalue(GetNPP(),
NPPVpluginCoreAnimationLayer,
&caLayer);
if (result != NPERR_NO_ERROR || !caLayer) {
PLUGIN_LOG_DEBUG(("Plugin requested CoreAnimation but did not "
"provide CALayer."));
*handled = false;
return false;
}
mCARenderer.SetupRenderer(caLayer, mWindow.width, mWindow.height);
// Flash needs to have the window set again after this step
if (mPluginIface->setwindow)
(void) mPluginIface->setwindow(&mData, &mWindow);
}
} else {
PLUGIN_LOG_DEBUG(("Invalid event type for "
"AnswerNNP_HandleEvent_IOSurface."));
*handled = false;
return false;
}
mCARenderer.Render(mWindow.width, mWindow.height, nsnull);
return true;
}
#else
bool
PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
const uint32_t &surfaceid,
int16_t* handled)
{
NS_RUNTIMEABORT("NPP_HandleEvent_IOSurface is a OSX-only message");
return false;
}
#endif
bool
PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event)
{
#ifdef OS_WIN
int16_t dontcare;
return AnswerNPP_HandleEvent(event, &dontcare);
#else
NS_RUNTIMEABORT("WindowPosChanged is a windows-only message");
return false;
#endif
}
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
static bool
XVisualIDToInfo(Display* aDisplay, VisualID aVisualID,
Visual** aVisual, unsigned int* aDepth)
{
if (aVisualID == None) {
*aVisual = NULL;
*aDepth = 0;
return true;
}
const Screen* screen = DefaultScreenOfDisplay(aDisplay);
for (int d = 0; d < screen->ndepths; d++) {
Depth *d_info = &screen->depths[d];
for (int v = 0; v < d_info->nvisuals; v++) {
Visual* visual = &d_info->visuals[v];
if (visual->visualid == aVisualID) {
*aVisual = visual;
*aDepth = d_info->depth;
return true;
}
}
}
NS_ERROR("VisualID not on Screen.");
return false;
}
#endif
bool
PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
{
PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
FULLFUNCTION,
aWindow.window,
aWindow.x, aWindow.y,
aWindow.width, aWindow.height));
AssertPluginThread();
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
// The minimum info is sent over IPC to allow this
// code to determine the rest.
mWindow.window = reinterpret_cast<void*>(aWindow.window);
mWindow.x = aWindow.x;
mWindow.y = aWindow.y;
mWindow.width = aWindow.width;
mWindow.height = aWindow.height;
mWindow.clipRect = aWindow.clipRect;
mWindow.type = aWindow.type;
mWsInfo.colormap = aWindow.colormap;
if (!XVisualIDToInfo(mWsInfo.display, aWindow.visualID,
&mWsInfo.visual, &mWsInfo.depth))
return false;
mLayersRendering = false;
#ifdef MOZ_WIDGET_GTK2
if (gtk_check_version(2,18,7) != NULL) { // older
if (aWindow.type == NPWindowTypeWindow) {
GdkWindow* socket_window = gdk_window_lookup(aWindow.window);
if (socket_window) {
// A GdkWindow for the socket already exists. Need to
// workaround https://bugzilla.gnome.org/show_bug.cgi?id=607061
// See wrap_gtk_plug_embedded in PluginModuleChild.cpp.
g_object_set_data(G_OBJECT(socket_window),
"moz-existed-before-set-window",
GUINT_TO_POINTER(1));
}
}
if (aWindow.visualID != None
&& gtk_check_version(2, 12, 10) != NULL) { // older
// Workaround for a bug in Gtk+ (prior to 2.12.10) where deleting
// a foreign GdkColormap will also free the XColormap.
// http://git.gnome.org/browse/gtk+/log/gdk/x11/gdkcolor-x11.c?id=GTK_2_12_10
GdkVisual *gdkvisual = gdkx_visual_get(aWindow.visualID);
GdkColormap *gdkcolor =
gdk_x11_colormap_foreign_new(gdkvisual, aWindow.colormap);
if (g_object_get_data(G_OBJECT(gdkcolor), "moz-have-extra-ref")) {
// We already have a ref to keep the object alive.
g_object_unref(gdkcolor);
} else {
// leak and mark as already leaked
g_object_set_data(G_OBJECT(gdkcolor),
"moz-have-extra-ref", GUINT_TO_POINTER(1));
}
}
}
#endif
if (mPluginIface->setwindow)
(void) mPluginIface->setwindow(&mData, &mWindow);
#elif defined(OS_WIN)
switch (aWindow.type) {
case NPWindowTypeWindow:
{
if (!CreatePluginWindow())
return false;
ReparentPluginWindow((HWND)aWindow.window);
SizePluginWindow(aWindow.width, aWindow.height);
mWindow.window = (void*)mPluginWindowHWND;
mWindow.x = aWindow.x;
mWindow.y = aWindow.y;
mWindow.width = aWindow.width;
mWindow.height = aWindow.height;
mWindow.type = aWindow.type;
if (mPluginIface->setwindow) {
SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
(void) mPluginIface->setwindow(&mData, &mWindow);
WNDPROC wndProc = reinterpret_cast<WNDPROC>(
GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC));
if (wndProc != PluginWindowProc) {
mPluginWndProc = reinterpret_cast<WNDPROC>(
SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>(PluginWindowProc)));
}
RemoveProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty);
HookSetWindowLongPtr();
}
}
break;
case NPWindowTypeDrawable:
mWindow.type = aWindow.type;
if (mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK)
CreateWinlessPopupSurrogate();
if (mQuirks & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)
SetupFlashMsgThrottle();
return SharedSurfaceSetWindow(aWindow);
break;
default:
NS_NOTREACHED("Bad plugin window type.");
return false;
break;
}
#elif defined(XP_MACOSX)
mWindow.x = aWindow.x;
mWindow.y = aWindow.y;
mWindow.width = aWindow.width;
mWindow.height = aWindow.height;
mWindow.clipRect = aWindow.clipRect;
mWindow.type = aWindow.type;
if (mShContext) {
// Release the shared context so that it is reallocated
// with the new size.
::CGContextRelease(mShContext);
mShContext = nsnull;
}
if (mPluginIface->setwindow)
(void) mPluginIface->setwindow(&mData, &mWindow);
#elif defined(ANDROID)
# warning Need Android impl
#else
# error Implement me for your OS
#endif
return true;
}
bool
PluginInstanceChild::Initialize()
{
return true;
}
#if defined(OS_WIN)
static const TCHAR kWindowClassName[] = TEXT("GeckoPluginWindow");
static const TCHAR kPluginInstanceChildProperty[] = TEXT("PluginInstanceChildProperty");
static const TCHAR kFlashThrottleProperty[] = TEXT("MozillaFlashThrottleProperty");
// static
bool
PluginInstanceChild::RegisterWindowClass()
{
static bool alreadyRegistered = false;
if (alreadyRegistered)
return true;
alreadyRegistered = true;
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_DBLCLKS;
wcex.lpfnWndProc = DummyWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hIcon = 0;
wcex.hCursor = 0;
wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
wcex.lpszMenuName = 0;
wcex.lpszClassName = kWindowClassName;
wcex.hIconSm = 0;
return RegisterClassEx(&wcex) ? true : false;
}
bool
PluginInstanceChild::CreatePluginWindow()
{
// already initialized
if (mPluginWindowHWND)
return true;
if (!RegisterWindowClass())
return false;
mPluginWindowHWND =
CreateWindowEx(WS_EX_LEFT | WS_EX_LTRREADING |
WS_EX_NOPARENTNOTIFY | // XXXbent Get rid of this!
WS_EX_RIGHTSCROLLBAR,
kWindowClassName, 0,
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0,
0, 0, NULL, 0, GetModuleHandle(NULL), 0);
if (!mPluginWindowHWND)
return false;
if (!SetProp(mPluginWindowHWND, kPluginInstanceChildProperty, this))
return false;
// Apparently some plugins require an ASCII WndProc.
SetWindowLongPtrA(mPluginWindowHWND, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>(DefWindowProcA));
return true;
}
void
PluginInstanceChild::DestroyPluginWindow()
{
if (mPluginWindowHWND) {
// Unsubclass the window.
WNDPROC wndProc = reinterpret_cast<WNDPROC>(
GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC));
// Removed prior to SetWindowLongPtr, see HookSetWindowLongPtr.
RemoveProp(mPluginWindowHWND, kPluginInstanceChildProperty);
if (wndProc == PluginWindowProc) {
NS_ASSERTION(mPluginWndProc, "Should have old proc here!");
SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>(mPluginWndProc));
mPluginWndProc = 0;
}
DestroyWindow(mPluginWindowHWND);
mPluginWindowHWND = 0;
}
}
void
PluginInstanceChild::ReparentPluginWindow(HWND hWndParent)
{
if (hWndParent != mPluginParentHWND && IsWindow(hWndParent)) {
// Fix the child window's style to be a child window.
LONG_PTR style = GetWindowLongPtr(mPluginWindowHWND, GWL_STYLE);
style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
style &= ~WS_POPUP;
SetWindowLongPtr(mPluginWindowHWND, GWL_STYLE, style);
// Do the reparenting.
SetParent(mPluginWindowHWND, hWndParent);
// Make sure we're visible.
ShowWindow(mPluginWindowHWND, SW_SHOWNA);
}
mPluginParentHWND = hWndParent;
}
void
PluginInstanceChild::SizePluginWindow(int width,
int height)
{
if (mPluginWindowHWND) {
mPluginSize.x = width;
mPluginSize.y = height;
SetWindowPos(mPluginWindowHWND, NULL, 0, 0, width, height,
SWP_NOZORDER | SWP_NOREPOSITION);
}
}
// See chromium's webplugin_delegate_impl.cc for explanation of this function.
// static
LRESULT CALLBACK
PluginInstanceChild::DummyWindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
return CallWindowProc(DefWindowProc, hWnd, message, wParam, lParam);
}
// static
LRESULT CALLBACK
PluginInstanceChild::PluginWindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(),
"Failed to prevent a nonqueued message from running!");
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
GetProp(hWnd, kPluginInstanceChildProperty));
if (!self) {
NS_NOTREACHED("Badness!");
return 0;
}
NS_ASSERTION(self->mPluginWindowHWND == hWnd, "Wrong window!");
// Adobe's shockwave positions the plugin window relative to the browser
// frame when it initializes. With oopp disabled, this wouldn't have an
// effect. With oopp, GeckoPluginWindow is a child of the parent plugin
// window, so the move offsets the child within the parent. Generally
// we don't want plugins moving or sizing our window, so we prevent these
// changes here.
if (message == WM_WINDOWPOSCHANGING) {
WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lParam);
if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE))) {
pos->x = pos->y = 0;
pos->cx = self->mPluginSize.x;
pos->cy = self->mPluginSize.y;
LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
lParam);
pos->x = pos->y = 0;
pos->cx = self->mPluginSize.x;
pos->cy = self->mPluginSize.y;
return res;
}
}
// The plugin received keyboard focus, let the parent know so the dom is up to date.
if (message == WM_MOUSEACTIVATE)
self->CallPluginFocusChange(true);
// Prevent lockups due to plugins making rpc calls when the parent
// is making a synchronous SendMessage call to the child window. Add
// more messages as needed.
if ((InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
switch(message) {
case WM_KILLFOCUS:
case WM_MOUSEHWHEEL:
case WM_MOUSEWHEEL:
case WM_HSCROLL:
case WM_VSCROLL:
ReplyMessage(0);
break;
}
}
if (message == WM_KILLFOCUS)
self->CallPluginFocusChange(false);
if (message == WM_USER+1 &&
(self->mQuirks & PluginInstanceChild::QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)) {
self->FlashThrottleMessage(hWnd, message, wParam, lParam, true);
return 0;
}
// Make sure capture is released by the child on mouse events. Fixes a
// problem with flash full screen mode mouse input. Appears to be
// caused by a bug in flash, since we are not setting the capture
// on the window. (In non-oopp land, we would set and release via
// widget for other reasons.)
switch(message) {
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
ReleaseCapture();
break;
}
LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
lParam);
if (message == WM_CLOSE)
self->DestroyPluginWindow();
if (message == WM_NCDESTROY)
RemoveProp(hWnd, kPluginInstanceChildProperty);
return res;
}
/* set window long ptr hook for flash */
/*
* Flash will reset the subclass of our widget at various times.
* (Notably when entering and exiting full screen mode.) This
* occurs independent of the main plugin window event procedure.
* We trap these subclass calls to prevent our subclass hook from
* getting dropped.
* Note, ascii versions can be nixed once flash versions < 10.1
* are considered obsolete.
*/
#ifdef _WIN64
typedef LONG_PTR
(WINAPI *User32SetWindowLongPtrA)(HWND hWnd,
int nIndex,
LONG_PTR dwNewLong);
typedef LONG_PTR
(WINAPI *User32SetWindowLongPtrW)(HWND hWnd,
int nIndex,
LONG_PTR dwNewLong);
static User32SetWindowLongPtrA sUser32SetWindowLongAHookStub = NULL;
static User32SetWindowLongPtrW sUser32SetWindowLongWHookStub = NULL;
#else
typedef LONG
(WINAPI *User32SetWindowLongA)(HWND hWnd,
int nIndex,
LONG dwNewLong);
typedef LONG
(WINAPI *User32SetWindowLongW)(HWND hWnd,
int nIndex,
LONG dwNewLong);
static User32SetWindowLongA sUser32SetWindowLongAHookStub = NULL;
static User32SetWindowLongW sUser32SetWindowLongWHookStub = NULL;
#endif
extern LRESULT CALLBACK
NeuteredWindowProc(HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
// static
PRBool
PluginInstanceChild::SetWindowLongHookCheck(HWND hWnd,
int nIndex,
LONG_PTR newLong)
{
// Let this go through if it's not a subclass
if (nIndex != GWLP_WNDPROC ||
// if it's not a subclassed plugin window
!GetProp(hWnd, kPluginInstanceChildProperty) ||
// if we're not disabled
GetProp(hWnd, kPluginIgnoreSubclassProperty) ||
// if the subclass is set to a known procedure
newLong == reinterpret_cast<LONG_PTR>(PluginWindowProc) ||
newLong == reinterpret_cast<LONG_PTR>(NeuteredWindowProc) ||
newLong == reinterpret_cast<LONG_PTR>(DefWindowProcA) ||
newLong == reinterpret_cast<LONG_PTR>(DefWindowProcW) ||
// if the subclass is a WindowsMessageLoop subclass restore
GetProp(hWnd, kOldWndProcProp))
return PR_TRUE;
// prevent the subclass
return PR_FALSE;
}
#ifdef _WIN64
LONG_PTR WINAPI
PluginInstanceChild::SetWindowLongPtrAHook(HWND hWnd,
int nIndex,
LONG_PTR newLong)
#else
LONG WINAPI
PluginInstanceChild::SetWindowLongAHook(HWND hWnd,
int nIndex,
LONG newLong)
#endif
{
if (SetWindowLongHookCheck(hWnd, nIndex, newLong))
return sUser32SetWindowLongAHookStub(hWnd, nIndex, newLong);
// Set flash's new subclass to get the result.
LONG_PTR proc = sUser32SetWindowLongAHookStub(hWnd, nIndex, newLong);
// We already checked this in SetWindowLongHookCheck
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
GetProp(hWnd, kPluginInstanceChildProperty));
// Hook our subclass back up, just like we do on setwindow.
self->mPluginWndProc =
reinterpret_cast<WNDPROC>(sUser32SetWindowLongAHookStub(hWnd, nIndex,
reinterpret_cast<LONG_PTR>(PluginWindowProc)));
return proc;
}
#ifdef _WIN64
LONG_PTR WINAPI
PluginInstanceChild::SetWindowLongPtrWHook(HWND hWnd,
int nIndex,
LONG_PTR newLong)
#else
LONG WINAPI
PluginInstanceChild::SetWindowLongWHook(HWND hWnd,
int nIndex,
LONG newLong)
#endif
{
if (SetWindowLongHookCheck(hWnd, nIndex, newLong))
return sUser32SetWindowLongWHookStub(hWnd, nIndex, newLong);
// Set flash's new subclass to get the result.
LONG_PTR proc = sUser32SetWindowLongWHookStub(hWnd, nIndex, newLong);
// We already checked this in SetWindowLongHookCheck
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
GetProp(hWnd, kPluginInstanceChildProperty));
// Hook our subclass back up, just like we do on setwindow.
self->mPluginWndProc =
reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(hWnd, nIndex,
reinterpret_cast<LONG_PTR>(PluginWindowProc)));
return proc;
}
void
PluginInstanceChild::HookSetWindowLongPtr()
{
#ifdef _WIN64
// XXX WindowsDllInterceptor doesn't support hooks
// in 64-bit builds, disabling this code for now.
return;
#endif
if (!(GetQuirks() & QUIRK_FLASH_HOOK_SETLONGPTR))
return;
sUser32Intercept.Init("user32.dll");
#ifdef _WIN64
sUser32Intercept.AddHook("SetWindowLongPtrA", SetWindowLongPtrAHook,
(void**) &sUser32SetWindowLongAHookStub);
sUser32Intercept.AddHook("SetWindowLongPtrW", SetWindowLongPtrWHook,
(void**) &sUser32SetWindowLongWHookStub);
#else
sUser32Intercept.AddHook("SetWindowLongA", SetWindowLongAHook,
(void**) &sUser32SetWindowLongAHookStub);
sUser32Intercept.AddHook("SetWindowLongW", SetWindowLongWHook,
(void**) &sUser32SetWindowLongWHookStub);
#endif
}
/* windowless track popup menu helpers */
BOOL
WINAPI
PluginInstanceChild::TrackPopupHookProc(HMENU hMenu,
UINT uFlags,
int x,
int y,
int nReserved,
HWND hWnd,
CONST RECT *prcRect)
{
if (!sUser32TrackPopupMenuStub) {
NS_ERROR("TrackPopupMenu stub isn't set! Badness!");
return 0;
}
// Only change the parent when we know this is a context on the plugin
// surface within the browser. Prevents resetting the parent on child ui
// displayed by plugins that have working parent-child relationships.
PRUnichar szClass[21];
bool haveClass = GetClassNameW(hWnd, szClass, NS_ARRAY_LENGTH(szClass));
if (!haveClass ||
(wcscmp(szClass, L"MozillaWindowClass") &&
wcscmp(szClass, L"SWFlash_Placeholder"))) {
// Unrecognized parent
return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
hWnd, prcRect);
}
// Called on an unexpected event, warn.
if (!sWinlessPopupSurrogateHWND) {
NS_WARNING(
"Untraced TrackPopupHookProc call! Menu might not work right!");
return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
hWnd, prcRect);
}
HWND surrogateHwnd = sWinlessPopupSurrogateHWND;
sWinlessPopupSurrogateHWND = NULL;
// Popups that don't use TPM_RETURNCMD expect a final command message
// when an item is selected and the context closes. Since we replace
// the parent, we need to forward this back to the real parent so it
// can act on the menu item selected.
bool isRetCmdCall = (uFlags & TPM_RETURNCMD);
// A little trick scrounged from chromium's code - set the focus
// to our surrogate parent so keyboard nav events go to the menu.
HWND focusHwnd = SetFocus(surrogateHwnd);
DWORD res = sUser32TrackPopupMenuStub(hMenu, uFlags|TPM_RETURNCMD, x, y,
nReserved, surrogateHwnd, prcRect);
if (IsWindow(focusHwnd)) {
SetFocus(focusHwnd);
}
if (!isRetCmdCall && res) {
SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(res, 0), 0);
}
return res;
}
void
PluginInstanceChild::InitPopupMenuHook()
{
if (!(mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) ||
sUser32TrackPopupMenuStub)
return;
// Note, once WindowsDllInterceptor is initialized for a module,
// it remains initialized for that particular module for it's
// lifetime. Additional instances are needed if other modules need
// to be hooked.
sUser32Intercept.Init("user32.dll");
sUser32Intercept.AddHook("TrackPopupMenu", TrackPopupHookProc,
(void**) &sUser32TrackPopupMenuStub);
}
void
PluginInstanceChild::CreateWinlessPopupSurrogate()
{
// already initialized
if (mWinlessPopupSurrogateHWND)
return;
HWND hwnd = NULL;
NPError result;
if (!CallNPN_GetValue_NPNVnetscapeWindow(&hwnd, &result)) {
NS_ERROR("CallNPN_GetValue_NPNVnetscapeWindow failed.");
return;
}
mWinlessPopupSurrogateHWND =
CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", NULL, WS_CHILD, 0, 0,
0, 0, hwnd, 0, GetModuleHandle(NULL), 0);
if (!mWinlessPopupSurrogateHWND) {
NS_ERROR("CreateWindowEx failed for winless placeholder!");
return;
}
return;
}
void
PluginInstanceChild::DestroyWinlessPopupSurrogate()
{
if (mWinlessPopupSurrogateHWND)
DestroyWindow(mWinlessPopupSurrogateHWND);
mWinlessPopupSurrogateHWND = NULL;
}
/* windowless handle event helpers */
static bool
NeedsNestedEventCoverage(UINT msg)
{
// Events we assume some sort of modal ui *might* be generated.
switch (msg) {
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_CONTEXTMENU:
return true;
}
return false;
}
static bool
IsMouseInputEvent(UINT msg)
{
switch (msg) {
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
return true;
}
return false;
}
int16_t
PluginInstanceChild::WinlessHandleEvent(NPEvent& event)
{
if (!mPluginIface->event)
return false;
if (!NeedsNestedEventCoverage(event.event)) {
return mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
}
// Events that might generate nested event dispatch loops need
// special handling during delivery.
int16_t handled;
// TrackPopupMenu will fail if the parent window is not associated with
// our ui thread. So we hook TrackPopupMenu so we can hand in a surrogate
// parent created in the child process.
if ((mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) && // XXX turn on by default?
(event.event == WM_RBUTTONDOWN || // flash
event.event == WM_RBUTTONUP)) { // silverlight
sWinlessPopupSurrogateHWND = mWinlessPopupSurrogateHWND;
}
handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
sWinlessPopupSurrogateHWND = NULL;
return handled;
}
/* windowless drawing helpers */
bool
PluginInstanceChild::SharedSurfaceSetWindow(const NPRemoteWindow& aWindow)
{
// If the surfaceHandle is empty, parent is telling us we can reuse our cached
// memory surface and hdc. Otherwise, we need to reset, usually due to a
// expanding plugin port size.
if (!aWindow.surfaceHandle) {
if (!mSharedSurfaceDib.IsValid()) {
return false;
}
}
else {
// Attach to the new shared surface parent handed us.
if (NS_FAILED(mSharedSurfaceDib.Attach((SharedDIB::Handle)aWindow.surfaceHandle,
aWindow.width, aWindow.height, false)))
return false;
// Free any alpha extraction resources if needed. This will be reset
// the next time it's used.
AlphaExtractCacheRelease();
}
// NPRemoteWindow's origin is the origin of our shared dib.
mWindow.x = aWindow.x;
mWindow.y = aWindow.y;
mWindow.width = aWindow.width;
mWindow.height = aWindow.height;
mWindow.type = aWindow.type;
mWindow.window = reinterpret_cast<void*>(mSharedSurfaceDib.GetHDC());
::SetViewportOrgEx(mSharedSurfaceDib.GetHDC(), -aWindow.x, -aWindow.y, NULL);
if (mPluginIface->setwindow)
mPluginIface->setwindow(&mData, &mWindow);
return true;
}
void
PluginInstanceChild::SharedSurfaceRelease()
{
mSharedSurfaceDib.Close();
AlphaExtractCacheRelease();
}
/* double pass cache buffer - (rarely) used in cases where alpha extraction
* occurs for windowless plugins. */
bool
PluginInstanceChild::AlphaExtractCacheSetup()
{
AlphaExtractCacheRelease();
mAlphaExtract.hdc = ::CreateCompatibleDC(NULL);
if (!mAlphaExtract.hdc)
return false;
BITMAPINFOHEADER bmih;
memset((void*)&bmih, 0, sizeof(BITMAPINFOHEADER));
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = mWindow.width;
bmih.biHeight = mWindow.height;
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
void* ppvBits = nsnull;
mAlphaExtract.bmp = ::CreateDIBSection(mAlphaExtract.hdc,
(BITMAPINFO*)&bmih,
DIB_RGB_COLORS,
(void**)&ppvBits,
NULL,
(unsigned long)sizeof(BITMAPINFOHEADER));
if (!mAlphaExtract.bmp)
return false;
DeleteObject(::SelectObject(mAlphaExtract.hdc, mAlphaExtract.bmp));
return true;
}
void
PluginInstanceChild::AlphaExtractCacheRelease()
{
if (mAlphaExtract.bmp)
::DeleteObject(mAlphaExtract.bmp);
if (mAlphaExtract.hdc)
::DeleteObject(mAlphaExtract.hdc);
mAlphaExtract.bmp = NULL;
mAlphaExtract.hdc = NULL;
}
void
PluginInstanceChild::UpdatePaintClipRect(RECT* aRect)
{
if (aRect) {
// Update the clip rect on our internal hdc
HRGN clip = ::CreateRectRgnIndirect(aRect);
::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip);
::DeleteObject(clip);
}
}
int16_t
PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy)
{
if (!mPluginIface->event)
return false;
RECT* pRect = reinterpret_cast<RECT*>(evcopy.lParam);
switch(mAlphaExtract.doublePass) {
case RENDER_NATIVE:
// pass the internal hdc to the plugin
UpdatePaintClipRect(pRect);
evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
return mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
break;
case RENDER_BACK_ONE:
// Handle a double pass render used in alpha extraction for transparent
// plugins. (See nsObjectFrame and gfxWindowsNativeDrawing for details.)
// We render twice, once to the shared dib, and once to a cache which
// we copy back on a second paint. These paints can't be spread across
// multiple rpc messages as delays cause animation frame changes.
if (!mAlphaExtract.bmp && !AlphaExtractCacheSetup()) {
mAlphaExtract.doublePass = RENDER_NATIVE;
return false;
}
// See gfxWindowsNativeDrawing, color order doesn't have to match.
UpdatePaintClipRect(pRect);
::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
mAlphaExtract.doublePass = RENDER_NATIVE;
return false;
}
// Copy to cache. We render to shared dib so we don't have to call
// setwindow between calls (flash issue).
::BitBlt(mAlphaExtract.hdc,
pRect->left,
pRect->top,
pRect->right - pRect->left,
pRect->bottom - pRect->top,
mSharedSurfaceDib.GetHDC(),
pRect->left,
pRect->top,
SRCCOPY);
::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
mAlphaExtract.doublePass = RENDER_NATIVE;
return false;
}
mAlphaExtract.doublePass = RENDER_BACK_TWO;
return true;
break;
case RENDER_BACK_TWO:
// copy our cached surface back
UpdatePaintClipRect(pRect);
::BitBlt(mSharedSurfaceDib.GetHDC(),
pRect->left,
pRect->top,
pRect->right - pRect->left,
pRect->bottom - pRect->top,
mAlphaExtract.hdc,
pRect->left,
pRect->top,
SRCCOPY);
mAlphaExtract.doublePass = RENDER_NATIVE;
return true;
break;
}
return false;
}
/* flash msg throttling helpers */
// Flash has the unfortunate habit of flooding dispatch loops with custom
// windowing events they use for timing. We throttle these by dropping the
// delivery priority below any other event, including pending ipc io
// notifications. We do this for both windowed and windowless controls.
// Note flash's windowless msg window can last longer than our instance,
// so we try to unhook when the window is destroyed and in NPP_Destroy.
void
PluginInstanceChild::UnhookWinlessFlashThrottle()
{
// We may have already unhooked
if (!mWinlessThrottleOldWndProc)
return;
WNDPROC tmpProc = mWinlessThrottleOldWndProc;
mWinlessThrottleOldWndProc = nsnull;
NS_ASSERTION(mWinlessHiddenMsgHWND,
"Missing mWinlessHiddenMsgHWND w/subclass set??");
// reset the subclass
SetWindowLongPtr(mWinlessHiddenMsgHWND, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>(tmpProc));
// Remove our instance prop
RemoveProp(mWinlessHiddenMsgHWND, kFlashThrottleProperty);
mWinlessHiddenMsgHWND = nsnull;
}
// static
LRESULT CALLBACK
PluginInstanceChild::WinlessHiddenFlashWndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
GetProp(hWnd, kFlashThrottleProperty));
if (!self) {
NS_NOTREACHED("Badness!");
return 0;
}
NS_ASSERTION(self->mWinlessThrottleOldWndProc,
"Missing subclass procedure!!");
// Throttle
if (message == WM_USER+1) {
self->FlashThrottleMessage(hWnd, message, wParam, lParam, false);
return 0;
}
// Unhook
if (message == WM_CLOSE || message == WM_NCDESTROY) {
WNDPROC tmpProc = self->mWinlessThrottleOldWndProc;
self->UnhookWinlessFlashThrottle();
LRESULT res = CallWindowProc(tmpProc, hWnd, message, wParam, lParam);
return res;
}
return CallWindowProc(self->mWinlessThrottleOldWndProc,
hWnd, message, wParam, lParam);
}
// Enumerate all thread windows looking for flash's hidden message window.
// Once we find it, sub class it so we can throttle user msgs.
// static
BOOL CALLBACK
PluginInstanceChild::EnumThreadWindowsCallback(HWND hWnd,
LPARAM aParam)
{
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(aParam);
if (!self) {
NS_NOTREACHED("Enum befuddled!");
return FALSE;
}
PRUnichar className[64];
if (!GetClassNameW(hWnd, className, sizeof(className)/sizeof(PRUnichar)))
return TRUE;
if (!wcscmp(className, L"SWFlash_PlaceholderX")) {
WNDPROC oldWndProc =
reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
// Only set this if we haven't already.
if (oldWndProc != WinlessHiddenFlashWndProc) {
if (self->mWinlessThrottleOldWndProc) {
NS_WARNING("mWinlessThrottleWndProc already set???");
return FALSE;
}
// Subsclass and store self as a property
self->mWinlessHiddenMsgHWND = hWnd;
self->mWinlessThrottleOldWndProc =
reinterpret_cast<WNDPROC>(SetWindowLongPtr(hWnd, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>(WinlessHiddenFlashWndProc)));
SetProp(hWnd, kFlashThrottleProperty, self);
NS_ASSERTION(self->mWinlessThrottleOldWndProc,
"SetWindowLongPtr failed?!");
}
// Return no matter what once we find the right window.
return FALSE;
}
return TRUE;
}
void
PluginInstanceChild::SetupFlashMsgThrottle()
{
if (mWindow.type == NPWindowTypeDrawable) {
// Search for the flash hidden message window and subclass it. Only
// search for flash windows belonging to our ui thread!
if (mWinlessThrottleOldWndProc)
return;
EnumThreadWindows(GetCurrentThreadId(), EnumThreadWindowsCallback,
reinterpret_cast<LPARAM>(this));
}
else {
// Already setup through quirks and the subclass.
return;
}
}
WNDPROC
PluginInstanceChild::FlashThrottleAsyncMsg::GetProc()
{
if (mInstance) {
return mWindowed ? mInstance->mPluginWndProc :
mInstance->mWinlessThrottleOldWndProc;
}
return nsnull;
}
void
PluginInstanceChild::FlashThrottleAsyncMsg::Run()
{
RemoveFromAsyncList();
// GetProc() checks mInstance, and pulls the procedure from
// PluginInstanceChild. We don't transport sub-class procedure
// ptrs around in FlashThrottleAsyncMsg msgs.
if (!GetProc())
return;
// deliver the event to flash
CallWindowProc(GetProc(), GetWnd(), GetMsg(), GetWParam(), GetLParam());
}
void
PluginInstanceChild::FlashThrottleMessage(HWND aWnd,
UINT aMsg,
WPARAM aWParam,
LPARAM aLParam,
bool isWindowed)
{
// We reuse ChildAsyncCall so we get the cancelation work
// that's done in Destroy.
FlashThrottleAsyncMsg* task = new FlashThrottleAsyncMsg(this,
aWnd, aMsg, aWParam, aLParam, isWindowed);
if (!task)
return;
{
MutexAutoLock lock(mAsyncCallMutex);
mPendingAsyncCalls.AppendElement(task);
}
MessageLoop::current()->PostDelayedTask(FROM_HERE,
task, kFlashWMUSERMessageThrottleDelayMs);
}
#endif // OS_WIN
bool
PluginInstanceChild::AnswerSetPluginFocus()
{
PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION));
#if defined(OS_WIN)
// Parent is letting us know something set focus to the plugin.
if (::GetFocus() == mPluginWindowHWND)
return true;
::SetFocus(mPluginWindowHWND);
return true;
#else
NS_NOTREACHED("PluginInstanceChild::AnswerSetPluginFocus not implemented!");
return false;
#endif
}
bool
PluginInstanceChild::AnswerUpdateWindow()
{
PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION));
#if defined(OS_WIN)
if (mPluginWindowHWND) {
RECT rect;
if (GetUpdateRect(GetParent(mPluginWindowHWND), &rect, FALSE)) {
::InvalidateRect(mPluginWindowHWND, &rect, FALSE);
}
UpdateWindow(mPluginWindowHWND);
}
return true;
#else
NS_NOTREACHED("PluginInstanceChild::AnswerUpdateWindow not implemented!");
return false;
#endif
}
PPluginScriptableObjectChild*
PluginInstanceChild::AllocPPluginScriptableObject()
{
AssertPluginThread();
return new PluginScriptableObjectChild(Proxy);
}
bool
PluginInstanceChild::DeallocPPluginScriptableObject(
PPluginScriptableObjectChild* aObject)
{
AssertPluginThread();
delete aObject;
return true;
}
bool
PluginInstanceChild::RecvPPluginScriptableObjectConstructor(
PPluginScriptableObjectChild* aActor)
{
AssertPluginThread();
// This is only called in response to the parent process requesting the
// creation of an actor. This actor will represent an NPObject that is
// created by the browser and returned to the plugin.
PluginScriptableObjectChild* actor =
static_cast<PluginScriptableObjectChild*>(aActor);
NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!");
actor->InitializeProxy();
NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
return true;
}
bool
PluginInstanceChild::AnswerPBrowserStreamConstructor(
PBrowserStreamChild* aActor,
const nsCString& url,
const uint32_t& length,
const uint32_t& lastmodified,
PStreamNotifyChild* notifyData,
const nsCString& headers,
const nsCString& mimeType,
const bool& seekable,
NPError* rv,
uint16_t* stype)
{
AssertPluginThread();
*rv = static_cast<BrowserStreamChild*>(aActor)
->StreamConstructed(mimeType, seekable, stype);
return true;
}
PBrowserStreamChild*
PluginInstanceChild::AllocPBrowserStream(const nsCString& url,
const uint32_t& length,
const uint32_t& lastmodified,
PStreamNotifyChild* notifyData,
const nsCString& headers,
const nsCString& mimeType,
const bool& seekable,
NPError* rv,
uint16_t *stype)
{
AssertPluginThread();
return new BrowserStreamChild(this, url, length, lastmodified,
static_cast<StreamNotifyChild*>(notifyData),
headers, mimeType, seekable, rv, stype);
}
bool
PluginInstanceChild::DeallocPBrowserStream(PBrowserStreamChild* stream)
{
AssertPluginThread();
delete stream;
return true;
}
PPluginStreamChild*
PluginInstanceChild::AllocPPluginStream(const nsCString& mimeType,
const nsCString& target,
NPError* result)
{
NS_RUNTIMEABORT("not callable");
return NULL;
}
bool
PluginInstanceChild::DeallocPPluginStream(PPluginStreamChild* stream)
{
AssertPluginThread();
delete stream;
return true;
}
PStreamNotifyChild*
PluginInstanceChild::AllocPStreamNotify(const nsCString& url,
const nsCString& target,
const bool& post,
const nsCString& buffer,
const bool& file,
NPError* result)
{
AssertPluginThread();
NS_RUNTIMEABORT("not reached");
return NULL;
}
void
StreamNotifyChild::ActorDestroy(ActorDestroyReason why)
{
if (AncestorDeletion == why && mBrowserStream) {
NS_ERROR("Pending NPP_URLNotify not called when closing an instance.");
// reclaim responsibility for deleting ourself
mBrowserStream->mStreamNotify = NULL;
mBrowserStream = NULL;
}
}
void
StreamNotifyChild::SetAssociatedStream(BrowserStreamChild* bs)
{
NS_ASSERTION(bs, "Shouldn't be null");
NS_ASSERTION(!mBrowserStream, "Two streams for one streamnotify?");
mBrowserStream = bs;
}
bool
StreamNotifyChild::Recv__delete__(const NPReason& reason)
{
AssertPluginThread();
if (mBrowserStream)
mBrowserStream->NotifyPending();
else
NPP_URLNotify(reason);
return true;
}
void
StreamNotifyChild::NPP_URLNotify(NPReason reason)
{
PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(Manager());
if (mClosure)
instance->mPluginIface->urlnotify(instance->GetNPP(), mURL.get(),
reason, mClosure);
}
bool
PluginInstanceChild::DeallocPStreamNotify(PStreamNotifyChild* notifyData)
{
AssertPluginThread();
if (!static_cast<StreamNotifyChild*>(notifyData)->mBrowserStream)
delete notifyData;
return true;
}
PluginScriptableObjectChild*
PluginInstanceChild::GetActorForNPObject(NPObject* aObject)
{
AssertPluginThread();
NS_ASSERTION(aObject, "Null pointer!");
if (aObject->_class == PluginScriptableObjectChild::GetClass()) {
// One of ours! It's a browser-provided object.
ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
NS_ASSERTION(object->parent, "Null actor!");
return object->parent;
}
PluginScriptableObjectChild* actor =
PluginModuleChild::current()->GetActorForNPObject(aObject);
if (actor) {
// Plugin-provided object that we've previously wrapped.
return actor;
}
actor = new PluginScriptableObjectChild(LocalObject);
if (!SendPPluginScriptableObjectConstructor(actor)) {
NS_ERROR("Failed to send constructor message!");
return nsnull;
}
actor->InitializeLocal(aObject);
return actor;
}
NPError
PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
NPStream** aStream)
{
AssertPluginThread();
PluginStreamChild* ps = new PluginStreamChild();
NPError result;
CallPPluginStreamConstructor(ps, nsDependentCString(aMIMEType),
NullableString(aWindow), &result);
if (NPERR_NO_ERROR != result) {
*aStream = NULL;
PPluginStreamChild::Call__delete__(ps, NPERR_GENERIC_ERROR, true);
return result;
}
*aStream = &ps->mStream;
return NPERR_NO_ERROR;
}
bool
PluginInstanceChild::RecvAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
const NPRemoteWindow& aWindow)
{
AssertPluginThread();
mWindow.window = reinterpret_cast<void*>(aWindow.window);
if (mWindow.width != aWindow.width || mWindow.height != aWindow.height) {
mCurrentSurface = nsnull;
mHelperSurface = nsnull;
mAccumulatedInvalidRect = nsIntRect(0, 0, aWindow.width, aWindow.height);
}
if (mWindow.clipRect.top != aWindow.clipRect.top ||
mWindow.clipRect.left != aWindow.clipRect.left ||
mWindow.clipRect.bottom != aWindow.clipRect.bottom ||
mWindow.clipRect.right != aWindow.clipRect.right)
mWindow.x = aWindow.x;
mWindow.y = aWindow.y;
mWindow.width = aWindow.width;
mWindow.height = aWindow.height;
mWindow.clipRect = aWindow.clipRect;
mWindow.type = aWindow.type;
if (mQuirks & QUIRK_SILVERLIGHT_DEFAULT_TRANSPARENT)
mIsTransparent = true;
mLayersRendering = true;
mSurfaceType = aSurfaceType;
UpdateWindowAttributes(true);
#ifdef XP_WIN
if (mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK)
CreateWinlessPopupSurrogate();
if (mQuirks & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)
SetupFlashMsgThrottle();
#endif
if (!mAccumulatedInvalidRect.IsEmpty()) {
AsyncShowPluginFrame();
}
return true;
}
static inline gfxRect
GfxFromNsRect(const nsIntRect& aRect)
{
return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
}
bool
PluginInstanceChild::CreateOptSurface(void)
{
nsRefPtr<gfxASurface> retsurf;
gfxASurface::gfxImageFormat format =
mIsTransparent ? gfxASurface::ImageFormatARGB32 :
gfxASurface::ImageFormatRGB24;
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
// On Maemo 5, we must send the Visibility event to activate the plugin
if (mMaemoImageRendering) {
NPEvent pluginEvent;
XVisibilityEvent& visibilityEvent = pluginEvent.xvisibility;
visibilityEvent.type = VisibilityNotify;
visibilityEvent.display = 0;
visibilityEvent.state = VisibilityUnobscured;
mPluginIface->event(&mData, reinterpret_cast<void*>(&pluginEvent));
}
#endif
#ifdef MOZ_X11
Display* dpy = mWsInfo.display;
Screen* screen = DefaultScreenOfDisplay(dpy);
if (format == gfxASurface::ImageFormatRGB24 &&
DefaultDepth(dpy, DefaultScreen(dpy)) == 16) {
format = gfxASurface::ImageFormatRGB16_565;
}
if (mSurfaceType == gfxASurface::SurfaceTypeXlib) {
XRenderPictFormat* xfmt = gfxXlibSurface::FindRenderFormat(dpy, format);
if (!xfmt) {
NS_ERROR("Need X falback surface, but FindRenderFormat failed");
return false;
}
mCurrentSurface =
gfxXlibSurface::Create(screen, xfmt,
gfxIntSize(mWindow.width,
mWindow.height));
return mCurrentSurface != nsnull;
}
#endif
#ifdef XP_WIN
if (mSurfaceType == gfxASurface::SurfaceTypeWin32 ||
mSurfaceType == gfxASurface::SurfaceTypeD2D) {
SharedDIBSurface* s = new SharedDIBSurface();
if (!s->Create(reinterpret_cast<HDC>(mWindow.window),
mWindow.width, mWindow.height, mIsTransparent))
return false;
mCurrentSurface = s;
return true;
}
NS_RUNTIMEABORT("Shared-memory drawing not expected on Windows.");
#endif
// Make common shmem implementation working for any platform
mCurrentSurface = new gfxSharedImageSurface();
return static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->
Init(this, gfxIntSize(mWindow.width, mWindow.height), format);
}
bool
PluginInstanceChild::MaybeCreatePlatformHelperSurface(void)
{
if (!mCurrentSurface) {
NS_ERROR("Cannot create helper surface without mCurrentSurface");
return false;
}
#ifdef MOZ_PLATFORM_MAEMO
// On maemo plugins support non-default visual rendering
bool supportNonDefaultVisual = true;
#else
bool supportNonDefaultVisual = false;
#endif
#ifdef MOZ_X11
Screen* screen = DefaultScreenOfDisplay(mWsInfo.display);
Visual* defaultVisual = DefaultVisualOfScreen(screen);
Visual* visual = nsnull;
Colormap colormap = 0;
mDoAlphaExtraction = false;
bool createHelperSurface = false;
if (mCurrentSurface->GetType() == gfxASurface::SurfaceTypeXlib) {
static_cast<gfxXlibSurface*>(mCurrentSurface.get())->
GetColormapAndVisual(&colormap, &visual);
// Create helper surface if layer surface visual not same as default
// and we don't support non-default visual rendering
if (!visual || (defaultVisual != visual && !supportNonDefaultVisual)) {
createHelperSurface = true;
visual = defaultVisual;
mDoAlphaExtraction = mIsTransparent;
}
} else if (mCurrentSurface->GetType() == gfxASurface::SurfaceTypeImage) {
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
if (mMaemoImageRendering) {
// No helper surface needed, when mMaemoImageRendering is TRUE.
// we can rendering directly into image memory
// with NPImageExpose Maemo5 NPAPI
return PR_TRUE;
}
#endif
// For image layer surface we should always create helper surface
createHelperSurface = true;
// Check if we can create helper surface with non-default visual
visual = gfxXlibSurface::FindVisual(screen,
static_cast<gfxImageSurface*>(mCurrentSurface.get())->Format());
if (visual && defaultVisual != visual && !supportNonDefaultVisual) {
visual = defaultVisual;
mDoAlphaExtraction = mIsTransparent;
}
}
if (createHelperSurface) {
if (!visual) {
NS_ERROR("Need X falback surface, but visual failed");
return false;
}
mHelperSurface =
gfxXlibSurface::Create(screen, visual,
mCurrentSurface->GetSize());
if (!mHelperSurface) {
NS_WARNING("Fail to create create helper surface");
return false;
}
}
#endif
return true;
}
bool
PluginInstanceChild::EnsureCurrentBuffer(void)
{
if (mCurrentSurface) {
return true;
}
if (!mWindow.width || !mWindow.height) {
return false;
}
if (!CreateOptSurface()) {
NS_ERROR("Cannot create optimized surface");
return false;
}
if (!MaybeCreatePlatformHelperSurface()) {
NS_ERROR("Cannot create helper surface");
return false;
}
return true;
}
void
PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow)
{
nsRefPtr<gfxASurface> curSurface = mHelperSurface ? mHelperSurface : mCurrentSurface;
bool needWindowUpdate = aForceSetWindow;
#ifdef MOZ_X11
Visual* visual = nsnull;
Colormap colormap = 0;
if (curSurface && curSurface->GetType() == gfxASurface::SurfaceTypeXlib) {
static_cast<gfxXlibSurface*>(curSurface.get())->
GetColormapAndVisual(&colormap, &visual);
if (visual != mWsInfo.visual || colormap != mWsInfo.colormap) {
mWsInfo.visual = visual;
mWsInfo.colormap = colormap;
needWindowUpdate = true;
}
}
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
else if (curSurface && curSurface->GetType() == gfxASurface::SurfaceTypeImage
&& mMaemoImageRendering) {
// For maemo5 we need to setup window/colormap to 0
// and specify depth of image surface
gfxImageSurface* img = static_cast<gfxImageSurface*>(curSurface.get());
if (mWindow.window ||
mWsInfo.depth != gfxUtils::ImageFormatToDepth(img->Format()) ||
mWsInfo.colormap) {
mWindow.window = nsnull;
mWsInfo.depth = gfxUtils::ImageFormatToDepth(img->Format());
mWsInfo.colormap = 0;
needWindowUpdate = PR_TRUE;
}
}
#endif // MAEMO
#endif // MOZ_X11
#ifdef XP_WIN
HDC dc = NULL;
if (curSurface) {
NS_ASSERTION(SharedDIBSurface::IsSharedDIBSurface(curSurface),
"Expected (SharedDIB) image surface.");
SharedDIBSurface* dibsurf = static_cast<SharedDIBSurface*>(curSurface.get());
dc = dibsurf->GetHDC();
}
if (mWindow.window != dc) {
mWindow.window = dc;
needWindowUpdate = true;
}
#endif // XP_WIN
if (!needWindowUpdate) {
return;
}
#ifndef XP_WIN
// On Windows, we translate the device context, in order for the window
// origin to be correct.
mWindow.x = mWindow.y = 0;
#endif
if (IsVisible()) {
// The clip rect is relative to drawable top-left.
nsIntRect clipRect;
// Don't ask the plugin to draw outside the drawable. The clip rect
// is in plugin coordinates, not window coordinates.
// This also ensures that the unsigned clip rectangle offsets won't be -ve.
clipRect.SetRect(0, 0, mWindow.width, mWindow.height);
mWindow.clipRect.left = 0;
mWindow.clipRect.top = 0;
mWindow.clipRect.right = clipRect.XMost();
mWindow.clipRect.bottom = clipRect.YMost();
}
#ifdef XP_WIN
// Windowless plugins on Windows need a WM_WINDOWPOSCHANGED event to update
// their location... or at least Flash does: Silverlight uses the
// window.x/y passed to NPP_SetWindow
if (mPluginIface->event) {
WINDOWPOS winpos = {
0, 0,
mWindow.x, mWindow.y,
mWindow.width, mWindow.height
};
NPEvent pluginEvent = {
WM_WINDOWPOSCHANGED, 0,
(LPARAM) &winpos
};
mPluginIface->event(&mData, &pluginEvent);
}
#endif
if (mPluginIface->setwindow) {
mPluginIface->setwindow(&mData, &mWindow);
}
return;
}
void
PluginInstanceChild::PaintRectToPlatformSurface(const nsIntRect& aRect,
gfxASurface* aSurface)
{
UpdateWindowAttributes();
#ifdef MOZ_X11
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
// On maemo5 we do support Image rendering NPAPI
if (mMaemoImageRendering &&
aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
mPendingPluginCall = PR_TRUE;
gfxImageSurface* image = static_cast<gfxImageSurface*>(aSurface);
NPImageExpose imgExp;
imgExp.depth = gfxUtils::ImageFormatToDepth(image->Format());
imgExp.x = aRect.x;
imgExp.y = aRect.y;
imgExp.width = aRect.width;
imgExp.height = aRect.height;
imgExp.stride = image->Stride();
imgExp.data = (char *)image->Data();
imgExp.dataSize.width = image->Width();
imgExp.dataSize.height = image->Height();
imgExp.translateX = 0;
imgExp.translateY = 0;
imgExp.scaleX = 1;
imgExp.scaleY = 1;
NPEvent pluginEvent;
XGraphicsExposeEvent& exposeEvent = pluginEvent.xgraphicsexpose;
exposeEvent.type = GraphicsExpose;
exposeEvent.display = 0;
// Store imageExpose structure pointer as drawable member
exposeEvent.drawable = (Drawable)&imgExp;
exposeEvent.x = imgExp.x;
exposeEvent.y = imgExp.y;
exposeEvent.width = imgExp.width;
exposeEvent.height = imgExp.height;
exposeEvent.count = 0;
// information not set:
exposeEvent.serial = 0;
exposeEvent.send_event = False;
exposeEvent.major_code = 0;
exposeEvent.minor_code = 0;
mPluginIface->event(&mData, reinterpret_cast<void*>(&exposeEvent));
mPendingPluginCall = PR_FALSE;
return;
}
#endif
NS_ASSERTION(aSurface->GetType() == gfxASurface::SurfaceTypeXlib,
"Non supported platform surface type");
mPendingPluginCall = true;
NPEvent pluginEvent;
XGraphicsExposeEvent& exposeEvent = pluginEvent.xgraphicsexpose;
exposeEvent.type = GraphicsExpose;
exposeEvent.display = mWsInfo.display;
exposeEvent.drawable = static_cast<gfxXlibSurface*>(aSurface)->XDrawable();
exposeEvent.x = aRect.x;
exposeEvent.y = aRect.y;
exposeEvent.width = aRect.width;
exposeEvent.height = aRect.height;
exposeEvent.count = 0;
// information not set:
exposeEvent.serial = 0;
exposeEvent.send_event = False;
exposeEvent.major_code = 0;
exposeEvent.minor_code = 0;
mPluginIface->event(&mData, reinterpret_cast<void*>(&exposeEvent));
mPendingPluginCall = false;
return;
#endif
#ifdef XP_WIN
NS_ASSERTION(SharedDIBSurface::IsSharedDIBSurface(aSurface),
"Expected (SharedDIB) image surface.");
mPendingPluginCall = true;
// This rect is in the window coordinate space. aRect is in the plugin
// coordinate space.
RECT rect = {
mWindow.x + aRect.x,
mWindow.y + aRect.y,
mWindow.x + aRect.x + aRect.width,
mWindow.y + aRect.y + aRect.height
};
NPEvent paintEvent = {
WM_PAINT,
uintptr_t(mWindow.window),
uintptr_t(&rect)
};
::SetViewportOrgEx((HDC) mWindow.window, -mWindow.x, -mWindow.y, NULL);
mPluginIface->event(&mData, reinterpret_cast<void*>(&paintEvent));
mPendingPluginCall = false;
return;
#endif
NS_RUNTIMEABORT("Surface type not implemented.");
}
void
PluginInstanceChild::PaintRectToSurface(const nsIntRect& aRect,
gfxASurface* aSurface,
const gfxRGBA& aColor)
{
// Render using temporary X surface, with copy to image surface
nsIntRect plPaintRect(aRect);
nsRefPtr<gfxASurface> renderSurface = aSurface;
#ifdef MOZ_X11
if (mIsTransparent && mFlash10Quirks) {
// Work around a bug in Flash up to 10.1 d51 at least, where expose event
// top left coordinates within the plugin-rect and not at the drawable
// origin are misinterpreted. (We can move the top left coordinate
// provided it is within the clipRect.), see bug 574583
plPaintRect.SetRect(0, 0, aRect.XMost(), aRect.YMost());
}
if (renderSurface->GetType() != gfxASurface::SurfaceTypeXlib) {
// On X11 we can paint to non Xlib surface only with HelperSurface
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
// Don't use mHelperSurface if surface is image and mMaemoImageRendering is TRUE
if (!mMaemoImageRendering ||
renderSurface->GetType() != gfxASurface::SurfaceTypeImage)
#endif
renderSurface = mHelperSurface;
}
#endif
if (mIsTransparent) {
// Clear surface content for transparent rendering
nsRefPtr<gfxContext> ctx = new gfxContext(renderSurface);
ctx->SetColor(aColor);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->Rectangle(GfxFromNsRect(plPaintRect));
ctx->Fill();
}
PaintRectToPlatformSurface(plPaintRect, renderSurface);
if (renderSurface != aSurface) {
// Copy helper surface content to target
nsRefPtr<gfxContext> ctx = new gfxContext(aSurface);
ctx->SetSource(renderSurface);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->Rectangle(GfxFromNsRect(aRect));
ctx->Fill();
}
}
void
PluginInstanceChild::PaintRectWithAlphaExtraction(const nsIntRect& aRect,
gfxASurface* aSurface)
{
// Paint onto black image
bool needImageSurface = true;
nsRefPtr<gfxImageSurface> blackImage;
gfxIntSize clipSize(aRect.width, aRect.height);
gfxPoint deviceOffset(-aRect.x, -aRect.y);
// Try to re-use existing image surface, and avoid one copy
if (aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
gfxImageSurface *surface = static_cast<gfxImageSurface*>(aSurface);
if (surface->Format() == gfxASurface::ImageFormatARGB32) {
needImageSurface = false;
blackImage = surface->GetSubimage(GfxFromNsRect(aRect));
}
}
// otherwise create new helper surface
if (needImageSurface) {
blackImage = new gfxImageSurface(clipSize, gfxASurface::ImageFormatARGB32);
}
// Paint to black image
blackImage->SetDeviceOffset(deviceOffset);
PaintRectToSurface(aRect, blackImage, gfxRGBA(0.0, 0.0, 0.0));
// Paint onto white image
nsRefPtr<gfxImageSurface> whiteImage =
new gfxImageSurface(clipSize, gfxASurface::ImageFormatRGB24);
whiteImage->SetDeviceOffset(deviceOffset);
PaintRectToSurface(aRect, whiteImage, gfxRGBA(1.0, 1.0, 1.0));
// Extract Alpha from black and white image and store to black Image
gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height);
if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage, nsnull)) {
return;
}
if (needImageSurface) {
nsRefPtr<gfxContext> ctx = new gfxContext(aSurface);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->SetSource(blackImage);
ctx->Rectangle(GfxFromNsRect(aRect));
ctx->Fill();
}
}
bool
PluginInstanceChild::ShowPluginFrame()
{
if (mPendingPluginCall) {
return false;
}
if (!EnsureCurrentBuffer()) {
return false;
}
// Make expose rect not bigger than clip rect
mAccumulatedInvalidRect.IntersectRect(mAccumulatedInvalidRect,
nsIntRect(mWindow.clipRect.left, mWindow.clipRect.top,
mWindow.clipRect.right - mWindow.clipRect.left,
mWindow.clipRect.bottom - mWindow.clipRect.top));
// Clear accRect here to be able to pass
// test_invalidate_during_plugin_paint test
nsIntRect rect = mAccumulatedInvalidRect;
mAccumulatedInvalidRect.Empty();
if (!ReadbackDifferenceRect(rect)) {
// Just repaint whole plugin, because we cannot read back from Shmem which is owned by another process
rect.SetRect(0, 0, mWindow.width, mWindow.height);
}
if (mDoAlphaExtraction) {
PaintRectWithAlphaExtraction(rect, mCurrentSurface);
} else {
PaintRectToSurface(rect, mCurrentSurface, gfxRGBA(0.0, 0.0, 0.0, 0.0));
}
NPRect r = { rect.y, rect.x, rect.YMost(), rect.XMost() };
SurfaceDescriptor currSurf;
#ifdef MOZ_X11
if (mCurrentSurface->GetType() == gfxASurface::SurfaceTypeXlib) {
gfxXlibSurface *xsurf = static_cast<gfxXlibSurface*>(mCurrentSurface.get());
currSurf = SurfaceDescriptorX11(xsurf->XDrawable(), xsurf->XRenderFormat()->id,
mCurrentSurface->GetSize());
// Need to sync all pending x-paint requests
// before giving drawable to another process
XSync(mWsInfo.display, False);
} else
#endif
#ifdef XP_WIN
if (SharedDIBSurface::IsSharedDIBSurface(mCurrentSurface)) {
base::SharedMemoryHandle handle = NULL;
SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get());
s->ShareToProcess(PluginModuleChild::current()->OtherProcess(), &handle);
currSurf = SurfaceDescriptorWin(handle, mCurrentSurface->GetSize(), mIsTransparent);
s->Flush();
} else
#endif
if (gfxSharedImageSurface::IsSharedImage(mCurrentSurface)) {
currSurf = static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem();
} else {
NS_RUNTIMEABORT("Surface type is not remotable");
return false;
}
// Unused, except to possibly return a shmem to us
SurfaceDescriptor returnSurf;
if (!SendShow(r, currSurf, &returnSurf)) {
return false;
}
nsRefPtr<gfxASurface> tmp = mCurrentSurface;
mCurrentSurface = mBackSurface;
mBackSurface = tmp;
// Outdated back surface... not usable anymore due to changed plugin size.
// Dropping obsolete surface
if (mCurrentSurface && mBackSurface &&
mCurrentSurface->GetSize() != mBackSurface->GetSize()) {
mCurrentSurface = nsnull;
}
mSurfaceDifferenceRect = rect;
return true;
}
bool
PluginInstanceChild::ReadbackDifferenceRect(const nsIntRect& rect)
{
if (!mBackSurface)
return false;
// We can read safely from XSurface and SharedDIBSurface, because
// PluginHost is not able to modify that surface
#if defined(MOZ_X11)
if (mBackSurface->GetType() != gfxASurface::SurfaceTypeXlib)
return false;
#elif defined(XP_WIN)
if (!SharedDIBSurface::IsSharedDIBSurface(mBackSurface))
return false;
#else
return false;
#endif
if (mSurfaceDifferenceRect.IsEmpty())
return true;
// Read back previous content
nsRefPtr<gfxContext> ctx = new gfxContext(mCurrentSurface);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->SetSource(mBackSurface);
// Subtract from mSurfaceDifferenceRect area which is overlapping with rect
nsIntRegion result;
result.Sub(mSurfaceDifferenceRect, nsIntRegion(rect));
nsIntRegionRectIterator iter(result);
const nsIntRect* r;
while ((r = iter.Next()) != nsnull) {
ctx->Rectangle(GfxFromNsRect(*r));
}
ctx->Fill();
return true;
}
void
PluginInstanceChild::InvalidateRectDelayed(void)
{
if (!mCurrentInvalidateTask || !IsVisible()) {
return;
}
mCurrentInvalidateTask = nsnull;
if (mAccumulatedInvalidRect.IsEmpty()) {
return;
}
if (!ShowPluginFrame()) {
AsyncShowPluginFrame();
}
}
void
PluginInstanceChild::AsyncShowPluginFrame(void)
{
if (mCurrentInvalidateTask || !IsVisible()) {
return;
}
mCurrentInvalidateTask =
NewRunnableMethod(this, &PluginInstanceChild::InvalidateRectDelayed);
MessageLoop::current()->PostTask(FROM_HERE, mCurrentInvalidateTask);
}
void
PluginInstanceChild::InvalidateRect(NPRect* aInvalidRect)
{
NS_ASSERTION(aInvalidRect, "Null pointer!");
#ifdef OS_WIN
// Invalidate and draw locally for windowed plugins.
if (mWindow.type == NPWindowTypeWindow) {
NS_ASSERTION(IsWindow(mPluginWindowHWND), "Bad window?!");
RECT rect = { aInvalidRect->left, aInvalidRect->top,
aInvalidRect->right, aInvalidRect->bottom };
::InvalidateRect(mPluginWindowHWND, &rect, FALSE);
return;
}
#endif
if (mLayersRendering) {
nsIntRect r(aInvalidRect->left, aInvalidRect->top,
aInvalidRect->right - aInvalidRect->left,
aInvalidRect->bottom - aInvalidRect->top);
mAccumulatedInvalidRect.UnionRect(r, mAccumulatedInvalidRect);
// If we are able to paint and invalidate sent, then reset
// accumulated rectangle
AsyncShowPluginFrame();
return;
}
SendNPN_InvalidateRect(*aInvalidRect);
}
uint32_t
PluginInstanceChild::ScheduleTimer(uint32_t interval, bool repeat,
TimerFunc func)
{
ChildTimer* t = new ChildTimer(this, interval, repeat, func);
if (0 == t->ID()) {
delete t;
return 0;
}
mTimers.AppendElement(t);
return t->ID();
}
void
PluginInstanceChild::UnscheduleTimer(uint32_t id)
{
if (0 == id)
return;
mTimers.RemoveElement(id, ChildTimer::IDComparator());
}
void
PluginInstanceChild::AsyncCall(PluginThreadCallback aFunc, void* aUserData)
{
ChildAsyncCall* task = new ChildAsyncCall(this, aFunc, aUserData);
{
MutexAutoLock lock(mAsyncCallMutex);
mPendingAsyncCalls.AppendElement(task);
}
ProcessChild::message_loop()->PostTask(FROM_HERE, task);
}
static PLDHashOperator
InvalidateObject(DeletingObjectEntry* e, void* userArg)
{
NPObject* o = e->GetKey();
if (!e->mDeleted && o->_class && o->_class->invalidate)
o->_class->invalidate(o);
return PL_DHASH_NEXT;
}
static PLDHashOperator
DeleteObject(DeletingObjectEntry* e, void* userArg)
{
NPObject* o = e->GetKey();
if (!e->mDeleted) {
e->mDeleted = true;
#ifdef NS_BUILD_REFCNT_LOGGING
{
int32_t refcnt = o->referenceCount;
while (refcnt) {
--refcnt;
NS_LOG_RELEASE(o, refcnt, "NPObject");
}
}
#endif
PluginModuleChild::DeallocNPObject(o);
}
return PL_DHASH_NEXT;
}
bool
PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
{
PLUGIN_LOG_DEBUG_METHOD;
AssertPluginThread();
#if defined(OS_WIN)
SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
#endif
if (mBackSurface) {
// Get last surface back, and drop it
SurfaceDescriptor temp = null_t();
NPRect r = { 0, 0, 1, 1 };
SendShow(r, temp, &temp);
}
if (gfxSharedImageSurface::IsSharedImage(mCurrentSurface))
DeallocShmem(static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem());
if (gfxSharedImageSurface::IsSharedImage(mBackSurface))
DeallocShmem(static_cast<gfxSharedImageSurface*>(mBackSurface.get())->GetShmem());
mCurrentSurface = nsnull;
mBackSurface = nsnull;
nsTArray<PBrowserStreamChild*> streams;
ManagedPBrowserStreamChild(streams);
// First make sure none of these streams become deleted
for (PRUint32 i = 0; i < streams.Length(); ) {
if (static_cast<BrowserStreamChild*>(streams[i])->InstanceDying())
++i;
else
streams.RemoveElementAt(i);
}
for (PRUint32 i = 0; i < streams.Length(); ++i)
static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery();
{
MutexAutoLock lock(mAsyncCallMutex);
for (PRUint32 i = 0; i < mPendingAsyncCalls.Length(); ++i)
mPendingAsyncCalls[i]->Cancel();
mPendingAsyncCalls.TruncateLength(0);
}
mTimers.Clear();
if (mCurrentInvalidateTask) {
mCurrentInvalidateTask->Cancel();
mCurrentInvalidateTask = nsnull;
}
PluginModuleChild::current()->NPP_Destroy(this);
mData.ndata = 0;
mDeletingHash = new nsTHashtable<DeletingObjectEntry>;
mDeletingHash->Init();
PluginModuleChild::current()->FindNPObjectsForInstance(this);
mDeletingHash->EnumerateEntries(InvalidateObject, NULL);
mDeletingHash->EnumerateEntries(DeleteObject, NULL);
// Null out our cached actors as they should have been killed in the
// PluginInstanceDestroyed call above.
mCachedWindowActor = nsnull;
mCachedElementActor = nsnull;
#if defined(OS_WIN)
SharedSurfaceRelease();
DestroyWinlessPopupSurrogate();
UnhookWinlessFlashThrottle();
#endif
return true;
}