]> git.sesse.net Git - vlc/blob - projects/mozilla/vlcplugin.cpp
Change plugin path for Mozilla plugin on Mac OS X
[vlc] / projects / mozilla / vlcplugin.cpp
1 /*****************************************************************************
2  * vlcplugin.cpp: a VLC plugin for Mozilla
3  *****************************************************************************
4  * Copyright (C) 2002-2010 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Damien Fouilleul <damienf.fouilleul@laposte.net>
9  *          Jean-Paul Saman <jpsaman@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include "config.h"
30
31 #include "vlcplugin.h"
32 #include "control/npolibvlc.h"
33
34 #include <ctype.h>
35
36 #if defined(XP_UNIX)
37 #   include <pthread.h>
38 #elif defined(XP_WIN)
39     /* windows headers */
40 #   include <winbase.h>
41 #else
42 #warning "locking not implemented for this platform"
43 #endif
44
45 #include <stdio.h>
46 #include <assert.h>
47 #include <stdlib.h>
48
49 /*****************************************************************************
50  * utilitiy functions
51  *****************************************************************************/
52 static void plugin_lock_init(plugin_lock_t *lock)
53 {
54     assert(lock);
55
56 #if defined(XP_UNIX)
57     pthread_mutex_init(&lock->mutex, NULL);
58 #elif defined(XP_WIN)
59     InitializeCriticalSection(&lock->cs);
60 #else
61 #warning "locking not implemented in this platform"
62 #endif
63 }
64
65 static void plugin_lock_destroy(plugin_lock_t *lock)
66 {
67     assert(lock);
68
69 #if defined(XP_UNIX)
70     pthread_mutex_destroy(&lock->mutex);
71 #elif defined(XP_WIN)
72     DeleteCriticalSection(&lock->cs);
73 #else
74 #warning "locking not implemented in this platform"
75 #endif
76 }
77
78 static void plugin_lock(plugin_lock_t *lock)
79 {
80     assert(lock);
81
82 #if defined(XP_UNIX)
83     pthread_mutex_lock(&lock->mutex);
84 #elif defined(XP_WIN)
85     EnterCriticalSection(&lock->cs);
86 #else
87 #warning "locking not implemented in this platform"
88 #endif
89 }
90
91 static void plugin_unlock(plugin_lock_t *lock)
92 {
93     assert(lock);
94
95 #if defined(XP_UNIX)
96     pthread_mutex_unlock(&lock->mutex);
97 #elif defined(XP_WIN)
98     LeaveCriticalSection(&lock->cs);
99 #else
100 #warning "locking not implemented in this platform"
101 #endif
102 }
103
104 /*****************************************************************************
105  * VlcPlugin constructor and destructor
106  *****************************************************************************/
107 #if (((NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR) < 20)
108 VlcPlugin::VlcPlugin( NPP instance, uint16 mode ) :
109 #else
110 VlcPlugin::VlcPlugin( NPP instance, uint16_t mode ) :
111 #endif
112     i_npmode(mode),
113     b_stream(0),
114     b_autoplay(1),
115     b_toolbar(0),
116     psz_text(NULL),
117     psz_target(NULL),
118     playlist_index(-1),
119     libvlc_instance(NULL),
120     libvlc_media_list(NULL),
121     libvlc_media_player(NULL),
122     p_scriptClass(NULL),
123     p_browser(instance),
124     psz_baseURL(NULL)
125 #if defined(XP_WIN)
126     ,pf_wndproc(NULL)
127 #endif
128 #if defined(XP_UNIX)
129     ,i_width((unsigned)-1)
130     ,i_height((unsigned)-1)
131     ,i_tb_width(0)
132     ,i_tb_height(0)
133     ,i_last_position(0)
134     ,p_btnPlay(NULL)
135     ,p_btnPause(NULL)
136     ,p_btnStop(NULL)
137     ,p_btnMute(NULL)
138     ,p_btnUnmute(NULL)
139     ,p_btnFullscreen(NULL)
140     ,p_btnTime(NULL)
141     ,p_timeline(NULL)
142 #endif
143 {
144     memset(&npwindow, 0, sizeof(NPWindow));
145 #if defined(XP_UNIX)
146     memset(&npvideo, 0, sizeof(Window));
147     memset(&npcontrol, 0, sizeof(Window));
148 #endif
149 }
150
151 static bool boolValue(const char *value) {
152     return ( !strcmp(value, "1") ||
153              !strcasecmp(value, "true") ||
154              !strcasecmp(value, "yes") );
155 }
156
157 bool EventObj::init()
158 {
159     plugin_lock_init(&lock);
160     return true;
161 }
162
163 EventObj::~EventObj()
164 {
165     plugin_lock_destroy(&lock);
166 }
167
168 void EventObj::deliver(NPP browser)
169 {
170     NPVariant result;
171     NPVariant params[1];
172
173     plugin_lock(&lock);
174
175     for( ev_l::iterator i=_elist.begin();i!=_elist.end();++i )
176     {
177         libvlc_event_type_t event = *i;
178         STRINGZ_TO_NPVARIANT(libvlc_event_type_name(event), params[0]);
179
180         // Invalid events aren't supposed to be queued up.
181         // if( !have_event(event) ) continue;
182
183         for( lr_l::iterator j=_llist.begin();j!=_llist.end();++j )
184         {
185             if (j->get(event))
186             {
187                 NPN_InvokeDefault(browser, j->listener(), params, 1, &result);
188                 NPN_ReleaseVariantValue(&result);
189             }
190         }
191     }
192     _elist.clear();
193
194     plugin_unlock(&lock);
195 }
196
197 void VlcPlugin::eventAsync(void *param)
198 {
199     VlcPlugin *plugin = (VlcPlugin*)param;
200     plugin->events.deliver(plugin->getBrowser());
201 }
202
203 void EventObj::callback(const libvlc_event_t* event)
204 {
205     plugin_lock(&lock);
206
207     if( have_event(event->type) )
208         _elist.push_back(event->type);
209
210     plugin_unlock(&lock);
211 }
212
213 void VlcPlugin::event_callback(const libvlc_event_t* event, void *param)
214 {
215     VlcPlugin *plugin = (VlcPlugin*)param;
216 #if defined(XP_UNIX)
217     plugin->events.callback(event);
218     NPN_PluginThreadAsyncCall(plugin->getBrowser(), eventAsync, plugin);
219 #else
220 #warning NPN_PluginThreadAsyncCall not implemented yet.
221     printf("No NPN_PluginThreadAsyncCall(), doing nothing.");
222 #endif
223 }
224
225 inline EventObj::event_t EventObj::find_event(const char *s) const
226 {
227     event_t i;
228     for(i=0;i<maxbit();++i)
229         if(!strcmp(s,libvlc_event_type_name(i)))
230             break;
231     return i;
232 }
233
234 bool EventObj::insert(const NPString &s, NPObject *l, bool b)
235 {
236     event_t e = find_event(s.UTF8Characters);
237     if( e>=maxbit() )
238         return false;
239
240     if( !have_event(e) && !ask_for_event(e) )
241         return false;
242
243     lr_l::iterator i;
244     for(i=_llist.begin();i!=_llist.end();++i)
245         if(i->listener()==l && i->bubble()==b)
246             break;
247
248     if( i == _llist.end() ) {
249         _llist.push_back(Listener(e,l,b));
250     } else {
251         if( i->get(e) )
252             return false;
253         i->get(e);
254     }
255     return true;
256 }
257
258
259 bool EventObj::remove(const NPString &s, NPObject *l, bool b)
260 {
261     event_t e = find_event(s.UTF8Characters);
262     if( e>=maxbit() || !get(e) )
263         return false;
264
265     bool any=false;
266     for(lr_l::iterator i=_llist.begin();i!=_llist.end();)
267     {
268         if(i->listener()!=l || i->bubble()!=b)
269             any|=i->get(e);
270         else
271         {
272             i->reset(e);
273             if(i->empty())
274             {
275                 i=_llist.erase(i);
276                 continue;
277             }
278         }
279         ++i;
280     }
281     if(!any)
282         unask_for_event(e);
283
284     return true;
285 }
286
287
288 void EventObj::hook_manager(libvlc_event_manager_t *em,
289                             libvlc_callback_t cb, void *udata)
290 {
291     _em = em; _cb = cb; _ud = udata;
292     if( !_em )
293         return;
294     for(size_t i=0;i<maxbit();++i)
295         if(get(i))
296             libvlc_event_attach(_em, i, _cb, _ud);
297 }
298
299 void EventObj::unhook_manager()
300 {
301     if( !_em )
302     return;
303     for(size_t i=0;i<maxbit();++i)
304         if(get(i))
305             libvlc_event_detach(_em, i, _cb, _ud);
306 }
307
308
309 bool EventObj::ask_for_event(event_t e)
310 {
311     return _em?0==libvlc_event_attach(_em, e, _cb, _ud):false;
312 }
313
314
315 void EventObj::unask_for_event(event_t e)
316 {
317     if(_em) libvlc_event_detach(_em, e, _cb, _ud);
318 }
319
320
321 NPError VlcPlugin::init(int argc, char* const argn[], char* const argv[])
322 {
323     /* prepare VLC command line */
324     const char *ppsz_argv[32];
325     int ppsz_argc = 0;
326
327 #ifndef NDEBUG
328     ppsz_argv[ppsz_argc++] = "--no-plugins-cache";
329 #endif
330
331     /* locate VLC module path */
332 #ifdef XP_MACOSX
333     ppsz_argv[ppsz_argc++] = "--plugin-path=/Library/Internet\\ Plug-Ins/VLC\\ Plugin.plugin/Contents/MacOS/plugins";
334     ppsz_argv[ppsz_argc++] = "--vout=minimal_macosx";
335 #elif defined(XP_WIN)
336     HKEY h_key;
337     DWORD i_type, i_data = MAX_PATH + 1;
338     char p_data[MAX_PATH + 1];
339     if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
340                       0, KEY_READ, &h_key ) == ERROR_SUCCESS )
341     {
342          if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
343                               (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
344          {
345              if( i_type == REG_SZ )
346              {
347                  strcat( p_data, "\\plugins" );
348                  ppsz_argv[ppsz_argc++] = "--plugin-path";
349                  ppsz_argv[ppsz_argc++] = p_data;
350              }
351          }
352          RegCloseKey( h_key );
353     }
354     ppsz_argv[ppsz_argc++] = "--no-one-instance";
355
356 #endif /* XP_MACOSX */
357
358     /* common settings */
359     ppsz_argv[ppsz_argc++] = "-vv";
360     ppsz_argv[ppsz_argc++] = "--no-stats";
361     ppsz_argv[ppsz_argc++] = "--no-media-library";
362     ppsz_argv[ppsz_argc++] = "--intf=dummy";
363     ppsz_argv[ppsz_argc++] = "--no-video-title-show";
364
365     const char *progid = NULL;
366
367     /* parse plugin arguments */
368     for( int i = 0; (i < argc) && (ppsz_argc < 32); i++ )
369     {
370        /* fprintf(stderr, "argn=%s, argv=%s\n", argn[i], argv[i]); */
371
372         if( !strcmp( argn[i], "target" )
373          || !strcmp( argn[i], "mrl")
374          || !strcmp( argn[i], "filename")
375          || !strcmp( argn[i], "src") )
376         {
377             psz_target = argv[i];
378         }
379         else if( !strcmp( argn[i], "text" ) )
380         {
381             free( psz_text );
382             psz_text = strdup( argv[i] );
383         }
384         else if( !strcmp( argn[i], "autoplay")
385               || !strcmp( argn[i], "autostart") )
386         {
387             b_autoplay = boolValue(argv[i]);
388         }
389         else if( !strcmp( argn[i], "fullscreen" ) )
390         {
391             if( boolValue(argv[i]) )
392             {
393                 ppsz_argv[ppsz_argc++] = "--fullscreen";
394             }
395             else
396             {
397                 ppsz_argv[ppsz_argc++] = "--no-fullscreen";
398             }
399         }
400         else if( !strcmp( argn[i], "mute" ) )
401         {
402             if( boolValue(argv[i]) )
403             {
404                 ppsz_argv[ppsz_argc++] = "--volume=0";
405             }
406         }
407         else if( !strcmp( argn[i], "loop")
408               || !strcmp( argn[i], "autoloop") )
409         {
410             if( boolValue(argv[i]) )
411             {
412                 ppsz_argv[ppsz_argc++] = "--loop";
413             }
414             else
415             {
416                 ppsz_argv[ppsz_argc++] = "--no-loop";
417             }
418         }
419         else if( !strcmp( argn[i], "version")
420               || !strcmp( argn[i], "progid") )
421         {
422             progid = argv[i];
423         }
424         else if( !strcmp( argn[i], "toolbar" ) )
425         {
426 /* FIXME: Remove this when toolbar functionality has been implemented on
427  * MacOS X and Win32 for Firefox/Mozilla/Safari. */
428 #ifdef XP_UNIX
429             b_toolbar = boolValue(argv[i]);
430 #endif
431         }
432     }
433
434     libvlc_instance = libvlc_new(ppsz_argc, ppsz_argv);
435     if( !libvlc_instance )
436         return NPERR_GENERIC_ERROR;
437     libvlc_media_list = libvlc_media_list_new(libvlc_instance);
438
439     /*
440     ** fetch plugin base URL, which is the URL of the page containing the plugin
441     ** this URL is used for making absolute URL from relative URL that may be
442     ** passed as an MRL argument
443     */
444     NPObject *plugin = NULL;
445
446     if( NPERR_NO_ERROR == NPN_GetValue(p_browser, NPNVWindowNPObject, &plugin) )
447     {
448         /*
449         ** is there a better way to get that info ?
450         */
451         static const char docLocHref[] = "document.location.href";
452         NPString script;
453         NPVariant result;
454
455         script.UTF8Characters = docLocHref;
456         script.UTF8Length = sizeof(docLocHref)-1;
457
458         if( NPN_Evaluate(p_browser, plugin, &script, &result) )
459         {
460             if( NPVARIANT_IS_STRING(result) )
461             {
462                 NPString &location = NPVARIANT_TO_STRING(result);
463
464                 psz_baseURL = (char *) malloc(location.UTF8Length+1);
465                 if( psz_baseURL )
466                 {
467                     strncpy(psz_baseURL, location.UTF8Characters, location.UTF8Length);
468                     psz_baseURL[location.UTF8Length] = '\0';
469                 }
470             }
471             NPN_ReleaseVariantValue(&result);
472         }
473         NPN_ReleaseObject(plugin);
474     }
475
476     if( psz_target )
477     {
478         // get absolute URL from src
479         char *psz_absurl = getAbsoluteURL(psz_target);
480         psz_target = psz_absurl ? psz_absurl : strdup(psz_target);
481     }
482
483     /* assign plugin script root class */
484     /* new APIs */
485     p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
486
487     if( !events.init() )
488         return NPERR_GENERIC_ERROR;
489
490     return NPERR_NO_ERROR;
491 }
492
493 VlcPlugin::~VlcPlugin()
494 {
495     free(psz_baseURL);
496     free(psz_target);
497     free(psz_text);
498
499     if( libvlc_media_player )
500     {
501         if( playlist_isplaying() )
502             playlist_stop();
503         events.unhook_manager();
504         libvlc_media_player_release( libvlc_media_player );
505     }
506     if( libvlc_media_list )
507         libvlc_media_list_release( libvlc_media_list );
508     if( libvlc_instance )
509         libvlc_release(libvlc_instance);
510 }
511
512 /*****************************************************************************
513  * VlcPlugin playlist replacement methods
514  *****************************************************************************/
515 void VlcPlugin::set_player_window()
516 {
517 #ifdef XP_UNIX
518     libvlc_media_player_set_xwindow(libvlc_media_player,
519                                     (uint32_t)getVideoWindow());
520 #endif
521 #ifdef XP_MACOSX
522     // XXX FIXME insert appropriate call here
523 #endif
524 #ifdef XP_WIN
525     libvlc_media_player_set_hwnd(libvlc_media_player,
526                                  getWindow().window);
527 #endif
528 }
529
530 int VlcPlugin::playlist_add( const char *mrl )
531 {
532     int item = -1;
533     libvlc_media_t *p_m = libvlc_media_new_location(libvlc_instance,mrl);
534     if( !p_m )
535         return -1;
536     assert( libvlc_media_list );
537     libvlc_media_list_lock(libvlc_media_list);
538     if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
539         item = libvlc_media_list_count(libvlc_media_list)-1;
540     libvlc_media_list_unlock(libvlc_media_list);
541
542     libvlc_media_release(p_m);
543
544     return item;
545 }
546
547 int VlcPlugin::playlist_add_extended_untrusted( const char *mrl, const char *name,
548                     int optc, const char **optv )
549 {
550     libvlc_media_t *p_m;
551     int item = -1;
552
553     assert( libvlc_media_list );
554
555     p_m = libvlc_media_new_location(libvlc_instance, mrl);
556     if( !p_m )
557         return -1;
558
559     for( int i = 0; i < optc; ++i )
560         libvlc_media_add_option_flag(p_m, optv[i], libvlc_media_option_unique);
561
562     libvlc_media_list_lock(libvlc_media_list);
563     if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
564         item = libvlc_media_list_count(libvlc_media_list)-1;
565     libvlc_media_list_unlock(libvlc_media_list);
566     libvlc_media_release(p_m);
567
568     return item;
569 }
570
571 bool VlcPlugin::playlist_select( int idx )
572 {
573     libvlc_media_t *p_m = NULL;
574
575     assert( libvlc_media_list );
576
577     libvlc_media_list_lock(libvlc_media_list);
578
579     int count = libvlc_media_list_count(libvlc_media_list);
580     if( idx<0||idx>=count )
581         goto bad_unlock;
582
583     playlist_index = idx;
584
585     p_m = libvlc_media_list_item_at_index(libvlc_media_list,playlist_index);
586     libvlc_media_list_unlock(libvlc_media_list);
587
588     if( !p_m )
589         return false;
590
591     if( libvlc_media_player )
592     {
593         if( playlist_isplaying() )
594             playlist_stop();
595         events.unhook_manager();
596         libvlc_media_player_release( libvlc_media_player );
597         libvlc_media_player = NULL;
598     }
599
600     libvlc_media_player = libvlc_media_player_new_from_media(p_m);
601     if( libvlc_media_player )
602     {
603         set_player_window();
604         events.hook_manager(
605                       libvlc_media_player_event_manager(libvlc_media_player),
606                       event_callback, this);
607     }
608
609     libvlc_media_release( p_m );
610     return true;
611
612 bad_unlock:
613     libvlc_media_list_unlock(libvlc_media_list);
614     return false;
615 }
616
617 int VlcPlugin::playlist_delete_item( int idx )
618 {
619     if( !libvlc_media_list )
620         return -1;
621     libvlc_media_list_lock(libvlc_media_list);
622     int ret = libvlc_media_list_remove_index(libvlc_media_list,idx);
623     libvlc_media_list_unlock(libvlc_media_list);
624     return ret;
625 }
626
627 void VlcPlugin::playlist_clear()
628 {
629     if( libvlc_media_list )
630         libvlc_media_list_release(libvlc_media_list);
631     libvlc_media_list = libvlc_media_list_new(getVLC());
632 }
633
634 int VlcPlugin::playlist_count()
635 {
636     int items_count = 0;
637     if( !libvlc_media_list )
638         return items_count;
639     libvlc_media_list_lock(libvlc_media_list);
640     items_count = libvlc_media_list_count(libvlc_media_list);
641     libvlc_media_list_unlock(libvlc_media_list);
642     return items_count;
643 }
644
645 void VlcPlugin::toggle_fullscreen()
646 {
647     if( playlist_isplaying() )
648         libvlc_toggle_fullscreen(libvlc_media_player);
649 }
650
651 void VlcPlugin::set_fullscreen( int yes )
652 {
653     if( playlist_isplaying() )
654         libvlc_set_fullscreen(libvlc_media_player,yes);
655 }
656
657 int  VlcPlugin::get_fullscreen()
658 {
659     int r = 0;
660     if( playlist_isplaying() )
661         r = libvlc_get_fullscreen(libvlc_media_player);
662     return r;
663 }
664
665 bool  VlcPlugin::player_has_vout()
666 {
667     bool r = false;
668     if( playlist_isplaying() )
669         r = libvlc_media_player_has_vout(libvlc_media_player);
670     return r;
671 }
672
673 /*****************************************************************************
674  * VlcPlugin methods
675  *****************************************************************************/
676
677 char *VlcPlugin::getAbsoluteURL(const char *url)
678 {
679     if( NULL != url )
680     {
681         // check whether URL is already absolute
682         const char *end=strchr(url, ':');
683         if( (NULL != end) && (end != url) )
684         {
685             // validate protocol header
686             const char *start = url;
687             char c = *start;
688             if( isalpha(c) )
689             {
690                 ++start;
691                 while( start != end )
692                 {
693                     c  = *start;
694                     if( ! (isalnum(c)
695                        || ('-' == c)
696                        || ('+' == c)
697                        || ('.' == c)
698                        || ('/' == c)) ) /* VLC uses / to allow user to specify a demuxer */
699                         // not valid protocol header, assume relative URL
700                         goto relativeurl;
701                     ++start;
702                 }
703                 /* we have a protocol header, therefore URL is absolute */
704                 return strdup(url);
705             }
706             // not a valid protocol header, assume relative URL
707         }
708
709 relativeurl:
710
711         if( psz_baseURL )
712         {
713             size_t baseLen = strlen(psz_baseURL);
714             char *href = (char *) malloc(baseLen+strlen(url)+1);
715             if( href )
716             {
717                 /* prepend base URL */
718                 memcpy(href, psz_baseURL, baseLen+1);
719
720                 /*
721                 ** relative url could be empty,
722                 ** in which case return base URL
723                 */
724                 if( '\0' == *url )
725                     return href;
726
727                 /*
728                 ** locate pathname part of base URL
729                 */
730
731                 /* skip over protocol part  */
732                 char *pathstart = strchr(href, ':');
733                 char *pathend = href+baseLen;
734                 if( pathstart )
735                 {
736                     if( '/' == *(++pathstart) )
737                     {
738                         if( '/' == *(++pathstart) )
739                         {
740                             ++pathstart;
741                         }
742                     }
743                     /* skip over host part */
744                     pathstart = strchr(pathstart, '/');
745                     if( ! pathstart )
746                     {
747                         // no path, add a / past end of url (over '\0')
748                         pathstart = pathend;
749                         *pathstart = '/';
750                     }
751                 }
752                 else
753                 {
754                     /* baseURL is just a UNIX path */
755                     if( '/' != *href )
756                     {
757                         /* baseURL is not an absolute path */
758                         free(href);
759                         return NULL;
760                     }
761                     pathstart = href;
762                 }
763
764                 /* relative URL made of an absolute path ? */
765                 if( '/' == *url )
766                 {
767                     /* replace path completely */
768                     strcpy(pathstart, url);
769                     return href;
770                 }
771
772                 /* find last path component and replace it */
773                 while( '/' != *pathend)
774                     --pathend;
775
776                 /*
777                 ** if relative url path starts with one or more '../',
778                 ** factor them out of href so that we return a
779                 ** normalized URL
780                 */
781                 while( pathend != pathstart )
782                 {
783                     const char *p = url;
784                     if( '.' != *p )
785                         break;
786                     ++p;
787                     if( '\0' == *p  )
788                     {
789                         /* relative url is just '.' */
790                         url = p;
791                         break;
792                     }
793                     if( '/' == *p  )
794                     {
795                         /* relative url starts with './' */
796                         url = ++p;
797                         continue;
798                     }
799                     if( '.' != *p )
800                         break;
801                     ++p;
802                     if( '\0' == *p )
803                     {
804                         /* relative url is '..' */
805                     }
806                     else
807                     {
808                         if( '/' != *p )
809                             break;
810                         /* relative url starts with '../' */
811                         ++p;
812                     }
813                     url = p;
814                     do
815                     {
816                         --pathend;
817                     }
818                     while( '/' != *pathend );
819                 }
820                 /* skip over '/' separator */
821                 ++pathend;
822                 /* concatenate remaining base URL and relative URL */
823                 strcpy(pathend, url);
824             }
825             return href;
826         }
827     }
828     return NULL;
829 }
830
831 #if defined(XP_UNIX)
832 int  VlcPlugin::setSize(unsigned width, unsigned height)
833 {
834     int diff = (width != i_width) || (height != i_height);
835
836     i_width = width;
837     i_height = height;
838
839     /* return size */
840     return diff;
841 }
842
843 #define BTN_SPACE ((unsigned int)4)
844 void VlcPlugin::showToolbar()
845 {
846     const NPWindow& window = getWindow();
847     Window control = getControlWindow();
848     Window video = getVideoWindow();
849     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
850     unsigned int i_height = 0, i_width = BTN_SPACE;
851
852     /* load icons */
853     if( !p_btnPlay )
854         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/play.xpm",
855                             &p_btnPlay, NULL, NULL);
856     if( p_btnPlay )
857     {
858         i_height = __MAX( i_height, p_btnPlay->height );
859     }
860     if( !p_btnPause )
861         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/pause.xpm",
862                             &p_btnPause, NULL, NULL);
863     if( p_btnPause )
864     {
865         i_height = __MAX( i_height, p_btnPause->height );
866     }
867     i_width += __MAX( p_btnPause->width, p_btnPlay->width );
868
869     if( !p_btnStop )
870         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/stop.xpm",
871                             &p_btnStop, NULL, NULL );
872     if( p_btnStop )
873     {
874         i_height = __MAX( i_height, p_btnStop->height );
875         i_width += BTN_SPACE + p_btnStop->width;
876     }
877     if( !p_timeline )
878         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/time_line.xpm",
879                             &p_timeline, NULL, NULL);
880     if( p_timeline )
881     {
882         i_height = __MAX( i_height, p_timeline->height );
883         i_width += BTN_SPACE + p_timeline->width;
884     }
885     if( !p_btnTime )
886         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/time_icon.xpm",
887                             &p_btnTime, NULL, NULL);
888     if( p_btnTime )
889     {
890         i_height = __MAX( i_height, p_btnTime->height );
891         i_width += BTN_SPACE + p_btnTime->width;
892     }
893     if( !p_btnFullscreen )
894         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/fullscreen.xpm",
895                             &p_btnFullscreen, NULL, NULL);
896     if( p_btnFullscreen )
897     {
898         i_height = __MAX( i_height, p_btnFullscreen->height );
899         i_width += BTN_SPACE + p_btnFullscreen->width;
900     }
901     if( !p_btnMute )
902         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/volume_max.xpm",
903                             &p_btnMute, NULL, NULL);
904     if( p_btnMute )
905     {
906         i_height = __MAX( i_height, p_btnMute->height );
907     }
908     if( !p_btnUnmute )
909         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/volume_mute.xpm",
910                             &p_btnUnmute, NULL, NULL);
911     if( p_btnUnmute )
912     {
913         i_height = __MAX( i_height, p_btnUnmute->height );
914     }
915     i_width += BTN_SPACE + __MAX( p_btnUnmute->width, p_btnMute->width );
916
917     setToolbarSize( i_width, i_height );
918
919     if( !p_btnPlay || !p_btnPause || !p_btnStop || !p_timeline ||
920         !p_btnTime || !p_btnFullscreen || !p_btnMute || !p_btnUnmute )
921         fprintf(stderr, "Error: some button images not found in %s\n", DATA_PATH );
922
923     /* reset panels position and size */
924     /* XXX  use i_width */
925     XResizeWindow( p_display, video, window.width, window.height - i_height);
926     XMoveWindow( p_display, control, 0, window.height - i_height );
927     XResizeWindow( p_display, control, window.width, i_height -1);
928
929     b_toolbar = 1; /* says toolbar is now shown */
930     redrawToolbar();
931 }
932
933 void VlcPlugin::hideToolbar()
934 {
935     const NPWindow& window = getWindow();
936     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
937     Window control = getControlWindow();
938     Window video = getVideoWindow();
939
940     i_tb_width = i_tb_height = 0;
941
942     if( p_btnPlay )  XDestroyImage( p_btnPlay );
943     if( p_btnPause ) XDestroyImage( p_btnPause );
944     if( p_btnStop )  XDestroyImage( p_btnStop );
945     if( p_timeline ) XDestroyImage( p_timeline );
946     if( p_btnTime )  XDestroyImage( p_btnTime );
947     if( p_btnFullscreen ) XDestroyImage( p_btnFullscreen );
948     if( p_btnMute )  XDestroyImage( p_btnMute );
949     if( p_btnUnmute ) XDestroyImage( p_btnUnmute );
950
951     p_btnPlay = NULL;
952     p_btnPause = NULL;
953     p_btnStop = NULL;
954     p_timeline = NULL;
955     p_btnTime = NULL;
956     p_btnFullscreen = NULL;
957     p_btnMute = NULL;
958     p_btnUnmute = NULL;
959
960     /* reset panels position and size */
961     /* XXX  use i_width */
962     XResizeWindow( p_display, video, window.width, window.height );
963     XMoveWindow( p_display, control, 0, window.height-1 );
964     XResizeWindow( p_display, control, window.width, 1 );
965
966     b_toolbar = 0; /* says toolbar is now hidden */
967     redrawToolbar();
968 }
969
970 void VlcPlugin::redrawToolbar()
971 {
972     int is_playing = 0;
973     bool b_mute = false;
974     unsigned int dst_x, dst_y;
975     GC gc;
976     XGCValues gcv;
977     unsigned int i_tb_width, i_tb_height;
978
979     /* This method does nothing if toolbar is hidden. */
980     if( !b_toolbar || !libvlc_media_player )
981         return;
982
983     const NPWindow& window = getWindow();
984     Window control = getControlWindow();
985     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
986
987     getToolbarSize( &i_tb_width, &i_tb_height );
988
989     /* get mute info */
990     b_mute = libvlc_audio_get_mute( libvlc_media_player );
991
992     gcv.foreground = BlackPixel( p_display, 0 );
993     gc = XCreateGC( p_display, control, GCForeground, &gcv );
994
995     XFillRectangle( p_display, control, gc,
996                     0, 0, window.width, i_tb_height );
997     gcv.foreground = WhitePixel( p_display, 0 );
998     XChangeGC( p_display, gc, GCForeground, &gcv );
999
1000     /* position icons */
1001     dst_x = BTN_SPACE;
1002     dst_y = i_tb_height >> 1; /* baseline = vertical middle */
1003
1004     if( p_btnPause && (is_playing == 1) )
1005     {
1006         XPutImage( p_display, control, gc, p_btnPause, 0, 0, dst_x,
1007                    dst_y - (p_btnPause->height >> 1),
1008                    p_btnPause->width, p_btnPause->height );
1009         dst_x += BTN_SPACE + p_btnPause->width;
1010     }
1011     else if( p_btnPlay )
1012     {
1013         XPutImage( p_display, control, gc, p_btnPlay, 0, 0, dst_x,
1014                    dst_y - (p_btnPlay->height >> 1),
1015                    p_btnPlay->width, p_btnPlay->height );
1016         dst_x += BTN_SPACE + p_btnPlay->width;
1017     }
1018
1019     if( p_btnStop )
1020         XPutImage( p_display, control, gc, p_btnStop, 0, 0, dst_x,
1021                    dst_y - (p_btnStop->height >> 1),
1022                    p_btnStop->width, p_btnStop->height );
1023
1024     dst_x += BTN_SPACE + ( p_btnStop ? p_btnStop->width : 0 );
1025
1026     if( p_btnFullscreen )
1027         XPutImage( p_display, control, gc, p_btnFullscreen, 0, 0, dst_x,
1028                    dst_y - (p_btnFullscreen->height >> 1),
1029                    p_btnFullscreen->width, p_btnFullscreen->height );
1030
1031     dst_x += BTN_SPACE + ( p_btnFullscreen ? p_btnFullscreen->width : 0 );
1032
1033     if( p_btnUnmute && b_mute )
1034     {
1035         XPutImage( p_display, control, gc, p_btnUnmute, 0, 0, dst_x,
1036                    dst_y - (p_btnUnmute->height >> 1),
1037                    p_btnUnmute->width, p_btnUnmute->height );
1038
1039         dst_x += BTN_SPACE + ( p_btnUnmute ? p_btnUnmute->width : 0 );
1040     }
1041     else if( p_btnMute )
1042     {
1043         XPutImage( p_display, control, gc, p_btnMute, 0, 0, dst_x,
1044                    dst_y - (p_btnMute->height >> 1),
1045                    p_btnMute->width, p_btnMute->height );
1046
1047         dst_x += BTN_SPACE + ( p_btnMute ? p_btnMute->width : 0 );
1048     }
1049
1050     if( p_timeline )
1051         XPutImage( p_display, control, gc, p_timeline, 0, 0, dst_x,
1052                    dst_y - (p_timeline->height >> 1),
1053                    (window.width-(dst_x+BTN_SPACE)), p_timeline->height );
1054
1055     /* get movie position in % */
1056     if( playlist_isplaying() )
1057     {
1058         i_last_position = (int)((window.width-(dst_x+BTN_SPACE))*
1059                    libvlc_media_player_get_position(libvlc_media_player));
1060     }
1061
1062     if( p_btnTime )
1063         XPutImage( p_display, control, gc, p_btnTime,
1064                    0, 0, (dst_x+i_last_position),
1065                    dst_y - (p_btnTime->height >> 1),
1066                    p_btnTime->width, p_btnTime->height );
1067
1068     XFreeGC( p_display, gc );
1069 }
1070
1071 vlc_toolbar_clicked_t VlcPlugin::getToolbarButtonClicked( int i_xpos, int i_ypos )
1072 {
1073     unsigned int i_dest = BTN_SPACE;
1074     int is_playing = 0;
1075     bool b_mute = false;
1076
1077 #ifndef NDEBUG
1078     fprintf( stderr, "ToolbarButtonClicked:: "
1079                      "trying to match (%d,%d) (%d,%d)\n",
1080              i_xpos, i_ypos, i_tb_height, i_tb_width );
1081 #endif
1082     if( i_ypos >= i_tb_width )
1083         return clicked_Unknown;
1084
1085     /* Note: the order of testing is dependend on the original
1086      * drawing positions of the icon buttons. Buttons are tested
1087      * left to right.
1088      */
1089
1090     /* get isplaying */
1091     is_playing = playlist_isplaying();
1092
1093     /* get mute info */
1094     if( libvlc_media_player )
1095         b_mute = libvlc_audio_get_mute( libvlc_media_player );
1096
1097     /* is Pause of Play button clicked */
1098     if( (is_playing != 1) &&
1099         (i_xpos >= (BTN_SPACE>>1)) &&
1100         (i_xpos <= i_dest + p_btnPlay->width + (BTN_SPACE>>1)) )
1101         return clicked_Play;
1102     else if( (i_xpos >= (BTN_SPACE>>1))  &&
1103              (i_xpos <= i_dest + p_btnPause->width) )
1104         return clicked_Pause;
1105
1106     /* is Stop button clicked */
1107     if( is_playing != 1 )
1108         i_dest += (p_btnPlay->width + (BTN_SPACE>>1));
1109     else
1110         i_dest += (p_btnPause->width + (BTN_SPACE>>1));
1111
1112     if( (i_xpos >= i_dest) &&
1113         (i_xpos <= i_dest + p_btnStop->width + (BTN_SPACE>>1)) )
1114         return clicked_Stop;
1115
1116     /* is Fullscreen button clicked */
1117     i_dest += (p_btnStop->width + (BTN_SPACE>>1));
1118     if( (i_xpos >= i_dest) &&
1119         (i_xpos <= i_dest + p_btnFullscreen->width + (BTN_SPACE>>1)) )
1120         return clicked_Fullscreen;
1121
1122     /* is Mute or Unmute button clicked */
1123     i_dest += (p_btnFullscreen->width + (BTN_SPACE>>1));
1124     if( !b_mute && (i_xpos >= i_dest) &&
1125         (i_xpos <= i_dest + p_btnMute->width + (BTN_SPACE>>1)) )
1126         return clicked_Mute;
1127     else if( (i_xpos >= i_dest) &&
1128              (i_xpos <= i_dest + p_btnUnmute->width + (BTN_SPACE>>1)) )
1129         return clicked_Unmute;
1130
1131     /* is timeline clicked */
1132     if( !b_mute )
1133         i_dest += (p_btnMute->width + (BTN_SPACE>>1));
1134     else
1135         i_dest += (p_btnUnmute->width + (BTN_SPACE>>1));
1136     if( (i_xpos >= i_dest) &&
1137         (i_xpos <= i_dest + p_timeline->width + (BTN_SPACE>>1)) )
1138         return clicked_timeline;
1139
1140     /* is time button clicked */
1141     i_dest += (p_timeline->width + (BTN_SPACE>>1));
1142     if( (i_xpos >= i_dest) &&
1143         (i_xpos <= i_dest + p_btnTime->width + (BTN_SPACE>>1)) )
1144         return clicked_Time;
1145
1146     return clicked_Unknown;
1147 }
1148 #undef BTN_SPACE
1149 #endif
1150
1151 // Verifies the version of the NPAPI.
1152 // The eventListeners use a NPAPI function available
1153 // since Gecko 1.9.
1154 bool VlcPlugin::canUseEventListener()
1155 {
1156     int plugin_major, plugin_minor;
1157     int browser_major, browser_minor;
1158
1159     NPN_Version(&plugin_major, &plugin_minor,
1160                 &browser_major, &browser_minor);
1161
1162     if (browser_minor >= 19 || browser_major > 0)
1163         return true;
1164     return false;
1165 }