PluginViewBlackBerry.cpp   [plain text]


/*
 * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
 * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
 * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "PluginView.h"

#include "DocumentLoader.h"
#include "Event.h"
#include "EventNames.h"
#include "Frame.h"
#include "FrameView.h"
#include "HTMLNames.h"
#include "HTMLPlugInElement.h"
#include "HostWindow.h"
#include "JSDOMBinding.h"
#include "KeyboardEvent.h"
#include "MouseEvent.h"
#include "NPCallbacksBlackBerry.h"
#include "NotImplemented.h"
#include "Page.h"
#include "PlatformContextSkia.h"
#include "PlatformKeyboardEvent.h"
#include "PluginDebug.h"
#include "PluginMainThreadScheduler.h"
#include "PluginPackage.h"
#include "PluginViewPrivateBlackBerry.h"
#include "RenderLayer.h"
#include "Settings.h"
#include "TouchEvent.h"
#include "TouchList.h"
#include "WheelEvent.h"
#include "npruntime_impl.h"
#include "runtime_root.h"

#if USE(ACCELERATED_COMPOSITING)
#include "Chrome.h"
#include "ChromeClient.h"
#include "PluginLayerWebKitThread.h"
#endif

#include <BlackBerryPlatformGraphics.h>
#include <BlackBerryPlatformIntRectRegion.h>
#include <BlackBerryPlatformWindow.h>

#include <runtime/JSLock.h>
#include <runtime/JSValue.h>
#include <sys/keycodes.h>
#include <vector>

const unsigned UninitializedCoordinate = 0xffffffff;

namespace WebCore {

template<class T> static NPRect toNPRect(const T& rect)
{
    NPRect npRect;
    npRect.top = rect.y();
    npRect.left = rect.x();
    npRect.bottom = rect.y() + rect.height();
    npRect.right = rect.x() + rect.width();
    return npRect;
}

using JSC::ExecState;
using JSC::Interpreter;
using JSC::JSLock;
using JSC::JSObject;
using JSC::UString;

using namespace std;
using namespace WTF;
using namespace HTMLNames;

void PluginView::updatePluginWidget()
{
    if (!parent() || !m_private)
        return;

    ASSERT(parent()->isFrameView());
    FrameView* frameView = static_cast<FrameView*>(parent());

    IntRect oldWindowRect = m_windowRect;
    IntRect oldClipRect = m_clipRect;

    m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());

    // Map rect to content coordinate space of main frame.
    m_windowRect.move(root()->scrollOffset());

    m_clipRect = calculateClipRect();

    // Notify the plugin if it may or may not be on/offscreen.
    handleScrollEvent();

    bool zoomFactorChanged = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor
        != frameView->hostWindow()->platformPageClient()->currentZoomFactor();

    if (!zoomFactorChanged && m_windowRect == oldWindowRect && m_clipRect == oldClipRect)
        return;

    // Do not call setNPWindowIfNeeded immediately, will be called on paint().
    m_private->m_hasPendingGeometryChange = true;

    // (i) In order to move/resize the plugin window at the same time as the
    // rest of frame during e.g. scrolling, we set the window geometry
    // in the paint() function, but as paint() isn't called when the
    // plugin window is outside the frame which can be caused by a
    // scroll, we need to move/resize immediately.
    // (ii) If we are running layout tests from DRT, paint() won't ever get called
    // so we need to call setNPWindowIfNeeded() if window geometry has changed.
    if (m_clipRect.isEmpty() || (platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect || zoomFactorChanged)))
        setNPWindowIfNeeded();

    // Make sure we get repainted afterwards. This is necessary for downward
    // scrolling to move the plugin widget properly.
    invalidate();
}

void PluginView::setFocus(bool focused)
{
    if (!m_private || m_private->m_isFocused == focused)
        return;

    ASSERT(platformPluginWidget() == platformWidget());
    Widget::setFocus(focused);

    if (focused)
        handleFocusInEvent();
    else
        handleFocusOutEvent();
}

void PluginView::show()
{
    setSelfVisible(true);
    Widget::show();
    updatePluginWidget();
}

void PluginView::hide()
{
    setSelfVisible(false);
    Widget::hide();
    updatePluginWidget();
}

void PluginView::updateBuffer(const IntRect& bufferRect)
{
    if (!m_private)
        return;

    // Update the zoom factor here, it happens right before setNPWindowIfNeeded
    // ensuring that the plugin has every opportunity to get the zoom factor before
    // it paints anything.
    if (FrameView* frameView = static_cast<FrameView*>(parent()))
        m_private->setZoomFactor(frameView->hostWindow()->platformPageClient()->currentZoomFactor());

    setNPWindowIfNeeded();

    // Build and dispatch an event to the plugin to notify it we are about to draw whatever
    // is in the front buffer. This is it's opportunity to do a swap.
    IntRect exposedRect(bufferRect);
    exposedRect.intersect(IntRect(IntPoint(0, 0), frameRect().size()));

    // Only ask the plugin to draw if the exposed rect was explicitly invalidated
    // by the plugin.
    BlackBerry::Platform::IntRectRegion exposedRegion = BlackBerry::Platform::IntRectRegion::intersectRegions(m_private->m_invalidateRegion, BlackBerry::Platform::IntRect(exposedRect));
    if (!exposedRegion.isEmpty()) {
        m_private->m_invalidateRegion = BlackBerry::Platform::IntRectRegion::subtractRegions(m_private->m_invalidateRegion, exposedRegion);
        std::vector<BlackBerry::Platform::IntRect> exposedRects = exposedRegion.rects();
        for (unsigned i = 0; i < exposedRects.size(); ++i) {
            NPDrawEvent draw;
            draw.pluginRect = toNPRect(m_windowRect);
            draw.clipRect = toNPRect(m_clipRect);
            draw.drawRect = (NPRect*)alloca(sizeof(NPRect));
            draw.drawRectCount = 1;
            *draw.drawRect = toNPRect(exposedRects.at(i));
            draw.zoomFactor = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor;

            NPEvent npEvent;
            npEvent.type = NP_DrawEvent;
            npEvent.data = &draw;

            // FIXME: Return early if this fails? Or just repaint?
            dispatchNPEvent(npEvent);
        }
    }
}

void PluginView::paint(GraphicsContext* context, const IntRect& rect)
{
    if (!m_private || !m_isStarted) {
        paintMissingPluginIcon(context, rect);
        return;
    }

    // Update the zoom factor here, it happens right before setNPWindowIfNeeded
    // ensuring that the plugin has every opportunity to get the zoom factor before
    // it paints anything.
    if (FrameView* frameView = static_cast<FrameView*>(parent()))
        m_private->setZoomFactor(frameView->hostWindow()->platformPageClient()->currentZoomFactor());

    if (context->paintingDisabled())
        return;

    setNPWindowIfNeeded();

#if USE(ACCELERATED_COMPOSITING)
    if (m_private->m_platformLayer)
        return;
#endif

    // Build and dispatch an event to the plugin to notify it we are about to draw whatever
    // is in the front buffer. This is it's opportunity to do a swap.
    IntRect rectClip(rect);
    rectClip.intersect(frameRect());

    IntRect exposedRect(rectClip);
    exposedRect.move(-frameRect().x(), -frameRect().y());

    updateBuffer(exposedRect);

    PthreadReadLocker frontLock(&m_private->m_frontBufferRwLock);

    BlackBerry::Platform::Graphics::Buffer* frontBuffer =
        m_private->m_pluginBuffers[m_private->m_pluginFrontBuffer];

    // Don't paint anything if there is no buffer.
    if (!frontBuffer)
        return;

    const BlackBerry::Platform::Graphics::BackingImage* backingImage =
        BlackBerry::Platform::Graphics::lockBufferBackingImage(frontBuffer,
                                                            BlackBerry::Platform::Graphics::ReadAccess);
    if (!backingImage)
        return;

    // Draw the changed buffer contents to the screen.
    context->save();

    const SkBitmap& pluginImage = *backingImage;
    PlatformGraphicsContext* graphics = context->platformContext();
    ASSERT(graphics);
    SkCanvas* canvas = graphics->canvas();

    // Source rectangle we will draw to the screen.
    SkIRect skSrcRect;
    skSrcRect.set(exposedRect.x(), exposedRect.y(),
                  exposedRect.x() + exposedRect.width(),
                  exposedRect.y() + exposedRect.height());

    // Prepare the hole punch rectangle.
    SkIRect unclippedHolePunchRect;
    unclippedHolePunchRect.set(m_private->m_holePunchRect.x(),
                               m_private->m_holePunchRect.y(),
                               m_private->m_holePunchRect.x() + m_private->m_holePunchRect.width(),
                               m_private->m_holePunchRect.y() + m_private->m_holePunchRect.height());

    // holePunchRect is clipped.
    SkIRect holePunchRect;

    // All source rectangles are scaled by the zoom factor because the source bitmap may be a
    // higher resolution than the 1:1 page. This allows the flash player to scale the content
    // it is drawing to match the scale of the page.
    double zoomFactorH = static_cast<double>(m_private->m_pluginBufferSize.width()) / static_cast<double>(frameRect().width());
    double zoomFactorW = static_cast<double>(m_private->m_pluginBufferSize.height()) / static_cast<double>(frameRect().height());
    double zoomFactor = (zoomFactorH + zoomFactorW) / 2.0;

    // This method draws a hole if specified.
    if (!m_private->m_holePunchRect.isEmpty()
        && holePunchRect.intersect(unclippedHolePunchRect, skSrcRect)) {

        // Draw the top chunk if needed.
        if (holePunchRect.fTop > skSrcRect.fTop) {
            SkIRect srcRect;
            srcRect.set(skSrcRect.fLeft * zoomFactor, skSrcRect.fTop * zoomFactor,
                        skSrcRect.fRight * zoomFactor, holePunchRect.fTop * zoomFactor);

            SkRect dstRect;
            dstRect.set(skSrcRect.fLeft, skSrcRect.fTop,
                        skSrcRect.fRight, holePunchRect.fTop);
            dstRect.offset(frameRect().x(), frameRect().y());

            canvas->drawBitmapRect(pluginImage, &srcRect, dstRect);
        }

        // Draw the left chunk if needed.
        if (holePunchRect.fLeft > skSrcRect.fLeft) {
            SkIRect srcRect;
            srcRect.set(skSrcRect.fLeft * zoomFactor, holePunchRect.fTop * zoomFactor,
                        holePunchRect.fLeft * zoomFactor, holePunchRect.fBottom * zoomFactor);

            SkRect dstRect;
            dstRect.set(skSrcRect.fLeft, holePunchRect.fTop,
                        holePunchRect.fLeft, holePunchRect.fBottom);
            dstRect.offset(frameRect().x(), frameRect().y());

            canvas->drawBitmapRect(pluginImage, &srcRect, dstRect);
        }

        // Draw the hole chunk.
        {
            SkPaint paint;
            paint.setXfermodeMode(SkXfermode::kSrc_Mode);

            SkIRect srcRect;
            srcRect.set(holePunchRect.fLeft * zoomFactor, holePunchRect.fTop * zoomFactor,
                        holePunchRect.fRight * zoomFactor, holePunchRect.fBottom * zoomFactor);

            SkRect dstRect;
            dstRect.set(holePunchRect.fLeft, holePunchRect.fTop,
                        holePunchRect.fRight, holePunchRect.fBottom);
            dstRect.offset(frameRect().x(), frameRect().y());

            canvas->drawBitmapRect(pluginImage, &srcRect, dstRect, &paint);
        }

        // Draw the right chunk if needed.
        if (holePunchRect.fRight < skSrcRect.fRight) {
            SkIRect srcRect;
            srcRect.set(holePunchRect.fRight * zoomFactor, holePunchRect.fTop * zoomFactor,
                        skSrcRect.fRight * zoomFactor, holePunchRect.fBottom * zoomFactor);

            SkRect dstRect;
            dstRect.set(holePunchRect.fRight, holePunchRect.fTop, skSrcRect.fRight, holePunchRect.fBottom);
            dstRect.offset(frameRect().x(), frameRect().y());

            canvas->drawBitmapRect(pluginImage, &srcRect, dstRect);
        }

        // Draw the bottom chunk if needed.
        if (holePunchRect.fBottom < skSrcRect.fBottom) {
            SkIRect srcRect;
            srcRect.set(skSrcRect.fLeft * zoomFactor, holePunchRect.fBottom * zoomFactor,
                        skSrcRect.fRight * zoomFactor, skSrcRect.fBottom * zoomFactor);

            SkRect dstRect;
            dstRect.set(skSrcRect.fLeft, holePunchRect.fBottom,
                        skSrcRect.fRight, skSrcRect.fBottom);
            dstRect.offset(frameRect().x(), frameRect().y());

            canvas->drawBitmapRect(pluginImage, &srcRect, dstRect);
        }
    } else {
        SkIRect srcRect;
        srcRect.set(skSrcRect.fLeft * zoomFactor, skSrcRect.fTop * zoomFactor,
                    skSrcRect.fRight * zoomFactor, skSrcRect.fBottom * zoomFactor);

        // Calculate the destination rectangle.
        SkRect dstRect;
        dstRect.set(rectClip.x(), rectClip.y(),
                    rectClip.x() + rectClip.width(),
                    rectClip.y() + rectClip.height());

        // Don't punch a hole.
        canvas->drawBitmapRect(pluginImage, &srcRect, dstRect);
    }

    context->restore();

    BlackBerry::Platform::Graphics::releaseBufferBackingImage(frontBuffer);
}


bool PluginView::dispatchFullScreenNPEvent(NPEvent& event)
{
    if (!m_private)
        return false;

    ASSERT(m_private->m_isFullScreen);
    return dispatchNPEvent(event);
}

// FIXME: Unify across ports.
bool PluginView::dispatchNPEvent(NPEvent& event)
{
    if (!m_plugin || !m_plugin->pluginFuncs()->event)
        return false;

    PluginView::setCurrentPluginView(this);
    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonJSGlobalData());
    setCallingPlugin(true);

    bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);

    setCallingPlugin(false);
    PluginView::setCurrentPluginView(0);

    return accepted;
}

void PluginView::handleKeyboardEvent(KeyboardEvent* event)
{
    NPEvent npEvent;
    NPKeyboardEvent keyEvent;
    const PlatformKeyboardEvent *platformKeyEvent = event->keyEvent();

    keyEvent.modifiers = 0;

    if (platformKeyEvent->shiftKey())
        keyEvent.modifiers |= KEYMOD_SHIFT;

    if (platformKeyEvent->ctrlKey())
        keyEvent.modifiers |= KEYMOD_CTRL;

    if (platformKeyEvent->altKey())
        keyEvent.modifiers |= KEYMOD_ALT;

    if (platformKeyEvent->metaKey())
        keyEvent.modifiers |= KEYMOD_ALTGR;

    keyEvent.cap = platformKeyEvent->unmodifiedCharacter();
    keyEvent.sym = keyEvent.cap;
    keyEvent.scan = 0;
    keyEvent.flags = 0;

    if (platformKeyEvent->type() == PlatformKeyboardEvent::RawKeyDown
        || platformKeyEvent->type() == PlatformKeyboardEvent::KeyDown) {
        keyEvent.flags = KEY_DOWN | KEY_SYM_VALID | KEY_CAP_VALID;
    } else if (platformKeyEvent->type() == PlatformKeyboardEvent::KeyUp)
        keyEvent.flags = KEY_SYM_VALID | KEY_CAP_VALID;

    npEvent.type = NP_KeyEvent;
    npEvent.data = &keyEvent;
    if (dispatchNPEvent(npEvent))
        event->setDefaultHandled();
}

void PluginView::handleWheelEvent(WheelEvent* event)
{
    if (!m_private)
        return;

    if (!m_private->m_isFocused)
        focusPluginElement();

    NPEvent npEvent;
    NPWheelEvent wheelEvent;

    wheelEvent.x = event->offsetX();
    wheelEvent.y = event->offsetY();

    wheelEvent.flags = 0;

    wheelEvent.xDelta = event->rawDeltaX();
    wheelEvent.yDelta = event->rawDeltaY();

    npEvent.type = NP_WheelEvent;
    npEvent.data = &wheelEvent;

    if (dispatchNPEvent(npEvent))
        event->setDefaultHandled();
}

void PluginView::handleTouchEvent(TouchEvent* event)
{
    if (!m_private)
        return;

    if (!m_private->m_isFocused)
        focusPluginElement();

    NPTouchEvent npTouchEvent;

    if (event->isDoubleTap())
        npTouchEvent.type = TOUCH_EVENT_DOUBLETAP;
    else if (event->isTouchHold())
        npTouchEvent.type = TOUCH_EVENT_TOUCHHOLD;
    else if (event->type() == eventNames().touchstartEvent)
        npTouchEvent.type = TOUCH_EVENT_START;
    else if (event->type() == eventNames().touchendEvent)
        npTouchEvent.type = TOUCH_EVENT_END;
    else if (event->type() == eventNames().touchmoveEvent)
        npTouchEvent.type = TOUCH_EVENT_MOVE;
    else if (event->type() == eventNames().touchcancelEvent)
        npTouchEvent.type = TOUCH_EVENT_CANCEL;
    else {
        ASSERT_NOT_REACHED();
        return;
    }

    TouchList* touchList;
    // The touches list is empty if in a touch end event. Use changedTouches instead.
    if (npTouchEvent.type == TOUCH_EVENT_DOUBLETAP || npTouchEvent.type == TOUCH_EVENT_END)
        touchList = event->changedTouches();
    else
        touchList = event->touches();

    npTouchEvent.points = 0;
    npTouchEvent.size = touchList->length();

    OwnArrayPtr<NPTouchPoint> touchPoints;
    if (touchList->length()) {
        touchPoints = adoptArrayPtr(new NPTouchPoint[touchList->length()]);
        npTouchEvent.points = touchPoints.get();
        for (unsigned i = 0; i < touchList->length(); i++) {
            Touch* touchItem = touchList->item(i);
            touchPoints[i].touchId = touchItem->identifier();
            touchPoints[i].clientX = touchItem->pageX() - frameRect().x();
            touchPoints[i].clientY = touchItem->pageY() - frameRect().y();
            touchPoints[i].screenX = touchItem->screenX();
            touchPoints[i].screenY = touchItem->screenY();
            touchPoints[i].pageX = touchItem->pageX();
            touchPoints[i].pageY = touchItem->pageY();

        }
    }

    NPEvent npEvent;
    npEvent.type = NP_TouchEvent;
    npEvent.data = &npTouchEvent;

    if (dispatchNPEvent(npEvent))
        event->setDefaultHandled();
    else if (npTouchEvent.type == TOUCH_EVENT_DOUBLETAP) {
        // Send Touch Up if double tap not consumed
        npTouchEvent.type = TOUCH_EVENT_END;
        npEvent.data = &npTouchEvent;
        if (dispatchNPEvent(npEvent))
            event->setDefaultHandled();
    }
}

void PluginView::handleMouseEvent(MouseEvent* event)
{
    if (!m_private)
        return;

    if (!m_private->m_isFocused)
        focusPluginElement();

    NPEvent npEvent;
    NPMouseEvent mouseEvent;

    mouseEvent.x = event->offsetX();
    mouseEvent.y = event->offsetY();

    if (event->type() == eventNames().mousedownEvent) {
        mouseEvent.type = MOUSE_BUTTON_DOWN;
        parentFrame()->eventHandler()->setCapturingMouseEventsNode(node());
    } else if (event->type() == eventNames().mousemoveEvent)
        mouseEvent.type = MOUSE_MOTION;
    else if (event->type() == eventNames().mouseoutEvent)
        mouseEvent.type = MOUSE_OUTBOUND;
    else if (event->type() == eventNames().mouseoverEvent)
        mouseEvent.type = MOUSE_OVER;
    else if (event->type() == eventNames().mouseupEvent) {
        mouseEvent.type = MOUSE_BUTTON_UP;
        parentFrame()->eventHandler()->setCapturingMouseEventsNode(0);
    } else
        return;

    mouseEvent.button = event->button();
    mouseEvent.flags = 0;

    npEvent.type = NP_MouseEvent;
    npEvent.data = &mouseEvent;

    if (dispatchNPEvent(npEvent))
        event->setDefaultHandled();
}

void PluginView::handleFocusInEvent()
{
    if (!m_private)
        return;

    if (m_private->m_isFocused)
        return;

    m_private->m_isFocused = true;

    NPEvent npEvent;
    npEvent.type = NP_FocusGainedEvent;
    npEvent.data = 0;
    dispatchNPEvent(npEvent);
}

void PluginView::handleFocusOutEvent()
{
    if (!m_private)
        return;

    if (!m_private->m_isFocused)
        return;

    m_private->m_isFocused = false;

    NPEvent npEvent;
    npEvent.type = NP_FocusLostEvent;
    npEvent.data = 0;
    dispatchNPEvent(npEvent);
}

void PluginView::handlePauseEvent()
{
    NPEvent npEvent;
    npEvent.type = NP_PauseEvent;
    npEvent.data = 0;
    dispatchNPEvent(npEvent);
}

void PluginView::handleResumeEvent()
{
    NPEvent npEvent;
    npEvent.type = NP_ResumeEvent;
    npEvent.data = 0;
    dispatchNPEvent(npEvent);
}

void PluginView::handleScrollEvent()
{
    FrameView* frameView = static_cast<FrameView*>(parent());

    // As a special case, if the frameView extent in either dimension is
    // empty, then send an on screen event. This is important for sites like
    // picnik.com which use a hidden iframe (read: width = 0 and height = 0)
    // with an embedded swf to execute some javascript. Unless we send an
    // on screen event the swf will not execute the javascript and the real
    // site will never load.
    bool onScreenEvent = frameView && (!frameView->width() || !frameView->height());

    NPEvent npEvent;
    npEvent.type = m_clipRect.isEmpty() && !onScreenEvent ? NP_OffScreenEvent : NP_OnScreenEvent;
    npEvent.data = 0;
    dispatchNPEvent(npEvent);
}

IntRect PluginView::calculateClipRect() const
{
    FrameView* frameView = static_cast<FrameView*>(parent());
    bool visible = frameView && isVisible();

    if (visible && frameView->width() && frameView->height()) {
        IntSize windowSize = frameView->hostWindow()->platformPageClient()->viewportSize();

        // Get the clipped rectangle for this player within the current frame.
        IntRect visibleContentRect;
        IntRect contentRect = m_element->renderer()->absoluteClippedOverflowRect();
        FloatPoint contentLocal = m_element->renderer()->absoluteToLocal(FloatPoint(contentRect.location()));

        contentRect.setLocation(roundedIntPoint(contentLocal));
        contentRect.move(frameRect().x(), frameRect().y());

        // Clip against any frames that the widget is inside. Note that if the frames are also clipped
        // by a div, that will not be included in this calculation. That is an improvement that still
        // needs to be made.
        const Widget* current = this;
        while (current->parent() && visible) {
            // Determine if it is visible in this scrollview.
            visibleContentRect = current->parent()->visibleContentRect();

            // Special case for the root ScrollView. Its size does not match the actual window size.
            if (current->parent() == root())
                visibleContentRect.setSize(windowSize);

            contentRect.intersect(visibleContentRect);
            visible = !contentRect.isEmpty();

            // Offset to visible coordinates in scrollview widget's coordinate system (except in the case of
            // the top scroll view).
            if (current->parent()->parent())
                contentRect.move(-visibleContentRect.x(), -visibleContentRect.y());

            current = current->parent();

            // Don't include the offset for the root window or we get the wrong coordinates.
            if (current->parent()) {
                // Move content rect into the parent scrollview's coordinates.
                IntRect curFrameRect = current->frameRect();
                contentRect.move(curFrameRect.x(), curFrameRect.y());
            }
        }

        return contentRect;
    }

    return IntRect();
}

void PluginView::handleOnLoadEvent()
{
    if (!m_private)
        return;

    if (m_private->m_sentOnLoad)
        return;

    m_private->m_sentOnLoad = true;

    NPEvent npEvent;
    npEvent.type = NP_OnLoadEvent;
    npEvent.data = 0;

    dispatchNPEvent(npEvent);

    // Send an initial OnScreen/OffScreen event. It must always come after onLoad is sent.
    handleScrollEvent();
}

void PluginView::handleFreeMemoryEvent()
{
    NPEvent npEvent;
    npEvent.type = NP_FreeMemoryEvent;
    npEvent.data = 0;

    dispatchNPEvent(npEvent);
}

void PluginView::handleBackgroundEvent()
{
    NPEvent npEvent;
    npEvent.type = NP_BackgroundEvent;
    npEvent.data = 0;

    dispatchNPEvent(npEvent);
}

void PluginView::handleForegroundEvent()
{
    setNPWindowIfNeeded();

    NPEvent npEvent;
    npEvent.type = NP_ForegroundEvent;
    npEvent.data = 0;

    dispatchNPEvent(npEvent);
}

void PluginView::handleFullScreenAllowedEvent()
{
    if (!m_private)
        return;

    NPEvent npEvent;
    npEvent.type = NP_FullScreenReadyEvent;
    npEvent.data = 0;

    if (FrameView* frameView = static_cast<FrameView*>(parent())) {
        frameView->hostWindow()->platformPageClient()->didPluginEnterFullScreen(this, m_private->m_pluginUniquePrefix.c_str());

        if (!dispatchNPEvent(npEvent))
            frameView->hostWindow()->platformPageClient()->didPluginExitFullScreen(this, m_private->m_pluginUniquePrefix.c_str());
        else
            m_private->m_isFullScreen = true;
    }
}

void PluginView::handleFullScreenExitEvent()
{
    if (!m_private)
        return;

    NPEvent npEvent;
    npEvent.type = NP_FullScreenExitEvent;
    npEvent.data = 0;

    dispatchNPEvent(npEvent);

    if (FrameView* frameView = static_cast<FrameView*>(parent()))
        frameView->hostWindow()->platformPageClient()->didPluginExitFullScreen(this, m_private->m_pluginUniquePrefix.c_str());

    m_private->m_isFullScreen = false;
    invalidate();
}

void PluginView::handleIdleEvent(bool enterIdle)
{
    NPEvent npEvent;
    npEvent.data = 0;

    if (enterIdle)
        npEvent.type = NP_EnterIdleEvent;
    else
        npEvent.type = NP_ExitIdleEvent;

    dispatchNPEvent(npEvent);
}


void PluginView::handleAppActivatedEvent()
{
    NPEvent npEvent;
    npEvent.data = 0;
    npEvent.type = NP_AppActivatedEvent;

    dispatchNPEvent(npEvent);
}

void PluginView::handleAppDeactivatedEvent()
{
    if (!m_private)
        return;

    // Plugin wants to know that it has to exit fullscreen on deactivation.
    if (m_private->m_isFullScreen)
        handleFullScreenExitEvent();

    NPEvent npEvent;
    npEvent.data = 0;
    npEvent.type = NP_AppDeactivatedEvent;

    dispatchNPEvent(npEvent);
}

void PluginView::handleAppStandbyEvent()
{
    // FIXME: This should send an QNP_AppStandbyEvent
    NPEvent npEvent;
    npEvent.data = 0;
    npEvent.type = NP_AppStandByEvent;

    dispatchNPEvent(npEvent);
}

void PluginView::handleOrientationEvent(int angle)
{
    NPEvent npEvent;
    npEvent.type = NP_OrientationEvent;
    npEvent.data = (void*)angle;

    dispatchNPEvent(npEvent);
}

void PluginView::handleSwipeEvent()
{
    if (!m_private)
        return;

    // Plugin only wants to know that it has to exit fullscreen.
    if (m_private->m_isFullScreen)
        handleFullScreenExitEvent();
}

void PluginView::handleScreenPowerEvent(bool powered)
{
    NPEvent npEvent;
    npEvent.data = 0;

    if (powered)
        npEvent.type = NP_ScreenPowerUpEvent;
    else
        npEvent.type = NP_ScreenPowerDownEvent;

    dispatchNPEvent(npEvent);
}

void PluginView::setParent(ScrollView* parentWidget)
{
    // If parentWidget is 0, lets unregister the plugin with the current parent.
    if (m_private && (!parentWidget || parentWidget != parent())) {
        if (FrameView* frameView = static_cast<FrameView*>(parent())) {
            if (m_private->m_isBackgroundPlaying)
                frameView->hostWindow()->platformPageClient()->onPluginStopBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());

            if (m_private->m_isFullScreen)
                handleFullScreenExitEvent();

            // This will unlock the idle (if we have locked it).
            m_private->preventIdle(false);

            // This will release any keepVisibleRects if they were set.
            m_private->clearVisibleRects();

#if USE(ACCELERATED_COMPOSITING)
            // If we had a hole punch rect set, clear it.
            if (m_private->m_platformLayer && !m_private->m_holePunchRect.isEmpty())
                m_private->m_platformLayer->setHolePunchRect(IntRect());
#endif
            frameView->hostWindow()->platformPageClient()->registerPlugin(this, false /*shouldRegister*/);
        }
    }

    Widget::setParent(parentWidget);

    if (parentWidget) {
        init();

        FrameView* frameView = static_cast<FrameView*>(parentWidget);

        if (frameView && m_private) {
            frameView->hostWindow()->platformPageClient()->registerPlugin(this, true /*shouldRegister*/);
            if (frameView->frame()
               && frameView->frame()->loader()
               && frameView->frame()->loader()->frameHasLoaded())
                handleOnLoadEvent();

            if (m_private->m_isBackgroundPlaying)
                frameView->hostWindow()->platformPageClient()->onPluginStartBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());
        }
    }
}

void PluginView::setNPWindowRect(const IntRect&)
{
    setNPWindowIfNeeded();
}

void PluginView::setNPWindowIfNeeded()
{
    if (!m_private || !m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
        return;

    FrameView* frameView = static_cast<FrameView*>(parent());
    if (!frameView->hostWindow()->platformPageClient()->isActive())
        return;

    // If the plugin didn't load sucessfully, no point in calling setwindow
    if (m_status != PluginStatusLoadedSuccessfully)
        return;

    if (m_private->m_isFullScreen)
        return;

    if (!m_private->m_hasPendingGeometryChange)
        return;

    m_private->m_hasPendingGeometryChange = false;

    m_npWindow.x = m_windowRect.x();
    m_npWindow.y = m_windowRect.y();

    m_npWindow.clipRect.left = max(0, m_clipRect.x());
    m_npWindow.clipRect.top = max(0, m_clipRect.y());
    m_npWindow.clipRect.right = max(0, m_clipRect.maxX());
    m_npWindow.clipRect.bottom = max(0, m_clipRect.maxY());

    if (m_plugin->quirks().contains(PluginQuirkDontCallSetWindowMoreThanOnce)) {
        // Only set the width and height of the plugin content the first time setNPWindow() is called
        // so as to workaround an issue in Flash where multiple calls to setNPWindow() cause the plugin
        // to crash in windowed mode.
        if (!m_isWindowed || m_npWindow.width == UninitializedCoordinate || m_npWindow.height == UninitializedCoordinate) {
            m_npWindow.width = m_windowRect.width();
            m_npWindow.height = m_windowRect.height();
        }
    } else {
        m_npWindow.width = m_windowRect.width();
        m_npWindow.height = m_windowRect.height();
    }

    m_npWindow.type = NPWindowTypeDrawable;

    BlackBerry::Platform::Graphics::Window* window = frameView->hostWindow()->platformPageClient()->platformWindow();
    if (window)
        ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->windowGroup = window->windowGroup();

    PluginView::setCurrentPluginView(this);
    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonJSGlobalData());
    setCallingPlugin(true);

    // FIXME: Passing zoomFactor to setwindow make windowed plugin scale incorrectly.
    // Handling the zoom factor properly in the plugin side may be a better solution.
    double oldZoomFactor = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor;
    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor = 1.;
    m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor = oldZoomFactor;

    setCallingPlugin(false);
    PluginView::setCurrentPluginView(0);
}

void PluginView::setParentVisible(bool visible)
{
    if (isParentVisible() == visible)
        return;

    Widget::setParentVisible(visible);

    // FIXME: We might want to tell the plugin to hide it's window here, but it doesn't matter
    // since the window manager should take care of that for us.
}

NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf)
{
    String filename(buf, len);

    if (filename.startsWith("file:///"))
        filename = filename.substring(8);

    long long size;
    if (!getFileSize(filename, size))
        return NPERR_FILE_NOT_FOUND;

    FILE* fileHandle = fopen(filename.utf8().data(), "r");
    if (!fileHandle)
        return NPERR_FILE_NOT_FOUND;

    buffer.resize(size);
    int bytesRead = fread(buffer.data(), 1, size, fileHandle);

    fclose(fileHandle);

    if (bytesRead <= 0)
        return NPERR_FILE_NOT_FOUND;

    return NPERR_NO_ERROR;
}

bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result)
{
    switch (variable) {
    case NPNVToolkit:
        *static_cast<uint32_t*>(value) = 0;
        *result = NPERR_NO_ERROR;
        return true;

    case NPNVSupportsXEmbedBool:
        *static_cast<NPBool*>(value) = true;
        *result = NPERR_NO_ERROR;
        return true;

    case NPNVjavascriptEnabledBool:
        *static_cast<NPBool*>(value) = true;
        *result = NPERR_NO_ERROR;
        return true;

    case NPNVSupportsWindowless:
        *static_cast<NPBool*>(value) = true;
        *result = NPERR_NO_ERROR;
        return true;

    case NPNVNPCallbacksPtr:
        *((void **) value) = (void*)&s_NpCallbacks;
        *result = NPERR_NO_ERROR;
        return true;

    case NPNVxDisplay:
    case NPNVxtAppContext:
    case NPNVnetscapeWindow:
    case NPNVasdEnabledBool:
    case NPNVisOfflineBool:
    case NPNVserviceManager:
    case NPNVDOMElement:
    case NPNVDOMWindow:
    case NPNVWindowNPObject:
    case NPNVPluginElementNPObject:
    case NPNVprivateModeBool:
    case NPNVZoomFactor:
    case NPNVRootWindowGroup:
    case NPNVBrowserWindowGroup:
    case NPNVBrowserDisplayContext:
    case NPNVPluginWindowPrefix:
        return false;

    default:
        ASSERT_NOT_REACHED();
        return false;
    }
}

bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result)
{
    switch (variable) {
    case NPNVZoomFactor:
        *(static_cast<void**>(value)) = static_cast<void*>(&((static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info)))->zoomFactor);
        *result = NPERR_NO_ERROR;
        return true;

    case NPNVRootWindowGroup: {
        FrameView* frameView = static_cast<FrameView*>(parent());
        if (frameView) {
            BlackBerry::Platform::Graphics::Window *window = frameView->hostWindow()->platformPageClient()->platformWindow();
            if (window) {
                void** tempValue = static_cast<void**>(value);
                *tempValue = (void*)window->rootGroup();

                if (*tempValue) {
                    *result = NPERR_NO_ERROR;
                    return true;
                }
            }
        }
        *result = NPERR_GENERIC_ERROR;
        return false;
    }

    case NPNVBrowserWindowGroup: {
        FrameView* frameView = static_cast<FrameView*>(parent());
        if (frameView) {
            BlackBerry::Platform::Graphics::Window* window = frameView->hostWindow()->platformPageClient()->platformWindow();
            if (window) {
                void** tempValue = static_cast<void**>(value);
                *tempValue = reinterpret_cast<void*>(const_cast<char*>(window->windowGroup()));

                if (*tempValue) {
                    *result = NPERR_NO_ERROR;
                    return true;
                }
            }
        }
        *result = NPERR_GENERIC_ERROR;
        return false;
    }

    case NPNVBrowserDisplayContext: {
        FrameView* frameView = static_cast<FrameView*>(parent());
        if (frameView) {
            BlackBerry::Platform::Graphics::PlatformDisplayContextHandle context = BlackBerry::Platform::Graphics::platformDisplayContext();
            if (context) {
                void** tempValue = static_cast<void**>(value);
                *tempValue = static_cast<void*>(context);

                if (*tempValue) {
                    *result = NPERR_NO_ERROR;
                    return true;
                }
            }
        }
        *result = NPERR_GENERIC_ERROR;
        return false;
    }

    case NPNVPluginWindowPrefix: {
        void** tempValue = static_cast<void**>(value);
        *tempValue = static_cast<void*>(const_cast<char*>(m_private->m_pluginUniquePrefix.c_str()));

        if (*tempValue) {
            *result = NPERR_NO_ERROR;
            return true;
        }
        *result = NPERR_GENERIC_ERROR;
        return false;
    }

    case NPNVxDisplay:
    case NPNVxtAppContext:
    case NPNVnetscapeWindow:
    case NPNVjavascriptEnabledBool:
    case NPNVasdEnabledBool:
    case NPNVisOfflineBool:
    case NPNVserviceManager:
    case NPNVDOMElement:
    case NPNVDOMWindow:
    case NPNVToolkit:
    case NPNVSupportsXEmbedBool:
    case NPNVWindowNPObject:
    case NPNVPluginElementNPObject:
    case NPNVSupportsWindowless:
    case NPNVprivateModeBool:
    case NPNVNPCallbacksPtr:
        return platformGetValueStatic(variable, value, result);

    default:
        ASSERT_NOT_REACHED();
        return false;
    }
}

// This is a pure virtual inherited from Widget class and all invalidates
// are funneled through this method. We forward them on to either the
// compositing layer or the PluginView::invalidateWindowlessPluginRect method.
void PluginView::invalidateRect(const IntRect& rect)
{
    if (!m_private)
        return;

    // Record the region that we've been asked to invalidate
    m_private->m_invalidateRegion = BlackBerry::Platform::IntRectRegion::unionRegions(BlackBerry::Platform::IntRect(rect), m_private->m_invalidateRegion);

#if USE(ACCELERATED_COMPOSITING)
    if (m_private->m_platformLayer) {
        m_private->m_platformLayer->setNeedsDisplay();
        return;
    }
#endif

    invalidateWindowlessPluginRect(rect);
}

void PluginView::invalidateRect(NPRect* rect)
{
    if (!rect) {
        invalidate();
        return;
    }
    invalidateRect(IntRect(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top));
}

void PluginView::invalidateRegion(NPRegion region)
{
    invalidate();
}

void PluginView::forceRedraw()
{
    invalidate();
}

bool PluginView::platformStart()
{
    ASSERT(m_isStarted);
    ASSERT(m_status == PluginStatusLoadedSuccessfully);

    m_private = new PluginViewPrivate(this);

    if (m_plugin->pluginFuncs()->getvalue) {
        PluginView::setCurrentPluginView(this);
        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonJSGlobalData());
        setCallingPlugin(true);
        m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed);
        setCallingPlugin(false);
        PluginView::setCurrentPluginView(0);
    }

#if USE(ACCELERATED_COMPOSITING)
    if (m_parentFrame->page()->chrome()->client()->allowsAcceleratedCompositing()
        && m_parentFrame->page()->settings()
        && m_parentFrame->page()->settings()->acceleratedCompositingEnabled()) {
        m_private->m_platformLayer = PluginLayerWebKitThread::create(this);
        // Trigger layer computation in RenderLayerCompositor
        m_element->setNeedsStyleRecalc(SyntheticStyleChange);
    }
#endif

    m_npWindow.type = NPWindowTypeDrawable;
    m_npWindow.window = 0; // Not used?
    m_npWindow.x = 0;
    m_npWindow.y = 0;
    m_npWindow.width = UninitializedCoordinate;
    m_npWindow.height = UninitializedCoordinate;
    m_npWindow.ws_info = new NPSetWindowCallbackStruct;
    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor = 1.;
    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->windowGroup = 0;

    show();

    if (FrameView* frameView = static_cast<FrameView*>(parent()))
        handleOrientationEvent(frameView->hostWindow()->platformPageClient()->orientation());

    if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) {
        updatePluginWidget();
        setNPWindowIfNeeded();
    }

    return true;
}

void PluginView::platformDestroy()
{
    if (!m_private)
        return;

    // This will unlock the idle (if we have locked it).
    m_private->preventIdle(false);

    // This will release any keepVisibleRects if they were set.
    m_private->clearVisibleRects();

    // This will ensure that we unregistered the plugin.
    if (FrameView* frameView = static_cast<FrameView*>(parent())) {
        if (m_private->m_isBackgroundPlaying)
            frameView->hostWindow()->platformPageClient()->onPluginStopBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());

        // If we were still fullscreen, ensure we notify everyone we're not.
        if (m_private->m_isFullScreen)
            frameView->hostWindow()->platformPageClient()->didPluginExitFullScreen(this, m_private->m_pluginUniquePrefix.c_str());

        if (m_private->m_orientationLocked)
            frameView->hostWindow()->platformPageClient()->unlockOrientation();

        frameView->hostWindow()->platformPageClient()->registerPlugin(this, false /*shouldRegister*/);
    }

    m_private->m_isBackgroundPlaying = false;
    m_private->m_isFullScreen = false;

    delete m_private;
    m_private = 0;
}

void PluginView::getWindowInfo(Vector<PluginWindowInfo>& windowList)
{
    if (!m_plugin->pluginFuncs()->getvalue)
        return;

    void* valPtr = 0;

    PluginView::setCurrentPluginView(this);
    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonJSGlobalData());
    setCallingPlugin(true);
    m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginScreenWindow, &valPtr);
    setCallingPlugin(false);
    PluginView::setCurrentPluginView(0);

    if (!valPtr)
        return;

    NPScreenWindowHandles* screenWinHandles = static_cast<NPScreenWindowHandles*>(valPtr);

    for (int i = 0; i < screenWinHandles->numOfWindows; i++) {
        PluginWindowInfo info;
        info.windowPtr = screenWinHandles->windowHandles[i];

        NPRect* rc = screenWinHandles->windowRects[i];
        info.windowRect = IntRect(rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top);

        windowList.append(info);
    }
}

BlackBerry::Platform::Graphics::Buffer* PluginView::lockFrontBufferForRead() const
{
    if (!m_private)
        return 0;

    BlackBerry::Platform::Graphics::Buffer* buffer = m_private->lockReadFrontBufferInternal();

    if (!buffer)
        m_private->unlockReadFrontBuffer();

    return buffer;
}

void PluginView::unlockFrontBuffer() const
{
    if (!m_private)
        return;
    m_private->unlockReadFrontBuffer();
}

#if USE(ACCELERATED_COMPOSITING)
PlatformLayer* PluginView::platformLayer() const
{
    if (!m_private)
        return 0;
    return m_private->m_platformLayer.get();
}
#endif

IntRect PluginView::ensureVisibleRect()
{
    if (!m_private)
        return IntRect();
    return m_private->m_keepVisibleRect;
}

void PluginView::setBackgroundPlay(bool value)
{
    if (!m_private || m_private->m_isBackgroundPlaying == value)
        return;

    FrameView* frameView = static_cast<FrameView*>(m_private->m_view->parent());
    m_private->m_isBackgroundPlaying = value;
    if (m_private->m_isBackgroundPlaying)
        frameView->hostWindow()->platformPageClient()->onPluginStartBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());
    else
        frameView->hostWindow()->platformPageClient()->onPluginStopBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());
}

} // namespace WebCore