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         AutoJObject webViewCore = m_core->getJavaObject();
    164         m_layer = new WebCore::MediaLayer(webview, webViewCore.get());
    165     }
    166     else if (model != kOpenGL_ANPDrawingModel && m_layer != 0) {
    167         m_layer->unref();
    168         m_layer = 0;
    169     }
    170 
    171     if (m_drawingModel != model) {
    172         // Trigger layer computation in RenderLayerCompositor
    173         m_pluginView->getElement()->setNeedsStyleRecalc(SyntheticStyleChange);
    174     }
    175 
    176     m_drawingModel = model;
    177     return true;
    178 }
    179 
    180 void PluginWidgetAndroid::checkSurfaceReady() {
    181     if(!m_drawEventDelayed)
    182         return;
    183 
    184     m_drawEventDelayed = false;
    185     sendSizeAndVisibilityEvents(true);
    186 }
    187 
    188 // returned rect is in the page coordinate
    189 bool PluginWidgetAndroid::isDirty(SkIRect* rect) const {
    190     // nothing to report if we haven't had setWindow() called yet
    191     if (NULL == m_flipPixelRef) {
    192         return false;
    193     }
    194 
    195     const SkRegion& dirty = m_flipPixelRef->dirtyRgn();
    196     if (dirty.isEmpty()) {
    197         return false;
    198     } else {
    199         if (rect) {
    200             *rect = dirty.getBounds();
    201             rect->offset(m_pluginWindow->x, m_pluginWindow->y);
    202         }
    203         return true;
    204     }
    205 }
    206 
    207 void PluginWidgetAndroid::inval(const WebCore::IntRect& rect,
    208                                 bool signalRedraw) {
    209     // nothing to do if we haven't had setWindow() called yet. m_flipPixelRef
    210     // will also be null if this is a Surface model.
    211     if (NULL == m_flipPixelRef) {
    212         return;
    213     }
    214 
    215     m_flipPixelRef->inval(rect);
    216 
    217     if (signalRedraw && m_flipPixelRef->isDirty()) {
    218         m_core->invalPlugin(this);
    219     }
    220 }
    221 
    222 void PluginWidgetAndroid::viewInvalidate() {
    223     WebCore::IntRect rect(m_pluginBounds.fLeft, m_pluginBounds.fTop,
    224             m_pluginBounds.width(), m_pluginBounds.height());
    225     m_core->viewInvalidate(rect);
    226 }
    227 
    228 void PluginWidgetAndroid::draw(SkCanvas* canvas) {
    229     if (NULL == m_flipPixelRef || !m_flipPixelRef->isDirty()) {
    230         return;
    231     }
    232 
    233     SkAutoFlipUpdate update(m_flipPixelRef);
    234     const SkBitmap& bitmap = update.bitmap();
    235     const SkRegion& dirty = update.dirty();
    236 
    237     ANPEvent    event;
    238     SkANP::InitEvent(&event, kDraw_ANPEventType);
    239 
    240     event.data.draw.model = m_drawingModel;
    241     SkANP::SetRect(&event.data.draw.clip, dirty.getBounds());
    242 
    243     switch (m_drawingModel) {
    244         case kBitmap_ANPDrawingModel: {
    245             WebCore::PluginPackage* pkg = m_pluginView->plugin();
    246             NPP instance = m_pluginView->instance();
    247 
    248             if (SkANP::SetBitmap(&event.data.draw.data.bitmap,
    249                                  bitmap) &&
    250                     pkg->pluginFuncs()->event(instance, &event)) {
    251 
    252                 if (canvas && m_pluginWindow) {
    253                     SkBitmap bm(bitmap);
    254                     bm.setPixelRef(m_flipPixelRef);
    255                     canvas->drawBitmap(bm, 0, 0);
    256                 }
    257             }
    258             break;
    259         }
    260         default:
    261             break;
    262     }
    263 }
    264 
    265 void 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 
    281 void 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 
    330 int16_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 
    372 void 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 
    390 bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
    391     return m_eventFlags & flag;
    392 }
    393 
    394 void 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 
    438 void 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 
    470 ANPRectI 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 
    488 void 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 
    521 void 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 
    574 void 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 
    605 void 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 
    648 void 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 
    672 void 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 
    701 void PluginWidgetAndroid::requestCenterFitZoom() {
    702     m_core->centerFitRect(m_pluginWindow->x, m_pluginWindow->y,
    703             m_pluginWindow->width, m_pluginWindow->height);
    704 }
    705 
    706 void 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