1 /*
2 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18 */
19 #include "config.h"
20 #include "PluginView.h"
21
22 #include "Bridge.h"
23 #include "Document.h"
24 #include "DocumentLoader.h"
25 #include "Element.h"
26 #include "FocusController.h"
27 #include "Frame.h"
28 #include "FrameLoadRequest.h"
29 #include "FrameLoader.h"
30 #include "FrameTree.h"
31 #include "FrameView.h"
32 #include "GraphicsContext.h"
33 #include "HTMLNames.h"
34 #include "HTMLPlugInElement.h"
35 #include "HostWindow.h"
36 #include "Image.h"
37 #include "JSDOMBinding.h"
38 #include "KeyboardEvent.h"
39 #include "MouseEvent.h"
40 #include "NotImplemented.h"
41 #include "Page.h"
42 #include "PlatformKeyboardEvent.h"
43 #include "PlatformMouseEvent.h"
44 #include "PluginContainerSymbian.h"
45 #include "PluginDebug.h"
46 #include "PluginMainThreadScheduler.h"
47 #include "PluginPackage.h"
48 #include "QWebPageClient.h"
49 #include "RenderLayer.h"
50 #include "ScriptController.h"
51 #include "Settings.h"
52 #include "npfunctions.h"
53 #include "npinterface.h"
54 #include "npruntime_impl.h"
55 #include "runtime_root.h"
56 #include <QKeyEvent>
57 #include <QPixmap>
58 #include <QRegion>
59 #include <QVector>
60 #include <QWidget>
61 #include <runtime/JSLock.h>
62 #include <runtime/JSValue.h>
63
64 using JSC::ExecState;
65 using JSC::Interpreter;
66 using JSC::JSLock;
67 using JSC::JSObject;
68 using JSC::UString;
69
70 using namespace std;
71
72 using namespace WTF;
73
74 namespace WebCore {
75
76 using namespace HTMLNames;
77
updatePluginWidget()78 void PluginView::updatePluginWidget()
79 {
80 if (!parent())
81 return;
82 ASSERT(parent()->isFrameView());
83 FrameView* frameView = static_cast<FrameView*>(parent());
84 IntRect oldWindowRect = m_windowRect;
85 IntRect oldClipRect = m_clipRect;
86
87 m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
88 m_clipRect = windowClipRect();
89 m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
90 if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect)
91 return;
92
93 // in order to move/resize the plugin window at the same time as the rest of frame
94 // during e.g. scrolling, we set the mask and geometry in the paint() function, but
95 // as paint() isn't called when the plugin window is outside the frame which can
96 // be caused by a scroll, we need to move/resize immediately.
97 if (!m_windowRect.intersects(frameView->frameRect()))
98 setNPWindowIfNeeded();
99 }
100
setFocus()101 void PluginView::setFocus()
102 {
103 if (platformPluginWidget())
104 platformPluginWidget()->setFocus(Qt::OtherFocusReason);
105 else
106 Widget::setFocus();
107 }
108
show()109 void PluginView::show()
110 {
111 setSelfVisible(true);
112
113 if (isParentVisible() && platformPluginWidget())
114 platformPluginWidget()->setVisible(true);
115 }
116
hide()117 void PluginView::hide()
118 {
119 setSelfVisible(false);
120
121 if (isParentVisible() && platformPluginWidget())
122 platformPluginWidget()->setVisible(false);
123 }
124
paint(GraphicsContext * context,const IntRect & rect)125 void PluginView::paint(GraphicsContext* context, const IntRect& rect)
126 {
127 if (!m_isStarted) {
128 paintMissingPluginIcon(context, rect);
129 return;
130 }
131
132 if (context->paintingDisabled())
133 return;
134 m_npWindow.ws_info = (void*)(context->platformContext());
135 setNPWindowIfNeeded();
136
137 if (m_isWindowed && platformPluginWidget())
138 static_cast<PluginContainerSymbian*>(platformPluginWidget())->adjustGeometry();
139
140 if (m_isWindowed)
141 return;
142
143 context->save();
144 IntRect clipRect(rect);
145 clipRect.intersect(frameRect());
146 context->clip(clipRect);
147 context->translate(frameRect().location().x(), frameRect().location().y());
148
149 QPaintEvent ev(rect);
150 QEvent& npEvent = ev;
151 dispatchNPEvent(npEvent);
152
153 context->restore();
154 }
155
156 // TODO: Unify across ports.
dispatchNPEvent(NPEvent & event)157 bool PluginView::dispatchNPEvent(NPEvent& event)
158 {
159 if (!m_plugin->pluginFuncs()->event)
160 return false;
161
162 PluginView::setCurrentPluginView(this);
163 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
164
165 setCallingPlugin(true);
166 bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
167 setCallingPlugin(false);
168 PluginView::setCurrentPluginView(0);
169
170 return accepted;
171 }
172
handleKeyboardEvent(KeyboardEvent * event)173 void PluginView::handleKeyboardEvent(KeyboardEvent* event)
174 {
175 if (m_isWindowed)
176 return;
177
178 QEvent& npEvent = *(event->keyEvent()->qtEvent());
179 if (!dispatchNPEvent(npEvent))
180 event->setDefaultHandled();
181 }
182
handleMouseEvent(MouseEvent * event)183 void PluginView::handleMouseEvent(MouseEvent* event)
184 {
185 if (m_isWindowed)
186 return;
187
188 if (event->type() == eventNames().mousedownEvent) {
189 // Give focus to the plugin on click
190 if (Page* page = m_parentFrame->page())
191 page->focusController()->setActive(true);
192
193 focusPluginElement();
194 }
195
196 QEvent::Type type;
197 if (event->type() == eventNames().mousedownEvent)
198 type = QEvent::MouseButtonPress;
199 else if (event->type() == eventNames().mousemoveEvent)
200 type = QEvent::MouseMove;
201 else if (event->type() == eventNames().mouseupEvent)
202 type = QEvent::MouseButtonRelease;
203 else
204 return;
205
206 QPoint position(event->offsetX(), event->offsetY());
207 Qt::MouseButton button;
208 switch (event->which()) {
209 case 1:
210 button = Qt::LeftButton;
211 break;
212 case 2:
213 button = Qt::MidButton;
214 break;
215 case 3:
216 button = Qt::RightButton;
217 break;
218 default:
219 button = Qt::NoButton;
220 }
221 Qt::KeyboardModifiers modifiers = 0;
222 if (event->ctrlKey())
223 modifiers |= Qt::ControlModifier;
224 if (event->altKey())
225 modifiers |= Qt::AltModifier;
226 if (event->shiftKey())
227 modifiers |= Qt::ShiftModifier;
228 if (event->metaKey())
229 modifiers |= Qt::MetaModifier;
230 QMouseEvent mouseEvent(type, position, button, button, modifiers);
231 QEvent& npEvent = mouseEvent;
232 if (!dispatchNPEvent(npEvent))
233 event->setDefaultHandled();
234 }
235
setParent(ScrollView * parent)236 void PluginView::setParent(ScrollView* parent)
237 {
238 Widget::setParent(parent);
239
240 if (parent)
241 init();
242 }
243
setNPWindowRect(const IntRect &)244 void PluginView::setNPWindowRect(const IntRect&)
245 {
246 if (!m_isWindowed)
247 setNPWindowIfNeeded();
248 }
249
setNPWindowIfNeeded()250 void PluginView::setNPWindowIfNeeded()
251 {
252 if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
253 return;
254 if (m_isWindowed) {
255 ASSERT(platformPluginWidget());
256 platformPluginWidget()->setGeometry(m_windowRect);
257 // if setMask is set with an empty QRegion, no clipping will
258 // be performed, so in that case we hide the plugin view
259 platformPluginWidget()->setVisible(!m_clipRect.isEmpty());
260 platformPluginWidget()->setMask(QRegion(m_clipRect));
261
262 m_npWindow.x = m_windowRect.x();
263 m_npWindow.y = m_windowRect.y();
264
265 m_npWindow.clipRect.left = m_clipRect.x();
266 m_npWindow.clipRect.top = m_clipRect.y();
267 m_npWindow.clipRect.right = m_clipRect.width();
268 m_npWindow.clipRect.bottom = m_clipRect.height();
269
270 } else {
271 // always call this method before painting.
272 m_npWindow.x = 0;
273 m_npWindow.y = 0;
274
275 m_npWindow.clipRect.left = 0;
276 m_npWindow.clipRect.top = 0;
277 m_npWindow.clipRect.right = m_windowRect.width();
278 m_npWindow.clipRect.bottom = m_windowRect.height();
279 m_npWindow.window = 0;
280 }
281
282 m_npWindow.width = m_windowRect.width();
283 m_npWindow.height = m_windowRect.height();
284
285 PluginView::setCurrentPluginView(this);
286 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
287 setCallingPlugin(true);
288 m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
289 setCallingPlugin(false);
290 PluginView::setCurrentPluginView(0);
291 }
292
setParentVisible(bool visible)293 void PluginView::setParentVisible(bool visible)
294 {
295 if (isParentVisible() == visible)
296 return;
297
298 Widget::setParentVisible(visible);
299
300 if (isSelfVisible() && platformPluginWidget())
301 platformPluginWidget()->setVisible(visible);
302 }
303
handlePostReadFile(Vector<char> & buffer,uint32 len,const char * buf)304 NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf)
305 {
306 notImplemented();
307 return NPERR_NO_ERROR;
308 }
309
getValueStatic(NPNVariable variable,void * value)310 NPError PluginView::getValueStatic(NPNVariable variable, void* value)
311 {
312 LOG(Plugins, "PluginView::getValueStatic(%s)", prettyNameForNPNVariable(variable).data());
313
314 switch (variable) {
315 case NPNVjavascriptEnabledBool:
316 *static_cast<NPBool*>(value) = true;
317 return NPERR_NO_ERROR;
318
319 case NPNVSupportsWindowless:
320 *static_cast<NPBool*>(value) = true;
321 return NPERR_NO_ERROR;
322
323 default:
324 return NPERR_GENERIC_ERROR;
325 }
326 }
327
getValue(NPNVariable variable,void * value)328 NPError PluginView::getValue(NPNVariable variable, void* value)
329 {
330 LOG(Plugins, "PluginView::getValue(%s)", prettyNameForNPNVariable(variable).data());
331
332 switch (variable) {
333 case NPNVWindowNPObject: {
334 if (m_isJavaScriptPaused)
335 return NPERR_GENERIC_ERROR;
336
337 NPObject* windowScriptObject = m_parentFrame->script()->windowScriptNPObject();
338
339 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
340 if (windowScriptObject)
341 _NPN_RetainObject(windowScriptObject);
342
343 void** v = (void**)value;
344 *v = windowScriptObject;
345
346 return NPERR_NO_ERROR;
347 }
348
349 case NPNVPluginElementNPObject: {
350 if (m_isJavaScriptPaused)
351 return NPERR_GENERIC_ERROR;
352
353 NPObject* pluginScriptObject = 0;
354
355 if (m_element->hasTagName(appletTag) || m_element->hasTagName(embedTag) || m_element->hasTagName(objectTag))
356 pluginScriptObject = static_cast<HTMLPlugInElement*>(m_element)->getNPObject();
357
358 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
359 if (pluginScriptObject)
360 _NPN_RetainObject(pluginScriptObject);
361
362 void** v = (void**)value;
363 *v = pluginScriptObject;
364
365 return NPERR_NO_ERROR;
366 }
367 default:
368 return getValueStatic(variable, value);
369 }
370 }
371
invalidateRect(const IntRect & rect)372 void PluginView::invalidateRect(const IntRect& rect)
373 {
374 if (m_isWindowed) {
375 platformWidget()->update(rect);
376 return;
377 }
378
379 invalidateWindowlessPluginRect(rect);
380 }
381
invalidateRect(NPRect * rect)382 void PluginView::invalidateRect(NPRect* rect)
383 {
384 if (m_isWindowed)
385 return;
386 if (!rect) {
387 invalidate();
388 return;
389 }
390 IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
391 m_invalidRects.append(r);
392 if (!m_invalidateTimer.isActive())
393 m_invalidateTimer.startOneShot(0.001);
394 }
395
invalidateRegion(NPRegion region)396 void PluginView::invalidateRegion(NPRegion region)
397 {
398 if (m_isWindowed)
399 return;
400
401 if (!region)
402 return;
403
404 QVector<QRect> rects = region->rects();
405 for (int i = 0; i < rects.size(); ++i) {
406 const QRect& qRect = rects.at(i);
407 m_invalidRects.append(qRect);
408 if (!m_invalidateTimer.isActive())
409 m_invalidateTimer.startOneShot(0.001);
410 }
411 }
412
forceRedraw()413 void PluginView::forceRedraw()
414 {
415 if (m_isWindowed)
416 return;
417 invalidate();
418 }
419
platformStart()420 bool PluginView::platformStart()
421 {
422 ASSERT(m_isStarted);
423 ASSERT(m_status == PluginStatusLoadedSuccessfully);
424
425 show();
426
427 if (m_isWindowed) {
428 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
429 // FIXME this will not work for QGraphicsView.
430 // But we cannot use winId because it will create a window and on S60,
431 // QWidgets should not create a window.
432 Q_ASSERT(qobject_cast<QWidget*>(client->pluginParent()));
433 setPlatformWidget(new PluginContainerSymbian(this,
434 qobject_cast<QWidget*>(client->pluginParent())));
435 m_npWindow.type = NPWindowTypeWindow;
436 m_npWindow.window = (void*)platformPluginWidget();
437
438 } else {
439 setPlatformWidget(0);
440 m_npWindow.type = NPWindowTypeDrawable;
441 m_npWindow.window = 0; // Not used?
442 }
443 setNPWindowIfNeeded();
444
445 return true;
446 }
447
platformDestroy()448 void PluginView::platformDestroy()
449 {
450 delete platformPluginWidget();
451 }
452
halt()453 void PluginView::halt()
454 {
455 }
456
restart()457 void PluginView::restart()
458 {
459 }
460
461 } // namespace WebCore
462