OSDN Git Service

am de7e9124: am ec4b5ee4: As we are sharing the view between embedded and full screen...
[android-x86/external-webkit.git] / WebKit / android / plugins / PluginWidgetAndroid.cpp
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 "WebViewCore.h"
44 #include "android_graphics.h"
45 #include <JNIUtility.h>
46
47 #define DEBUG_VISIBLE_RECTS 1 // temporary debug printfs and fixes
48
49 PluginWidgetAndroid::PluginWidgetAndroid(WebCore::PluginView* view)
50         : m_pluginView(view) {
51     m_flipPixelRef = NULL;
52     m_core = NULL;
53     m_drawingModel = kBitmap_ANPDrawingModel;
54     m_eventFlags = 0;
55     m_pluginWindow = NULL;
56     m_requestedVisibleRectCount = 0;
57     m_requestedDocRect.setEmpty();
58     m_visibleDocRect.setEmpty();
59     m_pluginBounds.setEmpty();
60     m_hasFocus = false;
61     m_isFullScreen = false;
62     m_visible = true;
63     m_zoomLevel = 0;
64     m_embeddedView = NULL;
65     m_acceptEvents = false;
66 }
67
68 PluginWidgetAndroid::~PluginWidgetAndroid() {
69     m_acceptEvents = false;
70     if (m_core) {
71         m_core->removePlugin(this);
72         if (m_isFullScreen) {
73             exitFullScreen(true);
74         }
75         if (m_embeddedView) {
76             m_core->destroySurface(m_embeddedView);
77         }
78     }
79
80     // cleanup any remaining JNI References
81     JNIEnv* env = JSC::Bindings::getJNIEnv();
82     if (m_embeddedView) {
83         env->DeleteGlobalRef(m_embeddedView);
84     }
85
86     m_flipPixelRef->safeUnref();
87 }
88
89 void PluginWidgetAndroid::init(android::WebViewCore* core) {
90     m_core = core;
91     m_core->addPlugin(this);
92     m_acceptEvents = true;
93 }
94
95 static SkBitmap::Config computeConfig(bool isTransparent) {
96     return isTransparent ? SkBitmap::kARGB_8888_Config
97                          : SkBitmap::kRGB_565_Config;
98 }
99
100 void PluginWidgetAndroid::setWindow(NPWindow* window, bool isTransparent) {
101
102     // store the reference locally for easy lookup
103     m_pluginWindow = window;
104
105     // make a copy of the previous bounds
106     SkIRect oldPluginBounds = m_pluginBounds;
107
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);
113
114     if (m_drawingModel == kSurface_ANPDrawingModel) {
115
116         // if the surface does not exist then create a new surface
117         if (!m_embeddedView) {
118
119             WebCore::PluginPackage* pkg = m_pluginView->plugin();
120             NPP instance = m_pluginView->instance();
121
122             jobject pluginSurface;
123             pkg->pluginFuncs()->getvalue(instance, kJavaSurface_ANPGetValue,
124                                          static_cast<void*>(&pluginSurface));
125
126             jobject tempObj = m_core->addSurface(pluginSurface,
127                                                  window->x, window->y,
128                                                  window->width, window->height);
129             if (tempObj) {
130                 JNIEnv* env = JSC::Bindings::getJNIEnv();
131                 m_embeddedView = env->NewGlobalRef(tempObj);
132             }
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);
138             } else {
139                 m_core->updateSurface(m_embeddedView, window->x, window->y,
140                                       window->width, window->height);
141             }
142         }
143     } else {
144         m_flipPixelRef->safeUnref();
145         m_flipPixelRef = new SkFlipPixelRef(computeConfig(isTransparent),
146                                             window->width, window->height);
147     }
148 }
149
150 bool PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) {
151     m_drawingModel = model;
152     return true;
153 }
154
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) {
159         return false;
160     }
161
162     const SkRegion& dirty = m_flipPixelRef->dirtyRgn();
163     if (dirty.isEmpty()) {
164         return false;
165     } else {
166         if (rect) {
167             *rect = dirty.getBounds();
168             rect->offset(m_pluginWindow->x, m_pluginWindow->y);
169         }
170         return true;
171     }
172 }
173
174 void PluginWidgetAndroid::inval(const WebCore::IntRect& rect,
175                                 bool signalRedraw) {
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) {
179         return;
180     }
181
182     m_flipPixelRef->inval(rect);
183
184     if (signalRedraw && m_flipPixelRef->isDirty()) {
185         m_core->invalPlugin(this);
186     }
187 }
188
189 void PluginWidgetAndroid::draw(SkCanvas* canvas) {
190     if (NULL == m_flipPixelRef || !m_flipPixelRef->isDirty()) {
191         return;
192     }
193
194     SkAutoFlipUpdate update(m_flipPixelRef);
195     const SkBitmap& bitmap = update.bitmap();
196     const SkRegion& dirty = update.dirty();
197
198     ANPEvent    event;
199     SkANP::InitEvent(&event, kDraw_ANPEventType);
200
201     event.data.draw.model = m_drawingModel;
202     SkANP::SetRect(&event.data.draw.clip, dirty.getBounds());
203
204     switch (m_drawingModel) {
205         case kBitmap_ANPDrawingModel: {
206             WebCore::PluginPackage* pkg = m_pluginView->plugin();
207             NPP instance = m_pluginView->instance();
208
209             if (SkANP::SetBitmap(&event.data.draw.data.bitmap,
210                                  bitmap) &&
211                     pkg->pluginFuncs()->event(instance, &event)) {
212
213                 if (canvas && m_pluginWindow) {
214                     SkBitmap bm(bitmap);
215                     bm.setPixelRef(m_flipPixelRef);
216                     canvas->drawBitmap(bm, 0, 0);
217                 }
218             }
219             break;
220         }
221         default:
222             break;
223     }
224 }
225
226 bool PluginWidgetAndroid::sendEvent(const ANPEvent& evt) {
227     if (!m_acceptEvents)
228         return false;
229     WebCore::PluginPackage* pkg = m_pluginView->plugin();
230     NPP instance = m_pluginView->instance();
231     // "missing" plugins won't have these
232     if (pkg && instance) {
233
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)
237                m_hasFocus = false;
238            else if (evt.data.lifecycle.action == kGainFocus_ANPLifecycleAction)
239                m_hasFocus = true;
240         }
241
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);
246     }
247     return false;
248 }
249
250 void PluginWidgetAndroid::updateEventFlags(ANPEventFlags flags) {
251
252     // if there are no differences then immediately return
253     if (m_eventFlags == flags) {
254         return;
255     }
256
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);
264        } else {
265            if (Page* page = doc->page())
266                page->chrome()->client()->needTouchEvents(false, false);
267        }
268     }
269 #endif
270
271     m_eventFlags = flags;
272 }
273
274 bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
275     return m_eventFlags & flag;
276 }
277
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);
282 #endif
283     // TODO update the bitmap size based on the zoom? (for kBitmap_ANPDrawingModel)
284
285     int oldScreenW = m_visibleDocRect.width();
286     int oldScreenH = m_visibleDocRect.height();
287
288     m_visibleDocRect.set(visibleDocRect.left, visibleDocRect.top,
289                          visibleDocRect.right, visibleDocRect.bottom);
290
291     int newScreenW = m_visibleDocRect.width();
292     int newScreenH = m_visibleDocRect.height();
293
294     if (oldScreenW != newScreenW || oldScreenH != newScreenH)
295         computeVisibleDocRect();
296
297     bool visible = SkIRect::Intersects(m_visibleDocRect, m_pluginBounds);
298     if(m_visible != visible) {
299         // change the visibility
300         m_visible = visible;
301         // send the event
302         ANPEvent event;
303         SkANP::InitEvent(&event, kLifecycle_ANPEventType);
304         event.data.lifecycle.action = visible ? kOnScreen_ANPLifecycleAction
305                                               : kOffScreen_ANPLifecycleAction;
306         sendEvent(event);
307     }
308 }
309
310 void PluginWidgetAndroid::setVisibleRects(const ANPRectI rects[], int32_t count) {
311 #if DEBUG_VISIBLE_RECTS
312     SkDebugf("%s count=%d", __FUNCTION__, count);
313 #endif
314     // ensure the count does not exceed our allocated space
315     if (count > MAX_REQUESTED_RECTS)
316         count = MAX_REQUESTED_RECTS;
317
318     // store the values in member variables
319     m_requestedVisibleRectCount = count;
320     memcpy(m_requestedVisibleRect, rects, count * sizeof(rects[0]));
321
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;
333         }
334         if (m_requestedVisibleRect[index].top ==
335                 m_requestedVisibleRect[index].bottom) {
336             m_requestedVisibleRect[index].bottom += 1;
337         }
338     }
339 #endif
340     computeVisibleDocRect();
341 }
342
343 void PluginWidgetAndroid::computeVisibleDocRect() {
344
345     // ensure the visibleDocRect has been set (i.e. not equal to zero)
346     if (m_visibleDocRect.isEmpty() || !m_pluginWindow)
347         return;
348
349     // create a rect that will contain as many of the rects that will fit on screen
350     SkIRect visibleRect;
351     visibleRect.setEmpty();
352
353     for (int counter = 0; counter < m_requestedVisibleRectCount; counter++) {
354
355         ANPRectI* rect = &m_requestedVisibleRect[counter];
356
357         // create skia rect for easier manipulation and convert it to page coordinates
358         SkIRect pluginRect;
359         pluginRect.set(rect->left, rect->top, rect->right, rect->bottom);
360         pluginRect.offset(m_pluginWindow->x, m_pluginWindow->y);
361
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);
372 #endif
373             continue;
374         }
375         // combine this new rect with the higher priority rects
376         pluginRect.join(visibleRect);
377
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()))
383           break;
384
385         // set the new visible rect
386         visibleRect = pluginRect;
387     }
388
389     m_requestedDocRect = visibleRect;
390     scrollToVisibleDocRect();
391 }
392
393 void PluginWidgetAndroid::scrollToVisibleDocRect() {
394
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());
400 #endif
401         return;
402     }
403     // if the entire rect is already visible then we don't need to scroll
404     if (m_visibleDocRect.contains(m_requestedDocRect))
405         return;
406
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;
410
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;
414
415     //compute the delta of the two points
416     int deltaX = rectCenterX - screenCenterX;
417     int deltaY = rectCenterY - screenCenterY;
418
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);
423 #endif
424     core->scrollBy(deltaX, deltaY, true);
425 }
426
427 void PluginWidgetAndroid::requestFullScreen() {
428     if (m_isFullScreen || !m_embeddedView) {
429         return;
430     }
431
432     // send event to notify plugin of full screen change
433     ANPEvent event;
434     SkANP::InitEvent(&event, kLifecycle_ANPEventType);
435     event.data.lifecycle.action = kEnterFullScreen_ANPLifecycleAction;
436     sendEvent(event);
437
438     // remove the embedded surface from the view hierarchy
439     m_core->destroySurface(m_embeddedView);
440
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;
446 }
447
448 void PluginWidgetAndroid::exitFullScreen(bool pluginInitiated) {
449     if (!m_isFullScreen || !m_embeddedView) {
450         return;
451     }
452
453     // remove the full screen surface from the view hierarchy
454     if (pluginInitiated) {
455         m_core->hideFullScreenPlugin();
456     }
457
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);
461
462     // send event to notify plugin of full screen change
463     ANPEvent event;
464     SkANP::InitEvent(&event, kLifecycle_ANPEventType);
465     event.data.lifecycle.action = kExitFullScreen_ANPLifecycleAction;
466     sendEvent(event);
467
468     m_isFullScreen = false;
469 }