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