Home | History | Annotate | Download | only in plugins
      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 
     57 PluginWidgetAndroid::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 
     82 PluginWidgetAndroid::~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 
    106 void 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 
    113 static SkBitmap::Config computeConfig(bool isTransparent) {
    114     return isTransparent ? SkBitmap::kARGB_8888_Config
    115                          : SkBitmap::kRGB_565_Config;
    116 }
    117 
    118 void 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 
    159 bool 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 
    179 void 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
    188 bool 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 
    206 void 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 
    221 void 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 
    227 void 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 
    264 void 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 
    280 void 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 
    329 int16_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 
    371 void 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 
    389 bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
    390     return m_eventFlags & flag;
    391 }
    392 
    393 void 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 
    437 void 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 
    469 ANPRectI 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 
    487 void 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 
    520 void 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 
    573 void 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 
    604 void 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 
    647 void 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 
    671 void 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 
    700 void PluginWidgetAndroid::requestCenterFitZoom() {
    701     m_core->centerFitRect(m_pluginWindow->x, m_pluginWindow->y,
    702             m_pluginWindow->width, m_pluginWindow->height);
    703 }
    704 
    705 void 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