• 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 "BackgroundPlugin.h"
27 #include "android_npapi.h"
28 
29 #include <stdio.h>
30 #include <sys/time.h>
31 #include <time.h>
32 #include <math.h>
33 #include <string.h>
34 
35 extern NPNetscapeFuncs*        browser;
36 extern ANPBitmapInterfaceV0    gBitmapI;
37 extern ANPCanvasInterfaceV0    gCanvasI;
38 extern ANPLogInterfaceV0       gLogI;
39 extern ANPPaintInterfaceV0     gPaintI;
40 extern ANPSurfaceInterfaceV0   gSurfaceI;
41 extern ANPSystemInterfaceV0    gSystemI;
42 extern ANPTypefaceInterfaceV0  gTypefaceI;
43 extern ANPWindowInterfaceV0    gWindowI;
44 
45 #define ARRAY_COUNT(array)      (sizeof(array) / sizeof(array[0]))
46 
getMSecs()47 static uint32_t getMSecs() {
48     struct timeval tv;
49     gettimeofday(&tv, NULL);
50     return (uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds
51 }
52 
53 ///////////////////////////////////////////////////////////////////////////////
54 
BackgroundPlugin(NPP inst)55 BackgroundPlugin::BackgroundPlugin(NPP inst) : SurfaceSubPlugin(inst) {
56 
57     // initialize the drawing surface
58     m_surface = NULL;
59 
60     //initialize bitmap transparency variables
61     mFinishedStageOne   = false;
62     mFinishedStageTwo   = false;
63     mFinishedStageThree = false;
64 
65     // test basic plugin functionality
66     test_logging(); // android logging
67     test_timers();  // plugin timers
68     test_bitmaps(); // android bitmaps
69     test_domAccess();
70     test_javascript();
71     test_loadJavaClass();
72 
73     //register for touch events
74     ANPEventFlags flags = kTouch_ANPEventFlag;
75     NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
76     if (err != NPERR_NO_ERROR) {
77         gLogI.log(kError_ANPLogType, "Error selecting input events.");
78     }
79 }
80 
~BackgroundPlugin()81 BackgroundPlugin::~BackgroundPlugin() {
82     setContext(NULL);
83     destroySurface();
84 }
85 
getSurface()86 jobject BackgroundPlugin::getSurface() {
87 
88     if (m_surface) {
89         return m_surface;
90     }
91 
92     // load the appropriate java class and instantiate it
93     JNIEnv* env = NULL;
94     if (gVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
95         gLogI.log(kError_ANPLogType, " ---- getSurface: failed to get env");
96         return NULL;
97     }
98 
99     const char* className = "com.android.sampleplugin.BackgroundSurface";
100     jclass backgroundClass = gSystemI.loadJavaClass(inst(), className);
101 
102     if(!backgroundClass) {
103         gLogI.log(kError_ANPLogType, " ---- getSurface: failed to load class");
104         return NULL;
105     }
106 
107     jmethodID constructor = env->GetMethodID(backgroundClass, "<init>", "(Landroid/content/Context;)V");
108     jobject backgroundSurface = env->NewObject(backgroundClass, constructor, m_context);
109 
110     if(!backgroundSurface) {
111         gLogI.log(kError_ANPLogType, " ---- getSurface: failed to construct object");
112         return NULL;
113     }
114 
115     m_surface = env->NewGlobalRef(backgroundSurface);
116     return m_surface;
117 }
118 
destroySurface()119 void BackgroundPlugin::destroySurface() {
120     JNIEnv* env = NULL;
121     if (m_surface && gVM->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
122         env->DeleteGlobalRef(m_surface);
123         m_surface = NULL;
124     }
125 }
126 
drawPlugin(int surfaceWidth,int surfaceHeight)127 void BackgroundPlugin::drawPlugin(int surfaceWidth, int surfaceHeight) {
128 
129     // get the plugin's dimensions according to the DOM
130     PluginObject *obj = (PluginObject*) inst()->pdata;
131     const int W = obj->window->width;
132     const int H = obj->window->height;
133 
134     // compute the current zoom level
135     const float zoomFactorW = static_cast<float>(surfaceWidth) / W;
136     const float zoomFactorH = static_cast<float>(surfaceHeight) / H;
137 
138     // check to make sure the zoom level is uniform
139     if (zoomFactorW + .01 < zoomFactorH && zoomFactorW - .01 > zoomFactorH)
140         gLogI.log(kError_ANPLogType, " ------ %p zoom is out of sync (%f,%f)",
141                   inst(), zoomFactorW, zoomFactorH);
142 
143     // scale the variables based on the zoom level
144     const int fontSize = (int)(zoomFactorW * 16);
145     const int leftMargin = (int)(zoomFactorW * 10);
146 
147     // lock the surface
148     ANPBitmap bitmap;
149     JNIEnv* env = NULL;
150     if (!m_surface || gVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK ||
151         !gSurfaceI.lock(env, m_surface, &bitmap, NULL)) {
152         gLogI.log(kError_ANPLogType, " ------ %p unable to lock the plugin", inst());
153         return;
154     }
155 
156     // create a canvas
157     ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
158     gCanvasI.drawColor(canvas, 0xFFFFFFFF);
159 
160     ANPPaint* paint = gPaintI.newPaint();
161     gPaintI.setFlags(paint, gPaintI.getFlags(paint) | kAntiAlias_ANPPaintFlag);
162     gPaintI.setColor(paint, 0xFFFF0000);
163     gPaintI.setTextSize(paint, fontSize);
164 
165     ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
166     gPaintI.setTypeface(paint, tf);
167     gTypefaceI.unref(tf);
168 
169     ANPFontMetrics fm;
170     gPaintI.getFontMetrics(paint, &fm);
171 
172     gPaintI.setColor(paint, 0xFF0000FF);
173     const char c[] = "This is a background plugin.";
174     gCanvasI.drawText(canvas, c, sizeof(c)-1, leftMargin, -fm.fTop, paint);
175 
176     // clean up variables and unlock the surface
177     gPaintI.deletePaint(paint);
178     gCanvasI.deleteCanvas(canvas);
179     gSurfaceI.unlock(env, m_surface);
180 }
181 
handleEvent(const ANPEvent * evt)182 int16_t BackgroundPlugin::handleEvent(const ANPEvent* evt) {
183     switch (evt->eventType) {
184         case kDraw_ANPEventType:
185             gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request draw events", inst());
186             break;
187         case kLifecycle_ANPEventType:
188             switch (evt->data.lifecycle.action)  {
189                 case kOnLoad_ANPLifecycleAction:
190                     gLogI.log(kDebug_ANPLogType, " ------ %p onLoad", inst());
191                     return 1;
192                 case kOnScreen_ANPLifecycleAction:
193                     gLogI.log(kDebug_ANPLogType, " ------ %p onScreen", inst());
194                     return 1;
195                 case kOffScreen_ANPLifecycleAction:
196                     gLogI.log(kDebug_ANPLogType, " ------ %p offScreen", inst());
197                     return 1;
198             }
199             break; // end kLifecycle_ANPEventType
200         case kTouch_ANPEventType:
201             if (kLongPress_ANPTouchAction == evt->data.touch.action) {
202                 browser->geturl(inst(), "javascript:alert('Detected long press event.')", 0);
203                 gWindowI.requestFullScreen(inst());
204             }
205             else if (kDoubleTap_ANPTouchAction == evt->data.touch.action)
206                 browser->geturl(inst(), "javascript:alert('Detected double tap event.')", 0);
207             break;
208         case kKey_ANPEventType:
209             gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request key events", inst());
210             break;
211         default:
212             break;
213     }
214     return 0;   // unknown or unhandled event
215 }
216 
217 ///////////////////////////////////////////////////////////////////////////////
218 // LOGGING TESTS
219 ///////////////////////////////////////////////////////////////////////////////
220 
221 
test_logging()222 void BackgroundPlugin::test_logging() {
223     NPP instance = this->inst();
224 
225     //LOG_ERROR(instance, " ------ %p Testing Log Error", instance);
226     gLogI.log(kError_ANPLogType, " ------ %p Testing Log Error", instance);
227     gLogI.log(kWarning_ANPLogType, " ------ %p Testing Log Warning", instance);
228     gLogI.log(kDebug_ANPLogType, " ------ %p Testing Log Debug", instance);
229 }
230 
231 ///////////////////////////////////////////////////////////////////////////////
232 // TIMER TESTS
233 ///////////////////////////////////////////////////////////////////////////////
234 
235 #define TIMER_INTERVAL     50
236 static void timer_oneshot(NPP instance, uint32_t timerID);
237 static void timer_repeat(NPP instance, uint32_t timerID);
238 static void timer_neverfires(NPP instance, uint32_t timerID);
239 static void timer_latency(NPP instance, uint32_t timerID);
240 
test_timers()241 void BackgroundPlugin::test_timers() {
242     NPP instance = this->inst();
243 
244     //Setup the testing counters
245     mTimerRepeatCount = 5;
246     mTimerLatencyCount = 5;
247 
248     // test for bogus timerID
249     browser->unscheduletimer(instance, 999999);
250     // test one-shot
251     browser->scheduletimer(instance, 100, false, timer_oneshot);
252     // test repeat
253     browser->scheduletimer(instance, 50, true, timer_repeat);
254     // test timer latency
255     browser->scheduletimer(instance, TIMER_INTERVAL, true, timer_latency);
256     mStartTime = mPrevTime = getMSecs();
257     // test unschedule immediately
258     uint32_t id = browser->scheduletimer(instance, 100, false, timer_neverfires);
259     browser->unscheduletimer(instance, id);
260     // test double unschedule (should be no-op)
261     browser->unscheduletimer(instance, id);
262 
263 }
264 
timer_oneshot(NPP instance,uint32_t timerID)265 static void timer_oneshot(NPP instance, uint32_t timerID) {
266     gLogI.log(kDebug_ANPLogType, "-------- oneshot timer\n");
267 }
268 
timer_repeat(NPP instance,uint32_t timerID)269 static void timer_repeat(NPP instance, uint32_t timerID) {
270     BackgroundPlugin *obj = ((BackgroundPlugin*) ((PluginObject*) instance->pdata)->activePlugin);
271 
272     gLogI.log(kDebug_ANPLogType, "-------- repeat timer %d\n",
273               obj->mTimerRepeatCount);
274     if (--obj->mTimerRepeatCount == 0) {
275         browser->unscheduletimer(instance, timerID);
276     }
277 }
278 
timer_neverfires(NPP instance,uint32_t timerID)279 static void timer_neverfires(NPP instance, uint32_t timerID) {
280     gLogI.log(kError_ANPLogType, "-------- timer_neverfires!!!\n");
281 }
282 
timer_latency(NPP instance,uint32_t timerID)283 static void timer_latency(NPP instance, uint32_t timerID) {
284     BackgroundPlugin *obj = ((BackgroundPlugin*) ((PluginObject*) instance->pdata)->activePlugin);
285 
286     obj->mTimerLatencyCurrentCount += 1;
287 
288     uint32_t now = getMSecs();
289     uint32_t interval = now - obj->mPrevTime;
290     uint32_t dur = now - obj->mStartTime;
291     uint32_t expectedDur = obj->mTimerLatencyCurrentCount * TIMER_INTERVAL;
292     int32_t drift = dur - expectedDur;
293     int32_t avgDrift = drift / obj->mTimerLatencyCurrentCount;
294 
295     obj->mPrevTime = now;
296 
297     gLogI.log(kDebug_ANPLogType,
298               "-------- latency test: [%3d] interval %d expected %d, total %d expected %d, drift %d avg %d\n",
299               obj->mTimerLatencyCurrentCount, interval, TIMER_INTERVAL, dur,
300               expectedDur, drift, avgDrift);
301 
302     if (--obj->mTimerLatencyCount == 0) {
303         browser->unscheduletimer(instance, timerID);
304     }
305 }
306 
307 ///////////////////////////////////////////////////////////////////////////////
308 // BITMAP TESTS
309 ///////////////////////////////////////////////////////////////////////////////
310 
311 static void test_formats(NPP instance);
312 
test_bitmaps()313 void BackgroundPlugin::test_bitmaps() {
314     test_formats(this->inst());
315 }
316 
test_formats(NPP instance)317 static void test_formats(NPP instance) {
318 
319     // TODO pull names from enum in npapi instead of hardcoding them
320     static const struct {
321         ANPBitmapFormat fFormat;
322         const char*     fName;
323     } gRecs[] = {
324         { kUnknown_ANPBitmapFormat,   "unknown" },
325         { kRGBA_8888_ANPBitmapFormat, "8888" },
326         { kRGB_565_ANPBitmapFormat,   "565" },
327     };
328 
329     ANPPixelPacking packing;
330     for (size_t i = 0; i < ARRAY_COUNT(gRecs); i++) {
331         if (gBitmapI.getPixelPacking(gRecs[i].fFormat, &packing)) {
332             gLogI.log(kDebug_ANPLogType,
333                       "pixel format [%d] %s has packing ARGB [%d %d] [%d %d] [%d %d] [%d %d]\n",
334                       gRecs[i].fFormat, gRecs[i].fName,
335                       packing.AShift, packing.ABits,
336                       packing.RShift, packing.RBits,
337                       packing.GShift, packing.GBits,
338                       packing.BShift, packing.BBits);
339         } else {
340             gLogI.log(kDebug_ANPLogType,
341                       "pixel format [%d] %s has no packing\n",
342                       gRecs[i].fFormat, gRecs[i].fName);
343         }
344     }
345 }
346 
test_bitmap_transparency(const ANPEvent * evt)347 void BackgroundPlugin::test_bitmap_transparency(const ANPEvent* evt) {
348     NPP instance = this->inst();
349 
350     // check default & set transparent
351     if (!mFinishedStageOne) {
352 
353         gLogI.log(kDebug_ANPLogType, "BEGIN: testing bitmap transparency");
354 
355         //check to make sure it is not transparent
356         if (evt->data.draw.data.bitmap.format == kRGBA_8888_ANPBitmapFormat) {
357             gLogI.log(kError_ANPLogType, "bitmap default format is transparent");
358         }
359 
360         //make it transparent (any non-null value will set it to true)
361         bool value = true;
362         NPError err = browser->setvalue(instance, NPPVpluginTransparentBool, &value);
363         if (err != NPERR_NO_ERROR) {
364             gLogI.log(kError_ANPLogType, "Error setting transparency.");
365         }
366 
367         mFinishedStageOne = true;
368         browser->invalidaterect(instance, NULL);
369     }
370     // check transparent & set opaque
371     else if (!mFinishedStageTwo) {
372 
373         //check to make sure it is transparent
374         if (evt->data.draw.data.bitmap.format != kRGBA_8888_ANPBitmapFormat) {
375             gLogI.log(kError_ANPLogType, "bitmap did not change to transparent format");
376         }
377 
378         //make it opaque
379         NPError err = browser->setvalue(instance, NPPVpluginTransparentBool, NULL);
380         if (err != NPERR_NO_ERROR) {
381             gLogI.log(kError_ANPLogType, "Error setting transparency.");
382         }
383 
384         mFinishedStageTwo = true;
385     }
386     // check opaque
387     else if (!mFinishedStageThree) {
388 
389         //check to make sure it is not transparent
390         if (evt->data.draw.data.bitmap.format == kRGBA_8888_ANPBitmapFormat) {
391             gLogI.log(kError_ANPLogType, "bitmap default format is transparent");
392         }
393 
394         gLogI.log(kDebug_ANPLogType, "END: testing bitmap transparency");
395 
396         mFinishedStageThree = true;
397     }
398 }
399 
400 ///////////////////////////////////////////////////////////////////////////////
401 // DOM TESTS
402 ///////////////////////////////////////////////////////////////////////////////
403 
test_domAccess()404 void BackgroundPlugin::test_domAccess() {
405     NPP instance = this->inst();
406 
407     gLogI.log(kDebug_ANPLogType, " ------ %p Testing DOM Access", instance);
408 
409     // Get the plugin's DOM object
410     NPObject* windowObject = NULL;
411     browser->getvalue(instance, NPNVWindowNPObject, &windowObject);
412 
413     if (!windowObject)
414         gLogI.log(kError_ANPLogType, " ------ %p Unable to retrieve DOM Window", instance);
415 
416     // Retrieve a property from the plugin's DOM object
417     NPIdentifier topIdentifier = browser->getstringidentifier("top");
418     NPVariant topObjectVariant;
419     browser->getproperty(instance, windowObject, topIdentifier, &topObjectVariant);
420 
421     if (topObjectVariant.type != NPVariantType_Object)
422         gLogI.log(kError_ANPLogType, " ------ %p Invalid Variant type for DOM Property: %d,%d", instance, topObjectVariant.type, NPVariantType_Object);
423 }
424 
425 
426 ///////////////////////////////////////////////////////////////////////////////
427 // JAVASCRIPT TESTS
428 ///////////////////////////////////////////////////////////////////////////////
429 
430 
test_javascript()431 void BackgroundPlugin::test_javascript() {
432     NPP instance = this->inst();
433 
434     gLogI.log(kDebug_ANPLogType, " ------ %p Testing JavaScript Access", instance);
435 
436     // Get the plugin's DOM object
437     NPObject* windowObject = NULL;
438     browser->getvalue(instance, NPNVWindowNPObject, &windowObject);
439 
440     if (!windowObject)
441         gLogI.log(kError_ANPLogType, " ------ %p Unable to retrieve DOM Window", instance);
442 
443     // create a string (JS code) that is stored in memory allocated by the browser
444     const char* jsString = "1200 + 34";
445     void* stringMem = browser->memalloc(strlen(jsString));
446     memcpy(stringMem, jsString, strlen(jsString));
447 
448     // execute the javascript in the plugin's DOM object
449     NPString script = { (char*)stringMem, strlen(jsString) };
450     NPVariant scriptVariant;
451     if (!browser->evaluate(instance, windowObject, &script, &scriptVariant))
452         gLogI.log(kError_ANPLogType, " ------ %p Unable to eval the JS.", instance);
453 
454     if (scriptVariant.type == NPVariantType_Int32) {
455         if (scriptVariant.value.intValue != 1234)
456             gLogI.log(kError_ANPLogType, " ------ %p Invalid Value for JS Return: %d,1234", instance, scriptVariant.value.intValue);
457     } else {
458         gLogI.log(kError_ANPLogType, " ------ %p Invalid Variant type for JS Return: %d,%d", instance, scriptVariant.type, NPVariantType_Int32);
459     }
460 
461     // free the memory allocated within the browser
462     browser->memfree(stringMem);
463 }
464 
465 ///////////////////////////////////////////////////////////////////////////////
466 // Load Java Classes Tests
467 ///////////////////////////////////////////////////////////////////////////////
468 
test_loadJavaClass()469 void BackgroundPlugin::test_loadJavaClass() {
470 
471     JNIEnv* env = NULL;
472     if (gVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
473         gLogI.log(kError_ANPLogType, " ---- LoadJavaTest: failed to get env");
474         return;
475     }
476 
477     const char* className = "com.android.sampleplugin.BackgroundTest";
478     jclass backgroundClass = gSystemI.loadJavaClass(inst(), className);
479 
480     if(!backgroundClass) {
481         gLogI.log(kError_ANPLogType, " ---- LoadJavaTest: failed to load class");
482         return;
483     }
484 
485     jmethodID constructor = env->GetMethodID(backgroundClass, "<init>", "()V");
486     jmethodID addMethod = env->GetMethodID(backgroundClass, "addInt", "(II)I");
487     jobject backgroundObject = env->NewObject(backgroundClass, constructor);
488 
489     if(!backgroundObject) {
490         gLogI.log(kError_ANPLogType, " ---- LoadJavaTest: failed to construct object");
491         return;
492     }
493 
494     jint result = env->CallIntMethod(backgroundObject, addMethod, 2, 2);
495 
496     if (result != 4) {
497         gLogI.log(kError_ANPLogType, " ---- LoadJavaTest: invalid result (%d != 4)", result);
498     }
499 }
500