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 * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "PluginView.h"
31
32#include "DocumentLoader.h"
33#include "Event.h"
34#include "EventNames.h"
35#include "Frame.h"
36#include "FrameView.h"
37#include "HTMLNames.h"
38#include "HTMLPlugInElement.h"
39#include "HostWindow.h"
40#include "JSDOMBinding.h"
41#include "KeyboardEvent.h"
42#include "MouseEvent.h"
43#include "NPCallbacksBlackBerry.h"
44#include "NotImplemented.h"
45#include "Page.h"
46#include "PlatformKeyboardEvent.h"
47#include "PluginDebug.h"
48#include "PluginMainThreadScheduler.h"
49#include "PluginPackage.h"
50#include "PluginViewPrivateBlackBerry.h"
51#include "RenderLayer.h"
52#include "Settings.h"
53#include "TouchEvent.h"
54#include "TouchList.h"
55#include "WheelEvent.h"
56#include "npruntime_impl.h"
57#include "runtime_root.h"
58
59#if USE(ACCELERATED_COMPOSITING)
60#include "Chrome.h"
61#include "ChromeClient.h"
62#include "PluginLayerWebKitThread.h"
63#endif
64
65#include <BlackBerryPlatformGraphics.h>
66#include <BlackBerryPlatformIntRectRegion.h>
67#include <BlackBerryPlatformWindow.h>
68
69#include <runtime/JSCJSValue.h>
70#include <runtime/JSLock.h>
71#include <sys/keycodes.h>
72#include <vector>
73
74const unsigned UninitializedCoordinate = 0xffffffff;
75
76namespace WebCore {
77
78template<class T> static NPRect toNPRect(const T& rect)
79{
80    NPRect npRect;
81    npRect.top = rect.y();
82    npRect.left = rect.x();
83    npRect.bottom = rect.y() + rect.height();
84    npRect.right = rect.x() + rect.width();
85    return npRect;
86}
87
88using JSC::ExecState;
89using JSC::Interpreter;
90using JSC::JSLock;
91using JSC::JSObject;
92
93using namespace std;
94using namespace WTF;
95using namespace HTMLNames;
96
97void PluginView::updatePluginWidget()
98{
99    if (!parent() || !m_private)
100        return;
101
102    ASSERT(parent()->isFrameView());
103    FrameView* frameView = toFrameView(parent());
104
105    IntRect oldWindowRect = m_windowRect;
106    IntRect oldClipRect = m_clipRect;
107
108    m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
109
110    ScrollView* theRoot = root();
111    if (!theRoot)
112        return; // ASSERT(parent()->isFrameView()) should prevent this but check just in case
113    // Map rect to content coordinate space of main frame.
114    m_windowRect.move(theRoot->scrollOffset());
115
116    m_clipRect = calculateClipRect();
117
118    // Notify the plugin if it may or may not be on/offscreen.
119    handleScrollEvent();
120
121    bool zoomFactorChanged = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor
122        != frameView->hostWindow()->platformPageClient()->currentZoomFactor();
123
124    if (!zoomFactorChanged && m_windowRect == oldWindowRect && m_clipRect == oldClipRect)
125        return;
126
127    // Do not call setNPWindowIfNeeded immediately, will be called on paint().
128    m_private->m_hasPendingGeometryChange = true;
129
130    // (i) In order to move/resize the plugin window at the same time as the
131    // rest of frame during e.g. scrolling, we set the window geometry
132    // in the paint() function, but as paint() isn't called when the
133    // plugin window is outside the frame which can be caused by a
134    // scroll, we need to move/resize immediately.
135    // (ii) If we are running layout tests from DRT, paint() won't ever get called
136    // so we need to call setNPWindowIfNeeded() if window geometry has changed.
137    if (m_clipRect.isEmpty() || (platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect || zoomFactorChanged)))
138        setNPWindowIfNeeded();
139
140    // Make sure we get repainted afterwards. This is necessary for downward
141    // scrolling to move the plugin widget properly.
142    invalidate();
143}
144
145void PluginView::setFocus(bool focused)
146{
147    if (!m_private || m_private->m_isFocused == focused)
148        return;
149
150    ASSERT(platformPluginWidget() == platformWidget());
151    Widget::setFocus(focused);
152
153    if (focused)
154        handleFocusInEvent();
155    else
156        handleFocusOutEvent();
157}
158
159void PluginView::show()
160{
161    setSelfVisible(true);
162    Widget::show();
163    updatePluginWidget();
164}
165
166void PluginView::hide()
167{
168    setSelfVisible(false);
169    Widget::hide();
170    updatePluginWidget();
171}
172
173void PluginView::updateBuffer(const IntRect& bufferRect)
174{
175    if (!m_private)
176        return;
177
178    // Update the zoom factor here, it happens right before setNPWindowIfNeeded
179    // ensuring that the plugin has every opportunity to get the zoom factor before
180    // it paints anything.
181    if (FrameView* frameView = toFrameView(parent()))
182        m_private->setZoomFactor(frameView->hostWindow()->platformPageClient()->currentZoomFactor());
183
184    setNPWindowIfNeeded();
185
186    // Build and dispatch an event to the plugin to notify it we are about to draw whatever
187    // is in the front buffer. This is it's opportunity to do a swap.
188    IntRect exposedRect(bufferRect);
189    exposedRect.intersect(IntRect(IntPoint(0, 0), frameRect().size()));
190
191    // Only ask the plugin to draw if the exposed rect was explicitly invalidated
192    // by the plugin.
193    BlackBerry::Platform::IntRectRegion exposedRegion = BlackBerry::Platform::IntRectRegion::intersectRegions(m_private->m_invalidateRegion, BlackBerry::Platform::IntRect(exposedRect));
194    if (!exposedRegion.isEmpty()) {
195        m_private->m_invalidateRegion = BlackBerry::Platform::IntRectRegion::subtractRegions(m_private->m_invalidateRegion, exposedRegion);
196        std::vector<BlackBerry::Platform::IntRect> exposedRects = exposedRegion.rects();
197        for (unsigned i = 0; i < exposedRects.size(); ++i) {
198            NPDrawEvent draw;
199            NPRect tempRect = toNPRect(exposedRects.at(i));
200            draw.pluginRect = toNPRect(m_windowRect);
201            draw.clipRect = toNPRect(m_clipRect);
202            draw.drawRect = &tempRect;
203            draw.drawRectCount = 1;
204            draw.zoomFactor = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor;
205
206            NPEvent npEvent;
207            npEvent.type = NP_DrawEvent;
208            npEvent.data = &draw;
209
210            // FIXME: Return early if this fails? Or just repaint?
211            dispatchNPEvent(npEvent);
212        }
213    }
214}
215
216void PluginView::paint(GraphicsContext* context, const IntRect& rect)
217{
218    if (!m_private || !m_isStarted) {
219        paintMissingPluginIcon(context, rect);
220        return;
221    }
222
223    // Update the zoom factor here, it happens right before setNPWindowIfNeeded
224    // ensuring that the plugin has every opportunity to get the zoom factor before
225    // it paints anything.
226    if (FrameView* frameView = toFrameView(parent()))
227        m_private->setZoomFactor(frameView->hostWindow()->platformPageClient()->currentZoomFactor());
228
229    if (context->paintingDisabled())
230        return;
231
232    setNPWindowIfNeeded();
233
234#if USE(ACCELERATED_COMPOSITING)
235    if (m_private->m_platformLayer)
236        return;
237#endif
238
239    // Build and dispatch an event to the plugin to notify it we are about to draw whatever
240    // is in the front buffer. This is it's opportunity to do a swap.
241    IntRect rectClip(rect);
242    rectClip.intersect(frameRect());
243
244    IntRect exposedRect(rectClip);
245    exposedRect.move(-frameRect().x(), -frameRect().y());
246
247    updateBuffer(exposedRect);
248}
249
250
251bool PluginView::dispatchFullScreenNPEvent(NPEvent& event)
252{
253    if (!m_private)
254        return false;
255
256    ASSERT(m_private->m_isFullScreen);
257    return dispatchNPEvent(event);
258}
259
260// FIXME: Unify across ports.
261bool PluginView::dispatchNPEvent(NPEvent& event)
262{
263    if (!m_plugin || !m_plugin->pluginFuncs()->event)
264        return false;
265
266    PluginView::setCurrentPluginView(this);
267    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
268    setCallingPlugin(true);
269
270    bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
271
272    setCallingPlugin(false);
273    PluginView::setCurrentPluginView(0);
274
275    return accepted;
276}
277
278void PluginView::handleKeyboardEvent(KeyboardEvent* event)
279{
280    NPEvent npEvent;
281    NPKeyboardEvent keyEvent;
282    const PlatformKeyboardEvent *platformKeyEvent = event->keyEvent();
283
284    keyEvent.modifiers = 0;
285
286    if (platformKeyEvent->shiftKey())
287        keyEvent.modifiers |= KEYMOD_SHIFT;
288
289    if (platformKeyEvent->ctrlKey())
290        keyEvent.modifiers |= KEYMOD_CTRL;
291
292    if (platformKeyEvent->altKey())
293        keyEvent.modifiers |= KEYMOD_ALT;
294
295    if (platformKeyEvent->metaKey())
296        keyEvent.modifiers |= KEYMOD_ALTGR;
297
298    keyEvent.cap = platformKeyEvent->unmodifiedCharacter();
299    keyEvent.sym = keyEvent.cap;
300    keyEvent.scan = 0;
301    keyEvent.flags = 0;
302
303    if (platformKeyEvent->type() == PlatformKeyboardEvent::RawKeyDown
304        || platformKeyEvent->type() == PlatformKeyboardEvent::KeyDown) {
305        keyEvent.flags = KEY_DOWN | KEY_SYM_VALID | KEY_CAP_VALID;
306    } else if (platformKeyEvent->type() == PlatformKeyboardEvent::KeyUp)
307        keyEvent.flags = KEY_SYM_VALID | KEY_CAP_VALID;
308
309    npEvent.type = NP_KeyEvent;
310    npEvent.data = &keyEvent;
311    if (dispatchNPEvent(npEvent))
312        event->setDefaultHandled();
313}
314
315void PluginView::handleWheelEvent(WheelEvent* event)
316{
317    if (!m_private)
318        return;
319
320    if (!m_private->m_isFocused)
321        focusPluginElement();
322
323    NPEvent npEvent;
324    NPWheelEvent wheelEvent;
325
326    wheelEvent.x = event->offsetX();
327    wheelEvent.y = event->offsetY();
328
329    wheelEvent.flags = 0;
330
331    wheelEvent.xDelta = event->rawDeltaX();
332    wheelEvent.yDelta = event->rawDeltaY();
333
334    npEvent.type = NP_WheelEvent;
335    npEvent.data = &wheelEvent;
336
337    if (dispatchNPEvent(npEvent))
338        event->setDefaultHandled();
339}
340
341void PluginView::handleTouchEvent(TouchEvent* event)
342{
343    if (!m_private)
344        return;
345
346    if (!m_private->m_isFocused)
347        focusPluginElement();
348
349    NPTouchEvent npTouchEvent;
350
351    if (event->isDoubleTap())
352        npTouchEvent.type = TOUCH_EVENT_DOUBLETAP;
353    else if (event->isTouchHold())
354        npTouchEvent.type = TOUCH_EVENT_TOUCHHOLD;
355    else if (event->type() == eventNames().touchcancelEvent)
356        npTouchEvent.type = TOUCH_EVENT_CANCEL;
357    else
358        return;
359
360    TouchList* touchList;
361    // The touches list is empty if in a touch end event.
362    // Since DoubleTap is ususally a TouchEnd Use changedTouches instead.
363    if (npTouchEvent.type == TOUCH_EVENT_DOUBLETAP)
364        touchList = event->changedTouches();
365    else
366        touchList = event->touches();
367
368    npTouchEvent.points = 0;
369    npTouchEvent.size = touchList->length();
370
371    OwnArrayPtr<NPTouchPoint> touchPoints;
372    if (touchList->length()) {
373        touchPoints = adoptArrayPtr(new NPTouchPoint[touchList->length()]);
374        npTouchEvent.points = touchPoints.get();
375        for (unsigned i = 0; i < touchList->length(); i++) {
376            Touch* touchItem = touchList->item(i);
377            touchPoints[i].touchId = touchItem->identifier();
378            touchPoints[i].clientX = touchItem->pageX() - frameRect().x();
379            touchPoints[i].clientY = touchItem->pageY() - frameRect().y();
380            touchPoints[i].screenX = touchItem->screenX();
381            touchPoints[i].screenY = touchItem->screenY();
382            touchPoints[i].pageX = touchItem->pageX();
383            touchPoints[i].pageY = touchItem->pageY();
384
385        }
386    }
387
388    NPEvent npEvent;
389    npEvent.type = NP_TouchEvent;
390    npEvent.data = &npTouchEvent;
391
392    if (dispatchNPEvent(npEvent))
393        event->setDefaultHandled();
394}
395
396void PluginView::handleMouseEvent(MouseEvent* event)
397{
398    if (!m_private)
399        return;
400
401    if (!m_private->m_isFocused)
402        focusPluginElement();
403
404    NPEvent npEvent;
405    NPMouseEvent mouseEvent;
406
407    mouseEvent.x = event->offsetX();
408    mouseEvent.y = event->offsetY();
409
410    if (event->type() == eventNames().mousedownEvent)
411        mouseEvent.type = MOUSE_BUTTON_DOWN;
412    else if (event->type() == eventNames().mousemoveEvent)
413        mouseEvent.type = MOUSE_MOTION;
414    else if (event->type() == eventNames().mouseoutEvent)
415        mouseEvent.type = MOUSE_OUTBOUND;
416    else if (event->type() == eventNames().mouseoverEvent)
417        mouseEvent.type = MOUSE_OVER;
418    else if (event->type() == eventNames().mouseupEvent)
419        mouseEvent.type = MOUSE_BUTTON_UP;
420    else
421        return;
422
423    mouseEvent.button = event->button();
424    mouseEvent.flags = 0;
425
426    npEvent.type = NP_MouseEvent;
427    npEvent.data = &mouseEvent;
428
429    if (dispatchNPEvent(npEvent))
430        event->setDefaultHandled();
431}
432
433void PluginView::handleFocusInEvent()
434{
435    if (!m_private)
436        return;
437
438    if (m_private->m_isFocused)
439        return;
440
441    m_private->m_isFocused = true;
442
443    NPEvent npEvent;
444    npEvent.type = NP_FocusGainedEvent;
445    npEvent.data = 0;
446    dispatchNPEvent(npEvent);
447}
448
449void PluginView::handleFocusOutEvent()
450{
451    if (!m_private)
452        return;
453
454    if (!m_private->m_isFocused)
455        return;
456
457    m_private->m_isFocused = false;
458
459    NPEvent npEvent;
460    npEvent.type = NP_FocusLostEvent;
461    npEvent.data = 0;
462    dispatchNPEvent(npEvent);
463}
464
465void PluginView::handlePauseEvent()
466{
467    NPEvent npEvent;
468    npEvent.type = NP_PauseEvent;
469    npEvent.data = 0;
470    dispatchNPEvent(npEvent);
471}
472
473void PluginView::handleResumeEvent()
474{
475    NPEvent npEvent;
476    npEvent.type = NP_ResumeEvent;
477    npEvent.data = 0;
478    dispatchNPEvent(npEvent);
479}
480
481void PluginView::handleScrollEvent()
482{
483    FrameView* frameView = toFrameView(parent());
484
485    // As a special case, if the frameView extent in either dimension is
486    // empty, then send an on screen event. This is important for sites like
487    // picnik.com which use a hidden iframe (read: width = 0 and height = 0)
488    // with an embedded swf to execute some javascript. Unless we send an
489    // on screen event the swf will not execute the javascript and the real
490    // site will never load.
491    bool onScreenEvent = frameView && (!frameView->width() || !frameView->height());
492
493    NPEvent npEvent;
494    npEvent.type = m_clipRect.isEmpty() && !onScreenEvent ? NP_OffScreenEvent : NP_OnScreenEvent;
495    npEvent.data = 0;
496    dispatchNPEvent(npEvent);
497}
498
499IntRect PluginView::calculateClipRect() const
500{
501    FrameView* frameView = toFrameView(parent());
502    bool visible = frameView && isVisible();
503
504    if (visible && frameView->width() && frameView->height()) {
505        IntSize windowSize = frameView->hostWindow()->platformPageClient()->viewportSize();
506
507        // Get the clipped rectangle for this player within the current frame.
508        IntRect visibleContentRect;
509        IntRect contentRect = m_element->renderer()->absoluteClippedOverflowRect();
510        FloatPoint contentLocal = m_element->renderer()->absoluteToLocal(FloatPoint(contentRect.location()));
511
512        contentRect.setLocation(roundedIntPoint(contentLocal));
513        contentRect.move(frameRect().x(), frameRect().y());
514
515        // Clip against any frames that the widget is inside. Note that if the frames are also clipped
516        // by a div, that will not be included in this calculation. That is an improvement that still
517        // needs to be made.
518        const Widget* current = this;
519        while (current->parent() && visible) {
520            // Determine if it is visible in this scrollview.
521            visibleContentRect = current->parent()->visibleContentRect();
522
523            // Special case for the root ScrollView. Its size does not match the actual window size.
524            if (current->parent() == root())
525                visibleContentRect.setSize(windowSize);
526
527            contentRect.intersect(visibleContentRect);
528            visible = !contentRect.isEmpty();
529
530            // Offset to visible coordinates in scrollview widget's coordinate system (except in the case of
531            // the top scroll view).
532            if (current->parent()->parent())
533                contentRect.move(-visibleContentRect.x(), -visibleContentRect.y());
534
535            current = current->parent();
536
537            // Don't include the offset for the root window or we get the wrong coordinates.
538            if (current->parent()) {
539                // Move content rect into the parent scrollview's coordinates.
540                IntRect curFrameRect = current->frameRect();
541                contentRect.move(curFrameRect.x(), curFrameRect.y());
542            }
543        }
544
545        return contentRect;
546    }
547
548    return IntRect();
549}
550
551void PluginView::handleOnLoadEvent()
552{
553    if (!m_private)
554        return;
555
556    if (m_private->m_sentOnLoad)
557        return;
558
559    m_private->m_sentOnLoad = true;
560
561    NPEvent npEvent;
562    npEvent.type = NP_OnLoadEvent;
563    npEvent.data = 0;
564
565    dispatchNPEvent(npEvent);
566
567    // Send an initial OnScreen/OffScreen event. It must always come after onLoad is sent.
568    handleScrollEvent();
569}
570
571void PluginView::handleFreeMemoryEvent()
572{
573    NPEvent npEvent;
574    npEvent.type = NP_FreeMemoryEvent;
575    npEvent.data = 0;
576
577    dispatchNPEvent(npEvent);
578}
579
580void PluginView::handleBackgroundEvent()
581{
582    NPEvent npEvent;
583    npEvent.type = NP_BackgroundEvent;
584    npEvent.data = 0;
585
586    dispatchNPEvent(npEvent);
587}
588
589void PluginView::handleForegroundEvent()
590{
591    setNPWindowIfNeeded();
592
593    NPEvent npEvent;
594    npEvent.type = NP_ForegroundEvent;
595    npEvent.data = 0;
596
597    dispatchNPEvent(npEvent);
598}
599
600void PluginView::handleFullScreenAllowedEvent()
601{
602    if (!m_private)
603        return;
604
605    NPEvent npEvent;
606    npEvent.type = NP_FullScreenReadyEvent;
607    npEvent.data = 0;
608
609    if (FrameView* frameView = toFrameView(parent())) {
610        frameView->hostWindow()->platformPageClient()->didPluginEnterFullScreen(this, m_private->m_pluginUniquePrefix.c_str());
611
612        if (!dispatchNPEvent(npEvent))
613            frameView->hostWindow()->platformPageClient()->didPluginExitFullScreen(this, m_private->m_pluginUniquePrefix.c_str());
614        else
615            m_private->m_isFullScreen = true;
616    }
617}
618
619void PluginView::handleFullScreenExitEvent()
620{
621    if (!m_private)
622        return;
623
624    NPEvent npEvent;
625    npEvent.type = NP_FullScreenExitEvent;
626    npEvent.data = 0;
627
628    dispatchNPEvent(npEvent);
629
630    if (FrameView* frameView = toFrameView(parent()))
631        frameView->hostWindow()->platformPageClient()->didPluginExitFullScreen(this, m_private->m_pluginUniquePrefix.c_str());
632
633    m_private->m_isFullScreen = false;
634    invalidate();
635}
636
637void PluginView::handleIdleEvent(bool enterIdle)
638{
639    NPEvent npEvent;
640    npEvent.data = 0;
641
642    if (enterIdle)
643        npEvent.type = NP_EnterIdleEvent;
644    else
645        npEvent.type = NP_ExitIdleEvent;
646
647    dispatchNPEvent(npEvent);
648}
649
650
651void PluginView::handleAppActivatedEvent()
652{
653    NPEvent npEvent;
654    npEvent.data = 0;
655    npEvent.type = NP_AppActivatedEvent;
656
657    dispatchNPEvent(npEvent);
658}
659
660void PluginView::handleAppDeactivatedEvent()
661{
662    if (!m_private)
663        return;
664
665    // Plugin wants to know that it has to exit fullscreen on deactivation.
666    if (m_private->m_isFullScreen)
667        handleFullScreenExitEvent();
668
669    NPEvent npEvent;
670    npEvent.data = 0;
671    npEvent.type = NP_AppDeactivatedEvent;
672
673    dispatchNPEvent(npEvent);
674}
675
676void PluginView::handleAppStandbyEvent()
677{
678    // FIXME: This should send an QNP_AppStandbyEvent
679    NPEvent npEvent;
680    npEvent.data = 0;
681    npEvent.type = NP_AppStandByEvent;
682
683    dispatchNPEvent(npEvent);
684}
685
686void PluginView::handleOrientationEvent(int angle)
687{
688    NPEvent npEvent;
689    npEvent.type = NP_OrientationEvent;
690    npEvent.data = (void*)angle;
691
692    dispatchNPEvent(npEvent);
693}
694
695void PluginView::handleSwipeEvent()
696{
697    if (!m_private)
698        return;
699
700    // Plugin only wants to know that it has to exit fullscreen.
701    if (m_private->m_isFullScreen)
702        handleFullScreenExitEvent();
703}
704
705void PluginView::handleScreenPowerEvent(bool powered)
706{
707    NPEvent npEvent;
708    npEvent.data = 0;
709
710    if (powered)
711        npEvent.type = NP_ScreenPowerUpEvent;
712    else
713        npEvent.type = NP_ScreenPowerDownEvent;
714
715    dispatchNPEvent(npEvent);
716}
717
718void PluginView::setParent(ScrollView* parentWidget)
719{
720    // If parentWidget is 0, lets unregister the plugin with the current parent.
721    if (m_private && (!parentWidget || parentWidget != parent())) {
722        if (FrameView* frameView = toFrameView(parent())) {
723            if (m_private->m_isBackgroundPlaying)
724                frameView->hostWindow()->platformPageClient()->onPluginStopBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());
725
726            if (m_private->m_isFullScreen)
727                handleFullScreenExitEvent();
728
729            // This will unlock the idle (if we have locked it).
730            m_private->preventIdle(false);
731
732            // This will release any keepVisibleRects if they were set.
733            m_private->clearVisibleRects();
734
735#if USE(ACCELERATED_COMPOSITING)
736            // If we had a hole punch rect set, clear it.
737            if (m_private->m_platformLayer && !m_private->m_holePunchRect.isEmpty())
738                m_private->m_platformLayer->setHolePunchRect(IntRect());
739#endif
740            frameView->hostWindow()->platformPageClient()->registerPlugin(this, false /*shouldRegister*/);
741        }
742    }
743
744    Widget::setParent(parentWidget);
745
746    if (parentWidget) {
747        init();
748
749        FrameView* frameView = toFrameView(parentWidget);
750
751        if (frameView && m_private) {
752            frameView->hostWindow()->platformPageClient()->registerPlugin(this, true /*shouldRegister*/);
753            if (frameView->frame()
754               && frameView->frame()->loader()
755               && frameView->frame()->loader()->frameHasLoaded())
756                handleOnLoadEvent();
757
758            if (m_private->m_isBackgroundPlaying)
759                frameView->hostWindow()->platformPageClient()->onPluginStartBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());
760        }
761    }
762}
763
764void PluginView::setNPWindowRect(const IntRect&)
765{
766    setNPWindowIfNeeded();
767}
768
769void PluginView::setNPWindowIfNeeded()
770{
771    if (!m_private || !m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
772        return;
773
774    FrameView* frameView = toFrameView(parent());
775    if (!frameView->hostWindow()->platformPageClient()->isActive())
776        return;
777
778    // If the plugin didn't load sucessfully, no point in calling setwindow
779    if (m_status != PluginStatusLoadedSuccessfully)
780        return;
781
782    if (m_private->m_isFullScreen)
783        return;
784
785    if (!m_private->m_hasPendingGeometryChange)
786        return;
787
788    m_private->m_hasPendingGeometryChange = false;
789
790    m_npWindow.x = m_windowRect.x();
791    m_npWindow.y = m_windowRect.y();
792
793    m_npWindow.clipRect.left = max(0, m_clipRect.x());
794    m_npWindow.clipRect.top = max(0, m_clipRect.y());
795    m_npWindow.clipRect.right = max(0, m_clipRect.maxX());
796    m_npWindow.clipRect.bottom = max(0, m_clipRect.maxY());
797
798    if (m_plugin->quirks().contains(PluginQuirkDontCallSetWindowMoreThanOnce)) {
799        // Only set the width and height of the plugin content the first time setNPWindow() is called
800        // so as to workaround an issue in Flash where multiple calls to setNPWindow() cause the plugin
801        // to crash in windowed mode.
802        if (!m_isWindowed || m_npWindow.width == UninitializedCoordinate || m_npWindow.height == UninitializedCoordinate) {
803            m_npWindow.width = m_windowRect.width();
804            m_npWindow.height = m_windowRect.height();
805        }
806    } else {
807        m_npWindow.width = m_windowRect.width();
808        m_npWindow.height = m_windowRect.height();
809    }
810
811    m_npWindow.type = NPWindowTypeDrawable;
812
813    BlackBerry::Platform::Graphics::Window* window = frameView->hostWindow()->platformPageClient()->platformWindow();
814    if (window)
815        ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->windowGroup = window->windowGroup();
816
817    PluginView::setCurrentPluginView(this);
818    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
819    setCallingPlugin(true);
820
821    // FIXME: Passing zoomFactor to setwindow make windowed plugin scale incorrectly.
822    // Handling the zoom factor properly in the plugin side may be a better solution.
823    double oldZoomFactor = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor;
824    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor = 1.;
825    m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
826    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor = oldZoomFactor;
827
828    setCallingPlugin(false);
829    PluginView::setCurrentPluginView(0);
830}
831
832void PluginView::setParentVisible(bool visible)
833{
834    if (isParentVisible() == visible)
835        return;
836
837    Widget::setParentVisible(visible);
838
839    // FIXME: We might want to tell the plugin to hide it's window here, but it doesn't matter
840    // since the window manager should take care of that for us.
841}
842
843NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf)
844{
845    String filename(buf, len);
846
847    if (filename.startsWith("file:///"))
848        filename = filename.substring(8);
849
850    long long size;
851    if (!getFileSize(filename, size))
852        return NPERR_FILE_NOT_FOUND;
853
854    FILE* fileHandle = fopen(filename.utf8().data(), "r");
855    if (!fileHandle)
856        return NPERR_FILE_NOT_FOUND;
857
858    buffer.resize(size);
859    int bytesRead = fread(buffer.data(), 1, size, fileHandle);
860
861    fclose(fileHandle);
862
863    if (bytesRead <= 0)
864        return NPERR_FILE_NOT_FOUND;
865
866    return NPERR_NO_ERROR;
867}
868
869bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result)
870{
871    switch (variable) {
872    case NPNVToolkit:
873        *static_cast<uint32_t*>(value) = 0;
874        *result = NPERR_NO_ERROR;
875        return true;
876
877    case NPNVSupportsXEmbedBool:
878        *static_cast<NPBool*>(value) = true;
879        *result = NPERR_NO_ERROR;
880        return true;
881
882    case NPNVjavascriptEnabledBool:
883        *static_cast<NPBool*>(value) = true;
884        *result = NPERR_NO_ERROR;
885        return true;
886
887    case NPNVSupportsWindowless:
888        *static_cast<NPBool*>(value) = true;
889        *result = NPERR_NO_ERROR;
890        return true;
891
892    case NPNVNPCallbacksPtr:
893        *((void **) value) = (void*)&s_NpCallbacks;
894        *result = NPERR_NO_ERROR;
895        return true;
896
897    case NPNVxDisplay:
898    case NPNVxtAppContext:
899    case NPNVnetscapeWindow:
900    case NPNVasdEnabledBool:
901    case NPNVisOfflineBool:
902    case NPNVserviceManager:
903    case NPNVDOMElement:
904    case NPNVDOMWindow:
905    case NPNVWindowNPObject:
906    case NPNVPluginElementNPObject:
907    case NPNVprivateModeBool:
908    case NPNVZoomFactor:
909    case NPNVRootWindowGroup:
910    case NPNVBrowserWindowGroup:
911    case NPNVBrowserDisplayContext:
912    case NPNVPluginWindowPrefix:
913        return false;
914
915    default:
916        ASSERT_NOT_REACHED();
917        return false;
918    }
919}
920
921bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result)
922{
923    switch (variable) {
924    case NPNVZoomFactor:
925        *(static_cast<void**>(value)) = static_cast<void*>(&((static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info)))->zoomFactor);
926        *result = NPERR_NO_ERROR;
927        return true;
928
929    case NPNVRootWindowGroup: {
930        FrameView* frameView = toFrameView(parent());
931        if (frameView) {
932            BlackBerry::Platform::Graphics::Window *window = frameView->hostWindow()->platformPageClient()->platformWindow();
933            if (window) {
934                void** tempValue = static_cast<void**>(value);
935                *tempValue = (void*)window->rootGroup();
936
937                if (*tempValue) {
938                    *result = NPERR_NO_ERROR;
939                    return true;
940                }
941            }
942        }
943        *result = NPERR_GENERIC_ERROR;
944        return false;
945    }
946
947    case NPNVBrowserWindowGroup: {
948        FrameView* frameView = toFrameView(parent());
949        if (frameView) {
950            BlackBerry::Platform::Graphics::Window* window = frameView->hostWindow()->platformPageClient()->platformWindow();
951            if (window) {
952                void** tempValue = static_cast<void**>(value);
953                *tempValue = reinterpret_cast<void*>(const_cast<char*>(window->windowGroup()));
954
955                if (*tempValue) {
956                    *result = NPERR_NO_ERROR;
957                    return true;
958                }
959            }
960        }
961        *result = NPERR_GENERIC_ERROR;
962        return false;
963    }
964
965    case NPNVBrowserDisplayContext: {
966        FrameView* frameView = toFrameView(parent());
967        if (frameView) {
968            BlackBerry::Platform::Graphics::PlatformDisplayContextHandle context = BlackBerry::Platform::Graphics::platformDisplayContext();
969            if (context) {
970                void** tempValue = static_cast<void**>(value);
971                *tempValue = static_cast<void*>(context);
972
973                if (*tempValue) {
974                    *result = NPERR_NO_ERROR;
975                    return true;
976                }
977            }
978        }
979        *result = NPERR_GENERIC_ERROR;
980        return false;
981    }
982
983    case NPNVPluginWindowPrefix: {
984        void** tempValue = static_cast<void**>(value);
985        *tempValue = static_cast<void*>(const_cast<char*>(m_private->m_pluginUniquePrefix.c_str()));
986
987        if (*tempValue) {
988            *result = NPERR_NO_ERROR;
989            return true;
990        }
991        *result = NPERR_GENERIC_ERROR;
992        return false;
993    }
994
995    case NPNVxDisplay:
996    case NPNVxtAppContext:
997    case NPNVnetscapeWindow:
998    case NPNVjavascriptEnabledBool:
999    case NPNVasdEnabledBool:
1000    case NPNVisOfflineBool:
1001    case NPNVserviceManager:
1002    case NPNVDOMElement:
1003    case NPNVDOMWindow:
1004    case NPNVToolkit:
1005    case NPNVSupportsXEmbedBool:
1006    case NPNVWindowNPObject:
1007    case NPNVPluginElementNPObject:
1008    case NPNVSupportsWindowless:
1009    case NPNVprivateModeBool:
1010    case NPNVNPCallbacksPtr:
1011        return platformGetValueStatic(variable, value, result);
1012
1013    default:
1014        ASSERT_NOT_REACHED();
1015        return false;
1016    }
1017}
1018
1019// This is a pure virtual inherited from Widget class and all invalidates
1020// are funneled through this method. We forward them on to either the
1021// compositing layer or the PluginView::invalidateWindowlessPluginRect method.
1022void PluginView::invalidateRect(const IntRect& rect)
1023{
1024    if (!m_private)
1025        return;
1026
1027    // Record the region that we've been asked to invalidate
1028    m_private->m_invalidateRegion = BlackBerry::Platform::IntRectRegion::unionRegions(BlackBerry::Platform::IntRect(rect), m_private->m_invalidateRegion);
1029
1030#if USE(ACCELERATED_COMPOSITING)
1031    if (m_private->m_platformLayer) {
1032        m_private->m_platformLayer->setNeedsDisplay();
1033        return;
1034    }
1035#endif
1036
1037    invalidateWindowlessPluginRect(rect);
1038}
1039
1040void PluginView::invalidateRect(NPRect* rect)
1041{
1042    if (!rect) {
1043        invalidate();
1044        return;
1045    }
1046    invalidateRect(IntRect(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top));
1047}
1048
1049void PluginView::invalidateRegion(NPRegion)
1050{
1051    invalidate();
1052}
1053
1054void PluginView::forceRedraw()
1055{
1056    invalidate();
1057}
1058
1059bool PluginView::platformStart()
1060{
1061    ASSERT(m_isStarted);
1062    ASSERT(m_status == PluginStatusLoadedSuccessfully);
1063
1064    m_private = new PluginViewPrivate(this);
1065
1066    if (m_plugin->pluginFuncs()->getvalue) {
1067        PluginView::setCurrentPluginView(this);
1068        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
1069        setCallingPlugin(true);
1070        m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed);
1071        setCallingPlugin(false);
1072        PluginView::setCurrentPluginView(0);
1073    }
1074
1075#if USE(ACCELERATED_COMPOSITING)
1076    if (m_parentFrame->page()->chrome().client()->allowsAcceleratedCompositing()
1077        && m_parentFrame->page()->settings()
1078        && m_parentFrame->page()->settings()->acceleratedCompositingEnabled()) {
1079        m_private->m_platformLayer = PluginLayerWebKitThread::create(this);
1080        // Trigger layer computation in RenderLayerCompositor
1081        m_element->setNeedsStyleRecalc(SyntheticStyleChange);
1082    }
1083#endif
1084
1085    m_npWindow.type = NPWindowTypeDrawable;
1086    m_npWindow.window = 0; // Not used?
1087    m_npWindow.x = 0;
1088    m_npWindow.y = 0;
1089    m_npWindow.width = UninitializedCoordinate;
1090    m_npWindow.height = UninitializedCoordinate;
1091    m_npWindow.ws_info = new NPSetWindowCallbackStruct;
1092    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->zoomFactor = 1.;
1093    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->windowGroup = 0;
1094
1095    show();
1096
1097    if (FrameView* frameView = toFrameView(parent()))
1098        handleOrientationEvent(frameView->hostWindow()->platformPageClient()->orientation());
1099
1100    if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) {
1101        updatePluginWidget();
1102        setNPWindowIfNeeded();
1103    }
1104
1105    return true;
1106}
1107
1108void PluginView::platformDestroy()
1109{
1110    if (!m_private)
1111        return;
1112
1113    // This will unlock the idle (if we have locked it).
1114    m_private->preventIdle(false);
1115
1116    // This will release any keepVisibleRects if they were set.
1117    m_private->clearVisibleRects();
1118
1119    // This will ensure that we unregistered the plugin.
1120    if (FrameView* frameView = toFrameView(parent())) {
1121        if (m_private->m_isBackgroundPlaying)
1122            frameView->hostWindow()->platformPageClient()->onPluginStopBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());
1123
1124        // If we were still fullscreen, ensure we notify everyone we're not.
1125        if (m_private->m_isFullScreen)
1126            frameView->hostWindow()->platformPageClient()->didPluginExitFullScreen(this, m_private->m_pluginUniquePrefix.c_str());
1127
1128        if (m_private->m_orientationLocked)
1129            frameView->hostWindow()->platformPageClient()->unlockOrientation();
1130
1131        frameView->hostWindow()->platformPageClient()->registerPlugin(this, false /*shouldRegister*/);
1132    }
1133
1134    m_private->m_isBackgroundPlaying = false;
1135    m_private->m_isFullScreen = false;
1136
1137    delete m_private;
1138    m_private = 0;
1139}
1140
1141void PluginView::getWindowInfo(Vector<PluginWindowInfo>& windowList)
1142{
1143    if (!m_plugin->pluginFuncs()->getvalue)
1144        return;
1145
1146    void* valPtr = 0;
1147
1148    PluginView::setCurrentPluginView(this);
1149    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
1150    setCallingPlugin(true);
1151    m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginScreenWindow, &valPtr);
1152    setCallingPlugin(false);
1153    PluginView::setCurrentPluginView(0);
1154
1155    if (!valPtr)
1156        return;
1157
1158    NPScreenWindowHandles* screenWinHandles = static_cast<NPScreenWindowHandles*>(valPtr);
1159
1160    for (int i = 0; i < screenWinHandles->numOfWindows; i++) {
1161        PluginWindowInfo info;
1162        info.windowPtr = screenWinHandles->windowHandles[i];
1163
1164        NPRect* rc = screenWinHandles->windowRects[i];
1165        info.windowRect = IntRect(rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top);
1166
1167        windowList.append(info);
1168    }
1169}
1170
1171BlackBerry::Platform::Graphics::Buffer* PluginView::lockFrontBufferForRead() const
1172{
1173    if (!m_private)
1174        return 0;
1175
1176    BlackBerry::Platform::Graphics::Buffer* buffer = m_private->lockReadFrontBufferInternal();
1177
1178    if (!buffer)
1179        m_private->unlockReadFrontBuffer();
1180
1181    return buffer;
1182}
1183
1184void PluginView::unlockFrontBuffer() const
1185{
1186    if (!m_private)
1187        return;
1188    m_private->unlockReadFrontBuffer();
1189}
1190
1191#if USE(ACCELERATED_COMPOSITING)
1192PlatformLayer* PluginView::platformLayer() const
1193{
1194    if (!m_private)
1195        return 0;
1196    return m_private->m_platformLayer.get();
1197}
1198#endif
1199
1200IntRect PluginView::ensureVisibleRect()
1201{
1202    if (!m_private)
1203        return IntRect();
1204    return m_private->m_keepVisibleRect;
1205}
1206
1207void PluginView::setBackgroundPlay(bool value)
1208{
1209    if (!m_private || m_private->m_isBackgroundPlaying == value)
1210        return;
1211
1212    FrameView* frameView = toFrameView(m_private->m_view->parent());
1213    m_private->m_isBackgroundPlaying = value;
1214    if (m_private->m_isBackgroundPlaying)
1215        frameView->hostWindow()->platformPageClient()->onPluginStartBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());
1216    else
1217        frameView->hostWindow()->platformPageClient()->onPluginStopBackgroundPlay(this, m_private->m_pluginUniquePrefix.c_str());
1218}
1219
1220} // namespace WebCore
1221
1222
1223