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