gecko-dev/lib/libmocha/lm_plgin.c

1268 lines
34 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*
* JS reflection of Navigator Plugins.
*
* This file contains implementations of several JS objects:
*
* + JSPluginList, a lazily-filled array of all installed
* plug-ins, based on the information exported from lib/plugin
* by the functions in np.h. The plug-in properties in the
* array are named by plug-in name, so you can do an array
* lookup by name, e.g. plugins["Shockwave"].
*
* + JSMIMETypeList, a lazily-filled array of all handled
* MIME types, based in the information exported from libnet
* by the functions in net.h. The type properties in the
* array are named by type, so you can do an array lookup by
* type, e.g. mimetypes["video/quicktime"].
*
* + JSPlugin, the reflection of an installed plug-in, with
* static properties for the plug-in's name, file name, etc.
* and dynamic properties for the MIME types supported by
* the plug-in.
*
* + JSMIMEType, the reflection of an individual MIME type,
* with properties for the type, file extensions, platform-
* specific file type, description of the type, and enabled
* plug-in. The enabled plug-in property is a weak object
* reference marked as JSPROP_BACKEDGE to break the cycle of
* references from plug-in to mime type to plug-in.
*
*/
#include "lm.h"
#include "prmem.h"
#include "np.h"
#include "net.h"
/*
* -----------------------------------------------------------------------
*
* Data types
*
* -----------------------------------------------------------------------
*/
typedef struct JSMIMEType
{
MochaDecoder *decoder;
JSObject *obj;
JSString *type; /* MIME type itself, e.g. "text/html" */
JSString *description; /* English description of MIME type */
JSString *suffixes; /* Comma-separated list of file suffixes */
JSObject *enabledPluginObj; /* Plug-in enabled for this MIME type */
void *fileType; /* Platform-specific file type */
} JSMIMEType;
typedef struct JSPlugin
{
MochaDecoder *decoder;
JSObject *obj;
JSString *name; /* Name of plug-in */
JSString *filename; /* Name of file on disk */
JSString *description; /* Descriptive HTML (version, etc.) */
NPReference plugref; /* Opaque reference to private plugin object */
uint32 length; /* Total number of mime types we have */
XP_Bool reentrant; /* Flag to halt recursion of getProperty */
} JSPlugin;
typedef struct JSPluginList
{
MochaDecoder *decoder;
JSObject *obj;
uint32 length; /* Total number of plug-ins */
XP_Bool reentrant; /* Flag to halt recursion of getProperty */
} JSPluginList;
typedef struct JSMIMETypeList
{
MochaDecoder *decoder;
JSObject *obj;
uint32 length; /* Total number of mime types */
PRPackedBool reentrant; /* Flag to halt recursion of getProperty */
} JSMIMETypeList;
/*
* -----------------------------------------------------------------------
*
* Function protos (all private to this file)
*
* -----------------------------------------------------------------------
*/
static JSMIMEType* mimetype_create_self(JSContext *cx, MochaDecoder* decoder, char* type,
char* description, char** suffixes, uint32 numExtents, void* fileType);
JSBool PR_CALLBACK mimetype_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
void PR_CALLBACK mimetype_finalize(JSContext *cx, JSObject *obj);
static JSPlugin* plugin_create_self(JSContext *cx, MochaDecoder* decoder, char* name,
char* filename, char* description, NPReference plugref);
static JSMIMEType* plugin_create_mimetype(JSContext* cx, JSPlugin* plugin,
XP_Bool byName, const char* targetName, jsint targetSlot);
JSBool PR_CALLBACK plugin_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSBool PR_CALLBACK plugin_resolve_name(JSContext *cx, JSObject *obj, jsval id);
void PR_CALLBACK plugin_finalize(JSContext *cx, JSObject *obj);
static JSPluginList* pluginlist_create_self(JSContext *cx, MochaDecoder* decoder, JSObject* parent_obj);
static JSPlugin* pluginlist_create_plugin(JSContext *cx, JSPluginList *pluginlist,
XP_Bool byName, const char* targetName, jsint targetSlot);
JSBool PR_CALLBACK pluginlist_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSBool PR_CALLBACK pluginlist_resolve_name(JSContext *cx, JSObject *obj, jsval id);
JSBool PR_CALLBACK pluginlist_enumerate(JSContext *cx, JSObject *obj);
void PR_CALLBACK pluginlist_finalize(JSContext *cx, JSObject *obj);
JSBool PR_CALLBACK pluginlist_refresh(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval);
static JSMIMETypeList* mimetypelist_create_self(JSContext *cx, MochaDecoder* decoder);
static JSMIMEType* mimetypelist_create_mimetype(JSContext* cx, JSMIMETypeList* mimetypelist,
XP_Bool byName, const char* targetName, jsint targetSlot);
JSBool PR_CALLBACK mimetypelist_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSBool PR_CALLBACK mimetypelist_resolve_name(JSContext *cx, JSObject *obj, jsval id);
JSBool PR_CALLBACK mimetypelist_enumerate(JSContext *cx, JSObject *obj);
void PR_CALLBACK mimetypelist_finalize(JSContext *cx, JSObject *obj);
/*
* -----------------------------------------------------------------------
*
* Reflection of a MIME type.
*
* -----------------------------------------------------------------------
*/
enum mimetype_slot
{
MIMETYPE_TYPE = -1,
MIMETYPE_DESCRIPTION = -2,
MIMETYPE_SUFFIXES = -3,
MIMETYPE_ENABLEDPLUGIN = -4
};
static JSPropertySpec mimetype_props[] =
{
{"type", MIMETYPE_TYPE, JSPROP_ENUMERATE|JSPROP_READONLY},
{"description", MIMETYPE_DESCRIPTION, JSPROP_ENUMERATE|JSPROP_READONLY},
{"suffixes", MIMETYPE_SUFFIXES, JSPROP_ENUMERATE|JSPROP_READONLY},
{"enabledPlugin", MIMETYPE_ENABLEDPLUGIN, JSPROP_ENUMERATE|JSPROP_READONLY},
{0}
};
static JSClass mimetype_class =
{
"MimeType", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
mimetype_getProperty, mimetype_getProperty, JS_EnumerateStub,
JS_ResolveStub, JS_ConvertStub, mimetype_finalize
};
PR_STATIC_CALLBACK(JSBool)
MimeType(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
/* Constructor method for a JSMIMEType object */
static JSMIMEType*
mimetype_create_self(JSContext *cx, MochaDecoder* decoder,
char* type, char* description, char ** suffixes,
uint32 numExtents, void* fileType)
{
JSObject *obj;
JSMIMEType *mimetype;
mimetype = JS_malloc(cx, sizeof(JSMIMEType));
if (!mimetype)
return NULL;
XP_BZERO(mimetype, sizeof *mimetype);
obj = JS_NewObject(cx, &mimetype_class, NULL, NULL);
if (!obj || !JS_SetPrivate(cx, obj, mimetype)) {
JS_free(cx, mimetype);
return NULL;
}
if (!JS_DefineProperties(cx, obj, mimetype_props))
return NULL;
mimetype->decoder = HOLD_BACK_COUNT(decoder);
mimetype->obj = obj;
mimetype->fileType = fileType;
mimetype->type = JS_NewStringCopyZ(cx, type);
if (!mimetype->type || !JS_LockGCThing(cx, mimetype->type))
return NULL;
mimetype->description = JS_NewStringCopyZ(cx, description);
if (!mimetype->description || !JS_LockGCThing(cx, mimetype->description))
return NULL;
/*
* Assemble the list of file extensions into a string.
* The extensions are stored in an array of individual strings, so we
* first traverse the array to see how big the concatenated string will
* be, then allocate the memory and re-traverse the array to build the
* string. Each extension is seperated by trailing comma and space.
*/
{
uint32 index;
uint32 totalSize = 0;
char *extStr;
if (numExtents > 0)
{
for (index = 0; index < numExtents; index++)
totalSize += XP_STRLEN(suffixes[index]);
/* Add space for ', ' after each extension */
totalSize += (2 * (numExtents - 1));
}
/* Total size plus terminator */
extStr = XP_ALLOC(totalSize + 1);
if (! extStr)
return NULL;
extStr[0] = 0;
for (index = 0; index < numExtents; index++)
{
extStr = strcat(extStr, suffixes[index]);
if (index < numExtents - 1)
extStr = strcat(extStr, ", ");
}
mimetype->suffixes = JS_NewStringCopyZ(cx, extStr);
XP_FREE(extStr);
if (!mimetype->suffixes || !JS_LockGCThing(cx, mimetype->suffixes))
return NULL;
}
/*
* Conjure up the JS object for the plug-in enabled for this
* MIME type. First we get the name of the plug-in from libplugin;
* then we look up the plugin object by name in the global plug-in
* list (it's actually a "resolve", not a "lookup", so that the
* plug-in object will be created if it doesn't already exist).
*/
if (type) {
char* pluginName = NPL_FindPluginEnabledForType(type);
if (pluginName)
{
/* Look up the global plugin list object */
jsval val;
if (JS_LookupProperty(cx, decoder->navigator, "plugins", &val) &&
JSVAL_IS_OBJECT(val))
{
/* Look up the desired plugin by name in the list */
JSObject* pluginListObj = JSVAL_TO_OBJECT(val);
if (JS_GetProperty(cx, pluginListObj, pluginName, &val) &&
JSVAL_IS_OBJECT(val))
{
mimetype->enabledPluginObj = JSVAL_TO_OBJECT(val);
}
}
XP_FREE(pluginName);
}
}
return mimetype;
}
JSBool PR_CALLBACK
mimetype_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSMIMEType *mimetype;
JSString *str;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
mimetype = JS_GetInstancePrivate(cx, obj, &mimetype_class, NULL);
if (!mimetype)
return JS_TRUE;
switch (slot)
{
case MIMETYPE_TYPE:
str = mimetype->type;
break;
case MIMETYPE_DESCRIPTION:
str = mimetype->description;
break;
case MIMETYPE_SUFFIXES:
str = mimetype->suffixes;
break;
case MIMETYPE_ENABLEDPLUGIN:
*vp = OBJECT_TO_JSVAL(mimetype->enabledPluginObj);
return JS_TRUE;
default:
/* don't mess with user-defined props. */
return JS_TRUE;
}
if (str)
*vp = STRING_TO_JSVAL(str);
else
*vp = JS_GetEmptyStringValue(cx);
return JS_TRUE;
}
void PR_CALLBACK
mimetype_finalize(JSContext *cx, JSObject *obj)
{
JSMIMEType *mimetype;
mimetype = JS_GetPrivate(cx, obj);
if (!mimetype)
return;
DROP_BACK_COUNT(mimetype->decoder);
JS_UnlockGCThing(cx, mimetype->type);
JS_UnlockGCThing(cx, mimetype->description);
JS_UnlockGCThing(cx, mimetype->suffixes);
JS_free(cx, mimetype);
}
/*
* -----------------------------------------------------------------------
*
* Reflection of an installed plug-in.
*
* -----------------------------------------------------------------------
*/
enum plugin_slot
{
PLUGIN_NAME = -1,
PLUGIN_FILENAME = -2,
PLUGIN_DESCRIPTION = -3,
PLUGIN_ARRAY_LENGTH = -4
};
static JSPropertySpec plugin_props[] =
{
{"name", PLUGIN_NAME, JSPROP_ENUMERATE | JSPROP_READONLY},
{"filename", PLUGIN_FILENAME, JSPROP_ENUMERATE | JSPROP_READONLY},
{"description", PLUGIN_DESCRIPTION, JSPROP_ENUMERATE | JSPROP_READONLY},
{"length", PLUGIN_ARRAY_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY},
{0}
};
static JSClass plugin_class =
{
"Plugin", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
plugin_getProperty, plugin_getProperty, JS_EnumerateStub,
plugin_resolve_name, JS_ConvertStub, plugin_finalize
};
PR_STATIC_CALLBACK(JSBool)
Plugin(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
/* Constructor method for a JSPlugin object */
static JSPlugin*
plugin_create_self(JSContext *cx, MochaDecoder* decoder, char* name,
char* filename, char* description, NPReference plugref)
{
JSObject *obj;
JSPlugin *plugin;
plugin = JS_malloc(cx, sizeof(JSPlugin));
if (!plugin)
return NULL;
XP_BZERO(plugin, sizeof *plugin);
obj = JS_NewObject(cx, &plugin_class, NULL, NULL);
if (!obj || !JS_SetPrivate(cx, obj, plugin)) {
JS_free(cx, plugin);
return NULL;
}
if (!JS_DefineProperties(cx, obj, plugin_props))
return NULL;
/* Fill out static property fields */
plugin->decoder = HOLD_BACK_COUNT(decoder);
plugin->obj = obj;
plugin->plugref = plugref;
plugin->name = JS_NewStringCopyZ(cx, name);
if (!plugin->name || !JS_LockGCThing(cx, plugin->name))
return NULL;
plugin->filename = JS_NewStringCopyZ(cx, filename);
if (!plugin->filename || !JS_LockGCThing(cx, plugin->filename))
return NULL;
plugin->description = JS_NewStringCopyZ(cx, description);
if (!plugin->description || !JS_LockGCThing(cx, plugin->description))
return NULL;
/* Count how many MIME types we have */
{
NPReference typeref = NPRefFromStart;
while (NPL_IteratePluginTypes(&typeref, plugref, NULL, NULL, NULL, NULL))
plugin->length++;
}
return plugin;
}
/* Create a mimetype property for a plugin for a specified slot or name */
static JSMIMEType*
plugin_create_mimetype(JSContext* cx, JSPlugin* plugin, XP_Bool byName,
const char* targetName, jsint targetSlot)
{
NPMIMEType type = NULL;
char** suffixes = NULL;
char* description = NULL;
void* fileType = NULL;
NPReference typeref = NPRefFromStart;
jsint slot = 0;
XP_Bool found = FALSE;
/* Search for the type (by type name or slot number) */
while (NPL_IteratePluginTypes(&typeref, plugin->plugref, &type,
&suffixes, &description, &fileType))
{
if (byName)
found = (type && (XP_STRCMP(targetName, type) == 0));
else
found = (targetSlot == slot);
if (found)
break;
slot++;
}
/* Found the mime type, so create an object and property */
if (found) {
JSMIMEType* mimetype;
uint32 numExtents = 0;
while (suffixes[numExtents])
numExtents++;
mimetype = mimetype_create_self(cx, plugin->decoder, type, description,
suffixes, numExtents, fileType);
if (mimetype) {
char *name;
jsval val;
name = JS_GetStringBytes(mimetype->type);
val = OBJECT_TO_JSVAL(mimetype->obj);
JS_DefineProperty(cx, plugin->obj, name, val, NULL, NULL,
JSPROP_ENUMERATE | JSPROP_READONLY);
JS_AliasElement(cx, plugin->obj, name, slot);
}
return mimetype;
}
return NULL;
}
JSBool PR_CALLBACK
plugin_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSPlugin *plugin;
JSString *str;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
plugin = JS_GetInstancePrivate(cx, obj, &plugin_class, NULL);
if (!plugin)
return JS_TRUE;
/* Prevent infinite recursion through call to GetSlot below */
if (plugin->reentrant)
return JS_TRUE;
switch (slot)
{
case PLUGIN_NAME:
str = plugin->name;
break;
case PLUGIN_FILENAME:
str = plugin->filename;
break;
case PLUGIN_DESCRIPTION:
str = plugin->description;
break;
case PLUGIN_ARRAY_LENGTH:
*vp = INT_TO_JSVAL(plugin->length);
return JS_TRUE;
default:
/* Don't mess with a user-defined property. */
if (slot >= 0 && slot < (jsint) plugin->length) {
/* Search for an existing JSMIMEType for this slot */
JSObject* mimetypeObj = NULL;
jsval val;
plugin->reentrant = TRUE;
if (JS_GetElement(cx, obj, slot, &val) && JSVAL_IS_OBJECT(val))
mimetypeObj = JSVAL_TO_OBJECT(val);
else {
JSMIMEType* mimetype;
mimetype = plugin_create_mimetype(cx, plugin, FALSE, NULL, slot);
if (mimetype)
mimetypeObj = mimetype->obj;
}
plugin->reentrant = FALSE;
*vp = OBJECT_TO_JSVAL(mimetypeObj);
return JS_TRUE;
}
return JS_FALSE;
}
if (str)
*vp = STRING_TO_JSVAL(str);
else
*vp = JS_GetEmptyStringValue(cx);
return JS_TRUE;
}
JSBool PR_CALLBACK
plugin_resolve_name(JSContext *cx, JSObject *obj, jsval id)
{
JSPlugin* plugin;
jsval val;
const char * name;
if (!JSVAL_IS_STRING(id))
return JS_TRUE;
name = JS_GetStringBytes(JSVAL_TO_STRING(id));
plugin = JS_GetPrivate(cx, obj);
if (!plugin)
return JS_TRUE;
/* Prevent infinite recursion through call to LookupProperty below */
if (plugin->reentrant)
return JS_TRUE;
/* Look for a JSMIMEType object by this name already in our list */
plugin->reentrant = TRUE;
if (JS_LookupProperty(cx, obj, name, &val) && JSVAL_IS_OBJECT(val)) {
plugin->reentrant = FALSE;
return JS_TRUE;
}
plugin->reentrant = FALSE;
/* We don't already have the object, so make a new one */
if (plugin_create_mimetype(cx, plugin, TRUE, name, 0))
return JS_TRUE;
/* Still no match for the name? Must be some other property, or invalid. */
return JS_TRUE;
}
void PR_CALLBACK
plugin_finalize(JSContext *cx, JSObject *obj)
{
JSPlugin* plugin;
plugin = JS_GetPrivate(cx, obj);
if (!plugin)
return;
DROP_BACK_COUNT(plugin->decoder);
JS_UnlockGCThing(cx, plugin->name);
JS_UnlockGCThing(cx, plugin->filename);
JS_UnlockGCThing(cx, plugin->description);
JS_free(cx, plugin);
}
/*
* -----------------------------------------------------------------------
*
* Reflection of the list of installed plug-ins.
* The only static property is the array length;
* the array elements (JSPlugins) are added
* lazily when referenced.
*
* -----------------------------------------------------------------------
*/
enum pluginlist_slot
{
PLUGINLIST_ARRAY_LENGTH = -1
};
static JSPropertySpec pluginlist_props[] =
{
{"length", PLUGINLIST_ARRAY_LENGTH, JSPROP_READONLY},
{0}
};
static JSClass pluginlist_class =
{
"PluginArray", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
pluginlist_getProperty, pluginlist_getProperty, pluginlist_enumerate,
pluginlist_resolve_name, JS_ConvertStub, pluginlist_finalize
};
static JSFunctionSpec pluginlist_methods[] =
{
{"refresh", pluginlist_refresh, 0},
{0}
};
PR_STATIC_CALLBACK(JSBool)
PluginList(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
/* Constructor method for a JSPluginList object */
static JSPluginList*
pluginlist_create_self(JSContext *cx, MochaDecoder* decoder, JSObject* parent_obj)
{
JSObject *obj;
JSPluginList *pluginlist;
pluginlist = JS_malloc(cx, sizeof(JSPluginList));
if (!pluginlist)
return NULL;
XP_BZERO(pluginlist, sizeof *pluginlist);
obj = JS_InitClass(cx, parent_obj, NULL, &pluginlist_class,
PluginList, 0, pluginlist_props,
pluginlist_methods, NULL, NULL);
if (!obj || !JS_SetPrivate(cx, obj, pluginlist)) {
JS_free(cx, pluginlist);
return NULL;
}
pluginlist->decoder = HOLD_BACK_COUNT(decoder);
pluginlist->obj = obj;
{
/* Compute total number of plug-ins (potential slots) */
NPReference ref = NPRefFromStart;
while (NPL_IteratePluginFiles(&ref, NULL, NULL, NULL))
pluginlist->length++;
}
return pluginlist;
}
/* Look up the plugin for the specified slot of the plug-in list */
static JSPlugin*
pluginlist_create_plugin(JSContext *cx, JSPluginList *pluginlist, XP_Bool byName,
const char* targetName, jsint targetSlot)
{
char* plugname = NULL;
char* filename = NULL;
char* description = NULL;
NPReference plugref = NPRefFromStart;
jsint slot = 0;
XP_Bool found = FALSE;
/* Search for the plug-in (by name or slot number) */
while (NPL_IteratePluginFiles(&plugref, &plugname, &filename, &description)) {
if (byName)
found = (plugname && (XP_STRCMP(targetName, plugname) == 0));
else
found = (targetSlot == slot);
if (found)
break;
slot++;
}
/* Found the plug-in, so create an object and property */
if (found) {
JSPlugin* plugin;
plugin = plugin_create_self(cx, pluginlist->decoder, plugname,
filename, description, plugref);
if (plugin) {
char *name;
jsval val;
name = JS_GetStringBytes(plugin->name);
val = OBJECT_TO_JSVAL(plugin->obj);
JS_DefineProperty(cx, pluginlist->obj, name, val, NULL, NULL,
JSPROP_ENUMERATE | JSPROP_READONLY);
JS_AliasElement(cx, pluginlist->obj, name, slot);
}
return plugin;
}
return NULL;
}
JSBool PR_CALLBACK
pluginlist_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSPluginList *pluginlist;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
pluginlist = JS_GetInstancePrivate(cx, obj, &pluginlist_class, NULL);
if (!pluginlist)
return JS_TRUE;
/* Prevent infinite recursion through call to GetSlot below */
if (pluginlist->reentrant)
return JS_TRUE;
switch (slot) {
case PLUGINLIST_ARRAY_LENGTH:
*vp = INT_TO_JSVAL(pluginlist->length);
break;
default:
/* Don't mess with a user-defined property. */
if (slot >= 0 && slot < (jsint) pluginlist->length) {
/* Search for an existing JSPlugin for this slot */
JSObject* pluginObj = NULL;
jsval val;
pluginlist->reentrant = TRUE;
if (JS_GetElement(cx, obj, slot, &val) && JSVAL_IS_OBJECT(val)) {
pluginObj = JSVAL_TO_OBJECT(val);
}
else {
JSPlugin* plugin;
plugin = pluginlist_create_plugin(cx, pluginlist, FALSE, NULL, slot);
if (plugin)
pluginObj = plugin->obj;
}
pluginlist->reentrant = FALSE;
*vp = OBJECT_TO_JSVAL(pluginObj);
return JS_TRUE;
}
}
return JS_TRUE;
}
JSBool PR_CALLBACK
pluginlist_resolve_name(JSContext *cx, JSObject *obj, jsval id)
{
JSPluginList* pluginlist;
jsval val;
const char * name;
if (!JSVAL_IS_STRING(id))
return JS_TRUE;
name = JS_GetStringBytes(JSVAL_TO_STRING(id));
pluginlist = JS_GetPrivate(cx, obj);
if (!pluginlist)
return JS_TRUE;
/* Prevent infinite recursion through call to LookupProperty below */
if (pluginlist->reentrant)
return JS_TRUE;
/* Look for a JSMIMEType object by this name already in our list */
pluginlist->reentrant = TRUE;
if (JS_LookupProperty(cx, obj, name, &val) && JSVAL_IS_OBJECT(val))
{
pluginlist->reentrant = FALSE;
return JS_TRUE;
}
pluginlist->reentrant = FALSE;
/* We don't already have the object, so make a new one */
if (pluginlist_create_plugin(cx, pluginlist, TRUE, name, 0))
return JS_TRUE;
/* Still no match for the name? Must be some other property, or invalid. */
return JS_TRUE;
}
JSBool PR_CALLBACK
pluginlist_enumerate(JSContext *cx, JSObject *obj)
{
return JS_TRUE;
}
void PR_CALLBACK
pluginlist_finalize(JSContext *cx, JSObject *obj)
{
JSPluginList* pluginlist;
pluginlist = JS_GetPrivate(cx, obj);
if (!pluginlist)
return;
DROP_BACK_COUNT(pluginlist->decoder);
JS_free(cx, pluginlist);
}
JSBool PR_CALLBACK
pluginlist_refresh(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSBool value = JS_FALSE;
JSPluginList* pluginlist;
JSObject* navigator;
MochaDecoder* decoder;
MochaDecoder* crippled_decoder;
NPError error = NPERR_NO_ERROR;
if (!(pluginlist = JS_GetInstancePrivate(cx, obj, &pluginlist_class, argv)))
return JS_FALSE;
decoder = pluginlist->decoder;
/*
* Evaluate the parameter (if any). If the parameter
* is missing or can't be evaluated, default to FALSE.
*/
if (argc > 0)
(void) JS_ValueToBoolean(cx, argv[0], &value);
/* Synchronously execute this function on the Mozilla thread */
error = (NPError) ET_npl_RefreshPluginList(decoder->window_context, value);
if (error != NPERR_NO_ERROR)
{
/* XXX should JS_ReportError() here, but can't happen currently */
return JS_FALSE;
}
/*
* Remove references to the existing navigator object,
* and make a new one. If scripts have outstanding
* references to the old objects, they'll still be
* valid, but if they reference navigator.plugins
* anew they'll see any new plug-ins registered by
* NPL_RefreshPlugins.
*/
navigator = decoder->navigator;
decoder->navigator = NULL; /* Prevent lm_DefineNavigator from short-circuiting */
crippled_decoder = LM_GetCrippledDecoder();
crippled_decoder->navigator = NULL;
crippled_decoder->navigator = lm_DefineNavigator(decoder);
if (!decoder->navigator)
{
/*
* We failed to create a new navigator object, so
* restore the old one (saved in a local variable).
*/
decoder->navigator = navigator;
return JS_FALSE;
}
return JS_TRUE;
}
/*
* -----------------------------------------------------------------------
*
* Reflection of the list of handled MIME types.
*
* -----------------------------------------------------------------------
*/
enum mimetypelist_slot
{
MIMETYPELIST_ARRAY_LENGTH = -1
};
static JSPropertySpec mimetypelist_props[] =
{
{"length", MIMETYPELIST_ARRAY_LENGTH, JSPROP_READONLY},
{0}
};
static JSClass mimetypelist_class =
{
"MimeTypeArray", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
mimetypelist_getProperty, mimetypelist_getProperty, mimetypelist_enumerate,
mimetypelist_resolve_name, JS_ConvertStub, mimetypelist_finalize
};
/* Constructor method for a JSMIMETypeList object */
static JSMIMETypeList*
mimetypelist_create_self(JSContext *cx, MochaDecoder* decoder)
{
JSObject *obj;
JSMIMETypeList *mimetypelist;
mimetypelist = JS_malloc(cx, sizeof(JSMIMETypeList));
if (!mimetypelist)
return NULL;
XP_BZERO(mimetypelist, sizeof *mimetypelist);
obj = JS_NewObject(cx, &mimetypelist_class, NULL, NULL);
if (!obj || !JS_SetPrivate(cx, obj, mimetypelist)) {
JS_free(cx, mimetypelist);
return NULL;
}
if (!JS_DefineProperties(cx, obj, mimetypelist_props))
return NULL;
mimetypelist->decoder = HOLD_BACK_COUNT(decoder);
mimetypelist->obj = obj;
/*
* Count the number of types in the list.
* We can't just go by the number of items
* in the list, since it contains encodings, too.
*/
{
XP_List* cinfoList = cinfo_MasterListPointer();
NET_cdataStruct* cdata = NULL;
mimetypelist->length = 0;
while (cinfoList)
{
cdata = cinfoList->object;
if (cdata && cdata->ci.type)
mimetypelist->length++;
cinfoList = cinfoList->next;
}
}
return mimetypelist;
}
/*
* Common code to take a cdata and create a mimetype
*/
static JSMIMEType*
define_mimetype(JSContext* cx, JSMIMETypeList* mimetypelist,
NET_cdataStruct* cdata, jsint targetSlot)
{
JSMIMEType* mimetype;
mimetype = mimetype_create_self(cx, mimetypelist->decoder, cdata->ci.type,
cdata->ci.desc, cdata->exts, cdata->num_exts, NULL);
if (mimetype) {
char *name;
name = JS_GetStringBytes(mimetype->type);
JS_DefineProperty(cx, mimetypelist->obj, name,
OBJECT_TO_JSVAL(mimetype->obj), NULL, NULL,
JSPROP_ENUMERATE | JSPROP_READONLY);
JS_AliasElement(cx, mimetypelist->obj, name, targetSlot);
}
return mimetype;
}
/* Create a mimetype property for a specified slot or name */
static JSMIMEType*
mimetypelist_create_mimetype(JSContext* cx, JSMIMETypeList* mimetypelist,
XP_Bool byName, const char* targetName, jsint targetSlot)
{
XP_List* cinfoList = cinfo_MasterListPointer();
NET_cdataStruct* cdata = NULL;
XP_Bool found = FALSE;
if (byName) {
/* Look up by name */
targetSlot = 0;
while (cinfoList) {
cdata = cinfoList->object;
if (cdata) {
char* type = cdata->ci.type;
if (type && (XP_STRCMP(type, targetName) == 0)) {
found = TRUE;
break;
}
}
targetSlot++;
cinfoList = cinfoList->next;
}
}
else {
/*
* Look up by slot.
* The slot number does not directly correspond to list index,
* since not all items in the list correspond to properties
* (encodings are in list list as well as types).
*/
uint32 count = targetSlot + 1;
while (cinfoList) {
cdata = cinfoList->object;
if (cdata && cdata->ci.type)
count--;
if (count == 0) {
found = TRUE;
break;
}
cinfoList = cinfoList->next;
}
}
if (found)
return define_mimetype(cx, mimetypelist, cdata, targetSlot);
return NULL;
}
JSBool PR_CALLBACK
mimetypelist_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSMIMETypeList *mimetypelist;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
mimetypelist = JS_GetInstancePrivate(cx, obj, &mimetypelist_class, NULL);
if (!mimetypelist)
return JS_TRUE;
/* Prevent infinite recursion through call to GetSlot below */
if (mimetypelist->reentrant)
return JS_TRUE;
switch (slot)
{
case MIMETYPELIST_ARRAY_LENGTH:
*vp = INT_TO_JSVAL(mimetypelist->length);
break;
default:
/* Don't mess with a user-defined property. */
if (slot >= 0 && slot < (jsint) mimetypelist->length) {
/* Search for an existing JSMIMEType for this slot */
JSObject* mimetypeObj = NULL;
jsval val = JSVAL_VOID;
mimetypelist->reentrant = TRUE;
if (JS_GetElement(cx, obj, slot, &val) && JSVAL_IS_OBJECT(val)) {
mimetypeObj = JSVAL_TO_OBJECT(val);
}
else {
JSMIMEType* mimetype;
mimetype = mimetypelist_create_mimetype(cx, mimetypelist, FALSE, NULL, slot);
if (mimetype)
mimetypeObj = mimetype->obj;
}
mimetypelist->reentrant = FALSE;
*vp = OBJECT_TO_JSVAL(mimetypeObj);
return JS_TRUE;
}
break;
}
return JS_TRUE;
}
JSBool PR_CALLBACK
mimetypelist_resolve_name(JSContext *cx, JSObject *obj, jsval id)
{
JSMIMETypeList* mimetypelist;
jsval val;
const char * name;
if (!JSVAL_IS_STRING(id))
return JS_TRUE;
name = JS_GetStringBytes(JSVAL_TO_STRING(id));
mimetypelist = JS_GetPrivate(cx, obj);
if (!mimetypelist)
return JS_TRUE;
/* Prevent infinite recursion through call to LookupProperty below */
if (mimetypelist->reentrant)
return JS_TRUE;
/* Look for a JSMIMEType object by this name already in our list */
mimetypelist->reentrant = TRUE;
if (JS_LookupProperty(cx, obj, name, &val) && JSVAL_IS_OBJECT(val))
{
mimetypelist->reentrant = FALSE;
return JS_TRUE;
}
mimetypelist->reentrant = FALSE;
/* We don't already have the object, so make a new one */
if (mimetypelist_create_mimetype(cx, mimetypelist, TRUE, name, 0))
return JS_TRUE;
/* Still no match for the name? Must be some other property, or invalid. */
return JS_TRUE;
}
JSBool PR_CALLBACK
mimetypelist_enumerate(JSContext *cx, JSObject *obj)
{
JSMIMETypeList* mimetypelist;
XP_List* cinfoList = cinfo_MasterListPointer();
NET_cdataStruct* cdata = NULL;
jsint targetSlot = 0;
mimetypelist = JS_GetPrivate(cx, obj);
if (!mimetypelist)
return JS_TRUE;
while (cinfoList) {
cdata = cinfoList->object;
if (cdata)
define_mimetype(cx, mimetypelist, cdata, targetSlot++);
cinfoList = cinfoList->next;
}
return JS_TRUE;
}
void PR_CALLBACK
mimetypelist_finalize(JSContext *cx, JSObject *obj)
{
JSMIMETypeList *mimetypelist;
mimetypelist = JS_GetPrivate(cx, obj);
if (!mimetypelist)
return;
DROP_BACK_COUNT(mimetypelist->decoder);
JS_free(cx, mimetypelist);
}
/*
* Exported entry point, called from lm_nav.c.
* This function creates the JSPluginList object. The
* properties for the installed plug-ins are instantiated
* lazily in pluginlist_resolve_name.
*/
JSObject*
lm_NewPluginList(JSContext *cx, JSObject *parent_obj)
{
MochaDecoder* decoder;
JSPluginList* pluginlist;
decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
pluginlist = pluginlist_create_self(cx, decoder, parent_obj);
return (pluginlist ? pluginlist->obj : NULL);
}
/*
* Exported entry point to this file, called from lm_nav.c.
* This function creates the JSMIMETypeList object. The
* properties for the MIME types are instantiated
* lazily in mimetypelist_resolve_name.
*/
JSObject*
lm_NewMIMETypeList(JSContext *cx)
{
MochaDecoder* decoder;
JSMIMETypeList* mimetypelist;
decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
mimetypelist = mimetypelist_create_self(cx, decoder);
return (mimetypelist ? mimetypelist->obj : NULL);
}
JSBool
lm_DefinePluginClasses(MochaDecoder *decoder)
{
JSContext *cx = decoder->js_context;
if (!JS_InitClass(cx, decoder->window_object, NULL, &mimetype_class,
MimeType, 0, mimetype_props, NULL, NULL, NULL))
return JS_FALSE;
if (!JS_InitClass(cx, decoder->window_object, NULL, &plugin_class,
Plugin, 0, plugin_props, NULL, NULL, NULL))
return JS_FALSE;
return JS_TRUE;
}