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