FreeWRL / FreeX3D 4.3.0
plugin_main.c
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2
3
4 FreeWRL plugin for Mozilla compatible browsers.
5 Works in Firefox 1.x - 3.0 on Linux.
6
7*/
8
9/****************************************************************************
10 This file is part of the FreeWRL/FreeX3D Distribution.
11
12 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
13
14 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
15 it under the terms of the GNU Lesser Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
26****************************************************************************/
27
28/*******************************************************************************
29 * Simple LiveConnect Sample Plugin
30 * Copyright (c) 1996 Netscape Communications. All rights reserved.
31 *
32 * Modified by John Stewart - CRC Canada to provide for plugin capabilities
33 * for FreeWRL - an open source VRML and X3D browser.
34 *
35 * Operation:
36 *
37 * In the NPP_Initialize routine, a pipe is created and sent as the window
38 * title to FreeWRL. FreeWRL (OpenGL/OpenGL.xs) looks at this "wintitle",
39 * and if it starts with "pipe:", then treats the number following as
40 * a pipe id to send the window id back through. The pipe is then closed.
41 *
42 * The Plugin uses this window id to rehost the window.
43 *
44 * John Stewart, Alya Khan, Sarah Dumoulin - CRC Canada 2002 - 2006.
45 * Michel Briand - 2009.
46 ******************************************************************************/
47
48#include <config.h>
49#include <system.h>
50
51#if defined(HAVE_STDARG_H)
52# include <stdarg.h>
53#endif
54
55#include <plugin_utils.h>
56#include <npapi.h>
57
58#include <X11/Xlib.h>
59#include <X11/Intrinsic.h>
60#include <X11/StringDefs.h>
61#include <X11/Xatom.h>
62
63#ifdef HAVE_NSPR /* for PRBool */
64#include <prtypes.h>
65#else
66/* in nspr as of 2011-07-15, PRBool was just an int; if no nspr then define explicitly */
67# define PR_TRUE 1
68# define PR_FALSE 0
69typedef int PRBool
70#endif
71
72#define PLUGIN_NAME "FreeWRL X3D/VRML"
73
74#define BOOL_STR(b) (b ? "TRUE" : "FALSE")
75
76#define RECORD_FILE_NAME_IF_NULL \
77 if (me->fName == NULL) { \
78 /* Get the base file name for FreeWRL to run */ \
79 me->fName = (char *) NPN_MemAlloc((strlen(stream->url) +1) *sizeof(char *)); \
80 strcpy(me->fName,stream->url); \
81 PRINT("Can record filename now, name is %s\n", me->fName); \
82 }
83
84/* used in init. Don't write to the socket until a request has been received */
85int gotRequestFromFreeWRL = FALSE;
86
87char *paramline[20]; /* parameter line */
88static void *seqNo = 0;
89
90static int PluginVerbose = 1; /* CHECK LOG FILE PATH BEFORE SETTING THIS TO 1 */
91
92/*******************************************************************************
93 * Instance state information about the plugin.
94 ******************************************************************************/
95
96typedef struct _FW_PluginInstance
97{
98 int interfaceFile[2];
99 Display *display;
100 int32 x, y;
101 uint32 width, height;
102 Window mozwindow;
103 Window fwwindow;
104 pid_t childPID;
105 char *fName;
106 int freewrl_running;
107 int interfacePipe[2]; /* pipe plugin FROM freewrl */
108 char *cacheFileName;
109 int cacheFileNameLen;
110 FILE *logFile;
111 char *logFileName;
113
114typedef void (* Sigfunc) (int);
115
116static int np_fileDescriptor;
117
118/* Socket file descriptors */
119#define SOCKET_2 0
120#define SOCKET_1 1
121
122#define PIPE_PLUGINSIDE 0
123#define PIPE_FREEWRLSIDE 1
124
125#if 0
126static void signalHandler (int);
127/* Sigfunc signal (int, Sigfunc func); */
128void freewrlReceive(int fileDescriptor);
129#endif
130
131/* libFreeWRL defines those macros ...
132 we will redefine them here for our own purpose
133*/
134#undef MALLOC
135#define MALLOC NPN_MemAlloc
136#undef FREE
137#define FREE NPN_MemFree
138
139struct timeval mytime;
140struct timezone tz; /* unused see man gettimeofday */
141NPStream *currentStream = NULL;
142
147static void create_log_file(FW_PluginInstance *me)
148{
149 FILE *tty;
150 char *logfilename, *hostname, *username;
151 static const char log_file_pat[] = "/tmp/npfreewrl_%s-%s.log";
152
153 hostname = MALLOC(4096);
154 if (gethostname(hostname, 4096) < 0) {
155 int err = errno;
156 fprintf(stderr, "system error: %s\n", strerror(err));
157 sprintf(hostname, "unknown-host");
158 }
159 username = getenv("LOGNAME");
160 if (!username) {
161 username = getlogin();
162 }
163 if (!username) {
164 int err = errno;
165 fprintf(stderr, "system error: %s\n", strerror(err));
166 username = "unknown-user";
167 }
168
169 // -4 %s%s
170 // +1 \n
171 logfilename = MALLOC(strlen(log_file_pat)
172 + strlen(hostname) + strlen(username) -4 +1 +4 );
173 sprintf(logfilename, log_file_pat, hostname, username);
174 FREE(hostname);
175 /* do not free username */
176
177 // Open log file (create it if doesn't exist
178 tty = fopen(logfilename, "a");
179
180 if (tty == NULL) {
181 fprintf (stderr, "FreeWRL plugin ERROR: plugin could not open log file: %s. Will output to stderr.\n", logfilename);
182 FREE(logfilename);
183 logfilename = NULL;
184 tty = stderr;
185 }
186
187 me->logFile = tty;
188 me->logFileName = logfilename;
189}
190
196static void print(FW_PluginInstance *me, const char *format, ...)
197{
198 va_list ap;
199 va_start(ap, format);
200
201 FILE *tty;
202 double TickTime;
203
204 if (!PluginVerbose) return;
205
206 /* Set the timestamp */
207 gettimeofday (&mytime,&tz);
208 TickTime = (double) mytime.tv_sec + (double)mytime.tv_usec/1000000.0;
209
210 if (!me)
211 tty = stderr;
212 else
213 tty = me->logFile;
214
215 fprintf(tty, "%f: FreeWRL plugin: ", TickTime);
216 vfprintf(tty, format, ap);
217 fflush(tty);
218
219 va_end(ap);
220}
221
222#define PRINT(_formargs...) print(me, ##_formargs)
223
224#define PRINT_PERROR(_msg) print(me, "system error: %s failed: %s (%d)\n", \
225 _msg, strerror(errno), errno)
226
227
228#if 0
229Sigfunc signal(int signo, Sigfunc func)
230{
231 struct sigaction action, old_action;
232
233 action.sa_handler = func;
234 /*
235 * Initialize action's signal set as empty set
236 * (see man page sigsetops(3)).
237 */
238 sigemptyset(&action.sa_mask);
239
240 action.sa_flags = 0; /* Is this a good idea??? */
241
242 /* Add option flags for handling signal: */
243 action.sa_flags |= SA_NOCLDSTOP;
244#ifdef SA_NOCLDWAIT
245 action.sa_flags |= SA_NOCLDWAIT;
246#endif
247
248 if (sigaction(signo, &action, &old_action) < 0) {
249 /* print_here("Call to sigaction failed"); */
250 return(SIG_ERR);
251 }
252 /* Return the old action for the signal or SIG_ERR. */
253 return(old_action.sa_handler);
254}
255#endif
256
257#if 0
258void signalHandler(int signo) {
259 /* sprintf(debs, "ACTION signalHandler %d", signo); */
260 /* print_here(debs); */
261
262 if (signo == SIGIO) {
263 freewrlReceive(np_fileDescriptor);
264
265 } else {
266 /* Should handle all except the uncatchable ones. */
267 /* print_here("\nClosing plugin log.\n"); */
268 }
269}
270
271void freewrlReceive(int fileDescriptor)
272{
273 FW_PluginInstance *me = NULL;
274 sigset_t newmask, oldmask;
275
276 urlRequest request;
277 size_t request_size = 0;
278 NPError rv = 0;
279
280 sprintf(debs, "Call to freewrlReceive fileDescriptor %d.", fileDescriptor);
281 print_here(debs);
282
283 bzero(request.url, FILENAME_MAX);
284 request.instance = 0;
285 request.notifyCode = 0; /* not currently used */
286
287 request_size = sizeof(request);
288
289 /*
290 * The signal handling code is based on the work of
291 * W. Richard Stevens from Unix Network Programming,
292 * Networking APIs: Sockets and XTI.
293 */
294
295 /* Init. the signal sets as empty sets. */
296 if (sigemptyset(&newmask) < 0) {
297 print_here("Call to sigemptyset with arg newmask failed");
298 return;
299 }
300
301 if (sigemptyset(&oldmask) < 0) {
302 print_here("Call to sigemptyset with arg oldmask failed");
303 return;
304 }
305
306 if (sigaddset(&newmask, SIGIO) < 0) {
307 print_here("Call to sigaddset failed");
308 return;
309 }
310
311 /* Code to block SIGIO while saving the old signal set. */
312 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
313 print_here("Call to sigprocmask failed");
314 return;
315 }
316
317 /* If blocked or interrupted, be silent. */
318 if (read(fileDescriptor, (urlRequest *) &request, request_size) < 0) {
319 if (errno != EINTR && errno != EAGAIN) {
320 print_here("Call to read failed");
321 }
322 /* FreeWRL has died, or THIS IS US WRITING CREATING THAT SIG. */
323 print_here ("freewrlReceive, quick return; either this is us writing or freewrl croaked");
324 return;
325 } else {
326 sprintf (debs, "notifyCode = %d url = %s", request.notifyCode, request.url);
327 print_here(debs);
328
329 /* signal that we have now received a file request from FreeWRL */
330 gotRequestFromFreeWRL = TRUE;
331
332 /* is this a getUrl, or a "open new window for url" */
333 if (request.notifyCode == 0) {
334 /* get Url and return it to FreeWRL */
335
336 seqNo++;
337 /* printf ("request seq %d, url %s\n",seqNo, request.url); */
338
339 if ((rv = NPN_GetURLNotify(request.instance,
340 request.url, NULL,
341 (void *)(seqNo))) != NPERR_NO_ERROR) {
342 sprintf(debs, "Call to NPN_GetURLNotify failed with error %d.", rv);
343 print_here(debs);
344 }
345
346
347 sprintf (debs, "step 2a, NPN_GetURLNotify with request.url %s",request.url);
348 print_here(debs);
349
350 } else if (request.notifyCode == -99) {
351 /* Firefox, etc took too long. we have timed out. */
352 sprintf (debs,"notifyCode = -99, we have timed out for %s",request.url);
353 print_here(debs);
354 if (currentStream != NULL) {
355 NPN_DestroyStream(request.instance, currentStream, NPRES_USER_BREAK);
356 sprintf (debs, "FreeWRL can not find: %s",request.url);
357 print_here(debs);
358 NPN_Status (request.instance, debs);
359 currentStream = NULL;
360 }
361
362 } else {
363 /* request.notifyCode must be 1 */
364 sprintf (debs,"NPN_GetStream...");
365 print_here(debs);
366
367 NPStream* stream;
368 NPError err = NPERR_NO_ERROR;
369 char* myData = "<HTML><B>This is a message from my plug-in!</b></html>";
370 int32 myLength = strlen(myData) + 1;
371 err = NPN_NewStream(request.instance,
372 "text/html",
373 "_AnchorFailsinFreeWRL",
374 &stream);
375 print_here ("NewStream made");
376
377 err = NPN_Write(request.instance,
378 stream,
379 myLength,
380 myData);
381 print_here ("NPN_Write made");
382 }
383
384 /* now, put a status line on bottom of browser */
385 sprintf (debs, "FreeWRL loading: %s",request.url);
386 print_here(debs);
387 NPN_Status (request.instance, debs);
388 }
389
390 /* Restore old signal set, which unblocks SIGIO. */
391 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
392 print_here("Call to sigprocmask failed");
393 return;
394 }
395
396 print_here("returning from freewrl_receive");
397 return;
398}
399#endif
400
401static int init_socket(FW_PluginInstance *me, int fileDescriptor, Boolean nonblock)
402{
403 int io_flags;
404
405 if (fcntl(fileDescriptor, F_SETOWN, getpid()) < 0) {
406 PRINT("Call to fcntl with command F_SETOWN failed\n");
407 return(NPERR_GENERIC_ERROR);
408 }
409
410 if ( (io_flags = fcntl(fileDescriptor, F_GETFL, 0)) < 0 ) {
411 PRINT("Call to fcntl with command F_GETFL failed\n");
412 return(NPERR_GENERIC_ERROR);
413 }
414
415 /*
416 * O_ASYNC is specific to BSD and Linux.
417 * Use ioctl with FIOASYNC for others.
418 */
419#ifndef __sgi
420 io_flags |= O_ASYNC;
421#endif
422
423 if (nonblock) { io_flags |= O_NONBLOCK; }
424
425 if ( (io_flags = fcntl(fileDescriptor, F_SETFL, io_flags)) < 0 ) {
426 PRINT("Call to fcntl with command F_SETFL failed\n");
427 return(NPERR_GENERIC_ERROR);
428 }
429 return(NPERR_NO_ERROR);
430}
431
432/* actually run FreeWRL and swallow it, if enough information has been found */
433int Run (NPP instance)
434{
435 FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
436
437 char pipetome[25];
438 char childFd[25];
439 char instanceStr[25];
440 int carg = 0;
441
442 XWindowAttributes mywin;
443 Window child_window = 0;
444
445 pid_t child;
446 pid_t mine;
447
448 int secpipe[2];
449#define clean_pipe { close(secpipe[0]); close(secpipe[1]); }
450
451 int err;
452 int count;
453 int nbytes;
454
455 PRINT("Run starts... Checking if can run; disp %u win %u fname %s\n",
456 me->mozwindow, me->display, me->fName);
457
458 /* Return if we do not have all of the required parameters. */
459 if (me->mozwindow == 0) return FALSE;
460
461 if (me->fName == NULL) return FALSE;
462
463 if (me->display == 0) return FALSE;
464
465 PRINT("Run ... ok\n");
466
467 /* start FreeWRL, if it is not running already. */
468 if (me->freewrl_running) {
469 PRINT("Run ... FreeWRL already running, returning.\n");
470 return TRUE;
471 }
472
473 if (pipe(secpipe) < 0) {
474 PRINT_PERROR("pipe");
475 return FALSE;
476 }
477
478 if (fcntl(secpipe[1], F_SETFD, fcntl(secpipe[1], F_GETFD) | FD_CLOEXEC)) {
479 PRINT_PERROR("fcntl");
480 clean_pipe;
481 return FALSE;
482 }
483
484 switch ((child = fork())) {
485 case -1:
486 PRINT_PERROR("fork");
487 clean_pipe;
488 return FALSE;
489 case 0:
490 mine = getpid();
491 if (setpgid(mine, mine) < 0) {
492 PRINT_PERROR("setpgid");
493 }
494
495 /* create pipe string */
496 sprintf(pipetome, "pipe:%d",
497 me->interfacePipe[PIPE_FREEWRLSIDE]);
498
499 /* child file descriptor - to send requests back here */
500 sprintf(childFd, "%d", me->interfaceFile[SOCKET_2]);
501
502 /* Instance, so that FreeWRL knows its us... */
503 sprintf(instanceStr, "%lu",
504 (unsigned long int) (uintptr_t) instance);
505
506 /* Nice FreeWRL to a lower priority */
507 paramline[carg++] = "nice";
508 paramline[carg++] = "freewrl";
509 paramline[carg++] = "--logfile";
510
511 if (me->logFileName) {
512 paramline[carg++] = me->logFileName;
513 } else {
514 paramline[carg++] = "-"; // no log file (stderr)
515
516 /* this is usefull to build a test case
517 for child death... because FreeWRL
518 will exit with this param... */
519 /* paramline[carg++] = NULL; */
520 }
521
522 /* We have the file name, so include it */
523 paramline[carg++] = me->fName;
524
525 /* Pass in the pipe number so FreeWRL can return the
526 window id */
527 paramline[carg++] = "--plugin";
528 paramline[carg++] = pipetome;
529
530 /* EAI connection */
531 paramline [carg++] = "--eai";
532
533 /* File descriptor and instance - allows FreeWRL to
534 request files from browser's cache */
535 paramline[carg++] = "--fd";
536 paramline[carg++] = childFd;
537 paramline[carg++] = "--instance";
538 paramline[carg++] = instanceStr;
539 paramline[carg] = NULL;
540 /* End of arguments */
541
542 PRINT("exec param line is %s %s %s %s %s %s %s %s %s %s %s\n",
543 paramline[0],paramline[1],paramline[2],paramline[3],
544 paramline[4],paramline[5],paramline[6],paramline[7],
545 paramline[8],paramline[9],paramline[10]);
546
547 close(secpipe[0]);
548 execvp(paramline[0], paramline);
549 write(secpipe[1], &errno, sizeof(int));
550 _exit(0);
551 break;
552
553 default:
554 close(secpipe[1]);
555 while ((count = read(secpipe[0], &err, sizeof(errno))) == -1)
556 if (errno != EAGAIN && errno != EINTR) break;
557
558 if (count) {
559 PRINT_PERROR("execvp");
560 clean_pipe;
561 return FALSE;
562 }
563
564 close(secpipe[0]);
565#if 0
566 PRINT("waiting for child...\n");
567 while (waitpid(child, &err, 0) == -1)
568 if (errno != EINTR) {
569 PRINT_PERROR("waitpid");
570 return FALSE;
571 }
572
573 if (WIFEXITED(err))
574 PRINT("child exited with %d\n", WEXITSTATUS(err));
575
576 else if (WIFSIGNALED(err))
577 PRINT("child killed by %d\n", WTERMSIG(err));
578#endif
579 }
580
581 me->childPID = child;
582 PRINT("CHILD %d\n", me->childPID);
583
584 PRINT("after FW_Plugin->freewrl_running call - waiting on pipe\n");
585
586 usleep(1500);
587
588 nbytes = read(me->interfacePipe[PIPE_PLUGINSIDE], &child_window, sizeof(Window));
589 if ((nbytes < 0) || (nbytes == 0)) {
590 int status = 0;
591 // error reading pipe: child died
592 PRINT("ERROR: child %d FreeWRL program died (%d), waiting...\n",
593 me->childPID, nbytes);
594
595 switch (waitpid(me->childPID, &status, WNOHANG)) {
596 case 0: PRINT("child is gone (nothing to wait), exit code: %d\n", status);
597 break;
598 case -1: PRINT_PERROR("waitpid");
599 break;
600 default: PRINT("child passed away, exit code: %d\n", status);
601 break;
602 }
603
604 me->childPID = 0;
605 return FALSE;
606 }
607
608 PRINT("After exec, and after read from pipe, FW window is %u\n",
609 child_window);
610
611 me->fwwindow = child_window;
612
613 PRINT("disp mozwindow height width %u %u %u %u\n",
614 me->display, me->mozwindow, me->width, me->height);
615
616 /*reparent the window */
617
618 XGetWindowAttributes(me->display,me->fwwindow, &mywin);
619
620 PRINT("Plugin: mapped_state %d, IsUnmapped %d, isUnviewable %d isViewable %d\n"
621 "x %d y %d wid %d height %d\n",
622 mywin.map_state, IsUnmapped, IsUnviewable, IsViewable,
623 mywin.x,mywin.y,mywin.width,mywin.height);
624
625 /* print_here ("going to XFlush"); */
626 /* XFlush(me->display); */
627
628 /* print_here ("going to XSync"); */
629 /* XSync (me->display, FALSE); */
630
631 PRINT("Going to resize FreeWRL: %d x %d -> %d x %d\n",
632 mywin.width, mywin.height, me->width, me->height);
633
634 /* MB 28-12-2009 : added this sync to prevent the plugin from "loosing" the resize event ... */
635 /* XSync (me->display, FALSE); */
636
637 /* here the two next calls seems to be operating well in any order... hum... */
638 {
639 XSizeHints size_hints;
640 memset(&size_hints, 0, sizeof(size_hints));
641 size_hints.min_width = size_hints.max_width = me->width;
642 size_hints.min_height = size_hints.max_height = me->height;
643 XSetWMNormalHints(me->display, me->fwwindow, &size_hints);
644 }
645 XResizeWindow(me->display, me->fwwindow, me->width, me->height);
646
647 PRINT("Going to reparent\n");
648 XReparentWindow(me->display, me->fwwindow, me->mozwindow, 0,0);
649
650 PRINT("Going to remap\n");
651 XMapWindow(me->display,me->fwwindow);
652
653 XGetWindowAttributes(me->display,me->fwwindow, &mywin);
654
655 PRINT("Plugin, after reparenting, mapped_state %d, "
656 "IsUnmapped %d, isUnviewable %d isViewable %d\n"
657 "x %d y %d wid %d height %d\n",
658 mywin.map_state, IsUnmapped, IsUnviewable, IsViewable,
659 mywin.x,mywin.y,mywin.width,mywin.height);
660
661 me->freewrl_running = TRUE;
662
663 PRINT("Run function finished\n");
664 return TRUE;
665}
666
667
668/*******************************************************************************
669 ******************************************************************************/
670#ifdef LEGACY_NPAPI
671char *
672#else
673const char *
674#endif
675NPP_GetMIMEDescription(void)
676{
677 static const char mime_types[] =
678 "x-world/x-vrml:wrl:FreeWRL VRML Browser;"
679 "model/vrml:wrl:FreeWRL VRML Browser;"
680 "model/x3d:x3d:FreeWRL X3D Browser;"
681 "model/x3d+xml:x3d:FreeWRL X3D Browser;"
682 "model/x3d+vrml:x3dv:FreeWRL X3D Browser;"
683 "model/x3d+binary:x3db:FreeWRL X3D Browser"
684 ;
685
686 print (NULL, "NPP_GetMIMEDescription: %s\n", mime_types);
687 return (char *) mime_types;
688}
689
690NPError
691NPP_GetValue(NPP instance, NPPVariable variable, void *value)
692{
693#define VERSION_DESCRIPTION_SIZE 1024
694 static char version_description[VERSION_DESCRIPTION_SIZE];
695 FW_PluginInstance* me = NULL; /* instance may be NULL */
696 NPError err = NPERR_NO_ERROR;
697
698 if (!value) return NPERR_GENERIC_ERROR;
699
700 if (instance)
701 me = (FW_PluginInstance *) instance->pdata;
702
703 PRINT("NPP_GetValue %u\n", variable);
704
705 switch (variable) {
706 case NPPVpluginNameString:
707 *((char **)value) = PLUGIN_NAME;
708 break;
709
710 case NPPVpluginNeedsXEmbed:
711 *((PRBool *)value) = PR_TRUE;
712 break;
713
714 case NPPVpluginDescriptionString:
715 snprintf(version_description, VERSION_DESCRIPTION_SIZE,
716 "<b>FreeWRL is a VRML/X3D plugin.</b><br>"
717 "Visit us at <a href=\"http://freewrl.sourceforge.net/\">"
718 "http://freewrl.sourceforge.net/</a>.<br>"
719 "Plugin version: <b>%s</b>.<br>"
720 "Build timestamp: <b>%s</b>.<br>",
721 freewrl_plugin_get_version(),
722 BUILD_TIMESTAMP);
723 *((char **)value) = version_description;
724 break;
725
726 default:
727 err = NPERR_INVALID_PARAM;
728 }
729 return err;
730}
731
732/*******************************************************************************
733 * General Plug-in Calls
734 ******************************************************************************/
735
736/*
737** NPP_Initialize is called when your DLL is being loaded to do any
738** DLL-specific initialization.
739*/
740NPError NPP_Initialize(void) {
741 /* print_here ("NPP_Initialize"); */
742 return NPERR_NO_ERROR;
743}
744
745#ifdef OJI
746jref NPP_GetJavaClass( void )
747{
748 return NULL;
749}
750#endif
751
752/*
753** NPP_Shutdown is called when your DLL is being unloaded to do any
754** DLL-specific shut-down. You should be a good citizen and declare that
755** you're not using your java class any more. FW_Plugin allows java to unload
756** it, freeing up memory.
757*/
758void NPP_Shutdown(void) {
759 /* print_here ("NPP_Shutdown"); */
760}
761
762/*
763** NPP_New is called when your plugin is instantiated (i.e. when an EMBED
764** tag appears on a page).
765*/
766NPError
767NPP_New(NPMIMEType pluginType,
768 NPP instance,
769 uint16 mode,
770 int16 argc,
771 char* argn[],
772 char* argv[],
773 NPSavedData* saved) {
774
775 FW_PluginInstance* me = NULL; /* only case */
776 unsigned int err;
777 void *tmp;
778
779 if( instance == NULL ) {
780 return NPERR_INVALID_INSTANCE_ERROR;
781 }
782
783 /* Create plugin instance structure */
784 tmp = NPN_MemAlloc(sizeof(FW_PluginInstance));
785 if (!tmp)
786 return NPERR_OUT_OF_MEMORY_ERROR;
787
788 instance->pdata = tmp;
789 me = (FW_PluginInstance*) tmp;
790 memset(me, 0, sizeof(FW_PluginInstance));
791
792 /* Create log file */
793 create_log_file(me);
794 PRINT("FreeWRL plugin log restarted. Version: %s. Build: %s\n",
795 freewrl_plugin_get_version(), BUILD_TIMESTAMP);
796
797 PRINT("NPP_New, argc %d argn %s argv %s\n", argc, argn[0], argv[0]);
798
799 /* mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h) */
800 switch (mode) {
801 case NP_EMBED: PRINT("NPP_New, mode NP_EMBED\n"); break;
802 case NP_FULL: PRINT("NPP_New, mode NP_FULL\n"); break;
803 default: PRINT("NPP_New, mode UNKNOWN MODE\n"); break;
804 }
805
806 seqNo = 0;
807 gotRequestFromFreeWRL = FALSE;
808
809 if (pipe(me->interfacePipe) < 0) {
810 PRINT("Pipe connection to FW_Plugin->interfacePipe failed: %d,%s [%s:%d]\n",
811 errno, strerror(errno), __FILE__,__LINE__);
812 }
813
814 PRINT("Pipe created, PIPE_FREEWRLSIDE %d PIPE_PLUGINSIDE %d\n",
815 me->interfacePipe[PIPE_FREEWRLSIDE], me->interfacePipe[PIPE_PLUGINSIDE]);
816
817 /* Assume plugin and FreeWRL child process run on the same machine,
818 then we can use UDP and have incredibly close to 100.00% reliability */
819
820 if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, me->interfaceFile) < 0) {
821 PRINT("Call to socketpair failed\n");
822 return (NPERR_GENERIC_ERROR);
823 }
824 PRINT("file pair created, SOCKET_1 %d SOCKET_2 %d\n",
825 me->interfaceFile[SOCKET_1], me->interfaceFile[SOCKET_2]);
826
827 np_fileDescriptor = me->interfaceFile[SOCKET_1];
828
829#if 0
830 if (signal(SIGIO, signalHandler) == SIG_ERR) return (NPERR_GENERIC_ERROR);
831 if (signal(SIGBUS, signalHandler) == SIG_ERR) return (NPERR_GENERIC_ERROR);
832#endif
833
834 /* prepare communication sockets */
835 if ((err=init_socket(me, me->interfaceFile[SOCKET_2], FALSE))!=NPERR_NO_ERROR)
836 return err;
837 if ((err=init_socket(me, me->interfaceFile[SOCKET_1], TRUE))!=NPERR_NO_ERROR)
838 return err;
839 PRINT("NPP_New returning %d\n", err);
840 return err;
841}
842
843NPError
844NPP_Destroy(NPP instance, NPSavedData** save)
845{
846 FW_PluginInstance* me = (FW_PluginInstance*) instance->pdata;
847 int status;
848
849 /* fprintf(stderr, "NPP_Destroy (%p %p)\n", (void*)instance, (void*)save); */
850
851 PRINT("NPP_Destroy begin\n");
852
853 if (instance == NULL)
854 return NPERR_INVALID_INSTANCE_ERROR;
855
856 if (me != NULL) {
857
858 if (me->fName != NULL) {
859 NPN_MemFree(me->fName);
860 }
861
862 if (me->childPID >0) {
863
864 PRINT("killing command kill %d\n", me->childPID);
865 /* which signal ? TERM, QUIT or KILL */
866 kill(me->childPID, SIGTERM);
867 waitpid(me->childPID, &status, 0);
868 }
869
870 if (me->cacheFileName != NULL) {
871 NPN_MemFree(me->cacheFileName);
872 }
873
874 if (me->interfacePipe[PIPE_FREEWRLSIDE] != 0) {
875 close (me->interfacePipe[PIPE_FREEWRLSIDE]);
876 close (me->interfacePipe[PIPE_PLUGINSIDE]);
877 }
878
879 NPN_MemFree(instance->pdata);
880 instance->pdata = NULL;
881 }
882 me->freewrl_running = FALSE;
883 gotRequestFromFreeWRL = FALSE;
884
885 PRINT("NPP_Destroy end\n");
886 return NPERR_NO_ERROR;
887}
888
889void
890NPP_URLNotify (NPP instance, const char *url, NPReason reason, void* notifyData)
891{
892 FW_PluginInstance* me = (FW_PluginInstance*) instance->pdata;
893#define returnBadURL "this file is not to be found on the internet"
894 int bytes;
895
896 PRINT("NPP_URLNotify, url %s reason %d notifyData %p\n",
897 url, reason, notifyData);
898
899 if (seqNo != notifyData) {
900 PRINT("NPP_URLNotify, expected seq %p, got %p for %s\n",
901 seqNo, notifyData, url);
902 return;
903 }
904
905 if (reason == NPRES_DONE) {
906 PRINT("NPP_UrlNotify - NPRES_DONE\n");
907 bytes = (strlen(me->cacheFileName)+1)*sizeof(const char *);
908 if (write(me->interfaceFile[SOCKET_1], me->cacheFileName, bytes) < 0) {
909 PRINT("Call to write failed\n");
910 }
911
912 /* send a "done" message to status bar */
913 NPN_Status(instance,"FreeWRL: Done");
914 return;
915
916 } else if (reason == NPRES_USER_BREAK) {
917 PRINT("NPP_UrlNotify - NPRES_USER_BREAK\n");
918 } else if (reason == NPRES_NETWORK_ERR) {
919 PRINT("NPP_UrlNotify - NPRES_NETWORK_ERR\n");
920 } else {
921 PRINT("NPP_UrlNotify - unknown\n");
922 }
923
924 PRINT("NPP_UrlNotify - writing %s (%u bytes) to socket %d\n",
925 returnBadURL, strlen(returnBadURL), me->interfaceFile[SOCKET_1]);
926
927 NPN_Status(instance,"FreeWRL: NPP_URLNotify failed");
928
929 /* if we got a request from FreeWRL for the file, then return
930 the name. If FreeWRL was "Run", from within NPP_NewStream,
931 it will not be expecting this write, until it asks for a
932 file - a case of "the cart before the horse" */
933
934 if (gotRequestFromFreeWRL) {
935 PRINT("NPP_UrlNotify, gotRequestFromFreeWRL - writing data\n");
936 if (write(me->interfaceFile[SOCKET_1], returnBadURL,
937 strlen(returnBadURL)) < 0) {
938 PRINT("Call to write failed\n");
939 }
940 } else {
941 PRINT("call to write (for returnBadURL) skipped, because gotRequestFromFreeWRL = FALSE\n");
942 }
943}
944
945
946NPError
947NPP_SetWindow(NPP instance, NPWindow *browser_window)
948{
949 FW_PluginInstance* me = (FW_PluginInstance*) instance->pdata;
950 NPError result = NPERR_NO_ERROR;
951
952 PRINT("start of NPP_SetWindow\n");
953
954 if (instance == NULL)
955 return NPERR_INVALID_INSTANCE_ERROR;
956
957 /* do we have a file name yet? */
958 PRINT("file name in SetWindow is %s\n", me->fName);
959
960 /* set the display, if we know it yet */
961 if (!me->display) {
962 if ((NPSetWindowCallbackStruct *)(browser_window->ws_info) != NULL) {
963 me->display = ((NPSetWindowCallbackStruct *)
964 browser_window->ws_info)->display;
965
966 PRINT("NPP_SetWindow, plugin display now is %u\n", me->display);
967 }
968 }
969
970 /* verify that the display has not changed */
971 if ((NPSetWindowCallbackStruct *)(browser_window->ws_info) != NULL) {
972 if ((me->display) != ((NPSetWindowCallbackStruct *)
973 browser_window->ws_info)->display) {
974
975 PRINT("HMMM - display has changed\n");
976 me->display = ((NPSetWindowCallbackStruct *)
977 browser_window->ws_info)->display;
978 }
979 }
980
981 PRINT("NPP_SetWindow, moz window is %u childPID is %u\n",
982 browser_window->window, me->childPID);
983
984 me->width = browser_window->width;
985 me->height = browser_window->height;
986
987
988 if (me->mozwindow != (Window) browser_window->window) {
989 me->mozwindow = (Window) browser_window->window;
990
991 /* run FreeWRL, if it is not already running. It might not be... */
992 if (!me->freewrl_running) {
993
994 PRINT("NPP_SetWindow, running FreeWRL here!\n");
995
996 if (!Run(instance)) {
997 PRINT("NPP_SetWindow, FreeWRL program failed!\n");
998 return NPERR_MODULE_LOAD_FAILED_ERROR;
999 }
1000
1001 PRINT("NPP_SetWindow, returned from Run!\n");
1002 }
1003 }
1004
1005 /* Handle the FreeWRL window */
1006 if (me->fwwindow) {
1007 PRINT("xresize x %d y %d wid %d hei %d\n",
1008 me->x, me->y, me->width, me->height);
1009
1010 XResizeWindow(me->display, me->fwwindow,
1011 me->width, me->height);
1012
1013 XSync (me->display,FALSE);
1014 }
1015 PRINT("exiting NPP_SetWindow\n");
1016 return result;
1017}
1018
1019NPError
1020NPP_NewStream(NPP instance,
1021 NPMIMEType type,
1022 NPStream *stream,
1023 NPBool seekable,
1024 uint16 *stype)
1025{
1026 FW_PluginInstance* me = (FW_PluginInstance*) instance->pdata;
1027
1028 if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR;
1029
1030 if (stream->url == NULL) return(NPERR_NO_DATA);
1031
1032 if (currentStream == NULL) {
1033 currentStream = stream;
1034 } else {
1035 PRINT("NPP_NewStream, currentstream NOT NULL\n");
1036 }
1037
1038 PRINT("NPP_NewStream, filename %s instance %p, type %s, "
1039 "stream %p, seekable %s stype %d\n",
1040 me->fName, instance, type,
1041 stream, BOOL_STR(seekable), (stype ? (*stype) : 0));
1042
1043 RECORD_FILE_NAME_IF_NULL;
1044
1045 /* run FreeWRL, if it is not already running. It might not be... */
1046 if (!me->freewrl_running) {
1047
1048 PRINT("NPP_NewStream, running FreeWRL here!\n");
1049
1050 if (!Run(instance)) {
1051 PRINT("NPP_NewStream, FreeWRL program failed!\n");
1052 return NPERR_MODULE_LOAD_FAILED_ERROR;
1053 }
1054 }
1055
1056 /* Lets tell netscape to save this to a file. */
1057 *stype = NP_ASFILEONLY;
1058 seekable = FALSE;
1059
1060 PRINT("NPP_NewStream returning noerror\n");
1061 return NPERR_NO_ERROR;
1062}
1063
1064
1065/* PLUGIN DEVELOPERS:
1066 * These next 2 functions are directly relevant in a plug-in which
1067 * handles the data in a streaming manner. If you want zero bytes
1068 * because no buffer space is YET available, return 0. As long as
1069 * the stream has not been written to the plugin, Navigator will
1070 * continue trying to send bytes. If the plugin doesn't want them,
1071 * just return some large number from NPP_WriteReady(), and
1072 * ignore them in NPP_Write(). For a NP_ASFILE stream, they are
1073 * still called but can safely be ignored using this strategy.
1074 */
1075
1076int32 STREAMBUFSIZE = 0X0FFFFFFF; /* If we are reading from a file in NPAsFile
1077 * mode so we can take any size stream in our
1078 * write call (since we ignore it) */
1079
1080int32
1081NPP_WriteReady(NPP instance, NPStream *stream)
1082{
1083 FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
1084 PRINT("NPP_WriteReady\n");
1085 /* Number of bytes ready to accept in NPP_Write() */
1086 return STREAMBUFSIZE;
1087}
1088
1089
1090int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
1091{
1092 FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
1093 PRINT("NPP_Write\n");
1094 return 0;
1095 return len; /* The number of bytes accepted */
1096}
1097
1098
1099NPError
1100NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason)
1101{
1102 FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
1103
1104 PRINT("NPP_DestroyStream, instance %p stream %p\n",
1105 instance, stream);
1106
1107 if (reason == NPRES_DONE) PRINT("reason: NPRES_DONE\n");
1108 if (reason == NPRES_USER_BREAK) PRINT("reason: NPRES_USER_BREAK\n");
1109 if (reason == NPRES_NETWORK_ERR) PRINT("reason: NPRES_NETWORK_ERR\n");
1110
1111 if (stream == currentStream) {
1112 currentStream = NULL;
1113 } else {
1114 PRINT("NPP_DestroyStream, STREAMS DO NOT MATCH!\n");
1115 }
1116
1117 if (instance == NULL)
1118 return NPERR_INVALID_INSTANCE_ERROR;
1119 return NPERR_NO_ERROR;
1120}
1121
1122
1123void
1124NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname)
1125{
1126 FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
1127 int bytes;
1128
1129 PRINT("NPP_StreamAsFile, start with fname %s\n", fname);
1130
1131 if (instance != NULL) {
1132 me = (FW_PluginInstance*) instance->pdata;
1133
1134 RECORD_FILE_NAME_IF_NULL;
1135
1136 if (!me->freewrl_running) {
1137 /* if we are not running yet, see if we have enough to start. */
1138 if (!Run(instance)) {
1139 PRINT("NPP_StreamAsFile, FreeWRL program failed!\n");
1140 /* TODO: clean-up */
1141 return;
1142 }
1143
1144 } else {
1145 if (fname == NULL) {
1146 PRINT("NPP_StreamAsFile has a NULL file\n");
1147
1148 /* Try sending an empty string */
1149 if (write(me->interfaceFile[SOCKET_1], "", 1) < 0) {
1150 PRINT("Call to write failed\n");
1151 }
1152 } else {
1153
1154 /* if we got a request from FreeWRL
1155 for the file, then return the
1156 name. If FreeWRL was "Run", from
1157 within NPP_NewStream, it will not
1158 be expecting this write, until it
1159 asks for a file - a case of "the
1160 cart before the horse" */
1161
1162 if (gotRequestFromFreeWRL) {
1163 bytes = (strlen(fname)+1)*sizeof(const char *);
1164 if (bytes > (me->cacheFileNameLen -10)) {
1165 if (me->cacheFileName != NULL) {
1166 NPN_MemFree(me->cacheFileName);
1167 }
1168
1169 me->cacheFileNameLen = bytes+20;
1170 me->cacheFileName = NPN_MemAlloc(me->cacheFileNameLen);
1171 }
1172
1173 memcpy (me->cacheFileName, fname, bytes);
1174 PRINT("NPP_StreamAsFile: saving name to cachename\n");
1175 } else {
1176 PRINT("NPP_StreamAsFile: skipping file write, as gotRequestFromFreeWRL = FALSE\n");
1177 }
1178 }
1179 }
1180 }
1181}
1182
1183
1184void
1185NPP_Print(NPP instance, NPPrint* printInfo)
1186{
1187 if(printInfo == NULL)
1188 return;
1189
1190 if (instance != NULL) {
1191
1192 if (printInfo->mode == NP_FULL) {
1193 /*
1194 * PLUGIN DEVELOPERS:
1195 * If your plugin would like to take over
1196 * printing completely when it is in full-screen mode,
1197 * set printInfo->pluginPrinted to TRUE and print your
1198 * plugin as you see fit. If your plugin wants Netscape
1199 * to handle printing in this case, set
1200 * printInfo->pluginPrinted to FALSE (the default) and
1201 * do nothing. If you do want to handle printing
1202 * yourself, printOne is true if the print button
1203 * (as opposed to the print menu) was clicked.
1204 * On the Macintosh, platformPrint is a THPrint; on
1205 * Windows, platformPrint is a structure
1206 * (defined in npapi.h) containing the printer name, port,
1207 * etc.
1208 */
1209
1210 /* void* platformPrint = */
1211 /* printInfo->print.fullPrint.platformPrint; */
1212 /* NPBool printOne = */
1213 /* printInfo->print.fullPrint.printOne; */
1214
1215 /* Do the default*/
1216 printInfo->print.fullPrint.pluginPrinted = FALSE;
1217 }
1218 else { /* If not fullscreen, we must be embedded */
1219 /* NPWindow* printWindow = */
1220 /* &(printInfo->print.embedPrint.window); */
1221 /* void* platformPrint = */
1222 /* printInfo->print.embedPrint.platformPrint; */
1223 }
1224 }
1225}
1226
1227/* Local Variables: */
1228/* c-basic-offset: 8 */
1229/* End: */
Definition npapi.h:149