|
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 #include "Bridge.h" |
|
32 #include "Document.h" |
|
33 #include "DocumentLoader.h" |
|
34 #include "Element.h" |
|
35 #include "FloatPoint.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 "GraphicsContext.h" |
|
43 #include "HTMLNames.h" |
|
44 #include "HTMLPlugInElement.h" |
|
45 #include "HostWindow.h" |
|
46 #include "Image.h" |
|
47 #include "JSDOMBinding.h" |
|
48 #include "KeyboardEvent.h" |
|
49 #include "MouseEvent.h" |
|
50 #include "NotImplemented.h" |
|
51 #include "Page.h" |
|
52 #include "PlatformMouseEvent.h" |
|
53 #include "PlatformKeyboardEvent.h" |
|
54 #include "PluginContainerQt.h" |
|
55 #include "PluginDebug.h" |
|
56 #include "PluginPackage.h" |
|
57 #include "PluginMainThreadScheduler.h" |
|
58 #include "QWebPageClient.h" |
|
59 #include "RenderLayer.h" |
|
60 #include "ScriptController.h" |
|
61 #include "Settings.h" |
|
62 #include "npruntime_impl.h" |
|
63 #include "qwebpage_p.h" |
|
64 #include "runtime_root.h" |
|
65 |
|
66 #include <QApplication> |
|
67 #include <QDesktopWidget> |
|
68 #include <QKeyEvent> |
|
69 #include <QPainter> |
|
70 #include <QWidget> |
|
71 #include <QX11Info> |
|
72 #include <X11/X.h> |
|
73 #ifndef QT_NO_XRENDER |
|
74 #define Bool int |
|
75 #define Status int |
|
76 #include <X11/extensions/Xrender.h> |
|
77 #endif |
|
78 #include <runtime/JSLock.h> |
|
79 #include <runtime/JSValue.h> |
|
80 |
|
81 using JSC::ExecState; |
|
82 using JSC::Interpreter; |
|
83 using JSC::JSLock; |
|
84 using JSC::JSObject; |
|
85 using JSC::UString; |
|
86 |
|
87 using std::min; |
|
88 |
|
89 using namespace WTF; |
|
90 |
|
91 namespace WebCore { |
|
92 |
|
93 using namespace HTMLNames; |
|
94 |
|
95 void PluginView::updatePluginWidget() |
|
96 { |
|
97 if (!parent()) |
|
98 return; |
|
99 |
|
100 ASSERT(parent()->isFrameView()); |
|
101 FrameView* frameView = static_cast<FrameView*>(parent()); |
|
102 |
|
103 IntRect oldWindowRect = m_windowRect; |
|
104 IntRect oldClipRect = m_clipRect; |
|
105 |
|
106 m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); |
|
107 m_clipRect = windowClipRect(); |
|
108 m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); |
|
109 |
|
110 if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect) |
|
111 return; |
|
112 |
|
113 if (!m_isWindowed && m_windowRect.size() != oldWindowRect.size()) { |
|
114 if (m_drawable) |
|
115 XFreePixmap(QX11Info::display(), m_drawable); |
|
116 |
|
117 m_drawable = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), m_windowRect.width(), m_windowRect.height(), |
|
118 ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth); |
|
119 QApplication::syncX(); // make sure that the server knows about the Drawable |
|
120 } |
|
121 |
|
122 // do not call setNPWindowIfNeeded immediately, will be called on paint() |
|
123 m_hasPendingGeometryChange = true; |
|
124 |
|
125 // (i) in order to move/resize the plugin window at the same time as the |
|
126 // rest of frame during e.g. scrolling, we set the window geometry |
|
127 // in the paint() function, but as paint() isn't called when the |
|
128 // plugin window is outside the frame which can be caused by a |
|
129 // scroll, we need to move/resize immediately. |
|
130 // (ii) if we are running layout tests from DRT, paint() won't ever get called |
|
131 // so we need to call setNPWindowIfNeeded() if window geometry has changed |
|
132 if (!m_windowRect.intersects(frameView->frameRect()) |
|
133 || (QWebPagePrivate::drtRun && platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect))) |
|
134 setNPWindowIfNeeded(); |
|
135 |
|
136 // Make sure we get repainted afterwards. This is necessary for downward |
|
137 // scrolling to move the plugin widget properly. |
|
138 invalidate(); |
|
139 } |
|
140 |
|
141 void PluginView::setFocus(bool focused) |
|
142 { |
|
143 if (platformPluginWidget()) { |
|
144 if (focused) |
|
145 platformPluginWidget()->setFocus(Qt::OtherFocusReason); |
|
146 } else { |
|
147 Widget::setFocus(focused); |
|
148 } |
|
149 } |
|
150 |
|
151 void PluginView::show() |
|
152 { |
|
153 Q_ASSERT(platformPluginWidget() == platformWidget()); |
|
154 Widget::show(); |
|
155 } |
|
156 |
|
157 void PluginView::hide() |
|
158 { |
|
159 Q_ASSERT(platformPluginWidget() == platformWidget()); |
|
160 Widget::hide(); |
|
161 } |
|
162 |
|
163 void PluginView::paint(GraphicsContext* context, const IntRect& rect) |
|
164 { |
|
165 if (!m_isStarted) { |
|
166 paintMissingPluginIcon(context, rect); |
|
167 return; |
|
168 } |
|
169 |
|
170 if (context->paintingDisabled()) |
|
171 return; |
|
172 |
|
173 setNPWindowIfNeeded(); |
|
174 |
|
175 if (m_isWindowed || !m_drawable) |
|
176 return; |
|
177 |
|
178 const bool syncX = m_pluginDisplay && m_pluginDisplay != QX11Info::display(); |
|
179 |
|
180 QPainter* painter = context->platformContext(); |
|
181 IntRect exposedRect(rect); |
|
182 exposedRect.intersect(frameRect()); |
|
183 exposedRect.move(-frameRect().x(), -frameRect().y()); |
|
184 |
|
185 QPixmap qtDrawable = QPixmap::fromX11Pixmap(m_drawable, QPixmap::ExplicitlyShared); |
|
186 const int drawableDepth = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth; |
|
187 ASSERT(drawableDepth == qtDrawable.depth()); |
|
188 |
|
189 // When printing, Qt uses a QPicture to capture the output in preview mode. The |
|
190 // QPicture holds a reference to the X Pixmap. As a result, the print preview would |
|
191 // update itself when the X Pixmap changes. To prevent this, we create a copy. |
|
192 if (m_element->document()->printing()) |
|
193 qtDrawable = qtDrawable.copy(); |
|
194 |
|
195 if (m_isTransparent && drawableDepth != 32) { |
|
196 // Attempt content propagation for drawable with no alpha by copying over from the backing store |
|
197 QPoint offset; |
|
198 QPaintDevice* backingStoreDevice = QPainter::redirected(painter->device(), &offset); |
|
199 offset = -offset; // negating the offset gives us the offset of the view within the backing store pixmap |
|
200 |
|
201 const bool hasValidBackingStore = backingStoreDevice && backingStoreDevice->devType() == QInternal::Pixmap; |
|
202 QPixmap* backingStorePixmap = static_cast<QPixmap*>(backingStoreDevice); |
|
203 |
|
204 // We cannot grab contents from the backing store when painting on QGraphicsView items |
|
205 // (because backing store contents are already transformed). What we really mean to do |
|
206 // here is to check if we are painting on QWebView, but let's be a little permissive :) |
|
207 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); |
|
208 const bool backingStoreHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent()); |
|
209 |
|
210 if (hasValidBackingStore && backingStorePixmap->depth() == drawableDepth |
|
211 && backingStoreHasUntransformedContents) { |
|
212 GC gc = XDefaultGC(QX11Info::display(), QX11Info::appScreen()); |
|
213 XCopyArea(QX11Info::display(), backingStorePixmap->handle(), m_drawable, gc, |
|
214 offset.x() + m_windowRect.x() + exposedRect.x(), offset.y() + m_windowRect.y() + exposedRect.y(), |
|
215 exposedRect.width(), exposedRect.height(), exposedRect.x(), exposedRect.y()); |
|
216 } else { // no backing store, clean the pixmap because the plugin thinks its transparent |
|
217 QPainter painter(&qtDrawable); |
|
218 painter.fillRect(exposedRect, Qt::white); |
|
219 } |
|
220 |
|
221 if (syncX) |
|
222 QApplication::syncX(); |
|
223 } |
|
224 |
|
225 XEvent xevent; |
|
226 memset(&xevent, 0, sizeof(XEvent)); |
|
227 XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; |
|
228 exposeEvent.type = GraphicsExpose; |
|
229 exposeEvent.display = QX11Info::display(); |
|
230 exposeEvent.drawable = qtDrawable.handle(); |
|
231 exposeEvent.x = exposedRect.x(); |
|
232 exposeEvent.y = exposedRect.y(); |
|
233 exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode |
|
234 exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode |
|
235 |
|
236 dispatchNPEvent(xevent); |
|
237 |
|
238 if (syncX) |
|
239 XSync(m_pluginDisplay, False); // sync changes by plugin |
|
240 |
|
241 painter->drawPixmap(QPoint(frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y()), qtDrawable, |
|
242 exposedRect); |
|
243 } |
|
244 |
|
245 // TODO: Unify across ports. |
|
246 bool PluginView::dispatchNPEvent(NPEvent& event) |
|
247 { |
|
248 if (!m_plugin->pluginFuncs()->event) |
|
249 return false; |
|
250 |
|
251 PluginView::setCurrentPluginView(this); |
|
252 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); |
|
253 setCallingPlugin(true); |
|
254 bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event); |
|
255 setCallingPlugin(false); |
|
256 PluginView::setCurrentPluginView(0); |
|
257 |
|
258 return accepted; |
|
259 } |
|
260 |
|
261 void setSharedXEventFields(XEvent* xEvent, QWidget* ownerWidget) |
|
262 { |
|
263 xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server |
|
264 xEvent->xany.send_event = false; |
|
265 xEvent->xany.display = QX11Info::display(); |
|
266 // NOTE: event->xany.window doesn't always respond to the .window property of other XEvent's |
|
267 // but does in the case of KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify |
|
268 // events; thus, this is right: |
|
269 xEvent->xany.window = ownerWidget ? ownerWidget->window()->handle() : 0; |
|
270 } |
|
271 |
|
272 void PluginView::initXEvent(XEvent* xEvent) |
|
273 { |
|
274 memset(xEvent, 0, sizeof(XEvent)); |
|
275 |
|
276 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); |
|
277 QWidget* ownerWidget = client ? client->ownerWidget() : 0; |
|
278 setSharedXEventFields(xEvent, ownerWidget); |
|
279 } |
|
280 |
|
281 void setXKeyEventSpecificFields(XEvent* xEvent, KeyboardEvent* event) |
|
282 { |
|
283 QKeyEvent* qKeyEvent = event->keyEvent()->qtEvent(); |
|
284 |
|
285 xEvent->type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // ints as Qt unsets KeyPress and KeyRelease |
|
286 xEvent->xkey.root = QX11Info::appRootWindow(); |
|
287 xEvent->xkey.subwindow = 0; // we have no child window |
|
288 xEvent->xkey.time = event->timeStamp(); |
|
289 xEvent->xkey.state = qKeyEvent->nativeModifiers(); |
|
290 xEvent->xkey.keycode = qKeyEvent->nativeScanCode(); |
|
291 xEvent->xkey.same_screen = true; |
|
292 |
|
293 // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window |
|
294 // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not |
|
295 // set to their normal Xserver values. e.g. Key events don't have a position. |
|
296 // source: https://developer.mozilla.org/en/NPEvent |
|
297 xEvent->xkey.x = 0; |
|
298 xEvent->xkey.y = 0; |
|
299 xEvent->xkey.x_root = 0; |
|
300 xEvent->xkey.y_root = 0; |
|
301 } |
|
302 |
|
303 void PluginView::handleKeyboardEvent(KeyboardEvent* event) |
|
304 { |
|
305 if (m_isWindowed) |
|
306 return; |
|
307 |
|
308 if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent) |
|
309 return; |
|
310 |
|
311 XEvent npEvent; |
|
312 initXEvent(&npEvent); |
|
313 setXKeyEventSpecificFields(&npEvent, event); |
|
314 |
|
315 if (!dispatchNPEvent(npEvent)) |
|
316 event->setDefaultHandled(); |
|
317 } |
|
318 |
|
319 static unsigned int inputEventState(MouseEvent* event) |
|
320 { |
|
321 unsigned int state = 0; |
|
322 if (event->ctrlKey()) |
|
323 state |= ControlMask; |
|
324 if (event->shiftKey()) |
|
325 state |= ShiftMask; |
|
326 if (event->altKey()) |
|
327 state |= Mod1Mask; |
|
328 if (event->metaKey()) |
|
329 state |= Mod4Mask; |
|
330 return state; |
|
331 } |
|
332 |
|
333 static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) |
|
334 { |
|
335 XButtonEvent& xbutton = xEvent->xbutton; |
|
336 xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease; |
|
337 xbutton.root = QX11Info::appRootWindow(); |
|
338 xbutton.subwindow = 0; |
|
339 xbutton.time = event->timeStamp(); |
|
340 xbutton.x = postZoomPos.x(); |
|
341 xbutton.y = postZoomPos.y(); |
|
342 xbutton.x_root = event->screenX(); |
|
343 xbutton.y_root = event->screenY(); |
|
344 xbutton.state = inputEventState(event); |
|
345 switch (event->button()) { |
|
346 case MiddleButton: |
|
347 xbutton.button = Button2; |
|
348 break; |
|
349 case RightButton: |
|
350 xbutton.button = Button3; |
|
351 break; |
|
352 case LeftButton: |
|
353 default: |
|
354 xbutton.button = Button1; |
|
355 break; |
|
356 } |
|
357 xbutton.same_screen = true; |
|
358 } |
|
359 |
|
360 static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) |
|
361 { |
|
362 XMotionEvent& xmotion = xEvent->xmotion; |
|
363 xmotion.type = MotionNotify; |
|
364 xmotion.root = QX11Info::appRootWindow(); |
|
365 xmotion.subwindow = 0; |
|
366 xmotion.time = event->timeStamp(); |
|
367 xmotion.x = postZoomPos.x(); |
|
368 xmotion.y = postZoomPos.y(); |
|
369 xmotion.x_root = event->screenX(); |
|
370 xmotion.y_root = event->screenY(); |
|
371 xmotion.state = inputEventState(event); |
|
372 xmotion.is_hint = NotifyNormal; |
|
373 xmotion.same_screen = true; |
|
374 } |
|
375 |
|
376 static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) |
|
377 { |
|
378 XCrossingEvent& xcrossing = xEvent->xcrossing; |
|
379 xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify; |
|
380 xcrossing.root = QX11Info::appRootWindow(); |
|
381 xcrossing.subwindow = 0; |
|
382 xcrossing.time = event->timeStamp(); |
|
383 xcrossing.x = postZoomPos.y(); |
|
384 xcrossing.y = postZoomPos.x(); |
|
385 xcrossing.x_root = event->screenX(); |
|
386 xcrossing.y_root = event->screenY(); |
|
387 xcrossing.state = inputEventState(event); |
|
388 xcrossing.mode = NotifyNormal; |
|
389 xcrossing.detail = NotifyDetailNone; |
|
390 xcrossing.same_screen = true; |
|
391 xcrossing.focus = false; |
|
392 } |
|
393 |
|
394 void PluginView::handleMouseEvent(MouseEvent* event) |
|
395 { |
|
396 if (m_isWindowed) |
|
397 return; |
|
398 |
|
399 if (event->type() == eventNames().mousedownEvent) { |
|
400 // Give focus to the plugin on click |
|
401 if (Page* page = m_parentFrame->page()) |
|
402 page->focusController()->setActive(true); |
|
403 |
|
404 focusPluginElement(); |
|
405 } |
|
406 |
|
407 XEvent npEvent; |
|
408 initXEvent(&npEvent); |
|
409 |
|
410 IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); |
|
411 |
|
412 if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent) |
|
413 setXButtonEventSpecificFields(&npEvent, event, postZoomPos); |
|
414 else if (event->type() == eventNames().mousemoveEvent) |
|
415 setXMotionEventSpecificFields(&npEvent, event, postZoomPos); |
|
416 else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent) |
|
417 setXCrossingEventSpecificFields(&npEvent, event, postZoomPos); |
|
418 else |
|
419 return; |
|
420 |
|
421 if (!dispatchNPEvent(npEvent)) |
|
422 event->setDefaultHandled(); |
|
423 } |
|
424 |
|
425 void PluginView::handleFocusInEvent() |
|
426 { |
|
427 XEvent npEvent; |
|
428 initXEvent(&npEvent); |
|
429 |
|
430 XFocusChangeEvent& event = npEvent.xfocus; |
|
431 event.type = 9; /* int as Qt unsets FocusIn */ |
|
432 event.mode = NotifyNormal; |
|
433 event.detail = NotifyDetailNone; |
|
434 |
|
435 dispatchNPEvent(npEvent); |
|
436 } |
|
437 |
|
438 void PluginView::handleFocusOutEvent() |
|
439 { |
|
440 XEvent npEvent; |
|
441 initXEvent(&npEvent); |
|
442 |
|
443 XFocusChangeEvent& event = npEvent.xfocus; |
|
444 event.type = 10; /* int as Qt unsets FocusOut */ |
|
445 event.mode = NotifyNormal; |
|
446 event.detail = NotifyDetailNone; |
|
447 |
|
448 dispatchNPEvent(npEvent); |
|
449 } |
|
450 |
|
451 void PluginView::setParent(ScrollView* parent) |
|
452 { |
|
453 Widget::setParent(parent); |
|
454 |
|
455 if (parent) |
|
456 init(); |
|
457 } |
|
458 |
|
459 void PluginView::setNPWindowRect(const IntRect&) |
|
460 { |
|
461 if (!m_isWindowed) |
|
462 setNPWindowIfNeeded(); |
|
463 } |
|
464 |
|
465 void PluginView::setNPWindowIfNeeded() |
|
466 { |
|
467 if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow) |
|
468 return; |
|
469 |
|
470 // If the plugin didn't load sucessfully, no point in calling setwindow |
|
471 if (m_status != PluginStatusLoadedSuccessfully) |
|
472 return; |
|
473 |
|
474 // On Unix, only call plugin if it's full-page or windowed |
|
475 if (m_mode != NP_FULL && m_mode != NP_EMBED) |
|
476 return; |
|
477 |
|
478 // Check if the platformPluginWidget still exists |
|
479 if (m_isWindowed && !platformPluginWidget()) |
|
480 return; |
|
481 |
|
482 if (!m_hasPendingGeometryChange) |
|
483 return; |
|
484 m_hasPendingGeometryChange = false; |
|
485 |
|
486 if (m_isWindowed) { |
|
487 platformPluginWidget()->setGeometry(m_windowRect); |
|
488 // if setMask is set with an empty QRegion, no clipping will |
|
489 // be performed, so in that case we hide the plugin view |
|
490 platformPluginWidget()->setVisible(!m_clipRect.isEmpty()); |
|
491 platformPluginWidget()->setMask(QRegion(m_clipRect)); |
|
492 |
|
493 m_npWindow.x = m_windowRect.x(); |
|
494 m_npWindow.y = m_windowRect.y(); |
|
495 |
|
496 m_npWindow.clipRect.left = max(0, m_clipRect.x()); |
|
497 m_npWindow.clipRect.top = max(0, m_clipRect.y()); |
|
498 m_npWindow.clipRect.right = m_clipRect.x() + m_clipRect.width(); |
|
499 m_npWindow.clipRect.bottom = m_clipRect.y() + m_clipRect.height(); |
|
500 } else { |
|
501 m_npWindow.x = 0; |
|
502 m_npWindow.y = 0; |
|
503 |
|
504 m_npWindow.clipRect.left = 0; |
|
505 m_npWindow.clipRect.top = 0; |
|
506 m_npWindow.clipRect.right = 0; |
|
507 m_npWindow.clipRect.bottom = 0; |
|
508 } |
|
509 |
|
510 if (m_plugin->quirks().contains(PluginQuirkDontCallSetWindowMoreThanOnce)) { |
|
511 // FLASH WORKAROUND: Only set initially. Multiple calls to |
|
512 // setNPWindow() cause the plugin to crash in windowed mode. |
|
513 if (!m_isWindowed || m_npWindow.width == -1 || m_npWindow.height == -1) { |
|
514 m_npWindow.width = m_windowRect.width(); |
|
515 m_npWindow.height = m_windowRect.height(); |
|
516 } |
|
517 } else { |
|
518 m_npWindow.width = m_windowRect.width(); |
|
519 m_npWindow.height = m_windowRect.height(); |
|
520 } |
|
521 |
|
522 PluginView::setCurrentPluginView(this); |
|
523 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); |
|
524 setCallingPlugin(true); |
|
525 m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); |
|
526 setCallingPlugin(false); |
|
527 PluginView::setCurrentPluginView(0); |
|
528 } |
|
529 |
|
530 void PluginView::setParentVisible(bool visible) |
|
531 { |
|
532 if (isParentVisible() == visible) |
|
533 return; |
|
534 |
|
535 Widget::setParentVisible(visible); |
|
536 |
|
537 if (isSelfVisible() && platformPluginWidget()) |
|
538 platformPluginWidget()->setVisible(visible); |
|
539 } |
|
540 |
|
541 NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) |
|
542 { |
|
543 String filename(buf, len); |
|
544 |
|
545 if (filename.startsWith("file:///")) |
|
546 filename = filename.substring(8); |
|
547 |
|
548 long long size; |
|
549 if (!getFileSize(filename, size)) |
|
550 return NPERR_FILE_NOT_FOUND; |
|
551 |
|
552 FILE* fileHandle = fopen((filename.utf8()).data(), "r"); |
|
553 if (!fileHandle) |
|
554 return NPERR_FILE_NOT_FOUND; |
|
555 |
|
556 buffer.resize(size); |
|
557 int bytesRead = fread(buffer.data(), 1, size, fileHandle); |
|
558 |
|
559 fclose(fileHandle); |
|
560 |
|
561 if (bytesRead <= 0) |
|
562 return NPERR_FILE_NOT_FOUND; |
|
563 |
|
564 return NPERR_NO_ERROR; |
|
565 } |
|
566 |
|
567 bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) |
|
568 { |
|
569 switch (variable) { |
|
570 case NPNVToolkit: |
|
571 *static_cast<uint32_t*>(value) = 0; |
|
572 *result = NPERR_NO_ERROR; |
|
573 return true; |
|
574 |
|
575 case NPNVSupportsXEmbedBool: |
|
576 *static_cast<NPBool*>(value) = true; |
|
577 *result = NPERR_NO_ERROR; |
|
578 return true; |
|
579 |
|
580 case NPNVjavascriptEnabledBool: |
|
581 *static_cast<NPBool*>(value) = true; |
|
582 *result = NPERR_NO_ERROR; |
|
583 return true; |
|
584 |
|
585 case NPNVSupportsWindowless: |
|
586 *static_cast<NPBool*>(value) = true; |
|
587 *result = NPERR_NO_ERROR; |
|
588 return true; |
|
589 |
|
590 default: |
|
591 return false; |
|
592 } |
|
593 } |
|
594 |
|
595 bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) |
|
596 { |
|
597 switch (variable) { |
|
598 case NPNVxDisplay: |
|
599 *(void **)value = QX11Info::display(); |
|
600 *result = NPERR_NO_ERROR; |
|
601 return true; |
|
602 |
|
603 case NPNVxtAppContext: |
|
604 *result = NPERR_GENERIC_ERROR; |
|
605 return true; |
|
606 |
|
607 case NPNVnetscapeWindow: { |
|
608 void* w = reinterpret_cast<void*>(value); |
|
609 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); |
|
610 *((XID *)w) = client ? client->ownerWidget()->window()->winId() : 0; |
|
611 *result = NPERR_NO_ERROR; |
|
612 return true; |
|
613 } |
|
614 |
|
615 case NPNVToolkit: |
|
616 if (m_plugin->quirks().contains(PluginQuirkRequiresGtkToolKit)) { |
|
617 *((uint32_t *)value) = 2; |
|
618 *result = NPERR_NO_ERROR; |
|
619 return true; |
|
620 } |
|
621 return false; |
|
622 |
|
623 default: |
|
624 return false; |
|
625 } |
|
626 } |
|
627 |
|
628 void PluginView::invalidateRect(const IntRect& rect) |
|
629 { |
|
630 if (m_isWindowed) { |
|
631 if (platformWidget()) |
|
632 platformWidget()->update(rect); |
|
633 return; |
|
634 } |
|
635 |
|
636 invalidateWindowlessPluginRect(rect); |
|
637 } |
|
638 |
|
639 void PluginView::invalidateRect(NPRect* rect) |
|
640 { |
|
641 if (!rect) { |
|
642 invalidate(); |
|
643 return; |
|
644 } |
|
645 IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); |
|
646 invalidateWindowlessPluginRect(r); |
|
647 } |
|
648 |
|
649 void PluginView::invalidateRegion(NPRegion region) |
|
650 { |
|
651 invalidate(); |
|
652 } |
|
653 |
|
654 void PluginView::forceRedraw() |
|
655 { |
|
656 invalidate(); |
|
657 } |
|
658 |
|
659 static Display *getPluginDisplay() |
|
660 { |
|
661 // The plugin toolkit might run using a different X connection. At the moment, we only |
|
662 // support gdk based plugins (like flash) that use a different X connection. |
|
663 // The code below has the same effect as this one: |
|
664 // Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default()); |
|
665 QLibrary library("libgdk-x11-2.0"); |
|
666 if (!library.load()) |
|
667 return 0; |
|
668 |
|
669 typedef void *(*gdk_display_get_default_ptr)(); |
|
670 gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default"); |
|
671 if (!gdk_display_get_default) |
|
672 return 0; |
|
673 |
|
674 typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *); |
|
675 gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay"); |
|
676 if (!gdk_x11_display_get_xdisplay) |
|
677 return 0; |
|
678 |
|
679 return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default()); |
|
680 } |
|
681 |
|
682 static void getVisualAndColormap(int depth, Visual **visual, Colormap *colormap) |
|
683 { |
|
684 *visual = 0; |
|
685 *colormap = 0; |
|
686 |
|
687 #ifndef QT_NO_XRENDER |
|
688 static const bool useXRender = qgetenv("QT_X11_NO_XRENDER").isNull(); // Should also check for XRender >= 0.5 |
|
689 #else |
|
690 static const bool useXRender = false; |
|
691 #endif |
|
692 |
|
693 if (!useXRender && depth == 32) |
|
694 return; |
|
695 |
|
696 int nvi; |
|
697 XVisualInfo templ; |
|
698 templ.screen = QX11Info::appScreen(); |
|
699 templ.depth = depth; |
|
700 templ.c_class = TrueColor; |
|
701 XVisualInfo* xvi = XGetVisualInfo(QX11Info::display(), VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi); |
|
702 |
|
703 if (!xvi) |
|
704 return; |
|
705 |
|
706 #ifndef QT_NO_XRENDER |
|
707 if (depth == 32) { |
|
708 for (int idx = 0; idx < nvi; ++idx) { |
|
709 XRenderPictFormat* format = XRenderFindVisualFormat(QX11Info::display(), xvi[idx].visual); |
|
710 if (format->type == PictTypeDirect && format->direct.alphaMask) { |
|
711 *visual = xvi[idx].visual; |
|
712 break; |
|
713 } |
|
714 } |
|
715 } else |
|
716 #endif // QT_NO_XRENDER |
|
717 *visual = xvi[0].visual; |
|
718 |
|
719 XFree(xvi); |
|
720 |
|
721 if (*visual) |
|
722 *colormap = XCreateColormap(QX11Info::display(), QX11Info::appRootWindow(), *visual, AllocNone); |
|
723 } |
|
724 |
|
725 bool PluginView::platformStart() |
|
726 { |
|
727 ASSERT(m_isStarted); |
|
728 ASSERT(m_status == PluginStatusLoadedSuccessfully); |
|
729 |
|
730 if (m_plugin->pluginFuncs()->getvalue) { |
|
731 PluginView::setCurrentPluginView(this); |
|
732 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); |
|
733 setCallingPlugin(true); |
|
734 m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed); |
|
735 setCallingPlugin(false); |
|
736 PluginView::setCurrentPluginView(0); |
|
737 } |
|
738 |
|
739 if (m_isWindowed) { |
|
740 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); |
|
741 if (m_needsXEmbed && client) { |
|
742 setPlatformWidget(new PluginContainerQt(this, client->ownerWidget())); |
|
743 // sync our XEmbed container window creation before sending the xid to plugins. |
|
744 QApplication::syncX(); |
|
745 } else { |
|
746 notImplemented(); |
|
747 m_status = PluginStatusCanNotLoadPlugin; |
|
748 return false; |
|
749 } |
|
750 } else { |
|
751 setPlatformWidget(0); |
|
752 m_pluginDisplay = getPluginDisplay(); |
|
753 } |
|
754 |
|
755 show(); |
|
756 |
|
757 NPSetWindowCallbackStruct* wsi = new NPSetWindowCallbackStruct(); |
|
758 wsi->type = 0; |
|
759 |
|
760 if (m_isWindowed) { |
|
761 const QX11Info* x11Info = &platformPluginWidget()->x11Info(); |
|
762 |
|
763 wsi->display = x11Info->display(); |
|
764 wsi->visual = (Visual*)x11Info->visual(); |
|
765 wsi->depth = x11Info->depth(); |
|
766 wsi->colormap = x11Info->colormap(); |
|
767 |
|
768 m_npWindow.type = NPWindowTypeWindow; |
|
769 m_npWindow.window = (void*)platformPluginWidget()->winId(); |
|
770 m_npWindow.width = -1; |
|
771 m_npWindow.height = -1; |
|
772 } else { |
|
773 const QX11Info* x11Info = &QApplication::desktop()->x11Info(); |
|
774 |
|
775 if (x11Info->depth() == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) { |
|
776 getVisualAndColormap(32, &m_visual, &m_colormap); |
|
777 wsi->depth = 32; |
|
778 } |
|
779 |
|
780 if (!m_visual) { |
|
781 getVisualAndColormap(x11Info->depth(), &m_visual, &m_colormap); |
|
782 wsi->depth = x11Info->depth(); |
|
783 } |
|
784 |
|
785 wsi->display = x11Info->display(); |
|
786 wsi->visual = m_visual; |
|
787 wsi->colormap = m_colormap; |
|
788 |
|
789 m_npWindow.type = NPWindowTypeDrawable; |
|
790 m_npWindow.window = 0; // Not used? |
|
791 m_npWindow.x = 0; |
|
792 m_npWindow.y = 0; |
|
793 m_npWindow.width = -1; |
|
794 m_npWindow.height = -1; |
|
795 } |
|
796 |
|
797 m_npWindow.ws_info = wsi; |
|
798 |
|
799 if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) { |
|
800 updatePluginWidget(); |
|
801 setNPWindowIfNeeded(); |
|
802 } |
|
803 |
|
804 return true; |
|
805 } |
|
806 |
|
807 void PluginView::platformDestroy() |
|
808 { |
|
809 if (platformPluginWidget()) |
|
810 delete platformPluginWidget(); |
|
811 |
|
812 if (m_drawable) |
|
813 XFreePixmap(QX11Info::display(), m_drawable); |
|
814 |
|
815 if (m_colormap) |
|
816 XFreeColormap(QX11Info::display(), m_colormap); |
|
817 } |
|
818 |
|
819 void PluginView::halt() |
|
820 { |
|
821 } |
|
822 |
|
823 void PluginView::restart() |
|
824 { |
|
825 } |
|
826 |
|
827 } // namespace WebCore |