vlcplugin_base.cpp 23.4 KB
Newer Older
1
/*****************************************************************************
2
 * vlcplugin_base.cpp: a VLC plugin for Mozilla
3
 *****************************************************************************
4
 * Copyright (C) 2002-2012 VLC authors and VideoLAN
5
 * $Id$
6
 *
7
 * Authors: Damien Fouilleul <damienf.fouilleul@laposte.net>
8
 *          Jean-Paul Saman <jpsaman@videolan.org>
9
 *          Sergey Radionov <rsatom@gmail.com>
10
 *          Cheng Sun <chengsun9@gmail.com>
11 12 13
 *          Yannick Brehon <y.brehon@qiplay.com>
 *          Felix Paul Kühne <fkuehne # videolan.org>
 *          Ludovic Fauvet <etix@videolan.org>
14
 *          Hugo Beauzée-Luyssen <hugo@beauzee.fr>
15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * 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
28
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
29 30 31 32 33
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
34

35 36 37 38
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

Ludovic Fauvet's avatar
Ludovic Fauvet committed
39
#include "vlcplugin_base.h"
40
#include "vlcplugin.h"
Jean-Paul Saman's avatar
Jean-Paul Saman committed
41

42
#include "npruntime/npolibvlc.h"
43

44
#include <cctype>
45

46 47 48
#include <cstdio>
#include <cstdlib>
#include <cstring>
49

Jean-Paul Saman's avatar
Jean-Paul Saman committed
50
/*****************************************************************************
51
 * VlcPluginBase constructor and destructor
Jean-Paul Saman's avatar
Jean-Paul Saman committed
52
 *****************************************************************************/
53
VlcPluginBase::VlcPluginBase( NPP instance, NPuint16_t mode ) :
Jean-Paul Saman's avatar
Jean-Paul Saman committed
54 55 56 57 58 59
    i_npmode(mode),
    b_stream(0),
    psz_target(NULL),
    p_scriptClass(NULL),
    p_browser(instance),
    psz_baseURL(NULL)
60
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
61
    memset(&npwindow, 0, sizeof(NPWindow));
62 63
}

Jean-Paul Saman's avatar
Jean-Paul Saman committed
64
static bool boolValue(const char *value) {
Ludovic Fauvet's avatar
Ludovic Fauvet committed
65
    return ( *value == '\0' ||
66
             !strcmp(value, "1") ||
Jean-Paul Saman's avatar
Jean-Paul Saman committed
67 68 69
             !strcasecmp(value, "true") ||
             !strcasecmp(value, "yes") );
}
70

71
NPError VlcPluginBase::init(int argc, char* const argn[], char* const argv[])
72
{
73
    /* prepare VLC command line */
74
    const char *ppsz_argv[MAX_PARAMS];
75
    int ppsz_argc = 0;
76

77 78 79 80
#ifndef NDEBUG
    ppsz_argv[ppsz_argc++] = "--no-plugins-cache";
#endif

81
    /* locate VLC module path */
Felix Paul Kühne's avatar
Felix Paul Kühne committed
82
#ifdef XP_WIN
83 84 85 86 87
    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 )
88
    {
89 90 91 92 93
         if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
                              (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
         {
             if( i_type == REG_SZ )
             {
94
                 strcat( p_data, "\\plugins" );
95 96
                 ppsz_argv[ppsz_argc++] = "--plugin-path";
                 ppsz_argv[ppsz_argc++] = p_data;
97 98 99
             }
         }
         RegCloseKey( h_key );
100
    }
101
    ppsz_argv[ppsz_argc++] = "--no-one-instance";
102

103 104
#endif
#ifdef XP_MACOSX
105
    ppsz_argv[ppsz_argc++] = "--vout=caopengllayer";
106 107 108
    ppsz_argv[ppsz_argc++] = "--scaletempo-stride=30";
    ppsz_argv[ppsz_argc++] = "--scaletempo-overlap=0,2";
    ppsz_argv[ppsz_argc++] = "--scaletempo-search=14";
109
    ppsz_argv[ppsz_argc++] = "--auhal-volume=256";
110 111
    ppsz_argv[ppsz_argc++] = "--auhal-audio-device=0";
    ppsz_argv[ppsz_argc++] = "--no-volume-save";
Felix Paul Kühne's avatar
Felix Paul Kühne committed
112
#endif
113

114
    /* common settings */
115
#ifndef NDEBUG
116
    ppsz_argv[ppsz_argc++] = "-vv";
117 118 119
#else
    ppsz_argv[ppsz_argc++] = "--quiet";
#endif
120 121
    ppsz_argv[ppsz_argc++] = "--no-stats";
    ppsz_argv[ppsz_argc++] = "--no-media-library";
122
    ppsz_argv[ppsz_argc++] = "--intf=dummy";
123
    ppsz_argv[ppsz_argc++] = "--no-video-title-show";
Christophe Mutricy's avatar
Christophe Mutricy committed
124
    ppsz_argv[ppsz_argc++] = "--no-xlib";
125

126
    bool b_autoloop = false;
127
    bool b_mute = false;
128
    int i_volume = -1;
129

130
    /* parse plugin arguments */
131
    for( int i = 0; (i < argc) && (ppsz_argc < MAX_PARAMS); i++ )
132
    {
133
       /* fprintf(stderr, "argn=%s, argv=%s\n", argn[i], argv[i]); */
134 135 136 137 138 139 140 141

        if( !strcmp( argn[i], "target" )
         || !strcmp( argn[i], "mrl")
         || !strcmp( argn[i], "filename")
         || !strcmp( argn[i], "src") )
        {
            psz_target = argv[i];
        }
142 143
        else if( !strcmp( argn[i], "text" ) )
        {
144
            set_bg_text( argv[i] );
145
        }
146 147 148
        else if( !strcmp( argn[i], "autoplay")
              || !strcmp( argn[i], "autostart") )
        {
149
            set_autoplay( boolValue(argv[i]) );
150
        }
151
        else if( !strcmp( argn[i], "fullscreen" )
152 153
              || !strcmp( argn[i], "allowfullscreen" )
              || !strcmp( argn[i], "fullscreenenabled" ) )
154
        {
155
            set_enable_fs( boolValue(argv[i]) );
156 157 158
        }
        else if( !strcmp( argn[i], "mute" ) )
        {
159
            b_mute = boolValue( argv[i] );
160
        }
161 162 163 164
        else if( !strcmp( argn[i], "volume" ) )
        {
            i_volume = atoi( argv[i] );
        }
165 166 167
        else if( !strcmp( argn[i], "loop")
              || !strcmp( argn[i], "autoloop") )
        {
168
            b_autoloop = boolValue(argv[i]);
169
        }
170 171
        else if( !strcmp( argn[i], "toolbar" )
              || !strcmp( argn[i], "controls") )
172
        {
173
            set_show_toolbar( boolValue(argv[i]) );
174
        }
175 176
        else if( !strcmp( argn[i], "bgcolor" ) )
        {
177
            set_bg_color( argv[i] );
178
        }
179 180 181 182
        else if( !strcmp( argn[i], "branding" ) )
        {
            set_enable_branding( boolValue(argv[i]) );
        }
183 184
    }

185 186
    try {
        VLC::Instance instance( ppsz_argc, ppsz_argv );
187
        m_player.open(instance);
188 189
    }
    catch (std::runtime_error&) {
190
        return NPERR_GENERIC_ERROR;
191
    }
192

193
    m_player.mlp().setPlaybackMode( b_autoloop ? libvlc_playback_mode_loop :
194 195 196 197
                                      libvlc_playback_mode_default );

    if( b_mute )
        m_player.get_mp().setMute( true );
198

199 200 201
    if( i_volume >= 0 && i_volume <= 200 )
        m_player.get_mp().setVolume( i_volume );

202 203 204 205 206
    /*
    ** 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
207
    NPObject *plugin = NULL;
208 209 210 211 212 213 214 215 216 217

    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;

218 219
        script.UTF8Characters = docLocHref;
        script.UTF8Length = sizeof(docLocHref)-1;
220 221 222 223 224 225 226

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

227
                psz_baseURL = (char *) malloc(location.UTF8Length+1);
228 229
                if( psz_baseURL )
                {
230 231
                    strncpy(psz_baseURL, location.UTF8Characters, location.UTF8Length);
                    psz_baseURL[location.UTF8Length] = '\0';
232 233 234 235 236 237 238 239 240 241
                }
            }
            NPN_ReleaseVariantValue(&result);
        }
        NPN_ReleaseObject(plugin);
    }

    if( psz_target )
    {
        // get absolute URL from src
242 243
        char *psz_absurl = getAbsoluteURL(psz_target);
        psz_target = psz_absurl ? psz_absurl : strdup(psz_target);
244 245 246
    }

    /* assign plugin script root class */
247 248
    /* new APIs */
    p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
249

250 251 252 253 254
    // Update the UI if required when we switch media
    m_player.mlp().eventManager().onNextItemSet([this](VLC::MediaPtr) {
        update_controls();
    });

255
    return NPERR_NO_ERROR;
256 257
}

258
VlcPluginBase::~VlcPluginBase()
259
{
260 261
    free(psz_baseURL);
    free(psz_target);
262 263
}

264
void VlcPluginBase::setWindow(const NPWindow &window)
265 266
{
    npwindow = window;
267
}
268

269 270 271 272 273 274 275
#if defined(XP_MACOSX)
NPError VlcPluginBase::get_root_layer(void *value)
{
    return NPERR_GENERIC_ERROR;
}
#endif

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
276
bool VlcPluginBase::handle_event(void *)
277 278 279 280
{
    return false;
}

281
template <typename... Args>
282 283
struct AsyncEventWrapper
{
284 285
    template <typename... PFArgs>
    AsyncEventWrapper(NPP b, NPObject* l, PFArgs&&... args)
286
        : browser( b )
287
        , listener( l )
288
        , args( std::make_tuple( std::forward<Args>( args )... ) )
289 290 291 292
    {
    }

    NPP browser;
293
    NPObject* listener;
294 295 296 297
    // If we do not decay Args here, we will end up declaring a tuple holding references
    // The tuple returned by make_tuple does that, but we end up converting from the correct tuple
    // type to an invalid tuple type, since we would end up using dangling references.
    std::tuple<typename std::decay<Args>::type...> args;
298 299 300
};

template <typename... Args>
301
static void invokeEvent( NPP browser, NPObject* listener, Args&&... args )
302
{
303
    auto wrapper = new AsyncEventWrapper<Args...>( browser, listener, std::forward<Args>( args )... );
304
    NPN_PluginThreadAsyncCall( browser, [](void* data) {
305 306
        auto w = reinterpret_cast<AsyncEventWrapper<Args...>*>( data );
        auto args = npapi::wrap( w->args );
307
        NPVariant result;
308
        if (NPN_InvokeDefault( w->browser, w->listener, reinterpret_cast<NPVariant*>( args.data() ), args.size(), &result ))
309 310 311 312 313 314 315 316 317 318 319
        {
            // "result" content is unspecified when invoke fails. Don't clean potential garbage
            NPN_ReleaseVariantValue( &result );
        }
        delete w;
    }, wrapper);
}

class CallbackClosure
{
public:
320
    CallbackClosure(NPP browser, npapi::Variant&& listener)
321 322 323 324 325 326
        : _browser( browser )
        , _listener( std::move( listener ) )
    {
    }

    CallbackClosure(const CallbackClosure&) = delete;
327 328

#ifndef _MSC_VER
329
    CallbackClosure(CallbackClosure&&) = default;
330 331 332 333 334 335 336
#else
    CallbackClosure(CallbackClosure&& c)
        : _browser( std::move(c._browser) )
        , _listener( std::move(c._listener) )
    {
    }
#endif
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375

    template <typename... Args>
    void operator()(Args&&... params) const
    {
        // This is expected to receive a copy of the listener
        invokeEvent( _browser, _listener, std::forward<Args>( params )... );
    }

    void operator()(VLC::MediaPtr) const
    {
        // Force Media to be ignored since we don't have a binding for it.
        // This is expected to receive a copy of the listener
        invokeEvent( _browser, _listener );
    }
private:
    NPP _browser;
    npapi::Variant _listener;
};

static struct vlcevents_t {
    const char* name;
    libvlc_event_type_t type;
} vlcevents[] = {
    { "MediaPlayerMediaChanged", libvlc_MediaPlayerMediaChanged },
    { "MediaPlayerNothingSpecial", libvlc_MediaPlayerNothingSpecial },
    { "MediaPlayerOpening", libvlc_MediaPlayerOpening },
    { "MediaPlayerBuffering", libvlc_MediaPlayerBuffering },
    { "MediaPlayerPlaying", libvlc_MediaPlayerPlaying },
    { "MediaPlayerPaused", libvlc_MediaPlayerPaused },
    { "MediaPlayerStopped", libvlc_MediaPlayerStopped },
    { "MediaPlayerForward", libvlc_MediaPlayerForward },
    { "MediaPlayerBackward", libvlc_MediaPlayerBackward },
    { "MediaPlayerEndReached", libvlc_MediaPlayerEndReached },
    { "MediaPlayerEncounteredError", libvlc_MediaPlayerEncounteredError },
    { "MediaPlayerTimeChanged", libvlc_MediaPlayerTimeChanged },
    { "MediaPlayerPositionChanged", libvlc_MediaPlayerPositionChanged },
    { "MediaPlayerSeekableChanged", libvlc_MediaPlayerSeekableChanged },
    { "MediaPlayerPausableChanged", libvlc_MediaPlayerPausableChanged },
    { "MediaPlayerTitleChanged", libvlc_MediaPlayerTitleChanged },
376 377 378
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)
    { "MediaPlayerChapterChanged", libvlc_MediaPlayerChapterChanged },
#endif
379
    { "MediaPlayerLengthChanged", libvlc_MediaPlayerLengthChanged },
380 381 382 383 384 385
    { "MediaPlayerVout", libvlc_MediaPlayerVout },
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0)
    { "MediaPlayerMuted", libvlc_MediaPlayerMuted },
    { "MediaPlayerUnmuted", libvlc_MediaPlayerUnmuted },
    { "MediaPlayerAudioVolume", libvlc_MediaPlayerAudioVolume },
#endif
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
};

void VlcPluginBase::subscribe(const char* eventName, npapi::Variant listener)
{
    auto event = std::find_if(std::begin(vlcevents), std::end(vlcevents), [eventName](const vlcevents_t& e) {
        return !strcmp( e.name, eventName);
    });
    if (event == std::end(vlcevents))
        return;

    auto listenerRaw = (NPObject*)listener;
    auto closure = CallbackClosure{ p_browser, std::move( listener ) };

    VLC::EventManager::RegisteredEvent e = nullptr;
    switch ( event->type )
    {
        case libvlc_MediaPlayerNothingSpecial:
403
            e = player().get_mp().eventManager().onNothingSpecial( std::move( closure ) );
404 405
            break;
        case libvlc_MediaPlayerOpening:
406
            e = player().get_mp().eventManager().onOpening( std::move( closure ) );
407 408
            break;
        case libvlc_MediaPlayerPlaying:
409
            e = player().get_mp().eventManager().onPlaying( std::move( closure ) );
410 411
            break;
        case libvlc_MediaPlayerPaused:
412
            e = player().get_mp().eventManager().onPaused( std::move( closure ) );
413 414
            break;
        case libvlc_MediaPlayerStopped:
415
            e = player().get_mp().eventManager().onStopped( std::move( closure ) );
416 417
            break;
        case libvlc_MediaPlayerForward:
418
            e = player().get_mp().eventManager().onForward( std::move( closure ) );
419 420
            break;
        case libvlc_MediaPlayerBackward:
421
            e = player().get_mp().eventManager().onBackward( std::move( closure ) );
422 423
            break;
        case libvlc_MediaPlayerEndReached:
424
            e = player().get_mp().eventManager().onEndReached( std::move( closure ) );
425 426
            break;
        case libvlc_MediaPlayerEncounteredError:
427
            e = player().get_mp().eventManager().onEncounteredError( std::move( closure ) );
428 429
            break;
        case libvlc_MediaPlayerBuffering:
430
            e = player().get_mp().eventManager().onBuffering( std::move( closure ) );
431 432
            break;
        case libvlc_MediaPlayerTimeChanged:
433
            e = player().get_mp().eventManager().onTimeChanged( std::move( closure ) );
434 435
            break;
        case libvlc_MediaPlayerMediaChanged:
436
            e = player().get_mp().eventManager().onMediaChanged( std::move( closure ) );
437 438
            break;
        case libvlc_MediaPlayerPositionChanged:
439
            e = player().get_mp().eventManager().onPositionChanged( std::move( closure ) );
440 441
            break;
        case libvlc_MediaPlayerSeekableChanged:
442
            e = player().get_mp().eventManager().onSeekableChanged( std::move( closure ) );
443 444
            break;
        case libvlc_MediaPlayerPausableChanged:
445
            e = player().get_mp().eventManager().onPausableChanged( std::move( closure ) );
446 447
            break;
        case libvlc_MediaPlayerTitleChanged:
448
            e = player().get_mp().eventManager().onTitleChanged( std::move( closure ) );
449
            break;
450 451 452 453 454
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)
        case libvlc_MediaPlayerChapterChanged:
            e = player().get_mp().eventManager().onChapterChanged( std::move( closure ) );
            break;
#endif
455
        case libvlc_MediaPlayerLengthChanged:
456
            e = player().get_mp().eventManager().onLengthChanged( std::move( closure ) );
457
            break;
458 459 460 461 462 463 464 465 466 467 468 469 470 471
        case libvlc_MediaPlayerVout:
            e = player().get_mp().eventManager().onVout( std::move( closure ) );
            break;
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0)
        case libvlc_MediaPlayerMuted:
            e = player().get_mp().eventManager().onMuted( std::move( closure ) );
            break;
        case libvlc_MediaPlayerUnmuted:
            e = player().get_mp().eventManager().onUnmuted( std::move( closure ) );
            break;
        case libvlc_MediaPlayerAudioVolume:
            e = player().get_mp().eventManager().onAudioVolume( std::move( closure ) );
            break;
#endif
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
        default:
            break;
    }
    if ( e != nullptr )
    {
        m_events.emplace_back( std::string(eventName), listenerRaw, e );
    }
}

void VlcPluginBase::unsubscribe(const char* eventName, npapi::Variant listener)
{
    auto event = std::find_if( begin( m_events ), end( m_events ), [eventName, listener](const decltype(m_events)::value_type e) {
        return std::get<0>( e ) == eventName && std::get<1>( e ) == listener;
    });
    if ( event == end( m_events ) )
        return;
    std::get<2>( *event )->unregister();
    m_events.erase( event );
}

492
/*****************************************************************************
493
 * VlcPluginBase methods
494
 *****************************************************************************/
495

496
char *VlcPluginBase::getAbsoluteURL(const char *url)
497
{
498
    if( NULL != url )
499
    {
500 501 502
        // check whether URL is already absolute
        const char *end=strchr(url, ':');
        if( (NULL != end) && (end != url) )
503
        {
504 505
            // validate protocol header
            const char *start = url;
506 507 508
            char c = *start;
            if( isalpha(c) )
            {
509
                ++start;
510 511 512 513 514 515 516 517 518 519 520 521 522 523
                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);
524
            }
525
            // not a valid protocol header, assume relative URL
526
        }
527

528 529
relativeurl:

530 531 532
        if( psz_baseURL )
        {
            size_t baseLen = strlen(psz_baseURL);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
533
            char *href = (char *) malloc(baseLen+strlen(url)+1);
534 535 536
            if( href )
            {
                /* prepend base URL */
JP Dinger's avatar
JP Dinger committed
537
                memcpy(href, psz_baseURL, baseLen+1);
538 539 540 541 542 543 544 545 546 547 548 549 550 551

                /*
                ** 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
552
                char *pathend = href+baseLen;
553
                if( pathstart )
554 555 556
                {
                    if( '/' == *(++pathstart) )
                    {
557 558 559 560 561 562 563 564 565 566 567 568
                        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 = '/';
569 570
                    }
                }
571
                else
572
                {
573 574 575 576
                    /* baseURL is just a UNIX path */
                    if( '/' != *href )
                    {
                        /* baseURL is not an absolute path */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
577
                        free(href);
578 579 580
                        return NULL;
                    }
                    pathstart = href;
581 582 583 584 585 586 587 588 589 590
                }

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

591
                /* find last path component and replace it */
592 593
                while( '/' != *pathend)
                    --pathend;
594 595 596 597 598 599 600 601 602 603 604 605

                /*
                ** 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;
606 607 608 609
                    if( '\0' == *p  )
                    {
                        /* relative url is just '.' */
                        url = p;
610
                        break;
611 612 613 614 615 616 617
                    }
                    if( '/' == *p  )
                    {
                        /* relative url starts with './' */
                        url = ++p;
                        continue;
                    }
618
                    if( '.' != *p )
619 620
                        break;
                    ++p;
621 622 623 624 625 626
                    if( '\0' == *p )
                    {
                        /* relative url is '..' */
                    }
                    else
                    {
627
                        if( '/' != *p )
628 629 630 631
                            break;
                        /* relative url starts with '../' */
                        ++p;
                    }
632
                    url = p;
633 634 635 636 637
                    do
                    {
                        --pathend;
                    }
                    while( '/' != *pathend );
638
                }
639 640
                /* skip over '/' separator */
                ++pathend;
641
                /* concatenate remaining base URL and relative URL */
642
                strcpy(pathend, url);
643 644 645
            }
            return href;
        }
646
    }
647 648
    return NULL;
}
649

650
void VlcPluginBase::control_handler(vlc_toolbar_clicked_t clicked)
651
{
652
    switch( clicked )
653
    {
654 655
        case clicked_Play:
        {
656
            player().play();
657 658
        }
        break;
659

660 661
        case clicked_Pause:
        {
662
            player().get_mp().pause();
663 664
        }
        break;
665

666 667
        case clicked_Stop:
        {
668
            player().get_mp().stop();
669 670
        }
        break;
671

672 673
        case clicked_Fullscreen:
        {
674
            toggle_fullscreen();
675 676
        }
        break;
677

678 679 680 681 682 683 684 685 686
        case clicked_Mute:
        case clicked_Unmute:
#if 0
        {
            if( p_md )
                libvlc_audio_toggle_mute( p_md );
        }
#endif
        break;
687

688 689 690 691 692 693 694 695
        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;
696

697 698
                f_length = (float)f_length *
                        ( ((float)i_xPos-4.0 ) / ( ((float)i_width-8.0)/100) );
699

700 701 702
                libvlc_media_player_set_time( p_md, f_length );
            }
        }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
703
#endif
704
        break;
705

706 707 708 709 710
        case clicked_Time:
        {
            /* Not implemented yet*/
        }
        break;
711

712 713 714 715
        default: /* button_Unknown */
            fprintf(stderr, "button Unknown!\n");
        break;
    }
716
}
717 718 719 720

// Verifies the version of the NPAPI.
// The eventListeners use a NPAPI function available
// since Gecko 1.9.
721
bool VlcPluginBase::canUseEventListener()
722 723 724 725 726 727 728 729 730 731 732
{
    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;
}
733