mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
570 lines
20 KiB
C
570 lines
20 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla 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/MPL/
|
|
*
|
|
* 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 Communicator client code, released
|
|
* March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
|
|
*
|
|
* It contains the native code implementation of the JavaPackage class.
|
|
*
|
|
* A JavaPackage is JavaScript's representation of a Java package. The
|
|
* JavaPackage object contains only a string, which is the path to the package,
|
|
* e.g. "java/lang". The JS properties of a JavaPackage are either nested packages
|
|
* or a JavaClass object, which represents the path to a Java class.
|
|
*
|
|
* Note that there is no equivalent to a JavaPackage object in Java. Example:
|
|
* Although there are instances of java.lang.String and there are static methods
|
|
* of java.lang.String that can be invoked, there's no such thing as a java.lang
|
|
* object in Java that exists at run time.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "jsj_private.h" /* LiveConnect internals */
|
|
#include "jsjava.h"
|
|
|
|
|
|
JSClass JavaPackage_class; /* Forward declaration */
|
|
|
|
/*
|
|
* The native part of a JavaPackage object. It gets stored in the object's
|
|
* private slot.
|
|
*/
|
|
typedef struct {
|
|
const char * path; /* e.g. "java/lang" or NULL if top level package */
|
|
int flags; /* e.g. PKG_SYSTEM, PKG_CLASS */
|
|
} JavaPackage_Private;
|
|
|
|
static JSObject *
|
|
define_JavaPackage(JSContext *cx, JSObject *parent_obj,
|
|
const char *obj_name, const char *path, int flags, int access)
|
|
{
|
|
JSObject *package_obj, *obj;
|
|
JavaPackage_Private *package;
|
|
jsval v;
|
|
|
|
/*
|
|
* Expose the same JSObject for Packages.java and java.
|
|
* "java" will be defined during the java package initialization stage.
|
|
* "Packages.java" will be lazily resolved by JavaPackage_resolve.
|
|
* Ditto for sun and netscape.
|
|
* See bugzilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=248409.
|
|
*/
|
|
if (!strcmp(obj_name, path) &&
|
|
(obj = JS_GetParent(cx, parent_obj)) &&
|
|
JS_LookupProperty(cx, obj, obj_name, &v) &&
|
|
!JSVAL_IS_PRIMITIVE(v))
|
|
{
|
|
if (!JS_DefineProperty(cx, parent_obj, obj_name, v, NULL, NULL,
|
|
JSPROP_PERMANENT | access)) {
|
|
return NULL;
|
|
}
|
|
|
|
package_obj = JSVAL_TO_OBJECT(v);
|
|
return package_obj;
|
|
}
|
|
|
|
package_obj = JS_DefineObject(cx, parent_obj, obj_name, &JavaPackage_class,
|
|
0, JSPROP_PERMANENT | access);
|
|
|
|
if (!package_obj)
|
|
return NULL;
|
|
|
|
/* Attach private, native data to the JS object */
|
|
package = (JavaPackage_Private *)JS_malloc(cx, sizeof(JavaPackage_Private));
|
|
if (!package) {
|
|
JS_DeleteProperty(cx, parent_obj, obj_name);
|
|
return NULL;
|
|
}
|
|
JS_SetPrivate(cx, package_obj, (void *)package);
|
|
if (path)
|
|
package->path = JS_strdup(cx, path);
|
|
else
|
|
package->path = "";
|
|
|
|
package->flags = flags;
|
|
|
|
/* Check for OOM */
|
|
if (!package->path) {
|
|
JS_DeleteProperty(cx, parent_obj, obj_name);
|
|
JS_free(cx, package);
|
|
return NULL;
|
|
}
|
|
|
|
return package_obj;
|
|
}
|
|
|
|
/* JavaPackage uses standard JS getProperty */
|
|
|
|
/*
|
|
* Don't allow user-defined properties to be set on Java package objects, e.g.
|
|
* it is illegal to write "java.lang.myProperty = 4". We probably could relax
|
|
* this restriction, but it's potentially confusing and not clearly useful.
|
|
*/
|
|
static JSBool
|
|
JavaPackage_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
|
|
{
|
|
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
|
|
if (!package) {
|
|
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
|
|
JSJMSG_BAD_ADD_TO_PACKAGE);
|
|
return JS_FALSE;
|
|
}
|
|
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
|
|
JSJMSG_DONT_ADD_TO_PACKAGE);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
static JSBool quiet_resolve_failure;
|
|
|
|
/*
|
|
* Resolve a component name to be either the name of a class or a package.
|
|
*/
|
|
static JSBool
|
|
JavaPackage_resolve(JSContext *cx, JSObject *obj, jsval id)
|
|
{
|
|
JavaPackage_Private *package;
|
|
JSBool ok = JS_TRUE;
|
|
jclass jclazz;
|
|
char *subPath, *newPath;
|
|
const char *path;
|
|
JNIEnv *jEnv;
|
|
JSJavaThreadState *jsj_env;
|
|
|
|
/* Painful hack for pre_define_java_packages() */
|
|
if (quiet_resolve_failure)
|
|
return JS_FALSE;
|
|
|
|
package = (JavaPackage_Private *)JS_GetPrivate(cx, obj);
|
|
if (!package)
|
|
return JS_TRUE;
|
|
|
|
if (!JSVAL_IS_STRING(id))
|
|
return JS_TRUE;
|
|
subPath = JS_GetStringBytes(JSVAL_TO_STRING(id));
|
|
|
|
/*
|
|
* There will be an attempt to invoke the toString() method when producing
|
|
* the string representation of a JavaPackage. When this occurs, avoid
|
|
* creating a bogus toString package. (This means that no one can ever
|
|
* create a package with the simple name "toString", but we'll live with
|
|
* that limitation.)
|
|
*/
|
|
if (!strcmp(subPath, "toString"))
|
|
return JS_FALSE;
|
|
|
|
path = package->path;
|
|
newPath = JS_smprintf("%s%s%s", path, (path[0] ? "/" : ""), subPath);
|
|
if (!newPath) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
jsj_env = jsj_EnterJava(cx, &jEnv);
|
|
if (!jEnv) {
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
Unfortunately, Java provides no way to find out whether a particular
|
|
name is a package or not. The only way to tell is to try to load the
|
|
name as a class file and, if that fails, assume it's a package. This
|
|
makes things work as expected for the most part, but it has three
|
|
noticeable problems that keep coming up:
|
|
|
|
- You can refer to a package like java.lang.i.buried.paul without
|
|
generating a complaint. Of course, you'll never be able to refer to
|
|
any classes through it.
|
|
|
|
- An annoying consequence of the above is that misspelling a class name
|
|
results in a cryptic error about packages.
|
|
|
|
- In a browser context, i.e. where applets are involved, figuring out
|
|
whether something is a class may require looking for it over the net
|
|
using the current classloader. This means that the first time you
|
|
refer to java.lang.System in a js context, there will be an attempt
|
|
to search for [[DOCBASE]]/java.class on the server.
|
|
|
|
A solution is to explicitly tell jsjava the names of all the (local)
|
|
packages on the CLASSPATH. (Not implemented yet.)
|
|
|
|
*/
|
|
jclazz = (*jEnv)->FindClass(jEnv, newPath);
|
|
if (jclazz) {
|
|
JSObject *newClass;
|
|
|
|
newClass = jsj_define_JavaClass(cx, jEnv, obj, subPath, jclazz);
|
|
(*jEnv)->DeleteLocalRef(jEnv, jclazz);
|
|
if (!newClass) {
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
} else {
|
|
|
|
/* We assume that any failed attempt to load a class is because it
|
|
doesn't exist. If we wanted to do a better job, we would check
|
|
the exception type and make sure that it's NoClassDefFoundError */
|
|
(*jEnv)->ExceptionClear(jEnv);
|
|
|
|
/*
|
|
* If there's no class of the given name, then we must be referring to
|
|
* a package. However, don't allow bogus sub-packages of pre-defined
|
|
* system packages to be created.
|
|
*/
|
|
if (JS_InstanceOf(cx, obj, &JavaPackage_class, NULL)) {
|
|
JavaPackage_Private *current_package;
|
|
|
|
current_package = JS_GetPrivate(cx, obj);
|
|
if (current_package->flags & PKG_SYSTEM) {
|
|
char *msg, *cp;
|
|
msg = JS_strdup(cx, newPath);
|
|
|
|
/* Check for OOM */
|
|
if (msg) {
|
|
/* Convert package of form "java/lang" to "java.lang" */
|
|
for (cp = msg; *cp != '\0'; cp++)
|
|
if (*cp == '/')
|
|
*cp = '.';
|
|
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
|
|
JSJMSG_MISSING_PACKAGE, msg);
|
|
free((char*)msg);
|
|
}
|
|
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (!define_JavaPackage(cx, obj, subPath, newPath, 0, JSPROP_READONLY)) {
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/* printf("JavaPackage \'%s\' created\n", newPath); */
|
|
#endif
|
|
|
|
}
|
|
|
|
out:
|
|
JS_smprintf_free(newPath);
|
|
jsj_ExitJava(jsj_env);
|
|
return ok;
|
|
}
|
|
|
|
static JSBool
|
|
JavaPackage_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
|
{
|
|
JSString *str;
|
|
char *name, *cp;
|
|
|
|
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
|
|
if (!package) {
|
|
fprintf(stderr, "JavaPackage_resolve: no private data!\n");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
switch (type) {
|
|
|
|
/* Pretty-printing of JavaPackage */
|
|
case JSTYPE_VOID: /* Default value */
|
|
case JSTYPE_NUMBER:
|
|
case JSTYPE_STRING:
|
|
/* Convert '/' to '.' so that it looks like Java language syntax. */
|
|
if (!package->path)
|
|
break;
|
|
name = JS_smprintf("[JavaPackage %s]", package->path);
|
|
if (!name) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
for (cp = name; *cp != '\0'; cp++)
|
|
if (*cp == '/')
|
|
*cp = '.';
|
|
str = JS_NewString(cx, name, strlen(name));
|
|
if (!str) {
|
|
JS_smprintf_free(name);
|
|
/* It's not necessary to call JS_ReportOutOfMemory(), as
|
|
JS_NewString() will do so on failure. */
|
|
return JS_FALSE;
|
|
}
|
|
|
|
*vp = STRING_TO_JSVAL(str);
|
|
break;
|
|
|
|
case JSTYPE_OBJECT:
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Free the private native data associated with the JavaPackage object.
|
|
*/
|
|
static void
|
|
JavaPackage_finalize(JSContext *cx, JSObject *obj)
|
|
{
|
|
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
|
|
if (!package)
|
|
return;
|
|
|
|
if (package->path)
|
|
JS_free(cx, (char *)package->path);
|
|
JS_free(cx, package);
|
|
}
|
|
|
|
/*
|
|
* The definition of the JavaPackage class
|
|
*/
|
|
JSClass JavaPackage_class = {
|
|
"JavaPackage", JSCLASS_HAS_PRIVATE,
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JavaPackage_setProperty,
|
|
JS_EnumerateStub, JavaPackage_resolve,
|
|
JavaPackage_convert, JavaPackage_finalize,
|
|
|
|
/* Optionally non-null members start here. */
|
|
NULL, /* getObjectOps */
|
|
NULL, /* checkAccess */
|
|
NULL, /* call */
|
|
NULL, /* construct */
|
|
NULL, /* xdrObject */
|
|
NULL, /* hasInstance */
|
|
NULL, /* mark */
|
|
0, /* spare */
|
|
};
|
|
|
|
JavaPackageDef
|
|
standard_java_packages[] = {
|
|
{"java", NULL, PKG_USER, 0},
|
|
{"java.applet", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.awt", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.awt.datatransfer",
|
|
NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.awt.event", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.awt.image", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.awt.peer", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.beans", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.io", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.lang", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.lang.reflect", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.math", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.net", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.rmi", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.rmi.dgc", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.rmi.user", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.rmi.registry", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.rmi.server", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.security", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.security.acl", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.security.interfaces",
|
|
NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.sql", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.text", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.text.resources", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"java.util", NULL, PKG_USER, JSPROP_READONLY},
|
|
{"java.util.zip", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
|
|
{"netscape", NULL, PKG_USER, 0},
|
|
{"netscape.applet", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.application",NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.debug", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.javascript", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.ldap", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.misc", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.net", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.plugin", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.util", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.secfile", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.security", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
{"netscape.WAI", NULL, PKG_SYSTEM, JSPROP_READONLY},
|
|
|
|
{"sun", NULL, PKG_USER, 0},
|
|
{"Packages", "", PKG_USER, JSPROP_READONLY},
|
|
|
|
{NULL, NULL, 0, 0}
|
|
};
|
|
|
|
/*
|
|
* On systems which provide strtok_r we'll use that function to avoid
|
|
* problems with non-thread-safety.
|
|
*/
|
|
#if HAVE_STRTOK_R
|
|
# define STRTOK_1ST(str, seps, res) strtok_r (str, seps, &res)
|
|
# define STRTOK_OTHER(seps, res) strtok_r (res, seps, &res)
|
|
#else
|
|
# define STRTOK_1ST(str, seps, res) strtok (str, seps)
|
|
# define STRTOK_OTHER(seps, res) strtok (NULL, seps)
|
|
#endif
|
|
|
|
/*
|
|
* Pre-define a hierarchy of JavaPackage objects.
|
|
* Pre-defining a Java package at initialization time is not necessary, but
|
|
* it will make package lookup faster and, more importantly, will avoid
|
|
* unnecessary network accesses if classes are being loaded over the network.
|
|
*/
|
|
static JSBool
|
|
pre_define_java_packages(JSContext *cx, JSObject *global_obj,
|
|
JavaPackageDef *predefined_packages)
|
|
{
|
|
JSBool package_exists;
|
|
JSObject *parent_obj;
|
|
JavaPackageDef *package_def;
|
|
char *simple_name, *cp, *package_name, *path;
|
|
int flags;
|
|
|
|
if (!predefined_packages)
|
|
return JS_TRUE;
|
|
|
|
/* Iterate over all pre-defined Java packages */
|
|
for (package_def = predefined_packages; package_def->name; package_def++) {
|
|
#if HAVE_STRTOK_R
|
|
char *nextstr;
|
|
#endif
|
|
package_name = path = NULL;
|
|
|
|
parent_obj = global_obj;
|
|
package_name = strdup(package_def->name);
|
|
if (!package_name)
|
|
goto out_of_memory;
|
|
|
|
/* Walk the chain of JavaPackage objects to get to the parent of the
|
|
rightmost sub-package in the fully-qualified package name. */
|
|
for (simple_name = STRTOK_1ST(package_name, ".", nextstr); simple_name; simple_name = STRTOK_OTHER(".", nextstr)) {
|
|
jsval v;
|
|
|
|
/* Check to see if the sub-package already exists */
|
|
quiet_resolve_failure = JS_TRUE;
|
|
package_exists = JS_LookupProperty(cx, parent_obj, simple_name, &v) && JSVAL_IS_OBJECT(v);
|
|
quiet_resolve_failure = JS_FALSE;
|
|
|
|
if (package_exists) {
|
|
parent_obj = JSVAL_TO_OBJECT(v);
|
|
continue;
|
|
}
|
|
|
|
/* New package objects should only be created at the terminal
|
|
sub-package in a fully-qualified package-name */
|
|
if (STRTOK_OTHER(".", nextstr)) {
|
|
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
|
|
JSJMSG_BAD_PACKAGE_PREDEF,
|
|
package_def->name);
|
|
goto error;
|
|
}
|
|
|
|
if (package_def->path) {
|
|
path = strdup(package_def->path);
|
|
if (!path)
|
|
goto out_of_memory;
|
|
} else {
|
|
/*
|
|
* The default path is specified, so create it from the
|
|
* fully-qualified package name.
|
|
*/
|
|
path = strdup(package_def->name);
|
|
if (!path)
|
|
goto out_of_memory;
|
|
/* Transform package name, e.g. "java.lang" ==> "java/lang" */
|
|
for (cp = path; *cp != '\0'; cp++) {
|
|
if (*cp == '.')
|
|
*cp = '/';
|
|
}
|
|
}
|
|
flags = package_def->flags;
|
|
parent_obj = define_JavaPackage(cx, parent_obj, simple_name, path, flags,
|
|
package_def->access);
|
|
if (!parent_obj)
|
|
goto error;
|
|
|
|
free(path);
|
|
break;
|
|
}
|
|
free(package_name);
|
|
}
|
|
return JS_TRUE;
|
|
|
|
out_of_memory:
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
error:
|
|
JS_FREE_IF(cx, package_name);
|
|
JS_FREE_IF(cx, path);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
static JSBool
|
|
JavaPackage_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
jsval *rval)
|
|
{
|
|
if (!JS_InstanceOf(cx, obj, &JavaPackage_class, argv))
|
|
return JS_FALSE;
|
|
return JavaPackage_convert(cx, obj, JSTYPE_STRING, rval);
|
|
}
|
|
|
|
static JSFunctionSpec JavaPackage_methods[] = {
|
|
{"toString", JavaPackage_toString, 0, 0, 0},
|
|
{0, 0, 0, 0, 0},
|
|
};
|
|
|
|
/*
|
|
* One-time initialization for the JavaPackage class. (This is not
|
|
* run once per thread, rather it's run once for a given JSContext.)
|
|
*/
|
|
JSBool
|
|
jsj_init_JavaPackage(JSContext *cx, JSObject *global_obj,
|
|
JavaPackageDef *additional_predefined_packages) {
|
|
|
|
/* Define JavaPackage class */
|
|
if (!JS_InitClass(cx, global_obj, 0, &JavaPackage_class,
|
|
0, 0, 0, JavaPackage_methods, 0, 0))
|
|
return JS_FALSE;
|
|
|
|
/* Add top-level packages, e.g. : java, netscape, sun */
|
|
if (!pre_define_java_packages(cx, global_obj, standard_java_packages))
|
|
return JS_FALSE;
|
|
if (!pre_define_java_packages(cx, global_obj, additional_predefined_packages))
|
|
return JS_FALSE;
|
|
|
|
return JS_TRUE;
|
|
}
|