forked from mirrors/gecko-dev
3810 lines
123 KiB
C++
3810 lines
123 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: sw=2 ts=4 et :
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "PluginBackgroundDestroyer.h"
|
|
#include "PluginInstanceChild.h"
|
|
#include "PluginModuleChild.h"
|
|
#include "BrowserStreamChild.h"
|
|
#include "StreamNotifyChild.h"
|
|
#include "PluginProcessChild.h"
|
|
#include "gfxASurface.h"
|
|
#include "gfxPlatform.h"
|
|
#include "gfx2DGlue.h"
|
|
#include "nsNPAPIPluginInstance.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "mozilla/gfx/Logging.h"
|
|
#ifdef MOZ_X11
|
|
# include "gfxXlibSurface.h"
|
|
#endif
|
|
#ifdef XP_WIN
|
|
# include "mozilla/D3DMessageUtils.h"
|
|
# include "mozilla/gfx/SharedDIBSurface.h"
|
|
# include "nsCrashOnException.h"
|
|
# include "gfxWindowsPlatform.h"
|
|
extern const wchar_t* kFlashFullscreenClass;
|
|
using mozilla::gfx::SharedDIBSurface;
|
|
#endif
|
|
#include "gfxSharedImageSurface.h"
|
|
#include "gfxUtils.h"
|
|
#include "gfxAlphaRecovery.h"
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/BasicEvents.h"
|
|
#include "mozilla/ipc/MessageChannel.h"
|
|
#include "mozilla/AutoRestore.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "ImageContainer.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::plugins;
|
|
using namespace mozilla::layers;
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::widget;
|
|
|
|
#ifdef MOZ_WIDGET_GTK
|
|
|
|
# include <gtk/gtk.h>
|
|
# include <gdk/gdkx.h>
|
|
# include <gdk/gdk.h>
|
|
|
|
#elif defined(OS_WIN)
|
|
|
|
# include <windows.h>
|
|
# include <windowsx.h>
|
|
|
|
# include "mozilla/widget/WinMessages.h"
|
|
# include "nsWindowsDllInterceptor.h"
|
|
# include "X11UndefineNone.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 = nullptr;
|
|
static WindowsDllInterceptor::FuncHookType<User32TrackPopupMenu>
|
|
sUser32TrackPopupMenuStub;
|
|
|
|
static WindowsDllInterceptor sImm32Intercept;
|
|
static WindowsDllInterceptor::FuncHookType<decltype(&ImmGetContext)>
|
|
sImm32ImmGetContextStub;
|
|
static WindowsDllInterceptor::FuncHookType<decltype(&ImmGetCompositionStringW)>
|
|
sImm32ImmGetCompositionStringStub;
|
|
static WindowsDllInterceptor::FuncHookType<decltype(&ImmSetCandidateWindow)>
|
|
sImm32ImmSetCandidateWindowStub;
|
|
static WindowsDllInterceptor::FuncHookType<decltype(&ImmNotifyIME)>
|
|
sImm32ImmNotifyIME;
|
|
static WindowsDllInterceptor::FuncHookType<decltype(&ImmAssociateContextEx)>
|
|
sImm32ImmAssociateContextExStub;
|
|
|
|
static PluginInstanceChild* sCurrentPluginInstance = nullptr;
|
|
static const HIMC sHookIMC = (const HIMC)0xefefefef;
|
|
|
|
using mozilla::gfx::SharedDIB;
|
|
|
|
// 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>
|
|
# include "PluginUtilsOSX.h"
|
|
#endif // defined(XP_MACOSX)
|
|
|
|
/**
|
|
* We can't use gfxPlatform::CreateDrawTargetForSurface() because calling
|
|
* gfxPlatform::GetPlatform() instantiates the prefs service, and that's not
|
|
* allowed from processes other than the main process. So we have our own
|
|
* version here.
|
|
*/
|
|
static RefPtr<DrawTarget> CreateDrawTargetForSurface(gfxASurface* aSurface) {
|
|
SurfaceFormat format = aSurface->GetSurfaceFormat();
|
|
RefPtr<DrawTarget> drawTarget = Factory::CreateDrawTargetForCairoSurface(
|
|
aSurface->CairoSurface(), aSurface->GetSize(), &format);
|
|
if (!drawTarget) {
|
|
MOZ_CRASH("CreateDrawTargetForSurface failed in plugin");
|
|
}
|
|
return drawTarget;
|
|
}
|
|
|
|
PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
|
|
const nsCString& aMimeType,
|
|
const nsTArray<nsCString>& aNames,
|
|
const nsTArray<nsCString>& aValues)
|
|
: mPluginIface(aPluginIface),
|
|
mMimeType(aMimeType),
|
|
mNames(aNames.Clone()),
|
|
mValues(aValues.Clone())
|
|
#if defined(XP_DARWIN) || defined(XP_WIN)
|
|
,
|
|
mContentsScaleFactor(1.0)
|
|
#endif
|
|
,
|
|
mCSSZoomFactor(0.0),
|
|
mDrawingModel(kDefaultDrawingModel),
|
|
mCurrentDirectSurface(nullptr),
|
|
mAsyncInvalidateMutex("PluginInstanceChild::mAsyncInvalidateMutex"),
|
|
mAsyncInvalidateTask(0),
|
|
mCachedWindowActor(nullptr),
|
|
mCachedElementActor(nullptr)
|
|
#if defined(OS_WIN)
|
|
,
|
|
mPluginWindowHWND(0),
|
|
mPluginWndProc(0),
|
|
mPluginParentHWND(0),
|
|
mCachedWinlessPluginHWND(0),
|
|
mWinlessPopupSurrogateHWND(0),
|
|
mWinlessThrottleOldWndProc(0),
|
|
mWinlessHiddenMsgHWND(0)
|
|
#endif // OS_WIN
|
|
#if defined(MOZ_WIDGET_COCOA)
|
|
# if defined(__i386__)
|
|
,
|
|
mEventModel(NPEventModelCarbon)
|
|
# endif
|
|
,
|
|
mShColorSpace(nullptr),
|
|
mShContext(nullptr),
|
|
mCGLayer(nullptr),
|
|
mCARefreshTimer(0),
|
|
mCurrentEvent(nullptr)
|
|
#endif
|
|
,
|
|
mLayersRendering(false)
|
|
#ifdef XP_WIN
|
|
,
|
|
mCurrentSurfaceActor(nullptr),
|
|
mBackSurfaceActor(nullptr)
|
|
#endif
|
|
,
|
|
mAccumulatedInvalidRect(0, 0, 0, 0),
|
|
mIsTransparent(false),
|
|
mSurfaceType(gfxSurfaceType::Max),
|
|
mPendingPluginCall(false),
|
|
mDoAlphaExtraction(false),
|
|
mHasPainted(false),
|
|
mSurfaceDifferenceRect(0, 0, 0, 0),
|
|
mDestroyed(false)
|
|
#ifdef XP_WIN
|
|
,
|
|
mLastEnableIMEState(true)
|
|
#endif // #ifdef XP_WIN
|
|
,
|
|
mStackDepth(0) {
|
|
memset(&mWindow, 0, sizeof(mWindow));
|
|
mWindow.type = NPWindowTypeWindow;
|
|
mData.ndata = (void*)this;
|
|
mData.pdata = nullptr;
|
|
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
|
|
mWindow.ws_info = &mWsInfo;
|
|
memset(&mWsInfo, 0, sizeof(mWsInfo));
|
|
# ifdef MOZ_WIDGET_GTK
|
|
mWsInfo.display = nullptr;
|
|
# else
|
|
mWsInfo.display = DefaultXDisplay();
|
|
# endif
|
|
#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
|
|
#if defined(OS_WIN)
|
|
InitPopupMenuHook();
|
|
InitImm32Hook();
|
|
#endif // OS_WIN
|
|
}
|
|
|
|
PluginInstanceChild::~PluginInstanceChild() {
|
|
#if defined(OS_WIN)
|
|
NS_ASSERTION(!mPluginWindowHWND,
|
|
"Destroying PluginInstanceChild without NPP_Destroy?");
|
|
// In the event that we registered for audio device changes, stop.
|
|
PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
|
|
if (chromeInstance) {
|
|
chromeInstance->PluginRequiresAudioDeviceChanges(this, false);
|
|
}
|
|
#endif
|
|
#if defined(MOZ_WIDGET_COCOA)
|
|
if (mShColorSpace) {
|
|
::CGColorSpaceRelease(mShColorSpace);
|
|
}
|
|
if (mShContext) {
|
|
::CGContextRelease(mShContext);
|
|
}
|
|
if (mCGLayer) {
|
|
PluginUtilsOSX::ReleaseCGLayer(mCGLayer);
|
|
}
|
|
if (mDrawingModel == NPDrawingModelCoreAnimation) {
|
|
UnscheduleTimer(mCARefreshTimer);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
NPError PluginInstanceChild::DoNPP_New() {
|
|
// unpack the arguments into a C format
|
|
int argc = mNames.Length();
|
|
NS_ASSERTION(argc == (int)mValues.Length(), "argn.length != argv.length");
|
|
|
|
UniquePtr<char*[]> argn(new char*[1 + argc]);
|
|
UniquePtr<char*[]> argv(new char*[1 + argc]);
|
|
argn[argc] = 0;
|
|
argv[argc] = 0;
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
argn[i] = const_cast<char*>(NullableStringGet(mNames[i]));
|
|
argv[i] = const_cast<char*>(NullableStringGet(mValues[i]));
|
|
}
|
|
|
|
NPP npp = GetNPP();
|
|
|
|
NPError rv = mPluginIface->newp((char*)NullableStringGet(mMimeType), npp,
|
|
NP_EMBED, argc, argn.get(), argv.get(), 0);
|
|
if (NPERR_NO_ERROR != rv) {
|
|
return rv;
|
|
}
|
|
|
|
if (!Initialize()) {
|
|
rv = NPERR_MODULE_LOAD_FAILED_ERROR;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int PluginInstanceChild::GetQuirks() {
|
|
return PluginModuleChild::GetChrome()->GetQuirks();
|
|
}
|
|
|
|
NPError PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
|
|
NPObject** aObject) {
|
|
PluginScriptableObjectChild* actor = nullptr;
|
|
NPError result = NPERR_NO_ERROR;
|
|
|
|
switch (aValue) {
|
|
case NPNVWindowNPObject:
|
|
if (!(actor = mCachedWindowActor)) {
|
|
result = NPERR_GENERIC_ERROR;
|
|
PPluginScriptableObjectChild* actorProtocol;
|
|
if (CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result) &&
|
|
(result == NPERR_NO_ERROR)) {
|
|
actor = mCachedWindowActor =
|
|
static_cast<PluginScriptableObjectChild*>(actorProtocol);
|
|
NS_ASSERTION(actor, "Null actor!");
|
|
if (!actor->GetObject(false)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
PluginModuleChild::sBrowserFuncs.retainobject(
|
|
actor->GetObject(false));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NPNVPluginElementNPObject:
|
|
if (!(actor = mCachedElementActor)) {
|
|
result = NPERR_GENERIC_ERROR;
|
|
PPluginScriptableObjectChild* actorProtocol;
|
|
if (CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol,
|
|
&result) &&
|
|
(result == NPERR_NO_ERROR)) {
|
|
actor = mCachedElementActor =
|
|
static_cast<PluginScriptableObjectChild*>(actorProtocol);
|
|
NS_ASSERTION(actor, "Null actor!");
|
|
if (!actor->GetObject(false)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
PluginModuleChild::sBrowserFuncs.retainobject(
|
|
actor->GetObject(false));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
result = NPERR_GENERIC_ERROR;
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
"Don't know what to do with this value "
|
|
"type!");
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
NPError currentResult;
|
|
PPluginScriptableObjectChild* currentActor = nullptr;
|
|
|
|
switch (aValue) {
|
|
case NPNVWindowNPObject:
|
|
CallNPN_GetValue_NPNVWindowNPObject(¤tActor, ¤tResult);
|
|
break;
|
|
case NPNVPluginElementNPObject:
|
|
CallNPN_GetValue_NPNVPluginElementNPObject(¤tActor,
|
|
¤tResult);
|
|
break;
|
|
default:
|
|
MOZ_ASSERT(false);
|
|
}
|
|
|
|
// Make sure that the current actor returned by the parent matches our
|
|
// cached actor!
|
|
NS_ASSERTION(!currentActor || static_cast<PluginScriptableObjectChild*>(
|
|
currentActor) == actor,
|
|
"Cached actor is out of date!");
|
|
}
|
|
#endif
|
|
|
|
if (result != NPERR_NO_ERROR) {
|
|
return result;
|
|
}
|
|
|
|
NPObject* object;
|
|
if (!(object = actor->GetObject(false))) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
|
|
*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();
|
|
AutoStackHelper guard(this);
|
|
|
|
switch (aVar) {
|
|
#if defined(MOZ_X11)
|
|
case NPNVToolkit:
|
|
*((NPNToolkitType*)aValue) = NPNVGtk2;
|
|
return NPERR_NO_ERROR;
|
|
|
|
case NPNVxDisplay:
|
|
if (!mWsInfo.display) {
|
|
// We are called before Initialize() so we have to call it now.
|
|
if (!Initialize()) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
NS_ASSERTION(mWsInfo.display, "We should have a valid display!");
|
|
}
|
|
*(void**)aValue = mWsInfo.display;
|
|
return NPERR_NO_ERROR;
|
|
|
|
#elif defined(OS_WIN)
|
|
case NPNVToolkit:
|
|
return NPERR_GENERIC_ERROR;
|
|
#endif
|
|
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 NPNVdocumentOrigin: {
|
|
nsCString v;
|
|
NPError result;
|
|
if (!CallNPN_GetValue_NPNVdocumentOrigin(&v, &result)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
if (result == NPERR_NO_ERROR ||
|
|
(GetQuirks() & QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN)) {
|
|
*static_cast<char**>(aValue) = ToNewCString(v);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
case NPNVWindowNPObject: // Intentional fall-through
|
|
case NPNVPluginElementNPObject: {
|
|
NPObject* object;
|
|
*((NPObject**)aValue) = nullptr;
|
|
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
|
|
}
|
|
|
|
case NPNVsupportsAsyncBitmapSurfaceBool: {
|
|
bool value = false;
|
|
CallNPN_GetValue_SupportsAsyncBitmapSurface(&value);
|
|
*((NPBool*)aValue) = value;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
case NPNVsupportsAsyncWindowsDXGISurfaceBool: {
|
|
bool value = false;
|
|
CallNPN_GetValue_SupportsAsyncDXGISurface(&value);
|
|
*((NPBool*)aValue) = value;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
#endif
|
|
|
|
#ifdef XP_WIN
|
|
case NPNVpreferredDXGIAdapter: {
|
|
DxgiAdapterDesc desc;
|
|
if (!CallNPN_GetValue_PreferredDXGIAdapter(&desc)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
*reinterpret_cast<DXGI_ADAPTER_DESC*>(aValue) = desc.ToDesc();
|
|
return NPERR_NO_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 NPNVsupportsCompositingCoreAnimationPluginsBool: {
|
|
*((NPBool*)aValue) = true;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
case NPNVsupportsCocoaBool: {
|
|
*((NPBool*)aValue) = true;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
# ifndef NP_NO_CARBON
|
|
case NPNVsupportsCarbonBool: {
|
|
*((NPBool*)aValue) = false;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
# endif
|
|
|
|
case NPNVsupportsUpdatedCocoaTextInputBool: {
|
|
*static_cast<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 */
|
|
|
|
#if defined(XP_MACOSX) || defined(XP_WIN)
|
|
case NPNVcontentsScaleFactor: {
|
|
*static_cast<double*>(aValue) = mContentsScaleFactor;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
#endif /* defined(XP_MACOSX) || defined(XP_WIN) */
|
|
|
|
case NPNVCSSZoomFactor: {
|
|
*static_cast<double*>(aValue) = mCSSZoomFactor;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
case NPNVjavascriptEnabledBool:
|
|
case NPNVasdEnabledBool:
|
|
case NPNVisOfflineBool:
|
|
case NPNVSupportsXEmbedBool:
|
|
case NPNVSupportsWindowless:
|
|
MOZ_FALLTHROUGH_ASSERT(
|
|
"NPNVariable should be handled in "
|
|
"PluginModuleChild.");
|
|
#endif
|
|
|
|
default:
|
|
MOZ_LOG(GetPluginLog(), LogLevel::Warning,
|
|
("In PluginInstanceChild::NPN_GetValue: Unhandled NPNVariable %i "
|
|
"(%s)",
|
|
(int)aVar, NPNVariableToString(aVar)));
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
}
|
|
|
|
#ifdef MOZ_WIDGET_COCOA
|
|
# define DEFAULT_REFRESH_MS 20 // CoreAnimation: 50 FPS
|
|
|
|
void CAUpdate(NPP npp, uint32_t timerID) {
|
|
static_cast<PluginInstanceChild*>(npp->ndata)->Invalidate();
|
|
}
|
|
|
|
void PluginInstanceChild::Invalidate() {
|
|
NPRect windowRect = {0, 0, uint16_t(mWindow.height), uint16_t(mWindow.width)};
|
|
|
|
InvalidateRect(&windowRect);
|
|
}
|
|
#endif
|
|
|
|
NPError PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue) {
|
|
MOZ_LOG(GetPluginLog(), LogLevel::Debug,
|
|
("%s (aVar=%i, aValue=%p)", FULLFUNCTION, (int)aVar, aValue));
|
|
|
|
AssertPluginThread();
|
|
|
|
AutoStackHelper guard(this);
|
|
|
|
switch (aVar) {
|
|
case NPPVpluginWindowBool: {
|
|
NPError rv;
|
|
bool windowed = (NPBool)(intptr_t)aValue;
|
|
|
|
if (windowed) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
|
|
if (!CallNPN_SetValue_NPPVpluginWindow(windowed, &rv))
|
|
return NPERR_GENERIC_ERROR;
|
|
|
|
mWindow.type = NPWindowTypeDrawable;
|
|
return rv;
|
|
}
|
|
|
|
case NPPVpluginTransparentBool: {
|
|
NPError rv;
|
|
mIsTransparent = (!!aValue);
|
|
|
|
if (!CallNPN_SetValue_NPPVpluginTransparent(mIsTransparent, &rv))
|
|
return NPERR_GENERIC_ERROR;
|
|
|
|
return rv;
|
|
}
|
|
|
|
case NPPVpluginUsesDOMForCursorBool: {
|
|
NPError rv = NPERR_GENERIC_ERROR;
|
|
if (!CallNPN_SetValue_NPPVpluginUsesDOMForCursor((NPBool)(intptr_t)aValue,
|
|
&rv)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
case NPPVpluginDrawingModel: {
|
|
NPError rv;
|
|
int drawingModel = (int16_t)(intptr_t)aValue;
|
|
|
|
if (!CallNPN_SetValue_NPPVpluginDrawingModel(drawingModel, &rv))
|
|
return NPERR_GENERIC_ERROR;
|
|
|
|
mDrawingModel = drawingModel;
|
|
|
|
#ifdef XP_MACOSX
|
|
if (drawingModel == NPDrawingModelCoreAnimation) {
|
|
mCARefreshTimer = ScheduleTimer(DEFAULT_REFRESH_MS, true, CAUpdate);
|
|
}
|
|
#endif
|
|
|
|
PLUGIN_LOG_DEBUG(
|
|
(" Plugin requested drawing model id #%i\n", mDrawingModel));
|
|
|
|
return rv;
|
|
}
|
|
|
|
#ifdef XP_MACOSX
|
|
case NPPVpluginEventModel: {
|
|
NPError rv;
|
|
int eventModel = (int16_t)(intptr_t)aValue;
|
|
|
|
if (!CallNPN_SetValue_NPPVpluginEventModel(eventModel, &rv))
|
|
return NPERR_GENERIC_ERROR;
|
|
# if defined(__i386__)
|
|
mEventModel = static_cast<NPEventModel>(eventModel);
|
|
# endif
|
|
|
|
PLUGIN_LOG_DEBUG(
|
|
(" Plugin requested event model id # %i\n", eventModel));
|
|
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
case NPPVpluginIsPlayingAudio: {
|
|
NPError rv = NPERR_GENERIC_ERROR;
|
|
if (!CallNPN_SetValue_NPPVpluginIsPlayingAudio((NPBool)(intptr_t)aValue,
|
|
&rv)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
case NPPVpluginRequiresAudioDeviceChanges: {
|
|
// Many other NPN_SetValue variables are forwarded to our
|
|
// PluginInstanceParent, which runs on a content process. We
|
|
// instead forward this message to the PluginModuleParent, which runs
|
|
// on the chrome process. This is because our audio
|
|
// API calls should run the chrome proc, not content.
|
|
NPError rv = NPERR_GENERIC_ERROR;
|
|
PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
|
|
if (chromeInstance) {
|
|
rv = chromeInstance->PluginRequiresAudioDeviceChanges(
|
|
this, (NPBool)(intptr_t)aValue);
|
|
}
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
MOZ_LOG(GetPluginLog(), LogLevel::Warning,
|
|
("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i "
|
|
"(%s)",
|
|
(int)aVar, NPPVariableToString(aVar)));
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(
|
|
bool* wantsAllStreams, NPError* rv) {
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
|
|
uint32_t value = 0;
|
|
if (!mPluginIface->getvalue) {
|
|
*rv = NPERR_GENERIC_ERROR;
|
|
} else {
|
|
*rv = mPluginIface->getvalue(GetNPP(), NPPVpluginWantsAllNetworkStreams,
|
|
&value);
|
|
}
|
|
*wantsAllStreams = value;
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginScriptableNPObject(
|
|
PPluginScriptableObjectChild** aValue, NPError* aResult) {
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
|
|
NPObject* object = nullptr;
|
|
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 IPC_OK();
|
|
}
|
|
|
|
NS_ERROR("Failed to get actor!");
|
|
result = NPERR_GENERIC_ERROR;
|
|
} else {
|
|
result = NPERR_GENERIC_ERROR;
|
|
}
|
|
|
|
*aValue = nullptr;
|
|
*aResult = result;
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(
|
|
nsCString* aPlugId, NPError* aResult) {
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
|
|
#if MOZ_ACCESSIBILITY_ATK
|
|
|
|
char* plugId = nullptr;
|
|
NPError result = NPERR_GENERIC_ERROR;
|
|
if (mPluginIface->getvalue) {
|
|
result = mPluginIface->getvalue(
|
|
GetNPP(), NPPVpluginNativeAccessibleAtkPlugId, &plugId);
|
|
}
|
|
|
|
*aPlugId = nsCString(plugId);
|
|
*aResult = result;
|
|
return IPC_OK();
|
|
|
|
#else
|
|
|
|
MOZ_CRASH("shouldn't be called on non-ATK platforms");
|
|
|
|
#endif
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
PluginInstanceChild::AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value,
|
|
NPError* result) {
|
|
if (!mPluginIface->setvalue) {
|
|
*result = NPERR_GENERIC_ERROR;
|
|
return IPC_OK();
|
|
}
|
|
|
|
NPBool v = value;
|
|
*result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
PluginInstanceChild::AnswerNPP_SetValue_NPNVCSSZoomFactor(const double& value,
|
|
NPError* result) {
|
|
if (!mPluginIface->setvalue) {
|
|
*result = NPERR_GENERIC_ERROR;
|
|
return IPC_OK();
|
|
}
|
|
|
|
mCSSZoomFactor = value;
|
|
double v = value;
|
|
*result = mPluginIface->setvalue(GetNPP(), NPNVCSSZoomFactor, &v);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
PluginInstanceChild::AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value,
|
|
NPError* result) {
|
|
if (!mPluginIface->setvalue) {
|
|
*result = NPERR_GENERIC_ERROR;
|
|
return IPC_OK();
|
|
}
|
|
|
|
NPBool v = value;
|
|
*result = mPluginIface->setvalue(GetNPP(), NPNVmuteAudioBool, &v);
|
|
return IPC_OK();
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
NPError PluginInstanceChild::DefaultAudioDeviceChanged(
|
|
NPAudioDeviceChangeDetails& details) {
|
|
if (!mPluginIface->setvalue) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
return mPluginIface->setvalue(GetNPP(), NPNVaudioDeviceChangeDetails,
|
|
(void*)&details);
|
|
}
|
|
|
|
NPError PluginInstanceChild::AudioDeviceStateChanged(
|
|
NPAudioDeviceStateChanged& aDeviceState) {
|
|
if (!mPluginIface->setvalue) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
return mPluginIface->setvalue(GetNPP(), NPNVaudioDeviceStateChanged,
|
|
(void*)&aDeviceState);
|
|
}
|
|
|
|
void SetMouseEventWParam(NPEvent* aEvent) {
|
|
// Fill in potentially missing key state info. See
|
|
// nsPluginInstanceOwner::ProcessEvent for circumstances where this happens.
|
|
const auto kMouseMessages = mozilla::Array<int, 9>(
|
|
WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN, WM_LBUTTONUP,
|
|
WM_MBUTTONUP, WM_RBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL);
|
|
|
|
bool isInvalidWParam =
|
|
(aEvent->wParam == NPAPI_INVALID_WPARAM) &&
|
|
(std::find(kMouseMessages.begin(), kMouseMessages.end(),
|
|
static_cast<int>(aEvent->event)) != kMouseMessages.end());
|
|
|
|
if (!isInvalidWParam) {
|
|
return;
|
|
}
|
|
|
|
aEvent->wParam = (::GetKeyState(VK_CONTROL) ? MK_CONTROL : 0) |
|
|
(::GetKeyState(VK_SHIFT) ? MK_SHIFT : 0) |
|
|
(::GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0) |
|
|
(::GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0) |
|
|
(::GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0) |
|
|
(::GetKeyState(VK_XBUTTON1) ? MK_XBUTTON1 : 0) |
|
|
(::GetKeyState(VK_XBUTTON2) ? MK_XBUTTON2 : 0);
|
|
}
|
|
#endif
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_HandleEvent(
|
|
const NPRemoteEvent& event, int16_t* handled) {
|
|
PLUGIN_LOG_DEBUG_FUNCTION;
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
|
|
#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
|
|
|
|
#if defined(XP_MACOSX) || defined(XP_WIN)
|
|
// event.contentsScaleFactor <= 0 is a signal we shouldn't use it,
|
|
// for example when AnswerNPP_HandleEvent() is called from elsewhere
|
|
// in the child process (not via rpc code from the parent process).
|
|
if (event.contentsScaleFactor > 0) {
|
|
mContentsScaleFactor = event.contentsScaleFactor;
|
|
}
|
|
#endif
|
|
|
|
#ifdef OS_WIN
|
|
// FIXME/bug 567645: temporarily drop the "dummy event" on the floor
|
|
if (WM_NULL == evcopy.event) return IPC_OK();
|
|
|
|
SetMouseEventWParam(&evcopy);
|
|
*handled = WinlessHandleEvent(evcopy);
|
|
return IPC_OK();
|
|
#endif
|
|
|
|
// XXX A previous call to mPluginIface->event might block, e.g. right click
|
|
// for context menu. Still, we might get here again, calling into the plugin
|
|
// a second time while it's in the previous call.
|
|
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, X11False);
|
|
}
|
|
#endif
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
#ifdef XP_MACOSX
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(
|
|
const NPRemoteEvent& event, Shmem&& mem, int16_t* handled, Shmem* rtnmem) {
|
|
PLUGIN_LOG_DEBUG_FUNCTION;
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
|
|
PaintTracker pt;
|
|
|
|
NPCocoaEvent evcopy = event.event;
|
|
mContentsScaleFactor = event.contentsScaleFactor;
|
|
|
|
if (evcopy.type == NPCocoaEventDrawRect) {
|
|
int scaleFactor = ceil(mContentsScaleFactor);
|
|
if (!mShColorSpace) {
|
|
mShColorSpace = CreateSystemColorSpace();
|
|
if (!mShColorSpace) {
|
|
PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
|
|
*handled = false;
|
|
*rtnmem = mem;
|
|
return IPC_OK();
|
|
}
|
|
}
|
|
if (!mShContext) {
|
|
void* cgContextByte = mem.get<char>();
|
|
mShContext = ::CGBitmapContextCreate(
|
|
cgContextByte, mWindow.width * scaleFactor,
|
|
mWindow.height * scaleFactor, 8, mWindow.width * 4 * scaleFactor,
|
|
mShColorSpace,
|
|
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
|
|
|
|
if (!mShContext) {
|
|
PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
|
|
*handled = false;
|
|
*rtnmem = mem;
|
|
return IPC_OK();
|
|
}
|
|
}
|
|
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 IPC_OK();
|
|
}
|
|
|
|
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 IPC_OK();
|
|
}
|
|
|
|
#else
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(
|
|
const NPRemoteEvent& event, Shmem&& mem, int16_t* handled, Shmem* rtnmem) {
|
|
MOZ_CRASH("not reached.");
|
|
*rtnmem = mem;
|
|
return IPC_OK();
|
|
}
|
|
#endif
|
|
|
|
#ifdef XP_MACOSX
|
|
|
|
void CallCGDraw(CGContextRef ref, void* aPluginInstance,
|
|
nsIntRect aUpdateRect) {
|
|
PluginInstanceChild* pluginInstance = (PluginInstanceChild*)aPluginInstance;
|
|
|
|
pluginInstance->CGDraw(ref, aUpdateRect);
|
|
}
|
|
|
|
bool PluginInstanceChild::CGDraw(CGContextRef ref, nsIntRect aUpdateRect) {
|
|
NPCocoaEvent drawEvent;
|
|
drawEvent.type = NPCocoaEventDrawRect;
|
|
drawEvent.version = 0;
|
|
drawEvent.data.draw.x = aUpdateRect.x;
|
|
drawEvent.data.draw.y = aUpdateRect.y;
|
|
drawEvent.data.draw.width = aUpdateRect.width;
|
|
drawEvent.data.draw.height = aUpdateRect.height;
|
|
drawEvent.data.draw.context = ref;
|
|
|
|
NPRemoteEvent remoteDrawEvent = {drawEvent};
|
|
// Signal to AnswerNPP_HandleEvent() not to use this value
|
|
remoteDrawEvent.contentsScaleFactor = -1.0;
|
|
|
|
int16_t handled;
|
|
AnswerNPP_HandleEvent(remoteDrawEvent, &handled);
|
|
return handled == true;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(
|
|
const NPRemoteEvent& event, const uint32_t& surfaceid, int16_t* handled) {
|
|
PLUGIN_LOG_DEBUG_FUNCTION;
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
|
|
PaintTracker pt;
|
|
|
|
NPCocoaEvent evcopy = event.event;
|
|
mContentsScaleFactor = event.contentsScaleFactor;
|
|
RefPtr<MacIOSurface> surf =
|
|
MacIOSurface::LookupSurface(surfaceid, mContentsScaleFactor);
|
|
if (!surf) {
|
|
NS_ERROR("Invalid IOSurface.");
|
|
*handled = false;
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
if (!mCARenderer) {
|
|
mCARenderer = new nsCARenderer();
|
|
}
|
|
|
|
if (evcopy.type == NPCocoaEventDrawRect) {
|
|
mCARenderer->AttachIOSurface(surf);
|
|
if (!mCARenderer->isInit()) {
|
|
void* caLayer = nullptr;
|
|
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 IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
mCARenderer->SetupRenderer(caLayer, mWindow.width, mWindow.height,
|
|
mContentsScaleFactor,
|
|
GetQuirks() & QUIRK_ALLOW_OFFLINE_RENDERER
|
|
? ALLOW_OFFLINE_RENDERER
|
|
: DISALLOW_OFFLINE_RENDERER);
|
|
|
|
// 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 IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
mCARenderer->Render(mWindow.width, mWindow.height, mContentsScaleFactor,
|
|
nullptr);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
#else
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(
|
|
const NPRemoteEvent& event, const uint32_t& surfaceid, int16_t* handled) {
|
|
MOZ_CRASH("NPP_HandleEvent_IOSurface is a OSX-only message");
|
|
}
|
|
#endif
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::RecvWindowPosChanged(
|
|
const NPRemoteEvent& event) {
|
|
NS_ASSERTION(!mLayersRendering && !mPendingPluginCall,
|
|
"Shouldn't be receiving WindowPosChanged with layer rendering");
|
|
|
|
#ifdef OS_WIN
|
|
int16_t dontcare;
|
|
return AnswerNPP_HandleEvent(event, &dontcare);
|
|
#else
|
|
MOZ_CRASH("WindowPosChanged is a windows-only message");
|
|
#endif
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::RecvContentsScaleFactorChanged(
|
|
const double& aContentsScaleFactor) {
|
|
#if defined(XP_MACOSX) || defined(XP_WIN)
|
|
mContentsScaleFactor = aContentsScaleFactor;
|
|
# if defined(XP_MACOSX)
|
|
if (mShContext) {
|
|
// Release the shared context so that it is reallocated
|
|
// with the new size.
|
|
::CGContextRelease(mShContext);
|
|
mShContext = nullptr;
|
|
}
|
|
# endif
|
|
return IPC_OK();
|
|
#else
|
|
MOZ_CRASH("ContentsScaleFactorChanged is an Windows or OSX only message");
|
|
#endif
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerCreateChildPluginWindow(
|
|
NativeWindowHandle* aChildPluginWindow) {
|
|
#if defined(XP_WIN)
|
|
MOZ_ASSERT(!mPluginWindowHWND);
|
|
|
|
if (!CreatePluginWindow()) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
MOZ_ASSERT(mPluginWindowHWND);
|
|
|
|
*aChildPluginWindow = mPluginWindowHWND;
|
|
return IPC_OK();
|
|
#else
|
|
MOZ_ASSERT_UNREACHABLE("CreateChildPluginWindow not implemented!");
|
|
return IPC_FAIL_NO_REASON(this);
|
|
#endif
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::RecvCreateChildPopupSurrogate(
|
|
const NativeWindowHandle& aNetscapeWindow) {
|
|
#if defined(XP_WIN)
|
|
mCachedWinlessPluginHWND = aNetscapeWindow;
|
|
CreateWinlessPopupSurrogate();
|
|
return IPC_OK();
|
|
#else
|
|
MOZ_ASSERT_UNREACHABLE("CreateChildPluginWindow not implemented!");
|
|
return IPC_FAIL_NO_REASON(this);
|
|
#endif
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_SetWindow(
|
|
const NPRemoteWindow& aWindow) {
|
|
PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%" PRIx64
|
|
", x: %d, y: %d, width: %d, height: %d>)",
|
|
FULLFUNCTION, aWindow.window, aWindow.x, aWindow.y,
|
|
aWindow.width, aWindow.height));
|
|
NS_ASSERTION(!mLayersRendering && !mPendingPluginCall,
|
|
"Shouldn't be receiving NPP_SetWindow with layer rendering");
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
|
|
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
|
|
NS_ASSERTION(mWsInfo.display, "We should have a valid display!");
|
|
|
|
// The minimum info is sent over IPC to allow this
|
|
// code to determine the rest.
|
|
|
|
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;
|
|
int depth;
|
|
FindVisualAndDepth(mWsInfo.display, aWindow.visualID, &mWsInfo.visual,
|
|
&depth);
|
|
mWsInfo.depth = depth;
|
|
|
|
PLUGIN_LOG_DEBUG(
|
|
("[InstanceChild][%p] Answer_SetWindow w=<x=%d,y=%d, w=%d,h=%d>, "
|
|
"clip=<l=%d,t=%d,r=%d,b=%d>",
|
|
this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
|
|
mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right,
|
|
mWindow.clipRect.bottom));
|
|
|
|
if (mPluginIface->setwindow) (void)mPluginIface->setwindow(&mData, &mWindow);
|
|
|
|
#elif defined(OS_WIN)
|
|
switch (aWindow.type) {
|
|
case NPWindowTypeWindow: {
|
|
MOZ_ASSERT(mPluginWindowHWND,
|
|
"Child plugin window must exist before call to SetWindow");
|
|
|
|
HWND parentHWND = reinterpret_cast<HWND>(aWindow.window);
|
|
if (mPluginWindowHWND != parentHWND) {
|
|
mPluginParentHWND = parentHWND;
|
|
ShowWindow(mPluginWindowHWND, SW_SHOWNA);
|
|
}
|
|
|
|
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;
|
|
mContentsScaleFactor = aWindow.contentsScaleFactor;
|
|
|
|
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)));
|
|
NS_ASSERTION(mPluginWndProc != PluginWindowProc, "WTF?");
|
|
}
|
|
RemoveProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty);
|
|
HookSetWindowLongPtr();
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Bad plugin window type.");
|
|
return IPC_FAIL_NO_REASON(this);
|
|
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;
|
|
mContentsScaleFactor = aWindow.contentsScaleFactor;
|
|
|
|
if (mShContext) {
|
|
// Release the shared context so that it is reallocated
|
|
// with the new size.
|
|
::CGContextRelease(mShContext);
|
|
mShContext = nullptr;
|
|
}
|
|
|
|
if (mPluginIface->setwindow) (void)mPluginIface->setwindow(&mData, &mWindow);
|
|
|
|
#elif defined(ANDROID)
|
|
// TODO: Need Android impl
|
|
#elif defined(MOZ_WIDGET_UIKIT) || defined(MOZ_WAYLAND)
|
|
// Don't care
|
|
#else
|
|
# error Implement me for your OS
|
|
#endif
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool PluginInstanceChild::Initialize() {
|
|
#if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
|
|
if (mWsInfo.display) {
|
|
// Already initialized
|
|
return true;
|
|
}
|
|
|
|
// Request for windowless plugins is set in newp(), before this call.
|
|
if (mWindow.type == NPWindowTypeWindow) {
|
|
return false;
|
|
}
|
|
|
|
mWsInfo.display = DefaultXDisplay();
|
|
#endif
|
|
|
|
#if defined(XP_MACOSX) && defined(__i386__)
|
|
// If an i386 Mac OS X plugin has selected the Carbon event model then
|
|
// we have to fail. We do not support putting Carbon event model plugins
|
|
// out of process. Note that Carbon is the default model so out of process
|
|
// plugins need to actively negotiate something else in order to work
|
|
// out of process.
|
|
if (EventModel() == NPEventModelCarbon) {
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
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(nullptr);
|
|
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);
|
|
}
|
|
|
|
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, nullptr, 0, GetModuleHandle(nullptr), 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::SizePluginWindow(int width, int height) {
|
|
if (mPluginWindowHWND) {
|
|
mPluginSize.x = width;
|
|
mPluginSize.y = height;
|
|
SetWindowPos(mPluginWindowHWND, nullptr, 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) {
|
|
return mozilla::CallWindowProcCrashProtected(PluginWindowProcInternal, hWnd,
|
|
message, wParam, lParam);
|
|
}
|
|
|
|
// static
|
|
LRESULT CALLBACK PluginInstanceChild::PluginWindowProcInternal(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam) {
|
|
NS_ASSERTION(!mozilla::ipc::MessageChannel::IsPumpingMessages(),
|
|
"Failed to prevent a nonqueued message from running!");
|
|
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
|
|
GetProp(hWnd, kPluginInstanceChildProperty));
|
|
if (!self) {
|
|
MOZ_ASSERT_UNREACHABLE("Badness!");
|
|
return 0;
|
|
}
|
|
|
|
NS_ASSERTION(self->mPluginWindowHWND == hWnd, "Wrong window!");
|
|
NS_ASSERTION(
|
|
self->mPluginWndProc != PluginWindowProc,
|
|
"Self-referential windowproc. Infinite recursion will happen soon.");
|
|
|
|
bool isIMECompositionMessage = false;
|
|
switch (message) {
|
|
// 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.
|
|
case 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;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_IME_STARTCOMPOSITION:
|
|
isIMECompositionMessage = true;
|
|
break;
|
|
case WM_IME_ENDCOMPOSITION:
|
|
isIMECompositionMessage = true;
|
|
break;
|
|
case WM_IME_COMPOSITION:
|
|
isIMECompositionMessage = true;
|
|
// XXX Some old IME may not send WM_IME_COMPOSITION_START or
|
|
// WM_IME_COMPSOITION_END properly. So, we need to check
|
|
// WM_IME_COMPSOITION and if it includes commit string.
|
|
break;
|
|
|
|
// The plugin received keyboard focus, let the parent know so the dom
|
|
// is up to date.
|
|
case WM_MOUSEACTIVATE:
|
|
self->CallPluginFocusChange(true);
|
|
break;
|
|
}
|
|
|
|
// 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(nullptr) & (ISMEX_REPLIED | ISMEX_SEND)) == ISMEX_SEND) {
|
|
switch (message) {
|
|
case WM_CHILDACTIVATE:
|
|
case WM_KILLFOCUS:
|
|
ReplyMessage(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (message == WM_KILLFOCUS) {
|
|
self->CallPluginFocusChange(false);
|
|
}
|
|
|
|
if (message == WM_USER + 1 &&
|
|
(self->GetQuirks() & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)) {
|
|
self->FlashThrottleMessage(hWnd, message, wParam, lParam, true);
|
|
return 0;
|
|
}
|
|
|
|
NS_ASSERTION(self->mPluginWndProc != PluginWindowProc,
|
|
"Self-referential windowproc happened inside our hook proc. "
|
|
"Infinite recursion will happen soon.");
|
|
|
|
LRESULT res =
|
|
CallWindowProc(self->mPluginWndProc, hWnd, message, wParam, lParam);
|
|
|
|
// 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.
|
|
if (message == WM_LBUTTONDOWN &&
|
|
self->GetQuirks() & QUIRK_FLASH_FIXUP_MOUSE_CAPTURE) {
|
|
wchar_t szClass[26];
|
|
HWND hwnd = GetForegroundWindow();
|
|
if (hwnd &&
|
|
GetClassNameW(hwnd, szClass, sizeof(szClass) / sizeof(char16_t)) &&
|
|
!wcscmp(szClass, kFlashFullscreenClass)) {
|
|
ReleaseCapture();
|
|
SetFocus(hwnd);
|
|
}
|
|
}
|
|
|
|
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 WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrA>
|
|
sUser32SetWindowLongAHookStub;
|
|
static WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrW>
|
|
sUser32SetWindowLongWHookStub;
|
|
# else
|
|
typedef LONG(WINAPI* User32SetWindowLongA)(HWND hWnd, int nIndex,
|
|
LONG dwNewLong);
|
|
typedef LONG(WINAPI* User32SetWindowLongW)(HWND hWnd, int nIndex,
|
|
LONG dwNewLong);
|
|
static WindowsDllInterceptor::FuncHookType<User32SetWindowLongA>
|
|
sUser32SetWindowLongAHookStub;
|
|
static WindowsDllInterceptor::FuncHookType<User32SetWindowLongW>
|
|
sUser32SetWindowLongWHookStub;
|
|
# endif
|
|
|
|
extern LRESULT CALLBACK NeuteredWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|
LPARAM lParam);
|
|
|
|
const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
|
|
|
|
// static
|
|
bool 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 true;
|
|
// prevent the subclass
|
|
return 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.
|
|
WNDPROC currentProc =
|
|
reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
|
|
if (currentProc != PluginWindowProc) {
|
|
self->mPluginWndProc =
|
|
reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(
|
|
hWnd, nIndex, reinterpret_cast<LONG_PTR>(PluginWindowProc)));
|
|
NS_ASSERTION(self->mPluginWndProc != PluginWindowProc,
|
|
"Infinite recursion coming up!");
|
|
}
|
|
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.
|
|
WNDPROC currentProc =
|
|
reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
|
|
if (currentProc != PluginWindowProc) {
|
|
self->mPluginWndProc =
|
|
reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(
|
|
hWnd, nIndex, reinterpret_cast<LONG_PTR>(PluginWindowProc)));
|
|
NS_ASSERTION(self->mPluginWndProc != PluginWindowProc,
|
|
"Infinite recursion coming up!");
|
|
}
|
|
return proc;
|
|
}
|
|
|
|
void PluginInstanceChild::HookSetWindowLongPtr() {
|
|
if (!(GetQuirks() & QUIRK_FLASH_HOOK_SETLONGPTR)) {
|
|
return;
|
|
}
|
|
|
|
sUser32Intercept.Init("user32.dll");
|
|
# ifdef _WIN64
|
|
sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongPtrA",
|
|
&SetWindowLongPtrAHook);
|
|
sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongPtrW",
|
|
&SetWindowLongPtrWHook);
|
|
# else
|
|
sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongA",
|
|
&SetWindowLongAHook);
|
|
sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongW",
|
|
&SetWindowLongWHook);
|
|
# 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.
|
|
wchar_t szClass[21];
|
|
bool haveClass = GetClassNameW(hWnd, szClass, ArrayLength(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 = nullptr;
|
|
|
|
// 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);
|
|
|
|
DWORD res = sUser32TrackPopupMenuStub(hMenu, uFlags | TPM_RETURNCMD, x, y,
|
|
nReserved, surrogateHwnd, prcRect);
|
|
|
|
if (!isRetCmdCall && res) {
|
|
SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(res, 0), 0);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void PluginInstanceChild::InitPopupMenuHook() {
|
|
if (!(GetQuirks() & QUIRK_WINLESS_TRACKPOPUP_HOOK)) {
|
|
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");
|
|
sUser32TrackPopupMenuStub.Set(sUser32Intercept, "TrackPopupMenu",
|
|
&TrackPopupHookProc);
|
|
}
|
|
|
|
void PluginInstanceChild::CreateWinlessPopupSurrogate() {
|
|
// already initialized
|
|
if (mWinlessPopupSurrogateHWND) return;
|
|
|
|
mWinlessPopupSurrogateHWND =
|
|
CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", nullptr, WS_POPUP, 0, 0,
|
|
0, 0, nullptr, 0, GetModuleHandle(nullptr), 0);
|
|
if (!mWinlessPopupSurrogateHWND) {
|
|
NS_ERROR("CreateWindowEx failed for winless placeholder!");
|
|
return;
|
|
}
|
|
|
|
SendSetNetscapeWindowAsParent(mWinlessPopupSurrogateHWND);
|
|
}
|
|
|
|
// static
|
|
HIMC PluginInstanceChild::ImmGetContextProc(HWND aWND) {
|
|
if (!sCurrentPluginInstance) {
|
|
return sImm32ImmGetContextStub(aWND);
|
|
}
|
|
|
|
wchar_t szClass[21];
|
|
int haveClass = GetClassNameW(aWND, szClass, ArrayLength(szClass));
|
|
if (!haveClass || wcscmp(szClass, L"SWFlash_PlaceholderX")) {
|
|
NS_WARNING("We cannot recongnize hooked window class");
|
|
return sImm32ImmGetContextStub(aWND);
|
|
}
|
|
|
|
return sHookIMC;
|
|
}
|
|
|
|
// static
|
|
LONG PluginInstanceChild::ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex,
|
|
LPVOID aBuf, DWORD aLen) {
|
|
if (aIMC != sHookIMC) {
|
|
return sImm32ImmGetCompositionStringStub(aIMC, aIndex, aBuf, aLen);
|
|
}
|
|
if (!sCurrentPluginInstance) {
|
|
return IMM_ERROR_GENERAL;
|
|
}
|
|
AutoTArray<uint8_t, 16> dist;
|
|
int32_t length = 0; // IMM_ERROR_NODATA
|
|
sCurrentPluginInstance->SendGetCompositionString(aIndex, &dist, &length);
|
|
if (length == IMM_ERROR_NODATA || length == IMM_ERROR_GENERAL) {
|
|
return length;
|
|
}
|
|
|
|
if (aBuf && aLen >= static_cast<DWORD>(length)) {
|
|
memcpy(aBuf, dist.Elements(), length);
|
|
}
|
|
return length;
|
|
}
|
|
|
|
// staitc
|
|
BOOL PluginInstanceChild::ImmSetCandidateWindowProc(HIMC aIMC,
|
|
LPCANDIDATEFORM aForm) {
|
|
return FALSE;
|
|
}
|
|
|
|
// static
|
|
BOOL PluginInstanceChild::ImmNotifyIME(HIMC aIMC, DWORD aAction, DWORD aIndex,
|
|
DWORD aValue) {
|
|
if (aIMC != sHookIMC) {
|
|
return sImm32ImmNotifyIME(aIMC, aAction, aIndex, aValue);
|
|
}
|
|
|
|
// We only supports NI_COMPOSITIONSTR because Flash uses it only
|
|
if (!sCurrentPluginInstance || aAction != NI_COMPOSITIONSTR ||
|
|
(aIndex != CPS_COMPLETE && aIndex != CPS_CANCEL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
sCurrentPluginInstance->SendRequestCommitOrCancel(aAction == CPS_COMPLETE);
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
BOOL PluginInstanceChild::ImmAssociateContextExProc(HWND hWND, HIMC hImc,
|
|
DWORD dwFlags) {
|
|
PluginInstanceChild* self = sCurrentPluginInstance;
|
|
if (!self) {
|
|
// If ImmAssociateContextEx calls unexpected window message,
|
|
// we can use child instance object from window property if available.
|
|
self = reinterpret_cast<PluginInstanceChild*>(
|
|
GetProp(hWND, kFlashThrottleProperty));
|
|
NS_WARNING_ASSERTION(self, "Cannot find PluginInstanceChild");
|
|
}
|
|
|
|
// HIMC is always nullptr on Flash's windowless
|
|
if (!hImc && self) {
|
|
// Store the last IME state since Flash may call ImmAssociateContextEx
|
|
// before taking focus.
|
|
self->mLastEnableIMEState = !!(dwFlags & IACE_DEFAULT);
|
|
}
|
|
return sImm32ImmAssociateContextExStub(hWND, hImc, dwFlags);
|
|
}
|
|
|
|
void PluginInstanceChild::InitImm32Hook() {
|
|
if (!(GetQuirks() & QUIRK_WINLESS_HOOK_IME)) {
|
|
return;
|
|
}
|
|
|
|
// When using windowless plugin, IMM API won't work due to OOP.
|
|
//
|
|
// ImmReleaseContext on Windows 7+ just returns TRUE only, so we don't
|
|
// need to hook this.
|
|
|
|
sImm32Intercept.Init("imm32.dll");
|
|
sImm32ImmGetContextStub.Set(sImm32Intercept, "ImmGetContext",
|
|
&ImmGetContextProc);
|
|
sImm32ImmGetCompositionStringStub.Set(sImm32Intercept,
|
|
"ImmGetCompositionStringW",
|
|
&ImmGetCompositionStringProc);
|
|
sImm32ImmSetCandidateWindowStub.Set(sImm32Intercept, "ImmSetCandidateWindow",
|
|
&ImmSetCandidateWindowProc);
|
|
sImm32ImmNotifyIME.Set(sImm32Intercept, "ImmNotifyIME", &ImmNotifyIME);
|
|
sImm32ImmAssociateContextExStub.Set(sImm32Intercept, "ImmAssociateContextEx",
|
|
&ImmAssociateContextExProc);
|
|
}
|
|
|
|
void PluginInstanceChild::DestroyWinlessPopupSurrogate() {
|
|
if (mWinlessPopupSurrogateHWND) DestroyWindow(mWinlessPopupSurrogateHWND);
|
|
mWinlessPopupSurrogateHWND = nullptr;
|
|
}
|
|
|
|
int16_t PluginInstanceChild::WinlessHandleEvent(NPEvent& event) {
|
|
if (!mPluginIface->event) return false;
|
|
|
|
// Events that might generate nested event dispatch loops need
|
|
// special handling during delivery.
|
|
int16_t handled;
|
|
|
|
HWND focusHwnd = nullptr;
|
|
|
|
// 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 ((GetQuirks() &
|
|
QUIRK_WINLESS_TRACKPOPUP_HOOK) && // XXX turn on by default?
|
|
(event.event == WM_RBUTTONDOWN || // flash
|
|
event.event == WM_RBUTTONUP)) { // silverlight
|
|
sWinlessPopupSurrogateHWND = mWinlessPopupSurrogateHWND;
|
|
|
|
// A little trick scrounged from chromium's code - set the focus
|
|
// to our surrogate parent so keyboard nav events go to the menu.
|
|
focusHwnd = SetFocus(mWinlessPopupSurrogateHWND);
|
|
}
|
|
|
|
AutoRestore<PluginInstanceChild*> pluginInstance(sCurrentPluginInstance);
|
|
if (event.event == WM_IME_STARTCOMPOSITION ||
|
|
event.event == WM_IME_COMPOSITION || event.event == WM_LBUTTONDOWN ||
|
|
event.event == WM_KILLFOCUS) {
|
|
sCurrentPluginInstance = this;
|
|
}
|
|
|
|
MessageLoop* loop = MessageLoop::current();
|
|
AutoRestore<bool> modalLoop(loop->os_modal_loop());
|
|
|
|
handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
|
|
|
|
sWinlessPopupSurrogateHWND = nullptr;
|
|
|
|
if (IsWindow(focusHwnd)) {
|
|
SetFocus(focusHwnd);
|
|
}
|
|
|
|
// This is hack of Flash's behaviour.
|
|
//
|
|
// When moving focus from chrome to plugin by mouse click, Gecko sends
|
|
// mouse message (such as WM_LBUTTONDOWN etc) at first, then sends
|
|
// WM_SETFOCUS. But Flash will call ImmAssociateContextEx on WM_LBUTTONDOWN
|
|
// even if it doesn't receive WM_SETFOCUS.
|
|
//
|
|
// In this situation, after sending mouse message, content process will be
|
|
// activated and set input context with PLUGIN. So after activating
|
|
// content process, we have to set current IME state again.
|
|
|
|
if (event.event == WM_KILLFOCUS) {
|
|
// Flash always calls ImmAssociateContextEx by taking focus.
|
|
// Although this flag doesn't have to be reset, I add it for safety.
|
|
mLastEnableIMEState = true;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
/* 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 = nullptr;
|
|
|
|
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 = nullptr;
|
|
}
|
|
|
|
// static
|
|
LRESULT CALLBACK PluginInstanceChild::WinlessHiddenFlashWndProc(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam) {
|
|
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
|
|
GetProp(hWnd, kFlashThrottleProperty));
|
|
if (!self) {
|
|
MOZ_ASSERT_UNREACHABLE("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) {
|
|
MOZ_ASSERT_UNREACHABLE("Enum befuddled!");
|
|
return FALSE;
|
|
}
|
|
|
|
wchar_t className[64];
|
|
if (!GetClassNameW(hWnd, className, sizeof(className) / sizeof(char16_t)))
|
|
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::FlashThrottleMsg::GetProc() {
|
|
if (mInstance) {
|
|
return mWindowed ? mInstance->mPluginWndProc
|
|
: mInstance->mWinlessThrottleOldWndProc;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PluginInstanceChild::FlashThrottleMsg::Run() {
|
|
if (!mInstance) {
|
|
return NS_OK;
|
|
}
|
|
|
|
mInstance->mPendingFlashThrottleMsgs.RemoveElement(this);
|
|
|
|
// GetProc() checks mInstance, and pulls the procedure from
|
|
// PluginInstanceChild. We don't transport sub-class procedure
|
|
// ptrs around in FlashThrottleMsg msgs.
|
|
if (!GetProc()) return NS_OK;
|
|
|
|
// deliver the event to flash
|
|
CallWindowProc(GetProc(), GetWnd(), GetMsg(), GetWParam(), GetLParam());
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult PluginInstanceChild::FlashThrottleMsg::Cancel() {
|
|
MOZ_ASSERT(mInstance);
|
|
mInstance = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
void PluginInstanceChild::FlashThrottleMessage(HWND aWnd, UINT aMsg,
|
|
WPARAM aWParam, LPARAM aLParam,
|
|
bool isWindowed) {
|
|
// We save a reference to the FlashThrottleMsg so we can cancel it in
|
|
// Destroy if it's still alive.
|
|
RefPtr<FlashThrottleMsg> task =
|
|
new FlashThrottleMsg(this, aWnd, aMsg, aWParam, aLParam, isWindowed);
|
|
|
|
mPendingFlashThrottleMsgs.AppendElement(task);
|
|
|
|
MessageLoop::current()->PostDelayedTask(task.forget(),
|
|
kFlashWMUSERMessageThrottleDelayMs);
|
|
}
|
|
|
|
#endif // OS_WIN
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerSetPluginFocus() {
|
|
MOZ_LOG(GetPluginLog(), LogLevel::Debug, ("%s", FULLFUNCTION));
|
|
|
|
#if defined(OS_WIN)
|
|
// Parent is letting us know the dom set focus to the plugin. Note,
|
|
// focus can change during transit in certain edge cases, for example
|
|
// when a button click brings up a full screen window. Since we send
|
|
// this in response to a WM_SETFOCUS event on our parent, the parent
|
|
// should have focus when we receive this. If not, ignore the call.
|
|
if (::GetFocus() == mPluginWindowHWND) return IPC_OK();
|
|
::SetFocus(mPluginWindowHWND);
|
|
return IPC_OK();
|
|
#else
|
|
MOZ_ASSERT_UNREACHABLE("AnswerSetPluginFocus not implemented!");
|
|
return IPC_FAIL_NO_REASON(this);
|
|
#endif
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerUpdateWindow() {
|
|
MOZ_LOG(GetPluginLog(), LogLevel::Debug, ("%s", FULLFUNCTION));
|
|
|
|
#if defined(OS_WIN)
|
|
if (mPluginWindowHWND) {
|
|
RECT rect;
|
|
if (GetUpdateRect(GetParent(mPluginWindowHWND), &rect, FALSE)) {
|
|
::InvalidateRect(mPluginWindowHWND, &rect, FALSE);
|
|
}
|
|
UpdateWindow(mPluginWindowHWND);
|
|
}
|
|
return IPC_OK();
|
|
#else
|
|
MOZ_ASSERT_UNREACHABLE("AnswerUpdateWindow not implemented!");
|
|
return IPC_FAIL_NO_REASON(this);
|
|
#endif
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::RecvNPP_DidComposite() {
|
|
if (mPluginIface->didComposite) {
|
|
mPluginIface->didComposite(GetNPP());
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
PPluginScriptableObjectChild*
|
|
PluginInstanceChild::AllocPPluginScriptableObjectChild() {
|
|
AssertPluginThread();
|
|
return new PluginScriptableObjectChild(Proxy);
|
|
}
|
|
|
|
bool PluginInstanceChild::DeallocPPluginScriptableObjectChild(
|
|
PPluginScriptableObjectChild* aObject) {
|
|
AssertPluginThread();
|
|
delete aObject;
|
|
return true;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
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 IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::RecvPBrowserStreamConstructor(
|
|
PBrowserStreamChild* aActor, const nsCString& url, const uint32_t& length,
|
|
const uint32_t& lastmodified, PStreamNotifyChild* notifyData,
|
|
const nsCString& headers) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
NPError PluginInstanceChild::DoNPP_NewStream(BrowserStreamChild* actor,
|
|
const nsCString& mimeType,
|
|
const bool& seekable,
|
|
uint16_t* stype) {
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
NPError rv = actor->StreamConstructed(mimeType, seekable, stype);
|
|
return rv;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_NewStream(
|
|
PBrowserStreamChild* actor, const nsCString& mimeType, const bool& seekable,
|
|
NPError* rv, uint16_t* stype) {
|
|
*rv = DoNPP_NewStream(static_cast<BrowserStreamChild*>(actor), mimeType,
|
|
seekable, stype);
|
|
return IPC_OK();
|
|
}
|
|
|
|
PBrowserStreamChild* PluginInstanceChild::AllocPBrowserStreamChild(
|
|
const nsCString& url, const uint32_t& length, const uint32_t& lastmodified,
|
|
PStreamNotifyChild* notifyData, const nsCString& headers) {
|
|
AssertPluginThread();
|
|
return new BrowserStreamChild(this, url, length, lastmodified,
|
|
static_cast<StreamNotifyChild*>(notifyData),
|
|
headers);
|
|
}
|
|
|
|
bool PluginInstanceChild::DeallocPBrowserStreamChild(
|
|
PBrowserStreamChild* stream) {
|
|
AssertPluginThread();
|
|
delete stream;
|
|
return true;
|
|
}
|
|
|
|
PStreamNotifyChild* PluginInstanceChild::AllocPStreamNotifyChild(
|
|
const nsCString& url, const nsCString& target, const bool& post,
|
|
const nsCString& buffer, const bool& file, NPError* result) {
|
|
AssertPluginThread();
|
|
MOZ_CRASH("not reached");
|
|
return nullptr;
|
|
}
|
|
|
|
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 = nullptr;
|
|
mBrowserStream = nullptr;
|
|
}
|
|
}
|
|
|
|
void StreamNotifyChild::SetAssociatedStream(BrowserStreamChild* bs) {
|
|
NS_ASSERTION(!mBrowserStream, "Two streams for one streamnotify?");
|
|
|
|
mBrowserStream = bs;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult StreamNotifyChild::Recv__delete__(
|
|
const NPReason& reason) {
|
|
AssertPluginThread();
|
|
|
|
if (mBrowserStream)
|
|
mBrowserStream->NotifyPending();
|
|
else
|
|
NPP_URLNotify(reason);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult StreamNotifyChild::RecvRedirectNotify(
|
|
const nsCString& url, const int32_t& status) {
|
|
// NPP_URLRedirectNotify requires a non-null closure. Since core logic
|
|
// assumes that all out-of-process notify streams have non-null closure
|
|
// data it will assume that the plugin was notified at this point and
|
|
// expect a response otherwise the redirect will hang indefinitely.
|
|
if (!mClosure) {
|
|
SendRedirectNotifyResponse(false);
|
|
}
|
|
|
|
PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(Manager());
|
|
if (instance->mPluginIface->urlredirectnotify)
|
|
instance->mPluginIface->urlredirectnotify(instance->GetNPP(), url.get(),
|
|
status, mClosure);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
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::DeallocPStreamNotifyChild(
|
|
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 =
|
|
PluginScriptableObjectChild::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 nullptr;
|
|
}
|
|
|
|
actor->InitializeLocal(aObject);
|
|
return actor;
|
|
}
|
|
|
|
void PluginInstanceChild::NPN_URLRedirectResponse(void* notifyData,
|
|
NPBool allow) {
|
|
if (!notifyData) {
|
|
return;
|
|
}
|
|
|
|
nsTArray<PStreamNotifyChild*> notifyStreams;
|
|
ManagedPStreamNotifyChild(notifyStreams);
|
|
uint32_t notifyStreamCount = notifyStreams.Length();
|
|
for (uint32_t i = 0; i < notifyStreamCount; i++) {
|
|
StreamNotifyChild* sn = static_cast<StreamNotifyChild*>(notifyStreams[i]);
|
|
if (sn->mClosure == notifyData) {
|
|
sn->SendRedirectNotifyResponse(static_cast<bool>(allow));
|
|
return;
|
|
}
|
|
}
|
|
NS_ASSERTION(false, "Couldn't find stream for redirect response!");
|
|
}
|
|
|
|
bool PluginInstanceChild::IsUsingDirectDrawing() {
|
|
return IsDrawingModelDirect(mDrawingModel);
|
|
}
|
|
|
|
PluginInstanceChild::DirectBitmap::DirectBitmap(PluginInstanceChild* aOwner,
|
|
const Shmem& shmem,
|
|
const IntSize& size,
|
|
uint32_t stride,
|
|
SurfaceFormat format)
|
|
: mOwner(aOwner),
|
|
mShmem(shmem),
|
|
mFormat(format),
|
|
mSize(size),
|
|
mStride(stride) {}
|
|
|
|
PluginInstanceChild::DirectBitmap::~DirectBitmap() {
|
|
mOwner->DeallocShmem(mShmem);
|
|
}
|
|
|
|
static inline SurfaceFormat NPImageFormatToSurfaceFormat(
|
|
NPImageFormat aFormat) {
|
|
switch (aFormat) {
|
|
case NPImageFormatBGRA32:
|
|
return SurfaceFormat::B8G8R8A8;
|
|
case NPImageFormatBGRX32:
|
|
return SurfaceFormat::B8G8R8X8;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("unknown NPImageFormat");
|
|
return SurfaceFormat::UNKNOWN;
|
|
}
|
|
}
|
|
|
|
static inline gfx::IntRect NPRectToIntRect(const NPRect& in) {
|
|
return IntRect(in.left, in.top, in.right - in.left, in.bottom - in.top);
|
|
}
|
|
|
|
NPError PluginInstanceChild::NPN_InitAsyncSurface(NPSize* size,
|
|
NPImageFormat format,
|
|
void* initData,
|
|
NPAsyncSurface* surface) {
|
|
AssertPluginThread();
|
|
AutoStackHelper guard(this);
|
|
|
|
if (!IsUsingDirectDrawing()) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
if (format != NPImageFormatBGRA32 && format != NPImageFormatBGRX32) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
PodZero(surface);
|
|
|
|
// NPAPI guarantees that the SetCurrentAsyncSurface call will release the
|
|
// previous surface if it was different. However, no functionality exists
|
|
// within content to synchronize a non-shadow-layers transaction with the
|
|
// compositor.
|
|
//
|
|
// To get around this, we allocate two surfaces: a child copy, which we
|
|
// hand off to the plugin, and a parent copy, which we will hand off to
|
|
// the compositor. Each call to SetCurrentAsyncSurface will copy the
|
|
// invalid region from the child surface to its parent.
|
|
switch (mDrawingModel) {
|
|
case NPDrawingModelAsyncBitmapSurface: {
|
|
// Validate that the caller does not expect initial data to be set.
|
|
if (initData) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
// Validate that we're not double-allocating a surface.
|
|
RefPtr<DirectBitmap> holder;
|
|
if (mDirectBitmaps.Get(surface, getter_AddRefs(holder))) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
SurfaceFormat mozformat = NPImageFormatToSurfaceFormat(format);
|
|
int32_t bytesPerPixel = BytesPerPixel(mozformat);
|
|
|
|
if (size->width <= 0 || size->height <= 0) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
CheckedInt<uint32_t> nbytes =
|
|
SafeBytesForBitmap(size->width, size->height, bytesPerPixel);
|
|
if (!nbytes.isValid()) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
Shmem shmem;
|
|
if (!AllocUnsafeShmem(nbytes.value(), SharedMemory::TYPE_BASIC, &shmem)) {
|
|
return NPERR_OUT_OF_MEMORY_ERROR;
|
|
}
|
|
MOZ_ASSERT(shmem.Size<uint8_t>() == nbytes.value());
|
|
|
|
surface->version = 0;
|
|
surface->size = *size;
|
|
surface->format = format;
|
|
surface->bitmap.data = shmem.get<unsigned char>();
|
|
surface->bitmap.stride = size->width * bytesPerPixel;
|
|
|
|
// Hold the shmem alive until Finalize() is called or this actor dies.
|
|
holder = new DirectBitmap(this, shmem, IntSize(size->width, size->height),
|
|
surface->bitmap.stride, mozformat);
|
|
mDirectBitmaps.InsertOrUpdate(surface, std::move(holder));
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
#if defined(XP_WIN)
|
|
case NPDrawingModelAsyncWindowsDXGISurface: {
|
|
// Validate that the caller does not expect initial data to be set.
|
|
if (initData) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
// Validate that we're not double-allocating a surface.
|
|
WindowsHandle handle = 0;
|
|
if (mDxgiSurfaces.Get(surface, &handle)) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
NPError error = NPERR_NO_ERROR;
|
|
SurfaceFormat mozformat = NPImageFormatToSurfaceFormat(format);
|
|
if (!SendInitDXGISurface(mozformat, IntSize(size->width, size->height),
|
|
&handle, &error)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
if (error != NPERR_NO_ERROR) {
|
|
return error;
|
|
}
|
|
|
|
surface->version = 0;
|
|
surface->size = *size;
|
|
surface->format = format;
|
|
surface->sharedHandle = reinterpret_cast<HANDLE>(handle);
|
|
|
|
mDxgiSurfaces.InsertOrUpdate(surface, handle);
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
#endif
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("unknown drawing model");
|
|
}
|
|
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
NPError PluginInstanceChild::NPN_FinalizeAsyncSurface(NPAsyncSurface* surface) {
|
|
AssertPluginThread();
|
|
|
|
if (!IsUsingDirectDrawing()) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
|
|
switch (mDrawingModel) {
|
|
case NPDrawingModelAsyncBitmapSurface: {
|
|
RefPtr<DirectBitmap> bitmap;
|
|
if (!mDirectBitmaps.Get(surface, getter_AddRefs(bitmap))) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
PodZero(surface);
|
|
mDirectBitmaps.Remove(surface);
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
#if defined(XP_WIN)
|
|
case NPDrawingModelAsyncWindowsDXGISurface: {
|
|
WindowsHandle handle;
|
|
if (!mDxgiSurfaces.Get(surface, &handle)) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
SendFinalizeDXGISurface(handle);
|
|
mDxgiSurfaces.Remove(surface);
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
#endif
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("unknown drawing model");
|
|
}
|
|
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
void PluginInstanceChild::NPN_SetCurrentAsyncSurface(NPAsyncSurface* surface,
|
|
NPRect* changed) {
|
|
AssertPluginThread();
|
|
|
|
if (!IsUsingDirectDrawing()) {
|
|
return;
|
|
}
|
|
|
|
mCurrentDirectSurface = surface;
|
|
|
|
if (!surface) {
|
|
SendRevokeCurrentDirectSurface();
|
|
return;
|
|
}
|
|
|
|
switch (mDrawingModel) {
|
|
case NPDrawingModelAsyncBitmapSurface: {
|
|
RefPtr<DirectBitmap> bitmap;
|
|
if (!mDirectBitmaps.Get(surface, getter_AddRefs(bitmap))) {
|
|
return;
|
|
}
|
|
|
|
IntRect dirty = changed ? NPRectToIntRect(*changed)
|
|
: IntRect(IntPoint(0, 0), bitmap->mSize);
|
|
|
|
// Need a holder since IPDL zaps the object for mysterious reasons.
|
|
Shmem shmemHolder = bitmap->mShmem;
|
|
SendShowDirectBitmap(std::move(shmemHolder), bitmap->mFormat,
|
|
bitmap->mStride, bitmap->mSize, dirty);
|
|
break;
|
|
}
|
|
#if defined(XP_WIN)
|
|
case NPDrawingModelAsyncWindowsDXGISurface: {
|
|
WindowsHandle handle;
|
|
if (!mDxgiSurfaces.Get(surface, &handle)) {
|
|
return;
|
|
}
|
|
|
|
IntRect dirty =
|
|
changed ? NPRectToIntRect(*changed)
|
|
: IntRect(IntPoint(0, 0),
|
|
IntSize(surface->size.width, surface->size.height));
|
|
|
|
SendShowDirectDXGISurface(handle, dirty);
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("unknown drawing model");
|
|
}
|
|
}
|
|
|
|
void PluginInstanceChild::DoAsyncRedraw() {
|
|
{
|
|
MutexAutoLock autoLock(mAsyncInvalidateMutex);
|
|
mAsyncInvalidateTask = nullptr;
|
|
}
|
|
|
|
SendRedrawPlugin();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::RecvAsyncSetWindow(
|
|
const gfxSurfaceType& aSurfaceType, const NPRemoteWindow& aWindow) {
|
|
AssertPluginThread();
|
|
|
|
AutoStackHelper guard(this);
|
|
NS_ASSERTION(!aWindow.window, "Remote window should be null.");
|
|
|
|
if (mCurrentAsyncSetWindowTask) {
|
|
mCurrentAsyncSetWindowTask->Cancel();
|
|
mCurrentAsyncSetWindowTask = nullptr;
|
|
}
|
|
|
|
// We shouldn't process this now because it may be received within a nested
|
|
// RPC call, and both Flash and Java don't expect to receive setwindow calls
|
|
// at arbitrary times.
|
|
mCurrentAsyncSetWindowTask =
|
|
NewNonOwningCancelableRunnableMethod<gfxSurfaceType, NPRemoteWindow,
|
|
bool>(
|
|
"plugins::PluginInstanceChild::DoAsyncSetWindow", this,
|
|
&PluginInstanceChild::DoAsyncSetWindow, aSurfaceType, aWindow, true);
|
|
RefPtr<Runnable> addrefedTask = mCurrentAsyncSetWindowTask;
|
|
MessageLoop::current()->PostTask(addrefedTask.forget());
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
void PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
|
|
const NPRemoteWindow& aWindow,
|
|
bool aIsAsync) {
|
|
PLUGIN_LOG_DEBUG(
|
|
("[InstanceChild][%p] AsyncSetWindow to <x=%d,y=%d, w=%d,h=%d>", this,
|
|
aWindow.x, aWindow.y, aWindow.width, aWindow.height));
|
|
|
|
AssertPluginThread();
|
|
NS_ASSERTION(!aWindow.window, "Remote window should be null.");
|
|
NS_ASSERTION(!mPendingPluginCall, "Can't do SetWindow during plugin call!");
|
|
|
|
if (aIsAsync) {
|
|
if (!mCurrentAsyncSetWindowTask) {
|
|
return;
|
|
}
|
|
mCurrentAsyncSetWindowTask = nullptr;
|
|
}
|
|
|
|
mWindow.window = nullptr;
|
|
if (mWindow.width != aWindow.width || mWindow.height != aWindow.height ||
|
|
mWindow.clipRect.top != aWindow.clipRect.top ||
|
|
mWindow.clipRect.left != aWindow.clipRect.left ||
|
|
mWindow.clipRect.bottom != aWindow.clipRect.bottom ||
|
|
mWindow.clipRect.right != aWindow.clipRect.right)
|
|
mAccumulatedInvalidRect = nsIntRect(0, 0, aWindow.width, aWindow.height);
|
|
|
|
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 defined(XP_MACOSX) || defined(XP_WIN)
|
|
mContentsScaleFactor = aWindow.contentsScaleFactor;
|
|
#endif
|
|
|
|
mLayersRendering = true;
|
|
mSurfaceType = aSurfaceType;
|
|
UpdateWindowAttributes(true);
|
|
|
|
#ifdef XP_WIN
|
|
if (GetQuirks() & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS) SetupFlashMsgThrottle();
|
|
#endif
|
|
|
|
if (!mAccumulatedInvalidRect.IsEmpty()) {
|
|
AsyncShowPluginFrame();
|
|
}
|
|
}
|
|
|
|
bool PluginInstanceChild::CreateOptSurface(void) {
|
|
MOZ_ASSERT(mSurfaceType != gfxSurfaceType::Max,
|
|
"Need a valid surface type here");
|
|
NS_ASSERTION(!mCurrentSurface, "mCurrentSurfaceActor can get out of sync.");
|
|
|
|
// Use an opaque surface unless we're transparent and *don't* have
|
|
// a background to source from.
|
|
gfxImageFormat format = (mIsTransparent && !mBackground)
|
|
? SurfaceFormat::A8R8G8B8_UINT32
|
|
: SurfaceFormat::X8R8G8B8_UINT32;
|
|
|
|
#ifdef MOZ_X11
|
|
Display* dpy = mWsInfo.display;
|
|
Screen* screen = DefaultScreenOfDisplay(dpy);
|
|
if (format == SurfaceFormat::X8R8G8B8_UINT32 &&
|
|
DefaultDepth(dpy, DefaultScreen(dpy)) == 16) {
|
|
format = SurfaceFormat::R5G6B5_UINT16;
|
|
}
|
|
|
|
if (mSurfaceType == gfxSurfaceType::Xlib) {
|
|
if (!mIsTransparent || mBackground) {
|
|
Visual* defaultVisual = DefaultVisualOfScreen(screen);
|
|
mCurrentSurface = gfxXlibSurface::Create(
|
|
screen, defaultVisual, IntSize(mWindow.width, mWindow.height));
|
|
return mCurrentSurface != nullptr;
|
|
}
|
|
|
|
XRenderPictFormat* xfmt =
|
|
XRenderFindStandardFormat(dpy, PictStandardARGB32);
|
|
if (!xfmt) {
|
|
NS_ERROR("Need X falback surface, but FindRenderFormat failed");
|
|
return false;
|
|
}
|
|
mCurrentSurface = gfxXlibSurface::Create(
|
|
screen, xfmt, IntSize(mWindow.width, mWindow.height));
|
|
return mCurrentSurface != nullptr;
|
|
}
|
|
#endif
|
|
|
|
#ifdef XP_WIN
|
|
if (mSurfaceType == gfxSurfaceType::Win32) {
|
|
bool willHaveTransparentPixels = mIsTransparent && !mBackground;
|
|
|
|
SharedDIBSurface* s = new SharedDIBSurface();
|
|
if (!s->Create(reinterpret_cast<HDC>(mWindow.window), mWindow.width,
|
|
mWindow.height, willHaveTransparentPixels))
|
|
return false;
|
|
|
|
mCurrentSurface = s;
|
|
return true;
|
|
}
|
|
|
|
MOZ_CRASH("Shared-memory drawing not expected on Windows.");
|
|
#endif
|
|
|
|
// Make common shmem implementation working for any platform
|
|
mCurrentSurface = gfxSharedImageSurface::CreateUnsafe(
|
|
this, IntSize(mWindow.width, mWindow.height), format);
|
|
return !!mCurrentSurface;
|
|
}
|
|
|
|
bool PluginInstanceChild::MaybeCreatePlatformHelperSurface(void) {
|
|
if (!mCurrentSurface) {
|
|
NS_ERROR("Cannot create helper surface without mCurrentSurface");
|
|
return false;
|
|
}
|
|
|
|
#ifdef MOZ_X11
|
|
bool supportNonDefaultVisual = false;
|
|
Screen* screen = DefaultScreenOfDisplay(mWsInfo.display);
|
|
Visual* defaultVisual = DefaultVisualOfScreen(screen);
|
|
Visual* visual = nullptr;
|
|
Colormap colormap = 0;
|
|
mDoAlphaExtraction = false;
|
|
bool createHelperSurface = false;
|
|
|
|
if (mCurrentSurface->GetType() == gfxSurfaceType::Xlib) {
|
|
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() == gfxSurfaceType::Image) {
|
|
// 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;
|
|
}
|
|
}
|
|
#elif defined(XP_WIN)
|
|
mDoAlphaExtraction = mIsTransparent && !mBackground;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PluginInstanceChild::EnsureCurrentBuffer(void) {
|
|
#ifndef XP_DARWIN
|
|
nsIntRect toInvalidate(0, 0, 0, 0);
|
|
IntSize winSize = IntSize(mWindow.width, mWindow.height);
|
|
|
|
if (mBackground && mBackground->GetSize() != winSize) {
|
|
// It would be nice to keep the old background here, but doing
|
|
// so can lead to cases in which we permanently keep the old
|
|
// background size.
|
|
mBackground = nullptr;
|
|
toInvalidate.UnionRect(toInvalidate,
|
|
nsIntRect(0, 0, winSize.width, winSize.height));
|
|
}
|
|
|
|
if (mCurrentSurface) {
|
|
IntSize surfSize = mCurrentSurface->GetSize();
|
|
if (winSize != surfSize || (mBackground && !CanPaintOnBackground()) ||
|
|
(mBackground &&
|
|
gfxContentType::COLOR != mCurrentSurface->GetContentType()) ||
|
|
(!mBackground && mIsTransparent &&
|
|
gfxContentType::COLOR == mCurrentSurface->GetContentType())) {
|
|
// Don't try to use an old, invalid DC.
|
|
mWindow.window = nullptr;
|
|
ClearCurrentSurface();
|
|
toInvalidate.UnionRect(toInvalidate,
|
|
nsIntRect(0, 0, winSize.width, winSize.height));
|
|
}
|
|
}
|
|
|
|
mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect, toInvalidate);
|
|
|
|
if (mCurrentSurface) {
|
|
return true;
|
|
}
|
|
|
|
if (!CreateOptSurface()) {
|
|
NS_ERROR("Cannot create optimized surface");
|
|
return false;
|
|
}
|
|
|
|
if (!MaybeCreatePlatformHelperSurface()) {
|
|
NS_ERROR("Cannot create helper surface");
|
|
return false;
|
|
}
|
|
#elif defined(XP_MACOSX)
|
|
|
|
if (!mDoubleBufferCARenderer.HasCALayer()) {
|
|
void* caLayer = nullptr;
|
|
if (mDrawingModel == NPDrawingModelCoreGraphics) {
|
|
if (!mCGLayer) {
|
|
caLayer = mozilla::plugins::PluginUtilsOSX::GetCGLayer(
|
|
CallCGDraw, this, mContentsScaleFactor);
|
|
|
|
if (!caLayer) {
|
|
PLUGIN_LOG_DEBUG(("GetCGLayer failed."));
|
|
return false;
|
|
}
|
|
}
|
|
mCGLayer = caLayer;
|
|
} else {
|
|
NPError result = mPluginIface->getvalue(
|
|
GetNPP(), NPPVpluginCoreAnimationLayer, &caLayer);
|
|
if (result != NPERR_NO_ERROR || !caLayer) {
|
|
PLUGIN_LOG_DEBUG(
|
|
("Plugin requested CoreAnimation but did not "
|
|
"provide CALayer."));
|
|
return false;
|
|
}
|
|
}
|
|
mDoubleBufferCARenderer.SetCALayer(caLayer);
|
|
}
|
|
|
|
if (mDoubleBufferCARenderer.HasFrontSurface() &&
|
|
(mDoubleBufferCARenderer.GetFrontSurfaceWidth() != mWindow.width ||
|
|
mDoubleBufferCARenderer.GetFrontSurfaceHeight() != mWindow.height ||
|
|
mDoubleBufferCARenderer.GetContentsScaleFactor() !=
|
|
mContentsScaleFactor)) {
|
|
mDoubleBufferCARenderer.ClearFrontSurface();
|
|
}
|
|
|
|
if (!mDoubleBufferCARenderer.HasFrontSurface()) {
|
|
bool allocSurface = mDoubleBufferCARenderer.InitFrontSurface(
|
|
mWindow.width, mWindow.height, mContentsScaleFactor,
|
|
GetQuirks() & QUIRK_ALLOW_OFFLINE_RENDERER ? ALLOW_OFFLINE_RENDERER
|
|
: DISALLOW_OFFLINE_RENDERER);
|
|
if (!allocSurface) {
|
|
PLUGIN_LOG_DEBUG(("Fail to allocate front IOSurface"));
|
|
return false;
|
|
}
|
|
|
|
if (mPluginIface->setwindow)
|
|
(void)mPluginIface->setwindow(&mData, &mWindow);
|
|
|
|
nsIntRect toInvalidate(0, 0, mWindow.width, mWindow.height);
|
|
mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect, toInvalidate);
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow) {
|
|
#if defined(MOZ_X11) || defined(XP_WIN)
|
|
RefPtr<gfxASurface> curSurface =
|
|
mHelperSurface ? mHelperSurface : mCurrentSurface;
|
|
#endif // Only used within MOZ_X11 or XP_WIN blocks. Unused variable otherwise
|
|
bool needWindowUpdate = aForceSetWindow;
|
|
#ifdef MOZ_X11
|
|
Visual* visual = nullptr;
|
|
Colormap colormap = 0;
|
|
if (curSurface && curSurface->GetType() == gfxSurfaceType::Xlib) {
|
|
static_cast<gfxXlibSurface*>(curSurface.get())
|
|
->GetColormapAndVisual(&colormap, &visual);
|
|
if (visual != mWsInfo.visual || colormap != mWsInfo.colormap) {
|
|
mWsInfo.visual = visual;
|
|
mWsInfo.colormap = colormap;
|
|
needWindowUpdate = true;
|
|
}
|
|
}
|
|
#endif // MOZ_X11
|
|
#ifdef XP_WIN
|
|
HDC dc = nullptr;
|
|
|
|
if (curSurface) {
|
|
if (!SharedDIBSurface::IsSharedDIBSurface(curSurface))
|
|
MOZ_CRASH("Expected SharedDIBSurface!");
|
|
|
|
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_MACOSX
|
|
// Adjusting the window isn't needed for OSX
|
|
# 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();
|
|
}
|
|
#endif // XP_MACOSX
|
|
|
|
#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) {
|
|
// width and height are stored as units, but narrow to ints here
|
|
MOZ_RELEASE_ASSERT(mWindow.width <= INT_MAX);
|
|
MOZ_RELEASE_ASSERT(mWindow.height <= INT_MAX);
|
|
|
|
WINDOWPOS winpos = {0,
|
|
0,
|
|
mWindow.x,
|
|
mWindow.y,
|
|
(int32_t)mWindow.width,
|
|
(int32_t)mWindow.height,
|
|
0};
|
|
NPEvent pluginEvent = {WM_WINDOWPOSCHANGED, 0, (LPARAM)&winpos};
|
|
mPluginIface->event(&mData, &pluginEvent);
|
|
}
|
|
#endif
|
|
|
|
PLUGIN_LOG_DEBUG(
|
|
("[InstanceChild][%p] UpdateWindow w=<x=%d,y=%d, w=%d,h=%d>, "
|
|
"clip=<l=%d,t=%d,r=%d,b=%d>",
|
|
this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
|
|
mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right,
|
|
mWindow.clipRect.bottom));
|
|
|
|
if (mPluginIface->setwindow) {
|
|
mPluginIface->setwindow(&mData, &mWindow);
|
|
}
|
|
}
|
|
|
|
void PluginInstanceChild::PaintRectToPlatformSurface(const nsIntRect& aRect,
|
|
gfxASurface* aSurface) {
|
|
UpdateWindowAttributes();
|
|
|
|
// We should not send an async surface if we're using direct rendering.
|
|
MOZ_ASSERT(!IsUsingDirectDrawing());
|
|
|
|
#ifdef MOZ_X11
|
|
{
|
|
NS_ASSERTION(aSurface->GetType() == gfxSurfaceType::Xlib,
|
|
"Non supported platform surface type");
|
|
|
|
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 = X11False;
|
|
exposeEvent.major_code = 0;
|
|
exposeEvent.minor_code = 0;
|
|
mPluginIface->event(&mData, reinterpret_cast<void*>(&exposeEvent));
|
|
}
|
|
#elif defined(XP_WIN)
|
|
NS_ASSERTION(SharedDIBSurface::IsSharedDIBSurface(aSurface),
|
|
"Expected (SharedDIB) image surface.");
|
|
|
|
// 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.XMost(), mWindow.y + aRect.YMost()};
|
|
NPEvent paintEvent = {WM_PAINT, uintptr_t(mWindow.window), intptr_t(&rect)};
|
|
|
|
::SetViewportOrgEx((HDC)mWindow.window, -mWindow.x, -mWindow.y, nullptr);
|
|
::SelectClipRgn((HDC)mWindow.window, nullptr);
|
|
::IntersectClipRect((HDC)mWindow.window, rect.left, rect.top, rect.right,
|
|
rect.bottom);
|
|
mPluginIface->event(&mData, reinterpret_cast<void*>(&paintEvent));
|
|
#else
|
|
MOZ_CRASH("Surface type not implemented.");
|
|
#endif
|
|
}
|
|
|
|
void PluginInstanceChild::PaintRectToSurface(const nsIntRect& aRect,
|
|
gfxASurface* aSurface,
|
|
const DeviceColor& aColor) {
|
|
// Render using temporary X surface, with copy to image surface
|
|
nsIntRect plPaintRect(aRect);
|
|
RefPtr<gfxASurface> renderSurface = aSurface;
|
|
#ifdef MOZ_X11
|
|
if (mIsTransparent && (GetQuirks() & QUIRK_FLASH_EXPOSE_COORD_TRANSLATION)) {
|
|
// 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 (mHelperSurface) {
|
|
// On X11 we can paint to non Xlib surface only with HelperSurface
|
|
renderSurface = mHelperSurface;
|
|
}
|
|
#endif
|
|
|
|
if (mIsTransparent && !CanPaintOnBackground()) {
|
|
RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(renderSurface);
|
|
gfx::Rect rect(plPaintRect.x, plPaintRect.y, plPaintRect.width,
|
|
plPaintRect.height);
|
|
// Moz2D treats OP_SOURCE operations as unbounded, so we need to
|
|
// clip to the rect that we want to fill:
|
|
dt->PushClipRect(rect);
|
|
dt->FillRect(rect,
|
|
ColorPattern(aColor), // aColor is already a device color
|
|
DrawOptions(1.f, CompositionOp::OP_SOURCE));
|
|
dt->PopClip();
|
|
dt->Flush();
|
|
}
|
|
|
|
PaintRectToPlatformSurface(plPaintRect, renderSurface);
|
|
|
|
if (renderSurface != aSurface) {
|
|
RefPtr<DrawTarget> dt;
|
|
if (aSurface == mCurrentSurface &&
|
|
aSurface->GetType() == gfxSurfaceType::Image &&
|
|
aSurface->GetSurfaceFormat() == SurfaceFormat::B8G8R8X8) {
|
|
gfxImageSurface* imageSurface = static_cast<gfxImageSurface*>(aSurface);
|
|
// Bug 1196927 - Reinterpret target surface as BGRA to fill alpha with
|
|
// opaque. Certain backends (i.e. Skia) may not truly support BGRX
|
|
// formats, so they must be emulated by filling the alpha channel opaque
|
|
// as if it was BGRA data. Cairo leaves the alpha zeroed out for BGRX, so
|
|
// we cause Cairo to fill it as opaque by handling the copy target as a
|
|
// BGRA surface.
|
|
dt = Factory::CreateDrawTargetForData(
|
|
BackendType::CAIRO, imageSurface->Data(), imageSurface->GetSize(),
|
|
imageSurface->Stride(), SurfaceFormat::B8G8R8A8);
|
|
} else {
|
|
// Copy helper surface content to target
|
|
dt = CreateDrawTargetForSurface(aSurface);
|
|
}
|
|
if (dt && dt->IsValid()) {
|
|
RefPtr<SourceSurface> surface =
|
|
gfxPlatform::GetSourceSurfaceForSurface(dt, renderSurface);
|
|
dt->CopySurface(surface, aRect, aRect.TopLeft());
|
|
} else {
|
|
gfxWarning() << "PluginInstanceChild::PaintRectToSurface failure";
|
|
}
|
|
}
|
|
}
|
|
|
|
void PluginInstanceChild::PaintRectWithAlphaExtraction(const nsIntRect& aRect,
|
|
gfxASurface* aSurface) {
|
|
MOZ_ASSERT(aSurface->GetContentType() == gfxContentType::COLOR_ALPHA,
|
|
"Refusing to pointlessly recover alpha");
|
|
|
|
nsIntRect rect(aRect);
|
|
// If |aSurface| can be used to paint and can have alpha values
|
|
// recovered directly to it, do that to save a tmp surface and
|
|
// copy.
|
|
bool useSurfaceSubimageForBlack = false;
|
|
if (gfxSurfaceType::Image == aSurface->GetType()) {
|
|
gfxImageSurface* surfaceAsImage = static_cast<gfxImageSurface*>(aSurface);
|
|
useSurfaceSubimageForBlack =
|
|
(surfaceAsImage->Format() == SurfaceFormat::A8R8G8B8_UINT32);
|
|
// If we're going to use a subimage, nudge the rect so that we
|
|
// can use optimal alpha recovery. If we're not using a
|
|
// subimage, the temporaries should automatically get
|
|
// fast-path alpha recovery so we don't need to do anything.
|
|
if (useSurfaceSubimageForBlack) {
|
|
rect =
|
|
gfxAlphaRecovery::AlignRectForSubimageRecovery(aRect, surfaceAsImage);
|
|
}
|
|
}
|
|
|
|
RefPtr<gfxImageSurface> whiteImage;
|
|
RefPtr<gfxImageSurface> blackImage;
|
|
gfxRect targetRect(rect.x, rect.y, rect.width, rect.height);
|
|
IntSize targetSize(rect.width, rect.height);
|
|
|
|
// We always use a temporary "white image"
|
|
whiteImage = new gfxImageSurface(targetSize, SurfaceFormat::X8R8G8B8_UINT32);
|
|
if (whiteImage->CairoStatus()) {
|
|
return;
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
// On windows, we need an HDC and so can't paint directly to
|
|
// vanilla image surfaces. Bifurcate this painting code so that
|
|
// we don't accidentally attempt that.
|
|
if (!SharedDIBSurface::IsSharedDIBSurface(aSurface))
|
|
MOZ_CRASH("Expected SharedDIBSurface!");
|
|
|
|
// Paint the plugin directly onto the target, with a white
|
|
// background and copy the result
|
|
PaintRectToSurface(rect, aSurface, DeviceColor::MaskOpaqueWhite());
|
|
{
|
|
RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(whiteImage);
|
|
RefPtr<SourceSurface> surface =
|
|
gfxPlatform::GetSourceSurfaceForSurface(dt, aSurface);
|
|
dt->CopySurface(surface, rect, IntPoint());
|
|
}
|
|
|
|
// Paint the plugin directly onto the target, with a black
|
|
// background
|
|
PaintRectToSurface(rect, aSurface, DeviceColor::MaskOpaqueBlack());
|
|
|
|
// Don't copy the result, just extract a subimage so that we can
|
|
// recover alpha directly into the target
|
|
gfxImageSurface* image = static_cast<gfxImageSurface*>(aSurface);
|
|
blackImage = image->GetSubimage(targetRect);
|
|
|
|
#else
|
|
gfxPoint deviceOffset = -targetRect.TopLeft();
|
|
// Paint onto white background
|
|
whiteImage->SetDeviceOffset(deviceOffset);
|
|
PaintRectToSurface(rect, whiteImage, DeviceColor::MaskOpaqueWhite());
|
|
|
|
if (useSurfaceSubimageForBlack) {
|
|
gfxImageSurface* surface = static_cast<gfxImageSurface*>(aSurface);
|
|
blackImage = surface->GetSubimage(targetRect);
|
|
} else {
|
|
blackImage =
|
|
new gfxImageSurface(targetSize, SurfaceFormat::A8R8G8B8_UINT32);
|
|
}
|
|
|
|
// Paint onto black background
|
|
blackImage->SetDeviceOffset(deviceOffset);
|
|
PaintRectToSurface(rect, blackImage, DeviceColor::MaskOpaqueBlack());
|
|
#endif
|
|
|
|
MOZ_ASSERT(whiteImage && blackImage, "Didn't paint enough!");
|
|
|
|
// Extract alpha from black and white image and store to black
|
|
// image
|
|
if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) {
|
|
return;
|
|
}
|
|
|
|
// If we had to use a temporary black surface, copy the pixels
|
|
// with alpha back to the target
|
|
if (!useSurfaceSubimageForBlack) {
|
|
RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(aSurface);
|
|
RefPtr<SourceSurface> surface =
|
|
gfxPlatform::GetSourceSurfaceForSurface(dt, blackImage);
|
|
dt->CopySurface(surface, IntRect(0, 0, rect.width, rect.height),
|
|
rect.TopLeft());
|
|
}
|
|
}
|
|
|
|
bool PluginInstanceChild::CanPaintOnBackground() {
|
|
return (mBackground && mCurrentSurface &&
|
|
mCurrentSurface->GetSize() == mBackground->GetSize());
|
|
}
|
|
|
|
bool PluginInstanceChild::ShowPluginFrame() {
|
|
// mLayersRendering can be false if we somehow get here without
|
|
// receiving AsyncSetWindow() first. mPendingPluginCall is our
|
|
// re-entrancy guard; we can't paint while nested inside another
|
|
// paint.
|
|
if (!mLayersRendering || mPendingPluginCall) {
|
|
return false;
|
|
}
|
|
|
|
// We should not attempt to asynchronously show the plugin if we're using
|
|
// direct rendering.
|
|
MOZ_ASSERT(!IsUsingDirectDrawing());
|
|
|
|
AutoRestore<bool> pending(mPendingPluginCall);
|
|
mPendingPluginCall = true;
|
|
|
|
bool temporarilyMakeVisible = !IsVisible() && !mHasPainted;
|
|
if (temporarilyMakeVisible && mWindow.width && mWindow.height) {
|
|
mWindow.clipRect.right = mWindow.width;
|
|
mWindow.clipRect.bottom = mWindow.height;
|
|
} else if (!IsVisible()) {
|
|
// If we're not visible, don't bother painting a <0,0,0,0>
|
|
// rect. If we're eventually made visible, the visibility
|
|
// change will invalidate our window.
|
|
ClearCurrentSurface();
|
|
return true;
|
|
}
|
|
|
|
if (!EnsureCurrentBuffer()) {
|
|
return false;
|
|
}
|
|
|
|
#ifdef MOZ_WIDGET_COCOA
|
|
// We can't use the thebes code with CoreAnimation so we will
|
|
// take a different code path.
|
|
if (mDrawingModel == NPDrawingModelCoreAnimation ||
|
|
mDrawingModel == NPDrawingModelInvalidatingCoreAnimation ||
|
|
mDrawingModel == NPDrawingModelCoreGraphics) {
|
|
if (!IsVisible()) {
|
|
return true;
|
|
}
|
|
|
|
if (!mDoubleBufferCARenderer.HasFrontSurface()) {
|
|
NS_ERROR("CARenderer not initialized for rendering");
|
|
return false;
|
|
}
|
|
|
|
// Clear accRect here to be able to pass
|
|
// test_invalidate_during_plugin_paint test
|
|
nsIntRect rect = mAccumulatedInvalidRect;
|
|
mAccumulatedInvalidRect.SetEmpty();
|
|
|
|
// Fix up old invalidations that might have been made when our
|
|
// surface was a different size
|
|
rect.IntersectRect(
|
|
rect, nsIntRect(0, 0, mDoubleBufferCARenderer.GetFrontSurfaceWidth(),
|
|
mDoubleBufferCARenderer.GetFrontSurfaceHeight()));
|
|
|
|
if (mDrawingModel == NPDrawingModelCoreGraphics) {
|
|
mozilla::plugins::PluginUtilsOSX::Repaint(mCGLayer, rect);
|
|
}
|
|
|
|
mDoubleBufferCARenderer.Render();
|
|
|
|
NPRect r = {(uint16_t)rect.y, (uint16_t)rect.x, (uint16_t)rect.YMost(),
|
|
(uint16_t)rect.XMost()};
|
|
SurfaceDescriptor currSurf;
|
|
currSurf =
|
|
IOSurfaceDescriptor(mDoubleBufferCARenderer.GetFrontSurfaceID(),
|
|
mDoubleBufferCARenderer.GetContentsScaleFactor());
|
|
|
|
mHasPainted = true;
|
|
|
|
SurfaceDescriptor returnSurf;
|
|
|
|
if (!SendShow(r, currSurf, &returnSurf)) {
|
|
return false;
|
|
}
|
|
|
|
SwapSurfaces();
|
|
return true;
|
|
} else {
|
|
NS_ERROR("Unsupported drawing model for async layer rendering");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
NS_ASSERTION(mWindow.width == uint32_t(mWindow.clipRect.right -
|
|
mWindow.clipRect.left) &&
|
|
mWindow.height ==
|
|
uint32_t(mWindow.clipRect.bottom - mWindow.clipRect.top),
|
|
"Clip rect should be same size as window when using layers");
|
|
|
|
// Clear accRect here to be able to pass
|
|
// test_invalidate_during_plugin_paint test
|
|
nsIntRect rect = mAccumulatedInvalidRect;
|
|
mAccumulatedInvalidRect.SetEmpty();
|
|
|
|
// Fix up old invalidations that might have been made when our
|
|
// surface was a different size
|
|
IntSize surfaceSize = mCurrentSurface->GetSize();
|
|
rect.IntersectRect(rect,
|
|
nsIntRect(0, 0, surfaceSize.width, surfaceSize.height));
|
|
|
|
if (!ReadbackDifferenceRect(rect)) {
|
|
// We couldn't read back the pixels that differ between the
|
|
// current surface and last, so we have to invalidate the
|
|
// entire window.
|
|
rect.SetRect(0, 0, mWindow.width, mWindow.height);
|
|
}
|
|
|
|
bool haveTransparentPixels =
|
|
gfxContentType::COLOR_ALPHA == mCurrentSurface->GetContentType();
|
|
PLUGIN_LOG_DEBUG(
|
|
("[InstanceChild][%p] Painting%s <x=%d,y=%d, w=%d,h=%d> on surface "
|
|
"<w=%d,h=%d>",
|
|
this, haveTransparentPixels ? " with alpha" : "", rect.x, rect.y,
|
|
rect.width, rect.height, mCurrentSurface->GetSize().width,
|
|
mCurrentSurface->GetSize().height));
|
|
|
|
if (CanPaintOnBackground()) {
|
|
PLUGIN_LOG_DEBUG((" (on background)"));
|
|
// Source the background pixels ...
|
|
{
|
|
RefPtr<gfxASurface> surface =
|
|
mHelperSurface ? mHelperSurface : mCurrentSurface;
|
|
RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(surface);
|
|
RefPtr<SourceSurface> backgroundSurface =
|
|
gfxPlatform::GetSourceSurfaceForSurface(dt, mBackground);
|
|
dt->CopySurface(backgroundSurface, rect, rect.TopLeft());
|
|
}
|
|
// ... and hand off to the plugin
|
|
// BEWARE: mBackground may die during this call
|
|
PaintRectToSurface(rect, mCurrentSurface, DeviceColor());
|
|
} else if (!temporarilyMakeVisible && mDoAlphaExtraction) {
|
|
// We don't want to pay the expense of alpha extraction for
|
|
// phony paints.
|
|
PLUGIN_LOG_DEBUG((" (with alpha recovery)"));
|
|
PaintRectWithAlphaExtraction(rect, mCurrentSurface);
|
|
} else {
|
|
PLUGIN_LOG_DEBUG((" (onto opaque surface)"));
|
|
|
|
// If we're on a platform that needs helper surfaces for
|
|
// plugins, and we're forcing a throwaway paint of a
|
|
// wmode=transparent plugin, then make sure to use the helper
|
|
// surface here.
|
|
RefPtr<gfxASurface> target = (temporarilyMakeVisible && mHelperSurface)
|
|
? mHelperSurface
|
|
: mCurrentSurface;
|
|
|
|
PaintRectToSurface(rect, target, DeviceColor());
|
|
}
|
|
mHasPainted = true;
|
|
|
|
if (temporarilyMakeVisible) {
|
|
mWindow.clipRect.right = mWindow.clipRect.bottom = 0;
|
|
|
|
PLUGIN_LOG_DEBUG(
|
|
("[InstanceChild][%p] Undoing temporary clipping w=<x=%d,y=%d, "
|
|
"w=%d,h=%d>, clip=<l=%d,t=%d,r=%d,b=%d>",
|
|
this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
|
|
mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right,
|
|
mWindow.clipRect.bottom));
|
|
|
|
if (mPluginIface->setwindow) {
|
|
mPluginIface->setwindow(&mData, &mWindow);
|
|
}
|
|
|
|
// Skip forwarding the results of the phony paint to the
|
|
// browser. We may have painted a transparent plugin using
|
|
// the opaque-plugin path, which can result in wrong pixels.
|
|
// We also don't want to pay the expense of forwarding the
|
|
// surface for plugins that might really be invisible.
|
|
mAccumulatedInvalidRect.SetRect(0, 0, mWindow.width, mWindow.height);
|
|
return true;
|
|
}
|
|
|
|
NPRect r = {(uint16_t)rect.y, (uint16_t)rect.x, (uint16_t)rect.YMost(),
|
|
(uint16_t)rect.XMost()};
|
|
SurfaceDescriptor currSurf;
|
|
#ifdef MOZ_X11
|
|
if (mCurrentSurface->GetType() == gfxSurfaceType::Xlib) {
|
|
gfxXlibSurface* xsurf = static_cast<gfxXlibSurface*>(mCurrentSurface.get());
|
|
currSurf = SurfaceDescriptorX11(xsurf);
|
|
// Need to sync all pending x-paint requests
|
|
// before giving drawable to another process
|
|
XSync(mWsInfo.display, X11False);
|
|
} else
|
|
#endif
|
|
#ifdef XP_WIN
|
|
if (SharedDIBSurface::IsSharedDIBSurface(mCurrentSurface)) {
|
|
SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get());
|
|
if (!mCurrentSurfaceActor) {
|
|
base::SharedMemoryHandle handle = nullptr;
|
|
s->ShareToProcess(OtherPid(), &handle);
|
|
|
|
mCurrentSurfaceActor = SendPPluginSurfaceConstructor(
|
|
handle, mCurrentSurface->GetSize(), haveTransparentPixels);
|
|
}
|
|
currSurf = mCurrentSurfaceActor;
|
|
s->Flush();
|
|
} else
|
|
#endif
|
|
if (gfxSharedImageSurface::IsSharedImage(mCurrentSurface)) {
|
|
currSurf = std::move(
|
|
static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem());
|
|
} else {
|
|
MOZ_CRASH("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;
|
|
}
|
|
|
|
SwapSurfaces();
|
|
mSurfaceDifferenceRect = rect;
|
|
return true;
|
|
}
|
|
|
|
bool PluginInstanceChild::ReadbackDifferenceRect(const nsIntRect& rect) {
|
|
if (!mBackSurface) return false;
|
|
|
|
// We can read safely from XSurface,SharedDIBSurface and Unsafe
|
|
// SharedMemory, because PluginHost is not able to modify that surface
|
|
#if defined(MOZ_X11)
|
|
if (mBackSurface->GetType() != gfxSurfaceType::Xlib &&
|
|
!gfxSharedImageSurface::IsSharedImage(mBackSurface))
|
|
return false;
|
|
#elif defined(XP_WIN)
|
|
if (!SharedDIBSurface::IsSharedDIBSurface(mBackSurface)) return false;
|
|
#endif
|
|
|
|
#if defined(MOZ_X11) || defined(XP_WIN)
|
|
if (mCurrentSurface->GetContentType() != mBackSurface->GetContentType())
|
|
return false;
|
|
|
|
if (mSurfaceDifferenceRect.IsEmpty()) return true;
|
|
|
|
PLUGIN_LOG_DEBUG(
|
|
("[InstanceChild][%p] Reading back part of <x=%d,y=%d, w=%d,h=%d>", this,
|
|
mSurfaceDifferenceRect.x, mSurfaceDifferenceRect.y,
|
|
mSurfaceDifferenceRect.width, mSurfaceDifferenceRect.height));
|
|
|
|
// Read back previous content
|
|
RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(mCurrentSurface);
|
|
RefPtr<SourceSurface> source =
|
|
gfxPlatform::GetSourceSurfaceForSurface(dt, mBackSurface);
|
|
// Subtract from mSurfaceDifferenceRect area which is overlapping with rect
|
|
nsIntRegion result;
|
|
result.Sub(mSurfaceDifferenceRect, nsIntRegion(rect));
|
|
for (auto iter = result.RectIter(); !iter.Done(); iter.Next()) {
|
|
const nsIntRect& r = iter.Get();
|
|
dt->CopySurface(source, r, r.TopLeft());
|
|
}
|
|
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void PluginInstanceChild::InvalidateRectDelayed(void) {
|
|
if (!mCurrentInvalidateTask) {
|
|
return;
|
|
}
|
|
|
|
mCurrentInvalidateTask = nullptr;
|
|
|
|
// When this method is run asynchronously, we can end up switching to
|
|
// direct drawing before while we wait to run. In that case, bail.
|
|
if (IsUsingDirectDrawing()) {
|
|
return;
|
|
}
|
|
|
|
if (mAccumulatedInvalidRect.IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
if (!ShowPluginFrame()) {
|
|
AsyncShowPluginFrame();
|
|
}
|
|
}
|
|
|
|
void PluginInstanceChild::AsyncShowPluginFrame(void) {
|
|
if (mCurrentInvalidateTask) {
|
|
return;
|
|
}
|
|
|
|
// When the plugin is using direct surfaces to draw, it is not driving
|
|
// paints via paint events - it will drive painting via its own events
|
|
// and/or DidComposite callbacks.
|
|
if (IsUsingDirectDrawing()) {
|
|
return;
|
|
}
|
|
|
|
mCurrentInvalidateTask = NewNonOwningCancelableRunnableMethod(
|
|
"plugins::PluginInstanceChild::InvalidateRectDelayed", this,
|
|
&PluginInstanceChild::InvalidateRectDelayed);
|
|
RefPtr<Runnable> addrefedTask = mCurrentInvalidateTask;
|
|
MessageLoop::current()->PostTask(addrefedTask.forget());
|
|
}
|
|
|
|
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 (IsUsingDirectDrawing()) {
|
|
NS_ASSERTION(false,
|
|
"Should not call InvalidateRect() in direct surface mode!");
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// If we were going to use layers rendering but it's not set up
|
|
// yet, and the plugin happens to call this first, we'll forward
|
|
// the invalidation to the browser. It's unclear whether
|
|
// non-layers plugins need this rect forwarded when their window
|
|
// width or height is 0, which it would be for layers plugins
|
|
// before their first SetWindow().
|
|
SendNPN_InvalidateRect(*aInvalidRect);
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::RecvUpdateBackground(
|
|
const SurfaceDescriptor& aBackground, const nsIntRect& aRect) {
|
|
MOZ_ASSERT(mIsTransparent, "Only transparent plugins use backgrounds");
|
|
|
|
if (!mBackground) {
|
|
// XXX refactor me
|
|
switch (aBackground.type()) {
|
|
#ifdef MOZ_X11
|
|
case SurfaceDescriptor::TSurfaceDescriptorX11: {
|
|
mBackground = aBackground.get_SurfaceDescriptorX11().OpenForeign();
|
|
break;
|
|
}
|
|
#endif
|
|
case SurfaceDescriptor::TShmem: {
|
|
mBackground = gfxSharedImageSurface::Open(aBackground.get_Shmem());
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_CRASH("Unexpected background surface descriptor");
|
|
}
|
|
|
|
if (!mBackground) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
IntSize bgSize = mBackground->GetSize();
|
|
mAccumulatedInvalidRect.UnionRect(
|
|
mAccumulatedInvalidRect, nsIntRect(0, 0, bgSize.width, bgSize.height));
|
|
AsyncShowPluginFrame();
|
|
return IPC_OK();
|
|
}
|
|
|
|
// XXX refactor me
|
|
mAccumulatedInvalidRect.UnionRect(aRect, mAccumulatedInvalidRect);
|
|
|
|
// This must be asynchronous, because we may be nested within RPC messages
|
|
// which do not expect to receiving paint events.
|
|
AsyncShowPluginFrame();
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
PPluginBackgroundDestroyerChild*
|
|
PluginInstanceChild::AllocPPluginBackgroundDestroyerChild() {
|
|
return new PluginBackgroundDestroyerChild();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
PluginInstanceChild::RecvPPluginBackgroundDestroyerConstructor(
|
|
PPluginBackgroundDestroyerChild* aActor) {
|
|
// Our background changed, so we have to invalidate the area
|
|
// painted with the old background. If the background was
|
|
// destroyed because we have a new background, then we expect to
|
|
// be notified of that "soon", before processing the asynchronous
|
|
// invalidation here. If we're *not* getting a new background,
|
|
// our current front surface is stale and we want to repaint
|
|
// "soon" so that we can hand the browser back a surface with
|
|
// alpha values. (We should be notified of that invalidation soon
|
|
// too, but we don't assume that here.)
|
|
if (mBackground) {
|
|
IntSize bgsize = mBackground->GetSize();
|
|
mAccumulatedInvalidRect.UnionRect(
|
|
nsIntRect(0, 0, bgsize.width, bgsize.height), mAccumulatedInvalidRect);
|
|
|
|
// NB: we don't have to XSync here because only ShowPluginFrame()
|
|
// uses mBackground, and it always XSyncs after finishing.
|
|
mBackground = nullptr;
|
|
AsyncShowPluginFrame();
|
|
}
|
|
|
|
if (!PPluginBackgroundDestroyerChild::Send__delete__(aActor)) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool PluginInstanceChild::DeallocPPluginBackgroundDestroyerChild(
|
|
PPluginBackgroundDestroyerChild* aActor) {
|
|
delete aActor;
|
|
return true;
|
|
}
|
|
|
|
uint32_t PluginInstanceChild::ScheduleTimer(uint32_t interval, bool repeat,
|
|
TimerFunc func) {
|
|
auto* 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::SwapSurfaces() {
|
|
RefPtr<gfxASurface> tmpsurf = mCurrentSurface;
|
|
#ifdef XP_WIN
|
|
PPluginSurfaceChild* tmpactor = mCurrentSurfaceActor;
|
|
#endif
|
|
|
|
mCurrentSurface = mBackSurface;
|
|
#ifdef XP_WIN
|
|
mCurrentSurfaceActor = mBackSurfaceActor;
|
|
#endif
|
|
|
|
mBackSurface = tmpsurf;
|
|
#ifdef XP_WIN
|
|
mBackSurfaceActor = tmpactor;
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_COCOA
|
|
mDoubleBufferCARenderer.SwapSurfaces();
|
|
|
|
// Outdated back surface... not usable anymore due to changed plugin size.
|
|
// Dropping obsolete surface
|
|
if (mDoubleBufferCARenderer.HasFrontSurface() &&
|
|
mDoubleBufferCARenderer.HasBackSurface() &&
|
|
(mDoubleBufferCARenderer.GetFrontSurfaceWidth() !=
|
|
mDoubleBufferCARenderer.GetBackSurfaceWidth() ||
|
|
mDoubleBufferCARenderer.GetFrontSurfaceHeight() !=
|
|
mDoubleBufferCARenderer.GetBackSurfaceHeight() ||
|
|
mDoubleBufferCARenderer.GetFrontSurfaceContentsScaleFactor() !=
|
|
mDoubleBufferCARenderer.GetBackSurfaceContentsScaleFactor())) {
|
|
mDoubleBufferCARenderer.ClearFrontSurface();
|
|
}
|
|
#else
|
|
if (mCurrentSurface && mBackSurface &&
|
|
(mCurrentSurface->GetSize() != mBackSurface->GetSize() ||
|
|
mCurrentSurface->GetContentType() != mBackSurface->GetContentType())) {
|
|
ClearCurrentSurface();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void PluginInstanceChild::ClearCurrentSurface() {
|
|
mCurrentSurface = nullptr;
|
|
#ifdef MOZ_WIDGET_COCOA
|
|
if (mDoubleBufferCARenderer.HasFrontSurface()) {
|
|
mDoubleBufferCARenderer.ClearFrontSurface();
|
|
}
|
|
#endif
|
|
#ifdef XP_WIN
|
|
if (mCurrentSurfaceActor) {
|
|
PPluginSurfaceChild::Send__delete__(mCurrentSurfaceActor);
|
|
mCurrentSurfaceActor = nullptr;
|
|
}
|
|
#endif
|
|
mHelperSurface = nullptr;
|
|
}
|
|
|
|
void PluginInstanceChild::ClearAllSurfaces() {
|
|
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 = nullptr;
|
|
mBackSurface = nullptr;
|
|
|
|
#ifdef XP_WIN
|
|
if (mCurrentSurfaceActor) {
|
|
PPluginSurfaceChild::Send__delete__(mCurrentSurfaceActor);
|
|
mCurrentSurfaceActor = nullptr;
|
|
}
|
|
if (mBackSurfaceActor) {
|
|
PPluginSurfaceChild::Send__delete__(mBackSurfaceActor);
|
|
mBackSurfaceActor = nullptr;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_COCOA
|
|
if (mDoubleBufferCARenderer.HasBackSurface()) {
|
|
// Get last surface back, and drop it
|
|
SurfaceDescriptor temp = null_t();
|
|
NPRect r = {0, 0, 1, 1};
|
|
SendShow(r, temp, &temp);
|
|
}
|
|
|
|
if (mCGLayer) {
|
|
mozilla::plugins::PluginUtilsOSX::ReleaseCGLayer(mCGLayer);
|
|
mCGLayer = nullptr;
|
|
}
|
|
|
|
mDoubleBufferCARenderer.ClearFrontSurface();
|
|
mDoubleBufferCARenderer.ClearBackSurface();
|
|
#endif
|
|
}
|
|
|
|
static void InvalidateObjects(nsTHashtable<DeletingObjectEntry>& aEntries) {
|
|
for (const auto& e : aEntries) {
|
|
NPObject* o = e.GetKey();
|
|
if (!e.mDeleted && o->_class && o->_class->invalidate) {
|
|
o->_class->invalidate(o);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void DeleteObjects(nsTHashtable<DeletingObjectEntry>& aEntries) {
|
|
for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
|
|
DeletingObjectEntry* e = iter.Get();
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PluginInstanceChild::Destroy() {
|
|
if (mDestroyed) {
|
|
return;
|
|
}
|
|
if (mStackDepth != 0) {
|
|
MOZ_CRASH("Destroying plugin instance on the stack.");
|
|
}
|
|
mDestroyed = true;
|
|
|
|
#if defined(OS_WIN)
|
|
SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
|
|
#endif
|
|
|
|
nsTArray<PBrowserStreamChild*> streams;
|
|
ManagedPBrowserStreamChild(streams);
|
|
|
|
// First make sure none of these streams become deleted
|
|
streams.RemoveElementsBy([](const auto& stream) {
|
|
return !static_cast<BrowserStreamChild*>(stream)->InstanceDying();
|
|
});
|
|
for (uint32_t i = 0; i < streams.Length(); ++i)
|
|
static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery();
|
|
|
|
mTimers.Clear();
|
|
|
|
// NPP_Destroy() should be a synchronization point for plugin threads
|
|
// calling NPN_AsyncCall: after this function returns, they are no longer
|
|
// allowed to make async calls on this instance.
|
|
static_cast<PluginModuleChild*>(Manager())->NPP_Destroy(this);
|
|
mData.ndata = 0;
|
|
|
|
if (mCurrentInvalidateTask) {
|
|
mCurrentInvalidateTask->Cancel();
|
|
mCurrentInvalidateTask = nullptr;
|
|
}
|
|
if (mCurrentAsyncSetWindowTask) {
|
|
mCurrentAsyncSetWindowTask->Cancel();
|
|
mCurrentAsyncSetWindowTask = nullptr;
|
|
}
|
|
{
|
|
MutexAutoLock autoLock(mAsyncInvalidateMutex);
|
|
if (mAsyncInvalidateTask) {
|
|
mAsyncInvalidateTask->Cancel();
|
|
mAsyncInvalidateTask = nullptr;
|
|
}
|
|
}
|
|
|
|
ClearAllSurfaces();
|
|
mDirectBitmaps.Clear();
|
|
|
|
mDeletingHash = MakeUnique<nsTHashtable<DeletingObjectEntry>>();
|
|
PluginScriptableObjectChild::NotifyOfInstanceShutdown(this);
|
|
|
|
InvalidateObjects(*mDeletingHash);
|
|
DeleteObjects(*mDeletingHash);
|
|
|
|
// Null out our cached actors as they should have been killed in the
|
|
// PluginInstanceDestroyed call above.
|
|
mCachedWindowActor = nullptr;
|
|
mCachedElementActor = nullptr;
|
|
|
|
#if defined(OS_WIN)
|
|
DestroyWinlessPopupSurrogate();
|
|
UnhookWinlessFlashThrottle();
|
|
DestroyPluginWindow();
|
|
|
|
for (uint32_t i = 0; i < mPendingFlashThrottleMsgs.Length(); ++i) {
|
|
mPendingFlashThrottleMsgs[i]->Cancel();
|
|
}
|
|
mPendingFlashThrottleMsgs.Clear();
|
|
#endif
|
|
}
|
|
|
|
mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_Destroy(
|
|
NPError* aResult) {
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
AssertPluginThread();
|
|
*aResult = NPERR_NO_ERROR;
|
|
|
|
Destroy();
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
void PluginInstanceChild::ActorDestroy(ActorDestroyReason why) {
|
|
#ifdef XP_WIN
|
|
// ClearAllSurfaces() should not try to send anything after ActorDestroy.
|
|
mCurrentSurfaceActor = nullptr;
|
|
mBackSurfaceActor = nullptr;
|
|
#endif
|
|
|
|
Destroy();
|
|
}
|