2 * Copyright 2008, The Android Open Source Project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "PluginWidgetAndroid.h"
29 #if ENABLE(TOUCH_EVENTS)
30 #include "ChromeClient.h"
36 #include "PluginPackage.h"
37 #include "PluginView.h"
38 #include "PluginWidgetAndroid.h"
39 #include "ScrollView.h"
41 #include "SkFlipPixelRef.h"
43 #include "WebViewCore.h"
44 #include "android_graphics.h"
45 #include <JNIUtility.h>
47 #define DEBUG_VISIBLE_RECTS 1 // temporary debug printfs and fixes
49 PluginWidgetAndroid::PluginWidgetAndroid(WebCore::PluginView* view)
50 : m_pluginView(view) {
51 m_flipPixelRef = NULL;
53 m_drawingModel = kBitmap_ANPDrawingModel;
55 m_pluginWindow = NULL;
56 m_requestedVisibleRectCount = 0;
57 m_requestedDocRect.setEmpty();
58 m_visibleDocRect.setEmpty();
59 m_pluginBounds.setEmpty();
61 m_isFullScreen = false;
64 m_embeddedView = NULL;
65 m_acceptEvents = false;
68 PluginWidgetAndroid::~PluginWidgetAndroid() {
69 m_acceptEvents = false;
71 m_core->removePlugin(this);
76 m_core->destroySurface(m_embeddedView);
80 // cleanup any remaining JNI References
81 JNIEnv* env = JSC::Bindings::getJNIEnv();
83 env->DeleteGlobalRef(m_embeddedView);
86 m_flipPixelRef->safeUnref();
89 void PluginWidgetAndroid::init(android::WebViewCore* core) {
91 m_core->addPlugin(this);
92 m_acceptEvents = true;
95 static SkBitmap::Config computeConfig(bool isTransparent) {
96 return isTransparent ? SkBitmap::kARGB_8888_Config
97 : SkBitmap::kRGB_565_Config;
100 void PluginWidgetAndroid::setWindow(NPWindow* window, bool isTransparent) {
102 // store the reference locally for easy lookup
103 m_pluginWindow = window;
105 // make a copy of the previous bounds
106 SkIRect oldPluginBounds = m_pluginBounds;
108 // keep a local copy of the plugin bounds because the m_pluginWindow pointer
109 // gets updated values prior to this method being called
110 m_pluginBounds.set(m_pluginWindow->x, m_pluginWindow->y,
111 m_pluginWindow->x + m_pluginWindow->width,
112 m_pluginWindow->y + m_pluginWindow->height);
114 if (m_drawingModel == kSurface_ANPDrawingModel) {
116 // if the surface does not exist then create a new surface
117 if (!m_embeddedView) {
119 WebCore::PluginPackage* pkg = m_pluginView->plugin();
120 NPP instance = m_pluginView->instance();
122 jobject pluginSurface;
123 pkg->pluginFuncs()->getvalue(instance, kJavaSurface_ANPGetValue,
124 static_cast<void*>(&pluginSurface));
126 jobject tempObj = m_core->addSurface(pluginSurface,
127 window->x, window->y,
128 window->width, window->height);
130 JNIEnv* env = JSC::Bindings::getJNIEnv();
131 m_embeddedView = env->NewGlobalRef(tempObj);
133 } else if (m_pluginBounds != oldPluginBounds) {
134 // if the surface exists check for changes and update accordingly
135 if (m_isFullScreen) {
136 m_core->updateFullScreenPlugin(window->x, window->y,
137 window->width, window->height);
139 m_core->updateSurface(m_embeddedView, window->x, window->y,
140 window->width, window->height);
144 m_flipPixelRef->safeUnref();
145 m_flipPixelRef = new SkFlipPixelRef(computeConfig(isTransparent),
146 window->width, window->height);
150 bool PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) {
151 m_drawingModel = model;
155 // returned rect is in the page coordinate
156 bool PluginWidgetAndroid::isDirty(SkIRect* rect) const {
157 // nothing to report if we haven't had setWindow() called yet
158 if (NULL == m_flipPixelRef) {
162 const SkRegion& dirty = m_flipPixelRef->dirtyRgn();
163 if (dirty.isEmpty()) {
167 *rect = dirty.getBounds();
168 rect->offset(m_pluginWindow->x, m_pluginWindow->y);
174 void PluginWidgetAndroid::inval(const WebCore::IntRect& rect,
176 // nothing to do if we haven't had setWindow() called yet. m_flipPixelRef
177 // will also be null if this is a Surface model.
178 if (NULL == m_flipPixelRef) {
182 m_flipPixelRef->inval(rect);
184 if (signalRedraw && m_flipPixelRef->isDirty()) {
185 m_core->invalPlugin(this);
189 void PluginWidgetAndroid::draw(SkCanvas* canvas) {
190 if (NULL == m_flipPixelRef || !m_flipPixelRef->isDirty()) {
194 SkAutoFlipUpdate update(m_flipPixelRef);
195 const SkBitmap& bitmap = update.bitmap();
196 const SkRegion& dirty = update.dirty();
199 SkANP::InitEvent(&event, kDraw_ANPEventType);
201 event.data.draw.model = m_drawingModel;
202 SkANP::SetRect(&event.data.draw.clip, dirty.getBounds());
204 switch (m_drawingModel) {
205 case kBitmap_ANPDrawingModel: {
206 WebCore::PluginPackage* pkg = m_pluginView->plugin();
207 NPP instance = m_pluginView->instance();
209 if (SkANP::SetBitmap(&event.data.draw.data.bitmap,
211 pkg->pluginFuncs()->event(instance, &event)) {
213 if (canvas && m_pluginWindow) {
215 bm.setPixelRef(m_flipPixelRef);
216 canvas->drawBitmap(bm, 0, 0);
226 bool PluginWidgetAndroid::sendEvent(const ANPEvent& evt) {
229 WebCore::PluginPackage* pkg = m_pluginView->plugin();
230 NPP instance = m_pluginView->instance();
231 // "missing" plugins won't have these
232 if (pkg && instance) {
234 // keep track of whether or not the plugin currently has focus
235 if (evt.eventType == kLifecycle_ANPEventType) {
236 if (evt.data.lifecycle.action == kLoseFocus_ANPLifecycleAction)
238 else if (evt.data.lifecycle.action == kGainFocus_ANPLifecycleAction)
242 // make a localCopy since the actual plugin may not respect its constness,
243 // and so we don't want our caller to have its param modified
244 ANPEvent localCopy = evt;
245 return pkg->pluginFuncs()->event(instance, &localCopy);
250 void PluginWidgetAndroid::updateEventFlags(ANPEventFlags flags) {
252 // if there are no differences then immediately return
253 if (m_eventFlags == flags) {
257 Document* doc = m_pluginView->getParentFrame()->document();
258 #if ENABLE(TOUCH_EVENTS)
259 if((m_eventFlags ^ flags) & kTouch_ANPEventFlag) {
260 if (flags & kTouch_ANPEventFlag) {
261 if (Page* page = doc->page())
262 page->chrome()->client()->needTouchEvents(true, false);
263 doc->addListenerTypeIfNeeded(eventNames().touchstartEvent);
265 if (Page* page = doc->page())
266 page->chrome()->client()->needTouchEvents(false, false);
271 m_eventFlags = flags;
274 bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
275 return m_eventFlags & flag;
278 void PluginWidgetAndroid::setVisibleScreen(const ANPRectI& visibleDocRect, float zoom) {
279 #if DEBUG_VISIBLE_RECTS
280 SkDebugf("%s (%d,%d,%d,%d)", __FUNCTION__, visibleDocRect.left,
281 visibleDocRect.top, visibleDocRect.right, visibleDocRect.bottom);
283 // TODO update the bitmap size based on the zoom? (for kBitmap_ANPDrawingModel)
285 int oldScreenW = m_visibleDocRect.width();
286 int oldScreenH = m_visibleDocRect.height();
288 m_visibleDocRect.set(visibleDocRect.left, visibleDocRect.top,
289 visibleDocRect.right, visibleDocRect.bottom);
291 int newScreenW = m_visibleDocRect.width();
292 int newScreenH = m_visibleDocRect.height();
294 if (oldScreenW != newScreenW || oldScreenH != newScreenH)
295 computeVisibleDocRect();
297 bool visible = SkIRect::Intersects(m_visibleDocRect, m_pluginBounds);
298 if(m_visible != visible) {
299 // change the visibility
303 SkANP::InitEvent(&event, kLifecycle_ANPEventType);
304 event.data.lifecycle.action = visible ? kOnScreen_ANPLifecycleAction
305 : kOffScreen_ANPLifecycleAction;
310 void PluginWidgetAndroid::setVisibleRects(const ANPRectI rects[], int32_t count) {
311 #if DEBUG_VISIBLE_RECTS
312 SkDebugf("%s count=%d", __FUNCTION__, count);
314 // ensure the count does not exceed our allocated space
315 if (count > MAX_REQUESTED_RECTS)
316 count = MAX_REQUESTED_RECTS;
318 // store the values in member variables
319 m_requestedVisibleRectCount = count;
320 memcpy(m_requestedVisibleRect, rects, count * sizeof(rects[0]));
322 #if DEBUG_VISIBLE_RECTS // FIXME: this fixes bad data from the plugin
323 // take it out once plugin supplies better data
324 for (int index = 0; index < count; index++) {
325 SkDebugf("%s [%d](%d,%d,%d,%d)", __FUNCTION__, index,
326 m_requestedVisibleRect[index].left,
327 m_requestedVisibleRect[index].top,
328 m_requestedVisibleRect[index].right,
329 m_requestedVisibleRect[index].bottom);
330 if (m_requestedVisibleRect[index].left ==
331 m_requestedVisibleRect[index].right) {
332 m_requestedVisibleRect[index].right += 1;
334 if (m_requestedVisibleRect[index].top ==
335 m_requestedVisibleRect[index].bottom) {
336 m_requestedVisibleRect[index].bottom += 1;
340 computeVisibleDocRect();
343 void PluginWidgetAndroid::computeVisibleDocRect() {
345 // ensure the visibleDocRect has been set (i.e. not equal to zero)
346 if (m_visibleDocRect.isEmpty() || !m_pluginWindow)
349 // create a rect that will contain as many of the rects that will fit on screen
351 visibleRect.setEmpty();
353 for (int counter = 0; counter < m_requestedVisibleRectCount; counter++) {
355 ANPRectI* rect = &m_requestedVisibleRect[counter];
357 // create skia rect for easier manipulation and convert it to page coordinates
359 pluginRect.set(rect->left, rect->top, rect->right, rect->bottom);
360 pluginRect.offset(m_pluginWindow->x, m_pluginWindow->y);
362 // ensure the rect falls within the plugin's bounds
363 if (!m_pluginBounds.contains(pluginRect)) {
364 #if DEBUG_VISIBLE_RECTS
365 SkDebugf("%s (%d,%d,%d,%d) !contain (%d,%d,%d,%d)", __FUNCTION__,
366 m_pluginBounds.fLeft, m_pluginBounds.fTop,
367 m_pluginBounds.fRight, m_pluginBounds.fBottom,
368 pluginRect.fLeft, pluginRect.fTop,
369 pluginRect.fRight, pluginRect.fBottom);
370 // FIXME: assume that the desired outcome is to clamp to the container
371 pluginRect.intersect(m_pluginBounds);
375 // combine this new rect with the higher priority rects
376 pluginRect.join(visibleRect);
378 // check to see if the new rect fits within the screen bounds. If this
379 // is the highest priority rect then attempt to center even if it doesn't
380 // fit on the screen.
381 if (counter > 0 && (m_visibleDocRect.width() < pluginRect.width() ||
382 m_visibleDocRect.height() < pluginRect.height()))
385 // set the new visible rect
386 visibleRect = pluginRect;
389 m_requestedDocRect = visibleRect;
390 scrollToVisibleDocRect();
393 void PluginWidgetAndroid::scrollToVisibleDocRect() {
395 if (!m_hasFocus || m_requestedDocRect.isEmpty() || m_visibleDocRect.isEmpty()) {
396 #if DEBUG_VISIBLE_RECTS
397 SkDebugf("%s call m_hasFocus=%d m_requestedDocRect.isEmpty()=%d"
398 " m_visibleDocRect.isEmpty()=%d", __FUNCTION__, m_hasFocus,
399 m_requestedDocRect.isEmpty(), m_visibleDocRect.isEmpty());
403 // if the entire rect is already visible then we don't need to scroll
404 if (m_visibleDocRect.contains(m_requestedDocRect))
407 // find the center of the visibleRect in document coordinates
408 int rectCenterX = m_requestedDocRect.fLeft + m_requestedDocRect.width()/2;
409 int rectCenterY = m_requestedDocRect.fTop + m_requestedDocRect.height()/2;
411 // find document coordinates for center of the visible screen
412 int screenCenterX = m_visibleDocRect.fLeft + m_visibleDocRect.width()/2;
413 int screenCenterY = m_visibleDocRect.fTop + m_visibleDocRect.height()/2;
415 //compute the delta of the two points
416 int deltaX = rectCenterX - screenCenterX;
417 int deltaY = rectCenterY - screenCenterY;
419 ScrollView* scrollView = m_pluginView->parent();
420 android::WebViewCore* core = android::WebViewCore::getWebViewCore(scrollView);
421 #if DEBUG_VISIBLE_RECTS
422 SkDebugf("%s call scrollBy (%d,%d)", __FUNCTION__, deltaX, deltaY);
424 core->scrollBy(deltaX, deltaY, true);
427 void PluginWidgetAndroid::requestFullScreen() {
428 if (m_isFullScreen || !m_embeddedView) {
432 // send event to notify plugin of full screen change
434 SkANP::InitEvent(&event, kLifecycle_ANPEventType);
435 event.data.lifecycle.action = kEnterFullScreen_ANPLifecycleAction;
438 // remove the embedded surface from the view hierarchy
439 m_core->destroySurface(m_embeddedView);
441 // add the full screen view
442 m_core->showFullScreenPlugin(m_embeddedView, m_pluginView->instance(),
443 m_pluginWindow->x, m_pluginWindow->y, m_pluginWindow->width,
444 m_pluginWindow->height);
445 m_isFullScreen = true;
448 void PluginWidgetAndroid::exitFullScreen(bool pluginInitiated) {
449 if (!m_isFullScreen || !m_embeddedView) {
453 // remove the full screen surface from the view hierarchy
454 if (pluginInitiated) {
455 m_core->hideFullScreenPlugin();
458 // add the embedded view back
459 m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
460 m_pluginWindow->width, m_pluginWindow->height);
462 // send event to notify plugin of full screen change
464 SkANP::InitEvent(&event, kLifecycle_ANPEventType);
465 event.data.lifecycle.action = kExitFullScreen_ANPLifecycleAction;
468 m_isFullScreen = false;