• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PluginWidgetAndroid(WebCore::PluginView * view)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 
~PluginWidgetAndroid()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 
init(android::WebViewCore * core)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 
computeConfig(bool isTransparent)113 static SkBitmap::Config computeConfig(bool isTransparent) {
114     return isTransparent ? SkBitmap::kARGB_8888_Config
115                          : SkBitmap::kRGB_565_Config;
116 }
117 
setWindow(NPWindow * window,bool isTransparent)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 
setDrawingModel(ANPDrawingModel model)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 
checkSurfaceReady()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
isDirty(SkIRect * rect) const188 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 
inval(const WebCore::IntRect & rect,bool signalRedraw)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 
viewInvalidate()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 
draw(SkCanvas * canvas)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 
setSurfaceClip(const SkIRect & clip)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 
layoutSurface(bool pluginBoundsChanged)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 
sendEvent(const ANPEvent & evt)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 
updateEventFlags(ANPEventFlags flags)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 
isAcceptingEvent(ANPEventFlag flag)389 bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
390     return m_eventFlags & flag;
391 }
392 
sendSizeAndVisibilityEvents(const bool updateDimensions)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 
setVisibleScreen(const ANPRectI & visibleDocRect,float zoom)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 
visibleRect()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 
setVisibleRects(const ANPRectI rects[],int32_t count)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 
computeVisiblePluginRect()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 
scrollToVisiblePluginRect()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 
requestFullScreen()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 
exitFullScreen(bool pluginInitiated)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 
setFullScreenOrientation(ANPScreenOrientation orientation)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 
requestCenterFitZoom()700 void PluginWidgetAndroid::requestCenterFitZoom() {
701     m_core->centerFitRect(m_pluginWindow->x, m_pluginWindow->y,
702             m_pluginWindow->width, m_pluginWindow->height);
703 }
704 
setPowerState(ANPPowerState powerState)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