summaryrefslogtreecommitdiffstats
path: root/dom/plugins/ipc/PluginInstanceChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/ipc/PluginInstanceChild.cpp')
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.cpp4045
1 files changed, 4045 insertions, 0 deletions
diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp
new file mode 100644
index 0000000000..403a735c83
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -0,0 +1,4045 @@
+/* -*- 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 "mozilla/widget/WinModifierKeyState.h"
+# include "mozilla/widget/WinNativeEventData.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;
+}
+
+bool PluginInstanceChild::sIsIMEComposing = false;
+
+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),
+ mPostingKeyEvents(0),
+ mPostingKeyEventsOutdated(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
+ ,
+ mLastKeyEventConsumed(false),
+ 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(&currentActor, &currentResult);
+ break;
+ case NPNVPluginElementNPObject:
+ CallNPN_GetValue_NPNVPluginElementNPObject(&currentActor,
+ &currentResult);
+ 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;
+}
+
+mozilla::ipc::IPCResult PluginInstanceChild::RecvHandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData, const bool& aIsConsumed) {
+#if defined(OS_WIN)
+ const WinNativeKeyEventData* eventData =
+ static_cast<const WinNativeKeyEventData*>(aKeyEventData);
+ switch (eventData->mMessage) {
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ mLastKeyEventConsumed = aIsConsumed;
+ break;
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ // If preceding keydown or keyup event is consumed by the chrome
+ // process, we should consume WM_*CHAR messages too.
+ if (mLastKeyEventConsumed) {
+ return IPC_OK();
+ }
+ default:
+ MOZ_CRASH("Needs to handle all messages posted to the parent");
+ }
+#endif // #if defined(OS_WIN)
+
+ // Unknown key input shouldn't be sent to plugin for security.
+ // XXX Is this possible if a plugin process which posted the message
+ // already crashed and this plugin process is recreated?
+ if (NS_WARN_IF(!mPostingKeyEvents && !mPostingKeyEventsOutdated)) {
+ return IPC_OK();
+ }
+
+ // If there is outdated posting key events, we should consume the key
+ // events.
+ if (mPostingKeyEventsOutdated) {
+ mPostingKeyEventsOutdated--;
+ return IPC_OK();
+ }
+
+ mPostingKeyEvents--;
+
+ // If composition has been started after posting the key event,
+ // we should discard the event since if we send the event to plugin,
+ // the plugin may be confused and the result may be broken because
+ // the event order is shuffled.
+ if (aIsConsumed || sIsIMEComposing) {
+ return IPC_OK();
+ }
+
+#if defined(OS_WIN)
+ UINT message = 0;
+ switch (eventData->mMessage) {
+ case WM_KEYDOWN:
+ message = MOZ_WM_KEYDOWN;
+ break;
+ case WM_SYSKEYDOWN:
+ message = MOZ_WM_SYSKEYDOWN;
+ break;
+ case WM_KEYUP:
+ message = MOZ_WM_KEYUP;
+ break;
+ case WM_SYSKEYUP:
+ message = MOZ_WM_SYSKEYUP;
+ break;
+ case WM_CHAR:
+ message = MOZ_WM_CHAR;
+ break;
+ case WM_SYSCHAR:
+ message = MOZ_WM_SYSCHAR;
+ break;
+ case WM_DEADCHAR:
+ message = MOZ_WM_DEADCHAR;
+ break;
+ case WM_SYSDEADCHAR:
+ message = MOZ_WM_SYSDEADCHAR;
+ break;
+ default:
+ MOZ_CRASH("Needs to handle all messages posted to the parent");
+ }
+ PluginWindowProcInternal(mPluginWindowHWND, message, eventData->mWParam,
+ eventData->mLParam);
+#endif
+ return IPC_OK();
+}
+
+#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_SETFOCUS:
+ // If this gets focus, ensure that there is no pending key events.
+ // Even if there were, we should ignore them for performance reason.
+ // Although, such case shouldn't occur.
+ NS_WARNING_ASSERTION(self->mPostingKeyEvents == 0, "pending events");
+ self->mPostingKeyEvents = 0;
+ self->mLastKeyEventConsumed = false;
+ break;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ if (self->MaybePostKeyMessage(message, wParam, lParam)) {
+ // If PreHandleKeyMessage() posts the message to the parent
+ // process, we need to wait RecvOnKeyEventHandledBeforePlugin()
+ // to be called.
+ return 0; // Consume current message temporarily.
+ }
+ break;
+
+ case MOZ_WM_KEYDOWN:
+ message = WM_KEYDOWN;
+ break;
+ case MOZ_WM_SYSKEYDOWN:
+ message = WM_SYSKEYDOWN;
+ break;
+ case MOZ_WM_KEYUP:
+ message = WM_KEYUP;
+ break;
+ case MOZ_WM_SYSKEYUP:
+ message = WM_SYSKEYUP;
+ break;
+ case MOZ_WM_CHAR:
+ message = WM_CHAR;
+ break;
+ case MOZ_WM_SYSCHAR:
+ message = WM_SYSCHAR;
+ break;
+ case MOZ_WM_DEADCHAR:
+ message = WM_DEADCHAR;
+ break;
+ case MOZ_WM_SYSDEADCHAR:
+ message = WM_SYSDEADCHAR;
+ break;
+
+ case WM_IME_STARTCOMPOSITION:
+ isIMECompositionMessage = true;
+ sIsIMEComposing = true;
+ break;
+ case WM_IME_ENDCOMPOSITION:
+ isIMECompositionMessage = true;
+ sIsIMEComposing = false;
+ 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.
+ sIsIMEComposing = !(lParam & GCS_RESULTSTR);
+ break;
+
+ // The plugin received keyboard focus, let the parent know so the dom
+ // is up to date.
+ case WM_MOUSEACTIVATE:
+ self->CallPluginFocusChange(true);
+ break;
+ }
+
+ // When a composition is committed, there may be pending key
+ // events which were posted to the parent process before starting
+ // the composition. Then, we shouldn't handle it since they are
+ // now outdated.
+ if (isIMECompositionMessage && !sIsIMEComposing) {
+ self->mPostingKeyEventsOutdated += self->mPostingKeyEvents;
+ self->mPostingKeyEvents = 0;
+ }
+
+ // 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;
+}
+
+bool PluginInstanceChild::ShouldPostKeyMessage(UINT message, WPARAM wParam,
+ LPARAM lParam) {
+ // If there is a composition, we shouldn't post the key message to the
+ // parent process because we cannot handle IME messages asynchronously.
+ // Therefore, if we posted key events to the parent process, the event
+ // order of the posted key events and IME events are shuffled.
+ if (sIsIMEComposing) {
+ return false;
+ }
+
+ // If there are some pending keyboard events which are not handled in
+ // the parent process, we should post the message for avoiding to shuffle
+ // the key event order.
+ if (mPostingKeyEvents) {
+ return true;
+ }
+
+ // If we are not waiting calls of RecvOnKeyEventHandledBeforePlugin(),
+ // we don't need to post WM_*CHAR messages.
+ switch (message) {
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ return false;
+ }
+
+ // Otherwise, we should post key message which might match with a
+ // shortcut key.
+ ModifierKeyState modifierState;
+ if (!modifierState.MaybeMatchShortcutKey()) {
+ // For better UX, we shouldn't use IPC when user tries to
+ // input character(s).
+ return false;
+ }
+
+ // Ignore modifier key events and keys already handled by IME.
+ switch (wParam) {
+ case VK_SHIFT:
+ case VK_CONTROL:
+ case VK_MENU:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_CAPITAL:
+ case VK_NUMLOCK:
+ case VK_SCROLL:
+ // Following virtual keycodes shouldn't come with WM_(SYS)KEY* message
+ // but check it for avoiding unnecessary cross process communication.
+ case VK_LSHIFT:
+ case VK_RSHIFT:
+ case VK_LCONTROL:
+ case VK_RCONTROL:
+ case VK_LMENU:
+ case VK_RMENU:
+ case VK_PROCESSKEY:
+ case VK_PACKET:
+ case 0xFF: // 0xFF could be sent with unidentified key by the layout.
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool PluginInstanceChild::MaybePostKeyMessage(UINT message, WPARAM wParam,
+ LPARAM lParam) {
+ if (!ShouldPostKeyMessage(message, wParam, lParam)) {
+ return false;
+ }
+
+ ModifierKeyState modifierState;
+ WinNativeKeyEventData winNativeKeyData(message, wParam, lParam,
+ modifierState);
+ NativeEventData nativeKeyData;
+ nativeKeyData.Copy(winNativeKeyData);
+ if (NS_WARN_IF(!SendOnWindowedPluginKeyEvent(nativeKeyData))) {
+ return false;
+ }
+
+ mPostingKeyEvents++;
+ return true;
+}
+
+/* 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.Put(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.Put(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 (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
+ DeletingObjectEntry* e = iter.Get();
+ 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();
+}