• 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 APPLE COMPUTER, INC. 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 "android_graphics.h"
28 #include "Document.h"
29 #include "Element.h"
30 #include "Frame.h"
31 #include "PluginPackage.h"
32 #include "PluginView.h"
33 #include "PluginWidgetAndroid.h"
34 #include "ScrollView.h"
35 #include "SkANP.h"
36 #include "SkFlipPixelRef.h"
37 #include "SkString.h"
38 #include "WebViewCore.h"
39 
PluginWidgetAndroid(WebCore::PluginView * view)40 PluginWidgetAndroid::PluginWidgetAndroid(WebCore::PluginView* view)
41         : m_pluginView(view) {
42     m_flipPixelRef = NULL;
43     m_core = NULL;
44     m_drawingModel = kBitmap_ANPDrawingModel;
45     m_eventFlags = 0;
46     m_pluginWindow = NULL;
47     m_requestedVisibleRectCount = 0;
48     m_requestedFrameRect.setEmpty();
49     m_visibleDocRect.setEmpty();
50     m_hasFocus = false;
51     m_zoomLevel = 0;
52     m_javaClassName = NULL;
53     m_childView = NULL;
54 }
55 
~PluginWidgetAndroid()56 PluginWidgetAndroid::~PluginWidgetAndroid() {
57     if (m_core) {
58         m_core->removePlugin(this);
59         if (m_childView) {
60             m_core->destroySurface(m_childView);
61         }
62     }
63     if (m_javaClassName) {
64         free(m_javaClassName);
65     }
66     m_flipPixelRef->safeUnref();
67 }
68 
init(android::WebViewCore * core)69 void PluginWidgetAndroid::init(android::WebViewCore* core) {
70     m_core = core;
71     m_core->addPlugin(this);
72 }
73 
computeConfig(bool isTransparent)74 static SkBitmap::Config computeConfig(bool isTransparent) {
75     return isTransparent ? SkBitmap::kARGB_8888_Config
76                          : SkBitmap::kRGB_565_Config;
77 }
78 
setWindow(NPWindow * window,bool isTransparent)79 void PluginWidgetAndroid::setWindow(NPWindow* window, bool isTransparent) {
80     m_pluginWindow = window;
81 
82     if (m_drawingModel == kSurface_ANPDrawingModel) {
83         if (!m_childView) {
84             IntPoint docPoint = frameToDocumentCoords(window->x, window->y);
85 
86             const String& libName = m_pluginView->plugin()->path();
87             SkString skLibName;
88             skLibName.setUTF16(libName.characters(), libName.length());
89 
90             m_childView = m_core->createSurface(skLibName.c_str(), m_javaClassName,
91                                                 m_pluginView->instance(),
92                                                 docPoint.x(), docPoint.y(),
93                                                 window->width, window->height);
94         }
95     } else {
96         m_flipPixelRef->safeUnref();
97         m_flipPixelRef = new SkFlipPixelRef(computeConfig(isTransparent),
98                                             window->width, window->height);
99     }
100 }
101 
setPluginStubJavaClassName(const char * className)102 bool PluginWidgetAndroid::setPluginStubJavaClassName(const char* className) {
103 
104     if (m_javaClassName) {
105         free(m_javaClassName);
106     }
107 
108     // don't call strdup() if the className is to be set to NULL
109     if (!className) {
110         m_javaClassName = NULL;
111         return true;
112     }
113 
114     // make a local copy of the className
115     m_javaClassName = strdup(className);
116     return (m_javaClassName != NULL);
117 }
118 
requestFullScreenMode()119 void PluginWidgetAndroid::requestFullScreenMode() {
120 
121     if (!m_javaClassName) {
122         return;
123     }
124 
125     const String& libName = m_pluginView->plugin()->path();
126     SkString skLibName;
127     skLibName.setUTF16(libName.characters(), libName.length());
128 
129     m_core->startFullScreenPluginActivity(skLibName.c_str(), m_javaClassName,
130                                           m_pluginView->instance());
131 }
132 
setDrawingModel(ANPDrawingModel model)133 bool PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) {
134 
135     // disallow the surface drawing model if no java class name has been given
136     if (model == kSurface_ANPDrawingModel && m_javaClassName == NULL) {
137         return false;
138     }
139 
140     m_drawingModel = model;
141     return true;
142 }
143 
localToDocumentCoords(SkIRect * rect) const144 void PluginWidgetAndroid::localToDocumentCoords(SkIRect* rect) const {
145     if (m_pluginWindow) {
146         IntPoint pluginDocCoords = frameToDocumentCoords(m_pluginWindow->x,
147                                                          m_pluginWindow->y);
148         rect->offset(pluginDocCoords.x(), pluginDocCoords.y());
149     }
150 }
151 
isDirty(SkIRect * rect) const152 bool PluginWidgetAndroid::isDirty(SkIRect* rect) const {
153     // nothing to report if we haven't had setWindow() called yet
154     if (NULL == m_flipPixelRef) {
155         return false;
156     }
157 
158     const SkRegion& dirty = m_flipPixelRef->dirtyRgn();
159     if (dirty.isEmpty()) {
160         return false;
161     } else {
162         if (rect) {
163             *rect = dirty.getBounds();
164         }
165         return true;
166     }
167 }
168 
inval(const WebCore::IntRect & rect,bool signalRedraw)169 void PluginWidgetAndroid::inval(const WebCore::IntRect& rect,
170                                 bool signalRedraw) {
171     // nothing to do if we haven't had setWindow() called yet. m_flipPixelRef
172     // will also be null if this is a Surface model.
173     if (NULL == m_flipPixelRef) {
174         return;
175     }
176 
177     m_flipPixelRef->inval(rect);
178 
179     if (signalRedraw && m_flipPixelRef->isDirty()) {
180         m_core->invalPlugin(this);
181     }
182 }
183 
draw(SkCanvas * canvas)184 void PluginWidgetAndroid::draw(SkCanvas* canvas) {
185     if (NULL == m_flipPixelRef || !m_flipPixelRef->isDirty()) {
186         return;
187     }
188 
189     SkAutoFlipUpdate update(m_flipPixelRef);
190     const SkBitmap& bitmap = update.bitmap();
191     const SkRegion& dirty = update.dirty();
192 
193     ANPEvent    event;
194     SkANP::InitEvent(&event, kDraw_ANPEventType);
195 
196     event.data.draw.model = m_drawingModel;
197     SkANP::SetRect(&event.data.draw.clip, dirty.getBounds());
198 
199     switch (m_drawingModel) {
200         case kBitmap_ANPDrawingModel: {
201             WebCore::PluginPackage* pkg = m_pluginView->plugin();
202             NPP instance = m_pluginView->instance();
203 
204             if (SkANP::SetBitmap(&event.data.draw.data.bitmap,
205                                  bitmap) &&
206                     pkg->pluginFuncs()->event(instance, &event)) {
207 
208                 if (canvas && m_pluginWindow) {
209                     SkBitmap bm(bitmap);
210                     bm.setPixelRef(m_flipPixelRef);
211                     canvas->drawBitmap(bm, SkIntToScalar(m_pluginWindow->x),
212                                            SkIntToScalar(m_pluginWindow->y), NULL);
213                 }
214             }
215             break;
216         }
217         default:
218             break;
219     }
220 }
221 
sendEvent(const ANPEvent & evt)222 bool PluginWidgetAndroid::sendEvent(const ANPEvent& evt) {
223     WebCore::PluginPackage* pkg = m_pluginView->plugin();
224     NPP instance = m_pluginView->instance();
225     // "missing" plugins won't have these
226     if (pkg && instance) {
227 
228         // keep track of whether or not the plugin currently has focus
229         if (evt.eventType == kLifecycle_ANPEventType) {
230            if (evt.data.lifecycle.action == kLoseFocus_ANPLifecycleAction)
231                m_hasFocus = false;
232            else if (evt.data.lifecycle.action == kGainFocus_ANPLifecycleAction)
233                m_hasFocus = true;
234         }
235 
236         // make a localCopy since the actual plugin may not respect its constness,
237         // and so we don't want our caller to have its param modified
238         ANPEvent localCopy = evt;
239         return pkg->pluginFuncs()->event(instance, &localCopy);
240     }
241     return false;
242 }
243 
updateEventFlags(ANPEventFlags flags)244 void PluginWidgetAndroid::updateEventFlags(ANPEventFlags flags) {
245 
246     // if there are no differences then immediately return
247     if (m_eventFlags == flags) {
248         return;
249     }
250 
251     Document* doc = m_pluginView->getParentFrame()->document();
252     if((m_eventFlags ^ flags) & kTouch_ANPEventFlag) {
253         if(flags & kTouch_ANPEventFlag)
254             doc->addTouchEventListener(m_pluginView->getElement());
255         else
256             doc->removeTouchEventListener(m_pluginView->getElement());
257     }
258 
259     m_eventFlags = flags;
260 }
261 
isAcceptingEvent(ANPEventFlag flag)262 bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
263     return m_eventFlags & flag;
264 }
265 
setVisibleScreen(const ANPRectI & visibleDocRect,float zoom)266 void PluginWidgetAndroid::setVisibleScreen(const ANPRectI& visibleDocRect, float zoom) {
267 
268     // TODO update the bitmap size based on the zoom? (for kBitmap_ANPDrawingModel)
269 
270     int oldScreenW = m_visibleDocRect.width();
271     int oldScreenH = m_visibleDocRect.height();
272 
273     m_visibleDocRect.set(visibleDocRect.left, visibleDocRect.top,
274                          visibleDocRect.right, visibleDocRect.bottom);
275 
276     int newScreenW = m_visibleDocRect.width();
277     int newScreenH = m_visibleDocRect.height();
278 
279     if (oldScreenW != newScreenW || oldScreenH != newScreenH)
280         computeVisibleFrameRect();
281 }
282 
setVisibleRects(const ANPRectI rects[],int32_t count)283 void PluginWidgetAndroid::setVisibleRects(const ANPRectI rects[], int32_t count) {
284 
285     // ensure the count does not exceed our allocated space
286     if (count > MAX_REQUESTED_RECTS)
287         count = MAX_REQUESTED_RECTS;
288 
289     // store the values in member variables
290     m_requestedVisibleRectCount = count;
291     memcpy(m_requestedVisibleRect, rects, count * sizeof(rects[0]));
292 
293     computeVisibleFrameRect();
294 }
295 
computeVisibleFrameRect()296 void PluginWidgetAndroid::computeVisibleFrameRect() {
297 
298     // ensure the visibleDocRect has been set (i.e. not equal to zero)
299     if (m_visibleDocRect.isEmpty() || !m_pluginWindow)
300         return;
301 
302     // create a rect that represents the plugin's bounds
303     SkIRect pluginBounds;
304     pluginBounds.set(m_pluginWindow->x, m_pluginWindow->y,
305                      m_pluginWindow->x + m_pluginWindow->width,
306                      m_pluginWindow->y + m_pluginWindow->height);
307 
308     // create a rect that will contain as many of the rects that will fit on screen
309     SkIRect visibleRect;
310     visibleRect.setEmpty();
311 
312     for (int counter = 0; counter < m_requestedVisibleRectCount; counter++) {
313 
314         ANPRectI* rect = &m_requestedVisibleRect[counter];
315 
316         // create skia rect for easier manipulation and convert it to frame coordinates
317         SkIRect pluginRect;
318         pluginRect.set(rect->left, rect->top, rect->right, rect->bottom);
319         pluginRect.offset(m_pluginWindow->x, m_pluginWindow->y);
320 
321         // ensure the rect falls within the plugin's bounds
322         if (!pluginBounds.contains(pluginRect))
323           continue;
324 
325         // combine this new rect with the higher priority rects
326         pluginRect.join(visibleRect);
327 
328         // check to see if the new rect fits within the screen bounds. If this
329         // is the highest priority rect then attempt to center even if it doesn't
330         // fit on the screen.
331         if (counter > 0 && (m_visibleDocRect.width() < pluginRect.width() ||
332                                m_visibleDocRect.height() < pluginRect.height()))
333           break;
334 
335         // set the new visible rect
336         visibleRect = pluginRect;
337     }
338 
339     m_requestedFrameRect = visibleRect;
340     scrollToVisibleFrameRect();
341 }
342 
scrollToVisibleFrameRect()343 void PluginWidgetAndroid::scrollToVisibleFrameRect() {
344 
345     if (!m_hasFocus || m_requestedFrameRect.isEmpty() || m_visibleDocRect.isEmpty())
346         return;
347 
348     // if the entire rect is already visible then we don't need to scroll, which
349     // requires converting the m_requestedFrameRect from frame to doc coordinates
350     IntPoint pluginDocPoint = frameToDocumentCoords(m_requestedFrameRect.fLeft,
351                                                     m_requestedFrameRect.fTop);
352     SkIRect requestedDocRect;
353     requestedDocRect.set(pluginDocPoint.x(), pluginDocPoint.y(),
354                          pluginDocPoint.x() + m_requestedFrameRect.width(),
355                          pluginDocPoint.y() + m_requestedFrameRect.height());
356 
357     if (m_visibleDocRect.contains(requestedDocRect))
358         return;
359 
360     // find the center of the visibleRect in document coordinates
361     int rectCenterX = requestedDocRect.fLeft + requestedDocRect.width()/2;
362     int rectCenterY = requestedDocRect.fTop + requestedDocRect.height()/2;
363 
364     // find document coordinates for center of the visible screen
365     int screenCenterX = m_visibleDocRect.fLeft + m_visibleDocRect.width()/2;
366     int screenCenterY = m_visibleDocRect.fTop + m_visibleDocRect.height()/2;
367 
368     //compute the delta of the two points
369     int deltaX = rectCenterX - screenCenterX;
370     int deltaY = rectCenterY - screenCenterY;
371 
372     ScrollView* scrollView = m_pluginView->parent();
373     android::WebViewCore* core = android::WebViewCore::getWebViewCore(scrollView);
374     core->scrollBy(deltaX, deltaY, true);
375 }
376 
frameToDocumentCoords(int frameX,int frameY) const377 IntPoint PluginWidgetAndroid::frameToDocumentCoords(int frameX, int frameY) const {
378     IntPoint docPoint = IntPoint(frameX, frameY);
379 
380     const ScrollView* currentScrollView = m_pluginView->parent();
381     if (currentScrollView) {
382         const ScrollView* parentScrollView = currentScrollView->parent();
383         while (parentScrollView) {
384 
385             docPoint.move(currentScrollView->x(), currentScrollView->y());
386 
387             currentScrollView = parentScrollView;
388             parentScrollView = parentScrollView->parent();
389         }
390     }
391 
392     return docPoint;
393 }
394