1/*
2 * Copyright 2008, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "PluginWidgetAndroid.h"
28
29#if ENABLE(TOUCH_EVENTS)
30#include "ChromeClient.h"
31#endif
32#include "Document.h"
33#include "Element.h"
34#include "Frame.h"
35#include "Page.h"
36#include "PluginPackage.h"
37#include "PluginView.h"
38#include "PluginWidgetAndroid.h"
39#include "ScrollView.h"
40#include "SkANP.h"
41#include "SkFlipPixelRef.h"
42#include "SkString.h"
43#include "SkTime.h"
44#include "WebViewCore.h"
45#include <JNIUtility.h>
46
47//#define PLUGIN_DEBUG_LOCAL // controls the printing of log messages
48#define DEBUG_EVENTS 0 // logs event contents, return value, and processing time
49#define DEBUG_VISIBLE_RECTS 0 // temporary debug printfs and fixes
50
51#define MAX( a, b ) ( ((a) > (b)) ? (a) : (b) )
52
53// this include statement must follow the declaration of PLUGIN_DEBUG_LOCAL
54#include "PluginDebugAndroid.h"
55
56PluginWidgetAndroid::PluginWidgetAndroid(WebCore::PluginView* view)
57        : m_pluginView(view) {
58    m_flipPixelRef = NULL;
59    m_core = NULL;
60    m_drawingModel = kBitmap_ANPDrawingModel;
61    m_eventFlags = 0;
62    m_pluginWindow = NULL;
63    m_requestedVisibleRectCount = 0;
64    m_requestedVisibleRect.setEmpty();
65    m_visibleDocRect.setEmpty();
66    m_pluginBounds.setEmpty();
67    m_hasFocus = false;
68    m_isFullScreen = false;
69    m_visible = false;
70    m_cachedZoomLevel = 0;
71    m_embeddedView = NULL;
72    m_embeddedViewAttached = false;
73    m_acceptEvents = false;
74    m_isSurfaceClippedOut = false;
75    m_layer = 0;
76    m_powerState = kDefault_ANPPowerState;
77    m_fullScreenOrientation = -1;
78    m_drawEventDelayed = false;
79}
80
81PluginWidgetAndroid::~PluginWidgetAndroid() {
82    PLUGIN_LOG("%p Deleting Plugin", m_pluginView->instance());
83    m_acceptEvents = false;
84    if (m_core) {
85        setPowerState(kDefault_ANPPowerState);
86        m_core->removePlugin(this);
87        if (m_isFullScreen) {
88            exitFullScreen(true);
89        }
90        if (m_embeddedView) {
91            m_core->destroySurface(m_embeddedView);
92        }
93    }
94
95    // cleanup any remaining JNI References
96    JNIEnv* env = JSC::Bindings::getJNIEnv();
97    if (m_embeddedView) {
98        env->DeleteGlobalRef(m_embeddedView);
99    }
100
101    SkSafeUnref(m_flipPixelRef);
102    SkSafeUnref(m_layer);
103}
104
105void PluginWidgetAndroid::init(android::WebViewCore* core) {
106    m_core = core;
107    m_core->addPlugin(this);
108    m_acceptEvents = true;
109    PLUGIN_LOG("%p Initialized Plugin", m_pluginView->instance());
110}
111
112static SkBitmap::Config computeConfig(bool isTransparent) {
113    return isTransparent ? SkBitmap::kARGB_8888_Config
114                         : SkBitmap::kRGB_565_Config;
115}
116
117void PluginWidgetAndroid::setWindow(NPWindow* window, bool isTransparent) {
118
119    // store the reference locally for easy lookup
120    m_pluginWindow = window;
121
122    // make a copy of the previous bounds
123    SkIRect oldPluginBounds = m_pluginBounds;
124
125    // keep a local copy of the plugin bounds because the m_pluginWindow pointer
126    // gets updated values prior to this method being called
127    m_pluginBounds.set(m_pluginWindow->x, m_pluginWindow->y,
128                       m_pluginWindow->x + m_pluginWindow->width,
129                       m_pluginWindow->y + m_pluginWindow->height);
130
131    PLUGIN_LOG("%p PluginBounds (%d,%d,%d,%d)", m_pluginView->instance(),
132               m_pluginBounds.fLeft, m_pluginBounds.fTop,
133               m_pluginBounds.fRight, m_pluginBounds.fBottom);
134
135    const bool boundsChanged = m_pluginBounds != oldPluginBounds;
136
137    //TODO hack to ensure that we grab the most recent screen dimensions and scale
138    ANPRectI screenCoords;
139    m_core->getVisibleScreen(screenCoords);
140    float scale = m_core->scale();
141    bool scaleChanged = m_cachedZoomLevel != scale;
142    setVisibleScreen(screenCoords, scale);
143
144    // if the scale changed then setVisibleScreen will call this function and
145    // this call will potentially fire a duplicate draw event
146    if (!scaleChanged) {
147        sendSizeAndVisibilityEvents(boundsChanged);
148    }
149    layoutSurface(boundsChanged);
150
151    if (m_drawingModel != kSurface_ANPDrawingModel) {
152        SkSafeUnref(m_flipPixelRef);
153        m_flipPixelRef = new SkFlipPixelRef(computeConfig(isTransparent),
154                                            window->width, window->height);
155    }
156}
157
158bool PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) {
159
160    if (model == kOpenGL_ANPDrawingModel && m_layer == 0) {
161        jobject webview = m_core->getWebViewJavaObject();
162        AutoJObject webViewCore = m_core->getJavaObject();
163        m_layer = new WebCore::MediaLayer(webview, webViewCore.get());
164    }
165    else if (model != kOpenGL_ANPDrawingModel && m_layer != 0) {
166        m_layer->unref();
167        m_layer = 0;
168    }
169
170    if (m_drawingModel != model) {
171        // Trigger layer computation in RenderLayerCompositor
172        m_pluginView->getElement()->setNeedsStyleRecalc(SyntheticStyleChange);
173    }
174
175    m_drawingModel = model;
176    return true;
177}
178
179void PluginWidgetAndroid::checkSurfaceReady() {
180    if(!m_drawEventDelayed)
181        return;
182
183    m_drawEventDelayed = false;
184    sendSizeAndVisibilityEvents(true);
185}
186
187// returned rect is in the page coordinate
188bool PluginWidgetAndroid::isDirty(SkIRect* rect) const {
189    // nothing to report if we haven't had setWindow() called yet
190    if (NULL == m_flipPixelRef) {
191        return false;
192    }
193
194    const SkRegion& dirty = m_flipPixelRef->dirtyRgn();
195    if (dirty.isEmpty()) {
196        return false;
197    } else {
198        if (rect) {
199            *rect = dirty.getBounds();
200            rect->offset(m_pluginWindow->x, m_pluginWindow->y);
201        }
202        return true;
203    }
204}
205
206void PluginWidgetAndroid::inval(const WebCore::IntRect& rect,
207                                bool signalRedraw) {
208    // nothing to do if we haven't had setWindow() called yet. m_flipPixelRef
209    // will also be null if this is a Surface model.
210    if (NULL == m_flipPixelRef) {
211        return;
212    }
213
214    m_flipPixelRef->inval(rect);
215
216    if (signalRedraw && m_flipPixelRef->isDirty()) {
217        m_core->invalPlugin(this);
218    }
219}
220
221void PluginWidgetAndroid::viewInvalidate() {
222    WebCore::IntRect rect(m_pluginBounds.fLeft, m_pluginBounds.fTop,
223            m_pluginBounds.width(), m_pluginBounds.height());
224    m_core->viewInvalidate(rect);
225}
226
227void PluginWidgetAndroid::draw(PlatformGraphicsContext* context) {
228    if (NULL == m_flipPixelRef || !m_flipPixelRef->isDirty()) {
229        return;
230    }
231
232    SkAutoFlipUpdate update(m_flipPixelRef);
233    const SkBitmap& bitmap = update.bitmap();
234    const SkRegion& dirty = update.dirty();
235
236    ANPEvent    event;
237    SkANP::InitEvent(&event, kDraw_ANPEventType);
238
239    event.data.draw.model = m_drawingModel;
240    SkANP::SetRect(&event.data.draw.clip, dirty.getBounds());
241
242    switch (m_drawingModel) {
243        case kBitmap_ANPDrawingModel: {
244            WebCore::PluginPackage* pkg = m_pluginView->plugin();
245            NPP instance = m_pluginView->instance();
246
247            if (SkANP::SetBitmap(&event.data.draw.data.bitmap,
248                                 bitmap) &&
249                    pkg->pluginFuncs()->event(instance, &event)) {
250
251                if (context && m_pluginWindow) {
252                    SkBitmap bm(bitmap);
253                    bm.setPixelRef(m_flipPixelRef);
254                    IntRect dst(0, 0, bm.width(), bm.height());
255                    context->drawBitmapRect(bm, 0, dst);
256                }
257            }
258            break;
259        }
260        default:
261            break;
262    }
263}
264
265void PluginWidgetAndroid::setSurfaceClip(const SkIRect& clip) {
266
267    if (m_drawingModel != kSurface_ANPDrawingModel)
268        return;
269
270    /* don't display surfaces that are either entirely clipped or only 1x1 in
271       size. It appears that when an element is absolutely positioned and has
272       been completely clipped in CSS that webkit still sends a clip of 1x1.
273     */
274    bool clippedOut = (clip.width() <= 1 && clip.height() <= 1);
275    if(clippedOut != m_isSurfaceClippedOut) {
276        m_isSurfaceClippedOut = clippedOut;
277        layoutSurface();
278    }
279}
280
281void PluginWidgetAndroid::layoutSurface(bool pluginBoundsChanged) {
282
283    if (m_drawingModel != kSurface_ANPDrawingModel)
284        return;
285    if (!m_pluginWindow)
286        return;
287
288
289    bool displayPlugin = m_pluginView->isVisible() && !m_isSurfaceClippedOut;
290    PLUGIN_LOG("%p DisplayPlugin[%d] visible=[%d] clipped=[%d]",
291            m_pluginView->instance(), displayPlugin,
292            m_pluginView->isVisible(), m_isSurfaceClippedOut);
293
294    // if the surface does not exist then create a new surface
295    if (!m_embeddedView && displayPlugin) {
296
297        WebCore::PluginPackage* pkg = m_pluginView->plugin();
298        NPP instance = m_pluginView->instance();
299
300        jobject pluginSurface;
301        pkg->pluginFuncs()->getvalue(instance, kJavaSurface_ANPGetValue,
302                                     static_cast<void*>(&pluginSurface));
303
304        jobject tempObj = m_core->addSurface(pluginSurface,
305                m_pluginWindow->x, m_pluginWindow->y,
306                m_pluginWindow->width, m_pluginWindow->height);
307
308        if (tempObj) {
309            JNIEnv* env = JSC::Bindings::getJNIEnv();
310            m_embeddedView = env->NewGlobalRef(tempObj);
311            m_embeddedViewAttached = true;
312        }
313    // if the view is unattached but visible then attach it
314    } else if (m_embeddedView && !m_embeddedViewAttached && displayPlugin && !m_isFullScreen) {
315        m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
316                              m_pluginWindow->width, m_pluginWindow->height);
317        m_embeddedViewAttached = true;
318    // if the view is attached but invisible then remove it
319    } else if (m_embeddedView && m_embeddedViewAttached && !displayPlugin) {
320        m_core->destroySurface(m_embeddedView);
321        m_embeddedViewAttached = false;
322    // if the plugin's bounds have changed and it's visible then update it
323    } else if (pluginBoundsChanged && displayPlugin && !m_isFullScreen) {
324        m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
325                              m_pluginWindow->width, m_pluginWindow->height);
326
327    }
328}
329
330int16_t PluginWidgetAndroid::sendEvent(const ANPEvent& evt) {
331    if (!m_acceptEvents)
332        return 0;
333    WebCore::PluginPackage* pkg = m_pluginView->plugin();
334    NPP instance = m_pluginView->instance();
335    // "missing" plugins won't have these
336    if (pkg && instance) {
337
338        // if the plugin is gaining focus then update our state now to allow
339        // the plugin's event handler to perform actions that require focus
340        if (evt.eventType == kLifecycle_ANPEventType &&
341                evt.data.lifecycle.action == kGainFocus_ANPLifecycleAction) {
342            m_hasFocus = true;
343        }
344
345#if DEBUG_EVENTS
346        SkMSec startTime = SkTime::GetMSecs();
347#endif
348
349        // make a localCopy since the actual plugin may not respect its constness,
350        // and so we don't want our caller to have its param modified
351        ANPEvent localCopy = evt;
352        int16_t result = pkg->pluginFuncs()->event(instance, &localCopy);
353
354#if DEBUG_EVENTS
355        SkMSec endTime = SkTime::GetMSecs();
356        PLUGIN_LOG_EVENT(instance, &evt, result, endTime - startTime);
357#endif
358
359        // if the plugin is losing focus then delay the update of our state
360        // until after we notify the plugin and allow them to perform actions
361        // that may require focus
362        if (evt.eventType == kLifecycle_ANPEventType &&
363                evt.data.lifecycle.action == kLoseFocus_ANPLifecycleAction) {
364            m_hasFocus = false;
365        }
366
367        return result;
368    }
369    return 0;
370}
371
372void PluginWidgetAndroid::updateEventFlags(ANPEventFlags flags) {
373
374    // if there are no differences then immediately return
375    if (m_eventFlags == flags) {
376        return;
377    }
378
379    Document* doc = m_pluginView->parentFrame()->document();
380#if ENABLE(TOUCH_EVENTS)
381    if((m_eventFlags ^ flags) & kTouch_ANPEventFlag) {
382        if (flags & kTouch_ANPEventFlag)
383           doc->addListenerTypeIfNeeded(eventNames().touchstartEvent);
384    }
385#endif
386
387    m_eventFlags = flags;
388}
389
390bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
391    return m_eventFlags & flag;
392}
393
394void PluginWidgetAndroid::sendSizeAndVisibilityEvents(const bool updateDimensions) {
395
396    if (m_drawingModel == kOpenGL_ANPDrawingModel &&
397            !m_layer->acquireNativeWindowForContent()) {
398        m_drawEventDelayed = true;
399        return;
400    }
401
402    // TODO update the bitmap size based on the zoom? (for kBitmap_ANPDrawingModel)
403    const float zoomLevel = m_core->scale();
404
405    // notify the plugin of the new size
406    if (m_drawingModel == kOpenGL_ANPDrawingModel && updateDimensions && m_pluginWindow) {
407        PLUGIN_LOG("%s (%d,%d)[%f]", __FUNCTION__, m_pluginWindow->width,
408                m_pluginWindow->height, zoomLevel);
409        ANPEvent event;
410        SkANP::InitEvent(&event, kDraw_ANPEventType);
411        event.data.draw.model = kOpenGL_ANPDrawingModel;
412        event.data.draw.data.surface.width = m_pluginWindow->width * zoomLevel;
413        event.data.draw.data.surface.height = m_pluginWindow->height * zoomLevel;
414        sendEvent(event);
415    }
416
417    bool visible = SkIRect::Intersects(m_visibleDocRect, m_pluginBounds);
418    if(m_visible != visible) {
419
420#if DEBUG_VISIBLE_RECTS
421        PLUGIN_LOG("%p changeVisiblity[%d] pluginBounds(%d,%d,%d,%d)",
422                   m_pluginView->instance(), visible,
423                   m_pluginBounds.fLeft, m_pluginBounds.fTop,
424                   m_pluginBounds.fRight, m_pluginBounds.fBottom);
425#endif
426
427        // change the visibility
428        m_visible = visible;
429        // send the event
430        ANPEvent event;
431        SkANP::InitEvent(&event, kLifecycle_ANPEventType);
432        event.data.lifecycle.action = visible ? kOnScreen_ANPLifecycleAction
433                                              : kOffScreen_ANPLifecycleAction;
434        sendEvent(event);
435    }
436}
437
438void PluginWidgetAndroid::setVisibleScreen(const ANPRectI& visibleDocRect, float zoom) {
439#if DEBUG_VISIBLE_RECTS
440    PLUGIN_LOG("%s (%d,%d,%d,%d)[%f]", __FUNCTION__, visibleDocRect.left,
441            visibleDocRect.top, visibleDocRect.right,
442            visibleDocRect.bottom, zoom);
443#endif
444    int oldScreenW = m_visibleDocRect.width();
445    int oldScreenH = m_visibleDocRect.height();
446
447    const bool zoomChanged = m_cachedZoomLevel != zoom;
448
449    // make local copies of the parameters
450    m_cachedZoomLevel = zoom;
451    m_visibleDocRect.set(visibleDocRect.left,
452                         visibleDocRect.top,
453                         visibleDocRect.right,
454                         visibleDocRect.bottom);
455
456    int newScreenW = m_visibleDocRect.width();
457    int newScreenH = m_visibleDocRect.height();
458
459    // if the screen dimensions have changed by more than 5 pixels in either
460    // direction then recompute the plugin's visible rectangle
461    if (abs(oldScreenW - newScreenW) > 5 || abs(oldScreenH - newScreenH) > 5) {
462        PLUGIN_LOG("%s VisibleDoc old=[%d,%d] new=[%d,%d] ", __FUNCTION__,
463                   oldScreenW, oldScreenH, newScreenW, newScreenH);
464        computeVisiblePluginRect();
465    }
466
467    sendSizeAndVisibilityEvents(zoomChanged);
468}
469
470ANPRectI PluginWidgetAndroid::visibleRect() {
471
472    SkIRect visibleRect;
473    visibleRect.setEmpty();
474
475    // compute the interesection of the visible screen and the plugin
476    bool visible = visibleRect.intersect(m_visibleDocRect, m_pluginBounds);
477    if (visible) {
478        // convert from absolute coordinates to the plugin's relative coordinates
479        visibleRect.offset(-m_pluginBounds.fLeft, -m_pluginBounds.fTop);
480    }
481
482    // convert from SkRect to ANPRect
483    ANPRectI result;
484    memcpy(&result, &visibleRect, sizeof(ANPRectI));
485    return result;
486}
487
488void PluginWidgetAndroid::setVisibleRects(const ANPRectI rects[], int32_t count) {
489#if DEBUG_VISIBLE_RECTS
490    PLUGIN_LOG("%s count=%d", __FUNCTION__, count);
491#endif
492    // ensure the count does not exceed our allocated space
493    if (count > MAX_REQUESTED_RECTS)
494        count = MAX_REQUESTED_RECTS;
495
496    // store the values in member variables
497    m_requestedVisibleRectCount = count;
498    memcpy(m_requestedVisibleRects, rects, count * sizeof(rects[0]));
499
500#if DEBUG_VISIBLE_RECTS // FIXME: this fixes bad data from the plugin
501    // take it out once plugin supplies better data
502    for (int index = 0; index < count; index++) {
503        PLUGIN_LOG("%s [%d](%d,%d,%d,%d)", __FUNCTION__, index,
504            m_requestedVisibleRects[index].left,
505            m_requestedVisibleRects[index].top,
506            m_requestedVisibleRects[index].right,
507            m_requestedVisibleRects[index].bottom);
508        if (m_requestedVisibleRects[index].left ==
509                m_requestedVisibleRects[index].right) {
510            m_requestedVisibleRects[index].right += 1;
511        }
512        if (m_requestedVisibleRects[index].top ==
513                m_requestedVisibleRects[index].bottom) {
514            m_requestedVisibleRects[index].bottom += 1;
515        }
516    }
517#endif
518    computeVisiblePluginRect();
519}
520
521void PluginWidgetAndroid::computeVisiblePluginRect() {
522
523    // ensure the visibleDocRect has been set (i.e. not equal to zero)
524    if (m_visibleDocRect.isEmpty() || !m_pluginWindow || m_requestedVisibleRectCount < 1)
525        return;
526
527    // create a rect that will contain as many of the rects that will fit on screen
528    SkIRect visibleRect;
529    visibleRect.setEmpty();
530
531    for (int counter = 0; counter < m_requestedVisibleRectCount; counter++) {
532
533        ANPRectI* rect = &m_requestedVisibleRects[counter];
534
535        // create skia rect for easier manipulation and convert it to page coordinates
536        SkIRect pluginRect;
537        pluginRect.set(rect->left, rect->top, rect->right, rect->bottom);
538        pluginRect.offset(m_pluginWindow->x, m_pluginWindow->y);
539
540        // ensure the rect falls within the plugin's bounds
541        if (!m_pluginBounds.contains(pluginRect)) {
542#if DEBUG_VISIBLE_RECTS
543            PLUGIN_LOG("%s (%d,%d,%d,%d) !contain (%d,%d,%d,%d)", __FUNCTION__,
544                       m_pluginBounds.fLeft, m_pluginBounds.fTop,
545                       m_pluginBounds.fRight, m_pluginBounds.fBottom,
546                       pluginRect.fLeft, pluginRect.fTop,
547                       pluginRect.fRight, pluginRect.fBottom);
548            // assume that the desired outcome is to clamp to the container
549            if (pluginRect.intersect(m_pluginBounds)) {
550                visibleRect = pluginRect;
551            }
552#endif
553            continue;
554        }
555
556        // combine this new rect with the higher priority rects
557        pluginRect.join(visibleRect);
558
559        // check to see if the new rect could be made to fit within the screen
560        // bounds. If this is the highest priority rect then attempt to center
561        // even if it doesn't fit on the screen.
562        if (counter > 0 && (m_visibleDocRect.width() < pluginRect.width() ||
563                            m_visibleDocRect.height() < pluginRect.height()))
564          break;
565
566        // set the new visible rect
567        visibleRect = pluginRect;
568    }
569
570    m_requestedVisibleRect = visibleRect;
571    scrollToVisiblePluginRect();
572}
573
574void PluginWidgetAndroid::scrollToVisiblePluginRect() {
575
576    if (!m_hasFocus || m_requestedVisibleRect.isEmpty() || m_visibleDocRect.isEmpty()) {
577#if DEBUG_VISIBLE_RECTS
578        PLUGIN_LOG("%s call m_hasFocus=%d m_requestedVisibleRect.isEmpty()=%d"
579                " m_visibleDocRect.isEmpty()=%d", __FUNCTION__, m_hasFocus,
580                m_requestedVisibleRect.isEmpty(), m_visibleDocRect.isEmpty());
581#endif
582        return;
583    }
584    // if the entire rect is already visible then we don't need to scroll
585    if (m_visibleDocRect.contains(m_requestedVisibleRect))
586        return;
587
588    // find the center of the visibleRect in document coordinates
589    int rectCenterX = m_requestedVisibleRect.fLeft + m_requestedVisibleRect.width()/2;
590    int rectCenterY = m_requestedVisibleRect.fTop + m_requestedVisibleRect.height()/2;
591
592    // position the corner of the visible doc to center the requested rect
593    int scrollDocX = MAX(0, rectCenterX - (m_visibleDocRect.width()/2));
594    int scrollDocY = MAX(0, rectCenterY - (m_visibleDocRect.height()/2));
595
596    ScrollView* scrollView = m_pluginView->parent();
597    android::WebViewCore* core = android::WebViewCore::getWebViewCore(scrollView);
598#if DEBUG_VISIBLE_RECTS
599    PLUGIN_LOG("%s call scrollTo (%d,%d) to center (%d,%d)", __FUNCTION__,
600            scrollDocX, scrollDocY, rectCenterX, rectCenterY);
601#endif
602    core->scrollTo(scrollDocX, scrollDocY, true);
603}
604
605void PluginWidgetAndroid::requestFullScreen() {
606    if (m_isFullScreen) {
607        return;
608    }
609
610    if (!m_embeddedView && m_drawingModel == kOpenGL_ANPDrawingModel) {
611        WebCore::PluginPackage* pkg = m_pluginView->plugin();
612        NPP instance = m_pluginView->instance();
613
614        jobject pluginSurface;
615        pkg->pluginFuncs()->getvalue(instance, kJavaSurface_ANPGetValue,
616                                     static_cast<void*>(&pluginSurface));
617
618        // create the surface, but do not add it to the view hierarchy
619        jobject tempObj = m_core->createSurface(pluginSurface);
620
621        if (tempObj) {
622            JNIEnv* env = JSC::Bindings::getJNIEnv();
623            m_embeddedView = env->NewGlobalRef(tempObj);
624            m_embeddedViewAttached = false;
625        }
626    }
627
628    if (!m_embeddedView) {
629        return;
630    }
631
632    // send event to notify plugin of full screen change
633    ANPEvent event;
634    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
635    event.data.lifecycle.action = kEnterFullScreen_ANPLifecycleAction;
636    sendEvent(event);
637
638    // remove the embedded surface from the view hierarchy
639    if (m_drawingModel != kOpenGL_ANPDrawingModel)
640        m_core->destroySurface(m_embeddedView);
641
642    // add the full screen view
643    m_core->showFullScreenPlugin(m_embeddedView, m_fullScreenOrientation,
644                                 m_pluginView->instance());
645    m_isFullScreen = true;
646}
647
648void PluginWidgetAndroid::exitFullScreen(bool pluginInitiated) {
649    if (!m_isFullScreen || !m_embeddedView) {
650        return;
651    }
652
653    // remove the full screen surface from the view hierarchy
654    if (pluginInitiated) {
655        m_core->hideFullScreenPlugin();
656    }
657
658    // add the embedded view back
659    if (m_drawingModel != kOpenGL_ANPDrawingModel)
660        m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
661                m_pluginWindow->width, m_pluginWindow->height);
662
663    // send event to notify plugin of full screen change
664    ANPEvent event;
665    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
666    event.data.lifecycle.action = kExitFullScreen_ANPLifecycleAction;
667    sendEvent(event);
668
669    m_isFullScreen = false;
670}
671
672void PluginWidgetAndroid::setFullScreenOrientation(ANPScreenOrientation orientation) {
673
674    int internalOrienationId;
675    /* We need to validate that the input is legitimate and then convert the
676     * value from the plugin enum to the enum used by the android view system.
677     * The view system values correspond to those values for the
678     * screenOrientation attribute in R.java (see also ActivityInfo.java).
679     */
680    switch (orientation) {
681        case kFixedLandscape_ANPScreenOrientation:
682            internalOrienationId = 0;
683            break;
684        case kFixedPortrait_ANPScreenOrientation:
685            internalOrienationId = 1;
686            break;
687        case kLandscape_ANPScreenOrientation:
688            internalOrienationId = 6;
689            break;
690        case kPortrait_ANPScreenOrientation:
691            internalOrienationId = 7;
692            break;
693        default:
694            internalOrienationId = -1;
695    }
696
697    PLUGIN_LOG("%s orientation (%d)", __FUNCTION__, internalOrienationId);
698    m_fullScreenOrientation = internalOrienationId;
699}
700
701void PluginWidgetAndroid::requestCenterFitZoom() {
702    m_core->centerFitRect(m_pluginWindow->x, m_pluginWindow->y,
703            m_pluginWindow->width, m_pluginWindow->height);
704}
705
706void PluginWidgetAndroid::setPowerState(ANPPowerState powerState) {
707    if(m_powerState == powerState)
708        return;
709
710    // cleanup the old power state
711    switch (m_powerState) {
712        case kDefault_ANPPowerState:
713            break;
714        case kScreenOn_ANPPowerState:
715            m_core->keepScreenOn(false);
716            break;
717    }
718
719    // setup the new power state
720    switch (powerState) {
721        case kDefault_ANPPowerState:
722            break;
723        case kScreenOn_ANPPowerState:
724            m_core->keepScreenOn(true);
725            break;
726    }
727
728    m_powerState = powerState;
729}
730
731