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 "android_graphics.h"
46#include <JNIUtility.h>
47
48//#define PLUGIN_DEBUG_LOCAL // controls the printing of log messages
49#define DEBUG_EVENTS 0 // logs event contents, return value, and processing time
50#define DEBUG_VISIBLE_RECTS 0 // temporary debug printfs and fixes
51
52#define MAX( a, b ) ( ((a) > (b)) ? (a) : (b) )
53
54// this include statement must follow the declaration of PLUGIN_DEBUG_LOCAL
55#include "PluginDebugAndroid.h"
56
57PluginWidgetAndroid::PluginWidgetAndroid(WebCore::PluginView* view)
58        : m_pluginView(view) {
59    m_flipPixelRef = NULL;
60    m_core = NULL;
61    m_drawingModel = kBitmap_ANPDrawingModel;
62    m_eventFlags = 0;
63    m_pluginWindow = NULL;
64    m_requestedVisibleRectCount = 0;
65    m_requestedVisibleRect.setEmpty();
66    m_visibleDocRect.setEmpty();
67    m_pluginBounds.setEmpty();
68    m_hasFocus = false;
69    m_isFullScreen = false;
70    m_visible = false;
71    m_cachedZoomLevel = 0;
72    m_embeddedView = NULL;
73    m_embeddedViewAttached = false;
74    m_acceptEvents = false;
75    m_isSurfaceClippedOut = false;
76    m_layer = 0;
77    m_powerState = kDefault_ANPPowerState;
78    m_fullScreenOrientation = -1;
79    m_drawEventDelayed = false;
80}
81
82PluginWidgetAndroid::~PluginWidgetAndroid() {
83    PLUGIN_LOG("%p Deleting Plugin", m_pluginView->instance());
84    m_acceptEvents = false;
85    if (m_core) {
86        setPowerState(kDefault_ANPPowerState);
87        m_core->removePlugin(this);
88        if (m_isFullScreen) {
89            exitFullScreen(true);
90        }
91        if (m_embeddedView) {
92            m_core->destroySurface(m_embeddedView);
93        }
94    }
95
96    // cleanup any remaining JNI References
97    JNIEnv* env = JSC::Bindings::getJNIEnv();
98    if (m_embeddedView) {
99        env->DeleteGlobalRef(m_embeddedView);
100    }
101
102    SkSafeUnref(m_flipPixelRef);
103    SkSafeUnref(m_layer);
104}
105
106void PluginWidgetAndroid::init(android::WebViewCore* core) {
107    m_core = core;
108    m_core->addPlugin(this);
109    m_acceptEvents = true;
110    PLUGIN_LOG("%p Initialized Plugin", m_pluginView->instance());
111}
112
113static SkBitmap::Config computeConfig(bool isTransparent) {
114    return isTransparent ? SkBitmap::kARGB_8888_Config
115                         : SkBitmap::kRGB_565_Config;
116}
117
118void PluginWidgetAndroid::setWindow(NPWindow* window, bool isTransparent) {
119
120    // store the reference locally for easy lookup
121    m_pluginWindow = window;
122
123    // make a copy of the previous bounds
124    SkIRect oldPluginBounds = m_pluginBounds;
125
126    // keep a local copy of the plugin bounds because the m_pluginWindow pointer
127    // gets updated values prior to this method being called
128    m_pluginBounds.set(m_pluginWindow->x, m_pluginWindow->y,
129                       m_pluginWindow->x + m_pluginWindow->width,
130                       m_pluginWindow->y + m_pluginWindow->height);
131
132    PLUGIN_LOG("%p PluginBounds (%d,%d,%d,%d)", m_pluginView->instance(),
133               m_pluginBounds.fLeft, m_pluginBounds.fTop,
134               m_pluginBounds.fRight, m_pluginBounds.fBottom);
135
136    const bool boundsChanged = m_pluginBounds != oldPluginBounds;
137
138    //TODO hack to ensure that we grab the most recent screen dimensions and scale
139    ANPRectI screenCoords;
140    m_core->getVisibleScreen(screenCoords);
141    float scale = m_core->scale();
142    bool scaleChanged = m_cachedZoomLevel != scale;
143    setVisibleScreen(screenCoords, scale);
144
145    // if the scale changed then setVisibleScreen will call this function and
146    // this call will potentially fire a duplicate draw event
147    if (!scaleChanged) {
148        sendSizeAndVisibilityEvents(boundsChanged);
149    }
150    layoutSurface(boundsChanged);
151
152    if (m_drawingModel != kSurface_ANPDrawingModel) {
153        SkSafeUnref(m_flipPixelRef);
154        m_flipPixelRef = new SkFlipPixelRef(computeConfig(isTransparent),
155                                            window->width, window->height);
156    }
157}
158
159bool PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) {
160
161    if (model == kOpenGL_ANPDrawingModel && m_layer == 0) {
162        jobject webview = m_core->getWebViewJavaObject();
163        m_layer = new WebCore::MediaLayer(webview);
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(SkCanvas* canvas) {
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 (canvas && m_pluginWindow) {
252                    SkBitmap bm(bitmap);
253                    bm.setPixelRef(m_flipPixelRef);
254                    canvas->drawBitmap(bm, 0, 0);
255                }
256            }
257            break;
258        }
259        default:
260            break;
261    }
262}
263
264void PluginWidgetAndroid::setSurfaceClip(const SkIRect& clip) {
265
266    if (m_drawingModel != kSurface_ANPDrawingModel)
267        return;
268
269    /* don't display surfaces that are either entirely clipped or only 1x1 in
270       size. It appears that when an element is absolutely positioned and has
271       been completely clipped in CSS that webkit still sends a clip of 1x1.
272     */
273    bool clippedOut = (clip.width() <= 1 && clip.height() <= 1);
274    if(clippedOut != m_isSurfaceClippedOut) {
275        m_isSurfaceClippedOut = clippedOut;
276        layoutSurface();
277    }
278}
279
280void PluginWidgetAndroid::layoutSurface(bool pluginBoundsChanged) {
281
282    if (m_drawingModel != kSurface_ANPDrawingModel)
283        return;
284    if (!m_pluginWindow)
285        return;
286
287
288    bool displayPlugin = m_pluginView->isVisible() && !m_isSurfaceClippedOut;
289    PLUGIN_LOG("%p DisplayPlugin[%d] visible=[%d] clipped=[%d]",
290            m_pluginView->instance(), displayPlugin,
291            m_pluginView->isVisible(), m_isSurfaceClippedOut);
292
293    // if the surface does not exist then create a new surface
294    if (!m_embeddedView && displayPlugin) {
295
296        WebCore::PluginPackage* pkg = m_pluginView->plugin();
297        NPP instance = m_pluginView->instance();
298
299        jobject pluginSurface;
300        pkg->pluginFuncs()->getvalue(instance, kJavaSurface_ANPGetValue,
301                                     static_cast<void*>(&pluginSurface));
302
303        jobject tempObj = m_core->addSurface(pluginSurface,
304                m_pluginWindow->x, m_pluginWindow->y,
305                m_pluginWindow->width, m_pluginWindow->height);
306
307        if (tempObj) {
308            JNIEnv* env = JSC::Bindings::getJNIEnv();
309            m_embeddedView = env->NewGlobalRef(tempObj);
310            m_embeddedViewAttached = true;
311        }
312    // if the view is unattached but visible then attach it
313    } else if (m_embeddedView && !m_embeddedViewAttached && displayPlugin && !m_isFullScreen) {
314        m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
315                              m_pluginWindow->width, m_pluginWindow->height);
316        m_embeddedViewAttached = true;
317    // if the view is attached but invisible then remove it
318    } else if (m_embeddedView && m_embeddedViewAttached && !displayPlugin) {
319        m_core->destroySurface(m_embeddedView);
320        m_embeddedViewAttached = false;
321    // if the plugin's bounds have changed and it's visible then update it
322    } else if (pluginBoundsChanged && displayPlugin && !m_isFullScreen) {
323        m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
324                              m_pluginWindow->width, m_pluginWindow->height);
325
326    }
327}
328
329int16_t PluginWidgetAndroid::sendEvent(const ANPEvent& evt) {
330    if (!m_acceptEvents)
331        return 0;
332    WebCore::PluginPackage* pkg = m_pluginView->plugin();
333    NPP instance = m_pluginView->instance();
334    // "missing" plugins won't have these
335    if (pkg && instance) {
336
337        // if the plugin is gaining focus then update our state now to allow
338        // the plugin's event handler to perform actions that require focus
339        if (evt.eventType == kLifecycle_ANPEventType &&
340                evt.data.lifecycle.action == kGainFocus_ANPLifecycleAction) {
341            m_hasFocus = true;
342        }
343
344#if DEBUG_EVENTS
345        SkMSec startTime = SkTime::GetMSecs();
346#endif
347
348        // make a localCopy since the actual plugin may not respect its constness,
349        // and so we don't want our caller to have its param modified
350        ANPEvent localCopy = evt;
351        int16_t result = pkg->pluginFuncs()->event(instance, &localCopy);
352
353#if DEBUG_EVENTS
354        SkMSec endTime = SkTime::GetMSecs();
355        PLUGIN_LOG_EVENT(instance, &evt, result, endTime - startTime);
356#endif
357
358        // if the plugin is losing focus then delay the update of our state
359        // until after we notify the plugin and allow them to perform actions
360        // that may require focus
361        if (evt.eventType == kLifecycle_ANPEventType &&
362                evt.data.lifecycle.action == kLoseFocus_ANPLifecycleAction) {
363            m_hasFocus = false;
364        }
365
366        return result;
367    }
368    return 0;
369}
370
371void PluginWidgetAndroid::updateEventFlags(ANPEventFlags flags) {
372
373    // if there are no differences then immediately return
374    if (m_eventFlags == flags) {
375        return;
376    }
377
378    Document* doc = m_pluginView->parentFrame()->document();
379#if ENABLE(TOUCH_EVENTS)
380    if((m_eventFlags ^ flags) & kTouch_ANPEventFlag) {
381        if (flags & kTouch_ANPEventFlag)
382           doc->addListenerTypeIfNeeded(eventNames().touchstartEvent);
383    }
384#endif
385
386    m_eventFlags = flags;
387}
388
389bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
390    return m_eventFlags & flag;
391}
392
393void PluginWidgetAndroid::sendSizeAndVisibilityEvents(const bool updateDimensions) {
394
395    if (m_drawingModel == kOpenGL_ANPDrawingModel &&
396            !m_layer->acquireNativeWindowForContent()) {
397        m_drawEventDelayed = true;
398        return;
399    }
400
401    // TODO update the bitmap size based on the zoom? (for kBitmap_ANPDrawingModel)
402    const float zoomLevel = m_core->scale();
403
404    // notify the plugin of the new size
405    if (m_drawingModel == kOpenGL_ANPDrawingModel && updateDimensions && m_pluginWindow) {
406        PLUGIN_LOG("%s (%d,%d)[%f]", __FUNCTION__, m_pluginWindow->width,
407                m_pluginWindow->height, zoomLevel);
408        ANPEvent event;
409        SkANP::InitEvent(&event, kDraw_ANPEventType);
410        event.data.draw.model = kOpenGL_ANPDrawingModel;
411        event.data.draw.data.surface.width = m_pluginWindow->width * zoomLevel;
412        event.data.draw.data.surface.height = m_pluginWindow->height * zoomLevel;
413        sendEvent(event);
414    }
415
416    bool visible = SkIRect::Intersects(m_visibleDocRect, m_pluginBounds);
417    if(m_visible != visible) {
418
419#if DEBUG_VISIBLE_RECTS
420        PLUGIN_LOG("%p changeVisiblity[%d] pluginBounds(%d,%d,%d,%d)",
421                   m_pluginView->instance(), visible,
422                   m_pluginBounds.fLeft, m_pluginBounds.fTop,
423                   m_pluginBounds.fRight, m_pluginBounds.fBottom);
424#endif
425
426        // change the visibility
427        m_visible = visible;
428        // send the event
429        ANPEvent event;
430        SkANP::InitEvent(&event, kLifecycle_ANPEventType);
431        event.data.lifecycle.action = visible ? kOnScreen_ANPLifecycleAction
432                                              : kOffScreen_ANPLifecycleAction;
433        sendEvent(event);
434    }
435}
436
437void PluginWidgetAndroid::setVisibleScreen(const ANPRectI& visibleDocRect, float zoom) {
438#if DEBUG_VISIBLE_RECTS
439    PLUGIN_LOG("%s (%d,%d,%d,%d)[%f]", __FUNCTION__, visibleDocRect.left,
440            visibleDocRect.top, visibleDocRect.right,
441            visibleDocRect.bottom, zoom);
442#endif
443    int oldScreenW = m_visibleDocRect.width();
444    int oldScreenH = m_visibleDocRect.height();
445
446    const bool zoomChanged = m_cachedZoomLevel != zoom;
447
448    // make local copies of the parameters
449    m_cachedZoomLevel = zoom;
450    m_visibleDocRect.set(visibleDocRect.left,
451                         visibleDocRect.top,
452                         visibleDocRect.right,
453                         visibleDocRect.bottom);
454
455    int newScreenW = m_visibleDocRect.width();
456    int newScreenH = m_visibleDocRect.height();
457
458    // if the screen dimensions have changed by more than 5 pixels in either
459    // direction then recompute the plugin's visible rectangle
460    if (abs(oldScreenW - newScreenW) > 5 || abs(oldScreenH - newScreenH) > 5) {
461        PLUGIN_LOG("%s VisibleDoc old=[%d,%d] new=[%d,%d] ", __FUNCTION__,
462                   oldScreenW, oldScreenH, newScreenW, newScreenH);
463        computeVisiblePluginRect();
464    }
465
466    sendSizeAndVisibilityEvents(zoomChanged);
467}
468
469ANPRectI PluginWidgetAndroid::visibleRect() {
470
471    SkIRect visibleRect;
472    visibleRect.setEmpty();
473
474    // compute the interesection of the visible screen and the plugin
475    bool visible = visibleRect.intersect(m_visibleDocRect, m_pluginBounds);
476    if (visible) {
477        // convert from absolute coordinates to the plugin's relative coordinates
478        visibleRect.offset(-m_pluginBounds.fLeft, -m_pluginBounds.fTop);
479    }
480
481    // convert from SkRect to ANPRect
482    ANPRectI result;
483    memcpy(&result, &visibleRect, sizeof(ANPRectI));
484    return result;
485}
486
487void PluginWidgetAndroid::setVisibleRects(const ANPRectI rects[], int32_t count) {
488#if DEBUG_VISIBLE_RECTS
489    PLUGIN_LOG("%s count=%d", __FUNCTION__, count);
490#endif
491    // ensure the count does not exceed our allocated space
492    if (count > MAX_REQUESTED_RECTS)
493        count = MAX_REQUESTED_RECTS;
494
495    // store the values in member variables
496    m_requestedVisibleRectCount = count;
497    memcpy(m_requestedVisibleRects, rects, count * sizeof(rects[0]));
498
499#if DEBUG_VISIBLE_RECTS // FIXME: this fixes bad data from the plugin
500    // take it out once plugin supplies better data
501    for (int index = 0; index < count; index++) {
502        PLUGIN_LOG("%s [%d](%d,%d,%d,%d)", __FUNCTION__, index,
503            m_requestedVisibleRects[index].left,
504            m_requestedVisibleRects[index].top,
505            m_requestedVisibleRects[index].right,
506            m_requestedVisibleRects[index].bottom);
507        if (m_requestedVisibleRects[index].left ==
508                m_requestedVisibleRects[index].right) {
509            m_requestedVisibleRects[index].right += 1;
510        }
511        if (m_requestedVisibleRects[index].top ==
512                m_requestedVisibleRects[index].bottom) {
513            m_requestedVisibleRects[index].bottom += 1;
514        }
515    }
516#endif
517    computeVisiblePluginRect();
518}
519
520void PluginWidgetAndroid::computeVisiblePluginRect() {
521
522    // ensure the visibleDocRect has been set (i.e. not equal to zero)
523    if (m_visibleDocRect.isEmpty() || !m_pluginWindow || m_requestedVisibleRectCount < 1)
524        return;
525
526    // create a rect that will contain as many of the rects that will fit on screen
527    SkIRect visibleRect;
528    visibleRect.setEmpty();
529
530    for (int counter = 0; counter < m_requestedVisibleRectCount; counter++) {
531
532        ANPRectI* rect = &m_requestedVisibleRects[counter];
533
534        // create skia rect for easier manipulation and convert it to page coordinates
535        SkIRect pluginRect;
536        pluginRect.set(rect->left, rect->top, rect->right, rect->bottom);
537        pluginRect.offset(m_pluginWindow->x, m_pluginWindow->y);
538
539        // ensure the rect falls within the plugin's bounds
540        if (!m_pluginBounds.contains(pluginRect)) {
541#if DEBUG_VISIBLE_RECTS
542            PLUGIN_LOG("%s (%d,%d,%d,%d) !contain (%d,%d,%d,%d)", __FUNCTION__,
543                       m_pluginBounds.fLeft, m_pluginBounds.fTop,
544                       m_pluginBounds.fRight, m_pluginBounds.fBottom,
545                       pluginRect.fLeft, pluginRect.fTop,
546                       pluginRect.fRight, pluginRect.fBottom);
547            // assume that the desired outcome is to clamp to the container
548            if (pluginRect.intersect(m_pluginBounds)) {
549                visibleRect = pluginRect;
550            }
551#endif
552            continue;
553        }
554
555        // combine this new rect with the higher priority rects
556        pluginRect.join(visibleRect);
557
558        // check to see if the new rect could be made to fit within the screen
559        // bounds. If this is the highest priority rect then attempt to center
560        // even if it doesn't fit on the screen.
561        if (counter > 0 && (m_visibleDocRect.width() < pluginRect.width() ||
562                            m_visibleDocRect.height() < pluginRect.height()))
563          break;
564
565        // set the new visible rect
566        visibleRect = pluginRect;
567    }
568
569    m_requestedVisibleRect = visibleRect;
570    scrollToVisiblePluginRect();
571}
572
573void PluginWidgetAndroid::scrollToVisiblePluginRect() {
574
575    if (!m_hasFocus || m_requestedVisibleRect.isEmpty() || m_visibleDocRect.isEmpty()) {
576#if DEBUG_VISIBLE_RECTS
577        PLUGIN_LOG("%s call m_hasFocus=%d m_requestedVisibleRect.isEmpty()=%d"
578                " m_visibleDocRect.isEmpty()=%d", __FUNCTION__, m_hasFocus,
579                m_requestedVisibleRect.isEmpty(), m_visibleDocRect.isEmpty());
580#endif
581        return;
582    }
583    // if the entire rect is already visible then we don't need to scroll
584    if (m_visibleDocRect.contains(m_requestedVisibleRect))
585        return;
586
587    // find the center of the visibleRect in document coordinates
588    int rectCenterX = m_requestedVisibleRect.fLeft + m_requestedVisibleRect.width()/2;
589    int rectCenterY = m_requestedVisibleRect.fTop + m_requestedVisibleRect.height()/2;
590
591    // position the corner of the visible doc to center the requested rect
592    int scrollDocX = MAX(0, rectCenterX - (m_visibleDocRect.width()/2));
593    int scrollDocY = MAX(0, rectCenterY - (m_visibleDocRect.height()/2));
594
595    ScrollView* scrollView = m_pluginView->parent();
596    android::WebViewCore* core = android::WebViewCore::getWebViewCore(scrollView);
597#if DEBUG_VISIBLE_RECTS
598    PLUGIN_LOG("%s call scrollTo (%d,%d) to center (%d,%d)", __FUNCTION__,
599            scrollDocX, scrollDocX, rectCenterX, rectCenterY);
600#endif
601    core->scrollTo(scrollDocX, scrollDocX, true);
602}
603
604void PluginWidgetAndroid::requestFullScreen() {
605    if (m_isFullScreen) {
606        return;
607    }
608
609    if (!m_embeddedView && m_drawingModel == kOpenGL_ANPDrawingModel) {
610        WebCore::PluginPackage* pkg = m_pluginView->plugin();
611        NPP instance = m_pluginView->instance();
612
613        jobject pluginSurface;
614        pkg->pluginFuncs()->getvalue(instance, kJavaSurface_ANPGetValue,
615                                     static_cast<void*>(&pluginSurface));
616
617        // create the surface, but do not add it to the view hierarchy
618        jobject tempObj = m_core->createSurface(pluginSurface);
619
620        if (tempObj) {
621            JNIEnv* env = JSC::Bindings::getJNIEnv();
622            m_embeddedView = env->NewGlobalRef(tempObj);
623            m_embeddedViewAttached = false;
624        }
625    }
626
627    if (!m_embeddedView) {
628        return;
629    }
630
631    // send event to notify plugin of full screen change
632    ANPEvent event;
633    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
634    event.data.lifecycle.action = kEnterFullScreen_ANPLifecycleAction;
635    sendEvent(event);
636
637    // remove the embedded surface from the view hierarchy
638    if (m_drawingModel != kOpenGL_ANPDrawingModel)
639        m_core->destroySurface(m_embeddedView);
640
641    // add the full screen view
642    m_core->showFullScreenPlugin(m_embeddedView, m_fullScreenOrientation,
643                                 m_pluginView->instance());
644    m_isFullScreen = true;
645}
646
647void PluginWidgetAndroid::exitFullScreen(bool pluginInitiated) {
648    if (!m_isFullScreen || !m_embeddedView) {
649        return;
650    }
651
652    // remove the full screen surface from the view hierarchy
653    if (pluginInitiated) {
654        m_core->hideFullScreenPlugin();
655    }
656
657    // add the embedded view back
658    if (m_drawingModel != kOpenGL_ANPDrawingModel)
659        m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
660                m_pluginWindow->width, m_pluginWindow->height);
661
662    // send event to notify plugin of full screen change
663    ANPEvent event;
664    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
665    event.data.lifecycle.action = kExitFullScreen_ANPLifecycleAction;
666    sendEvent(event);
667
668    m_isFullScreen = false;
669}
670
671void PluginWidgetAndroid::setFullScreenOrientation(ANPScreenOrientation orientation) {
672
673    int internalOrienationId;
674    /* We need to validate that the input is legitimate and then convert the
675     * value from the plugin enum to the enum used by the android view system.
676     * The view system values correspond to those values for the
677     * screenOrientation attribute in R.java (see also ActivityInfo.java).
678     */
679    switch (orientation) {
680        case kFixedLandscape_ANPScreenOrientation:
681            internalOrienationId = 0;
682            break;
683        case kFixedPortrait_ANPScreenOrientation:
684            internalOrienationId = 1;
685            break;
686        case kLandscape_ANPScreenOrientation:
687            internalOrienationId = 6;
688            break;
689        case kPortrait_ANPScreenOrientation:
690            internalOrienationId = 7;
691            break;
692        default:
693            internalOrienationId = -1;
694    }
695
696    PLUGIN_LOG("%s orientation (%d)", __FUNCTION__, internalOrienationId);
697    m_fullScreenOrientation = internalOrienationId;
698}
699
700void PluginWidgetAndroid::requestCenterFitZoom() {
701    m_core->centerFitRect(m_pluginWindow->x, m_pluginWindow->y,
702            m_pluginWindow->width, m_pluginWindow->height);
703}
704
705void PluginWidgetAndroid::setPowerState(ANPPowerState powerState) {
706    if(m_powerState == powerState)
707        return;
708
709    // cleanup the old power state
710    switch (m_powerState) {
711        case kDefault_ANPPowerState:
712            break;
713        case kScreenOn_ANPPowerState:
714            m_core->keepScreenOn(false);
715            break;
716    }
717
718    // setup the new power state
719    switch (powerState) {
720        case kDefault_ANPPowerState:
721            break;
722        case kScreenOn_ANPPowerState:
723            m_core->keepScreenOn(true);
724            break;
725    }
726
727    m_powerState = powerState;
728}
729
730