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