1 /* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Zan Dobersek <zandobersek (at) gmail.com> 4 * Copyright (C) 2009 Holger Hans Peter Freyther 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "config.h" 29 #include "PluginObject.h" 30 #include "PluginTest.h" 31 32 #include "npapi.h" 33 #include "npruntime.h" 34 #include "npfunctions.h" 35 36 #include <stdarg.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <stdlib.h> 40 #include <X11/Xlib.h> 41 #include <X11/Xutil.h> 42 #include <string> 43 44 using namespace std; 45 46 extern "C" { 47 NPError NP_Initialize (NPNetscapeFuncs *aMozillaVTable, NPPluginFuncs *aPluginVTable); 48 NPError NP_Shutdown(void); 49 NPError NP_GetValue(void *future, NPPVariable variable, void *value); 50 char* NP_GetMIMEDescription(void); 51 } 52 53 static void executeScript(const PluginObject* obj, const char* script); 54 55 static NPError 56 webkit_test_plugin_new_instance(NPMIMEType mimetype, 57 NPP instance, 58 uint16_t mode, 59 int16_t argc, 60 char *argn[], 61 char *argv[], 62 NPSavedData* savedData) 63 { 64 if (browser->version >= 14) { 65 PluginObject* obj = (PluginObject*)browser->createobject(instance, getPluginClass()); 66 instance->pdata = obj; 67 68 string testIdentifier; 69 70 for (int i = 0; i < argc; i++) { 71 if (strcasecmp(argn[i], "test") == 0) 72 testIdentifier = argv[i]; 73 else if (strcasecmp(argn[i], "onstreamload") == 0 && !obj->onStreamLoad) 74 obj->onStreamLoad = strdup(argv[i]); 75 else if (strcasecmp(argn[i], "onStreamDestroy") == 0 && !obj->onStreamDestroy) 76 obj->onStreamDestroy = strdup(argv[i]); 77 else if (strcasecmp(argn[i], "onURLNotify") == 0 && !obj->onURLNotify) 78 obj->onURLNotify = strdup(argv[i]); 79 else if (strcasecmp(argn[i], "src") == 0 && 80 strcasecmp(argv[i], "data:application/x-webkit-test-netscape,returnerrorfromnewstream") == 0) 81 obj->returnErrorFromNewStream = TRUE; 82 else if (!strcasecmp(argn[i], "src") 83 && !strcasecmp(argv[i], "data:application/x-webkit-test-netscape,alertwhenloaded")) 84 executeScript(obj, "alert('Plugin Loaded!')"); 85 else if (strcasecmp(argn[i], "logfirstsetwindow") == 0) 86 obj->logSetWindow = TRUE; 87 else if (strcasecmp(argn[i], "testnpruntime") == 0) 88 testNPRuntime(instance); 89 else if (strcasecmp(argn[i], "logSrc") == 0) { 90 for (int i = 0; i < argc; i++) 91 if (strcasecmp(argn[i], "src") == 0) 92 pluginLog(instance, "src: %s", argv[i]); 93 } else if (strcasecmp(argn[i], "cleardocumentduringnew") == 0) 94 executeScript(obj, "document.body.innerHTML = ''"); 95 else if (!strcasecmp(argn[i], "ondestroy")) 96 obj->onDestroy = strdup(argv[i]); 97 else if (strcasecmp(argn[i], "testwindowopen") == 0) 98 obj->testWindowOpen = TRUE; 99 else if (strcasecmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow) 100 obj->onSetWindow = strdup(argv[i]); 101 } 102 103 browser->getvalue(instance, NPNVprivateModeBool, (void *)&obj->cachedPrivateBrowsingMode); 104 105 obj->pluginTest = PluginTest::create(instance, testIdentifier); 106 107 return obj->pluginTest->NPP_New(mimetype, mode, argc, argn, argv, savedData); 108 } 109 110 return NPERR_NO_ERROR; 111 } 112 113 static NPError 114 webkit_test_plugin_destroy_instance(NPP instance, NPSavedData** save) 115 { 116 PluginObject* obj = static_cast<PluginObject*>(instance->pdata); 117 if (obj) { 118 if (obj->onDestroy) { 119 executeScript(obj, obj->onDestroy); 120 free(obj->onDestroy); 121 } 122 123 if (obj->onStreamLoad) 124 free(obj->onStreamLoad); 125 126 if (obj->onStreamDestroy) 127 free(obj->onStreamDestroy); 128 129 if (obj->onURLNotify) 130 free(obj->onURLNotify); 131 132 if (obj->logDestroy) 133 pluginLog(instance, "NPP_Destroy"); 134 135 if (obj->onSetWindow) 136 free(obj->onSetWindow); 137 138 obj->pluginTest->NPP_Destroy(save); 139 140 browser->releaseobject(&obj->header); 141 } 142 143 return NPERR_NO_ERROR; 144 } 145 146 static NPError 147 webkit_test_plugin_set_window(NPP instance, NPWindow *window) 148 { 149 PluginObject* obj = static_cast<PluginObject*>(instance->pdata); 150 151 if (obj) { 152 obj->lastWindow = *window; 153 154 if (obj->logSetWindow) { 155 pluginLog(instance, "NPP_SetWindow: %d %d", (int)window->width, (int)window->height); 156 obj->logSetWindow = false; 157 } 158 if (obj->onSetWindow) 159 executeScript(obj, obj->onSetWindow); 160 161 if (obj->testWindowOpen) { 162 testWindowOpen(instance); 163 obj->testWindowOpen = FALSE; 164 } 165 166 } 167 168 return obj->pluginTest->NPP_SetWindow(instance, window); 169 } 170 171 static void executeScript(const PluginObject* obj, const char* script) 172 { 173 NPObject *windowScriptObject; 174 browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject); 175 176 NPString npScript; 177 npScript.UTF8Characters = script; 178 npScript.UTF8Length = strlen(script); 179 180 NPVariant browserResult; 181 browser->evaluate(obj->npp, windowScriptObject, &npScript, &browserResult); 182 browser->releasevariantvalue(&browserResult); 183 } 184 185 static NPError 186 webkit_test_plugin_new_stream(NPP instance, 187 NPMIMEType /*type*/, 188 NPStream *stream, 189 NPBool /*seekable*/, 190 uint16_t* stype) 191 { 192 PluginObject* obj = static_cast<PluginObject*>(instance->pdata); 193 obj->stream = stream; 194 *stype = NP_NORMAL; 195 196 if (obj->returnErrorFromNewStream) 197 return NPERR_GENERIC_ERROR; 198 199 if (browser->version >= NPVERS_HAS_RESPONSE_HEADERS) 200 notifyStream(obj, stream->url, stream->headers); 201 202 if (obj->onStreamLoad) 203 executeScript(obj, obj->onStreamLoad); 204 205 return NPERR_NO_ERROR; 206 } 207 208 static NPError 209 webkit_test_plugin_destroy_stream(NPP instance, NPStream* stream, NPError reason) 210 { 211 PluginObject* obj = (PluginObject*)instance->pdata; 212 213 if (obj->onStreamDestroy) { 214 NPObject* windowObject = 0; 215 NPError error = browser->getvalue(instance, NPNVWindowNPObject, &windowObject); 216 217 if (error == NPERR_NO_ERROR) { 218 NPVariant onStreamDestroyVariant; 219 if (browser->getproperty(instance, windowObject, browser->getstringidentifier(obj->onStreamDestroy), &onStreamDestroyVariant)) { 220 if (NPVARIANT_IS_OBJECT(onStreamDestroyVariant)) { 221 NPObject* onStreamDestroyFunction = NPVARIANT_TO_OBJECT(onStreamDestroyVariant); 222 223 NPVariant reasonVariant; 224 INT32_TO_NPVARIANT(reason, reasonVariant); 225 226 NPVariant result; 227 browser->invokeDefault(instance, onStreamDestroyFunction, &reasonVariant, 1, &result); 228 browser->releasevariantvalue(&result); 229 } 230 browser->releasevariantvalue(&onStreamDestroyVariant); 231 } 232 browser->releaseobject(windowObject); 233 } 234 } 235 236 return obj->pluginTest->NPP_DestroyStream(stream, reason); 237 } 238 239 static void 240 webkit_test_plugin_stream_as_file(NPP /*instance*/, NPStream* /*stream*/, const char* /*fname*/) 241 { 242 } 243 244 static int32_t 245 webkit_test_plugin_write_ready(NPP /*instance*/, NPStream* /*stream*/) 246 { 247 return 4096; 248 } 249 250 static int32_t 251 webkit_test_plugin_write(NPP instance, 252 NPStream* /*stream*/, 253 int32_t /*offset*/, 254 int32_t len, 255 void* /*buffer*/) 256 { 257 PluginObject* obj = (PluginObject*)instance->pdata; 258 259 if (obj->returnNegativeOneFromWrite) 260 return -1; 261 262 return len; 263 } 264 265 static void 266 webkit_test_plugin_print(NPP /*instance*/, NPPrint* /*platformPrint*/) 267 { 268 } 269 270 static char keyEventToChar(XKeyEvent* event) 271 { 272 char c = ' '; 273 XLookupString(event, &c, sizeof(c), 0, 0); 274 return c; 275 } 276 277 static int16_t 278 webkit_test_plugin_handle_event(NPP instance, void* event) 279 { 280 PluginObject* obj = static_cast<PluginObject*>(instance->pdata); 281 if (!obj->eventLogging) 282 return 0; 283 284 XEvent* evt = static_cast<XEvent*>(event); 285 286 switch (evt->type) { 287 case ButtonRelease: 288 pluginLog(instance, "mouseUp at (%d, %d)", evt->xbutton.x, evt->xbutton.y); 289 break; 290 case ButtonPress: 291 pluginLog(instance, "mouseDown at (%d, %d)", evt->xbutton.x, evt->xbutton.y); 292 break; 293 case KeyRelease: 294 pluginLog(instance, "keyUp '%c'", keyEventToChar(&evt->xkey)); 295 break; 296 case KeyPress: 297 pluginLog(instance, "keyDown '%c'", keyEventToChar(&evt->xkey)); 298 break; 299 case MotionNotify: 300 case EnterNotify: 301 case LeaveNotify: 302 break; 303 case FocusIn: 304 pluginLog(instance, "getFocusEvent"); 305 break; 306 case FocusOut: 307 pluginLog(instance, "loseFocusEvent"); 308 break; 309 default: 310 pluginLog(instance, "event %d", evt->type); 311 } 312 313 return 0; 314 } 315 316 static void 317 webkit_test_plugin_url_notify(NPP instance, const char* url, NPReason reason, void* notifyData) 318 { 319 PluginObject* obj = static_cast<PluginObject*>(instance->pdata); 320 321 if (obj->onURLNotify) 322 executeScript(obj, obj->onURLNotify); 323 324 handleCallback(obj, url, reason, notifyData); 325 } 326 327 static NPError 328 webkit_test_plugin_get_value(NPP instance, NPPVariable variable, void *value) 329 { 330 PluginObject* obj = 0; 331 if (instance) 332 obj = static_cast<PluginObject*>(instance->pdata); 333 334 // First, check if the PluginTest object supports getting this value. 335 if (obj && obj->pluginTest->NPP_GetValue(variable, value) == NPERR_NO_ERROR) 336 return NPERR_NO_ERROR; 337 338 NPError err = NPERR_NO_ERROR; 339 340 switch (variable) { 341 case NPPVpluginNameString: 342 *((char **)value) = const_cast<char*>("WebKit Test PlugIn"); 343 break; 344 case NPPVpluginDescriptionString: 345 *((char **)value) = const_cast<char*>("Simple Netscape plug-in that handles test content for WebKit"); 346 break; 347 case NPPVpluginNeedsXEmbed: 348 *((NPBool *)value) = TRUE; 349 break; 350 case NPPVpluginScriptableIID: 351 case NPPVpluginScriptableInstance: 352 case NPPVpluginScriptableNPObject: 353 err = NPERR_GENERIC_ERROR; 354 break; 355 default: 356 err = NPERR_GENERIC_ERROR; 357 break; 358 } 359 360 if (variable == NPPVpluginScriptableNPObject) { 361 void **v = (void **)value; 362 browser->retainobject((NPObject *)obj); 363 *v = obj; 364 err = NPERR_NO_ERROR; 365 } 366 367 return err; 368 } 369 370 static NPError 371 webkit_test_plugin_set_value(NPP instance, NPNVariable variable, void* value) 372 { 373 PluginObject* obj = static_cast<PluginObject*>(instance->pdata); 374 375 switch (variable) { 376 case NPNVprivateModeBool: 377 obj->cachedPrivateBrowsingMode = *(NPBool*)value; 378 return NPERR_NO_ERROR; 379 default: 380 return NPERR_GENERIC_ERROR; 381 } 382 } 383 384 char * 385 NP_GetMIMEDescription(void) 386 { 387 // We sentence-case the mime-type here to ensure that ports are not 388 // case-sensitive when loading plugins. See https://webkit.org/b/36815 389 return const_cast<char*>("application/x-Webkit-Test-Netscape:testnetscape:test netscape content"); 390 } 391 392 NPError 393 NP_Initialize (NPNetscapeFuncs *aMozillaVTable, NPPluginFuncs *aPluginVTable) 394 { 395 if (aMozillaVTable == NULL || aPluginVTable == NULL) 396 return NPERR_INVALID_FUNCTABLE_ERROR; 397 398 if ((aMozillaVTable->version >> 8) > NP_VERSION_MAJOR) 399 return NPERR_INCOMPATIBLE_VERSION_ERROR; 400 401 if (aPluginVTable->size < sizeof (NPPluginFuncs)) 402 return NPERR_INVALID_FUNCTABLE_ERROR; 403 404 browser = aMozillaVTable; 405 pluginFunctions = aPluginVTable; 406 407 aPluginVTable->size = sizeof (NPPluginFuncs); 408 aPluginVTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; 409 aPluginVTable->newp = webkit_test_plugin_new_instance; 410 aPluginVTable->destroy = webkit_test_plugin_destroy_instance; 411 aPluginVTable->setwindow = webkit_test_plugin_set_window; 412 aPluginVTable->newstream = webkit_test_plugin_new_stream; 413 aPluginVTable->destroystream = webkit_test_plugin_destroy_stream; 414 aPluginVTable->asfile = webkit_test_plugin_stream_as_file; 415 aPluginVTable->writeready = webkit_test_plugin_write_ready; 416 aPluginVTable->write = webkit_test_plugin_write; 417 aPluginVTable->print = webkit_test_plugin_print; 418 aPluginVTable->event = webkit_test_plugin_handle_event; 419 aPluginVTable->urlnotify = webkit_test_plugin_url_notify; 420 aPluginVTable->javaClass = NULL; 421 aPluginVTable->getvalue = webkit_test_plugin_get_value; 422 aPluginVTable->setvalue = webkit_test_plugin_set_value; 423 424 return NPERR_NO_ERROR; 425 } 426 427 NPError 428 NP_Shutdown(void) 429 { 430 return NPERR_NO_ERROR; 431 } 432 433 NPError 434 NP_GetValue(void* /*future*/, NPPVariable variable, void *value) 435 { 436 return webkit_test_plugin_get_value(NULL, variable, value); 437 } 438