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 <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 
     56 PluginWidgetAndroid::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 
     81 PluginWidgetAndroid::~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 
    105 void 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 
    112 static SkBitmap::Config computeConfig(bool isTransparent) {
    113     return isTransparent ? SkBitmap::kARGB_8888_Config
    114                          : SkBitmap::kRGB_565_Config;
    115 }
    116 
    117 void 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 
    158 bool 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 
    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(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 
    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