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