1/* 2 * Copyright 2009, The Android Open Source Project 3 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#define LOG_TAG "WebCore" 28 29#include "config.h" 30#include "PluginView.h" 31 32#include "Document.h" 33#include "Element.h" 34#include "EventNames.h" 35#include "FocusController.h" 36#include "FrameLoader.h" 37#include "FrameLoadRequest.h" 38#include "FrameTree.h" 39#include "Frame.h" 40#include "FrameView.h" 41#include "GraphicsContext.h" 42#include "HTMLNames.h" 43#include "HTMLPlugInElement.h" 44#include "Image.h" 45#include "KeyboardEvent.h" 46#include "MIMETypeRegistry.h" 47#include "MouseEvent.h" 48#include "NetworkStateNotifier.h" 49#include "NotImplemented.h" 50#include "Page.h" 51#include "PlatformGraphicsContext.h" 52#include "PlatformKeyboardEvent.h" 53#include "PluginMainThreadScheduler.h" 54#include "PluginPackage.h" 55#include "Touch.h" 56#include "TouchEvent.h" 57#include "TouchList.h" 58#include "android_graphics.h" 59#include "SkCanvas.h" 60#include "npruntime_impl.h" 61// #include "runtime_root.h" 62#include "utils/SystemClock.h" 63#include "ScriptController.h" 64#include "Settings.h" 65 66#if USE(JSC) 67#include <runtime/JSLock.h> 68#endif 69 70#include <wtf/ASCIICType.h> 71// #include "runtime.h" 72#include "WebViewCore.h" 73 74/* Controls the printing of log messages in this file. This must be defined 75 before PluginDebugAndroid.h is included. 76 */ 77// #define PLUGIN_DEBUG_LOCAL 78#define TRACE_KEY_EVENTS 0 79 80#include "PluginDebug.h" 81#include "PluginDebugAndroid.h" 82#include "PluginViewBridgeAndroid.h" 83#include "PluginWidgetAndroid.h" 84 85#include "android_npapi.h" 86#include "ANPSurface_npapi.h" 87#include "ANPSystem_npapi.h" 88#include "SkANP.h" 89#include "SkFlipPixelRef.h" 90 91/////////////////////////////////////////////////////////////////////////////// 92 93extern void ANPAudioTrackInterfaceV0_Init(ANPInterface* value); 94extern void ANPBitmapInterfaceV0_Init(ANPInterface* value); 95extern void ANPCanvasInterfaceV0_Init(ANPInterface* value); 96extern void ANPEventInterfaceV0_Init(ANPInterface* value); 97extern void ANPLogInterfaceV0_Init(ANPInterface* value); 98extern void ANPMatrixInterfaceV0_Init(ANPInterface* value); 99extern void ANPOffscreenInterfaceV0_Init(ANPInterface* value); 100extern void ANPPaintInterfaceV0_Init(ANPInterface* value); 101extern void ANPPathInterfaceV0_Init(ANPInterface* value); 102extern void ANPSurfaceInterfaceV0_Init(ANPInterface* value); 103extern void ANPTypefaceInterfaceV0_Init(ANPInterface* value); 104extern void ANPWindowInterfaceV0_Init(ANPInterface* value); 105extern void ANPSystemInterfaceV0_Init(ANPInterface* value); 106 107struct VarProcPair { 108 int enumValue; 109 size_t size; 110 void (*proc)(ANPInterface*); 111}; 112 113#define VARPROCLINE(name) \ 114 k##name##_ANPGetValue, sizeof(ANP##name), ANP##name##_Init 115 116static const VarProcPair gVarProcs[] = { 117 { VARPROCLINE(AudioTrackInterfaceV0) }, 118 { VARPROCLINE(BitmapInterfaceV0) }, 119 { VARPROCLINE(CanvasInterfaceV0) }, 120 { VARPROCLINE(EventInterfaceV0) }, 121 { VARPROCLINE(LogInterfaceV0) }, 122 { VARPROCLINE(MatrixInterfaceV0) }, 123 { VARPROCLINE(PaintInterfaceV0) }, 124 { VARPROCLINE(PathInterfaceV0) }, 125 { VARPROCLINE(SurfaceInterfaceV0) }, 126 { VARPROCLINE(TypefaceInterfaceV0) }, 127 { VARPROCLINE(WindowInterfaceV0) }, 128 { VARPROCLINE(SystemInterfaceV0) }, 129}; 130 131/* return true if var was an interface request (error will be set accordingly) 132 return false if var is not a recognized interface (and ignore error param) 133 */ 134static bool anp_getInterface(NPNVariable var, void* value, NPError* error) { 135 const VarProcPair* iter = gVarProcs; 136 const VarProcPair* stop = gVarProcs + SK_ARRAY_COUNT(gVarProcs); 137 while (iter < stop) { 138 if (iter->enumValue == var) { 139 ANPInterface* i = reinterpret_cast<ANPInterface*>(value); 140 if (i->inSize < iter->size) { 141 SkDebugf("------- interface %d, expected size %d, allocated %d\n", 142 var, iter->size, i->inSize); 143 *error = NPERR_INCOMPATIBLE_VERSION_ERROR; 144 } else { 145 iter->proc(i); 146 *error = NPERR_NO_ERROR; 147 } 148 return true; 149 } 150 iter += 1; 151 } 152 SkDebugf("------ unknown NPNVariable %d\n", var); 153 return false; 154} 155 156/////////////////////////////////////////////////////////////////////////////// 157 158using std::min; 159 160using namespace WTF; 161 162namespace WebCore { 163 164using namespace HTMLNames; 165 166void PluginView::platformInit() 167{ 168 setPlatformWidget(new PluginViewBridgeAndroid()); 169 170 m_isWindowed = false; // we don't support windowed yet 171 172 m_window = new PluginWidgetAndroid(this); 173 174 m_npWindow.type = NPWindowTypeDrawable; 175 m_npWindow.window = 0; 176} 177 178bool PluginView::platformStart() 179{ 180 return true; 181} 182 183void PluginView::platformDestroy() 184{ 185 delete m_window; 186} 187 188void PluginView::handleTouchEvent(TouchEvent* event) 189{ 190 if (!m_window->isAcceptingEvent(kTouch_ANPEventFlag)) 191 return; 192 193 if (!m_window->inFullScreen() && m_parentFrame->document()->focusedNode() != m_element) 194 return; 195 196 ANPEvent evt; 197 SkANP::InitEvent(&evt, kTouch_ANPEventType); 198 199 const AtomicString& type = event->type(); 200 if (eventNames().touchstartEvent == type) 201 evt.data.touch.action = kDown_ANPTouchAction; 202 else if (eventNames().touchendEvent == type) 203 evt.data.touch.action = kUp_ANPTouchAction; 204 else if (eventNames().touchmoveEvent == type) 205 evt.data.touch.action = kMove_ANPTouchAction; 206 else if (eventNames().touchcancelEvent == type) 207 evt.data.touch.action = kCancel_ANPTouchAction; 208 else if (eventNames().touchlongpressEvent == type) 209 evt.data.touch.action = kLongPress_ANPTouchAction; 210 else if (eventNames().touchdoubletapEvent == type) 211 evt.data.touch.action = kDoubleTap_ANPTouchAction; 212 else 213 return; 214 215 evt.data.touch.modifiers = 0; // todo 216 217 // In the event of a touchend (up) or touchcancel event, we must ask the changedTouch for the 218 // co-ordinates as there is no touch in touches anymore. 219 TouchList* touches = (evt.data.touch.action == kUp_ANPTouchAction 220 || evt.data.touch.action == kCancel_ANPTouchAction) ? event->changedTouches() : event->touches(); 221 222 // Convert to coordinates that are relative to the plugin. 223 // We only support single touch points at the moment, so we want to look at index 0 only. 224 IntPoint localPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(IntPoint(touches->item(0)->pageX(), touches->item(0)->pageY()))); 225 evt.data.touch.x = localPos.x(); 226 evt.data.touch.y = localPos.y(); 227 228 if (m_window->sendEvent(evt)) 229 event->preventDefault(); 230} 231 232void PluginView::handleMouseEvent(MouseEvent* event) 233{ 234 const AtomicString& type = event->type(); 235 bool isUp = (eventNames().mouseupEvent == type); 236 bool isDown = (eventNames().mousedownEvent == type); 237 238 ANPEvent evt; 239 240 if (isUp || isDown) { 241 SkANP::InitEvent(&evt, kMouse_ANPEventType); 242 evt.data.mouse.action = isUp ? kUp_ANPMouseAction : kDown_ANPMouseAction; 243 244 // Convert to coordinates that are relative to the plugin. 245 IntPoint localPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); 246 evt.data.mouse.x = localPos.x(); 247 evt.data.mouse.y = localPos.y(); 248 249 if (isDown) { 250 // The plugin needs focus to receive keyboard and touch events 251 m_element->focus(); 252 event->setDefaultHandled(); 253 } 254 } 255 else { 256 return; 257 } 258 259 if (m_window->sendEvent(evt)) { 260 event->setDefaultHandled(); 261 } 262} 263 264static ANPKeyModifier make_modifiers(bool shift, bool alt) { 265 ANPKeyModifier mod = 0; 266 if (shift) { 267 mod |= kShift_ANPKeyModifier; 268 } 269 if (alt) { 270 mod |= kAlt_ANPKeyModifier; 271 } 272 return mod; 273} 274 275void PluginView::handleFocusEvent(bool hasFocus) 276{ 277 ANPEvent evt; 278 SkANP::InitEvent(&evt, kLifecycle_ANPEventType); 279 evt.data.lifecycle.action = hasFocus ? kGainFocus_ANPLifecycleAction : 280 kLoseFocus_ANPLifecycleAction; 281 m_window->sendEvent(evt); 282 283 // redraw the plugin which subsequently invalidates the nav cache 284 IntRect rect = IntRect(m_npWindow.x, m_npWindow.y, 285 m_npWindow.width, m_npWindow.height); 286 m_window->webViewCore()->contentInvalidate(rect); 287} 288 289void PluginView::handleKeyboardEvent(KeyboardEvent* event) 290{ 291 if (!m_window->isAcceptingEvent(kKey_ANPEventFlag)) 292 return; 293 294 const PlatformKeyboardEvent* pke = event->keyEvent(); 295 if (NULL == pke) { 296 return; 297 } 298 299 bool ignoreEvent = false; 300 301 ANPEvent evt; 302 SkANP::InitEvent(&evt, kKey_ANPEventType); 303 304 switch (pke->type()) { 305 case PlatformKeyboardEvent::KeyDown: 306#if TRACE_KEY_EVENTS 307 PLUGIN_LOG("--------- KeyDown, ignore\n"); 308#endif 309 ignoreEvent = true; 310 break; 311 case PlatformKeyboardEvent::RawKeyDown: 312 evt.data.key.action = kDown_ANPKeyAction; 313 break; 314 case PlatformKeyboardEvent::Char: 315#if TRACE_KEY_EVENTS 316 PLUGIN_LOG("--------- Char, ignore\n"); 317#endif 318 ignoreEvent = true; 319 break; 320 case PlatformKeyboardEvent::KeyUp: 321 evt.data.key.action = kUp_ANPKeyAction; 322 break; 323 default: 324#if TRACE_KEY_EVENTS 325 PLUGIN_LOG("------ unexpected keyevent type %d\n", pke->type()); 326#endif 327 ignoreEvent = true; 328 break; 329 } 330 331 /* the plugin should be the only party able to return nav control to the 332 * browser UI. Therefore, if we discard an event on behalf of the plugin 333 * we should mark the event as being handled. 334 */ 335 if (ignoreEvent) { 336 int keyCode = pke->nativeVirtualKeyCode(); 337 if (keyCode >= kDpadUp_ANPKeyCode && keyCode <= kDpadCenter_ANPKeyCode) 338 event->setDefaultHandled(); 339 return; 340 } 341 342 evt.data.key.nativeCode = pke->nativeVirtualKeyCode(); 343 evt.data.key.virtualCode = pke->windowsVirtualKeyCode(); 344 evt.data.key.repeatCount = pke->repeatCount(); 345 evt.data.key.modifiers = make_modifiers(pke->shiftKey(), pke->altKey()); 346 evt.data.key.unichar = pke->unichar(); 347 348 if (m_window->sendEvent(evt)) { 349 event->setDefaultHandled(); 350 } else if (m_window->inFullScreen()){ 351 // while in the full screen mode, always consumes the key events and 352 // keeps the document focus 353 event->setDefaultHandled(); 354 } else { 355 // remove the plugin from the document's focus 356 m_parentFrame->document()->focusedNodeRemoved(); 357 } 358} 359 360NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf) 361{ 362 notImplemented(); 363 return NPERR_GENERIC_ERROR; 364} 365 366NPError PluginView::getValueStatic(NPNVariable variable, void* value) 367{ 368 // our interface query is valid with no NPP instance 369 NPError error = NPERR_GENERIC_ERROR; 370 371 switch (variable) { 372 case NPNVisOfflineBool: { 373 if (value != NULL) { 374 bool* retValue = static_cast<bool*>(value); 375 *retValue = !networkStateNotifier().onLine(); 376 return NPERR_NO_ERROR; 377 } 378 break; 379 } 380 case kJavaContext_ANPGetValue: { 381 jobject* retObject = static_cast<jobject*>(value); 382 *retObject = android::WebViewCore::getApplicationContext(); 383 return NPERR_NO_ERROR; 384 } 385 default: 386 ; // do nothing 387 } 388 389 (void)anp_getInterface(variable, value, &error); 390 return error; 391} 392 393void PluginView::setParent(ScrollView* parent) 394{ 395 PLUGIN_LOG("--%p SetParent old=[%p], new=[%p] \n", instance(), this->parent(), parent); 396 397 Widget::setParent(parent); 398 399 if (parent) { 400 // the widget needs initialized now so that the plugin has access to 401 // WebViewCore when NPP_New is called 402 if (m_window && !m_window->webViewCore()) { 403 android::WebViewCore* c = android::WebViewCore::getWebViewCore(this->parent()); 404 m_window->init(c); 405 } 406 init(); 407 408 /* Our widget needs to recompute its m_windowRect which then sets 409 the NPWindowRect if necessary. This ensures that if NPWindowRect 410 is set prior to parent() being set that we still (1) notify the 411 plugin of its current rect and (2) that we execute our logic in 412 PluginWidgetAndroid in response to changes to NPWindowRect. 413 */ 414 updatePluginWidget(); 415 } 416} 417 418void PluginView::setNPWindowRect(const IntRect&) 419{ 420 setNPWindowIfNeeded(); 421} 422 423void PluginView::setNPWindowIfNeeded() 424{ 425 PLUGIN_LOG("--%p SetWindow isStarted=[%d] \n", instance(), m_isStarted); 426 427 if (!m_isStarted || !parent()) 428 return; 429 430 // in Android, plugin always get the setwindow() in the page coordinate. 431 432 // the m_npWindow is relative to the page 433 m_npWindow.x = m_pageRect.x(); 434 m_npWindow.y = m_pageRect.y(); 435 m_npWindow.width = m_pageRect.width(); 436 m_npWindow.height = m_pageRect.height(); 437 438 m_npWindow.clipRect.left = m_pageRect.x(); 439 m_npWindow.clipRect.top = m_pageRect.y(); 440 m_npWindow.clipRect.right = m_pageRect.x() + m_pageRect.width(); 441 m_npWindow.clipRect.bottom = m_pageRect.y() + m_pageRect.height(); 442 443 if (m_plugin->pluginFuncs()->setwindow) { 444#if USE(JSC) 445 JSC::JSLock::DropAllLocks dropAllLocks(false); 446#endif 447 setCallingPlugin(true); 448 m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); 449 setCallingPlugin(false); 450 } 451 452 m_window->setWindow(&m_npWindow, m_isTransparent); 453} 454 455NPError PluginView::getValue(NPNVariable variable, void* value) 456{ 457 switch (variable) { 458 case NPNVWindowNPObject: { 459 NPObject* windowScriptObject = 460 m_parentFrame->script()->windowScriptNPObject(); 461 462 // Return value is expected to be retained, as described 463 // here: 464 // <http://www.mozilla.org/projects/plugin/npruntime.html> 465 if (windowScriptObject) 466 _NPN_RetainObject(windowScriptObject); 467 468 void** v = (void**)value; 469 *v = windowScriptObject; 470 471 return NPERR_NO_ERROR; 472 } 473 474 case NPNVPluginElementNPObject: { 475 NPObject* pluginScriptObject = 0; 476 477 if (m_element->hasTagName(appletTag) || 478 m_element->hasTagName(embedTag) || 479 m_element->hasTagName(objectTag)) { 480 HTMLPlugInElement* pluginElement = 481 static_cast<HTMLPlugInElement*>(m_element); 482 pluginScriptObject = pluginElement->getNPObject(); 483 } 484 485 // Return value is expected to be retained, as described 486 // here: 487 // <http://www.mozilla.org/projects/plugin/npruntime.html> 488 if (pluginScriptObject) 489 _NPN_RetainObject(pluginScriptObject); 490 491 void** v = (void**)value; 492 *v = pluginScriptObject; 493 494 return NPERR_NO_ERROR; 495 } 496 497 case NPNVnetscapeWindow: { 498 // Return the top level WebView Java object associated 499 // with this instance. 500 jobject *retObject = static_cast<jobject*>(value); 501 *retObject = android::WebViewCore::getWebViewCore(parent())->getWebViewJavaObject(); 502 return NPERR_NO_ERROR; 503 } 504 505 case NPNVisOfflineBool: { 506 if (value == NULL) { 507 return NPERR_GENERIC_ERROR; 508 } 509 bool* retValue = static_cast<bool*>(value); 510 *retValue = !networkStateNotifier().onLine(); 511 return NPERR_NO_ERROR; 512 } 513 514 case kSupportedDrawingModel_ANPGetValue: { 515 uint32_t* bits = reinterpret_cast<uint32_t*>(value); 516 *bits = kBitmap_ANPDrawingModel & kSurface_ANPDrawingModel; 517 return NPERR_NO_ERROR; 518 } 519 520 case kJavaContext_ANPGetValue: { 521 jobject* retObject = static_cast<jobject*>(value); 522 *retObject = android::WebViewCore::getWebViewCore(parent())->getContext(); 523 return NPERR_NO_ERROR; 524 } 525 526 default: { 527 NPError error = NPERR_GENERIC_ERROR; 528 (void)anp_getInterface(variable, value, &error); 529 return error; 530 } 531 } 532} 533 534NPError PluginView::platformSetValue(NPPVariable variable, void* value) 535{ 536 NPError error = NPERR_GENERIC_ERROR; 537 538 switch (variable) { 539 case kRequestDrawingModel_ANPSetValue: { 540 ANPDrawingModel model = reinterpret_cast<ANPDrawingModel>(value); 541 if (m_window->setDrawingModel(model)) 542 error = NPERR_NO_ERROR; 543 break; 544 } 545 case kAcceptEvents_ANPSetValue : { 546 if(value) { 547 ANPEventFlags flags = *reinterpret_cast<ANPEventFlags*>(value); 548 m_window->updateEventFlags(flags); 549 error = NPERR_NO_ERROR; 550 } 551 break; 552 } 553 default: 554 break; 555 } 556 return error; 557} 558 559void PluginView::invalidateRect(const IntRect& r) 560{ 561 m_window->inval(r, true); 562} 563 564void PluginView::invalidateRect(NPRect* rect) 565{ 566 IntRect r; 567 568 if (rect) { 569 r = IntRect(rect->left, rect->top, 570 rect->right - rect->left, rect->bottom - rect->top); 571 } else { 572 r = IntRect(0, 0, m_npWindow.width, m_npWindow.height); 573 } 574 575 m_window->inval(r, true); 576} 577 578void PluginView::invalidateRegion(NPRegion region) 579{ 580 // we don't support/define regions (yet), so do nothing 581} 582 583void PluginView::forceRedraw() 584{ 585 this->invalidateRect(0); 586} 587 588void PluginView::setFocus() 589{ 590 Widget::setFocus(); 591// SkDebugf("------------- setFocus %p\n", this); 592} 593 594void PluginView::show() 595{ 596 setSelfVisible(true); 597 Widget::show(); 598 599 if (platformPluginWidget()) 600 platformPluginWidget()->layoutSurface(); 601 602} 603 604void PluginView::hide() 605{ 606 setSelfVisible(false); 607 Widget::hide(); 608 609 if (platformPluginWidget()) 610 platformPluginWidget()->layoutSurface(); 611} 612 613void PluginView::setParentVisible(bool visible) { 614 615 if (isParentVisible() == visible) 616 return; 617 618 Widget::setParentVisible(visible); 619 620 if (platformPluginWidget()) 621 platformPluginWidget()->layoutSurface(); 622 623} 624 625void PluginView::paint(GraphicsContext* context, const IntRect& rect) 626{ 627 if (!m_isStarted) { 628 // Draw the "missing plugin" image 629 paintMissingPluginIcon(context, rect); 630 return; 631 } 632 633 IntRect frame = frameRect(); 634 if (!frame.width() || !frame.height()) { 635 PLUGIN_LOG("--%p FrameRect Dimensions are (0,0).\n", instance()); 636 return; 637 } 638 639 if (m_window->isSurfaceDrawingModel()) { 640 /* the document position of the frame (e.g. iFrame) containing the 641 surface may have changed, which requires us to to update the global 642 coordinates of the surface. This is necessary because the plugin has 643 not moved within its parent frame and therefore will not get any 644 notification of its global position change. 645 */ 646 updatePluginWidget(); 647 m_window->setSurfaceClip(context->platformContext()->mCanvas->getTotalClip().getBounds()); 648 } else { 649 m_window->inval(rect, false); 650 context->save(); 651 context->translate(frame.x(), frame.y()); 652 m_window->draw(android_gc2canvas(context)); 653 context->restore(); 654 } 655 656 657} 658 659void PluginView::updatePluginWidget() 660{ 661 FrameView* frameView = static_cast<FrameView*>(parent()); 662 PLUGIN_LOG("--%p UpdatePluginWidget frame=[%p] \n", instance(), frameView); 663 if (frameView) { 664 m_windowRect = frameView->contentsToWindow(frameRect()); 665 666 IntRect oldPageRect = m_pageRect; 667 668 // only the top ScrollView can have the offset 669 m_pageRect = m_windowRect; 670 ScrollView* top = parent(); 671 while (top->parent()) 672 top = top->parent(); 673 m_pageRect.move(top->scrollOffset()); 674 675 if (m_pageRect != oldPageRect) 676 setNPWindowIfNeeded(); 677 } 678} 679 680void PluginView::halt() { 681 notImplemented(); 682} 683 684void PluginView::restart() { 685 notImplemented(); 686} 687 688} // namespace WebCore 689