diff options
Diffstat (limited to 'testplugin/testplugin.c')
-rw-r--r-- | testplugin/testplugin.c | 671 |
1 files changed, 671 insertions, 0 deletions
diff --git a/testplugin/testplugin.c b/testplugin/testplugin.c new file mode 100644 index 0000000..7132be4 --- /dev/null +++ b/testplugin/testplugin.c @@ -0,0 +1,671 @@ +/* $Xorg: testplugin.c,v 1.4 2001/02/09 02:05:58 xorgcvs Exp $ */ +/* + +Copyright 1996, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- +ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABIL- +ITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization from +The Open Group. + +*/ +/* + * This is a (not so) basic program to "test" a netscape plugin. + * It exercises the plugin in a way close (I hope) to how netscape does. + * It is designed to allow minimal debugging of the plugin, with the + * possibility of using purify. + * + * Arnaud Le Hors + */ + +#include <stdio.h> +#include <Xm/Form.h> +#include <Xm/DrawingA.h> +#include <Xm/ScrolledW.h> +#include <Xm/PushB.h> +#ifdef SUPPORT_EDITRES +#include <X11/Xmu/Editres.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <unistd.h> +#if defined(SYSV) || defined(SVR4) +#include <string.h> +#else +#include <strings.h> +#endif + +#include "npapi.h" + +/* default mime type */ +#ifndef MIMETYPE +#define MIMETYPE "undefined" +#endif + +/* define how many bytes should be read at most */ +#ifndef READNBYTES +#define READNBYTES 100 +#endif + +/* default plugin size */ +#define DEFAULT_WIDTH 50 +#define DEFAULT_HEIGHT 50 + + +#ifdef PLUGIN_TRACE +#define PRINTTRACE(msg) fprintf(stderr, msg) +#else +#define PRINTTRACE(msg) +#endif + +/* different possible states */ +typedef enum { + SETUP, SETUPWINDOW, PROCESSINPUT, SETUPSTREAM, PROCESSREPLY, DONE +} State; + +/* main structure */ +typedef struct { + State state; + char **plugin_argn; + char **plugin_argv; + int plugin_argc; + uint32 width; + uint32 height; + Widget plugin; + char *src; + NPStream **streams; + int nstreams; + int streams_len; + Bool setup_window; +} AppData; + +/* create plugin instance */ +void +SetupPlugin(NPP instance) +{ + AppData *data = (AppData *)instance->ndata; + NPError status; + + /* NPError NPP_Initialize(void) */ + PRINTTRACE(" NPP_Initialize\n"); + status = NPP_Initialize(); + + /* + * NPError NPP_New(NPMIMEType *pluginType, NPP instance, uint16 mode, + * int16 argc, char *argn[], char *argv[], NPSavedData *saved) + */ + PRINTTRACE(" NPP_New\n"); + status = NPP_New(MIMETYPE, instance, 0, + data->plugin_argc, data->plugin_argn, data->plugin_argv, + NULL); +} + + +void +SetupPluginWindow(NPP instance) +{ + AppData *data = (AppData *)instance->ndata; + NPError status; + NPSetWindowCallbackStruct window_data; + NPWindow window; + + /* setup window structure */ + window_data.type = 0; /* what's this supposed to be ? */ + window_data.display = XtDisplay(data->plugin); + window_data.visual = DefaultVisual(window_data.display, + DefaultScreen(window_data.display)); + window_data.colormap = DefaultColormap(window_data.display, + DefaultScreen(window_data.display)); + window_data.depth = DefaultDepth(window_data.display, + DefaultScreen(window_data.display)); + + window.window = (void *) XtWindow(data->plugin); + window.x = 0; + window.y = 0; + window.width = data->width; + window.height = data->height; + window.ws_info = (void *) &window_data; + + /* NPError NPP_SetWindow(NPP instance, NPWindow *window) */ + PRINTTRACE(" NPP_SetWindow\n"); + status = NPP_SetWindow(instance, &window); +} + +void +MakeStream(NPP instance, const char *url, const char *window, FILE *fp) +{ + AppData *data = (AppData *)instance->ndata; + NPStream *stream, **ptr; + char *urlcopy; + + /* setup a stream */ + stream = (NPStream *) malloc(sizeof(NPStream)); + if (stream == NULL) { + fprintf(stderr, "cannot malloc enough memory: %d\n", + sizeof(NPStream)); + exit(1); + } + + urlcopy = (char *) malloc(strlen(url) + 1); + if (urlcopy == NULL) { + fprintf(stderr, "cannot malloc enough memory: %d\n", + strlen(url) + 1); + exit(1); + } + strcpy(urlcopy, url); + + stream->ndata = (void *) fp; + stream->pdata = NULL; + stream->url = urlcopy; + /* if target window different from plugin window throw reply away */ + stream->end = (window == NULL) ? 0 : -1; + stream->lastmodified = 0; /* I don't have this info */ + stream->notifyData = NULL; /* I don't support this for now */ + + /* add it to the queue */ + data->nstreams++; + if (data->nstreams > data->streams_len) { + ptr = (NPStream **) realloc(data->streams, + sizeof(NPStream *) * data->nstreams); + if (ptr == NULL) { + fprintf(stderr, "cannot malloc enough memory: %d\n", + sizeof(NPStream *) * data->nstreams); + exit(1); + } + data->streams = ptr; + data->streams_len = data->nstreams; + } + data->streams[data->nstreams - 1] = stream; +} + +void +DestroyStream(NPP instance, int i) +{ + AppData *data = (AppData *)instance->ndata; + /* + * NPError NPP_DestroyStream(NPP instance, NPStream *stream, + * NPError reason) + */ + PRINTTRACE(" NPP_DestroyStream\n"); + NPP_DestroyStream(instance, data->streams[i], NPRES_DONE); + if (data->streams[i]->url != NULL) + free((void *)data->streams[i]->url); + free(data->streams[i]); +} + +void +CloseStream(NPP instance, int i) +{ + AppData *data = (AppData *)instance->ndata; + FILE *fp = (FILE *) data->streams[i]->ndata; + + DestroyStream(instance, 0); + /* remove from queue */ + data->nstreams--; + for (i = 0; i < data->nstreams; i++) + data->streams[i] = data->streams[i + 1]; +} + +void +WriteStreamProc(NPP instance, int *fd, XtInputId *id) +{ + AppData *data = (AppData *)instance->ndata; + FILE *fp = (FILE *) data->streams[0]->ndata; + NPError status; + char buf[BUFSIZ]; + int32 readysize, readb, len; + + /* int32 NPP_WriteReady(NPP instance, NPStream *stream) */ + PRINTTRACE(" NPP_WriteReady\n"); + readysize = NPP_WriteReady(instance, data->streams[0]); + + /* make sure we won't read more than our buf size */ + if (readysize > BUFSIZ) + readysize = BUFSIZ; + + /* force a smaller read size so NPP_Write is called more than once, + this is just for the purpose of testing! */ + if (readysize > READNBYTES) + len = READNBYTES; + else /* take half of the given value */ + len = (readysize >> 1); + + /* read next chunk of data */ + len = fread(buf, sizeof(char), len, fp); + + if (len != 0) { /* send it to plugin */ + /* + * int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, + * int32 len, void *buf); + */ + PRINTTRACE(" NPP_Write\n"); + readb = NPP_Write(instance, data->streams[0], data->streams[0]->end, + len, buf); + + /* plugin should have read all of it since we read less than readysize, + * if it read less than what it claimed it was ready to use bark */ + if (readb < len) { + fprintf(stderr, + "plugin claimed to be ready to read %d but only read %d\n", + readysize, readb); + exit(1); + } + data->streams[0]->end += len; + } else { /* no more to read */ + XtRemoveInput(*id); + data->state = DONE; + } +} + +/* destroy plugin */ +void +ShutdownPlugin(NPP instance) +{ + AppData *data = (AppData *)instance->ndata; + NPSavedData *saved_data = NULL; + + if (data->streams != NULL) { + int i; + for (i = 0; i < data->nstreams; i++) + DestroyStream(instance, i); + free(data->streams); + } + PRINTTRACE(" NPP_Destroy\n"); + NPP_Destroy(instance, &saved_data); + if (saved_data != NULL) + free(saved_data); /* so this is not reported as memory leak */ + PRINTTRACE(" NPP_Shutdown\n"); + NPP_Shutdown(); +} + +/* quit callback: shutdown plugin and exit */ +void +QuitCB(Widget widget, XtPointer closure, XtPointer call_data) +{ + ShutdownPlugin((NPP) closure); + exit(0); +} + +/* resize handler: like Netscape change the plugin widget */ +void +ResizeEH(Widget toplevel, XtPointer client_data, XEvent *event, Boolean *cont) +{ + if (event->type == ConfigureNotify) { + NPP instance = (NPP) client_data; + AppData *data = (AppData *)instance->ndata; + Arg args[10]; + int n; + Widget oldplugin, scrolledW; + Dimension width, height; + + /* remove the current plugin from its parent's geometry management */ + oldplugin = data->plugin; + + if (XtIsRealized(oldplugin) == False) + return; + + /* we only want to deal with resize */ + if (event->xconfigure.x != 0 || event->xconfigure.y != 0) + return; + + scrolledW = XtParent(XtParent(oldplugin)); + XtUnmanageChild(oldplugin); + + /* create a copy of the plugin widget */ + n = 0; + XtSetArg(args[n], XmNwidth, &width); n++; + XtSetArg(args[n], XmNheight, &height); n++; + XtGetValues(oldplugin, args, n); + + n = 0; + XtSetArg(args[n], XmNwidth, width); n++; + XtSetArg(args[n], XmNheight, height); n++; + XtSetArg(args[n], XmNmarginWidth, 0); n++; + XtSetArg(args[n], XmNmarginHeight, 0); n++; + XtSetArg(args[n], XmNresizePolicy, XmRESIZE_NONE); n++; + data->plugin = XmCreateDrawingArea(scrolledW, "plugin", args, n); + + n = 0; + XtSetArg(args[n], XmNworkWindow, data->plugin); n++; + XtSetValues(scrolledW, args, n); + XtManageChild(data->plugin); + + /* then destroy the old plugin widget */ + XtDestroyWidget(oldplugin); + + data->setup_window = True; + } +} + +/* build a GUI with a form containing a ScrolledWindow over a DrawingArea for + the plugin and a button to quit */ +Widget +BuildGUI(Widget toplevel, Dimension width, Dimension height, NPP instance) +{ + Widget form, scrolledW, plugin, button; + Arg args[10]; + int n; + + XtAddRawEventHandler(toplevel, StructureNotifyMask, False, + ResizeEH, instance); + + form = XmCreateForm(toplevel, "form", NULL, 0); + + n = 0; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; + button = XmCreatePushButton(form, "quit", args, n); + XtAddCallback(button, XmNactivateCallback, QuitCB, instance); + + n = 0; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; + XtSetArg(args[n], XmNbottomWidget, button); n++; + XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmSTATIC); n++; + XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); n++; + scrolledW = XmCreateScrolledWindow(form, "scrolledWindow", args, n); + + n = 0; + XtSetArg(args[n], XmNwidth, width); n++; + XtSetArg(args[n], XmNheight, height); n++; + XtSetArg(args[n], XmNmarginWidth, 0); n++; + XtSetArg(args[n], XmNmarginHeight, 0); n++; + XtSetArg(args[n], XmNresizePolicy, XmRESIZE_NONE); n++; + plugin = XmCreateDrawingArea(scrolledW, "plugin", args, n); + + n = 0; + XtSetArg(args[n], XmNworkWindow, plugin); n++; + XtSetValues(scrolledW, args, n); + + XtManageChild(plugin); + XtManageChild(scrolledW); + XtManageChild(button); + XtManageChild(form); + return plugin; +} + +/* parse program arguments and return plugin arguments list */ +void +ParseProgramArgs(int pg_argc, char **pg_argv, + int *argc_ret, char ***argn_ret, char ***argv_ret) +{ + int argc, i; + char **argn, **argv, *ptr; + + argn = (char **)malloc(sizeof(char *) * pg_argc); + argv = (char **)malloc(sizeof(char *) * pg_argc); + if (argn == NULL || argv == NULL) { + fprintf(stderr, "cannot malloc enough memory: %d\n", + sizeof(char *) * pg_argc); + exit(1); + } + argc = 0; + for (i = 0; i < pg_argc; i++) { + /* look for arguments of the form "name=value", skip malformed ones */ + ptr = strchr(pg_argv[i], '='); + if (ptr != NULL) { + /* "split" the string and store both parts */ + *ptr = '\0'; + argn[argc] = pg_argv[i]; + argv[argc] = ptr + 1; + argc++; + } else + fprintf(stderr, "invalid argument: %s\n", pg_argv[i]); + } + *argn_ret = argn; + *argv_ret = argv; + *argc_ret = argc; +} + + +/* retreive src, width, and height arguments value */ +void +ParsePluginArgs(int argc, char **argn, char **argv, + char **src_ret, int *width_ret, int *height_ret) +{ + int i; + + *src_ret = NULL; + *width_ret = DEFAULT_WIDTH; + *height_ret = DEFAULT_HEIGHT; + /* dumb look up */ + for (i = 0; i < argc; i++) { + if (strcasecmp(argn[i], "src") == 0) + *src_ret = argv[i]; + else if (strcasecmp(argn[i], "width") == 0) + *width_ret = atoi(argv[i]); + else if (strcasecmp(argn[i], "height") == 0) + *height_ret = atoi(argv[i]); + } +} + +/* main routine exercising the plug-in within a state machine */ +Boolean +MainWorkProc(XtPointer closure) +{ + NPP instance = (NPP) closure; + AppData *data = (AppData *)instance->ndata; + NPError status; + + switch (data->state) { + case SETUP: + /* create plugin instance */ + SetupPlugin(instance); + data->state = SETUPWINDOW; + break; + + case SETUPWINDOW: + /* create plugin window */ + SetupPluginWindow(instance); + data->state = PROCESSINPUT; + break; + + case PROCESSINPUT: + /* set default next state, + may be overriden by the call to GetURL */ + data->state = DONE; + + /* perform GET request on input data */ + if (data->src != NULL) { + status = NPN_GetURL(instance, data->src, NULL); + data->state = SETUPSTREAM; + } + break; + + case SETUPSTREAM: + /* if there is a GET request in progress setup to process reply */ + if (data->streams != NULL && data->nstreams != 0) { + if (data->streams[0]->end == -1) /* throw it away */ + fprintf(stderr, + "GetURL request response is thrown away (sorry)\n"); + else { + /* + * NPError NPP_NewStream(NPP instance, NPMIMEType type, + * NPStream *stream, NPBool seekable, uint16* stype) + */ + PRINTTRACE(" NPP_NewStream\n"); + status = NPP_NewStream(instance, MIMETYPE, data->streams[0], + FALSE, (uint16*)NP_NORMAL); + /* and add a handler for it */ + XtAppAddInput(XtWidgetToApplicationContext(data->plugin), + fileno((FILE *)(data->streams[0]->ndata)), + (XtPointer) XtInputReadMask, + (XtInputCallbackProc) WriteStreamProc, + (XtPointer) instance); + } + } + data->state = PROCESSREPLY; + break; + + case PROCESSREPLY: + /* if no more query responses are pending move to next state */ + if (data->streams == NULL || data->nstreams == 0 || + data->streams[0]->end == -1) + data->state = DONE; /* no more to be read */ + break; + + case DONE: + /* if there is a registered stream close it */ + if (data->streams != NULL && data->nstreams != 0) { + CloseStream(instance, 0); + if (data->nstreams > 0) { + /* then call for process of next reply */ + data->state = SETUPSTREAM; + return False; /* work proc is to be called again */ + } + } + /* then exit from state machine */ + return True; /* work proc is to be removed */ + } + return False; /* work proc is to be called again */ +} + +main(int argc, char **argv) +{ + Widget toplevel, plugin; + XtAppContext app_context; + NPP_t instance; + AppData app_data; + char **plugin_argn, **plugin_argv; + int plugin_argc; + char *src; + int width, height; + XEvent event; + + if (argc < 2 || !strncmp(argv[1], "-h", 2) || !strncmp(argv[1], "-?", 2)) { + fprintf(stderr, + "Usage: %s src=url [width=w] [height=h] [name=value ...]\n", + argv[0]); + exit(1); + } + + /* build GUI */ + toplevel = XtAppInitialize(&app_context, "Plugin Test", 0, 0, + &argc, argv, 0, 0, 0); +#ifdef SUPPORT_EDITRES + XtAddEventHandler(toplevel, 0, True, _XEditResCheckMessages, NULL); +#endif + + ParseProgramArgs(argc - 1, argv + 1, + &plugin_argc, &plugin_argn, &plugin_argv); + + ParsePluginArgs(plugin_argc, plugin_argn, plugin_argv, + &src, &width, &height); + + /* make sure width and height are valid */ + if (width < 0 || height < 0) { + fprintf(stderr, "invalid size specification (< 0) width=%d height=%d", + width, height); + exit(1); + } + + if (src == NULL) { + fprintf(stderr, "source argument required!\n"); + exit(1); + } + + plugin = + BuildGUI(toplevel, (Dimension)width, (Dimension)height, &instance); + + /* setup instance structure */ + app_data.state = SETUP; + app_data.plugin_argn = plugin_argn; + app_data.plugin_argv = plugin_argv; + app_data.plugin_argc = plugin_argc; + app_data.width = (uint32) width; + app_data.height = (uint32) height; + app_data.plugin = plugin; + app_data.src = src; + app_data.streams = NULL; + app_data.nstreams = 0; + app_data.streams_len = 0; + app_data.setup_window = False; + instance.ndata = (void *) &app_data; + + /* register main work proc (heart of the beast) */ + XtAppAddWorkProc(app_context, MainWorkProc, &instance); + + XtRealizeWidget(toplevel); + + /* wait for user to quit */ + for (;;) { + XtAppNextEvent (app_context, &event); + XtDispatchEvent (&event); + if (app_data.setup_window) { + SetupPluginWindow(&instance); + app_data.setup_window = False; + } + } +} + + +/* + * Netscape Methods + */ + +void * +NPN_MemAlloc(uint32 size) +{ + return malloc(size); +} + +uint32 +NPN_MemFlush(uint32 size) +{ + /* do nothing */ + return 0; +} + +void +NPN_MemFree(void *ptr) +{ + free(ptr); +} + +/* This is an asynchronous function, so perform request, setup stream for + * reply, and put it in the queue. */ +NPError +NPN_GetURL(NPP instance, const char *url, const char *window) +{ + AppData *data = (AppData *)instance->ndata; + char buf[BUFSIZ]; + FILE *fp; + + if (url == NULL) + return NPERR_INVALID_URL; + + /* perform request using "www" */ + sprintf(buf, "www -source \"%s\"", url); + fp = popen(buf, "r"); + if (fp == NULL) { + fprintf(stderr, "GetURL request failed on: %s\n", url); + return NPERR_INVALID_URL; + } + + MakeStream(instance, url, window, fp); + + return NPERR_NO_ERROR; +} |