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