summaryrefslogtreecommitdiff
path: root/testplugin/testplugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'testplugin/testplugin.c')
-rw-r--r--testplugin/testplugin.c671
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;
+}