vlcplugin_base.cpp 28 KB
Newer Older
1
/*****************************************************************************
2
 * vlcplugin_base.cpp: a VLC plugin for Mozilla
3
 *****************************************************************************
Jean-Paul Saman's avatar
Jean-Paul Saman committed
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
 *          Cheng Sun <chengsun9@gmail.com>
11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 26 27 28 29
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
30 31 32 33
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

34
#include "vlcplugin.h"
Jean-Paul Saman's avatar
Jean-Paul Saman committed
35

36
#include "control/npolibvlc.h"
37

38
#include <cctype>
39

40
#if defined(XP_UNIX)
41 42 43 44
#   include <pthread.h>
#elif defined(XP_WIN)
    /* windows headers */
#   include <winbase.h>
45 46 47 48
#else
#warning "locking not implemented for this platform"
#endif

49 50 51 52
#include <cstdio>
#include <cassert>
#include <cstdlib>
#include <cstring>
53

54
/*****************************************************************************
Cheng Sun's avatar
Cheng Sun committed
55
 * utility functions
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
 *****************************************************************************/
static void plugin_lock_init(plugin_lock_t *lock)
{
    assert(lock);

#if defined(XP_UNIX)
    pthread_mutex_init(&lock->mutex, NULL);
#elif defined(XP_WIN)
    InitializeCriticalSection(&lock->cs);
#else
#warning "locking not implemented in this platform"
#endif
}

static void plugin_lock_destroy(plugin_lock_t *lock)
{
    assert(lock);

#if defined(XP_UNIX)
    pthread_mutex_destroy(&lock->mutex);
#elif defined(XP_WIN)
    DeleteCriticalSection(&lock->cs);
#else
#warning "locking not implemented in this platform"
#endif
}

static void plugin_lock(plugin_lock_t *lock)
{
    assert(lock);

#if defined(XP_UNIX)
    pthread_mutex_lock(&lock->mutex);
#elif defined(XP_WIN)
    EnterCriticalSection(&lock->cs);
#else
#warning "locking not implemented in this platform"
#endif
}

static void plugin_unlock(plugin_lock_t *lock)
{
    assert(lock);

#if defined(XP_UNIX)
    pthread_mutex_unlock(&lock->mutex);
#elif defined(XP_WIN)
    LeaveCriticalSection(&lock->cs);
#else
#warning "locking not implemented in this platform"
#endif
}

109
/*****************************************************************************
Jean-Paul Saman's avatar
Jean-Paul Saman committed
110
 * Event Object
111
 *****************************************************************************/
Jean-Paul Saman's avatar
Jean-Paul Saman committed
112 113 114 115 116 117 118
static void handle_input_event(const libvlc_event_t* event, void *param);
static void handle_changed_event(const libvlc_event_t* event, void *param);

static vlcplugin_event_t vlcevents[] = {
    { "MediaPlayerMediaChanged", libvlc_MediaPlayerMediaChanged, handle_input_event },
    { "MediaPlayerNothingSpecial", libvlc_MediaPlayerNothingSpecial, handle_input_event },
    { "MediaPlayerOpening", libvlc_MediaPlayerOpening, handle_input_event },
119
    { "MediaPlayerBuffering", libvlc_MediaPlayerBuffering, handle_changed_event },
Jean-Paul Saman's avatar
Jean-Paul Saman committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    { "MediaPlayerPlaying", libvlc_MediaPlayerPlaying, handle_input_event },
    { "MediaPlayerPaused", libvlc_MediaPlayerPaused, handle_input_event },
    { "MediaPlayerStopped", libvlc_MediaPlayerStopped, handle_input_event },
    { "MediaPlayerForward", libvlc_MediaPlayerForward, handle_input_event },
    { "MediaPlayerBackward", libvlc_MediaPlayerBackward, handle_input_event },
    { "MediaPlayerEndReached", libvlc_MediaPlayerEndReached, handle_input_event },
    { "MediaPlayerEncounteredError", libvlc_MediaPlayerEncounteredError, handle_input_event },
    { "MediaPlayerTimeChanged", libvlc_MediaPlayerTimeChanged, handle_changed_event },
    { "MediaPlayerPositionChanged", libvlc_MediaPlayerPositionChanged, handle_changed_event },
    { "MediaPlayerSeekableChanged", libvlc_MediaPlayerSeekableChanged, handle_changed_event },
    { "MediaPlayerPausableChanged", libvlc_MediaPlayerPausableChanged, handle_changed_event },
    { "MediaPlayerTitleChanged", libvlc_MediaPlayerTitleChanged, handle_changed_event },
    { "MediaPlayerLengthChanged", libvlc_MediaPlayerLengthChanged, handle_changed_event },
};

static void handle_input_event(const libvlc_event_t* event, void *param)
136
{
137
    VlcPluginBase *plugin = (VlcPluginBase*)param;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
138 139 140 141 142 143 144 145 146 147 148
    switch( event->type )
    {
        case libvlc_MediaPlayerNothingSpecial:
        case libvlc_MediaPlayerOpening:
        case libvlc_MediaPlayerPlaying:
        case libvlc_MediaPlayerPaused:
        case libvlc_MediaPlayerStopped:
        case libvlc_MediaPlayerForward:
        case libvlc_MediaPlayerBackward:
        case libvlc_MediaPlayerEndReached:
        case libvlc_MediaPlayerEncounteredError:
149
            plugin->event_callback(event, NULL, 0);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
150 151 152 153
            break;
        default: /* ignore all other libvlc_event_type_t */
            break;
    }
154 155
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
156 157 158 159 160
static void handle_changed_event(const libvlc_event_t* event, void *param)
{
    uint32_t   npcount = 1;
    NPVariant *npparam = (NPVariant *) NPN_MemAlloc( sizeof(NPVariant) * npcount );

161
    VlcPluginBase *plugin = (VlcPluginBase*)param;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
162 163
    switch( event->type )
    {
164
#ifdef LIBVLC120
165 166 167
        case libvlc_MediaPlayerBuffering:
            DOUBLE_TO_NPVARIANT(event->u.media_player_buffering.new_cache, npparam[0]);
            break;
168
#endif
Jean-Paul Saman's avatar
Jean-Paul Saman committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
        case libvlc_MediaPlayerTimeChanged:
            DOUBLE_TO_NPVARIANT(event->u.media_player_time_changed.new_time, npparam[0]);
            break;
        case libvlc_MediaPlayerPositionChanged:
            DOUBLE_TO_NPVARIANT(event->u.media_player_position_changed.new_position, npparam[0]);
            break;
        case libvlc_MediaPlayerSeekableChanged:
            BOOLEAN_TO_NPVARIANT(event->u.media_player_seekable_changed.new_seekable, npparam[0]);
            break;
        case libvlc_MediaPlayerPausableChanged:
            BOOLEAN_TO_NPVARIANT(event->u.media_player_pausable_changed.new_pausable, npparam[0]);
            break;
        case libvlc_MediaPlayerTitleChanged:
            BOOLEAN_TO_NPVARIANT(event->u.media_player_title_changed.new_title, npparam[0]);
            break;
        case libvlc_MediaPlayerLengthChanged:
            DOUBLE_TO_NPVARIANT(event->u.media_player_length_changed.new_length, npparam[0]);
            break;
        default: /* ignore all other libvlc_event_type_t */
            NPN_MemFree( npparam );
            return;
    }
191
    plugin->event_callback(event, npparam, npcount);
192
}
193

194 195
bool EventObj::init()
{
196 197
    plugin_lock_init(&lock);
    return true;
198 199 200 201
}

EventObj::~EventObj()
{
202
    plugin_lock_destroy(&lock);
203
}
204 205

void EventObj::deliver(NPP browser)
206
{
207 208 209
    if(_already_in_deliver)
        return;

210
    plugin_lock(&lock);
211
    _already_in_deliver = true;
212

Jean-Paul Saman's avatar
Jean-Paul Saman committed
213
    for( ev_l::iterator iter = _elist.begin(); iter != _elist.end(); ++iter )
214
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
215
        for( lr_l::iterator j = _llist.begin(); j != _llist.end(); ++j )
216
        {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
217
            if( j->event_type() == iter->event_type() )
218
            {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
                NPVariant result;
                NPVariant *params = iter->params();
                uint32_t   count  = iter->count();

                NPObject *listener = j->listener();
                assert( listener );

                NPN_InvokeDefault( browser, listener, params, count, &result );
                NPN_ReleaseVariantValue( &result );

                for( uint32_t n = 0; n < count; n++ )
                {
                    if( NPVARIANT_IS_STRING(params[n]) )
                        NPN_MemFree( (void*) NPVARIANT_TO_STRING(params[n]).UTF8Characters );
                    else if( NPVARIANT_IS_OBJECT(params[n]) )
                    {
                        NPN_ReleaseObject( NPVARIANT_TO_OBJECT(params[n]) );
                        NPN_MemFree( (void*)NPVARIANT_TO_OBJECT(params[n]) );
                    }
                }
239
                if (params) NPN_MemFree( params );
240 241 242
            }
        }
    }
243
    _elist.clear();
244

245
    _already_in_deliver = false;
246
    plugin_unlock(&lock);
247 248
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
249 250
void EventObj::callback(const libvlc_event_t* event,
                        NPVariant *npparams, uint32_t count)
251
{
252
    plugin_lock(&lock);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
253
    _elist.push_back(VLCEvent(event->type, npparams, count));
254
    plugin_unlock(&lock);
255
}
256

Jean-Paul Saman's avatar
Jean-Paul Saman committed
257
vlcplugin_event_t *EventObj::find_event(const char *s) const
258
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
259 260 261 262 263 264
    for( int i = 0; i < ARRAY_SIZE(vlcevents); i++ )
    {
        if( strncmp(vlcevents[i].name, s, strlen(vlcevents[i].name)) == 0 )
            return &vlcevents[i];
    }
    return NULL;
265 266
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
267
const char *EventObj::find_name(const libvlc_event_t *event)
268
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
269 270 271 272 273 274
    for( int i = 0; i < ARRAY_SIZE(vlcevents); i++ )
    {
        if( vlcevents[i].libvlc_type == event->type )
            return vlcevents[i].name;
    }
    return NULL;
275 276
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
277
bool EventObj::insert(const NPString &name, NPObject *listener, bool bubble)
278
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
279 280
    vlcplugin_event_t *event = find_event(name.UTF8Characters);
    if( !event )
281 282
        return false;

283 284 285 286 287
    for( lr_l::iterator iter = _llist.begin(); iter != _llist.end(); ++iter )
    {
        if( iter->listener() == listener &&
            event->libvlc_type == iter->event_type() &&
            iter->bubble() == bubble )
288 289 290
        {
            return false;
        }
291 292
    }

293
    _llist.push_back( Listener(event, listener, bubble) );
294
    return true;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
295
}
296

Jean-Paul Saman's avatar
Jean-Paul Saman committed
297
bool EventObj::remove(const NPString &name, NPObject *listener, bool bubble)
298
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
299 300
    vlcplugin_event_t *event = find_event(name.UTF8Characters);
    if( !event )
301 302
        return false;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
303
    for( lr_l::iterator iter = _llist.begin(); iter !=_llist.end(); iter++ )
304
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
305 306 307
        if( iter->event_type() == event->libvlc_type &&
            iter->listener() == listener &&
            iter->bubble() == bubble )
308
        {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
309 310
            iter = _llist.erase(iter);
            return true;
311 312
        }
    }
313

Jean-Paul Saman's avatar
Jean-Paul Saman committed
314
    return false;
315 316
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
317
void EventObj::hook_manager( libvlc_event_manager_t *em, void *userdata )
318
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
319 320 321 322 323 324 325 326 327 328 329 330
    _em = em;

    if( _em )
    {
        /* attach all libvlc events we care about */
        for( int i = 0; i < ARRAY_SIZE(vlcevents); i++ )
        {
            libvlc_event_attach( _em, vlcevents[i].libvlc_type,
                                      vlcevents[i].libvlc_callback,
                                      userdata );
        }
    }
331 332
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
333
void EventObj::unhook_manager( void *userdata )
334
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
335 336 337 338 339 340 341 342 343 344
    if( _em )
    {
		/* detach all libvlc events we cared about */
        for( int i = 0; i < ARRAY_SIZE(vlcevents); i++ )
        {
            libvlc_event_detach( _em, vlcevents[i].libvlc_type,
                                      vlcevents[i].libvlc_callback,
                                      userdata );
        }
    }
345 346
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
347
/*****************************************************************************
348
 * VlcPluginBase constructor and destructor
Jean-Paul Saman's avatar
Jean-Paul Saman committed
349
 *****************************************************************************/
350
VlcPluginBase::VlcPluginBase( NPP instance, NPuint16_t mode ) :
Jean-Paul Saman's avatar
Jean-Paul Saman committed
351 352 353 354 355 356 357 358 359 360
    i_npmode(mode),
    b_stream(0),
    psz_target(NULL),
    playlist_index(-1),
    libvlc_instance(NULL),
    libvlc_media_list(NULL),
    libvlc_media_player(NULL),
    p_scriptClass(NULL),
    p_browser(instance),
    psz_baseURL(NULL)
361
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
362
    memset(&npwindow, 0, sizeof(NPWindow));
363
    _instances.insert(this);
364 365
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
366 367 368 369 370
static bool boolValue(const char *value) {
    return ( !strcmp(value, "1") ||
             !strcasecmp(value, "true") ||
             !strcasecmp(value, "yes") );
}
371

372 373
std::set<VlcPluginBase*> VlcPluginBase::_instances;

374
void VlcPluginBase::eventAsync(void *param)
375
{
376
    VlcPluginBase *plugin = (VlcPluginBase*)param;
377
    if( _instances.find(plugin) == _instances.end() )
378 379
        return;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
380
    plugin->events.deliver(plugin->getBrowser());
381
    plugin->update_controls();
382 383
}

384 385
void VlcPluginBase::event_callback(const libvlc_event_t* event,
                NPVariant *npparams, uint32_t npcount)
Jean-Paul Saman's avatar
Jean-Paul Saman committed
386
{
387
#if defined(XP_UNIX) || defined(XP_WIN)
388 389
    events.callback(event, npparams, npcount);
    NPN_PluginThreadAsyncCall(getBrowser(), eventAsync, this);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
390
#else
Cheng Sun's avatar
Cheng Sun committed
391
#   warning NPN_PluginThreadAsyncCall not implemented yet.
Jean-Paul Saman's avatar
Jean-Paul Saman committed
392 393 394
    printf("No NPN_PluginThreadAsyncCall(), doing nothing.\n");
#endif
}
395

396
NPError VlcPluginBase::init(int argc, char* const argn[], char* const argv[])
397
{
398
    /* prepare VLC command line */
399
    const char *ppsz_argv[32];
400
    int ppsz_argc = 0;
401

402 403 404 405
#ifndef NDEBUG
    ppsz_argv[ppsz_argc++] = "--no-plugins-cache";
#endif

406 407
    /* locate VLC module path */
#ifdef XP_MACOSX
408
    ppsz_argv[ppsz_argc++] = "--plugin-path=/Library/Internet\\ Plug-Ins/VLC\\ Plugin.plugin/Contents/MacOS/plugins";
409
    ppsz_argv[ppsz_argc++] = "--vout=minimal_macosx";
410 411 412 413 414 415
#elif defined(XP_WIN)
    HKEY h_key;
    DWORD i_type, i_data = MAX_PATH + 1;
    char p_data[MAX_PATH + 1];
    if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
                      0, KEY_READ, &h_key ) == ERROR_SUCCESS )
416
    {
417 418 419 420 421
         if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
                              (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
         {
             if( i_type == REG_SZ )
             {
422
                 strcat( p_data, "\\plugins" );
423 424
                 ppsz_argv[ppsz_argc++] = "--plugin-path";
                 ppsz_argv[ppsz_argc++] = p_data;
425 426 427
             }
         }
         RegCloseKey( h_key );
428
    }
429
    ppsz_argv[ppsz_argc++] = "--no-one-instance";
430

431
#endif /* XP_MACOSX */
432

433 434 435 436
    /* common settings */
    ppsz_argv[ppsz_argc++] = "-vv";
    ppsz_argv[ppsz_argc++] = "--no-stats";
    ppsz_argv[ppsz_argc++] = "--no-media-library";
437
    ppsz_argv[ppsz_argc++] = "--intf=dummy";
438
    ppsz_argv[ppsz_argc++] = "--no-video-title-show";
Christophe Mutricy's avatar
Christophe Mutricy committed
439
    ppsz_argv[ppsz_argc++] = "--no-xlib";
440

441
    /* parse plugin arguments */
442
    for( int i = 0; (i < argc) && (ppsz_argc < 32); i++ )
443
    {
444
       /* fprintf(stderr, "argn=%s, argv=%s\n", argn[i], argv[i]); */
445 446 447 448 449 450 451 452

        if( !strcmp( argn[i], "target" )
         || !strcmp( argn[i], "mrl")
         || !strcmp( argn[i], "filename")
         || !strcmp( argn[i], "src") )
        {
            psz_target = argv[i];
        }
453 454
        else if( !strcmp( argn[i], "text" ) )
        {
455
            set_bg_text( argv[i] );
456
        }
457 458 459
        else if( !strcmp( argn[i], "autoplay")
              || !strcmp( argn[i], "autostart") )
        {
460
            set_autoplay(boolValue(argv[i]));
461
        }
462 463
        else if( !strcmp( argn[i], "fullscreen" )
              || !strcmp( argn[i], "allowfullscreen" ) )
464
        {
465
            set_enable_fs( boolValue(argv[i]) );
466 467 468 469 470
        }
        else if( !strcmp( argn[i], "mute" ) )
        {
            if( boolValue(argv[i]) )
            {
471
                ppsz_argv[ppsz_argc++] = "--volume=0";
472 473 474 475 476 477 478 479 480
            }
        }
        else if( !strcmp( argn[i], "loop")
              || !strcmp( argn[i], "autoloop") )
        {
            if( boolValue(argv[i]) )
            {
                ppsz_argv[ppsz_argc++] = "--loop";
            }
481 482
            else
            {
483 484 485
                ppsz_argv[ppsz_argc++] = "--no-loop";
            }
        }
486
        else if( !strcmp( argn[i], "toolbar" ) )
487
        {
488
            set_show_toolbar( boolValue(argv[i]) );
489
        }
490 491
        else if( !strcmp( argn[i], "bgcolor" ) )
        {
492
            set_bg_color( argv[i] );
493
        }
494 495
    }

496
    libvlc_instance = libvlc_new(ppsz_argc, ppsz_argv);
497 498
    if( !libvlc_instance )
        return NPERR_GENERIC_ERROR;
499
    libvlc_media_list = libvlc_media_list_new(libvlc_instance);
500 501 502 503 504 505

    /*
    ** fetch plugin base URL, which is the URL of the page containing the plugin
    ** this URL is used for making absolute URL from relative URL that may be
    ** passed as an MRL argument
    */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
506
    NPObject *plugin = NULL;
507 508 509 510 511 512 513 514 515 516

    if( NPERR_NO_ERROR == NPN_GetValue(p_browser, NPNVWindowNPObject, &plugin) )
    {
        /*
        ** is there a better way to get that info ?
        */
        static const char docLocHref[] = "document.location.href";
        NPString script;
        NPVariant result;

517 518
        script.UTF8Characters = docLocHref;
        script.UTF8Length = sizeof(docLocHref)-1;
519 520 521 522 523 524 525

        if( NPN_Evaluate(p_browser, plugin, &script, &result) )
        {
            if( NPVARIANT_IS_STRING(result) )
            {
                NPString &location = NPVARIANT_TO_STRING(result);

526
                psz_baseURL = (char *) malloc(location.UTF8Length+1);
527 528
                if( psz_baseURL )
                {
529 530
                    strncpy(psz_baseURL, location.UTF8Characters, location.UTF8Length);
                    psz_baseURL[location.UTF8Length] = '\0';
531 532 533 534 535 536 537 538 539 540
                }
            }
            NPN_ReleaseVariantValue(&result);
        }
        NPN_ReleaseObject(plugin);
    }

    if( psz_target )
    {
        // get absolute URL from src
541 542
        char *psz_absurl = getAbsoluteURL(psz_target);
        psz_target = psz_absurl ? psz_absurl : strdup(psz_target);
543 544 545
    }

    /* assign plugin script root class */
546 547
    /* new APIs */
    p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
548

549
    if( !events.init() )
550 551
        return NPERR_GENERIC_ERROR;

552
    return NPERR_NO_ERROR;
553 554
}

555
VlcPluginBase::~VlcPluginBase()
556
{
557 558
    free(psz_baseURL);
    free(psz_target);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
559

560
    if( libvlc_media_player )
561
    {
562 563
        if( playlist_isplaying() )
            playlist_stop();
Jean-Paul Saman's avatar
Jean-Paul Saman committed
564
        events.unhook_manager( this );
565
        libvlc_media_player_release( libvlc_media_player );
566
    }
567 568
    if( libvlc_media_list )
        libvlc_media_list_release( libvlc_media_list );
569
    if( libvlc_instance )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
570
        libvlc_release( libvlc_instance );
571 572

    _instances.erase(this);
573 574
}

575
void VlcPluginBase::setWindow(const NPWindow &window)
576 577
{
    npwindow = window;
578
}
579

580
/*****************************************************************************
581
 * VlcPluginBase playlist replacement methods
582
 *****************************************************************************/
583
int VlcPluginBase::playlist_add( const char *mrl )
584 585
{
    int item = -1;
586
    libvlc_media_t *p_m = libvlc_media_new_location(libvlc_instance,mrl);
587
    if( !p_m )
588
        return -1;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
589
    assert( libvlc_media_list );
590
    libvlc_media_list_lock(libvlc_media_list);
591
    if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
592
        item = libvlc_media_list_count(libvlc_media_list)-1;
593 594 595 596 597 598 599
    libvlc_media_list_unlock(libvlc_media_list);

    libvlc_media_release(p_m);

    return item;
}

600
int VlcPluginBase::playlist_add_extended_untrusted( const char *mrl, const char *name,
601
                    int optc, const char **optv )
602
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
603
    libvlc_media_t *p_m;
604
    int item = -1;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
605 606 607

    assert( libvlc_media_list );

608
    p_m = libvlc_media_new_location(libvlc_instance, mrl);
609
    if( !p_m )
610 611 612
        return -1;

    for( int i = 0; i < optc; ++i )
613
        libvlc_media_add_option_flag(p_m, optv[i], libvlc_media_option_unique);
614 615

    libvlc_media_list_lock(libvlc_media_list);
616
    if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
617
        item = libvlc_media_list_count(libvlc_media_list)-1;
618 619 620 621 622 623
    libvlc_media_list_unlock(libvlc_media_list);
    libvlc_media_release(p_m);

    return item;
}

624
bool VlcPluginBase::playlist_select( int idx )
625 626 627
{
    libvlc_media_t *p_m = NULL;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
628 629
    assert( libvlc_media_list );

630 631
    libvlc_media_list_lock(libvlc_media_list);

632
    int count = libvlc_media_list_count(libvlc_media_list);
633 634 635 636 637
    if( idx<0||idx>=count )
        goto bad_unlock;

    playlist_index = idx;

638
    p_m = libvlc_media_list_item_at_index(libvlc_media_list,playlist_index);
639 640
    libvlc_media_list_unlock(libvlc_media_list);

641
    if( !p_m )
642 643 644 645
        return false;

    if( libvlc_media_player )
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
646 647
        if( playlist_isplaying() )
            playlist_stop();
Jean-Paul Saman's avatar
Jean-Paul Saman committed
648
        events.unhook_manager( this );
649
        on_media_player_release();
650 651 652 653
        libvlc_media_player_release( libvlc_media_player );
        libvlc_media_player = NULL;
    }

Jean-Paul Saman's avatar
Jean-Paul Saman committed
654
    libvlc_media_player = libvlc_media_player_new_from_media( p_m );
655
    if( libvlc_media_player )
656
    {
657
        on_media_player_new();
658
        set_player_window();
Jean-Paul Saman's avatar
Jean-Paul Saman committed
659 660 661 662

        libvlc_event_manager_t *p_em;
        p_em = libvlc_media_player_event_manager( libvlc_media_player );
        events.hook_manager( p_em, this );
663 664
    }

665
    libvlc_media_release( p_m );
666
    return true;
667 668

bad_unlock:
Jean-Paul Saman's avatar
Jean-Paul Saman committed
669
    libvlc_media_list_unlock( libvlc_media_list );
670 671 672
    return false;
}

673
int VlcPluginBase::playlist_delete_item( int idx )
674
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
675 676
    if( !libvlc_media_list )
        return -1;
677
    libvlc_media_list_lock(libvlc_media_list);
678
    int ret = libvlc_media_list_remove_index(libvlc_media_list,idx);
679
    libvlc_media_list_unlock(libvlc_media_list);
680
    return ret;
681 682
}

683
void VlcPluginBase::playlist_clear()
684 685 686
{
    if( libvlc_media_list )
        libvlc_media_list_release(libvlc_media_list);
687
    libvlc_media_list = libvlc_media_list_new(getVLC());
688 689
}

690
int VlcPluginBase::playlist_count()
691 692
{
    int items_count = 0;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
693 694
    if( !libvlc_media_list )
        return items_count;
695
    libvlc_media_list_lock(libvlc_media_list);
696
    items_count = libvlc_media_list_count(libvlc_media_list);
697 698 699 700 701
    libvlc_media_list_unlock(libvlc_media_list);
    return items_count;
}


702
bool  VlcPluginBase::player_has_vout()
703
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
704
    bool r = false;
705
    if( playlist_isplaying() )
706
        r = libvlc_media_player_has_vout(libvlc_media_player);
707 708 709
    return r;
}

710
/*****************************************************************************
711
 * VlcPluginBase methods
712
 *****************************************************************************/
713

714
char *VlcPluginBase::getAbsoluteURL(const char *url)
715
{
716
    if( NULL != url )
717
    {
718 719 720
        // check whether URL is already absolute
        const char *end=strchr(url, ':');
        if( (NULL != end) && (end != url) )
721
        {
722 723
            // validate protocol header
            const char *start = url;
724 725 726
            char c = *start;
            if( isalpha(c) )
            {
727
                ++start;
728 729 730 731 732 733 734 735 736 737 738 739 740 741
                while( start != end )
                {
                    c  = *start;
                    if( ! (isalnum(c)
                       || ('-' == c)
                       || ('+' == c)
                       || ('.' == c)
                       || ('/' == c)) ) /* VLC uses / to allow user to specify a demuxer */
                        // not valid protocol header, assume relative URL
                        goto relativeurl;
                    ++start;
                }
                /* we have a protocol header, therefore URL is absolute */
                return strdup(url);
742
            }
743
            // not a valid protocol header, assume relative URL
744
        }
745

746 747
relativeurl:

748 749 750
        if( psz_baseURL )
        {
            size_t baseLen = strlen(psz_baseURL);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
751
            char *href = (char *) malloc(baseLen+strlen(url)+1);
752 753 754
            if( href )
            {
                /* prepend base URL */
JP Dinger's avatar
JP Dinger committed
755
                memcpy(href, psz_baseURL, baseLen+1);
756 757 758 759 760 761 762 763 764 765 766 767 768 769

                /*
                ** relative url could be empty,
                ** in which case return base URL
                */
                if( '\0' == *url )
                    return href;

                /*
                ** locate pathname part of base URL
                */

                /* skip over protocol part  */
                char *pathstart = strchr(href, ':');
JP Dinger's avatar
JP Dinger committed
770
                char *pathend = href+baseLen;
771
                if( pathstart )
772 773 774
                {
                    if( '/' == *(++pathstart) )
                    {
775 776 777 778 779 780 781 782 783 784 785 786
                        if( '/' == *(++pathstart) )
                        {
                            ++pathstart;
                        }
                    }
                    /* skip over host part */
                    pathstart = strchr(pathstart, '/');
                    if( ! pathstart )
                    {
                        // no path, add a / past end of url (over '\0')
                        pathstart = pathend;
                        *pathstart = '/';
787 788
                    }
                }
789
                else
790
                {
791 792 793 794
                    /* baseURL is just a UNIX path */
                    if( '/' != *href )
                    {
                        /* baseURL is not an absolute path */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
795
                        free(href);
796 797 798
                        return NULL;
                    }
                    pathstart = href;
799 800 801 802 803 804 805 806 807 808
                }

                /* relative URL made of an absolute path ? */
                if( '/' == *url )
                {
                    /* replace path completely */
                    strcpy(pathstart, url);
                    return href;
                }

809
                /* find last path component and replace it */
810 811
                while( '/' != *pathend)
                    --pathend;
812 813 814 815 816 817 818 819 820 821 822 823

                /*
                ** if relative url path starts with one or more '../',
                ** factor them out of href so that we return a
                ** normalized URL
                */
                while( pathend != pathstart )
                {
                    const char *p = url;
                    if( '.' != *p )
                        break;
                    ++p;
824 825 826 827
                    if( '\0' == *p  )
                    {
                        /* relative url is just '.' */
                        url = p;
828
                        break;
829 830 831 832 833 834 835
                    }
                    if( '/' == *p  )
                    {
                        /* relative url starts with './' */
                        url = ++p;
                        continue;
                    }
836
                    if( '.' != *p )
837 838
                        break;
                    ++p;
839 840 841 842 843 844
                    if( '\0' == *p )
                    {
                        /* relative url is '..' */
                    }
                    else
                    {
845
                        if( '/' != *p )
846 847 848 849
                            break;
                        /* relative url starts with '../' */
                        ++p;
                    }
850
                    url = p;
851 852 853 854 855
                    do
                    {
                        --pathend;
                    }
                    while( '/' != *pathend );
856
                }
857 858
                /* skip over '/' separator */
                ++pathend;
859
                /* concatenate remaining base URL and relative URL */
860
                strcpy(pathend, url);
861 862 863
            }
            return href;
        }
864
    }
865 866
    return NULL;
}
867

868
void VlcPluginBase::control_handler(vlc_toolbar_clicked_t clicked)
869
{
870
    switch( clicked )
871
    {
872 873 874 875 876
        case clicked_Play:
        {
            playlist_play();
        }
        break;
877

878 879 880 881 882
        case clicked_Pause:
        {
            playlist_pause();
        }
        break;
883

884 885 886 887 888
        case clicked_Stop:
        {
            playlist_stop();
        }
        break;
889

890 891
        case clicked_Fullscreen:
        {
892
            toggle_fullscreen();
893 894
        }
        break;
895

896 897 898 899 900 901 902 903 904
        case clicked_Mute:
        case clicked_Unmute:
#if 0
        {
            if( p_md )
                libvlc_audio_toggle_mute( p_md );
        }
#endif
        break;
905

906 907 908 909 910 911 912 913
        case clicked_timeline:
#if 0
        {
            /* if a movie is loaded */
            if( p_md )
            {
                int64_t f_length;
                f_length = libvlc_media_player_get_length( p_md ) / 100;
914

915 916
                f_length = (float)f_length *
                        ( ((float)i_xPos-4.0 ) / ( ((float)i_width-8.0)/100) );
917

918 919 920
                libvlc_media_player_set_time( p_md, f_length );
            }
        }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
921
#endif
922
        break;
923

924 925 926 927 928
        case clicked_Time:
        {
            /* Not implemented yet*/
        }
        break;
929

930 931 932 933
        default: /* button_Unknown */
            fprintf(stderr, "button Unknown!\n");
        break;
    }
934
}
935 936 937 938

// Verifies the version of the NPAPI.
// The eventListeners use a NPAPI function available
// since Gecko 1.9.
939
bool VlcPluginBase::canUseEventListener()
940 941 942 943 944 945 946 947 948 949 950
{
    int plugin_major, plugin_minor;
    int browser_major, browser_minor;

    NPN_Version(&plugin_major, &plugin_minor,
                &browser_major, &browser_minor);

    if (browser_minor >= 19 || browser_major > 0)
        return true;
    return false;
}