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