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