mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
1690 lines
52 KiB
C++
1690 lines
52 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: sw=4 ts=4 et :
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla Plugin App.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Chris Jones <jones.chris.g@gmail.com>
|
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Jim Mathies <jmathies@mozilla.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "PluginInstanceChild.h"
|
|
#include "PluginModuleChild.h"
|
|
#include "BrowserStreamChild.h"
|
|
#include "PluginStreamChild.h"
|
|
#include "StreamNotifyChild.h"
|
|
|
|
#include "mozilla/ipc/SyncChannel.h"
|
|
|
|
using namespace mozilla::plugins;
|
|
|
|
#ifdef MOZ_WIDGET_GTK2
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <gdk/gdk.h>
|
|
#include "gtk2xtbin.h"
|
|
|
|
#elif defined(MOZ_WIDGET_QT)
|
|
#include <QX11Info>
|
|
#elif defined(OS_WIN)
|
|
|
|
#include "nsWindowsDllInterceptor.h"
|
|
|
|
typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu,
|
|
UINT uFlags,
|
|
int x,
|
|
int y,
|
|
int nReserved,
|
|
HWND hWnd,
|
|
CONST RECT *prcRect);
|
|
static WindowsDllInterceptor sUser32Intercept;
|
|
static HWND sWinlessPopupSurrogateHWND = NULL;
|
|
static User32TrackPopupMenu sUser32TrackPopupMenuStub = NULL;
|
|
|
|
using mozilla::gfx::SharedDIB;
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
|
|
#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
|
|
|
|
#endif // defined(OS_WIN)
|
|
|
|
PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
|
|
const nsCString& aMimeType)
|
|
: mPluginIface(aPluginIface)
|
|
, mQuirks(0)
|
|
, mCachedWindowActor(nsnull)
|
|
, mCachedElementActor(nsnull)
|
|
#if defined(OS_WIN)
|
|
, mPluginWindowHWND(0)
|
|
, mPluginWndProc(0)
|
|
, mPluginParentHWND(0)
|
|
, mNestedEventHook(0)
|
|
, mNestedEventLevelDepth(0)
|
|
, mNestedEventState(false)
|
|
, mCachedWinlessPluginHWND(0)
|
|
, mWinlessPopupSurrogateHWND(0)
|
|
#endif // OS_WIN
|
|
{
|
|
memset(&mWindow, 0, sizeof(mWindow));
|
|
mData.ndata = (void*) this;
|
|
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
|
|
mWindow.ws_info = &mWsInfo;
|
|
memset(&mWsInfo, 0, sizeof(mWsInfo));
|
|
#ifdef MOZ_WIDGET_GTK2
|
|
mWsInfo.display = GDK_DISPLAY();
|
|
#elif defined(MOZ_WIDGET_QT)
|
|
mWsInfo.display = QX11Info::display();
|
|
#endif // MOZ_WIDGET_GTK2
|
|
#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
|
|
#if defined(OS_WIN)
|
|
memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
|
|
mAlphaExtract.doublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID);
|
|
#endif // OS_WIN
|
|
InitQuirksModes(aMimeType);
|
|
#if defined(OS_WIN)
|
|
InitPopupMenuHook();
|
|
#endif // OS_WIN
|
|
}
|
|
|
|
PluginInstanceChild::~PluginInstanceChild()
|
|
{
|
|
#if defined(OS_WIN)
|
|
DestroyPluginWindow();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::InitQuirksModes(const nsCString& aMimeType)
|
|
{
|
|
#ifdef OS_WIN
|
|
// application/x-silverlight
|
|
// application/x-silverlight-2
|
|
NS_NAMED_LITERAL_CSTRING(silverlight, "application/x-silverlight");
|
|
// application/x-shockwave-flash
|
|
NS_NAMED_LITERAL_CSTRING(flash, "application/x-shockwave-flash");
|
|
if (FindInReadable(silverlight, aMimeType)) {
|
|
mQuirks |= QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION;
|
|
mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
|
|
}
|
|
else if (FindInReadable(flash, aMimeType)) {
|
|
mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
NPError
|
|
PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
|
|
NPObject** aObject)
|
|
{
|
|
PluginScriptableObjectChild* actor;
|
|
NPError result = NPERR_NO_ERROR;
|
|
|
|
switch (aValue) {
|
|
case NPNVWindowNPObject:
|
|
if (!(actor = mCachedWindowActor)) {
|
|
PPluginScriptableObjectChild* actorProtocol;
|
|
CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result);
|
|
if (result == NPERR_NO_ERROR) {
|
|
actor = mCachedWindowActor =
|
|
static_cast<PluginScriptableObjectChild*>(actorProtocol);
|
|
NS_ASSERTION(actor, "Null actor!");
|
|
PluginModuleChild::sBrowserFuncs.retainobject(
|
|
actor->GetObject(false));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NPNVPluginElementNPObject:
|
|
if (!(actor = mCachedElementActor)) {
|
|
PPluginScriptableObjectChild* actorProtocol;
|
|
CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol,
|
|
&result);
|
|
if (result == NPERR_NO_ERROR) {
|
|
actor = mCachedElementActor =
|
|
static_cast<PluginScriptableObjectChild*>(actorProtocol);
|
|
NS_ASSERTION(actor, "Null actor!");
|
|
PluginModuleChild::sBrowserFuncs.retainobject(
|
|
actor->GetObject(false));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
NS_NOTREACHED("Don't know what to do with this value type!");
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
NPError currentResult;
|
|
PPluginScriptableObjectChild* currentActor;
|
|
|
|
switch (aValue) {
|
|
case NPNVWindowNPObject:
|
|
CallNPN_GetValue_NPNVWindowNPObject(¤tActor,
|
|
¤tResult);
|
|
break;
|
|
case NPNVPluginElementNPObject:
|
|
CallNPN_GetValue_NPNVPluginElementNPObject(¤tActor,
|
|
¤tResult);
|
|
break;
|
|
default:
|
|
NS_NOTREACHED("Don't know what to do with this value type!");
|
|
}
|
|
|
|
// Make sure that the current actor returned by the parent matches our
|
|
// cached actor!
|
|
NS_ASSERTION(static_cast<PluginScriptableObjectChild*>(currentActor) ==
|
|
actor, "Cached actor is out of date!");
|
|
NS_ASSERTION(currentResult == result, "Results don't match?!");
|
|
}
|
|
#endif
|
|
|
|
if (result != NPERR_NO_ERROR) {
|
|
return result;
|
|
}
|
|
|
|
NPObject* object = actor->GetObject(false);
|
|
NS_ASSERTION(object, "Null object?!");
|
|
|
|
*aObject = PluginModuleChild::sBrowserFuncs.retainobject(object);
|
|
return NPERR_NO_ERROR;
|
|
|
|
}
|
|
|
|
NPError
|
|
PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
|
|
void* aValue)
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar));
|
|
AssertPluginThread();
|
|
|
|
switch(aVar) {
|
|
|
|
case NPNVSupportsWindowless:
|
|
#if defined(OS_LINUX) || defined(OS_WIN)
|
|
*((NPBool*)aValue) = true;
|
|
#else
|
|
*((NPBool*)aValue) = false;
|
|
#endif
|
|
return NPERR_NO_ERROR;
|
|
|
|
#if defined(OS_LINUX)
|
|
case NPNVSupportsXEmbedBool:
|
|
*((NPBool*)aValue) = true;
|
|
return NPERR_NO_ERROR;
|
|
|
|
case NPNVToolkit:
|
|
*((NPNToolkitType*)aValue) = NPNVGtk2;
|
|
return NPERR_NO_ERROR;
|
|
|
|
#elif defined(OS_WIN)
|
|
case NPNVToolkit:
|
|
return NPERR_GENERIC_ERROR;
|
|
#endif
|
|
case NPNVjavascriptEnabledBool: {
|
|
bool v = false;
|
|
NPError result;
|
|
if (!CallNPN_GetValue_NPNVjavascriptEnabledBool(&v, &result)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
*static_cast<NPBool*>(aValue) = v;
|
|
return result;
|
|
}
|
|
|
|
case NPNVisOfflineBool: {
|
|
bool v = false;
|
|
NPError result;
|
|
if (!CallNPN_GetValue_NPNVisOfflineBool(&v, &result)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
*static_cast<NPBool*>(aValue) = v;
|
|
return result;
|
|
}
|
|
|
|
case NPNVprivateModeBool: {
|
|
bool v = false;
|
|
NPError result;
|
|
if (!CallNPN_GetValue_NPNVprivateModeBool(&v, &result)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
*static_cast<NPBool*>(aValue) = v;
|
|
return result;
|
|
}
|
|
|
|
case NPNVWindowNPObject: // Intentional fall-through
|
|
case NPNVPluginElementNPObject: {
|
|
NPObject* object;
|
|
NPError result = InternalGetNPObjectForValue(aVar, &object);
|
|
if (result == NPERR_NO_ERROR) {
|
|
*((NPObject**)aValue) = object;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
case NPNVnetscapeWindow: {
|
|
#ifdef XP_WIN
|
|
if (mWindow.type == NPWindowTypeDrawable) {
|
|
if (mCachedWinlessPluginHWND) {
|
|
*static_cast<HWND*>(aValue) = mCachedWinlessPluginHWND;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
NPError result;
|
|
if (!CallNPN_GetValue_NPNVnetscapeWindow(&mCachedWinlessPluginHWND, &result)) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
*static_cast<HWND*>(aValue) = mCachedWinlessPluginHWND;
|
|
return result;
|
|
}
|
|
else {
|
|
*static_cast<HWND*>(aValue) = mPluginWindowHWND;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
#elif defined(MOZ_X11)
|
|
NPError result;
|
|
CallNPN_GetValue_NPNVnetscapeWindow(static_cast<XID*>(aValue), &result);
|
|
return result;
|
|
#else
|
|
return NPERR_GENERIC_ERROR;
|
|
#endif
|
|
}
|
|
|
|
default:
|
|
PR_LOG(gPluginLog, PR_LOG_WARNING,
|
|
("In PluginInstanceChild::NPN_GetValue: Unhandled NPNVariable %i (%s)",
|
|
(int) aVar, NPNVariableToString(aVar)));
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NPError
|
|
PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
|
|
{
|
|
PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s (aVar=%i, aValue=%p)",
|
|
FULLFUNCTION, (int) aVar, aValue));
|
|
|
|
AssertPluginThread();
|
|
|
|
switch (aVar) {
|
|
case NPPVpluginWindowBool: {
|
|
NPError rv;
|
|
bool windowed = (NPBool) (intptr_t) aValue;
|
|
|
|
if (!CallNPN_SetValue_NPPVpluginWindow(windowed, &rv))
|
|
return NPERR_GENERIC_ERROR;
|
|
|
|
return rv;
|
|
}
|
|
|
|
case NPPVpluginTransparentBool: {
|
|
NPError rv;
|
|
bool transparent = (NPBool) (intptr_t) aValue;
|
|
|
|
if (!CallNPN_SetValue_NPPVpluginTransparent(transparent, &rv))
|
|
return NPERR_GENERIC_ERROR;
|
|
|
|
return rv;
|
|
}
|
|
|
|
default:
|
|
PR_LOG(gPluginLog, PR_LOG_WARNING,
|
|
("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i (%s)",
|
|
(int) aVar, NPPVariableToString(aVar)));
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(
|
|
bool* needs, NPError* rv)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
#ifdef MOZ_X11
|
|
// The documentation on the types for many variables in NP(N|P)_GetValue
|
|
// is vague. Often boolean values are NPBool (1 byte), but
|
|
// https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins
|
|
// treats NPPVpluginNeedsXEmbed as PRBool (int), and
|
|
// on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|.
|
|
// thus we can't use NPBool for needsXEmbed, or the three bytes above
|
|
// it on the stack would get clobbered. so protect with the larger PRBool.
|
|
PRBool needsXEmbed = 0;
|
|
if (!mPluginIface->getvalue) {
|
|
*rv = NPERR_GENERIC_ERROR;
|
|
}
|
|
else {
|
|
*rv = mPluginIface->getvalue(GetNPP(), NPPVpluginNeedsXEmbed,
|
|
&needsXEmbed);
|
|
}
|
|
*needs = needsXEmbed;
|
|
return true;
|
|
|
|
#else
|
|
|
|
NS_RUNTIMEABORT("shouldn't be called on non-X11 platforms");
|
|
return false; // not reached
|
|
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginScriptableNPObject(
|
|
PPluginScriptableObjectChild** aValue,
|
|
NPError* aResult)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
NPObject* object = nsnull;
|
|
NPError result = NPERR_GENERIC_ERROR;
|
|
if (mPluginIface->getvalue) {
|
|
result = mPluginIface->getvalue(GetNPP(), NPPVpluginScriptableNPObject,
|
|
&object);
|
|
}
|
|
if (result == NPERR_NO_ERROR && object) {
|
|
PluginScriptableObjectChild* actor = GetActorForNPObject(object);
|
|
|
|
// If we get an actor then it has retained. Otherwise we don't need it
|
|
// any longer.
|
|
PluginModuleChild::sBrowserFuncs.releaseobject(object);
|
|
if (actor) {
|
|
*aValue = actor;
|
|
*aResult = NPERR_NO_ERROR;
|
|
return true;
|
|
}
|
|
|
|
NS_ERROR("Failed to get actor!");
|
|
result = NPERR_GENERIC_ERROR;
|
|
}
|
|
else {
|
|
result = NPERR_GENERIC_ERROR;
|
|
}
|
|
|
|
*aValue = nsnull;
|
|
*aResult = result;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value,
|
|
NPError* result)
|
|
{
|
|
if (!mPluginIface->setvalue) {
|
|
*result = NPERR_GENERIC_ERROR;
|
|
return true;
|
|
}
|
|
|
|
NPBool v = value;
|
|
*result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
|
|
int16_t* handled)
|
|
{
|
|
PLUGIN_LOG_DEBUG_FUNCTION;
|
|
AssertPluginThread();
|
|
|
|
#if defined(OS_LINUX) && defined(DEBUG)
|
|
if (GraphicsExpose == event.event.type)
|
|
PLUGIN_LOG_DEBUG((" received drawable 0x%lx\n",
|
|
event.event.xgraphicsexpose.drawable));
|
|
#endif
|
|
|
|
#ifdef OS_MACOSX
|
|
// Mac OS X does not define an NPEvent structure. It defines more specific types.
|
|
NPCocoaEvent evcopy = event.event;
|
|
#else
|
|
// Make a copy since we may modify values.
|
|
NPEvent evcopy = event.event;
|
|
#endif
|
|
|
|
#ifdef OS_WIN
|
|
// Painting for win32. SharedSurfacePaint handles everything.
|
|
if (mWindow.type == NPWindowTypeDrawable) {
|
|
if (evcopy.event == WM_PAINT) {
|
|
*handled = SharedSurfacePaint(evcopy);
|
|
return true;
|
|
}
|
|
else if (evcopy.event == mAlphaExtract.doublePassEvent) {
|
|
// We'll render to mSharedSurfaceDib first, then render to a cached bitmap
|
|
// we store locally. The two passes are for alpha extraction, so the second
|
|
// pass must be to a flat white surface in order for things to work.
|
|
mAlphaExtract.doublePass = RENDER_BACK_ONE;
|
|
*handled = true;
|
|
return true;
|
|
}
|
|
}
|
|
*handled = WinlessHandleEvent(evcopy);
|
|
return true;
|
|
#endif
|
|
|
|
if (!mPluginIface->event)
|
|
*handled = false;
|
|
else
|
|
*handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
|
|
|
|
#ifdef MOZ_X11
|
|
if (GraphicsExpose == event.event.type) {
|
|
// Make sure the X server completes the drawing before the parent
|
|
// draws on top and destroys the Drawable.
|
|
//
|
|
// XSync() waits for the X server to complete. Really this child
|
|
// process does not need to wait; the parent is the process that needs
|
|
// to wait. A possibly-slightly-better alternative would be to send
|
|
// an X event to the parent that the parent would wait for.
|
|
XSync(mWsInfo.display, False);
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event)
|
|
{
|
|
#ifdef OS_WIN
|
|
int16_t dontcare;
|
|
return AnswerNPP_HandleEvent(event, &dontcare);
|
|
#else
|
|
NS_RUNTIMEABORT("WindowPosChanged is a windows-only message");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
|
|
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
|
|
static bool
|
|
XVisualIDToInfo(Display* aDisplay, VisualID aVisualID,
|
|
Visual** aVisual, unsigned int* aDepth)
|
|
{
|
|
if (aVisualID == None) {
|
|
*aVisual = NULL;
|
|
*aDepth = 0;
|
|
return true;
|
|
}
|
|
|
|
const Screen* screen = DefaultScreenOfDisplay(aDisplay);
|
|
|
|
for (int d = 0; d < screen->ndepths; d++) {
|
|
Depth *d_info = &screen->depths[d];
|
|
for (int v = 0; v < d_info->nvisuals; v++) {
|
|
Visual* visual = &d_info->visuals[v];
|
|
if (visual->visualid == aVisualID) {
|
|
*aVisual = visual;
|
|
*aDepth = d_info->depth;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_ERROR("VisualID not on Screen.");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
|
|
FULLFUNCTION,
|
|
aWindow.window,
|
|
aWindow.x, aWindow.y,
|
|
aWindow.width, aWindow.height));
|
|
AssertPluginThread();
|
|
|
|
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
|
|
// The minimum info is sent over IPC to allow this
|
|
// code to determine the rest.
|
|
|
|
mWindow.window = reinterpret_cast<void*>(aWindow.window);
|
|
mWindow.x = aWindow.x;
|
|
mWindow.y = aWindow.y;
|
|
mWindow.width = aWindow.width;
|
|
mWindow.height = aWindow.height;
|
|
mWindow.clipRect = aWindow.clipRect;
|
|
mWindow.type = aWindow.type;
|
|
|
|
mWsInfo.colormap = aWindow.colormap;
|
|
if (!XVisualIDToInfo(mWsInfo.display, aWindow.visualID,
|
|
&mWsInfo.visual, &mWsInfo.depth))
|
|
return false;
|
|
|
|
if (aWindow.type == NPWindowTypeWindow) {
|
|
#ifdef MOZ_WIDGET_GTK2
|
|
if (GdkWindow* socket_window = gdk_window_lookup(aWindow.window)) {
|
|
// A GdkWindow for the socket already exists. Need to
|
|
// workaround https://bugzilla.gnome.org/show_bug.cgi?id=607061
|
|
// See wrap_gtk_plug_embedded in PluginModuleChild.cpp.
|
|
g_object_set_data(G_OBJECT(socket_window),
|
|
"moz-existed-before-set-window",
|
|
GUINT_TO_POINTER(1));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (mPluginIface->setwindow)
|
|
(void) mPluginIface->setwindow(&mData, &mWindow);
|
|
|
|
#elif defined(OS_WIN)
|
|
switch (aWindow.type) {
|
|
case NPWindowTypeWindow:
|
|
{
|
|
if (!CreatePluginWindow())
|
|
return false;
|
|
|
|
ReparentPluginWindow((HWND)aWindow.window);
|
|
SizePluginWindow(aWindow.width, aWindow.height);
|
|
|
|
mWindow.window = (void*)mPluginWindowHWND;
|
|
mWindow.x = aWindow.x;
|
|
mWindow.y = aWindow.y;
|
|
mWindow.width = aWindow.width;
|
|
mWindow.height = aWindow.height;
|
|
mWindow.type = aWindow.type;
|
|
|
|
if (mPluginIface->setwindow) {
|
|
(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>(PluginWindowProc)));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NPWindowTypeDrawable:
|
|
if (mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK)
|
|
CreateWinlessPopupSurrogate();
|
|
return SharedSurfaceSetWindow(aWindow);
|
|
break;
|
|
|
|
default:
|
|
NS_NOTREACHED("Bad plugin window type.");
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
#elif defined(OS_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;
|
|
|
|
#else
|
|
# error Implement me for your OS
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::Initialize()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#if defined(OS_WIN)
|
|
|
|
static const TCHAR kWindowClassName[] = TEXT("GeckoPluginWindow");
|
|
static const TCHAR kPluginInstanceChildProperty[] = TEXT("PluginInstanceChildProperty");
|
|
|
|
// static
|
|
bool
|
|
PluginInstanceChild::RegisterWindowClass()
|
|
{
|
|
static bool alreadyRegistered = false;
|
|
if (alreadyRegistered)
|
|
return true;
|
|
|
|
alreadyRegistered = true;
|
|
|
|
WNDCLASSEX wcex;
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
wcex.style = CS_DBLCLKS;
|
|
wcex.lpfnWndProc = DummyWindowProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = GetModuleHandle(NULL);
|
|
wcex.hIcon = 0;
|
|
wcex.hCursor = 0;
|
|
wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
|
|
wcex.lpszMenuName = 0;
|
|
wcex.lpszClassName = kWindowClassName;
|
|
wcex.hIconSm = 0;
|
|
|
|
return RegisterClassEx(&wcex) ? true : false;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::CreatePluginWindow()
|
|
{
|
|
// already initialized
|
|
if (mPluginWindowHWND)
|
|
return true;
|
|
|
|
if (!RegisterWindowClass())
|
|
return false;
|
|
|
|
mPluginWindowHWND =
|
|
CreateWindowEx(WS_EX_LEFT | WS_EX_LTRREADING |
|
|
WS_EX_NOPARENTNOTIFY | // XXXbent Get rid of this!
|
|
WS_EX_RIGHTSCROLLBAR,
|
|
kWindowClassName, 0,
|
|
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0,
|
|
0, 0, NULL, 0, GetModuleHandle(NULL), 0);
|
|
if (!mPluginWindowHWND)
|
|
return false;
|
|
if (!SetProp(mPluginWindowHWND, kPluginInstanceChildProperty, this))
|
|
return false;
|
|
|
|
// Apparently some plugins require an ASCII WndProc.
|
|
SetWindowLongPtrA(mPluginWindowHWND, GWLP_WNDPROC,
|
|
reinterpret_cast<LONG>(DefWindowProcA));
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::DestroyPluginWindow()
|
|
{
|
|
if (mPluginWindowHWND) {
|
|
// Unsubclass the window.
|
|
WNDPROC wndProc = reinterpret_cast<WNDPROC>(
|
|
GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC));
|
|
if (wndProc == PluginWindowProc) {
|
|
NS_ASSERTION(mPluginWndProc, "Should have old proc here!");
|
|
SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC,
|
|
reinterpret_cast<LONG>(mPluginWndProc));
|
|
mPluginWndProc = 0;
|
|
}
|
|
|
|
RemoveProp(mPluginWindowHWND, kPluginInstanceChildProperty);
|
|
DestroyWindow(mPluginWindowHWND);
|
|
mPluginWindowHWND = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::ReparentPluginWindow(HWND hWndParent)
|
|
{
|
|
if (hWndParent != mPluginParentHWND && IsWindow(hWndParent)) {
|
|
// Fix the child window's style to be a child window.
|
|
LONG style = GetWindowLongPtr(mPluginWindowHWND, GWL_STYLE);
|
|
style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
|
style &= ~WS_POPUP;
|
|
SetWindowLongPtr(mPluginWindowHWND, GWL_STYLE, style);
|
|
|
|
// Do the reparenting.
|
|
SetParent(mPluginWindowHWND, hWndParent);
|
|
|
|
// Make sure we're visible.
|
|
ShowWindow(mPluginWindowHWND, SW_SHOWNA);
|
|
}
|
|
mPluginParentHWND = hWndParent;
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::SizePluginWindow(int width,
|
|
int height)
|
|
{
|
|
if (mPluginWindowHWND) {
|
|
mPluginSize.x = width;
|
|
mPluginSize.y = height;
|
|
SetWindowPos(mPluginWindowHWND, NULL, 0, 0, width, height,
|
|
SWP_NOZORDER | SWP_NOREPOSITION);
|
|
}
|
|
}
|
|
|
|
// See chromium's webplugin_delegate_impl.cc for explanation of this function.
|
|
// static
|
|
LRESULT CALLBACK
|
|
PluginInstanceChild::DummyWindowProc(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
return CallWindowProc(DefWindowProc, hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
// static
|
|
LRESULT CALLBACK
|
|
PluginInstanceChild::PluginWindowProc(HWND hWnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(),
|
|
"Failed to prevent a nonqueued message from running!");
|
|
|
|
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
|
|
GetProp(hWnd, kPluginInstanceChildProperty));
|
|
if (!self) {
|
|
NS_NOTREACHED("Badness!");
|
|
return 0;
|
|
}
|
|
|
|
NS_ASSERTION(self->mPluginWindowHWND == hWnd, "Wrong window!");
|
|
|
|
// Adobe's shockwave positions the plugin window relative to the browser
|
|
// frame when it initializes. With oopp disabled, this wouldn't have an
|
|
// effect. With oopp, GeckoPluginWindow is a child of the parent plugin
|
|
// window, so the move offsets the child within the parent. Generally
|
|
// we don't want plugins moving or sizing our window, so we prevent these
|
|
// changes here.
|
|
if (message == WM_WINDOWPOSCHANGING) {
|
|
WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lParam);
|
|
if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE))) {
|
|
pos->x = pos->y = 0;
|
|
pos->cx = self->mPluginSize.x;
|
|
pos->cy = self->mPluginSize.y;
|
|
LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
|
|
lParam);
|
|
pos->x = pos->y = 0;
|
|
pos->cx = self->mPluginSize.x;
|
|
pos->cy = self->mPluginSize.y;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// The plugin received keyboard focus, let the parent know so the dom is up to date.
|
|
if (message == WM_MOUSEACTIVATE)
|
|
self->CallPluginGotFocus();
|
|
|
|
// Prevent lockups due to plugins making rpc calls when the parent
|
|
// is making a synchronous SetFocus api call. (bug 541362) Add more
|
|
// windowing events as needed for other api.
|
|
if (message == WM_KILLFOCUS &&
|
|
((InSendMessageEx(NULL) & (ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND)) {
|
|
ReplyMessage(0); // Unblock the caller
|
|
}
|
|
|
|
LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
|
|
lParam);
|
|
|
|
if (message == WM_CLOSE)
|
|
self->DestroyPluginWindow();
|
|
|
|
if (message == WM_NCDESTROY)
|
|
RemoveProp(hWnd, kPluginInstanceChildProperty);
|
|
|
|
return res;
|
|
}
|
|
|
|
/* winless modal ui loop logic */
|
|
|
|
// gTempChildPointer is only in use from the time we enter handle event, to the
|
|
// point where ui might be created by that call. If ui isn't created, there's
|
|
// no issue. If ui is created, the parent can't start processing messages in
|
|
// spin loop until InternalCallSetNestedEventState is set, at which point,
|
|
// gTempChildPointer is no longer needed.
|
|
static PluginInstanceChild* gTempChildPointer;
|
|
|
|
LRESULT CALLBACK
|
|
PluginInstanceChild::NestedInputEventHook(int nCode,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
if (!gTempChildPointer) {
|
|
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
|
}
|
|
|
|
if (nCode >= 0) {
|
|
NS_ASSERTION(gTempChildPointer, "Never should be null here!");
|
|
gTempChildPointer->ResetNestedEventHook();
|
|
gTempChildPointer->InternalCallSetNestedEventState(true);
|
|
|
|
gTempChildPointer = NULL;
|
|
}
|
|
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::SetNestedInputEventHook()
|
|
{
|
|
NS_ASSERTION(!mNestedEventHook,
|
|
"mNestedEventHook already setup in call to SetNestedInputEventHook?");
|
|
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
|
|
// WH_GETMESSAGE hooks are triggered by peek message calls in parent due to
|
|
// attached message queues, resulting in stomped in-process ipc calls. So
|
|
// we use a filter hook specific to dialogs, menus, and scroll bars to kick
|
|
// things off.
|
|
mNestedEventHook = SetWindowsHookEx(WH_MSGFILTER,
|
|
NestedInputEventHook,
|
|
NULL,
|
|
GetCurrentThreadId());
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::ResetNestedEventHook()
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
if (mNestedEventHook)
|
|
UnhookWindowsHookEx(mNestedEventHook);
|
|
mNestedEventHook = NULL;
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::InternalCallSetNestedEventState(bool aState)
|
|
{
|
|
if (aState != mNestedEventState) {
|
|
PLUGIN_LOG_DEBUG(
|
|
("PluginInstanceChild::InternalCallSetNestedEventState(%i)",
|
|
(int)aState));
|
|
mNestedEventState = aState;
|
|
SendSetNestedEventState(mNestedEventState);
|
|
}
|
|
}
|
|
|
|
/* windowless track popup menu helpers */
|
|
|
|
BOOL
|
|
WINAPI
|
|
PluginInstanceChild::TrackPopupHookProc(HMENU hMenu,
|
|
UINT uFlags,
|
|
int x,
|
|
int y,
|
|
int nReserved,
|
|
HWND hWnd,
|
|
CONST RECT *prcRect)
|
|
{
|
|
if (!sUser32TrackPopupMenuStub) {
|
|
NS_ERROR("TrackPopupMenu stub isn't set! Badness!");
|
|
return 0;
|
|
}
|
|
|
|
// Only change the parent when we know this is a context on the plugin
|
|
// surface within the browser. Prevents resetting the parent on child ui
|
|
// displayed by plugins that have working parent-child relationships.
|
|
PRUnichar szClass[21];
|
|
bool haveClass = GetClassNameW(hWnd, szClass, NS_ARRAY_LENGTH(szClass));
|
|
if (!haveClass ||
|
|
(wcscmp(szClass, L"MozillaWindowClass") &&
|
|
wcscmp(szClass, L"SWFlash_Placeholder"))) {
|
|
// Unrecognized parent
|
|
return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
|
|
hWnd, prcRect);
|
|
}
|
|
|
|
// Called on an unexpected event, warn.
|
|
if (!sWinlessPopupSurrogateHWND) {
|
|
NS_WARNING(
|
|
"Untraced TrackPopupHookProc call! Menu might not work right!");
|
|
return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
|
|
hWnd, prcRect);
|
|
}
|
|
|
|
HWND surrogateHwnd = sWinlessPopupSurrogateHWND;
|
|
sWinlessPopupSurrogateHWND = NULL;
|
|
|
|
// Popups that don't use TPM_RETURNCMD expect a final command message
|
|
// when an item is selected and the context closes. Since we replace
|
|
// the parent, we need to forward this back to the real parent so it
|
|
// can act on the menu item selected.
|
|
bool isRetCmdCall = (uFlags & TPM_RETURNCMD);
|
|
|
|
// A little trick scrounged from chromium's code - set the focus
|
|
// to our surrogate parent so keyboard nav events go to the menu.
|
|
HWND focusHwnd = SetFocus(surrogateHwnd);
|
|
DWORD res = sUser32TrackPopupMenuStub(hMenu, uFlags|TPM_RETURNCMD, x, y,
|
|
nReserved, surrogateHwnd, prcRect);
|
|
if (IsWindow(focusHwnd)) {
|
|
SetFocus(focusHwnd);
|
|
}
|
|
|
|
if (!isRetCmdCall && res) {
|
|
SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(res, 0), 0);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::InitPopupMenuHook()
|
|
{
|
|
if (!(mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) ||
|
|
sUser32TrackPopupMenuStub)
|
|
return;
|
|
|
|
// Note, once WindowsDllInterceptor is initialized for a module,
|
|
// it remains initialized for that particular module for it's
|
|
// lifetime. Additional instances are needed if other modules need
|
|
// to be hooked.
|
|
sUser32Intercept.Init("user32.dll");
|
|
sUser32Intercept.AddHook("TrackPopupMenu", TrackPopupHookProc,
|
|
(void**) &sUser32TrackPopupMenuStub);
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::CreateWinlessPopupSurrogate()
|
|
{
|
|
// already initialized
|
|
if (mWinlessPopupSurrogateHWND)
|
|
return;
|
|
|
|
HWND hwnd = NULL;
|
|
NPError result;
|
|
if (!CallNPN_GetValue_NPNVnetscapeWindow(&hwnd, &result)) {
|
|
NS_ERROR("CallNPN_GetValue_NPNVnetscapeWindow failed.");
|
|
return;
|
|
}
|
|
|
|
mWinlessPopupSurrogateHWND =
|
|
CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", NULL, WS_CHILD, 0, 0,
|
|
0, 0, hwnd, 0, GetModuleHandle(NULL), 0);
|
|
if (!mWinlessPopupSurrogateHWND) {
|
|
NS_ERROR("CreateWindowEx failed for winless placeholder!");
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::DestroyWinlessPopupSurrogate()
|
|
{
|
|
if (mWinlessPopupSurrogateHWND)
|
|
DestroyWindow(mWinlessPopupSurrogateHWND);
|
|
mWinlessPopupSurrogateHWND = NULL;
|
|
}
|
|
|
|
/* windowless handle event helpers */
|
|
|
|
static bool
|
|
NeedsNestedEventCoverage(UINT msg)
|
|
{
|
|
// Events we assume some sort of modal ui *might* be generated.
|
|
switch (msg) {
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_CONTEXTMENU:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
IsMouseInputEvent(UINT msg)
|
|
{
|
|
switch (msg) {
|
|
case WM_MOUSEMOVE:
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_MBUTTONDBLCLK:
|
|
case WM_RBUTTONDBLCLK:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int16_t
|
|
PluginInstanceChild::WinlessHandleEvent(NPEvent& event)
|
|
{
|
|
if (!mPluginIface->event)
|
|
return false;
|
|
|
|
// Winless Silverlight quirk: winposchanged events are not used in
|
|
// determining the position of the plugin within the parent window,
|
|
// NPP_SetWindow values are used instead. Due to shared memory dib
|
|
// rendering, the origin of NPP_SetWindow is 0x0, so we trap
|
|
// winposchanged events here and do the translation internally for
|
|
// mouse input events.
|
|
if (mQuirks & QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION) {
|
|
if (event.event == WM_WINDOWPOSCHANGED && event.lParam) {
|
|
WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(event.lParam);
|
|
mPluginOffset.x = pos->x;
|
|
mPluginOffset.y = pos->y;
|
|
}
|
|
else if (IsMouseInputEvent(event.event)) {
|
|
event.lParam =
|
|
MAKELPARAM((GET_X_LPARAM(event.lParam) - mPluginOffset.x),
|
|
(GET_Y_LPARAM(event.lParam) - mPluginOffset.y));
|
|
}
|
|
}
|
|
|
|
if (!NeedsNestedEventCoverage(event.event)) {
|
|
return mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
|
|
}
|
|
|
|
// Events that might generate nested event dispatch loops need
|
|
// special handling during delivery.
|
|
int16_t handled;
|
|
|
|
mNestedEventLevelDepth++;
|
|
PLUGIN_LOG_DEBUG(("WinlessHandleEvent start depth: %i", mNestedEventLevelDepth));
|
|
|
|
// On the first, non-reentrant call, setup our modal ui detection hook.
|
|
if (mNestedEventLevelDepth == 1) {
|
|
NS_ASSERTION(!gTempChildPointer, "valid gTempChildPointer here?");
|
|
gTempChildPointer = this;
|
|
SetNestedInputEventHook();
|
|
}
|
|
|
|
// TrackPopupMenu will fail if the parent window is not associated with
|
|
// our ui thread. So we hook TrackPopupMenu so we can hand in a surrogate
|
|
// parent created in the child process.
|
|
if ((mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) && // XXX turn on by default?
|
|
(event.event == WM_RBUTTONDOWN || // flash
|
|
event.event == WM_RBUTTONUP)) { // silverlight
|
|
sWinlessPopupSurrogateHWND = mWinlessPopupSurrogateHWND;
|
|
}
|
|
|
|
bool old_state = MessageLoop::current()->NestableTasksAllowed();
|
|
MessageLoop::current()->SetNestableTasksAllowed(true);
|
|
handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
|
|
MessageLoop::current()->SetNestableTasksAllowed(old_state);
|
|
|
|
gTempChildPointer = NULL;
|
|
sWinlessPopupSurrogateHWND = NULL;
|
|
|
|
mNestedEventLevelDepth--;
|
|
PLUGIN_LOG_DEBUG(("WinlessHandleEvent end depth: %i", mNestedEventLevelDepth));
|
|
|
|
NS_ASSERTION(!(mNestedEventLevelDepth < 0), "mNestedEventLevelDepth < 0?");
|
|
if (mNestedEventLevelDepth <= 0) {
|
|
ResetNestedEventHook();
|
|
InternalCallSetNestedEventState(false);
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
/* windowless drawing helpers */
|
|
|
|
bool
|
|
PluginInstanceChild::SharedSurfaceSetWindow(const NPRemoteWindow& aWindow)
|
|
{
|
|
// If the surfaceHandle is empty, parent is telling us we can reuse our cached
|
|
// memory surface and hdc. Otherwise, we need to reset, usually due to a
|
|
// expanding plugin port size.
|
|
if (!aWindow.surfaceHandle) {
|
|
if (!mSharedSurfaceDib.IsValid()) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
// Attach to the new shared surface parent handed us.
|
|
if (NS_FAILED(mSharedSurfaceDib.Attach((SharedDIB::Handle)aWindow.surfaceHandle,
|
|
aWindow.width, aWindow.height, 32)))
|
|
return false;
|
|
// Free any alpha extraction resources if needed. This will be reset
|
|
// the next time it's used.
|
|
AlphaExtractCacheRelease();
|
|
}
|
|
|
|
// NPRemoteWindow's origin is the origin of our shared dib.
|
|
mWindow.x = 0;
|
|
mWindow.y = 0;
|
|
mWindow.width = aWindow.width;
|
|
mWindow.height = aWindow.height;
|
|
mWindow.type = aWindow.type;
|
|
|
|
mWindow.window = reinterpret_cast<void*>(mSharedSurfaceDib.GetHDC());
|
|
|
|
if (mPluginIface->setwindow)
|
|
mPluginIface->setwindow(&mData, &mWindow);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::SharedSurfaceRelease()
|
|
{
|
|
mSharedSurfaceDib.Close();
|
|
AlphaExtractCacheRelease();
|
|
}
|
|
|
|
/* double pass cache buffer - (rarely) used in cases where alpha extraction
|
|
* occurs for windowless plugins. */
|
|
|
|
bool
|
|
PluginInstanceChild::AlphaExtractCacheSetup()
|
|
{
|
|
AlphaExtractCacheRelease();
|
|
|
|
mAlphaExtract.hdc = ::CreateCompatibleDC(NULL);
|
|
|
|
if (!mAlphaExtract.hdc)
|
|
return false;
|
|
|
|
BITMAPINFOHEADER bmih;
|
|
memset((void*)&bmih, 0, sizeof(BITMAPINFOHEADER));
|
|
bmih.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmih.biWidth = mWindow.width;
|
|
bmih.biHeight = mWindow.height;
|
|
bmih.biPlanes = 1;
|
|
bmih.biBitCount = 32;
|
|
bmih.biCompression = BI_RGB;
|
|
|
|
void* ppvBits = nsnull;
|
|
mAlphaExtract.bmp = ::CreateDIBSection(mAlphaExtract.hdc,
|
|
(BITMAPINFO*)&bmih,
|
|
DIB_RGB_COLORS,
|
|
(void**)&ppvBits,
|
|
NULL,
|
|
(unsigned long)sizeof(BITMAPINFOHEADER));
|
|
if (!mAlphaExtract.bmp)
|
|
return false;
|
|
|
|
DeleteObject(::SelectObject(mAlphaExtract.hdc, mAlphaExtract.bmp));
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::AlphaExtractCacheRelease()
|
|
{
|
|
if (mAlphaExtract.bmp)
|
|
::DeleteObject(mAlphaExtract.bmp);
|
|
|
|
if (mAlphaExtract.hdc)
|
|
::DeleteObject(mAlphaExtract.hdc);
|
|
|
|
mAlphaExtract.bmp = NULL;
|
|
mAlphaExtract.hdc = NULL;
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::UpdatePaintClipRect(RECT* aRect)
|
|
{
|
|
if (aRect) {
|
|
// Update the clip rect on our internal hdc
|
|
HRGN clip = ::CreateRectRgnIndirect(aRect);
|
|
::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip);
|
|
::DeleteObject(clip);
|
|
}
|
|
}
|
|
|
|
int16_t
|
|
PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy)
|
|
{
|
|
if (!mPluginIface->event)
|
|
return false;
|
|
|
|
RECT* pRect = reinterpret_cast<RECT*>(evcopy.lParam);
|
|
|
|
switch(mAlphaExtract.doublePass) {
|
|
case RENDER_NATIVE:
|
|
// pass the internal hdc to the plugin
|
|
UpdatePaintClipRect(pRect);
|
|
evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
|
|
return mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
|
|
break;
|
|
case RENDER_BACK_ONE:
|
|
// Handle a double pass render used in alpha extraction for transparent
|
|
// plugins. (See nsObjectFrame and gfxWindowsNativeDrawing for details.)
|
|
// We render twice, once to the shared dib, and once to a cache which
|
|
// we copy back on a second paint. These paints can't be spread across
|
|
// multiple rpc messages as delays cause animation frame changes.
|
|
if (!mAlphaExtract.bmp && !AlphaExtractCacheSetup()) {
|
|
mAlphaExtract.doublePass = RENDER_NATIVE;
|
|
return false;
|
|
}
|
|
|
|
// See gfxWindowsNativeDrawing, color order doesn't have to match.
|
|
::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
|
|
UpdatePaintClipRect(pRect);
|
|
evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
|
|
if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
|
|
mAlphaExtract.doublePass = RENDER_NATIVE;
|
|
return false;
|
|
}
|
|
|
|
// Copy to cache. We render to shared dib so we don't have to call
|
|
// setwindow between calls (flash issue).
|
|
::BitBlt(mAlphaExtract.hdc,
|
|
pRect->left,
|
|
pRect->top,
|
|
pRect->right - pRect->left,
|
|
pRect->bottom - pRect->top,
|
|
mSharedSurfaceDib.GetHDC(),
|
|
pRect->left,
|
|
pRect->top,
|
|
SRCCOPY);
|
|
|
|
::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
|
|
mAlphaExtract.doublePass = RENDER_NATIVE;
|
|
return false;
|
|
}
|
|
mAlphaExtract.doublePass = RENDER_BACK_TWO;
|
|
return true;
|
|
break;
|
|
case RENDER_BACK_TWO:
|
|
// copy our cached surface back
|
|
::BitBlt(mSharedSurfaceDib.GetHDC(),
|
|
pRect->left,
|
|
pRect->top,
|
|
pRect->right - pRect->left,
|
|
pRect->bottom - pRect->top,
|
|
mAlphaExtract.hdc,
|
|
pRect->left,
|
|
pRect->top,
|
|
SRCCOPY);
|
|
mAlphaExtract.doublePass = RENDER_NATIVE;
|
|
return true;
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endif // OS_WIN
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerSetPluginFocus()
|
|
{
|
|
PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION));
|
|
|
|
#if defined(OS_WIN)
|
|
// Parent is letting us know something set focus to the plugin.
|
|
if (::GetFocus() == mPluginWindowHWND)
|
|
return true;
|
|
::SetFocus(mPluginWindowHWND);
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED("PluginInstanceChild::AnswerSetPluginFocus not implemented!");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerUpdateWindow()
|
|
{
|
|
PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION));
|
|
|
|
#if defined(OS_WIN)
|
|
if (mPluginWindowHWND)
|
|
UpdateWindow(mPluginWindowHWND);
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED("PluginInstanceChild::AnswerUpdateWindow not implemented!");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
PPluginScriptableObjectChild*
|
|
PluginInstanceChild::AllocPPluginScriptableObject()
|
|
{
|
|
AssertPluginThread();
|
|
return new PluginScriptableObjectChild(Proxy);
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::DeallocPPluginScriptableObject(
|
|
PPluginScriptableObjectChild* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
delete aObject;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::RecvPPluginScriptableObjectConstructor(
|
|
PPluginScriptableObjectChild* aActor)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
// This is only called in response to the parent process requesting the
|
|
// creation of an actor. This actor will represent an NPObject that is
|
|
// created by the browser and returned to the plugin.
|
|
PluginScriptableObjectChild* actor =
|
|
static_cast<PluginScriptableObjectChild*>(aActor);
|
|
NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!");
|
|
|
|
actor->InitializeProxy();
|
|
NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerPBrowserStreamConstructor(
|
|
PBrowserStreamChild* aActor,
|
|
const nsCString& url,
|
|
const uint32_t& length,
|
|
const uint32_t& lastmodified,
|
|
PStreamNotifyChild* notifyData,
|
|
const nsCString& headers,
|
|
const nsCString& mimeType,
|
|
const bool& seekable,
|
|
NPError* rv,
|
|
uint16_t* stype)
|
|
{
|
|
AssertPluginThread();
|
|
*rv = static_cast<BrowserStreamChild*>(aActor)
|
|
->StreamConstructed(mimeType, seekable, stype);
|
|
return true;
|
|
}
|
|
|
|
PBrowserStreamChild*
|
|
PluginInstanceChild::AllocPBrowserStream(const nsCString& url,
|
|
const uint32_t& length,
|
|
const uint32_t& lastmodified,
|
|
PStreamNotifyChild* notifyData,
|
|
const nsCString& headers,
|
|
const nsCString& mimeType,
|
|
const bool& seekable,
|
|
NPError* rv,
|
|
uint16_t *stype)
|
|
{
|
|
AssertPluginThread();
|
|
return new BrowserStreamChild(this, url, length, lastmodified,
|
|
static_cast<StreamNotifyChild*>(notifyData),
|
|
headers, mimeType, seekable, rv, stype);
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::DeallocPBrowserStream(PBrowserStreamChild* stream)
|
|
{
|
|
AssertPluginThread();
|
|
delete stream;
|
|
return true;
|
|
}
|
|
|
|
PPluginStreamChild*
|
|
PluginInstanceChild::AllocPPluginStream(const nsCString& mimeType,
|
|
const nsCString& target,
|
|
NPError* result)
|
|
{
|
|
NS_RUNTIMEABORT("not callable");
|
|
return NULL;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::DeallocPPluginStream(PPluginStreamChild* stream)
|
|
{
|
|
AssertPluginThread();
|
|
delete stream;
|
|
return true;
|
|
}
|
|
|
|
PStreamNotifyChild*
|
|
PluginInstanceChild::AllocPStreamNotify(const nsCString& url,
|
|
const nsCString& target,
|
|
const bool& post,
|
|
const nsCString& buffer,
|
|
const bool& file,
|
|
NPError* result)
|
|
{
|
|
AssertPluginThread();
|
|
NS_RUNTIMEABORT("not reached");
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
StreamNotifyChild::ActorDestroy(ActorDestroyReason why)
|
|
{
|
|
if (AncestorDeletion == why && mBrowserStream) {
|
|
NS_ERROR("Pending NPP_URLNotify not called when closing an instance.");
|
|
|
|
// reclaim responsibility for deleting ourself
|
|
mBrowserStream->mStreamNotify = NULL;
|
|
mBrowserStream = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
StreamNotifyChild::SetAssociatedStream(BrowserStreamChild* bs)
|
|
{
|
|
NS_ASSERTION(bs, "Shouldn't be null");
|
|
NS_ASSERTION(!mBrowserStream, "Two streams for one streamnotify?");
|
|
|
|
mBrowserStream = bs;
|
|
}
|
|
|
|
bool
|
|
StreamNotifyChild::Recv__delete__(const NPReason& reason)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (mBrowserStream)
|
|
mBrowserStream->NotifyPending();
|
|
else
|
|
NPP_URLNotify(reason);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
StreamNotifyChild::NPP_URLNotify(NPReason reason)
|
|
{
|
|
PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(Manager());
|
|
|
|
if (mClosure)
|
|
instance->mPluginIface->urlnotify(instance->GetNPP(), mURL.get(),
|
|
reason, mClosure);
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::DeallocPStreamNotify(PStreamNotifyChild* notifyData)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
if (!static_cast<StreamNotifyChild*>(notifyData)->mBrowserStream)
|
|
delete notifyData;
|
|
return true;
|
|
}
|
|
|
|
PluginScriptableObjectChild*
|
|
PluginInstanceChild::GetActorForNPObject(NPObject* aObject)
|
|
{
|
|
AssertPluginThread();
|
|
NS_ASSERTION(aObject, "Null pointer!");
|
|
|
|
if (aObject->_class == PluginScriptableObjectChild::GetClass()) {
|
|
// One of ours! It's a browser-provided object.
|
|
ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
|
|
NS_ASSERTION(object->parent, "Null actor!");
|
|
return object->parent;
|
|
}
|
|
|
|
PluginScriptableObjectChild* actor =
|
|
PluginModuleChild::current()->GetActorForNPObject(aObject);
|
|
if (actor) {
|
|
// Plugin-provided object that we've previously wrapped.
|
|
return actor;
|
|
}
|
|
|
|
actor = new PluginScriptableObjectChild(LocalObject);
|
|
if (!SendPPluginScriptableObjectConstructor(actor)) {
|
|
NS_ERROR("Failed to send constructor message!");
|
|
return nsnull;
|
|
}
|
|
|
|
actor->InitializeLocal(aObject);
|
|
return actor;
|
|
}
|
|
|
|
NPError
|
|
PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
|
|
NPStream** aStream)
|
|
{
|
|
AssertPluginThread();
|
|
|
|
PluginStreamChild* ps = new PluginStreamChild();
|
|
|
|
NPError result;
|
|
CallPPluginStreamConstructor(ps, nsDependentCString(aMIMEType),
|
|
NullableString(aWindow), &result);
|
|
if (NPERR_NO_ERROR != result) {
|
|
*aStream = NULL;
|
|
PPluginStreamChild::Call__delete__(ps, NPERR_GENERIC_ERROR, true);
|
|
return result;
|
|
}
|
|
|
|
*aStream = &ps->mStream;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
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
|
|
|
|
SendNPN_InvalidateRect(*aInvalidRect);
|
|
}
|
|
|
|
uint32_t
|
|
PluginInstanceChild::ScheduleTimer(uint32_t interval, bool repeat,
|
|
TimerFunc func)
|
|
{
|
|
ChildTimer* t = new ChildTimer(this, interval, repeat, func);
|
|
if (0 == t->ID()) {
|
|
delete t;
|
|
return 0;
|
|
}
|
|
|
|
mTimers.AppendElement(t);
|
|
return t->ID();
|
|
}
|
|
|
|
void
|
|
PluginInstanceChild::UnscheduleTimer(uint32_t id)
|
|
{
|
|
if (0 == id)
|
|
return;
|
|
|
|
mTimers.RemoveElement(id, ChildTimer::IDComparator());
|
|
}
|
|
|
|
static PLDHashOperator
|
|
InvalidateObject(DeletingObjectEntry* e, void* userArg)
|
|
{
|
|
NPObject* o = e->GetKey();
|
|
if (!e->mDeleted && o->_class && o->_class->invalidate)
|
|
o->_class->invalidate(o);
|
|
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
static PLDHashOperator
|
|
DeleteObject(DeletingObjectEntry* e, void* userArg)
|
|
{
|
|
NPObject* o = e->GetKey();
|
|
if (!e->mDeleted) {
|
|
e->mDeleted = true;
|
|
|
|
#ifdef NS_BUILD_REFCNT_LOGGING
|
|
{
|
|
int32_t refcnt = o->referenceCount;
|
|
while (refcnt) {
|
|
--refcnt;
|
|
NS_LOG_RELEASE(o, refcnt, "NPObject");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
PluginModuleChild::DeallocNPObject(o);
|
|
}
|
|
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
bool
|
|
PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
AssertPluginThread();
|
|
|
|
nsTArray<PBrowserStreamChild*> streams;
|
|
ManagedPBrowserStreamChild(streams);
|
|
|
|
// First make sure none of these streams become deleted
|
|
for (PRUint32 i = 0; i < streams.Length(); ) {
|
|
if (static_cast<BrowserStreamChild*>(streams[i])->InstanceDying())
|
|
++i;
|
|
else
|
|
streams.RemoveElementAt(i);
|
|
}
|
|
for (PRUint32 i = 0; i < streams.Length(); ++i)
|
|
static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery();
|
|
|
|
for (PRUint32 i = 0; i < mPendingAsyncCalls.Length(); ++i)
|
|
mPendingAsyncCalls[i]->Cancel();
|
|
mPendingAsyncCalls.TruncateLength(0);
|
|
|
|
mTimers.Clear();
|
|
|
|
PluginModuleChild::current()->NPP_Destroy(this);
|
|
mData.ndata = 0;
|
|
|
|
mDeletingHash = new nsTHashtable<DeletingObjectEntry>;
|
|
mDeletingHash->Init();
|
|
PluginModuleChild::current()->FindNPObjectsForInstance(this);
|
|
|
|
mDeletingHash->EnumerateEntries(InvalidateObject, NULL);
|
|
mDeletingHash->EnumerateEntries(DeleteObject, NULL);
|
|
|
|
// Null out our cached actors as they should have been killed in the
|
|
// PluginInstanceDestroyed call above.
|
|
mCachedWindowActor = nsnull;
|
|
mCachedElementActor = nsnull;
|
|
|
|
#if defined(OS_WIN)
|
|
SharedSurfaceRelease();
|
|
ResetNestedEventHook();
|
|
DestroyWinlessPopupSurrogate();
|
|
#endif
|
|
|
|
return true;
|
|
}
|