1/* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Collabora Ltd. All rights reserved. 4 * Copyright (C) 2009, 2010 Kakai, Inc. <brian@kakai.com> 5 * Copyright (C) 2010 Igalia S.L. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "PluginView.h" 31 32#include "BridgeJSC.h" 33#include "Document.h" 34#include "DocumentLoader.h" 35#include "Element.h" 36#include "FocusController.h" 37#include "Frame.h" 38#include "FrameLoadRequest.h" 39#include "FrameLoader.h" 40#include "FrameTree.h" 41#include "FrameView.h" 42#include "GRefPtrGtk.h" 43#include "GraphicsContext.h" 44#include "GtkVersioning.h" 45#include "HTMLNames.h" 46#include "HTMLPlugInElement.h" 47#include "HostWindow.h" 48#include "Image.h" 49#include "JSDOMBinding.h" 50#include "JSDOMWindowBase.h" 51#include "KeyboardEvent.h" 52#include "MouseEvent.h" 53#include "NotImplemented.h" 54#include "Page.h" 55#include "PlatformContextCairo.h" 56#include "PlatformKeyboardEvent.h" 57#include "PlatformMouseEvent.h" 58#include "PluginDebug.h" 59#include "PluginMainThreadScheduler.h" 60#include "PluginPackage.h" 61#include "RenderObject.h" 62#include "Settings.h" 63#include "SpatialNavigation.h" 64#include "npruntime_impl.h" 65#include "runtime_root.h" 66#include <runtime/JSCJSValue.h> 67#include <runtime/JSLock.h> 68 69#ifdef GTK_API_VERSION_2 70#include <gdkconfig.h> 71#endif 72#include <gtk/gtk.h> 73 74#define String XtStringType 75#include "RefPtrCairo.h" 76#include "gtk2xtbin.h" 77#define Bool int // this got undefined somewhere 78#define Status int // ditto 79#include <X11/extensions/Xrender.h> 80#include <cairo-xlib.h> 81#include <gdk/gdkx.h> 82 83using JSC::ExecState; 84using JSC::Interpreter; 85using JSC::JSLock; 86using JSC::JSObject; 87 88using std::min; 89 90using namespace WTF; 91 92namespace WebCore { 93 94using namespace HTMLNames; 95 96bool PluginView::dispatchNPEvent(NPEvent& event) 97{ 98 // sanity check 99 if (!m_plugin->pluginFuncs()->event) 100 return false; 101 102 PluginView::setCurrentPluginView(this); 103 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 104 setCallingPlugin(true); 105 106 bool accepted = !m_plugin->pluginFuncs()->event(m_instance, &event); 107 108 setCallingPlugin(false); 109 PluginView::setCurrentPluginView(0); 110 return accepted; 111} 112 113static Window getRootWindow(Frame* parentFrame) 114{ 115 GtkWidget* parentWidget = parentFrame->view()->hostWindow()->platformPageClient(); 116 GdkScreen* gscreen = gtk_widget_get_screen(parentWidget); 117 return GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(gscreen)); 118} 119 120void PluginView::updatePluginWidget() 121{ 122 if (!parent()) 123 return; 124 125 ASSERT(parent()->isFrameView()); 126 FrameView* frameView = toFrameView(parent()); 127 128 IntRect oldWindowRect = m_windowRect; 129 IntRect oldClipRect = m_clipRect; 130 131 m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); 132 m_clipRect = windowClipRect(); 133 m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); 134 135 if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect) 136 return; 137 138 if (m_status != PluginStatusLoadedSuccessfully) 139 return; 140 141 if (!m_isWindowed && !m_windowRect.isEmpty()) { 142 Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 143 if (m_drawable) 144 XFreePixmap(display, m_drawable); 145 146 m_drawable = XCreatePixmap(display, getRootWindow(m_parentFrame.get()), 147 m_windowRect.width(), m_windowRect.height(), 148 ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth); 149 XSync(display, false); // make sure that the server knows about the Drawable 150 } 151 152 setNPWindowIfNeeded(); 153} 154 155void PluginView::setFocus(bool focused) 156{ 157 ASSERT(platformPluginWidget() == platformWidget()); 158 if (focused && platformWidget()) 159 gtk_widget_grab_focus(platformWidget()); 160 Widget::setFocus(focused); 161} 162 163void PluginView::show() 164{ 165 ASSERT(platformPluginWidget() == platformWidget()); 166 Widget::show(); 167} 168 169void PluginView::hide() 170{ 171 ASSERT(platformPluginWidget() == platformWidget()); 172 Widget::hide(); 173} 174 175void PluginView::paint(GraphicsContext* context, const IntRect& rect) 176{ 177 if (!m_isStarted || m_status != PluginStatusLoadedSuccessfully) { 178 paintMissingPluginIcon(context, rect); 179 return; 180 } 181 182 if (context->paintingDisabled()) 183 return; 184 185 setNPWindowIfNeeded(); 186 187 if (m_isWindowed) 188 return; 189 190 if (!m_drawable) 191 return; 192 193 Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 194 const bool syncX = m_pluginDisplay && m_pluginDisplay != display; 195 196 IntRect exposedRect(rect); 197 exposedRect.intersect(frameRect()); 198 exposedRect.move(-frameRect().x(), -frameRect().y()); 199 200 RefPtr<cairo_surface_t> drawableSurface = adoptRef(cairo_xlib_surface_create(display, 201 m_drawable, 202 m_visual, 203 m_windowRect.width(), 204 m_windowRect.height())); 205 206 if (m_isTransparent) { 207 // If we have a 32 bit drawable and the plugin wants transparency, 208 // we'll clear the exposed area to transparent first. Otherwise, 209 // we'd end up with junk in there from the last paint, or, worse, 210 // uninitialized data. 211 RefPtr<cairo_t> cr = adoptRef(cairo_create(drawableSurface.get())); 212 213 if (!(cairo_surface_get_content(drawableSurface.get()) & CAIRO_CONTENT_ALPHA)) { 214 // Attempt to fake it when we don't have an alpha channel on our 215 // pixmap. If that's not possible, at least clear the window to 216 // avoid drawing artifacts. 217 218 // This Would not work without double buffering, but we always use it. 219 cairo_set_source_surface(cr.get(), cairo_get_group_target(context->platformContext()->cr()), 220 -m_windowRect.x(), -m_windowRect.y()); 221 cairo_set_operator(cr.get(), CAIRO_OPERATOR_SOURCE); 222 } else 223 cairo_set_operator(cr.get(), CAIRO_OPERATOR_CLEAR); 224 225 cairo_rectangle(cr.get(), exposedRect.x(), exposedRect.y(), 226 exposedRect.width(), exposedRect.height()); 227 cairo_fill(cr.get()); 228 } 229 230 XEvent xevent; 231 memset(&xevent, 0, sizeof(XEvent)); 232 XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; 233 exposeEvent.type = GraphicsExpose; 234 exposeEvent.display = display; 235 exposeEvent.drawable = m_drawable; 236 exposeEvent.x = exposedRect.x(); 237 exposeEvent.y = exposedRect.y(); 238 exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode 239 exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode 240 241 dispatchNPEvent(xevent); 242 243 if (syncX) 244 XSync(m_pluginDisplay, false); // sync changes by plugin 245 246 cairo_t* cr = context->platformContext()->cr(); 247 cairo_save(cr); 248 249 cairo_set_source_surface(cr, drawableSurface.get(), frameRect().x(), frameRect().y()); 250 251 cairo_rectangle(cr, 252 frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y(), 253 exposedRect.width(), exposedRect.height()); 254 cairo_clip(cr); 255 256 if (m_isTransparent) 257 cairo_set_operator(cr, CAIRO_OPERATOR_OVER); 258 else 259 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); 260 cairo_paint(cr); 261 262 cairo_restore(cr); 263} 264 265void PluginView::handleKeyboardEvent(KeyboardEvent* event) 266{ 267 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 268 269 if (!m_isStarted || m_status != PluginStatusLoadedSuccessfully) 270 return; 271 272 if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent) 273 return; 274 275 NPEvent xEvent; 276 initXEvent(&xEvent); 277 GdkEventKey* gdkEvent = event->keyEvent()->gdkEventKey(); 278 279 xEvent.type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // KeyPress/Release get unset somewhere 280 xEvent.xkey.root = getRootWindow(m_parentFrame.get()); 281 xEvent.xkey.subwindow = 0; // we have no child window 282 xEvent.xkey.time = event->timeStamp(); 283 xEvent.xkey.state = gdkEvent->state; // GdkModifierType mirrors xlib state masks 284 xEvent.xkey.keycode = gdkEvent->hardware_keycode; 285 xEvent.xkey.same_screen = true; 286 287 // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window 288 // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not 289 // set to their normal Xserver values. e.g. Key events don't have a position. 290 // source: https://developer.mozilla.org/en/NPEvent 291 xEvent.xkey.x = 0; 292 xEvent.xkey.y = 0; 293 xEvent.xkey.x_root = 0; 294 xEvent.xkey.y_root = 0; 295 296 if (dispatchNPEvent(xEvent)) 297 event->setDefaultHandled(); 298} 299 300static unsigned int inputEventState(MouseEvent* event) 301{ 302 unsigned int state = 0; 303 if (event->ctrlKey()) 304 state |= ControlMask; 305 if (event->shiftKey()) 306 state |= ShiftMask; 307 if (event->altKey()) 308 state |= Mod1Mask; 309 if (event->metaKey()) 310 state |= Mod4Mask; 311 return state; 312} 313 314void PluginView::initXEvent(XEvent* xEvent) 315{ 316 memset(xEvent, 0, sizeof(XEvent)); 317 318 xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server 319 xEvent->xany.send_event = false; 320 GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient(); 321 xEvent->xany.display = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(widget)); 322 323 // Mozilla also sends None here for windowless plugins. See nsObjectFrame.cpp in the Mozilla sources. 324 // This method also sets up FocusIn and FocusOut events for windows plugins, but Mozilla doesn't 325 // even send these types of events to windowed plugins. In the future, it may be good to only 326 // send them to windowless plugins. 327 xEvent->xany.window = None; 328} 329 330static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame) 331{ 332 XButtonEvent& xbutton = xEvent->xbutton; 333 xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease; 334 xbutton.root = getRootWindow(parentFrame); 335 xbutton.subwindow = 0; 336 xbutton.time = event->timeStamp(); 337 xbutton.x = postZoomPos.x(); 338 xbutton.y = postZoomPos.y(); 339 xbutton.x_root = event->screenX(); 340 xbutton.y_root = event->screenY(); 341 xbutton.state = inputEventState(event); 342 switch (event->button()) { 343 case MiddleButton: 344 xbutton.button = Button2; 345 break; 346 case RightButton: 347 xbutton.button = Button3; 348 break; 349 case LeftButton: 350 default: 351 xbutton.button = Button1; 352 break; 353 } 354 xbutton.same_screen = true; 355} 356 357static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame) 358{ 359 XMotionEvent& xmotion = xEvent->xmotion; 360 xmotion.type = MotionNotify; 361 xmotion.root = getRootWindow(parentFrame); 362 xmotion.subwindow = 0; 363 xmotion.time = event->timeStamp(); 364 xmotion.x = postZoomPos.x(); 365 xmotion.y = postZoomPos.y(); 366 xmotion.x_root = event->screenX(); 367 xmotion.y_root = event->screenY(); 368 xmotion.state = inputEventState(event); 369 xmotion.is_hint = NotifyNormal; 370 xmotion.same_screen = true; 371} 372 373static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame) 374{ 375 XCrossingEvent& xcrossing = xEvent->xcrossing; 376 xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify; 377 xcrossing.root = getRootWindow(parentFrame); 378 xcrossing.subwindow = 0; 379 xcrossing.time = event->timeStamp(); 380 xcrossing.x = postZoomPos.y(); 381 xcrossing.y = postZoomPos.x(); 382 xcrossing.x_root = event->screenX(); 383 xcrossing.y_root = event->screenY(); 384 xcrossing.state = inputEventState(event); 385 xcrossing.mode = NotifyNormal; 386 xcrossing.detail = NotifyDetailNone; 387 xcrossing.same_screen = true; 388 xcrossing.focus = false; 389} 390 391void PluginView::handleMouseEvent(MouseEvent* event) 392{ 393 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 394 395 if (!m_isStarted || m_status != PluginStatusLoadedSuccessfully) 396 return; 397 398 if (event->button() == RightButton && m_plugin->quirks().contains(PluginQuirkIgnoreRightClickInWindowlessMode)) 399 return; 400 401 if (event->type() == eventNames().mousedownEvent) { 402 if (Page* page = m_parentFrame->page()) 403 page->focusController()->setActive(true); 404 focusPluginElement(); 405 } 406 407 NPEvent xEvent; 408 initXEvent(&xEvent); 409 410 IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); 411 412 if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent) 413 setXButtonEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get()); 414 else if (event->type() == eventNames().mousemoveEvent) 415 setXMotionEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get()); 416 else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent) { 417 setXCrossingEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get()); 418 419 // This is a work-around for plugins which change the cursor. When that happens we 420 // get out of sync with GDK somehow. Resetting the cursor here seems to fix the issue. 421 if (event->type() == eventNames().mouseoutEvent) 422 gdk_window_set_cursor(gtk_widget_get_window(m_parentFrame->view()->hostWindow()->platformPageClient()), 0); 423 } 424 else 425 return; 426 427 if (dispatchNPEvent(xEvent)) 428 event->setDefaultHandled(); 429} 430 431void PluginView::handleFocusInEvent() 432{ 433 if (!m_isStarted || m_status != PluginStatusLoadedSuccessfully) 434 return; 435 436 XEvent npEvent; 437 initXEvent(&npEvent); 438 439 XFocusChangeEvent& event = npEvent.xfocus; 440 event.type = 9; // FocusIn gets unset somewhere 441 event.mode = NotifyNormal; 442 event.detail = NotifyDetailNone; 443 444 dispatchNPEvent(npEvent); 445} 446 447void PluginView::handleFocusOutEvent() 448{ 449 if (!m_isStarted || m_status != PluginStatusLoadedSuccessfully) 450 return; 451 452 XEvent npEvent; 453 initXEvent(&npEvent); 454 455 XFocusChangeEvent& event = npEvent.xfocus; 456 event.type = 10; // FocusOut gets unset somewhere 457 event.mode = NotifyNormal; 458 event.detail = NotifyDetailNone; 459 460 dispatchNPEvent(npEvent); 461} 462 463void PluginView::setParent(ScrollView* parent) 464{ 465 Widget::setParent(parent); 466 467 if (parent) 468 init(); 469} 470 471void PluginView::setNPWindowRect(const IntRect&) 472{ 473 if (!m_isWindowed) 474 setNPWindowIfNeeded(); 475} 476 477void PluginView::setNPWindowIfNeeded() 478{ 479 if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow) 480 return; 481 482 // If the plugin didn't load sucessfully, no point in calling setwindow 483 if (m_status != PluginStatusLoadedSuccessfully) 484 return; 485 486 // On Unix, only call plugin's setwindow if it's full-page or windowed 487 if (m_mode != NP_FULL && m_mode != NP_EMBED) 488 return; 489 490 // Check if the platformPluginWidget still exists 491 if (m_isWindowed && !platformPluginWidget()) 492 return; 493 494 if (m_clipRect.isEmpty()) { 495 // If width or height are null, set the clipRect to null, 496 // indicating that the plugin is not visible/scrolled out. 497 m_npWindow.clipRect.left = 0; 498 m_npWindow.clipRect.right = 0; 499 m_npWindow.clipRect.top = 0; 500 m_npWindow.clipRect.bottom = 0; 501 } else { 502 // Clipping rectangle of the plug-in; the origin is the top left corner of the drawable or window. 503 m_npWindow.clipRect.left = m_npWindow.x + m_clipRect.x(); 504 m_npWindow.clipRect.top = m_npWindow.y + m_clipRect.y(); 505 m_npWindow.clipRect.right = m_npWindow.x + m_clipRect.x() + m_clipRect.width(); 506 m_npWindow.clipRect.bottom = m_npWindow.y + m_clipRect.y() + m_clipRect.height(); 507 } 508 509 // FLASH WORKAROUND: Only set initially. Multiple calls to 510 // setNPWindow() cause the plugin to crash in windowed mode. 511 if (!m_plugin->quirks().contains(PluginQuirkDontCallSetWindowMoreThanOnce) || !m_isWindowed 512 || m_npWindow.width == static_cast<uint32_t>(-1) || m_npWindow.height == static_cast<uint32_t>(-1)) { 513 m_npWindow.width = m_windowRect.width(); 514 m_npWindow.height = m_windowRect.height(); 515 } 516 517 PluginView::setCurrentPluginView(this); 518 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 519 setCallingPlugin(true); 520 m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); 521 setCallingPlugin(false); 522 PluginView::setCurrentPluginView(0); 523 524 if (!m_isWindowed) 525 return; 526 527 // GtkXtBin will call gtk_widget_size_allocate, so we don't need to do it here. 528 if (!m_needsXEmbed) { 529 gtk_xtbin_set_position(GTK_XTBIN(platformPluginWidget()), m_windowRect.x(), m_windowRect.y()); 530 gtk_xtbin_resize(platformPluginWidget(), m_windowRect.width(), m_windowRect.height()); 531 return; 532 } 533 534 m_delayedAllocation = m_windowRect; 535 updateWidgetAllocationAndClip(); 536} 537 538void PluginView::updateWidgetAllocationAndClip() 539{ 540 // If the window has not been embedded yet (the plug added), we delay setting its allocation until 541 // that point. This fixes issues with some Java plugin instances not rendering immediately. 542 if (!m_plugAdded || m_delayedAllocation.isEmpty()) 543 return; 544 545 GtkWidget* widget = platformPluginWidget(); 546 if (gtk_widget_get_realized(widget)) { 547 GdkRectangle clipRect = m_clipRect; 548#ifdef GTK_API_VERSION_2 549 GdkRegion* clipRegion = gdk_region_rectangle(&clipRect); 550 gdk_window_shape_combine_region(gtk_widget_get_window(widget), clipRegion, 0, 0); 551 gdk_region_destroy(clipRegion); 552#else 553 cairo_region_t* clipRegion = cairo_region_create_rectangle(&clipRect); 554 gdk_window_shape_combine_region(gtk_widget_get_window(widget), clipRegion, 0, 0); 555 cairo_region_destroy(clipRegion); 556#endif 557 } 558 559 // The goal is to try to avoid calling gtk_widget_size_allocate in the WebView's 560 // size-allocate method. It blocks the main loop and if the widget is offscreen 561 // or hasn't moved it isn't required. 562 563 // Don't do anything if the allocation has not changed. 564 GtkAllocation currentAllocation; 565 gtk_widget_get_allocation(widget, ¤tAllocation); 566 if (currentAllocation == m_delayedAllocation) 567 return; 568 569 // Don't do anything if both the old and the new allocations are outside the frame. 570 IntRect currentAllocationRect(currentAllocation); 571 currentAllocationRect.intersect(frameRect()); 572 if (currentAllocationRect.isEmpty() && m_clipRect.isEmpty()) 573 return; 574 575 g_object_set_data(G_OBJECT(widget), "delayed-allocation", &m_delayedAllocation); 576} 577 578void PluginView::setParentVisible(bool visible) 579{ 580 if (isParentVisible() == visible) 581 return; 582 583 Widget::setParentVisible(visible); 584 585 if (isSelfVisible() && platformPluginWidget()) { 586 if (visible) 587 gtk_widget_show(platformPluginWidget()); 588 else 589 gtk_widget_hide(platformPluginWidget()); 590 } 591} 592 593NPError PluginView::handlePostReadFile(Vector<char>& outputBuffer, uint32_t filenameLength, const char* filenameBuffer) 594{ 595 // There doesn't seem to be any documentation about what encoding the filename 596 // is in, but most ports seem to assume UTF-8 here and the test plugin is definitely 597 // sending the path in UTF-8 encoding. 598 CString filename(filenameBuffer, filenameLength); 599 600 GRefPtr<GFile> file = adoptGRef(g_file_new_for_commandline_arg(filename.data())); 601 if (g_file_query_file_type(file.get(), G_FILE_QUERY_INFO_NONE, 0) != G_FILE_TYPE_REGULAR) 602 return NPERR_FILE_NOT_FOUND; 603 604 GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(file.get(), 605 G_FILE_ATTRIBUTE_STANDARD_SIZE, 606 G_FILE_QUERY_INFO_NONE, 607 0, 0)); 608 if (!fileInfo) 609 return NPERR_FILE_NOT_FOUND; 610 611 GRefPtr<GFileInputStream> inputStream = adoptGRef(g_file_read(file.get(), 0, 0)); 612 if (!inputStream) 613 return NPERR_FILE_NOT_FOUND; 614 615 outputBuffer.resize(g_file_info_get_size(fileInfo.get())); 616 gsize bytesRead = 0; 617 if (!g_input_stream_read_all(G_INPUT_STREAM(inputStream.get()), 618 outputBuffer.data(), outputBuffer.size(), &bytesRead, 0, 0)) 619 return NPERR_FILE_NOT_FOUND; 620 621 return NPERR_NO_ERROR; 622} 623 624bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) 625{ 626 switch (variable) { 627 case NPNVToolkit: 628 *static_cast<uint32_t*>(value) = 2; 629 *result = NPERR_NO_ERROR; 630 return true; 631 632 case NPNVSupportsXEmbedBool: 633 *static_cast<NPBool*>(value) = true; 634 *result = NPERR_NO_ERROR; 635 return true; 636 637 case NPNVjavascriptEnabledBool: 638 *static_cast<NPBool*>(value) = true; 639 *result = NPERR_NO_ERROR; 640 return true; 641 642 case NPNVSupportsWindowless: 643 *static_cast<NPBool*>(value) = true; 644 *result = NPERR_NO_ERROR; 645 return true; 646 647 default: 648 return false; 649 } 650} 651 652bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) 653{ 654 switch (variable) { 655 case NPNVxDisplay: 656 if (m_needsXEmbed) 657 *(void **)value = (void *)GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 658 else 659 *(void **)value = (void *)GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay; 660 *result = NPERR_NO_ERROR; 661 return true; 662 663 case NPNVxtAppContext: 664 if (!m_needsXEmbed) { 665 *(void **)value = XtDisplayToApplicationContext (GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay); 666 667 *result = NPERR_NO_ERROR; 668 } else 669 *result = NPERR_GENERIC_ERROR; 670 return true; 671 672 case NPNVnetscapeWindow: { 673 GdkWindow* gdkWindow = gtk_widget_get_window(m_parentFrame->view()->hostWindow()->platformPageClient()); 674 GdkWindow* toplevelWindow = gdk_window_get_toplevel(gdkWindow); 675 if (!toplevelWindow) { 676 *result = NPERR_GENERIC_ERROR; 677 return true; 678 } 679 *static_cast<Window*>(value) = GDK_WINDOW_XWINDOW(toplevelWindow); 680 *result = NPERR_NO_ERROR; 681 return true; 682 } 683 684 default: 685 return false; 686 } 687} 688 689void PluginView::invalidateRect(const IntRect& rect) 690{ 691 if (m_isWindowed) { 692 gtk_widget_queue_draw_area(GTK_WIDGET(platformPluginWidget()), rect.x(), rect.y(), rect.width(), rect.height()); 693 return; 694 } 695 696 invalidateWindowlessPluginRect(rect); 697} 698 699void PluginView::invalidateRect(NPRect* rect) 700{ 701 if (!rect) { 702 invalidate(); 703 return; 704 } 705 706 IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); 707 invalidateWindowlessPluginRect(r); 708} 709 710void PluginView::invalidateRegion(NPRegion) 711{ 712 // TODO: optimize 713 invalidate(); 714} 715 716void PluginView::forceRedraw() 717{ 718 if (m_isWindowed) 719 gtk_widget_queue_draw(platformPluginWidget()); 720 else 721 gtk_widget_queue_draw(m_parentFrame->view()->hostWindow()->platformPageClient()); 722} 723 724static Display* getPluginDisplay() 725{ 726 // The plugin toolkit might have a different X connection open. Since we're 727 // a gdk/gtk app, we'll (probably?) have the same X connection as any gdk-based 728 // plugins, so we can return that. We might want to add other implementations here 729 // later. 730 731 return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 732} 733 734static void getVisualAndColormap(int depth, Visual** visual, Colormap* colormap) 735{ 736 *visual = 0; 737 *colormap = 0; 738 739 Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 740 int rmaj, rmin; 741 if (depth == 32 && (!XRenderQueryVersion(display, &rmaj, &rmin) || (rmaj == 0 && rmin < 5))) 742 return; 743 744 XVisualInfo templ; 745 templ.screen = gdk_screen_get_number(gdk_screen_get_default()); 746 templ.depth = depth; 747 templ.c_class = TrueColor; 748 int nVisuals; 749 XVisualInfo* visualInfo = XGetVisualInfo(display, VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nVisuals); 750 751 if (!nVisuals) 752 return; 753 754 if (depth == 32) { 755 for (int idx = 0; idx < nVisuals; ++idx) { 756 XRenderPictFormat* format = XRenderFindVisualFormat(display, visualInfo[idx].visual); 757 if (format->type == PictTypeDirect && format->direct.alphaMask) { 758 *visual = visualInfo[idx].visual; 759 break; 760 } 761 } 762 } else 763 *visual = visualInfo[0].visual; 764 765 XFree(visualInfo); 766 767 if (*visual) 768 *colormap = XCreateColormap(display, GDK_ROOT_WINDOW(), *visual, AllocNone); 769} 770 771gboolean PluginView::plugRemovedCallback(GtkSocket* socket, PluginView* view) 772{ 773 view->m_plugAdded = false; 774 return TRUE; 775} 776 777void PluginView::plugAddedCallback(GtkSocket* socket, PluginView* view) 778{ 779 ASSERT(socket); 780 ASSERT(view); 781 view->m_plugAdded = true; 782 view->updateWidgetAllocationAndClip(); 783} 784 785bool PluginView::platformStart() 786{ 787 ASSERT(m_isStarted); 788 ASSERT(m_status == PluginStatusLoadedSuccessfully); 789 790 if (m_plugin->pluginFuncs()->getvalue) { 791 PluginView::setCurrentPluginView(this); 792 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 793 setCallingPlugin(true); 794 m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed); 795 setCallingPlugin(false); 796 PluginView::setCurrentPluginView(0); 797 } 798 799 if (m_isWindowed) { 800 GtkWidget* pageClient = m_parentFrame->view()->hostWindow()->platformPageClient(); 801 if (m_needsXEmbed) { 802 // If our parent is not anchored the startup process will 803 // fail miserably for XEmbed plugins a bit later on when 804 // we try to get the ID of our window (since realize will 805 // fail), so let's just abort here. 806 if (!gtk_widget_get_parent(pageClient)) 807 return false; 808 809 m_plugAdded = false; 810 setPlatformWidget(gtk_socket_new()); 811 gtk_container_add(GTK_CONTAINER(pageClient), platformPluginWidget()); 812 g_signal_connect(platformPluginWidget(), "plug-added", G_CALLBACK(PluginView::plugAddedCallback), this); 813 g_signal_connect(platformPluginWidget(), "plug-removed", G_CALLBACK(PluginView::plugRemovedCallback), this); 814 } else 815 setPlatformWidget(gtk_xtbin_new(pageClient, 0)); 816 } else { 817 setPlatformWidget(0); 818 m_pluginDisplay = getPluginDisplay(); 819 } 820 821 show(); 822 823 NPSetWindowCallbackStruct* ws = new NPSetWindowCallbackStruct(); 824 ws->type = 0; 825 826 if (m_isWindowed) { 827 m_npWindow.type = NPWindowTypeWindow; 828 if (m_needsXEmbed) { 829 GtkWidget* widget = platformPluginWidget(); 830 gtk_widget_realize(widget); 831 m_npWindow.window = reinterpret_cast<void*>(gtk_socket_get_id(GTK_SOCKET(platformPluginWidget()))); 832 GdkWindow* window = gtk_widget_get_window(widget); 833 ws->display = GDK_WINDOW_XDISPLAY(window); 834 ws->visual = GDK_VISUAL_XVISUAL(gdk_window_get_visual(window)); 835 ws->depth = gdk_visual_get_depth(gdk_window_get_visual(window)); 836 ws->colormap = XCreateColormap(ws->display, GDK_ROOT_WINDOW(), ws->visual, AllocNone); 837 } else { 838 m_npWindow.window = reinterpret_cast<void*>((GTK_XTBIN(platformPluginWidget())->xtwindow)); 839 ws->display = GTK_XTBIN(platformPluginWidget())->xtdisplay; 840 ws->visual = GTK_XTBIN(platformPluginWidget())->xtclient.xtvisual; 841 ws->depth = GTK_XTBIN(platformPluginWidget())->xtclient.xtdepth; 842 ws->colormap = GTK_XTBIN(platformPluginWidget())->xtclient.xtcolormap; 843 } 844 XFlush (ws->display); 845 } else { 846 m_npWindow.type = NPWindowTypeDrawable; 847 m_npWindow.window = 0; // Not used? 848 849 GdkScreen* gscreen = gdk_screen_get_default(); 850 GdkVisual* gvisual = gdk_screen_get_system_visual(gscreen); 851 852 if (gdk_visual_get_depth(gvisual) == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) { 853 getVisualAndColormap(32, &m_visual, &m_colormap); 854 ws->depth = 32; 855 } 856 857 if (!m_visual) { 858 getVisualAndColormap(gdk_visual_get_depth(gvisual), &m_visual, &m_colormap); 859 ws->depth = gdk_visual_get_depth(gvisual); 860 } 861 862 ws->display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 863 ws->visual = m_visual; 864 ws->colormap = m_colormap; 865 866 m_npWindow.x = 0; 867 m_npWindow.y = 0; 868 m_npWindow.width = -1; 869 m_npWindow.height = -1; 870 } 871 872 m_npWindow.ws_info = ws; 873 874 // TODO remove in favor of null events, like mac port? 875 if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) 876 updatePluginWidget(); // was: setNPWindowIfNeeded(), but this doesn't produce 0x0 rects at first go 877 878 return true; 879} 880 881void PluginView::platformDestroy() 882{ 883 if (m_drawable) { 884 XFreePixmap(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), m_drawable); 885 m_drawable = 0; 886 } 887 888 GtkWidget* widget = platformWidget(); 889 if (widget) { 890 GtkWidget* parent = gtk_widget_get_parent(widget); 891 ASSERT(parent); 892 gtk_container_remove(GTK_CONTAINER(parent), widget); 893 } 894} 895 896} // namespace WebCore 897