]> git.sesse.net Git - vlc/blob - projects/mozilla/vlcplugin.cpp
Update the lua README.
[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     ppsz_argv[ppsz_argc++] = "--no-xlib";
365
366     const char *progid = NULL;
367
368     /* parse plugin arguments */
369     for( int i = 0; (i < argc) && (ppsz_argc < 32); i++ )
370     {
371        /* fprintf(stderr, "argn=%s, argv=%s\n", argn[i], argv[i]); */
372
373         if( !strcmp( argn[i], "target" )
374          || !strcmp( argn[i], "mrl")
375          || !strcmp( argn[i], "filename")
376          || !strcmp( argn[i], "src") )
377         {
378             psz_target = argv[i];
379         }
380         else if( !strcmp( argn[i], "text" ) )
381         {
382             free( psz_text );
383             psz_text = strdup( argv[i] );
384         }
385         else if( !strcmp( argn[i], "autoplay")
386               || !strcmp( argn[i], "autostart") )
387         {
388             b_autoplay = boolValue(argv[i]);
389         }
390         else if( !strcmp( argn[i], "fullscreen" ) )
391         {
392             if( boolValue(argv[i]) )
393             {
394                 ppsz_argv[ppsz_argc++] = "--fullscreen";
395             }
396             else
397             {
398                 ppsz_argv[ppsz_argc++] = "--no-fullscreen";
399             }
400         }
401         else if( !strcmp( argn[i], "mute" ) )
402         {
403             if( boolValue(argv[i]) )
404             {
405                 ppsz_argv[ppsz_argc++] = "--volume=0";
406             }
407         }
408         else if( !strcmp( argn[i], "loop")
409               || !strcmp( argn[i], "autoloop") )
410         {
411             if( boolValue(argv[i]) )
412             {
413                 ppsz_argv[ppsz_argc++] = "--loop";
414             }
415             else
416             {
417                 ppsz_argv[ppsz_argc++] = "--no-loop";
418             }
419         }
420         else if( !strcmp( argn[i], "version")
421               || !strcmp( argn[i], "progid") )
422         {
423             progid = argv[i];
424         }
425         else if( !strcmp( argn[i], "toolbar" ) )
426         {
427 /* FIXME: Remove this when toolbar functionality has been implemented on
428  * MacOS X and Win32 for Firefox/Mozilla/Safari. */
429 #ifdef XP_UNIX
430             b_toolbar = boolValue(argv[i]);
431 #endif
432         }
433     }
434
435     libvlc_instance = libvlc_new(ppsz_argc, ppsz_argv);
436     if( !libvlc_instance )
437         return NPERR_GENERIC_ERROR;
438     libvlc_media_list = libvlc_media_list_new(libvlc_instance);
439
440     /*
441     ** fetch plugin base URL, which is the URL of the page containing the plugin
442     ** this URL is used for making absolute URL from relative URL that may be
443     ** passed as an MRL argument
444     */
445     NPObject *plugin = NULL;
446
447     if( NPERR_NO_ERROR == NPN_GetValue(p_browser, NPNVWindowNPObject, &plugin) )
448     {
449         /*
450         ** is there a better way to get that info ?
451         */
452         static const char docLocHref[] = "document.location.href";
453         NPString script;
454         NPVariant result;
455
456         script.UTF8Characters = docLocHref;
457         script.UTF8Length = sizeof(docLocHref)-1;
458
459         if( NPN_Evaluate(p_browser, plugin, &script, &result) )
460         {
461             if( NPVARIANT_IS_STRING(result) )
462             {
463                 NPString &location = NPVARIANT_TO_STRING(result);
464
465                 psz_baseURL = (char *) malloc(location.UTF8Length+1);
466                 if( psz_baseURL )
467                 {
468                     strncpy(psz_baseURL, location.UTF8Characters, location.UTF8Length);
469                     psz_baseURL[location.UTF8Length] = '\0';
470                 }
471             }
472             NPN_ReleaseVariantValue(&result);
473         }
474         NPN_ReleaseObject(plugin);
475     }
476
477     if( psz_target )
478     {
479         // get absolute URL from src
480         char *psz_absurl = getAbsoluteURL(psz_target);
481         psz_target = psz_absurl ? psz_absurl : strdup(psz_target);
482     }
483
484     /* assign plugin script root class */
485     /* new APIs */
486     p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
487
488     if( !events.init() )
489         return NPERR_GENERIC_ERROR;
490
491     return NPERR_NO_ERROR;
492 }
493
494 VlcPlugin::~VlcPlugin()
495 {
496     free(psz_baseURL);
497     free(psz_target);
498     free(psz_text);
499
500     if( libvlc_media_player )
501     {
502         if( playlist_isplaying() )
503             playlist_stop();
504         events.unhook_manager();
505         libvlc_media_player_release( libvlc_media_player );
506     }
507     if( libvlc_media_list )
508         libvlc_media_list_release( libvlc_media_list );
509     if( libvlc_instance )
510         libvlc_release(libvlc_instance);
511 }
512
513 /*****************************************************************************
514  * VlcPlugin playlist replacement methods
515  *****************************************************************************/
516 void VlcPlugin::set_player_window()
517 {
518 #ifdef XP_UNIX
519     libvlc_media_player_set_xwindow(libvlc_media_player,
520                                     (uint32_t)getVideoWindow());
521 #endif
522 #ifdef XP_MACOSX
523     // XXX FIXME insert appropriate call here
524 #endif
525 #ifdef XP_WIN
526     libvlc_media_player_set_hwnd(libvlc_media_player,
527                                  getWindow().window);
528 #endif
529 }
530
531 int VlcPlugin::playlist_add( const char *mrl )
532 {
533     int item = -1;
534     libvlc_media_t *p_m = libvlc_media_new_location(libvlc_instance,mrl);
535     if( !p_m )
536         return -1;
537     assert( libvlc_media_list );
538     libvlc_media_list_lock(libvlc_media_list);
539     if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
540         item = libvlc_media_list_count(libvlc_media_list)-1;
541     libvlc_media_list_unlock(libvlc_media_list);
542
543     libvlc_media_release(p_m);
544
545     return item;
546 }
547
548 int VlcPlugin::playlist_add_extended_untrusted( const char *mrl, const char *name,
549                     int optc, const char **optv )
550 {
551     libvlc_media_t *p_m;
552     int item = -1;
553
554     assert( libvlc_media_list );
555
556     p_m = libvlc_media_new_location(libvlc_instance, mrl);
557     if( !p_m )
558         return -1;
559
560     for( int i = 0; i < optc; ++i )
561         libvlc_media_add_option_flag(p_m, optv[i], libvlc_media_option_unique);
562
563     libvlc_media_list_lock(libvlc_media_list);
564     if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
565         item = libvlc_media_list_count(libvlc_media_list)-1;
566     libvlc_media_list_unlock(libvlc_media_list);
567     libvlc_media_release(p_m);
568
569     return item;
570 }
571
572 bool VlcPlugin::playlist_select( int idx )
573 {
574     libvlc_media_t *p_m = NULL;
575
576     assert( libvlc_media_list );
577
578     libvlc_media_list_lock(libvlc_media_list);
579
580     int count = libvlc_media_list_count(libvlc_media_list);
581     if( idx<0||idx>=count )
582         goto bad_unlock;
583
584     playlist_index = idx;
585
586     p_m = libvlc_media_list_item_at_index(libvlc_media_list,playlist_index);
587     libvlc_media_list_unlock(libvlc_media_list);
588
589     if( !p_m )
590         return false;
591
592     if( libvlc_media_player )
593     {
594         if( playlist_isplaying() )
595             playlist_stop();
596         events.unhook_manager();
597         libvlc_media_player_release( libvlc_media_player );
598         libvlc_media_player = NULL;
599     }
600
601     libvlc_media_player = libvlc_media_player_new_from_media(p_m);
602     if( libvlc_media_player )
603     {
604         set_player_window();
605         events.hook_manager(
606                       libvlc_media_player_event_manager(libvlc_media_player),
607                       event_callback, this);
608     }
609
610     libvlc_media_release( p_m );
611     return true;
612
613 bad_unlock:
614     libvlc_media_list_unlock(libvlc_media_list);
615     return false;
616 }
617
618 int VlcPlugin::playlist_delete_item( int idx )
619 {
620     if( !libvlc_media_list )
621         return -1;
622     libvlc_media_list_lock(libvlc_media_list);
623     int ret = libvlc_media_list_remove_index(libvlc_media_list,idx);
624     libvlc_media_list_unlock(libvlc_media_list);
625     return ret;
626 }
627
628 void VlcPlugin::playlist_clear()
629 {
630     if( libvlc_media_list )
631         libvlc_media_list_release(libvlc_media_list);
632     libvlc_media_list = libvlc_media_list_new(getVLC());
633 }
634
635 int VlcPlugin::playlist_count()
636 {
637     int items_count = 0;
638     if( !libvlc_media_list )
639         return items_count;
640     libvlc_media_list_lock(libvlc_media_list);
641     items_count = libvlc_media_list_count(libvlc_media_list);
642     libvlc_media_list_unlock(libvlc_media_list);
643     return items_count;
644 }
645
646 void VlcPlugin::toggle_fullscreen()
647 {
648     if( playlist_isplaying() )
649         libvlc_toggle_fullscreen(libvlc_media_player);
650 }
651
652 void VlcPlugin::set_fullscreen( int yes )
653 {
654     if( playlist_isplaying() )
655         libvlc_set_fullscreen(libvlc_media_player,yes);
656 }
657
658 int  VlcPlugin::get_fullscreen()
659 {
660     int r = 0;
661     if( playlist_isplaying() )
662         r = libvlc_get_fullscreen(libvlc_media_player);
663     return r;
664 }
665
666 bool  VlcPlugin::player_has_vout()
667 {
668     bool r = false;
669     if( playlist_isplaying() )
670         r = libvlc_media_player_has_vout(libvlc_media_player);
671     return r;
672 }
673
674 /*****************************************************************************
675  * VlcPlugin methods
676  *****************************************************************************/
677
678 char *VlcPlugin::getAbsoluteURL(const char *url)
679 {
680     if( NULL != url )
681     {
682         // check whether URL is already absolute
683         const char *end=strchr(url, ':');
684         if( (NULL != end) && (end != url) )
685         {
686             // validate protocol header
687             const char *start = url;
688             char c = *start;
689             if( isalpha(c) )
690             {
691                 ++start;
692                 while( start != end )
693                 {
694                     c  = *start;
695                     if( ! (isalnum(c)
696                        || ('-' == c)
697                        || ('+' == c)
698                        || ('.' == c)
699                        || ('/' == c)) ) /* VLC uses / to allow user to specify a demuxer */
700                         // not valid protocol header, assume relative URL
701                         goto relativeurl;
702                     ++start;
703                 }
704                 /* we have a protocol header, therefore URL is absolute */
705                 return strdup(url);
706             }
707             // not a valid protocol header, assume relative URL
708         }
709
710 relativeurl:
711
712         if( psz_baseURL )
713         {
714             size_t baseLen = strlen(psz_baseURL);
715             char *href = (char *) malloc(baseLen+strlen(url)+1);
716             if( href )
717             {
718                 /* prepend base URL */
719                 memcpy(href, psz_baseURL, baseLen+1);
720
721                 /*
722                 ** relative url could be empty,
723                 ** in which case return base URL
724                 */
725                 if( '\0' == *url )
726                     return href;
727
728                 /*
729                 ** locate pathname part of base URL
730                 */
731
732                 /* skip over protocol part  */
733                 char *pathstart = strchr(href, ':');
734                 char *pathend = href+baseLen;
735                 if( pathstart )
736                 {
737                     if( '/' == *(++pathstart) )
738                     {
739                         if( '/' == *(++pathstart) )
740                         {
741                             ++pathstart;
742                         }
743                     }
744                     /* skip over host part */
745                     pathstart = strchr(pathstart, '/');
746                     if( ! pathstart )
747                     {
748                         // no path, add a / past end of url (over '\0')
749                         pathstart = pathend;
750                         *pathstart = '/';
751                     }
752                 }
753                 else
754                 {
755                     /* baseURL is just a UNIX path */
756                     if( '/' != *href )
757                     {
758                         /* baseURL is not an absolute path */
759                         free(href);
760                         return NULL;
761                     }
762                     pathstart = href;
763                 }
764
765                 /* relative URL made of an absolute path ? */
766                 if( '/' == *url )
767                 {
768                     /* replace path completely */
769                     strcpy(pathstart, url);
770                     return href;
771                 }
772
773                 /* find last path component and replace it */
774                 while( '/' != *pathend)
775                     --pathend;
776
777                 /*
778                 ** if relative url path starts with one or more '../',
779                 ** factor them out of href so that we return a
780                 ** normalized URL
781                 */
782                 while( pathend != pathstart )
783                 {
784                     const char *p = url;
785                     if( '.' != *p )
786                         break;
787                     ++p;
788                     if( '\0' == *p  )
789                     {
790                         /* relative url is just '.' */
791                         url = p;
792                         break;
793                     }
794                     if( '/' == *p  )
795                     {
796                         /* relative url starts with './' */
797                         url = ++p;
798                         continue;
799                     }
800                     if( '.' != *p )
801                         break;
802                     ++p;
803                     if( '\0' == *p )
804                     {
805                         /* relative url is '..' */
806                     }
807                     else
808                     {
809                         if( '/' != *p )
810                             break;
811                         /* relative url starts with '../' */
812                         ++p;
813                     }
814                     url = p;
815                     do
816                     {
817                         --pathend;
818                     }
819                     while( '/' != *pathend );
820                 }
821                 /* skip over '/' separator */
822                 ++pathend;
823                 /* concatenate remaining base URL and relative URL */
824                 strcpy(pathend, url);
825             }
826             return href;
827         }
828     }
829     return NULL;
830 }
831
832 #if defined(XP_UNIX)
833 int  VlcPlugin::setSize(unsigned width, unsigned height)
834 {
835     int diff = (width != i_width) || (height != i_height);
836
837     i_width = width;
838     i_height = height;
839
840     /* return size */
841     return diff;
842 }
843
844 #define BTN_SPACE ((unsigned int)4)
845 void VlcPlugin::showToolbar()
846 {
847     const NPWindow& window = getWindow();
848     Window control = getControlWindow();
849     Window video = getVideoWindow();
850     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
851     unsigned int i_height = 0, i_width = BTN_SPACE;
852
853     /* load icons */
854     if( !p_btnPlay )
855         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/play.xpm",
856                             &p_btnPlay, NULL, NULL);
857     if( p_btnPlay )
858     {
859         i_height = __MAX( i_height, p_btnPlay->height );
860     }
861     if( !p_btnPause )
862         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/pause.xpm",
863                             &p_btnPause, NULL, NULL);
864     if( p_btnPause )
865     {
866         i_height = __MAX( i_height, p_btnPause->height );
867     }
868     i_width += __MAX( p_btnPause->width, p_btnPlay->width );
869
870     if( !p_btnStop )
871         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/stop.xpm",
872                             &p_btnStop, NULL, NULL );
873     if( p_btnStop )
874     {
875         i_height = __MAX( i_height, p_btnStop->height );
876         i_width += BTN_SPACE + p_btnStop->width;
877     }
878     if( !p_timeline )
879         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/time_line.xpm",
880                             &p_timeline, NULL, NULL);
881     if( p_timeline )
882     {
883         i_height = __MAX( i_height, p_timeline->height );
884         i_width += BTN_SPACE + p_timeline->width;
885     }
886     if( !p_btnTime )
887         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/time_icon.xpm",
888                             &p_btnTime, NULL, NULL);
889     if( p_btnTime )
890     {
891         i_height = __MAX( i_height, p_btnTime->height );
892         i_width += BTN_SPACE + p_btnTime->width;
893     }
894     if( !p_btnFullscreen )
895         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/fullscreen.xpm",
896                             &p_btnFullscreen, NULL, NULL);
897     if( p_btnFullscreen )
898     {
899         i_height = __MAX( i_height, p_btnFullscreen->height );
900         i_width += BTN_SPACE + p_btnFullscreen->width;
901     }
902     if( !p_btnMute )
903         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/volume_max.xpm",
904                             &p_btnMute, NULL, NULL);
905     if( p_btnMute )
906     {
907         i_height = __MAX( i_height, p_btnMute->height );
908     }
909     if( !p_btnUnmute )
910         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/volume_mute.xpm",
911                             &p_btnUnmute, NULL, NULL);
912     if( p_btnUnmute )
913     {
914         i_height = __MAX( i_height, p_btnUnmute->height );
915     }
916     i_width += BTN_SPACE + __MAX( p_btnUnmute->width, p_btnMute->width );
917
918     setToolbarSize( i_width, i_height );
919
920     if( !p_btnPlay || !p_btnPause || !p_btnStop || !p_timeline ||
921         !p_btnTime || !p_btnFullscreen || !p_btnMute || !p_btnUnmute )
922         fprintf(stderr, "Error: some button images not found in %s\n", DATA_PATH );
923
924     /* reset panels position and size */
925     /* XXX  use i_width */
926     XResizeWindow( p_display, video, window.width, window.height - i_height);
927     XMoveWindow( p_display, control, 0, window.height - i_height );
928     XResizeWindow( p_display, control, window.width, i_height -1);
929
930     b_toolbar = 1; /* says toolbar is now shown */
931     redrawToolbar();
932 }
933
934 void VlcPlugin::hideToolbar()
935 {
936     const NPWindow& window = getWindow();
937     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
938     Window control = getControlWindow();
939     Window video = getVideoWindow();
940
941     i_tb_width = i_tb_height = 0;
942
943     if( p_btnPlay )  XDestroyImage( p_btnPlay );
944     if( p_btnPause ) XDestroyImage( p_btnPause );
945     if( p_btnStop )  XDestroyImage( p_btnStop );
946     if( p_timeline ) XDestroyImage( p_timeline );
947     if( p_btnTime )  XDestroyImage( p_btnTime );
948     if( p_btnFullscreen ) XDestroyImage( p_btnFullscreen );
949     if( p_btnMute )  XDestroyImage( p_btnMute );
950     if( p_btnUnmute ) XDestroyImage( p_btnUnmute );
951
952     p_btnPlay = NULL;
953     p_btnPause = NULL;
954     p_btnStop = NULL;
955     p_timeline = NULL;
956     p_btnTime = NULL;
957     p_btnFullscreen = NULL;
958     p_btnMute = NULL;
959     p_btnUnmute = NULL;
960
961     /* reset panels position and size */
962     /* XXX  use i_width */
963     XResizeWindow( p_display, video, window.width, window.height );
964     XMoveWindow( p_display, control, 0, window.height-1 );
965     XResizeWindow( p_display, control, window.width, 1 );
966
967     b_toolbar = 0; /* says toolbar is now hidden */
968     redrawToolbar();
969 }
970
971 void VlcPlugin::redrawToolbar()
972 {
973     int is_playing = 0;
974     bool b_mute = false;
975     unsigned int dst_x, dst_y;
976     GC gc;
977     XGCValues gcv;
978     unsigned int i_tb_width, i_tb_height;
979
980     /* This method does nothing if toolbar is hidden. */
981     if( !b_toolbar || !libvlc_media_player )
982         return;
983
984     const NPWindow& window = getWindow();
985     Window control = getControlWindow();
986     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
987
988     getToolbarSize( &i_tb_width, &i_tb_height );
989
990     /* get mute info */
991     b_mute = libvlc_audio_get_mute( libvlc_media_player );
992
993     gcv.foreground = BlackPixel( p_display, 0 );
994     gc = XCreateGC( p_display, control, GCForeground, &gcv );
995
996     XFillRectangle( p_display, control, gc,
997                     0, 0, window.width, i_tb_height );
998     gcv.foreground = WhitePixel( p_display, 0 );
999     XChangeGC( p_display, gc, GCForeground, &gcv );
1000
1001     /* position icons */
1002     dst_x = BTN_SPACE;
1003     dst_y = i_tb_height >> 1; /* baseline = vertical middle */
1004
1005     if( p_btnPause && (is_playing == 1) )
1006     {
1007         XPutImage( p_display, control, gc, p_btnPause, 0, 0, dst_x,
1008                    dst_y - (p_btnPause->height >> 1),
1009                    p_btnPause->width, p_btnPause->height );
1010         dst_x += BTN_SPACE + p_btnPause->width;
1011     }
1012     else if( p_btnPlay )
1013     {
1014         XPutImage( p_display, control, gc, p_btnPlay, 0, 0, dst_x,
1015                    dst_y - (p_btnPlay->height >> 1),
1016                    p_btnPlay->width, p_btnPlay->height );
1017         dst_x += BTN_SPACE + p_btnPlay->width;
1018     }
1019
1020     if( p_btnStop )
1021         XPutImage( p_display, control, gc, p_btnStop, 0, 0, dst_x,
1022                    dst_y - (p_btnStop->height >> 1),
1023                    p_btnStop->width, p_btnStop->height );
1024
1025     dst_x += BTN_SPACE + ( p_btnStop ? p_btnStop->width : 0 );
1026
1027     if( p_btnFullscreen )
1028         XPutImage( p_display, control, gc, p_btnFullscreen, 0, 0, dst_x,
1029                    dst_y - (p_btnFullscreen->height >> 1),
1030                    p_btnFullscreen->width, p_btnFullscreen->height );
1031
1032     dst_x += BTN_SPACE + ( p_btnFullscreen ? p_btnFullscreen->width : 0 );
1033
1034     if( p_btnUnmute && b_mute )
1035     {
1036         XPutImage( p_display, control, gc, p_btnUnmute, 0, 0, dst_x,
1037                    dst_y - (p_btnUnmute->height >> 1),
1038                    p_btnUnmute->width, p_btnUnmute->height );
1039
1040         dst_x += BTN_SPACE + ( p_btnUnmute ? p_btnUnmute->width : 0 );
1041     }
1042     else if( p_btnMute )
1043     {
1044         XPutImage( p_display, control, gc, p_btnMute, 0, 0, dst_x,
1045                    dst_y - (p_btnMute->height >> 1),
1046                    p_btnMute->width, p_btnMute->height );
1047
1048         dst_x += BTN_SPACE + ( p_btnMute ? p_btnMute->width : 0 );
1049     }
1050
1051     if( p_timeline )
1052         XPutImage( p_display, control, gc, p_timeline, 0, 0, dst_x,
1053                    dst_y - (p_timeline->height >> 1),
1054                    (window.width-(dst_x+BTN_SPACE)), p_timeline->height );
1055
1056     /* get movie position in % */
1057     if( playlist_isplaying() )
1058     {
1059         i_last_position = (int)((window.width-(dst_x+BTN_SPACE))*
1060                    libvlc_media_player_get_position(libvlc_media_player));
1061     }
1062
1063     if( p_btnTime )
1064         XPutImage( p_display, control, gc, p_btnTime,
1065                    0, 0, (dst_x+i_last_position),
1066                    dst_y - (p_btnTime->height >> 1),
1067                    p_btnTime->width, p_btnTime->height );
1068
1069     XFreeGC( p_display, gc );
1070 }
1071
1072 vlc_toolbar_clicked_t VlcPlugin::getToolbarButtonClicked( int i_xpos, int i_ypos )
1073 {
1074     unsigned int i_dest = BTN_SPACE;
1075     int is_playing = 0;
1076     bool b_mute = false;
1077
1078 #ifndef NDEBUG
1079     fprintf( stderr, "ToolbarButtonClicked:: "
1080                      "trying to match (%d,%d) (%d,%d)\n",
1081              i_xpos, i_ypos, i_tb_height, i_tb_width );
1082 #endif
1083     if( i_ypos >= i_tb_width )
1084         return clicked_Unknown;
1085
1086     /* Note: the order of testing is dependend on the original
1087      * drawing positions of the icon buttons. Buttons are tested
1088      * left to right.
1089      */
1090
1091     /* get isplaying */
1092     is_playing = playlist_isplaying();
1093
1094     /* get mute info */
1095     if( libvlc_media_player )
1096         b_mute = libvlc_audio_get_mute( libvlc_media_player );
1097
1098     /* is Pause of Play button clicked */
1099     if( (is_playing != 1) &&
1100         (i_xpos >= (BTN_SPACE>>1)) &&
1101         (i_xpos <= i_dest + p_btnPlay->width + (BTN_SPACE>>1)) )
1102         return clicked_Play;
1103     else if( (i_xpos >= (BTN_SPACE>>1))  &&
1104              (i_xpos <= i_dest + p_btnPause->width) )
1105         return clicked_Pause;
1106
1107     /* is Stop button clicked */
1108     if( is_playing != 1 )
1109         i_dest += (p_btnPlay->width + (BTN_SPACE>>1));
1110     else
1111         i_dest += (p_btnPause->width + (BTN_SPACE>>1));
1112
1113     if( (i_xpos >= i_dest) &&
1114         (i_xpos <= i_dest + p_btnStop->width + (BTN_SPACE>>1)) )
1115         return clicked_Stop;
1116
1117     /* is Fullscreen button clicked */
1118     i_dest += (p_btnStop->width + (BTN_SPACE>>1));
1119     if( (i_xpos >= i_dest) &&
1120         (i_xpos <= i_dest + p_btnFullscreen->width + (BTN_SPACE>>1)) )
1121         return clicked_Fullscreen;
1122
1123     /* is Mute or Unmute button clicked */
1124     i_dest += (p_btnFullscreen->width + (BTN_SPACE>>1));
1125     if( !b_mute && (i_xpos >= i_dest) &&
1126         (i_xpos <= i_dest + p_btnMute->width + (BTN_SPACE>>1)) )
1127         return clicked_Mute;
1128     else if( (i_xpos >= i_dest) &&
1129              (i_xpos <= i_dest + p_btnUnmute->width + (BTN_SPACE>>1)) )
1130         return clicked_Unmute;
1131
1132     /* is timeline clicked */
1133     if( !b_mute )
1134         i_dest += (p_btnMute->width + (BTN_SPACE>>1));
1135     else
1136         i_dest += (p_btnUnmute->width + (BTN_SPACE>>1));
1137     if( (i_xpos >= i_dest) &&
1138         (i_xpos <= i_dest + p_timeline->width + (BTN_SPACE>>1)) )
1139         return clicked_timeline;
1140
1141     /* is time button clicked */
1142     i_dest += (p_timeline->width + (BTN_SPACE>>1));
1143     if( (i_xpos >= i_dest) &&
1144         (i_xpos <= i_dest + p_btnTime->width + (BTN_SPACE>>1)) )
1145         return clicked_Time;
1146
1147     return clicked_Unknown;
1148 }
1149 #undef BTN_SPACE
1150 #endif
1151
1152 // Verifies the version of the NPAPI.
1153 // The eventListeners use a NPAPI function available
1154 // since Gecko 1.9.
1155 bool VlcPlugin::canUseEventListener()
1156 {
1157     int plugin_major, plugin_minor;
1158     int browser_major, browser_minor;
1159
1160     NPN_Version(&plugin_major, &plugin_minor,
1161                 &browser_major, &browser_minor);
1162
1163     if (browser_minor >= 19 || browser_major > 0)
1164         return true;
1165     return false;
1166 }