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