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 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 101 void PluginView::setFocus() 102 { 103 if (platformPluginWidget()) 104 platformPluginWidget()->setFocus(Qt::OtherFocusReason); 105 else 106 Widget::setFocus(); 107 } 108 109 void PluginView::show() 110 { 111 setSelfVisible(true); 112 113 if (isParentVisible() && platformPluginWidget()) 114 platformPluginWidget()->setVisible(true); 115 } 116 117 void PluginView::hide() 118 { 119 setSelfVisible(false); 120 121 if (isParentVisible() && platformPluginWidget()) 122 platformPluginWidget()->setVisible(false); 123 } 124 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. 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 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 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 236 void PluginView::setParent(ScrollView* parent) 237 { 238 Widget::setParent(parent); 239 240 if (parent) 241 init(); 242 } 243 244 void PluginView::setNPWindowRect(const IntRect&) 245 { 246 if (!m_isWindowed) 247 setNPWindowIfNeeded(); 248 } 249 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 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 304 NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf) 305 { 306 notImplemented(); 307 return NPERR_NO_ERROR; 308 } 309 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 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 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 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 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 413 void PluginView::forceRedraw() 414 { 415 if (m_isWindowed) 416 return; 417 invalidate(); 418 } 419 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 448 void PluginView::platformDestroy() 449 { 450 delete platformPluginWidget(); 451 } 452 453 void PluginView::halt() 454 { 455 } 456 457 void PluginView::restart() 458 { 459 } 460 461 } // namespace WebCore 462