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 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 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 81 BackgroundPlugin::~BackgroundPlugin() { 82 setContext(NULL); 83 destroySurface(); 84 } 85 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 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 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 182 int16 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 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 timerID); 237 static void timer_repeat(NPP instance, uint32 timerID); 238 static void timer_neverfires(NPP instance, uint32 timerID); 239 static void timer_latency(NPP instance, uint32 timerID); 240 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 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 265 static void timer_oneshot(NPP instance, uint32 timerID) { 266 gLogI.log(kDebug_ANPLogType, "-------- oneshot timer\n"); 267 } 268 269 static void timer_repeat(NPP instance, uint32 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 279 static void timer_neverfires(NPP instance, uint32 timerID) { 280 gLogI.log(kError_ANPLogType, "-------- timer_neverfires!!!\n"); 281 } 282 283 static void timer_latency(NPP instance, uint32 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 313 void BackgroundPlugin::test_bitmaps() { 314 test_formats(this->inst()); 315 } 316 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 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 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 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 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