/* | |
* Copyright (C) 2006, 2007 Apple Inc. All rights reserved. | |
* Copyright (C) 2008 Collabora Ltd. All rights reserved. | |
* Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#include "config.h" | |
#include "PluginView.h" | |
#include "Bridge.h" | |
#include "Document.h" | |
#include "DocumentLoader.h" | |
#include "Element.h" | |
#include "FloatPoint.h" | |
#include "FocusController.h" | |
#include "Frame.h" | |
#include "FrameLoadRequest.h" | |
#include "FrameLoader.h" | |
#include "FrameTree.h" | |
#include "FrameView.h" | |
#include "GraphicsContext.h" | |
#include "HTMLNames.h" | |
#include "HTMLPlugInElement.h" | |
#include "HostWindow.h" | |
#include "Image.h" | |
#include "JSDOMBinding.h" | |
#include "KeyboardEvent.h" | |
#include "MouseEvent.h" | |
#include "NotImplemented.h" | |
#include "Page.h" | |
#include "PlatformMouseEvent.h" | |
#include "PlatformKeyboardEvent.h" | |
#include "PluginContainerQt.h" | |
#include "PluginDebug.h" | |
#include "PluginPackage.h" | |
#include "PluginMainThreadScheduler.h" | |
#include "QWebPageClient.h" | |
#include "RenderLayer.h" | |
#include "ScriptController.h" | |
#include "Settings.h" | |
#include "npruntime_impl.h" | |
#include "runtime_root.h" | |
#include <QApplication> | |
#include <QDesktopWidget> | |
#include <QKeyEvent> | |
#include <QPainter> | |
#include <QWidget> | |
#include <QX11Info> | |
#include <X11/X.h> | |
#ifndef QT_NO_XRENDER | |
#define Bool int | |
#define Status int | |
#include <X11/extensions/Xrender.h> | |
#endif | |
#include <runtime/JSLock.h> | |
#include <runtime/JSValue.h> | |
using JSC::ExecState; | |
using JSC::Interpreter; | |
using JSC::JSLock; | |
using JSC::JSObject; | |
using JSC::UString; | |
using std::min; | |
using namespace WTF; | |
namespace WebCore { | |
using namespace HTMLNames; | |
void PluginView::updatePluginWidget() | |
{ | |
if (!parent()) | |
return; | |
ASSERT(parent()->isFrameView()); | |
FrameView* frameView = static_cast<FrameView*>(parent()); | |
IntRect oldWindowRect = m_windowRect; | |
IntRect oldClipRect = m_clipRect; | |
m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); | |
m_clipRect = windowClipRect(); | |
m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); | |
if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect) | |
return; | |
if (!m_isWindowed && m_windowRect.size() != oldWindowRect.size()) { | |
#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO == 5) | |
// On Maemo5, Flash always renders to 16-bit buffer | |
if (m_renderToImage) | |
m_image = QImage(m_windowRect.width(), m_windowRect.height(), QImage::Format_RGB16); | |
else | |
#endif | |
{ | |
if (m_drawable) | |
XFreePixmap(QX11Info::display(), m_drawable); | |
m_drawable = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), m_windowRect.width(), m_windowRect.height(), | |
((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth); | |
QApplication::syncX(); // make sure that the server knows about the Drawable | |
} | |
} | |
// do not call setNPWindowIfNeeded immediately, will be called on paint() | |
m_hasPendingGeometryChange = true; | |
// in order to move/resize the plugin window at the same time as the | |
// rest of frame during e.g. scrolling, we set the window geometry | |
// in the paint() function, but as paint() isn't called when the | |
// plugin window is outside the frame which can be caused by a | |
// scroll, we need to move/resize immediately. | |
if (!m_windowRect.intersects(frameView->frameRect())) | |
setNPWindowIfNeeded(); | |
// Make sure we get repainted afterwards. This is necessary for downward | |
// scrolling to move the plugin widget properly. | |
invalidate(); | |
} | |
void PluginView::setFocus() | |
{ | |
if (platformPluginWidget()) | |
platformPluginWidget()->setFocus(Qt::OtherFocusReason); | |
else | |
Widget::setFocus(); | |
} | |
void PluginView::show() | |
{ | |
Q_ASSERT(platformPluginWidget() == platformWidget()); | |
Widget::show(); | |
} | |
void PluginView::hide() | |
{ | |
Q_ASSERT(platformPluginWidget() == platformWidget()); | |
Widget::hide(); | |
} | |
#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO == 5) | |
void PluginView::paintUsingImageSurfaceExtension(QPainter* painter, const IntRect& exposedRect) | |
{ | |
NPImageExpose imageExpose; | |
QPoint offset; | |
QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); | |
const bool surfaceHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent()); | |
QPaintDevice* surface = QPainter::redirected(painter->device(), &offset); | |
// If the surface is a QImage, we can render directly into it | |
if (surfaceHasUntransformedContents && surface && surface->devType() == QInternal::Image) { | |
QImage* image = static_cast<QImage*>(surface); | |
offset = -offset; // negating the offset gives us the offset of the view within the surface | |
imageExpose.data = reinterpret_cast<char*>(image->bits()); | |
imageExpose.dataSize.width = image->width(); | |
imageExpose.dataSize.height = image->height(); | |
imageExpose.stride = image->bytesPerLine(); | |
imageExpose.depth = image->depth(); // this is guaranteed to be 16 on Maemo5 | |
imageExpose.translateX = offset.x() + m_windowRect.x(); | |
imageExpose.translateY = offset.y() + m_windowRect.y(); | |
imageExpose.scaleX = 1; | |
imageExpose.scaleY = 1; | |
} else { | |
if (m_isTransparent) { | |
// On Maemo5, Flash expects the buffer to contain the contents that are below it. | |
// We don't support transparency for non-raster graphicssystem, so clean the image | |
// before giving to Flash. | |
QPainter imagePainter(&m_image); | |
imagePainter.fillRect(exposedRect, Qt::white); | |
} | |
imageExpose.data = reinterpret_cast<char*>(m_image.bits()); | |
imageExpose.dataSize.width = m_image.width(); | |
imageExpose.dataSize.height = m_image.height(); | |
imageExpose.stride = m_image.bytesPerLine(); | |
imageExpose.depth = m_image.depth(); | |
imageExpose.translateX = 0; | |
imageExpose.translateY = 0; | |
imageExpose.scaleX = 1; | |
imageExpose.scaleY = 1; | |
} | |
imageExpose.x = exposedRect.x(); | |
imageExpose.y = exposedRect.y(); | |
imageExpose.width = exposedRect.width(); | |
imageExpose.height = exposedRect.height(); | |
XEvent xevent; | |
memset(&xevent, 0, sizeof(XEvent)); | |
XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; | |
exposeEvent.type = GraphicsExpose; | |
exposeEvent.display = 0; | |
exposeEvent.drawable = reinterpret_cast<XID>(&imageExpose); | |
exposeEvent.x = exposedRect.x(); | |
exposeEvent.y = exposedRect.y(); | |
exposeEvent.width = exposedRect.width(); | |
exposeEvent.height = exposedRect.height(); | |
dispatchNPEvent(xevent); | |
if (!surfaceHasUntransformedContents || !surface || surface->devType() != QInternal::Image) | |
painter->drawImage(QPoint(frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y()), m_image, exposedRect); | |
} | |
#endif | |
void PluginView::paint(GraphicsContext* context, const IntRect& rect) | |
{ | |
if (!m_isStarted) { | |
paintMissingPluginIcon(context, rect); | |
return; | |
} | |
if (context->paintingDisabled()) | |
return; | |
setNPWindowIfNeeded(); | |
if (m_isWindowed) | |
return; | |
if (!m_drawable | |
#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO == 5) | |
&& m_image.isNull() | |
#endif | |
) | |
return; | |
QPainter* painter = context->platformContext(); | |
IntRect exposedRect(rect); | |
exposedRect.intersect(frameRect()); | |
exposedRect.move(-frameRect().x(), -frameRect().y()); | |
#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO == 5) | |
if (!m_image.isNull()) { | |
paintUsingImageSurfaceExtension(painter, exposedRect); | |
return; | |
} | |
#endif | |
QPixmap qtDrawable = QPixmap::fromX11Pixmap(m_drawable, QPixmap::ExplicitlyShared); | |
const int drawableDepth = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth; | |
ASSERT(drawableDepth == qtDrawable.depth()); | |
const bool syncX = m_pluginDisplay && m_pluginDisplay != QX11Info::display(); | |
// When printing, Qt uses a QPicture to capture the output in preview mode. The | |
// QPicture holds a reference to the X Pixmap. As a result, the print preview would | |
// update itself when the X Pixmap changes. To prevent this, we create a copy. | |
if (m_element->document()->printing()) | |
qtDrawable = qtDrawable.copy(); | |
if (m_isTransparent && drawableDepth != 32) { | |
// Attempt content propagation for drawable with no alpha by copying over from the backing store | |
QPoint offset; | |
QPaintDevice* backingStoreDevice = QPainter::redirected(painter->device(), &offset); | |
offset = -offset; // negating the offset gives us the offset of the view within the backing store pixmap | |
const bool hasValidBackingStore = backingStoreDevice && backingStoreDevice->devType() == QInternal::Pixmap; | |
QPixmap* backingStorePixmap = static_cast<QPixmap*>(backingStoreDevice); | |
// We cannot grab contents from the backing store when painting on QGraphicsView items | |
// (because backing store contents are already transformed). What we really mean to do | |
// here is to check if we are painting on QWebView, but let's be a little permissive :) | |
QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); | |
const bool backingStoreHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent()); | |
if (hasValidBackingStore && backingStorePixmap->depth() == drawableDepth | |
&& backingStoreHasUntransformedContents) { | |
GC gc = XDefaultGC(QX11Info::display(), QX11Info::appScreen()); | |
XCopyArea(QX11Info::display(), backingStorePixmap->handle(), m_drawable, gc, | |
offset.x() + m_windowRect.x() + exposedRect.x(), offset.y() + m_windowRect.y() + exposedRect.y(), | |
exposedRect.width(), exposedRect.height(), exposedRect.x(), exposedRect.y()); | |
} else { // no backing store, clean the pixmap because the plugin thinks its transparent | |
QPainter painter(&qtDrawable); | |
painter.fillRect(exposedRect, Qt::white); | |
} | |
if (syncX) | |
QApplication::syncX(); | |
} | |
XEvent xevent; | |
memset(&xevent, 0, sizeof(XEvent)); | |
XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; | |
exposeEvent.type = GraphicsExpose; | |
exposeEvent.display = QX11Info::display(); | |
exposeEvent.drawable = qtDrawable.handle(); | |
exposeEvent.x = exposedRect.x(); | |
exposeEvent.y = exposedRect.y(); | |
exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode | |
exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode | |
dispatchNPEvent(xevent); | |
if (syncX) | |
XSync(m_pluginDisplay, False); // sync changes by plugin | |
painter->drawPixmap(QPoint(frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y()), qtDrawable, | |
exposedRect); | |
} | |
// TODO: Unify across ports. | |
bool PluginView::dispatchNPEvent(NPEvent& event) | |
{ | |
if (!m_plugin->pluginFuncs()->event) | |
return false; | |
PluginView::setCurrentPluginView(this); | |
JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); | |
setCallingPlugin(true); | |
bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event); | |
setCallingPlugin(false); | |
PluginView::setCurrentPluginView(0); | |
return accepted; | |
} | |
void setSharedXEventFields(XEvent* xEvent, QWidget* ownerWidget) | |
{ | |
xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server | |
xEvent->xany.send_event = false; | |
xEvent->xany.display = QX11Info::display(); | |
// NOTE: event->xany.window doesn't always respond to the .window property of other XEvent's | |
// but does in the case of KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify | |
// events; thus, this is right: | |
xEvent->xany.window = ownerWidget ? ownerWidget->window()->handle() : 0; | |
} | |
void PluginView::initXEvent(XEvent* xEvent) | |
{ | |
memset(xEvent, 0, sizeof(XEvent)); | |
QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); | |
QWidget* ownerWidget = client ? client->ownerWidget() : 0; | |
setSharedXEventFields(xEvent, ownerWidget); | |
} | |
void setXKeyEventSpecificFields(XEvent* xEvent, KeyboardEvent* event) | |
{ | |
QKeyEvent* qKeyEvent = event->keyEvent()->qtEvent(); | |
xEvent->type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // ints as Qt unsets KeyPress and KeyRelease | |
xEvent->xkey.root = QX11Info::appRootWindow(); | |
xEvent->xkey.subwindow = 0; // we have no child window | |
xEvent->xkey.time = event->timeStamp(); | |
xEvent->xkey.state = qKeyEvent->nativeModifiers(); | |
xEvent->xkey.keycode = qKeyEvent->nativeScanCode(); | |
xEvent->xkey.same_screen = true; | |
// NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window | |
// corresponding to the plug-in rectangle, some of the members of the XEvent structures are not | |
// set to their normal Xserver values. e.g. Key events don't have a position. | |
// source: https://developer.mozilla.org/en/NPEvent | |
xEvent->xkey.x = 0; | |
xEvent->xkey.y = 0; | |
xEvent->xkey.x_root = 0; | |
xEvent->xkey.y_root = 0; | |
} | |
void PluginView::handleKeyboardEvent(KeyboardEvent* event) | |
{ | |
if (m_isWindowed) | |
return; | |
if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent) | |
return; | |
XEvent npEvent; | |
initXEvent(&npEvent); | |
setXKeyEventSpecificFields(&npEvent, event); | |
if (!dispatchNPEvent(npEvent)) | |
event->setDefaultHandled(); | |
} | |
static unsigned int inputEventState(MouseEvent* event) | |
{ | |
unsigned int state = 0; | |
if (event->ctrlKey()) | |
state |= ControlMask; | |
if (event->shiftKey()) | |
state |= ShiftMask; | |
if (event->altKey()) | |
state |= Mod1Mask; | |
if (event->metaKey()) | |
state |= Mod4Mask; | |
return state; | |
} | |
static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) | |
{ | |
XButtonEvent& xbutton = xEvent->xbutton; | |
xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease; | |
xbutton.root = QX11Info::appRootWindow(); | |
xbutton.subwindow = 0; | |
xbutton.time = event->timeStamp(); | |
xbutton.x = postZoomPos.x(); | |
xbutton.y = postZoomPos.y(); | |
xbutton.x_root = event->screenX(); | |
xbutton.y_root = event->screenY(); | |
xbutton.state = inputEventState(event); | |
switch (event->button()) { | |
case MiddleButton: | |
xbutton.button = Button2; | |
break; | |
case RightButton: | |
xbutton.button = Button3; | |
break; | |
case LeftButton: | |
default: | |
xbutton.button = Button1; | |
break; | |
} | |
xbutton.same_screen = true; | |
} | |
static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) | |
{ | |
XMotionEvent& xmotion = xEvent->xmotion; | |
xmotion.type = MotionNotify; | |
xmotion.root = QX11Info::appRootWindow(); | |
xmotion.subwindow = 0; | |
xmotion.time = event->timeStamp(); | |
xmotion.x = postZoomPos.x(); | |
xmotion.y = postZoomPos.y(); | |
xmotion.x_root = event->screenX(); | |
xmotion.y_root = event->screenY(); | |
xmotion.state = inputEventState(event); | |
xmotion.is_hint = NotifyNormal; | |
xmotion.same_screen = true; | |
} | |
static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) | |
{ | |
XCrossingEvent& xcrossing = xEvent->xcrossing; | |
xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify; | |
xcrossing.root = QX11Info::appRootWindow(); | |
xcrossing.subwindow = 0; | |
xcrossing.time = event->timeStamp(); | |
xcrossing.x = postZoomPos.y(); | |
xcrossing.y = postZoomPos.x(); | |
xcrossing.x_root = event->screenX(); | |
xcrossing.y_root = event->screenY(); | |
xcrossing.state = inputEventState(event); | |
xcrossing.mode = NotifyNormal; | |
xcrossing.detail = NotifyDetailNone; | |
xcrossing.same_screen = true; | |
xcrossing.focus = false; | |
} | |
void PluginView::handleMouseEvent(MouseEvent* event) | |
{ | |
if (m_isWindowed) | |
return; | |
if (event->type() == eventNames().mousedownEvent) { | |
// Give focus to the plugin on click | |
if (Page* page = m_parentFrame->page()) | |
page->focusController()->setActive(true); | |
focusPluginElement(); | |
} | |
XEvent npEvent; | |
initXEvent(&npEvent); | |
IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); | |
if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent) | |
setXButtonEventSpecificFields(&npEvent, event, postZoomPos); | |
else if (event->type() == eventNames().mousemoveEvent) | |
setXMotionEventSpecificFields(&npEvent, event, postZoomPos); | |
else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent) | |
setXCrossingEventSpecificFields(&npEvent, event, postZoomPos); | |
else | |
return; | |
if (!dispatchNPEvent(npEvent)) | |
event->setDefaultHandled(); | |
} | |
void PluginView::handleFocusInEvent() | |
{ | |
XEvent npEvent; | |
initXEvent(&npEvent); | |
XFocusChangeEvent& event = npEvent.xfocus; | |
event.type = 9; /* int as Qt unsets FocusIn */ | |
event.mode = NotifyNormal; | |
event.detail = NotifyDetailNone; | |
dispatchNPEvent(npEvent); | |
} | |
void PluginView::handleFocusOutEvent() | |
{ | |
XEvent npEvent; | |
initXEvent(&npEvent); | |
XFocusChangeEvent& event = npEvent.xfocus; | |
event.type = 10; /* int as Qt unsets FocusOut */ | |
event.mode = NotifyNormal; | |
event.detail = NotifyDetailNone; | |
dispatchNPEvent(npEvent); | |
} | |
void PluginView::setParent(ScrollView* parent) | |
{ | |
Widget::setParent(parent); | |
if (parent) | |
init(); | |
} | |
void PluginView::setNPWindowRect(const IntRect&) | |
{ | |
if (!m_isWindowed) | |
setNPWindowIfNeeded(); | |
} | |
void PluginView::setNPWindowIfNeeded() | |
{ | |
if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow) | |
return; | |
// If the plugin didn't load sucessfully, no point in calling setwindow | |
if (m_status != PluginStatusLoadedSuccessfully) | |
return; | |
// On Unix, only call plugin if it's full-page or windowed | |
if (m_mode != NP_FULL && m_mode != NP_EMBED) | |
return; | |
// Check if the platformPluginWidget still exists | |
if (m_isWindowed && !platformPluginWidget()) | |
return; | |
if (!m_hasPendingGeometryChange) | |
return; | |
m_hasPendingGeometryChange = false; | |
if (m_isWindowed) { | |
platformPluginWidget()->setGeometry(m_windowRect); | |
// if setMask is set with an empty QRegion, no clipping will | |
// be performed, so in that case we hide the plugin view | |
platformPluginWidget()->setVisible(!m_clipRect.isEmpty()); | |
platformPluginWidget()->setMask(QRegion(m_clipRect)); | |
m_npWindow.x = m_windowRect.x(); | |
m_npWindow.y = m_windowRect.y(); | |
m_npWindow.clipRect.left = m_clipRect.x(); | |
m_npWindow.clipRect.top = m_clipRect.y(); | |
m_npWindow.clipRect.right = m_clipRect.width(); | |
m_npWindow.clipRect.bottom = m_clipRect.height(); | |
} else { | |
m_npWindow.x = 0; | |
m_npWindow.y = 0; | |
m_npWindow.clipRect.left = 0; | |
m_npWindow.clipRect.top = 0; | |
m_npWindow.clipRect.right = 0; | |
m_npWindow.clipRect.bottom = 0; | |
} | |
// FLASH WORKAROUND: Only set initially. Multiple calls to | |
// setNPWindow() cause the plugin to crash in windowed mode. | |
if (!m_isWindowed || m_npWindow.width == -1 || m_npWindow.height == -1) { | |
m_npWindow.width = m_windowRect.width(); | |
m_npWindow.height = m_windowRect.height(); | |
} | |
PluginView::setCurrentPluginView(this); | |
JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); | |
setCallingPlugin(true); | |
m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); | |
setCallingPlugin(false); | |
PluginView::setCurrentPluginView(0); | |
} | |
void PluginView::setParentVisible(bool visible) | |
{ | |
if (isParentVisible() == visible) | |
return; | |
Widget::setParentVisible(visible); | |
if (isSelfVisible() && platformPluginWidget()) | |
platformPluginWidget()->setVisible(visible); | |
} | |
NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf) | |
{ | |
String filename(buf, len); | |
if (filename.startsWith("file:///")) | |
filename = filename.substring(8); | |
long long size; | |
if (!getFileSize(filename, size)) | |
return NPERR_FILE_NOT_FOUND; | |
FILE* fileHandle = fopen((filename.utf8()).data(), "r"); | |
if (!fileHandle) | |
return NPERR_FILE_NOT_FOUND; | |
buffer.resize(size); | |
int bytesRead = fread(buffer.data(), 1, size, fileHandle); | |
fclose(fileHandle); | |
if (bytesRead <= 0) | |
return NPERR_FILE_NOT_FOUND; | |
return NPERR_NO_ERROR; | |
} | |
bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) | |
{ | |
switch (variable) { | |
case NPNVToolkit: | |
*static_cast<uint32*>(value) = 0; | |
*result = NPERR_NO_ERROR; | |
return true; | |
case NPNVSupportsXEmbedBool: | |
*static_cast<NPBool*>(value) = true; | |
*result = NPERR_NO_ERROR; | |
return true; | |
case NPNVjavascriptEnabledBool: | |
*static_cast<NPBool*>(value) = true; | |
*result = NPERR_NO_ERROR; | |
return true; | |
case NPNVSupportsWindowless: | |
*static_cast<NPBool*>(value) = true; | |
*result = NPERR_NO_ERROR; | |
return true; | |
#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO == 5) | |
case NPNVSupportsWindowlessLocal: | |
*static_cast<NPBool*>(value) = true; | |
*result = NPERR_NO_ERROR; | |
return true; | |
#endif | |
default: | |
return false; | |
} | |
} | |
bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) | |
{ | |
switch (variable) { | |
case NPNVxDisplay: | |
*(void **)value = QX11Info::display(); | |
*result = NPERR_NO_ERROR; | |
return true; | |
case NPNVxtAppContext: | |
*result = NPERR_GENERIC_ERROR; | |
return true; | |
case NPNVnetscapeWindow: { | |
void* w = reinterpret_cast<void*>(value); | |
QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); | |
*((XID *)w) = client ? client->ownerWidget()->window()->winId() : 0; | |
*result = NPERR_NO_ERROR; | |
return true; | |
} | |
case NPNVToolkit: | |
if (m_plugin->quirks().contains(PluginQuirkRequiresGtkToolKit)) { | |
*((uint32 *)value) = 2; | |
*result = NPERR_NO_ERROR; | |
return true; | |
} | |
return false; | |
default: | |
return false; | |
} | |
} | |
void PluginView::invalidateRect(const IntRect& rect) | |
{ | |
if (m_isWindowed) { | |
if (platformWidget()) | |
platformWidget()->update(rect); | |
return; | |
} | |
invalidateWindowlessPluginRect(rect); | |
} | |
void PluginView::invalidateRect(NPRect* rect) | |
{ | |
if (!rect) { | |
invalidate(); | |
return; | |
} | |
IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); | |
invalidateWindowlessPluginRect(r); | |
} | |
void PluginView::invalidateRegion(NPRegion region) | |
{ | |
invalidate(); | |
} | |
void PluginView::forceRedraw() | |
{ | |
invalidate(); | |
} | |
static Display *getPluginDisplay() | |
{ | |
// The plugin toolkit might run using a different X connection. At the moment, we only | |
// support gdk based plugins (like flash) that use a different X connection. | |
// The code below has the same effect as this one: | |
// Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default()); | |
QLibrary library("libgdk-x11-2.0.so.0"); | |
if (!library.load()) | |
return 0; | |
typedef void *(*gdk_display_get_default_ptr)(); | |
gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default"); | |
if (!gdk_display_get_default) | |
return 0; | |
typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *); | |
gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay"); | |
if (!gdk_x11_display_get_xdisplay) | |
return 0; | |
return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default()); | |
} | |
static void getVisualAndColormap(int depth, Visual **visual, Colormap *colormap) | |
{ | |
*visual = 0; | |
*colormap = 0; | |
#ifndef QT_NO_XRENDER | |
static const bool useXRender = qgetenv("QT_X11_NO_XRENDER").isNull(); // Should also check for XRender >= 0.5 | |
#else | |
static const bool useXRender = false; | |
#endif | |
if (!useXRender && depth == 32) | |
return; | |
int nvi; | |
XVisualInfo templ; | |
templ.screen = QX11Info::appScreen(); | |
templ.depth = depth; | |
templ.c_class = TrueColor; | |
XVisualInfo* xvi = XGetVisualInfo(QX11Info::display(), VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi); | |
if (!xvi) | |
return; | |
#ifndef QT_NO_XRENDER | |
if (depth == 32) { | |
for (int idx = 0; idx < nvi; ++idx) { | |
XRenderPictFormat* format = XRenderFindVisualFormat(QX11Info::display(), xvi[idx].visual); | |
if (format->type == PictTypeDirect && format->direct.alphaMask) { | |
*visual = xvi[idx].visual; | |
break; | |
} | |
} | |
} else | |
#endif // QT_NO_XRENDER | |
*visual = xvi[0].visual; | |
XFree(xvi); | |
if (*visual) | |
*colormap = XCreateColormap(QX11Info::display(), QX11Info::appRootWindow(), *visual, AllocNone); | |
} | |
bool PluginView::platformStart() | |
{ | |
ASSERT(m_isStarted); | |
ASSERT(m_status == PluginStatusLoadedSuccessfully); | |
if (m_plugin->pluginFuncs()->getvalue) { | |
PluginView::setCurrentPluginView(this); | |
JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); | |
setCallingPlugin(true); | |
m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed); | |
setCallingPlugin(false); | |
PluginView::setCurrentPluginView(0); | |
} | |
if (m_isWindowed) { | |
QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); | |
if (m_needsXEmbed && client) { | |
setPlatformWidget(new PluginContainerQt(this, client->ownerWidget())); | |
// sync our XEmbed container window creation before sending the xid to plugins. | |
QApplication::syncX(); | |
} else { | |
notImplemented(); | |
m_status = PluginStatusCanNotLoadPlugin; | |
return false; | |
} | |
} else { | |
setPlatformWidget(0); | |
m_pluginDisplay = getPluginDisplay(); | |
} | |
show(); | |
NPSetWindowCallbackStruct* wsi = new NPSetWindowCallbackStruct(); | |
wsi->type = 0; | |
if (m_isWindowed) { | |
const QX11Info* x11Info = &platformPluginWidget()->x11Info(); | |
wsi->display = x11Info->display(); | |
wsi->visual = (Visual*)x11Info->visual(); | |
wsi->depth = x11Info->depth(); | |
wsi->colormap = x11Info->colormap(); | |
m_npWindow.type = NPWindowTypeWindow; | |
m_npWindow.window = (void*)platformPluginWidget()->winId(); | |
m_npWindow.width = -1; | |
m_npWindow.height = -1; | |
} else { | |
const QX11Info* x11Info = &QApplication::desktop()->x11Info(); | |
if (x11Info->depth() == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) { | |
getVisualAndColormap(32, &m_visual, &m_colormap); | |
wsi->depth = 32; | |
} | |
if (!m_visual) { | |
getVisualAndColormap(x11Info->depth(), &m_visual, &m_colormap); | |
wsi->depth = x11Info->depth(); | |
} | |
wsi->display = x11Info->display(); | |
wsi->visual = m_visual; | |
wsi->colormap = m_colormap; | |
m_npWindow.type = NPWindowTypeDrawable; | |
m_npWindow.window = 0; // Not used? | |
m_npWindow.x = 0; | |
m_npWindow.y = 0; | |
m_npWindow.width = -1; | |
m_npWindow.height = -1; | |
} | |
m_npWindow.ws_info = wsi; | |
if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) { | |
updatePluginWidget(); | |
setNPWindowIfNeeded(); | |
} | |
return true; | |
} | |
void PluginView::platformDestroy() | |
{ | |
if (platformPluginWidget()) | |
delete platformPluginWidget(); | |
if (m_drawable) | |
XFreePixmap(QX11Info::display(), m_drawable); | |
if (m_colormap) | |
XFreeColormap(QX11Info::display(), m_colormap); | |
} | |
void PluginView::halt() | |
{ | |
} | |
void PluginView::restart() | |
{ | |
} | |
} // namespace WebCore |