Commit d1c39c40 authored by JP Dinger's avatar JP Dinger Committed by Rémi Denis-Courmont

mozilla plugin: rework events listeners to dom level 2 type, sort of.

parent 6005ce1a
......@@ -76,45 +76,6 @@ NPVariant copyNPVariant(const NPVariant& original)
return res;
}
// Parse an event Array given as a NPObject by JS.
// This function is similar to LibvlcPlaylistNPObject::parseOptions,
// but we don't use it because it's not clearly accessible and the FIXME flags
// implie that it might be modified.
bool parseEventArray(NPObject *obj, eventtypes_bitmap_t &eventToGet, NPP instance)
{
NPIdentifier propId = NPN_GetStringIdentifier("length");
NPVariant value;
if (!NPN_GetProperty(instance, obj, propId, &value))
return false;
int count = NPVARIANT_TO_INT32(value);
NPN_ReleaseVariantValue(&value);
if (count == 0)
return false;
int nOptions = 0;
while (nOptions < count)
{
propId = NPN_GetIntIdentifier(nOptions);
// if there is no other string in the array.
if( ! NPN_GetProperty(instance, obj, propId, &value) )
break;
// if the element is not a string.
if( ! NPVARIANT_IS_STRING(value) )
{
NPN_ReleaseVariantValue(&value);
break;
}
if (!eventToGet.add_event(NPVARIANT_TO_STRING(value).utf8characters))
return false;
nOptions++;
}
return true;
}
/*
** implementation of libvlc root object
*/
......@@ -134,6 +95,7 @@ LibvlcRootNPObject::~LibvlcRootNPObject()
if( audioObj ) NPN_ReleaseObject(audioObj);
if( inputObj ) NPN_ReleaseObject(inputObj);
if( playlistObj ) NPN_ReleaseObject(playlistObj);
if( subtitleObj ) NPN_ReleaseObject(subtitleObj);
if( videoObj ) NPN_ReleaseObject(videoObj);
}
}
......@@ -145,7 +107,6 @@ const NPUTF8 * const LibvlcRootNPObject::propertyNames[] =
"playlist",
"subtitle",
"video",
"events",
"VersionInfo",
};
COUNTNAMES(LibvlcRootNPObject,propertyCount,propertyNames);
......@@ -157,7 +118,6 @@ enum LibvlcRootNPObjectPropertyIds
ID_root_playlist,
ID_root_subtitle,
ID_root_video,
ID_root_events,
ID_root_VersionInfo,
};
......@@ -189,14 +149,6 @@ LibvlcRootNPObject::getProperty(int index, NPVariant &result)
InstantObj<LibvlcVideoNPObject>( videoObj );
OBJECT_TO_NPVARIANT(NPN_RetainObject(videoObj), result);
return INVOKERESULT_NO_ERROR;
case ID_root_events:
// create child object in lazyman fashion to avoid
// ownership problem with firefox
if( ! eventObj )
eventObj = NPN_CreateObject(_instance,
RuntimeNPClass<LibvlcEventNPObject>::getClass());
OBJECT_TO_NPVARIANT(NPN_RetainObject(eventObj), result);
return INVOKERESULT_NO_ERROR;
case ID_root_VersionInfo:
return invokeResultString(libvlc_get_version(),result);
default:
......@@ -209,34 +161,65 @@ LibvlcRootNPObject::getProperty(int index, NPVariant &result)
const NPUTF8 * const LibvlcRootNPObject::methodNames[] =
{
"versionInfo",
"addEventListener",
"removeEventListener",
};
COUNTNAMES(LibvlcRootNPObject,methodCount,methodNames);
enum LibvlcRootNPObjectMethodIds
{
ID_root_versionInfo,
ID_root_addeventlistener,
ID_root_removeeventlistener,
};
RuntimeNPObject::InvokeResult LibvlcRootNPObject::invoke(int index,
const NPVariant *args, uint32_t argCount, NPVariant &result)
{
/* is plugin still running */
if( isPluginRunning() )
if( !isPluginRunning() )
return INVOKERESULT_GENERIC_ERROR;
libvlc_exception_t ex;
libvlc_exception_init(&ex);
switch( index )
{
libvlc_exception_t ex;
libvlc_exception_init(&ex);
case ID_root_versionInfo:
if( 0 != argCount )
return INVOKERESULT_NO_SUCH_METHOD;
return invokeResultString(libvlc_get_version(),result);
case ID_root_addeventlistener:
case ID_root_removeeventlistener:
if( (3 != argCount) ||
!NPVARIANT_IS_STRING(args[0]) ||
!NPVARIANT_IS_OBJECT(args[1]) ||
!NPVARIANT_IS_BOOLEAN(args[2]) )
break;
switch( index )
if( !VlcPlugin::canUseEventListener() )
{
case ID_root_versionInfo:
if( 0 != argCount )
return INVOKERESULT_NO_SUCH_METHOD;
return invokeResultString(libvlc_get_version(),result);
default:
;
NPN_SetException(this, ERROR_API_VERSION);
return INVOKERESULT_GENERIC_ERROR;
}
NPObject *listener = NPVARIANT_TO_OBJECT(args[1]);
VlcPlugin* p_plugin = getPrivate<VlcPlugin>();
bool b;
if(ID_root_removeeventlistener!=index)
b = p_plugin->events.insert(NPVARIANT_TO_STRING(args[0]),
listener, NPVARIANT_TO_BOOLEAN(args[2]));
else
b = p_plugin->events.remove(NPVARIANT_TO_STRING(args[0]),
listener, NPVARIANT_TO_BOOLEAN(args[2]));
VOID_TO_NPVARIANT(result);
return b ? INVOKERESULT_NO_ERROR : INVOKERESULT_GENERIC_ERROR;
}
return INVOKERESULT_GENERIC_ERROR;
return INVOKERESULT_NO_SUCH_METHOD;
}
/*
......@@ -2061,125 +2044,3 @@ LibvlcDeinterlaceNPObject::invoke(int index, const NPVariant *args,
return INVOKERESULT_NO_ERROR;
}
/*
** implementation of libvlc event object
*/
const NPUTF8 * const LibvlcEventNPObject::propertyNames[] =
{
};
enum LibvlcEventNPObjectPropertyIds
{
};
COUNTNAMES(LibvlcEventNPObject,propertyCount,propertyNames);
const NPUTF8 * const LibvlcEventNPObject::methodNames[] =
{
"addListener",
"removeListeners",
};
COUNTNAMES(LibvlcEventNPObject,methodCount,methodNames);
enum LibvlcEventNPObjectMethodIds
{
ID_event_addListener,
ID_event_removeListeners,
};
bool LibvlcEventNPObject::parseArgs(const NPVariant *args, uint32_t argCount,
eventtypes_bitmap_t &eventToGet)
{
if (argCount > 2)
eventToGet.clear();
for (int argIndex = 2; argIndex < argCount; argIndex++)
{
if (NPVARIANT_IS_STRING(args[argIndex]))
{
if (!eventToGet.add_event(NPVARIANT_TO_STRING(args[argIndex]).utf8characters))
return false;
}
else if (NPVARIANT_IS_OBJECT(args[argIndex]))
{
if (!parseEventArray(NPVARIANT_TO_OBJECT(args[argIndex]), eventToGet, _instance))
return false;
}
else
return false;
}
return true;
}
RuntimeNPObject::InvokeResult
LibvlcEventNPObject::invoke(int index, const NPVariant *args,
uint32_t argCount, NPVariant &result)
{
/* is plugin still running */
if( isPluginRunning() )
{
libvlc_exception_t ex;
libvlc_exception_init(&ex);
switch( index )
{
case ID_event_addListener:
if (argCount >= 2)
{
// Checks if the first argument is a NPObject
if (!NPVARIANT_IS_OBJECT(args[0]))
return INVOKERESULT_NO_SUCH_METHOD;
// Checks if the browser has the NPAPI version 0.19 at least.
if (!VlcPlugin::canUseEventListener())
{
NPN_SetException(this, strdup(ERROR_API_VERSION));
return INVOKERESULT_GENERIC_ERROR;
}
VlcPlugin* p_plugin = getPrivate<VlcPlugin>();
// Gets the binary field corresponding to the events the
// listener must listen to if specified.
// Else, listen to all events.
eventtypes_bitmap_t eventToGet;
eventToGet.set_all_events();
if (!parseArgs(args, argCount, eventToGet))
{
NPN_SetException(this, strdup(ERROR_EVENT_NOT_FOUND));
return INVOKERESULT_GENERIC_ERROR;
}
NPObject *listener = NPVARIANT_TO_OBJECT(args[0]);
NPN_RetainObject(listener);
EventListener *eventListener = new EventListener();
eventListener->listener = listener;
eventListener->id = copyNPVariant(args[1]);
eventListener->eventMap = eventToGet;
p_plugin->eventToCatch.add_event(eventToGet);
p_plugin->eventListeners.push_back(eventListener);
return INVOKERESULT_NO_ERROR;
}
return INVOKERESULT_NO_SUCH_METHOD;
case ID_event_removeListeners:
if (argCount == 0)
{
VlcPlugin* p_plugin = getPrivate<VlcPlugin>();
p_plugin->eventListeners.clear();
p_plugin->eventToCatch.clear();
return INVOKERESULT_NO_ERROR;
}
return INVOKERESULT_NO_SUCH_METHOD;
default:
;
}
}
return INVOKERESULT_GENERIC_ERROR;
}
......@@ -38,8 +38,7 @@ protected:
inputObj(NULL),
playlistObj(NULL),
subtitleObj(NULL),
videoObj(NULL),
eventObj(NULL) {};
videoObj(NULL) { }
virtual ~LibvlcRootNPObject();
......@@ -59,7 +58,6 @@ private:
NPObject *playlistObj;
NPObject *subtitleObj;
NPObject *videoObj;
NPObject *eventObj;
};
class LibvlcAudioNPObject: public RuntimeNPObject
......@@ -265,23 +263,3 @@ protected:
InvokeResult invoke(int index, const NPVariant *args, uint32_t argCount, NPVariant &result);
};
class LibvlcEventNPObject: public RuntimeNPObject
{
protected:
friend class RuntimeNPClass<LibvlcEventNPObject>;
LibvlcEventNPObject(NPP instance, const NPClass *aClass) :
RuntimeNPObject(instance, aClass) {};
virtual ~LibvlcEventNPObject() {};
static const int propertyCount;
static const NPUTF8 * const propertyNames[];
static const int methodCount;
static const NPUTF8 * const methodNames[];
InvokeResult invoke(int index, const NPVariant *args, uint32_t argCount, NPVariant &result);
bool parseArgs(const NPVariant *args, uint32_t argCount,
eventtypes_bitmap_t &eventToGet);
};
......@@ -36,7 +36,7 @@
#include "control/npolibvlc.h"
#include <ctype.h>
#include <string>
#include <pthread.h>
/*****************************************************************************
* VlcPlugin constructor and destructor
......@@ -87,48 +87,156 @@ static bool boolValue(const char *value) {
!strcasecmp(value, "yes") );
}
void eventAsync(void *param)
void EventObj::deliver(NPP browser)
{
VlcPlugin *plugin = (VlcPlugin*)param;
NPVariant result;
NPVariant params[2];
NPVariant params[1];
pthread_mutex_lock(&plugin->mutex);
pthread_mutex_lock(&mutex);
for (int i = 0; i < plugin->eventList.size(); i++)
for( ev_l::iterator i=_elist.begin();i!=_elist.end();++i )
{
for (int j = 0; j < plugin->eventListeners.size(); j++)
{
libvlc_event_type_t event = plugin->eventList[i];
libvlc_event_type_t event = *i;
STRINGZ_TO_NPVARIANT(libvlc_event_type_name(event), params[0]);
// Invalid events aren't supposed to be queued up.
// if( !have_event(event) ) continue;
if (plugin->eventListeners[j]->eventMap.have_event(event))
for( lr_l::iterator j=_llist.begin();j!=_llist.end();++j )
{
if (j->get(event))
{
STRINGZ_TO_NPVARIANT(libvlc_event_type_name(event), params[0]);
params[1] = plugin->eventListeners[j]->id;
NPN_InvokeDefault(plugin->getBrowser(), plugin->eventListeners[j]->listener, params, 2, &result);
NPN_InvokeDefault(browser, j->listener(), params, 1, &result);
NPN_ReleaseVariantValue(&result);
}
}
}
plugin->eventList.clear();
_elist.clear();
pthread_mutex_unlock(&plugin->mutex);
pthread_mutex_unlock(&mutex);
}
void event_callback(const libvlc_event_t* event, void *param)
void VlcPlugin::eventAsync(void *param)
{
VlcPlugin *plugin = (VlcPlugin*)param;
plugin->events.deliver(plugin->getBrowser());
}
pthread_mutex_lock(&plugin->mutex);
void EventObj::callback(const libvlc_event_t* event)
{
pthread_mutex_lock(&mutex);
if (plugin->eventToCatch.have_event(event->type))
plugin->eventList.push_back(event->type);
if( have_event(event->type) )
_elist.push_back(event->type);
pthread_mutex_unlock(&plugin->mutex);
pthread_mutex_unlock(&mutex);
}
void VlcPlugin::event_callback(const libvlc_event_t* event, void *param)
{
VlcPlugin *plugin = (VlcPlugin*)param;
plugin->events.callback(event);
#ifdef XP_UNIX
NPN_PluginThreadAsyncCall(plugin->getBrowser(), eventAsync, plugin);
#else
NPN_SetException(this, "NPN_PluginThreadAsyncCall not implemented yet.");
#endif
}
inline EventObj::event_t EventObj::find_event(const char *s) const
{
event_t i;
for(i=0;i<maxbit();++i)
if(!strcmp(s,libvlc_event_type_name(i)))
break;
return i;
}
bool EventObj::insert(const NPString &s, NPObject *l, bool b)
{
event_t e = find_event(s.utf8characters);
if( e>=maxbit() )
return false;
if( !have_event(e) && !ask_for_event(e) )
return false;
lr_l::iterator i;
for(i=_llist.begin();i!=_llist.end();++i)
if(i->listener()==l && i->bubble()==b)
break;
if( i == _llist.end() ) {
_llist.push_back(Listener(e,l,b));
} else {
if( i->get(e) )
return false;
i->get(e);
}
}
bool EventObj::remove(const NPString &s, NPObject *l, bool b)
{
event_t e = find_event(s.utf8characters);
if( e>=maxbit() || !get(e) )
return false;
bool any=false;
for(lr_l::iterator i=_llist.begin();i!=_llist.end();)
{
if(i->listener()!=l || i->bubble()!=b)
any|=i->get(e);
else
{
i->reset(e);
if(i->empty())
{
i=_llist.erase(i);
continue;
}
}
++i;
}
if(!any)
unask_for_event(e);
}
void EventObj::hook_manager(libvlc_event_manager_t *em,
libvlc_callback_t cb, void *udata)
{
_em = em; _cb = cb; _ud = udata;
if( !_em )
return;
for(size_t i=0;i<maxbit();++i)
if(get(i))
libvlc_event_attach(_em, i, _cb, _ud);
}
void EventObj::unhook_manager()
{
if( !_em )
return;
for(size_t i=0;i<maxbit();++i)
if(get(i))
libvlc_event_detach(_em, i, _cb, _ud);
}
bool EventObj::ask_for_event(event_t e)
{
return _em?0==libvlc_event_attach(_em, e, _cb, _ud):false;
}
void EventObj::unask_for_event(event_t e)
{
if(_em) libvlc_event_detach(_em, e, _cb, _ud);
}
NPError VlcPlugin::init(int argc, char* const argn[], char* const argv[])
{
/* prepare VLC command line */
......@@ -302,7 +410,7 @@ NPError VlcPlugin::init(int argc, char* const argn[], char* const argv[])
/* new APIs */
p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
if (pthread_mutex_init(&mutex, NULL) != 0)
if( !events.init() )
return NPERR_GENERIC_ERROR;
return NPERR_NO_ERROR;
......@@ -315,20 +423,14 @@ VlcPlugin::~VlcPlugin()
free(psz_text);
if( libvlc_media_player )
{
events.unhook_manager();
libvlc_media_player_release( libvlc_media_player );
}
if( libvlc_media_list )
libvlc_media_list_release( libvlc_media_list );
if( libvlc_instance )
libvlc_release(libvlc_instance);
for (int i = 0; i < eventListeners.size(); i++)
{
NPN_ReleaseObject(eventListeners[i]->listener);
NPN_ReleaseVariantValue(&(eventListeners[i]->id));
delete eventListeners[i];
}
pthread_mutex_destroy(&mutex);
}
/*****************************************************************************
......@@ -391,7 +493,6 @@ int VlcPlugin::playlist_add_extended_untrusted( const char *mrl, const char *nam
bool VlcPlugin::playlist_select( int idx, libvlc_exception_t *ex )
{
libvlc_media_t *p_m = NULL;
libvlc_event_manager_t *eventManager = NULL;
libvlc_media_list_lock(libvlc_media_list);
......@@ -410,6 +511,7 @@ bool VlcPlugin::playlist_select( int idx, libvlc_exception_t *ex )
if( libvlc_media_player )
{
events.unhook_manager();
libvlc_media_player_release( libvlc_media_player );
libvlc_media_player = NULL;
}
......@@ -418,25 +520,9 @@ bool VlcPlugin::playlist_select( int idx, libvlc_exception_t *ex )
if( libvlc_media_player )
{
set_player_window();
// Registers the events we're interested in.
eventManager = libvlc_media_player_event_manager(libvlc_media_player, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerOpening, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerBuffering, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerPlaying, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerStopped, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerPaused, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerEndReached, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerForward, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerBackward, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerEndReached, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerEncounteredError, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerTimeChanged, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerPositionChanged, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerTitleChanged, event_callback, this, ex);
libvlc_event_attach(eventManager, libvlc_MediaPlayerSnapshotTaken, event_callback, this, ex);
events.hook_manager(
libvlc_media_player_event_manager(libvlc_media_player),
event_callback, this);
}
libvlc_media_release( p_m );
......
......@@ -30,8 +30,10 @@
#define __VLCPLUGIN_H__
#include <vlc/vlc.h>
#include <pthread.h>
#include <npapi.h>
#include <vector>
#include "control/nporuntime.h"
#if !defined(XP_MACOSX) && !defined(XP_UNIX) && !defined(XP_WIN)
......@@ -90,11 +92,7 @@ private:
enum { bmax=M, bpu=1<<shift, mask=bpu-1, units=(bmax+bpu-1)/bpu };
bitu_t bits[units];
public:
bool get(size_t idx) const
{
return bits[idx>>shift]&(1<<(idx&mask));
}
bool get(size_t idx) const { return bits[idx>>shift]&(1<<(idx&mask)); }
void set(size_t idx) { bits[idx>>shift]|= 1<<(idx&mask); }
void reset(size_t idx) { bits[idx>>shift]&=~(1<<(idx&mask)); }
void toggle(size_t idx) { bits[idx>>shift]^= 1<<(idx&mask); }
......@@ -102,86 +100,63 @@ public:
void clear() { memset(bits,0,sizeof(bits)); }
bitmap() { clear(); }
~bitmap() { }
bool empty() const { // naive invert() will break this
for(size_t i=0;i<units;++i)
if(bits[i]) return false;
return true;
}
};
typedef bitmap<libvlc_num_event_types> parent;
typedef bitmap<libvlc_num_event_types> eventtypes_bitmap_t;
class eventtypes_bitmap_t: private bitmap<libvlc_num_event_types> {
class EventObj: private eventtypes_bitmap_t
{
private:
typedef libvlc_event_type_t event_t;
event_t find_event(const char *s) const
{
event_t i;
for(i=0;i<maxbit();++i)
if(!strcmp(s,libvlc_event_type_name(i)))
break;
return i;
}
public:
bool add_event(const eventtypes_bitmap_t &eventBitmap)
{
event_t i;
for(i=0;i<maxbit();++i)
if (eventBitmap.have_event(i))
set(i);
}
bool add_event(const char *s)
{
if (!strcmp(s, "all"))
{
set_all_events();
return true;
}
if (!strcmp(s, "none"))
{
clear();
return true;
}
bool have_event(event_t e) const { return e<maxbit()?get(e):false; }
event_t event = find_event(s);
bool b = event<maxbit();
if(b) set(event);
return b;
}
bool del_event(const char *s)
{
event_t event=find_event(s);
bool b=event<maxbit();
if(b) reset(event);
return b;
}
bool have_event(libvlc_event_type_t event) const
{
return event<maxbit()?get(event):false;
}
void clear()
class Listener: public eventtypes_bitmap_t
{
parent::clear();
}
void set_all_events()
{
event_t i;
for(i=0;i<maxbit();++i)
set(i);
}
};
public:
Listener(event_t e,NPObject *o,bool b): _l(o), _b(b)
{ NPN_RetainObject(o); set(e); }
Listener(): _l(NULL), _b(false) { }
~Listener() { if(_l) NPN_ReleaseObject(_l); }
NPObject *listener() const { return _l; }
bool bubble() const { return _b; }
private:
NPObject *_l;
bool _b;
};
libvlc_event_manager_t *_em;
libvlc_callback_t _cb;
void *_ud;
public:
EventObj(): _em(NULL) { /* deferred to init() */ }
bool init() { return pthread_mutex_init(&mutex, NULL) == 0; }
~EventObj() { pthread_mutex_destroy(&mutex); }
void deliver(NPP browser);
void callback(const libvlc_event_t*);
bool insert(const NPString &, NPObject *, bool);
bool remove(const NPString &, NPObject *, bool);
void unhook_manager();
void hook_manager(libvlc_event_manager_t *,libvlc_callback_t, void *);
private:
event_t find_event(const char *s) const;
typedef std::vector<Listener> lr_l;
typedef std::vector<libvlc_event_type_t> ev_l;
lr_l _llist;
ev_l _elist;
pthread_mutex_t mutex;
// Structure used to represent an EventListener.
// It contains the listener object that will be invoked,
// An Id given by the addEventListener function and sent
// when invoking the listener. Can be anything or nothing.
// The profile associated with the listener used to invoke
// the listener only to some events.
typedef struct s_EventListener
{
NPObject *listener;
NPVariant id;
eventtypes_bitmap_t eventMap;
} EventListener;
bool ask_for_event(event_t e);
void unask_for_event(event_t e);
};
void event_callback(const libvlc_event_t* event, void *param);
class VlcPlugin