1/*
2 * Copyright (C) 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4 * Copyright (C) 2008-2009 Torch Mobile, Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "PluginView.h"
30
31#include "BitmapImage.h"
32#include "BitmapInfo.h"
33#include "BridgeJSC.h"
34#include "Chrome.h"
35#include "ChromeClient.h"
36#include "Document.h"
37#include "DocumentLoader.h"
38#include "Element.h"
39#include "EventNames.h"
40#include "FocusController.h"
41#include "Frame.h"
42#include "FrameLoadRequest.h"
43#include "FrameLoader.h"
44#include "FrameTree.h"
45#include "FrameView.h"
46#include "GraphicsContext.h"
47#include "HTMLNames.h"
48#include "HTMLPlugInElement.h"
49#include "HostWindow.h"
50#include "Image.h"
51#include "JSDOMBinding.h"
52#include "JSDOMWindow.h"
53#include "KeyboardEvent.h"
54#include "LocalWindowsContext.h"
55#include "MIMETypeRegistry.h"
56#include "MouseEvent.h"
57#include "Page.h"
58#include "PlatformMouseEvent.h"
59#include "PluginDatabase.h"
60#include "PluginDebug.h"
61#include "PluginMainThreadScheduler.h"
62#include "PluginMessageThrottlerWin.h"
63#include "PluginPackage.h"
64#include "RenderWidget.h"
65#include "Settings.h"
66#include "WebCoreInstanceHandle.h"
67#include "c_instance.h"
68#include "npruntime_impl.h"
69#include "runtime_root.h"
70#include <runtime/JSCJSValue.h>
71#include <runtime/JSLock.h>
72#include <wtf/ASCIICType.h>
73#include <wtf/text/WTFString.h>
74#include <wtf/win/GDIObject.h>
75
76#if OS(WINCE)
77#undef LOG_NPERROR
78#define LOG_NPERROR(x)
79#undef LOG_PLUGIN_NET_ERROR
80#define LOG_PLUGIN_NET_ERROR()
81#endif
82
83#if USE(CAIRO)
84#include "PlatformContextCairo.h"
85#include <cairo-win32.h>
86#endif
87
88#if PLATFORM(GTK)
89#include <gdk/gdkwin32.h>
90#include <gtk/gtk.h>
91#endif
92
93static inline HWND windowHandleForPageClient(PlatformPageClient client)
94{
95#if PLATFORM(GTK)
96    if (!client)
97        return 0;
98    if (GdkWindow* window = gtk_widget_get_window(client))
99        return static_cast<HWND>(GDK_WINDOW_HWND(window));
100    return 0;
101#else
102    return client;
103#endif
104}
105
106using JSC::ExecState;
107using JSC::JSLock;
108using JSC::JSObject;
109
110using std::min;
111
112using namespace WTF;
113
114namespace WebCore {
115
116using namespace HTMLNames;
117
118const LPCWSTR kWebPluginViewdowClassName = L"WebPluginView";
119const LPCWSTR kWebPluginViewProperty = L"WebPluginViewProperty";
120
121#if !OS(WINCE)
122// The code used to hook BeginPaint/EndPaint originally came from
123// <http://www.fengyuan.com/article/wmprint.html>.
124// Copyright (C) 2000 by Feng Yuan (www.fengyuan.com).
125
126static unsigned beginPaintSysCall;
127static BYTE* beginPaint;
128
129static unsigned endPaintSysCall;
130static BYTE* endPaint;
131
132typedef HDC (WINAPI *PtrBeginPaint)(HWND, PAINTSTRUCT*);
133typedef BOOL (WINAPI *PtrEndPaint)(HWND, const PAINTSTRUCT*);
134
135#if OS(WINDOWS) && CPU(X86_64) && COMPILER(MSVC)
136extern "C" HDC __stdcall _HBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
137extern "C" BOOL __stdcall _HEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint);
138#endif
139
140HDC WINAPI PluginView::hookedBeginPaint(HWND hWnd, PAINTSTRUCT* lpPaint)
141{
142    PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty));
143    if (pluginView && pluginView->m_wmPrintHDC) {
144        // We're secretly handling WM_PRINTCLIENT, so set up the PAINTSTRUCT so
145        // that the plugin will paint into the HDC we provide.
146        memset(lpPaint, 0, sizeof(PAINTSTRUCT));
147        lpPaint->hdc = pluginView->m_wmPrintHDC;
148        GetClientRect(hWnd, &lpPaint->rcPaint);
149        return pluginView->m_wmPrintHDC;
150    }
151
152#if COMPILER(GCC)
153    HDC result;
154    asm ("push    %2\n"
155         "push    %3\n"
156         "call    *%4\n"
157         : "=a" (result)
158         : "a" (beginPaintSysCall), "g" (lpPaint), "g" (hWnd), "m" (beginPaint)
159         : "memory"
160        );
161    return result;
162#elif defined(_M_IX86)
163    // Call through to the original BeginPaint.
164    __asm   mov     eax, beginPaintSysCall
165    __asm   push    lpPaint
166    __asm   push    hWnd
167    __asm   call    beginPaint
168#else
169    return _HBeginPaint(hWnd, lpPaint);
170#endif
171}
172
173BOOL WINAPI PluginView::hookedEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint)
174{
175    PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty));
176    if (pluginView && pluginView->m_wmPrintHDC) {
177        // We're secretly handling WM_PRINTCLIENT, so we don't have to do any
178        // cleanup.
179        return TRUE;
180    }
181
182#if COMPILER(GCC)
183    BOOL result;
184    asm ("push   %2\n"
185         "push   %3\n"
186         "call   *%4\n"
187         : "=a" (result)
188         : "a" (endPaintSysCall), "g" (lpPaint), "g" (hWnd), "m" (endPaint)
189        );
190    return result;
191#elif defined (_M_IX86)
192    // Call through to the original EndPaint.
193    __asm   mov     eax, endPaintSysCall
194    __asm   push    lpPaint
195    __asm   push    hWnd
196    __asm   call    endPaint
197#else
198    return _HEndPaint(hWnd, lpPaint);
199#endif
200}
201
202static void hook(const char* module, const char* proc, unsigned& sysCallID, BYTE*& pProc, const void* pNewProc)
203{
204    // See <http://www.fengyuan.com/article/wmprint.html> for an explanation of
205    // how this function works.
206
207    HINSTANCE hMod = GetModuleHandleA(module);
208
209    pProc = reinterpret_cast<BYTE*>(reinterpret_cast<ptrdiff_t>(GetProcAddress(hMod, proc)));
210
211#if COMPILER(GCC) || defined(_M_IX86)
212    if (pProc[0] != 0xB8)
213        return;
214
215    // FIXME: Should we be reading the bytes one-by-one instead of doing an
216    // unaligned read?
217    sysCallID = *reinterpret_cast<unsigned*>(pProc + 1);
218
219    DWORD flOldProtect;
220    if (!VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, &flOldProtect))
221        return;
222
223    pProc[0] = 0xE9;
224    *reinterpret_cast<unsigned*>(pProc + 1) = reinterpret_cast<intptr_t>(pNewProc) - reinterpret_cast<intptr_t>(pProc + 5);
225
226    pProc += 5;
227#else
228    /* Disassembly of BeginPaint()
229    00000000779FC5B0 4C 8B D1         mov         r10,rcx
230    00000000779FC5B3 B8 17 10 00 00   mov         eax,1017h
231    00000000779FC5B8 0F 05            syscall
232    00000000779FC5BA C3               ret
233    00000000779FC5BB 90               nop
234    00000000779FC5BC 90               nop
235    00000000779FC5BD 90               nop
236    00000000779FC5BE 90               nop
237    00000000779FC5BF 90               nop
238    00000000779FC5C0 90               nop
239    00000000779FC5C1 90               nop
240    00000000779FC5C2 90               nop
241    00000000779FC5C3 90               nop
242    */
243    // Check for the signature as in the above disassembly
244    DWORD guard = 0xB8D18B4C;
245    if (*reinterpret_cast<DWORD*>(pProc) != guard)
246        return;
247
248    DWORD flOldProtect;
249    VirtualProtect(pProc, 12, PAGE_EXECUTE_READWRITE, & flOldProtect);
250    pProc[0] = 0x48;    // mov rax, this
251    pProc[1] = 0xb8;
252    *(__int64*)(pProc+2) = (__int64)pNewProc;
253    pProc[10] = 0xff;   // jmp rax
254    pProc[11] = 0xe0;
255#endif
256}
257
258static void setUpOffscreenPaintingHooks(HDC (WINAPI*hookedBeginPaint)(HWND, PAINTSTRUCT*), BOOL (WINAPI*hookedEndPaint)(HWND, const PAINTSTRUCT*))
259{
260    static bool haveHooked = false;
261    if (haveHooked)
262        return;
263    haveHooked = true;
264
265    // Most (all?) windowed plugins don't seem to respond to WM_PRINTCLIENT, so
266    // we hook into BeginPaint/EndPaint to allow their normal WM_PAINT handling
267    // to draw into a given HDC. Note that this hooking affects the entire
268    // process.
269    hook("user32.dll", "BeginPaint", beginPaintSysCall, beginPaint, reinterpret_cast<const void *>(reinterpret_cast<ptrdiff_t>(hookedBeginPaint)));
270    hook("user32.dll", "EndPaint", endPaintSysCall, endPaint, reinterpret_cast<const void *>(reinterpret_cast<ptrdiff_t>(hookedEndPaint)));
271
272}
273#endif
274
275static bool registerPluginView()
276{
277    static bool haveRegisteredWindowClass = false;
278    if (haveRegisteredWindowClass)
279        return true;
280
281    haveRegisteredWindowClass = true;
282
283#if PLATFORM(GTK)
284    WebCore::setInstanceHandle((HINSTANCE)(GetModuleHandle(0)));
285#endif
286
287    ASSERT(WebCore::instanceHandle());
288
289#if OS(WINCE)
290    WNDCLASS wcex = { 0 };
291#else
292    WNDCLASSEX wcex;
293    wcex.cbSize = sizeof(WNDCLASSEX);
294    wcex.hIconSm        = 0;
295#endif
296
297    wcex.style          = CS_DBLCLKS;
298#if OS(WINCE)
299    wcex.style          |= CS_PARENTDC;
300#endif
301    wcex.lpfnWndProc    = DefWindowProc;
302    wcex.cbClsExtra     = 0;
303    wcex.cbWndExtra     = 0;
304    wcex.hInstance      = WebCore::instanceHandle();
305    wcex.hIcon          = 0;
306    wcex.hCursor        = LoadCursor(0, IDC_ARROW);
307    wcex.hbrBackground  = (HBRUSH)COLOR_WINDOW;
308    wcex.lpszMenuName   = 0;
309    wcex.lpszClassName  = kWebPluginViewdowClassName;
310
311#if OS(WINCE)
312    return !!RegisterClass(&wcex);
313#else
314    return !!RegisterClassEx(&wcex);
315#endif
316}
317
318LRESULT CALLBACK PluginView::PluginViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
319{
320    PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty));
321
322    return pluginView->wndProc(hWnd, message, wParam, lParam);
323}
324
325static bool isWindowsMessageUserGesture(UINT message)
326{
327    switch (message) {
328        case WM_LBUTTONUP:
329        case WM_MBUTTONUP:
330        case WM_RBUTTONUP:
331        case WM_KEYUP:
332            return true;
333        default:
334            return false;
335    }
336}
337
338static inline IntPoint contentsToNativeWindow(FrameView* view, const IntPoint& point)
339{
340    return view->contentsToWindow(point);
341}
342
343static inline IntRect contentsToNativeWindow(FrameView* view, const IntRect& rect)
344{
345    return view->contentsToWindow(rect);
346}
347
348LRESULT
349PluginView::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
350{
351    // <rdar://5711136> Sometimes Flash will call SetCapture before creating
352    // a full-screen window and will not release it, which causes the
353    // full-screen window to never receive mouse events. We set/release capture
354    // on mouse down/up before sending the event to the plug-in to prevent that.
355    switch (message) {
356        case WM_LBUTTONDOWN:
357        case WM_MBUTTONDOWN:
358        case WM_RBUTTONDOWN:
359            ::SetCapture(hWnd);
360            break;
361        case WM_LBUTTONUP:
362        case WM_MBUTTONUP:
363        case WM_RBUTTONUP:
364            ::ReleaseCapture();
365            break;
366    }
367
368    if (message == m_lastMessage &&
369        m_plugin->quirks().contains(PluginQuirkDontCallWndProcForSameMessageRecursively) &&
370        m_isCallingPluginWndProc)
371        return 1;
372
373    if (message == WM_USER + 1 &&
374        m_plugin->quirks().contains(PluginQuirkThrottleWMUserPlusOneMessages)) {
375        if (!m_messageThrottler)
376            m_messageThrottler = adoptPtr(new PluginMessageThrottlerWin(this));
377
378        m_messageThrottler->appendMessage(hWnd, message, wParam, lParam);
379        return 0;
380    }
381
382    m_lastMessage = message;
383    m_isCallingPluginWndProc = true;
384
385    // If the plug-in doesn't explicitly support changing the pop-up state, we enable
386    // popups for all user gestures.
387    // Note that we need to pop the state in a timer, because the Flash plug-in
388    // pops up windows in response to a posted message.
389    if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE &&
390        isWindowsMessageUserGesture(message) && !m_popPopupsStateTimer.isActive()) {
391
392        pushPopupsEnabledState(true);
393
394        m_popPopupsStateTimer.startOneShot(0);
395    }
396
397#if !OS(WINCE)
398    if (message == WM_PRINTCLIENT) {
399        // Most (all?) windowed plugins don't respond to WM_PRINTCLIENT, so we
400        // change the message to WM_PAINT and rely on our hooked versions of
401        // BeginPaint/EndPaint to make the plugin draw into the given HDC.
402        message = WM_PAINT;
403        m_wmPrintHDC = reinterpret_cast<HDC>(wParam);
404    }
405#endif
406
407    // Call the plug-in's window proc.
408    LRESULT result = ::CallWindowProc(m_pluginWndProc, hWnd, message, wParam, lParam);
409
410    m_wmPrintHDC = 0;
411
412    m_isCallingPluginWndProc = false;
413
414    return result;
415}
416
417void PluginView::updatePluginWidget()
418{
419    if (!parent())
420        return;
421
422    ASSERT(parent()->isFrameView());
423    FrameView* frameView = toFrameView(parent());
424
425    IntRect oldWindowRect = m_windowRect;
426    IntRect oldClipRect = m_clipRect;
427
428#if OS(WINCE)
429    m_windowRect = frameView->contentsToWindow(frameRect());
430#else
431    m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
432#endif
433    m_clipRect = windowClipRect();
434    m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
435
436    if (platformPluginWidget() && (!m_haveUpdatedPluginWidget || m_windowRect != oldWindowRect || m_clipRect != oldClipRect)) {
437
438        setCallingPlugin(true);
439
440        // To prevent flashes while scrolling, we disable drawing during the window
441        // update process by clipping the window to the zero rect.
442
443        bool clipToZeroRect = !m_plugin->quirks().contains(PluginQuirkDontClipToZeroRectWhenScrolling);
444
445        if (clipToZeroRect) {
446            auto rgn = adoptGDIObject(::CreateRectRgn(0, 0, 0, 0));
447            ::SetWindowRgn(platformPluginWidget(), rgn.leak(), FALSE);
448        } else {
449            auto rgn = adoptGDIObject(::CreateRectRgn(m_clipRect.x(), m_clipRect.y(), m_clipRect.maxX(), m_clipRect.maxY()));
450            ::SetWindowRgn(platformPluginWidget(), rgn.leak(), TRUE);
451        }
452
453        if (!m_haveUpdatedPluginWidget || m_windowRect != oldWindowRect)
454            ::MoveWindow(platformPluginWidget(), m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), TRUE);
455
456        if (clipToZeroRect) {
457            auto rgn = adoptGDIObject(::CreateRectRgn(m_clipRect.x(), m_clipRect.y(), m_clipRect.maxX(), m_clipRect.maxY()));
458            ::SetWindowRgn(platformPluginWidget(), rgn.leak(), TRUE);
459        }
460
461        setCallingPlugin(false);
462
463        m_haveUpdatedPluginWidget = true;
464    }
465}
466
467void PluginView::setFocus(bool focused)
468{
469    if (focused && platformPluginWidget())
470        SetFocus(platformPluginWidget());
471
472    Widget::setFocus(focused);
473
474    if (!m_plugin || m_isWindowed)
475        return;
476
477    NPEvent npEvent;
478
479    npEvent.event = focused ? WM_SETFOCUS : WM_KILLFOCUS;
480    npEvent.wParam = 0;
481    npEvent.lParam = 0;
482
483    dispatchNPEvent(npEvent);
484}
485
486void PluginView::show()
487{
488    setSelfVisible(true);
489
490    if (isParentVisible() && platformPluginWidget())
491        ShowWindow(platformPluginWidget(), SW_SHOWNA);
492
493    Widget::show();
494}
495
496void PluginView::hide()
497{
498    setSelfVisible(false);
499
500    if (isParentVisible() && platformPluginWidget())
501        ShowWindow(platformPluginWidget(), SW_HIDE);
502
503    Widget::hide();
504}
505
506bool PluginView::dispatchNPEvent(NPEvent& npEvent)
507{
508    if (!m_plugin->pluginFuncs()->event)
509        return true;
510
511    bool shouldPop = false;
512
513    if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE && isWindowsMessageUserGesture(npEvent.event)) {
514        pushPopupsEnabledState(true);
515        shouldPop = true;
516    }
517
518    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
519    setCallingPlugin(true);
520    bool accepted = !m_plugin->pluginFuncs()->event(m_instance, &npEvent);
521    setCallingPlugin(false);
522
523    if (shouldPop)
524        popPopupsEnabledState();
525
526    return accepted;
527}
528
529void PluginView::paintIntoTransformedContext(HDC hdc)
530{
531    if (m_isWindowed) {
532#if !OS(WINCE)
533        SendMessage(platformPluginWidget(), WM_PRINTCLIENT, reinterpret_cast<WPARAM>(hdc), PRF_CLIENT | PRF_CHILDREN | PRF_OWNED);
534#endif
535        return;
536    }
537
538    m_npWindow.type = NPWindowTypeDrawable;
539    m_npWindow.window = hdc;
540
541    WINDOWPOS windowpos = { 0, 0, 0, 0, 0, 0, 0 };
542
543    IntRect r = contentsToNativeWindow(toFrameView(parent()), frameRect());
544
545    windowpos.x = r.x();
546    windowpos.y = r.y();
547    windowpos.cx = r.width();
548    windowpos.cy = r.height();
549
550    NPEvent npEvent;
551    npEvent.event = WM_WINDOWPOSCHANGED;
552    npEvent.lParam = reinterpret_cast<uintptr_t>(&windowpos);
553    npEvent.wParam = 0;
554
555    dispatchNPEvent(npEvent);
556
557    setNPWindowRect(frameRect());
558
559    npEvent.event = WM_PAINT;
560    npEvent.wParam = reinterpret_cast<uintptr_t>(hdc);
561
562    // This is supposed to be a pointer to the dirty rect, but it seems that the Flash plugin
563    // ignores it so we just pass null.
564    npEvent.lParam = 0;
565
566    dispatchNPEvent(npEvent);
567}
568
569void PluginView::paintWindowedPluginIntoContext(GraphicsContext* context, const IntRect& rect)
570{
571#if !USE(WINGDI)
572    ASSERT(m_isWindowed);
573    ASSERT(context->shouldIncludeChildWindows());
574
575    ASSERT(parent()->isFrameView());
576    IntPoint locationInWindow = toFrameView(parent())->convertToContainingWindow(frameRect().location());
577
578    LocalWindowsContext windowsContext(context, frameRect(), false);
579
580#if USE(CAIRO)
581    // Must flush drawings up to this point to the backing metafile, otherwise the
582    // plugin region will be overwritten with any clear regions specified in the
583    // cairo-controlled portions of the rendering.
584    cairo_show_page(context->platformContext()->cr());
585#endif
586
587    HDC hdc = windowsContext.hdc();
588    XFORM originalTransform;
589    GetWorldTransform(hdc, &originalTransform);
590
591    // The plugin expects the DC to be in client coordinates, so we translate
592    // the DC to make that so.
593    AffineTransform ctm = context->getCTM();
594    ctm.translate(locationInWindow.x(), locationInWindow.y());
595    XFORM transform = static_cast<XFORM>(ctm.toTransformationMatrix());
596
597    SetWorldTransform(hdc, &transform);
598
599    paintIntoTransformedContext(hdc);
600
601    SetWorldTransform(hdc, &originalTransform);
602#endif
603}
604
605void PluginView::paint(GraphicsContext* context, const IntRect& rect)
606{
607    if (!m_isStarted) {
608        // Draw the "missing plugin" image
609        paintMissingPluginIcon(context, rect);
610        return;
611    }
612
613    if (context->paintingDisabled())
614        return;
615
616    // Ensure that we have called SetWindow before we try to paint.
617    if (!m_haveCalledSetWindow)
618        setNPWindowRect(frameRect());
619
620    if (m_isWindowed) {
621#if !USE(WINGDI)
622        if (context->shouldIncludeChildWindows())
623            paintWindowedPluginIntoContext(context, rect);
624#endif
625        return;
626    }
627
628    ASSERT(parent()->isFrameView());
629
630    // In the GTK and Qt ports we draw in an offscreen buffer and don't want to use the window
631    // coordinates.
632#if PLATFORM(GTK)
633    IntRect rectInWindow(rect);
634    rectInWindow.intersect(frameRect());
635#else
636    IntRect rectInWindow = toFrameView(parent())->contentsToWindow(frameRect());
637#endif
638    LocalWindowsContext windowsContext(context, rectInWindow, m_isTransparent);
639
640    // On Safari/Windows without transparency layers the GraphicsContext returns the HDC
641    // of the window and the plugin expects that the passed in DC has window coordinates.
642    // In the GTK and Qt ports we always draw in an offscreen buffer and therefore need
643    // to preserve the translation set in getWindowsContext.
644#if !PLATFORM(GTK) && !OS(WINCE)
645    if (!context->isInTransparencyLayer()) {
646        XFORM transform;
647        GetWorldTransform(windowsContext.hdc(), &transform);
648        transform.eDx = 0;
649        transform.eDy = 0;
650        SetWorldTransform(windowsContext.hdc(), &transform);
651    }
652#endif
653
654    paintIntoTransformedContext(windowsContext.hdc());
655}
656
657void PluginView::handleKeyboardEvent(KeyboardEvent* event)
658{
659    ASSERT(m_plugin && !m_isWindowed);
660
661    NPEvent npEvent;
662
663    npEvent.wParam = event->keyCode();
664
665    if (event->type() == eventNames().keydownEvent) {
666        npEvent.event = WM_KEYDOWN;
667        npEvent.lParam = 0;
668    } else if (event->type() == eventNames().keypressEvent) {
669        npEvent.event = WM_CHAR;
670        npEvent.lParam = 0;
671    } else if (event->type() == eventNames().keyupEvent) {
672        npEvent.event = WM_KEYUP;
673        npEvent.lParam = 0x8000;
674    } else
675        return;
676
677    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
678    if (dispatchNPEvent(npEvent))
679        event->setDefaultHandled();
680}
681
682#if !OS(WINCE)
683extern bool ignoreNextSetCursor;
684#endif
685
686void PluginView::handleMouseEvent(MouseEvent* event)
687{
688    ASSERT(m_plugin && !m_isWindowed);
689
690    NPEvent npEvent;
691
692    IntPoint p = contentsToNativeWindow(toFrameView(parent()), IntPoint(event->pageX(), event->pageY()));
693
694    npEvent.lParam = MAKELPARAM(p.x(), p.y());
695    npEvent.wParam = 0;
696
697    if (event->ctrlKey())
698        npEvent.wParam |= MK_CONTROL;
699    if (event->shiftKey())
700        npEvent.wParam |= MK_SHIFT;
701
702    if (event->type() == eventNames().mousemoveEvent ||
703        event->type() == eventNames().mouseoutEvent ||
704        event->type() == eventNames().mouseoverEvent) {
705        npEvent.event = WM_MOUSEMOVE;
706        if (event->buttonDown())
707            switch (event->button()) {
708                case LeftButton:
709                    npEvent.wParam |= MK_LBUTTON;
710                    break;
711                case MiddleButton:
712                    npEvent.wParam |= MK_MBUTTON;
713                    break;
714                case RightButton:
715                    npEvent.wParam |= MK_RBUTTON;
716                break;
717            }
718    }
719    else if (event->type() == eventNames().mousedownEvent) {
720        focusPluginElement();
721        switch (event->button()) {
722            case 0:
723                npEvent.event = WM_LBUTTONDOWN;
724                break;
725            case 1:
726                npEvent.event = WM_MBUTTONDOWN;
727                break;
728            case 2:
729                npEvent.event = WM_RBUTTONDOWN;
730                break;
731        }
732    } else if (event->type() == eventNames().mouseupEvent) {
733        switch (event->button()) {
734            case 0:
735                npEvent.event = WM_LBUTTONUP;
736                break;
737            case 1:
738                npEvent.event = WM_MBUTTONUP;
739                break;
740            case 2:
741                npEvent.event = WM_RBUTTONUP;
742                break;
743        }
744    } else
745        return;
746
747    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
748    // FIXME: Consider back porting the http://webkit.org/b/58108 fix here.
749    if (dispatchNPEvent(npEvent))
750        event->setDefaultHandled();
751
752#if !PLATFORM(GTK) && !OS(WINCE)
753    // Currently, Widget::setCursor is always called after this function in EventHandler.cpp
754    // and since we don't want that we set ignoreNextSetCursor to true here to prevent that.
755    ignoreNextSetCursor = true;
756    if (Page* page = m_parentFrame->page())
757        page->chrome().client().setLastSetCursorToCurrentCursor();
758#endif
759}
760
761void PluginView::setParent(ScrollView* parent)
762{
763    Widget::setParent(parent);
764
765#if OS(WINCE)
766    if (parent) {
767        init();
768        if (parent->isVisible())
769            show();
770        else
771            hide();
772    }
773#else
774    if (parent)
775        init();
776    else {
777        if (!platformPluginWidget())
778            return;
779
780        // If the plug-in window or one of its children have the focus, we need to
781        // clear it to prevent the web view window from being focused because that can
782        // trigger a layout while the plugin element is being detached.
783        HWND focusedWindow = ::GetFocus();
784        if (platformPluginWidget() == focusedWindow || ::IsChild(platformPluginWidget(), focusedWindow))
785            ::SetFocus(0);
786    }
787#endif
788}
789
790void PluginView::setParentVisible(bool visible)
791{
792    if (isParentVisible() == visible)
793        return;
794
795    Widget::setParentVisible(visible);
796
797    if (isSelfVisible() && platformPluginWidget()) {
798        if (visible)
799            ShowWindow(platformPluginWidget(), SW_SHOWNA);
800        else
801            ShowWindow(platformPluginWidget(), SW_HIDE);
802    }
803}
804
805void PluginView::setNPWindowRect(const IntRect& rect)
806{
807    if (!m_isStarted)
808        return;
809
810#if OS(WINCE)
811    IntRect r = toFrameView(parent())->contentsToWindow(rect);
812    m_npWindow.x = r.x();
813    m_npWindow.y = r.y();
814
815    m_npWindow.width = r.width();
816    m_npWindow.height = r.height();
817
818    m_npWindow.clipRect.right = r.width();
819    m_npWindow.clipRect.bottom = r.height();
820#else
821    // In the GTK and Qt ports we draw in an offscreen buffer and don't want to use the window
822    // coordinates.
823# if PLATFORM(GTK)
824    IntPoint p = rect.location();
825# else
826    IntPoint p = toFrameView(parent())->contentsToWindow(rect.location());
827# endif
828    m_npWindow.x = p.x();
829    m_npWindow.y = p.y();
830
831    m_npWindow.width = rect.width();
832    m_npWindow.height = rect.height();
833
834    m_npWindow.clipRect.right = rect.width();
835    m_npWindow.clipRect.bottom = rect.height();
836#endif
837    m_npWindow.clipRect.left = 0;
838    m_npWindow.clipRect.top = 0;
839
840    if (m_plugin->pluginFuncs()->setwindow) {
841        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
842        setCallingPlugin(true);
843        m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
844        setCallingPlugin(false);
845
846        m_haveCalledSetWindow = true;
847
848        if (!m_isWindowed)
849            return;
850
851        ASSERT(platformPluginWidget());
852
853#if OS(WINCE)
854        if (!m_pluginWndProc) {
855            WNDPROC currentWndProc = (WNDPROC)GetWindowLong(platformPluginWidget(), GWL_WNDPROC);
856            if (currentWndProc != PluginViewWndProc)
857                m_pluginWndProc = (WNDPROC)SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)PluginViewWndProc);
858        }
859#else
860        WNDPROC currentWndProc = (WNDPROC)GetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC);
861        if (currentWndProc != PluginViewWndProc)
862            m_pluginWndProc = (WNDPROC)SetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC, (LONG_PTR)PluginViewWndProc);
863#endif
864    }
865}
866
867NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf)
868{
869    String filename(buf, len);
870
871    if (filename.startsWith("file:///"))
872        filename = filename.substring(8);
873
874    // Get file info
875    WIN32_FILE_ATTRIBUTE_DATA attrs;
876    if (GetFileAttributesExW(filename.charactersWithNullTermination().data(), GetFileExInfoStandard, &attrs) == 0)
877        return NPERR_FILE_NOT_FOUND;
878
879    if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
880        return NPERR_FILE_NOT_FOUND;
881
882    HANDLE fileHandle = CreateFileW(filename.charactersWithNullTermination().data(), FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
883
884    if (fileHandle == INVALID_HANDLE_VALUE)
885        return NPERR_FILE_NOT_FOUND;
886
887    buffer.resize(attrs.nFileSizeLow);
888
889    DWORD bytesRead;
890    int retval = ReadFile(fileHandle, buffer.data(), attrs.nFileSizeLow, &bytesRead, 0);
891
892    CloseHandle(fileHandle);
893
894    if (retval == 0 || bytesRead != attrs.nFileSizeLow)
895        return NPERR_FILE_NOT_FOUND;
896
897    return NPERR_NO_ERROR;
898}
899
900bool PluginView::platformGetValueStatic(NPNVariable, void*, NPError*)
901{
902    return false;
903}
904
905bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result)
906{
907    switch (variable) {
908        case NPNVnetscapeWindow: {
909            HWND* w = reinterpret_cast<HWND*>(value);
910            *w = windowHandleForPageClient(parent() ? parent()->hostWindow()->platformPageClient() : 0);
911            *result = NPERR_NO_ERROR;
912            return true;
913        }
914
915        case NPNVSupportsWindowless: {
916            NPBool* flag = reinterpret_cast<NPBool*>(value);
917            *flag = TRUE;
918            *result = NPERR_NO_ERROR;
919            return true;
920        }
921
922    default:
923        return false;
924    }
925}
926
927void PluginView::invalidateRect(const IntRect& rect)
928{
929    if (m_isWindowed) {
930        RECT invalidRect = { rect.x(), rect.y(), rect.maxX(), rect.maxY() };
931        ::InvalidateRect(platformPluginWidget(), &invalidRect, false);
932        return;
933    }
934
935    invalidateWindowlessPluginRect(rect);
936}
937
938void PluginView::invalidateRect(NPRect* rect)
939{
940    if (!rect) {
941        invalidate();
942        return;
943    }
944
945    IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
946
947    if (m_isWindowed) {
948        RECT invalidRect = { r.x(), r.y(), r.maxX(), r.maxY() };
949        InvalidateRect(platformPluginWidget(), &invalidRect, FALSE);
950    } else {
951        if (m_plugin->quirks().contains(PluginQuirkThrottleInvalidate)) {
952            m_invalidRects.append(r);
953            if (!m_invalidateTimer.isActive())
954                m_invalidateTimer.startOneShot(0.001);
955        } else
956            invalidateRect(r);
957    }
958}
959
960void PluginView::invalidateRegion(NPRegion region)
961{
962    if (m_isWindowed)
963        return;
964
965    RECT r;
966
967    if (GetRgnBox(region, &r) == 0) {
968        invalidate();
969        return;
970    }
971
972    IntRect rect(IntPoint(r.left, r.top), IntSize(r.right-r.left, r.bottom-r.top));
973    invalidateRect(rect);
974}
975
976void PluginView::forceRedraw()
977{
978    if (m_isWindowed)
979        ::UpdateWindow(platformPluginWidget());
980    else
981        ::UpdateWindow(windowHandleForPageClient(parent() ? parent()->hostWindow()->platformPageClient() : 0));
982}
983
984bool PluginView::platformStart()
985{
986    ASSERT(m_isStarted);
987    ASSERT(m_status == PluginStatusLoadedSuccessfully);
988
989    if (m_isWindowed) {
990        registerPluginView();
991#if !OS(WINCE)
992        setUpOffscreenPaintingHooks(hookedBeginPaint, hookedEndPaint);
993#endif
994
995        DWORD flags = WS_CHILD;
996        if (isSelfVisible())
997            flags |= WS_VISIBLE;
998
999        HWND parentWindowHandle = windowHandleForPageClient(m_parentFrame->view()->hostWindow()->platformPageClient());
1000        HWND window = ::CreateWindowEx(0, kWebPluginViewdowClassName, 0, flags,
1001                                       0, 0, 0, 0, parentWindowHandle, 0, WebCore::instanceHandle(), 0);
1002
1003#if OS(WINDOWS) && PLATFORM(GTK)
1004        m_window = window;
1005#else
1006        setPlatformWidget(window);
1007#endif
1008
1009        // Calling SetWindowLongPtrA here makes the window proc ASCII, which is required by at least
1010        // the Shockwave Director plug-in.
1011#if OS(WINDOWS) && CPU(X86_64)
1012        ::SetWindowLongPtrA(platformPluginWidget(), GWLP_WNDPROC, (LONG_PTR)DefWindowProcA);
1013#elif OS(WINCE)
1014        ::SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)DefWindowProc);
1015#else
1016        ::SetWindowLongPtrA(platformPluginWidget(), GWL_WNDPROC, (LONG)DefWindowProcA);
1017#endif
1018        SetProp(platformPluginWidget(), kWebPluginViewProperty, this);
1019
1020        m_npWindow.type = NPWindowTypeWindow;
1021        m_npWindow.window = platformPluginWidget();
1022    } else {
1023        m_npWindow.type = NPWindowTypeDrawable;
1024        m_npWindow.window = 0;
1025    }
1026
1027    updatePluginWidget();
1028
1029    if (!m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))
1030        setNPWindowRect(frameRect());
1031
1032    return true;
1033}
1034
1035void PluginView::platformDestroy()
1036{
1037    if (!platformPluginWidget())
1038        return;
1039
1040    DestroyWindow(platformPluginWidget());
1041    setPlatformPluginWidget(0);
1042}
1043
1044PassRefPtr<Image> PluginView::snapshot()
1045{
1046#if !PLATFORM(GTK) && !USE(WINGDI)
1047    auto hdc = adoptGDIObject(::CreateCompatibleDC(0));
1048
1049    if (!m_isWindowed) {
1050        // Enable world transforms.
1051        SetGraphicsMode(hdc.get(), GM_ADVANCED);
1052
1053        XFORM transform;
1054        GetWorldTransform(hdc.get(), &transform);
1055
1056        // Windowless plug-ins assume that they're drawing onto the view's DC.
1057        // Translate the context so that the plug-in draws at (0, 0).
1058        ASSERT(parent()->isFrameView());
1059        IntPoint position = toFrameView(parent())->contentsToWindow(frameRect()).location();
1060        transform.eDx = -position.x();
1061        transform.eDy = -position.y();
1062        SetWorldTransform(hdc.get(), &transform);
1063    }
1064
1065    void* bits;
1066    BitmapInfo bmp = BitmapInfo::createBottomUp(frameRect().size());
1067    auto hbmp = adoptGDIObject(::CreateDIBSection(0, &bmp, DIB_RGB_COLORS, &bits, 0, 0));
1068
1069    HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdc.get(), hbmp.get()));
1070
1071    paintIntoTransformedContext(hdc.get());
1072
1073    SelectObject(hdc.get(), hbmpOld);
1074
1075    return BitmapImage::create(hbmp.get());
1076#else
1077    return 0;
1078#endif
1079}
1080
1081} // namespace WebCore
1082