1 /*
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4 * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
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 #if USE(JSC)
32 #include "BridgeJSC.h"
33 #endif
34 #include "Chrome.h"
35 #include "ChromeClient.h"
36 #include "Document.h"
37 #include "DocumentLoader.h"
38 #include "Element.h"
39 #include "FloatPoint.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 "IFrameShimSupport.h"
51 #include "Image.h"
52 #if USE(JSC)
53 #include "JSDOMBinding.h"
54 #endif
55 #include "KeyboardEvent.h"
56 #include "MouseEvent.h"
57 #include "NotImplemented.h"
58 #include "Page.h"
59 #include "PlatformMouseEvent.h"
60 #include "PlatformKeyboardEvent.h"
61 #include "PluginContainerQt.h"
62 #include "PluginDebug.h"
63 #include "PluginPackage.h"
64 #include "PluginMainThreadScheduler.h"
65 #include "QWebPageClient.h"
66 #include "RenderLayer.h"
67 #include "Settings.h"
68 #include "npruntime_impl.h"
69 #include "qwebpage_p.h"
70 #if USE(JSC)
71 #include "runtime_root.h"
72 #endif
73
74 #include <QApplication>
75 #include <QDesktopWidget>
76 #include <QGraphicsWidget>
77 #include <QKeyEvent>
78 #include <QPainter>
79 #include <QStyleOptionGraphicsItem>
80 #include <QWidget>
81 #include <QX11Info>
82 #include <X11/X.h>
83 #ifndef QT_NO_XRENDER
84 #define Bool int
85 #define Status int
86 #include <X11/extensions/Xrender.h>
87 #endif
88 #include <runtime/JSLock.h>
89 #include <runtime/JSValue.h>
90
91 using JSC::ExecState;
92 #if USE(JSC)
93 using JSC::Interpreter;
94 #endif
95 using JSC::JSLock;
96 using JSC::JSObject;
97 using JSC::UString;
98
99 using std::min;
100
101 using namespace WTF;
102
103 namespace WebCore {
104
105 using namespace HTMLNames;
106
107 #if USE(ACCELERATED_COMPOSITING)
108 // Qt's GraphicsLayer (GraphicsLayerQt) requires layers to be QGraphicsWidgets
109 class PluginGraphicsLayerQt : public QGraphicsWidget {
110 public:
PluginGraphicsLayerQt(PluginView * view)111 PluginGraphicsLayerQt(PluginView* view) : m_view(view) { }
~PluginGraphicsLayerQt()112 ~PluginGraphicsLayerQt() { }
113
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget=0)114 void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0)
115 {
116 Q_UNUSED(widget);
117 m_view->paintUsingXPixmap(painter, option->exposedRect.toRect());
118 }
119
120 private:
121 PluginView* m_view;
122 };
123
shouldUseAcceleratedCompositing() const124 bool PluginView::shouldUseAcceleratedCompositing() const
125 {
126 return m_parentFrame->page()->chrome()->client()->allowsAcceleratedCompositing()
127 && m_parentFrame->page()->settings()
128 && m_parentFrame->page()->settings()->acceleratedCompositingEnabled();
129 }
130 #endif
131
updatePluginWidget()132 void PluginView::updatePluginWidget()
133 {
134 if (!parent())
135 return;
136
137 ASSERT(parent()->isFrameView());
138 FrameView* frameView = static_cast<FrameView*>(parent());
139
140 IntRect oldWindowRect = m_windowRect;
141 IntRect oldClipRect = m_clipRect;
142
143 m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
144 m_clipRect = windowClipRect();
145 m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
146
147 if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect)
148 return;
149
150 // The plugin had a zero width or height before but was resized, we need to show it again.
151 if (oldWindowRect.isEmpty())
152 show();
153
154 if (!m_isWindowed && m_windowRect.size() != oldWindowRect.size()) {
155 #if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5)
156 // On Maemo5, Flash always renders to 16-bit buffer
157 if (m_renderToImage)
158 m_image = QImage(m_windowRect.width(), m_windowRect.height(), QImage::Format_RGB16);
159 else
160 #endif
161 {
162 if (m_drawable)
163 XFreePixmap(QX11Info::display(), m_drawable);
164
165 m_drawable = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), m_windowRect.width(), m_windowRect.height(),
166 ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth);
167 QApplication::syncX(); // make sure that the server knows about the Drawable
168 }
169 }
170
171 // do not call setNPWindowIfNeeded immediately, will be called on paint()
172 m_hasPendingGeometryChange = true;
173
174 // (i) in order to move/resize the plugin window at the same time as the
175 // rest of frame during e.g. scrolling, we set the window geometry
176 // in the paint() function, but as paint() isn't called when the
177 // plugin window is outside the frame which can be caused by a
178 // scroll, we need to move/resize immediately.
179 // (ii) if we are running layout tests from DRT, paint() won't ever get called
180 // so we need to call setNPWindowIfNeeded() if window geometry has changed
181 if (!m_windowRect.intersects(frameView->frameRect())
182 || (QWebPagePrivate::drtRun && platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect)))
183 setNPWindowIfNeeded();
184
185 if (!m_platformLayer) {
186 // Make sure we get repainted afterwards. This is necessary for downward
187 // scrolling to move the plugin widget properly.
188 // Note that we don't invalidate the frameRect() here. This is because QWebFrame::renderRelativeCoords()
189 // imitates ScrollView and adds the scroll offset back on to the rect we damage here (making the co-ordinates absolute
190 // to the frame again) before passing it to FrameView.
191 invalidate();
192 }
193 }
194
setFocus(bool focused)195 void PluginView::setFocus(bool focused)
196 {
197 if (platformPluginWidget()) {
198 if (focused)
199 platformPluginWidget()->setFocus(Qt::OtherFocusReason);
200 } else {
201 Widget::setFocus(focused);
202 }
203 }
204
show()205 void PluginView::show()
206 {
207 Q_ASSERT(platformPluginWidget() == platformWidget());
208 Widget::show();
209 }
210
hide()211 void PluginView::hide()
212 {
213 Q_ASSERT(platformPluginWidget() == platformWidget());
214 Widget::hide();
215 }
216
217 #if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5)
paintUsingImageSurfaceExtension(QPainter * painter,const IntRect & exposedRect)218 void PluginView::paintUsingImageSurfaceExtension(QPainter* painter, const IntRect& exposedRect)
219 {
220 NPImageExpose imageExpose;
221 QPoint offset;
222 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
223 const bool surfaceHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent());
224
225 QPaintDevice* surface = QPainter::redirected(painter->device(), &offset);
226
227 // If the surface is a QImage, we can render directly into it
228 if (surfaceHasUntransformedContents && surface && surface->devType() == QInternal::Image) {
229 QImage* image = static_cast<QImage*>(surface);
230 offset = -offset; // negating the offset gives us the offset of the view within the surface
231 imageExpose.data = reinterpret_cast<char*>(image->bits());
232 imageExpose.dataSize.width = image->width();
233 imageExpose.dataSize.height = image->height();
234 imageExpose.stride = image->bytesPerLine();
235 imageExpose.depth = image->depth(); // this is guaranteed to be 16 on Maemo5
236 imageExpose.translateX = offset.x() + m_windowRect.x();
237 imageExpose.translateY = offset.y() + m_windowRect.y();
238 imageExpose.scaleX = 1;
239 imageExpose.scaleY = 1;
240 } else {
241 if (m_isTransparent) {
242 // On Maemo5, Flash expects the buffer to contain the contents that are below it.
243 // We don't support transparency for non-raster graphicssystem, so clean the image
244 // before giving to Flash.
245 QPainter imagePainter(&m_image);
246 imagePainter.fillRect(exposedRect, Qt::white);
247 }
248
249 imageExpose.data = reinterpret_cast<char*>(m_image.bits());
250 imageExpose.dataSize.width = m_image.width();
251 imageExpose.dataSize.height = m_image.height();
252 imageExpose.stride = m_image.bytesPerLine();
253 imageExpose.depth = m_image.depth();
254 imageExpose.translateX = 0;
255 imageExpose.translateY = 0;
256 imageExpose.scaleX = 1;
257 imageExpose.scaleY = 1;
258 }
259 imageExpose.x = exposedRect.x();
260 imageExpose.y = exposedRect.y();
261 imageExpose.width = exposedRect.width();
262 imageExpose.height = exposedRect.height();
263
264 XEvent xevent;
265 memset(&xevent, 0, sizeof(XEvent));
266 XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose;
267 exposeEvent.type = GraphicsExpose;
268 exposeEvent.display = 0;
269 exposeEvent.drawable = reinterpret_cast<XID>(&imageExpose);
270 exposeEvent.x = exposedRect.x();
271 exposeEvent.y = exposedRect.y();
272 exposeEvent.width = exposedRect.width();
273 exposeEvent.height = exposedRect.height();
274
275 dispatchNPEvent(xevent);
276
277 if (!surfaceHasUntransformedContents || !surface || surface->devType() != QInternal::Image)
278 painter->drawImage(QPoint(frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y()), m_image, exposedRect);
279 }
280 #endif
281
paintUsingXPixmap(QPainter * painter,const QRect & exposedRect)282 void PluginView::paintUsingXPixmap(QPainter* painter, const QRect &exposedRect)
283 {
284 QPixmap qtDrawable = QPixmap::fromX11Pixmap(m_drawable, QPixmap::ExplicitlyShared);
285 const int drawableDepth = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth;
286 ASSERT(drawableDepth == qtDrawable.depth());
287 const bool syncX = m_pluginDisplay && m_pluginDisplay != QX11Info::display();
288
289 // When printing, Qt uses a QPicture to capture the output in preview mode. The
290 // QPicture holds a reference to the X Pixmap. As a result, the print preview would
291 // update itself when the X Pixmap changes. To prevent this, we create a copy.
292 if (m_element->document()->printing())
293 qtDrawable = qtDrawable.copy();
294
295 if (m_isTransparent && drawableDepth != 32) {
296 // Attempt content propagation for drawable with no alpha by copying over from the backing store
297 QPoint offset;
298 QPaintDevice* backingStoreDevice = QPainter::redirected(painter->device(), &offset);
299 offset = -offset; // negating the offset gives us the offset of the view within the backing store pixmap
300
301 const bool hasValidBackingStore = backingStoreDevice && backingStoreDevice->devType() == QInternal::Pixmap;
302 QPixmap* backingStorePixmap = static_cast<QPixmap*>(backingStoreDevice);
303
304 // We cannot grab contents from the backing store when painting on QGraphicsView items
305 // (because backing store contents are already transformed). What we really mean to do
306 // here is to check if we are painting on QWebView, but let's be a little permissive :)
307 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
308 const bool backingStoreHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent());
309
310 if (hasValidBackingStore && backingStorePixmap->depth() == drawableDepth
311 && backingStoreHasUntransformedContents) {
312 GC gc = XDefaultGC(QX11Info::display(), QX11Info::appScreen());
313 XCopyArea(QX11Info::display(), backingStorePixmap->handle(), m_drawable, gc,
314 offset.x() + m_windowRect.x() + exposedRect.x(), offset.y() + m_windowRect.y() + exposedRect.y(),
315 exposedRect.width(), exposedRect.height(), exposedRect.x(), exposedRect.y());
316 } else { // no backing store, clean the pixmap because the plugin thinks its transparent
317 QPainter painter(&qtDrawable);
318 painter.fillRect(exposedRect, Qt::white);
319 }
320
321 if (syncX)
322 QApplication::syncX();
323 }
324
325 XEvent xevent;
326 memset(&xevent, 0, sizeof(XEvent));
327 XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose;
328 exposeEvent.type = GraphicsExpose;
329 exposeEvent.display = QX11Info::display();
330 exposeEvent.drawable = qtDrawable.handle();
331 exposeEvent.x = exposedRect.x();
332 exposeEvent.y = exposedRect.y();
333 exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode
334 exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode
335
336 dispatchNPEvent(xevent);
337
338 if (syncX)
339 XSync(m_pluginDisplay, false); // sync changes by plugin
340
341 painter->drawPixmap(QPoint(exposedRect.x(), exposedRect.y()), qtDrawable, exposedRect);
342 }
343
paint(GraphicsContext * context,const IntRect & rect)344 void PluginView::paint(GraphicsContext* context, const IntRect& rect)
345 {
346 if (!m_isStarted) {
347 paintMissingPluginIcon(context, rect);
348 return;
349 }
350
351 if (context->paintingDisabled())
352 return;
353
354 setNPWindowIfNeeded();
355
356 if (m_isWindowed)
357 return;
358
359 #if USE(ACCELERATED_COMPOSITING)
360 if (m_platformLayer)
361 return;
362 #endif
363
364 if (!m_drawable
365 #if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5)
366 && m_image.isNull()
367 #endif
368 )
369 return;
370
371 QPainter* painter = context->platformContext();
372 IntRect exposedRect(rect);
373 exposedRect.intersect(frameRect());
374 exposedRect.move(-frameRect().x(), -frameRect().y());
375
376 #if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5)
377 if (!m_image.isNull()) {
378 paintUsingImageSurfaceExtension(painter, exposedRect);
379 return;
380 }
381 #endif
382
383 painter->translate(frameRect().x(), frameRect().y());
384 paintUsingXPixmap(painter, exposedRect);
385 painter->translate(-frameRect().x(), -frameRect().y());
386 }
387
388 // TODO: Unify across ports.
dispatchNPEvent(NPEvent & event)389 bool PluginView::dispatchNPEvent(NPEvent& event)
390 {
391 if (!m_plugin->pluginFuncs()->event)
392 return false;
393
394 PluginView::setCurrentPluginView(this);
395 #if USE(JSC)
396 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
397 #endif
398 setCallingPlugin(true);
399 bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
400 setCallingPlugin(false);
401 PluginView::setCurrentPluginView(0);
402
403 return accepted;
404 }
405
setSharedXEventFields(XEvent * xEvent,QWidget * ownerWidget)406 void setSharedXEventFields(XEvent* xEvent, QWidget* ownerWidget)
407 {
408 xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server
409 xEvent->xany.send_event = false;
410 xEvent->xany.display = QX11Info::display();
411 // NOTE: event->xany.window doesn't always respond to the .window property of other XEvent's
412 // but does in the case of KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify
413 // events; thus, this is right:
414 xEvent->xany.window = ownerWidget ? ownerWidget->window()->handle() : 0;
415 }
416
initXEvent(XEvent * xEvent)417 void PluginView::initXEvent(XEvent* xEvent)
418 {
419 memset(xEvent, 0, sizeof(XEvent));
420
421 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
422 QWidget* ownerWidget = client ? client->ownerWidget() : 0;
423 setSharedXEventFields(xEvent, ownerWidget);
424 }
425
setXKeyEventSpecificFields(XEvent * xEvent,KeyboardEvent * event)426 void setXKeyEventSpecificFields(XEvent* xEvent, KeyboardEvent* event)
427 {
428 const PlatformKeyboardEvent* keyEvent = event->keyEvent();
429
430 xEvent->type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // ints as Qt unsets KeyPress and KeyRelease
431 xEvent->xkey.root = QX11Info::appRootWindow();
432 xEvent->xkey.subwindow = 0; // we have no child window
433 xEvent->xkey.time = event->timeStamp();
434 xEvent->xkey.state = keyEvent->nativeModifiers();
435 xEvent->xkey.keycode = keyEvent->nativeScanCode();
436
437 // We may not have a nativeScanCode() if the key event is from DRT's eventsender. In that
438 // case fetch the XEvent's keycode from the event's text. The only
439 // place this keycode will be used is in webkit_test_plugin_handle_event().
440 // FIXME: Create Qt API so that we can set the appropriate keycode in DRT EventSender instead.
441 if (QWebPagePrivate::drtRun && !xEvent->xkey.keycode) {
442 QKeyEvent* qKeyEvent = keyEvent->qtEvent();
443 ASSERT(qKeyEvent);
444 QString keyText = qKeyEvent->text().left(1);
445 xEvent->xkey.keycode = XKeysymToKeycode(QX11Info::display(), XStringToKeysym(keyText.toUtf8().constData()));
446 }
447
448 xEvent->xkey.same_screen = true;
449
450 // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window
451 // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not
452 // set to their normal Xserver values. e.g. Key events don't have a position.
453 // source: https://developer.mozilla.org/en/NPEvent
454 xEvent->xkey.x = 0;
455 xEvent->xkey.y = 0;
456 xEvent->xkey.x_root = 0;
457 xEvent->xkey.y_root = 0;
458 }
459
handleKeyboardEvent(KeyboardEvent * event)460 void PluginView::handleKeyboardEvent(KeyboardEvent* event)
461 {
462 if (m_isWindowed)
463 return;
464
465 if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent)
466 return;
467
468 XEvent npEvent;
469 initXEvent(&npEvent);
470 setXKeyEventSpecificFields(&npEvent, event);
471
472 if (!dispatchNPEvent(npEvent))
473 event->setDefaultHandled();
474 }
475
inputEventState(MouseEvent * event)476 static unsigned int inputEventState(MouseEvent* event)
477 {
478 unsigned int state = 0;
479 if (event->ctrlKey())
480 state |= ControlMask;
481 if (event->shiftKey())
482 state |= ShiftMask;
483 if (event->altKey())
484 state |= Mod1Mask;
485 if (event->metaKey())
486 state |= Mod4Mask;
487 return state;
488 }
489
setXButtonEventSpecificFields(XEvent * xEvent,MouseEvent * event,const IntPoint & postZoomPos)490 static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos)
491 {
492 XButtonEvent& xbutton = xEvent->xbutton;
493 xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease;
494 xbutton.root = QX11Info::appRootWindow();
495 xbutton.subwindow = 0;
496 xbutton.time = event->timeStamp();
497 xbutton.x = postZoomPos.x();
498 xbutton.y = postZoomPos.y();
499 xbutton.x_root = event->screenX();
500 xbutton.y_root = event->screenY();
501 xbutton.state = inputEventState(event);
502 switch (event->button()) {
503 case MiddleButton:
504 xbutton.button = Button2;
505 break;
506 case RightButton:
507 xbutton.button = Button3;
508 break;
509 case LeftButton:
510 default:
511 xbutton.button = Button1;
512 break;
513 }
514 xbutton.same_screen = true;
515 }
516
setXMotionEventSpecificFields(XEvent * xEvent,MouseEvent * event,const IntPoint & postZoomPos)517 static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos)
518 {
519 XMotionEvent& xmotion = xEvent->xmotion;
520 xmotion.type = MotionNotify;
521 xmotion.root = QX11Info::appRootWindow();
522 xmotion.subwindow = 0;
523 xmotion.time = event->timeStamp();
524 xmotion.x = postZoomPos.x();
525 xmotion.y = postZoomPos.y();
526 xmotion.x_root = event->screenX();
527 xmotion.y_root = event->screenY();
528 xmotion.state = inputEventState(event);
529 xmotion.is_hint = NotifyNormal;
530 xmotion.same_screen = true;
531 }
532
setXCrossingEventSpecificFields(XEvent * xEvent,MouseEvent * event,const IntPoint & postZoomPos)533 static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos)
534 {
535 XCrossingEvent& xcrossing = xEvent->xcrossing;
536 xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify;
537 xcrossing.root = QX11Info::appRootWindow();
538 xcrossing.subwindow = 0;
539 xcrossing.time = event->timeStamp();
540 xcrossing.x = postZoomPos.y();
541 xcrossing.y = postZoomPos.x();
542 xcrossing.x_root = event->screenX();
543 xcrossing.y_root = event->screenY();
544 xcrossing.state = inputEventState(event);
545 xcrossing.mode = NotifyNormal;
546 xcrossing.detail = NotifyDetailNone;
547 xcrossing.same_screen = true;
548 xcrossing.focus = false;
549 }
550
handleMouseEvent(MouseEvent * event)551 void PluginView::handleMouseEvent(MouseEvent* event)
552 {
553 if (m_isWindowed)
554 return;
555
556 if (event->button() == RightButton && m_plugin->quirks().contains(PluginQuirkIgnoreRightClickInWindowlessMode))
557 return;
558
559 if (event->type() == eventNames().mousedownEvent) {
560 // Give focus to the plugin on click
561 if (Page* page = m_parentFrame->page())
562 page->focusController()->setActive(true);
563
564 focusPluginElement();
565 }
566
567 XEvent npEvent;
568 initXEvent(&npEvent);
569
570 IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation()));
571
572 if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
573 setXButtonEventSpecificFields(&npEvent, event, postZoomPos);
574 else if (event->type() == eventNames().mousemoveEvent)
575 setXMotionEventSpecificFields(&npEvent, event, postZoomPos);
576 else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent)
577 setXCrossingEventSpecificFields(&npEvent, event, postZoomPos);
578 else
579 return;
580
581 if (!dispatchNPEvent(npEvent))
582 event->setDefaultHandled();
583 }
584
handleFocusInEvent()585 void PluginView::handleFocusInEvent()
586 {
587 XEvent npEvent;
588 initXEvent(&npEvent);
589
590 XFocusChangeEvent& event = npEvent.xfocus;
591 event.type = 9; /* int as Qt unsets FocusIn */
592 event.mode = NotifyNormal;
593 event.detail = NotifyDetailNone;
594
595 dispatchNPEvent(npEvent);
596 }
597
handleFocusOutEvent()598 void PluginView::handleFocusOutEvent()
599 {
600 XEvent npEvent;
601 initXEvent(&npEvent);
602
603 XFocusChangeEvent& event = npEvent.xfocus;
604 event.type = 10; /* int as Qt unsets FocusOut */
605 event.mode = NotifyNormal;
606 event.detail = NotifyDetailNone;
607
608 dispatchNPEvent(npEvent);
609 }
610
setParent(ScrollView * parent)611 void PluginView::setParent(ScrollView* parent)
612 {
613 Widget::setParent(parent);
614
615 if (parent)
616 init();
617 }
618
setNPWindowRect(const IntRect &)619 void PluginView::setNPWindowRect(const IntRect&)
620 {
621 if (!m_isWindowed)
622 setNPWindowIfNeeded();
623 }
624
setNPWindowIfNeeded()625 void PluginView::setNPWindowIfNeeded()
626 {
627 if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
628 return;
629
630 // If the plugin didn't load sucessfully, no point in calling setwindow
631 if (m_status != PluginStatusLoadedSuccessfully)
632 return;
633
634 // On Unix, only call plugin if it's full-page or windowed
635 if (m_mode != NP_FULL && m_mode != NP_EMBED)
636 return;
637
638 // Check if the platformPluginWidget still exists
639 if (m_isWindowed && !platformPluginWidget())
640 return;
641
642 if (!m_hasPendingGeometryChange)
643 return;
644 m_hasPendingGeometryChange = false;
645
646 if (m_isWindowed) {
647 platformPluginWidget()->setGeometry(m_windowRect);
648
649 // Cut out areas of the plugin occluded by iframe shims
650 Vector<IntRect> cutOutRects;
651 QRegion clipRegion = QRegion(m_clipRect);
652 getPluginOcclusions(m_element, this->parent(), frameRect(), cutOutRects);
653 for (size_t i = 0; i < cutOutRects.size(); i++) {
654 cutOutRects[i].move(-frameRect().x(), -frameRect().y());
655 clipRegion = clipRegion.subtracted(QRegion(cutOutRects[i]));
656 }
657 // if setMask is set with an empty QRegion, no clipping will
658 // be performed, so in that case we hide the plugin view
659 platformPluginWidget()->setVisible(!clipRegion.isEmpty());
660 platformPluginWidget()->setMask(clipRegion);
661
662 m_npWindow.x = m_windowRect.x();
663 m_npWindow.y = m_windowRect.y();
664 } else {
665 m_npWindow.x = 0;
666 m_npWindow.y = 0;
667 }
668
669 // If the width or height are null, set the clipRect to null, indicating that
670 // the plugin is not visible/scrolled out.
671 if (!m_clipRect.width() || !m_clipRect.height()) {
672 m_npWindow.clipRect.left = 0;
673 m_npWindow.clipRect.right = 0;
674 m_npWindow.clipRect.top = 0;
675 m_npWindow.clipRect.bottom = 0;
676 } else {
677 // Clipping rectangle of the plug-in; the origin is the top left corner of the drawable or window.
678 m_npWindow.clipRect.left = m_npWindow.x + m_clipRect.x();
679 m_npWindow.clipRect.top = m_npWindow.y + m_clipRect.y();
680 m_npWindow.clipRect.right = m_npWindow.x + m_clipRect.x() + m_clipRect.width();
681 m_npWindow.clipRect.bottom = m_npWindow.y + m_clipRect.y() + m_clipRect.height();
682 }
683
684 if (m_plugin->quirks().contains(PluginQuirkDontCallSetWindowMoreThanOnce)) {
685 // FLASH WORKAROUND: Only set initially. Multiple calls to
686 // setNPWindow() cause the plugin to crash in windowed mode.
687 if (!m_isWindowed || m_npWindow.width == -1 || m_npWindow.height == -1) {
688 m_npWindow.width = m_windowRect.width();
689 m_npWindow.height = m_windowRect.height();
690 }
691 } else {
692 m_npWindow.width = m_windowRect.width();
693 m_npWindow.height = m_windowRect.height();
694 }
695
696 PluginView::setCurrentPluginView(this);
697 #if USE(JSC)
698 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
699 #endif
700 setCallingPlugin(true);
701 m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
702 setCallingPlugin(false);
703 PluginView::setCurrentPluginView(0);
704 }
705
setParentVisible(bool visible)706 void PluginView::setParentVisible(bool visible)
707 {
708 if (isParentVisible() == visible)
709 return;
710
711 Widget::setParentVisible(visible);
712
713 if (isSelfVisible() && platformPluginWidget())
714 platformPluginWidget()->setVisible(visible);
715 }
716
handlePostReadFile(Vector<char> & buffer,uint32_t len,const char * buf)717 NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf)
718 {
719 String filename(buf, len);
720
721 if (filename.startsWith("file:///"))
722 filename = filename.substring(8);
723
724 long long size;
725 if (!getFileSize(filename, size))
726 return NPERR_FILE_NOT_FOUND;
727
728 FILE* fileHandle = fopen((filename.utf8()).data(), "r");
729 if (!fileHandle)
730 return NPERR_FILE_NOT_FOUND;
731
732 buffer.resize(size);
733 int bytesRead = fread(buffer.data(), 1, size, fileHandle);
734
735 fclose(fileHandle);
736
737 if (bytesRead <= 0)
738 return NPERR_FILE_NOT_FOUND;
739
740 return NPERR_NO_ERROR;
741 }
742
platformGetValueStatic(NPNVariable variable,void * value,NPError * result)743 bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result)
744 {
745 switch (variable) {
746 case NPNVToolkit:
747 *static_cast<uint32_t*>(value) = 0;
748 *result = NPERR_NO_ERROR;
749 return true;
750
751 case NPNVSupportsXEmbedBool:
752 *static_cast<NPBool*>(value) = true;
753 *result = NPERR_NO_ERROR;
754 return true;
755
756 case NPNVjavascriptEnabledBool:
757 *static_cast<NPBool*>(value) = true;
758 *result = NPERR_NO_ERROR;
759 return true;
760
761 case NPNVSupportsWindowless:
762 *static_cast<NPBool*>(value) = true;
763 *result = NPERR_NO_ERROR;
764 return true;
765
766 #if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5)
767 case NPNVSupportsWindowlessLocal:
768 *static_cast<NPBool*>(value) = true;
769 *result = NPERR_NO_ERROR;
770 return true;
771 #endif
772
773 default:
774 return false;
775 }
776 }
777
platformGetValue(NPNVariable variable,void * value,NPError * result)778 bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result)
779 {
780 switch (variable) {
781 case NPNVxDisplay:
782 *(void **)value = QX11Info::display();
783 *result = NPERR_NO_ERROR;
784 return true;
785
786 case NPNVxtAppContext:
787 *result = NPERR_GENERIC_ERROR;
788 return true;
789
790 case NPNVnetscapeWindow: {
791 void* w = reinterpret_cast<void*>(value);
792 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
793 *((XID *)w) = client ? client->ownerWidget()->window()->winId() : 0;
794 *result = NPERR_NO_ERROR;
795 return true;
796 }
797
798 case NPNVToolkit:
799 if (m_plugin->quirks().contains(PluginQuirkRequiresGtkToolKit)) {
800 *((uint32_t *)value) = 2;
801 *result = NPERR_NO_ERROR;
802 return true;
803 }
804 return false;
805
806 default:
807 return false;
808 }
809 }
810
invalidateRect(const IntRect & rect)811 void PluginView::invalidateRect(const IntRect& rect)
812 {
813 #if USE(ACCELERATED_COMPOSITING) && !USE(TEXTURE_MAPPER)
814 if (m_platformLayer) {
815 m_platformLayer->update(QRectF(rect));
816 return;
817 }
818 #endif
819
820 if (m_isWindowed) {
821 if (platformWidget()) {
822 // update() will schedule a repaint of the widget so ensure
823 // its knowledge of its position on the page is up to date.
824 platformWidget()->setGeometry(m_windowRect);
825 platformWidget()->update(rect);
826 }
827 return;
828 }
829
830 invalidateWindowlessPluginRect(rect);
831 }
832
invalidateRect(NPRect * rect)833 void PluginView::invalidateRect(NPRect* rect)
834 {
835 if (!rect) {
836 invalidate();
837 return;
838 }
839 IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
840 invalidateRect(r);
841 }
842
invalidateRegion(NPRegion region)843 void PluginView::invalidateRegion(NPRegion region)
844 {
845 Q_UNUSED(region);
846 invalidate();
847 }
848
forceRedraw()849 void PluginView::forceRedraw()
850 {
851 invalidate();
852 }
853
getPluginDisplay()854 static Display *getPluginDisplay()
855 {
856 // The plugin toolkit might run using a different X connection. At the moment, we only
857 // support gdk based plugins (like flash) that use a different X connection.
858 // The code below has the same effect as this one:
859 // Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default());
860 QLibrary library(QLatin1String("libgdk-x11-2.0"), 0);
861 if (!library.load())
862 return 0;
863
864 typedef void *(*gdk_display_get_default_ptr)();
865 gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default");
866 if (!gdk_display_get_default)
867 return 0;
868
869 typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *);
870 gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay");
871 if (!gdk_x11_display_get_xdisplay)
872 return 0;
873
874 return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default());
875 }
876
getVisualAndColormap(int depth,Visual ** visual,Colormap * colormap)877 static void getVisualAndColormap(int depth, Visual **visual, Colormap *colormap)
878 {
879 *visual = 0;
880 *colormap = 0;
881
882 #ifndef QT_NO_XRENDER
883 static const bool useXRender = qgetenv("QT_X11_NO_XRENDER").isNull(); // Should also check for XRender >= 0.5
884 #else
885 static const bool useXRender = false;
886 #endif
887
888 if (!useXRender && depth == 32)
889 return;
890
891 int nvi;
892 XVisualInfo templ;
893 templ.screen = QX11Info::appScreen();
894 templ.depth = depth;
895 templ.c_class = TrueColor;
896 XVisualInfo* xvi = XGetVisualInfo(QX11Info::display(), VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi);
897
898 if (!xvi)
899 return;
900
901 #ifndef QT_NO_XRENDER
902 if (depth == 32) {
903 for (int idx = 0; idx < nvi; ++idx) {
904 XRenderPictFormat* format = XRenderFindVisualFormat(QX11Info::display(), xvi[idx].visual);
905 if (format->type == PictTypeDirect && format->direct.alphaMask) {
906 *visual = xvi[idx].visual;
907 break;
908 }
909 }
910 } else
911 #endif // QT_NO_XRENDER
912 *visual = xvi[0].visual;
913
914 XFree(xvi);
915
916 if (*visual)
917 *colormap = XCreateColormap(QX11Info::display(), QX11Info::appRootWindow(), *visual, AllocNone);
918 }
919
platformStart()920 bool PluginView::platformStart()
921 {
922 ASSERT(m_isStarted);
923 ASSERT(m_status == PluginStatusLoadedSuccessfully);
924
925 if (m_plugin->pluginFuncs()->getvalue) {
926 PluginView::setCurrentPluginView(this);
927 #if USE(JSC)
928 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
929 #endif
930 setCallingPlugin(true);
931 m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed);
932 setCallingPlugin(false);
933 PluginView::setCurrentPluginView(0);
934 }
935
936 if (m_isWindowed) {
937 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
938 if (m_needsXEmbed && client) {
939 setPlatformWidget(new PluginContainerQt(this, client->ownerWidget()));
940 // sync our XEmbed container window creation before sending the xid to plugins.
941 QApplication::syncX();
942 } else {
943 notImplemented();
944 m_status = PluginStatusCanNotLoadPlugin;
945 return false;
946 }
947 } else {
948 setPlatformWidget(0);
949 m_pluginDisplay = getPluginDisplay();
950
951 #if USE(ACCELERATED_COMPOSITING) && !USE(TEXTURE_MAPPER)
952 if (shouldUseAcceleratedCompositing()) {
953 m_platformLayer = adoptPtr(new PluginGraphicsLayerQt(this));
954 // Trigger layer computation in RenderLayerCompositor
955 m_element->setNeedsStyleRecalc(SyntheticStyleChange);
956 }
957 #endif
958 }
959
960 // If the width and the height are not zero we show the PluginView.
961 if (!frameRect().isEmpty())
962 show();
963
964 NPSetWindowCallbackStruct* wsi = new NPSetWindowCallbackStruct();
965 wsi->type = 0;
966
967 if (m_isWindowed) {
968 const QX11Info* x11Info = &platformPluginWidget()->x11Info();
969
970 wsi->display = x11Info->display();
971 wsi->visual = (Visual*)x11Info->visual();
972 wsi->depth = x11Info->depth();
973 wsi->colormap = x11Info->colormap();
974
975 m_npWindow.type = NPWindowTypeWindow;
976 m_npWindow.window = (void*)platformPluginWidget()->winId();
977 m_npWindow.width = -1;
978 m_npWindow.height = -1;
979 } else {
980 const QX11Info* x11Info = &QApplication::desktop()->x11Info();
981
982 if (x11Info->depth() == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) {
983 getVisualAndColormap(32, &m_visual, &m_colormap);
984 wsi->depth = 32;
985 }
986
987 if (!m_visual) {
988 getVisualAndColormap(x11Info->depth(), &m_visual, &m_colormap);
989 wsi->depth = x11Info->depth();
990 }
991
992 wsi->display = x11Info->display();
993 wsi->visual = m_visual;
994 wsi->colormap = m_colormap;
995
996 m_npWindow.type = NPWindowTypeDrawable;
997 m_npWindow.window = 0; // Not used?
998 m_npWindow.x = 0;
999 m_npWindow.y = 0;
1000 m_npWindow.width = -1;
1001 m_npWindow.height = -1;
1002 }
1003
1004 m_npWindow.ws_info = wsi;
1005
1006 if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) {
1007 updatePluginWidget();
1008 setNPWindowIfNeeded();
1009 }
1010
1011 return true;
1012 }
1013
platformDestroy()1014 void PluginView::platformDestroy()
1015 {
1016 if (platformPluginWidget())
1017 delete platformPluginWidget();
1018
1019 if (m_drawable)
1020 XFreePixmap(QX11Info::display(), m_drawable);
1021
1022 if (m_colormap)
1023 XFreeColormap(QX11Info::display(), m_colormap);
1024 }
1025
halt()1026 void PluginView::halt()
1027 {
1028 }
1029
restart()1030 void PluginView::restart()
1031 {
1032 }
1033
1034 #if USE(ACCELERATED_COMPOSITING)
platformLayer() const1035 PlatformLayer* PluginView::platformLayer() const
1036 {
1037 return m_platformLayer.get();
1038 }
1039 #endif
1040
1041 } // namespace WebCore
1042