/*
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2008 Collabora Ltd. All rights reserved.
* Copyright (C) 2008-2009 Torch Mobile, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "PluginView.h"
#include "BitmapImage.h"
#if !PLATFORM(WX)
#include "BitmapInfo.h"
#endif
#include "Bridge.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "Element.h"
#include "EventNames.h"
#include "FrameLoader.h"
#include "FrameLoadRequest.h"
#include "FrameTree.h"
#include "Frame.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "HostWindow.h"
#include "Image.h"
#include "HTMLNames.h"
#include "HTMLPlugInElement.h"
#include "JSDOMWindow.h"
#include "KeyboardEvent.h"
#include "MIMETypeRegistry.h"
#include "MouseEvent.h"
#include "Page.h"
#include "FocusController.h"
#include "PlatformMouseEvent.h"
#include "PluginMessageThrottlerWin.h"
#include "PluginPackage.h"
#include "PluginMainThreadScheduler.h"
#include "RenderWidget.h"
#include "JSDOMBinding.h"
#include "ScriptController.h"
#include "PluginDatabase.h"
#include "PluginDebug.h"
#include "PluginPackage.h"
#include "Settings.h"
#include "c_instance.h"
#include "npruntime_impl.h"
#include "runtime_root.h"
#include <runtime/JSLock.h>
#include <runtime/JSValue.h>
#include <wtf/ASCIICType.h>
#if OS(WINCE)
#undef LOG_NPERROR
#define LOG_NPERROR(x)
#undef LOG_PLUGIN_NET_ERROR
#define LOG_PLUGIN_NET_ERROR()
#endif
#if PLATFORM(CAIRO)
#include <cairo-win32.h>
#endif
#if PLATFORM(QT)
#include "QWebPageClient.h"
#include <QWidget>
#endif
#if PLATFORM(WX)
#include <wx/defs.h>
#include <wx/window.h>
#endif
static inline HWND windowHandleForPageClient(PlatformPageClient client)
{
#if PLATFORM(QT)
if (!client)
return 0;
if (QWidget* pluginParent = qobject_cast<QWidget*>(client->pluginParent()))
return pluginParent->winId();
return 0;
#elif PLATFORM(WX)
if (!client)
return 0;
return (HWND)client->GetHandle();
#else
return client;
#endif
}
using JSC::ExecState;
using JSC::JSLock;
using JSC::JSObject;
using JSC::UString;
using std::min;
using namespace WTF;
namespace WebCore {
using namespace HTMLNames;
const LPCWSTR kWebPluginViewdowClassName = L"WebPluginView";
const LPCWSTR kWebPluginViewProperty = L"WebPluginViewProperty";
#if !OS(WINCE)
// The code used to hook BeginPaint/EndPaint originally came from
// <http://www.fengyuan.com/article/wmprint.html>.
// Copyright (C) 2000 by Feng Yuan (www.fengyuan.com).
static unsigned beginPaintSysCall;
static BYTE* beginPaint;
static unsigned endPaintSysCall;
static BYTE* endPaint;
typedef HDC (WINAPI *PtrBeginPaint)(HWND, PAINTSTRUCT*);
typedef BOOL (WINAPI *PtrEndPaint)(HWND, const PAINTSTRUCT*);
#if OS(WINDOWS) && PLATFORM(X86_64) && COMPILER(MSVC)
extern "C" HDC __stdcall _HBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
extern "C" BOOL __stdcall _HEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint);
#endif
HDC WINAPI PluginView::hookedBeginPaint(HWND hWnd, PAINTSTRUCT* lpPaint)
{
PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty));
if (pluginView && pluginView->m_wmPrintHDC) {
// We're secretly handling WM_PRINTCLIENT, so set up the PAINTSTRUCT so
// that the plugin will paint into the HDC we provide.
memset(lpPaint, 0, sizeof(PAINTSTRUCT));
lpPaint->hdc = pluginView->m_wmPrintHDC;
GetClientRect(hWnd, &lpPaint->rcPaint);
return pluginView->m_wmPrintHDC;
}
#if COMPILER(GCC)
HDC result;
asm ("push %2\n"
"push %3\n"
"call *%4\n"
: "=a" (result)
: "a" (beginPaintSysCall), "g" (lpPaint), "g" (hWnd), "m" (beginPaint)
: "memory"
);
return result;
#elif defined(_M_IX86)
// Call through to the original BeginPaint.
__asm mov eax, beginPaintSysCall
__asm push lpPaint
__asm push hWnd
__asm call beginPaint
#else
return _HBeginPaint(hWnd, lpPaint);
#endif
}
BOOL WINAPI PluginView::hookedEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint)
{
PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty));
if (pluginView && pluginView->m_wmPrintHDC) {
// We're secretly handling WM_PRINTCLIENT, so we don't have to do any
// cleanup.
return TRUE;
}
#if COMPILER(GCC)
BOOL result;
asm ("push %2\n"
"push %3\n"
"call *%4\n"
: "=a" (result)
: "a" (endPaintSysCall), "g" (lpPaint), "g" (hWnd), "m" (endPaint)
);
return result;
#elif defined (_M_IX86)
// Call through to the original EndPaint.
__asm mov eax, endPaintSysCall
__asm push lpPaint
__asm push hWnd
__asm call endPaint
#else
return _HEndPaint(hWnd, lpPaint);
#endif
}
static void hook(const char* module, const char* proc, unsigned& sysCallID, BYTE*& pProc, const void* pNewProc)
{
// See <http://www.fengyuan.com/article/wmprint.html> for an explanation of
// how this function works.
HINSTANCE hMod = GetModuleHandleA(module);
pProc = reinterpret_cast<BYTE*>(reinterpret_cast<ptrdiff_t>(GetProcAddress(hMod, proc)));
#if COMPILER(GCC) || defined(_M_IX86)
if (pProc[0] != 0xB8)
return;
// FIXME: Should we be reading the bytes one-by-one instead of doing an
// unaligned read?
sysCallID = *reinterpret_cast<unsigned*>(pProc + 1);
DWORD flOldProtect;
if (!VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, &flOldProtect))
return;
pProc[0] = 0xE9;
*reinterpret_cast<unsigned*>(pProc + 1) = reinterpret_cast<intptr_t>(pNewProc) - reinterpret_cast<intptr_t>(pProc + 5);
pProc += 5;
#else
/* Disassembly of BeginPaint()
00000000779FC5B0 4C 8B D1 mov r10,rcx
00000000779FC5B3 B8 17 10 00 00 mov eax,1017h
00000000779FC5B8 0F 05 syscall
00000000779FC5BA C3 ret
00000000779FC5BB 90 nop
00000000779FC5BC 90 nop
00000000779FC5BD 90 nop
00000000779FC5BE 90 nop
00000000779FC5BF 90 nop
00000000779FC5C0 90 nop
00000000779FC5C1 90 nop
00000000779FC5C2 90 nop
00000000779FC5C3 90 nop
*/
// Check for the signature as in the above disassembly
DWORD guard = 0xB8D18B4C;
if (*reinterpret_cast<DWORD*>(pProc) != guard)
return;
DWORD flOldProtect;
VirtualProtect(pProc, 12, PAGE_EXECUTE_READWRITE, & flOldProtect);
pProc[0] = 0x48; // mov rax, this
pProc[1] = 0xb8;
*(__int64*)(pProc+2) = (__int64)pNewProc;
pProc[10] = 0xff; // jmp rax
pProc[11] = 0xe0;
#endif
}
static void setUpOffscreenPaintingHooks(HDC (WINAPI*hookedBeginPaint)(HWND, PAINTSTRUCT*), BOOL (WINAPI*hookedEndPaint)(HWND, const PAINTSTRUCT*))
{
static bool haveHooked = false;
if (haveHooked)
return;
haveHooked = true;
// Most (all?) windowed plugins don't seem to respond to WM_PRINTCLIENT, so
// we hook into BeginPaint/EndPaint to allow their normal WM_PAINT handling
// to draw into a given HDC. Note that this hooking affects the entire
// process.
hook("user32.dll", "BeginPaint", beginPaintSysCall, beginPaint, reinterpret_cast<const void *>(reinterpret_cast<ptrdiff_t>(hookedBeginPaint)));
hook("user32.dll", "EndPaint", endPaintSysCall, endPaint, reinterpret_cast<const void *>(reinterpret_cast<ptrdiff_t>(hookedEndPaint)));
}
#endif
static bool registerPluginView()
{
static bool haveRegisteredWindowClass = false;
if (haveRegisteredWindowClass)
return true;
haveRegisteredWindowClass = true;
#if PLATFORM(QT)
Page::setInstanceHandle((HINSTANCE)(qWinAppInst()));
#endif
ASSERT(Page::instanceHandle());
#if OS(WINCE)
WNDCLASS wcex = { 0 };
#else
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.hIconSm = 0;
#endif
wcex.style = CS_DBLCLKS;
#if OS(WINCE)
wcex.style |= CS_PARENTDC;
#endif
wcex.lpfnWndProc = DefWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = Page::instanceHandle();
wcex.hIcon = 0;
wcex.hCursor = LoadCursor(0, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)COLOR_WINDOW;
wcex.lpszMenuName = 0;
wcex.lpszClassName = kWebPluginViewdowClassName;
#if OS(WINCE)
return !!RegisterClass(&wcex);
#else
return !!RegisterClassEx(&wcex);
#endif
}
LRESULT CALLBACK PluginView::PluginViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty));
return pluginView->wndProc(hWnd, message, wParam, lParam);
}
static bool isWindowsMessageUserGesture(UINT message)
{
switch (message) {
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_KEYUP:
return true;
default:
return false;
}
}
LRESULT
PluginView::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// <rdar://5711136> Sometimes Flash will call SetCapture before creating
// a full-screen window and will not release it, which causes the
// full-screen window to never receive mouse events. We set/release capture
// on mouse down/up before sending the event to the plug-in to prevent that.
switch (message) {
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
::SetCapture(hWnd);
break;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
::ReleaseCapture();
break;
}
if (message == m_lastMessage &&
m_plugin->quirks().contains(PluginQuirkDontCallWndProcForSameMessageRecursively) &&
m_isCallingPluginWndProc)
return 1;
if (message == WM_USER + 1 &&
m_plugin->quirks().contains(PluginQuirkThrottleWMUserPlusOneMessages)) {
if (!m_messageThrottler)
m_messageThrottler.set(new PluginMessageThrottlerWin(this));
m_messageThrottler->appendMessage(hWnd, message, wParam, lParam);
return 0;
}
m_lastMessage = message;
m_isCallingPluginWndProc = true;
// If the plug-in doesn't explicitly support changing the pop-up state, we enable
// popups for all user gestures.
// Note that we need to pop the state in a timer, because the Flash plug-in
// pops up windows in response to a posted message.
if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE &&
isWindowsMessageUserGesture(message) && !m_popPopupsStateTimer.isActive()) {
pushPopupsEnabledState(true);
m_popPopupsStateTimer.startOneShot(0);
}
#if !OS(WINCE)
if (message == WM_PRINTCLIENT) {
// Most (all?) windowed plugins don't respond to WM_PRINTCLIENT, so we
// change the message to WM_PAINT and rely on our hooked versions of
// BeginPaint/EndPaint to make the plugin draw into the given HDC.
message = WM_PAINT;
m_wmPrintHDC = reinterpret_cast<HDC>(wParam);
}
#endif
// Call the plug-in's window proc.
LRESULT result = ::CallWindowProc(m_pluginWndProc, hWnd, message, wParam, lParam);
m_wmPrintHDC = 0;
m_isCallingPluginWndProc = false;
return result;
}
void PluginView::updatePluginWidget()
{
if (!parent())
return;
ASSERT(parent()->isFrameView());
FrameView* frameView = static_cast<FrameView*>(parent());
IntRect oldWindowRect = m_windowRect;
IntRect oldClipRect = m_clipRect;
#if OS(WINCE)
m_windowRect = frameView->contentsToWindow(frameRect());
#else
m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
#endif
m_clipRect = windowClipRect();
m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
if (platformPluginWidget() && (!m_haveUpdatedPluginWidget || m_windowRect != oldWindowRect || m_clipRect != oldClipRect)) {
HRGN rgn;
setCallingPlugin(true);
// To prevent flashes while scrolling, we disable drawing during the window
// update process by clipping the window to the zero rect.
bool clipToZeroRect = !m_plugin->quirks().contains(PluginQuirkDontClipToZeroRectWhenScrolling);
if (clipToZeroRect) {
rgn = ::CreateRectRgn(0, 0, 0, 0);
::SetWindowRgn(platformPluginWidget(), rgn, FALSE);
} else {
rgn = ::CreateRectRgn(m_clipRect.x(), m_clipRect.y(), m_clipRect.right(), m_clipRect.bottom());
::SetWindowRgn(platformPluginWidget(), rgn, TRUE);
}
if (!m_haveUpdatedPluginWidget || m_windowRect != oldWindowRect)
::MoveWindow(platformPluginWidget(), m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), TRUE);
if (clipToZeroRect) {
rgn = ::CreateRectRgn(m_clipRect.x(), m_clipRect.y(), m_clipRect.right(), m_clipRect.bottom());
::SetWindowRgn(platformPluginWidget(), rgn, TRUE);
}
setCallingPlugin(false);
m_haveUpdatedPluginWidget = true;
}
}
void PluginView::setFocus()
{
if (platformPluginWidget())
SetFocus(platformPluginWidget());
Widget::setFocus();
}
void PluginView::show()
{
setSelfVisible(true);
if (isParentVisible() && platformPluginWidget())
ShowWindow(platformPluginWidget(), SW_SHOWNA);
Widget::show();
}
void PluginView::hide()
{
setSelfVisible(false);
if (isParentVisible() && platformPluginWidget())
ShowWindow(platformPluginWidget(), SW_HIDE);
Widget::hide();
}
bool PluginView::dispatchNPEvent(NPEvent& npEvent)
{
if (!m_plugin->pluginFuncs()->event)
return true;
bool shouldPop = false;
if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE && isWindowsMessageUserGesture(npEvent.event)) {
pushPopupsEnabledState(true);
shouldPop = true;
}
JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
setCallingPlugin(true);
bool result = m_plugin->pluginFuncs()->event(m_instance, &npEvent);
setCallingPlugin(false);
if (shouldPop)
popPopupsEnabledState();
return result;
}
void PluginView::paintIntoTransformedContext(HDC hdc)
{
if (m_isWindowed) {
SendMessage(platformPluginWidget(), WM_PRINTCLIENT, reinterpret_cast<WPARAM>(hdc), PRF_CLIENT | PRF_CHILDREN | PRF_OWNED);
return;
}
m_npWindow.type = NPWindowTypeDrawable;
m_npWindow.window = hdc;
WINDOWPOS windowpos = { 0 };
#if OS(WINCE)
IntRect r = static_cast<FrameView*>(parent())->contentsToWindow(frameRect());
windowpos.x = r.x();
windowpos.y = r.y();
windowpos.cx = r.width();
windowpos.cy = r.height();
#else
IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(frameRect().location());
windowpos.x = p.x();
windowpos.y = p.y();
windowpos.cx = frameRect().width();
windowpos.cy = frameRect().height();
#endif
NPEvent npEvent;
npEvent.event = WM_WINDOWPOSCHANGED;
npEvent.lParam = reinterpret_cast<uint32>(&windowpos);
npEvent.wParam = 0;
dispatchNPEvent(npEvent);
setNPWindowRect(frameRect());
npEvent.event = WM_PAINT;
npEvent.wParam = reinterpret_cast<uint32>(hdc);
// This is supposed to be a pointer to the dirty rect, but it seems that the Flash plugin
// ignores it so we just pass null.
npEvent.lParam = 0;
dispatchNPEvent(npEvent);
}
void PluginView::paintWindowedPluginIntoContext(GraphicsContext* context, const IntRect& rect)
{
#if !OS(WINCE)
ASSERT(m_isWindowed);
ASSERT(context->shouldIncludeChildWindows());
ASSERT(parent()->isFrameView());
IntPoint locationInWindow = static_cast<FrameView*>(parent())->contentsToWindow(frameRect().location());
HDC hdc = context->getWindowsContext(frameRect(), false);
#if PLATFORM(CAIRO)
// Must flush drawings up to this point to the backing metafile, otherwise the
// plugin region will be overwritten with any clear regions specified in the
// cairo-controlled portions of the rendering.
PlatformGraphicsContext* ctx = context->platformContext();
cairo_show_page(ctx);
#endif
XFORM originalTransform;
GetWorldTransform(hdc, &originalTransform);
// The plugin expects the DC to be in client coordinates, so we translate
// the DC to make that so.
AffineTransform ctm = context->getCTM();
ctm.translate(locationInWindow.x(), locationInWindow.y());
XFORM transform = static_cast<XFORM>(ctm.toTransformationMatrix());
SetWorldTransform(hdc, &transform);
paintIntoTransformedContext(hdc);
SetWorldTransform(hdc, &originalTransform);
context->releaseWindowsContext(hdc, frameRect(), false);
#endif
}
void PluginView::paint(GraphicsContext* context, const IntRect& rect)
{
if (!m_isStarted) {
// Draw the "missing plugin" image
paintMissingPluginIcon(context, rect);
return;
}
if (context->paintingDisabled())
return;
if (m_isWindowed) {
#if !OS(WINCE)
if (context->shouldIncludeChildWindows())
paintWindowedPluginIntoContext(context, rect);
#endif
return;
}
ASSERT(parent()->isFrameView());
IntRect rectInWindow = static_cast<FrameView*>(parent())->contentsToWindow(frameRect());
HDC hdc = context->getWindowsContext(rectInWindow, m_isTransparent);
// On Safari/Windows without transparency layers the GraphicsContext returns the HDC
// of the window and the plugin expects that the passed in DC has window coordinates.
// In the Qt port we always draw in an offscreen buffer and therefore need to preserve
// the translation set in getWindowsContext.
#if !PLATFORM(QT) && !OS(WINCE)
if (!context->inTransparencyLayer()) {
XFORM transform;
GetWorldTransform(hdc, &transform);
transform.eDx = 0;
transform.eDy = 0;
SetWorldTransform(hdc, &transform);
}
#endif
paintIntoTransformedContext(hdc);
context->releaseWindowsContext(hdc, frameRect(), m_isTransparent);
}
void PluginView::handleKeyboardEvent(KeyboardEvent* event)
{
NPEvent npEvent;
npEvent.wParam = event->keyCode();
if (event->type() == eventNames().keydownEvent) {
npEvent.event = WM_KEYDOWN;
npEvent.lParam = 0;
} else if (event->type() == eventNames().keyupEvent) {
npEvent.event = WM_KEYUP;
npEvent.lParam = 0x8000;
}
JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
if (!dispatchNPEvent(npEvent))
event->setDefaultHandled();
}
#if !OS(WINCE)
extern HCURSOR lastSetCursor;
extern bool ignoreNextSetCursor;
#endif
void PluginView::handleMouseEvent(MouseEvent* event)
{
NPEvent npEvent;
IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(IntPoint(event->pageX(), event->pageY()));
npEvent.lParam = MAKELPARAM(p.x(), p.y());
npEvent.wParam = 0;
if (event->ctrlKey())
npEvent.wParam |= MK_CONTROL;
if (event->shiftKey())
npEvent.wParam |= MK_SHIFT;
if (event->type() == eventNames().mousemoveEvent ||
event->type() == eventNames().mouseoutEvent ||
event->type() == eventNames().mouseoverEvent) {
npEvent.event = WM_MOUSEMOVE;
if (event->buttonDown())
switch (event->button()) {
case LeftButton:
npEvent.wParam |= MK_LBUTTON;
break;
case MiddleButton:
npEvent.wParam |= MK_MBUTTON;
break;
case RightButton:
npEvent.wParam |= MK_RBUTTON;
break;
}
}
else if (event->type() == eventNames().mousedownEvent) {
focusPluginElement();
switch (event->button()) {
case 0:
npEvent.event = WM_LBUTTONDOWN;
break;
case 1:
npEvent.event = WM_MBUTTONDOWN;
break;
case 2:
npEvent.event = WM_RBUTTONDOWN;
break;
}
} else if (event->type() == eventNames().mouseupEvent) {
switch (event->button()) {
case 0:
npEvent.event = WM_LBUTTONUP;
break;
case 1:
npEvent.event = WM_MBUTTONUP;
break;
case 2:
npEvent.event = WM_RBUTTONUP;
break;
}
} else
return;
JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
if (!dispatchNPEvent(npEvent))
event->setDefaultHandled();
#if !PLATFORM(QT) && !PLATFORM(WX) && !OS(WINCE)
// Currently, Widget::setCursor is always called after this function in EventHandler.cpp
// and since we don't want that we set ignoreNextSetCursor to true here to prevent that.
ignoreNextSetCursor = true;
lastSetCursor = ::GetCursor();
#endif
}
void PluginView::setParent(ScrollView* parent)
{
Widget::setParent(parent);
#if OS(WINCE)
if (parent) {
init();
if (parent->isVisible())
show();
else
hide();
}
#else
if (parent)
init();
else {
if (!platformPluginWidget())
return;
// If the plug-in window or one of its children have the focus, we need to
// clear it to prevent the web view window from being focused because that can
// trigger a layout while the plugin element is being detached.
HWND focusedWindow = ::GetFocus();
if (platformPluginWidget() == focusedWindow || ::IsChild(platformPluginWidget(), focusedWindow))
::SetFocus(0);
}
#endif
}
void PluginView::setParentVisible(bool visible)
{
if (isParentVisible() == visible)
return;
Widget::setParentVisible(visible);
if (isSelfVisible() && platformPluginWidget()) {
if (visible)
ShowWindow(platformPluginWidget(), SW_SHOWNA);
else
ShowWindow(platformPluginWidget(), SW_HIDE);
}
}
void PluginView::setNPWindowRect(const IntRect& rect)
{
if (!m_isStarted)
return;
#if OS(WINCE)
IntRect r = static_cast<FrameView*>(parent())->contentsToWindow(rect);
m_npWindow.x = r.x();
m_npWindow.y = r.y();
m_npWindow.width = r.width();
m_npWindow.height = r.height();
m_npWindow.clipRect.right = r.width();
m_npWindow.clipRect.bottom = r.height();
#else
IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(rect.location());
m_npWindow.x = p.x();
m_npWindow.y = p.y();
m_npWindow.width = rect.width();
m_npWindow.height = rect.height();
m_npWindow.clipRect.right = rect.width();
m_npWindow.clipRect.bottom = rect.height();
#endif
m_npWindow.clipRect.left = 0;
m_npWindow.clipRect.top = 0;
if (m_plugin->pluginFuncs()->setwindow) {
JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
setCallingPlugin(true);
m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
setCallingPlugin(false);
if (!m_isWindowed)
return;
ASSERT(platformPluginWidget());
#if OS(WINCE)
if (!m_pluginWndProc) {
WNDPROC currentWndProc = (WNDPROC)GetWindowLong(platformPluginWidget(), GWL_WNDPROC);
if (currentWndProc != PluginViewWndProc)
m_pluginWndProc = (WNDPROC)SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)PluginViewWndProc);
}
#else
WNDPROC currentWndProc = (WNDPROC)GetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC);
if (currentWndProc != PluginViewWndProc)
m_pluginWndProc = (WNDPROC)SetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC, (LONG)PluginViewWndProc);
#endif
}
}
NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf)
{
String filename(buf, len);
if (filename.startsWith("file:///"))
filename = filename.substring(8);
// Get file info
WIN32_FILE_ATTRIBUTE_DATA attrs;
if (GetFileAttributesExW(filename.charactersWithNullTermination(), GetFileExInfoStandard, &attrs) == 0)
return NPERR_FILE_NOT_FOUND;
if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return NPERR_FILE_NOT_FOUND;
HANDLE fileHandle = CreateFileW(filename.charactersWithNullTermination(), FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (fileHandle == INVALID_HANDLE_VALUE)
return NPERR_FILE_NOT_FOUND;
buffer.resize(attrs.nFileSizeLow);
DWORD bytesRead;
int retval = ReadFile(fileHandle, buffer.data(), attrs.nFileSizeLow, &bytesRead, 0);
CloseHandle(fileHandle);
if (retval == 0 || bytesRead != attrs.nFileSizeLow)
return NPERR_FILE_NOT_FOUND;
return NPERR_NO_ERROR;
}
NPError PluginView::getValueStatic(NPNVariable variable, void* value)
{
LOG(Plugins, "PluginView::getValueStatic(%s)", prettyNameForNPNVariable(variable).data());
return NPERR_GENERIC_ERROR;
}
NPError PluginView::getValue(NPNVariable variable, void* value)
{
LOG(Plugins, "PluginView::getValue(%s)", prettyNameForNPNVariable(variable).data());
switch (variable) {
#if ENABLE(NETSCAPE_PLUGIN_API)
case NPNVWindowNPObject: {
if (m_isJavaScriptPaused)
return NPERR_GENERIC_ERROR;
NPObject* windowScriptObject = m_parentFrame->script()->windowScriptNPObject();
// Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
if (windowScriptObject)
_NPN_RetainObject(windowScriptObject);
void** v = (void**)value;
*v = windowScriptObject;
return NPERR_NO_ERROR;
}
case NPNVPluginElementNPObject: {
if (m_isJavaScriptPaused)
return NPERR_GENERIC_ERROR;
NPObject* pluginScriptObject = 0;
if (m_element->hasTagName(appletTag) || m_element->hasTagName(embedTag) || m_element->hasTagName(objectTag))
pluginScriptObject = static_cast<HTMLPlugInElement*>(m_element)->getNPObject();
// Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
if (pluginScriptObject)
_NPN_RetainObject(pluginScriptObject);
void** v = (void**)value;
*v = pluginScriptObject;
return NPERR_NO_ERROR;
}
#endif
case NPNVnetscapeWindow: {
HWND* w = reinterpret_cast<HWND*>(value);
*w = windowHandleForPageClient(parent() ? parent()->hostWindow()->platformPageClient() : 0);
return NPERR_NO_ERROR;
}
case NPNVSupportsWindowless: {
NPBool *result = reinterpret_cast<NPBool*>(value);
*result = TRUE;
return NPERR_NO_ERROR;
}
default:
return NPERR_GENERIC_ERROR;
}
}
void PluginView::invalidateRect(const IntRect& rect)
{
if (m_isWindowed) {
RECT invalidRect = { rect.x(), rect.y(), rect.right(), rect.bottom() };
::InvalidateRect(platformPluginWidget(), &invalidRect, false);
return;
}
invalidateWindowlessPluginRect(rect);
}
void PluginView::invalidateRect(NPRect* rect)
{
if (!rect) {
invalidate();
return;
}
IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
if (m_isWindowed) {
RECT invalidRect = { r.x(), r.y(), r.right(), r.bottom() };
InvalidateRect(platformPluginWidget(), &invalidRect, FALSE);
} else {
if (m_plugin->quirks().contains(PluginQuirkThrottleInvalidate)) {
m_invalidRects.append(r);
if (!m_invalidateTimer.isActive())
m_invalidateTimer.startOneShot(0.001);
} else
invalidateRect(r);
}
}
void PluginView::invalidateRegion(NPRegion region)
{
if (m_isWindowed)
return;
RECT r;
if (GetRgnBox(region, &r) == 0) {
invalidate();
return;
}
IntRect rect(IntPoint(r.left, r.top), IntSize(r.right-r.left, r.bottom-r.top));
invalidateRect(rect);
}
void PluginView::forceRedraw()
{
if (m_isWindowed)
::UpdateWindow(platformPluginWidget());
else
::UpdateWindow(windowHandleForPageClient(parent() ? parent()->hostWindow()->platformPageClient() : 0));
}
bool PluginView::platformStart()
{
ASSERT(m_isStarted);
ASSERT(m_status == PluginStatusLoadedSuccessfully);
if (m_isWindowed) {
registerPluginView();
#if !OS(WINCE)
setUpOffscreenPaintingHooks(hookedBeginPaint, hookedEndPaint);
#endif
DWORD flags = WS_CHILD;
if (isSelfVisible())
flags |= WS_VISIBLE;
HWND parentWindowHandle = windowHandleForPageClient(m_parentFrame->view()->hostWindow()->platformPageClient());
HWND window = ::CreateWindowEx(0, kWebPluginViewdowClassName, 0, flags,
0, 0, 0, 0, parentWindowHandle, 0, Page::instanceHandle(), 0);
#if OS(WINDOWS) && (PLATFORM(QT) || PLATFORM(WX))
m_window = window;
#else
setPlatformWidget(window);
#endif
// Calling SetWindowLongPtrA here makes the window proc ASCII, which is required by at least
// the Shockwave Director plug-in.
#if OS(WINDOWS) && PLATFORM(X86_64) && COMPILER(MSVC)
::SetWindowLongPtrA(platformPluginWidget(), GWLP_WNDPROC, (LONG_PTR)DefWindowProcA);
#elif OS(WINCE)
::SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)DefWindowProc);
#else
::SetWindowLongPtrA(platformPluginWidget(), GWL_WNDPROC, (LONG)DefWindowProcA);
#endif
SetProp(platformPluginWidget(), kWebPluginViewProperty, this);
m_npWindow.type = NPWindowTypeWindow;
m_npWindow.window = platformPluginWidget();
} else {
m_npWindow.type = NPWindowTypeDrawable;
m_npWindow.window = 0;
}
updatePluginWidget();
if (!m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))
setNPWindowRect(frameRect());
return true;
}
void PluginView::platformDestroy()
{
if (!platformPluginWidget())
return;
DestroyWindow(platformPluginWidget());
setPlatformPluginWidget(0);
}
PassRefPtr<Image> PluginView::snapshot()
{
#if !PLATFORM(WX)
OwnPtr<HDC> hdc(CreateCompatibleDC(0));
if (!m_isWindowed) {
// Enable world transforms.
SetGraphicsMode(hdc.get(), GM_ADVANCED);
XFORM transform;
GetWorldTransform(hdc.get(), &transform);
// Windowless plug-ins assume that they're drawing onto the view's DC.
// Translate the context so that the plug-in draws at (0, 0).
ASSERT(parent()->isFrameView());
IntPoint position = static_cast<FrameView*>(parent())->contentsToWindow(frameRect()).location();
transform.eDx = -position.x();
transform.eDy = -position.y();
SetWorldTransform(hdc.get(), &transform);
}
void* bits;
BitmapInfo bmp = BitmapInfo::createBottomUp(frameRect().size());
OwnPtr<HBITMAP> hbmp(CreateDIBSection(0, &bmp, DIB_RGB_COLORS, &bits, 0, 0));
HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdc.get(), hbmp.get()));
paintIntoTransformedContext(hdc.get());
SelectObject(hdc.get(), hbmpOld);
return BitmapImage::create(hbmp.get());
#else
return 0;
#endif
}
void PluginView::halt()
{
ASSERT(!m_isHalted);
ASSERT(m_isStarted);
#if !PLATFORM(QT)
// Show a screenshot of the plug-in.
toRenderWidget(m_element->renderer())->showSubstituteImage(snapshot());
#endif
m_isHalted = true;
m_hasBeenHalted = true;
stop();
platformDestroy();
}
void PluginView::restart()
{
ASSERT(!m_isStarted);
ASSERT(m_isHalted);
// Clear any substitute image.
toRenderWidget(m_element->renderer())->showSubstituteImage(0);
m_isHalted = false;
m_haveUpdatedPluginWidget = false;
start();
}
} // namespace WebCore