mozilla: don't hold any reference to root object to prevent ownership issues on...
[vlc.git] / mozilla / vlcplugin.cpp
1 /*****************************************************************************
2  * vlcplugin.cpp: a VLC plugin for Mozilla
3  *****************************************************************************
4  * Copyright (C) 2002-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Damien Fouilleul <damienf.fouilleul@laposte.net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "config.h"
29
30 #ifdef HAVE_MOZILLA_CONFIG_H
31 #   include <mozilla-config.h>
32 #endif
33
34 #include "vlcplugin.h"
35 #include "control/npovlc.h"
36 #include "control/npolibvlc.h"
37
38 #include <ctype.h>
39
40 /*****************************************************************************
41  * VlcPlugin constructor and destructor
42  *****************************************************************************/
43 VlcPlugin::VlcPlugin( NPP instance, uint16 mode ) :
44     i_npmode(mode),
45     b_stream(0),
46     b_autoplay(1),
47     psz_target(NULL),
48     libvlc_instance(NULL),
49     libvlc_log(NULL),
50     p_scriptClass(NULL),
51     p_browser(instance),
52     psz_baseURL(NULL)
53 #if XP_WIN
54     ,pf_wndproc(NULL)
55 #endif
56 #if XP_UNIX
57     ,i_width((unsigned)-1)
58     ,i_height((unsigned)-1)
59 #endif
60 {
61     memset(&npwindow, 0, sizeof(NPWindow));
62 }
63
64 static bool boolValue(const char *value) {
65     return ( !strcmp(value, "1") || 
66              !strcasecmp(value, "true") ||
67              !strcasecmp(value, "yes") );
68 }
69
70 NPError VlcPlugin::init(int argc, char* const argn[], char* const argv[])
71 {
72     /* prepare VLC command line */
73     char *ppsz_argv[32] = { "vlc" };
74     int ppsz_argc = 1;
75
76     /* locate VLC module path */
77 #ifdef XP_MACOSX
78     ppsz_argv[ppsz_argc++] = "--plugin-path";
79     ppsz_argv[ppsz_argc++] = "/Library/Internet Plug-Ins/VLC Plugin.plugin/"
80                              "Contents/MacOS/modules";
81 #elif defined(XP_WIN)
82     HKEY h_key;
83     DWORD i_type, i_data = MAX_PATH + 1;
84     char p_data[MAX_PATH + 1];
85     if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
86                       0, KEY_READ, &h_key ) == ERROR_SUCCESS )
87     {
88          if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
89                               (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
90          {
91              if( i_type == REG_SZ )
92              {
93                  strcat( p_data, "\\plugins" );
94                  ppsz_argv[ppsz_argc++] = "--plugin-path";
95                  ppsz_argv[ppsz_argc++] = p_data;
96              }
97          }
98          RegCloseKey( h_key );
99     }
100     ppsz_argv[ppsz_argc++] = "--no-one-instance";
101     if( IsDebuggerPresent() )
102     {
103         /*
104         ** VLC default threading mechanism is designed to be as compatible
105         ** with POSIX as possible. However when debugged on win32, threads
106         ** lose signals and eventually VLC get stuck during initialization.
107         ** threading support can be configured to be more debugging friendly
108         ** but it will be less compatible with POSIX.
109         ** This is done by initializing with the following options:
110         */
111         ppsz_argv[ppsz_argc++] = "--fast-mutex";
112         ppsz_argv[ppsz_argc++] = "--win9x-cv-method=1";
113     }
114
115
116 #if 0
117     ppsz_argv[0] = "C:\\Cygwin\\home\\damienf\\vlc-trunk\\vlc";
118 #endif
119
120 #endif /* XP_MACOSX */
121
122     /* common settings */
123     ppsz_argv[ppsz_argc++] = "-vv";
124     ppsz_argv[ppsz_argc++] = "--no-stats";
125     ppsz_argv[ppsz_argc++] = "--no-media-library";
126     ppsz_argv[ppsz_argc++] = "--intf";
127     ppsz_argv[ppsz_argc++] = "dummy";
128
129     const char *progid = NULL;
130
131     /* parse plugin arguments */
132     for( int i = 0; i < argc ; i++ )
133     {
134         fprintf(stderr, "argn=%s, argv=%s\n", argn[i], argv[i]);
135
136         if( !strcmp( argn[i], "target" )
137          || !strcmp( argn[i], "mrl")
138          || !strcmp( argn[i], "filename")
139          || !strcmp( argn[i], "src") )
140         {
141             psz_target = argv[i];
142         }
143         else if( !strcmp( argn[i], "autoplay")
144               || !strcmp( argn[i], "autostart") )
145         {
146             b_autoplay = boolValue(argv[i]);
147         }
148         else if( !strcmp( argn[i], "fullscreen" ) )
149         {
150             if( boolValue(argv[i]) )
151             {
152                 ppsz_argv[ppsz_argc++] = "--fullscreen";
153             }
154             else
155             {
156                 ppsz_argv[ppsz_argc++] = "--no-fullscreen";
157             }
158         }
159         else if( !strcmp( argn[i], "mute" ) )
160         {
161             if( boolValue(argv[i]) )
162             {
163                 ppsz_argv[ppsz_argc++] = "--volume";
164                 ppsz_argv[ppsz_argc++] = "0";
165             }
166         }
167         else if( !strcmp( argn[i], "loop")
168               || !strcmp( argn[i], "autoloop") )
169         {
170             if( boolValue(argv[i]) )
171             {
172                 ppsz_argv[ppsz_argc++] = "--loop";
173             }
174             else {
175                 ppsz_argv[ppsz_argc++] = "--no-loop";
176             }
177         }
178         else if( !strcmp( argn[i], "version")
179               || !strcmp( argn[i], "progid") )
180         {
181             progid = argv[i];
182         }
183     }
184
185     libvlc_instance = libvlc_new(ppsz_argc, ppsz_argv, NULL);
186     if( ! libvlc_instance )
187     {
188         return NPERR_GENERIC_ERROR;
189     }
190
191     /*
192     ** fetch plugin base URL, which is the URL of the page containing the plugin
193     ** this URL is used for making absolute URL from relative URL that may be
194     ** passed as an MRL argument
195     */
196     NPObject *plugin;
197
198     if( NPERR_NO_ERROR == NPN_GetValue(p_browser, NPNVWindowNPObject, &plugin) )
199     {
200         /*
201         ** is there a better way to get that info ?
202         */
203         static const char docLocHref[] = "document.location.href";
204         NPString script;
205         NPVariant result;
206
207         script.utf8characters = docLocHref;
208         script.utf8length = sizeof(docLocHref)-1;
209
210         if( NPN_Evaluate(p_browser, plugin, &script, &result) )
211         {
212             if( NPVARIANT_IS_STRING(result) )
213             {
214                 NPString &location = NPVARIANT_TO_STRING(result);
215
216                 psz_baseURL = new char[location.utf8length+1];
217                 if( psz_baseURL )
218                 {
219                     strncpy(psz_baseURL, location.utf8characters, location.utf8length);
220                     psz_baseURL[location.utf8length] = '\0';
221                 }
222             }
223             NPN_ReleaseVariantValue(&result);
224         }
225         NPN_ReleaseObject(plugin);
226     }
227
228     if( psz_target )
229     {
230         // get absolute URL from src
231         char *psz_absurl = getAbsoluteURL(psz_target);
232         psz_target = psz_absurl ? psz_absurl : strdup(psz_target);
233     }
234
235     /* assign plugin script root class */
236     if( (NULL != progid) && (!strcmp(progid, "VideoLAN.VLCPlugin.2")) )
237     {
238         /* new APIs */
239         p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
240     }
241     else
242     {
243         /* legacy APIs */
244         p_scriptClass = RuntimeNPClass<VlcNPObject>::getClass();
245     }
246
247     return NPERR_NO_ERROR;
248 }
249
250 #if 0
251 #ifdef XP_WIN
252 /* This is really ugly but there is a deadlock when stopping a stream
253  * (in VLC_CleanUp()) because the video output is a child of the drawable but
254  * is in a different thread. */
255 static void HackStopVout( VlcPlugin* p_plugin )
256 {
257     MSG msg;
258     HWND hwnd;
259     vlc_value_t value;
260
261     int i_vlc = libvlc_get_vlc_id(p_plugin->libvlc_instance);
262     VLC_VariableGet( i_vlc, "drawable", &value );
263
264     hwnd = FindWindowEx( (HWND)value.i_int, 0, 0, 0 );
265     if( !hwnd ) return;
266
267     PostMessage( hwnd, WM_CLOSE, 0, 0 );
268
269     do
270     {
271         while( PeekMessage( &msg, (HWND)value.i_int, 0, 0, PM_REMOVE ) )
272         {
273             TranslateMessage(&msg);
274             DispatchMessage(&msg);
275         }
276         if( FindWindowEx( (HWND)value.i_int, 0, 0, 0 ) ) Sleep( 10 );
277     }
278     while( (hwnd = FindWindowEx( (HWND)value.i_int, 0, 0, 0 )) );
279 }
280 #endif /* XP_WIN */
281 #endif
282
283 VlcPlugin::~VlcPlugin()
284 {
285     delete psz_baseURL;
286     delete psz_target;
287     if( libvlc_log )
288         libvlc_log_close(libvlc_log, NULL);
289     if( libvlc_instance )
290         libvlc_destroy(libvlc_instance, NULL );
291 }
292
293 /*****************************************************************************
294  * VlcPlugin methods
295  *****************************************************************************/
296
297 char *VlcPlugin::getAbsoluteURL(const char *url)
298 {
299     if( NULL != url )
300     {
301         // check whether URL is already absolute
302         const char *end=strchr(url, ':');
303         if( (NULL != end) && (end != url) )
304         {
305             // validate protocol header
306             const char *start = url;
307             while( start != end ) {
308                 char c = tolower(*start);
309                 if( (c < 'a') || (c > 'z') )
310                     // not valid protocol header, assume relative URL
311                     goto relativeurl;
312                 ++start;
313             }
314             /* we have a protocol header, therefore URL is absolute */
315             return strdup(url);
316         }
317
318 relativeurl:
319
320         if( psz_baseURL )
321         {
322             size_t baseLen = strlen(psz_baseURL);
323             char *href = new char[baseLen+strlen(url)+1];
324             if( href )
325             {
326                 /* prepend base URL */
327                 strcpy(href, psz_baseURL);
328
329                 /*
330                 ** relative url could be empty,
331                 ** in which case return base URL
332                 */
333                 if( '\0' == *url )
334                     return href;
335
336                 /*
337                 ** locate pathname part of base URL
338                 */
339
340                 /* skip over protocol part  */
341                 char *pathstart = strchr(href, ':');
342                 char *pathend;
343                 if( pathstart )
344                 {
345                     if( '/' == *(++pathstart) )
346                     {
347                         if( '/' == *(++pathstart) )
348                         {
349                             ++pathstart;
350                         }
351                     }
352                     /* skip over host part */
353                     pathstart = strchr(pathstart, '/');
354                     pathend = href+baseLen;
355                     if( ! pathstart )
356                     {
357                         // no path, add a / past end of url (over '\0')
358                         pathstart = pathend;
359                         *pathstart = '/';
360                     }
361                 }
362                 else
363                 {
364                     /* baseURL is just a UNIX path */
365                     if( '/' != *href )
366                     {
367                         /* baseURL is not an absolute path */
368                         return NULL;
369                     }
370                     pathstart = href;
371                     pathend = href+baseLen;
372                 }
373
374                 /* relative URL made of an absolute path ? */
375                 if( '/' == *url )
376                 {
377                     /* replace path completely */
378                     strcpy(pathstart, url);
379                     return href;
380                 }
381
382                 /* find last path component and replace it */ 
383                 while( '/' != *pathend)
384                     --pathend;
385
386                 /*
387                 ** if relative url path starts with one or more '../',
388                 ** factor them out of href so that we return a
389                 ** normalized URL
390                 */
391                 while( pathend != pathstart )
392                 {
393                     const char *p = url;
394                     if( '.' != *p )
395                         break;
396                     ++p;
397                     if( '\0' == *p  )
398                     {
399                         /* relative url is just '.' */
400                         url = p;
401                         break;
402                     }
403                     if( '/' == *p  )
404                     {
405                         /* relative url starts with './' */
406                         url = ++p;
407                         continue;
408                     }
409                     if( '.' != *p ) 
410                         break;
411                     ++p;
412                     if( '\0' == *p )
413                     {
414                         /* relative url is '..' */
415                     }
416                     else
417                     {
418                         if( '/' != *p ) 
419                             break;
420                         /* relative url starts with '../' */
421                         ++p;
422                     }
423                     url = p;
424                     do
425                     {
426                         --pathend;
427                     }
428                     while( '/' != *pathend );
429                 }
430                 /* skip over '/' separator */
431                 ++pathend;
432                 /* concatenate remaining base URL and relative URL */
433                 strcpy(pathend, url);
434             }
435             return href;
436         }
437     }
438     return NULL;
439 }
440
441 #if XP_UNIX
442 int  VlcPlugin::setSize(unsigned width, unsigned height)
443 {
444     int diff = (width != i_width) || (height != i_height);
445
446     i_width = width;
447     i_height = height;
448
449     /* return size */
450     return diff;
451 }
452 #endif
453