vlcplugin_base.cpp 28.4 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 361 362 363
    i_npmode(mode),
    b_stream(0),
    b_autoplay(1),
    b_toolbar(0),
    psz_text(NULL),
    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)
364
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
365
    memset(&npwindow, 0, sizeof(NPWindow));
366
    _instances.insert(this);
367 368
}

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

375 376
std::set<VlcPluginBase*> VlcPluginBase::_instances;

377
void VlcPluginBase::eventAsync(void *param)
378
{
379
    VlcPluginBase *plugin = (VlcPluginBase*)param;
380 381 382
    if(_instances.find(plugin) == _instances.end())
        return;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
383
    plugin->events.deliver(plugin->getBrowser());
384
    plugin->update_controls();
385 386
}

387 388
void VlcPluginBase::event_callback(const libvlc_event_t* event,
                NPVariant *npparams, uint32_t npcount)
Jean-Paul Saman's avatar
Jean-Paul Saman committed
389
{
390
#if defined(XP_UNIX) || defined(XP_WIN)
391 392
    events.callback(event, npparams, npcount);
    NPN_PluginThreadAsyncCall(getBrowser(), eventAsync, this);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
393
#else
394 395 396
#ifdef _MSC_VER
#pragma message("NPN_PluginThreadAsyncCall not implemented yet.")
#else
Jean-Paul Saman's avatar
Jean-Paul Saman committed
397
#warning NPN_PluginThreadAsyncCall not implemented yet.
398
#endif //_MSC_VER
Jean-Paul Saman's avatar
Jean-Paul Saman committed
399 400 401
    printf("No NPN_PluginThreadAsyncCall(), doing nothing.\n");
#endif
}
402

403
NPError VlcPluginBase::init(int argc, char* const argn[], char* const argv[])
404
{
405
    /* prepare VLC command line */
406
    const char *ppsz_argv[32];
407
    int ppsz_argc = 0;
408

409 410 411 412
#ifndef NDEBUG
    ppsz_argv[ppsz_argc++] = "--no-plugins-cache";
#endif

413 414
    /* locate VLC module path */
#ifdef XP_MACOSX
415
    ppsz_argv[ppsz_argc++] = "--plugin-path=/Library/Internet\\ Plug-Ins/VLC\\ Plugin.plugin/Contents/MacOS/plugins";
416
    ppsz_argv[ppsz_argc++] = "--vout=minimal_macosx";
417 418 419 420 421 422
#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 )
423
    {
424 425 426 427 428
         if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
                              (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
         {
             if( i_type == REG_SZ )
             {
429
                 strcat( p_data, "\\plugins" );
430 431
                 ppsz_argv[ppsz_argc++] = "--plugin-path";
                 ppsz_argv[ppsz_argc++] = p_data;
432 433 434
             }
         }
         RegCloseKey( h_key );
435
    }
436
    ppsz_argv[ppsz_argc++] = "--no-one-instance";
437

438
#endif /* XP_MACOSX */
439

440 441 442 443
    /* common settings */
    ppsz_argv[ppsz_argc++] = "-vv";
    ppsz_argv[ppsz_argc++] = "--no-stats";
    ppsz_argv[ppsz_argc++] = "--no-media-library";
444
    ppsz_argv[ppsz_argc++] = "--intf=dummy";
445
    ppsz_argv[ppsz_argc++] = "--no-video-title-show";
Christophe Mutricy's avatar
Christophe Mutricy committed
446
    ppsz_argv[ppsz_argc++] = "--no-xlib";
447

448
    const char *progid = NULL;
449 450

    /* parse plugin arguments */
451
    for( int i = 0; (i < argc) && (ppsz_argc < 32); i++ )
452
    {
453
       /* fprintf(stderr, "argn=%s, argv=%s\n", argn[i], argv[i]); */
454 455 456 457 458 459 460 461

        if( !strcmp( argn[i], "target" )
         || !strcmp( argn[i], "mrl")
         || !strcmp( argn[i], "filename")
         || !strcmp( argn[i], "src") )
        {
            psz_target = argv[i];
        }
462 463
        else if( !strcmp( argn[i], "text" ) )
        {
464
            free( psz_text );
465 466
            psz_text = strdup( argv[i] );
        }
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
        else if( !strcmp( argn[i], "autoplay")
              || !strcmp( argn[i], "autostart") )
        {
            b_autoplay = boolValue(argv[i]);
        }
        else if( !strcmp( argn[i], "fullscreen" ) )
        {
            if( boolValue(argv[i]) )
            {
                ppsz_argv[ppsz_argc++] = "--fullscreen";
            }
            else
            {
                ppsz_argv[ppsz_argc++] = "--no-fullscreen";
            }
        }
        else if( !strcmp( argn[i], "mute" ) )
        {
            if( boolValue(argv[i]) )
            {
487
                ppsz_argv[ppsz_argc++] = "--volume=0";
488 489 490 491 492 493 494 495 496
            }
        }
        else if( !strcmp( argn[i], "loop")
              || !strcmp( argn[i], "autoloop") )
        {
            if( boolValue(argv[i]) )
            {
                ppsz_argv[ppsz_argc++] = "--loop";
            }
497 498
            else
            {
499 500 501
                ppsz_argv[ppsz_argc++] = "--no-loop";
            }
        }
502 503
        else if( !strcmp( argn[i], "version")
              || !strcmp( argn[i], "progid") )
504
        {
505
            progid = argv[i];
506
        }
507
        else if( !strcmp( argn[i], "toolbar" ) )
508
        {
509
            b_toolbar = boolValue(argv[i]);
510
        }
511 512
    }

513
    libvlc_instance = libvlc_new(ppsz_argc, ppsz_argv);
514 515
    if( !libvlc_instance )
        return NPERR_GENERIC_ERROR;
516
    libvlc_media_list = libvlc_media_list_new(libvlc_instance);
517 518 519 520 521 522

    /*
    ** 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
523
    NPObject *plugin = NULL;
524 525 526 527 528 529 530 531 532 533

    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;

534 535
        script.UTF8Characters = docLocHref;
        script.UTF8Length = sizeof(docLocHref)-1;
536 537 538 539 540 541 542

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

543
                psz_baseURL = (char *) malloc(location.UTF8Length+1);
544 545
                if( psz_baseURL )
                {
546 547
                    strncpy(psz_baseURL, location.UTF8Characters, location.UTF8Length);
                    psz_baseURL[location.UTF8Length] = '\0';
548 549 550 551 552 553 554 555 556 557
                }
            }
            NPN_ReleaseVariantValue(&result);
        }
        NPN_ReleaseObject(plugin);
    }

    if( psz_target )
    {
        // get absolute URL from src
558 559
        char *psz_absurl = getAbsoluteURL(psz_target);
        psz_target = psz_absurl ? psz_absurl : strdup(psz_target);
560 561 562
    }

    /* assign plugin script root class */
563 564
    /* new APIs */
    p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
565

566
    if( !events.init() )
567 568
        return NPERR_GENERIC_ERROR;

569
    return NPERR_NO_ERROR;
570 571
}

572
VlcPluginBase::~VlcPluginBase()
573
{
574 575
    free(psz_baseURL);
    free(psz_target);
576
    free(psz_text);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
577

578
    if( libvlc_media_player )
579
    {
580 581
        if( playlist_isplaying() )
            playlist_stop();
Jean-Paul Saman's avatar
Jean-Paul Saman committed
582
        events.unhook_manager( this );
583
        libvlc_media_player_release( libvlc_media_player );
584
    }
585 586
    if( libvlc_media_list )
        libvlc_media_list_release( libvlc_media_list );
587
    if( libvlc_instance )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
588
        libvlc_release( libvlc_instance );
589 590

    _instances.erase(this);
591 592
}

593
void VlcPluginBase::setWindow(const NPWindow &window)
594 595 596 597
{
    npwindow = window;
};

598
/*****************************************************************************
599
 * VlcPluginBase playlist replacement methods
600
 *****************************************************************************/
601
int VlcPluginBase::playlist_add( const char *mrl )
602 603
{
    int item = -1;
604
    libvlc_media_t *p_m = libvlc_media_new_location(libvlc_instance,mrl);
605
    if( !p_m )
606
        return -1;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
607
    assert( libvlc_media_list );
608
    libvlc_media_list_lock(libvlc_media_list);
609
    if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
610
        item = libvlc_media_list_count(libvlc_media_list)-1;
611 612 613 614 615 616 617
    libvlc_media_list_unlock(libvlc_media_list);

    libvlc_media_release(p_m);

    return item;
}

618
int VlcPluginBase::playlist_add_extended_untrusted( const char *mrl, const char *name,
619
                    int optc, const char **optv )
620
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
621
    libvlc_media_t *p_m;
622
    int item = -1;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
623 624 625

    assert( libvlc_media_list );

626
    p_m = libvlc_media_new_location(libvlc_instance, mrl);
627
    if( !p_m )
628 629 630
        return -1;

    for( int i = 0; i < optc; ++i )
631
        libvlc_media_add_option_flag(p_m, optv[i], libvlc_media_option_unique);
632 633

    libvlc_media_list_lock(libvlc_media_list);
634
    if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
635
        item = libvlc_media_list_count(libvlc_media_list)-1;
636 637 638 639 640 641
    libvlc_media_list_unlock(libvlc_media_list);
    libvlc_media_release(p_m);

    return item;
}

642
bool VlcPluginBase::playlist_select( int idx )
643 644 645
{
    libvlc_media_t *p_m = NULL;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
646 647
    assert( libvlc_media_list );

648 649
    libvlc_media_list_lock(libvlc_media_list);

650
    int count = libvlc_media_list_count(libvlc_media_list);
651 652 653 654 655
    if( idx<0||idx>=count )
        goto bad_unlock;

    playlist_index = idx;

656
    p_m = libvlc_media_list_item_at_index(libvlc_media_list,playlist_index);
657 658
    libvlc_media_list_unlock(libvlc_media_list);

659
    if( !p_m )
660 661 662 663
        return false;

    if( libvlc_media_player )
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
664 665
        if( playlist_isplaying() )
            playlist_stop();
Jean-Paul Saman's avatar
Jean-Paul Saman committed
666
        events.unhook_manager( this );
667
        on_media_player_release();
668 669 670 671
        libvlc_media_player_release( libvlc_media_player );
        libvlc_media_player = NULL;
    }

Jean-Paul Saman's avatar
Jean-Paul Saman committed
672
    libvlc_media_player = libvlc_media_player_new_from_media( p_m );
673
    if( libvlc_media_player )
674
    {
675
        on_media_player_new();
676
        set_player_window();
Jean-Paul Saman's avatar
Jean-Paul Saman committed
677 678 679 680

        libvlc_event_manager_t *p_em;
        p_em = libvlc_media_player_event_manager( libvlc_media_player );
        events.hook_manager( p_em, this );
681 682
    }

683
    libvlc_media_release( p_m );
684
    return true;
685 686

bad_unlock:
Jean-Paul Saman's avatar
Jean-Paul Saman committed
687
    libvlc_media_list_unlock( libvlc_media_list );
688 689 690
    return false;
}

691
int VlcPluginBase::playlist_delete_item( int idx )
692
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
693 694
    if( !libvlc_media_list )
        return -1;
695
    libvlc_media_list_lock(libvlc_media_list);
696
    int ret = libvlc_media_list_remove_index(libvlc_media_list,idx);
697
    libvlc_media_list_unlock(libvlc_media_list);
698
    return ret;
699 700
}

701
void VlcPluginBase::playlist_clear()
702 703 704
{
    if( libvlc_media_list )
        libvlc_media_list_release(libvlc_media_list);
705
    libvlc_media_list = libvlc_media_list_new(getVLC());
706 707
}

708
int VlcPluginBase::playlist_count()
709 710
{
    int items_count = 0;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
711 712
    if( !libvlc_media_list )
        return items_count;
713
    libvlc_media_list_lock(libvlc_media_list);
714
    items_count = libvlc_media_list_count(libvlc_media_list);
715 716 717 718 719
    libvlc_media_list_unlock(libvlc_media_list);
    return items_count;
}


720
bool  VlcPluginBase::player_has_vout()
721
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
722
    bool r = false;
723
    if( playlist_isplaying() )
724
        r = libvlc_media_player_has_vout(libvlc_media_player);
725 726 727
    return r;
}

728
/*****************************************************************************
729
 * VlcPluginBase methods
730
 *****************************************************************************/
731

732
char *VlcPluginBase::getAbsoluteURL(const char *url)
733
{
734
    if( NULL != url )
735
    {
736 737 738
        // check whether URL is already absolute
        const char *end=strchr(url, ':');
        if( (NULL != end) && (end != url) )
739
        {
740 741
            // validate protocol header
            const char *start = url;
742 743 744
            char c = *start;
            if( isalpha(c) )
            {
745
                ++start;
746 747 748 749 750 751 752 753 754 755 756 757 758 759
                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);
760
            }
761
            // not a valid protocol header, assume relative URL
762
        }
763

764 765
relativeurl:

766 767 768
        if( psz_baseURL )
        {
            size_t baseLen = strlen(psz_baseURL);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
769
            char *href = (char *) malloc(baseLen+strlen(url)+1);
770 771 772
            if( href )
            {
                /* prepend base URL */
JP Dinger's avatar
JP Dinger committed
773
                memcpy(href, psz_baseURL, baseLen+1);
774 775 776 777 778 779 780 781 782 783 784 785 786 787

                /*
                ** 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
788
                char *pathend = href+baseLen;
789
                if( pathstart )
790 791 792
                {
                    if( '/' == *(++pathstart) )
                    {
793 794 795 796 797 798 799 800 801 802 803 804
                        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 = '/';
805 806
                    }
                }
807
                else
808
                {
809 810 811 812
                    /* baseURL is just a UNIX path */
                    if( '/' != *href )
                    {
                        /* baseURL is not an absolute path */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
813
                        free(href);
814 815 816
                        return NULL;
                    }
                    pathstart = href;
817 818 819 820 821 822 823 824 825 826
                }

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

827
                /* find last path component and replace it */
828 829
                while( '/' != *pathend)
                    --pathend;
830 831 832 833 834 835 836 837 838 839 840 841

                /*
                ** 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;
842 843 844 845
                    if( '\0' == *p  )
                    {
                        /* relative url is just '.' */
                        url = p;
846
                        break;
847 848 849 850 851 852 853
                    }
                    if( '/' == *p  )
                    {
                        /* relative url starts with './' */
                        url = ++p;
                        continue;
                    }
854
                    if( '.' != *p )
855 856
                        break;
                    ++p;
857 858 859 860 861 862
                    if( '\0' == *p )
                    {
                        /* relative url is '..' */
                    }
                    else
                    {
863
                        if( '/' != *p )
864 865 866 867
                            break;
                        /* relative url starts with '../' */
                        ++p;
                    }
868
                    url = p;
869 870 871 872 873
                    do
                    {
                        --pathend;
                    }
                    while( '/' != *pathend );
874
                }
875 876
                /* skip over '/' separator */
                ++pathend;
877
                /* concatenate remaining base URL and relative URL */
878
                strcpy(pathend, url);
879 880 881
            }
            return href;
        }
882
    }
883 884
    return NULL;
}
885

886
void VlcPluginBase::control_handler(vlc_toolbar_clicked_t clicked)
887
{
888
    switch( clicked )
889
    {
890 891 892 893 894
        case clicked_Play:
        {
            playlist_play();
        }
        break;
895

896 897 898 899 900
        case clicked_Pause:
        {
            playlist_pause();
        }
        break;
901

902 903 904 905 906
        case clicked_Stop:
        {
            playlist_stop();
        }
        break;
907

908 909
        case clicked_Fullscreen:
        {
910
            toggle_fullscreen();
911 912
        }
        break;
913

914 915 916 917 918 919 920 921 922
        case clicked_Mute:
        case clicked_Unmute:
#if 0
        {
            if( p_md )
                libvlc_audio_toggle_mute( p_md );
        }
#endif
        break;
923

924 925 926 927 928 929 930 931
        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;
932

933 934
                f_length = (float)f_length *
                        ( ((float)i_xPos-4.0 ) / ( ((float)i_width-8.0)/100) );
935

936 937 938
                libvlc_media_player_set_time( p_md, f_length );
            }
        }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
939
#endif
940
        break;
941

942 943 944 945 946
        case clicked_Time:
        {
            /* Not implemented yet*/
        }
        break;
947

948 949 950 951
        default: /* button_Unknown */
            fprintf(stderr, "button Unknown!\n");
        break;
    }
952
}
953 954 955 956

// Verifies the version of the NPAPI.
// The eventListeners use a NPAPI function available
// since Gecko 1.9.
957
bool VlcPluginBase::canUseEventListener()
958 959 960 961 962 963 964 965 966 967 968
{
    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;
}