1/*
2 * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include "config.h"
20#include "PluginViewPrivateBlackBerry.h"
21
22#include "FrameView.h"
23#include "HostWindow.h"
24#if USE(ACCELERATED_COMPOSITING)
25#include "PluginLayerWebKitThread.h"
26#endif
27#include "NPCallbacksBlackBerry.h"
28#include <BlackBerryPlatformExecutableMessage.h>
29#include <BlackBerryPlatformMessageClient.h>
30#include <wtf/MainThread.h>
31
32static unsigned s_counter = 0;
33
34namespace WebCore {
35
36PluginViewPrivate::PluginViewPrivate(PluginView* view)
37    : m_view(view)
38    , m_pluginBufferType(BlackBerry::Platform::Graphics::PluginBufferWithAlpha)
39    , m_pluginFrontBuffer(0)
40    , m_idlePrevented(false)
41    , m_hasPendingGeometryChange(true)
42    , m_sentOnLoad(false)
43    , m_isFullScreen(false)
44    , m_isFocused(false)
45    , m_orientationLocked(false)
46    , m_isBackgroundPlaying(false)
47{
48    m_pluginBuffers[0] = 0;
49    m_pluginBuffers[1] = 0;
50
51    pthread_mutexattr_t mutexAttributes;
52    pthread_mutexattr_init(&mutexAttributes);
53    pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE);
54
55    pthread_mutex_init(&m_backBufferMutex, &mutexAttributes);
56    pthread_rwlock_init(&m_frontBufferRwLock, 0);
57
58    pthread_mutexattr_destroy(&mutexAttributes);
59
60    char uniqueId[50];
61    snprintf(uniqueId, sizeof(uniqueId), "PluginViewBB-%08x%08x-", s_counter++, (int)this);
62    m_pluginUniquePrefix = uniqueId;
63}
64
65PluginViewPrivate::~PluginViewPrivate()
66{
67#if USE(ACCELERATED_COMPOSITING)
68        if (m_platformLayer)
69            static_cast<PluginLayerWebKitThread*>(m_platformLayer.get())->setPluginView(0);
70#endif
71
72    destroyBuffers();
73
74    pthread_mutex_destroy(&m_backBufferMutex);
75    pthread_rwlock_destroy(&m_frontBufferRwLock);
76}
77
78BlackBerry::Platform::Graphics::BufferType PluginViewPrivate::toBufferType(NPSurfaceFormat format)
79{
80    switch (format) {
81    case FORMAT_RGB_565:
82        return BlackBerry::Platform::Graphics::PluginBuffer;
83    case FORMAT_RGBA_8888:
84    default:
85        return BlackBerry::Platform::Graphics::PluginBufferWithAlpha;
86    }
87}
88
89void PluginViewPrivate::setZoomFactor(float zoomFactor)
90{
91    if (((NPSetWindowCallbackStruct*)m_view->m_npWindow.ws_info)->zoomFactor != zoomFactor)
92        m_hasPendingGeometryChange = true;
93
94    ((NPSetWindowCallbackStruct*)m_view->m_npWindow.ws_info)->zoomFactor = zoomFactor;
95}
96
97void PluginViewPrivate::setVisibleRects(const NPRect rects[], int32_t count)
98{
99    m_keepVisibleRect = IntRect();
100
101    if (!m_view->parent() || !count)
102        return;
103
104    for (int i = 0; i < count; i++) {
105        IntRect addRect = IntRect(rects[i].left, rects[i].top, rects[i].right - rects[i].left, rects[i].bottom - rects[i].top);
106        m_keepVisibleRect.unite(addRect);
107    }
108
109    // Don't cause a possible scroll if the result is an empty rectangle.
110    if (m_keepVisibleRect.isEmpty())
111        return;
112
113    // Adjust the rect to the parent window and then adjust for scrolling.
114    m_keepVisibleRect = m_view->convertToContainingWindow(m_keepVisibleRect);
115    FrameView* frameView = toFrameView(m_view->parent());
116    m_keepVisibleRect.move(frameView->scrollPosition().x(), frameView->scrollPosition().y());
117
118    frameView->hostWindow()->platformPageClient()->ensureContentVisible();
119}
120
121void PluginViewPrivate::clearVisibleRects()
122{
123    if (!m_keepVisibleRect.isEmpty())
124        setVisibleRects(0, 0);
125}
126
127void PluginViewPrivate::showKeyboard(bool value)
128{
129    FrameView* frameView = toFrameView(m_view->parent());
130    frameView->hostWindow()->platformPageClient()->showVirtualKeyboard(value);
131}
132
133void PluginViewPrivate::requestFullScreen()
134{
135    if (FrameView* frameView = toFrameView(m_view->parent()))
136        if (frameView->hostWindow()->platformPageClient()->shouldPluginEnterFullScreen(m_view, m_pluginUniquePrefix.c_str()))
137            m_view->handleFullScreenAllowedEvent();
138}
139
140void PluginViewPrivate::exitFullScreen()
141{
142    m_view->handleFullScreenExitEvent();
143}
144
145void PluginViewPrivate::requestCenterFitZoom()
146{
147    FrameView* frameView = toFrameView(m_view->parent());
148
149    if (!frameView)
150        return;
151
152    frameView->hostWindow()->platformPageClient()->zoomToContentRect(m_view->m_windowRect);
153}
154
155void PluginViewPrivate::lockOrientation(bool landscape)
156{
157    FrameView* frameView = toFrameView(m_view->parent());
158
159    if (!frameView)
160        return;
161
162    frameView->hostWindow()->platformPageClient()->lockOrientation(landscape);
163    m_orientationLocked = true;
164}
165
166void PluginViewPrivate::unlockOrientation()
167{
168    if (!m_orientationLocked)
169        return;
170
171    FrameView* frameView = toFrameView(m_view->parent());
172
173    if (!frameView)
174        return;
175
176    frameView->hostWindow()->platformPageClient()->unlockOrientation();
177    m_orientationLocked = false;
178}
179
180void PluginViewPrivate::preventIdle(bool preventIdle)
181{
182    if (preventIdle == m_idlePrevented)
183        return;
184
185    FrameView* frameView = toFrameView(m_view->parent());
186    if (!frameView)
187        return;
188
189    frameView->hostWindow()->platformPageClient()->setPreventsScreenDimming(preventIdle);
190    m_idlePrevented = preventIdle;
191}
192
193void PluginViewPrivate::setHolePunch(int x, int y, int width, int height)
194{
195    if (width > 0 && height > 0)
196        m_holePunchRect = IntRect(x, y, width, height);
197    else
198        m_holePunchRect = IntRect();
199
200    // Clip the hole punch rectangle in case a plugin is 'overzealous', or
201    // does not clean up the hole punch rectangle correctly when exiting
202    // fullscreen (will mask bugs in the plugin).
203    m_holePunchRect.intersect(IntRect(IntPoint(0, 0), m_view->frameRect().size()));
204
205#if USE(ACCELERATED_COMPOSITING)
206    if (m_platformLayer) {
207        IntRect rect = m_holePunchRect;
208
209        // Translate from plugin coordinates to contents coordinates.
210        if (!m_holePunchRect.isEmpty())
211            rect.move(m_view->frameRect().x(), m_view->frameRect().y());
212
213        OwnPtr<HolePunchData> hp = adoptPtr(new HolePunchData);
214        hp->x = m_holePunchRect.x();
215        hp->y = m_holePunchRect.y();
216        hp->w = m_holePunchRect.width();
217        hp->h = m_holePunchRect.height();
218        hp->layer = m_platformLayer;
219
220        // Notify compositing layer and page client in order to be able to
221        // punch through composited and non-composited parts of the page.
222        callOnMainThread(npSetHolePunchHandler, hp.leakPtr()); // npSetHolePunchHandler() takes ownership of hp.
223    }
224#endif
225}
226
227NPSurface PluginViewPrivate::lockBackBuffer()
228{
229    pthread_mutex_lock(&m_backBufferMutex);
230    BlackBerry::Platform::Graphics::Buffer* buffer = m_pluginBuffers[(m_pluginFrontBuffer + 1) % PLUGIN_BUFFERS];
231
232    if (!buffer || !BlackBerry::Platform::Graphics::platformBufferHandle(buffer)) {
233        unlockBackBuffer();
234        return 0;
235    }
236
237    return BlackBerry::Platform::Graphics::platformBufferHandle(buffer);
238}
239
240void PluginViewPrivate::unlockBackBuffer()
241{
242    pthread_mutex_unlock(&m_backBufferMutex);
243}
244
245BlackBerry::Platform::Graphics::Buffer* PluginViewPrivate::lockReadFrontBufferInternal()
246{
247    pthread_rwlock_rdlock(&m_frontBufferRwLock);
248    return m_pluginBuffers[m_pluginFrontBuffer];
249}
250
251NPSurface PluginViewPrivate::lockReadFrontBuffer()
252{
253    BlackBerry::Platform::Graphics::Buffer* buffer = lockReadFrontBufferInternal();
254
255    if (!buffer || !BlackBerry::Platform::Graphics::platformBufferHandle(buffer)) {
256        unlockReadFrontBuffer();
257        return 0;
258    }
259
260    return BlackBerry::Platform::Graphics::platformBufferHandle(buffer);
261}
262
263void PluginViewPrivate::unlockReadFrontBuffer()
264{
265    pthread_rwlock_unlock(&m_frontBufferRwLock);
266}
267
268void PluginViewPrivate::swapBuffers()
269{
270    PthreadMutexLocker backLock(&m_backBufferMutex);
271    PthreadWriteLocker frontLock(&m_frontBufferRwLock);
272    m_pluginFrontBuffer = (m_pluginFrontBuffer + 1) % 2;
273}
274
275bool PluginViewPrivate::createBuffers(NPSurfaceFormat format, int width, int height)
276{
277    bool success = true;
278
279    PthreadMutexLocker backLock(&m_backBufferMutex);
280    PthreadWriteLocker frontLock(&m_frontBufferRwLock);
281
282    bool didDestroyBuffers = false;
283    for (int i = 0; i < PLUGIN_BUFFERS; i++) {
284        if (m_pluginBuffers[i]) {
285            didDestroyBuffers = true;
286            BlackBerry::Platform::Graphics::destroyBuffer(m_pluginBuffers[i]);
287            m_pluginBuffers[i] = 0;
288        }
289
290        if (width <= 0 || height <= 0)
291            success = true;
292        else {
293            m_pluginBuffers[i] = BlackBerry::Platform::Graphics::createBuffer(
294                BlackBerry::Platform::IntSize(width, height),
295                toBufferType(format));
296
297            if (!m_pluginBuffers[i])
298                success = false;
299        }
300    }
301
302    if (success) {
303        m_pluginBufferSize = IntSize(width, height);
304        m_pluginBufferType = toBufferType(format);
305
306        if (didDestroyBuffers) {
307            BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage(
308                BlackBerry::Platform::createFunctionCallMessage(&BlackBerry::Platform::Graphics::collectThreadSpecificGarbage));
309        }
310    } else {
311        m_pluginBufferSize = IntSize();
312        m_pluginBufferType = BlackBerry::Platform::Graphics::PluginBufferWithAlpha;
313        destroyBuffers();
314    }
315
316    return success;
317}
318
319bool PluginViewPrivate::resizeBuffers(NPSurfaceFormat format, int width, int height)
320{
321    bool success = true;
322
323    // If there is no buffer created, then try to create it.
324    if (!m_pluginBufferSize.width() || !m_pluginBufferSize.height())
325        return createBuffers(format, width, height);
326
327    if (!width || !height)
328        return destroyBuffers();
329
330    PthreadMutexLocker backLock(&m_backBufferMutex);
331    PthreadWriteLocker frontLock(&m_frontBufferRwLock);
332
333    for (int i = 0; i < PLUGIN_BUFFERS && success; i++) {
334        success &= BlackBerry::Platform::Graphics::reallocBuffer(m_pluginBuffers[i],
335            BlackBerry::Platform::IntSize(width, height),
336            toBufferType(format));
337    }
338
339    if (success) {
340        m_pluginBufferSize = IntSize(width, height);
341        m_pluginBufferType = toBufferType(format);
342        return true;
343    }
344
345    // Attempt to undo if we failed to get the new size/format. We can't guarantee
346    // we will get it back though.
347    if (!m_pluginBufferSize.width() || !m_pluginBufferSize.height()) {
348        destroyBuffers();
349        return false;
350    }
351
352    bool undone = true;
353    for (int i = 0; i < PLUGIN_BUFFERS; i++) {
354        undone &= BlackBerry::Platform::Graphics::reallocBuffer(m_pluginBuffers[i],
355            m_pluginBufferSize, m_pluginBufferType);
356    }
357
358    // If we fail to undo, delete the buffers altogether.
359    if (!undone)
360        destroyBuffers();
361
362    return false;
363}
364
365bool PluginViewPrivate::destroyBuffers()
366{
367    bool didDestroyBuffers = false;
368
369    {
370        PthreadMutexLocker backLock(&m_backBufferMutex);
371        PthreadWriteLocker frontLock(&m_frontBufferRwLock);
372
373        for (int i = 0; i < PLUGIN_BUFFERS; i++) {
374            if (m_pluginBuffers[i]) {
375                didDestroyBuffers = true;
376                BlackBerry::Platform::Graphics::destroyBuffer(m_pluginBuffers[i]);
377                m_pluginBuffers[i] = 0;
378            }
379        }
380        m_pluginBufferSize = IntSize();
381    }
382
383    if (didDestroyBuffers) {
384        BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage(
385            BlackBerry::Platform::createFunctionCallMessage(&BlackBerry::Platform::Graphics::collectThreadSpecificGarbage));
386    }
387
388    return true;
389}
390
391} // namespace WebCore
392
393
394